From 2048fc94c67949f4edd699172ebaebd1666745ff Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Fri, 27 Jun 2025 05:59:35 +0200 Subject: [PATCH 001/572] Pokabbie's Overworld Pokemon (FollowMon) first draft --- data/event_scripts.s | 1 + data/scripts/followmon.inc | 8 + data/specials.inc | 1 + include/constants/event_objects.h | 17 + include/constants/vars.h | 8 + include/followmon.h | 51 +++ include/sprite.h | 1 + include/wild_encounter.h | 2 + src/event_object_movement.c | 42 ++- src/field_control_avatar.c | 11 +- src/followmon.c | 519 ++++++++++++++++++++++++++++++ src/overworld.c | 4 + src/sprite.c | 11 + src/wild_encounter.c | 44 +++ 14 files changed, 715 insertions(+), 5 deletions(-) create mode 100644 data/scripts/followmon.inc create mode 100644 include/followmon.h create mode 100644 src/followmon.c diff --git a/data/event_scripts.s b/data/event_scripts.s index bc9388be640b..80c75ad57094 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -1162,3 +1162,4 @@ EventScript_VsSeekerChargingDone:: .include "data/scripts/dexnav.inc" .include "data/scripts/battle_frontier.inc" .include "data/scripts/apricorn_tree.inc" + .include "data/scripts/followmon.inc" diff --git a/data/scripts/followmon.inc b/data/scripts/followmon.inc new file mode 100644 index 000000000000..7ead8510dba8 --- /dev/null +++ b/data/scripts/followmon.inc @@ -0,0 +1,8 @@ +InteractWithDynamicWildFollowMon:: + lockall + special CreateFollowMonEncounter + removeobject VAR_LAST_TALKED + dowildbattle + releaseall + end + diff --git a/data/specials.inc b/data/specials.inc index f80933c0112c..b21e727676e7 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -567,3 +567,4 @@ gSpecials:: def_special SetAbility def_special ObjectEventInteractionGetApricornTreeData def_special ObjectEventInteractionPickApricornTree + def_special CreateFollowMonEncounter diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index c20c2c79c3ef..99983cb21097 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -275,6 +275,19 @@ #define OBJ_EVENT_GFX_VAR_E (OBJ_EVENT_GFX_VARS + 0xE) #define OBJ_EVENT_GFX_VAR_F (OBJ_EVENT_GFX_VARS + 0xF) +#define OBJ_EVENT_GFX_VAR_FIRST OBJ_EVENT_GFX_VAR_0 +#define OBJ_EVENT_GFX_VAR_LAST OBJ_EVENT_GFX_VAR_F + +#define OBJ_EVENT_GFX_FOLLOW_MON_0 (OBJ_EVENT_GFX_VAR_F + 1) +#define OBJ_EVENT_GFX_FOLLOW_MON_1 (OBJ_EVENT_GFX_VAR_F + 2) +#define OBJ_EVENT_GFX_FOLLOW_MON_2 (OBJ_EVENT_GFX_VAR_F + 3) +#define OBJ_EVENT_GFX_FOLLOW_MON_3 (OBJ_EVENT_GFX_VAR_F + 4) +#define OBJ_EVENT_GFX_FOLLOW_MON_4 (OBJ_EVENT_GFX_VAR_F + 5) +#define OBJ_EVENT_GFX_FOLLOW_MON_5 (OBJ_EVENT_GFX_VAR_F + 6) + +#define OBJ_EVENT_GFX_FOLLOW_MON_FIRST OBJ_EVENT_GFX_FOLLOW_MON_0 +#define OBJ_EVENT_GFX_FOLLOW_MON_LAST OBJ_EVENT_GFX_FOLLOW_MON_5 + // Don't use (1u << 15) to avoid conflict with BLEND_IMMUNE_FLAG. #define OBJ_EVENT_MON (1u << 14) #define OBJ_EVENT_MON_SHINY (1u << 13) @@ -337,6 +350,10 @@ #define OBJ_EVENT_ID_FOLLOWER 0xFE #define OBJ_EVENT_ID_NPC_FOLLOWER 0xFD +// IDs for dynamic object event spawns +#define OBJ_EVENT_ID_FOLLOW_MON_FIRST 230 +#define OBJ_EVENT_ID_FOLLOW_MON_LAST 240 + // Aliases for old names. "object event id" normally refers to an index into gObjectEvents, which these are not. // Used for link player OWs in CreateLinkPlayerSprite #define OBJ_EVENT_ID_DYNAMIC_BASE 0xF0 diff --git a/include/constants/vars.h b/include/constants/vars.h index a12c2ff3a508..80cda6df4d82 100644 --- a/include/constants/vars.h +++ b/include/constants/vars.h @@ -274,6 +274,14 @@ #define VAR_UNUSED_0x40FE 0x40FE // Unused Var #define VAR_UNUSED_0x40FF 0x40FF // Unused Var +/* +#define VAR_FOLLOW_MON_0 0x40FA +#define VAR_FOLLOW_MON_1 0x40FB +#define VAR_FOLLOW_MON_2 0x40FC +#define VAR_FOLLOW_MON_3 0x40FD +#define VAR_FOLLOW_MON_4 0x40FE +#define VAR_FOLLOW_MON_5 0x40FF +*/ #define VARS_END 0x40FF #define VARS_COUNT (VARS_END - VARS_START + 1) diff --git a/include/followmon.h b/include/followmon.h new file mode 100644 index 000000000000..9814ec410496 --- /dev/null +++ b/include/followmon.h @@ -0,0 +1,51 @@ +#ifndef GUARD_FOLLOWMON_H +#define GUARD_FOLLOWMON_H + +#define FOLLOWMON_SHINY_OFFSET 10000 +#define FOLLOWMON_MAX_SPAWN_SLOTS 6 // 4 reserved palette slots, 1 for the 10th pal slot (if not in use) and 1 for the follower (todo change to 6) +#define FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT 8 + +#define INVALID_SPAWN_SLOT 0xFF + +struct FollowMon +{ + u32 personality; + u16 species; + u16 level; +}; + +struct FollowMonData +{ + bool8 pendingInterction; + u8 activeCount; + //u8 encounterChainCount; + u16 spawnCountdown; + u16 spawnSlot; + u16 pendingSpawnAnim; + //u16 encounterChainSpecies; + //u16 cachedPartnerMonGfx; + struct FollowMon list[FOLLOWMON_MAX_SPAWN_SLOTS]; +}; + +enum FollowMonSpawnAnim +{ + FOLLOWMON_SPAWN_ANIM_SHINY, + FOLLOWMON_SPAWN_ANIM_WATER, + FOLLOWMON_SPAWN_ANIM_CAVE, + FOLLOWMON_SPAWN_ANIM_GRASS, +}; + +//data/scripts/followmon.inc +extern const u8 InteractWithDynamicWildFollowMon[]; + +void FollowMon_OverworldCB(); +void CreateFollowMonEncounter(void); +bool8 FollowMon_ProcessMonInteraction(void); +bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); +bool8 FollowMon_IsMonObject(struct ObjectEvent* object); +void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent); +void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); +u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId); +void FollowMon_OnWarp(void); + +#endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/include/sprite.h b/include/sprite.h index 791907505319..e9b964385b5b 100644 --- a/include/sprite.h +++ b/include/sprite.h @@ -305,6 +305,7 @@ void FreeSpriteTileRanges(void); u16 GetSpriteTileStartByTag(u16 tag); u16 GetSpriteTileTagByTileStart(u16 start); void FreeAllSpritePalettes(void); +u8 CountFreePaletteSlots(void); u32 LoadSpritePalette(const struct SpritePalette *palette); u32 LoadSpritePaletteWithTag(const u16 *pal, u16 tag); u8 LoadSpritePaletteInSlot(const struct SpritePalette *palette, u8 paletteNum); diff --git a/include/wild_encounter.h b/include/wild_encounter.h index c8f60712f906..e6c4f97d2b34 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -3,6 +3,7 @@ #include "rtc.h" #include "constants/wild_encounter.h" +#include "followmon.h" #define HEADER_NONE 0xFFFF @@ -49,6 +50,7 @@ extern bool8 gIsFishingEncounter; extern bool8 gIsSurfingEncounter; extern u8 gChainFishingDexNavStreak; +bool8 GenerateFollowMon(struct FollowMon *followMon, bool8 inWater); void DisableWildEncounters(bool8 disabled); u8 PickWildMonNature(void); bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 848289aec7d7..9b4b2ca79cf3 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -57,6 +57,8 @@ #include "constants/union_room.h" #include "constants/weather.h" +#include "followmon.h" + #define SPECIAL_LOCALIDS_START (min(LOCALID_CAMERA, \ min(LOCALID_PLAYER, \ LOCALID_BERRY_BLENDER_PLAYER_END - MAX_RFU_PLAYERS + 1))) @@ -1547,6 +1549,12 @@ void RemoveObjectEventByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup) static void RemoveObjectEventInternal(struct ObjectEvent *objectEvent) { struct SpriteFrameImage image; + + if(FollowMon_IsMonObject(objectEvent)) + { + FollowMon_OnObjectEventRemoved(objectEvent); + } + image.size = GetObjectEventGraphicsInfo(objectEvent->graphicsId)->size; gSprites[objectEvent->spriteId].images = ℑ // It's possible that this function is called while the sprite pointed to `== sDummySprite`, i.e during map resume; @@ -1726,8 +1734,17 @@ static u8 TrySetupObjectEventSprite(const struct ObjectEventTemplate *objectEven sprite = &gSprites[spriteId]; // Use palette from species palette table - if (spriteTemplate->paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) - sprite->oam.paletteNum = LoadDynamicFollowerPalette(OW_SPECIES(objectEvent), OW_SHINY(objectEvent), OW_FEMALE(objectEvent)); + if (spriteTemplate->paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) { + if(objectEvent->graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && objectEvent->graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) { + u16 tmpGraphicsId = GetFollowMonObjectEventGraphicsId(objectEvent->graphicsId); + sprite->oam.paletteNum = LoadDynamicFollowerPalette( + tmpGraphicsId & OBJ_EVENT_MON_SPECIES_MASK, + tmpGraphicsId & OBJ_EVENT_MON_SHINY, + tmpGraphicsId & OBJ_EVENT_MON_FEMALE); + } else { + sprite->oam.paletteNum = LoadDynamicFollowerPalette(OW_SPECIES(objectEvent), OW_SHINY(objectEvent), OW_FEMALE(objectEvent)); + } + } if (OW_GFX_COMPRESS && sprite->usingSheet) sprite->sheetSpan = GetSpanPerImage(sprite->oam.shape, sprite->oam.size); GetMapCoordsFromSpritePos(objectEvent->currentCoords.x + cameraX, objectEvent->currentCoords.y + cameraY, &sprite->x, &sprite->y); @@ -1768,6 +1785,11 @@ u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemp if (subspriteTables) SetSubspriteTables(&gSprites[gObjectEvents[objectEventId].spriteId], subspriteTables); + if(FollowMon_IsMonObject(&gObjectEvents[objectEventId])) + { + FollowMon_OnObjectEventSpawned(&gObjectEvents[objectEventId]); + } + return objectEventId; } @@ -2856,7 +2878,15 @@ static void SpawnObjectEventOnReturnToField(u8 objectEventId, s16 x, s16 y) if (spriteTemplate.paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) { - u32 paletteNum = LoadDynamicFollowerPalette(OW_SPECIES(objectEvent), OW_SHINY(objectEvent), OW_FEMALE(objectEvent)); + u32 paletteNum; + if(objectEvent->graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && objectEvent->graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) { + u16 tmpGraphicsId = GetFollowMonObjectEventGraphicsId(objectEvent->graphicsId); + paletteNum = LoadDynamicFollowerPalette( + tmpGraphicsId & OBJ_EVENT_MON_SPECIES_MASK, + tmpGraphicsId & OBJ_EVENT_MON_SHINY, + tmpGraphicsId & OBJ_EVENT_MON_FEMALE); + } else + paletteNum = LoadDynamicFollowerPalette(OW_SPECIES(objectEvent), OW_SHINY(objectEvent), OW_FEMALE(objectEvent)); spriteTemplate.paletteTag = GetSpritePaletteTagByPaletteNum(paletteNum); } else if (spriteTemplate.paletteTag != TAG_NONE) @@ -3067,6 +3097,9 @@ const struct ObjectEventGraphicsInfo *GetObjectEventGraphicsInfo(u16 graphicsId) if (graphicsId == OBJ_EVENT_GFX_BARD) return gMauvilleOldManGraphicsInfoPointers[GetCurrentMauvilleOldMan()]; + if (graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) + graphicsId = GetFollowMonObjectEventGraphicsId(graphicsId); + if (graphicsId & OBJ_EVENT_MON) return SpeciesToGraphicsInfo(graphicsId & OBJ_EVENT_MON_SPECIES_MASK, graphicsId & OBJ_EVENT_MON_SHINY, graphicsId & OBJ_EVENT_MON_FEMALE); @@ -6446,7 +6479,8 @@ u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, b { curObject = &gObjectEvents[i]; if (curObject->active && (curObject->movementType != MOVEMENT_TYPE_FOLLOW_PLAYER || objectEvent != &gObjectEvents[gPlayerAvatar.objectEventId]) && curObject != objectEvent - && !FollowerNPC_IsCollisionExempt(curObject, objectEvent) + && !FollowerNPC_IsCollisionExempt(curObject, objectEvent) // Partner + && !FollowMon_IsCollisionExempt(curObject, objectEvent) // Wild Pokemon ) { // check for collision if curObject is active, not the object in question, and not exempt from collisions diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index f35c349fc6b1..ddb1ce0c5ddb 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -42,6 +42,8 @@ #include "constants/songs.h" #include "constants/trainer_hill.h" +#include "followmon.h" + static EWRAM_DATA u8 sWildEncounterImmunitySteps = 0; static EWRAM_DATA u16 sPrevMetatileBehavior = 0; @@ -177,6 +179,11 @@ int ProcessPlayerFieldInput(struct FieldInput *input) if (TryRunOnFrameMapScript() == TRUE) return TRUE; + if(FollowMon_ProcessMonInteraction() == TRUE) + { + return TRUE; + } + if (input->pressedBButton && TrySetupDiveEmergeScript() == TRUE) return TRUE; if (input->tookStep) @@ -399,7 +406,9 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; gSpecialVar_Facing = direction; - if (InTrainerHill() == TRUE) + if (FollowMon_IsMonObject(&gObjectEvents[objectEventId])) + script = InteractWithDynamicWildFollowMon; + else if (InTrainerHill() == TRUE) script = GetTrainerHillTrainerScript(); else if (PlayerHasFollowerNPC() && objectEventId == GetFollowerNPCObjectId()) script = GetFollowerNPCScriptPointer(); diff --git a/src/followmon.c b/src/followmon.c new file mode 100644 index 000000000000..c8b2d24093bc --- /dev/null +++ b/src/followmon.c @@ -0,0 +1,519 @@ +#include "global.h" +#include "constants/event_objects.h" +#include "constants/map_types.h" +#include "constants/songs.h" +#include "constants/vars.h" +#include "battle_setup.h" +#include "event_data.h" +#include "event_object_movement.h" +#include "fieldmap.h" +#include "field_player_avatar.h" +#include "metatile_behavior.h" +#include "script.h" +#include "sprite.h" +#include "sound.h" +#include "random.h" + +#include "wild_encounter.h" +#include "followmon.h" + + +static EWRAM_DATA struct FollowMonData sFollowMonData = { 0 }; + +static bool8 TrySelectTile(s16* outX, s16* outY); +static u8 NextSpawnMonSlot(); +static bool8 IsSpawningWaterMons(); +static u8 CountActiveObjectEvents(); +static u8 ActiveSpawnSlotCount(); +static bool8 isFollowMonFemale(u8 spawnSlot); +static bool8 isFollowMonShiny(u8 spawnSlot); +static bool8 IsSafeToSpawnObjectEvents(void); +static u8 FindObjectEventForGfx(u16 gfxId); +static bool8 AreElevationsCompatible(u8 a, u8 b); +static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); + +void FollowMon_OverworldCB() +{ + // Speed up spawning + if(FALSE) + { + if(sFollowMonData.activeCount <= 1) + { + // Super fast spawn for new things on screen + sFollowMonData.spawnCountdown = min(sFollowMonData.spawnCountdown, 15); + } + else if(sFollowMonData.activeCount <= (ActiveSpawnSlotCount() - 1)) + { + // Fast spawn to reach capacity + sFollowMonData.spawnCountdown = min(sFollowMonData.spawnCountdown, 60); + } + } + + if(sFollowMonData.spawnCountdown == 0 && sFollowMonData.activeCount < 4) + { + s16 x, y; + + if(IsSafeToSpawnObjectEvents() && TrySelectTile(&x, &y)) + { + u16 spawnSlot = NextSpawnMonSlot(); + + if(spawnSlot != INVALID_SPAWN_SLOT) + { + u8 localId = OBJ_EVENT_ID_FOLLOW_MON_FIRST + spawnSlot; + u8 objectEventId = SpawnSpecialObjectEventParameterized( + OBJ_EVENT_GFX_FOLLOW_MON_0 + spawnSlot, + MOVEMENT_TYPE_WANDER_AROUND, + localId, + x, + y, + MapGridGetElevationAt(x, y) + ); + + gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; + gObjectEvents[objectEventId].range.rangeX = 8; + gObjectEvents[objectEventId].range.rangeY = 8; + + + // Hide reflections for spawns in water + // (It just looks weird) + if(IsSpawningWaterMons()) + { + gObjectEvents[objectEventId].hideReflection = TRUE; + } + + // Slower replacement spawning + sFollowMonData.spawnCountdown = 60 * (3 + Random() % 2); + } + } + } + else + { + --sFollowMonData.spawnCountdown; + } + + // Play spawn animation when player is close enough + if(sFollowMonData.pendingSpawnAnim != 0) + { + u16 spawnSlot; + u16 gfxId; + u16 bitFlag; + u8 objectEventId; + enum FollowMonSpawnAnim spawnAnimType; + + for(gfxId = OBJ_EVENT_GFX_FOLLOW_MON_0; gfxId < OBJ_EVENT_GFX_FOLLOW_MON_LAST; ++gfxId) + { + spawnSlot = gfxId - OBJ_EVENT_GFX_FOLLOW_MON_0; + bitFlag = (1 << spawnSlot); + + if((sFollowMonData.pendingSpawnAnim & bitFlag) != 0) + { + objectEventId = FindObjectEventForGfx(gfxId); + + if(objectEventId != OBJECT_EVENTS_COUNT) + { + if(isFollowMonShiny(spawnSlot)) + { + PlaySE(SE_SHINY); + spawnAnimType = FOLLOWMON_SPAWN_ANIM_SHINY; + sFollowMonData.pendingSpawnAnim &= ~bitFlag; + } + else + { + PlayCry_Normal(sFollowMonData.list[spawnSlot].species, 25); + if (IsSpawningWaterMons()) + spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; + else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) + spawnAnimType = FOLLOWMON_SPAWN_ANIM_CAVE; + else + spawnAnimType = FOLLOWMON_SPAWN_ANIM_GRASS; + } + // Instantly play a small animation to ground the spawning a bit (Disable for now) + //MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objectEventId]); + sFollowMonData.pendingSpawnAnim &= ~bitFlag; + } + } + } + } +} + +static u8 NextSpawnMonSlot() +{ + u8 slot; + + slot = FOLLOWMON_MAX_SPAWN_SLOTS; + + for(slot = 0; slot < FOLLOWMON_MAX_SPAWN_SLOTS; ++slot) + { + if(!sFollowMonData.list[slot].species) + break; + } + + // All mon slots are in use + if(slot == FOLLOWMON_MAX_SPAWN_SLOTS) + { + // Cycle through so we remove the oldest mon first + sFollowMonData.spawnSlot = (sFollowMonData.spawnSlot + 1) % FOLLOWMON_MAX_SPAWN_SLOTS; + slot = sFollowMonData.spawnSlot; + } + + // Remove any existing id by this slot + RemoveObjectEventByLocalIdAndMap(OBJ_EVENT_ID_FOLLOW_MON_FIRST + slot, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + + // Check that we don't have too many sprites on screen before spawning + // (lag reduction) + if(sFollowMonData.activeCount != 0 && CountActiveObjectEvents() >= FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT) + { + return INVALID_SPAWN_SLOT; + } + + if (!GenerateFollowMon(&sFollowMonData.list[slot], IsSpawningWaterMons())) + return INVALID_SPAWN_SLOT; + + return slot; +} + +static bool8 TrySelectTile(s16* outX, s16* outY) +{ + u8 tryCount; + u8 elevation; + u16 tileBehavior; + s16 playerX, playerY; + s16 x, y; + u8 closeDistance; + + for(tryCount = 0; tryCount < 3; ++tryCount) + { + // Spawn further away when surfing + if(IsSpawningWaterMons()) + closeDistance = 3; + else + closeDistance = 1; + + // Select a random tile in [-7, -4] [7, 4] range + // Make sure is not directly next to player + do + { + x = (s16)(Random() % 15) - 7; + y = (s16)(Random() % 9) - 4; + } + while (abs(x) <= closeDistance && abs(y) <= closeDistance); + + // We won't spawn mons in in the immediate facing direction + // (stops mons spawning in as I'm running in a straight line) + switch (GetPlayerFacingDirection()) + { + case DIR_NORTH: + if(x == 0 && y < 0) + x = -1; + break; + case DIR_SOUTH: + if(x == 0 && y > 0) + x = 1; + break; + + case DIR_EAST: + if(y == 0 && x > 0) + y = -1; + break; + case DIR_WEST: + if(y == 0 && x < 0) + y = 1; + break; + } + + PlayerGetDestCoords(&playerX, &playerY); + x += playerX; + y += playerY; + + elevation = MapGridGetElevationAt(x, y); + + // 0 is change of elevation, 15 is multiple elevation e.g. bridges + // Causes weird interaction issues so just don't let mons spawn here + if (elevation == 0 || elevation == 15) + return FALSE; + + tileBehavior = MapGridGetMetatileBehaviorAt(x, y); + if(IsSpawningWaterMons()) + { + if(MetatileBehavior_IsWaterWildEncounter(tileBehavior) && !MapGridGetCollisionAt(x, y)) + { + *outX = x; + *outY = y; + + if(!CheckForObjectEventAtLocation(x, y)) + return TRUE; + } + } + else + { + if(MetatileBehavior_IsLandWildEncounter(tileBehavior) && !MapGridGetCollisionAt(x, y)) + { + *outX = x; + *outY = y; + + if(!CheckForObjectEventAtLocation(x, y)) + return TRUE; + } + } + } + + return FALSE; +} + +void CreateFollowMonEncounter(void) { + u8 lastTalkedId = VarGet(VAR_LAST_TALKED); + u8 objEventId = GetObjectEventIdByLocalIdAndMap(lastTalkedId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + u16 slot = 0; + + if(objEventId < OBJECT_EVENTS_COUNT) + { + struct ObjectEvent *curObject = &gObjectEvents[objEventId]; + if(FollowMon_IsMonObject(curObject)) + slot = curObject->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; + } + + ZeroEnemyPartyMons(); + CreateMon( + &gEnemyParty[0], + sFollowMonData.list[slot].species, + sFollowMonData.list[slot].level, + USE_RANDOM_IVS, + TRUE, + sFollowMonData.list[slot].personality, + OT_ID_PRESET, + T1_READ_32(gSaveBlock2Ptr->playerTrainerId) + ); +} + + + +bool8 FollowMon_ProcessMonInteraction(void) +{ + if(VarGet(VAR_REPEL_STEP_COUNT) != 0) + { + // Never auto trigger battle whilst repel is active + sFollowMonData.pendingInterction = FALSE; + return FALSE; + } + + if(sFollowMonData.pendingInterction) + { + u8 i; + struct ObjectEvent *curObject; + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + + sFollowMonData.pendingInterction = FALSE; + + for (i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + curObject = &gObjectEvents[i]; + if (curObject->active && curObject != player && FollowMon_IsMonObject(curObject)) + { + if ((curObject->currentCoords.x == player->currentCoords.x && curObject->currentCoords.y == player->currentCoords.y) || (curObject->previousCoords.x == player->currentCoords.x && curObject->previousCoords.y == player->currentCoords.y)) + { + if (AreElevationsCompatible(curObject->currentElevation, player->currentElevation)) + { + // There is a valid collision so exectute the attached script + const u8* script = InteractWithDynamicWildFollowMon; + gSpecialVar_LastTalked = curObject->localId; + //VarSet(VAR_LAST_TALKED, curObject->localId); + ScriptContext_SetupScript(script); + + //CreateFollowMonEncounter(); + //BattleSetup_StartScriptedWildBattle(); + return TRUE; + } + } + } + } + } + + return FALSE; +} + +bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider) +{ + struct ObjectEvent* player = &gObjectEvents[gPlayerAvatar.objectEventId]; + if (collider == player) + { + // Player can walk on top of follow mon + if(FollowMon_IsMonObject(obstacle)) + { + sFollowMonData.pendingInterction = TRUE; + return TRUE; + } + } + else if(obstacle == player) + { + // Follow mon can walk onto player + if(FollowMon_IsMonObject(collider)) + { + sFollowMonData.pendingInterction = TRUE; + return TRUE; + } + } else if(!FollowMon_IsMonObject(collider) && FollowMon_IsMonObject(obstacle)) + { + // Other objects can walk through follow mons, whilst wandering mons is active + return TRUE; + } + return FALSE; + +} + +bool8 FollowMon_IsMonObject(struct ObjectEvent* object) +{ + u16 localId = object->localId; + u16 graphicsId = object->graphicsId; + + if(localId >= OBJ_EVENT_ID_FOLLOW_MON_FIRST && localId <= OBJ_EVENT_ID_FOLLOW_MON_LAST) + { + // Fast check + return TRUE; + } + + // Check gfx id + if(graphicsId >= OBJ_EVENT_GFX_VAR_FIRST && graphicsId <= OBJ_EVENT_GFX_VAR_LAST) + { + graphicsId = VarGet(VAR_OBJ_GFX_ID_0 + (object->graphicsId - OBJ_EVENT_GFX_VAR_FIRST)); + } + + if(object->graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && object->graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) + return TRUE; + + return FALSE; +} + + +void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) +{ + u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; + + if(sFollowMonData.activeCount != 255) + ++sFollowMonData.activeCount; + + sFollowMonData.pendingSpawnAnim |= (1 << spawnSlot); +} + +void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) +{ + + if(sFollowMonData.activeCount != 0) + --sFollowMonData.activeCount; +} + +u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) +{ + u16 slot = graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; + graphicsId = OBJ_EVENT_MON; + graphicsId += sFollowMonData.list[slot].species; + if (isFollowMonShiny(slot)) + graphicsId += OBJ_EVENT_MON_SHINY; + if (isFollowMonFemale(slot)) + graphicsId += OBJ_EVENT_MON_FEMALE; + return graphicsId; +} + +void FollowMon_OnWarp(void) +{ + sFollowMonData.spawnCountdown = 0; + sFollowMonData.activeCount = 0; + for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { + sFollowMonData.list[i].species = 0; + sFollowMonData.list[i].personality = 0; + sFollowMonData.list[i].level = 0; + } +} + +static bool8 IsSafeToSpawnObjectEvents(void) +{ + struct ObjectEvent* player = &gObjectEvents[gPlayerAvatar.objectEventId]; + + // Only spawn when player is at a valid tile position + return (player->currentCoords.x == player->previousCoords.x && player->currentCoords.y == player->previousCoords.y); +} + +static u8 CountActiveObjectEvents() +{ + u8 i; + u8 count = 0; + + for(i = 0; i < OBJECT_EVENTS_COUNT; ++i) + { + if(gObjectEvents[i].active) + ++count; + } + + return count; +} + +static u8 ActiveSpawnSlotCount() +{ + u8 slot; + u8 count = CountFreePaletteSlots(); + for(slot = 0; slot < FOLLOWMON_MAX_SPAWN_SLOTS; ++slot) + { + if(sFollowMonData.list[slot].species) + ++count; + } + + return count; +} + +static bool8 IsSpawningWaterMons() +{ + return (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER)); +} + +static bool8 isFollowMonFemale(u8 spawnSlot) +{ + u16 species = sFollowMonData.list[spawnSlot].species; + u32 personality = sFollowMonData.list[spawnSlot].personality; + u8 gender = GetGenderFromSpeciesAndPersonality(species, personality); + return (gender == MON_FEMALE); +} + +static bool8 isFollowMonShiny(u8 spawnSlot) +{ + u32 personality = sFollowMonData.list[spawnSlot].personality; + u32 shinyValue = GET_SHINY_VALUE(T1_READ_32(gSaveBlock2Ptr->playerTrainerId), personality); + return (shinyValue < SHINY_ODDS); +} + +static u8 FindObjectEventForGfx(u16 gfxId) +{ + u8 i; + for(i = 0; i < OBJECT_EVENTS_COUNT; ++i) + { + if(gObjectEvents[i].active && gObjectEvents[i].graphicsId == gfxId) + { + return i; + } + } + + return OBJECT_EVENTS_COUNT; +} + +static bool8 CheckForObjectEventAtLocation(s16 x, s16 y) +{ + u8 i; + for(i = 0; i < OBJECT_EVENTS_COUNT; ++i) + { + if(gObjectEvents[i].active && gObjectEvents[i].currentCoords.x == x && gObjectEvents[i].currentCoords.y == y) + { + return TRUE; + } + } + + return FALSE; +} + +static bool8 AreElevationsCompatible(u8 a, u8 b) +{ + if (a == 0 || b == 0) + return TRUE; + + if (a != b) + return FALSE; + + return TRUE; +} diff --git a/src/overworld.c b/src/overworld.c index 8eb83d076e67..6965472b9a35 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -78,6 +78,8 @@ #include "constants/trainer_hill.h" #include "constants/weather.h" +#include "followmon.h" + STATIC_ASSERT((B_FLAG_FOLLOWERS_DISABLED == 0 || OW_FOLLOWERS_ENABLED), FollowersFlagAssignedWithoutEnablingThem); struct CableClubPlayer @@ -677,6 +679,7 @@ void WarpIntoMap(void) ApplyCurrentWarp(); LoadCurrentMapData(); SetPlayerCoordsFromWarp(); + FollowMon_OnWarp(); } void SetWarpDestination(s8 mapGroup, s8 mapNum, s8 warpId, s8 x, s8 y) @@ -1538,6 +1541,7 @@ static void DoCB1_Overworld(u16 newKeys, u16 heldKeys) else { PlayerStep(inputStruct.dpadDirection, newKeys, heldKeys); + FollowMon_OverworldCB(); } } // If stop running but keep holding B -> fix follower frame. diff --git a/src/sprite.c b/src/sprite.c index 1b89a50be16b..8d6e94c77f78 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -1653,6 +1653,17 @@ u32 IndexOfSpritePaletteTag(u16 tag) return 0xFF; } +u8 CountFreePaletteSlots(void) +{ + u32 i; + u8 count = 0; + for (i = gReservedSpritePaletteCount; i < 16; i++) + if (sSpritePaletteTags[i] == TAG_NONE) + ++count; + + return count; +} + u16 GetSpritePaletteTagByPaletteNum(u8 paletteNum) { return sSpritePaletteTags[paletteNum]; diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 4c81fa371c6d..6de5ec547c5e 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -29,6 +29,9 @@ #include "constants/layouts.h" #include "constants/weather.h" +#include "pokemon.h" +#include "random.h" + extern const u8 EventScript_SprayWoreOff[]; #define MAX_ENCOUNTER_RATE 2880 @@ -576,6 +579,47 @@ static bool8 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum return TRUE; } +bool8 GenerateFollowMon(struct FollowMon *followMon, bool8 inWater) +{ + //struct WildPokemon *wildMon; + const struct WildPokemonInfo *wildMonInfo; + u32 headerId; + u8 wildMonIndex = 0; + enum TimeOfDay timeOfDay; + + headerId = GetCurrentMapWildMonHeaderId(); + if (inWater) { + wildMonIndex = ChooseWildMonIndex_WaterRock(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + followMon->level = ChooseWildMonLevel(wildMonInfo->wildPokemon, wildMonIndex, WILD_AREA_WATER); + + } else { + wildMonIndex = ChooseWildMonIndex_Land(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + followMon->level = ChooseWildMonLevel(wildMonInfo->wildPokemon, wildMonIndex, WILD_AREA_LAND); + } + + if (wildMonInfo == NULL) { + return FALSE; + } + + followMon->species = wildMonInfo->wildPokemon[wildMonIndex].species; + followMon->personality = Random32(); + + /* + gender = GetGenderFromSpeciesAndPersonality(*species, personality); + u32 shinyValue = GET_SHINY_VALUE(gSaveBlock2Ptr->playerTrainerId, personality); + followMon->gfxId = OBJ_EVENT_MON + species + if (isShiny) + followMon->gfxId += OBJ_EVENT_MON_SHINY + if (gender == MON_FEMALE) + followMon->gfxId += OBJ_EVENT_MON_FEMALE + */ + return TRUE; +} + static u16 GenerateFishingWildMon(const struct WildPokemonInfo *wildMonInfo, u8 rod) { u8 wildMonIndex = ChooseWildMonIndex_Fishing(rod); From b0e82926c71816b104a7243ccdca08f59a1b1f80 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:31:16 -0600 Subject: [PATCH 002/572] change to proper function name ChooseWildMonIndex_Water --- src/wild_encounter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 6de5ec547c5e..1d55a677f81b 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -589,7 +589,7 @@ bool8 GenerateFollowMon(struct FollowMon *followMon, bool8 inWater) headerId = GetCurrentMapWildMonHeaderId(); if (inWater) { - wildMonIndex = ChooseWildMonIndex_WaterRock(); + wildMonIndex = ChooseWildMonIndex_Water(); timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; followMon->level = ChooseWildMonLevel(wildMonInfo->wildPokemon, wildMonIndex, WILD_AREA_WATER); From 3f028801fd011b6ec5d5efc378be3c5f8e04d3d2 Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Fri, 27 Jun 2025 06:55:38 +0200 Subject: [PATCH 003/572] Add flag to enable/disable followmon --- include/followmon.h | 4 +++- src/followmon.c | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/include/followmon.h b/include/followmon.h index 9814ec410496..3a821ba3e1a4 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -1,8 +1,10 @@ #ifndef GUARD_FOLLOWMON_H #define GUARD_FOLLOWMON_H +#define OW_FLAG_SPAWN_OVERWORLD_MON 0 + #define FOLLOWMON_SHINY_OFFSET 10000 -#define FOLLOWMON_MAX_SPAWN_SLOTS 6 // 4 reserved palette slots, 1 for the 10th pal slot (if not in use) and 1 for the follower (todo change to 6) +#define FOLLOWMON_MAX_SPAWN_SLOTS 6 #define FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT 8 #define INVALID_SPAWN_SLOT 0xFF diff --git a/src/followmon.c b/src/followmon.c index c8b2d24093bc..7c1fbd813e98 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -25,6 +25,7 @@ static u8 NextSpawnMonSlot(); static bool8 IsSpawningWaterMons(); static u8 CountActiveObjectEvents(); static u8 ActiveSpawnSlotCount(); +static void RemoveAllFollowMonObjects(void); static bool8 isFollowMonFemale(u8 spawnSlot); static bool8 isFollowMonShiny(u8 spawnSlot); static bool8 IsSafeToSpawnObjectEvents(void); @@ -34,6 +35,16 @@ static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); void FollowMon_OverworldCB() { + if (!FlagGet(OW_FLAG_SPAWN_OVERWORLD_MON)) { + RemoveAllFollowMonObjects(); + // Zero sFollowMonData ; + u8 *raw = (u8 *)&sFollowMonData; + for (u32 i = 0; i < sizeof(struct FollowMonData); i++) { + raw[i] = 0; + } + return; + } + // Speed up spawning if(FALSE) { @@ -464,6 +475,13 @@ static bool8 IsSpawningWaterMons() return (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER)); } +static void RemoveAllFollowMonObjects(void) { + for(u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { + if(gObjectEvents[i].graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_0 && gObjectEvents[i].graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) + RemoveObjectEvent(&gObjectEvents[i]); + } +} + static bool8 isFollowMonFemale(u8 spawnSlot) { u16 species = sFollowMonData.list[spawnSlot].species; From 594fad59e098abffc240734dbe93819db47c1f8d Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Fri, 27 Jun 2025 07:09:44 +0200 Subject: [PATCH 004/572] Temporary save fix: Followmon are not saved --- include/followmon.h | 3 ++- src/followmon.c | 3 +-- src/load_save.c | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/followmon.h b/include/followmon.h index 3a821ba3e1a4..2f72330ea165 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -1,7 +1,7 @@ #ifndef GUARD_FOLLOWMON_H #define GUARD_FOLLOWMON_H -#define OW_FLAG_SPAWN_OVERWORLD_MON 0 +#define OW_FLAG_SPAWN_OVERWORLD_MON FLAG_UNUSED_0x020 #define FOLLOWMON_SHINY_OFFSET 10000 #define FOLLOWMON_MAX_SPAWN_SLOTS 6 @@ -49,5 +49,6 @@ void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId); void FollowMon_OnWarp(void); +void RemoveAllFollowMonObjects(void); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/followmon.c b/src/followmon.c index 7c1fbd813e98..681a62dfd83f 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -25,7 +25,6 @@ static u8 NextSpawnMonSlot(); static bool8 IsSpawningWaterMons(); static u8 CountActiveObjectEvents(); static u8 ActiveSpawnSlotCount(); -static void RemoveAllFollowMonObjects(void); static bool8 isFollowMonFemale(u8 spawnSlot); static bool8 isFollowMonShiny(u8 spawnSlot); static bool8 IsSafeToSpawnObjectEvents(void); @@ -475,7 +474,7 @@ static bool8 IsSpawningWaterMons() return (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER)); } -static void RemoveAllFollowMonObjects(void) { +void RemoveAllFollowMonObjects(void) { for(u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { if(gObjectEvents[i].graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_0 && gObjectEvents[i].graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) RemoveObjectEvent(&gObjectEvents[i]); diff --git a/src/load_save.c b/src/load_save.c index ea0fcbdb8d05..453b68eb3efb 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -19,6 +19,8 @@ #include "event_data.h" #include "constants/event_objects.h" +#include "followmon.h" + static void ApplyNewEncryptionKeyToAllEncryptedData(u32 encryptionKey); #define SAVEBLOCK_MOVE_RANGE 128 @@ -200,6 +202,9 @@ void SaveObjectEvents(void) int i; u16 graphicsId; + // Temporary fix until we reduce followmon data size + // and include it in the save + RemoveAllFollowMonObjects(); for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { gSaveBlock1Ptr->objectEvents[i] = gObjectEvents[i]; From e3653cfbdfdb3f056ebfd43a6c07792f3cb8802e Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Fri, 27 Jun 2025 07:36:12 +0200 Subject: [PATCH 005/572] Add spawn animations to followmon --- include/event_object_movement.h | 10 +++++++++ include/followmon.h | 8 ------- src/event_object_movement.c | 9 ++++++++ src/field_effect_helpers.c | 39 ++++++++++++++++++++++++++++++++- src/followmon.c | 2 +- 5 files changed, 58 insertions(+), 10 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 4ec46709e367..a327b411d9da 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -60,6 +60,14 @@ enum FollowerTransformTypes TRANSFORM_TYPE_WEATHER, }; +enum FollowMonSpawnAnim +{ + FOLLOWMON_SPAWN_ANIM_GRASS, + FOLLOWMON_SPAWN_ANIM_WATER, + FOLLOWMON_SPAWN_ANIM_CAVE, + FOLLOWMON_SPAWN_ANIM_SHINY, +}; + #define FIGURE_8_LENGTH 72 #define GROUND_EFFECT_FLAG_TALL_GRASS_ON_SPAWN (1 << 0) @@ -514,4 +522,6 @@ bool8 PlayerIsUnderWaterfall(struct ObjectEvent *objectEvent); u8 GetObjectEventApricornTreeId(u8 objectEventId); +bool8 MovementAction_FollowMonSpawn(enum FollowMonSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); + #endif //GUARD_EVENT_OBJECT_MOVEMENT_H diff --git a/include/followmon.h b/include/followmon.h index 2f72330ea165..8253666c036d 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -29,14 +29,6 @@ struct FollowMonData struct FollowMon list[FOLLOWMON_MAX_SPAWN_SLOTS]; }; -enum FollowMonSpawnAnim -{ - FOLLOWMON_SPAWN_ANIM_SHINY, - FOLLOWMON_SPAWN_ANIM_WATER, - FOLLOWMON_SPAWN_ANIM_CAVE, - FOLLOWMON_SPAWN_ANIM_GRASS, -}; - //data/scripts/followmon.inc extern const u8 InteractWithDynamicWildFollowMon[]; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 9b4b2ca79cf3..139f5f30a82d 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11575,3 +11575,12 @@ u8 GetObjectEventApricornTreeId(u8 objectEventId) { return gObjectEvents[objectEventId].trainerRange_berryTreeId; } + +bool8 MovementAction_FollowMonSpawn(enum FollowMonSpawnAnim spawnAnimType, struct ObjectEvent *objEvent) { + gFieldEffectArguments[0] = objEvent->currentCoords.x; + gFieldEffectArguments[1] = objEvent->currentCoords.y; + gFieldEffectArguments[2] = gSprites[objEvent->spriteId].oam.priority + 1; + gFieldEffectArguments[3] = spawnAnimType; + FieldEffectStart(FLDEFF_BUBBLES); // Commandeer this field effect for the spawn anims + return TRUE; +} diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 3623437a4d09..8b7eb17dbc63 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1471,9 +1471,46 @@ void UpdateSandPileFieldEffect(struct Sprite *sprite) u32 FldEff_Bubbles(void) { u8 spriteId; + u8 visual; + //struct Sprite *sprite; + //struct SpriteTemplate template; + s16 xOffset, yOffset; + //u8 spriteData; + //u16 paletteNum = 255; + + switch (gFieldEffectArguments[3]) + { + case 0: // Grass spawn + visual = FLDEFFOBJ_JUMP_TALL_GRASS; + xOffset = 0; + yOffset = 8; + break; + + case 1: // Water spawn + visual = FLDEFFOBJ_JUMP_BIG_SPLASH; //FldEff_SecretPowerShrub + xOffset = 0; + yOffset = 0; + //spriteData = FLDEFF_WATER_SURFACING; + break; + + case 2: // Cave spawn + visual = FLDEFFOBJ_GROUND_IMPACT_DUST; + xOffset = 0; + yOffset = 8; + break; + + default: // Shiny spawn + visual = FLDEFFOBJ_BUBBLES; + xOffset = 0; + yOffset = 0; + break; + } + + // RogueNote: This is hacky, calling through FldEff_Bubbles will expect this to go through FLDEFF_PAL_TAG_GENERAL_0, so override this here + //memcpy(&template, gFieldEffectObjectTemplatePointers[visual], sizeof(template)); SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 0); - spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[FLDEFFOBJ_BUBBLES], gFieldEffectArguments[0], gFieldEffectArguments[1], 82); + spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[visual], gFieldEffectArguments[0] + xOffset, gFieldEffectArguments[1] + yOffset, 82); if (spriteId != MAX_SPRITES) { struct Sprite *sprite = &gSprites[spriteId]; diff --git a/src/followmon.c b/src/followmon.c index 681a62dfd83f..2aca779b06b7 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -138,7 +138,7 @@ void FollowMon_OverworldCB() spawnAnimType = FOLLOWMON_SPAWN_ANIM_GRASS; } // Instantly play a small animation to ground the spawning a bit (Disable for now) - //MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objectEventId]); + MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objectEventId]); sFollowMonData.pendingSpawnAnim &= ~bitFlag; } } From c8bd570a785c67d0d7e83183fceb7ee13a8de230 Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Fri, 27 Jun 2025 15:26:48 +0200 Subject: [PATCH 006/572] Followmon spawn only inside the map the player is in --- src/followmon.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/followmon.c b/src/followmon.c index 2aca779b06b7..c3c0771bf791 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -6,16 +6,17 @@ #include "battle_setup.h" #include "event_data.h" #include "event_object_movement.h" +#include "followmon.h" #include "fieldmap.h" #include "field_player_avatar.h" #include "metatile_behavior.h" +#include "overworld.h" +#include "random.h" #include "script.h" #include "sprite.h" #include "sound.h" -#include "random.h" - #include "wild_encounter.h" -#include "followmon.h" + static EWRAM_DATA struct FollowMonData sFollowMonData = { 0 }; @@ -31,6 +32,8 @@ static bool8 IsSafeToSpawnObjectEvents(void); static u8 FindObjectEventForGfx(u16 gfxId); static bool8 AreElevationsCompatible(u8 a, u8 b); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); +static bool8 IsInsidePlayerMap(s16 x, s16 y); +static void GetMapSize(s32 *width, s32 *height); void FollowMon_OverworldCB() { @@ -237,6 +240,9 @@ static bool8 TrySelectTile(s16* outX, s16* outY) elevation = MapGridGetElevationAt(x, y); + if (!IsInsidePlayerMap(x, y)) { + return FALSE; + } // 0 is change of elevation, 15 is multiple elevation e.g. bridges // Causes weird interaction issues so just don't let mons spawn here if (elevation == 0 || elevation == 15) @@ -534,3 +540,21 @@ static bool8 AreElevationsCompatible(u8 a, u8 b) return TRUE; } + +static bool8 IsInsidePlayerMap(s16 x, s16 y) +{ + s32 width, height; + GetMapSize(&width, &height); + if (x >= 0 && x <= width && y >= 0 && y <= height) { + return TRUE; + } + return FALSE; +} + +static void GetMapSize(s32 *width, s32 *height) +{ + const struct MapLayout *layout; + layout = Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->mapLayout; + *width = layout->width; + *height = layout->height; +} \ No newline at end of file From fa8cc477b9929e9b39fdf0ca29f869b641838538 Mon Sep 17 00:00:00 2001 From: Phantonomy <131238004+Ddaretrogamer@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:37:36 -0500 Subject: [PATCH 007/572] adding OW encounter option to options menu --- include/config/overworld.h | 2 +- include/constants/event_objects.h | 5 ++--- include/constants/flags.h | 6 +++--- include/followmon.h | 2 +- include/strings.h | 5 +++++ src/followmon.c | 2 +- src/strings.c | 3 +++ 7 files changed, 16 insertions(+), 9 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 8d060d723129..bd9f5b23e6c7 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -106,7 +106,7 @@ // To use the following features in scripting, replace the 0s with the flag ID you're assigning it to. // Eg: Replace with FLAG_UNUSED_0x264 so you can use that flag to toggle the feature. #define OW_FLAG_PAUSE_TIME 0 // If this flag is set and OW_USE_FAKE_RTC is enabled, seconds on the in-game clock will not advance. -#define OW_FLAG_NO_ENCOUNTER 0 // If this flag is set, wild encounters will be disabled. +#define OW_FLAG_NO_ENCOUNTER FLAG_OW_NO_ENCOUNTER // If this flag is set, wild encounters will be disabled. #define OW_FLAG_NO_TRAINER_SEE 0 // If this flag is set, trainers will not battle the player unless they're talked to. #define OW_FLAG_NO_COLLISION 0 // If this flag is set, the player will be able to walk over tiles with collision. Mainly intended for debugging purposes. #define OW_FLAG_POKE_RIDER 0 // If this flag is set, the player will be able to use fly from the Pokenav Region Map and the Town Map key item by pressing 'R' on a city/location they are able to fly to. diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 99983cb21097..584e81fb0d2e 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -345,10 +345,9 @@ #define LOCALID_NONE 0 #define LOCALID_CAMERA 127 #define LOCALID_BERRY_BLENDER_PLAYER_END 240 // This will use 5 (MAX_RFU_PLAYERS) IDs ending at 240, i.e. 236-240 -#define LOCALID_FOLLOWING_POKEMON 254 #define LOCALID_PLAYER 255 -#define OBJ_EVENT_ID_FOLLOWER 0xFE -#define OBJ_EVENT_ID_NPC_FOLLOWER 0xFD +#define OBJ_EVENT_ID_FOLLOWER 0xFE //254 +#define OBJ_EVENT_ID_NPC_FOLLOWER 0xFD //253 // IDs for dynamic object event spawns #define OBJ_EVENT_ID_FOLLOW_MON_FIRST 230 diff --git a/include/constants/flags.h b/include/constants/flags.h index 47a3b245e192..86ec982cb316 100644 --- a/include/constants/flags.h +++ b/include/constants/flags.h @@ -43,7 +43,7 @@ #define TEMP_FLAGS_END FLAG_TEMP_1F #define NUM_TEMP_FLAGS (TEMP_FLAGS_END - TEMP_FLAGS_START + 1) -#define FLAG_UNUSED_0x020 0x20 // Unused Flag +#define FLAG_UNUSED_0x020 0x20 // followmon (OWencounter flag) #define FLAG_UNUSED_0x021 0x21 // Unused Flag #define FLAG_UNUSED_0x022 0x22 // Unused Flag #define FLAG_UNUSED_0x023 0x23 // Unused Flag @@ -675,8 +675,8 @@ #define FLAG_UNUSED_0x274 0x274 // Unused Flag #define FLAG_UNUSED_0x275 0x275 // Unused Flag #define FLAG_UNUSED_0x276 0x276 // Unused Flag -#define FLAG_UNUSED_0x277 0x277 // Unused Flag -#define FLAG_UNUSED_0x278 0x278 // Unused Flag +#define FLAG_DISABLE_FOLLOWERS 0x277 // Follower toggle +#define FLAG_OW_NO_ENCOUNTER 0x278 // OW_FLAG_NO_ENCOUNTER #define FLAG_UNUSED_0x279 0x279 // Unused Flag #define FLAG_UNUSED_0x27A 0x27A // Unused Flag #define FLAG_UNUSED_0x27B 0x27B // Unused Flag diff --git a/include/followmon.h b/include/followmon.h index 8253666c036d..4cc5928280be 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -4,7 +4,7 @@ #define OW_FLAG_SPAWN_OVERWORLD_MON FLAG_UNUSED_0x020 #define FOLLOWMON_SHINY_OFFSET 10000 -#define FOLLOWMON_MAX_SPAWN_SLOTS 6 +#define FOLLOWMON_MAX_SPAWN_SLOTS 5 #define FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT 8 #define INVALID_SPAWN_SLOT 0xFF diff --git a/include/strings.h b/include/strings.h index a9264b69fda8..ad2caf4504c2 100644 --- a/include/strings.h +++ b/include/strings.h @@ -2415,4 +2415,9 @@ extern const u8 gText_CannotSendMonToBoxHM[]; extern const u8 gText_CannotSendMonToBoxActive[]; extern const u8 gText_CannotSendMonToBoxPartner[]; +// Followmon text +extern const u8 gText_OW_Encounter[]; +extern const u8 gText_OW_Encounter_On[]; +extern const u8 gText_OW_Encounter_Off[]; + #endif // GUARD_STRINGS_H diff --git a/src/followmon.c b/src/followmon.c index c3c0771bf791..3ab084b471b4 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -62,7 +62,7 @@ void FollowMon_OverworldCB() } } - if(sFollowMonData.spawnCountdown == 0 && sFollowMonData.activeCount < 4) + if(sFollowMonData.spawnCountdown == 0) { s16 x, y; diff --git a/src/strings.c b/src/strings.c index 34c15fe35a4c..1eaa457213a2 100644 --- a/src/strings.c +++ b/src/strings.c @@ -987,6 +987,9 @@ const u8 gText_CommErrorEllipsis[] = _("Communication error…"); const u8 gText_MoveCloserToLinkPartner[] = _("Move closer to your link partner(s).\nAvoid obstacles between partners."); const u8 gText_ABtnRegistrationCounter[] = _("A Button: Registration Counter"); const u8 gText_ABtnTitleScreen[] = _("A Button: Title Screen"); +const u8 gText_OW_Encounter[] = _("OVERWORLD {PKMN}"); +const u8 gText_OW_Encounter_On[] = _("ON"); +const u8 gText_OW_Encounter_Off[] = _("OFF"); const u8 gText_NumPlayerLink[] = _("{STR_VAR_1}P LINK"); const u8 gText_BronzeCard[] = _("BRONZE"); const u8 gText_CopperCard[] = _("COPPER"); From 24630ce1703a5a954341d25ce063e73377638112 Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Sun, 29 Jun 2025 11:22:35 +0200 Subject: [PATCH 008/572] Refactor shiny odds calculations --- include/pokemon.h | 1 + src/pokemon.c | 84 +++++++++++++++++++++++++++-------------------- 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/include/pokemon.h b/include/pokemon.h index 9293e9b26a59..f9f2ee02d674 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -709,6 +709,7 @@ void ZeroMonData(struct Pokemon *mon); void ZeroPlayerPartyMons(void); void ZeroEnemyPartyMons(void); void CreateMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId); +bool32 ComputePlayerShinyOdds(u32 personality); void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId); void CreateMonWithNature(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 nature); void CreateMonWithGenderNatureLetter(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 gender, u8 nature, u8 unownLetter); diff --git a/src/pokemon.c b/src/pokemon.c index 639e6adaedbd..2584dec004f0 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1037,6 +1037,54 @@ void CreateMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFix CalculateMonStats(mon); } +bool32 ComputePlayerShinyOdds(u32 personality) +{ + bool32 isShiny; + + u32 value = gSaveBlock2Ptr->playerTrainerId[0] + | (gSaveBlock2Ptr->playerTrainerId[1] << 8) + | (gSaveBlock2Ptr->playerTrainerId[2] << 16) + | (gSaveBlock2Ptr->playerTrainerId[3] << 24); + + if (P_FLAG_FORCE_NO_SHINY != 0 && FlagGet(P_FLAG_FORCE_NO_SHINY)) + { + isShiny = FALSE; + } + else if (P_FLAG_FORCE_SHINY != 0 && FlagGet(P_FLAG_FORCE_SHINY)) + { + isShiny = TRUE; + } + else if (P_ONLY_OBTAINABLE_SHINIES && InBattlePyramid()) + { + isShiny = FALSE; + } + else if (P_NO_SHINIES_WITHOUT_POKEBALLS && !HasAtLeastOnePokeBall()) + { + isShiny = FALSE; + } + else + { + u32 totalRerolls = 0; + if (CheckBagHasItem(ITEM_SHINY_CHARM, 1)) + totalRerolls += I_SHINY_CHARM_ADDITIONAL_ROLLS; + if (LURE_STEP_COUNT != 0) + totalRerolls += 1; + if (I_FISHING_CHAIN && gIsFishingEncounter) + totalRerolls += CalculateChainFishingShinyRolls(); + if (gDexNavSpecies) + totalRerolls += CalculateDexNavShinyRolls(); + + while (GET_SHINY_VALUE(value, personality) >= SHINY_ODDS && totalRerolls > 0) + { + personality = Random32(); + totalRerolls--; + } + + isShiny = GET_SHINY_VALUE(value, personality) < SHINY_ODDS; + } + return isShiny; +} + void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId) { u8 speciesName[POKEMON_NAME_LENGTH + 1]; @@ -1068,41 +1116,7 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, | (gSaveBlock2Ptr->playerTrainerId[2] << 16) | (gSaveBlock2Ptr->playerTrainerId[3] << 24); - if (P_FLAG_FORCE_NO_SHINY != 0 && FlagGet(P_FLAG_FORCE_NO_SHINY)) - { - isShiny = FALSE; - } - else if (P_FLAG_FORCE_SHINY != 0 && FlagGet(P_FLAG_FORCE_SHINY)) - { - isShiny = TRUE; - } - else if (P_ONLY_OBTAINABLE_SHINIES && (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || (B_FLAG_NO_CATCHING != 0 && FlagGet(B_FLAG_NO_CATCHING)))) - { - isShiny = FALSE; - } - else if (P_NO_SHINIES_WITHOUT_POKEBALLS && !HasAtLeastOnePokeBall()) - { - isShiny = FALSE; - } - else - { - u32 totalRerolls = 0; - if (CheckBagHasItem(ITEM_SHINY_CHARM, 1)) - totalRerolls += I_SHINY_CHARM_ADDITIONAL_ROLLS; - if (LURE_STEP_COUNT != 0) - totalRerolls += 1; - totalRerolls += CalculateChainFishingShinyRolls(); - if (gDexNavSpecies) - totalRerolls += CalculateDexNavShinyRolls(); - - while (GET_SHINY_VALUE(value, personality) >= SHINY_ODDS && totalRerolls > 0) - { - personality = Random32(); - totalRerolls--; - } - - isShiny = GET_SHINY_VALUE(value, personality) < SHINY_ODDS; - } + isShiny = ComputePlayerShinyOdds(personality); } if (hasFixedPersonality) From 306d46b8b330e8590a90c47dea679c1d12ffddc0 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:15:45 -0600 Subject: [PATCH 009/572] added missing includes and proper func name --- src/pokemon.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pokemon.c b/src/pokemon.c index 2584dec004f0..dff48d8655e7 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -50,6 +50,8 @@ #include "text.h" #include "trainer_hill.h" #include "util.h" +#include "wild_encounter.h" +#include "config/fishing.h" #include "constants/abilities.h" #include "constants/battle_frontier.h" #include "constants/battle_move_effects.h" @@ -1054,7 +1056,7 @@ bool32 ComputePlayerShinyOdds(u32 personality) { isShiny = TRUE; } - else if (P_ONLY_OBTAINABLE_SHINIES && InBattlePyramid()) + else if (P_ONLY_OBTAINABLE_SHINIES && InBattlePyramid_()) { isShiny = FALSE; } From a7f985d31e6500c1f497c079f2edbb158738ae44 Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Sun, 29 Jun 2025 12:15:30 +0200 Subject: [PATCH 010/572] Refactor FollowMon struct to use less EWRAM and prepare for FollowMon saving --- include/followmon.h | 12 +++- include/global.fieldmap.h | 4 +- include/wild_encounter.h | 3 +- src/followmon.c | 112 +++++++++++++++++++++----------------- src/wild_encounter.c | 44 ++++----------- 5 files changed, 85 insertions(+), 90 deletions(-) diff --git a/include/followmon.h b/include/followmon.h index 4cc5928280be..8b1c9565d29f 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -9,13 +9,19 @@ #define INVALID_SPAWN_SLOT 0xFF +// Could be reduced to an u8 but I prefer to leave some potential for more advanced features struct FollowMon { - u32 personality; - u16 species; - u16 level; + u16 isShiny:1; + u16 onWater:1; + u16 timeOfDay:2; + u16 unused:4; + u16 encounterIndex:8; + }; +#define EMPTY_FOLLOWMON 0xFF; + struct FollowMonData { bool8 pendingInterction; diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index 5e0f99031db2..208b04a57483 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -216,7 +216,7 @@ struct ObjectEvent u32 hideReflection:1; u32 shiny:1; // OW mon shininess u32 jumpDone:1; - u32 padding:2; + u32 spawnTimeOfDay:2; // Used for FollowMon /*0x04*/ u16 graphicsId; // 12 bits for species; high 4 bits for form /*0x06*/ u8 movementType; /*0x07*/ u8 trainerType; @@ -238,7 +238,7 @@ struct ObjectEvent /*0x1A*/ u8 fieldEffectSpriteId; /*0x1B*/ u8 warpArrowSpriteId; /*0x1C*/ u8 movementActionId; - /*0x1D*/ u8 trainerRange_berryTreeId; + /*0x1D*/ u8 trainerRange_berryTreeId; //Also stores encounterIndex for FollowMon /*0x1E*/ u8 currentMetatileBehavior; /*0x1F*/ u8 previousMetatileBehavior; /*0x20*/ u8 previousMovementDirection:4; diff --git a/include/wild_encounter.h b/include/wild_encounter.h index e6c4f97d2b34..9cf192cee882 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -50,7 +50,8 @@ extern bool8 gIsFishingEncounter; extern bool8 gIsSurfingEncounter; extern u8 gChainFishingDexNavStreak; -bool8 GenerateFollowMon(struct FollowMon *followMon, bool8 inWater); +void GenerateFollowMon(struct FollowMon *followMon, bool8 inWater); +u8 ChooseWildMonLevel(const struct WildPokemon *wildPokemon, u8 wildMonIndex, enum WildPokemonArea area); void DisableWildEncounters(bool8 disabled); u8 PickWildMonNature(void); bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior); diff --git a/src/followmon.c b/src/followmon.c index 3ab084b471b4..2bf4a96ae913 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -24,10 +24,9 @@ static EWRAM_DATA struct FollowMonData sFollowMonData = { 0 }; static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool8 IsSpawningWaterMons(); +static u16 GetFollowMonSpecies(struct FollowMon *followMon); static u8 CountActiveObjectEvents(); static u8 ActiveSpawnSlotCount(); -static bool8 isFollowMonFemale(u8 spawnSlot); -static bool8 isFollowMonShiny(u8 spawnSlot); static bool8 IsSafeToSpawnObjectEvents(void); static u8 FindObjectEventForGfx(u16 gfxId); static bool8 AreElevationsCompatible(u8 a, u8 b); @@ -35,6 +34,8 @@ static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); static bool8 IsInsidePlayerMap(s16 x, s16 y); static void GetMapSize(s32 *width, s32 *height); +#define sEncounterIndex trainerRange_berryTreeId + void FollowMon_OverworldCB() { if (!FlagGet(OW_FLAG_SPAWN_OVERWORLD_MON)) { @@ -85,7 +86,13 @@ void FollowMon_OverworldCB() gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; gObjectEvents[objectEventId].range.rangeX = 8; gObjectEvents[objectEventId].range.rangeY = 8; - + + // Only used for save/load as well as loading encounters, + // Most of teh time, followmon data is tracked in sFollowMonData + const struct FollowMon *followMon = &sFollowMonData.list[spawnSlot]; + gObjectEvents[objectEventId].shiny = followMon->isShiny; + gObjectEvents[objectEventId].spawnTimeOfDay = followMon->timeOfDay; + gObjectEvents[objectEventId].sEncounterIndex = followMon->encounterIndex; // Hide reflections for spawns in water // (It just looks weird) @@ -124,7 +131,7 @@ void FollowMon_OverworldCB() if(objectEventId != OBJECT_EVENTS_COUNT) { - if(isFollowMonShiny(spawnSlot)) + if(sFollowMonData.list[spawnSlot].isShiny) { PlaySE(SE_SHINY); spawnAnimType = FOLLOWMON_SPAWN_ANIM_SHINY; @@ -132,7 +139,8 @@ void FollowMon_OverworldCB() } else { - PlayCry_Normal(sFollowMonData.list[spawnSlot].species, 25); + PlayCry_Normal(GetFollowMonSpecies(&sFollowMonData.list[spawnSlot]), 25); + //MetatileBehavior_IsSurfableWaterOrUnderwater(gObjectEvents[objectEventId].currentMetatileBehavior) if (IsSpawningWaterMons()) spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) @@ -149,7 +157,7 @@ void FollowMon_OverworldCB() } } -static u8 NextSpawnMonSlot() +static u8 NextSpawnMonSlot(void) { u8 slot; @@ -157,7 +165,7 @@ static u8 NextSpawnMonSlot() for(slot = 0; slot < FOLLOWMON_MAX_SPAWN_SLOTS; ++slot) { - if(!sFollowMonData.list[slot].species) + if(sFollowMonData.list[slot].encounterIndex == 0) break; } @@ -179,8 +187,7 @@ static u8 NextSpawnMonSlot() return INVALID_SPAWN_SLOT; } - if (!GenerateFollowMon(&sFollowMonData.list[slot], IsSpawningWaterMons())) - return INVALID_SPAWN_SLOT; + GenerateFollowMon(&sFollowMonData.list[slot], IsSpawningWaterMons()); return slot; } @@ -277,28 +284,45 @@ static bool8 TrySelectTile(s16* outX, s16* outY) } void CreateFollowMonEncounter(void) { + struct ObjectEvent *curObject; u8 lastTalkedId = VarGet(VAR_LAST_TALKED); u8 objEventId = GetObjectEventIdByLocalIdAndMap(lastTalkedId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); - u16 slot = 0; if(objEventId < OBJECT_EVENTS_COUNT) { - struct ObjectEvent *curObject = &gObjectEvents[objEventId]; - if(FollowMon_IsMonObject(curObject)) - slot = curObject->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; + curObject = &gObjectEvents[objEventId]; + if(!FollowMon_IsMonObject(curObject)) + return; + } else + return; + + const struct WildPokemonInfo *wildMonInfo; + u32 headerId = GetCurrentMapWildMonHeaderId(); + u8 index = curObject->sEncounterIndex - 1; + u8 level = 0; + if (MetatileBehavior_IsSurfableWaterOrUnderwater(curObject->currentMetatileBehavior)) { + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[curObject->spawnTimeOfDay].waterMonsInfo; + level = ChooseWildMonLevel(wildMonInfo->wildPokemon, index, WILD_AREA_WATER); + } else { + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[curObject->spawnTimeOfDay].landMonsInfo; + level = ChooseWildMonLevel(wildMonInfo->wildPokemon,index, WILD_AREA_LAND); } + u16 species = wildMonInfo->wildPokemon[index].species; + bool8 shiny = curObject->shiny; + ZeroEnemyPartyMons(); CreateMon( &gEnemyParty[0], - sFollowMonData.list[slot].species, - sFollowMonData.list[slot].level, + species, + level, USE_RANDOM_IVS, - TRUE, - sFollowMonData.list[slot].personality, - OT_ID_PRESET, - T1_READ_32(gSaveBlock2Ptr->playerTrainerId) + FALSE, + 0, + OT_ID_PLAYER_ID, + 0 ); + SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); } @@ -387,13 +411,7 @@ bool8 FollowMon_IsMonObject(struct ObjectEvent* object) return TRUE; } - // Check gfx id - if(graphicsId >= OBJ_EVENT_GFX_VAR_FIRST && graphicsId <= OBJ_EVENT_GFX_VAR_LAST) - { - graphicsId = VarGet(VAR_OBJ_GFX_ID_0 + (object->graphicsId - OBJ_EVENT_GFX_VAR_FIRST)); - } - - if(object->graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && object->graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) + if(graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) return TRUE; return FALSE; @@ -420,12 +438,11 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) { u16 slot = graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; - graphicsId = OBJ_EVENT_MON; - graphicsId += sFollowMonData.list[slot].species; - if (isFollowMonShiny(slot)) + u16 species = GetFollowMonSpecies(&sFollowMonData.list[slot]); + + graphicsId = OBJ_EVENT_MON + species; + if (sFollowMonData.list[slot].isShiny) graphicsId += OBJ_EVENT_MON_SHINY; - if (isFollowMonFemale(slot)) - graphicsId += OBJ_EVENT_MON_FEMALE; return graphicsId; } @@ -434,12 +451,22 @@ void FollowMon_OnWarp(void) sFollowMonData.spawnCountdown = 0; sFollowMonData.activeCount = 0; for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - sFollowMonData.list[i].species = 0; - sFollowMonData.list[i].personality = 0; - sFollowMonData.list[i].level = 0; + sFollowMonData.list[i].encounterIndex = 0; } } +static u16 GetFollowMonSpecies(struct FollowMon *followMon) +{ + u16 species = 0; + u32 headerId = GetCurrentMapWildMonHeaderId(); + if (followMon->onWater) { + species = gWildMonHeaders[headerId].encounterTypes[followMon->timeOfDay].waterMonsInfo->wildPokemon[followMon->encounterIndex - 1].species; + } else { + species = gWildMonHeaders[headerId].encounterTypes[followMon->timeOfDay].landMonsInfo->wildPokemon[followMon->encounterIndex - 1].species; + } + return species; +} + static bool8 IsSafeToSpawnObjectEvents(void) { struct ObjectEvent* player = &gObjectEvents[gPlayerAvatar.objectEventId]; @@ -468,7 +495,7 @@ static u8 ActiveSpawnSlotCount() u8 count = CountFreePaletteSlots(); for(slot = 0; slot < FOLLOWMON_MAX_SPAWN_SLOTS; ++slot) { - if(sFollowMonData.list[slot].species) + if(sFollowMonData.list[slot].encounterIndex == 0) ++count; } @@ -487,21 +514,6 @@ void RemoveAllFollowMonObjects(void) { } } -static bool8 isFollowMonFemale(u8 spawnSlot) -{ - u16 species = sFollowMonData.list[spawnSlot].species; - u32 personality = sFollowMonData.list[spawnSlot].personality; - u8 gender = GetGenderFromSpeciesAndPersonality(species, personality); - return (gender == MON_FEMALE); -} - -static bool8 isFollowMonShiny(u8 spawnSlot) -{ - u32 personality = sFollowMonData.list[spawnSlot].personality; - u32 shinyValue = GET_SHINY_VALUE(T1_READ_32(gSaveBlock2Ptr->playerTrainerId), personality); - return (shinyValue < SHINY_ODDS); -} - static u8 FindObjectEventForGfx(u16 gfxId) { u8 i; diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 1d55a677f81b..bcd152a5e46b 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -330,7 +330,7 @@ static u32 ChooseWildMonIndex_Fishing(u8 rod) return wildMonIndex; } -static u8 ChooseWildMonLevel(const struct WildPokemon *wildPokemon, u8 wildMonIndex, enum WildPokemonArea area) +u8 ChooseWildMonLevel(const struct WildPokemon *wildPokemon, u8 wildMonIndex, enum WildPokemonArea area) { u8 min; u8 max; @@ -579,45 +579,21 @@ static bool8 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum return TRUE; } -bool8 GenerateFollowMon(struct FollowMon *followMon, bool8 inWater) +void GenerateFollowMon(struct FollowMon *followMon, bool8 inWater) { - //struct WildPokemon *wildMon; - const struct WildPokemonInfo *wildMonInfo; - u32 headerId; - u8 wildMonIndex = 0; - enum TimeOfDay timeOfDay; - - headerId = GetCurrentMapWildMonHeaderId(); + u32 headerId = GetCurrentMapWildMonHeaderId(); if (inWater) { - wildMonIndex = ChooseWildMonIndex_Water(); - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); - wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; - followMon->level = ChooseWildMonLevel(wildMonInfo->wildPokemon, wildMonIndex, WILD_AREA_WATER); + followMon->encounterIndex = ChooseWildMonIndex_WaterRock() + 1; + followMon->timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + followMon->onWater = TRUE; } else { - wildMonIndex = ChooseWildMonIndex_Land(); - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; - followMon->level = ChooseWildMonLevel(wildMonInfo->wildPokemon, wildMonIndex, WILD_AREA_LAND); + followMon->encounterIndex = ChooseWildMonIndex_Land() + 1; + followMon->timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + followMon->onWater = FALSE; } - if (wildMonInfo == NULL) { - return FALSE; - } - - followMon->species = wildMonInfo->wildPokemon[wildMonIndex].species; - followMon->personality = Random32(); - - /* - gender = GetGenderFromSpeciesAndPersonality(*species, personality); - u32 shinyValue = GET_SHINY_VALUE(gSaveBlock2Ptr->playerTrainerId, personality); - followMon->gfxId = OBJ_EVENT_MON + species - if (isShiny) - followMon->gfxId += OBJ_EVENT_MON_SHINY - if (gender == MON_FEMALE) - followMon->gfxId += OBJ_EVENT_MON_FEMALE - */ - return TRUE; + followMon->isShiny = ComputePlayerShinyOdds(Random32()); } static u16 GenerateFishingWildMon(const struct WildPokemonInfo *wildMonInfo, u8 rod) From acd0ceede2e9658786e929f59065c5de852d63d0 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:23:12 -0600 Subject: [PATCH 011/572] revert "WaterRock" func name again --- src/wild_encounter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wild_encounter.c b/src/wild_encounter.c index bcd152a5e46b..de149e77b02d 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -583,7 +583,7 @@ void GenerateFollowMon(struct FollowMon *followMon, bool8 inWater) { u32 headerId = GetCurrentMapWildMonHeaderId(); if (inWater) { - followMon->encounterIndex = ChooseWildMonIndex_WaterRock() + 1; + followMon->encounterIndex = ChooseWildMonIndex_Water() + 1; followMon->timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); followMon->onWater = TRUE; From b76b1edcb80d88af400103b08ae47b0a30153e6f Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Sun, 29 Jun 2025 14:30:34 +0200 Subject: [PATCH 012/572] Fix despawn code --- src/followmon.c | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/src/followmon.c b/src/followmon.c index 2bf4a96ae913..62e4eb91e3fe 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -26,7 +26,6 @@ static u8 NextSpawnMonSlot(); static bool8 IsSpawningWaterMons(); static u16 GetFollowMonSpecies(struct FollowMon *followMon); static u8 CountActiveObjectEvents(); -static u8 ActiveSpawnSlotCount(); static bool8 IsSafeToSpawnObjectEvents(void); static u8 FindObjectEventForGfx(u16 gfxId); static bool8 AreElevationsCompatible(u8 a, u8 b); @@ -48,22 +47,7 @@ void FollowMon_OverworldCB() return; } - // Speed up spawning - if(FALSE) - { - if(sFollowMonData.activeCount <= 1) - { - // Super fast spawn for new things on screen - sFollowMonData.spawnCountdown = min(sFollowMonData.spawnCountdown, 15); - } - else if(sFollowMonData.activeCount <= (ActiveSpawnSlotCount() - 1)) - { - // Fast spawn to reach capacity - sFollowMonData.spawnCountdown = min(sFollowMonData.spawnCountdown, 60); - } - } - - if(sFollowMonData.spawnCountdown == 0) + if(sFollowMonData.spawnCountdown == 0 && sFollowMonData.activeCount < 4) { s16 x, y; @@ -430,7 +414,8 @@ void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { - + u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; + sFollowMonData.list[spawnSlot].encounterIndex = 0; if(sFollowMonData.activeCount != 0) --sFollowMonData.activeCount; } @@ -489,19 +474,6 @@ static u8 CountActiveObjectEvents() return count; } -static u8 ActiveSpawnSlotCount() -{ - u8 slot; - u8 count = CountFreePaletteSlots(); - for(slot = 0; slot < FOLLOWMON_MAX_SPAWN_SLOTS; ++slot) - { - if(sFollowMonData.list[slot].encounterIndex == 0) - ++count; - } - - return count; -} - static bool8 IsSpawningWaterMons() { return (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER)); From 167ad3336ce5084063d8a33d3b28916e9c5ed7ea Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Sun, 29 Jun 2025 15:25:13 +0200 Subject: [PATCH 013/572] Fix bug prevent more followMon to load after map change --- src/overworld.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld.c b/src/overworld.c index 6965472b9a35..8872b67723d0 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -594,6 +594,7 @@ void ApplyCurrentWarp(void) gSaveBlock1Ptr->location = sWarpDestination; sFixedDiveWarp = sDummyWarpData; sFixedHoleWarp = sDummyWarpData; + FollowMon_OnWarp(); } static void ClearDiveAndHoleWarps(void) @@ -679,7 +680,6 @@ void WarpIntoMap(void) ApplyCurrentWarp(); LoadCurrentMapData(); SetPlayerCoordsFromWarp(); - FollowMon_OnWarp(); } void SetWarpDestination(s8 mapGroup, s8 mapNum, s8 warpId, s8 x, s8 y) From 33c918efd5d8b3213f8a45519d6bd5abed7b3dcd Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Sun, 29 Jun 2025 15:27:02 +0200 Subject: [PATCH 014/572] FollowMons are now saved and loaded properly --- include/followmon.h | 9 +++------ src/followmon.c | 36 +++++++++++++++++++++++++++--------- src/load_save.c | 5 ++++- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/include/followmon.h b/include/followmon.h index 8b1c9565d29f..d510d0da418a 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -25,20 +25,17 @@ struct FollowMon struct FollowMonData { bool8 pendingInterction; - u8 activeCount; - //u8 encounterChainCount; + u8 spawnSlot; u16 spawnCountdown; - u16 spawnSlot; u16 pendingSpawnAnim; - //u16 encounterChainSpecies; - //u16 cachedPartnerMonGfx; struct FollowMon list[FOLLOWMON_MAX_SPAWN_SLOTS]; }; //data/scripts/followmon.inc extern const u8 InteractWithDynamicWildFollowMon[]; -void FollowMon_OverworldCB(); +void LoadFollowMonData(struct ObjectEvent *objectEvent); +void FollowMon_OverworldCB(void); void CreateFollowMonEncounter(void); bool8 FollowMon_ProcessMonInteraction(void); bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); diff --git a/src/followmon.c b/src/followmon.c index 62e4eb91e3fe..0152ea8c1ea6 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -21,6 +21,7 @@ static EWRAM_DATA struct FollowMonData sFollowMonData = { 0 }; +static u8 CountActiveFollowMon(); static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool8 IsSpawningWaterMons(); @@ -35,7 +36,18 @@ static void GetMapSize(s32 *width, s32 *height); #define sEncounterIndex trainerRange_berryTreeId -void FollowMon_OverworldCB() +void LoadFollowMonData(struct ObjectEvent *objectEvent) +{ + u8 slot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; + sFollowMonData.list[slot].isShiny = objectEvent->shiny; + sFollowMonData.list[slot].timeOfDay = objectEvent->spawnTimeOfDay; + sFollowMonData.list[slot].encounterIndex = objectEvent->sEncounterIndex; + + sFollowMonData.spawnCountdown += 60; +} + + +void FollowMon_OverworldCB(void) { if (!FlagGet(OW_FLAG_SPAWN_OVERWORLD_MON)) { RemoveAllFollowMonObjects(); @@ -47,7 +59,7 @@ void FollowMon_OverworldCB() return; } - if(sFollowMonData.spawnCountdown == 0 && sFollowMonData.activeCount < 4) + if(sFollowMonData.spawnCountdown == 0 && CountActiveFollowMon() < 4) { s16 x, y; @@ -166,7 +178,7 @@ static u8 NextSpawnMonSlot(void) // Check that we don't have too many sprites on screen before spawning // (lag reduction) - if(sFollowMonData.activeCount != 0 && CountActiveObjectEvents() >= FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT) + if(CountActiveObjectEvents() >= FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT) { return INVALID_SPAWN_SLOT; } @@ -406,9 +418,6 @@ void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; - if(sFollowMonData.activeCount != 255) - ++sFollowMonData.activeCount; - sFollowMonData.pendingSpawnAnim |= (1 << spawnSlot); } @@ -416,8 +425,6 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; sFollowMonData.list[spawnSlot].encounterIndex = 0; - if(sFollowMonData.activeCount != 0) - --sFollowMonData.activeCount; } u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) @@ -434,7 +441,6 @@ u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) void FollowMon_OnWarp(void) { sFollowMonData.spawnCountdown = 0; - sFollowMonData.activeCount = 0; for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { sFollowMonData.list[i].encounterIndex = 0; } @@ -460,6 +466,18 @@ static bool8 IsSafeToSpawnObjectEvents(void) return (player->currentCoords.x == player->previousCoords.x && player->currentCoords.y == player->previousCoords.y); } +static u8 CountActiveFollowMon() +{ + u8 count = 0; + for(u8 slot = 0; slot < FOLLOWMON_MAX_SPAWN_SLOTS; slot++) + { + if(sFollowMonData.list[slot].encounterIndex) + count++; + } + + return count; +} + static u8 CountActiveObjectEvents() { u8 i; diff --git a/src/load_save.c b/src/load_save.c index 453b68eb3efb..713a0790c6ce 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -204,7 +204,6 @@ void SaveObjectEvents(void) // Temporary fix until we reduce followmon data size // and include it in the save - RemoveAllFollowMonObjects(); for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { gSaveBlock1Ptr->objectEvents[i] = gObjectEvents[i]; @@ -236,6 +235,10 @@ void LoadObjectEvents(void) if (gObjectEvents[i].spriteId != 127) gObjectEvents[i].graphicsId &= 0xFF; gObjectEvents[i].spriteId = 0; + + if(gObjectEvents[i].graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && gObjectEvents[i].graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) { + LoadFollowMonData(&gObjectEvents[i]); + } // Try to restore saved inactive follower if (gObjectEvents[i].localId == OBJ_EVENT_ID_FOLLOWER && !gObjectEvents[i].active && From e50b0959341d430b02e0e4e85cb7c9cb57e1db6d Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Sun, 29 Jun 2025 15:28:36 +0200 Subject: [PATCH 015/572] Remove FollowMon limit added for testing --- src/followmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/followmon.c b/src/followmon.c index 0152ea8c1ea6..f4df5cfe6a9a 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -59,7 +59,7 @@ void FollowMon_OverworldCB(void) return; } - if(sFollowMonData.spawnCountdown == 0 && CountActiveFollowMon() < 4) + if(sFollowMonData.spawnCountdown == 0) { s16 x, y; From bae226e33a53ac1cdc97dc1ce523a8acaf8c0d32 Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Sun, 29 Jun 2025 15:41:36 +0200 Subject: [PATCH 016/572] Fix bug when loading water FollowMon from savefile --- src/followmon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/followmon.c b/src/followmon.c index f4df5cfe6a9a..9873028e74d4 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -42,6 +42,7 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent) sFollowMonData.list[slot].isShiny = objectEvent->shiny; sFollowMonData.list[slot].timeOfDay = objectEvent->spawnTimeOfDay; sFollowMonData.list[slot].encounterIndex = objectEvent->sEncounterIndex; + sFollowMonData.list[slot].onWater = MetatileBehavior_IsSurfableWaterOrUnderwater(objectEvent->currentMetatileBehavior); sFollowMonData.spawnCountdown += 60; } @@ -84,7 +85,7 @@ void FollowMon_OverworldCB(void) gObjectEvents[objectEventId].range.rangeY = 8; // Only used for save/load as well as loading encounters, - // Most of teh time, followmon data is tracked in sFollowMonData + // Most of the time, followmon data is tracked in sFollowMonData const struct FollowMon *followMon = &sFollowMonData.list[spawnSlot]; gObjectEvents[objectEventId].shiny = followMon->isShiny; gObjectEvents[objectEventId].spawnTimeOfDay = followMon->timeOfDay; @@ -136,7 +137,6 @@ void FollowMon_OverworldCB(void) else { PlayCry_Normal(GetFollowMonSpecies(&sFollowMonData.list[spawnSlot]), 25); - //MetatileBehavior_IsSurfableWaterOrUnderwater(gObjectEvents[objectEventId].currentMetatileBehavior) if (IsSpawningWaterMons()) spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) From ee918273ebe41e5578dbd73981133b5bfefa2f50 Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Mon, 30 Jun 2025 04:29:58 +0200 Subject: [PATCH 017/572] Fix bug where it tries to spawn followmon when the encounter table is empty --- src/followmon.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/followmon.c b/src/followmon.c index 9873028e74d4..b3884cf9e44d 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -29,6 +29,7 @@ static u16 GetFollowMonSpecies(struct FollowMon *followMon); static u8 CountActiveObjectEvents(); static bool8 IsSafeToSpawnObjectEvents(void); static u8 FindObjectEventForGfx(u16 gfxId); +static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); static bool8 AreElevationsCompatible(u8 a, u8 b); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); static bool8 IsInsidePlayerMap(s16 x, s16 y); @@ -63,8 +64,10 @@ void FollowMon_OverworldCB(void) if(sFollowMonData.spawnCountdown == 0) { s16 x, y; + const struct WildPokemonInfo *wildMonInfo = NULL; + wildMonInfo = GetActiveEncounterTable(IsSpawningWaterMons()); - if(IsSafeToSpawnObjectEvents() && TrySelectTile(&x, &y)) + if(wildMonInfo && IsSafeToSpawnObjectEvents() && TrySelectTile(&x, &y)) { u16 spawnSlot = NextSpawnMonSlot(); @@ -532,6 +535,22 @@ static bool8 CheckForObjectEventAtLocation(s16 x, s16 y) return FALSE; } +static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater) +{ + u32 headerId = GetCurrentMapWildMonHeaderId(); + if (headerId == HEADER_NONE) + return NULL; + enum TimeOfDay timeOfDay; + + if (onWater) { + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + return gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + } + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + return gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + +} + static bool8 AreElevationsCompatible(u8 a, u8 b) { if (a == 0 || b == 0) From 1ebfc5ae5a7657653fc239335e8cdd9c99e58b05 Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Mon, 30 Jun 2025 05:13:30 +0200 Subject: [PATCH 018/572] Clean up GFX_FOLLOWMON defines --- include/constants/event_objects.h | 1 + src/event_object_movement.c | 6 +++--- src/followmon.c | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 584e81fb0d2e..efdb359b027d 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -288,6 +288,7 @@ #define OBJ_EVENT_GFX_FOLLOW_MON_FIRST OBJ_EVENT_GFX_FOLLOW_MON_0 #define OBJ_EVENT_GFX_FOLLOW_MON_LAST OBJ_EVENT_GFX_FOLLOW_MON_5 +#define IS_FOLLOWMON_GFXID(gfxID) (gfxID >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && gfxID <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) // Don't use (1u << 15) to avoid conflict with BLEND_IMMUNE_FLAG. #define OBJ_EVENT_MON (1u << 14) #define OBJ_EVENT_MON_SHINY (1u << 13) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 139f5f30a82d..6cd369ac137d 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1735,7 +1735,7 @@ static u8 TrySetupObjectEventSprite(const struct ObjectEventTemplate *objectEven sprite = &gSprites[spriteId]; // Use palette from species palette table if (spriteTemplate->paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) { - if(objectEvent->graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && objectEvent->graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) { + if (IS_FOLLOWMON_GFXID(objectEvent->graphicsId)) { u16 tmpGraphicsId = GetFollowMonObjectEventGraphicsId(objectEvent->graphicsId); sprite->oam.paletteNum = LoadDynamicFollowerPalette( tmpGraphicsId & OBJ_EVENT_MON_SPECIES_MASK, @@ -2879,7 +2879,7 @@ static void SpawnObjectEventOnReturnToField(u8 objectEventId, s16 x, s16 y) if (spriteTemplate.paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) { u32 paletteNum; - if(objectEvent->graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && objectEvent->graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) { + if (IS_FOLLOWMON_GFXID(objectEvent->graphicsId)) { u16 tmpGraphicsId = GetFollowMonObjectEventGraphicsId(objectEvent->graphicsId); paletteNum = LoadDynamicFollowerPalette( tmpGraphicsId & OBJ_EVENT_MON_SPECIES_MASK, @@ -3097,7 +3097,7 @@ const struct ObjectEventGraphicsInfo *GetObjectEventGraphicsInfo(u16 graphicsId) if (graphicsId == OBJ_EVENT_GFX_BARD) return gMauvilleOldManGraphicsInfoPointers[GetCurrentMauvilleOldMan()]; - if (graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) + if (IS_FOLLOWMON_GFXID(graphicsId)) graphicsId = GetFollowMonObjectEventGraphicsId(graphicsId); if (graphicsId & OBJ_EVENT_MON) diff --git a/src/followmon.c b/src/followmon.c index b3884cf9e44d..6611a6f809d8 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -39,7 +39,7 @@ static void GetMapSize(s32 *width, s32 *height); void LoadFollowMonData(struct ObjectEvent *objectEvent) { - u8 slot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; + u8 slot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; sFollowMonData.list[slot].isShiny = objectEvent->shiny; sFollowMonData.list[slot].timeOfDay = objectEvent->spawnTimeOfDay; sFollowMonData.list[slot].encounterIndex = objectEvent->sEncounterIndex; @@ -75,7 +75,7 @@ void FollowMon_OverworldCB(void) { u8 localId = OBJ_EVENT_ID_FOLLOW_MON_FIRST + spawnSlot; u8 objectEventId = SpawnSpecialObjectEventParameterized( - OBJ_EVENT_GFX_FOLLOW_MON_0 + spawnSlot, + OBJ_EVENT_GFX_FOLLOW_MON_FIRST + spawnSlot, MOVEMENT_TYPE_WANDER_AROUND, localId, x, @@ -120,9 +120,9 @@ void FollowMon_OverworldCB(void) u8 objectEventId; enum FollowMonSpawnAnim spawnAnimType; - for(gfxId = OBJ_EVENT_GFX_FOLLOW_MON_0; gfxId < OBJ_EVENT_GFX_FOLLOW_MON_LAST; ++gfxId) + for(gfxId = OBJ_EVENT_GFX_FOLLOW_MON_FIRST; gfxId < OBJ_EVENT_GFX_FOLLOW_MON_LAST; ++gfxId) { - spawnSlot = gfxId - OBJ_EVENT_GFX_FOLLOW_MON_0; + spawnSlot = gfxId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; bitFlag = (1 << spawnSlot); if((sFollowMonData.pendingSpawnAnim & bitFlag) != 0) @@ -404,13 +404,13 @@ bool8 FollowMon_IsMonObject(struct ObjectEvent* object) u16 localId = object->localId; u16 graphicsId = object->graphicsId; - if(localId >= OBJ_EVENT_ID_FOLLOW_MON_FIRST && localId <= OBJ_EVENT_ID_FOLLOW_MON_LAST) + if (localId >= OBJ_EVENT_ID_FOLLOW_MON_FIRST && localId <= OBJ_EVENT_ID_FOLLOW_MON_LAST) { // Fast check return TRUE; } - if(graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) + if (IS_FOLLOWMON_GFXID(graphicsId)) return TRUE; return FALSE; @@ -419,20 +419,20 @@ bool8 FollowMon_IsMonObject(struct ObjectEvent* object) void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { - u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; + u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; sFollowMonData.pendingSpawnAnim |= (1 << spawnSlot); } void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { - u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; + u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; sFollowMonData.list[spawnSlot].encounterIndex = 0; } u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) { - u16 slot = graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_0; + u16 slot = graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; u16 species = GetFollowMonSpecies(&sFollowMonData.list[slot]); graphicsId = OBJ_EVENT_MON + species; @@ -502,7 +502,7 @@ static bool8 IsSpawningWaterMons() void RemoveAllFollowMonObjects(void) { for(u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { - if(gObjectEvents[i].graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_0 && gObjectEvents[i].graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) + if(IS_FOLLOWMON_GFXID(gObjectEvents[i].graphicsId)) RemoveObjectEvent(&gObjectEvents[i]); } } From 33f69e5ce85dce26b7a3126af81b84f9e2c43def Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Mon, 30 Jun 2025 05:14:15 +0200 Subject: [PATCH 019/572] Disable grass animations when followmon are moving --- src/event_object_movement.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 6cd369ac137d..cc04e94d7ea1 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -9964,6 +9964,8 @@ void GroundEffect_SpawnOnTallGrass(struct ObjectEvent *objEvent, struct Sprite * void GroundEffect_StepOnTallGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) { + if (IS_FOLLOWMON_GFXID(objEvent->graphicsId)) + return; gFieldEffectArguments[0] = objEvent->currentCoords.x; gFieldEffectArguments[1] = objEvent->currentCoords.y; gFieldEffectArguments[2] = objEvent->previousElevation; @@ -9990,6 +9992,8 @@ void GroundEffect_SpawnOnLongGrass(struct ObjectEvent *objEvent, struct Sprite * void GroundEffect_StepOnLongGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) { + if (IS_FOLLOWMON_GFXID(objEvent->graphicsId)) + return; gFieldEffectArguments[0] = objEvent->currentCoords.x; gFieldEffectArguments[1] = objEvent->currentCoords.y; gFieldEffectArguments[2] = objEvent->previousElevation; From 0597844b140bd845096cf564581d9d2368302c85 Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Tue, 1 Jul 2025 17:53:11 +0200 Subject: [PATCH 020/572] Trying to optimise followmon code --- include/constants/event_objects.h | 14 +-- include/followmon.h | 5 +- src/followmon.c | 169 ++++++++++++++---------------- 3 files changed, 88 insertions(+), 100 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index efdb359b027d..d2c77c97ae0c 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -278,17 +278,17 @@ #define OBJ_EVENT_GFX_VAR_FIRST OBJ_EVENT_GFX_VAR_0 #define OBJ_EVENT_GFX_VAR_LAST OBJ_EVENT_GFX_VAR_F -#define OBJ_EVENT_GFX_FOLLOW_MON_0 (OBJ_EVENT_GFX_VAR_F + 1) -#define OBJ_EVENT_GFX_FOLLOW_MON_1 (OBJ_EVENT_GFX_VAR_F + 2) -#define OBJ_EVENT_GFX_FOLLOW_MON_2 (OBJ_EVENT_GFX_VAR_F + 3) -#define OBJ_EVENT_GFX_FOLLOW_MON_3 (OBJ_EVENT_GFX_VAR_F + 4) -#define OBJ_EVENT_GFX_FOLLOW_MON_4 (OBJ_EVENT_GFX_VAR_F + 5) -#define OBJ_EVENT_GFX_FOLLOW_MON_5 (OBJ_EVENT_GFX_VAR_F + 6) +#define OBJ_EVENT_GFX_FOLLOW_MON_0 512 //we want it to be a power of 2 to check followmon with bit masking +#define OBJ_EVENT_GFX_FOLLOW_MON_1 (OBJ_EVENT_GFX_FOLLOW_MON_0 + 1) +#define OBJ_EVENT_GFX_FOLLOW_MON_2 (OBJ_EVENT_GFX_FOLLOW_MON_0 + 2) +#define OBJ_EVENT_GFX_FOLLOW_MON_3 (OBJ_EVENT_GFX_FOLLOW_MON_0 + 3) +#define OBJ_EVENT_GFX_FOLLOW_MON_4 (OBJ_EVENT_GFX_FOLLOW_MON_0 + 4) +#define OBJ_EVENT_GFX_FOLLOW_MON_5 (OBJ_EVENT_GFX_FOLLOW_MON_0 + 5) #define OBJ_EVENT_GFX_FOLLOW_MON_FIRST OBJ_EVENT_GFX_FOLLOW_MON_0 #define OBJ_EVENT_GFX_FOLLOW_MON_LAST OBJ_EVENT_GFX_FOLLOW_MON_5 -#define IS_FOLLOWMON_GFXID(gfxID) (gfxID >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && gfxID <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) +#define IS_FOLLOWMON_GFXID(gfxID) (gfxID & OBJ_EVENT_GFX_FOLLOW_MON_FIRST) // Don't use (1u << 15) to avoid conflict with BLEND_IMMUNE_FLAG. #define OBJ_EVENT_MON (1u << 14) #define OBJ_EVENT_MON_SHINY (1u << 13) diff --git a/include/followmon.h b/include/followmon.h index d510d0da418a..c23b25667898 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -24,8 +24,9 @@ struct FollowMon struct FollowMonData { - bool8 pendingInterction; - u8 spawnSlot; + bool8 pendingInteraction; + u8 oldestSlot:4; + u8 usedSlots:4; u16 spawnCountdown; u16 pendingSpawnAnim; struct FollowMon list[FOLLOWMON_MAX_SPAWN_SLOTS]; diff --git a/src/followmon.c b/src/followmon.c index 6611a6f809d8..fdd7c2df1939 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -46,6 +46,7 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent) sFollowMonData.list[slot].onWater = MetatileBehavior_IsSurfableWaterOrUnderwater(objectEvent->currentMetatileBehavior); sFollowMonData.spawnCountdown += 60; + sFollowMonData.usedSlots++; } @@ -147,7 +148,7 @@ void FollowMon_OverworldCB(void) else spawnAnimType = FOLLOWMON_SPAWN_ANIM_GRASS; } - // Instantly play a small animation to ground the spawning a bit (Disable for now) + // Instantly play a small animation to ground the spawning a bit MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objectEventId]); sFollowMonData.pendingSpawnAnim &= ~bitFlag; } @@ -160,20 +161,14 @@ static u8 NextSpawnMonSlot(void) { u8 slot; - slot = FOLLOWMON_MAX_SPAWN_SLOTS; - - for(slot = 0; slot < FOLLOWMON_MAX_SPAWN_SLOTS; ++slot) - { - if(sFollowMonData.list[slot].encounterIndex == 0) - break; - } + slot = sFollowMonData.usedSlots; // All mon slots are in use if(slot == FOLLOWMON_MAX_SPAWN_SLOTS) { // Cycle through so we remove the oldest mon first - sFollowMonData.spawnSlot = (sFollowMonData.spawnSlot + 1) % FOLLOWMON_MAX_SPAWN_SLOTS; - slot = sFollowMonData.spawnSlot; + sFollowMonData.oldestSlot = (sFollowMonData.oldestSlot + 1) % FOLLOWMON_MAX_SPAWN_SLOTS; + slot = sFollowMonData.oldestSlot; } // Remove any existing id by this slot @@ -200,82 +195,79 @@ static bool8 TrySelectTile(s16* outX, s16* outY) s16 x, y; u8 closeDistance; - for(tryCount = 0; tryCount < 3; ++tryCount) + // Spawn further away when surfing + if(IsSpawningWaterMons()) + closeDistance = 3; + else + closeDistance = 1; + + // Select a random tile in [-7, -4] [7, 4] range + // Make sure is not directly next to player + do { - // Spawn further away when surfing - if(IsSpawningWaterMons()) - closeDistance = 3; - else - closeDistance = 1; - - // Select a random tile in [-7, -4] [7, 4] range - // Make sure is not directly next to player - do - { - x = (s16)(Random() % 15) - 7; - y = (s16)(Random() % 9) - 4; - } - while (abs(x) <= closeDistance && abs(y) <= closeDistance); + x = (s16)(Random() % 15) - 7; + y = (s16)(Random() % 9) - 4; + } + while (abs(x) <= closeDistance && abs(y) <= closeDistance); - // We won't spawn mons in in the immediate facing direction - // (stops mons spawning in as I'm running in a straight line) - switch (GetPlayerFacingDirection()) - { - case DIR_NORTH: - if(x == 0 && y < 0) - x = -1; - break; - case DIR_SOUTH: - if(x == 0 && y > 0) - x = 1; - break; - - case DIR_EAST: - if(y == 0 && x > 0) - y = -1; - break; - case DIR_WEST: - if(y == 0 && x < 0) - y = 1; - break; - } - - PlayerGetDestCoords(&playerX, &playerY); - x += playerX; - y += playerY; + // We won't spawn mons in in the immediate facing direction + // (stops mons spawning in as I'm running in a straight line) + switch (GetPlayerFacingDirection()) + { + case DIR_NORTH: + if(x == 0 && y < 0) + x = -1; + break; + case DIR_SOUTH: + if(x == 0 && y > 0) + x = 1; + break; + + case DIR_EAST: + if(y == 0 && x > 0) + y = -1; + break; + case DIR_WEST: + if(y == 0 && x < 0) + y = 1; + break; + } + + PlayerGetDestCoords(&playerX, &playerY); + x += playerX; + y += playerY; - elevation = MapGridGetElevationAt(x, y); + elevation = MapGridGetElevationAt(x, y); - if (!IsInsidePlayerMap(x, y)) { - return FALSE; - } - // 0 is change of elevation, 15 is multiple elevation e.g. bridges - // Causes weird interaction issues so just don't let mons spawn here - if (elevation == 0 || elevation == 15) - return FALSE; + if (!IsInsidePlayerMap(x, y)) { + return FALSE; + } + // 0 is change of elevation, 15 is multiple elevation e.g. bridges + // Causes weird interaction issues so just don't let mons spawn here + if (elevation == 0 || elevation == 15) + return FALSE; - tileBehavior = MapGridGetMetatileBehaviorAt(x, y); - if(IsSpawningWaterMons()) + tileBehavior = MapGridGetMetatileBehaviorAt(x, y); + if(IsSpawningWaterMons()) + { + if(MetatileBehavior_IsWaterWildEncounter(tileBehavior) && !MapGridGetCollisionAt(x, y)) { - if(MetatileBehavior_IsWaterWildEncounter(tileBehavior) && !MapGridGetCollisionAt(x, y)) - { - *outX = x; - *outY = y; + *outX = x; + *outY = y; - if(!CheckForObjectEventAtLocation(x, y)) - return TRUE; - } + if(!CheckForObjectEventAtLocation(x, y)) + return TRUE; } - else + } + else + { + if(MetatileBehavior_IsLandWildEncounter(tileBehavior) && !MapGridGetCollisionAt(x, y)) { - if(MetatileBehavior_IsLandWildEncounter(tileBehavior) && !MapGridGetCollisionAt(x, y)) - { - *outX = x; - *outY = y; + *outX = x; + *outY = y; - if(!CheckForObjectEventAtLocation(x, y)) - return TRUE; - } + if(!CheckForObjectEventAtLocation(x, y)) + return TRUE; } } @@ -331,17 +323,17 @@ bool8 FollowMon_ProcessMonInteraction(void) if(VarGet(VAR_REPEL_STEP_COUNT) != 0) { // Never auto trigger battle whilst repel is active - sFollowMonData.pendingInterction = FALSE; + sFollowMonData.pendingInteraction = FALSE; return FALSE; } - if(sFollowMonData.pendingInterction) + if(sFollowMonData.pendingInteraction) { u8 i; struct ObjectEvent *curObject; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - sFollowMonData.pendingInterction = FALSE; + sFollowMonData.pendingInteraction = FALSE; for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { @@ -372,22 +364,21 @@ bool8 FollowMon_ProcessMonInteraction(void) bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider) { - struct ObjectEvent* player = &gObjectEvents[gPlayerAvatar.objectEventId]; - if (collider == player) + if (collider->isPlayer) { // Player can walk on top of follow mon if(FollowMon_IsMonObject(obstacle)) { - sFollowMonData.pendingInterction = TRUE; + sFollowMonData.pendingInteraction = TRUE; return TRUE; } } - else if(obstacle == player) + else if(obstacle->isPlayer) { // Follow mon can walk onto player if(FollowMon_IsMonObject(collider)) { - sFollowMonData.pendingInterction = TRUE; + sFollowMonData.pendingInteraction = TRUE; return TRUE; } } else if(!FollowMon_IsMonObject(collider) && FollowMon_IsMonObject(obstacle)) @@ -404,12 +395,6 @@ bool8 FollowMon_IsMonObject(struct ObjectEvent* object) u16 localId = object->localId; u16 graphicsId = object->graphicsId; - if (localId >= OBJ_EVENT_ID_FOLLOW_MON_FIRST && localId <= OBJ_EVENT_ID_FOLLOW_MON_LAST) - { - // Fast check - return TRUE; - } - if (IS_FOLLOWMON_GFXID(graphicsId)) return TRUE; @@ -420,7 +405,7 @@ bool8 FollowMon_IsMonObject(struct ObjectEvent* object) void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; - + sFollowMonData.usedSlots++; sFollowMonData.pendingSpawnAnim |= (1 << spawnSlot); } @@ -428,6 +413,7 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; sFollowMonData.list[spawnSlot].encounterIndex = 0; + sFollowMonData.usedSlots--; } u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) @@ -444,6 +430,7 @@ u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) void FollowMon_OnWarp(void) { sFollowMonData.spawnCountdown = 0; + sFollowMonData.usedSlots = 0; for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { sFollowMonData.list[i].encounterIndex = 0; } From f6b497608f798e304a189492dcd06db2ef478531 Mon Sep 17 00:00:00 2001 From: Jamie Foster Date: Tue, 1 Jul 2025 18:10:35 +0200 Subject: [PATCH 021/572] Reduce animation when moving to grass --- src/event_object_movement.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index cc04e94d7ea1..bd07f10191f7 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -9973,7 +9973,7 @@ void GroundEffect_StepOnTallGrass(struct ObjectEvent *objEvent, struct Sprite *s gFieldEffectArguments[4] = objEvent->localId << 8 | objEvent->mapNum; gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; - gFieldEffectArguments[7] = FALSE; // don't skip to end of anim + gFieldEffectArguments[7] = TRUE; // don't skip to end of anim FieldEffectStart(FLDEFF_TALL_GRASS); } @@ -9986,7 +9986,7 @@ void GroundEffect_SpawnOnLongGrass(struct ObjectEvent *objEvent, struct Sprite * gFieldEffectArguments[4] = objEvent->localId << 8 | objEvent->mapNum; gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; - gFieldEffectArguments[7] = 1; + gFieldEffectArguments[7] = TRUE; FieldEffectStart(FLDEFF_LONG_GRASS); } @@ -10001,7 +10001,7 @@ void GroundEffect_StepOnLongGrass(struct ObjectEvent *objEvent, struct Sprite *s gFieldEffectArguments[4] = (objEvent->localId << 8) | objEvent->mapNum; gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; - gFieldEffectArguments[7] = 0; + gFieldEffectArguments[7] = TRUE; FieldEffectStart(FLDEFF_LONG_GRASS); } From 831188c81bccd3dd2ea3b73aad5803a3e80b2ee0 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:49:04 -0600 Subject: [PATCH 022/572] fixed "skip to end of anim" comment --- src/event_object_movement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index bd07f10191f7..869b6e53c245 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -9973,7 +9973,7 @@ void GroundEffect_StepOnTallGrass(struct ObjectEvent *objEvent, struct Sprite *s gFieldEffectArguments[4] = objEvent->localId << 8 | objEvent->mapNum; gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; - gFieldEffectArguments[7] = TRUE; // don't skip to end of anim + gFieldEffectArguments[7] = TRUE; // skip to end of anim FieldEffectStart(FLDEFF_TALL_GRASS); } From 8ccf771ac73fe98ed32b8e193648610103f66552 Mon Sep 17 00:00:00 2001 From: Phantonomy <131238004+Ddaretrogamer@users.noreply.github.com> Date: Fri, 4 Jul 2025 11:07:22 -0500 Subject: [PATCH 023/572] Updating OW mons spawns and adding sparkle effect --- graphics/field_effects/pics/shiny_sparkle.png | Bin 0 -> 606 bytes include/constants/field_effects.h | 1 + .../field_effect_object_template_pointers.h | 2 + src/data/field_effects/field_effect_objects.h | 38 ++++++++++++++++++ .../object_events/object_event_graphics.h | 1 + src/field_effect_helpers.c | 2 +- src/followmon.c | 12 +++++- 7 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 graphics/field_effects/pics/shiny_sparkle.png diff --git a/graphics/field_effects/pics/shiny_sparkle.png b/graphics/field_effects/pics/shiny_sparkle.png new file mode 100644 index 0000000000000000000000000000000000000000..537a5d6a5f2e39aae8c5a25dff0e36653ef53dfa GIT binary patch literal 606 zcmV-k0-^nhP)F? z7Ayw{6)Lv?000+sMObuGZ)S9NVRB^vM@&RePDdbiWpW@hE;Mn4q~QPn00?w&PDe*f zL^A*Y002sWDIWj;00d`2O+f$vv5yPQ5eyFqe+bg81UMGR>2e-R zfN!;XcZ-$!&+(X=SX}M(`A(+%YR~S(E7OVq)64xiC^#hH$GRD=m?;e|FjZVW= maxSpawns) { // Cycle through so we remove the oldest mon first - sFollowMonData.oldestSlot = (sFollowMonData.oldestSlot + 1) % FOLLOWMON_MAX_SPAWN_SLOTS; + sFollowMonData.oldestSlot = (sFollowMonData.oldestSlot + 1) % maxSpawns; slot = sFollowMonData.oldestSlot; } From eb5cb5a4a1bf2f1e9984543544c8d6b264a747ad Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:01:07 -0600 Subject: [PATCH 024/572] a bit of a cleanup --- src/followmon.c | 87 ++++++++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/src/followmon.c b/src/followmon.c index 08bed46e66d7..9fb898df1d98 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -17,8 +17,6 @@ #include "sound.h" #include "wild_encounter.h" - - static EWRAM_DATA struct FollowMonData sFollowMonData = { 0 }; static u8 CountActiveFollowMon(); @@ -49,16 +47,18 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent) sFollowMonData.usedSlots++; } - void FollowMon_OverworldCB(void) { - if (!FlagGet(OW_FLAG_SPAWN_OVERWORLD_MON)) { + if (!FlagGet(OW_FLAG_SPAWN_OVERWORLD_MON)) + { RemoveAllFollowMonObjects(); // Zero sFollowMonData ; u8 *raw = (u8 *)&sFollowMonData; - for (u32 i = 0; i < sizeof(struct FollowMonData); i++) { + for (u32 i = 0; i < sizeof(struct FollowMonData); i++) + { raw[i] = 0; } + return; } @@ -66,6 +66,7 @@ void FollowMon_OverworldCB(void) { s16 x, y; const struct WildPokemonInfo *wildMonInfo = NULL; + wildMonInfo = GetActiveEncounterTable(IsSpawningWaterMons()); if(wildMonInfo && IsSafeToSpawnObjectEvents() && TrySelectTile(&x, &y)) @@ -98,9 +99,7 @@ void FollowMon_OverworldCB(void) // Hide reflections for spawns in water // (It just looks weird) if(IsSpawningWaterMons()) - { - gObjectEvents[objectEventId].hideReflection = TRUE; - } + gObjectEvents[objectEventId].hideReflection = TRUE; // Slower replacement spawning sFollowMonData.spawnCountdown = 60 * (3 + Random() % 2); @@ -156,6 +155,7 @@ void FollowMon_OverworldCB(void) } } } + static u8 GetMaxFollowMonSpawns(void) { if (IsSpawningWaterMons() || gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) @@ -185,9 +185,7 @@ static u8 NextSpawnMonSlot(void) // Check that we don't have too many sprites on screen before spawning // (lag reduction) if(CountActiveObjectEvents() >= FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT) - { return INVALID_SPAWN_SLOT; - } GenerateFollowMon(&sFollowMonData.list[slot], IsSpawningWaterMons()); @@ -230,7 +228,6 @@ static bool8 TrySelectTile(s16* outX, s16* outY) if(x == 0 && y > 0) x = 1; break; - case DIR_EAST: if(y == 0 && x > 0) y = -1; @@ -247,9 +244,9 @@ static bool8 TrySelectTile(s16* outX, s16* outY) elevation = MapGridGetElevationAt(x, y); - if (!IsInsidePlayerMap(x, y)) { + if (!IsInsidePlayerMap(x, y)) return FALSE; - } + // 0 is change of elevation, 15 is multiple elevation e.g. bridges // Causes weird interaction issues so just don't let mons spawn here if (elevation == 0 || elevation == 15) @@ -289,20 +286,27 @@ void CreateFollowMonEncounter(void) { if(objEventId < OBJECT_EVENTS_COUNT) { - curObject = &gObjectEvents[objEventId]; - if(!FollowMon_IsMonObject(curObject)) - return; - } else + curObject = &gObjectEvents[objEventId]; + if(!FollowMon_IsMonObject(curObject)) + return; + } + else + { return; + } const struct WildPokemonInfo *wildMonInfo; u32 headerId = GetCurrentMapWildMonHeaderId(); u8 index = curObject->sEncounterIndex - 1; u8 level = 0; - if (MetatileBehavior_IsSurfableWaterOrUnderwater(curObject->currentMetatileBehavior)) { + + if (MetatileBehavior_IsSurfableWaterOrUnderwater(curObject->currentMetatileBehavior)) + { wildMonInfo = gWildMonHeaders[headerId].encounterTypes[curObject->spawnTimeOfDay].waterMonsInfo; level = ChooseWildMonLevel(wildMonInfo->wildPokemon, index, WILD_AREA_WATER); - } else { + } + else + { wildMonInfo = gWildMonHeaders[headerId].encounterTypes[curObject->spawnTimeOfDay].landMonsInfo; level = ChooseWildMonLevel(wildMonInfo->wildPokemon,index, WILD_AREA_LAND); } @@ -324,8 +328,6 @@ void CreateFollowMonEncounter(void) { SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); } - - bool8 FollowMon_ProcessMonInteraction(void) { if(VarGet(VAR_REPEL_STEP_COUNT) != 0) @@ -389,13 +391,14 @@ bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEve sFollowMonData.pendingInteraction = TRUE; return TRUE; } - } else if(!FollowMon_IsMonObject(collider) && FollowMon_IsMonObject(obstacle)) + } + else if(!FollowMon_IsMonObject(collider) && FollowMon_IsMonObject(obstacle)) { // Other objects can walk through follow mons, whilst wandering mons is active return TRUE; } - return FALSE; + return FALSE; } bool8 FollowMon_IsMonObject(struct ObjectEvent* object) @@ -409,7 +412,6 @@ bool8 FollowMon_IsMonObject(struct ObjectEvent* object) return FALSE; } - void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; @@ -432,6 +434,7 @@ u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) graphicsId = OBJ_EVENT_MON + species; if (sFollowMonData.list[slot].isShiny) graphicsId += OBJ_EVENT_MON_SHINY; + return graphicsId; } @@ -439,7 +442,9 @@ void FollowMon_OnWarp(void) { sFollowMonData.spawnCountdown = 0; sFollowMonData.usedSlots = 0; - for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { + + for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) + { sFollowMonData.list[i].encounterIndex = 0; } } @@ -448,11 +453,12 @@ static u16 GetFollowMonSpecies(struct FollowMon *followMon) { u16 species = 0; u32 headerId = GetCurrentMapWildMonHeaderId(); - if (followMon->onWater) { + + if (followMon->onWater) species = gWildMonHeaders[headerId].encounterTypes[followMon->timeOfDay].waterMonsInfo->wildPokemon[followMon->encounterIndex - 1].species; - } else { + else species = gWildMonHeaders[headerId].encounterTypes[followMon->timeOfDay].landMonsInfo->wildPokemon[followMon->encounterIndex - 1].species; - } + return species; } @@ -495,8 +501,10 @@ static bool8 IsSpawningWaterMons() return (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER)); } -void RemoveAllFollowMonObjects(void) { - for(u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { +void RemoveAllFollowMonObjects(void) +{ + for(u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) + { if(IS_FOLLOWMON_GFXID(gObjectEvents[i].graphicsId)) RemoveObjectEvent(&gObjectEvents[i]); } @@ -505,12 +513,11 @@ void RemoveAllFollowMonObjects(void) { static u8 FindObjectEventForGfx(u16 gfxId) { u8 i; + for(i = 0; i < OBJECT_EVENTS_COUNT; ++i) { if(gObjectEvents[i].active && gObjectEvents[i].graphicsId == gfxId) - { return i; - } } return OBJECT_EVENTS_COUNT; @@ -519,12 +526,11 @@ static u8 FindObjectEventForGfx(u16 gfxId) static bool8 CheckForObjectEventAtLocation(s16 x, s16 y) { u8 i; + for(i = 0; i < OBJECT_EVENTS_COUNT; ++i) { if(gObjectEvents[i].active && gObjectEvents[i].currentCoords.x == x && gObjectEvents[i].currentCoords.y == y) - { return TRUE; - } } return FALSE; @@ -533,11 +539,14 @@ static bool8 CheckForObjectEventAtLocation(s16 x, s16 y) static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater) { u32 headerId = GetCurrentMapWildMonHeaderId(); + if (headerId == HEADER_NONE) return NULL; + enum TimeOfDay timeOfDay; - if (onWater) { + if (onWater) + { timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); return gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; } @@ -560,17 +569,19 @@ static bool8 AreElevationsCompatible(u8 a, u8 b) static bool8 IsInsidePlayerMap(s16 x, s16 y) { s32 width, height; + GetMapSize(&width, &height); - if (x >= 0 && x <= width && y >= 0 && y <= height) { + if (x >= 0 && x <= width && y >= 0 && y <= height) return TRUE; - } + return FALSE; } static void GetMapSize(s32 *width, s32 *height) { const struct MapLayout *layout; + layout = Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->mapLayout; *width = layout->width; *height = layout->height; -} \ No newline at end of file +} From 6c770118760dfcd750394e8c2425396d2e9acece Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 20 Nov 2025 17:36:34 -0600 Subject: [PATCH 025/572] refactored slot system --- include/followmon.h | 4 ++-- src/followmon.c | 46 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/include/followmon.h b/include/followmon.h index c23b25667898..f963b6d7af23 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -15,9 +15,9 @@ struct FollowMon u16 isShiny:1; u16 onWater:1; u16 timeOfDay:2; - u16 unused:4; + u16 age:4; u16 encounterIndex:8; - + u16 unused:8; }; #define EMPTY_FOLLOWMON 0xFF; diff --git a/src/followmon.c b/src/followmon.c index 9fb898df1d98..231b66bbfdad 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -164,19 +164,42 @@ static u8 GetMaxFollowMonSpawns(void) return 3; } +static u32 GetNewOldestSlot(u32 oldSlot) +{ + u32 i; + u32 nextOldest = FOLLOWMON_MAX_SPAWN_SLOTS; + + for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) + { + if (sFollowMonData.list[i].encounterIndex != 0) + { + if (i != oldSlot && (nextOldest == FOLLOWMON_MAX_SPAWN_SLOTS || sFollowMonData.list[i].age > sFollowMonData.list[nextOldest].age)) + nextOldest = i; + } + } + + return nextOldest; +} + static u8 NextSpawnMonSlot(void) { - u8 slot; + u8 slot = 0; u8 maxSpawns = GetMaxFollowMonSpawns(); - slot = sFollowMonData.usedSlots; - // All mon slots are in use - if(slot >= maxSpawns) + if(CountActiveFollowMon() + 1 >= maxSpawns) { // Cycle through so we remove the oldest mon first - sFollowMonData.oldestSlot = (sFollowMonData.oldestSlot + 1) % maxSpawns; - slot = sFollowMonData.oldestSlot; + slot = sFollowMonData.oldestSlot; + sFollowMonData.oldestSlot = GetNewOldestSlot(slot); + } + else + { + for (slot = 0; slot < maxSpawns; slot++) + { + if (sFollowMonData.list[slot].encounterIndex == 0) + break; + } } // Remove any existing id by this slot @@ -414,15 +437,24 @@ bool8 FollowMon_IsMonObject(struct ObjectEvent* object) void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { + u32 i; u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; sFollowMonData.usedSlots++; sFollowMonData.pendingSpawnAnim |= (1 << spawnSlot); + + // Increase the age of all followmons + for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) + { + if (sFollowMonData.list[i].encounterIndex != 0) + sFollowMonData.list[i].age++; + } } void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; sFollowMonData.list[spawnSlot].encounterIndex = 0; + sFollowMonData.list[spawnSlot].age = 0; sFollowMonData.usedSlots--; } @@ -442,10 +474,12 @@ void FollowMon_OnWarp(void) { sFollowMonData.spawnCountdown = 0; sFollowMonData.usedSlots = 0; + sFollowMonData.oldestSlot = 0; for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { sFollowMonData.list[i].encounterIndex = 0; + sFollowMonData.list[i].age = 0; } } From 9eac651fe2ba37e96ecaed73b3063976d6c4d072 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 20 Nov 2025 23:41:47 +0000 Subject: [PATCH 026/572] Add Configs for OW Encounters --- include/config/overworld.h | 6 ++++-- include/constants/flags.h | 6 +++--- include/followmon.h | 4 +++- src/field_control_avatar.c | 2 +- src/followmon.c | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index bd9f5b23e6c7..5d2a6516e752 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -54,7 +54,9 @@ // Compressed gfx are incompatible with non-power-of-two sprite sizes: // (You should not use 48x48 sprites/tables for compressed gfx) // 16x32, 32x32, 64x64 etc are fine -#define OW_MON_WANDER_WALK TRUE // If true, OW pokemon with MOVEMENT_TYPE_WANDER will walk-in-place in between steps. +#define OW_MON_WANDER_WALK TRUE // If true, OW Pokémon with MOVEMENT_TYPE_WANDER will walk-in-place in between steps. +#define OW_VANILLA_WILD_ENCOUNTERS TRUE // If true, Pokémon can spawn on tiles that can trigger wild encounters. +#define OW_SPAWN_OW_WILD_ENCOUNTERS FALSE // If true, OW Pokémon can spawn as wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. // Follower Pokémon #define OW_FOLLOWERS_ENABLED FALSE // Enables follower Pokémon, HGSS style. Requires OW_POKEMON_OBJECT_EVENTS. Note that additional scripting may be required for them to be fully supported! #define OW_FOLLOWERS_BOBBING TRUE // If TRUE, follower Pokémon will bob up and down during their idle & walking animations @@ -106,7 +108,7 @@ // To use the following features in scripting, replace the 0s with the flag ID you're assigning it to. // Eg: Replace with FLAG_UNUSED_0x264 so you can use that flag to toggle the feature. #define OW_FLAG_PAUSE_TIME 0 // If this flag is set and OW_USE_FAKE_RTC is enabled, seconds on the in-game clock will not advance. -#define OW_FLAG_NO_ENCOUNTER FLAG_OW_NO_ENCOUNTER // If this flag is set, wild encounters will be disabled. +#define OW_FLAG_NO_ENCOUNTER 0 // If this flag is set, wild encounters will be disabled. #define OW_FLAG_NO_TRAINER_SEE 0 // If this flag is set, trainers will not battle the player unless they're talked to. #define OW_FLAG_NO_COLLISION 0 // If this flag is set, the player will be able to walk over tiles with collision. Mainly intended for debugging purposes. #define OW_FLAG_POKE_RIDER 0 // If this flag is set, the player will be able to use fly from the Pokenav Region Map and the Town Map key item by pressing 'R' on a city/location they are able to fly to. diff --git a/include/constants/flags.h b/include/constants/flags.h index 86ec982cb316..47a3b245e192 100644 --- a/include/constants/flags.h +++ b/include/constants/flags.h @@ -43,7 +43,7 @@ #define TEMP_FLAGS_END FLAG_TEMP_1F #define NUM_TEMP_FLAGS (TEMP_FLAGS_END - TEMP_FLAGS_START + 1) -#define FLAG_UNUSED_0x020 0x20 // followmon (OWencounter flag) +#define FLAG_UNUSED_0x020 0x20 // Unused Flag #define FLAG_UNUSED_0x021 0x21 // Unused Flag #define FLAG_UNUSED_0x022 0x22 // Unused Flag #define FLAG_UNUSED_0x023 0x23 // Unused Flag @@ -675,8 +675,8 @@ #define FLAG_UNUSED_0x274 0x274 // Unused Flag #define FLAG_UNUSED_0x275 0x275 // Unused Flag #define FLAG_UNUSED_0x276 0x276 // Unused Flag -#define FLAG_DISABLE_FOLLOWERS 0x277 // Follower toggle -#define FLAG_OW_NO_ENCOUNTER 0x278 // OW_FLAG_NO_ENCOUNTER +#define FLAG_UNUSED_0x277 0x277 // Unused Flag +#define FLAG_UNUSED_0x278 0x278 // Unused Flag #define FLAG_UNUSED_0x279 0x279 // Unused Flag #define FLAG_UNUSED_0x27A 0x27A // Unused Flag #define FLAG_UNUSED_0x27B 0x27B // Unused Flag diff --git a/include/followmon.h b/include/followmon.h index c23b25667898..cef026337c2e 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -1,7 +1,9 @@ #ifndef GUARD_FOLLOWMON_H #define GUARD_FOLLOWMON_H -#define OW_FLAG_SPAWN_OVERWORLD_MON FLAG_UNUSED_0x020 +#if OW_POKEMON_OBJECT_EVENTS == FALSE && OW_SPAWN_OW_WILD_ENCOUNTERS == TRUE +#error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OW_SPAWN_OW_WILD_ENCOUNTERS to work." +#endif #define FOLLOWMON_SHINY_OFFSET 10000 #define FOLLOWMON_MAX_SPAWN_SLOTS 5 diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index ddb1ce0c5ddb..2f49f16c3c8b 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -823,7 +823,7 @@ void RestartWildEncounterImmunitySteps(void) static bool8 CheckStandardWildEncounter(u16 metatileBehavior) { - if (FlagGet(OW_FLAG_NO_ENCOUNTER)) + if (FlagGet(OW_FLAG_NO_ENCOUNTER) || OW_VANILLA_WILD_ENCOUNTERS == FALSE) return FALSE; if (sWildEncounterImmunitySteps < 4) diff --git a/src/followmon.c b/src/followmon.c index 08bed46e66d7..51f8609b1a3c 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -52,7 +52,7 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent) void FollowMon_OverworldCB(void) { - if (!FlagGet(OW_FLAG_SPAWN_OVERWORLD_MON)) { + if (!OW_SPAWN_OW_WILD_ENCOUNTERS) { RemoveAllFollowMonObjects(); // Zero sFollowMonData ; u8 *raw = (u8 *)&sFollowMonData; From 11156d29765af96644e2263f0b61dedeffe6163e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 20 Nov 2025 23:55:16 +0000 Subject: [PATCH 027/572] padding --- include/followmon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/followmon.h b/include/followmon.h index d56af7988da0..efefe2e54964 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -19,7 +19,7 @@ struct FollowMon u16 timeOfDay:2; u16 age:4; u16 encounterIndex:8; - u16 unused:8; + u16 padding; }; #define EMPTY_FOLLOWMON 0xFF; From 9f0bd52058072f4e1dfe04b9d92bbe3bc69f727e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 21 Nov 2025 00:01:02 +0000 Subject: [PATCH 028/572] Make sFollowMonData a pointer to EWRAM --- src/followmon.c | 94 +++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 49 deletions(-) diff --git a/src/followmon.c b/src/followmon.c index 62aa3cc722a1..bcc902c03cf4 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -9,6 +9,7 @@ #include "followmon.h" #include "fieldmap.h" #include "field_player_avatar.h" +#include "malloc.h" #include "metatile_behavior.h" #include "overworld.h" #include "random.h" @@ -17,7 +18,7 @@ #include "sound.h" #include "wild_encounter.h" -static EWRAM_DATA struct FollowMonData sFollowMonData = { 0 }; +static EWRAM_DATA struct FollowMonData *sFollowMonData = NULL; static u8 CountActiveFollowMon(); static bool8 TrySelectTile(s16* outX, s16* outY); @@ -38,13 +39,13 @@ static void GetMapSize(s32 *width, s32 *height); void LoadFollowMonData(struct ObjectEvent *objectEvent) { u8 slot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; - sFollowMonData.list[slot].isShiny = objectEvent->shiny; - sFollowMonData.list[slot].timeOfDay = objectEvent->spawnTimeOfDay; - sFollowMonData.list[slot].encounterIndex = objectEvent->sEncounterIndex; - sFollowMonData.list[slot].onWater = MetatileBehavior_IsSurfableWaterOrUnderwater(objectEvent->currentMetatileBehavior); + sFollowMonData->list[slot].isShiny = objectEvent->shiny; + sFollowMonData->list[slot].timeOfDay = objectEvent->spawnTimeOfDay; + sFollowMonData->list[slot].encounterIndex = objectEvent->sEncounterIndex; + sFollowMonData->list[slot].onWater = MetatileBehavior_IsSurfableWaterOrUnderwater(objectEvent->currentMetatileBehavior); - sFollowMonData.spawnCountdown += 60; - sFollowMonData.usedSlots++; + sFollowMonData->spawnCountdown += 60; + sFollowMonData->usedSlots++; } void FollowMon_OverworldCB(void) @@ -53,16 +54,11 @@ void FollowMon_OverworldCB(void) { RemoveAllFollowMonObjects(); // Zero sFollowMonData ; - u8 *raw = (u8 *)&sFollowMonData; - for (u32 i = 0; i < sizeof(struct FollowMonData); i++) - { - raw[i] = 0; - } - + sFollowMonData = AllocZeroed(sizeof(struct FollowMonData)); return; } - if(sFollowMonData.spawnCountdown == 0) + if(sFollowMonData->spawnCountdown == 0) { s16 x, y; const struct WildPokemonInfo *wildMonInfo = NULL; @@ -91,7 +87,7 @@ void FollowMon_OverworldCB(void) // Only used for save/load as well as loading encounters, // Most of the time, followmon data is tracked in sFollowMonData - const struct FollowMon *followMon = &sFollowMonData.list[spawnSlot]; + const struct FollowMon *followMon = &sFollowMonData->list[spawnSlot]; gObjectEvents[objectEventId].shiny = followMon->isShiny; gObjectEvents[objectEventId].spawnTimeOfDay = followMon->timeOfDay; gObjectEvents[objectEventId].sEncounterIndex = followMon->encounterIndex; @@ -102,17 +98,17 @@ void FollowMon_OverworldCB(void) gObjectEvents[objectEventId].hideReflection = TRUE; // Slower replacement spawning - sFollowMonData.spawnCountdown = 60 * (3 + Random() % 2); + sFollowMonData->spawnCountdown = 60 * (3 + Random() % 2); } } } else { - --sFollowMonData.spawnCountdown; + --sFollowMonData->spawnCountdown; } // Play spawn animation when player is close enough - if(sFollowMonData.pendingSpawnAnim != 0) + if(sFollowMonData->pendingSpawnAnim != 0) { u16 spawnSlot; u16 gfxId; @@ -125,21 +121,21 @@ void FollowMon_OverworldCB(void) spawnSlot = gfxId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; bitFlag = (1 << spawnSlot); - if((sFollowMonData.pendingSpawnAnim & bitFlag) != 0) + if((sFollowMonData->pendingSpawnAnim & bitFlag) != 0) { objectEventId = FindObjectEventForGfx(gfxId); if(objectEventId != OBJECT_EVENTS_COUNT) { - if(sFollowMonData.list[spawnSlot].isShiny) + if(sFollowMonData->list[spawnSlot].isShiny) { PlaySE(SE_SHINY); spawnAnimType = FOLLOWMON_SPAWN_ANIM_SHINY; - sFollowMonData.pendingSpawnAnim &= ~bitFlag; + sFollowMonData->pendingSpawnAnim &= ~bitFlag; } else { - PlayCry_Normal(GetFollowMonSpecies(&sFollowMonData.list[spawnSlot]), 25); + PlayCry_Normal(GetFollowMonSpecies(&sFollowMonData->list[spawnSlot]), 25); if (IsSpawningWaterMons()) spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) @@ -149,7 +145,7 @@ void FollowMon_OverworldCB(void) } // Instantly play a small animation to ground the spawning a bit MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objectEventId]); - sFollowMonData.pendingSpawnAnim &= ~bitFlag; + sFollowMonData->pendingSpawnAnim &= ~bitFlag; } } } @@ -171,9 +167,9 @@ static u32 GetNewOldestSlot(u32 oldSlot) for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - if (sFollowMonData.list[i].encounterIndex != 0) + if (sFollowMonData->list[i].encounterIndex != 0) { - if (i != oldSlot && (nextOldest == FOLLOWMON_MAX_SPAWN_SLOTS || sFollowMonData.list[i].age > sFollowMonData.list[nextOldest].age)) + if (i != oldSlot && (nextOldest == FOLLOWMON_MAX_SPAWN_SLOTS || sFollowMonData->list[i].age > sFollowMonData->list[nextOldest].age)) nextOldest = i; } } @@ -190,14 +186,14 @@ static u8 NextSpawnMonSlot(void) if(CountActiveFollowMon() + 1 >= maxSpawns) { // Cycle through so we remove the oldest mon first - slot = sFollowMonData.oldestSlot; - sFollowMonData.oldestSlot = GetNewOldestSlot(slot); + slot = sFollowMonData->oldestSlot; + sFollowMonData->oldestSlot = GetNewOldestSlot(slot); } else { for (slot = 0; slot < maxSpawns; slot++) { - if (sFollowMonData.list[slot].encounterIndex == 0) + if (sFollowMonData->list[slot].encounterIndex == 0) break; } } @@ -210,7 +206,7 @@ static u8 NextSpawnMonSlot(void) if(CountActiveObjectEvents() >= FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT) return INVALID_SPAWN_SLOT; - GenerateFollowMon(&sFollowMonData.list[slot], IsSpawningWaterMons()); + GenerateFollowMon(&sFollowMonData->list[slot], IsSpawningWaterMons()); return slot; } @@ -356,17 +352,17 @@ bool8 FollowMon_ProcessMonInteraction(void) if(VarGet(VAR_REPEL_STEP_COUNT) != 0) { // Never auto trigger battle whilst repel is active - sFollowMonData.pendingInteraction = FALSE; + sFollowMonData->pendingInteraction = FALSE; return FALSE; } - if(sFollowMonData.pendingInteraction) + if(sFollowMonData->pendingInteraction) { u8 i; struct ObjectEvent *curObject; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - sFollowMonData.pendingInteraction = FALSE; + sFollowMonData->pendingInteraction = FALSE; for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { @@ -402,7 +398,7 @@ bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEve // Player can walk on top of follow mon if(FollowMon_IsMonObject(obstacle)) { - sFollowMonData.pendingInteraction = TRUE; + sFollowMonData->pendingInteraction = TRUE; return TRUE; } } @@ -411,7 +407,7 @@ bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEve // Follow mon can walk onto player if(FollowMon_IsMonObject(collider)) { - sFollowMonData.pendingInteraction = TRUE; + sFollowMonData->pendingInteraction = TRUE; return TRUE; } } @@ -439,32 +435,32 @@ void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { u32 i; u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; - sFollowMonData.usedSlots++; - sFollowMonData.pendingSpawnAnim |= (1 << spawnSlot); + sFollowMonData->usedSlots++; + sFollowMonData->pendingSpawnAnim |= (1 << spawnSlot); // Increase the age of all followmons for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - if (sFollowMonData.list[i].encounterIndex != 0) - sFollowMonData.list[i].age++; + if (sFollowMonData->list[i].encounterIndex != 0) + sFollowMonData->list[i].age++; } } void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; - sFollowMonData.list[spawnSlot].encounterIndex = 0; - sFollowMonData.list[spawnSlot].age = 0; - sFollowMonData.usedSlots--; + sFollowMonData->list[spawnSlot].encounterIndex = 0; + sFollowMonData->list[spawnSlot].age = 0; + sFollowMonData->usedSlots--; } u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) { u16 slot = graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; - u16 species = GetFollowMonSpecies(&sFollowMonData.list[slot]); + u16 species = GetFollowMonSpecies(&sFollowMonData->list[slot]); graphicsId = OBJ_EVENT_MON + species; - if (sFollowMonData.list[slot].isShiny) + if (sFollowMonData->list[slot].isShiny) graphicsId += OBJ_EVENT_MON_SHINY; return graphicsId; @@ -472,14 +468,14 @@ u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) void FollowMon_OnWarp(void) { - sFollowMonData.spawnCountdown = 0; - sFollowMonData.usedSlots = 0; - sFollowMonData.oldestSlot = 0; + sFollowMonData->spawnCountdown = 0; + sFollowMonData->usedSlots = 0; + sFollowMonData->oldestSlot = 0; for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - sFollowMonData.list[i].encounterIndex = 0; - sFollowMonData.list[i].age = 0; + sFollowMonData->list[i].encounterIndex = 0; + sFollowMonData->list[i].age = 0; } } @@ -509,7 +505,7 @@ static u8 CountActiveFollowMon() u8 count = 0; for(u8 slot = 0; slot < FOLLOWMON_MAX_SPAWN_SLOTS; slot++) { - if(sFollowMonData.list[slot].encounterIndex) + if(sFollowMonData->list[slot].encounterIndex) count++; } From fb6c67ed65679113c47e5629945df1418f220acb Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 21 Nov 2025 00:10:55 +0000 Subject: [PATCH 029/572] Free sFollowMonData when removing follower objects --- include/followmon.h | 1 + src/followmon.c | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/followmon.h b/include/followmon.h index efefe2e54964..3b28c8d8044a 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -48,5 +48,6 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId); void FollowMon_OnWarp(void); void RemoveAllFollowMonObjects(void); +void FreeFollowMonData(void); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/followmon.c b/src/followmon.c index bcc902c03cf4..e8d443af672b 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -48,17 +48,27 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent) sFollowMonData->usedSlots++; } +void FreeFollowMonData(void) +{ + if (sFollowMonData != NULL) + { + Free(sFollowMonData); + sFollowMonData = NULL; + } +} + void FollowMon_OverworldCB(void) { if (!OW_SPAWN_OW_WILD_ENCOUNTERS) { RemoveAllFollowMonObjects(); - // Zero sFollowMonData ; - sFollowMonData = AllocZeroed(sizeof(struct FollowMonData)); return; } - if(sFollowMonData->spawnCountdown == 0) + if (sFollowMonData == NULL) + sFollowMonData = AllocZeroed(sizeof(struct FollowMonData)); + + if (sFollowMonData->spawnCountdown == 0) { s16 x, y; const struct WildPokemonInfo *wildMonInfo = NULL; @@ -538,6 +548,7 @@ void RemoveAllFollowMonObjects(void) if(IS_FOLLOWMON_GFXID(gObjectEvents[i].graphicsId)) RemoveObjectEvent(&gObjectEvents[i]); } + FreeFollowMonData(); } static u8 FindObjectEventForGfx(u16 gfxId) From ef9974ea6bd829ec762043a6fd02f417bc8d8f44 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:22:27 -0600 Subject: [PATCH 030/572] Fix bug where it spawns 1 less than it should --- src/followmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/followmon.c b/src/followmon.c index e8d443af672b..c6f4851df8a4 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -193,7 +193,7 @@ static u8 NextSpawnMonSlot(void) u8 maxSpawns = GetMaxFollowMonSpawns(); // All mon slots are in use - if(CountActiveFollowMon() + 1 >= maxSpawns) + if(CountActiveFollowMon() >= maxSpawns) { // Cycle through so we remove the oldest mon first slot = sFollowMonData->oldestSlot; From 0ac54d116c5801759ad463b715d1c866e3a5ee3e Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 21 Nov 2025 19:45:10 -0600 Subject: [PATCH 031/572] Revert "Free sFollowMonData when removing follower objects" This reverts commit fb6c67ed65679113c47e5629945df1418f220acb. --- include/followmon.h | 1 - src/followmon.c | 17 +++-------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/include/followmon.h b/include/followmon.h index 3b28c8d8044a..efefe2e54964 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -48,6 +48,5 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId); void FollowMon_OnWarp(void); void RemoveAllFollowMonObjects(void); -void FreeFollowMonData(void); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/followmon.c b/src/followmon.c index c6f4851df8a4..2e241af21e04 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -48,27 +48,17 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent) sFollowMonData->usedSlots++; } -void FreeFollowMonData(void) -{ - if (sFollowMonData != NULL) - { - Free(sFollowMonData); - sFollowMonData = NULL; - } -} - void FollowMon_OverworldCB(void) { if (!OW_SPAWN_OW_WILD_ENCOUNTERS) { RemoveAllFollowMonObjects(); + // Zero sFollowMonData ; + sFollowMonData = AllocZeroed(sizeof(struct FollowMonData)); return; } - if (sFollowMonData == NULL) - sFollowMonData = AllocZeroed(sizeof(struct FollowMonData)); - - if (sFollowMonData->spawnCountdown == 0) + if(sFollowMonData->spawnCountdown == 0) { s16 x, y; const struct WildPokemonInfo *wildMonInfo = NULL; @@ -548,7 +538,6 @@ void RemoveAllFollowMonObjects(void) if(IS_FOLLOWMON_GFXID(gObjectEvents[i].graphicsId)) RemoveObjectEvent(&gObjectEvents[i]); } - FreeFollowMonData(); } static u8 FindObjectEventForGfx(u16 gfxId) From 08a7e756bb42b4a31cb770e13cf827c49383d3db Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 21 Nov 2025 19:45:18 -0600 Subject: [PATCH 032/572] Revert "Make sFollowMonData a pointer to EWRAM" This reverts commit 9f0bd52058072f4e1dfe04b9d92bbe3bc69f727e. --- src/followmon.c | 94 ++++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/src/followmon.c b/src/followmon.c index 2e241af21e04..d11a6b37b7a9 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -9,7 +9,6 @@ #include "followmon.h" #include "fieldmap.h" #include "field_player_avatar.h" -#include "malloc.h" #include "metatile_behavior.h" #include "overworld.h" #include "random.h" @@ -18,7 +17,7 @@ #include "sound.h" #include "wild_encounter.h" -static EWRAM_DATA struct FollowMonData *sFollowMonData = NULL; +static EWRAM_DATA struct FollowMonData sFollowMonData = { 0 }; static u8 CountActiveFollowMon(); static bool8 TrySelectTile(s16* outX, s16* outY); @@ -39,13 +38,13 @@ static void GetMapSize(s32 *width, s32 *height); void LoadFollowMonData(struct ObjectEvent *objectEvent) { u8 slot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; - sFollowMonData->list[slot].isShiny = objectEvent->shiny; - sFollowMonData->list[slot].timeOfDay = objectEvent->spawnTimeOfDay; - sFollowMonData->list[slot].encounterIndex = objectEvent->sEncounterIndex; - sFollowMonData->list[slot].onWater = MetatileBehavior_IsSurfableWaterOrUnderwater(objectEvent->currentMetatileBehavior); + sFollowMonData.list[slot].isShiny = objectEvent->shiny; + sFollowMonData.list[slot].timeOfDay = objectEvent->spawnTimeOfDay; + sFollowMonData.list[slot].encounterIndex = objectEvent->sEncounterIndex; + sFollowMonData.list[slot].onWater = MetatileBehavior_IsSurfableWaterOrUnderwater(objectEvent->currentMetatileBehavior); - sFollowMonData->spawnCountdown += 60; - sFollowMonData->usedSlots++; + sFollowMonData.spawnCountdown += 60; + sFollowMonData.usedSlots++; } void FollowMon_OverworldCB(void) @@ -54,11 +53,16 @@ void FollowMon_OverworldCB(void) { RemoveAllFollowMonObjects(); // Zero sFollowMonData ; - sFollowMonData = AllocZeroed(sizeof(struct FollowMonData)); + u8 *raw = (u8 *)&sFollowMonData; + for (u32 i = 0; i < sizeof(struct FollowMonData); i++) + { + raw[i] = 0; + } + return; } - if(sFollowMonData->spawnCountdown == 0) + if(sFollowMonData.spawnCountdown == 0) { s16 x, y; const struct WildPokemonInfo *wildMonInfo = NULL; @@ -87,7 +91,7 @@ void FollowMon_OverworldCB(void) // Only used for save/load as well as loading encounters, // Most of the time, followmon data is tracked in sFollowMonData - const struct FollowMon *followMon = &sFollowMonData->list[spawnSlot]; + const struct FollowMon *followMon = &sFollowMonData.list[spawnSlot]; gObjectEvents[objectEventId].shiny = followMon->isShiny; gObjectEvents[objectEventId].spawnTimeOfDay = followMon->timeOfDay; gObjectEvents[objectEventId].sEncounterIndex = followMon->encounterIndex; @@ -98,17 +102,17 @@ void FollowMon_OverworldCB(void) gObjectEvents[objectEventId].hideReflection = TRUE; // Slower replacement spawning - sFollowMonData->spawnCountdown = 60 * (3 + Random() % 2); + sFollowMonData.spawnCountdown = 60 * (3 + Random() % 2); } } } else { - --sFollowMonData->spawnCountdown; + --sFollowMonData.spawnCountdown; } // Play spawn animation when player is close enough - if(sFollowMonData->pendingSpawnAnim != 0) + if(sFollowMonData.pendingSpawnAnim != 0) { u16 spawnSlot; u16 gfxId; @@ -121,21 +125,21 @@ void FollowMon_OverworldCB(void) spawnSlot = gfxId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; bitFlag = (1 << spawnSlot); - if((sFollowMonData->pendingSpawnAnim & bitFlag) != 0) + if((sFollowMonData.pendingSpawnAnim & bitFlag) != 0) { objectEventId = FindObjectEventForGfx(gfxId); if(objectEventId != OBJECT_EVENTS_COUNT) { - if(sFollowMonData->list[spawnSlot].isShiny) + if(sFollowMonData.list[spawnSlot].isShiny) { PlaySE(SE_SHINY); spawnAnimType = FOLLOWMON_SPAWN_ANIM_SHINY; - sFollowMonData->pendingSpawnAnim &= ~bitFlag; + sFollowMonData.pendingSpawnAnim &= ~bitFlag; } else { - PlayCry_Normal(GetFollowMonSpecies(&sFollowMonData->list[spawnSlot]), 25); + PlayCry_Normal(GetFollowMonSpecies(&sFollowMonData.list[spawnSlot]), 25); if (IsSpawningWaterMons()) spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) @@ -145,7 +149,7 @@ void FollowMon_OverworldCB(void) } // Instantly play a small animation to ground the spawning a bit MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objectEventId]); - sFollowMonData->pendingSpawnAnim &= ~bitFlag; + sFollowMonData.pendingSpawnAnim &= ~bitFlag; } } } @@ -167,9 +171,9 @@ static u32 GetNewOldestSlot(u32 oldSlot) for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - if (sFollowMonData->list[i].encounterIndex != 0) + if (sFollowMonData.list[i].encounterIndex != 0) { - if (i != oldSlot && (nextOldest == FOLLOWMON_MAX_SPAWN_SLOTS || sFollowMonData->list[i].age > sFollowMonData->list[nextOldest].age)) + if (i != oldSlot && (nextOldest == FOLLOWMON_MAX_SPAWN_SLOTS || sFollowMonData.list[i].age > sFollowMonData.list[nextOldest].age)) nextOldest = i; } } @@ -186,14 +190,14 @@ static u8 NextSpawnMonSlot(void) if(CountActiveFollowMon() >= maxSpawns) { // Cycle through so we remove the oldest mon first - slot = sFollowMonData->oldestSlot; - sFollowMonData->oldestSlot = GetNewOldestSlot(slot); + slot = sFollowMonData.oldestSlot; + sFollowMonData.oldestSlot = GetNewOldestSlot(slot); } else { for (slot = 0; slot < maxSpawns; slot++) { - if (sFollowMonData->list[slot].encounterIndex == 0) + if (sFollowMonData.list[slot].encounterIndex == 0) break; } } @@ -206,7 +210,7 @@ static u8 NextSpawnMonSlot(void) if(CountActiveObjectEvents() >= FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT) return INVALID_SPAWN_SLOT; - GenerateFollowMon(&sFollowMonData->list[slot], IsSpawningWaterMons()); + GenerateFollowMon(&sFollowMonData.list[slot], IsSpawningWaterMons()); return slot; } @@ -352,17 +356,17 @@ bool8 FollowMon_ProcessMonInteraction(void) if(VarGet(VAR_REPEL_STEP_COUNT) != 0) { // Never auto trigger battle whilst repel is active - sFollowMonData->pendingInteraction = FALSE; + sFollowMonData.pendingInteraction = FALSE; return FALSE; } - if(sFollowMonData->pendingInteraction) + if(sFollowMonData.pendingInteraction) { u8 i; struct ObjectEvent *curObject; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - sFollowMonData->pendingInteraction = FALSE; + sFollowMonData.pendingInteraction = FALSE; for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { @@ -398,7 +402,7 @@ bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEve // Player can walk on top of follow mon if(FollowMon_IsMonObject(obstacle)) { - sFollowMonData->pendingInteraction = TRUE; + sFollowMonData.pendingInteraction = TRUE; return TRUE; } } @@ -407,7 +411,7 @@ bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEve // Follow mon can walk onto player if(FollowMon_IsMonObject(collider)) { - sFollowMonData->pendingInteraction = TRUE; + sFollowMonData.pendingInteraction = TRUE; return TRUE; } } @@ -435,32 +439,32 @@ void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { u32 i; u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; - sFollowMonData->usedSlots++; - sFollowMonData->pendingSpawnAnim |= (1 << spawnSlot); + sFollowMonData.usedSlots++; + sFollowMonData.pendingSpawnAnim |= (1 << spawnSlot); // Increase the age of all followmons for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - if (sFollowMonData->list[i].encounterIndex != 0) - sFollowMonData->list[i].age++; + if (sFollowMonData.list[i].encounterIndex != 0) + sFollowMonData.list[i].age++; } } void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; - sFollowMonData->list[spawnSlot].encounterIndex = 0; - sFollowMonData->list[spawnSlot].age = 0; - sFollowMonData->usedSlots--; + sFollowMonData.list[spawnSlot].encounterIndex = 0; + sFollowMonData.list[spawnSlot].age = 0; + sFollowMonData.usedSlots--; } u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) { u16 slot = graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; - u16 species = GetFollowMonSpecies(&sFollowMonData->list[slot]); + u16 species = GetFollowMonSpecies(&sFollowMonData.list[slot]); graphicsId = OBJ_EVENT_MON + species; - if (sFollowMonData->list[slot].isShiny) + if (sFollowMonData.list[slot].isShiny) graphicsId += OBJ_EVENT_MON_SHINY; return graphicsId; @@ -468,14 +472,14 @@ u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) void FollowMon_OnWarp(void) { - sFollowMonData->spawnCountdown = 0; - sFollowMonData->usedSlots = 0; - sFollowMonData->oldestSlot = 0; + sFollowMonData.spawnCountdown = 0; + sFollowMonData.usedSlots = 0; + sFollowMonData.oldestSlot = 0; for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - sFollowMonData->list[i].encounterIndex = 0; - sFollowMonData->list[i].age = 0; + sFollowMonData.list[i].encounterIndex = 0; + sFollowMonData.list[i].age = 0; } } @@ -505,7 +509,7 @@ static u8 CountActiveFollowMon() u8 count = 0; for(u8 slot = 0; slot < FOLLOWMON_MAX_SPAWN_SLOTS; slot++) { - if(sFollowMonData->list[slot].encounterIndex) + if(sFollowMonData.list[slot].encounterIndex) count++; } From e31663b44adb1176498afe88c7224ce70017ef15 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 23 Nov 2025 14:24:56 +0000 Subject: [PATCH 033/572] Edit Config Names --- include/config/overworld.h | 4 ++-- include/followmon.h | 4 ++-- src/field_control_avatar.c | 2 +- src/followmon.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 5d2a6516e752..a21691a36bed 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -55,8 +55,8 @@ // (You should not use 48x48 sprites/tables for compressed gfx) // 16x32, 32x32, 64x64 etc are fine #define OW_MON_WANDER_WALK TRUE // If true, OW Pokémon with MOVEMENT_TYPE_WANDER will walk-in-place in between steps. -#define OW_VANILLA_WILD_ENCOUNTERS TRUE // If true, Pokémon can spawn on tiles that can trigger wild encounters. -#define OW_SPAWN_OW_WILD_ENCOUNTERS FALSE // If true, OW Pokémon can spawn as wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. +#define OW_WILD_ENCOUNTERS_RANDOM TRUE // If true, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. +#define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If true, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. // Follower Pokémon #define OW_FOLLOWERS_ENABLED FALSE // Enables follower Pokémon, HGSS style. Requires OW_POKEMON_OBJECT_EVENTS. Note that additional scripting may be required for them to be fully supported! #define OW_FOLLOWERS_BOBBING TRUE // If TRUE, follower Pokémon will bob up and down during their idle & walking animations diff --git a/include/followmon.h b/include/followmon.h index efefe2e54964..6c9fbef4b6ac 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -1,8 +1,8 @@ #ifndef GUARD_FOLLOWMON_H #define GUARD_FOLLOWMON_H -#if OW_POKEMON_OBJECT_EVENTS == FALSE && OW_SPAWN_OW_WILD_ENCOUNTERS == TRUE -#error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OW_SPAWN_OW_WILD_ENCOUNTERS to work." +#if OW_POKEMON_OBJECT_EVENTS == FALSE && OW_WILD_ENCOUNTERS_OVERWORLD == TRUE +#error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OW_WILD_ENCOUNTERS_OVERWORLD to work." #endif #define FOLLOWMON_SHINY_OFFSET 10000 diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 2f49f16c3c8b..6480f20d6199 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -823,7 +823,7 @@ void RestartWildEncounterImmunitySteps(void) static bool8 CheckStandardWildEncounter(u16 metatileBehavior) { - if (FlagGet(OW_FLAG_NO_ENCOUNTER) || OW_VANILLA_WILD_ENCOUNTERS == FALSE) + if (FlagGet(OW_FLAG_NO_ENCOUNTER) || OW_WILD_ENCOUNTERS_RANDOM == FALSE) return FALSE; if (sWildEncounterImmunitySteps < 4) diff --git a/src/followmon.c b/src/followmon.c index d11a6b37b7a9..edb73e631bf1 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -49,7 +49,7 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent) void FollowMon_OverworldCB(void) { - if (!OW_SPAWN_OW_WILD_ENCOUNTERS) + if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER)) { RemoveAllFollowMonObjects(); // Zero sFollowMonData ; From 748d8e463ea00907af8d19991b34196b7cc0b6c7 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 23 Nov 2025 18:29:35 +0000 Subject: [PATCH 034/572] Add Encounter Movement Types --- include/config/overworld.h | 1 + include/constants/event_object_movement.h | 6 +- include/event_object_movement.h | 8 ++ .../movement_action_func_tables.h | 40 +++++++++ src/event_object_movement.c | 88 +++++++++++++++++++ src/followmon.c | 20 ++++- 6 files changed, 160 insertions(+), 3 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index a21691a36bed..2c607babc22b 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -57,6 +57,7 @@ #define OW_MON_WANDER_WALK TRUE // If true, OW Pokémon with MOVEMENT_TYPE_WANDER will walk-in-place in between steps. #define OW_WILD_ENCOUNTERS_RANDOM TRUE // If true, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. #define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If true, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. +#define OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT TRUE // If true, OW Pokémon spawned as overworld wild encounters will only use restricted movement types (MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER/MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER/MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER). // Follower Pokémon #define OW_FOLLOWERS_ENABLED FALSE // Enables follower Pokémon, HGSS style. Requires OW_POKEMON_OBJECT_EVENTS. Note that additional scripting may be required for them to be fully supported! #define OW_FOLLOWERS_BOBBING TRUE // If TRUE, follower Pokémon will bob up and down during their idle & walking animations diff --git a/include/constants/event_object_movement.h b/include/constants/event_object_movement.h index 4f183d766740..54c38b06dc51 100755 --- a/include/constants/event_object_movement.h +++ b/include/constants/event_object_movement.h @@ -83,7 +83,11 @@ #define MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_LEFT 0x4F #define MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT 0x50 #define MOVEMENT_TYPE_FOLLOW_PLAYER 0x51 -#define NUM_MOVEMENT_TYPES 0x52 +#define MOVEMENT_TYPE_WANDER_ON_MAP 0x52 +#define MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER 0x53 +#define MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER 0x54 +#define MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER 0x55 +#define NUM_MOVEMENT_TYPES 0x56 #define MOVEMENT_ACTION_FACE_DOWN 0x0 #define MOVEMENT_ACTION_FACE_UP 0x1 diff --git a/include/event_object_movement.h b/include/event_object_movement.h index a327b411d9da..c66127310209 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -324,6 +324,10 @@ void MovementType_RunInPlace(struct Sprite *sprite); void MovementType_Invisible(struct Sprite *sprite); void MovementType_WalkSlowlyInPlace(struct Sprite *sprite); void MovementType_FollowPlayer(struct Sprite *sprite); +void MovementType_WanderOnMap(struct Sprite *sprite); +void MovementType_WanderOnLandEncounter(struct Sprite *sprite); +void MovementType_WanderOnWaterEncounter(struct Sprite *sprite); +void MovementType_WanderOnIndoorEncounter(struct Sprite *sprite); u8 GetSlideMovementAction(u32); u8 GetJump2MovementAction(u32); u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); @@ -498,6 +502,10 @@ u8 MovementType_RunInPlace_Step0(struct ObjectEvent *objectEvent, struct Sprite u8 MovementType_Invisible_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_WanderOnMap_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_WanderOnWaterEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_WanderOnIndoorEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevation, u8 direction); void TurnVirtualObject(u8 virtualObjId, u8 direction); diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index 17f601af8a96..bb7c9a782da9 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1719,3 +1719,43 @@ u8 (*const gMovementActionFuncs_WalkFastDiagonalDownRight[])(struct ObjectEvent MovementAction_PauseSpriteAnim, }; +u8 (*const gMovementTypeFuncs_WanderOnMap[])(struct ObjectEvent *, struct Sprite *) = { + MovementType_WanderAround_Step0, + MovementType_WanderAround_Step1, + MovementType_WanderAround_Step2, + MovementType_Wander_Step3, + MovementType_WanderOnMap_Step4, + MovementType_WanderAround_Step5, + MovementType_WanderAround_Step6, +}; + +u8 (*const gMovementTypeFuncs_WanderOnLandEncounter[])(struct ObjectEvent *, struct Sprite *) = { + MovementType_WanderAround_Step0, + MovementType_WanderAround_Step1, + MovementType_WanderAround_Step2, + MovementType_Wander_Step3, + MovementType_WanderOnLandEncounter_Step4, + MovementType_WanderAround_Step5, + MovementType_WanderAround_Step6, +}; + +u8 (*const gMovementTypeFuncs_WanderOnWaterEncounter[])(struct ObjectEvent *, struct Sprite *) = { + MovementType_WanderAround_Step0, + MovementType_WanderAround_Step1, + MovementType_WanderAround_Step2, + MovementType_Wander_Step3, + MovementType_WanderOnWaterEncounter_Step4, + MovementType_WanderAround_Step5, + MovementType_WanderAround_Step6, +}; + +u8 (*const gMovementTypeFuncs_WanderOnIndoorEncounter[])(struct ObjectEvent *, struct Sprite *) = { + MovementType_WanderAround_Step0, + MovementType_WanderAround_Step1, + MovementType_WanderAround_Step2, + MovementType_Wander_Step3, + MovementType_WanderOnIndoorEncounter_Step4, + MovementType_WanderAround_Step5, + MovementType_WanderAround_Step6, +}; + diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 869b6e53c245..529b5038aba1 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -350,6 +350,10 @@ static void (*const sMovementTypeCallbacks[])(struct Sprite *) = [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_LEFT] = MovementType_WalkSlowlyInPlace, [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT] = MovementType_WalkSlowlyInPlace, [MOVEMENT_TYPE_FOLLOW_PLAYER] = MovementType_FollowPlayer, + [MOVEMENT_TYPE_WANDER_ON_MAP] = MovementType_WanderOnMap, + [MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER] = MovementType_WanderOnLandEncounter, + [MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER] = MovementType_WanderOnWaterEncounter, + [MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER] = MovementType_WanderOnIndoorEncounter, }; static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { @@ -394,6 +398,10 @@ static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_COPY_PLAYER_OPPOSITE_IN_GRASS] = TRUE, [MOVEMENT_TYPE_COPY_PLAYER_COUNTERCLOCKWISE_IN_GRASS] = TRUE, [MOVEMENT_TYPE_COPY_PLAYER_CLOCKWISE_IN_GRASS] = TRUE, + [MOVEMENT_TYPE_WANDER_ON_MAP] = TRUE, + [MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER] = TRUE, + [MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER] = TRUE, + [MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER] = TRUE, }; const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { @@ -479,6 +487,10 @@ const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_LEFT] = DIR_WEST, [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT] = DIR_EAST, [MOVEMENT_TYPE_FOLLOW_PLAYER] = DIR_SOUTH, + [MOVEMENT_TYPE_WANDER_ON_MAP] = DIR_SOUTH, + [MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER] = DIR_SOUTH, + [MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER] = DIR_SOUTH, + [MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER] = DIR_SOUTH, }; #include "data/object_events/object_event_graphics_info_pointers.h" @@ -11588,3 +11600,79 @@ bool8 MovementAction_FollowMonSpawn(enum FollowMonSpawnAnim spawnAnimType, struc FieldEffectStart(FLDEFF_BUBBLES); // Commandeer this field effect for the spawn anims return TRUE; } + +static bool32 CheckIfObjectWillStayOnMap(void) +{ + return TRUE; // Placeholder: In actual implementation, this would check map boundaries. +} + +movement_type_def(MovementType_WanderOnMap, gMovementTypeFuncs_WanderOnMap) + +bool8 MovementType_WanderOnMap_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 directions[4]; + u8 chosenDirection; + memcpy(directions, gStandardDirections, sizeof directions); + chosenDirection = directions[Random() & 3]; + SetObjectEventDirection(objectEvent, chosenDirection); + sprite->sTypeFuncId = 5; + // if (!CheckIfObjectWillStayOnMap() + // || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x, objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y)) + // || GetCollisionInDirection(objectEvent, chosenDirection)) + // sprite->sTypeFuncId = 1; + + return TRUE; +} + +movement_type_def(MovementType_WanderOnLandEncounter, gMovementTypeFuncs_WanderOnLandEncounter) + +bool8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 directions[4]; + u8 chosenDirection; + memcpy(directions, gStandardDirections, sizeof directions); + chosenDirection = directions[Random() & 3]; + SetObjectEventDirection(objectEvent, chosenDirection); + sprite->sTypeFuncId = 5; + if (!CheckIfObjectWillStayOnMap() + || !MetatileBehavior_IsLandWildEncounter(MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x, objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y)) + || GetCollisionInDirection(objectEvent, chosenDirection)) + sprite->sTypeFuncId = 1; + + return TRUE; +} + +movement_type_def(MovementType_WanderOnWaterEncounter, gMovementTypeFuncs_WanderOnWaterEncounter) + +bool8 MovementType_WanderOnWaterEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 directions[4]; + u8 chosenDirection; + memcpy(directions, gStandardDirections, sizeof directions); + chosenDirection = directions[Random() & 3]; + SetObjectEventDirection(objectEvent, chosenDirection); + sprite->sTypeFuncId = 5; + if (!CheckIfObjectWillStayOnMap() + || !MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x, objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y)) + || GetCollisionInDirection(objectEvent, chosenDirection)) + sprite->sTypeFuncId = 1; + + return TRUE; +} + +movement_type_def(MovementType_WanderOnIndoorEncounter, gMovementTypeFuncs_WanderOnIndoorEncounter) + +bool8 MovementType_WanderOnIndoorEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 directions[4]; + u8 chosenDirection; + memcpy(directions, gStandardDirections, sizeof directions); + chosenDirection = directions[Random() & 3]; + SetObjectEventDirection(objectEvent, chosenDirection); + sprite->sTypeFuncId = 5; + if (!MetatileBehavior_IsIndoorEncounter(MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x, objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y)) + || GetCollisionInDirection(objectEvent, chosenDirection)) + sprite->sTypeFuncId = 1; + + return TRUE; +} diff --git a/src/followmon.c b/src/followmon.c index edb73e631bf1..58f129b9abf6 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -75,10 +75,26 @@ void FollowMon_OverworldCB(void) if(spawnSlot != INVALID_SPAWN_SLOT) { + bool32 waterMons = IsSpawningWaterMons(); + bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; + u32 movementType; + if (OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT) // These checks need to be improved + { + if (waterMons) + movementType = MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER; + else if (indoors) + movementType = MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER; + else + movementType = MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER; + } + else + { + movementType = MOVEMENT_TYPE_WANDER_ON_MAP; + } u8 localId = OBJ_EVENT_ID_FOLLOW_MON_FIRST + spawnSlot; u8 objectEventId = SpawnSpecialObjectEventParameterized( OBJ_EVENT_GFX_FOLLOW_MON_FIRST + spawnSlot, - MOVEMENT_TYPE_WANDER_AROUND, + movementType, localId, x, y, @@ -98,7 +114,7 @@ void FollowMon_OverworldCB(void) // Hide reflections for spawns in water // (It just looks weird) - if(IsSpawningWaterMons()) + if (waterMons) gObjectEvents[objectEventId].hideReflection = TRUE; // Slower replacement spawning From 3dc70e085052c3708a95b15762b058da8733d732 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 23 Nov 2025 18:46:50 +0000 Subject: [PATCH 035/572] Use IsInsidePlayerMap in Encounter Movement Types --- include/event_object_movement.h | 1 + src/event_object_movement.c | 58 +++++++++++++++++++++++++-------- src/followmon.c | 22 ------------- 3 files changed, 45 insertions(+), 36 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index c66127310209..579f8baae0b8 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -531,5 +531,6 @@ bool8 PlayerIsUnderWaterfall(struct ObjectEvent *objectEvent); u8 GetObjectEventApricornTreeId(u8 objectEventId); bool8 MovementAction_FollowMonSpawn(enum FollowMonSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); +bool32 IsInsidePlayerMap(s16 x, s16 y); #endif //GUARD_EVENT_OBJECT_MOVEMENT_H diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 529b5038aba1..82ec32c826e3 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -226,6 +226,8 @@ static const struct SpriteFrameImage sPicTable_PechaBerryTree[]; static void StartSlowRunningAnim(struct ObjectEvent *objectEvent, struct Sprite *sprite, u8 direction); +static void GetMapSize(s32 *width, s32 *height); + const u8 gReflectionEffectPaletteMap[16] = { [PALSLOT_PLAYER] = PALSLOT_PLAYER_REFLECTION, [PALSLOT_PLAYER_REFLECTION] = PALSLOT_PLAYER_REFLECTION, @@ -3763,6 +3765,26 @@ u16 GetObjectPaletteTag(u8 palSlot) return OBJ_EVENT_PAL_TAG_NONE; } +bool32 IsInsidePlayerMap(s16 x, s16 y) +{ + s32 width, height; + + GetMapSize(&width, &height); + if (x >= 0 && x <= width && y >= 0 && y <= height) + return TRUE; + + return FALSE; +} + +static void GetMapSize(s32 *width, s32 *height) +{ + const struct MapLayout *layout; + + layout = Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->mapLayout; + *width = layout->width; + *height = layout->height; +} + movement_type_empty_callback(MovementType_None) movement_type_def(MovementType_WanderAround, gMovementTypeFuncs_WanderAround) @@ -11601,25 +11623,23 @@ bool8 MovementAction_FollowMonSpawn(enum FollowMonSpawnAnim spawnAnimType, struc return TRUE; } -static bool32 CheckIfObjectWillStayOnMap(void) -{ - return TRUE; // Placeholder: In actual implementation, this would check map boundaries. -} - movement_type_def(MovementType_WanderOnMap, gMovementTypeFuncs_WanderOnMap) bool8 MovementType_WanderOnMap_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u8 directions[4]; u8 chosenDirection; + s16 x, y; memcpy(directions, gStandardDirections, sizeof directions); chosenDirection = directions[Random() & 3]; SetObjectEventDirection(objectEvent, chosenDirection); + x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; + y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - // if (!CheckIfObjectWillStayOnMap() - // || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x, objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y)) - // || GetCollisionInDirection(objectEvent, chosenDirection)) - // sprite->sTypeFuncId = 1; + if (!IsInsidePlayerMap(x, y) + || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) + || GetCollisionInDirection(objectEvent, chosenDirection)) + sprite->sTypeFuncId = 1; return TRUE; } @@ -11630,12 +11650,15 @@ bool8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, { u8 directions[4]; u8 chosenDirection; + s16 x, y; memcpy(directions, gStandardDirections, sizeof directions); chosenDirection = directions[Random() & 3]; SetObjectEventDirection(objectEvent, chosenDirection); + x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; + y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!CheckIfObjectWillStayOnMap() - || !MetatileBehavior_IsLandWildEncounter(MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x, objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y)) + if (!IsInsidePlayerMap(x, y) + || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11648,12 +11671,15 @@ bool8 MovementType_WanderOnWaterEncounter_Step4(struct ObjectEvent *objectEvent, { u8 directions[4]; u8 chosenDirection; + s16 x, y; memcpy(directions, gStandardDirections, sizeof directions); chosenDirection = directions[Random() & 3]; SetObjectEventDirection(objectEvent, chosenDirection); + x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; + y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!CheckIfObjectWillStayOnMap() - || !MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x, objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y)) + if (!IsInsidePlayerMap(x, y) + || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11666,11 +11692,15 @@ bool8 MovementType_WanderOnIndoorEncounter_Step4(struct ObjectEvent *objectEvent { u8 directions[4]; u8 chosenDirection; + s16 x, y; memcpy(directions, gStandardDirections, sizeof directions); chosenDirection = directions[Random() & 3]; SetObjectEventDirection(objectEvent, chosenDirection); + x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; + y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!MetatileBehavior_IsIndoorEncounter(MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x, objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y)) + if (!IsInsidePlayerMap(x, y) + || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; diff --git a/src/followmon.c b/src/followmon.c index 58f129b9abf6..2200616ff092 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -30,8 +30,6 @@ static u8 FindObjectEventForGfx(u16 gfxId); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); static bool8 AreElevationsCompatible(u8 a, u8 b); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); -static bool8 IsInsidePlayerMap(s16 x, s16 y); -static void GetMapSize(s32 *width, s32 *height); #define sEncounterIndex trainerRange_berryTreeId @@ -615,23 +613,3 @@ static bool8 AreElevationsCompatible(u8 a, u8 b) return TRUE; } - -static bool8 IsInsidePlayerMap(s16 x, s16 y) -{ - s32 width, height; - - GetMapSize(&width, &height); - if (x >= 0 && x <= width && y >= 0 && y <= height) - return TRUE; - - return FALSE; -} - -static void GetMapSize(s32 *width, s32 *height) -{ - const struct MapLayout *layout; - - layout = Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->mapLayout; - *width = layout->width; - *height = layout->height; -} From 832e86d1be8f5a947b735f392d33cf0d99565b1f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 23 Nov 2025 19:10:28 +0000 Subject: [PATCH 036/572] Update Nomenclature and move update func to Overworld Basic --- include/followmon.h | 2 +- src/followmon.c | 7 +++++-- src/overworld.c | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/followmon.h b/include/followmon.h index 6c9fbef4b6ac..428642861f7a 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -38,7 +38,7 @@ struct FollowMonData extern const u8 InteractWithDynamicWildFollowMon[]; void LoadFollowMonData(struct ObjectEvent *objectEvent); -void FollowMon_OverworldCB(void); +void UpdateOverworldEncounters(void); void CreateFollowMonEncounter(void); bool8 FollowMon_ProcessMonInteraction(void); bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); diff --git a/src/followmon.c b/src/followmon.c index 2200616ff092..426390ba923a 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -45,9 +45,12 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent) sFollowMonData.usedSlots++; } -void FollowMon_OverworldCB(void) +void UpdateOverworldEncounters(void) { - if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER)) + if (ArePlayerFieldControlsLocked()) + return; + + if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER)) // Need check for if header has encounters? { RemoveAllFollowMonObjects(); // Zero sFollowMonData ; diff --git a/src/overworld.c b/src/overworld.c index 8872b67723d0..902b653641d0 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1541,7 +1541,6 @@ static void DoCB1_Overworld(u16 newKeys, u16 heldKeys) else { PlayerStep(inputStruct.dpadDirection, newKeys, heldKeys); - FollowMon_OverworldCB(); } } // If stop running but keep holding B -> fix follower frame. @@ -1727,6 +1726,7 @@ static void OverworldBasic(void) ApplyWeatherColorMapIfIdle(gWeatherPtr->colorMapIndex); } } + UpdateOverworldEncounters(); } // This CB2 is used when starting From f9e3d3854c2a0b608dcc2e1a2aaa8049ae196c51 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 23 Nov 2025 19:17:52 +0000 Subject: [PATCH 037/572] Update more nomenclature --- include/followmon.h | 4 ++-- src/followmon.c | 6 +++--- src/overworld.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/followmon.h b/include/followmon.h index 428642861f7a..bf9c648e6ac0 100644 --- a/include/followmon.h +++ b/include/followmon.h @@ -46,7 +46,7 @@ bool8 FollowMon_IsMonObject(struct ObjectEvent* object); void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId); -void FollowMon_OnWarp(void); -void RemoveAllFollowMonObjects(void); +void ClearOverworldEncounterData(void); +void RemoveOverworldEncounterObjects(void); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/followmon.c b/src/followmon.c index 426390ba923a..04e2994e5c4a 100644 --- a/src/followmon.c +++ b/src/followmon.c @@ -52,7 +52,7 @@ void UpdateOverworldEncounters(void) if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER)) // Need check for if header has encounters? { - RemoveAllFollowMonObjects(); + RemoveOverworldEncounterObjects(); // Zero sFollowMonData ; u8 *raw = (u8 *)&sFollowMonData; for (u32 i = 0; i < sizeof(struct FollowMonData); i++) @@ -487,7 +487,7 @@ u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) return graphicsId; } -void FollowMon_OnWarp(void) +void ClearOverworldEncounterData(void) { sFollowMonData.spawnCountdown = 0; sFollowMonData.usedSlots = 0; @@ -552,7 +552,7 @@ static bool8 IsSpawningWaterMons() return (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER)); } -void RemoveAllFollowMonObjects(void) +void RemoveOverworldEncounterObjects(void) { for(u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { diff --git a/src/overworld.c b/src/overworld.c index 902b653641d0..fcc11e3d77d1 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -594,7 +594,7 @@ void ApplyCurrentWarp(void) gSaveBlock1Ptr->location = sWarpDestination; sFixedDiveWarp = sDummyWarpData; sFixedHoleWarp = sDummyWarpData; - FollowMon_OnWarp(); + ClearOverworldEncounterData(); } static void ClearDiveAndHoleWarps(void) From f81e9f4f787db4b313e174c4dac87ac5a7c58431 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 23 Nov 2025 19:42:34 +0000 Subject: [PATCH 038/572] Rename followmon files --- include/{followmon.h => overworld_encounters.h} | 0 include/wild_encounter.h | 2 +- src/event_object_movement.c | 3 +-- src/field_control_avatar.c | 9 +++------ src/load_save.c | 9 +++------ src/overworld.c | 3 +-- src/{followmon.c => overworld_encounters.c} | 10 +++++----- 7 files changed, 14 insertions(+), 22 deletions(-) rename include/{followmon.h => overworld_encounters.h} (100%) rename src/{followmon.c => overworld_encounters.c} (99%) diff --git a/include/followmon.h b/include/overworld_encounters.h similarity index 100% rename from include/followmon.h rename to include/overworld_encounters.h diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 9cf192cee882..8b38978ceea0 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -3,7 +3,7 @@ #include "rtc.h" #include "constants/wild_encounter.h" -#include "followmon.h" +#include "overworld_encounters.h" #define HEADER_NONE 0xFFFF diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 82ec32c826e3..15411b8c24ea 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -25,6 +25,7 @@ #include "mauville_old_man.h" #include "metatile_behavior.h" #include "overworld.h" +#include "overworld_encounters.h" #include "palette.h" #include "party_menu.h" #include "pokemon.h" @@ -57,8 +58,6 @@ #include "constants/union_room.h" #include "constants/weather.h" -#include "followmon.h" - #define SPECIAL_LOCALIDS_START (min(LOCALID_CAMERA, \ min(LOCALID_PLAYER, \ LOCALID_BERRY_BLENDER_PLAYER_END - MAX_RFU_PLAYERS + 1))) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 6480f20d6199..abc05519558e 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -25,6 +25,7 @@ #include "match_call.h" #include "metatile_behavior.h" #include "overworld.h" +#include "overworld_encounters.h" #include "pokemon.h" #include "safari_zone.h" #include "script.h" @@ -42,8 +43,6 @@ #include "constants/songs.h" #include "constants/trainer_hill.h" -#include "followmon.h" - static EWRAM_DATA u8 sWildEncounterImmunitySteps = 0; static EWRAM_DATA u16 sPrevMetatileBehavior = 0; @@ -179,10 +178,8 @@ int ProcessPlayerFieldInput(struct FieldInput *input) if (TryRunOnFrameMapScript() == TRUE) return TRUE; - if(FollowMon_ProcessMonInteraction() == TRUE) - { + if (FollowMon_ProcessMonInteraction() == TRUE) return TRUE; - } if (input->pressedBButton && TrySetupDiveEmergeScript() == TRUE) return TRUE; @@ -407,7 +404,7 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 gSpecialVar_Facing = direction; if (FollowMon_IsMonObject(&gObjectEvents[objectEventId])) - script = InteractWithDynamicWildFollowMon; + script = InteractWithDynamicWildFollowMon; else if (InTrainerHill() == TRUE) script = GetTrainerHillTrainerScript(); else if (PlayerHasFollowerNPC() && objectEventId == GetFollowerNPCObjectId()) diff --git a/src/load_save.c b/src/load_save.c index 713a0790c6ce..de5bbd2df29e 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -7,6 +7,7 @@ #include "load_save.h" #include "main.h" #include "overworld.h" +#include "overworld_encounters.h" #include "pokemon.h" #include "pokemon_storage_system.h" #include "random.h" @@ -19,8 +20,6 @@ #include "event_data.h" #include "constants/event_objects.h" -#include "followmon.h" - static void ApplyNewEncryptionKeyToAllEncryptedData(u32 encryptionKey); #define SAVEBLOCK_MOVE_RANGE 128 @@ -202,8 +201,6 @@ void SaveObjectEvents(void) int i; u16 graphicsId; - // Temporary fix until we reduce followmon data size - // and include it in the save for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { gSaveBlock1Ptr->objectEvents[i] = gObjectEvents[i]; @@ -236,9 +233,9 @@ void LoadObjectEvents(void) gObjectEvents[i].graphicsId &= 0xFF; gObjectEvents[i].spriteId = 0; - if(gObjectEvents[i].graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && gObjectEvents[i].graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) { + if(gObjectEvents[i].graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && gObjectEvents[i].graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) LoadFollowMonData(&gObjectEvents[i]); - } + // Try to restore saved inactive follower if (gObjectEvents[i].localId == OBJ_EVENT_ID_FOLLOWER && !gObjectEvents[i].active && diff --git a/src/overworld.c b/src/overworld.c index fcc11e3d77d1..4eeb5b957900 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -44,6 +44,7 @@ #include "money.h" #include "new_game.h" #include "oras_dowse.h" +#include "overworld_encounters.h" #include "palette.h" #include "play_time.h" #include "random.h" @@ -78,8 +79,6 @@ #include "constants/trainer_hill.h" #include "constants/weather.h" -#include "followmon.h" - STATIC_ASSERT((B_FLAG_FOLLOWERS_DISABLED == 0 || OW_FOLLOWERS_ENABLED), FollowersFlagAssignedWithoutEnablingThem); struct CableClubPlayer diff --git a/src/followmon.c b/src/overworld_encounters.c similarity index 99% rename from src/followmon.c rename to src/overworld_encounters.c index 04e2994e5c4a..6899764919e9 100644 --- a/src/followmon.c +++ b/src/overworld_encounters.c @@ -1,12 +1,8 @@ #include "global.h" -#include "constants/event_objects.h" -#include "constants/map_types.h" -#include "constants/songs.h" -#include "constants/vars.h" +#include "overworld_encounters.h" #include "battle_setup.h" #include "event_data.h" #include "event_object_movement.h" -#include "followmon.h" #include "fieldmap.h" #include "field_player_avatar.h" #include "metatile_behavior.h" @@ -16,6 +12,10 @@ #include "sprite.h" #include "sound.h" #include "wild_encounter.h" +#include "constants/event_objects.h" +#include "constants/map_types.h" +#include "constants/songs.h" +#include "constants/vars.h" static EWRAM_DATA struct FollowMonData sFollowMonData = { 0 }; From 9b27d98f4aba335b8f2105cd0c99d81f6ce446d6 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 24 Nov 2025 19:25:24 +0000 Subject: [PATCH 039/572] Cleanup --- include/constants/event_objects.h | 5 +++-- include/strings.h | 5 ----- src/strings.c | 3 --- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index d2c77c97ae0c..c443c527f5c3 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -346,9 +346,10 @@ #define LOCALID_NONE 0 #define LOCALID_CAMERA 127 #define LOCALID_BERRY_BLENDER_PLAYER_END 240 // This will use 5 (MAX_RFU_PLAYERS) IDs ending at 240, i.e. 236-240 +#define LOCALID_FOLLOWING_POKEMON 254 #define LOCALID_PLAYER 255 -#define OBJ_EVENT_ID_FOLLOWER 0xFE //254 -#define OBJ_EVENT_ID_NPC_FOLLOWER 0xFD //253 +#define OBJ_EVENT_ID_FOLLOWER 0xFE +#define OBJ_EVENT_ID_NPC_FOLLOWER 0xFD // IDs for dynamic object event spawns #define OBJ_EVENT_ID_FOLLOW_MON_FIRST 230 diff --git a/include/strings.h b/include/strings.h index ad2caf4504c2..a9264b69fda8 100644 --- a/include/strings.h +++ b/include/strings.h @@ -2415,9 +2415,4 @@ extern const u8 gText_CannotSendMonToBoxHM[]; extern const u8 gText_CannotSendMonToBoxActive[]; extern const u8 gText_CannotSendMonToBoxPartner[]; -// Followmon text -extern const u8 gText_OW_Encounter[]; -extern const u8 gText_OW_Encounter_On[]; -extern const u8 gText_OW_Encounter_Off[]; - #endif // GUARD_STRINGS_H diff --git a/src/strings.c b/src/strings.c index 1eaa457213a2..34c15fe35a4c 100644 --- a/src/strings.c +++ b/src/strings.c @@ -987,9 +987,6 @@ const u8 gText_CommErrorEllipsis[] = _("Communication error…"); const u8 gText_MoveCloserToLinkPartner[] = _("Move closer to your link partner(s).\nAvoid obstacles between partners."); const u8 gText_ABtnRegistrationCounter[] = _("A Button: Registration Counter"); const u8 gText_ABtnTitleScreen[] = _("A Button: Title Screen"); -const u8 gText_OW_Encounter[] = _("OVERWORLD {PKMN}"); -const u8 gText_OW_Encounter_On[] = _("ON"); -const u8 gText_OW_Encounter_Off[] = _("OFF"); const u8 gText_NumPlayerLink[] = _("{STR_VAR_1}P LINK"); const u8 gText_BronzeCard[] = _("BRONZE"); const u8 gText_CopperCard[] = _("COPPER"); From 104c9149ec47493448a4455de477cbc8574a1227 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 24 Nov 2025 20:28:33 +0000 Subject: [PATCH 040/572] Update IsInsideMap and Potentially Despawn Mons on Transition --- include/event_object_movement.h | 1 + include/global.fieldmap.h | 2 +- src/event_object_movement.c | 26 ++++++++++++++++---------- src/overworld.c | 4 +++- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 579f8baae0b8..f8b81c579347 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -531,6 +531,7 @@ bool8 PlayerIsUnderWaterfall(struct ObjectEvent *objectEvent); u8 GetObjectEventApricornTreeId(u8 objectEventId); bool8 MovementAction_FollowMonSpawn(enum FollowMonSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); +bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); bool32 IsInsidePlayerMap(s16 x, s16 y); #endif //GUARD_EVENT_OBJECT_MOVEMENT_H diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index 208b04a57483..6980b68cdefe 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -238,7 +238,7 @@ struct ObjectEvent /*0x1A*/ u8 fieldEffectSpriteId; /*0x1B*/ u8 warpArrowSpriteId; /*0x1C*/ u8 movementActionId; - /*0x1D*/ u8 trainerRange_berryTreeId; //Also stores encounterIndex for FollowMon + /*0x1D*/ u8 trainerRange_berryTreeId; //Also stores encounterIndex for Overworld Encounters /*0x1E*/ u8 currentMetatileBehavior; /*0x1F*/ u8 previousMetatileBehavior; /*0x20*/ u8 previousMovementDirection:4; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 15411b8c24ea..f9ef88f65d67 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -225,7 +225,7 @@ static const struct SpriteFrameImage sPicTable_PechaBerryTree[]; static void StartSlowRunningAnim(struct ObjectEvent *objectEvent, struct Sprite *sprite, u8 direction); -static void GetMapSize(s32 *width, s32 *height); +static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height); const u8 gReflectionEffectPaletteMap[16] = { [PALSLOT_PLAYER] = PALSLOT_PLAYER_REFLECTION, @@ -3764,22 +3764,29 @@ u16 GetObjectPaletteTag(u8 palSlot) return OBJ_EVENT_PAL_TAG_NONE; } -bool32 IsInsidePlayerMap(s16 x, s16 y) +bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) { s32 width, height; + GetMapSize(mapGroup, mapNum, &width, &height); + x -= MAP_OFFSET; + y -= MAP_OFFSET; - GetMapSize(&width, &height); if (x >= 0 && x <= width && y >= 0 && y <= height) return TRUE; return FALSE; } -static void GetMapSize(s32 *width, s32 *height) +bool32 IsInsidePlayerMap(s16 x, s16 y) +{ + return IsInsideMap(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, x, y); +} + +static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height) { const struct MapLayout *layout; - layout = Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->mapLayout; + layout = Overworld_GetMapHeaderByGroupAndId(mapGroup, mapNum)->mapLayout; *width = layout->width; *height = layout->height; } @@ -11635,8 +11642,7 @@ bool8 MovementType_WanderOnMap_Step4(struct ObjectEvent *objectEvent, struct Spr x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsInsidePlayerMap(x, y) - || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) + if (!IsInsideMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11656,7 +11662,7 @@ bool8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsInsidePlayerMap(x, y) + if (!IsInsideMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11677,7 +11683,7 @@ bool8 MovementType_WanderOnWaterEncounter_Step4(struct ObjectEvent *objectEvent, x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsInsidePlayerMap(x, y) + if (!IsInsideMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11698,7 +11704,7 @@ bool8 MovementType_WanderOnIndoorEncounter_Step4(struct ObjectEvent *objectEvent x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsInsidePlayerMap(x, y) + if (!IsInsideMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; diff --git a/src/overworld.c b/src/overworld.c index 4eeb5b957900..902940ddd612 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -593,7 +593,6 @@ void ApplyCurrentWarp(void) gSaveBlock1Ptr->location = sWarpDestination; sFixedDiveWarp = sDummyWarpData; sFixedHoleWarp = sDummyWarpData; - ClearOverworldEncounterData(); } static void ClearDiveAndHoleWarps(void) @@ -895,6 +894,8 @@ if (I_VS_SEEKER_CHARGING != 0) || gMapHeader.regionMapSectionId != sLastMapSectionId) ShowMapNamePopup(); } + // ClearOverworldEncounterData(); + // RemoveOverworldEncounterObjects(); } static void LoadMapFromWarp(bool32 a1) @@ -955,6 +956,7 @@ if (I_VS_SEEKER_CHARGING != 0) UpdateTVScreensOnMap(gBackupMapLayout.width, gBackupMapLayout.height); InitSecretBaseAppearance(TRUE); } + ClearOverworldEncounterData(); } void ResetInitialPlayerAvatarState(void) From a68dc9279c09a81b4b1761f8d6c7ff01622ccaab Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 24 Nov 2025 21:09:56 +0000 Subject: [PATCH 041/572] Add IsInsideMap check for OW Mon --- include/event_object_movement.h | 1 + include/overworld_encounters.h | 1 - src/event_object_movement.c | 24 +++++++++++++++++++----- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index f8b81c579347..dc4704a14537 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -533,5 +533,6 @@ u8 GetObjectEventApricornTreeId(u8 objectEventId); bool8 MovementAction_FollowMonSpawn(enum FollowMonSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); bool32 IsInsidePlayerMap(s16 x, s16 y); +bool32 IsInsideSpawnedMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); #endif //GUARD_EVENT_OBJECT_MOVEMENT_H diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index bf9c648e6ac0..134f0c3206ea 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -5,7 +5,6 @@ #error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OW_WILD_ENCOUNTERS_OVERWORLD to work." #endif -#define FOLLOWMON_SHINY_OFFSET 10000 #define FOLLOWMON_MAX_SPAWN_SLOTS 5 #define FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT 8 diff --git a/src/event_object_movement.c b/src/event_object_movement.c index f9ef88f65d67..6db53dd3760b 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -3771,7 +3771,7 @@ bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) x -= MAP_OFFSET; y -= MAP_OFFSET; - if (x >= 0 && x <= width && y >= 0 && y <= height) + if (x >= 0 && x < width && y >= 0 && y < height) return TRUE; return FALSE; @@ -3782,6 +3782,20 @@ bool32 IsInsidePlayerMap(s16 x, s16 y) return IsInsideMap(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, x, y); } +bool32 IsInsideSpawnedMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) +{ + bool32 inside; + + if (mapGroup == gSaveBlock1Ptr->location.mapGroup && mapNum == gSaveBlock1Ptr->location.mapNum) + { + return IsInsidePlayerMap(x, y); + } + else + { + return !IsInsidePlayerMap(x, y); + } +} + static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height) { const struct MapLayout *layout; @@ -11642,7 +11656,7 @@ bool8 MovementType_WanderOnMap_Step4(struct ObjectEvent *objectEvent, struct Spr x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsInsideMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) + if (!IsInsideSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11662,7 +11676,7 @@ bool8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsInsideMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) + if (!IsInsideSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11683,7 +11697,7 @@ bool8 MovementType_WanderOnWaterEncounter_Step4(struct ObjectEvent *objectEvent, x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsInsideMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) + if (!IsInsideSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11704,7 +11718,7 @@ bool8 MovementType_WanderOnIndoorEncounter_Step4(struct ObjectEvent *objectEvent x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsInsideMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) + if (!IsInsideSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; From c434422519d9a678f0eb03a5042c2abdb58426bc Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 24 Nov 2025 21:13:36 +0000 Subject: [PATCH 042/572] Move Functions Back to overworld_encounters.c --- include/event_object_movement.h | 3 -- include/overworld_encounters.h | 1 + src/event_object_movement.c | 50 +++------------------------------ src/overworld_encounters.c | 44 +++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 49 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index dc4704a14537..c66127310209 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -531,8 +531,5 @@ bool8 PlayerIsUnderWaterfall(struct ObjectEvent *objectEvent); u8 GetObjectEventApricornTreeId(u8 objectEventId); bool8 MovementAction_FollowMonSpawn(enum FollowMonSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); -bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); -bool32 IsInsidePlayerMap(s16 x, s16 y); -bool32 IsInsideSpawnedMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); #endif //GUARD_EVENT_OBJECT_MOVEMENT_H diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 134f0c3206ea..ffae1cab569d 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -47,5 +47,6 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId); void ClearOverworldEncounterData(void); void RemoveOverworldEncounterObjects(void); +bool32 IsOverworldEncounterInSpawnedMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 6db53dd3760b..377e51c430c4 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -225,7 +225,6 @@ static const struct SpriteFrameImage sPicTable_PechaBerryTree[]; static void StartSlowRunningAnim(struct ObjectEvent *objectEvent, struct Sprite *sprite, u8 direction); -static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height); const u8 gReflectionEffectPaletteMap[16] = { [PALSLOT_PLAYER] = PALSLOT_PLAYER_REFLECTION, @@ -3764,47 +3763,6 @@ u16 GetObjectPaletteTag(u8 palSlot) return OBJ_EVENT_PAL_TAG_NONE; } -bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) -{ - s32 width, height; - GetMapSize(mapGroup, mapNum, &width, &height); - x -= MAP_OFFSET; - y -= MAP_OFFSET; - - if (x >= 0 && x < width && y >= 0 && y < height) - return TRUE; - - return FALSE; -} - -bool32 IsInsidePlayerMap(s16 x, s16 y) -{ - return IsInsideMap(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, x, y); -} - -bool32 IsInsideSpawnedMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) -{ - bool32 inside; - - if (mapGroup == gSaveBlock1Ptr->location.mapGroup && mapNum == gSaveBlock1Ptr->location.mapNum) - { - return IsInsidePlayerMap(x, y); - } - else - { - return !IsInsidePlayerMap(x, y); - } -} - -static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height) -{ - const struct MapLayout *layout; - - layout = Overworld_GetMapHeaderByGroupAndId(mapGroup, mapNum)->mapLayout; - *width = layout->width; - *height = layout->height; -} - movement_type_empty_callback(MovementType_None) movement_type_def(MovementType_WanderAround, gMovementTypeFuncs_WanderAround) @@ -11656,7 +11614,7 @@ bool8 MovementType_WanderOnMap_Step4(struct ObjectEvent *objectEvent, struct Spr x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsInsideSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) + if (!IsOverworldEncounterInSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11676,7 +11634,7 @@ bool8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsInsideSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) + if (!IsOverworldEncounterInSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11697,7 +11655,7 @@ bool8 MovementType_WanderOnWaterEncounter_Step4(struct ObjectEvent *objectEvent, x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsInsideSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) + if (!IsOverworldEncounterInSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11718,7 +11676,7 @@ bool8 MovementType_WanderOnIndoorEncounter_Step4(struct ObjectEvent *objectEvent x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsInsideSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) + if (!IsOverworldEncounterInSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 6899764919e9..6feb3a7a77c1 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -30,6 +30,9 @@ static u8 FindObjectEventForGfx(u16 gfxId); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); static bool8 AreElevationsCompatible(u8 a, u8 b); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); +static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height); +static bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); +static bool32 IsInsidePlayerMap(s16 x, s16 y); #define sEncounterIndex trainerRange_berryTreeId @@ -616,3 +619,44 @@ static bool8 AreElevationsCompatible(u8 a, u8 b) return TRUE; } + +static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height) +{ + const struct MapLayout *layout; + + layout = Overworld_GetMapHeaderByGroupAndId(mapGroup, mapNum)->mapLayout; + *width = layout->width; + *height = layout->height; +} + +static bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) +{ + s32 width, height; + GetMapSize(mapGroup, mapNum, &width, &height); + x -= MAP_OFFSET; + y -= MAP_OFFSET; + + if (x >= 0 && x < width && y >= 0 && y < height) + return TRUE; + + return FALSE; +} + +static bool32 IsInsidePlayerMap(s16 x, s16 y) +{ + return IsInsideMap(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, x, y); +} + +bool32 IsOverworldEncounterInSpawnedMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) +{ + bool32 inside; + + if (mapGroup == gSaveBlock1Ptr->location.mapGroup && mapNum == gSaveBlock1Ptr->location.mapNum) + { + return IsInsidePlayerMap(x, y); + } + else + { + return !IsInsidePlayerMap(x, y); + } +} From 4e26374dbc6c6d5366f426a16e1c0326ef4033b9 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 24 Nov 2025 21:29:37 +0000 Subject: [PATCH 043/572] Cleanup --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 8 ++++---- src/overworld_encounters.c | 9 +++------ 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index ffae1cab569d..597aef99102b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -47,6 +47,6 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId); void ClearOverworldEncounterData(void); void RemoveOverworldEncounterObjects(void); -bool32 IsOverworldEncounterInSpawnedMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); +bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 377e51c430c4..06db4f9fbbd6 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11614,7 +11614,7 @@ bool8 MovementType_WanderOnMap_Step4(struct ObjectEvent *objectEvent, struct Spr x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsOverworldEncounterInSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) + if (!IsOverworldEncounterObjectEventInSpawnedMap(objectEvent, x, y) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11634,7 +11634,7 @@ bool8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsOverworldEncounterInSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) + if (!IsOverworldEncounterObjectEventInSpawnedMap(objectEvent, x, y) || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11655,7 +11655,7 @@ bool8 MovementType_WanderOnWaterEncounter_Step4(struct ObjectEvent *objectEvent, x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsOverworldEncounterInSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) + if (!IsOverworldEncounterObjectEventInSpawnedMap(objectEvent, x, y) || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11676,7 +11676,7 @@ bool8 MovementType_WanderOnIndoorEncounter_Step4(struct ObjectEvent *objectEvent x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsOverworldEncounterInSpawnedMap(objectEvent->mapGroup, objectEvent->mapNum, x, y) + if (!IsOverworldEncounterObjectEventInSpawnedMap(objectEvent, x, y) || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 6feb3a7a77c1..d8f464a49ad3 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -647,16 +647,13 @@ static bool32 IsInsidePlayerMap(s16 x, s16 y) return IsInsideMap(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, x, y); } -bool32 IsOverworldEncounterInSpawnedMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) +bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y) { - bool32 inside; + u8 mapGroup = objectEvent->mapGroup; + u8 mapNum = objectEvent->mapNum; if (mapGroup == gSaveBlock1Ptr->location.mapGroup && mapNum == gSaveBlock1Ptr->location.mapNum) - { return IsInsidePlayerMap(x, y); - } else - { return !IsInsidePlayerMap(x, y); - } } From feb4670bf3d2081bb12574c3945f0fed02c7e215 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 24 Nov 2025 21:32:15 +0000 Subject: [PATCH 044/572] Migrate FollowMon_IsMonObject to OW_ENCOUNTER Bit --- include/constants/event_objects.h | 5 +-- include/overworld_encounters.h | 1 - src/event_object_movement.c | 40 ++++++------------------ src/field_control_avatar.c | 2 +- src/load_save.c | 2 +- src/overworld_encounters.c | 52 ++++++++++++++----------------- 6 files changed, 37 insertions(+), 65 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index c443c527f5c3..92751b674715 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -288,12 +288,12 @@ #define OBJ_EVENT_GFX_FOLLOW_MON_FIRST OBJ_EVENT_GFX_FOLLOW_MON_0 #define OBJ_EVENT_GFX_FOLLOW_MON_LAST OBJ_EVENT_GFX_FOLLOW_MON_5 -#define IS_FOLLOWMON_GFXID(gfxID) (gfxID & OBJ_EVENT_GFX_FOLLOW_MON_FIRST) // Don't use (1u << 15) to avoid conflict with BLEND_IMMUNE_FLAG. #define OBJ_EVENT_MON (1u << 14) #define OBJ_EVENT_MON_SHINY (1u << 13) #define OBJ_EVENT_MON_FEMALE (1u << 12) -#define OBJ_EVENT_MON_SPECIES_MASK (~(7u << 12)) +#define OBJ_EVENT_MON_ENCOUNTER (1u << 11) +#define OBJ_EVENT_MON_SPECIES_MASK (~(15u << 11)) // Used to call a specific species' follower graphics. Useful for static encounters. #define OBJ_EVENT_GFX_SPECIES(name) (SPECIES_##name + OBJ_EVENT_MON) @@ -304,6 +304,7 @@ #define OW_SPECIES(x) ((x)->graphicsId & OBJ_EVENT_MON_SPECIES_MASK) #define OW_SHINY(x) ((x)->graphicsId & OBJ_EVENT_MON_SHINY) #define OW_FEMALE(x) ((x)->graphicsId & OBJ_EVENT_MON_FEMALE) +#define OW_ENCOUNTER(x) ((x)->graphicsId & OBJ_EVENT_MON_ENCOUNTER) // Whether Object Event is an OW pokemon #define IS_OW_MON_OBJ(obj) ((obj)->graphicsId & OBJ_EVENT_MON) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 597aef99102b..10262728af2b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -41,7 +41,6 @@ void UpdateOverworldEncounters(void); void CreateFollowMonEncounter(void); bool8 FollowMon_ProcessMonInteraction(void); bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); -bool8 FollowMon_IsMonObject(struct ObjectEvent* object); void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 06db4f9fbbd6..95cc6670fc40 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1562,10 +1562,8 @@ static void RemoveObjectEventInternal(struct ObjectEvent *objectEvent) { struct SpriteFrameImage image; - if(FollowMon_IsMonObject(objectEvent)) - { + if (OW_ENCOUNTER(objectEvent)) FollowMon_OnObjectEventRemoved(objectEvent); - } image.size = GetObjectEventGraphicsInfo(objectEvent->graphicsId)->size; gSprites[objectEvent->spriteId].images = ℑ @@ -1746,17 +1744,8 @@ static u8 TrySetupObjectEventSprite(const struct ObjectEventTemplate *objectEven sprite = &gSprites[spriteId]; // Use palette from species palette table - if (spriteTemplate->paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) { - if (IS_FOLLOWMON_GFXID(objectEvent->graphicsId)) { - u16 tmpGraphicsId = GetFollowMonObjectEventGraphicsId(objectEvent->graphicsId); - sprite->oam.paletteNum = LoadDynamicFollowerPalette( - tmpGraphicsId & OBJ_EVENT_MON_SPECIES_MASK, - tmpGraphicsId & OBJ_EVENT_MON_SHINY, - tmpGraphicsId & OBJ_EVENT_MON_FEMALE); - } else { - sprite->oam.paletteNum = LoadDynamicFollowerPalette(OW_SPECIES(objectEvent), OW_SHINY(objectEvent), OW_FEMALE(objectEvent)); - } - } + if (spriteTemplate->paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) + sprite->oam.paletteNum = LoadDynamicFollowerPalette(OW_SPECIES(objectEvent), OW_SHINY(objectEvent), OW_FEMALE(objectEvent)); if (OW_GFX_COMPRESS && sprite->usingSheet) sprite->sheetSpan = GetSpanPerImage(sprite->oam.shape, sprite->oam.size); GetMapCoordsFromSpritePos(objectEvent->currentCoords.x + cameraX, objectEvent->currentCoords.y + cameraY, &sprite->x, &sprite->y); @@ -1797,10 +1786,8 @@ u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemp if (subspriteTables) SetSubspriteTables(&gSprites[gObjectEvents[objectEventId].spriteId], subspriteTables); - if(FollowMon_IsMonObject(&gObjectEvents[objectEventId])) - { + if (OW_ENCOUNTER(&gObjectEvents[objectEventId])) FollowMon_OnObjectEventSpawned(&gObjectEvents[objectEventId]); - } return objectEventId; } @@ -2890,15 +2877,7 @@ static void SpawnObjectEventOnReturnToField(u8 objectEventId, s16 x, s16 y) if (spriteTemplate.paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) { - u32 paletteNum; - if (IS_FOLLOWMON_GFXID(objectEvent->graphicsId)) { - u16 tmpGraphicsId = GetFollowMonObjectEventGraphicsId(objectEvent->graphicsId); - paletteNum = LoadDynamicFollowerPalette( - tmpGraphicsId & OBJ_EVENT_MON_SPECIES_MASK, - tmpGraphicsId & OBJ_EVENT_MON_SHINY, - tmpGraphicsId & OBJ_EVENT_MON_FEMALE); - } else - paletteNum = LoadDynamicFollowerPalette(OW_SPECIES(objectEvent), OW_SHINY(objectEvent), OW_FEMALE(objectEvent)); + u32 paletteNum = LoadDynamicFollowerPalette(OW_SPECIES(objectEvent), OW_SHINY(objectEvent), OW_FEMALE(objectEvent)); spriteTemplate.paletteTag = GetSpritePaletteTagByPaletteNum(paletteNum); } else if (spriteTemplate.paletteTag != TAG_NONE) @@ -3109,9 +3088,6 @@ const struct ObjectEventGraphicsInfo *GetObjectEventGraphicsInfo(u16 graphicsId) if (graphicsId == OBJ_EVENT_GFX_BARD) return gMauvilleOldManGraphicsInfoPointers[GetCurrentMauvilleOldMan()]; - if (IS_FOLLOWMON_GFXID(graphicsId)) - graphicsId = GetFollowMonObjectEventGraphicsId(graphicsId); - if (graphicsId & OBJ_EVENT_MON) return SpeciesToGraphicsInfo(graphicsId & OBJ_EVENT_MON_SPECIES_MASK, graphicsId & OBJ_EVENT_MON_SHINY, graphicsId & OBJ_EVENT_MON_FEMALE); @@ -9976,8 +9952,9 @@ void GroundEffect_SpawnOnTallGrass(struct ObjectEvent *objEvent, struct Sprite * void GroundEffect_StepOnTallGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) { - if (IS_FOLLOWMON_GFXID(objEvent->graphicsId)) + if (OW_ENCOUNTER(objEvent)) return; + gFieldEffectArguments[0] = objEvent->currentCoords.x; gFieldEffectArguments[1] = objEvent->currentCoords.y; gFieldEffectArguments[2] = objEvent->previousElevation; @@ -10004,8 +9981,9 @@ void GroundEffect_SpawnOnLongGrass(struct ObjectEvent *objEvent, struct Sprite * void GroundEffect_StepOnLongGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) { - if (IS_FOLLOWMON_GFXID(objEvent->graphicsId)) + if (OW_ENCOUNTER(objEvent)) return; + gFieldEffectArguments[0] = objEvent->currentCoords.x; gFieldEffectArguments[1] = objEvent->currentCoords.y; gFieldEffectArguments[2] = objEvent->previousElevation; diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index abc05519558e..96f30b244a01 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -403,7 +403,7 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; gSpecialVar_Facing = direction; - if (FollowMon_IsMonObject(&gObjectEvents[objectEventId])) + if (OW_ENCOUNTER(&gObjectEvents[objectEventId])) script = InteractWithDynamicWildFollowMon; else if (InTrainerHill() == TRUE) script = GetTrainerHillTrainerScript(); diff --git a/src/load_save.c b/src/load_save.c index de5bbd2df29e..5b3108c38cf4 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -233,7 +233,7 @@ void LoadObjectEvents(void) gObjectEvents[i].graphicsId &= 0xFF; gObjectEvents[i].spriteId = 0; - if(gObjectEvents[i].graphicsId >= OBJ_EVENT_GFX_FOLLOW_MON_FIRST && gObjectEvents[i].graphicsId <= OBJ_EVENT_GFX_FOLLOW_MON_LAST) + if (OW_ENCOUNTER(&gObjectEvents[i])) LoadFollowMonData(&gObjectEvents[i]); // Try to restore saved inactive follower diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d8f464a49ad3..76fae18d6cc5 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -42,7 +42,7 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent) sFollowMonData.list[slot].isShiny = objectEvent->shiny; sFollowMonData.list[slot].timeOfDay = objectEvent->spawnTimeOfDay; sFollowMonData.list[slot].encounterIndex = objectEvent->sEncounterIndex; - sFollowMonData.list[slot].onWater = MetatileBehavior_IsSurfableWaterOrUnderwater(objectEvent->currentMetatileBehavior); + sFollowMonData.list[slot].onWater = MetatileBehavior_IsWaterWildEncounter(objectEvent->currentMetatileBehavior); sFollowMonData.spawnCountdown += 60; sFollowMonData.usedSlots++; @@ -77,8 +77,9 @@ void UpdateOverworldEncounters(void) { u16 spawnSlot = NextSpawnMonSlot(); - if(spawnSlot != INVALID_SPAWN_SLOT) + if (spawnSlot != INVALID_SPAWN_SLOT) { + const struct FollowMon *followMon = &sFollowMonData.list[spawnSlot]; bool32 waterMons = IsSpawningWaterMons(); bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; u32 movementType; @@ -97,7 +98,7 @@ void UpdateOverworldEncounters(void) } u8 localId = OBJ_EVENT_ID_FOLLOW_MON_FIRST + spawnSlot; u8 objectEventId = SpawnSpecialObjectEventParameterized( - OBJ_EVENT_GFX_FOLLOW_MON_FIRST + spawnSlot, + GetFollowMonSpecies(&sFollowMonData.list[spawnSlot]) + OBJ_EVENT_MON + OBJ_EVENT_MON_ENCOUNTER, movementType, localId, x, @@ -111,7 +112,6 @@ void UpdateOverworldEncounters(void) // Only used for save/load as well as loading encounters, // Most of the time, followmon data is tracked in sFollowMonData - const struct FollowMon *followMon = &sFollowMonData.list[spawnSlot]; gObjectEvents[objectEventId].shiny = followMon->isShiny; gObjectEvents[objectEventId].spawnTimeOfDay = followMon->timeOfDay; gObjectEvents[objectEventId].sEncounterIndex = followMon->encounterIndex; @@ -140,16 +140,16 @@ void UpdateOverworldEncounters(void) u8 objectEventId; enum FollowMonSpawnAnim spawnAnimType; - for(gfxId = OBJ_EVENT_GFX_FOLLOW_MON_FIRST; gfxId < OBJ_EVENT_GFX_FOLLOW_MON_LAST; ++gfxId) + for (gfxId = OBJ_EVENT_GFX_FOLLOW_MON_FIRST; gfxId < OBJ_EVENT_GFX_FOLLOW_MON_LAST; ++gfxId) { spawnSlot = gfxId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; bitFlag = (1 << spawnSlot); - if((sFollowMonData.pendingSpawnAnim & bitFlag) != 0) + if ((sFollowMonData.pendingSpawnAnim & bitFlag) != 0) { objectEventId = FindObjectEventForGfx(gfxId); - if(objectEventId != OBJECT_EVENTS_COUNT) + if (objectEventId != OBJECT_EVENTS_COUNT) { if(sFollowMonData.list[spawnSlot].isShiny) { @@ -327,10 +327,10 @@ void CreateFollowMonEncounter(void) { u8 lastTalkedId = VarGet(VAR_LAST_TALKED); u8 objEventId = GetObjectEventIdByLocalIdAndMap(lastTalkedId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); - if(objEventId < OBJECT_EVENTS_COUNT) + if (objEventId < OBJECT_EVENTS_COUNT) { curObject = &gObjectEvents[objEventId]; - if(!FollowMon_IsMonObject(curObject)) + if (!OW_ENCOUNTER(curObject)) return; } else @@ -343,7 +343,7 @@ void CreateFollowMonEncounter(void) { u8 index = curObject->sEncounterIndex - 1; u8 level = 0; - if (MetatileBehavior_IsSurfableWaterOrUnderwater(curObject->currentMetatileBehavior)) + if (MetatileBehavior_IsWaterWildEncounter(curObject->currentMetatileBehavior)) { wildMonInfo = gWildMonHeaders[headerId].encounterTypes[curObject->spawnTimeOfDay].waterMonsInfo; level = ChooseWildMonLevel(wildMonInfo->wildPokemon, index, WILD_AREA_WATER); @@ -391,7 +391,7 @@ bool8 FollowMon_ProcessMonInteraction(void) for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { curObject = &gObjectEvents[i]; - if (curObject->active && curObject != player && FollowMon_IsMonObject(curObject)) + if (curObject->active && curObject != player && OW_ENCOUNTER(curObject)) { if ((curObject->currentCoords.x == player->currentCoords.x && curObject->currentCoords.y == player->currentCoords.y) || (curObject->previousCoords.x == player->currentCoords.x && curObject->previousCoords.y == player->currentCoords.y)) { @@ -420,7 +420,7 @@ bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEve if (collider->isPlayer) { // Player can walk on top of follow mon - if(FollowMon_IsMonObject(obstacle)) + if (OW_ENCOUNTER(obstacle)) { sFollowMonData.pendingInteraction = TRUE; return TRUE; @@ -429,13 +429,13 @@ bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEve else if(obstacle->isPlayer) { // Follow mon can walk onto player - if(FollowMon_IsMonObject(collider)) + if (OW_ENCOUNTER(collider)) { sFollowMonData.pendingInteraction = TRUE; return TRUE; } } - else if(!FollowMon_IsMonObject(collider) && FollowMon_IsMonObject(obstacle)) + else if (!OW_ENCOUNTER(collider) && OW_ENCOUNTER(obstacle)) { // Other objects can walk through follow mons, whilst wandering mons is active return TRUE; @@ -444,17 +444,6 @@ bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEve return FALSE; } -bool8 FollowMon_IsMonObject(struct ObjectEvent* object) -{ - u16 localId = object->localId; - u16 graphicsId = object->graphicsId; - - if (IS_FOLLOWMON_GFXID(graphicsId)) - return TRUE; - - return FALSE; -} - void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { u32 i; @@ -483,7 +472,11 @@ u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) u16 slot = graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; u16 species = GetFollowMonSpecies(&sFollowMonData.list[slot]); - graphicsId = OBJ_EVENT_MON + species; + graphicsId = species + OBJ_EVENT_MON + OBJ_EVENT_MON_ENCOUNTER; + + // if (sFollowMonData.list[slot].isFemale) + // graphicsId += OBJ_EVENT_MON_FEMALE; + if (sFollowMonData.list[slot].isShiny) graphicsId += OBJ_EVENT_MON_SHINY; @@ -557,10 +550,11 @@ static bool8 IsSpawningWaterMons() void RemoveOverworldEncounterObjects(void) { - for(u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { - if(IS_FOLLOWMON_GFXID(gObjectEvents[i].graphicsId)) - RemoveObjectEvent(&gObjectEvents[i]); + struct ObjectEvent *obj = &gObjectEvents[i]; + if (OW_ENCOUNTER(obj)) + RemoveObjectEvent(obj); } } From d05374347182dcfc4e729db8384de3927b9aedf2 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:06:16 +0000 Subject: [PATCH 045/572] Make Encounter Check use TRAINER_TYPE_ENCOUNTER --- include/constants/event_objects.h | 4 +--- include/constants/trainer_types.h | 1 + include/overworld_encounters.h | 1 + src/event_object_movement.c | 8 ++++---- src/field_control_avatar.c | 2 +- src/load_save.c | 2 +- src/overworld_encounters.c | 31 ++++++++++++++++++------------- 7 files changed, 27 insertions(+), 22 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 92751b674715..675d5f9fe0bc 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -292,8 +292,7 @@ #define OBJ_EVENT_MON (1u << 14) #define OBJ_EVENT_MON_SHINY (1u << 13) #define OBJ_EVENT_MON_FEMALE (1u << 12) -#define OBJ_EVENT_MON_ENCOUNTER (1u << 11) -#define OBJ_EVENT_MON_SPECIES_MASK (~(15u << 11)) +#define OBJ_EVENT_MON_SPECIES_MASK (~(7u << 12)) // Used to call a specific species' follower graphics. Useful for static encounters. #define OBJ_EVENT_GFX_SPECIES(name) (SPECIES_##name + OBJ_EVENT_MON) @@ -304,7 +303,6 @@ #define OW_SPECIES(x) ((x)->graphicsId & OBJ_EVENT_MON_SPECIES_MASK) #define OW_SHINY(x) ((x)->graphicsId & OBJ_EVENT_MON_SHINY) #define OW_FEMALE(x) ((x)->graphicsId & OBJ_EVENT_MON_FEMALE) -#define OW_ENCOUNTER(x) ((x)->graphicsId & OBJ_EVENT_MON_ENCOUNTER) // Whether Object Event is an OW pokemon #define IS_OW_MON_OBJ(obj) ((obj)->graphicsId & OBJ_EVENT_MON) diff --git a/include/constants/trainer_types.h b/include/constants/trainer_types.h index 8886cf44237a..03c4e9ad231b 100644 --- a/include/constants/trainer_types.h +++ b/include/constants/trainer_types.h @@ -5,5 +5,6 @@ #define TRAINER_TYPE_NORMAL 1 #define TRAINER_TYPE_SEE_ALL_DIRECTIONS 2 #define TRAINER_TYPE_BURIED 3 +#define TRAINER_TYPE_ENCOUNTER 4 #endif // GUARD_CONSTANTS_TRAINER_TYPES_H diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 10262728af2b..69b63bbc338b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -47,5 +47,6 @@ u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId); void ClearOverworldEncounterData(void); void RemoveOverworldEncounterObjects(void); bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); +bool32 IsGeneratedOverworldEncounter(struct ObjectEvent *objectEvent); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 95cc6670fc40..24b391b8e7b3 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1562,7 +1562,7 @@ static void RemoveObjectEventInternal(struct ObjectEvent *objectEvent) { struct SpriteFrameImage image; - if (OW_ENCOUNTER(objectEvent)) + if (IsGeneratedOverworldEncounter(objectEvent)) FollowMon_OnObjectEventRemoved(objectEvent); image.size = GetObjectEventGraphicsInfo(objectEvent->graphicsId)->size; @@ -1786,7 +1786,7 @@ u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemp if (subspriteTables) SetSubspriteTables(&gSprites[gObjectEvents[objectEventId].spriteId], subspriteTables); - if (OW_ENCOUNTER(&gObjectEvents[objectEventId])) + if (IsGeneratedOverworldEncounter(&gObjectEvents[objectEventId])) FollowMon_OnObjectEventSpawned(&gObjectEvents[objectEventId]); return objectEventId; @@ -9952,7 +9952,7 @@ void GroundEffect_SpawnOnTallGrass(struct ObjectEvent *objEvent, struct Sprite * void GroundEffect_StepOnTallGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) { - if (OW_ENCOUNTER(objEvent)) + if (IsGeneratedOverworldEncounter(objEvent)) return; gFieldEffectArguments[0] = objEvent->currentCoords.x; @@ -9981,7 +9981,7 @@ void GroundEffect_SpawnOnLongGrass(struct ObjectEvent *objEvent, struct Sprite * void GroundEffect_StepOnLongGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) { - if (OW_ENCOUNTER(objEvent)) + if (IsGeneratedOverworldEncounter(objEvent)) return; gFieldEffectArguments[0] = objEvent->currentCoords.x; diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 96f30b244a01..fcea303a8ffc 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -403,7 +403,7 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; gSpecialVar_Facing = direction; - if (OW_ENCOUNTER(&gObjectEvents[objectEventId])) + if (IsGeneratedOverworldEncounter(&gObjectEvents[objectEventId])) script = InteractWithDynamicWildFollowMon; else if (InTrainerHill() == TRUE) script = GetTrainerHillTrainerScript(); diff --git a/src/load_save.c b/src/load_save.c index 5b3108c38cf4..c3d44eb55e97 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -233,7 +233,7 @@ void LoadObjectEvents(void) gObjectEvents[i].graphicsId &= 0xFF; gObjectEvents[i].spriteId = 0; - if (OW_ENCOUNTER(&gObjectEvents[i])) + if (IsGeneratedOverworldEncounter(&gObjectEvents[i])) LoadFollowMonData(&gObjectEvents[i]); // Try to restore saved inactive follower diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 76fae18d6cc5..cdcc89f1ac4d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -14,6 +14,7 @@ #include "wild_encounter.h" #include "constants/event_objects.h" #include "constants/map_types.h" +#include "constants/trainer_types.h" #include "constants/songs.h" #include "constants/vars.h" @@ -98,7 +99,7 @@ void UpdateOverworldEncounters(void) } u8 localId = OBJ_EVENT_ID_FOLLOW_MON_FIRST + spawnSlot; u8 objectEventId = SpawnSpecialObjectEventParameterized( - GetFollowMonSpecies(&sFollowMonData.list[spawnSlot]) + OBJ_EVENT_MON + OBJ_EVENT_MON_ENCOUNTER, + GetFollowMonObjectEventGraphicsId(spawnSlot), movementType, localId, x, @@ -109,6 +110,7 @@ void UpdateOverworldEncounters(void) gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; gObjectEvents[objectEventId].range.rangeX = 8; gObjectEvents[objectEventId].range.rangeY = 8; + gObjectEvents[objectEventId].trainerType = TRAINER_TYPE_ENCOUNTER; // Only used for save/load as well as loading encounters, // Most of the time, followmon data is tracked in sFollowMonData @@ -330,7 +332,7 @@ void CreateFollowMonEncounter(void) { if (objEventId < OBJECT_EVENTS_COUNT) { curObject = &gObjectEvents[objEventId]; - if (!OW_ENCOUNTER(curObject)) + if (!IsGeneratedOverworldEncounter(curObject)) return; } else @@ -391,7 +393,7 @@ bool8 FollowMon_ProcessMonInteraction(void) for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { curObject = &gObjectEvents[i]; - if (curObject->active && curObject != player && OW_ENCOUNTER(curObject)) + if (curObject->active && curObject != player && IsGeneratedOverworldEncounter(curObject)) { if ((curObject->currentCoords.x == player->currentCoords.x && curObject->currentCoords.y == player->currentCoords.y) || (curObject->previousCoords.x == player->currentCoords.x && curObject->previousCoords.y == player->currentCoords.y)) { @@ -420,7 +422,7 @@ bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEve if (collider->isPlayer) { // Player can walk on top of follow mon - if (OW_ENCOUNTER(obstacle)) + if (IsGeneratedOverworldEncounter(obstacle)) { sFollowMonData.pendingInteraction = TRUE; return TRUE; @@ -429,13 +431,13 @@ bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEve else if(obstacle->isPlayer) { // Follow mon can walk onto player - if (OW_ENCOUNTER(collider)) + if (IsGeneratedOverworldEncounter(collider)) { sFollowMonData.pendingInteraction = TRUE; return TRUE; } } - else if (!OW_ENCOUNTER(collider) && OW_ENCOUNTER(obstacle)) + else if (!IsGeneratedOverworldEncounter(collider) && IsGeneratedOverworldEncounter(obstacle)) { // Other objects can walk through follow mons, whilst wandering mons is active return TRUE; @@ -467,17 +469,15 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) sFollowMonData.usedSlots--; } -u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId) +u16 GetFollowMonObjectEventGraphicsId(u16 spawnSlot) { - u16 slot = graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; - u16 species = GetFollowMonSpecies(&sFollowMonData.list[slot]); - - graphicsId = species + OBJ_EVENT_MON + OBJ_EVENT_MON_ENCOUNTER; + u16 species = GetFollowMonSpecies(&sFollowMonData.list[spawnSlot]); + u16 graphicsId = species + OBJ_EVENT_MON; // if (sFollowMonData.list[slot].isFemale) // graphicsId += OBJ_EVENT_MON_FEMALE; - if (sFollowMonData.list[slot].isShiny) + if (sFollowMonData.list[spawnSlot].isShiny) graphicsId += OBJ_EVENT_MON_SHINY; return graphicsId; @@ -553,7 +553,7 @@ void RemoveOverworldEncounterObjects(void) for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { struct ObjectEvent *obj = &gObjectEvents[i]; - if (OW_ENCOUNTER(obj)) + if (IsGeneratedOverworldEncounter(obj)) RemoveObjectEvent(obj); } } @@ -651,3 +651,8 @@ bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEve else return !IsInsidePlayerMap(x, y); } + +bool32 IsGeneratedOverworldEncounter(struct ObjectEvent *objectEvent) +{ + return (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_ENCOUNTER); +} From 57062b085ed95e1e1a61ed8a20991c884f9adf39 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:17:45 +0000 Subject: [PATCH 046/572] Create OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER --- include/constants/event_objects.h | 6 ++---- src/overworld_encounters.c | 7 +++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 675d5f9fe0bc..27a748d1b6b9 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -350,13 +350,11 @@ #define OBJ_EVENT_ID_FOLLOWER 0xFE #define OBJ_EVENT_ID_NPC_FOLLOWER 0xFD -// IDs for dynamic object event spawns -#define OBJ_EVENT_ID_FOLLOW_MON_FIRST 230 -#define OBJ_EVENT_ID_FOLLOW_MON_LAST 240 - // Aliases for old names. "object event id" normally refers to an index into gObjectEvents, which these are not. // Used for link player OWs in CreateLinkPlayerSprite #define OBJ_EVENT_ID_DYNAMIC_BASE 0xF0 +// Uses OBJ_EVENT_ID_DYNAMIC_BASE as the last local id for OW Encounter Objects +#define OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER OBJ_EVENT_ID_DYNAMIC_BASE #define OBJ_EVENT_ID_CAMERA LOCALID_CAMERA #define OBJ_EVENT_ID_PLAYER LOCALID_PLAYER diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index cdcc89f1ac4d..83b6f7623b8a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -97,7 +97,7 @@ void UpdateOverworldEncounters(void) { movementType = MOVEMENT_TYPE_WANDER_ON_MAP; } - u8 localId = OBJ_EVENT_ID_FOLLOW_MON_FIRST + spawnSlot; + u8 localId = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - spawnSlot; u8 objectEventId = SpawnSpecialObjectEventParameterized( GetFollowMonObjectEventGraphicsId(spawnSlot), movementType, @@ -225,7 +225,7 @@ static u8 NextSpawnMonSlot(void) } // Remove any existing id by this slot - RemoveObjectEventByLocalIdAndMap(OBJ_EVENT_ID_FOLLOW_MON_FIRST + slot, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + RemoveObjectEventByLocalIdAndMap(OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - slot, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); // Check that we don't have too many sprites on screen before spawning // (lag reduction) @@ -326,8 +326,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) void CreateFollowMonEncounter(void) { struct ObjectEvent *curObject; - u8 lastTalkedId = VarGet(VAR_LAST_TALKED); - u8 objEventId = GetObjectEventIdByLocalIdAndMap(lastTalkedId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + u8 objEventId = GetObjectEventIdByLocalIdAndMap(gSpecialVar_LastTalked, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); if (objEventId < OBJECT_EVENTS_COUNT) { From 85c3646edd2e04ebacda9a9deff2a99d26db65a8 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 26 Nov 2025 21:56:46 +0000 Subject: [PATCH 047/572] Add a new field effect for Overworld Encounter in Grass --- data/field_effect_scripts.s | 10 +++ include/constants/field_effects.h | 6 ++ .../field_effect_object_template_pointers.h | 6 +- src/data/field_effects/field_effect_objects.h | 34 +++++++++++ src/event_object_movement.c | 30 ++++++--- src/field_effect_helpers.c | 61 ++++++++++++++++++- 6 files changed, 134 insertions(+), 13 deletions(-) diff --git a/data/field_effect_scripts.s b/data/field_effect_scripts.s index ce71a102fdb9..0a0200048200 100644 --- a/data/field_effect_scripts.s +++ b/data/field_effect_scripts.s @@ -84,6 +84,8 @@ gFieldEffectScriptPointers:: .4byte gFieldEffectScript_UseRockClimb @ FLDEFF_USE_ROCK_CLIMB .4byte gFieldEffectScript_RockClimbDust @ FLDEFF_ROCK_CLIMB_DUST .4byte gFieldEffectScript_ORASDowse @ FLDEFF_ORAS_DOWSE + .4byte gFieldEffectScript_TallGrass_OWE @ FLDEFF_TALL_GRASS_OWE + .4byte gFieldEffectScript_LongGrass_OWE @ FLDEFF_LONG_GRASS_OWE gFieldEffectScript_ExclamationMarkIcon1:: field_eff_callnative FldEff_ExclamationMarkIcon @@ -105,6 +107,10 @@ gFieldEffectScript_TallGrass:: field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_TallGrass field_eff_end +gFieldEffectScript_TallGrass_OWE:: + field_eff_loadgfx_callnative gSpriteSheet_TallGrass, gSpritePalette_GeneralFieldEffect1, FldEff_TallGrass_OverworldEncounter + field_eff_end + gFieldEffectScript_Ripple:: field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_Ripple field_eff_end @@ -157,6 +163,10 @@ gFieldEffectScript_LongGrass:: field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_LongGrass field_eff_end +gFieldEffectScript_LongGrass_OWE:: + field_eff_loadgfx_callnative gSpriteSheet_LongGrass, gSpritePalette_GeneralFieldEffect1, FldEff_LongGrass_OverworldEncounter + field_eff_end + gFieldEffectScript_JumpLongGrass:: field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_JumpLongGrass field_eff_end diff --git a/include/constants/field_effects.h b/include/constants/field_effects.h index 8750ac9646e7..2b94ab922048 100644 --- a/include/constants/field_effects.h +++ b/include/constants/field_effects.h @@ -80,6 +80,8 @@ #define FLDEFF_USE_ROCK_CLIMB 75 #define FLDEFF_ROCK_CLIMB_DUST 76 #define FLDEFF_ORAS_DOWSE 77 +#define FLDEFF_TALL_GRASS_OWE 78 +#define FLDEFF_LONG_GRASS_OWE 79 #define FLDEFFOBJ_SHADOW_S 0 #define FLDEFFOBJ_SHADOW_M 1 @@ -127,6 +129,8 @@ #define FLDEFFOBJ_ORAS_DOWSE_BRENDAN 43 #define FLDEFFOBJ_ORAS_DOWSE_MAY 44 #define FLDEFFOBJ_SHINY_SPARKLE 45 +#define FLDEFFOBJ_TALL_GRASS_OWE 46 +#define FLDEFFOBJ_LONG_GRASS_OWE 47 #define FLDEFF_PAL_TAG_CUT_GRASS 0x1000 #define FLDEFF_PAL_TAG_SECRET_POWER_TREE 0x1003 @@ -150,5 +154,7 @@ #define FLDEFF_TILE_TAG_SHADOW_MEDIUM 0x1401 #define FLDEFF_TILE_TAG_SHADOW_LARGE 0x1402 #define FLDEFF_TILE_TAG_SHADOW_EXTRA_LARGE 0x1403 +#define FLDEFF_TILE_TAG_TALL_GRASS_OWE 0x1404 +#define FLDEFF_TILE_TAG_LONG_GRASS_OWE 0x1405 #endif // GUARD_FIELD_EFFECT_CONSTANTS_H diff --git a/src/data/field_effects/field_effect_object_template_pointers.h b/src/data/field_effects/field_effect_object_template_pointers.h index d478885eabe3..49fb9f575504 100755 --- a/src/data/field_effects/field_effect_object_template_pointers.h +++ b/src/data/field_effects/field_effect_object_template_pointers.h @@ -45,6 +45,8 @@ extern const struct SpriteTemplate gFieldEffectObjectTemplate_RockClimbBlob; extern const struct SpriteTemplate gFieldEffectObjectTemplate_RockClimbDust; extern const struct SpriteTemplate gFieldEffectObjectTemplate_ORASDowsingBrendan; extern const struct SpriteTemplate gFieldEffectObjectTemplate_ORASDowsingMay; +extern const struct SpriteTemplate gFieldEffectObjectTemplate_TallGrass_OverworldEncounter; +extern const struct SpriteTemplate gFieldEffectObjectTemplate_LongGrass_OverworldEncounter; const struct SpriteTemplate *const gFieldEffectObjectTemplatePointers[] = { [FLDEFFOBJ_SHADOW_S] = &gFieldEffectObjectTemplate_ShadowSmall, @@ -83,7 +85,6 @@ const struct SpriteTemplate *const gFieldEffectObjectTemplatePointers[] = { [FLDEFFOBJ_ASH_LAUNCH] = &gFieldEffectObjectTemplate_AshLaunch, [FLDEFFOBJ_BUBBLES] = &gFieldEffectObjectTemplate_Bubbles, [FLDEFFOBJ_SMALL_SPARKLE] = &gFieldEffectObjectTemplate_SmallSparkle, - [FLDEFFOBJ_SHINY_SPARKLE] = &gFieldEffectObjectTemplate_ShinySparkle, [FLDEFFOBJ_RAYQUAZA] = &gFieldEffectObjectTemplate_Rayquaza, [FLDEFFOBJ_TRACKS_SLITHER] = &gFieldEffectObjectTemplate_SlitherTracks, [FLDEFFOBJ_TRACKS_SPOT] = &gFieldEffectObjectTemplate_SpotTracks, @@ -93,4 +94,7 @@ const struct SpriteTemplate *const gFieldEffectObjectTemplatePointers[] = { [FLDEFFOBJ_ROCK_CLIMB_DUST] = &gFieldEffectObjectTemplate_RockClimbDust, [FLDEFFOBJ_ORAS_DOWSE_BRENDAN] = &gFieldEffectObjectTemplate_ORASDowsingBrendan, [FLDEFFOBJ_ORAS_DOWSE_MAY] = &gFieldEffectObjectTemplate_ORASDowsingMay, + [FLDEFFOBJ_SHINY_SPARKLE] = &gFieldEffectObjectTemplate_ShinySparkle, + [FLDEFFOBJ_TALL_GRASS_OWE] = &gFieldEffectObjectTemplate_TallGrass_OverworldEncounter, + [FLDEFFOBJ_LONG_GRASS_OWE] = &gFieldEffectObjectTemplate_LongGrass_OverworldEncounter, }; diff --git a/src/data/field_effects/field_effect_objects.h b/src/data/field_effects/field_effect_objects.h index e36051efc50c..8da5c9c61866 100755 --- a/src/data/field_effects/field_effect_objects.h +++ b/src/data/field_effects/field_effect_objects.h @@ -149,6 +149,23 @@ const struct SpriteTemplate gFieldEffectObjectTemplate_TallGrass = { .callback = UpdateTallGrassFieldEffect, }; +const struct SpriteSheet gSpriteSheet_TallGrass = +{ + .data = gFieldEffectObjectPic_TallGrass, + .size = sizeof(gFieldEffectObjectPic_TallGrass), + .tag = FLDEFF_TILE_TAG_TALL_GRASS_OWE, +}; + +const struct SpriteTemplate gFieldEffectObjectTemplate_TallGrass_OverworldEncounter = { + .tileTag = FLDEFF_TILE_TAG_TALL_GRASS_OWE, + .paletteTag = FLDEFF_PAL_TAG_GENERAL_1, + .oam = &gObjectEventBaseOam_16x16, + .anims = sAnimTable_TallGrass, + .images = sPicTable_TallGrass, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = UpdateTallGrassFieldEffect, +}; + static const struct SpriteFrameImage sPicTable_Ripple[] = { overworld_frame(gFieldEffectObjectPic_Ripple, 2, 2, 0), overworld_frame(gFieldEffectObjectPic_Ripple, 2, 2, 1), @@ -741,6 +758,23 @@ const struct SpriteTemplate gFieldEffectObjectTemplate_LongGrass = { .callback = UpdateLongGrassFieldEffect, }; +const struct SpriteSheet gSpriteSheet_LongGrass = +{ + .data = gFieldEffectObjectPic_LongGrass, + .size = sizeof(gFieldEffectObjectPic_LongGrass), + .tag = FLDEFF_TILE_TAG_LONG_GRASS_OWE, +}; + +const struct SpriteTemplate gFieldEffectObjectTemplate_LongGrass_OverworldEncounter = { + .tileTag = FLDEFF_TILE_TAG_LONG_GRASS_OWE, + .paletteTag = FLDEFF_PAL_TAG_GENERAL_1, + .oam = &gObjectEventBaseOam_16x16, + .anims = sAnimTable_LongGrass, + .images = sPicTable_LongGrass, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = UpdateLongGrassFieldEffect, +}; + static const struct SpriteFrameImage sPicTable_JumpLongGrass[] = { overworld_frame(gFieldEffectObjectPic_JumpLongGrass, 2, 2, 0), overworld_frame(gFieldEffectObjectPic_JumpLongGrass, 2, 2, 1), diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 24b391b8e7b3..3dbad9e95cee 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -9947,14 +9947,15 @@ void GroundEffect_SpawnOnTallGrass(struct ObjectEvent *objEvent, struct Sprite * gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; gFieldEffectArguments[7] = TRUE; // skip to end of anim - FieldEffectStart(FLDEFF_TALL_GRASS); + + if (IsGeneratedOverworldEncounter(objEvent)) + FieldEffectStart(FLDEFF_TALL_GRASS_OWE); + else + FieldEffectStart(FLDEFF_TALL_GRASS); } void GroundEffect_StepOnTallGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) { - if (IsGeneratedOverworldEncounter(objEvent)) - return; - gFieldEffectArguments[0] = objEvent->currentCoords.x; gFieldEffectArguments[1] = objEvent->currentCoords.y; gFieldEffectArguments[2] = objEvent->previousElevation; @@ -9963,7 +9964,11 @@ void GroundEffect_StepOnTallGrass(struct ObjectEvent *objEvent, struct Sprite *s gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; gFieldEffectArguments[7] = TRUE; // skip to end of anim - FieldEffectStart(FLDEFF_TALL_GRASS); + + if (IsGeneratedOverworldEncounter(objEvent)) + FieldEffectStart(FLDEFF_TALL_GRASS_OWE); + else + FieldEffectStart(FLDEFF_TALL_GRASS); } void GroundEffect_SpawnOnLongGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) @@ -9976,14 +9981,15 @@ void GroundEffect_SpawnOnLongGrass(struct ObjectEvent *objEvent, struct Sprite * gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; gFieldEffectArguments[7] = TRUE; - FieldEffectStart(FLDEFF_LONG_GRASS); + + if (IsGeneratedOverworldEncounter(objEvent)) + FieldEffectStart(FLDEFF_LONG_GRASS_OWE); + else + FieldEffectStart(FLDEFF_LONG_GRASS); } void GroundEffect_StepOnLongGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) { - if (IsGeneratedOverworldEncounter(objEvent)) - return; - gFieldEffectArguments[0] = objEvent->currentCoords.x; gFieldEffectArguments[1] = objEvent->currentCoords.y; gFieldEffectArguments[2] = objEvent->previousElevation; @@ -9992,7 +9998,11 @@ void GroundEffect_StepOnLongGrass(struct ObjectEvent *objEvent, struct Sprite *s gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; gFieldEffectArguments[7] = TRUE; - FieldEffectStart(FLDEFF_LONG_GRASS); + + if (IsGeneratedOverworldEncounter(objEvent)) + FieldEffectStart(FLDEFF_LONG_GRASS_OWE); + else + FieldEffectStart(FLDEFF_LONG_GRASS); } void GroundEffect_WaterReflection(struct ObjectEvent *objEvent, struct Sprite *sprite) diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 5c757775f449..d0ecd9f05369 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -7,6 +7,7 @@ #include "fieldmap.h" #include "gpu_regs.h" #include "metatile_behavior.h" +#include "overworld_encounters.h" #include "palette.h" #include "sound.h" #include "sprite.h" @@ -450,6 +451,31 @@ u32 FldEff_TallGrass(void) return 0; } +u32 FldEff_TallGrass_OverworldEncounter(void) +{ + u8 spriteId; + s16 x = gFieldEffectArguments[0]; + s16 y = gFieldEffectArguments[1]; + SetSpritePosToOffsetMapCoords(&x, &y, 8, 8); + spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[FLDEFFOBJ_TALL_GRASS_OWE], x, y, 0); + if (spriteId != MAX_SPRITES) + { + struct Sprite *sprite = &gSprites[spriteId]; + sprite->coordOffsetEnabled = TRUE; + sprite->oam.priority = gFieldEffectArguments[3]; + sprite->sElevation = gFieldEffectArguments[2]; + sprite->sX = gFieldEffectArguments[0]; + sprite->sY = gFieldEffectArguments[1]; + sprite->sMapNum = gFieldEffectArguments[4]; // Also sLocalId + sprite->sMapGroup = gFieldEffectArguments[5]; + sprite->sCurrentMap = gFieldEffectArguments[6]; + + if (gFieldEffectArguments[7]) + SeekSpriteAnim(sprite, 4); // Skip to end of anim + } + return 0; +} + void UpdateTallGrassFieldEffect(struct Sprite *sprite) { u8 metatileBehavior; @@ -473,7 +499,10 @@ void UpdateTallGrassFieldEffect(struct Sprite *sprite) || !MetatileBehavior_IsTallGrass(metatileBehavior) || (sprite->sObjectMoved && sprite->animEnded)) { - FieldEffectStop(sprite, FLDEFF_TALL_GRASS); + if (IsGeneratedOverworldEncounter(&gObjectEvents[objectEventId])) + FieldEffectStop(sprite, FLDEFF_TALL_GRASS_OWE); + else + FieldEffectStop(sprite, FLDEFF_TALL_GRASS); } else { @@ -554,6 +583,31 @@ u32 FldEff_LongGrass(void) return 0; } +u32 FldEff_LongGrass_OverworldEncounter(void) +{ + u8 spriteId; + s16 x = gFieldEffectArguments[0]; + s16 y = gFieldEffectArguments[1]; + SetSpritePosToOffsetMapCoords(&x, &y, 8, 8); + spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[FLDEFFOBJ_LONG_GRASS_OWE], x, y, 0); + if (spriteId != MAX_SPRITES) + { + struct Sprite *sprite = &gSprites[spriteId]; + sprite->coordOffsetEnabled = TRUE; + sprite->oam.priority = ElevationToPriority(gFieldEffectArguments[2]); + sprite->sElevation = gFieldEffectArguments[2]; + sprite->sX = gFieldEffectArguments[0]; + sprite->sY = gFieldEffectArguments[1]; + sprite->sMapNum = gFieldEffectArguments[4]; // Also sLocalId + sprite->sMapGroup = gFieldEffectArguments[5]; + sprite->sCurrentMap = gFieldEffectArguments[6]; + + if (gFieldEffectArguments[7]) + SeekSpriteAnim(sprite, 6); // Skip to end of anim + } + return 0; +} + void UpdateLongGrassFieldEffect(struct Sprite *sprite) { u8 metatileBehavior; @@ -576,7 +630,10 @@ void UpdateLongGrassFieldEffect(struct Sprite *sprite) || !MetatileBehavior_IsLongGrass(metatileBehavior) || (sprite->sObjectMoved && sprite->animEnded)) { - FieldEffectStop(sprite, FLDEFF_LONG_GRASS); + if (IsGeneratedOverworldEncounter(&gObjectEvents[objectEventId])) + FieldEffectStop(sprite, FLDEFF_LONG_GRASS_OWE); + else + FieldEffectStop(sprite, FLDEFF_LONG_GRASS); } else { From 5cd19a9756b1169370949beb7bd35476dc8f73ad Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 26 Nov 2025 22:57:11 +0000 Subject: [PATCH 048/572] Clean Up Collision Check Code --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 32 +++++++++----------------------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 69b63bbc338b..c5dd152d6dcf 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -40,7 +40,7 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent); void UpdateOverworldEncounters(void); void CreateFollowMonEncounter(void); bool8 FollowMon_ProcessMonInteraction(void); -bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); +bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 3dbad9e95cee..852a525386cf 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -6468,7 +6468,7 @@ u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, b curObject = &gObjectEvents[i]; if (curObject->active && (curObject->movementType != MOVEMENT_TYPE_FOLLOW_PLAYER || objectEvent != &gObjectEvents[gPlayerAvatar.objectEventId]) && curObject != objectEvent && !FollowerNPC_IsCollisionExempt(curObject, objectEvent) // Partner - && !FollowMon_IsCollisionExempt(curObject, objectEvent) // Wild Pokemon + && !OverworldEncounter_IsCollisionExempt(curObject, objectEvent) // Wild Pokemon ) { // check for collision if curObject is active, not the object in question, and not exempt from collisions diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 83b6f7623b8a..7f04df1d3cd9 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -416,31 +416,17 @@ bool8 FollowMon_ProcessMonInteraction(void) return FALSE; } -bool8 FollowMon_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider) +bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider) { - if (collider->isPlayer) - { - // Player can walk on top of follow mon - if (IsGeneratedOverworldEncounter(obstacle)) - { - sFollowMonData.pendingInteraction = TRUE; - return TRUE; - } - } - else if(obstacle->isPlayer) - { - // Follow mon can walk onto player - if (IsGeneratedOverworldEncounter(collider)) - { - sFollowMonData.pendingInteraction = TRUE; - return TRUE; - } - } - else if (!IsGeneratedOverworldEncounter(collider) && IsGeneratedOverworldEncounter(obstacle)) - { - // Other objects can walk through follow mons, whilst wandering mons is active + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + + if ((collider == player && IsGeneratedOverworldEncounter(obstacle)) + || (obstacle == player && IsGeneratedOverworldEncounter(collider))) + return sFollowMonData.pendingInteraction = TRUE; + + // Non-player, non-overworld encounters do not have collision with overworld encounters + if (!IsGeneratedOverworldEncounter(collider) && IsGeneratedOverworldEncounter(obstacle)) return TRUE; - } return FALSE; } From c2047018456759d8eff250d2fa20d81e9645f8fd Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 26 Nov 2025 23:11:45 +0000 Subject: [PATCH 049/572] Just make default Tall/Long Grass Field Effect Tile Shared --- data/field_effect_scripts.s | 14 +---- include/constants/field_effects.h | 4 -- .../field_effect_object_template_pointers.h | 4 -- src/data/field_effects/field_effect_objects.h | 24 +------- src/event_object_movement.c | 24 ++------ src/field_effect_helpers.c | 61 +------------------ 6 files changed, 10 insertions(+), 121 deletions(-) diff --git a/data/field_effect_scripts.s b/data/field_effect_scripts.s index 0a0200048200..9050a2debd81 100644 --- a/data/field_effect_scripts.s +++ b/data/field_effect_scripts.s @@ -84,8 +84,6 @@ gFieldEffectScriptPointers:: .4byte gFieldEffectScript_UseRockClimb @ FLDEFF_USE_ROCK_CLIMB .4byte gFieldEffectScript_RockClimbDust @ FLDEFF_ROCK_CLIMB_DUST .4byte gFieldEffectScript_ORASDowse @ FLDEFF_ORAS_DOWSE - .4byte gFieldEffectScript_TallGrass_OWE @ FLDEFF_TALL_GRASS_OWE - .4byte gFieldEffectScript_LongGrass_OWE @ FLDEFF_LONG_GRASS_OWE gFieldEffectScript_ExclamationMarkIcon1:: field_eff_callnative FldEff_ExclamationMarkIcon @@ -104,11 +102,7 @@ gFieldEffectScript_Shadow:: field_eff_end gFieldEffectScript_TallGrass:: - field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_TallGrass - field_eff_end - -gFieldEffectScript_TallGrass_OWE:: - field_eff_loadgfx_callnative gSpriteSheet_TallGrass, gSpritePalette_GeneralFieldEffect1, FldEff_TallGrass_OverworldEncounter + field_eff_loadgfx_callnative gSpriteSheet_TallGrass, gSpritePalette_GeneralFieldEffect1, FldEff_TallGrass field_eff_end gFieldEffectScript_Ripple:: @@ -160,11 +154,7 @@ gFieldEffectScript_JumpSmallSplash:: field_eff_end gFieldEffectScript_LongGrass:: - field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_LongGrass - field_eff_end - -gFieldEffectScript_LongGrass_OWE:: - field_eff_loadgfx_callnative gSpriteSheet_LongGrass, gSpritePalette_GeneralFieldEffect1, FldEff_LongGrass_OverworldEncounter + field_eff_loadgfx_callnative gSpriteSheet_LongGrass, gSpritePalette_GeneralFieldEffect1, FldEff_LongGrass field_eff_end gFieldEffectScript_JumpLongGrass:: diff --git a/include/constants/field_effects.h b/include/constants/field_effects.h index 2b94ab922048..c3aadad320dd 100644 --- a/include/constants/field_effects.h +++ b/include/constants/field_effects.h @@ -80,8 +80,6 @@ #define FLDEFF_USE_ROCK_CLIMB 75 #define FLDEFF_ROCK_CLIMB_DUST 76 #define FLDEFF_ORAS_DOWSE 77 -#define FLDEFF_TALL_GRASS_OWE 78 -#define FLDEFF_LONG_GRASS_OWE 79 #define FLDEFFOBJ_SHADOW_S 0 #define FLDEFFOBJ_SHADOW_M 1 @@ -129,8 +127,6 @@ #define FLDEFFOBJ_ORAS_DOWSE_BRENDAN 43 #define FLDEFFOBJ_ORAS_DOWSE_MAY 44 #define FLDEFFOBJ_SHINY_SPARKLE 45 -#define FLDEFFOBJ_TALL_GRASS_OWE 46 -#define FLDEFFOBJ_LONG_GRASS_OWE 47 #define FLDEFF_PAL_TAG_CUT_GRASS 0x1000 #define FLDEFF_PAL_TAG_SECRET_POWER_TREE 0x1003 diff --git a/src/data/field_effects/field_effect_object_template_pointers.h b/src/data/field_effects/field_effect_object_template_pointers.h index 49fb9f575504..4abed5e03dfc 100755 --- a/src/data/field_effects/field_effect_object_template_pointers.h +++ b/src/data/field_effects/field_effect_object_template_pointers.h @@ -45,8 +45,6 @@ extern const struct SpriteTemplate gFieldEffectObjectTemplate_RockClimbBlob; extern const struct SpriteTemplate gFieldEffectObjectTemplate_RockClimbDust; extern const struct SpriteTemplate gFieldEffectObjectTemplate_ORASDowsingBrendan; extern const struct SpriteTemplate gFieldEffectObjectTemplate_ORASDowsingMay; -extern const struct SpriteTemplate gFieldEffectObjectTemplate_TallGrass_OverworldEncounter; -extern const struct SpriteTemplate gFieldEffectObjectTemplate_LongGrass_OverworldEncounter; const struct SpriteTemplate *const gFieldEffectObjectTemplatePointers[] = { [FLDEFFOBJ_SHADOW_S] = &gFieldEffectObjectTemplate_ShadowSmall, @@ -95,6 +93,4 @@ const struct SpriteTemplate *const gFieldEffectObjectTemplatePointers[] = { [FLDEFFOBJ_ORAS_DOWSE_BRENDAN] = &gFieldEffectObjectTemplate_ORASDowsingBrendan, [FLDEFFOBJ_ORAS_DOWSE_MAY] = &gFieldEffectObjectTemplate_ORASDowsingMay, [FLDEFFOBJ_SHINY_SPARKLE] = &gFieldEffectObjectTemplate_ShinySparkle, - [FLDEFFOBJ_TALL_GRASS_OWE] = &gFieldEffectObjectTemplate_TallGrass_OverworldEncounter, - [FLDEFFOBJ_LONG_GRASS_OWE] = &gFieldEffectObjectTemplate_LongGrass_OverworldEncounter, }; diff --git a/src/data/field_effects/field_effect_objects.h b/src/data/field_effects/field_effect_objects.h index 8da5c9c61866..f50b044c7f99 100755 --- a/src/data/field_effects/field_effect_objects.h +++ b/src/data/field_effects/field_effect_objects.h @@ -139,16 +139,6 @@ static const union AnimCmd *const sAnimTable_TallGrass[] = sAnim_TallGrass, }; -const struct SpriteTemplate gFieldEffectObjectTemplate_TallGrass = { - .tileTag = TAG_NONE, - .paletteTag = FLDEFF_PAL_TAG_GENERAL_1, - .oam = &gObjectEventBaseOam_16x16, - .anims = sAnimTable_TallGrass, - .images = sPicTable_TallGrass, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = UpdateTallGrassFieldEffect, -}; - const struct SpriteSheet gSpriteSheet_TallGrass = { .data = gFieldEffectObjectPic_TallGrass, @@ -156,7 +146,7 @@ const struct SpriteSheet gSpriteSheet_TallGrass = .tag = FLDEFF_TILE_TAG_TALL_GRASS_OWE, }; -const struct SpriteTemplate gFieldEffectObjectTemplate_TallGrass_OverworldEncounter = { +const struct SpriteTemplate gFieldEffectObjectTemplate_TallGrass = { .tileTag = FLDEFF_TILE_TAG_TALL_GRASS_OWE, .paletteTag = FLDEFF_PAL_TAG_GENERAL_1, .oam = &gObjectEventBaseOam_16x16, @@ -748,16 +738,6 @@ static const union AnimCmd *const sAnimTable_LongGrass[] = sAnim_LongGrass, }; -const struct SpriteTemplate gFieldEffectObjectTemplate_LongGrass = { - .tileTag = TAG_NONE, - .paletteTag = FLDEFF_PAL_TAG_GENERAL_1, - .oam = &gObjectEventBaseOam_16x16, - .anims = sAnimTable_LongGrass, - .images = sPicTable_LongGrass, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = UpdateLongGrassFieldEffect, -}; - const struct SpriteSheet gSpriteSheet_LongGrass = { .data = gFieldEffectObjectPic_LongGrass, @@ -765,7 +745,7 @@ const struct SpriteSheet gSpriteSheet_LongGrass = .tag = FLDEFF_TILE_TAG_LONG_GRASS_OWE, }; -const struct SpriteTemplate gFieldEffectObjectTemplate_LongGrass_OverworldEncounter = { +const struct SpriteTemplate gFieldEffectObjectTemplate_LongGrass = { .tileTag = FLDEFF_TILE_TAG_LONG_GRASS_OWE, .paletteTag = FLDEFF_PAL_TAG_GENERAL_1, .oam = &gObjectEventBaseOam_16x16, diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 852a525386cf..a8c33b02dbf9 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -9947,11 +9947,7 @@ void GroundEffect_SpawnOnTallGrass(struct ObjectEvent *objEvent, struct Sprite * gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; gFieldEffectArguments[7] = TRUE; // skip to end of anim - - if (IsGeneratedOverworldEncounter(objEvent)) - FieldEffectStart(FLDEFF_TALL_GRASS_OWE); - else - FieldEffectStart(FLDEFF_TALL_GRASS); + FieldEffectStart(FLDEFF_TALL_GRASS); } void GroundEffect_StepOnTallGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) @@ -9964,11 +9960,7 @@ void GroundEffect_StepOnTallGrass(struct ObjectEvent *objEvent, struct Sprite *s gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; gFieldEffectArguments[7] = TRUE; // skip to end of anim - - if (IsGeneratedOverworldEncounter(objEvent)) - FieldEffectStart(FLDEFF_TALL_GRASS_OWE); - else - FieldEffectStart(FLDEFF_TALL_GRASS); + FieldEffectStart(FLDEFF_TALL_GRASS); } void GroundEffect_SpawnOnLongGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) @@ -9981,11 +9973,7 @@ void GroundEffect_SpawnOnLongGrass(struct ObjectEvent *objEvent, struct Sprite * gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; gFieldEffectArguments[7] = TRUE; - - if (IsGeneratedOverworldEncounter(objEvent)) - FieldEffectStart(FLDEFF_LONG_GRASS_OWE); - else - FieldEffectStart(FLDEFF_LONG_GRASS); + FieldEffectStart(FLDEFF_LONG_GRASS); } void GroundEffect_StepOnLongGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) @@ -9998,11 +9986,7 @@ void GroundEffect_StepOnLongGrass(struct ObjectEvent *objEvent, struct Sprite *s gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; gFieldEffectArguments[7] = TRUE; - - if (IsGeneratedOverworldEncounter(objEvent)) - FieldEffectStart(FLDEFF_LONG_GRASS_OWE); - else - FieldEffectStart(FLDEFF_LONG_GRASS); + FieldEffectStart(FLDEFF_LONG_GRASS); } void GroundEffect_WaterReflection(struct ObjectEvent *objEvent, struct Sprite *sprite) diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index d0ecd9f05369..5c757775f449 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -7,7 +7,6 @@ #include "fieldmap.h" #include "gpu_regs.h" #include "metatile_behavior.h" -#include "overworld_encounters.h" #include "palette.h" #include "sound.h" #include "sprite.h" @@ -451,31 +450,6 @@ u32 FldEff_TallGrass(void) return 0; } -u32 FldEff_TallGrass_OverworldEncounter(void) -{ - u8 spriteId; - s16 x = gFieldEffectArguments[0]; - s16 y = gFieldEffectArguments[1]; - SetSpritePosToOffsetMapCoords(&x, &y, 8, 8); - spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[FLDEFFOBJ_TALL_GRASS_OWE], x, y, 0); - if (spriteId != MAX_SPRITES) - { - struct Sprite *sprite = &gSprites[spriteId]; - sprite->coordOffsetEnabled = TRUE; - sprite->oam.priority = gFieldEffectArguments[3]; - sprite->sElevation = gFieldEffectArguments[2]; - sprite->sX = gFieldEffectArguments[0]; - sprite->sY = gFieldEffectArguments[1]; - sprite->sMapNum = gFieldEffectArguments[4]; // Also sLocalId - sprite->sMapGroup = gFieldEffectArguments[5]; - sprite->sCurrentMap = gFieldEffectArguments[6]; - - if (gFieldEffectArguments[7]) - SeekSpriteAnim(sprite, 4); // Skip to end of anim - } - return 0; -} - void UpdateTallGrassFieldEffect(struct Sprite *sprite) { u8 metatileBehavior; @@ -499,10 +473,7 @@ void UpdateTallGrassFieldEffect(struct Sprite *sprite) || !MetatileBehavior_IsTallGrass(metatileBehavior) || (sprite->sObjectMoved && sprite->animEnded)) { - if (IsGeneratedOverworldEncounter(&gObjectEvents[objectEventId])) - FieldEffectStop(sprite, FLDEFF_TALL_GRASS_OWE); - else - FieldEffectStop(sprite, FLDEFF_TALL_GRASS); + FieldEffectStop(sprite, FLDEFF_TALL_GRASS); } else { @@ -583,31 +554,6 @@ u32 FldEff_LongGrass(void) return 0; } -u32 FldEff_LongGrass_OverworldEncounter(void) -{ - u8 spriteId; - s16 x = gFieldEffectArguments[0]; - s16 y = gFieldEffectArguments[1]; - SetSpritePosToOffsetMapCoords(&x, &y, 8, 8); - spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[FLDEFFOBJ_LONG_GRASS_OWE], x, y, 0); - if (spriteId != MAX_SPRITES) - { - struct Sprite *sprite = &gSprites[spriteId]; - sprite->coordOffsetEnabled = TRUE; - sprite->oam.priority = ElevationToPriority(gFieldEffectArguments[2]); - sprite->sElevation = gFieldEffectArguments[2]; - sprite->sX = gFieldEffectArguments[0]; - sprite->sY = gFieldEffectArguments[1]; - sprite->sMapNum = gFieldEffectArguments[4]; // Also sLocalId - sprite->sMapGroup = gFieldEffectArguments[5]; - sprite->sCurrentMap = gFieldEffectArguments[6]; - - if (gFieldEffectArguments[7]) - SeekSpriteAnim(sprite, 6); // Skip to end of anim - } - return 0; -} - void UpdateLongGrassFieldEffect(struct Sprite *sprite) { u8 metatileBehavior; @@ -630,10 +576,7 @@ void UpdateLongGrassFieldEffect(struct Sprite *sprite) || !MetatileBehavior_IsLongGrass(metatileBehavior) || (sprite->sObjectMoved && sprite->animEnded)) { - if (IsGeneratedOverworldEncounter(&gObjectEvents[objectEventId])) - FieldEffectStop(sprite, FLDEFF_LONG_GRASS_OWE); - else - FieldEffectStop(sprite, FLDEFF_LONG_GRASS); + FieldEffectStop(sprite, FLDEFF_LONG_GRASS); } else { From 5ff5958d9276bbfee2c950a7dc153893dd74bd44 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 26 Nov 2025 23:51:59 +0000 Subject: [PATCH 050/572] Remove `bool8 pendingInteraction;` --- include/overworld_encounters.h | 2 -- src/event_object_movement.c | 2 +- src/field_control_avatar.c | 3 -- src/overworld_encounters.c | 65 ++++++++++++++++------------------ 4 files changed, 31 insertions(+), 41 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index c5dd152d6dcf..5143fa864e05 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -25,7 +25,6 @@ struct FollowMon struct FollowMonData { - bool8 pendingInteraction; u8 oldestSlot:4; u8 usedSlots:4; u16 spawnCountdown; @@ -39,7 +38,6 @@ extern const u8 InteractWithDynamicWildFollowMon[]; void LoadFollowMonData(struct ObjectEvent *objectEvent); void UpdateOverworldEncounters(void); void CreateFollowMonEncounter(void); -bool8 FollowMon_ProcessMonInteraction(void); bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index a8c33b02dbf9..4d9e95273eca 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -6468,7 +6468,7 @@ u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, b curObject = &gObjectEvents[i]; if (curObject->active && (curObject->movementType != MOVEMENT_TYPE_FOLLOW_PLAYER || objectEvent != &gObjectEvents[gPlayerAvatar.objectEventId]) && curObject != objectEvent && !FollowerNPC_IsCollisionExempt(curObject, objectEvent) // Partner - && !OverworldEncounter_IsCollisionExempt(curObject, objectEvent) // Wild Pokemon + && !OverworldEncounter_IsCollisionExempt(curObject, objectEvent) // Overworld Wild Encounters ) { // check for collision if curObject is active, not the object in question, and not exempt from collisions diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index fcea303a8ffc..e3965d9e214d 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -178,9 +178,6 @@ int ProcessPlayerFieldInput(struct FieldInput *input) if (TryRunOnFrameMapScript() == TRUE) return TRUE; - if (FollowMon_ProcessMonInteraction() == TRUE) - return TRUE; - if (input->pressedBButton && TrySetupDiveEmergeScript() == TRUE) return TRUE; if (input->tookStep) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7f04df1d3cd9..ecddd964b8ea 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -34,6 +34,7 @@ static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height); static bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); static bool32 IsInsidePlayerMap(s16 x, s16 y); +static bool32 OverworldEncounters_ProcessMonInteraction(void); #define sEncounterIndex trainerRange_berryTreeId @@ -67,6 +68,8 @@ void UpdateOverworldEncounters(void) return; } + OverworldEncounters_ProcessMonInteraction(); + if(sFollowMonData.spawnCountdown == 0) { s16 x, y; @@ -372,42 +375,30 @@ void CreateFollowMonEncounter(void) { SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); } -bool8 FollowMon_ProcessMonInteraction(void) +static bool32 OverworldEncounters_ProcessMonInteraction(void) { - if(VarGet(VAR_REPEL_STEP_COUNT) != 0) - { - // Never auto trigger battle whilst repel is active - sFollowMonData.pendingInteraction = FALSE; - return FALSE; - } - - if(sFollowMonData.pendingInteraction) - { - u8 i; - struct ObjectEvent *curObject; - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + u8 i; + struct ObjectEvent *curObject; + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - sFollowMonData.pendingInteraction = FALSE; - - for (i = 0; i < OBJECT_EVENTS_COUNT; i++) + for (i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + curObject = &gObjectEvents[i]; + if (curObject->active && curObject != player && IsGeneratedOverworldEncounter(curObject)) { - curObject = &gObjectEvents[i]; - if (curObject->active && curObject != player && IsGeneratedOverworldEncounter(curObject)) + if ((curObject->currentCoords.x == player->currentCoords.x && curObject->currentCoords.y == player->currentCoords.y) || (curObject->previousCoords.x == player->currentCoords.x && curObject->previousCoords.y == player->currentCoords.y)) { - if ((curObject->currentCoords.x == player->currentCoords.x && curObject->currentCoords.y == player->currentCoords.y) || (curObject->previousCoords.x == player->currentCoords.x && curObject->previousCoords.y == player->currentCoords.y)) + if (AreElevationsCompatible(curObject->currentElevation, player->currentElevation)) { - if (AreElevationsCompatible(curObject->currentElevation, player->currentElevation)) - { - // There is a valid collision so exectute the attached script - const u8* script = InteractWithDynamicWildFollowMon; - gSpecialVar_LastTalked = curObject->localId; - //VarSet(VAR_LAST_TALKED, curObject->localId); - ScriptContext_SetupScript(script); - - //CreateFollowMonEncounter(); - //BattleSetup_StartScriptedWildBattle(); - return TRUE; - } + // There is a valid collision so exectute the attached script + const u8* script = InteractWithDynamicWildFollowMon; + gSpecialVar_LastTalked = curObject->localId; + //VarSet(VAR_LAST_TALKED, curObject->localId); + ScriptContext_SetupScript(script); + + //CreateFollowMonEncounter(); + //BattleSetup_StartScriptedWildBattle(); + return TRUE; } } } @@ -418,13 +409,17 @@ bool8 FollowMon_ProcessMonInteraction(void) bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider) { + // The player can only collide with overworld encounters when not using a repel. + // Non-player, non-overworld encounters do not have collision with overworld encounters. + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - if ((collider == player && IsGeneratedOverworldEncounter(obstacle)) - || (obstacle == player && IsGeneratedOverworldEncounter(collider))) - return sFollowMonData.pendingInteraction = TRUE; + if (collider == player && IsGeneratedOverworldEncounter(obstacle) /* && VarGet(VAR_REPEL_STEP_COUNT) == 0 */) + return TRUE; + + if (obstacle == player && IsGeneratedOverworldEncounter(collider) /* && VarGet(VAR_REPEL_STEP_COUNT) == 0 */) + return TRUE; - // Non-player, non-overworld encounters do not have collision with overworld encounters if (!IsGeneratedOverworldEncounter(collider) && IsGeneratedOverworldEncounter(obstacle)) return TRUE; From 3717b361af69b6d7f7b6deb2771b5a3e4def15cf Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 28 Nov 2025 20:47:20 +0000 Subject: [PATCH 051/572] OverworldEncounters_ProcessMonInteraction Improvements --- src/overworld_encounters.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index ecddd964b8ea..31fa55764a80 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -34,7 +34,7 @@ static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height); static bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); static bool32 IsInsidePlayerMap(s16 x, s16 y); -static bool32 OverworldEncounters_ProcessMonInteraction(void); +static void OverworldEncounters_ProcessMonInteraction(void); #define sEncounterIndex trainerRange_berryTreeId @@ -375,36 +375,24 @@ void CreateFollowMonEncounter(void) { SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); } -static bool32 OverworldEncounters_ProcessMonInteraction(void) +static void OverworldEncounters_ProcessMonInteraction(void) { u8 i; - struct ObjectEvent *curObject; + struct ObjectEvent *object; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { - curObject = &gObjectEvents[i]; - if (curObject->active && curObject != player && IsGeneratedOverworldEncounter(curObject)) + object = &gObjectEvents[i]; + if (IsGeneratedOverworldEncounter(object) && object->active && object != player + && ((object->currentCoords.x == player->currentCoords.x && object->currentCoords.y == player->currentCoords.y) + || (object->previousCoords.x == player->currentCoords.x && object->previousCoords.y == player->currentCoords.y)) + && AreElevationsCompatible(object->currentElevation, player->currentElevation)) { - if ((curObject->currentCoords.x == player->currentCoords.x && curObject->currentCoords.y == player->currentCoords.y) || (curObject->previousCoords.x == player->currentCoords.x && curObject->previousCoords.y == player->currentCoords.y)) - { - if (AreElevationsCompatible(curObject->currentElevation, player->currentElevation)) - { - // There is a valid collision so exectute the attached script - const u8* script = InteractWithDynamicWildFollowMon; - gSpecialVar_LastTalked = curObject->localId; - //VarSet(VAR_LAST_TALKED, curObject->localId); - ScriptContext_SetupScript(script); - - //CreateFollowMonEncounter(); - //BattleSetup_StartScriptedWildBattle(); - return TRUE; - } - } + gSpecialVar_LastTalked = object->localId; + ScriptContext_SetupScript(InteractWithDynamicWildFollowMon); } } - - return FALSE; } bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider) From b8703a8595baa18dd01657c18d27264b35ab7ea5 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 28 Nov 2025 21:23:54 +0000 Subject: [PATCH 052/572] Remove CountFreePaletteSlots --- include/sprite.h | 1 - src/sprite.c | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/include/sprite.h b/include/sprite.h index e9b964385b5b..791907505319 100644 --- a/include/sprite.h +++ b/include/sprite.h @@ -305,7 +305,6 @@ void FreeSpriteTileRanges(void); u16 GetSpriteTileStartByTag(u16 tag); u16 GetSpriteTileTagByTileStart(u16 start); void FreeAllSpritePalettes(void); -u8 CountFreePaletteSlots(void); u32 LoadSpritePalette(const struct SpritePalette *palette); u32 LoadSpritePaletteWithTag(const u16 *pal, u16 tag); u8 LoadSpritePaletteInSlot(const struct SpritePalette *palette, u8 paletteNum); diff --git a/src/sprite.c b/src/sprite.c index 8d6e94c77f78..1b89a50be16b 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -1653,17 +1653,6 @@ u32 IndexOfSpritePaletteTag(u16 tag) return 0xFF; } -u8 CountFreePaletteSlots(void) -{ - u32 i; - u8 count = 0; - for (i = gReservedSpritePaletteCount; i < 16; i++) - if (sSpritePaletteTags[i] == TAG_NONE) - ++count; - - return count; -} - u16 GetSpritePaletteTagByPaletteNum(u8 paletteNum) { return sSpritePaletteTags[paletteNum]; From ae8954699d49ec9f805166268575ff2072e4106c Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 28 Nov 2025 17:35:54 -0600 Subject: [PATCH 053/572] Bugfix: FollowMon_OnObjectEventSpawned never ran --- src/overworld_encounters.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index ecddd964b8ea..c1e8e33ae2ce 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -86,6 +86,7 @@ void UpdateOverworldEncounters(void) const struct FollowMon *followMon = &sFollowMonData.list[spawnSlot]; bool32 waterMons = IsSpawningWaterMons(); bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; + u32 localId = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - spawnSlot; u32 movementType; if (OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT) // These checks need to be improved { @@ -100,20 +101,22 @@ void UpdateOverworldEncounters(void) { movementType = MOVEMENT_TYPE_WANDER_ON_MAP; } - u8 localId = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - spawnSlot; - u8 objectEventId = SpawnSpecialObjectEventParameterized( - GetFollowMonObjectEventGraphicsId(spawnSlot), - movementType, - localId, - x, - y, - MapGridGetElevationAt(x, y) - ); + + struct ObjectEventTemplate objectEventTemplate = { + .localId = localId, + .graphicsId = GetFollowMonObjectEventGraphicsId(spawnSlot), + .x = x - MAP_OFFSET, + .y = y - MAP_OFFSET, + .elevation = MapGridGetElevationAt(x, y), + .movementType = movementType, + .trainerType = TRAINER_TYPE_ENCOUNTER, + }; + + u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; gObjectEvents[objectEventId].range.rangeX = 8; gObjectEvents[objectEventId].range.rangeY = 8; - gObjectEvents[objectEventId].trainerType = TRAINER_TYPE_ENCOUNTER; // Only used for save/load as well as loading encounters, // Most of the time, followmon data is tracked in sFollowMonData From b3f3f1daf558e12e86bfd76386c38189b028992a Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 28 Nov 2025 17:59:40 -0600 Subject: [PATCH 054/572] Changed localIds to be after dynamic start --- include/constants/event_objects.h | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 27a748d1b6b9..4a05a35253b1 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -342,19 +342,17 @@ // There are a few special IDs reserved for objects that don't have templates in the map data -- one for the player // in regular offline play, five for linked players while playing Berry Blender, and one for an invisible object that // can be spawned for the camera to track instead of the player. Additionally, the value 0 is reserved as an "empty" indicator. -#define LOCALID_NONE 0 -#define LOCALID_CAMERA 127 -#define LOCALID_BERRY_BLENDER_PLAYER_END 240 // This will use 5 (MAX_RFU_PLAYERS) IDs ending at 240, i.e. 236-240 -#define LOCALID_FOLLOWING_POKEMON 254 -#define LOCALID_PLAYER 255 -#define OBJ_EVENT_ID_FOLLOWER 0xFE -#define OBJ_EVENT_ID_NPC_FOLLOWER 0xFD +#define LOCALID_NONE 0 +#define LOCALID_CAMERA 127 +#define LOCALID_BERRY_BLENDER_PLAYER_END 240 // This will use 5 (MAX_RFU_PLAYERS) IDs ending at 240, i.e. 236-240 +#define LOCALID_FOLLOWING_POKEMON 254 +#define LOCALID_PLAYER 255 +#define OBJ_EVENT_ID_FOLLOWER 0xFE +#define OBJ_EVENT_ID_NPC_FOLLOWER 0xFD +#define OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER 0xFC // This will use up to 5 IDs for OW Encounters. (248-252) // Aliases for old names. "object event id" normally refers to an index into gObjectEvents, which these are not. // Used for link player OWs in CreateLinkPlayerSprite -#define OBJ_EVENT_ID_DYNAMIC_BASE 0xF0 -// Uses OBJ_EVENT_ID_DYNAMIC_BASE as the last local id for OW Encounter Objects -#define OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER OBJ_EVENT_ID_DYNAMIC_BASE #define OBJ_EVENT_ID_CAMERA LOCALID_CAMERA #define OBJ_EVENT_ID_PLAYER LOCALID_PLAYER From 595106b2954f9b7aabfe56c89b40a6e8647c8a5e Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 28 Nov 2025 18:16:13 -0600 Subject: [PATCH 055/572] Reworked pendingSpawnAnim --- include/overworld_encounters.h | 9 ++--- src/overworld_encounters.c | 62 ++++++++++++++-------------------- 2 files changed, 30 insertions(+), 41 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 5143fa864e05..b19d919b871f 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -25,11 +25,12 @@ struct FollowMon struct FollowMonData { - u8 oldestSlot:4; - u8 usedSlots:4; - u16 spawnCountdown; - u16 pendingSpawnAnim; struct FollowMon list[FOLLOWMON_MAX_SPAWN_SLOTS]; + u16 spawnCountdown; + u16 oldestSlot:4; + u16 usedSlots:4; + u16 pendingSpawnAnim:3; + u16 padding:5; }; //data/scripts/followmon.inc diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c1e8e33ae2ce..e74ffdb3e6bc 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -40,7 +40,7 @@ static bool32 OverworldEncounters_ProcessMonInteraction(void); void LoadFollowMonData(struct ObjectEvent *objectEvent) { - u8 slot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; + u32 slot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - objectEvent->localId; sFollowMonData.list[slot].isShiny = objectEvent->shiny; sFollowMonData.list[slot].timeOfDay = objectEvent->spawnTimeOfDay; sFollowMonData.list[slot].encounterIndex = objectEvent->sEncounterIndex; @@ -142,44 +142,30 @@ void UpdateOverworldEncounters(void) // Play spawn animation when player is close enough if(sFollowMonData.pendingSpawnAnim != 0) { - u16 spawnSlot; - u16 gfxId; - u16 bitFlag; - u8 objectEventId; + u32 spawnSlot = sFollowMonData.pendingSpawnAnim - 1; + u32 objEventId = GetObjectEventIdByLocalId(OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - spawnSlot); enum FollowMonSpawnAnim spawnAnimType; - for (gfxId = OBJ_EVENT_GFX_FOLLOW_MON_FIRST; gfxId < OBJ_EVENT_GFX_FOLLOW_MON_LAST; ++gfxId) + if (sFollowMonData.list[spawnSlot].encounterIndex != 0) { - spawnSlot = gfxId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; - bitFlag = (1 << spawnSlot); - - if ((sFollowMonData.pendingSpawnAnim & bitFlag) != 0) + if(sFollowMonData.list[spawnSlot].isShiny) { - objectEventId = FindObjectEventForGfx(gfxId); - - if (objectEventId != OBJECT_EVENTS_COUNT) - { - if(sFollowMonData.list[spawnSlot].isShiny) - { - PlaySE(SE_SHINY); - spawnAnimType = FOLLOWMON_SPAWN_ANIM_SHINY; - sFollowMonData.pendingSpawnAnim &= ~bitFlag; - } - else - { - PlayCry_Normal(GetFollowMonSpecies(&sFollowMonData.list[spawnSlot]), 25); - if (IsSpawningWaterMons()) - spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; - else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) - spawnAnimType = FOLLOWMON_SPAWN_ANIM_CAVE; - else - spawnAnimType = FOLLOWMON_SPAWN_ANIM_GRASS; - } - // Instantly play a small animation to ground the spawning a bit - MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objectEventId]); - sFollowMonData.pendingSpawnAnim &= ~bitFlag; - } + PlaySE(SE_SHINY); + spawnAnimType = FOLLOWMON_SPAWN_ANIM_SHINY; } + else + { + PlayCry_Normal(GetFollowMonSpecies(&sFollowMonData.list[spawnSlot]), 25); + if (IsSpawningWaterMons()) + spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; + else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) + spawnAnimType = FOLLOWMON_SPAWN_ANIM_CAVE; + else + spawnAnimType = FOLLOWMON_SPAWN_ANIM_GRASS; + } + // Instantly play a small animation to ground the spawning a bit + MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objEventId]); + sFollowMonData.pendingSpawnAnim = 0; } } } @@ -432,9 +418,10 @@ bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { u32 i; - u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; + u32 spawnSlot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - objectEvent->localId; + sFollowMonData.usedSlots++; - sFollowMonData.pendingSpawnAnim |= (1 << spawnSlot); + sFollowMonData.pendingSpawnAnim = spawnSlot + 1; // Increase the age of all followmons for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) @@ -446,7 +433,8 @@ void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { - u16 spawnSlot = objectEvent->graphicsId - OBJ_EVENT_GFX_FOLLOW_MON_FIRST; + u32 spawnSlot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - objectEvent->localId; + sFollowMonData.list[spawnSlot].encounterIndex = 0; sFollowMonData.list[spawnSlot].age = 0; sFollowMonData.usedSlots--; From d9e891fc58f58631e79b932b965a623aac5d209d Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 28 Nov 2025 18:22:29 -0600 Subject: [PATCH 056/572] Removed usedSlots (unused) --- include/overworld_encounters.h | 6 ++---- src/overworld_encounters.c | 4 ---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index b19d919b871f..0ee7a650e626 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -27,10 +27,8 @@ struct FollowMonData { struct FollowMon list[FOLLOWMON_MAX_SPAWN_SLOTS]; u16 spawnCountdown; - u16 oldestSlot:4; - u16 usedSlots:4; - u16 pendingSpawnAnim:3; - u16 padding:5; + u8 oldestSlot:4; + u8 pendingSpawnAnim:4; }; //data/scripts/followmon.inc diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e74ffdb3e6bc..4abf565f9202 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -47,7 +47,6 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent) sFollowMonData.list[slot].onWater = MetatileBehavior_IsWaterWildEncounter(objectEvent->currentMetatileBehavior); sFollowMonData.spawnCountdown += 60; - sFollowMonData.usedSlots++; } void UpdateOverworldEncounters(void) @@ -420,7 +419,6 @@ void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) u32 i; u32 spawnSlot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - objectEvent->localId; - sFollowMonData.usedSlots++; sFollowMonData.pendingSpawnAnim = spawnSlot + 1; // Increase the age of all followmons @@ -437,7 +435,6 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) sFollowMonData.list[spawnSlot].encounterIndex = 0; sFollowMonData.list[spawnSlot].age = 0; - sFollowMonData.usedSlots--; } u16 GetFollowMonObjectEventGraphicsId(u16 spawnSlot) @@ -457,7 +454,6 @@ u16 GetFollowMonObjectEventGraphicsId(u16 spawnSlot) void ClearOverworldEncounterData(void) { sFollowMonData.spawnCountdown = 0; - sFollowMonData.usedSlots = 0; sFollowMonData.oldestSlot = 0; for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) From 04047033bbbde2b1b0b37df51b1da292d8de4c9a Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 28 Nov 2025 23:47:26 -0600 Subject: [PATCH 057/572] Reworked species and level data --- include/constants/event_objects.h | 10 --- include/overworld_encounters.h | 13 ++-- include/wild_encounter.h | 1 - src/overworld_encounters.c | 102 ++++++++++++------------------ src/wild_encounter.c | 17 ----- 5 files changed, 47 insertions(+), 96 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 4a05a35253b1..2046f84158ff 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -278,16 +278,6 @@ #define OBJ_EVENT_GFX_VAR_FIRST OBJ_EVENT_GFX_VAR_0 #define OBJ_EVENT_GFX_VAR_LAST OBJ_EVENT_GFX_VAR_F -#define OBJ_EVENT_GFX_FOLLOW_MON_0 512 //we want it to be a power of 2 to check followmon with bit masking -#define OBJ_EVENT_GFX_FOLLOW_MON_1 (OBJ_EVENT_GFX_FOLLOW_MON_0 + 1) -#define OBJ_EVENT_GFX_FOLLOW_MON_2 (OBJ_EVENT_GFX_FOLLOW_MON_0 + 2) -#define OBJ_EVENT_GFX_FOLLOW_MON_3 (OBJ_EVENT_GFX_FOLLOW_MON_0 + 3) -#define OBJ_EVENT_GFX_FOLLOW_MON_4 (OBJ_EVENT_GFX_FOLLOW_MON_0 + 4) -#define OBJ_EVENT_GFX_FOLLOW_MON_5 (OBJ_EVENT_GFX_FOLLOW_MON_0 + 5) - -#define OBJ_EVENT_GFX_FOLLOW_MON_FIRST OBJ_EVENT_GFX_FOLLOW_MON_0 -#define OBJ_EVENT_GFX_FOLLOW_MON_LAST OBJ_EVENT_GFX_FOLLOW_MON_5 - // Don't use (1u << 15) to avoid conflict with BLEND_IMMUNE_FLAG. #define OBJ_EVENT_MON (1u << 14) #define OBJ_EVENT_MON_SHINY (1u << 13) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 0ee7a650e626..4b8c302652bd 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -10,19 +10,16 @@ #define INVALID_SPAWN_SLOT 0xFF -// Could be reduced to an u8 but I prefer to leave some potential for more advanced features struct FollowMon { + u16 species; + u16 level:7; u16 isShiny:1; - u16 onWater:1; - u16 timeOfDay:2; + u16 isFemale:1; + u16 form:3; u16 age:4; - u16 encounterIndex:8; - u16 padding; }; -#define EMPTY_FOLLOWMON 0xFF; - struct FollowMonData { struct FollowMon list[FOLLOWMON_MAX_SPAWN_SLOTS]; @@ -40,7 +37,7 @@ void CreateFollowMonEncounter(void); bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); -u16 GetFollowMonObjectEventGraphicsId(u16 graphicsId); +u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y); void ClearOverworldEncounterData(void); void RemoveOverworldEncounterObjects(void); bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 8b38978ceea0..02fb69c36aaa 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -50,7 +50,6 @@ extern bool8 gIsFishingEncounter; extern bool8 gIsSurfingEncounter; extern u8 gChainFishingDexNavStreak; -void GenerateFollowMon(struct FollowMon *followMon, bool8 inWater); u8 ChooseWildMonLevel(const struct WildPokemon *wildPokemon, u8 wildMonIndex, enum WildPokemonArea area); void DisableWildEncounters(bool8 disabled); u8 PickWildMonNature(void); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 4abf565f9202..b17ddb5bc1a3 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -24,10 +24,9 @@ static u8 CountActiveFollowMon(); static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool8 IsSpawningWaterMons(); -static u16 GetFollowMonSpecies(struct FollowMon *followMon); +static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y); static u8 CountActiveObjectEvents(); static bool8 IsSafeToSpawnObjectEvents(void); -static u8 FindObjectEventForGfx(u16 gfxId); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); static bool8 AreElevationsCompatible(u8 a, u8 b); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); @@ -42,9 +41,6 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent) { u32 slot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - objectEvent->localId; sFollowMonData.list[slot].isShiny = objectEvent->shiny; - sFollowMonData.list[slot].timeOfDay = objectEvent->spawnTimeOfDay; - sFollowMonData.list[slot].encounterIndex = objectEvent->sEncounterIndex; - sFollowMonData.list[slot].onWater = MetatileBehavior_IsWaterWildEncounter(objectEvent->currentMetatileBehavior); sFollowMonData.spawnCountdown += 60; } @@ -103,7 +99,7 @@ void UpdateOverworldEncounters(void) struct ObjectEventTemplate objectEventTemplate = { .localId = localId, - .graphicsId = GetFollowMonObjectEventGraphicsId(spawnSlot), + .graphicsId = GetFollowMonObjectEventGraphicsId(spawnSlot, x, y), .x = x - MAP_OFFSET, .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), @@ -120,8 +116,6 @@ void UpdateOverworldEncounters(void) // Only used for save/load as well as loading encounters, // Most of the time, followmon data is tracked in sFollowMonData gObjectEvents[objectEventId].shiny = followMon->isShiny; - gObjectEvents[objectEventId].spawnTimeOfDay = followMon->timeOfDay; - gObjectEvents[objectEventId].sEncounterIndex = followMon->encounterIndex; // Hide reflections for spawns in water // (It just looks weird) @@ -145,7 +139,7 @@ void UpdateOverworldEncounters(void) u32 objEventId = GetObjectEventIdByLocalId(OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - spawnSlot); enum FollowMonSpawnAnim spawnAnimType; - if (sFollowMonData.list[spawnSlot].encounterIndex != 0) + if (sFollowMonData.list[spawnSlot].species != SPECIES_NONE) { if(sFollowMonData.list[spawnSlot].isShiny) { @@ -154,7 +148,7 @@ void UpdateOverworldEncounters(void) } else { - PlayCry_Normal(GetFollowMonSpecies(&sFollowMonData.list[spawnSlot]), 25); + PlayCry_Normal(sFollowMonData.list[spawnSlot].species, 25); if (IsSpawningWaterMons()) spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) @@ -184,7 +178,7 @@ static u32 GetNewOldestSlot(u32 oldSlot) for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - if (sFollowMonData.list[i].encounterIndex != 0) + if (sFollowMonData.list[i].species != SPECIES_NONE) { if (i != oldSlot && (nextOldest == FOLLOWMON_MAX_SPAWN_SLOTS || sFollowMonData.list[i].age > sFollowMonData.list[nextOldest].age)) nextOldest = i; @@ -210,7 +204,7 @@ static u8 NextSpawnMonSlot(void) { for (slot = 0; slot < maxSpawns; slot++) { - if (sFollowMonData.list[slot].encounterIndex == 0) + if (sFollowMonData.list[slot].species == SPECIES_NONE) break; } } @@ -221,16 +215,14 @@ static u8 NextSpawnMonSlot(void) // Check that we don't have too many sprites on screen before spawning // (lag reduction) if(CountActiveObjectEvents() >= FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT) + // TODO: Clear slot data. return INVALID_SPAWN_SLOT; - GenerateFollowMon(&sFollowMonData.list[slot], IsSpawningWaterMons()); - return slot; } static bool8 TrySelectTile(s16* outX, s16* outY) { - u8 tryCount; u8 elevation; u16 tileBehavior; s16 playerX, playerY; @@ -317,7 +309,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) void CreateFollowMonEncounter(void) { struct ObjectEvent *curObject; - u8 objEventId = GetObjectEventIdByLocalIdAndMap(gSpecialVar_LastTalked, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + u32 objEventId = GetObjectEventIdByLocalIdAndMap(gSpecialVar_LastTalked, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); if (objEventId < OBJECT_EVENTS_COUNT) { @@ -330,30 +322,14 @@ void CreateFollowMonEncounter(void) { return; } - const struct WildPokemonInfo *wildMonInfo; - u32 headerId = GetCurrentMapWildMonHeaderId(); - u8 index = curObject->sEncounterIndex - 1; - u8 level = 0; - - if (MetatileBehavior_IsWaterWildEncounter(curObject->currentMetatileBehavior)) - { - wildMonInfo = gWildMonHeaders[headerId].encounterTypes[curObject->spawnTimeOfDay].waterMonsInfo; - level = ChooseWildMonLevel(wildMonInfo->wildPokemon, index, WILD_AREA_WATER); - } - else - { - wildMonInfo = gWildMonHeaders[headerId].encounterTypes[curObject->spawnTimeOfDay].landMonsInfo; - level = ChooseWildMonLevel(wildMonInfo->wildPokemon,index, WILD_AREA_LAND); - } - - u16 species = wildMonInfo->wildPokemon[index].species; - bool8 shiny = curObject->shiny; + u32 slot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - gObjectEvents[objEventId].localId; + bool32 shiny = curObject->shiny; ZeroEnemyPartyMons(); CreateMon( &gEnemyParty[0], - species, - level, + sFollowMonData.list[slot].species, + sFollowMonData.list[slot].level, USE_RANDOM_IVS, FALSE, 0, @@ -424,7 +400,7 @@ void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) // Increase the age of all followmons for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - if (sFollowMonData.list[i].encounterIndex != 0) + if (sFollowMonData.list[i].species != SPECIES_NONE) sFollowMonData.list[i].age++; } } @@ -433,13 +409,13 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { u32 spawnSlot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - objectEvent->localId; - sFollowMonData.list[spawnSlot].encounterIndex = 0; + sFollowMonData.list[spawnSlot].species = SPECIES_NONE; sFollowMonData.list[spawnSlot].age = 0; } -u16 GetFollowMonObjectEventGraphicsId(u16 spawnSlot) +u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y) { - u16 species = GetFollowMonSpecies(&sFollowMonData.list[spawnSlot]); + u16 species = GetFollowMonSpecies(spawnSlot, x, y); u16 graphicsId = species + OBJ_EVENT_MON; // if (sFollowMonData.list[slot].isFemale) @@ -458,20 +434,39 @@ void ClearOverworldEncounterData(void) for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - sFollowMonData.list[i].encounterIndex = 0; + sFollowMonData.list[i].species = SPECIES_NONE; sFollowMonData.list[i].age = 0; } } -static u16 GetFollowMonSpecies(struct FollowMon *followMon) +static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y) { - u16 species = 0; + const struct WildPokemonInfo *wildMonInfo; + u32 species = 0; u32 headerId = GetCurrentMapWildMonHeaderId(); + u32 tileBehavior = MapGridGetMetatileBehaviorAt(x, y); + u32 timeOfDay, encounterIndex, level; - if (followMon->onWater) - species = gWildMonHeaders[headerId].encounterTypes[followMon->timeOfDay].waterMonsInfo->wildPokemon[followMon->encounterIndex - 1].species; + if (MetatileBehavior_IsWaterWildEncounter(tileBehavior)) + { + encounterIndex = ChooseWildMonIndex_Water(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + species = wildMonInfo->wildPokemon[encounterIndex].species; + level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_WATER); + } else - species = gWildMonHeaders[headerId].encounterTypes[followMon->timeOfDay].landMonsInfo->wildPokemon[followMon->encounterIndex - 1].species; + { + encounterIndex = ChooseWildMonIndex_Land(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + species = wildMonInfo->wildPokemon[encounterIndex].species; + level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_LAND); + } + + sFollowMonData.list[spawnSlot].species = species; + sFollowMonData.list[spawnSlot].level = level; + sFollowMonData.list[spawnSlot].isShiny = ComputePlayerShinyOdds(Random32()); return species; } @@ -489,7 +484,7 @@ static u8 CountActiveFollowMon() u8 count = 0; for(u8 slot = 0; slot < FOLLOWMON_MAX_SPAWN_SLOTS; slot++) { - if(sFollowMonData.list[slot].encounterIndex) + if(sFollowMonData.list[slot].species != SPECIES_NONE) count++; } @@ -525,19 +520,6 @@ void RemoveOverworldEncounterObjects(void) } } -static u8 FindObjectEventForGfx(u16 gfxId) -{ - u8 i; - - for(i = 0; i < OBJECT_EVENTS_COUNT; ++i) - { - if(gObjectEvents[i].active && gObjectEvents[i].graphicsId == gfxId) - return i; - } - - return OBJECT_EVENTS_COUNT; -} - static bool8 CheckForObjectEventAtLocation(s16 x, s16 y) { u8 i; diff --git a/src/wild_encounter.c b/src/wild_encounter.c index de149e77b02d..96c3b22b7a53 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -579,23 +579,6 @@ static bool8 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum return TRUE; } -void GenerateFollowMon(struct FollowMon *followMon, bool8 inWater) -{ - u32 headerId = GetCurrentMapWildMonHeaderId(); - if (inWater) { - followMon->encounterIndex = ChooseWildMonIndex_Water() + 1; - followMon->timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); - followMon->onWater = TRUE; - - } else { - followMon->encounterIndex = ChooseWildMonIndex_Land() + 1; - followMon->timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - followMon->onWater = FALSE; - } - - followMon->isShiny = ComputePlayerShinyOdds(Random32()); -} - static u16 GenerateFishingWildMon(const struct WildPokemonInfo *wildMonInfo, u8 rod) { u8 wildMonIndex = ChooseWildMonIndex_Fishing(rod); From 91f50769981f42400e5b8a2398b021cd96b219c0 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 29 Nov 2025 00:59:48 -0600 Subject: [PATCH 058/572] Implemented isFemale for ow mons --- src/overworld_encounters.c | 45 ++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index b17ddb5bc1a3..ba43d57556c6 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -307,6 +307,30 @@ static bool8 TrySelectTile(s16* outX, s16* outY) return FALSE; } +static void CreateMonWithGender(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 otIdType, u32 fixedOtId, bool32 isFemale) +{ + u32 personality; + + if (isFemale) + { + do + { + personality = Random32(); + } + while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_FEMALE); + } + else + { + do + { + personality = Random32(); + } + while (GetGenderFromSpeciesAndPersonality(species, personality) == MON_FEMALE); + } + + CreateMon(mon, species, level, fixedIV, TRUE, personality, otIdType, fixedOtId); +} + void CreateFollowMonEncounter(void) { struct ObjectEvent *curObject; u32 objEventId = GetObjectEventIdByLocalIdAndMap(gSpecialVar_LastTalked, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); @@ -326,15 +350,14 @@ void CreateFollowMonEncounter(void) { bool32 shiny = curObject->shiny; ZeroEnemyPartyMons(); - CreateMon( + CreateMonWithGender( &gEnemyParty[0], sFollowMonData.list[slot].species, sFollowMonData.list[slot].level, - USE_RANDOM_IVS, - FALSE, - 0, + USE_RANDOM_IVS, OT_ID_PLAYER_ID, - 0 + 0, + sFollowMonData.list[slot].isFemale ); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); } @@ -357,11 +380,7 @@ static bool32 OverworldEncounters_ProcessMonInteraction(void) // There is a valid collision so exectute the attached script const u8* script = InteractWithDynamicWildFollowMon; gSpecialVar_LastTalked = curObject->localId; - //VarSet(VAR_LAST_TALKED, curObject->localId); ScriptContext_SetupScript(script); - - //CreateFollowMonEncounter(); - //BattleSetup_StartScriptedWildBattle(); return TRUE; } } @@ -418,8 +437,8 @@ u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y) u16 species = GetFollowMonSpecies(spawnSlot, x, y); u16 graphicsId = species + OBJ_EVENT_MON; - // if (sFollowMonData.list[slot].isFemale) - // graphicsId += OBJ_EVENT_MON_FEMALE; + if (sFollowMonData.list[spawnSlot].isFemale) + graphicsId += OBJ_EVENT_MON_FEMALE; if (sFollowMonData.list[spawnSlot].isShiny) graphicsId += OBJ_EVENT_MON_SHINY; @@ -467,6 +486,10 @@ static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y) sFollowMonData.list[spawnSlot].species = species; sFollowMonData.list[spawnSlot].level = level; sFollowMonData.list[spawnSlot].isShiny = ComputePlayerShinyOdds(Random32()); + if (GetGenderFromSpeciesAndPersonality(species, Random32()) == MON_FEMALE) + sFollowMonData.list[spawnSlot].isFemale = TRUE; + else + sFollowMonData.list[spawnSlot].isFemale = FALSE; return species; } From 1761de5a978d1f5f77021ffc3929c251f324a3af Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 29 Nov 2025 01:00:21 -0600 Subject: [PATCH 059/572] Added basic handling for Unown forms --- src/overworld_encounters.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index ba43d57556c6..dfdea30e6ae1 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -483,6 +483,14 @@ static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y) level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_LAND); } + if (species == SPECIES_UNOWN) + { + u32 rand = Random32() % NUM_UNOWN_FORMS; + + if (rand != 0) + species = SPECIES_UNOWN_B + rand - 1; + } + sFollowMonData.list[spawnSlot].species = species; sFollowMonData.list[spawnSlot].level = level; sFollowMonData.list[spawnSlot].isShiny = ComputePlayerShinyOdds(Random32()); From 8d565fd6a3ba29c0d9e4982b650a6ac7f559aa4b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 29 Nov 2025 10:45:44 +0000 Subject: [PATCH 060/572] Match LOCALID_OW_ENCOUNTER_END to other defines --- include/constants/event_objects.h | 2 +- src/overworld_encounters.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 2046f84158ff..0a8f56d9cc39 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -335,11 +335,11 @@ #define LOCALID_NONE 0 #define LOCALID_CAMERA 127 #define LOCALID_BERRY_BLENDER_PLAYER_END 240 // This will use 5 (MAX_RFU_PLAYERS) IDs ending at 240, i.e. 236-240 +#define LOCALID_OW_ENCOUNTER_END 252 // This will use 5 (FOLLOWMON_MAX_SPAWN_SLOTS) IDs ending at 252, i.e. 248-252 #define LOCALID_FOLLOWING_POKEMON 254 #define LOCALID_PLAYER 255 #define OBJ_EVENT_ID_FOLLOWER 0xFE #define OBJ_EVENT_ID_NPC_FOLLOWER 0xFD -#define OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER 0xFC // This will use up to 5 IDs for OW Encounters. (248-252) // Aliases for old names. "object event id" normally refers to an index into gObjectEvents, which these are not. // Used for link player OWs in CreateLinkPlayerSprite diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 1755722bdb9c..7ed8058f5fe4 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -39,7 +39,7 @@ static void OverworldEncounters_ProcessMonInteraction(void); void LoadFollowMonData(struct ObjectEvent *objectEvent) { - u32 slot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - objectEvent->localId; + u32 slot = LOCALID_OW_ENCOUNTER_END - objectEvent->localId; sFollowMonData.list[slot].isShiny = objectEvent->shiny; sFollowMonData.spawnCountdown += 60; @@ -81,7 +81,7 @@ void UpdateOverworldEncounters(void) const struct FollowMon *followMon = &sFollowMonData.list[spawnSlot]; bool32 waterMons = IsSpawningWaterMons(); bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; - u32 localId = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - spawnSlot; + u32 localId = LOCALID_OW_ENCOUNTER_END - spawnSlot; u32 movementType; if (OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT) // These checks need to be improved { @@ -136,7 +136,7 @@ void UpdateOverworldEncounters(void) if(sFollowMonData.pendingSpawnAnim != 0) { u32 spawnSlot = sFollowMonData.pendingSpawnAnim - 1; - u32 objEventId = GetObjectEventIdByLocalId(OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - spawnSlot); + u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot); enum FollowMonSpawnAnim spawnAnimType; if (sFollowMonData.list[spawnSlot].species != SPECIES_NONE) @@ -210,7 +210,7 @@ static u8 NextSpawnMonSlot(void) } // Remove any existing id by this slot - RemoveObjectEventByLocalIdAndMap(OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - slot, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + RemoveObjectEventByLocalIdAndMap(LOCALID_OW_ENCOUNTER_END - slot, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); // Check that we don't have too many sprites on screen before spawning // (lag reduction) @@ -346,7 +346,7 @@ void CreateFollowMonEncounter(void) { return; } - u32 slot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - gObjectEvents[objEventId].localId; + u32 slot = LOCALID_OW_ENCOUNTER_END - gObjectEvents[objEventId].localId; bool32 shiny = curObject->shiny; ZeroEnemyPartyMons(); @@ -404,7 +404,7 @@ bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { u32 i; - u32 spawnSlot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - objectEvent->localId; + u32 spawnSlot = LOCALID_OW_ENCOUNTER_END - objectEvent->localId; sFollowMonData.pendingSpawnAnim = spawnSlot + 1; @@ -418,7 +418,7 @@ void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { - u32 spawnSlot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - objectEvent->localId; + u32 spawnSlot = LOCALID_OW_ENCOUNTER_END - objectEvent->localId; sFollowMonData.list[spawnSlot].species = SPECIES_NONE; sFollowMonData.list[spawnSlot].age = 0; From 4261f7c84a4d42542069f45e1d2082079a724e82 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 29 Nov 2025 13:22:27 -0600 Subject: [PATCH 061/572] Mon age sorting --- include/overworld_encounters.h | 3 +- src/overworld_encounters.c | 69 +++++++++++++++++++++++++++------- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 4b8c302652bd..ef766719a0bc 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -24,8 +24,7 @@ struct FollowMonData { struct FollowMon list[FOLLOWMON_MAX_SPAWN_SLOTS]; u16 spawnCountdown; - u8 oldestSlot:4; - u8 pendingSpawnAnim:4; + u8 pendingSpawnAnim; }; //data/scripts/followmon.inc diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index dfdea30e6ae1..0f6cf9cd2050 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -171,21 +171,21 @@ static u8 GetMaxFollowMonSpawns(void) return 3; } -static u32 GetNewOldestSlot(u32 oldSlot) +static u32 GetOldestSlot(void) { u32 i; - u32 nextOldest = FOLLOWMON_MAX_SPAWN_SLOTS; + u32 oldest = 0; for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { if (sFollowMonData.list[i].species != SPECIES_NONE) { - if (i != oldSlot && (nextOldest == FOLLOWMON_MAX_SPAWN_SLOTS || sFollowMonData.list[i].age > sFollowMonData.list[nextOldest].age)) - nextOldest = i; + if (sFollowMonData.list[i].age > sFollowMonData.list[oldest].age) + oldest = i; } } - return nextOldest; + return oldest; } static u8 NextSpawnMonSlot(void) @@ -197,8 +197,7 @@ static u8 NextSpawnMonSlot(void) if(CountActiveFollowMon() >= maxSpawns) { // Cycle through so we remove the oldest mon first - slot = sFollowMonData.oldestSlot; - sFollowMonData.oldestSlot = GetNewOldestSlot(slot); + slot = GetOldestSlot(); } else { @@ -409,19 +408,62 @@ bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct return FALSE; } -void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) +struct AgeSort { - u32 i; - u32 spawnSlot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - objectEvent->localId; + u8 slot:4; + u8 age:4; +}; - sFollowMonData.pendingSpawnAnim = spawnSlot + 1; +static void SortOWEMonAges(void) +{ + struct AgeSort array[FOLLOWMON_MAX_SPAWN_SLOTS]; + struct AgeSort current; + u32 numActive = CountActiveFollowMon(); + u32 count = 0; + s32 i, j; - // Increase the age of all followmons for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { if (sFollowMonData.list[i].species != SPECIES_NONE) - sFollowMonData.list[i].age++; + { + array[count].slot = i; + array[count].age = sFollowMonData.list[i].age; + count++; + } + if (count == numActive) + break; + } + + for (i = 1; i < numActive; i++) + { + current = array[i]; + j = i - 1; + + while (j >= 0 && array[j].age < current.age) + { + array[j + 1] = array[j]; + j--; + } + + array[j + 1] = current; } + + array[0].age = numActive; + sFollowMonData.list[array[0].slot].age = numActive; + + for (i = 1; i < numActive; i++) + { + array[i].age = array[i - 1].age - 1; + sFollowMonData.list[array[i].slot].age = array[i].age; + } +} + +void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) +{ + u32 spawnSlot = OBJ_EVENT_ID_LAST_OVERWORLD_ENCOUNTER - objectEvent->localId; + + sFollowMonData.pendingSpawnAnim = spawnSlot + 1; + SortOWEMonAges(); } void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) @@ -449,7 +491,6 @@ u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y) void ClearOverworldEncounterData(void) { sFollowMonData.spawnCountdown = 0; - sFollowMonData.oldestSlot = 0; for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { From 8a189f0f6f812f48bf89fd842dd7bc7a2426d586 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 29 Nov 2025 19:56:50 +0000 Subject: [PATCH 062/572] Move CreateMonWithGender to pokemon.h --- include/pokemon.h | 1 + src/overworld_encounters.c | 24 ------------------------ src/pokemon.c | 16 ++++++++++++++++ 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/include/pokemon.h b/include/pokemon.h index f9f2ee02d674..7bc7c2daecd8 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -722,6 +722,7 @@ void CreateBattleTowerMon_HandleLevel(struct Pokemon *mon, struct BattleTowerPok void CreateApprenticeMon(struct Pokemon *mon, const struct Apprentice *src, u8 monId); void CreateMonWithEVSpreadNatureOTID(struct Pokemon *mon, u16 species, u8 level, u8 nature, u8 fixedIV, u8 evSpread, u32 otId); void ConvertPokemonToBattleTowerPokemon(struct Pokemon *mon, struct BattleTowerPokemon *dest); +void CreateMonWithGender(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 otIdType, u32 fixedOtId, bool32 isFemale); bool8 ShouldIgnoreDeoxysForm(u8 caseId, u8 battler); u16 GetUnionRoomTrainerPic(void); enum TrainerClassID GetUnionRoomTrainerClass(void); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7ed8058f5fe4..3244e72702cb 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -307,30 +307,6 @@ static bool8 TrySelectTile(s16* outX, s16* outY) return FALSE; } -static void CreateMonWithGender(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 otIdType, u32 fixedOtId, bool32 isFemale) -{ - u32 personality; - - if (isFemale) - { - do - { - personality = Random32(); - } - while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_FEMALE); - } - else - { - do - { - personality = Random32(); - } - while (GetGenderFromSpeciesAndPersonality(species, personality) == MON_FEMALE); - } - - CreateMon(mon, species, level, fixedIV, TRUE, personality, otIdType, fixedOtId); -} - void CreateFollowMonEncounter(void) { struct ObjectEvent *curObject; u32 objEventId = GetObjectEventIdByLocalIdAndMap(gSpecialVar_LastTalked, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); diff --git a/src/pokemon.c b/src/pokemon.c index dff48d8655e7..84ed1c3755d9 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1570,6 +1570,22 @@ static void CreateEventMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedI SetMonData(mon, MON_DATA_MODERN_FATEFUL_ENCOUNTER, &isModernFatefulEncounter); } +void CreateMonWithGender(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 otIdType, u32 fixedOtId, bool32 isFemale) +{ + u32 personality; + + if (isFemale) + do + personality = Random32(); + while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_FEMALE); + else + do + personality = Random32(); + while (GetGenderFromSpeciesAndPersonality(species, personality) == MON_FEMALE); + + CreateMon(mon, species, level, fixedIV, TRUE, personality, otIdType, fixedOtId); +} + // If FALSE, should load this game's Deoxys form. If TRUE, should load normal Deoxys form bool8 ShouldIgnoreDeoxysForm(u8 caseId, u8 battler) { From cee9280e522d7e38966676c10210a80701d19b14 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 29 Nov 2025 20:05:23 +0000 Subject: [PATCH 063/572] Make AreElevationsCompatible Global --- include/event_object_movement.h | 1 + src/event_object_movement.c | 3 +-- src/overworld_encounters.c | 13 ------------- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index c66127310209..9f52b3f6ad29 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -267,6 +267,7 @@ u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemp bool8 GetFollowerInfo(u32 *species, bool32 *shiny, bool32 *female); const struct ObjectEventGraphicsInfo *SpeciesToGraphicsInfo(u32 species, bool32 shiny, bool32 female); u16 GetObjectEventFlagIdByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup); +bool32 AreElevationsCompatible(u32, u32); void MovementType_None(struct Sprite *sprite); void MovementType_LookAround(struct Sprite *sprite); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 4d9e95273eca..028cda53eef4 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -215,7 +215,6 @@ static void DestroyLevitateMovementTask(u8); static u32 LoadDynamicFollowerPalette(u32 species, bool32 shiny, bool32 female); const struct ObjectEventGraphicsInfo *SpeciesToGraphicsInfo(u32 species, bool32 shiny, bool32 female); static bool8 NpcTakeStep(struct Sprite *); -static bool8 AreElevationsCompatible(u8, u8); static void CopyObjectGraphicsInfoToSpriteTemplate_WithMovementType(u16 graphicsId, u16 movementType, struct SpriteTemplate *spriteTemplate, const struct SubspriteTable **subspriteTables); static u16 GetGraphicsIdForMon(u32 species, bool32 shiny, bool32 female); @@ -9926,7 +9925,7 @@ static void ObjectEventUpdateSubpriority(struct ObjectEvent *objEvent, struct Sp SetObjectSubpriorityByElevation(objEvent->previousElevation, sprite, 1); } -static bool8 AreElevationsCompatible(u8 a, u8 b) +bool32 AreElevationsCompatible(u32 a, u32 b) { if (a == 0 || b == 0) return TRUE; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 432513fd3477..759c7be89111 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -28,7 +28,6 @@ static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y); static u8 CountActiveObjectEvents(); static bool8 IsSafeToSpawnObjectEvents(void); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); -static bool8 AreElevationsCompatible(u8 a, u8 b); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height); static bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); @@ -589,18 +588,6 @@ static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater) } timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); return gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; - -} - -static bool8 AreElevationsCompatible(u8 a, u8 b) -{ - if (a == 0 || b == 0) - return TRUE; - - if (a != b) - return FALSE; - - return TRUE; } static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height) From fd435d51e798cfc2d638c324b535fd428d126666 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 29 Nov 2025 20:23:42 +0000 Subject: [PATCH 064/572] Adjust CreateMaleMon --- src/pokemon.c | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/pokemon.c b/src/pokemon.c index 84ed1c3755d9..c91790082b7c 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1277,19 +1277,26 @@ void CreateMonWithGenderNatureLetter(struct Pokemon *mon, u16 species, u8 level, CreateMon(mon, species, level, fixedIV, TRUE, personality, OT_ID_PLAYER_ID, 0); } -// This is only used to create Wally's Ralts. -void CreateMaleMon(struct Pokemon *mon, u16 species, u8 level) +void CreateMonWithGender(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 otIdType, u32 fixedOtId, bool32 isFemale) { u32 personality; - u32 otId; - do - { - otId = Random32(); - personality = Random32(); - } - while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_MALE); - CreateMon(mon, species, level, USE_RANDOM_IVS, TRUE, personality, OT_ID_PRESET, otId); + if (isFemale) + do + personality = Random32(); + while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_FEMALE); + else + do + personality = Random32(); + while (GetGenderFromSpeciesAndPersonality(species, personality) == MON_FEMALE); + + CreateMon(mon, species, level, fixedIV, TRUE, personality, otIdType, fixedOtId); +} + +// This is only used to create Wally's Ralts. +void CreateMaleMon(struct Pokemon *mon, u16 species, u8 level) +{ + CreateMonWithGender(mon, species, level, USE_RANDOM_IVS, OT_ID_PRESET, Random32(), MALE); } void CreateMonWithIVsPersonality(struct Pokemon *mon, u16 species, u8 level, u32 ivs, u32 personality) @@ -1570,22 +1577,6 @@ static void CreateEventMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedI SetMonData(mon, MON_DATA_MODERN_FATEFUL_ENCOUNTER, &isModernFatefulEncounter); } -void CreateMonWithGender(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 otIdType, u32 fixedOtId, bool32 isFemale) -{ - u32 personality; - - if (isFemale) - do - personality = Random32(); - while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_FEMALE); - else - do - personality = Random32(); - while (GetGenderFromSpeciesAndPersonality(species, personality) == MON_FEMALE); - - CreateMon(mon, species, level, fixedIV, TRUE, personality, otIdType, fixedOtId); -} - // If FALSE, should load this game's Deoxys form. If TRUE, should load normal Deoxys form bool8 ShouldIgnoreDeoxysForm(u8 caseId, u8 battler) { From a33b4a51aeba87e0af33bff820ac7bd69597f2e7 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 29 Nov 2025 20:59:49 +0000 Subject: [PATCH 065/572] Update CreateMonWithGender --- src/pokemon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pokemon.c b/src/pokemon.c index c91790082b7c..7383e4fceab2 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1288,7 +1288,7 @@ void CreateMonWithGender(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, else do personality = Random32(); - while (GetGenderFromSpeciesAndPersonality(species, personality) == MON_FEMALE); + while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_MALE); CreateMon(mon, species, level, fixedIV, TRUE, personality, otIdType, fixedOtId); } From f71bcb59dc1e85ec65e048da48f54f9d9b8691cf Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 29 Nov 2025 21:04:10 +0000 Subject: [PATCH 066/572] Update event_objects.h --- include/constants/event_objects.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 0a8f56d9cc39..3c8cb179b4d5 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -323,9 +323,6 @@ #define OBJ_KIND_NORMAL 0 #define OBJ_KIND_CLONE 255 // Exclusive to FRLG -// Special object event local ids -// Used for link player OWs in CreateLinkPlayerSprite -#define OBJ_EVENT_ID_DYNAMIC_BASE 0xF0 // Each object event template gets an ID that can be used to refer to it in scripts and elsewhere. // This is referred to as the "local id" (and it's really just 1 + its index in the templates array). @@ -345,6 +342,7 @@ // Used for link player OWs in CreateLinkPlayerSprite #define OBJ_EVENT_ID_CAMERA LOCALID_CAMERA #define OBJ_EVENT_ID_PLAYER LOCALID_PLAYER +#define OBJ_EVENT_ID_DYNAMIC_BASE 0xF0 // Moved from src/event_object_movement.c so that they're accesible from other files. #define OBJ_EVENT_PAL_TAG_BRENDAN 0x1100 From fdf6ea35d305d23a82ed25d50c6bddb1b77c9dfa Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 29 Nov 2025 21:40:05 +0000 Subject: [PATCH 067/572] Account for all 100% Gendered mons in CreateMonWithGender --- src/pokemon.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/pokemon.c b/src/pokemon.c index 7383e4fceab2..82e9e4dca4eb 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1281,14 +1281,25 @@ void CreateMonWithGender(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, { u32 personality; - if (isFemale) - do - personality = Random32(); - while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_FEMALE); - else - do - personality = Random32(); - while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_MALE); + switch (gSpeciesInfo[species].genderRatio) + { + case MON_MALE: + case MON_FEMALE: + case MON_GENDERLESS: + personality = Random32(); + break; + + default: + if (isFemale) + do + personality = Random32(); + while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_FEMALE); + else + do + personality = Random32(); + while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_MALE); + break; + } CreateMon(mon, species, level, fixedIV, TRUE, personality, otIdType, fixedOtId); } From 733ddf08ed8169f8763426b75f61072e7d0eeb1b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 29 Nov 2025 21:59:26 +0000 Subject: [PATCH 068/572] Unown Handling --- src/overworld_encounters.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 759c7be89111..2b80c3de5fb5 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -469,10 +469,11 @@ void ClearOverworldEncounterData(void) static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y) { const struct WildPokemonInfo *wildMonInfo; - u32 species = 0; + u32 species = SPECIES_NONE; u32 headerId = GetCurrentMapWildMonHeaderId(); u32 tileBehavior = MapGridGetMetatileBehaviorAt(x, y); u32 timeOfDay, encounterIndex, level; + u32 personality = Random32(); if (MetatileBehavior_IsWaterWildEncounter(tileBehavior)) { @@ -492,17 +493,12 @@ static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y) } if (species == SPECIES_UNOWN) - { - u32 rand = Random32() % NUM_UNOWN_FORMS; - - if (rand != 0) - species = SPECIES_UNOWN_B + rand - 1; - } + species = GetUnownSpeciesId(personality); sFollowMonData.list[spawnSlot].species = species; sFollowMonData.list[spawnSlot].level = level; - sFollowMonData.list[spawnSlot].isShiny = ComputePlayerShinyOdds(Random32()); - if (GetGenderFromSpeciesAndPersonality(species, Random32()) == MON_FEMALE) + sFollowMonData.list[spawnSlot].isShiny = ComputePlayerShinyOdds(personality); + if (GetGenderFromSpeciesAndPersonality(species, personality) == MON_FEMALE) sFollowMonData.list[spawnSlot].isFemale = TRUE; else sFollowMonData.list[spawnSlot].isFemale = FALSE; From 2b9a0ae42682b26c0c00bd64a5c5ded66e10f100 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 29 Nov 2025 22:41:07 +0000 Subject: [PATCH 069/572] Fix Movement Type Checks --- src/event_object_movement.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 028cda53eef4..c5484030d0bb 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11606,7 +11606,7 @@ bool8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; if (!IsOverworldEncounterObjectEventInSpawnedMap(objectEvent, x, y) - || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) + || !MetatileBehavior_IsLandWildEncounter(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11627,7 +11627,7 @@ bool8 MovementType_WanderOnWaterEncounter_Step4(struct ObjectEvent *objectEvent, y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; if (!IsOverworldEncounterObjectEventInSpawnedMap(objectEvent, x, y) - || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) + || !MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11648,7 +11648,7 @@ bool8 MovementType_WanderOnIndoorEncounter_Step4(struct ObjectEvent *objectEvent y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; if (!IsOverworldEncounterObjectEventInSpawnedMap(objectEvent, x, y) - || !MetatileBehavior_IsPokeGrass(MapGridGetMetatileBehaviorAt(x, y)) + || !MetatileBehavior_IsIndoorEncounter(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; From dc399d268c2d5f44ca6a87a7698a146b7aca2928 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 29 Nov 2025 22:58:14 +0000 Subject: [PATCH 070/572] Update comments in OverworldEncounter_IsCollisionExempt --- src/overworld_encounters.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 2b80c3de5fb5..9a5fbcf136c4 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -358,8 +358,8 @@ static void OverworldEncounters_ProcessMonInteraction(void) bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider) { - // The player can only collide with overworld encounters when not using a repel. - // Non-player, non-overworld encounters do not have collision with overworld encounters. + // The player is only exempt from collisions with overworld encounters when not using a repel. + // Non-player, non-overworld encounters are not collision exempt with overworld encounters. struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; From 7f56897b8234877812b687da4289262e274414fa Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 29 Nov 2025 23:15:05 +0000 Subject: [PATCH 071/572] Update overworld_encounters.c --- src/overworld_encounters.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 9a5fbcf136c4..123df3f94381 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -34,8 +34,6 @@ static bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); static bool32 IsInsidePlayerMap(s16 x, s16 y); static void OverworldEncounters_ProcessMonInteraction(void); -#define sEncounterIndex trainerRange_berryTreeId - void LoadFollowMonData(struct ObjectEvent *objectEvent) { u32 slot = LOCALID_OW_ENCOUNTER_END - objectEvent->localId; From caefdbf03316df97b85c71926a2eab6ae764d72f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 29 Nov 2025 23:18:53 +0000 Subject: [PATCH 072/572] Update global.fieldmap.h --- include/global.fieldmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index 6980b68cdefe..16dd3d0df31f 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -216,7 +216,7 @@ struct ObjectEvent u32 hideReflection:1; u32 shiny:1; // OW mon shininess u32 jumpDone:1; - u32 spawnTimeOfDay:2; // Used for FollowMon + u32 padding:2; /*0x04*/ u16 graphicsId; // 12 bits for species; high 4 bits for form /*0x06*/ u8 movementType; /*0x07*/ u8 trainerType; From 91652e6bfe2ab060c8c7fd282f553436a5cecdf6 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 29 Nov 2025 18:01:43 -0600 Subject: [PATCH 073/572] Bugfix: spawn replacement not working when at FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT --- src/overworld_encounters.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 123df3f94381..809ce5076802 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -189,9 +189,10 @@ static u8 NextSpawnMonSlot(void) { u8 slot = 0; u8 maxSpawns = GetMaxFollowMonSpawns(); + bool32 maxAllowedObjects = CountActiveObjectEvents() >= FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT; // All mon slots are in use - if(CountActiveFollowMon() >= maxSpawns) + if(CountActiveFollowMon() >= maxSpawns || maxAllowedObjects) { // Cycle through so we remove the oldest mon first slot = GetOldestSlot(); @@ -210,9 +211,12 @@ static u8 NextSpawnMonSlot(void) // Check that we don't have too many sprites on screen before spawning // (lag reduction) - if(CountActiveObjectEvents() >= FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT) - // TODO: Clear slot data. + if(maxAllowedObjects) + { + // Reset the countdown so multiple mons don't get destroyed at once. + sFollowMonData.spawnCountdown = 60; return INVALID_SPAWN_SLOT; + } return slot; } From 861981482fa4037e9a373326f088ad73c5c724ef Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 29 Nov 2025 18:31:49 -0600 Subject: [PATCH 074/572] Grouped configs into their own section --- include/config/overworld.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 2c607babc22b..867daf8c38fa 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -55,9 +55,6 @@ // (You should not use 48x48 sprites/tables for compressed gfx) // 16x32, 32x32, 64x64 etc are fine #define OW_MON_WANDER_WALK TRUE // If true, OW Pokémon with MOVEMENT_TYPE_WANDER will walk-in-place in between steps. -#define OW_WILD_ENCOUNTERS_RANDOM TRUE // If true, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. -#define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If true, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. -#define OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT TRUE // If true, OW Pokémon spawned as overworld wild encounters will only use restricted movement types (MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER/MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER/MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER). // Follower Pokémon #define OW_FOLLOWERS_ENABLED FALSE // Enables follower Pokémon, HGSS style. Requires OW_POKEMON_OBJECT_EVENTS. Note that additional scripting may be required for them to be fully supported! #define OW_FOLLOWERS_BOBBING TRUE // If TRUE, follower Pokémon will bob up and down during their idle & walking animations @@ -74,6 +71,11 @@ #define OW_FOLLOWERS_ALLOWED_MET_LVL (0) #define OW_FOLLOWERS_ALLOWED_MET_LOC (0) +// Overworld Encounters +#define OW_WILD_ENCOUNTERS_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. +#define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. +#define OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will only use restricted movement types (MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER/MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER/MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER). + // Out-of-battle Ability effects #define OW_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8+, if a Pokémon with Synchronize leads the party, wild Pokémon will always have their same Nature as opposed to the 50% chance in previous games. Gift Pokémon excluded. // In USUM (here GEN_7), if a Pokémon with Synchronize leads the party, gift Pokémon will always have their same Nature regardless of their Egg Group. From 62d7624f2f5fbb5d969ca7af72cce14d6392d950 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 30 Nov 2025 11:01:47 +0000 Subject: [PATCH 075/572] Remove isShiny and isFemale from Struct --- include/overworld_encounters.h | 5 ++-- src/overworld_encounters.c | 55 ++++++++++++++++------------------ 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index ef766719a0bc..ec1f6befdee2 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -14,8 +14,7 @@ struct FollowMon { u16 species; u16 level:7; - u16 isShiny:1; - u16 isFemale:1; + u16 padding:2; u16 form:3; u16 age:4; }; @@ -36,7 +35,7 @@ void CreateFollowMonEncounter(void); bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); -u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y); +u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, bool32 *isShiny, bool32 *isFemale); void ClearOverworldEncounterData(void); void RemoveOverworldEncounterObjects(void); bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 123df3f94381..c0f826f2380c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -24,7 +24,7 @@ static u8 CountActiveFollowMon(); static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool8 IsSpawningWaterMons(); -static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y); +static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y, bool32 *isShiny, bool32 *isFemale); static u8 CountActiveObjectEvents(); static bool8 IsSafeToSpawnObjectEvents(void); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); @@ -37,7 +37,6 @@ static void OverworldEncounters_ProcessMonInteraction(void); void LoadFollowMonData(struct ObjectEvent *objectEvent) { u32 slot = LOCALID_OW_ENCOUNTER_END - objectEvent->localId; - sFollowMonData.list[slot].isShiny = objectEvent->shiny; sFollowMonData.spawnCountdown += 60; } @@ -60,6 +59,9 @@ void UpdateOverworldEncounters(void) return; } + bool32 isShiny = FALSE; + bool32 isFemale = FALSE; + OverworldEncounters_ProcessMonInteraction(); if(sFollowMonData.spawnCountdown == 0) @@ -96,7 +98,7 @@ void UpdateOverworldEncounters(void) struct ObjectEventTemplate objectEventTemplate = { .localId = localId, - .graphicsId = GetFollowMonObjectEventGraphicsId(spawnSlot, x, y), + .graphicsId = GetFollowMonObjectEventGraphicsId(spawnSlot, x, y, &isShiny, &isFemale), .x = x - MAP_OFFSET, .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), @@ -110,10 +112,6 @@ void UpdateOverworldEncounters(void) gObjectEvents[objectEventId].range.rangeX = 8; gObjectEvents[objectEventId].range.rangeY = 8; - // Only used for save/load as well as loading encounters, - // Most of the time, followmon data is tracked in sFollowMonData - gObjectEvents[objectEventId].shiny = followMon->isShiny; - // Hide reflections for spawns in water // (It just looks weird) if (waterMons) @@ -138,7 +136,7 @@ void UpdateOverworldEncounters(void) if (sFollowMonData.list[spawnSlot].species != SPECIES_NONE) { - if(sFollowMonData.list[spawnSlot].isShiny) + if (isShiny) { PlaySE(SE_SHINY); spawnAnimType = FOLLOWMON_SPAWN_ANIM_SHINY; @@ -303,23 +301,20 @@ static bool8 TrySelectTile(s16* outX, s16* outY) return FALSE; } -void CreateFollowMonEncounter(void) { - struct ObjectEvent *curObject; +void CreateFollowMonEncounter(void) +{ u32 objEventId = GetObjectEventIdByLocalIdAndMap(gSpecialVar_LastTalked, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + struct ObjectEvent *object = &gObjectEvents[objEventId]; - if (objEventId < OBJECT_EVENTS_COUNT) - { - curObject = &gObjectEvents[objEventId]; - if (!IsGeneratedOverworldEncounter(curObject)) - return; - } - else - { + if (objEventId >= OBJECT_EVENTS_COUNT) + return; + + if (!IsGeneratedOverworldEncounter(object)) return; - } - u32 slot = LOCALID_OW_ENCOUNTER_END - gObjectEvents[objEventId].localId; - bool32 shiny = curObject->shiny; + u32 slot = LOCALID_OW_ENCOUNTER_END - object->localId; + bool32 shiny = OW_SHINY(object); + bool32 isFemale = OW_FEMALE(object); ZeroEnemyPartyMons(); CreateMonWithGender( @@ -329,7 +324,7 @@ void CreateFollowMonEncounter(void) { USE_RANDOM_IVS, OT_ID_PLAYER_ID, 0, - sFollowMonData.list[slot].isFemale + isFemale ); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); } @@ -439,15 +434,15 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) sFollowMonData.list[spawnSlot].age = 0; } -u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y) +u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, bool32 *isShiny, bool32 *isFemale) { - u16 species = GetFollowMonSpecies(spawnSlot, x, y); + u16 species = GetFollowMonSpecies(spawnSlot, x, y, isShiny, isFemale); u16 graphicsId = species + OBJ_EVENT_MON; - if (sFollowMonData.list[spawnSlot].isFemale) + if (*isFemale) graphicsId += OBJ_EVENT_MON_FEMALE; - if (sFollowMonData.list[spawnSlot].isShiny) + if (*isShiny) graphicsId += OBJ_EVENT_MON_SHINY; return graphicsId; @@ -464,7 +459,7 @@ void ClearOverworldEncounterData(void) } } -static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y) +static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y, bool32 *isShiny, bool32 *isFemale) { const struct WildPokemonInfo *wildMonInfo; u32 species = SPECIES_NONE; @@ -495,11 +490,11 @@ static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y) sFollowMonData.list[spawnSlot].species = species; sFollowMonData.list[spawnSlot].level = level; - sFollowMonData.list[spawnSlot].isShiny = ComputePlayerShinyOdds(personality); + *isShiny = ComputePlayerShinyOdds(personality); if (GetGenderFromSpeciesAndPersonality(species, personality) == MON_FEMALE) - sFollowMonData.list[spawnSlot].isFemale = TRUE; + *isFemale = TRUE; else - sFollowMonData.list[spawnSlot].isFemale = FALSE; + *isFemale = FALSE; return species; } From 4707488693ffdba2fa63406caa4bfd5faed1b82c Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 30 Nov 2025 12:53:16 +0000 Subject: [PATCH 076/572] Remove Species from EWRAM --- include/overworld_encounters.h | 6 +-- src/overworld_encounters.c | 77 ++++++++++++++++++---------------- 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index ec1f6befdee2..5a09e584812d 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -12,11 +12,11 @@ struct FollowMon { - u16 species; u16 level:7; - u16 padding:2; u16 form:3; u16 age:4; + u16 padding:12; + u16 padding2; }; struct FollowMonData @@ -35,7 +35,7 @@ void CreateFollowMonEncounter(void); bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); -u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, bool32 *isShiny, bool32 *isFemale); +u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale); void ClearOverworldEncounterData(void); void RemoveOverworldEncounterObjects(void); bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c0f826f2380c..5e543142021c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -24,7 +24,7 @@ static u8 CountActiveFollowMon(); static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool8 IsSpawningWaterMons(); -static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y, bool32 *isShiny, bool32 *isFemale); +static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale); static u8 CountActiveObjectEvents(); static bool8 IsSafeToSpawnObjectEvents(void); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); @@ -59,6 +59,7 @@ void UpdateOverworldEncounters(void) return; } + u16 speciesId = SPECIES_NONE; bool32 isShiny = FALSE; bool32 isFemale = FALSE; @@ -98,7 +99,7 @@ void UpdateOverworldEncounters(void) struct ObjectEventTemplate objectEventTemplate = { .localId = localId, - .graphicsId = GetFollowMonObjectEventGraphicsId(spawnSlot, x, y, &isShiny, &isFemale), + .graphicsId = GetFollowMonObjectEventGraphicsId(spawnSlot, x, y, &speciesId, &isShiny, &isFemale), .x = x - MAP_OFFSET, .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), @@ -134,7 +135,7 @@ void UpdateOverworldEncounters(void) u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot); enum FollowMonSpawnAnim spawnAnimType; - if (sFollowMonData.list[spawnSlot].species != SPECIES_NONE) + if (speciesId != SPECIES_NONE) { if (isShiny) { @@ -143,7 +144,7 @@ void UpdateOverworldEncounters(void) } else { - PlayCry_Normal(sFollowMonData.list[spawnSlot].species, 25); + PlayCry_Normal(speciesId, 25); if (IsSpawningWaterMons()) spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) @@ -168,15 +169,16 @@ static u8 GetMaxFollowMonSpawns(void) static u32 GetOldestSlot(void) { - u32 i; u32 oldest = 0; - for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) + for (u32 spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) { - if (sFollowMonData.list[i].species != SPECIES_NONE) + u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot); + struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; + if (OW_SPECIES(objectEvent) != SPECIES_NONE) { - if (sFollowMonData.list[i].age > sFollowMonData.list[oldest].age) - oldest = i; + if (sFollowMonData.list[spawnSlot].age > sFollowMonData.list[oldest].age) + oldest = spawnSlot; } } @@ -185,26 +187,28 @@ static u32 GetOldestSlot(void) static u8 NextSpawnMonSlot(void) { - u8 slot = 0; - u8 maxSpawns = GetMaxFollowMonSpawns(); + u32 spawnSlot = 0; + u32 maxSpawns = GetMaxFollowMonSpawns(); // All mon slots are in use - if(CountActiveFollowMon() >= maxSpawns) + if (CountActiveFollowMon() >= maxSpawns) { // Cycle through so we remove the oldest mon first - slot = GetOldestSlot(); + spawnSlot = GetOldestSlot(); } else { - for (slot = 0; slot < maxSpawns; slot++) + for (spawnSlot; spawnSlot < maxSpawns; spawnSlot++) { - if (sFollowMonData.list[slot].species == SPECIES_NONE) + u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot); + struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; + if (OW_SPECIES(objectEvent) == SPECIES_NONE) break; } } // Remove any existing id by this slot - RemoveObjectEventByLocalIdAndMap(LOCALID_OW_ENCOUNTER_END - slot, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + RemoveObjectEventByLocalIdAndMap(LOCALID_OW_ENCOUNTER_END - spawnSlot, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); // Check that we don't have too many sprites on screen before spawning // (lag reduction) @@ -212,7 +216,7 @@ static u8 NextSpawnMonSlot(void) // TODO: Clear slot data. return INVALID_SPAWN_SLOT; - return slot; + return spawnSlot; } static bool8 TrySelectTile(s16* outX, s16* outY) @@ -313,13 +317,14 @@ void CreateFollowMonEncounter(void) return; u32 slot = LOCALID_OW_ENCOUNTER_END - object->localId; + u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object); bool32 isFemale = OW_FEMALE(object); ZeroEnemyPartyMons(); CreateMonWithGender( &gEnemyParty[0], - sFollowMonData.list[slot].species, + speciesId, sFollowMonData.list[slot].level, USE_RANDOM_IVS, OT_ID_PLAYER_ID, @@ -384,7 +389,9 @@ static void SortOWEMonAges(void) for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - if (sFollowMonData.list[i].species != SPECIES_NONE) + u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - i); + struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; + if (OW_SPECIES(objectEvent) != SPECIES_NONE) { array[count].slot = i; array[count].age = sFollowMonData.list[i].age; @@ -430,14 +437,13 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { u32 spawnSlot = LOCALID_OW_ENCOUNTER_END - objectEvent->localId; - sFollowMonData.list[spawnSlot].species = SPECIES_NONE; sFollowMonData.list[spawnSlot].age = 0; } -u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, bool32 *isShiny, bool32 *isFemale) +u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale) { - u16 species = GetFollowMonSpecies(spawnSlot, x, y, isShiny, isFemale); - u16 graphicsId = species + OBJ_EVENT_MON; + SetOverworldEncounterSpeciesInfo(spawnSlot, x, y, speciesId, isShiny, isFemale); + u16 graphicsId = *speciesId + OBJ_EVENT_MON; if (*isFemale) graphicsId += OBJ_EVENT_MON_FEMALE; @@ -454,15 +460,13 @@ void ClearOverworldEncounterData(void) for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - sFollowMonData.list[i].species = SPECIES_NONE; sFollowMonData.list[i].age = 0; } } -static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y, bool32 *isShiny, bool32 *isFemale) +static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale) { const struct WildPokemonInfo *wildMonInfo; - u32 species = SPECIES_NONE; u32 headerId = GetCurrentMapWildMonHeaderId(); u32 tileBehavior = MapGridGetMetatileBehaviorAt(x, y); u32 timeOfDay, encounterIndex, level; @@ -473,7 +477,7 @@ static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y, bool32 *isShiny, boo encounterIndex = ChooseWildMonIndex_Water(); timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; - species = wildMonInfo->wildPokemon[encounterIndex].species; + *speciesId = wildMonInfo->wildPokemon[encounterIndex].species; level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_WATER); } else @@ -481,22 +485,19 @@ static u32 GetFollowMonSpecies(u32 spawnSlot, s32 x, s32 y, bool32 *isShiny, boo encounterIndex = ChooseWildMonIndex_Land(); timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; - species = wildMonInfo->wildPokemon[encounterIndex].species; + *speciesId = wildMonInfo->wildPokemon[encounterIndex].species; level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_LAND); } - if (species == SPECIES_UNOWN) - species = GetUnownSpeciesId(personality); + if (*speciesId == SPECIES_UNOWN) + *speciesId = GetUnownSpeciesId(personality); - sFollowMonData.list[spawnSlot].species = species; sFollowMonData.list[spawnSlot].level = level; *isShiny = ComputePlayerShinyOdds(personality); - if (GetGenderFromSpeciesAndPersonality(species, personality) == MON_FEMALE) + if (GetGenderFromSpeciesAndPersonality(*speciesId, personality) == MON_FEMALE) *isFemale = TRUE; else *isFemale = FALSE; - - return species; } static bool8 IsSafeToSpawnObjectEvents(void) @@ -509,10 +510,12 @@ static bool8 IsSafeToSpawnObjectEvents(void) static u8 CountActiveFollowMon() { - u8 count = 0; - for(u8 slot = 0; slot < FOLLOWMON_MAX_SPAWN_SLOTS; slot++) + u32 count = 0; + for (u32 spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) { - if(sFollowMonData.list[slot].species != SPECIES_NONE) + u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot); + struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; + if (OW_SPECIES(objectEvent) != SPECIES_NONE) count++; } From fe360f6de517f28d210f98dc233687a909fdfa9e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 30 Nov 2025 12:59:29 +0000 Subject: [PATCH 077/572] Add GetOverworldSpeciesFromSpawnSlot Wrapper Function --- src/overworld_encounters.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5e543142021c..84c8434c8957 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -33,6 +33,7 @@ static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height); static bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); static bool32 IsInsidePlayerMap(s16 x, s16 y); static void OverworldEncounters_ProcessMonInteraction(void); +static u16 GetOverworldSpeciesFromSpawnSlot(u32 spawnSlot); void LoadFollowMonData(struct ObjectEvent *objectEvent) { @@ -173,9 +174,7 @@ static u32 GetOldestSlot(void) for (u32 spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) { - u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot); - struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; - if (OW_SPECIES(objectEvent) != SPECIES_NONE) + if (GetOverworldSpeciesFromSpawnSlot(spawnSlot) != SPECIES_NONE) { if (sFollowMonData.list[spawnSlot].age > sFollowMonData.list[oldest].age) oldest = spawnSlot; @@ -200,9 +199,7 @@ static u8 NextSpawnMonSlot(void) { for (spawnSlot; spawnSlot < maxSpawns; spawnSlot++) { - u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot); - struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; - if (OW_SPECIES(objectEvent) == SPECIES_NONE) + if (GetOverworldSpeciesFromSpawnSlot(spawnSlot) == SPECIES_NONE) break; } } @@ -389,9 +386,7 @@ static void SortOWEMonAges(void) for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - i); - struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; - if (OW_SPECIES(objectEvent) != SPECIES_NONE) + if (GetOverworldSpeciesFromSpawnSlot(i) != SPECIES_NONE) { array[count].slot = i; array[count].age = sFollowMonData.list[i].age; @@ -513,9 +508,7 @@ static u8 CountActiveFollowMon() u32 count = 0; for (u32 spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) { - u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot); - struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; - if (OW_SPECIES(objectEvent) != SPECIES_NONE) + if (GetOverworldSpeciesFromSpawnSlot(spawnSlot) != SPECIES_NONE) count++; } @@ -624,3 +617,10 @@ bool32 IsGeneratedOverworldEncounter(struct ObjectEvent *objectEvent) { return (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_ENCOUNTER); } + +u16 GetOverworldSpeciesFromSpawnSlot(u32 spawnSlot) +{ + u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot); + struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; + return OW_SPECIES(objectEvent); +} From 4ca2b1c43b2f6856db40ac2ebe66143f33ec5fb4 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 30 Nov 2025 13:01:57 +0000 Subject: [PATCH 078/572] Fix Female and Shiny Not Applying Correctly --- src/overworld_encounters.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 84c8434c8957..9fdfd5e51873 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -315,8 +315,8 @@ void CreateFollowMonEncounter(void) u32 slot = LOCALID_OW_ENCOUNTER_END - object->localId; u16 speciesId = OW_SPECIES(object); - bool32 shiny = OW_SHINY(object); - bool32 isFemale = OW_FEMALE(object); + bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; + bool32 isFemale = OW_FEMALE(object) ? TRUE : FALSE; ZeroEnemyPartyMons(); CreateMonWithGender( From 4b21d943a967bbc3f3b23db27d48436fb463b917 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 30 Nov 2025 13:17:32 +0000 Subject: [PATCH 079/572] Cleanup --- src/overworld_encounters.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 9fdfd5e51873..8f371acbc141 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -37,8 +37,6 @@ static u16 GetOverworldSpeciesFromSpawnSlot(u32 spawnSlot); void LoadFollowMonData(struct ObjectEvent *objectEvent) { - u32 slot = LOCALID_OW_ENCOUNTER_END - objectEvent->localId; - sFollowMonData.spawnCountdown += 60; } @@ -79,7 +77,6 @@ void UpdateOverworldEncounters(void) if (spawnSlot != INVALID_SPAWN_SLOT) { - const struct FollowMon *followMon = &sFollowMonData.list[spawnSlot]; bool32 waterMons = IsSpawningWaterMons(); bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; u32 localId = LOCALID_OW_ENCOUNTER_END - spawnSlot; @@ -186,7 +183,7 @@ static u32 GetOldestSlot(void) static u8 NextSpawnMonSlot(void) { - u32 spawnSlot = 0; + u32 spawnSlot; u32 maxSpawns = GetMaxFollowMonSpawns(); // All mon slots are in use @@ -197,7 +194,7 @@ static u8 NextSpawnMonSlot(void) } else { - for (spawnSlot; spawnSlot < maxSpawns; spawnSlot++) + for (spawnSlot = 0; spawnSlot < maxSpawns; spawnSlot++) { if (GetOverworldSpeciesFromSpawnSlot(spawnSlot) == SPECIES_NONE) break; From 553263ff61e1ad1618e07df93f199f21e0e548e1 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 30 Nov 2025 13:24:53 +0000 Subject: [PATCH 080/572] Add More Wrapper Functions --- src/overworld_encounters.c | 41 +++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8f371acbc141..078a6b3c9f55 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -33,7 +33,9 @@ static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height); static bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); static bool32 IsInsidePlayerMap(s16 x, s16 y); static void OverworldEncounters_ProcessMonInteraction(void); -static u16 GetOverworldSpeciesFromSpawnSlot(u32 spawnSlot); +static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); +static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); +static u32 GetSpawnSlotByLocalId(u32 localId); void LoadFollowMonData(struct ObjectEvent *objectEvent) { @@ -79,7 +81,7 @@ void UpdateOverworldEncounters(void) { bool32 waterMons = IsSpawningWaterMons(); bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; - u32 localId = LOCALID_OW_ENCOUNTER_END - spawnSlot; + u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); u32 movementType; if (OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT) // These checks need to be improved { @@ -130,7 +132,7 @@ void UpdateOverworldEncounters(void) if(sFollowMonData.pendingSpawnAnim != 0) { u32 spawnSlot = sFollowMonData.pendingSpawnAnim - 1; - u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot); + u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); enum FollowMonSpawnAnim spawnAnimType; if (speciesId != SPECIES_NONE) @@ -171,7 +173,7 @@ static u32 GetOldestSlot(void) for (u32 spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) { - if (GetOverworldSpeciesFromSpawnSlot(spawnSlot) != SPECIES_NONE) + if (GetOverworldSpeciesBySpawnSlot(spawnSlot) != SPECIES_NONE) { if (sFollowMonData.list[spawnSlot].age > sFollowMonData.list[oldest].age) oldest = spawnSlot; @@ -196,13 +198,13 @@ static u8 NextSpawnMonSlot(void) { for (spawnSlot = 0; spawnSlot < maxSpawns; spawnSlot++) { - if (GetOverworldSpeciesFromSpawnSlot(spawnSlot) == SPECIES_NONE) + if (GetOverworldSpeciesBySpawnSlot(spawnSlot) == SPECIES_NONE) break; } } // Remove any existing id by this slot - RemoveObjectEventByLocalIdAndMap(LOCALID_OW_ENCOUNTER_END - spawnSlot, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + RemoveObjectEventByLocalIdAndMap(GetLocalIdByOverworldSpawnSlot(spawnSlot), gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); // Check that we don't have too many sprites on screen before spawning // (lag reduction) @@ -301,7 +303,8 @@ static bool8 TrySelectTile(s16* outX, s16* outY) void CreateFollowMonEncounter(void) { - u32 objEventId = GetObjectEventIdByLocalIdAndMap(gSpecialVar_LastTalked, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + u32 localId = gSpecialVar_LastTalked; + u32 objEventId = GetObjectEventIdByLocalIdAndMap(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); struct ObjectEvent *object = &gObjectEvents[objEventId]; if (objEventId >= OBJECT_EVENTS_COUNT) @@ -310,7 +313,7 @@ void CreateFollowMonEncounter(void) if (!IsGeneratedOverworldEncounter(object)) return; - u32 slot = LOCALID_OW_ENCOUNTER_END - object->localId; + u32 slot = GetSpawnSlotByLocalId(localId); u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; bool32 isFemale = OW_FEMALE(object) ? TRUE : FALSE; @@ -383,7 +386,7 @@ static void SortOWEMonAges(void) for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - if (GetOverworldSpeciesFromSpawnSlot(i) != SPECIES_NONE) + if (GetOverworldSpeciesBySpawnSlot(i) != SPECIES_NONE) { array[count].slot = i; array[count].age = sFollowMonData.list[i].age; @@ -419,7 +422,7 @@ static void SortOWEMonAges(void) void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { - u32 spawnSlot = LOCALID_OW_ENCOUNTER_END - objectEvent->localId; + u32 spawnSlot = GetSpawnSlotByLocalId(objectEvent->localId); sFollowMonData.pendingSpawnAnim = spawnSlot + 1; SortOWEMonAges(); @@ -427,7 +430,7 @@ void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { - u32 spawnSlot = LOCALID_OW_ENCOUNTER_END - objectEvent->localId; + u32 spawnSlot = GetSpawnSlotByLocalId(objectEvent->localId); sFollowMonData.list[spawnSlot].age = 0; } @@ -505,7 +508,7 @@ static u8 CountActiveFollowMon() u32 count = 0; for (u32 spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) { - if (GetOverworldSpeciesFromSpawnSlot(spawnSlot) != SPECIES_NONE) + if (GetOverworldSpeciesBySpawnSlot(spawnSlot) != SPECIES_NONE) count++; } @@ -615,9 +618,19 @@ bool32 IsGeneratedOverworldEncounter(struct ObjectEvent *objectEvent) return (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_ENCOUNTER); } -u16 GetOverworldSpeciesFromSpawnSlot(u32 spawnSlot) +u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) { - u32 objEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot); + u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; return OW_SPECIES(objectEvent); } + +static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot) +{ + return LOCALID_OW_ENCOUNTER_END - spawnSlot; +} + +static u32 GetSpawnSlotByLocalId(u32 localId) +{ + return LOCALID_OW_ENCOUNTER_END - localId; +} From ceaa8af9a16347b5ac2275a5db1d0066e902e26d Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 30 Nov 2025 13:25:27 +0000 Subject: [PATCH 081/572] Static Declare GetOverworldSpeciesBySpawnSlot --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 078a6b3c9f55..b319b7bba37d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -618,7 +618,7 @@ bool32 IsGeneratedOverworldEncounter(struct ObjectEvent *objectEvent) return (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_ENCOUNTER); } -u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) +static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) { u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; From 3c14e7fa5ba7427cb49fe2689adbf5ccca2b6ae9 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 30 Nov 2025 18:55:41 -0600 Subject: [PATCH 082/572] Added OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT config --- include/config/overworld.h | 1 + src/overworld_encounters.c | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 867daf8c38fa..0285219d9c0c 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -75,6 +75,7 @@ #define OW_WILD_ENCOUNTERS_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. #define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. #define OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will only use restricted movement types (MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER/MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER/MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER). +#define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. // Out-of-battle Ability effects #define OW_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8+, if a Pokémon with Synchronize leads the party, wild Pokémon will always have their same Nature as opposed to the 50% chance in previous games. Gift Pokémon excluded. diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 430144751bc3..60a36b9fa4e6 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -204,9 +204,12 @@ static u8 NextSpawnMonSlot(void) } } - // Remove any existing id by this slot - RemoveObjectEventByLocalIdAndMap(GetLocalIdByOverworldSpawnSlot(spawnSlot), gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); - + if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) + { + // Remove any existing id by this slot + RemoveObjectEventByLocalIdAndMap(GetLocalIdByOverworldSpawnSlot(spawnSlot), gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + } + // Check that we don't have too many sprites on screen before spawning // (lag reduction) if(maxAllowedObjects) From 24445a49bdc2d6413237d0211eea000c7cb2f0fa Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 30 Nov 2025 19:37:25 -0600 Subject: [PATCH 083/572] Turned max spawn values into constants and separated cave max value from water max value --- include/overworld_encounters.h | 4 ++++ src/overworld_encounters.c | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 5a09e584812d..4ad58c3436ef 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -8,6 +8,10 @@ #define FOLLOWMON_MAX_SPAWN_SLOTS 5 #define FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT 8 +#define OWE_MAX_LAND_SPAWNS 3 +#define OWE_MAX_WATER_SPAWNS 5 +#define OWE_MAX_CAVE_SPAWNS 4 + #define INVALID_SPAWN_SLOT 0xFF struct FollowMon diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 60a36b9fa4e6..b58245158ad2 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -161,10 +161,12 @@ void UpdateOverworldEncounters(void) static u8 GetMaxFollowMonSpawns(void) { - if (IsSpawningWaterMons() || gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) - return 5; + if (IsSpawningWaterMons()) + return OWE_MAX_WATER_SPAWNS; + else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) + return OWE_MAX_CAVE_SPAWNS; else - return 3; + return OWE_MAX_LAND_SPAWNS; } static u32 GetOldestSlot(void) From 002af834e7162404646a12f0b9533011533ea721 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 1 Dec 2025 00:14:11 -0600 Subject: [PATCH 084/572] OW mons are destroyed to allow new objects if at object limit and removed FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT --- include/overworld_encounters.h | 12 +++++++----- src/event_object_movement.c | 22 +++++++++++++++++++++- src/field_effect_helpers.c | 7 +++++++ src/overworld_encounters.c | 30 ++++++++++++++++-------------- 4 files changed, 51 insertions(+), 20 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 4ad58c3436ef..92fafa39a36b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -5,12 +5,11 @@ #error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OW_WILD_ENCOUNTERS_OVERWORLD to work." #endif -#define FOLLOWMON_MAX_SPAWN_SLOTS 5 -#define FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT 8 +#define FOLLOWMON_MAX_SPAWN_SLOTS 5 -#define OWE_MAX_LAND_SPAWNS 3 -#define OWE_MAX_WATER_SPAWNS 5 -#define OWE_MAX_CAVE_SPAWNS 4 +#define OWE_MAX_LAND_SPAWNS 3 +#define OWE_MAX_WATER_SPAWNS 5 +#define OWE_MAX_CAVE_SPAWNS 4 #define INVALID_SPAWN_SLOT 0xFF @@ -35,14 +34,17 @@ extern const u8 InteractWithDynamicWildFollowMon[]; void LoadFollowMonData(struct ObjectEvent *objectEvent); void UpdateOverworldEncounters(void); +u32 GetOldestSlot(void); void CreateFollowMonEncounter(void); bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale); void ClearOverworldEncounterData(void); +u8 CountActiveFollowMon(); void RemoveOverworldEncounterObjects(void); bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); bool32 IsGeneratedOverworldEncounter(struct ObjectEvent *objectEvent); +u32 GetNewestOWEncounterLocalId(void); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index c5484030d0bb..7603c65f4332 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1522,20 +1522,39 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * // loaded, returns TRUE. { u8 i = 0; + bool32 canRemoveOWEncounter = (OW_WILD_ENCOUNTERS_OVERWORLD && CountActiveFollowMon() != 0 && (localId <= (LOCALID_OW_ENCOUNTER_END - FOLLOWMON_MAX_SPAWN_SLOTS + 1) || localId > LOCALID_OW_ENCOUNTER_END)); for (i = 0; i < OBJECT_EVENTS_COUNT && gObjectEvents[i].active; i++) { if (gObjectEvents[i].localId == localId && gObjectEvents[i].mapNum == mapNum && gObjectEvents[i].mapGroup == mapGroup) return TRUE; } - if (i >= OBJECT_EVENTS_COUNT) + + if (i >= OBJECT_EVENTS_COUNT && !canRemoveOWEncounter) return TRUE; + *objectEventId = i; for (; i < OBJECT_EVENTS_COUNT; i++) { if (gObjectEvents[i].active && gObjectEvents[i].localId == localId && gObjectEvents[i].mapNum == mapNum && gObjectEvents[i].mapGroup == mapGroup) return TRUE; } + + // Destroy the oldest OW Encounter mon to make room for the new object. + if (*objectEventId >= OBJECT_EVENTS_COUNT && canRemoveOWEncounter) + { + *objectEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - GetOldestSlot()); + s16 *fldEffSpriteId = &gSprites[gObjectEvents[*objectEventId].spriteId].data[6]; + + // Stop the associated field effect if it is active. + if (*fldEffSpriteId != 0) + { + FieldEffectStop(&gSprites[*fldEffSpriteId - 1], FLDEFF_BUBBLES); + } + + RemoveObjectEvent(&gObjectEvents[*objectEventId]); + } + return FALSE; } @@ -11568,6 +11587,7 @@ bool8 MovementAction_FollowMonSpawn(enum FollowMonSpawnAnim spawnAnimType, struc gFieldEffectArguments[1] = objEvent->currentCoords.y; gFieldEffectArguments[2] = gSprites[objEvent->spriteId].oam.priority + 1; gFieldEffectArguments[3] = spawnAnimType; + gFieldEffectArguments[4] = objEvent->spriteId; FieldEffectStart(FLDEFF_BUBBLES); // Commandeer this field effect for the spawn anims return TRUE; } diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 5c757775f449..0a7dc45c08fc 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -7,6 +7,7 @@ #include "fieldmap.h" #include "gpu_regs.h" #include "metatile_behavior.h" +#include "overworld_encounters.h" #include "palette.h" #include "sound.h" #include "sprite.h" @@ -1516,6 +1517,9 @@ u32 FldEff_Bubbles(void) struct Sprite *sprite = &gSprites[spriteId]; sprite->coordOffsetEnabled = TRUE; sprite->oam.priority = 1; + + // Save the spriteId in the sprite data of the corresponding OW Encounter object. + gSprites[gFieldEffectArguments[4]].data[6] = spriteId + 1; } return 0; } @@ -1530,7 +1534,10 @@ void UpdateBubblesFieldEffect(struct Sprite *sprite) sprite->y -= sprite->sY >> 8; UpdateObjectEventSpriteInvisibility(sprite, FALSE); if (sprite->invisible || sprite->animEnded) + { FieldEffectStop(sprite, FLDEFF_BUBBLES); + gSprites[gObjectEvents[GetNewestOWEncounterLocalId()].spriteId].data[6] = 0; + } } #undef sY diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index b58245158ad2..69353fb98aef 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -20,7 +20,6 @@ static EWRAM_DATA struct FollowMonData sFollowMonData = { 0 }; -static u8 CountActiveFollowMon(); static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool8 IsSpawningWaterMons(); @@ -169,7 +168,7 @@ static u8 GetMaxFollowMonSpawns(void) return OWE_MAX_LAND_SPAWNS; } -static u32 GetOldestSlot(void) +u32 GetOldestSlot(void) { u32 oldest = 0; @@ -189,10 +188,9 @@ static u8 NextSpawnMonSlot(void) { u32 spawnSlot; u32 maxSpawns = GetMaxFollowMonSpawns(); - bool32 maxAllowedObjects = CountActiveObjectEvents() >= FOLLOWMON_IDEAL_OBJECT_EVENT_COUNT; // All mon slots are in use - if (CountActiveFollowMon() >= maxSpawns || maxAllowedObjects) + if (CountActiveFollowMon() >= maxSpawns) { // Cycle through so we remove the oldest mon first spawnSlot = GetOldestSlot(); @@ -212,15 +210,6 @@ static u8 NextSpawnMonSlot(void) RemoveObjectEventByLocalIdAndMap(GetLocalIdByOverworldSpawnSlot(spawnSlot), gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); } - // Check that we don't have too many sprites on screen before spawning - // (lag reduction) - if(maxAllowedObjects) - { - // Reset the countdown so multiple mons don't get destroyed at once. - sFollowMonData.spawnCountdown = 60; - return INVALID_SPAWN_SLOT; - } - return spawnSlot; } @@ -512,7 +501,7 @@ static bool8 IsSafeToSpawnObjectEvents(void) return (player->currentCoords.x == player->previousCoords.x && player->currentCoords.y == player->previousCoords.y); } -static u8 CountActiveFollowMon() +u8 CountActiveFollowMon() { u32 count = 0; for (u32 spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) @@ -643,3 +632,16 @@ static u32 GetSpawnSlotByLocalId(u32 localId) { return LOCALID_OW_ENCOUNTER_END - localId; } + +u32 GetNewestOWEncounterLocalId(void) +{ + u32 i; + u32 newestSlot = 0; + for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) + { + if (sFollowMonData.list[newestSlot].age > sFollowMonData.list[i].age) + newestSlot = i; + } + + return newestSlot; +} From a93c6a7cdbe37c8448427a81c6b9aae0d4113565 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 10:02:45 +0000 Subject: [PATCH 085/572] Clean up additions to event_object_movement.c --- include/overworld_encounters.h | 2 ++ src/event_object_movement.c | 18 +++--------------- src/overworld_encounters.c | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 92fafa39a36b..567f01e7e893 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -46,5 +46,7 @@ void RemoveOverworldEncounterObjects(void); bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); bool32 IsGeneratedOverworldEncounter(struct ObjectEvent *objectEvent); u32 GetNewestOWEncounterLocalId(void); +bool32 CanRemoveOverworldEncounter(u32 localId); +void RemoveOldestOverworldEncounter(void); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 7603c65f4332..efd5791a5715 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1522,7 +1522,6 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * // loaded, returns TRUE. { u8 i = 0; - bool32 canRemoveOWEncounter = (OW_WILD_ENCOUNTERS_OVERWORLD && CountActiveFollowMon() != 0 && (localId <= (LOCALID_OW_ENCOUNTER_END - FOLLOWMON_MAX_SPAWN_SLOTS + 1) || localId > LOCALID_OW_ENCOUNTER_END)); for (i = 0; i < OBJECT_EVENTS_COUNT && gObjectEvents[i].active; i++) { @@ -1530,7 +1529,7 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * return TRUE; } - if (i >= OBJECT_EVENTS_COUNT && !canRemoveOWEncounter) + if (i >= OBJECT_EVENTS_COUNT && !CanRemoveOverworldEncounter(localId)) return TRUE; *objectEventId = i; @@ -1541,19 +1540,8 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * } // Destroy the oldest OW Encounter mon to make room for the new object. - if (*objectEventId >= OBJECT_EVENTS_COUNT && canRemoveOWEncounter) - { - *objectEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - GetOldestSlot()); - s16 *fldEffSpriteId = &gSprites[gObjectEvents[*objectEventId].spriteId].data[6]; - - // Stop the associated field effect if it is active. - if (*fldEffSpriteId != 0) - { - FieldEffectStop(&gSprites[*fldEffSpriteId - 1], FLDEFF_BUBBLES); - } - - RemoveObjectEvent(&gObjectEvents[*objectEventId]); - } + if (*objectEventId >= OBJECT_EVENTS_COUNT && CanRemoveOverworldEncounter(localId)) + RemoveOldestOverworldEncounter(); return FALSE; } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 69353fb98aef..0350330ec112 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -4,6 +4,7 @@ #include "event_data.h" #include "event_object_movement.h" #include "fieldmap.h" +#include "field_effect.h" #include "field_player_avatar.h" #include "metatile_behavior.h" #include "overworld.h" @@ -13,6 +14,7 @@ #include "sound.h" #include "wild_encounter.h" #include "constants/event_objects.h" +#include "constants/field_effects.h" #include "constants/map_types.h" #include "constants/trainer_types.h" #include "constants/songs.h" @@ -645,3 +647,22 @@ u32 GetNewestOWEncounterLocalId(void) return newestSlot; } + +bool32 CanRemoveOverworldEncounter(u32 localId) +{ + return (OW_WILD_ENCOUNTERS_OVERWORLD && CountActiveFollowMon() != 0 + && (localId <= (LOCALID_OW_ENCOUNTER_END - FOLLOWMON_MAX_SPAWN_SLOTS + 1) + || localId > LOCALID_OW_ENCOUNTER_END)); +} + +void RemoveOldestOverworldEncounter(void) +{ + u32 objectEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - GetOldestSlot()); + s16 *fldEffSpriteId = &gSprites[gObjectEvents[objectEventId].spriteId].data[6]; + + // Stop the associated field effect if it is active. + if (*fldEffSpriteId != 0) + FieldEffectStop(&gSprites[*fldEffSpriteId - 1], FLDEFF_BUBBLES); + + RemoveObjectEvent(&gObjectEvents[objectEventId]); +} From 28ece5fb4cf55934e1f6cdad1bd9700c8460afce Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 10:17:48 +0000 Subject: [PATCH 086/572] Comments and minor changes --- src/event_object_movement.c | 18 ++++++++++++++++++ src/overworld_encounters.c | 6 +++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index efd5791a5715..816e551daa97 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1540,6 +1540,24 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * } // Destroy the oldest OW Encounter mon to make room for the new object. + /* Can we integrate this with this line above: + if (i >= OBJECT_EVENTS_COUNT && !CanRemoveOverworldEncounter(localId)) + + Something like: + if (i >= OBJECT_EVENTS_COUNT) + return TryAndRemoveOldestOverworldEncounter(localId); + + Where: + bool32 TryAndRemoveOldestOverworldEncounter(u32 localId) + { + if (CanRemoveOverworldEncounter(localId)) + { + RemoveOldestOverworldEncounter(); + return FALSE; + } + return TRUE; + } + */ if (*objectEventId >= OBJECT_EVENTS_COUNT && CanRemoveOverworldEncounter(localId)) RemoveOldestOverworldEncounter(); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 0350330ec112..ec8c439de736 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -650,6 +650,8 @@ u32 GetNewestOWEncounterLocalId(void) bool32 CanRemoveOverworldEncounter(u32 localId) { + // Can the last of these checks be replaced by IsGeneratedOverworldEncounter? + // Include a check for the encounter not being shiny or a roamer. return (OW_WILD_ENCOUNTERS_OVERWORLD && CountActiveFollowMon() != 0 && (localId <= (LOCALID_OW_ENCOUNTER_END - FOLLOWMON_MAX_SPAWN_SLOTS + 1) || localId > LOCALID_OW_ENCOUNTER_END)); @@ -657,7 +659,9 @@ bool32 CanRemoveOverworldEncounter(u32 localId) void RemoveOldestOverworldEncounter(void) { - u32 objectEventId = GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - GetOldestSlot()); + // Can we have a dedicated remove object function as it can be used for the other case of + // RemoveObjectEvent in this function too? + u32 objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(GetOldestSlot())); s16 *fldEffSpriteId = &gSprites[gObjectEvents[objectEventId].spriteId].data[6]; // Stop the associated field effect if it is active. From 4af56e9e97443e985aff298afabff512500ebbb0 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:12:17 +0000 Subject: [PATCH 087/572] Further Reduce Changes to event_object_movement.c --- include/overworld_encounters.h | 3 +-- src/event_object_movement.c | 27 +++------------------------ src/overworld_encounters.c | 18 ++++++++++++++++-- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 567f01e7e893..91c010b00e97 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -46,7 +46,6 @@ void RemoveOverworldEncounterObjects(void); bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); bool32 IsGeneratedOverworldEncounter(struct ObjectEvent *objectEvent); u32 GetNewestOWEncounterLocalId(void); -bool32 CanRemoveOverworldEncounter(u32 localId); -void RemoveOldestOverworldEncounter(void); +bool32 TryAndRemoveOldestOverworldEncounter(u32 localId); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 816e551daa97..355d04c284a7 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1529,8 +1529,9 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * return TRUE; } - if (i >= OBJECT_EVENTS_COUNT && !CanRemoveOverworldEncounter(localId)) - return TRUE; + // If possible, destroy the oldest OW Encounter mon to make room for the new object. + if (i >= OBJECT_EVENTS_COUNT) + return TryAndRemoveOldestOverworldEncounter(localId); *objectEventId = i; for (; i < OBJECT_EVENTS_COUNT; i++) @@ -1539,28 +1540,6 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * return TRUE; } - // Destroy the oldest OW Encounter mon to make room for the new object. - /* Can we integrate this with this line above: - if (i >= OBJECT_EVENTS_COUNT && !CanRemoveOverworldEncounter(localId)) - - Something like: - if (i >= OBJECT_EVENTS_COUNT) - return TryAndRemoveOldestOverworldEncounter(localId); - - Where: - bool32 TryAndRemoveOldestOverworldEncounter(u32 localId) - { - if (CanRemoveOverworldEncounter(localId)) - { - RemoveOldestOverworldEncounter(); - return FALSE; - } - return TRUE; - } - */ - if (*objectEventId >= OBJECT_EVENTS_COUNT && CanRemoveOverworldEncounter(localId)) - RemoveOldestOverworldEncounter(); - return FALSE; } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index ec8c439de736..c7882ed280d9 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -37,6 +37,9 @@ static void OverworldEncounters_ProcessMonInteraction(void); static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); static u32 GetSpawnSlotByLocalId(u32 localId); +static bool32 CanRemoveOverworldEncounter(u32 localId); +static void RemoveOldestOverworldEncounter(void); +static void SortOWEMonAges(void); void LoadFollowMonData(struct ObjectEvent *objectEvent) { @@ -648,7 +651,7 @@ u32 GetNewestOWEncounterLocalId(void) return newestSlot; } -bool32 CanRemoveOverworldEncounter(u32 localId) +static bool32 CanRemoveOverworldEncounter(u32 localId) { // Can the last of these checks be replaced by IsGeneratedOverworldEncounter? // Include a check for the encounter not being shiny or a roamer. @@ -657,7 +660,7 @@ bool32 CanRemoveOverworldEncounter(u32 localId) || localId > LOCALID_OW_ENCOUNTER_END)); } -void RemoveOldestOverworldEncounter(void) +static void RemoveOldestOverworldEncounter(void) { // Can we have a dedicated remove object function as it can be used for the other case of // RemoveObjectEvent in this function too? @@ -670,3 +673,14 @@ void RemoveOldestOverworldEncounter(void) RemoveObjectEvent(&gObjectEvents[objectEventId]); } + +bool32 TryAndRemoveOldestOverworldEncounter(u32 localId) +{ + if (CanRemoveOverworldEncounter(localId)) + { + RemoveOldestOverworldEncounter(); + return FALSE; + } + + return TRUE; +} From c869f9e7c55d99d816359335c33976d4c456fefe Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:18:10 +0000 Subject: [PATCH 088/572] Create Dedicated RemoveOverworldEncounterObject Function --- include/overworld_encounters.h | 2 +- src/overworld.c | 2 -- src/overworld_encounters.c | 16 ++++++++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 91c010b00e97..0250af010bc2 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -42,7 +42,7 @@ void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale); void ClearOverworldEncounterData(void); u8 CountActiveFollowMon(); -void RemoveOverworldEncounterObjects(void); +void RemoveAllOverworldEncounterObjects(void); bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); bool32 IsGeneratedOverworldEncounter(struct ObjectEvent *objectEvent); u32 GetNewestOWEncounterLocalId(void); diff --git a/src/overworld.c b/src/overworld.c index 902940ddd612..e9c984519cf0 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -894,8 +894,6 @@ if (I_VS_SEEKER_CHARGING != 0) || gMapHeader.regionMapSectionId != sLastMapSectionId) ShowMapNamePopup(); } - // ClearOverworldEncounterData(); - // RemoveOverworldEncounterObjects(); } static void LoadMapFromWarp(bool32 a1) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c7882ed280d9..5cab7d35db16 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -40,6 +40,7 @@ static u32 GetSpawnSlotByLocalId(u32 localId); static bool32 CanRemoveOverworldEncounter(u32 localId); static void RemoveOldestOverworldEncounter(void); static void SortOWEMonAges(void); +static void RemoveOverworldEncounterObject(struct ObjectEvent *objectEvent); void LoadFollowMonData(struct ObjectEvent *objectEvent) { @@ -53,7 +54,7 @@ void UpdateOverworldEncounters(void) if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER)) // Need check for if header has encounters? { - RemoveOverworldEncounterObjects(); + RemoveAllOverworldEncounterObjects(); // Zero sFollowMonData ; u8 *raw = (u8 *)&sFollowMonData; for (u32 i = 0; i < sizeof(struct FollowMonData); i++) @@ -537,13 +538,18 @@ static bool8 IsSpawningWaterMons() return (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER)); } -void RemoveOverworldEncounterObjects(void) +static void RemoveOverworldEncounterObject(struct ObjectEvent *objectEvent) +{ + RemoveObjectEvent(objectEvent); +} + +void RemoveAllOverworldEncounterObjects(void) { for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { struct ObjectEvent *obj = &gObjectEvents[i]; if (IsGeneratedOverworldEncounter(obj)) - RemoveObjectEvent(obj); + RemoveOverworldEncounterObject(obj); } } @@ -662,8 +668,6 @@ static bool32 CanRemoveOverworldEncounter(u32 localId) static void RemoveOldestOverworldEncounter(void) { - // Can we have a dedicated remove object function as it can be used for the other case of - // RemoveObjectEvent in this function too? u32 objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(GetOldestSlot())); s16 *fldEffSpriteId = &gSprites[gObjectEvents[objectEventId].spriteId].data[6]; @@ -671,7 +675,7 @@ static void RemoveOldestOverworldEncounter(void) if (*fldEffSpriteId != 0) FieldEffectStop(&gSprites[*fldEffSpriteId - 1], FLDEFF_BUBBLES); - RemoveObjectEvent(&gObjectEvents[objectEventId]); + RemoveOverworldEncounterObject(&gObjectEvents[objectEventId]); } bool32 TryAndRemoveOldestOverworldEncounter(u32 localId) From 6967ce700b92f313595ec5929d7655b00e2564b3 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:48:11 +0000 Subject: [PATCH 089/572] Fix Uninttialised ObjecctEventId --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 15 ++++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 0250af010bc2..1f7b8aad24ca 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -46,6 +46,6 @@ void RemoveAllOverworldEncounterObjects(void); bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); bool32 IsGeneratedOverworldEncounter(struct ObjectEvent *objectEvent); u32 GetNewestOWEncounterLocalId(void); -bool32 TryAndRemoveOldestOverworldEncounter(u32 localId); +bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 355d04c284a7..3a3f99cc07ec 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1531,7 +1531,7 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * // If possible, destroy the oldest OW Encounter mon to make room for the new object. if (i >= OBJECT_EVENTS_COUNT) - return TryAndRemoveOldestOverworldEncounter(localId); + return TryAndRemoveOldestOverworldEncounter(localId, objectEventId); *objectEventId = i; for (; i < OBJECT_EVENTS_COUNT; i++) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5cab7d35db16..27a7fa7c68a3 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -38,7 +38,7 @@ static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); static u32 GetSpawnSlotByLocalId(u32 localId); static bool32 CanRemoveOverworldEncounter(u32 localId); -static void RemoveOldestOverworldEncounter(void); +static void RemoveOldestOverworldEncounter(u8 *objectEventId); static void SortOWEMonAges(void); static void RemoveOverworldEncounterObject(struct ObjectEvent *objectEvent); @@ -666,23 +666,24 @@ static bool32 CanRemoveOverworldEncounter(u32 localId) || localId > LOCALID_OW_ENCOUNTER_END)); } -static void RemoveOldestOverworldEncounter(void) +static void RemoveOldestOverworldEncounter(u8 *objectEventId) { - u32 objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(GetOldestSlot())); - s16 *fldEffSpriteId = &gSprites[gObjectEvents[objectEventId].spriteId].data[6]; + *objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(GetOldestSlot())); + s16 *fldEffSpriteId = &gSprites[gObjectEvents[*objectEventId].spriteId].data[6]; // Stop the associated field effect if it is active. if (*fldEffSpriteId != 0) FieldEffectStop(&gSprites[*fldEffSpriteId - 1], FLDEFF_BUBBLES); - RemoveOverworldEncounterObject(&gObjectEvents[objectEventId]); + RemoveOverworldEncounterObject(&gObjectEvents[*objectEventId]); } -bool32 TryAndRemoveOldestOverworldEncounter(u32 localId) +bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) { + // How does this work for objects if they are on a different map? if (CanRemoveOverworldEncounter(localId)) { - RemoveOldestOverworldEncounter(); + RemoveOldestOverworldEncounter(objectEventId); return FALSE; } From cc60e86c0297faa284cd4c7b41a7507f30b69162 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:53:17 +0000 Subject: [PATCH 090/572] Start to tidy RemoveObject --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 4 +--- src/overworld_encounters.c | 6 ++++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 1f7b8aad24ca..39024a0a881c 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -38,7 +38,7 @@ u32 GetOldestSlot(void); void CreateFollowMonEncounter(void); bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent); -void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent); +void OverworldEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale); void ClearOverworldEncounterData(void); u8 CountActiveFollowMon(); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 3a3f99cc07ec..97a4f2bd4fac 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1549,6 +1549,7 @@ void RemoveObjectEvent(struct ObjectEvent *objectEvent) RemoveObjectEventInternal(objectEvent); // zero potential species info objectEvent->graphicsId = objectEvent->shiny = 0; + OverworldEncounter_OnObjectEventRemoved(objectEvent); } void RemoveObjectEventByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup) @@ -1565,9 +1566,6 @@ static void RemoveObjectEventInternal(struct ObjectEvent *objectEvent) { struct SpriteFrameImage image; - if (IsGeneratedOverworldEncounter(objectEvent)) - FollowMon_OnObjectEventRemoved(objectEvent); - image.size = GetObjectEventGraphicsInfo(objectEvent->graphicsId)->size; gSprites[objectEvent->spriteId].images = ℑ // It's possible that this function is called while the sprite pointed to `== sDummySprite`, i.e during map resume; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 27a7fa7c68a3..704384519add 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -432,10 +432,12 @@ void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) SortOWEMonAges(); } -void FollowMon_OnObjectEventRemoved(struct ObjectEvent *objectEvent) +void OverworldEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { + if (!IsGeneratedOverworldEncounter(objectEvent)) + return; + u32 spawnSlot = GetSpawnSlotByLocalId(objectEvent->localId); - sFollowMonData.list[spawnSlot].age = 0; } From a5e0921c88e939767047a4c5a15b146c5bd22827 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:55:35 +0000 Subject: [PATCH 091/572] Remove Dedicated Overworld Encounter Destroy Function --- src/overworld_encounters.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 704384519add..61b64d3939ea 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -40,7 +40,6 @@ static u32 GetSpawnSlotByLocalId(u32 localId); static bool32 CanRemoveOverworldEncounter(u32 localId); static void RemoveOldestOverworldEncounter(u8 *objectEventId); static void SortOWEMonAges(void); -static void RemoveOverworldEncounterObject(struct ObjectEvent *objectEvent); void LoadFollowMonData(struct ObjectEvent *objectEvent) { @@ -540,18 +539,13 @@ static bool8 IsSpawningWaterMons() return (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER)); } -static void RemoveOverworldEncounterObject(struct ObjectEvent *objectEvent) -{ - RemoveObjectEvent(objectEvent); -} - void RemoveAllOverworldEncounterObjects(void) { for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { struct ObjectEvent *obj = &gObjectEvents[i]; if (IsGeneratedOverworldEncounter(obj)) - RemoveOverworldEncounterObject(obj); + RemoveObjectEvent(obj); } } @@ -677,7 +671,7 @@ static void RemoveOldestOverworldEncounter(u8 *objectEventId) if (*fldEffSpriteId != 0) FieldEffectStop(&gSprites[*fldEffSpriteId - 1], FLDEFF_BUBBLES); - RemoveOverworldEncounterObject(&gObjectEvents[*objectEventId]); + RemoveObjectEvent(&gObjectEvents[*objectEventId]); } bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) From 1a10f6d9babab52731d7b1dbda78e3a4285bf34e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:59:19 +0000 Subject: [PATCH 092/572] Remove Comment as is fine --- src/overworld_encounters.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 61b64d3939ea..f4f4c42ea473 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -676,7 +676,6 @@ static void RemoveOldestOverworldEncounter(u8 *objectEventId) bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) { - // How does this work for objects if they are on a different map? if (CanRemoveOverworldEncounter(localId)) { RemoveOldestOverworldEncounter(objectEventId); From 82722c50e41aa0e7d418a6d96976a949e3cde57e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 14:17:34 +0000 Subject: [PATCH 093/572] Add DexNav check for UpdateOverworldEncounters and OverworldEncounter_IsCollisionExempt --- src/overworld_encounters.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f4f4c42ea473..78f850ff0e8c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -48,7 +48,7 @@ void LoadFollowMonData(struct ObjectEvent *objectEvent) void UpdateOverworldEncounters(void) { - if (ArePlayerFieldControlsLocked()) + if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING)) return; if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER)) // Need check for if header has encounters? @@ -356,18 +356,15 @@ static void OverworldEncounters_ProcessMonInteraction(void) bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider) { - // The player is only exempt from collisions with overworld encounters when not using a repel. - // Non-player, non-overworld encounters are not collision exempt with overworld encounters. + // The player only is exempt from collisions with OW Encounters when not using a repel or the DexNav is inactive. struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + bool32 forcePlayerCollision = (/* VarGet(VAR_REPEL_STEP_COUNT) > 0 || */ FlagGet(DN_FLAG_SEARCHING)); - if (collider == player && IsGeneratedOverworldEncounter(obstacle) /* && VarGet(VAR_REPEL_STEP_COUNT) == 0 */) + if (collider == player && IsGeneratedOverworldEncounter(obstacle) && !forcePlayerCollision) return TRUE; - if (obstacle == player && IsGeneratedOverworldEncounter(collider) /* && VarGet(VAR_REPEL_STEP_COUNT) == 0 */) - return TRUE; - - if (!IsGeneratedOverworldEncounter(collider) && IsGeneratedOverworldEncounter(obstacle)) + if (obstacle == player && IsGeneratedOverworldEncounter(collider) && !forcePlayerCollision) return TRUE; return FALSE; From a4234b391b53e8e7374f9121907da19a4929a86d Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 15:36:38 +0000 Subject: [PATCH 094/572] Differentiate Between Generated on Non-Generated OW Encounter --- include/overworld_encounters.h | 9 +++++---- src/event_object_movement.c | 5 ++--- src/field_control_avatar.c | 2 +- src/load_save.c | 4 +--- src/overworld_encounters.c | 33 +++++++++++++++++++++------------ 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 39024a0a881c..e090fa6f3287 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -32,19 +32,20 @@ struct FollowMonData //data/scripts/followmon.inc extern const u8 InteractWithDynamicWildFollowMon[]; -void LoadFollowMonData(struct ObjectEvent *objectEvent); +void LoadFollowMonData(void); void UpdateOverworldEncounters(void); u32 GetOldestSlot(void); void CreateFollowMonEncounter(void); bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); -void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent); -void OverworldEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); +void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); +void GeneratedOverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale); void ClearOverworldEncounterData(void); u8 CountActiveFollowMon(); void RemoveAllOverworldEncounterObjects(void); bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); -bool32 IsGeneratedOverworldEncounter(struct ObjectEvent *objectEvent); +bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent); +bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent); u32 GetNewestOWEncounterLocalId(void); bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 97a4f2bd4fac..4ac6eb3a20cc 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1549,7 +1549,7 @@ void RemoveObjectEvent(struct ObjectEvent *objectEvent) RemoveObjectEventInternal(objectEvent); // zero potential species info objectEvent->graphicsId = objectEvent->shiny = 0; - OverworldEncounter_OnObjectEventRemoved(objectEvent); + GeneratedOverworldWildEncounter_OnObjectEventRemoved(objectEvent); } void RemoveObjectEventByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup) @@ -1787,8 +1787,7 @@ u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemp if (subspriteTables) SetSubspriteTables(&gSprites[gObjectEvents[objectEventId].spriteId], subspriteTables); - if (IsGeneratedOverworldEncounter(&gObjectEvents[objectEventId])) - FollowMon_OnObjectEventSpawned(&gObjectEvents[objectEventId]); + GeneratedOverworldWildEncounter_OnObjectEventSpawned(&gObjectEvents[objectEventId]); return objectEventId; } diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index e3965d9e214d..2750be77a83a 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -400,7 +400,7 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; gSpecialVar_Facing = direction; - if (IsGeneratedOverworldEncounter(&gObjectEvents[objectEventId])) + if (IsOverworldWildEncounter(&gObjectEvents[objectEventId]) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL) script = InteractWithDynamicWildFollowMon; else if (InTrainerHill() == TRUE) script = GetTrainerHillTrainerScript(); diff --git a/src/load_save.c b/src/load_save.c index c3d44eb55e97..3c32198af000 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -221,6 +221,7 @@ void LoadObjectEvents(void) int i; u16 graphicsId; + LoadFollowMonData(); for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { gObjectEvents[i] = gSaveBlock1Ptr->objectEvents[i]; @@ -233,9 +234,6 @@ void LoadObjectEvents(void) gObjectEvents[i].graphicsId &= 0xFF; gObjectEvents[i].spriteId = 0; - if (IsGeneratedOverworldEncounter(&gObjectEvents[i])) - LoadFollowMonData(&gObjectEvents[i]); - // Try to restore saved inactive follower if (gObjectEvents[i].localId == OBJ_EVENT_ID_FOLLOWER && !gObjectEvents[i].active && diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 78f850ff0e8c..7c6fdb7684c3 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -41,7 +41,7 @@ static bool32 CanRemoveOverworldEncounter(u32 localId); static void RemoveOldestOverworldEncounter(u8 *objectEventId); static void SortOWEMonAges(void); -void LoadFollowMonData(struct ObjectEvent *objectEvent) +void LoadFollowMonData(void) { sFollowMonData.spawnCountdown += 60; } @@ -313,7 +313,7 @@ void CreateFollowMonEncounter(void) if (objEventId >= OBJECT_EVENTS_COUNT) return; - if (!IsGeneratedOverworldEncounter(object)) + if (!IsOverworldWildEncounter(object)) return; u32 slot = GetSpawnSlotByLocalId(localId); @@ -343,7 +343,7 @@ static void OverworldEncounters_ProcessMonInteraction(void) for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { object = &gObjectEvents[i]; - if (IsGeneratedOverworldEncounter(object) && object->active && object != player + if (IsOverworldWildEncounter(object) && object->active && object != player && ((object->currentCoords.x == player->currentCoords.x && object->currentCoords.y == player->currentCoords.y) || (object->previousCoords.x == player->currentCoords.x && object->previousCoords.y == player->currentCoords.y)) && AreElevationsCompatible(object->currentElevation, player->currentElevation)) @@ -361,10 +361,10 @@ bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; bool32 forcePlayerCollision = (/* VarGet(VAR_REPEL_STEP_COUNT) > 0 || */ FlagGet(DN_FLAG_SEARCHING)); - if (collider == player && IsGeneratedOverworldEncounter(obstacle) && !forcePlayerCollision) + if (collider == player && IsOverworldWildEncounter(obstacle) && !forcePlayerCollision) return TRUE; - if (obstacle == player && IsGeneratedOverworldEncounter(collider) && !forcePlayerCollision) + if (obstacle == player && IsOverworldWildEncounter(collider) && !forcePlayerCollision) return TRUE; return FALSE; @@ -420,17 +420,19 @@ static void SortOWEMonAges(void) } } -void FollowMon_OnObjectEventSpawned(struct ObjectEvent *objectEvent) +void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { + if (!IsGeneratedOverworldWildEncounter(objectEvent)) + return; + u32 spawnSlot = GetSpawnSlotByLocalId(objectEvent->localId); - sFollowMonData.pendingSpawnAnim = spawnSlot + 1; SortOWEMonAges(); } -void OverworldEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent) +void GeneratedOverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { - if (!IsGeneratedOverworldEncounter(objectEvent)) + if (!IsGeneratedOverworldWildEncounter(objectEvent)) return; u32 spawnSlot = GetSpawnSlotByLocalId(objectEvent->localId); @@ -541,7 +543,7 @@ void RemoveAllOverworldEncounterObjects(void) for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { struct ObjectEvent *obj = &gObjectEvents[i]; - if (IsGeneratedOverworldEncounter(obj)) + if (IsGeneratedOverworldWildEncounter(obj)) RemoveObjectEvent(obj); } } @@ -615,11 +617,18 @@ bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEve return !IsInsidePlayerMap(x, y); } -bool32 IsGeneratedOverworldEncounter(struct ObjectEvent *objectEvent) +bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent) { return (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_ENCOUNTER); } +bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent) +{ + return IsOverworldWildEncounter(objectEvent) + && (objectEvent->localId <= LOCALID_OW_ENCOUNTER_END + && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - FOLLOWMON_MAX_SPAWN_SLOTS)); +} + static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) { u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); @@ -652,7 +661,7 @@ u32 GetNewestOWEncounterLocalId(void) static bool32 CanRemoveOverworldEncounter(u32 localId) { - // Can the last of these checks be replaced by IsGeneratedOverworldEncounter? + // Can the last of these checks be replaced by IsOverworldWildEncounter? // Include a check for the encounter not being shiny or a roamer. return (OW_WILD_ENCOUNTERS_OVERWORLD && CountActiveFollowMon() != 0 && (localId <= (LOCALID_OW_ENCOUNTER_END - FOLLOWMON_MAX_SPAWN_SLOTS + 1) From cdc7d1c50b04417042e7da149b1027c71660e07a Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 15:53:54 +0000 Subject: [PATCH 095/572] Remove Level from EWRAM --- include/global.fieldmap.h | 2 +- include/overworld_encounters.h | 5 ++--- src/overworld_encounters.c | 30 ++++++++++++++++-------------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index 16dd3d0df31f..25a0f218d5e5 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -238,7 +238,7 @@ struct ObjectEvent /*0x1A*/ u8 fieldEffectSpriteId; /*0x1B*/ u8 warpArrowSpriteId; /*0x1C*/ u8 movementActionId; - /*0x1D*/ u8 trainerRange_berryTreeId; //Also stores encounterIndex for Overworld Encounters + /*0x1D*/ u8 trainerRange_berryTreeId; // Also stores level for Overworld Encounters /*0x1E*/ u8 currentMetatileBehavior; /*0x1F*/ u8 previousMetatileBehavior; /*0x20*/ u8 previousMovementDirection:4; diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index e090fa6f3287..19d30172c8b0 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -15,10 +15,9 @@ struct FollowMon { - u16 level:7; u16 form:3; u16 age:4; - u16 padding:12; + u16 padding:9; u16 padding2; }; @@ -39,7 +38,7 @@ void CreateFollowMonEncounter(void); bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void GeneratedOverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); -u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale); +u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); void ClearOverworldEncounterData(void); u8 CountActiveFollowMon(); void RemoveAllOverworldEncounterObjects(void); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7c6fdb7684c3..c6079cade52d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -20,12 +20,14 @@ #include "constants/songs.h" #include "constants/vars.h" +#define sOverworldEncounterLevel trainerRange_berryTreeId + static EWRAM_DATA struct FollowMonData sFollowMonData = { 0 }; static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool8 IsSpawningWaterMons(); -static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale); +static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); static u8 CountActiveObjectEvents(); static bool8 IsSafeToSpawnObjectEvents(void); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); @@ -86,7 +88,7 @@ void UpdateOverworldEncounters(void) bool32 waterMons = IsSpawningWaterMons(); bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); - u32 movementType; + u32 movementType, level; if (OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT) // These checks need to be improved { if (waterMons) @@ -103,7 +105,7 @@ void UpdateOverworldEncounters(void) struct ObjectEventTemplate objectEventTemplate = { .localId = localId, - .graphicsId = GetFollowMonObjectEventGraphicsId(spawnSlot, x, y, &speciesId, &isShiny, &isFemale), + .graphicsId = GetFollowMonObjectEventGraphicsId(spawnSlot, x, y, &speciesId, &isShiny, &isFemale, &level), .x = x - MAP_OFFSET, .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), @@ -116,6 +118,7 @@ void UpdateOverworldEncounters(void) gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; gObjectEvents[objectEventId].range.rangeX = 8; gObjectEvents[objectEventId].range.rangeY = 8; + gObjectEvents[objectEventId].sOverworldEncounterLevel = level; // Hide reflections for spawns in water // (It just looks weird) @@ -306,8 +309,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) void CreateFollowMonEncounter(void) { - u32 localId = gSpecialVar_LastTalked; - u32 objEventId = GetObjectEventIdByLocalIdAndMap(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + u32 objEventId = GetObjectEventIdByLocalIdAndMap(gSpecialVar_LastTalked, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); struct ObjectEvent *object = &gObjectEvents[objEventId]; if (objEventId >= OBJECT_EVENTS_COUNT) @@ -316,7 +318,6 @@ void CreateFollowMonEncounter(void) if (!IsOverworldWildEncounter(object)) return; - u32 slot = GetSpawnSlotByLocalId(localId); u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; bool32 isFemale = OW_FEMALE(object) ? TRUE : FALSE; @@ -325,7 +326,7 @@ void CreateFollowMonEncounter(void) CreateMonWithGender( &gEnemyParty[0], speciesId, - sFollowMonData.list[slot].level, + object->sOverworldEncounterLevel, USE_RANDOM_IVS, OT_ID_PLAYER_ID, 0, @@ -439,9 +440,9 @@ void GeneratedOverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *ob sFollowMonData.list[spawnSlot].age = 0; } -u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale) +u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) { - SetOverworldEncounterSpeciesInfo(spawnSlot, x, y, speciesId, isShiny, isFemale); + SetOverworldEncounterSpeciesInfo(spawnSlot, x, y, speciesId, isShiny, isFemale, level); u16 graphicsId = *speciesId + OBJ_EVENT_MON; if (*isFemale) @@ -463,12 +464,12 @@ void ClearOverworldEncounterData(void) } } -static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale) +static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) { const struct WildPokemonInfo *wildMonInfo; u32 headerId = GetCurrentMapWildMonHeaderId(); u32 tileBehavior = MapGridGetMetatileBehaviorAt(x, y); - u32 timeOfDay, encounterIndex, level; + u32 timeOfDay, encounterIndex; u32 personality = Random32(); if (MetatileBehavior_IsWaterWildEncounter(tileBehavior)) @@ -477,7 +478,7 @@ static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *s timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; *speciesId = wildMonInfo->wildPokemon[encounterIndex].species; - level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_WATER); + *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_WATER); } else { @@ -485,13 +486,12 @@ static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *s timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; *speciesId = wildMonInfo->wildPokemon[encounterIndex].species; - level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_LAND); + *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_LAND); } if (*speciesId == SPECIES_UNOWN) *speciesId = GetUnownSpeciesId(personality); - sFollowMonData.list[spawnSlot].level = level; *isShiny = ComputePlayerShinyOdds(personality); if (GetGenderFromSpeciesAndPersonality(*speciesId, personality) == MON_FEMALE) *isFemale = TRUE; @@ -690,3 +690,5 @@ bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) return TRUE; } + +#undef sOverworldEncounterLevel From 6fd6fed05fe1256046e80423a80af94876c750e7 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:07:26 +0000 Subject: [PATCH 096/572] Add OW_ENCOUNTER_MOVEMENT_RANGE --- include/config/overworld.h | 2 ++ src/overworld_encounters.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 0285219d9c0c..d60876a3b286 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -76,6 +76,8 @@ #define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. #define OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will only use restricted movement types (MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER/MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER/MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER). #define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. +#define OW_ENCOUNTER_MOVEMENT_RANGE_X 8 // The horizontal movement range for OW encounter Pokémon. +#define OW_ENCOUNTER_MOVEMENT_RANGE_Y 8 // The vertical movement range for OW encounter Pokémon. // Out-of-battle Ability effects #define OW_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8+, if a Pokémon with Synchronize leads the party, wild Pokémon will always have their same Nature as opposed to the 50% chance in previous games. Gift Pokémon excluded. diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c6079cade52d..f792ca8c7842 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -116,8 +116,8 @@ void UpdateOverworldEncounters(void) u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; - gObjectEvents[objectEventId].range.rangeX = 8; - gObjectEvents[objectEventId].range.rangeY = 8; + gObjectEvents[objectEventId].range.rangeX = OW_ENCOUNTER_MOVEMENT_RANGE_X; + gObjectEvents[objectEventId].range.rangeY = OW_ENCOUNTER_MOVEMENT_RANGE_Y; gObjectEvents[objectEventId].sOverworldEncounterLevel = level; // Hide reflections for spawns in water From cd6462812bc5cd237bc372354a0b2d708269adbd Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:26:35 +0000 Subject: [PATCH 097/572] Add level santisation --- src/overworld_encounters.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f792ca8c7842..42c22b945e2d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -321,12 +321,18 @@ void CreateFollowMonEncounter(void) u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; bool32 isFemale = OW_FEMALE(object) ? TRUE : FALSE; + u32 level = object->sOverworldEncounterLevel; + + if (level > MAX_LEVEL) + level = MAX_LEVEL; + else if ( level < MIN_LEVEL) + level = MIN_LEVEL; ZeroEnemyPartyMons(); CreateMonWithGender( &gEnemyParty[0], speciesId, - object->sOverworldEncounterLevel, + level, USE_RANDOM_IVS, OT_ID_PLAYER_ID, 0, From cf548e749e190935601cbecbb1be34429cf27dd6 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:57:29 +0000 Subject: [PATCH 098/572] Add ShouldRunOverworldEncounterScript --- include/overworld_encounters.h | 2 ++ src/field_control_avatar.c | 2 +- src/overworld_encounters.c | 13 +++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 19d30172c8b0..3009b57bc8bb 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -45,7 +45,9 @@ void RemoveAllOverworldEncounterObjects(void); bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent); +bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent); u32 GetNewestOWEncounterLocalId(void); bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); +bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 2750be77a83a..2102cb9f71fb 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -400,7 +400,7 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; gSpecialVar_Facing = direction; - if (IsOverworldWildEncounter(&gObjectEvents[objectEventId]) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL) + if (ShouldRunOverworldEncounterScript(objectEventId)) script = InteractWithDynamicWildFollowMon; else if (InTrainerHill() == TRUE) script = GetTrainerHillTrainerScript(); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 42c22b945e2d..126e8e8a2925 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -635,6 +635,13 @@ bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent) && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - FOLLOWMON_MAX_SPAWN_SLOTS)); } +bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent) +{ + return IsOverworldWildEncounter(objectEvent) + && (objectEvent->localId > LOCALID_OW_ENCOUNTER_END + || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - FOLLOWMON_MAX_SPAWN_SLOTS)); +} + static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) { u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); @@ -697,4 +704,10 @@ bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) return TRUE; } +bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) +{ + return IsGeneratedOverworldWildEncounter(&gObjectEvents[objectEventId]) + || (IsManualOverworldWildEncounter(&gObjectEvents[objectEventId]) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL); +} + #undef sOverworldEncounterLevel From 44e065d2b098daaeca920781d7b6d19af4b3fe8f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:05:45 +0000 Subject: [PATCH 099/572] Revert addition of TryAndRemoveOldestOverworldEncounter --- include/overworld_encounters.h | 4 +++- src/event_object_movement.c | 29 ++++++++++++++++++++++++++--- src/overworld_encounters.c | 16 +++++++--------- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 3009b57bc8bb..1de8d56704a8 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -47,7 +47,9 @@ bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent); u32 GetNewestOWEncounterLocalId(void); -bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); +bool32 CanRemoveOverworldEncounter(u32 localId); +void RemoveOldestOverworldEncounter(void); +bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 4ac6eb3a20cc..61b350da7164 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1529,9 +1529,9 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * return TRUE; } - // If possible, destroy the oldest OW Encounter mon to make room for the new object. - if (i >= OBJECT_EVENTS_COUNT) - return TryAndRemoveOldestOverworldEncounter(localId, objectEventId); + // Only if a generated Overworld Encounter cannot be removed. + if (i >= OBJECT_EVENTS_COUNT && !CanRemoveOverworldEncounter(localId)) + return TRUE; *objectEventId = i; for (; i < OBJECT_EVENTS_COUNT; i++) @@ -1540,6 +1540,29 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * return TRUE; } + // Destroy the oldest OW Encounter mon to make room for the new object. + if (*objectEventId >= OBJECT_EVENTS_COUNT && CanRemoveOverworldEncounter(localId)) + RemoveOldestOverworldEncounter(); + + /* Can we integrate this with this line above: + if (i >= OBJECT_EVENTS_COUNT && !CanRemoveOverworldEncounter(localId)) + + Something like: + if (i >= OBJECT_EVENTS_COUNT) + return TryAndRemoveOldestOverworldEncounter(localId); + + Where: + bool32 TryAndRemoveOldestOverworldEncounter(u32 localId) + { + if (CanRemoveOverworldEncounter(localId)) + { + RemoveOldestOverworldEncounter(); + return FALSE; + } + return TRUE; + } + */ + return FALSE; } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 126e8e8a2925..d80bbfb03632 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -39,8 +39,6 @@ static void OverworldEncounters_ProcessMonInteraction(void); static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); static u32 GetSpawnSlotByLocalId(u32 localId); -static bool32 CanRemoveOverworldEncounter(u32 localId); -static void RemoveOldestOverworldEncounter(u8 *objectEventId); static void SortOWEMonAges(void); void LoadFollowMonData(void) @@ -672,7 +670,7 @@ u32 GetNewestOWEncounterLocalId(void) return newestSlot; } -static bool32 CanRemoveOverworldEncounter(u32 localId) +bool32 CanRemoveOverworldEncounter(u32 localId) { // Can the last of these checks be replaced by IsOverworldWildEncounter? // Include a check for the encounter not being shiny or a roamer. @@ -681,23 +679,23 @@ static bool32 CanRemoveOverworldEncounter(u32 localId) || localId > LOCALID_OW_ENCOUNTER_END)); } -static void RemoveOldestOverworldEncounter(u8 *objectEventId) +void RemoveOldestOverworldEncounter(void) { - *objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(GetOldestSlot())); - s16 *fldEffSpriteId = &gSprites[gObjectEvents[*objectEventId].spriteId].data[6]; + u32 objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(GetOldestSlot())); + s16 *fldEffSpriteId = &gSprites[gObjectEvents[objectEventId].spriteId].data[6]; // Stop the associated field effect if it is active. if (*fldEffSpriteId != 0) FieldEffectStop(&gSprites[*fldEffSpriteId - 1], FLDEFF_BUBBLES); - RemoveObjectEvent(&gObjectEvents[*objectEventId]); + RemoveObjectEvent(&gObjectEvents[objectEventId]); } -bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) +bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) { if (CanRemoveOverworldEncounter(localId)) { - RemoveOldestOverworldEncounter(objectEventId); + // RemoveOldestOverworldEncounter(objectEventId); return FALSE; } From 5f0e678c64e092bd81196063f737e820ec2758bc Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:12:20 +0000 Subject: [PATCH 100/572] Set Pointer in RemoveOldestOverworldEncounter --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 1de8d56704a8..c1d592890a76 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -49,7 +49,7 @@ bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent); u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); -void RemoveOldestOverworldEncounter(void); +void RemoveOldestOverworldEncounter(u8 *objectEventId); bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 61b350da7164..41c81f6da68d 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1542,7 +1542,7 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * // Destroy the oldest OW Encounter mon to make room for the new object. if (*objectEventId >= OBJECT_EVENTS_COUNT && CanRemoveOverworldEncounter(localId)) - RemoveOldestOverworldEncounter(); + RemoveOldestOverworldEncounter(objectEventId); /* Can we integrate this with this line above: if (i >= OBJECT_EVENTS_COUNT && !CanRemoveOverworldEncounter(localId)) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d80bbfb03632..d76a662e9cca 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -679,23 +679,23 @@ bool32 CanRemoveOverworldEncounter(u32 localId) || localId > LOCALID_OW_ENCOUNTER_END)); } -void RemoveOldestOverworldEncounter(void) +void RemoveOldestOverworldEncounter(u8 *objectEventId) { - u32 objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(GetOldestSlot())); - s16 *fldEffSpriteId = &gSprites[gObjectEvents[objectEventId].spriteId].data[6]; + *objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(GetOldestSlot())); + s16 *fldEffSpriteId = &gSprites[gObjectEvents[*objectEventId].spriteId].data[6]; // Stop the associated field effect if it is active. if (*fldEffSpriteId != 0) FieldEffectStop(&gSprites[*fldEffSpriteId - 1], FLDEFF_BUBBLES); - RemoveObjectEvent(&gObjectEvents[objectEventId]); + RemoveObjectEvent(&gObjectEvents[*objectEventId]); } bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) { if (CanRemoveOverworldEncounter(localId)) { - // RemoveOldestOverworldEncounter(objectEventId); + RemoveOldestOverworldEncounter(objectEventId); return FALSE; } From f007be55f41a18d8f0e129a4295ffd7bc0ad195e Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 1 Dec 2025 15:10:09 -0600 Subject: [PATCH 101/572] Allow safari battles --- data/scripts/followmon.inc | 4 +--- src/overworld_encounters.c | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/scripts/followmon.inc b/data/scripts/followmon.inc index 7ead8510dba8..4373c3405e46 100644 --- a/data/scripts/followmon.inc +++ b/data/scripts/followmon.inc @@ -1,8 +1,6 @@ InteractWithDynamicWildFollowMon:: lockall special CreateFollowMonEncounter - removeobject VAR_LAST_TALKED - dowildbattle - releaseall + waitstate end diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d76a662e9cca..88a1608924d0 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -337,6 +337,8 @@ void CreateFollowMonEncounter(void) isFemale ); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); + RemoveObjectEvent(object); + BattleSetup_StartWildBattle(); } static void OverworldEncounters_ProcessMonInteraction(void) From 7831876c05a2d594a94d7ae6f91c7cd8919dd46d Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 1 Dec 2025 15:35:36 -0600 Subject: [PATCH 102/572] Reset slot age before object is removed --- src/event_object_movement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 41c81f6da68d..e58620057f12 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1569,10 +1569,10 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * void RemoveObjectEvent(struct ObjectEvent *objectEvent) { objectEvent->active = FALSE; + GeneratedOverworldWildEncounter_OnObjectEventRemoved(objectEvent); RemoveObjectEventInternal(objectEvent); // zero potential species info objectEvent->graphicsId = objectEvent->shiny = 0; - GeneratedOverworldWildEncounter_OnObjectEventRemoved(objectEvent); } void RemoveObjectEventByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup) From 47cd428f2019f072fbfdc21cf2a6034ee8ed7153 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 21:52:53 +0000 Subject: [PATCH 103/572] Start to Add Variable Object Events as Overworld Encounters --- data/scripts/followmon.inc | 2 +- data/specials.inc | 2 +- include/constants/wild_encounter.h | 8 +++ include/overworld_encounters.h | 5 +- include/wild_encounter.h | 8 --- src/event_object_movement.c | 2 +- src/overworld.c | 2 + src/overworld_encounters.c | 89 +++++++++++++++++++++++++++++- 8 files changed, 103 insertions(+), 15 deletions(-) diff --git a/data/scripts/followmon.inc b/data/scripts/followmon.inc index 4373c3405e46..4ea4f9cfbea6 100644 --- a/data/scripts/followmon.inc +++ b/data/scripts/followmon.inc @@ -1,6 +1,6 @@ InteractWithDynamicWildFollowMon:: lockall - special CreateFollowMonEncounter + special CreateOverworldWildEncounter waitstate end diff --git a/data/specials.inc b/data/specials.inc index b21e727676e7..644bc2ec58ab 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -567,4 +567,4 @@ gSpecials:: def_special SetAbility def_special ObjectEventInteractionGetApricornTreeData def_special ObjectEventInteractionPickApricornTree - def_special CreateFollowMonEncounter + def_special CreateOverworldWildEncounter diff --git a/include/constants/wild_encounter.h b/include/constants/wild_encounter.h index 364dc7b35a22..d2c001f56a84 100644 --- a/include/constants/wild_encounter.h +++ b/include/constants/wild_encounter.h @@ -9,4 +9,12 @@ #define NUM_ALTERING_CAVE_TABLES 9 +enum WildPokemonArea { + WILD_AREA_LAND, + WILD_AREA_WATER, + WILD_AREA_ROCKS, + WILD_AREA_FISHING, + WILD_AREA_HIDDEN +}; + #endif // GUARD_CONSTANTS_WILD_ENCOUNTER_H diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index c1d592890a76..21ff86657cbf 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -34,10 +34,10 @@ extern const u8 InteractWithDynamicWildFollowMon[]; void LoadFollowMonData(void); void UpdateOverworldEncounters(void); u32 GetOldestSlot(void); -void CreateFollowMonEncounter(void); +void CreateOverworldWildEncounter(void); bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); -void GeneratedOverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); +void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); void ClearOverworldEncounterData(void); u8 CountActiveFollowMon(); @@ -51,5 +51,6 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); void RemoveOldestOverworldEncounter(u8 *objectEventId); bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); +void SetAllObjectAsWildEncounter(void); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 02fb69c36aaa..dcf90452d81b 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -7,14 +7,6 @@ #define HEADER_NONE 0xFFFF -enum WildPokemonArea { - WILD_AREA_LAND, - WILD_AREA_WATER, - WILD_AREA_ROCKS, - WILD_AREA_FISHING, - WILD_AREA_HIDDEN -}; - struct WildPokemon { u8 minLevel; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index e58620057f12..0b56d971bee2 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1569,7 +1569,7 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * void RemoveObjectEvent(struct ObjectEvent *objectEvent) { objectEvent->active = FALSE; - GeneratedOverworldWildEncounter_OnObjectEventRemoved(objectEvent); + OverworldWildEncounter_OnObjectEventRemoved(objectEvent); RemoveObjectEventInternal(objectEvent); // zero potential species info objectEvent->graphicsId = objectEvent->shiny = 0; diff --git a/src/overworld.c b/src/overworld.c index e9c984519cf0..b8db9e152aa1 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -869,6 +869,7 @@ if (I_VS_SEEKER_CHARGING != 0) ChooseAmbientCrySpecies(); SetDefaultFlashLevel(); Overworld_ClearSavedMusic(); + SetAllObjectAsWildEncounter(); RunOnTransitionMapScript(); InitMap(); CopySecondaryTilesetToVramUsingHeap(gMapHeader.mapLayout); @@ -938,6 +939,7 @@ if (I_VS_SEEKER_CHARGING != 0) FlagClear(FLAG_SYS_USE_FLASH); SetDefaultFlashLevel(); Overworld_ClearSavedMusic(); + SetAllObjectAsWildEncounter(); RunOnTransitionMapScript(); UpdateLocationHistoryForRoamer(); MoveAllRoamersToOtherLocationSets(); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 88a1608924d0..40451646d0f0 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -19,6 +19,7 @@ #include "constants/trainer_types.h" #include "constants/songs.h" #include "constants/vars.h" +#include "constants/wild_encounter.h" #define sOverworldEncounterLevel trainerRange_berryTreeId @@ -305,7 +306,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) return FALSE; } -void CreateFollowMonEncounter(void) +void CreateOverworldWildEncounter(void) { u32 objEventId = GetObjectEventIdByLocalIdAndMap(gSpecialVar_LastTalked, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); struct ObjectEvent *object = &gObjectEvents[objEventId]; @@ -437,8 +438,11 @@ void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *ob SortOWEMonAges(); } -void GeneratedOverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent) +void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { + if (IsManualOverworldWildEncounter(objectEvent)) + FlagSet(GetObjectEventFlagIdByLocalIdAndMap(GetObjectEventIdByLocalId(objectEvent->localId), objectEvent->mapNum, objectEvent->mapGroup)); + if (!IsGeneratedOverworldWildEncounter(objectEvent)) return; @@ -710,4 +714,85 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) || (IsManualOverworldWildEncounter(&gObjectEvents[objectEventId]) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL); } +void SetObjectAsWildEncounter(struct ObjectEvent *objectEvent) +{ + // u32 localId = VarGet(ScriptReadHalfword(ctx)); + // enum WildPokemonArea encounterType = ScriptReadByte(ctx); + + // u32 objectEventId = GetObjectEventIdByLocalId(localId); + // struct ObjectEvent *objectEvent = &gObjectEvents[objectEventId]; + enum WildPokemonArea encounterType = WILD_AREA_LAND; + u32 graphicsId = objectEvent->graphicsId; + // DebugPrintf("%d, %d, %d", localId, objectEventId, graphicsId); + u32 variableOffset = (graphicsId >= OBJ_EVENT_GFX_VAR_FIRST) ? graphicsId - OBJ_EVENT_GFX_VAR_FIRST : 0; + u32 objectEventVariable = VAR_OBJ_GFX_ID_0 + variableOffset; + u32 headerId = GetCurrentMapWildMonHeaderId(); + + if (graphicsId < OBJ_EVENT_GFX_VAR_FIRST || graphicsId > OBJ_EVENT_GFX_VAR_LAST || headerId == HEADER_NONE) + return; + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(objectEventVariable); + + enum TimeOfDay timeOfDay = GetTimeOfDayForEncounters(headerId, encounterType); + const struct WildEncounterTypes wildEncounterType = gWildMonHeaders[headerId].encounterTypes[timeOfDay]; + const struct WildPokemonInfo *wildMonInfo; + u32 encounterIndex; + u16 speciesId; + u32 level; + u32 personality = Random32(); + bool32 isFemale = FALSE; + bool32 isShiny = FALSE; + + switch (encounterType) + { + default: + case WILD_AREA_LAND: + encounterIndex = ChooseWildMonIndex_Land(); + wildMonInfo = wildEncounterType.landMonsInfo; + speciesId = wildMonInfo->wildPokemon[encounterIndex].species; + level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_LAND); + break; + + case WILD_AREA_WATER: + encounterIndex = ChooseWildMonIndex_Water(); + wildMonInfo = wildEncounterType.waterMonsInfo; + speciesId = wildMonInfo->wildPokemon[encounterIndex].species; + level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_WATER); + break; + } + + if (speciesId == SPECIES_UNOWN) + speciesId = GetUnownSpeciesId(personality); + + isShiny = ComputePlayerShinyOdds(personality); + if (GetGenderFromSpeciesAndPersonality(speciesId, personality) == MON_FEMALE) + isFemale = TRUE; + else + isFemale = FALSE; + + graphicsId = speciesId + OBJ_EVENT_MON; + if (isFemale) + graphicsId += OBJ_EVENT_MON_FEMALE; + if (isShiny) + graphicsId += OBJ_EVENT_MON_SHINY; + + VarSet(objectEventVariable, graphicsId); + objectEvent->trainerType = TRAINER_TYPE_ENCOUNTER; + objectEvent->sOverworldEncounterLevel = level; +} + +void SetAllObjectAsWildEncounter(void) +{ + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) + { + struct ObjectEvent *objectEvent = &gObjectEvents[i]; + if (objectEvent->graphicsId >= OBJ_EVENT_GFX_VARS && objectEvent->graphicsId <= OBJ_EVENT_GFX_VAR_F + && IsManualOverworldWildEncounter(objectEvent)) + { + SetObjectAsWildEncounter(objectEvent); + } + } +} + #undef sOverworldEncounterLevel From 95c0186ee3235892ff6dfb8e076292e90b533f91 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:11:38 +0000 Subject: [PATCH 104/572] Add OBJ_EVENT_GFX_OWES --- include/constants/event_objects.h | 22 ++++++++++++++++-- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 6 +++++ src/overworld.c | 2 -- src/overworld_encounters.c | 37 +++++-------------------------- 5 files changed, 32 insertions(+), 37 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 3c8cb179b4d5..3d3c299118c7 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -274,9 +274,27 @@ #define OBJ_EVENT_GFX_VAR_D (OBJ_EVENT_GFX_VARS + 0xD) #define OBJ_EVENT_GFX_VAR_E (OBJ_EVENT_GFX_VARS + 0xE) #define OBJ_EVENT_GFX_VAR_F (OBJ_EVENT_GFX_VARS + 0xF) +#define OBJ_EVENT_GFX_VARS_END OBJ_EVENT_GFX_VAR_F + +#define OBJ_EVENT_GFX_OWES (OBJ_EVENT_GFX_VARS_END + 1) +#define OBJ_EVENT_GFX_OWE_0 (OBJ_EVENT_GFX_OWES + 0x0) +#define OBJ_EVENT_GFX_OWE_1 (OBJ_EVENT_GFX_OWES + 0x1) +#define OBJ_EVENT_GFX_OWE_2 (OBJ_EVENT_GFX_OWES + 0x2) +#define OBJ_EVENT_GFX_OWE_3 (OBJ_EVENT_GFX_OWES + 0x3) +#define OBJ_EVENT_GFX_OWE_4 (OBJ_EVENT_GFX_OWES + 0x4) +#define OBJ_EVENT_GFX_OWE_5 (OBJ_EVENT_GFX_OWES + 0x5) +#define OBJ_EVENT_GFX_OWE_6 (OBJ_EVENT_GFX_OWES + 0x6) +#define OBJ_EVENT_GFX_OWE_7 (OBJ_EVENT_GFX_OWES + 0x7) +#define OBJ_EVENT_GFX_OWE_8 (OBJ_EVENT_GFX_OWES + 0x8) +#define OBJ_EVENT_GFX_OWE_9 (OBJ_EVENT_GFX_OWES + 0x9) +#define OBJ_EVENT_GFX_OWE_A (OBJ_EVENT_GFX_OWES + 0xA) +#define OBJ_EVENT_GFX_OWE_B (OBJ_EVENT_GFX_OWES + 0xB) +#define OBJ_EVENT_GFX_OWE_C (OBJ_EVENT_GFX_OWES + 0xC) +#define OBJ_EVENT_GFX_OWE_D (OBJ_EVENT_GFX_OWES + 0xD) +#define OBJ_EVENT_GFX_OWE_E (OBJ_EVENT_GFX_OWES + 0xE) +#define OBJ_EVENT_GFX_OWE_F (OBJ_EVENT_GFX_OWES + 0xF) +#define OBJ_EVENT_GFX_OWES_END OBJ_EVENT_GFX_OWE_F -#define OBJ_EVENT_GFX_VAR_FIRST OBJ_EVENT_GFX_VAR_0 -#define OBJ_EVENT_GFX_VAR_LAST OBJ_EVENT_GFX_VAR_F // Don't use (1u << 15) to avoid conflict with BLEND_IMMUNE_FLAG. #define OBJ_EVENT_MON (1u << 14) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 21ff86657cbf..a0a8c1cacefe 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -51,6 +51,6 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); void RemoveOldestOverworldEncounter(u8 *objectEventId); bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); -void SetAllObjectAsWildEncounter(void); +u16 GetGraphicsIdForOverworldEncounterGfx(void); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 0b56d971bee2..b2a4152b86d3 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -3108,6 +3108,9 @@ const struct ObjectEventGraphicsInfo *GetObjectEventGraphicsInfo(u16 graphicsId) if (graphicsId >= OBJ_EVENT_GFX_VARS && graphicsId <= OBJ_EVENT_GFX_VAR_F) graphicsId = VarGetObjectEventGraphicsId(graphicsId - OBJ_EVENT_GFX_VARS); + if (graphicsId >= OBJ_EVENT_GFX_OWES && graphicsId <= OBJ_EVENT_GFX_OWES_END) + graphicsId = GetGraphicsIdForOverworldEncounterGfx(); + if (graphicsId == OBJ_EVENT_GFX_BARD) return gMauvilleOldManGraphicsInfoPointers[GetCurrentMauvilleOldMan()]; @@ -3124,6 +3127,9 @@ static void SetObjectEventDynamicGraphicsId(struct ObjectEvent *objectEvent) { if (objectEvent->graphicsId >= OBJ_EVENT_GFX_VARS && objectEvent->graphicsId <= OBJ_EVENT_GFX_VAR_F) objectEvent->graphicsId = VarGetObjectEventGraphicsId(objectEvent->graphicsId - OBJ_EVENT_GFX_VARS); + + if (objectEvent->graphicsId >= OBJ_EVENT_GFX_OWES && objectEvent->graphicsId <= OBJ_EVENT_GFX_OWES_END) + objectEvent->graphicsId = GetGraphicsIdForOverworldEncounterGfx(); } void SetObjectInvisibility(u8 localId, u8 mapNum, u8 mapGroup, bool8 invisible) diff --git a/src/overworld.c b/src/overworld.c index b8db9e152aa1..e9c984519cf0 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -869,7 +869,6 @@ if (I_VS_SEEKER_CHARGING != 0) ChooseAmbientCrySpecies(); SetDefaultFlashLevel(); Overworld_ClearSavedMusic(); - SetAllObjectAsWildEncounter(); RunOnTransitionMapScript(); InitMap(); CopySecondaryTilesetToVramUsingHeap(gMapHeader.mapLayout); @@ -939,7 +938,6 @@ if (I_VS_SEEKER_CHARGING != 0) FlagClear(FLAG_SYS_USE_FLASH); SetDefaultFlashLevel(); Overworld_ClearSavedMusic(); - SetAllObjectAsWildEncounter(); RunOnTransitionMapScript(); UpdateLocationHistoryForRoamer(); MoveAllRoamersToOtherLocationSets(); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 40451646d0f0..083d13a10419 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -714,26 +714,12 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) || (IsManualOverworldWildEncounter(&gObjectEvents[objectEventId]) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL); } -void SetObjectAsWildEncounter(struct ObjectEvent *objectEvent) +u16 GetGraphicsIdForOverworldEncounterGfx(void) { - // u32 localId = VarGet(ScriptReadHalfword(ctx)); - // enum WildPokemonArea encounterType = ScriptReadByte(ctx); - - // u32 objectEventId = GetObjectEventIdByLocalId(localId); - // struct ObjectEvent *objectEvent = &gObjectEvents[objectEventId]; enum WildPokemonArea encounterType = WILD_AREA_LAND; - u32 graphicsId = objectEvent->graphicsId; - // DebugPrintf("%d, %d, %d", localId, objectEventId, graphicsId); - u32 variableOffset = (graphicsId >= OBJ_EVENT_GFX_VAR_FIRST) ? graphicsId - OBJ_EVENT_GFX_VAR_FIRST : 0; - u32 objectEventVariable = VAR_OBJ_GFX_ID_0 + variableOffset; + u32 graphicsId; u32 headerId = GetCurrentMapWildMonHeaderId(); - if (graphicsId < OBJ_EVENT_GFX_VAR_FIRST || graphicsId > OBJ_EVENT_GFX_VAR_LAST || headerId == HEADER_NONE) - return; - - Script_RequestEffects(SCREFF_V1); - Script_RequestWriteVar(objectEventVariable); - enum TimeOfDay timeOfDay = GetTimeOfDayForEncounters(headerId, encounterType); const struct WildEncounterTypes wildEncounterType = gWildMonHeaders[headerId].encounterTypes[timeOfDay]; const struct WildPokemonInfo *wildMonInfo; @@ -777,22 +763,9 @@ void SetObjectAsWildEncounter(struct ObjectEvent *objectEvent) if (isShiny) graphicsId += OBJ_EVENT_MON_SHINY; - VarSet(objectEventVariable, graphicsId); - objectEvent->trainerType = TRAINER_TYPE_ENCOUNTER; - objectEvent->sOverworldEncounterLevel = level; -} - -void SetAllObjectAsWildEncounter(void) -{ - for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) - { - struct ObjectEvent *objectEvent = &gObjectEvents[i]; - if (objectEvent->graphicsId >= OBJ_EVENT_GFX_VARS && objectEvent->graphicsId <= OBJ_EVENT_GFX_VAR_F - && IsManualOverworldWildEncounter(objectEvent)) - { - SetObjectAsWildEncounter(objectEvent); - } - } + // objectEvent->trainerType = TRAINER_TYPE_ENCOUNTER; + // objectEvent->sOverworldEncounterLevel = level; + return graphicsId; } #undef sOverworldEncounterLevel From 29fa31139d609509937d74dd00ef08b251d3672b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:17:24 +0000 Subject: [PATCH 105/572] Put back enum WildPokemonArea --- include/constants/wild_encounter.h | 8 -------- include/wild_encounter.h | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/constants/wild_encounter.h b/include/constants/wild_encounter.h index d2c001f56a84..364dc7b35a22 100644 --- a/include/constants/wild_encounter.h +++ b/include/constants/wild_encounter.h @@ -9,12 +9,4 @@ #define NUM_ALTERING_CAVE_TABLES 9 -enum WildPokemonArea { - WILD_AREA_LAND, - WILD_AREA_WATER, - WILD_AREA_ROCKS, - WILD_AREA_FISHING, - WILD_AREA_HIDDEN -}; - #endif // GUARD_CONSTANTS_WILD_ENCOUNTER_H diff --git a/include/wild_encounter.h b/include/wild_encounter.h index dcf90452d81b..14fbe5c1350a 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -5,6 +5,14 @@ #include "constants/wild_encounter.h" #include "overworld_encounters.h" +enum WildPokemonArea { + WILD_AREA_LAND, + WILD_AREA_WATER, + WILD_AREA_ROCKS, + WILD_AREA_FISHING, + WILD_AREA_HIDDEN +}; + #define HEADER_NONE 0xFFFF struct WildPokemon From 4f00e3456e761feb19167eb0aa5e90e35bec106a Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:17:50 +0000 Subject: [PATCH 106/572] Update wild_encounter.h --- include/wild_encounter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 14fbe5c1350a..02fb69c36aaa 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -5,6 +5,8 @@ #include "constants/wild_encounter.h" #include "overworld_encounters.h" +#define HEADER_NONE 0xFFFF + enum WildPokemonArea { WILD_AREA_LAND, WILD_AREA_WATER, @@ -13,8 +15,6 @@ enum WildPokemonArea { WILD_AREA_HIDDEN }; -#define HEADER_NONE 0xFFFF - struct WildPokemon { u8 minLevel; From 9a77bc2ccd92b58637ccba872be6712f26078431 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:35:52 +0000 Subject: [PATCH 107/572] Fix Flag Not Being Set --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 083d13a10419..ab4b537418de 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -441,7 +441,7 @@ void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *ob void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { if (IsManualOverworldWildEncounter(objectEvent)) - FlagSet(GetObjectEventFlagIdByLocalIdAndMap(GetObjectEventIdByLocalId(objectEvent->localId), objectEvent->mapNum, objectEvent->mapGroup)); + FlagSet(GetObjectEventFlagIdByLocalIdAndMap(objectEvent->localId, objectEvent->mapNum, objectEvent->mapGroup)); if (!IsGeneratedOverworldWildEncounter(objectEvent)) return; From 5f1eb1336fab8e0853b94e84646383697cb1f96a Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:44:42 +0000 Subject: [PATCH 108/572] Use Just One OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER --- include/constants/event_objects.h | 21 +-------------------- src/event_object_movement.c | 4 ++-- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 3d3c299118c7..4148d056a328 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -274,26 +274,7 @@ #define OBJ_EVENT_GFX_VAR_D (OBJ_EVENT_GFX_VARS + 0xD) #define OBJ_EVENT_GFX_VAR_E (OBJ_EVENT_GFX_VARS + 0xE) #define OBJ_EVENT_GFX_VAR_F (OBJ_EVENT_GFX_VARS + 0xF) -#define OBJ_EVENT_GFX_VARS_END OBJ_EVENT_GFX_VAR_F - -#define OBJ_EVENT_GFX_OWES (OBJ_EVENT_GFX_VARS_END + 1) -#define OBJ_EVENT_GFX_OWE_0 (OBJ_EVENT_GFX_OWES + 0x0) -#define OBJ_EVENT_GFX_OWE_1 (OBJ_EVENT_GFX_OWES + 0x1) -#define OBJ_EVENT_GFX_OWE_2 (OBJ_EVENT_GFX_OWES + 0x2) -#define OBJ_EVENT_GFX_OWE_3 (OBJ_EVENT_GFX_OWES + 0x3) -#define OBJ_EVENT_GFX_OWE_4 (OBJ_EVENT_GFX_OWES + 0x4) -#define OBJ_EVENT_GFX_OWE_5 (OBJ_EVENT_GFX_OWES + 0x5) -#define OBJ_EVENT_GFX_OWE_6 (OBJ_EVENT_GFX_OWES + 0x6) -#define OBJ_EVENT_GFX_OWE_7 (OBJ_EVENT_GFX_OWES + 0x7) -#define OBJ_EVENT_GFX_OWE_8 (OBJ_EVENT_GFX_OWES + 0x8) -#define OBJ_EVENT_GFX_OWE_9 (OBJ_EVENT_GFX_OWES + 0x9) -#define OBJ_EVENT_GFX_OWE_A (OBJ_EVENT_GFX_OWES + 0xA) -#define OBJ_EVENT_GFX_OWE_B (OBJ_EVENT_GFX_OWES + 0xB) -#define OBJ_EVENT_GFX_OWE_C (OBJ_EVENT_GFX_OWES + 0xC) -#define OBJ_EVENT_GFX_OWE_D (OBJ_EVENT_GFX_OWES + 0xD) -#define OBJ_EVENT_GFX_OWE_E (OBJ_EVENT_GFX_OWES + 0xE) -#define OBJ_EVENT_GFX_OWE_F (OBJ_EVENT_GFX_OWES + 0xF) -#define OBJ_EVENT_GFX_OWES_END OBJ_EVENT_GFX_OWE_F +#define OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER (OBJ_EVENT_GFX_VARS + 0x10) // Don't use (1u << 15) to avoid conflict with BLEND_IMMUNE_FLAG. diff --git a/src/event_object_movement.c b/src/event_object_movement.c index b2a4152b86d3..601411b1bad3 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -3108,7 +3108,7 @@ const struct ObjectEventGraphicsInfo *GetObjectEventGraphicsInfo(u16 graphicsId) if (graphicsId >= OBJ_EVENT_GFX_VARS && graphicsId <= OBJ_EVENT_GFX_VAR_F) graphicsId = VarGetObjectEventGraphicsId(graphicsId - OBJ_EVENT_GFX_VARS); - if (graphicsId >= OBJ_EVENT_GFX_OWES && graphicsId <= OBJ_EVENT_GFX_OWES_END) + if (graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER) graphicsId = GetGraphicsIdForOverworldEncounterGfx(); if (graphicsId == OBJ_EVENT_GFX_BARD) @@ -3128,7 +3128,7 @@ static void SetObjectEventDynamicGraphicsId(struct ObjectEvent *objectEvent) if (objectEvent->graphicsId >= OBJ_EVENT_GFX_VARS && objectEvent->graphicsId <= OBJ_EVENT_GFX_VAR_F) objectEvent->graphicsId = VarGetObjectEventGraphicsId(objectEvent->graphicsId - OBJ_EVENT_GFX_VARS); - if (objectEvent->graphicsId >= OBJ_EVENT_GFX_OWES && objectEvent->graphicsId <= OBJ_EVENT_GFX_OWES_END) + if (objectEvent->graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER) objectEvent->graphicsId = GetGraphicsIdForOverworldEncounterGfx(); } From 5b08cb1c0b557042e5bc2eb6b1a8262a33319e0e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:51:04 +0000 Subject: [PATCH 109/572] Comments --- src/overworld_encounters.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index ab4b537418de..9c10a7679753 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -716,6 +716,8 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) u16 GetGraphicsIdForOverworldEncounterGfx(void) { + // encounterType is hardcoded to land encounters for now + // can level be set or does it have to be hardcorded too? enum WildPokemonArea encounterType = WILD_AREA_LAND; u32 graphicsId; u32 headerId = GetCurrentMapWildMonHeaderId(); From 4eeacfa476cddd987003a01f5a43f3d68b71cb24 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:58:44 +0000 Subject: [PATCH 110/572] Add Collision to Overworld Encounters when OW_WILD_ENCOUNTERS_OVERWORLD is FALSE --- src/overworld_encounters.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 9c10a7679753..c154a2388bba 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -364,6 +364,9 @@ static void OverworldEncounters_ProcessMonInteraction(void) bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider) { + if (!OW_WILD_ENCOUNTERS_OVERWORLD) + return FALSE; + // The player only is exempt from collisions with OW Encounters when not using a repel or the DexNav is inactive. struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; From 7304bf3e17df3cc9159f105e297c49f03b5d60cc Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:25:37 -0600 Subject: [PATCH 111/572] Removed pendingSpawnAnim --- include/overworld_encounters.h | 1 - src/overworld_encounters.c | 56 +++++++++++++++------------------- 2 files changed, 24 insertions(+), 33 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index c1d592890a76..f1eabbf63b7d 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -25,7 +25,6 @@ struct FollowMonData { struct FollowMon list[FOLLOWMON_MAX_SPAWN_SLOTS]; u16 spawnCountdown; - u8 pendingSpawnAnim; }; //data/scripts/followmon.inc diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 88a1608924d0..c6cb69197368 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -125,6 +125,30 @@ void UpdateOverworldEncounters(void) // Slower replacement spawning sFollowMonData.spawnCountdown = 60 * (3 + Random() % 2); + + enum FollowMonSpawnAnim spawnAnimType; + + // Play spawn animation. + if (speciesId != SPECIES_NONE) + { + if (isShiny) + { + PlaySE(SE_SHINY); + spawnAnimType = FOLLOWMON_SPAWN_ANIM_SHINY; + } + else + { + PlayCry_Normal(speciesId, 25); + if (IsSpawningWaterMons()) + spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; + else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) + spawnAnimType = FOLLOWMON_SPAWN_ANIM_CAVE; + else + spawnAnimType = FOLLOWMON_SPAWN_ANIM_GRASS; + } + // Instantly play a small animation to ground the spawning a bit + MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objectEventId]); + } } } } @@ -132,36 +156,6 @@ void UpdateOverworldEncounters(void) { --sFollowMonData.spawnCountdown; } - - // Play spawn animation when player is close enough - if(sFollowMonData.pendingSpawnAnim != 0) - { - u32 spawnSlot = sFollowMonData.pendingSpawnAnim - 1; - u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); - enum FollowMonSpawnAnim spawnAnimType; - - if (speciesId != SPECIES_NONE) - { - if (isShiny) - { - PlaySE(SE_SHINY); - spawnAnimType = FOLLOWMON_SPAWN_ANIM_SHINY; - } - else - { - PlayCry_Normal(speciesId, 25); - if (IsSpawningWaterMons()) - spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; - else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) - spawnAnimType = FOLLOWMON_SPAWN_ANIM_CAVE; - else - spawnAnimType = FOLLOWMON_SPAWN_ANIM_GRASS; - } - // Instantly play a small animation to ground the spawning a bit - MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objEventId]); - sFollowMonData.pendingSpawnAnim = 0; - } - } } static u8 GetMaxFollowMonSpawns(void) @@ -432,8 +426,6 @@ void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *ob if (!IsGeneratedOverworldWildEncounter(objectEvent)) return; - u32 spawnSlot = GetSpawnSlotByLocalId(objectEvent->localId); - sFollowMonData.pendingSpawnAnim = spawnSlot + 1; SortOWEMonAges(); } From 64bc82809d3ae586bc9bff7ce3f236537f78546b Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:27:27 -0600 Subject: [PATCH 112/572] Removed unused function CountActiveObjectEvents --- src/overworld_encounters.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c6cb69197368..e0dd63058595 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -28,7 +28,6 @@ static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool8 IsSpawningWaterMons(); static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); -static u8 CountActiveObjectEvents(); static bool8 IsSafeToSpawnObjectEvents(void); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); @@ -519,18 +518,6 @@ u8 CountActiveFollowMon() static u8 CountActiveObjectEvents() { - u8 i; - u8 count = 0; - - for(i = 0; i < OBJECT_EVENTS_COUNT; ++i) - { - if(gObjectEvents[i].active) - ++count; - } - - return count; -} - static bool8 IsSpawningWaterMons() { return (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER)); From 63836444c7b8b3a82b7bc15d29b308b88e30395f Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:28:28 -0600 Subject: [PATCH 113/572] Removed ALL of the unused function --- src/overworld_encounters.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e0dd63058595..d31cf01a6005 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -516,8 +516,6 @@ u8 CountActiveFollowMon() return count; } -static u8 CountActiveObjectEvents() -{ static bool8 IsSpawningWaterMons() { return (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER)); From 59b894e8e9b2f7217d99757333c052a0bc4b3947 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:35:03 -0600 Subject: [PATCH 114/572] Return invalid slot if at max ow mons and replacement turned off --- src/overworld_encounters.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d31cf01a6005..9190de5acc38 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -191,8 +191,11 @@ static u8 NextSpawnMonSlot(void) // All mon slots are in use if (CountActiveFollowMon() >= maxSpawns) { - // Cycle through so we remove the oldest mon first - spawnSlot = GetOldestSlot(); + if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) + // Cycle through so we remove the oldest mon first + spawnSlot = GetOldestSlot(); + else + return INVALID_SPAWN_SLOT; } else { From 4d65eec44402e95e19dcb00d9939db65ab3c4bb4 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:37:51 -0600 Subject: [PATCH 115/572] If at max ow mons, wait 1 second between spawn attempts --- src/overworld_encounters.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 9190de5acc38..4ac5c78bb899 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -149,6 +149,10 @@ void UpdateOverworldEncounters(void) MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objectEventId]); } } + else + { + sFollowMonData.spawnCountdown += 60; + } } } else From 0e0c62da130448bd06b4ad868df017fa823202f9 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 1 Dec 2025 19:01:53 -0600 Subject: [PATCH 116/572] Deleted FollowMon struct and moved age to main struct --- include/overworld_encounters.h | 10 +--------- src/overworld_encounters.c | 14 +++++++------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index f1eabbf63b7d..5e8a66bee8aa 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -13,18 +13,10 @@ #define INVALID_SPAWN_SLOT 0xFF -struct FollowMon -{ - u16 form:3; - u16 age:4; - u16 padding:9; - u16 padding2; -}; - struct FollowMonData { - struct FollowMon list[FOLLOWMON_MAX_SPAWN_SLOTS]; u16 spawnCountdown; + u8 age[FOLLOWMON_MAX_SPAWN_SLOTS]; }; //data/scripts/followmon.inc diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 4ac5c78bb899..d13edc617100 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -179,7 +179,7 @@ u32 GetOldestSlot(void) { if (GetOverworldSpeciesBySpawnSlot(spawnSlot) != SPECIES_NONE) { - if (sFollowMonData.list[spawnSlot].age > sFollowMonData.list[oldest].age) + if (sFollowMonData.age[spawnSlot] > sFollowMonData.age[oldest]) oldest = spawnSlot; } } @@ -396,7 +396,7 @@ static void SortOWEMonAges(void) if (GetOverworldSpeciesBySpawnSlot(i) != SPECIES_NONE) { array[count].slot = i; - array[count].age = sFollowMonData.list[i].age; + array[count].age = sFollowMonData.age[i]; count++; } if (count == numActive) @@ -418,12 +418,12 @@ static void SortOWEMonAges(void) } array[0].age = numActive; - sFollowMonData.list[array[0].slot].age = numActive; + sFollowMonData.age[array[0].slot] = numActive; for (i = 1; i < numActive; i++) { array[i].age = array[i - 1].age - 1; - sFollowMonData.list[array[i].slot].age = array[i].age; + sFollowMonData.age[array[i].slot] = array[i].age; } } @@ -441,7 +441,7 @@ void GeneratedOverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *ob return; u32 spawnSlot = GetSpawnSlotByLocalId(objectEvent->localId); - sFollowMonData.list[spawnSlot].age = 0; + sFollowMonData.age[spawnSlot] = 0; } u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) @@ -464,7 +464,7 @@ void ClearOverworldEncounterData(void) for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - sFollowMonData.list[i].age = 0; + sFollowMonData.age[i] = 0; } } @@ -649,7 +649,7 @@ u32 GetNewestOWEncounterLocalId(void) u32 newestSlot = 0; for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - if (sFollowMonData.list[newestSlot].age > sFollowMonData.list[i].age) + if (sFollowMonData.age[newestSlot] > sFollowMonData.age[i]) newestSlot = i; } From 986effd23b37d4467c58707a116051400d3f49de Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 1 Dec 2025 19:13:59 -0600 Subject: [PATCH 117/572] Changed spawnCountdown to use constants --- include/overworld_encounters.h | 3 +++ src/overworld_encounters.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 5e8a66bee8aa..302af3a71098 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -11,6 +11,9 @@ #define OWE_MAX_WATER_SPAWNS 5 #define OWE_MAX_CAVE_SPAWNS 4 +#define OWE_TIME_BETWEEN_SPAWNS 180 // Minimum wait time (in frames) between spawns. +#define OWE_SPAWN_TIME_VARIABILITY 60 // A random number of frames between 0 and this value will be added to OWE_TIME_BETWEEN_SPAWNS every reset for variability. + #define INVALID_SPAWN_SLOT 0xFF struct FollowMonData diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d13edc617100..8cad1720e4ad 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -123,7 +123,7 @@ void UpdateOverworldEncounters(void) gObjectEvents[objectEventId].hideReflection = TRUE; // Slower replacement spawning - sFollowMonData.spawnCountdown = 60 * (3 + Random() % 2); + sFollowMonData.spawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); enum FollowMonSpawnAnim spawnAnimType; From 9a6bceaea44c7ebaef9b9112ccc378ca5e36822f Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 2 Dec 2025 11:35:10 -0600 Subject: [PATCH 118/572] Moved ow mon age to playerCopyableMovement --- include/global.fieldmap.h | 4 +-- include/overworld_encounters.h | 1 - src/overworld_encounters.c | 48 +++++++++++++++++++--------------- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index 25a0f218d5e5..fdc76a5ed384 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -238,13 +238,13 @@ struct ObjectEvent /*0x1A*/ u8 fieldEffectSpriteId; /*0x1B*/ u8 warpArrowSpriteId; /*0x1C*/ u8 movementActionId; - /*0x1D*/ u8 trainerRange_berryTreeId; // Also stores level for Overworld Encounters + /*0x1D*/ u8 trainerRange_berryTreeId; // Also stores level for Overworld Encounters. /*0x1E*/ u8 currentMetatileBehavior; /*0x1F*/ u8 previousMetatileBehavior; /*0x20*/ u8 previousMovementDirection:4; u8 directionOverwrite:4; /*0x21*/ u8 directionSequenceIndex; - /*0x22*/ u8 playerCopyableMovement; // COPY_MOVE_* + /*0x22*/ u8 playerCopyableMovement; // COPY_MOVE_* Also stores age for Overworld Encounters. /*0x23*/ u8 spriteId; /*size = 0x24*/ }; diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 302af3a71098..ae7f011de498 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -19,7 +19,6 @@ struct FollowMonData { u16 spawnCountdown; - u8 age[FOLLOWMON_MAX_SPAWN_SLOTS]; }; //data/scripts/followmon.inc diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8cad1720e4ad..c39880a8cfc2 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -21,6 +21,7 @@ #include "constants/vars.h" #define sOverworldEncounterLevel trainerRange_berryTreeId +#define sAge playerCopyableMovement static EWRAM_DATA struct FollowMonData sFollowMonData = { 0 }; @@ -173,18 +174,20 @@ static u8 GetMaxFollowMonSpawns(void) u32 GetOldestSlot(void) { - u32 oldest = 0; + struct ObjectEvent *slotMon; + struct ObjectEvent *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; for (u32 spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) { - if (GetOverworldSpeciesBySpawnSlot(spawnSlot) != SPECIES_NONE) + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot)]; + if (OW_SPECIES(slotMon) != SPECIES_NONE) { - if (sFollowMonData.age[spawnSlot] > sFollowMonData.age[oldest]) - oldest = spawnSlot; + if (slotMon->sAge > oldest->sAge) + oldest = slotMon; } } - return oldest; + return GetSpawnSlotByLocalId(oldest->localId); } static u8 NextSpawnMonSlot(void) @@ -385,6 +388,7 @@ struct AgeSort static void SortOWEMonAges(void) { + struct ObjectEvent *slotMon; struct AgeSort array[FOLLOWMON_MAX_SPAWN_SLOTS]; struct AgeSort current; u32 numActive = CountActiveFollowMon(); @@ -393,10 +397,11 @@ static void SortOWEMonAges(void) for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - if (GetOverworldSpeciesBySpawnSlot(i) != SPECIES_NONE) + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - i)]; + if (OW_SPECIES(slotMon) != SPECIES_NONE) { array[count].slot = i; - array[count].age = sFollowMonData.age[i]; + array[count].age = slotMon->sAge; count++; } if (count == numActive) @@ -418,12 +423,14 @@ static void SortOWEMonAges(void) } array[0].age = numActive; - sFollowMonData.age[array[0].slot] = numActive; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - array[0].slot)]; + slotMon->sAge = numActive; for (i = 1; i < numActive; i++) { + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - array[i].slot)]; array[i].age = array[i - 1].age - 1; - sFollowMonData.age[array[i].slot] = array[i].age; + slotMon->sAge = array[i].age; } } @@ -439,9 +446,6 @@ void GeneratedOverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *ob { if (!IsGeneratedOverworldWildEncounter(objectEvent)) return; - - u32 spawnSlot = GetSpawnSlotByLocalId(objectEvent->localId); - sFollowMonData.age[spawnSlot] = 0; } u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) @@ -461,11 +465,6 @@ u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesI void ClearOverworldEncounterData(void) { sFollowMonData.spawnCountdown = 0; - - for (u32 i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) - { - sFollowMonData.age[i] = 0; - } } static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) @@ -645,15 +644,21 @@ static u32 GetSpawnSlotByLocalId(u32 localId) u32 GetNewestOWEncounterLocalId(void) { + struct ObjectEvent *slotMon; + struct ObjectEvent *newest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; u32 i; - u32 newestSlot = 0; + for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - if (sFollowMonData.age[newestSlot] > sFollowMonData.age[i]) - newestSlot = i; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - i)]; + if (OW_SPECIES(slotMon) != SPECIES_NONE) + { + if (newest->sAge > slotMon->sAge) + newest = slotMon; + } } - return newestSlot; + return GetSpawnSlotByLocalId(newest->localId); } bool32 CanRemoveOverworldEncounter(u32 localId) @@ -695,3 +700,4 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) } #undef sOverworldEncounterLevel +#undef sAge From 45ec33e8c26403d0caf2737ee9a285e80a016cde Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 2 Dec 2025 12:19:00 -0600 Subject: [PATCH 119/572] Changed spawn countdown minimum to a constant --- include/overworld_encounters.h | 5 +++-- src/overworld_encounters.c | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index ae7f011de498..3eb93a363e2c 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -11,8 +11,9 @@ #define OWE_MAX_WATER_SPAWNS 5 #define OWE_MAX_CAVE_SPAWNS 4 -#define OWE_TIME_BETWEEN_SPAWNS 180 // Minimum wait time (in frames) between spawns. -#define OWE_SPAWN_TIME_VARIABILITY 60 // A random number of frames between 0 and this value will be added to OWE_TIME_BETWEEN_SPAWNS every reset for variability. +#define OWE_TIME_BETWEEN_SPAWNS 180 // Minimum wait time (in frames) between spawns. +#define OWE_SPAWN_TIME_VARIABILITY 60 // A random number of frames between 0 and this value will be added to OWE_TIME_BETWEEN_SPAWNS every reset for variability. +#define OWE_SPAWN_TIME_MINIMUM 60 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. #define INVALID_SPAWN_SLOT 0xFF diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c39880a8cfc2..ea146ce615c8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -43,7 +43,7 @@ static void SortOWEMonAges(void); void LoadFollowMonData(void) { - sFollowMonData.spawnCountdown += 60; + sFollowMonData.spawnCountdown = OWE_SPAWN_TIME_MINIMUM; } void UpdateOverworldEncounters(void) @@ -152,7 +152,7 @@ void UpdateOverworldEncounters(void) } else { - sFollowMonData.spawnCountdown += 60; + sFollowMonData.spawnCountdown = OWE_SPAWN_TIME_MINIMUM; } } } @@ -464,7 +464,7 @@ u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesI void ClearOverworldEncounterData(void) { - sFollowMonData.spawnCountdown = 0; + sFollowMonData.spawnCountdown = OWE_SPAWN_TIME_MINIMUM; } static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) From 73c808bd67475474186bae0de2cd5fcfd84c2db6 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 2 Dec 2025 12:24:43 -0600 Subject: [PATCH 120/572] Cleaned up clearing the data --- src/overworld_encounters.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index ea146ce615c8..9759e7746501 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -54,14 +54,7 @@ void UpdateOverworldEncounters(void) if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER)) // Need check for if header has encounters? { RemoveAllOverworldEncounterObjects(); - // Zero sFollowMonData ; - u8 *raw = (u8 *)&sFollowMonData; - for (u32 i = 0; i < sizeof(struct FollowMonData); i++) - { - raw[i] = 0; - } - - return; + ClearOverworldEncounterData(); } u16 speciesId = SPECIES_NONE; From e17fc86c31859399804d269fd6d51a0192ecd758 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 2 Dec 2025 12:26:48 -0600 Subject: [PATCH 121/572] Moved check for OW_WILD_ENCOUNTERS_OVERWORLD --- src/overworld_encounters.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 9759e7746501..328c2158371e 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -48,10 +48,10 @@ void LoadFollowMonData(void) void UpdateOverworldEncounters(void) { - if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING)) + if (!OW_WILD_ENCOUNTERS_OVERWORLD || ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING)) return; - if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER)) // Need check for if header has encounters? + if (FlagGet(OW_FLAG_NO_ENCOUNTER)) // Need check for if header has encounters? { RemoveAllOverworldEncounterObjects(); ClearOverworldEncounterData(); From fe371a47bf46ab6a729b03be683936781d6a80be Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 2 Dec 2025 12:39:36 -0600 Subject: [PATCH 122/572] OW objects don't get removed on every frame if OW_FLAG_NO_ENCOUNTER is set --- src/overworld_encounters.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 328c2158371e..69187f3fc6ad 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -51,10 +51,19 @@ void UpdateOverworldEncounters(void) if (!OW_WILD_ENCOUNTERS_OVERWORLD || ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING)) return; - if (FlagGet(OW_FLAG_NO_ENCOUNTER)) // Need check for if header has encounters? + if (FlagGet(OW_FLAG_NO_ENCOUNTER)) { - RemoveAllOverworldEncounterObjects(); - ClearOverworldEncounterData(); + if (sFollowMonData.spawnCountdown != 255) + { + RemoveAllOverworldEncounterObjects(); + sFollowMonData.spawnCountdown = 255; + } + + return; + } + else if (sFollowMonData.spawnCountdown == 255) + { + sFollowMonData.spawnCountdown = OWE_SPAWN_TIME_MINIMUM; } u16 speciesId = SPECIES_NONE; From 0f2bde2137a7206b28b7852790969ed8ddb62e29 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 2 Dec 2025 12:44:41 -0600 Subject: [PATCH 123/572] Changed EWRAM data to a single u8 --- src/overworld_encounters.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 69187f3fc6ad..f0c2320042e8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -23,7 +23,7 @@ #define sOverworldEncounterLevel trainerRange_berryTreeId #define sAge playerCopyableMovement -static EWRAM_DATA struct FollowMonData sFollowMonData = { 0 }; +static EWRAM_DATA u8 sOWESpawnCountdown = 0; static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); @@ -43,7 +43,7 @@ static void SortOWEMonAges(void); void LoadFollowMonData(void) { - sFollowMonData.spawnCountdown = OWE_SPAWN_TIME_MINIMUM; + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } void UpdateOverworldEncounters(void) @@ -53,17 +53,17 @@ void UpdateOverworldEncounters(void) if (FlagGet(OW_FLAG_NO_ENCOUNTER)) { - if (sFollowMonData.spawnCountdown != 255) + if (sOWESpawnCountdown != 255) { RemoveAllOverworldEncounterObjects(); - sFollowMonData.spawnCountdown = 255; + sOWESpawnCountdown = 255; } return; } - else if (sFollowMonData.spawnCountdown == 255) + else if (sOWESpawnCountdown == 255) { - sFollowMonData.spawnCountdown = OWE_SPAWN_TIME_MINIMUM; + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } u16 speciesId = SPECIES_NONE; @@ -72,7 +72,7 @@ void UpdateOverworldEncounters(void) OverworldEncounters_ProcessMonInteraction(); - if(sFollowMonData.spawnCountdown == 0) + if(sOWESpawnCountdown == 0) { s16 x, y; const struct WildPokemonInfo *wildMonInfo = NULL; @@ -126,7 +126,7 @@ void UpdateOverworldEncounters(void) gObjectEvents[objectEventId].hideReflection = TRUE; // Slower replacement spawning - sFollowMonData.spawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); + sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); enum FollowMonSpawnAnim spawnAnimType; @@ -154,13 +154,13 @@ void UpdateOverworldEncounters(void) } else { - sFollowMonData.spawnCountdown = OWE_SPAWN_TIME_MINIMUM; + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } } } else { - --sFollowMonData.spawnCountdown; + --sOWESpawnCountdown; } } @@ -466,7 +466,7 @@ u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesI void ClearOverworldEncounterData(void) { - sFollowMonData.spawnCountdown = OWE_SPAWN_TIME_MINIMUM; + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) From d3b0a1adbde084b04d52cf66128010f52d8b31c0 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 2 Dec 2025 15:53:25 -0600 Subject: [PATCH 124/572] Shinies can no longer be chosen as the oldest ow mon --- src/overworld_encounters.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f0c2320042e8..6e08d1ef7006 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -178,11 +178,25 @@ u32 GetOldestSlot(void) { struct ObjectEvent *slotMon; struct ObjectEvent *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; + u32 spawnSlot; - for (u32 spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) + for (spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot)]; - if (OW_SPECIES(slotMon) != SPECIES_NONE) + if (OW_SPECIES(slotMon) != SPECIES_NONE && !OW_SHINY(slotMon)) + { + oldest = slotMon; + break; + } + } + + if (spawnSlot >= FOLLOWMON_MAX_SPAWN_SLOTS) + return INVALID_SPAWN_SLOT; + + for (spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) + { + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot)]; + if (OW_SPECIES(slotMon) != SPECIES_NONE && !OW_SHINY(slotMon)) { if (slotMon->sAge > oldest->sAge) oldest = slotMon; @@ -201,10 +215,16 @@ static u8 NextSpawnMonSlot(void) if (CountActiveFollowMon() >= maxSpawns) { if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) + { // Cycle through so we remove the oldest mon first spawnSlot = GetOldestSlot(); + if (spawnSlot == INVALID_SPAWN_SLOT) + return INVALID_SPAWN_SLOT; + } else + { return INVALID_SPAWN_SLOT; + } } else { From a4b99bccc80304b8a760cf7651837056e53b0e1a Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 2 Dec 2025 23:00:19 +0000 Subject: [PATCH 125/572] Add SetOverworldEncounterSpeciesInfo_Helper --- src/overworld_encounters.c | 101 +++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c8e267b50793..ec9d2b06cd63 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -496,29 +496,25 @@ void ClearOverworldEncounterData(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } -static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) +static void SetOverworldEncounterSpeciesInfo_Helper(u32 x, u32 y, u32 *encounterIndex, u32 headerId, enum TimeOfDay *timeOfDay, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 personality) { const struct WildPokemonInfo *wildMonInfo; - u32 headerId = GetCurrentMapWildMonHeaderId(); - u32 tileBehavior = MapGridGetMetatileBehaviorAt(x, y); - u32 timeOfDay, encounterIndex; - u32 personality = Random32(); - if (MetatileBehavior_IsWaterWildEncounter(tileBehavior)) + if (MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(x, y))) { - encounterIndex = ChooseWildMonIndex_Water(); - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); - wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; - *speciesId = wildMonInfo->wildPokemon[encounterIndex].species; - *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_WATER); + *encounterIndex = ChooseWildMonIndex_Water(); + *timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[*timeOfDay].waterMonsInfo; + *speciesId = wildMonInfo->wildPokemon[*encounterIndex].species; + *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, *encounterIndex, WILD_AREA_WATER); } else { - encounterIndex = ChooseWildMonIndex_Land(); - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; - *speciesId = wildMonInfo->wildPokemon[encounterIndex].species; - *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_LAND); + *encounterIndex = ChooseWildMonIndex_Land(); + *timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[*timeOfDay].landMonsInfo; + *speciesId = wildMonInfo->wildPokemon[*encounterIndex].species; + *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, *encounterIndex, WILD_AREA_LAND); } if (*speciesId == SPECIES_UNOWN) @@ -531,6 +527,27 @@ static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *s *isFemale = FALSE; } +static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) +{ + u32 headerId = GetCurrentMapWildMonHeaderId(); + enum TimeOfDay timeOfDay; + u32 encounterIndex; + u32 personality = Random32(); + + SetOverworldEncounterSpeciesInfo_Helper( + x, + y, + &encounterIndex, + headerId, + &timeOfDay, + speciesId, + isShiny, + isFemale, + level, + personality + ); +} + static bool8 IsSafeToSpawnObjectEvents(void) { struct ObjectEvent* player = &gObjectEvents[gPlayerAvatar.objectEventId]; @@ -730,48 +747,32 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) u16 GetGraphicsIdForOverworldEncounterGfx(void) { - // encounterType is hardcoded to land encounters for now + // x and y coords are hardcoded to set encounter type for now // can level be set or does it have to be hardcorded too? - enum WildPokemonArea encounterType = WILD_AREA_LAND; + u32 x = 3, y = 5; + u32 graphicsId; u32 headerId = GetCurrentMapWildMonHeaderId(); - - enum TimeOfDay timeOfDay = GetTimeOfDayForEncounters(headerId, encounterType); - const struct WildEncounterTypes wildEncounterType = gWildMonHeaders[headerId].encounterTypes[timeOfDay]; - const struct WildPokemonInfo *wildMonInfo; u32 encounterIndex; u16 speciesId; + bool32 isShiny = FALSE; + bool32 isFemale = FALSE; u32 level; u32 personality = Random32(); - bool32 isFemale = FALSE; - bool32 isShiny = FALSE; - - switch (encounterType) - { - default: - case WILD_AREA_LAND: - encounterIndex = ChooseWildMonIndex_Land(); - wildMonInfo = wildEncounterType.landMonsInfo; - speciesId = wildMonInfo->wildPokemon[encounterIndex].species; - level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_LAND); - break; - - case WILD_AREA_WATER: - encounterIndex = ChooseWildMonIndex_Water(); - wildMonInfo = wildEncounterType.waterMonsInfo; - speciesId = wildMonInfo->wildPokemon[encounterIndex].species; - level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_WATER); - break; - } - - if (speciesId == SPECIES_UNOWN) - speciesId = GetUnownSpeciesId(personality); + enum TimeOfDay timeOfDay; - isShiny = ComputePlayerShinyOdds(personality); - if (GetGenderFromSpeciesAndPersonality(speciesId, personality) == MON_FEMALE) - isFemale = TRUE; - else - isFemale = FALSE; + SetOverworldEncounterSpeciesInfo_Helper( + x, + y, + &encounterIndex, + headerId, + &timeOfDay, + &speciesId, + &isShiny, + &isFemale, + &level, + personality + ); graphicsId = speciesId + OBJ_EVENT_MON; if (isFemale) From 38a4173649320b1ef984546bdc8ac0df0d061d1e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 2 Dec 2025 23:34:01 +0000 Subject: [PATCH 126/572] Add Two OBJ_EVENT_GFX_OVERWORLD_ENCOUNTERS --- include/constants/event_objects.h | 5 +++- include/overworld_encounters.h | 6 ++++- src/event_data.c | 1 + src/event_object_movement.c | 8 +++--- src/overworld_encounters.c | 43 +++++++++++++++++-------------- 5 files changed, 38 insertions(+), 25 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 4148d056a328..3cbf83537b8b 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -274,7 +274,10 @@ #define OBJ_EVENT_GFX_VAR_D (OBJ_EVENT_GFX_VARS + 0xD) #define OBJ_EVENT_GFX_VAR_E (OBJ_EVENT_GFX_VARS + 0xE) #define OBJ_EVENT_GFX_VAR_F (OBJ_EVENT_GFX_VARS + 0xF) -#define OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER (OBJ_EVENT_GFX_VARS + 0x10) + +#define OBJ_EVENT_GFX_OVERWORLD_ENCOUNTERS (OBJ_EVENT_GFX_VARS + 0x10) +#define OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER_LAND (OBJ_EVENT_GFX_OVERWORLD_ENCOUNTERS + 0x0) +#define OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER_WATER (OBJ_EVENT_GFX_OVERWORLD_ENCOUNTERS + 0x1) // Don't use (1u << 15) to avoid conflict with BLEND_IMMUNE_FLAG. diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 8d6239b68c96..17d4a39022b1 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -15,6 +15,10 @@ #define OWE_SPAWN_TIME_VARIABILITY 60 // A random number of frames between 0 and this value will be added to OWE_TIME_BETWEEN_SPAWNS every reset for variability. #define OWE_SPAWN_TIME_MINIMUM 60 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. +#define OWE_SPAWN_METATILE 0 +#define OWE_SPAWN_LAND 1 +#define OWE_SPAWN_WATER 2 + #define INVALID_SPAWN_SLOT 0xFF struct FollowMonData @@ -45,6 +49,6 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); void RemoveOldestOverworldEncounter(u8 *objectEventId); bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); -u16 GetGraphicsIdForOverworldEncounterGfx(void); +u16 GetGraphicsIdForOverworldEncounterGfx(u32 id); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_data.c b/src/event_data.c index a8a05598a541..4d4222c25072 100644 --- a/src/event_data.c +++ b/src/event_data.c @@ -221,6 +221,7 @@ bool8 VarSet(u16 id, u16 value) u16 VarGetObjectEventGraphicsId(u8 id) { + DebugPrintf("TWat"); return VarGet(VAR_OBJ_GFX_ID_0 + id); } diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 601411b1bad3..84fbcfb45aa3 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -3108,8 +3108,8 @@ const struct ObjectEventGraphicsInfo *GetObjectEventGraphicsInfo(u16 graphicsId) if (graphicsId >= OBJ_EVENT_GFX_VARS && graphicsId <= OBJ_EVENT_GFX_VAR_F) graphicsId = VarGetObjectEventGraphicsId(graphicsId - OBJ_EVENT_GFX_VARS); - if (graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER) - graphicsId = GetGraphicsIdForOverworldEncounterGfx(); + if (graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER_LAND || graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER_WATER) + graphicsId = GetGraphicsIdForOverworldEncounterGfx(graphicsId - OBJ_EVENT_GFX_OVERWORLD_ENCOUNTERS); if (graphicsId == OBJ_EVENT_GFX_BARD) return gMauvilleOldManGraphicsInfoPointers[GetCurrentMauvilleOldMan()]; @@ -3128,8 +3128,8 @@ static void SetObjectEventDynamicGraphicsId(struct ObjectEvent *objectEvent) if (objectEvent->graphicsId >= OBJ_EVENT_GFX_VARS && objectEvent->graphicsId <= OBJ_EVENT_GFX_VAR_F) objectEvent->graphicsId = VarGetObjectEventGraphicsId(objectEvent->graphicsId - OBJ_EVENT_GFX_VARS); - if (objectEvent->graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER) - objectEvent->graphicsId = GetGraphicsIdForOverworldEncounterGfx(); + if (objectEvent->graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER_LAND || objectEvent->graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER_WATER) + objectEvent->graphicsId = GetGraphicsIdForOverworldEncounterGfx(objectEvent->graphicsId - OBJ_EVENT_GFX_OVERWORLD_ENCOUNTERS); } void SetObjectInvisibility(u8 localId, u8 mapNum, u8 mapGroup, bool8 invisible) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index ec9d2b06cd63..9db6fd9c4a74 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -496,19 +496,17 @@ void ClearOverworldEncounterData(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } -static void SetOverworldEncounterSpeciesInfo_Helper(u32 x, u32 y, u32 *encounterIndex, u32 headerId, enum TimeOfDay *timeOfDay, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 personality) +static void SetOverworldEncounterSpeciesInfo_Helper(u32 x, u32 y, u32 *encounterIndex, u32 headerId, enum TimeOfDay *timeOfDay, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 personality, u32 forceEncounterType) { const struct WildPokemonInfo *wildMonInfo; + bool32 isLandEncounter = MetatileBehavior_IsLandWildEncounter(MapGridGetMetatileBehaviorAt(x, y)); - if (MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(x, y))) - { - *encounterIndex = ChooseWildMonIndex_Water(); - *timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); - wildMonInfo = gWildMonHeaders[headerId].encounterTypes[*timeOfDay].waterMonsInfo; - *speciesId = wildMonInfo->wildPokemon[*encounterIndex].species; - *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, *encounterIndex, WILD_AREA_WATER); - } - else + if (forceEncounterType == OWE_SPAWN_LAND) + isLandEncounter = TRUE; + else if (forceEncounterType == OWE_SPAWN_WATER) + isLandEncounter = FALSE; + + if (isLandEncounter) { *encounterIndex = ChooseWildMonIndex_Land(); *timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); @@ -516,6 +514,14 @@ static void SetOverworldEncounterSpeciesInfo_Helper(u32 x, u32 y, u32 *encounter *speciesId = wildMonInfo->wildPokemon[*encounterIndex].species; *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, *encounterIndex, WILD_AREA_LAND); } + else + { + *encounterIndex = ChooseWildMonIndex_Water(); + *timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[*timeOfDay].waterMonsInfo; + *speciesId = wildMonInfo->wildPokemon[*encounterIndex].species; + *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, *encounterIndex, WILD_AREA_WATER); + } if (*speciesId == SPECIES_UNOWN) *speciesId = GetUnownSpeciesId(personality); @@ -544,7 +550,8 @@ static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *s isShiny, isFemale, level, - personality + personality, + OWE_SPAWN_METATILE ); } @@ -745,12 +752,8 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) || (IsManualOverworldWildEncounter(&gObjectEvents[objectEventId]) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL); } -u16 GetGraphicsIdForOverworldEncounterGfx(void) +u16 GetGraphicsIdForOverworldEncounterGfx(u32 id) { - // x and y coords are hardcoded to set encounter type for now - // can level be set or does it have to be hardcorded too? - u32 x = 3, y = 5; - u32 graphicsId; u32 headerId = GetCurrentMapWildMonHeaderId(); u32 encounterIndex; @@ -762,8 +765,8 @@ u16 GetGraphicsIdForOverworldEncounterGfx(void) enum TimeOfDay timeOfDay; SetOverworldEncounterSpeciesInfo_Helper( - x, - y, + 0, + 0, &encounterIndex, headerId, &timeOfDay, @@ -771,7 +774,8 @@ u16 GetGraphicsIdForOverworldEncounterGfx(void) &isShiny, &isFemale, &level, - personality + personality, + id + 1 ); graphicsId = speciesId + OBJ_EVENT_MON; @@ -782,6 +786,7 @@ u16 GetGraphicsIdForOverworldEncounterGfx(void) // objectEvent->trainerType = TRAINER_TYPE_ENCOUNTER; // objectEvent->sOverworldEncounterLevel = level; + DebugPrintf("%d, %d", id, graphicsId); return graphicsId; } From 6a6616d06c0a0279357d500cfb96cb18e05f8622 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 2 Dec 2025 23:34:04 +0000 Subject: [PATCH 127/572] Revert "Add Two OBJ_EVENT_GFX_OVERWORLD_ENCOUNTERS" This reverts commit 38a4173649320b1ef984546bdc8ac0df0d061d1e. --- include/constants/event_objects.h | 5 +--- include/overworld_encounters.h | 6 +---- src/event_data.c | 1 - src/event_object_movement.c | 8 +++--- src/overworld_encounters.c | 43 ++++++++++++++----------------- 5 files changed, 25 insertions(+), 38 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 3cbf83537b8b..4148d056a328 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -274,10 +274,7 @@ #define OBJ_EVENT_GFX_VAR_D (OBJ_EVENT_GFX_VARS + 0xD) #define OBJ_EVENT_GFX_VAR_E (OBJ_EVENT_GFX_VARS + 0xE) #define OBJ_EVENT_GFX_VAR_F (OBJ_EVENT_GFX_VARS + 0xF) - -#define OBJ_EVENT_GFX_OVERWORLD_ENCOUNTERS (OBJ_EVENT_GFX_VARS + 0x10) -#define OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER_LAND (OBJ_EVENT_GFX_OVERWORLD_ENCOUNTERS + 0x0) -#define OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER_WATER (OBJ_EVENT_GFX_OVERWORLD_ENCOUNTERS + 0x1) +#define OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER (OBJ_EVENT_GFX_VARS + 0x10) // Don't use (1u << 15) to avoid conflict with BLEND_IMMUNE_FLAG. diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 17d4a39022b1..8d6239b68c96 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -15,10 +15,6 @@ #define OWE_SPAWN_TIME_VARIABILITY 60 // A random number of frames between 0 and this value will be added to OWE_TIME_BETWEEN_SPAWNS every reset for variability. #define OWE_SPAWN_TIME_MINIMUM 60 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. -#define OWE_SPAWN_METATILE 0 -#define OWE_SPAWN_LAND 1 -#define OWE_SPAWN_WATER 2 - #define INVALID_SPAWN_SLOT 0xFF struct FollowMonData @@ -49,6 +45,6 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); void RemoveOldestOverworldEncounter(u8 *objectEventId); bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); -u16 GetGraphicsIdForOverworldEncounterGfx(u32 id); +u16 GetGraphicsIdForOverworldEncounterGfx(void); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_data.c b/src/event_data.c index 4d4222c25072..a8a05598a541 100644 --- a/src/event_data.c +++ b/src/event_data.c @@ -221,7 +221,6 @@ bool8 VarSet(u16 id, u16 value) u16 VarGetObjectEventGraphicsId(u8 id) { - DebugPrintf("TWat"); return VarGet(VAR_OBJ_GFX_ID_0 + id); } diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 84fbcfb45aa3..601411b1bad3 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -3108,8 +3108,8 @@ const struct ObjectEventGraphicsInfo *GetObjectEventGraphicsInfo(u16 graphicsId) if (graphicsId >= OBJ_EVENT_GFX_VARS && graphicsId <= OBJ_EVENT_GFX_VAR_F) graphicsId = VarGetObjectEventGraphicsId(graphicsId - OBJ_EVENT_GFX_VARS); - if (graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER_LAND || graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER_WATER) - graphicsId = GetGraphicsIdForOverworldEncounterGfx(graphicsId - OBJ_EVENT_GFX_OVERWORLD_ENCOUNTERS); + if (graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER) + graphicsId = GetGraphicsIdForOverworldEncounterGfx(); if (graphicsId == OBJ_EVENT_GFX_BARD) return gMauvilleOldManGraphicsInfoPointers[GetCurrentMauvilleOldMan()]; @@ -3128,8 +3128,8 @@ static void SetObjectEventDynamicGraphicsId(struct ObjectEvent *objectEvent) if (objectEvent->graphicsId >= OBJ_EVENT_GFX_VARS && objectEvent->graphicsId <= OBJ_EVENT_GFX_VAR_F) objectEvent->graphicsId = VarGetObjectEventGraphicsId(objectEvent->graphicsId - OBJ_EVENT_GFX_VARS); - if (objectEvent->graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER_LAND || objectEvent->graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER_WATER) - objectEvent->graphicsId = GetGraphicsIdForOverworldEncounterGfx(objectEvent->graphicsId - OBJ_EVENT_GFX_OVERWORLD_ENCOUNTERS); + if (objectEvent->graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER) + objectEvent->graphicsId = GetGraphicsIdForOverworldEncounterGfx(); } void SetObjectInvisibility(u8 localId, u8 mapNum, u8 mapGroup, bool8 invisible) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 9db6fd9c4a74..ec9d2b06cd63 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -496,25 +496,11 @@ void ClearOverworldEncounterData(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } -static void SetOverworldEncounterSpeciesInfo_Helper(u32 x, u32 y, u32 *encounterIndex, u32 headerId, enum TimeOfDay *timeOfDay, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 personality, u32 forceEncounterType) +static void SetOverworldEncounterSpeciesInfo_Helper(u32 x, u32 y, u32 *encounterIndex, u32 headerId, enum TimeOfDay *timeOfDay, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 personality) { const struct WildPokemonInfo *wildMonInfo; - bool32 isLandEncounter = MetatileBehavior_IsLandWildEncounter(MapGridGetMetatileBehaviorAt(x, y)); - if (forceEncounterType == OWE_SPAWN_LAND) - isLandEncounter = TRUE; - else if (forceEncounterType == OWE_SPAWN_WATER) - isLandEncounter = FALSE; - - if (isLandEncounter) - { - *encounterIndex = ChooseWildMonIndex_Land(); - *timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - wildMonInfo = gWildMonHeaders[headerId].encounterTypes[*timeOfDay].landMonsInfo; - *speciesId = wildMonInfo->wildPokemon[*encounterIndex].species; - *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, *encounterIndex, WILD_AREA_LAND); - } - else + if (MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(x, y))) { *encounterIndex = ChooseWildMonIndex_Water(); *timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); @@ -522,6 +508,14 @@ static void SetOverworldEncounterSpeciesInfo_Helper(u32 x, u32 y, u32 *encounter *speciesId = wildMonInfo->wildPokemon[*encounterIndex].species; *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, *encounterIndex, WILD_AREA_WATER); } + else + { + *encounterIndex = ChooseWildMonIndex_Land(); + *timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[*timeOfDay].landMonsInfo; + *speciesId = wildMonInfo->wildPokemon[*encounterIndex].species; + *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, *encounterIndex, WILD_AREA_LAND); + } if (*speciesId == SPECIES_UNOWN) *speciesId = GetUnownSpeciesId(personality); @@ -550,8 +544,7 @@ static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *s isShiny, isFemale, level, - personality, - OWE_SPAWN_METATILE + personality ); } @@ -752,8 +745,12 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) || (IsManualOverworldWildEncounter(&gObjectEvents[objectEventId]) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL); } -u16 GetGraphicsIdForOverworldEncounterGfx(u32 id) +u16 GetGraphicsIdForOverworldEncounterGfx(void) { + // x and y coords are hardcoded to set encounter type for now + // can level be set or does it have to be hardcorded too? + u32 x = 3, y = 5; + u32 graphicsId; u32 headerId = GetCurrentMapWildMonHeaderId(); u32 encounterIndex; @@ -765,8 +762,8 @@ u16 GetGraphicsIdForOverworldEncounterGfx(u32 id) enum TimeOfDay timeOfDay; SetOverworldEncounterSpeciesInfo_Helper( - 0, - 0, + x, + y, &encounterIndex, headerId, &timeOfDay, @@ -774,8 +771,7 @@ u16 GetGraphicsIdForOverworldEncounterGfx(u32 id) &isShiny, &isFemale, &level, - personality, - id + 1 + personality ); graphicsId = speciesId + OBJ_EVENT_MON; @@ -786,7 +782,6 @@ u16 GetGraphicsIdForOverworldEncounterGfx(u32 id) // objectEvent->trainerType = TRAINER_TYPE_ENCOUNTER; // objectEvent->sOverworldEncounterLevel = level; - DebugPrintf("%d, %d", id, graphicsId); return graphicsId; } From 9ea1027c1e36566a5e7973595a4ce046af7344b3 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 2 Dec 2025 23:56:08 +0000 Subject: [PATCH 128/572] Fix Overworld Encounters Right Graphics and Level --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 10 ++++++---- src/overworld_encounters.c | 15 ++++++--------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 8d6239b68c96..31a51ec0cb7f 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -45,6 +45,6 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); void RemoveOldestOverworldEncounter(u8 *objectEventId); bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); -u16 GetGraphicsIdForOverworldEncounterGfx(void); +u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 601411b1bad3..ce2ade7b2908 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1474,9 +1474,11 @@ static u8 InitObjectEventStateFromTemplate(const struct ObjectEventTemplate *tem objectEvent->previousElevation = template->elevation; objectEvent->range.rangeX = template->movementRangeX; objectEvent->range.rangeY = template->movementRangeY; - objectEvent->trainerType = template->trainerType; + if (IsManualOverworldWildEncounter(objectEvent) && objectEvent->trainerType == TRAINER_TYPE_NONE) + objectEvent->trainerType = template->trainerType; objectEvent->mapNum = mapNum; - objectEvent->trainerRange_berryTreeId = template->trainerRange_berryTreeId; + if (IsManualOverworldWildEncounter(objectEvent) && objectEvent->trainerRange_berryTreeId == 0) + objectEvent->trainerRange_berryTreeId = template->trainerRange_berryTreeId; objectEvent->previousMovementDirection = gInitialMovementTypeFacingDirections[template->movementType]; SetObjectEventDirection(objectEvent, objectEvent->previousMovementDirection); if (sMovementTypeHasRange[objectEvent->movementType]) @@ -3109,7 +3111,7 @@ const struct ObjectEventGraphicsInfo *GetObjectEventGraphicsInfo(u16 graphicsId) graphicsId = VarGetObjectEventGraphicsId(graphicsId - OBJ_EVENT_GFX_VARS); if (graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER) - graphicsId = GetGraphicsIdForOverworldEncounterGfx(); + graphicsId = OBJ_EVENT_GFX_OW_MON; if (graphicsId == OBJ_EVENT_GFX_BARD) return gMauvilleOldManGraphicsInfoPointers[GetCurrentMauvilleOldMan()]; @@ -3129,7 +3131,7 @@ static void SetObjectEventDynamicGraphicsId(struct ObjectEvent *objectEvent) objectEvent->graphicsId = VarGetObjectEventGraphicsId(objectEvent->graphicsId - OBJ_EVENT_GFX_VARS); if (objectEvent->graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER) - objectEvent->graphicsId = GetGraphicsIdForOverworldEncounterGfx(); + objectEvent->graphicsId = GetGraphicsIdForOverworldEncounterGfx(objectEvent); } void SetObjectInvisibility(u8 localId, u8 mapNum, u8 mapGroup, bool8 invisible) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index ec9d2b06cd63..ec88eb5b9ae4 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -745,12 +745,9 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) || (IsManualOverworldWildEncounter(&gObjectEvents[objectEventId]) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL); } -u16 GetGraphicsIdForOverworldEncounterGfx(void) +u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent) { - // x and y coords are hardcoded to set encounter type for now - // can level be set or does it have to be hardcorded too? - u32 x = 3, y = 5; - + struct ObjectEventTemplate template = *GetObjectEventTemplateByLocalIdAndMap(objectEvent->localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); u32 graphicsId; u32 headerId = GetCurrentMapWildMonHeaderId(); u32 encounterIndex; @@ -762,8 +759,8 @@ u16 GetGraphicsIdForOverworldEncounterGfx(void) enum TimeOfDay timeOfDay; SetOverworldEncounterSpeciesInfo_Helper( - x, - y, + template.x, + template.y, &encounterIndex, headerId, &timeOfDay, @@ -780,8 +777,8 @@ u16 GetGraphicsIdForOverworldEncounterGfx(void) if (isShiny) graphicsId += OBJ_EVENT_MON_SHINY; - // objectEvent->trainerType = TRAINER_TYPE_ENCOUNTER; - // objectEvent->sOverworldEncounterLevel = level; + objectEvent->trainerType = TRAINER_TYPE_ENCOUNTER; + objectEvent->sOverworldEncounterLevel = level; return graphicsId; } From 151898e07207929d721b509d36bb02f360b83e8f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 3 Dec 2025 00:05:37 +0000 Subject: [PATCH 129/572] Change 'AndMap' Functions --- src/overworld_encounters.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index ec88eb5b9ae4..dde1c6b011ad 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -238,8 +238,10 @@ static u8 NextSpawnMonSlot(void) if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) { - // Remove any existing id by this slot - RemoveObjectEventByLocalIdAndMap(GetLocalIdByOverworldSpawnSlot(spawnSlot), gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); + u32 objectEventId = GetObjectEventIdByLocalId(localId); + struct ObjectEvent *object = &gObjectEvents[objectEventId]; + RemoveObjectEventByLocalIdAndMap(object->localId, object->mapNum, object->mapGroup); } return spawnSlot; @@ -333,7 +335,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) void CreateOverworldWildEncounter(void) { - u32 objEventId = GetObjectEventIdByLocalIdAndMap(gSpecialVar_LastTalked, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + u32 objEventId = GetObjectEventIdByLocalId(gSpecialVar_LastTalked); struct ObjectEvent *object = &gObjectEvents[objEventId]; if (objEventId >= OBJECT_EVENTS_COUNT) @@ -363,7 +365,7 @@ void CreateOverworldWildEncounter(void) isFemale ); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); - RemoveObjectEvent(object); + RemoveObjectEventByLocalIdAndMap(object->localId, object->mapNum, object->mapGroup); BattleSetup_StartWildBattle(); } @@ -470,9 +472,6 @@ void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *ob void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { - if (IsManualOverworldWildEncounter(objectEvent)) - FlagSet(GetObjectEventFlagIdByLocalIdAndMap(objectEvent->localId, objectEvent->mapNum, objectEvent->mapGroup)); - if (!IsGeneratedOverworldWildEncounter(objectEvent)) return; } @@ -579,7 +578,7 @@ void RemoveAllOverworldEncounterObjects(void) { struct ObjectEvent *obj = &gObjectEvents[i]; if (IsGeneratedOverworldWildEncounter(obj)) - RemoveObjectEvent(obj); + RemoveObjectEventByLocalIdAndMap(obj->localId, obj->mapNum, obj->mapGroup); } } From 11620df8d7670299a6ce9d27cc44b30dbdb981dd Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 3 Dec 2025 00:19:15 +0000 Subject: [PATCH 130/572] Fix Collisions for Semi Manual Ecnounter --- include/overworld_encounters.h | 1 + src/event_object_movement.c | 6 ++++-- src/overworld_encounters.c | 5 +++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 31a51ec0cb7f..63bbf16a493a 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -40,6 +40,7 @@ bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEve bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent); +bool32 IsSemiManualOverworldWildEncounter(struct ObjectEvent *objectEvent); u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index ce2ade7b2908..d7d61e152583 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1442,6 +1442,7 @@ static u8 InitObjectEventStateFromTemplate(const struct ObjectEventTemplate *tem u8 objectEventId; s16 x; s16 y; + bool32 semiManuelOverworldWildEncounter; if (GetAvailableObjectEventId(template->localId, mapNum, mapGroup, &objectEventId)) return OBJECT_EVENTS_COUNT; @@ -1452,6 +1453,7 @@ static u8 InitObjectEventStateFromTemplate(const struct ObjectEventTemplate *tem objectEvent->active = TRUE; objectEvent->triggerGroundEffectsOnMove = TRUE; objectEvent->graphicsId = template->graphicsId; + semiManuelOverworldWildEncounter = IsSemiManualOverworldWildEncounter(objectEvent); SetObjectEventDynamicGraphicsId(objectEvent); if (IS_OW_MON_OBJ(objectEvent)) { @@ -1474,10 +1476,10 @@ static u8 InitObjectEventStateFromTemplate(const struct ObjectEventTemplate *tem objectEvent->previousElevation = template->elevation; objectEvent->range.rangeX = template->movementRangeX; objectEvent->range.rangeY = template->movementRangeY; - if (IsManualOverworldWildEncounter(objectEvent) && objectEvent->trainerType == TRAINER_TYPE_NONE) + if (!semiManuelOverworldWildEncounter) objectEvent->trainerType = template->trainerType; objectEvent->mapNum = mapNum; - if (IsManualOverworldWildEncounter(objectEvent) && objectEvent->trainerRange_berryTreeId == 0) + if (!semiManuelOverworldWildEncounter) objectEvent->trainerRange_berryTreeId = template->trainerRange_berryTreeId; objectEvent->previousMovementDirection = gInitialMovementTypeFacingDirections[template->movementType]; SetObjectEventDirection(objectEvent, objectEvent->previousMovementDirection); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index dde1c6b011ad..b6d1fd4c3f0f 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -670,6 +670,11 @@ bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent) || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - FOLLOWMON_MAX_SPAWN_SLOTS)); } +bool32 IsSemiManualOverworldWildEncounter(struct ObjectEvent *objectEvent) +{ + return objectEvent->graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER; +} + static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) { u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); From 535ab75c3643faa4fbf6bb7f5d990260f44f8574 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 2 Dec 2025 19:55:44 -0600 Subject: [PATCH 131/572] Allow level to be manually set for manual ow objects --- src/event_object_movement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index d7d61e152583..0d69698b0e91 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1479,7 +1479,7 @@ static u8 InitObjectEventStateFromTemplate(const struct ObjectEventTemplate *tem if (!semiManuelOverworldWildEncounter) objectEvent->trainerType = template->trainerType; objectEvent->mapNum = mapNum; - if (!semiManuelOverworldWildEncounter) + if (!semiManuelOverworldWildEncounter || template->trainerRange_berryTreeId != 0) objectEvent->trainerRange_berryTreeId = template->trainerRange_berryTreeId; objectEvent->previousMovementDirection = gInitialMovementTypeFacingDirections[template->movementType]; SetObjectEventDirection(objectEvent, objectEvent->previousMovementDirection); From 82103a1416d0a0d7bec3ea285375cea45f0f0c6b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 3 Dec 2025 18:00:57 +0000 Subject: [PATCH 132/572] More OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER Edit --- include/constants/event_objects.h | 4 ++-- .../object_event_graphics_info_pointers.h | 1 + src/event_object_movement.c | 15 +++++---------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 4148d056a328..cd5a312c4123 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -247,11 +247,12 @@ #define OBJ_EVENT_GFX_OW_MON 240 #define OBJ_EVENT_GFX_LIGHT_SPRITE 241 #define OBJ_EVENT_GFX_APRICORN_TREE 242 +#define OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER 243 // NOTE: The maximum amount of object events has been expanded from 255 to 65535. // Since dynamic graphics ids still require at least 16 free values, the actual limit // is 65519, but even considering follower Pokémon, this should be more than enough :) -#define NUM_OBJ_EVENT_GFX 243 +#define NUM_OBJ_EVENT_GFX 244 // These are dynamic object gfx ids. @@ -274,7 +275,6 @@ #define OBJ_EVENT_GFX_VAR_D (OBJ_EVENT_GFX_VARS + 0xD) #define OBJ_EVENT_GFX_VAR_E (OBJ_EVENT_GFX_VARS + 0xE) #define OBJ_EVENT_GFX_VAR_F (OBJ_EVENT_GFX_VARS + 0xF) -#define OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER (OBJ_EVENT_GFX_VARS + 0x10) // Don't use (1u << 15) to avoid conflict with BLEND_IMMUNE_FLAG. diff --git a/src/data/object_events/object_event_graphics_info_pointers.h b/src/data/object_events/object_event_graphics_info_pointers.h index a239bce99317..ce154e94b51b 100755 --- a/src/data/object_events/object_event_graphics_info_pointers.h +++ b/src/data/object_events/object_event_graphics_info_pointers.h @@ -494,6 +494,7 @@ const struct ObjectEventGraphicsInfo *const gObjectEventGraphicsInfoPointers[NUM [OBJ_EVENT_GFX_OW_MON] = &gObjectEventGraphicsInfo_Follower, [OBJ_EVENT_GFX_LIGHT_SPRITE] = &gObjectEventGraphicsInfo_BallLight, [OBJ_EVENT_GFX_APRICORN_TREE] = &gObjectEventGraphicsInfo_ApricornTree, + [OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER] = &gObjectEventGraphicsInfo_Follower, }; const struct ObjectEventGraphicsInfo *const gMauvilleOldManGraphicsInfoPointers[] = { diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 0d69698b0e91..e492d2c1bef1 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1438,22 +1438,20 @@ u8 GetObjectEventIdByLocalId(u8 localId) static u8 InitObjectEventStateFromTemplate(const struct ObjectEventTemplate *template, u8 mapNum, u8 mapGroup) { - struct ObjectEvent *objectEvent; + struct ObjectEvent *objectEvent, *objectEventTemp; u8 objectEventId; s16 x; s16 y; - bool32 semiManuelOverworldWildEncounter; if (GetAvailableObjectEventId(template->localId, mapNum, mapGroup, &objectEventId)) return OBJECT_EVENTS_COUNT; - objectEvent = &gObjectEvents[objectEventId]; + objectEvent = objectEventTemp = &gObjectEvents[objectEventId]; ClearObjectEvent(objectEvent); x = template->x + MAP_OFFSET; y = template->y + MAP_OFFSET; objectEvent->active = TRUE; objectEvent->triggerGroundEffectsOnMove = TRUE; objectEvent->graphicsId = template->graphicsId; - semiManuelOverworldWildEncounter = IsSemiManualOverworldWildEncounter(objectEvent); SetObjectEventDynamicGraphicsId(objectEvent); if (IS_OW_MON_OBJ(objectEvent)) { @@ -1476,10 +1474,10 @@ static u8 InitObjectEventStateFromTemplate(const struct ObjectEventTemplate *tem objectEvent->previousElevation = template->elevation; objectEvent->range.rangeX = template->movementRangeX; objectEvent->range.rangeY = template->movementRangeY; - if (!semiManuelOverworldWildEncounter) + if (!IsSemiManualOverworldWildEncounter(objectEventTemp)) objectEvent->trainerType = template->trainerType; objectEvent->mapNum = mapNum; - if (!semiManuelOverworldWildEncounter || template->trainerRange_berryTreeId != 0) + if (!IsSemiManualOverworldWildEncounter(objectEventTemp)) objectEvent->trainerRange_berryTreeId = template->trainerRange_berryTreeId; objectEvent->previousMovementDirection = gInitialMovementTypeFacingDirections[template->movementType]; SetObjectEventDirection(objectEvent, objectEvent->previousMovementDirection); @@ -3112,9 +3110,6 @@ const struct ObjectEventGraphicsInfo *GetObjectEventGraphicsInfo(u16 graphicsId) if (graphicsId >= OBJ_EVENT_GFX_VARS && graphicsId <= OBJ_EVENT_GFX_VAR_F) graphicsId = VarGetObjectEventGraphicsId(graphicsId - OBJ_EVENT_GFX_VARS); - if (graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER) - graphicsId = OBJ_EVENT_GFX_OW_MON; - if (graphicsId == OBJ_EVENT_GFX_BARD) return gMauvilleOldManGraphicsInfoPointers[GetCurrentMauvilleOldMan()]; @@ -3132,7 +3127,7 @@ static void SetObjectEventDynamicGraphicsId(struct ObjectEvent *objectEvent) if (objectEvent->graphicsId >= OBJ_EVENT_GFX_VARS && objectEvent->graphicsId <= OBJ_EVENT_GFX_VAR_F) objectEvent->graphicsId = VarGetObjectEventGraphicsId(objectEvent->graphicsId - OBJ_EVENT_GFX_VARS); - if (objectEvent->graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER) + if (IsSemiManualOverworldWildEncounter(objectEvent)) objectEvent->graphicsId = GetGraphicsIdForOverworldEncounterGfx(objectEvent); } From dd238f187d7db2fc81fc243ac20a331b2007f1c5 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 3 Dec 2025 18:14:38 +0000 Subject: [PATCH 133/572] Change More RemoveObjects --- src/overworld_encounters.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index b6d1fd4c3f0f..54ec449b60e4 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -241,7 +241,7 @@ static u8 NextSpawnMonSlot(void) u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); u32 objectEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[objectEventId]; - RemoveObjectEventByLocalIdAndMap(object->localId, object->mapNum, object->mapGroup); + RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); } return spawnSlot; @@ -335,7 +335,8 @@ static bool8 TrySelectTile(s16* outX, s16* outY) void CreateOverworldWildEncounter(void) { - u32 objEventId = GetObjectEventIdByLocalId(gSpecialVar_LastTalked); + u32 localId = gSpecialVar_LastTalked; + u32 objEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[objEventId]; if (objEventId >= OBJECT_EVENTS_COUNT) @@ -365,7 +366,7 @@ void CreateOverworldWildEncounter(void) isFemale ); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); - RemoveObjectEventByLocalIdAndMap(object->localId, object->mapNum, object->mapGroup); + RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); BattleSetup_StartWildBattle(); } @@ -722,14 +723,16 @@ bool32 CanRemoveOverworldEncounter(u32 localId) void RemoveOldestOverworldEncounter(u8 *objectEventId) { - *objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(GetOldestSlot())); + u32 localId = GetLocalIdByOverworldSpawnSlot(GetOldestSlot()); + *objectEventId = GetObjectEventIdByLocalId(localId); + struct ObjectEvent *object = &gObjectEvents[*objectEventId]; s16 *fldEffSpriteId = &gSprites[gObjectEvents[*objectEventId].spriteId].data[6]; // Stop the associated field effect if it is active. if (*fldEffSpriteId != 0) FieldEffectStop(&gSprites[*fldEffSpriteId - 1], FLDEFF_BUBBLES); - RemoveObjectEvent(&gObjectEvents[*objectEventId]); + RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); } bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) From 5fbad0f72fa641dd2374ff7d29cff6c5dd957568 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 3 Dec 2025 18:15:39 +0000 Subject: [PATCH 134/572] Remove Restriction to Map for OWEs and Remove Remain on Map Movement --- include/constants/event_object_movement.h | 9 ++-- include/event_object_movement.h | 2 - include/overworld_encounters.h | 1 - .../movement_action_func_tables.h | 10 ---- src/event_object_movement.c | 32 ++----------- src/overworld_encounters.c | 48 ++----------------- 6 files changed, 12 insertions(+), 90 deletions(-) diff --git a/include/constants/event_object_movement.h b/include/constants/event_object_movement.h index 54c38b06dc51..170d71cc5e02 100755 --- a/include/constants/event_object_movement.h +++ b/include/constants/event_object_movement.h @@ -83,11 +83,10 @@ #define MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_LEFT 0x4F #define MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT 0x50 #define MOVEMENT_TYPE_FOLLOW_PLAYER 0x51 -#define MOVEMENT_TYPE_WANDER_ON_MAP 0x52 -#define MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER 0x53 -#define MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER 0x54 -#define MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER 0x55 -#define NUM_MOVEMENT_TYPES 0x56 +#define MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER 0x52 +#define MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER 0x53 +#define MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER 0x54 +#define NUM_MOVEMENT_TYPES 0x55 #define MOVEMENT_ACTION_FACE_DOWN 0x0 #define MOVEMENT_ACTION_FACE_UP 0x1 diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 9f52b3f6ad29..122288dd9253 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -325,7 +325,6 @@ void MovementType_RunInPlace(struct Sprite *sprite); void MovementType_Invisible(struct Sprite *sprite); void MovementType_WalkSlowlyInPlace(struct Sprite *sprite); void MovementType_FollowPlayer(struct Sprite *sprite); -void MovementType_WanderOnMap(struct Sprite *sprite); void MovementType_WanderOnLandEncounter(struct Sprite *sprite); void MovementType_WanderOnWaterEncounter(struct Sprite *sprite); void MovementType_WanderOnIndoorEncounter(struct Sprite *sprite); @@ -503,7 +502,6 @@ u8 MovementType_RunInPlace_Step0(struct ObjectEvent *objectEvent, struct Sprite u8 MovementType_Invisible_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_WanderOnMap_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderOnWaterEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderOnIndoorEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 63bbf16a493a..c6fefe9a58ad 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -36,7 +36,6 @@ u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesI void ClearOverworldEncounterData(void); u8 CountActiveFollowMon(); void RemoveAllOverworldEncounterObjects(void); -bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent); diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index bb7c9a782da9..11747ca845d0 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1719,16 +1719,6 @@ u8 (*const gMovementActionFuncs_WalkFastDiagonalDownRight[])(struct ObjectEvent MovementAction_PauseSpriteAnim, }; -u8 (*const gMovementTypeFuncs_WanderOnMap[])(struct ObjectEvent *, struct Sprite *) = { - MovementType_WanderAround_Step0, - MovementType_WanderAround_Step1, - MovementType_WanderAround_Step2, - MovementType_Wander_Step3, - MovementType_WanderOnMap_Step4, - MovementType_WanderAround_Step5, - MovementType_WanderAround_Step6, -}; - u8 (*const gMovementTypeFuncs_WanderOnLandEncounter[])(struct ObjectEvent *, struct Sprite *) = { MovementType_WanderAround_Step0, MovementType_WanderAround_Step1, diff --git a/src/event_object_movement.c b/src/event_object_movement.c index e492d2c1bef1..5807c8e15b2e 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -349,7 +349,6 @@ static void (*const sMovementTypeCallbacks[])(struct Sprite *) = [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_LEFT] = MovementType_WalkSlowlyInPlace, [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT] = MovementType_WalkSlowlyInPlace, [MOVEMENT_TYPE_FOLLOW_PLAYER] = MovementType_FollowPlayer, - [MOVEMENT_TYPE_WANDER_ON_MAP] = MovementType_WanderOnMap, [MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER] = MovementType_WanderOnLandEncounter, [MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER] = MovementType_WanderOnWaterEncounter, [MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER] = MovementType_WanderOnIndoorEncounter, @@ -397,7 +396,6 @@ static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_COPY_PLAYER_OPPOSITE_IN_GRASS] = TRUE, [MOVEMENT_TYPE_COPY_PLAYER_COUNTERCLOCKWISE_IN_GRASS] = TRUE, [MOVEMENT_TYPE_COPY_PLAYER_CLOCKWISE_IN_GRASS] = TRUE, - [MOVEMENT_TYPE_WANDER_ON_MAP] = TRUE, [MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER] = TRUE, [MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER] = TRUE, [MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER] = TRUE, @@ -486,7 +484,6 @@ const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_LEFT] = DIR_WEST, [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT] = DIR_EAST, [MOVEMENT_TYPE_FOLLOW_PLAYER] = DIR_SOUTH, - [MOVEMENT_TYPE_WANDER_ON_MAP] = DIR_SOUTH, [MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER] = DIR_SOUTH, [MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER] = DIR_SOUTH, [MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER] = DIR_SOUTH, @@ -11602,26 +11599,6 @@ bool8 MovementAction_FollowMonSpawn(enum FollowMonSpawnAnim spawnAnimType, struc return TRUE; } -movement_type_def(MovementType_WanderOnMap, gMovementTypeFuncs_WanderOnMap) - -bool8 MovementType_WanderOnMap_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) -{ - u8 directions[4]; - u8 chosenDirection; - s16 x, y; - memcpy(directions, gStandardDirections, sizeof directions); - chosenDirection = directions[Random() & 3]; - SetObjectEventDirection(objectEvent, chosenDirection); - x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; - y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; - sprite->sTypeFuncId = 5; - if (!IsOverworldEncounterObjectEventInSpawnedMap(objectEvent, x, y) - || GetCollisionInDirection(objectEvent, chosenDirection)) - sprite->sTypeFuncId = 1; - - return TRUE; -} - movement_type_def(MovementType_WanderOnLandEncounter, gMovementTypeFuncs_WanderOnLandEncounter) bool8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) @@ -11635,8 +11612,7 @@ bool8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsOverworldEncounterObjectEventInSpawnedMap(objectEvent, x, y) - || !MetatileBehavior_IsLandWildEncounter(MapGridGetMetatileBehaviorAt(x, y)) + if (!MetatileBehavior_IsLandWildEncounter(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11656,8 +11632,7 @@ bool8 MovementType_WanderOnWaterEncounter_Step4(struct ObjectEvent *objectEvent, x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsOverworldEncounterObjectEventInSpawnedMap(objectEvent, x, y) - || !MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(x, y)) + if (!MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11677,8 +11652,7 @@ bool8 MovementType_WanderOnIndoorEncounter_Step4(struct ObjectEvent *objectEvent x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!IsOverworldEncounterObjectEventInSpawnedMap(objectEvent, x, y) - || !MetatileBehavior_IsIndoorEncounter(MapGridGetMetatileBehaviorAt(x, y)) + if (!MetatileBehavior_IsIndoorEncounter(MapGridGetMetatileBehaviorAt(x, y)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 54ec449b60e4..3ce2fa70f872 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -33,9 +33,6 @@ static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *s static bool8 IsSafeToSpawnObjectEvents(void); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); -static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height); -static bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); -static bool32 IsInsidePlayerMap(s16 x, s16 y); static void OverworldEncounters_ProcessMonInteraction(void); static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); @@ -101,7 +98,7 @@ void UpdateOverworldEncounters(void) } else { - movementType = MOVEMENT_TYPE_WANDER_ON_MAP; + movementType = MOVEMENT_TYPE_WANDER_AROUND; } struct ObjectEventTemplate objectEventTemplate = { @@ -254,6 +251,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) s16 playerX, playerY; s16 x, y; u8 closeDistance; + const struct MapLayout *layout; // Spawn further away when surfing if(IsSpawningWaterMons()) @@ -298,7 +296,9 @@ static bool8 TrySelectTile(s16* outX, s16* outY) elevation = MapGridGetElevationAt(x, y); - if (!IsInsidePlayerMap(x, y)) + layout = Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->mapLayout; + if ((x + MAP_OFFSET) < 0 || (x + MAP_OFFSET) >= layout->width || + (y + MAP_OFFSET) < 0 || (y + MAP_OFFSET) >= layout->height) return FALSE; // 0 is change of elevation, 15 is multiple elevation e.g. bridges @@ -614,44 +614,6 @@ static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater) return gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; } -static void GetMapSize(u8 mapGroup, u8 mapNum, s32 *width, s32 *height) -{ - const struct MapLayout *layout; - - layout = Overworld_GetMapHeaderByGroupAndId(mapGroup, mapNum)->mapLayout; - *width = layout->width; - *height = layout->height; -} - -static bool32 IsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) -{ - s32 width, height; - GetMapSize(mapGroup, mapNum, &width, &height); - x -= MAP_OFFSET; - y -= MAP_OFFSET; - - if (x >= 0 && x < width && y >= 0 && y < height) - return TRUE; - - return FALSE; -} - -static bool32 IsInsidePlayerMap(s16 x, s16 y) -{ - return IsInsideMap(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, x, y); -} - -bool32 IsOverworldEncounterObjectEventInSpawnedMap(struct ObjectEvent *objectEvent, s16 x, s16 y) -{ - u8 mapGroup = objectEvent->mapGroup; - u8 mapNum = objectEvent->mapNum; - - if (mapGroup == gSaveBlock1Ptr->location.mapGroup && mapNum == gSaveBlock1Ptr->location.mapNum) - return IsInsidePlayerMap(x, y); - else - return !IsInsidePlayerMap(x, y); -} - bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent) { return (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_ENCOUNTER); From d1d70d87024256121806d7e2cd0130875fad62ae Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:53:47 +0000 Subject: [PATCH 135/572] Use Wrapper Functions --- src/overworld_encounters.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 3ce2fa70f872..b25052a561b1 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -174,13 +174,12 @@ static u8 GetMaxFollowMonSpawns(void) u32 GetOldestSlot(void) { - struct ObjectEvent *slotMon; - struct ObjectEvent *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; + struct ObjectEvent *slotMon, *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; u32 spawnSlot; for (spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot)]; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; if (OW_SPECIES(slotMon) != SPECIES_NONE && !OW_SHINY(slotMon)) { oldest = slotMon; @@ -193,7 +192,7 @@ u32 GetOldestSlot(void) for (spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - spawnSlot)]; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; if (OW_SPECIES(slotMon) != SPECIES_NONE && !OW_SHINY(slotMon)) { if (slotMon->sAge > oldest->sAge) @@ -426,7 +425,7 @@ static void SortOWEMonAges(void) for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - i)]; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; if (OW_SPECIES(slotMon) != SPECIES_NONE) { array[count].slot = i; @@ -452,12 +451,12 @@ static void SortOWEMonAges(void) } array[0].age = numActive; - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - array[0].slot)]; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(array[0].slot))]; slotMon->sAge = numActive; for (i = 1; i < numActive; i++) { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - array[i].slot)]; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(array[i].slot))]; array[i].age = array[i - 1].age - 1; slotMon->sAge = array[i].age; } @@ -663,7 +662,7 @@ u32 GetNewestOWEncounterLocalId(void) for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END - i)]; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; if (OW_SPECIES(slotMon) != SPECIES_NONE) { if (newest->sAge > slotMon->sAge) From da0f7125958e5dfb15467d4b5023e9462841c792 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 3 Dec 2025 20:38:27 +0000 Subject: [PATCH 136/572] Add comment --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index b25052a561b1..5cdd74ff6674 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -715,6 +715,7 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent) { + // Does this work? struct ObjectEventTemplate template = *GetObjectEventTemplateByLocalIdAndMap(objectEvent->localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); u32 graphicsId; u32 headerId = GetCurrentMapWildMonHeaderId(); From e8d93c7d3efd532ab9bd5b90964102ba62a58952 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 3 Dec 2025 20:41:50 +0000 Subject: [PATCH 137/572] Reorder stuff --- src/overworld_encounters.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5cdd74ff6674..bc093dda78b4 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -529,9 +529,9 @@ static void SetOverworldEncounterSpeciesInfo_Helper(u32 x, u32 y, u32 *encounter static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) { u32 headerId = GetCurrentMapWildMonHeaderId(); - enum TimeOfDay timeOfDay; u32 encounterIndex; u32 personality = Random32(); + enum TimeOfDay timeOfDay; SetOverworldEncounterSpeciesInfo_Helper( x, @@ -718,12 +718,12 @@ u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent) // Does this work? struct ObjectEventTemplate template = *GetObjectEventTemplateByLocalIdAndMap(objectEvent->localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); u32 graphicsId; - u32 headerId = GetCurrentMapWildMonHeaderId(); - u32 encounterIndex; u16 speciesId; bool32 isShiny = FALSE; bool32 isFemale = FALSE; u32 level; + u32 headerId = GetCurrentMapWildMonHeaderId(); + u32 encounterIndex; u32 personality = Random32(); enum TimeOfDay timeOfDay; From 2cd2a2b69a8630de0fcf36f9e146de9b457e0222 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:18:20 +0000 Subject: [PATCH 138/572] Add Early Returns --- src/overworld_encounters.c | 163 ++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 83 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index bc093dda78b4..761d55386cd6 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -70,95 +70,92 @@ void UpdateOverworldEncounters(void) OverworldEncounters_ProcessMonInteraction(); - if(sOWESpawnCountdown == 0) + if (sOWESpawnCountdown != 0) { - s16 x, y; - const struct WildPokemonInfo *wildMonInfo = NULL; + sOWESpawnCountdown--; + return; + } - wildMonInfo = GetActiveEncounterTable(IsSpawningWaterMons()); + s16 x, y; + if (GetActiveEncounterTable(IsSpawningWaterMons()) && IsSafeToSpawnObjectEvents() && TrySelectTile(&x, &y)) + { + u16 spawnSlot = NextSpawnMonSlot(); - if(wildMonInfo && IsSafeToSpawnObjectEvents() && TrySelectTile(&x, &y)) + if (spawnSlot == INVALID_SPAWN_SLOT) { - u16 spawnSlot = NextSpawnMonSlot(); - - if (spawnSlot != INVALID_SPAWN_SLOT) - { - bool32 waterMons = IsSpawningWaterMons(); - bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; - u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); - u32 movementType, level; - if (OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT) // These checks need to be improved - { - if (waterMons) - movementType = MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER; - else if (indoors) - movementType = MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER; - else - movementType = MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER; - } - else - { - movementType = MOVEMENT_TYPE_WANDER_AROUND; - } - - struct ObjectEventTemplate objectEventTemplate = { - .localId = localId, - .graphicsId = GetFollowMonObjectEventGraphicsId(spawnSlot, x, y, &speciesId, &isShiny, &isFemale, &level), - .x = x - MAP_OFFSET, - .y = y - MAP_OFFSET, - .elevation = MapGridGetElevationAt(x, y), - .movementType = movementType, - .trainerType = TRAINER_TYPE_ENCOUNTER, - }; - - u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); - - gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; - gObjectEvents[objectEventId].range.rangeX = OW_ENCOUNTER_MOVEMENT_RANGE_X; - gObjectEvents[objectEventId].range.rangeY = OW_ENCOUNTER_MOVEMENT_RANGE_Y; - gObjectEvents[objectEventId].sOverworldEncounterLevel = level; - - // Hide reflections for spawns in water - // (It just looks weird) - if (waterMons) - gObjectEvents[objectEventId].hideReflection = TRUE; - - // Slower replacement spawning - sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); - - enum FollowMonSpawnAnim spawnAnimType; - - // Play spawn animation. - if (speciesId != SPECIES_NONE) - { - if (isShiny) - { - PlaySE(SE_SHINY); - spawnAnimType = FOLLOWMON_SPAWN_ANIM_SHINY; - } - else - { - PlayCry_Normal(speciesId, 25); - if (IsSpawningWaterMons()) - spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; - else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) - spawnAnimType = FOLLOWMON_SPAWN_ANIM_CAVE; - else - spawnAnimType = FOLLOWMON_SPAWN_ANIM_GRASS; - } - // Instantly play a small animation to ground the spawning a bit - MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objectEventId]); - } - } + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + return; + } + + bool32 waterMons = IsSpawningWaterMons(); + bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; + u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); + u32 movementType, level; + if (OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT) // These checks need to be improved + { + if (waterMons) + movementType = MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER; + else if (indoors) + movementType = MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER; else - { - sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - } + movementType = MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER; } - } - else - { - --sOWESpawnCountdown; + else + { + movementType = MOVEMENT_TYPE_WANDER_AROUND; + } + + struct ObjectEventTemplate objectEventTemplate = { + .localId = localId, + .graphicsId = GetFollowMonObjectEventGraphicsId(spawnSlot, x, y, &speciesId, &isShiny, &isFemale, &level), + .x = x - MAP_OFFSET, + .y = y - MAP_OFFSET, + .elevation = MapGridGetElevationAt(x, y), + .movementType = movementType, + .trainerType = TRAINER_TYPE_ENCOUNTER, + }; + + u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); + + gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; + gObjectEvents[objectEventId].range.rangeX = OW_ENCOUNTER_MOVEMENT_RANGE_X; + gObjectEvents[objectEventId].range.rangeY = OW_ENCOUNTER_MOVEMENT_RANGE_Y; + gObjectEvents[objectEventId].sOverworldEncounterLevel = level; + + // Hide reflections for spawns in water + // (It just looks weird) + if (waterMons) + gObjectEvents[objectEventId].hideReflection = TRUE; + + // Slower replacement spawning + sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); + + enum FollowMonSpawnAnim spawnAnimType; + + // Play spawn animation. + if (speciesId == SPECIES_NONE) + { + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + return; + } + + if (isShiny) + { + PlaySE(SE_SHINY); + spawnAnimType = FOLLOWMON_SPAWN_ANIM_SHINY; + } + else + { + PlayCry_Normal(speciesId, 25); + if (IsSpawningWaterMons()) + spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; + else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) + spawnAnimType = FOLLOWMON_SPAWN_ANIM_CAVE; + else + spawnAnimType = FOLLOWMON_SPAWN_ANIM_GRASS; + } + // Instantly play a small animation to ground the spawning a bit + MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objectEventId]); } } From a778e37a657abae129b5d861439e2d6a97d0df13 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:28:05 +0000 Subject: [PATCH 139/572] Edit IsSpawningWaterMons --- src/overworld_encounters.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 761d55386cd6..e744b0169d47 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -28,7 +28,7 @@ static EWRAM_DATA u8 sOWESpawnCountdown = 0; static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); -static bool8 IsSpawningWaterMons(); +static bool32 OWE_ShouldSpawnWaterMons(void); static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); static bool8 IsSafeToSpawnObjectEvents(void); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); @@ -77,7 +77,7 @@ void UpdateOverworldEncounters(void) } s16 x, y; - if (GetActiveEncounterTable(IsSpawningWaterMons()) && IsSafeToSpawnObjectEvents() && TrySelectTile(&x, &y)) + if (GetActiveEncounterTable(OWE_ShouldSpawnWaterMons()) && IsSafeToSpawnObjectEvents() && TrySelectTile(&x, &y)) { u16 spawnSlot = NextSpawnMonSlot(); @@ -87,7 +87,7 @@ void UpdateOverworldEncounters(void) return; } - bool32 waterMons = IsSpawningWaterMons(); + bool32 waterMons = OWE_ShouldSpawnWaterMons(); bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); u32 movementType, level; @@ -147,7 +147,7 @@ void UpdateOverworldEncounters(void) else { PlayCry_Normal(speciesId, 25); - if (IsSpawningWaterMons()) + if (OWE_ShouldSpawnWaterMons()) spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) spawnAnimType = FOLLOWMON_SPAWN_ANIM_CAVE; @@ -161,7 +161,7 @@ void UpdateOverworldEncounters(void) static u8 GetMaxFollowMonSpawns(void) { - if (IsSpawningWaterMons()) + if (OWE_ShouldSpawnWaterMons()) return OWE_MAX_WATER_SPAWNS; else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) return OWE_MAX_CAVE_SPAWNS; @@ -250,7 +250,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) const struct MapLayout *layout; // Spawn further away when surfing - if(IsSpawningWaterMons()) + if (OWE_ShouldSpawnWaterMons()) closeDistance = 3; else closeDistance = 1; @@ -303,7 +303,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) return FALSE; tileBehavior = MapGridGetMetatileBehaviorAt(x, y); - if(IsSpawningWaterMons()) + if (OWE_ShouldSpawnWaterMons()) { if(MetatileBehavior_IsWaterWildEncounter(tileBehavior) && !MapGridGetCollisionAt(x, y)) { @@ -564,9 +564,9 @@ u8 CountActiveFollowMon() return count; } -static bool8 IsSpawningWaterMons() +static bool32 OWE_ShouldSpawnWaterMons(void) { - return (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER)); + return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); } void RemoveAllOverworldEncounterObjects(void) From a285d8fd1def82e0c7d0db5e21fb595ef19ce9df Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:28:56 +0000 Subject: [PATCH 140/572] Update overworld_encounters.c --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e744b0169d47..7d65f6e6896b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -240,6 +240,7 @@ static u8 NextSpawnMonSlot(void) return spawnSlot; } +// Magic Numbers static bool8 TrySelectTile(s16* outX, s16* outY) { u8 elevation; From 947ddd5925fdd629f963a3d0d477cf94ce5e7239 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 3 Dec 2025 23:27:47 +0000 Subject: [PATCH 141/572] Remove Excess Spawn Slot Variable --- include/overworld_encounters.h | 2 +- src/overworld_encounters.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index c6fefe9a58ad..b65492aa8a4b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -32,7 +32,7 @@ void CreateOverworldWildEncounter(void); bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); -u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); +u32 GetFollowMonObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); void ClearOverworldEncounterData(void); u8 CountActiveFollowMon(); void RemoveAllOverworldEncounterObjects(void); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7d65f6e6896b..7ece05ec049c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -29,7 +29,7 @@ static EWRAM_DATA u8 sOWESpawnCountdown = 0; static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool32 OWE_ShouldSpawnWaterMons(void); -static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); static bool8 IsSafeToSpawnObjectEvents(void); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); @@ -107,7 +107,7 @@ void UpdateOverworldEncounters(void) struct ObjectEventTemplate objectEventTemplate = { .localId = localId, - .graphicsId = GetFollowMonObjectEventGraphicsId(spawnSlot, x, y, &speciesId, &isShiny, &isFemale, &level), + .graphicsId = GetFollowMonObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level), .x = x - MAP_OFFSET, .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), @@ -474,9 +474,9 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent return; } -u32 GetFollowMonObjectEventGraphicsId(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) +u32 GetFollowMonObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) { - SetOverworldEncounterSpeciesInfo(spawnSlot, x, y, speciesId, isShiny, isFemale, level); + SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level); u16 graphicsId = *speciesId + OBJ_EVENT_MON; if (*isFemale) @@ -524,7 +524,7 @@ static void SetOverworldEncounterSpeciesInfo_Helper(u32 x, u32 y, u32 *encounter *isFemale = FALSE; } -static void SetOverworldEncounterSpeciesInfo(u32 spawnSlot, s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) { u32 headerId = GetCurrentMapWildMonHeaderId(); u32 encounterIndex; From 27639f83dfc5571a96c88a30e734eaad6477992b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 4 Dec 2025 00:40:23 +0000 Subject: [PATCH 142/572] Update overworld_encounters.c --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7ece05ec049c..aad2b4864c72 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -682,6 +682,7 @@ bool32 CanRemoveOverworldEncounter(u32 localId) void RemoveOldestOverworldEncounter(u8 *objectEventId) { + // include a check for oldest slot not being INVALID_SPAWN_SLOT u32 localId = GetLocalIdByOverworldSpawnSlot(GetOldestSlot()); *objectEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[*objectEventId]; From 14b76705f3e614929cd4100c800d74eeec0be8c2 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 4 Dec 2025 01:14:08 +0000 Subject: [PATCH 143/572] Change Encounters to Occur on Collision --- data/scripts/followmon.inc | 4 ++ include/overworld_encounters.h | 2 +- src/event_object_movement.c | 6 +- src/overworld_encounters.c | 116 +++++++++++++++++++++------------ 4 files changed, 84 insertions(+), 44 deletions(-) diff --git a/data/scripts/followmon.inc b/data/scripts/followmon.inc index 4ea4f9cfbea6..aa1de51ade03 100644 --- a/data/scripts/followmon.inc +++ b/data/scripts/followmon.inc @@ -1,5 +1,9 @@ InteractWithDynamicWildFollowMon:: lockall + applymovement VAR_LAST_TALKED, Common_Movement_ExclamationMark + callnative ScriptFaceLastTalked + playmoncry VAR_0x8004, CRY_MODE_NORMAL + waitmoncry special CreateOverworldWildEncounter waitstate end diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index b65492aa8a4b..a1604e081e3b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -29,7 +29,6 @@ void LoadFollowMonData(void); void UpdateOverworldEncounters(void); u32 GetOldestSlot(void); void CreateOverworldWildEncounter(void); -bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider); void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetFollowMonObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); @@ -46,5 +45,6 @@ bool32 CanRemoveOverworldEncounter(u32 localId); void RemoveOldestOverworldEncounter(u8 *objectEventId); bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent); +void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); #endif // GUARD_FOLLOWMON_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 5807c8e15b2e..c51cb6979b8f 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -6493,14 +6493,18 @@ u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, b curObject = &gObjectEvents[i]; if (curObject->active && (curObject->movementType != MOVEMENT_TYPE_FOLLOW_PLAYER || objectEvent != &gObjectEvents[gPlayerAvatar.objectEventId]) && curObject != objectEvent && !FollowerNPC_IsCollisionExempt(curObject, objectEvent) // Partner - && !OverworldEncounter_IsCollisionExempt(curObject, objectEvent) // Overworld Wild Encounters ) { // check for collision if curObject is active, not the object in question, and not exempt from collisions if ((curObject->currentCoords.x == x && curObject->currentCoords.y == y) || (curObject->previousCoords.x == x && curObject->previousCoords.y == y)) { if (AreElevationsCompatible(objectEvent->currentElevation, curObject->currentElevation)) + { + // check if objects can actually collide with this or if it returns no too early + // should be fine + OWE_TryTriggerEncounter(objectEvent, curObject); return i; + } } } } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index aad2b4864c72..5f54ebbda8f6 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -33,7 +33,6 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 static bool8 IsSafeToSpawnObjectEvents(void); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); -static void OverworldEncounters_ProcessMonInteraction(void); static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); static u32 GetSpawnSlotByLocalId(u32 localId); @@ -68,8 +67,6 @@ void UpdateOverworldEncounters(void) bool32 isShiny = FALSE; bool32 isFemale = FALSE; - OverworldEncounters_ProcessMonInteraction(); - if (sOWESpawnCountdown != 0) { sOWESpawnCountdown--; @@ -367,45 +364,6 @@ void CreateOverworldWildEncounter(void) BattleSetup_StartWildBattle(); } -static void OverworldEncounters_ProcessMonInteraction(void) -{ - u8 i; - struct ObjectEvent *object; - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - - for (i = 0; i < OBJECT_EVENTS_COUNT; i++) - { - object = &gObjectEvents[i]; - if (IsOverworldWildEncounter(object) && object->active && object != player - && ((object->currentCoords.x == player->currentCoords.x && object->currentCoords.y == player->currentCoords.y) - || (object->previousCoords.x == player->currentCoords.x && object->previousCoords.y == player->currentCoords.y)) - && AreElevationsCompatible(object->currentElevation, player->currentElevation)) - { - gSpecialVar_LastTalked = object->localId; - ScriptContext_SetupScript(InteractWithDynamicWildFollowMon); - } - } -} - -bool32 OverworldEncounter_IsCollisionExempt(struct ObjectEvent* obstacle, struct ObjectEvent* collider) -{ - if (!OW_WILD_ENCOUNTERS_OVERWORLD) - return FALSE; - - // The player only is exempt from collisions with OW Encounters when not using a repel or the DexNav is inactive. - - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - bool32 forcePlayerCollision = (/* VarGet(VAR_REPEL_STEP_COUNT) > 0 || */ FlagGet(DN_FLAG_SEARCHING)); - - if (collider == player && IsOverworldWildEncounter(obstacle) && !forcePlayerCollision) - return TRUE; - - if (obstacle == player && IsOverworldWildEncounter(collider) && !forcePlayerCollision) - return TRUE; - - return FALSE; -} - struct AgeSort { u8 slot:4; @@ -750,5 +708,79 @@ u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent) return graphicsId; } +static u32 DetermineNPCDirection(struct ObjectEvent *player, struct ObjectEvent *npc) +{ + // Can be moved out of here and consolidated with FollowerNPC version of function. + s32 delta_x = npc->currentCoords.x - player->currentCoords.x; + s32 delta_y = npc->currentCoords.y - player->currentCoords.y; + + if (delta_x < 0) + return DIR_EAST; + else if (delta_x > 0) + return DIR_WEST; + + if (delta_y < 0) + return DIR_SOUTH; + else if (delta_y > 0) + return DIR_NORTH; + + return DIR_NONE; +} + +void ScriptFaceLastTalked(struct ScriptContext *ctx) +{ + // Can be moved out of here and consolidated with FollowerNPC version of function. + u32 playerDirection, npcDirection; + struct ObjectEvent *player, *npc; + npc = &gObjectEvents[gPlayerAvatar.objectEventId]; + npc = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; + + if (npc->invisible == FALSE) + { + playerDirection = DetermineNPCDirection(player, npc); + npcDirection = playerDirection; + + //Flip direction. + switch (playerDirection) + { + case DIR_NORTH: + playerDirection = DIR_SOUTH; + break; + case DIR_SOUTH: + playerDirection = DIR_NORTH; + break; + case DIR_WEST: + playerDirection = DIR_EAST; + break; + case DIR_EAST: + playerDirection = DIR_WEST; + break; + } + + ObjectEventTurn(player, playerDirection); + ObjectEventTurn(npc, npcDirection); + } +} + +void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) +{ + // The only automatically interacts with an OW Encounter when; + // Not using a repel or the DexNav is inactive. + if (/* VarGet(VAR_REPEL_STEP_COUNT) > 0 || */ FlagGet(DN_FLAG_SEARCHING)) + return; + + bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle)); + bool32 playerIsObstacle = (obstacle->isPlayer && IsOverworldWildEncounter(collider)); + + if (playerIsCollider || playerIsObstacle) + { + struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; + + gSpecialVar_LastTalked = wildMon->localId; + gSpecialVar_0x8004 = OW_SPECIES(wildMon); + ScriptContext_SetupScript(InteractWithDynamicWildFollowMon); + } +} + #undef sOverworldEncounterLevel #undef sAge From fdcc877a67fddcf1d6a20ffd29f4c50052327e1c Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 4 Dec 2025 11:16:04 +0000 Subject: [PATCH 144/572] Typo --- src/overworld_encounters.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5f54ebbda8f6..27814eed6760 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -710,7 +710,6 @@ u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent) static u32 DetermineNPCDirection(struct ObjectEvent *player, struct ObjectEvent *npc) { - // Can be moved out of here and consolidated with FollowerNPC version of function. s32 delta_x = npc->currentCoords.x - player->currentCoords.x; s32 delta_y = npc->currentCoords.y - player->currentCoords.y; @@ -732,7 +731,7 @@ void ScriptFaceLastTalked(struct ScriptContext *ctx) // Can be moved out of here and consolidated with FollowerNPC version of function. u32 playerDirection, npcDirection; struct ObjectEvent *player, *npc; - npc = &gObjectEvents[gPlayerAvatar.objectEventId]; + player = &gObjectEvents[gPlayerAvatar.objectEventId]; npc = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; if (npc->invisible == FALSE) From 2db3a2cd922075edf0ae720646d6812d62072c3c Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 4 Dec 2025 11:44:15 +0000 Subject: [PATCH 145/572] Cry Adjustments --- data/scripts/followmon.inc | 2 +- src/overworld_encounters.c | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/data/scripts/followmon.inc b/data/scripts/followmon.inc index aa1de51ade03..f05a6522e7cb 100644 --- a/data/scripts/followmon.inc +++ b/data/scripts/followmon.inc @@ -2,7 +2,7 @@ InteractWithDynamicWildFollowMon:: lockall applymovement VAR_LAST_TALKED, Common_Movement_ExclamationMark callnative ScriptFaceLastTalked - playmoncry VAR_0x8004, CRY_MODE_NORMAL + playmoncry VAR_0x8004, CRY_MODE_DOUBLES waitmoncry special CreateOverworldWildEncounter waitstate diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 27814eed6760..f1959ac19412 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -136,6 +136,9 @@ void UpdateOverworldEncounters(void) return; } + u32 pan = (Random() % 88) + 212; + u32 volume = (Random() % 30) + 50; + PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); if (isShiny) { PlaySE(SE_SHINY); @@ -143,7 +146,6 @@ void UpdateOverworldEncounters(void) } else { - PlayCry_Normal(speciesId, 25); if (OWE_ShouldSpawnWaterMons()) spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) @@ -666,8 +668,16 @@ bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventI bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) { - return IsGeneratedOverworldWildEncounter(&gObjectEvents[objectEventId]) - || (IsManualOverworldWildEncounter(&gObjectEvents[objectEventId]) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL); + struct ObjectEvent *object = &gObjectEvents[objectEventId]; + + if (IsGeneratedOverworldWildEncounter(object) + || (IsManualOverworldWildEncounter(object) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL)) + { + gSpecialVar_0x8004 = OW_SPECIES(object); + return TRUE; + } + + return FALSE; } u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent) From f7a678167db65c7b1dacf5ffd55fc0da336399d2 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 4 Dec 2025 11:15:11 -0600 Subject: [PATCH 146/572] Renamed everything from "FollowMon" to "OverworldEncounter" --- data/event_scripts.s | 2 +- ...followmon.inc => overworld_encounters.inc} | 2 +- include/constants/event_objects.h | 2 +- include/constants/vars.h | 8 --- include/event_object_movement.h | 12 ++--- include/overworld_encounters.h | 22 +++----- src/event_object_movement.c | 2 +- src/field_control_avatar.c | 2 +- src/load_save.c | 2 +- src/overworld_encounters.c | 52 +++++++++---------- 10 files changed, 46 insertions(+), 60 deletions(-) rename data/scripts/{followmon.inc => overworld_encounters.inc} (84%) diff --git a/data/event_scripts.s b/data/event_scripts.s index 80c75ad57094..97e0754359e8 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -1162,4 +1162,4 @@ EventScript_VsSeekerChargingDone:: .include "data/scripts/dexnav.inc" .include "data/scripts/battle_frontier.inc" .include "data/scripts/apricorn_tree.inc" - .include "data/scripts/followmon.inc" + .include "data/scripts/overworld_encounters.inc" diff --git a/data/scripts/followmon.inc b/data/scripts/overworld_encounters.inc similarity index 84% rename from data/scripts/followmon.inc rename to data/scripts/overworld_encounters.inc index f05a6522e7cb..13a6c5830d74 100644 --- a/data/scripts/followmon.inc +++ b/data/scripts/overworld_encounters.inc @@ -1,4 +1,4 @@ -InteractWithDynamicWildFollowMon:: +InteractWithDynamicWildOverworldEncounter:: lockall applymovement VAR_LAST_TALKED, Common_Movement_ExclamationMark callnative ScriptFaceLastTalked diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index cd5a312c4123..98c0942ad2f9 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -331,7 +331,7 @@ #define LOCALID_NONE 0 #define LOCALID_CAMERA 127 #define LOCALID_BERRY_BLENDER_PLAYER_END 240 // This will use 5 (MAX_RFU_PLAYERS) IDs ending at 240, i.e. 236-240 -#define LOCALID_OW_ENCOUNTER_END 252 // This will use 5 (FOLLOWMON_MAX_SPAWN_SLOTS) IDs ending at 252, i.e. 248-252 +#define LOCALID_OW_ENCOUNTER_END 252 // This will use 5 (OWE_MAX_SPAWN_SLOTS) IDs ending at 252, i.e. 248-252 #define LOCALID_FOLLOWING_POKEMON 254 #define LOCALID_PLAYER 255 #define OBJ_EVENT_ID_FOLLOWER 0xFE diff --git a/include/constants/vars.h b/include/constants/vars.h index 80cda6df4d82..a12c2ff3a508 100644 --- a/include/constants/vars.h +++ b/include/constants/vars.h @@ -274,14 +274,6 @@ #define VAR_UNUSED_0x40FE 0x40FE // Unused Var #define VAR_UNUSED_0x40FF 0x40FF // Unused Var -/* -#define VAR_FOLLOW_MON_0 0x40FA -#define VAR_FOLLOW_MON_1 0x40FB -#define VAR_FOLLOW_MON_2 0x40FC -#define VAR_FOLLOW_MON_3 0x40FD -#define VAR_FOLLOW_MON_4 0x40FE -#define VAR_FOLLOW_MON_5 0x40FF -*/ #define VARS_END 0x40FF #define VARS_COUNT (VARS_END - VARS_START + 1) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 122288dd9253..5572fc7961be 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -60,12 +60,12 @@ enum FollowerTransformTypes TRANSFORM_TYPE_WEATHER, }; -enum FollowMonSpawnAnim +enum OverworldEncounterSpawnAnim { - FOLLOWMON_SPAWN_ANIM_GRASS, - FOLLOWMON_SPAWN_ANIM_WATER, - FOLLOWMON_SPAWN_ANIM_CAVE, - FOLLOWMON_SPAWN_ANIM_SHINY, + OWE_SPAWN_ANIM_GRASS, + OWE_SPAWN_ANIM_WATER, + OWE_SPAWN_ANIM_CAVE, + OWE_SPAWN_ANIM_SHINY, }; #define FIGURE_8_LENGTH 72 @@ -529,6 +529,6 @@ bool8 PlayerIsUnderWaterfall(struct ObjectEvent *objectEvent); u8 GetObjectEventApricornTreeId(u8 objectEventId); -bool8 MovementAction_FollowMonSpawn(enum FollowMonSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); +bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); #endif //GUARD_EVENT_OBJECT_MOVEMENT_H diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index a1604e081e3b..095f0b7204f0 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -1,11 +1,11 @@ -#ifndef GUARD_FOLLOWMON_H -#define GUARD_FOLLOWMON_H +#ifndef GUARD_OVERWORLD_ENCOUNTERS_H +#define GUARD_OVERWORLD_ENCOUNTERS_H #if OW_POKEMON_OBJECT_EVENTS == FALSE && OW_WILD_ENCOUNTERS_OVERWORLD == TRUE #error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OW_WILD_ENCOUNTERS_OVERWORLD to work." #endif -#define FOLLOWMON_MAX_SPAWN_SLOTS 5 +#define OWE_MAX_SPAWN_SLOTS 5 #define OWE_MAX_LAND_SPAWNS 3 #define OWE_MAX_WATER_SPAWNS 5 @@ -17,23 +17,17 @@ #define INVALID_SPAWN_SLOT 0xFF -struct FollowMonData -{ - u16 spawnCountdown; -}; +extern const u8 InteractWithDynamicWildOverworldEncounter[]; -//data/scripts/followmon.inc -extern const u8 InteractWithDynamicWildFollowMon[]; - -void LoadFollowMonData(void); +void LoadOverworldEncounterData(void); void UpdateOverworldEncounters(void); u32 GetOldestSlot(void); void CreateOverworldWildEncounter(void); void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); -u32 GetFollowMonObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); +u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); void ClearOverworldEncounterData(void); -u8 CountActiveFollowMon(); +u8 CountActiveOverworldEncounters(void); void RemoveAllOverworldEncounterObjects(void); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent); @@ -47,4 +41,4 @@ bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventI u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); -#endif // GUARD_FOLLOWMON_H \ No newline at end of file +#endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index c51cb6979b8f..0ba9c5079b4b 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11593,7 +11593,7 @@ u8 GetObjectEventApricornTreeId(u8 objectEventId) return gObjectEvents[objectEventId].trainerRange_berryTreeId; } -bool8 MovementAction_FollowMonSpawn(enum FollowMonSpawnAnim spawnAnimType, struct ObjectEvent *objEvent) { +bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent) { gFieldEffectArguments[0] = objEvent->currentCoords.x; gFieldEffectArguments[1] = objEvent->currentCoords.y; gFieldEffectArguments[2] = gSprites[objEvent->spriteId].oam.priority + 1; diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 2102cb9f71fb..b41e9a3692ff 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -401,7 +401,7 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 gSpecialVar_Facing = direction; if (ShouldRunOverworldEncounterScript(objectEventId)) - script = InteractWithDynamicWildFollowMon; + script = InteractWithDynamicWildOverworldEncounter; else if (InTrainerHill() == TRUE) script = GetTrainerHillTrainerScript(); else if (PlayerHasFollowerNPC() && objectEventId == GetFollowerNPCObjectId()) diff --git a/src/load_save.c b/src/load_save.c index 3c32198af000..763073b84f4c 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -221,7 +221,7 @@ void LoadObjectEvents(void) int i; u16 graphicsId; - LoadFollowMonData(); + LoadOverworldEncounterData(); for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { gObjectEvents[i] = gSaveBlock1Ptr->objectEvents[i]; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f1959ac19412..0bb1ba8fe1a8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -38,7 +38,7 @@ static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); static u32 GetSpawnSlotByLocalId(u32 localId); static void SortOWEMonAges(void); -void LoadFollowMonData(void) +void LoadOverworldEncounterData(void) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } @@ -104,7 +104,7 @@ void UpdateOverworldEncounters(void) struct ObjectEventTemplate objectEventTemplate = { .localId = localId, - .graphicsId = GetFollowMonObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level), + .graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level), .x = x - MAP_OFFSET, .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), @@ -127,7 +127,7 @@ void UpdateOverworldEncounters(void) // Slower replacement spawning sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); - enum FollowMonSpawnAnim spawnAnimType; + enum OverworldEncounterSpawnAnim spawnAnimType; // Play spawn animation. if (speciesId == SPECIES_NONE) @@ -142,23 +142,23 @@ void UpdateOverworldEncounters(void) if (isShiny) { PlaySE(SE_SHINY); - spawnAnimType = FOLLOWMON_SPAWN_ANIM_SHINY; + spawnAnimType = OWE_SPAWN_ANIM_SHINY; } else { if (OWE_ShouldSpawnWaterMons()) - spawnAnimType = FOLLOWMON_SPAWN_ANIM_WATER; + spawnAnimType = OWE_SPAWN_ANIM_WATER; else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) - spawnAnimType = FOLLOWMON_SPAWN_ANIM_CAVE; + spawnAnimType = OWE_SPAWN_ANIM_CAVE; else - spawnAnimType = FOLLOWMON_SPAWN_ANIM_GRASS; + spawnAnimType = OWE_SPAWN_ANIM_GRASS; } // Instantly play a small animation to ground the spawning a bit - MovementAction_FollowMonSpawn(spawnAnimType, &gObjectEvents[objectEventId]); + MovementAction_OverworldEncounterSpawn(spawnAnimType, &gObjectEvents[objectEventId]); } } -static u8 GetMaxFollowMonSpawns(void) +static u8 GetMaxOverworldEncounterSpawns(void) { if (OWE_ShouldSpawnWaterMons()) return OWE_MAX_WATER_SPAWNS; @@ -173,7 +173,7 @@ u32 GetOldestSlot(void) struct ObjectEvent *slotMon, *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; u32 spawnSlot; - for (spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) + for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWN_SLOTS; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; if (OW_SPECIES(slotMon) != SPECIES_NONE && !OW_SHINY(slotMon)) @@ -183,10 +183,10 @@ u32 GetOldestSlot(void) } } - if (spawnSlot >= FOLLOWMON_MAX_SPAWN_SLOTS) + if (spawnSlot >= OWE_MAX_SPAWN_SLOTS) return INVALID_SPAWN_SLOT; - for (spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) + for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWN_SLOTS; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; if (OW_SPECIES(slotMon) != SPECIES_NONE && !OW_SHINY(slotMon)) @@ -202,10 +202,10 @@ u32 GetOldestSlot(void) static u8 NextSpawnMonSlot(void) { u32 spawnSlot; - u32 maxSpawns = GetMaxFollowMonSpawns(); + u32 maxSpawns = GetMaxOverworldEncounterSpawns(); // All mon slots are in use - if (CountActiveFollowMon() >= maxSpawns) + if (CountActiveOverworldEncounters() >= maxSpawns) { if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) { @@ -375,13 +375,13 @@ struct AgeSort static void SortOWEMonAges(void) { struct ObjectEvent *slotMon; - struct AgeSort array[FOLLOWMON_MAX_SPAWN_SLOTS]; + struct AgeSort array[OWE_MAX_SPAWN_SLOTS]; struct AgeSort current; - u32 numActive = CountActiveFollowMon(); + u32 numActive = CountActiveOverworldEncounters(); u32 count = 0; s32 i, j; - for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) + for (i = 0; i < OWE_MAX_SPAWN_SLOTS; i++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; if (OW_SPECIES(slotMon) != SPECIES_NONE) @@ -434,7 +434,7 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent return; } -u32 GetFollowMonObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) +u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) { SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level); u16 graphicsId = *speciesId + OBJ_EVENT_MON; @@ -513,10 +513,10 @@ static bool8 IsSafeToSpawnObjectEvents(void) return (player->currentCoords.x == player->previousCoords.x && player->currentCoords.y == player->previousCoords.y); } -u8 CountActiveFollowMon() +u8 CountActiveOverworldEncounters(void) { u32 count = 0; - for (u32 spawnSlot = 0; spawnSlot < FOLLOWMON_MAX_SPAWN_SLOTS; spawnSlot++) + for (u32 spawnSlot = 0; spawnSlot < OWE_MAX_SPAWN_SLOTS; spawnSlot++) { if (GetOverworldSpeciesBySpawnSlot(spawnSlot) != SPECIES_NONE) count++; @@ -580,14 +580,14 @@ bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent) { return IsOverworldWildEncounter(objectEvent) && (objectEvent->localId <= LOCALID_OW_ENCOUNTER_END - && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - FOLLOWMON_MAX_SPAWN_SLOTS)); + && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS)); } bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent) { return IsOverworldWildEncounter(objectEvent) && (objectEvent->localId > LOCALID_OW_ENCOUNTER_END - || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - FOLLOWMON_MAX_SPAWN_SLOTS)); + || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS)); } bool32 IsSemiManualOverworldWildEncounter(struct ObjectEvent *objectEvent) @@ -618,7 +618,7 @@ u32 GetNewestOWEncounterLocalId(void) struct ObjectEvent *newest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; u32 i; - for (i = 0; i < FOLLOWMON_MAX_SPAWN_SLOTS; i++) + for (i = 0; i < OWE_MAX_SPAWN_SLOTS; i++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; if (OW_SPECIES(slotMon) != SPECIES_NONE) @@ -635,8 +635,8 @@ bool32 CanRemoveOverworldEncounter(u32 localId) { // Can the last of these checks be replaced by IsOverworldWildEncounter? // Include a check for the encounter not being shiny or a roamer. - return (OW_WILD_ENCOUNTERS_OVERWORLD && CountActiveFollowMon() != 0 - && (localId <= (LOCALID_OW_ENCOUNTER_END - FOLLOWMON_MAX_SPAWN_SLOTS + 1) + return (OW_WILD_ENCOUNTERS_OVERWORLD && CountActiveOverworldEncounters() != 0 + && (localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS + 1) || localId > LOCALID_OW_ENCOUNTER_END)); } @@ -787,7 +787,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c gSpecialVar_LastTalked = wildMon->localId; gSpecialVar_0x8004 = OW_SPECIES(wildMon); - ScriptContext_SetupScript(InteractWithDynamicWildFollowMon); + ScriptContext_SetupScript(InteractWithDynamicWildOverworldEncounter); } } From 86f8d11ad766de9f750d6fc6439bf78730bdc30f Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 4 Dec 2025 17:14:58 -0600 Subject: [PATCH 147/572] Fixed GetOverworldSpeciesBySpawnSlot returning the wrong value --- src/overworld_encounters.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 0bb1ba8fe1a8..2c04a90f1778 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -599,6 +599,10 @@ static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) { u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; + + if (objEventId == OBJECT_EVENTS_COUNT) + return SPECIES_NONE; + return OW_SPECIES(objectEvent); } From 93471641c9845634aaee75590c79c7e05f53665a Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 4 Dec 2025 17:16:28 -0600 Subject: [PATCH 148/572] On battle start, OWE object gets removed after battle transition --- src/battle_setup.c | 7 +++++++ src/overworld_encounters.c | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/battle_setup.c b/src/battle_setup.c index 44f09ec40fec..853d2a35c7a6 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -29,6 +29,7 @@ #include "secret_base.h" #include "string_util.h" #include "overworld.h" +#include "overworld_encounters.h" #include "field_weather.h" #include "battle_tower.h" #include "gym_leader_rematch.h" @@ -252,6 +253,12 @@ static void Task_BattleStart(u8 taskId) case 1: if (IsBattleTransitionDone() == TRUE) { + if (gSpecialVar_LastTalked > LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS && gSpecialVar_LastTalked <= LOCALID_OW_ENCOUNTER_END) + { + RemoveObjectEvent(&gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]); + gSpecialVar_LastTalked = LOCALID_NONE; + } + PrepareForFollowerNPCBattle(); CleanupOverworldWindowsAndTilemaps(); SetMainCallback2(CB2_InitBattle); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 2c04a90f1778..4973d434b0bc 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -362,7 +362,6 @@ void CreateOverworldWildEncounter(void) isFemale ); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); - RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); BattleSetup_StartWildBattle(); } From 8261dcb4b41a88e147577d5e378fc9375ff460d1 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:10:21 -0600 Subject: [PATCH 149/572] Return early if `objectEventId` isn't valid --- src/overworld_encounters.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 4973d434b0bc..9a3d27bc5339 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -114,6 +114,12 @@ void UpdateOverworldEncounters(void) u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); + if (objectEventId == OBJECT_EVENTS_COUNT) + { + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + return; + } + gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; gObjectEvents[objectEventId].range.rangeX = OW_ENCOUNTER_MOVEMENT_RANGE_X; gObjectEvents[objectEventId].range.rangeY = OW_ENCOUNTER_MOVEMENT_RANGE_Y; From 19b08ec5083b17eb3396b4fbd598b27a7943923f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 6 Dec 2025 18:20:51 +0000 Subject: [PATCH 150/572] Wrapper Function to Remove OWE Object --- include/overworld_encounters.h | 1 + src/battle_setup.c | 7 +------ src/overworld_encounters.c | 11 +++++++++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 095f0b7204f0..8577707ed720 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -40,5 +40,6 @@ void RemoveOldestOverworldEncounter(u8 *objectEventId); bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); +void TryRemoveOverworldWildEncounter(u32 localId); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/battle_setup.c b/src/battle_setup.c index 853d2a35c7a6..e79f3d775755 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -253,12 +253,7 @@ static void Task_BattleStart(u8 taskId) case 1: if (IsBattleTransitionDone() == TRUE) { - if (gSpecialVar_LastTalked > LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS && gSpecialVar_LastTalked <= LOCALID_OW_ENCOUNTER_END) - { - RemoveObjectEvent(&gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]); - gSpecialVar_LastTalked = LOCALID_NONE; - } - + TryRemoveOverworldWildEncounter(gSpecialVar_LastTalked); PrepareForFollowerNPCBattle(); CleanupOverworldWindowsAndTilemaps(); SetMainCallback2(CB2_InitBattle); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 9a3d27bc5339..53b8a1f04ac6 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -800,5 +800,16 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c } } +void TryRemoveOverworldWildEncounter(u32 localId) +{ + struct ObjectEvent *object = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; + + if (IsOverworldWildEncounter(object)) + { + RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); + gSpecialVar_LastTalked = LOCALID_NONE; + } +} + #undef sOverworldEncounterLevel #undef sAge From 144558d15caec1c1cfc738abe2f89e59dbb40d0b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 6 Dec 2025 18:32:47 +0000 Subject: [PATCH 151/572] Change equal check to greater or equal --- src/overworld_encounters.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 53b8a1f04ac6..28d9792d2cb8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -114,7 +114,7 @@ void UpdateOverworldEncounters(void) u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); - if (objectEventId == OBJECT_EVENTS_COUNT) + if (objectEventId >= OBJECT_EVENTS_COUNT) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; return; @@ -605,7 +605,7 @@ static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; - if (objEventId == OBJECT_EVENTS_COUNT) + if (objEventId >= OBJECT_EVENTS_COUNT) return SPECIES_NONE; return OW_SPECIES(objectEvent); From 939e2952d672c7b90c7984a44d2fde6454dcb8f0 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 6 Dec 2025 18:40:14 +0000 Subject: [PATCH 152/572] Reduce Checks and Comment --- src/overworld_encounters.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 28d9792d2cb8..6cbe05f0294a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -66,6 +66,7 @@ void UpdateOverworldEncounters(void) u16 speciesId = SPECIES_NONE; bool32 isShiny = FALSE; bool32 isFemale = FALSE; + s16 x, y; if (sOWESpawnCountdown != 0) { @@ -73,8 +74,10 @@ void UpdateOverworldEncounters(void) return; } - s16 x, y; - if (GetActiveEncounterTable(OWE_ShouldSpawnWaterMons()) && IsSafeToSpawnObjectEvents() && TrySelectTile(&x, &y)) + if (!IsSafeToSpawnObjectEvents() || !TrySelectTile(&x, &y)) + return; + + if (GetActiveEncounterTable(OWE_ShouldSpawnWaterMons())) { u16 spawnSlot = NextSpawnMonSlot(); @@ -512,6 +515,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 static bool8 IsSafeToSpawnObjectEvents(void) { + // Can this just be a check for player not moving? struct ObjectEvent* player = &gObjectEvents[gPlayerAvatar.objectEventId]; // Only spawn when player is at a valid tile position From 867a49c3026cc24be0e995cfbd9654ea6f95890a Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 6 Dec 2025 20:38:57 -0600 Subject: [PATCH 153/572] Don't spawn mon if can't load palette --- include/event_object_movement.h | 1 + src/event_object_movement.c | 3 +-- src/overworld_encounters.c | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 5572fc7961be..3b30f1707e3b 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -266,6 +266,7 @@ const struct ObjectEventTemplate *GetObjectEventTemplateByLocalIdAndMap(u8 local u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemplate, u8 mapNum, u8 mapGroup, s16 cameraX, s16 cameraY); bool8 GetFollowerInfo(u32 *species, bool32 *shiny, bool32 *female); const struct ObjectEventGraphicsInfo *SpeciesToGraphicsInfo(u32 species, bool32 shiny, bool32 female); +u32 LoadDynamicFollowerPalette(u32 species, bool32 shiny, bool32 female); u16 GetObjectEventFlagIdByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup); bool32 AreElevationsCompatible(u32, u32); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 78226dd3cf9f..fe98d32a16d6 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -212,7 +212,6 @@ static u8 DoJumpSpriteMovement(struct Sprite *); static u8 DoJumpSpecialSpriteMovement(struct Sprite *); static void CreateLevitateMovementTask(struct ObjectEvent *); static void DestroyLevitateMovementTask(u8); -static u32 LoadDynamicFollowerPalette(u32 species, bool32 shiny, bool32 female); const struct ObjectEventGraphicsInfo *SpeciesToGraphicsInfo(u32 species, bool32 shiny, bool32 female); static bool8 NpcTakeStep(struct Sprite *); static void CopyObjectGraphicsInfoToSpriteTemplate_WithMovementType(u16 graphicsId, u16 movementType, struct SpriteTemplate *spriteTemplate, const struct SubspriteTable **subspriteTables); @@ -2070,7 +2069,7 @@ const struct ObjectEventGraphicsInfo *SpeciesToGraphicsInfo(u32 species, bool32 } // Find, or load, the palette for the specified pokemon info -static u32 LoadDynamicFollowerPalette(u32 species, bool32 shiny, bool32 female) +u32 LoadDynamicFollowerPalette(u32 species, bool32 shiny, bool32 female) { u32 paletteNum; // Use standalone palette, unless entry is OOB or NULL (fallback to front-sprite-based) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 9a3d27bc5339..cc217bf19b4f 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -112,6 +112,10 @@ void UpdateOverworldEncounters(void) .trainerType = TRAINER_TYPE_ENCOUNTER, }; + // If all palette slots are in use and the mon's palette isn't already loaded, don't spawn. + if (IndexOfSpritePaletteTag(TAG_NONE) == 0xFF && LoadDynamicFollowerPalette(speciesId, isShiny, isFemale) == 0xFF) + return; + u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); if (objectEventId == OBJECT_EVENTS_COUNT) From 7d115cabb1110e73f2b2448d3258a772737a4029 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 6 Dec 2025 22:18:37 -0600 Subject: [PATCH 154/572] Use return value instead of setting pointer --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 095f0b7204f0..3dd461f2f7cc 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -36,7 +36,7 @@ bool32 IsSemiManualOverworldWildEncounter(struct ObjectEvent *objectEvent); u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); -void RemoveOldestOverworldEncounter(u8 *objectEventId); +u32 RemoveOldestOverworldEncounter(void); bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index fe98d32a16d6..ceeb8261fec2 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1537,7 +1537,7 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * // Destroy the oldest OW Encounter mon to make room for the new object. if (*objectEventId >= OBJECT_EVENTS_COUNT && CanRemoveOverworldEncounter(localId)) - RemoveOldestOverworldEncounter(objectEventId); + *objectEventId = RemoveOldestOverworldEncounter(); /* Can we integrate this with this line above: if (i >= OBJECT_EVENTS_COUNT && !CanRemoveOverworldEncounter(localId)) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index cc217bf19b4f..fa6e05803bd5 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -653,26 +653,27 @@ bool32 CanRemoveOverworldEncounter(u32 localId) || localId > LOCALID_OW_ENCOUNTER_END)); } -void RemoveOldestOverworldEncounter(u8 *objectEventId) +u32 RemoveOldestOverworldEncounter(void) { // include a check for oldest slot not being INVALID_SPAWN_SLOT u32 localId = GetLocalIdByOverworldSpawnSlot(GetOldestSlot()); - *objectEventId = GetObjectEventIdByLocalId(localId); - struct ObjectEvent *object = &gObjectEvents[*objectEventId]; - s16 *fldEffSpriteId = &gSprites[gObjectEvents[*objectEventId].spriteId].data[6]; + u32 objectEventId = GetObjectEventIdByLocalId(localId); + struct ObjectEvent *object = &gObjectEvents[objectEventId]; + s16 *fldEffSpriteId = &gSprites[gObjectEvents[objectEventId].spriteId].data[6]; // Stop the associated field effect if it is active. if (*fldEffSpriteId != 0) FieldEffectStop(&gSprites[*fldEffSpriteId - 1], FLDEFF_BUBBLES); RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); + return objectEventId; } bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) { if (CanRemoveOverworldEncounter(localId)) { - RemoveOldestOverworldEncounter(objectEventId); + *objectEventId = RemoveOldestOverworldEncounter(); return FALSE; } From e993e578a6b9c240a64e587b12b72dd179042e98 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 6 Dec 2025 23:07:57 -0600 Subject: [PATCH 155/572] Despawn OWEs to make room for other palettes when needed --- include/sprite.h | 1 + src/overworld_encounters.c | 18 ++++++++++++++++-- src/sprite.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/include/sprite.h b/include/sprite.h index 791907505319..915ab1c41725 100644 --- a/include/sprite.h +++ b/include/sprite.h @@ -325,5 +325,6 @@ u32 GetSpanPerImage(u32 shape, u32 size); void RequestSpriteFrameImageCopy(u16 index, u16 tileNum, const struct SpriteFrameImage *images); void SetSpriteOamFlipBits(struct Sprite *sprite, u8 hFlip, u8 vFlip); u8 IndexOfSpriteTileTag(u16 tag); +u32 GetNumUnusedPalSlots(void); #endif //GUARD_SPRITE_H diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index fa6e05803bd5..71f349faf3c8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -87,6 +87,7 @@ void UpdateOverworldEncounters(void) bool32 waterMons = OWE_ShouldSpawnWaterMons(); bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); + u32 numOpenPalSlots = GetNumUnusedPalSlots(); u32 movementType, level; if (OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT) // These checks need to be improved { @@ -112,9 +113,22 @@ void UpdateOverworldEncounters(void) .trainerType = TRAINER_TYPE_ENCOUNTER, }; - // If all palette slots are in use and the mon's palette isn't already loaded, don't spawn. - if (IndexOfSpritePaletteTag(TAG_NONE) == 0xFF && LoadDynamicFollowerPalette(speciesId, isShiny, isFemale) == 0xFF) + // We need at least 2 pal slots open. One for the object and one for the spawn field effect. + if (numOpenPalSlots == 1) + { + u32 palTag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); + + if (isFemale && gSpeciesInfo[speciesId].overworldShinyPaletteFemale != NULL) + palTag += OBJ_EVENT_MON_FEMALE; + + // If the mon's palette isn't already loaded, don't spawn. + if (IndexOfSpritePaletteTag(palTag) == 0xFF) return; + } + else if (numOpenPalSlots == 0) + { + return; + } u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); diff --git a/src/sprite.c b/src/sprite.c index e670850779e1..535f68f1c62b 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -1,6 +1,7 @@ #include "global.h" #include "sprite.h" #include "main.h" +#include "overworld_encounters.h" #include "palette.h" #define MAX_SPRITE_COPY_REQUESTS 64 @@ -1592,6 +1593,21 @@ u32 LoadSpritePalette(const struct SpritePalette *palette) if (index != 0xFF) return index; + if (OW_WILD_ENCOUNTERS_OVERWORLD && GetNumUnusedPalSlots() < 2) + { + u32 count = CountActiveOverworldEncounters(); + + if (count > 0) + { + for (; count > 0; count--) + { + RemoveOldestOverworldEncounter(); + if (GetNumUnusedPalSlots() >= 2) + break; + } + } + } + index = IndexOfSpritePaletteTag(TAG_NONE); if (index == 0xFF) @@ -1798,3 +1814,15 @@ u32 GetSpanPerImage(u32 shape, u32 size) { return sSpanPerImage[shape][size]; } + +u32 GetNumUnusedPalSlots(void) +{ + u32 count = 0; + u32 i; + + for (i = gReservedSpritePaletteCount; i < 16; i++) + if (sSpritePaletteTags[i] == TAG_NONE) + count++; + + return count; +} From c547ed640e4447eb4a8d257fafeb6a2821399bf3 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 9 Dec 2025 12:39:41 -0600 Subject: [PATCH 156/572] Used fieldEffectSpriteId for field effect instead of sprite data --- src/event_object_movement.c | 2 +- src/field_effect_helpers.c | 7 ++----- src/overworld_encounters.c | 9 ++++++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index ceeb8261fec2..2f021cece0db 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11595,7 +11595,7 @@ bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim sp gFieldEffectArguments[2] = gSprites[objEvent->spriteId].oam.priority + 1; gFieldEffectArguments[3] = spawnAnimType; gFieldEffectArguments[4] = objEvent->spriteId; - FieldEffectStart(FLDEFF_BUBBLES); // Commandeer this field effect for the spawn anims + objEvent->fieldEffectSpriteId = FieldEffectStart(FLDEFF_BUBBLES) + 1; // Commandeer this field effect for the spawn anims return TRUE; } diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 611daf9900c5..277f39a7e2c3 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1517,11 +1517,8 @@ u32 FldEff_Bubbles(void) struct Sprite *sprite = &gSprites[spriteId]; sprite->coordOffsetEnabled = TRUE; sprite->oam.priority = 1; - - // Save the spriteId in the sprite data of the corresponding OW Encounter object. - gSprites[gFieldEffectArguments[4]].data[6] = spriteId + 1; } - return 0; + return spriteId; } #define sY data[0] @@ -1536,7 +1533,7 @@ void UpdateBubblesFieldEffect(struct Sprite *sprite) if (sprite->invisible || sprite->animEnded) { FieldEffectStop(sprite, FLDEFF_BUBBLES); - gSprites[gObjectEvents[GetNewestOWEncounterLocalId()].spriteId].data[6] = 0; + gObjectEvents[GetNewestOWEncounterLocalId()].fieldEffectSpriteId = 0; } } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 71f349faf3c8..021764fe5398 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -673,11 +673,14 @@ u32 RemoveOldestOverworldEncounter(void) u32 localId = GetLocalIdByOverworldSpawnSlot(GetOldestSlot()); u32 objectEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[objectEventId]; - s16 *fldEffSpriteId = &gSprites[gObjectEvents[objectEventId].spriteId].data[6]; + u32 fldEffSpriteId = object->fieldEffectSpriteId; // Stop the associated field effect if it is active. - if (*fldEffSpriteId != 0) - FieldEffectStop(&gSprites[*fldEffSpriteId - 1], FLDEFF_BUBBLES); + if (fldEffSpriteId != 0) + { + FieldEffectStop(&gSprites[fldEffSpriteId - 1], FLDEFF_BUBBLES); + object->fieldEffectSpriteId = 0; + } RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); return objectEventId; From 380accad8ecd7e329095459c8ed26f6517f89c35 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 12 Dec 2025 17:03:26 +0000 Subject: [PATCH 157/572] Name Changes & Comments --- include/overworld_encounters.h | 1 + include/sprite.h | 2 +- src/overworld_encounters.c | 28 +++++++++++++++++++++++++--- src/sprite.c | 21 +++------------------ 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 2fdb971386b0..75958bbef4c2 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -41,5 +41,6 @@ bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventI u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); void TryRemoveOverworldWildEncounter(u32 localId); +void DespawnOldestOWE_Pal(void); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/include/sprite.h b/include/sprite.h index 915ab1c41725..3f16788659d7 100644 --- a/include/sprite.h +++ b/include/sprite.h @@ -325,6 +325,6 @@ u32 GetSpanPerImage(u32 shape, u32 size); void RequestSpriteFrameImageCopy(u16 index, u16 tileNum, const struct SpriteFrameImage *images); void SetSpriteOamFlipBits(struct Sprite *sprite, u8 hFlip, u8 vFlip); u8 IndexOfSpriteTileTag(u16 tag); -u32 GetNumUnusedPalSlots(void); +u32 CountFreePaletteSlots(void); #endif //GUARD_SPRITE_H diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c0207725a9c5..32978adcd6f8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -90,7 +90,7 @@ void UpdateOverworldEncounters(void) bool32 waterMons = OWE_ShouldSpawnWaterMons(); bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); - u32 numOpenPalSlots = GetNumUnusedPalSlots(); + u32 numFreePalSlots = CountFreePaletteSlots(); u32 movementType, level; if (OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT) // These checks need to be improved { @@ -117,7 +117,8 @@ void UpdateOverworldEncounters(void) }; // We need at least 2 pal slots open. One for the object and one for the spawn field effect. - if (numOpenPalSlots == 1) + // Add this and tiles to seperate graphics check function + if (numFreePalSlots == 1) { u32 palTag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); @@ -127,8 +128,10 @@ void UpdateOverworldEncounters(void) // If the mon's palette isn't already loaded, don't spawn. if (IndexOfSpritePaletteTag(palTag) == 0xFF) return; + + // Add check if field effect pallete is already loaded } - else if (numOpenPalSlots == 0) + else if (numFreePalSlots == 0) { return; } @@ -837,5 +840,24 @@ void TryRemoveOverworldWildEncounter(u32 localId) } } +void DespawnOldestOWE_Pal(void) +{ + // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles + if (OW_WILD_ENCOUNTERS_OVERWORLD && CountFreePaletteSlots() < 2) + { + u32 count = CountActiveOverworldEncounters(); + + if (count > 0) + { + for (; count > 0; count--) + { + RemoveOldestOverworldEncounter(); + if (CountFreePaletteSlots() >= 2) + break; + } + } + } +} + #undef sOverworldEncounterLevel #undef sAge diff --git a/src/sprite.c b/src/sprite.c index 535f68f1c62b..fb2b6c0a15d8 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -1593,21 +1593,7 @@ u32 LoadSpritePalette(const struct SpritePalette *palette) if (index != 0xFF) return index; - if (OW_WILD_ENCOUNTERS_OVERWORLD && GetNumUnusedPalSlots() < 2) - { - u32 count = CountActiveOverworldEncounters(); - - if (count > 0) - { - for (; count > 0; count--) - { - RemoveOldestOverworldEncounter(); - if (GetNumUnusedPalSlots() >= 2) - break; - } - } - } - + DespawnOldestOWE_Pal(); index = IndexOfSpritePaletteTag(TAG_NONE); if (index == 0xFF) @@ -1815,10 +1801,9 @@ u32 GetSpanPerImage(u32 shape, u32 size) return sSpanPerImage[shape][size]; } -u32 GetNumUnusedPalSlots(void) +u32 CountFreePaletteSlots(void) { - u32 count = 0; - u32 i; + u32 i, count = 0; for (i = gReservedSpritePaletteCount; i < 16; i++) if (sSpritePaletteTags[i] == TAG_NONE) From 154292e53fdf81bb27dc44c09bd058fb97ca4cdc Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 12 Dec 2025 17:16:19 +0000 Subject: [PATCH 158/572] Decouple OW Spawn Anim from Bubbles Field Effect --- data/field_effect_scripts.s | 5 ++ include/constants/field_effects.h | 1 + src/event_object_movement.c | 5 +- src/field_effect_helpers.c | 89 ++++++++++++++++--------------- src/overworld_encounters.c | 3 +- 5 files changed, 58 insertions(+), 45 deletions(-) diff --git a/data/field_effect_scripts.s b/data/field_effect_scripts.s index 9050a2debd81..b174005184be 100644 --- a/data/field_effect_scripts.s +++ b/data/field_effect_scripts.s @@ -84,6 +84,7 @@ gFieldEffectScriptPointers:: .4byte gFieldEffectScript_UseRockClimb @ FLDEFF_USE_ROCK_CLIMB .4byte gFieldEffectScript_RockClimbDust @ FLDEFF_ROCK_CLIMB_DUST .4byte gFieldEffectScript_ORASDowse @ FLDEFF_ORAS_DOWSE + .4byte gFieldEffectScript_OWE_SpawnAnim @ FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_ANIM gFieldEffectScript_ExclamationMarkIcon1:: field_eff_callnative FldEff_ExclamationMarkIcon @@ -303,6 +304,10 @@ gFieldEffectScript_Bubbles:: field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect0, FldEff_Bubbles field_eff_end +gFieldEffectScript_OWE_SpawnAnim:: + field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect0, FldEff_OWE_SpawnAnim + field_eff_end + gFieldEffectScript_Sparkle:: field_eff_loadfadedpal_callnative gSpritePalette_SmallSparkle, FldEff_Sparkle field_eff_end diff --git a/include/constants/field_effects.h b/include/constants/field_effects.h index c3aadad320dd..51bfb6762765 100644 --- a/include/constants/field_effects.h +++ b/include/constants/field_effects.h @@ -80,6 +80,7 @@ #define FLDEFF_USE_ROCK_CLIMB 75 #define FLDEFF_ROCK_CLIMB_DUST 76 #define FLDEFF_ORAS_DOWSE 77 +#define FLDEFF_OW_ENCOUNTER_SPAWN_ANIM 78 #define FLDEFFOBJ_SHADOW_S 0 #define FLDEFFOBJ_SHADOW_M 1 diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 2f021cece0db..e4918b932abb 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11589,13 +11589,14 @@ u8 GetObjectEventApricornTreeId(u8 objectEventId) return gObjectEvents[objectEventId].trainerRange_berryTreeId; } -bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent) { +bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent) +{ gFieldEffectArguments[0] = objEvent->currentCoords.x; gFieldEffectArguments[1] = objEvent->currentCoords.y; gFieldEffectArguments[2] = gSprites[objEvent->spriteId].oam.priority + 1; gFieldEffectArguments[3] = spawnAnimType; gFieldEffectArguments[4] = objEvent->spriteId; - objEvent->fieldEffectSpriteId = FieldEffectStart(FLDEFF_BUBBLES) + 1; // Commandeer this field effect for the spawn anims + objEvent->fieldEffectSpriteId = FieldEffectStart(FLDEFF_OW_ENCOUNTER_SPAWN_ANIM); return TRUE; } diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 277f39a7e2c3..0b9196b2baaa 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1472,53 +1472,16 @@ void UpdateSandPileFieldEffect(struct Sprite *sprite) u32 FldEff_Bubbles(void) { u8 spriteId; - u8 visual; - //struct Sprite *sprite; - //struct SpriteTemplate template; - s16 xOffset, yOffset; - //u8 spriteData; - //u16 paletteNum = 255; - - switch (gFieldEffectArguments[3]) - { - case 0: // Grass spawn - visual = FLDEFFOBJ_JUMP_TALL_GRASS; - xOffset = 0; - yOffset = 8; - break; - - case 1: // Water spawn - visual = FLDEFFOBJ_JUMP_BIG_SPLASH; //FldEff_SecretPowerShrub - xOffset = 0; - yOffset = 0; - //spriteData = FLDEFF_WATER_SURFACING; - break; - - case 2: // Cave spawn - visual = FLDEFFOBJ_GROUND_IMPACT_DUST; - xOffset = 0; - yOffset = 8; - break; - - default: // Shiny spawn - visual = FLDEFFOBJ_SHINY_SPARKLE; - xOffset = 0; - yOffset = 0; - break; - } - - // RogueNote: This is hacky, calling through FldEff_Bubbles will expect this to go through FLDEFF_PAL_TAG_GENERAL_0, so override this here - //memcpy(&template, gFieldEffectObjectTemplatePointers[visual], sizeof(template)); SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 0); - spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[visual], gFieldEffectArguments[0] + xOffset, gFieldEffectArguments[1] + yOffset, 82); + spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[FLDEFFOBJ_BUBBLES], gFieldEffectArguments[0], gFieldEffectArguments[1], 82); if (spriteId != MAX_SPRITES) { struct Sprite *sprite = &gSprites[spriteId]; sprite->coordOffsetEnabled = TRUE; sprite->oam.priority = 1; } - return spriteId; + return 0; } #define sY data[0] @@ -1531,10 +1494,7 @@ void UpdateBubblesFieldEffect(struct Sprite *sprite) sprite->y -= sprite->sY >> 8; UpdateObjectEventSpriteInvisibility(sprite, FALSE); if (sprite->invisible || sprite->animEnded) - { FieldEffectStop(sprite, FLDEFF_BUBBLES); - gObjectEvents[GetNewestOWEncounterLocalId()].fieldEffectSpriteId = 0; - } } #undef sY @@ -1556,6 +1516,51 @@ u32 FldEff_BerryTreeGrowthSparkle(void) return spriteId; } +u32 FldEff_OWE_SpawnAnim(void) +{ + u8 spriteId; + u8 visual; + s16 xOffset, yOffset; + + switch (gFieldEffectArguments[3]) + { + case OWE_SPAWN_ANIM_GRASS: + visual = FLDEFFOBJ_JUMP_TALL_GRASS; + xOffset = 0; + yOffset = 8; + break; + + case OWE_SPAWN_ANIM_WATER: + visual = FLDEFFOBJ_JUMP_BIG_SPLASH; + xOffset = 0; + yOffset = 0; + break; + + case OWE_SPAWN_ANIM_CAVE: + visual = FLDEFFOBJ_GROUND_IMPACT_DUST; + xOffset = 0; + yOffset = 8; + break; + + case OWE_SPAWN_ANIM_SHINY: + default: + visual = FLDEFFOBJ_SHINY_SPARKLE; + xOffset = 0; + yOffset = 0; + break; + } + + SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 0); + spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[visual], gFieldEffectArguments[0] + xOffset, gFieldEffectArguments[1] + yOffset, 82); + if (spriteId != MAX_SPRITES) + { + struct Sprite *sprite = &gSprites[spriteId]; + sprite->coordOffsetEnabled = TRUE; + sprite->oam.priority = 1; + } + return spriteId; +} + // Sprite data for FLDEFF_TREE_DISGUISE / FLDEFF_MOUNTAIN_DISGUISE / FLDEFF_SAND_DISGUISE #define sState data[0] #define sFldEff data[1] diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 32978adcd6f8..3ee6b89f2b98 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -130,6 +130,7 @@ void UpdateOverworldEncounters(void) return; // Add check if field effect pallete is already loaded + // Bubbles field effect occurs on every movement } else if (numFreePalSlots == 0) { @@ -685,7 +686,7 @@ u32 RemoveOldestOverworldEncounter(void) // Stop the associated field effect if it is active. if (fldEffSpriteId != 0) { - FieldEffectStop(&gSprites[fldEffSpriteId - 1], FLDEFF_BUBBLES); + FieldEffectStop(&gSprites[fldEffSpriteId - 1], FLDEFF_OW_ENCOUNTER_SPAWN_ANIM); object->fieldEffectSpriteId = 0; } From fb28d90625d7d6166c57c4a074e12dcd5e485c1a Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 12 Dec 2025 23:52:43 +0000 Subject: [PATCH 159/572] Please Correct This If Needed --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 3ee6b89f2b98..fcab4d89bcb9 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -686,7 +686,7 @@ u32 RemoveOldestOverworldEncounter(void) // Stop the associated field effect if it is active. if (fldEffSpriteId != 0) { - FieldEffectStop(&gSprites[fldEffSpriteId - 1], FLDEFF_OW_ENCOUNTER_SPAWN_ANIM); + FieldEffectStop(&gSprites[fldEffSpriteId], FLDEFF_OW_ENCOUNTER_SPAWN_ANIM); object->fieldEffectSpriteId = 0; } From e21cd242a025f0b9ba7ef8998d26c4ddeaa597e2 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 13 Dec 2025 17:52:13 -0600 Subject: [PATCH 160/572] Killed some magic numbers in TrySelectTile --- include/overworld_encounters.h | 8 ++++++++ src/overworld_encounters.c | 11 +++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 3dd461f2f7cc..414bbbed4b3d 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -11,6 +11,14 @@ #define OWE_MAX_WATER_SPAWNS 5 #define OWE_MAX_CAVE_SPAWNS 4 +#define OWE_SPAWN_DISTANCE_LAND 1 // A spawn cannot happen within this many tiles of the player position. +#define OWE_SPAWN_DISTANCE_WATER 3 // A spawn cannot happen within this many tiles of the player position (while surfing). + +#define OWE_TOTAL_SPAWN_WIDTH 15 // Width of the on-screen spawn area in tiles. +#define OWE_TOTAL_SPAWN_HEIGHT 9 // Height of the on-screen spawn area in tiles. +#define OWE_SPAWN_RADUIS_WIDTH (OWE_TOTAL_SPAWN_WIDTH - 1) / 2 // Distance from center to left/right edge (not including center). +#define OWE_SPAWN_RADUIS_HEIGHT (OWE_TOTAL_SPAWN_HEIGHT - 1) / 2 // Distance from center to top/bottom edge (not including center). + #define OWE_TIME_BETWEEN_SPAWNS 180 // Minimum wait time (in frames) between spawns. #define OWE_SPAWN_TIME_VARIABILITY 60 // A random number of frames between 0 and this value will be added to OWE_TIME_BETWEEN_SPAWNS every reset for variability. #define OWE_SPAWN_TIME_MINIMUM 60 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 021764fe5398..e43ea9698080 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -263,7 +263,6 @@ static u8 NextSpawnMonSlot(void) return spawnSlot; } -// Magic Numbers static bool8 TrySelectTile(s16* outX, s16* outY) { u8 elevation; @@ -275,20 +274,20 @@ static bool8 TrySelectTile(s16* outX, s16* outY) // Spawn further away when surfing if (OWE_ShouldSpawnWaterMons()) - closeDistance = 3; + closeDistance = OWE_SPAWN_DISTANCE_WATER; else - closeDistance = 1; + closeDistance = OWE_SPAWN_DISTANCE_LAND; // Select a random tile in [-7, -4] [7, 4] range // Make sure is not directly next to player do { - x = (s16)(Random() % 15) - 7; - y = (s16)(Random() % 9) - 4; + x = (s16)(Random() % OWE_TOTAL_SPAWN_WIDTH) - OWE_SPAWN_RADUIS_WIDTH; + y = (s16)(Random() % OWE_TOTAL_SPAWN_HEIGHT) - OWE_SPAWN_RADUIS_HEIGHT; } while (abs(x) <= closeDistance && abs(y) <= closeDistance); - // We won't spawn mons in in the immediate facing direction + // We won't spawn mons in the immediate facing direction // (stops mons spawning in as I'm running in a straight line) switch (GetPlayerFacingDirection()) { From 37c080b825ac39b399f20d28f7aa92e024389f71 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 13 Dec 2025 18:12:23 -0600 Subject: [PATCH 161/572] Add check for INVALID_SPAWN_SLOT from RemoveOldestOverworldEncounter --- src/event_object_movement.c | 3 +++ src/overworld_encounters.c | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 2f021cece0db..3e62d79bb93c 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1539,6 +1539,9 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * if (*objectEventId >= OBJECT_EVENTS_COUNT && CanRemoveOverworldEncounter(localId)) *objectEventId = RemoveOldestOverworldEncounter(); + if (*objectEventId == OBJECT_EVENTS_COUNT) + return TRUE; + /* Can we integrate this with this line above: if (i >= OBJECT_EVENTS_COUNT && !CanRemoveOverworldEncounter(localId)) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e43ea9698080..ee4c9174b193 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -669,7 +669,12 @@ bool32 CanRemoveOverworldEncounter(u32 localId) u32 RemoveOldestOverworldEncounter(void) { // include a check for oldest slot not being INVALID_SPAWN_SLOT - u32 localId = GetLocalIdByOverworldSpawnSlot(GetOldestSlot()); + u32 oldestSlot = GetOldestSlot(); + + if (oldestSlot == INVALID_SPAWN_SLOT) + return OBJECT_EVENTS_COUNT; + + u32 localId = GetLocalIdByOverworldSpawnSlot(oldestSlot); u32 objectEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[objectEventId]; u32 fldEffSpriteId = object->fieldEffectSpriteId; From 69abdb55b55fb917f96fb3067031f6e5f037cb7e Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 13 Dec 2025 18:41:27 -0600 Subject: [PATCH 162/572] Used TryAndRemoveOldestOverworldEncounter in GetAvailableObjectEventId --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 34 ++-------------------------------- src/overworld_encounters.c | 8 ++++++-- 3 files changed, 9 insertions(+), 35 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 414bbbed4b3d..5b04903d5ae2 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -45,7 +45,7 @@ u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); u32 RemoveOldestOverworldEncounter(void); -bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); +bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 3e62d79bb93c..d8fa486bf259 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1523,44 +1523,14 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * if (gObjectEvents[i].localId == localId && gObjectEvents[i].mapNum == mapNum && gObjectEvents[i].mapGroup == mapGroup) return TRUE; } - - // Only if a generated Overworld Encounter cannot be removed. - if (i >= OBJECT_EVENTS_COUNT && !CanRemoveOverworldEncounter(localId)) - return TRUE; - + if (i >= OBJECT_EVENTS_COUNT) + return TryAndRemoveOldestOverworldEncounter(localId, objectEventId); *objectEventId = i; for (; i < OBJECT_EVENTS_COUNT; i++) { if (gObjectEvents[i].active && gObjectEvents[i].localId == localId && gObjectEvents[i].mapNum == mapNum && gObjectEvents[i].mapGroup == mapGroup) return TRUE; } - - // Destroy the oldest OW Encounter mon to make room for the new object. - if (*objectEventId >= OBJECT_EVENTS_COUNT && CanRemoveOverworldEncounter(localId)) - *objectEventId = RemoveOldestOverworldEncounter(); - - if (*objectEventId == OBJECT_EVENTS_COUNT) - return TRUE; - - /* Can we integrate this with this line above: - if (i >= OBJECT_EVENTS_COUNT && !CanRemoveOverworldEncounter(localId)) - - Something like: - if (i >= OBJECT_EVENTS_COUNT) - return TryAndRemoveOldestOverworldEncounter(localId); - - Where: - bool32 TryAndRemoveOldestOverworldEncounter(u32 localId) - { - if (CanRemoveOverworldEncounter(localId)) - { - RemoveOldestOverworldEncounter(); - return FALSE; - } - return TRUE; - } - */ - return FALSE; } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index ee4c9174b193..6abdda840c11 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -690,12 +690,16 @@ u32 RemoveOldestOverworldEncounter(void) return objectEventId; } -bool32 UNUSED TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) +bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) { if (CanRemoveOverworldEncounter(localId)) { *objectEventId = RemoveOldestOverworldEncounter(); - return FALSE; + + if (*objectEventId == OBJECT_EVENTS_COUNT) + return TRUE; + else + return FALSE; } return TRUE; From eb2e3a659a4a1a0ea073881f9b6220e1ae3b2798 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 14 Dec 2025 22:58:10 +0000 Subject: [PATCH 163/572] Consolidate Follower NPC Object Functions --- include/event_object_movement.h | 2 ++ src/event_object_movement.c | 57 +++++++++++++++++++++++++++++++++ src/follower_npc.c | 43 ++----------------------- src/overworld_encounters.c | 53 ------------------------------ 4 files changed, 61 insertions(+), 94 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 3b30f1707e3b..d42fd9e48883 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -269,6 +269,8 @@ const struct ObjectEventGraphicsInfo *SpeciesToGraphicsInfo(u32 species, bool32 u32 LoadDynamicFollowerPalette(u32 species, bool32 shiny, bool32 female); u16 GetObjectEventFlagIdByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup); bool32 AreElevationsCompatible(u32, u32); +u32 DetermineObjectEventDirectionFromObject(struct ObjectEvent *player, struct ObjectEvent *npc); +void ObjectEventTurnToObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo); void MovementType_None(struct Sprite *sprite); void MovementType_LookAround(struct Sprite *sprite); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 50c36de84437..2156ad517de6 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -9935,6 +9935,63 @@ bool32 AreElevationsCompatible(u32 a, u32 b) return TRUE; } +void ScriptFaceLastTalked(struct ScriptContext *ctx) +{ + struct ObjectEvent *player, *npc; + player = &gObjectEvents[gPlayerAvatar.objectEventId]; + npc = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; + ObjectEventTurnToObject(player, npc); +} + +u32 DetermineObjectEventDirectionFromObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo) +{ + s32 delta_x = objectTwo->currentCoords.x - objectOne->currentCoords.x; + s32 delta_y = objectTwo->currentCoords.y - objectOne->currentCoords.y; + + if (delta_x < 0) + return DIR_EAST; + else if (delta_x > 0) + return DIR_WEST; + + if (delta_y < 0) + return DIR_SOUTH; + else if (delta_y > 0) + return DIR_NORTH; + + return DIR_NONE; +} + +void ObjectEventTurnToObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo) +{ + u32 objectDirOne, objectDirTwo; + + if (objectTwo->invisible == FALSE) + { + objectDirOne = DetermineObjectEventDirectionFromObject(objectOne, objectTwo); + objectDirTwo = objectDirOne; + + //Flip direction. + switch (objectDirOne) + { + case DIR_NORTH: + objectDirOne = DIR_SOUTH; + break; + case DIR_SOUTH: + objectDirOne = DIR_NORTH; + break; + case DIR_WEST: + objectDirOne = DIR_EAST; + break; + case DIR_EAST: + objectDirOne = DIR_WEST; + break; + } + + ObjectEventTurn(objectOne, objectDirOne); + ObjectEventTurn(objectTwo, objectDirTwo); + } +} + void GroundEffect_SpawnOnTallGrass(struct ObjectEvent *objEvent, struct Sprite *sprite) { gFieldEffectArguments[0] = objEvent->currentCoords.x; diff --git a/src/follower_npc.c b/src/follower_npc.c index 90b766473346..f1e63eab1454 100644 --- a/src/follower_npc.c +++ b/src/follower_npc.c @@ -1361,20 +1361,7 @@ void FollowerNPC_HandleSprite(void) u32 DetermineFollowerNPCDirection(struct ObjectEvent *player, struct ObjectEvent *follower) { - s32 delta_x = follower->currentCoords.x - player->currentCoords.x; - s32 delta_y = follower->currentCoords.y - player->currentCoords.y; - - if (delta_x < 0) - return DIR_EAST; - else if (delta_x > 0) - return DIR_WEST; - - if (delta_y < 0) - return DIR_SOUTH; - else if (delta_y > 0) - return DIR_NORTH; - - return DIR_NONE; + return DetermineObjectEventDirectionFromObject(player, follower); } u32 GetFollowerNPCObjectId(void) @@ -1820,36 +1807,10 @@ void ScriptFaceFollowerNPC(struct ScriptContext *ctx) if (!FNPC_ENABLE_NPC_FOLLOWERS || !PlayerHasFollowerNPC()) return; - u32 playerDirection, followerDirection; struct ObjectEvent *player, *follower; player = &gObjectEvents[gPlayerAvatar.objectEventId]; follower = &gObjectEvents[GetFollowerNPCData(FNPC_DATA_OBJ_ID)]; - - if (follower->invisible == FALSE) - { - playerDirection = DetermineFollowerNPCDirection(player, follower); - followerDirection = playerDirection; - - //Flip direction. - switch (playerDirection) - { - case DIR_NORTH: - playerDirection = DIR_SOUTH; - break; - case DIR_SOUTH: - playerDirection = DIR_NORTH; - break; - case DIR_WEST: - playerDirection = DIR_EAST; - break; - case DIR_EAST: - playerDirection = DIR_WEST; - break; - } - - ObjectEventTurn(player, playerDirection); - ObjectEventTurn(follower, followerDirection); - } + ObjectEventTurnToObject(player, follower); } static const u8 *const FollowerNPCHideMovementsSpeedTable[][4] = diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 9137d52dcc67..29fa7948b55c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -765,59 +765,6 @@ u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent) return graphicsId; } -static u32 DetermineNPCDirection(struct ObjectEvent *player, struct ObjectEvent *npc) -{ - s32 delta_x = npc->currentCoords.x - player->currentCoords.x; - s32 delta_y = npc->currentCoords.y - player->currentCoords.y; - - if (delta_x < 0) - return DIR_EAST; - else if (delta_x > 0) - return DIR_WEST; - - if (delta_y < 0) - return DIR_SOUTH; - else if (delta_y > 0) - return DIR_NORTH; - - return DIR_NONE; -} - -void ScriptFaceLastTalked(struct ScriptContext *ctx) -{ - // Can be moved out of here and consolidated with FollowerNPC version of function. - u32 playerDirection, npcDirection; - struct ObjectEvent *player, *npc; - player = &gObjectEvents[gPlayerAvatar.objectEventId]; - npc = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; - - if (npc->invisible == FALSE) - { - playerDirection = DetermineNPCDirection(player, npc); - npcDirection = playerDirection; - - //Flip direction. - switch (playerDirection) - { - case DIR_NORTH: - playerDirection = DIR_SOUTH; - break; - case DIR_SOUTH: - playerDirection = DIR_NORTH; - break; - case DIR_WEST: - playerDirection = DIR_EAST; - break; - case DIR_EAST: - playerDirection = DIR_WEST; - break; - } - - ObjectEventTurn(player, playerDirection); - ObjectEventTurn(npc, npcDirection); - } -} - void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) { // The only automatically interacts with an OW Encounter when; From 546b10bdba9b58666b05560a212555b935305169 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 15 Dec 2025 18:40:57 +0000 Subject: [PATCH 164/572] Add Comment --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 29fa7948b55c..102e321e770d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -753,6 +753,7 @@ u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent) &level, personality ); + // Have a fallback incase of no header mons graphicsId = speciesId + OBJ_EVENT_MON; if (isFemale) From ae675e1e2adc1fe3ef37b7f0336db398184ed8aa Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 15 Dec 2025 18:57:12 +0000 Subject: [PATCH 165/572] Update Spawning of SemiOWEs to Template Level --- include/overworld_encounters.h | 4 ++-- src/event_object_movement.c | 21 +++++++++------------ src/overworld_encounters.c | 23 ++++++++++++++--------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 0cbda6e0c023..6cbf498c6a7b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -40,13 +40,13 @@ void RemoveAllOverworldEncounterObjects(void); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent); -bool32 IsSemiManualOverworldWildEncounter(struct ObjectEvent *objectEvent); +bool32 IsSemiManualOverworldWildEncounter(u32 graphicsId); u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); u32 RemoveOldestOverworldEncounter(void); bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); -u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent); +struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); void TryRemoveOverworldWildEncounter(u32 localId); void DespawnOldestOWE_Pal(void); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 2156ad517de6..fb9ef15586b4 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1431,14 +1431,14 @@ u8 GetObjectEventIdByLocalId(u8 localId) static u8 InitObjectEventStateFromTemplate(const struct ObjectEventTemplate *template, u8 mapNum, u8 mapGroup) { - struct ObjectEvent *objectEvent, *objectEventTemp; + struct ObjectEvent *objectEvent; u8 objectEventId; s16 x; s16 y; if (GetAvailableObjectEventId(template->localId, mapNum, mapGroup, &objectEventId)) return OBJECT_EVENTS_COUNT; - objectEvent = objectEventTemp = &gObjectEvents[objectEventId]; + objectEvent = &gObjectEvents[objectEventId]; ClearObjectEvent(objectEvent); x = template->x + MAP_OFFSET; y = template->y + MAP_OFFSET; @@ -1467,11 +1467,9 @@ static u8 InitObjectEventStateFromTemplate(const struct ObjectEventTemplate *tem objectEvent->previousElevation = template->elevation; objectEvent->range.rangeX = template->movementRangeX; objectEvent->range.rangeY = template->movementRangeY; - if (!IsSemiManualOverworldWildEncounter(objectEventTemp)) - objectEvent->trainerType = template->trainerType; + objectEvent->trainerType = template->trainerType; objectEvent->mapNum = mapNum; - if (!IsSemiManualOverworldWildEncounter(objectEventTemp)) - objectEvent->trainerRange_berryTreeId = template->trainerRange_berryTreeId; + objectEvent->trainerRange_berryTreeId = template->trainerRange_berryTreeId; objectEvent->previousMovementDirection = gInitialMovementTypeFacingDirections[template->movementType]; SetObjectEventDirection(objectEvent, objectEvent->previousMovementDirection); if (sMovementTypeHasRange[objectEvent->movementType]) @@ -1760,17 +1758,19 @@ static u8 TrySetupObjectEventSprite(const struct ObjectEventTemplate *objectEven u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemplate, u8 mapNum, u8 mapGroup, s16 cameraX, s16 cameraY) { u8 objectEventId; - u16 graphicsId = objectEventTemplate->graphicsId; struct SpriteTemplate spriteTemplate; struct SpriteFrameImage spriteFrameImage; const struct ObjectEventGraphicsInfo *graphicsInfo; const struct SubspriteTable *subspriteTables = NULL; + // May be a good idea to move the if check contained by this function to outside it for clarity. + struct ObjectEventTemplate objectEventTemplateTemp = TryGetObjectEventTemplateForOverworldEncounter(objectEventTemplate); + u16 graphicsId = objectEventTemplateTemp.graphicsId; graphicsInfo = GetObjectEventGraphicsInfo(graphicsId); - CopyObjectGraphicsInfoToSpriteTemplate_WithMovementType(graphicsId, objectEventTemplate->movementType, &spriteTemplate, &subspriteTables); + CopyObjectGraphicsInfoToSpriteTemplate_WithMovementType(graphicsId, objectEventTemplateTemp.movementType, &spriteTemplate, &subspriteTables); spriteFrameImage.size = graphicsInfo->size; spriteTemplate.images = &spriteFrameImage; - objectEventId = TrySetupObjectEventSprite(objectEventTemplate, &spriteTemplate, mapNum, mapGroup, cameraX, cameraY); + objectEventId = TrySetupObjectEventSprite(&objectEventTemplateTemp, &spriteTemplate, mapNum, mapGroup, cameraX, cameraY); if (objectEventId == OBJECT_EVENTS_COUNT) return OBJECT_EVENTS_COUNT; @@ -3092,9 +3092,6 @@ static void SetObjectEventDynamicGraphicsId(struct ObjectEvent *objectEvent) { if (objectEvent->graphicsId >= OBJ_EVENT_GFX_VARS && objectEvent->graphicsId <= OBJ_EVENT_GFX_VAR_F) objectEvent->graphicsId = VarGetObjectEventGraphicsId(objectEvent->graphicsId - OBJ_EVENT_GFX_VARS); - - if (IsSemiManualOverworldWildEncounter(objectEvent)) - objectEvent->graphicsId = GetGraphicsIdForOverworldEncounterGfx(objectEvent); } void SetObjectInvisibility(u8 localId, u8 mapNum, u8 mapGroup, bool8 invisible) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 102e321e770d..04a3cf0c381b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -620,9 +620,9 @@ bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent) || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS)); } -bool32 IsSemiManualOverworldWildEncounter(struct ObjectEvent *objectEvent) +bool32 IsSemiManualOverworldWildEncounter(u32 graphicsId) { - return objectEvent->graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER; + return graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER; } static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) @@ -727,10 +727,14 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) return FALSE; } -u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent) +struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) { + if (!IsSemiManualOverworldWildEncounter(template->graphicsId)) + return *template; + + struct ObjectEventTemplate templateOWE = *template; + // Does this work? - struct ObjectEventTemplate template = *GetObjectEventTemplateByLocalIdAndMap(objectEvent->localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); u32 graphicsId; u16 speciesId; bool32 isShiny = FALSE; @@ -742,8 +746,8 @@ u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent) enum TimeOfDay timeOfDay; SetOverworldEncounterSpeciesInfo_Helper( - template.x, - template.y, + template->x - MAP_OFFSET, + template->y - MAP_OFFSET, &encounterIndex, headerId, &timeOfDay, @@ -761,9 +765,10 @@ u16 GetGraphicsIdForOverworldEncounterGfx(struct ObjectEvent *objectEvent) if (isShiny) graphicsId += OBJ_EVENT_MON_SHINY; - objectEvent->trainerType = TRAINER_TYPE_ENCOUNTER; - objectEvent->sOverworldEncounterLevel = level; - return graphicsId; + templateOWE.graphicsId = graphicsId; + templateOWE.sOverworldEncounterLevel = level; + templateOWE.trainerType = TRAINER_TYPE_ENCOUNTER; + return templateOWE; } void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) From 5c87e08f26469a77b60819a9baf1f6fdf2737baa Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 15 Dec 2025 20:23:56 -0600 Subject: [PATCH 166/572] Removed comments --- src/overworld_encounters.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 04a3cf0c381b..a63de97f0ccc 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -667,7 +667,6 @@ u32 GetNewestOWEncounterLocalId(void) bool32 CanRemoveOverworldEncounter(u32 localId) { - // Can the last of these checks be replaced by IsOverworldWildEncounter? // Include a check for the encounter not being shiny or a roamer. return (OW_WILD_ENCOUNTERS_OVERWORLD && CountActiveOverworldEncounters() != 0 && (localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS + 1) @@ -676,7 +675,6 @@ bool32 CanRemoveOverworldEncounter(u32 localId) u32 RemoveOldestOverworldEncounter(void) { - // include a check for oldest slot not being INVALID_SPAWN_SLOT u32 oldestSlot = GetOldestSlot(); if (oldestSlot == INVALID_SPAWN_SLOT) From 986da3f31ea5cbdb85a03bc01423187b00024426 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 10:09:26 +0000 Subject: [PATCH 167/572] Commnet --- src/event_object_movement.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index fb9ef15586b4..c24449bd01ab 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -61,6 +61,7 @@ #define SPECIAL_LOCALIDS_START (min(LOCALID_CAMERA, \ min(LOCALID_PLAYER, \ LOCALID_BERRY_BLENDER_PLAYER_END - MAX_RFU_PLAYERS + 1))) + // Need an addition here? // The object event templates on a map cannot use the special IDs listed above or they can behave unexpectedly. // For more details on these special IDs see their definitions in 'include/constants/event_objects.h'. From d581affd67942c81af725b2046eed2231fcf0eb5 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 10:12:26 +0000 Subject: [PATCH 168/572] Add Randomised Spawn Facing Dirctions --- src/overworld_encounters.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index a63de97f0ccc..de81471fb20b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -150,6 +150,9 @@ void UpdateOverworldEncounters(void) gObjectEvents[objectEventId].range.rangeY = OW_ENCOUNTER_MOVEMENT_RANGE_Y; gObjectEvents[objectEventId].sOverworldEncounterLevel = level; + u8 directions[4] = {DIR_SOUTH, DIR_NORTH, DIR_WEST, DIR_EAST}; + ObjectEventTurn(&gObjectEvents[objectEventId], directions[Random() & 3]); + // Hide reflections for spawns in water // (It just looks weird) if (waterMons) From b015a3791590cb458648bbd0cb9abb146b90582f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 10:43:28 +0000 Subject: [PATCH 169/572] Consolidate OWE Movement Types --- include/config/overworld.h | 2 +- include/constants/event_object_movement.h | 6 +- include/event_object_movement.h | 8 +-- include/overworld_encounters.h | 1 + .../movement_action_func_tables.h | 24 +------ src/event_object_movement.c | 66 ++++--------------- src/overworld_encounters.c | 38 ++++++----- 7 files changed, 41 insertions(+), 104 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index d60876a3b286..210143e4086a 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -74,8 +74,8 @@ // Overworld Encounters #define OW_WILD_ENCOUNTERS_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. #define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. -#define OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will only use restricted movement types (MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER/MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER/MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER). #define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. +#define OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same metatile behavior as the one it spawned on. #define OW_ENCOUNTER_MOVEMENT_RANGE_X 8 // The horizontal movement range for OW encounter Pokémon. #define OW_ENCOUNTER_MOVEMENT_RANGE_Y 8 // The vertical movement range for OW encounter Pokémon. diff --git a/include/constants/event_object_movement.h b/include/constants/event_object_movement.h index 170d71cc5e02..2ca630ebd5cf 100755 --- a/include/constants/event_object_movement.h +++ b/include/constants/event_object_movement.h @@ -83,10 +83,8 @@ #define MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_LEFT 0x4F #define MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT 0x50 #define MOVEMENT_TYPE_FOLLOW_PLAYER 0x51 -#define MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER 0x52 -#define MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER 0x53 -#define MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER 0x54 -#define NUM_MOVEMENT_TYPES 0x55 +#define MOVEMENT_TYPE_WANDER_AROUND_OWE 0x52 +#define NUM_MOVEMENT_TYPES 0x53 #define MOVEMENT_ACTION_FACE_DOWN 0x0 #define MOVEMENT_ACTION_FACE_UP 0x1 diff --git a/include/event_object_movement.h b/include/event_object_movement.h index d42fd9e48883..665e0be71a8e 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -328,9 +328,7 @@ void MovementType_RunInPlace(struct Sprite *sprite); void MovementType_Invisible(struct Sprite *sprite); void MovementType_WalkSlowlyInPlace(struct Sprite *sprite); void MovementType_FollowPlayer(struct Sprite *sprite); -void MovementType_WanderOnLandEncounter(struct Sprite *sprite); -void MovementType_WanderOnWaterEncounter(struct Sprite *sprite); -void MovementType_WanderOnIndoorEncounter(struct Sprite *sprite); +void MovementType_WanderAround_OverworldWildEncounter(struct Sprite *sprite); u8 GetSlideMovementAction(u32); u8 GetJump2MovementAction(u32); u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); @@ -505,9 +503,7 @@ u8 MovementType_RunInPlace_Step0(struct ObjectEvent *objectEvent, struct Sprite u8 MovementType_Invisible_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_WanderOnWaterEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_WanderOnIndoorEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevation, u8 direction); void TurnVirtualObject(u8 virtualObjId, u8 direction); diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 6cbf498c6a7b..b31eae3531b3 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -49,6 +49,7 @@ bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); void TryRemoveOverworldWildEncounter(u32 localId); +bool32 OWE_CheckRestrictedMovement(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); void DespawnOldestOWE_Pal(void); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index 11747ca845d0..6ca8252c2123 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1719,32 +1719,12 @@ u8 (*const gMovementActionFuncs_WalkFastDiagonalDownRight[])(struct ObjectEvent MovementAction_PauseSpriteAnim, }; -u8 (*const gMovementTypeFuncs_WanderOnLandEncounter[])(struct ObjectEvent *, struct Sprite *) = { +u8 (*const gMovementTypeFuncs_WanderAround_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { MovementType_WanderAround_Step0, MovementType_WanderAround_Step1, MovementType_WanderAround_Step2, MovementType_Wander_Step3, - MovementType_WanderOnLandEncounter_Step4, - MovementType_WanderAround_Step5, - MovementType_WanderAround_Step6, -}; - -u8 (*const gMovementTypeFuncs_WanderOnWaterEncounter[])(struct ObjectEvent *, struct Sprite *) = { - MovementType_WanderAround_Step0, - MovementType_WanderAround_Step1, - MovementType_WanderAround_Step2, - MovementType_Wander_Step3, - MovementType_WanderOnWaterEncounter_Step4, - MovementType_WanderAround_Step5, - MovementType_WanderAround_Step6, -}; - -u8 (*const gMovementTypeFuncs_WanderOnIndoorEncounter[])(struct ObjectEvent *, struct Sprite *) = { - MovementType_WanderAround_Step0, - MovementType_WanderAround_Step1, - MovementType_WanderAround_Step2, - MovementType_Wander_Step3, - MovementType_WanderOnIndoorEncounter_Step4, + MovementType_WanderAround_OverworldWildEncounter_Step4, MovementType_WanderAround_Step5, MovementType_WanderAround_Step6, }; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index c24449bd01ab..c581817c25aa 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -346,9 +346,7 @@ static void (*const sMovementTypeCallbacks[])(struct Sprite *) = [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_LEFT] = MovementType_WalkSlowlyInPlace, [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT] = MovementType_WalkSlowlyInPlace, [MOVEMENT_TYPE_FOLLOW_PLAYER] = MovementType_FollowPlayer, - [MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER] = MovementType_WanderOnLandEncounter, - [MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER] = MovementType_WanderOnWaterEncounter, - [MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER] = MovementType_WanderOnIndoorEncounter, + [MOVEMENT_TYPE_WANDER_AROUND_OWE] = MovementType_WanderAround_OverworldWildEncounter, }; static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { @@ -393,9 +391,7 @@ static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_COPY_PLAYER_OPPOSITE_IN_GRASS] = TRUE, [MOVEMENT_TYPE_COPY_PLAYER_COUNTERCLOCKWISE_IN_GRASS] = TRUE, [MOVEMENT_TYPE_COPY_PLAYER_CLOCKWISE_IN_GRASS] = TRUE, - [MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER] = TRUE, - [MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER] = TRUE, - [MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER] = TRUE, + [MOVEMENT_TYPE_WANDER_AROUND_OWE] = TRUE, }; const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { @@ -481,9 +477,7 @@ const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_LEFT] = DIR_WEST, [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT] = DIR_EAST, [MOVEMENT_TYPE_FOLLOW_PLAYER] = DIR_SOUTH, - [MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER] = DIR_SOUTH, - [MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER] = DIR_SOUTH, - [MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER] = DIR_SOUTH, + [MOVEMENT_TYPE_WANDER_AROUND_OWE] = DIR_SOUTH, }; #include "data/object_events/object_event_graphics_info_pointers.h" @@ -11628,60 +11622,22 @@ bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim sp return TRUE; } -movement_type_def(MovementType_WanderOnLandEncounter, gMovementTypeFuncs_WanderOnLandEncounter) +movement_type_def(MovementType_WanderAround_OverworldWildEncounter, gMovementTypeFuncs_WanderAround_OverworldWildEncounter) -bool8 MovementType_WanderOnLandEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u8 directions[4]; u8 chosenDirection; - s16 x, y; + s16 xCurrent, yCurrent, xNew, yNew; memcpy(directions, gStandardDirections, sizeof directions); chosenDirection = directions[Random() & 3]; SetObjectEventDirection(objectEvent, chosenDirection); - x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; - y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; + xCurrent = objectEvent->currentCoords.x; + yCurrent = objectEvent->currentCoords.y; + xNew = xCurrent + gDirectionToVectors[chosenDirection].x; + yNew = yCurrent + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if (!MetatileBehavior_IsLandWildEncounter(MapGridGetMetatileBehaviorAt(x, y)) - || GetCollisionInDirection(objectEvent, chosenDirection)) - sprite->sTypeFuncId = 1; - - return TRUE; -} - -movement_type_def(MovementType_WanderOnWaterEncounter, gMovementTypeFuncs_WanderOnWaterEncounter) - -bool8 MovementType_WanderOnWaterEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) -{ - u8 directions[4]; - u8 chosenDirection; - s16 x, y; - memcpy(directions, gStandardDirections, sizeof directions); - chosenDirection = directions[Random() & 3]; - SetObjectEventDirection(objectEvent, chosenDirection); - x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; - y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; - sprite->sTypeFuncId = 5; - if (!MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(x, y)) - || GetCollisionInDirection(objectEvent, chosenDirection)) - sprite->sTypeFuncId = 1; - - return TRUE; -} - -movement_type_def(MovementType_WanderOnIndoorEncounter, gMovementTypeFuncs_WanderOnIndoorEncounter) - -bool8 MovementType_WanderOnIndoorEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) -{ - u8 directions[4]; - u8 chosenDirection; - s16 x, y; - memcpy(directions, gStandardDirections, sizeof directions); - chosenDirection = directions[Random() & 3]; - SetObjectEventDirection(objectEvent, chosenDirection); - x = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; - y = objectEvent->currentCoords.y + gDirectionToVectors[chosenDirection].y; - sprite->sTypeFuncId = 5; - if (!MetatileBehavior_IsIndoorEncounter(MapGridGetMetatileBehaviorAt(x, y)) + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(xCurrent, yCurrent, xNew, yNew)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index de81471fb20b..8fda0149d4e1 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -88,23 +88,9 @@ void UpdateOverworldEncounters(void) } bool32 waterMons = OWE_ShouldSpawnWaterMons(); - bool32 indoors = gMapHeader.mapType == MAP_TYPE_INDOOR; u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); u32 numFreePalSlots = CountFreePaletteSlots(); - u32 movementType, level; - if (OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT) // These checks need to be improved - { - if (waterMons) - movementType = MOVEMENT_TYPE_WANDER_ON_WATER_ENCOUNTER; - else if (indoors) - movementType = MOVEMENT_TYPE_WANDER_ON_INDOOR_ENCOUNTER; - else - movementType = MOVEMENT_TYPE_WANDER_ON_LAND_ENCOUNTER; - } - else - { - movementType = MOVEMENT_TYPE_WANDER_AROUND; - } + u32 level; struct ObjectEventTemplate objectEventTemplate = { .localId = localId, @@ -112,7 +98,7 @@ void UpdateOverworldEncounters(void) .x = x - MAP_OFFSET, .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), - .movementType = movementType, + .movementType = MOVEMENT_TYPE_WANDER_AROUND_OWE, .trainerType = TRAINER_TYPE_ENCOUNTER, }; @@ -803,6 +789,26 @@ void TryRemoveOverworldWildEncounter(u32 localId) } } +bool32 OWE_CheckRestrictedMovement(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew) +{ + u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); + u32 metatileBehaviourNew = MapGridGetMetatileBehaviorAt(xNew, yNew); + + if (MetatileBehavior_IsLandWildEncounter(metatileBehaviourCurrent) + && MetatileBehavior_IsLandWildEncounter(metatileBehaviourNew)) + return FALSE; + + if (MetatileBehavior_IsWaterWildEncounter(metatileBehaviourCurrent) + && MetatileBehavior_IsWaterWildEncounter(metatileBehaviourNew)) + return FALSE; + + if (MetatileBehavior_IsIndoorEncounter(metatileBehaviourCurrent) + && MetatileBehavior_IsIndoorEncounter(metatileBehaviourNew)) + return FALSE; + + return TRUE; +} + void DespawnOldestOWE_Pal(void) { // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles From b297cfd8668bc62844b34f574ec1fb11833ef788 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:31:42 +0000 Subject: [PATCH 170/572] Seperate Spawn Anims to seperate function --- include/overworld_encounters.h | 1 + src/overworld_encounters.c | 51 ++++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index b31eae3531b3..94c6589e9c22 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -45,6 +45,7 @@ u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); u32 RemoveOldestOverworldEncounter(void); +void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent); bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8fda0149d4e1..40fb3dc3a388 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -146,8 +146,6 @@ void UpdateOverworldEncounters(void) // Slower replacement spawning sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); - - enum OverworldEncounterSpawnAnim spawnAnimType; // Play spawn animation. if (speciesId == SPECIES_NONE) @@ -156,26 +154,37 @@ void UpdateOverworldEncounters(void) return; } - u32 pan = (Random() % 88) + 212; - u32 volume = (Random() % 30) + 50; - PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); - if (isShiny) - { - PlaySE(SE_SHINY); - spawnAnimType = OWE_SPAWN_ANIM_SHINY; - } - else - { - if (OWE_ShouldSpawnWaterMons()) - spawnAnimType = OWE_SPAWN_ANIM_WATER; - else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) - spawnAnimType = OWE_SPAWN_ANIM_CAVE; - else - spawnAnimType = OWE_SPAWN_ANIM_GRASS; - } - // Instantly play a small animation to ground the spawning a bit - MovementAction_OverworldEncounterSpawn(spawnAnimType, &gObjectEvents[objectEventId]); + OWE_DoSpawnAnim(&gObjectEvents[objectEventId]); + } +} + +void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) +{ + // Need to edit anims: + // If object is on water then use water anim. + // If object is indoor, need an indoor anim? + enum OverworldEncounterSpawnAnim spawnAnimType; + u32 speciesId = OW_SPECIES(objectEvent); + bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; + u32 pan = (Random() % 88) + 212; + u32 volume = (Random() % 30) + 50; + PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); + if (isShiny) + { + PlaySE(SE_SHINY); + spawnAnimType = OWE_SPAWN_ANIM_SHINY; + } + else + { + if (OWE_ShouldSpawnWaterMons()) + spawnAnimType = OWE_SPAWN_ANIM_WATER; + else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) + spawnAnimType = OWE_SPAWN_ANIM_CAVE; + else + spawnAnimType = OWE_SPAWN_ANIM_GRASS; } + // Instantly play a small animation to ground the spawning a bit + MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); } static u8 GetMaxOverworldEncounterSpawns(void) From 7e4d72e7719fbc0046455d80f11a9d5ff5fae92e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:36:07 +0000 Subject: [PATCH 171/572] Update IsSemiManualOverworldWildEncounter --- include/overworld_encounters.h | 2 +- src/overworld_encounters.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 94c6589e9c22..188b98746de0 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -40,7 +40,7 @@ void RemoveAllOverworldEncounterObjects(void); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent); -bool32 IsSemiManualOverworldWildEncounter(u32 graphicsId); +bool32 IsSemiManualOverworldWildEncounter(u32 graphicsId, u32 trainerType); u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 40fb3dc3a388..1f65f2de4044 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -618,9 +618,9 @@ bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent) || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS)); } -bool32 IsSemiManualOverworldWildEncounter(u32 graphicsId) +bool32 IsSemiManualOverworldWildEncounter(u32 graphicsId, u32 trainerType) { - return graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER; + return graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER && trainerType == TRAINER_TYPE_ENCOUNTER; } static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) @@ -725,7 +725,7 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) { - if (!IsSemiManualOverworldWildEncounter(template->graphicsId)) + if (!IsSemiManualOverworldWildEncounter(template->graphicsId, template->trainerType)) return *template; struct ObjectEventTemplate templateOWE = *template; From 273ff77489b764fa0e7f4c5ad2448ce3b207d650 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 12:35:18 +0000 Subject: [PATCH 172/572] Move Species None Check --- src/overworld_encounters.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 1f65f2de4044..80989ac14a57 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -102,6 +102,12 @@ void UpdateOverworldEncounters(void) .trainerType = TRAINER_TYPE_ENCOUNTER, }; + if (speciesId == SPECIES_NONE) + { + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + return; + } + // We need at least 2 pal slots open. One for the object and one for the spawn field effect. // Add this and tiles to seperate graphics check function if (numFreePalSlots == 1) @@ -147,13 +153,6 @@ void UpdateOverworldEncounters(void) // Slower replacement spawning sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); - // Play spawn animation. - if (speciesId == SPECIES_NONE) - { - sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - return; - } - OWE_DoSpawnAnim(&gObjectEvents[objectEventId]); } } From 283746fc5e6e1cb1181f3299eb686714dd50f86c Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 12:29:38 +0000 Subject: [PATCH 173/572] Add Comments --- include/overworld_encounters.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 188b98746de0..54484241876b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -25,6 +25,27 @@ #define INVALID_SPAWN_SLOT 0xFF +/* +Compile Errors when moving here +enum OverworldEncounterSpawnAnim +{ + OWE_SPAWN_ANIM_GRASS, + OWE_SPAWN_ANIM_WATER, + OWE_SPAWN_ANIM_CAVE, + OWE_SPAWN_ANIM_SHINY, +}; + +Combine OWE Type Checks into one function using these. +Need to figure a clean way to adjust SemiManual check as takes template. +enum OverworldObjectEncounterType +{ + OWE_NONE, + OWE_GENERATED, + OWE_SEMI_MANUAL, + OWE_MANUAL, +}; +*/ + extern const u8 InteractWithDynamicWildOverworldEncounter[]; void LoadOverworldEncounterData(void); From 176f2ebf08da2bf415e164bb30cd5ae377b639df Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 12:57:09 +0000 Subject: [PATCH 174/572] Update OnSpawn/Remove Functions --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 16 +++++++++------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 54484241876b..7028c6a26b5f 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -52,7 +52,7 @@ void LoadOverworldEncounterData(void); void UpdateOverworldEncounters(void); u32 GetOldestSlot(void); void CreateOverworldWildEncounter(void); -void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); +void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); void ClearOverworldEncounterData(void); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index c581817c25aa..21d15db6a688 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1773,7 +1773,7 @@ u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemp if (subspriteTables) SetSubspriteTables(&gSprites[gObjectEvents[objectEventId].spriteId], subspriteTables); - GeneratedOverworldWildEncounter_OnObjectEventSpawned(&gObjectEvents[objectEventId]); + OverworldWildEncounter_OnObjectEventSpawned(&gObjectEvents[objectEventId]); return objectEventId; } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 80989ac14a57..6b238b244787 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -446,18 +446,20 @@ static void SortOWEMonAges(void) } } -void GeneratedOverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent) +void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { - if (!IsGeneratedOverworldWildEncounter(objectEvent)) - return; - - SortOWEMonAges(); + if (IsGeneratedOverworldWildEncounter(objectEvent)) + SortOWEMonAges(); + + if (IsSemiManualOverworldWildEncounter(objectEvent->graphicsId, objectEvent->trainerType) + || IsManualOverworldWildEncounter(objectEvent)) + OWE_DoSpawnAnim(objectEvent); } void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { - if (!IsGeneratedOverworldWildEncounter(objectEvent)) - return; + // Currently Unused + if (IsGeneratedOverworldWildEncounter(objectEvent)) {} } u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) From 326b58790db6a24891a18734947900bc20cfb473 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:05:52 +0000 Subject: [PATCH 175/572] Move Spawn Anim to On Spawn Object Event Function --- src/overworld_encounters.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 6b238b244787..723f7f894091 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -152,8 +152,6 @@ void UpdateOverworldEncounters(void) // Slower replacement spawning sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); - - OWE_DoSpawnAnim(&gObjectEvents[objectEventId]); } } @@ -451,8 +449,7 @@ void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent if (IsGeneratedOverworldWildEncounter(objectEvent)) SortOWEMonAges(); - if (IsSemiManualOverworldWildEncounter(objectEvent->graphicsId, objectEvent->trainerType) - || IsManualOverworldWildEncounter(objectEvent)) + if (IsOverworldWildEncounter(objectEvent)) OWE_DoSpawnAnim(objectEvent); } From 512842d1806d0e2200fe4242c21c7b0678a01f85 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:20:42 +0000 Subject: [PATCH 176/572] Remove SetOverworldEncounterSpeciesInfo_Helper --- src/overworld_encounters.c | 59 +++++++++++--------------------------- 1 file changed, 17 insertions(+), 42 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 723f7f894091..babb3f7629d2 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -478,25 +478,29 @@ void ClearOverworldEncounterData(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } -static void SetOverworldEncounterSpeciesInfo_Helper(u32 x, u32 y, u32 *encounterIndex, u32 headerId, enum TimeOfDay *timeOfDay, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 personality) +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) { const struct WildPokemonInfo *wildMonInfo; + enum TimeOfDay timeOfDay; + u32 encounterIndex; + u32 personality = Random32(); + u32 headerId = GetCurrentMapWildMonHeaderId(); if (MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(x, y))) { - *encounterIndex = ChooseWildMonIndex_Water(); - *timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); - wildMonInfo = gWildMonHeaders[headerId].encounterTypes[*timeOfDay].waterMonsInfo; - *speciesId = wildMonInfo->wildPokemon[*encounterIndex].species; - *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, *encounterIndex, WILD_AREA_WATER); + encounterIndex = ChooseWildMonIndex_Water(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + *speciesId = wildMonInfo->wildPokemon[encounterIndex].species; + *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_WATER); } else { - *encounterIndex = ChooseWildMonIndex_Land(); - *timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - wildMonInfo = gWildMonHeaders[headerId].encounterTypes[*timeOfDay].landMonsInfo; - *speciesId = wildMonInfo->wildPokemon[*encounterIndex].species; - *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, *encounterIndex, WILD_AREA_LAND); + encounterIndex = ChooseWildMonIndex_Land(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + *speciesId = wildMonInfo->wildPokemon[encounterIndex].species; + *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_LAND); } if (*speciesId == SPECIES_UNOWN) @@ -509,27 +513,6 @@ static void SetOverworldEncounterSpeciesInfo_Helper(u32 x, u32 y, u32 *encounter *isFemale = FALSE; } -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) -{ - u32 headerId = GetCurrentMapWildMonHeaderId(); - u32 encounterIndex; - u32 personality = Random32(); - enum TimeOfDay timeOfDay; - - SetOverworldEncounterSpeciesInfo_Helper( - x, - y, - &encounterIndex, - headerId, - &timeOfDay, - speciesId, - isShiny, - isFemale, - level, - personality - ); -} - static bool8 IsSafeToSpawnObjectEvents(void) { // Can this just be a check for player not moving? @@ -734,22 +717,14 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const bool32 isShiny = FALSE; bool32 isFemale = FALSE; u32 level; - u32 headerId = GetCurrentMapWildMonHeaderId(); - u32 encounterIndex; - u32 personality = Random32(); - enum TimeOfDay timeOfDay; - SetOverworldEncounterSpeciesInfo_Helper( + SetOverworldEncounterSpeciesInfo( template->x - MAP_OFFSET, template->y - MAP_OFFSET, - &encounterIndex, - headerId, - &timeOfDay, &speciesId, &isShiny, &isFemale, - &level, - personality + &level ); // Have a fallback incase of no header mons From 289c10c19056bb742b9f4a20a0174758db82df4d Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:32:43 +0000 Subject: [PATCH 177/572] Add Comment --- src/overworld_encounters.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index babb3f7629d2..bda2ab35f967 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -503,6 +503,8 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_LAND); } + // Fallback for species here? Should it be earlier in code? + if (*speciesId == SPECIES_UNOWN) *speciesId = GetUnownSpeciesId(personality); From c5d11b3b4318839a672440da32d39d24535f3410 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:51:55 +0000 Subject: [PATCH 178/572] Add another comment --- src/overworld_encounters.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index bda2ab35f967..f0d6fa91b6b0 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -164,6 +164,8 @@ void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) u32 speciesId = OW_SPECIES(objectEvent); bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; u32 pan = (Random() % 88) + 212; + // Calculate location of mon to set pan value? + // Or at least calculate if object is left or right and randomise that range? u32 volume = (Random() % 30) + 50; PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); if (isShiny) From e14e524e3731efbe815424d82bf2cadc3ea653f1 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 14:19:35 +0000 Subject: [PATCH 179/572] Address Comment --- include/event_object_movement.h | 2 +- src/overworld_encounters.c | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 665e0be71a8e..4cf668754cc0 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -269,7 +269,7 @@ const struct ObjectEventGraphicsInfo *SpeciesToGraphicsInfo(u32 species, bool32 u32 LoadDynamicFollowerPalette(u32 species, bool32 shiny, bool32 female); u16 GetObjectEventFlagIdByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup); bool32 AreElevationsCompatible(u32, u32); -u32 DetermineObjectEventDirectionFromObject(struct ObjectEvent *player, struct ObjectEvent *npc); +u32 DetermineObjectEventDirectionFromObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo); void ObjectEventTurnToObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo); void MovementType_None(struct Sprite *sprite); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f0d6fa91b6b0..b914c7fec7c0 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -163,11 +163,24 @@ void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) enum OverworldEncounterSpawnAnim spawnAnimType; u32 speciesId = OW_SPECIES(objectEvent); bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; - u32 pan = (Random() % 88) + 212; - // Calculate location of mon to set pan value? - // Or at least calculate if object is left or right and randomise that range? u32 volume = (Random() % 30) + 50; + s32 pan; + + switch (DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent)) + { + case DIR_WEST: + pan = (Random() % 44); + break; + + case DIR_EAST: + pan = -(Random() % 44); + break; + + default: + pan = 0; + } PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); + if (isShiny) { PlaySE(SE_SHINY); @@ -182,7 +195,6 @@ void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) else spawnAnimType = OWE_SPAWN_ANIM_GRASS; } - // Instantly play a small animation to ground the spawning a bit MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); } From 78d9892d305d3ef1b156a3b6357eadc26a5d2217 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 14:22:38 +0000 Subject: [PATCH 180/572] Fix Messed Up Defines --- include/config/overworld.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 210143e4086a..e9578332222c 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -72,8 +72,8 @@ #define OW_FOLLOWERS_ALLOWED_MET_LOC (0) // Overworld Encounters -#define OW_WILD_ENCOUNTERS_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. -#define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. +#define OW_WILD_ENCOUNTERS_RANDOM FALSE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. +#define OW_WILD_ENCOUNTERS_OVERWORLD TRUE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. #define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. #define OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same metatile behavior as the one it spawned on. #define OW_ENCOUNTER_MOVEMENT_RANGE_X 8 // The horizontal movement range for OW encounter Pokémon. From c8e02704a0d7627456551cbeed7e2f18c6cebc17 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 14:23:54 +0000 Subject: [PATCH 181/572] Try Fix Defines Again --- include/config/overworld.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index e9578332222c..6570af503b44 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -72,10 +72,10 @@ #define OW_FOLLOWERS_ALLOWED_MET_LOC (0) // Overworld Encounters -#define OW_WILD_ENCOUNTERS_RANDOM FALSE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. -#define OW_WILD_ENCOUNTERS_OVERWORLD TRUE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. -#define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. +#define OW_WILD_ENCOUNTERS_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. +#define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. #define OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same metatile behavior as the one it spawned on. +#define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. #define OW_ENCOUNTER_MOVEMENT_RANGE_X 8 // The horizontal movement range for OW encounter Pokémon. #define OW_ENCOUNTER_MOVEMENT_RANGE_Y 8 // The vertical movement range for OW encounter Pokémon. From da6585e5f04219465088e70ec90c08fa41c2ddcb Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 14:27:09 +0000 Subject: [PATCH 182/572] Add comment --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index b914c7fec7c0..8f7faf06b5ea 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -180,6 +180,7 @@ void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) pan = 0; } PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); + // Also move this to a dedicated ambient cries function that plays spawned mon cries. if (isShiny) { From 93ea81e4e249a1ecd655905d4e328108910b3140 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 14:57:14 +0000 Subject: [PATCH 183/572] Create OWE_CanEncounterBeLoaded --- src/overworld_encounters.c | 50 +++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8f7faf06b5ea..137dbe5ac56a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -37,6 +37,7 @@ static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); static u32 GetSpawnSlotByLocalId(u32 localId); static void SortOWEMonAges(void); +static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny); void LoadOverworldEncounterData(void) { @@ -89,7 +90,6 @@ void UpdateOverworldEncounters(void) bool32 waterMons = OWE_ShouldSpawnWaterMons(); u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); - u32 numFreePalSlots = CountFreePaletteSlots(); u32 level; struct ObjectEventTemplate objectEventTemplate = { @@ -108,26 +108,8 @@ void UpdateOverworldEncounters(void) return; } - // We need at least 2 pal slots open. One for the object and one for the spawn field effect. - // Add this and tiles to seperate graphics check function - if (numFreePalSlots == 1) - { - u32 palTag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); - - if (isFemale && gSpeciesInfo[speciesId].overworldShinyPaletteFemale != NULL) - palTag += OBJ_EVENT_MON_FEMALE; - - // If the mon's palette isn't already loaded, don't spawn. - if (IndexOfSpritePaletteTag(palTag) == 0xFF) - return; - - // Add check if field effect pallete is already loaded - // Bubbles field effect occurs on every movement - } - else if (numFreePalSlots == 0) - { + if (!OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) return; - } u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); @@ -155,6 +137,34 @@ void UpdateOverworldEncounters(void) } } +static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny) +{ + u32 numFreePalSlots = CountFreePaletteSlots(); + + // We need at least 2 pal slots open. One for the object and one for the spawn field effect. + // Add this and tiles to seperate graphics check function + if (numFreePalSlots == 1) + { + u32 palTag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); + + if (isFemale && gSpeciesInfo[speciesId].overworldShinyPaletteFemale != NULL) + palTag += OBJ_EVENT_MON_FEMALE; + + // If the mon's palette isn't already loaded, don't spawn. + if (IndexOfSpritePaletteTag(palTag) == 0xFF) + return FALSE; + + // Add check if field effect pallete is already loaded + // Bubbles field effect occurs on every movement + } + else if (numFreePalSlots == 0) + { + return FALSE; + } + + return TRUE; +} + void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) { // Need to edit anims: From d3b474270522bb409eccc3533dceb4704b396beb Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 17 Dec 2025 23:59:44 +0000 Subject: [PATCH 184/572] Better reflect config --- src/event_object_movement.c | 13 +++++++------ src/overworld_encounters.c | 10 +++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 21d15db6a688..50188dc01982 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11628,16 +11628,17 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent { u8 directions[4]; u8 chosenDirection; - s16 xCurrent, yCurrent, xNew, yNew; + s16 xInitial, yInitial, xNew, yNew; memcpy(directions, gStandardDirections, sizeof directions); chosenDirection = directions[Random() & 3]; SetObjectEventDirection(objectEvent, chosenDirection); - xCurrent = objectEvent->currentCoords.x; - yCurrent = objectEvent->currentCoords.y; - xNew = xCurrent + gDirectionToVectors[chosenDirection].x; - yNew = yCurrent + gDirectionToVectors[chosenDirection].y; + xInitial = objectEvent->initialCoords.x; + yInitial = objectEvent->initialCoords.y; + xNew = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; + yNew = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(xCurrent, yCurrent, xNew, yNew)) + // Could add a flag for this check + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(xInitial, yInitial, xNew, yNew)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 137dbe5ac56a..1f439ebbd27d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -798,20 +798,20 @@ void TryRemoveOverworldWildEncounter(u32 localId) } } -bool32 OWE_CheckRestrictedMovement(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew) +bool32 OWE_CheckRestrictedMovement(s32 xInitial, s32 yInitial, s32 xNew, s32 yNew) { - u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); + u32 metatileBehaviourInitial = MapGridGetMetatileBehaviorAt(xInitial, yInitial); u32 metatileBehaviourNew = MapGridGetMetatileBehaviorAt(xNew, yNew); - if (MetatileBehavior_IsLandWildEncounter(metatileBehaviourCurrent) + if (MetatileBehavior_IsLandWildEncounter(metatileBehaviourInitial) && MetatileBehavior_IsLandWildEncounter(metatileBehaviourNew)) return FALSE; - if (MetatileBehavior_IsWaterWildEncounter(metatileBehaviourCurrent) + if (MetatileBehavior_IsWaterWildEncounter(metatileBehaviourInitial) && MetatileBehavior_IsWaterWildEncounter(metatileBehaviourNew)) return FALSE; - if (MetatileBehavior_IsIndoorEncounter(metatileBehaviourCurrent) + if (MetatileBehavior_IsIndoorEncounter(metatileBehaviourInitial) && MetatileBehavior_IsIndoorEncounter(metatileBehaviourNew)) return FALSE; From b088b261ed6268417bbee33935eae5ffffdda315 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 18 Dec 2025 00:04:10 +0000 Subject: [PATCH 185/572] Add comments on ambient cries --- src/overworld_encounters.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 1f439ebbd27d..8def0fca8e26 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -190,7 +190,8 @@ void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) pan = 0; } PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); - // Also move this to a dedicated ambient cries function that plays spawned mon cries. + // Also move this to a dedicated ambient cries function that plays spawned mon cries, when spawn timer gets to 0 but no new mons are spawned. + // Do a calculation of coordinate distance from player, north south edits volume, east west edits pan. if (isShiny) { From 52a66febfcecd2ace8d1c4c8571d89cb31a6f53e Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 18 Dec 2025 13:39:44 -0600 Subject: [PATCH 186/572] Revert "Better reflect config" This reverts commit d3b474270522bb409eccc3533dceb4704b396beb. --- src/event_object_movement.c | 13 ++++++------- src/overworld_encounters.c | 10 +++++----- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 50188dc01982..21d15db6a688 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11628,17 +11628,16 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent { u8 directions[4]; u8 chosenDirection; - s16 xInitial, yInitial, xNew, yNew; + s16 xCurrent, yCurrent, xNew, yNew; memcpy(directions, gStandardDirections, sizeof directions); chosenDirection = directions[Random() & 3]; SetObjectEventDirection(objectEvent, chosenDirection); - xInitial = objectEvent->initialCoords.x; - yInitial = objectEvent->initialCoords.y; - xNew = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].x; - yNew = objectEvent->currentCoords.x + gDirectionToVectors[chosenDirection].y; + xCurrent = objectEvent->currentCoords.x; + yCurrent = objectEvent->currentCoords.y; + xNew = xCurrent + gDirectionToVectors[chosenDirection].x; + yNew = yCurrent + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - // Could add a flag for this check - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(xInitial, yInitial, xNew, yNew)) + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(xCurrent, yCurrent, xNew, yNew)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8def0fca8e26..4115dc4cf59a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -799,20 +799,20 @@ void TryRemoveOverworldWildEncounter(u32 localId) } } -bool32 OWE_CheckRestrictedMovement(s32 xInitial, s32 yInitial, s32 xNew, s32 yNew) +bool32 OWE_CheckRestrictedMovement(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew) { - u32 metatileBehaviourInitial = MapGridGetMetatileBehaviorAt(xInitial, yInitial); + u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); u32 metatileBehaviourNew = MapGridGetMetatileBehaviorAt(xNew, yNew); - if (MetatileBehavior_IsLandWildEncounter(metatileBehaviourInitial) + if (MetatileBehavior_IsLandWildEncounter(metatileBehaviourCurrent) && MetatileBehavior_IsLandWildEncounter(metatileBehaviourNew)) return FALSE; - if (MetatileBehavior_IsWaterWildEncounter(metatileBehaviourInitial) + if (MetatileBehavior_IsWaterWildEncounter(metatileBehaviourCurrent) && MetatileBehavior_IsWaterWildEncounter(metatileBehaviourNew)) return FALSE; - if (MetatileBehavior_IsIndoorEncounter(metatileBehaviourInitial) + if (MetatileBehavior_IsIndoorEncounter(metatileBehaviourCurrent) && MetatileBehavior_IsIndoorEncounter(metatileBehaviourNew)) return FALSE; From 813563ff214861b15e01cd10c014e65b5baec980 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 18 Dec 2025 14:57:53 -0600 Subject: [PATCH 187/572] Consolidated OWE_CheckRestrictedMovement --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 7 +------ src/overworld_encounters.c | 6 +++++- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 7028c6a26b5f..f9eabbc0c667 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -71,7 +71,7 @@ bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); void TryRemoveOverworldWildEncounter(u32 localId); -bool32 OWE_CheckRestrictedMovement(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); +bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction); void DespawnOldestOWE_Pal(void); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 21d15db6a688..a8f89ea56b9f 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11628,16 +11628,11 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent { u8 directions[4]; u8 chosenDirection; - s16 xCurrent, yCurrent, xNew, yNew; memcpy(directions, gStandardDirections, sizeof directions); chosenDirection = directions[Random() & 3]; SetObjectEventDirection(objectEvent, chosenDirection); - xCurrent = objectEvent->currentCoords.x; - yCurrent = objectEvent->currentCoords.y; - xNew = xCurrent + gDirectionToVectors[chosenDirection].x; - yNew = yCurrent + gDirectionToVectors[chosenDirection].y; sprite->sTypeFuncId = 5; - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(xCurrent, yCurrent, xNew, yNew)) + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, chosenDirection)) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 4115dc4cf59a..8fe166cbe928 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -799,8 +799,12 @@ void TryRemoveOverworldWildEncounter(u32 localId) } } -bool32 OWE_CheckRestrictedMovement(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew) +bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction) { + s16 xCurrent = objectEvent->currentCoords.x; + s16 yCurrent = objectEvent->currentCoords.y; + s16 xNew = xCurrent + gDirectionToVectors[direction].x; + s16 yNew = yCurrent + gDirectionToVectors[direction].y; u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); u32 metatileBehaviourNew = MapGridGetMetatileBehaviorAt(xNew, yNew); From ba055d3aba9d5bfe8edf81717fd070ad3bcf0d93 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 18 Dec 2025 14:59:47 -0600 Subject: [PATCH 188/572] Added first draft of MOVEMENT_TYPE_CHASE_PLAYER_OWE --- include/constants/event_object_movement.h | 3 +- include/event_object_movement.h | 8 ++ include/overworld_encounters.h | 8 ++ .../movement_action_func_tables.h | 10 +- src/event_object_movement.c | 105 ++++++++++++++++++ src/overworld_encounters.c | 65 +++++++++++ 6 files changed, 197 insertions(+), 2 deletions(-) diff --git a/include/constants/event_object_movement.h b/include/constants/event_object_movement.h index 2ca630ebd5cf..740e362e60ec 100755 --- a/include/constants/event_object_movement.h +++ b/include/constants/event_object_movement.h @@ -84,7 +84,8 @@ #define MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT 0x50 #define MOVEMENT_TYPE_FOLLOW_PLAYER 0x51 #define MOVEMENT_TYPE_WANDER_AROUND_OWE 0x52 -#define NUM_MOVEMENT_TYPES 0x53 +#define MOVEMENT_TYPE_CHASE_PLAYER_OWE 0x53 +#define NUM_MOVEMENT_TYPES 0x54 #define MOVEMENT_ACTION_FACE_DOWN 0x0 #define MOVEMENT_ACTION_FACE_UP 0x1 diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 4cf668754cc0..b0e8d2c801cf 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -329,6 +329,7 @@ void MovementType_Invisible(struct Sprite *sprite); void MovementType_WalkSlowlyInPlace(struct Sprite *sprite); void MovementType_FollowPlayer(struct Sprite *sprite); void MovementType_WanderAround_OverworldWildEncounter(struct Sprite *sprite); +void MovementType_ChasePlayer_OverworldWildEncounter(struct Sprite *sprite); u8 GetSlideMovementAction(u32); u8 GetJump2MovementAction(u32); u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); @@ -503,7 +504,14 @@ u8 MovementType_RunInPlace_Step0(struct ObjectEvent *objectEvent, struct Sprite u8 MovementType_Invisible_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ChasePlayer_OverworldWildEncounter_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ChasePlayer_OverworldWildEncounter_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ChasePlayer_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ChasePlayer_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ChasePlayer_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ChasePlayer_OverworldWildEncounter_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevation, u8 direction); void TurnVirtualObject(u8 virtualObjId, u8 direction); diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index f9eabbc0c667..3c7d59421c18 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -23,6 +23,11 @@ #define OWE_SPAWN_TIME_VARIABILITY 60 // A random number of frames between 0 and this value will be added to OWE_TIME_BETWEEN_SPAWNS every reset for variability. #define OWE_SPAWN_TIME_MINIMUM 60 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. +#define OWE_MON_SIGHT_WIDTH 3 +#define OWE_MON_SIGHT_LENGTH 4 + +#define OWE_CHASE_RANGE 5 + #define INVALID_SPAWN_SLOT 0xFF /* @@ -73,5 +78,8 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c void TryRemoveOverworldWildEncounter(u32 localId); bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction); void DespawnOldestOWE_Pal(void); +bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon); +bool32 OWE_IsPlayerInsideChaseRange(struct ObjectEvent *mon); +u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index 6ca8252c2123..0321c1e5c64f 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1723,9 +1723,17 @@ u8 (*const gMovementTypeFuncs_WanderAround_OverworldWildEncounter[])(struct Obje MovementType_WanderAround_Step0, MovementType_WanderAround_Step1, MovementType_WanderAround_Step2, - MovementType_Wander_Step3, + MovementType_WanderAround_OverworldWildEncounter_Step3, MovementType_WanderAround_OverworldWildEncounter_Step4, MovementType_WanderAround_Step5, MovementType_WanderAround_Step6, }; +u8 (*const gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { + MovementType_ChasePlayer_OverworldWildEncounter_Step0, + MovementType_ChasePlayer_OverworldWildEncounter_Step1, + MovementType_ChasePlayer_OverworldWildEncounter_Step2, + MovementType_ChasePlayer_OverworldWildEncounter_Step3, + MovementType_ChasePlayer_OverworldWildEncounter_Step4, + MovementType_ChasePlayer_OverworldWildEncounter_Step5, +}; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index a8f89ea56b9f..de700fc99051 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -347,6 +347,7 @@ static void (*const sMovementTypeCallbacks[])(struct Sprite *) = [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT] = MovementType_WalkSlowlyInPlace, [MOVEMENT_TYPE_FOLLOW_PLAYER] = MovementType_FollowPlayer, [MOVEMENT_TYPE_WANDER_AROUND_OWE] = MovementType_WanderAround_OverworldWildEncounter, + [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = MovementType_ChasePlayer_OverworldWildEncounter, }; static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { @@ -478,6 +479,7 @@ const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT] = DIR_EAST, [MOVEMENT_TYPE_FOLLOW_PLAYER] = DIR_SOUTH, [MOVEMENT_TYPE_WANDER_AROUND_OWE] = DIR_SOUTH, + [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = DIR_SOUTH, }; #include "data/object_events/object_event_graphics_info_pointers.h" @@ -11624,6 +11626,30 @@ bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim sp movement_type_def(MovementType_WanderAround_OverworldWildEncounter, gMovementTypeFuncs_WanderAround_OverworldWildEncounter) +bool8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + if (WaitForMovementDelay(sprite)) + { + // resets a mid-movement sprite + ClearObjectEventMovement(objectEvent, sprite); + sprite->sTypeFuncId = 4; + return TRUE; + } + else + { + if (OW_MON_WANDER_WALK == TRUE && IS_OW_MON_OBJ(objectEvent)) + UpdateMonMoveInPlace(objectEvent, sprite); + + if (OWE_CanMonSeePlayer(objectEvent)) + { + SetTrainerMovementType(objectEvent, MOVEMENT_TYPE_CHASE_PLAYER_OWE); + sprite->sTypeFuncId = 0; + return FALSE; + } + } + return FALSE; +} + bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u8 directions[4]; @@ -11638,3 +11664,82 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent return TRUE; } + +movement_type_def(MovementType_ChasePlayer_OverworldWildEncounter, gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter) + +bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + ClearObjectEventMovement(objectEvent, sprite); + sprite->sTypeFuncId = 1; + return TRUE; +} + +bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); + + SetObjectEventDirection(objectEvent, direction); + ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK); + PlaySE(SE_PIN); + sprite->sTypeFuncId = 2; + return TRUE; +} + +bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) + { + sprite->sTypeFuncId = 3; + } + return TRUE; +} + +bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); + + SetObjectEventDirection(objectEvent, direction); + sprite->sTypeFuncId = 4; + return TRUE; +} + +bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + bool8 collision; + u8 movementActionId; + + collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); + movementActionId = GetWalkSlowMovementAction(objectEvent->movementDirection); + + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) + { + u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); + movementActionId = GetWalkSlowMovementAction(newDirection); + collision = GetCollisionInDirection(objectEvent, newDirection); + + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, newDirection)) || collision) + movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); + } + + ObjectEventSetSingleMovement(objectEvent, sprite, movementActionId); + objectEvent->singleMovementActive = TRUE; + sprite->sTypeFuncId = 5; + return TRUE; +} + +bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) + { + objectEvent->singleMovementActive = FALSE; + + if (!OWE_IsPlayerInsideChaseRange(objectEvent)) + { + SetTrainerMovementType(objectEvent, MOVEMENT_TYPE_WANDER_AROUND_OWE); + sprite->sTypeFuncId = 0; + return FALSE; + } + sprite->sTypeFuncId = 3; + } + return FALSE; +} diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8fe166cbe928..b53c05f9cc25 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -842,5 +842,70 @@ void DespawnOldestOWE_Pal(void) } } +bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon) +{ + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + + switch (mon->facingDirection) + { + case DIR_NORTH: + if (player->currentCoords.y < mon->currentCoords.y && (mon->currentCoords.y - player->currentCoords.y) <= OWE_MON_SIGHT_LENGTH + && player->currentCoords.x >= (mon->currentCoords.x - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.x <= (mon->currentCoords.x + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) + return TRUE; + break; + case DIR_SOUTH: + if (player->currentCoords.y > mon->currentCoords.y && (player->currentCoords.y - mon->currentCoords.y) <= OWE_MON_SIGHT_LENGTH + && player->currentCoords.x >= (mon->currentCoords.x - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.x <= (mon->currentCoords.x + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) + return TRUE; + break; + case DIR_EAST: + if (player->currentCoords.x > mon->currentCoords.x && (player->currentCoords.x - mon->currentCoords.x) <= OWE_MON_SIGHT_LENGTH + && player->currentCoords.y >= (mon->currentCoords.y - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.y <= (mon->currentCoords.y + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) + return TRUE; + break; + case DIR_WEST: + if (player->currentCoords.x < mon->currentCoords.x && (mon->currentCoords.x - player->currentCoords.x) <= OWE_MON_SIGHT_LENGTH + && player->currentCoords.y >= (mon->currentCoords.y - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.y <= (mon->currentCoords.y + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) + return TRUE; + break; + } + + return FALSE; +} + +bool32 OWE_IsPlayerInsideChaseRange(struct ObjectEvent *mon) +{ + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + + if (player->currentCoords.y <= mon->currentCoords.y + OWE_CHASE_RANGE && player->currentCoords.y >= mon->currentCoords.y - OWE_CHASE_RANGE + && player->currentCoords.x <= mon->currentCoords.x + OWE_CHASE_RANGE && player->currentCoords.x >= mon->currentCoords.x - OWE_CHASE_RANGE) + return TRUE; + + return FALSE; +} + +u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) +{ + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + + switch (mon->movementDirection) + { + case DIR_NORTH: + case DIR_SOUTH: + if (player->currentCoords.x < mon->currentCoords.x) + return DIR_WEST; + else + return DIR_EAST; + case DIR_EAST: + case DIR_WEST: + if (player->currentCoords.y < mon->currentCoords.y) + return DIR_NORTH; + else + return DIR_SOUTH; + } + + return mon->movementDirection; +} + #undef sOverworldEncounterLevel #undef sAge From 5fae74ea4a766494c94bea23a6cc196c5b8de84e Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 18 Dec 2025 15:44:21 -0600 Subject: [PATCH 189/572] Improved DetermineObjectEventDirectionFromObject --- src/event_object_movement.c | 48 +++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index de700fc99051..042e164ed1eb 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -9939,19 +9939,47 @@ void ScriptFaceLastTalked(struct ScriptContext *ctx) u32 DetermineObjectEventDirectionFromObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo) { - s32 delta_x = objectTwo->currentCoords.x - objectOne->currentCoords.x; - s32 delta_y = objectTwo->currentCoords.y - objectOne->currentCoords.y; + s16 absX, absY; + s16 distanceX = objectOne->currentCoords.x - objectTwo->currentCoords.x; + s16 distanceY = objectOne->currentCoords.y - objectTwo->currentCoords.y; - if (delta_x < 0) - return DIR_EAST; - else if (delta_x > 0) - return DIR_WEST; + // Get absolute X distance. + if (distanceX < 0) + absX = distanceX * -1; + else + absX = distanceX; - if (delta_y < 0) - return DIR_SOUTH; - else if (delta_y > 0) - return DIR_NORTH; + // Get absolute Y distance. + if (distanceY < 0) + absY = distanceY * -1; + else + absY = distanceY; + if (absX > absY) + { + if (distanceX < 0) + return DIR_WEST; + else + return DIR_EAST; + } + else + { + if (absX < absY) + { + if (distanceY < 0) + return DIR_NORTH; + else + return DIR_SOUTH; + } + if (absX == absY) + { + if (distanceY < 0) + return DIR_NORTH; + else + return DIR_SOUTH; + } + } + return DIR_NONE; } From 7c5c17aec37768e511677cd6c5087b5765d6d3bf Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 18 Dec 2025 19:51:59 -0600 Subject: [PATCH 190/572] Let movements finish before triggering the encounter --- include/overworld_encounters.h | 1 + src/event_object_movement.c | 12 ++++++++++++ src/overworld_encounters.c | 36 +++++++++++++++++++++++++++++++--- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 3c7d59421c18..96a305f208e2 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -81,5 +81,6 @@ void DespawnOldestOWE_Pal(void); bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon); bool32 OWE_IsPlayerInsideChaseRange(struct ObjectEvent *mon); u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); +void Task_OWE_WaitMovements(u8 taskId); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 042e164ed1eb..86e6c4ba9f57 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11741,6 +11741,18 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step4(struct ObjectEvent * if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) { + s16 x = objectEvent->currentCoords.x; + s16 y = objectEvent->currentCoords.y; + MoveCoords(objectEvent->movementDirection, &x, &y); + // If colliding with the player object, don't try to walk around it. + if (GetObjectEventIdByXY(x, y) == gPlayerAvatar.objectEventId) + { + objectEvent->movementActionId = MOVEMENT_ACTION_NONE; + sprite->sActionFuncId = 0; + objectEvent->singleMovementActive = FALSE; + sprite->sTypeFuncId = 3; + return FALSE; + } u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); movementActionId = GetWalkSlowMovementAction(newDirection); collision = GetCollisionInDirection(objectEvent, newDirection); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index b53c05f9cc25..835a839cdd8b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -12,6 +12,7 @@ #include "script.h" #include "sprite.h" #include "sound.h" +#include "task.h" #include "wild_encounter.h" #include "constants/event_objects.h" #include "constants/field_effects.h" @@ -782,9 +783,10 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c { struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; - gSpecialVar_LastTalked = wildMon->localId; - gSpecialVar_0x8004 = OW_SPECIES(wildMon); - ScriptContext_SetupScript(InteractWithDynamicWildOverworldEncounter); + LockPlayerFieldControls(); + // Wait for both the player and the mon to finish their current movements. + u8 taskId = CreateTask(Task_OWE_WaitMovements, 0); + gTasks[taskId].data[0] = wildMon->localId; } } @@ -907,5 +909,33 @@ u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) return mon->movementDirection; } +static bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon) +{ + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + + if ((mon->currentCoords.x != player->currentCoords.x && mon->currentCoords.y != player->currentCoords.y) || (mon->currentCoords.x < player->currentCoords.x - 1 || mon->currentCoords.x > player->currentCoords.x + 1 || mon->currentCoords.y < player->currentCoords.y - 1 || mon->currentCoords.y > player->currentCoords.y + 1)) + return FALSE; + + return TRUE; +} + +void Task_OWE_WaitMovements(u8 taskId) +{ + struct ObjectEvent *mon = &gObjectEvents[GetObjectEventIdByLocalId(gTasks[taskId].data[0])]; + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + + if (mon->singleMovementActive == 0 && player->singleMovementActive == 0) + { + // Let the mon continue to take steps until right next to the player. + if (OWE_IsMonNextToPlayer(mon)) + { + gSpecialVar_LastTalked = gTasks[taskId].data[0]; + gSpecialVar_0x8004 = OW_SPECIES(&gObjectEvents[GetObjectEventIdByLocalId(gTasks[taskId].data[0])]); + ScriptContext_SetupScript(InteractWithDynamicWildOverworldEncounter); + DestroyTask(taskId); + } + } +} + #undef sOverworldEncounterLevel #undef sAge From a5319e8b2a264e6bdd51a56cbd64f1cfa6fb1d6e Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 11:46:57 -0600 Subject: [PATCH 191/572] Fixed check for collision with player --- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 86e6c4ba9f57..57a5d8895910 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11745,7 +11745,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step4(struct ObjectEvent * s16 y = objectEvent->currentCoords.y; MoveCoords(objectEvent->movementDirection, &x, &y); // If colliding with the player object, don't try to walk around it. - if (GetObjectEventIdByXY(x, y) == gPlayerAvatar.objectEventId) + if (GetObjectObjectCollidesWith(objectEvent, x, y, FALSE) == gPlayerAvatar.objectEventId) { objectEvent->movementActionId = MOVEMENT_ACTION_NONE; sprite->sActionFuncId = 0; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 835a839cdd8b..da7486410624 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -779,7 +779,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle)); bool32 playerIsObstacle = (obstacle->isPlayer && IsOverworldWildEncounter(collider)); - if (playerIsCollider || playerIsObstacle) + if ((playerIsCollider || playerIsObstacle) && FindTaskIdByFunc(Task_OWE_WaitMovements) == TASK_NONE) { struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; From e87ce20fe2481faa61bd4f93a61a4870021c82c6 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 12:28:27 -0600 Subject: [PATCH 192/572] Don't do notice emote when right next to player --- include/overworld_encounters.h | 1 + src/event_object_movement.c | 11 +++++++---- src/overworld_encounters.c | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 96a305f208e2..592fe79f0eeb 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -81,6 +81,7 @@ void DespawnOldestOWE_Pal(void); bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon); bool32 OWE_IsPlayerInsideChaseRange(struct ObjectEvent *mon); u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); +bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); void Task_OWE_WaitMovements(u8 taskId); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 57a5d8895910..bfe1e5f139aa 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11671,7 +11671,6 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent if (OWE_CanMonSeePlayer(objectEvent)) { SetTrainerMovementType(objectEvent, MOVEMENT_TYPE_CHASE_PLAYER_OWE); - sprite->sTypeFuncId = 0; return FALSE; } } @@ -11707,15 +11706,19 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step1(struct ObjectEvent * u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); SetObjectEventDirection(objectEvent, direction); - ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK); - PlaySE(SE_PIN); + + if (!OWE_IsMonNextToPlayer(objectEvent)) + { + ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK); + PlaySE(SE_PIN); + } sprite->sTypeFuncId = 2; return TRUE; } bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) + if (!objectEvent->singleMovementActive || ObjectEventExecSingleMovementAction(objectEvent, sprite)) { sprite->sTypeFuncId = 3; } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index da7486410624..e88b03597fa3 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -909,7 +909,7 @@ u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) return mon->movementDirection; } -static bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon) +bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; From 6e0fa206d84f0c828180ce04076089e8d3487472 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 12:34:09 -0600 Subject: [PATCH 193/572] Simplify notice emote flow --- src/event_object_movement.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index bfe1e5f139aa..1181b8aace42 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11711,14 +11711,19 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step1(struct ObjectEvent * { ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK); PlaySE(SE_PIN); + sprite->sTypeFuncId = 2; } - sprite->sTypeFuncId = 2; + else + { + sprite->sTypeFuncId = 3; + } + return TRUE; } bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (!objectEvent->singleMovementActive || ObjectEventExecSingleMovementAction(objectEvent, sprite)) + if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { sprite->sTypeFuncId = 3; } From 241df82746e3e85455a423aa5ba7dab090db68d5 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 15:32:18 -0600 Subject: [PATCH 194/572] OWE mons cannot turn more than 90 degrees at a time --- src/event_object_movement.c | 38 +++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 1181b8aace42..af84fc322fbc 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11641,6 +11641,35 @@ u8 GetObjectEventApricornTreeId(u8 objectEventId) return gObjectEvents[objectEventId].trainerRange_berryTreeId; } +static u32 TurnDirectionNinetyDegrees(u32 direction, bool32 counterclockwise) +{ + switch (direction) + { + case DIR_NORTH: + if (counterclockwise) + return DIR_WEST; + else + return DIR_EAST; + case DIR_SOUTH: + if (counterclockwise) + return DIR_EAST; + else + return DIR_WEST; + case DIR_EAST: + if (counterclockwise) + return DIR_NORTH; + else + return DIR_SOUTH; + case DIR_WEST: + if (counterclockwise) + return DIR_SOUTH; + else + return DIR_NORTH; + } + + return DIR_NONE; +} + bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent) { gFieldEffectArguments[0] = objEvent->currentCoords.x; @@ -11679,10 +11708,11 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - u8 directions[4]; - u8 chosenDirection; - memcpy(directions, gStandardDirections, sizeof directions); - chosenDirection = directions[Random() & 3]; + u8 chosenDirection = objectEvent->movementDirection; + + if ((Random() & 3) != 0) + chosenDirection = TurnDirectionNinetyDegrees(chosenDirection, Random() & 2); + SetObjectEventDirection(objectEvent, chosenDirection); sprite->sTypeFuncId = 5; if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, chosenDirection)) From 01adfa80f8e8cfb994ec17ac4d8dfcfe7cf1f729 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 15:41:12 -0600 Subject: [PATCH 195/572] Added custom movement delays --- include/event_object_movement.h | 1 + src/data/object_events/movement_action_func_tables.h | 2 +- src/event_object_movement.c | 11 +++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index b0e8d2c801cf..f3d2a70e4c77 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -504,6 +504,7 @@ u8 MovementType_RunInPlace_Step0(struct ObjectEvent *objectEvent, struct Sprite u8 MovementType_Invisible_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_ChasePlayer_OverworldWildEncounter_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite); diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index 0321c1e5c64f..fa793184fc88 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1722,7 +1722,7 @@ u8 (*const gMovementActionFuncs_WalkFastDiagonalDownRight[])(struct ObjectEvent u8 (*const gMovementTypeFuncs_WanderAround_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { MovementType_WanderAround_Step0, MovementType_WanderAround_Step1, - MovementType_WanderAround_Step2, + MovementType_WanderAround_OverworldWildEncounter_Step2, MovementType_WanderAround_OverworldWildEncounter_Step3, MovementType_WanderAround_OverworldWildEncounter_Step4, MovementType_WanderAround_Step5, diff --git a/src/event_object_movement.c b/src/event_object_movement.c index af84fc322fbc..b89c92e9bc6e 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -760,6 +760,8 @@ static const s16 sMovementDelaysMedium[] = {32, 64, 96, 128}; static const s16 sMovementDelaysLong[] = {32, 64, 128, 192}; // Unused static const s16 sMovementDelaysShort[] = {32, 48, 64, 80}; +static const s16 sMovementDelaysOWE[] = {64, 80, 96, 128}; + #include "data/object_events/movement_type_func_tables.h" static const u8 sFaceDirectionAnimNums[] = { @@ -11683,6 +11685,15 @@ bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim sp movement_type_def(MovementType_WanderAround_OverworldWildEncounter, gMovementTypeFuncs_WanderAround_OverworldWildEncounter) +bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + if (!ObjectEventExecSingleMovementAction(objectEvent, sprite)) + return FALSE; + SetMovementDelay(sprite, sMovementDelaysOWE[Random() % ARRAY_COUNT(sMovementDelaysOWE)]); + sprite->sTypeFuncId = 3; + return TRUE; +} + bool8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (WaitForMovementDelay(sprite)) From 8c4865d9adbd18acb82e930a3e7481943636ca52 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:05:33 -0600 Subject: [PATCH 196/572] Mons notice from any direction if running or biking --- src/overworld_encounters.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e88b03597fa3..3568a5dfe559 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -848,6 +848,13 @@ bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) || TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE)) + { + if (player->currentCoords.y <= mon->currentCoords.y + OWE_MON_SIGHT_LENGTH && player->currentCoords.y >= mon->currentCoords.y - OWE_MON_SIGHT_LENGTH + && player->currentCoords.x <= mon->currentCoords.x + OWE_MON_SIGHT_LENGTH && player->currentCoords.x >= mon->currentCoords.x - OWE_MON_SIGHT_LENGTH) + return TRUE; + } + switch (mon->facingDirection) { case DIR_NORTH: From 7393df120838c9e6813381bd13b3eb8d57c45b73 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:10:14 -0600 Subject: [PATCH 197/572] Don't run walking logic if running or biking --- src/overworld_encounters.c | 46 ++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 3568a5dfe559..17e924e5c178 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -854,29 +854,31 @@ bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon) && player->currentCoords.x <= mon->currentCoords.x + OWE_MON_SIGHT_LENGTH && player->currentCoords.x >= mon->currentCoords.x - OWE_MON_SIGHT_LENGTH) return TRUE; } - - switch (mon->facingDirection) + else { - case DIR_NORTH: - if (player->currentCoords.y < mon->currentCoords.y && (mon->currentCoords.y - player->currentCoords.y) <= OWE_MON_SIGHT_LENGTH - && player->currentCoords.x >= (mon->currentCoords.x - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.x <= (mon->currentCoords.x + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) - return TRUE; - break; - case DIR_SOUTH: - if (player->currentCoords.y > mon->currentCoords.y && (player->currentCoords.y - mon->currentCoords.y) <= OWE_MON_SIGHT_LENGTH - && player->currentCoords.x >= (mon->currentCoords.x - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.x <= (mon->currentCoords.x + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) - return TRUE; - break; - case DIR_EAST: - if (player->currentCoords.x > mon->currentCoords.x && (player->currentCoords.x - mon->currentCoords.x) <= OWE_MON_SIGHT_LENGTH - && player->currentCoords.y >= (mon->currentCoords.y - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.y <= (mon->currentCoords.y + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) - return TRUE; - break; - case DIR_WEST: - if (player->currentCoords.x < mon->currentCoords.x && (mon->currentCoords.x - player->currentCoords.x) <= OWE_MON_SIGHT_LENGTH - && player->currentCoords.y >= (mon->currentCoords.y - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.y <= (mon->currentCoords.y + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) - return TRUE; - break; + switch (mon->facingDirection) + { + case DIR_NORTH: + if (player->currentCoords.y < mon->currentCoords.y && (mon->currentCoords.y - player->currentCoords.y) <= OWE_MON_SIGHT_LENGTH + && player->currentCoords.x >= (mon->currentCoords.x - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.x <= (mon->currentCoords.x + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) + return TRUE; + break; + case DIR_SOUTH: + if (player->currentCoords.y > mon->currentCoords.y && (player->currentCoords.y - mon->currentCoords.y) <= OWE_MON_SIGHT_LENGTH + && player->currentCoords.x >= (mon->currentCoords.x - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.x <= (mon->currentCoords.x + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) + return TRUE; + break; + case DIR_EAST: + if (player->currentCoords.x > mon->currentCoords.x && (player->currentCoords.x - mon->currentCoords.x) <= OWE_MON_SIGHT_LENGTH + && player->currentCoords.y >= (mon->currentCoords.y - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.y <= (mon->currentCoords.y + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) + return TRUE; + break; + case DIR_WEST: + if (player->currentCoords.x < mon->currentCoords.x && (mon->currentCoords.x - player->currentCoords.x) <= OWE_MON_SIGHT_LENGTH + && player->currentCoords.y >= (mon->currentCoords.y - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.y <= (mon->currentCoords.y + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) + return TRUE; + break; + } } return FALSE; From 35f7574cca3c7a2635881302e5262ed1eda84907 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:20:28 -0600 Subject: [PATCH 198/572] Bike only alerts when moving --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 17e924e5c178..e8b7ac4b19c5 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -848,7 +848,7 @@ bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) || TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE)) + if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING)) { if (player->currentCoords.y <= mon->currentCoords.y + OWE_MON_SIGHT_LENGTH && player->currentCoords.y >= mon->currentCoords.y - OWE_MON_SIGHT_LENGTH && player->currentCoords.x <= mon->currentCoords.x + OWE_MON_SIGHT_LENGTH && player->currentCoords.x >= mon->currentCoords.x - OWE_MON_SIGHT_LENGTH) From a26afffab1895acc6efed6526a3a2c0c6a0d1a54 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:22:35 -0600 Subject: [PATCH 199/572] Made OWE_IsPlayerInsideRange... function more broad --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 9 ++++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 592fe79f0eeb..b630370b0da1 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -79,7 +79,7 @@ void TryRemoveOverworldWildEncounter(u32 localId); bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction); void DespawnOldestOWE_Pal(void); bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon); -bool32 OWE_IsPlayerInsideChaseRange(struct ObjectEvent *mon); +bool32 OWE_IsPlayerInsideRangeFromMon(struct ObjectEvent *mon, u32 distance); u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); void Task_OWE_WaitMovements(u8 taskId); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index b89c92e9bc6e..80babe1cb306 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11822,7 +11822,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step5(struct ObjectEvent * { objectEvent->singleMovementActive = FALSE; - if (!OWE_IsPlayerInsideChaseRange(objectEvent)) + if (!OWE_IsPlayerInsideRangeFromMon(objectEvent, OWE_CHASE_RANGE)) { SetTrainerMovementType(objectEvent, MOVEMENT_TYPE_WANDER_AROUND_OWE); sprite->sTypeFuncId = 0; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e8b7ac4b19c5..55dd3f4b47fb 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -850,8 +850,7 @@ bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon) if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING)) { - if (player->currentCoords.y <= mon->currentCoords.y + OWE_MON_SIGHT_LENGTH && player->currentCoords.y >= mon->currentCoords.y - OWE_MON_SIGHT_LENGTH - && player->currentCoords.x <= mon->currentCoords.x + OWE_MON_SIGHT_LENGTH && player->currentCoords.x >= mon->currentCoords.x - OWE_MON_SIGHT_LENGTH) + if (OWE_IsPlayerInsideRangeFromMon(mon, OWE_MON_SIGHT_LENGTH)) return TRUE; } else @@ -884,12 +883,12 @@ bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon) return FALSE; } -bool32 OWE_IsPlayerInsideChaseRange(struct ObjectEvent *mon) +bool32 OWE_IsPlayerInsideRangeFromMon(struct ObjectEvent *mon, u32 distance) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - if (player->currentCoords.y <= mon->currentCoords.y + OWE_CHASE_RANGE && player->currentCoords.y >= mon->currentCoords.y - OWE_CHASE_RANGE - && player->currentCoords.x <= mon->currentCoords.x + OWE_CHASE_RANGE && player->currentCoords.x >= mon->currentCoords.x - OWE_CHASE_RANGE) + if (player->currentCoords.y <= mon->currentCoords.y + distance && player->currentCoords.y >= mon->currentCoords.y - distance + && player->currentCoords.x <= mon->currentCoords.x + distance && player->currentCoords.x >= mon->currentCoords.x - distance) return TRUE; return FALSE; From 0e94fd343cc1f41df1210943445b314518987c3c Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 17:56:25 -0600 Subject: [PATCH 200/572] Added wander steps inside behavior movement types --- include/event_object_movement.h | 12 +++---- .../movement_action_func_tables.h | 19 +++++++---- src/event_object_movement.c | 32 +++++++++---------- src/overworld_encounters.c | 2 +- 4 files changed, 36 insertions(+), 29 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index f3d2a70e4c77..ba00a74da74d 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -507,12 +507,12 @@ u8 MovementType_Invisible_Step2(struct ObjectEvent *objectEvent, struct Sprite * u8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ChasePlayer_OverworldWildEncounter_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ChasePlayer_OverworldWildEncounter_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ChasePlayer_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ChasePlayer_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ChasePlayer_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ChasePlayer_OverworldWildEncounter_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ChasePlayer_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ChasePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ChasePlayer_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ChasePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ChasePlayer_OverworldWildEncounter_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevation, u8 direction); void TurnVirtualObject(u8 virtualObjId, u8 direction); diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index fa793184fc88..29ad8237f197 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1730,10 +1730,17 @@ u8 (*const gMovementTypeFuncs_WanderAround_OverworldWildEncounter[])(struct Obje }; u8 (*const gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { - MovementType_ChasePlayer_OverworldWildEncounter_Step0, - MovementType_ChasePlayer_OverworldWildEncounter_Step1, - MovementType_ChasePlayer_OverworldWildEncounter_Step2, - MovementType_ChasePlayer_OverworldWildEncounter_Step3, - MovementType_ChasePlayer_OverworldWildEncounter_Step4, - MovementType_ChasePlayer_OverworldWildEncounter_Step5, + MovementType_WanderAround_Step0, + MovementType_WanderAround_Step1, + MovementType_WanderAround_OverworldWildEncounter_Step2, + MovementType_WanderAround_OverworldWildEncounter_Step3, + MovementType_WanderAround_OverworldWildEncounter_Step4, + MovementType_WanderAround_Step5, + MovementType_WanderAround_Step6, + MovementType_ChasePlayer_OverworldWildEncounter_Step7, + MovementType_ChasePlayer_OverworldWildEncounter_Step8, + MovementType_ChasePlayer_OverworldWildEncounter_Step9, + MovementType_ChasePlayer_OverworldWildEncounter_Step10, + MovementType_ChasePlayer_OverworldWildEncounter_Step11, + MovementType_ChasePlayer_OverworldWildEncounter_Step12, }; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 80babe1cb306..1e31d185eaa1 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -393,6 +393,7 @@ static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_COPY_PLAYER_COUNTERCLOCKWISE_IN_GRASS] = TRUE, [MOVEMENT_TYPE_COPY_PLAYER_CLOCKWISE_IN_GRASS] = TRUE, [MOVEMENT_TYPE_WANDER_AROUND_OWE] = TRUE, + [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = TRUE, }; const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { @@ -11710,7 +11711,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent if (OWE_CanMonSeePlayer(objectEvent)) { - SetTrainerMovementType(objectEvent, MOVEMENT_TYPE_CHASE_PLAYER_OWE); + sprite->sTypeFuncId = 7; return FALSE; } } @@ -11735,14 +11736,14 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent movement_type_def(MovementType_ChasePlayer_OverworldWildEncounter, gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter) -bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite) { ClearObjectEventMovement(objectEvent, sprite); - sprite->sTypeFuncId = 1; + sprite->sTypeFuncId = 8; return TRUE; } -bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); @@ -11752,35 +11753,35 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step1(struct ObjectEvent * { ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK); PlaySE(SE_PIN); - sprite->sTypeFuncId = 2; + sprite->sTypeFuncId = 9; } else { - sprite->sTypeFuncId = 3; + sprite->sTypeFuncId = 10; } return TRUE; } -bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { - sprite->sTypeFuncId = 3; + sprite->sTypeFuncId = 10; } return TRUE; } -bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); SetObjectEventDirection(objectEvent, direction); - sprite->sTypeFuncId = 4; + sprite->sTypeFuncId = 11; return TRUE; } -bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { bool8 collision; u8 movementActionId; @@ -11799,7 +11800,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step4(struct ObjectEvent * objectEvent->movementActionId = MOVEMENT_ACTION_NONE; sprite->sActionFuncId = 0; objectEvent->singleMovementActive = FALSE; - sprite->sTypeFuncId = 3; + sprite->sTypeFuncId = 10; return FALSE; } u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); @@ -11812,11 +11813,11 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step4(struct ObjectEvent * ObjectEventSetSingleMovement(objectEvent, sprite, movementActionId); objectEvent->singleMovementActive = TRUE; - sprite->sTypeFuncId = 5; + sprite->sTypeFuncId = 12; return TRUE; } -bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { @@ -11824,11 +11825,10 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step5(struct ObjectEvent * if (!OWE_IsPlayerInsideRangeFromMon(objectEvent, OWE_CHASE_RANGE)) { - SetTrainerMovementType(objectEvent, MOVEMENT_TYPE_WANDER_AROUND_OWE); sprite->sTypeFuncId = 0; return FALSE; } - sprite->sTypeFuncId = 3; + sprite->sTypeFuncId = 10; } return FALSE; } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 55dd3f4b47fb..887d324e3843 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -99,7 +99,7 @@ void UpdateOverworldEncounters(void) .x = x - MAP_OFFSET, .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), - .movementType = MOVEMENT_TYPE_WANDER_AROUND_OWE, + .movementType = MOVEMENT_TYPE_CHASE_PLAYER_OWE, .trainerType = TRAINER_TYPE_ENCOUNTER, }; From 39a4985e71b25d3704a93ac505dfb87ebab2f67b Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 21:06:25 -0600 Subject: [PATCH 201/572] Object faces player when directly adjacent within the movement step --- src/event_object_movement.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 1e31d185eaa1..128f8f7e5185 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11788,6 +11788,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkSlowMovementAction(objectEvent->movementDirection); + sprite->sTypeFuncId = 12; if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) { @@ -11797,10 +11798,8 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent // If colliding with the player object, don't try to walk around it. if (GetObjectObjectCollidesWith(objectEvent, x, y, FALSE) == gPlayerAvatar.objectEventId) { - objectEvent->movementActionId = MOVEMENT_ACTION_NONE; - sprite->sActionFuncId = 0; - objectEvent->singleMovementActive = FALSE; - sprite->sTypeFuncId = 10; + ObjectEventSetSingleMovement(objectEvent, sprite, GetFaceDirectionMovementAction(objectEvent->facingDirection)); + objectEvent->singleMovementActive = TRUE; return FALSE; } u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); @@ -11813,7 +11812,6 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent ObjectEventSetSingleMovement(objectEvent, sprite, movementActionId); objectEvent->singleMovementActive = TRUE; - sprite->sTypeFuncId = 12; return TRUE; } From d776b851cf1f0fcd047b26418a9e82f371964437 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 21:08:56 -0600 Subject: [PATCH 202/572] Freeze mon to prevent creating the task multiple times --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 887d324e3843..3f7bed3569e5 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -940,6 +940,7 @@ void Task_OWE_WaitMovements(u8 taskId) gSpecialVar_LastTalked = gTasks[taskId].data[0]; gSpecialVar_0x8004 = OW_SPECIES(&gObjectEvents[GetObjectEventIdByLocalId(gTasks[taskId].data[0])]); ScriptContext_SetupScript(InteractWithDynamicWildOverworldEncounter); + FreezeObjectEvent(mon); DestroyTask(taskId); } } From eadf62a4448e8f9fe405a07b16563952e6662a99 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 19 Dec 2025 22:36:30 -0600 Subject: [PATCH 203/572] Added MOVEMENT_TYPE_FLEE_PLAYER_OWE --- include/constants/event_object_movement.h | 3 +- include/event_object_movement.h | 9 ++- .../movement_action_func_tables.h | 20 ++++- src/event_object_movement.c | 79 ++++++++++++++++++- src/overworld_encounters.c | 32 ++++++-- 5 files changed, 131 insertions(+), 12 deletions(-) diff --git a/include/constants/event_object_movement.h b/include/constants/event_object_movement.h index 740e362e60ec..27202b505148 100755 --- a/include/constants/event_object_movement.h +++ b/include/constants/event_object_movement.h @@ -85,7 +85,8 @@ #define MOVEMENT_TYPE_FOLLOW_PLAYER 0x51 #define MOVEMENT_TYPE_WANDER_AROUND_OWE 0x52 #define MOVEMENT_TYPE_CHASE_PLAYER_OWE 0x53 -#define NUM_MOVEMENT_TYPES 0x54 +#define MOVEMENT_TYPE_FLEE_PLAYER_OWE 0x54 +#define NUM_MOVEMENT_TYPES 0x55 #define MOVEMENT_ACTION_FACE_DOWN 0x0 #define MOVEMENT_ACTION_FACE_UP 0x1 diff --git a/include/event_object_movement.h b/include/event_object_movement.h index ba00a74da74d..3f9feb16bbfd 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -330,6 +330,7 @@ void MovementType_WalkSlowlyInPlace(struct Sprite *sprite); void MovementType_FollowPlayer(struct Sprite *sprite); void MovementType_WanderAround_OverworldWildEncounter(struct Sprite *sprite); void MovementType_ChasePlayer_OverworldWildEncounter(struct Sprite *sprite); +void MovementType_FleePlayer_OverworldWildEncounter(struct Sprite *sprite); u8 GetSlideMovementAction(u32); u8 GetJump2MovementAction(u32); u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); @@ -507,12 +508,16 @@ u8 MovementType_Invisible_Step2(struct ObjectEvent *objectEvent, struct Sprite * u8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ChasePlayer_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_Common_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_ChasePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_ChasePlayer_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_ChasePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ChasePlayer_OverworldWildEncounter_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_Common_OverworldWildEncounter_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_FleePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_FleePlayer_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevation, u8 direction); void TurnVirtualObject(u8 virtualObjId, u8 direction); diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index 29ad8237f197..159b4f58adec 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1737,10 +1737,26 @@ u8 (*const gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter[])(struct Objec MovementType_WanderAround_OverworldWildEncounter_Step4, MovementType_WanderAround_Step5, MovementType_WanderAround_Step6, - MovementType_ChasePlayer_OverworldWildEncounter_Step7, + MovementType_Common_OverworldWildEncounter_Step7, MovementType_ChasePlayer_OverworldWildEncounter_Step8, MovementType_ChasePlayer_OverworldWildEncounter_Step9, MovementType_ChasePlayer_OverworldWildEncounter_Step10, MovementType_ChasePlayer_OverworldWildEncounter_Step11, - MovementType_ChasePlayer_OverworldWildEncounter_Step12, + MovementType_Common_OverworldWildEncounter_Step12, +}; + +u8 (*const gMovementTypeFuncs_FleePlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { + MovementType_WanderAround_Step0, + MovementType_WanderAround_Step1, + MovementType_WanderAround_OverworldWildEncounter_Step2, + MovementType_WanderAround_OverworldWildEncounter_Step3, + MovementType_WanderAround_OverworldWildEncounter_Step4, + MovementType_WanderAround_Step5, + MovementType_WanderAround_Step6, + MovementType_Common_OverworldWildEncounter_Step7, + MovementType_FleePlayer_OverworldWildEncounter_Step8, + MovementType_FleePlayer_OverworldWildEncounter_Step9, + MovementType_FleePlayer_OverworldWildEncounter_Step10, + MovementType_FleePlayer_OverworldWildEncounter_Step11, + MovementType_Common_OverworldWildEncounter_Step12, }; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 128f8f7e5185..8e90e198e8a6 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -348,6 +348,7 @@ static void (*const sMovementTypeCallbacks[])(struct Sprite *) = [MOVEMENT_TYPE_FOLLOW_PLAYER] = MovementType_FollowPlayer, [MOVEMENT_TYPE_WANDER_AROUND_OWE] = MovementType_WanderAround_OverworldWildEncounter, [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = MovementType_ChasePlayer_OverworldWildEncounter, + [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = MovementType_FleePlayer_OverworldWildEncounter, }; static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { @@ -394,6 +395,7 @@ static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_COPY_PLAYER_CLOCKWISE_IN_GRASS] = TRUE, [MOVEMENT_TYPE_WANDER_AROUND_OWE] = TRUE, [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = TRUE, + [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = TRUE, }; const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { @@ -481,6 +483,7 @@ const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_FOLLOW_PLAYER] = DIR_SOUTH, [MOVEMENT_TYPE_WANDER_AROUND_OWE] = DIR_SOUTH, [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = DIR_SOUTH, + [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = DIR_SOUTH, }; #include "data/object_events/object_event_graphics_info_pointers.h" @@ -11736,7 +11739,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent movement_type_def(MovementType_ChasePlayer_OverworldWildEncounter, gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter) -bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_Common_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite) { ClearObjectEventMovement(objectEvent, sprite); sprite->sTypeFuncId = 8; @@ -11815,7 +11818,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent return TRUE; } -bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_Common_OverworldWildEncounter_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { @@ -11830,3 +11833,75 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step12(struct ObjectEvent } return FALSE; } + +movement_type_def(MovementType_FleePlayer_OverworldWildEncounter, gMovementTypeFuncs_FleePlayer_OverworldWildEncounter) + +bool8 MovementType_FleePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 direction = GetOppositeDirection(DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent)); + + SetObjectEventDirection(objectEvent, direction); + + ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK); + PlaySE(SE_PIN); + sprite->sTypeFuncId = 9; + + return TRUE; +} + +bool8 MovementType_FleePlayer_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) + { + sprite->sTypeFuncId = 10; + } + return TRUE; +} + +#define sSpriteTaskState sprite->data[6] +#define STARTED 1 + +bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); + + if (sSpriteTaskState != STARTED) + { + direction = GetOppositeDirection(direction); + } + SetObjectEventDirection(objectEvent, direction); + sprite->sTypeFuncId = 11; + return TRUE; +} + +bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + bool8 collision; + u8 movementActionId; + + collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); + movementActionId = GetWalkNormalMovementAction(objectEvent->movementDirection); + + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) + { + u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); + + if (sSpriteTaskState != STARTED) + { + newDirection = GetOppositeDirection(newDirection); + } + movementActionId = GetWalkNormalMovementAction(newDirection); + collision = GetCollisionInDirection(objectEvent, newDirection); + + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, newDirection)) || collision) + movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); + } + + ObjectEventSetSingleMovement(objectEvent, sprite, movementActionId); + objectEvent->singleMovementActive = TRUE; + sprite->sTypeFuncId = 12; + return TRUE; +} + +#undef sSpriteTaskState +#undef STARTED diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 3f7bed3569e5..3803d7a5d0d2 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -99,7 +99,7 @@ void UpdateOverworldEncounters(void) .x = x - MAP_OFFSET, .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), - .movementType = MOVEMENT_TYPE_CHASE_PLAYER_OWE, + .movementType = MOVEMENT_TYPE_FLEE_PLAYER_OWE, .trainerType = TRAINER_TYPE_ENCOUNTER, }; @@ -769,6 +769,8 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const return templateOWE; } +#define tLocalId gTasks[taskId].data[0] + void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) { // The only automatically interacts with an OW Encounter when; @@ -786,7 +788,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c LockPlayerFieldControls(); // Wait for both the player and the mon to finish their current movements. u8 taskId = CreateTask(Task_OWE_WaitMovements, 0); - gTasks[taskId].data[0] = wildMon->localId; + tLocalId = wildMon->localId; } } @@ -904,12 +906,16 @@ u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) case DIR_SOUTH: if (player->currentCoords.x < mon->currentCoords.x) return DIR_WEST; + else if (player->currentCoords.x == mon->currentCoords.x) + return (Random() % 2) == 0 ? DIR_EAST : DIR_WEST; else return DIR_EAST; case DIR_EAST: case DIR_WEST: if (player->currentCoords.y < mon->currentCoords.y) return DIR_NORTH; + else if (player->currentCoords.y == mon->currentCoords.y) + return (Random() % 2) == 0 ? DIR_NORTH : DIR_SOUTH; else return DIR_SOUTH; } @@ -927,18 +933,29 @@ bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon) return TRUE; } +#define tTaskStarted gTasks[taskId].data[1] +#define sSpriteTaskState gSprites[mon->spriteId].data[6] +#define NOT_STARTED 0 +#define STARTED 1 + void Task_OWE_WaitMovements(u8 taskId) { - struct ObjectEvent *mon = &gObjectEvents[GetObjectEventIdByLocalId(gTasks[taskId].data[0])]; + struct ObjectEvent *mon = &gObjectEvents[GetObjectEventIdByLocalId(tLocalId)]; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + + if (tTaskStarted == NOT_STARTED) + { + sSpriteTaskState = STARTED; + tTaskStarted = STARTED; + } if (mon->singleMovementActive == 0 && player->singleMovementActive == 0) { // Let the mon continue to take steps until right next to the player. if (OWE_IsMonNextToPlayer(mon)) { - gSpecialVar_LastTalked = gTasks[taskId].data[0]; - gSpecialVar_0x8004 = OW_SPECIES(&gObjectEvents[GetObjectEventIdByLocalId(gTasks[taskId].data[0])]); + gSpecialVar_LastTalked = tLocalId; + gSpecialVar_0x8004 = OW_SPECIES(&gObjectEvents[GetObjectEventIdByLocalId(tLocalId)]); ScriptContext_SetupScript(InteractWithDynamicWildOverworldEncounter); FreezeObjectEvent(mon); DestroyTask(taskId); @@ -946,5 +963,10 @@ void Task_OWE_WaitMovements(u8 taskId) } } +#undef tLocalId +#undef tTaskStarted +#undef sSpriteTaskState +#undef NOT_STARTED +#undef STARTED #undef sOverworldEncounterLevel #undef sAge From b35f3d265c33f6c56cd6ed3b085121668135088c Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 20 Dec 2025 14:14:20 -0600 Subject: [PATCH 204/572] Improved DetermineObjectEventDirectionFromObject for better pathfinding --- src/event_object_movement.c | 40 +++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 8e90e198e8a6..54a24a833c8f 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -9980,9 +9980,45 @@ u32 DetermineObjectEventDirectionFromObject(struct ObjectEvent *objectOne, struc if (absX == absY) { if (distanceY < 0) - return DIR_NORTH; + { + if (objectTwo->movementDirection == DIR_NORTH) + return DIR_NORTH; + + if (distanceX < 0) + { + if (objectTwo->movementDirection == DIR_WEST) + return DIR_WEST; + else + return (Random() % 2) == 0 ? DIR_NORTH : DIR_WEST; + } + else + { + if (objectTwo->movementDirection == DIR_EAST) + return DIR_EAST; + else + return (Random() % 2) == 0 ? DIR_NORTH : DIR_EAST; + } + } else - return DIR_SOUTH; + { + if (objectTwo->movementDirection == DIR_SOUTH) + return DIR_SOUTH; + + if (distanceX < 0) + { + if (objectTwo->movementDirection == DIR_WEST) + return DIR_WEST; + else + return (Random() % 2) == 0 ? DIR_SOUTH : DIR_WEST; + } + else + { + if (objectTwo->movementDirection == DIR_EAST) + return DIR_EAST; + else + return (Random() % 2) == 0 ? DIR_SOUTH : DIR_EAST; + } + } } } From 877c34f498e4479fc68ad6aa089b8ac41f5ac404 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 20 Dec 2025 14:16:29 -0600 Subject: [PATCH 205/572] Simplified check to else --- src/event_object_movement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 54a24a833c8f..d30adb46ac32 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -9977,7 +9977,7 @@ u32 DetermineObjectEventDirectionFromObject(struct ObjectEvent *objectOne, struc else return DIR_SOUTH; } - if (absX == absY) + else { if (distanceY < 0) { From 71b59ca94cf11789f2e0981dbb4be5ba971c5dce Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 21 Dec 2025 00:32:55 -0600 Subject: [PATCH 206/572] Added config for fleeing mons to despawn if stuck --- include/config/overworld.h | 1 + include/overworld_encounters.h | 3 +++ src/event_object_movement.c | 22 +++++++++++++++++++++- src/overworld_encounters.c | 10 ++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 6570af503b44..a3cca93d9fa1 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -76,6 +76,7 @@ #define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. #define OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same metatile behavior as the one it spawned on. #define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. +#define OW_WILD_ENCOUNTERS_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. #define OW_ENCOUNTER_MOVEMENT_RANGE_X 8 // The horizontal movement range for OW encounter Pokémon. #define OW_ENCOUNTER_MOVEMENT_RANGE_Y 8 // The vertical movement range for OW encounter Pokémon. diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index b630370b0da1..7ee786de874b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -28,6 +28,8 @@ #define OWE_CHASE_RANGE 5 +#define OWE_FLEE_COLLISION_TIME 6 // If a fleeing mon is unable to take a step for this many tries it will despawn. (Multiply this value by 16 to get number of frames.) + #define INVALID_SPAWN_SLOT 0xFF /* @@ -83,5 +85,6 @@ bool32 OWE_IsPlayerInsideRangeFromMon(struct ObjectEvent *mon, u32 distance); u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); void Task_OWE_WaitMovements(u8 taskId); +u32 OWE_GetDespawnAnimType(u32 metatileBehavior); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index d30adb46ac32..600c80d30752 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11718,7 +11718,6 @@ bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim sp gFieldEffectArguments[1] = objEvent->currentCoords.y; gFieldEffectArguments[2] = gSprites[objEvent->spriteId].oam.priority + 1; gFieldEffectArguments[3] = spawnAnimType; - gFieldEffectArguments[4] = objEvent->spriteId; objEvent->fieldEffectSpriteId = FieldEffectStart(FLDEFF_OW_ENCOUNTER_SPAWN_ANIM); return TRUE; } @@ -11894,11 +11893,20 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step9(struct ObjectEvent *o return TRUE; } +#define sCollisionTimer sprite->data[4] #define sSpriteTaskState sprite->data[6] #define STARTED 1 bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { + if (OW_WILD_ENCOUNTERS_FLEE_DESPAWN && sCollisionTimer >= OWE_FLEE_COLLISION_TIME) + { + u32 animType = OWE_GetDespawnAnimType(objectEvent->currentMetatileBehavior); + MovementAction_OverworldEncounterSpawn(animType, objectEvent); + RemoveObjectEvent(objectEvent); + return FALSE; + } + u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); if (sSpriteTaskState != STARTED) @@ -11930,7 +11938,18 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * collision = GetCollisionInDirection(objectEvent, newDirection); if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, newDirection)) || collision) + { + sCollisionTimer++; movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); + } + else + { + sCollisionTimer = 0; + } + } + else + { + sCollisionTimer = 0; } ObjectEventSetSingleMovement(objectEvent, sprite, movementActionId); @@ -11939,5 +11958,6 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * return TRUE; } +#undef sCollisionTimer #undef sSpriteTaskState #undef STARTED diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 3803d7a5d0d2..308f03c30ceb 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -963,6 +963,16 @@ void Task_OWE_WaitMovements(u8 taskId) } } +u32 OWE_GetDespawnAnimType(u32 metatileBehavior) +{ + if (MetatileBehavior_IsPokeGrass(metatileBehavior) || MetatileBehavior_IsAshGrass(metatileBehavior)) + return OWE_SPAWN_ANIM_GRASS; + else if (MetatileBehavior_IsSurfableFishableWater(metatileBehavior)) + return OWE_SPAWN_ANIM_WATER; + else + return OWE_SPAWN_ANIM_CAVE; +} + #undef tLocalId #undef tTaskStarted #undef sSpriteTaskState From 588e26d0b05a33c77f96c52527f72337854bb20b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 12:36:22 +0000 Subject: [PATCH 207/572] Add Species Based OWE Bahviours --- include/overworld_encounters.h | 8 ++++++++ include/pokemon.h | 2 ++ src/overworld_encounters.c | 31 +++++++++++++++++++++++-------- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 7ee786de874b..879fccba0bd6 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -53,6 +53,14 @@ enum OverworldObjectEncounterType }; */ +enum OverworldEncounterBehaviors +{ + OWE_BEHAVIOR_WANDER_AROUND, + OWE_BEHAVIOR_CHASE, + OWE_BEHAVIOR_FLEE, + OWE_BEHAVIOR_COUNT +}; + extern const u8 InteractWithDynamicWildOverworldEncounter[]; void LoadOverworldEncounterData(void); diff --git a/include/pokemon.h b/include/pokemon.h index b58b24d241d4..d563c041a681 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -2,6 +2,7 @@ #define GUARD_POKEMON_H #include "contest_effect.h" +#include "overworld_encounters.h" #include "sprite.h" #include "constants/battle.h" #include "constants/cries.h" @@ -536,6 +537,7 @@ struct SpeciesInfo /*0xC4*/ #endif //P_GENDER_DIFFERENCES #endif //OW_PKMN_OBJECTS_SHARE_PALETTES #endif //OW_POKEMON_OBJECT_EVENTS + enum OverworldEncounterBehaviors overworldEncounterBehavior; }; struct AbilityInfo diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 308f03c30ceb..5184f165f624 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -39,6 +39,14 @@ static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); static u32 GetSpawnSlotByLocalId(u32 localId); static void SortOWEMonAges(void); static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny); +static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId); + +static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = +{ + [OWE_BEHAVIOR_WANDER_AROUND] = MOVEMENT_TYPE_WANDER_AROUND_OWE, + [OWE_BEHAVIOR_CHASE] = MOVEMENT_TYPE_CHASE_PLAYER_OWE, + [OWE_BEHAVIOR_FLEE] = MOVEMENT_TYPE_FLEE_PLAYER_OWE, +}; void LoadOverworldEncounterData(void) { @@ -92,23 +100,24 @@ void UpdateOverworldEncounters(void) bool32 waterMons = OWE_ShouldSpawnWaterMons(); u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); u32 level; + u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level); + + if (speciesId == SPECIES_NONE) + { + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + return; + } struct ObjectEventTemplate objectEventTemplate = { .localId = localId, - .graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level), + .graphicsId = graphicsId, .x = x - MAP_OFFSET, .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), - .movementType = MOVEMENT_TYPE_FLEE_PLAYER_OWE, + .movementType = OWE_GetMovementTypeFromSpecies(speciesId), .trainerType = TRAINER_TYPE_ENCOUNTER, }; - if (speciesId == SPECIES_NONE) - { - sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - return; - } - if (!OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) return; @@ -973,6 +982,12 @@ u32 OWE_GetDespawnAnimType(u32 metatileBehavior) return OWE_SPAWN_ANIM_CAVE; } +static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId) +{ + return MOVEMENT_TYPE_WANDER_AROUND_OWE; // Replace for Testing + return sOWE_MovementBehaviorType[gSpeciesInfo[speciesId].overworldEncounterBehavior]; +} + #undef tLocalId #undef tTaskStarted #undef sSpriteTaskState From b69783e7532915013fb3f458031660c2b6fb50be Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 12:40:51 +0000 Subject: [PATCH 208/572] Prevent OOB for non MOVEMENT_TYPE_WANDER_AROUND_OWE --- src/event_object_movement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 600c80d30752..3c65d5e3bece 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11747,7 +11747,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent if (OW_MON_WANDER_WALK == TRUE && IS_OW_MON_OBJ(objectEvent)) UpdateMonMoveInPlace(objectEvent, sprite); - if (OWE_CanMonSeePlayer(objectEvent)) + if (OWE_CanMonSeePlayer(objectEvent) && objectEvent->movementType != MOVEMENT_TYPE_WANDER_AROUND_OWE) { sprite->sTypeFuncId = 7; return FALSE; From ff5ebb93a76777d229a6cd94ec3f6f4aa91509ce Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 12:47:48 +0000 Subject: [PATCH 209/572] Add OWE_WANDER_AROUND_COMMON_STEPS --- .../movement_action_func_tables.h | 46 ++++++++----------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index 159b4f58adec..42f29a283a9f 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1719,24 +1719,23 @@ u8 (*const gMovementActionFuncs_WalkFastDiagonalDownRight[])(struct ObjectEvent MovementAction_PauseSpriteAnim, }; -u8 (*const gMovementTypeFuncs_WanderAround_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { - MovementType_WanderAround_Step0, - MovementType_WanderAround_Step1, - MovementType_WanderAround_OverworldWildEncounter_Step2, - MovementType_WanderAround_OverworldWildEncounter_Step3, - MovementType_WanderAround_OverworldWildEncounter_Step4, - MovementType_WanderAround_Step5, - MovementType_WanderAround_Step6, -}; - -u8 (*const gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { - MovementType_WanderAround_Step0, - MovementType_WanderAround_Step1, - MovementType_WanderAround_OverworldWildEncounter_Step2, - MovementType_WanderAround_OverworldWildEncounter_Step3, - MovementType_WanderAround_OverworldWildEncounter_Step4, - MovementType_WanderAround_Step5, - MovementType_WanderAround_Step6, +#define OWE_WANDER_AROUND_COMMON_STEPS \ + MovementType_WanderAround_Step0, \ + MovementType_WanderAround_Step1, \ + MovementType_WanderAround_OverworldWildEncounter_Step2, \ + MovementType_WanderAround_OverworldWildEncounter_Step3, \ + MovementType_WanderAround_OverworldWildEncounter_Step4, \ + MovementType_WanderAround_Step5, \ + MovementType_WanderAround_Step6 + +u8 (*const gMovementTypeFuncs_WanderAround_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = +{ + OWE_WANDER_AROUND_COMMON_STEPS, +}; + +u8 (*const gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = +{ + OWE_WANDER_AROUND_COMMON_STEPS, MovementType_Common_OverworldWildEncounter_Step7, MovementType_ChasePlayer_OverworldWildEncounter_Step8, MovementType_ChasePlayer_OverworldWildEncounter_Step9, @@ -1745,14 +1744,9 @@ u8 (*const gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter[])(struct Objec MovementType_Common_OverworldWildEncounter_Step12, }; -u8 (*const gMovementTypeFuncs_FleePlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { - MovementType_WanderAround_Step0, - MovementType_WanderAround_Step1, - MovementType_WanderAround_OverworldWildEncounter_Step2, - MovementType_WanderAround_OverworldWildEncounter_Step3, - MovementType_WanderAround_OverworldWildEncounter_Step4, - MovementType_WanderAround_Step5, - MovementType_WanderAround_Step6, +u8 (*const gMovementTypeFuncs_FleePlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = +{ + OWE_WANDER_AROUND_COMMON_STEPS, MovementType_Common_OverworldWildEncounter_Step7, MovementType_FleePlayer_OverworldWildEncounter_Step8, MovementType_FleePlayer_OverworldWildEncounter_Step9, From 1974094e3019f50c9abba239110f7d820ff8bf9b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 19:40:01 +0000 Subject: [PATCH 210/572] Use TryGenerateWildMon in Overworld Spawning --- include/constants/wild_encounter.h | 3 +++ include/wild_encounter.h | 1 + src/overworld_encounters.c | 28 +++++++++++++++++----------- src/wild_encounter.c | 5 +---- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/include/constants/wild_encounter.h b/include/constants/wild_encounter.h index 364dc7b35a22..ae79e452c5ea 100644 --- a/include/constants/wild_encounter.h +++ b/include/constants/wild_encounter.h @@ -9,4 +9,7 @@ #define NUM_ALTERING_CAVE_TABLES 9 +#define WILD_CHECK_REPEL (1 << 0) +#define WILD_CHECK_KEEN_EYE (1 << 1) + #endif // GUARD_CONSTANTS_WILD_ENCOUNTER_H diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 02fb69c36aaa..9f1bf7cfab16 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -64,6 +64,7 @@ bool8 TryDoDoubleWildBattle(void); bool8 StandardWildEncounter_Debug(void); u32 CalculateChainFishingShinyRolls(void); void CreateWildMon(u16 species, u8 level); +bool32 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags); u16 GetCurrentMapWildMonHeaderId(void); u32 ChooseWildMonIndex_Land(void); u32 ChooseWildMonIndex_Water(void); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5184f165f624..07eaf8040d34 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -517,29 +517,33 @@ void ClearOverworldEncounterData(void) static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) { const struct WildPokemonInfo *wildMonInfo; + enum WildPokemonArea wildArea; enum TimeOfDay timeOfDay; - u32 encounterIndex; - u32 personality = Random32(); + u32 personality; u32 headerId = GetCurrentMapWildMonHeaderId(); if (MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(x, y))) { - encounterIndex = ChooseWildMonIndex_Water(); - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + wildArea = WILD_AREA_WATER; + timeOfDay = GetTimeOfDayForEncounters(headerId, wildArea); wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; - *speciesId = wildMonInfo->wildPokemon[encounterIndex].species; - *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_WATER); } else { - encounterIndex = ChooseWildMonIndex_Land(); - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + wildArea = WILD_AREA_WATER; + timeOfDay = GetTimeOfDayForEncounters(headerId, wildArea); wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; - *speciesId = wildMonInfo->wildPokemon[encounterIndex].species; - *level = ChooseWildMonLevel(wildMonInfo->wildPokemon, encounterIndex, WILD_AREA_LAND); } - // Fallback for species here? Should it be earlier in code? + if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE)) + { + *speciesId = SPECIES_NONE; + return; + } + + *speciesId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); + *level = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); + personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY); if (*speciesId == SPECIES_UNOWN) *speciesId = GetUnownSpeciesId(personality); @@ -549,6 +553,8 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 *isFemale = TRUE; else *isFemale = FALSE; + + ZeroEnemyPartyMons(); } static bool8 IsSafeToSpawnObjectEvents(void) diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 96c3b22b7a53..3c3472c816ec 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -45,9 +45,6 @@ extern const u8 EventScript_SprayWoreOff[]; #define NUM_FISHING_SPOTS_3 149 #define NUM_FISHING_SPOTS (NUM_FISHING_SPOTS_1 + NUM_FISHING_SPOTS_2 + NUM_FISHING_SPOTS_3) -#define WILD_CHECK_REPEL (1 << 0) -#define WILD_CHECK_KEEN_EYE (1 << 1) - static u16 FeebasRandom(void); static void FeebasSeedRng(u16 seed); static bool8 IsWildLevelAllowedByRepel(u8 level); @@ -521,7 +518,7 @@ void CreateWildMon(u16 species, u8 level) #define TRY_GET_ABILITY_INFLUENCED_WILD_MON_INDEX(wildPokemon, type, ability, ptr, count) TryGetAbilityInfluencedWildMonIndex(wildPokemon, type, ability, ptr) #endif -static bool8 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags) +bool32 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags) { u8 wildMonIndex = 0; u8 level; From 6b4154d303736bbed604a36a30281633e3e1c86c Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 19:56:12 +0000 Subject: [PATCH 211/572] Tie WildEncounterCheck into OWE Spawning --- include/wild_encounter.h | 1 + src/overworld_encounters.c | 12 +++++++----- src/wild_encounter.c | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 9f1bf7cfab16..86974fdff020 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -65,6 +65,7 @@ bool8 StandardWildEncounter_Debug(void); u32 CalculateChainFishingShinyRolls(void); void CreateWildMon(u16 species, u8 level); bool32 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags); +bool32 WildEncounterCheck(u32 encounterRate, bool8 ignoreAbility); u16 GetCurrentMapWildMonHeaderId(void); u32 ChooseWildMonIndex_Land(void); u32 ChooseWildMonIndex_Water(void); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 07eaf8040d34..39ffca701040 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -30,7 +30,7 @@ static EWRAM_DATA u8 sOWESpawnCountdown = 0; static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool32 OWE_ShouldSpawnWaterMons(void); -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, bool32 encounterCheck); static bool8 IsSafeToSpawnObjectEvents(void); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); @@ -497,7 +497,7 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) { - SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level); + SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level, TRUE); u16 graphicsId = *speciesId + OBJ_EVENT_MON; if (*isFemale) @@ -514,7 +514,7 @@ void ClearOverworldEncounterData(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, bool32 encounterCheck) { const struct WildPokemonInfo *wildMonInfo; enum WildPokemonArea wildArea; @@ -535,7 +535,8 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; } - if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE)) + if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE) + || (!WildEncounterCheck(wildMonInfo->encounterRate, FALSE) && encounterCheck)) { *speciesId = SPECIES_NONE; return; @@ -768,7 +769,8 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const &speciesId, &isShiny, &isFemale, - &level + &level, + FALSE ); // Have a fallback incase of no header mons diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 3c3472c816ec..f7100e60537e 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -622,7 +622,7 @@ static bool8 EncounterOddsCheck(u16 encounterRate) } // Returns true if it will try to create a wild encounter. -static bool8 WildEncounterCheck(u32 encounterRate, bool8 ignoreAbility) +bool32 WildEncounterCheck(u32 encounterRate, bool8 ignoreAbility) { encounterRate *= 16; if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_MACH_BIKE | PLAYER_AVATAR_FLAG_ACRO_BIKE)) From beddcce8b51210d4a9a4838c435d525c3779b599 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 19:56:37 +0000 Subject: [PATCH 212/572] Revert "Tie WildEncounterCheck into OWE Spawning" This reverts commit 6b4154d303736bbed604a36a30281633e3e1c86c. --- include/wild_encounter.h | 1 - src/overworld_encounters.c | 12 +++++------- src/wild_encounter.c | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 86974fdff020..9f1bf7cfab16 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -65,7 +65,6 @@ bool8 StandardWildEncounter_Debug(void); u32 CalculateChainFishingShinyRolls(void); void CreateWildMon(u16 species, u8 level); bool32 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags); -bool32 WildEncounterCheck(u32 encounterRate, bool8 ignoreAbility); u16 GetCurrentMapWildMonHeaderId(void); u32 ChooseWildMonIndex_Land(void); u32 ChooseWildMonIndex_Water(void); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 39ffca701040..07eaf8040d34 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -30,7 +30,7 @@ static EWRAM_DATA u8 sOWESpawnCountdown = 0; static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool32 OWE_ShouldSpawnWaterMons(void); -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, bool32 encounterCheck); +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); static bool8 IsSafeToSpawnObjectEvents(void); static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); @@ -497,7 +497,7 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) { - SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level, TRUE); + SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level); u16 graphicsId = *speciesId + OBJ_EVENT_MON; if (*isFemale) @@ -514,7 +514,7 @@ void ClearOverworldEncounterData(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, bool32 encounterCheck) +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) { const struct WildPokemonInfo *wildMonInfo; enum WildPokemonArea wildArea; @@ -535,8 +535,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; } - if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE) - || (!WildEncounterCheck(wildMonInfo->encounterRate, FALSE) && encounterCheck)) + if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE)) { *speciesId = SPECIES_NONE; return; @@ -769,8 +768,7 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const &speciesId, &isShiny, &isFemale, - &level, - FALSE + &level ); // Have a fallback incase of no header mons diff --git a/src/wild_encounter.c b/src/wild_encounter.c index f7100e60537e..3c3472c816ec 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -622,7 +622,7 @@ static bool8 EncounterOddsCheck(u16 encounterRate) } // Returns true if it will try to create a wild encounter. -bool32 WildEncounterCheck(u32 encounterRate, bool8 ignoreAbility) +static bool8 WildEncounterCheck(u32 encounterRate, bool8 ignoreAbility) { encounterRate *= 16; if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_MACH_BIKE | PLAYER_AVATAR_FLAG_ACRO_BIKE)) From 07e0f5e23d520f1d526f653cdca2e80ef150c6c4 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 19:58:30 +0000 Subject: [PATCH 213/572] Remove redundant code --- src/overworld_encounters.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 07eaf8040d34..930968377b01 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -100,7 +100,6 @@ void UpdateOverworldEncounters(void) bool32 waterMons = OWE_ShouldSpawnWaterMons(); u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); u32 level; - u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level); if (speciesId == SPECIES_NONE) { @@ -110,7 +109,7 @@ void UpdateOverworldEncounters(void) struct ObjectEventTemplate objectEventTemplate = { .localId = localId, - .graphicsId = graphicsId, + .graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level), .x = x - MAP_OFFSET, .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), From 8a30d69ffe58965822d549ab80a1eb05d36ae0cd Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:49:47 +0000 Subject: [PATCH 214/572] Add Comment --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 930968377b01..1119836c363a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -579,6 +579,7 @@ u8 CountActiveOverworldEncounters(void) static bool32 OWE_ShouldSpawnWaterMons(void) { + // Needs refactoring, and this replacing with a check for coords in many cases. return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); } From c4ff22b7b130922ba52ca711c4b439912e1cd064 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:45:18 +0000 Subject: [PATCH 215/572] BUGGED: Add Ambient Cries --- src/overworld_encounters.c | 65 ++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 1119836c363a..0254f7e68ac9 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -40,6 +40,7 @@ static u32 GetSpawnSlotByLocalId(u32 localId); static void SortOWEMonAges(void); static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny); static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId); +static void OWE_DoAmbientCry(void); static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = { @@ -85,7 +86,10 @@ void UpdateOverworldEncounters(void) } if (!IsSafeToSpawnObjectEvents() || !TrySelectTile(&x, &y)) + { + OWE_DoAmbientCry(); return; + } if (GetActiveEncounterTable(OWE_ShouldSpawnWaterMons())) { @@ -94,6 +98,7 @@ void UpdateOverworldEncounters(void) if (spawnSlot == INVALID_SPAWN_SLOT) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + OWE_DoAmbientCry(); return; } @@ -104,6 +109,7 @@ void UpdateOverworldEncounters(void) if (speciesId == SPECIES_NONE) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + OWE_DoAmbientCry(); return; } @@ -118,13 +124,18 @@ void UpdateOverworldEncounters(void) }; if (!OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) + { + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + OWE_DoAmbientCry(); return; + } u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); if (objectEventId >= OBJECT_EVENTS_COUNT) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + OWE_DoAmbientCry(); return; } @@ -180,27 +191,8 @@ void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) // If object is on water then use water anim. // If object is indoor, need an indoor anim? enum OverworldEncounterSpawnAnim spawnAnimType; - u32 speciesId = OW_SPECIES(objectEvent); bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; - u32 volume = (Random() % 30) + 50; - s32 pan; - - switch (DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent)) - { - case DIR_WEST: - pan = (Random() % 44); - break; - - case DIR_EAST: - pan = -(Random() % 44); - break; - - default: - pan = 0; - } - PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); - // Also move this to a dedicated ambient cries function that plays spawned mon cries, when spawn timer gets to 0 but no new mons are spawned. - // Do a calculation of coordinate distance from player, north south edits volume, east west edits pan. + OWE_DoAmbientCry(); if (isShiny) { @@ -994,6 +986,39 @@ static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId) return sOWE_MovementBehaviorType[gSpeciesInfo[speciesId].overworldEncounterBehavior]; } +static void OWE_DoAmbientCry(void) +{ + struct ObjectEvent objectEventTemp = + { + .graphicsId = OBJ_EVENT_GFX_SPECIES(PIKACHU), + .currentCoords.x = gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, + .currentCoords.y = gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y, + }; + struct ObjectEvent *objectEvent = &objectEventTemp; + // Will need to pass the object instead of this. + // All cases other than in OWE_DoSpawnAnim will need to randomise an active encounter. + // Currently bugs out, and should calculate values for pan and volume based on location. + + u32 speciesId = OW_SPECIES(objectEvent); + u32 volume = (Random() % 30) + 50; + s32 pan; + + switch (DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent)) + { + case DIR_WEST: + pan = (Random() % 44); + break; + + case DIR_EAST: + pan = -(Random() % 44); + break; + + default: + pan = 0; + } + PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); +} + #undef tLocalId #undef tTaskStarted #undef sSpriteTaskState From a880fe40b7efd515c0b2ce84f642fba467f14f57 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:52:40 +0000 Subject: [PATCH 216/572] This was NOT redundant This reverts commit 07e0f5e23d520f1d526f653cdca2e80ef150c6c4. --- src/overworld_encounters.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 0254f7e68ac9..349e220c68d3 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -105,6 +105,7 @@ void UpdateOverworldEncounters(void) bool32 waterMons = OWE_ShouldSpawnWaterMons(); u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); u32 level; + u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level); if (speciesId == SPECIES_NONE) { @@ -115,7 +116,7 @@ void UpdateOverworldEncounters(void) struct ObjectEventTemplate objectEventTemplate = { .localId = localId, - .graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level), + .graphicsId = graphicsId, .x = x - MAP_OFFSET, .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), From 1b80064a046f4ce0cde643d09dbd7f31b6583cea Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 23:16:41 +0000 Subject: [PATCH 217/572] BUGFIX: Adds Missing OWE_SPAWN_TIME_MINIMUM --- src/overworld_encounters.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 349e220c68d3..95a3585c0ca1 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -87,6 +87,7 @@ void UpdateOverworldEncounters(void) if (!IsSafeToSpawnObjectEvents() || !TrySelectTile(&x, &y)) { + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; OWE_DoAmbientCry(); return; } @@ -989,6 +990,7 @@ static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId) static void OWE_DoAmbientCry(void) { + // This can be used to determine how often the UpdateOverworldEncounters function reaches various points. struct ObjectEvent objectEventTemp = { .graphicsId = OBJ_EVENT_GFX_SPECIES(PIKACHU), From c4b1a4c1d8b607c3e4460d924256fed4a6df66c5 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 23:23:05 +0000 Subject: [PATCH 218/572] Add comment --- src/overworld_encounters.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 95a3585c0ca1..c2d3d24359ef 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -51,6 +51,8 @@ static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = void LoadOverworldEncounterData(void) { + // This and ClearOverworldEncounterData is the same + // Consolidate these and use one of these to call OWE_DoAmbientCry sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } From 5264bbf1074777566b91a87abcbec307cb16244d Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 21 Dec 2025 23:47:04 +0000 Subject: [PATCH 219/572] Move Anim Selection --- src/overworld_encounters.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c2d3d24359ef..2aefd2415078 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -191,9 +191,6 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) { - // Need to edit anims: - // If object is on water then use water anim. - // If object is indoor, need an indoor anim? enum OverworldEncounterSpawnAnim spawnAnimType; bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; OWE_DoAmbientCry(); @@ -205,12 +202,7 @@ void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) } else { - if (OWE_ShouldSpawnWaterMons()) - spawnAnimType = OWE_SPAWN_ANIM_WATER; - else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) - spawnAnimType = OWE_SPAWN_ANIM_CAVE; - else - spawnAnimType = OWE_SPAWN_ANIM_GRASS; + spawnAnimType = OWE_GetDespawnAnimType(objectEvent->currentMetatileBehavior); } MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); } @@ -976,6 +968,11 @@ void Task_OWE_WaitMovements(u8 taskId) u32 OWE_GetDespawnAnimType(u32 metatileBehavior) { + // Need to edit anims: + // If object is on water then use water anim. + // If object is indoor, need an indoor anim? + // Long grass anim? + if (MetatileBehavior_IsPokeGrass(metatileBehavior) || MetatileBehavior_IsAshGrass(metatileBehavior)) return OWE_SPAWN_ANIM_GRASS; else if (MetatileBehavior_IsSurfableFishableWater(metatileBehavior)) From e85eaf0d05d1d0748edea8d0096614afdc767f8e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 22 Dec 2025 00:13:50 +0000 Subject: [PATCH 220/572] CheckForObjectEventAtLocation Cleanup --- src/overworld_encounters.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 2aefd2415078..6220ae262f12 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -358,7 +358,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) *outX = x; *outY = y; - if(!CheckForObjectEventAtLocation(x, y)) + if (!CheckForObjectEventAtLocation(x, y)) return TRUE; } } @@ -369,7 +369,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) *outX = x; *outY = y; - if(!CheckForObjectEventAtLocation(x, y)) + if (!CheckForObjectEventAtLocation(x, y)) return TRUE; } } @@ -583,11 +583,10 @@ void RemoveAllOverworldEncounterObjects(void) static bool8 CheckForObjectEventAtLocation(s16 x, s16 y) { - u8 i; - - for(i = 0; i < OBJECT_EVENTS_COUNT; ++i) + for (u8 i = 0; i < OBJECT_EVENTS_COUNT; i++) { - if(gObjectEvents[i].active && gObjectEvents[i].currentCoords.x == x && gObjectEvents[i].currentCoords.y == y) + if (gObjectEvents[i].active && gObjectEvents[i].currentCoords.x == x + && gObjectEvents[i].currentCoords.y == y) return TRUE; } From 759bb881b1219643ea910d5095a6df1be71af878 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 22 Dec 2025 01:58:08 +0000 Subject: [PATCH 221/572] Fix Metatile Behaviour Choice --- src/overworld_encounters.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 6220ae262f12..bc963dc9b128 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -202,7 +202,8 @@ void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) } else { - spawnAnimType = OWE_GetDespawnAnimType(objectEvent->currentMetatileBehavior); + u32 metatileBehavior = MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x, objectEvent->currentCoords.y); + spawnAnimType = OWE_GetDespawnAnimType(metatileBehavior); } MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); } From df15095611d9b6c4450ca1ae19024583e212f3e0 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 22 Dec 2025 22:34:45 +0000 Subject: [PATCH 222/572] Add OW Ambient Cries based on location --- src/overworld_encounters.c | 71 +++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index bc963dc9b128..f27011c731b4 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -40,7 +40,8 @@ static u32 GetSpawnSlotByLocalId(u32 localId); static void SortOWEMonAges(void); static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny); static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId); -static void OWE_DoAmbientCry(void); +static void OWE_DoAmbientCry(struct ObjectEvent *objectEvent); +static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = { @@ -90,7 +91,7 @@ void UpdateOverworldEncounters(void) if (!IsSafeToSpawnObjectEvents() || !TrySelectTile(&x, &y)) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - OWE_DoAmbientCry(); + OWE_DoAmbientCry(OWE_GetRandomActiveEncounterObject()); return; } @@ -101,7 +102,7 @@ void UpdateOverworldEncounters(void) if (spawnSlot == INVALID_SPAWN_SLOT) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - OWE_DoAmbientCry(); + OWE_DoAmbientCry(OWE_GetRandomActiveEncounterObject()); return; } @@ -113,7 +114,7 @@ void UpdateOverworldEncounters(void) if (speciesId == SPECIES_NONE) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - OWE_DoAmbientCry(); + OWE_DoAmbientCry(OWE_GetRandomActiveEncounterObject()); return; } @@ -130,7 +131,7 @@ void UpdateOverworldEncounters(void) if (!OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - OWE_DoAmbientCry(); + OWE_DoAmbientCry(OWE_GetRandomActiveEncounterObject()); return; } @@ -139,7 +140,7 @@ void UpdateOverworldEncounters(void) if (objectEventId >= OBJECT_EVENTS_COUNT) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - OWE_DoAmbientCry(); + OWE_DoAmbientCry(OWE_GetRandomActiveEncounterObject()); return; } @@ -193,7 +194,7 @@ void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) { enum OverworldEncounterSpawnAnim spawnAnimType; bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; - OWE_DoAmbientCry(); + OWE_DoAmbientCry(objectEvent); if (isShiny) { @@ -981,45 +982,51 @@ u32 OWE_GetDespawnAnimType(u32 metatileBehavior) return OWE_SPAWN_ANIM_CAVE; } +static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) +{ + return &gObjectEvents[gPlayerAvatar.objectEventId]; +} + + static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId) { return MOVEMENT_TYPE_WANDER_AROUND_OWE; // Replace for Testing return sOWE_MovementBehaviorType[gSpeciesInfo[speciesId].overworldEncounterBehavior]; } -static void OWE_DoAmbientCry(void) +// Are these needed? Not defined elsewhere? I don't think so. +#define MAP_METATILE_VIEW_X 7 +#define MAP_METATILE_VIEW_Y 5 +static void OWE_DoAmbientCry(struct ObjectEvent *objectEvent) { // This can be used to determine how often the UpdateOverworldEncounters function reaches various points. - struct ObjectEvent objectEventTemp = - { - .graphicsId = OBJ_EVENT_GFX_SPECIES(PIKACHU), - .currentCoords.x = gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x, - .currentCoords.y = gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y, - }; - struct ObjectEvent *objectEvent = &objectEventTemp; - // Will need to pass the object instead of this. - // All cases other than in OWE_DoSpawnAnim will need to randomise an active encounter. - // Currently bugs out, and should calculate values for pan and volume based on location. - u32 speciesId = OW_SPECIES(objectEvent); - u32 volume = (Random() % 30) + 50; + if (objectEvent->isPlayer) // Testing + speciesId = SPECIES_NONE; + u32 volume; s32 pan; + s32 distanceX = objectEvent->currentCoords.x - gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x; + s32 distanceY = objectEvent->currentCoords.y - gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y; - switch (DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent)) - { - case DIR_WEST: - pan = (Random() % 44); - break; - - case DIR_EAST: - pan = -(Random() % 44); - break; + if (distanceX > MAP_METATILE_VIEW_X) + distanceX = MAP_METATILE_VIEW_X; - default: - pan = 0; - } + if (distanceX < -MAP_METATILE_VIEW_X) + distanceX = -MAP_METATILE_VIEW_X; + + if (distanceY > MAP_METATILE_VIEW_Y) + distanceY = MAP_METATILE_VIEW_Y; + + if (distanceY < -MAP_METATILE_VIEW_Y) + distanceY = -MAP_METATILE_VIEW_Y; + + pan = (distanceX * 44) / MAP_METATILE_VIEW_X; + volume = 50 + ((distanceY + MAP_METATILE_VIEW_Y) * 30) / (2 * MAP_METATILE_VIEW_Y); + PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); } +#undef MAP_METATILE_VIEW_X +#undef MAP_METATILE_VIEW_Y #undef tLocalId #undef tTaskStarted From ab9e133dcc5578e1f1e8590dc1aa27de032af93d Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 22 Dec 2025 20:47:25 -0600 Subject: [PATCH 223/572] Moved OverworldEncounterSpawnAnim enum --- include/event_object_movement.h | 8 -------- include/overworld_encounters.h | 3 +-- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 3f9feb16bbfd..317f14c7db58 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -60,14 +60,6 @@ enum FollowerTransformTypes TRANSFORM_TYPE_WEATHER, }; -enum OverworldEncounterSpawnAnim -{ - OWE_SPAWN_ANIM_GRASS, - OWE_SPAWN_ANIM_WATER, - OWE_SPAWN_ANIM_CAVE, - OWE_SPAWN_ANIM_SHINY, -}; - #define FIGURE_8_LENGTH 72 #define GROUND_EFFECT_FLAG_TALL_GRASS_ON_SPAWN (1 << 0) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 879fccba0bd6..ba83e56c9aad 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -32,8 +32,6 @@ #define INVALID_SPAWN_SLOT 0xFF -/* -Compile Errors when moving here enum OverworldEncounterSpawnAnim { OWE_SPAWN_ANIM_GRASS, @@ -42,6 +40,7 @@ enum OverworldEncounterSpawnAnim OWE_SPAWN_ANIM_SHINY, }; +/* Combine OWE Type Checks into one function using these. Need to figure a clean way to adjust SemiManual check as takes template. enum OverworldObjectEncounterType From 7c7dd28b5ace6619a2e80a1db030939975cd16c2 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 22 Dec 2025 21:29:46 -0600 Subject: [PATCH 224/572] Fixed TrySelectTile wrongly applying MAP_OFFSET --- src/overworld_encounters.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f27011c731b4..e243e37ee049 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -343,8 +343,8 @@ static bool8 TrySelectTile(s16* outX, s16* outY) elevation = MapGridGetElevationAt(x, y); layout = Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->mapLayout; - if ((x + MAP_OFFSET) < 0 || (x + MAP_OFFSET) >= layout->width || - (y + MAP_OFFSET) < 0 || (y + MAP_OFFSET) >= layout->height) + if ((x - MAP_OFFSET) < 0 || (x - MAP_OFFSET) >= layout->width || + (y - MAP_OFFSET) < 0 || (y - MAP_OFFSET) >= layout->height) return FALSE; // 0 is change of elevation, 15 is multiple elevation e.g. bridges From ca4d0db973c1cd437a12398f3d10b326fda6063d Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 22 Dec 2025 21:51:55 -0600 Subject: [PATCH 225/572] Fixed WILD_AREA_WATER typo --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e243e37ee049..78852240facb 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -519,7 +519,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 } else { - wildArea = WILD_AREA_WATER; + wildArea = WILD_AREA_LAND; timeOfDay = GetTimeOfDayForEncounters(headerId, wildArea); wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; } From 374c5d6672296dc530c8918f529ab7c4e46f3dea Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 23 Dec 2025 09:58:40 +0000 Subject: [PATCH 226/572] Ambient Cry and Spawn Timer Cleanup --- include/config/overworld.h | 2 ++ include/overworld_encounters.h | 4 +-- src/load_save.c | 2 +- src/overworld.c | 5 +++- src/overworld_encounters.c | 51 +++++++++++++++++----------------- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index a3cca93d9fa1..c1ef34edd004 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -79,6 +79,8 @@ #define OW_WILD_ENCOUNTERS_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. #define OW_ENCOUNTER_MOVEMENT_RANGE_X 8 // The horizontal movement range for OW encounter Pokémon. #define OW_ENCOUNTER_MOVEMENT_RANGE_Y 8 // The vertical movement range for OW encounter Pokémon. +#define OW_VANILLA_AMBIENT_CRIES TRUE // If TRUE, the ambient wild Pokémon cries will play on routes with encounter tables. +#define OW_WILD_ENCOUNTERS_AMBIENT_CRIES TRUE // If TRUE, generated overworld encounters will play ambient cries based on their location relative to the player. // Out-of-battle Ability effects #define OW_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8+, if a Pokémon with Synchronize leads the party, wild Pokémon will always have their same Nature as opposed to the 50% chance in previous games. Gift Pokémon excluded. diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index ba83e56c9aad..16921c6f7bc3 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -62,14 +62,14 @@ enum OverworldEncounterBehaviors extern const u8 InteractWithDynamicWildOverworldEncounter[]; -void LoadOverworldEncounterData(void); +void OWE_ResetSpawnCounterPlayAmbientCry(void); void UpdateOverworldEncounters(void); u32 GetOldestSlot(void); void CreateOverworldWildEncounter(void); void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); -void ClearOverworldEncounterData(void); +void OverworldWildEncounter_SetMinimumSpawnTimer(void); u8 CountActiveOverworldEncounters(void); void RemoveAllOverworldEncounterObjects(void); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent); diff --git a/src/load_save.c b/src/load_save.c index 763073b84f4c..1ad1f4abce1e 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -221,7 +221,7 @@ void LoadObjectEvents(void) int i; u16 graphicsId; - LoadOverworldEncounterData(); + OverworldWildEncounter_SetMinimumSpawnTimer(); for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { gObjectEvents[i] = gSaveBlock1Ptr->objectEvents[i]; diff --git a/src/overworld.c b/src/overworld.c index cd10aa376dbd..417530b0ad42 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -954,7 +954,7 @@ static void LoadMapFromWarp(bool32 a1) UpdateTVScreensOnMap(gBackupMapLayout.width, gBackupMapLayout.height); InitSecretBaseAppearance(TRUE); } - ClearOverworldEncounterData(); + OverworldWildEncounter_SetMinimumSpawnTimer(); } void ResetInitialPlayerAvatarState(void) @@ -1331,6 +1331,9 @@ void Overworld_FadeOutMapMusic(void) static void PlayAmbientCry(void) { + if (!OW_VANILLA_AMBIENT_CRIES) + return; + s16 x, y; s8 pan; s8 volume; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 78852240facb..128355c0a220 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -40,7 +40,7 @@ static u32 GetSpawnSlotByLocalId(u32 localId); static void SortOWEMonAges(void); static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny); static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId); -static void OWE_DoAmbientCry(struct ObjectEvent *objectEvent); +static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = @@ -50,11 +50,11 @@ static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = [OWE_BEHAVIOR_FLEE] = MOVEMENT_TYPE_FLEE_PLAYER_OWE, }; -void LoadOverworldEncounterData(void) +void OWE_ResetSpawnCounterPlayAmbientCry(void) { - // This and ClearOverworldEncounterData is the same - // Consolidate these and use one of these to call OWE_DoAmbientCry - sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + OverworldWildEncounter_SetMinimumSpawnTimer(); + if (OW_WILD_ENCOUNTERS_AMBIENT_CRIES) + OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); } void UpdateOverworldEncounters(void) @@ -74,7 +74,7 @@ void UpdateOverworldEncounters(void) } else if (sOWESpawnCountdown == 255) { - sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + OverworldWildEncounter_SetMinimumSpawnTimer(); } u16 speciesId = SPECIES_NONE; @@ -90,8 +90,7 @@ void UpdateOverworldEncounters(void) if (!IsSafeToSpawnObjectEvents() || !TrySelectTile(&x, &y)) { - sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - OWE_DoAmbientCry(OWE_GetRandomActiveEncounterObject()); + OWE_ResetSpawnCounterPlayAmbientCry(); return; } @@ -101,8 +100,7 @@ void UpdateOverworldEncounters(void) if (spawnSlot == INVALID_SPAWN_SLOT) { - sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - OWE_DoAmbientCry(OWE_GetRandomActiveEncounterObject()); + OWE_ResetSpawnCounterPlayAmbientCry(); return; } @@ -113,8 +111,7 @@ void UpdateOverworldEncounters(void) if (speciesId == SPECIES_NONE) { - sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - OWE_DoAmbientCry(OWE_GetRandomActiveEncounterObject()); + OWE_ResetSpawnCounterPlayAmbientCry(); return; } @@ -130,8 +127,7 @@ void UpdateOverworldEncounters(void) if (!OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) { - sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - OWE_DoAmbientCry(OWE_GetRandomActiveEncounterObject()); + OWE_ResetSpawnCounterPlayAmbientCry(); return; } @@ -139,8 +135,7 @@ void UpdateOverworldEncounters(void) if (objectEventId >= OBJECT_EVENTS_COUNT) { - sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - OWE_DoAmbientCry(OWE_GetRandomActiveEncounterObject()); + OWE_ResetSpawnCounterPlayAmbientCry(); return; } @@ -194,7 +189,7 @@ void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) { enum OverworldEncounterSpawnAnim spawnAnimType; bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; - OWE_DoAmbientCry(objectEvent); + OWE_PlayMonObjectCry(objectEvent); if (isShiny) { @@ -498,7 +493,7 @@ u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, boo return graphicsId; } -void ClearOverworldEncounterData(void) +void OverworldWildEncounter_SetMinimumSpawnTimer(void) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } @@ -997,27 +992,31 @@ static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId) // Are these needed? Not defined elsewhere? I don't think so. #define MAP_METATILE_VIEW_X 7 #define MAP_METATILE_VIEW_Y 5 -static void OWE_DoAmbientCry(struct ObjectEvent *objectEvent) +static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) { - // This can be used to determine how often the UpdateOverworldEncounters function reaches various points. + if (!IS_OW_MON_OBJ(objectEvent)) + return; + u32 speciesId = OW_SPECIES(objectEvent); - if (objectEvent->isPlayer) // Testing - speciesId = SPECIES_NONE; u32 volume; s32 pan; s32 distanceX = objectEvent->currentCoords.x - gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x; s32 distanceY = objectEvent->currentCoords.y - gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y; + // TESTING: Setting this species can be used as a test to play a consistent sound to check how often the + // code in UpdateOverworldEncounters runs, as OWE_GetRandomActiveEncounterObject cuurently returns + // the player object. + if (objectEvent->isPlayer) + speciesId = SPECIES_NONE; + if (distanceX > MAP_METATILE_VIEW_X) distanceX = MAP_METATILE_VIEW_X; - - if (distanceX < -MAP_METATILE_VIEW_X) + else if (distanceX < -MAP_METATILE_VIEW_X) distanceX = -MAP_METATILE_VIEW_X; if (distanceY > MAP_METATILE_VIEW_Y) distanceY = MAP_METATILE_VIEW_Y; - - if (distanceY < -MAP_METATILE_VIEW_Y) + else if (distanceY < -MAP_METATILE_VIEW_Y) distanceY = -MAP_METATILE_VIEW_Y; pan = (distanceX * 44) / MAP_METATILE_VIEW_X; From 741ec1ea043ef2dd0c1e9ed3db48383ae5b8cc1d Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 23 Dec 2025 12:07:41 -0600 Subject: [PATCH 227/572] Can't use sprite->data[4] --- include/overworld_encounters.h | 1 + src/event_object_movement.c | 8 +++----- src/overworld_encounters.c | 20 +++++++++----------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index ba83e56c9aad..f746f21d5862 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -92,6 +92,7 @@ bool32 OWE_IsPlayerInsideRangeFromMon(struct ObjectEvent *mon, u32 distance); u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); void Task_OWE_WaitMovements(u8 taskId); +bool32 OWE_IsWaitTaskActive(void); u32 OWE_GetDespawnAnimType(u32 metatileBehavior); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 3c65d5e3bece..6c477d926231 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11893,8 +11893,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step9(struct ObjectEvent *o return TRUE; } -#define sCollisionTimer sprite->data[4] -#define sSpriteTaskState sprite->data[6] +#define sCollisionTimer sprite->data[6] #define STARTED 1 bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) @@ -11909,7 +11908,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent * u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - if (sSpriteTaskState != STARTED) + if (!OWE_IsWaitTaskActive()) { direction = GetOppositeDirection(direction); } @@ -11930,7 +11929,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * { u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - if (sSpriteTaskState != STARTED) + if (!OWE_IsWaitTaskActive()) { newDirection = GetOppositeDirection(newDirection); } @@ -11959,5 +11958,4 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * } #undef sCollisionTimer -#undef sSpriteTaskState #undef STARTED diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 78852240facb..21a4152c5976 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -785,7 +785,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle)); bool32 playerIsObstacle = (obstacle->isPlayer && IsOverworldWildEncounter(collider)); - if ((playerIsCollider || playerIsObstacle) && FindTaskIdByFunc(Task_OWE_WaitMovements) == TASK_NONE) + if ((playerIsCollider || playerIsObstacle) && !OWE_IsWaitTaskActive()) { struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; @@ -937,8 +937,6 @@ bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon) return TRUE; } -#define tTaskStarted gTasks[taskId].data[1] -#define sSpriteTaskState gSprites[mon->spriteId].data[6] #define NOT_STARTED 0 #define STARTED 1 @@ -946,12 +944,6 @@ void Task_OWE_WaitMovements(u8 taskId) { struct ObjectEvent *mon = &gObjectEvents[GetObjectEventIdByLocalId(tLocalId)]; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - - if (tTaskStarted == NOT_STARTED) - { - sSpriteTaskState = STARTED; - tTaskStarted = STARTED; - } if (mon->singleMovementActive == 0 && player->singleMovementActive == 0) { @@ -967,6 +959,14 @@ void Task_OWE_WaitMovements(u8 taskId) } } +bool32 OWE_IsWaitTaskActive(void) +{ + if (FindTaskIdByFunc(Task_OWE_WaitMovements) != TASK_NONE) + return TRUE; + + return FALSE; +} + u32 OWE_GetDespawnAnimType(u32 metatileBehavior) { // Need to edit anims: @@ -1029,8 +1029,6 @@ static void OWE_DoAmbientCry(struct ObjectEvent *objectEvent) #undef MAP_METATILE_VIEW_Y #undef tLocalId -#undef tTaskStarted -#undef sSpriteTaskState #undef NOT_STARTED #undef STARTED #undef sOverworldEncounterLevel From a20e1148b339f853581a1abe1fea8176e960fc45 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 24 Dec 2025 14:55:50 -0600 Subject: [PATCH 228/572] Better pathfinding --- src/event_object_movement.c | 3 ++- src/overworld_encounters.c | 31 +++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 6c477d926231..94530634d15c 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11927,9 +11927,10 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) { + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - if (!OWE_IsWaitTaskActive()) + if (!OWE_IsWaitTaskActive() && !(objectEvent->currentCoords.x == player->currentCoords.x || objectEvent->currentCoords.y == player->currentCoords.y)) { newDirection = GetOppositeDirection(newDirection); } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 21a4152c5976..d2172a3371cb 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -900,6 +900,33 @@ bool32 OWE_IsPlayerInsideRangeFromMon(struct ObjectEvent *mon, u32 distance) return FALSE; } +static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, u32 newDirection) +{ + s16 x = mon->currentCoords.x; + s16 y = mon->currentCoords.y; + + MoveCoords(newDirection, &x, &y); + if (!GetCollisionAtCoords(mon, x, y, newDirection)) + { + MoveCoords(mon->movementDirection, &x, &y); + if (!GetCollisionAtCoords(mon, x, y, mon->movementDirection)) + return newDirection; + } + + x = mon->currentCoords.x; + y = mon->currentCoords.y; + + MoveCoords(GetOppositeDirection(newDirection), &x, &y); + if (!GetCollisionAtCoords(mon, x, y, GetOppositeDirection(newDirection))) + { + MoveCoords(mon->movementDirection, &x, &y); + if (!GetCollisionAtCoords(mon, x, y, mon->movementDirection)) + return GetOppositeDirection(newDirection); + } + + return mon->movementDirection; +} + u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; @@ -911,7 +938,7 @@ u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) if (player->currentCoords.x < mon->currentCoords.x) return DIR_WEST; else if (player->currentCoords.x == mon->currentCoords.x) - return (Random() % 2) == 0 ? DIR_EAST : DIR_WEST; + return OWE_CheckPathToPlayerFromCollision(mon, (Random() % 2) == 0 ? DIR_EAST : DIR_WEST); else return DIR_EAST; case DIR_EAST: @@ -919,7 +946,7 @@ u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) if (player->currentCoords.y < mon->currentCoords.y) return DIR_NORTH; else if (player->currentCoords.y == mon->currentCoords.y) - return (Random() % 2) == 0 ? DIR_NORTH : DIR_SOUTH; + return OWE_CheckPathToPlayerFromCollision(mon, (Random() % 2) == 0 ? DIR_NORTH : DIR_SOUTH); else return DIR_SOUTH; } From b31d0b413de8f9bbc5687c50c72498a7c491344f Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 24 Dec 2025 15:52:44 -0600 Subject: [PATCH 229/572] Species behavior data now fully customizable --- .../overworld_encounter_species_behavior.h | 24 ++++++++++++++++ include/overworld_encounters.h | 28 +++++++++++++++++++ include/pokemon.h | 2 +- src/overworld_encounters.c | 5 ++-- 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 include/overworld_encounter_species_behavior.h diff --git a/include/overworld_encounter_species_behavior.h b/include/overworld_encounter_species_behavior.h new file mode 100644 index 000000000000..ba4df4c79a91 --- /dev/null +++ b/include/overworld_encounter_species_behavior.h @@ -0,0 +1,24 @@ +#ifndef GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H +#define GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H + +const struct MonSpeciesOWEData sOWESpeciesBehaviors[OWE_SPECIES_BEHAVIOR_COUNT] = { + [OWE_IGNORE_PLAYER] = { + .behavior = OWE_BEHAVIOR_WANDER_AROUND, + }, + [OWE_CHASE_PLAYER_SLOW] = { + .behavior = OWE_BEHAVIOR_CHASE, + .idleSpeed = OWE_SPEED_SLOW, + .activeSpeed = OWE_SPEED_SLOW, + .viewDistance = 5, + .viewWidth = 3 + }, + [OWE_FLEE_PLAYER_NORMAL] = { + .behavior = OWE_BEHAVIOR_FLEE, + .idleSpeed = OWE_SPEED_NORMAL, + .activeSpeed = OWE_SPEED_NORMAL, + .viewDistance = 5, + .viewWidth = 3 + } +}; + +#endif // GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index f746f21d5862..42f73be8efe2 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -60,6 +60,34 @@ enum OverworldEncounterBehaviors OWE_BEHAVIOR_COUNT }; +enum OWESpeeds +{ + OWE_SPEED_NORMAL, + OWE_SPEED_FAST, + OWE_SPEED_FASTER, + OWE_SPEED_SLOW, + OWE_SPEED_SLOWER +}; + +struct MonSpeciesOWEData +{ + enum OverworldEncounterBehaviors behavior; + enum OWESpeeds idleSpeed; + enum OWESpeeds activeSpeed; + u16 viewDistance:4; + u16 viewWidth:4; + u16 activeDistance:4; + u16 padding:4; +}; + +enum OWESpeciesBehaviors +{ + OWE_IGNORE_PLAYER, + OWE_CHASE_PLAYER_SLOW, + OWE_FLEE_PLAYER_NORMAL, + OWE_SPECIES_BEHAVIOR_COUNT +}; + extern const u8 InteractWithDynamicWildOverworldEncounter[]; void LoadOverworldEncounterData(void); diff --git a/include/pokemon.h b/include/pokemon.h index d563c041a681..8d134c8deeed 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -537,7 +537,7 @@ struct SpeciesInfo /*0xC4*/ #endif //P_GENDER_DIFFERENCES #endif //OW_PKMN_OBJECTS_SHARE_PALETTES #endif //OW_POKEMON_OBJECT_EVENTS - enum OverworldEncounterBehaviors overworldEncounterBehavior; + enum OWESpeciesBehaviors overworldEncounterBehavior; }; struct AbilityInfo diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d2172a3371cb..dba1a76a34e8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1,5 +1,6 @@ #include "global.h" #include "overworld_encounters.h" +#include "overworld_encounter_species_behavior.h" #include "battle_setup.h" #include "event_data.h" #include "event_object_movement.h" @@ -1017,8 +1018,8 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId) { - return MOVEMENT_TYPE_WANDER_AROUND_OWE; // Replace for Testing - return sOWE_MovementBehaviorType[gSpeciesInfo[speciesId].overworldEncounterBehavior]; + //return MOVEMENT_TYPE_WANDER_AROUND_OWE; // Replace for Testing + return sOWE_MovementBehaviorType[sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].behavior]; } // Are these needed? Not defined elsewhere? I don't think so. From 4aa8c2ccab3dfcfbf333b9bfbd71c94b7ee0e36e Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 24 Dec 2025 17:17:46 -0600 Subject: [PATCH 230/572] Movement speeds are now read from the species --- include/event_object_movement.h | 1 + .../overworld_encounter_species_behavior.h | 2 +- include/overworld_encounters.h | 6 ++-- .../movement_action_func_tables.h | 2 +- src/event_object_movement.c | 34 ++++++++++++++++--- 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 317f14c7db58..160fc2668656 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -500,6 +500,7 @@ u8 MovementType_Invisible_Step2(struct ObjectEvent *objectEvent, struct Sprite * u8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_WanderAround_OverworldWildEncounter_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Common_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_ChasePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_ChasePlayer_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite); diff --git a/include/overworld_encounter_species_behavior.h b/include/overworld_encounter_species_behavior.h index ba4df4c79a91..c58eb634a3cc 100644 --- a/include/overworld_encounter_species_behavior.h +++ b/include/overworld_encounter_species_behavior.h @@ -1,7 +1,7 @@ #ifndef GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H #define GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H -const struct MonSpeciesOWEData sOWESpeciesBehaviors[OWE_SPECIES_BEHAVIOR_COUNT] = { +static const struct MonSpeciesOWEData sOWESpeciesBehaviors[OWE_SPECIES_BEHAVIOR_COUNT] = { [OWE_IGNORE_PLAYER] = { .behavior = OWE_BEHAVIOR_WANDER_AROUND, }, diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 42f73be8efe2..8b04e0e177be 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -60,13 +60,13 @@ enum OverworldEncounterBehaviors OWE_BEHAVIOR_COUNT }; +// OWE_SPEED_FASTER seems to visually bug out sometimes. enum OWESpeeds { OWE_SPEED_NORMAL, - OWE_SPEED_FAST, - OWE_SPEED_FASTER, OWE_SPEED_SLOW, - OWE_SPEED_SLOWER + OWE_SPEED_FAST, + OWE_SPEED_FASTER }; struct MonSpeciesOWEData diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index 42f29a283a9f..8c31135b8776 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1725,7 +1725,7 @@ u8 (*const gMovementActionFuncs_WalkFastDiagonalDownRight[])(struct ObjectEvent MovementType_WanderAround_OverworldWildEncounter_Step2, \ MovementType_WanderAround_OverworldWildEncounter_Step3, \ MovementType_WanderAround_OverworldWildEncounter_Step4, \ - MovementType_WanderAround_Step5, \ + MovementType_WanderAround_OverworldWildEncounter_Step5, \ MovementType_WanderAround_Step6 u8 (*const gMovementTypeFuncs_WanderAround_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 94530634d15c..0e75af28a464 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -26,6 +26,7 @@ #include "metatile_behavior.h" #include "overworld.h" #include "overworld_encounters.h" +#include "overworld_encounter_species_behavior.h" #include "palette.h" #include "party_menu.h" #include "pokemon.h" @@ -11712,6 +11713,21 @@ static u32 TurnDirectionNinetyDegrees(u32 direction, bool32 counterclockwise) return DIR_NONE; } +static u8 GetWalkMovementActionInDirectionWithSpeed(u32 direction, u32 speed) +{ + switch (speed) + { + case OWE_SPEED_SLOW: + return GetWalkSlowMovementAction(direction); + case OWE_SPEED_FAST: + return GetWalkFastMovementAction(direction); + case OWE_SPEED_FASTER: + return GetWalkFasterMovementAction(direction); + } + + return GetWalkNormalMovementAction(direction); +} + bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent) { gFieldEffectArguments[0] = objEvent->currentCoords.x; @@ -11772,6 +11788,14 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent return TRUE; } +bool8 MovementType_WanderAround_OverworldWildEncounter_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[OW_SPECIES(objectEvent)].overworldEncounterBehavior].idleSpeed)); + objectEvent->singleMovementActive = TRUE; + sprite->sTypeFuncId = 6; + return TRUE; +} + movement_type_def(MovementType_ChasePlayer_OverworldWildEncounter, gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter) bool8 MovementType_Common_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite) @@ -11821,11 +11845,12 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step10(struct ObjectEvent bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { + u16 speciesId = OW_SPECIES(objectEvent); bool8 collision; u8 movementActionId; collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); - movementActionId = GetWalkSlowMovementAction(objectEvent->movementDirection); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); sprite->sTypeFuncId = 12; if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) @@ -11841,7 +11866,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent return FALSE; } u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - movementActionId = GetWalkSlowMovementAction(newDirection); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); collision = GetCollisionInDirection(objectEvent, newDirection); if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, newDirection)) || collision) @@ -11919,11 +11944,12 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent * bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { + u16 speciesId = OW_SPECIES(objectEvent); bool8 collision; u8 movementActionId; collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); - movementActionId = GetWalkNormalMovementAction(objectEvent->movementDirection); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) { @@ -11934,7 +11960,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * { newDirection = GetOppositeDirection(newDirection); } - movementActionId = GetWalkNormalMovementAction(newDirection); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); collision = GetCollisionInDirection(objectEvent, newDirection); if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, newDirection)) || collision) From 8ac912d35f784c6760a8b818996e253f81149a2d Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 24 Dec 2025 18:39:46 -0600 Subject: [PATCH 231/572] Active distance is now read from the species --- include/overworld_encounter_species_behavior.h | 6 ++++-- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 9 +++++++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/overworld_encounter_species_behavior.h b/include/overworld_encounter_species_behavior.h index c58eb634a3cc..f646ceac064c 100644 --- a/include/overworld_encounter_species_behavior.h +++ b/include/overworld_encounter_species_behavior.h @@ -10,14 +10,16 @@ static const struct MonSpeciesOWEData sOWESpeciesBehaviors[OWE_SPECIES_BEHAVIOR_ .idleSpeed = OWE_SPEED_SLOW, .activeSpeed = OWE_SPEED_SLOW, .viewDistance = 5, - .viewWidth = 3 + .viewWidth = 3, + .activeDistance = 5 }, [OWE_FLEE_PLAYER_NORMAL] = { .behavior = OWE_BEHAVIOR_FLEE, .idleSpeed = OWE_SPEED_NORMAL, .activeSpeed = OWE_SPEED_NORMAL, .viewDistance = 5, - .viewWidth = 3 + .viewWidth = 3, + .activeDistance = 5 } }; diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 8b04e0e177be..3f86141b0f40 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -116,7 +116,7 @@ void TryRemoveOverworldWildEncounter(u32 localId); bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction); void DespawnOldestOWE_Pal(void); bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon); -bool32 OWE_IsPlayerInsideRangeFromMon(struct ObjectEvent *mon, u32 distance); +bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon); u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); void Task_OWE_WaitMovements(u8 taskId); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 0e75af28a464..7579531f0a17 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11884,7 +11884,7 @@ bool8 MovementType_Common_OverworldWildEncounter_Step12(struct ObjectEvent *obje { objectEvent->singleMovementActive = FALSE; - if (!OWE_IsPlayerInsideRangeFromMon(objectEvent, OWE_CHASE_RANGE)) + if (!OWE_IsPlayerInsideMonActiveDistance(objectEvent)) { sprite->sTypeFuncId = 0; return FALSE; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index dba1a76a34e8..7220b05d0767 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -857,7 +857,7 @@ bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon) if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING)) { - if (OWE_IsPlayerInsideRangeFromMon(mon, OWE_MON_SIGHT_LENGTH)) + if (OWE_IsPlayerInsideMonActiveDistance(mon)) return TRUE; } else @@ -890,9 +890,14 @@ bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon) return FALSE; } -bool32 OWE_IsPlayerInsideRangeFromMon(struct ObjectEvent *mon, u32 distance) +bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + u32 distance = OWE_CHASE_RANGE; + u32 speciesId = OW_SPECIES(mon); + + if (speciesId != SPECIES_NONE) + distance = sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeDistance; if (player->currentCoords.y <= mon->currentCoords.y + distance && player->currentCoords.y >= mon->currentCoords.y - distance && player->currentCoords.x <= mon->currentCoords.x + distance && player->currentCoords.x >= mon->currentCoords.x - distance) From f2224671050d3f80ec0d1f86990d6858a43d15e8 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 24 Dec 2025 18:50:36 -0600 Subject: [PATCH 232/572] View distance & width are now read from species --- src/overworld_encounters.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7220b05d0767..f006c3c58c31 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -862,26 +862,30 @@ bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon) } else { + u32 speciesId = OW_SPECIES(mon); + u32 viewDistance = sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].viewDistance; + u32 viewWidth = sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].viewWidth; + switch (mon->facingDirection) { case DIR_NORTH: - if (player->currentCoords.y < mon->currentCoords.y && (mon->currentCoords.y - player->currentCoords.y) <= OWE_MON_SIGHT_LENGTH - && player->currentCoords.x >= (mon->currentCoords.x - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.x <= (mon->currentCoords.x + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) + if (player->currentCoords.y < mon->currentCoords.y && (mon->currentCoords.y - player->currentCoords.y) <= viewDistance + && player->currentCoords.x >= (mon->currentCoords.x - ((viewWidth - 1) / 2)) && player->currentCoords.x <= (mon->currentCoords.x + ((viewWidth - 1) / 2))) return TRUE; break; case DIR_SOUTH: - if (player->currentCoords.y > mon->currentCoords.y && (player->currentCoords.y - mon->currentCoords.y) <= OWE_MON_SIGHT_LENGTH - && player->currentCoords.x >= (mon->currentCoords.x - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.x <= (mon->currentCoords.x + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) + if (player->currentCoords.y > mon->currentCoords.y && (player->currentCoords.y - mon->currentCoords.y) <= viewDistance + && player->currentCoords.x >= (mon->currentCoords.x - ((viewWidth - 1) / 2)) && player->currentCoords.x <= (mon->currentCoords.x + ((viewWidth - 1) / 2))) return TRUE; break; case DIR_EAST: - if (player->currentCoords.x > mon->currentCoords.x && (player->currentCoords.x - mon->currentCoords.x) <= OWE_MON_SIGHT_LENGTH - && player->currentCoords.y >= (mon->currentCoords.y - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.y <= (mon->currentCoords.y + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) + if (player->currentCoords.x > mon->currentCoords.x && (player->currentCoords.x - mon->currentCoords.x) <= viewDistance + && player->currentCoords.y >= (mon->currentCoords.y - ((viewWidth - 1) / 2)) && player->currentCoords.y <= (mon->currentCoords.y + ((viewWidth - 1) / 2))) return TRUE; break; case DIR_WEST: - if (player->currentCoords.x < mon->currentCoords.x && (mon->currentCoords.x - player->currentCoords.x) <= OWE_MON_SIGHT_LENGTH - && player->currentCoords.y >= (mon->currentCoords.y - ((OWE_MON_SIGHT_WIDTH - 1) / 2)) && player->currentCoords.y <= (mon->currentCoords.y + ((OWE_MON_SIGHT_WIDTH - 1) / 2))) + if (player->currentCoords.x < mon->currentCoords.x && (mon->currentCoords.x - player->currentCoords.x) <= viewDistance + && player->currentCoords.y >= (mon->currentCoords.y - ((viewWidth - 1) / 2)) && player->currentCoords.y <= (mon->currentCoords.y + ((viewWidth - 1) / 2))) return TRUE; break; } From 6bdde50c9f79db1d9de29d6dea680c8f4b024d39 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 24 Dec 2025 19:29:14 -0600 Subject: [PATCH 233/572] Added MOVEMENT_TYPE_WATCH_PLAYER_OWE --- include/constants/event_object_movement.h | 3 +- include/event_object_movement.h | 7 ++- .../overworld_encounter_species_behavior.h | 15 ++++-- include/overworld_encounters.h | 6 ++- .../movement_action_func_tables.h | 15 +++++- src/event_object_movement.c | 54 ++++++++++++++----- src/overworld_encounters.c | 5 +- 7 files changed, 80 insertions(+), 25 deletions(-) diff --git a/include/constants/event_object_movement.h b/include/constants/event_object_movement.h index 27202b505148..d5ae42111e76 100755 --- a/include/constants/event_object_movement.h +++ b/include/constants/event_object_movement.h @@ -86,7 +86,8 @@ #define MOVEMENT_TYPE_WANDER_AROUND_OWE 0x52 #define MOVEMENT_TYPE_CHASE_PLAYER_OWE 0x53 #define MOVEMENT_TYPE_FLEE_PLAYER_OWE 0x54 -#define NUM_MOVEMENT_TYPES 0x55 +#define MOVEMENT_TYPE_WATCH_PLAYER_OWE 0x55 +#define NUM_MOVEMENT_TYPES 0x56 #define MOVEMENT_ACTION_FACE_DOWN 0x0 #define MOVEMENT_ACTION_FACE_UP 0x1 diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 160fc2668656..05f4ebaedfda 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -323,6 +323,7 @@ void MovementType_FollowPlayer(struct Sprite *sprite); void MovementType_WanderAround_OverworldWildEncounter(struct Sprite *sprite); void MovementType_ChasePlayer_OverworldWildEncounter(struct Sprite *sprite); void MovementType_FleePlayer_OverworldWildEncounter(struct Sprite *sprite); +void MovementType_WatchPlayer_OverworldWildEncounter(struct Sprite *sprite); u8 GetSlideMovementAction(u32); u8 GetJump2MovementAction(u32); u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); @@ -503,14 +504,16 @@ u8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent *ob u8 MovementType_WanderAround_OverworldWildEncounter_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Common_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_ChasePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ChasePlayer_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_Common_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_ChasePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Common_OverworldWildEncounter_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_FleePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_FleePlayer_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_WatchPlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_WatchPlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_WatchPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevation, u8 direction); void TurnVirtualObject(u8 virtualObjId, u8 direction); diff --git a/include/overworld_encounter_species_behavior.h b/include/overworld_encounter_species_behavior.h index f646ceac064c..2fd51ce36c67 100644 --- a/include/overworld_encounter_species_behavior.h +++ b/include/overworld_encounter_species_behavior.h @@ -6,18 +6,25 @@ static const struct MonSpeciesOWEData sOWESpeciesBehaviors[OWE_SPECIES_BEHAVIOR_ .behavior = OWE_BEHAVIOR_WANDER_AROUND, }, [OWE_CHASE_PLAYER_SLOW] = { - .behavior = OWE_BEHAVIOR_CHASE, + .behavior = OWE_BEHAVIOR_CHASE_PLAYER, .idleSpeed = OWE_SPEED_SLOW, .activeSpeed = OWE_SPEED_SLOW, - .viewDistance = 5, + .viewDistance = 4, .viewWidth = 3, .activeDistance = 5 }, [OWE_FLEE_PLAYER_NORMAL] = { - .behavior = OWE_BEHAVIOR_FLEE, + .behavior = OWE_BEHAVIOR_FLEE_PLAYER, .idleSpeed = OWE_SPEED_NORMAL, .activeSpeed = OWE_SPEED_NORMAL, - .viewDistance = 5, + .viewDistance = 4, + .viewWidth = 3, + .activeDistance = 5 + }, + [OWE_WATCH_PLAYER_NORMAL] = { + .behavior = OWE_BEHAVIOR_WATCH_PLAYER, + .idleSpeed = OWE_SPEED_NORMAL, + .viewDistance = 4, .viewWidth = 3, .activeDistance = 5 } diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 3f86141b0f40..b458458c8e9e 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -55,8 +55,9 @@ enum OverworldObjectEncounterType enum OverworldEncounterBehaviors { OWE_BEHAVIOR_WANDER_AROUND, - OWE_BEHAVIOR_CHASE, - OWE_BEHAVIOR_FLEE, + OWE_BEHAVIOR_CHASE_PLAYER, + OWE_BEHAVIOR_FLEE_PLAYER, + OWE_BEHAVIOR_WATCH_PLAYER, OWE_BEHAVIOR_COUNT }; @@ -85,6 +86,7 @@ enum OWESpeciesBehaviors OWE_IGNORE_PLAYER, OWE_CHASE_PLAYER_SLOW, OWE_FLEE_PLAYER_NORMAL, + OWE_WATCH_PLAYER_NORMAL, OWE_SPECIES_BEHAVIOR_COUNT }; diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index 8c31135b8776..5b66fda8eec2 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1738,7 +1738,7 @@ u8 (*const gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter[])(struct Objec OWE_WANDER_AROUND_COMMON_STEPS, MovementType_Common_OverworldWildEncounter_Step7, MovementType_ChasePlayer_OverworldWildEncounter_Step8, - MovementType_ChasePlayer_OverworldWildEncounter_Step9, + MovementType_Common_OverworldWildEncounter_Step9, MovementType_ChasePlayer_OverworldWildEncounter_Step10, MovementType_ChasePlayer_OverworldWildEncounter_Step11, MovementType_Common_OverworldWildEncounter_Step12, @@ -1749,8 +1749,19 @@ u8 (*const gMovementTypeFuncs_FleePlayer_OverworldWildEncounter[])(struct Object OWE_WANDER_AROUND_COMMON_STEPS, MovementType_Common_OverworldWildEncounter_Step7, MovementType_FleePlayer_OverworldWildEncounter_Step8, - MovementType_FleePlayer_OverworldWildEncounter_Step9, + MovementType_Common_OverworldWildEncounter_Step9, MovementType_FleePlayer_OverworldWildEncounter_Step10, MovementType_FleePlayer_OverworldWildEncounter_Step11, MovementType_Common_OverworldWildEncounter_Step12, }; + +u8 (*const gMovementTypeFuncs_WatchPlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = +{ + OWE_WANDER_AROUND_COMMON_STEPS, + MovementType_Common_OverworldWildEncounter_Step7, + MovementType_WatchPlayer_OverworldWildEncounter_Step8, + MovementType_Common_OverworldWildEncounter_Step9, + MovementType_WatchPlayer_OverworldWildEncounter_Step10, + MovementType_WatchPlayer_OverworldWildEncounter_Step11, + MovementType_Common_OverworldWildEncounter_Step12, +}; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 7579531f0a17..a1f81528d207 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -350,6 +350,7 @@ static void (*const sMovementTypeCallbacks[])(struct Sprite *) = [MOVEMENT_TYPE_WANDER_AROUND_OWE] = MovementType_WanderAround_OverworldWildEncounter, [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = MovementType_ChasePlayer_OverworldWildEncounter, [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = MovementType_FleePlayer_OverworldWildEncounter, + [MOVEMENT_TYPE_WATCH_PLAYER_OWE] = MovementType_WatchPlayer_OverworldWildEncounter, }; static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { @@ -397,6 +398,7 @@ static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_WANDER_AROUND_OWE] = TRUE, [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = TRUE, [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = TRUE, + [MOVEMENT_TYPE_WATCH_PLAYER_OWE] = TRUE, }; const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { @@ -485,6 +487,7 @@ const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_WANDER_AROUND_OWE] = DIR_SOUTH, [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = DIR_SOUTH, [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = DIR_SOUTH, + [MOVEMENT_TYPE_WATCH_PLAYER_OWE] = DIR_SOUTH, }; #include "data/object_events/object_event_graphics_info_pointers.h" @@ -11825,7 +11828,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step8(struct ObjectEvent * return TRUE; } -bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_Common_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { @@ -11909,17 +11912,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *o return TRUE; } -bool8 MovementType_FleePlayer_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite) -{ - if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) - { - sprite->sTypeFuncId = 10; - } - return TRUE; -} - #define sCollisionTimer sprite->data[6] -#define STARTED 1 bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { @@ -11984,5 +11977,42 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * return TRUE; } +movement_type_def(MovementType_WatchPlayer_OverworldWildEncounter, gMovementTypeFuncs_WatchPlayer_OverworldWildEncounter) + +bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); + + SetObjectEventDirection(objectEvent, direction); + + if (!OWE_IsMonNextToPlayer(objectEvent)) + { + ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_QUESTION_MARK); + sprite->sTypeFuncId = 9; + } + else + { + sprite->sTypeFuncId = 10; + } + + return TRUE; +} + +bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); + + SetObjectEventDirection(objectEvent, direction); + sprite->sTypeFuncId = 11; + return TRUE; +} + +bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection)); + objectEvent->singleMovementActive = TRUE; + sprite->sTypeFuncId = 12; + return TRUE; +} + #undef sCollisionTimer -#undef STARTED diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f006c3c58c31..08c4114f9868 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -47,8 +47,9 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = { [OWE_BEHAVIOR_WANDER_AROUND] = MOVEMENT_TYPE_WANDER_AROUND_OWE, - [OWE_BEHAVIOR_CHASE] = MOVEMENT_TYPE_CHASE_PLAYER_OWE, - [OWE_BEHAVIOR_FLEE] = MOVEMENT_TYPE_FLEE_PLAYER_OWE, + [OWE_BEHAVIOR_CHASE_PLAYER] = MOVEMENT_TYPE_CHASE_PLAYER_OWE, + [OWE_BEHAVIOR_FLEE_PLAYER] = MOVEMENT_TYPE_FLEE_PLAYER_OWE, + [OWE_BEHAVIOR_WATCH_PLAYER] = MOVEMENT_TYPE_WATCH_PLAYER_OWE, }; void LoadOverworldEncounterData(void) From ff89f19ffc2df23ec4783b458507426d40242da4 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 24 Dec 2025 22:30:50 -0600 Subject: [PATCH 234/572] Added MOVEMENT_TYPE_APPROACH_PLAYER_OWE --- include/constants/event_object_movement.h | 3 +- include/event_object_movement.h | 4 + .../overworld_encounter_species_behavior.h | 8 ++ .../movement_action_func_tables.h | 11 ++ src/event_object_movement.c | 118 +++++++++++++++++- src/overworld_encounters.c | 38 +++++- 6 files changed, 174 insertions(+), 8 deletions(-) diff --git a/include/constants/event_object_movement.h b/include/constants/event_object_movement.h index d5ae42111e76..1364caaf13a8 100755 --- a/include/constants/event_object_movement.h +++ b/include/constants/event_object_movement.h @@ -87,7 +87,8 @@ #define MOVEMENT_TYPE_CHASE_PLAYER_OWE 0x53 #define MOVEMENT_TYPE_FLEE_PLAYER_OWE 0x54 #define MOVEMENT_TYPE_WATCH_PLAYER_OWE 0x55 -#define NUM_MOVEMENT_TYPES 0x56 +#define MOVEMENT_TYPE_APPROACH_PLAYER_OWE 0x56 +#define NUM_MOVEMENT_TYPES 0x57 #define MOVEMENT_ACTION_FACE_DOWN 0x0 #define MOVEMENT_ACTION_FACE_UP 0x1 diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 05f4ebaedfda..1520d10e0393 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -324,6 +324,7 @@ void MovementType_WanderAround_OverworldWildEncounter(struct Sprite *sprite); void MovementType_ChasePlayer_OverworldWildEncounter(struct Sprite *sprite); void MovementType_FleePlayer_OverworldWildEncounter(struct Sprite *sprite); void MovementType_WatchPlayer_OverworldWildEncounter(struct Sprite *sprite); +void MovementType_ApproachPlayer_OverworldWildEncounter(struct Sprite *sprite); u8 GetSlideMovementAction(u32); u8 GetJump2MovementAction(u32); u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); @@ -514,6 +515,9 @@ u8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *obj u8 MovementType_WatchPlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WatchPlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WatchPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ApproachPlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ApproachPlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevation, u8 direction); void TurnVirtualObject(u8 virtualObjId, u8 direction); diff --git a/include/overworld_encounter_species_behavior.h b/include/overworld_encounter_species_behavior.h index 2fd51ce36c67..27d8a7dc2db4 100644 --- a/include/overworld_encounter_species_behavior.h +++ b/include/overworld_encounter_species_behavior.h @@ -27,6 +27,14 @@ static const struct MonSpeciesOWEData sOWESpeciesBehaviors[OWE_SPECIES_BEHAVIOR_ .viewDistance = 4, .viewWidth = 3, .activeDistance = 5 + }, + [OWE_APPROACH_PLAYER_SLOW] = { + .behavior = OWE_BEHAVIOR_APPROACH_PLAYER, + .idleSpeed = OWE_SPEED_NORMAL, + .activeSpeed = OWE_SPEED_SLOW, + .viewDistance = 4, + .viewWidth = 3, + .activeDistance = 5 } }; diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index 5b66fda8eec2..b14219ea4c75 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1765,3 +1765,14 @@ u8 (*const gMovementTypeFuncs_WatchPlayer_OverworldWildEncounter[])(struct Objec MovementType_WatchPlayer_OverworldWildEncounter_Step11, MovementType_Common_OverworldWildEncounter_Step12, }; + +u8 (*const gMovementTypeFuncs_ApproachPlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = +{ + OWE_WANDER_AROUND_COMMON_STEPS, + MovementType_Common_OverworldWildEncounter_Step7, + MovementType_ApproachPlayer_OverworldWildEncounter_Step8, + MovementType_Common_OverworldWildEncounter_Step9, + MovementType_ApproachPlayer_OverworldWildEncounter_Step10, + MovementType_ApproachPlayer_OverworldWildEncounter_Step11, + MovementType_Common_OverworldWildEncounter_Step12, +}; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index a1f81528d207..bec3ee35ad37 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -351,6 +351,7 @@ static void (*const sMovementTypeCallbacks[])(struct Sprite *) = [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = MovementType_ChasePlayer_OverworldWildEncounter, [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = MovementType_FleePlayer_OverworldWildEncounter, [MOVEMENT_TYPE_WATCH_PLAYER_OWE] = MovementType_WatchPlayer_OverworldWildEncounter, + [MOVEMENT_TYPE_APPROACH_PLAYER_OWE] = MovementType_ApproachPlayer_OverworldWildEncounter, }; static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { @@ -399,6 +400,7 @@ static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = TRUE, [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = TRUE, [MOVEMENT_TYPE_WATCH_PLAYER_OWE] = TRUE, + [MOVEMENT_TYPE_APPROACH_PLAYER_OWE] = TRUE, }; const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { @@ -488,6 +490,7 @@ const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = DIR_SOUTH, [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = DIR_SOUTH, [MOVEMENT_TYPE_WATCH_PLAYER_OWE] = DIR_SOUTH, + [MOVEMENT_TYPE_APPROACH_PLAYER_OWE] = DIR_SOUTH, }; #include "data/object_events/object_event_graphics_info_pointers.h" @@ -11977,6 +11980,8 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * return TRUE; } +#undef sCollisionTimer + movement_type_def(MovementType_WatchPlayer_OverworldWildEncounter, gMovementTypeFuncs_WatchPlayer_OverworldWildEncounter) bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) @@ -12015,4 +12020,115 @@ bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step11(struct ObjectEvent return TRUE; } -#undef sCollisionTimer +movement_type_def(MovementType_ApproachPlayer_OverworldWildEncounter, gMovementTypeFuncs_ApproachPlayer_OverworldWildEncounter) + +#define sJumpTimer sprite->data[7] + +bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); + + SetObjectEventDirection(objectEvent, direction); + sJumpTimer = (Random() % (OWE_APPROACH_JUMP_TIMER_MAX - OWE_APPROACH_JUMP_TIMER_MIN)) + OWE_APPROACH_JUMP_TIMER_MIN; + + if (!OWE_IsMonNextToPlayer(objectEvent)) + { + ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_QUESTION_MARK); + sprite->sTypeFuncId = 9; + } + else + { + sprite->sTypeFuncId = 10; + } + + return TRUE; +} + +bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); + + SetObjectEventDirection(objectEvent, direction); + sprite->sTypeFuncId = 11; + return TRUE; +} + +bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + bool32 equalDistances = FALSE; + u32 distance = OWE_GetApproachingMonDistanceToPlayer(objectEvent, &equalDistances); + u16 speciesId = OW_SPECIES(objectEvent); + bool8 collision; + u8 movementActionId; + + if (distance <= 1 && !OWE_IsWaitTaskActive()) + { + SetObjectEventDirection(objectEvent, GetOppositeDirection(objectEvent->movementDirection)); + + collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) + { + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); + + if (!OWE_IsWaitTaskActive() && !(objectEvent->currentCoords.x == player->currentCoords.x || objectEvent->currentCoords.y == player->currentCoords.y)) + { + newDirection = GetOppositeDirection(newDirection); + } + movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + collision = GetCollisionInDirection(objectEvent, newDirection); + + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, newDirection)) || collision) + { + movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); + } + } + } + else if (distance == OWE_APPROACH_DISTANCE && !equalDistances && !OWE_IsWaitTaskActive()) + { + if (sJumpTimer <= 0) + { + sJumpTimer = (Random() % (OWE_APPROACH_JUMP_TIMER_MAX - OWE_APPROACH_JUMP_TIMER_MIN)) + OWE_APPROACH_JUMP_TIMER_MIN; + movementActionId = GetJumpInPlaceMovementAction(objectEvent->facingDirection); + PlaySE(SE_LEDGE); + } + else + { + sJumpTimer--; + movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); + } + } + else + { + collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) + { + s16 x = objectEvent->currentCoords.x; + s16 y = objectEvent->currentCoords.y; + MoveCoords(objectEvent->movementDirection, &x, &y); + // If colliding with the player object, don't try to walk around it. + if (GetObjectObjectCollidesWith(objectEvent, x, y, FALSE) == gPlayerAvatar.objectEventId) + { + ObjectEventSetSingleMovement(objectEvent, sprite, GetFaceDirectionMovementAction(objectEvent->facingDirection)); + objectEvent->singleMovementActive = TRUE; + return FALSE; + } + u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + collision = GetCollisionInDirection(objectEvent, newDirection); + + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, newDirection)) || collision) + movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); + } + + sJumpTimer = (Random() % (OWE_APPROACH_JUMP_TIMER_MAX - OWE_APPROACH_JUMP_TIMER_MIN)) + OWE_APPROACH_JUMP_TIMER_MIN; + } + + ObjectEventSetSingleMovement(objectEvent, sprite, movementActionId); + objectEvent->singleMovementActive = TRUE; + sprite->sTypeFuncId = 12; + return TRUE; +} diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 08c4114f9868..5e6ec48c9ab5 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -46,10 +46,11 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = { - [OWE_BEHAVIOR_WANDER_AROUND] = MOVEMENT_TYPE_WANDER_AROUND_OWE, - [OWE_BEHAVIOR_CHASE_PLAYER] = MOVEMENT_TYPE_CHASE_PLAYER_OWE, - [OWE_BEHAVIOR_FLEE_PLAYER] = MOVEMENT_TYPE_FLEE_PLAYER_OWE, - [OWE_BEHAVIOR_WATCH_PLAYER] = MOVEMENT_TYPE_WATCH_PLAYER_OWE, + [OWE_BEHAVIOR_WANDER_AROUND] = MOVEMENT_TYPE_WANDER_AROUND_OWE, + [OWE_BEHAVIOR_CHASE_PLAYER] = MOVEMENT_TYPE_CHASE_PLAYER_OWE, + [OWE_BEHAVIOR_FLEE_PLAYER] = MOVEMENT_TYPE_FLEE_PLAYER_OWE, + [OWE_BEHAVIOR_WATCH_PLAYER] = MOVEMENT_TYPE_WATCH_PLAYER_OWE, + [OWE_BEHAVIOR_APPROACH_PLAYER] = MOVEMENT_TYPE_APPROACH_PLAYER_OWE, }; void LoadOverworldEncounterData(void) @@ -975,8 +976,33 @@ bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon) return TRUE; } -#define NOT_STARTED 0 -#define STARTED 1 +u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances) +{ + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + s16 absX, absY; + s16 distanceX = player->currentCoords.x - mon->currentCoords.x; + s16 distanceY = player->currentCoords.y - mon->currentCoords.y; + + // Get absolute X distance. + if (distanceX < 0) + absX = distanceX * -1; + else + absX = distanceX; + + // Get absolute Y distance. + if (distanceY < 0) + absY = distanceY * -1; + else + absY = distanceY; + + if (absY == absX) + *equalDistances = TRUE; + + if (absY > absX) + return absY; + else + return absX; +} void Task_OWE_WaitMovements(u8 taskId) { From ea7331064863b9ebe114e7b873b866d878265712 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 25 Dec 2025 22:49:19 -0600 Subject: [PATCH 235/572] Missed these for approach behavior commit --- include/overworld_encounters.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index b458458c8e9e..0940ccb345f3 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -27,6 +27,10 @@ #define OWE_MON_SIGHT_LENGTH 4 #define OWE_CHASE_RANGE 5 +#define OWE_APPROACH_DISTANCE 2 + +#define OWE_APPROACH_JUMP_TIMER_MIN 16 +#define OWE_APPROACH_JUMP_TIMER_MAX 64 #define OWE_FLEE_COLLISION_TIME 6 // If a fleeing mon is unable to take a step for this many tries it will despawn. (Multiply this value by 16 to get number of frames.) @@ -58,6 +62,7 @@ enum OverworldEncounterBehaviors OWE_BEHAVIOR_CHASE_PLAYER, OWE_BEHAVIOR_FLEE_PLAYER, OWE_BEHAVIOR_WATCH_PLAYER, + OWE_BEHAVIOR_APPROACH_PLAYER, OWE_BEHAVIOR_COUNT }; @@ -87,6 +92,7 @@ enum OWESpeciesBehaviors OWE_CHASE_PLAYER_SLOW, OWE_FLEE_PLAYER_NORMAL, OWE_WATCH_PLAYER_NORMAL, + OWE_APPROACH_PLAYER_SLOW, OWE_SPECIES_BEHAVIOR_COUNT }; @@ -121,6 +127,7 @@ bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon); bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon); u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); +u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); void Task_OWE_WaitMovements(u8 taskId); bool32 OWE_IsWaitTaskActive(void); u32 OWE_GetDespawnAnimType(u32 metatileBehavior); From 882e95175eddc6048bfc4f16bf79490360aa5a3d Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 25 Dec 2025 22:50:33 -0600 Subject: [PATCH 236/572] Added MOVEMENT_TYPE_DESPAWN_OWE --- include/constants/event_object_movement.h | 3 +- include/event_object_movement.h | 4 ++ .../overworld_encounter_species_behavior.h | 7 +++ include/overworld_encounters.h | 4 ++ .../movement_action_func_tables.h | 11 +++++ src/event_object_movement.c | 48 +++++++++++++++++++ src/overworld_encounters.c | 1 + 7 files changed, 77 insertions(+), 1 deletion(-) diff --git a/include/constants/event_object_movement.h b/include/constants/event_object_movement.h index 1364caaf13a8..18d3777bb7ca 100755 --- a/include/constants/event_object_movement.h +++ b/include/constants/event_object_movement.h @@ -88,7 +88,8 @@ #define MOVEMENT_TYPE_FLEE_PLAYER_OWE 0x54 #define MOVEMENT_TYPE_WATCH_PLAYER_OWE 0x55 #define MOVEMENT_TYPE_APPROACH_PLAYER_OWE 0x56 -#define NUM_MOVEMENT_TYPES 0x57 +#define MOVEMENT_TYPE_DESPAWN_OWE 0x57 +#define NUM_MOVEMENT_TYPES 0x58 #define MOVEMENT_ACTION_FACE_DOWN 0x0 #define MOVEMENT_ACTION_FACE_UP 0x1 diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 1520d10e0393..dc22d7d99393 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -325,6 +325,7 @@ void MovementType_ChasePlayer_OverworldWildEncounter(struct Sprite *sprite); void MovementType_FleePlayer_OverworldWildEncounter(struct Sprite *sprite); void MovementType_WatchPlayer_OverworldWildEncounter(struct Sprite *sprite); void MovementType_ApproachPlayer_OverworldWildEncounter(struct Sprite *sprite); +void MovementType_Despawn_OverworldWildEncounter(struct Sprite *sprite); u8 GetSlideMovementAction(u32); u8 GetJump2MovementAction(u32); u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); @@ -518,6 +519,9 @@ u8 MovementType_WatchPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *ob u8 MovementType_ApproachPlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_ApproachPlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_Despawn_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_Despawn_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_Despawn_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevation, u8 direction); void TurnVirtualObject(u8 virtualObjId, u8 direction); diff --git a/include/overworld_encounter_species_behavior.h b/include/overworld_encounter_species_behavior.h index 27d8a7dc2db4..974c516420ef 100644 --- a/include/overworld_encounter_species_behavior.h +++ b/include/overworld_encounter_species_behavior.h @@ -35,6 +35,13 @@ static const struct MonSpeciesOWEData sOWESpeciesBehaviors[OWE_SPECIES_BEHAVIOR_ .viewDistance = 4, .viewWidth = 3, .activeDistance = 5 + }, + [OWE_DESPAWN_ON_NOTICE] = { + .behavior = OWE_BEHAVIOR_DESPAWN, + .idleSpeed = OWE_SPEED_NORMAL, + .viewDistance = 4, + .viewWidth = 3, + .activeDistance = 5 } }; diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 0940ccb345f3..4f2a56c907ef 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -34,6 +34,8 @@ #define OWE_FLEE_COLLISION_TIME 6 // If a fleeing mon is unable to take a step for this many tries it will despawn. (Multiply this value by 16 to get number of frames.) +#define OWE_DESPAWN_FRAMES 30 // Number of frames before a mon despawns after noticing the player (OWE_BEHAVIOR_DESPAWN) + #define INVALID_SPAWN_SLOT 0xFF enum OverworldEncounterSpawnAnim @@ -63,6 +65,7 @@ enum OverworldEncounterBehaviors OWE_BEHAVIOR_FLEE_PLAYER, OWE_BEHAVIOR_WATCH_PLAYER, OWE_BEHAVIOR_APPROACH_PLAYER, + OWE_BEHAVIOR_DESPAWN, OWE_BEHAVIOR_COUNT }; @@ -93,6 +96,7 @@ enum OWESpeciesBehaviors OWE_FLEE_PLAYER_NORMAL, OWE_WATCH_PLAYER_NORMAL, OWE_APPROACH_PLAYER_SLOW, + OWE_DESPAWN_ON_NOTICE, OWE_SPECIES_BEHAVIOR_COUNT }; diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index b14219ea4c75..b5a50bd64f67 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1776,3 +1776,14 @@ u8 (*const gMovementTypeFuncs_ApproachPlayer_OverworldWildEncounter[])(struct Ob MovementType_ApproachPlayer_OverworldWildEncounter_Step11, MovementType_Common_OverworldWildEncounter_Step12, }; + +u8 (*const gMovementTypeFuncs_Despawn_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = +{ + OWE_WANDER_AROUND_COMMON_STEPS, + MovementType_Common_OverworldWildEncounter_Step7, + MovementType_Despawn_OverworldWildEncounter_Step8, + MovementType_Common_OverworldWildEncounter_Step9, + MovementType_Despawn_OverworldWildEncounter_Step10, + MovementType_Despawn_OverworldWildEncounter_Step11, + MovementType_Common_OverworldWildEncounter_Step12, +}; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index bec3ee35ad37..ac32efc85c0b 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -352,6 +352,7 @@ static void (*const sMovementTypeCallbacks[])(struct Sprite *) = [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = MovementType_FleePlayer_OverworldWildEncounter, [MOVEMENT_TYPE_WATCH_PLAYER_OWE] = MovementType_WatchPlayer_OverworldWildEncounter, [MOVEMENT_TYPE_APPROACH_PLAYER_OWE] = MovementType_ApproachPlayer_OverworldWildEncounter, + [MOVEMENT_TYPE_DESPAWN_OWE] = MovementType_Despawn_OverworldWildEncounter, }; static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { @@ -401,6 +402,7 @@ static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = TRUE, [MOVEMENT_TYPE_WATCH_PLAYER_OWE] = TRUE, [MOVEMENT_TYPE_APPROACH_PLAYER_OWE] = TRUE, + [MOVEMENT_TYPE_DESPAWN_OWE] = TRUE, }; const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { @@ -491,6 +493,7 @@ const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = DIR_SOUTH, [MOVEMENT_TYPE_WATCH_PLAYER_OWE] = DIR_SOUTH, [MOVEMENT_TYPE_APPROACH_PLAYER_OWE] = DIR_SOUTH, + [MOVEMENT_TYPE_DESPAWN_OWE] = DIR_SOUTH, }; #include "data/object_events/object_event_graphics_info_pointers.h" @@ -12132,3 +12135,48 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve sprite->sTypeFuncId = 12; return TRUE; } + +movement_type_def(MovementType_Despawn_OverworldWildEncounter, gMovementTypeFuncs_Despawn_OverworldWildEncounter) + +#define sDespawnTimer sprite->data[6] + +bool8 MovementType_Despawn_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); + + SetObjectEventDirection(objectEvent, direction); + ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK); + PlaySE(SE_PIN); + sDespawnTimer = 0; + sprite->sTypeFuncId = 9; + + return TRUE; +} + +bool8 MovementType_Despawn_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); + + SetObjectEventDirection(objectEvent, direction); + sprite->sTypeFuncId = 11; + return TRUE; +} + +bool8 MovementType_Despawn_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + if (sDespawnTimer == OWE_DESPAWN_FRAMES) + { + u32 animType = OWE_GetDespawnAnimType(objectEvent->currentMetatileBehavior); + MovementAction_OverworldEncounterSpawn(animType, objectEvent); + RemoveObjectEvent(objectEvent); + return FALSE; + } + + ObjectEventSetSingleMovement(objectEvent, sprite, GetFaceDirectionMovementAction(objectEvent->facingDirection)); + objectEvent->singleMovementActive = TRUE; + sDespawnTimer++; + sprite->sTypeFuncId = 12; + return TRUE; +} + +#undef sDespawnTimer diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5e6ec48c9ab5..21d97c4441fe 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -51,6 +51,7 @@ static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = [OWE_BEHAVIOR_FLEE_PLAYER] = MOVEMENT_TYPE_FLEE_PLAYER_OWE, [OWE_BEHAVIOR_WATCH_PLAYER] = MOVEMENT_TYPE_WATCH_PLAYER_OWE, [OWE_BEHAVIOR_APPROACH_PLAYER] = MOVEMENT_TYPE_APPROACH_PLAYER_OWE, + [OWE_BEHAVIOR_DESPAWN] = MOVEMENT_TYPE_DESPAWN_OWE }; void LoadOverworldEncounterData(void) From 20f79bb5540310a7653c07044b6f11d423d2a6e0 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 26 Dec 2025 23:07:06 +0000 Subject: [PATCH 237/572] Refactor Despawn Anims into OnRemovedFunction --- include/overworld_encounters.h | 2 +- src/battle_setup.c | 2 +- src/event_object_movement.c | 2 -- src/overworld_encounters.c | 24 +++++++++++++++++------- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 16921c6f7bc3..685de9e1f4a2 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -80,7 +80,7 @@ u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); u32 RemoveOldestOverworldEncounter(void); -void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent); +void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 spawn); bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); diff --git a/src/battle_setup.c b/src/battle_setup.c index e79f3d775755..f68ed75e3857 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -253,12 +253,12 @@ static void Task_BattleStart(u8 taskId) case 1: if (IsBattleTransitionDone() == TRUE) { - TryRemoveOverworldWildEncounter(gSpecialVar_LastTalked); PrepareForFollowerNPCBattle(); CleanupOverworldWindowsAndTilemaps(); SetMainCallback2(CB2_InitBattle); RestartWildEncounterImmunitySteps(); ClearPoisonStepCounter(); + TryRemoveOverworldWildEncounter(gSpecialVar_LastTalked); DestroyTask(taskId); } break; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 3c65d5e3bece..141ca5ce0f03 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11901,8 +11901,6 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent * { if (OW_WILD_ENCOUNTERS_FLEE_DESPAWN && sCollisionTimer >= OWE_FLEE_COLLISION_TIME) { - u32 animType = OWE_GetDespawnAnimType(objectEvent->currentMetatileBehavior); - MovementAction_OverworldEncounterSpawn(animType, objectEvent); RemoveObjectEvent(objectEvent); return FALSE; } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 128355c0a220..513be61d973a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1,6 +1,7 @@ #include "global.h" #include "overworld_encounters.h" #include "battle_setup.h" +#include "battle_main.h" #include "event_data.h" #include "event_object_movement.h" #include "fieldmap.h" @@ -185,13 +186,17 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is return TRUE; } -void OWE_DoSpawnAnim(struct ObjectEvent *objectEvent) +void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 spawn) { enum OverworldEncounterSpawnAnim spawnAnimType; bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; - OWE_PlayMonObjectCry(objectEvent); - if (isShiny) + if (spawn) + OWE_PlayMonObjectCry(objectEvent); + else + PlaySE(SE_FLEE); + + if (isShiny && spawn) { PlaySE(SE_SHINY); spawnAnimType = OWE_SPAWN_ANIM_SHINY; @@ -466,17 +471,22 @@ static void SortOWEMonAges(void) void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { + if (!IsOverworldWildEncounter(objectEvent)) + return; + if (IsGeneratedOverworldWildEncounter(objectEvent)) SortOWEMonAges(); - if (IsOverworldWildEncounter(objectEvent)) - OWE_DoSpawnAnim(objectEvent); + OWE_DoSpawnDespawnAnim(objectEvent, TRUE); } void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { - // Currently Unused - if (IsGeneratedOverworldWildEncounter(objectEvent)) {} + if (!IsOverworldWildEncounter(objectEvent)) + return; + + if (gMain.callback2 != CB2_InitBattle) + OWE_DoSpawnDespawnAnim(objectEvent, FALSE); } u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) From 4a17a2066bf51810e959122752ffabdd75d7529f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 26 Dec 2025 23:33:27 +0000 Subject: [PATCH 238/572] Change GetActiveEncounterTable to OWE_CheckActiveEncounterTable --- src/overworld_encounters.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 513be61d973a..c1be7d30956b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -33,7 +33,7 @@ static u8 NextSpawnMonSlot(); static bool32 OWE_ShouldSpawnWaterMons(void); static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); static bool8 IsSafeToSpawnObjectEvents(void); -static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater); +static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); @@ -95,7 +95,8 @@ void UpdateOverworldEncounters(void) return; } - if (GetActiveEncounterTable(OWE_ShouldSpawnWaterMons())) + bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); + if (OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) { u16 spawnSlot = NextSpawnMonSlot(); @@ -105,7 +106,6 @@ void UpdateOverworldEncounters(void) return; } - bool32 waterMons = OWE_ShouldSpawnWaterMons(); u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); u32 level; u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level); @@ -150,7 +150,7 @@ void UpdateOverworldEncounters(void) // Hide reflections for spawns in water // (It just looks weird) - if (waterMons) + if (shouldSpawnWaterMons) gObjectEvents[objectEventId].hideReflection = TRUE; // Slower replacement spawning @@ -600,22 +600,22 @@ static bool8 CheckForObjectEventAtLocation(s16 x, s16 y) return FALSE; } -static const struct WildPokemonInfo *GetActiveEncounterTable(bool8 onWater) +static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) { u32 headerId = GetCurrentMapWildMonHeaderId(); + enum TimeOfDay timeOfDay; if (headerId == HEADER_NONE) - return NULL; - - enum TimeOfDay timeOfDay; + return FALSE; - if (onWater) + if (shouldSpawnWaterMons) { timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); - return gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + return gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo != NULL; } + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - return gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + return gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; } bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent) From e7539f1da9315b766a72f03ece9119d6555262ea Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 26 Dec 2025 23:53:28 +0000 Subject: [PATCH 239/572] Add Indoor Mons to TrySelectTile --- src/overworld_encounters.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c1be7d30956b..8f542a680a6b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -297,6 +297,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) s16 playerX, playerY; s16 x, y; u8 closeDistance; + bool32 isEncounterTile = FALSE; const struct MapLayout *layout; // Spawn further away when surfing @@ -353,27 +354,19 @@ static bool8 TrySelectTile(s16* outX, s16* outY) return FALSE; tileBehavior = MapGridGetMetatileBehaviorAt(x, y); - if (OWE_ShouldSpawnWaterMons()) - { - if(MetatileBehavior_IsWaterWildEncounter(tileBehavior) && !MapGridGetCollisionAt(x, y)) - { - *outX = x; - *outY = y; + if (OWE_ShouldSpawnWaterMons() && MetatileBehavior_IsWaterWildEncounter(tileBehavior)) + isEncounterTile = TRUE; - if (!CheckForObjectEventAtLocation(x, y)) - return TRUE; - } - } - else + if (!OWE_ShouldSpawnWaterMons() && (MetatileBehavior_IsLandWildEncounter(tileBehavior) || MetatileBehavior_IsIndoorEncounter(tileBehavior))) + isEncounterTile = TRUE; + + if (isEncounterTile && !MapGridGetCollisionAt(x, y)) { - if(MetatileBehavior_IsLandWildEncounter(tileBehavior) && !MapGridGetCollisionAt(x, y)) - { - *outX = x; - *outY = y; + *outX = x; + *outY = y; - if (!CheckForObjectEventAtLocation(x, y)) - return TRUE; - } + if (!CheckForObjectEventAtLocation(x, y)) + return TRUE; } return FALSE; From b5b610c5376040678d3d9ff12289e1d68245fca8 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 27 Dec 2025 19:57:28 +0000 Subject: [PATCH 240/572] Add Basics for Roamer --- include/constants/global.h | 2 +- include/global.fieldmap.h | 2 +- include/wild_encounter.h | 1 + src/overworld_encounters.c | 40 +++++++++++++++++++++++++++++++++++++- src/wild_encounter.c | 3 +-- 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/include/constants/global.h b/include/constants/global.h index fa0ebbcc0fb2..6f4fa606481d 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -90,7 +90,7 @@ #define GIFT_RIBBONS_COUNT 11 #define SAVED_TRENDS_COUNT 5 #define PYRAMID_BAG_ITEMS_COUNT 10 -#define ROAMER_COUNT 1 // Number of maximum concurrent active roamers +#define ROAMER_COUNT 1 // Number of maximum concurrent active roamers, stored in a u8 in Struct ObjectEvent when used for Overworld Ecnounters. // Bag constants #define BAG_ITEMS_COUNT 30 diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index fdc76a5ed384..378a92998654 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -243,7 +243,7 @@ struct ObjectEvent /*0x1F*/ u8 previousMetatileBehavior; /*0x20*/ u8 previousMovementDirection:4; u8 directionOverwrite:4; - /*0x21*/ u8 directionSequenceIndex; + /*0x21*/ u8 directionSequenceIndex; // Also stores roamer status for Overworld Encounters. /*0x22*/ u8 playerCopyableMovement; // COPY_MOVE_* Also stores age for Overworld Encounters. /*0x23*/ u8 spriteId; /*size = 0x24*/ diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 9f1bf7cfab16..29656a1068b6 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -60,6 +60,7 @@ void FishingWildEncounter(u8 rod); u16 GetLocalWildMon(bool8 *isWaterMon); u16 GetLocalWaterMon(void); bool8 UpdateRepelCounter(void); +bool32 IsWildLevelAllowedByRepel(u8 wildLevel); bool8 TryDoDoubleWildBattle(void); bool8 StandardWildEncounter_Debug(void); u32 CalculateChainFishingShinyRolls(void); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8f542a680a6b..6a18da22bd24 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -10,6 +10,7 @@ #include "metatile_behavior.h" #include "overworld.h" #include "random.h" +#include "roamer.h" #include "script.h" #include "sprite.h" #include "sound.h" @@ -25,6 +26,7 @@ #define sOverworldEncounterLevel trainerRange_berryTreeId #define sAge playerCopyableMovement +#define sRoamerStatus directionSequenceIndex static EWRAM_DATA u8 sOWESpawnCountdown = 0; @@ -81,6 +83,7 @@ void UpdateOverworldEncounters(void) u16 speciesId = SPECIES_NONE; bool32 isShiny = FALSE; bool32 isFemale = FALSE; + u32 roamerIndex = ROAMER_COUNT; s16 x, y; if (sOWESpawnCountdown != 0) @@ -110,7 +113,32 @@ void UpdateOverworldEncounters(void) u32 level; u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level); - if (speciesId == SPECIES_NONE) + if (TryStartRoamerEncounter()) + { + roamerIndex = gEncounteredRoamerIndex; + u32 personality = gSaveBlock1Ptr->roamer[roamerIndex].personality; + speciesId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); + level = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); + + // Consolidate next section into a function? + isShiny = ComputePlayerShinyOdds(personality); + if (GetGenderFromSpeciesAndPersonality(speciesId, personality) == MON_FEMALE) + isFemale = TRUE; + else + isFemale = FALSE; + + // Consolidate ext section into a function? + graphicsId = speciesId + OBJ_EVENT_MON; + if (isFemale) + graphicsId += OBJ_EVENT_MON_FEMALE; + + if (isShiny) + graphicsId += OBJ_EVENT_MON_SHINY; + + ZeroEnemyPartyMons(); + } + + if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(level)) { OWE_ResetSpawnCounterPlayAmbientCry(); return; @@ -144,6 +172,7 @@ void UpdateOverworldEncounters(void) gObjectEvents[objectEventId].range.rangeX = OW_ENCOUNTER_MOVEMENT_RANGE_X; gObjectEvents[objectEventId].range.rangeY = OW_ENCOUNTER_MOVEMENT_RANGE_Y; gObjectEvents[objectEventId].sOverworldEncounterLevel = level; + gObjectEvents[objectEventId].sRoamerStatus = roamerIndex; u8 directions[4] = {DIR_SOUTH, DIR_NORTH, DIR_WEST, DIR_EAST}; ObjectEventTurn(&gObjectEvents[objectEventId], directions[Random() & 3]); @@ -384,6 +413,14 @@ void CreateOverworldWildEncounter(void) if (!IsOverworldWildEncounter(object)) return; + if (object->sRoamerStatus < ROAMER_COUNT) + { + CreateRoamerMonInstance(object->sRoamerStatus); + gEncounteredRoamerIndex = object->sRoamerStatus; + BattleSetup_StartRoamerBattle(); + return; + } + u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; bool32 isFemale = OW_FEMALE(object) ? TRUE : FALSE; @@ -1037,3 +1074,4 @@ static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) #undef STARTED #undef sOverworldEncounterLevel #undef sAge +#undef sRoamerStatus diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 3c3472c816ec..9a66a3d56163 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -47,7 +47,6 @@ extern const u8 EventScript_SprayWoreOff[]; static u16 FeebasRandom(void); static void FeebasSeedRng(u16 seed); -static bool8 IsWildLevelAllowedByRepel(u8 level); static void ApplyFluteEncounterRateMod(u32 *encRate); static void ApplyCleanseTagEncounterRateMod(u32 *encRate); static u8 GetMaxLevelOfSpeciesInWildTable(const struct WildPokemon *wildMon, u16 species, enum WildPokemonArea area); @@ -1085,7 +1084,7 @@ bool8 UpdateRepelCounter(void) return FALSE; } -static bool8 IsWildLevelAllowedByRepel(u8 wildLevel) +bool32 IsWildLevelAllowedByRepel(u8 wildLevel) { u8 i; From 1cb4d971efa4a4f2a4bd5ece6eaeba92618d7725 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 27 Dec 2025 19:59:19 +0000 Subject: [PATCH 241/572] Function Name and Type --- include/overworld_encounters.h | 2 +- src/overworld_encounters.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 685de9e1f4a2..c06dcf390a90 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -92,6 +92,6 @@ bool32 OWE_IsPlayerInsideRangeFromMon(struct ObjectEvent *mon, u32 distance); u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); void Task_OWE_WaitMovements(u8 taskId); -u32 OWE_GetDespawnAnimType(u32 metatileBehavior); +enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 6a18da22bd24..d7c11b08c385 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -233,7 +233,7 @@ void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 spawn) else { u32 metatileBehavior = MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x, objectEvent->currentCoords.y); - spawnAnimType = OWE_GetDespawnAnimType(metatileBehavior); + spawnAnimType = OWE_GetSpawnDespawnAnimType(metatileBehavior); } MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); } @@ -1002,7 +1002,7 @@ void Task_OWE_WaitMovements(u8 taskId) } } -u32 OWE_GetDespawnAnimType(u32 metatileBehavior) +enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior) { // Need to edit anims: // If object is on water then use water anim. From 7867c63a8627b8b65afae08c2ade1d76c2a76ff2 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:47:21 +0000 Subject: [PATCH 242/572] Consolidate Roamer Code --- include/overworld_encounters.h | 2 +- src/overworld_encounters.c | 48 ++++++++++++---------------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index c06dcf390a90..b4cf2e4e759d 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -68,7 +68,7 @@ u32 GetOldestSlot(void); void CreateOverworldWildEncounter(void); void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); -u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); +u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); void OverworldWildEncounter_SetMinimumSpawnTimer(void); u8 CountActiveOverworldEncounters(void); void RemoveAllOverworldEncounterObjects(void); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d7c11b08c385..832407af1d3d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -33,7 +33,7 @@ static EWRAM_DATA u8 sOWESpawnCountdown = 0; static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool32 OWE_ShouldSpawnWaterMons(void); -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level); +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); static bool8 IsSafeToSpawnObjectEvents(void); static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); @@ -111,32 +111,7 @@ void UpdateOverworldEncounters(void) u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); u32 level; - u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level); - - if (TryStartRoamerEncounter()) - { - roamerIndex = gEncounteredRoamerIndex; - u32 personality = gSaveBlock1Ptr->roamer[roamerIndex].personality; - speciesId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); - level = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); - - // Consolidate next section into a function? - isShiny = ComputePlayerShinyOdds(personality); - if (GetGenderFromSpeciesAndPersonality(speciesId, personality) == MON_FEMALE) - isFemale = TRUE; - else - isFemale = FALSE; - - // Consolidate ext section into a function? - graphicsId = speciesId + OBJ_EVENT_MON; - if (isFemale) - graphicsId += OBJ_EVENT_MON_FEMALE; - - if (isShiny) - graphicsId += OBJ_EVENT_MON_SHINY; - - ZeroEnemyPartyMons(); - } + u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &roamerIndex); if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(level)) { @@ -519,9 +494,9 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent OWE_DoSpawnDespawnAnim(objectEvent, FALSE); } -u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) +u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex) { - SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level); + SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level, roamerIndex); u16 graphicsId = *speciesId + OBJ_EVENT_MON; if (*isFemale) @@ -538,7 +513,7 @@ void OverworldWildEncounter_SetMinimumSpawnTimer(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level) +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex) { const struct WildPokemonInfo *wildMonInfo; enum WildPokemonArea wildArea; @@ -559,12 +534,19 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; } - if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE)) + if (TryStartRoamerEncounter()) { + *roamerIndex = gEncounteredRoamerIndex; + } + else if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE)) + { + ZeroEnemyPartyMons(); *speciesId = SPECIES_NONE; return; } + // gEnemyParty[1] will contain a generated wild mon if a roaming encounter was generated. + // If not it will be contained in gEnemyParty[0]. *speciesId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); *level = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY); @@ -785,6 +767,7 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const bool32 isShiny = FALSE; bool32 isFemale = FALSE; u32 level; + u32 roamerIndex = ROAMER_COUNT; SetOverworldEncounterSpeciesInfo( template->x - MAP_OFFSET, @@ -792,7 +775,8 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const &speciesId, &isShiny, &isFemale, - &level + &level, + &roamerIndex ); // Have a fallback incase of no header mons From 8ceb21e57b0fd8050886105d2d2007108a802554 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 27 Dec 2025 21:14:56 +0000 Subject: [PATCH 243/572] Only Allow One of Each Roamer to Exist on Map --- src/overworld_encounters.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 832407af1d3d..1fbabf54fca9 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -45,6 +45,7 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId); static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); +static bool32 OWE_DoesRoamerExistOnMap(void); static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = { @@ -489,6 +490,8 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent { if (!IsOverworldWildEncounter(objectEvent)) return; + + objectEvent->sRoamerStatus = 0; if (gMain.callback2 != CB2_InitBattle) OWE_DoSpawnDespawnAnim(objectEvent, FALSE); @@ -534,7 +537,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; } - if (TryStartRoamerEncounter()) + if (TryStartRoamerEncounter() && !OWE_DoesRoamerExistOnMap()) { *roamerIndex = gEncounteredRoamerIndex; } @@ -1051,6 +1054,18 @@ static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) #undef MAP_METATILE_VIEW_X #undef MAP_METATILE_VIEW_Y +static bool32 OWE_DoesRoamerExistOnMap(void) +{ + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + struct ObjectEvent *object = &gObjectEvents[i]; + if (IsOverworldWildEncounter(object) && object->sRoamerStatus == gEncounteredRoamerIndex) + return TRUE; + } + + return FALSE; +} + #undef tLocalId #undef tTaskStarted #undef sSpriteTaskState From 7947c5d9ed7908c32902d91b7e770d4cc7bc80a7 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 00:27:02 +0000 Subject: [PATCH 244/572] Finish Roamers --- include/overworld_encounters.h | 1 + src/event_object_movement.c | 1 + src/overworld_encounters.c | 33 +++++++++++++++++++++++++++------ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index b4cf2e4e759d..10f99780bec7 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -93,5 +93,6 @@ u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); void Task_OWE_WaitMovements(u8 taskId); enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); +void OverworldWildEncounter_InitRoamerStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 141ca5ce0f03..2908acb30afc 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1474,6 +1474,7 @@ static u8 InitObjectEventStateFromTemplate(const struct ObjectEventTemplate *tem objectEvent->mapNum = mapNum; objectEvent->trainerRange_berryTreeId = template->trainerRange_berryTreeId; objectEvent->previousMovementDirection = gInitialMovementTypeFacingDirections[template->movementType]; + OverworldWildEncounter_InitRoamerStatus(objectEvent, template); SetObjectEventDirection(objectEvent, objectEvent->previousMovementDirection); if (sMovementTypeHasRange[objectEvent->movementType]) { diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 1fbabf54fca9..6f4f832aa703 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -381,6 +381,7 @@ void CreateOverworldWildEncounter(void) { u32 localId = gSpecialVar_LastTalked; u32 objEventId = GetObjectEventIdByLocalId(localId); + u32 roamerIndex; struct ObjectEvent *object = &gObjectEvents[objEventId]; if (objEventId >= OBJECT_EVENTS_COUNT) @@ -389,10 +390,11 @@ void CreateOverworldWildEncounter(void) if (!IsOverworldWildEncounter(object)) return; - if (object->sRoamerStatus < ROAMER_COUNT) + roamerIndex = object->sRoamerStatus; + if (roamerIndex < ROAMER_COUNT && IsRoamerAt(roamerIndex, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) { - CreateRoamerMonInstance(object->sRoamerStatus); - gEncounteredRoamerIndex = object->sRoamerStatus; + CreateRoamerMonInstance(roamerIndex); + gEncounteredRoamerIndex = roamerIndex; BattleSetup_StartRoamerBattle(); return; } @@ -537,7 +539,11 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; } - if (TryStartRoamerEncounter() && !OWE_DoesRoamerExistOnMap()) + if (*roamerIndex < ROAMER_COUNT && IsRoamerAt(*roamerIndex, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) + { + CreateRoamerMonInstance(*roamerIndex); + } + else if (TryStartRoamerEncounter() && !OWE_DoesRoamerExistOnMap()) { *roamerIndex = gEncounteredRoamerIndex; } @@ -548,7 +554,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 return; } - // gEnemyParty[1] will contain a generated wild mon if a roaming encounter was generated. + // gEnemyParty[1] will contain a generated wild mon if a roaming encounter was generated or specified manually. // If not it will be contained in gEnemyParty[0]. *speciesId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); *level = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); @@ -771,6 +777,10 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const bool32 isFemale = FALSE; u32 level; u32 roamerIndex = ROAMER_COUNT; + u32 storedRoamer = (template->trainerType >> 8) & 0xFF; + + if (storedRoamer) + roamerIndex = storedRoamer; SetOverworldEncounterSpeciesInfo( template->x - MAP_OFFSET, @@ -791,7 +801,7 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const templateOWE.graphicsId = graphicsId; templateOWE.sOverworldEncounterLevel = level; - templateOWE.trainerType = TRAINER_TYPE_ENCOUNTER; + templateOWE.trainerType = (TRAINER_TYPE_ENCOUNTER & 0xFF) | ((roamerIndex & 0xFF) << 8); return templateOWE; } @@ -1066,6 +1076,17 @@ static bool32 OWE_DoesRoamerExistOnMap(void) return FALSE; } +void OverworldWildEncounter_InitRoamerStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template) +{ + // Should only occur for Manual or Semi-Manual Overworld Encounters. + // Trainer type can be be set on manual encounters to specify a roamer index, + // but probably shouldn't unless it can be consistently guarenteed. + if (!IsOverworldWildEncounter(objectEvent)) + return; + + objectEvent->sRoamerStatus = (template->trainerType >> 8) & 0xFF; +} + #undef tLocalId #undef tTaskStarted #undef sSpriteTaskState From 7e93acd1d3a7273d2ee2c89cb96ab0526eb08dc8 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 00:36:20 +0000 Subject: [PATCH 245/572] Merge Fixes --- src/event_object_movement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 7c7e0a4485af..3587e476f6fc 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -12165,7 +12165,7 @@ bool8 MovementType_Despawn_OverworldWildEncounter_Step11(struct ObjectEvent *obj { if (sDespawnTimer == OWE_DESPAWN_FRAMES) { - u32 animType = OWE_GetDespawnAnimType(objectEvent->currentMetatileBehavior); + u32 animType = OWE_GetSpawnDespawnAnimType(objectEvent->currentMetatileBehavior); MovementAction_OverworldEncounterSpawn(animType, objectEvent); RemoveObjectEvent(objectEvent); return FALSE; From cc204e0ba5b6f747013d453206c55bb27fadb38d Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:59:52 -0600 Subject: [PATCH 246/572] Wandering mons now walk into player for encounter --- src/event_object_movement.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 3587e476f6fc..82aeba2efaf3 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11754,6 +11754,41 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent { if (!ObjectEventExecSingleMovementAction(objectEvent, sprite)) return FALSE; + + if (OWE_IsWaitTaskActive()) + { + u16 speciesId = OW_SPECIES(objectEvent); + u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); + bool8 collision; + u8 movementActionId; + + SetObjectEventDirection(objectEvent, direction); + collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + + if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) + { + s16 x = objectEvent->currentCoords.x; + s16 y = objectEvent->currentCoords.y; + MoveCoords(objectEvent->movementDirection, &x, &y); + // If colliding with the player object, don't try to walk around it. + if (GetObjectObjectCollidesWith(objectEvent, x, y, FALSE) == gPlayerAvatar.objectEventId) + { + movementActionId = GetFaceDirectionMovementAction(objectEvent->facingDirection); + } + else + { + direction = OWE_DirectionToPlayerFromCollision(objectEvent); + SetObjectEventDirection(objectEvent, direction); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + } + } + ObjectEventSetSingleMovement(objectEvent, sprite, movementActionId); + objectEvent->singleMovementActive = TRUE; + sprite->sTypeFuncId = 6; + return FALSE; + } + SetMovementDelay(sprite, sMovementDelaysOWE[Random() % ARRAY_COUNT(sMovementDelaysOWE)]); sprite->sTypeFuncId = 3; return TRUE; From 976952ba7a22ef17db82a821bea163aace36016c Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 27 Dec 2025 22:03:15 -0600 Subject: [PATCH 247/572] Added support for Feebas spots --- include/wild_encounter.h | 2 ++ src/overworld_encounters.c | 7 +++++++ src/wild_encounter.c | 10 +++++----- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 29656a1068b6..a1bacd492f25 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -46,6 +46,7 @@ struct WildPokemonHeader extern const struct WildPokemonHeader gWildMonHeaders[]; +extern const struct WildPokemon sWildFeebas; extern bool8 gIsFishingEncounter; extern bool8 gIsSurfingEncounter; extern u8 gChainFishingDexNavStreak; @@ -67,6 +68,7 @@ u32 CalculateChainFishingShinyRolls(void); void CreateWildMon(u16 species, u8 level); bool32 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags); u16 GetCurrentMapWildMonHeaderId(void); +bool8 CheckFeebasAtCoords(s16 x, s16 y); u32 ChooseWildMonIndex_Land(void); u32 ChooseWildMonIndex_Water(void); u32 ChooseWildMonIndex_Rocks(void); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 80d5787c7f9b..3e36dd673575 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -551,6 +551,13 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 { *roamerIndex = gEncounteredRoamerIndex; } + else if (wildArea == WILD_AREA_WATER && CheckFeebasAtCoords(x, y)) + { + *level = ChooseWildMonLevel(&sWildFeebas, 0, WILD_AREA_FISHING); + + *speciesId = sWildFeebas.species; + CreateWildMon(*speciesId, *level); + } else if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE)) { ZeroEnemyPartyMons(); diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 9a66a3d56163..73cf950fbbae 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -65,7 +65,7 @@ EWRAM_DATA u8 gChainFishingDexNavStreak = 0; #include "data/wild_encounters.h" -static const struct WildPokemon sWildFeebas = {20, 25, SPECIES_FEEBAS}; +const struct WildPokemon sWildFeebas = {20, 25, SPECIES_FEEBAS}; static const u16 sRoute119WaterTileData[] = { @@ -111,18 +111,16 @@ static u16 GetFeebasFishingSpotId(s16 targetX, s16 targetY, u8 section) return spotId + 1; } -static bool8 CheckFeebas(void) +bool8 CheckFeebasAtCoords(s16 x, s16 y) { u8 i; u16 feebasSpots[NUM_FEEBAS_SPOTS]; - s16 x, y; u8 route119Section = 0; u16 spotId; if (gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(MAP_ROUTE119) && gSaveBlock1Ptr->location.mapNum == MAP_NUM(MAP_ROUTE119)) { - GetXYCoordsOneStepInFrontOfPlayer(&x, &y); x -= MAP_OFFSET; y -= MAP_OFFSET; @@ -966,10 +964,12 @@ void FishingWildEncounter(u8 rod) { u16 species; u32 headerId; + s16 x, y; enum TimeOfDay timeOfDay; gIsFishingEncounter = TRUE; - if (CheckFeebas() == TRUE) + GetXYCoordsOneStepInFrontOfPlayer(&x, &y); + if (CheckFeebasAtCoords(x, y) == TRUE) { u8 level = ChooseWildMonLevel(&sWildFeebas, 0, WILD_AREA_FISHING); From 43cc041ea346af5be2b68f6671738e5365b3208b Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 27 Dec 2025 22:08:52 -0600 Subject: [PATCH 248/572] Added config for Feebas spots --- include/config/overworld.h | 1 + src/overworld_encounters.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index c1ef34edd004..c95fb8bdd1bb 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -81,6 +81,7 @@ #define OW_ENCOUNTER_MOVEMENT_RANGE_Y 8 // The vertical movement range for OW encounter Pokémon. #define OW_VANILLA_AMBIENT_CRIES TRUE // If TRUE, the ambient wild Pokémon cries will play on routes with encounter tables. #define OW_WILD_ENCOUNTERS_AMBIENT_CRIES TRUE // If TRUE, generated overworld encounters will play ambient cries based on their location relative to the player. +#define OW_WILD_ENCOUNTERS_FEEBAS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. // Out-of-battle Ability effects #define OW_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8+, if a Pokémon with Synchronize leads the party, wild Pokémon will always have their same Nature as opposed to the 50% chance in previous games. Gift Pokémon excluded. diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 3e36dd673575..88588a9719d3 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -551,7 +551,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 { *roamerIndex = gEncounteredRoamerIndex; } - else if (wildArea == WILD_AREA_WATER && CheckFeebasAtCoords(x, y)) + else if (OW_WILD_ENCOUNTERS_FEEBAS && wildArea == WILD_AREA_WATER && CheckFeebasAtCoords(x, y)) { *level = ChooseWildMonLevel(&sWildFeebas, 0, WILD_AREA_FISHING); From 7b36b5ec6e25ba1691f456a06b118cd30e5f5b5e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 11:29:12 +0000 Subject: [PATCH 249/572] Remove Despawn Anim Handled by Remove Object --- src/event_object_movement.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 82aeba2efaf3..bf901cc6945d 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -12200,8 +12200,6 @@ bool8 MovementType_Despawn_OverworldWildEncounter_Step11(struct ObjectEvent *obj { if (sDespawnTimer == OWE_DESPAWN_FRAMES) { - u32 animType = OWE_GetSpawnDespawnAnimType(objectEvent->currentMetatileBehavior); - MovementAction_OverworldEncounterSpawn(animType, objectEvent); RemoveObjectEvent(objectEvent); return FALSE; } From fad9974453768e09a3a6594023395af73033dada Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 11:33:16 +0000 Subject: [PATCH 250/572] Better Check --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 88588a9719d3..4fed06ec28d1 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -499,7 +499,7 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent objectEvent->sRoamerStatus = 0; - if (gMain.callback2 != CB2_InitBattle) + if (gMain.callback2 == CB2_Overworld) OWE_DoSpawnDespawnAnim(objectEvent, FALSE); } From c5b98b4bf19f56009e70fadb63dacf8b0b4a5e1d Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 12:44:48 +0000 Subject: [PATCH 251/572] Readd Map Restriction Behind a Config --- include/config/overworld.h | 3 +- include/overworld.h | 2 ++ src/event_object_movement.c | 20 +++++------ src/overworld.c | 17 ++++++++++ src/overworld_encounters.c | 68 ++++++++++++++++++++++++------------- 5 files changed, 75 insertions(+), 35 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index c95fb8bdd1bb..6803bd62c55b 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -74,7 +74,8 @@ // Overworld Encounters #define OW_WILD_ENCOUNTERS_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. #define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. -#define OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same metatile behavior as the one it spawned on. +#define OW_WILD_ENCOUNTERS_RESTRICT_METATILE TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same metatile behavior as the one it spawned on. +#define OW_WILD_ENCOUNTERS_RESTRICT_MAP TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within their current map bounds. #define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. #define OW_WILD_ENCOUNTERS_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. #define OW_ENCOUNTER_MOVEMENT_RANGE_X 8 // The horizontal movement range for OW encounter Pokémon. diff --git a/include/overworld.h b/include/overworld.h index e0df9c3b899e..b511b3236bce 100644 --- a/include/overworld.h +++ b/include/overworld.h @@ -138,6 +138,8 @@ bool8 IsMapTypeIndoors(enum MapType mapType); mapsec_u8_t GetSavedWarpRegionMapSectionId(void); mapsec_u8_t GetCurrentRegionMapSectionId(void); enum MapBattleScene GetCurrentMapBattleScene(void); +bool32 AreCoordsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); +bool32 AreCoordsInsidePlayerMap(s16 x, s16 y); void CleanupOverworldWindowsAndTilemaps(void); bool32 IsOverworldLinkActive(void); void CB1_Overworld(void); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index bf901cc6945d..311ecd87ae68 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11766,7 +11766,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) { s16 x = objectEvent->currentCoords.x; s16 y = objectEvent->currentCoords.y; @@ -11826,7 +11826,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent SetObjectEventDirection(objectEvent, chosenDirection); sprite->sTypeFuncId = 5; - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, chosenDirection)) + if (OWE_CheckRestrictedMovement(objectEvent, chosenDirection) || GetCollisionInDirection(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; @@ -11898,7 +11898,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); sprite->sTypeFuncId = 12; - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) { s16 x = objectEvent->currentCoords.x; s16 y = objectEvent->currentCoords.y; @@ -11914,7 +11914,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); collision = GetCollisionInDirection(objectEvent, newDirection); - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, newDirection)) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); } @@ -11984,7 +11984,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); @@ -11996,7 +11996,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); collision = GetCollisionInDirection(objectEvent, newDirection); - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, newDirection)) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) { sCollisionTimer++; movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); @@ -12104,7 +12104,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); @@ -12116,7 +12116,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); collision = GetCollisionInDirection(objectEvent, newDirection); - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, newDirection)) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) { movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); } @@ -12141,7 +12141,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) { s16 x = objectEvent->currentCoords.x; s16 y = objectEvent->currentCoords.y; @@ -12157,7 +12157,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); collision = GetCollisionInDirection(objectEvent, newDirection); - if ((OW_WILD_ENCOUNTERS_RESTRICTED_MOVEMENT && OWE_CheckRestrictedMovement(objectEvent, newDirection)) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); } diff --git a/src/overworld.c b/src/overworld.c index 417530b0ad42..ebbb5dc16779 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1488,6 +1488,23 @@ enum MapBattleScene GetCurrentMapBattleScene(void) return Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->battleType; } +bool32 AreCoordsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) +{ + const struct MapLayout *layout = Overworld_GetMapHeaderByGroupAndId(mapGroup, mapNum)->mapLayout; + s32 width = layout->width - MAP_OFFSET; + s32 height = layout->height - MAP_OFFSET; + + if (x >= 0 && x < width && y >= 0 && y < height) + return TRUE; + + return FALSE; +} + +bool32 AreCoordsInsidePlayerMap(s16 x, s16 y) +{ + return AreCoordsInsideMap(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, x, y); +} + static void InitOverworldBgs(void) { InitBgsFromTemplates(0, sOverworldBgTemplates, ARRAY_COUNT(sOverworldBgTemplates)); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 4fed06ec28d1..61254c7177b9 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -47,6 +47,8 @@ static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId); static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_DoesRoamerExistOnMap(void); +static bool32 OWE_CheckRestrictedMovementMetatile(struct ObjectEvent *objectEvent, u32 direction); +static bool32 OWE_CheckRestrictedMovementMap(struct ObjectEvent *objectEvent, u32 direction); static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = { @@ -307,7 +309,6 @@ static bool8 TrySelectTile(s16* outX, s16* outY) s16 x, y; u8 closeDistance; bool32 isEncounterTile = FALSE; - const struct MapLayout *layout; // Spawn further away when surfing if (OWE_ShouldSpawnWaterMons()) @@ -352,9 +353,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) elevation = MapGridGetElevationAt(x, y); - layout = Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->mapLayout; - if ((x - MAP_OFFSET) < 0 || (x - MAP_OFFSET) >= layout->width || - (y - MAP_OFFSET) < 0 || (y - MAP_OFFSET) >= layout->height) + if (!AreCoordsInsidePlayerMap(x, y)) return FALSE; // 0 is change of elevation, 15 is multiple elevation e.g. bridges @@ -852,26 +851,8 @@ void TryRemoveOverworldWildEncounter(u32 localId) bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction) { - s16 xCurrent = objectEvent->currentCoords.x; - s16 yCurrent = objectEvent->currentCoords.y; - s16 xNew = xCurrent + gDirectionToVectors[direction].x; - s16 yNew = yCurrent + gDirectionToVectors[direction].y; - u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); - u32 metatileBehaviourNew = MapGridGetMetatileBehaviorAt(xNew, yNew); - - if (MetatileBehavior_IsLandWildEncounter(metatileBehaviourCurrent) - && MetatileBehavior_IsLandWildEncounter(metatileBehaviourNew)) - return FALSE; - - if (MetatileBehavior_IsWaterWildEncounter(metatileBehaviourCurrent) - && MetatileBehavior_IsWaterWildEncounter(metatileBehaviourNew)) - return FALSE; - - if (MetatileBehavior_IsIndoorEncounter(metatileBehaviourCurrent) - && MetatileBehavior_IsIndoorEncounter(metatileBehaviourNew)) - return FALSE; - - return TRUE; + return ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictedMovementMetatile(objectEvent, direction)) + || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictedMovementMap(objectEvent, direction))); } void DespawnOldestOWE_Pal(void) @@ -1159,6 +1140,45 @@ void OverworldWildEncounter_InitRoamerStatus(struct ObjectEvent *objectEvent, co objectEvent->sRoamerStatus = (template->trainerType >> 8) & 0xFF; } +static bool32 OWE_CheckRestrictedMovementMetatile(struct ObjectEvent *objectEvent, u32 direction) +{ + s16 xCurrent = objectEvent->currentCoords.x; + s16 yCurrent = objectEvent->currentCoords.y; + s16 xNew = xCurrent + gDirectionToVectors[direction].x; + s16 yNew = yCurrent + gDirectionToVectors[direction].y; + u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); + u32 metatileBehaviourNew = MapGridGetMetatileBehaviorAt(xNew, yNew); + + if (MetatileBehavior_IsLandWildEncounter(metatileBehaviourCurrent) + && MetatileBehavior_IsLandWildEncounter(metatileBehaviourNew)) + return FALSE; + + if (MetatileBehavior_IsWaterWildEncounter(metatileBehaviourCurrent) + && MetatileBehavior_IsWaterWildEncounter(metatileBehaviourNew)) + return FALSE; + + if (MetatileBehavior_IsIndoorEncounter(metatileBehaviourCurrent) + && MetatileBehavior_IsIndoorEncounter(metatileBehaviourNew)) + return FALSE; + + return TRUE; +} + +static bool32 OWE_CheckRestrictedMovementMap(struct ObjectEvent *objectEvent, u32 direction) +{ + s16 xCurrent = objectEvent->currentCoords.x; + s16 yCurrent = objectEvent->currentCoords.y; + s16 xNew = xCurrent + gDirectionToVectors[direction].x; + s16 yNew = yCurrent + gDirectionToVectors[direction].y; + u32 mapGroup = objectEvent->mapGroup; + u32 mapNum = objectEvent->mapNum; + + if (mapGroup == gSaveBlock1Ptr->location.mapGroup && mapNum == gSaveBlock1Ptr->location.mapNum) + return AreCoordsInsidePlayerMap(xNew, yNew); + else + return !AreCoordsInsidePlayerMap(xNew, yNew); +} + #undef tLocalId #undef NOT_STARTED #undef STARTED From c4e5b38952739dc6b9fef99ae717c4e3341cac45 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 13:54:59 +0000 Subject: [PATCH 252/572] Rename sWildFeebas --- include/wild_encounter.h | 2 +- src/overworld_encounters.c | 4 ++-- src/wild_encounter.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/wild_encounter.h b/include/wild_encounter.h index a1bacd492f25..f03b6e141d7e 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -46,7 +46,7 @@ struct WildPokemonHeader extern const struct WildPokemonHeader gWildMonHeaders[]; -extern const struct WildPokemon sWildFeebas; +extern const struct WildPokemon gWildFeebas; extern bool8 gIsFishingEncounter; extern bool8 gIsSurfingEncounter; extern u8 gChainFishingDexNavStreak; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 61254c7177b9..d5e2c204767f 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -552,9 +552,9 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 } else if (OW_WILD_ENCOUNTERS_FEEBAS && wildArea == WILD_AREA_WATER && CheckFeebasAtCoords(x, y)) { - *level = ChooseWildMonLevel(&sWildFeebas, 0, WILD_AREA_FISHING); + *level = ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING); - *speciesId = sWildFeebas.species; + *speciesId = gWildFeebas.species; CreateWildMon(*speciesId, *level); } else if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE)) diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 73cf950fbbae..4aa06ae18d5a 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -65,7 +65,7 @@ EWRAM_DATA u8 gChainFishingDexNavStreak = 0; #include "data/wild_encounters.h" -const struct WildPokemon sWildFeebas = {20, 25, SPECIES_FEEBAS}; +const struct WildPokemon gWildFeebas = {20, 25, SPECIES_FEEBAS}; static const u16 sRoute119WaterTileData[] = { @@ -971,9 +971,9 @@ void FishingWildEncounter(u8 rod) GetXYCoordsOneStepInFrontOfPlayer(&x, &y); if (CheckFeebasAtCoords(x, y) == TRUE) { - u8 level = ChooseWildMonLevel(&sWildFeebas, 0, WILD_AREA_FISHING); + u8 level = ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING); - species = sWildFeebas.species; + species = gWildFeebas.species; CreateWildMon(species, level); } else From fa8ffabe0705857fa53e9844a25d9ed90c45f004 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 14:08:35 +0000 Subject: [PATCH 253/572] Finish OWE_GetRandomActiveEncounterObject --- include/overworld_encounters.h | 1 - src/overworld_encounters.c | 49 +++++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 12726e515ca2..aaaa95e202ee 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -110,7 +110,6 @@ void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); void OverworldWildEncounter_SetMinimumSpawnTimer(void); -u8 CountActiveOverworldEncounters(void); void RemoveAllOverworldEncounterObjects(void); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d5e2c204767f..d045b9b8ce76 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -49,6 +49,8 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_DoesRoamerExistOnMap(void); static bool32 OWE_CheckRestrictedMovementMetatile(struct ObjectEvent *objectEvent, u32 direction); static bool32 OWE_CheckRestrictedMovementMap(struct ObjectEvent *objectEvent, u32 direction); +static u32 GetNumActiveOverworldEncounters(void); +static u32 GetNumActiveGeneratedOverworldEncounters(void); static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = { @@ -63,7 +65,8 @@ static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = void OWE_ResetSpawnCounterPlayAmbientCry(void) { OverworldWildEncounter_SetMinimumSpawnTimer(); - if (OW_WILD_ENCOUNTERS_AMBIENT_CRIES) + // Currently may not play manual or semi-manual encounter cries if no wild mon header exists + if (OW_WILD_ENCOUNTERS_AMBIENT_CRIES && GetNumActiveOverworldEncounters()) OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); } @@ -267,7 +270,7 @@ static u8 NextSpawnMonSlot(void) u32 maxSpawns = GetMaxOverworldEncounterSpawns(); // All mon slots are in use - if (CountActiveOverworldEncounters() >= maxSpawns) + if (GetNumActiveGeneratedOverworldEncounters() >= maxSpawns) { if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) { @@ -437,7 +440,7 @@ static void SortOWEMonAges(void) struct ObjectEvent *slotMon; struct AgeSort array[OWE_MAX_SPAWN_SLOTS]; struct AgeSort current; - u32 numActive = CountActiveOverworldEncounters(); + u32 numActive = GetNumActiveGeneratedOverworldEncounters(); u32 count = 0; s32 i, j; @@ -591,15 +594,26 @@ static bool8 IsSafeToSpawnObjectEvents(void) return (player->currentCoords.x == player->previousCoords.x && player->currentCoords.y == player->previousCoords.y); } -u8 CountActiveOverworldEncounters(void) +static u32 GetNumActiveOverworldEncounters(void) +{ + u32 numActive = 0; + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + if (IsOverworldWildEncounter(&gObjectEvents[i])) + numActive++; + } + return numActive; +} + +static u32 GetNumActiveGeneratedOverworldEncounters(void) { u32 count = 0; + for (u32 spawnSlot = 0; spawnSlot < OWE_MAX_SPAWN_SLOTS; spawnSlot++) { if (GetOverworldSpeciesBySpawnSlot(spawnSlot) != SPECIES_NONE) count++; } - return count; } @@ -716,7 +730,7 @@ u32 GetNewestOWEncounterLocalId(void) bool32 CanRemoveOverworldEncounter(u32 localId) { // Include a check for the encounter not being shiny or a roamer. - return (OW_WILD_ENCOUNTERS_OVERWORLD && CountActiveOverworldEncounters() != 0 + return (OW_WILD_ENCOUNTERS_OVERWORLD && GetNumActiveGeneratedOverworldEncounters() != 0 && (localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS + 1) || localId > LOCALID_OW_ENCOUNTER_END)); } @@ -860,7 +874,7 @@ void DespawnOldestOWE_Pal(void) // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles if (OW_WILD_ENCOUNTERS_OVERWORLD && CountFreePaletteSlots() < 2) { - u32 count = CountActiveOverworldEncounters(); + u32 count = GetNumActiveGeneratedOverworldEncounters(); if (count > 0) { @@ -1069,7 +1083,22 @@ enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavio static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) { - return &gObjectEvents[gPlayerAvatar.objectEventId]; + u32 numActive = GetNumActiveOverworldEncounters(); + u32 randomIndex; + struct ObjectEvent *slotMon; + + if (numActive) + randomIndex = Random() % numActive; + else + return NULL; + + for (u32 i = 0; i < numActive; i++) + { + slotMon = &gObjectEvents[i]; + if (IsOverworldWildEncounter(slotMon) && (i == randomIndex)) + return slotMon; + } + return NULL; } @@ -1096,8 +1125,8 @@ static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) // TESTING: Setting this species can be used as a test to play a consistent sound to check how often the // code in UpdateOverworldEncounters runs, as OWE_GetRandomActiveEncounterObject cuurently returns // the player object. - if (objectEvent->isPlayer) - speciesId = SPECIES_NONE; + if (objectEvent == NULL) + return; if (distanceX > MAP_METATILE_VIEW_X) distanceX = MAP_METATILE_VIEW_X; From d4cf958f4d569e3534efdb7966a4023e8c538207 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 15:22:25 +0000 Subject: [PATCH 254/572] Consolidate Behaviour Data and Create Getter Functions --- .../overworld_encounter_species_behavior.h | 48 -------------- include/overworld_encounters.h | 25 +++----- include/pokemon.h | 8 ++- .../pokemon/overworld_encounter_behaviors.h | 63 +++++++++++++++++++ src/event_object_movement.c | 23 ++++--- src/overworld_encounters.c | 25 +------- src/pokemon.c | 44 +++++++++++++ 7 files changed, 135 insertions(+), 101 deletions(-) delete mode 100644 include/overworld_encounter_species_behavior.h create mode 100644 src/data/pokemon/overworld_encounter_behaviors.h diff --git a/include/overworld_encounter_species_behavior.h b/include/overworld_encounter_species_behavior.h deleted file mode 100644 index 974c516420ef..000000000000 --- a/include/overworld_encounter_species_behavior.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H -#define GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H - -static const struct MonSpeciesOWEData sOWESpeciesBehaviors[OWE_SPECIES_BEHAVIOR_COUNT] = { - [OWE_IGNORE_PLAYER] = { - .behavior = OWE_BEHAVIOR_WANDER_AROUND, - }, - [OWE_CHASE_PLAYER_SLOW] = { - .behavior = OWE_BEHAVIOR_CHASE_PLAYER, - .idleSpeed = OWE_SPEED_SLOW, - .activeSpeed = OWE_SPEED_SLOW, - .viewDistance = 4, - .viewWidth = 3, - .activeDistance = 5 - }, - [OWE_FLEE_PLAYER_NORMAL] = { - .behavior = OWE_BEHAVIOR_FLEE_PLAYER, - .idleSpeed = OWE_SPEED_NORMAL, - .activeSpeed = OWE_SPEED_NORMAL, - .viewDistance = 4, - .viewWidth = 3, - .activeDistance = 5 - }, - [OWE_WATCH_PLAYER_NORMAL] = { - .behavior = OWE_BEHAVIOR_WATCH_PLAYER, - .idleSpeed = OWE_SPEED_NORMAL, - .viewDistance = 4, - .viewWidth = 3, - .activeDistance = 5 - }, - [OWE_APPROACH_PLAYER_SLOW] = { - .behavior = OWE_BEHAVIOR_APPROACH_PLAYER, - .idleSpeed = OWE_SPEED_NORMAL, - .activeSpeed = OWE_SPEED_SLOW, - .viewDistance = 4, - .viewWidth = 3, - .activeDistance = 5 - }, - [OWE_DESPAWN_ON_NOTICE] = { - .behavior = OWE_BEHAVIOR_DESPAWN, - .idleSpeed = OWE_SPEED_NORMAL, - .viewDistance = 4, - .viewWidth = 3, - .activeDistance = 5 - } -}; - -#endif // GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index aaaa95e202ee..7401415afb58 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -58,17 +58,6 @@ enum OverworldObjectEncounterType }; */ -enum OverworldEncounterBehaviors -{ - OWE_BEHAVIOR_WANDER_AROUND, - OWE_BEHAVIOR_CHASE_PLAYER, - OWE_BEHAVIOR_FLEE_PLAYER, - OWE_BEHAVIOR_WATCH_PLAYER, - OWE_BEHAVIOR_APPROACH_PLAYER, - OWE_BEHAVIOR_DESPAWN, - OWE_BEHAVIOR_COUNT -}; - // OWE_SPEED_FASTER seems to visually bug out sometimes. enum OWESpeeds { @@ -78,18 +67,18 @@ enum OWESpeeds OWE_SPEED_FASTER }; -struct MonSpeciesOWEData +struct OWESpeciesBehavior { - enum OverworldEncounterBehaviors behavior; + u32 movementType:8; + u32 viewDistance:4; + u32 viewWidth:4; + u32 activeDistance:4; + u32 padding:12; enum OWESpeeds idleSpeed; enum OWESpeeds activeSpeed; - u16 viewDistance:4; - u16 viewWidth:4; - u16 activeDistance:4; - u16 padding:4; }; -enum OWESpeciesBehaviors +enum OverworldEncounterBehaviors { OWE_IGNORE_PLAYER, OWE_CHASE_PLAYER_SLOW, diff --git a/include/pokemon.h b/include/pokemon.h index 8d134c8deeed..ec72ffe1f6e5 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -537,7 +537,7 @@ struct SpeciesInfo /*0xC4*/ #endif //P_GENDER_DIFFERENCES #endif //OW_PKMN_OBJECTS_SHARE_PALETTES #endif //OW_POKEMON_OBJECT_EVENTS - enum OWESpeciesBehaviors overworldEncounterBehavior; + enum OverworldEncounterBehaviors overworldEncounterBehavior; }; struct AbilityInfo @@ -922,5 +922,11 @@ struct Pokemon *GetSavedPlayerPartyMon(u32 index); u8 *GetSavedPlayerPartyCount(void); void SavePlayerPartyMon(u32 index, struct Pokemon *mon); bool32 IsSpeciesOfType(u32 species, enum Type type); +u32 OWE_GetMovementTypeFromSpecies(u32 speciesId); +u32 OWE_GetViewDistanceFromSpecies(u32 speciesId); +u32 OWE_GetViewWidthFromSpecies(u32 speciesId); +u32 OWE_GetViewActiveDistanceFromSpecies(u32 speciesId); +enum OWESpeeds OWE_GetIdleSpeedFromSpecies(u32 speciesId); +enum OWESpeeds OWE_GetActiveSpeedFromSpecies(u32 speciesId); #endif // GUARD_POKEMON_H diff --git a/src/data/pokemon/overworld_encounter_behaviors.h b/src/data/pokemon/overworld_encounter_behaviors.h new file mode 100644 index 000000000000..a271b45c6026 --- /dev/null +++ b/src/data/pokemon/overworld_encounter_behaviors.h @@ -0,0 +1,63 @@ +#ifndef GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H +#define GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H + +#include "overworld_encounters.h" +#include "constants/event_object_movement.h" + +static const struct OWESpeciesBehavior sOWESpeciesBehavior[OWE_SPECIES_BEHAVIOR_COUNT] = +{ + [OWE_IGNORE_PLAYER] = + { + .movementType = MOVEMENT_TYPE_WANDER_AROUND_OWE, + }, + + [OWE_CHASE_PLAYER_SLOW] = + { + .movementType = MOVEMENT_TYPE_CHASE_PLAYER_OWE, + .viewDistance = 4, + .viewWidth = 3, + .activeDistance = 5, + .idleSpeed = OWE_SPEED_SLOW, + .activeSpeed = OWE_SPEED_SLOW, + }, + + [OWE_FLEE_PLAYER_NORMAL] = + { + .movementType = MOVEMENT_TYPE_FLEE_PLAYER_OWE, + .viewDistance = 4, + .viewWidth = 3, + .activeDistance = 5, + .idleSpeed = OWE_SPEED_NORMAL, + .activeSpeed = OWE_SPEED_NORMAL, + }, + + [OWE_WATCH_PLAYER_NORMAL] = + { + .movementType = MOVEMENT_TYPE_WATCH_PLAYER_OWE, + .viewDistance = 4, + .viewWidth = 3, + .activeDistance = 5, + .idleSpeed = OWE_SPEED_NORMAL, + }, + + [OWE_APPROACH_PLAYER_SLOW] = + { + .movementType = MOVEMENT_TYPE_APPROACH_PLAYER_OWE, + .viewDistance = 4, + .viewWidth = 3, + .activeDistance = 5, + .idleSpeed = OWE_SPEED_NORMAL, + .activeSpeed = OWE_SPEED_SLOW, + }, + + [OWE_DESPAWN_ON_NOTICE] = + { + .movementType = MOVEMENT_TYPE_DESPAWN_OWE, + .viewDistance = 4, + .viewWidth = 3, + .activeDistance = 5, + .idleSpeed = OWE_SPEED_NORMAL, + } +}; + +#endif // GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 311ecd87ae68..7c8e9a27a0c1 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -26,7 +26,6 @@ #include "metatile_behavior.h" #include "overworld.h" #include "overworld_encounters.h" -#include "overworld_encounter_species_behavior.h" #include "palette.h" #include "party_menu.h" #include "pokemon.h" @@ -11764,7 +11763,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent SetObjectEventDirection(objectEvent, direction); collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) { @@ -11780,7 +11779,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent { direction = OWE_DirectionToPlayerFromCollision(objectEvent); SetObjectEventDirection(objectEvent, direction); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); } } ObjectEventSetSingleMovement(objectEvent, sprite, movementActionId); @@ -11835,7 +11834,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent bool8 MovementType_WanderAround_OverworldWildEncounter_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[OW_SPECIES(objectEvent)].overworldEncounterBehavior].idleSpeed)); + ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetIdleSpeedFromSpecies(OW_SPECIES(objectEvent)))); objectEvent->singleMovementActive = TRUE; sprite->sTypeFuncId = 6; return TRUE; @@ -11895,7 +11894,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent u8 movementActionId; collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); sprite->sTypeFuncId = 12; if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) @@ -11911,7 +11910,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent return FALSE; } u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); collision = GetCollisionInDirection(objectEvent, newDirection); if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) @@ -11982,7 +11981,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * u8 movementActionId; collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) { @@ -11993,7 +11992,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * { newDirection = GetOppositeDirection(newDirection); } - movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); collision = GetCollisionInDirection(objectEvent, newDirection); if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) @@ -12103,7 +12102,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve SetObjectEventDirection(objectEvent, GetOppositeDirection(objectEvent->movementDirection)); collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; @@ -12113,7 +12112,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve { newDirection = GetOppositeDirection(newDirection); } - movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); collision = GetCollisionInDirection(objectEvent, newDirection); if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) @@ -12139,7 +12138,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve else { collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) { @@ -12154,7 +12153,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve return FALSE; } u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeSpeed); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); collision = GetCollisionInDirection(objectEvent, newDirection); if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d045b9b8ce76..c8eac749fd5f 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1,6 +1,5 @@ #include "global.h" #include "overworld_encounters.h" -#include "overworld_encounter_species_behavior.h" #include "battle_setup.h" #include "battle_main.h" #include "event_data.h" @@ -43,7 +42,6 @@ static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); static u32 GetSpawnSlotByLocalId(u32 localId); static void SortOWEMonAges(void); static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny); -static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId); static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_DoesRoamerExistOnMap(void); @@ -52,16 +50,6 @@ static bool32 OWE_CheckRestrictedMovementMap(struct ObjectEvent *objectEvent, u3 static u32 GetNumActiveOverworldEncounters(void); static u32 GetNumActiveGeneratedOverworldEncounters(void); -static const u32 sOWE_MovementBehaviorType[OWE_BEHAVIOR_COUNT] = -{ - [OWE_BEHAVIOR_WANDER_AROUND] = MOVEMENT_TYPE_WANDER_AROUND_OWE, - [OWE_BEHAVIOR_CHASE_PLAYER] = MOVEMENT_TYPE_CHASE_PLAYER_OWE, - [OWE_BEHAVIOR_FLEE_PLAYER] = MOVEMENT_TYPE_FLEE_PLAYER_OWE, - [OWE_BEHAVIOR_WATCH_PLAYER] = MOVEMENT_TYPE_WATCH_PLAYER_OWE, - [OWE_BEHAVIOR_APPROACH_PLAYER] = MOVEMENT_TYPE_APPROACH_PLAYER_OWE, - [OWE_BEHAVIOR_DESPAWN] = MOVEMENT_TYPE_DESPAWN_OWE -}; - void OWE_ResetSpawnCounterPlayAmbientCry(void) { OverworldWildEncounter_SetMinimumSpawnTimer(); @@ -900,8 +888,8 @@ bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon) else { u32 speciesId = OW_SPECIES(mon); - u32 viewDistance = sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].viewDistance; - u32 viewWidth = sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].viewWidth; + u32 viewDistance = OWE_GetViewDistanceFromSpecies(speciesId); + u32 viewWidth = OWE_GetViewWidthFromSpecies(speciesId); switch (mon->facingDirection) { @@ -938,7 +926,7 @@ bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon) u32 speciesId = OW_SPECIES(mon); if (speciesId != SPECIES_NONE) - distance = sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].activeDistance; + distance = OWE_GetViewActiveDistanceFromSpecies(speciesId); if (player->currentCoords.y <= mon->currentCoords.y + distance && player->currentCoords.y >= mon->currentCoords.y - distance && player->currentCoords.x <= mon->currentCoords.x + distance && player->currentCoords.x >= mon->currentCoords.x - distance) @@ -1101,13 +1089,6 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) return NULL; } - -static u32 OWE_GetMovementTypeFromSpecies(u32 speciesId) -{ - //return MOVEMENT_TYPE_WANDER_AROUND_OWE; // Replace for Testing - return sOWE_MovementBehaviorType[sOWESpeciesBehaviors[gSpeciesInfo[speciesId].overworldEncounterBehavior].behavior]; -} - // Are these needed? Not defined elsewhere? I don't think so. #define MAP_METATILE_VIEW_X 7 #define MAP_METATILE_VIEW_Y 5 diff --git a/src/pokemon.c b/src/pokemon.c index 40286ec58384..fcc82694198b 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -713,6 +713,7 @@ const struct NatureInfo gNaturesInfo[NUM_NATURES] = #endif #include "data/pokemon/teachable_learnsets.h" +#include "data/pokemon/overworld_encounter_behaviors.h" #include "data/pokemon/egg_moves.h" #include "data/pokemon/form_species_tables.h" #include "data/pokemon/form_change_tables.h" @@ -7475,3 +7476,46 @@ bool32 IsSpeciesOfType(u32 species, enum Type type) return TRUE; return FALSE; } + +u32 OWE_GetMovementTypeFromSpecies(u32 speciesId) +{ + // return MOVEMENT_TYPE_WANDER_AROUND_OWE; // Replace for Testing + speciesId = SanitizeSpeciesId(speciesId); + enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; + return sOWESpeciesBehavior[behavior].movementType; +} + +u32 OWE_GetViewDistanceFromSpecies(u32 speciesId) +{ + speciesId = SanitizeSpeciesId(speciesId); + enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; + return sOWESpeciesBehavior[behavior].viewDistance; +} + +u32 OWE_GetViewWidthFromSpecies(u32 speciesId) +{ + speciesId = SanitizeSpeciesId(speciesId); + enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; + return sOWESpeciesBehavior[behavior].viewWidth; +} + +u32 OWE_GetViewActiveDistanceFromSpecies(u32 speciesId) +{ + speciesId = SanitizeSpeciesId(speciesId); + enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; + return sOWESpeciesBehavior[behavior].activeDistance; +} + +enum OWESpeeds OWE_GetIdleSpeedFromSpecies(u32 speciesId) +{ + speciesId = SanitizeSpeciesId(speciesId); + enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; + return sOWESpeciesBehavior[behavior].idleSpeed; +} + +enum OWESpeeds OWE_GetActiveSpeedFromSpecies(u32 speciesId) +{ + speciesId = SanitizeSpeciesId(speciesId); + enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; + return sOWESpeciesBehavior[behavior].activeSpeed; +} From 8d837ef4bae4086c94beb0b9c8f6f90aa70a3901 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 15:27:54 +0000 Subject: [PATCH 255/572] Add Sootopolis Check --- include/wild_encounter.h | 1 + src/overworld_encounters.c | 7 +++++++ src/wild_encounter.c | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/wild_encounter.h b/include/wild_encounter.h index f03b6e141d7e..c797db4f3043 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -67,6 +67,7 @@ bool8 StandardWildEncounter_Debug(void); u32 CalculateChainFishingShinyRolls(void); void CreateWildMon(u16 species, u8 level); bool32 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags); +bool32 AreLegendariesInSootopolisPreventingEncounters(void); u16 GetCurrentMapWildMonHeaderId(void); bool8 CheckFeebasAtCoords(s16 x, s16 y); u32 ChooseWildMonIndex_Land(void); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c8eac749fd5f..032bc4ee7662 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -97,6 +97,13 @@ void UpdateOverworldEncounters(void) } bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); + + if (shouldSpawnWaterMons && !AreLegendariesInSootopolisPreventingEncounters()) + { + OWE_ResetSpawnCounterPlayAmbientCry(); + return; + } + if (OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) { u16 spawnSlot = NextSpawnMonSlot(); diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 4aa06ae18d5a..d1d9fe24f3aa 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -668,7 +668,7 @@ static bool8 AllowWildCheckOnNewMetatile(void) return TRUE; } -static bool8 AreLegendariesInSootopolisPreventingEncounters(void) +bool32 AreLegendariesInSootopolisPreventingEncounters(void) { if (gSaveBlock1Ptr->location.mapGroup != MAP_GROUP(MAP_SOOTOPOLIS_CITY) || gSaveBlock1Ptr->location.mapNum != MAP_NUM(MAP_SOOTOPOLIS_CITY)) From 218b06305a97bb014a79c7f1883c8c84136a849f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 15:57:52 +0000 Subject: [PATCH 256/572] Add Mass Outbreak Functionality --- include/wild_encounter.h | 2 ++ src/overworld_encounters.c | 69 ++++++++++++++++++++++++-------------- src/wild_encounter.c | 4 +-- 3 files changed, 48 insertions(+), 27 deletions(-) diff --git a/include/wild_encounter.h b/include/wild_encounter.h index c797db4f3043..6d930f4b0005 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -67,6 +67,8 @@ bool8 StandardWildEncounter_Debug(void); u32 CalculateChainFishingShinyRolls(void); void CreateWildMon(u16 species, u8 level); bool32 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags); +bool32 SetUpMassOutbreakEncounter(u8 flags); +bool32 DoMassOutbreakEncounterTest(void); bool32 AreLegendariesInSootopolisPreventingEncounters(void); u16 GetCurrentMapWildMonHeaderId(void); bool8 CheckFeebasAtCoords(s16 x, s16 y); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 032bc4ee7662..36344e66bf4e 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -26,14 +26,16 @@ #define sOverworldEncounterLevel trainerRange_berryTreeId #define sAge playerCopyableMovement -#define sRoamerStatus directionSequenceIndex +#define sRoamerOutbreakStatus directionSequenceIndex +#define OWE_NON_ROAMER_OUTBREAK ROAMER_COUNT // If less than this value, OWE is a roamer. +#define OWE_MASS_OUTBREAK_INDEX OWE_NON_ROAMER_OUTBREAK + 1 static EWRAM_DATA u8 sOWESpawnCountdown = 0; static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool32 OWE_ShouldSpawnWaterMons(void); -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak); static bool8 IsSafeToSpawnObjectEvents(void); static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); @@ -81,7 +83,7 @@ void UpdateOverworldEncounters(void) u16 speciesId = SPECIES_NONE; bool32 isShiny = FALSE; bool32 isFemale = FALSE; - u32 roamerIndex = ROAMER_COUNT; + u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; s16 x, y; if (sOWESpawnCountdown != 0) @@ -116,7 +118,7 @@ void UpdateOverworldEncounters(void) u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); u32 level; - u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &roamerIndex); + u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(level)) { @@ -152,7 +154,7 @@ void UpdateOverworldEncounters(void) gObjectEvents[objectEventId].range.rangeX = OW_ENCOUNTER_MOVEMENT_RANGE_X; gObjectEvents[objectEventId].range.rangeY = OW_ENCOUNTER_MOVEMENT_RANGE_Y; gObjectEvents[objectEventId].sOverworldEncounterLevel = level; - gObjectEvents[objectEventId].sRoamerStatus = roamerIndex; + gObjectEvents[objectEventId].sRoamerOutbreakStatus = indexRoamerOutbreak; u8 directions[4] = {DIR_SOUTH, DIR_NORTH, DIR_WEST, DIR_EAST}; ObjectEventTurn(&gObjectEvents[objectEventId], directions[Random() & 3]); @@ -382,7 +384,7 @@ void CreateOverworldWildEncounter(void) { u32 localId = gSpecialVar_LastTalked; u32 objEventId = GetObjectEventIdByLocalId(localId); - u32 roamerIndex; + u32 indexRoamerOutbreak; struct ObjectEvent *object = &gObjectEvents[objEventId]; if (objEventId >= OBJECT_EVENTS_COUNT) @@ -391,11 +393,11 @@ void CreateOverworldWildEncounter(void) if (!IsOverworldWildEncounter(object)) return; - roamerIndex = object->sRoamerStatus; - if (roamerIndex < ROAMER_COUNT && IsRoamerAt(roamerIndex, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) + indexRoamerOutbreak = object->sRoamerOutbreakStatus; + if (indexRoamerOutbreak < OWE_NON_ROAMER_OUTBREAK && IsRoamerAt(indexRoamerOutbreak, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) { - CreateRoamerMonInstance(roamerIndex); - gEncounteredRoamerIndex = roamerIndex; + CreateRoamerMonInstance(indexRoamerOutbreak); + gEncounteredRoamerIndex = indexRoamerOutbreak; BattleSetup_StartRoamerBattle(); return; } @@ -421,6 +423,13 @@ void CreateOverworldWildEncounter(void) isFemale ); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); + if (indexRoamerOutbreak == OWE_MASS_OUTBREAK_INDEX && gSaveBlock1Ptr->outbreakPokemonSpecies == speciesId + && gSaveBlock1Ptr->location.mapNum == gSaveBlock1Ptr->outbreakLocationMapNum + && gSaveBlock1Ptr->location.mapGroup == gSaveBlock1Ptr->outbreakLocationMapGroup) + { + for (u32 i = 0; i < MAX_MON_MOVES; i++) + SetMonMoveSlot(&gEnemyParty[0], gSaveBlock1Ptr->outbreakPokemonMoves[i], i); + } BattleSetup_StartWildBattle(); } @@ -494,15 +503,15 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent if (!IsOverworldWildEncounter(objectEvent)) return; - objectEvent->sRoamerStatus = 0; + objectEvent->sRoamerOutbreakStatus = 0; if (gMain.callback2 == CB2_Overworld) OWE_DoSpawnDespawnAnim(objectEvent, FALSE); } -u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex) +u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) { - SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level, roamerIndex); + SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level, indexRoamerOutbreak); u16 graphicsId = *speciesId + OBJ_EVENT_MON; if (*isFemale) @@ -519,7 +528,7 @@ void OverworldWildEncounter_SetMinimumSpawnTimer(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; } -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex) +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) { const struct WildPokemonInfo *wildMonInfo; enum WildPokemonArea wildArea; @@ -540,21 +549,31 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; } - if (*roamerIndex < ROAMER_COUNT && IsRoamerAt(*roamerIndex, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) + if (*indexRoamerOutbreak < OWE_NON_ROAMER_OUTBREAK && IsRoamerAt(*indexRoamerOutbreak, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) + { + CreateRoamerMonInstance(*indexRoamerOutbreak); + } + else if (*indexRoamerOutbreak == OWE_MASS_OUTBREAK_INDEX && gSaveBlock1Ptr->outbreakPokemonSpecies != SPECIES_NONE + && gSaveBlock1Ptr->location.mapNum == gSaveBlock1Ptr->outbreakLocationMapNum + && gSaveBlock1Ptr->location.mapGroup == gSaveBlock1Ptr->outbreakLocationMapGroup) { - CreateRoamerMonInstance(*roamerIndex); + SetUpMassOutbreakEncounter(0); } else if (TryStartRoamerEncounter() && !OWE_DoesRoamerExistOnMap()) { - *roamerIndex = gEncounteredRoamerIndex; + *indexRoamerOutbreak = gEncounteredRoamerIndex; } else if (OW_WILD_ENCOUNTERS_FEEBAS && wildArea == WILD_AREA_WATER && CheckFeebasAtCoords(x, y)) { *level = ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING); - *speciesId = gWildFeebas.species; CreateWildMon(*speciesId, *level); } + else if (DoMassOutbreakEncounterTest()) + { + SetUpMassOutbreakEncounter(WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE); + *indexRoamerOutbreak = OWE_MASS_OUTBREAK_INDEX; + } else if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE)) { ZeroEnemyPartyMons(); @@ -795,11 +814,11 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const bool32 isShiny = FALSE; bool32 isFemale = FALSE; u32 level; - u32 roamerIndex = ROAMER_COUNT; + u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; u32 storedRoamer = (template->trainerType >> 8) & 0xFF; if (storedRoamer) - roamerIndex = storedRoamer; + indexRoamerOutbreak = storedRoamer; SetOverworldEncounterSpeciesInfo( template->x - MAP_OFFSET, @@ -808,7 +827,7 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const &isShiny, &isFemale, &level, - &roamerIndex + &indexRoamerOutbreak ); // Have a fallback incase of no header mons @@ -820,7 +839,7 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const templateOWE.graphicsId = graphicsId; templateOWE.sOverworldEncounterLevel = level; - templateOWE.trainerType = (TRAINER_TYPE_ENCOUNTER & 0xFF) | ((roamerIndex & 0xFF) << 8); + templateOWE.trainerType = (TRAINER_TYPE_ENCOUNTER & 0xFF) | ((indexRoamerOutbreak & 0xFF) << 8); return templateOWE; } @@ -1139,7 +1158,7 @@ static bool32 OWE_DoesRoamerExistOnMap(void) for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { struct ObjectEvent *object = &gObjectEvents[i]; - if (IsOverworldWildEncounter(object) && object->sRoamerStatus == gEncounteredRoamerIndex) + if (IsOverworldWildEncounter(object) && object->sRoamerOutbreakStatus == gEncounteredRoamerIndex) return TRUE; } @@ -1154,7 +1173,7 @@ void OverworldWildEncounter_InitRoamerStatus(struct ObjectEvent *objectEvent, co if (!IsOverworldWildEncounter(objectEvent)) return; - objectEvent->sRoamerStatus = (template->trainerType >> 8) & 0xFF; + objectEvent->sRoamerOutbreakStatus = (template->trainerType >> 8) & 0xFF; } static bool32 OWE_CheckRestrictedMovementMetatile(struct ObjectEvent *objectEvent, u32 direction) @@ -1201,4 +1220,4 @@ static bool32 OWE_CheckRestrictedMovementMap(struct ObjectEvent *objectEvent, u3 #undef STARTED #undef sOverworldEncounterLevel #undef sAge -#undef sRoamerStatus +#undef sRoamerOutbreakStatus diff --git a/src/wild_encounter.c b/src/wild_encounter.c index d1d9fe24f3aa..1823f3576293 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -584,7 +584,7 @@ static u16 GenerateFishingWildMon(const struct WildPokemonInfo *wildMonInfo, u8 return wildMonSpecies; } -static bool8 SetUpMassOutbreakEncounter(u8 flags) +bool32 SetUpMassOutbreakEncounter(u8 flags) { u16 i; @@ -598,7 +598,7 @@ static bool8 SetUpMassOutbreakEncounter(u8 flags) return TRUE; } -static bool8 DoMassOutbreakEncounterTest(void) +bool32 DoMassOutbreakEncounterTest(void) { if (gSaveBlock1Ptr->outbreakPokemonSpecies != SPECIES_NONE && gSaveBlock1Ptr->location.mapNum == gSaveBlock1Ptr->outbreakLocationMapNum From 4bbfcb3d3dffeaa52b1338243de01e8251ad66ec Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 16:01:07 +0000 Subject: [PATCH 257/572] Rename Function --- src/overworld_encounters.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 36344e66bf4e..76c3f0a3ced9 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -46,7 +46,7 @@ static void SortOWEMonAges(void); static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny); static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); -static bool32 OWE_DoesRoamerExistOnMap(void); +static bool32 OWE_DoesRoamerObjectExist(void); static bool32 OWE_CheckRestrictedMovementMetatile(struct ObjectEvent *objectEvent, u32 direction); static bool32 OWE_CheckRestrictedMovementMap(struct ObjectEvent *objectEvent, u32 direction); static u32 GetNumActiveOverworldEncounters(void); @@ -559,7 +559,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 { SetUpMassOutbreakEncounter(0); } - else if (TryStartRoamerEncounter() && !OWE_DoesRoamerExistOnMap()) + else if (TryStartRoamerEncounter() && !OWE_DoesRoamerObjectExist()) { *indexRoamerOutbreak = gEncounteredRoamerIndex; } @@ -1153,7 +1153,7 @@ static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) #undef MAP_METATILE_VIEW_X #undef MAP_METATILE_VIEW_Y -static bool32 OWE_DoesRoamerExistOnMap(void) +static bool32 OWE_DoesRoamerObjectExist(void) { for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { From 64665b73f57dafcde06d7cc05f3659dd8cc545e0 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 16:08:02 +0000 Subject: [PATCH 258/572] Fix OWE_CheckRestrictedMovement --- src/overworld_encounters.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 76c3f0a3ced9..370baf85d12f 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -47,8 +47,8 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_DoesRoamerObjectExist(void); -static bool32 OWE_CheckRestrictedMovementMetatile(struct ObjectEvent *objectEvent, u32 direction); -static bool32 OWE_CheckRestrictedMovementMap(struct ObjectEvent *objectEvent, u32 direction); +static bool32 OWE_CheckRestrictMovementMetatile(struct ObjectEvent *objectEvent, u32 direction); +static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, u32 direction); static u32 GetNumActiveOverworldEncounters(void); static u32 GetNumActiveGeneratedOverworldEncounters(void); @@ -879,8 +879,9 @@ void TryRemoveOverworldWildEncounter(u32 localId) bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction) { - return ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictedMovementMetatile(objectEvent, direction)) - || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictedMovementMap(objectEvent, direction))); + // Returns TRUE if movement is restricted. + return ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatile(objectEvent, direction)) + || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMap(objectEvent, direction))); } void DespawnOldestOWE_Pal(void) @@ -1176,7 +1177,7 @@ void OverworldWildEncounter_InitRoamerStatus(struct ObjectEvent *objectEvent, co objectEvent->sRoamerOutbreakStatus = (template->trainerType >> 8) & 0xFF; } -static bool32 OWE_CheckRestrictedMovementMetatile(struct ObjectEvent *objectEvent, u32 direction) +static bool32 OWE_CheckRestrictMovementMetatile(struct ObjectEvent *objectEvent, u32 direction) { s16 xCurrent = objectEvent->currentCoords.x; s16 yCurrent = objectEvent->currentCoords.y; @@ -1200,7 +1201,7 @@ static bool32 OWE_CheckRestrictedMovementMetatile(struct ObjectEvent *objectEven return TRUE; } -static bool32 OWE_CheckRestrictedMovementMap(struct ObjectEvent *objectEvent, u32 direction) +static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, u32 direction) { s16 xCurrent = objectEvent->currentCoords.x; s16 yCurrent = objectEvent->currentCoords.y; @@ -1210,9 +1211,9 @@ static bool32 OWE_CheckRestrictedMovementMap(struct ObjectEvent *objectEvent, u3 u32 mapNum = objectEvent->mapNum; if (mapGroup == gSaveBlock1Ptr->location.mapGroup && mapNum == gSaveBlock1Ptr->location.mapNum) - return AreCoordsInsidePlayerMap(xNew, yNew); - else return !AreCoordsInsidePlayerMap(xNew, yNew); + else + return AreCoordsInsidePlayerMap(xNew, yNew); } #undef tLocalId From 5a584a07734f089f539a9ae46ae82183aa034126 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 16:26:49 +0000 Subject: [PATCH 259/572] Add OWE_CreateEnemyPartyMon and documentation --- src/overworld_encounters.c | 71 +++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 370baf85d12f..7a90c0dfda76 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -51,6 +51,7 @@ static bool32 OWE_CheckRestrictMovementMetatile(struct ObjectEvent *objectEvent, static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, u32 direction); static u32 GetNumActiveOverworldEncounters(void); static u32 GetNumActiveGeneratedOverworldEncounters(void); +static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); void OWE_ResetSpawnCounterPlayAmbientCry(void) { @@ -529,14 +530,37 @@ void OverworldWildEncounter_SetMinimumSpawnTimer(void) } static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) +{ + u32 personality; + + if (!OWE_CreateEnemyPartyMon(speciesId, level, indexRoamerOutbreak, x, y)) + return; + + *speciesId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); + *level = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); + personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY); + + if (*speciesId == SPECIES_UNOWN) + *speciesId = GetUnownSpeciesId(personality); + + *isShiny = ComputePlayerShinyOdds(personality); + if (GetGenderFromSpeciesAndPersonality(*speciesId, personality) == MON_FEMALE) + *isFemale = TRUE; + else + *isFemale = FALSE; + + ZeroEnemyPartyMons(); +} + +static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) { const struct WildPokemonInfo *wildMonInfo; enum WildPokemonArea wildArea; enum TimeOfDay timeOfDay; - u32 personality; u32 headerId = GetCurrentMapWildMonHeaderId(); + u32 metatileBehavior = MapGridGetMetatileBehaviorAt(x, y); - if (MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(x, y))) + if (MetatileBehavior_IsWaterWildEncounter(metatileBehavior)) { wildArea = WILD_AREA_WATER; timeOfDay = GetTimeOfDayForEncounters(headerId, wildArea); @@ -549,6 +573,26 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; } + /* + These functions perform checks of various encounter types in the following order: + 1. Manual/Semi-Manual OWE Defined Roamer Encounter + 2. Manual/Semi-Manual OWE Defined Mass Outbreak Encounter + 3. Attempted Generated Roamer Encounter + 4. Attempted Generated Feebas encounter + 5. Attempted Generated Mass Outbreak Encounter + 6. Attempted Generated Standard Wild Encounter generation + Note: While a player cannot be stopped from trying to trigger checks 1 and 2, it is not recommended. + This is due to the fact that it requires careful tracking of the Roamer/Mass Outbreak in the save. + Regardless of this, 'functionality' is provided mostly to prevent breakages if the player does so, + accidentally. + + The structure of this statement ensures that only one of these encounter types can succeed per call, + with the resultant wild mon being created in gEnemyParty[0]. + If none of these checks succeed, speciesId is set to SPECIES_NONE and FALSE is returned. + */ + + // REGARDING ABOVE: A sanitisation check to clean the top 8 bits of trainerType when it is read from templates would prevent it, but remove 'functionality'. + if (*indexRoamerOutbreak < OWE_NON_ROAMER_OUTBREAK && IsRoamerAt(*indexRoamerOutbreak, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) { CreateRoamerMonInstance(*indexRoamerOutbreak); @@ -563,13 +607,13 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 { *indexRoamerOutbreak = gEncounteredRoamerIndex; } - else if (OW_WILD_ENCOUNTERS_FEEBAS && wildArea == WILD_AREA_WATER && CheckFeebasAtCoords(x, y)) + else if (OW_WILD_ENCOUNTERS_FEEBAS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) { *level = ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING); *speciesId = gWildFeebas.species; CreateWildMon(*speciesId, *level); } - else if (DoMassOutbreakEncounterTest()) + else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior)) { SetUpMassOutbreakEncounter(WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE); *indexRoamerOutbreak = OWE_MASS_OUTBREAK_INDEX; @@ -578,25 +622,10 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 { ZeroEnemyPartyMons(); *speciesId = SPECIES_NONE; - return; + return FALSE; } - // gEnemyParty[1] will contain a generated wild mon if a roaming encounter was generated or specified manually. - // If not it will be contained in gEnemyParty[0]. - *speciesId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); - *level = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); - personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY); - - if (*speciesId == SPECIES_UNOWN) - *speciesId = GetUnownSpeciesId(personality); - - *isShiny = ComputePlayerShinyOdds(personality); - if (GetGenderFromSpeciesAndPersonality(*speciesId, personality) == MON_FEMALE) - *isFemale = TRUE; - else - *isFemale = FALSE; - - ZeroEnemyPartyMons(); + return TRUE; } static bool8 IsSafeToSpawnObjectEvents(void) From 6b1846669c53477edea3b1a01410ecf2c53d7abc Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 17:11:00 +0000 Subject: [PATCH 260/572] Rename Function --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 7401415afb58..be765cbc4c6b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -123,6 +123,6 @@ u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equal void Task_OWE_WaitMovements(u8 taskId); bool32 OWE_IsWaitTaskActive(void); enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); -void OverworldWildEncounter_InitRoamerStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template); +void OverworldWildEncounter_InitRoamerOutbreakStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 7c8e9a27a0c1..ffaa267f8063 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1483,7 +1483,7 @@ static u8 InitObjectEventStateFromTemplate(const struct ObjectEventTemplate *tem objectEvent->mapNum = mapNum; objectEvent->trainerRange_berryTreeId = template->trainerRange_berryTreeId; objectEvent->previousMovementDirection = gInitialMovementTypeFacingDirections[template->movementType]; - OverworldWildEncounter_InitRoamerStatus(objectEvent, template); + OverworldWildEncounter_InitRoamerOutbreakStatus(objectEvent, template); SetObjectEventDirection(objectEvent, objectEvent->previousMovementDirection); if (sMovementTypeHasRange[objectEvent->movementType]) { diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7a90c0dfda76..041a7c2fb68a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1195,7 +1195,7 @@ static bool32 OWE_DoesRoamerObjectExist(void) return FALSE; } -void OverworldWildEncounter_InitRoamerStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template) +void OverworldWildEncounter_InitRoamerOutbreakStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template) { // Should only occur for Manual or Semi-Manual Overworld Encounters. // Trainer type can be be set on manual encounters to specify a roamer index, From b603d1082777dc7806682cdcf2181f41836507a6 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 17:13:27 +0000 Subject: [PATCH 261/572] Add Battle Pyramid/Pike Functionality --- include/wild_encounter.h | 2 ++ src/overworld_encounters.c | 72 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 6d930f4b0005..a6655cd44299 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -46,6 +46,8 @@ struct WildPokemonHeader extern const struct WildPokemonHeader gWildMonHeaders[]; +extern const struct WildPokemonHeader gBattlePyramidWildMonHeaders[]; +extern const struct WildPokemonHeader gBattlePikeWildMonHeaders[]; extern const struct WildPokemon gWildFeebas; extern bool8 gIsFishingEncounter; extern bool8 gIsSurfingEncounter; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 041a7c2fb68a..c94c199bab5d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -2,6 +2,8 @@ #include "overworld_encounters.h" #include "battle_setup.h" #include "battle_main.h" +#include "battle_pike.h" +#include "battle_pyramid.h" #include "event_data.h" #include "event_object_movement.h" #include "fieldmap.h" @@ -18,11 +20,13 @@ #include "wild_encounter.h" #include "constants/event_objects.h" #include "constants/field_effects.h" +#include "constants/layouts.h" #include "constants/map_types.h" #include "constants/trainer_types.h" #include "constants/songs.h" #include "constants/vars.h" #include "constants/wild_encounter.h" +// #include "data/wild_encounters.h" #define sOverworldEncounterLevel trainerRange_berryTreeId #define sAge playerCopyableMovement @@ -386,6 +390,7 @@ void CreateOverworldWildEncounter(void) u32 localId = gSpecialVar_LastTalked; u32 objEventId = GetObjectEventIdByLocalId(localId); u32 indexRoamerOutbreak; + u32 headerId = GetCurrentMapWildMonHeaderId(); struct ObjectEvent *object = &gObjectEvents[objEventId]; if (objEventId >= OBJECT_EVENTS_COUNT) @@ -424,6 +429,23 @@ void CreateOverworldWildEncounter(void) isFemale ); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); + + if (headerId == HEADER_NONE) + { + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) + { + TryGenerateBattlePikeWildMon(FALSE); + BattleSetup_StartBattlePikeWildBattle(); + return; + } + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + { + GenerateBattlePyramidWildMon(); + BattleSetup_StartWildBattle(); + return; + } + } + if (indexRoamerOutbreak == OWE_MASS_OUTBREAK_INDEX && gSaveBlock1Ptr->outbreakPokemonSpecies == speciesId && gSaveBlock1Ptr->location.mapNum == gSaveBlock1Ptr->outbreakLocationMapNum && gSaveBlock1Ptr->location.mapGroup == gSaveBlock1Ptr->outbreakLocationMapGroup) @@ -560,6 +582,42 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam u32 headerId = GetCurrentMapWildMonHeaderId(); u32 metatileBehavior = MapGridGetMetatileBehaviorAt(x, y); + if (headerId == HEADER_NONE) + { + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) + { + headerId = GetBattlePikeWildMonHeaderId(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) + { + ZeroEnemyPartyMons(); + *speciesId = SPECIES_NONE; + return FALSE; + } + + TryGenerateBattlePikeWildMon(FALSE); + return TRUE; + } + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + { + headerId = gSaveBlock2Ptr->frontier.curChallengeBattleNum; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) + { + ZeroEnemyPartyMons(); + *speciesId = SPECIES_NONE; + return FALSE; + } + + GenerateBattlePyramidWildMon(); + return TRUE; + } + + ZeroEnemyPartyMons(); + *speciesId = SPECIES_NONE; + return FALSE; + } + if (MetatileBehavior_IsWaterWildEncounter(metatileBehavior)) { wildArea = WILD_AREA_WATER; @@ -694,7 +752,21 @@ static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) enum TimeOfDay timeOfDay; if (headerId == HEADER_NONE) + { + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) + { + headerId = GetBattlePikeWildMonHeaderId(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + return gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; + } + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + { + headerId = gSaveBlock2Ptr->frontier.curChallengeBattleNum; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + return gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; + } return FALSE; + } if (shouldSpawnWaterMons) { From 08548cade8cc92afa6f21e0836a58eed1cb1dd2a Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 17:35:03 +0000 Subject: [PATCH 262/572] Consolidate CreateOverworldWildEncounter Functions --- src/overworld_encounters.c | 49 +++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c94c199bab5d..a422c4c89a01 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -56,6 +56,9 @@ static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, u32 static u32 GetNumActiveOverworldEncounters(void); static u32 GetNumActiveGeneratedOverworldEncounters(void); static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); +static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); +static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); +static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); void OWE_ResetSpawnCounterPlayAmbientCry(void) { @@ -400,13 +403,8 @@ void CreateOverworldWildEncounter(void) return; indexRoamerOutbreak = object->sRoamerOutbreakStatus; - if (indexRoamerOutbreak < OWE_NON_ROAMER_OUTBREAK && IsRoamerAt(indexRoamerOutbreak, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) - { - CreateRoamerMonInstance(indexRoamerOutbreak); - gEncounteredRoamerIndex = indexRoamerOutbreak; - BattleSetup_StartRoamerBattle(); + if (CreateOverworldWildEncounter_CheckRoamer(indexRoamerOutbreak)) return; - } u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; @@ -430,30 +428,63 @@ void CreateOverworldWildEncounter(void) ); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); + if (CreateOverworldWildEncounter_CheckBattleFrontier(headerId)) + return; + + if (CreateOverworldWildEncounter_CheckMassOutbreak(indexRoamerOutbreak, speciesId)) + return; + + BattleSetup_StartWildBattle(); +} + +static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak) +{ + if (indexRoamerOutbreak < OWE_NON_ROAMER_OUTBREAK && IsRoamerAt(indexRoamerOutbreak, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) + { + CreateRoamerMonInstance(indexRoamerOutbreak); + gEncounteredRoamerIndex = indexRoamerOutbreak; + BattleSetup_StartRoamerBattle(); + return TRUE; + } + + return FALSE; +} + +static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId) +{ if (headerId == HEADER_NONE) { if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) { TryGenerateBattlePikeWildMon(FALSE); BattleSetup_StartBattlePikeWildBattle(); - return; + return TRUE; } if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) { GenerateBattlePyramidWildMon(); BattleSetup_StartWildBattle(); - return; + return TRUE; } } + return FALSE; +} + +static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId) +{ if (indexRoamerOutbreak == OWE_MASS_OUTBREAK_INDEX && gSaveBlock1Ptr->outbreakPokemonSpecies == speciesId && gSaveBlock1Ptr->location.mapNum == gSaveBlock1Ptr->outbreakLocationMapNum && gSaveBlock1Ptr->location.mapGroup == gSaveBlock1Ptr->outbreakLocationMapGroup) { for (u32 i = 0; i < MAX_MON_MOVES; i++) SetMonMoveSlot(&gEnemyParty[0], gSaveBlock1Ptr->outbreakPokemonMoves[i], i); + + BattleSetup_StartWildBattle(); + return TRUE; } - BattleSetup_StartWildBattle(); + + return FALSE; } struct AgeSort From f743ad8438acac316abd5bdf6d071cf1adf8c5f5 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 28 Dec 2025 12:39:03 -0600 Subject: [PATCH 263/572] Encounter task only applies to one mon at a time --- include/overworld_encounters.h | 3 ++- src/event_object_movement.c | 12 ++++++------ src/overworld_encounters.c | 19 ++++++++++--------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 12726e515ca2..fad37e25d939 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -38,6 +38,8 @@ #define INVALID_SPAWN_SLOT 0xFF +#define OWE_FLAG_START_ENCOUNTER 0x8000 + enum OverworldEncounterSpawnAnim { OWE_SPAWN_ANIM_GRASS, @@ -133,7 +135,6 @@ u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); void Task_OWE_WaitMovements(u8 taskId); -bool32 OWE_IsWaitTaskActive(void); enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); void OverworldWildEncounter_InitRoamerStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 82aeba2efaf3..faf8e7328c72 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11755,7 +11755,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent if (!ObjectEventExecSingleMovementAction(objectEvent, sprite)) return FALSE; - if (OWE_IsWaitTaskActive()) + if (objectEvent->trainerRange_berryTreeId & OWE_FLAG_START_ENCOUNTER) { u16 speciesId = OW_SPECIES(objectEvent); u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); @@ -11966,7 +11966,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent * u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - if (!OWE_IsWaitTaskActive()) + if (!(objectEvent->trainerRange_berryTreeId & OWE_FLAG_START_ENCOUNTER)) { direction = GetOppositeDirection(direction); } @@ -11989,7 +11989,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - if (!OWE_IsWaitTaskActive() && !(objectEvent->currentCoords.x == player->currentCoords.x || objectEvent->currentCoords.y == player->currentCoords.y)) + if (!(objectEvent->trainerRange_berryTreeId & OWE_FLAG_START_ENCOUNTER) && !(objectEvent->currentCoords.x == player->currentCoords.x || objectEvent->currentCoords.y == player->currentCoords.y)) { newDirection = GetOppositeDirection(newDirection); } @@ -12098,7 +12098,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve bool8 collision; u8 movementActionId; - if (distance <= 1 && !OWE_IsWaitTaskActive()) + if (distance <= 1 && !(objectEvent->trainerRange_berryTreeId & OWE_FLAG_START_ENCOUNTER)) { SetObjectEventDirection(objectEvent, GetOppositeDirection(objectEvent->movementDirection)); @@ -12109,7 +12109,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - if (!OWE_IsWaitTaskActive() && !(objectEvent->currentCoords.x == player->currentCoords.x || objectEvent->currentCoords.y == player->currentCoords.y)) + if (!(objectEvent->trainerRange_berryTreeId & OWE_FLAG_START_ENCOUNTER) && !(objectEvent->currentCoords.x == player->currentCoords.x || objectEvent->currentCoords.y == player->currentCoords.y)) { newDirection = GetOppositeDirection(newDirection); } @@ -12122,7 +12122,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve } } } - else if (distance == OWE_APPROACH_DISTANCE && !equalDistances && !OWE_IsWaitTaskActive()) + else if (distance == OWE_APPROACH_DISTANCE && !equalDistances && !(objectEvent->trainerRange_berryTreeId & OWE_FLAG_START_ENCOUNTER)) { if (sJumpTimer <= 0) { diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 88588a9719d3..62944cfdbca7 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -406,7 +406,7 @@ void CreateOverworldWildEncounter(void) u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; bool32 isFemale = OW_FEMALE(object) ? TRUE : FALSE; - u32 level = object->sOverworldEncounterLevel; + u32 level = (object->sOverworldEncounterLevel &= ~OWE_FLAG_START_ENCOUNTER); if (level > MAX_LEVEL) level = MAX_LEVEL; @@ -816,6 +816,14 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const return templateOWE; } +static bool32 OWE_IsWaitTaskActive(void) +{ + if (FindTaskIdByFunc(Task_OWE_WaitMovements) != TASK_NONE) + return TRUE; + + return FALSE; +} + #define tLocalId gTasks[taskId].data[0] void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) @@ -835,6 +843,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c LockPlayerFieldControls(); // Wait for both the player and the mon to finish their current movements. u8 taskId = CreateTask(Task_OWE_WaitMovements, 0); + wildMon->trainerRange_berryTreeId |= OWE_FLAG_START_ENCOUNTER; tLocalId = wildMon->localId; } } @@ -1063,14 +1072,6 @@ void Task_OWE_WaitMovements(u8 taskId) } } -bool32 OWE_IsWaitTaskActive(void) -{ - if (FindTaskIdByFunc(Task_OWE_WaitMovements) != TASK_NONE) - return TRUE; - - return FALSE; -} - enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior) { // Need to edit anims: From aa33cc059aaaab39184ac26a1e295f48b8c8378b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 18:17:00 +0000 Subject: [PATCH 264/572] Add Configs for Flee Sound --- include/config/overworld.h | 1 + src/overworld_encounters.c | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 6803bd62c55b..2db4a02fd0d9 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -83,6 +83,7 @@ #define OW_VANILLA_AMBIENT_CRIES TRUE // If TRUE, the ambient wild Pokémon cries will play on routes with encounter tables. #define OW_WILD_ENCOUNTERS_AMBIENT_CRIES TRUE // If TRUE, generated overworld encounters will play ambient cries based on their location relative to the player. #define OW_WILD_ENCOUNTERS_FEEBAS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. +#define OW_WILD_ENCOUNTERS_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an overworld wild encounter despawns. // Out-of-battle Ability effects #define OW_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8+, if a Pokémon with Synchronize leads the party, wild Pokémon will always have their same Nature as opposed to the 50% chance in previous games. Gift Pokémon excluded. diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index a422c4c89a01..9529aa44a31d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -59,6 +59,7 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); +static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); void OWE_ResetSpawnCounterPlayAmbientCry(void) { @@ -212,7 +213,7 @@ void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 spawn) if (spawn) OWE_PlayMonObjectCry(objectEvent); - else + else if (!spawn && OWE_ShouldPlayMonFleeSound(objectEvent)) PlaySE(SE_FLEE); if (isShiny && spawn) @@ -1348,6 +1349,17 @@ static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, u32 return AreCoordsInsidePlayerMap(xNew, yNew); } +static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) +{ + if (!IsOverworldWildEncounter(objectEvent) || OW_SPECIES(objectEvent) == SPECIES_NONE) + return FALSE; + + if (!AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) + return FALSE; + + return OW_WILD_ENCOUNTERS_DESPAWN_SOUND; +} + #undef tLocalId #undef NOT_STARTED #undef STARTED From 0a87db5c5510e11309b3bb16bd5ac46b50222395 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 18:29:48 +0000 Subject: [PATCH 265/572] Only Perform Level Check for Generated Mons --- src/overworld_encounters.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 9529aa44a31d..94070823d38a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -705,10 +705,10 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam } else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior)) { - SetUpMassOutbreakEncounter(WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE); + SetUpMassOutbreakEncounter(WILD_CHECK_KEEN_EYE); *indexRoamerOutbreak = OWE_MASS_OUTBREAK_INDEX; } - else if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE)) + else if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_KEEN_EYE)) { ZeroEnemyPartyMons(); *speciesId = SPECIES_NONE; From 0dc2f4d6a9ed2477d9532821a855f1903c2028a3 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 18:44:58 +0000 Subject: [PATCH 266/572] Add Double Battle Check --- src/overworld_encounters.c | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 94070823d38a..dcfb6a67066a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -59,6 +59,7 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); +static bool32 CreateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); void OWE_ResetSpawnCounterPlayAmbientCry(void) @@ -435,6 +436,9 @@ void CreateOverworldWildEncounter(void) if (CreateOverworldWildEncounter_CheckMassOutbreak(indexRoamerOutbreak, speciesId)) return; + if (CreateOverworldWildEncounter_CheckDoubleBattle(object, headerId)) + return; + BattleSetup_StartWildBattle(); } @@ -488,6 +492,41 @@ static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutb return FALSE; } +static bool32 CreateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId) +{ + enum WildPokemonArea wildArea; + enum TimeOfDay timeOfDay; + const struct WildPokemonInfo *wildMonInfo; + u32 metatileBehavior = MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x, objectEvent->currentCoords.y); + + if (TryDoDoubleWildBattle()) + { + struct Pokemon mon1 = gEnemyParty[0]; + + if (MetatileBehavior_IsWaterWildEncounter(metatileBehavior)) + { + wildArea = WILD_AREA_WATER; + timeOfDay = GetTimeOfDayForEncounters(headerId, wildArea); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + } + else + { + wildArea = WILD_AREA_LAND; + timeOfDay = GetTimeOfDayForEncounters(headerId, wildArea); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + } + + if (TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE)) + { + gEnemyParty[1] = mon1; + BattleSetup_StartDoubleWildBattle(); + return TRUE; + } + } + + return FALSE; +} + struct AgeSort { u8 slot:4; From 5583a23457627972f41cefe1e4639b28fd00ed00 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 18:50:21 +0000 Subject: [PATCH 267/572] Freeze Overworld Encounters During DexNav --- include/overworld_encounters.h | 1 + src/dexnav.c | 3 +++ src/overworld_encounters.c | 10 ++++++++++ 3 files changed, 14 insertions(+) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index be765cbc4c6b..f7fcc82814a5 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -124,5 +124,6 @@ void Task_OWE_WaitMovements(u8 taskId); bool32 OWE_IsWaitTaskActive(void); enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); void OverworldWildEncounter_InitRoamerOutbreakStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template); +void OverworldWildEncounter_FreezeAllObjects(void); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/dexnav.c b/src/dexnav.c index 432d8debce9f..874c23f22615 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -832,6 +832,7 @@ static void Task_SetUpDexNavSearch(u8 taskId) } FlagSet(DN_FLAG_SEARCHING); + OverworldWildEncounter_FreezeAllObjects(); gPlayerAvatar.creeping = TRUE; //initialize as true in case mon appears beside you task->tProximity = gSprites[gPlayerAvatar.spriteId].x; task->tFrameCount = 0; @@ -969,6 +970,7 @@ bool8 TryStartDexNavSearch(void) void EndDexNavSearch(u8 taskId) { FlagClear(DN_FLAG_SEARCHING); + UnfreezeObjectEvents(); DestroyTask(taskId); RemoveDexNavWindowAndGfx(); FieldEffectStop(&gSprites[sDexNavSearchDataPtr->fldEffSpriteId], sDexNavSearchDataPtr->fldEffId); @@ -1087,6 +1089,7 @@ static void Task_DexNavSearch(u8 taskId) sDexNavSearchDataPtr->abilityNum, sDexNavSearchDataPtr->heldItem, sDexNavSearchDataPtr->moves); FlagClear(DN_FLAG_SEARCHING); + UnfreezeObjectEvents(); ScriptContext_SetupScript(EventScript_StartDexNavBattle); Free(sDexNavSearchDataPtr); DestroyTask(taskId); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index dcfb6a67066a..2881d2e552fb 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1399,6 +1399,16 @@ static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) return OW_WILD_ENCOUNTERS_DESPAWN_SOUND; } +void OverworldWildEncounter_FreezeAllObjects(void) +{ + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + struct ObjectEvent *objectEvent = &gObjectEvents[i]; + if (IsOverworldWildEncounter(objectEvent)) + FreezeObjectEvent(objectEvent); + } +} + #undef tLocalId #undef NOT_STARTED #undef STARTED From 7e0daff96173baf899a396b2cb05fb16a0f7bb78 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 18:51:33 +0000 Subject: [PATCH 268/572] Update overworld_encounters.c --- src/overworld_encounters.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 2881d2e552fb..dd7013b265a5 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1340,9 +1340,7 @@ static bool32 OWE_DoesRoamerObjectExist(void) void OverworldWildEncounter_InitRoamerOutbreakStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template) { - // Should only occur for Manual or Semi-Manual Overworld Encounters. - // Trainer type can be be set on manual encounters to specify a roamer index, - // but probably shouldn't unless it can be consistently guarenteed. + // See comment in OWE_CreateEnemyPartyMon. if (!IsOverworldWildEncounter(objectEvent)) return; From 8314035148329ac26e5c4f6a4708c7cdd1aca351 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 18:53:38 +0000 Subject: [PATCH 269/572] More Efficient Code --- src/overworld_encounters.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index dd7013b265a5..11006a259f46 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -627,7 +627,11 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 u32 personality; if (!OWE_CreateEnemyPartyMon(speciesId, level, indexRoamerOutbreak, x, y)) + { + ZeroEnemyPartyMons(); + *speciesId = SPECIES_NONE; return; + } *speciesId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); *level = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); @@ -660,11 +664,7 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam headerId = GetBattlePikeWildMonHeaderId(); timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) - { - ZeroEnemyPartyMons(); - *speciesId = SPECIES_NONE; return FALSE; - } TryGenerateBattlePikeWildMon(FALSE); return TRUE; @@ -674,18 +674,12 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam headerId = gSaveBlock2Ptr->frontier.curChallengeBattleNum; timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) - { - ZeroEnemyPartyMons(); - *speciesId = SPECIES_NONE; return FALSE; - } GenerateBattlePyramidWildMon(); return TRUE; } - ZeroEnemyPartyMons(); - *speciesId = SPECIES_NONE; return FALSE; } @@ -749,8 +743,6 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam } else if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_KEEN_EYE)) { - ZeroEnemyPartyMons(); - *speciesId = SPECIES_NONE; return FALSE; } From 9922a245755e2d0588542f3dfe4c4701497862e7 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 19:14:21 +0000 Subject: [PATCH 270/572] Early return for OWE_CheckActiveEncounterTable --- src/overworld_encounters.c | 112 +++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 55 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index cc1cb836c9a9..5c19a69d24e8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -90,6 +90,13 @@ void UpdateOverworldEncounters(void) OverworldWildEncounter_SetMinimumSpawnTimer(); } + bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); + if (OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) + { + OWE_ResetSpawnCounterPlayAmbientCry(); + return; + } + u16 speciesId = SPECIES_NONE; bool32 isShiny = FALSE; bool32 isFemale = FALSE; @@ -107,8 +114,6 @@ void UpdateOverworldEncounters(void) OWE_ResetSpawnCounterPlayAmbientCry(); return; } - - bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); if (shouldSpawnWaterMons && !AreLegendariesInSootopolisPreventingEncounters()) { @@ -116,67 +121,64 @@ void UpdateOverworldEncounters(void) return; } - if (OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) - { - u16 spawnSlot = NextSpawnMonSlot(); + u16 spawnSlot = NextSpawnMonSlot(); - if (spawnSlot == INVALID_SPAWN_SLOT) - { - OWE_ResetSpawnCounterPlayAmbientCry(); - return; - } - - u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); - u32 level; - u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); + if (spawnSlot == INVALID_SPAWN_SLOT) + { + OWE_ResetSpawnCounterPlayAmbientCry(); + return; + } + + u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); + u32 level; + u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); - if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(level)) - { - OWE_ResetSpawnCounterPlayAmbientCry(); - return; - } - - struct ObjectEventTemplate objectEventTemplate = { - .localId = localId, - .graphicsId = graphicsId, - .x = x - MAP_OFFSET, - .y = y - MAP_OFFSET, - .elevation = MapGridGetElevationAt(x, y), - .movementType = OWE_GetMovementTypeFromSpecies(speciesId), - .trainerType = TRAINER_TYPE_ENCOUNTER, - }; - - if (!OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) - { - OWE_ResetSpawnCounterPlayAmbientCry(); - return; - } + if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(level)) + { + OWE_ResetSpawnCounterPlayAmbientCry(); + return; + } + + struct ObjectEventTemplate objectEventTemplate = { + .localId = localId, + .graphicsId = graphicsId, + .x = x - MAP_OFFSET, + .y = y - MAP_OFFSET, + .elevation = MapGridGetElevationAt(x, y), + .movementType = OWE_GetMovementTypeFromSpecies(speciesId), + .trainerType = TRAINER_TYPE_ENCOUNTER, + }; + + if (!OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) + { + OWE_ResetSpawnCounterPlayAmbientCry(); + return; + } - u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); + u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); - if (objectEventId >= OBJECT_EVENTS_COUNT) - { - OWE_ResetSpawnCounterPlayAmbientCry(); - return; - } + if (objectEventId >= OBJECT_EVENTS_COUNT) + { + OWE_ResetSpawnCounterPlayAmbientCry(); + return; + } - gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; - gObjectEvents[objectEventId].range.rangeX = OW_ENCOUNTER_MOVEMENT_RANGE_X; - gObjectEvents[objectEventId].range.rangeY = OW_ENCOUNTER_MOVEMENT_RANGE_Y; - gObjectEvents[objectEventId].sOverworldEncounterLevel = level; - gObjectEvents[objectEventId].sRoamerOutbreakStatus = indexRoamerOutbreak; + gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; + gObjectEvents[objectEventId].range.rangeX = OW_ENCOUNTER_MOVEMENT_RANGE_X; + gObjectEvents[objectEventId].range.rangeY = OW_ENCOUNTER_MOVEMENT_RANGE_Y; + gObjectEvents[objectEventId].sOverworldEncounterLevel = level; + gObjectEvents[objectEventId].sRoamerOutbreakStatus = indexRoamerOutbreak; - u8 directions[4] = {DIR_SOUTH, DIR_NORTH, DIR_WEST, DIR_EAST}; - ObjectEventTurn(&gObjectEvents[objectEventId], directions[Random() & 3]); + u8 directions[4] = {DIR_SOUTH, DIR_NORTH, DIR_WEST, DIR_EAST}; + ObjectEventTurn(&gObjectEvents[objectEventId], directions[Random() & 3]); - // Hide reflections for spawns in water - // (It just looks weird) - if (shouldSpawnWaterMons) - gObjectEvents[objectEventId].hideReflection = TRUE; + // Hide reflections for spawns in water + // (It just looks weird) + if (shouldSpawnWaterMons) + gObjectEvents[objectEventId].hideReflection = TRUE; - // Slower replacement spawning - sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); - } + // Slower replacement spawning + sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); } static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny) From 0bb6d8caa9ec0949801525302e0ddc9c5efd8702 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 20:50:44 +0000 Subject: [PATCH 271/572] Update overworld_encounters.c --- include/overworld_encounters.h | 2 +- src/overworld.c | 1 + src/overworld_encounters.c | 59 +++++++++++----------------------- 3 files changed, 20 insertions(+), 42 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 45d31bb5dbe8..83190723e541 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -101,7 +101,7 @@ void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); void OverworldWildEncounter_SetMinimumSpawnTimer(void); -void RemoveAllOverworldEncounterObjects(void); +void RemoveAllGeneratedOverworldEncounterObjects(void); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent); bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent); diff --git a/src/overworld.c b/src/overworld.c index ebbb5dc16779..f659684d58a1 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -894,6 +894,7 @@ void LoadMapFromCameraTransition(u8 mapGroup, u8 mapNum) || gMapHeader.regionMapSectionId != sLastMapSectionId) ShowMapNamePopup(); } + OverworldWildEncounter_SetMinimumSpawnTimer(); } static void LoadMapFromWarp(bool32 a1) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5c19a69d24e8..38a85dd4e36d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -72,63 +72,40 @@ void OWE_ResetSpawnCounterPlayAmbientCry(void) void UpdateOverworldEncounters(void) { - if (!OW_WILD_ENCOUNTERS_OVERWORLD || ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING)) + if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING)) return; - if (FlagGet(OW_FLAG_NO_ENCOUNTER)) - { - if (sOWESpawnCountdown != 255) - { - RemoveAllOverworldEncounterObjects(); - sOWESpawnCountdown = 255; - } - - return; - } - else if (sOWESpawnCountdown == 255) - { - OverworldWildEncounter_SetMinimumSpawnTimer(); - } - bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); - if (OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) + if (!OW_WILD_ENCOUNTERS_OVERWORLD + || FlagGet(OW_FLAG_NO_ENCOUNTER) + || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) { - OWE_ResetSpawnCounterPlayAmbientCry(); + OverworldWildEncounter_SetMinimumSpawnTimer(); + RemoveAllGeneratedOverworldEncounterObjects(); return; } - u16 speciesId = SPECIES_NONE; - bool32 isShiny = FALSE; - bool32 isFemale = FALSE; - u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; - s16 x, y; - if (sOWESpawnCountdown != 0) { sOWESpawnCountdown--; return; } - if (!IsSafeToSpawnObjectEvents() || !TrySelectTile(&x, &y)) - { - OWE_ResetSpawnCounterPlayAmbientCry(); - return; - } - - if (shouldSpawnWaterMons && !AreLegendariesInSootopolisPreventingEncounters()) - { - OWE_ResetSpawnCounterPlayAmbientCry(); - return; - } - + s16 x, y; u16 spawnSlot = NextSpawnMonSlot(); - - if (spawnSlot == INVALID_SPAWN_SLOT) + if (!IsSafeToSpawnObjectEvents() + || !TrySelectTile(&x, &y) + || spawnSlot == INVALID_SPAWN_SLOT + || (shouldSpawnWaterMons && !AreLegendariesInSootopolisPreventingEncounters())) { OWE_ResetSpawnCounterPlayAmbientCry(); return; } - + + u16 speciesId = SPECIES_NONE; + bool32 isShiny = FALSE; + bool32 isFemale = FALSE; + u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); u32 level; u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); @@ -789,12 +766,12 @@ static bool32 OWE_ShouldSpawnWaterMons(void) return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); } -void RemoveAllOverworldEncounterObjects(void) +void RemoveAllGeneratedOverworldEncounterObjects(void) { for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { struct ObjectEvent *obj = &gObjectEvents[i]; - if (IsGeneratedOverworldWildEncounter(obj)) + if (IsGeneratedOverworldWildEncounter(obj) && obj->active) RemoveObjectEventByLocalIdAndMap(obj->localId, obj->mapNum, obj->mapGroup); } } From ec3a68fcbdd9810663091c0d19ffff46e4cff229 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:13:13 +0000 Subject: [PATCH 272/572] Prevent RemoveAllGeneratedOverworldEncounterObjects every frame --- src/overworld_encounters.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 38a85dd4e36d..e866d429e228 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -76,9 +76,10 @@ void UpdateOverworldEncounters(void) return; bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); - if (!OW_WILD_ENCOUNTERS_OVERWORLD + if ((!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER) || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) + && sOWESpawnCountdown != OWE_SPAWN_TIME_MINIMUM) { OverworldWildEncounter_SetMinimumSpawnTimer(); RemoveAllGeneratedOverworldEncounterObjects(); From 465caaade8221529ccaa3171d4ea8e6038f64e04 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:27:21 +0000 Subject: [PATCH 273/572] Revert Countdown Check --- src/overworld_encounters.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e866d429e228..1337fa26e8a4 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -76,15 +76,21 @@ void UpdateOverworldEncounters(void) return; bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); - if ((!OW_WILD_ENCOUNTERS_OVERWORLD + if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER) || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) - && sOWESpawnCountdown != OWE_SPAWN_TIME_MINIMUM) { - OverworldWildEncounter_SetMinimumSpawnTimer(); - RemoveAllGeneratedOverworldEncounterObjects(); + if (sOWESpawnCountdown != 255) + { + RemoveAllOverworldEncounterObjects(); + sOWESpawnCountdown = 255; + } return; } + else if (sOWESpawnCountdown == 255) + { + OverworldWildEncounter_SetMinimumSpawnTimer(); + } if (sOWESpawnCountdown != 0) { From 7bb6276427c4aee7a3d8f9b3c9d71670510e636a Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:53:11 +0000 Subject: [PATCH 274/572] Name Fixes --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 1337fa26e8a4..30293c8717d6 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -82,7 +82,7 @@ void UpdateOverworldEncounters(void) { if (sOWESpawnCountdown != 255) { - RemoveAllOverworldEncounterObjects(); + RemoveAllGeneratedOverworldEncounterObjects(); sOWESpawnCountdown = 255; } return; From 902605c7b2b52d4d5d07b4dff11c19e545b05696 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:53:57 +0000 Subject: [PATCH 275/572] Add Bubbles Spawn Effect --- include/overworld_encounters.h | 1 + src/field_effect_helpers.c | 6 ++++++ src/overworld_encounters.c | 4 +++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 83190723e541..bd3b1594b9d8 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -44,6 +44,7 @@ enum OverworldEncounterSpawnAnim { OWE_SPAWN_ANIM_GRASS, OWE_SPAWN_ANIM_WATER, + OWE_SPAWN_ANIM_UNDERWATER, OWE_SPAWN_ANIM_CAVE, OWE_SPAWN_ANIM_SHINY, }; diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 0b9196b2baaa..b190d73d6434 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1536,6 +1536,12 @@ u32 FldEff_OWE_SpawnAnim(void) yOffset = 0; break; + case OWE_SPAWN_ANIM_UNDERWATER: + visual = FLDEFFOBJ_BUBBLES; + xOffset = 0; + yOffset = 0; + break; + case OWE_SPAWN_ANIM_CAVE: visual = FLDEFFOBJ_GROUND_IMPACT_DUST; xOffset = 0; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 30293c8717d6..a33d076435a5 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1241,8 +1241,10 @@ enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavio if (MetatileBehavior_IsPokeGrass(metatileBehavior) || MetatileBehavior_IsAshGrass(metatileBehavior)) return OWE_SPAWN_ANIM_GRASS; - else if (MetatileBehavior_IsSurfableFishableWater(metatileBehavior)) + else if (MetatileBehavior_IsSurfableFishableWater(metatileBehavior) && gMapHeader.mapType != MAP_TYPE_UNDERWATER) return OWE_SPAWN_ANIM_WATER; + else if (MetatileBehavior_IsSurfableFishableWater(metatileBehavior) && gMapHeader.mapType == MAP_TYPE_UNDERWATER) + return OWE_SPAWN_ANIM_UNDERWATER; else return OWE_SPAWN_ANIM_CAVE; } From 7ce8e41342b5bba01ba79ae1a6ee7147ee4918b9 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:56:25 +0000 Subject: [PATCH 276/572] Add Long Grass Spawn Type --- include/overworld_encounters.h | 1 + src/field_effect_helpers.c | 6 ++++++ src/overworld_encounters.c | 2 ++ 3 files changed, 9 insertions(+) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index bd3b1594b9d8..84b45587bc16 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -43,6 +43,7 @@ enum OverworldEncounterSpawnAnim { OWE_SPAWN_ANIM_GRASS, + OWE_SPAWN_ANIM_LONG_GRASS, OWE_SPAWN_ANIM_WATER, OWE_SPAWN_ANIM_UNDERWATER, OWE_SPAWN_ANIM_CAVE, diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index b190d73d6434..08ab31e08355 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1530,6 +1530,12 @@ u32 FldEff_OWE_SpawnAnim(void) yOffset = 8; break; + case OWE_SPAWN_ANIM_LONG_GRASS: + visual = FLDEFFOBJ_JUMP_LONG_GRASS; + xOffset = 0; + yOffset = 0; + break; + case OWE_SPAWN_ANIM_WATER: visual = FLDEFFOBJ_JUMP_BIG_SPLASH; xOffset = 0; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index a33d076435a5..9943a21fa248 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1241,6 +1241,8 @@ enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavio if (MetatileBehavior_IsPokeGrass(metatileBehavior) || MetatileBehavior_IsAshGrass(metatileBehavior)) return OWE_SPAWN_ANIM_GRASS; + else if (MetatileBehavior_IsLongGrass(metatileBehavior)) + return OWE_SPAWN_ANIM_LONG_GRASS; else if (MetatileBehavior_IsSurfableFishableWater(metatileBehavior) && gMapHeader.mapType != MAP_TYPE_UNDERWATER) return OWE_SPAWN_ANIM_WATER; else if (MetatileBehavior_IsSurfableFishableWater(metatileBehavior) && gMapHeader.mapType == MAP_TYPE_UNDERWATER) From 2b652d0a1ead4b9f3410f32f488c715bfa4c36ca Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:56:47 +0000 Subject: [PATCH 277/572] Remove Spawn Anim Comment --- src/overworld_encounters.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 9943a21fa248..effcfc3deb86 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1234,11 +1234,6 @@ void Task_OWE_WaitMovements(u8 taskId) enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior) { - // Need to edit anims: - // If object is on water then use water anim. - // If object is indoor, need an indoor anim? - // Long grass anim? - if (MetatileBehavior_IsPokeGrass(metatileBehavior) || MetatileBehavior_IsAshGrass(metatileBehavior)) return OWE_SPAWN_ANIM_GRASS; else if (MetatileBehavior_IsLongGrass(metatileBehavior)) From 19dc9a6afc2bb485f84458c2a55a23bbdf5736a4 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:56:57 +0000 Subject: [PATCH 278/572] Interaction Script Changes --- src/field_control_avatar.c | 10 +++++++++- src/overworld_encounters.c | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index b41e9a3692ff..94e3928e141c 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -564,8 +564,16 @@ static const u8 *GetInteractedMetatileScript(struct MapPosition *position, u8 me return NULL; } -static const u8 *GetInteractedWaterScript(struct MapPosition *unused1, u8 metatileBehavior, u8 direction) +static const u8 *GetInteractedWaterScript(struct MapPosition *position, u8 metatileBehavior, u8 direction) { + u8 objectEventId = GetObjectEventIdByPosition(position->x, position->y, 1); + struct ObjectEvent *object = &gObjectEvents[objectEventId]; + if (IsPlayerFacingSurfableFishableWater() == TRUE && IsOverworldWildEncounter(object)) + { + gSpecialVar_0x8004 = OW_SPECIES(object); + return InteractWithDynamicWildOverworldEncounter; + } + if (IsFieldMoveUnlocked(FIELD_MOVE_SURF) && PartyHasMonWithSurf() == TRUE && IsPlayerFacingSurfableFishableWater() == TRUE && CheckFollowerNPCFlag(FOLLOWER_NPC_FLAG_CAN_SURF) ) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index effcfc3deb86..49157371b0c9 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -942,7 +942,7 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) struct ObjectEvent *object = &gObjectEvents[objectEventId]; if (IsGeneratedOverworldWildEncounter(object) - || (IsManualOverworldWildEncounter(object) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL)) + || (!IsGeneratedOverworldWildEncounter(object) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL)) { gSpecialVar_0x8004 = OW_SPECIES(object); return TRUE; From 636bd0c7439b44c21818f6c0c5c9aa2036b9edce Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 28 Dec 2025 22:05:02 +0000 Subject: [PATCH 279/572] Remove Unneeded Line --- src/overworld_encounters.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 49157371b0c9..c1edcc4de885 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -26,7 +26,6 @@ #include "constants/songs.h" #include "constants/vars.h" #include "constants/wild_encounter.h" -// #include "data/wild_encounters.h" #define sOverworldEncounterLevel trainerRange_berryTreeId #define sAge playerCopyableMovement From cde8934c7c2b07544ef123109812cb75322ac460 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 28 Dec 2025 18:40:16 -0600 Subject: [PATCH 280/572] Use raw coords for AreCoordsInsidePlayerMap --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 30293c8717d6..e72ca6e9289b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -349,7 +349,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) elevation = MapGridGetElevationAt(x, y); - if (!AreCoordsInsidePlayerMap(x, y)) + if (!AreCoordsInsidePlayerMap(x - MAP_OFFSET, y - MAP_OFFSET)) return FALSE; // 0 is change of elevation, 15 is multiple elevation e.g. bridges From 91330102747bb623277fda41b099b262cba96098 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 28 Dec 2025 18:40:52 -0600 Subject: [PATCH 281/572] Fixed AreLegendariesInSootopolisPreventingEncounters check --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e72ca6e9289b..a54c87d25ace 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -103,7 +103,7 @@ void UpdateOverworldEncounters(void) if (!IsSafeToSpawnObjectEvents() || !TrySelectTile(&x, &y) || spawnSlot == INVALID_SPAWN_SLOT - || (shouldSpawnWaterMons && !AreLegendariesInSootopolisPreventingEncounters())) + || (shouldSpawnWaterMons && AreLegendariesInSootopolisPreventingEncounters())) { OWE_ResetSpawnCounterPlayAmbientCry(); return; From 5826f75515381bd94367945c9466ed82934e34b8 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 28 Dec 2025 20:51:39 -0600 Subject: [PATCH 282/572] Fixed MAP_OFFSET shenanigans --- src/overworld.c | 4 ++-- src/overworld_encounters.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/overworld.c b/src/overworld.c index f659684d58a1..6bb28aa3a1b0 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1492,8 +1492,8 @@ enum MapBattleScene GetCurrentMapBattleScene(void) bool32 AreCoordsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) { const struct MapLayout *layout = Overworld_GetMapHeaderByGroupAndId(mapGroup, mapNum)->mapLayout; - s32 width = layout->width - MAP_OFFSET; - s32 height = layout->height - MAP_OFFSET; + s32 width = layout->width + MAP_OFFSET; + s32 height = layout->height + MAP_OFFSET; if (x >= 0 && x < width && y >= 0 && y < height) return TRUE; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index a54c87d25ace..3641c22c09a9 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -349,7 +349,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) elevation = MapGridGetElevationAt(x, y); - if (!AreCoordsInsidePlayerMap(x - MAP_OFFSET, y - MAP_OFFSET)) + if (!AreCoordsInsidePlayerMap(x, y)) return FALSE; // 0 is change of elevation, 15 is multiple elevation e.g. bridges From 34ec8ad34430d3abbfa540146f5f1bc4d137c68c Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:08:17 -0600 Subject: [PATCH 283/572] Replaced magic number for personal sanity --- include/overworld_encounters.h | 4 +++- src/overworld_encounters.c | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 83190723e541..70d2111e2d46 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -36,7 +36,9 @@ #define OWE_DESPAWN_FRAMES 30 // Number of frames before a mon despawns after noticing the player (OWE_BEHAVIOR_DESPAWN) -#define INVALID_SPAWN_SLOT 0xFF +#define OWE_NO_ENCOUNTER_SET 255 + +#define INVALID_SPAWN_SLOT 0xFF #define OWE_FLAG_START_ENCOUNTER 0x8000 diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 3641c22c09a9..059d381aed22 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -80,14 +80,14 @@ void UpdateOverworldEncounters(void) || FlagGet(OW_FLAG_NO_ENCOUNTER) || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) { - if (sOWESpawnCountdown != 255) + if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) { RemoveAllGeneratedOverworldEncounterObjects(); - sOWESpawnCountdown = 255; + sOWESpawnCountdown = OWE_NO_ENCOUNTER_SET; } return; } - else if (sOWESpawnCountdown == 255) + else if (sOWESpawnCountdown == OWE_NO_ENCOUNTER_SET) { OverworldWildEncounter_SetMinimumSpawnTimer(); } From 4642ff9e8fbb567ef74c05df65e3b7d71f1ef383 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:26:06 -0600 Subject: [PATCH 284/572] Allow mons to spawn much more often when player is moving --- src/overworld_encounters.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 059d381aed22..825f6a87121e 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -98,12 +98,14 @@ void UpdateOverworldEncounters(void) return; } + if (!IsSafeToSpawnObjectEvents()) + return; + s16 x, y; u16 spawnSlot = NextSpawnMonSlot(); - if (!IsSafeToSpawnObjectEvents() - || !TrySelectTile(&x, &y) - || spawnSlot == INVALID_SPAWN_SLOT - || (shouldSpawnWaterMons && AreLegendariesInSootopolisPreventingEncounters())) + if (spawnSlot == INVALID_SPAWN_SLOT + || (shouldSpawnWaterMons && AreLegendariesInSootopolisPreventingEncounters()) + || !TrySelectTile(&x, &y)) { OWE_ResetSpawnCounterPlayAmbientCry(); return; From 2ff017eba10e1ff3a2ac23a2a83106e2c0ab4daa Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 29 Dec 2025 09:45:10 -0600 Subject: [PATCH 285/572] Fixed bit flag value to fit in u8 --- include/overworld_encounters.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 70d2111e2d46..36a9275bd8b0 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -40,7 +40,7 @@ #define INVALID_SPAWN_SLOT 0xFF -#define OWE_FLAG_START_ENCOUNTER 0x8000 +#define OWE_FLAG_START_ENCOUNTER 0x80 enum OverworldEncounterSpawnAnim { From 9be6f8711ed9fa9aea5d49ed3e3a1ed8d20d6e47 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 29 Dec 2025 13:20:26 -0600 Subject: [PATCH 286/572] Added more parameters to the default species behavior --- src/data/pokemon/overworld_encounter_behaviors.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/data/pokemon/overworld_encounter_behaviors.h b/src/data/pokemon/overworld_encounter_behaviors.h index a271b45c6026..9baf719f3673 100644 --- a/src/data/pokemon/overworld_encounter_behaviors.h +++ b/src/data/pokemon/overworld_encounter_behaviors.h @@ -9,6 +9,11 @@ static const struct OWESpeciesBehavior sOWESpeciesBehavior[OWE_SPECIES_BEHAVIOR_ [OWE_IGNORE_PLAYER] = { .movementType = MOVEMENT_TYPE_WANDER_AROUND_OWE, + .viewDistance = 4, + .viewWidth = 3, + .activeDistance = 5, + .idleSpeed = OWE_SPEED_NORMAL, + .activeSpeed = OWE_SPEED_NORMAL, }, [OWE_CHASE_PLAYER_SLOW] = From d097698ce9d38c1b4ab45654c8a6a4f9e26786b8 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 29 Dec 2025 16:37:00 -0600 Subject: [PATCH 287/572] Errors if OW_GFX_COMPRESS is TRUE --- include/overworld_encounters.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 36a9275bd8b0..0af1e69756e4 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -5,6 +5,10 @@ #error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OW_WILD_ENCOUNTERS_OVERWORLD to work." #endif +#if OW_GFX_COMPRESS == TRUE && OW_WILD_ENCOUNTERS_OVERWORLD == TRUE +#error "OW_GFX_COMPRESS needs to be FALSE in order for OW_WILD_ENCOUNTERS_OVERWORLD to work." +#endif + #define OWE_MAX_SPAWN_SLOTS 5 #define OWE_MAX_LAND_SPAWNS 3 From 3587bad3e62001e08f96bb577f92edc5c1893d5e Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 29 Dec 2025 18:15:43 -0600 Subject: [PATCH 288/572] Use bubbles for spawn anim underwater --- include/overworld_encounters.h | 1 + src/field_effect_helpers.c | 6 ++++++ src/overworld_encounters.c | 2 ++ 3 files changed, 9 insertions(+) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 0af1e69756e4..268182a6a2e8 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -50,6 +50,7 @@ enum OverworldEncounterSpawnAnim { OWE_SPAWN_ANIM_GRASS, OWE_SPAWN_ANIM_WATER, + OWE_SPAWN_ANIM_UNDERWATER, OWE_SPAWN_ANIM_CAVE, OWE_SPAWN_ANIM_SHINY, }; diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 0b9196b2baaa..4dae2ac107a2 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1541,6 +1541,12 @@ u32 FldEff_OWE_SpawnAnim(void) xOffset = 0; yOffset = 8; break; + + case OWE_SPAWN_ANIM_UNDERWATER: + visual = FLDEFFOBJ_BUBBLES; + xOffset = 0; + yOffset = 0; + break; case OWE_SPAWN_ANIM_SHINY: default: diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 825f6a87121e..d18c8e1bc343 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1245,6 +1245,8 @@ enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavio return OWE_SPAWN_ANIM_GRASS; else if (MetatileBehavior_IsSurfableFishableWater(metatileBehavior)) return OWE_SPAWN_ANIM_WATER; + else if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_UNDERWATER)) + return OWE_SPAWN_ANIM_UNDERWATER; else return OWE_SPAWN_ANIM_CAVE; } From abc18d4d5bafad3ae76cd2eeb928cb883329c7b7 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 29 Dec 2025 23:23:15 -0600 Subject: [PATCH 289/572] Mon movement no longer restricted by range values --- include/config/overworld.h | 2 -- src/event_object_movement.c | 6 ------ src/overworld_encounters.c | 2 -- 3 files changed, 10 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 2db4a02fd0d9..c8c38dcbfac7 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -78,8 +78,6 @@ #define OW_WILD_ENCOUNTERS_RESTRICT_MAP TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within their current map bounds. #define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. #define OW_WILD_ENCOUNTERS_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. -#define OW_ENCOUNTER_MOVEMENT_RANGE_X 8 // The horizontal movement range for OW encounter Pokémon. -#define OW_ENCOUNTER_MOVEMENT_RANGE_Y 8 // The vertical movement range for OW encounter Pokémon. #define OW_VANILLA_AMBIENT_CRIES TRUE // If TRUE, the ambient wild Pokémon cries will play on routes with encounter tables. #define OW_WILD_ENCOUNTERS_AMBIENT_CRIES TRUE // If TRUE, generated overworld encounters will play ambient cries based on their location relative to the player. #define OW_WILD_ENCOUNTERS_FEEBAS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 54e5329adc3c..364163c836c9 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -396,12 +396,6 @@ static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { [MOVEMENT_TYPE_COPY_PLAYER_OPPOSITE_IN_GRASS] = TRUE, [MOVEMENT_TYPE_COPY_PLAYER_COUNTERCLOCKWISE_IN_GRASS] = TRUE, [MOVEMENT_TYPE_COPY_PLAYER_CLOCKWISE_IN_GRASS] = TRUE, - [MOVEMENT_TYPE_WANDER_AROUND_OWE] = TRUE, - [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = TRUE, - [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = TRUE, - [MOVEMENT_TYPE_WATCH_PLAYER_OWE] = TRUE, - [MOVEMENT_TYPE_APPROACH_PLAYER_OWE] = TRUE, - [MOVEMENT_TYPE_DESPAWN_OWE] = TRUE, }; const u8 gInitialMovementTypeFacingDirections[NUM_MOVEMENT_TYPES] = { diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d18c8e1bc343..674e02048feb 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -150,8 +150,6 @@ void UpdateOverworldEncounters(void) } gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; - gObjectEvents[objectEventId].range.rangeX = OW_ENCOUNTER_MOVEMENT_RANGE_X; - gObjectEvents[objectEventId].range.rangeY = OW_ENCOUNTER_MOVEMENT_RANGE_Y; gObjectEvents[objectEventId].sOverworldEncounterLevel = level; gObjectEvents[objectEventId].sRoamerOutbreakStatus = indexRoamerOutbreak; From 9b450d33a8eebc3472541788f78893ca4f9aff41 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 29 Dec 2025 23:24:16 -0600 Subject: [PATCH 290/572] Doubled the spawn rate --- include/overworld_encounters.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 268182a6a2e8..1bf5818ed78e 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -23,9 +23,9 @@ #define OWE_SPAWN_RADUIS_WIDTH (OWE_TOTAL_SPAWN_WIDTH - 1) / 2 // Distance from center to left/right edge (not including center). #define OWE_SPAWN_RADUIS_HEIGHT (OWE_TOTAL_SPAWN_HEIGHT - 1) / 2 // Distance from center to top/bottom edge (not including center). -#define OWE_TIME_BETWEEN_SPAWNS 180 // Minimum wait time (in frames) between spawns. -#define OWE_SPAWN_TIME_VARIABILITY 60 // A random number of frames between 0 and this value will be added to OWE_TIME_BETWEEN_SPAWNS every reset for variability. -#define OWE_SPAWN_TIME_MINIMUM 60 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. +#define OWE_TIME_BETWEEN_SPAWNS 60 // Minimum wait time (in frames) between spawns. +#define OWE_SPAWN_TIME_VARIABILITY 30 // A random number of frames between 0 and this value will be added to OWE_TIME_BETWEEN_SPAWNS every reset for variability. +#define OWE_SPAWN_TIME_MINIMUM 30 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. #define OWE_MON_SIGHT_WIDTH 3 #define OWE_MON_SIGHT_LENGTH 4 From bd597bef29da16dbaad66ac2dec7e2364d3a3240 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 30 Dec 2025 15:59:48 -0600 Subject: [PATCH 291/572] Despawn oldest OWE only after no empty palette is found --- src/sprite.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/sprite.c b/src/sprite.c index fb2b6c0a15d8..9c8e7a3e0c0e 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -1593,19 +1593,20 @@ u32 LoadSpritePalette(const struct SpritePalette *palette) if (index != 0xFF) return index; - DespawnOldestOWE_Pal(); index = IndexOfSpritePaletteTag(TAG_NONE); if (index == 0xFF) { - return 0xFF; - } - else - { - sSpritePaletteTags[index] = palette->tag; - DoLoadSpritePalette(palette->data, PLTT_ID(index)); - return index; + DespawnOldestOWE_Pal(); + index = IndexOfSpritePaletteTag(TAG_NONE); + + if (index == 0xFF) + return 0xFF; } + + sSpritePaletteTags[index] = palette->tag; + DoLoadSpritePalette(palette->data, PLTT_ID(index)); + return index; } u32 LoadSpritePaletteWithTag(const u16 *pal, u16 tag) From ef09a7e0ea488f80e5c797d98837ee1890c23439 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 30 Dec 2025 16:34:33 -0600 Subject: [PATCH 292/572] No need to stop field effect on OWE destruction --- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 364163c836c9..47023ef12a9a 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11737,7 +11737,7 @@ bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim sp gFieldEffectArguments[1] = objEvent->currentCoords.y; gFieldEffectArguments[2] = gSprites[objEvent->spriteId].oam.priority + 1; gFieldEffectArguments[3] = spawnAnimType; - objEvent->fieldEffectSpriteId = FieldEffectStart(FLDEFF_OW_ENCOUNTER_SPAWN_ANIM); + FieldEffectStart(FLDEFF_OW_ENCOUNTER_SPAWN_ANIM); return TRUE; } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 674e02048feb..b61dcb089c31 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -909,14 +909,6 @@ u32 RemoveOldestOverworldEncounter(void) u32 localId = GetLocalIdByOverworldSpawnSlot(oldestSlot); u32 objectEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[objectEventId]; - u32 fldEffSpriteId = object->fieldEffectSpriteId; - - // Stop the associated field effect if it is active. - if (fldEffSpriteId != 0) - { - FieldEffectStop(&gSprites[fldEffSpriteId], FLDEFF_OW_ENCOUNTER_SPAWN_ANIM); - object->fieldEffectSpriteId = 0; - } RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); return objectEventId; From 3c86dbc8b170db06a87e57fe961a631cf162a927 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 1 Jan 2026 15:12:31 +0000 Subject: [PATCH 293/572] Merge Fixes --- src/field_effect_helpers.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index c60edbfdac98..08ab31e08355 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1553,12 +1553,6 @@ u32 FldEff_OWE_SpawnAnim(void) xOffset = 0; yOffset = 8; break; - - case OWE_SPAWN_ANIM_UNDERWATER: - visual = FLDEFFOBJ_BUBBLES; - xOffset = 0; - yOffset = 0; - break; case OWE_SPAWN_ANIM_SHINY: default: From e6fea0e4b0475619b4f4a0161ba17c980d260ecb Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 1 Jan 2026 15:13:34 +0000 Subject: [PATCH 294/572] Update TryGetObjectEventTemplateForOverworldEncounter --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 8 ++++---- src/overworld_encounters.c | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 60501faeb844..3b76b39eba57 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -120,7 +120,7 @@ bool32 CanRemoveOverworldEncounter(u32 localId); u32 RemoveOldestOverworldEncounter(void); void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 spawn); bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); -struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); +const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); void TryRemoveOverworldWildEncounter(u32 localId); bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 47023ef12a9a..b450c079e189 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1770,14 +1770,14 @@ u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemp const struct ObjectEventGraphicsInfo *graphicsInfo; const struct SubspriteTable *subspriteTables = NULL; // May be a good idea to move the if check contained by this function to outside it for clarity. - struct ObjectEventTemplate objectEventTemplateTemp = TryGetObjectEventTemplateForOverworldEncounter(objectEventTemplate); - u16 graphicsId = objectEventTemplateTemp.graphicsId; + const struct ObjectEventTemplate objectEventTemplateLocal = TryGetObjectEventTemplateForOverworldEncounter(objectEventTemplate); + u16 graphicsId = objectEventTemplateLocal.graphicsId; graphicsInfo = GetObjectEventGraphicsInfo(graphicsId); - CopyObjectGraphicsInfoToSpriteTemplate_WithMovementType(graphicsId, objectEventTemplateTemp.movementType, &spriteTemplate, &subspriteTables); + CopyObjectGraphicsInfoToSpriteTemplate_WithMovementType(graphicsId, objectEventTemplateLocal.movementType, &spriteTemplate, &subspriteTables); spriteFrameImage.size = graphicsInfo->size; spriteTemplate.images = &spriteFrameImage; - objectEventId = TrySetupObjectEventSprite(&objectEventTemplateTemp, &spriteTemplate, mapNum, mapGroup, cameraX, cameraY); + objectEventId = TrySetupObjectEventSprite(&objectEventTemplateLocal, &spriteTemplate, mapNum, mapGroup, cameraX, cameraY); if (objectEventId == OBJECT_EVENTS_COUNT) return OBJECT_EVENTS_COUNT; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 16ada2c4f135..263af6e3e29c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -942,7 +942,7 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) return FALSE; } -struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) +const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) { if (!IsSemiManualOverworldWildEncounter(template->graphicsId, template->trainerType)) return *template; @@ -981,6 +981,7 @@ struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const templateOWE.graphicsId = graphicsId; templateOWE.sOverworldEncounterLevel = level; templateOWE.trainerType = (TRAINER_TYPE_ENCOUNTER & 0xFF) | ((indexRoamerOutbreak & 0xFF) << 8); + templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); return templateOWE; } From f6b703f02f0e96e20501b3d53f4500c59f6e9f68 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 1 Jan 2026 15:16:01 +0000 Subject: [PATCH 295/572] Repel Prevents OWE Encounters --- src/overworld_encounters.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 263af6e3e29c..9a12c1e4d61a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -21,6 +21,7 @@ #include "constants/event_objects.h" #include "constants/field_effects.h" #include "constants/layouts.h" +#include "constants/item.h" #include "constants/map_types.h" #include "constants/trainer_types.h" #include "constants/songs.h" @@ -999,7 +1000,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c { // The only automatically interacts with an OW Encounter when; // Not using a repel or the DexNav is inactive. - if (/* VarGet(VAR_REPEL_STEP_COUNT) > 0 || */ FlagGet(DN_FLAG_SEARCHING)) + if (REPEL_STEP_COUNT || FlagGet(DN_FLAG_SEARCHING)) return; bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle)); From d143e63810ef282f470b3c3e022802e1bc2caa6b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 1 Jan 2026 15:18:43 +0000 Subject: [PATCH 296/572] Lures Halve Spawn Time Further --- src/overworld_encounters.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 9a12c1e4d61a..add1258bcec4 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -163,6 +163,8 @@ void UpdateOverworldEncounters(void) // Slower replacement spawning sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); + if (LURE_STEP_COUNT) + sOWESpawnCountdown /= 2; } static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny) @@ -606,6 +608,8 @@ u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, boo void OverworldWildEncounter_SetMinimumSpawnTimer(void) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + if (LURE_STEP_COUNT) + sOWESpawnCountdown /= 2; } static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) From 1e19f1b87cf14b6c78b49de8067a10c03ed2d32c Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 1 Jan 2026 15:32:37 +0000 Subject: [PATCH 297/572] Add Missing Script Check --- src/overworld_encounters.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index add1258bcec4..23a38b17827a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -937,6 +937,9 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) { struct ObjectEvent *object = &gObjectEvents[objectEventId]; + if (!IsOverworldWildEncounter(object)) + return FALSE; + if (IsGeneratedOverworldWildEncounter(object) || (!IsGeneratedOverworldWildEncounter(object) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL)) { From feab26bb445e0ed38b5ca8e2ff1541738aefc2bd Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 1 Jan 2026 15:43:42 +0000 Subject: [PATCH 298/572] Update ComputePlayerShinyOdds --- src/pokemon.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pokemon.c b/src/pokemon.c index fcc82694198b..f1bc9f225af8 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -51,8 +51,6 @@ #include "text.h" #include "trainer_hill.h" #include "util.h" -#include "wild_encounter.h" -#include "config/fishing.h" #include "constants/abilities.h" #include "constants/battle_frontier.h" #include "constants/battle_move_effects.h" @@ -1068,8 +1066,7 @@ bool32 ComputePlayerShinyOdds(u32 personality) totalRerolls += I_SHINY_CHARM_ADDITIONAL_ROLLS; if (LURE_STEP_COUNT != 0) totalRerolls += 1; - if (I_FISHING_CHAIN && gIsFishingEncounter) - totalRerolls += CalculateChainFishingShinyRolls(); + totalRerolls += CalculateChainFishingShinyRolls(); if (gDexNavSpecies) totalRerolls += CalculateDexNavShinyRolls(); From caef40f344e4d21ac63cd8bfabff3cf4eabc98f0 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 1 Jan 2026 15:51:59 +0000 Subject: [PATCH 299/572] Edit Water Spawn Anim Offset --- src/field_effect_helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 08ab31e08355..4679f2585795 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1539,7 +1539,7 @@ u32 FldEff_OWE_SpawnAnim(void) case OWE_SPAWN_ANIM_WATER: visual = FLDEFFOBJ_JUMP_BIG_SPLASH; xOffset = 0; - yOffset = 0; + yOffset = 8; break; case OWE_SPAWN_ANIM_UNDERWATER: From e1e10d83d678a08eaddb37dc337fff45302cb2bf Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 1 Jan 2026 16:06:36 +0000 Subject: [PATCH 300/572] Properly Check Abilities Allowing Encounters --- include/wild_encounter.h | 1 + src/overworld_encounters.c | 6 +++--- src/wild_encounter.c | 3 +-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/wild_encounter.h b/include/wild_encounter.h index a6655cd44299..7c38256aea05 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -64,6 +64,7 @@ u16 GetLocalWildMon(bool8 *isWaterMon); u16 GetLocalWaterMon(void); bool8 UpdateRepelCounter(void); bool32 IsWildLevelAllowedByRepel(u8 wildLevel); +bool32 IsAbilityAllowingEncounter(u8 level); bool8 TryDoDoubleWildBattle(void); bool8 StandardWildEncounter_Debug(void); u32 CalculateChainFishingShinyRolls(void); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 23a38b17827a..894987a9ded9 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -119,7 +119,7 @@ void UpdateOverworldEncounters(void) u32 level; u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); - if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(level)) + if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(level) || !IsAbilityAllowingEncounter(level)) { OWE_ResetSpawnCounterPlayAmbientCry(); return; @@ -728,10 +728,10 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam } else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior)) { - SetUpMassOutbreakEncounter(WILD_CHECK_KEEN_EYE); + SetUpMassOutbreakEncounter(0); *indexRoamerOutbreak = OWE_MASS_OUTBREAK_INDEX; } - else if (!TryGenerateWildMon(wildMonInfo, wildArea, WILD_CHECK_KEEN_EYE)) + else if (!TryGenerateWildMon(wildMonInfo, wildArea, 0)) { return FALSE; } diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 1823f3576293..1070632ca2f9 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -55,7 +55,6 @@ static bool8 TryGetAbilityInfluencedWildMonIndex(const struct WildPokemon *wildM #else static bool8 TryGetAbilityInfluencedWildMonIndex(const struct WildPokemon *wildMon, enum Type type, enum Ability ability, u8 *monIndex); #endif -static bool8 IsAbilityAllowingEncounter(u8 level); EWRAM_DATA static u8 sWildEncountersDisabled = 0; EWRAM_DATA static u32 sFeebasRngValue = 0; @@ -1103,7 +1102,7 @@ bool32 IsWildLevelAllowedByRepel(u8 wildLevel) return FALSE; } -static bool8 IsAbilityAllowingEncounter(u8 level) +bool32 IsAbilityAllowingEncounter(u8 level) { enum Ability ability; From 5cc09e8736b5de499bcac9d9edc09d7173f02e65 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 1 Jan 2026 16:07:50 +0000 Subject: [PATCH 301/572] Remove Comments --- src/event_object_movement.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index b450c079e189..19e56b3b3569 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -6465,7 +6465,7 @@ u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, b { curObject = &gObjectEvents[i]; if (curObject->active && (curObject->movementType != MOVEMENT_TYPE_FOLLOW_PLAYER || objectEvent != &gObjectEvents[gPlayerAvatar.objectEventId]) && curObject != objectEvent - && !FollowerNPC_IsCollisionExempt(curObject, objectEvent) // Partner + && !FollowerNPC_IsCollisionExempt(curObject, objectEvent) ) { // check for collision if curObject is active, not the object in question, and not exempt from collisions @@ -6473,8 +6473,6 @@ u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, b { if (AreElevationsCompatible(objectEvent->currentElevation, curObject->currentElevation)) { - // check if objects can actually collide with this or if it returns no too early - // should be fine OWE_TryTriggerEncounter(objectEvent, curObject); return i; } From 24f91132d30558316a9eb232bbadd40905a064fa Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 1 Jan 2026 16:10:04 +0000 Subject: [PATCH 302/572] Cleanup GetMaxOverworldEncounterSpawns --- src/overworld_encounters.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 894987a9ded9..c619dd54dd96 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -61,6 +61,7 @@ static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); static bool32 CreateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); +static u32 GetMaxOverworldEncounterSpawns(void); void OWE_ResetSpawnCounterPlayAmbientCry(void) { @@ -218,7 +219,7 @@ void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 spawn) MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); } -static u8 GetMaxOverworldEncounterSpawns(void) +static u32 GetMaxOverworldEncounterSpawns(void) { if (OWE_ShouldSpawnWaterMons()) return OWE_MAX_WATER_SPAWNS; From ea861706a82de13d8b8488e2476cb15308235998 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 1 Jan 2026 18:02:26 +0000 Subject: [PATCH 303/572] Remove Roamers/Outbreaks from Semi-Manual and Manual Encounters --- src/overworld_encounters.c | 81 ++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c619dd54dd96..c86813d6496a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -31,8 +31,8 @@ #define sOverworldEncounterLevel trainerRange_berryTreeId #define sAge playerCopyableMovement #define sRoamerOutbreakStatus directionSequenceIndex -#define OWE_NON_ROAMER_OUTBREAK ROAMER_COUNT // If less than this value, OWE is a roamer. -#define OWE_MASS_OUTBREAK_INDEX OWE_NON_ROAMER_OUTBREAK + 1 +#define OWE_NON_ROAMER_OUTBREAK 0 +#define OWE_MASS_OUTBREAK_INDEX ROAMER_COUNT + 1 static EWRAM_DATA u8 sOWESpawnCountdown = 0; @@ -62,6 +62,8 @@ static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutb static bool32 CreateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); static u32 GetMaxOverworldEncounterSpawns(void); +static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); +static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); void OWE_ResetSpawnCounterPlayAmbientCry(void) { @@ -383,9 +385,9 @@ void CreateOverworldWildEncounter(void) { u32 localId = gSpecialVar_LastTalked; u32 objEventId = GetObjectEventIdByLocalId(localId); - u32 indexRoamerOutbreak; u32 headerId = GetCurrentMapWildMonHeaderId(); struct ObjectEvent *object = &gObjectEvents[objEventId]; + u32 indexRoamerOutbreak = object->sRoamerOutbreakStatus; if (objEventId >= OBJECT_EVENTS_COUNT) return; @@ -393,8 +395,7 @@ void CreateOverworldWildEncounter(void) if (!IsOverworldWildEncounter(object)) return; - indexRoamerOutbreak = object->sRoamerOutbreakStatus; - if (CreateOverworldWildEncounter_CheckRoamer(indexRoamerOutbreak)) + if (indexRoamerOutbreak && CreateOverworldWildEncounter_CheckRoamer(OWE_GetObjectRoamerOutbreakStatus(object))) return; u16 speciesId = OW_SPECIES(object); @@ -433,7 +434,8 @@ void CreateOverworldWildEncounter(void) static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak) { - if (indexRoamerOutbreak < OWE_NON_ROAMER_OUTBREAK && IsRoamerAt(indexRoamerOutbreak, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) + if (indexRoamerOutbreak < ROAMER_COUNT + && IsRoamerAt(indexRoamerOutbreak, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) { CreateRoamerMonInstance(indexRoamerOutbreak); gEncounteredRoamerIndex = indexRoamerOutbreak; @@ -467,9 +469,10 @@ static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId) static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId) { - if (indexRoamerOutbreak == OWE_MASS_OUTBREAK_INDEX && gSaveBlock1Ptr->outbreakPokemonSpecies == speciesId - && gSaveBlock1Ptr->location.mapNum == gSaveBlock1Ptr->outbreakLocationMapNum - && gSaveBlock1Ptr->location.mapGroup == gSaveBlock1Ptr->outbreakLocationMapGroup) + if (indexRoamerOutbreak == OWE_MASS_OUTBREAK_INDEX + && gSaveBlock1Ptr->outbreakPokemonSpecies == speciesId + && gSaveBlock1Ptr->location.mapNum == gSaveBlock1Ptr->outbreakLocationMapNum + && gSaveBlock1Ptr->location.mapGroup == gSaveBlock1Ptr->outbreakLocationMapGroup) { for (u32 i = 0; i < MAX_MON_MOVES; i++) SetMonMoveSlot(&gEnemyParty[0], gSaveBlock1Ptr->outbreakPokemonMoves[i], i); @@ -689,37 +692,19 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam /* These functions perform checks of various encounter types in the following order: - 1. Manual/Semi-Manual OWE Defined Roamer Encounter - 2. Manual/Semi-Manual OWE Defined Mass Outbreak Encounter - 3. Attempted Generated Roamer Encounter - 4. Attempted Generated Feebas encounter - 5. Attempted Generated Mass Outbreak Encounter - 6. Attempted Generated Standard Wild Encounter generation - Note: While a player cannot be stopped from trying to trigger checks 1 and 2, it is not recommended. - This is due to the fact that it requires careful tracking of the Roamer/Mass Outbreak in the save. - Regardless of this, 'functionality' is provided mostly to prevent breakages if the player does so, - accidentally. + 1. Attempted Generated Roamer Encounter + 2. Attempted Generated Feebas Encounter + 3. Attempted Generated Mass Outbreak Encounter + 4. Attempted Generated Standard Wild Encounter generation The structure of this statement ensures that only one of these encounter types can succeed per call, with the resultant wild mon being created in gEnemyParty[0]. If none of these checks succeed, speciesId is set to SPECIES_NONE and FALSE is returned. */ - // REGARDING ABOVE: A sanitisation check to clean the top 8 bits of trainerType when it is read from templates would prevent it, but remove 'functionality'. - - if (*indexRoamerOutbreak < OWE_NON_ROAMER_OUTBREAK && IsRoamerAt(*indexRoamerOutbreak, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) - { - CreateRoamerMonInstance(*indexRoamerOutbreak); - } - else if (*indexRoamerOutbreak == OWE_MASS_OUTBREAK_INDEX && gSaveBlock1Ptr->outbreakPokemonSpecies != SPECIES_NONE - && gSaveBlock1Ptr->location.mapNum == gSaveBlock1Ptr->outbreakLocationMapNum - && gSaveBlock1Ptr->location.mapGroup == gSaveBlock1Ptr->outbreakLocationMapGroup) - { - SetUpMassOutbreakEncounter(0); - } - else if (TryStartRoamerEncounter() && !OWE_DoesRoamerObjectExist()) + if (TryStartRoamerEncounter() && !OWE_DoesRoamerObjectExist()) { - *indexRoamerOutbreak = gEncounteredRoamerIndex; + *indexRoamerOutbreak = OWE_GetObjectRoamerStatusFromIndex(gEncounteredRoamerIndex); } else if (OW_WILD_ENCOUNTERS_FEEBAS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) { @@ -965,10 +950,6 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( bool32 isFemale = FALSE; u32 level; u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; - u32 storedRoamer = (template->trainerType >> 8) & 0xFF; - - if (storedRoamer) - indexRoamerOutbreak = storedRoamer; SetOverworldEncounterSpeciesInfo( template->x - MAP_OFFSET, @@ -989,7 +970,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( templateOWE.graphicsId = graphicsId; templateOWE.sOverworldEncounterLevel = level; - templateOWE.trainerType = (TRAINER_TYPE_ENCOUNTER & 0xFF) | ((indexRoamerOutbreak & 0xFF) << 8); + templateOWE.trainerType = TRAINER_TYPE_ENCOUNTER; templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); return templateOWE; } @@ -1310,7 +1291,7 @@ static bool32 OWE_DoesRoamerObjectExist(void) for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { struct ObjectEvent *object = &gObjectEvents[i]; - if (IsOverworldWildEncounter(object) && object->sRoamerOutbreakStatus == gEncounteredRoamerIndex) + if (IsOverworldWildEncounter(object) && OWE_GetObjectRoamerOutbreakStatus(object) == gEncounteredRoamerIndex) return TRUE; } @@ -1386,6 +1367,28 @@ void OverworldWildEncounter_FreezeAllObjects(void) } } +static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index) +{ + if (index < ROAMER_COUNT) + return index + 1; + + return index; +} + +static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) +{ + if (!IsOverworldWildEncounter(objectEvent)) + return OWE_NON_ROAMER_OUTBREAK; + + u32 status = objectEvent->sRoamerOutbreakStatus; + if (status == OWE_NON_ROAMER_OUTBREAK || status == OWE_MASS_OUTBREAK_INDEX) + { + return status; + } + + return status - 1; +} + #undef tLocalId #undef NOT_STARTED #undef STARTED From 8ec6453f12c136eb5e7d9b2a6a2221448338bd7d Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 4 Jan 2026 19:59:02 +0000 Subject: [PATCH 304/572] Add OverworldWildEncounter_IsStartingWildEncounter Wrapper --- include/overworld_encounters.h | 1 + src/event_object_movement.c | 28 +++++++++------------------- src/overworld_encounters.c | 5 +++++ 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 3b76b39eba57..45285f1da38e 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -134,5 +134,6 @@ void Task_OWE_WaitMovements(u8 taskId); enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); void OverworldWildEncounter_InitRoamerOutbreakStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template); void OverworldWildEncounter_FreezeAllObjects(void); +bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *objectEvent); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 19e56b3b3569..d917bc20354b 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11746,7 +11746,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent if (!ObjectEventExecSingleMovementAction(objectEvent, sprite)) return FALSE; - if (objectEvent->trainerRange_berryTreeId & OWE_FLAG_START_ENCOUNTER) + if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) { u16 speciesId = OW_SPECIES(objectEvent); u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); @@ -11956,11 +11956,9 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent * } u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - - if (!(objectEvent->trainerRange_berryTreeId & OWE_FLAG_START_ENCOUNTER)) - { + if (!OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) direction = GetOppositeDirection(direction); - } + SetObjectEventDirection(objectEvent, direction); sprite->sTypeFuncId = 11; return TRUE; @@ -11979,11 +11977,9 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - - if (!(objectEvent->trainerRange_berryTreeId & OWE_FLAG_START_ENCOUNTER) && !(objectEvent->currentCoords.x == player->currentCoords.x || objectEvent->currentCoords.y == player->currentCoords.y)) - { + if (!OverworldWildEncounter_IsStartingWildEncounter(objectEvent) && objectEvent->currentCoords.x != player->currentCoords.x && objectEvent->currentCoords.y != player->currentCoords.y) newDirection = GetOppositeDirection(newDirection); - } + movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); collision = GetCollisionInDirection(objectEvent, newDirection); @@ -12089,31 +12085,25 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve bool8 collision; u8 movementActionId; - if (distance <= 1 && !(objectEvent->trainerRange_berryTreeId & OWE_FLAG_START_ENCOUNTER)) + if (distance <= 1 && !OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) { SetObjectEventDirection(objectEvent, GetOppositeDirection(objectEvent->movementDirection)); - collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - - if (!(objectEvent->trainerRange_berryTreeId & OWE_FLAG_START_ENCOUNTER) && !(objectEvent->currentCoords.x == player->currentCoords.x || objectEvent->currentCoords.y == player->currentCoords.y)) - { + if (!OverworldWildEncounter_IsStartingWildEncounter(objectEvent) && objectEvent->currentCoords.x != player->currentCoords.x && objectEvent->currentCoords.y != player->currentCoords.y) newDirection = GetOppositeDirection(newDirection); - } + movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); collision = GetCollisionInDirection(objectEvent, newDirection); - if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) - { movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); - } } } - else if (distance == OWE_APPROACH_DISTANCE && !equalDistances && !(objectEvent->trainerRange_berryTreeId & OWE_FLAG_START_ENCOUNTER)) + else if (distance == OWE_APPROACH_DISTANCE && !equalDistances && !OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) { if (sJumpTimer <= 0) { diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c86813d6496a..5fc1360442e6 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1389,6 +1389,11 @@ static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) return status - 1; } +bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *objectEvent) +{ + return objectEvent->sOverworldEncounterLevel & OWE_FLAG_START_ENCOUNTER; +} + #undef tLocalId #undef NOT_STARTED #undef STARTED From fd395b62d5c4f4655f634a0e2193594a88305d68 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 4 Jan 2026 20:02:10 +0000 Subject: [PATCH 305/572] Allow OWEs unrestricted access when encounter begins --- src/overworld_encounters.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5fc1360442e6..d4a41edaa7a8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1020,6 +1020,9 @@ void TryRemoveOverworldWildEncounter(u32 localId) bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction) { + if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) + return FALSE; + // Returns TRUE if movement is restricted. return ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatile(objectEvent, direction)) || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMap(objectEvent, direction))); From 7c3fb3571c18beab2bdc9bd4b4f8e7214866e209 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:33:21 +0000 Subject: [PATCH 306/572] Edit Lure Logic --- src/overworld_encounters.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d4a41edaa7a8..d95bfd030138 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -95,7 +95,13 @@ void UpdateOverworldEncounters(void) OverworldWildEncounter_SetMinimumSpawnTimer(); } - if (sOWESpawnCountdown != 0) + u16 spawnSlot = NextSpawnMonSlot(); + if (LURE_STEP_COUNT && spawnSlot != INVALID_SPAWN_SLOT + && (GetNumActiveGeneratedOverworldEncounters() < GetMaxOverworldEncounterSpawns())) + { + OverworldWildEncounter_SetMinimumSpawnTimer(); + } + else if (sOWESpawnCountdown) { sOWESpawnCountdown--; return; @@ -105,7 +111,6 @@ void UpdateOverworldEncounters(void) return; s16 x, y; - u16 spawnSlot = NextSpawnMonSlot(); if (spawnSlot == INVALID_SPAWN_SLOT || (shouldSpawnWaterMons && AreLegendariesInSootopolisPreventingEncounters()) || !TrySelectTile(&x, &y)) @@ -166,8 +171,6 @@ void UpdateOverworldEncounters(void) // Slower replacement spawning sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); - if (LURE_STEP_COUNT) - sOWESpawnCountdown /= 2; } static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny) @@ -612,8 +615,6 @@ u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, boo void OverworldWildEncounter_SetMinimumSpawnTimer(void) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - if (LURE_STEP_COUNT) - sOWESpawnCountdown /= 2; } static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) From 0f8a7827455d42b60f70e1771736356955855d08 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:59:55 +0000 Subject: [PATCH 307/572] Fix Interaction Of Water OWEs When on Land --- src/field_control_avatar.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 94e3928e141c..7b1be910fbf0 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -566,11 +566,14 @@ static const u8 *GetInteractedMetatileScript(struct MapPosition *position, u8 me static const u8 *GetInteractedWaterScript(struct MapPosition *position, u8 metatileBehavior, u8 direction) { + // Does this need a define for the surf elevation (1) check? + // Can be used in sElevationToSubpriority and other places too u8 objectEventId = GetObjectEventIdByPosition(position->x, position->y, 1); - struct ObjectEvent *object = &gObjectEvents[objectEventId]; - if (IsPlayerFacingSurfableFishableWater() == TRUE && IsOverworldWildEncounter(object)) + if (IsPlayerFacingSurfableFishableWater() == TRUE && ShouldRunOverworldEncounterScript(objectEventId)) { + struct ObjectEvent *object = &gObjectEvents[objectEventId]; gSpecialVar_0x8004 = OW_SPECIES(object); + gSpecialVar_LastTalked = object->localId; return InteractWithDynamicWildOverworldEncounter; } From fb2eb8f2dd3a21f67e0df316ab14a16326830061 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:29:50 +0000 Subject: [PATCH 308/572] Edit Should ShouldRunOverworldEncounterScript --- src/overworld_encounters.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d95bfd030138..6c4645712f6e 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -928,7 +928,9 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) return FALSE; if (IsGeneratedOverworldWildEncounter(object) - || (!IsGeneratedOverworldWildEncounter(object) && GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL)) + || (!IsGeneratedOverworldWildEncounter(object) + && (GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL + || GetObjectEventScriptPointerByObjectEventId(objectEventId) == InteractWithDynamicWildOverworldEncounter))) { gSpecialVar_0x8004 = OW_SPECIES(object); return TRUE; From 825d8a635480fff8917fe5ec99f62db5fc1ae133 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:30:03 +0000 Subject: [PATCH 309/572] Fix Coords Used in TryGetObjectEventTemplateForOverworldEncounter --- src/overworld_encounters.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 6c4645712f6e..67f10d3b6ff2 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -955,8 +955,8 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; SetOverworldEncounterSpeciesInfo( - template->x - MAP_OFFSET, - template->y - MAP_OFFSET, + template->x, + template->y, &speciesId, &isShiny, &isFemale, From fdec4f906e192b9064dd9bcf897789457e8f38f7 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:37:16 +0000 Subject: [PATCH 310/572] An idea --- src/overworld_encounters.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 67f10d3b6ff2..c3a08a993b9c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -322,6 +322,8 @@ static bool8 TrySelectTile(s16* outX, s16* outY) // Select a random tile in [-7, -4] [7, 4] range // Make sure is not directly next to player + // Can we make get random tile its own function for use elsewhere in the codebase? + // Have defines used and then replace MAP_METATILE_VIEW_X/Y with them do { x = (s16)(Random() % OWE_TOTAL_SPAWN_WIDTH) - OWE_SPAWN_RADUIS_WIDTH; From c33d7ba23a09477bcad1b2de47caa0009e841b74 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:39:47 +0000 Subject: [PATCH 311/572] An issue --- src/overworld_encounters.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c3a08a993b9c..07539451be3b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1238,6 +1238,8 @@ enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavio static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) { + // Uses slots so needs to be generated encounters only. + // Or needs to change functionality to count all active encounters. u32 numActive = GetNumActiveOverworldEncounters(); u32 randomIndex; struct ObjectEvent *slotMon; From a8ef1cdf6d47465c45d024677712df82e84bf930 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 12:20:06 +0000 Subject: [PATCH 312/572] Remove OWE Support from Battle Frontier --- include/overworld_encounters.h | 1 + src/field_control_avatar.c | 2 +- src/overworld_encounters.c | 75 ++++++---------------------------- 3 files changed, 14 insertions(+), 64 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 45285f1da38e..91d17416c479 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -135,5 +135,6 @@ enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavio void OverworldWildEncounter_InitRoamerOutbreakStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template); void OverworldWildEncounter_FreezeAllObjects(void); bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *objectEvent); +bool32 OverworldWildEncounter_ShouldEnableRandomBattleFrontierSpawns(void); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 7b1be910fbf0..4c5326ebf52d 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -828,7 +828,7 @@ void RestartWildEncounterImmunitySteps(void) static bool8 CheckStandardWildEncounter(u16 metatileBehavior) { - if (FlagGet(OW_FLAG_NO_ENCOUNTER) || OW_WILD_ENCOUNTERS_RANDOM == FALSE) + if (FlagGet(OW_FLAG_NO_ENCOUNTER) || (!OW_WILD_ENCOUNTERS_RANDOM && OverworldWildEncounter_ShouldEnableRandomBattleFrontierSpawns())) return FALSE; if (sWildEncounterImmunitySteps < 4) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 07539451be3b..d3e7ce2ea101 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -17,6 +17,7 @@ #include "sprite.h" #include "sound.h" #include "task.h" +#include "trainer_hill.h" #include "wild_encounter.h" #include "constants/event_objects.h" #include "constants/field_effects.h" @@ -57,7 +58,6 @@ static u32 GetNumActiveOverworldEncounters(void); static u32 GetNumActiveGeneratedOverworldEncounters(void); static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); -static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); static bool32 CreateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); @@ -81,7 +81,10 @@ void UpdateOverworldEncounters(void) bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER) - || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) + || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons) + || CurrentBattlePyramidLocation() + || InBattlePike() + || InTrainerHillChallenge()) { if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) { @@ -424,9 +427,6 @@ void CreateOverworldWildEncounter(void) isFemale ); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); - - if (CreateOverworldWildEncounter_CheckBattleFrontier(headerId)) - return; if (CreateOverworldWildEncounter_CheckMassOutbreak(indexRoamerOutbreak, speciesId)) return; @@ -451,27 +451,6 @@ static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak) return FALSE; } -static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId) -{ - if (headerId == HEADER_NONE) - { - if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) - { - TryGenerateBattlePikeWildMon(FALSE); - BattleSetup_StartBattlePikeWildBattle(); - return TRUE; - } - if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) - { - GenerateBattlePyramidWildMon(); - BattleSetup_StartWildBattle(); - return TRUE; - } - } - - return FALSE; -} - static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId) { if (indexRoamerOutbreak == OWE_MASS_OUTBREAK_INDEX @@ -655,30 +634,7 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam u32 metatileBehavior = MapGridGetMetatileBehaviorAt(x, y); if (headerId == HEADER_NONE) - { - if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) - { - headerId = GetBattlePikeWildMonHeaderId(); - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) - return FALSE; - - TryGenerateBattlePikeWildMon(FALSE); - return TRUE; - } - if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) - { - headerId = gSaveBlock2Ptr->frontier.curChallengeBattleNum; - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) - return FALSE; - - GenerateBattlePyramidWildMon(); - return TRUE; - } - return FALSE; - } if (MetatileBehavior_IsWaterWildEncounter(metatileBehavior)) { @@ -794,21 +750,7 @@ static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) enum TimeOfDay timeOfDay; if (headerId == HEADER_NONE) - { - if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) - { - headerId = GetBattlePikeWildMonHeaderId(); - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - return gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; - } - if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) - { - headerId = gSaveBlock2Ptr->frontier.curChallengeBattleNum; - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - return gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; - } return FALSE; - } if (shouldSpawnWaterMons) { @@ -1404,6 +1346,13 @@ bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *object return objectEvent->sOverworldEncounterLevel & OWE_FLAG_START_ENCOUNTER; } +bool32 OverworldWildEncounter_ShouldEnableRandomBattleFrontierSpawns(void) +{ + return ((gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS + || gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + && !OW_WILD_ENCOUNTERS_RANDOM && OW_WILD_ENCOUNTERS_OVERWORLD); +} + #undef tLocalId #undef NOT_STARTED #undef STARTED From b902b4cb8865ad8c52a0dcde0707d007ef85e8cb Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:31:35 +0000 Subject: [PATCH 313/572] Remove Excess Includes --- src/wild_encounter.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 1070632ca2f9..844089794b49 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -29,9 +29,6 @@ #include "constants/layouts.h" #include "constants/weather.h" -#include "pokemon.h" -#include "random.h" - extern const u8 EventScript_SprayWoreOff[]; #define MAX_ENCOUNTER_RATE 2880 From efa2988c48d4815e75ed4346d9d8b5ebe8324063 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:32:39 +0000 Subject: [PATCH 314/572] Remove Whitespace --- src/load_save.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/load_save.c b/src/load_save.c index 1ad1f4abce1e..ac900aa6e164 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -233,7 +233,6 @@ void LoadObjectEvents(void) if (gObjectEvents[i].spriteId != 127) gObjectEvents[i].graphicsId &= 0xFF; gObjectEvents[i].spriteId = 0; - // Try to restore saved inactive follower if (gObjectEvents[i].localId == OBJ_EVENT_ID_FOLLOWER && !gObjectEvents[i].active && From 4270fa68a4b189c45c7c40c6f5c774c96d3f9122 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:39:32 +0000 Subject: [PATCH 315/572] Reorder GetInteractedObjectEventScript --- src/field_control_avatar.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 4c5326ebf52d..2478a3092b89 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -400,12 +400,12 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; gSpecialVar_Facing = direction; - if (ShouldRunOverworldEncounterScript(objectEventId)) - script = InteractWithDynamicWildOverworldEncounter; - else if (InTrainerHill() == TRUE) + if (InTrainerHill() == TRUE) script = GetTrainerHillTrainerScript(); else if (PlayerHasFollowerNPC() && objectEventId == GetFollowerNPCObjectId()) script = GetFollowerNPCScriptPointer(); + else if (ShouldRunOverworldEncounterScript(objectEventId)) + script = InteractWithDynamicWildOverworldEncounter; else script = GetObjectEventScriptPointerByObjectEventId(objectEventId); From dc323145ce52efd5ab0093aece5b429f6e9cf649 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:16:14 +0000 Subject: [PATCH 316/572] Only Set Semi Manual OWE Movement Type if doesn't have one --- src/overworld_encounters.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d3e7ce2ea101..2d0eee2076de 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -918,7 +918,9 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( templateOWE.graphicsId = graphicsId; templateOWE.sOverworldEncounterLevel = level; templateOWE.trainerType = TRAINER_TYPE_ENCOUNTER; - templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); + if (templateOWE.movementType == MOVEMENT_TYPE_NONE) + templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); + return templateOWE; } From 89e94698c80352d0f80eab96422dee492b91c515 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:35:08 +0000 Subject: [PATCH 317/572] Use OBJ_EVENT_GFX_OW_MON instead of OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER --- include/constants/event_objects.h | 3 +-- src/data/object_events/object_event_graphics_info_pointers.h | 1 - src/overworld_encounters.c | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 98c0942ad2f9..fb05a98efdde 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -247,12 +247,11 @@ #define OBJ_EVENT_GFX_OW_MON 240 #define OBJ_EVENT_GFX_LIGHT_SPRITE 241 #define OBJ_EVENT_GFX_APRICORN_TREE 242 -#define OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER 243 // NOTE: The maximum amount of object events has been expanded from 255 to 65535. // Since dynamic graphics ids still require at least 16 free values, the actual limit // is 65519, but even considering follower Pokémon, this should be more than enough :) -#define NUM_OBJ_EVENT_GFX 244 +#define NUM_OBJ_EVENT_GFX 243 // These are dynamic object gfx ids. diff --git a/src/data/object_events/object_event_graphics_info_pointers.h b/src/data/object_events/object_event_graphics_info_pointers.h index ce154e94b51b..a239bce99317 100755 --- a/src/data/object_events/object_event_graphics_info_pointers.h +++ b/src/data/object_events/object_event_graphics_info_pointers.h @@ -494,7 +494,6 @@ const struct ObjectEventGraphicsInfo *const gObjectEventGraphicsInfoPointers[NUM [OBJ_EVENT_GFX_OW_MON] = &gObjectEventGraphicsInfo_Follower, [OBJ_EVENT_GFX_LIGHT_SPRITE] = &gObjectEventGraphicsInfo_BallLight, [OBJ_EVENT_GFX_APRICORN_TREE] = &gObjectEventGraphicsInfo_ApricornTree, - [OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER] = &gObjectEventGraphicsInfo_Follower, }; const struct ObjectEventGraphicsInfo *const gMauvilleOldManGraphicsInfoPointers[] = { diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 2d0eee2076de..b4fda14a0c42 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -783,7 +783,7 @@ bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent) bool32 IsSemiManualOverworldWildEncounter(u32 graphicsId, u32 trainerType) { - return graphicsId == OBJ_EVENT_GFX_OVERWORLD_ENCOUNTER && trainerType == TRAINER_TYPE_ENCOUNTER; + return graphicsId == OBJ_EVENT_GFX_OW_MON && trainerType == TRAINER_TYPE_ENCOUNTER; } static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) From c63d26fd7326596cde19712f3f968b2efa2d3abf Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 23:28:53 +0000 Subject: [PATCH 318/572] Update OverworldWildEncounter_RemoveObjectOnBattle --- include/overworld_encounters.h | 2 +- src/battle_setup.c | 2 +- src/overworld_encounters.c | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 91d17416c479..d0fc32566f3c 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -122,7 +122,7 @@ void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 spawn); bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); -void TryRemoveOverworldWildEncounter(u32 localId); +void OverworldWildEncounter_RemoveObjectOnBattle(void); bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction); void DespawnOldestOWE_Pal(void); bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon); diff --git a/src/battle_setup.c b/src/battle_setup.c index f68ed75e3857..417336ee0f09 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -258,7 +258,7 @@ static void Task_BattleStart(u8 taskId) SetMainCallback2(CB2_InitBattle); RestartWildEncounterImmunitySteps(); ClearPoisonStepCounter(); - TryRemoveOverworldWildEncounter(gSpecialVar_LastTalked); + OverworldWildEncounter_RemoveObjectOnBattle(); DestroyTask(taskId); } break; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index b4fda14a0c42..edc4e60dd65d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -956,9 +956,10 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c } } -void TryRemoveOverworldWildEncounter(u32 localId) +void OverworldWildEncounter_RemoveObjectOnBattle(void) { - struct ObjectEvent *object = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; + u32 localId = gSpecialVar_LastTalked; + struct ObjectEvent *object = &gObjectEvents[GetObjectEventIdByLocalId(localId)]; if (IsOverworldWildEncounter(object)) { From ff9adfd0c6dda0c735a4d9b54be3c4ee612278a4 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 23:33:41 +0000 Subject: [PATCH 319/572] Tidy OWE_DoSpawnDespawnAnim --- include/overworld_encounters.h | 1 - src/overworld_encounters.c | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index d0fc32566f3c..3ed19d694eb1 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -118,7 +118,6 @@ u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); u32 RemoveOldestOverworldEncounter(void); -void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 spawn); bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index edc4e60dd65d..93b75e720282 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -64,6 +64,7 @@ static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); static u32 GetMaxOverworldEncounterSpawns(void); static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); +static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); void OWE_ResetSpawnCounterPlayAmbientCry(void) { @@ -204,17 +205,18 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is return TRUE; } -void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 spawn) +static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn) { enum OverworldEncounterSpawnAnim spawnAnimType; bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; - if (spawn) + if (animSpawn) OWE_PlayMonObjectCry(objectEvent); - else if (!spawn && OWE_ShouldPlayMonFleeSound(objectEvent)) + + if (!animSpawn && OWE_ShouldPlayMonFleeSound(objectEvent)) PlaySE(SE_FLEE); - if (isShiny && spawn) + if (isShiny && animSpawn) { PlaySE(SE_SHINY); spawnAnimType = OWE_SPAWN_ANIM_SHINY; From d23e6e048726d49c5a367d6aa18baef9ea686d44 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 23:35:57 +0000 Subject: [PATCH 320/572] Remove Excess Wild Mon Headers --- include/wild_encounter.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 7c38256aea05..343cbb2a85c5 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -46,8 +46,6 @@ struct WildPokemonHeader extern const struct WildPokemonHeader gWildMonHeaders[]; -extern const struct WildPokemonHeader gBattlePyramidWildMonHeaders[]; -extern const struct WildPokemonHeader gBattlePikeWildMonHeaders[]; extern const struct WildPokemon gWildFeebas; extern bool8 gIsFishingEncounter; extern bool8 gIsSurfingEncounter; From 411bf0915c480c563f3a7fc718cc36fe87d9f194 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 23:38:08 +0000 Subject: [PATCH 321/572] Another Idea --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 93b75e720282..29d1ba9eaf13 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -248,6 +248,7 @@ u32 GetOldestSlot(void) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; if (OW_SPECIES(slotMon) != SPECIES_NONE && !OW_SHINY(slotMon)) + // Add a not feebas tile feebas? { oldest = slotMon; break; From 3206ab5836f8295f0411fca3614362c8d6e3e55b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 7 Jan 2026 23:43:04 +0000 Subject: [PATCH 322/572] Need preproc --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 29d1ba9eaf13..614b3ddaf4ad 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -187,6 +187,7 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is { u32 palTag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); + // Need Preproc checks for overworldShinyPaletteFemale if (isFemale && gSpeciesInfo[speciesId].overworldShinyPaletteFemale != NULL) palTag += OBJ_EVENT_MON_FEMALE; From e814c035a98e76298c68eede9f04be59be0eafeb Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 8 Jan 2026 18:32:22 +0000 Subject: [PATCH 323/572] OWE Replacement Logic Refactor --- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 37 +++++++++++++++++++++++-------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index d917bc20354b..646228e7b4c6 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1541,8 +1541,8 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * void RemoveObjectEvent(struct ObjectEvent *objectEvent) { - objectEvent->active = FALSE; OverworldWildEncounter_OnObjectEventRemoved(objectEvent); + objectEvent->active = FALSE; RemoveObjectEventInternal(objectEvent); // zero potential species info objectEvent->graphicsId = objectEvent->shiny = 0; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 614b3ddaf4ad..367cf3980e0a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -65,6 +65,7 @@ static u32 GetMaxOverworldEncounterSpawns(void); static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); +static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); void OWE_ResetSpawnCounterPlayAmbientCry(void) { @@ -153,7 +154,11 @@ void UpdateOverworldEncounters(void) return; } - u8 objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); + u32 objectEventId = GetObjectEventIdByLocalId(localId); + struct ObjectEvent *object = &gObjectEvents[objectEventId]; + if (OWE_ShouldDespawnGeneratedForNewOWE(object)) + RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); + objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); if (objectEventId >= OBJECT_EVENTS_COUNT) { @@ -161,17 +166,18 @@ void UpdateOverworldEncounters(void) return; } - gObjectEvents[objectEventId].disableCoveringGroundEffects = TRUE; - gObjectEvents[objectEventId].sOverworldEncounterLevel = level; - gObjectEvents[objectEventId].sRoamerOutbreakStatus = indexRoamerOutbreak; + object = &gObjectEvents[objectEventId]; + object->disableCoveringGroundEffects = TRUE; + object->sOverworldEncounterLevel = level; + object->sRoamerOutbreakStatus = indexRoamerOutbreak; u8 directions[4] = {DIR_SOUTH, DIR_NORTH, DIR_WEST, DIR_EAST}; - ObjectEventTurn(&gObjectEvents[objectEventId], directions[Random() & 3]); + ObjectEventTurn(object, directions[Random() & 3]); // Hide reflections for spawns in water // (It just looks weird) if (shouldSpawnWaterMons) - gObjectEvents[objectEventId].hideReflection = TRUE; + object->hideReflection = TRUE; // Slower replacement spawning sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); @@ -301,14 +307,6 @@ static u8 NextSpawnMonSlot(void) } } - if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) - { - u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); - u32 objectEventId = GetObjectEventIdByLocalId(localId); - struct ObjectEvent *object = &gObjectEvents[objectEventId]; - RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); - } - return spawnSlot; } @@ -1313,6 +1311,9 @@ static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) if (!AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) return FALSE; + if (OWE_ShouldDespawnGeneratedForNewOWE(objectEvent)) + return FALSE; + return OW_WILD_ENCOUNTERS_DESPAWN_SOUND; } @@ -1360,6 +1361,14 @@ bool32 OverworldWildEncounter_ShouldEnableRandomBattleFrontierSpawns(void) && !OW_WILD_ENCOUNTERS_RANDOM && OW_WILD_ENCOUNTERS_OVERWORLD); } +static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) +{ + if (!IsGeneratedOverworldWildEncounter(object)) + return FALSE; + + return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumActiveGeneratedOverworldEncounters() == GetMaxOverworldEncounterSpawns(); +} + #undef tLocalId #undef NOT_STARTED #undef STARTED From acf4f102c52f609990af7a6109b2aa0360fd7cfc Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 8 Jan 2026 19:01:54 +0000 Subject: [PATCH 324/572] Zero More Data in OverworldWildEncounter_OnObjectEventRemoved --- src/overworld_encounters.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 367cf3980e0a..45ccd1f4aadf 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -575,6 +575,8 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent if (!IsOverworldWildEncounter(objectEvent)) return; + objectEvent->sOverworldEncounterLevel = 0; + objectEvent->sAge = 0; objectEvent->sRoamerOutbreakStatus = 0; if (gMain.callback2 == CB2_Overworld) From 9175eb309b135e5ce31819221e9008aa87e0a324 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 8 Jan 2026 19:03:35 +0000 Subject: [PATCH 325/572] Move CB2_Overworld Check to inside OWE_DoSpawnDespawnAnim --- src/overworld_encounters.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 45ccd1f4aadf..0f486ddd993e 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -214,6 +214,9 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn) { + if (gMain.callback2 != CB2_Overworld) + return; + enum OverworldEncounterSpawnAnim spawnAnimType; bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; @@ -579,8 +582,7 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent objectEvent->sAge = 0; objectEvent->sRoamerOutbreakStatus = 0; - if (gMain.callback2 == CB2_Overworld) - OWE_DoSpawnDespawnAnim(objectEvent, FALSE); + OWE_DoSpawnDespawnAnim(objectEvent, FALSE); } u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) From 80a8edd3d5c5ee8cb2dadc6d60f1a3b10b07cd71 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 12 Jan 2026 13:00:21 +0000 Subject: [PATCH 326/572] Refactor Battle Frontier Spawning --- include/overworld_encounters.h | 2 +- src/field_control_avatar.c | 2 +- src/overworld_encounters.c | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 3ed19d694eb1..5e15b851cd01 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -134,6 +134,6 @@ enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavio void OverworldWildEncounter_InitRoamerOutbreakStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template); void OverworldWildEncounter_FreezeAllObjects(void); bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *objectEvent); -bool32 OverworldWildEncounter_ShouldEnableRandomBattleFrontierSpawns(void); +bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 2478a3092b89..fee342f5e6f2 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -828,7 +828,7 @@ void RestartWildEncounterImmunitySteps(void) static bool8 CheckStandardWildEncounter(u16 metatileBehavior) { - if (FlagGet(OW_FLAG_NO_ENCOUNTER) || (!OW_WILD_ENCOUNTERS_RANDOM && OverworldWildEncounter_ShouldEnableRandomBattleFrontierSpawns())) + if (FlagGet(OW_FLAG_NO_ENCOUNTER) || OverworldWildEncounter_ShouldDisableRandomEncounters()) return FALSE; if (sWildEncounterImmunitySteps < 4) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 0f486ddd993e..c5bf77d749ea 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1358,11 +1358,13 @@ bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *object return objectEvent->sOverworldEncounterLevel & OWE_FLAG_START_ENCOUNTER; } -bool32 OverworldWildEncounter_ShouldEnableRandomBattleFrontierSpawns(void) +bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void) { - return ((gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS || gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) - && !OW_WILD_ENCOUNTERS_RANDOM && OW_WILD_ENCOUNTERS_OVERWORLD); + return FALSE; + + return !OW_WILD_ENCOUNTERS_RANDOM; } static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) From 0595459cbfea496ad1319a03720f54ab0c83f663 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 14 Jan 2026 10:22:45 +0000 Subject: [PATCH 327/572] Consistent Remove Mons When Changing Encounter Header --- src/overworld_encounters.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c5bf77d749ea..548d4f0f3fe7 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -77,13 +77,12 @@ void OWE_ResetSpawnCounterPlayAmbientCry(void) void UpdateOverworldEncounters(void) { - if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING)) + bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); + if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING) || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) return; - bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER) - || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons) || CurrentBattlePyramidLocation() || InBattlePike() || InTrainerHillChallenge()) From 3cea70583b3fe5ecd41e6ed3804ad5a9224e92a4 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 14 Jan 2026 11:40:04 +0000 Subject: [PATCH 328/572] Extern gStandardDirections --- include/event_object_movement.h | 2 ++ src/overworld_encounters.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index dc22d7d99393..e00c7d57b906 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -548,4 +548,6 @@ u8 GetObjectEventApricornTreeId(u8 objectEventId); bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); +extern const u8 gStandardDirections[]; + #endif //GUARD_EVENT_OBJECT_MOVEMENT_H diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 548d4f0f3fe7..bfa73db84679 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -170,7 +170,8 @@ void UpdateOverworldEncounters(void) object->sOverworldEncounterLevel = level; object->sRoamerOutbreakStatus = indexRoamerOutbreak; - u8 directions[4] = {DIR_SOUTH, DIR_NORTH, DIR_WEST, DIR_EAST}; + u8 directions[4]; + memcpy(directions, gStandardDirections, sizeof directions); ObjectEventTurn(object, directions[Random() & 3]); // Hide reflections for spawns in water From 6e2ccb2086bd138446b979172205eb40d47f89c5 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:57:36 +0000 Subject: [PATCH 329/572] Update comment --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index bfa73db84679..5868e73c3137 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -913,7 +913,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( &level, &indexRoamerOutbreak ); - // Have a fallback incase of no header mons + // Have an assertf and fallback incase of no header mons graphicsId = speciesId + OBJ_EVENT_MON; if (isFemale) From cfa5b033c20906db62e1e580939424cb7ebd2cec Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 16 Jan 2026 20:41:15 +0000 Subject: [PATCH 330/572] Update Merge Fixes --- src/overworld_encounters.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5868e73c3137..9d3a305d504b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -413,8 +413,17 @@ void CreateOverworldWildEncounter(void) u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; - bool32 isFemale = OW_FEMALE(object) ? TRUE : FALSE; + u32 gender = OW_FEMALE(object) ? MON_FEMALE : MON_MALE; u32 level = (object->sOverworldEncounterLevel &= ~OWE_FLAG_START_ENCOUNTER); + u32 personality; + + switch (gSpeciesInfo[speciesId].genderRatio) + { + case MON_MALE: + case MON_FEMALE: + case MON_GENDERLESS: + gender = gSpeciesInfo[speciesId].genderRatio; + } if (level > MAX_LEVEL) level = MAX_LEVEL; @@ -422,15 +431,9 @@ void CreateOverworldWildEncounter(void) level = MIN_LEVEL; ZeroEnemyPartyMons(); - CreateMonWithGender( - &gEnemyParty[0], - speciesId, - level, - USE_RANDOM_IVS, - OT_ID_PLAYER_ID, - 0, - isFemale - ); + personality = GetMonPersonality(speciesId, gender, NATURE_RANDOM, RANDOM_UNOWN_LETTER); + CreateMonWithIVs(&gEnemyParty[0], speciesId, level, personality, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); + GiveMonInitialMoveset(&gEnemyParty[0]); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); if (CreateOverworldWildEncounter_CheckMassOutbreak(indexRoamerOutbreak, speciesId)) @@ -622,7 +625,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 if (*speciesId == SPECIES_UNOWN) *speciesId = GetUnownSpeciesId(personality); - *isShiny = ComputePlayerShinyOdds(personality); + *isShiny = ComputePlayerShinyOdds(personality, READ_OTID_FROM_SAVE); if (GetGenderFromSpeciesAndPersonality(*speciesId, personality) == MON_FEMALE) *isFemale = TRUE; else From f8816e0b92d5b3e3a06e946f4378e4393c8549f0 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 16 Jan 2026 21:07:30 +0000 Subject: [PATCH 331/572] Remove OverworldWildEncounter_InitRoamerOutbreakStatus --- include/overworld_encounters.h | 1 - src/event_object_movement.c | 1 - src/overworld_encounters.c | 9 --------- 3 files changed, 11 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 5e15b851cd01..2bc7bc1a002a 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -131,7 +131,6 @@ bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); void Task_OWE_WaitMovements(u8 taskId); enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); -void OverworldWildEncounter_InitRoamerOutbreakStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template); void OverworldWildEncounter_FreezeAllObjects(void); bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *objectEvent); bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 646228e7b4c6..efb0dba1a864 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1477,7 +1477,6 @@ static u8 InitObjectEventStateFromTemplate(const struct ObjectEventTemplate *tem objectEvent->mapNum = mapNum; objectEvent->trainerRange_berryTreeId = template->trainerRange_berryTreeId; objectEvent->previousMovementDirection = gInitialMovementTypeFacingDirections[template->movementType]; - OverworldWildEncounter_InitRoamerOutbreakStatus(objectEvent, template); SetObjectEventDirection(objectEvent, objectEvent->previousMovementDirection); if (sMovementTypeHasRange[objectEvent->movementType]) { diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5868e73c3137..79c20127f54b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1259,15 +1259,6 @@ static bool32 OWE_DoesRoamerObjectExist(void) return FALSE; } -void OverworldWildEncounter_InitRoamerOutbreakStatus(struct ObjectEvent *objectEvent, const struct ObjectEventTemplate *template) -{ - // See comment in OWE_CreateEnemyPartyMon. - if (!IsOverworldWildEncounter(objectEvent)) - return; - - objectEvent->sRoamerOutbreakStatus = (template->trainerType >> 8) & 0xFF; -} - static bool32 OWE_CheckRestrictMovementMetatile(struct ObjectEvent *objectEvent, u32 direction) { s16 xCurrent = objectEvent->currentCoords.x; From e28ca99ce5c9ca0299670ffe3cb41a2cd0568c3d Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 16 Jan 2026 21:22:28 +0000 Subject: [PATCH 332/572] Remove Uneeded Line --- src/overworld_encounters.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 79c20127f54b..9390b6afc654 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -923,7 +923,6 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( templateOWE.graphicsId = graphicsId; templateOWE.sOverworldEncounterLevel = level; - templateOWE.trainerType = TRAINER_TYPE_ENCOUNTER; if (templateOWE.movementType == MOVEMENT_TYPE_NONE) templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); From dfb91c778201dd3f90427bd1496c6e3bb33926ad Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 18 Jan 2026 15:55:38 +0000 Subject: [PATCH 333/572] Add ASSERTFs --- src/overworld_encounters.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index fb4e410f2994..3ca2d0a089c8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -613,6 +613,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 if (!OWE_CreateEnemyPartyMon(speciesId, level, indexRoamerOutbreak, x, y)) { + // May be good to convert this to an assertf ZeroEnemyPartyMons(); *speciesId = SPECIES_NONE; return; @@ -658,6 +659,9 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; } + if (wildMonInfo == NULL) + return FALSE; + /* These functions perform checks of various encounter types in the following order: 1. Attempted Generated Roamer Encounter @@ -906,17 +910,26 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( bool32 isFemale = FALSE; u32 level; u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; + u32 x = template->x; + u32 y = template->y; SetOverworldEncounterSpeciesInfo( - template->x, - template->y, + x, + y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak ); - // Have an assertf and fallback incase of no header mons + + assertf(speciesId != SPECIES_NONE && speciesId < NUM_SPECIES && IsSpeciesEnabled(speciesId), "invalid semi-manual overworld encounter\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, x, y) + { + // Currently causes assertf on each player step as function is called. + templateOWE.graphicsId = OBJ_EVENT_GFX_BOY_1; + templateOWE.trainerType = TRAINER_TYPE_NONE; + return templateOWE; + } graphicsId = speciesId + OBJ_EVENT_MON; if (isFemale) From 9acf4e0fe20f12e411b3b70563311846df9bc485 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 19 Jan 2026 11:54:51 -0600 Subject: [PATCH 334/572] removed unnecessary #undefs --- src/overworld_encounters.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 3ca2d0a089c8..2539f23f5e57 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1382,8 +1382,6 @@ static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) } #undef tLocalId -#undef NOT_STARTED -#undef STARTED #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From 2b78e534b906d51a1e3a64da7f62f4a2a77a7d28 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 19 Jan 2026 12:41:26 -0600 Subject: [PATCH 335/572] Need handling for restricted movement --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 2539f23f5e57..6d82604bc6c0 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1079,6 +1079,7 @@ bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon) static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, u32 newDirection) { + // TODO: Add handling for restricted movement. s16 x = mon->currentCoords.x; s16 y = mon->currentCoords.y; From 96de26e2d8306ec1184df5060ffd933a82f26a34 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 19 Jan 2026 12:45:20 -0600 Subject: [PATCH 336/572] added function MoveToPlayerForEncounter --- src/event_object_movement.c | 109 ++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index ce6d1170d284..6efbeca14b95 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11735,6 +11735,38 @@ static u8 GetWalkMovementActionInDirectionWithSpeed(u32 direction, u32 speed) return GetWalkNormalMovementAction(direction); } +static void MoveToPlayerForEncounter(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + u16 speciesId = OW_SPECIES(objectEvent); + u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); + bool8 collision; + u8 movementActionId; + + SetObjectEventDirection(objectEvent, direction); + collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + + if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) + { + s16 x = objectEvent->currentCoords.x; + s16 y = objectEvent->currentCoords.y; + MoveCoords(objectEvent->movementDirection, &x, &y); + // If colliding with the player object, don't try to walk around it. + if (GetObjectObjectCollidesWith(objectEvent, x, y, FALSE) == gPlayerAvatar.objectEventId) + { + movementActionId = GetFaceDirectionMovementAction(objectEvent->facingDirection); + } + else + { + direction = OWE_DirectionToPlayerFromCollision(objectEvent); + SetObjectEventDirection(objectEvent, direction); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + } + } + ObjectEventSetSingleMovement(objectEvent, sprite, movementActionId); + objectEvent->singleMovementActive = TRUE; +} + bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent) { gFieldEffectArguments[0] = objEvent->currentCoords.x; @@ -11754,34 +11786,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) { - u16 speciesId = OW_SPECIES(objectEvent); - u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - bool8 collision; - u8 movementActionId; - - SetObjectEventDirection(objectEvent, direction); - collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - - if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) - { - s16 x = objectEvent->currentCoords.x; - s16 y = objectEvent->currentCoords.y; - MoveCoords(objectEvent->movementDirection, &x, &y); - // If colliding with the player object, don't try to walk around it. - if (GetObjectObjectCollidesWith(objectEvent, x, y, FALSE) == gPlayerAvatar.objectEventId) - { - movementActionId = GetFaceDirectionMovementAction(objectEvent->facingDirection); - } - else - { - direction = OWE_DirectionToPlayerFromCollision(objectEvent); - SetObjectEventDirection(objectEvent, direction); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - } - } - ObjectEventSetSingleMovement(objectEvent, sprite, movementActionId); - objectEvent->singleMovementActive = TRUE; + MoveToPlayerForEncounter(objectEvent, sprite); sprite->sTypeFuncId = 6; return FALSE; } @@ -11891,6 +11896,13 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent bool8 collision; u8 movementActionId; + if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) + { + sprite->sTypeFuncId = 12; + MoveToPlayerForEncounter(objectEvent, sprite); + return FALSE; + } + collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); sprite->sTypeFuncId = 12; @@ -11961,9 +11973,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent * return FALSE; } - u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - if (!OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) - direction = GetOppositeDirection(direction); + u8 direction = GetOppositeDirection(DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent)); SetObjectEventDirection(objectEvent, direction); sprite->sTypeFuncId = 11; @@ -11976,15 +11986,19 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * bool8 collision; u8 movementActionId; + if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) + { + MoveToPlayerForEncounter(objectEvent, sprite); + sprite->sTypeFuncId = 12; + return FALSE; + } + collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) { - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - if (!OverworldWildEncounter_IsStartingWildEncounter(objectEvent) && objectEvent->currentCoords.x != player->currentCoords.x && objectEvent->currentCoords.y != player->currentCoords.y) - newDirection = GetOppositeDirection(newDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); collision = GetCollisionInDirection(objectEvent, newDirection); @@ -12044,6 +12058,13 @@ bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step10(struct ObjectEvent bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { + if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) + { + MoveToPlayerForEncounter(objectEvent, sprite); + sprite->sTypeFuncId = 12; + return FALSE; + } + ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection)); objectEvent->singleMovementActive = TRUE; sprite->sTypeFuncId = 12; @@ -12091,7 +12112,14 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve bool8 collision; u8 movementActionId; - if (distance <= 1 && !OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) + if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) + { + MoveToPlayerForEncounter(objectEvent, sprite); + sprite->sTypeFuncId = 12; + return FALSE; + } + + if (distance <= 1) { SetObjectEventDirection(objectEvent, GetOppositeDirection(objectEvent->movementDirection)); collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); @@ -12100,7 +12128,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - if (!OverworldWildEncounter_IsStartingWildEncounter(objectEvent) && objectEvent->currentCoords.x != player->currentCoords.x && objectEvent->currentCoords.y != player->currentCoords.y) + if (objectEvent->currentCoords.x != player->currentCoords.x && objectEvent->currentCoords.y != player->currentCoords.y) newDirection = GetOppositeDirection(newDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); @@ -12109,7 +12137,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); } } - else if (distance == OWE_APPROACH_DISTANCE && !equalDistances && !OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) + else if (distance == OWE_APPROACH_DISTANCE && !equalDistances) { if (sJumpTimer <= 0) { @@ -12185,6 +12213,13 @@ bool8 MovementType_Despawn_OverworldWildEncounter_Step10(struct ObjectEvent *obj bool8 MovementType_Despawn_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { + if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) + { + MoveToPlayerForEncounter(objectEvent, sprite); + sprite->sTypeFuncId = 12; + return FALSE; + } + if (sDespawnTimer == OWE_DESPAWN_FRAMES) { RemoveObjectEvent(objectEvent); From 9a8c91332f17b8b5a38f9cc54795cd2b38e55c4a Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 19 Jan 2026 12:59:35 -0600 Subject: [PATCH 337/572] Removed Task_OWE_WaitMovements --- src/event_object_movement.c | 14 ++++++++++++++ src/overworld_encounters.c | 35 +---------------------------------- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 6efbeca14b95..9728a9517a43 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11741,6 +11741,20 @@ static void MoveToPlayerForEncounter(struct ObjectEvent *objectEvent, struct Spr u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); bool8 collision; u8 movementActionId; + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + + if (objectEvent->singleMovementActive == 0 && player->singleMovementActive == 0) + { + // Let the mon continue to take steps until right next to the player. + if (OWE_IsMonNextToPlayer(objectEvent)) + { + gSpecialVar_LastTalked = objectEvent->localId; + gSpecialVar_0x8004 = speciesId; + ScriptContext_SetupScript(InteractWithDynamicWildOverworldEncounter); + FreezeObjectEvent(objectEvent); + return; + } + } SetObjectEventDirection(objectEvent, direction); collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 6d82604bc6c0..5fc7b90cdbac 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -945,16 +945,6 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( return templateOWE; } -static bool32 OWE_IsWaitTaskActive(void) -{ - if (FindTaskIdByFunc(Task_OWE_WaitMovements) != TASK_NONE) - return TRUE; - - return FALSE; -} - -#define tLocalId gTasks[taskId].data[0] - void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) { // The only automatically interacts with an OW Encounter when; @@ -965,15 +955,12 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle)); bool32 playerIsObstacle = (obstacle->isPlayer && IsOverworldWildEncounter(collider)); - if ((playerIsCollider || playerIsObstacle) && !OWE_IsWaitTaskActive()) + if ((playerIsCollider || playerIsObstacle)) { struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; LockPlayerFieldControls(); - // Wait for both the player and the mon to finish their current movements. - u8 taskId = CreateTask(Task_OWE_WaitMovements, 0); wildMon->trainerRange_berryTreeId |= OWE_FLAG_START_ENCOUNTER; - tLocalId = wildMon->localId; } } @@ -1170,25 +1157,6 @@ u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equal return absX; } -void Task_OWE_WaitMovements(u8 taskId) -{ - struct ObjectEvent *mon = &gObjectEvents[GetObjectEventIdByLocalId(tLocalId)]; - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - - if (mon->singleMovementActive == 0 && player->singleMovementActive == 0) - { - // Let the mon continue to take steps until right next to the player. - if (OWE_IsMonNextToPlayer(mon)) - { - gSpecialVar_LastTalked = tLocalId; - gSpecialVar_0x8004 = OW_SPECIES(&gObjectEvents[GetObjectEventIdByLocalId(tLocalId)]); - ScriptContext_SetupScript(InteractWithDynamicWildOverworldEncounter); - FreezeObjectEvent(mon); - DestroyTask(taskId); - } - } -} - enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior) { if (MetatileBehavior_IsPokeGrass(metatileBehavior) || MetatileBehavior_IsAshGrass(metatileBehavior)) @@ -1382,7 +1350,6 @@ static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumActiveGeneratedOverworldEncounters() == GetMaxOverworldEncounterSpawns(); } -#undef tLocalId #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From cecfe6d43da4622624e6d6f8bcca6a1cbfd7698f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 19 Jan 2026 21:15:59 +0000 Subject: [PATCH 338/572] Add Tile Num Checking (Messy) --- include/overworld_encounters.h | 4 ---- include/sprite.h | 1 + src/overworld_encounters.c | 29 +++++++++++++++++++++--- src/sprite.c | 41 +++++++++++++++++++++++++++++++++- 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 2bc7bc1a002a..ed7e080c7254 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -5,10 +5,6 @@ #error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OW_WILD_ENCOUNTERS_OVERWORLD to work." #endif -#if OW_GFX_COMPRESS == TRUE && OW_WILD_ENCOUNTERS_OVERWORLD == TRUE -#error "OW_GFX_COMPRESS needs to be FALSE in order for OW_WILD_ENCOUNTERS_OVERWORLD to work." -#endif - #define OWE_MAX_SPAWN_SLOTS 5 #define OWE_MAX_LAND_SPAWNS 3 diff --git a/include/sprite.h b/include/sprite.h index f50da09c5241..dea18c33146b 100644 --- a/include/sprite.h +++ b/include/sprite.h @@ -307,6 +307,7 @@ u16 LoadSpriteSheet(const struct SpriteSheet *sheet); u16 LoadSpriteSheetByTemplate(const struct SpriteTemplate *template, u32 frame, s32 offset); void LoadSpriteSheets(const struct SpriteSheet *sheets); s16 AllocSpriteTiles(u16 tileCount); +bool32 CanAllocSpriteTiles(u16 tileCount); void FreeSpriteTilesByTag(u16 tag); void FreeSpriteTileRanges(void); u16 GetSpriteTileStartByTag(u16 tag); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 3ca2d0a089c8..bd228eb84714 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -186,19 +186,19 @@ void UpdateOverworldEncounters(void) static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny) { u32 numFreePalSlots = CountFreePaletteSlots(); + u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); // We need at least 2 pal slots open. One for the object and one for the spawn field effect. // Add this and tiles to seperate graphics check function if (numFreePalSlots == 1) { - u32 palTag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); // Need Preproc checks for overworldShinyPaletteFemale if (isFemale && gSpeciesInfo[speciesId].overworldShinyPaletteFemale != NULL) - palTag += OBJ_EVENT_MON_FEMALE; + tag += OBJ_EVENT_MON_FEMALE; // If the mon's palette isn't already loaded, don't spawn. - if (IndexOfSpritePaletteTag(palTag) == 0xFF) + if (IndexOfSpritePaletteTag(tag) == 0xFF) return FALSE; // Add check if field effect pallete is already loaded @@ -209,6 +209,29 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is return FALSE; } + const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(speciesId, isShiny, isFemale); + u32 tileCount = graphicsInfo->size / TILE_SIZE_4BPP; + DebugPrintf("\n\nSpecies: %S\nSheet Tile Count: %d", GetSpeciesName(speciesId), tileCount); + if (OW_GFX_COMPRESS) + { + // If tiles are already existing return early, spritesheet is loaded when compressed + if (IndexOfSpriteTileTag(tag) != 0xFF) + { + DebugPrintf("Already Loaded"); + return TRUE; + } + + u32 frames = graphicsInfo->anims == sAnimTable_Following_Asym ? 8 : 6; + tileCount *= frames; + DebugPrintf("Tile Count for all Anims: %d", tileCount); + } + + if (!CanAllocSpriteTiles(tileCount)) + { + DebugPrintf("No Spawn"); + return FALSE; + } + return TRUE; } diff --git a/src/sprite.c b/src/sprite.c index 72f952aac320..ed55c218ad29 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -1,7 +1,6 @@ #include "global.h" #include "sprite.h" #include "main.h" -#include "overworld_encounters.h" #include "palette.h" #include "string_util.h" #include "text.h" @@ -681,6 +680,46 @@ s16 AllocSpriteTiles(u16 tileCount) return start; } +bool32 CanAllocSpriteTiles(u16 tileCount) +{ + u16 i; + s16 start; + u16 numTilesFound; + + if (tileCount == 0) + return TRUE; + + i = gReservedSpriteTileCount; + + for (;;) + { + while (SPRITE_TILE_IS_ALLOCATED(i)) + { + i++; + if (i == TOTAL_OBJ_TILE_COUNT) + return FALSE; + } + + start = i; + numTilesFound = 1; + + while (numTilesFound != tileCount) + { + i++; + if (i == TOTAL_OBJ_TILE_COUNT) + return FALSE; + + if (!SPRITE_TILE_IS_ALLOCATED(i)) + numTilesFound++; + else + break; + } + + if (numTilesFound == tileCount) + return TRUE; + } +} + u8 SpriteTileAllocBitmapOp(u16 bit, u8 op) { u8 index = bit / 8; From eede494e21e79da164d0bc8985b6c60218eda779 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 19 Jan 2026 21:35:23 +0000 Subject: [PATCH 339/572] Fix Invalid Mon Cries --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index bd228eb84714..8d3fe3158df6 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1252,7 +1252,7 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) #define MAP_METATILE_VIEW_Y 5 static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) { - if (!IS_OW_MON_OBJ(objectEvent)) + if (!IsOverworldWildEncounter(objectEvent)) return; u32 speciesId = OW_SPECIES(objectEvent); From e1d6735bc09c7c5827994e7025342086beeb0346 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 19 Jan 2026 22:14:14 +0000 Subject: [PATCH 340/572] Config to allow mons to ignore movement restrictions when seeing player --- include/config/overworld.h | 3 ++- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 16 +++++++++++++--- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 2dd4e77dc822..e0fb5e713fa9 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -75,8 +75,9 @@ // Overworld Encounters #define OW_WILD_ENCOUNTERS_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. #define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. -#define OW_WILD_ENCOUNTERS_RESTRICT_METATILE TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same metatile behavior as the one it spawned on. +#define OW_WILD_ENCOUNTERS_RESTRICT_METATILE TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same encounter metatile behavior as the one it is currently on, if any. #define OW_WILD_ENCOUNTERS_RESTRICT_MAP TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within their current map bounds. +#define OW_WILD_ENCOUNTERS_UNRESTRICT_SIGHT FALSE // If TRUE, OW Pokémon spawned as overworld wild encounters will ignore all movement restrictions when they can see the player. #define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. #define OW_WILD_ENCOUNTERS_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. #define OW_VANILLA_AMBIENT_CRIES TRUE // If TRUE, the ambient wild Pokémon cries will play on routes with encounter tables. diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index ed7e080c7254..3954841587fe 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -120,7 +120,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c void OverworldWildEncounter_RemoveObjectOnBattle(void); bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction); void DespawnOldestOWE_Pal(void); -bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon); +bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon); bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon); u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 9728a9517a43..8451ad634c3b 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11824,7 +11824,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent if (OW_MON_WANDER_WALK == TRUE && IS_OW_MON_OBJ(objectEvent)) UpdateMonMoveInPlace(objectEvent, sprite); - if (OWE_CanMonSeePlayer(objectEvent) && objectEvent->movementType != MOVEMENT_TYPE_WANDER_AROUND_OWE) + if (OWE_CanAwareMonSeePlayer(objectEvent)) { sprite->sTypeFuncId = 7; return FALSE; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index fbaf142cf5c0..07f276654110 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1003,6 +1003,9 @@ bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 directio { if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) return FALSE; + + if (OWE_CanAwareMonSeePlayer(objectEvent) && OW_WILD_ENCOUNTERS_UNRESTRICT_SIGHT) + return FALSE; // Returns TRUE if movement is restricted. return ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatile(objectEvent, direction)) @@ -1028,9 +1031,10 @@ void DespawnOldestOWE_Pal(void) } } -bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon) +bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon) { - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + if (mon->movementType == MOVEMENT_TYPE_WANDER_AROUND_OWE) + return FALSE; if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING)) { @@ -1039,6 +1043,7 @@ bool32 OWE_CanMonSeePlayer(struct ObjectEvent *mon) } else { + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u32 speciesId = OW_SPECIES(mon); u32 viewDistance = OWE_GetViewDistanceFromSpecies(speciesId); u32 viewWidth = OWE_GetViewWidthFromSpecies(speciesId); @@ -1287,6 +1292,11 @@ static bool32 OWE_CheckRestrictMovementMetatile(struct ObjectEvent *objectEvent, && MetatileBehavior_IsIndoorEncounter(metatileBehaviourNew)) return FALSE; + if (!MetatileBehavior_IsLandWildEncounter(metatileBehaviourCurrent) + && !MetatileBehavior_IsWaterWildEncounter(metatileBehaviourCurrent) + && !MetatileBehavior_IsIndoorEncounter(metatileBehaviourCurrent)) + return FALSE; + return TRUE; } @@ -1299,7 +1309,7 @@ static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, u32 u32 mapGroup = objectEvent->mapGroup; u32 mapNum = objectEvent->mapNum; - if (mapGroup == gSaveBlock1Ptr->location.mapGroup && mapNum == gSaveBlock1Ptr->location.mapNum) + if (AreCoordsInsidePlayerMap(xCurrent, yCurrent)) return !AreCoordsInsidePlayerMap(xNew, yNew); else return AreCoordsInsidePlayerMap(xNew, yNew); From 2030dcff165efaf56734313dd595e1b753d339c8 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 19 Jan 2026 22:52:10 +0000 Subject: [PATCH 341/572] Remove Excess Variables --- src/overworld_encounters.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 07f276654110..8c1b4f0e97f7 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1306,8 +1306,6 @@ static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, u32 s16 yCurrent = objectEvent->currentCoords.y; s16 xNew = xCurrent + gDirectionToVectors[direction].x; s16 yNew = yCurrent + gDirectionToVectors[direction].y; - u32 mapGroup = objectEvent->mapGroup; - u32 mapNum = objectEvent->mapNum; if (AreCoordsInsidePlayerMap(xCurrent, yCurrent)) return !AreCoordsInsidePlayerMap(xNew, yNew); From 8f30fac2f940b30e62894557cd18bcb34141883d Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 19 Jan 2026 22:52:24 +0000 Subject: [PATCH 342/572] Change DebugPrintfs --- src/overworld_encounters.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8c1b4f0e97f7..b205d1e1e50b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -211,27 +211,26 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(speciesId, isShiny, isFemale); u32 tileCount = graphicsInfo->size / TILE_SIZE_4BPP; - DebugPrintf("\n\nSpecies: %S\nSheet Tile Count: %d", GetSpeciesName(speciesId), tileCount); if (OW_GFX_COMPRESS) { // If tiles are already existing return early, spritesheet is loaded when compressed if (IndexOfSpriteTileTag(tag) != 0xFF) { - DebugPrintf("Already Loaded"); + DebugPrintf("\n\nALREADY LOADED\nSpecies: %S", GetSpeciesName(speciesId)); return TRUE; } u32 frames = graphicsInfo->anims == sAnimTable_Following_Asym ? 8 : 6; tileCount *= frames; - DebugPrintf("Tile Count for all Anims: %d", tileCount); } if (!CanAllocSpriteTiles(tileCount)) { - DebugPrintf("No Spawn"); + DebugPrintf("\n\nNO SPAWN\nSpecies: %S\nSheet Tile Count: %d", GetSpeciesName(speciesId), tileCount); return FALSE; } + DebugPrintf("\n\nSPAWN\nSpecies: %S\nSheet Tile Count: %d", GetSpeciesName(speciesId), tileCount); return TRUE; } From 968a85fd8f598d77abc9d64fbe49dfb7d6c973c4 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 19 Jan 2026 23:15:10 +0000 Subject: [PATCH 343/572] Another attempt at fixing (Doesnt work) --- include/event_object_movement.h | 1 + src/overworld_encounters.c | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 9ef27ac8d5e3..2d5853eb41aa 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -260,6 +260,7 @@ u8 GetObjectEventBerryTreeId(u8 objectEventId); void SetBerryTreeJustPicked(u8 mapId, u8 mapNumber, u8 mapGroup); bool8 IsBerryTreeSparkling(u8 localId, u8 mapNum, u8 mapGroup); const struct ObjectEventTemplate *GetObjectEventTemplateByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup); +u16 LoadSheetGraphicsInfo(const struct ObjectEventGraphicsInfo *info, u16 uuid, struct Sprite *sprite); u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemplate, u8 mapNum, u8 mapGroup, s16 cameraX, s16 cameraY); bool8 GetFollowerInfo(u32 *species, bool32 *shiny, bool32 *female); const struct ObjectEventGraphicsInfo *SpeciesToGraphicsInfo(u32 species, bool32 shiny, bool32 female); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index b205d1e1e50b..4ddfb4ca3214 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -188,14 +188,14 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is u32 numFreePalSlots = CountFreePaletteSlots(); u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); + // Need Preproc checks for overworldShinyPaletteFemale + if (isFemale && gSpeciesInfo[speciesId].overworldShinyPaletteFemale != NULL) + tag += OBJ_EVENT_MON_FEMALE; + // We need at least 2 pal slots open. One for the object and one for the spawn field effect. // Add this and tiles to seperate graphics check function if (numFreePalSlots == 1) { - - // Need Preproc checks for overworldShinyPaletteFemale - if (isFemale && gSpeciesInfo[speciesId].overworldShinyPaletteFemale != NULL) - tag += OBJ_EVENT_MON_FEMALE; // If the mon's palette isn't already loaded, don't spawn. if (IndexOfSpritePaletteTag(tag) == 0xFF) @@ -209,7 +209,9 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is return FALSE; } - const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(speciesId, isShiny, isFemale); + // const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(speciesId, isShiny, isFemale); + const struct ObjectEventGraphicsInfo *graphicsInfo = GetObjectEventGraphicsInfo(tag); + tag = LoadSheetGraphicsInfo(graphicsInfo, tag, NULL); u32 tileCount = graphicsInfo->size / TILE_SIZE_4BPP; if (OW_GFX_COMPRESS) { @@ -231,6 +233,7 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is } DebugPrintf("\n\nSPAWN\nSpecies: %S\nSheet Tile Count: %d", GetSpeciesName(speciesId), tileCount); + FreeSpriteTilesByTag(tag); return TRUE; } From 77d2b9466e1a966cc671ba1d19bdb99f47819fea Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 20 Jan 2026 10:25:31 -0600 Subject: [PATCH 344/572] Moved collision into OWE_CheckRestrictedMovement --- include/event_object_movement.h | 1 + src/event_object_movement.c | 35 ++++++++++----------------------- src/overworld_encounters.c | 3 +++ 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 9ef27ac8d5e3..737bc96af6d6 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -194,6 +194,7 @@ bool8 ObjectEventIsHeldMovementActive(struct ObjectEvent *objectEvent); u8 ObjectEventClearHeldMovementIfFinished(struct ObjectEvent *objectEvent); u8 GetObjectEventIdByPosition(u16 x, u16 y, u8 elevation); void SetTrainerMovementType(struct ObjectEvent *objectEvent, u8 movementType); +u8 GetCollisionInDirection(struct ObjectEvent *, u8); u8 GetTrainerFacingDirectionMovementType(u8 direction); const u8 *GetObjectEventScriptPointerByObjectEventId(u8 objectEventId); u8 GetCollisionFlagsAtCoords(struct ObjectEvent *objectEvent, s16 x, s16 y, u8 direction); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 9728a9517a43..40a4a30178ab 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -131,7 +131,6 @@ static bool8 ObjectEventExecSingleMovementAction(struct ObjectEvent *, struct Sp static bool32 UpdateMonMoveInPlace(struct ObjectEvent *, struct Sprite *); static void SetMovementDelay(struct Sprite *, s16); static bool8 WaitForMovementDelay(struct Sprite *); -static u8 GetCollisionInDirection(struct ObjectEvent *, u8); static u32 GetCopyDirection(u8, u32, u32); static void TryEnableObjectEventAnim(struct ObjectEvent *, struct Sprite *); static void ObjectEventExecHeldMovementAction(struct ObjectEvent *, struct Sprite *); @@ -11739,7 +11738,6 @@ static void MoveToPlayerForEncounter(struct ObjectEvent *objectEvent, struct Spr { u16 speciesId = OW_SPECIES(objectEvent); u8 direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - bool8 collision; u8 movementActionId; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; @@ -11757,10 +11755,9 @@ static void MoveToPlayerForEncounter(struct ObjectEvent *objectEvent, struct Spr } SetObjectEventDirection(objectEvent, direction); - collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) { s16 x = objectEvent->currentCoords.x; s16 y = objectEvent->currentCoords.y; @@ -11842,8 +11839,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent SetObjectEventDirection(objectEvent, chosenDirection); sprite->sTypeFuncId = 5; - if (OWE_CheckRestrictedMovement(objectEvent, chosenDirection) - || GetCollisionInDirection(objectEvent, chosenDirection)) + if (OWE_CheckRestrictedMovement(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; return TRUE; @@ -11907,7 +11903,6 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step10(struct ObjectEvent bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u16 speciesId = OW_SPECIES(objectEvent); - bool8 collision; u8 movementActionId; if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) @@ -11917,11 +11912,10 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent return FALSE; } - collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); sprite->sTypeFuncId = 12; - if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) { s16 x = objectEvent->currentCoords.x; s16 y = objectEvent->currentCoords.y; @@ -11935,9 +11929,8 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent } u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - collision = GetCollisionInDirection(objectEvent, newDirection); - if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); } @@ -11997,7 +11990,6 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent * bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u16 speciesId = OW_SPECIES(objectEvent); - bool8 collision; u8 movementActionId; if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) @@ -12007,17 +11999,15 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * return FALSE; } - collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) { u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - collision = GetCollisionInDirection(objectEvent, newDirection); - if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) { sCollisionTimer++; movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); @@ -12123,7 +12113,6 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve bool32 equalDistances = FALSE; u32 distance = OWE_GetApproachingMonDistanceToPlayer(objectEvent, &equalDistances); u16 speciesId = OW_SPECIES(objectEvent); - bool8 collision; u8 movementActionId; if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) @@ -12136,9 +12125,8 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve if (distance <= 1) { SetObjectEventDirection(objectEvent, GetOppositeDirection(objectEvent->movementDirection)); - collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); @@ -12146,8 +12134,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve newDirection = GetOppositeDirection(newDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - collision = GetCollisionInDirection(objectEvent, newDirection); - if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); } } @@ -12167,10 +12154,9 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve } else { - collision = GetCollisionInDirection(objectEvent, objectEvent->movementDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) { s16 x = objectEvent->currentCoords.x; s16 y = objectEvent->currentCoords.y; @@ -12184,9 +12170,8 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve } u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - collision = GetCollisionInDirection(objectEvent, newDirection); - if (OWE_CheckRestrictedMovement(objectEvent, newDirection) || collision) + if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5fc7b90cdbac..1db2234b1743 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -980,6 +980,9 @@ bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 directio { if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) return FALSE; + + if (GetCollisionInDirection(objectEvent, direction)) + return TRUE; // Returns TRUE if movement is restricted. return ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatile(objectEvent, direction)) From 14ac84a6e912d4f027eb04061d0270926ccdce7f Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 20 Jan 2026 10:47:51 -0600 Subject: [PATCH 345/572] Added restricted movement checks to OWE_CheckPathToPlayerFromCollision --- src/overworld_encounters.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 1db2234b1743..77a965885b1f 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -52,8 +52,8 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_DoesRoamerObjectExist(void); -static bool32 OWE_CheckRestrictMovementMetatile(struct ObjectEvent *objectEvent, u32 direction); -static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, u32 direction); +static bool32 OWE_CheckRestrictMovementMetatileAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xCurrent, u32 yCurrent); +static bool32 OWE_CheckRestrictMovementMapAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xCurrent, u32 yCurrent); static u32 GetNumActiveOverworldEncounters(void); static u32 GetNumActiveGeneratedOverworldEncounters(void); static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); @@ -985,8 +985,8 @@ bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 directio return TRUE; // Returns TRUE if movement is restricted. - return ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatile(objectEvent, direction)) - || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMap(objectEvent, direction))); + return ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(objectEvent, direction, objectEvent->currentCoords.x, objectEvent->currentCoords.y)) + || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(objectEvent, direction, objectEvent->currentCoords.x, objectEvent->currentCoords.y))); } void DespawnOldestOWE_Pal(void) @@ -1069,15 +1069,14 @@ bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon) static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, u32 newDirection) { - // TODO: Add handling for restricted movement. s16 x = mon->currentCoords.x; s16 y = mon->currentCoords.y; MoveCoords(newDirection, &x, &y); - if (!GetCollisionAtCoords(mon, x, y, newDirection)) + if (!GetCollisionAtCoords(mon, x, y, newDirection) && !(OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) && !(OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y))) { MoveCoords(mon->movementDirection, &x, &y); - if (!GetCollisionAtCoords(mon, x, y, mon->movementDirection)) + if (!GetCollisionAtCoords(mon, x, y, mon->movementDirection) && !(OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) && !(OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y))) return newDirection; } @@ -1085,10 +1084,10 @@ static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, u32 newDi y = mon->currentCoords.y; MoveCoords(GetOppositeDirection(newDirection), &x, &y); - if (!GetCollisionAtCoords(mon, x, y, GetOppositeDirection(newDirection))) + if (!GetCollisionAtCoords(mon, x, y, GetOppositeDirection(newDirection)) && !(OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) && !(OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y))) { MoveCoords(mon->movementDirection, &x, &y); - if (!GetCollisionAtCoords(mon, x, y, mon->movementDirection)) + if (!GetCollisionAtCoords(mon, x, y, mon->movementDirection) && !(OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) && !(OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y))) return GetOppositeDirection(newDirection); } @@ -1246,10 +1245,8 @@ static bool32 OWE_DoesRoamerObjectExist(void) return FALSE; } -static bool32 OWE_CheckRestrictMovementMetatile(struct ObjectEvent *objectEvent, u32 direction) +static bool32 OWE_CheckRestrictMovementMetatileAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xCurrent, u32 yCurrent) { - s16 xCurrent = objectEvent->currentCoords.x; - s16 yCurrent = objectEvent->currentCoords.y; s16 xNew = xCurrent + gDirectionToVectors[direction].x; s16 yNew = yCurrent + gDirectionToVectors[direction].y; u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); @@ -1270,10 +1267,8 @@ static bool32 OWE_CheckRestrictMovementMetatile(struct ObjectEvent *objectEvent, return TRUE; } -static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, u32 direction) +static bool32 OWE_CheckRestrictMovementMapAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xCurrent, u32 yCurrent) { - s16 xCurrent = objectEvent->currentCoords.x; - s16 yCurrent = objectEvent->currentCoords.y; s16 xNew = xCurrent + gDirectionToVectors[direction].x; s16 yNew = yCurrent + gDirectionToVectors[direction].y; u32 mapGroup = objectEvent->mapGroup; From d525e73624ce395deb2aa0b167bc389b6d8c2d91 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 20 Jan 2026 11:34:58 -0600 Subject: [PATCH 346/572] Fixed faulty restricted movement checks --- src/overworld_encounters.c | 51 +++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 77a965885b1f..cbb1a6499f49 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -52,8 +52,10 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_DoesRoamerObjectExist(void); -static bool32 OWE_CheckRestrictMovementMetatileAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xCurrent, u32 yCurrent); -static bool32 OWE_CheckRestrictMovementMapAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xCurrent, u32 yCurrent); +static bool32 OWE_CheckRestrictMovementMetatileInDirection(struct ObjectEvent *objectEvent, u32 direction); +static bool32 OWE_CheckRestrictMovementMapInDirection(struct ObjectEvent *objectEvent, u32 direction); +static bool32 OWE_CheckRestrictMovementMetatileAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xNew, u32 yNew); +static bool32 OWE_CheckRestrictMovementMapAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xNew, u32 yNew); static u32 GetNumActiveOverworldEncounters(void); static u32 GetNumActiveGeneratedOverworldEncounters(void); static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); @@ -985,8 +987,8 @@ bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 directio return TRUE; // Returns TRUE if movement is restricted. - return ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(objectEvent, direction, objectEvent->currentCoords.x, objectEvent->currentCoords.y)) - || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(objectEvent, direction, objectEvent->currentCoords.x, objectEvent->currentCoords.y))); + return ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileInDirection(objectEvent, direction)) + || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapInDirection(objectEvent, direction))); } void DespawnOldestOWE_Pal(void) @@ -1245,8 +1247,10 @@ static bool32 OWE_DoesRoamerObjectExist(void) return FALSE; } -static bool32 OWE_CheckRestrictMovementMetatileAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xCurrent, u32 yCurrent) +static bool32 OWE_CheckRestrictMovementMetatileInDirection(struct ObjectEvent *objectEvent, u32 direction) { + s16 xCurrent = objectEvent->currentCoords.x; + s16 yCurrent = objectEvent->currentCoords.y; s16 xNew = xCurrent + gDirectionToVectors[direction].x; s16 yNew = yCurrent + gDirectionToVectors[direction].y; u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); @@ -1267,8 +1271,10 @@ static bool32 OWE_CheckRestrictMovementMetatileAtCoords(struct ObjectEvent *obje return TRUE; } -static bool32 OWE_CheckRestrictMovementMapAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xCurrent, u32 yCurrent) +static bool32 OWE_CheckRestrictMovementMapInDirection(struct ObjectEvent *objectEvent, u32 direction) { + s16 xCurrent = objectEvent->currentCoords.x; + s16 yCurrent = objectEvent->currentCoords.y; s16 xNew = xCurrent + gDirectionToVectors[direction].x; s16 yNew = yCurrent + gDirectionToVectors[direction].y; u32 mapGroup = objectEvent->mapGroup; @@ -1280,6 +1286,39 @@ static bool32 OWE_CheckRestrictMovementMapAtCoords(struct ObjectEvent *objectEve return AreCoordsInsidePlayerMap(xNew, yNew); } +static bool32 OWE_CheckRestrictMovementMetatileAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xNew, u32 yNew) +{ + s16 xCurrent = xNew - gDirectionToVectors[direction].x; + s16 yCurrent = yNew - gDirectionToVectors[direction].y; + u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); + u32 metatileBehaviourNew = MapGridGetMetatileBehaviorAt(xNew, yNew); + + if (MetatileBehavior_IsLandWildEncounter(metatileBehaviourCurrent) + && MetatileBehavior_IsLandWildEncounter(metatileBehaviourNew)) + return FALSE; + + if (MetatileBehavior_IsWaterWildEncounter(metatileBehaviourCurrent) + && MetatileBehavior_IsWaterWildEncounter(metatileBehaviourNew)) + return FALSE; + + if (MetatileBehavior_IsIndoorEncounter(metatileBehaviourCurrent) + && MetatileBehavior_IsIndoorEncounter(metatileBehaviourNew)) + return FALSE; + + return TRUE; +} + +static bool32 OWE_CheckRestrictMovementMapAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xNew, u32 yNew) +{ + u32 mapGroup = objectEvent->mapGroup; + u32 mapNum = objectEvent->mapNum; + + if (mapGroup == gSaveBlock1Ptr->location.mapGroup && mapNum == gSaveBlock1Ptr->location.mapNum) + return !AreCoordsInsidePlayerMap(xNew, yNew); + else + return AreCoordsInsidePlayerMap(xNew, yNew); +} + static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(objectEvent) || OW_SPECIES(objectEvent) == SPECIES_NONE) From ebac69d0983b5716d4894ef066344d86c949716e Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 20 Jan 2026 11:35:33 -0600 Subject: [PATCH 347/572] Improved pathfinding for fleeing mons --- src/event_object_movement.c | 3 +++ src/overworld_encounters.c | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 40a4a30178ab..e5c7d2e32aa4 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -12004,6 +12004,9 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) { u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); + + if (newDirection != objectEvent->movementDirection) + newDirection = GetOppositeDirection(newDirection); movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index cbb1a6499f49..f37e5df1a48c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1077,6 +1077,9 @@ static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, u32 newDi MoveCoords(newDirection, &x, &y); if (!GetCollisionAtCoords(mon, x, y, newDirection) && !(OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) && !(OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y))) { + if (mon->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) + return GetOppositeDirection(newDirection); + MoveCoords(mon->movementDirection, &x, &y); if (!GetCollisionAtCoords(mon, x, y, mon->movementDirection) && !(OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) && !(OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y))) return newDirection; @@ -1088,6 +1091,9 @@ static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, u32 newDi MoveCoords(GetOppositeDirection(newDirection), &x, &y); if (!GetCollisionAtCoords(mon, x, y, GetOppositeDirection(newDirection)) && !(OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) && !(OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y))) { + if (mon->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) + return newDirection; + MoveCoords(mon->movementDirection, &x, &y); if (!GetCollisionAtCoords(mon, x, y, mon->movementDirection) && !(OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) && !(OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y))) return GetOppositeDirection(newDirection); From 1c46928250f63465ae88c717f0e31b72c9231d25 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:45:04 -0600 Subject: [PATCH 348/572] Check for player not moving doesn't work here --- src/overworld_encounters.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f37e5df1a48c..4913484ce470 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -701,7 +701,6 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam static bool8 IsSafeToSpawnObjectEvents(void) { - // Can this just be a check for player not moving? struct ObjectEvent* player = &gObjectEvents[gPlayerAvatar.objectEventId]; // Only spawn when player is at a valid tile position From 1072c57176dc76531da2bc8b2ec9fb6b01a340c8 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:59:53 -0600 Subject: [PATCH 349/572] Spawns faster when fewer active OWEs --- include/overworld_encounters.h | 7 +++++-- src/overworld_encounters.c | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 2bc7bc1a002a..520b1cef87e4 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -23,9 +23,12 @@ #define OWE_SPAWN_RADUIS_WIDTH (OWE_TOTAL_SPAWN_WIDTH - 1) / 2 // Distance from center to left/right edge (not including center). #define OWE_SPAWN_RADUIS_HEIGHT (OWE_TOTAL_SPAWN_HEIGHT - 1) / 2 // Distance from center to top/bottom edge (not including center). -#define OWE_TIME_BETWEEN_SPAWNS 60 // Minimum wait time (in frames) between spawns. -#define OWE_SPAWN_TIME_VARIABILITY 30 // A random number of frames between 0 and this value will be added to OWE_TIME_BETWEEN_SPAWNS every reset for variability. +// #define OWE_TIME_BETWEEN_SPAWNS 60 // Minimum wait time (in frames) between spawns. +// #define OWE_SPAWN_TIME_VARIABILITY 30 // A random number of frames between 0 and this value will be added to OWE_TIME_BETWEEN_SPAWNS every reset for variability. + #define OWE_SPAWN_TIME_MINIMUM 30 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. +#define OWE_SPAWN_TIME_PER_ACTIVE 30 // The number of frames that will be added to the countdown per currently active spawn. +#define OWE_SPAWN_TIME_REPLACEMENT 240 // The number of frames before an existing spawn will be replaced with a new one (requires OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT). #define OWE_MON_SIGHT_WIDTH 3 #define OWE_MON_SIGHT_LENGTH 4 diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 4913484ce470..88a58f38ca16 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -48,6 +48,7 @@ static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); static u32 GetSpawnSlotByLocalId(u32 localId); static void SortOWEMonAges(void); +static void OWE_SetNewSpawnCountdown(void); static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny); static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); @@ -181,8 +182,18 @@ void UpdateOverworldEncounters(void) if (shouldSpawnWaterMons) object->hideReflection = TRUE; - // Slower replacement spawning - sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); + OWE_SetNewSpawnCountdown(); + // sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); +} + +static void OWE_SetNewSpawnCountdown(void) +{ + u32 numActive = GetNumActiveGeneratedOverworldEncounters(); + + if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && numActive >= GetMaxOverworldEncounterSpawns()) + sOWESpawnCountdown = OWE_SPAWN_TIME_REPLACEMENT; + else + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); } static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny) From b7d081255a2a5bcd5d696557a1f1af0d5048138b Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:00:44 -0600 Subject: [PATCH 350/572] Set new OWE spawn countdown at encounter start --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 88a58f38ca16..7f7188f5406c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -984,6 +984,7 @@ void OverworldWildEncounter_RemoveObjectOnBattle(void) if (IsOverworldWildEncounter(object)) { RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); + OWE_SetNewSpawnCountdown(); gSpecialVar_LastTalked = LOCALID_NONE; } } From e3ccb9813a0634dce1fa88412f467356034df28c Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 21 Jan 2026 20:17:52 -0600 Subject: [PATCH 351/572] Check restricted movement before collision --- src/overworld_encounters.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7f7188f5406c..30316c1e97b3 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -989,17 +989,20 @@ void OverworldWildEncounter_RemoveObjectOnBattle(void) } } +// Returns TRUE if movement is restricted. bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction) { if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) return FALSE; + + if ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileInDirection(objectEvent, direction)) + || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapInDirection(objectEvent, direction))) + return TRUE; if (GetCollisionInDirection(objectEvent, direction)) return TRUE; - - // Returns TRUE if movement is restricted. - return ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileInDirection(objectEvent, direction)) - || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapInDirection(objectEvent, direction))); + + return FALSE; } void DespawnOldestOWE_Pal(void) From 5747111348d6a1e9a14f985469b18e11d38d55f6 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 21 Jan 2026 21:06:39 -0600 Subject: [PATCH 352/572] Fixed spawn anim palette loading --- data/field_effect_scripts.s | 9 +++++++-- include/constants/field_effects.h | 3 ++- src/event_object_movement.c | 8 ++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/data/field_effect_scripts.s b/data/field_effect_scripts.s index ac08d6bbe494..6fd448938756 100644 --- a/data/field_effect_scripts.s +++ b/data/field_effect_scripts.s @@ -84,7 +84,8 @@ gFieldEffectScriptPointers:: .4byte gFieldEffectScript_UseRockClimb @ FLDEFF_USE_ROCK_CLIMB .4byte gFieldEffectScript_RockClimbDust @ FLDEFF_ROCK_CLIMB_DUST .4byte gFieldEffectScript_ORASDowse @ FLDEFF_ORAS_DOWSE - .4byte gFieldEffectScript_OWE_SpawnAnim @ FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_ANIM + .4byte gFieldEffectScript_OWE_SpawnAnim0 @ FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_0 + .4byte gFieldEffectScript_OWE_SpawnAnim1 @ FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_1 gFieldEffectScript_ExclamationMarkIcon1:: field_eff_callnative FldEff_ExclamationMarkIcon @@ -304,10 +305,14 @@ gFieldEffectScript_Bubbles:: field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect0, FldEff_Bubbles field_eff_end -gFieldEffectScript_OWE_SpawnAnim:: +gFieldEffectScript_OWE_SpawnAnim0:: field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect0, FldEff_OWE_SpawnAnim field_eff_end +gFieldEffectScript_OWE_SpawnAnim1:: + field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_OWE_SpawnAnim + field_eff_end + gFieldEffectScript_Sparkle:: field_eff_loadfadedpal_callnative gSpritePalette_SmallSparkle, FldEff_Sparkle field_eff_end diff --git a/include/constants/field_effects.h b/include/constants/field_effects.h index 51bfb6762765..b9718ee87574 100644 --- a/include/constants/field_effects.h +++ b/include/constants/field_effects.h @@ -80,7 +80,8 @@ #define FLDEFF_USE_ROCK_CLIMB 75 #define FLDEFF_ROCK_CLIMB_DUST 76 #define FLDEFF_ORAS_DOWSE 77 -#define FLDEFF_OW_ENCOUNTER_SPAWN_ANIM 78 +#define FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_0 78 +#define FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_1 79 #define FLDEFFOBJ_SHADOW_S 0 #define FLDEFFOBJ_SHADOW_M 1 diff --git a/src/event_object_movement.c b/src/event_object_movement.c index e5c7d2e32aa4..6d9fd7f6361a 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11782,9 +11782,13 @@ bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim sp { gFieldEffectArguments[0] = objEvent->currentCoords.x; gFieldEffectArguments[1] = objEvent->currentCoords.y; - gFieldEffectArguments[2] = gSprites[objEvent->spriteId].oam.priority + 1; gFieldEffectArguments[3] = spawnAnimType; - FieldEffectStart(FLDEFF_OW_ENCOUNTER_SPAWN_ANIM); + + if (spawnAnimType == OWE_SPAWN_ANIM_GRASS || spawnAnimType == OWE_SPAWN_ANIM_LONG_GRASS) + FieldEffectStart(FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_1); + else + FieldEffectStart(FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_0); + return TRUE; } From 6698433d8196a28efac3d8e77eff1b476a5ace83 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 21 Jan 2026 21:08:59 -0600 Subject: [PATCH 353/572] Fixed shiny sparkle anim (spritesheet_rules.mk) --- spritesheet_rules.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spritesheet_rules.mk b/spritesheet_rules.mk index a14fd188137a..95ef1b5f2eae 100644 --- a/spritesheet_rules.mk +++ b/spritesheet_rules.mk @@ -747,6 +747,9 @@ $(FLDEFFGFXDIR)/secret_power_tree.4bpp: %.4bpp: %.png $(FLDEFFGFXDIR)/record_mix_lights.4bpp: %.4bpp: %.png $(GFX) $< $@ -mwidth 4 -mheight 1 +$(FLDEFFGFXDIR)/shiny_sparkle.4bpp: %.4bpp: %.png + $(GFX) $< $@ -mwidth 2 -mheight 4 + $(POKEMONGFXDIR)/question_mark/overworld.4bpp: %.4bpp: %.png $(GFX) $< $@ -mwidth 4 -mheight 4 From de547993f51bf8b640c1012938e3ff5fc97accb5 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 21 Jan 2026 21:11:59 -0600 Subject: [PATCH 354/572] Removed leftover spawn countdown stuff --- include/overworld_encounters.h | 3 --- src/overworld_encounters.c | 1 - 2 files changed, 4 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 520b1cef87e4..7ecca1224308 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -23,9 +23,6 @@ #define OWE_SPAWN_RADUIS_WIDTH (OWE_TOTAL_SPAWN_WIDTH - 1) / 2 // Distance from center to left/right edge (not including center). #define OWE_SPAWN_RADUIS_HEIGHT (OWE_TOTAL_SPAWN_HEIGHT - 1) / 2 // Distance from center to top/bottom edge (not including center). -// #define OWE_TIME_BETWEEN_SPAWNS 60 // Minimum wait time (in frames) between spawns. -// #define OWE_SPAWN_TIME_VARIABILITY 30 // A random number of frames between 0 and this value will be added to OWE_TIME_BETWEEN_SPAWNS every reset for variability. - #define OWE_SPAWN_TIME_MINIMUM 30 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. #define OWE_SPAWN_TIME_PER_ACTIVE 30 // The number of frames that will be added to the countdown per currently active spawn. #define OWE_SPAWN_TIME_REPLACEMENT 240 // The number of frames before an existing spawn will be replaced with a new one (requires OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT). diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 30316c1e97b3..5bf93e8314ca 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -183,7 +183,6 @@ void UpdateOverworldEncounters(void) object->hideReflection = TRUE; OWE_SetNewSpawnCountdown(); - // sOWESpawnCountdown = OWE_TIME_BETWEEN_SPAWNS + (Random() % OWE_SPAWN_TIME_VARIABILITY); } static void OWE_SetNewSpawnCountdown(void) From f1ee436e1d33009f2ed3d1884fd6b66e6a6f098c Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 21 Jan 2026 21:42:30 -0600 Subject: [PATCH 355/572] Remove all generated OWEs on dexnav search --- include/overworld_encounters.h | 2 +- src/dexnav.c | 3 +-- src/overworld_encounters.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 7ecca1224308..ec785e45fd6d 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -131,7 +131,7 @@ bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); void Task_OWE_WaitMovements(u8 taskId); enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); -void OverworldWildEncounter_FreezeAllObjects(void); +void UNUSED_OverworldWildEncounter_FreezeAllObjects(void); bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *objectEvent); bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void); diff --git a/src/dexnav.c b/src/dexnav.c index deb977889996..b511eb0e9077 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -745,6 +745,7 @@ static bool8 TryStartHiddenMonFieldEffect(enum EncounterType environment, u8 xSi if (fldEffId != 0) { + RemoveAllGeneratedOverworldEncounterObjects(); gFieldEffectArguments[0] = sDexNavSearchDataPtr->tileX; gFieldEffectArguments[1] = sDexNavSearchDataPtr->tileY; gFieldEffectArguments[2] = 0xFF; // subpriority @@ -847,7 +848,6 @@ static bool8 InitDexNavSearch(u32 species, u32 environment) return TRUE; } FlagSet(DN_FLAG_SEARCHING); - OverworldWildEncounter_FreezeAllObjects(); // assign non-objects to struct sDexNavSearchDataPtr->species = species; @@ -2567,7 +2567,6 @@ bool32 TryFindHiddenPokemon(void) sDexNavSearchDataPtr = AllocZeroed(sizeof(struct DexNavSearch)); FlagSet(DN_FLAG_SEARCHING); - OverworldWildEncounter_FreezeAllObjects(); // init search data sDexNavSearchDataPtr->isHiddenMon = isHiddenMon; sDexNavSearchDataPtr->species = species; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5bf93e8314ca..df30ebb89b14 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1352,7 +1352,7 @@ static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) return OW_WILD_ENCOUNTERS_DESPAWN_SOUND; } -void OverworldWildEncounter_FreezeAllObjects(void) +void UNUSED_OverworldWildEncounter_FreezeAllObjects(void) { for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { From c9018bd7743df20f6adaabb2cccf28c8b05438a8 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 21 Jan 2026 22:29:20 -0600 Subject: [PATCH 356/572] Don't wait for movement delay during wander when starting encounter --- src/event_object_movement.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 6d9fd7f6361a..5d36e49a4b5b 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11798,13 +11798,6 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent { if (!ObjectEventExecSingleMovementAction(objectEvent, sprite)) return FALSE; - - if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) - { - MoveToPlayerForEncounter(objectEvent, sprite); - sprite->sTypeFuncId = 6; - return FALSE; - } SetMovementDelay(sprite, sMovementDelaysOWE[Random() % ARRAY_COUNT(sMovementDelaysOWE)]); sprite->sTypeFuncId = 3; @@ -11813,6 +11806,14 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent bool8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite) { + if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) + { + ClearObjectEventMovement(objectEvent, sprite); + MoveToPlayerForEncounter(objectEvent, sprite); + sprite->sTypeFuncId = 6; + return FALSE; + } + if (WaitForMovementDelay(sprite)) { // resets a mid-movement sprite From 4addcf4fb876f1f3a257404d94aa2397faff4875 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 21 Jan 2026 22:47:25 -0600 Subject: [PATCH 357/572] Added config to allow instant battle start --- include/config/overworld.h | 1 + src/overworld_encounters.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/include/config/overworld.h b/include/config/overworld.h index 2dd4e77dc822..0bab255b8225 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -83,6 +83,7 @@ #define OW_WILD_ENCOUNTERS_AMBIENT_CRIES TRUE // If TRUE, generated overworld encounters will play ambient cries based on their location relative to the player. #define OW_WILD_ENCOUNTERS_FEEBAS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. #define OW_WILD_ENCOUNTERS_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an overworld wild encounter despawns. +#define OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE TRUE // If TRUE, the mon will take steps to be right next to the player before the battle starts. // Out-of-battle Ability effects #define OW_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8+, if a Pokémon with Synchronize leads the party, wild Pokémon will always have their same Nature as opposed to the 50% chance in previous games. Gift Pokémon excluded. diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index df30ebb89b14..ece58f506fd6 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -69,6 +69,7 @@ static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); +static void OWE_StartEncounterInstant(struct ObjectEvent *mon); void OWE_ResetSpawnCounterPlayAmbientCry(void) { @@ -971,6 +972,9 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; LockPlayerFieldControls(); + if (!OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE) + OWE_StartEncounterInstant(wildMon); + wildMon->trainerRange_berryTreeId |= OWE_FLAG_START_ENCOUNTER; } } @@ -1406,6 +1410,14 @@ static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumActiveGeneratedOverworldEncounters() == GetMaxOverworldEncounterSpawns(); } +void OWE_StartEncounterInstant(struct ObjectEvent *mon) +{ + gSpecialVar_LastTalked = mon->localId; + gSpecialVar_0x8004 = OW_SPECIES(mon); + ScriptContext_SetupScript(InteractWithDynamicWildOverworldEncounter); + FreezeObjectEvents(); +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From 7b4e0d14beaddc8d8d9d11ef72c2b7616095849e Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:51:51 -0600 Subject: [PATCH 358/572] OWEs no longer impede trainer sight --- src/trainer_see.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/trainer_see.c b/src/trainer_see.c index 5e6c9de31fc8..b2f1e0fcd418 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -647,6 +647,17 @@ static u8 CheckPathBetweenTrainerAndPlayer(struct ObjectEvent *trainerObj, u8 ap { // Check for collisions on approach, ignoring the "out of range" collision for regular movement collision = GetCollisionFlagsAtCoords(trainerObj, x, y, direction); + if ((collision & (1 << (COLLISION_OBJECT_EVENT - 1)))) + { + u32 objectId = GetObjectEventIdByXY(x, y); + + if (gObjectEvents[objectId].trainerType == TRAINER_TYPE_ENCOUNTER) + { + RemoveObjectEventByLocalIdAndMap(gObjectEvents[objectId].localId, gObjectEvents[objectId].mapNum, gObjectEvents[objectId].mapGroup); + collision &= 1 << (COLLISION_OBJECT_EVENT - 1); + } + } + if (collision != 0 && (collision & ~(1 << (COLLISION_OUTSIDE_RANGE - 1)))) return 0; } From fa94211d848e4206d908a65e070dff054f220685 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 22 Jan 2026 11:14:21 -0600 Subject: [PATCH 359/572] Normal NPCs despawn OWEs when walking into them --- src/event_object_movement.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 5d36e49a4b5b..7d44350d75a0 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -6478,6 +6478,12 @@ u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, b { if (AreElevationsCompatible(objectEvent->currentElevation, curObject->currentElevation)) { + // Despawn the OW encounter if an NPC tries to path into it. + if (curObject->trainerType == TRAINER_TYPE_ENCOUNTER && !objectEvent->isPlayer && objectEvent->trainerType != TRAINER_TYPE_ENCOUNTER) + { + RemoveObjectEventByLocalIdAndMap(curObject->localId, curObject->mapNum, curObject->mapGroup); + continue; + } OWE_TryTriggerEncounter(objectEvent, curObject); return i; } From b4ec13366d60879d38f2732bbeeab3487f08dcbb Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 17:02:22 +0000 Subject: [PATCH 360/572] Remove Unused --- src/sprite.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sprite.c b/src/sprite.c index ed55c218ad29..a875b946e6e2 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -683,7 +683,6 @@ s16 AllocSpriteTiles(u16 tileCount) bool32 CanAllocSpriteTiles(u16 tileCount) { u16 i; - s16 start; u16 numTilesFound; if (tileCount == 0) @@ -700,7 +699,6 @@ bool32 CanAllocSpriteTiles(u16 tileCount) return FALSE; } - start = i; numTilesFound = 1; while (numTilesFound != tileCount) From cf50a407ae1c6c43fe80d7fef7efe0d801104ac8 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 17:23:29 +0000 Subject: [PATCH 361/572] Refactor Field Effect Palette Loading This reverts commit 5747111348d6a1e9a14f985469b18e11d38d55f6. --- data/field_effect_scripts.s | 11 +++-------- include/constants/field_effects.h | 3 +-- include/event_object_movement.h | 3 +++ include/field_effect.h | 3 +++ src/event_object_movement.c | 8 ++------ src/field_effect.c | 7 +++++++ src/field_effect_helpers.c | 4 ++++ 7 files changed, 23 insertions(+), 16 deletions(-) diff --git a/data/field_effect_scripts.s b/data/field_effect_scripts.s index 6fd448938756..c4698931865c 100644 --- a/data/field_effect_scripts.s +++ b/data/field_effect_scripts.s @@ -84,8 +84,7 @@ gFieldEffectScriptPointers:: .4byte gFieldEffectScript_UseRockClimb @ FLDEFF_USE_ROCK_CLIMB .4byte gFieldEffectScript_RockClimbDust @ FLDEFF_ROCK_CLIMB_DUST .4byte gFieldEffectScript_ORASDowse @ FLDEFF_ORAS_DOWSE - .4byte gFieldEffectScript_OWE_SpawnAnim0 @ FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_0 - .4byte gFieldEffectScript_OWE_SpawnAnim1 @ FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_1 + .4byte gFieldEffectScript_OWE_SpawnAnim @ FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_ANIM gFieldEffectScript_ExclamationMarkIcon1:: field_eff_callnative FldEff_ExclamationMarkIcon @@ -305,12 +304,8 @@ gFieldEffectScript_Bubbles:: field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect0, FldEff_Bubbles field_eff_end -gFieldEffectScript_OWE_SpawnAnim0:: - field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect0, FldEff_OWE_SpawnAnim - field_eff_end - -gFieldEffectScript_OWE_SpawnAnim1:: - field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_OWE_SpawnAnim +gFieldEffectScript_OWE_SpawnAnim:: + field_eff_callnative FldEff_OWE_SpawnAnim field_eff_end gFieldEffectScript_Sparkle:: diff --git a/include/constants/field_effects.h b/include/constants/field_effects.h index b9718ee87574..51bfb6762765 100644 --- a/include/constants/field_effects.h +++ b/include/constants/field_effects.h @@ -80,8 +80,7 @@ #define FLDEFF_USE_ROCK_CLIMB 75 #define FLDEFF_ROCK_CLIMB_DUST 76 #define FLDEFF_ORAS_DOWSE 77 -#define FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_0 78 -#define FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_1 79 +#define FLDEFF_OW_ENCOUNTER_SPAWN_ANIM 78 #define FLDEFFOBJ_SHADOW_S 0 #define FLDEFFOBJ_SHADOW_M 1 diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 5df7e6b756e0..4d53d95a34cb 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -124,6 +124,9 @@ extern const u8 gReflectionEffectPaletteMap[]; extern const struct SpriteFrameImage *const gBerryTreePicTablePointers[]; extern const u8 *const gBerryTreePaletteSlotTablePointers[]; +extern const struct SpritePalette gSpritePalette_GeneralFieldEffect0; +extern const struct SpritePalette gSpritePalette_GeneralFieldEffect1; + void ResetObjectEvents(void); u8 GetMoveDirectionAnimNum(u8 direction); u8 GetObjectEventIdByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroupId); diff --git a/include/field_effect.h b/include/field_effect.h index 0bfe15308b20..91dacd0af2b5 100644 --- a/include/field_effect.h +++ b/include/field_effect.h @@ -1,6 +1,8 @@ #ifndef GUARD_FIELD_EFFECTS_H #define GUARD_FIELD_EFFECTS_H +#include "field_weather.h" + extern const struct SpritePalette gNewGameBirchObjectPaletteInfo; extern const struct SpriteTemplate gNewGameBirchObjectTemplate; extern const struct OamData gNewGameBirchOamAttributes; @@ -23,6 +25,7 @@ void MultiplyInvertedPaletteRGBComponents(u16 i, u8 r, u8 g, u8 b); void FieldEffectActiveListAdd(u8 id); void FieldEffectScript_LoadTiles(u8 **script); void FieldEffectScript_LoadFadedPalette(u8 **script); +void FieldEffect_LoadFadedPalette(struct SpritePalette *palette, enum ColorMapType colorMap); void FieldEffectScript_LoadPalette(u8 **script); void FieldEffectScript_CallNative(u8 **script, u32 *val); void FieldEffectFreeGraphicsResources(struct Sprite *sprite); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 038b960f8835..9660c22f6643 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11788,13 +11788,9 @@ bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim sp { gFieldEffectArguments[0] = objEvent->currentCoords.x; gFieldEffectArguments[1] = objEvent->currentCoords.y; + gFieldEffectArguments[2] = gSprites[objEvent->spriteId].oam.priority + 1; gFieldEffectArguments[3] = spawnAnimType; - - if (spawnAnimType == OWE_SPAWN_ANIM_GRASS || spawnAnimType == OWE_SPAWN_ANIM_LONG_GRASS) - FieldEffectStart(FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_1); - else - FieldEffectStart(FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_0); - + FieldEffectStart(FLDEFF_OW_ENCOUNTER_SPAWN_ANIM); return TRUE; } diff --git a/src/field_effect.c b/src/field_effect.c index e72bbecc14a2..4db91aa8e5d1 100644 --- a/src/field_effect.c +++ b/src/field_effect.c @@ -807,6 +807,13 @@ void FieldEffectScript_LoadFadedPalette(u8 **script) (*script)++; } +void FieldEffect_LoadFadedPalette(struct SpritePalette *palette, enum ColorMapType colorMap) +{ + u32 paletteSlot = LoadSpritePalette(palette); + SetPaletteColorMapType(paletteSlot + 16, colorMap); + UpdateSpritePaletteWithWeather(paletteSlot, TRUE); +} + void FieldEffectScript_LoadPalette(u8 **script) { struct SpritePalette *palette = (struct SpritePalette *)FieldEffectScript_ReadWord(script); diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 4679f2585795..644243aa2d4b 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1521,6 +1521,7 @@ u32 FldEff_OWE_SpawnAnim(void) u8 spriteId; u8 visual; s16 xOffset, yOffset; + struct SpritePalette palette = gSpritePalette_GeneralFieldEffect0; switch (gFieldEffectArguments[3]) { @@ -1528,12 +1529,14 @@ u32 FldEff_OWE_SpawnAnim(void) visual = FLDEFFOBJ_JUMP_TALL_GRASS; xOffset = 0; yOffset = 8; + palette = gSpritePalette_GeneralFieldEffect1; break; case OWE_SPAWN_ANIM_LONG_GRASS: visual = FLDEFFOBJ_JUMP_LONG_GRASS; xOffset = 0; yOffset = 0; + palette = gSpritePalette_GeneralFieldEffect1; break; case OWE_SPAWN_ANIM_WATER: @@ -1562,6 +1565,7 @@ u32 FldEff_OWE_SpawnAnim(void) break; } + FieldEffect_LoadFadedPalette(&palette, COLOR_MAP_DARK_CONTRAST); SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 0); spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[visual], gFieldEffectArguments[0] + xOffset, gFieldEffectArguments[1] + yOffset, 82); if (spriteId != MAX_SPRITES) From 4af1b7ae661cc7e66039da6a198477904d52da80 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 18:15:22 +0000 Subject: [PATCH 362/572] Add comment and idea? --- src/overworld_encounters.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 6172e82e2ef1..694b75a779bc 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -196,6 +196,7 @@ static void OWE_SetNewSpawnCountdown(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); } +#define OWE_FIELD_EFFECT_TILE_NUM 16 // Number of tiiles to add for field effect spawning static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny) { u32 numFreePalSlots = CountFreePaletteSlots(); @@ -239,6 +240,7 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is tileCount *= frames; } + tileCount += OWE_FIELD_EFFECT_TILE_NUM; if (!CanAllocSpriteTiles(tileCount)) { DebugPrintf("\n\nNO SPAWN\nSpecies: %S\nSheet Tile Count: %d", GetSpeciesName(speciesId), tileCount); From ec64d6a914bb2adca1100bddaf2e28d791111356 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 18:34:33 +0000 Subject: [PATCH 363/572] Despawn Mon Helper Functions --- include/overworld_encounters.h | 2 ++ src/event_object_movement.c | 7 ++----- src/overworld_encounters.c | 24 ++++++++++++++++++++++++ src/trainer_see.c | 12 +----------- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 595e91e2bdde..ae94f5ecff38 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -130,5 +130,7 @@ enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavio void UNUSED_OverworldWildEncounter_FreezeAllObjects(void); bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *objectEvent); bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void); +bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); +u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 9660c22f6643..4c0b5afc900a 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -6478,12 +6478,9 @@ u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, b { if (AreElevationsCompatible(objectEvent->currentElevation, curObject->currentElevation)) { - // Despawn the OW encounter if an NPC tries to path into it. - if (curObject->trainerType == TRAINER_TYPE_ENCOUNTER && !objectEvent->isPlayer && objectEvent->trainerType != TRAINER_TYPE_ENCOUNTER) - { - RemoveObjectEventByLocalIdAndMap(curObject->localId, curObject->mapNum, curObject->mapGroup); + if (OWE_DespawnMonDueToNPCCollision(curObject, objectEvent)) continue; - } + OWE_TryTriggerEncounter(objectEvent, curObject); return i; } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 694b75a779bc..5bb15b583c80 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1453,6 +1453,30 @@ void OWE_StartEncounterInstant(struct ObjectEvent *mon) FreezeObjectEvents(); } +bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent) +{ + if (!IsGeneratedOverworldWildEncounter(curObject) || IsOverworldWildEncounter(objectEvent)) + return FALSE; + + RemoveObjectEventByLocalIdAndMap(curObject->localId, curObject->mapNum, curObject->mapGroup); + return TRUE; +} + +u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y) +{ + if ((collision & (1 << (COLLISION_OBJECT_EVENT - 1)))) + { + struct ObjectEvent *objectEvent = &gObjectEvents[GetObjectEventIdByXY(x, y)]; + if (IsGeneratedOverworldWildEncounter(objectEvent)) + { + RemoveObjectEventByLocalIdAndMap(objectEvent->localId, objectEvent->mapNum, objectEvent->mapGroup); + collision &= 1 << (COLLISION_OBJECT_EVENT - 1); + } + } + + return collision; +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus diff --git a/src/trainer_see.c b/src/trainer_see.c index b2f1e0fcd418..70b4b76622d4 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -647,17 +647,7 @@ static u8 CheckPathBetweenTrainerAndPlayer(struct ObjectEvent *trainerObj, u8 ap { // Check for collisions on approach, ignoring the "out of range" collision for regular movement collision = GetCollisionFlagsAtCoords(trainerObj, x, y, direction); - if ((collision & (1 << (COLLISION_OBJECT_EVENT - 1)))) - { - u32 objectId = GetObjectEventIdByXY(x, y); - - if (gObjectEvents[objectId].trainerType == TRAINER_TYPE_ENCOUNTER) - { - RemoveObjectEventByLocalIdAndMap(gObjectEvents[objectId].localId, gObjectEvents[objectId].mapNum, gObjectEvents[objectId].mapGroup); - collision &= 1 << (COLLISION_OBJECT_EVENT - 1); - } - } - + collision = OWE_DespawnMonDueToTrainerSight(collision, x, y); if (collision != 0 && (collision & ~(1 << (COLLISION_OUTSIDE_RANGE - 1)))) return 0; } From 8f163fd5c86f89cd81bef7aded1f4172bb1a9dfe Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 18:43:57 +0000 Subject: [PATCH 364/572] Remove Unused Function --- include/overworld_encounters.h | 1 - src/overworld_encounters.c | 9 --------- 2 files changed, 10 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index ae94f5ecff38..19741fcf9cf4 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -127,7 +127,6 @@ bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); void Task_OWE_WaitMovements(u8 taskId); enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); -void UNUSED_OverworldWildEncounter_FreezeAllObjects(void); bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *objectEvent); bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void); bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5bb15b583c80..553c4c5997d5 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1389,16 +1389,7 @@ static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) return FALSE; return OW_WILD_ENCOUNTERS_DESPAWN_SOUND; -} -void UNUSED_OverworldWildEncounter_FreezeAllObjects(void) -{ - for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) - { - struct ObjectEvent *objectEvent = &gObjectEvents[i]; - if (IsOverworldWildEncounter(objectEvent)) - FreezeObjectEvent(objectEvent); - } } static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index) From 617c787d5c19c2f5f78b7f0d84febbcf77e85cd5 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 18:50:44 +0000 Subject: [PATCH 365/572] silly mistake --- data/field_effect_scripts.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/field_effect_scripts.s b/data/field_effect_scripts.s index c4698931865c..b6e2720d0d9a 100644 --- a/data/field_effect_scripts.s +++ b/data/field_effect_scripts.s @@ -84,7 +84,7 @@ gFieldEffectScriptPointers:: .4byte gFieldEffectScript_UseRockClimb @ FLDEFF_USE_ROCK_CLIMB .4byte gFieldEffectScript_RockClimbDust @ FLDEFF_ROCK_CLIMB_DUST .4byte gFieldEffectScript_ORASDowse @ FLDEFF_ORAS_DOWSE - .4byte gFieldEffectScript_OWE_SpawnAnim @ FLDEFF_OW_ENCOUNTER_SPAWN_ANIM_ANIM + .4byte gFieldEffectScript_OWE_SpawnAnim @ FLDEFF_OW_ENCOUNTER_SPAWN_ANIM gFieldEffectScript_ExclamationMarkIcon1:: field_eff_callnative FldEff_ExclamationMarkIcon From c86f58c669102239b08b0f54da6d5af22b9fb766 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 19:07:56 +0000 Subject: [PATCH 366/572] Cleanup Spawn Anim Function --- src/field_effect_helpers.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 644243aa2d4b..283c4b52a459 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1520,59 +1520,53 @@ u32 FldEff_OWE_SpawnAnim(void) { u8 spriteId; u8 visual; - s16 xOffset, yOffset; + u8 subpriority = 0; + s16 xOffset = 0, yOffset = 0; struct SpritePalette palette = gSpritePalette_GeneralFieldEffect0; switch (gFieldEffectArguments[3]) { case OWE_SPAWN_ANIM_GRASS: visual = FLDEFFOBJ_JUMP_TALL_GRASS; - xOffset = 0; yOffset = 8; palette = gSpritePalette_GeneralFieldEffect1; break; case OWE_SPAWN_ANIM_LONG_GRASS: visual = FLDEFFOBJ_JUMP_LONG_GRASS; - xOffset = 0; - yOffset = 0; palette = gSpritePalette_GeneralFieldEffect1; break; case OWE_SPAWN_ANIM_WATER: visual = FLDEFFOBJ_JUMP_BIG_SPLASH; - xOffset = 0; yOffset = 8; break; case OWE_SPAWN_ANIM_UNDERWATER: visual = FLDEFFOBJ_BUBBLES; - xOffset = 0; - yOffset = 0; + subpriority = 82; break; case OWE_SPAWN_ANIM_CAVE: visual = FLDEFFOBJ_GROUND_IMPACT_DUST; - xOffset = 0; yOffset = 8; break; case OWE_SPAWN_ANIM_SHINY: default: visual = FLDEFFOBJ_SHINY_SPARKLE; - xOffset = 0; - yOffset = 0; + subpriority = 82; break; } FieldEffect_LoadFadedPalette(&palette, COLOR_MAP_DARK_CONTRAST); SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 0); - spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[visual], gFieldEffectArguments[0] + xOffset, gFieldEffectArguments[1] + yOffset, 82); + spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[visual], gFieldEffectArguments[0] + xOffset, gFieldEffectArguments[1] + yOffset, subpriority); if (spriteId != MAX_SPRITES) { struct Sprite *sprite = &gSprites[spriteId]; sprite->coordOffsetEnabled = TRUE; - sprite->oam.priority = 1; + sprite->oam.priority = gFieldEffectArguments[2]; } return spriteId; } From e5813c54972fa2f15ed85d3d6660247111ddd49b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 19:23:22 +0000 Subject: [PATCH 367/572] Upcoming Merge Fixes --- include/event_object_movement.h | 2 +- src/event_object_movement.c | 2 ++ src/overworld_encounters.c | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index c68618cd5b3d..2ffd73c24a4a 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -556,6 +556,6 @@ u8 GetObjectEventApricornTreeId(u8 objectEventId); bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); -extern const u8 gStandardDirections[]; +extern const enum Direction gStandardDirections[]; #endif //GUARD_EVENT_OBJECT_MOVEMENT_H diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 83d43fb2b544..24e84c22dd41 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -10047,6 +10047,8 @@ void ObjectEventTurnToObject(struct ObjectEvent *objectOne, struct ObjectEvent * case DIR_EAST: objectDirOne = DIR_WEST; break; + default: + break; } ObjectEventTurn(objectOne, objectDirOne); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 553c4c5997d5..d68f8d92587a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -399,6 +399,8 @@ static bool8 TrySelectTile(s16* outX, s16* outY) if(y == 0 && x < 0) y = 1; break; + default: + break; } PlayerGetDestCoords(&playerX, &playerY); From 41cfc8b20d448cff1993eb0b152c8820dec53db0 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 21:37:19 +0000 Subject: [PATCH 368/572] Refactor Semi and Manual OWEs --- src/overworld_encounters.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d68f8d92587a..4545152ca981 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -148,7 +148,7 @@ void UpdateOverworldEncounters(void) .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), .movementType = OWE_GetMovementTypeFromSpecies(speciesId), - .trainerType = TRAINER_TYPE_ENCOUNTER, + .trainerType = TRAINER_TYPE_NONE, // Not set in template to avoid check in TryGetObjectEventTemplateForOverworldEncounter }; if (!OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) @@ -170,6 +170,7 @@ void UpdateOverworldEncounters(void) } object = &gObjectEvents[objectEventId]; + object->trainerType = TRAINER_TYPE_ENCOUNTER; object->disableCoveringGroundEffects = TRUE; object->sOverworldEncounterLevel = level; object->sRoamerOutbreakStatus = indexRoamerOutbreak; @@ -939,17 +940,17 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) { - if (!IsSemiManualOverworldWildEncounter(template->graphicsId, template->trainerType)) + if (template->trainerType != TRAINER_TYPE_ENCOUNTER) return *template; struct ObjectEventTemplate templateOWE = *template; // Does this work? u32 graphicsId; - u16 speciesId; - bool32 isShiny = FALSE; - bool32 isFemale = FALSE; - u32 level; + u16 speciesId, speciesTemplate = SanitizeSpeciesId(templateOWE.graphicsId & OBJ_EVENT_MON_SPECIES_MASK); + bool32 isShiny = FALSE, isShinyTemplate = (templateOWE.graphicsId & OBJ_EVENT_MON_SHINY) ? TRUE : FALSE; + bool32 isFemale = FALSE, isFemaleTemplate = (templateOWE.graphicsId & OBJ_EVENT_MON_FEMALE) ? TRUE : FALSE; + u32 level, levelTemplate = templateOWE.sOverworldEncounterLevel; u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; u32 x = template->x; u32 y = template->y; @@ -964,11 +965,28 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( &indexRoamerOutbreak ); + if (speciesTemplate) + speciesId = speciesTemplate; + + if (isShinyTemplate) + isShiny = isShinyTemplate; + + if (isFemaleTemplate) + isFemale = isFemaleTemplate; + + if (levelTemplate) + level = levelTemplate; + + if (templateOWE.movementType == MOVEMENT_TYPE_NONE) + templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); + assertf(speciesId != SPECIES_NONE && speciesId < NUM_SPECIES && IsSpeciesEnabled(speciesId), "invalid semi-manual overworld encounter\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, x, y) { // Currently causes assertf on each player step as function is called. templateOWE.graphicsId = OBJ_EVENT_GFX_BOY_1; templateOWE.trainerType = TRAINER_TYPE_NONE; + templateOWE.sOverworldEncounterLevel = 0; + templateOWE.movementType = MOVEMENT_TYPE_NONE; return templateOWE; } @@ -980,8 +998,6 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( templateOWE.graphicsId = graphicsId; templateOWE.sOverworldEncounterLevel = level; - if (templateOWE.movementType == MOVEMENT_TYPE_NONE) - templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); return templateOWE; } From fe4ad2f5517b364a91edac54349f30e91561a8de Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 21:37:24 +0000 Subject: [PATCH 369/572] Missed Alias --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 4545152ca981..4dac2272c7dd 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1020,7 +1020,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c if (!OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE) OWE_StartEncounterInstant(wildMon); - wildMon->trainerRange_berryTreeId |= OWE_FLAG_START_ENCOUNTER; + wildMon->sOverworldEncounterLevel |= OWE_FLAG_START_ENCOUNTER; } } From 7a7771a20b7441a2e285e71440d1c5f19efbe90d Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 21:46:24 +0000 Subject: [PATCH 370/572] Add P_GENDER_DIFFERENCES Preproc --- src/overworld_encounters.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 4dac2272c7dd..7b96415d3ca7 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -203,9 +203,10 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is u32 numFreePalSlots = CountFreePaletteSlots(); u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); - // Need Preproc checks for overworldShinyPaletteFemale +#if P_GENDER_DIFFERENCES if (isFemale && gSpeciesInfo[speciesId].overworldShinyPaletteFemale != NULL) tag += OBJ_EVENT_MON_FEMALE; +#endif // We need at least 2 pal slots open. One for the object and one for the spawn field effect. // Add this and tiles to seperate graphics check function From e504ba227553a3b2992b90f6439de864cb62fbb5 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 21:56:16 +0000 Subject: [PATCH 371/572] Missed Player Object Check --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7b96415d3ca7..04befe722130 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1465,7 +1465,7 @@ void OWE_StartEncounterInstant(struct ObjectEvent *mon) bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent) { - if (!IsGeneratedOverworldWildEncounter(curObject) || IsOverworldWildEncounter(objectEvent)) + if (!IsGeneratedOverworldWildEncounter(curObject) || IsOverworldWildEncounter(objectEvent) || objectEvent->isPlayer) return FALSE; RemoveObjectEventByLocalIdAndMap(curObject->localId, curObject->mapNum, curObject->mapGroup); From 5f41a3acc23d125ac01d0ad1747111aaefb4ee21 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 23 Jan 2026 22:17:30 +0000 Subject: [PATCH 372/572] Consolidate IsOverworldWildEncounter Functions --- include/overworld_encounters.h | 14 ++----- src/overworld_encounters.c | 75 ++++++++++++++++------------------ 2 files changed, 38 insertions(+), 51 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 19741fcf9cf4..8c16d208e860 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -52,17 +52,12 @@ enum OverworldEncounterSpawnAnim OWE_SPAWN_ANIM_SHINY, }; -/* -Combine OWE Type Checks into one function using these. -Need to figure a clean way to adjust SemiManual check as takes template. enum OverworldObjectEncounterType { - OWE_NONE, + OWE_ANY, OWE_GENERATED, - OWE_SEMI_MANUAL, - OWE_MANUAL, + OWE_MANUAL }; -*/ // OWE_SPEED_FASTER seems to visually bug out sometimes. enum OWESpeeds @@ -106,10 +101,7 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); void OverworldWildEncounter_SetMinimumSpawnTimer(void); void RemoveAllGeneratedOverworldEncounterObjects(void); -bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent); -bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent); -bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent); -bool32 IsSemiManualOverworldWildEncounter(u32 graphicsId, u32 trainerType); +bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 04befe722130..88dd082a9070 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -449,7 +449,7 @@ void CreateOverworldWildEncounter(void) if (objEventId >= OBJECT_EVENTS_COUNT) return; - if (!IsOverworldWildEncounter(object)) + if (!IsOverworldWildEncounter(object, OWE_ANY)) return; if (indexRoamerOutbreak && CreateOverworldWildEncounter_CheckRoamer(OWE_GetObjectRoamerOutbreakStatus(object))) @@ -611,10 +611,10 @@ static void SortOWEMonAges(void) void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { - if (!IsOverworldWildEncounter(objectEvent)) + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; - if (IsGeneratedOverworldWildEncounter(objectEvent)) + if (IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) SortOWEMonAges(); OWE_DoSpawnDespawnAnim(objectEvent, TRUE); @@ -622,7 +622,7 @@ void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent) { - if (!IsOverworldWildEncounter(objectEvent)) + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; objectEvent->sOverworldEncounterLevel = 0; @@ -754,7 +754,7 @@ static u32 GetNumActiveOverworldEncounters(void) u32 numActive = 0; for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { - if (IsOverworldWildEncounter(&gObjectEvents[i])) + if (IsOverworldWildEncounter(&gObjectEvents[i], OWE_ANY)) numActive++; } return numActive; @@ -783,7 +783,7 @@ void RemoveAllGeneratedOverworldEncounterObjects(void) for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { struct ObjectEvent *obj = &gObjectEvents[i]; - if (IsGeneratedOverworldWildEncounter(obj) && obj->active) + if (IsOverworldWildEncounter(obj, OWE_GENERATED) && obj->active) RemoveObjectEventByLocalIdAndMap(obj->localId, obj->mapNum, obj->mapGroup); } } @@ -818,28 +818,23 @@ static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) return gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; } -bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent) +bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType) { - return (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_ENCOUNTER); -} - -bool32 IsGeneratedOverworldWildEncounter(struct ObjectEvent *objectEvent) -{ - return IsOverworldWildEncounter(objectEvent) - && (objectEvent->localId <= LOCALID_OW_ENCOUNTER_END - && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS)); -} - -bool32 IsManualOverworldWildEncounter(struct ObjectEvent *objectEvent) -{ - return IsOverworldWildEncounter(objectEvent) - && (objectEvent->localId > LOCALID_OW_ENCOUNTER_END - || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS)); -} + bool32 isOWE = (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_ENCOUNTER); + switch (oweType) + { + default: + case OWE_ANY: + return isOWE; + + case OWE_GENERATED: + return isOWE && (objectEvent->localId <= LOCALID_OW_ENCOUNTER_END + && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS)); -bool32 IsSemiManualOverworldWildEncounter(u32 graphicsId, u32 trainerType) -{ - return graphicsId == OBJ_EVENT_GFX_OW_MON && trainerType == TRAINER_TYPE_ENCOUNTER; + case OWE_MANUAL: + return isOWE && (objectEvent->localId > LOCALID_OW_ENCOUNTER_END + || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS)); + } } static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) @@ -924,11 +919,11 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) { struct ObjectEvent *object = &gObjectEvents[objectEventId]; - if (!IsOverworldWildEncounter(object)) + if (!IsOverworldWildEncounter(object, OWE_ANY)) return FALSE; - if (IsGeneratedOverworldWildEncounter(object) - || (!IsGeneratedOverworldWildEncounter(object) + if (IsOverworldWildEncounter(object, OWE_GENERATED) + || (!IsOverworldWildEncounter(object, OWE_GENERATED) && (GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL || GetObjectEventScriptPointerByObjectEventId(objectEventId) == InteractWithDynamicWildOverworldEncounter))) { @@ -1010,8 +1005,8 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c if (REPEL_STEP_COUNT || FlagGet(DN_FLAG_SEARCHING)) return; - bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle)); - bool32 playerIsObstacle = (obstacle->isPlayer && IsOverworldWildEncounter(collider)); + bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle, OWE_ANY)); + bool32 playerIsObstacle = (obstacle->isPlayer && IsOverworldWildEncounter(collider, OWE_ANY)); if ((playerIsCollider || playerIsObstacle)) { @@ -1030,7 +1025,7 @@ void OverworldWildEncounter_RemoveObjectOnBattle(void) u32 localId = gSpecialVar_LastTalked; struct ObjectEvent *object = &gObjectEvents[GetObjectEventIdByLocalId(localId)]; - if (IsOverworldWildEncounter(object)) + if (IsOverworldWildEncounter(object, OWE_ANY)) { RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); OWE_SetNewSpawnCountdown(); @@ -1265,7 +1260,7 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) for (u32 i = 0; i < numActive; i++) { slotMon = &gObjectEvents[i]; - if (IsOverworldWildEncounter(slotMon) && (i == randomIndex)) + if (IsOverworldWildEncounter(slotMon, OWE_ANY) && (i == randomIndex)) return slotMon; } return NULL; @@ -1276,7 +1271,7 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) #define MAP_METATILE_VIEW_Y 5 static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) { - if (!IsOverworldWildEncounter(objectEvent)) + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; u32 speciesId = OW_SPECIES(objectEvent); @@ -1314,7 +1309,7 @@ static bool32 OWE_DoesRoamerObjectExist(void) for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { struct ObjectEvent *object = &gObjectEvents[i]; - if (IsOverworldWildEncounter(object) && OWE_GetObjectRoamerOutbreakStatus(object) == gEncounteredRoamerIndex) + if (IsOverworldWildEncounter(object, OWE_ANY) && OWE_GetObjectRoamerOutbreakStatus(object) == gEncounteredRoamerIndex) return TRUE; } @@ -1398,7 +1393,7 @@ static bool32 OWE_CheckRestrictMovementMapAtCoords(struct ObjectEvent *objectEve static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) { - if (!IsOverworldWildEncounter(objectEvent) || OW_SPECIES(objectEvent) == SPECIES_NONE) + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY) || OW_SPECIES(objectEvent) == SPECIES_NONE) return FALSE; if (!AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) @@ -1421,7 +1416,7 @@ static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index) static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) { - if (!IsOverworldWildEncounter(objectEvent)) + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return OWE_NON_ROAMER_OUTBREAK; u32 status = objectEvent->sRoamerOutbreakStatus; @@ -1449,7 +1444,7 @@ bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void) static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) { - if (!IsGeneratedOverworldWildEncounter(object)) + if (!IsOverworldWildEncounter(object, OWE_GENERATED)) return FALSE; return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumActiveGeneratedOverworldEncounters() == GetMaxOverworldEncounterSpawns(); @@ -1465,7 +1460,7 @@ void OWE_StartEncounterInstant(struct ObjectEvent *mon) bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent) { - if (!IsGeneratedOverworldWildEncounter(curObject) || IsOverworldWildEncounter(objectEvent) || objectEvent->isPlayer) + if (!IsOverworldWildEncounter(curObject, OWE_GENERATED) || IsOverworldWildEncounter(objectEvent, OWE_ANY) || objectEvent->isPlayer) return FALSE; RemoveObjectEventByLocalIdAndMap(curObject->localId, curObject->mapNum, curObject->mapGroup); @@ -1477,7 +1472,7 @@ u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y) if ((collision & (1 << (COLLISION_OBJECT_EVENT - 1)))) { struct ObjectEvent *objectEvent = &gObjectEvents[GetObjectEventIdByXY(x, y)]; - if (IsGeneratedOverworldWildEncounter(objectEvent)) + if (IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) { RemoveObjectEventByLocalIdAndMap(objectEvent->localId, objectEvent->mapNum, objectEvent->mapGroup); collision &= 1 << (COLLISION_OBJECT_EVENT - 1); From 94f4bd0ff2ec4f1b2f9a2f31d49218605806ce30 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 00:32:17 +0000 Subject: [PATCH 373/572] Hardcode Spawn Anim Priority --- src/event_object_movement.c | 3 +-- src/field_effect_helpers.c | 9 +++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 24e84c22dd41..f8169d600317 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11775,8 +11775,7 @@ bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim sp { gFieldEffectArguments[0] = objEvent->currentCoords.x; gFieldEffectArguments[1] = objEvent->currentCoords.y; - gFieldEffectArguments[2] = gSprites[objEvent->spriteId].oam.priority + 1; - gFieldEffectArguments[3] = spawnAnimType; + gFieldEffectArguments[2] = spawnAnimType; FieldEffectStart(FLDEFF_OW_ENCOUNTER_SPAWN_ANIM); return TRUE; } diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index e4b5114638af..aa13f6c58e22 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1520,11 +1520,10 @@ u32 FldEff_OWE_SpawnAnim(void) { u8 spriteId; u8 visual; - u8 subpriority = 0; s16 xOffset = 0, yOffset = 0; struct SpritePalette palette = gSpritePalette_GeneralFieldEffect0; - switch (gFieldEffectArguments[3]) + switch (gFieldEffectArguments[2]) { case OWE_SPAWN_ANIM_GRASS: visual = FLDEFFOBJ_JUMP_TALL_GRASS; @@ -1544,7 +1543,6 @@ u32 FldEff_OWE_SpawnAnim(void) case OWE_SPAWN_ANIM_UNDERWATER: visual = FLDEFFOBJ_BUBBLES; - subpriority = 82; break; case OWE_SPAWN_ANIM_CAVE: @@ -1555,18 +1553,17 @@ u32 FldEff_OWE_SpawnAnim(void) case OWE_SPAWN_ANIM_SHINY: default: visual = FLDEFFOBJ_SHINY_SPARKLE; - subpriority = 82; break; } FieldEffect_LoadFadedPalette(&palette, COLOR_MAP_DARK_CONTRAST); SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 0); - spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[visual], gFieldEffectArguments[0] + xOffset, gFieldEffectArguments[1] + yOffset, subpriority); + spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[visual], gFieldEffectArguments[0] + xOffset, gFieldEffectArguments[1] + yOffset, 82); if (spriteId != MAX_SPRITES) { struct Sprite *sprite = &gSprites[spriteId]; sprite->coordOffsetEnabled = TRUE; - sprite->oam.priority = gFieldEffectArguments[2]; + sprite->oam.priority = 2; } return spriteId; } From 5ffff8163c35dda13d25571a6621ea472b097456 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 00:33:26 +0000 Subject: [PATCH 374/572] Add OverworldWildEncounter_OnObjectEventSpawned after Setting --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 88dd082a9070..f14bfe4b6c13 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -184,6 +184,7 @@ void UpdateOverworldEncounters(void) if (shouldSpawnWaterMons) object->hideReflection = TRUE; + OverworldWildEncounter_OnObjectEventSpawned(object); OWE_SetNewSpawnCountdown(); } From aa74a11e836af4d54ed8e447f4d9e3bada5444bf Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 00:50:23 +0000 Subject: [PATCH 375/572] Change Check for templateOWE --- src/overworld_encounters.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f14bfe4b6c13..25ea0b32e614 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -148,7 +148,7 @@ void UpdateOverworldEncounters(void) .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), .movementType = OWE_GetMovementTypeFromSpecies(speciesId), - .trainerType = TRAINER_TYPE_NONE, // Not set in template to avoid check in TryGetObjectEventTemplateForOverworldEncounter + .trainerType = TRAINER_TYPE_ENCOUNTER, }; if (!OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) @@ -170,7 +170,6 @@ void UpdateOverworldEncounters(void) } object = &gObjectEvents[objectEventId]; - object->trainerType = TRAINER_TYPE_ENCOUNTER; object->disableCoveringGroundEffects = TRUE; object->sOverworldEncounterLevel = level; object->sRoamerOutbreakStatus = indexRoamerOutbreak; @@ -184,7 +183,6 @@ void UpdateOverworldEncounters(void) if (shouldSpawnWaterMons) object->hideReflection = TRUE; - OverworldWildEncounter_OnObjectEventSpawned(object); OWE_SetNewSpawnCountdown(); } @@ -937,7 +935,8 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) { - if (template->trainerType != TRAINER_TYPE_ENCOUNTER) + if (template->trainerType != TRAINER_TYPE_ENCOUNTER || (template->localId <= LOCALID_OW_ENCOUNTER_END + && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS))) return *template; struct ObjectEventTemplate templateOWE = *template; From 731e949c8b8c3e36abc045789f039b7c3e7a62ef Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 10:08:30 +0000 Subject: [PATCH 376/572] Couple Capitalisation --- include/config/overworld.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index cab0b025e018..c59c16ace1f4 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -49,14 +49,14 @@ // Overworld Pokémon #define OW_POKEMON_OBJECT_EVENTS TRUE // Adds Object Event fields for every species. Can be used for NPCs using the OBJ_EVENT_GFX_SPECIES macro (eg. OBJ_EVENT_GFX_SPECIES(BULBASAUR)) #define OW_SUBSTITUTE_PLACEHOLDER TRUE // Use a substitute OW for Pokémon that are missing overworld sprites -#define OW_LARGE_OW_SUPPORT TRUE // If true, adds a small amount of overhead to OW code so that large (48x48, 64x64) OWs will display correctly under bridges, etc. +#define OW_LARGE_OW_SUPPORT TRUE // If TRUE, adds a small amount of overhead to OW code so that large (48x48, 64x64) OWs will display correctly under bridges, etc. #define OW_PKMN_OBJECTS_SHARE_PALETTES FALSE // [WIP!! NOT ALL PALETTES HAVE BEEN ADJUSTED FOR THIS!!] If TRUE, follower palettes are taken from battle sprites. #define OW_GFX_COMPRESS TRUE // Adds support for compressed OW graphics, (Also compresses pokemon follower graphics). // IMPORTANT: Gfx are loaded into VRAM to avoid continous decompression. If you require more VRAM or want to use a lot of overworld Pokémon at once, you should disable this config. // Compressed gfx are incompatible with non-power-of-two sprite sizes: // (You should not use 48x48 sprites/tables for compressed gfx) // 16x32, 32x32, 64x64 etc are fine -#define OW_MON_WANDER_WALK TRUE // If true, OW Pokémon with MOVEMENT_TYPE_WANDER will walk-in-place in between steps. +#define OW_MON_WANDER_WALK TRUE // If TRUE, OW Pokémon with MOVEMENT_TYPE_WANDER will walk-in-place in between steps. // Follower Pokémon #define OW_FOLLOWERS_ENABLED FALSE // Enables follower Pokémon, HGSS style. Requires OW_POKEMON_OBJECT_EVENTS. Note that additional scripting may be required for them to be fully supported! #define OW_FOLLOWERS_BOBBING TRUE // If TRUE, follower Pokémon will bob up and down during their idle & walking animations From 1e4835e9f10ba9109288ea3a83f326d637c63409 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 12:04:43 +0000 Subject: [PATCH 377/572] Refactor ShouldRunOverworldEncounterScript --- src/field_control_avatar.c | 4 +--- src/overworld_encounters.c | 19 +++++++------------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 2b71a5789cca..8caa4d226abd 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -571,9 +571,7 @@ static const u8 *GetInteractedWaterScript(struct MapPosition *position, u8 metat u8 objectEventId = GetObjectEventIdByPosition(position->x, position->y, 1); if (IsPlayerFacingSurfableFishableWater() == TRUE && ShouldRunOverworldEncounterScript(objectEventId)) { - struct ObjectEvent *object = &gObjectEvents[objectEventId]; - gSpecialVar_0x8004 = OW_SPECIES(object); - gSpecialVar_LastTalked = object->localId; + gSpecialVar_LastTalked = &gObjectEvents[objectEventId].localId; return InteractWithDynamicWildOverworldEncounter; } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 25ea0b32e614..12ecc8ac048e 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -149,6 +149,7 @@ void UpdateOverworldEncounters(void) .elevation = MapGridGetElevationAt(x, y), .movementType = OWE_GetMovementTypeFromSpecies(speciesId), .trainerType = TRAINER_TYPE_ENCOUNTER, + .script = InteractWithDynamicWildOverworldEncounter, }; if (!OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) @@ -917,20 +918,11 @@ bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) { struct ObjectEvent *object = &gObjectEvents[objectEventId]; - - if (!IsOverworldWildEncounter(object, OWE_ANY)) + if (!IsOverworldWildEncounter(object, OWE_ANY) || GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithDynamicWildOverworldEncounter) return FALSE; - if (IsOverworldWildEncounter(object, OWE_GENERATED) - || (!IsOverworldWildEncounter(object, OWE_GENERATED) - && (GetObjectEventScriptPointerByObjectEventId(objectEventId) == NULL - || GetObjectEventScriptPointerByObjectEventId(objectEventId) == InteractWithDynamicWildOverworldEncounter))) - { - gSpecialVar_0x8004 = OW_SPECIES(object); - return TRUE; - } - - return FALSE; + gSpecialVar_0x8004 = OW_SPECIES(object); + return TRUE; } const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) @@ -976,6 +968,9 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( if (templateOWE.movementType == MOVEMENT_TYPE_NONE) templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); + if (templateOWE.script == NULL) + templateOWE.script = InteractWithDynamicWildOverworldEncounter; + assertf(speciesId != SPECIES_NONE && speciesId < NUM_SPECIES && IsSpeciesEnabled(speciesId), "invalid semi-manual overworld encounter\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, x, y) { // Currently causes assertf on each player step as function is called. From 66ee7cf098b11158b3ae9d780983a6818be555ac Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 16:51:07 +0000 Subject: [PATCH 378/572] Add isPlayer Check --- src/overworld_encounters.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 12ecc8ac048e..57663db648e8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -820,6 +820,9 @@ static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType) { + if (objectEvent->isPlayer) + return FALSE; + bool32 isOWE = (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_ENCOUNTER); switch (oweType) { From ab30fb4cedd2909120fc5f9b69031437ebf66c33 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 24 Jan 2026 14:29:44 -0600 Subject: [PATCH 379/572] Mon behavior states are now restored after being unloaded --- include/overworld_encounters.h | 1 + include/pokemon.h | 2 +- src/event_object_movement.c | 6 ++++++ src/overworld_encounters.c | 11 +++++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 8c16d208e860..08bedad509e4 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -123,5 +123,6 @@ bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *object bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void); bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); +void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/include/pokemon.h b/include/pokemon.h index 4fa50012d6a9..3795958aaa2e 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -2,8 +2,8 @@ #define GUARD_POKEMON_H #include "contest_effect.h" -#include "overworld_encounters.h" #include "sprite.h" +#include "overworld_encounters.h" #include "constants/battle.h" #include "constants/cries.h" #include "constants/egg_ids.h" diff --git a/src/event_object_movement.c b/src/event_object_movement.c index f8169d600317..9c45bd69a0ec 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -2910,6 +2910,7 @@ static void SpawnObjectEventOnReturnToField(u8 objectEventId, s16 x, s16 y) ResetObjectEventFldEffData(objectEvent); SetObjectSubpriorityByElevation(objectEvent->previousElevation, sprite, 1); + OWE_RestoreBehaviorState(objectEvent, sprite); } } @@ -11846,15 +11847,20 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step5(struct ObjectEvent return TRUE; } +#define sSavedMovementState warpArrowSpriteId + movement_type_def(MovementType_ChasePlayer_OverworldWildEncounter, gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter) bool8 MovementType_Common_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite) { ClearObjectEventMovement(objectEvent, sprite); + objectEvent->sSavedMovementState = 10; sprite->sTypeFuncId = 8; return TRUE; } +#undef sSavedMovementState + bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 25ea0b32e614..f37fe722ec74 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1482,6 +1482,17 @@ u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y) return collision; } +#define sTypeFuncId data[1] +#define sSavedMovementState warpArrowSpriteId + +void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + if (IsOverworldWildEncounter(objectEvent, OWE_ANY)) + sprite->sTypeFuncId = objectEvent->sSavedMovementState; +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus +#undef sSavedMovementState +#undef sTypeFuncId From 9c74633f1cd206f391ae20a90e5bb6b45f2f38b9 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 21:29:58 +0000 Subject: [PATCH 380/572] Add IsOverworldWildEncounter Checks --- src/overworld_encounters.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 57663db648e8..1c118ee53f90 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1071,7 +1071,7 @@ void DespawnOldestOWE_Pal(void) bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon) { - if (mon->movementType == MOVEMENT_TYPE_WANDER_AROUND_OWE) + if (!IsOverworldWildEncounter(mon, OWE_ANY) || mon->movementType == MOVEMENT_TYPE_WANDER_AROUND_OWE) return FALSE; if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING)) @@ -1116,6 +1116,9 @@ bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon) bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon) { + if (!IsOverworldWildEncounter(mon, OWE_ANY)) + return FALSE; + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u32 distance = OWE_CHASE_RANGE; u32 speciesId = OW_SPECIES(mon); From ce47cf0246a9c5ced4fd50a917a8813aee686d08 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 21:36:14 +0000 Subject: [PATCH 381/572] Add Early Returns to OWE_DespawnMonDueToTrainerSight --- src/overworld_encounters.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 1c118ee53f90..92f6917f586b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1470,17 +1470,15 @@ bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct Obj u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y) { - if ((collision & (1 << (COLLISION_OBJECT_EVENT - 1)))) - { - struct ObjectEvent *objectEvent = &gObjectEvents[GetObjectEventIdByXY(x, y)]; - if (IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) - { - RemoveObjectEventByLocalIdAndMap(objectEvent->localId, objectEvent->mapNum, objectEvent->mapGroup); - collision &= 1 << (COLLISION_OBJECT_EVENT - 1); - } - } + if (!(collision & (1 << (COLLISION_OBJECT_EVENT - 1)))) + return collision; + + struct ObjectEvent *objectEvent = &gObjectEvents[GetObjectEventIdByXY(x, y)]; + if (!IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) + return collision; - return collision; + RemoveObjectEventByLocalIdAndMap(objectEvent->localId, objectEvent->mapNum, objectEvent->mapGroup); + return collision & (1 << (COLLISION_OBJECT_EVENT - 1)); } #undef sOverworldEncounterLevel From db8dddac929a3395864fbb8e865580ae031cedad Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 21:38:53 +0000 Subject: [PATCH 382/572] Update field_control_avatar.c --- src/field_control_avatar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 8caa4d226abd..066004fdf71d 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -571,7 +571,7 @@ static const u8 *GetInteractedWaterScript(struct MapPosition *position, u8 metat u8 objectEventId = GetObjectEventIdByPosition(position->x, position->y, 1); if (IsPlayerFacingSurfableFishableWater() == TRUE && ShouldRunOverworldEncounterScript(objectEventId)) { - gSpecialVar_LastTalked = &gObjectEvents[objectEventId].localId; + gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; return InteractWithDynamicWildOverworldEncounter; } From 21cf4c03f1714b4c6927f0d0170866d1e8dc8e31 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 21:41:18 +0000 Subject: [PATCH 383/572] Enum Conversion --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 92f6917f586b..8d467881cf24 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -175,7 +175,7 @@ void UpdateOverworldEncounters(void) object->sOverworldEncounterLevel = level; object->sRoamerOutbreakStatus = indexRoamerOutbreak; - u8 directions[4]; + enum Direction directions[4]; memcpy(directions, gStandardDirections, sizeof directions); ObjectEventTurn(object, directions[Random() & 3]); From 1565c8eb943daa881b7b75cc12ad69c52ea02829 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 21:42:37 +0000 Subject: [PATCH 384/572] Allow for Water OWE Reflections --- src/overworld_encounters.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8d467881cf24..c03c30fff435 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -178,12 +178,6 @@ void UpdateOverworldEncounters(void) enum Direction directions[4]; memcpy(directions, gStandardDirections, sizeof directions); ObjectEventTurn(object, directions[Random() & 3]); - - // Hide reflections for spawns in water - // (It just looks weird) - if (shouldSpawnWaterMons) - object->hideReflection = TRUE; - OWE_SetNewSpawnCountdown(); } From 55adce83fbfaf2273b22f84c3a481b7b213e1643 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 21:46:03 +0000 Subject: [PATCH 385/572] Move OWE_CanEncounterBeLoaded --- src/overworld_encounters.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c03c30fff435..af2a0d517e49 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -135,7 +135,10 @@ void UpdateOverworldEncounters(void) u32 level; u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); - if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(level) || !IsAbilityAllowingEncounter(level)) + if (speciesId == SPECIES_NONE + || !IsWildLevelAllowedByRepel(level) + || !IsAbilityAllowingEncounter(level) + || !OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) { OWE_ResetSpawnCounterPlayAmbientCry(); return; @@ -151,13 +154,6 @@ void UpdateOverworldEncounters(void) .trainerType = TRAINER_TYPE_ENCOUNTER, .script = InteractWithDynamicWildOverworldEncounter, }; - - if (!OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) - { - OWE_ResetSpawnCounterPlayAmbientCry(); - return; - } - u32 objectEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[objectEventId]; if (OWE_ShouldDespawnGeneratedForNewOWE(object)) From 967dbee8bd3a45bd73c1a612b1bdf80e5cd87d7f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 22:04:08 +0000 Subject: [PATCH 386/572] Add check for field effect palette inOWE_CanEncounterBeLoaded --- include/overworld_encounters.h | 1 + src/field_effect_helpers.c | 7 +++--- src/overworld_encounters.c | 39 ++++++++++++++++++++++++++-------- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 8c16d208e860..54d8df83fbd0 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -123,5 +123,6 @@ bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *object bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void); bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); +struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index aa13f6c58e22..b2c52dd045fb 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1521,19 +1521,18 @@ u32 FldEff_OWE_SpawnAnim(void) u8 spriteId; u8 visual; s16 xOffset = 0, yOffset = 0; - struct SpritePalette palette = gSpritePalette_GeneralFieldEffect0; + enum OverworldEncounterSpawnAnim spawnAnim = gFieldEffectArguments[2]; + struct SpritePalette palette = OWE_GetSpawnAnimFldEffPalette(spawnAnim); - switch (gFieldEffectArguments[2]) + switch (spawnAnim) { case OWE_SPAWN_ANIM_GRASS: visual = FLDEFFOBJ_JUMP_TALL_GRASS; yOffset = 8; - palette = gSpritePalette_GeneralFieldEffect1; break; case OWE_SPAWN_ANIM_LONG_GRASS: visual = FLDEFFOBJ_JUMP_LONG_GRASS; - palette = gSpritePalette_GeneralFieldEffect1; break; case OWE_SPAWN_ANIM_WATER: diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index af2a0d517e49..ac4c2000eb2b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -49,7 +49,7 @@ static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); static u32 GetSpawnSlotByLocalId(u32 localId); static void SortOWEMonAges(void); static void OWE_SetNewSpawnCountdown(void); -static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny); +static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_DoesRoamerObjectExist(void); @@ -135,10 +135,12 @@ void UpdateOverworldEncounters(void) u32 level; u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); + // Does X and Y need to be adjusted for map offset here, for all subsequent? + // If so can be done when setting them originally? if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(level) || !IsAbilityAllowingEncounter(level) - || !OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny)) + || !OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny, x, y)) { OWE_ResetSpawnCounterPlayAmbientCry(); return; @@ -188,7 +190,7 @@ static void OWE_SetNewSpawnCountdown(void) } #define OWE_FIELD_EFFECT_TILE_NUM 16 // Number of tiiles to add for field effect spawning -static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny) +static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) { u32 numFreePalSlots = CountFreePaletteSlots(); u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); @@ -202,13 +204,11 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is // Add this and tiles to seperate graphics check function if (numFreePalSlots == 1) { - - // If the mon's palette isn't already loaded, don't spawn. - if (IndexOfSpritePaletteTag(tag) == 0xFF) + u32 metatileBehavior = MapGridGetMetatileBehaviorAt(x, y); + struct SpritePalette palette = OWE_GetSpawnAnimFldEffPalette(OWE_GetSpawnDespawnAnimType(metatileBehavior)); + // If the mon's palette or field effect palette isn't already loaded, don't spawn. + if (IndexOfSpritePaletteTag(tag) == 0xFF || IndexOfSpritePaletteTag(palette.tag) == 0xFF) return FALSE; - - // Add check if field effect pallete is already loaded - // Bubbles field effect occurs on every movement } else if (numFreePalSlots == 0) { @@ -1471,6 +1471,27 @@ u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y) return collision & (1 << (COLLISION_OBJECT_EVENT - 1)); } +struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim) +{ + struct SpritePalette palette = gSpritePalette_GeneralFieldEffect0; + switch (spawnAnim) + { + case OWE_SPAWN_ANIM_GRASS: + case OWE_SPAWN_ANIM_LONG_GRASS: + palette = gSpritePalette_GeneralFieldEffect1; + break; + + case OWE_SPAWN_ANIM_WATER: + case OWE_SPAWN_ANIM_UNDERWATER: + case OWE_SPAWN_ANIM_CAVE: + case OWE_SPAWN_ANIM_SHINY: + default: + break; + } + + return palette; +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From efd85c6b326df32a532b56040adc3474827bbdeb Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 23:20:14 +0000 Subject: [PATCH 387/572] Explicetly prevent Manual OWEs being Roamers or Mass Outbreaks --- src/overworld_encounters.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index ac4c2000eb2b..75d81e9da293 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -29,11 +29,12 @@ #include "constants/vars.h" #include "constants/wild_encounter.h" -#define sOverworldEncounterLevel trainerRange_berryTreeId -#define sAge playerCopyableMovement -#define sRoamerOutbreakStatus directionSequenceIndex -#define OWE_NON_ROAMER_OUTBREAK 0 -#define OWE_MASS_OUTBREAK_INDEX ROAMER_COUNT + 1 +#define sOverworldEncounterLevel trainerRange_berryTreeId +#define sAge playerCopyableMovement +#define sRoamerOutbreakStatus directionSequenceIndex +#define OWE_NON_ROAMER_OUTBREAK 0 +#define OWE_MASS_OUTBREAK_INDEX ROAMER_COUNT + 1 +#define OWE_INVALID_ROAMER_OUTBREAK OWE_MASS_OUTBREAK_INDEX + 1 static EWRAM_DATA u8 sOWESpawnCountdown = 0; @@ -708,7 +709,7 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam If none of these checks succeed, speciesId is set to SPECIES_NONE and FALSE is returned. */ - if (TryStartRoamerEncounter() && !OWE_DoesRoamerObjectExist()) + if (TryStartRoamerEncounter() && !OWE_DoesRoamerObjectExist() && *indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) { *indexRoamerOutbreak = OWE_GetObjectRoamerStatusFromIndex(gEncounteredRoamerIndex); } @@ -718,7 +719,7 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam *speciesId = gWildFeebas.species; CreateWildMon(*speciesId, *level); } - else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior)) + else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior) && *indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) { SetUpMassOutbreakEncounter(0); *indexRoamerOutbreak = OWE_MASS_OUTBREAK_INDEX; @@ -932,7 +933,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( bool32 isShiny = FALSE, isShinyTemplate = (templateOWE.graphicsId & OBJ_EVENT_MON_SHINY) ? TRUE : FALSE; bool32 isFemale = FALSE, isFemaleTemplate = (templateOWE.graphicsId & OBJ_EVENT_MON_FEMALE) ? TRUE : FALSE; u32 level, levelTemplate = templateOWE.sOverworldEncounterLevel; - u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; + u32 indexRoamerOutbreak = OWE_INVALID_ROAMER_OUTBREAK; u32 x = template->x; u32 y = template->y; From 253a00edebeb44e9dc732f86768e7245b3672eee Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 24 Jan 2026 23:20:31 +0000 Subject: [PATCH 388/572] Remove References to SemiManual OWEs --- src/overworld_encounters.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 75d81e9da293..af1330dfe908 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -75,7 +75,7 @@ static void OWE_StartEncounterInstant(struct ObjectEvent *mon); void OWE_ResetSpawnCounterPlayAmbientCry(void) { OverworldWildEncounter_SetMinimumSpawnTimer(); - // Currently may not play manual or semi-manual encounter cries if no wild mon header exists + // Currently may not play manual encounter cries if no wild mon header exists if (OW_WILD_ENCOUNTERS_AMBIENT_CRIES && GetNumActiveOverworldEncounters()) OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); } @@ -965,7 +965,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( if (templateOWE.script == NULL) templateOWE.script = InteractWithDynamicWildOverworldEncounter; - assertf(speciesId != SPECIES_NONE && speciesId < NUM_SPECIES && IsSpeciesEnabled(speciesId), "invalid semi-manual overworld encounter\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, x, y) + assertf(speciesId != SPECIES_NONE && speciesId < NUM_SPECIES && IsSpeciesEnabled(speciesId), "invalid manual overworld encounter\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, x, y) { // Currently causes assertf on each player step as function is called. templateOWE.graphicsId = OBJ_EVENT_GFX_BOY_1; From b6c3d6ad14434adca5eff2d741645e871484ca14 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 12:43:40 +0000 Subject: [PATCH 389/572] Increase Roamer Safety --- src/overworld_encounters.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index af1330dfe908..cce4844c7236 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1000,6 +1000,12 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c if ((playerIsCollider || playerIsObstacle)) { struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; + if (!IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), + gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) + { + RemoveObjectEventByLocalIdAndMap(wildMon->localId, wildMon->mapNum, wildMon->mapGroup); + return; + } LockPlayerFieldControls(); if (!OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE) @@ -1409,12 +1415,12 @@ static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index) static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) - return OWE_NON_ROAMER_OUTBREAK; + return OWE_INVALID_ROAMER_OUTBREAK; u32 status = objectEvent->sRoamerOutbreakStatus; if (status == OWE_NON_ROAMER_OUTBREAK || status == OWE_MASS_OUTBREAK_INDEX) { - return status; + return OWE_INVALID_ROAMER_OUTBREAK; } return status - 1; From 15c21f2880fc1e35571ba361dd572cc59d28b85d Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 12:46:16 +0000 Subject: [PATCH 390/572] Start adding documentation and adjust code to reflect it --- .../how_to_overworld_wild_encounters.md | 34 +++++++++++++ src/overworld_encounters.c | 49 +++++++++++-------- 2 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 docs/tutorials/how_to_overworld_wild_encounters.md diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md new file mode 100644 index 000000000000..07bd7c4d6eb1 --- /dev/null +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -0,0 +1,34 @@ +# Overworld Wild Encounters Tutorial +## OWE Spawning +Overworld Wild Encounters (OWEs), refer to the wild encounters that can be seen as object events in the overworld, prior to engaging in battle with them. They use either the `WILD_AREA_LAND` or `WILD_AREA_WATER` encounter tables by default. OWEs come in two types, Generated or Manual. + +### Generated OWEs +Generated OWEs are spawned automatically when `OW_WILD_ENCOUNTERS_OVERWORLD` is set to `TRUE`, being spawned on a random encounter tile near the player, with the encounter table used dependant on it. These are considered low priority OWEs, and automatically populate a level, species, gender and shinyness exactly how a vanilla wild encounter would, or can even be a special spawn, but more on those later. + +### Manual OWEs +Manual OWEs are created by the developer as any other object event would be and are defined by having the `.trainerType` set to `TRAINER_TYPE_ENCOUNTER`. These can be fully customised by the developer, with the level, species, gender and shinyness all able to be specified. The level can be set by filling the desired value in the `trainerRange_berryTreeId`. The latter three are specified by the `graphicsId` of the object, for example; +- `OBJ_EVENT_GFX_SPECIES(BULBASAUR)` will produce a male, non-shiny Bulbasaur. +- `OBJ_EVENT_GFX_SPECIES_SHINY(CHARMANDER)` will produce a male, shiny Charmander. +- `OBJ_EVENT_GFX_SPECIES_FEMALE(SQUIRTLE)` will produce a female, non-shiny Squirtle. +- `OBJ_EVENT_GFX_SPECIES_SHINY_FEMALE(PIKACHU)` will produce a female, shiny Pikachu. + +However Manual OWEs do not have to be defined fully, leaving any of the level, species, gender, shinyness or script zeroed will revert to default behaviours and any set parameters used. Leaving the level or species blank will take one the relevant encounter table. Leaving the shinyness blank will revert to default shiny odds, although this can still be affected by `P_FLAG_FORCE_SHINY` and `P_FLAG_FORCE_NO_SHINY`. Setting the `OBJ_EVENT_MON` bit of the `graphicsId`, but not the `OBJ_EVENT_MON_FEMALE` will result in a male encounter, setting both will result in a female encounter, as seen above, but setting neither will randomise the gender based on species. A species can be defined with a random gender by just using the species define. A specific script can be specified, but if not the default OWE encounter script will be used. +Assuming the following `graphicsId` have `.trainerType` set to `TRAINER_TYPE_ENCOUNTER`; +- `SPECIES_EEVEE` will result in an Eevee with a randomised level, gender and shinyness, using the default encounter script. +- `OBJ_EVENT_GFX_SPECIES(NONE)` will result in a male randomised species of randomised level, gender and shinyness, using the default encounter script. +- `OBJ_EVENT_GFX_SPECIES_SHINY_FEMALE(PIKACHU)` will result in a female, shiny randomised species with randomised level and gender, using the default encounter script. + +As level and species are potentially taken from the Wild Encounter Header, an `assertf` to let developers know when an invalid value is used. If the resultant level is invalid, it will be set to `MIN_LEVEL` (1). If the species is invalid, a replacement object will be created using `OBJ_EVENT_GFX_BOY_1`, this will not be an OWE of any kind. + +No matter how much of a Manual OWE is defined, it is considered a high priority OWE, and treated as a regular object event in all ways other than ones outlined above. These however, have restricted special spawns types. + +### Special Spawns +Special spawns can be one of three types, in decreasing priority: A Roamer, Feebas, or Mass Outbreak Encounter. Generated OWEs can have any of these, however, Manual OWEs can only have the Feebas Special Spawn. These work exactly as they would normally; +- If a Roamer is on the route and is able to spawn, then it may appear where a Generated OWE would. +- If any OWE spawns on a tile where a Feebas would spawn, it may appear is a Feebas. +- If a Generated OWE spawns on a route that has a mass outbreak occuring, it may spawn as an encounter from that mass outbreak. + +## High Priority and Low Priority OWE Behaviours +## Encounter Types +## Repel and Lure Behaviours +## OWE Behaviour Types diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index cce4844c7236..c4765d5f5975 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -460,9 +460,7 @@ void CreateOverworldWildEncounter(void) gender = gSpeciesInfo[speciesId].genderRatio; } - if (level > MAX_LEVEL) - level = MAX_LEVEL; - else if ( level < MIN_LEVEL) + if (level < MIN_LEVEL || level > MAX_LEVEL) level = MIN_LEVEL; ZeroEnemyPartyMons(); @@ -699,10 +697,10 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam /* These functions perform checks of various encounter types in the following order: - 1. Attempted Generated Roamer Encounter - 2. Attempted Generated Feebas Encounter - 3. Attempted Generated Mass Outbreak Encounter - 4. Attempted Generated Standard Wild Encounter generation + 1. Attempt to generate a Roamer Encounter + 2. Attempt to generate a Feebas Encounter + 3. Attempt to generate a Mass Outbreak Encounter + 4. Attempt to generate a Standard Wild Encounter The structure of this statement ensures that only one of these encounter types can succeed per call, with the resultant wild mon being created in gEnemyParty[0]. @@ -719,7 +717,7 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam *speciesId = gWildFeebas.species; CreateWildMon(*speciesId, *level); } - else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior) && *indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) + else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior) && *indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) { SetUpMassOutbreakEncounter(0); *indexRoamerOutbreak = OWE_MASS_OUTBREAK_INDEX; @@ -931,7 +929,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( u32 graphicsId; u16 speciesId, speciesTemplate = SanitizeSpeciesId(templateOWE.graphicsId & OBJ_EVENT_MON_SPECIES_MASK); bool32 isShiny = FALSE, isShinyTemplate = (templateOWE.graphicsId & OBJ_EVENT_MON_SHINY) ? TRUE : FALSE; - bool32 isFemale = FALSE, isFemaleTemplate = (templateOWE.graphicsId & OBJ_EVENT_MON_FEMALE) ? TRUE : FALSE; + bool32 isFemale = FALSE; u32 level, levelTemplate = templateOWE.sOverworldEncounterLevel; u32 indexRoamerOutbreak = OWE_INVALID_ROAMER_OUTBREAK; u32 x = template->x; @@ -950,31 +948,40 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( if (speciesTemplate) speciesId = speciesTemplate; + assertf(speciesId != SPECIES_NONE && speciesId < NUM_SPECIES && IsSpeciesEnabled(speciesId), "invalid manual overworld encounter\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, x, y) + { + // Currently causes assertf on each player step as function is called. + templateOWE.graphicsId = OBJ_EVENT_GFX_BOY_1; + templateOWE.trainerType = TRAINER_TYPE_NONE; + templateOWE.sOverworldEncounterLevel = 0; + templateOWE.movementType = MOVEMENT_TYPE_NONE; + return templateOWE; + } + if (isShinyTemplate) isShiny = isShinyTemplate; - if (isFemaleTemplate) - isFemale = isFemaleTemplate; + if (templateOWE.graphicsId & OBJ_EVENT_MON && templateOWE.graphicsId & OBJ_EVENT_MON_FEMALE) + isFemale = TRUE; + else if (templateOWE.graphicsId & OBJ_EVENT_MON) + isFemale = FALSE; + else + isFemale = GetGenderFromSpeciesAndPersonality(speciesId, Random32()) == MON_FEMALE; if (levelTemplate) level = levelTemplate; + assertf(level < MIN_LEVEL || level > MAX_LEVEL, "invalid manual overworld encounter\nlevel: %d\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", level, speciesId, x, y) + { + level = MIN_LEVEL; + } + if (templateOWE.movementType == MOVEMENT_TYPE_NONE) templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); if (templateOWE.script == NULL) templateOWE.script = InteractWithDynamicWildOverworldEncounter; - assertf(speciesId != SPECIES_NONE && speciesId < NUM_SPECIES && IsSpeciesEnabled(speciesId), "invalid manual overworld encounter\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, x, y) - { - // Currently causes assertf on each player step as function is called. - templateOWE.graphicsId = OBJ_EVENT_GFX_BOY_1; - templateOWE.trainerType = TRAINER_TYPE_NONE; - templateOWE.sOverworldEncounterLevel = 0; - templateOWE.movementType = MOVEMENT_TYPE_NONE; - return templateOWE; - } - graphicsId = speciesId + OBJ_EVENT_MON; if (isFemale) graphicsId += OBJ_EVENT_MON_FEMALE; From 18e688d7af67421a18d0b640c9b30ca5ee05c86b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:12:31 +0000 Subject: [PATCH 391/572] More Documentation --- docs/tutorials/how_to_overworld_wild_encounters.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 07bd7c4d6eb1..8513a3a45bfe 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -28,7 +28,17 @@ Special spawns can be one of three types, in decreasing priority: A Roamer, Feeb - If any OWE spawns on a tile where a Feebas would spawn, it may appear is a Feebas. - If a Generated OWE spawns on a route that has a mass outbreak occuring, it may spawn as an encounter from that mass outbreak. -## High Priority and Low Priority OWE Behaviours -## Encounter Types +### Restricted Despawning +## High Priority and Low Priority OWEs +Low Priority OWEs may face not be spawned or even be destroyed in certain situations. There are palettes and object tiles checks to prevent these from spawning if it would fail, as well as similar checks for number of event objects, palettes and object tiles that despawn the oldest if High Priority OWEs or other objects event are attempting to be spawned and Low Priority OWEs are using these resources. High priority OWEs are treated as regular objects, and will not be destroyed, may cause the destruction of Generated OWEs and will not face spawning restrictions as all outlined above. +These despawn conditions will overwrite the restrictive despawns mentioned above. +> Is this true? Does it take the oldest or oldest not marked for restriction, what if all are marked to not despawn. +> Setting `OW_GFX_COMPRESS` to `FALSE` will free more space in VRAM, allowing for more large OWEs to spawn. + +## Encountering an OWE +Collision between Player and OWE or Interacting with one. Can also interact with an OWE in the water even when the player is not. +### Encounter Types ## Repel and Lure Behaviours ## OWE Behaviour Types +## OWE Movements +### Restricted Movements From 55b5522d8d09421381f8df1df8897b048821dafe Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:22:43 +0000 Subject: [PATCH 392/572] Small Adjustments --- include/overworld_encounters.h | 1 + src/event_object_movement.c | 6 +----- src/overworld_encounters.c | 12 ++++++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 8666895d685b..c4f574fc9f12 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -125,5 +125,6 @@ bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct Obj u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite); +void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent, u32 state); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 9c45bd69a0ec..8292c920092a 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11847,20 +11847,16 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step5(struct ObjectEvent return TRUE; } -#define sSavedMovementState warpArrowSpriteId - movement_type_def(MovementType_ChasePlayer_OverworldWildEncounter, gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter) bool8 MovementType_Common_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite) { ClearObjectEventMovement(objectEvent, sprite); - objectEvent->sSavedMovementState = 10; + OWE_SetSavedMovementState(objectEvent, 10); sprite->sTypeFuncId = 8; return TRUE; } -#undef sSavedMovementState - bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 455d508babd0..aee9514e9e6d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -32,6 +32,7 @@ #define sOverworldEncounterLevel trainerRange_berryTreeId #define sAge playerCopyableMovement #define sRoamerOutbreakStatus directionSequenceIndex +#define sSavedMovementState warpArrowSpriteId #define OWE_NON_ROAMER_OUTBREAK 0 #define OWE_MASS_OUTBREAK_INDEX ROAMER_COUNT + 1 #define OWE_INVALID_ROAMER_OUTBREAK OWE_MASS_OUTBREAK_INDEX + 1 @@ -1506,17 +1507,20 @@ struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnA return palette; } -#define sTypeFuncId data[1] -#define sSavedMovementState warpArrowSpriteId - +#define sTypeFuncId data[1] // Same as in src/event_object_movement.c void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (IsOverworldWildEncounter(objectEvent, OWE_ANY)) sprite->sTypeFuncId = objectEvent->sSavedMovementState; } +#undef sTypeFuncId + +void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent, u32 state) +{ + objectEvent->sSavedMovementState = state; +} #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus #undef sSavedMovementState -#undef sTypeFuncId From 46b02a766117fa589fe1503c84c3574db5ffd96c Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:28:54 +0000 Subject: [PATCH 393/572] Tidy OWE_CanAwareMonSeePlayer --- src/overworld_encounters.c | 68 ++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index aee9514e9e6d..6e4d3ffc2830 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1079,41 +1079,45 @@ bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon) if (!IsOverworldWildEncounter(mon, OWE_ANY) || mon->movementType == MOVEMENT_TYPE_WANDER_AROUND_OWE) return FALSE; - if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING)) + if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) + || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING)) + return OWE_IsPlayerInsideMonActiveDistance(mon); + + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + u32 speciesId = OW_SPECIES(mon); + u32 viewDistance = OWE_GetViewDistanceFromSpecies(speciesId); + u32 viewWidth = OWE_GetViewWidthFromSpecies(speciesId); + s32 halfWidth = (viewWidth - 1) / 2; + + switch (mon->facingDirection) { - if (OWE_IsPlayerInsideMonActiveDistance(mon)) + case DIR_NORTH: + if (player->currentCoords.y < mon->currentCoords.y + && mon->currentCoords.y - player->currentCoords.y <= viewDistance + && player->currentCoords.x >= mon->currentCoords.x - halfWidth + && player->currentCoords.x <= mon->currentCoords.x + halfWidth) return TRUE; - } - else - { - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - u32 speciesId = OW_SPECIES(mon); - u32 viewDistance = OWE_GetViewDistanceFromSpecies(speciesId); - u32 viewWidth = OWE_GetViewWidthFromSpecies(speciesId); - switch (mon->facingDirection) - { - case DIR_NORTH: - if (player->currentCoords.y < mon->currentCoords.y && (mon->currentCoords.y - player->currentCoords.y) <= viewDistance - && player->currentCoords.x >= (mon->currentCoords.x - ((viewWidth - 1) / 2)) && player->currentCoords.x <= (mon->currentCoords.x + ((viewWidth - 1) / 2))) - return TRUE; - break; - case DIR_SOUTH: - if (player->currentCoords.y > mon->currentCoords.y && (player->currentCoords.y - mon->currentCoords.y) <= viewDistance - && player->currentCoords.x >= (mon->currentCoords.x - ((viewWidth - 1) / 2)) && player->currentCoords.x <= (mon->currentCoords.x + ((viewWidth - 1) / 2))) - return TRUE; - break; - case DIR_EAST: - if (player->currentCoords.x > mon->currentCoords.x && (player->currentCoords.x - mon->currentCoords.x) <= viewDistance - && player->currentCoords.y >= (mon->currentCoords.y - ((viewWidth - 1) / 2)) && player->currentCoords.y <= (mon->currentCoords.y + ((viewWidth - 1) / 2))) - return TRUE; - break; - case DIR_WEST: - if (player->currentCoords.x < mon->currentCoords.x && (mon->currentCoords.x - player->currentCoords.x) <= viewDistance - && player->currentCoords.y >= (mon->currentCoords.y - ((viewWidth - 1) / 2)) && player->currentCoords.y <= (mon->currentCoords.y + ((viewWidth - 1) / 2))) - return TRUE; - break; - } + case DIR_SOUTH: + if (player->currentCoords.y > mon->currentCoords.y + && player->currentCoords.y - mon->currentCoords.y <= viewDistance + && player->currentCoords.x >= mon->currentCoords.x - halfWidth + && player->currentCoords.x <= mon->currentCoords.x + halfWidth) + return TRUE; + + case DIR_EAST: + if (player->currentCoords.x > mon->currentCoords.x + && player->currentCoords.x - mon->currentCoords.x <= viewDistance + && player->currentCoords.y >= mon->currentCoords.y - halfWidth + && player->currentCoords.y <= mon->currentCoords.y + halfWidth) + return TRUE; + + case DIR_WEST: + if (player->currentCoords.x < mon->currentCoords.x + && mon->currentCoords.x - player->currentCoords.x <= viewDistance + && player->currentCoords.y >= mon->currentCoords.y - halfWidth + && player->currentCoords.y <= mon->currentCoords.y + halfWidth) + return TRUE; } return FALSE; From d5ae7ee3e90b0715a7cdfddc082323cd7cf02424 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 15:42:52 +0000 Subject: [PATCH 394/572] Add line of sight to OWEs --- src/overworld_encounters.c | 47 ++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 6e4d3ffc2830..47c7ca6e3eab 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -72,6 +72,7 @@ static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); static void OWE_StartEncounterInstant(struct ObjectEvent *mon); +static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance); void OWE_ResetSpawnCounterPlayAmbientCry(void) { @@ -1079,47 +1080,61 @@ bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon) if (!IsOverworldWildEncounter(mon, OWE_ANY) || mon->movementType == MOVEMENT_TYPE_WANDER_AROUND_OWE) return FALSE; - if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) + if ((TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING)) - return OWE_IsPlayerInsideMonActiveDistance(mon); + && OWE_IsPlayerInsideMonActiveDistance(mon)); + return TRUE; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u32 speciesId = OW_SPECIES(mon); u32 viewDistance = OWE_GetViewDistanceFromSpecies(speciesId); u32 viewWidth = OWE_GetViewWidthFromSpecies(speciesId); s32 halfWidth = (viewWidth - 1) / 2; + enum Direction direction = mon->facingDirection; + bool32 retVal = FALSE; - switch (mon->facingDirection) + switch (direction) { case DIR_NORTH: if (player->currentCoords.y < mon->currentCoords.y && mon->currentCoords.y - player->currentCoords.y <= viewDistance && player->currentCoords.x >= mon->currentCoords.x - halfWidth && player->currentCoords.x <= mon->currentCoords.x + halfWidth) - return TRUE; + retVal = TRUE; + break; case DIR_SOUTH: if (player->currentCoords.y > mon->currentCoords.y && player->currentCoords.y - mon->currentCoords.y <= viewDistance && player->currentCoords.x >= mon->currentCoords.x - halfWidth && player->currentCoords.x <= mon->currentCoords.x + halfWidth) - return TRUE; + retVal = TRUE; + break; case DIR_EAST: if (player->currentCoords.x > mon->currentCoords.x && player->currentCoords.x - mon->currentCoords.x <= viewDistance && player->currentCoords.y >= mon->currentCoords.y - halfWidth && player->currentCoords.y <= mon->currentCoords.y + halfWidth) - return TRUE; + retVal = TRUE; + break; case DIR_WEST: if (player->currentCoords.x < mon->currentCoords.x && mon->currentCoords.x - player->currentCoords.x <= viewDistance && player->currentCoords.y >= mon->currentCoords.y - halfWidth && player->currentCoords.y <= mon->currentCoords.y + halfWidth) - return TRUE; + retVal = TRUE; + break; + + default: + retVal = FALSE; + break; } + if (retVal && OWE_IsLineOfSightClear(player, GetOppositeDirection(direction), viewDistance)) + return TRUE; + return FALSE; } @@ -1524,6 +1539,24 @@ void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent, u32 state) objectEvent->sSavedMovementState = state; } +static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance) +{ + s16 x = player->currentCoords.x; + s16 y = player->currentCoords.y; + u32 i; + u32 collision; + + for (i = 0; i < distance; i++) + { + MoveCoords(direction, &x, &y); + collision = GetCollisionFlagsAtCoords(player, x, y, direction); + if (collision != 0 && (collision & ~(1 << (COLLISION_OUTSIDE_RANGE - 1)))) + return FALSE; + } + + return TRUE; +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From 4acb437aef4e0a3c465c63217258c104978abe0c Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 15:47:29 +0000 Subject: [PATCH 395/572] Pack enum OverworldEncounterBehaviors --- include/overworld_encounters.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index c4f574fc9f12..1e892123ec36 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -79,7 +79,7 @@ struct OWESpeciesBehavior enum OWESpeeds activeSpeed; }; -enum OverworldEncounterBehaviors +enum __attribute__((packed)) OverworldEncounterBehaviors { OWE_IGNORE_PLAYER, OWE_CHASE_PLAYER_SLOW, From cd6139b0c71c2978e2034cd983e09e593a4a6c6d Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 16:00:13 +0000 Subject: [PATCH 396/572] Revert "Add isPlayer Check" This reverts commit 66ee7cf098b11158b3ae9d780983a6818be555ac. --- src/overworld_encounters.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 47c7ca6e3eab..dd7b6f1919ac 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -811,9 +811,6 @@ static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType) { - if (objectEvent->isPlayer) - return FALSE; - bool32 isOWE = (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_ENCOUNTER); switch (oweType) { From 5a3266b2e42ed2e694f43f8ce6a7094e9c54c20b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 16:08:11 +0000 Subject: [PATCH 397/572] Fix Roamer Safety --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index dd7b6f1919ac..0cd1803473fa 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1006,7 +1006,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c if ((playerIsCollider || playerIsObstacle)) { struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; - if (!IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), + if (wildMon->sRoamerOutbreakStatus && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) { RemoveObjectEventByLocalIdAndMap(wildMon->localId, wildMon->mapNum, wildMon->mapGroup); From e76ad788e684074a8cf2d99aa56c5a253a205d66 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 16:36:49 +0000 Subject: [PATCH 398/572] Fix If Check --- src/overworld_encounters.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 0cd1803473fa..daa94e7dd1df 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1077,9 +1077,8 @@ bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon) if (!IsOverworldWildEncounter(mon, OWE_ANY) || mon->movementType == MOVEMENT_TYPE_WANDER_AROUND_OWE) return FALSE; - if ((TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) - || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING)) - && OWE_IsPlayerInsideMonActiveDistance(mon)); + if (OWE_IsPlayerInsideMonActiveDistance(mon) && (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) + || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING))) return TRUE; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; From 258b3869cddbce1dfcb397bdc19387b7bc77c7aa Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 19:35:20 +0000 Subject: [PATCH 399/572] Reorder Lines --- src/event_object_movement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 8292c920092a..83f19c0e4a3b 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11902,8 +11902,8 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) { - sprite->sTypeFuncId = 12; MoveToPlayerForEncounter(objectEvent, sprite); + sprite->sTypeFuncId = 12; return FALSE; } From 8c73ecef5869a45a5dd9c2de2c636efc24288c20 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 20:15:09 +0000 Subject: [PATCH 400/572] Update how_to_overworld_wild_encounters.md --- docs/tutorials/how_to_overworld_wild_encounters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 8513a3a45bfe..ba85cbf696fa 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -30,7 +30,7 @@ Special spawns can be one of three types, in decreasing priority: A Roamer, Feeb ### Restricted Despawning ## High Priority and Low Priority OWEs -Low Priority OWEs may face not be spawned or even be destroyed in certain situations. There are palettes and object tiles checks to prevent these from spawning if it would fail, as well as similar checks for number of event objects, palettes and object tiles that despawn the oldest if High Priority OWEs or other objects event are attempting to be spawned and Low Priority OWEs are using these resources. High priority OWEs are treated as regular objects, and will not be destroyed, may cause the destruction of Generated OWEs and will not face spawning restrictions as all outlined above. +Low Priority OWEs may face not be spawned or even be destroyed in certain situations. There are palettes and object tiles checks to prevent these from spawning if it would fail, as well as similar checks for number of event objects, palettes and object tiles that despawn the oldest of High Priority OWEs or other objects event are attempting to be spawned and Low Priority OWEs are using these resources. Low Priority OWEs may also be destroyed by NPC object events colliding with them due to their movement functions or them being in the way of a trainer interaction. High priority OWEs are treated as regular objects and will not be destroyed in the ways outlined above, but may cause the destruction of Generated OWEs and will not face spawning restrictions. These despawn conditions will overwrite the restrictive despawns mentioned above. > Is this true? Does it take the oldest or oldest not marked for restriction, what if all are marked to not despawn. > Setting `OW_GFX_COMPRESS` to `FALSE` will free more space in VRAM, allowing for more large OWEs to spawn. From 8d900ea703449256dcec7b7ad6d15dc7fc1044ca Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 20:20:33 +0000 Subject: [PATCH 401/572] Remove Nesting --- src/overworld_encounters.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index daa94e7dd1df..b50d27c17851 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1003,22 +1003,22 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle, OWE_ANY)); bool32 playerIsObstacle = (obstacle->isPlayer && IsOverworldWildEncounter(collider, OWE_ANY)); - if ((playerIsCollider || playerIsObstacle)) + if (!(playerIsCollider || playerIsObstacle)) + return; + + struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; + if (wildMon->sRoamerOutbreakStatus && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), + gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) { - struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; - if (wildMon->sRoamerOutbreakStatus && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), - gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) - { - RemoveObjectEventByLocalIdAndMap(wildMon->localId, wildMon->mapNum, wildMon->mapGroup); - return; - } + RemoveObjectEventByLocalIdAndMap(wildMon->localId, wildMon->mapNum, wildMon->mapGroup); + return; + } - LockPlayerFieldControls(); - if (!OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE) - OWE_StartEncounterInstant(wildMon); + LockPlayerFieldControls(); + if (!OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE) + OWE_StartEncounterInstant(wildMon); - wildMon->sOverworldEncounterLevel |= OWE_FLAG_START_ENCOUNTER; - } + wildMon->sOverworldEncounterLevel |= OWE_FLAG_START_ENCOUNTER; } void OverworldWildEncounter_RemoveObjectOnBattle(void) From e446e0dacbbfbb385b713ec0e488f64536986a5e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 20:22:02 +0000 Subject: [PATCH 402/572] Reorder Collision Checks --- src/overworld_encounters.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index b50d27c17851..63850920a01a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1037,17 +1037,17 @@ void OverworldWildEncounter_RemoveObjectOnBattle(void) // Returns TRUE if movement is restricted. bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction) { + if (GetCollisionInDirection(objectEvent, direction)) + return TRUE; + if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) return FALSE; - - if ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileInDirection(objectEvent, direction)) - || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapInDirection(objectEvent, direction))) - return TRUE; if (OWE_CanAwareMonSeePlayer(objectEvent) && OW_WILD_ENCOUNTERS_UNRESTRICT_SIGHT) return FALSE; - if (GetCollisionInDirection(objectEvent, direction)) + if ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileInDirection(objectEvent, direction)) + || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapInDirection(objectEvent, direction))) return TRUE; return FALSE; From 6833082a65fc3a600a48b6f2f018878a08a28d51 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 21:59:31 +0000 Subject: [PATCH 403/572] Hide Followers if they are in the way of collision --- include/config/follower_npc.h | 2 +- include/follower_npc.h | 2 ++ include/overworld_encounters.h | 2 +- src/event_object_movement.c | 2 +- src/follower_npc.c | 5 +++++ src/overworld_encounters.c | 28 +++++++++++++++++++++++++++- 6 files changed, 37 insertions(+), 4 deletions(-) diff --git a/include/config/follower_npc.h b/include/config/follower_npc.h index 222248ae8053..6bae9fa24116 100644 --- a/include/config/follower_npc.h +++ b/include/config/follower_npc.h @@ -2,7 +2,7 @@ #define GUARD_FOLLOWER_NPC_OVERWORLD_H // NPC Followers -#define FNPC_ENABLE_NPC_FOLLOWERS FALSE // Enables the use of script macros to designate NPCs to follow behind the player, DPP style. Slightly increases the size of the saveblock (SaveBlock3). +#define FNPC_ENABLE_NPC_FOLLOWERS TRUE // Enables the use of script macros to designate NPCs to follow behind the player, DPP style. Slightly increases the size of the saveblock (SaveBlock3). #define FNPC_FLAG_HEAL_AFTER_FOLLOWER_BATTLE 0 // Replace the 0 with a flag in order to use that flag to toggle whether the Player's party will be automatically healed after every follower partner battle. If you want this to always be active without using a flag, replace 0 with FNPC_ALWAYS. #define FNPC_FLAG_PARTNER_WILD_BATTLES 0 // Replace the 0 with a flag in order to use that flag to toggle whether the follower partner will join you for wild battles. If you want this to always be active without using a flag, replace 0 with FNPC_ALWAYS. #define FNPC_NPC_FOLLOWER_WILD_BATTLE_VS_2 TRUE // If set to TRUE, two wild Pokemon will show up to the partner battle instead of just one. diff --git a/include/follower_npc.h b/include/follower_npc.h index bba2a84b816b..694affb54a5b 100644 --- a/include/follower_npc.h +++ b/include/follower_npc.h @@ -142,4 +142,6 @@ void FollowerNPC_TryRemoveFollowerOnWhiteOut(void); void Task_MoveNPCFollowerAfterForcedMovement(u8 taskId); void Task_HideNPCFollowerAfterMovementFinish(u8 taskId); +const u8 *const GetFollowerNPCHideMovementsSpeed(enum Direction direction, u32 walkSpeed); + #endif // GUARD_FOLLOWER_NPC_H diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 1e892123ec36..03cacec6eabf 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -108,7 +108,7 @@ bool32 CanRemoveOverworldEncounter(u32 localId); u32 RemoveOldestOverworldEncounter(void); bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); -void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); +void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider, s32 xCollision, s32 yCollision); void OverworldWildEncounter_RemoveObjectOnBattle(void); bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction); void DespawnOldestOWE_Pal(void); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 83f19c0e4a3b..a542eb41d9a4 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -6470,7 +6470,7 @@ u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, b if (OWE_DespawnMonDueToNPCCollision(curObject, objectEvent)) continue; - OWE_TryTriggerEncounter(objectEvent, curObject); + OWE_TryTriggerEncounter(objectEvent, curObject, x, y); return i; } } diff --git a/src/follower_npc.c b/src/follower_npc.c index dbec206b2286..79204cbdeefb 100644 --- a/src/follower_npc.c +++ b/src/follower_npc.c @@ -1866,3 +1866,8 @@ void ScriptChangeFollowerNPCBattlePartner(struct ScriptContext *ctx) SetFollowerNPCData(FNPC_DATA_BATTLE_PARTNER, newBattlePartner); } + +const u8 *const GetFollowerNPCHideMovementsSpeed(enum Direction direction, u32 walkSpeed) +{ + return FollowerNPCHideMovementsSpeedTable[direction][walkSpeed]; +} diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 63850920a01a..1e2d21a5680e 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -9,11 +9,13 @@ #include "fieldmap.h" #include "field_effect.h" #include "field_player_avatar.h" +#include "follower_npc.h" #include "metatile_behavior.h" #include "overworld.h" #include "random.h" #include "roamer.h" #include "script.h" +#include "script_movement.h" #include "sprite.h" #include "sound.h" #include "task.h" @@ -993,7 +995,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( return templateOWE; } -void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) +void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider, s32 xCollision, s32 yCollision) { // The only automatically interacts with an OW Encounter when; // Not using a repel or the DexNav is inactive. @@ -1016,7 +1018,31 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c LockPlayerFieldControls(); if (!OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE) + { OWE_StartEncounterInstant(wildMon); + return; + } + + struct ObjectEvent *followerMon = GetFollowerObject(); + if (followerMon != NULL && !followerMon->invisible + && ((followerMon->currentCoords.x == xCollision && followerMon->currentCoords.y == yCollision) + || (followerMon->previousCoords.x == xCollision && followerMon->previousCoords.y == yCollision))) + { + ClearObjectEventMovement(followerMon, &gSprites[followerMon->spriteId]); + gSprites[followerMon->spriteId].animCmdIndex = 0; + ObjectEventSetHeldMovement(followerMon, MOVEMENT_ACTION_ENTER_POKEBALL); + } + + u32 idFollowerNPC = GetFollowerNPCObjectId(); + struct ObjectEvent *followerNPC = &gObjectEvents[idFollowerNPC]; + if (FNPC_ENABLE_NPC_FOLLOWERS && PlayerHasFollowerNPC() && !followerNPC->invisible + && ((followerNPC->currentCoords.x == xCollision && followerNPC->currentCoords.y == yCollision) + || (followerNPC->previousCoords.x == xCollision && followerNPC->previousCoords.y == yCollision))) + { + enum Direction direction = DetermineFollowerNPCDirection(&gObjectEvents[gPlayerAvatar.objectEventId], followerNPC); + ScriptMovement_StartObjectMovementScript(OBJ_EVENT_ID_NPC_FOLLOWER, followerNPC->mapGroup, followerNPC->mapNum, GetFollowerNPCHideMovementsSpeed(direction, 3)); + SetFollowerNPCData(FNPC_DATA_WARP_END, FNPC_WARP_REAPPEAR); + } wildMon->sOverworldEncounterLevel |= OWE_FLAG_START_ENCOUNTER; } From 75736929ceca9f3a4a78e64ad526e38f2e3dd04a Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 25 Jan 2026 22:22:40 +0000 Subject: [PATCH 404/572] Fix Collision Checking --- include/event_object_movement.h | 1 + src/event_object_movement.c | 3 +-- src/overworld_encounters.c | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 2ffd73c24a4a..5a39d49fa261 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -191,6 +191,7 @@ u8 GetWalkInPlaceFastMovementAction(u32); u8 GetWalkInPlaceNormalMovementAction(u32); u8 GetWalkInPlaceSlowMovementAction(u32); enum Collision GetCollisionAtCoords(struct ObjectEvent *objectEvent, s16 x, s16 y, enum Direction dir); +bool32 IsMetatileDirectionallyImpassable(struct ObjectEvent *objectEvent, s16 x, s16 y, enum Direction direction); u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, bool32 addCoords); void MoveCoords(enum Direction direction, s16 *x, s16 *y); bool8 ObjectEventIsHeldMovementActive(struct ObjectEvent *objectEvent); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index a542eb41d9a4..c44dba2a8ac9 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -136,7 +136,6 @@ static void TryEnableObjectEventAnim(struct ObjectEvent *, struct Sprite *); static void ObjectEventExecHeldMovementAction(struct ObjectEvent *, struct Sprite *); static void UpdateObjectEventSpriteAnimPause(struct ObjectEvent *, struct Sprite *); static bool8 IsCoordOutsideObjectEventMovementRange(struct ObjectEvent *, s16, s16); -static bool8 IsMetatileDirectionallyImpassable(struct ObjectEvent *, s16, s16, enum Direction); static bool8 DoesObjectCollideWithObjectAt(struct ObjectEvent *, s16, s16); static void UpdateObjectEventOffscreen(struct ObjectEvent *, struct Sprite *); static void UpdateObjectEventSpriteVisibility(struct ObjectEvent *, struct Sprite *); @@ -6432,7 +6431,7 @@ static bool8 IsCoordOutsideObjectEventMovementRange(struct ObjectEvent *objectEv return FALSE; } -static bool8 IsMetatileDirectionallyImpassable(struct ObjectEvent *objectEvent, s16 x, s16 y, enum Direction direction) +bool32 IsMetatileDirectionallyImpassable(struct ObjectEvent *objectEvent, s16 x, s16 y, enum Direction direction) { if (gOppositeDirectionBlockedMetatileFuncs[direction - 1](objectEvent->currentMetatileBehavior) || gDirectionBlockedMetatileFuncs[direction - 1](MapGridGetMetatileBehaviorAt(x, y))) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 1e2d21a5680e..de20537a2f29 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1566,13 +1566,14 @@ static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction s16 x = player->currentCoords.x; s16 y = player->currentCoords.y; u32 i; - u32 collision; for (i = 0; i < distance; i++) { MoveCoords(direction, &x, &y); - collision = GetCollisionFlagsAtCoords(player, x, y, direction); - if (collision != 0 && (collision & ~(1 << (COLLISION_OUTSIDE_RANGE - 1)))) + if (MapGridGetCollisionAt(x, y) + || GetMapBorderIdAt(x, y) == CONNECTION_INVALID + || IsMetatileDirectionallyImpassable(player, x, y, GetOppositeDirection(direction)) + || IsElevationMismatchAt(player->currentElevation, x, y)) return FALSE; } From fe5c2c31fa98cd1540b19114f6a50eb06938a807 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:41:57 +0000 Subject: [PATCH 405/572] Add Battle Frontier to OWEs --- include/battle_pyramid.h | 1 + include/config/overworld.h | 2 + include/wild_encounter.h | 2 + src/battle_pyramid.c | 29 +++++++++++ src/overworld_encounters.c | 99 +++++++++++++++++++++++++++++++++++--- 5 files changed, 127 insertions(+), 6 deletions(-) diff --git a/include/battle_pyramid.h b/include/battle_pyramid.h index d55d8d2e9399..c81027e045e5 100644 --- a/include/battle_pyramid.h +++ b/include/battle_pyramid.h @@ -22,5 +22,6 @@ void LoadBattlePyramidObjectEventTemplates(void); void LoadBattlePyramidFloorObjectEventScripts(void); u8 GetNumBattlePyramidObjectEvents(void); u16 GetBattlePyramidPickupItemId(void); +u32 GetBattlePyramidWildMonHeaderIdFromSpecies(u32 species, u32 round, enum FrontierLevelMode levelMode); #endif // GUARD_BATTLE_PYRAMID_H diff --git a/include/config/overworld.h b/include/config/overworld.h index c59c16ace1f4..55f2b2153cf1 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -76,6 +76,8 @@ // Overworld Encounters #define OW_WILD_ENCOUNTERS_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. #define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. +#define OW_WILD_ENCOUNTERS_BATTLE_PIKE TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pike, if FALSE random encounters will be enabled instead. Requires OW_WILD_ENCOUNTERS_OVERWORLD to be TRUE. +#define OW_WILD_ENCOUNTERS_BATTLE_PYRAMID TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pyramid, if FALSE random encounters will be enabled instead. Requires OW_WILD_ENCOUNTERS_OVERWORLD to be TRUE. #define OW_WILD_ENCOUNTERS_RESTRICT_METATILE TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same encounter metatile behavior as the one it is currently on, if any. #define OW_WILD_ENCOUNTERS_RESTRICT_MAP TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within their current map bounds. #define OW_WILD_ENCOUNTERS_UNRESTRICT_SIGHT FALSE // If TRUE, OW Pokémon spawned as overworld wild encounters will ignore all movement restrictions when they can see the player. diff --git a/include/wild_encounter.h b/include/wild_encounter.h index ee8970330f53..02d9850daf8e 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -46,6 +46,8 @@ struct WildPokemonHeader extern const struct WildPokemonHeader gWildMonHeaders[]; +extern const struct WildPokemonHeader gBattlePikeWildMonHeaders[]; +extern const struct WildPokemonHeader gBattlePyramidWildMonHeaders[]; extern const struct WildPokemon gWildFeebas; extern bool8 gIsFishingEncounter; extern bool8 gIsSurfingEncounter; diff --git a/src/battle_pyramid.c b/src/battle_pyramid.c index f777be99cf35..9b9c76f823b0 100644 --- a/src/battle_pyramid.c +++ b/src/battle_pyramid.c @@ -2206,3 +2206,32 @@ u16 GetBattlePyramidPickupItemId(void) else return sPickupItemsLvl50[round][i]; } + +u32 GetBattlePyramidWildMonHeaderIdFromSpecies(u32 species, u32 round, enum FrontierLevelMode levelMode) +{ + // This check may return an incorrect index if duplicate species exist in the same round's table. + // This does no occur in vanilla Emerald. + const struct PyramidWildMon *wildMons; + u32 i; + + if (round >= TOTAL_PYRAMID_ROUNDS) + return 0; + + if (levelMode != FRONTIER_LVL_50) + wildMons = sOpenLevelWildMonPointers[round]; + else + wildMons = sLevel50WildMonPointers[round]; + + for (i = 0; ; i++) + { + u32 entrySpecies = SanitizeSpeciesId(wildMons[i].species); + // Will trigger an assert and failure if species in the table before desired species is disabled. + + if (entrySpecies == SPECIES_NONE) + return 0; + + if (entrySpecies == species) + return i + 1; + } +} + diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index de20537a2f29..685ae0def866 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -21,6 +21,7 @@ #include "task.h" #include "trainer_hill.h" #include "wild_encounter.h" +#include "constants/battle_frontier.h" #include "constants/event_objects.h" #include "constants/field_effects.h" #include "constants/layouts.h" @@ -65,6 +66,7 @@ static u32 GetNumActiveOverworldEncounters(void); static u32 GetNumActiveGeneratedOverworldEncounters(void); static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); +static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); static bool32 CreateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); @@ -92,8 +94,8 @@ void UpdateOverworldEncounters(void) if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER) - || CurrentBattlePyramidLocation() - || InBattlePike() + || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !OW_WILD_ENCOUNTERS_BATTLE_PIKE) + || (CurrentBattlePyramidLocation() && !OW_WILD_ENCOUNTERS_BATTLE_PYRAMID) || InTrainerHillChallenge()) { if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) @@ -406,8 +408,17 @@ static bool8 TrySelectTile(s16* outX, s16* outY) elevation = MapGridGetElevationAt(x, y); - if (!AreCoordsInsidePlayerMap(x, y)) - return FALSE; + if (gMapHeader.mapLayoutId != LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + { + if (!AreCoordsInsidePlayerMap(x, y)) + return FALSE; + } + else + { + if (x < 0 || x >= 32 || y < 0 || y >= 32) + return FALSE; + } + // 0 is change of elevation, 15 is multiple elevation e.g. bridges // Causes weird interaction issues so just don't let mons spawn here @@ -421,6 +432,10 @@ static bool8 TrySelectTile(s16* outX, s16* outY) if (!OWE_ShouldSpawnWaterMons() && (MetatileBehavior_IsLandWildEncounter(tileBehavior) || MetatileBehavior_IsIndoorEncounter(tileBehavior))) isEncounterTile = TRUE; + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS + || gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + isEncounterTile = TRUE; + if (isEncounterTile && !MapGridGetCollisionAt(x, y)) { *outX = x; @@ -473,6 +488,9 @@ void CreateOverworldWildEncounter(void) GiveMonInitialMoveset(&gEnemyParty[0]); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); + if (CreateOverworldWildEncounter_CheckBattleFrontier(headerId)) + return; + if (CreateOverworldWildEncounter_CheckMassOutbreak(indexRoamerOutbreak, speciesId)) return; @@ -496,6 +514,37 @@ static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak) return FALSE; } +static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId) +{ + if (headerId == HEADER_NONE) + { + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) + { + TryGenerateBattlePikeWildMon(FALSE); + BattleSetup_StartBattlePikeWildBattle(); + return TRUE; + } + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + { + enum FrontierLevelMode levelMode = gSaveBlock2Ptr->frontier.lvlMode; + u32 species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); + u32 round = (gSaveBlock2Ptr->frontier.pyramidWinStreaks[levelMode] / FRONTIER_STAGES_PER_CHALLENGE) % TOTAL_PYRAMID_ROUNDS; + u32 id = GetBattlePyramidWildMonHeaderIdFromSpecies(species, round, levelMode); + assertf(id, "could not find species in battle pyramid wild mon data. defaulting to regular encounter.\nspecies: %d\nround: %d\nlevel mode: %d", species, round, levelMode); + { + return FALSE; + } + + SetMonData(&gEnemyParty[0], MON_DATA_SPECIES, &id); + GenerateBattlePyramidWildMon(); + BattleSetup_StartWildBattle(); + return TRUE; + } + } + + return FALSE; +} + static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId) { if (indexRoamerOutbreak == OWE_MASS_OUTBREAK_INDEX @@ -681,7 +730,31 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam u32 metatileBehavior = MapGridGetMetatileBehaviorAt(x, y); if (headerId == HEADER_NONE) + { + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) + { + headerId = GetBattlePikeWildMonHeaderId(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) + return FALSE; + else if (!TryGenerateBattlePikeWildMon(TRUE)) + return FALSE; + + return TRUE; + } + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + { + headerId = gSaveBlock2Ptr->frontier.curChallengeBattleNum; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) + return FALSE; + + GenerateBattlePyramidWildMon(); + return TRUE; + } + return FALSE; + } if (MetatileBehavior_IsWaterWildEncounter(metatileBehavior)) { @@ -799,7 +872,21 @@ static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) enum TimeOfDay timeOfDay; if (headerId == HEADER_NONE) + { + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) + { + headerId = GetBattlePikeWildMonHeaderId(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + return gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; + } + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + { + headerId = gSaveBlock2Ptr->frontier.curChallengeBattleNum; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + return gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; + } return FALSE; + } if (shouldSpawnWaterMons) { @@ -1482,8 +1569,8 @@ bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *object bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void) { - if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS - || gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + if ((gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !OW_WILD_ENCOUNTERS_BATTLE_PIKE) + || (CurrentBattlePyramidLocation() && !OW_WILD_ENCOUNTERS_BATTLE_PYRAMID)) return FALSE; return !OW_WILD_ENCOUNTERS_RANDOM; From b04f390b8bb2344accc1fc7d050a78735d9f4527 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:15:47 +0000 Subject: [PATCH 406/572] Update how_to_overworld_wild_encounters.md --- docs/tutorials/how_to_overworld_wild_encounters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index ba85cbf696fa..3ca4d446c16c 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -20,7 +20,7 @@ Assuming the following `graphicsId` have `.trainerType` set to `TRAINER_TYPE_ENC As level and species are potentially taken from the Wild Encounter Header, an `assertf` to let developers know when an invalid value is used. If the resultant level is invalid, it will be set to `MIN_LEVEL` (1). If the species is invalid, a replacement object will be created using `OBJ_EVENT_GFX_BOY_1`, this will not be an OWE of any kind. -No matter how much of a Manual OWE is defined, it is considered a high priority OWE, and treated as a regular object event in all ways other than ones outlined above. These however, have restricted special spawns types. +No matter how much of a Manual OWE is defined, it is considered a high priority OWE, and treated as a regular object event in all ways other than ones outlined above. They will always spawn, regardless of level of abilties of player Pokémon, however, they have restricted special spawns types. ### Special Spawns Special spawns can be one of three types, in decreasing priority: A Roamer, Feebas, or Mass Outbreak Encounter. Generated OWEs can have any of these, however, Manual OWEs can only have the Feebas Special Spawn. These work exactly as they would normally; From 9d56aa171496ac4c07da5a08e4611014ede561fb Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:18:03 +0000 Subject: [PATCH 407/572] Fix assert conditions --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 685ae0def866..6ba1f3d6e0a4 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1059,7 +1059,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( if (levelTemplate) level = levelTemplate; - assertf(level < MIN_LEVEL || level > MAX_LEVEL, "invalid manual overworld encounter\nlevel: %d\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", level, speciesId, x, y) + assertf(level >= MIN_LEVEL && level <= MAX_LEVEL, "invalid manual overworld encounter\nlevel: %d\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", level, speciesId, x, y) { level = MIN_LEVEL; } From afb5eda87f625199c8900dff0d5788b54f699bfc Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:33:47 +0000 Subject: [PATCH 408/572] stray config --- include/config/follower_npc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config/follower_npc.h b/include/config/follower_npc.h index 6bae9fa24116..222248ae8053 100644 --- a/include/config/follower_npc.h +++ b/include/config/follower_npc.h @@ -2,7 +2,7 @@ #define GUARD_FOLLOWER_NPC_OVERWORLD_H // NPC Followers -#define FNPC_ENABLE_NPC_FOLLOWERS TRUE // Enables the use of script macros to designate NPCs to follow behind the player, DPP style. Slightly increases the size of the saveblock (SaveBlock3). +#define FNPC_ENABLE_NPC_FOLLOWERS FALSE // Enables the use of script macros to designate NPCs to follow behind the player, DPP style. Slightly increases the size of the saveblock (SaveBlock3). #define FNPC_FLAG_HEAL_AFTER_FOLLOWER_BATTLE 0 // Replace the 0 with a flag in order to use that flag to toggle whether the Player's party will be automatically healed after every follower partner battle. If you want this to always be active without using a flag, replace 0 with FNPC_ALWAYS. #define FNPC_FLAG_PARTNER_WILD_BATTLES 0 // Replace the 0 with a flag in order to use that flag to toggle whether the follower partner will join you for wild battles. If you want this to always be active without using a flag, replace 0 with FNPC_ALWAYS. #define FNPC_NPC_FOLLOWER_WILD_BATTLE_VS_2 TRUE // If set to TRUE, two wild Pokemon will show up to the partner battle instead of just one. From 41b3f5a02965b76359b7dde7ef68bb6100e1a6b2 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:38:26 +0000 Subject: [PATCH 409/572] Consistent checking --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 6ba1f3d6e0a4..85675706c612 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -95,7 +95,7 @@ void UpdateOverworldEncounters(void) if (!OW_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER) || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !OW_WILD_ENCOUNTERS_BATTLE_PIKE) - || (CurrentBattlePyramidLocation() && !OW_WILD_ENCOUNTERS_BATTLE_PYRAMID) + || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR && !OW_WILD_ENCOUNTERS_BATTLE_PYRAMID) || InTrainerHillChallenge()) { if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) From cf14b4fc101de5d7f37940760d1eb626200c9264 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:51:53 +0000 Subject: [PATCH 410/572] Remove Comment --- src/overworld_encounters.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 85675706c612..8b977cd4a671 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -142,8 +142,6 @@ void UpdateOverworldEncounters(void) u32 level; u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); - // Does X and Y need to be adjusted for map offset here, for all subsequent? - // If so can be done when setting them originally? if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(level) || !IsAbilityAllowingEncounter(level) From d1b20f177bc7f338c77b41966ad136cb0f1315a3 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:57:10 +0000 Subject: [PATCH 411/572] Fix Follower NPC Direction None --- src/event_object_movement.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index c44dba2a8ac9..bee3486d7d41 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -9947,6 +9947,9 @@ enum Direction DetermineObjectEventDirectionFromObject(struct ObjectEvent *objec s16 distanceX = objectOne->currentCoords.x - objectTwo->currentCoords.x; s16 distanceY = objectOne->currentCoords.y - objectTwo->currentCoords.y; + if (distanceX == 0 && distanceY == 0) + return DIR_NONE; + // Get absolute X distance. if (distanceX < 0) absX = distanceX * -1; @@ -10019,8 +10022,6 @@ enum Direction DetermineObjectEventDirectionFromObject(struct ObjectEvent *objec } } } - - return DIR_NONE; } void ObjectEventTurnToObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo) From 1315aef7a18e472261d03c7e971c1329ac768b71 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:27:51 +0000 Subject: [PATCH 412/572] Edit Follower NPC OWE Function --- include/follower_npc.h | 2 -- src/follower_npc.c | 5 ----- src/overworld_encounters.c | 6 ++++-- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/include/follower_npc.h b/include/follower_npc.h index 694affb54a5b..bba2a84b816b 100644 --- a/include/follower_npc.h +++ b/include/follower_npc.h @@ -142,6 +142,4 @@ void FollowerNPC_TryRemoveFollowerOnWhiteOut(void); void Task_MoveNPCFollowerAfterForcedMovement(u8 taskId); void Task_HideNPCFollowerAfterMovementFinish(u8 taskId); -const u8 *const GetFollowerNPCHideMovementsSpeed(enum Direction direction, u32 walkSpeed); - #endif // GUARD_FOLLOWER_NPC_H diff --git a/src/follower_npc.c b/src/follower_npc.c index 79204cbdeefb..dbec206b2286 100644 --- a/src/follower_npc.c +++ b/src/follower_npc.c @@ -1866,8 +1866,3 @@ void ScriptChangeFollowerNPCBattlePartner(struct ScriptContext *ctx) SetFollowerNPCData(FNPC_DATA_BATTLE_PARTNER, newBattlePartner); } - -const u8 *const GetFollowerNPCHideMovementsSpeed(enum Direction direction, u32 walkSpeed) -{ - return FollowerNPCHideMovementsSpeedTable[direction][walkSpeed]; -} diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8b977cd4a671..109d8005119f 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1125,8 +1125,10 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c || (followerNPC->previousCoords.x == xCollision && followerNPC->previousCoords.y == yCollision))) { enum Direction direction = DetermineFollowerNPCDirection(&gObjectEvents[gPlayerAvatar.objectEventId], followerNPC); - ScriptMovement_StartObjectMovementScript(OBJ_EVENT_ID_NPC_FOLLOWER, followerNPC->mapGroup, followerNPC->mapNum, GetFollowerNPCHideMovementsSpeed(direction, 3)); - SetFollowerNPCData(FNPC_DATA_WARP_END, FNPC_WARP_REAPPEAR); + ClearObjectEventMovement(followerNPC, &gSprites[followerNPC->spriteId]); + gSprites[followerNPC->spriteId].animCmdIndex = 0; + ObjectEventSetHeldMovement(followerNPC, GetWalkNormalMovementAction(direction)); + CreateTask(Task_HideNPCFollowerAfterMovementFinish, 2); } wildMon->sOverworldEncounterLevel |= OWE_FLAG_START_ENCOUNTER; From 49c8b1f26819950852a59c2208ec59ddd1224438 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 21:11:43 +0000 Subject: [PATCH 413/572] Fix 1 Free OWE Palette Loading --- src/overworld_encounters.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 109d8005119f..509225ed3340 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -212,7 +212,8 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is u32 metatileBehavior = MapGridGetMetatileBehaviorAt(x, y); struct SpritePalette palette = OWE_GetSpawnAnimFldEffPalette(OWE_GetSpawnDespawnAnimType(metatileBehavior)); // If the mon's palette or field effect palette isn't already loaded, don't spawn. - if (IndexOfSpritePaletteTag(tag) == 0xFF || IndexOfSpritePaletteTag(palette.tag) == 0xFF) + // Include check if female or shiny mon is loaded and use that tag if possible + if (IndexOfSpritePaletteTag(tag) == 0xFF && IndexOfSpritePaletteTag(palette.tag) == 0xFF) return FALSE; } else if (numFreePalSlots == 0) From b23ae0e388d9721b31cc2c6b2c5643dfd77ec9a0 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 21:11:56 +0000 Subject: [PATCH 414/572] Disable Generated OWE Reflections --- src/field_effect_helpers.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index b2c52dd045fb..7abc0ecdcc6c 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -65,6 +65,9 @@ void SetUpShadow(struct ObjectEvent *objectEvent) void SetUpReflection(struct ObjectEvent *objectEvent, struct Sprite *sprite, bool8 stillReflection) { + if (IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) + return; + struct Sprite *reflectionSprite; reflectionSprite = &gSprites[CreateCopySpriteAt(sprite, sprite->x, sprite->y, 152)]; From 2600c357e24f0949dcbd4c5aa031f6e210bcc2ab Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 21:52:35 +0000 Subject: [PATCH 415/572] Cleanup OWE_CheckPathToPlayerFromCollision --- src/overworld_encounters.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 509225ed3340..e4c57ae01989 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -77,6 +77,8 @@ static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animS static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); static void OWE_StartEncounterInstant(struct ObjectEvent *mon); static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance); +static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 x, s16 y, enum Direction newDirection, enum Direction collisionDirection); +static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); void OWE_ResetSpawnCounterPlayAmbientCry(void) { @@ -1267,33 +1269,46 @@ bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon) return FALSE; } -static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, u32 newDirection) +static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 x, s16 y, enum Direction newDirection, enum Direction collisionDirection) +{ + if (OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) + return FALSE; + + if (OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y)) + return FALSE; + + if (GetCollisionAtCoords(mon, x, y, collisionDirection)) + return FALSE; + + return TRUE; +} + +static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection) { s16 x = mon->currentCoords.x; s16 y = mon->currentCoords.y; MoveCoords(newDirection, &x, &y); - if (!GetCollisionAtCoords(mon, x, y, newDirection) && !(OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) && !(OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y))) + if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, newDirection)) { if (mon->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) return GetOppositeDirection(newDirection); MoveCoords(mon->movementDirection, &x, &y); - if (!GetCollisionAtCoords(mon, x, y, mon->movementDirection) && !(OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) && !(OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y))) + if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, mon->movementDirection)) return newDirection; } x = mon->currentCoords.x; y = mon->currentCoords.y; - MoveCoords(GetOppositeDirection(newDirection), &x, &y); - if (!GetCollisionAtCoords(mon, x, y, GetOppositeDirection(newDirection)) && !(OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) && !(OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y))) + if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, newDirection)) { if (mon->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) return newDirection; MoveCoords(mon->movementDirection, &x, &y); - if (!GetCollisionAtCoords(mon, x, y, mon->movementDirection) && !(OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) && !(OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y))) + if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, mon->movementDirection)) return GetOppositeDirection(newDirection); } @@ -1495,6 +1510,9 @@ static bool32 OWE_CheckRestrictMovementMapInDirection(struct ObjectEvent *object static bool32 OWE_CheckRestrictMovementMetatileAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xNew, u32 yNew) { + if (!OW_WILD_ENCOUNTERS_RESTRICT_METATILE) + return FALSE; + s16 xCurrent = xNew - gDirectionToVectors[direction].x; s16 yCurrent = yNew - gDirectionToVectors[direction].y; u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); @@ -1517,6 +1535,9 @@ static bool32 OWE_CheckRestrictMovementMetatileAtCoords(struct ObjectEvent *obje static bool32 OWE_CheckRestrictMovementMapAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xNew, u32 yNew) { + if (!OW_WILD_ENCOUNTERS_RESTRICT_MAP) + return FALSE; + u32 mapGroup = objectEvent->mapGroup; u32 mapNum = objectEvent->mapNum; From e1311f8e237202315befaaa778ffca9282d5fcfe Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 21:55:23 +0000 Subject: [PATCH 416/572] Clean Up OWE_CheckRestrictedMovement --- src/overworld_encounters.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e4c57ae01989..604eb94fd6e1 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1162,8 +1162,10 @@ bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 directio if (OWE_CanAwareMonSeePlayer(objectEvent) && OW_WILD_ENCOUNTERS_UNRESTRICT_SIGHT) return FALSE; - if ((OW_WILD_ENCOUNTERS_RESTRICT_METATILE && OWE_CheckRestrictMovementMetatileInDirection(objectEvent, direction)) - || (OW_WILD_ENCOUNTERS_RESTRICT_MAP && OWE_CheckRestrictMovementMapInDirection(objectEvent, direction))) + if (OWE_CheckRestrictMovementMetatileInDirection(objectEvent, direction)) + return TRUE; + + if (OWE_CheckRestrictMovementMapInDirection(objectEvent, direction)) return TRUE; return FALSE; @@ -1468,6 +1470,9 @@ static bool32 OWE_DoesRoamerObjectExist(void) static bool32 OWE_CheckRestrictMovementMetatileInDirection(struct ObjectEvent *objectEvent, u32 direction) { + if (!OW_WILD_ENCOUNTERS_RESTRICT_METATILE) + return FALSE; + s16 xCurrent = objectEvent->currentCoords.x; s16 yCurrent = objectEvent->currentCoords.y; s16 xNew = xCurrent + gDirectionToVectors[direction].x; @@ -1497,6 +1502,9 @@ static bool32 OWE_CheckRestrictMovementMetatileInDirection(struct ObjectEvent *o static bool32 OWE_CheckRestrictMovementMapInDirection(struct ObjectEvent *objectEvent, u32 direction) { + if (!OW_WILD_ENCOUNTERS_RESTRICT_MAP) + return FALSE; + s16 xCurrent = objectEvent->currentCoords.x; s16 yCurrent = objectEvent->currentCoords.y; s16 xNew = xCurrent + gDirectionToVectors[direction].x; From ba699a6a0111abf751921b272d54bd0d155020be Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 22:39:46 +0000 Subject: [PATCH 417/572] Consolidate OWE_CheckRestrictMovement Functions --- src/overworld_encounters.c | 77 ++++++++------------------------------ 1 file changed, 16 insertions(+), 61 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 604eb94fd6e1..fdb67fe903d8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -58,10 +58,8 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_DoesRoamerObjectExist(void); -static bool32 OWE_CheckRestrictMovementMetatileInDirection(struct ObjectEvent *objectEvent, u32 direction); -static bool32 OWE_CheckRestrictMovementMapInDirection(struct ObjectEvent *objectEvent, u32 direction); -static bool32 OWE_CheckRestrictMovementMetatileAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xNew, u32 yNew); -static bool32 OWE_CheckRestrictMovementMapAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xNew, u32 yNew); +static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); +static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); static u32 GetNumActiveOverworldEncounters(void); static u32 GetNumActiveGeneratedOverworldEncounters(void); static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); @@ -1161,11 +1159,16 @@ bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 directio if (OWE_CanAwareMonSeePlayer(objectEvent) && OW_WILD_ENCOUNTERS_UNRESTRICT_SIGHT) return FALSE; - - if (OWE_CheckRestrictMovementMetatileInDirection(objectEvent, direction)) + + s32 xCurrent = objectEvent->currentCoords.x; + s32 yCurrent = objectEvent->currentCoords.y; + s32 xNew = xCurrent + gDirectionToVectors[direction].x; + s32 yNew = yCurrent + gDirectionToVectors[direction].y; + + if (OWE_CheckRestrictMovementMetatile(xCurrent, yCurrent, xNew, yNew)) return TRUE; - if (OWE_CheckRestrictMovementMapInDirection(objectEvent, direction)) + if (OWE_CheckRestrictMovementMap(objectEvent, xNew, yNew)) return TRUE; return FALSE; @@ -1273,10 +1276,10 @@ bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon) static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 x, s16 y, enum Direction newDirection, enum Direction collisionDirection) { - if (OWE_CheckRestrictMovementMetatileAtCoords(mon, newDirection, x, y)) + if (OWE_CheckRestrictMovementMetatile(mon->currentCoords.x, mon->currentCoords.y, x, y)) return FALSE; - if (OWE_CheckRestrictMovementMapAtCoords(mon, newDirection, x, y)) + if (OWE_CheckRestrictMovementMap(mon, x, y)) return FALSE; if (GetCollisionAtCoords(mon, x, y, collisionDirection)) @@ -1468,15 +1471,10 @@ static bool32 OWE_DoesRoamerObjectExist(void) return FALSE; } -static bool32 OWE_CheckRestrictMovementMetatileInDirection(struct ObjectEvent *objectEvent, u32 direction) +static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew) { if (!OW_WILD_ENCOUNTERS_RESTRICT_METATILE) return FALSE; - - s16 xCurrent = objectEvent->currentCoords.x; - s16 yCurrent = objectEvent->currentCoords.y; - s16 xNew = xCurrent + gDirectionToVectors[direction].x; - s16 yNew = yCurrent + gDirectionToVectors[direction].y; u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); u32 metatileBehaviourNew = MapGridGetMetatileBehaviorAt(xNew, yNew); @@ -1500,56 +1498,13 @@ static bool32 OWE_CheckRestrictMovementMetatileInDirection(struct ObjectEvent *o return TRUE; } -static bool32 OWE_CheckRestrictMovementMapInDirection(struct ObjectEvent *objectEvent, u32 direction) +static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew) { if (!OW_WILD_ENCOUNTERS_RESTRICT_MAP) return FALSE; - s16 xCurrent = objectEvent->currentCoords.x; - s16 yCurrent = objectEvent->currentCoords.y; - s16 xNew = xCurrent + gDirectionToVectors[direction].x; - s16 yNew = yCurrent + gDirectionToVectors[direction].y; - - if (AreCoordsInsidePlayerMap(xCurrent, yCurrent)) - return !AreCoordsInsidePlayerMap(xNew, yNew); - else - return AreCoordsInsidePlayerMap(xNew, yNew); -} - -static bool32 OWE_CheckRestrictMovementMetatileAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xNew, u32 yNew) -{ - if (!OW_WILD_ENCOUNTERS_RESTRICT_METATILE) - return FALSE; - - s16 xCurrent = xNew - gDirectionToVectors[direction].x; - s16 yCurrent = yNew - gDirectionToVectors[direction].y; - u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); - u32 metatileBehaviourNew = MapGridGetMetatileBehaviorAt(xNew, yNew); - - if (MetatileBehavior_IsLandWildEncounter(metatileBehaviourCurrent) - && MetatileBehavior_IsLandWildEncounter(metatileBehaviourNew)) - return FALSE; - - if (MetatileBehavior_IsWaterWildEncounter(metatileBehaviourCurrent) - && MetatileBehavior_IsWaterWildEncounter(metatileBehaviourNew)) - return FALSE; - - if (MetatileBehavior_IsIndoorEncounter(metatileBehaviourCurrent) - && MetatileBehavior_IsIndoorEncounter(metatileBehaviourNew)) - return FALSE; - - return TRUE; -} - -static bool32 OWE_CheckRestrictMovementMapAtCoords(struct ObjectEvent *objectEvent, u32 direction, u32 xNew, u32 yNew) -{ - if (!OW_WILD_ENCOUNTERS_RESTRICT_MAP) - return FALSE; - - u32 mapGroup = objectEvent->mapGroup; - u32 mapNum = objectEvent->mapNum; - - if (mapGroup == gSaveBlock1Ptr->location.mapGroup && mapNum == gSaveBlock1Ptr->location.mapNum) + if (objectEvent->mapGroup == gSaveBlock1Ptr->location.mapGroup + && objectEvent->mapNum == gSaveBlock1Ptr->location.mapNum) return !AreCoordsInsidePlayerMap(xNew, yNew); else return AreCoordsInsidePlayerMap(xNew, yNew); From 578b60d2f1f0e4357808e0ba814824f9246de0e6 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 26 Jan 2026 22:51:19 +0000 Subject: [PATCH 418/572] Rename OWE_CheckRestrictedMovementAtCoords Parameters --- src/overworld_encounters.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index fdb67fe903d8..fec1a60397d6 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -75,7 +75,7 @@ static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animS static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); static void OWE_StartEncounterInstant(struct ObjectEvent *mon); static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance); -static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 x, s16 y, enum Direction newDirection, enum Direction collisionDirection); +static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 xNew, s16 yNew, enum Direction newDirection, enum Direction collisionDirection); static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); void OWE_ResetSpawnCounterPlayAmbientCry(void) @@ -1274,15 +1274,15 @@ bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon) return FALSE; } -static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 x, s16 y, enum Direction newDirection, enum Direction collisionDirection) +static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 xNew, s16 yNew, enum Direction newDirection, enum Direction collisionDirection) { - if (OWE_CheckRestrictMovementMetatile(mon->currentCoords.x, mon->currentCoords.y, x, y)) + if (OWE_CheckRestrictMovementMetatile(mon->currentCoords.x, mon->currentCoords.y, xNew, yNew)) return FALSE; - if (OWE_CheckRestrictMovementMap(mon, x, y)) + if (OWE_CheckRestrictMovementMap(mon, xNew, yNew)) return FALSE; - if (GetCollisionAtCoords(mon, x, y, collisionDirection)) + if (GetCollisionAtCoords(mon, xNew, yNew, collisionDirection)) return FALSE; return TRUE; From a8f0fd733fd0b151d0879c969941bdb56448592e Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:53:02 -0600 Subject: [PATCH 419/572] Approach for encounter rework --- data/scripts/overworld_encounters.inc | 9 +- include/event_object_movement.h | 1 + include/overworld_encounters.h | 9 +- src/event_object_movement.c | 89 +--------------- src/field_control_avatar.c | 4 +- src/overworld_encounters.c | 145 +++++++++++++++++--------- 6 files changed, 114 insertions(+), 143 deletions(-) diff --git a/data/scripts/overworld_encounters.inc b/data/scripts/overworld_encounters.inc index 13a6c5830d74..84e33e0b5c0b 100644 --- a/data/scripts/overworld_encounters.inc +++ b/data/scripts/overworld_encounters.inc @@ -1,5 +1,12 @@ -InteractWithDynamicWildOverworldEncounter:: +InteractWithDynamicWildOverworldEncounterInstant:: lockall + goto InteractWithDynamicWildOverworldEncounter + +InteractWithDynamicWildOverworldEncounterApproach:: + lock + callnative OWE_ApproachForBattle + waitstate +InteractWithDynamicWildOverworldEncounter: applymovement VAR_LAST_TALKED, Common_Movement_ExclamationMark callnative ScriptFaceLastTalked playmoncry VAR_0x8004, CRY_MODE_DOUBLES diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 5a39d49fa261..bd4357c85610 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -556,6 +556,7 @@ bool8 PlayerIsUnderWaterfall(struct ObjectEvent *objectEvent); u8 GetObjectEventApricornTreeId(u8 objectEventId); bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); +u8 GetWalkMovementActionInDirectionWithSpeed(u32 direction, u32 speed); extern const enum Direction gStandardDirections[]; diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 03cacec6eabf..56b9d8eeade9 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -40,8 +40,6 @@ #define INVALID_SPAWN_SLOT 0xFF -#define OWE_FLAG_START_ENCOUNTER 0x80 - enum OverworldEncounterSpawnAnim { OWE_SPAWN_ANIM_GRASS, @@ -90,7 +88,8 @@ enum __attribute__((packed)) OverworldEncounterBehaviors OWE_SPECIES_BEHAVIOR_COUNT }; -extern const u8 InteractWithDynamicWildOverworldEncounter[]; +extern const u8 InteractWithDynamicWildOverworldEncounterInstant[]; +extern const u8 InteractWithDynamicWildOverworldEncounterApproach[]; void OWE_ResetSpawnCounterPlayAmbientCry(void); void UpdateOverworldEncounters(void); @@ -108,7 +107,7 @@ bool32 CanRemoveOverworldEncounter(u32 localId); u32 RemoveOldestOverworldEncounter(void); bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); -void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider, s32 xCollision, s32 yCollision); +void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); void OverworldWildEncounter_RemoveObjectOnBattle(void); bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction); void DespawnOldestOWE_Pal(void); @@ -119,12 +118,12 @@ bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); void Task_OWE_WaitMovements(u8 taskId); enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); -bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *objectEvent); bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void); bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite); void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent, u32 state); +const u8 *OWE_GetScriptPointer(void); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index bee3486d7d41..1c152b363f48 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -6469,7 +6469,7 @@ u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, b if (OWE_DespawnMonDueToNPCCollision(curObject, objectEvent)) continue; - OWE_TryTriggerEncounter(objectEvent, curObject, x, y); + OWE_TryTriggerEncounter(objectEvent, curObject); return i; } } @@ -11713,7 +11713,7 @@ static u32 TurnDirectionNinetyDegrees(u32 direction, bool32 counterclockwise) return DIR_NONE; } -static u8 GetWalkMovementActionInDirectionWithSpeed(u32 direction, u32 speed) +u8 GetWalkMovementActionInDirectionWithSpeed(u32 direction, u32 speed) { switch (speed) { @@ -11728,50 +11728,6 @@ static u8 GetWalkMovementActionInDirectionWithSpeed(u32 direction, u32 speed) return GetWalkNormalMovementAction(direction); } -static void MoveToPlayerForEncounter(struct ObjectEvent *objectEvent, struct Sprite *sprite) -{ - u16 speciesId = OW_SPECIES(objectEvent); - enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - u8 movementActionId; - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - - if (objectEvent->singleMovementActive == 0 && player->singleMovementActive == 0) - { - // Let the mon continue to take steps until right next to the player. - if (OWE_IsMonNextToPlayer(objectEvent)) - { - gSpecialVar_LastTalked = objectEvent->localId; - gSpecialVar_0x8004 = speciesId; - ScriptContext_SetupScript(InteractWithDynamicWildOverworldEncounter); - FreezeObjectEvent(objectEvent); - return; - } - } - - SetObjectEventDirection(objectEvent, direction); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - - if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) - { - s16 x = objectEvent->currentCoords.x; - s16 y = objectEvent->currentCoords.y; - MoveCoords(objectEvent->movementDirection, &x, &y); - // If colliding with the player object, don't try to walk around it. - if (GetObjectObjectCollidesWith(objectEvent, x, y, FALSE) == gPlayerAvatar.objectEventId) - { - movementActionId = GetFaceDirectionMovementAction(objectEvent->facingDirection); - } - else - { - direction = OWE_DirectionToPlayerFromCollision(objectEvent); - SetObjectEventDirection(objectEvent, direction); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - } - } - ObjectEventSetSingleMovement(objectEvent, sprite, movementActionId); - objectEvent->singleMovementActive = TRUE; -} - bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent) { gFieldEffectArguments[0] = objEvent->currentCoords.x; @@ -11795,14 +11751,6 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent bool8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) - { - ClearObjectEventMovement(objectEvent, sprite); - MoveToPlayerForEncounter(objectEvent, sprite); - sprite->sTypeFuncId = 6; - return FALSE; - } - if (WaitForMovementDelay(sprite)) { // resets a mid-movement sprite @@ -11900,13 +11848,6 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent u16 speciesId = OW_SPECIES(objectEvent); u8 movementActionId; - if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) - { - MoveToPlayerForEncounter(objectEvent, sprite); - sprite->sTypeFuncId = 12; - return FALSE; - } - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); sprite->sTypeFuncId = 12; @@ -11987,13 +11928,6 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * u16 speciesId = OW_SPECIES(objectEvent); u8 movementActionId; - if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) - { - MoveToPlayerForEncounter(objectEvent, sprite); - sprite->sTypeFuncId = 12; - return FALSE; - } - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) @@ -12060,12 +11994,6 @@ bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step10(struct ObjectEvent bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) - { - MoveToPlayerForEncounter(objectEvent, sprite); - sprite->sTypeFuncId = 12; - return FALSE; - } ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection)); objectEvent->singleMovementActive = TRUE; @@ -12113,13 +12041,6 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve u16 speciesId = OW_SPECIES(objectEvent); u8 movementActionId; - if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) - { - MoveToPlayerForEncounter(objectEvent, sprite); - sprite->sTypeFuncId = 12; - return FALSE; - } - if (distance <= 1) { SetObjectEventDirection(objectEvent, GetOppositeDirection(objectEvent->movementDirection)); @@ -12210,12 +12131,6 @@ bool8 MovementType_Despawn_OverworldWildEncounter_Step10(struct ObjectEvent *obj bool8 MovementType_Despawn_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) - { - MoveToPlayerForEncounter(objectEvent, sprite); - sprite->sTypeFuncId = 12; - return FALSE; - } if (sDespawnTimer == OWE_DESPAWN_FRAMES) { diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 066004fdf71d..676f38f6ba90 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -405,7 +405,7 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 else if (PlayerHasFollowerNPC() && objectEventId == GetFollowerNPCObjectId()) script = GetFollowerNPCScriptPointer(); else if (ShouldRunOverworldEncounterScript(objectEventId)) - script = InteractWithDynamicWildOverworldEncounter; + script = OWE_GetScriptPointer(); else script = GetObjectEventScriptPointerByObjectEventId(objectEventId); @@ -572,7 +572,7 @@ static const u8 *GetInteractedWaterScript(struct MapPosition *position, u8 metat if (IsPlayerFacingSurfableFishableWater() == TRUE && ShouldRunOverworldEncounterScript(objectEventId)) { gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; - return InteractWithDynamicWildOverworldEncounter; + return OWE_GetScriptPointer(); } if (IsFieldMoveUnlocked(FIELD_MOVE_SURF) && PartyHasMonWithSurf() == TRUE && IsPlayerFacingSurfableFishableWater() == TRUE diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index fec1a60397d6..c379604e733e 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -73,10 +73,11 @@ static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); -static void OWE_StartEncounterInstant(struct ObjectEvent *mon); +static void OWE_StartEncounter(struct ObjectEvent *mon); static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance); static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 xNew, s16 yNew, enum Direction newDirection, enum Direction collisionDirection); static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); +static void Task_OWE_ApproachForBattle(u8 taskId); void OWE_ResetSpawnCounterPlayAmbientCry(void) { @@ -159,7 +160,7 @@ void UpdateOverworldEncounters(void) .elevation = MapGridGetElevationAt(x, y), .movementType = OWE_GetMovementTypeFromSpecies(speciesId), .trainerType = TRAINER_TYPE_ENCOUNTER, - .script = InteractWithDynamicWildOverworldEncounter, + .script = OWE_GetScriptPointer(), }; u32 objectEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[objectEventId]; @@ -467,7 +468,7 @@ void CreateOverworldWildEncounter(void) u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; u32 gender = OW_FEMALE(object) ? MON_FEMALE : MON_MALE; - u32 level = (object->sOverworldEncounterLevel &= ~OWE_FLAG_START_ENCOUNTER); + u32 level = object->sOverworldEncounterLevel; u32 personality; switch (gSpeciesInfo[speciesId].genderRatio) @@ -997,7 +998,7 @@ bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) { struct ObjectEvent *object = &gObjectEvents[objectEventId]; - if (!IsOverworldWildEncounter(object, OWE_ANY) || GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithDynamicWildOverworldEncounter) + if (!IsOverworldWildEncounter(object, OWE_ANY) || GetObjectEventScriptPointerByObjectEventId(objectEventId) != OWE_GetScriptPointer()) return FALSE; gSpecialVar_0x8004 = OW_SPECIES(object); @@ -1067,7 +1068,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); if (templateOWE.script == NULL) - templateOWE.script = InteractWithDynamicWildOverworldEncounter; + templateOWE.script = OWE_GetScriptPointer(); graphicsId = speciesId + OBJ_EVENT_MON; if (isFemale) @@ -1081,7 +1082,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( return templateOWE; } -void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider, s32 xCollision, s32 yCollision) +void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) { // The only automatically interacts with an OW Encounter when; // Not using a repel or the DexNav is inactive. @@ -1102,37 +1103,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c return; } - LockPlayerFieldControls(); - if (!OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE) - { - OWE_StartEncounterInstant(wildMon); - return; - } - - struct ObjectEvent *followerMon = GetFollowerObject(); - if (followerMon != NULL && !followerMon->invisible - && ((followerMon->currentCoords.x == xCollision && followerMon->currentCoords.y == yCollision) - || (followerMon->previousCoords.x == xCollision && followerMon->previousCoords.y == yCollision))) - { - ClearObjectEventMovement(followerMon, &gSprites[followerMon->spriteId]); - gSprites[followerMon->spriteId].animCmdIndex = 0; - ObjectEventSetHeldMovement(followerMon, MOVEMENT_ACTION_ENTER_POKEBALL); - } - - u32 idFollowerNPC = GetFollowerNPCObjectId(); - struct ObjectEvent *followerNPC = &gObjectEvents[idFollowerNPC]; - if (FNPC_ENABLE_NPC_FOLLOWERS && PlayerHasFollowerNPC() && !followerNPC->invisible - && ((followerNPC->currentCoords.x == xCollision && followerNPC->currentCoords.y == yCollision) - || (followerNPC->previousCoords.x == xCollision && followerNPC->previousCoords.y == yCollision))) - { - enum Direction direction = DetermineFollowerNPCDirection(&gObjectEvents[gPlayerAvatar.objectEventId], followerNPC); - ClearObjectEventMovement(followerNPC, &gSprites[followerNPC->spriteId]); - gSprites[followerNPC->spriteId].animCmdIndex = 0; - ObjectEventSetHeldMovement(followerNPC, GetWalkNormalMovementAction(direction)); - CreateTask(Task_HideNPCFollowerAfterMovementFinish, 2); - } - - wildMon->sOverworldEncounterLevel |= OWE_FLAG_START_ENCOUNTER; + OWE_StartEncounter(wildMon); } void OverworldWildEncounter_RemoveObjectOnBattle(void) @@ -1154,9 +1125,6 @@ bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 directio if (GetCollisionInDirection(objectEvent, direction)) return TRUE; - if (OverworldWildEncounter_IsStartingWildEncounter(objectEvent)) - return FALSE; - if (OWE_CanAwareMonSeePlayer(objectEvent) && OW_WILD_ENCOUNTERS_UNRESTRICT_SIGHT) return FALSE; @@ -1547,11 +1515,6 @@ static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) return status - 1; } -bool32 OverworldWildEncounter_IsStartingWildEncounter(struct ObjectEvent *objectEvent) -{ - return objectEvent->sOverworldEncounterLevel & OWE_FLAG_START_ENCOUNTER; -} - bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void) { if ((gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !OW_WILD_ENCOUNTERS_BATTLE_PIKE) @@ -1569,12 +1532,17 @@ static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumActiveGeneratedOverworldEncounters() == GetMaxOverworldEncounterSpawns(); } -void OWE_StartEncounterInstant(struct ObjectEvent *mon) +void OWE_StartEncounter(struct ObjectEvent *mon) { gSpecialVar_LastTalked = mon->localId; gSpecialVar_0x8004 = OW_SPECIES(mon); - ScriptContext_SetupScript(InteractWithDynamicWildOverworldEncounter); - FreezeObjectEvents(); + gSelectedObjectEvent = GetObjectEventIdByLocalId(mon->localId); + + // Stop the bobbing animation. + if (mon->movementActionId >= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_DOWN && mon->movementActionId <= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_RIGHT) + ClearObjectEventMovement(mon, &gSprites[mon->spriteId]); + + ScriptContext_SetupScript(OWE_GetScriptPointer()); } bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent) @@ -1652,6 +1620,87 @@ static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction return TRUE; } +const u8 *OWE_GetScriptPointer(void) +{ + if (OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE) + return InteractWithDynamicWildOverworldEncounterApproach; + else + return InteractWithDynamicWildOverworldEncounterInstant; +} + +#define tObjectId data[0] + +void OWE_ApproachForBattle(void) +{ + u32 taskId = CreateTask(Task_OWE_ApproachForBattle, 2); + + gTasks[taskId].tObjectId = GetObjectEventIdByLocalId(gSpecialVar_LastTalked); +} + +static void Task_OWE_ApproachForBattle(u8 taskId) +{ + struct ObjectEvent *OWE = &gObjectEvents[gTasks[taskId].tObjectId]; + + // Let the mon continue to take steps until right next to the player. + if (ObjectEventClearHeldMovementIfFinished(OWE)) + { + if (OWE_IsMonNextToPlayer(OWE)) + { + ScriptContext_Enable(); + DestroyTask(taskId); + return; + } + + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + u16 speciesId = OW_SPECIES(OWE); + enum Direction direction = DetermineObjectEventDirectionFromObject(player, OWE); + u8 movementActionId; + + SetObjectEventDirection(OWE, direction); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(OWE->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + + if (OWE_CheckRestrictedMovement(OWE, OWE->movementDirection)) + { + struct ObjectEvent *followerMon = GetFollowerObject(); + u32 idFollowerNPC = GetFollowerNPCObjectId(); + struct ObjectEvent *followerNPC = &gObjectEvents[idFollowerNPC]; + s16 x = OWE->currentCoords.x; + s16 y = OWE->currentCoords.y; + u32 collidingObject; + + MoveCoords(OWE->movementDirection, &x, &y); + collidingObject = GetObjectObjectCollidesWith(OWE, x, y, FALSE); + + if (collidingObject == GetObjectEventIdByLocalId(followerMon->localId) && followerMon != NULL && !followerMon->invisible) + { + ClearObjectEventMovement(followerMon, &gSprites[followerMon->spriteId]); + gSprites[followerMon->spriteId].animCmdIndex = 0; + ObjectEventSetHeldMovement(followerMon, MOVEMENT_ACTION_ENTER_POKEBALL); + } + else if (collidingObject == idFollowerNPC && FNPC_ENABLE_NPC_FOLLOWERS && PlayerHasFollowerNPC() && !followerNPC->invisible) + { + enum Direction direction = DetermineFollowerNPCDirection(&gObjectEvents[gPlayerAvatar.objectEventId], followerNPC); + ClearObjectEventMovement(followerNPC, &gSprites[followerNPC->spriteId]); + gSprites[followerNPC->spriteId].animCmdIndex = 0; + ObjectEventSetHeldMovement(followerNPC, GetWalkNormalMovementAction(direction)); + CreateTask(Task_HideNPCFollowerAfterMovementFinish, 2); + } + else if (collidingObject == gPlayerAvatar.objectEventId) + { + movementActionId = GetFaceDirectionMovementAction(OWE->facingDirection); + } + else + { + direction = OWE_DirectionToPlayerFromCollision(OWE); + SetObjectEventDirection(OWE, direction); + movementActionId = GetWalkMovementActionInDirectionWithSpeed(OWE->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + } + } + ObjectEventSetHeldMovement(OWE, movementActionId); + } + +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From 1aabec379fff5165d92702f04509100c351d3c09 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 07:50:31 +0000 Subject: [PATCH 420/572] Approach for Encounter Script Shortening --- data/scripts/overworld_encounters.inc | 8 +------ include/overworld_encounters.h | 4 +--- src/field_control_avatar.c | 4 ++-- src/overworld_encounters.c | 30 +++++++++++++-------------- 4 files changed, 18 insertions(+), 28 deletions(-) diff --git a/data/scripts/overworld_encounters.inc b/data/scripts/overworld_encounters.inc index 84e33e0b5c0b..5642af1a3426 100644 --- a/data/scripts/overworld_encounters.inc +++ b/data/scripts/overworld_encounters.inc @@ -1,12 +1,6 @@ -InteractWithDynamicWildOverworldEncounterInstant:: +InteractWithDynamicWildOverworldEncounter:: lockall - goto InteractWithDynamicWildOverworldEncounter - -InteractWithDynamicWildOverworldEncounterApproach:: - lock callnative OWE_ApproachForBattle - waitstate -InteractWithDynamicWildOverworldEncounter: applymovement VAR_LAST_TALKED, Common_Movement_ExclamationMark callnative ScriptFaceLastTalked playmoncry VAR_0x8004, CRY_MODE_DOUBLES diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 56b9d8eeade9..4419ebcac63b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -88,8 +88,7 @@ enum __attribute__((packed)) OverworldEncounterBehaviors OWE_SPECIES_BEHAVIOR_COUNT }; -extern const u8 InteractWithDynamicWildOverworldEncounterInstant[]; -extern const u8 InteractWithDynamicWildOverworldEncounterApproach[]; +extern const u8 InteractWithDynamicWildOverworldEncounter[]; void OWE_ResetSpawnCounterPlayAmbientCry(void); void UpdateOverworldEncounters(void); @@ -124,6 +123,5 @@ u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite); void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent, u32 state); -const u8 *OWE_GetScriptPointer(void); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 676f38f6ba90..066004fdf71d 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -405,7 +405,7 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 else if (PlayerHasFollowerNPC() && objectEventId == GetFollowerNPCObjectId()) script = GetFollowerNPCScriptPointer(); else if (ShouldRunOverworldEncounterScript(objectEventId)) - script = OWE_GetScriptPointer(); + script = InteractWithDynamicWildOverworldEncounter; else script = GetObjectEventScriptPointerByObjectEventId(objectEventId); @@ -572,7 +572,7 @@ static const u8 *GetInteractedWaterScript(struct MapPosition *position, u8 metat if (IsPlayerFacingSurfableFishableWater() == TRUE && ShouldRunOverworldEncounterScript(objectEventId)) { gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; - return OWE_GetScriptPointer(); + return InteractWithDynamicWildOverworldEncounter; } if (IsFieldMoveUnlocked(FIELD_MOVE_SURF) && PartyHasMonWithSurf() == TRUE && IsPlayerFacingSurfableFishableWater() == TRUE diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c379604e733e..c47d1dd03a3c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -160,7 +160,7 @@ void UpdateOverworldEncounters(void) .elevation = MapGridGetElevationAt(x, y), .movementType = OWE_GetMovementTypeFromSpecies(speciesId), .trainerType = TRAINER_TYPE_ENCOUNTER, - .script = OWE_GetScriptPointer(), + .script = InteractWithDynamicWildOverworldEncounter, }; u32 objectEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[objectEventId]; @@ -998,7 +998,7 @@ bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) { struct ObjectEvent *object = &gObjectEvents[objectEventId]; - if (!IsOverworldWildEncounter(object, OWE_ANY) || GetObjectEventScriptPointerByObjectEventId(objectEventId) != OWE_GetScriptPointer()) + if (!IsOverworldWildEncounter(object, OWE_ANY) || GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithDynamicWildOverworldEncounter) return FALSE; gSpecialVar_0x8004 = OW_SPECIES(object); @@ -1068,7 +1068,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); if (templateOWE.script == NULL) - templateOWE.script = OWE_GetScriptPointer(); + templateOWE.script = InteractWithDynamicWildOverworldEncounter; graphicsId = speciesId + OBJ_EVENT_MON; if (isFemale) @@ -1542,7 +1542,7 @@ void OWE_StartEncounter(struct ObjectEvent *mon) if (mon->movementActionId >= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_DOWN && mon->movementActionId <= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_RIGHT) ClearObjectEventMovement(mon, &gSprites[mon->spriteId]); - ScriptContext_SetupScript(OWE_GetScriptPointer()); + ScriptContext_SetupScript(InteractWithDynamicWildOverworldEncounter); } bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent) @@ -1620,21 +1620,18 @@ static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction return TRUE; } -const u8 *OWE_GetScriptPointer(void) -{ - if (OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE) - return InteractWithDynamicWildOverworldEncounterApproach; - else - return InteractWithDynamicWildOverworldEncounterInstant; -} - -#define tObjectId data[0] - +#define tObjectId data[0] void OWE_ApproachForBattle(void) { - u32 taskId = CreateTask(Task_OWE_ApproachForBattle, 2); + if (!OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE) + return; - gTasks[taskId].tObjectId = GetObjectEventIdByLocalId(gSpecialVar_LastTalked); + u32 taskId = CreateTask(Task_OWE_ApproachForBattle, 2); + if (FindTaskIdByFunc(Task_OWE_ApproachForBattle) != TASK_NONE) + { + ScriptContext_Stop(); + gTasks[taskId].tObjectId = GetObjectEventIdByLocalId(gSpecialVar_LastTalked); + } } static void Task_OWE_ApproachForBattle(u8 taskId) @@ -1700,6 +1697,7 @@ static void Task_OWE_ApproachForBattle(u8 taskId) } } +#undef tObjectId #undef sOverworldEncounterLevel #undef sAge From 69e7f80b2c67ee3010dc47d0aa27be6a4e49b0e3 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 08:06:16 +0000 Subject: [PATCH 421/572] Move GetWalkMovementActionInDirectionWithSpeed --- include/event_object_movement.h | 1 - include/overworld_encounters.h | 1 + src/event_object_movement.c | 33 +++++++++------------------------ src/overworld_encounters.c | 19 +++++++++++++++++-- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index bd4357c85610..5a39d49fa261 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -556,7 +556,6 @@ bool8 PlayerIsUnderWaterfall(struct ObjectEvent *objectEvent); u8 GetObjectEventApricornTreeId(u8 objectEventId); bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); -u8 GetWalkMovementActionInDirectionWithSpeed(u32 direction, u32 speed); extern const enum Direction gStandardDirections[]; diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 4419ebcac63b..daee87399b9a 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -123,5 +123,6 @@ u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite); void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent, u32 state); +u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 1c152b363f48..691cc97c6364 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11713,21 +11713,6 @@ static u32 TurnDirectionNinetyDegrees(u32 direction, bool32 counterclockwise) return DIR_NONE; } -u8 GetWalkMovementActionInDirectionWithSpeed(u32 direction, u32 speed) -{ - switch (speed) - { - case OWE_SPEED_SLOW: - return GetWalkSlowMovementAction(direction); - case OWE_SPEED_FAST: - return GetWalkFastMovementAction(direction); - case OWE_SPEED_FASTER: - return GetWalkFasterMovementAction(direction); - } - - return GetWalkNormalMovementAction(direction); -} - bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent) { gFieldEffectArguments[0] = objEvent->currentCoords.x; @@ -11789,7 +11774,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent bool8 MovementType_WanderAround_OverworldWildEncounter_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetIdleSpeedFromSpecies(OW_SPECIES(objectEvent)))); + ObjectEventSetSingleMovement(objectEvent, sprite, OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetIdleSpeedFromSpecies(OW_SPECIES(objectEvent)))); objectEvent->singleMovementActive = TRUE; sprite->sTypeFuncId = 6; return TRUE; @@ -11848,7 +11833,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent u16 speciesId = OW_SPECIES(objectEvent); u8 movementActionId; - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); sprite->sTypeFuncId = 12; if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) @@ -11864,7 +11849,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent return FALSE; } u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); @@ -11928,7 +11913,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * u16 speciesId = OW_SPECIES(objectEvent); u8 movementActionId; - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) { @@ -11937,7 +11922,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * if (newDirection != objectEvent->movementDirection) newDirection = GetOppositeDirection(newDirection); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) { @@ -12044,7 +12029,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve if (distance <= 1) { SetObjectEventDirection(objectEvent, GetOppositeDirection(objectEvent->movementDirection)); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; @@ -12052,7 +12037,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve if (objectEvent->currentCoords.x != player->currentCoords.x && objectEvent->currentCoords.y != player->currentCoords.y) newDirection = GetOppositeDirection(newDirection); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); } @@ -12073,7 +12058,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve } else { - movementActionId = GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) { @@ -12088,7 +12073,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve return FALSE; } u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c47d1dd03a3c..66f8d7aaf07b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1654,7 +1654,7 @@ static void Task_OWE_ApproachForBattle(u8 taskId) u8 movementActionId; SetObjectEventDirection(OWE, direction); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(OWE->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(OWE->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(OWE, OWE->movementDirection)) { @@ -1690,7 +1690,7 @@ static void Task_OWE_ApproachForBattle(u8 taskId) { direction = OWE_DirectionToPlayerFromCollision(OWE); SetObjectEventDirection(OWE, direction); - movementActionId = GetWalkMovementActionInDirectionWithSpeed(OWE->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(OWE->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); } } ObjectEventSetHeldMovement(OWE, movementActionId); @@ -1699,6 +1699,21 @@ static void Task_OWE_ApproachForBattle(u8 taskId) } #undef tObjectId +u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed) +{ + switch (speed) + { + case OWE_SPEED_SLOW: + return GetWalkSlowMovementAction(direction); + case OWE_SPEED_FAST: + return GetWalkFastMovementAction(direction); + case OWE_SPEED_FASTER: + return GetWalkFasterMovementAction(direction); + } + + return GetWalkNormalMovementAction(direction); +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From bebab7084ead7edc8195cf23ab3af849ffed10fe Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 08:08:53 +0000 Subject: [PATCH 422/572] More Direction Enum Conversion --- include/overworld_encounters.h | 2 +- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index daee87399b9a..70243ca81983 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -108,7 +108,7 @@ bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); void OverworldWildEncounter_RemoveObjectOnBattle(void); -bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction); +bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Direction direction); void DespawnOldestOWE_Pal(void); bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon); bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 691cc97c6364..788bd3fee536 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11684,7 +11684,7 @@ u8 GetObjectEventApricornTreeId(u8 objectEventId) return gObjectEvents[objectEventId].trainerRange_berryTreeId; } -static u32 TurnDirectionNinetyDegrees(u32 direction, bool32 counterclockwise) +static u32 TurnDirectionNinetyDegrees(enum Direction direction, bool32 counterclockwise) { switch (direction) { diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 66f8d7aaf07b..428fc31aca99 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1120,7 +1120,7 @@ void OverworldWildEncounter_RemoveObjectOnBattle(void) } // Returns TRUE if movement is restricted. -bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, u32 direction) +bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Direction direction) { if (GetCollisionInDirection(objectEvent, direction)) return TRUE; From c7250600d7d5d4d1b5a114f94a18f90ed1c967af Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 08:24:21 +0000 Subject: [PATCH 423/572] TurnDirectionNinetyDegrees to GetNinetyDegreeDirection --- include/event_object_movement.h | 1 + src/event_object_movement.c | 53 ++++++++++++++------------------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 5a39d49fa261..6ed3fa5ea4e2 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -186,6 +186,7 @@ void InitObjectEventPalettes(u8 reflectionType); void UpdateObjectEventCurrentMovement(struct ObjectEvent *objectEvent, struct Sprite *sprite, bool8 (*callback)(struct ObjectEvent *, struct Sprite *)); bool8 ObjectEventFaceOppositeDirection(struct ObjectEvent *objectEvent, enum Direction direction); enum Direction GetOppositeDirection(enum Direction direction); +enum Direction GetNinetyDegreeDirection(enum Direction direction, bool32 clockwise); u8 GetWalkInPlaceFasterMovementAction(u32); u8 GetWalkInPlaceFastMovementAction(u32); u8 GetWalkInPlaceNormalMovementAction(u32); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 788bd3fee536..398b82769a74 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1260,6 +1260,19 @@ static const u8 sOppositeDirections[] = { DIR_SOUTHEAST, DIR_SOUTHWEST, }; +// Should this and above be enum Direction? +static const u8 sRotate90Direction[][2] = +{ + [DIR_NONE] = { DIR_NONE, DIR_NONE }, + [DIR_SOUTH] = { DIR_EAST, DIR_WEST }, + [DIR_NORTH] = { DIR_WEST, DIR_EAST }, + [DIR_WEST] = { DIR_SOUTH, DIR_NORTH }, + [DIR_EAST] = { DIR_NORTH, DIR_SOUTH }, + [DIR_SOUTHWEST] = { DIR_SOUTHEAST, DIR_NORTHWEST }, + [DIR_SOUTHEAST] = { DIR_NORTHEAST, DIR_SOUTHWEST }, + [DIR_NORTHWEST] = { DIR_SOUTHWEST, DIR_NORTHEAST }, + [DIR_NORTHEAST] = { DIR_NORTHWEST, DIR_SOUTHEAST }, +}; // Takes the player's original and current facing direction to get the direction that should be considered to copy. // Note that this means an NPC who copies the player's movement changes how they copy them based on how @@ -6802,6 +6815,15 @@ enum Direction GetOppositeDirection(enum Direction direction) return directions[direction - 1]; } +enum Direction GetNinetyDegreeDirection(enum Direction direction, bool32 clockwise) +{ + if (direction <= DIR_NONE || direction >= NELEMS(sRotate90Direction)) + return DIR_NONE; + + return sRotate90Direction[direction][clockwise]; +} + + // Takes the player's original and current direction and gives a direction the copy NPC should consider as the player's direction. // See comments at the table's definition. static u32 GetPlayerDirectionForCopy(u8 initDir, u8 moveDir) @@ -11684,35 +11706,6 @@ u8 GetObjectEventApricornTreeId(u8 objectEventId) return gObjectEvents[objectEventId].trainerRange_berryTreeId; } -static u32 TurnDirectionNinetyDegrees(enum Direction direction, bool32 counterclockwise) -{ - switch (direction) - { - case DIR_NORTH: - if (counterclockwise) - return DIR_WEST; - else - return DIR_EAST; - case DIR_SOUTH: - if (counterclockwise) - return DIR_EAST; - else - return DIR_WEST; - case DIR_EAST: - if (counterclockwise) - return DIR_NORTH; - else - return DIR_SOUTH; - case DIR_WEST: - if (counterclockwise) - return DIR_SOUTH; - else - return DIR_NORTH; - } - - return DIR_NONE; -} - bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent) { gFieldEffectArguments[0] = objEvent->currentCoords.x; @@ -11762,7 +11755,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent u8 chosenDirection = objectEvent->movementDirection; if ((Random() & 3) != 0) - chosenDirection = TurnDirectionNinetyDegrees(chosenDirection, Random() & 2); + chosenDirection = GetNinetyDegreeDirection(chosenDirection, Random() % 2); SetObjectEventDirection(objectEvent, chosenDirection); sprite->sTypeFuncId = 5; From 7bb0f5d2ac59b81b1fe8e74d9a77735da22206b1 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 09:40:05 +0000 Subject: [PATCH 424/572] Remove Unneeded DexNav Additions --- src/dexnav.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/dexnav.c b/src/dexnav.c index 27a75c260af2..e2c18fa49b0e 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -834,7 +834,6 @@ static void DexNavSearchBail(const u8 *script) { TRY_FREE_AND_SET_NULL(sDexNavSearchDataPtr); FlagClear(DN_FLAG_SEARCHING); - UnfreezeObjectEvents(); FreeMonIconPalettes(); ScriptContext_SetupScript(script); } @@ -976,7 +975,6 @@ void EndDexNavSearch(void) FieldEffectStop(&gSprites[sDexNavSearchDataPtr->fldEffSpriteId], sDexNavSearchDataPtr->fldEffId); FREE_AND_SET_NULL(sDexNavSearchDataPtr); FlagClear(DN_FLAG_SEARCHING); - UnfreezeObjectEvents(); } static void EndDexNavSearchSetupScript(const u8 *script) @@ -1107,7 +1105,6 @@ bool32 OnStep_DexNavSearch(void) ScriptContext_SetupScript(EventScript_StartDexNavBattle); FREE_AND_SET_NULL(sDexNavSearchDataPtr); FlagClear(DN_FLAG_SEARCHING); - UnfreezeObjectEvents(); return TRUE; } @@ -2577,7 +2574,6 @@ bool32 TryFindHiddenPokemon(void) { FREE_AND_SET_NULL(sDexNavSearchDataPtr); FlagClear(DN_FLAG_SEARCHING); - UnfreezeObjectEvents(); return FALSE; } @@ -2586,7 +2582,6 @@ bool32 TryFindHiddenPokemon(void) { FREE_AND_SET_NULL(sDexNavSearchDataPtr); FlagClear(DN_FLAG_SEARCHING); - UnfreezeObjectEvents(); return FALSE; } From 46eb590a0c8346d621da2eba03973ed5d32e12c5 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 10:30:13 +0000 Subject: [PATCH 425/572] Remove GetBattlePyramidWildMonHeaderIdFromSpecies --- include/battle_pyramid.h | 1 - src/battle_pyramid.c | 29 ----------------------------- src/overworld_encounters.c | 12 +++--------- 3 files changed, 3 insertions(+), 39 deletions(-) diff --git a/include/battle_pyramid.h b/include/battle_pyramid.h index c81027e045e5..d55d8d2e9399 100644 --- a/include/battle_pyramid.h +++ b/include/battle_pyramid.h @@ -22,6 +22,5 @@ void LoadBattlePyramidObjectEventTemplates(void); void LoadBattlePyramidFloorObjectEventScripts(void); u8 GetNumBattlePyramidObjectEvents(void); u16 GetBattlePyramidPickupItemId(void); -u32 GetBattlePyramidWildMonHeaderIdFromSpecies(u32 species, u32 round, enum FrontierLevelMode levelMode); #endif // GUARD_BATTLE_PYRAMID_H diff --git a/src/battle_pyramid.c b/src/battle_pyramid.c index 9b9c76f823b0..f777be99cf35 100644 --- a/src/battle_pyramid.c +++ b/src/battle_pyramid.c @@ -2206,32 +2206,3 @@ u16 GetBattlePyramidPickupItemId(void) else return sPickupItemsLvl50[round][i]; } - -u32 GetBattlePyramidWildMonHeaderIdFromSpecies(u32 species, u32 round, enum FrontierLevelMode levelMode) -{ - // This check may return an incorrect index if duplicate species exist in the same round's table. - // This does no occur in vanilla Emerald. - const struct PyramidWildMon *wildMons; - u32 i; - - if (round >= TOTAL_PYRAMID_ROUNDS) - return 0; - - if (levelMode != FRONTIER_LVL_50) - wildMons = sOpenLevelWildMonPointers[round]; - else - wildMons = sLevel50WildMonPointers[round]; - - for (i = 0; ; i++) - { - u32 entrySpecies = SanitizeSpeciesId(wildMons[i].species); - // Will trigger an assert and failure if species in the table before desired species is disabled. - - if (entrySpecies == SPECIES_NONE) - return 0; - - if (entrySpecies == species) - return i + 1; - } -} - diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 428fc31aca99..1ff4cc7d8ab9 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -526,15 +526,7 @@ static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId) } if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) { - enum FrontierLevelMode levelMode = gSaveBlock2Ptr->frontier.lvlMode; - u32 species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); - u32 round = (gSaveBlock2Ptr->frontier.pyramidWinStreaks[levelMode] / FRONTIER_STAGES_PER_CHALLENGE) % TOTAL_PYRAMID_ROUNDS; - u32 id = GetBattlePyramidWildMonHeaderIdFromSpecies(species, round, levelMode); - assertf(id, "could not find species in battle pyramid wild mon data. defaulting to regular encounter.\nspecies: %d\nround: %d\nlevel mode: %d", species, round, levelMode); - { - return FALSE; - } - + u32 id = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); SetMonData(&gEnemyParty[0], MON_DATA_SPECIES, &id); GenerateBattlePyramidWildMon(); BattleSetup_StartWildBattle(); @@ -749,7 +741,9 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) return FALSE; + u32 id = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); GenerateBattlePyramidWildMon(); + SetMonData(&gEnemyParty[0], MON_DATA_LEVEL, &id); return TRUE; } From f71f3444311793743b60bb8c8124578b564bde47 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 10:45:40 +0000 Subject: [PATCH 426/572] Add assertf --- src/overworld_encounters.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 1ff4cc7d8ab9..dfca9be33a88 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -78,6 +78,7 @@ static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 xNew, s16 yNew, enum Direction newDirection, enum Direction collisionDirection); static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); static void Task_OWE_ApproachForBattle(u8 taskId); +static bool32 OWE_CheckSpecies(u32 speciesId); void OWE_ResetSpawnCounterPlayAmbientCry(void) { @@ -669,6 +670,7 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) { SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level, indexRoamerOutbreak); + assertf(OWE_CheckSpecies(*speciesId), "invalid generated overworld encounter\nspecies: %d\ncheck if valid wild mon header exists", speciesId, x, y); u16 graphicsId = *speciesId + OBJ_EVENT_MON; if (*isFemale) @@ -691,7 +693,6 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 if (!OWE_CreateEnemyPartyMon(speciesId, level, indexRoamerOutbreak, x, y)) { - // May be good to convert this to an assertf ZeroEnemyPartyMons(); *speciesId = SPECIES_NONE; return; @@ -1030,7 +1031,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( if (speciesTemplate) speciesId = speciesTemplate; - assertf(speciesId != SPECIES_NONE && speciesId < NUM_SPECIES && IsSpeciesEnabled(speciesId), "invalid manual overworld encounter\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, x, y) + assertf(OWE_CheckSpecies(speciesId), "invalid manual overworld encounter\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, x, y) { // Currently causes assertf on each player step as function is called. templateOWE.graphicsId = OBJ_EVENT_GFX_BOY_1; @@ -1708,6 +1709,13 @@ u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 return GetWalkNormalMovementAction(direction); } +static bool32 OWE_CheckSpecies(u32 speciesId) +{ + return speciesId != SPECIES_NONE + && speciesId < NUM_SPECIES + && IsSpeciesEnabled(speciesId); +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From 918efdb40543e35870ef28ca578ca28a47f9b203 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 10:53:23 +0000 Subject: [PATCH 427/572] Use IsOverworldWildEncounter in TryGetObjectEventTemplateForOverworldEncounter --- src/overworld_encounters.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index dfca9be33a88..3bc87160144f 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1002,8 +1002,7 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) { - if (template->trainerType != TRAINER_TYPE_ENCOUNTER || (template->localId <= LOCALID_OW_ENCOUNTER_END - && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS))) + if (!IsOverworldWildEncounter((struct ObjectEvent *)template, OWE_MANUAL)) return *template; struct ObjectEventTemplate templateOWE = *template; From 3e0dcfaecd0970e9346147bc45a6a32edca1b11e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 11:03:59 +0000 Subject: [PATCH 428/572] Consolidate GetNumActiveOverworldEncounters --- src/overworld_encounters.c | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 3bc87160144f..83f881edfa37 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -60,8 +60,7 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_DoesRoamerObjectExist(void); static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); -static u32 GetNumActiveOverworldEncounters(void); -static u32 GetNumActiveGeneratedOverworldEncounters(void); +static u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); @@ -84,7 +83,7 @@ void OWE_ResetSpawnCounterPlayAmbientCry(void) { OverworldWildEncounter_SetMinimumSpawnTimer(); // Currently may not play manual encounter cries if no wild mon header exists - if (OW_WILD_ENCOUNTERS_AMBIENT_CRIES && GetNumActiveOverworldEncounters()) + if (OW_WILD_ENCOUNTERS_AMBIENT_CRIES && GetNumberActiveOverworldEncounters(OWE_ANY)) OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); } @@ -114,7 +113,7 @@ void UpdateOverworldEncounters(void) u16 spawnSlot = NextSpawnMonSlot(); if (LURE_STEP_COUNT && spawnSlot != INVALID_SPAWN_SLOT - && (GetNumActiveGeneratedOverworldEncounters() < GetMaxOverworldEncounterSpawns())) + && (GetNumberActiveOverworldEncounters(OWE_GENERATED) < GetMaxOverworldEncounterSpawns())) { OverworldWildEncounter_SetMinimumSpawnTimer(); } @@ -188,7 +187,7 @@ void UpdateOverworldEncounters(void) static void OWE_SetNewSpawnCountdown(void) { - u32 numActive = GetNumActiveGeneratedOverworldEncounters(); + u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && numActive >= GetMaxOverworldEncounterSpawns()) sOWESpawnCountdown = OWE_SPAWN_TIME_REPLACEMENT; @@ -327,7 +326,7 @@ static u8 NextSpawnMonSlot(void) u32 maxSpawns = GetMaxOverworldEncounterSpawns(); // All mon slots are in use - if (GetNumActiveGeneratedOverworldEncounters() >= maxSpawns) + if (GetNumberActiveOverworldEncounters(OWE_GENERATED) >= maxSpawns) { if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) { @@ -601,7 +600,7 @@ static void SortOWEMonAges(void) struct ObjectEvent *slotMon; struct AgeSort array[OWE_MAX_SPAWN_SLOTS]; struct AgeSort current; - u32 numActive = GetNumActiveGeneratedOverworldEncounters(); + u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); u32 count = 0; s32 i, j; @@ -810,29 +809,17 @@ static bool8 IsSafeToSpawnObjectEvents(void) return (player->currentCoords.x == player->previousCoords.x && player->currentCoords.y == player->previousCoords.y); } -static u32 GetNumActiveOverworldEncounters(void) +static u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType) { u32 numActive = 0; for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { - if (IsOverworldWildEncounter(&gObjectEvents[i], OWE_ANY)) + if (IsOverworldWildEncounter(&gObjectEvents[i], oweType)) numActive++; } return numActive; } -static u32 GetNumActiveGeneratedOverworldEncounters(void) -{ - u32 count = 0; - - for (u32 spawnSlot = 0; spawnSlot < OWE_MAX_SPAWN_SLOTS; spawnSlot++) - { - if (GetOverworldSpeciesBySpawnSlot(spawnSlot) != SPECIES_NONE) - count++; - } - return count; -} - static bool32 OWE_ShouldSpawnWaterMons(void) { // Needs refactoring, and this replacing with a check for coords in many cases. @@ -955,7 +942,7 @@ u32 GetNewestOWEncounterLocalId(void) bool32 CanRemoveOverworldEncounter(u32 localId) { // Include a check for the encounter not being shiny or a roamer. - return (OW_WILD_ENCOUNTERS_OVERWORLD && GetNumActiveGeneratedOverworldEncounters() != 0 + return (OW_WILD_ENCOUNTERS_OVERWORLD && GetNumberActiveOverworldEncounters(OWE_GENERATED) != 0 && (localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS + 1) || localId > LOCALID_OW_ENCOUNTER_END)); } @@ -1141,7 +1128,7 @@ void DespawnOldestOWE_Pal(void) // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles if (OW_WILD_ENCOUNTERS_OVERWORLD && CountFreePaletteSlots() < 2) { - u32 count = GetNumActiveGeneratedOverworldEncounters(); + u32 count = GetNumberActiveOverworldEncounters(OWE_GENERATED); if (count > 0) { @@ -1365,7 +1352,7 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) { // Uses slots so needs to be generated encounters only. // Or needs to change functionality to count all active encounters. - u32 numActive = GetNumActiveOverworldEncounters(); + u32 numActive = GetNumberActiveOverworldEncounters(OWE_ANY); u32 randomIndex; struct ObjectEvent *slotMon; @@ -1523,7 +1510,7 @@ static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) if (!IsOverworldWildEncounter(object, OWE_GENERATED)) return FALSE; - return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumActiveGeneratedOverworldEncounters() == GetMaxOverworldEncounterSpawns(); + return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) == GetMaxOverworldEncounterSpawns(); } void OWE_StartEncounter(struct ObjectEvent *mon) From e9b4d6192a2c0ad54d836899e779722aedfbb3c7 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 11:11:41 +0000 Subject: [PATCH 429/572] Specify GetMaxOverworldEncounterSpawns --- src/overworld_encounters.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 83f881edfa37..32e2901b53fc 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -67,7 +67,7 @@ static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); static bool32 CreateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); -static u32 GetMaxOverworldEncounterSpawns(void); +static u32 GetMaxGeneratedOverworldEncounterSpawns(void); static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); @@ -113,7 +113,7 @@ void UpdateOverworldEncounters(void) u16 spawnSlot = NextSpawnMonSlot(); if (LURE_STEP_COUNT && spawnSlot != INVALID_SPAWN_SLOT - && (GetNumberActiveOverworldEncounters(OWE_GENERATED) < GetMaxOverworldEncounterSpawns())) + && (GetNumberActiveOverworldEncounters(OWE_GENERATED) < GetMaxGeneratedOverworldEncounterSpawns())) { OverworldWildEncounter_SetMinimumSpawnTimer(); } @@ -189,7 +189,7 @@ static void OWE_SetNewSpawnCountdown(void) { u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); - if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && numActive >= GetMaxOverworldEncounterSpawns()) + if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && numActive >= GetMaxGeneratedOverworldEncounterSpawns()) sOWESpawnCountdown = OWE_SPAWN_TIME_REPLACEMENT; else sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); @@ -278,8 +278,10 @@ static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animS MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); } -static u32 GetMaxOverworldEncounterSpawns(void) +static u32 GetMaxGeneratedOverworldEncounterSpawns(void) { + // Should this just be a flat number? + // How does it effect things if going from water to land etc or vice versa if (OWE_ShouldSpawnWaterMons()) return OWE_MAX_WATER_SPAWNS; else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) @@ -323,7 +325,7 @@ u32 GetOldestSlot(void) static u8 NextSpawnMonSlot(void) { u32 spawnSlot; - u32 maxSpawns = GetMaxOverworldEncounterSpawns(); + u32 maxSpawns = GetMaxGeneratedOverworldEncounterSpawns(); // All mon slots are in use if (GetNumberActiveOverworldEncounters(OWE_GENERATED) >= maxSpawns) @@ -1510,7 +1512,7 @@ static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) if (!IsOverworldWildEncounter(object, OWE_GENERATED)) return FALSE; - return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) == GetMaxOverworldEncounterSpawns(); + return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) == GetMaxGeneratedOverworldEncounterSpawns(); } void OWE_StartEncounter(struct ObjectEvent *mon) From 81cbe6ba39d75384e1b5835bc7707f766e7842a2 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 11:32:14 +0000 Subject: [PATCH 430/572] Don't allow manual owes in the Battle Pyramid --- src/overworld_encounters.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 32e2901b53fc..88c7284be23a 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1016,7 +1016,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( &indexRoamerOutbreak ); - if (speciesTemplate) + if (speciesTemplate && gMapHeader.mapLayoutId != LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) speciesId = speciesTemplate; assertf(OWE_CheckSpecies(speciesId), "invalid manual overworld encounter\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, x, y) @@ -1032,19 +1032,22 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( if (isShinyTemplate) isShiny = isShinyTemplate; - if (templateOWE.graphicsId & OBJ_EVENT_MON && templateOWE.graphicsId & OBJ_EVENT_MON_FEMALE) - isFemale = TRUE; - else if (templateOWE.graphicsId & OBJ_EVENT_MON) - isFemale = FALSE; - else - isFemale = GetGenderFromSpeciesAndPersonality(speciesId, Random32()) == MON_FEMALE; + if (gMapHeader.mapLayoutId != LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + { + if (templateOWE.graphicsId & OBJ_EVENT_MON && templateOWE.graphicsId & OBJ_EVENT_MON_FEMALE) + isFemale = TRUE; + else if (templateOWE.graphicsId & OBJ_EVENT_MON) + isFemale = FALSE; + else + isFemale = GetGenderFromSpeciesAndPersonality(speciesId, Random32()) == MON_FEMALE; - if (levelTemplate) - level = levelTemplate; + if (levelTemplate) + level = levelTemplate; - assertf(level >= MIN_LEVEL && level <= MAX_LEVEL, "invalid manual overworld encounter\nlevel: %d\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", level, speciesId, x, y) - { - level = MIN_LEVEL; + assertf(level >= MIN_LEVEL && level <= MAX_LEVEL, "invalid manual overworld encounter\nlevel: %d\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", level, speciesId, x, y) + { + level = MIN_LEVEL; + } } if (templateOWE.movementType == MOVEMENT_TYPE_NONE) From 0f7a1f9b30024d0b5adfbdc146feaa190da060bd Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 11:32:26 +0000 Subject: [PATCH 431/572] Revert "Don't allow manual owes in the Battle Pyramid" as should be no objects This reverts commit 3eccadfb1bb7dc43804090bf7d20ccc88fc59eb4. --- src/overworld_encounters.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 88c7284be23a..32e2901b53fc 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1016,7 +1016,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( &indexRoamerOutbreak ); - if (speciesTemplate && gMapHeader.mapLayoutId != LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + if (speciesTemplate) speciesId = speciesTemplate; assertf(OWE_CheckSpecies(speciesId), "invalid manual overworld encounter\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, x, y) @@ -1032,22 +1032,19 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( if (isShinyTemplate) isShiny = isShinyTemplate; - if (gMapHeader.mapLayoutId != LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) - { - if (templateOWE.graphicsId & OBJ_EVENT_MON && templateOWE.graphicsId & OBJ_EVENT_MON_FEMALE) - isFemale = TRUE; - else if (templateOWE.graphicsId & OBJ_EVENT_MON) - isFemale = FALSE; - else - isFemale = GetGenderFromSpeciesAndPersonality(speciesId, Random32()) == MON_FEMALE; + if (templateOWE.graphicsId & OBJ_EVENT_MON && templateOWE.graphicsId & OBJ_EVENT_MON_FEMALE) + isFemale = TRUE; + else if (templateOWE.graphicsId & OBJ_EVENT_MON) + isFemale = FALSE; + else + isFemale = GetGenderFromSpeciesAndPersonality(speciesId, Random32()) == MON_FEMALE; - if (levelTemplate) - level = levelTemplate; + if (levelTemplate) + level = levelTemplate; - assertf(level >= MIN_LEVEL && level <= MAX_LEVEL, "invalid manual overworld encounter\nlevel: %d\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", level, speciesId, x, y) - { - level = MIN_LEVEL; - } + assertf(level >= MIN_LEVEL && level <= MAX_LEVEL, "invalid manual overworld encounter\nlevel: %d\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", level, speciesId, x, y) + { + level = MIN_LEVEL; } if (templateOWE.movementType == MOVEMENT_TYPE_NONE) From 15dd5bc68856a1961acdda50ca0cce87ad47530c Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 11:44:15 +0000 Subject: [PATCH 432/572] Seperate OWE_CanEncounterBeLoaded Functions --- src/overworld_encounters.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 32e2901b53fc..8587780bdb61 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -55,6 +55,8 @@ static u32 GetSpawnSlotByLocalId(u32 localId); static void SortOWEMonAges(void); static void OWE_SetNewSpawnCountdown(void); static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); +static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); +static bool32 OWE_CanEncounterBeLoaded_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_DoesRoamerObjectExist(void); @@ -195,8 +197,18 @@ static void OWE_SetNewSpawnCountdown(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); } -#define OWE_FIELD_EFFECT_TILE_NUM 16 // Number of tiiles to add for field effect spawning static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) +{ + if (!OWE_CanEncounterBeLoaded_Palette(speciesId, isFemale, isShiny, x, y)) + return FALSE; + + if (!OWE_CanEncounterBeLoaded_Tiles(speciesId, isFemale, isShiny, x, y)) + return FALSE; + + return TRUE; +} + +static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) { u32 numFreePalSlots = CountFreePaletteSlots(); u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); @@ -222,6 +234,12 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is return FALSE; } + return TRUE; +} +#define OWE_FIELD_EFFECT_TILE_NUM 16 // Number of tiiles to add for field effect spawning +static bool32 OWE_CanEncounterBeLoaded_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) +{ + u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); // const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(speciesId, isShiny, isFemale); const struct ObjectEventGraphicsInfo *graphicsInfo = GetObjectEventGraphicsInfo(tag); tag = LoadSheetGraphicsInfo(graphicsInfo, tag, NULL); @@ -250,7 +268,7 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is FreeSpriteTilesByTag(tag); return TRUE; } - +#undef OWE_FIELD_EFFECT_TILE_NUM static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn) { if (gMain.callback2 != CB2_Overworld) From 292e05a32887e025bf97f581932517bf93c4b6fb Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 12:00:17 +0000 Subject: [PATCH 433/572] Add OWE_CanEncounterBeLoaded assertfs --- src/overworld_encounters.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8587780bdb61..bb915aabf179 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -199,11 +199,15 @@ static void OWE_SetNewSpawnCountdown(void) static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) { - if (!OWE_CanEncounterBeLoaded_Palette(speciesId, isFemale, isShiny, x, y)) + assertf(OWE_CanEncounterBeLoaded_Palette(speciesId, isFemale, isShiny, x, y), "could not load palette for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) + { return FALSE; + } - if (!OWE_CanEncounterBeLoaded_Tiles(speciesId, isFemale, isShiny, x, y)) + assertf(OWE_CanEncounterBeLoaded_Tiles(speciesId, isFemale, isShiny, x, y), "could not load palette for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) + { return FALSE; + } return TRUE; } From b61ca64984658a2b19bfb99d3a3547aac79b6c9c Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 12:16:01 +0000 Subject: [PATCH 434/572] Add assertf when object limit reached --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index bb915aabf179..da517d157644 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -170,7 +170,7 @@ void UpdateOverworldEncounters(void) RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); - if (objectEventId >= OBJECT_EVENTS_COUNT) + assertf(objectEventId < OBJECT_EVENTS_COUNT, "could not spawn generated overworld encounter. too many object events exist") { OWE_ResetSpawnCounterPlayAmbientCry(); return; From f7b8d29cb81fe2b00e6fa791cbb064eff716e56f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 12:47:45 +0000 Subject: [PATCH 435/572] Move and Rename Remove Oldest OWE functions --- include/overworld_encounters.h | 4 +- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 68 +++++++++++++++++----------------- src/sprite.c | 2 +- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 70243ca81983..c0b6a913757b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -104,12 +104,12 @@ u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); u32 RemoveOldestOverworldEncounter(void); -bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId); +bool32 OWE_TryAndRemoveOldestOverworldEncounter_Object(u32 localId, u8 *objectEventId); +void OWE_TryAndRemoveOldestOverworldEncounter_Palette(void); const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); void OverworldWildEncounter_RemoveObjectOnBattle(void); bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Direction direction); -void DespawnOldestOWE_Pal(void); bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon); bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon); u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 398b82769a74..bdfc77a41153 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1539,7 +1539,7 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * return TRUE; } if (i >= OBJECT_EVENTS_COUNT) - return TryAndRemoveOldestOverworldEncounter(localId, objectEventId); + return OWE_TryAndRemoveOldestOverworldEncounter_Object(localId, objectEventId); *objectEventId = i; for (; i < OBJECT_EVENTS_COUNT; i++) { diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index da517d157644..7ab794f359ba 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -197,6 +197,40 @@ static void OWE_SetNewSpawnCountdown(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); } +bool32 OWE_TryAndRemoveOldestOverworldEncounter_Object(u32 localId, u8 *objectEventId) +{ + if (CanRemoveOverworldEncounter(localId)) + { + *objectEventId = RemoveOldestOverworldEncounter(); + + if (*objectEventId == OBJECT_EVENTS_COUNT) + return TRUE; + else + return FALSE; + } + + return TRUE; +} + +void OWE_TryAndRemoveOldestOverworldEncounter_Palette(void) +{ + // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles + if (OW_WILD_ENCOUNTERS_OVERWORLD && CountFreePaletteSlots() < 2) + { + u32 count = GetNumberActiveOverworldEncounters(OWE_GENERATED); + + if (count > 0) + { + for (; count > 0; count--) + { + RemoveOldestOverworldEncounter(); + if (CountFreePaletteSlots() >= 2) + break; + } + } + } +} + static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) { assertf(OWE_CanEncounterBeLoaded_Palette(speciesId, isFemale, isShiny, x, y), "could not load palette for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) @@ -986,21 +1020,6 @@ u32 RemoveOldestOverworldEncounter(void) return objectEventId; } -bool32 TryAndRemoveOldestOverworldEncounter(u32 localId, u8 *objectEventId) -{ - if (CanRemoveOverworldEncounter(localId)) - { - *objectEventId = RemoveOldestOverworldEncounter(); - - if (*objectEventId == OBJECT_EVENTS_COUNT) - return TRUE; - else - return FALSE; - } - - return TRUE; -} - bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) { struct ObjectEvent *object = &gObjectEvents[objectEventId]; @@ -1147,25 +1166,6 @@ bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Directi return FALSE; } -void DespawnOldestOWE_Pal(void) -{ - // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles - if (OW_WILD_ENCOUNTERS_OVERWORLD && CountFreePaletteSlots() < 2) - { - u32 count = GetNumberActiveOverworldEncounters(OWE_GENERATED); - - if (count > 0) - { - for (; count > 0; count--) - { - RemoveOldestOverworldEncounter(); - if (CountFreePaletteSlots() >= 2) - break; - } - } - } -} - bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon) { if (!IsOverworldWildEncounter(mon, OWE_ANY) || mon->movementType == MOVEMENT_TYPE_WANDER_AROUND_OWE) diff --git a/src/sprite.c b/src/sprite.c index 0ecc32b9f417..33986be3725e 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -1630,7 +1630,7 @@ u32 LoadSpritePalette(const struct SpritePalette *palette) if (index == 0xFF) { - DespawnOldestOWE_Pal(); + OWE_TryAndRemoveOldestOverworldEncounter_Palette(); index = IndexOfSpritePaletteTag(TAG_NONE); if (index == 0xFF) From 42efb478b0126ddb3dac59296cd993d474043812 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 12:49:28 +0000 Subject: [PATCH 436/572] Rename RemoveOldestOverworldEncounter to RemoveOldestGeneratedOverworldEncounter --- include/overworld_encounters.h | 6 +++--- src/event_object_movement.c | 2 +- src/overworld_encounters.c | 10 +++++----- src/sprite.c | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index c0b6a913757b..32eb890e9973 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -103,9 +103,9 @@ bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldO u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); bool32 CanRemoveOverworldEncounter(u32 localId); -u32 RemoveOldestOverworldEncounter(void); -bool32 OWE_TryAndRemoveOldestOverworldEncounter_Object(u32 localId, u8 *objectEventId); -void OWE_TryAndRemoveOldestOverworldEncounter_Palette(void); +u32 RemoveOldestGeneratedOverworldEncounter(void); +bool32 OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId); +void OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Palette(void); const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); void OverworldWildEncounter_RemoveObjectOnBattle(void); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index bdfc77a41153..6ab7ef4f7a75 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1539,7 +1539,7 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * return TRUE; } if (i >= OBJECT_EVENTS_COUNT) - return OWE_TryAndRemoveOldestOverworldEncounter_Object(localId, objectEventId); + return OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Object(localId, objectEventId); *objectEventId = i; for (; i < OBJECT_EVENTS_COUNT; i++) { diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7ab794f359ba..116a5c216838 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -197,11 +197,11 @@ static void OWE_SetNewSpawnCountdown(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); } -bool32 OWE_TryAndRemoveOldestOverworldEncounter_Object(u32 localId, u8 *objectEventId) +bool32 OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId) { if (CanRemoveOverworldEncounter(localId)) { - *objectEventId = RemoveOldestOverworldEncounter(); + *objectEventId = RemoveOldestGeneratedOverworldEncounter(); if (*objectEventId == OBJECT_EVENTS_COUNT) return TRUE; @@ -212,7 +212,7 @@ bool32 OWE_TryAndRemoveOldestOverworldEncounter_Object(u32 localId, u8 *objectEv return TRUE; } -void OWE_TryAndRemoveOldestOverworldEncounter_Palette(void) +void OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Palette(void) { // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles if (OW_WILD_ENCOUNTERS_OVERWORLD && CountFreePaletteSlots() < 2) @@ -223,7 +223,7 @@ void OWE_TryAndRemoveOldestOverworldEncounter_Palette(void) { for (; count > 0; count--) { - RemoveOldestOverworldEncounter(); + RemoveOldestGeneratedOverworldEncounter(); if (CountFreePaletteSlots() >= 2) break; } @@ -1005,7 +1005,7 @@ bool32 CanRemoveOverworldEncounter(u32 localId) || localId > LOCALID_OW_ENCOUNTER_END)); } -u32 RemoveOldestOverworldEncounter(void) +u32 RemoveOldestGeneratedOverworldEncounter(void) { u32 oldestSlot = GetOldestSlot(); diff --git a/src/sprite.c b/src/sprite.c index 33986be3725e..fff28398ece4 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -1630,7 +1630,7 @@ u32 LoadSpritePalette(const struct SpritePalette *palette) if (index == 0xFF) { - OWE_TryAndRemoveOldestOverworldEncounter_Palette(); + OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Palette(); index = IndexOfSpritePaletteTag(TAG_NONE); if (index == 0xFF) From cbcfb5f6dab1d77fa20162fea49a18c310c11f21 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 12:58:05 +0000 Subject: [PATCH 437/572] Convert RemoveObjectEventByLocalIdAndMap to RemoveObjectEvent --- src/overworld_encounters.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 116a5c216838..a661cec17da6 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -167,7 +167,7 @@ void UpdateOverworldEncounters(void) u32 objectEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[objectEventId]; if (OWE_ShouldDespawnGeneratedForNewOWE(object)) - RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); + RemoveObjectEvent(object); objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); assertf(objectEventId < OBJECT_EVENTS_COUNT, "could not spawn generated overworld encounter. too many object events exist") @@ -890,7 +890,7 @@ void RemoveAllGeneratedOverworldEncounterObjects(void) { struct ObjectEvent *obj = &gObjectEvents[i]; if (IsOverworldWildEncounter(obj, OWE_GENERATED) && obj->active) - RemoveObjectEventByLocalIdAndMap(obj->localId, obj->mapNum, obj->mapGroup); + RemoveObjectEvent(obj); } } @@ -1012,11 +1012,8 @@ u32 RemoveOldestGeneratedOverworldEncounter(void) if (oldestSlot == INVALID_SPAWN_SLOT) return OBJECT_EVENTS_COUNT; - u32 localId = GetLocalIdByOverworldSpawnSlot(oldestSlot); - u32 objectEventId = GetObjectEventIdByLocalId(localId); - struct ObjectEvent *object = &gObjectEvents[objectEventId]; - - RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); + u32 objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(oldestSlot)); + RemoveObjectEvent(&gObjectEvents[objectEventId]); return objectEventId; } @@ -1123,7 +1120,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c if (wildMon->sRoamerOutbreakStatus && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) { - RemoveObjectEventByLocalIdAndMap(wildMon->localId, wildMon->mapNum, wildMon->mapGroup); + RemoveObjectEvent(wildMon); return; } @@ -1132,12 +1129,10 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c void OverworldWildEncounter_RemoveObjectOnBattle(void) { - u32 localId = gSpecialVar_LastTalked; - struct ObjectEvent *object = &gObjectEvents[GetObjectEventIdByLocalId(localId)]; - + struct ObjectEvent *object = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; if (IsOverworldWildEncounter(object, OWE_ANY)) { - RemoveObjectEventByLocalIdAndMap(localId, object->mapNum, object->mapGroup); + RemoveObjectEvent(object); OWE_SetNewSpawnCountdown(); gSpecialVar_LastTalked = LOCALID_NONE; } @@ -1555,7 +1550,7 @@ bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct Obj if (!IsOverworldWildEncounter(curObject, OWE_GENERATED) || IsOverworldWildEncounter(objectEvent, OWE_ANY) || objectEvent->isPlayer) return FALSE; - RemoveObjectEventByLocalIdAndMap(curObject->localId, curObject->mapNum, curObject->mapGroup); + RemoveObjectEvent(curObject); return TRUE; } @@ -1568,7 +1563,7 @@ u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y) if (!IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) return collision; - RemoveObjectEventByLocalIdAndMap(objectEvent->localId, objectEvent->mapNum, objectEvent->mapGroup); + RemoveObjectEvent(objectEvent); return collision & (1 << (COLLISION_OBJECT_EVENT - 1)); } From 12018c62d1e8d68cf92386fb70e88c8dd90abc40 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 15:30:53 +0000 Subject: [PATCH 438/572] Adjust OWE_PlayMonObjectCry --- src/overworld_encounters.c | 43 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index a661cec17da6..a30db45e6dd2 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1389,43 +1389,44 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) return NULL; } -// Are these needed? Not defined elsewhere? I don't think so. -#define MAP_METATILE_VIEW_X 7 -#define MAP_METATILE_VIEW_Y 5 static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; - - u32 speciesId = OW_SPECIES(objectEvent); - u32 volume; - s32 pan; - s32 distanceX = objectEvent->currentCoords.x - gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x; - s32 distanceY = objectEvent->currentCoords.y - gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y; // TESTING: Setting this species can be used as a test to play a consistent sound to check how often the // code in UpdateOverworldEncounters runs, as OWE_GetRandomActiveEncounterObject cuurently returns // the player object. if (objectEvent == NULL) return; + + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + u32 speciesId = OW_SPECIES(objectEvent); + s32 distanceX = objectEvent->currentCoords.x - player->currentCoords.x; + s32 distanceY = objectEvent->currentCoords.y - player->currentCoords.y; + u32 distanceMax = OWE_SPAWN_RADUIS_WIDTH + OWE_SPAWN_RADUIS_HEIGHT; + u32 distance; + u32 volume; + s32 pan; + + if (distanceX > OWE_SPAWN_RADUIS_WIDTH) + distanceX = OWE_SPAWN_RADUIS_WIDTH; + else if (distanceX < -OWE_SPAWN_RADUIS_WIDTH) + distanceX = -OWE_SPAWN_RADUIS_WIDTH; - if (distanceX > MAP_METATILE_VIEW_X) - distanceX = MAP_METATILE_VIEW_X; - else if (distanceX < -MAP_METATILE_VIEW_X) - distanceX = -MAP_METATILE_VIEW_X; + distanceY = abs(distanceY); + if (distanceY > OWE_SPAWN_RADUIS_HEIGHT) + distanceY = OWE_SPAWN_RADUIS_HEIGHT; - if (distanceY > MAP_METATILE_VIEW_Y) - distanceY = MAP_METATILE_VIEW_Y; - else if (distanceY < -MAP_METATILE_VIEW_Y) - distanceY = -MAP_METATILE_VIEW_Y; + distance = abs(distanceX) + distanceY; + if (distance > distanceMax) + distance = distanceMax; - pan = (distanceX * 44) / MAP_METATILE_VIEW_X; - volume = 50 + ((distanceY + MAP_METATILE_VIEW_Y) * 30) / (2 * MAP_METATILE_VIEW_Y); + volume = 80 - (distance * (80 - 50)) / distanceMax; + pan = 212 + ((distanceX + OWE_SPAWN_RADUIS_WIDTH) * (300 - 212)) / (2 * OWE_SPAWN_RADUIS_WIDTH); PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); } -#undef MAP_METATILE_VIEW_X -#undef MAP_METATILE_VIEW_Y static bool32 OWE_DoesRoamerObjectExist(void) { From 06d3be7a5ac838273f4e9ea4e9b61067e28551f2 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:30:58 +0000 Subject: [PATCH 439/572] Adjust Created Macros --- asm/macros/event.inc | 10 ++++++++++ data/scripts/overworld_encounters.inc | 4 ++-- data/specials.inc | 1 - include/event_object_movement.h | 2 +- src/event_object_movement.c | 6 +++--- src/follower_npc.c | 2 +- src/overworld_encounters.c | 5 +---- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index a8df70154154..b76b7cf675c0 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2793,3 +2793,13 @@ callnative ScrCmd_setstartingstatus .byte \status .endm + + @ Makes the player and selected object (if there is one) face one another. + .macro facetogether + callnative ScriptFaceEachOther + .endm + + @ Starts an Overworld Wild Encounter with a selected object, if possible. + .macro tryoverworldwildencounter + callnative CreateOverworldWildEncounter + .endm diff --git a/data/scripts/overworld_encounters.inc b/data/scripts/overworld_encounters.inc index 5642af1a3426..893859a1457b 100644 --- a/data/scripts/overworld_encounters.inc +++ b/data/scripts/overworld_encounters.inc @@ -2,10 +2,10 @@ InteractWithDynamicWildOverworldEncounter:: lockall callnative OWE_ApproachForBattle applymovement VAR_LAST_TALKED, Common_Movement_ExclamationMark - callnative ScriptFaceLastTalked + facetogether playmoncry VAR_0x8004, CRY_MODE_DOUBLES waitmoncry - special CreateOverworldWildEncounter + tryoverworldwildencounter waitstate end diff --git a/data/specials.inc b/data/specials.inc index 333ca718bcc2..1aee7e91cb06 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -568,4 +568,3 @@ gSpecials:: def_special CanTeachMoveBoxMon def_special ObjectEventInteractionGetApricornTreeData def_special ObjectEventInteractionPickApricornTree - def_special CreateOverworldWildEncounter diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 6ed3fa5ea4e2..a6975ea571d4 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -274,7 +274,7 @@ u32 LoadDynamicFollowerPalette(u32 species, bool32 shiny, bool32 female); u16 GetObjectEventFlagIdByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup); bool32 AreElevationsCompatible(u32, u32); enum Direction DetermineObjectEventDirectionFromObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo); -void ObjectEventTurnToObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo); +void ObjectEventsTurnToEachOther(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo); void MovementType_None(struct Sprite *sprite); void MovementType_LookAround(struct Sprite *sprite); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 6ab7ef4f7a75..9e7f0613a376 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -9955,12 +9955,12 @@ bool32 AreElevationsCompatible(u32 a, u32 b) return TRUE; } -void ScriptFaceLastTalked(struct ScriptContext *ctx) +void ScriptFaceEachOther(struct ScriptContext *ctx) { struct ObjectEvent *player, *npc; player = &gObjectEvents[gPlayerAvatar.objectEventId]; npc = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; - ObjectEventTurnToObject(player, npc); + ObjectEventsTurnToEachOther(player, npc); } enum Direction DetermineObjectEventDirectionFromObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo) @@ -10046,7 +10046,7 @@ enum Direction DetermineObjectEventDirectionFromObject(struct ObjectEvent *objec } } -void ObjectEventTurnToObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo) +void ObjectEventsTurnToEachOther(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo) { enum Direction objectDirOne, objectDirTwo; diff --git a/src/follower_npc.c b/src/follower_npc.c index dbec206b2286..259f88d5abbb 100644 --- a/src/follower_npc.c +++ b/src/follower_npc.c @@ -1816,7 +1816,7 @@ void ScriptFaceFollowerNPC(struct ScriptContext *ctx) struct ObjectEvent *player, *follower; player = &gObjectEvents[gPlayerAvatar.objectEventId]; follower = &gObjectEvents[GetFollowerNPCData(FNPC_DATA_OBJ_ID)]; - ObjectEventTurnToObject(player, follower); + ObjectEventsTurnToEachOther(player, follower); } static const u8 *const FollowerNPCHideMovementsSpeedTable[][4] = diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index a30db45e6dd2..f8b5218bfaf2 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -514,10 +514,7 @@ void CreateOverworldWildEncounter(void) struct ObjectEvent *object = &gObjectEvents[objEventId]; u32 indexRoamerOutbreak = object->sRoamerOutbreakStatus; - if (objEventId >= OBJECT_EVENTS_COUNT) - return; - - if (!IsOverworldWildEncounter(object, OWE_ANY)) + if (objEventId >= OBJECT_EVENTS_COUNT || !IsOverworldWildEncounter(object, OWE_ANY)) return; if (indexRoamerOutbreak && CreateOverworldWildEncounter_CheckRoamer(OWE_GetObjectRoamerOutbreakStatus(object))) From b02c70ce66469aba05b7255279eb26a4b2ca25a0 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:35:07 +0000 Subject: [PATCH 440/572] Create overworldwildencounterapproach macro --- asm/macros/event.inc | 5 +++++ data/scripts/overworld_encounters.inc | 2 +- src/overworld_encounters.c | 7 ++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index b76b7cf675c0..0febd89317ca 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2803,3 +2803,8 @@ .macro tryoverworldwildencounter callnative CreateOverworldWildEncounter .endm + + @ Start a scripted approach for an overworld wild encounter towards the player. + .macro overworldwildencounterapproach + callnative OWE_ApproachForBattle + .endm diff --git a/data/scripts/overworld_encounters.inc b/data/scripts/overworld_encounters.inc index 893859a1457b..8fa46b4d1cae 100644 --- a/data/scripts/overworld_encounters.inc +++ b/data/scripts/overworld_encounters.inc @@ -1,6 +1,6 @@ InteractWithDynamicWildOverworldEncounter:: lockall - callnative OWE_ApproachForBattle + overworldwildencounterapproach applymovement VAR_LAST_TALKED, Common_Movement_ExclamationMark facetogether playmoncry VAR_0x8004, CRY_MODE_DOUBLES diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f8b5218bfaf2..d8bdf7c33544 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1624,11 +1624,16 @@ void OWE_ApproachForBattle(void) if (!OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE) return; + u32 objectEventId = GetObjectEventIdByLocalId(gSpecialVar_LastTalked); + struct ObjectEvent *objectEvent = &gObjectEvents[objectEventId]; + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + return; + u32 taskId = CreateTask(Task_OWE_ApproachForBattle, 2); if (FindTaskIdByFunc(Task_OWE_ApproachForBattle) != TASK_NONE) { ScriptContext_Stop(); - gTasks[taskId].tObjectId = GetObjectEventIdByLocalId(gSpecialVar_LastTalked); + gTasks[taskId].tObjectId = objectEventId; } } From 5979f8ae8d96fdad004c1dcf792176c9a02437e9 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:40:38 +0000 Subject: [PATCH 441/572] Revert "Use IsOverworldWildEncounter in TryGetObjectEventTemplateForOverworldEncounter" This reverts commit 918efdb40543e35870ef28ca578ca28a47f9b203. --- src/overworld_encounters.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index d8bdf7c33544..0aeffba2b816 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1026,7 +1026,8 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) { - if (!IsOverworldWildEncounter((struct ObjectEvent *)template, OWE_MANUAL)) + if (template->trainerType != TRAINER_TYPE_ENCOUNTER || (template->localId <= LOCALID_OW_ENCOUNTER_END + && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS))) return *template; struct ObjectEventTemplate templateOWE = *template; From 946cce5193bfb5c2ab8688cef54d340f1764a549 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:41:38 +0000 Subject: [PATCH 442/572] typo --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 0aeffba2b816..82ed39cb5695 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -238,7 +238,7 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is return FALSE; } - assertf(OWE_CanEncounterBeLoaded_Tiles(speciesId, isFemale, isShiny, x, y), "could not load palette for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) + assertf(OWE_CanEncounterBeLoaded_Tiles(speciesId, isFemale, isShiny, x, y), "could not load sprite tiles for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) { return FALSE; } From 830cc8c21a783c55438464a6c21d9aed77206c79 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 17:26:56 +0000 Subject: [PATCH 443/572] Fix For OWE mid movement --- data/scripts/overworld_encounters.inc | 2 +- src/overworld_encounters.c | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/data/scripts/overworld_encounters.inc b/data/scripts/overworld_encounters.inc index 8fa46b4d1cae..e91f2be2c98b 100644 --- a/data/scripts/overworld_encounters.inc +++ b/data/scripts/overworld_encounters.inc @@ -1,5 +1,5 @@ InteractWithDynamicWildOverworldEncounter:: - lockall + lock overworldwildencounterapproach applymovement VAR_LAST_TALKED, Common_Movement_ExclamationMark facetogether diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 82ed39cb5695..0e36afc61ebe 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1622,20 +1622,26 @@ static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction #define tObjectId data[0] void OWE_ApproachForBattle(void) { - if (!OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE) - return; - u32 objectEventId = GetObjectEventIdByLocalId(gSpecialVar_LastTalked); struct ObjectEvent *objectEvent = &gObjectEvents[objectEventId]; + if (!OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE || !IsOverworldWildEncounter(objectEvent, OWE_ANY)) + { + FreezeObjectEvent(objectEvent); + return; + } + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; u32 taskId = CreateTask(Task_OWE_ApproachForBattle, 2); - if (FindTaskIdByFunc(Task_OWE_ApproachForBattle) != TASK_NONE) + if (FindTaskIdByFunc(Task_OWE_ApproachForBattle) == TASK_NONE) { - ScriptContext_Stop(); - gTasks[taskId].tObjectId = objectEventId; + FreezeObjectEvent(objectEvent); + return; } + + ScriptContext_Stop(); + gTasks[taskId].tObjectId = objectEventId; } static void Task_OWE_ApproachForBattle(u8 taskId) From 1310c3591208b14a2e5e36164ca535fdb12f1de8 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 18:24:34 +0000 Subject: [PATCH 444/572] Updated Fix --- src/overworld_encounters.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 0e36afc61ebe..464351c51ebd 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1651,14 +1651,15 @@ static void Task_OWE_ApproachForBattle(u8 taskId) // Let the mon continue to take steps until right next to the player. if (ObjectEventClearHeldMovementIfFinished(OWE)) { + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; if (OWE_IsMonNextToPlayer(OWE)) { + ObjectEventsTurnToEachOther(player, OWE); ScriptContext_Enable(); DestroyTask(taskId); return; } - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u16 speciesId = OW_SPECIES(OWE); enum Direction direction = DetermineObjectEventDirectionFromObject(player, OWE); u8 movementActionId; From ba9ab9a800c81f032f31fddbd99178a0c189c3e7 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 18:44:46 +0000 Subject: [PATCH 445/572] Assertf Soft Lock --- src/overworld_encounters.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 464351c51ebd..2a1e819c813c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -514,8 +514,12 @@ void CreateOverworldWildEncounter(void) struct ObjectEvent *object = &gObjectEvents[objEventId]; u32 indexRoamerOutbreak = object->sRoamerOutbreakStatus; - if (objEventId >= OBJECT_EVENTS_COUNT || !IsOverworldWildEncounter(object, OWE_ANY)) + assertf(objEventId < OBJECT_EVENTS_COUNT && IsOverworldWildEncounter(object, OWE_ANY), "cannot start overworld wild enocunter as the selected object is invalid.\nlocalId: %d", localId) + { + UnlockPlayerFieldControls(); + UnfreezeObjectEvents(); return; + } if (indexRoamerOutbreak && CreateOverworldWildEncounter_CheckRoamer(OWE_GetObjectRoamerOutbreakStatus(object))) return; From 26752930eaf349d16802db5a7698081f01ffadac Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 18:58:46 +0000 Subject: [PATCH 446/572] Adjust ShouldRunOverworldEncounterScript --- src/field_control_avatar.c | 1 + src/overworld_encounters.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 066004fdf71d..9cd4e028313e 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -400,6 +400,7 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; gSpecialVar_Facing = direction; + DebugPrintf("shouldrunowescript=%d", ShouldRunOverworldEncounterScript(objectEventId)); if (InTrainerHill() == TRUE) script = GetTrainerHillTrainerScript(); else if (PlayerHasFollowerNPC() && objectEventId == GetFollowerNPCObjectId()) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 2a1e819c813c..15a0637b8f32 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1021,7 +1021,7 @@ u32 RemoveOldestGeneratedOverworldEncounter(void) bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) { struct ObjectEvent *object = &gObjectEvents[objectEventId]; - if (!IsOverworldWildEncounter(object, OWE_ANY) || GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithDynamicWildOverworldEncounter) + if (!IsOverworldWildEncounter(object, OWE_ANY)) return FALSE; gSpecialVar_0x8004 = OW_SPECIES(object); From 29ecbb0de9ee7a3a72693fbab3eb74cba9174f40 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 19:22:34 +0000 Subject: [PATCH 447/572] Better ShouldRunOverworldEncounterScript Adjustment --- src/overworld_encounters.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 15a0637b8f32..338c5be83b9d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1024,6 +1024,11 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) if (!IsOverworldWildEncounter(object, OWE_ANY)) return FALSE; + if (IsOverworldWildEncounter(object, OWE_MANUAL) + && GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithDynamicWildOverworldEncounter + && GetObjectEventScriptPointerByObjectEventId(objectEventId) != NULL) + return FALSE; + gSpecialVar_0x8004 = OW_SPECIES(object); return TRUE; } From 9c1e66d1828170be67ff755137ef333c877742bb Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:55:11 -0600 Subject: [PATCH 448/572] Reduced sSavedMovementState to 1 bit --- include/overworld_encounters.h | 6 +++++- src/event_object_movement.c | 3 ++- src/overworld_encounters.c | 13 +++++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 32eb890e9973..eca09a27a110 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -40,6 +40,9 @@ #define INVALID_SPAWN_SLOT 0xFF +#define OWE_SAVED_MOVEMENT_STATE 0x80 +#define OWE_RESTORED_MOVEMENT_FUNC_ID 10 + enum OverworldEncounterSpawnAnim { OWE_SPAWN_ANIM_GRASS, @@ -122,7 +125,8 @@ bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct Obj u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite); -void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent, u32 state); +void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent); +void OWE_ClearSavedMovementState(struct ObjectEvent *objectEvent); u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 9e7f0613a376..3f11dc762c12 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11778,7 +11778,7 @@ movement_type_def(MovementType_ChasePlayer_OverworldWildEncounter, gMovementType bool8 MovementType_Common_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite) { ClearObjectEventMovement(objectEvent, sprite); - OWE_SetSavedMovementState(objectEvent, 10); + OWE_SetSavedMovementState(objectEvent); sprite->sTypeFuncId = 8; return TRUE; } @@ -11861,6 +11861,7 @@ bool8 MovementType_Common_OverworldWildEncounter_Step12(struct ObjectEvent *obje if (!OWE_IsPlayerInsideMonActiveDistance(objectEvent)) { + OWE_ClearSavedMovementState(objectEvent); sprite->sTypeFuncId = 0; return FALSE; } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 338c5be83b9d..b0e52bea9ef9 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1599,14 +1599,19 @@ struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnA #define sTypeFuncId data[1] // Same as in src/event_object_movement.c void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (IsOverworldWildEncounter(objectEvent, OWE_ANY)) - sprite->sTypeFuncId = objectEvent->sSavedMovementState; + if (IsOverworldWildEncounter(objectEvent, OWE_ANY) && objectEvent->sSavedMovementState & OWE_SAVED_MOVEMENT_STATE) + sprite->sTypeFuncId = OWE_RESTORED_MOVEMENT_FUNC_ID; } #undef sTypeFuncId -void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent, u32 state) +void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent) { - objectEvent->sSavedMovementState = state; + objectEvent->sSavedMovementState |= OWE_SAVED_MOVEMENT_STATE; +} + +void OWE_ClearSavedMovementState(struct ObjectEvent *objectEvent) +{ + objectEvent->sSavedMovementState ^= OWE_SAVED_MOVEMENT_STATE; } static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance) From 7654813b5452ccd2f39c601f8f74384ea12fe719 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 27 Jan 2026 22:14:22 +0000 Subject: [PATCH 449/572] Update how_to_overworld_wild_encounters.md --- docs/tutorials/how_to_overworld_wild_encounters.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 3ca4d446c16c..eaa8d1749269 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -4,6 +4,7 @@ Overworld Wild Encounters (OWEs), refer to the wild encounters that can be seen ### Generated OWEs Generated OWEs are spawned automatically when `OW_WILD_ENCOUNTERS_OVERWORLD` is set to `TRUE`, being spawned on a random encounter tile near the player, with the encounter table used dependant on it. These are considered low priority OWEs, and automatically populate a level, species, gender and shinyness exactly how a vanilla wild encounter would, or can even be a special spawn, but more on those later. +> Setting `OW_GFX_COMPRESS` to `FALSE` will free more space in VRAM, allowing for more large OWEs to spawn, and reducing the chance of running into a tiles error when trying to spawn Generated OWEs. ### Manual OWEs Manual OWEs are created by the developer as any other object event would be and are defined by having the `.trainerType` set to `TRAINER_TYPE_ENCOUNTER`. These can be fully customised by the developer, with the level, species, gender and shinyness all able to be specified. The level can be set by filling the desired value in the `trainerRange_berryTreeId`. The latter three are specified by the `graphicsId` of the object, for example; @@ -27,13 +28,13 @@ Special spawns can be one of three types, in decreasing priority: A Roamer, Feeb - If a Roamer is on the route and is able to spawn, then it may appear where a Generated OWE would. - If any OWE spawns on a tile where a Feebas would spawn, it may appear is a Feebas. - If a Generated OWE spawns on a route that has a mass outbreak occuring, it may spawn as an encounter from that mass outbreak. +> OWE_MAX_ROAMERS ### Restricted Despawning ## High Priority and Low Priority OWEs Low Priority OWEs may face not be spawned or even be destroyed in certain situations. There are palettes and object tiles checks to prevent these from spawning if it would fail, as well as similar checks for number of event objects, palettes and object tiles that despawn the oldest of High Priority OWEs or other objects event are attempting to be spawned and Low Priority OWEs are using these resources. Low Priority OWEs may also be destroyed by NPC object events colliding with them due to their movement functions or them being in the way of a trainer interaction. High priority OWEs are treated as regular objects and will not be destroyed in the ways outlined above, but may cause the destruction of Generated OWEs and will not face spawning restrictions. These despawn conditions will overwrite the restrictive despawns mentioned above. > Is this true? Does it take the oldest or oldest not marked for restriction, what if all are marked to not despawn. -> Setting `OW_GFX_COMPRESS` to `FALSE` will free more space in VRAM, allowing for more large OWEs to spawn. ## Encountering an OWE Collision between Player and OWE or Interacting with one. Can also interact with an OWE in the water even when the player is not. From a0c05457531fd9175c8ed93dc4de0915bc9a9fb8 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:55:48 -0600 Subject: [PATCH 450/572] Added OWE_NO_REPLACE_FLAG bit --- include/config/overworld.h | 30 ++++++++++++++++-------------- include/overworld_encounters.h | 4 +++- src/overworld_encounters.c | 26 +++++++++++++++----------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 55f2b2153cf1..c8eff09a7845 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -74,20 +74,22 @@ #define OW_FOLLOWERS_ALLOWED_MET_LOC (0) // Overworld Encounters -#define OW_WILD_ENCOUNTERS_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. -#define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. -#define OW_WILD_ENCOUNTERS_BATTLE_PIKE TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pike, if FALSE random encounters will be enabled instead. Requires OW_WILD_ENCOUNTERS_OVERWORLD to be TRUE. -#define OW_WILD_ENCOUNTERS_BATTLE_PYRAMID TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pyramid, if FALSE random encounters will be enabled instead. Requires OW_WILD_ENCOUNTERS_OVERWORLD to be TRUE. -#define OW_WILD_ENCOUNTERS_RESTRICT_METATILE TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same encounter metatile behavior as the one it is currently on, if any. -#define OW_WILD_ENCOUNTERS_RESTRICT_MAP TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within their current map bounds. -#define OW_WILD_ENCOUNTERS_UNRESTRICT_SIGHT FALSE // If TRUE, OW Pokémon spawned as overworld wild encounters will ignore all movement restrictions when they can see the player. -#define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. -#define OW_WILD_ENCOUNTERS_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. -#define OW_VANILLA_AMBIENT_CRIES TRUE // If TRUE, the ambient wild Pokémon cries will play on routes with encounter tables. -#define OW_WILD_ENCOUNTERS_AMBIENT_CRIES TRUE // If TRUE, generated overworld encounters will play ambient cries based on their location relative to the player. -#define OW_WILD_ENCOUNTERS_FEEBAS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. -#define OW_WILD_ENCOUNTERS_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an overworld wild encounter despawns. -#define OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE TRUE // If TRUE, the mon will take steps to be right next to the player before the battle starts. +#define OW_WILD_ENCOUNTERS_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. +#define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. +#define OW_WILD_ENCOUNTERS_BATTLE_PIKE TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pike, if FALSE random encounters will be enabled instead. Requires OW_WILD_ENCOUNTERS_OVERWORLD to be TRUE. +#define OW_WILD_ENCOUNTERS_BATTLE_PYRAMID TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pyramid, if FALSE random encounters will be enabled instead. Requires OW_WILD_ENCOUNTERS_OVERWORLD to be TRUE. +#define OW_WILD_ENCOUNTERS_RESTRICT_METATILE TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same encounter metatile behavior as the one it is currently on, if any. +#define OW_WILD_ENCOUNTERS_RESTRICT_MAP TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within their current map bounds. +#define OW_WILD_ENCOUNTERS_UNRESTRICT_SIGHT FALSE // If TRUE, OW Pokémon spawned as overworld wild encounters will ignore all movement restrictions when they can see the player. +#define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. +#define OW_WILD_ENCOUNTERS_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. +#define OW_VANILLA_AMBIENT_CRIES TRUE // If TRUE, the ambient wild Pokémon cries will play on routes with encounter tables. +#define OW_WILD_ENCOUNTERS_AMBIENT_CRIES TRUE // If TRUE, generated overworld encounters will play ambient cries based on their location relative to the player. +#define OW_WILD_ENCOUNTERS_FEEBAS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. +#define OW_WILD_ENCOUNTERS_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an overworld wild encounter despawns. +#define OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE TRUE // If TRUE, the mon will take steps to be right next to the player before the battle starts. +#define OW_WILD_ENCOUNTERS_PREVENT_SHINY_REPLACEMENT TRUE // If TRUE, shiny OW Pokémon objects will not be despawned if OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. +#define OW_WILD_ENCOUNTERS_PREVENT_FEEBAS_REPLACEMENT TRUE // If TRUE, Feebas OW objects spawned from special Feebas fishing spots (when OW_WILD_ENCOUNTERS_FEEBAS is TRUE) will not be despawned if OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. // Out-of-battle Ability effects #define OW_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8+, if a Pokémon with Synchronize leads the party, wild Pokémon will always have their same Nature as opposed to the 50% chance in previous games. Gift Pokémon excluded. diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index eca09a27a110..6ef39a12fe27 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -41,6 +41,8 @@ #define INVALID_SPAWN_SLOT 0xFF #define OWE_SAVED_MOVEMENT_STATE 0x80 +#define OWE_NO_REPLACE_FLAG 0x80 + #define OWE_RESTORED_MOVEMENT_FUNC_ID 10 enum OverworldEncounterSpawnAnim @@ -95,7 +97,7 @@ extern const u8 InteractWithDynamicWildOverworldEncounter[]; void OWE_ResetSpawnCounterPlayAmbientCry(void); void UpdateOverworldEncounters(void); -u32 GetOldestSlot(void); +u32 GetOldestSlot(bool32 forceRemove); void CreateOverworldWildEncounter(void); void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index b0e52bea9ef9..34525c9ac331 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -63,7 +63,7 @@ static bool32 OWE_DoesRoamerObjectExist(void); static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); static u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); -static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); +static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y, bool32 *isFeebasSpot); static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); @@ -346,7 +346,7 @@ static u32 GetMaxGeneratedOverworldEncounterSpawns(void) return OWE_MAX_LAND_SPAWNS; } -u32 GetOldestSlot(void) +u32 GetOldestSlot(bool32 forceRemove) { struct ObjectEvent *slotMon, *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; u32 spawnSlot; @@ -354,8 +354,7 @@ u32 GetOldestSlot(void) for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWN_SLOTS; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE && !OW_SHINY(slotMon)) - // Add a not feebas tile feebas? + if (OW_SPECIES(slotMon) != SPECIES_NONE && (!(slotMon->sOverworldEncounterLevel & OWE_NO_REPLACE_FLAG) || forceRemove == TRUE)) { oldest = slotMon; break; @@ -368,7 +367,7 @@ u32 GetOldestSlot(void) for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWN_SLOTS; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE && !OW_SHINY(slotMon)) + if (OW_SPECIES(slotMon) != SPECIES_NONE && (!(slotMon->sOverworldEncounterLevel & OWE_NO_REPLACE_FLAG) || forceRemove == TRUE)) { if (slotMon->sAge > oldest->sAge) oldest = slotMon; @@ -389,7 +388,7 @@ static u8 NextSpawnMonSlot(void) if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) { // Cycle through so we remove the oldest mon first - spawnSlot = GetOldestSlot(); + spawnSlot = GetOldestSlot(FALSE); if (spawnSlot == INVALID_SPAWN_SLOT) return INVALID_SPAWN_SLOT; } @@ -527,7 +526,7 @@ void CreateOverworldWildEncounter(void) u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; u32 gender = OW_FEMALE(object) ? MON_FEMALE : MON_MALE; - u32 level = object->sOverworldEncounterLevel; + u32 level = (object->sOverworldEncounterLevel &= ~OWE_NO_REPLACE_FLAG); u32 personality; switch (gSpeciesInfo[speciesId].genderRatio) @@ -748,8 +747,9 @@ void OverworldWildEncounter_SetMinimumSpawnTimer(void) static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) { u32 personality; + bool32 isFeebasSpot = FALSE; - if (!OWE_CreateEnemyPartyMon(speciesId, level, indexRoamerOutbreak, x, y)) + if (!OWE_CreateEnemyPartyMon(speciesId, level, indexRoamerOutbreak, x, y, &isFeebasSpot)) { ZeroEnemyPartyMons(); *speciesId = SPECIES_NONE; @@ -769,10 +769,13 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 else *isFemale = FALSE; + if ((OW_WILD_ENCOUNTERS_PREVENT_SHINY_REPLACEMENT && *isShiny) || (OW_WILD_ENCOUNTERS_PREVENT_FEEBAS_REPLACEMENT && isFeebasSpot)) + *level |= OWE_NO_REPLACE_FLAG; + ZeroEnemyPartyMons(); } -static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) +static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y, bool32 *isFeebasSpot) { const struct WildPokemonInfo *wildMonInfo; enum WildPokemonArea wildArea; @@ -845,6 +848,7 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam { *level = ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING); *speciesId = gWildFeebas.species; + *isFeebasSpot = TRUE; CreateWildMon(*speciesId, *level); } else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior) && *indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) @@ -1008,7 +1012,7 @@ bool32 CanRemoveOverworldEncounter(u32 localId) u32 RemoveOldestGeneratedOverworldEncounter(void) { - u32 oldestSlot = GetOldestSlot(); + u32 oldestSlot = GetOldestSlot(TRUE); if (oldestSlot == INVALID_SPAWN_SLOT) return OBJECT_EVENTS_COUNT; @@ -1536,7 +1540,7 @@ static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) { if (!IsOverworldWildEncounter(object, OWE_GENERATED)) return FALSE; - + return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) == GetMaxGeneratedOverworldEncounterSpawns(); } From 9310ec669be847f7a727c9b8ee3ef4a0875dc78d Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:57:14 -0600 Subject: [PATCH 451/572] Renamed OWE_SAVED_MOVEMENT_STATE to OWE_SAVED_MOVEMENT_STATE_FLAG --- include/overworld_encounters.h | 2 +- src/overworld_encounters.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 6ef39a12fe27..78c05e6af31c 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -40,9 +40,9 @@ #define INVALID_SPAWN_SLOT 0xFF -#define OWE_SAVED_MOVEMENT_STATE 0x80 #define OWE_NO_REPLACE_FLAG 0x80 +#define OWE_SAVED_MOVEMENT_STATE_FLAG 0x80 #define OWE_RESTORED_MOVEMENT_FUNC_ID 10 enum OverworldEncounterSpawnAnim diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 34525c9ac331..a61c59d27289 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1603,19 +1603,19 @@ struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnA #define sTypeFuncId data[1] // Same as in src/event_object_movement.c void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (IsOverworldWildEncounter(objectEvent, OWE_ANY) && objectEvent->sSavedMovementState & OWE_SAVED_MOVEMENT_STATE) + if (IsOverworldWildEncounter(objectEvent, OWE_ANY) && objectEvent->sSavedMovementState & OWE_SAVED_MOVEMENT_STATE_FLAG) sprite->sTypeFuncId = OWE_RESTORED_MOVEMENT_FUNC_ID; } #undef sTypeFuncId void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent) { - objectEvent->sSavedMovementState |= OWE_SAVED_MOVEMENT_STATE; + objectEvent->sSavedMovementState |= OWE_SAVED_MOVEMENT_STATE_FLAG; } void OWE_ClearSavedMovementState(struct ObjectEvent *objectEvent) { - objectEvent->sSavedMovementState ^= OWE_SAVED_MOVEMENT_STATE; + objectEvent->sSavedMovementState ^= OWE_SAVED_MOVEMENT_STATE_FLAG; } static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance) From 6f6fe20a8daeb53a4d8e02b25ab4ae5bad7fce89 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 27 Jan 2026 17:10:10 -0600 Subject: [PATCH 452/572] Consolidated max spawns into one value --- include/overworld_encounters.h | 4 +--- src/overworld_encounters.c | 24 +++++------------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 78c05e6af31c..71342bc0be7e 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -7,9 +7,7 @@ #define OWE_MAX_SPAWN_SLOTS 5 -#define OWE_MAX_LAND_SPAWNS 3 -#define OWE_MAX_WATER_SPAWNS 5 -#define OWE_MAX_CAVE_SPAWNS 4 +#define OWE_MAX_SPAWNS 4 #define OWE_SPAWN_DISTANCE_LAND 1 // A spawn cannot happen within this many tiles of the player position. #define OWE_SPAWN_DISTANCE_WATER 3 // A spawn cannot happen within this many tiles of the player position (while surfing). diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index a61c59d27289..758abba3a4df 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -69,7 +69,6 @@ static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); static bool32 CreateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); -static u32 GetMaxGeneratedOverworldEncounterSpawns(void); static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); @@ -115,7 +114,7 @@ void UpdateOverworldEncounters(void) u16 spawnSlot = NextSpawnMonSlot(); if (LURE_STEP_COUNT && spawnSlot != INVALID_SPAWN_SLOT - && (GetNumberActiveOverworldEncounters(OWE_GENERATED) < GetMaxGeneratedOverworldEncounterSpawns())) + && (GetNumberActiveOverworldEncounters(OWE_GENERATED) < OWE_MAX_SPAWNS)) { OverworldWildEncounter_SetMinimumSpawnTimer(); } @@ -191,7 +190,7 @@ static void OWE_SetNewSpawnCountdown(void) { u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); - if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && numActive >= GetMaxGeneratedOverworldEncounterSpawns()) + if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && numActive >= OWE_MAX_SPAWNS) sOWESpawnCountdown = OWE_SPAWN_TIME_REPLACEMENT; else sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); @@ -334,18 +333,6 @@ static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animS MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); } -static u32 GetMaxGeneratedOverworldEncounterSpawns(void) -{ - // Should this just be a flat number? - // How does it effect things if going from water to land etc or vice versa - if (OWE_ShouldSpawnWaterMons()) - return OWE_MAX_WATER_SPAWNS; - else if (gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) - return OWE_MAX_CAVE_SPAWNS; - else - return OWE_MAX_LAND_SPAWNS; -} - u32 GetOldestSlot(bool32 forceRemove) { struct ObjectEvent *slotMon, *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; @@ -380,10 +367,9 @@ u32 GetOldestSlot(bool32 forceRemove) static u8 NextSpawnMonSlot(void) { u32 spawnSlot; - u32 maxSpawns = GetMaxGeneratedOverworldEncounterSpawns(); // All mon slots are in use - if (GetNumberActiveOverworldEncounters(OWE_GENERATED) >= maxSpawns) + if (GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_MAX_SPAWNS) { if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) { @@ -399,7 +385,7 @@ static u8 NextSpawnMonSlot(void) } else { - for (spawnSlot = 0; spawnSlot < maxSpawns; spawnSlot++) + for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWNS; spawnSlot++) { if (GetOverworldSpeciesBySpawnSlot(spawnSlot) == SPECIES_NONE) break; @@ -1541,7 +1527,7 @@ static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) if (!IsOverworldWildEncounter(object, OWE_GENERATED)) return FALSE; - return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) == GetMaxGeneratedOverworldEncounterSpawns(); + return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_MAX_SPAWNS; } void OWE_StartEncounter(struct ObjectEvent *mon) From d9ca3e2e7b2d6ebeaeccf23f65c787c837626f6c Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 27 Jan 2026 17:18:33 -0600 Subject: [PATCH 453/572] Added config to disable shiny spawn anim --- include/config/overworld.h | 1 + src/overworld_encounters.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index c8eff09a7845..167c35c8ca91 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -85,6 +85,7 @@ #define OW_WILD_ENCOUNTERS_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. #define OW_VANILLA_AMBIENT_CRIES TRUE // If TRUE, the ambient wild Pokémon cries will play on routes with encounter tables. #define OW_WILD_ENCOUNTERS_AMBIENT_CRIES TRUE // If TRUE, generated overworld encounters will play ambient cries based on their location relative to the player. +#define OW_WILD_ENCOUNTERS_SHINY_SPARKLE FALSE // If TRUE, a shiny overworld wild encounter will spawn with a sparkle animation and play the shiny sound effect. #define OW_WILD_ENCOUNTERS_FEEBAS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. #define OW_WILD_ENCOUNTERS_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an overworld wild encounter despawns. #define OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE TRUE // If TRUE, the mon will take steps to be right next to the player before the battle starts. diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 758abba3a4df..7c89c8fc279c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -320,7 +320,7 @@ static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animS if (!animSpawn && OWE_ShouldPlayMonFleeSound(objectEvent)) PlaySE(SE_FLEE); - if (isShiny && animSpawn) + if (OW_WILD_ENCOUNTERS_SHINY_SPARKLE && isShiny && animSpawn) { PlaySE(SE_SHINY); spawnAnimType = OWE_SPAWN_ANIM_SHINY; From 45d89cfeb96b4dbfccaf006569169dddc04938e8 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 27 Jan 2026 23:36:16 -0600 Subject: [PATCH 454/572] Renamed OW_WILD_ENCOUNTERS_RANDOM for clarity --- include/config/overworld.h | 2 +- src/overworld_encounters.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 167c35c8ca91..52d453716a47 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -74,7 +74,7 @@ #define OW_FOLLOWERS_ALLOWED_MET_LOC (0) // Overworld Encounters -#define OW_WILD_ENCOUNTERS_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. +#define OW_VANILLA_RANDOM_ENCOUNTERS TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. #define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. #define OW_WILD_ENCOUNTERS_BATTLE_PIKE TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pike, if FALSE random encounters will be enabled instead. Requires OW_WILD_ENCOUNTERS_OVERWORLD to be TRUE. #define OW_WILD_ENCOUNTERS_BATTLE_PYRAMID TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pyramid, if FALSE random encounters will be enabled instead. Requires OW_WILD_ENCOUNTERS_OVERWORLD to be TRUE. diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7c89c8fc279c..f88b4f395246 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1519,7 +1519,7 @@ bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void) || (CurrentBattlePyramidLocation() && !OW_WILD_ENCOUNTERS_BATTLE_PYRAMID)) return FALSE; - return !OW_WILD_ENCOUNTERS_RANDOM; + return !OW_VANILLA_RANDOM_ENCOUNTERS; } static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) From f253e91df9a946bf797f0bebcb7122617334a27f Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 28 Jan 2026 00:08:38 -0600 Subject: [PATCH 455/572] Moved OWE configs to their own file --- .../how_to_overworld_wild_encounters.md | 2 +- include/config/overworld.h | 19 --------- include/config/overworld_encounters.h | 24 +++++++++++ include/constants/global.h | 1 + include/overworld_encounters.h | 6 +-- src/event_object_movement.c | 2 +- src/overworld.c | 2 +- src/overworld_encounters.c | 41 ++++++++++--------- 8 files changed, 52 insertions(+), 45 deletions(-) create mode 100644 include/config/overworld_encounters.h diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 3ca4d446c16c..7bb43a927a07 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -3,7 +3,7 @@ Overworld Wild Encounters (OWEs), refer to the wild encounters that can be seen as object events in the overworld, prior to engaging in battle with them. They use either the `WILD_AREA_LAND` or `WILD_AREA_WATER` encounter tables by default. OWEs come in two types, Generated or Manual. ### Generated OWEs -Generated OWEs are spawned automatically when `OW_WILD_ENCOUNTERS_OVERWORLD` is set to `TRUE`, being spawned on a random encounter tile near the player, with the encounter table used dependant on it. These are considered low priority OWEs, and automatically populate a level, species, gender and shinyness exactly how a vanilla wild encounter would, or can even be a special spawn, but more on those later. +Generated OWEs are spawned automatically when `OWE_WILD_ENCOUNTERS_OVERWORLD` is set to `TRUE`, being spawned on a random encounter tile near the player, with the encounter table used dependant on it. These are considered low priority OWEs, and automatically populate a level, species, gender and shinyness exactly how a vanilla wild encounter would, or can even be a special spawn, but more on those later. ### Manual OWEs Manual OWEs are created by the developer as any other object event would be and are defined by having the `.trainerType` set to `TRAINER_TYPE_ENCOUNTER`. These can be fully customised by the developer, with the level, species, gender and shinyness all able to be specified. The level can be set by filling the desired value in the `trainerRange_berryTreeId`. The latter three are specified by the `graphicsId` of the object, for example; diff --git a/include/config/overworld.h b/include/config/overworld.h index 52d453716a47..767e1cf36510 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -73,25 +73,6 @@ #define OW_FOLLOWERS_ALLOWED_MET_LVL (0) #define OW_FOLLOWERS_ALLOWED_MET_LOC (0) -// Overworld Encounters -#define OW_VANILLA_RANDOM_ENCOUNTERS TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. -#define OW_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. -#define OW_WILD_ENCOUNTERS_BATTLE_PIKE TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pike, if FALSE random encounters will be enabled instead. Requires OW_WILD_ENCOUNTERS_OVERWORLD to be TRUE. -#define OW_WILD_ENCOUNTERS_BATTLE_PYRAMID TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pyramid, if FALSE random encounters will be enabled instead. Requires OW_WILD_ENCOUNTERS_OVERWORLD to be TRUE. -#define OW_WILD_ENCOUNTERS_RESTRICT_METATILE TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same encounter metatile behavior as the one it is currently on, if any. -#define OW_WILD_ENCOUNTERS_RESTRICT_MAP TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within their current map bounds. -#define OW_WILD_ENCOUNTERS_UNRESTRICT_SIGHT FALSE // If TRUE, OW Pokémon spawned as overworld wild encounters will ignore all movement restrictions when they can see the player. -#define OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. -#define OW_WILD_ENCOUNTERS_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. -#define OW_VANILLA_AMBIENT_CRIES TRUE // If TRUE, the ambient wild Pokémon cries will play on routes with encounter tables. -#define OW_WILD_ENCOUNTERS_AMBIENT_CRIES TRUE // If TRUE, generated overworld encounters will play ambient cries based on their location relative to the player. -#define OW_WILD_ENCOUNTERS_SHINY_SPARKLE FALSE // If TRUE, a shiny overworld wild encounter will spawn with a sparkle animation and play the shiny sound effect. -#define OW_WILD_ENCOUNTERS_FEEBAS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. -#define OW_WILD_ENCOUNTERS_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an overworld wild encounter despawns. -#define OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE TRUE // If TRUE, the mon will take steps to be right next to the player before the battle starts. -#define OW_WILD_ENCOUNTERS_PREVENT_SHINY_REPLACEMENT TRUE // If TRUE, shiny OW Pokémon objects will not be despawned if OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. -#define OW_WILD_ENCOUNTERS_PREVENT_FEEBAS_REPLACEMENT TRUE // If TRUE, Feebas OW objects spawned from special Feebas fishing spots (when OW_WILD_ENCOUNTERS_FEEBAS is TRUE) will not be despawned if OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. - // Out-of-battle Ability effects #define OW_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8+, if a Pokémon with Synchronize leads the party, wild Pokémon will always have their same Nature as opposed to the 50% chance in previous games. Gift Pokémon excluded. // In USUM (here GEN_7), if a Pokémon with Synchronize leads the party, gift Pokémon will always have their same Nature regardless of their Egg Group. diff --git a/include/config/overworld_encounters.h b/include/config/overworld_encounters.h new file mode 100644 index 000000000000..93948fd606a5 --- /dev/null +++ b/include/config/overworld_encounters.h @@ -0,0 +1,24 @@ +#ifndef GUARD_CONFIG_OVERWORLD_ENCOUNTERS_H +#define GUARD_CONFIG_OVERWORLD_ENCOUNTERS_H + +// If OWE_WILD_ENCOUNTERS_OVERWORLD is TRUE, it is recommended that OW_GFX_COMPRESS be set to FALSE to prevent VRAM issues. + +#define OWE_VANILLA_RANDOM_ENCOUNTERS TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. +#define OWE_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. +#define OWE_WILD_ENCOUNTERS_BATTLE_PIKE TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pike, if FALSE random encounters will be enabled instead. Requires OWE_WILD_ENCOUNTERS_OVERWORLD to be TRUE. +#define OWE_WILD_ENCOUNTERS_BATTLE_PYRAMID TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pyramid, if FALSE random encounters will be enabled instead. Requires OWE_WILD_ENCOUNTERS_OVERWORLD to be TRUE. +#define OWE_WILD_ENCOUNTERS_RESTRICT_METATILE TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same encounter metatile behavior as the one it is currently on, if any. +#define OWE_WILD_ENCOUNTERS_RESTRICT_MAP TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within their current map bounds. +#define OWE_WILD_ENCOUNTERS_UNRESTRICT_SIGHT FALSE // If TRUE, OW Pokémon spawned as overworld wild encounters will ignore all movement restrictions when they can see the player. +#define OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. +#define OWE_WILD_ENCOUNTERS_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. +#define OWE_VANILLA_AMBIENT_CRIES TRUE // If TRUE, the ambient wild Pokémon cries will play on routes with encounter tables. +#define OWE_WILD_ENCOUNTERS_AMBIENT_CRIES TRUE // If TRUE, generated overworld encounters will play ambient cries based on their location relative to the player. +#define OWE_WILD_ENCOUNTERS_SHINY_SPARKLE FALSE // If TRUE, a shiny overworld wild encounter will spawn with a sparkle animation and play the shiny sound effect. +#define OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. +#define OWE_WILD_ENCOUNTERS_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an overworld wild encounter despawns. +#define OWE_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE TRUE // If TRUE, the mon will take steps to be right next to the player before the battle starts. +#define OWE_WILD_ENCOUNTERS_PREVENT_SHINY_REPLACEMENT TRUE // If TRUE, shiny OW Pokémon objects will not be despawned if OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. +#define OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_REPLACEMENT TRUE // If TRUE, Feebas OW objects spawned from special Feebas fishing spots (when OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS is TRUE) will not be despawned if OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. + +#endif // GUARD_CONFIG_OVERWORLD_ENCOUNTERS_H diff --git a/include/constants/global.h b/include/constants/global.h index e0e9a46b06a6..65a0f5f09005 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -26,6 +26,7 @@ #include "config/general.h" #include "config/item.h" #include "config/overworld.h" +#include "config/overworld_encounters.h" #include "config/pokemon.h" #include "config/summary_screen.h" diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 71342bc0be7e..2205882f1fd7 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -1,8 +1,8 @@ #ifndef GUARD_OVERWORLD_ENCOUNTERS_H #define GUARD_OVERWORLD_ENCOUNTERS_H -#if OW_POKEMON_OBJECT_EVENTS == FALSE && OW_WILD_ENCOUNTERS_OVERWORLD == TRUE -#error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OW_WILD_ENCOUNTERS_OVERWORLD to work." +#if OW_POKEMON_OBJECT_EVENTS == FALSE && OWE_WILD_ENCOUNTERS_OVERWORLD == TRUE +#error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OWE_WILD_ENCOUNTERS_OVERWORLD to work." #endif #define OWE_MAX_SPAWN_SLOTS 5 @@ -19,7 +19,7 @@ #define OWE_SPAWN_TIME_MINIMUM 30 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. #define OWE_SPAWN_TIME_PER_ACTIVE 30 // The number of frames that will be added to the countdown per currently active spawn. -#define OWE_SPAWN_TIME_REPLACEMENT 240 // The number of frames before an existing spawn will be replaced with a new one (requires OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT). +#define OWE_SPAWN_TIME_REPLACEMENT 240 // The number of frames before an existing spawn will be replaced with a new one (requires OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT). #define OWE_MON_SIGHT_WIDTH 3 #define OWE_MON_SIGHT_LENGTH 4 diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 3f11dc762c12..dc54dc6aa7ee 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11889,7 +11889,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *o bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (OW_WILD_ENCOUNTERS_FLEE_DESPAWN && sCollisionTimer >= OWE_FLEE_COLLISION_TIME) + if (OWE_WILD_ENCOUNTERS_FLEE_DESPAWN && sCollisionTimer >= OWE_FLEE_COLLISION_TIME) { RemoveObjectEvent(objectEvent); return FALSE; diff --git a/src/overworld.c b/src/overworld.c index 2f132b7b0c77..f871245fd3a8 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1323,7 +1323,7 @@ void Overworld_FadeOutMapMusic(void) static void PlayAmbientCry(void) { - if (!OW_VANILLA_AMBIENT_CRIES) + if (!OWE_VANILLA_AMBIENT_CRIES) return; s16 x, y; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f88b4f395246..01d024ec7d0f 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -84,7 +84,7 @@ void OWE_ResetSpawnCounterPlayAmbientCry(void) { OverworldWildEncounter_SetMinimumSpawnTimer(); // Currently may not play manual encounter cries if no wild mon header exists - if (OW_WILD_ENCOUNTERS_AMBIENT_CRIES && GetNumberActiveOverworldEncounters(OWE_ANY)) + if (OWE_WILD_ENCOUNTERS_AMBIENT_CRIES && GetNumberActiveOverworldEncounters(OWE_ANY)) OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); } @@ -94,10 +94,10 @@ void UpdateOverworldEncounters(void) if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING) || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) return; - if (!OW_WILD_ENCOUNTERS_OVERWORLD + if (!OWE_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER) - || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !OW_WILD_ENCOUNTERS_BATTLE_PIKE) - || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR && !OW_WILD_ENCOUNTERS_BATTLE_PYRAMID) + || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !OWE_WILD_ENCOUNTERS_BATTLE_PIKE) + || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR && !OWE_WILD_ENCOUNTERS_BATTLE_PYRAMID) || InTrainerHillChallenge()) { if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) @@ -190,7 +190,7 @@ static void OWE_SetNewSpawnCountdown(void) { u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); - if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && numActive >= OWE_MAX_SPAWNS) + if (OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && numActive >= OWE_MAX_SPAWNS) sOWESpawnCountdown = OWE_SPAWN_TIME_REPLACEMENT; else sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); @@ -214,7 +214,7 @@ bool32 OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Object(u32 localId, u8 void OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Palette(void) { // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles - if (OW_WILD_ENCOUNTERS_OVERWORLD && CountFreePaletteSlots() < 2) + if (OWE_WILD_ENCOUNTERS_OVERWORLD && CountFreePaletteSlots() < 2) { u32 count = GetNumberActiveOverworldEncounters(OWE_GENERATED); @@ -320,7 +320,7 @@ static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animS if (!animSpawn && OWE_ShouldPlayMonFleeSound(objectEvent)) PlaySE(SE_FLEE); - if (OW_WILD_ENCOUNTERS_SHINY_SPARKLE && isShiny && animSpawn) + if (OWE_WILD_ENCOUNTERS_SHINY_SPARKLE && isShiny && animSpawn) { PlaySE(SE_SHINY); spawnAnimType = OWE_SPAWN_ANIM_SHINY; @@ -371,7 +371,7 @@ static u8 NextSpawnMonSlot(void) // All mon slots are in use if (GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_MAX_SPAWNS) { - if (OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) + if (OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) { // Cycle through so we remove the oldest mon first spawnSlot = GetOldestSlot(FALSE); @@ -397,6 +397,7 @@ static u8 NextSpawnMonSlot(void) static bool8 TrySelectTile(s16* outX, s16* outY) { +DebugPrintf("Try Select Tile"); u8 elevation; u16 tileBehavior; s16 playerX, playerY; @@ -755,7 +756,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 else *isFemale = FALSE; - if ((OW_WILD_ENCOUNTERS_PREVENT_SHINY_REPLACEMENT && *isShiny) || (OW_WILD_ENCOUNTERS_PREVENT_FEEBAS_REPLACEMENT && isFeebasSpot)) + if ((OWE_WILD_ENCOUNTERS_PREVENT_SHINY_REPLACEMENT && *isShiny) || (OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_REPLACEMENT && isFeebasSpot)) *level |= OWE_NO_REPLACE_FLAG; ZeroEnemyPartyMons(); @@ -830,7 +831,7 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam { *indexRoamerOutbreak = OWE_GetObjectRoamerStatusFromIndex(gEncounteredRoamerIndex); } - else if (OW_WILD_ENCOUNTERS_FEEBAS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) + else if (OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) { *level = ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING); *speciesId = gWildFeebas.species; @@ -991,7 +992,7 @@ u32 GetNewestOWEncounterLocalId(void) bool32 CanRemoveOverworldEncounter(u32 localId) { // Include a check for the encounter not being shiny or a roamer. - return (OW_WILD_ENCOUNTERS_OVERWORLD && GetNumberActiveOverworldEncounters(OWE_GENERATED) != 0 + return (OWE_WILD_ENCOUNTERS_OVERWORLD && GetNumberActiveOverworldEncounters(OWE_GENERATED) != 0 && (localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS + 1) || localId > LOCALID_OW_ENCOUNTER_END)); } @@ -1141,7 +1142,7 @@ bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Directi if (GetCollisionInDirection(objectEvent, direction)) return TRUE; - if (OWE_CanAwareMonSeePlayer(objectEvent) && OW_WILD_ENCOUNTERS_UNRESTRICT_SIGHT) + if (OWE_CanAwareMonSeePlayer(objectEvent) && OWE_WILD_ENCOUNTERS_UNRESTRICT_SIGHT) return FALSE; s32 xCurrent = objectEvent->currentCoords.x; @@ -1439,7 +1440,7 @@ static bool32 OWE_DoesRoamerObjectExist(void) static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew) { - if (!OW_WILD_ENCOUNTERS_RESTRICT_METATILE) + if (!OWE_WILD_ENCOUNTERS_RESTRICT_METATILE) return FALSE; u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); u32 metatileBehaviourNew = MapGridGetMetatileBehaviorAt(xNew, yNew); @@ -1466,7 +1467,7 @@ static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew) { - if (!OW_WILD_ENCOUNTERS_RESTRICT_MAP) + if (!OWE_WILD_ENCOUNTERS_RESTRICT_MAP) return FALSE; if (objectEvent->mapGroup == gSaveBlock1Ptr->location.mapGroup @@ -1487,7 +1488,7 @@ static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) if (OWE_ShouldDespawnGeneratedForNewOWE(objectEvent)) return FALSE; - return OW_WILD_ENCOUNTERS_DESPAWN_SOUND; + return OWE_WILD_ENCOUNTERS_DESPAWN_SOUND; } @@ -1515,11 +1516,11 @@ static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void) { - if ((gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !OW_WILD_ENCOUNTERS_BATTLE_PIKE) - || (CurrentBattlePyramidLocation() && !OW_WILD_ENCOUNTERS_BATTLE_PYRAMID)) + if ((gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !OWE_WILD_ENCOUNTERS_BATTLE_PIKE) + || (CurrentBattlePyramidLocation() && !OWE_WILD_ENCOUNTERS_BATTLE_PYRAMID)) return FALSE; - return !OW_VANILLA_RANDOM_ENCOUNTERS; + return !OWE_VANILLA_RANDOM_ENCOUNTERS; } static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) @@ -1527,7 +1528,7 @@ static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) if (!IsOverworldWildEncounter(object, OWE_GENERATED)) return FALSE; - return OW_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_MAX_SPAWNS; + return OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_MAX_SPAWNS; } void OWE_StartEncounter(struct ObjectEvent *mon) @@ -1628,7 +1629,7 @@ void OWE_ApproachForBattle(void) { u32 objectEventId = GetObjectEventIdByLocalId(gSpecialVar_LastTalked); struct ObjectEvent *objectEvent = &gObjectEvents[objectEventId]; - if (!OW_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE || !IsOverworldWildEncounter(objectEvent, OWE_ANY)) + if (!OWE_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE || !IsOverworldWildEncounter(objectEvent, OWE_ANY)) { FreezeObjectEvent(objectEvent); return; From 59ec42f6e83ca194ae43e7cd346978678befd7cd Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 28 Jan 2026 00:16:30 -0600 Subject: [PATCH 456/572] Remove debug line --- src/overworld_encounters.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 01d024ec7d0f..fd69743f09b8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -397,7 +397,6 @@ static u8 NextSpawnMonSlot(void) static bool8 TrySelectTile(s16* outX, s16* outY) { -DebugPrintf("Try Select Tile"); u8 elevation; u16 tileBehavior; s16 playerX, playerY; From ba182b6efb130639375c2772fe7d2cd018c3ad18 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 28 Jan 2026 13:21:54 -0600 Subject: [PATCH 457/572] Renamed TRAINER_TYPE_ENCOUNTER to TRAINER_TYPE_OW_WILD_ENCOUNTER --- docs/tutorials/how_to_overworld_wild_encounters.md | 4 ++-- include/constants/trainer_types.h | 2 +- src/overworld_encounters.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 7bb43a927a07..3300d0486816 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -6,14 +6,14 @@ Overworld Wild Encounters (OWEs), refer to the wild encounters that can be seen Generated OWEs are spawned automatically when `OWE_WILD_ENCOUNTERS_OVERWORLD` is set to `TRUE`, being spawned on a random encounter tile near the player, with the encounter table used dependant on it. These are considered low priority OWEs, and automatically populate a level, species, gender and shinyness exactly how a vanilla wild encounter would, or can even be a special spawn, but more on those later. ### Manual OWEs -Manual OWEs are created by the developer as any other object event would be and are defined by having the `.trainerType` set to `TRAINER_TYPE_ENCOUNTER`. These can be fully customised by the developer, with the level, species, gender and shinyness all able to be specified. The level can be set by filling the desired value in the `trainerRange_berryTreeId`. The latter three are specified by the `graphicsId` of the object, for example; +Manual OWEs are created by the developer as any other object event would be and are defined by having the `.trainerType` set to `TRAINER_TYPE_OW_WILD_ENCOUNTER`. These can be fully customised by the developer, with the level, species, gender and shinyness all able to be specified. The level can be set by filling the desired value in the `trainerRange_berryTreeId`. The latter three are specified by the `graphicsId` of the object, for example; - `OBJ_EVENT_GFX_SPECIES(BULBASAUR)` will produce a male, non-shiny Bulbasaur. - `OBJ_EVENT_GFX_SPECIES_SHINY(CHARMANDER)` will produce a male, shiny Charmander. - `OBJ_EVENT_GFX_SPECIES_FEMALE(SQUIRTLE)` will produce a female, non-shiny Squirtle. - `OBJ_EVENT_GFX_SPECIES_SHINY_FEMALE(PIKACHU)` will produce a female, shiny Pikachu. However Manual OWEs do not have to be defined fully, leaving any of the level, species, gender, shinyness or script zeroed will revert to default behaviours and any set parameters used. Leaving the level or species blank will take one the relevant encounter table. Leaving the shinyness blank will revert to default shiny odds, although this can still be affected by `P_FLAG_FORCE_SHINY` and `P_FLAG_FORCE_NO_SHINY`. Setting the `OBJ_EVENT_MON` bit of the `graphicsId`, but not the `OBJ_EVENT_MON_FEMALE` will result in a male encounter, setting both will result in a female encounter, as seen above, but setting neither will randomise the gender based on species. A species can be defined with a random gender by just using the species define. A specific script can be specified, but if not the default OWE encounter script will be used. -Assuming the following `graphicsId` have `.trainerType` set to `TRAINER_TYPE_ENCOUNTER`; +Assuming the following `graphicsId` have `.trainerType` set to `TRAINER_TYPE_OW_WILD_ENCOUNTER`; - `SPECIES_EEVEE` will result in an Eevee with a randomised level, gender and shinyness, using the default encounter script. - `OBJ_EVENT_GFX_SPECIES(NONE)` will result in a male randomised species of randomised level, gender and shinyness, using the default encounter script. - `OBJ_EVENT_GFX_SPECIES_SHINY_FEMALE(PIKACHU)` will result in a female, shiny randomised species with randomised level and gender, using the default encounter script. diff --git a/include/constants/trainer_types.h b/include/constants/trainer_types.h index 03c4e9ad231b..8d9a34b6cc89 100644 --- a/include/constants/trainer_types.h +++ b/include/constants/trainer_types.h @@ -5,6 +5,6 @@ #define TRAINER_TYPE_NORMAL 1 #define TRAINER_TYPE_SEE_ALL_DIRECTIONS 2 #define TRAINER_TYPE_BURIED 3 -#define TRAINER_TYPE_ENCOUNTER 4 +#define TRAINER_TYPE_OW_WILD_ENCOUNTER 255 #endif // GUARD_CONSTANTS_TRAINER_TYPES_H diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index fd69743f09b8..e4b871177413 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -160,7 +160,7 @@ void UpdateOverworldEncounters(void) .y = y - MAP_OFFSET, .elevation = MapGridGetElevationAt(x, y), .movementType = OWE_GetMovementTypeFromSpecies(speciesId), - .trainerType = TRAINER_TYPE_ENCOUNTER, + .trainerType = TRAINER_TYPE_OW_WILD_ENCOUNTER, .script = InteractWithDynamicWildOverworldEncounter, }; u32 objectEventId = GetObjectEventIdByLocalId(localId); @@ -931,7 +931,7 @@ static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType) { - bool32 isOWE = (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_ENCOUNTER); + bool32 isOWE = (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_OW_WILD_ENCOUNTER); switch (oweType) { default: @@ -1025,7 +1025,7 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) { - if (template->trainerType != TRAINER_TYPE_ENCOUNTER || (template->localId <= LOCALID_OW_ENCOUNTER_END + if (template->trainerType != TRAINER_TYPE_OW_WILD_ENCOUNTER || (template->localId <= LOCALID_OW_ENCOUNTER_END && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS))) return *template; From fba730eed481f8b24a086b3b176372e79304d170 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:07:17 -0600 Subject: [PATCH 458/572] Added doc to SUMMARY.md so the check will stop yelling at me --- docs/SUMMARY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 7d6447cabda0..4317c20fa861 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -45,6 +45,7 @@ - [Vs. Seeker](tutorials/vs_seeker.md) - [Teachable Learnsets Explanations](tutorials/teachable_learnsets.md) - [Struct Pokemon Generation](tutorials/mon_generation.md) + - [How to use Overworld Wild Encounters](tutorials/how_to_overworld_wild_encounters.md) - [Changelog](./CHANGELOG.md) - [1.14.x]() - [Version 1.14.2](changelogs/1.14.x/1.14.2.md) From dd3052631121db637b5a9ad5bef811e169aad1af Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:44:15 -0600 Subject: [PATCH 459/572] OWE_GetRandomActiveEncounterObject now includes manual OWEs --- src/overworld_encounters.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e4b871177413..81e01b036638 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1366,22 +1366,26 @@ enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavio static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) { - // Uses slots so needs to be generated encounters only. - // Or needs to change functionality to count all active encounters. u32 numActive = GetNumberActiveOverworldEncounters(OWE_ANY); u32 randomIndex; - struct ObjectEvent *slotMon; + u32 counter = 0; + struct ObjectEvent *object; if (numActive) randomIndex = Random() % numActive; else return NULL; - for (u32 i = 0; i < numActive; i++) + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { - slotMon = &gObjectEvents[i]; - if (IsOverworldWildEncounter(slotMon, OWE_ANY) && (i == randomIndex)) - return slotMon; + object = &gObjectEvents[i]; + if (IsOverworldWildEncounter(object, OWE_ANY)) + { + if (counter >= randomIndex) + return object; + else + counter++; + } } return NULL; } From 706b5efea29d7073db19a0a0e83a762ad03b6e05 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:48:39 +0000 Subject: [PATCH 460/572] Set Manual OWE Flags When Removed --- docs/tutorials/how_to_overworld_wild_encounters.md | 2 ++ src/overworld_encounters.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 9e7221fe8cf6..89745c6a389a 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -23,6 +23,8 @@ As level and species are potentially taken from the Wild Encounter Header, an `a No matter how much of a Manual OWE is defined, it is considered a high priority OWE, and treated as a regular object event in all ways other than ones outlined above. They will always spawn, regardless of level of abilties of player Pokémon, however, they have restricted special spawns types. +> Flags are set when removed. + ### Special Spawns Special spawns can be one of three types, in decreasing priority: A Roamer, Feebas, or Mass Outbreak Encounter. Generated OWEs can have any of these, however, Manual OWEs can only have the Feebas Special Spawn. These work exactly as they would normally; - If a Roamer is on the route and is able to spawn, then it may appear where a Generated OWE would. diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e4b871177413..3c08045c4e24 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -703,6 +703,9 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; + if (IsOverworldWildEncounter(objectEvent, OWE_MANUAL)) + FlagSet(GetObjectEventFlagIdByLocalIdAndMap(objectEvent->localId, objectEvent->mapNum, objectEvent->mapGroup)); + objectEvent->sOverworldEncounterLevel = 0; objectEvent->sAge = 0; objectEvent->sRoamerOutbreakStatus = 0; From a055753e2a7dc692a94f3a304c135e8484606830 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 28 Jan 2026 23:49:19 +0000 Subject: [PATCH 461/572] Disallow Cries if Outside View --- include/event_object_movement.h | 1 + src/event_object_movement.c | 15 +++++++++++---- src/overworld_encounters.c | 3 +++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index c1e9d761d21f..82ee9a3d46b8 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -345,6 +345,7 @@ u8 GetJump2MovementAction(u32); u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); u8 CreateCopySpriteAt(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); bool8 IsElevationMismatchAt(u8, s16, s16); +bool32 IsObjectEventOutsideView(struct ObjectEvent *objectEvent); u8 MovementType_WanderAround_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderAround_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index d2523a080d76..804df53bb277 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -2973,7 +2973,7 @@ void RemoveObjectEventsOutsideView(void) } } -static void RemoveObjectEventIfOutsideView(struct ObjectEvent *objectEvent) +bool32 IsObjectEventOutsideView(struct ObjectEvent *objectEvent) { s16 left = gSaveBlock1Ptr->pos.x - 2; s16 right = gSaveBlock1Ptr->pos.x + 17; @@ -2982,11 +2982,18 @@ static void RemoveObjectEventIfOutsideView(struct ObjectEvent *objectEvent) if (objectEvent->currentCoords.x >= left && objectEvent->currentCoords.x <= right && objectEvent->currentCoords.y >= top && objectEvent->currentCoords.y <= bottom) - return; + return FALSE; if (objectEvent->initialCoords.x >= left && objectEvent->initialCoords.x <= right && objectEvent->initialCoords.y >= top && objectEvent->initialCoords.y <= bottom) - return; - RemoveObjectEvent(objectEvent); + return FALSE; + + return TRUE; +} + +static void RemoveObjectEventIfOutsideView(struct ObjectEvent *objectEvent) +{ + if (IsObjectEventOutsideView(objectEvent)) + RemoveObjectEvent(objectEvent); } void SpawnObjectEventsOnReturnToField(s16 x, s16 y) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 3c08045c4e24..7d84e3657cee 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1490,6 +1490,9 @@ static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) if (OWE_ShouldDespawnGeneratedForNewOWE(objectEvent)) return FALSE; + if (IsObjectEventOutsideView(objectEvent)) + return FALSE; + return OWE_WILD_ENCOUNTERS_DESPAWN_SOUND; } From 77c8a8e0035055df64f8ca1c36d0211945e2166f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Wed, 28 Jan 2026 23:53:12 +0000 Subject: [PATCH 462/572] Remove isFeebasSpot --- src/overworld_encounters.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7d84e3657cee..71ab244ace5e 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -63,7 +63,7 @@ static bool32 OWE_DoesRoamerObjectExist(void); static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); static u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); -static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y, bool32 *isFeebasSpot); +static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); @@ -736,9 +736,8 @@ void OverworldWildEncounter_SetMinimumSpawnTimer(void) static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) { u32 personality; - bool32 isFeebasSpot = FALSE; - if (!OWE_CreateEnemyPartyMon(speciesId, level, indexRoamerOutbreak, x, y, &isFeebasSpot)) + if (!OWE_CreateEnemyPartyMon(speciesId, level, indexRoamerOutbreak, x, y)) { ZeroEnemyPartyMons(); *speciesId = SPECIES_NONE; @@ -758,13 +757,13 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 else *isFemale = FALSE; - if ((OWE_WILD_ENCOUNTERS_PREVENT_SHINY_REPLACEMENT && *isShiny) || (OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_REPLACEMENT && isFeebasSpot)) + if ((OWE_WILD_ENCOUNTERS_PREVENT_SHINY_REPLACEMENT && *isShiny)) *level |= OWE_NO_REPLACE_FLAG; ZeroEnemyPartyMons(); } -static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y, bool32 *isFeebasSpot) +static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) { const struct WildPokemonInfo *wildMonInfo; enum WildPokemonArea wildArea; @@ -836,8 +835,9 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam else if (OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) { *level = ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING); + if (OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_REPLACEMENT) + *level |= OWE_NO_REPLACE_FLAG; *speciesId = gWildFeebas.species; - *isFeebasSpot = TRUE; CreateWildMon(*speciesId, *level); } else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior) && *indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) From cd1200ff10f53fc5d1535b2d9999dca87a09a6f8 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 28 Jan 2026 19:07:09 -0600 Subject: [PATCH 463/572] Ambient cries now have their own timer --- include/overworld_encounters.h | 5 ++++- src/load_save.c | 1 + src/overworld_encounters.c | 41 +++++++++++++++++++++++----------- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 2205882f1fd7..06727a93ce17 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -21,6 +21,9 @@ #define OWE_SPAWN_TIME_PER_ACTIVE 30 // The number of frames that will be added to the countdown per currently active spawn. #define OWE_SPAWN_TIME_REPLACEMENT 240 // The number of frames before an existing spawn will be replaced with a new one (requires OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT). +#define OWE_AMBIENT_CRY_TIMER_MIN 300 // The minimum number of frames between OWE ambient cries. +#define OWE_AMBIENT_CRY_TIMER_MAX 1000 // The maximum number of frames between OWE ambient cries. + #define OWE_MON_SIGHT_WIDTH 3 #define OWE_MON_SIGHT_LENGTH 4 @@ -93,13 +96,13 @@ enum __attribute__((packed)) OverworldEncounterBehaviors extern const u8 InteractWithDynamicWildOverworldEncounter[]; -void OWE_ResetSpawnCounterPlayAmbientCry(void); void UpdateOverworldEncounters(void); u32 GetOldestSlot(bool32 forceRemove); void CreateOverworldWildEncounter(void); void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); +void OWE_ResetAmbientCryTimer(void); void OverworldWildEncounter_SetMinimumSpawnTimer(void); void RemoveAllGeneratedOverworldEncounterObjects(void); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); diff --git a/src/load_save.c b/src/load_save.c index ac900aa6e164..7ca0c592812d 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -221,6 +221,7 @@ void LoadObjectEvents(void) int i; u16 graphicsId; + OWE_ResetAmbientCryTimer(); OverworldWildEncounter_SetMinimumSpawnTimer(); for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 81e01b036638..1208b8741b25 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -40,6 +40,7 @@ #define OWE_MASS_OUTBREAK_INDEX ROAMER_COUNT + 1 #define OWE_INVALID_ROAMER_OUTBREAK OWE_MASS_OUTBREAK_INDEX + 1 +static EWRAM_DATA u16 sOWEAmbientCryTimer = 0; static EWRAM_DATA u8 sOWESpawnCountdown = 0; static bool8 TrySelectTile(s16* outX, s16* outY); @@ -80,18 +81,26 @@ static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Dire static void Task_OWE_ApproachForBattle(u8 taskId); static bool32 OWE_CheckSpecies(u32 speciesId); -void OWE_ResetSpawnCounterPlayAmbientCry(void) -{ - OverworldWildEncounter_SetMinimumSpawnTimer(); - // Currently may not play manual encounter cries if no wild mon header exists - if (OWE_WILD_ENCOUNTERS_AMBIENT_CRIES && GetNumberActiveOverworldEncounters(OWE_ANY)) - OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); -} - void UpdateOverworldEncounters(void) { bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); - if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING) || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) + if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING)) + return; + + if (OWE_WILD_ENCOUNTERS_AMBIENT_CRIES) + { + if (sOWEAmbientCryTimer <= 0) + { + OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); + OWE_ResetAmbientCryTimer(); + } + else + { + sOWEAmbientCryTimer--; + } + } + + if (!OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) return; if (!OWE_WILD_ENCOUNTERS_OVERWORLD @@ -132,7 +141,7 @@ void UpdateOverworldEncounters(void) || (shouldSpawnWaterMons && AreLegendariesInSootopolisPreventingEncounters()) || !TrySelectTile(&x, &y)) { - OWE_ResetSpawnCounterPlayAmbientCry(); + OverworldWildEncounter_SetMinimumSpawnTimer(); return; } @@ -149,7 +158,7 @@ void UpdateOverworldEncounters(void) || !IsAbilityAllowingEncounter(level) || !OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny, x, y)) { - OWE_ResetSpawnCounterPlayAmbientCry(); + OverworldWildEncounter_SetMinimumSpawnTimer(); return; } @@ -171,7 +180,7 @@ void UpdateOverworldEncounters(void) assertf(objectEventId < OBJECT_EVENTS_COUNT, "could not spawn generated overworld encounter. too many object events exist") { - OWE_ResetSpawnCounterPlayAmbientCry(); + OverworldWildEncounter_SetMinimumSpawnTimer(); return; } @@ -183,6 +192,7 @@ void UpdateOverworldEncounters(void) enum Direction directions[4]; memcpy(directions, gStandardDirections, sizeof directions); ObjectEventTurn(object, directions[Random() & 3]); + OWE_ResetAmbientCryTimer(); OWE_SetNewSpawnCountdown(); } @@ -725,6 +735,11 @@ u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, boo return graphicsId; } +void OWE_ResetAmbientCryTimer(void) +{ + sOWEAmbientCryTimer = OWE_AMBIENT_CRY_TIMER_MIN + (Random() % (OWE_AMBIENT_CRY_TIMER_MAX - OWE_AMBIENT_CRY_TIMER_MIN)); +} + void OverworldWildEncounter_SetMinimumSpawnTimer(void) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; @@ -1397,7 +1412,7 @@ static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) // TESTING: Setting this species can be used as a test to play a consistent sound to check how often the // code in UpdateOverworldEncounters runs, as OWE_GetRandomActiveEncounterObject cuurently returns - // the player object. + // NULL. if (objectEvent == NULL) return; From 1edfcbb3e5b721ceb0e3b3beaee7c73fd41d0483 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 28 Jan 2026 19:16:34 -0600 Subject: [PATCH 464/572] Removed extra added line --- include/constants/event_objects.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 0d2edb319637..69baa2d04a5a 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -447,7 +447,6 @@ #define OBJ_EVENT_GFX_VAR_E (OBJ_EVENT_GFX_VARS + 0xE) #define OBJ_EVENT_GFX_VAR_F (OBJ_EVENT_GFX_VARS + 0xF) - // Don't use (1u << 15) to avoid conflict with BLEND_IMMUNE_FLAG. #define OBJ_EVENT_MON (1u << 14) #define OBJ_EVENT_MON_SHINY (1u << 13) From 2ad29a5d68110f3828eed67d9820cb79f56807b1 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 28 Jan 2026 19:37:10 -0600 Subject: [PATCH 465/572] Removed left over DebugPrintf --- src/field_control_avatar.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index a3ed2387af25..13ebb0a0a794 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -402,7 +402,6 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; gSpecialVar_Facing = direction; - DebugPrintf("shouldrunowescript=%d", ShouldRunOverworldEncounterScript(objectEventId)); if (InTrainerHill() == TRUE) script = GetTrainerHillTrainerScript(); else if (PlayerHasFollowerNPC() && objectEventId == GetFollowerNPCObjectId()) From b6fc14bdedeab8bad3b4c86229549d0edf8ff396 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 28 Jan 2026 19:40:24 -0600 Subject: [PATCH 466/572] Added empty line between functions --- src/pokemon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pokemon.c b/src/pokemon.c index 78e4cec5477d..c43c9a330293 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1357,6 +1357,7 @@ bool32 ComputePlayerShinyOdds(u32 personality, u32 value) } return isShiny; } + void SetBoxMonIVs(struct BoxPokemon *mon, u8 fixedIV) { u32 i, value; From 91232cf9165d12fcee72159c009b189d37874008 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 14:39:38 +0000 Subject: [PATCH 467/572] Ambient Cries Refactor --- include/config/overworld.h | 11 +++++++++ include/config/overworld_encounters.h | 2 -- include/overworld_encounters.h | 6 ++--- src/load_save.c | 1 - src/overworld.c | 31 ++++++++++++++++++++++++- src/overworld_encounters.c | 33 ++++++--------------------- 6 files changed, 50 insertions(+), 34 deletions(-) diff --git a/include/config/overworld.h b/include/config/overworld.h index 767e1cf36510..3d49352b5f0e 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -148,4 +148,15 @@ // Trainer Rematches #define OW_REMATCH_BADGE_COUNT 5 // Number of badges necessary before the match call or vs seeker features allow rematches +// Ambient Cries +// Constants +#define OW_AMBIENT_CRIES_NONE 0 // Do not play ambient cries. +#define OW_AMBIENT_CRIES_VANILLA 1 // Play ambient cries taken from encounter tables, as in vanilla. +#define OW_AMBIENT_CRIES_OWE_PRIORITY 2 // Play ambient cries based on active Overworld Wild Encounters, reverting to vanilla cries if none are present. +#define OW_AMBIENT_CRIES_OWE_ONLY 3 // Play ambient cries based on active Overworld Wild Encounters only. + // Overworld Wild Encounters will play ambient cries based on their location relative to the player. + +// Configuration +#define OW_AMBIENT_CRIES OW_AMBIENT_CRIES_VANILLA // Selects how ambient cries are played, if at all. As in vanilla, no matter what is chosen, cries will not play if the player is not on a map with Land or Water encounter tables. + #endif // GUARD_CONFIG_OVERWORLD_H diff --git a/include/config/overworld_encounters.h b/include/config/overworld_encounters.h index 93948fd606a5..01318b1b7992 100644 --- a/include/config/overworld_encounters.h +++ b/include/config/overworld_encounters.h @@ -12,8 +12,6 @@ #define OWE_WILD_ENCOUNTERS_UNRESTRICT_SIGHT FALSE // If TRUE, OW Pokémon spawned as overworld wild encounters will ignore all movement restrictions when they can see the player. #define OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. #define OWE_WILD_ENCOUNTERS_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. -#define OWE_VANILLA_AMBIENT_CRIES TRUE // If TRUE, the ambient wild Pokémon cries will play on routes with encounter tables. -#define OWE_WILD_ENCOUNTERS_AMBIENT_CRIES TRUE // If TRUE, generated overworld encounters will play ambient cries based on their location relative to the player. #define OWE_WILD_ENCOUNTERS_SHINY_SPARKLE FALSE // If TRUE, a shiny overworld wild encounter will spawn with a sparkle animation and play the shiny sound effect. #define OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. #define OWE_WILD_ENCOUNTERS_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an overworld wild encounter despawns. diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 06727a93ce17..13114c83e700 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -21,9 +21,6 @@ #define OWE_SPAWN_TIME_PER_ACTIVE 30 // The number of frames that will be added to the countdown per currently active spawn. #define OWE_SPAWN_TIME_REPLACEMENT 240 // The number of frames before an existing spawn will be replaced with a new one (requires OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT). -#define OWE_AMBIENT_CRY_TIMER_MIN 300 // The minimum number of frames between OWE ambient cries. -#define OWE_AMBIENT_CRY_TIMER_MAX 1000 // The maximum number of frames between OWE ambient cries. - #define OWE_MON_SIGHT_WIDTH 3 #define OWE_MON_SIGHT_LENGTH 4 @@ -102,7 +99,6 @@ void CreateOverworldWildEncounter(void); void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); -void OWE_ResetAmbientCryTimer(void); void OverworldWildEncounter_SetMinimumSpawnTimer(void); void RemoveAllGeneratedOverworldEncounterObjects(void); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); @@ -131,5 +127,7 @@ void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sp void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent); void OWE_ClearSavedMovementState(struct ObjectEvent *objectEvent); u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed); +void OWE_PlayAmbientCry(void); +u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/load_save.c b/src/load_save.c index 7ca0c592812d..ac900aa6e164 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -221,7 +221,6 @@ void LoadObjectEvents(void) int i; u16 graphicsId; - OWE_ResetAmbientCryTimer(); OverworldWildEncounter_SetMinimumSpawnTimer(); for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { diff --git a/src/overworld.c b/src/overworld.c index 46a507e4b1d4..6b2896147bd3 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1395,9 +1395,38 @@ void Overworld_FadeOutMapMusic(void) FadeOutMapMusic(4); } +static bool32 ShouldPlayVanillaAmbientCry(void) +{ + bool32 owePlayed = FALSE; + + if (GetNumberActiveOverworldEncounters(OWE_ANY)) + { + switch (OW_AMBIENT_CRIES) + { + case OW_AMBIENT_CRIES_OWE_ONLY: + case OW_AMBIENT_CRIES_OWE_PRIORITY: + OWE_PlayAmbientCry(); + owePlayed = TRUE; + break; + } + } + + switch (OW_AMBIENT_CRIES) + { + case OW_AMBIENT_CRIES_VANILLA: + return TRUE; + + case OW_AMBIENT_CRIES_OWE_PRIORITY: + return !owePlayed; + + default: + return FALSE; + } +} + static void PlayAmbientCry(void) { - if (!OWE_VANILLA_AMBIENT_CRIES) + if (!ShouldPlayVanillaAmbientCry()) return; s16 x, y; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7d5b738afe9a..13fd6907028d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -40,7 +40,6 @@ #define OWE_MASS_OUTBREAK_INDEX ROAMER_COUNT + 1 #define OWE_INVALID_ROAMER_OUTBREAK OWE_MASS_OUTBREAK_INDEX + 1 -static EWRAM_DATA u16 sOWEAmbientCryTimer = 0; static EWRAM_DATA u8 sOWESpawnCountdown = 0; static bool8 TrySelectTile(s16* outX, s16* outY); @@ -63,7 +62,6 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_DoesRoamerObjectExist(void); static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); -static u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); @@ -84,23 +82,7 @@ static bool32 OWE_CheckSpecies(u32 speciesId); void UpdateOverworldEncounters(void) { bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); - if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING)) - return; - - if (OWE_WILD_ENCOUNTERS_AMBIENT_CRIES) - { - if (sOWEAmbientCryTimer <= 0) - { - OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); - OWE_ResetAmbientCryTimer(); - } - else - { - sOWEAmbientCryTimer--; - } - } - - if (!OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) + if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING) || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) return; if (!OWE_WILD_ENCOUNTERS_OVERWORLD @@ -192,7 +174,6 @@ void UpdateOverworldEncounters(void) enum Direction directions[4]; memcpy(directions, gStandardDirections, sizeof directions); ObjectEventTurn(object, directions[Random() & 3]); - OWE_ResetAmbientCryTimer(); OWE_SetNewSpawnCountdown(); } @@ -738,11 +719,6 @@ u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, boo return graphicsId; } -void OWE_ResetAmbientCryTimer(void) -{ - sOWEAmbientCryTimer = OWE_AMBIENT_CRY_TIMER_MIN + (Random() % (OWE_AMBIENT_CRY_TIMER_MAX - OWE_AMBIENT_CRY_TIMER_MIN)); -} - void OverworldWildEncounter_SetMinimumSpawnTimer(void) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; @@ -876,7 +852,7 @@ static bool8 IsSafeToSpawnObjectEvents(void) return (player->currentCoords.x == player->previousCoords.x && player->currentCoords.y == player->previousCoords.y); } -static u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType) +u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType) { u32 numActive = 0; for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) @@ -1761,6 +1737,11 @@ static bool32 OWE_CheckSpecies(u32 speciesId) && IsSpeciesEnabled(speciesId); } +void OWE_PlayAmbientCry(void) +{ + OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From d74c30018296970415b69f174f89dbbcdc64b6c9 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:22:02 +0000 Subject: [PATCH 468/572] Allow OWEs to work when BATTLE_PYRAMID_RANDOM_ENCOUNTERS is TRUE --- include/battle_pyramid.h | 2 +- src/battle_pyramid.c | 9 +++++---- src/overworld_encounters.c | 7 +++++-- src/wild_encounter.c | 4 ++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/battle_pyramid.h b/include/battle_pyramid.h index d55d8d2e9399..38815aecd5ac 100644 --- a/include/battle_pyramid.h +++ b/include/battle_pyramid.h @@ -7,7 +7,7 @@ void CallBattlePyramidFunction(void); u16 LocalIdToPyramidTrainerId(u8 localId); bool8 GetBattlePyramidTrainerFlag(u8 eventId); void MarkApproachingPyramidTrainersAsBattled(void); -void GenerateBattlePyramidWildMon(void); +void GenerateBattlePyramidWildMon(u32 species); u8 GetPyramidRunMultiplier(void); u8 CurrentBattlePyramidLocation(void); bool8 InBattlePyramid_(void); diff --git a/src/battle_pyramid.c b/src/battle_pyramid.c index f777be99cf35..6d90d30197b1 100644 --- a/src/battle_pyramid.c +++ b/src/battle_pyramid.c @@ -1391,7 +1391,7 @@ static bool32 CheckBattlePyramidEvoRequirement(u16 species, const u16 *evoItems, } extern u32 GetTotalBaseStat(u32 species); -void GenerateBattlePyramidWildMon(void) +void GenerateBattlePyramidWildMon(u32 species) { u8 name[POKEMON_NAME_LENGTH + 1]; int i, j; @@ -1399,7 +1399,7 @@ void GenerateBattlePyramidWildMon(void) u32 lvl = gSaveBlock2Ptr->frontier.lvlMode; u16 round = (gSaveBlock2Ptr->frontier.pyramidWinStreaks[lvl] / 7) % TOTAL_PYRAMID_ROUNDS; const struct BattlePyramidRequirement *reqs = &sBattlePyramidRequirementsByRound[round]; - u16 species; + bool32 shouldRandom = (species == SPECIES_NONE); u32 bstLim; u16 *moves = NULL; u16 *abilities = NULL; @@ -1419,7 +1419,8 @@ void GenerateBattlePyramidWildMon(void) while (1) { - species = Random() % NUM_SPECIES; + if (shouldRandom) + species = Random() % NUM_SPECIES; // check if base species if (GET_BASE_SPECIES_ID(species) != species) @@ -1563,7 +1564,7 @@ void GenerateBattlePyramidWildMon(void) CalculateMonStats(&gEnemyParty[0]); } #else -void GenerateBattlePyramidWildMon(void) +void GenerateBattlePyramidWildMon(u32 species) { u8 name[POKEMON_NAME_LENGTH + 1]; int i; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 13fd6907028d..7456304bf12b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -562,8 +562,11 @@ static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId) if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) { u32 id = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); + u32 species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); SetMonData(&gEnemyParty[0], MON_DATA_SPECIES, &id); - GenerateBattlePyramidWildMon(); + if (!BATTLE_PYRAMID_RANDOM_ENCOUNTERS) + species = SPECIES_NONE; + GenerateBattlePyramidWildMon(species); BattleSetup_StartWildBattle(); return TRUE; } @@ -783,7 +786,7 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam return FALSE; u32 id = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); - GenerateBattlePyramidWildMon(); + GenerateBattlePyramidWildMon(SPECIES_NONE); SetMonData(&gEnemyParty[0], MON_DATA_LEVEL, &id); return TRUE; } diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 46438cdcb12a..192bc8770642 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -681,7 +681,7 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) else if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE) != TRUE) return FALSE; - GenerateBattlePyramidWildMon(); + GenerateBattlePyramidWildMon(SPECIES_NONE); BattleSetup_StartWildBattle(); return TRUE; } @@ -858,7 +858,7 @@ bool8 SweetScentWildEncounter(void) if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) return FALSE; - GenerateBattlePyramidWildMon(); + GenerateBattlePyramidWildMon(SPECIES_NONE); BattleSetup_StartWildBattle(); return TRUE; } From 61b097057e6f801f3f8619edb4229f72181f0ddf Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:23:39 +0000 Subject: [PATCH 469/572] Remove OWE_MAX_SPAWN_SLOTS for OWE_MAX_SPAWNS --- include/constants/event_objects.h | 2 +- include/overworld_encounters.h | 2 -- src/overworld_encounters.c | 20 ++++++++++---------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 69baa2d04a5a..7ad94b217d98 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -501,7 +501,7 @@ #define LOCALID_NONE 0 #define LOCALID_CAMERA 127 #define LOCALID_BERRY_BLENDER_PLAYER_END 240 // This will use 5 (MAX_RFU_PLAYERS) IDs ending at 240, i.e. 236-240 -#define LOCALID_OW_ENCOUNTER_END 252 // This will use 5 (OWE_MAX_SPAWN_SLOTS) IDs ending at 252, i.e. 248-252 +#define LOCALID_OW_ENCOUNTER_END 252 // This will use 5 (OWE_MAX_SPAWNS) IDs ending at 252, i.e. 249-252 #define LOCALID_FOLLOWING_POKEMON 254 #define LOCALID_PLAYER 255 #define OBJ_EVENT_ID_FOLLOWER 0xFE diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 13114c83e700..8fc9f62b4893 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -5,8 +5,6 @@ #error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OWE_WILD_ENCOUNTERS_OVERWORLD to work." #endif -#define OWE_MAX_SPAWN_SLOTS 5 - #define OWE_MAX_SPAWNS 4 #define OWE_SPAWN_DISTANCE_LAND 1 // A spawn cannot happen within this many tiles of the player position. diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7456304bf12b..ec7d434fe939 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -329,7 +329,7 @@ u32 GetOldestSlot(bool32 forceRemove) struct ObjectEvent *slotMon, *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; u32 spawnSlot; - for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWN_SLOTS; spawnSlot++) + for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWNS; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; if (OW_SPECIES(slotMon) != SPECIES_NONE && (!(slotMon->sOverworldEncounterLevel & OWE_NO_REPLACE_FLAG) || forceRemove == TRUE)) @@ -339,10 +339,10 @@ u32 GetOldestSlot(bool32 forceRemove) } } - if (spawnSlot >= OWE_MAX_SPAWN_SLOTS) + if (spawnSlot >= OWE_MAX_SPAWNS) return INVALID_SPAWN_SLOT; - for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWN_SLOTS; spawnSlot++) + for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWNS; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; if (OW_SPECIES(slotMon) != SPECIES_NONE && (!(slotMon->sOverworldEncounterLevel & OWE_NO_REPLACE_FLAG) || forceRemove == TRUE)) @@ -636,13 +636,13 @@ struct AgeSort static void SortOWEMonAges(void) { struct ObjectEvent *slotMon; - struct AgeSort array[OWE_MAX_SPAWN_SLOTS]; + struct AgeSort array[OWE_MAX_SPAWNS]; struct AgeSort current; u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); u32 count = 0; s32 i, j; - for (i = 0; i < OWE_MAX_SPAWN_SLOTS; i++) + for (i = 0; i < OWE_MAX_SPAWNS; i++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; if (OW_SPECIES(slotMon) != SPECIES_NONE) @@ -937,11 +937,11 @@ bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldO case OWE_GENERATED: return isOWE && (objectEvent->localId <= LOCALID_OW_ENCOUNTER_END - && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS)); + && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWNS)); case OWE_MANUAL: return isOWE && (objectEvent->localId > LOCALID_OW_ENCOUNTER_END - || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS)); + || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWNS)); } } @@ -972,7 +972,7 @@ u32 GetNewestOWEncounterLocalId(void) struct ObjectEvent *newest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; u32 i; - for (i = 0; i < OWE_MAX_SPAWN_SLOTS; i++) + for (i = 0; i < OWE_MAX_SPAWNS; i++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; if (OW_SPECIES(slotMon) != SPECIES_NONE) @@ -989,7 +989,7 @@ bool32 CanRemoveOverworldEncounter(u32 localId) { // Include a check for the encounter not being shiny or a roamer. return (OWE_WILD_ENCOUNTERS_OVERWORLD && GetNumberActiveOverworldEncounters(OWE_GENERATED) != 0 - && (localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS + 1) + && (localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWNS + 1) || localId > LOCALID_OW_ENCOUNTER_END)); } @@ -1023,7 +1023,7 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) { if (template->trainerType != TRAINER_TYPE_OW_WILD_ENCOUNTER || (template->localId <= LOCALID_OW_ENCOUNTER_END - && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWN_SLOTS))) + && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWNS))) return *template; struct ObjectEventTemplate templateOWE = *template; From 60ec6afd5a8887eefdfb4a07b734420dfdbdaee9 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:26:51 +0000 Subject: [PATCH 470/572] Revert "Disallow Cries if Outside View" This reverts commit a055753e2a7dc692a94f3a304c135e8484606830. --- include/event_object_movement.h | 1 - src/event_object_movement.c | 15 ++++----------- src/overworld_encounters.c | 3 --- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 82ee9a3d46b8..c1e9d761d21f 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -345,7 +345,6 @@ u8 GetJump2MovementAction(u32); u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); u8 CreateCopySpriteAt(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); bool8 IsElevationMismatchAt(u8, s16, s16); -bool32 IsObjectEventOutsideView(struct ObjectEvent *objectEvent); u8 MovementType_WanderAround_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_WanderAround_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 804df53bb277..d2523a080d76 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -2973,7 +2973,7 @@ void RemoveObjectEventsOutsideView(void) } } -bool32 IsObjectEventOutsideView(struct ObjectEvent *objectEvent) +static void RemoveObjectEventIfOutsideView(struct ObjectEvent *objectEvent) { s16 left = gSaveBlock1Ptr->pos.x - 2; s16 right = gSaveBlock1Ptr->pos.x + 17; @@ -2982,18 +2982,11 @@ bool32 IsObjectEventOutsideView(struct ObjectEvent *objectEvent) if (objectEvent->currentCoords.x >= left && objectEvent->currentCoords.x <= right && objectEvent->currentCoords.y >= top && objectEvent->currentCoords.y <= bottom) - return FALSE; + return; if (objectEvent->initialCoords.x >= left && objectEvent->initialCoords.x <= right && objectEvent->initialCoords.y >= top && objectEvent->initialCoords.y <= bottom) - return FALSE; - - return TRUE; -} - -static void RemoveObjectEventIfOutsideView(struct ObjectEvent *objectEvent) -{ - if (IsObjectEventOutsideView(objectEvent)) - RemoveObjectEvent(objectEvent); + return; + RemoveObjectEvent(objectEvent); } void SpawnObjectEventsOnReturnToField(s16 x, s16 y) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index ec7d434fe939..b7185b9a67ff 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1488,9 +1488,6 @@ static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) if (OWE_ShouldDespawnGeneratedForNewOWE(objectEvent)) return FALSE; - if (IsObjectEventOutsideView(objectEvent)) - return FALSE; - return OWE_WILD_ENCOUNTERS_DESPAWN_SOUND; } From cf962b6108013cf9c6930c6b9eb437508225ecd4 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:30:34 +0000 Subject: [PATCH 471/572] Reimplement Disallow Cries if Outside View --- src/event_object_movement.c | 3 +++ src/overworld_encounters.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index d2523a080d76..362f41dda772 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -2986,6 +2986,9 @@ static void RemoveObjectEventIfOutsideView(struct ObjectEvent *objectEvent) if (objectEvent->initialCoords.x >= left && objectEvent->initialCoords.x <= right && objectEvent->initialCoords.y >= top && objectEvent->initialCoords.y <= bottom) return; + + if (IsOverworldWildEncounter(objectEvent, OWE_ANY)) + objectEvent->offScreen = TRUE; RemoveObjectEvent(objectEvent); } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index b7185b9a67ff..653a4c57a6f3 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1488,6 +1488,9 @@ static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) if (OWE_ShouldDespawnGeneratedForNewOWE(objectEvent)) return FALSE; + if (objectEvent->offScreen) + return FALSE; + return OWE_WILD_ENCOUNTERS_DESPAWN_SOUND; } From c20051d4d61603337908066077c1202753af8838 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 21:57:19 +0000 Subject: [PATCH 472/572] More Roamer Safety --- src/overworld_encounters.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 653a4c57a6f3..67269666417d 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1111,7 +1111,9 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c return; struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; - if (wildMon->sRoamerOutbreakStatus && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), + if (wildMon->sRoamerOutbreakStatus + && wildMon->sRoamerOutbreakStatus < OWE_MASS_OUTBREAK_INDEX + && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) { RemoveObjectEvent(wildMon); From 157409dd74b38466b9dd31ed8ce4cd7b7e1e1a15 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 22:04:52 +0000 Subject: [PATCH 473/572] Move Roamer data into warpArrowSpriteId --- src/overworld_encounters.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 67269666417d..5acb0226b668 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -34,8 +34,7 @@ #define sOverworldEncounterLevel trainerRange_berryTreeId #define sAge playerCopyableMovement -#define sRoamerOutbreakStatus directionSequenceIndex -#define sSavedMovementState warpArrowSpriteId +#define sRoamerOutbreakStatus warpArrowSpriteId #define OWE_NON_ROAMER_OUTBREAK 0 #define OWE_MASS_OUTBREAK_INDEX ROAMER_COUNT + 1 #define OWE_INVALID_ROAMER_OUTBREAK OWE_MASS_OUTBREAK_INDEX + 1 @@ -488,7 +487,7 @@ void CreateOverworldWildEncounter(void) u32 objEventId = GetObjectEventIdByLocalId(localId); u32 headerId = GetCurrentMapWildMonHeaderId(); struct ObjectEvent *object = &gObjectEvents[objEventId]; - u32 indexRoamerOutbreak = object->sRoamerOutbreakStatus; + u32 indexRoamerOutbreak = object->sRoamerOutbreakStatus &~ OWE_SAVED_MOVEMENT_STATE_FLAG; assertf(objEventId < OBJECT_EVENTS_COUNT && IsOverworldWildEncounter(object, OWE_ANY), "cannot start overworld wild enocunter as the selected object is invalid.\nlocalId: %d", localId) { @@ -1111,8 +1110,9 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c return; struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; - if (wildMon->sRoamerOutbreakStatus - && wildMon->sRoamerOutbreakStatus < OWE_MASS_OUTBREAK_INDEX + u32 indexRoamerOutbreak = wildMon->sRoamerOutbreakStatus &~ OWE_SAVED_MOVEMENT_STATE_FLAG; + if (indexRoamerOutbreak + && indexRoamerOutbreak < OWE_MASS_OUTBREAK_INDEX && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) { @@ -1510,7 +1510,7 @@ static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return OWE_INVALID_ROAMER_OUTBREAK; - u32 status = objectEvent->sRoamerOutbreakStatus; + u32 status = objectEvent->sRoamerOutbreakStatus &~ OWE_SAVED_MOVEMENT_STATE_FLAG; if (status == OWE_NON_ROAMER_OUTBREAK || status == OWE_MASS_OUTBREAK_INDEX) { return OWE_INVALID_ROAMER_OUTBREAK; @@ -1595,19 +1595,19 @@ struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnA #define sTypeFuncId data[1] // Same as in src/event_object_movement.c void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (IsOverworldWildEncounter(objectEvent, OWE_ANY) && objectEvent->sSavedMovementState & OWE_SAVED_MOVEMENT_STATE_FLAG) + if (IsOverworldWildEncounter(objectEvent, OWE_ANY) && objectEvent->sRoamerOutbreakStatus & OWE_SAVED_MOVEMENT_STATE_FLAG) sprite->sTypeFuncId = OWE_RESTORED_MOVEMENT_FUNC_ID; } #undef sTypeFuncId void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent) { - objectEvent->sSavedMovementState |= OWE_SAVED_MOVEMENT_STATE_FLAG; + objectEvent->sRoamerOutbreakStatus |= OWE_SAVED_MOVEMENT_STATE_FLAG; } void OWE_ClearSavedMovementState(struct ObjectEvent *objectEvent) { - objectEvent->sSavedMovementState ^= OWE_SAVED_MOVEMENT_STATE_FLAG; + objectEvent->sRoamerOutbreakStatus &= ~OWE_SAVED_MOVEMENT_STATE_FLAG; } static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance) @@ -1750,4 +1750,3 @@ void OWE_PlayAmbientCry(void) #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus -#undef sSavedMovementState From 1b5b0d6432ff84db061988ed549600cd8d7dc5a2 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 29 Jan 2026 16:19:49 -0600 Subject: [PATCH 474/572] Mons with OWE_NO_DESPAWN_FLAG now persist off screen --- include/config/overworld_encounters.h | 4 ++-- include/overworld_encounters.h | 3 ++- src/event_object_movement.c | 6 ++++++ src/overworld_encounters.c | 22 +++++++++++++++------- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/include/config/overworld_encounters.h b/include/config/overworld_encounters.h index 01318b1b7992..b24f03aa1cec 100644 --- a/include/config/overworld_encounters.h +++ b/include/config/overworld_encounters.h @@ -16,7 +16,7 @@ #define OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. #define OWE_WILD_ENCOUNTERS_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an overworld wild encounter despawns. #define OWE_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE TRUE // If TRUE, the mon will take steps to be right next to the player before the battle starts. -#define OWE_WILD_ENCOUNTERS_PREVENT_SHINY_REPLACEMENT TRUE // If TRUE, shiny OW Pokémon objects will not be despawned if OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. -#define OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_REPLACEMENT TRUE // If TRUE, Feebas OW objects spawned from special Feebas fishing spots (when OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS is TRUE) will not be despawned if OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. +#define OWE_WILD_ENCOUNTERS_PREVENT_SHINY_DESPAWN FALSE // If TRUE, shiny OW Pokémon objects will not be despawned when off-screen if on the same map as the player, or be replaced if OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. +#define OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_DESPAWN FALSE // If TRUE, Feebas OW objects spawned from special Feebas fishing spots (when OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS is TRUE) will not be despawned when off-screen if on the same map as the player, or be replaced if OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. #endif // GUARD_CONFIG_OVERWORLD_ENCOUNTERS_H diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 8fc9f62b4893..6ef13bfcade0 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -36,7 +36,7 @@ #define INVALID_SPAWN_SLOT 0xFF -#define OWE_NO_REPLACE_FLAG 0x80 +#define OWE_NO_DESPAWN_FLAG 0x80 #define OWE_SAVED_MOVEMENT_STATE_FLAG 0x80 #define OWE_RESTORED_MOVEMENT_FUNC_ID 10 @@ -127,5 +127,6 @@ void OWE_ClearSavedMovementState(struct ObjectEvent *objectEvent); u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed); void OWE_PlayAmbientCry(void); u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); +bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 362f41dda772..2032d237cb6b 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -2988,7 +2988,13 @@ static void RemoveObjectEventIfOutsideView(struct ObjectEvent *objectEvent) return; if (IsOverworldWildEncounter(objectEvent, OWE_ANY)) + { + if (OWE_IsMonRemovalExempt(objectEvent) && AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) + return; + objectEvent->offScreen = TRUE; + } + RemoveObjectEvent(objectEvent); } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 653a4c57a6f3..5a59df11b985 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -332,7 +332,7 @@ u32 GetOldestSlot(bool32 forceRemove) for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWNS; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE && (!(slotMon->sOverworldEncounterLevel & OWE_NO_REPLACE_FLAG) || forceRemove == TRUE)) + if (OW_SPECIES(slotMon) != SPECIES_NONE && (!(slotMon->sOverworldEncounterLevel & OWE_NO_DESPAWN_FLAG) || forceRemove == TRUE)) { oldest = slotMon; break; @@ -345,7 +345,7 @@ u32 GetOldestSlot(bool32 forceRemove) for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWNS; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE && (!(slotMon->sOverworldEncounterLevel & OWE_NO_REPLACE_FLAG) || forceRemove == TRUE)) + if (OW_SPECIES(slotMon) != SPECIES_NONE && (!(slotMon->sOverworldEncounterLevel & OWE_NO_DESPAWN_FLAG) || forceRemove == TRUE)) { if (slotMon->sAge > oldest->sAge) oldest = slotMon; @@ -503,7 +503,7 @@ void CreateOverworldWildEncounter(void) u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; u32 gender = OW_FEMALE(object) ? MON_FEMALE : MON_MALE; - u32 level = (object->sOverworldEncounterLevel &= ~OWE_NO_REPLACE_FLAG); + u32 level = (object->sOverworldEncounterLevel &= ~OWE_NO_DESPAWN_FLAG); u32 personality; switch (gSpeciesInfo[speciesId].genderRatio) @@ -751,8 +751,8 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 else *isFemale = FALSE; - if ((OWE_WILD_ENCOUNTERS_PREVENT_SHINY_REPLACEMENT && *isShiny)) - *level |= OWE_NO_REPLACE_FLAG; + if ((OWE_WILD_ENCOUNTERS_PREVENT_SHINY_DESPAWN && *isShiny)) + *level |= OWE_NO_DESPAWN_FLAG; ZeroEnemyPartyMons(); } @@ -829,8 +829,8 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam else if (OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) { *level = ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING); - if (OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_REPLACEMENT) - *level |= OWE_NO_REPLACE_FLAG; + if (OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_DESPAWN) + *level |= OWE_NO_DESPAWN_FLAG; *speciesId = gWildFeebas.species; CreateWildMon(*speciesId, *level); } @@ -1745,6 +1745,14 @@ void OWE_PlayAmbientCry(void) OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); } +bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent) +{ + if (objectEvent->sOverworldEncounterLevel & OWE_NO_DESPAWN_FLAG) + return TRUE; + + return FALSE; +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From 9e91660a75191e82451adb9361fa6b1be446823c Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 29 Jan 2026 16:27:04 -0600 Subject: [PATCH 475/572] Don't sort if only 1 OWE --- src/overworld_encounters.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5a59df11b985..27102a5aa6e8 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -642,6 +642,9 @@ static void SortOWEMonAges(void) u32 count = 0; s32 i, j; + if (OWE_MAX_SPAWNS <= 1) + return; + for (i = 0; i < OWE_MAX_SPAWNS; i++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; From 21e7aa70e92573e078a2ee64ce87c9f95298d977 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 22:42:24 +0000 Subject: [PATCH 476/572] Add Static Inlines --- include/overworld_encounters.h | 3 -- src/overworld_encounters.c | 93 +++++++++++++++++++++++----------- 2 files changed, 63 insertions(+), 33 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 8fc9f62b4893..8978966692f6 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -36,9 +36,6 @@ #define INVALID_SPAWN_SLOT 0xFF -#define OWE_NO_REPLACE_FLAG 0x80 - -#define OWE_SAVED_MOVEMENT_STATE_FLAG 0x80 #define OWE_RESTORED_MOVEMENT_FUNC_ID 10 enum OverworldEncounterSpawnAnim diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5acb0226b668..9d6ddbef225e 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -32,14 +32,57 @@ #include "constants/vars.h" #include "constants/wild_encounter.h" -#define sOverworldEncounterLevel trainerRange_berryTreeId -#define sAge playerCopyableMovement -#define sRoamerOutbreakStatus warpArrowSpriteId -#define OWE_NON_ROAMER_OUTBREAK 0 -#define OWE_MASS_OUTBREAK_INDEX ROAMER_COUNT + 1 -#define OWE_INVALID_ROAMER_OUTBREAK OWE_MASS_OUTBREAK_INDEX + 1 -static EWRAM_DATA u8 sOWESpawnCountdown = 0; +#define sOverworldEncounterLevel trainerRange_berryTreeId +#define sAge playerCopyableMovement +#define sRoamerOutbreakStatus warpArrowSpriteId +#define OWE_NON_ROAMER_OUTBREAK 0 +#define OWE_MASS_OUTBREAK_INDEX ROAMER_COUNT + 1 +#define OWE_INVALID_ROAMER_OUTBREAK OWE_MASS_OUTBREAK_INDEX + 1 + +#define OWE_FLAG_BIT (1 << 7) +#define OWE_SAVED_MOVEMENT_STATE_FLAG OWE_FLAG_BIT +#define OWE_NO_REPLACE_FLAG OWE_FLAG_BIT + +static inline u32 OWE_GetRoamerIndex(const struct ObjectEvent *object) +{ + return object->sRoamerOutbreakStatus & ~OWE_SAVED_MOVEMENT_STATE_FLAG; +} + +static inline bool32 OWE_HasSavedMovementState(const struct ObjectEvent *object) +{ + return object->sRoamerOutbreakStatus & OWE_SAVED_MOVEMENT_STATE_FLAG; +} + +void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent) +{ + objectEvent->sRoamerOutbreakStatus |= OWE_SAVED_MOVEMENT_STATE_FLAG; +} + +void OWE_ClearSavedMovementState(struct ObjectEvent *objectEvent) +{ + objectEvent->sRoamerOutbreakStatus &= ~OWE_SAVED_MOVEMENT_STATE_FLAG; +} + +static inline u32 OWE_GetEncounterLevel(const struct ObjectEvent *object) +{ + return object->sOverworldEncounterLevel & ~OWE_NO_REPLACE_FLAG; +} + +static inline void OWE_SetEncounterLevel(u32 *level, u8 newLevel) +{ + *level = (*level & OWE_NO_REPLACE_FLAG) | (newLevel & ~OWE_NO_REPLACE_FLAG); +} + +static inline bool32 OWE_HasNoReplaceFlag(const struct ObjectEvent *object) +{ + return object->sOverworldEncounterLevel & OWE_NO_REPLACE_FLAG; +} + +static inline void OWE_SetNoReplaceFlag(u32 *level) +{ + *level |= OWE_NO_REPLACE_FLAG; +} static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); @@ -78,6 +121,8 @@ static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Dire static void Task_OWE_ApproachForBattle(u8 taskId); static bool32 OWE_CheckSpecies(u32 speciesId); +static EWRAM_DATA u8 sOWESpawnCountdown = 0; + void UpdateOverworldEncounters(void) { bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); @@ -331,7 +376,7 @@ u32 GetOldestSlot(bool32 forceRemove) for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWNS; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE && (!(slotMon->sOverworldEncounterLevel & OWE_NO_REPLACE_FLAG) || forceRemove == TRUE)) + if (OW_SPECIES(slotMon) != SPECIES_NONE && (!OWE_HasNoReplaceFlag(slotMon) || forceRemove == TRUE)) { oldest = slotMon; break; @@ -344,7 +389,7 @@ u32 GetOldestSlot(bool32 forceRemove) for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWNS; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE && (!(slotMon->sOverworldEncounterLevel & OWE_NO_REPLACE_FLAG) || forceRemove == TRUE)) + if (OW_SPECIES(slotMon) != SPECIES_NONE && (!OWE_HasNoReplaceFlag(slotMon) || forceRemove == TRUE)) { if (slotMon->sAge > oldest->sAge) oldest = slotMon; @@ -487,7 +532,7 @@ void CreateOverworldWildEncounter(void) u32 objEventId = GetObjectEventIdByLocalId(localId); u32 headerId = GetCurrentMapWildMonHeaderId(); struct ObjectEvent *object = &gObjectEvents[objEventId]; - u32 indexRoamerOutbreak = object->sRoamerOutbreakStatus &~ OWE_SAVED_MOVEMENT_STATE_FLAG; + u32 indexRoamerOutbreak = OWE_GetRoamerIndex(object); assertf(objEventId < OBJECT_EVENTS_COUNT && IsOverworldWildEncounter(object, OWE_ANY), "cannot start overworld wild enocunter as the selected object is invalid.\nlocalId: %d", localId) { @@ -502,7 +547,7 @@ void CreateOverworldWildEncounter(void) u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; u32 gender = OW_FEMALE(object) ? MON_FEMALE : MON_MALE; - u32 level = (object->sOverworldEncounterLevel &= ~OWE_NO_REPLACE_FLAG); + u32 level = OWE_GetEncounterLevel(object); u32 personality; switch (gSpeciesInfo[speciesId].genderRatio) @@ -738,7 +783,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 } *speciesId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); - *level = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); + OWE_SetEncounterLevel(level, GetMonData(&gEnemyParty[0], MON_DATA_LEVEL)); personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY); if (*speciesId == SPECIES_UNOWN) @@ -751,7 +796,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 *isFemale = FALSE; if ((OWE_WILD_ENCOUNTERS_PREVENT_SHINY_REPLACEMENT && *isShiny)) - *level |= OWE_NO_REPLACE_FLAG; + OWE_SetNoReplaceFlag(level); ZeroEnemyPartyMons(); } @@ -827,11 +872,9 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam } else if (OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) { - *level = ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING); + CreateWildMon(gWildFeebas.species, ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING)); if (OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_REPLACEMENT) - *level |= OWE_NO_REPLACE_FLAG; - *speciesId = gWildFeebas.species; - CreateWildMon(*speciesId, *level); + OWE_SetNoReplaceFlag(level); } else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior) && *indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) { @@ -1110,7 +1153,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c return; struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; - u32 indexRoamerOutbreak = wildMon->sRoamerOutbreakStatus &~ OWE_SAVED_MOVEMENT_STATE_FLAG; + u32 indexRoamerOutbreak = OWE_GetRoamerIndex(wildMon); if (indexRoamerOutbreak && indexRoamerOutbreak < OWE_MASS_OUTBREAK_INDEX && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), @@ -1510,7 +1553,7 @@ static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return OWE_INVALID_ROAMER_OUTBREAK; - u32 status = objectEvent->sRoamerOutbreakStatus &~ OWE_SAVED_MOVEMENT_STATE_FLAG; + u32 status = OWE_GetRoamerIndex(objectEvent); if (status == OWE_NON_ROAMER_OUTBREAK || status == OWE_MASS_OUTBREAK_INDEX) { return OWE_INVALID_ROAMER_OUTBREAK; @@ -1595,21 +1638,11 @@ struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnA #define sTypeFuncId data[1] // Same as in src/event_object_movement.c void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (IsOverworldWildEncounter(objectEvent, OWE_ANY) && objectEvent->sRoamerOutbreakStatus & OWE_SAVED_MOVEMENT_STATE_FLAG) + if (IsOverworldWildEncounter(objectEvent, OWE_ANY) && OWE_HasSavedMovementState(objectEvent)) sprite->sTypeFuncId = OWE_RESTORED_MOVEMENT_FUNC_ID; } #undef sTypeFuncId -void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent) -{ - objectEvent->sRoamerOutbreakStatus |= OWE_SAVED_MOVEMENT_STATE_FLAG; -} - -void OWE_ClearSavedMovementState(struct ObjectEvent *objectEvent) -{ - objectEvent->sRoamerOutbreakStatus &= ~OWE_SAVED_MOVEMENT_STATE_FLAG; -} - static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance) { s16 x = player->currentCoords.x; From 7519e164a62ff897133cc8f25b903b0f0f4d6ade Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 22:59:43 +0000 Subject: [PATCH 477/572] Consolidate OWE_IsMonRemovalExempt --- src/event_object_movement.c | 9 ++------- src/overworld_encounters.c | 6 +++++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 2032d237cb6b..970a44a905ea 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -2987,13 +2987,8 @@ static void RemoveObjectEventIfOutsideView(struct ObjectEvent *objectEvent) && objectEvent->initialCoords.y >= top && objectEvent->initialCoords.y <= bottom) return; - if (IsOverworldWildEncounter(objectEvent, OWE_ANY)) - { - if (OWE_IsMonRemovalExempt(objectEvent) && AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) - return; - - objectEvent->offScreen = TRUE; - } + if (OWE_IsMonRemovalExempt(objectEvent)) + return; RemoveObjectEvent(objectEvent); } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f45c2e69a821..3d940c2e0fef 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1785,9 +1785,13 @@ void OWE_PlayAmbientCry(void) bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent) { - if (objectEvent->sOverworldEncounterLevel & OWE_NO_DESPAWN_FLAG) + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + return FALSE; + + if (OWE_HasNoDespawnFlag(objectEvent) && AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) return TRUE; + objectEvent->offScreen = TRUE; return FALSE; } From 737072f2a56ea8cbc71a5a236c330e8a3f359a74 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 23:04:36 +0000 Subject: [PATCH 478/572] Add Types to RemoveAllGeneratedOverworldEncounterObjects --- include/overworld_encounters.h | 2 +- src/dexnav.c | 2 +- src/overworld_encounters.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index d636843380c6..30061a4d8151 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -95,7 +95,7 @@ void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); void OverworldWildEncounter_SetMinimumSpawnTimer(void); -void RemoveAllGeneratedOverworldEncounterObjects(void); +void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); diff --git a/src/dexnav.c b/src/dexnav.c index e2c18fa49b0e..656a99115fef 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -745,7 +745,7 @@ static bool8 TryStartHiddenMonFieldEffect(enum EncounterType environment, u8 xSi if (fldEffId != 0) { - RemoveAllGeneratedOverworldEncounterObjects(); + RemoveAllOverworldWildEncounterObjects(OWE_GENERATED); gFieldEffectArguments[0] = sDexNavSearchDataPtr->tileX; gFieldEffectArguments[1] = sDexNavSearchDataPtr->tileY; gFieldEffectArguments[2] = 0xFF; // subpriority diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 3d940c2e0fef..f358dbdef054 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -137,7 +137,7 @@ void UpdateOverworldEncounters(void) { if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) { - RemoveAllGeneratedOverworldEncounterObjects(); + RemoveAllOverworldWildEncounterObjects(OWE_GENERATED); sOWESpawnCountdown = OWE_NO_ENCOUNTER_SET; } return; @@ -917,12 +917,12 @@ static bool32 OWE_ShouldSpawnWaterMons(void) return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); } -void RemoveAllGeneratedOverworldEncounterObjects(void) +void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType) { for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { struct ObjectEvent *obj = &gObjectEvents[i]; - if (IsOverworldWildEncounter(obj, OWE_GENERATED) && obj->active) + if (IsOverworldWildEncounter(obj, oweType) && obj->active) RemoveObjectEvent(obj); } } From ca2d4de0ff7b528bf9a926e9292021ff61a7171e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 23:21:29 +0000 Subject: [PATCH 479/572] Fix Level Assertf Going Off --- src/overworld_encounters.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index f358dbdef054..a849ddbec963 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -64,9 +64,9 @@ void OWE_ClearSavedMovementState(struct ObjectEvent *objectEvent) objectEvent->sRoamerOutbreakStatus &= ~OWE_SAVED_MOVEMENT_STATE_FLAG; } -static inline u32 OWE_GetEncounterLevel(const struct ObjectEvent *object) +static inline u32 OWE_GetEncounterLevel(u32 level) { - return object->sOverworldEncounterLevel & ~OWE_NO_DESPAWN_FLAG; + return level & ~OWE_NO_DESPAWN_FLAG; } static inline void OWE_SetEncounterLevel(u32 *level, u8 newLevel) @@ -547,7 +547,7 @@ void CreateOverworldWildEncounter(void) u16 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; u32 gender = OW_FEMALE(object) ? MON_FEMALE : MON_MALE; - u32 level = OWE_GetEncounterLevel(object); + u32 level = OWE_GetEncounterLevel(object->sOverworldEncounterLevel); u32 personality; switch (gSpeciesInfo[speciesId].genderRatio) @@ -1119,7 +1119,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( if (levelTemplate) level = levelTemplate; - assertf(level >= MIN_LEVEL && level <= MAX_LEVEL, "invalid manual overworld encounter\nlevel: %d\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", level, speciesId, x, y) + assertf(OWE_GetEncounterLevel(level) >= MIN_LEVEL && OWE_GetEncounterLevel(level) <= MAX_LEVEL, "invalid manual overworld encounter\nlevel: %d\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", level, speciesId, x, y) { level = MIN_LEVEL; } From ea12754327fb907c7cbc4e174f502dfe04288acb Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 23:26:05 +0000 Subject: [PATCH 480/572] Unify Assertf --- src/overworld_encounters.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index a849ddbec963..8d4d07d406e4 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1096,14 +1096,26 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( if (speciesTemplate) speciesId = speciesTemplate; - assertf(OWE_CheckSpecies(speciesId), "invalid manual overworld encounter\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, x, y) + if (levelTemplate) + level = levelTemplate; + + bool32 validSpecies = OWE_CheckSpecies(speciesId); + bool32 validLevel = OWE_GetEncounterLevel(level) >= MIN_LEVEL && OWE_GetEncounterLevel(level) <= MAX_LEVEL; + assertf(validSpecies && validLevel, "invalid manual overworld encounter\nspecies: %d\nlevel: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, level, x, y) { - // Currently causes assertf on each player step as function is called. - templateOWE.graphicsId = OBJ_EVENT_GFX_BOY_1; - templateOWE.trainerType = TRAINER_TYPE_NONE; - templateOWE.sOverworldEncounterLevel = 0; - templateOWE.movementType = MOVEMENT_TYPE_NONE; - return templateOWE; + if (!validSpecies) + { + // Currently causes assertf on each player step as function is called. + templateOWE.graphicsId = OBJ_EVENT_GFX_BOY_1; + templateOWE.trainerType = TRAINER_TYPE_NONE; + templateOWE.sOverworldEncounterLevel = 0; + templateOWE.movementType = MOVEMENT_TYPE_NONE; + return templateOWE; + } + else if (!validLevel) + { + level = MIN_LEVEL; + } } if (isShinyTemplate) @@ -1116,14 +1128,6 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( else isFemale = GetGenderFromSpeciesAndPersonality(speciesId, Random32()) == MON_FEMALE; - if (levelTemplate) - level = levelTemplate; - - assertf(OWE_GetEncounterLevel(level) >= MIN_LEVEL && OWE_GetEncounterLevel(level) <= MAX_LEVEL, "invalid manual overworld encounter\nlevel: %d\nspecies: %d\nx: %d y: %d\ncheck if valid wild mon header exists", level, speciesId, x, y) - { - level = MIN_LEVEL; - } - if (templateOWE.movementType == MOVEMENT_TYPE_NONE) templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); From f0fff2cee1257e10e4aa739973a25679911088c2 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Thu, 29 Jan 2026 23:28:27 +0000 Subject: [PATCH 481/572] Remove Redundant Code --- src/overworld_encounters.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8d4d07d406e4..7d11bd14ed90 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1131,9 +1131,6 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( if (templateOWE.movementType == MOVEMENT_TYPE_NONE) templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); - if (templateOWE.script == NULL) - templateOWE.script = InteractWithDynamicWildOverworldEncounter; - graphicsId = speciesId + OBJ_EVENT_MON; if (isFemale) graphicsId += OBJ_EVENT_MON_FEMALE; From 2dd1dd0d22519170feb0efc4bbdd4f89224f34da Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 29 Jan 2026 19:25:28 -0600 Subject: [PATCH 482/572] Despawn all when entering a town (not finished) --- include/config/overworld_encounters.h | 1 + src/overworld.c | 1 + src/overworld_encounters.c | 11 ++++++++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/config/overworld_encounters.h b/include/config/overworld_encounters.h index b24f03aa1cec..20bf74522699 100644 --- a/include/config/overworld_encounters.h +++ b/include/config/overworld_encounters.h @@ -18,5 +18,6 @@ #define OWE_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE TRUE // If TRUE, the mon will take steps to be right next to the player before the battle starts. #define OWE_WILD_ENCOUNTERS_PREVENT_SHINY_DESPAWN FALSE // If TRUE, shiny OW Pokémon objects will not be despawned when off-screen if on the same map as the player, or be replaced if OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. #define OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_DESPAWN FALSE // If TRUE, Feebas OW objects spawned from special Feebas fishing spots (when OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS is TRUE) will not be despawned when off-screen if on the same map as the player, or be replaced if OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. +#define OWE_WILD_ENCOUNTERS_DESPAWN_ON_ENTER_TOWN TRUE // If TRUE, despawns all OW wild encounter objects upon entering a city or town (MAP_TYPE_TOWN or MAP_TYPE_CITY). #endif // GUARD_CONFIG_OVERWORLD_ENCOUNTERS_H diff --git a/src/overworld.c b/src/overworld.c index 6b2896147bd3..594f8c36e523 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -927,6 +927,7 @@ void LoadMapFromCameraTransition(u8 mapGroup, u8 mapNum) ShowMapNamePopup(); } OverworldWildEncounter_SetMinimumSpawnTimer(); + FlagSet(FLAG_UNUSED_0x020); } static void LoadMapFromWarp(bool32 a1) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7d11bd14ed90..6807de7089e4 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -126,9 +126,18 @@ static EWRAM_DATA u8 sOWESpawnCountdown = 0; void UpdateOverworldEncounters(void) { bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); + + if (FlagGet(FLAG_UNUSED_0x020)) + { + if (OWE_WILD_ENCOUNTERS_DESPAWN_ON_ENTER_TOWN && (GetMapTypeByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum) == MAP_TYPE_TOWN || GetMapTypeByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum) == MAP_TYPE_CITY)) + RemoveAllOverworldWildEncounterObjects(OWE_ANY); + + FlagClear(FLAG_UNUSED_0x020); + } + if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING) || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) return; - + if (!OWE_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER) || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !OWE_WILD_ENCOUNTERS_BATTLE_PIKE) From 40e0c8a7a24c4ac5a7adfa2d6eb57ff037691966 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 30 Jan 2026 15:52:28 +0000 Subject: [PATCH 483/572] Add Max Value for Roamers --- src/overworld_encounters.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 7d11bd14ed90..65a1cd15193e 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -39,6 +39,11 @@ #define OWE_NON_ROAMER_OUTBREAK 0 #define OWE_MASS_OUTBREAK_INDEX ROAMER_COUNT + 1 #define OWE_INVALID_ROAMER_OUTBREAK OWE_MASS_OUTBREAK_INDEX + 1 +#define OWE_MAX_ROAMERS UINT8_MAX - 2 + +#if OWE_WILD_ENCOUNTERS_OVERWORLD == TRUE && ROAMER_COUNT >= OWE_MAX_ROAMERS +#error "ROAMER_COUNT needs to be less than OWE_MAX_ROAMERS due to it being stored in the u8 field warpArrowSpriteId" +#endif #define OWE_FLAG_BIT (1 << 7) #define OWE_SAVED_MOVEMENT_STATE_FLAG OWE_FLAG_BIT From 09fe060d808ff58fd3df7080d62726a2c9c71870 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:18:37 +0000 Subject: [PATCH 484/572] Despawn OWEs On Town or City Transition --- include/overworld_encounters.h | 3 +- src/dexnav.c | 2 +- src/fieldmap.c | 1 + src/overworld.c | 1 - src/overworld_encounters.c | 62 ++++++++++++++++++++++++++++------ 5 files changed, 56 insertions(+), 13 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 30061a4d8151..0d44abe3513e 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -95,7 +95,7 @@ void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); void OverworldWildEncounter_SetMinimumSpawnTimer(void); -void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType); +void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, s32 x, s32 y); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); @@ -125,5 +125,6 @@ u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 void OWE_PlayAmbientCry(void); u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent); +void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(const struct MapConnection *connection, enum Connection direction); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/dexnav.c b/src/dexnav.c index 656a99115fef..d08515ee5490 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -745,7 +745,7 @@ static bool8 TryStartHiddenMonFieldEffect(enum EncounterType environment, u8 xSi if (fldEffId != 0) { - RemoveAllOverworldWildEncounterObjects(OWE_GENERATED); + RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, 0, 0); gFieldEffectArguments[0] = sDexNavSearchDataPtr->tileX; gFieldEffectArguments[1] = sDexNavSearchDataPtr->tileY; gFieldEffectArguments[2] = 0xFF; // subpriority diff --git a/src/fieldmap.c b/src/fieldmap.c index 2a2ca145392e..fe7dd3d3a980 100644 --- a/src/fieldmap.c +++ b/src/fieldmap.c @@ -808,6 +808,7 @@ bool8 CameraMove(int x, int y) gSaveBlock1Ptr->pos.x += x; gSaveBlock1Ptr->pos.y += y; MoveMapViewToBackup(direction); + OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(connection, direction); } return gCamera.active; diff --git a/src/overworld.c b/src/overworld.c index 594f8c36e523..6b2896147bd3 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -927,7 +927,6 @@ void LoadMapFromCameraTransition(u8 mapGroup, u8 mapNum) ShowMapNamePopup(); } OverworldWildEncounter_SetMinimumSpawnTimer(); - FlagSet(FLAG_UNUSED_0x020); } static void LoadMapFromWarp(bool32 a1) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 8a89207c1286..c4e5b798e73c 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -131,14 +131,6 @@ static EWRAM_DATA u8 sOWESpawnCountdown = 0; void UpdateOverworldEncounters(void) { bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); - - if (FlagGet(FLAG_UNUSED_0x020)) - { - if (OWE_WILD_ENCOUNTERS_DESPAWN_ON_ENTER_TOWN && (GetMapTypeByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum) == MAP_TYPE_TOWN || GetMapTypeByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum) == MAP_TYPE_CITY)) - RemoveAllOverworldWildEncounterObjects(OWE_ANY); - - FlagClear(FLAG_UNUSED_0x020); - } if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING) || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) return; @@ -151,7 +143,7 @@ void UpdateOverworldEncounters(void) { if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) { - RemoveAllOverworldWildEncounterObjects(OWE_GENERATED); + RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, 0, 0); sOWESpawnCountdown = OWE_NO_ENCOUNTER_SET; } return; @@ -931,13 +923,21 @@ static bool32 OWE_ShouldSpawnWaterMons(void) return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); } -void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType) +void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, s32 dx, s32 dy) { for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { struct ObjectEvent *obj = &gObjectEvents[i]; if (IsOverworldWildEncounter(obj, oweType) && obj->active) + { + obj->initialCoords.x += dx; + obj->initialCoords.y += dy; + obj->currentCoords.x += dx; + obj->currentCoords.y += dy; + obj->previousCoords.x += dx; + obj->previousCoords.y += dy; RemoveObjectEvent(obj); + } } } @@ -1810,6 +1810,48 @@ bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent) return FALSE; } +void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(const struct MapConnection *connection, enum Connection direction) +{ + if (gMain.callback2 != CB2_Overworld) + return; + + if (!OWE_WILD_ENCOUNTERS_DESPAWN_ON_ENTER_TOWN) + return; + + if (gMapHeader.mapType != MAP_TYPE_CITY && gMapHeader.mapType != MAP_TYPE_TOWN) + return; + + struct MapHeader const *mapHeader = GetMapHeaderFromConnection(connection); + s32 dx, dy; + switch (direction) + { + case CONNECTION_EAST: + dx = -mapHeader->mapLayout->width; + dy = connection->offset; + break; + + case CONNECTION_WEST: + dx = mapHeader->mapLayout->width; + dy = -connection->offset; + break; + + case CONNECTION_SOUTH: + dx = connection->offset; + dy = mapHeader->mapLayout->height; + break; + + case CONNECTION_NORTH: + dx = connection->offset; + dy = -mapHeader->mapLayout->height; + break; + + default: + return; + } + + RemoveAllOverworldWildEncounterObjects(OWE_ANY, dx, dy); +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From 1f63761f4e5993aafa03c81a05b751698ebe04c5 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:17:17 +0000 Subject: [PATCH 485/572] Fix Despawning on Map Connection --- include/event_object_movement.h | 1 + include/overworld_encounters.h | 2 +- src/dexnav.c | 2 +- src/event_object_movement.c | 23 ++++++++++------ src/overworld_encounters.c | 49 ++++++++------------------------- 5 files changed, 29 insertions(+), 48 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index c1e9d761d21f..8567fb71822c 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -277,6 +277,7 @@ void CopyObjectGraphicsInfoToSpriteTemplate(u16 graphicsId, void (*callback)(str bool32 AreElevationsCompatible(u32, u32); enum Direction DetermineObjectEventDirectionFromObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo); void ObjectEventsTurnToEachOther(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo); +void UpdateObjectEventCoords(struct ObjectEvent *objectEvent, s16 dx, s16 dy); void MovementType_None(struct Sprite *sprite); void MovementType_LookAround(struct Sprite *sprite); diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 0d44abe3513e..e7cb7f5ea8c9 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -95,7 +95,7 @@ void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); void OverworldWildEncounter_SetMinimumSpawnTimer(void); -void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, s32 x, s32 y); +void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); diff --git a/src/dexnav.c b/src/dexnav.c index d08515ee5490..656a99115fef 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -745,7 +745,7 @@ static bool8 TryStartHiddenMonFieldEffect(enum EncounterType environment, u8 xSi if (fldEffId != 0) { - RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, 0, 0); + RemoveAllOverworldWildEncounterObjects(OWE_GENERATED); gFieldEffectArguments[0] = sDexNavSearchDataPtr->tileX; gFieldEffectArguments[1] = sDexNavSearchDataPtr->tileY; gFieldEffectArguments[2] = 0xFF; // subpriority diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 970a44a905ea..f30b91b8f47b 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -3507,6 +3507,19 @@ void ShiftStillObjectEventCoords(struct ObjectEvent *objectEvent) ShiftObjectEventCoords(objectEvent, objectEvent->currentCoords.x, objectEvent->currentCoords.y); } +void UpdateObjectEventCoords(struct ObjectEvent *objectEvent, s16 dx, s16 dy) +{ + if (objectEvent->active) + { + objectEvent->initialCoords.x -= dx; + objectEvent->initialCoords.y -= dy; + objectEvent->currentCoords.x -= dx; + objectEvent->currentCoords.y -= dy; + objectEvent->previousCoords.x -= dx; + objectEvent->previousCoords.y -= dy; + } +} + void UpdateObjectEventCoordsForCameraUpdate(void) { u8 i; @@ -3519,15 +3532,7 @@ void UpdateObjectEventCoordsForCameraUpdate(void) dy = gCamera.y; for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { - if (gObjectEvents[i].active) - { - gObjectEvents[i].initialCoords.x -= dx; - gObjectEvents[i].initialCoords.y -= dy; - gObjectEvents[i].currentCoords.x -= dx; - gObjectEvents[i].currentCoords.y -= dy; - gObjectEvents[i].previousCoords.x -= dx; - gObjectEvents[i].previousCoords.y -= dy; - } + UpdateObjectEventCoords(&gObjectEvents[i], dx, dy); } } } diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index c4e5b798e73c..a3654072f235 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -143,7 +143,7 @@ void UpdateOverworldEncounters(void) { if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) { - RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, 0, 0); + RemoveAllOverworldWildEncounterObjects(OWE_GENERATED); sOWESpawnCountdown = OWE_NO_ENCOUNTER_SET; } return; @@ -923,19 +923,22 @@ static bool32 OWE_ShouldSpawnWaterMons(void) return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); } -void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, s32 dx, s32 dy) +void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType) { + s16 dx = 0; + s16 dy = 0; + if (gCamera.active) + { + dx = gCamera.x; + dy = gCamera.y; + } + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { struct ObjectEvent *obj = &gObjectEvents[i]; if (IsOverworldWildEncounter(obj, oweType) && obj->active) { - obj->initialCoords.x += dx; - obj->initialCoords.y += dy; - obj->currentCoords.x += dx; - obj->currentCoords.y += dy; - obj->previousCoords.x += dx; - obj->previousCoords.y += dy; + UpdateObjectEventCoords(obj, dx, dy); RemoveObjectEvent(obj); } } @@ -1821,35 +1824,7 @@ void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(const struct MapC if (gMapHeader.mapType != MAP_TYPE_CITY && gMapHeader.mapType != MAP_TYPE_TOWN) return; - struct MapHeader const *mapHeader = GetMapHeaderFromConnection(connection); - s32 dx, dy; - switch (direction) - { - case CONNECTION_EAST: - dx = -mapHeader->mapLayout->width; - dy = connection->offset; - break; - - case CONNECTION_WEST: - dx = mapHeader->mapLayout->width; - dy = -connection->offset; - break; - - case CONNECTION_SOUTH: - dx = connection->offset; - dy = mapHeader->mapLayout->height; - break; - - case CONNECTION_NORTH: - dx = connection->offset; - dy = -mapHeader->mapLayout->height; - break; - - default: - return; - } - - RemoveAllOverworldWildEncounterObjects(OWE_ANY, dx, dy); + RemoveAllOverworldWildEncounterObjects(OWE_ANY); } #undef sOverworldEncounterLevel From 7f3c67c6e38d88743a0dc6b14d4f601194d249a2 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:21:13 +0000 Subject: [PATCH 486/572] Change Function Types --- include/overworld_encounters.h | 2 +- src/fieldmap.c | 2 +- src/overworld_encounters.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index e7cb7f5ea8c9..92770db4004b 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -125,6 +125,6 @@ u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 void OWE_PlayAmbientCry(void); u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent); -void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(const struct MapConnection *connection, enum Connection direction); +void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void); #endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file diff --git a/src/fieldmap.c b/src/fieldmap.c index fe7dd3d3a980..255ea294f220 100644 --- a/src/fieldmap.c +++ b/src/fieldmap.c @@ -808,7 +808,7 @@ bool8 CameraMove(int x, int y) gSaveBlock1Ptr->pos.x += x; gSaveBlock1Ptr->pos.y += y; MoveMapViewToBackup(direction); - OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(connection, direction); + OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(); } return gCamera.active; diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index a3654072f235..25e36f64fbf2 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1813,7 +1813,7 @@ bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent) return FALSE; } -void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(const struct MapConnection *connection, enum Connection direction) +void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void) { if (gMain.callback2 != CB2_Overworld) return; From 4607c0b1c93492e404b6a1dcafe1841d57002d08 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:21:37 +0000 Subject: [PATCH 487/572] Change OWE_SPAWN_ANIM_CAVE yOffset --- src/field_effect_helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 7abc0ecdcc6c..92e31b5edf34 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1549,7 +1549,7 @@ u32 FldEff_OWE_SpawnAnim(void) case OWE_SPAWN_ANIM_CAVE: visual = FLDEFFOBJ_GROUND_IMPACT_DUST; - yOffset = 8; + yOffset = 12; break; case OWE_SPAWN_ANIM_SHINY: From 6226ac343e7a8fed55a9fda155eef7b7203cc100 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:22:36 +0000 Subject: [PATCH 488/572] Stop OWE Updates when DexNav Searching --- src/overworld_encounters.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 25e36f64fbf2..5e2ab222e5b2 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -137,6 +137,7 @@ void UpdateOverworldEncounters(void) if (!OWE_WILD_ENCOUNTERS_OVERWORLD || FlagGet(OW_FLAG_NO_ENCOUNTER) + || FlagGet(DN_FLAG_SEARCHING) || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !OWE_WILD_ENCOUNTERS_BATTLE_PIKE) || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR && !OWE_WILD_ENCOUNTERS_BATTLE_PYRAMID) || InTrainerHillChallenge()) From a08fd339b2e6ba3c378016d44d7906a1ba16b569 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:41:57 +0000 Subject: [PATCH 489/572] Make Battle Pyramid Adjustments Clearer --- include/battle_pyramid.h | 2 +- src/battle_pyramid.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/battle_pyramid.h b/include/battle_pyramid.h index 38815aecd5ac..4b0a1bbfe678 100644 --- a/include/battle_pyramid.h +++ b/include/battle_pyramid.h @@ -7,7 +7,7 @@ void CallBattlePyramidFunction(void); u16 LocalIdToPyramidTrainerId(u8 localId); bool8 GetBattlePyramidTrainerFlag(u8 eventId); void MarkApproachingPyramidTrainersAsBattled(void); -void GenerateBattlePyramidWildMon(u32 species); +void GenerateBattlePyramidWildMon(u32 forceSpecies); u8 GetPyramidRunMultiplier(void); u8 CurrentBattlePyramidLocation(void); bool8 InBattlePyramid_(void); diff --git a/src/battle_pyramid.c b/src/battle_pyramid.c index 6d90d30197b1..c95919854f55 100644 --- a/src/battle_pyramid.c +++ b/src/battle_pyramid.c @@ -1391,7 +1391,7 @@ static bool32 CheckBattlePyramidEvoRequirement(u16 species, const u16 *evoItems, } extern u32 GetTotalBaseStat(u32 species); -void GenerateBattlePyramidWildMon(u32 species) +void GenerateBattlePyramidWildMon(u32 forceSpecies) { u8 name[POKEMON_NAME_LENGTH + 1]; int i, j; @@ -1399,7 +1399,7 @@ void GenerateBattlePyramidWildMon(u32 species) u32 lvl = gSaveBlock2Ptr->frontier.lvlMode; u16 round = (gSaveBlock2Ptr->frontier.pyramidWinStreaks[lvl] / 7) % TOTAL_PYRAMID_ROUNDS; const struct BattlePyramidRequirement *reqs = &sBattlePyramidRequirementsByRound[round]; - bool32 shouldRandom = (species == SPECIES_NONE); + u16 species = forceSpecies; u32 bstLim; u16 *moves = NULL; u16 *abilities = NULL; @@ -1419,7 +1419,7 @@ void GenerateBattlePyramidWildMon(u32 species) while (1) { - if (shouldRandom) + if (!forceSpecies) species = Random() % NUM_SPECIES; // check if base species @@ -1564,7 +1564,7 @@ void GenerateBattlePyramidWildMon(u32 species) CalculateMonStats(&gEnemyParty[0]); } #else -void GenerateBattlePyramidWildMon(u32 species) +void GenerateBattlePyramidWildMon(u32 forceSpecies) { u8 name[POKEMON_NAME_LENGTH + 1]; int i; From da86e4eb02d62fea489f2c11a6241b121c7a12eb Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:44:02 +0000 Subject: [PATCH 490/572] Move Defines to reflect scope --- include/constants/trainer_types.h | 2 +- include/overworld_encounters.h | 43 +++++------------------------ src/overworld_encounters.c | 45 ++++++++++++++++++++++++------- 3 files changed, 42 insertions(+), 48 deletions(-) diff --git a/include/constants/trainer_types.h b/include/constants/trainer_types.h index 8d9a34b6cc89..c21f3c042a69 100644 --- a/include/constants/trainer_types.h +++ b/include/constants/trainer_types.h @@ -5,6 +5,6 @@ #define TRAINER_TYPE_NORMAL 1 #define TRAINER_TYPE_SEE_ALL_DIRECTIONS 2 #define TRAINER_TYPE_BURIED 3 -#define TRAINER_TYPE_OW_WILD_ENCOUNTER 255 +#define TRAINER_TYPE_OW_WILD_ENCOUNTER 0xFF #endif // GUARD_CONSTANTS_TRAINER_TYPES_H diff --git a/include/overworld_encounters.h b/include/overworld_encounters.h index 92770db4004b..9cd23d413cbb 100644 --- a/include/overworld_encounters.h +++ b/include/overworld_encounters.h @@ -1,42 +1,11 @@ #ifndef GUARD_OVERWORLD_ENCOUNTERS_H #define GUARD_OVERWORLD_ENCOUNTERS_H -#if OW_POKEMON_OBJECT_EVENTS == FALSE && OWE_WILD_ENCOUNTERS_OVERWORLD == TRUE -#error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OWE_WILD_ENCOUNTERS_OVERWORLD to work." -#endif - -#define OWE_MAX_SPAWNS 4 - -#define OWE_SPAWN_DISTANCE_LAND 1 // A spawn cannot happen within this many tiles of the player position. -#define OWE_SPAWN_DISTANCE_WATER 3 // A spawn cannot happen within this many tiles of the player position (while surfing). - -#define OWE_TOTAL_SPAWN_WIDTH 15 // Width of the on-screen spawn area in tiles. -#define OWE_TOTAL_SPAWN_HEIGHT 9 // Height of the on-screen spawn area in tiles. -#define OWE_SPAWN_RADUIS_WIDTH (OWE_TOTAL_SPAWN_WIDTH - 1) / 2 // Distance from center to left/right edge (not including center). -#define OWE_SPAWN_RADUIS_HEIGHT (OWE_TOTAL_SPAWN_HEIGHT - 1) / 2 // Distance from center to top/bottom edge (not including center). - -#define OWE_SPAWN_TIME_MINIMUM 30 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. -#define OWE_SPAWN_TIME_PER_ACTIVE 30 // The number of frames that will be added to the countdown per currently active spawn. -#define OWE_SPAWN_TIME_REPLACEMENT 240 // The number of frames before an existing spawn will be replaced with a new one (requires OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT). - -#define OWE_MON_SIGHT_WIDTH 3 -#define OWE_MON_SIGHT_LENGTH 4 - -#define OWE_CHASE_RANGE 5 -#define OWE_APPROACH_DISTANCE 2 - -#define OWE_APPROACH_JUMP_TIMER_MIN 16 -#define OWE_APPROACH_JUMP_TIMER_MAX 64 - -#define OWE_FLEE_COLLISION_TIME 6 // If a fleeing mon is unable to take a step for this many tries it will despawn. (Multiply this value by 16 to get number of frames.) - -#define OWE_DESPAWN_FRAMES 30 // Number of frames before a mon despawns after noticing the player (OWE_BEHAVIOR_DESPAWN) - -#define OWE_NO_ENCOUNTER_SET 255 - -#define INVALID_SPAWN_SLOT 0xFF - -#define OWE_RESTORED_MOVEMENT_FUNC_ID 10 +#define OWE_APPROACH_DISTANCE 2 +#define OWE_APPROACH_JUMP_TIMER_MIN 16 +#define OWE_APPROACH_JUMP_TIMER_MAX 64 +#define OWE_FLEE_COLLISION_TIME 6 // If a fleeing mon is unable to take a step for this many tries it will despawn. (Multiply this value by 16 to get number of frames.) +#define OWE_DESPAWN_FRAMES 30 // Number of frames before a mon despawns after noticing the player (OWE_BEHAVIOR_DESPAWN) enum OverworldEncounterSpawnAnim { @@ -45,7 +14,7 @@ enum OverworldEncounterSpawnAnim OWE_SPAWN_ANIM_WATER, OWE_SPAWN_ANIM_UNDERWATER, OWE_SPAWN_ANIM_CAVE, - OWE_SPAWN_ANIM_SHINY, + OWE_SPAWN_ANIM_SHINY }; enum OverworldObjectEncounterType diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5e2ab222e5b2..e178cab744ee 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -41,13 +41,38 @@ #define OWE_INVALID_ROAMER_OUTBREAK OWE_MASS_OUTBREAK_INDEX + 1 #define OWE_MAX_ROAMERS UINT8_MAX - 2 +#define OWE_FLAG_BIT (1 << 7) +#define OWE_SAVED_MOVEMENT_STATE_FLAG OWE_FLAG_BIT +#define OWE_NO_DESPAWN_FLAG OWE_FLAG_BIT + +#define OWE_MAX_SPAWNS 4 +#define OWE_SPAWN_DISTANCE_LAND 1 // A spawn cannot happen within this many tiles of the player position. +#define OWE_SPAWN_DISTANCE_WATER 3 // A spawn cannot happen within this many tiles of the player position (while surfing). +#define OWE_TOTAL_SPAWN_WIDTH 15 // Width of the on-screen spawn area in tiles. +#define OWE_TOTAL_SPAWN_HEIGHT 9 // Height of the on-screen spawn area in tiles. +#define OWE_SPAWN_RADUIS_WIDTH (OWE_TOTAL_SPAWN_WIDTH - 1) / 2 // Distance from center to left/right edge (not including center). +#define OWE_SPAWN_RADUIS_HEIGHT (OWE_TOTAL_SPAWN_HEIGHT - 1) / 2 // Distance from center to top/bottom edge (not including center). + +#define OWE_SPAWN_TIME_MINIMUM 30 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. +#define OWE_SPAWN_TIME_PER_ACTIVE 30 // The number of frames that will be added to the countdown per currently active spawn. +#define OWE_SPAWN_TIME_REPLACEMENT 240 // The number of frames before an existing spawn will be replaced with a new one (requires OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT). + +#define OWE_MON_SIGHT_WIDTH 3 +#define OWE_MON_SIGHT_LENGTH 4 +#define OWE_CHASE_RANGE 5 + +#define OWE_NO_ENCOUNTER_SET 0xFF +#define OWE_INVALID_SPAWN_SLOT 0xFF +#define OWE_RESTORED_MOVEMENT_FUNC_ID 10 + #if OWE_WILD_ENCOUNTERS_OVERWORLD == TRUE && ROAMER_COUNT >= OWE_MAX_ROAMERS #error "ROAMER_COUNT needs to be less than OWE_MAX_ROAMERS due to it being stored in the u8 field warpArrowSpriteId" #endif -#define OWE_FLAG_BIT (1 << 7) -#define OWE_SAVED_MOVEMENT_STATE_FLAG OWE_FLAG_BIT -#define OWE_NO_DESPAWN_FLAG OWE_FLAG_BIT +#if OW_POKEMON_OBJECT_EVENTS == FALSE && OWE_WILD_ENCOUNTERS_OVERWORLD == TRUE +#error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OWE_WILD_ENCOUNTERS_OVERWORLD to work." +#endif + static inline u32 OWE_GetRoamerIndex(const struct ObjectEvent *object) { @@ -155,7 +180,7 @@ void UpdateOverworldEncounters(void) } u16 spawnSlot = NextSpawnMonSlot(); - if (LURE_STEP_COUNT && spawnSlot != INVALID_SPAWN_SLOT + if (LURE_STEP_COUNT && spawnSlot != OWE_INVALID_SPAWN_SLOT && (GetNumberActiveOverworldEncounters(OWE_GENERATED) < OWE_MAX_SPAWNS)) { OverworldWildEncounter_SetMinimumSpawnTimer(); @@ -170,7 +195,7 @@ void UpdateOverworldEncounters(void) return; s16 x, y; - if (spawnSlot == INVALID_SPAWN_SLOT + if (spawnSlot == OWE_INVALID_SPAWN_SLOT || (shouldSpawnWaterMons && AreLegendariesInSootopolisPreventingEncounters()) || !TrySelectTile(&x, &y)) { @@ -391,7 +416,7 @@ u32 GetOldestSlot(bool32 forceRemove) } if (spawnSlot >= OWE_MAX_SPAWNS) - return INVALID_SPAWN_SLOT; + return OWE_INVALID_SPAWN_SLOT; for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWNS; spawnSlot++) { @@ -417,12 +442,12 @@ static u8 NextSpawnMonSlot(void) { // Cycle through so we remove the oldest mon first spawnSlot = GetOldestSlot(FALSE); - if (spawnSlot == INVALID_SPAWN_SLOT) - return INVALID_SPAWN_SLOT; + if (spawnSlot == OWE_INVALID_SPAWN_SLOT) + return OWE_INVALID_SPAWN_SLOT; } else { - return INVALID_SPAWN_SLOT; + return OWE_INVALID_SPAWN_SLOT; } } else @@ -1060,7 +1085,7 @@ u32 RemoveOldestGeneratedOverworldEncounter(void) { u32 oldestSlot = GetOldestSlot(TRUE); - if (oldestSlot == INVALID_SPAWN_SLOT) + if (oldestSlot == OWE_INVALID_SPAWN_SLOT) return OBJECT_EVENTS_COUNT; u32 objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(oldestSlot)); From 8c933fd8192332241df994bec1aa700df912400c Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 30 Jan 2026 17:15:33 -0600 Subject: [PATCH 491/572] Play sound when despawn on cross map connection --- src/overworld_encounters.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5e2ab222e5b2..4b140961521b 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1825,6 +1825,9 @@ void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void) if (gMapHeader.mapType != MAP_TYPE_CITY && gMapHeader.mapType != MAP_TYPE_TOWN) return; + if (OWE_WILD_ENCOUNTERS_DESPAWN_SOUND) + PlaySE(SE_FLEE); + RemoveAllOverworldWildEncounterObjects(OWE_ANY); } From d9e821c665e4bccd3287030db599e17a871a9a1e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:22:16 +0000 Subject: [PATCH 492/572] Undo Field Effect Changes --- data/field_effect_scripts.s | 4 ++-- include/constants/field_effects.h | 2 -- src/data/field_effects/field_effect_objects.h | 18 ++---------------- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/data/field_effect_scripts.s b/data/field_effect_scripts.s index 35ff6a558f1d..db73a38b7203 100644 --- a/data/field_effect_scripts.s +++ b/data/field_effect_scripts.s @@ -106,7 +106,7 @@ gFieldEffectScript_Shadow:: field_eff_end gFieldEffectScript_TallGrass:: - field_eff_loadgfx_callnative gSpriteSheet_TallGrass, gSpritePalette_GeneralFieldEffect1, FldEff_TallGrass + field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_TallGrass field_eff_end gFieldEffectScript_Ripple:: @@ -158,7 +158,7 @@ gFieldEffectScript_JumpSmallSplash:: field_eff_end gFieldEffectScript_LongGrass:: - field_eff_loadgfx_callnative gSpriteSheet_LongGrass, gSpritePalette_GeneralFieldEffect1, FldEff_LongGrass + field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_LongGrass field_eff_end gFieldEffectScript_JumpLongGrass:: diff --git a/include/constants/field_effects.h b/include/constants/field_effects.h index fbaa4b5cc220..dd3fb7136257 100644 --- a/include/constants/field_effects.h +++ b/include/constants/field_effects.h @@ -154,7 +154,5 @@ #define FLDEFF_TILE_TAG_SHADOW_MEDIUM 0x1401 #define FLDEFF_TILE_TAG_SHADOW_LARGE 0x1402 #define FLDEFF_TILE_TAG_SHADOW_EXTRA_LARGE 0x1403 -#define FLDEFF_TILE_TAG_TALL_GRASS_OWE 0x1404 -#define FLDEFF_TILE_TAG_LONG_GRASS_OWE 0x1405 #endif // GUARD_FIELD_EFFECT_CONSTANTS_H diff --git a/src/data/field_effects/field_effect_objects.h b/src/data/field_effects/field_effect_objects.h index 52c06ecd848f..f878f848ca47 100755 --- a/src/data/field_effects/field_effect_objects.h +++ b/src/data/field_effects/field_effect_objects.h @@ -132,15 +132,8 @@ static const union AnimCmd *const sAnimTable_TallGrass[] = sAnim_TallGrass, }; -const struct SpriteSheet gSpriteSheet_TallGrass = -{ - .data = gFieldEffectObjectPic_TallGrass, - .size = sizeof(gFieldEffectObjectPic_TallGrass), - .tag = FLDEFF_TILE_TAG_TALL_GRASS_OWE, -}; - const struct SpriteTemplate gFieldEffectObjectTemplate_TallGrass = { - .tileTag = FLDEFF_TILE_TAG_TALL_GRASS_OWE, + .tileTag = TAG_NONE, .paletteTag = FLDEFF_PAL_TAG_GENERAL_1, .oam = &gObjectEventBaseOam_16x16, .anims = sAnimTable_TallGrass, @@ -714,15 +707,8 @@ static const union AnimCmd *const sAnimTable_LongGrass[] = sAnim_LongGrass, }; -const struct SpriteSheet gSpriteSheet_LongGrass = -{ - .data = gFieldEffectObjectPic_LongGrass, - .size = sizeof(gFieldEffectObjectPic_LongGrass), - .tag = FLDEFF_TILE_TAG_LONG_GRASS_OWE, -}; - const struct SpriteTemplate gFieldEffectObjectTemplate_LongGrass = { - .tileTag = FLDEFF_TILE_TAG_LONG_GRASS_OWE, + .tileTag = TAG_NONE, .paletteTag = FLDEFF_PAL_TAG_GENERAL_1, .oam = &gObjectEventBaseOam_16x16, .anims = sAnimTable_LongGrass, From a66f914a0fb9068b2f59f3f3518ce8ec3084bc03 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:19:51 +0000 Subject: [PATCH 493/572] Robust Mon Generation and Prevent Manual from being special spawns at all --- .../how_to_overworld_wild_encounters.md | 2 +- src/overworld_encounters.c | 39 ++++++++++--------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 89745c6a389a..2abf3974e4cd 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -21,7 +21,7 @@ Assuming the following `graphicsId` have `.trainerType` set to `TRAINER_TYPE_OW_ As level and species are potentially taken from the Wild Encounter Header, an `assertf` to let developers know when an invalid value is used. If the resultant level is invalid, it will be set to `MIN_LEVEL` (1). If the species is invalid, a replacement object will be created using `OBJ_EVENT_GFX_BOY_1`, this will not be an OWE of any kind. -No matter how much of a Manual OWE is defined, it is considered a high priority OWE, and treated as a regular object event in all ways other than ones outlined above. They will always spawn, regardless of level of abilties of player Pokémon, however, they have restricted special spawns types. +No matter how much of a Manual OWE is defined, it is considered a high priority OWE, and treated as a regular object event in all ways other than ones outlined above. They will always spawn, regardless of level of abilties of player Pokémon, however, they cannot be special spawns. > Flags are set when removed. diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index e178cab744ee..41a2a0bee004 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -901,27 +901,30 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam If none of these checks succeed, speciesId is set to SPECIES_NONE and FALSE is returned. */ - if (TryStartRoamerEncounter() && !OWE_DoesRoamerObjectExist() && *indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) + if (*indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) { - *indexRoamerOutbreak = OWE_GetObjectRoamerStatusFromIndex(gEncounteredRoamerIndex); - } - else if (OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) - { - CreateWildMon(gWildFeebas.species, ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING)); - if (OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_DESPAWN) - OWE_SetNoDespawnFlag(level); - } - else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior) && *indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) - { - SetUpMassOutbreakEncounter(0); - *indexRoamerOutbreak = OWE_MASS_OUTBREAK_INDEX; - } - else if (!TryGenerateWildMon(wildMonInfo, wildArea, 0)) - { - return FALSE; + if (TryStartRoamerEncounter() && !OWE_DoesRoamerObjectExist()) + { + *indexRoamerOutbreak = OWE_GetObjectRoamerStatusFromIndex(gEncounteredRoamerIndex); + } + else if (OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) + { + CreateWildMon(gWildFeebas.species, ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING)); + if (OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_DESPAWN) + OWE_SetNoDespawnFlag(level); + } + else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior)) + { + SetUpMassOutbreakEncounter(0); + *indexRoamerOutbreak = OWE_MASS_OUTBREAK_INDEX; + } + else + { + return TryGenerateWildMon(wildMonInfo, wildArea, 0); + } } - return TRUE; + return TryGenerateWildMon(wildMonInfo, wildArea, 0); } static bool8 IsSafeToSpawnObjectEvents(void) From 232284a138ff3cae2b0646f025089c0b1e08f820 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:27:20 +0000 Subject: [PATCH 494/572] Move Functions to Fieldmap --- include/fieldmap.h | 2 ++ include/overworld.h | 2 -- src/fieldmap.c | 17 +++++++++++++++++ src/overworld.c | 17 ----------------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/fieldmap.h b/include/fieldmap.h index 2112aff35235..705eb8f8c544 100644 --- a/include/fieldmap.h +++ b/include/fieldmap.h @@ -62,6 +62,8 @@ void CopySecondaryTilesetToVram(struct MapLayout const *mapLayout); const struct MapHeader *const GetMapHeaderFromConnection(const struct MapConnection *connection); const struct MapConnection *GetMapConnectionAtPos(s16 x, s16 y); void MapGridSetMetatileImpassabilityAt(int x, int y, bool32 impassable); +bool32 AreCoordsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); +bool32 AreCoordsInsidePlayerMap(s16 x, s16 y); // field_region_map.c void FieldInitRegionMap(MainCallback callback); diff --git a/include/overworld.h b/include/overworld.h index a47e9ad921d9..b3aed23ae389 100644 --- a/include/overworld.h +++ b/include/overworld.h @@ -153,8 +153,6 @@ bool8 IsMapTypeIndoors(enum MapType mapType); mapsec_u8_t GetSavedWarpRegionMapSectionId(void); mapsec_u8_t GetCurrentRegionMapSectionId(void); enum MapBattleScene GetCurrentMapBattleScene(void); -bool32 AreCoordsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y); -bool32 AreCoordsInsidePlayerMap(s16 x, s16 y); void CleanupOverworldWindowsAndTilemaps(void); bool32 IsOverworldLinkActive(void); void CB1_Overworld(void); diff --git a/src/fieldmap.c b/src/fieldmap.c index 255ea294f220..8c50c23df4b4 100644 --- a/src/fieldmap.c +++ b/src/fieldmap.c @@ -1082,3 +1082,20 @@ void LoadMapTilesetPalettes(struct MapLayout const *mapLayout) LoadSecondaryTilesetPalette(mapLayout, FALSE); } } + +bool32 AreCoordsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) +{ + const struct MapLayout *layout = Overworld_GetMapHeaderByGroupAndId(mapGroup, mapNum)->mapLayout; + s32 width = layout->width + MAP_OFFSET; + s32 height = layout->height + MAP_OFFSET; + + if (x >= 0 && x < width && y >= 0 && y < height) + return TRUE; + + return FALSE; +} + +bool32 AreCoordsInsidePlayerMap(s16 x, s16 y) +{ + return AreCoordsInsideMap(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, x, y); +} diff --git a/src/overworld.c b/src/overworld.c index 6b2896147bd3..9e183366f5a2 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1592,23 +1592,6 @@ enum MapBattleScene GetCurrentMapBattleScene(void) return Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->battleType; } -bool32 AreCoordsInsideMap(u8 mapGroup, u8 mapNum, s16 x, s16 y) -{ - const struct MapLayout *layout = Overworld_GetMapHeaderByGroupAndId(mapGroup, mapNum)->mapLayout; - s32 width = layout->width + MAP_OFFSET; - s32 height = layout->height + MAP_OFFSET; - - if (x >= 0 && x < width && y >= 0 && y < height) - return TRUE; - - return FALSE; -} - -bool32 AreCoordsInsidePlayerMap(s16 x, s16 y) -{ - return AreCoordsInsideMap(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, x, y); -} - static void InitOverworldBgs(void) { InitBgsFromTemplates(0, sOverworldBgTemplates, ARRAY_COUNT(sOverworldBgTemplates)); From e69951f44b36fbd0514f01f837f355e9a677e8ea Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:30:40 +0000 Subject: [PATCH 495/572] Move Function Call --- src/load_save.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/load_save.c b/src/load_save.c index ac900aa6e164..8cf40d933c5f 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -221,7 +221,6 @@ void LoadObjectEvents(void) int i; u16 graphicsId; - OverworldWildEncounter_SetMinimumSpawnTimer(); for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { gObjectEvents[i] = gSaveBlock1Ptr->objectEvents[i]; @@ -239,6 +238,7 @@ void LoadObjectEvents(void) gObjectEvents[i].graphicsId & OBJ_EVENT_MON) gObjectEvents[i].active = TRUE; } + OverworldWildEncounter_SetMinimumSpawnTimer(); } void CopyPartyAndObjectsToSave(void) From 4e7d836c3deaf9cea5ea824d03e74e85e09c9149 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 30 Jan 2026 18:43:05 -0600 Subject: [PATCH 496/572] Step on grass effect doesn't skip to end of anim --- src/event_object_movement.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index f30b91b8f47b..e020d78c2fb0 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -10262,7 +10262,7 @@ void GroundEffect_StepOnTallGrass(struct ObjectEvent *objEvent, struct Sprite *s gFieldEffectArguments[4] = objEvent->localId << 8 | objEvent->mapNum; gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; - gFieldEffectArguments[7] = TRUE; // skip to end of anim + gFieldEffectArguments[7] = FALSE; // Don't skip to end of anim FieldEffectStart(FLDEFF_TALL_GRASS); } @@ -10288,7 +10288,7 @@ void GroundEffect_StepOnLongGrass(struct ObjectEvent *objEvent, struct Sprite *s gFieldEffectArguments[4] = (objEvent->localId << 8) | objEvent->mapNum; gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; - gFieldEffectArguments[7] = TRUE; + gFieldEffectArguments[7] = FALSE; FieldEffectStart(FLDEFF_LONG_GRASS); } From 1c8e210dd989986273f61486477fb2d0b0e8ea31 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:53:18 +0000 Subject: [PATCH 497/572] Match Upcoming --- src/event_object_movement.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index e020d78c2fb0..7055294098a3 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -10262,7 +10262,7 @@ void GroundEffect_StepOnTallGrass(struct ObjectEvent *objEvent, struct Sprite *s gFieldEffectArguments[4] = objEvent->localId << 8 | objEvent->mapNum; gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; - gFieldEffectArguments[7] = FALSE; // Don't skip to end of anim + gFieldEffectArguments[7] = FALSE; // don't skip to end of anim FieldEffectStart(FLDEFF_TALL_GRASS); } @@ -10275,7 +10275,7 @@ void GroundEffect_SpawnOnLongGrass(struct ObjectEvent *objEvent, struct Sprite * gFieldEffectArguments[4] = objEvent->localId << 8 | objEvent->mapNum; gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; - gFieldEffectArguments[7] = TRUE; + gFieldEffectArguments[7] = 1; FieldEffectStart(FLDEFF_LONG_GRASS); } @@ -10288,7 +10288,7 @@ void GroundEffect_StepOnLongGrass(struct ObjectEvent *objEvent, struct Sprite *s gFieldEffectArguments[4] = (objEvent->localId << 8) | objEvent->mapNum; gFieldEffectArguments[5] = objEvent->mapGroup; gFieldEffectArguments[6] = (u8)gSaveBlock1Ptr->location.mapNum << 8 | (u8)gSaveBlock1Ptr->location.mapGroup; - gFieldEffectArguments[7] = FALSE; + gFieldEffectArguments[7] = 0; FieldEffectStart(FLDEFF_LONG_GRASS); } From 7c015c3a7345de8aaf3552a6ead15835db9ab909 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 31 Jan 2026 01:12:29 +0000 Subject: [PATCH 498/572] Rename Files --- data/event_scripts.s | 2 +- ...orld_encounters.inc => wild_encounter.inc} | 0 .../how_to_overworld_wild_encounters.md | 2 +- include/config/overworld_encounters.h | 23 -------- include/config/wild_encounter.h | 27 ++++++++++ include/constants/global.h | 2 +- include/pokemon.h | 2 +- include/wild_encounter.h | 2 +- ...ncounters.h => wild_encounter_overworld.h} | 6 +-- src/battle_setup.c | 2 +- ....h => wild_encounter_overworld_behavior.h} | 8 +-- src/event_object_movement.c | 4 +- src/field_control_avatar.c | 2 +- src/field_effect_helpers.c | 2 +- src/load_save.c | 2 +- src/overworld.c | 2 +- src/overworld_encounters.c | 54 +++++++++---------- src/pokemon.c | 2 +- 18 files changed, 74 insertions(+), 70 deletions(-) rename data/scripts/{overworld_encounters.inc => wild_encounter.inc} (100%) delete mode 100644 include/config/overworld_encounters.h create mode 100644 include/config/wild_encounter.h rename include/{overworld_encounters.h => wild_encounter_overworld.h} (97%) rename src/data/pokemon/{overworld_encounter_behaviors.h => wild_encounter_overworld_behavior.h} (88%) diff --git a/data/event_scripts.s b/data/event_scripts.s index 6508418bebab..6a66d333caab 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -1741,4 +1741,4 @@ EventScript_PalletTown_PlayersHouse_2F_TurnOnPC:: .include "data/scripts/dexnav.inc" .include "data/scripts/battle_frontier.inc" .include "data/scripts/apricorn_tree.inc" - .include "data/scripts/overworld_encounters.inc" + .include "data/scripts/wild_encounter.inc" diff --git a/data/scripts/overworld_encounters.inc b/data/scripts/wild_encounter.inc similarity index 100% rename from data/scripts/overworld_encounters.inc rename to data/scripts/wild_encounter.inc diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 2abf3974e4cd..2168c46c278e 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -3,7 +3,7 @@ Overworld Wild Encounters (OWEs), refer to the wild encounters that can be seen as object events in the overworld, prior to engaging in battle with them. They use either the `WILD_AREA_LAND` or `WILD_AREA_WATER` encounter tables by default. OWEs come in two types, Generated or Manual. ### Generated OWEs -Generated OWEs are spawned automatically when `OWE_WILD_ENCOUNTERS_OVERWORLD` is set to `TRUE`, being spawned on a random encounter tile near the player, with the encounter table used dependant on it. These are considered low priority OWEs, and automatically populate a level, species, gender and shinyness exactly how a vanilla wild encounter would, or can even be a special spawn, but more on those later. +Generated OWEs are spawned automatically when `WE_OW_ENCOUNTERS` is set to `TRUE`, being spawned on a random encounter tile near the player, with the encounter table used dependant on it. These are considered low priority OWEs, and automatically populate a level, species, gender and shinyness exactly how a vanilla wild encounter would, or can even be a special spawn, but more on those later. > Setting `OW_GFX_COMPRESS` to `FALSE` will free more space in VRAM, allowing for more large OWEs to spawn, and reducing the chance of running into a tiles error when trying to spawn Generated OWEs. ### Manual OWEs diff --git a/include/config/overworld_encounters.h b/include/config/overworld_encounters.h deleted file mode 100644 index 20bf74522699..000000000000 --- a/include/config/overworld_encounters.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef GUARD_CONFIG_OVERWORLD_ENCOUNTERS_H -#define GUARD_CONFIG_OVERWORLD_ENCOUNTERS_H - -// If OWE_WILD_ENCOUNTERS_OVERWORLD is TRUE, it is recommended that OW_GFX_COMPRESS be set to FALSE to prevent VRAM issues. - -#define OWE_VANILLA_RANDOM_ENCOUNTERS TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. -#define OWE_WILD_ENCOUNTERS_OVERWORLD FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. -#define OWE_WILD_ENCOUNTERS_BATTLE_PIKE TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pike, if FALSE random encounters will be enabled instead. Requires OWE_WILD_ENCOUNTERS_OVERWORLD to be TRUE. -#define OWE_WILD_ENCOUNTERS_BATTLE_PYRAMID TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pyramid, if FALSE random encounters will be enabled instead. Requires OWE_WILD_ENCOUNTERS_OVERWORLD to be TRUE. -#define OWE_WILD_ENCOUNTERS_RESTRICT_METATILE TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same encounter metatile behavior as the one it is currently on, if any. -#define OWE_WILD_ENCOUNTERS_RESTRICT_MAP TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within their current map bounds. -#define OWE_WILD_ENCOUNTERS_UNRESTRICT_SIGHT FALSE // If TRUE, OW Pokémon spawned as overworld wild encounters will ignore all movement restrictions when they can see the player. -#define OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. -#define OWE_WILD_ENCOUNTERS_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. -#define OWE_WILD_ENCOUNTERS_SHINY_SPARKLE FALSE // If TRUE, a shiny overworld wild encounter will spawn with a sparkle animation and play the shiny sound effect. -#define OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. -#define OWE_WILD_ENCOUNTERS_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an overworld wild encounter despawns. -#define OWE_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE TRUE // If TRUE, the mon will take steps to be right next to the player before the battle starts. -#define OWE_WILD_ENCOUNTERS_PREVENT_SHINY_DESPAWN FALSE // If TRUE, shiny OW Pokémon objects will not be despawned when off-screen if on the same map as the player, or be replaced if OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. -#define OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_DESPAWN FALSE // If TRUE, Feebas OW objects spawned from special Feebas fishing spots (when OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS is TRUE) will not be despawned when off-screen if on the same map as the player, or be replaced if OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT is TRUE. -#define OWE_WILD_ENCOUNTERS_DESPAWN_ON_ENTER_TOWN TRUE // If TRUE, despawns all OW wild encounter objects upon entering a city or town (MAP_TYPE_TOWN or MAP_TYPE_CITY). - -#endif // GUARD_CONFIG_OVERWORLD_ENCOUNTERS_H diff --git a/include/config/wild_encounter.h b/include/config/wild_encounter.h new file mode 100644 index 000000000000..8bf0ff49d82d --- /dev/null +++ b/include/config/wild_encounter.h @@ -0,0 +1,27 @@ +#ifndef GUARD_CONFIG_WILD_ENCOUNTER_OVERWORLD_H +#define GUARD_CONFIG_WILD_ENCOUNTER_OVERWORLD_H + +// Vanilla +#define WE_VANILLA_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. + +// Overworld Wild Encounters +#define WE_OW_ENCOUNTERS FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. + // If WE_OW_ENCOUNTERS is TRUE, it is recommended that OW_GFX_COMPRESS be set to FALSE to prevent VRAM issues.s +#define WE_OWE_BATTLE_PIKE TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pike, if FALSE random encounters will be enabled instead. Requires WE_OW_ENCOUNTERS to be TRUE. +#define WE_OWE_BATTLE_PYRAMID TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pyramid, if FALSE random encounters will be enabled instead. Requires WE_OW_ENCOUNTERS to be TRUE. +#define WE_OWE_RESTRICT_METATILE TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same encounter metatile behavior as the one it is currently on, if any. +#define WE_OWE_RESTRICT_MAP TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within their current map bounds. +#define WE_OWE_UNRESTRICT_SIGHT FALSE // If TRUE, OW Pokémon spawned as overworld wild encounters will ignore all movement restrictions when they can see the player. +#define WE_OWE_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. +#define WE_OWE_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. +#define WE_OWE_SHINY_SPARKLE FALSE // If TRUE, a shiny overworld wild encounter will spawn with a sparkle animation and play the shiny sound effect. +#define WE_OWE_FEEBAS_SPOTS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. +#define WE_OWE_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an overworld wild encounter despawns. +#define WE_OWE_APPROACH_FOR_BATTLE TRUE // If TRUE, the mon will take steps to be right next to the player before the battle starts. +#define WE_OWE_PREVENT_SHINY_DESPAWN FALSE // If TRUE, shiny OW Pokémon objects will not be despawned when off-screen if on the same map as the player, or be replaced if WE_OWE_SPAWN_REPLACEMENT is TRUE. +#define WE_OWE_PREVENT_FEEBAS_DESPAWN FALSE // If TRUE, Feebas OW objects spawned from special Feebas fishing spots (when WE_OWE_FEEBAS_SPOTS is TRUE) will not be despawned when off-screen if on the same map as the player, or be replaced if WE_OWE_SPAWN_REPLACEMENT is TRUE. +#define WE_OWE_DESPAWN_ON_ENTER_TOWN TRUE // If TRUE, despawns all OW wild encounter objects upon entering a city or town (MAP_TYPE_TOWN or MAP_TYPE_CITY). + +// Should Move Other Configs? + +#endif // GUARD_CONFIG_WILD_ENCOUNTER_OVERWORLD_H diff --git a/include/constants/global.h b/include/constants/global.h index 3bd9af7a0aea..4d6e93dcb211 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -26,9 +26,9 @@ #include "config/general.h" #include "config/item.h" #include "config/overworld.h" -#include "config/overworld_encounters.h" #include "config/pokemon.h" #include "config/summary_screen.h" +#include "config/wild_encounter.h" // Invalid Versions show as "----------" in Gen 4 and Gen 5's summary screen. // In Gens 6 and 7, invalid versions instead show "a distant land" in the summary screen. diff --git a/include/pokemon.h b/include/pokemon.h index e032c9880f59..0df49b427364 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -3,7 +3,7 @@ #include "contest_effect.h" #include "sprite.h" -#include "overworld_encounters.h" +#include "wild_encounter_overworld.h" #include "constants/battle.h" #include "constants/cries.h" #include "constants/egg_ids.h" diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 02d9850daf8e..59aa9c99ceee 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -3,7 +3,7 @@ #include "rtc.h" #include "constants/wild_encounter.h" -#include "overworld_encounters.h" +#include "wild_encounter_overworld.h" #define HEADER_NONE 0xFFFF diff --git a/include/overworld_encounters.h b/include/wild_encounter_overworld.h similarity index 97% rename from include/overworld_encounters.h rename to include/wild_encounter_overworld.h index 9cd23d413cbb..504a0227d16b 100644 --- a/include/overworld_encounters.h +++ b/include/wild_encounter_overworld.h @@ -1,5 +1,5 @@ -#ifndef GUARD_OVERWORLD_ENCOUNTERS_H -#define GUARD_OVERWORLD_ENCOUNTERS_H +#ifndef GUARD_WILD_ENCOUNTER_OVERWORLD_H +#define GUARD_WILD_ENCOUNTER_OVERWORLD_H #define OWE_APPROACH_DISTANCE 2 #define OWE_APPROACH_JUMP_TIMER_MIN 16 @@ -96,4 +96,4 @@ u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent); void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void); -#endif // GUARD_OVERWORLD_ENCOUNTERS_H \ No newline at end of file +#endif // GUARD_WILD_ENCOUNTER_OVERWORLD_H \ No newline at end of file diff --git a/src/battle_setup.c b/src/battle_setup.c index 7097c5689383..e8c583135b8d 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -29,7 +29,6 @@ #include "secret_base.h" #include "string_util.h" #include "overworld.h" -#include "overworld_encounters.h" #include "field_weather.h" #include "battle_tower.h" #include "gym_leader_rematch.h" @@ -46,6 +45,7 @@ #include "item.h" #include "script.h" #include "field_name_box.h" +#include "wild_encounter_overworld.h" #include "constants/battle_frontier.h" #include "constants/battle_setup.h" #include "constants/event_objects.h" diff --git a/src/data/pokemon/overworld_encounter_behaviors.h b/src/data/pokemon/wild_encounter_overworld_behavior.h similarity index 88% rename from src/data/pokemon/overworld_encounter_behaviors.h rename to src/data/pokemon/wild_encounter_overworld_behavior.h index 9baf719f3673..e6b4d016ca31 100644 --- a/src/data/pokemon/overworld_encounter_behaviors.h +++ b/src/data/pokemon/wild_encounter_overworld_behavior.h @@ -1,7 +1,7 @@ -#ifndef GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H -#define GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H +#ifndef GUARD_WILD_ENCOUNTER_OVERWORLD_SPECIES_BEHAVIOR_H +#define GUARD_WILD_ENCOUNTER_OVERWORLD_SPECIES_BEHAVIOR_H -#include "overworld_encounters.h" +#include "wild_encounter_overworld.h" #include "constants/event_object_movement.h" static const struct OWESpeciesBehavior sOWESpeciesBehavior[OWE_SPECIES_BEHAVIOR_COUNT] = @@ -65,4 +65,4 @@ static const struct OWESpeciesBehavior sOWESpeciesBehavior[OWE_SPECIES_BEHAVIOR_ } }; -#endif // GUARD_OVERWORLD_ENCOUNTER_SPECIES_BEHAVIOR_H +#endif // GUARD_WILD_ENCOUNTER_OVERWORLD_SPECIES_BEHAVIOR_H diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 7055294098a3..99d88e8b3d4d 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -25,7 +25,6 @@ #include "mauville_old_man.h" #include "metatile_behavior.h" #include "overworld.h" -#include "overworld_encounters.h" #include "palette.h" #include "party_menu.h" #include "pokemon.h" @@ -41,6 +40,7 @@ #include "trainer_hill.h" #include "util.h" #include "wild_encounter.h" +#include "wild_encounter_overworld.h" #include "constants/event_object_movement.h" #include "constants/abilities.h" #include "constants/battle.h" @@ -12121,7 +12121,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *o bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (OWE_WILD_ENCOUNTERS_FLEE_DESPAWN && sCollisionTimer >= OWE_FLEE_COLLISION_TIME) + if (WE_OWE_FLEE_DESPAWN && sCollisionTimer >= OWE_FLEE_COLLISION_TIME) { RemoveObjectEvent(objectEvent); return FALSE; diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 13ebb0a0a794..65d179eac2f7 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -25,7 +25,6 @@ #include "match_call.h" #include "metatile_behavior.h" #include "overworld.h" -#include "overworld_encounters.h" #include "pokemon.h" #include "safari_zone.h" #include "script.h" @@ -36,6 +35,7 @@ #include "trainer_hill.h" #include "vs_seeker.h" #include "wild_encounter.h" +#include "wild_encounter_overworld.h" #include "constants/event_bg.h" #include "constants/event_objects.h" #include "constants/field_poison.h" diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 92e31b5edf34..f4e5c1dd6d44 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -7,11 +7,11 @@ #include "fieldmap.h" #include "gpu_regs.h" #include "metatile_behavior.h" -#include "overworld_encounters.h" #include "palette.h" #include "sound.h" #include "sprite.h" #include "trig.h" +#include "wild_encounter_overworld.h" #include "constants/event_objects.h" #include "constants/field_effects.h" #include "constants/rgb.h" diff --git a/src/load_save.c b/src/load_save.c index 8cf40d933c5f..6bbcc55077f7 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -7,13 +7,13 @@ #include "load_save.h" #include "main.h" #include "overworld.h" -#include "overworld_encounters.h" #include "pokemon.h" #include "pokemon_storage_system.h" #include "random.h" #include "save_location.h" #include "script_pokemon_util.h" #include "trainer_hill.h" +#include "wild_encounter_overworld.h" #include "gba/flash_internal.h" #include "decoration_inventory.h" #include "agb_flash.h" diff --git a/src/overworld.c b/src/overworld.c index 9e183366f5a2..99325be0e2ab 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -46,7 +46,6 @@ #include "money.h" #include "new_game.h" #include "oras_dowse.h" -#include "overworld_encounters.h" #include "palette.h" #include "play_time.h" #include "random.h" @@ -70,6 +69,7 @@ #include "tv.h" #include "scanline_effect.h" #include "wild_encounter.h" +#include "wild_encounter_overworld.h" #include "vs_seeker.h" #include "frontier_util.h" #include "constants/abilities.h" diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 20e830f95b41..5b18249ecd36 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1,5 +1,5 @@ #include "global.h" -#include "overworld_encounters.h" +#include "wild_encounter_overworld.h" #include "battle_setup.h" #include "battle_main.h" #include "battle_pike.h" @@ -55,7 +55,7 @@ #define OWE_SPAWN_TIME_MINIMUM 30 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. #define OWE_SPAWN_TIME_PER_ACTIVE 30 // The number of frames that will be added to the countdown per currently active spawn. -#define OWE_SPAWN_TIME_REPLACEMENT 240 // The number of frames before an existing spawn will be replaced with a new one (requires OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT). +#define OWE_SPAWN_TIME_REPLACEMENT 240 // The number of frames before an existing spawn will be replaced with a new one (requires WE_OWE_SPAWN_REPLACEMENT). #define OWE_MON_SIGHT_WIDTH 3 #define OWE_MON_SIGHT_LENGTH 4 @@ -65,12 +65,12 @@ #define OWE_INVALID_SPAWN_SLOT 0xFF #define OWE_RESTORED_MOVEMENT_FUNC_ID 10 -#if OWE_WILD_ENCOUNTERS_OVERWORLD == TRUE && ROAMER_COUNT >= OWE_MAX_ROAMERS +#if WE_OW_ENCOUNTERS == TRUE && ROAMER_COUNT >= OWE_MAX_ROAMERS #error "ROAMER_COUNT needs to be less than OWE_MAX_ROAMERS due to it being stored in the u8 field warpArrowSpriteId" #endif -#if OW_POKEMON_OBJECT_EVENTS == FALSE && OWE_WILD_ENCOUNTERS_OVERWORLD == TRUE -#error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OWE_WILD_ENCOUNTERS_OVERWORLD to work." +#if OW_POKEMON_OBJECT_EVENTS == FALSE && WE_OW_ENCOUNTERS == TRUE +#error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for WE_OW_ENCOUNTERS to work." #endif @@ -160,11 +160,11 @@ void UpdateOverworldEncounters(void) if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING) || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) return; - if (!OWE_WILD_ENCOUNTERS_OVERWORLD + if (!WE_OW_ENCOUNTERS || FlagGet(OW_FLAG_NO_ENCOUNTER) || FlagGet(DN_FLAG_SEARCHING) - || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !OWE_WILD_ENCOUNTERS_BATTLE_PIKE) - || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR && !OWE_WILD_ENCOUNTERS_BATTLE_PYRAMID) + || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !WE_OWE_BATTLE_PIKE) + || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR && !WE_OWE_BATTLE_PYRAMID) || InTrainerHillChallenge()) { if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) @@ -257,7 +257,7 @@ static void OWE_SetNewSpawnCountdown(void) { u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); - if (OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && numActive >= OWE_MAX_SPAWNS) + if (WE_OWE_SPAWN_REPLACEMENT && numActive >= OWE_MAX_SPAWNS) sOWESpawnCountdown = OWE_SPAWN_TIME_REPLACEMENT; else sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); @@ -281,7 +281,7 @@ bool32 OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Object(u32 localId, u8 void OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Palette(void) { // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles - if (OWE_WILD_ENCOUNTERS_OVERWORLD && CountFreePaletteSlots() < 2) + if (WE_OW_ENCOUNTERS && CountFreePaletteSlots() < 2) { u32 count = GetNumberActiveOverworldEncounters(OWE_GENERATED); @@ -387,7 +387,7 @@ static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animS if (!animSpawn && OWE_ShouldPlayMonFleeSound(objectEvent)) PlaySE(SE_FLEE); - if (OWE_WILD_ENCOUNTERS_SHINY_SPARKLE && isShiny && animSpawn) + if (WE_OWE_SHINY_SPARKLE && isShiny && animSpawn) { PlaySE(SE_SHINY); spawnAnimType = OWE_SPAWN_ANIM_SHINY; @@ -438,7 +438,7 @@ static u8 NextSpawnMonSlot(void) // All mon slots are in use if (GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_MAX_SPAWNS) { - if (OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT) + if (WE_OWE_SPAWN_REPLACEMENT) { // Cycle through so we remove the oldest mon first spawnSlot = GetOldestSlot(FALSE); @@ -830,7 +830,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 else *isFemale = FALSE; - if ((OWE_WILD_ENCOUNTERS_PREVENT_SHINY_DESPAWN && *isShiny)) + if ((WE_OWE_PREVENT_SHINY_DESPAWN && *isShiny)) OWE_SetNoDespawnFlag(level); ZeroEnemyPartyMons(); @@ -907,10 +907,10 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam { *indexRoamerOutbreak = OWE_GetObjectRoamerStatusFromIndex(gEncounteredRoamerIndex); } - else if (OWE_WILD_ENCOUNTERS_FEEBAS_SPOTS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) + else if (WE_OWE_FEEBAS_SPOTS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) { CreateWildMon(gWildFeebas.species, ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING)); - if (OWE_WILD_ENCOUNTERS_PREVENT_FEEBAS_DESPAWN) + if (WE_OWE_PREVENT_FEEBAS_DESPAWN) OWE_SetNoDespawnFlag(level); } else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior)) @@ -1079,7 +1079,7 @@ u32 GetNewestOWEncounterLocalId(void) bool32 CanRemoveOverworldEncounter(u32 localId) { // Include a check for the encounter not being shiny or a roamer. - return (OWE_WILD_ENCOUNTERS_OVERWORLD && GetNumberActiveOverworldEncounters(OWE_GENERATED) != 0 + return (WE_OW_ENCOUNTERS && GetNumberActiveOverworldEncounters(OWE_GENERATED) != 0 && (localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWNS + 1) || localId > LOCALID_OW_ENCOUNTER_END)); } @@ -1233,7 +1233,7 @@ bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Directi if (GetCollisionInDirection(objectEvent, direction)) return TRUE; - if (OWE_CanAwareMonSeePlayer(objectEvent) && OWE_WILD_ENCOUNTERS_UNRESTRICT_SIGHT) + if (OWE_CanAwareMonSeePlayer(objectEvent) && WE_OWE_UNRESTRICT_SIGHT) return FALSE; s32 xCurrent = objectEvent->currentCoords.x; @@ -1535,7 +1535,7 @@ static bool32 OWE_DoesRoamerObjectExist(void) static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew) { - if (!OWE_WILD_ENCOUNTERS_RESTRICT_METATILE) + if (!WE_OWE_RESTRICT_METATILE) return FALSE; u32 metatileBehaviourCurrent = MapGridGetMetatileBehaviorAt(xCurrent, yCurrent); u32 metatileBehaviourNew = MapGridGetMetatileBehaviorAt(xNew, yNew); @@ -1562,7 +1562,7 @@ static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew) { - if (!OWE_WILD_ENCOUNTERS_RESTRICT_MAP) + if (!WE_OWE_RESTRICT_MAP) return FALSE; if (objectEvent->mapGroup == gSaveBlock1Ptr->location.mapGroup @@ -1586,7 +1586,7 @@ static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) if (objectEvent->offScreen) return FALSE; - return OWE_WILD_ENCOUNTERS_DESPAWN_SOUND; + return WE_OWE_DESPAWN_SOUND; } @@ -1614,11 +1614,11 @@ static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void) { - if ((gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !OWE_WILD_ENCOUNTERS_BATTLE_PIKE) - || (CurrentBattlePyramidLocation() && !OWE_WILD_ENCOUNTERS_BATTLE_PYRAMID)) + if ((gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !WE_OWE_BATTLE_PIKE) + || (CurrentBattlePyramidLocation() && !WE_OWE_BATTLE_PYRAMID)) return FALSE; - return !OWE_VANILLA_RANDOM_ENCOUNTERS; + return !WE_VANILLA_RANDOM; } static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) @@ -1626,7 +1626,7 @@ static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) if (!IsOverworldWildEncounter(object, OWE_GENERATED)) return FALSE; - return OWE_WILD_ENCOUNTERS_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_MAX_SPAWNS; + return WE_OWE_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_MAX_SPAWNS; } void OWE_StartEncounter(struct ObjectEvent *mon) @@ -1717,7 +1717,7 @@ void OWE_ApproachForBattle(void) { u32 objectEventId = GetObjectEventIdByLocalId(gSpecialVar_LastTalked); struct ObjectEvent *objectEvent = &gObjectEvents[objectEventId]; - if (!OWE_WILD_ENCOUNTERS_APPROACH_FOR_BATTLE || !IsOverworldWildEncounter(objectEvent, OWE_ANY)) + if (!WE_OWE_APPROACH_FOR_BATTLE || !IsOverworldWildEncounter(objectEvent, OWE_ANY)) { FreezeObjectEvent(objectEvent); return; @@ -1847,13 +1847,13 @@ void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void) if (gMain.callback2 != CB2_Overworld) return; - if (!OWE_WILD_ENCOUNTERS_DESPAWN_ON_ENTER_TOWN) + if (!WE_OWE_DESPAWN_ON_ENTER_TOWN) return; if (gMapHeader.mapType != MAP_TYPE_CITY && gMapHeader.mapType != MAP_TYPE_TOWN) return; - if (OWE_WILD_ENCOUNTERS_DESPAWN_SOUND) + if (WE_OWE_DESPAWN_SOUND) PlaySE(SE_FLEE); RemoveAllOverworldWildEncounterObjects(OWE_ANY); diff --git a/src/pokemon.c b/src/pokemon.c index c43c9a330293..8b7f6119f062 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -975,11 +975,11 @@ const struct NatureInfo gNaturesInfo[NUM_NATURES] = #endif #include "data/pokemon/teachable_learnsets.h" -#include "data/pokemon/overworld_encounter_behaviors.h" #include "data/pokemon/egg_moves.h" #include "data/pokemon/form_species_tables.h" #include "data/pokemon/form_change_tables.h" #include "data/pokemon/form_change_table_pointers.h" +#include "data/pokemon/wild_encounter_overworld_behavior.h" #include "data/object_events/object_event_pic_tables_followers.h" #include "data/pokemon/species_info.h" From e68e26bb4be082e3071f7fd8e0f30ecc5d9a66ad Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 30 Jan 2026 21:39:16 -0600 Subject: [PATCH 499/572] Adjusted wording in doc --- docs/tutorials/how_to_overworld_wild_encounters.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 2168c46c278e..8ab34436325f 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -13,28 +13,28 @@ Manual OWEs are created by the developer as any other object event would be and - `OBJ_EVENT_GFX_SPECIES_FEMALE(SQUIRTLE)` will produce a female, non-shiny Squirtle. - `OBJ_EVENT_GFX_SPECIES_SHINY_FEMALE(PIKACHU)` will produce a female, shiny Pikachu. -However Manual OWEs do not have to be defined fully, leaving any of the level, species, gender, shinyness or script zeroed will revert to default behaviours and any set parameters used. Leaving the level or species blank will take one the relevant encounter table. Leaving the shinyness blank will revert to default shiny odds, although this can still be affected by `P_FLAG_FORCE_SHINY` and `P_FLAG_FORCE_NO_SHINY`. Setting the `OBJ_EVENT_MON` bit of the `graphicsId`, but not the `OBJ_EVENT_MON_FEMALE` will result in a male encounter, setting both will result in a female encounter, as seen above, but setting neither will randomise the gender based on species. A species can be defined with a random gender by just using the species define. A specific script can be specified, but if not the default OWE encounter script will be used. +However, Manual OWEs do not have to be defined fully, leaving any of the level, species, gender, shinyness or script unspecified will revert to default behaviours. If left blank, the level or species will be generated from the relevant encounter table. Leaving the shinyness blank will revert to default shiny odds, although this can still be affected by `P_FLAG_FORCE_SHINY` and `P_FLAG_FORCE_NO_SHINY`. Setting the `OBJ_EVENT_MON` bit of the `graphicsId`, but not the `OBJ_EVENT_MON_FEMALE` will result in a male encounter, setting both will result in a female encounter, as seen above, but setting neither will randomise the gender based on species. A species can be defined with a random gender by just using the species define. A specific script can be specified, but if not the default OWE encounter script will be used. Assuming the following `graphicsId` have `.trainerType` set to `TRAINER_TYPE_OW_WILD_ENCOUNTER`; - `SPECIES_EEVEE` will result in an Eevee with a randomised level, gender and shinyness, using the default encounter script. - `OBJ_EVENT_GFX_SPECIES(NONE)` will result in a male randomised species of randomised level, gender and shinyness, using the default encounter script. - `OBJ_EVENT_GFX_SPECIES_SHINY_FEMALE(PIKACHU)` will result in a female, shiny randomised species with randomised level and gender, using the default encounter script. -As level and species are potentially taken from the Wild Encounter Header, an `assertf` to let developers know when an invalid value is used. If the resultant level is invalid, it will be set to `MIN_LEVEL` (1). If the species is invalid, a replacement object will be created using `OBJ_EVENT_GFX_BOY_1`, this will not be an OWE of any kind. +As level and species are potentially taken from the Wild Encounter Header, there is an `assertf` to let developers know when an invalid value is used. If the resultant level is invalid, it will be set to `MIN_LEVEL` (1). If the species is invalid, a replacement object will be created using `OBJ_EVENT_GFX_BOY_1`, this will not be an OWE of any kind. -No matter how much of a Manual OWE is defined, it is considered a high priority OWE, and treated as a regular object event in all ways other than ones outlined above. They will always spawn, regardless of level of abilties of player Pokémon, however, they cannot be special spawns. +No matter how much of a Manual OWE is defined, it is considered a high priority OWE, and treated as a regular object event in all ways other than ones outlined above. They will always spawn, regardless of level of abilties of player Pokémon. However, they cannot be special spawns. > Flags are set when removed. ### Special Spawns Special spawns can be one of three types, in decreasing priority: A Roamer, Feebas, or Mass Outbreak Encounter. Generated OWEs can have any of these, however, Manual OWEs can only have the Feebas Special Spawn. These work exactly as they would normally; - If a Roamer is on the route and is able to spawn, then it may appear where a Generated OWE would. -- If any OWE spawns on a tile where a Feebas would spawn, it may appear is a Feebas. +- If any OWE spawns on a tile where a Feebas special fishing spot is, it may appear is a Feebas (only if `WE_OWE_FEEBAS_SPOTS` is TRUE). - If a Generated OWE spawns on a route that has a mass outbreak occuring, it may spawn as an encounter from that mass outbreak. > OWE_MAX_ROAMERS ### Restricted Despawning ## High Priority and Low Priority OWEs -Low Priority OWEs may face not be spawned or even be destroyed in certain situations. There are palettes and object tiles checks to prevent these from spawning if it would fail, as well as similar checks for number of event objects, palettes and object tiles that despawn the oldest of High Priority OWEs or other objects event are attempting to be spawned and Low Priority OWEs are using these resources. Low Priority OWEs may also be destroyed by NPC object events colliding with them due to their movement functions or them being in the way of a trainer interaction. High priority OWEs are treated as regular objects and will not be destroyed in the ways outlined above, but may cause the destruction of Generated OWEs and will not face spawning restrictions. +Low Priority OWEs may not be spawned or even be destroyed in certain situations. There are palettes and object tiles checks to prevent these from spawning if it would fail, as well as similar checks for number of event objects, palettes and object tiles. These checks will despawn the oldest of Low Priority OWEs when other objects event are attempting to be spawned and Low Priority OWEs are using these resources. Low Priority OWEs may also be destroyed by NPC object events colliding with them due to their movement functions or them being in the way of a trainer interaction. High priority OWEs are treated as regular objects and will not be destroyed in the ways outlined above, but may cause the destruction of Generated OWEs and will not face spawning restrictions. These despawn conditions will overwrite the restrictive despawns mentioned above. > Is this true? Does it take the oldest or oldest not marked for restriction, what if all are marked to not despawn. From d25405727259b6c04e58d363a176973ff3fac272 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 30 Jan 2026 21:55:03 -0600 Subject: [PATCH 500/572] Wrote Restricted Despawning section in the doc --- docs/tutorials/how_to_overworld_wild_encounters.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 8ab34436325f..001931f54f2c 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -33,8 +33,17 @@ Special spawns can be one of three types, in decreasing priority: A Roamer, Feeb > OWE_MAX_ROAMERS ### Restricted Despawning +There are three configs that can be used to restrict the despawning of Generated OWEs. + +- If `WE_OWE_PREVENT_SHINY_DESPAWN` is set to `TRUE`, any Generated OWE that spawns as shiny will not be despawned if they go outside of the viewable area. They can still be despawned off-screen if the player goes to another map. This config also ensures that shiny Generated OWEs will never be despawned and replaced if `WE_OWE_SPAWN_REPLACEMENT` is `TRUE`. +- If `WE_OWE_PREVENT_FEEBAS_DESPAWN` is set to `TRUE`, any Generated OWE that is a Feebas spawned from a Feebas fishing spot will follow the same rules as `WE_OWE_PREVENT_SHINY_DESPAWN` above. +- If `WE_OWE_DESPAWN_ON_ENTER_TOWN` is set to `TRUE`, all Generated OWEs will be instantly despawned upon the player crossing a map connection into a map with a map type of either `MAP_TYPE_TOWN` or `MAP_TYPE_CITY`, like what happens in Scarlet/Violet. + +None of these three configs can prevent the forceful despawning of OWEs to free up limited resources, as is explained in the next section. + + ## High Priority and Low Priority OWEs -Low Priority OWEs may not be spawned or even be destroyed in certain situations. There are palettes and object tiles checks to prevent these from spawning if it would fail, as well as similar checks for number of event objects, palettes and object tiles. These checks will despawn the oldest of Low Priority OWEs when other objects event are attempting to be spawned and Low Priority OWEs are using these resources. Low Priority OWEs may also be destroyed by NPC object events colliding with them due to their movement functions or them being in the way of a trainer interaction. High priority OWEs are treated as regular objects and will not be destroyed in the ways outlined above, but may cause the destruction of Generated OWEs and will not face spawning restrictions. +Low Priority OWEs may not be spawned or even be destroyed in certain situations. There are palettes and object tiles checks to prevent these from spawning if it would fail, as well as similar checks for number of event objects, palettes and object tiles. These checks will despawn the oldest of Low Priority OWEs when other objects event are attempting to be spawned and Low Priority OWEs are using these resources. Low Priority OWEs may also be destroyed by NPC object events colliding with them due to their movements or the OWE being in the way of a trainer interaction. High priority OWEs are treated as regular objects and will not be destroyed in the ways outlined above, but may cause the destruction of Generated OWEs and will not face spawning restrictions. These despawn conditions will overwrite the restrictive despawns mentioned above. > Is this true? Does it take the oldest or oldest not marked for restriction, what if all are marked to not despawn. From f2c00daff63919f0e79eebf5beb1ccc244557445 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:29:46 -0600 Subject: [PATCH 501/572] Wrote OWE Movements section of the doc --- docs/tutorials/how_to_overworld_wild_encounters.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 001931f54f2c..81faf8ceb1a8 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -53,4 +53,12 @@ Collision between Player and OWE or Interacting with one. Can also interact with ## Repel and Lure Behaviours ## OWE Behaviour Types ## OWE Movements +While OWE objects can be given any movement type, there are several different custom movement types that were made specifically for OWEs: +- The basic one is `MOVEMENT_TYPE_WANDER_AROUND_OWE`. All of the other OWE movement types start with this behavior and differentiate into other behavior when they notice the player. Similar to `MOVEMENT_TYPE_WANDER_AROUND`, the object will take steps in random directions at random intervals. The main difference here is that the object will never turn more than 90 degrees from their current facing direction when taking a step. OWEs with this movement type will completely ignore the player until they are directly collided/interacted with. +- `MOVEMENT_TYPE_CHASE_PLAYER_OWE` will switch the OWE to chasing down the player when it notices the player. The OWE is still blocked by collision, but does have the intelligence to walk around small obstacles. They will go back to wandering if the player goes outside of their sight radius. +- `MOVEMENT_TYPE_FLEE_PLAYER_OWE` will switch the OWE to fleeing from the player when it notices the player. They use the same pathfinding logic as `MOVEMENT_TYPE_CHASE_PLAYER_OWE`, but in the opposite direction from the player. They will go back to wandering if the player goes outside of their sight radius. If `WE_OWE_FLEE_DESPAWN` is set to `TRUE`, the fleeing OWE will despawn if it is unable to take a step for a short amount of time (ie, if they are cornered). +- `MOVEMENT_TYPE_WATCH_PLAYER_OWE` will switch the OWE to stand in place and always face in the direction of the player's location when the player is noticed by it. They will go back to wandering if the player goes outside of their sight radius. +- `MOVEMENT_TYPE_APPROACH_PLAYER_OWE` will switch the OWE to approach the player as if curious when it notices the player. The OWE will try to keep a one tile gap between itself and the player. They may also occasionally do an excited hop. They will go back to wandering if the player goes outside of their sight radius. +- `MOVEMENT_TYPE_DESPAWN_OWE` will make the OWE do a very brief animation of surprise and then instantly despawn when it notices the player. + ### Restricted Movements From 60c42d04e5706443e0f686d4bc36f8edd09b49e0 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 31 Jan 2026 19:23:51 +0000 Subject: [PATCH 502/572] Add WE_OWE_ALLOW_REPEL_DEXNAV_COLLISION --- include/config/wild_encounter.h | 1 + src/overworld_encounters.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/config/wild_encounter.h b/include/config/wild_encounter.h index 8bf0ff49d82d..bd108ab95b3a 100644 --- a/include/config/wild_encounter.h +++ b/include/config/wild_encounter.h @@ -21,6 +21,7 @@ #define WE_OWE_PREVENT_SHINY_DESPAWN FALSE // If TRUE, shiny OW Pokémon objects will not be despawned when off-screen if on the same map as the player, or be replaced if WE_OWE_SPAWN_REPLACEMENT is TRUE. #define WE_OWE_PREVENT_FEEBAS_DESPAWN FALSE // If TRUE, Feebas OW objects spawned from special Feebas fishing spots (when WE_OWE_FEEBAS_SPOTS is TRUE) will not be despawned when off-screen if on the same map as the player, or be replaced if WE_OWE_SPAWN_REPLACEMENT is TRUE. #define WE_OWE_DESPAWN_ON_ENTER_TOWN TRUE // If TRUE, despawns all OW wild encounter objects upon entering a city or town (MAP_TYPE_TOWN or MAP_TYPE_CITY). +#define WE_OWE_REPEL_DEXNAV_COLLISION FALSE // If TRUE, overworld wild encounters can still be triggered by a collision if a Repel or the DexNav is active. // Should Move Other Configs? diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5b18249ecd36..f15e4eb0c975 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -1193,7 +1193,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c { // The only automatically interacts with an OW Encounter when; // Not using a repel or the DexNav is inactive. - if (REPEL_STEP_COUNT || FlagGet(DN_FLAG_SEARCHING)) + if (!WE_OWE_REPEL_DEXNAV_COLLISION && (REPEL_STEP_COUNT || FlagGet(DN_FLAG_SEARCHING))) return; bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle, OWE_ANY)); From 0df238c5fb712749e9bec9d2369571a97b62e145 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 31 Jan 2026 16:43:52 -0600 Subject: [PATCH 503/572] Try spawn every frame when lure is active --- src/overworld_encounters.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 5b18249ecd36..eb69356495cf 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -65,6 +65,8 @@ #define OWE_INVALID_SPAWN_SLOT 0xFF #define OWE_RESTORED_MOVEMENT_FUNC_ID 10 +#define OWE_LURE_SPAWN_TIME 0 + #if WE_OW_ENCOUNTERS == TRUE && ROAMER_COUNT >= OWE_MAX_ROAMERS #error "ROAMER_COUNT needs to be less than OWE_MAX_ROAMERS due to it being stored in the u8 field warpArrowSpriteId" #endif @@ -179,13 +181,7 @@ void UpdateOverworldEncounters(void) OverworldWildEncounter_SetMinimumSpawnTimer(); } - u16 spawnSlot = NextSpawnMonSlot(); - if (LURE_STEP_COUNT && spawnSlot != OWE_INVALID_SPAWN_SLOT - && (GetNumberActiveOverworldEncounters(OWE_GENERATED) < OWE_MAX_SPAWNS)) - { - OverworldWildEncounter_SetMinimumSpawnTimer(); - } - else if (sOWESpawnCountdown) + if (sOWESpawnCountdown) { sOWESpawnCountdown--; return; @@ -194,6 +190,7 @@ void UpdateOverworldEncounters(void) if (!IsSafeToSpawnObjectEvents()) return; + u16 spawnSlot = NextSpawnMonSlot(); s16 x, y; if (spawnSlot == OWE_INVALID_SPAWN_SLOT || (shouldSpawnWaterMons && AreLegendariesInSootopolisPreventingEncounters()) @@ -259,6 +256,8 @@ static void OWE_SetNewSpawnCountdown(void) if (WE_OWE_SPAWN_REPLACEMENT && numActive >= OWE_MAX_SPAWNS) sOWESpawnCountdown = OWE_SPAWN_TIME_REPLACEMENT; + else if (LURE_STEP_COUNT && numActive < OWE_MAX_SPAWNS) + sOWESpawnCountdown = OWE_LURE_SPAWN_TIME; else sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); } @@ -804,6 +803,9 @@ u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, boo void OverworldWildEncounter_SetMinimumSpawnTimer(void) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + + if (LURE_STEP_COUNT && GetNumberActiveOverworldEncounters(OWE_GENERATED) < OWE_MAX_SPAWNS) + sOWESpawnCountdown = OWE_LURE_SPAWN_TIME; } static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) From 575d1fa676f61eb5b85ba3de1827367f9d58d1b7 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 31 Jan 2026 22:56:34 +0000 Subject: [PATCH 504/572] Change SpawnAnim Subpriority --- src/field_effect_helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index f4e5c1dd6d44..72302a93c7f5 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1560,7 +1560,7 @@ u32 FldEff_OWE_SpawnAnim(void) FieldEffect_LoadFadedPalette(&palette, COLOR_MAP_DARK_CONTRAST); SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 0); - spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[visual], gFieldEffectArguments[0] + xOffset, gFieldEffectArguments[1] + yOffset, 82); + spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[visual], gFieldEffectArguments[0] + xOffset, gFieldEffectArguments[1] + yOffset, 0); if (spriteId != MAX_SPRITES) { struct Sprite *sprite = &gSprites[spriteId]; From 35bddb5db9b4a01c7e90ba23d7f113b2c578fdda Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 31 Jan 2026 23:02:24 +0000 Subject: [PATCH 505/572] Update error calling --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 95bb33574e54..893f41f3bfeb 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -67,7 +67,7 @@ #define OWE_LURE_SPAWN_TIME 0 -#if WE_OW_ENCOUNTERS == TRUE && ROAMER_COUNT >= OWE_MAX_ROAMERS +#if WE_OW_ENCOUNTERS == TRUE && ROAMER_COUNT > OWE_MAX_ROAMERS #error "ROAMER_COUNT needs to be less than OWE_MAX_ROAMERS due to it being stored in the u8 field warpArrowSpriteId" #endif From 778caf76f9eccf2e17e66a070d75051270301867 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 31 Jan 2026 23:12:24 +0000 Subject: [PATCH 506/572] Rename Defines --- src/overworld_encounters.c | 74 +++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 893f41f3bfeb..7b5d256a17c1 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -45,27 +45,27 @@ #define OWE_SAVED_MOVEMENT_STATE_FLAG OWE_FLAG_BIT #define OWE_NO_DESPAWN_FLAG OWE_FLAG_BIT -#define OWE_MAX_SPAWNS 4 +#define OWE_SPAWNS_MAX 4 #define OWE_SPAWN_DISTANCE_LAND 1 // A spawn cannot happen within this many tiles of the player position. #define OWE_SPAWN_DISTANCE_WATER 3 // A spawn cannot happen within this many tiles of the player position (while surfing). -#define OWE_TOTAL_SPAWN_WIDTH 15 // Width of the on-screen spawn area in tiles. -#define OWE_TOTAL_SPAWN_HEIGHT 9 // Height of the on-screen spawn area in tiles. -#define OWE_SPAWN_RADUIS_WIDTH (OWE_TOTAL_SPAWN_WIDTH - 1) / 2 // Distance from center to left/right edge (not including center). -#define OWE_SPAWN_RADUIS_HEIGHT (OWE_TOTAL_SPAWN_HEIGHT - 1) / 2 // Distance from center to top/bottom edge (not including center). +#define OWE_SPAWN_WIDTH_TOTAL 15 // Width of the on-screen spawn area in tiles. +#define OWE_SPAWN_HEIGHT_TOTAL 9 // Height of the on-screen spawn area in tiles. +#define OWE_SPAWN_WIDTH_RADIUS (OWE_SPAWN_WIDTH_TOTAL - 1) / 2 // Distance from center to left/right edge (not including center). +#define OWE_SPAWN_HEIGHT_RADIUS (OWE_SPAWN_HEIGHT_TOTAL - 1) / 2 // Distance from center to top/bottom edge (not including center). +#define OWE_SPAWN_TIME_REPLACEMENT 240 // The number of frames before an existing spawn will be replaced with a new one (requires WE_OWE_SPAWN_REPLACEMENT). +#define OWE_SPAWN_TIME_LURE 0 #define OWE_SPAWN_TIME_MINIMUM 30 // The minimum value the spawn wait time can be reset to. Prevents spawn attempts every frame. #define OWE_SPAWN_TIME_PER_ACTIVE 30 // The number of frames that will be added to the countdown per currently active spawn. -#define OWE_SPAWN_TIME_REPLACEMENT 240 // The number of frames before an existing spawn will be replaced with a new one (requires WE_OWE_SPAWN_REPLACEMENT). #define OWE_MON_SIGHT_WIDTH 3 #define OWE_MON_SIGHT_LENGTH 4 #define OWE_CHASE_RANGE 5 +#define OWE_RESTORED_MOVEMENT_FUNC_ID 10 #define OWE_NO_ENCOUNTER_SET 0xFF #define OWE_INVALID_SPAWN_SLOT 0xFF -#define OWE_RESTORED_MOVEMENT_FUNC_ID 10 -#define OWE_LURE_SPAWN_TIME 0 #if WE_OW_ENCOUNTERS == TRUE && ROAMER_COUNT > OWE_MAX_ROAMERS #error "ROAMER_COUNT needs to be less than OWE_MAX_ROAMERS due to it being stored in the u8 field warpArrowSpriteId" @@ -254,10 +254,10 @@ static void OWE_SetNewSpawnCountdown(void) { u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); - if (WE_OWE_SPAWN_REPLACEMENT && numActive >= OWE_MAX_SPAWNS) + if (WE_OWE_SPAWN_REPLACEMENT && numActive >= OWE_SPAWNS_MAX) sOWESpawnCountdown = OWE_SPAWN_TIME_REPLACEMENT; - else if (LURE_STEP_COUNT && numActive < OWE_MAX_SPAWNS) - sOWESpawnCountdown = OWE_LURE_SPAWN_TIME; + else if (LURE_STEP_COUNT && numActive < OWE_SPAWNS_MAX) + sOWESpawnCountdown = OWE_SPAWN_TIME_LURE; else sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); } @@ -404,7 +404,7 @@ u32 GetOldestSlot(bool32 forceRemove) struct ObjectEvent *slotMon, *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; u32 spawnSlot; - for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWNS; spawnSlot++) + for (spawnSlot = 0; spawnSlot < OWE_SPAWNS_MAX; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; if (OW_SPECIES(slotMon) != SPECIES_NONE && (!OWE_HasNoDespawnFlag(slotMon) || forceRemove == TRUE)) @@ -414,10 +414,10 @@ u32 GetOldestSlot(bool32 forceRemove) } } - if (spawnSlot >= OWE_MAX_SPAWNS) + if (spawnSlot >= OWE_SPAWNS_MAX) return OWE_INVALID_SPAWN_SLOT; - for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWNS; spawnSlot++) + for (spawnSlot = 0; spawnSlot < OWE_SPAWNS_MAX; spawnSlot++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; if (OW_SPECIES(slotMon) != SPECIES_NONE && (!OWE_HasNoDespawnFlag(slotMon) || forceRemove == TRUE)) @@ -435,7 +435,7 @@ static u8 NextSpawnMonSlot(void) u32 spawnSlot; // All mon slots are in use - if (GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_MAX_SPAWNS) + if (GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_SPAWNS_MAX) { if (WE_OWE_SPAWN_REPLACEMENT) { @@ -451,7 +451,7 @@ static u8 NextSpawnMonSlot(void) } else { - for (spawnSlot = 0; spawnSlot < OWE_MAX_SPAWNS; spawnSlot++) + for (spawnSlot = 0; spawnSlot < OWE_SPAWNS_MAX; spawnSlot++) { if (GetOverworldSpeciesBySpawnSlot(spawnSlot) == SPECIES_NONE) break; @@ -482,8 +482,8 @@ static bool8 TrySelectTile(s16* outX, s16* outY) // Have defines used and then replace MAP_METATILE_VIEW_X/Y with them do { - x = (s16)(Random() % OWE_TOTAL_SPAWN_WIDTH) - OWE_SPAWN_RADUIS_WIDTH; - y = (s16)(Random() % OWE_TOTAL_SPAWN_HEIGHT) - OWE_SPAWN_RADUIS_HEIGHT; + x = (s16)(Random() % OWE_SPAWN_WIDTH_TOTAL) - OWE_SPAWN_WIDTH_RADIUS; + y = (s16)(Random() % OWE_SPAWN_HEIGHT_TOTAL) - OWE_SPAWN_HEIGHT_RADIUS; } while (abs(x) <= closeDistance && abs(y) <= closeDistance); @@ -711,16 +711,16 @@ struct AgeSort static void SortOWEMonAges(void) { struct ObjectEvent *slotMon; - struct AgeSort array[OWE_MAX_SPAWNS]; + struct AgeSort array[OWE_SPAWNS_MAX]; struct AgeSort current; u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); u32 count = 0; s32 i, j; - if (OWE_MAX_SPAWNS <= 1) + if (OWE_SPAWNS_MAX <= 1) return; - for (i = 0; i < OWE_MAX_SPAWNS; i++) + for (i = 0; i < OWE_SPAWNS_MAX; i++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; if (OW_SPECIES(slotMon) != SPECIES_NONE) @@ -804,8 +804,8 @@ void OverworldWildEncounter_SetMinimumSpawnTimer(void) { sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - if (LURE_STEP_COUNT && GetNumberActiveOverworldEncounters(OWE_GENERATED) < OWE_MAX_SPAWNS) - sOWESpawnCountdown = OWE_LURE_SPAWN_TIME; + if (LURE_STEP_COUNT && GetNumberActiveOverworldEncounters(OWE_GENERATED) < OWE_SPAWNS_MAX) + sOWESpawnCountdown = OWE_SPAWN_TIME_LURE; } static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) @@ -1030,11 +1030,11 @@ bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldO case OWE_GENERATED: return isOWE && (objectEvent->localId <= LOCALID_OW_ENCOUNTER_END - && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWNS)); + && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); case OWE_MANUAL: return isOWE && (objectEvent->localId > LOCALID_OW_ENCOUNTER_END - || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWNS)); + || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); } } @@ -1065,7 +1065,7 @@ u32 GetNewestOWEncounterLocalId(void) struct ObjectEvent *newest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; u32 i; - for (i = 0; i < OWE_MAX_SPAWNS; i++) + for (i = 0; i < OWE_SPAWNS_MAX; i++) { slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; if (OW_SPECIES(slotMon) != SPECIES_NONE) @@ -1082,7 +1082,7 @@ bool32 CanRemoveOverworldEncounter(u32 localId) { // Include a check for the encounter not being shiny or a roamer. return (WE_OW_ENCOUNTERS && GetNumberActiveOverworldEncounters(OWE_GENERATED) != 0 - && (localId <= (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWNS + 1) + && (localId <= (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX + 1) || localId > LOCALID_OW_ENCOUNTER_END)); } @@ -1116,7 +1116,7 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) { if (template->trainerType != TRAINER_TYPE_OW_WILD_ENCOUNTER || (template->localId <= LOCALID_OW_ENCOUNTER_END - && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_MAX_SPAWNS))) + && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX))) return *template; struct ObjectEventTemplate templateOWE = *template; @@ -1499,26 +1499,26 @@ static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) u32 speciesId = OW_SPECIES(objectEvent); s32 distanceX = objectEvent->currentCoords.x - player->currentCoords.x; s32 distanceY = objectEvent->currentCoords.y - player->currentCoords.y; - u32 distanceMax = OWE_SPAWN_RADUIS_WIDTH + OWE_SPAWN_RADUIS_HEIGHT; + u32 distanceMax = OWE_SPAWN_WIDTH_RADIUS + OWE_SPAWN_HEIGHT_RADIUS; u32 distance; u32 volume; s32 pan; - if (distanceX > OWE_SPAWN_RADUIS_WIDTH) - distanceX = OWE_SPAWN_RADUIS_WIDTH; - else if (distanceX < -OWE_SPAWN_RADUIS_WIDTH) - distanceX = -OWE_SPAWN_RADUIS_WIDTH; + if (distanceX > OWE_SPAWN_WIDTH_RADIUS) + distanceX = OWE_SPAWN_WIDTH_RADIUS; + else if (distanceX < -OWE_SPAWN_WIDTH_RADIUS) + distanceX = -OWE_SPAWN_WIDTH_RADIUS; distanceY = abs(distanceY); - if (distanceY > OWE_SPAWN_RADUIS_HEIGHT) - distanceY = OWE_SPAWN_RADUIS_HEIGHT; + if (distanceY > OWE_SPAWN_HEIGHT_RADIUS) + distanceY = OWE_SPAWN_HEIGHT_RADIUS; distance = abs(distanceX) + distanceY; if (distance > distanceMax) distance = distanceMax; volume = 80 - (distance * (80 - 50)) / distanceMax; - pan = 212 + ((distanceX + OWE_SPAWN_RADUIS_WIDTH) * (300 - 212)) / (2 * OWE_SPAWN_RADUIS_WIDTH); + pan = 212 + ((distanceX + OWE_SPAWN_WIDTH_RADIUS) * (300 - 212)) / (2 * OWE_SPAWN_WIDTH_RADIUS); PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); } @@ -1628,7 +1628,7 @@ static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) if (!IsOverworldWildEncounter(object, OWE_GENERATED)) return FALSE; - return WE_OWE_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_MAX_SPAWNS; + return WE_OWE_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_SPAWNS_MAX; } void OWE_StartEncounter(struct ObjectEvent *mon) From b9ea868bd37896c23dd843337d4dc12ef0bc5bab Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 31 Jan 2026 23:21:38 +0000 Subject: [PATCH 507/572] Rename Files Again, and the missed one --- include/config/wild_encounter.h | 6 +++--- include/pokemon.h | 2 +- include/wild_encounter.h | 2 +- .../{wild_encounter_overworld.h => wild_encounter_ow.h} | 6 +++--- src/battle_setup.c | 2 +- ..._overworld_behavior.h => wild_encounter_ow_behavior.h} | 8 ++++---- src/event_object_movement.c | 2 +- src/field_control_avatar.c | 2 +- src/field_effect_helpers.c | 2 +- src/load_save.c | 2 +- src/overworld.c | 2 +- src/pokemon.c | 2 +- src/{overworld_encounters.c => wild_encounter_ow.c} | 2 +- 13 files changed, 20 insertions(+), 20 deletions(-) rename include/{wild_encounter_overworld.h => wild_encounter_ow.h} (97%) rename src/data/pokemon/{wild_encounter_overworld_behavior.h => wild_encounter_ow_behavior.h} (88%) rename src/{overworld_encounters.c => wild_encounter_ow.c} (99%) diff --git a/include/config/wild_encounter.h b/include/config/wild_encounter.h index bd108ab95b3a..b9140ee943d9 100644 --- a/include/config/wild_encounter.h +++ b/include/config/wild_encounter.h @@ -1,5 +1,5 @@ -#ifndef GUARD_CONFIG_WILD_ENCOUNTER_OVERWORLD_H -#define GUARD_CONFIG_WILD_ENCOUNTER_OVERWORLD_H +#ifndef GUARD_CONFIG_WILD_ENCOUNTER_OW_H +#define GUARD_CONFIG_WILD_ENCOUNTER_OW_H // Vanilla #define WE_VANILLA_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. @@ -25,4 +25,4 @@ // Should Move Other Configs? -#endif // GUARD_CONFIG_WILD_ENCOUNTER_OVERWORLD_H +#endif // GUARD_CONFIG_WILD_ENCOUNTER_OW_H diff --git a/include/pokemon.h b/include/pokemon.h index 0df49b427364..ad259b62a6dd 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -3,7 +3,7 @@ #include "contest_effect.h" #include "sprite.h" -#include "wild_encounter_overworld.h" +#include "wild_encounter_ow.h" #include "constants/battle.h" #include "constants/cries.h" #include "constants/egg_ids.h" diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 59aa9c99ceee..ec55505c3d34 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -3,7 +3,7 @@ #include "rtc.h" #include "constants/wild_encounter.h" -#include "wild_encounter_overworld.h" +#include "wild_encounter_ow.h" #define HEADER_NONE 0xFFFF diff --git a/include/wild_encounter_overworld.h b/include/wild_encounter_ow.h similarity index 97% rename from include/wild_encounter_overworld.h rename to include/wild_encounter_ow.h index 504a0227d16b..d4949deb9863 100644 --- a/include/wild_encounter_overworld.h +++ b/include/wild_encounter_ow.h @@ -1,5 +1,5 @@ -#ifndef GUARD_WILD_ENCOUNTER_OVERWORLD_H -#define GUARD_WILD_ENCOUNTER_OVERWORLD_H +#ifndef GUARD_WILD_ENCOUNTER_OW_H +#define GUARD_WILD_ENCOUNTER_OW_H #define OWE_APPROACH_DISTANCE 2 #define OWE_APPROACH_JUMP_TIMER_MIN 16 @@ -96,4 +96,4 @@ u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent); void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void); -#endif // GUARD_WILD_ENCOUNTER_OVERWORLD_H \ No newline at end of file +#endif // GUARD_WILD_ENCOUNTER_OW_H \ No newline at end of file diff --git a/src/battle_setup.c b/src/battle_setup.c index e8c583135b8d..03bded7a9444 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -45,7 +45,7 @@ #include "item.h" #include "script.h" #include "field_name_box.h" -#include "wild_encounter_overworld.h" +#include "wild_encounter_ow.h" #include "constants/battle_frontier.h" #include "constants/battle_setup.h" #include "constants/event_objects.h" diff --git a/src/data/pokemon/wild_encounter_overworld_behavior.h b/src/data/pokemon/wild_encounter_ow_behavior.h similarity index 88% rename from src/data/pokemon/wild_encounter_overworld_behavior.h rename to src/data/pokemon/wild_encounter_ow_behavior.h index e6b4d016ca31..f98c1d9438ff 100644 --- a/src/data/pokemon/wild_encounter_overworld_behavior.h +++ b/src/data/pokemon/wild_encounter_ow_behavior.h @@ -1,7 +1,7 @@ -#ifndef GUARD_WILD_ENCOUNTER_OVERWORLD_SPECIES_BEHAVIOR_H -#define GUARD_WILD_ENCOUNTER_OVERWORLD_SPECIES_BEHAVIOR_H +#ifndef GUARD_WILD_ENCOUNTER_OW_SPECIES_BEHAVIOR_H +#define GUARD_WILD_ENCOUNTER_OW_SPECIES_BEHAVIOR_H -#include "wild_encounter_overworld.h" +#include "wild_encounter_ow.h" #include "constants/event_object_movement.h" static const struct OWESpeciesBehavior sOWESpeciesBehavior[OWE_SPECIES_BEHAVIOR_COUNT] = @@ -65,4 +65,4 @@ static const struct OWESpeciesBehavior sOWESpeciesBehavior[OWE_SPECIES_BEHAVIOR_ } }; -#endif // GUARD_WILD_ENCOUNTER_OVERWORLD_SPECIES_BEHAVIOR_H +#endif // GUARD_WILD_ENCOUNTER_OW_SPECIES_BEHAVIOR_H diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 99d88e8b3d4d..8e1fb4f7e788 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -40,7 +40,7 @@ #include "trainer_hill.h" #include "util.h" #include "wild_encounter.h" -#include "wild_encounter_overworld.h" +#include "wild_encounter_ow.h" #include "constants/event_object_movement.h" #include "constants/abilities.h" #include "constants/battle.h" diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 65d179eac2f7..86ec075d0aa0 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -35,7 +35,7 @@ #include "trainer_hill.h" #include "vs_seeker.h" #include "wild_encounter.h" -#include "wild_encounter_overworld.h" +#include "wild_encounter_ow.h" #include "constants/event_bg.h" #include "constants/event_objects.h" #include "constants/field_poison.h" diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 72302a93c7f5..18171ff08a84 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -11,7 +11,7 @@ #include "sound.h" #include "sprite.h" #include "trig.h" -#include "wild_encounter_overworld.h" +#include "wild_encounter_ow.h" #include "constants/event_objects.h" #include "constants/field_effects.h" #include "constants/rgb.h" diff --git a/src/load_save.c b/src/load_save.c index 6bbcc55077f7..7d5ca2311c4e 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -13,7 +13,7 @@ #include "save_location.h" #include "script_pokemon_util.h" #include "trainer_hill.h" -#include "wild_encounter_overworld.h" +#include "wild_encounter_ow.h" #include "gba/flash_internal.h" #include "decoration_inventory.h" #include "agb_flash.h" diff --git a/src/overworld.c b/src/overworld.c index 99325be0e2ab..f963287f6bf6 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -69,7 +69,7 @@ #include "tv.h" #include "scanline_effect.h" #include "wild_encounter.h" -#include "wild_encounter_overworld.h" +#include "wild_encounter_ow.h" #include "vs_seeker.h" #include "frontier_util.h" #include "constants/abilities.h" diff --git a/src/pokemon.c b/src/pokemon.c index 8b7f6119f062..c0b2da3757d8 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -979,7 +979,7 @@ const struct NatureInfo gNaturesInfo[NUM_NATURES] = #include "data/pokemon/form_species_tables.h" #include "data/pokemon/form_change_tables.h" #include "data/pokemon/form_change_table_pointers.h" -#include "data/pokemon/wild_encounter_overworld_behavior.h" +#include "data/pokemon/wild_encounter_ow_behavior.h" #include "data/object_events/object_event_pic_tables_followers.h" #include "data/pokemon/species_info.h" diff --git a/src/overworld_encounters.c b/src/wild_encounter_ow.c similarity index 99% rename from src/overworld_encounters.c rename to src/wild_encounter_ow.c index 7b5d256a17c1..8b45cd28e1b7 100644 --- a/src/overworld_encounters.c +++ b/src/wild_encounter_ow.c @@ -1,5 +1,5 @@ #include "global.h" -#include "wild_encounter_overworld.h" +#include "wild_encounter_ow.h" #include "battle_setup.h" #include "battle_main.h" #include "battle_pike.h" From a0398e17ef9728944a7f53135e456f65b44f9ab1 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sat, 31 Jan 2026 23:26:27 +0000 Subject: [PATCH 508/572] Rename Scripts --- data/scripts/wild_encounter.inc | 2 +- include/wild_encounter_ow.h | 2 +- src/field_control_avatar.c | 4 ++-- src/wild_encounter_ow.c | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/data/scripts/wild_encounter.inc b/data/scripts/wild_encounter.inc index e91f2be2c98b..fe2bf05c75c5 100644 --- a/data/scripts/wild_encounter.inc +++ b/data/scripts/wild_encounter.inc @@ -1,4 +1,4 @@ -InteractWithDynamicWildOverworldEncounter:: +InteractWithDynamicOverworldWildEncounter:: lock overworldwildencounterapproach applymovement VAR_LAST_TALKED, Common_Movement_ExclamationMark diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index d4949deb9863..2b5dceecbd35 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -55,7 +55,7 @@ enum __attribute__((packed)) OverworldEncounterBehaviors OWE_SPECIES_BEHAVIOR_COUNT }; -extern const u8 InteractWithDynamicWildOverworldEncounter[]; +extern const u8 InteractWithDynamicOverworldWildEncounter[]; void UpdateOverworldEncounters(void); u32 GetOldestSlot(bool32 forceRemove); diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 86ec075d0aa0..5ef8fe8bffc4 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -407,7 +407,7 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 else if (PlayerHasFollowerNPC() && objectEventId == GetFollowerNPCObjectId()) script = GetFollowerNPCScriptPointer(); else if (ShouldRunOverworldEncounterScript(objectEventId)) - script = InteractWithDynamicWildOverworldEncounter; + script = InteractWithDynamicOverworldWildEncounter; else script = GetObjectEventScriptPointerByObjectEventId(objectEventId); @@ -637,7 +637,7 @@ static const u8 *GetInteractedWaterScript(struct MapPosition *position, u8 metat if (IsPlayerFacingSurfableFishableWater() == TRUE && ShouldRunOverworldEncounterScript(objectEventId)) { gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; - return InteractWithDynamicWildOverworldEncounter; + return InteractWithDynamicOverworldWildEncounter; } if (MetatileBehavior_IsFastWater(metatileBehavior) == TRUE && !TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING)) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 8b45cd28e1b7..62264862fa18 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -225,7 +225,7 @@ void UpdateOverworldEncounters(void) .elevation = MapGridGetElevationAt(x, y), .movementType = OWE_GetMovementTypeFromSpecies(speciesId), .trainerType = TRAINER_TYPE_OW_WILD_ENCOUNTER, - .script = InteractWithDynamicWildOverworldEncounter, + .script = InteractWithDynamicOverworldWildEncounter, }; u32 objectEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[objectEventId]; @@ -1105,7 +1105,7 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) return FALSE; if (IsOverworldWildEncounter(object, OWE_MANUAL) - && GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithDynamicWildOverworldEncounter + && GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithDynamicOverworldWildEncounter && GetObjectEventScriptPointerByObjectEventId(objectEventId) != NULL) return FALSE; @@ -1641,7 +1641,7 @@ void OWE_StartEncounter(struct ObjectEvent *mon) if (mon->movementActionId >= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_DOWN && mon->movementActionId <= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_RIGHT) ClearObjectEventMovement(mon, &gSprites[mon->spriteId]); - ScriptContext_SetupScript(InteractWithDynamicWildOverworldEncounter); + ScriptContext_SetupScript(InteractWithDynamicOverworldWildEncounter); } bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent) From bdcff24e56894d4f5be5d4811540c8187cdc63be Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 1 Feb 2026 12:24:28 -0600 Subject: [PATCH 509/572] Despawn all OWEs before dexnav fade from black --- src/dexnav.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dexnav.c b/src/dexnav.c index 656a99115fef..3602e549232c 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -1846,6 +1846,7 @@ static void CB1_DexNavSearchCallback(void) static void Task_DexNavExitAndSearch(u8 taskId) { + RemoveAllOverworldWildEncounterObjects(OWE_GENERATED); DexNavGuiFreeResources(); DestroyTask(taskId); SetMainCallback1(CB1_DexNavSearchCallback); From 1610766b604547c9d1c4e6784b3a585a371e5ef6 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 1 Feb 2026 14:52:30 -0600 Subject: [PATCH 510/572] Wrote OWE Behaviour Types section of the doc --- .../how_to_overworld_wild_encounters.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 81faf8ceb1a8..07019cba5fd6 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -51,7 +51,34 @@ These despawn conditions will overwrite the restrictive despawns mentioned above Collision between Player and OWE or Interacting with one. Can also interact with an OWE in the water even when the player is not. ### Encounter Types ## Repel and Lure Behaviours + ## OWE Behaviour Types +The behaviors that these OWEs have by default is set up to be customizable for each individual species. By default, every species is set to `OWE_IGNORE_PLAYER`. To add a different behavior to a species, find their species struct in their `gen_X_families.h` file in the `src/data/pokemon/species_info` folder and add a `.overworldEncounterBehavior = ` to it. `` should be replaced with the behavior you want them to use. For example, if I wanted to add the `OWE_FLEE_PLAYER_NORMAL` behavior to Mudkip I would open `gen_3_families.h`, find the struct for `SPECIES_MUDKIP`, and add `.overworldEncounterBehavior = OWE_FLEE_PLAYER_NORMAL` to the end of it like so: +```diff + .eggMoveLearnset = sMudkipEggMoveLearnset, + .evolutions = EVOLUTION({EVO_LEVEL, 16, SPECIES_MARSHTOMP}), ++ .overworldEncounterBehavior = OWE_FLEE_PLAYER_NORMAL + }, + + [SPECIES_MARSHTOMP] = + { + .baseHP = 70, +``` + +The behaviors themselves are defined in `src/data/pokemon/wild_encounter_overworld_behavior.h`. These are the customizable parameters: +- `movementType` is the movement type you want the object event to have. More on these in the next section. +- `viewDistance` is the number of tiles away the mon is able to notice the player in the cardinal directions (similar to the sight distance of trainers). +- `viewWidth` is the total width of the area in which the mon will notice the player. For example, if `viewWidth` is set to `3`, the mon will be able to detect the player if they are within 1 tile of either side of the line of sight. +- `activeDistance` is the max distance away from the mon that the player can be before the mon loses track of them and goes back to wandering. +- `idleSpeed` is the speed at which the mon will take a step while wandering (player is not noticed). This must be one of the values in `enum OWESpeeds`. +- `activeSpeed` is the speed at which the mon will take a step while active (player has been noticed). This must be one of the values in `enum OWESpeeds`. + +If any of these parameters are not defined, they will be automatically assigned the value of `0`. + +A small number of premade behaviors have been provided and are ready to use. You can add as many new custom behaviors as you like, but make sure to add each behavior to `enum OverworldEncounterBehaviors`, making sure that `OWE_SPECIES_BEHAVIOR_COUNT` is always at the end. + +The same behavior can be used for multiple different species. + ## OWE Movements While OWE objects can be given any movement type, there are several different custom movement types that were made specifically for OWEs: - The basic one is `MOVEMENT_TYPE_WANDER_AROUND_OWE`. All of the other OWE movement types start with this behavior and differentiate into other behavior when they notice the player. Similar to `MOVEMENT_TYPE_WANDER_AROUND`, the object will take steps in random directions at random intervals. The main difference here is that the object will never turn more than 90 degrees from their current facing direction when taking a step. OWEs with this movement type will completely ignore the player until they are directly collided/interacted with. From 7f3c0084abf15579332ea502f8540618a5edb60a Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 1 Feb 2026 14:57:52 -0600 Subject: [PATCH 511/572] Removed question The stuff that removes OWEs for hard limitations ignores OWE_NO_DESPAWN_FLAG --- docs/tutorials/how_to_overworld_wild_encounters.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 07019cba5fd6..af98b78e7b84 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -45,7 +45,6 @@ None of these three configs can prevent the forceful despawning of OWEs to free ## High Priority and Low Priority OWEs Low Priority OWEs may not be spawned or even be destroyed in certain situations. There are palettes and object tiles checks to prevent these from spawning if it would fail, as well as similar checks for number of event objects, palettes and object tiles. These checks will despawn the oldest of Low Priority OWEs when other objects event are attempting to be spawned and Low Priority OWEs are using these resources. Low Priority OWEs may also be destroyed by NPC object events colliding with them due to their movements or the OWE being in the way of a trainer interaction. High priority OWEs are treated as regular objects and will not be destroyed in the ways outlined above, but may cause the destruction of Generated OWEs and will not face spawning restrictions. These despawn conditions will overwrite the restrictive despawns mentioned above. -> Is this true? Does it take the oldest or oldest not marked for restriction, what if all are marked to not despawn. ## Encountering an OWE Collision between Player and OWE or Interacting with one. Can also interact with an OWE in the water even when the player is not. From 6b551cb8e7e4b06cab06fee89bb8fde0ab59c399 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 1 Feb 2026 15:44:36 -0600 Subject: [PATCH 512/572] Removed redundant wording --- docs/tutorials/how_to_overworld_wild_encounters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index af98b78e7b84..9c6adede9203 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -52,7 +52,7 @@ Collision between Player and OWE or Interacting with one. Can also interact with ## Repel and Lure Behaviours ## OWE Behaviour Types -The behaviors that these OWEs have by default is set up to be customizable for each individual species. By default, every species is set to `OWE_IGNORE_PLAYER`. To add a different behavior to a species, find their species struct in their `gen_X_families.h` file in the `src/data/pokemon/species_info` folder and add a `.overworldEncounterBehavior = ` to it. `` should be replaced with the behavior you want them to use. For example, if I wanted to add the `OWE_FLEE_PLAYER_NORMAL` behavior to Mudkip I would open `gen_3_families.h`, find the struct for `SPECIES_MUDKIP`, and add `.overworldEncounterBehavior = OWE_FLEE_PLAYER_NORMAL` to the end of it like so: +The behaviors that these OWEs have is set up to be customizable for each individual species. By default, every species is set to `OWE_IGNORE_PLAYER`. To add a different behavior to a species, find their species struct in their `gen_X_families.h` file in the `src/data/pokemon/species_info` folder and add a `.overworldEncounterBehavior = ` to it. `` should be replaced with the behavior you want them to use. For example, if I wanted to add the `OWE_FLEE_PLAYER_NORMAL` behavior to Mudkip I would open `gen_3_families.h`, find the struct for `SPECIES_MUDKIP`, and add `.overworldEncounterBehavior = OWE_FLEE_PLAYER_NORMAL` to the end of it like so: ```diff .eggMoveLearnset = sMudkipEggMoveLearnset, .evolutions = EVOLUTION({EVO_LEVEL, 16, SPECIES_MARSHTOMP}), From 9ade3ef1257477fbb2cdae4408b08497253e5812 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 1 Feb 2026 22:08:28 +0000 Subject: [PATCH 513/572] Revert ROAMER_COUNT line --- include/constants/global.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/constants/global.h b/include/constants/global.h index 4d6e93dcb211..8c0300c23328 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -107,7 +107,7 @@ enum Language #define GIFT_RIBBONS_COUNT 11 #define SAVED_TRENDS_COUNT 5 #define PYRAMID_BAG_ITEMS_COUNT 10 -#define ROAMER_COUNT 1 // Number of maximum concurrent active roamers, stored in a u8 in Struct ObjectEvent when used for Overworld Ecnounters. +#define ROAMER_COUNT 1 // Number of maximum concurrent active roamers // Bag constants #define BAG_ITEMS_COUNT 30 From 3fa98e91f3e1be41118c49a1c9532f2db9489439 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 1 Feb 2026 17:07:10 -0600 Subject: [PATCH 514/572] Remove unnecessary RemoveAllOverworldWildEncounterObjects --- src/dexnav.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dexnav.c b/src/dexnav.c index 3602e549232c..4563935bf19a 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -745,7 +745,6 @@ static bool8 TryStartHiddenMonFieldEffect(enum EncounterType environment, u8 xSi if (fldEffId != 0) { - RemoveAllOverworldWildEncounterObjects(OWE_GENERATED); gFieldEffectArguments[0] = sDexNavSearchDataPtr->tileX; gFieldEffectArguments[1] = sDexNavSearchDataPtr->tileY; gFieldEffectArguments[2] = 0xFF; // subpriority From 197bf8106771228ea6d32a86f629885994ae5b9b Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:19:44 +0000 Subject: [PATCH 515/572] Revert global.fieldmap.h and add note to documentation --- docs/tutorials/how_to_overworld_wild_encounters.md | 12 ++++++++++++ include/global.fieldmap.h | 6 +++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 9c6adede9203..0b95b7b7b853 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -88,3 +88,15 @@ While OWE objects can be given any movement type, there are several different cu - `MOVEMENT_TYPE_DESPAWN_OWE` will make the OWE do a very brief animation of surprise and then instantly despawn when it notices the player. ### Restricted Movements +### How Data is Saved +``` +struct ObjectEvent +{ + … + u8 trainerRange_berryTreeId; // Also stores level for Overworld Encounters. + … + u8 directionSequenceIndex; // Also stores roamer status for Overworld Encounters. + u8 playerCopyableMovement; // COPY_MOVE_* Also stores age for Overworld Encounters. + … +} +``` diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index 5376a7f99fef..f71fadd26ab4 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -291,13 +291,13 @@ struct ObjectEvent /*0x1A*/ u8 fieldEffectSpriteId; /*0x1B*/ u8 warpArrowSpriteId; /*0x1C*/ u8 movementActionId; - /*0x1D*/ u8 trainerRange_berryTreeId; // Also stores level for Overworld Encounters. + /*0x1D*/ u8 trainerRange_berryTreeId; /*0x1E*/ u8 currentMetatileBehavior; /*0x1F*/ u8 previousMetatileBehavior; /*0x20*/ u8 previousMovementDirection:4; u8 directionOverwrite:4; - /*0x21*/ u8 directionSequenceIndex; // Also stores roamer status for Overworld Encounters. - /*0x22*/ u8 playerCopyableMovement; // COPY_MOVE_* Also stores age for Overworld Encounters. + /*0x21*/ u8 directionSequenceIndex; + /*0x22*/ u8 playerCopyableMovement; // COPY_MOVE_* /*0x23*/ u8 spriteId; /*size = 0x24*/ }; From 8de39d90782c2f2286be9787f9d7df4f19e237f8 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:26:50 +0000 Subject: [PATCH 516/572] Update field_effect_objects.h --- src/data/field_effects/field_effect_objects.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/field_effects/field_effect_objects.h b/src/data/field_effects/field_effect_objects.h index f878f848ca47..509970bf59fd 100755 --- a/src/data/field_effects/field_effect_objects.h +++ b/src/data/field_effects/field_effect_objects.h @@ -1284,6 +1284,7 @@ const struct SpriteTemplate gFieldEffectObjectTemplate_Bubbles = { .images = sPicTable_Bubbles, .callback = UpdateBubblesFieldEffect, }; + static const struct SpriteFrameImage sPicTable_ShinySparkle[] = { overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 0), overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 1), From 424e52208c96d96c5fad7d6754ce786323484f37 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:50:57 +0000 Subject: [PATCH 517/572] Rename OWE Movement Functions --- include/event_object_movement.h | 56 +++++++-------- .../movement_action_func_tables.h | 68 +++++++++---------- src/event_object_movement.c | 68 +++++++++---------- 3 files changed, 96 insertions(+), 96 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 8567fb71822c..0e709d145782 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -335,12 +335,12 @@ void MovementType_RunInPlace(struct Sprite *sprite); void MovementType_Invisible(struct Sprite *sprite); void MovementType_WalkSlowlyInPlace(struct Sprite *sprite); void MovementType_FollowPlayer(struct Sprite *sprite); -void MovementType_WanderAround_OverworldWildEncounter(struct Sprite *sprite); -void MovementType_ChasePlayer_OverworldWildEncounter(struct Sprite *sprite); -void MovementType_FleePlayer_OverworldWildEncounter(struct Sprite *sprite); -void MovementType_WatchPlayer_OverworldWildEncounter(struct Sprite *sprite); -void MovementType_ApproachPlayer_OverworldWildEncounter(struct Sprite *sprite); -void MovementType_Despawn_OverworldWildEncounter(struct Sprite *sprite); +void MovementType_OverworldWildEncounter_WanderAround(struct Sprite *sprite); +void MovementType_OverworldWildEncounter_ChasePlayer(struct Sprite *sprite); +void MovementType_OverworldWildEncounter_FleePlayer(struct Sprite *sprite); +void MovementType_OverworldWildEncounter_WatchPlayer(struct Sprite *sprite); +void MovementType_OverworldWildEncounter_ApproachPlayer(struct Sprite *sprite); +void MovementType_OverworldWildEncounter_Despawn(struct Sprite *sprite); u8 GetSlideMovementAction(u32); u8 GetJump2MovementAction(u32); u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); @@ -515,28 +515,28 @@ u8 MovementType_RunInPlace_Step0(struct ObjectEvent *objectEvent, struct Sprite u8 MovementType_Invisible_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_WanderAround_OverworldWildEncounter_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_Common_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ChasePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_Common_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ChasePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_Common_OverworldWildEncounter_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_FleePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_WatchPlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_WatchPlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_WatchPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ApproachPlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ApproachPlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_Despawn_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_Despawn_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_Despawn_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WanderAround_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WanderAround_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WanderAround_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WanderAround_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_Common_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_ChasePlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_Common_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_ChasePlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_ChasePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_Common_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_FleePlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_FleePlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_FleePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WatchPlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WatchPlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WatchPlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_ApproachPlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_ApproachPlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_ApproachPlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_Despawn_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_Despawn_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_Despawn_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevation, enum Direction direction); void TurnVirtualObject(u8 virtualObjId, enum Direction direction); diff --git a/src/data/object_events/movement_action_func_tables.h b/src/data/object_events/movement_action_func_tables.h index 2518f146e67a..d30749147b85 100755 --- a/src/data/object_events/movement_action_func_tables.h +++ b/src/data/object_events/movement_action_func_tables.h @@ -1762,10 +1762,10 @@ u8 (*const gMovementActionFuncs_SpinRight[])(struct ObjectEvent *, struct Sprite #define OWE_WANDER_AROUND_COMMON_STEPS \ MovementType_WanderAround_Step0, \ MovementType_WanderAround_Step1, \ - MovementType_WanderAround_OverworldWildEncounter_Step2, \ - MovementType_WanderAround_OverworldWildEncounter_Step3, \ - MovementType_WanderAround_OverworldWildEncounter_Step4, \ - MovementType_WanderAround_OverworldWildEncounter_Step5, \ + MovementType_OverworldWildEncounter_WanderAround_Step2, \ + MovementType_OverworldWildEncounter_WanderAround_Step3, \ + MovementType_OverworldWildEncounter_WanderAround_Step4, \ + MovementType_OverworldWildEncounter_WanderAround_Step5, \ MovementType_WanderAround_Step6 u8 (*const gMovementTypeFuncs_WanderAround_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = @@ -1776,54 +1776,54 @@ u8 (*const gMovementTypeFuncs_WanderAround_OverworldWildEncounter[])(struct Obje u8 (*const gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { OWE_WANDER_AROUND_COMMON_STEPS, - MovementType_Common_OverworldWildEncounter_Step7, - MovementType_ChasePlayer_OverworldWildEncounter_Step8, - MovementType_Common_OverworldWildEncounter_Step9, - MovementType_ChasePlayer_OverworldWildEncounter_Step10, - MovementType_ChasePlayer_OverworldWildEncounter_Step11, - MovementType_Common_OverworldWildEncounter_Step12, + MovementType_OverworldWildEncounter_Common_Step7, + MovementType_OverworldWildEncounter_ChasePlayer_Step8, + MovementType_OverworldWildEncounter_Common_Step9, + MovementType_OverworldWildEncounter_ChasePlayer_Step10, + MovementType_OverworldWildEncounter_ChasePlayer_Step11, + MovementType_OverworldWildEncounter_Common_Step12, }; u8 (*const gMovementTypeFuncs_FleePlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { OWE_WANDER_AROUND_COMMON_STEPS, - MovementType_Common_OverworldWildEncounter_Step7, - MovementType_FleePlayer_OverworldWildEncounter_Step8, - MovementType_Common_OverworldWildEncounter_Step9, - MovementType_FleePlayer_OverworldWildEncounter_Step10, - MovementType_FleePlayer_OverworldWildEncounter_Step11, - MovementType_Common_OverworldWildEncounter_Step12, + MovementType_OverworldWildEncounter_Common_Step7, + MovementType_OverworldWildEncounter_FleePlayer_Step8, + MovementType_OverworldWildEncounter_Common_Step9, + MovementType_OverworldWildEncounter_FleePlayer_Step10, + MovementType_OverworldWildEncounter_FleePlayer_Step11, + MovementType_OverworldWildEncounter_Common_Step12, }; u8 (*const gMovementTypeFuncs_WatchPlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { OWE_WANDER_AROUND_COMMON_STEPS, - MovementType_Common_OverworldWildEncounter_Step7, - MovementType_WatchPlayer_OverworldWildEncounter_Step8, - MovementType_Common_OverworldWildEncounter_Step9, - MovementType_WatchPlayer_OverworldWildEncounter_Step10, - MovementType_WatchPlayer_OverworldWildEncounter_Step11, - MovementType_Common_OverworldWildEncounter_Step12, + MovementType_OverworldWildEncounter_Common_Step7, + MovementType_OverworldWildEncounter_WatchPlayer_Step8, + MovementType_OverworldWildEncounter_Common_Step9, + MovementType_OverworldWildEncounter_WatchPlayer_Step10, + MovementType_OverworldWildEncounter_WatchPlayer_Step11, + MovementType_OverworldWildEncounter_Common_Step12, }; u8 (*const gMovementTypeFuncs_ApproachPlayer_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { OWE_WANDER_AROUND_COMMON_STEPS, - MovementType_Common_OverworldWildEncounter_Step7, - MovementType_ApproachPlayer_OverworldWildEncounter_Step8, - MovementType_Common_OverworldWildEncounter_Step9, - MovementType_ApproachPlayer_OverworldWildEncounter_Step10, - MovementType_ApproachPlayer_OverworldWildEncounter_Step11, - MovementType_Common_OverworldWildEncounter_Step12, + MovementType_OverworldWildEncounter_Common_Step7, + MovementType_OverworldWildEncounter_ApproachPlayer_Step8, + MovementType_OverworldWildEncounter_Common_Step9, + MovementType_OverworldWildEncounter_ApproachPlayer_Step10, + MovementType_OverworldWildEncounter_ApproachPlayer_Step11, + MovementType_OverworldWildEncounter_Common_Step12, }; u8 (*const gMovementTypeFuncs_Despawn_OverworldWildEncounter[])(struct ObjectEvent *, struct Sprite *) = { OWE_WANDER_AROUND_COMMON_STEPS, - MovementType_Common_OverworldWildEncounter_Step7, - MovementType_Despawn_OverworldWildEncounter_Step8, - MovementType_Common_OverworldWildEncounter_Step9, - MovementType_Despawn_OverworldWildEncounter_Step10, - MovementType_Despawn_OverworldWildEncounter_Step11, - MovementType_Common_OverworldWildEncounter_Step12, + MovementType_OverworldWildEncounter_Common_Step7, + MovementType_OverworldWildEncounter_Despawn_Step8, + MovementType_OverworldWildEncounter_Common_Step9, + MovementType_OverworldWildEncounter_Despawn_Step10, + MovementType_OverworldWildEncounter_Despawn_Step11, + MovementType_OverworldWildEncounter_Common_Step12, }; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 8e1fb4f7e788..ff7673457bff 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -344,12 +344,12 @@ static void (*const sMovementTypeCallbacks[])(struct Sprite *) = [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_LEFT] = MovementType_WalkSlowlyInPlace, [MOVEMENT_TYPE_WALK_SLOWLY_IN_PLACE_RIGHT] = MovementType_WalkSlowlyInPlace, [MOVEMENT_TYPE_FOLLOW_PLAYER] = MovementType_FollowPlayer, - [MOVEMENT_TYPE_WANDER_AROUND_OWE] = MovementType_WanderAround_OverworldWildEncounter, - [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = MovementType_ChasePlayer_OverworldWildEncounter, - [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = MovementType_FleePlayer_OverworldWildEncounter, - [MOVEMENT_TYPE_WATCH_PLAYER_OWE] = MovementType_WatchPlayer_OverworldWildEncounter, - [MOVEMENT_TYPE_APPROACH_PLAYER_OWE] = MovementType_ApproachPlayer_OverworldWildEncounter, - [MOVEMENT_TYPE_DESPAWN_OWE] = MovementType_Despawn_OverworldWildEncounter, + [MOVEMENT_TYPE_WANDER_AROUND_OWE] = MovementType_OverworldWildEncounter_WanderAround, + [MOVEMENT_TYPE_CHASE_PLAYER_OWE] = MovementType_OverworldWildEncounter_ChasePlayer, + [MOVEMENT_TYPE_FLEE_PLAYER_OWE] = MovementType_OverworldWildEncounter_FleePlayer, + [MOVEMENT_TYPE_WATCH_PLAYER_OWE] = MovementType_OverworldWildEncounter_WatchPlayer, + [MOVEMENT_TYPE_APPROACH_PLAYER_OWE] = MovementType_OverworldWildEncounter_ApproachPlayer, + [MOVEMENT_TYPE_DESPAWN_OWE] = MovementType_OverworldWildEncounter_Despawn, }; static const bool8 sMovementTypeHasRange[NUM_MOVEMENT_TYPES] = { @@ -11947,9 +11947,9 @@ bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim sp return TRUE; } -movement_type_def(MovementType_WanderAround_OverworldWildEncounter, gMovementTypeFuncs_WanderAround_OverworldWildEncounter) +movement_type_def(MovementType_OverworldWildEncounter_WanderAround, gMovementTypeFuncs_WanderAround_OverworldWildEncounter) -bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_WanderAround_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (!ObjectEventExecSingleMovementAction(objectEvent, sprite)) return FALSE; @@ -11959,7 +11959,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step2(struct ObjectEvent return TRUE; } -bool8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_WanderAround_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (WaitForMovementDelay(sprite)) { @@ -11982,7 +11982,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step3(struct ObjectEvent return FALSE; } -bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_WanderAround_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u8 chosenDirection = objectEvent->movementDirection; @@ -11997,7 +11997,7 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step4(struct ObjectEvent return TRUE; } -bool8 MovementType_WanderAround_OverworldWildEncounter_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_WanderAround_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite) { ObjectEventSetSingleMovement(objectEvent, sprite, OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetIdleSpeedFromSpecies(OW_SPECIES(objectEvent)))); objectEvent->singleMovementActive = TRUE; @@ -12005,9 +12005,9 @@ bool8 MovementType_WanderAround_OverworldWildEncounter_Step5(struct ObjectEvent return TRUE; } -movement_type_def(MovementType_ChasePlayer_OverworldWildEncounter, gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter) +movement_type_def(MovementType_OverworldWildEncounter_ChasePlayer, gMovementTypeFuncs_ChasePlayer_OverworldWildEncounter) -bool8 MovementType_Common_OverworldWildEncounter_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_Common_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite) { ClearObjectEventMovement(objectEvent, sprite); OWE_SetSavedMovementState(objectEvent); @@ -12015,7 +12015,7 @@ bool8 MovementType_Common_OverworldWildEncounter_Step7(struct ObjectEvent *objec return TRUE; } -bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); @@ -12035,7 +12035,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step8(struct ObjectEvent * return TRUE; } -bool8 MovementType_Common_OverworldWildEncounter_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_Common_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { @@ -12044,7 +12044,7 @@ bool8 MovementType_Common_OverworldWildEncounter_Step9(struct ObjectEvent *objec return TRUE; } -bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); @@ -12053,7 +12053,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step10(struct ObjectEvent return TRUE; } -bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u16 speciesId = OW_SPECIES(objectEvent); u8 movementActionId; @@ -12085,7 +12085,7 @@ bool8 MovementType_ChasePlayer_OverworldWildEncounter_Step11(struct ObjectEvent return TRUE; } -bool8 MovementType_Common_OverworldWildEncounter_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_Common_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { @@ -12102,9 +12102,9 @@ bool8 MovementType_Common_OverworldWildEncounter_Step12(struct ObjectEvent *obje return FALSE; } -movement_type_def(MovementType_FleePlayer_OverworldWildEncounter, gMovementTypeFuncs_FleePlayer_OverworldWildEncounter) +movement_type_def(MovementType_OverworldWildEncounter_FleePlayer, gMovementTypeFuncs_FleePlayer_OverworldWildEncounter) -bool8 MovementType_FleePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_FleePlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = GetOppositeDirection(DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent)); @@ -12119,7 +12119,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step8(struct ObjectEvent *o #define sCollisionTimer sprite->data[6] -bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_FleePlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (WE_OWE_FLEE_DESPAWN && sCollisionTimer >= OWE_FLEE_COLLISION_TIME) { @@ -12134,7 +12134,7 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step10(struct ObjectEvent * return TRUE; } -bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_FleePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u16 speciesId = OW_SPECIES(objectEvent); u8 movementActionId; @@ -12173,9 +12173,9 @@ bool8 MovementType_FleePlayer_OverworldWildEncounter_Step11(struct ObjectEvent * #undef sCollisionTimer -movement_type_def(MovementType_WatchPlayer_OverworldWildEncounter, gMovementTypeFuncs_WatchPlayer_OverworldWildEncounter) +movement_type_def(MovementType_OverworldWildEncounter_WatchPlayer, gMovementTypeFuncs_WatchPlayer_OverworldWildEncounter) -bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_WatchPlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); @@ -12194,7 +12194,7 @@ bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step8(struct ObjectEvent * return TRUE; } -bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_WatchPlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); @@ -12203,7 +12203,7 @@ bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step10(struct ObjectEvent return TRUE; } -bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_WatchPlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection)); @@ -12212,11 +12212,11 @@ bool8 MovementType_WatchPlayer_OverworldWildEncounter_Step11(struct ObjectEvent return TRUE; } -movement_type_def(MovementType_ApproachPlayer_OverworldWildEncounter, gMovementTypeFuncs_ApproachPlayer_OverworldWildEncounter) +movement_type_def(MovementType_OverworldWildEncounter_ApproachPlayer, gMovementTypeFuncs_ApproachPlayer_OverworldWildEncounter) #define sJumpTimer sprite->data[7] -bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_ApproachPlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); @@ -12236,7 +12236,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step8(struct ObjectEven return TRUE; } -bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_ApproachPlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); @@ -12245,7 +12245,7 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step10(struct ObjectEve return TRUE; } -bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_ApproachPlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { bool32 equalDistances = FALSE; u32 distance = OWE_GetApproachingMonDistanceToPlayer(objectEvent, &equalDistances); @@ -12314,11 +12314,11 @@ bool8 MovementType_ApproachPlayer_OverworldWildEncounter_Step11(struct ObjectEve return TRUE; } -movement_type_def(MovementType_Despawn_OverworldWildEncounter, gMovementTypeFuncs_Despawn_OverworldWildEncounter) +movement_type_def(MovementType_OverworldWildEncounter_Despawn, gMovementTypeFuncs_Despawn_OverworldWildEncounter) #define sDespawnTimer sprite->data[6] -bool8 MovementType_Despawn_OverworldWildEncounter_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_Despawn_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); @@ -12331,7 +12331,7 @@ bool8 MovementType_Despawn_OverworldWildEncounter_Step8(struct ObjectEvent *obje return TRUE; } -bool8 MovementType_Despawn_OverworldWildEncounter_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_Despawn_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); @@ -12340,7 +12340,7 @@ bool8 MovementType_Despawn_OverworldWildEncounter_Step10(struct ObjectEvent *obj return TRUE; } -bool8 MovementType_Despawn_OverworldWildEncounter_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) +bool8 MovementType_OverworldWildEncounter_Despawn_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (sDespawnTimer == OWE_DESPAWN_FRAMES) From f2b27db66ee64edc9d1ebad970b3c030a569b3b9 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 1 Feb 2026 18:07:25 -0600 Subject: [PATCH 518/572] Proper level checking for repel and ability --- src/overworld_encounters.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index eb69356495cf..b11a09667bc7 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -209,8 +209,8 @@ void UpdateOverworldEncounters(void) u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); if (speciesId == SPECIES_NONE - || !IsWildLevelAllowedByRepel(level) - || !IsAbilityAllowingEncounter(level) + || !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(level)) + || !IsAbilityAllowingEncounter(OWE_GetEncounterLevel(level)) || !OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny, x, y)) { OverworldWildEncounter_SetMinimumSpawnTimer(); From 469bd787d0169c05f44c71b25a486a4625462f4c Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 1 Feb 2026 18:33:49 -0600 Subject: [PATCH 519/572] Existing OWEs are despawned if repel restricts them --- include/wild_encounter_overworld.h | 1 + src/item_use.c | 1 + src/overworld_encounters.c | 19 ++++++++++++++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/include/wild_encounter_overworld.h b/include/wild_encounter_overworld.h index 504a0227d16b..f275db7ce01a 100644 --- a/include/wild_encounter_overworld.h +++ b/include/wild_encounter_overworld.h @@ -65,6 +65,7 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); void OverworldWildEncounter_SetMinimumSpawnTimer(void); void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType); +void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); diff --git a/src/item_use.c b/src/item_use.c index a0286aa3506c..5828b3264c97 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -1003,6 +1003,7 @@ static void Task_UseRepel(u8 taskId) VarSet(VAR_LAST_REPEL_LURE_USED, gSpecialVar_ItemId); #endif RemoveUsedItem(); + RemoveAllRepelRestrictedOverworldWildEncounterObjects(); if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); else diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index b11a09667bc7..82d2baf75544 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -187,6 +187,9 @@ void UpdateOverworldEncounters(void) return; } + if (REPEL_STEP_COUNT && GetNumberActiveOverworldEncounters(OWE_GENERATED)) + RemoveAllRepelRestrictedOverworldWildEncounterObjects(); + if (!IsSafeToSpawnObjectEvents()) return; @@ -975,6 +978,18 @@ void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType ow } } +void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void) +{ + struct ObjectEvent *obj; + + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) + { + obj = &gObjectEvents[i]; + if (IsOverworldWildEncounter(obj, OWE_GENERATED) && obj->active && !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) + RemoveObjectEvent(obj); + } +} + static bool8 CheckForObjectEventAtLocation(s16 x, s16 y) { for (u8 i = 0; i < OBJECT_EVENTS_COUNT; i++) @@ -1193,9 +1208,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) { - // The only automatically interacts with an OW Encounter when; - // Not using a repel or the DexNav is inactive. - if (REPEL_STEP_COUNT || FlagGet(DN_FLAG_SEARCHING)) + if (FlagGet(DN_FLAG_SEARCHING)) return; bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle, OWE_ANY)); From 13542cafdaf23c3be7445baf9c8cd92180ba6301 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 1 Feb 2026 18:53:08 -0600 Subject: [PATCH 520/572] Repel obeys OWE_NO_DESPAWN_FLAG --- src/overworld_encounters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overworld_encounters.c b/src/overworld_encounters.c index 82d2baf75544..2342082f1494 100644 --- a/src/overworld_encounters.c +++ b/src/overworld_encounters.c @@ -985,7 +985,7 @@ void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void) for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { obj = &gObjectEvents[i]; - if (IsOverworldWildEncounter(obj, OWE_GENERATED) && obj->active && !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) + if (IsOverworldWildEncounter(obj, OWE_GENERATED) && obj->active && !OWE_HasNoDespawnFlag(obj) && !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) RemoveObjectEvent(obj); } } From 4108e0bab2d2568b37e2096ccb0e222b1f421002 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 12:32:33 +0000 Subject: [PATCH 521/572] Biv Merge Fixes --- src/wild_encounter_ow.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 62264862fa18..032896d7e7d4 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -187,6 +187,9 @@ void UpdateOverworldEncounters(void) return; } + if (REPEL_STEP_COUNT && GetNumberActiveOverworldEncounters(OWE_GENERATED)) + RemoveAllRepelRestrictedOverworldWildEncounterObjects(); + if (!IsSafeToSpawnObjectEvents()) return; @@ -209,8 +212,8 @@ void UpdateOverworldEncounters(void) u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); if (speciesId == SPECIES_NONE - || !IsWildLevelAllowedByRepel(level) - || !IsAbilityAllowingEncounter(level) + || !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(level)) + || !IsAbilityAllowingEncounter(OWE_GetEncounterLevel(level)) || !OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny, x, y)) { OverworldWildEncounter_SetMinimumSpawnTimer(); @@ -1195,7 +1198,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c { // The only automatically interacts with an OW Encounter when; // Not using a repel or the DexNav is inactive. - if (!WE_OWE_REPEL_DEXNAV_COLLISION && (REPEL_STEP_COUNT || FlagGet(DN_FLAG_SEARCHING))) + if (FlagGet(DN_FLAG_SEARCHING)) return; bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle, OWE_ANY)); @@ -1861,6 +1864,18 @@ void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void) RemoveAllOverworldWildEncounterObjects(OWE_ANY); } +void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void) +{ + struct ObjectEvent *obj; + + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) + { + obj = &gObjectEvents[i]; + if (IsOverworldWildEncounter(obj, OWE_GENERATED) && obj->active && !OWE_HasNoDespawnFlag(obj) && !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) + RemoveObjectEvent(obj); + } +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From 863a655d6cf68d57def106c8aea676baa36bd3d3 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 13:22:30 +0000 Subject: [PATCH 522/572] Remove redundant call --- include/wild_encounter_ow.h | 1 - src/item_use.c | 1 - src/wild_encounter_ow.c | 3 ++- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index 67df165d30b4..2b5dceecbd35 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -65,7 +65,6 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); void OverworldWildEncounter_SetMinimumSpawnTimer(void); void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType); -void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); diff --git a/src/item_use.c b/src/item_use.c index 5828b3264c97..a0286aa3506c 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -1003,7 +1003,6 @@ static void Task_UseRepel(u8 taskId) VarSet(VAR_LAST_REPEL_LURE_USED, gSpecialVar_ItemId); #endif RemoveUsedItem(); - RemoveAllRepelRestrictedOverworldWildEncounterObjects(); if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); else diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 032896d7e7d4..d9e07c52bf23 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -152,6 +152,7 @@ static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 x static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); static void Task_OWE_ApproachForBattle(u8 taskId); static bool32 OWE_CheckSpecies(u32 speciesId); +static void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void); static EWRAM_DATA u8 sOWESpawnCountdown = 0; @@ -1864,7 +1865,7 @@ void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void) RemoveAllOverworldWildEncounterObjects(OWE_ANY); } -void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void) +static void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void) { struct ObjectEvent *obj; From 5f4f33fcb4f0a2cfc8d445f8da689f55831ce1a3 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 17:12:12 +0000 Subject: [PATCH 523/572] Swap to other RemoveAllRepelRestrictedOverworldWildEncounterObjects --- include/wild_encounter_ow.h | 1 + src/item_use.c | 1 + src/wild_encounter_ow.c | 11 +++++------ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index 2b5dceecbd35..bac7ecb7d3b7 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -95,5 +95,6 @@ void OWE_PlayAmbientCry(void); u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent); void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void); +void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void); #endif // GUARD_WILD_ENCOUNTER_OW_H \ No newline at end of file diff --git a/src/item_use.c b/src/item_use.c index a0286aa3506c..5828b3264c97 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -1003,6 +1003,7 @@ static void Task_UseRepel(u8 taskId) VarSet(VAR_LAST_REPEL_LURE_USED, gSpecialVar_ItemId); #endif RemoveUsedItem(); + RemoveAllRepelRestrictedOverworldWildEncounterObjects(); if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); else diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index d9e07c52bf23..cdd49809d39d 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -152,7 +152,6 @@ static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 x static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); static void Task_OWE_ApproachForBattle(u8 taskId); static bool32 OWE_CheckSpecies(u32 speciesId); -static void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void); static EWRAM_DATA u8 sOWESpawnCountdown = 0; @@ -188,9 +187,6 @@ void UpdateOverworldEncounters(void) return; } - if (REPEL_STEP_COUNT && GetNumberActiveOverworldEncounters(OWE_GENERATED)) - RemoveAllRepelRestrictedOverworldWildEncounterObjects(); - if (!IsSafeToSpawnObjectEvents()) return; @@ -1865,14 +1861,17 @@ void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void) RemoveAllOverworldWildEncounterObjects(OWE_ANY); } -static void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void) +void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void) { struct ObjectEvent *obj; for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { obj = &gObjectEvents[i]; - if (IsOverworldWildEncounter(obj, OWE_GENERATED) && obj->active && !OWE_HasNoDespawnFlag(obj) && !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) + if (IsOverworldWildEncounter(obj, OWE_GENERATED) + && obj->active + && !OWE_HasNoDespawnFlag(obj) + && !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) RemoveObjectEvent(obj); } } From 25fdd5734d14534b8ce35c8dcc0ce4f18add15b4 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:07:46 +0000 Subject: [PATCH 524/572] Initialise Level --- src/wild_encounter_ow.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index cdd49809d39d..c04fc90f6be7 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -205,7 +205,7 @@ void UpdateOverworldEncounters(void) bool32 isFemale = FALSE; u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); - u32 level; + u32 level = MIN_LEVEL; u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); if (speciesId == SPECIES_NONE @@ -832,7 +832,7 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 else *isFemale = FALSE; - if ((WE_OWE_PREVENT_SHINY_DESPAWN && *isShiny)) + if (WE_OWE_PREVENT_SHINY_DESPAWN && *isShiny) OWE_SetNoDespawnFlag(level); ZeroEnemyPartyMons(); @@ -1126,7 +1126,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( u16 speciesId, speciesTemplate = SanitizeSpeciesId(templateOWE.graphicsId & OBJ_EVENT_MON_SPECIES_MASK); bool32 isShiny = FALSE, isShinyTemplate = (templateOWE.graphicsId & OBJ_EVENT_MON_SHINY) ? TRUE : FALSE; bool32 isFemale = FALSE; - u32 level, levelTemplate = templateOWE.sOverworldEncounterLevel; + u32 level = MIN_LEVEL, levelTemplate = templateOWE.sOverworldEncounterLevel; u32 indexRoamerOutbreak = OWE_INVALID_ROAMER_OUTBREAK; u32 x = template->x; u32 y = template->y; From a0faad9f5134a70189c77e753532d58bb3dddf56 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:23:19 +0000 Subject: [PATCH 525/572] Cleanup event_object_movement.h --- include/event_object_movement.h | 61 +++++++++++++++++---------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 0e709d145782..7dec653c4a27 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -127,6 +127,8 @@ extern const u8 *const gBerryTreePaletteSlotTablePointers[]; extern const struct SpritePalette gSpritePalette_GeneralFieldEffect0; extern const struct SpritePalette gSpritePalette_GeneralFieldEffect1; +extern const enum Direction gStandardDirections[]; + void ResetObjectEvents(void); u8 GetMoveDirectionAnimNum(enum Direction direction); u8 GetObjectEventIdByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroupId); @@ -335,12 +337,6 @@ void MovementType_RunInPlace(struct Sprite *sprite); void MovementType_Invisible(struct Sprite *sprite); void MovementType_WalkSlowlyInPlace(struct Sprite *sprite); void MovementType_FollowPlayer(struct Sprite *sprite); -void MovementType_OverworldWildEncounter_WanderAround(struct Sprite *sprite); -void MovementType_OverworldWildEncounter_ChasePlayer(struct Sprite *sprite); -void MovementType_OverworldWildEncounter_FleePlayer(struct Sprite *sprite); -void MovementType_OverworldWildEncounter_WatchPlayer(struct Sprite *sprite); -void MovementType_OverworldWildEncounter_ApproachPlayer(struct Sprite *sprite); -void MovementType_OverworldWildEncounter_Despawn(struct Sprite *sprite); u8 GetSlideMovementAction(u32); u8 GetJump2MovementAction(u32); u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); @@ -515,28 +511,6 @@ u8 MovementType_RunInPlace_Step0(struct ObjectEvent *objectEvent, struct Sprite u8 MovementType_Invisible_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step1(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 MovementType_Invisible_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_WanderAround_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_WanderAround_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_WanderAround_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_WanderAround_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_Common_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_ChasePlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_Common_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_ChasePlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_ChasePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_Common_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_FleePlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_FleePlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_FleePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_WatchPlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_WatchPlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_WatchPlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_ApproachPlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_ApproachPlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_ApproachPlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_Despawn_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_Despawn_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); -u8 MovementType_OverworldWildEncounter_Despawn_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevation, enum Direction direction); void TurnVirtualObject(u8 virtualObjId, enum Direction direction); @@ -559,8 +533,37 @@ bool8 PlayerIsUnderWaterfall(struct ObjectEvent *objectEvent); u8 GetObjectEventApricornTreeId(u8 objectEventId); +// Overworld Wild Encounter bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); -extern const enum Direction gStandardDirections[]; +void MovementType_OverworldWildEncounter_WanderAround(struct Sprite *sprite); +void MovementType_OverworldWildEncounter_ChasePlayer(struct Sprite *sprite); +void MovementType_OverworldWildEncounter_FleePlayer(struct Sprite *sprite); +void MovementType_OverworldWildEncounter_WatchPlayer(struct Sprite *sprite); +void MovementType_OverworldWildEncounter_ApproachPlayer(struct Sprite *sprite); +void MovementType_OverworldWildEncounter_Despawn(struct Sprite *sprite); + +u8 MovementType_OverworldWildEncounter_WanderAround_Step2(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WanderAround_Step3(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WanderAround_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WanderAround_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_Common_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_ChasePlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_Common_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_ChasePlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_ChasePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_Common_Step12(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_FleePlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_FleePlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_FleePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WatchPlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WatchPlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_WatchPlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_ApproachPlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_ApproachPlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_ApproachPlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_Despawn_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_Despawn_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite); +u8 MovementType_OverworldWildEncounter_Despawn_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite); #endif //GUARD_EVENT_OBJECT_MOVEMENT_H From 40561e79bd38f9e3281f0ff00635791a95fca144 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:29:56 +0000 Subject: [PATCH 526/572] Start cleaning event_object_movement.c --- src/event_object_movement.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index ff7673457bff..b930f542eed5 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -222,7 +222,6 @@ static const struct SpriteFrameImage sPicTable_PechaBerryTree[]; static void StartSlowRunningAnim(struct ObjectEvent *objectEvent, struct Sprite *sprite, enum Direction direction); - const u8 gReflectionEffectPaletteMap[16] = { [PALSLOT_PLAYER] = PALSLOT_PLAYER_REFLECTION, [PALSLOT_PLAYER_REFLECTION] = PALSLOT_PLAYER_REFLECTION, @@ -781,7 +780,6 @@ static const u16 *const sObjectPaletteTagSets[] = { static const s16 sMovementDelaysMedium[] = {32, 64, 96, 128}; static const s16 sMovementDelaysLong[] = {32, 64, 128, 192}; // Unused static const s16 sMovementDelaysShort[] = {32, 48, 64, 80}; - static const s16 sMovementDelaysOWE[] = {64, 80, 96, 128}; #include "data/object_events/movement_type_func_tables.h" @@ -1717,7 +1715,6 @@ void RemoveObjectEventByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup) static void RemoveObjectEventInternal(struct ObjectEvent *objectEvent) { struct SpriteFrameImage image; - image.size = GetObjectEventGraphicsInfo(objectEvent->graphicsId)->size; gSprites[objectEvent->spriteId].images = ℑ // It's possible that this function is called while the sprite pointed to `== sDummySprite`, i.e during map resume; @@ -1942,7 +1939,6 @@ u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemp SetSubspriteTables(&gSprites[gObjectEvents[objectEventId].spriteId], subspriteTables); OverworldWildEncounter_OnObjectEventSpawned(&gObjectEvents[objectEventId]); - return objectEventId; } From 25f2625e0ea05cf8408cc7881ad72f1360159b5f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:37:42 +0000 Subject: [PATCH 527/572] Cleanup field_control_avatar.c and remove OverworldWildEncounter_ShouldDisableRandomEncounters --- include/wild_encounter_ow.h | 1 - src/field_control_avatar.c | 3 +-- src/wild_encounter_ow.c | 9 --------- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index bac7ecb7d3b7..b47591d75836 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -83,7 +83,6 @@ bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); void Task_OWE_WaitMovements(u8 taskId); enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); -bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void); bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 5ef8fe8bffc4..34cf38f608d2 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -642,7 +642,6 @@ static const u8 *GetInteractedWaterScript(struct MapPosition *position, u8 metat if (MetatileBehavior_IsFastWater(metatileBehavior) == TRUE && !TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING)) return EventScript_CurrentTooFast; - if (IsFieldMoveUnlocked(FIELD_MOVE_SURF) && PartyHasMonWithSurf() == TRUE && IsPlayerFacingSurfableFishableWater() == TRUE && CheckFollowerNPCFlag(FOLLOWER_NPC_FLAG_CAN_SURF) ) @@ -896,7 +895,7 @@ void RestartWildEncounterImmunitySteps(void) static bool8 CheckStandardWildEncounter(u16 metatileBehavior) { - if (FlagGet(OW_FLAG_NO_ENCOUNTER) || OverworldWildEncounter_ShouldDisableRandomEncounters()) + if (FlagGet(OW_FLAG_NO_ENCOUNTER) || WE_VANILLA_RANDOM) return FALSE; if (sWildEncounterImmunitySteps < 4) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index c04fc90f6be7..764386dcc76e 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -1614,15 +1614,6 @@ static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) return status - 1; } -bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void) -{ - if ((gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !WE_OWE_BATTLE_PIKE) - || (CurrentBattlePyramidLocation() && !WE_OWE_BATTLE_PYRAMID)) - return FALSE; - - return !WE_VANILLA_RANDOM; -} - static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) { if (!IsOverworldWildEncounter(object, OWE_GENERATED)) From 964c51e751aaea15286cc574c9dcf0d6407b80ac Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:56:52 +0000 Subject: [PATCH 528/572] Adjust Field Effects --- data/field_effect_scripts.s | 8 +-- src/field_effect_helpers.c | 102 ++++++++++++++++++------------------ 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/data/field_effect_scripts.s b/data/field_effect_scripts.s index db73a38b7203..aca2b79fa66b 100644 --- a/data/field_effect_scripts.s +++ b/data/field_effect_scripts.s @@ -307,10 +307,6 @@ gFieldEffectScript_Bubbles:: field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect0, FldEff_Bubbles field_eff_end -gFieldEffectScript_OWE_SpawnAnim:: - field_eff_callnative FldEff_OWE_SpawnAnim - field_eff_end - gFieldEffectScript_Sparkle:: field_eff_loadfadedpal_callnative gSpritePalette_SmallSparkle, FldEff_Sparkle field_eff_end @@ -420,3 +416,7 @@ gFieldEffectScript_HallOfFameRecordFrlg:: gFldEffScript_PhotoFlash:: field_eff_callnative FldEff_PhotoFlash field_eff_end + +gFieldEffectScript_OWE_SpawnAnim:: + field_eff_callnative FldEff_OWE_SpawnAnim + field_eff_end diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 18171ff08a84..1ba381c4486e 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1519,57 +1519,6 @@ u32 FldEff_BerryTreeGrowthSparkle(void) return spriteId; } -u32 FldEff_OWE_SpawnAnim(void) -{ - u8 spriteId; - u8 visual; - s16 xOffset = 0, yOffset = 0; - enum OverworldEncounterSpawnAnim spawnAnim = gFieldEffectArguments[2]; - struct SpritePalette palette = OWE_GetSpawnAnimFldEffPalette(spawnAnim); - - switch (spawnAnim) - { - case OWE_SPAWN_ANIM_GRASS: - visual = FLDEFFOBJ_JUMP_TALL_GRASS; - yOffset = 8; - break; - - case OWE_SPAWN_ANIM_LONG_GRASS: - visual = FLDEFFOBJ_JUMP_LONG_GRASS; - break; - - case OWE_SPAWN_ANIM_WATER: - visual = FLDEFFOBJ_JUMP_BIG_SPLASH; - yOffset = 8; - break; - - case OWE_SPAWN_ANIM_UNDERWATER: - visual = FLDEFFOBJ_BUBBLES; - break; - - case OWE_SPAWN_ANIM_CAVE: - visual = FLDEFFOBJ_GROUND_IMPACT_DUST; - yOffset = 12; - break; - - case OWE_SPAWN_ANIM_SHINY: - default: - visual = FLDEFFOBJ_SHINY_SPARKLE; - break; - } - - FieldEffect_LoadFadedPalette(&palette, COLOR_MAP_DARK_CONTRAST); - SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 0); - spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[visual], gFieldEffectArguments[0] + xOffset, gFieldEffectArguments[1] + yOffset, 0); - if (spriteId != MAX_SPRITES) - { - struct Sprite *sprite = &gSprites[spriteId]; - sprite->coordOffsetEnabled = TRUE; - sprite->oam.priority = 2; - } - return spriteId; -} - // Sprite data for FLDEFF_TREE_DISGUISE / FLDEFF_MOUNTAIN_DISGUISE / FLDEFF_SAND_DISGUISE #define sState data[0] #define sFldEff data[1] @@ -1958,3 +1907,54 @@ static void UpdateGrassFieldEffectSubpriority(struct Sprite *sprite, u8 elevatio } } } + +u32 FldEff_OWE_SpawnAnim(void) +{ + u8 spriteId; + u8 visual; + s16 xOffset = 0, yOffset = 0; + enum OverworldEncounterSpawnAnim spawnAnim = gFieldEffectArguments[2]; + struct SpritePalette palette = OWE_GetSpawnAnimFldEffPalette(spawnAnim); + + switch (spawnAnim) + { + case OWE_SPAWN_ANIM_GRASS: + visual = FLDEFFOBJ_JUMP_TALL_GRASS; + yOffset = 8; + break; + + case OWE_SPAWN_ANIM_LONG_GRASS: + visual = FLDEFFOBJ_JUMP_LONG_GRASS; + break; + + case OWE_SPAWN_ANIM_WATER: + visual = FLDEFFOBJ_JUMP_BIG_SPLASH; + yOffset = 8; + break; + + case OWE_SPAWN_ANIM_UNDERWATER: + visual = FLDEFFOBJ_BUBBLES; + break; + + case OWE_SPAWN_ANIM_CAVE: + visual = FLDEFFOBJ_GROUND_IMPACT_DUST; + yOffset = 12; + break; + + case OWE_SPAWN_ANIM_SHINY: + default: + visual = FLDEFFOBJ_SHINY_SPARKLE; + break; + } + + FieldEffect_LoadFadedPalette(&palette, COLOR_MAP_DARK_CONTRAST); + SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 0); + spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[visual], gFieldEffectArguments[0] + xOffset, gFieldEffectArguments[1] + yOffset, 0); + if (spriteId != MAX_SPRITES) + { + struct Sprite *sprite = &gSprites[spriteId]; + sprite->coordOffsetEnabled = TRUE; + sprite->oam.priority = 2; + } + return spriteId; +} From 4c1cb1383b519572e9ee44fb58c01a2bac97a404 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 19:02:16 +0000 Subject: [PATCH 529/572] Adjust OverworldWildEncounter_SetMinimumSpawnTimer --- src/wild_encounter_ow.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 764386dcc76e..8d95945ad91a 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -802,8 +802,10 @@ u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, boo void OverworldWildEncounter_SetMinimumSpawnTimer(void) { + if (!WE_OW_ENCOUNTERS) + return; + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - if (LURE_STEP_COUNT && GetNumberActiveOverworldEncounters(OWE_GENERATED) < OWE_SPAWNS_MAX) sOWESpawnCountdown = OWE_SPAWN_TIME_LURE; } From b76fabf935dea2c7951282252c39c11c31e5b65d Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 19:18:08 +0000 Subject: [PATCH 530/572] Adjust Repel Despawning --- include/wild_encounter_ow.h | 2 +- src/item_use.c | 2 +- src/wild_encounter_ow.c | 17 +++++++++++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index b47591d75836..aa9e779cdfa4 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -94,6 +94,6 @@ void OWE_PlayAmbientCry(void); u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent); void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void); -void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void); +void OverworldWildEncounter_SetInstantSpawnTimer(void); #endif // GUARD_WILD_ENCOUNTER_OW_H \ No newline at end of file diff --git a/src/item_use.c b/src/item_use.c index 5828b3264c97..4dcaf90d9d43 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -1003,7 +1003,7 @@ static void Task_UseRepel(u8 taskId) VarSet(VAR_LAST_REPEL_LURE_USED, gSpecialVar_ItemId); #endif RemoveUsedItem(); - RemoveAllRepelRestrictedOverworldWildEncounterObjects(); + OverworldWildEncounter_SetInstantSpawnTimer(); if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); else diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 8d95945ad91a..ed895ba4365b 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -152,6 +152,7 @@ static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 x static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); static void Task_OWE_ApproachForBattle(u8 taskId); static bool32 OWE_CheckSpecies(u32 speciesId); +static void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void); static EWRAM_DATA u8 sOWESpawnCountdown = 0; @@ -186,6 +187,8 @@ void UpdateOverworldEncounters(void) sOWESpawnCountdown--; return; } + + RemoveAllRepelRestrictedOverworldWildEncounterObjects(); if (!IsSafeToSpawnObjectEvents()) return; @@ -1854,10 +1857,12 @@ void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void) RemoveAllOverworldWildEncounterObjects(OWE_ANY); } -void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void) +static void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void) { - struct ObjectEvent *obj; + if (!REPEL_STEP_COUNT) + return; + struct ObjectEvent *obj; for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { obj = &gObjectEvents[i]; @@ -1869,6 +1874,14 @@ void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void) } } +void OverworldWildEncounter_SetInstantSpawnTimer(void) +{ + if (!WE_OW_ENCOUNTERS) + return; + + sOWESpawnCountdown = 0; +} + #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From c7967c5057fdf4ffe388a00fabd533b380988da7 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 19:30:38 +0000 Subject: [PATCH 531/572] Readd WE_OWE_REPEL_DEXNAV_COLLISION config --- src/wild_encounter_ow.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index ed895ba4365b..b6363c2af83b 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -1200,7 +1200,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c { // The only automatically interacts with an OW Encounter when; // Not using a repel or the DexNav is inactive. - if (FlagGet(DN_FLAG_SEARCHING)) + if (WE_OWE_REPEL_DEXNAV_COLLISION && (FlagGet(DN_FLAG_SEARCHING) || REPEL_STEP_COUNT)) return; bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle, OWE_ANY)); From 6fd11da226c5bd0cb3453a0e1895e9a1e4a3af61 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 19:58:57 +0000 Subject: [PATCH 532/572] Combine Remove Overworld Encounter Functions --- include/wild_encounter_ow.h | 2 +- src/dexnav.c | 2 +- src/wild_encounter_ow.c | 49 ++++++++++++++++++------------------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index aa9e779cdfa4..7fd5859cf642 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -64,7 +64,7 @@ void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); void OverworldWildEncounter_SetMinimumSpawnTimer(void); -void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType); +void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); diff --git a/src/dexnav.c b/src/dexnav.c index 4563935bf19a..18dc6dc25ef9 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -1845,7 +1845,7 @@ static void CB1_DexNavSearchCallback(void) static void Task_DexNavExitAndSearch(u8 taskId) { - RemoveAllOverworldWildEncounterObjects(OWE_GENERATED); + RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, 0); DexNavGuiFreeResources(); DestroyTask(taskId); SetMainCallback1(CB1_DexNavSearchCallback); diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index b6363c2af83b..8c82778af8d9 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -152,7 +152,6 @@ static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 x static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); static void Task_OWE_ApproachForBattle(u8 taskId); static bool32 OWE_CheckSpecies(u32 speciesId); -static void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void); static EWRAM_DATA u8 sOWESpawnCountdown = 0; @@ -172,7 +171,7 @@ void UpdateOverworldEncounters(void) { if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) { - RemoveAllOverworldWildEncounterObjects(OWE_GENERATED); + RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, 0); sOWESpawnCountdown = OWE_NO_ENCOUNTER_SET; } return; @@ -188,7 +187,7 @@ void UpdateOverworldEncounters(void) return; } - RemoveAllRepelRestrictedOverworldWildEncounterObjects(); + RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, WILD_CHECK_REPEL); if (!IsSafeToSpawnObjectEvents()) return; @@ -959,10 +958,11 @@ static bool32 OWE_ShouldSpawnWaterMons(void) return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); } -void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType) +void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags) { s16 dx = 0; s16 dy = 0; + if (gCamera.active) { dx = gCamera.x; @@ -972,11 +972,27 @@ void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType ow for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { struct ObjectEvent *obj = &gObjectEvents[i]; - if (IsOverworldWildEncounter(obj, oweType) && obj->active) + + if (!obj->active) + continue; + + if (!IsOverworldWildEncounter(obj, oweType)) + continue; + + if (flags & WILD_CHECK_REPEL) { - UpdateObjectEventCoords(obj, dx, dy); - RemoveObjectEvent(obj); + if (!REPEL_STEP_COUNT) + continue; + + if (OWE_HasNoDespawnFlag(obj)) + continue; + + if (IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) + continue; } + + UpdateObjectEventCoords(obj, dx, dy); + RemoveObjectEvent(obj); } } @@ -1854,24 +1870,7 @@ void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void) if (WE_OWE_DESPAWN_SOUND) PlaySE(SE_FLEE); - RemoveAllOverworldWildEncounterObjects(OWE_ANY); -} - -static void RemoveAllRepelRestrictedOverworldWildEncounterObjects(void) -{ - if (!REPEL_STEP_COUNT) - return; - - struct ObjectEvent *obj; - for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) - { - obj = &gObjectEvents[i]; - if (IsOverworldWildEncounter(obj, OWE_GENERATED) - && obj->active - && !OWE_HasNoDespawnFlag(obj) - && !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) - RemoveObjectEvent(obj); - } + RemoveAllOverworldWildEncounterObjects(OWE_ANY, 0); } void OverworldWildEncounter_SetInstantSpawnTimer(void) From 0e7ec00076d453b462fd1cdba755cf475ec9f48c Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 20:11:57 +0000 Subject: [PATCH 533/572] Add Check for Abilities to Despawn OWEs --- include/wild_encounter.h | 2 +- src/wild_encounter.c | 6 +++--- src/wild_encounter_ow.c | 13 +++++++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/include/wild_encounter.h b/include/wild_encounter.h index ec55505c3d34..8f3427b6b10a 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -63,7 +63,7 @@ u16 GetLocalWildMon(bool8 *isWaterMon); u16 GetLocalWaterMon(void); bool8 UpdateRepelCounter(void); bool32 IsWildLevelAllowedByRepel(u8 wildLevel); -bool32 IsAbilityAllowingEncounter(u8 level); +bool32 IsAbilityAllowingEncounter(u8 level, bool32 chanceTrigger); bool8 TryDoDoubleWildBattle(void); bool8 StandardWildEncounter_Debug(void); u32 CalculateChainFishingShinyRolls(void); diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 192bc8770642..f0379c54dce5 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -527,7 +527,7 @@ bool32 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPo level = ChooseWildMonLevel(wildMonInfo->wildPokemon, wildMonIndex, area); if (flags & WILD_CHECK_REPEL && !IsWildLevelAllowedByRepel(level)) return FALSE; - if (gMapHeader.mapLayoutId != LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && flags & WILD_CHECK_KEEN_EYE && !IsAbilityAllowingEncounter(level)) + if (gMapHeader.mapLayoutId != LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && flags & WILD_CHECK_KEEN_EYE && !IsAbilityAllowingEncounter(level, RandomPercentage(RNG_NONE, 50))) return FALSE; CreateWildMon(wildMonInfo->wildPokemon[wildMonIndex].species, level); @@ -1064,7 +1064,7 @@ bool32 IsWildLevelAllowedByRepel(u8 wildLevel) return FALSE; } -bool32 IsAbilityAllowingEncounter(u8 level) +bool32 IsAbilityAllowingEncounter(u8 level, bool32 chanceTrigger) { enum Ability ability; @@ -1075,7 +1075,7 @@ bool32 IsAbilityAllowingEncounter(u8 level) if (ability == ABILITY_KEEN_EYE || ability == ABILITY_INTIMIDATE) { u8 playerMonLevel = GetMonData(&gPlayerParty[0], MON_DATA_LEVEL); - if (playerMonLevel > 5 && level <= playerMonLevel - 5 && !(Random() % 2)) + if (playerMonLevel > 5 && level <= playerMonLevel - 5 && chanceTrigger) return FALSE; } diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 8c82778af8d9..fdfee522b0b4 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -187,7 +187,7 @@ void UpdateOverworldEncounters(void) return; } - RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, WILD_CHECK_REPEL); + RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE); if (!IsSafeToSpawnObjectEvents()) return; @@ -212,7 +212,7 @@ void UpdateOverworldEncounters(void) if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(level)) - || !IsAbilityAllowingEncounter(OWE_GetEncounterLevel(level)) + || !IsAbilityAllowingEncounter(OWE_GetEncounterLevel(level), RandomPercentage(RNG_NONE, 50)) || !OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny, x, y)) { OverworldWildEncounter_SetMinimumSpawnTimer(); @@ -979,7 +979,7 @@ void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType ow if (!IsOverworldWildEncounter(obj, oweType)) continue; - if (flags & WILD_CHECK_REPEL) + if (flags & WILD_CHECK_REPEL || flags & WILD_CHECK_KEEN_EYE) { if (!REPEL_STEP_COUNT) continue; @@ -987,7 +987,12 @@ void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType ow if (OWE_HasNoDespawnFlag(obj)) continue; - if (IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) + if (flags & WILD_CHECK_REPEL + && IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) + continue; + + if (flags & WILD_CHECK_KEEN_EYE + && IsAbilityAllowingEncounter(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel), TRUE)) continue; } From 4b936072a24b7b229f072cb2b53bde9cf039bdcc Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 20:13:42 +0000 Subject: [PATCH 534/572] Revert "Add Check for Abilities to Despawn OWEs" This reverts commit 0e7ec00076d453b462fd1cdba755cf475ec9f48c. --- include/wild_encounter.h | 2 +- src/wild_encounter.c | 6 +++--- src/wild_encounter_ow.c | 13 ++++--------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 8f3427b6b10a..ec55505c3d34 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -63,7 +63,7 @@ u16 GetLocalWildMon(bool8 *isWaterMon); u16 GetLocalWaterMon(void); bool8 UpdateRepelCounter(void); bool32 IsWildLevelAllowedByRepel(u8 wildLevel); -bool32 IsAbilityAllowingEncounter(u8 level, bool32 chanceTrigger); +bool32 IsAbilityAllowingEncounter(u8 level); bool8 TryDoDoubleWildBattle(void); bool8 StandardWildEncounter_Debug(void); u32 CalculateChainFishingShinyRolls(void); diff --git a/src/wild_encounter.c b/src/wild_encounter.c index f0379c54dce5..192bc8770642 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -527,7 +527,7 @@ bool32 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPo level = ChooseWildMonLevel(wildMonInfo->wildPokemon, wildMonIndex, area); if (flags & WILD_CHECK_REPEL && !IsWildLevelAllowedByRepel(level)) return FALSE; - if (gMapHeader.mapLayoutId != LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && flags & WILD_CHECK_KEEN_EYE && !IsAbilityAllowingEncounter(level, RandomPercentage(RNG_NONE, 50))) + if (gMapHeader.mapLayoutId != LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && flags & WILD_CHECK_KEEN_EYE && !IsAbilityAllowingEncounter(level)) return FALSE; CreateWildMon(wildMonInfo->wildPokemon[wildMonIndex].species, level); @@ -1064,7 +1064,7 @@ bool32 IsWildLevelAllowedByRepel(u8 wildLevel) return FALSE; } -bool32 IsAbilityAllowingEncounter(u8 level, bool32 chanceTrigger) +bool32 IsAbilityAllowingEncounter(u8 level) { enum Ability ability; @@ -1075,7 +1075,7 @@ bool32 IsAbilityAllowingEncounter(u8 level, bool32 chanceTrigger) if (ability == ABILITY_KEEN_EYE || ability == ABILITY_INTIMIDATE) { u8 playerMonLevel = GetMonData(&gPlayerParty[0], MON_DATA_LEVEL); - if (playerMonLevel > 5 && level <= playerMonLevel - 5 && chanceTrigger) + if (playerMonLevel > 5 && level <= playerMonLevel - 5 && !(Random() % 2)) return FALSE; } diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index fdfee522b0b4..8c82778af8d9 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -187,7 +187,7 @@ void UpdateOverworldEncounters(void) return; } - RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE); + RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, WILD_CHECK_REPEL); if (!IsSafeToSpawnObjectEvents()) return; @@ -212,7 +212,7 @@ void UpdateOverworldEncounters(void) if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(level)) - || !IsAbilityAllowingEncounter(OWE_GetEncounterLevel(level), RandomPercentage(RNG_NONE, 50)) + || !IsAbilityAllowingEncounter(OWE_GetEncounterLevel(level)) || !OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny, x, y)) { OverworldWildEncounter_SetMinimumSpawnTimer(); @@ -979,7 +979,7 @@ void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType ow if (!IsOverworldWildEncounter(obj, oweType)) continue; - if (flags & WILD_CHECK_REPEL || flags & WILD_CHECK_KEEN_EYE) + if (flags & WILD_CHECK_REPEL) { if (!REPEL_STEP_COUNT) continue; @@ -987,12 +987,7 @@ void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType ow if (OWE_HasNoDespawnFlag(obj)) continue; - if (flags & WILD_CHECK_REPEL - && IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) - continue; - - if (flags & WILD_CHECK_KEEN_EYE - && IsAbilityAllowingEncounter(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel), TRUE)) + if (IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) continue; } From ad31c160cb1b26c07ca3cc167f04df5f306236d3 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Mon, 2 Feb 2026 20:20:57 +0000 Subject: [PATCH 535/572] Bugged CheckStandardWildEncounter --- src/field_control_avatar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 34cf38f608d2..3159f4718dbb 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -895,7 +895,7 @@ void RestartWildEncounterImmunitySteps(void) static bool8 CheckStandardWildEncounter(u16 metatileBehavior) { - if (FlagGet(OW_FLAG_NO_ENCOUNTER) || WE_VANILLA_RANDOM) + if (FlagGet(OW_FLAG_NO_ENCOUNTER) || !WE_VANILLA_RANDOM) return FALSE; if (sWildEncounterImmunitySteps < 4) From c7715c02a49b59b9a47295883e8d998c72dc6285 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 2 Feb 2026 20:08:04 -0600 Subject: [PATCH 536/572] Disable vanilla encounters if WE_VANILLA_RANDOM is FALSE --- src/field_control_avatar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 34cf38f608d2..3159f4718dbb 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -895,7 +895,7 @@ void RestartWildEncounterImmunitySteps(void) static bool8 CheckStandardWildEncounter(u16 metatileBehavior) { - if (FlagGet(OW_FLAG_NO_ENCOUNTER) || WE_VANILLA_RANDOM) + if (FlagGet(OW_FLAG_NO_ENCOUNTER) || !WE_VANILLA_RANDOM) return FALSE; if (sWildEncounterImmunitySteps < 4) From 81fd4a0ab015deac5ceb648459b470d914a4e9c2 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Mon, 2 Feb 2026 20:43:31 -0600 Subject: [PATCH 537/572] Return after creating Feebas --- src/wild_encounter_ow.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 8c82778af8d9..037e2222dff3 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -918,6 +918,8 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam CreateWildMon(gWildFeebas.species, ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING)); if (WE_OWE_PREVENT_FEEBAS_DESPAWN) OWE_SetNoDespawnFlag(level); + + return TRUE; } else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior)) { From c022bb5c029a5fda8ffc3fa9de224d923a2a15c4 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:25:10 +0000 Subject: [PATCH 538/572] Move FlagSet to Create Encounter --- src/wild_encounter_ow.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 037e2222dff3..a54cdc901afa 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -574,6 +574,9 @@ void CreateOverworldWildEncounter(void) return; } + if (IsOverworldWildEncounter(object, OWE_MANUAL)) + FlagSet(GetObjectEventFlagIdByLocalIdAndMap(object->localId, object->mapNum, object->mapGroup)); + if (indexRoamerOutbreak && CreateOverworldWildEncounter_CheckRoamer(OWE_GetObjectRoamerOutbreakStatus(object))) return; @@ -777,9 +780,6 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; - if (IsOverworldWildEncounter(objectEvent, OWE_MANUAL)) - FlagSet(GetObjectEventFlagIdByLocalIdAndMap(objectEvent->localId, objectEvent->mapNum, objectEvent->mapGroup)); - objectEvent->sOverworldEncounterLevel = 0; objectEvent->sAge = 0; objectEvent->sRoamerOutbreakStatus = 0; From 1241a21ec0c373d021c531bcbff1c50778fcab8e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:31:45 +0000 Subject: [PATCH 539/572] Fix Other Special Spawns being overwritten --- src/wild_encounter_ow.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index a54cdc901afa..0e3425f16f08 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -912,6 +912,7 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam if (TryStartRoamerEncounter() && !OWE_DoesRoamerObjectExist()) { *indexRoamerOutbreak = OWE_GetObjectRoamerStatusFromIndex(gEncounteredRoamerIndex); + return TRUE; } else if (WE_OWE_FEEBAS_SPOTS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) { @@ -925,6 +926,7 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam { SetUpMassOutbreakEncounter(0); *indexRoamerOutbreak = OWE_MASS_OUTBREAK_INDEX; + return TRUE; } else { From 9091582ee6668bab8f793a47fd1ab40ab590edf7 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 13:15:08 +0000 Subject: [PATCH 540/572] Move Shiny Sparkle Data --- src/data/field_effects/field_effect_objects.h | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/data/field_effects/field_effect_objects.h b/src/data/field_effects/field_effect_objects.h index 509970bf59fd..8adaf0f2dbd2 100755 --- a/src/data/field_effects/field_effect_objects.h +++ b/src/data/field_effects/field_effect_objects.h @@ -1285,45 +1285,6 @@ const struct SpriteTemplate gFieldEffectObjectTemplate_Bubbles = { .callback = UpdateBubblesFieldEffect, }; -static const struct SpriteFrameImage sPicTable_ShinySparkle[] = { - overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 0), - overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 1), - overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 2), - overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 3), - overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 4), - overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 5), - overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 6), - overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 7), -}; - -static const union AnimCmd sAnim_ShinySparkle[] = -{ - ANIMCMD_FRAME(0, 4), - ANIMCMD_FRAME(1, 4), - ANIMCMD_FRAME(2, 4), - ANIMCMD_FRAME(3, 6), - ANIMCMD_FRAME(4, 6), - ANIMCMD_FRAME(5, 4), - ANIMCMD_FRAME(6, 4), - ANIMCMD_FRAME(7, 4), - ANIMCMD_END, -}; - -static const union AnimCmd *const sAnimTable_ShinySparkle[] = -{ - sAnim_ShinySparkle, -}; - -const struct SpriteTemplate gFieldEffectObjectTemplate_ShinySparkle = { - .tileTag = TAG_NONE, - .paletteTag = FLDEFF_PAL_TAG_GENERAL_0, - .oam = &gObjectEventBaseOam_16x32, - .anims = sAnimTable_ShinySparkle, - .images = sPicTable_ShinySparkle, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = UpdateBubblesFieldEffect, -}; - static const struct SpriteFrameImage sPicTable_SmallSparkle[] = { overworld_frame(gFieldEffectObjectPic_SmallSparkle, 2, 2, 0), overworld_frame(gFieldEffectObjectPic_SmallSparkle, 2, 2, 1), @@ -1437,3 +1398,42 @@ const struct SpriteTemplate gFieldEffectObjectTemplate_RockClimbDust = { }; const struct SpritePalette gSpritePalette_BigDust = {gFieldEffectPal_DustCloud, FLDEFF_PAL_TAG_DUST_CLOUD}; + +static const struct SpriteFrameImage sPicTable_ShinySparkle[] = { + overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 0), + overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 1), + overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 2), + overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 3), + overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 4), + overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 5), + overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 6), + overworld_frame(gFieldEffectObjectPic_ShinySparkle, 2, 4, 7), +}; + +static const union AnimCmd sAnim_ShinySparkle[] = +{ + ANIMCMD_FRAME(0, 4), + ANIMCMD_FRAME(1, 4), + ANIMCMD_FRAME(2, 4), + ANIMCMD_FRAME(3, 6), + ANIMCMD_FRAME(4, 6), + ANIMCMD_FRAME(5, 4), + ANIMCMD_FRAME(6, 4), + ANIMCMD_FRAME(7, 4), + ANIMCMD_END, +}; + +static const union AnimCmd *const sAnimTable_ShinySparkle[] = +{ + sAnim_ShinySparkle, +}; + +const struct SpriteTemplate gFieldEffectObjectTemplate_ShinySparkle = { + .tileTag = TAG_NONE, + .paletteTag = FLDEFF_PAL_TAG_GENERAL_0, + .oam = &gObjectEventBaseOam_16x32, + .anims = sAnimTable_ShinySparkle, + .images = sPicTable_ShinySparkle, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = UpdateBubblesFieldEffect, +}; From 341c9916039ed5f1457838f4fd0bf8030a8ed33e Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 13:16:12 +0000 Subject: [PATCH 541/572] Move gFieldEffectObjectPic_ShinySparkle --- src/data/object_events/object_event_graphics.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/object_events/object_event_graphics.h b/src/data/object_events/object_event_graphics.h index a93a0c5e83f6..8c814b1365e6 100755 --- a/src/data/object_events/object_event_graphics.h +++ b/src/data/object_events/object_event_graphics.h @@ -336,7 +336,6 @@ const u16 gFieldEffectPal_DustCloud[] = INCBIN_U16("graphics/field_effects/palet const u32 gFieldEffectObjectPic_AshPuff[] = INCBIN_U32("graphics/field_effects/pics/ash_puff.4bpp"); const u32 gFieldEffectObjectPic_AshLaunch[] = INCBIN_U32("graphics/field_effects/pics/ash_launch.4bpp"); const u32 gFieldEffectObjectPic_Bubbles[] = INCBIN_U32("graphics/field_effects/pics/bubbles.4bpp"); -const u32 gFieldEffectObjectPic_ShinySparkle[] = INCBIN_U32("graphics/field_effects/pics/shiny_sparkle.4bpp"); const u32 gFieldEffectObjectPic_SmallSparkle[] = INCBIN_U32("graphics/field_effects/pics/small_sparkle.4bpp"); const u16 gFieldEffectPal_SmallSparkle[] = INCBIN_U16("graphics/field_effects/palettes/small_sparkle.gbapal"); const u32 gFieldEffectObjectPic_Bird[] = INCBIN_U32("graphics/field_effects/pics/bird.4bpp"); @@ -469,6 +468,8 @@ const u16 gFieldEffectObjectPalette_CaveDust[] = INCBIN_U16("graphics/field_effe const u32 gObjectEventPic_ApricornTree[] = INCBIN_U32("graphics/object_events/pics/misc/apricorn_tree.4bpp"); +const u32 gFieldEffectObjectPic_ShinySparkle[] = INCBIN_U32("graphics/field_effects/pics/shiny_sparkle.4bpp"); + #if IS_FRLG const u16 gObjectEventPic_RedNormal[] = INCBIN_U16("graphics/object_events/pics/people/red/red_normal.4bpp"); From 1435876b659fa7e0708f33ab86463474575e81f9 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 13:18:42 +0000 Subject: [PATCH 542/572] Move OWE Flag Set and Clean OverworldWildEncounter_RemoveObjectOnBattle --- src/wild_encounter_ow.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 0e3425f16f08..335c161e5fd9 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -574,9 +574,6 @@ void CreateOverworldWildEncounter(void) return; } - if (IsOverworldWildEncounter(object, OWE_MANUAL)) - FlagSet(GetObjectEventFlagIdByLocalIdAndMap(object->localId, object->mapNum, object->mapGroup)); - if (indexRoamerOutbreak && CreateOverworldWildEncounter_CheckRoamer(OWE_GetObjectRoamerOutbreakStatus(object))) return; @@ -1246,12 +1243,15 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c void OverworldWildEncounter_RemoveObjectOnBattle(void) { struct ObjectEvent *object = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; - if (IsOverworldWildEncounter(object, OWE_ANY)) - { - RemoveObjectEvent(object); - OWE_SetNewSpawnCountdown(); - gSpecialVar_LastTalked = LOCALID_NONE; - } + if (!IsOverworldWildEncounter(object, OWE_ANY)) + return; + + if (IsOverworldWildEncounter(object, OWE_MANUAL)) + FlagSet(GetObjectEventFlagIdByLocalIdAndMap(object->localId, object->mapNum, object->mapGroup)); + + RemoveObjectEvent(object); + OWE_SetNewSpawnCountdown(); + gSpecialVar_LastTalked = LOCALID_NONE; } // Returns TRUE if movement is restricted. From fc5d83b621bfde58d34f70de966c87b7fa57c2c8 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 13:55:36 +0000 Subject: [PATCH 543/572] Clean Up DetermineObjectEventDirectionFromObject --- src/event_object_movement.c | 97 +++++++++++-------------------------- 1 file changed, 27 insertions(+), 70 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index b930f542eed5..a210497e4a20 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -10122,85 +10122,42 @@ void ScriptFaceEachOther(struct ScriptContext *ctx) enum Direction DetermineObjectEventDirectionFromObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo) { - s16 absX, absY; - s16 distanceX = objectOne->currentCoords.x - objectTwo->currentCoords.x; - s16 distanceY = objectOne->currentCoords.y - objectTwo->currentCoords.y; + s16 dx = objectOne->currentCoords.x - objectTwo->currentCoords.x; + s16 dy = objectOne->currentCoords.y - objectTwo->currentCoords.y; - if (distanceX == 0 && distanceY == 0) + if (dx == 0 && dy == 0) return DIR_NONE; - // Get absolute X distance. - if (distanceX < 0) - absX = distanceX * -1; - else - absX = distanceX; + s16 absX = abs(dx); + s16 absY = abs(dy); + + if (absX > absY && dx < 0) + return DIR_WEST; + else if (absX > absY && dx > 0) + return DIR_EAST; + else if (absY > absX && dy < 0) + return DIR_NORTH; + else if (absY > absX && dy > 0) + return DIR_SOUTH; - // Get absolute Y distance. - if (distanceY < 0) - absY = distanceY * -1; + enum Direction directionOne, directionTwo; + + if (dx < 0) + directionTwo = DIR_WEST; else - absY = distanceY; + directionTwo = DIR_EAST; - if (absX > absY) - { - if (distanceX < 0) - return DIR_WEST; - else - return DIR_EAST; - } + if (dy < 0) + directionOne = DIR_NORTH; else - { - if (absX < absY) - { - if (distanceY < 0) - return DIR_NORTH; - else - return DIR_SOUTH; - } - else - { - if (distanceY < 0) - { - if (objectTwo->movementDirection == DIR_NORTH) - return DIR_NORTH; + directionOne = DIR_SOUTH; - if (distanceX < 0) - { - if (objectTwo->movementDirection == DIR_WEST) - return DIR_WEST; - else - return (Random() % 2) == 0 ? DIR_NORTH : DIR_WEST; - } - else - { - if (objectTwo->movementDirection == DIR_EAST) - return DIR_EAST; - else - return (Random() % 2) == 0 ? DIR_NORTH : DIR_EAST; - } - } - else - { - if (objectTwo->movementDirection == DIR_SOUTH) - return DIR_SOUTH; + if (objectTwo->facingDirection == directionOne) + return directionOne; + else if (objectTwo->facingDirection == directionTwo) + return directionTwo; - if (distanceX < 0) - { - if (objectTwo->movementDirection == DIR_WEST) - return DIR_WEST; - else - return (Random() % 2) == 0 ? DIR_SOUTH : DIR_WEST; - } - else - { - if (objectTwo->movementDirection == DIR_EAST) - return DIR_EAST; - else - return (Random() % 2) == 0 ? DIR_SOUTH : DIR_EAST; - } - } - } - } + return (Random() % 2) ? directionOne : directionTwo; } void ObjectEventsTurnToEachOther(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo) From 5e5f3dc5aca2be13fe724fabdbd9a259d4c12096 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:36:00 +0000 Subject: [PATCH 544/572] Cleanup event_object_movement.c --- src/event_object_movement.c | 79 +++++++++---------------------------- 1 file changed, 18 insertions(+), 61 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index a210497e4a20..1ac2cbeeab13 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11921,17 +11921,13 @@ bool8 MovementType_OverworldWildEncounter_WanderAround_Step3(struct ObjectEvent sprite->sTypeFuncId = 4; return TRUE; } - else - { - if (OW_MON_WANDER_WALK == TRUE && IS_OW_MON_OBJ(objectEvent)) - UpdateMonMoveInPlace(objectEvent, sprite); + + if (OW_MON_WANDER_WALK == TRUE && IS_OW_MON_OBJ(objectEvent)) + UpdateMonMoveInPlace(objectEvent, sprite); - if (OWE_CanAwareMonSeePlayer(objectEvent)) - { - sprite->sTypeFuncId = 7; - return FALSE; - } - } + if (OWE_CanAwareMonSeePlayer(objectEvent)) + sprite->sTypeFuncId = 7; + return FALSE; } @@ -11971,36 +11967,30 @@ bool8 MovementType_OverworldWildEncounter_Common_Step7(struct ObjectEvent *objec bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - SetObjectEventDirection(objectEvent, direction); - - if (!OWE_IsMonNextToPlayer(objectEvent)) - { - ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK); - PlaySE(SE_PIN); - sprite->sTypeFuncId = 9; - } - else + if (OWE_IsMonNextToPlayer(objectEvent)) { sprite->sTypeFuncId = 10; + return TRUE; } + ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK); + PlaySE(SE_PIN); + sprite->sTypeFuncId = 9; return TRUE; } bool8 MovementType_OverworldWildEncounter_Common_Step9(struct ObjectEvent *objectEvent, struct Sprite *sprite) { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) - { sprite->sTypeFuncId = 10; - } + return TRUE; } bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - SetObjectEventDirection(objectEvent, direction); sprite->sTypeFuncId = 11; return TRUE; @@ -12009,9 +11999,7 @@ bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step10(struct ObjectEvent bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u16 speciesId = OW_SPECIES(objectEvent); - u8 movementActionId; - - movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + u8 movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); sprite->sTypeFuncId = 12; if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) @@ -12026,9 +12014,9 @@ bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step11(struct ObjectEvent objectEvent->singleMovementActive = TRUE; return FALSE; } + u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); } @@ -12043,14 +12031,12 @@ bool8 MovementType_OverworldWildEncounter_Common_Step12(struct ObjectEvent *obje if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { objectEvent->singleMovementActive = FALSE; - + sprite->sTypeFuncId = 10; if (!OWE_IsPlayerInsideMonActiveDistance(objectEvent)) { OWE_ClearSavedMovementState(objectEvent); sprite->sTypeFuncId = 0; - return FALSE; } - sprite->sTypeFuncId = 10; } return FALSE; } @@ -12060,13 +12046,10 @@ movement_type_def(MovementType_OverworldWildEncounter_FleePlayer, gMovementTypeF bool8 MovementType_OverworldWildEncounter_FleePlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = GetOppositeDirection(DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent)); - SetObjectEventDirection(objectEvent, direction); - ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK); PlaySE(SE_PIN); sprite->sTypeFuncId = 9; - return TRUE; } @@ -12081,7 +12064,6 @@ bool8 MovementType_OverworldWildEncounter_FleePlayer_Step10(struct ObjectEvent * } enum Direction direction = GetOppositeDirection(DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent)); - SetObjectEventDirection(objectEvent, direction); sprite->sTypeFuncId = 11; return TRUE; @@ -12090,19 +12072,14 @@ bool8 MovementType_OverworldWildEncounter_FleePlayer_Step10(struct ObjectEvent * bool8 MovementType_OverworldWildEncounter_FleePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u16 speciesId = OW_SPECIES(objectEvent); - u8 movementActionId; - - movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - + u8 movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) { u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - if (newDirection != objectEvent->movementDirection) newDirection = GetOppositeDirection(newDirection); movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) { sCollisionTimer++; @@ -12131,26 +12108,19 @@ movement_type_def(MovementType_OverworldWildEncounter_WatchPlayer, gMovementType bool8 MovementType_OverworldWildEncounter_WatchPlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - SetObjectEventDirection(objectEvent, direction); - + sprite->sTypeFuncId = 10; if (!OWE_IsMonNextToPlayer(objectEvent)) { ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_QUESTION_MARK); sprite->sTypeFuncId = 9; } - else - { - sprite->sTypeFuncId = 10; - } - return TRUE; } bool8 MovementType_OverworldWildEncounter_WatchPlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - SetObjectEventDirection(objectEvent, direction); sprite->sTypeFuncId = 11; return TRUE; @@ -12158,7 +12128,6 @@ bool8 MovementType_OverworldWildEncounter_WatchPlayer_Step10(struct ObjectEvent bool8 MovementType_OverworldWildEncounter_WatchPlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - ObjectEventSetSingleMovement(objectEvent, sprite, GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection)); objectEvent->singleMovementActive = TRUE; sprite->sTypeFuncId = 12; @@ -12172,27 +12141,20 @@ movement_type_def(MovementType_OverworldWildEncounter_ApproachPlayer, gMovementT bool8 MovementType_OverworldWildEncounter_ApproachPlayer_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - SetObjectEventDirection(objectEvent, direction); sJumpTimer = (Random() % (OWE_APPROACH_JUMP_TIMER_MAX - OWE_APPROACH_JUMP_TIMER_MIN)) + OWE_APPROACH_JUMP_TIMER_MIN; - + sprite->sTypeFuncId = 10; if (!OWE_IsMonNextToPlayer(objectEvent)) { ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_QUESTION_MARK); sprite->sTypeFuncId = 9; } - else - { - sprite->sTypeFuncId = 10; - } - return TRUE; } bool8 MovementType_OverworldWildEncounter_ApproachPlayer_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - SetObjectEventDirection(objectEvent, direction); sprite->sTypeFuncId = 11; return TRUE; @@ -12204,7 +12166,6 @@ bool8 MovementType_OverworldWildEncounter_ApproachPlayer_Step11(struct ObjectEve u32 distance = OWE_GetApproachingMonDistanceToPlayer(objectEvent, &equalDistances); u16 speciesId = OW_SPECIES(objectEvent); u8 movementActionId; - if (distance <= 1) { SetObjectEventDirection(objectEvent, GetOppositeDirection(objectEvent->movementDirection)); @@ -12274,20 +12235,17 @@ movement_type_def(MovementType_OverworldWildEncounter_Despawn, gMovementTypeFunc bool8 MovementType_OverworldWildEncounter_Despawn_Step8(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - SetObjectEventDirection(objectEvent, direction); ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK); PlaySE(SE_PIN); sDespawnTimer = 0; sprite->sTypeFuncId = 9; - return TRUE; } bool8 MovementType_OverworldWildEncounter_Despawn_Step10(struct ObjectEvent *objectEvent, struct Sprite *sprite) { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); - SetObjectEventDirection(objectEvent, direction); sprite->sTypeFuncId = 11; return TRUE; @@ -12295,7 +12253,6 @@ bool8 MovementType_OverworldWildEncounter_Despawn_Step10(struct ObjectEvent *obj bool8 MovementType_OverworldWildEncounter_Despawn_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (sDespawnTimer == OWE_DESPAWN_FRAMES) { RemoveObjectEvent(objectEvent); From a72e57efe9ab4246e1e03952b6642411feca7126 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 15:51:26 +0000 Subject: [PATCH 545/572] Cleanup wild_encounter_ow.h --- data/scripts/wild_encounter.inc | 2 +- include/wild_encounter_ow.h | 12 +++--------- src/field_control_avatar.c | 4 ++-- src/overworld.c | 2 +- src/wild_encounter_ow.c | 25 +++++++++++++++---------- 5 files changed, 22 insertions(+), 23 deletions(-) diff --git a/data/scripts/wild_encounter.inc b/data/scripts/wild_encounter.inc index fe2bf05c75c5..eddf3340b5fb 100644 --- a/data/scripts/wild_encounter.inc +++ b/data/scripts/wild_encounter.inc @@ -1,4 +1,4 @@ -InteractWithDynamicOverworldWildEncounter:: +InteractWithOverworldWildEncounter:: lock overworldwildencounterapproach applymovement VAR_LAST_TALKED, Common_Movement_ExclamationMark diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index 7fd5859cf642..d79952549452 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -55,20 +55,14 @@ enum __attribute__((packed)) OverworldEncounterBehaviors OWE_SPECIES_BEHAVIOR_COUNT }; -extern const u8 InteractWithDynamicOverworldWildEncounter[]; - -void UpdateOverworldEncounters(void); -u32 GetOldestSlot(bool32 forceRemove); +void OverworldWildEncounters_CB(void); void CreateOverworldWildEncounter(void); void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); -u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); void OverworldWildEncounter_SetMinimumSpawnTimer(void); void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); -u32 GetNewestOWEncounterLocalId(void); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); -bool32 CanRemoveOverworldEncounter(u32 localId); u32 RemoveOldestGeneratedOverworldEncounter(void); bool32 OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId); void OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Palette(void); @@ -81,8 +75,6 @@ bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon); u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); -void Task_OWE_WaitMovements(u8 taskId); -enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); @@ -96,4 +88,6 @@ bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent); void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void); void OverworldWildEncounter_SetInstantSpawnTimer(void); +extern const u8 InteractWithOverworldWildEncounter[]; + #endif // GUARD_WILD_ENCOUNTER_OW_H \ No newline at end of file diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 3159f4718dbb..f93640456bc8 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -407,7 +407,7 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 else if (PlayerHasFollowerNPC() && objectEventId == GetFollowerNPCObjectId()) script = GetFollowerNPCScriptPointer(); else if (ShouldRunOverworldEncounterScript(objectEventId)) - script = InteractWithDynamicOverworldWildEncounter; + script = InteractWithOverworldWildEncounter; else script = GetObjectEventScriptPointerByObjectEventId(objectEventId); @@ -637,7 +637,7 @@ static const u8 *GetInteractedWaterScript(struct MapPosition *position, u8 metat if (IsPlayerFacingSurfableFishableWater() == TRUE && ShouldRunOverworldEncounterScript(objectEventId)) { gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; - return InteractWithDynamicOverworldWildEncounter; + return InteractWithOverworldWildEncounter; } if (MetatileBehavior_IsFastWater(metatileBehavior) == TRUE && !TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING)) diff --git a/src/overworld.c b/src/overworld.c index f963287f6bf6..f63abc3f5748 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1851,7 +1851,7 @@ static void OverworldBasic(void) ApplyWeatherColorMapIfIdle(gWeatherPtr->colorMapIndex); } } - UpdateOverworldEncounters(); + OverworldWildEncounters_CB(); } // This CB2 is used when starting diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 335c161e5fd9..c13a5a91d17f 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -152,10 +152,14 @@ static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 x static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); static void Task_OWE_ApproachForBattle(u8 taskId); static bool32 OWE_CheckSpecies(u32 speciesId); +static u32 GetOldestSlot(bool32 forceRemove); +static u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); +static bool32 CanRemoveOverworldEncounter(u32 localId); +static enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); static EWRAM_DATA u8 sOWESpawnCountdown = 0; -void UpdateOverworldEncounters(void) +void OverworldWildEncounters_CB(void) { bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); @@ -227,7 +231,7 @@ void UpdateOverworldEncounters(void) .elevation = MapGridGetElevationAt(x, y), .movementType = OWE_GetMovementTypeFromSpecies(speciesId), .trainerType = TRAINER_TYPE_OW_WILD_ENCOUNTER, - .script = InteractWithDynamicOverworldWildEncounter, + .script = InteractWithOverworldWildEncounter, }; u32 objectEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[objectEventId]; @@ -266,6 +270,7 @@ static void OWE_SetNewSpawnCountdown(void) bool32 OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId) { + // does this need to be used in OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Palette if (CanRemoveOverworldEncounter(localId)) { *objectEventId = RemoveOldestGeneratedOverworldEncounter(); @@ -401,7 +406,7 @@ static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animS MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); } -u32 GetOldestSlot(bool32 forceRemove) +static u32 GetOldestSlot(bool32 forceRemove) { struct ObjectEvent *slotMon, *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; u32 spawnSlot; @@ -784,7 +789,7 @@ void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent OWE_DoSpawnDespawnAnim(objectEvent, FALSE); } -u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) +static u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) { SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level, indexRoamerOutbreak); assertf(OWE_CheckSpecies(*speciesId), "invalid generated overworld encounter\nspecies: %d\ncheck if valid wild mon header exists", speciesId, x, y); @@ -1081,7 +1086,7 @@ static u32 GetSpawnSlotByLocalId(u32 localId) return LOCALID_OW_ENCOUNTER_END - localId; } -u32 GetNewestOWEncounterLocalId(void) +static UNUSED u32 GetNewestOWEncounterLocalId(void) { struct ObjectEvent *slotMon; struct ObjectEvent *newest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; @@ -1100,7 +1105,7 @@ u32 GetNewestOWEncounterLocalId(void) return GetSpawnSlotByLocalId(newest->localId); } -bool32 CanRemoveOverworldEncounter(u32 localId) +static bool32 CanRemoveOverworldEncounter(u32 localId) { // Include a check for the encounter not being shiny or a roamer. return (WE_OW_ENCOUNTERS && GetNumberActiveOverworldEncounters(OWE_GENERATED) != 0 @@ -1127,7 +1132,7 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) return FALSE; if (IsOverworldWildEncounter(object, OWE_MANUAL) - && GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithDynamicOverworldWildEncounter + && GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithOverworldWildEncounter && GetObjectEventScriptPointerByObjectEventId(objectEventId) != NULL) return FALSE; @@ -1469,7 +1474,7 @@ u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equal return absX; } -enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior) +static enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior) { if (MetatileBehavior_IsPokeGrass(metatileBehavior) || MetatileBehavior_IsAshGrass(metatileBehavior)) return OWE_SPAWN_ANIM_GRASS; @@ -1515,7 +1520,7 @@ static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) return; // TESTING: Setting this species can be used as a test to play a consistent sound to check how often the - // code in UpdateOverworldEncounters runs, as OWE_GetRandomActiveEncounterObject cuurently returns + // code in OverworldWildEncounters_CB runs, as OWE_GetRandomActiveEncounterObject cuurently returns // NULL. if (objectEvent == NULL) return; @@ -1657,7 +1662,7 @@ void OWE_StartEncounter(struct ObjectEvent *mon) if (mon->movementActionId >= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_DOWN && mon->movementActionId <= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_RIGHT) ClearObjectEventMovement(mon, &gSprites[mon->spriteId]); - ScriptContext_SetupScript(InteractWithDynamicOverworldWildEncounter); + ScriptContext_SetupScript(InteractWithOverworldWildEncounter); } bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent) From b3db3a407424275dbd0939b960da6f0f184a3502 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 16:50:46 +0000 Subject: [PATCH 546/572] Rename and Reorder wild_encounter_ow.h --- asm/macros/event.inc | 2 +- include/wild_encounter_ow.h | 39 ++++++++++++++-------------- src/battle_setup.c | 2 +- src/dexnav.c | 2 +- src/event_object_movement.c | 6 ++--- src/fieldmap.c | 2 +- src/sprite.c | 2 +- src/wild_encounter_ow.c | 51 +++++++++++++++++++------------------ 8 files changed, 53 insertions(+), 53 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 2ed48a4e59e5..2d5444e70caa 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2822,7 +2822,7 @@ @ Starts an Overworld Wild Encounter with a selected object, if possible. .macro tryoverworldwildencounter - callnative CreateOverworldWildEncounter + callnative GenerateOverworldWildEncounter .endm @ Start a scripted approach for an overworld wild encounter towards the player. diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index d79952549452..cb18099ac711 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -56,37 +56,36 @@ enum __attribute__((packed)) OverworldEncounterBehaviors }; void OverworldWildEncounters_CB(void); -void CreateOverworldWildEncounter(void); -void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); -void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent); +void GenerateOverworldWildEncounter(void); +void OverworldWildEncounter_SetInstantSpawnTimer(void); void OverworldWildEncounter_SetMinimumSpawnTimer(void); -void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); -bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); -u32 RemoveOldestGeneratedOverworldEncounter(void); -bool32 OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId); -void OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Palette(void); -const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); -void OverworldWildEncounter_RemoveObjectOnBattle(void); -bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Direction direction); -bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon); -bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon); -u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); -bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); -u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); +bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); +void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); +void OverworldWildEncounter_OnObjectEventDespawned(struct ObjectEvent *objectEvent); bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); -struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); +bool32 OWE_IsDespawnExempt(struct ObjectEvent *objectEvent); +void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags); +bool32 OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId); +void OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette(void); +void OverworldWildEncounter_DespawnOnBattle(void); +void OWE_TryDespawnOverworldWildEncountersCrossingMapConnection(void); void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite); void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent); void OWE_ClearSavedMovementState(struct ObjectEvent *objectEvent); +bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Direction direction); +bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon); +bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon); +bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); +u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); +u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed); void OWE_PlayAmbientCry(void); u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); -bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent); -void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void); -void OverworldWildEncounter_SetInstantSpawnTimer(void); +const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); +struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); extern const u8 InteractWithOverworldWildEncounter[]; diff --git a/src/battle_setup.c b/src/battle_setup.c index 03bded7a9444..01d3856d04cc 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -260,7 +260,7 @@ static void Task_BattleStart(u8 taskId) SetMainCallback2(CB2_InitBattle); RestartWildEncounterImmunitySteps(); ClearPoisonStepCounter(); - OverworldWildEncounter_RemoveObjectOnBattle(); + OverworldWildEncounter_DespawnOnBattle(); DestroyTask(taskId); } break; diff --git a/src/dexnav.c b/src/dexnav.c index 18dc6dc25ef9..37c620745afd 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -1845,7 +1845,7 @@ static void CB1_DexNavSearchCallback(void) static void Task_DexNavExitAndSearch(u8 taskId) { - RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, 0); + DespwnAllOverworldWildEncounterObjects(OWE_GENERATED, 0); DexNavGuiFreeResources(); DestroyTask(taskId); SetMainCallback1(CB1_DexNavSearchCallback); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 1ac2cbeeab13..028dc135f427 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1683,7 +1683,7 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * return TRUE; } if (i >= OBJECT_EVENTS_COUNT) - return OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Object(localId, objectEventId); + return OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(localId, objectEventId); *objectEventId = i; for (; i < OBJECT_EVENTS_COUNT; i++) { @@ -1695,7 +1695,7 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * void RemoveObjectEvent(struct ObjectEvent *objectEvent) { - OverworldWildEncounter_OnObjectEventRemoved(objectEvent); + OverworldWildEncounter_OnObjectEventDespawned(objectEvent); objectEvent->active = FALSE; RemoveObjectEventInternal(objectEvent); // zero potential species info @@ -2983,7 +2983,7 @@ static void RemoveObjectEventIfOutsideView(struct ObjectEvent *objectEvent) && objectEvent->initialCoords.y >= top && objectEvent->initialCoords.y <= bottom) return; - if (OWE_IsMonRemovalExempt(objectEvent)) + if (OWE_IsDespawnExempt(objectEvent)) return; RemoveObjectEvent(objectEvent); diff --git a/src/fieldmap.c b/src/fieldmap.c index 8c50c23df4b4..b8e877fcf597 100644 --- a/src/fieldmap.c +++ b/src/fieldmap.c @@ -808,7 +808,7 @@ bool8 CameraMove(int x, int y) gSaveBlock1Ptr->pos.x += x; gSaveBlock1Ptr->pos.y += y; MoveMapViewToBackup(direction); - OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(); + OWE_TryDespawnOverworldWildEncountersCrossingMapConnection(); } return gCamera.active; diff --git a/src/sprite.c b/src/sprite.c index fff28398ece4..7da601fcae26 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -1630,7 +1630,7 @@ u32 LoadSpritePalette(const struct SpritePalette *palette) if (index == 0xFF) { - OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Palette(); + OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette(); index = IndexOfSpritePaletteTag(TAG_NONE); if (index == 0xFF) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index c13a5a91d17f..86f04e2bccc3 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -137,10 +137,10 @@ static bool32 OWE_DoesRoamerObjectExist(void); static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); -static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); -static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); -static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); -static bool32 CreateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); +static bool32 GenerateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); +static bool32 GenerateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); +static bool32 GenerateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); +static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); @@ -156,6 +156,7 @@ static u32 GetOldestSlot(bool32 forceRemove); static u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); static bool32 CanRemoveOverworldEncounter(u32 localId); static enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); +static u32 RemoveOldestGeneratedOverworldEncounter(void); static EWRAM_DATA u8 sOWESpawnCountdown = 0; @@ -175,7 +176,7 @@ void OverworldWildEncounters_CB(void) { if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) { - RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, 0); + DespwnAllOverworldWildEncounterObjects(OWE_GENERATED, 0); sOWESpawnCountdown = OWE_NO_ENCOUNTER_SET; } return; @@ -191,7 +192,7 @@ void OverworldWildEncounters_CB(void) return; } - RemoveAllOverworldWildEncounterObjects(OWE_GENERATED, WILD_CHECK_REPEL); + DespwnAllOverworldWildEncounterObjects(OWE_GENERATED, WILD_CHECK_REPEL); if (!IsSafeToSpawnObjectEvents()) return; @@ -268,9 +269,9 @@ static void OWE_SetNewSpawnCountdown(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); } -bool32 OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId) +bool32 OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId) { - // does this need to be used in OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Palette + // does this need to be used in OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette if (CanRemoveOverworldEncounter(localId)) { *objectEventId = RemoveOldestGeneratedOverworldEncounter(); @@ -284,7 +285,7 @@ bool32 OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Object(u32 localId, u8 return TRUE; } -void OWE_TryAndRemoveOldestGeneratedOverworldEncounter_Palette(void) +void OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette(void) { // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles if (WE_OW_ENCOUNTERS && CountFreePaletteSlots() < 2) @@ -564,7 +565,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) return FALSE; } -void CreateOverworldWildEncounter(void) +void GenerateOverworldWildEncounter(void) { u32 localId = gSpecialVar_LastTalked; u32 objEventId = GetObjectEventIdByLocalId(localId); @@ -579,7 +580,7 @@ void CreateOverworldWildEncounter(void) return; } - if (indexRoamerOutbreak && CreateOverworldWildEncounter_CheckRoamer(OWE_GetObjectRoamerOutbreakStatus(object))) + if (indexRoamerOutbreak && GenerateOverworldWildEncounter_CheckRoamer(OWE_GetObjectRoamerOutbreakStatus(object))) return; u16 speciesId = OW_SPECIES(object); @@ -605,19 +606,19 @@ void CreateOverworldWildEncounter(void) GiveMonInitialMoveset(&gEnemyParty[0]); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); - if (CreateOverworldWildEncounter_CheckBattleFrontier(headerId)) + if (GenerateOverworldWildEncounter_CheckBattleFrontier(headerId)) return; - if (CreateOverworldWildEncounter_CheckMassOutbreak(indexRoamerOutbreak, speciesId)) + if (GenerateOverworldWildEncounter_CheckMassOutbreak(indexRoamerOutbreak, speciesId)) return; - if (CreateOverworldWildEncounter_CheckDoubleBattle(object, headerId)) + if (GenerateOverworldWildEncounter_CheckDoubleBattle(object, headerId)) return; BattleSetup_StartWildBattle(); } -static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak) +static bool32 GenerateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak) { if (indexRoamerOutbreak < ROAMER_COUNT && IsRoamerAt(indexRoamerOutbreak, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) @@ -631,7 +632,7 @@ static bool32 CreateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak) return FALSE; } -static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId) +static bool32 GenerateOverworldWildEncounter_CheckBattleFrontier(u32 headerId) { if (headerId == HEADER_NONE) { @@ -657,7 +658,7 @@ static bool32 CreateOverworldWildEncounter_CheckBattleFrontier(u32 headerId) return FALSE; } -static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId) +static bool32 GenerateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId) { if (indexRoamerOutbreak == OWE_MASS_OUTBREAK_INDEX && gSaveBlock1Ptr->outbreakPokemonSpecies == speciesId @@ -674,7 +675,7 @@ static bool32 CreateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutb return FALSE; } -static bool32 CreateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId) +static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId) { enum WildPokemonArea wildArea; enum TimeOfDay timeOfDay; @@ -777,7 +778,7 @@ void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent OWE_DoSpawnDespawnAnim(objectEvent, TRUE); } -void OverworldWildEncounter_OnObjectEventRemoved(struct ObjectEvent *objectEvent) +void OverworldWildEncounter_OnObjectEventDespawned(struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; @@ -964,7 +965,7 @@ static bool32 OWE_ShouldSpawnWaterMons(void) return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); } -void RemoveAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags) +void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags) { s16 dx = 0; s16 dy = 0; @@ -1113,7 +1114,7 @@ static bool32 CanRemoveOverworldEncounter(u32 localId) || localId > LOCALID_OW_ENCOUNTER_END)); } -u32 RemoveOldestGeneratedOverworldEncounter(void) +static u32 RemoveOldestGeneratedOverworldEncounter(void) { u32 oldestSlot = GetOldestSlot(TRUE); @@ -1245,7 +1246,7 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c OWE_StartEncounter(wildMon); } -void OverworldWildEncounter_RemoveObjectOnBattle(void) +void OverworldWildEncounter_DespawnOnBattle(void) { struct ObjectEvent *object = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; if (!IsOverworldWildEncounter(object, OWE_ANY)) @@ -1853,7 +1854,7 @@ void OWE_PlayAmbientCry(void) OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); } -bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent) +bool32 OWE_IsDespawnExempt(struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return FALSE; @@ -1865,7 +1866,7 @@ bool32 OWE_IsMonRemovalExempt(struct ObjectEvent *objectEvent) return FALSE; } -void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void) +void OWE_TryDespawnOverworldWildEncountersCrossingMapConnection(void) { if (gMain.callback2 != CB2_Overworld) return; @@ -1879,7 +1880,7 @@ void OWE_TryRemoveOverworldWildEncountersCrossingMapConnection(void) if (WE_OWE_DESPAWN_SOUND) PlaySE(SE_FLEE); - RemoveAllOverworldWildEncounterObjects(OWE_ANY, 0); + DespwnAllOverworldWildEncounterObjects(OWE_ANY, 0); } void OverworldWildEncounter_SetInstantSpawnTimer(void) From a6cf4c2c68bb8b91e670c06b02b698624cb36379 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 16:54:47 +0000 Subject: [PATCH 547/572] Convert functions to static inline --- src/wild_encounter_ow.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 86f04e2bccc3..97a6857ab128 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -116,6 +116,16 @@ static inline void OWE_SetNoDespawnFlag(u32 *level) *level |= OWE_NO_DESPAWN_FLAG; } +static inline u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot) +{ + return LOCALID_OW_ENCOUNTER_END - spawnSlot; +} + +static inline u32 GetSpawnSlotByLocalId(u32 localId) +{ + return LOCALID_OW_ENCOUNTER_END - localId; +} + static bool8 TrySelectTile(s16* outX, s16* outY); static u8 NextSpawnMonSlot(); static bool32 OWE_ShouldSpawnWaterMons(void); @@ -124,8 +134,6 @@ static bool8 IsSafeToSpawnObjectEvents(void); static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons); static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); -static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot); -static u32 GetSpawnSlotByLocalId(u32 localId); static void SortOWEMonAges(void); static void OWE_SetNewSpawnCountdown(void); static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); @@ -1077,16 +1085,6 @@ static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) return OW_SPECIES(objectEvent); } -static u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot) -{ - return LOCALID_OW_ENCOUNTER_END - spawnSlot; -} - -static u32 GetSpawnSlotByLocalId(u32 localId) -{ - return LOCALID_OW_ENCOUNTER_END - localId; -} - static UNUSED u32 GetNewestOWEncounterLocalId(void) { struct ObjectEvent *slotMon; From 92d0e7768fd09c894b7f40aea6288561ee0e7b66 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:13:38 +0000 Subject: [PATCH 548/572] Remove CheckForObjectEventAtLocation --- src/wild_encounter_ow.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 97a6857ab128..f6e073e66cb1 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -132,7 +132,6 @@ static bool32 OWE_ShouldSpawnWaterMons(void); static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak); static bool8 IsSafeToSpawnObjectEvents(void); static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons); -static bool8 CheckForObjectEventAtLocation(s16 x, s16 y); static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); static void SortOWEMonAges(void); static void OWE_SetNewSpawnCountdown(void); @@ -566,7 +565,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) *outX = x; *outY = y; - if (!CheckForObjectEventAtLocation(x, y)) + if (!GetObjectEventIdByPosition(x, y, 0)) return TRUE; } @@ -1011,18 +1010,6 @@ void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType ow } } -static bool8 CheckForObjectEventAtLocation(s16 x, s16 y) -{ - for (u8 i = 0; i < OBJECT_EVENTS_COUNT; i++) - { - if (gObjectEvents[i].active && gObjectEvents[i].currentCoords.x == x - && gObjectEvents[i].currentCoords.y == y) - return TRUE; - } - - return FALSE; -} - static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) { u32 headerId = GetCurrentMapWildMonHeaderId(); From cf37283b3ba68db641efe0d659aae695282c0aac Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:27:16 +0000 Subject: [PATCH 549/572] Reorder declarations in wild_encounter_ow.c --- src/wild_encounter_ow.c | 50 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index f6e073e66cb1..83f108ca0422 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -126,44 +126,44 @@ static inline u32 GetSpawnSlotByLocalId(u32 localId) return LOCALID_OW_ENCOUNTER_END - localId; } -static bool8 TrySelectTile(s16* outX, s16* outY); -static u8 NextSpawnMonSlot(); +static bool32 OWE_CheckSpecies(u32 speciesId); +static void OWE_StartEncounter(struct ObjectEvent *mon); +static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); +static bool32 OWE_DoesRoamerObjectExist(void); +static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); +static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); +static bool32 GenerateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); +static bool32 GenerateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); +static bool32 GenerateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); +static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); static bool32 OWE_ShouldSpawnWaterMons(void); -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak); -static bool8 IsSafeToSpawnObjectEvents(void); static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons); +static bool8 IsSafeToSpawnObjectEvents(void); +static u32 GetOldestSlot(bool32 forceRemove); +static u8 NextSpawnMonSlot(void); static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); -static void SortOWEMonAges(void); -static void OWE_SetNewSpawnCountdown(void); +static bool8 TrySelectTile(s16* outX, s16* outY); +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak); +static u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); +static bool32 CanRemoveOverworldEncounter(u32 localId); static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); static bool32 OWE_CanEncounterBeLoaded_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); +static void SortOWEMonAges(void); +static u32 RemoveOldestGeneratedOverworldEncounter(void); +static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); +static void OWE_SetNewSpawnCountdown(void); +static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); +static enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); -static bool32 OWE_DoesRoamerObjectExist(void); -static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); -static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); -static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); -static bool32 GenerateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); -static bool32 GenerateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); -static bool32 GenerateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); -static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); -static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); -static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); -static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); -static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); -static void OWE_StartEncounter(struct ObjectEvent *mon); static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance); static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 xNew, s16 yNew, enum Direction newDirection, enum Direction collisionDirection); +static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); +static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); static void Task_OWE_ApproachForBattle(u8 taskId); -static bool32 OWE_CheckSpecies(u32 speciesId); -static u32 GetOldestSlot(bool32 forceRemove); -static u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); -static bool32 CanRemoveOverworldEncounter(u32 localId); -static enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); -static u32 RemoveOldestGeneratedOverworldEncounter(void); static EWRAM_DATA u8 sOWESpawnCountdown = 0; From 70401f5ffbb7f7c3ab618eda9fc7988eef2cf461 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 18:06:39 +0000 Subject: [PATCH 550/572] Reorder wild_encounter_ow files --- include/wild_encounter_ow.h | 6 +- src/wild_encounter_ow.c | 2040 +++++++++++++++++------------------ 2 files changed, 1024 insertions(+), 1022 deletions(-) diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index cb18099ac711..bfa0702689fd 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -56,17 +56,18 @@ enum __attribute__((packed)) OverworldEncounterBehaviors }; void OverworldWildEncounters_CB(void); +bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); +void OWE_StartEncounter(struct ObjectEvent *mon); void GenerateOverworldWildEncounter(void); void OverworldWildEncounter_SetInstantSpawnTimer(void); void OverworldWildEncounter_SetMinimumSpawnTimer(void); -bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); void OverworldWildEncounter_OnObjectEventDespawned(struct ObjectEvent *objectEvent); +bool32 OWE_IsDespawnExempt(struct ObjectEvent *objectEvent); bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); -bool32 OWE_IsDespawnExempt(struct ObjectEvent *objectEvent); void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags); bool32 OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId); void OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette(void); @@ -82,6 +83,7 @@ bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed); +void OWE_ApproachForBattle(void); void OWE_PlayAmbientCry(void); u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 83f108ca0422..61bf7dc8af42 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -76,6 +76,16 @@ #endif +static inline u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot) +{ + return LOCALID_OW_ENCOUNTER_END - spawnSlot; +} + +static inline u32 GetSpawnSlotByLocalId(u32 localId) +{ + return LOCALID_OW_ENCOUNTER_END - localId; +} + static inline u32 OWE_GetRoamerIndex(const struct ObjectEvent *object) { return object->sRoamerOutbreakStatus & ~OWE_SAVED_MOVEMENT_STATE_FLAG; @@ -116,18 +126,6 @@ static inline void OWE_SetNoDespawnFlag(u32 *level) *level |= OWE_NO_DESPAWN_FLAG; } -static inline u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot) -{ - return LOCALID_OW_ENCOUNTER_END - spawnSlot; -} - -static inline u32 GetSpawnSlotByLocalId(u32 localId) -{ - return LOCALID_OW_ENCOUNTER_END - localId; -} - -static bool32 OWE_CheckSpecies(u32 speciesId); -static void OWE_StartEncounter(struct ObjectEvent *mon); static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); static bool32 OWE_DoesRoamerObjectExist(void); static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); @@ -136,9 +134,9 @@ static bool32 GenerateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak static bool32 GenerateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); static bool32 GenerateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); -static bool32 OWE_ShouldSpawnWaterMons(void); +static bool32 OWE_ShouldSpawnWaterMons(void); // inline? static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons); -static bool8 IsSafeToSpawnObjectEvents(void); +static bool8 IsSafeToSpawnObjectEvents(void); // inline? static u32 GetOldestSlot(bool32 forceRemove); static u8 NextSpawnMonSlot(void); static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); @@ -158,15 +156,23 @@ static enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatile static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); -static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance); static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 xNew, s16 yNew, enum Direction newDirection, enum Direction collisionDirection); static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); +static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance); static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); static void Task_OWE_ApproachForBattle(u8 taskId); +static bool32 OWE_CheckSpecies(u32 speciesId); static EWRAM_DATA u8 sOWESpawnCountdown = 0; +struct AgeSort +{ + u8 slot:4; + u8 age:4; +}; + + void OverworldWildEncounters_CB(void) { bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); @@ -264,365 +270,218 @@ void OverworldWildEncounters_CB(void) OWE_SetNewSpawnCountdown(); } -static void OWE_SetNewSpawnCountdown(void) -{ - u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); - - if (WE_OWE_SPAWN_REPLACEMENT && numActive >= OWE_SPAWNS_MAX) - sOWESpawnCountdown = OWE_SPAWN_TIME_REPLACEMENT; - else if (LURE_STEP_COUNT && numActive < OWE_SPAWNS_MAX) - sOWESpawnCountdown = OWE_SPAWN_TIME_LURE; - else - sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); -} - -bool32 OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId) +bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType) { - // does this need to be used in OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette - if (CanRemoveOverworldEncounter(localId)) + bool32 isOWE = (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_OW_WILD_ENCOUNTER); + switch (oweType) { - *objectEventId = RemoveOldestGeneratedOverworldEncounter(); + default: + case OWE_ANY: + return isOWE; + + case OWE_GENERATED: + return isOWE && (objectEvent->localId <= LOCALID_OW_ENCOUNTER_END + && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); - if (*objectEventId == OBJECT_EVENTS_COUNT) - return TRUE; - else - return FALSE; + case OWE_MANUAL: + return isOWE && (objectEvent->localId > LOCALID_OW_ENCOUNTER_END + || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); } - - return TRUE; } -void OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette(void) +void OWE_StartEncounter(struct ObjectEvent *mon) { - // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles - if (WE_OW_ENCOUNTERS && CountFreePaletteSlots() < 2) - { - u32 count = GetNumberActiveOverworldEncounters(OWE_GENERATED); + gSpecialVar_LastTalked = mon->localId; + gSpecialVar_0x8004 = OW_SPECIES(mon); + gSelectedObjectEvent = GetObjectEventIdByLocalId(mon->localId); - if (count > 0) - { - for (; count > 0; count--) - { - RemoveOldestGeneratedOverworldEncounter(); - if (CountFreePaletteSlots() >= 2) - break; - } - } - } + // Stop the bobbing animation. + if (mon->movementActionId >= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_DOWN && mon->movementActionId <= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_RIGHT) + ClearObjectEventMovement(mon, &gSprites[mon->spriteId]); + + ScriptContext_SetupScript(InteractWithOverworldWildEncounter); } -static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) +void GenerateOverworldWildEncounter(void) { - assertf(OWE_CanEncounterBeLoaded_Palette(speciesId, isFemale, isShiny, x, y), "could not load palette for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) - { - return FALSE; - } + u32 localId = gSpecialVar_LastTalked; + u32 objEventId = GetObjectEventIdByLocalId(localId); + u32 headerId = GetCurrentMapWildMonHeaderId(); + struct ObjectEvent *object = &gObjectEvents[objEventId]; + u32 indexRoamerOutbreak = OWE_GetRoamerIndex(object); - assertf(OWE_CanEncounterBeLoaded_Tiles(speciesId, isFemale, isShiny, x, y), "could not load sprite tiles for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) + assertf(objEventId < OBJECT_EVENTS_COUNT && IsOverworldWildEncounter(object, OWE_ANY), "cannot start overworld wild enocunter as the selected object is invalid.\nlocalId: %d", localId) { - return FALSE; + UnlockPlayerFieldControls(); + UnfreezeObjectEvents(); + return; } - return TRUE; -} - -static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) -{ - u32 numFreePalSlots = CountFreePaletteSlots(); - u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); + if (indexRoamerOutbreak && GenerateOverworldWildEncounter_CheckRoamer(OWE_GetObjectRoamerOutbreakStatus(object))) + return; -#if P_GENDER_DIFFERENCES - if (isFemale && gSpeciesInfo[speciesId].overworldShinyPaletteFemale != NULL) - tag += OBJ_EVENT_MON_FEMALE; -#endif + u16 speciesId = OW_SPECIES(object); + bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; + u32 gender = OW_FEMALE(object) ? MON_FEMALE : MON_MALE; + u32 level = OWE_GetEncounterLevel(object->sOverworldEncounterLevel); + u32 personality; - // We need at least 2 pal slots open. One for the object and one for the spawn field effect. - // Add this and tiles to seperate graphics check function - if (numFreePalSlots == 1) - { - u32 metatileBehavior = MapGridGetMetatileBehaviorAt(x, y); - struct SpritePalette palette = OWE_GetSpawnAnimFldEffPalette(OWE_GetSpawnDespawnAnimType(metatileBehavior)); - // If the mon's palette or field effect palette isn't already loaded, don't spawn. - // Include check if female or shiny mon is loaded and use that tag if possible - if (IndexOfSpritePaletteTag(tag) == 0xFF && IndexOfSpritePaletteTag(palette.tag) == 0xFF) - return FALSE; - } - else if (numFreePalSlots == 0) + switch (gSpeciesInfo[speciesId].genderRatio) { - return FALSE; + case MON_MALE: + case MON_FEMALE: + case MON_GENDERLESS: + gender = gSpeciesInfo[speciesId].genderRatio; } - return TRUE; -} -#define OWE_FIELD_EFFECT_TILE_NUM 16 // Number of tiiles to add for field effect spawning -static bool32 OWE_CanEncounterBeLoaded_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) -{ - u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); - // const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(speciesId, isShiny, isFemale); - const struct ObjectEventGraphicsInfo *graphicsInfo = GetObjectEventGraphicsInfo(tag); - tag = LoadSheetGraphicsInfo(graphicsInfo, tag, NULL); - u32 tileCount = graphicsInfo->size / TILE_SIZE_4BPP; - if (OW_GFX_COMPRESS) - { - // If tiles are already existing return early, spritesheet is loaded when compressed - if (IndexOfSpriteTileTag(tag) != 0xFF) - { - DebugPrintf("\n\nALREADY LOADED\nSpecies: %S", GetSpeciesName(speciesId)); - return TRUE; - } - - u32 frames = graphicsInfo->anims == sAnimTable_Following_Asym ? 8 : 6; - tileCount *= frames; - } - - tileCount += OWE_FIELD_EFFECT_TILE_NUM; - if (!CanAllocSpriteTiles(tileCount)) - { - DebugPrintf("\n\nNO SPAWN\nSpecies: %S\nSheet Tile Count: %d", GetSpeciesName(speciesId), tileCount); - return FALSE; - } + if (level < MIN_LEVEL || level > MAX_LEVEL) + level = MIN_LEVEL; - DebugPrintf("\n\nSPAWN\nSpecies: %S\nSheet Tile Count: %d", GetSpeciesName(speciesId), tileCount); - FreeSpriteTilesByTag(tag); - return TRUE; -} -#undef OWE_FIELD_EFFECT_TILE_NUM -static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn) -{ - if (gMain.callback2 != CB2_Overworld) + ZeroEnemyPartyMons(); + personality = GetMonPersonality(speciesId, gender, NATURE_RANDOM, RANDOM_UNOWN_LETTER); + CreateMonWithIVs(&gEnemyParty[0], speciesId, level, personality, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); + GiveMonInitialMoveset(&gEnemyParty[0]); + SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); + + if (GenerateOverworldWildEncounter_CheckBattleFrontier(headerId)) return; - enum OverworldEncounterSpawnAnim spawnAnimType; - bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; + if (GenerateOverworldWildEncounter_CheckMassOutbreak(indexRoamerOutbreak, speciesId)) + return; - if (animSpawn) - OWE_PlayMonObjectCry(objectEvent); - - if (!animSpawn && OWE_ShouldPlayMonFleeSound(objectEvent)) - PlaySE(SE_FLEE); + if (GenerateOverworldWildEncounter_CheckDoubleBattle(object, headerId)) + return; - if (WE_OWE_SHINY_SPARKLE && isShiny && animSpawn) - { - PlaySE(SE_SHINY); - spawnAnimType = OWE_SPAWN_ANIM_SHINY; - } - else - { - u32 metatileBehavior = MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x, objectEvent->currentCoords.y); - spawnAnimType = OWE_GetSpawnDespawnAnimType(metatileBehavior); - } - MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); + BattleSetup_StartWildBattle(); } -static u32 GetOldestSlot(bool32 forceRemove) +static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) { - struct ObjectEvent *slotMon, *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; - u32 spawnSlot; + const struct WildPokemonInfo *wildMonInfo; + enum WildPokemonArea wildArea; + enum TimeOfDay timeOfDay; + u32 headerId = GetCurrentMapWildMonHeaderId(); + u32 metatileBehavior = MapGridGetMetatileBehaviorAt(x, y); - for (spawnSlot = 0; spawnSlot < OWE_SPAWNS_MAX; spawnSlot++) + if (headerId == HEADER_NONE) { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE && (!OWE_HasNoDespawnFlag(slotMon) || forceRemove == TRUE)) + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) { - oldest = slotMon; - break; + headerId = GetBattlePikeWildMonHeaderId(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) + return FALSE; + else if (!TryGenerateBattlePikeWildMon(TRUE)) + return FALSE; + + return TRUE; } - } + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + { + headerId = gSaveBlock2Ptr->frontier.curChallengeBattleNum; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) + return FALSE; - if (spawnSlot >= OWE_SPAWNS_MAX) - return OWE_INVALID_SPAWN_SLOT; + u32 id = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); + GenerateBattlePyramidWildMon(SPECIES_NONE); + SetMonData(&gEnemyParty[0], MON_DATA_LEVEL, &id); + return TRUE; + } - for (spawnSlot = 0; spawnSlot < OWE_SPAWNS_MAX; spawnSlot++) + return FALSE; + } + + if (MetatileBehavior_IsWaterWildEncounter(metatileBehavior)) { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE && (!OWE_HasNoDespawnFlag(slotMon) || forceRemove == TRUE)) - { - if (slotMon->sAge > oldest->sAge) - oldest = slotMon; - } + wildArea = WILD_AREA_WATER; + timeOfDay = GetTimeOfDayForEncounters(headerId, wildArea); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + } + else + { + wildArea = WILD_AREA_LAND; + timeOfDay = GetTimeOfDayForEncounters(headerId, wildArea); + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; } - return GetSpawnSlotByLocalId(oldest->localId); -} + if (wildMonInfo == NULL) + return FALSE; -static u8 NextSpawnMonSlot(void) -{ - u32 spawnSlot; + /* + These functions perform checks of various encounter types in the following order: + 1. Attempt to generate a Roamer Encounter + 2. Attempt to generate a Feebas Encounter + 3. Attempt to generate a Mass Outbreak Encounter + 4. Attempt to generate a Standard Wild Encounter + + The structure of this statement ensures that only one of these encounter types can succeed per call, + with the resultant wild mon being created in gEnemyParty[0]. + If none of these checks succeed, speciesId is set to SPECIES_NONE and FALSE is returned. + */ - // All mon slots are in use - if (GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_SPAWNS_MAX) + if (*indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) { - if (WE_OWE_SPAWN_REPLACEMENT) + if (TryStartRoamerEncounter() && !OWE_DoesRoamerObjectExist()) { - // Cycle through so we remove the oldest mon first - spawnSlot = GetOldestSlot(FALSE); - if (spawnSlot == OWE_INVALID_SPAWN_SLOT) - return OWE_INVALID_SPAWN_SLOT; + *indexRoamerOutbreak = OWE_GetObjectRoamerStatusFromIndex(gEncounteredRoamerIndex); + return TRUE; } - else + else if (WE_OWE_FEEBAS_SPOTS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) { - return OWE_INVALID_SPAWN_SLOT; + CreateWildMon(gWildFeebas.species, ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING)); + if (WE_OWE_PREVENT_FEEBAS_DESPAWN) + OWE_SetNoDespawnFlag(level); + + return TRUE; } - } - else - { - for (spawnSlot = 0; spawnSlot < OWE_SPAWNS_MAX; spawnSlot++) + else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior)) { - if (GetOverworldSpeciesBySpawnSlot(spawnSlot) == SPECIES_NONE) - break; + SetUpMassOutbreakEncounter(0); + *indexRoamerOutbreak = OWE_MASS_OUTBREAK_INDEX; + return TRUE; + } + else + { + return TryGenerateWildMon(wildMonInfo, wildArea, 0); } } - return spawnSlot; + return TryGenerateWildMon(wildMonInfo, wildArea, 0); } -static bool8 TrySelectTile(s16* outX, s16* outY) +static bool32 OWE_DoesRoamerObjectExist(void) { - u8 elevation; - u16 tileBehavior; - s16 playerX, playerY; - s16 x, y; - u8 closeDistance; - bool32 isEncounterTile = FALSE; - - // Spawn further away when surfing - if (OWE_ShouldSpawnWaterMons()) - closeDistance = OWE_SPAWN_DISTANCE_WATER; - else - closeDistance = OWE_SPAWN_DISTANCE_LAND; - - // Select a random tile in [-7, -4] [7, 4] range - // Make sure is not directly next to player - // Can we make get random tile its own function for use elsewhere in the codebase? - // Have defines used and then replace MAP_METATILE_VIEW_X/Y with them - do - { - x = (s16)(Random() % OWE_SPAWN_WIDTH_TOTAL) - OWE_SPAWN_WIDTH_RADIUS; - y = (s16)(Random() % OWE_SPAWN_HEIGHT_TOTAL) - OWE_SPAWN_HEIGHT_RADIUS; - } - while (abs(x) <= closeDistance && abs(y) <= closeDistance); - - // We won't spawn mons in the immediate facing direction - // (stops mons spawning in as I'm running in a straight line) - switch (GetPlayerFacingDirection()) - { - case DIR_NORTH: - if(x == 0 && y < 0) - x = -1; - break; - case DIR_SOUTH: - if(x == 0 && y > 0) - x = 1; - break; - case DIR_EAST: - if(y == 0 && x > 0) - y = -1; - break; - case DIR_WEST: - if(y == 0 && x < 0) - y = 1; - break; - default: - break; - } - - PlayerGetDestCoords(&playerX, &playerY); - x += playerX; - y += playerY; - - elevation = MapGridGetElevationAt(x, y); - - if (gMapHeader.mapLayoutId != LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) - { - if (!AreCoordsInsidePlayerMap(x, y)) - return FALSE; - } - else - { - if (x < 0 || x >= 32 || y < 0 || y >= 32) - return FALSE; - } - - - // 0 is change of elevation, 15 is multiple elevation e.g. bridges - // Causes weird interaction issues so just don't let mons spawn here - if (elevation == 0 || elevation == 15) - return FALSE; - - tileBehavior = MapGridGetMetatileBehaviorAt(x, y); - if (OWE_ShouldSpawnWaterMons() && MetatileBehavior_IsWaterWildEncounter(tileBehavior)) - isEncounterTile = TRUE; - - if (!OWE_ShouldSpawnWaterMons() && (MetatileBehavior_IsLandWildEncounter(tileBehavior) || MetatileBehavior_IsIndoorEncounter(tileBehavior))) - isEncounterTile = TRUE; - - if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS - || gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) - isEncounterTile = TRUE; - - if (isEncounterTile && !MapGridGetCollisionAt(x, y)) + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { - *outX = x; - *outY = y; - - if (!GetObjectEventIdByPosition(x, y, 0)) + struct ObjectEvent *object = &gObjectEvents[i]; + if (IsOverworldWildEncounter(object, OWE_ANY) && OWE_GetObjectRoamerOutbreakStatus(object) == gEncounteredRoamerIndex) return TRUE; } return FALSE; } -void GenerateOverworldWildEncounter(void) +static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index) { - u32 localId = gSpecialVar_LastTalked; - u32 objEventId = GetObjectEventIdByLocalId(localId); - u32 headerId = GetCurrentMapWildMonHeaderId(); - struct ObjectEvent *object = &gObjectEvents[objEventId]; - u32 indexRoamerOutbreak = OWE_GetRoamerIndex(object); - - assertf(objEventId < OBJECT_EVENTS_COUNT && IsOverworldWildEncounter(object, OWE_ANY), "cannot start overworld wild enocunter as the selected object is invalid.\nlocalId: %d", localId) - { - UnlockPlayerFieldControls(); - UnfreezeObjectEvents(); - return; - } - - if (indexRoamerOutbreak && GenerateOverworldWildEncounter_CheckRoamer(OWE_GetObjectRoamerOutbreakStatus(object))) - return; + if (index < ROAMER_COUNT) + return index + 1; + + return index; +} - u16 speciesId = OW_SPECIES(object); - bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; - u32 gender = OW_FEMALE(object) ? MON_FEMALE : MON_MALE; - u32 level = OWE_GetEncounterLevel(object->sOverworldEncounterLevel); - u32 personality; +static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) +{ + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + return OWE_INVALID_ROAMER_OUTBREAK; - switch (gSpeciesInfo[speciesId].genderRatio) + u32 status = OWE_GetRoamerIndex(objectEvent); + if (status == OWE_NON_ROAMER_OUTBREAK || status == OWE_MASS_OUTBREAK_INDEX) { - case MON_MALE: - case MON_FEMALE: - case MON_GENDERLESS: - gender = gSpeciesInfo[speciesId].genderRatio; + return OWE_INVALID_ROAMER_OUTBREAK; } - - if (level < MIN_LEVEL || level > MAX_LEVEL) - level = MIN_LEVEL; - - ZeroEnemyPartyMons(); - personality = GetMonPersonality(speciesId, gender, NATURE_RANDOM, RANDOM_UNOWN_LETTER); - CreateMonWithIVs(&gEnemyParty[0], speciesId, level, personality, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); - GiveMonInitialMoveset(&gEnemyParty[0]); - SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); - - if (GenerateOverworldWildEncounter_CheckBattleFrontier(headerId)) - return; - if (GenerateOverworldWildEncounter_CheckMassOutbreak(indexRoamerOutbreak, speciesId)) - return; - - if (GenerateOverworldWildEncounter_CheckDoubleBattle(object, headerId)) - return; - - BattleSetup_StartWildBattle(); + return status - 1; } static bool32 GenerateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak) @@ -717,148 +576,76 @@ static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEven return FALSE; } -struct AgeSort +void OverworldWildEncounter_SetInstantSpawnTimer(void) { - u8 slot:4; - u8 age:4; -}; + if (!WE_OW_ENCOUNTERS) + return; -static void SortOWEMonAges(void) -{ - struct ObjectEvent *slotMon; - struct AgeSort array[OWE_SPAWNS_MAX]; - struct AgeSort current; - u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); - u32 count = 0; - s32 i, j; + sOWESpawnCountdown = 0; +} - if (OWE_SPAWNS_MAX <= 1) +void OverworldWildEncounter_SetMinimumSpawnTimer(void) +{ + if (!WE_OW_ENCOUNTERS) return; - for (i = 0; i < OWE_SPAWNS_MAX; i++) - { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE) - { - array[count].slot = i; - array[count].age = slotMon->sAge; - count++; - } - if (count == numActive) - break; - } + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; + if (LURE_STEP_COUNT && GetNumberActiveOverworldEncounters(OWE_GENERATED) < OWE_SPAWNS_MAX) + sOWESpawnCountdown = OWE_SPAWN_TIME_LURE; +} - for (i = 1; i < numActive; i++) - { - current = array[i]; - j = i - 1; - - while (j >= 0 && array[j].age < current.age) - { - array[j + 1] = array[j]; - j--; - } - - array[j + 1] = current; - } - - array[0].age = numActive; - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(array[0].slot))]; - slotMon->sAge = numActive; - - for (i = 1; i < numActive; i++) - { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(array[i].slot))]; - array[i].age = array[i - 1].age - 1; - slotMon->sAge = array[i].age; - } -} - -void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent) +void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) { - if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + // The only automatically interacts with an OW Encounter when; + // Not using a repel or the DexNav is inactive. + if (WE_OWE_REPEL_DEXNAV_COLLISION && (FlagGet(DN_FLAG_SEARCHING) || REPEL_STEP_COUNT)) return; - - if (IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) - SortOWEMonAges(); - OWE_DoSpawnDespawnAnim(objectEvent, TRUE); -} + bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle, OWE_ANY)); + bool32 playerIsObstacle = (obstacle->isPlayer && IsOverworldWildEncounter(collider, OWE_ANY)); -void OverworldWildEncounter_OnObjectEventDespawned(struct ObjectEvent *objectEvent) -{ - if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + if (!(playerIsCollider || playerIsObstacle)) return; - objectEvent->sOverworldEncounterLevel = 0; - objectEvent->sAge = 0; - objectEvent->sRoamerOutbreakStatus = 0; - - OWE_DoSpawnDespawnAnim(objectEvent, FALSE); -} - -static u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) -{ - SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level, indexRoamerOutbreak); - assertf(OWE_CheckSpecies(*speciesId), "invalid generated overworld encounter\nspecies: %d\ncheck if valid wild mon header exists", speciesId, x, y); - u16 graphicsId = *speciesId + OBJ_EVENT_MON; - - if (*isFemale) - graphicsId += OBJ_EVENT_MON_FEMALE; - - if (*isShiny) - graphicsId += OBJ_EVENT_MON_SHINY; - - return graphicsId; -} - -void OverworldWildEncounter_SetMinimumSpawnTimer(void) -{ - if (!WE_OW_ENCOUNTERS) + struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; + u32 indexRoamerOutbreak = OWE_GetRoamerIndex(wildMon); + if (indexRoamerOutbreak + && indexRoamerOutbreak < OWE_MASS_OUTBREAK_INDEX + && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), + gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) + { + RemoveObjectEvent(wildMon); return; + } - sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - if (LURE_STEP_COUNT && GetNumberActiveOverworldEncounters(OWE_GENERATED) < OWE_SPAWNS_MAX) - sOWESpawnCountdown = OWE_SPAWN_TIME_LURE; + OWE_StartEncounter(wildMon); } -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) +bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) { - u32 personality; - - if (!OWE_CreateEnemyPartyMon(speciesId, level, indexRoamerOutbreak, x, y)) - { - ZeroEnemyPartyMons(); - *speciesId = SPECIES_NONE; - return; - } - - *speciesId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); - OWE_SetEncounterLevel(level, GetMonData(&gEnemyParty[0], MON_DATA_LEVEL)); - personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY); - - if (*speciesId == SPECIES_UNOWN) - *speciesId = GetUnownSpeciesId(personality); + struct ObjectEvent *object = &gObjectEvents[objectEventId]; + if (!IsOverworldWildEncounter(object, OWE_ANY)) + return FALSE; - *isShiny = ComputePlayerShinyOdds(personality, READ_OTID_FROM_SAVE); - if (GetGenderFromSpeciesAndPersonality(*speciesId, personality) == MON_FEMALE) - *isFemale = TRUE; - else - *isFemale = FALSE; + if (IsOverworldWildEncounter(object, OWE_MANUAL) + && GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithOverworldWildEncounter + && GetObjectEventScriptPointerByObjectEventId(objectEventId) != NULL) + return FALSE; - if (WE_OWE_PREVENT_SHINY_DESPAWN && *isShiny) - OWE_SetNoDespawnFlag(level); + gSpecialVar_0x8004 = OW_SPECIES(object); + return TRUE; +} - ZeroEnemyPartyMons(); +static bool32 OWE_ShouldSpawnWaterMons(void) +{ + // Needs refactoring, and this replacing with a check for coords in many cases. + return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); } -static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) +static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) { - const struct WildPokemonInfo *wildMonInfo; - enum WildPokemonArea wildArea; - enum TimeOfDay timeOfDay; u32 headerId = GetCurrentMapWildMonHeaderId(); - u32 metatileBehavior = MapGridGetMetatileBehaviorAt(x, y); + enum TimeOfDay timeOfDay; if (headerId == HEADER_NONE) { @@ -866,229 +653,247 @@ static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoam { headerId = GetBattlePikeWildMonHeaderId(); timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) - return FALSE; - else if (!TryGenerateBattlePikeWildMon(TRUE)) - return FALSE; - - return TRUE; + return gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; } if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) { headerId = gSaveBlock2Ptr->frontier.curChallengeBattleNum; timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) - return FALSE; - - u32 id = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); - GenerateBattlePyramidWildMon(SPECIES_NONE); - SetMonData(&gEnemyParty[0], MON_DATA_LEVEL, &id); - return TRUE; + return gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; } - return FALSE; } - if (MetatileBehavior_IsWaterWildEncounter(metatileBehavior)) - { - wildArea = WILD_AREA_WATER; - timeOfDay = GetTimeOfDayForEncounters(headerId, wildArea); - wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; - } - else + if (shouldSpawnWaterMons) { - wildArea = WILD_AREA_LAND; - timeOfDay = GetTimeOfDayForEncounters(headerId, wildArea); - wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + return gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo != NULL; } - if (wildMonInfo == NULL) - return FALSE; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + return gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; +} - /* - These functions perform checks of various encounter types in the following order: - 1. Attempt to generate a Roamer Encounter - 2. Attempt to generate a Feebas Encounter - 3. Attempt to generate a Mass Outbreak Encounter - 4. Attempt to generate a Standard Wild Encounter - - The structure of this statement ensures that only one of these encounter types can succeed per call, - with the resultant wild mon being created in gEnemyParty[0]. - If none of these checks succeed, speciesId is set to SPECIES_NONE and FALSE is returned. - */ +static bool8 IsSafeToSpawnObjectEvents(void) +{ + struct ObjectEvent* player = &gObjectEvents[gPlayerAvatar.objectEventId]; - if (*indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) + // Only spawn when player is at a valid tile position + return (player->currentCoords.x == player->previousCoords.x && player->currentCoords.y == player->previousCoords.y); +} + +static u32 GetOldestSlot(bool32 forceRemove) +{ + struct ObjectEvent *slotMon, *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; + u32 spawnSlot; + + for (spawnSlot = 0; spawnSlot < OWE_SPAWNS_MAX; spawnSlot++) { - if (TryStartRoamerEncounter() && !OWE_DoesRoamerObjectExist()) + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; + if (OW_SPECIES(slotMon) != SPECIES_NONE && (!OWE_HasNoDespawnFlag(slotMon) || forceRemove == TRUE)) { - *indexRoamerOutbreak = OWE_GetObjectRoamerStatusFromIndex(gEncounteredRoamerIndex); - return TRUE; + oldest = slotMon; + break; } - else if (WE_OWE_FEEBAS_SPOTS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) - { - CreateWildMon(gWildFeebas.species, ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING)); - if (WE_OWE_PREVENT_FEEBAS_DESPAWN) - OWE_SetNoDespawnFlag(level); + } - return TRUE; + if (spawnSlot >= OWE_SPAWNS_MAX) + return OWE_INVALID_SPAWN_SLOT; + + for (spawnSlot = 0; spawnSlot < OWE_SPAWNS_MAX; spawnSlot++) + { + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; + if (OW_SPECIES(slotMon) != SPECIES_NONE && (!OWE_HasNoDespawnFlag(slotMon) || forceRemove == TRUE)) + { + if (slotMon->sAge > oldest->sAge) + oldest = slotMon; } - else if (DoMassOutbreakEncounterTest() && MetatileBehavior_IsLandWildEncounter(metatileBehavior)) + } + + return GetSpawnSlotByLocalId(oldest->localId); +} + +static u8 NextSpawnMonSlot(void) +{ + u32 spawnSlot; + + // All mon slots are in use + if (GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_SPAWNS_MAX) + { + if (WE_OWE_SPAWN_REPLACEMENT) { - SetUpMassOutbreakEncounter(0); - *indexRoamerOutbreak = OWE_MASS_OUTBREAK_INDEX; - return TRUE; + // Cycle through so we remove the oldest mon first + spawnSlot = GetOldestSlot(FALSE); + if (spawnSlot == OWE_INVALID_SPAWN_SLOT) + return OWE_INVALID_SPAWN_SLOT; } else { - return TryGenerateWildMon(wildMonInfo, wildArea, 0); + return OWE_INVALID_SPAWN_SLOT; + } + } + else + { + for (spawnSlot = 0; spawnSlot < OWE_SPAWNS_MAX; spawnSlot++) + { + if (GetOverworldSpeciesBySpawnSlot(spawnSlot) == SPECIES_NONE) + break; } } - return TryGenerateWildMon(wildMonInfo, wildArea, 0); + return spawnSlot; } -static bool8 IsSafeToSpawnObjectEvents(void) +static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) { - struct ObjectEvent* player = &gObjectEvents[gPlayerAvatar.objectEventId]; + u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); + struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; - // Only spawn when player is at a valid tile position - return (player->currentCoords.x == player->previousCoords.x && player->currentCoords.y == player->previousCoords.y); + if (objEventId >= OBJECT_EVENTS_COUNT) + return SPECIES_NONE; + + return OW_SPECIES(objectEvent); } -u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType) +static bool8 TrySelectTile(s16* outX, s16* outY) { - u32 numActive = 0; - for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) + u8 elevation; + u16 tileBehavior; + s16 playerX, playerY; + s16 x, y; + u8 closeDistance; + bool32 isEncounterTile = FALSE; + + // Spawn further away when surfing + if (OWE_ShouldSpawnWaterMons()) + closeDistance = OWE_SPAWN_DISTANCE_WATER; + else + closeDistance = OWE_SPAWN_DISTANCE_LAND; + + // Select a random tile in [-7, -4] [7, 4] range + // Make sure is not directly next to player + // Can we make get random tile its own function for use elsewhere in the codebase? + // Have defines used and then replace MAP_METATILE_VIEW_X/Y with them + do { - if (IsOverworldWildEncounter(&gObjectEvents[i], oweType)) - numActive++; + x = (s16)(Random() % OWE_SPAWN_WIDTH_TOTAL) - OWE_SPAWN_WIDTH_RADIUS; + y = (s16)(Random() % OWE_SPAWN_HEIGHT_TOTAL) - OWE_SPAWN_HEIGHT_RADIUS; } - return numActive; -} + while (abs(x) <= closeDistance && abs(y) <= closeDistance); -static bool32 OWE_ShouldSpawnWaterMons(void) -{ - // Needs refactoring, and this replacing with a check for coords in many cases. - return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); -} + // We won't spawn mons in the immediate facing direction + // (stops mons spawning in as I'm running in a straight line) + switch (GetPlayerFacingDirection()) + { + case DIR_NORTH: + if(x == 0 && y < 0) + x = -1; + break; + case DIR_SOUTH: + if(x == 0 && y > 0) + x = 1; + break; + case DIR_EAST: + if(y == 0 && x > 0) + y = -1; + break; + case DIR_WEST: + if(y == 0 && x < 0) + y = 1; + break; + default: + break; + } + + PlayerGetDestCoords(&playerX, &playerY); + x += playerX; + y += playerY; -void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags) -{ - s16 dx = 0; - s16 dy = 0; + elevation = MapGridGetElevationAt(x, y); - if (gCamera.active) + if (gMapHeader.mapLayoutId != LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) { - dx = gCamera.x; - dy = gCamera.y; + if (!AreCoordsInsidePlayerMap(x, y)) + return FALSE; } - - for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) + else { - struct ObjectEvent *obj = &gObjectEvents[i]; + if (x < 0 || x >= 32 || y < 0 || y >= 32) + return FALSE; + } - if (!obj->active) - continue; - if (!IsOverworldWildEncounter(obj, oweType)) - continue; + // 0 is change of elevation, 15 is multiple elevation e.g. bridges + // Causes weird interaction issues so just don't let mons spawn here + if (elevation == 0 || elevation == 15) + return FALSE; - if (flags & WILD_CHECK_REPEL) - { - if (!REPEL_STEP_COUNT) - continue; + tileBehavior = MapGridGetMetatileBehaviorAt(x, y); + if (OWE_ShouldSpawnWaterMons() && MetatileBehavior_IsWaterWildEncounter(tileBehavior)) + isEncounterTile = TRUE; - if (OWE_HasNoDespawnFlag(obj)) - continue; + if (!OWE_ShouldSpawnWaterMons() && (MetatileBehavior_IsLandWildEncounter(tileBehavior) || MetatileBehavior_IsIndoorEncounter(tileBehavior))) + isEncounterTile = TRUE; - if (IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) - continue; - } + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS + || gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + isEncounterTile = TRUE; - UpdateObjectEventCoords(obj, dx, dy); - RemoveObjectEvent(obj); + if (isEncounterTile && !MapGridGetCollisionAt(x, y)) + { + *outX = x; + *outY = y; + + if (!GetObjectEventIdByPosition(x, y, 0)) + return TRUE; } + + return FALSE; } -static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) +static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) { - u32 headerId = GetCurrentMapWildMonHeaderId(); - enum TimeOfDay timeOfDay; + u32 personality; - if (headerId == HEADER_NONE) + if (!OWE_CreateEnemyPartyMon(speciesId, level, indexRoamerOutbreak, x, y)) { - if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) - { - headerId = GetBattlePikeWildMonHeaderId(); - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - return gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; - } - if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) - { - headerId = gSaveBlock2Ptr->frontier.curChallengeBattleNum; - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - return gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; - } - return FALSE; + ZeroEnemyPartyMons(); + *speciesId = SPECIES_NONE; + return; } + + *speciesId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); + OWE_SetEncounterLevel(level, GetMonData(&gEnemyParty[0], MON_DATA_LEVEL)); + personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY); - if (shouldSpawnWaterMons) - { - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); - return gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo != NULL; - } + if (*speciesId == SPECIES_UNOWN) + *speciesId = GetUnownSpeciesId(personality); - timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); - return gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; -} + *isShiny = ComputePlayerShinyOdds(personality, READ_OTID_FROM_SAVE); + if (GetGenderFromSpeciesAndPersonality(*speciesId, personality) == MON_FEMALE) + *isFemale = TRUE; + else + *isFemale = FALSE; -bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType) -{ - bool32 isOWE = (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_OW_WILD_ENCOUNTER); - switch (oweType) - { - default: - case OWE_ANY: - return isOWE; - - case OWE_GENERATED: - return isOWE && (objectEvent->localId <= LOCALID_OW_ENCOUNTER_END - && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); + if (WE_OWE_PREVENT_SHINY_DESPAWN && *isShiny) + OWE_SetNoDespawnFlag(level); - case OWE_MANUAL: - return isOWE && (objectEvent->localId > LOCALID_OW_ENCOUNTER_END - || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); - } + ZeroEnemyPartyMons(); } -static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) +static u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) { - u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); - struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; - - if (objEventId >= OBJECT_EVENTS_COUNT) - return SPECIES_NONE; + SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level, indexRoamerOutbreak); + assertf(OWE_CheckSpecies(*speciesId), "invalid generated overworld encounter\nspecies: %d\ncheck if valid wild mon header exists", speciesId, x, y); + u16 graphicsId = *speciesId + OBJ_EVENT_MON; - return OW_SPECIES(objectEvent); -} + if (*isFemale) + graphicsId += OBJ_EVENT_MON_FEMALE; -static UNUSED u32 GetNewestOWEncounterLocalId(void) -{ - struct ObjectEvent *slotMon; - struct ObjectEvent *newest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; - u32 i; - - for (i = 0; i < OWE_SPAWNS_MAX; i++) - { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE) - { - if (newest->sAge > slotMon->sAge) - newest = slotMon; - } - } + if (*isShiny) + graphicsId += OBJ_EVENT_MON_SHINY; - return GetSpawnSlotByLocalId(newest->localId); + return graphicsId; } static bool32 CanRemoveOverworldEncounter(u32 localId) @@ -1099,405 +904,366 @@ static bool32 CanRemoveOverworldEncounter(u32 localId) || localId > LOCALID_OW_ENCOUNTER_END)); } -static u32 RemoveOldestGeneratedOverworldEncounter(void) -{ - u32 oldestSlot = GetOldestSlot(TRUE); - - if (oldestSlot == OWE_INVALID_SPAWN_SLOT) - return OBJECT_EVENTS_COUNT; - - u32 objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(oldestSlot)); - RemoveObjectEvent(&gObjectEvents[objectEventId]); - return objectEventId; -} - -bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) +static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) { - struct ObjectEvent *object = &gObjectEvents[objectEventId]; - if (!IsOverworldWildEncounter(object, OWE_ANY)) + assertf(OWE_CanEncounterBeLoaded_Palette(speciesId, isFemale, isShiny, x, y), "could not load palette for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) + { return FALSE; + } - if (IsOverworldWildEncounter(object, OWE_MANUAL) - && GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithOverworldWildEncounter - && GetObjectEventScriptPointerByObjectEventId(objectEventId) != NULL) + assertf(OWE_CanEncounterBeLoaded_Tiles(speciesId, isFemale, isShiny, x, y), "could not load sprite tiles for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) + { return FALSE; + } - gSpecialVar_0x8004 = OW_SPECIES(object); return TRUE; } -const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) +static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) { - if (template->trainerType != TRAINER_TYPE_OW_WILD_ENCOUNTER || (template->localId <= LOCALID_OW_ENCOUNTER_END - && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX))) - return *template; - - struct ObjectEventTemplate templateOWE = *template; - - // Does this work? - u32 graphicsId; - u16 speciesId, speciesTemplate = SanitizeSpeciesId(templateOWE.graphicsId & OBJ_EVENT_MON_SPECIES_MASK); - bool32 isShiny = FALSE, isShinyTemplate = (templateOWE.graphicsId & OBJ_EVENT_MON_SHINY) ? TRUE : FALSE; - bool32 isFemale = FALSE; - u32 level = MIN_LEVEL, levelTemplate = templateOWE.sOverworldEncounterLevel; - u32 indexRoamerOutbreak = OWE_INVALID_ROAMER_OUTBREAK; - u32 x = template->x; - u32 y = template->y; - - SetOverworldEncounterSpeciesInfo( - x, - y, - &speciesId, - &isShiny, - &isFemale, - &level, - &indexRoamerOutbreak - ); + u32 numFreePalSlots = CountFreePaletteSlots(); + u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); - if (speciesTemplate) - speciesId = speciesTemplate; +#if P_GENDER_DIFFERENCES + if (isFemale && gSpeciesInfo[speciesId].overworldShinyPaletteFemale != NULL) + tag += OBJ_EVENT_MON_FEMALE; +#endif - if (levelTemplate) - level = levelTemplate; + // We need at least 2 pal slots open. One for the object and one for the spawn field effect. + // Add this and tiles to seperate graphics check function + if (numFreePalSlots == 1) + { + u32 metatileBehavior = MapGridGetMetatileBehaviorAt(x, y); + struct SpritePalette palette = OWE_GetSpawnAnimFldEffPalette(OWE_GetSpawnDespawnAnimType(metatileBehavior)); + // If the mon's palette or field effect palette isn't already loaded, don't spawn. + // Include check if female or shiny mon is loaded and use that tag if possible + if (IndexOfSpritePaletteTag(tag) == 0xFF && IndexOfSpritePaletteTag(palette.tag) == 0xFF) + return FALSE; + } + else if (numFreePalSlots == 0) + { + return FALSE; + } - bool32 validSpecies = OWE_CheckSpecies(speciesId); - bool32 validLevel = OWE_GetEncounterLevel(level) >= MIN_LEVEL && OWE_GetEncounterLevel(level) <= MAX_LEVEL; - assertf(validSpecies && validLevel, "invalid manual overworld encounter\nspecies: %d\nlevel: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, level, x, y) + return TRUE; +} +#define OWE_FIELD_EFFECT_TILE_NUM 16 // Number of tiiles to add for field effect spawning +static bool32 OWE_CanEncounterBeLoaded_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) +{ + u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); + // const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(speciesId, isShiny, isFemale); + const struct ObjectEventGraphicsInfo *graphicsInfo = GetObjectEventGraphicsInfo(tag); + tag = LoadSheetGraphicsInfo(graphicsInfo, tag, NULL); + u32 tileCount = graphicsInfo->size / TILE_SIZE_4BPP; + if (OW_GFX_COMPRESS) { - if (!validSpecies) - { - // Currently causes assertf on each player step as function is called. - templateOWE.graphicsId = OBJ_EVENT_GFX_BOY_1; - templateOWE.trainerType = TRAINER_TYPE_NONE; - templateOWE.sOverworldEncounterLevel = 0; - templateOWE.movementType = MOVEMENT_TYPE_NONE; - return templateOWE; - } - else if (!validLevel) + // If tiles are already existing return early, spritesheet is loaded when compressed + if (IndexOfSpriteTileTag(tag) != 0xFF) { - level = MIN_LEVEL; + DebugPrintf("\n\nALREADY LOADED\nSpecies: %S", GetSpeciesName(speciesId)); + return TRUE; } + + u32 frames = graphicsInfo->anims == sAnimTable_Following_Asym ? 8 : 6; + tileCount *= frames; } - - if (isShinyTemplate) - isShiny = isShinyTemplate; - - if (templateOWE.graphicsId & OBJ_EVENT_MON && templateOWE.graphicsId & OBJ_EVENT_MON_FEMALE) - isFemale = TRUE; - else if (templateOWE.graphicsId & OBJ_EVENT_MON) - isFemale = FALSE; - else - isFemale = GetGenderFromSpeciesAndPersonality(speciesId, Random32()) == MON_FEMALE; - - if (templateOWE.movementType == MOVEMENT_TYPE_NONE) - templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); - - graphicsId = speciesId + OBJ_EVENT_MON; - if (isFemale) - graphicsId += OBJ_EVENT_MON_FEMALE; - if (isShiny) - graphicsId += OBJ_EVENT_MON_SHINY; - - templateOWE.graphicsId = graphicsId; - templateOWE.sOverworldEncounterLevel = level; - return templateOWE; + tileCount += OWE_FIELD_EFFECT_TILE_NUM; + if (!CanAllocSpriteTiles(tileCount)) + { + DebugPrintf("\n\nNO SPAWN\nSpecies: %S\nSheet Tile Count: %d", GetSpeciesName(speciesId), tileCount); + return FALSE; + } + + DebugPrintf("\n\nSPAWN\nSpecies: %S\nSheet Tile Count: %d", GetSpeciesName(speciesId), tileCount); + FreeSpriteTilesByTag(tag); + return TRUE; } +#undef OWE_FIELD_EFFECT_TILE_NUM -void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) +static void SortOWEMonAges(void) { - // The only automatically interacts with an OW Encounter when; - // Not using a repel or the DexNav is inactive. - if (WE_OWE_REPEL_DEXNAV_COLLISION && (FlagGet(DN_FLAG_SEARCHING) || REPEL_STEP_COUNT)) - return; - - bool32 playerIsCollider = (collider->isPlayer && IsOverworldWildEncounter(obstacle, OWE_ANY)); - bool32 playerIsObstacle = (obstacle->isPlayer && IsOverworldWildEncounter(collider, OWE_ANY)); + struct ObjectEvent *slotMon; + struct AgeSort array[OWE_SPAWNS_MAX]; + struct AgeSort current; + u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); + u32 count = 0; + s32 i, j; - if (!(playerIsCollider || playerIsObstacle)) + if (OWE_SPAWNS_MAX <= 1) return; - struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; - u32 indexRoamerOutbreak = OWE_GetRoamerIndex(wildMon); - if (indexRoamerOutbreak - && indexRoamerOutbreak < OWE_MASS_OUTBREAK_INDEX - && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), - gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) + for (i = 0; i < OWE_SPAWNS_MAX; i++) { - RemoveObjectEvent(wildMon); - return; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; + if (OW_SPECIES(slotMon) != SPECIES_NONE) + { + array[count].slot = i; + array[count].age = slotMon->sAge; + count++; + } + if (count == numActive) + break; } - OWE_StartEncounter(wildMon); -} + for (i = 1; i < numActive; i++) + { + current = array[i]; + j = i - 1; -void OverworldWildEncounter_DespawnOnBattle(void) -{ - struct ObjectEvent *object = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; - if (!IsOverworldWildEncounter(object, OWE_ANY)) - return; + while (j >= 0 && array[j].age < current.age) + { + array[j + 1] = array[j]; + j--; + } - if (IsOverworldWildEncounter(object, OWE_MANUAL)) - FlagSet(GetObjectEventFlagIdByLocalIdAndMap(object->localId, object->mapNum, object->mapGroup)); + array[j + 1] = current; + } - RemoveObjectEvent(object); - OWE_SetNewSpawnCountdown(); - gSpecialVar_LastTalked = LOCALID_NONE; + array[0].age = numActive; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(array[0].slot))]; + slotMon->sAge = numActive; + + for (i = 1; i < numActive; i++) + { + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(array[i].slot))]; + array[i].age = array[i - 1].age - 1; + slotMon->sAge = array[i].age; + } } -// Returns TRUE if movement is restricted. -bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Direction direction) +void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent) { - if (GetCollisionInDirection(objectEvent, direction)) - return TRUE; - - if (OWE_CanAwareMonSeePlayer(objectEvent) && WE_OWE_UNRESTRICT_SIGHT) - return FALSE; - - s32 xCurrent = objectEvent->currentCoords.x; - s32 yCurrent = objectEvent->currentCoords.y; - s32 xNew = xCurrent + gDirectionToVectors[direction].x; - s32 yNew = yCurrent + gDirectionToVectors[direction].y; - - if (OWE_CheckRestrictMovementMetatile(xCurrent, yCurrent, xNew, yNew)) - return TRUE; + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + return; - if (OWE_CheckRestrictMovementMap(objectEvent, xNew, yNew)) - return TRUE; + if (IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) + SortOWEMonAges(); - return FALSE; + OWE_DoSpawnDespawnAnim(objectEvent, TRUE); } -bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon) +void OverworldWildEncounter_OnObjectEventDespawned(struct ObjectEvent *objectEvent) { - if (!IsOverworldWildEncounter(mon, OWE_ANY) || mon->movementType == MOVEMENT_TYPE_WANDER_AROUND_OWE) - return FALSE; - - if (OWE_IsPlayerInsideMonActiveDistance(mon) && (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) - || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING))) - return TRUE; - - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - u32 speciesId = OW_SPECIES(mon); - u32 viewDistance = OWE_GetViewDistanceFromSpecies(speciesId); - u32 viewWidth = OWE_GetViewWidthFromSpecies(speciesId); - s32 halfWidth = (viewWidth - 1) / 2; - enum Direction direction = mon->facingDirection; - bool32 retVal = FALSE; - - switch (direction) - { - case DIR_NORTH: - if (player->currentCoords.y < mon->currentCoords.y - && mon->currentCoords.y - player->currentCoords.y <= viewDistance - && player->currentCoords.x >= mon->currentCoords.x - halfWidth - && player->currentCoords.x <= mon->currentCoords.x + halfWidth) - retVal = TRUE; - break; - - case DIR_SOUTH: - if (player->currentCoords.y > mon->currentCoords.y - && player->currentCoords.y - mon->currentCoords.y <= viewDistance - && player->currentCoords.x >= mon->currentCoords.x - halfWidth - && player->currentCoords.x <= mon->currentCoords.x + halfWidth) - retVal = TRUE; - break; - - case DIR_EAST: - if (player->currentCoords.x > mon->currentCoords.x - && player->currentCoords.x - mon->currentCoords.x <= viewDistance - && player->currentCoords.y >= mon->currentCoords.y - halfWidth - && player->currentCoords.y <= mon->currentCoords.y + halfWidth) - retVal = TRUE; - break; - - case DIR_WEST: - if (player->currentCoords.x < mon->currentCoords.x - && mon->currentCoords.x - player->currentCoords.x <= viewDistance - && player->currentCoords.y >= mon->currentCoords.y - halfWidth - && player->currentCoords.y <= mon->currentCoords.y + halfWidth) - retVal = TRUE; - break; - - default: - retVal = FALSE; - break; - } + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + return; - if (retVal && OWE_IsLineOfSightClear(player, GetOppositeDirection(direction), viewDistance)) - return TRUE; + objectEvent->sOverworldEncounterLevel = 0; + objectEvent->sAge = 0; + objectEvent->sRoamerOutbreakStatus = 0; - return FALSE; + OWE_DoSpawnDespawnAnim(objectEvent, FALSE); } -bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon) +bool32 OWE_IsDespawnExempt(struct ObjectEvent *objectEvent) { - if (!IsOverworldWildEncounter(mon, OWE_ANY)) + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return FALSE; - - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - u32 distance = OWE_CHASE_RANGE; - u32 speciesId = OW_SPECIES(mon); - - if (speciesId != SPECIES_NONE) - distance = OWE_GetViewActiveDistanceFromSpecies(speciesId); - if (player->currentCoords.y <= mon->currentCoords.y + distance && player->currentCoords.y >= mon->currentCoords.y - distance - && player->currentCoords.x <= mon->currentCoords.x + distance && player->currentCoords.x >= mon->currentCoords.x - distance) + if (OWE_HasNoDespawnFlag(objectEvent) && AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) return TRUE; + objectEvent->offScreen = TRUE; return FALSE; } -static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 xNew, s16 yNew, enum Direction newDirection, enum Direction collisionDirection) +bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent) { - if (OWE_CheckRestrictMovementMetatile(mon->currentCoords.x, mon->currentCoords.y, xNew, yNew)) + if (!IsOverworldWildEncounter(curObject, OWE_GENERATED) || IsOverworldWildEncounter(objectEvent, OWE_ANY) || objectEvent->isPlayer) return FALSE; - if (OWE_CheckRestrictMovementMap(mon, xNew, yNew)) - return FALSE; + RemoveObjectEvent(curObject); + return TRUE; +} - if (GetCollisionAtCoords(mon, xNew, yNew, collisionDirection)) - return FALSE; +u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y) +{ + if (!(collision & (1 << (COLLISION_OBJECT_EVENT - 1)))) + return collision; - return TRUE; + struct ObjectEvent *objectEvent = &gObjectEvents[GetObjectEventIdByXY(x, y)]; + if (!IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) + return collision; + + RemoveObjectEvent(objectEvent); + return collision & (1 << (COLLISION_OBJECT_EVENT - 1)); } -static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection) +void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags) { - s16 x = mon->currentCoords.x; - s16 y = mon->currentCoords.y; + s16 dx = 0; + s16 dy = 0; - MoveCoords(newDirection, &x, &y); - if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, newDirection)) + if (gCamera.active) { - if (mon->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) - return GetOppositeDirection(newDirection); - - MoveCoords(mon->movementDirection, &x, &y); - if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, mon->movementDirection)) - return newDirection; + dx = gCamera.x; + dy = gCamera.y; } - x = mon->currentCoords.x; - y = mon->currentCoords.y; - MoveCoords(GetOppositeDirection(newDirection), &x, &y); - if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, newDirection)) + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { - if (mon->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) - return newDirection; + struct ObjectEvent *obj = &gObjectEvents[i]; - MoveCoords(mon->movementDirection, &x, &y); - if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, mon->movementDirection)) - return GetOppositeDirection(newDirection); - } + if (!obj->active) + continue; - return mon->movementDirection; + if (!IsOverworldWildEncounter(obj, oweType)) + continue; + + if (flags & WILD_CHECK_REPEL) + { + if (!REPEL_STEP_COUNT) + continue; + + if (OWE_HasNoDespawnFlag(obj)) + continue; + + if (IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) + continue; + } + + UpdateObjectEventCoords(obj, dx, dy); + RemoveObjectEvent(obj); + } } -u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) +bool32 OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId) { - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - - switch (mon->movementDirection) + // does this need to be used in OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette + if (CanRemoveOverworldEncounter(localId)) { - case DIR_NORTH: - case DIR_SOUTH: - if (player->currentCoords.x < mon->currentCoords.x) - return DIR_WEST; - else if (player->currentCoords.x == mon->currentCoords.x) - return OWE_CheckPathToPlayerFromCollision(mon, (Random() % 2) == 0 ? DIR_EAST : DIR_WEST); - else - return DIR_EAST; - case DIR_EAST: - case DIR_WEST: - if (player->currentCoords.y < mon->currentCoords.y) - return DIR_NORTH; - else if (player->currentCoords.y == mon->currentCoords.y) - return OWE_CheckPathToPlayerFromCollision(mon, (Random() % 2) == 0 ? DIR_NORTH : DIR_SOUTH); + *objectEventId = RemoveOldestGeneratedOverworldEncounter(); + + if (*objectEventId == OBJECT_EVENTS_COUNT) + return TRUE; else - return DIR_SOUTH; + return FALSE; } + + return TRUE; +} - return mon->movementDirection; +void OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette(void) +{ + // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles + if (WE_OW_ENCOUNTERS && CountFreePaletteSlots() < 2) + { + u32 count = GetNumberActiveOverworldEncounters(OWE_GENERATED); + + if (count > 0) + { + for (; count > 0; count--) + { + RemoveOldestGeneratedOverworldEncounter(); + if (CountFreePaletteSlots() >= 2) + break; + } + } + } } -bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon) +void OverworldWildEncounter_DespawnOnBattle(void) { - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + struct ObjectEvent *object = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; + if (!IsOverworldWildEncounter(object, OWE_ANY)) + return; - if ((mon->currentCoords.x != player->currentCoords.x && mon->currentCoords.y != player->currentCoords.y) || (mon->currentCoords.x < player->currentCoords.x - 1 || mon->currentCoords.x > player->currentCoords.x + 1 || mon->currentCoords.y < player->currentCoords.y - 1 || mon->currentCoords.y > player->currentCoords.y + 1)) - return FALSE; + if (IsOverworldWildEncounter(object, OWE_MANUAL)) + FlagSet(GetObjectEventFlagIdByLocalIdAndMap(object->localId, object->mapNum, object->mapGroup)); - return TRUE; + RemoveObjectEvent(object); + OWE_SetNewSpawnCountdown(); + gSpecialVar_LastTalked = LOCALID_NONE; } -u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances) +void OWE_TryDespawnOverworldWildEncountersCrossingMapConnection(void) { - struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - s16 absX, absY; - s16 distanceX = player->currentCoords.x - mon->currentCoords.x; - s16 distanceY = player->currentCoords.y - mon->currentCoords.y; + if (gMain.callback2 != CB2_Overworld) + return; - // Get absolute X distance. - if (distanceX < 0) - absX = distanceX * -1; - else - absX = distanceX; + if (!WE_OWE_DESPAWN_ON_ENTER_TOWN) + return; - // Get absolute Y distance. - if (distanceY < 0) - absY = distanceY * -1; - else - absY = distanceY; + if (gMapHeader.mapType != MAP_TYPE_CITY && gMapHeader.mapType != MAP_TYPE_TOWN) + return; - if (absY == absX) - *equalDistances = TRUE; + if (WE_OWE_DESPAWN_SOUND) + PlaySE(SE_FLEE); + + DespwnAllOverworldWildEncounterObjects(OWE_ANY, 0); +} - if (absY > absX) - return absY; - else - return absX; +static u32 RemoveOldestGeneratedOverworldEncounter(void) +{ + u32 oldestSlot = GetOldestSlot(TRUE); + + if (oldestSlot == OWE_INVALID_SPAWN_SLOT) + return OBJECT_EVENTS_COUNT; + + u32 objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(oldestSlot)); + RemoveObjectEvent(&gObjectEvents[objectEventId]); + return objectEventId; } -static enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior) +static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) { - if (MetatileBehavior_IsPokeGrass(metatileBehavior) || MetatileBehavior_IsAshGrass(metatileBehavior)) - return OWE_SPAWN_ANIM_GRASS; - else if (MetatileBehavior_IsLongGrass(metatileBehavior)) - return OWE_SPAWN_ANIM_LONG_GRASS; - else if (MetatileBehavior_IsSurfableFishableWater(metatileBehavior) && gMapHeader.mapType != MAP_TYPE_UNDERWATER) - return OWE_SPAWN_ANIM_WATER; - else if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_UNDERWATER)) - return OWE_SPAWN_ANIM_UNDERWATER; - else - return OWE_SPAWN_ANIM_CAVE; + if (!IsOverworldWildEncounter(object, OWE_GENERATED)) + return FALSE; + + return WE_OWE_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_SPAWNS_MAX; } -static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) +static void OWE_SetNewSpawnCountdown(void) { - u32 numActive = GetNumberActiveOverworldEncounters(OWE_ANY); - u32 randomIndex; - u32 counter = 0; - struct ObjectEvent *object; + u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); - if (numActive) - randomIndex = Random() % numActive; + if (WE_OWE_SPAWN_REPLACEMENT && numActive >= OWE_SPAWNS_MAX) + sOWESpawnCountdown = OWE_SPAWN_TIME_REPLACEMENT; + else if (LURE_STEP_COUNT && numActive < OWE_SPAWNS_MAX) + sOWESpawnCountdown = OWE_SPAWN_TIME_LURE; else - return NULL; + sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); +} + +static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn) +{ + if (gMain.callback2 != CB2_Overworld) + return; - for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) + enum OverworldEncounterSpawnAnim spawnAnimType; + bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; + + if (animSpawn) + OWE_PlayMonObjectCry(objectEvent); + + if (!animSpawn && OWE_ShouldPlayMonFleeSound(objectEvent)) + PlaySE(SE_FLEE); + + if (WE_OWE_SHINY_SPARKLE && isShiny && animSpawn) { - object = &gObjectEvents[i]; - if (IsOverworldWildEncounter(object, OWE_ANY)) - { - if (counter >= randomIndex) - return object; - else - counter++; - } + PlaySE(SE_SHINY); + spawnAnimType = OWE_SPAWN_ANIM_SHINY; } - return NULL; + else + { + u32 metatileBehavior = MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x, objectEvent->currentCoords.y); + spawnAnimType = OWE_GetSpawnDespawnAnimType(metatileBehavior); + } + MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); +} + +static enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior) +{ + if (MetatileBehavior_IsPokeGrass(metatileBehavior) || MetatileBehavior_IsAshGrass(metatileBehavior)) + return OWE_SPAWN_ANIM_GRASS; + else if (MetatileBehavior_IsLongGrass(metatileBehavior)) + return OWE_SPAWN_ANIM_LONG_GRASS; + else if (MetatileBehavior_IsSurfableFishableWater(metatileBehavior) && gMapHeader.mapType != MAP_TYPE_UNDERWATER) + return OWE_SPAWN_ANIM_WATER; + else if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_UNDERWATER)) + return OWE_SPAWN_ANIM_UNDERWATER; + else + return OWE_SPAWN_ANIM_CAVE; } static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) @@ -1539,18 +1305,94 @@ static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); } -static bool32 OWE_DoesRoamerObjectExist(void) +static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) { + u32 numActive = GetNumberActiveOverworldEncounters(OWE_ANY); + u32 randomIndex; + u32 counter = 0; + struct ObjectEvent *object; + + if (numActive) + randomIndex = Random() % numActive; + else + return NULL; + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { - struct ObjectEvent *object = &gObjectEvents[i]; - if (IsOverworldWildEncounter(object, OWE_ANY) && OWE_GetObjectRoamerOutbreakStatus(object) == gEncounteredRoamerIndex) - return TRUE; + object = &gObjectEvents[i]; + if (IsOverworldWildEncounter(object, OWE_ANY)) + { + if (counter >= randomIndex) + return object; + else + counter++; + } } + return NULL; +} + +static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) +{ + if (!IsOverworldWildEncounter(objectEvent, OWE_ANY) || OW_SPECIES(objectEvent) == SPECIES_NONE) + return FALSE; + + if (!AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) + return FALSE; + + if (OWE_ShouldDespawnGeneratedForNewOWE(objectEvent)) + return FALSE; + + if (objectEvent->offScreen) + return FALSE; + + return WE_OWE_DESPAWN_SOUND; +} + +#define sTypeFuncId data[1] // Same as in src/event_object_movement.c +void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite) +{ + if (IsOverworldWildEncounter(objectEvent, OWE_ANY) && OWE_HasSavedMovementState(objectEvent)) + sprite->sTypeFuncId = OWE_RESTORED_MOVEMENT_FUNC_ID; +} +#undef sTypeFuncId + +// Returns TRUE if movement is restricted. +bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Direction direction) +{ + if (GetCollisionInDirection(objectEvent, direction)) + return TRUE; + + if (OWE_CanAwareMonSeePlayer(objectEvent) && WE_OWE_UNRESTRICT_SIGHT) + return FALSE; + + s32 xCurrent = objectEvent->currentCoords.x; + s32 yCurrent = objectEvent->currentCoords.y; + s32 xNew = xCurrent + gDirectionToVectors[direction].x; + s32 yNew = yCurrent + gDirectionToVectors[direction].y; + + if (OWE_CheckRestrictMovementMetatile(xCurrent, yCurrent, xNew, yNew)) + return TRUE; + + if (OWE_CheckRestrictMovementMap(objectEvent, xNew, yNew)) + return TRUE; return FALSE; } +static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 xNew, s16 yNew, enum Direction newDirection, enum Direction collisionDirection) +{ + if (OWE_CheckRestrictMovementMetatile(mon->currentCoords.x, mon->currentCoords.y, xNew, yNew)) + return FALSE; + + if (OWE_CheckRestrictMovementMap(mon, xNew, yNew)) + return FALSE; + + if (GetCollisionAtCoords(mon, xNew, yNew, collisionDirection)) + return FALSE; + + return TRUE; +} + static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew) { if (!WE_OWE_RESTRICT_METATILE) @@ -1590,135 +1432,216 @@ static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 return AreCoordsInsidePlayerMap(xNew, yNew); } -static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) +bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon) { - if (!IsOverworldWildEncounter(objectEvent, OWE_ANY) || OW_SPECIES(objectEvent) == SPECIES_NONE) + if (!IsOverworldWildEncounter(mon, OWE_ANY) || mon->movementType == MOVEMENT_TYPE_WANDER_AROUND_OWE) return FALSE; - if (!AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) - return FALSE; + if (OWE_IsPlayerInsideMonActiveDistance(mon) && (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) + || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING))) + return TRUE; - if (OWE_ShouldDespawnGeneratedForNewOWE(objectEvent)) - return FALSE; + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + u32 speciesId = OW_SPECIES(mon); + u32 viewDistance = OWE_GetViewDistanceFromSpecies(speciesId); + u32 viewWidth = OWE_GetViewWidthFromSpecies(speciesId); + s32 halfWidth = (viewWidth - 1) / 2; + enum Direction direction = mon->facingDirection; + bool32 retVal = FALSE; - if (objectEvent->offScreen) - return FALSE; + switch (direction) + { + case DIR_NORTH: + if (player->currentCoords.y < mon->currentCoords.y + && mon->currentCoords.y - player->currentCoords.y <= viewDistance + && player->currentCoords.x >= mon->currentCoords.x - halfWidth + && player->currentCoords.x <= mon->currentCoords.x + halfWidth) + retVal = TRUE; + break; - return WE_OWE_DESPAWN_SOUND; + case DIR_SOUTH: + if (player->currentCoords.y > mon->currentCoords.y + && player->currentCoords.y - mon->currentCoords.y <= viewDistance + && player->currentCoords.x >= mon->currentCoords.x - halfWidth + && player->currentCoords.x <= mon->currentCoords.x + halfWidth) + retVal = TRUE; + break; -} + case DIR_EAST: + if (player->currentCoords.x > mon->currentCoords.x + && player->currentCoords.x - mon->currentCoords.x <= viewDistance + && player->currentCoords.y >= mon->currentCoords.y - halfWidth + && player->currentCoords.y <= mon->currentCoords.y + halfWidth) + retVal = TRUE; + break; -static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index) -{ - if (index < ROAMER_COUNT) - return index + 1; + case DIR_WEST: + if (player->currentCoords.x < mon->currentCoords.x + && mon->currentCoords.x - player->currentCoords.x <= viewDistance + && player->currentCoords.y >= mon->currentCoords.y - halfWidth + && player->currentCoords.y <= mon->currentCoords.y + halfWidth) + retVal = TRUE; + break; + + default: + retVal = FALSE; + break; + } + + if (retVal && OWE_IsLineOfSightClear(player, GetOppositeDirection(direction), viewDistance)) + return TRUE; - return index; + return FALSE; } -static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) +static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance) { - if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) - return OWE_INVALID_ROAMER_OUTBREAK; + s16 x = player->currentCoords.x; + s16 y = player->currentCoords.y; + u32 i; - u32 status = OWE_GetRoamerIndex(objectEvent); - if (status == OWE_NON_ROAMER_OUTBREAK || status == OWE_MASS_OUTBREAK_INDEX) + for (i = 0; i < distance; i++) { - return OWE_INVALID_ROAMER_OUTBREAK; + MoveCoords(direction, &x, &y); + if (MapGridGetCollisionAt(x, y) + || GetMapBorderIdAt(x, y) == CONNECTION_INVALID + || IsMetatileDirectionallyImpassable(player, x, y, GetOppositeDirection(direction)) + || IsElevationMismatchAt(player->currentElevation, x, y)) + return FALSE; } + + return TRUE; +} + +bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon) +{ + if (!IsOverworldWildEncounter(mon, OWE_ANY)) + return FALSE; - return status - 1; + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + u32 distance = OWE_CHASE_RANGE; + u32 speciesId = OW_SPECIES(mon); + + if (speciesId != SPECIES_NONE) + distance = OWE_GetViewActiveDistanceFromSpecies(speciesId); + + if (player->currentCoords.y <= mon->currentCoords.y + distance && player->currentCoords.y >= mon->currentCoords.y - distance + && player->currentCoords.x <= mon->currentCoords.x + distance && player->currentCoords.x >= mon->currentCoords.x - distance) + return TRUE; + + return FALSE; } -static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) +bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon) { - if (!IsOverworldWildEncounter(object, OWE_GENERATED)) + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + + if ((mon->currentCoords.x != player->currentCoords.x && mon->currentCoords.y != player->currentCoords.y) || (mon->currentCoords.x < player->currentCoords.x - 1 || mon->currentCoords.x > player->currentCoords.x + 1 || mon->currentCoords.y < player->currentCoords.y - 1 || mon->currentCoords.y > player->currentCoords.y + 1)) return FALSE; - return WE_OWE_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_SPAWNS_MAX; + return TRUE; } -void OWE_StartEncounter(struct ObjectEvent *mon) +u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) { - gSpecialVar_LastTalked = mon->localId; - gSpecialVar_0x8004 = OW_SPECIES(mon); - gSelectedObjectEvent = GetObjectEventIdByLocalId(mon->localId); + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - // Stop the bobbing animation. - if (mon->movementActionId >= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_DOWN && mon->movementActionId <= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_RIGHT) - ClearObjectEventMovement(mon, &gSprites[mon->spriteId]); + switch (mon->movementDirection) + { + case DIR_NORTH: + case DIR_SOUTH: + if (player->currentCoords.x < mon->currentCoords.x) + return DIR_WEST; + else if (player->currentCoords.x == mon->currentCoords.x) + return OWE_CheckPathToPlayerFromCollision(mon, (Random() % 2) == 0 ? DIR_EAST : DIR_WEST); + else + return DIR_EAST; + case DIR_EAST: + case DIR_WEST: + if (player->currentCoords.y < mon->currentCoords.y) + return DIR_NORTH; + else if (player->currentCoords.y == mon->currentCoords.y) + return OWE_CheckPathToPlayerFromCollision(mon, (Random() % 2) == 0 ? DIR_NORTH : DIR_SOUTH); + else + return DIR_SOUTH; + } - ScriptContext_SetupScript(InteractWithOverworldWildEncounter); + return mon->movementDirection; } -bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent) +u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances) { - if (!IsOverworldWildEncounter(curObject, OWE_GENERATED) || IsOverworldWildEncounter(objectEvent, OWE_ANY) || objectEvent->isPlayer) - return FALSE; + struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; + s16 absX, absY; + s16 distanceX = player->currentCoords.x - mon->currentCoords.x; + s16 distanceY = player->currentCoords.y - mon->currentCoords.y; - RemoveObjectEvent(curObject); - return TRUE; -} + // Get absolute X distance. + if (distanceX < 0) + absX = distanceX * -1; + else + absX = distanceX; -u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y) -{ - if (!(collision & (1 << (COLLISION_OBJECT_EVENT - 1)))) - return collision; + // Get absolute Y distance. + if (distanceY < 0) + absY = distanceY * -1; + else + absY = distanceY; - struct ObjectEvent *objectEvent = &gObjectEvents[GetObjectEventIdByXY(x, y)]; - if (!IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) - return collision; + if (absY == absX) + *equalDistances = TRUE; - RemoveObjectEvent(objectEvent); - return collision & (1 << (COLLISION_OBJECT_EVENT - 1)); + if (absY > absX) + return absY; + else + return absX; } -struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim) +u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed) { - struct SpritePalette palette = gSpritePalette_GeneralFieldEffect0; - switch (spawnAnim) + switch (speed) { - case OWE_SPAWN_ANIM_GRASS: - case OWE_SPAWN_ANIM_LONG_GRASS: - palette = gSpritePalette_GeneralFieldEffect1; - break; - - case OWE_SPAWN_ANIM_WATER: - case OWE_SPAWN_ANIM_UNDERWATER: - case OWE_SPAWN_ANIM_CAVE: - case OWE_SPAWN_ANIM_SHINY: - default: - break; + case OWE_SPEED_SLOW: + return GetWalkSlowMovementAction(direction); + case OWE_SPEED_FAST: + return GetWalkFastMovementAction(direction); + case OWE_SPEED_FASTER: + return GetWalkFasterMovementAction(direction); } - return palette; + return GetWalkNormalMovementAction(direction); } -#define sTypeFuncId data[1] // Same as in src/event_object_movement.c -void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite) +static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection) { - if (IsOverworldWildEncounter(objectEvent, OWE_ANY) && OWE_HasSavedMovementState(objectEvent)) - sprite->sTypeFuncId = OWE_RESTORED_MOVEMENT_FUNC_ID; -} -#undef sTypeFuncId + s16 x = mon->currentCoords.x; + s16 y = mon->currentCoords.y; -static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance) -{ - s16 x = player->currentCoords.x; - s16 y = player->currentCoords.y; - u32 i; + MoveCoords(newDirection, &x, &y); + if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, newDirection)) + { + if (mon->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) + return GetOppositeDirection(newDirection); - for (i = 0; i < distance; i++) + MoveCoords(mon->movementDirection, &x, &y); + if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, mon->movementDirection)) + return newDirection; + } + + x = mon->currentCoords.x; + y = mon->currentCoords.y; + MoveCoords(GetOppositeDirection(newDirection), &x, &y); + if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, newDirection)) { - MoveCoords(direction, &x, &y); - if (MapGridGetCollisionAt(x, y) - || GetMapBorderIdAt(x, y) == CONNECTION_INVALID - || IsMetatileDirectionallyImpassable(player, x, y, GetOppositeDirection(direction)) - || IsElevationMismatchAt(player->currentElevation, x, y)) - return FALSE; + if (mon->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) + return newDirection; + + MoveCoords(mon->movementDirection, &x, &y); + if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, mon->movementDirection)) + return GetOppositeDirection(newDirection); } - return TRUE; + return mon->movementDirection; } #define tObjectId data[0] @@ -1812,68 +1735,145 @@ static void Task_OWE_ApproachForBattle(u8 taskId) } #undef tObjectId -u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed) +void OWE_PlayAmbientCry(void) { - switch (speed) - { - case OWE_SPEED_SLOW: - return GetWalkSlowMovementAction(direction); - case OWE_SPEED_FAST: - return GetWalkFastMovementAction(direction); - case OWE_SPEED_FASTER: - return GetWalkFasterMovementAction(direction); - } - - return GetWalkNormalMovementAction(direction); + OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); } -static bool32 OWE_CheckSpecies(u32 speciesId) +u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType) { - return speciesId != SPECIES_NONE - && speciesId < NUM_SPECIES - && IsSpeciesEnabled(speciesId); + u32 numActive = 0; + for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + if (IsOverworldWildEncounter(&gObjectEvents[i], oweType)) + numActive++; + } + return numActive; } -void OWE_PlayAmbientCry(void) +const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) { - OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); -} + if (template->trainerType != TRAINER_TYPE_OW_WILD_ENCOUNTER || (template->localId <= LOCALID_OW_ENCOUNTER_END + && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX))) + return *template; -bool32 OWE_IsDespawnExempt(struct ObjectEvent *objectEvent) -{ - if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) - return FALSE; + struct ObjectEventTemplate templateOWE = *template; + + // Does this work? + u32 graphicsId; + u16 speciesId, speciesTemplate = SanitizeSpeciesId(templateOWE.graphicsId & OBJ_EVENT_MON_SPECIES_MASK); + bool32 isShiny = FALSE, isShinyTemplate = (templateOWE.graphicsId & OBJ_EVENT_MON_SHINY) ? TRUE : FALSE; + bool32 isFemale = FALSE; + u32 level = MIN_LEVEL, levelTemplate = templateOWE.sOverworldEncounterLevel; + u32 indexRoamerOutbreak = OWE_INVALID_ROAMER_OUTBREAK; + u32 x = template->x; + u32 y = template->y; - if (OWE_HasNoDespawnFlag(objectEvent) && AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) - return TRUE; + SetOverworldEncounterSpeciesInfo( + x, + y, + &speciesId, + &isShiny, + &isFemale, + &level, + &indexRoamerOutbreak + ); - objectEvent->offScreen = TRUE; - return FALSE; + if (speciesTemplate) + speciesId = speciesTemplate; + + if (levelTemplate) + level = levelTemplate; + + bool32 validSpecies = OWE_CheckSpecies(speciesId); + bool32 validLevel = OWE_GetEncounterLevel(level) >= MIN_LEVEL && OWE_GetEncounterLevel(level) <= MAX_LEVEL; + assertf(validSpecies && validLevel, "invalid manual overworld encounter\nspecies: %d\nlevel: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, level, x, y) + { + if (!validSpecies) + { + // Currently causes assertf on each player step as function is called. + templateOWE.graphicsId = OBJ_EVENT_GFX_BOY_1; + templateOWE.trainerType = TRAINER_TYPE_NONE; + templateOWE.sOverworldEncounterLevel = 0; + templateOWE.movementType = MOVEMENT_TYPE_NONE; + return templateOWE; + } + else if (!validLevel) + { + level = MIN_LEVEL; + } + } + + if (isShinyTemplate) + isShiny = isShinyTemplate; + + if (templateOWE.graphicsId & OBJ_EVENT_MON && templateOWE.graphicsId & OBJ_EVENT_MON_FEMALE) + isFemale = TRUE; + else if (templateOWE.graphicsId & OBJ_EVENT_MON) + isFemale = FALSE; + else + isFemale = GetGenderFromSpeciesAndPersonality(speciesId, Random32()) == MON_FEMALE; + + if (templateOWE.movementType == MOVEMENT_TYPE_NONE) + templateOWE.movementType = OWE_GetMovementTypeFromSpecies(speciesId); + + graphicsId = speciesId + OBJ_EVENT_MON; + if (isFemale) + graphicsId += OBJ_EVENT_MON_FEMALE; + if (isShiny) + graphicsId += OBJ_EVENT_MON_SHINY; + + templateOWE.graphicsId = graphicsId; + templateOWE.sOverworldEncounterLevel = level; + + return templateOWE; } -void OWE_TryDespawnOverworldWildEncountersCrossingMapConnection(void) +struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim) { - if (gMain.callback2 != CB2_Overworld) - return; + struct SpritePalette palette = gSpritePalette_GeneralFieldEffect0; + switch (spawnAnim) + { + case OWE_SPAWN_ANIM_GRASS: + case OWE_SPAWN_ANIM_LONG_GRASS: + palette = gSpritePalette_GeneralFieldEffect1; + break; - if (!WE_OWE_DESPAWN_ON_ENTER_TOWN) - return; + case OWE_SPAWN_ANIM_WATER: + case OWE_SPAWN_ANIM_UNDERWATER: + case OWE_SPAWN_ANIM_CAVE: + case OWE_SPAWN_ANIM_SHINY: + default: + break; + } - if (gMapHeader.mapType != MAP_TYPE_CITY && gMapHeader.mapType != MAP_TYPE_TOWN) - return; + return palette; +} - if (WE_OWE_DESPAWN_SOUND) - PlaySE(SE_FLEE); - - DespwnAllOverworldWildEncounterObjects(OWE_ANY, 0); +static bool32 OWE_CheckSpecies(u32 speciesId) +{ + return speciesId != SPECIES_NONE + && speciesId < NUM_SPECIES + && IsSpeciesEnabled(speciesId); } -void OverworldWildEncounter_SetInstantSpawnTimer(void) +static UNUSED u32 GetNewestOWEncounterLocalId(void) { - if (!WE_OW_ENCOUNTERS) - return; + struct ObjectEvent *slotMon; + struct ObjectEvent *newest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; + u32 i; + + for (i = 0; i < OWE_SPAWNS_MAX; i++) + { + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; + if (OW_SPECIES(slotMon) != SPECIES_NONE) + { + if (newest->sAge > slotMon->sAge) + newest = slotMon; + } + } - sOWESpawnCountdown = 0; + return GetSpawnSlotByLocalId(newest->localId); } #undef sOverworldEncounterLevel From 6e857e879197dc59e8a8e254c2197541870eddca Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 18:13:10 +0000 Subject: [PATCH 551/572] Remove IsSafeToSpawnObjectEvents --- src/wild_encounter_ow.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 61bf7dc8af42..81ea49b68620 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -136,7 +136,6 @@ static bool32 GenerateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOu static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); static bool32 OWE_ShouldSpawnWaterMons(void); // inline? static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons); -static bool8 IsSafeToSpawnObjectEvents(void); // inline? static u32 GetOldestSlot(bool32 forceRemove); static u8 NextSpawnMonSlot(void); static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); @@ -206,8 +205,8 @@ void OverworldWildEncounters_CB(void) } DespwnAllOverworldWildEncounterObjects(OWE_GENERATED, WILD_CHECK_REPEL); - - if (!IsSafeToSpawnObjectEvents()) + struct ObjectEvent* player = &gObjectEvents[gPlayerAvatar.objectEventId]; + if (player->currentCoords.x != player->previousCoords.x || player->currentCoords.y != player->previousCoords.y) return; u16 spawnSlot = NextSpawnMonSlot(); @@ -674,14 +673,6 @@ static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) return gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; } -static bool8 IsSafeToSpawnObjectEvents(void) -{ - struct ObjectEvent* player = &gObjectEvents[gPlayerAvatar.objectEventId]; - - // Only spawn when player is at a valid tile position - return (player->currentCoords.x == player->previousCoords.x && player->currentCoords.y == player->previousCoords.y); -} - static u32 GetOldestSlot(bool32 forceRemove) { struct ObjectEvent *slotMon, *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; From ec13a423f6792bc365654a1a3d2917507f68296f Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 18:15:03 +0000 Subject: [PATCH 552/572] Inline OWE_ShouldSpawnWaterMons --- src/wild_encounter_ow.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 81ea49b68620..b48abf43b24f 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -126,6 +126,11 @@ static inline void OWE_SetNoDespawnFlag(u32 *level) *level |= OWE_NO_DESPAWN_FLAG; } +static inline bool32 OWE_ShouldSpawnWaterMons(void) +{ + return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); +} + static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); static bool32 OWE_DoesRoamerObjectExist(void); static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); @@ -134,7 +139,6 @@ static bool32 GenerateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak static bool32 GenerateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); static bool32 GenerateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); -static bool32 OWE_ShouldSpawnWaterMons(void); // inline? static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons); static u32 GetOldestSlot(bool32 forceRemove); static u8 NextSpawnMonSlot(void); @@ -635,12 +639,6 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) return TRUE; } -static bool32 OWE_ShouldSpawnWaterMons(void) -{ - // Needs refactoring, and this replacing with a check for coords in many cases. - return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); -} - static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) { u32 headerId = GetCurrentMapWildMonHeaderId(); From 6bcd44d3f929b04ceca27d7a99c34a1321bca516 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 18:18:55 +0000 Subject: [PATCH 553/572] Remove comments --- src/wild_encounter_ow.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index b48abf43b24f..66f2e2d65f9f 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -599,8 +599,6 @@ void OverworldWildEncounter_SetMinimumSpawnTimer(void) void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) { - // The only automatically interacts with an OW Encounter when; - // Not using a repel or the DexNav is inactive. if (WE_OWE_REPEL_DEXNAV_COLLISION && (FlagGet(DN_FLAG_SEARCHING) || REPEL_STEP_COUNT)) return; @@ -1118,7 +1116,7 @@ void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType ow bool32 OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId) { - // does this need to be used in OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette + // does CanRemoveOverworldEncounter need to be used in OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette if (CanRemoveOverworldEncounter(localId)) { *objectEventId = RemoveOldestGeneratedOverworldEncounter(); @@ -1565,13 +1563,11 @@ u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equal s16 distanceX = player->currentCoords.x - mon->currentCoords.x; s16 distanceY = player->currentCoords.y - mon->currentCoords.y; - // Get absolute X distance. if (distanceX < 0) absX = distanceX * -1; else absX = distanceX; - // Get absolute Y distance. if (distanceY < 0) absY = distanceY * -1; else @@ -1748,7 +1744,6 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( struct ObjectEventTemplate templateOWE = *template; - // Does this work? u32 graphicsId; u16 speciesId, speciesTemplate = SanitizeSpeciesId(templateOWE.graphicsId & OBJ_EVENT_MON_SPECIES_MASK); bool32 isShiny = FALSE, isShinyTemplate = (templateOWE.graphicsId & OBJ_EVENT_MON_SHINY) ? TRUE : FALSE; @@ -1758,16 +1753,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( u32 x = template->x; u32 y = template->y; - SetOverworldEncounterSpeciesInfo( - x, - y, - &speciesId, - &isShiny, - &isFemale, - &level, - &indexRoamerOutbreak - ); - + SetOverworldEncounterSpeciesInfo(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); if (speciesTemplate) speciesId = speciesTemplate; From 7220141737b97d3ae5dafff46d3cfcb19cb1b9ca Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 18:22:41 +0000 Subject: [PATCH 554/572] Edit Function Headers --- src/wild_encounter_ow.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 66f2e2d65f9f..2c578284a635 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -144,8 +144,8 @@ static u32 GetOldestSlot(bool32 forceRemove); static u8 NextSpawnMonSlot(void); static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); static bool8 TrySelectTile(s16* outX, s16* outY); -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak); -static u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex); +static void SetOverworldEncounterSpeciesInfo(u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); +static u32 GetOverworldEncounterObjectEventGraphicsId(u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex, s32 x, s32 y); static bool32 CanRemoveOverworldEncounter(u32 localId); static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); @@ -229,7 +229,7 @@ void OverworldWildEncounters_CB(void) u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); u32 level = MIN_LEVEL; - u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); + u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(&speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak, x, y); if (speciesId == SPECIES_NONE || !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(level)) @@ -838,7 +838,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) return FALSE; } -static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) +static void SetOverworldEncounterSpeciesInfo(u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) { u32 personality; @@ -868,9 +868,9 @@ static void SetOverworldEncounterSpeciesInfo(s32 x, s32 y, u16 *speciesId, bool3 ZeroEnemyPartyMons(); } -static u32 GetOverworldEncounterObjectEventGraphicsId(s32 x, s32 y, u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak) +static u32 GetOverworldEncounterObjectEventGraphicsId(u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) { - SetOverworldEncounterSpeciesInfo(x, y, speciesId, isShiny, isFemale, level, indexRoamerOutbreak); + SetOverworldEncounterSpeciesInfo(speciesId, isShiny, isFemale, level, indexRoamerOutbreak, x, y); assertf(OWE_CheckSpecies(*speciesId), "invalid generated overworld encounter\nspecies: %d\ncheck if valid wild mon header exists", speciesId, x, y); u16 graphicsId = *speciesId + OBJ_EVENT_MON; @@ -1753,7 +1753,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( u32 x = template->x; u32 y = template->y; - SetOverworldEncounterSpeciesInfo(x, y, &speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak); + SetOverworldEncounterSpeciesInfo(&speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak, x, y); if (speciesTemplate) speciesId = speciesTemplate; From ff036eca70292f77c153df06ee0f030e81a189b4 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:11:47 +0000 Subject: [PATCH 555/572] Update typedefs --- include/event_object_movement.h | 4 +- include/wild_encounter.h | 12 +++--- include/wild_encounter_ow.h | 2 +- src/battle_pyramid.c | 2 +- src/event_object_movement.c | 12 +++--- src/wild_encounter.c | 12 +++--- src/wild_encounter_ow.c | 76 ++++++++++++++++----------------- 7 files changed, 59 insertions(+), 61 deletions(-) diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 7dec653c4a27..1e45743d52a7 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -194,7 +194,7 @@ u8 GetWalkInPlaceFastMovementAction(u32); u8 GetWalkInPlaceNormalMovementAction(u32); u8 GetWalkInPlaceSlowMovementAction(u32); enum Collision GetCollisionAtCoords(struct ObjectEvent *objectEvent, s16 x, s16 y, enum Direction dir); -bool32 IsMetatileDirectionallyImpassable(struct ObjectEvent *objectEvent, s16 x, s16 y, enum Direction direction); +bool8 IsMetatileDirectionallyImpassable(struct ObjectEvent *objectEvent, s16 x, s16 y, enum Direction direction); u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, bool32 addCoords); void MoveCoords(enum Direction direction, s16 *x, s16 *y); bool8 ObjectEventIsHeldMovementActive(struct ObjectEvent *objectEvent); @@ -276,7 +276,7 @@ const struct ObjectEventGraphicsInfo *SpeciesToGraphicsInfo(u32 species, bool32 u32 LoadDynamicFollowerPalette(u32 species, bool32 shiny, bool32 female); u16 GetObjectEventFlagIdByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup); void CopyObjectGraphicsInfoToSpriteTemplate(u16 graphicsId, void (*callback)(struct Sprite *), struct SpriteTemplate *spriteTemplate, const struct SubspriteTable **subspriteTables); -bool32 AreElevationsCompatible(u32, u32); +bool8 AreElevationsCompatible(u8, u8); enum Direction DetermineObjectEventDirectionFromObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo); void ObjectEventsTurnToEachOther(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo); void UpdateObjectEventCoords(struct ObjectEvent *objectEvent, s16 dx, s16 dy); diff --git a/include/wild_encounter.h b/include/wild_encounter.h index ec55505c3d34..6b376c970b2c 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -62,16 +62,16 @@ void FishingWildEncounter(u8 rod); u16 GetLocalWildMon(bool8 *isWaterMon); u16 GetLocalWaterMon(void); bool8 UpdateRepelCounter(void); -bool32 IsWildLevelAllowedByRepel(u8 wildLevel); -bool32 IsAbilityAllowingEncounter(u8 level); +bool8 IsWildLevelAllowedByRepel(u8 wildLevel); +bool8 IsAbilityAllowingEncounter(u8 level); bool8 TryDoDoubleWildBattle(void); bool8 StandardWildEncounter_Debug(void); u32 CalculateChainFishingShinyRolls(void); void CreateWildMon(u16 species, u8 level); -bool32 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags); -bool32 SetUpMassOutbreakEncounter(u8 flags); -bool32 DoMassOutbreakEncounterTest(void); -bool32 AreLegendariesInSootopolisPreventingEncounters(void); +bool8 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags); +bool8 SetUpMassOutbreakEncounter(u8 flags); +bool8 DoMassOutbreakEncounterTest(void); +bool8 AreLegendariesInSootopolisPreventingEncounters(void); u16 GetCurrentMapWildMonHeaderId(void); bool8 CheckFeebasAtCoords(s16 x, s16 y); u32 ChooseWildMonIndex_Land(void); diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index bfa0702689fd..28f8974dcabe 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -67,7 +67,7 @@ void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent void OverworldWildEncounter_OnObjectEventDespawned(struct ObjectEvent *objectEvent); bool32 OWE_IsDespawnExempt(struct ObjectEvent *objectEvent); bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); -u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y); +u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s32 x, s32 y); void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags); bool32 OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId); void OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette(void); diff --git a/src/battle_pyramid.c b/src/battle_pyramid.c index c95919854f55..b85742236607 100644 --- a/src/battle_pyramid.c +++ b/src/battle_pyramid.c @@ -1399,7 +1399,7 @@ void GenerateBattlePyramidWildMon(u32 forceSpecies) u32 lvl = gSaveBlock2Ptr->frontier.lvlMode; u16 round = (gSaveBlock2Ptr->frontier.pyramidWinStreaks[lvl] / 7) % TOTAL_PYRAMID_ROUNDS; const struct BattlePyramidRequirement *reqs = &sBattlePyramidRequirementsByRound[round]; - u16 species = forceSpecies; + u32 species = forceSpecies; u32 bstLim; u16 *moves = NULL; u16 *abilities = NULL; diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 028dc135f427..c2b75692a047 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -6600,7 +6600,7 @@ static bool8 IsCoordOutsideObjectEventMovementRange(struct ObjectEvent *objectEv return FALSE; } -bool32 IsMetatileDirectionallyImpassable(struct ObjectEvent *objectEvent, s16 x, s16 y, enum Direction direction) +bool8 IsMetatileDirectionallyImpassable(struct ObjectEvent *objectEvent, s16 x, s16 y, enum Direction direction) { if (gOppositeDirectionBlockedMetatileFuncs[direction - 1](objectEvent->currentMetatileBehavior) || gDirectionBlockedMetatileFuncs[direction - 1](MapGridGetMetatileBehaviorAt(x, y))) @@ -10101,7 +10101,7 @@ static void ObjectEventUpdateSubpriority(struct ObjectEvent *objEvent, struct Sp SetObjectSubpriorityByElevation(objEvent->previousElevation, sprite, 1); } -bool32 AreElevationsCompatible(u32 a, u32 b) +bool8 AreElevationsCompatible(u8 a, u8 b) { if (a == 0 || b == 0) return TRUE; @@ -10122,14 +10122,14 @@ void ScriptFaceEachOther(struct ScriptContext *ctx) enum Direction DetermineObjectEventDirectionFromObject(struct ObjectEvent *objectOne, struct ObjectEvent *objectTwo) { - s16 dx = objectOne->currentCoords.x - objectTwo->currentCoords.x; - s16 dy = objectOne->currentCoords.y - objectTwo->currentCoords.y; + s32 dx = objectOne->currentCoords.x - objectTwo->currentCoords.x; + s32 dy = objectOne->currentCoords.y - objectTwo->currentCoords.y; if (dx == 0 && dy == 0) return DIR_NONE; - s16 absX = abs(dx); - s16 absY = abs(dy); + s32 absX = abs(dx); + s32 absY = abs(dy); if (absX > absY && dx < 0) return DIR_WEST; diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 192bc8770642..0e451317b7ab 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -476,7 +476,7 @@ void CreateWildMon(u16 species, u8 level) #define TRY_GET_ABILITY_INFLUENCED_WILD_MON_INDEX(wildPokemon, type, ability, ptr, count) TryGetAbilityInfluencedWildMonIndex(wildPokemon, type, ability, ptr) #endif -bool32 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags) +bool8 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags) { u8 wildMonIndex = 0; u8 level; @@ -545,7 +545,7 @@ static u16 GenerateFishingWildMon(const struct WildPokemonInfo *wildMonInfo, u8 return wildMonSpecies; } -bool32 SetUpMassOutbreakEncounter(u8 flags) +bool8 SetUpMassOutbreakEncounter(u8 flags) { u16 i; @@ -559,7 +559,7 @@ bool32 SetUpMassOutbreakEncounter(u8 flags) return TRUE; } -bool32 DoMassOutbreakEncounterTest(void) +bool8 DoMassOutbreakEncounterTest(void) { if (gSaveBlock1Ptr->outbreakPokemonSpecies != SPECIES_NONE && gSaveBlock1Ptr->location.mapNum == gSaveBlock1Ptr->outbreakLocationMapNum @@ -629,7 +629,7 @@ static bool8 AllowWildCheckOnNewMetatile(void) return TRUE; } -bool32 AreLegendariesInSootopolisPreventingEncounters(void) +bool8 AreLegendariesInSootopolisPreventingEncounters(void) { if (gSaveBlock1Ptr->location.mapGroup != MAP_GROUP(MAP_SOOTOPOLIS_CITY) || gSaveBlock1Ptr->location.mapNum != MAP_NUM(MAP_SOOTOPOLIS_CITY)) @@ -1045,7 +1045,7 @@ bool8 UpdateRepelCounter(void) return FALSE; } -bool32 IsWildLevelAllowedByRepel(u8 wildLevel) +bool8 IsWildLevelAllowedByRepel(u8 wildLevel) { u8 i; @@ -1064,7 +1064,7 @@ bool32 IsWildLevelAllowedByRepel(u8 wildLevel) return FALSE; } -bool32 IsAbilityAllowingEncounter(u8 level) +bool8 IsAbilityAllowingEncounter(u8 level) { enum Ability ability; diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 2c578284a635..eafad12fdc2a 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -111,7 +111,7 @@ static inline u32 OWE_GetEncounterLevel(u32 level) return level & ~OWE_NO_DESPAWN_FLAG; } -static inline void OWE_SetEncounterLevel(u32 *level, u8 newLevel) +static inline void OWE_SetEncounterLevel(u32 *level, u32 newLevel) { *level = (*level & OWE_NO_DESPAWN_FLAG) | (newLevel & ~OWE_NO_DESPAWN_FLAG); } @@ -131,7 +131,7 @@ static inline bool32 OWE_ShouldSpawnWaterMons(void) return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); } -static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); +static bool32 OWE_CreateEnemyPartyMon(u32 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); static bool32 OWE_DoesRoamerObjectExist(void); static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); @@ -141,15 +141,15 @@ static bool32 GenerateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOu static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons); static u32 GetOldestSlot(bool32 forceRemove); -static u8 NextSpawnMonSlot(void); -static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); -static bool8 TrySelectTile(s16* outX, s16* outY); -static void SetOverworldEncounterSpeciesInfo(u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); -static u32 GetOverworldEncounterObjectEventGraphicsId(u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex, s32 x, s32 y); +static u32 NextSpawnMonSlot(void); +static u32 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); +static bool32 TrySelectTile(s32* outX, s32* outY); +static void SetOverworldEncounterSpeciesInfo(u32 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); +static u32 GetOverworldEncounterObjectEventGraphicsId(u32 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex, s32 x, s32 y); static bool32 CanRemoveOverworldEncounter(u32 localId); -static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); -static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); -static bool32 OWE_CanEncounterBeLoaded_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y); +static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y); +static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y); +static bool32 OWE_CanEncounterBeLoaded_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y); static void SortOWEMonAges(void); static u32 RemoveOldestGeneratedOverworldEncounter(void); static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); @@ -159,7 +159,7 @@ static enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatile static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); -static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 xNew, s16 yNew, enum Direction newDirection, enum Direction collisionDirection); +static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s32 xNew, s32 yNew, enum Direction newDirection, enum Direction collisionDirection); static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance); @@ -213,8 +213,8 @@ void OverworldWildEncounters_CB(void) if (player->currentCoords.x != player->previousCoords.x || player->currentCoords.y != player->previousCoords.y) return; - u16 spawnSlot = NextSpawnMonSlot(); - s16 x, y; + u32 spawnSlot = NextSpawnMonSlot(); + s32 x, y; if (spawnSlot == OWE_INVALID_SPAWN_SLOT || (shouldSpawnWaterMons && AreLegendariesInSootopolisPreventingEncounters()) || !TrySelectTile(&x, &y)) @@ -223,7 +223,7 @@ void OverworldWildEncounters_CB(void) return; } - u16 speciesId = SPECIES_NONE; + u32 speciesId = SPECIES_NONE; bool32 isShiny = FALSE; bool32 isFemale = FALSE; u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; @@ -323,7 +323,7 @@ void GenerateOverworldWildEncounter(void) if (indexRoamerOutbreak && GenerateOverworldWildEncounter_CheckRoamer(OWE_GetObjectRoamerOutbreakStatus(object))) return; - u16 speciesId = OW_SPECIES(object); + u32 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; u32 gender = OW_FEMALE(object) ? MON_FEMALE : MON_MALE; u32 level = OWE_GetEncounterLevel(object->sOverworldEncounterLevel); @@ -358,7 +358,7 @@ void GenerateOverworldWildEncounter(void) BattleSetup_StartWildBattle(); } -static bool32 OWE_CreateEnemyPartyMon(u16 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) +static bool32 OWE_CreateEnemyPartyMon(u32 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) { const struct WildPokemonInfo *wildMonInfo; enum WildPokemonArea wildArea; @@ -700,7 +700,7 @@ static u32 GetOldestSlot(bool32 forceRemove) return GetSpawnSlotByLocalId(oldest->localId); } -static u8 NextSpawnMonSlot(void) +static u32 NextSpawnMonSlot(void) { u32 spawnSlot; @@ -731,7 +731,7 @@ static u8 NextSpawnMonSlot(void) return spawnSlot; } -static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) +static u32 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) { u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; @@ -742,13 +742,13 @@ static u16 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) return OW_SPECIES(objectEvent); } -static bool8 TrySelectTile(s16* outX, s16* outY) +static bool32 TrySelectTile(s32* outX, s32* outY) { - u8 elevation; - u16 tileBehavior; + u32 elevation; + u32 tileBehavior; s16 playerX, playerY; s16 x, y; - u8 closeDistance; + u32 closeDistance; bool32 isEncounterTile = FALSE; // Spawn further away when surfing @@ -838,7 +838,7 @@ static bool8 TrySelectTile(s16* outX, s16* outY) return FALSE; } -static void SetOverworldEncounterSpeciesInfo(u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) +static void SetOverworldEncounterSpeciesInfo(u32 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) { u32 personality; @@ -868,11 +868,11 @@ static void SetOverworldEncounterSpeciesInfo(u16 *speciesId, bool32 *isShiny, bo ZeroEnemyPartyMons(); } -static u32 GetOverworldEncounterObjectEventGraphicsId(u16 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) +static u32 GetOverworldEncounterObjectEventGraphicsId(u32 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) { SetOverworldEncounterSpeciesInfo(speciesId, isShiny, isFemale, level, indexRoamerOutbreak, x, y); assertf(OWE_CheckSpecies(*speciesId), "invalid generated overworld encounter\nspecies: %d\ncheck if valid wild mon header exists", speciesId, x, y); - u16 graphicsId = *speciesId + OBJ_EVENT_MON; + u32 graphicsId = *speciesId + OBJ_EVENT_MON; if (*isFemale) graphicsId += OBJ_EVENT_MON_FEMALE; @@ -891,7 +891,7 @@ static bool32 CanRemoveOverworldEncounter(u32 localId) || localId > LOCALID_OW_ENCOUNTER_END)); } -static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) +static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y) { assertf(OWE_CanEncounterBeLoaded_Palette(speciesId, isFemale, isShiny, x, y), "could not load palette for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) { @@ -906,7 +906,7 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is return TRUE; } -static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) +static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y) { u32 numFreePalSlots = CountFreePaletteSlots(); u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); @@ -935,7 +935,7 @@ static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, b return TRUE; } #define OWE_FIELD_EFFECT_TILE_NUM 16 // Number of tiiles to add for field effect spawning -static bool32 OWE_CanEncounterBeLoaded_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s16 x, s16 y) +static bool32 OWE_CanEncounterBeLoaded_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y) { u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); // const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(speciesId, isShiny, isFemale); @@ -1063,7 +1063,7 @@ bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct Obj return TRUE; } -u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y) +u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s32 x, s32 y) { if (!(collision & (1 << (COLLISION_OBJECT_EVENT - 1)))) return collision; @@ -1078,8 +1078,7 @@ u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s16 x, s16 y) void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags) { - s16 dx = 0; - s16 dy = 0; + s32 dx = 0, dy = 0; if (gCamera.active) { @@ -1120,7 +1119,6 @@ bool32 OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(u32 localId, u8 if (CanRemoveOverworldEncounter(localId)) { *objectEventId = RemoveOldestGeneratedOverworldEncounter(); - if (*objectEventId == OBJECT_EVENTS_COUNT) return TRUE; else @@ -1366,7 +1364,7 @@ bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Directi return FALSE; } -static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s16 xNew, s16 yNew, enum Direction newDirection, enum Direction collisionDirection) +static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s32 xNew, s32 yNew, enum Direction newDirection, enum Direction collisionDirection) { if (OWE_CheckRestrictMovementMetatile(mon->currentCoords.x, mon->currentCoords.y, xNew, yNew)) return FALSE; @@ -1559,9 +1557,9 @@ u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - s16 absX, absY; - s16 distanceX = player->currentCoords.x - mon->currentCoords.x; - s16 distanceY = player->currentCoords.y - mon->currentCoords.y; + s32 absX, absY; + s32 distanceX = player->currentCoords.x - mon->currentCoords.x; + s32 distanceY = player->currentCoords.y - mon->currentCoords.y; if (distanceX < 0) absX = distanceX * -1; @@ -1670,9 +1668,9 @@ static void Task_OWE_ApproachForBattle(u8 taskId) return; } - u16 speciesId = OW_SPECIES(OWE); + u32 speciesId = OW_SPECIES(OWE); enum Direction direction = DetermineObjectEventDirectionFromObject(player, OWE); - u8 movementActionId; + u32 movementActionId; SetObjectEventDirection(OWE, direction); movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(OWE->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); @@ -1745,7 +1743,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( struct ObjectEventTemplate templateOWE = *template; u32 graphicsId; - u16 speciesId, speciesTemplate = SanitizeSpeciesId(templateOWE.graphicsId & OBJ_EVENT_MON_SPECIES_MASK); + u32 speciesId, speciesTemplate = SanitizeSpeciesId(templateOWE.graphicsId & OBJ_EVENT_MON_SPECIES_MASK); bool32 isShiny = FALSE, isShinyTemplate = (templateOWE.graphicsId & OBJ_EVENT_MON_SHINY) ? TRUE : FALSE; bool32 isFemale = FALSE; u32 level = MIN_LEVEL, levelTemplate = templateOWE.sOverworldEncounterLevel; From f1952e778b9009796ef232d13a87b2dc7d8a153c Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:34:21 +0000 Subject: [PATCH 556/572] Tidy multilines statements --- src/wild_encounter_ow.c | 101 +++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index eafad12fdc2a..96ff7726ec7d 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -184,11 +184,11 @@ void OverworldWildEncounters_CB(void) return; if (!WE_OW_ENCOUNTERS - || FlagGet(OW_FLAG_NO_ENCOUNTER) - || FlagGet(DN_FLAG_SEARCHING) - || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !WE_OWE_BATTLE_PIKE) - || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR && !WE_OWE_BATTLE_PYRAMID) - || InTrainerHillChallenge()) + || FlagGet(OW_FLAG_NO_ENCOUNTER) + || FlagGet(DN_FLAG_SEARCHING) + || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !WE_OWE_BATTLE_PIKE) + || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR && !WE_OWE_BATTLE_PYRAMID) + || InTrainerHillChallenge()) { if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) { @@ -216,8 +216,8 @@ void OverworldWildEncounters_CB(void) u32 spawnSlot = NextSpawnMonSlot(); s32 x, y; if (spawnSlot == OWE_INVALID_SPAWN_SLOT - || (shouldSpawnWaterMons && AreLegendariesInSootopolisPreventingEncounters()) - || !TrySelectTile(&x, &y)) + || (shouldSpawnWaterMons && AreLegendariesInSootopolisPreventingEncounters()) + || !TrySelectTile(&x, &y)) { OverworldWildEncounter_SetMinimumSpawnTimer(); return; @@ -232,9 +232,9 @@ void OverworldWildEncounters_CB(void) u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(&speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak, x, y); if (speciesId == SPECIES_NONE - || !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(level)) - || !IsAbilityAllowingEncounter(OWE_GetEncounterLevel(level)) - || !OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny, x, y)) + || !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(level)) + || !IsAbilityAllowingEncounter(OWE_GetEncounterLevel(level)) + || !OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny, x, y)) { OverworldWildEncounter_SetMinimumSpawnTimer(); return; @@ -275,20 +275,23 @@ void OverworldWildEncounters_CB(void) bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType) { - bool32 isOWE = (objectEvent->graphicsId & OBJ_EVENT_MON) && (objectEvent->trainerType == TRAINER_TYPE_OW_WILD_ENCOUNTER); + if (!IS_OW_MON_OBJ(objectEvent)) + return FALSE; + + if (objectEvent->trainerType != TRAINER_TYPE_OW_WILD_ENCOUNTER) + return FALSE; + switch (oweType) { default: case OWE_ANY: - return isOWE; + return TRUE; case OWE_GENERATED: - return isOWE && (objectEvent->localId <= LOCALID_OW_ENCOUNTER_END - && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); + return (objectEvent->localId <= LOCALID_OW_ENCOUNTER_END && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); case OWE_MANUAL: - return isOWE && (objectEvent->localId > LOCALID_OW_ENCOUNTER_END - || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); + return (objectEvent->localId > LOCALID_OW_ENCOUNTER_END || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); } } @@ -490,7 +493,7 @@ static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) static bool32 GenerateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak) { if (indexRoamerOutbreak < ROAMER_COUNT - && IsRoamerAt(indexRoamerOutbreak, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) + && IsRoamerAt(indexRoamerOutbreak, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) { CreateRoamerMonInstance(indexRoamerOutbreak); gEncounteredRoamerIndex = indexRoamerOutbreak; @@ -530,9 +533,9 @@ static bool32 GenerateOverworldWildEncounter_CheckBattleFrontier(u32 headerId) static bool32 GenerateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId) { if (indexRoamerOutbreak == OWE_MASS_OUTBREAK_INDEX - && gSaveBlock1Ptr->outbreakPokemonSpecies == speciesId - && gSaveBlock1Ptr->location.mapNum == gSaveBlock1Ptr->outbreakLocationMapNum - && gSaveBlock1Ptr->location.mapGroup == gSaveBlock1Ptr->outbreakLocationMapGroup) + && gSaveBlock1Ptr->outbreakPokemonSpecies == speciesId + && gSaveBlock1Ptr->location.mapNum == gSaveBlock1Ptr->outbreakLocationMapNum + && gSaveBlock1Ptr->location.mapGroup == gSaveBlock1Ptr->outbreakLocationMapGroup) { for (u32 i = 0; i < MAX_MON_MOVES; i++) SetMonMoveSlot(&gEnemyParty[0], gSaveBlock1Ptr->outbreakPokemonMoves[i], i); @@ -611,9 +614,8 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; u32 indexRoamerOutbreak = OWE_GetRoamerIndex(wildMon); if (indexRoamerOutbreak - && indexRoamerOutbreak < OWE_MASS_OUTBREAK_INDEX - && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), - gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) + && indexRoamerOutbreak < OWE_MASS_OUTBREAK_INDEX + && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) { RemoveObjectEvent(wildMon); return; @@ -629,8 +631,8 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) return FALSE; if (IsOverworldWildEncounter(object, OWE_MANUAL) - && GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithOverworldWildEncounter - && GetObjectEventScriptPointerByObjectEventId(objectEventId) != NULL) + && GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithOverworldWildEncounter + && GetObjectEventScriptPointerByObjectEventId(objectEventId) != NULL) return FALSE; gSpecialVar_0x8004 = OW_SPECIES(object); @@ -823,7 +825,7 @@ static bool32 TrySelectTile(s32* outX, s32* outY) isEncounterTile = TRUE; if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS - || gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) + || gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) isEncounterTile = TRUE; if (isEncounterTile && !MapGridGetCollisionAt(x, y)) @@ -885,10 +887,16 @@ static u32 GetOverworldEncounterObjectEventGraphicsId(u32 *speciesId, bool32 *is static bool32 CanRemoveOverworldEncounter(u32 localId) { - // Include a check for the encounter not being shiny or a roamer. - return (WE_OW_ENCOUNTERS && GetNumberActiveOverworldEncounters(OWE_GENERATED) != 0 - && (localId <= (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX + 1) - || localId > LOCALID_OW_ENCOUNTER_END)); + if (!WE_OW_ENCOUNTERS) + return FALSE; + + if (!GetNumberActiveOverworldEncounters(OWE_GENERATED)) + return FALSE; + + if (!(localId <= (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX + 1) || localId > LOCALID_OW_ENCOUNTER_END)) + return FALSE; + + return TRUE; } static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y) @@ -1386,20 +1394,20 @@ static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 u32 metatileBehaviourNew = MapGridGetMetatileBehaviorAt(xNew, yNew); if (MetatileBehavior_IsLandWildEncounter(metatileBehaviourCurrent) - && MetatileBehavior_IsLandWildEncounter(metatileBehaviourNew)) + && MetatileBehavior_IsLandWildEncounter(metatileBehaviourNew)) return FALSE; if (MetatileBehavior_IsWaterWildEncounter(metatileBehaviourCurrent) - && MetatileBehavior_IsWaterWildEncounter(metatileBehaviourNew)) + && MetatileBehavior_IsWaterWildEncounter(metatileBehaviourNew)) return FALSE; if (MetatileBehavior_IsIndoorEncounter(metatileBehaviourCurrent) - && MetatileBehavior_IsIndoorEncounter(metatileBehaviourNew)) + && MetatileBehavior_IsIndoorEncounter(metatileBehaviourNew)) return FALSE; if (!MetatileBehavior_IsLandWildEncounter(metatileBehaviourCurrent) - && !MetatileBehavior_IsWaterWildEncounter(metatileBehaviourCurrent) - && !MetatileBehavior_IsIndoorEncounter(metatileBehaviourCurrent)) + && !MetatileBehavior_IsWaterWildEncounter(metatileBehaviourCurrent) + && !MetatileBehavior_IsIndoorEncounter(metatileBehaviourCurrent)) return FALSE; return TRUE; @@ -1411,7 +1419,7 @@ static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 return FALSE; if (objectEvent->mapGroup == gSaveBlock1Ptr->location.mapGroup - && objectEvent->mapNum == gSaveBlock1Ptr->location.mapNum) + && objectEvent->mapNum == gSaveBlock1Ptr->location.mapNum) return !AreCoordsInsidePlayerMap(xNew, yNew); else return AreCoordsInsidePlayerMap(xNew, yNew); @@ -1489,9 +1497,9 @@ static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction { MoveCoords(direction, &x, &y); if (MapGridGetCollisionAt(x, y) - || GetMapBorderIdAt(x, y) == CONNECTION_INVALID - || IsMetatileDirectionallyImpassable(player, x, y, GetOppositeDirection(direction)) - || IsElevationMismatchAt(player->currentElevation, x, y)) + || GetMapBorderIdAt(x, y) == CONNECTION_INVALID + || IsMetatileDirectionallyImpassable(player, x, y, GetOppositeDirection(direction)) + || IsElevationMismatchAt(player->currentElevation, x, y)) return FALSE; } @@ -1737,7 +1745,7 @@ u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) { if (template->trainerType != TRAINER_TYPE_OW_WILD_ENCOUNTER || (template->localId <= LOCALID_OW_ENCOUNTER_END - && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX))) + && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX))) return *template; struct ObjectEventTemplate templateOWE = *template; @@ -1825,9 +1833,16 @@ struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnA static bool32 OWE_CheckSpecies(u32 speciesId) { - return speciesId != SPECIES_NONE - && speciesId < NUM_SPECIES - && IsSpeciesEnabled(speciesId); + if (speciesId == SPECIES_NONE) + return FALSE; + + if (speciesId >= NUM_SPECIES) + return FALSE; + + if (!IsSpeciesEnabled(speciesId)) + return FALSE; + + return TRUE; } static UNUSED u32 GetNewestOWEncounterLocalId(void) From 2125d7fd9240554d7e8bd53cb2f73cba5512f95a Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:38:38 +0000 Subject: [PATCH 557/572] Adjust ShouldPlayAmbientCryVanillaOWE --- src/overworld.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/overworld.c b/src/overworld.c index f63abc3f5748..eacc1646be28 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1395,7 +1395,7 @@ void Overworld_FadeOutMapMusic(void) FadeOutMapMusic(4); } -static bool32 ShouldPlayVanillaAmbientCry(void) +static bool32 ShouldPlayAmbientCryVanillaOWE(void) { bool32 owePlayed = FALSE; @@ -1408,6 +1408,9 @@ static bool32 ShouldPlayVanillaAmbientCry(void) OWE_PlayAmbientCry(); owePlayed = TRUE; break; + + default: + break; } } @@ -1426,7 +1429,7 @@ static bool32 ShouldPlayVanillaAmbientCry(void) static void PlayAmbientCry(void) { - if (!ShouldPlayVanillaAmbientCry()) + if (!ShouldPlayAmbientCryVanillaOWE()) return; s16 x, y; From 963337fb426cefb092846f7b89f4b11a390e77fa Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:57:15 +0000 Subject: [PATCH 558/572] Fix GetObjectEventIdByPosition check --- src/wild_encounter_ow.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 96ff7726ec7d..504cab4a9c90 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -833,7 +833,7 @@ static bool32 TrySelectTile(s32* outX, s32* outY) *outX = x; *outY = y; - if (!GetObjectEventIdByPosition(x, y, 0)) + if (GetObjectEventIdByPosition(x, y, 0) == OBJECT_EVENTS_COUNT) return TRUE; } From edd5522cbc40e6fb746bb2616bc8931b6d729283 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:01:22 -0600 Subject: [PATCH 559/572] Added OWE localIds check to SPECIAL_LOCALIDS_START --- include/constants/event_objects.h | 2 +- src/event_object_movement.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index 7ad94b217d98..71b0fc3764a0 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -501,7 +501,7 @@ #define LOCALID_NONE 0 #define LOCALID_CAMERA 127 #define LOCALID_BERRY_BLENDER_PLAYER_END 240 // This will use 5 (MAX_RFU_PLAYERS) IDs ending at 240, i.e. 236-240 -#define LOCALID_OW_ENCOUNTER_END 252 // This will use 5 (OWE_MAX_SPAWNS) IDs ending at 252, i.e. 249-252 +#define LOCALID_OW_ENCOUNTER_END 252 // This will use 4 (OWE_SPAWNS_MAX) IDs ending at 252, i.e. 249-252 #define LOCALID_FOLLOWING_POKEMON 254 #define LOCALID_PLAYER 255 #define OBJ_EVENT_ID_FOLLOWER 0xFE diff --git a/src/event_object_movement.c b/src/event_object_movement.c index b930f542eed5..12aa748ce693 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -60,8 +60,8 @@ #define SPECIAL_LOCALIDS_START (min(LOCALID_CAMERA, \ min(LOCALID_PLAYER, \ - LOCALID_BERRY_BLENDER_PLAYER_END - MAX_RFU_PLAYERS + 1))) - // Need an addition here? + min(LOCALID_BERRY_BLENDER_PLAYER_END - MAX_RFU_PLAYERS + 1, \ + LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX + 1)))) // The object event templates on a map cannot use the special IDs listed above or they can behave unexpectedly. // For more details on these special IDs see their definitions in 'include/constants/event_objects.h'. From a04a5ab205f50d3769b69665700fe948c583ff13 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 20:05:13 +0000 Subject: [PATCH 560/572] Remove Testing Code --- src/pokemon.c | 1 - src/wild_encounter_ow.c | 6 ------ 2 files changed, 7 deletions(-) diff --git a/src/pokemon.c b/src/pokemon.c index c0b2da3757d8..a3b82bee93aa 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -7267,7 +7267,6 @@ void ChangePokemonNicknameWithCallback(void (*callback)(void)) u32 OWE_GetMovementTypeFromSpecies(u32 speciesId) { - // return MOVEMENT_TYPE_WANDER_AROUND_OWE; // Replace for Testing speciesId = SanitizeSpeciesId(speciesId); enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; return sOWESpeciesBehavior[behavior].movementType; diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 504cab4a9c90..6b2fdb2d5f02 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -1263,12 +1263,6 @@ static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; - - // TESTING: Setting this species can be used as a test to play a consistent sound to check how often the - // code in OverworldWildEncounters_CB runs, as OWE_GetRandomActiveEncounterObject cuurently returns - // NULL. - if (objectEvent == NULL) - return; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u32 speciesId = OW_SPECIES(objectEvent); From 5eafeb72446080ec8eb3641d9a26d98feb241c82 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 20:06:10 +0000 Subject: [PATCH 561/572] Remove GetNewestOWEncounterLocalId --- src/wild_encounter_ow.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 6b2fdb2d5f02..198e8a4091ac 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -1839,25 +1839,6 @@ static bool32 OWE_CheckSpecies(u32 speciesId) return TRUE; } -static UNUSED u32 GetNewestOWEncounterLocalId(void) -{ - struct ObjectEvent *slotMon; - struct ObjectEvent *newest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; - u32 i; - - for (i = 0; i < OWE_SPAWNS_MAX; i++) - { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE) - { - if (newest->sAge > slotMon->sAge) - newest = slotMon; - } - } - - return GetSpawnSlotByLocalId(newest->localId); -} - #undef sOverworldEncounterLevel #undef sAge #undef sRoamerOutbreakStatus From dab6017593000196ddf5ca5468eb29901d3c97e1 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 20:20:57 +0000 Subject: [PATCH 562/572] Update WE config --- include/config/wild_encounter.h | 38 ++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/include/config/wild_encounter.h b/include/config/wild_encounter.h index b9140ee943d9..9bc6e7d52223 100644 --- a/include/config/wild_encounter.h +++ b/include/config/wild_encounter.h @@ -4,25 +4,25 @@ // Vanilla #define WE_VANILLA_RANDOM TRUE // If TRUE, Pokémon can randomly spawn on tiles that can trigger wild encounters, as in vanilla. -// Overworld Wild Encounters -#define WE_OW_ENCOUNTERS FALSE // If TRUE, OW Pokémon can spawn as overworld wild encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. - // If WE_OW_ENCOUNTERS is TRUE, it is recommended that OW_GFX_COMPRESS be set to FALSE to prevent VRAM issues.s -#define WE_OWE_BATTLE_PIKE TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pike, if FALSE random encounters will be enabled instead. Requires WE_OW_ENCOUNTERS to be TRUE. -#define WE_OWE_BATTLE_PYRAMID TRUE // If TRUE, overworld wild encounters can spawn in the Battle Pyramid, if FALSE random encounters will be enabled instead. Requires WE_OW_ENCOUNTERS to be TRUE. -#define WE_OWE_RESTRICT_METATILE TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within tiles with the same encounter metatile behavior as the one it is currently on, if any. -#define WE_OWE_RESTRICT_MAP TRUE // If TRUE, OW Pokémon spawned as overworld wild encounters will stay within their current map bounds. -#define WE_OWE_UNRESTRICT_SIGHT FALSE // If TRUE, OW Pokémon spawned as overworld wild encounters will ignore all movement restrictions when they can see the player. -#define WE_OWE_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OW wild encounter objects will despawn after a short time and be replaced with a new spawn if possible. -#define WE_OWE_FLEE_DESPAWN TRUE // If TRUE, a fleeing OW Pokémon will despawn if it is unable to take a step for a short time. -#define WE_OWE_SHINY_SPARKLE FALSE // If TRUE, a shiny overworld wild encounter will spawn with a sparkle animation and play the shiny sound effect. -#define WE_OWE_FEEBAS_SPOTS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas overworld wild encounter. -#define WE_OWE_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an overworld wild encounter despawns. -#define WE_OWE_APPROACH_FOR_BATTLE TRUE // If TRUE, the mon will take steps to be right next to the player before the battle starts. -#define WE_OWE_PREVENT_SHINY_DESPAWN FALSE // If TRUE, shiny OW Pokémon objects will not be despawned when off-screen if on the same map as the player, or be replaced if WE_OWE_SPAWN_REPLACEMENT is TRUE. -#define WE_OWE_PREVENT_FEEBAS_DESPAWN FALSE // If TRUE, Feebas OW objects spawned from special Feebas fishing spots (when WE_OWE_FEEBAS_SPOTS is TRUE) will not be despawned when off-screen if on the same map as the player, or be replaced if WE_OWE_SPAWN_REPLACEMENT is TRUE. -#define WE_OWE_DESPAWN_ON_ENTER_TOWN TRUE // If TRUE, despawns all OW wild encounter objects upon entering a city or town (MAP_TYPE_TOWN or MAP_TYPE_CITY). -#define WE_OWE_REPEL_DEXNAV_COLLISION FALSE // If TRUE, overworld wild encounters can still be triggered by a collision if a Repel or the DexNav is active. +// Overworld Wild Encounters (OWEs) +#define WE_OW_ENCOUNTERS FALSE // If TRUE, OW Pokémon can spawn as Overworld Wild Encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. + // If WE_OW_ENCOUNTERS is TRUE, it is recommended that OW_GFX_COMPRESS be set to FALSE to prevent VRAM issues. +#define WE_OWE_BATTLE_PIKE TRUE // If TRUE, OWEs can spawn in the Battle Pike, if FALSE random encounters will be enabled instead. Requires WE_OW_ENCOUNTERS to be TRUE. +#define WE_OWE_BATTLE_PYRAMID TRUE // If TRUE, OWEs can spawn in the Battle Pyramid, if FALSE random encounters will be enabled instead. Requires WE_OW_ENCOUNTERS to be TRUE. +#define WE_OWE_RESTRICT_METATILE TRUE // If TRUE, OWEs will stay within tiles with the same encounter metatile behavior as the one it is currently on, if any. +#define WE_OWE_RESTRICT_MAP TRUE // If TRUE, OWEs will stay within their current map bounds. +#define WE_OWE_UNRESTRICT_SIGHT FALSE // If TRUE, OWEs will ignore all movement restrictions when they can see the player. +#define WE_OWE_SPAWN_REPLACEMENT FALSE // If TRUE, the oldest OWE objects will despawn after a short time and be replaced with a new spawn if possible. +#define WE_OWE_FLEE_DESPAWN TRUE // If TRUE, a fleeing OWE will despawn if it is unable to take a step for a short time. +#define WE_OWE_SHINY_SPARKLE FALSE // If TRUE, shiny OWEs will spawn with a sparkle animation and play the shiny sound effect. +#define WE_OWE_FEEBAS_SPOTS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas OWE. +#define WE_OWE_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an OWE despawns. +#define WE_OWE_APPROACH_FOR_BATTLE TRUE // If TRUE, OWEs will take steps to be right next to the player before the battle starts. +#define WE_OWE_PREVENT_SHINY_DESPAWN FALSE // If TRUE, shiny OWEs will not be despawned when off-screen if on the same map as the player, or be replaced if WE_OWE_SPAWN_REPLACEMENT is TRUE. +#define WE_OWE_PREVENT_FEEBAS_DESPAWN FALSE // If TRUE, Feebas OWEs spawned from special Feebas fishing spots (when WE_OWE_FEEBAS_SPOTS is TRUE) will not be despawned when off-screen if on the same map as the player, or be replaced if WE_OWE_SPAWN_REPLACEMENT is TRUE. +#define WE_OWE_DESPAWN_ON_ENTER_TOWN TRUE // If TRUE, despawns all OWEs upon entering a city (MAP_TYPE_CITY) or town (MAP_TYPE_TOWN). +#define WE_OWE_REPEL_DEXNAV_COLLISION FALSE // If TRUE, OWEs can still be triggered by a collision if a Repel or the DexNav is active. -// Should Move Other Configs? +// Should move others from config/overworld.h here? #endif // GUARD_CONFIG_WILD_ENCOUNTER_OW_H From ec079475692dfc850a0fb0e6ecef51141aab1bbe Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 20:26:31 +0000 Subject: [PATCH 563/572] Readd OverworldWildEncounter_ShouldDisableRandomEncounters --- include/wild_encounter_ow.h | 1 + src/field_control_avatar.c | 2 +- src/wild_encounter_ow.c | 11 +++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index 28f8974dcabe..5863a6f22af0 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -59,6 +59,7 @@ void OverworldWildEncounters_CB(void); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); void OWE_StartEncounter(struct ObjectEvent *mon); void GenerateOverworldWildEncounter(void); +bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void); void OverworldWildEncounter_SetInstantSpawnTimer(void); void OverworldWildEncounter_SetMinimumSpawnTimer(void); void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index f93640456bc8..c399a3e2841e 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -895,7 +895,7 @@ void RestartWildEncounterImmunitySteps(void) static bool8 CheckStandardWildEncounter(u16 metatileBehavior) { - if (FlagGet(OW_FLAG_NO_ENCOUNTER) || !WE_VANILLA_RANDOM) + if (FlagGet(OW_FLAG_NO_ENCOUNTER) || OverworldWildEncounter_ShouldDisableRandomEncounters()) return FALSE; if (sWildEncounterImmunitySteps < 4) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 198e8a4091ac..b18a8a4e34f1 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -582,6 +582,17 @@ static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEven return FALSE; } +bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void) +{ + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !WE_OWE_BATTLE_PIKE) + return FALSE; + + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR && !WE_OWE_BATTLE_PYRAMID) + return FALSE; + + return !WE_VANILLA_RANDOM; +} + void OverworldWildEncounter_SetInstantSpawnTimer(void) { if (!WE_OW_ENCOUNTERS) From 6714562fd18a6cec60c9d869132157b1da06141a Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 20:27:46 +0000 Subject: [PATCH 564/572] default config --- include/config/wild_encounter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config/wild_encounter.h b/include/config/wild_encounter.h index 9bc6e7d52223..07bb6c3bb1cc 100644 --- a/include/config/wild_encounter.h +++ b/include/config/wild_encounter.h @@ -16,7 +16,7 @@ #define WE_OWE_FLEE_DESPAWN TRUE // If TRUE, a fleeing OWE will despawn if it is unable to take a step for a short time. #define WE_OWE_SHINY_SPARKLE FALSE // If TRUE, shiny OWEs will spawn with a sparkle animation and play the shiny sound effect. #define WE_OWE_FEEBAS_SPOTS FALSE // If TRUE, any spot that could result in a Feebas fishing encounter can spawn a Feebas OWE. -#define WE_OWE_DESPAWN_SOUND TRUE // If TRUE, plays SE_FLEE when an OWE despawns. +#define WE_OWE_DESPAWN_SOUND FALSE // If TRUE, plays SE_FLEE when an OWE despawns. #define WE_OWE_APPROACH_FOR_BATTLE TRUE // If TRUE, OWEs will take steps to be right next to the player before the battle starts. #define WE_OWE_PREVENT_SHINY_DESPAWN FALSE // If TRUE, shiny OWEs will not be despawned when off-screen if on the same map as the player, or be replaced if WE_OWE_SPAWN_REPLACEMENT is TRUE. #define WE_OWE_PREVENT_FEEBAS_DESPAWN FALSE // If TRUE, Feebas OWEs spawned from special Feebas fishing spots (when WE_OWE_FEEBAS_SPOTS is TRUE) will not be despawned when off-screen if on the same map as the player, or be replaced if WE_OWE_SPAWN_REPLACEMENT is TRUE. From 81942b33517b86ca8b5c8c164af1b4b5be09e9e1 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:08:03 +0000 Subject: [PATCH 565/572] Rename Functions and Remove OWE_StartEncounter --- asm/macros/event.inc | 4 +- include/wild_encounter_ow.h | 62 +++-- src/battle_setup.c | 2 +- src/dexnav.c | 2 +- src/event_object_movement.c | 76 +++--- src/field_control_avatar.c | 24 +- src/field_effect_helpers.c | 2 +- src/fieldmap.c | 2 +- src/item_use.c | 2 +- src/load_save.c | 2 +- src/overworld.c | 8 +- src/sprite.c | 2 +- src/trainer_see.c | 2 +- src/wild_encounter_ow.c | 464 +++++++++++++++++------------------- 14 files changed, 327 insertions(+), 327 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 2d5444e70caa..2645fbc9362b 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2822,12 +2822,12 @@ @ Starts an Overworld Wild Encounter with a selected object, if possible. .macro tryoverworldwildencounter - callnative GenerateOverworldWildEncounter + callnative StartWildBattleWithOWE .endm @ Start a scripted approach for an overworld wild encounter towards the player. .macro overworldwildencounterapproach - callnative OWE_ApproachForBattle + callnative OWEApproachForBattle .endm @ FRLG diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index 5863a6f22af0..40ef9f460f81 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -57,38 +57,36 @@ enum __attribute__((packed)) OverworldEncounterBehaviors void OverworldWildEncounters_CB(void); bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); -void OWE_StartEncounter(struct ObjectEvent *mon); -void GenerateOverworldWildEncounter(void); -bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void); -void OverworldWildEncounter_SetInstantSpawnTimer(void); -void OverworldWildEncounter_SetMinimumSpawnTimer(void); -void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); -bool32 ShouldRunOverworldEncounterScript(u32 objectEventId); -void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent); -void OverworldWildEncounter_OnObjectEventDespawned(struct ObjectEvent *objectEvent); -bool32 OWE_IsDespawnExempt(struct ObjectEvent *objectEvent); -bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); -u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s32 x, s32 y); -void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags); -bool32 OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId); -void OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette(void); -void OverworldWildEncounter_DespawnOnBattle(void); -void OWE_TryDespawnOverworldWildEncountersCrossingMapConnection(void); -void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite); -void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent); -void OWE_ClearSavedMovementState(struct ObjectEvent *objectEvent); -bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Direction direction); -bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon); -bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon); -bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon); -u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon); -u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); -u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed); -void OWE_ApproachForBattle(void); -void OWE_PlayAmbientCry(void); -u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType); -const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template); -struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); +void StartWildBattleWithOWE(void); +void SetInstantOWESpawnTimer(void); +void SetMinimumOWESpawnTimer(void); +void TryTriggerOverworldWilEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); +bool32 ShouldRunDefaultOWEScript(u32 objectEventId); +void OnOverworldWildEncounterSpawn(struct ObjectEvent *objectEvent); +void OnOverworldWildEncounterDespawn(struct ObjectEvent *objectEvent); +bool32 IsOWEDespawnExempt(struct ObjectEvent *objectEvent); +bool32 DespawnOWEDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); +u32 DespawnOWEDueToTrainerSight(u32 collision, s32 x, s32 y); +void DespwnAllOverworldWildEncounters(enum OverworldObjectEncounterType oweType, u32 flags); +bool32 TryAndDespawnOldestGeneratedOWE_Object(u32 localId, u8 *objectEventId); +void TryAndDespawnOldestGeneratedOWE_Palette(void); +void DespawnOWEOnBattleStart(void); +void TryDespawnOWEsCrossingMapConnection(void); +void RestoreSavedOWEBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite); +void SetSavedOWEMovementState(struct ObjectEvent *objectEvent); +void ClearSavedOWEMovementState(struct ObjectEvent *objectEvent); +bool32 CheckRestrictedOWEMovement(struct ObjectEvent *objectEvent, enum Direction direction); +bool32 CanAwareOWESeePlayer(struct ObjectEvent *mon); +bool32 IsPlayerInsideOWEActiveDistance(struct ObjectEvent *mon); +bool32 IsOWENextToPlayer(struct ObjectEvent *mon); +enum Direction DirectionOfOWEToPlayerFromCollision(struct ObjectEvent *mon); +u32 GetApproachingOWEDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); +u32 GetOWEWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed); +void OWEApproachForBattle(void); +void PlayAmbientOWECry(void); +u32 GetNumberOfActiveOWEs(enum OverworldObjectEncounterType oweType); +const struct ObjectEventTemplate TryGetObjectEventTemplateForOWE(const struct ObjectEventTemplate *template); +struct SpritePalette GetOWESpawnDespawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); extern const u8 InteractWithOverworldWildEncounter[]; diff --git a/src/battle_setup.c b/src/battle_setup.c index 01d3856d04cc..60c9621e5d49 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -260,7 +260,7 @@ static void Task_BattleStart(u8 taskId) SetMainCallback2(CB2_InitBattle); RestartWildEncounterImmunitySteps(); ClearPoisonStepCounter(); - OverworldWildEncounter_DespawnOnBattle(); + DespawnOWEOnBattleStart(); DestroyTask(taskId); } break; diff --git a/src/dexnav.c b/src/dexnav.c index 37c620745afd..ed2e6516b222 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -1845,7 +1845,7 @@ static void CB1_DexNavSearchCallback(void) static void Task_DexNavExitAndSearch(u8 taskId) { - DespwnAllOverworldWildEncounterObjects(OWE_GENERATED, 0); + DespwnAllOverworldWildEncounters(OWE_GENERATED, 0); DexNavGuiFreeResources(); DestroyTask(taskId); SetMainCallback1(CB1_DexNavSearchCallback); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index c785a279d1fe..9a5a8545c624 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1683,7 +1683,7 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * return TRUE; } if (i >= OBJECT_EVENTS_COUNT) - return OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(localId, objectEventId); + return TryAndDespawnOldestGeneratedOWE_Object(localId, objectEventId); *objectEventId = i; for (; i < OBJECT_EVENTS_COUNT; i++) { @@ -1695,7 +1695,7 @@ static bool8 GetAvailableObjectEventId(u16 localId, u8 mapNum, u8 mapGroup, u8 * void RemoveObjectEvent(struct ObjectEvent *objectEvent) { - OverworldWildEncounter_OnObjectEventDespawned(objectEvent); + OnOverworldWildEncounterDespawn(objectEvent); objectEvent->active = FALSE; RemoveObjectEventInternal(objectEvent); // zero potential species info @@ -1923,7 +1923,7 @@ u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemp const struct ObjectEventGraphicsInfo *graphicsInfo; const struct SubspriteTable *subspriteTables = NULL; // May be a good idea to move the if check contained by this function to outside it for clarity. - const struct ObjectEventTemplate objectEventTemplateLocal = TryGetObjectEventTemplateForOverworldEncounter(objectEventTemplate); + const struct ObjectEventTemplate objectEventTemplateLocal = TryGetObjectEventTemplateForOWE(objectEventTemplate); u16 graphicsId = objectEventTemplateLocal.graphicsId; graphicsInfo = GetObjectEventGraphicsInfo(graphicsId); @@ -1938,7 +1938,7 @@ u8 TrySpawnObjectEventTemplate(const struct ObjectEventTemplate *objectEventTemp if (subspriteTables) SetSubspriteTables(&gSprites[gObjectEvents[objectEventId].spriteId], subspriteTables); - OverworldWildEncounter_OnObjectEventSpawned(&gObjectEvents[objectEventId]); + OnOverworldWildEncounterSpawn(&gObjectEvents[objectEventId]); return objectEventId; } @@ -2983,7 +2983,7 @@ static void RemoveObjectEventIfOutsideView(struct ObjectEvent *objectEvent) && objectEvent->initialCoords.y >= top && objectEvent->initialCoords.y <= bottom) return; - if (OWE_IsDespawnExempt(objectEvent)) + if (IsOWEDespawnExempt(objectEvent)) return; RemoveObjectEvent(objectEvent); @@ -3068,7 +3068,7 @@ static void SpawnObjectEventOnReturnToField(u8 objectEventId, s16 x, s16 y) ResetObjectEventFldEffData(objectEvent); SetObjectSubpriorityByElevation(objectEvent->previousElevation, sprite, 1); - OWE_RestoreBehaviorState(objectEvent, sprite); + RestoreSavedOWEBehaviorState(objectEvent, sprite); } } @@ -6635,10 +6635,10 @@ u32 GetObjectObjectCollidesWith(struct ObjectEvent *objectEvent, s16 x, s16 y, b { if (AreElevationsCompatible(objectEvent->currentElevation, curObject->currentElevation)) { - if (OWE_DespawnMonDueToNPCCollision(curObject, objectEvent)) + if (DespawnOWEDueToNPCCollision(curObject, objectEvent)) continue; - OWE_TryTriggerEncounter(objectEvent, curObject); + TryTriggerOverworldWilEncounter(objectEvent, curObject); return i; } } @@ -11925,7 +11925,7 @@ bool8 MovementType_OverworldWildEncounter_WanderAround_Step3(struct ObjectEvent if (OW_MON_WANDER_WALK == TRUE && IS_OW_MON_OBJ(objectEvent)) UpdateMonMoveInPlace(objectEvent, sprite); - if (OWE_CanAwareMonSeePlayer(objectEvent)) + if (CanAwareOWESeePlayer(objectEvent)) sprite->sTypeFuncId = 7; return FALSE; @@ -11940,7 +11940,7 @@ bool8 MovementType_OverworldWildEncounter_WanderAround_Step4(struct ObjectEvent SetObjectEventDirection(objectEvent, chosenDirection); sprite->sTypeFuncId = 5; - if (OWE_CheckRestrictedMovement(objectEvent, chosenDirection)) + if (CheckRestrictedOWEMovement(objectEvent, chosenDirection)) sprite->sTypeFuncId = 1; return TRUE; @@ -11948,7 +11948,7 @@ bool8 MovementType_OverworldWildEncounter_WanderAround_Step4(struct ObjectEvent bool8 MovementType_OverworldWildEncounter_WanderAround_Step5(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - ObjectEventSetSingleMovement(objectEvent, sprite, OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetIdleSpeedFromSpecies(OW_SPECIES(objectEvent)))); + ObjectEventSetSingleMovement(objectEvent, sprite, GetOWEWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetIdleSpeedFromSpecies(OW_SPECIES(objectEvent)))); objectEvent->singleMovementActive = TRUE; sprite->sTypeFuncId = 6; return TRUE; @@ -11959,7 +11959,7 @@ movement_type_def(MovementType_OverworldWildEncounter_ChasePlayer, gMovementType bool8 MovementType_OverworldWildEncounter_Common_Step7(struct ObjectEvent *objectEvent, struct Sprite *sprite) { ClearObjectEventMovement(objectEvent, sprite); - OWE_SetSavedMovementState(objectEvent); + SetSavedOWEMovementState(objectEvent); sprite->sTypeFuncId = 8; return TRUE; } @@ -11968,7 +11968,7 @@ bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step8(struct ObjectEvent * { enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); SetObjectEventDirection(objectEvent, direction); - if (OWE_IsMonNextToPlayer(objectEvent)) + if (IsOWENextToPlayer(objectEvent)) { sprite->sTypeFuncId = 10; return TRUE; @@ -11999,10 +11999,10 @@ bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step10(struct ObjectEvent bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u16 speciesId = OW_SPECIES(objectEvent); - u8 movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + u8 movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); sprite->sTypeFuncId = 12; - if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) + if (CheckRestrictedOWEMovement(objectEvent, objectEvent->movementDirection)) { s16 x = objectEvent->currentCoords.x; s16 y = objectEvent->currentCoords.y; @@ -12015,9 +12015,9 @@ bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step11(struct ObjectEvent return FALSE; } - u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) + enum Direction newDirection = DirectionOfOWEToPlayerFromCollision(objectEvent); + movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + if (CheckRestrictedOWEMovement(objectEvent, newDirection)) movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); } @@ -12032,9 +12032,9 @@ bool8 MovementType_OverworldWildEncounter_Common_Step12(struct ObjectEvent *obje { objectEvent->singleMovementActive = FALSE; sprite->sTypeFuncId = 10; - if (!OWE_IsPlayerInsideMonActiveDistance(objectEvent)) + if (!IsPlayerInsideOWEActiveDistance(objectEvent)) { - OWE_ClearSavedMovementState(objectEvent); + ClearSavedOWEMovementState(objectEvent); sprite->sTypeFuncId = 0; } } @@ -12072,15 +12072,15 @@ bool8 MovementType_OverworldWildEncounter_FleePlayer_Step10(struct ObjectEvent * bool8 MovementType_OverworldWildEncounter_FleePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { u16 speciesId = OW_SPECIES(objectEvent); - u8 movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) + u8 movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + if (CheckRestrictedOWEMovement(objectEvent, objectEvent->movementDirection)) { - u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); + enum Direction newDirection = DirectionOfOWEToPlayerFromCollision(objectEvent); if (newDirection != objectEvent->movementDirection) newDirection = GetOppositeDirection(newDirection); - movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) + movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + if (CheckRestrictedOWEMovement(objectEvent, newDirection)) { sCollisionTimer++; movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); @@ -12110,7 +12110,7 @@ bool8 MovementType_OverworldWildEncounter_WatchPlayer_Step8(struct ObjectEvent * enum Direction direction = DetermineObjectEventDirectionFromObject(&gObjectEvents[gPlayerAvatar.objectEventId], objectEvent); SetObjectEventDirection(objectEvent, direction); sprite->sTypeFuncId = 10; - if (!OWE_IsMonNextToPlayer(objectEvent)) + if (!IsOWENextToPlayer(objectEvent)) { ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_QUESTION_MARK); sprite->sTypeFuncId = 9; @@ -12144,7 +12144,7 @@ bool8 MovementType_OverworldWildEncounter_ApproachPlayer_Step8(struct ObjectEven SetObjectEventDirection(objectEvent, direction); sJumpTimer = (Random() % (OWE_APPROACH_JUMP_TIMER_MAX - OWE_APPROACH_JUMP_TIMER_MIN)) + OWE_APPROACH_JUMP_TIMER_MIN; sprite->sTypeFuncId = 10; - if (!OWE_IsMonNextToPlayer(objectEvent)) + if (!IsOWENextToPlayer(objectEvent)) { ObjectEventSetSingleMovement(objectEvent, sprite, MOVEMENT_ACTION_EMOTE_QUESTION_MARK); sprite->sTypeFuncId = 9; @@ -12163,22 +12163,22 @@ bool8 MovementType_OverworldWildEncounter_ApproachPlayer_Step10(struct ObjectEve bool8 MovementType_OverworldWildEncounter_ApproachPlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { bool32 equalDistances = FALSE; - u32 distance = OWE_GetApproachingMonDistanceToPlayer(objectEvent, &equalDistances); + u32 distance = GetApproachingOWEDistanceToPlayer(objectEvent, &equalDistances); u16 speciesId = OW_SPECIES(objectEvent); u8 movementActionId; if (distance <= 1) { SetObjectEventDirection(objectEvent, GetOppositeDirection(objectEvent->movementDirection)); - movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) + movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + if (CheckRestrictedOWEMovement(objectEvent, objectEvent->movementDirection)) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); + enum Direction newDirection = DirectionOfOWEToPlayerFromCollision(objectEvent); if (objectEvent->currentCoords.x != player->currentCoords.x && objectEvent->currentCoords.y != player->currentCoords.y) newDirection = GetOppositeDirection(newDirection); - movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) + movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + if (CheckRestrictedOWEMovement(objectEvent, newDirection)) movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); } } @@ -12198,9 +12198,9 @@ bool8 MovementType_OverworldWildEncounter_ApproachPlayer_Step11(struct ObjectEve } else { - movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, objectEvent->movementDirection)) + if (CheckRestrictedOWEMovement(objectEvent, objectEvent->movementDirection)) { s16 x = objectEvent->currentCoords.x; s16 y = objectEvent->currentCoords.y; @@ -12212,10 +12212,10 @@ bool8 MovementType_OverworldWildEncounter_ApproachPlayer_Step11(struct ObjectEve objectEvent->singleMovementActive = TRUE; return FALSE; } - u8 newDirection = OWE_DirectionToPlayerFromCollision(objectEvent); - movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + enum Direction newDirection = DirectionOfOWEToPlayerFromCollision(objectEvent); + movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(newDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(objectEvent, newDirection)) + if (CheckRestrictedOWEMovement(objectEvent, newDirection)) movementActionId = GetWalkInPlaceNormalMovementAction(objectEvent->facingDirection); } diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index c399a3e2841e..b65bdb5e79a5 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -39,6 +39,7 @@ #include "constants/event_bg.h" #include "constants/event_objects.h" #include "constants/field_poison.h" +#include "constants/layouts.h" #include "constants/metatile_behaviors.h" #include "constants/songs.h" #include "constants/trainer_hill.h" @@ -406,7 +407,7 @@ static const u8 *GetInteractedObjectEventScript(struct MapPosition *position, u8 script = GetTrainerHillTrainerScript(); else if (PlayerHasFollowerNPC() && objectEventId == GetFollowerNPCObjectId()) script = GetFollowerNPCScriptPointer(); - else if (ShouldRunOverworldEncounterScript(objectEventId)) + else if (ShouldRunDefaultOWEScript(objectEventId)) script = InteractWithOverworldWildEncounter; else script = GetObjectEventScriptPointerByObjectEventId(objectEventId); @@ -634,7 +635,7 @@ static const u8 *GetInteractedWaterScript(struct MapPosition *position, u8 metat // Does this need a define for the surf elevation (1) check? // Can be used in sElevationToSubpriority and other places too u8 objectEventId = GetObjectEventIdByPosition(position->x, position->y, 1); - if (IsPlayerFacingSurfableFishableWater() == TRUE && ShouldRunOverworldEncounterScript(objectEventId)) + if (IsPlayerFacingSurfableFishableWater() == TRUE && ShouldRunDefaultOWEScript(objectEventId)) { gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; return InteractWithOverworldWildEncounter; @@ -893,9 +894,26 @@ void RestartWildEncounterImmunitySteps(void) sWildEncounterImmunitySteps = 0; } +static bool32 ShouldDisableRandomEncounters(void) +{ + if (FlagGet(OW_FLAG_NO_ENCOUNTER)) + return TRUE; + + if (!WE_VANILLA_RANDOM && WE_OW_ENCOUNTERS) + { + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !WE_OWE_BATTLE_PIKE) + return FALSE; + + if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR && !WE_OWE_BATTLE_PYRAMID) + return FALSE; + } + + return !WE_VANILLA_RANDOM; +} + static bool8 CheckStandardWildEncounter(u16 metatileBehavior) { - if (FlagGet(OW_FLAG_NO_ENCOUNTER) || OverworldWildEncounter_ShouldDisableRandomEncounters()) + if (ShouldDisableRandomEncounters()) return FALSE; if (sWildEncounterImmunitySteps < 4) diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 1ba381c4486e..0804482bd8ed 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1914,7 +1914,7 @@ u32 FldEff_OWE_SpawnAnim(void) u8 visual; s16 xOffset = 0, yOffset = 0; enum OverworldEncounterSpawnAnim spawnAnim = gFieldEffectArguments[2]; - struct SpritePalette palette = OWE_GetSpawnAnimFldEffPalette(spawnAnim); + struct SpritePalette palette = GetOWESpawnDespawnAnimFldEffPalette(spawnAnim); switch (spawnAnim) { diff --git a/src/fieldmap.c b/src/fieldmap.c index b8e877fcf597..7688fbe16870 100644 --- a/src/fieldmap.c +++ b/src/fieldmap.c @@ -808,7 +808,7 @@ bool8 CameraMove(int x, int y) gSaveBlock1Ptr->pos.x += x; gSaveBlock1Ptr->pos.y += y; MoveMapViewToBackup(direction); - OWE_TryDespawnOverworldWildEncountersCrossingMapConnection(); + TryDespawnOWEsCrossingMapConnection(); } return gCamera.active; diff --git a/src/item_use.c b/src/item_use.c index 4dcaf90d9d43..803a25041b6c 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -1003,7 +1003,7 @@ static void Task_UseRepel(u8 taskId) VarSet(VAR_LAST_REPEL_LURE_USED, gSpecialVar_ItemId); #endif RemoveUsedItem(); - OverworldWildEncounter_SetInstantSpawnTimer(); + SetInstantOWESpawnTimer(); if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); else diff --git a/src/load_save.c b/src/load_save.c index 7d5ca2311c4e..e9de05f7a8ae 100644 --- a/src/load_save.c +++ b/src/load_save.c @@ -238,7 +238,7 @@ void LoadObjectEvents(void) gObjectEvents[i].graphicsId & OBJ_EVENT_MON) gObjectEvents[i].active = TRUE; } - OverworldWildEncounter_SetMinimumSpawnTimer(); + SetMinimumOWESpawnTimer(); } void CopyPartyAndObjectsToSave(void) diff --git a/src/overworld.c b/src/overworld.c index eacc1646be28..c1198922764d 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -926,7 +926,7 @@ void LoadMapFromCameraTransition(u8 mapGroup, u8 mapNum) || gMapHeader.regionMapSectionId != sLastMapSectionId) ShowMapNamePopup(); } - OverworldWildEncounter_SetMinimumSpawnTimer(); + SetMinimumOWESpawnTimer(); } static void LoadMapFromWarp(bool32 a1) @@ -987,7 +987,7 @@ static void LoadMapFromWarp(bool32 a1) UpdateTVScreensOnMap(gBackupMapLayout.width, gBackupMapLayout.height); InitSecretBaseAppearance(TRUE); } - OverworldWildEncounter_SetMinimumSpawnTimer(); + SetMinimumOWESpawnTimer(); } void ResetInitialPlayerAvatarState(void) @@ -1399,13 +1399,13 @@ static bool32 ShouldPlayAmbientCryVanillaOWE(void) { bool32 owePlayed = FALSE; - if (GetNumberActiveOverworldEncounters(OWE_ANY)) + if (GetNumberOfActiveOWEs(OWE_ANY)) { switch (OW_AMBIENT_CRIES) { case OW_AMBIENT_CRIES_OWE_ONLY: case OW_AMBIENT_CRIES_OWE_PRIORITY: - OWE_PlayAmbientCry(); + PlayAmbientOWECry(); owePlayed = TRUE; break; diff --git a/src/sprite.c b/src/sprite.c index 7da601fcae26..78f846b94420 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -1630,7 +1630,7 @@ u32 LoadSpritePalette(const struct SpritePalette *palette) if (index == 0xFF) { - OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette(); + TryAndDespawnOldestGeneratedOWE_Palette(); index = IndexOfSpritePaletteTag(TAG_NONE); if (index == 0xFF) diff --git a/src/trainer_see.c b/src/trainer_see.c index a4caf9bb3779..a0e0a75b3abe 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -723,7 +723,7 @@ static u8 CheckPathBetweenTrainerAndPlayer(struct ObjectEvent *trainerObj, u8 ap { // Check for collisions on approach, ignoring the "out of range" collision for regular movement collision = GetCollisionFlagsAtCoords(trainerObj, x, y, direction); - collision = OWE_DespawnMonDueToTrainerSight(collision, x, y); + collision = DespawnOWEDueToTrainerSight(collision, x, y); if (collision != 0 && (collision & ~(1 << (COLLISION_OUTSIDE_RANGE - 1)))) return 0; } diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index b18a8a4e34f1..af4cc178af6e 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -76,96 +76,96 @@ #endif -static inline u32 GetLocalIdByOverworldSpawnSlot(u32 spawnSlot) +static inline u32 GetLocalIdByOWESpawnSlot(u32 spawnSlot) { return LOCALID_OW_ENCOUNTER_END - spawnSlot; } -static inline u32 GetSpawnSlotByLocalId(u32 localId) +static inline u32 GetSpawnSlotByOWELocalId(u32 localId) { return LOCALID_OW_ENCOUNTER_END - localId; } -static inline u32 OWE_GetRoamerIndex(const struct ObjectEvent *object) +static inline u32 GetOWERoamerIndex(const struct ObjectEvent *object) { return object->sRoamerOutbreakStatus & ~OWE_SAVED_MOVEMENT_STATE_FLAG; } -static inline bool32 OWE_HasSavedMovementState(const struct ObjectEvent *object) +static inline bool32 HasSavedOWEMovementState(const struct ObjectEvent *object) { return object->sRoamerOutbreakStatus & OWE_SAVED_MOVEMENT_STATE_FLAG; } -void OWE_SetSavedMovementState(struct ObjectEvent *objectEvent) +void SetSavedOWEMovementState(struct ObjectEvent *objectEvent) { objectEvent->sRoamerOutbreakStatus |= OWE_SAVED_MOVEMENT_STATE_FLAG; } -void OWE_ClearSavedMovementState(struct ObjectEvent *objectEvent) +void ClearSavedOWEMovementState(struct ObjectEvent *objectEvent) { objectEvent->sRoamerOutbreakStatus &= ~OWE_SAVED_MOVEMENT_STATE_FLAG; } -static inline u32 OWE_GetEncounterLevel(u32 level) +static inline u32 GetOWEEncounterLevel(u32 level) { return level & ~OWE_NO_DESPAWN_FLAG; } -static inline void OWE_SetEncounterLevel(u32 *level, u32 newLevel) +static inline void SetOWEEncounterLevel(u32 *level, u32 newLevel) { *level = (*level & OWE_NO_DESPAWN_FLAG) | (newLevel & ~OWE_NO_DESPAWN_FLAG); } -static inline bool32 OWE_HasNoDespawnFlag(const struct ObjectEvent *object) +static inline bool32 HasOWENoDespawnFlag(const struct ObjectEvent *object) { return object->sOverworldEncounterLevel & OWE_NO_DESPAWN_FLAG; } -static inline void OWE_SetNoDespawnFlag(u32 *level) +static inline void SetOWENoDespawnFlag(u32 *level) { *level |= OWE_NO_DESPAWN_FLAG; } -static inline bool32 OWE_ShouldSpawnWaterMons(void) +static inline bool32 ShouldSpawnWaterOWE(void) { return TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING | PLAYER_AVATAR_FLAG_UNDERWATER); } -static bool32 OWE_CreateEnemyPartyMon(u32 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); -static bool32 OWE_DoesRoamerObjectExist(void); -static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index); -static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent); -static bool32 GenerateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak); -static bool32 GenerateOverworldWildEncounter_CheckBattleFrontier(u32 headerId); -static bool32 GenerateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); -static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); -static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons); -static u32 GetOldestSlot(bool32 forceRemove); -static u32 NextSpawnMonSlot(void); -static u32 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot); -static bool32 TrySelectTile(s32* outX, s32* outY); -static void SetOverworldEncounterSpeciesInfo(u32 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); -static u32 GetOverworldEncounterObjectEventGraphicsId(u32 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *roamerIndex, s32 x, s32 y); -static bool32 CanRemoveOverworldEncounter(u32 localId); -static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y); -static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y); -static bool32 OWE_CanEncounterBeLoaded_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y); -static void SortOWEMonAges(void); -static u32 RemoveOldestGeneratedOverworldEncounter(void); -static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); -static void OWE_SetNewSpawnCountdown(void); -static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); -static enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior); -static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent); -static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void); -static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent); -static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s32 xNew, s32 yNew, enum Direction newDirection, enum Direction collisionDirection); -static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); -static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); -static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance); -static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); -static void Task_OWE_ApproachForBattle(u8 taskId); -static bool32 OWE_CheckSpecies(u32 speciesId); +static bool32 CreateEnemyPartyOWE(u32 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); +static bool32 OWE_DoesOWERoamerExist(void); +static u32 GetOWERoamerStatusFromIndex(u32 indexRoamer); +static u32 GetOWERoamerOutbreakStatus(struct ObjectEvent *objectEvent); +static bool32 StartWildBattleWithOWE_CheckRoamer(u32 indexRoamerOutbreak); +static bool32 StartWildBattleWithOWE_CheckBattleFrontier(u32 headerId); +static bool32 StartWildBattleWithOWE_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); +static bool32 StartWildBattleWithOWE_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); +static bool32 CheckCuurentWildMonHeaderForOWE(bool32 shouldSpawnWaterMons); +static u32 GetOldestActiveOWESlot(bool32 forceRemove); +static u32 GetNextOWESpawnSlot(void); +static u32 GetSpeciesByOWESpawnSlot(u32 spawnSlot); +static bool32 TrySelectTileForOWE(s32* outX, s32* outY); +static void SetSpeciesInfoForOWE(u32 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); +static u32 GetGraphicsIdForOWE(u32 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); +static bool32 CheckCanRemoveOWE(u32 localId); +static bool32 CheckCanLoadOWE(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y); +static bool32 CheckCanLoadOWE_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y); +static bool32 CheckCanLoadOWE_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y); +static void SortOWEAges(void); +static u32 RemoveOldestGeneratedOWE(void); +static bool32 ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); +static void SetNewOWESpawnCountdown(void); +static void DoOWESpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); +static enum OverworldEncounterSpawnAnim GetOWESpawnDespawnAnimType(u32 metatileBehavior); +static void PlayOWECry(struct ObjectEvent *objectEvent); +static struct ObjectEvent *GetOWEObjectEvent(void); +static bool32 OWE_ShouldPlayOWEFleeSound(struct ObjectEvent *objectEvent); +static bool32 CheckRestrictedOWEMovementAtCoords(struct ObjectEvent *mon, s32 xNew, s32 yNew, enum Direction newDirection, enum Direction collisionDirection); +static bool32 CheckRestrictedOWEMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); +static bool32 CheckRestrictedOWEMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); +static bool32 IsOWELineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance); +static enum Direction CheckOWEPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); +static void Task_OWEApproachForBattle(u8 taskId); +static bool32 CheckValidOWESpecies(u32 speciesId); static EWRAM_DATA u8 sOWESpawnCountdown = 0; @@ -178,9 +178,9 @@ struct AgeSort void OverworldWildEncounters_CB(void) { - bool32 shouldSpawnWaterMons = OWE_ShouldSpawnWaterMons(); + bool32 shouldSpawnWaterMons = ShouldSpawnWaterOWE(); - if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING) || !OWE_CheckActiveEncounterTable(shouldSpawnWaterMons)) + if (ArePlayerFieldControlsLocked() || FlagGet(DN_FLAG_SEARCHING) || !CheckCuurentWildMonHeaderForOWE(shouldSpawnWaterMons)) return; if (!WE_OW_ENCOUNTERS @@ -192,14 +192,14 @@ void OverworldWildEncounters_CB(void) { if (sOWESpawnCountdown != OWE_NO_ENCOUNTER_SET) { - DespwnAllOverworldWildEncounterObjects(OWE_GENERATED, 0); + DespwnAllOverworldWildEncounters(OWE_GENERATED, 0); sOWESpawnCountdown = OWE_NO_ENCOUNTER_SET; } return; } else if (sOWESpawnCountdown == OWE_NO_ENCOUNTER_SET) { - OverworldWildEncounter_SetMinimumSpawnTimer(); + SetMinimumOWESpawnTimer(); } if (sOWESpawnCountdown) @@ -208,18 +208,18 @@ void OverworldWildEncounters_CB(void) return; } - DespwnAllOverworldWildEncounterObjects(OWE_GENERATED, WILD_CHECK_REPEL); + DespwnAllOverworldWildEncounters(OWE_GENERATED, WILD_CHECK_REPEL); struct ObjectEvent* player = &gObjectEvents[gPlayerAvatar.objectEventId]; if (player->currentCoords.x != player->previousCoords.x || player->currentCoords.y != player->previousCoords.y) return; - u32 spawnSlot = NextSpawnMonSlot(); + u32 spawnSlot = GetNextOWESpawnSlot(); s32 x, y; if (spawnSlot == OWE_INVALID_SPAWN_SLOT || (shouldSpawnWaterMons && AreLegendariesInSootopolisPreventingEncounters()) - || !TrySelectTile(&x, &y)) + || !TrySelectTileForOWE(&x, &y)) { - OverworldWildEncounter_SetMinimumSpawnTimer(); + SetMinimumOWESpawnTimer(); return; } @@ -227,16 +227,16 @@ void OverworldWildEncounters_CB(void) bool32 isShiny = FALSE; bool32 isFemale = FALSE; u32 indexRoamerOutbreak = OWE_NON_ROAMER_OUTBREAK; - u32 localId = GetLocalIdByOverworldSpawnSlot(spawnSlot); + u32 localId = GetLocalIdByOWESpawnSlot(spawnSlot); u32 level = MIN_LEVEL; - u32 graphicsId = GetOverworldEncounterObjectEventGraphicsId(&speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak, x, y); + u32 graphicsId = GetGraphicsIdForOWE(&speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak, x, y); if (speciesId == SPECIES_NONE - || !IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(level)) - || !IsAbilityAllowingEncounter(OWE_GetEncounterLevel(level)) - || !OWE_CanEncounterBeLoaded(speciesId, isFemale, isShiny, x, y)) + || !IsWildLevelAllowedByRepel(GetOWEEncounterLevel(level)) + || !IsAbilityAllowingEncounter(GetOWEEncounterLevel(level)) + || !CheckCanLoadOWE(speciesId, isFemale, isShiny, x, y)) { - OverworldWildEncounter_SetMinimumSpawnTimer(); + SetMinimumOWESpawnTimer(); return; } @@ -252,13 +252,13 @@ void OverworldWildEncounters_CB(void) }; u32 objectEventId = GetObjectEventIdByLocalId(localId); struct ObjectEvent *object = &gObjectEvents[objectEventId]; - if (OWE_ShouldDespawnGeneratedForNewOWE(object)) + if (ShouldDespawnGeneratedForNewOWE(object)) RemoveObjectEvent(object); objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); assertf(objectEventId < OBJECT_EVENTS_COUNT, "could not spawn generated overworld encounter. too many object events exist") { - OverworldWildEncounter_SetMinimumSpawnTimer(); + SetMinimumOWESpawnTimer(); return; } @@ -270,7 +270,7 @@ void OverworldWildEncounters_CB(void) enum Direction directions[4]; memcpy(directions, gStandardDirections, sizeof directions); ObjectEventTurn(object, directions[Random() & 3]); - OWE_SetNewSpawnCountdown(); + SetNewOWESpawnCountdown(); } bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType) @@ -295,26 +295,13 @@ bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldO } } -void OWE_StartEncounter(struct ObjectEvent *mon) -{ - gSpecialVar_LastTalked = mon->localId; - gSpecialVar_0x8004 = OW_SPECIES(mon); - gSelectedObjectEvent = GetObjectEventIdByLocalId(mon->localId); - - // Stop the bobbing animation. - if (mon->movementActionId >= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_DOWN && mon->movementActionId <= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_RIGHT) - ClearObjectEventMovement(mon, &gSprites[mon->spriteId]); - - ScriptContext_SetupScript(InteractWithOverworldWildEncounter); -} - -void GenerateOverworldWildEncounter(void) +void StartWildBattleWithOWE(void) { u32 localId = gSpecialVar_LastTalked; u32 objEventId = GetObjectEventIdByLocalId(localId); u32 headerId = GetCurrentMapWildMonHeaderId(); struct ObjectEvent *object = &gObjectEvents[objEventId]; - u32 indexRoamerOutbreak = OWE_GetRoamerIndex(object); + u32 indexRoamerOutbreak = GetOWERoamerIndex(object); assertf(objEventId < OBJECT_EVENTS_COUNT && IsOverworldWildEncounter(object, OWE_ANY), "cannot start overworld wild enocunter as the selected object is invalid.\nlocalId: %d", localId) { @@ -323,13 +310,13 @@ void GenerateOverworldWildEncounter(void) return; } - if (indexRoamerOutbreak && GenerateOverworldWildEncounter_CheckRoamer(OWE_GetObjectRoamerOutbreakStatus(object))) + if (indexRoamerOutbreak && StartWildBattleWithOWE_CheckRoamer(GetOWERoamerOutbreakStatus(object))) return; u32 speciesId = OW_SPECIES(object); bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; u32 gender = OW_FEMALE(object) ? MON_FEMALE : MON_MALE; - u32 level = OWE_GetEncounterLevel(object->sOverworldEncounterLevel); + u32 level = GetOWEEncounterLevel(object->sOverworldEncounterLevel); u32 personality; switch (gSpeciesInfo[speciesId].genderRatio) @@ -349,19 +336,19 @@ void GenerateOverworldWildEncounter(void) GiveMonInitialMoveset(&gEnemyParty[0]); SetMonData(&gEnemyParty[0], MON_DATA_IS_SHINY, &shiny); - if (GenerateOverworldWildEncounter_CheckBattleFrontier(headerId)) + if (StartWildBattleWithOWE_CheckBattleFrontier(headerId)) return; - if (GenerateOverworldWildEncounter_CheckMassOutbreak(indexRoamerOutbreak, speciesId)) + if (StartWildBattleWithOWE_CheckMassOutbreak(indexRoamerOutbreak, speciesId)) return; - if (GenerateOverworldWildEncounter_CheckDoubleBattle(object, headerId)) + if (StartWildBattleWithOWE_CheckDoubleBattle(object, headerId)) return; BattleSetup_StartWildBattle(); } -static bool32 OWE_CreateEnemyPartyMon(u32 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) +static bool32 CreateEnemyPartyOWE(u32 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) { const struct WildPokemonInfo *wildMonInfo; enum WildPokemonArea wildArea; @@ -428,16 +415,16 @@ static bool32 OWE_CreateEnemyPartyMon(u32 *speciesId, u32 *level, u32 *indexRoam if (*indexRoamerOutbreak != OWE_INVALID_ROAMER_OUTBREAK) { - if (TryStartRoamerEncounter() && !OWE_DoesRoamerObjectExist()) + if (TryStartRoamerEncounter() && !OWE_DoesOWERoamerExist()) { - *indexRoamerOutbreak = OWE_GetObjectRoamerStatusFromIndex(gEncounteredRoamerIndex); + *indexRoamerOutbreak = GetOWERoamerStatusFromIndex(gEncounteredRoamerIndex); return TRUE; } else if (WE_OWE_FEEBAS_SPOTS && MetatileBehavior_IsWaterWildEncounter(metatileBehavior) && CheckFeebasAtCoords(x, y)) { CreateWildMon(gWildFeebas.species, ChooseWildMonLevel(&gWildFeebas, 0, WILD_AREA_FISHING)); if (WE_OWE_PREVENT_FEEBAS_DESPAWN) - OWE_SetNoDespawnFlag(level); + SetOWENoDespawnFlag(level); return TRUE; } @@ -456,32 +443,32 @@ static bool32 OWE_CreateEnemyPartyMon(u32 *speciesId, u32 *level, u32 *indexRoam return TryGenerateWildMon(wildMonInfo, wildArea, 0); } -static bool32 OWE_DoesRoamerObjectExist(void) +static bool32 OWE_DoesOWERoamerExist(void) { for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { struct ObjectEvent *object = &gObjectEvents[i]; - if (IsOverworldWildEncounter(object, OWE_ANY) && OWE_GetObjectRoamerOutbreakStatus(object) == gEncounteredRoamerIndex) + if (IsOverworldWildEncounter(object, OWE_ANY) && GetOWERoamerOutbreakStatus(object) == gEncounteredRoamerIndex) return TRUE; } return FALSE; } -static u32 OWE_GetObjectRoamerStatusFromIndex(u32 index) +static u32 GetOWERoamerStatusFromIndex(u32 indexRoamer) { - if (index < ROAMER_COUNT) - return index + 1; + if (indexRoamer < ROAMER_COUNT) + return indexRoamer + 1; - return index; + return indexRoamer; } -static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) +static u32 GetOWERoamerOutbreakStatus(struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return OWE_INVALID_ROAMER_OUTBREAK; - u32 status = OWE_GetRoamerIndex(objectEvent); + u32 status = GetOWERoamerIndex(objectEvent); if (status == OWE_NON_ROAMER_OUTBREAK || status == OWE_MASS_OUTBREAK_INDEX) { return OWE_INVALID_ROAMER_OUTBREAK; @@ -490,7 +477,7 @@ static u32 OWE_GetObjectRoamerOutbreakStatus(struct ObjectEvent *objectEvent) return status - 1; } -static bool32 GenerateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak) +static bool32 StartWildBattleWithOWE_CheckRoamer(u32 indexRoamerOutbreak) { if (indexRoamerOutbreak < ROAMER_COUNT && IsRoamerAt(indexRoamerOutbreak, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) @@ -504,7 +491,7 @@ static bool32 GenerateOverworldWildEncounter_CheckRoamer(u32 indexRoamerOutbreak return FALSE; } -static bool32 GenerateOverworldWildEncounter_CheckBattleFrontier(u32 headerId) +static bool32 StartWildBattleWithOWE_CheckBattleFrontier(u32 headerId) { if (headerId == HEADER_NONE) { @@ -530,7 +517,7 @@ static bool32 GenerateOverworldWildEncounter_CheckBattleFrontier(u32 headerId) return FALSE; } -static bool32 GenerateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId) +static bool32 StartWildBattleWithOWE_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId) { if (indexRoamerOutbreak == OWE_MASS_OUTBREAK_INDEX && gSaveBlock1Ptr->outbreakPokemonSpecies == speciesId @@ -547,7 +534,7 @@ static bool32 GenerateOverworldWildEncounter_CheckMassOutbreak(u32 indexRoamerOu return FALSE; } -static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId) +static bool32 StartWildBattleWithOWE_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId) { enum WildPokemonArea wildArea; enum TimeOfDay timeOfDay; @@ -582,18 +569,7 @@ static bool32 GenerateOverworldWildEncounter_CheckDoubleBattle(struct ObjectEven return FALSE; } -bool32 OverworldWildEncounter_ShouldDisableRandomEncounters(void) -{ - if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !WE_OWE_BATTLE_PIKE) - return FALSE; - - if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR && !WE_OWE_BATTLE_PYRAMID) - return FALSE; - - return !WE_VANILLA_RANDOM; -} - -void OverworldWildEncounter_SetInstantSpawnTimer(void) +void SetInstantOWESpawnTimer(void) { if (!WE_OW_ENCOUNTERS) return; @@ -601,17 +577,17 @@ void OverworldWildEncounter_SetInstantSpawnTimer(void) sOWESpawnCountdown = 0; } -void OverworldWildEncounter_SetMinimumSpawnTimer(void) +void SetMinimumOWESpawnTimer(void) { if (!WE_OW_ENCOUNTERS) return; sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM; - if (LURE_STEP_COUNT && GetNumberActiveOverworldEncounters(OWE_GENERATED) < OWE_SPAWNS_MAX) + if (LURE_STEP_COUNT && GetNumberOfActiveOWEs(OWE_GENERATED) < OWE_SPAWNS_MAX) sOWESpawnCountdown = OWE_SPAWN_TIME_LURE; } -void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) +void TryTriggerOverworldWilEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider) { if (WE_OWE_REPEL_DEXNAV_COLLISION && (FlagGet(DN_FLAG_SEARCHING) || REPEL_STEP_COUNT)) return; @@ -623,19 +599,27 @@ void OWE_TryTriggerEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *c return; struct ObjectEvent *wildMon = playerIsCollider ? obstacle : collider; - u32 indexRoamerOutbreak = OWE_GetRoamerIndex(wildMon); + u32 indexRoamerOutbreak = GetOWERoamerIndex(wildMon); if (indexRoamerOutbreak && indexRoamerOutbreak < OWE_MASS_OUTBREAK_INDEX - && !IsRoamerAt(OWE_GetObjectRoamerOutbreakStatus(wildMon), gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) + && !IsRoamerAt(GetOWERoamerOutbreakStatus(wildMon), gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) { RemoveObjectEvent(wildMon); return; } - OWE_StartEncounter(wildMon); + gSpecialVar_LastTalked = wildMon->localId; + gSpecialVar_0x8004 = OW_SPECIES(wildMon); + gSelectedObjectEvent = GetObjectEventIdByLocalId(wildMon->localId); + + // Stop the bobbing animation. + if (wildMon->movementActionId >= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_DOWN && wildMon->movementActionId <= MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_RIGHT) + ClearObjectEventMovement(wildMon, &gSprites[wildMon->spriteId]); + + ScriptContext_SetupScript(InteractWithOverworldWildEncounter); } -bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) +bool32 ShouldRunDefaultOWEScript(u32 objectEventId) { struct ObjectEvent *object = &gObjectEvents[objectEventId]; if (!IsOverworldWildEncounter(object, OWE_ANY)) @@ -650,7 +634,7 @@ bool32 ShouldRunOverworldEncounterScript(u32 objectEventId) return TRUE; } -static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) +static bool32 CheckCuurentWildMonHeaderForOWE(bool32 shouldSpawnWaterMons) { u32 headerId = GetCurrentMapWildMonHeaderId(); enum TimeOfDay timeOfDay; @@ -682,15 +666,15 @@ static bool32 OWE_CheckActiveEncounterTable(bool32 shouldSpawnWaterMons) return gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo != NULL; } -static u32 GetOldestSlot(bool32 forceRemove) +static u32 GetOldestActiveOWESlot(bool32 forceRemove) { struct ObjectEvent *slotMon, *oldest = &gObjectEvents[GetObjectEventIdByLocalId(LOCALID_OW_ENCOUNTER_END)]; u32 spawnSlot; for (spawnSlot = 0; spawnSlot < OWE_SPAWNS_MAX; spawnSlot++) { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE && (!OWE_HasNoDespawnFlag(slotMon) || forceRemove == TRUE)) + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOWESpawnSlot(spawnSlot))]; + if (OW_SPECIES(slotMon) != SPECIES_NONE && (!HasOWENoDespawnFlag(slotMon) || forceRemove == TRUE)) { oldest = slotMon; break; @@ -702,28 +686,28 @@ static u32 GetOldestSlot(bool32 forceRemove) for (spawnSlot = 0; spawnSlot < OWE_SPAWNS_MAX; spawnSlot++) { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot))]; - if (OW_SPECIES(slotMon) != SPECIES_NONE && (!OWE_HasNoDespawnFlag(slotMon) || forceRemove == TRUE)) + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOWESpawnSlot(spawnSlot))]; + if (OW_SPECIES(slotMon) != SPECIES_NONE && (!HasOWENoDespawnFlag(slotMon) || forceRemove == TRUE)) { if (slotMon->sAge > oldest->sAge) oldest = slotMon; } } - return GetSpawnSlotByLocalId(oldest->localId); + return GetSpawnSlotByOWELocalId(oldest->localId); } -static u32 NextSpawnMonSlot(void) +static u32 GetNextOWESpawnSlot(void) { u32 spawnSlot; // All mon slots are in use - if (GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_SPAWNS_MAX) + if (GetNumberOfActiveOWEs(OWE_GENERATED) >= OWE_SPAWNS_MAX) { if (WE_OWE_SPAWN_REPLACEMENT) { // Cycle through so we remove the oldest mon first - spawnSlot = GetOldestSlot(FALSE); + spawnSlot = GetOldestActiveOWESlot(FALSE); if (spawnSlot == OWE_INVALID_SPAWN_SLOT) return OWE_INVALID_SPAWN_SLOT; } @@ -736,7 +720,7 @@ static u32 NextSpawnMonSlot(void) { for (spawnSlot = 0; spawnSlot < OWE_SPAWNS_MAX; spawnSlot++) { - if (GetOverworldSpeciesBySpawnSlot(spawnSlot) == SPECIES_NONE) + if (GetSpeciesByOWESpawnSlot(spawnSlot) == SPECIES_NONE) break; } } @@ -744,9 +728,9 @@ static u32 NextSpawnMonSlot(void) return spawnSlot; } -static u32 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) +static u32 GetSpeciesByOWESpawnSlot(u32 spawnSlot) { - u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(spawnSlot)); + u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOWESpawnSlot(spawnSlot)); struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; if (objEventId >= OBJECT_EVENTS_COUNT) @@ -755,7 +739,7 @@ static u32 GetOverworldSpeciesBySpawnSlot(u32 spawnSlot) return OW_SPECIES(objectEvent); } -static bool32 TrySelectTile(s32* outX, s32* outY) +static bool32 TrySelectTileForOWE(s32* outX, s32* outY) { u32 elevation; u32 tileBehavior; @@ -765,7 +749,7 @@ static bool32 TrySelectTile(s32* outX, s32* outY) bool32 isEncounterTile = FALSE; // Spawn further away when surfing - if (OWE_ShouldSpawnWaterMons()) + if (ShouldSpawnWaterOWE()) closeDistance = OWE_SPAWN_DISTANCE_WATER; else closeDistance = OWE_SPAWN_DISTANCE_LAND; @@ -829,10 +813,10 @@ static bool32 TrySelectTile(s32* outX, s32* outY) return FALSE; tileBehavior = MapGridGetMetatileBehaviorAt(x, y); - if (OWE_ShouldSpawnWaterMons() && MetatileBehavior_IsWaterWildEncounter(tileBehavior)) + if (ShouldSpawnWaterOWE() && MetatileBehavior_IsWaterWildEncounter(tileBehavior)) isEncounterTile = TRUE; - if (!OWE_ShouldSpawnWaterMons() && (MetatileBehavior_IsLandWildEncounter(tileBehavior) || MetatileBehavior_IsIndoorEncounter(tileBehavior))) + if (!ShouldSpawnWaterOWE() && (MetatileBehavior_IsLandWildEncounter(tileBehavior) || MetatileBehavior_IsIndoorEncounter(tileBehavior))) isEncounterTile = TRUE; if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS @@ -851,11 +835,11 @@ static bool32 TrySelectTile(s32* outX, s32* outY) return FALSE; } -static void SetOverworldEncounterSpeciesInfo(u32 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) +static void SetSpeciesInfoForOWE(u32 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) { u32 personality; - if (!OWE_CreateEnemyPartyMon(speciesId, level, indexRoamerOutbreak, x, y)) + if (!CreateEnemyPartyOWE(speciesId, level, indexRoamerOutbreak, x, y)) { ZeroEnemyPartyMons(); *speciesId = SPECIES_NONE; @@ -863,7 +847,7 @@ static void SetOverworldEncounterSpeciesInfo(u32 *speciesId, bool32 *isShiny, bo } *speciesId = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); - OWE_SetEncounterLevel(level, GetMonData(&gEnemyParty[0], MON_DATA_LEVEL)); + SetOWEEncounterLevel(level, GetMonData(&gEnemyParty[0], MON_DATA_LEVEL)); personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY); if (*speciesId == SPECIES_UNOWN) @@ -876,15 +860,15 @@ static void SetOverworldEncounterSpeciesInfo(u32 *speciesId, bool32 *isShiny, bo *isFemale = FALSE; if (WE_OWE_PREVENT_SHINY_DESPAWN && *isShiny) - OWE_SetNoDespawnFlag(level); + SetOWENoDespawnFlag(level); ZeroEnemyPartyMons(); } -static u32 GetOverworldEncounterObjectEventGraphicsId(u32 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) +static u32 GetGraphicsIdForOWE(u32 *speciesId, bool32 *isShiny, bool32 *isFemale, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y) { - SetOverworldEncounterSpeciesInfo(speciesId, isShiny, isFemale, level, indexRoamerOutbreak, x, y); - assertf(OWE_CheckSpecies(*speciesId), "invalid generated overworld encounter\nspecies: %d\ncheck if valid wild mon header exists", speciesId, x, y); + SetSpeciesInfoForOWE(speciesId, isShiny, isFemale, level, indexRoamerOutbreak, x, y); + assertf(CheckValidOWESpecies(*speciesId), "invalid generated overworld encounter\nspecies: %d\ncheck if valid wild mon header exists", speciesId, x, y); u32 graphicsId = *speciesId + OBJ_EVENT_MON; if (*isFemale) @@ -896,12 +880,12 @@ static u32 GetOverworldEncounterObjectEventGraphicsId(u32 *speciesId, bool32 *is return graphicsId; } -static bool32 CanRemoveOverworldEncounter(u32 localId) +static bool32 CheckCanRemoveOWE(u32 localId) { if (!WE_OW_ENCOUNTERS) return FALSE; - if (!GetNumberActiveOverworldEncounters(OWE_GENERATED)) + if (!GetNumberOfActiveOWEs(OWE_GENERATED)) return FALSE; if (!(localId <= (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX + 1) || localId > LOCALID_OW_ENCOUNTER_END)) @@ -910,14 +894,14 @@ static bool32 CanRemoveOverworldEncounter(u32 localId) return TRUE; } -static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y) +static bool32 CheckCanLoadOWE(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y) { - assertf(OWE_CanEncounterBeLoaded_Palette(speciesId, isFemale, isShiny, x, y), "could not load palette for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) + assertf(CheckCanLoadOWE_Palette(speciesId, isFemale, isShiny, x, y), "could not load palette for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) { return FALSE; } - assertf(OWE_CanEncounterBeLoaded_Tiles(speciesId, isFemale, isShiny, x, y), "could not load sprite tiles for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) + assertf(CheckCanLoadOWE_Tiles(speciesId, isFemale, isShiny, x, y), "could not load sprite tiles for overworld encounter\nspecies: %d\nfemale: %d\nshiny: %d\ncoords: %d %d", speciesId, isFemale, isShiny, x, y) { return FALSE; } @@ -925,7 +909,7 @@ static bool32 OWE_CanEncounterBeLoaded(u32 speciesId, bool32 isFemale, bool32 is return TRUE; } -static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y) +static bool32 CheckCanLoadOWE_Palette(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y) { u32 numFreePalSlots = CountFreePaletteSlots(); u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); @@ -940,7 +924,7 @@ static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, b if (numFreePalSlots == 1) { u32 metatileBehavior = MapGridGetMetatileBehaviorAt(x, y); - struct SpritePalette palette = OWE_GetSpawnAnimFldEffPalette(OWE_GetSpawnDespawnAnimType(metatileBehavior)); + struct SpritePalette palette = GetOWESpawnDespawnAnimFldEffPalette(GetOWESpawnDespawnAnimType(metatileBehavior)); // If the mon's palette or field effect palette isn't already loaded, don't spawn. // Include check if female or shiny mon is loaded and use that tag if possible if (IndexOfSpritePaletteTag(tag) == 0xFF && IndexOfSpritePaletteTag(palette.tag) == 0xFF) @@ -954,7 +938,7 @@ static bool32 OWE_CanEncounterBeLoaded_Palette(u32 speciesId, bool32 isFemale, b return TRUE; } #define OWE_FIELD_EFFECT_TILE_NUM 16 // Number of tiiles to add for field effect spawning -static bool32 OWE_CanEncounterBeLoaded_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y) +static bool32 CheckCanLoadOWE_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y) { u32 tag = speciesId + OBJ_EVENT_MON + (isShiny ? OBJ_EVENT_MON_SHINY : 0); // const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(speciesId, isShiny, isFemale); @@ -987,12 +971,12 @@ static bool32 OWE_CanEncounterBeLoaded_Tiles(u32 speciesId, bool32 isFemale, boo } #undef OWE_FIELD_EFFECT_TILE_NUM -static void SortOWEMonAges(void) +static void SortOWEAges(void) { struct ObjectEvent *slotMon; struct AgeSort array[OWE_SPAWNS_MAX]; struct AgeSort current; - u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); + u32 numActive = GetNumberOfActiveOWEs(OWE_GENERATED); u32 count = 0; s32 i, j; @@ -1001,7 +985,7 @@ static void SortOWEMonAges(void) for (i = 0; i < OWE_SPAWNS_MAX; i++) { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(i))]; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOWESpawnSlot(i))]; if (OW_SPECIES(slotMon) != SPECIES_NONE) { array[count].slot = i; @@ -1027,29 +1011,29 @@ static void SortOWEMonAges(void) } array[0].age = numActive; - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(array[0].slot))]; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOWESpawnSlot(array[0].slot))]; slotMon->sAge = numActive; for (i = 1; i < numActive; i++) { - slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(array[i].slot))]; + slotMon = &gObjectEvents[GetObjectEventIdByLocalId(GetLocalIdByOWESpawnSlot(array[i].slot))]; array[i].age = array[i - 1].age - 1; slotMon->sAge = array[i].age; } } -void OverworldWildEncounter_OnObjectEventSpawned(struct ObjectEvent *objectEvent) +void OnOverworldWildEncounterSpawn(struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; if (IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) - SortOWEMonAges(); + SortOWEAges(); - OWE_DoSpawnDespawnAnim(objectEvent, TRUE); + DoOWESpawnDespawnAnim(objectEvent, TRUE); } -void OverworldWildEncounter_OnObjectEventDespawned(struct ObjectEvent *objectEvent) +void OnOverworldWildEncounterDespawn(struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; @@ -1058,22 +1042,22 @@ void OverworldWildEncounter_OnObjectEventDespawned(struct ObjectEvent *objectEve objectEvent->sAge = 0; objectEvent->sRoamerOutbreakStatus = 0; - OWE_DoSpawnDespawnAnim(objectEvent, FALSE); + DoOWESpawnDespawnAnim(objectEvent, FALSE); } -bool32 OWE_IsDespawnExempt(struct ObjectEvent *objectEvent) +bool32 IsOWEDespawnExempt(struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return FALSE; - if (OWE_HasNoDespawnFlag(objectEvent) && AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) + if (HasOWENoDespawnFlag(objectEvent) && AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) return TRUE; objectEvent->offScreen = TRUE; return FALSE; } -bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent) +bool32 DespawnOWEDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(curObject, OWE_GENERATED) || IsOverworldWildEncounter(objectEvent, OWE_ANY) || objectEvent->isPlayer) return FALSE; @@ -1082,7 +1066,7 @@ bool32 OWE_DespawnMonDueToNPCCollision(struct ObjectEvent *curObject, struct Obj return TRUE; } -u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s32 x, s32 y) +u32 DespawnOWEDueToTrainerSight(u32 collision, s32 x, s32 y) { if (!(collision & (1 << (COLLISION_OBJECT_EVENT - 1)))) return collision; @@ -1095,7 +1079,7 @@ u32 OWE_DespawnMonDueToTrainerSight(u32 collision, s32 x, s32 y) return collision & (1 << (COLLISION_OBJECT_EVENT - 1)); } -void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType oweType, u32 flags) +void DespwnAllOverworldWildEncounters(enum OverworldObjectEncounterType oweType, u32 flags) { s32 dx = 0, dy = 0; @@ -1120,10 +1104,10 @@ void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType ow if (!REPEL_STEP_COUNT) continue; - if (OWE_HasNoDespawnFlag(obj)) + if (HasOWENoDespawnFlag(obj)) continue; - if (IsWildLevelAllowedByRepel(OWE_GetEncounterLevel(obj->sOverworldEncounterLevel))) + if (IsWildLevelAllowedByRepel(GetOWEEncounterLevel(obj->sOverworldEncounterLevel))) continue; } @@ -1132,12 +1116,12 @@ void DespwnAllOverworldWildEncounterObjects(enum OverworldObjectEncounterType ow } } -bool32 OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(u32 localId, u8 *objectEventId) +bool32 TryAndDespawnOldestGeneratedOWE_Object(u32 localId, u8 *objectEventId) { - // does CanRemoveOverworldEncounter need to be used in OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette - if (CanRemoveOverworldEncounter(localId)) + // does CheckCanRemoveOWE need to be used in TryAndDespawnOldestGeneratedOWE_Palette + if (CheckCanRemoveOWE(localId)) { - *objectEventId = RemoveOldestGeneratedOverworldEncounter(); + *objectEventId = RemoveOldestGeneratedOWE(); if (*objectEventId == OBJECT_EVENTS_COUNT) return TRUE; else @@ -1147,18 +1131,18 @@ bool32 OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Object(u32 localId, u8 return TRUE; } -void OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette(void) +void TryAndDespawnOldestGeneratedOWE_Palette(void) { // Should have similar naming convention for these despawn functions based on Num Object Events, Pals & Tiles if (WE_OW_ENCOUNTERS && CountFreePaletteSlots() < 2) { - u32 count = GetNumberActiveOverworldEncounters(OWE_GENERATED); + u32 count = GetNumberOfActiveOWEs(OWE_GENERATED); if (count > 0) { for (; count > 0; count--) { - RemoveOldestGeneratedOverworldEncounter(); + RemoveOldestGeneratedOWE(); if (CountFreePaletteSlots() >= 2) break; } @@ -1166,7 +1150,7 @@ void OWE_TryAndDespawnOldestGeneratedOverworldEncounter_Palette(void) } } -void OverworldWildEncounter_DespawnOnBattle(void) +void DespawnOWEOnBattleStart(void) { struct ObjectEvent *object = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; if (!IsOverworldWildEncounter(object, OWE_ANY)) @@ -1176,11 +1160,11 @@ void OverworldWildEncounter_DespawnOnBattle(void) FlagSet(GetObjectEventFlagIdByLocalIdAndMap(object->localId, object->mapNum, object->mapGroup)); RemoveObjectEvent(object); - OWE_SetNewSpawnCountdown(); + SetNewOWESpawnCountdown(); gSpecialVar_LastTalked = LOCALID_NONE; } -void OWE_TryDespawnOverworldWildEncountersCrossingMapConnection(void) +void TryDespawnOWEsCrossingMapConnection(void) { if (gMain.callback2 != CB2_Overworld) return; @@ -1194,32 +1178,32 @@ void OWE_TryDespawnOverworldWildEncountersCrossingMapConnection(void) if (WE_OWE_DESPAWN_SOUND) PlaySE(SE_FLEE); - DespwnAllOverworldWildEncounterObjects(OWE_ANY, 0); + DespwnAllOverworldWildEncounters(OWE_ANY, 0); } -static u32 RemoveOldestGeneratedOverworldEncounter(void) +static u32 RemoveOldestGeneratedOWE(void) { - u32 oldestSlot = GetOldestSlot(TRUE); + u32 oldestSlot = GetOldestActiveOWESlot(TRUE); if (oldestSlot == OWE_INVALID_SPAWN_SLOT) return OBJECT_EVENTS_COUNT; - u32 objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOverworldSpawnSlot(oldestSlot)); + u32 objectEventId = GetObjectEventIdByLocalId(GetLocalIdByOWESpawnSlot(oldestSlot)); RemoveObjectEvent(&gObjectEvents[objectEventId]); return objectEventId; } -static bool32 OWE_ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) +static bool32 ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) { if (!IsOverworldWildEncounter(object, OWE_GENERATED)) return FALSE; - return WE_OWE_SPAWN_REPLACEMENT && GetNumberActiveOverworldEncounters(OWE_GENERATED) >= OWE_SPAWNS_MAX; + return WE_OWE_SPAWN_REPLACEMENT && GetNumberOfActiveOWEs(OWE_GENERATED) >= OWE_SPAWNS_MAX; } -static void OWE_SetNewSpawnCountdown(void) +static void SetNewOWESpawnCountdown(void) { - u32 numActive = GetNumberActiveOverworldEncounters(OWE_GENERATED); + u32 numActive = GetNumberOfActiveOWEs(OWE_GENERATED); if (WE_OWE_SPAWN_REPLACEMENT && numActive >= OWE_SPAWNS_MAX) sOWESpawnCountdown = OWE_SPAWN_TIME_REPLACEMENT; @@ -1229,7 +1213,7 @@ static void OWE_SetNewSpawnCountdown(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); } -static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn) +static void DoOWESpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn) { if (gMain.callback2 != CB2_Overworld) return; @@ -1238,9 +1222,9 @@ static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animS bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; if (animSpawn) - OWE_PlayMonObjectCry(objectEvent); + PlayOWECry(objectEvent); - if (!animSpawn && OWE_ShouldPlayMonFleeSound(objectEvent)) + if (!animSpawn && OWE_ShouldPlayOWEFleeSound(objectEvent)) PlaySE(SE_FLEE); if (WE_OWE_SHINY_SPARKLE && isShiny && animSpawn) @@ -1251,12 +1235,12 @@ static void OWE_DoSpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animS else { u32 metatileBehavior = MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x, objectEvent->currentCoords.y); - spawnAnimType = OWE_GetSpawnDespawnAnimType(metatileBehavior); + spawnAnimType = GetOWESpawnDespawnAnimType(metatileBehavior); } MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); } -static enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatileBehavior) +static enum OverworldEncounterSpawnAnim GetOWESpawnDespawnAnimType(u32 metatileBehavior) { if (MetatileBehavior_IsPokeGrass(metatileBehavior) || MetatileBehavior_IsAshGrass(metatileBehavior)) return OWE_SPAWN_ANIM_GRASS; @@ -1270,7 +1254,7 @@ static enum OverworldEncounterSpawnAnim OWE_GetSpawnDespawnAnimType(u32 metatile return OWE_SPAWN_ANIM_CAVE; } -static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) +static void PlayOWECry(struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; @@ -1303,9 +1287,9 @@ static void OWE_PlayMonObjectCry(struct ObjectEvent *objectEvent) PlayCry_NormalNoDucking(speciesId, pan, volume, CRY_PRIORITY_AMBIENT); } -static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) +static struct ObjectEvent *GetOWEObjectEvent(void) { - u32 numActive = GetNumberActiveOverworldEncounters(OWE_ANY); + u32 numActive = GetNumberOfActiveOWEs(OWE_ANY); u32 randomIndex; u32 counter = 0; struct ObjectEvent *object; @@ -1329,7 +1313,7 @@ static struct ObjectEvent *OWE_GetRandomActiveEncounterObject(void) return NULL; } -static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) +static bool32 OWE_ShouldPlayOWEFleeSound(struct ObjectEvent *objectEvent) { if (!IsOverworldWildEncounter(objectEvent, OWE_ANY) || OW_SPECIES(objectEvent) == SPECIES_NONE) return FALSE; @@ -1337,7 +1321,7 @@ static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) if (!AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) return FALSE; - if (OWE_ShouldDespawnGeneratedForNewOWE(objectEvent)) + if (ShouldDespawnGeneratedForNewOWE(objectEvent)) return FALSE; if (objectEvent->offScreen) @@ -1347,20 +1331,20 @@ static bool32 OWE_ShouldPlayMonFleeSound(struct ObjectEvent *objectEvent) } #define sTypeFuncId data[1] // Same as in src/event_object_movement.c -void OWE_RestoreBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite) +void RestoreSavedOWEBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - if (IsOverworldWildEncounter(objectEvent, OWE_ANY) && OWE_HasSavedMovementState(objectEvent)) + if (IsOverworldWildEncounter(objectEvent, OWE_ANY) && HasSavedOWEMovementState(objectEvent)) sprite->sTypeFuncId = OWE_RESTORED_MOVEMENT_FUNC_ID; } #undef sTypeFuncId // Returns TRUE if movement is restricted. -bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Direction direction) +bool32 CheckRestrictedOWEMovement(struct ObjectEvent *objectEvent, enum Direction direction) { if (GetCollisionInDirection(objectEvent, direction)) return TRUE; - if (OWE_CanAwareMonSeePlayer(objectEvent) && WE_OWE_UNRESTRICT_SIGHT) + if (CanAwareOWESeePlayer(objectEvent) && WE_OWE_UNRESTRICT_SIGHT) return FALSE; s32 xCurrent = objectEvent->currentCoords.x; @@ -1368,21 +1352,21 @@ bool32 OWE_CheckRestrictedMovement(struct ObjectEvent *objectEvent, enum Directi s32 xNew = xCurrent + gDirectionToVectors[direction].x; s32 yNew = yCurrent + gDirectionToVectors[direction].y; - if (OWE_CheckRestrictMovementMetatile(xCurrent, yCurrent, xNew, yNew)) + if (CheckRestrictedOWEMovementMetatile(xCurrent, yCurrent, xNew, yNew)) return TRUE; - if (OWE_CheckRestrictMovementMap(objectEvent, xNew, yNew)) + if (CheckRestrictedOWEMovementMap(objectEvent, xNew, yNew)) return TRUE; return FALSE; } -static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s32 xNew, s32 yNew, enum Direction newDirection, enum Direction collisionDirection) +static bool32 CheckRestrictedOWEMovementAtCoords(struct ObjectEvent *mon, s32 xNew, s32 yNew, enum Direction newDirection, enum Direction collisionDirection) { - if (OWE_CheckRestrictMovementMetatile(mon->currentCoords.x, mon->currentCoords.y, xNew, yNew)) + if (CheckRestrictedOWEMovementMetatile(mon->currentCoords.x, mon->currentCoords.y, xNew, yNew)) return FALSE; - if (OWE_CheckRestrictMovementMap(mon, xNew, yNew)) + if (CheckRestrictedOWEMovementMap(mon, xNew, yNew)) return FALSE; if (GetCollisionAtCoords(mon, xNew, yNew, collisionDirection)) @@ -1391,7 +1375,7 @@ static bool32 OWE_CheckRestrictedMovementAtCoords(struct ObjectEvent *mon, s32 x return TRUE; } -static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew) +static bool32 CheckRestrictedOWEMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew) { if (!WE_OWE_RESTRICT_METATILE) return FALSE; @@ -1418,7 +1402,7 @@ static bool32 OWE_CheckRestrictMovementMetatile(s32 xCurrent, s32 yCurrent, s32 return TRUE; } -static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew) +static bool32 CheckRestrictedOWEMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew) { if (!WE_OWE_RESTRICT_MAP) return FALSE; @@ -1430,12 +1414,12 @@ static bool32 OWE_CheckRestrictMovementMap(struct ObjectEvent *objectEvent, s32 return AreCoordsInsidePlayerMap(xNew, yNew); } -bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon) +bool32 CanAwareOWESeePlayer(struct ObjectEvent *mon) { if (!IsOverworldWildEncounter(mon, OWE_ANY) || mon->movementType == MOVEMENT_TYPE_WANDER_AROUND_OWE) return FALSE; - if (OWE_IsPlayerInsideMonActiveDistance(mon) && (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) + if (IsPlayerInsideOWEActiveDistance(mon) && (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING))) return TRUE; @@ -1486,13 +1470,13 @@ bool32 OWE_CanAwareMonSeePlayer(struct ObjectEvent *mon) break; } - if (retVal && OWE_IsLineOfSightClear(player, GetOppositeDirection(direction), viewDistance)) + if (retVal && IsOWELineOfSightClear(player, GetOppositeDirection(direction), viewDistance)) return TRUE; return FALSE; } -static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance) +static bool32 IsOWELineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance) { s16 x = player->currentCoords.x; s16 y = player->currentCoords.y; @@ -1511,7 +1495,7 @@ static bool32 OWE_IsLineOfSightClear(struct ObjectEvent *player, enum Direction return TRUE; } -bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon) +bool32 IsPlayerInsideOWEActiveDistance(struct ObjectEvent *mon) { if (!IsOverworldWildEncounter(mon, OWE_ANY)) return FALSE; @@ -1530,7 +1514,7 @@ bool32 OWE_IsPlayerInsideMonActiveDistance(struct ObjectEvent *mon) return FALSE; } -bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon) +bool32 IsOWENextToPlayer(struct ObjectEvent *mon) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; @@ -1540,7 +1524,7 @@ bool32 OWE_IsMonNextToPlayer(struct ObjectEvent *mon) return TRUE; } -u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) +enum Direction DirectionOfOWEToPlayerFromCollision(struct ObjectEvent *mon) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; @@ -1551,7 +1535,7 @@ u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) if (player->currentCoords.x < mon->currentCoords.x) return DIR_WEST; else if (player->currentCoords.x == mon->currentCoords.x) - return OWE_CheckPathToPlayerFromCollision(mon, (Random() % 2) == 0 ? DIR_EAST : DIR_WEST); + return CheckOWEPathToPlayerFromCollision(mon, (Random() % 2) == 0 ? DIR_EAST : DIR_WEST); else return DIR_EAST; case DIR_EAST: @@ -1559,7 +1543,7 @@ u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) if (player->currentCoords.y < mon->currentCoords.y) return DIR_NORTH; else if (player->currentCoords.y == mon->currentCoords.y) - return OWE_CheckPathToPlayerFromCollision(mon, (Random() % 2) == 0 ? DIR_NORTH : DIR_SOUTH); + return CheckOWEPathToPlayerFromCollision(mon, (Random() % 2) == 0 ? DIR_NORTH : DIR_SOUTH); else return DIR_SOUTH; } @@ -1567,7 +1551,7 @@ u32 OWE_DirectionToPlayerFromCollision(struct ObjectEvent *mon) return mon->movementDirection; } -u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances) +u32 GetApproachingOWEDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; s32 absX, absY; @@ -1593,7 +1577,7 @@ u32 OWE_GetApproachingMonDistanceToPlayer(struct ObjectEvent *mon, bool32 *equal return absX; } -u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed) +u32 GetOWEWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed) { switch (speed) { @@ -1608,32 +1592,32 @@ u32 OWE_GetWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 return GetWalkNormalMovementAction(direction); } -static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection) +static enum Direction CheckOWEPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection) { s16 x = mon->currentCoords.x; s16 y = mon->currentCoords.y; MoveCoords(newDirection, &x, &y); - if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, newDirection)) + if (CheckRestrictedOWEMovementAtCoords(mon, x, y, newDirection, newDirection)) { if (mon->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) return GetOppositeDirection(newDirection); MoveCoords(mon->movementDirection, &x, &y); - if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, mon->movementDirection)) + if (CheckRestrictedOWEMovementAtCoords(mon, x, y, newDirection, mon->movementDirection)) return newDirection; } x = mon->currentCoords.x; y = mon->currentCoords.y; MoveCoords(GetOppositeDirection(newDirection), &x, &y); - if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, newDirection)) + if (CheckRestrictedOWEMovementAtCoords(mon, x, y, newDirection, newDirection)) { if (mon->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) return newDirection; MoveCoords(mon->movementDirection, &x, &y); - if (OWE_CheckRestrictedMovementAtCoords(mon, x, y, newDirection, mon->movementDirection)) + if (CheckRestrictedOWEMovementAtCoords(mon, x, y, newDirection, mon->movementDirection)) return GetOppositeDirection(newDirection); } @@ -1641,7 +1625,7 @@ static u32 OWE_CheckPathToPlayerFromCollision(struct ObjectEvent *mon, enum Dire } #define tObjectId data[0] -void OWE_ApproachForBattle(void) +void OWEApproachForBattle(void) { u32 objectEventId = GetObjectEventIdByLocalId(gSpecialVar_LastTalked); struct ObjectEvent *objectEvent = &gObjectEvents[objectEventId]; @@ -1654,8 +1638,8 @@ void OWE_ApproachForBattle(void) if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) return; - u32 taskId = CreateTask(Task_OWE_ApproachForBattle, 2); - if (FindTaskIdByFunc(Task_OWE_ApproachForBattle) == TASK_NONE) + u32 taskId = CreateTask(Task_OWEApproachForBattle, 2); + if (FindTaskIdByFunc(Task_OWEApproachForBattle) == TASK_NONE) { FreezeObjectEvent(objectEvent); return; @@ -1665,7 +1649,7 @@ void OWE_ApproachForBattle(void) gTasks[taskId].tObjectId = objectEventId; } -static void Task_OWE_ApproachForBattle(u8 taskId) +static void Task_OWEApproachForBattle(u8 taskId) { struct ObjectEvent *OWE = &gObjectEvents[gTasks[taskId].tObjectId]; @@ -1673,7 +1657,7 @@ static void Task_OWE_ApproachForBattle(u8 taskId) if (ObjectEventClearHeldMovementIfFinished(OWE)) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - if (OWE_IsMonNextToPlayer(OWE)) + if (IsOWENextToPlayer(OWE)) { ObjectEventsTurnToEachOther(player, OWE); ScriptContext_Enable(); @@ -1686,9 +1670,9 @@ static void Task_OWE_ApproachForBattle(u8 taskId) u32 movementActionId; SetObjectEventDirection(OWE, direction); - movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(OWE->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(OWE->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); - if (OWE_CheckRestrictedMovement(OWE, OWE->movementDirection)) + if (CheckRestrictedOWEMovement(OWE, OWE->movementDirection)) { struct ObjectEvent *followerMon = GetFollowerObject(); u32 idFollowerNPC = GetFollowerNPCObjectId(); @@ -1720,9 +1704,9 @@ static void Task_OWE_ApproachForBattle(u8 taskId) } else { - direction = OWE_DirectionToPlayerFromCollision(OWE); + direction = DirectionOfOWEToPlayerFromCollision(OWE); SetObjectEventDirection(OWE, direction); - movementActionId = OWE_GetWalkMovementActionInDirectionWithSpeed(OWE->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(OWE->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); } } ObjectEventSetHeldMovement(OWE, movementActionId); @@ -1731,12 +1715,12 @@ static void Task_OWE_ApproachForBattle(u8 taskId) } #undef tObjectId -void OWE_PlayAmbientCry(void) +void PlayAmbientOWECry(void) { - OWE_PlayMonObjectCry(OWE_GetRandomActiveEncounterObject()); + PlayOWECry(GetOWEObjectEvent()); } -u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType) +u32 GetNumberOfActiveOWEs(enum OverworldObjectEncounterType oweType) { u32 numActive = 0; for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) @@ -1747,7 +1731,7 @@ u32 GetNumberActiveOverworldEncounters(enum OverworldObjectEncounterType oweType return numActive; } -const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter(const struct ObjectEventTemplate *template) +const struct ObjectEventTemplate TryGetObjectEventTemplateForOWE(const struct ObjectEventTemplate *template) { if (template->trainerType != TRAINER_TYPE_OW_WILD_ENCOUNTER || (template->localId <= LOCALID_OW_ENCOUNTER_END && template->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX))) @@ -1764,15 +1748,15 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( u32 x = template->x; u32 y = template->y; - SetOverworldEncounterSpeciesInfo(&speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak, x, y); + SetSpeciesInfoForOWE(&speciesId, &isShiny, &isFemale, &level, &indexRoamerOutbreak, x, y); if (speciesTemplate) speciesId = speciesTemplate; if (levelTemplate) level = levelTemplate; - bool32 validSpecies = OWE_CheckSpecies(speciesId); - bool32 validLevel = OWE_GetEncounterLevel(level) >= MIN_LEVEL && OWE_GetEncounterLevel(level) <= MAX_LEVEL; + bool32 validSpecies = CheckValidOWESpecies(speciesId); + bool32 validLevel = GetOWEEncounterLevel(level) >= MIN_LEVEL && GetOWEEncounterLevel(level) <= MAX_LEVEL; assertf(validSpecies && validLevel, "invalid manual overworld encounter\nspecies: %d\nlevel: %d\nx: %d y: %d\ncheck if valid wild mon header exists", speciesId, level, x, y) { if (!validSpecies) @@ -1815,7 +1799,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOverworldEncounter( return templateOWE; } -struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim) +struct SpritePalette GetOWESpawnDespawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim) { struct SpritePalette palette = gSpritePalette_GeneralFieldEffect0; switch (spawnAnim) @@ -1836,7 +1820,7 @@ struct SpritePalette OWE_GetSpawnAnimFldEffPalette(enum OverworldEncounterSpawnA return palette; } -static bool32 OWE_CheckSpecies(u32 speciesId) +static bool32 CheckValidOWESpecies(u32 speciesId) { if (speciesId == SPECIES_NONE) return FALSE; From 382255244bfb70da8e3d0522e9bf4ef7ac0e0fc4 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:11:28 +0000 Subject: [PATCH 566/572] Rename --- .../how_to_overworld_wild_encounters.md | 6 ++--- include/event_object_movement.h | 2 +- include/pokemon.h | 6 ++--- include/wild_encounter_ow.h | 22 +++++++++---------- src/data/pokemon/wild_encounter_ow_behavior.h | 2 +- src/event_object_movement.c | 2 +- src/field_effect_helpers.c | 2 +- src/pokemon.c | 16 +++++++------- src/wild_encounter_ow.c | 14 ++++++------ 9 files changed, 36 insertions(+), 36 deletions(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 0b95b7b7b853..713d67b5ea3c 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -69,12 +69,12 @@ The behaviors themselves are defined in `src/data/pokemon/wild_encounter_overwor - `viewDistance` is the number of tiles away the mon is able to notice the player in the cardinal directions (similar to the sight distance of trainers). - `viewWidth` is the total width of the area in which the mon will notice the player. For example, if `viewWidth` is set to `3`, the mon will be able to detect the player if they are within 1 tile of either side of the line of sight. - `activeDistance` is the max distance away from the mon that the player can be before the mon loses track of them and goes back to wandering. -- `idleSpeed` is the speed at which the mon will take a step while wandering (player is not noticed). This must be one of the values in `enum OWESpeeds`. -- `activeSpeed` is the speed at which the mon will take a step while active (player has been noticed). This must be one of the values in `enum OWESpeeds`. +- `idleSpeed` is the speed at which the mon will take a step while wandering (player is not noticed). This must be one of the values in `enum SpeedOWE`. +- `activeSpeed` is the speed at which the mon will take a step while active (player has been noticed). This must be one of the values in `enum SpeedOWE`. If any of these parameters are not defined, they will be automatically assigned the value of `0`. -A small number of premade behaviors have been provided and are ready to use. You can add as many new custom behaviors as you like, but make sure to add each behavior to `enum OverworldEncounterBehaviors`, making sure that `OWE_SPECIES_BEHAVIOR_COUNT` is always at the end. +A small number of premade behaviors have been provided and are ready to use. You can add as many new custom behaviors as you like, but make sure to add each behavior to `enum OverworldWildEncounterBehaviors`, making sure that `OWE_SPECIES_BEHAVIOR_COUNT` is always at the end. The same behavior can be used for multiple different species. diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 1e45743d52a7..43edb6935208 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -534,7 +534,7 @@ bool8 PlayerIsUnderWaterfall(struct ObjectEvent *objectEvent); u8 GetObjectEventApricornTreeId(u8 objectEventId); // Overworld Wild Encounter -bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent); +bool8 MovementAction_OverworldEncounterSpawn(enum SpawnDespawnTypeOWE spawnAnimType, struct ObjectEvent *objEvent); void MovementType_OverworldWildEncounter_WanderAround(struct Sprite *sprite); void MovementType_OverworldWildEncounter_ChasePlayer(struct Sprite *sprite); diff --git a/include/pokemon.h b/include/pokemon.h index ad259b62a6dd..ea841fe24367 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -518,7 +518,7 @@ struct SpeciesInfo /*0xC4*/ #endif //P_GENDER_DIFFERENCES #endif //OW_PKMN_OBJECTS_SHARE_PALETTES #endif //OW_POKEMON_OBJECT_EVENTS - enum OverworldEncounterBehaviors overworldEncounterBehavior; + enum OverworldWildEncounterBehaviors overworldEncounterBehavior; }; struct EggData @@ -928,7 +928,7 @@ u32 OWE_GetMovementTypeFromSpecies(u32 speciesId); u32 OWE_GetViewDistanceFromSpecies(u32 speciesId); u32 OWE_GetViewWidthFromSpecies(u32 speciesId); u32 OWE_GetViewActiveDistanceFromSpecies(u32 speciesId); -enum OWESpeeds OWE_GetIdleSpeedFromSpecies(u32 speciesId); -enum OWESpeeds OWE_GetActiveSpeedFromSpecies(u32 speciesId); +enum SpeedOWE OWE_GetIdleSpeedFromSpecies(u32 speciesId); +enum SpeedOWE OWE_GetActiveSpeedFromSpecies(u32 speciesId); #endif // GUARD_POKEMON_H diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index 40ef9f460f81..05f4e3ad6df2 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -7,7 +7,7 @@ #define OWE_FLEE_COLLISION_TIME 6 // If a fleeing mon is unable to take a step for this many tries it will despawn. (Multiply this value by 16 to get number of frames.) #define OWE_DESPAWN_FRAMES 30 // Number of frames before a mon despawns after noticing the player (OWE_BEHAVIOR_DESPAWN) -enum OverworldEncounterSpawnAnim +enum SpawnDespawnTypeOWE { OWE_SPAWN_ANIM_GRASS, OWE_SPAWN_ANIM_LONG_GRASS, @@ -17,7 +17,7 @@ enum OverworldEncounterSpawnAnim OWE_SPAWN_ANIM_SHINY }; -enum OverworldObjectEncounterType +enum TypeOWE { OWE_ANY, OWE_GENERATED, @@ -25,7 +25,7 @@ enum OverworldObjectEncounterType }; // OWE_SPEED_FASTER seems to visually bug out sometimes. -enum OWESpeeds +enum SpeedOWE { OWE_SPEED_NORMAL, OWE_SPEED_SLOW, @@ -33,18 +33,18 @@ enum OWESpeeds OWE_SPEED_FASTER }; -struct OWESpeciesBehavior +struct BehaviorOWE { u32 movementType:8; u32 viewDistance:4; u32 viewWidth:4; u32 activeDistance:4; u32 padding:12; - enum OWESpeeds idleSpeed; - enum OWESpeeds activeSpeed; + enum SpeedOWE idleSpeed; + enum SpeedOWE activeSpeed; }; -enum __attribute__((packed)) OverworldEncounterBehaviors +enum __attribute__((packed)) OverworldWildEncounterBehaviors { OWE_IGNORE_PLAYER, OWE_CHASE_PLAYER_SLOW, @@ -56,7 +56,7 @@ enum __attribute__((packed)) OverworldEncounterBehaviors }; void OverworldWildEncounters_CB(void); -bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType); +bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum TypeOWE oweType); void StartWildBattleWithOWE(void); void SetInstantOWESpawnTimer(void); void SetMinimumOWESpawnTimer(void); @@ -67,7 +67,7 @@ void OnOverworldWildEncounterDespawn(struct ObjectEvent *objectEvent); bool32 IsOWEDespawnExempt(struct ObjectEvent *objectEvent); bool32 DespawnOWEDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); u32 DespawnOWEDueToTrainerSight(u32 collision, s32 x, s32 y); -void DespwnAllOverworldWildEncounters(enum OverworldObjectEncounterType oweType, u32 flags); +void DespwnAllOverworldWildEncounters(enum TypeOWE oweType, u32 flags); bool32 TryAndDespawnOldestGeneratedOWE_Object(u32 localId, u8 *objectEventId); void TryAndDespawnOldestGeneratedOWE_Palette(void); void DespawnOWEOnBattleStart(void); @@ -84,9 +84,9 @@ u32 GetApproachingOWEDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDist u32 GetOWEWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed); void OWEApproachForBattle(void); void PlayAmbientOWECry(void); -u32 GetNumberOfActiveOWEs(enum OverworldObjectEncounterType oweType); +u32 GetNumberOfActiveOWEs(enum TypeOWE oweType); const struct ObjectEventTemplate TryGetObjectEventTemplateForOWE(const struct ObjectEventTemplate *template); -struct SpritePalette GetOWESpawnDespawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim); +struct SpritePalette GetOWESpawnDespawnAnimFldEffPalette(enum SpawnDespawnTypeOWE spawnAnim); extern const u8 InteractWithOverworldWildEncounter[]; diff --git a/src/data/pokemon/wild_encounter_ow_behavior.h b/src/data/pokemon/wild_encounter_ow_behavior.h index f98c1d9438ff..ea2bc0de2cdf 100644 --- a/src/data/pokemon/wild_encounter_ow_behavior.h +++ b/src/data/pokemon/wild_encounter_ow_behavior.h @@ -4,7 +4,7 @@ #include "wild_encounter_ow.h" #include "constants/event_object_movement.h" -static const struct OWESpeciesBehavior sOWESpeciesBehavior[OWE_SPECIES_BEHAVIOR_COUNT] = +static const struct BehaviorOWE sOWESpeciesBehavior[OWE_SPECIES_BEHAVIOR_COUNT] = { [OWE_IGNORE_PLAYER] = { diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 9a5a8545c624..7e4651108001 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11891,7 +11891,7 @@ bool8 MovementAction_SpinRight_Step1(struct ObjectEvent *objectEvent, struct Spr return FALSE; } -bool8 MovementAction_OverworldEncounterSpawn(enum OverworldEncounterSpawnAnim spawnAnimType, struct ObjectEvent *objEvent) +bool8 MovementAction_OverworldEncounterSpawn(enum SpawnDespawnTypeOWE spawnAnimType, struct ObjectEvent *objEvent) { gFieldEffectArguments[0] = objEvent->currentCoords.x; gFieldEffectArguments[1] = objEvent->currentCoords.y; diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 0804482bd8ed..ff11e80dcf10 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1913,7 +1913,7 @@ u32 FldEff_OWE_SpawnAnim(void) u8 spriteId; u8 visual; s16 xOffset = 0, yOffset = 0; - enum OverworldEncounterSpawnAnim spawnAnim = gFieldEffectArguments[2]; + enum SpawnDespawnTypeOWE spawnAnim = gFieldEffectArguments[2]; struct SpritePalette palette = GetOWESpawnDespawnAnimFldEffPalette(spawnAnim); switch (spawnAnim) diff --git a/src/pokemon.c b/src/pokemon.c index a3b82bee93aa..810bd223a289 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -7268,41 +7268,41 @@ void ChangePokemonNicknameWithCallback(void (*callback)(void)) u32 OWE_GetMovementTypeFromSpecies(u32 speciesId) { speciesId = SanitizeSpeciesId(speciesId); - enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; + enum OverworldWildEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; return sOWESpeciesBehavior[behavior].movementType; } u32 OWE_GetViewDistanceFromSpecies(u32 speciesId) { speciesId = SanitizeSpeciesId(speciesId); - enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; + enum OverworldWildEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; return sOWESpeciesBehavior[behavior].viewDistance; } u32 OWE_GetViewWidthFromSpecies(u32 speciesId) { speciesId = SanitizeSpeciesId(speciesId); - enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; + enum OverworldWildEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; return sOWESpeciesBehavior[behavior].viewWidth; } u32 OWE_GetViewActiveDistanceFromSpecies(u32 speciesId) { speciesId = SanitizeSpeciesId(speciesId); - enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; + enum OverworldWildEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; return sOWESpeciesBehavior[behavior].activeDistance; } -enum OWESpeeds OWE_GetIdleSpeedFromSpecies(u32 speciesId) +enum SpeedOWE OWE_GetIdleSpeedFromSpecies(u32 speciesId) { speciesId = SanitizeSpeciesId(speciesId); - enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; + enum OverworldWildEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; return sOWESpeciesBehavior[behavior].idleSpeed; } -enum OWESpeeds OWE_GetActiveSpeedFromSpecies(u32 speciesId) +enum SpeedOWE OWE_GetActiveSpeedFromSpecies(u32 speciesId) { speciesId = SanitizeSpeciesId(speciesId); - enum OverworldEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; + enum OverworldWildEncounterBehaviors behavior = gSpeciesInfo[speciesId].overworldEncounterBehavior; return sOWESpeciesBehavior[behavior].activeSpeed; } diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index af4cc178af6e..77b1892273fa 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -155,7 +155,7 @@ static u32 RemoveOldestGeneratedOWE(void); static bool32 ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); static void SetNewOWESpawnCountdown(void); static void DoOWESpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); -static enum OverworldEncounterSpawnAnim GetOWESpawnDespawnAnimType(u32 metatileBehavior); +static enum SpawnDespawnTypeOWE GetOWESpawnDespawnAnimType(u32 metatileBehavior); static void PlayOWECry(struct ObjectEvent *objectEvent); static struct ObjectEvent *GetOWEObjectEvent(void); static bool32 OWE_ShouldPlayOWEFleeSound(struct ObjectEvent *objectEvent); @@ -273,7 +273,7 @@ void OverworldWildEncounters_CB(void) SetNewOWESpawnCountdown(); } -bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum OverworldObjectEncounterType oweType) +bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum TypeOWE oweType) { if (!IS_OW_MON_OBJ(objectEvent)) return FALSE; @@ -1079,7 +1079,7 @@ u32 DespawnOWEDueToTrainerSight(u32 collision, s32 x, s32 y) return collision & (1 << (COLLISION_OBJECT_EVENT - 1)); } -void DespwnAllOverworldWildEncounters(enum OverworldObjectEncounterType oweType, u32 flags) +void DespwnAllOverworldWildEncounters(enum TypeOWE oweType, u32 flags) { s32 dx = 0, dy = 0; @@ -1218,7 +1218,7 @@ static void DoOWESpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSp if (gMain.callback2 != CB2_Overworld) return; - enum OverworldEncounterSpawnAnim spawnAnimType; + enum SpawnDespawnTypeOWE spawnAnimType; bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; if (animSpawn) @@ -1240,7 +1240,7 @@ static void DoOWESpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSp MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); } -static enum OverworldEncounterSpawnAnim GetOWESpawnDespawnAnimType(u32 metatileBehavior) +static enum SpawnDespawnTypeOWE GetOWESpawnDespawnAnimType(u32 metatileBehavior) { if (MetatileBehavior_IsPokeGrass(metatileBehavior) || MetatileBehavior_IsAshGrass(metatileBehavior)) return OWE_SPAWN_ANIM_GRASS; @@ -1720,7 +1720,7 @@ void PlayAmbientOWECry(void) PlayOWECry(GetOWEObjectEvent()); } -u32 GetNumberOfActiveOWEs(enum OverworldObjectEncounterType oweType) +u32 GetNumberOfActiveOWEs(enum TypeOWE oweType) { u32 numActive = 0; for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) @@ -1799,7 +1799,7 @@ const struct ObjectEventTemplate TryGetObjectEventTemplateForOWE(const struct Ob return templateOWE; } -struct SpritePalette GetOWESpawnDespawnAnimFldEffPalette(enum OverworldEncounterSpawnAnim spawnAnim) +struct SpritePalette GetOWESpawnDespawnAnimFldEffPalette(enum SpawnDespawnTypeOWE spawnAnim) { struct SpritePalette palette = gSpritePalette_GeneralFieldEffect0; switch (spawnAnim) From 106bc1c663b81cd5f6279412568dbba3b1c07957 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:16:25 +0000 Subject: [PATCH 567/572] Standardise ObjectEvent Name --- include/wild_encounter_ow.h | 28 +-- src/wild_encounter_ow.c | 362 ++++++++++++++++++------------------ 2 files changed, 195 insertions(+), 195 deletions(-) diff --git a/include/wild_encounter_ow.h b/include/wild_encounter_ow.h index 05f4e3ad6df2..965ca66495ad 100644 --- a/include/wild_encounter_ow.h +++ b/include/wild_encounter_ow.h @@ -56,31 +56,31 @@ enum __attribute__((packed)) OverworldWildEncounterBehaviors }; void OverworldWildEncounters_CB(void); -bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum TypeOWE oweType); +bool32 IsOverworldWildEncounter(struct ObjectEvent *owe, enum TypeOWE oweType); void StartWildBattleWithOWE(void); void SetInstantOWESpawnTimer(void); void SetMinimumOWESpawnTimer(void); void TryTriggerOverworldWilEncounter(struct ObjectEvent *obstacle, struct ObjectEvent *collider); bool32 ShouldRunDefaultOWEScript(u32 objectEventId); -void OnOverworldWildEncounterSpawn(struct ObjectEvent *objectEvent); -void OnOverworldWildEncounterDespawn(struct ObjectEvent *objectEvent); -bool32 IsOWEDespawnExempt(struct ObjectEvent *objectEvent); -bool32 DespawnOWEDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent); +void OnOverworldWildEncounterSpawn(struct ObjectEvent *owe); +void OnOverworldWildEncounterDespawn(struct ObjectEvent *owe); +bool32 IsOWEDespawnExempt(struct ObjectEvent *owe); +bool32 DespawnOWEDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *owe); u32 DespawnOWEDueToTrainerSight(u32 collision, s32 x, s32 y); void DespwnAllOverworldWildEncounters(enum TypeOWE oweType, u32 flags); bool32 TryAndDespawnOldestGeneratedOWE_Object(u32 localId, u8 *objectEventId); void TryAndDespawnOldestGeneratedOWE_Palette(void); void DespawnOWEOnBattleStart(void); void TryDespawnOWEsCrossingMapConnection(void); -void RestoreSavedOWEBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite); -void SetSavedOWEMovementState(struct ObjectEvent *objectEvent); -void ClearSavedOWEMovementState(struct ObjectEvent *objectEvent); -bool32 CheckRestrictedOWEMovement(struct ObjectEvent *objectEvent, enum Direction direction); -bool32 CanAwareOWESeePlayer(struct ObjectEvent *mon); -bool32 IsPlayerInsideOWEActiveDistance(struct ObjectEvent *mon); -bool32 IsOWENextToPlayer(struct ObjectEvent *mon); -enum Direction DirectionOfOWEToPlayerFromCollision(struct ObjectEvent *mon); -u32 GetApproachingOWEDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances); +void RestoreSavedOWEBehaviorState(struct ObjectEvent *owe, struct Sprite *sprite); +void SetSavedOWEMovementState(struct ObjectEvent *owe); +void ClearSavedOWEMovementState(struct ObjectEvent *owe); +bool32 CheckRestrictedOWEMovement(struct ObjectEvent *owe, enum Direction direction); +bool32 CanAwareOWESeePlayer(struct ObjectEvent *owe); +bool32 IsPlayerInsideOWEActiveDistance(struct ObjectEvent *owe); +bool32 IsOWENextToPlayer(struct ObjectEvent *owe); +enum Direction DirectionOfOWEToPlayerFromCollision(struct ObjectEvent *owe); +u32 GetApproachingOWEDistanceToPlayer(struct ObjectEvent *owe, bool32 *equalDistances); u32 GetOWEWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 speed); void OWEApproachForBattle(void); void PlayAmbientOWECry(void); diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 77b1892273fa..37578da6a854 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -86,24 +86,24 @@ static inline u32 GetSpawnSlotByOWELocalId(u32 localId) return LOCALID_OW_ENCOUNTER_END - localId; } -static inline u32 GetOWERoamerIndex(const struct ObjectEvent *object) +static inline u32 GetOWERoamerIndex(const struct ObjectEvent *owe) { - return object->sRoamerOutbreakStatus & ~OWE_SAVED_MOVEMENT_STATE_FLAG; + return owe->sRoamerOutbreakStatus & ~OWE_SAVED_MOVEMENT_STATE_FLAG; } -static inline bool32 HasSavedOWEMovementState(const struct ObjectEvent *object) +static inline bool32 HasSavedOWEMovementState(const struct ObjectEvent *owe) { - return object->sRoamerOutbreakStatus & OWE_SAVED_MOVEMENT_STATE_FLAG; + return owe->sRoamerOutbreakStatus & OWE_SAVED_MOVEMENT_STATE_FLAG; } -void SetSavedOWEMovementState(struct ObjectEvent *objectEvent) +void SetSavedOWEMovementState(struct ObjectEvent *owe) { - objectEvent->sRoamerOutbreakStatus |= OWE_SAVED_MOVEMENT_STATE_FLAG; + owe->sRoamerOutbreakStatus |= OWE_SAVED_MOVEMENT_STATE_FLAG; } -void ClearSavedOWEMovementState(struct ObjectEvent *objectEvent) +void ClearSavedOWEMovementState(struct ObjectEvent *owe) { - objectEvent->sRoamerOutbreakStatus &= ~OWE_SAVED_MOVEMENT_STATE_FLAG; + owe->sRoamerOutbreakStatus &= ~OWE_SAVED_MOVEMENT_STATE_FLAG; } static inline u32 GetOWEEncounterLevel(u32 level) @@ -116,9 +116,9 @@ static inline void SetOWEEncounterLevel(u32 *level, u32 newLevel) *level = (*level & OWE_NO_DESPAWN_FLAG) | (newLevel & ~OWE_NO_DESPAWN_FLAG); } -static inline bool32 HasOWENoDespawnFlag(const struct ObjectEvent *object) +static inline bool32 HasOWENoDespawnFlag(const struct ObjectEvent *owe) { - return object->sOverworldEncounterLevel & OWE_NO_DESPAWN_FLAG; + return owe->sOverworldEncounterLevel & OWE_NO_DESPAWN_FLAG; } static inline void SetOWENoDespawnFlag(u32 *level) @@ -134,11 +134,11 @@ static inline bool32 ShouldSpawnWaterOWE(void) static bool32 CreateEnemyPartyOWE(u32 *speciesId, u32 *level, u32 *indexRoamerOutbreak, s32 x, s32 y); static bool32 OWE_DoesOWERoamerExist(void); static u32 GetOWERoamerStatusFromIndex(u32 indexRoamer); -static u32 GetOWERoamerOutbreakStatus(struct ObjectEvent *objectEvent); +static u32 GetOWERoamerOutbreakStatus(struct ObjectEvent *owe); static bool32 StartWildBattleWithOWE_CheckRoamer(u32 indexRoamerOutbreak); static bool32 StartWildBattleWithOWE_CheckBattleFrontier(u32 headerId); static bool32 StartWildBattleWithOWE_CheckMassOutbreak(u32 indexRoamerOutbreak, u32 speciesId); -static bool32 StartWildBattleWithOWE_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId); +static bool32 StartWildBattleWithOWE_CheckDoubleBattle(struct ObjectEvent *owe, u32 headerId); static bool32 CheckCuurentWildMonHeaderForOWE(bool32 shouldSpawnWaterMons); static u32 GetOldestActiveOWESlot(bool32 forceRemove); static u32 GetNextOWESpawnSlot(void); @@ -152,18 +152,18 @@ static bool32 CheckCanLoadOWE_Palette(u32 speciesId, bool32 isFemale, bool32 isS static bool32 CheckCanLoadOWE_Tiles(u32 speciesId, bool32 isFemale, bool32 isShiny, s32 x, s32 y); static void SortOWEAges(void); static u32 RemoveOldestGeneratedOWE(void); -static bool32 ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object); +static bool32 ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *owe); static void SetNewOWESpawnCountdown(void); -static void DoOWESpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn); +static void DoOWESpawnDespawnAnim(struct ObjectEvent *owe, bool32 animSpawn); static enum SpawnDespawnTypeOWE GetOWESpawnDespawnAnimType(u32 metatileBehavior); -static void PlayOWECry(struct ObjectEvent *objectEvent); +static void PlayOWECry(struct ObjectEvent *owe); static struct ObjectEvent *GetOWEObjectEvent(void); -static bool32 OWE_ShouldPlayOWEFleeSound(struct ObjectEvent *objectEvent); -static bool32 CheckRestrictedOWEMovementAtCoords(struct ObjectEvent *mon, s32 xNew, s32 yNew, enum Direction newDirection, enum Direction collisionDirection); +static bool32 OWE_ShouldPlayOWEFleeSound(struct ObjectEvent *owe); +static bool32 CheckRestrictedOWEMovementAtCoords(struct ObjectEvent *owe, s32 xNew, s32 yNew, enum Direction newDirection, enum Direction collisionDirection); static bool32 CheckRestrictedOWEMovementMetatile(s32 xCurrent, s32 yCurrent, s32 xNew, s32 yNew); -static bool32 CheckRestrictedOWEMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew); +static bool32 CheckRestrictedOWEMovementMap(struct ObjectEvent *owe, s32 xNew, s32 yNew); static bool32 IsOWELineOfSightClear(struct ObjectEvent *player, enum Direction direction, u32 distance); -static enum Direction CheckOWEPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection); +static enum Direction CheckOWEPathToPlayerFromCollision(struct ObjectEvent *owe, enum Direction newDirection); static void Task_OWEApproachForBattle(u8 taskId); static bool32 CheckValidOWESpecies(u32 speciesId); @@ -251,9 +251,9 @@ void OverworldWildEncounters_CB(void) .script = InteractWithOverworldWildEncounter, }; u32 objectEventId = GetObjectEventIdByLocalId(localId); - struct ObjectEvent *object = &gObjectEvents[objectEventId]; - if (ShouldDespawnGeneratedForNewOWE(object)) - RemoveObjectEvent(object); + struct ObjectEvent *owe = &gObjectEvents[objectEventId]; + if (ShouldDespawnGeneratedForNewOWE(owe)) + RemoveObjectEvent(owe); objectEventId = SpawnSpecialObjectEvent(&objectEventTemplate); assertf(objectEventId < OBJECT_EVENTS_COUNT, "could not spawn generated overworld encounter. too many object events exist") @@ -262,23 +262,23 @@ void OverworldWildEncounters_CB(void) return; } - object = &gObjectEvents[objectEventId]; - object->disableCoveringGroundEffects = TRUE; - object->sOverworldEncounterLevel = level; - object->sRoamerOutbreakStatus = indexRoamerOutbreak; + owe = &gObjectEvents[objectEventId]; + owe->disableCoveringGroundEffects = TRUE; + owe->sOverworldEncounterLevel = level; + owe->sRoamerOutbreakStatus = indexRoamerOutbreak; enum Direction directions[4]; memcpy(directions, gStandardDirections, sizeof directions); - ObjectEventTurn(object, directions[Random() & 3]); + ObjectEventTurn(owe, directions[Random() & 3]); SetNewOWESpawnCountdown(); } -bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum TypeOWE oweType) +bool32 IsOverworldWildEncounter(struct ObjectEvent *owe, enum TypeOWE oweType) { - if (!IS_OW_MON_OBJ(objectEvent)) + if (!IS_OW_MON_OBJ(owe)) return FALSE; - if (objectEvent->trainerType != TRAINER_TYPE_OW_WILD_ENCOUNTER) + if (owe->trainerType != TRAINER_TYPE_OW_WILD_ENCOUNTER) return FALSE; switch (oweType) @@ -288,10 +288,10 @@ bool32 IsOverworldWildEncounter(struct ObjectEvent *objectEvent, enum TypeOWE ow return TRUE; case OWE_GENERATED: - return (objectEvent->localId <= LOCALID_OW_ENCOUNTER_END && objectEvent->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); + return (owe->localId <= LOCALID_OW_ENCOUNTER_END && owe->localId > (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); case OWE_MANUAL: - return (objectEvent->localId > LOCALID_OW_ENCOUNTER_END || objectEvent->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); + return (owe->localId > LOCALID_OW_ENCOUNTER_END || owe->localId <= (LOCALID_OW_ENCOUNTER_END - OWE_SPAWNS_MAX)); } } @@ -300,23 +300,23 @@ void StartWildBattleWithOWE(void) u32 localId = gSpecialVar_LastTalked; u32 objEventId = GetObjectEventIdByLocalId(localId); u32 headerId = GetCurrentMapWildMonHeaderId(); - struct ObjectEvent *object = &gObjectEvents[objEventId]; - u32 indexRoamerOutbreak = GetOWERoamerIndex(object); + struct ObjectEvent *owe = &gObjectEvents[objEventId]; + u32 indexRoamerOutbreak = GetOWERoamerIndex(owe); - assertf(objEventId < OBJECT_EVENTS_COUNT && IsOverworldWildEncounter(object, OWE_ANY), "cannot start overworld wild enocunter as the selected object is invalid.\nlocalId: %d", localId) + assertf(objEventId < OBJECT_EVENTS_COUNT && IsOverworldWildEncounter(owe, OWE_ANY), "cannot start overworld wild enocunter as the selected object is invalid.\nlocalId: %d", localId) { UnlockPlayerFieldControls(); UnfreezeObjectEvents(); return; } - if (indexRoamerOutbreak && StartWildBattleWithOWE_CheckRoamer(GetOWERoamerOutbreakStatus(object))) + if (indexRoamerOutbreak && StartWildBattleWithOWE_CheckRoamer(GetOWERoamerOutbreakStatus(owe))) return; - u32 speciesId = OW_SPECIES(object); - bool32 shiny = OW_SHINY(object) ? TRUE : FALSE; - u32 gender = OW_FEMALE(object) ? MON_FEMALE : MON_MALE; - u32 level = GetOWEEncounterLevel(object->sOverworldEncounterLevel); + u32 speciesId = OW_SPECIES(owe); + bool32 shiny = OW_SHINY(owe) ? TRUE : FALSE; + u32 gender = OW_FEMALE(owe) ? MON_FEMALE : MON_MALE; + u32 level = GetOWEEncounterLevel(owe->sOverworldEncounterLevel); u32 personality; switch (gSpeciesInfo[speciesId].genderRatio) @@ -342,7 +342,7 @@ void StartWildBattleWithOWE(void) if (StartWildBattleWithOWE_CheckMassOutbreak(indexRoamerOutbreak, speciesId)) return; - if (StartWildBattleWithOWE_CheckDoubleBattle(object, headerId)) + if (StartWildBattleWithOWE_CheckDoubleBattle(owe, headerId)) return; BattleSetup_StartWildBattle(); @@ -447,8 +447,8 @@ static bool32 OWE_DoesOWERoamerExist(void) { for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { - struct ObjectEvent *object = &gObjectEvents[i]; - if (IsOverworldWildEncounter(object, OWE_ANY) && GetOWERoamerOutbreakStatus(object) == gEncounteredRoamerIndex) + struct ObjectEvent *owe = &gObjectEvents[i]; + if (IsOverworldWildEncounter(owe, OWE_ANY) && GetOWERoamerOutbreakStatus(owe) == gEncounteredRoamerIndex) return TRUE; } @@ -463,12 +463,12 @@ static u32 GetOWERoamerStatusFromIndex(u32 indexRoamer) return indexRoamer; } -static u32 GetOWERoamerOutbreakStatus(struct ObjectEvent *objectEvent) +static u32 GetOWERoamerOutbreakStatus(struct ObjectEvent *owe) { - if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + if (!IsOverworldWildEncounter(owe, OWE_ANY)) return OWE_INVALID_ROAMER_OUTBREAK; - u32 status = GetOWERoamerIndex(objectEvent); + u32 status = GetOWERoamerIndex(owe); if (status == OWE_NON_ROAMER_OUTBREAK || status == OWE_MASS_OUTBREAK_INDEX) { return OWE_INVALID_ROAMER_OUTBREAK; @@ -534,12 +534,12 @@ static bool32 StartWildBattleWithOWE_CheckMassOutbreak(u32 indexRoamerOutbreak, return FALSE; } -static bool32 StartWildBattleWithOWE_CheckDoubleBattle(struct ObjectEvent *objectEvent, u32 headerId) +static bool32 StartWildBattleWithOWE_CheckDoubleBattle(struct ObjectEvent *owe, u32 headerId) { enum WildPokemonArea wildArea; enum TimeOfDay timeOfDay; const struct WildPokemonInfo *wildMonInfo; - u32 metatileBehavior = MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x, objectEvent->currentCoords.y); + u32 metatileBehavior = MapGridGetMetatileBehaviorAt(owe->currentCoords.x, owe->currentCoords.y); if (TryDoDoubleWildBattle()) { @@ -621,16 +621,16 @@ void TryTriggerOverworldWilEncounter(struct ObjectEvent *obstacle, struct Object bool32 ShouldRunDefaultOWEScript(u32 objectEventId) { - struct ObjectEvent *object = &gObjectEvents[objectEventId]; - if (!IsOverworldWildEncounter(object, OWE_ANY)) + struct ObjectEvent *owe = &gObjectEvents[objectEventId]; + if (!IsOverworldWildEncounter(owe, OWE_ANY)) return FALSE; - if (IsOverworldWildEncounter(object, OWE_MANUAL) + if (IsOverworldWildEncounter(owe, OWE_MANUAL) && GetObjectEventScriptPointerByObjectEventId(objectEventId) != InteractWithOverworldWildEncounter && GetObjectEventScriptPointerByObjectEventId(objectEventId) != NULL) return FALSE; - gSpecialVar_0x8004 = OW_SPECIES(object); + gSpecialVar_0x8004 = OW_SPECIES(owe); return TRUE; } @@ -731,12 +731,12 @@ static u32 GetNextOWESpawnSlot(void) static u32 GetSpeciesByOWESpawnSlot(u32 spawnSlot) { u32 objEventId = GetObjectEventIdByLocalId(GetLocalIdByOWESpawnSlot(spawnSlot)); - struct ObjectEvent *objectEvent = &gObjectEvents[objEventId]; + struct ObjectEvent *owe = &gObjectEvents[objEventId]; if (objEventId >= OBJECT_EVENTS_COUNT) return SPECIES_NONE; - return OW_SPECIES(objectEvent); + return OW_SPECIES(owe); } static bool32 TrySelectTileForOWE(s32* outX, s32* outY) @@ -1022,44 +1022,44 @@ static void SortOWEAges(void) } } -void OnOverworldWildEncounterSpawn(struct ObjectEvent *objectEvent) +void OnOverworldWildEncounterSpawn(struct ObjectEvent *owe) { - if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + if (!IsOverworldWildEncounter(owe, OWE_ANY)) return; - if (IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) + if (IsOverworldWildEncounter(owe, OWE_GENERATED)) SortOWEAges(); - DoOWESpawnDespawnAnim(objectEvent, TRUE); + DoOWESpawnDespawnAnim(owe, TRUE); } -void OnOverworldWildEncounterDespawn(struct ObjectEvent *objectEvent) +void OnOverworldWildEncounterDespawn(struct ObjectEvent *owe) { - if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + if (!IsOverworldWildEncounter(owe, OWE_ANY)) return; - objectEvent->sOverworldEncounterLevel = 0; - objectEvent->sAge = 0; - objectEvent->sRoamerOutbreakStatus = 0; + owe->sOverworldEncounterLevel = 0; + owe->sAge = 0; + owe->sRoamerOutbreakStatus = 0; - DoOWESpawnDespawnAnim(objectEvent, FALSE); + DoOWESpawnDespawnAnim(owe, FALSE); } -bool32 IsOWEDespawnExempt(struct ObjectEvent *objectEvent) +bool32 IsOWEDespawnExempt(struct ObjectEvent *owe) { - if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + if (!IsOverworldWildEncounter(owe, OWE_ANY)) return FALSE; - if (HasOWENoDespawnFlag(objectEvent) && AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) + if (HasOWENoDespawnFlag(owe) && AreCoordsInsidePlayerMap(owe->currentCoords.x, owe->currentCoords.y)) return TRUE; - objectEvent->offScreen = TRUE; + owe->offScreen = TRUE; return FALSE; } -bool32 DespawnOWEDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *objectEvent) +bool32 DespawnOWEDueToNPCCollision(struct ObjectEvent *curObject, struct ObjectEvent *owe) { - if (!IsOverworldWildEncounter(curObject, OWE_GENERATED) || IsOverworldWildEncounter(objectEvent, OWE_ANY) || objectEvent->isPlayer) + if (!IsOverworldWildEncounter(curObject, OWE_GENERATED) || IsOverworldWildEncounter(owe, OWE_ANY) || owe->isPlayer) return FALSE; RemoveObjectEvent(curObject); @@ -1071,11 +1071,11 @@ u32 DespawnOWEDueToTrainerSight(u32 collision, s32 x, s32 y) if (!(collision & (1 << (COLLISION_OBJECT_EVENT - 1)))) return collision; - struct ObjectEvent *objectEvent = &gObjectEvents[GetObjectEventIdByXY(x, y)]; - if (!IsOverworldWildEncounter(objectEvent, OWE_GENERATED)) + struct ObjectEvent *owe = &gObjectEvents[GetObjectEventIdByXY(x, y)]; + if (!IsOverworldWildEncounter(owe, OWE_GENERATED)) return collision; - RemoveObjectEvent(objectEvent); + RemoveObjectEvent(owe); return collision & (1 << (COLLISION_OBJECT_EVENT - 1)); } @@ -1091,12 +1091,12 @@ void DespwnAllOverworldWildEncounters(enum TypeOWE oweType, u32 flags) for (u32 i = 0; i < OBJECT_EVENTS_COUNT; ++i) { - struct ObjectEvent *obj = &gObjectEvents[i]; + struct ObjectEvent *owe = &gObjectEvents[i]; - if (!obj->active) + if (!owe->active) continue; - if (!IsOverworldWildEncounter(obj, oweType)) + if (!IsOverworldWildEncounter(owe, oweType)) continue; if (flags & WILD_CHECK_REPEL) @@ -1104,15 +1104,15 @@ void DespwnAllOverworldWildEncounters(enum TypeOWE oweType, u32 flags) if (!REPEL_STEP_COUNT) continue; - if (HasOWENoDespawnFlag(obj)) + if (HasOWENoDespawnFlag(owe)) continue; - if (IsWildLevelAllowedByRepel(GetOWEEncounterLevel(obj->sOverworldEncounterLevel))) + if (IsWildLevelAllowedByRepel(GetOWEEncounterLevel(owe->sOverworldEncounterLevel))) continue; } - UpdateObjectEventCoords(obj, dx, dy); - RemoveObjectEvent(obj); + UpdateObjectEventCoords(owe, dx, dy); + RemoveObjectEvent(owe); } } @@ -1152,14 +1152,14 @@ void TryAndDespawnOldestGeneratedOWE_Palette(void) void DespawnOWEOnBattleStart(void) { - struct ObjectEvent *object = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; - if (!IsOverworldWildEncounter(object, OWE_ANY)) + struct ObjectEvent *owe = &gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_LastTalked)]; + if (!IsOverworldWildEncounter(owe, OWE_ANY)) return; - if (IsOverworldWildEncounter(object, OWE_MANUAL)) - FlagSet(GetObjectEventFlagIdByLocalIdAndMap(object->localId, object->mapNum, object->mapGroup)); + if (IsOverworldWildEncounter(owe, OWE_MANUAL)) + FlagSet(GetObjectEventFlagIdByLocalIdAndMap(owe->localId, owe->mapNum, owe->mapGroup)); - RemoveObjectEvent(object); + RemoveObjectEvent(owe); SetNewOWESpawnCountdown(); gSpecialVar_LastTalked = LOCALID_NONE; } @@ -1193,9 +1193,9 @@ static u32 RemoveOldestGeneratedOWE(void) return objectEventId; } -static bool32 ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *object) +static bool32 ShouldDespawnGeneratedForNewOWE(struct ObjectEvent *owe) { - if (!IsOverworldWildEncounter(object, OWE_GENERATED)) + if (!IsOverworldWildEncounter(owe, OWE_GENERATED)) return FALSE; return WE_OWE_SPAWN_REPLACEMENT && GetNumberOfActiveOWEs(OWE_GENERATED) >= OWE_SPAWNS_MAX; @@ -1213,18 +1213,18 @@ static void SetNewOWESpawnCountdown(void) sOWESpawnCountdown = OWE_SPAWN_TIME_MINIMUM + (OWE_SPAWN_TIME_PER_ACTIVE * numActive); } -static void DoOWESpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSpawn) +static void DoOWESpawnDespawnAnim(struct ObjectEvent *owe, bool32 animSpawn) { if (gMain.callback2 != CB2_Overworld) return; enum SpawnDespawnTypeOWE spawnAnimType; - bool32 isShiny = OW_SHINY(objectEvent) ? TRUE : FALSE; + bool32 isShiny = OW_SHINY(owe) ? TRUE : FALSE; if (animSpawn) - PlayOWECry(objectEvent); + PlayOWECry(owe); - if (!animSpawn && OWE_ShouldPlayOWEFleeSound(objectEvent)) + if (!animSpawn && OWE_ShouldPlayOWEFleeSound(owe)) PlaySE(SE_FLEE); if (WE_OWE_SHINY_SPARKLE && isShiny && animSpawn) @@ -1234,10 +1234,10 @@ static void DoOWESpawnDespawnAnim(struct ObjectEvent *objectEvent, bool32 animSp } else { - u32 metatileBehavior = MapGridGetMetatileBehaviorAt(objectEvent->currentCoords.x, objectEvent->currentCoords.y); + u32 metatileBehavior = MapGridGetMetatileBehaviorAt(owe->currentCoords.x, owe->currentCoords.y); spawnAnimType = GetOWESpawnDespawnAnimType(metatileBehavior); } - MovementAction_OverworldEncounterSpawn(spawnAnimType, objectEvent); + MovementAction_OverworldEncounterSpawn(spawnAnimType, owe); } static enum SpawnDespawnTypeOWE GetOWESpawnDespawnAnimType(u32 metatileBehavior) @@ -1254,15 +1254,15 @@ static enum SpawnDespawnTypeOWE GetOWESpawnDespawnAnimType(u32 metatileBehavior) return OWE_SPAWN_ANIM_CAVE; } -static void PlayOWECry(struct ObjectEvent *objectEvent) +static void PlayOWECry(struct ObjectEvent *owe) { - if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + if (!IsOverworldWildEncounter(owe, OWE_ANY)) return; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - u32 speciesId = OW_SPECIES(objectEvent); - s32 distanceX = objectEvent->currentCoords.x - player->currentCoords.x; - s32 distanceY = objectEvent->currentCoords.y - player->currentCoords.y; + u32 speciesId = OW_SPECIES(owe); + s32 distanceX = owe->currentCoords.x - player->currentCoords.x; + s32 distanceY = owe->currentCoords.y - player->currentCoords.y; u32 distanceMax = OWE_SPAWN_WIDTH_RADIUS + OWE_SPAWN_HEIGHT_RADIUS; u32 distance; u32 volume; @@ -1292,7 +1292,7 @@ static struct ObjectEvent *GetOWEObjectEvent(void) u32 numActive = GetNumberOfActiveOWEs(OWE_ANY); u32 randomIndex; u32 counter = 0; - struct ObjectEvent *object; + struct ObjectEvent *owe; if (numActive) randomIndex = Random() % numActive; @@ -1301,11 +1301,11 @@ static struct ObjectEvent *GetOWEObjectEvent(void) for (u32 i = 0; i < OBJECT_EVENTS_COUNT; i++) { - object = &gObjectEvents[i]; - if (IsOverworldWildEncounter(object, OWE_ANY)) + owe = &gObjectEvents[i]; + if (IsOverworldWildEncounter(owe, OWE_ANY)) { if (counter >= randomIndex) - return object; + return owe; else counter++; } @@ -1313,63 +1313,63 @@ static struct ObjectEvent *GetOWEObjectEvent(void) return NULL; } -static bool32 OWE_ShouldPlayOWEFleeSound(struct ObjectEvent *objectEvent) +static bool32 OWE_ShouldPlayOWEFleeSound(struct ObjectEvent *owe) { - if (!IsOverworldWildEncounter(objectEvent, OWE_ANY) || OW_SPECIES(objectEvent) == SPECIES_NONE) + if (!IsOverworldWildEncounter(owe, OWE_ANY) || OW_SPECIES(owe) == SPECIES_NONE) return FALSE; - if (!AreCoordsInsidePlayerMap(objectEvent->currentCoords.x, objectEvent->currentCoords.y)) + if (!AreCoordsInsidePlayerMap(owe->currentCoords.x, owe->currentCoords.y)) return FALSE; - if (ShouldDespawnGeneratedForNewOWE(objectEvent)) + if (ShouldDespawnGeneratedForNewOWE(owe)) return FALSE; - if (objectEvent->offScreen) + if (owe->offScreen) return FALSE; return WE_OWE_DESPAWN_SOUND; } #define sTypeFuncId data[1] // Same as in src/event_object_movement.c -void RestoreSavedOWEBehaviorState(struct ObjectEvent *objectEvent, struct Sprite *sprite) +void RestoreSavedOWEBehaviorState(struct ObjectEvent *owe, struct Sprite *sprite) { - if (IsOverworldWildEncounter(objectEvent, OWE_ANY) && HasSavedOWEMovementState(objectEvent)) + if (IsOverworldWildEncounter(owe, OWE_ANY) && HasSavedOWEMovementState(owe)) sprite->sTypeFuncId = OWE_RESTORED_MOVEMENT_FUNC_ID; } #undef sTypeFuncId // Returns TRUE if movement is restricted. -bool32 CheckRestrictedOWEMovement(struct ObjectEvent *objectEvent, enum Direction direction) +bool32 CheckRestrictedOWEMovement(struct ObjectEvent *owe, enum Direction direction) { - if (GetCollisionInDirection(objectEvent, direction)) + if (GetCollisionInDirection(owe, direction)) return TRUE; - if (CanAwareOWESeePlayer(objectEvent) && WE_OWE_UNRESTRICT_SIGHT) + if (CanAwareOWESeePlayer(owe) && WE_OWE_UNRESTRICT_SIGHT) return FALSE; - s32 xCurrent = objectEvent->currentCoords.x; - s32 yCurrent = objectEvent->currentCoords.y; + s32 xCurrent = owe->currentCoords.x; + s32 yCurrent = owe->currentCoords.y; s32 xNew = xCurrent + gDirectionToVectors[direction].x; s32 yNew = yCurrent + gDirectionToVectors[direction].y; if (CheckRestrictedOWEMovementMetatile(xCurrent, yCurrent, xNew, yNew)) return TRUE; - if (CheckRestrictedOWEMovementMap(objectEvent, xNew, yNew)) + if (CheckRestrictedOWEMovementMap(owe, xNew, yNew)) return TRUE; return FALSE; } -static bool32 CheckRestrictedOWEMovementAtCoords(struct ObjectEvent *mon, s32 xNew, s32 yNew, enum Direction newDirection, enum Direction collisionDirection) +static bool32 CheckRestrictedOWEMovementAtCoords(struct ObjectEvent *owe, s32 xNew, s32 yNew, enum Direction newDirection, enum Direction collisionDirection) { - if (CheckRestrictedOWEMovementMetatile(mon->currentCoords.x, mon->currentCoords.y, xNew, yNew)) + if (CheckRestrictedOWEMovementMetatile(owe->currentCoords.x, owe->currentCoords.y, xNew, yNew)) return FALSE; - if (CheckRestrictedOWEMovementMap(mon, xNew, yNew)) + if (CheckRestrictedOWEMovementMap(owe, xNew, yNew)) return FALSE; - if (GetCollisionAtCoords(mon, xNew, yNew, collisionDirection)) + if (GetCollisionAtCoords(owe, xNew, yNew, collisionDirection)) return FALSE; return TRUE; @@ -1402,66 +1402,66 @@ static bool32 CheckRestrictedOWEMovementMetatile(s32 xCurrent, s32 yCurrent, s32 return TRUE; } -static bool32 CheckRestrictedOWEMovementMap(struct ObjectEvent *objectEvent, s32 xNew, s32 yNew) +static bool32 CheckRestrictedOWEMovementMap(struct ObjectEvent *owe, s32 xNew, s32 yNew) { if (!WE_OWE_RESTRICT_MAP) return FALSE; - if (objectEvent->mapGroup == gSaveBlock1Ptr->location.mapGroup - && objectEvent->mapNum == gSaveBlock1Ptr->location.mapNum) + if (owe->mapGroup == gSaveBlock1Ptr->location.mapGroup + && owe->mapNum == gSaveBlock1Ptr->location.mapNum) return !AreCoordsInsidePlayerMap(xNew, yNew); else return AreCoordsInsidePlayerMap(xNew, yNew); } -bool32 CanAwareOWESeePlayer(struct ObjectEvent *mon) +bool32 CanAwareOWESeePlayer(struct ObjectEvent *owe) { - if (!IsOverworldWildEncounter(mon, OWE_ANY) || mon->movementType == MOVEMENT_TYPE_WANDER_AROUND_OWE) + if (!IsOverworldWildEncounter(owe, OWE_ANY) || owe->movementType == MOVEMENT_TYPE_WANDER_AROUND_OWE) return FALSE; - if (IsPlayerInsideOWEActiveDistance(mon) && (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) + if (IsPlayerInsideOWEActiveDistance(owe) && (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH) || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && gPlayerAvatar.runningState == MOVING))) return TRUE; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - u32 speciesId = OW_SPECIES(mon); + u32 speciesId = OW_SPECIES(owe); u32 viewDistance = OWE_GetViewDistanceFromSpecies(speciesId); u32 viewWidth = OWE_GetViewWidthFromSpecies(speciesId); s32 halfWidth = (viewWidth - 1) / 2; - enum Direction direction = mon->facingDirection; + enum Direction direction = owe->facingDirection; bool32 retVal = FALSE; switch (direction) { case DIR_NORTH: - if (player->currentCoords.y < mon->currentCoords.y - && mon->currentCoords.y - player->currentCoords.y <= viewDistance - && player->currentCoords.x >= mon->currentCoords.x - halfWidth - && player->currentCoords.x <= mon->currentCoords.x + halfWidth) + if (player->currentCoords.y < owe->currentCoords.y + && owe->currentCoords.y - player->currentCoords.y <= viewDistance + && player->currentCoords.x >= owe->currentCoords.x - halfWidth + && player->currentCoords.x <= owe->currentCoords.x + halfWidth) retVal = TRUE; break; case DIR_SOUTH: - if (player->currentCoords.y > mon->currentCoords.y - && player->currentCoords.y - mon->currentCoords.y <= viewDistance - && player->currentCoords.x >= mon->currentCoords.x - halfWidth - && player->currentCoords.x <= mon->currentCoords.x + halfWidth) + if (player->currentCoords.y > owe->currentCoords.y + && player->currentCoords.y - owe->currentCoords.y <= viewDistance + && player->currentCoords.x >= owe->currentCoords.x - halfWidth + && player->currentCoords.x <= owe->currentCoords.x + halfWidth) retVal = TRUE; break; case DIR_EAST: - if (player->currentCoords.x > mon->currentCoords.x - && player->currentCoords.x - mon->currentCoords.x <= viewDistance - && player->currentCoords.y >= mon->currentCoords.y - halfWidth - && player->currentCoords.y <= mon->currentCoords.y + halfWidth) + if (player->currentCoords.x > owe->currentCoords.x + && player->currentCoords.x - owe->currentCoords.x <= viewDistance + && player->currentCoords.y >= owe->currentCoords.y - halfWidth + && player->currentCoords.y <= owe->currentCoords.y + halfWidth) retVal = TRUE; break; case DIR_WEST: - if (player->currentCoords.x < mon->currentCoords.x - && mon->currentCoords.x - player->currentCoords.x <= viewDistance - && player->currentCoords.y >= mon->currentCoords.y - halfWidth - && player->currentCoords.y <= mon->currentCoords.y + halfWidth) + if (player->currentCoords.x < owe->currentCoords.x + && owe->currentCoords.x - player->currentCoords.x <= viewDistance + && player->currentCoords.y >= owe->currentCoords.y - halfWidth + && player->currentCoords.y <= owe->currentCoords.y + halfWidth) retVal = TRUE; break; @@ -1495,68 +1495,68 @@ static bool32 IsOWELineOfSightClear(struct ObjectEvent *player, enum Direction d return TRUE; } -bool32 IsPlayerInsideOWEActiveDistance(struct ObjectEvent *mon) +bool32 IsPlayerInsideOWEActiveDistance(struct ObjectEvent *owe) { - if (!IsOverworldWildEncounter(mon, OWE_ANY)) + if (!IsOverworldWildEncounter(owe, OWE_ANY)) return FALSE; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; u32 distance = OWE_CHASE_RANGE; - u32 speciesId = OW_SPECIES(mon); + u32 speciesId = OW_SPECIES(owe); if (speciesId != SPECIES_NONE) distance = OWE_GetViewActiveDistanceFromSpecies(speciesId); - if (player->currentCoords.y <= mon->currentCoords.y + distance && player->currentCoords.y >= mon->currentCoords.y - distance - && player->currentCoords.x <= mon->currentCoords.x + distance && player->currentCoords.x >= mon->currentCoords.x - distance) + if (player->currentCoords.y <= owe->currentCoords.y + distance && player->currentCoords.y >= owe->currentCoords.y - distance + && player->currentCoords.x <= owe->currentCoords.x + distance && player->currentCoords.x >= owe->currentCoords.x - distance) return TRUE; return FALSE; } -bool32 IsOWENextToPlayer(struct ObjectEvent *mon) +bool32 IsOWENextToPlayer(struct ObjectEvent *owe) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - if ((mon->currentCoords.x != player->currentCoords.x && mon->currentCoords.y != player->currentCoords.y) || (mon->currentCoords.x < player->currentCoords.x - 1 || mon->currentCoords.x > player->currentCoords.x + 1 || mon->currentCoords.y < player->currentCoords.y - 1 || mon->currentCoords.y > player->currentCoords.y + 1)) + if ((owe->currentCoords.x != player->currentCoords.x && owe->currentCoords.y != player->currentCoords.y) || (owe->currentCoords.x < player->currentCoords.x - 1 || owe->currentCoords.x > player->currentCoords.x + 1 || owe->currentCoords.y < player->currentCoords.y - 1 || owe->currentCoords.y > player->currentCoords.y + 1)) return FALSE; return TRUE; } -enum Direction DirectionOfOWEToPlayerFromCollision(struct ObjectEvent *mon) +enum Direction DirectionOfOWEToPlayerFromCollision(struct ObjectEvent *owe) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - switch (mon->movementDirection) + switch (owe->movementDirection) { case DIR_NORTH: case DIR_SOUTH: - if (player->currentCoords.x < mon->currentCoords.x) + if (player->currentCoords.x < owe->currentCoords.x) return DIR_WEST; - else if (player->currentCoords.x == mon->currentCoords.x) - return CheckOWEPathToPlayerFromCollision(mon, (Random() % 2) == 0 ? DIR_EAST : DIR_WEST); + else if (player->currentCoords.x == owe->currentCoords.x) + return CheckOWEPathToPlayerFromCollision(owe, (Random() % 2) == 0 ? DIR_EAST : DIR_WEST); else return DIR_EAST; case DIR_EAST: case DIR_WEST: - if (player->currentCoords.y < mon->currentCoords.y) + if (player->currentCoords.y < owe->currentCoords.y) return DIR_NORTH; - else if (player->currentCoords.y == mon->currentCoords.y) - return CheckOWEPathToPlayerFromCollision(mon, (Random() % 2) == 0 ? DIR_NORTH : DIR_SOUTH); + else if (player->currentCoords.y == owe->currentCoords.y) + return CheckOWEPathToPlayerFromCollision(owe, (Random() % 2) == 0 ? DIR_NORTH : DIR_SOUTH); else return DIR_SOUTH; } - return mon->movementDirection; + return owe->movementDirection; } -u32 GetApproachingOWEDistanceToPlayer(struct ObjectEvent *mon, bool32 *equalDistances) +u32 GetApproachingOWEDistanceToPlayer(struct ObjectEvent *owe, bool32 *equalDistances) { struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; s32 absX, absY; - s32 distanceX = player->currentCoords.x - mon->currentCoords.x; - s32 distanceY = player->currentCoords.y - mon->currentCoords.y; + s32 distanceX = player->currentCoords.x - owe->currentCoords.x; + s32 distanceY = player->currentCoords.y - owe->currentCoords.y; if (distanceX < 0) absX = distanceX * -1; @@ -1592,56 +1592,56 @@ u32 GetOWEWalkMovementActionInDirectionWithSpeed(enum Direction direction, u32 s return GetWalkNormalMovementAction(direction); } -static enum Direction CheckOWEPathToPlayerFromCollision(struct ObjectEvent *mon, enum Direction newDirection) +static enum Direction CheckOWEPathToPlayerFromCollision(struct ObjectEvent *owe, enum Direction newDirection) { - s16 x = mon->currentCoords.x; - s16 y = mon->currentCoords.y; + s16 x = owe->currentCoords.x; + s16 y = owe->currentCoords.y; MoveCoords(newDirection, &x, &y); - if (CheckRestrictedOWEMovementAtCoords(mon, x, y, newDirection, newDirection)) + if (CheckRestrictedOWEMovementAtCoords(owe, x, y, newDirection, newDirection)) { - if (mon->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) + if (owe->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) return GetOppositeDirection(newDirection); - MoveCoords(mon->movementDirection, &x, &y); - if (CheckRestrictedOWEMovementAtCoords(mon, x, y, newDirection, mon->movementDirection)) + MoveCoords(owe->movementDirection, &x, &y); + if (CheckRestrictedOWEMovementAtCoords(owe, x, y, newDirection, owe->movementDirection)) return newDirection; } - x = mon->currentCoords.x; - y = mon->currentCoords.y; + x = owe->currentCoords.x; + y = owe->currentCoords.y; MoveCoords(GetOppositeDirection(newDirection), &x, &y); - if (CheckRestrictedOWEMovementAtCoords(mon, x, y, newDirection, newDirection)) + if (CheckRestrictedOWEMovementAtCoords(owe, x, y, newDirection, newDirection)) { - if (mon->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) + if (owe->movementType == MOVEMENT_TYPE_FLEE_PLAYER_OWE) return newDirection; - MoveCoords(mon->movementDirection, &x, &y); - if (CheckRestrictedOWEMovementAtCoords(mon, x, y, newDirection, mon->movementDirection)) + MoveCoords(owe->movementDirection, &x, &y); + if (CheckRestrictedOWEMovementAtCoords(owe, x, y, newDirection, owe->movementDirection)) return GetOppositeDirection(newDirection); } - return mon->movementDirection; + return owe->movementDirection; } #define tObjectId data[0] void OWEApproachForBattle(void) { u32 objectEventId = GetObjectEventIdByLocalId(gSpecialVar_LastTalked); - struct ObjectEvent *objectEvent = &gObjectEvents[objectEventId]; - if (!WE_OWE_APPROACH_FOR_BATTLE || !IsOverworldWildEncounter(objectEvent, OWE_ANY)) + struct ObjectEvent *owe = &gObjectEvents[objectEventId]; + if (!WE_OWE_APPROACH_FOR_BATTLE || !IsOverworldWildEncounter(owe, OWE_ANY)) { - FreezeObjectEvent(objectEvent); + FreezeObjectEvent(owe); return; } - if (!IsOverworldWildEncounter(objectEvent, OWE_ANY)) + if (!IsOverworldWildEncounter(owe, OWE_ANY)) return; u32 taskId = CreateTask(Task_OWEApproachForBattle, 2); if (FindTaskIdByFunc(Task_OWEApproachForBattle) == TASK_NONE) { - FreezeObjectEvent(objectEvent); + FreezeObjectEvent(owe); return; } From c1f238ec02d921086eca06a5676f858103cc4af8 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:18:29 +0000 Subject: [PATCH 568/572] Change typedefs --- src/event_object_movement.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 7e4651108001..23dd37fba8fd 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -11933,8 +11933,7 @@ bool8 MovementType_OverworldWildEncounter_WanderAround_Step3(struct ObjectEvent bool8 MovementType_OverworldWildEncounter_WanderAround_Step4(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - u8 chosenDirection = objectEvent->movementDirection; - + enum Direction chosenDirection = objectEvent->movementDirection; if ((Random() & 3) != 0) chosenDirection = GetNinetyDegreeDirection(chosenDirection, Random() % 2); @@ -11998,8 +11997,8 @@ bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step10(struct ObjectEvent bool8 MovementType_OverworldWildEncounter_ChasePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - u16 speciesId = OW_SPECIES(objectEvent); - u8 movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + u32 speciesId = OW_SPECIES(objectEvent); + u32 movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); sprite->sTypeFuncId = 12; if (CheckRestrictedOWEMovement(objectEvent, objectEvent->movementDirection)) @@ -12071,8 +12070,8 @@ bool8 MovementType_OverworldWildEncounter_FleePlayer_Step10(struct ObjectEvent * bool8 MovementType_OverworldWildEncounter_FleePlayer_Step11(struct ObjectEvent *objectEvent, struct Sprite *sprite) { - u16 speciesId = OW_SPECIES(objectEvent); - u8 movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); + u32 speciesId = OW_SPECIES(objectEvent); + u32 movementActionId = GetOWEWalkMovementActionInDirectionWithSpeed(objectEvent->movementDirection, OWE_GetActiveSpeedFromSpecies(speciesId)); if (CheckRestrictedOWEMovement(objectEvent, objectEvent->movementDirection)) { enum Direction newDirection = DirectionOfOWEToPlayerFromCollision(objectEvent); @@ -12164,8 +12163,8 @@ bool8 MovementType_OverworldWildEncounter_ApproachPlayer_Step11(struct ObjectEve { bool32 equalDistances = FALSE; u32 distance = GetApproachingOWEDistanceToPlayer(objectEvent, &equalDistances); - u16 speciesId = OW_SPECIES(objectEvent); - u8 movementActionId; + u32 speciesId = OW_SPECIES(objectEvent); + u32 movementActionId; if (distance <= 1) { SetObjectEventDirection(objectEvent, GetOppositeDirection(objectEvent->movementDirection)); From 64af2b393a6c54c3f38ba643c3a011abb1157779 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Tue, 3 Feb 2026 16:06:01 -0600 Subject: [PATCH 569/572] Reset jump timer on reload OW for MOVEMENT_TYPE_APPROACH_PLAYER_OWE --- src/wild_encounter_ow.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 37578da6a854..126431889f0b 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -1331,12 +1331,18 @@ static bool32 OWE_ShouldPlayOWEFleeSound(struct ObjectEvent *owe) } #define sTypeFuncId data[1] // Same as in src/event_object_movement.c +#define sJumpTimer sprite->data[7] // Same as in src/event_object_movement.c void RestoreSavedOWEBehaviorState(struct ObjectEvent *owe, struct Sprite *sprite) { if (IsOverworldWildEncounter(owe, OWE_ANY) && HasSavedOWEMovementState(owe)) + { sprite->sTypeFuncId = OWE_RESTORED_MOVEMENT_FUNC_ID; + if (owe->movementType == MOVEMENT_TYPE_APPROACH_PLAYER_OWE) + sJumpTimer = (Random() % (OWE_APPROACH_JUMP_TIMER_MAX - OWE_APPROACH_JUMP_TIMER_MIN)) + OWE_APPROACH_JUMP_TIMER_MIN; + } } #undef sTypeFuncId +#undef sJumpTimer // Returns TRUE if movement is restricted. bool32 CheckRestrictedOWEMovement(struct ObjectEvent *owe, enum Direction direction) From ad0fc81375a5a56766a1581c82a00e9070a86367 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 4 Feb 2026 12:19:50 -0600 Subject: [PATCH 570/572] Can use a flag to toggle generated OWEs --- include/config/wild_encounter.h | 1 + src/wild_encounter_ow.c | 1 + 2 files changed, 2 insertions(+) diff --git a/include/config/wild_encounter.h b/include/config/wild_encounter.h index 07bb6c3bb1cc..9dcaca345e08 100644 --- a/include/config/wild_encounter.h +++ b/include/config/wild_encounter.h @@ -7,6 +7,7 @@ // Overworld Wild Encounters (OWEs) #define WE_OW_ENCOUNTERS FALSE // If TRUE, OW Pokémon can spawn as Overworld Wild Encounters on the current map. Requires OW_POKEMON_OBJECT_EVENTS. // If WE_OW_ENCOUNTERS is TRUE, it is recommended that OW_GFX_COMPRESS be set to FALSE to prevent VRAM issues. +#define WE_OWE_FLAG_DISABLED 0 // Replace 0 with a flag to use it to enable/disable generated OWEs. #define WE_OWE_BATTLE_PIKE TRUE // If TRUE, OWEs can spawn in the Battle Pike, if FALSE random encounters will be enabled instead. Requires WE_OW_ENCOUNTERS to be TRUE. #define WE_OWE_BATTLE_PYRAMID TRUE // If TRUE, OWEs can spawn in the Battle Pyramid, if FALSE random encounters will be enabled instead. Requires WE_OW_ENCOUNTERS to be TRUE. #define WE_OWE_RESTRICT_METATILE TRUE // If TRUE, OWEs will stay within tiles with the same encounter metatile behavior as the one it is currently on, if any. diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 126431889f0b..508ce5e2f326 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -184,6 +184,7 @@ void OverworldWildEncounters_CB(void) return; if (!WE_OW_ENCOUNTERS + || FlagGet(WE_OWE_FLAG_DISABLED) || FlagGet(OW_FLAG_NO_ENCOUNTER) || FlagGet(DN_FLAG_SEARCHING) || (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && !WE_OWE_BATTLE_PIKE) From 247e313630044eba6d4057abf4fa088864288c94 Mon Sep 17 00:00:00 2001 From: HashtagMarky <143505183+HashtagMarky@users.noreply.github.com> Date: Sun, 1 Mar 2026 12:26:18 +0000 Subject: [PATCH 571/572] Elevation Documentation --- src/field_control_avatar.c | 2 +- src/wild_encounter_ow.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 605832a41464..5e450e6268db 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -639,7 +639,7 @@ static const u8 *GetInteractedWaterScript(struct MapPosition *position, u8 metat { // Does this need a define for the surf elevation (1) check? // Can be used in sElevationToSubpriority and other places too - u8 objectEventId = GetObjectEventIdByPosition(position->x, position->y, 1); + u8 objectEventId = GetObjectEventIdByPosition(position->x, position->y, ELEVATION_SURF); if (IsPlayerFacingSurfableFishableWater() == TRUE && ShouldRunDefaultOWEScript(objectEventId)) { gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; diff --git a/src/wild_encounter_ow.c b/src/wild_encounter_ow.c index 508ce5e2f326..f3123c1b71d1 100644 --- a/src/wild_encounter_ow.c +++ b/src/wild_encounter_ow.c @@ -807,10 +807,8 @@ static bool32 TrySelectTileForOWE(s32* outX, s32* outY) return FALSE; } - - // 0 is change of elevation, 15 is multiple elevation e.g. bridges - // Causes weird interaction issues so just don't let mons spawn here - if (elevation == 0 || elevation == 15) + // These elevations cause weird interactions, so spawns are prevented. + if (elevation == ELEVATION_TRANSITION || elevation == ELEVATION_MULTI_LEVEL) return FALSE; tileBehavior = MapGridGetMetatileBehaviorAt(x, y); From 098e8a29b3335fadb51e1b2d17a8adc80199b32d Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Thu, 5 Mar 2026 07:17:50 -0600 Subject: [PATCH 572/572] Changed PIKACHU to NONE --- docs/tutorials/how_to_overworld_wild_encounters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/how_to_overworld_wild_encounters.md b/docs/tutorials/how_to_overworld_wild_encounters.md index 713d67b5ea3c..210ff9c131fc 100644 --- a/docs/tutorials/how_to_overworld_wild_encounters.md +++ b/docs/tutorials/how_to_overworld_wild_encounters.md @@ -17,7 +17,7 @@ However, Manual OWEs do not have to be defined fully, leaving any of the level, Assuming the following `graphicsId` have `.trainerType` set to `TRAINER_TYPE_OW_WILD_ENCOUNTER`; - `SPECIES_EEVEE` will result in an Eevee with a randomised level, gender and shinyness, using the default encounter script. - `OBJ_EVENT_GFX_SPECIES(NONE)` will result in a male randomised species of randomised level, gender and shinyness, using the default encounter script. -- `OBJ_EVENT_GFX_SPECIES_SHINY_FEMALE(PIKACHU)` will result in a female, shiny randomised species with randomised level and gender, using the default encounter script. +- `OBJ_EVENT_GFX_SPECIES_SHINY_FEMALE(NONE)` will result in a female, shiny randomised species with randomised level and gender, using the default encounter script. As level and species are potentially taken from the Wild Encounter Header, there is an `assertf` to let developers know when an invalid value is used. If the resultant level is invalid, it will be set to `MIN_LEVEL` (1). If the species is invalid, a replacement object will be created using `OBJ_EVENT_GFX_BOY_1`, this will not be an OWE of any kind.