Add support for multiple roamers (#4762)

* Replicate initial commit

* Integrate the rest of Duke's vanilla commits

* Alter Roamer struct to add frostbite support

* Fix some indentation issues

* Add option to hide substitute followers

* Revert "Add option to hide substitute followers"

This reverts commit 3fadaafae5.

* Incorporate feedback
This commit is contained in:
Bassoonian 2024-06-15 11:17:32 +02:00 committed by GitHub
parent a118b2caa8
commit 22994c79d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 237 additions and 190 deletions

View file

@ -74,6 +74,7 @@
#define GIFT_RIBBONS_COUNT 11 #define GIFT_RIBBONS_COUNT 11
#define SAVED_TRENDS_COUNT 5 #define SAVED_TRENDS_COUNT 5
#define PYRAMID_BAG_ITEMS_COUNT 10 #define PYRAMID_BAG_ITEMS_COUNT 10
#define ROAMER_COUNT 1 // Number of maximum concurrent active roamers
// Number of facilities for Ranking Hall. // Number of facilities for Ranking Hall.
// 7 facilities for single mode + tower double mode + tower multi mode. // 7 facilities for single mode + tower double mode + tower multi mode.

View file

@ -600,14 +600,14 @@ struct Roamer
/*0x08*/ u16 species; /*0x08*/ u16 species;
/*0x0A*/ u16 hp; /*0x0A*/ u16 hp;
/*0x0C*/ u8 level; /*0x0C*/ u8 level;
/*0x0D*/ u8 status; /*0x0D*/ u16 status;
/*0x0E*/ u8 cool; /*0x0F*/ u8 cool;
/*0x0F*/ u8 beauty; /*0x10*/ u8 beauty;
/*0x10*/ u8 cute; /*0x11*/ u8 cute;
/*0x11*/ u8 smart; /*0x12*/ u8 smart;
/*0x12*/ u8 tough;
/*0x13*/ bool8 active; /*0x13*/ bool8 active;
/*0x14*/ u8 filler[0x8]; /*0x14*/ u8 tough;
/*0x15*/ u8 filler[0x7];
}; };
struct RamScriptData struct RamScriptData
@ -1057,7 +1057,7 @@ struct SaveBlock1
/*0x31A8*/ u8 giftRibbons[GIFT_RIBBONS_COUNT]; /*0x31A8*/ u8 giftRibbons[GIFT_RIBBONS_COUNT];
/*0x31B3*/ struct ExternalEventData externalEventData; /*0x31B3*/ struct ExternalEventData externalEventData;
/*0x31C7*/ struct ExternalEventFlags externalEventFlags; /*0x31C7*/ struct ExternalEventFlags externalEventFlags;
/*0x31DC*/ struct Roamer roamer; /*0x31DC*/ struct Roamer roamer[ROAMER_COUNT];
#if FREE_ENIGMA_BERRY == FALSE #if FREE_ENIGMA_BERRY == FALSE
/*0x31F8*/ struct EnigmaBerry enigmaBerry; /*0x31F8*/ struct EnigmaBerry enigmaBerry;
#endif //FREE_ENIGMA_BERRY #endif //FREE_ENIGMA_BERRY

View file

@ -1,17 +1,21 @@
#ifndef GUARD_ROAMER_H #ifndef GUARD_ROAMER_H
#define GUARD_ROAMER_H #define GUARD_ROAMER_H
void ClearRoamerData(void); void DeactivateAllRoamers(void);
void ClearRoamerLocationData(void);
void InitRoamer(void); void InitRoamer(void);
void UpdateLocationHistoryForRoamer(void); void UpdateLocationHistoryForRoamer(void);
void RoamerMoveToOtherLocationSet(void); void RoamerMoveToOtherLocationSet(u32 roamerIndex);
void RoamerMove(void); void RoamerMove(u32 roamerIndex);
bool8 IsRoamerAt(u8 mapGroup, u8 mapNum); bool8 IsRoamerAt(u32 roamerIndex, u8 mapGroup, u8 mapNum);
void CreateRoamerMonInstance(void); void CreateRoamerMonInstance(u32 roamerIndex);
u8 TryStartRoamerEncounter(void); u8 TryStartRoamerEncounter(void);
void UpdateRoamerHPStatus(struct Pokemon *mon); void UpdateRoamerHPStatus(struct Pokemon *mon);
void SetRoamerInactive(void); void SetRoamerInactive(u32 roamerIndex);
void GetRoamerLocation(u8 *mapGroup, u8 *mapNum); void GetRoamerLocation(u32 roamerIndex, u8 *mapGroup, u8 *mapNum);
bool8 TryAddRoamer(u16 species, u8 level);
void MoveAllRoamersToOtherLocationSets(void);
void MoveAllRoamers(void);
extern u8 gEncounteredRoamerIndex;
#endif // GUARD_ROAMER_H #endif // GUARD_ROAMER_H

View file

@ -5657,7 +5657,7 @@ static void ReturnFromBattleToOverworld(void)
#else #else
if ((gBattleOutcome == B_OUTCOME_WON) || gBattleOutcome == B_OUTCOME_CAUGHT) // Bug: When Roar is used by roamer, gBattleOutcome is B_OUTCOME_PLAYER_TELEPORTED (5). if ((gBattleOutcome == B_OUTCOME_WON) || gBattleOutcome == B_OUTCOME_CAUGHT) // Bug: When Roar is used by roamer, gBattleOutcome is B_OUTCOME_PLAYER_TELEPORTED (5).
#endif // & with B_OUTCOME_WON (1) will return TRUE and deactivates the roamer. #endif // & with B_OUTCOME_WON (1) will return TRUE and deactivates the roamer.
SetRoamerInactive(); SetRoamerInactive(gEncounteredRoamerIndex);
} }
m4aSongNumStop(SE_LOW_HEALTH); m4aSongNumStop(SE_LOW_HEALTH);

View file

@ -181,8 +181,7 @@ void NewGameInitData(void)
gPlayerPartyCount = 0; gPlayerPartyCount = 0;
ZeroPlayerPartyMons(); ZeroPlayerPartyMons();
ResetPokemonStorageSystem(); ResetPokemonStorageSystem();
ClearRoamerData(); DeactivateAllRoamers();
ClearRoamerLocationData();
gSaveBlock1Ptr->registeredItem = ITEM_NONE; gSaveBlock1Ptr->registeredItem = ITEM_NONE;
ClearBag(); ClearBag();
NewGameInitPCItems(); NewGameInitPCItems();

View file

@ -449,7 +449,7 @@ static void UpdateMiscOverworldStates(void)
ChooseAmbientCrySpecies(); ChooseAmbientCrySpecies();
ResetCyclingRoadChallengeData(); ResetCyclingRoadChallengeData();
UpdateLocationHistoryForRoamer(); UpdateLocationHistoryForRoamer();
RoamerMoveToOtherLocationSet(); MoveAllRoamersToOtherLocationSets();
} }
void ResetGameStats(void) void ResetGameStats(void)
@ -847,7 +847,7 @@ if (I_VS_SEEKER_CHARGING != 0)
InitSecondaryTilesetAnimation(); InitSecondaryTilesetAnimation();
UpdateLocationHistoryForRoamer(); UpdateLocationHistoryForRoamer();
RoamerMove(); MoveAllRoamers();
DoCurrentWeather(); DoCurrentWeather();
ResetFieldTasksArgs(); ResetFieldTasksArgs();
RunOnResumeMapScript(); RunOnResumeMapScript();
@ -906,7 +906,7 @@ if (I_VS_SEEKER_CHARGING != 0)
Overworld_ClearSavedMusic(); Overworld_ClearSavedMusic();
RunOnTransitionMapScript(); RunOnTransitionMapScript();
UpdateLocationHistoryForRoamer(); UpdateLocationHistoryForRoamer();
RoamerMoveToOtherLocationSet(); MoveAllRoamersToOtherLocationSets();
if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR)
InitBattlePyramidMap(FALSE); InitBattlePyramidMap(FALSE);
else if (InTrainerHill()) else if (InTrainerHill())

View file

@ -252,72 +252,67 @@ static void FindMapsWithMon(u16 species)
if (sPokedexAreaScreen->alteringCaveId >= NUM_ALTERING_CAVE_TABLES) if (sPokedexAreaScreen->alteringCaveId >= NUM_ALTERING_CAVE_TABLES)
sPokedexAreaScreen->alteringCaveId = 0; sPokedexAreaScreen->alteringCaveId = 0;
roamer = &gSaveBlock1Ptr->roamer; sPokedexAreaScreen->numOverworldAreas = 0;
if (species != roamer->species) sPokedexAreaScreen->numSpecialAreas = 0;
// Check if this species should be hidden from the area map.
// This only applies to Wynaut, to hide the encounters on Mirage Island.
for (i = 0; i < ARRAY_COUNT(sSpeciesHiddenFromAreaScreen); i++)
{ {
sPokedexAreaScreen->numOverworldAreas = 0; if (sSpeciesHiddenFromAreaScreen[i] == species)
sPokedexAreaScreen->numSpecialAreas = 0; return;
}
// Check if this species should be hidden from the area map. // Add Pokémon with special encounter circumstances (i.e. not listed
// This only applies to Wynaut, to hide the encounters on Mirage Island. // in the regular wild encounter table) to the area map.
for (i = 0; i < ARRAY_COUNT(sSpeciesHiddenFromAreaScreen); i++) // This only applies to Feebas on Route 119, but it was clearly set
// up to allow handling others.
for (i = 0; sFeebasData[i][0] != NUM_SPECIES; i++)
{
if (species == sFeebasData[i][0])
{ {
if (sSpeciesHiddenFromAreaScreen[i] == species) switch (sFeebasData[i][1])
return;
}
// Add Pokémon with special encounter circumstances (i.e. not listed
// in the regular wild encounter table) to the area map.
// This only applies to Feebas on Route 119, but it was clearly set
// up to allow handling others.
for (i = 0; sFeebasData[i][0] != NUM_SPECIES; i++)
{
if (species == sFeebasData[i][0])
{ {
switch (sFeebasData[i][1]) case MAP_GROUP_TOWNS_AND_ROUTES:
{ SetAreaHasMon(sFeebasData[i][1], sFeebasData[i][2]);
case MAP_GROUP_TOWNS_AND_ROUTES: break;
SetAreaHasMon(sFeebasData[i][1], sFeebasData[i][2]); case MAP_GROUP_DUNGEONS:
break; case MAP_GROUP_SPECIAL_AREA:
case MAP_GROUP_DUNGEONS: SetSpecialMapHasMon(sFeebasData[i][1], sFeebasData[i][2]);
case MAP_GROUP_SPECIAL_AREA: break;
SetSpecialMapHasMon(sFeebasData[i][1], sFeebasData[i][2]);
break;
}
}
}
// Add regular species to the area map
for (i = 0; gWildMonHeaders[i].mapGroup != MAP_GROUP(UNDEFINED); i++)
{
if (MapHasSpecies(&gWildMonHeaders[i], species))
{
switch (gWildMonHeaders[i].mapGroup)
{
case MAP_GROUP_TOWNS_AND_ROUTES:
SetAreaHasMon(gWildMonHeaders[i].mapGroup, gWildMonHeaders[i].mapNum);
break;
case MAP_GROUP_DUNGEONS:
case MAP_GROUP_SPECIAL_AREA:
SetSpecialMapHasMon(gWildMonHeaders[i].mapGroup, gWildMonHeaders[i].mapNum);
break;
}
} }
} }
} }
else
// Add regular species to the area map
for (i = 0; gWildMonHeaders[i].mapGroup != MAP_GROUP(UNDEFINED); i++)
{ {
// This is the roamer's species, show where the roamer is currently if (MapHasSpecies(&gWildMonHeaders[i], species))
sPokedexAreaScreen->numSpecialAreas = 0;
if (roamer->active)
{ {
GetRoamerLocation(&sPokedexAreaScreen->overworldAreasWithMons[0].mapGroup, &sPokedexAreaScreen->overworldAreasWithMons[0].mapNum); switch (gWildMonHeaders[i].mapGroup)
sPokedexAreaScreen->overworldAreasWithMons[0].regionMapSectionId = Overworld_GetMapHeaderByGroupAndId(sPokedexAreaScreen->overworldAreasWithMons[0].mapGroup, sPokedexAreaScreen->overworldAreasWithMons[0].mapNum)->regionMapSectionId; {
sPokedexAreaScreen->numOverworldAreas = 1; case MAP_GROUP_TOWNS_AND_ROUTES:
SetAreaHasMon(gWildMonHeaders[i].mapGroup, gWildMonHeaders[i].mapNum);
break;
case MAP_GROUP_DUNGEONS:
case MAP_GROUP_SPECIAL_AREA:
SetSpecialMapHasMon(gWildMonHeaders[i].mapGroup, gWildMonHeaders[i].mapNum);
break;
}
} }
else }
// Add roamers to the area map
for (i = 0; i < ROAMER_COUNT; i++)
{
roamer = &gSaveBlock1Ptr->roamer[i];
if (species == roamer->species && roamer->active)
{ {
sPokedexAreaScreen->numOverworldAreas = 0; // This is a roamer's species, show where this roamer is currently
struct OverworldArea *roamerLocation = &sPokedexAreaScreen->overworldAreasWithMons[sPokedexAreaScreen->numOverworldAreas];
GetRoamerLocation(i, &roamerLocation->mapGroup, &roamerLocation->mapNum);
roamerLocation->regionMapSectionId = Overworld_GetMapHeaderByGroupAndId(roamerLocation->mapGroup, roamerLocation->mapNum)->regionMapSectionId;
sPokedexAreaScreen->numOverworldAreas++;
} }
} }
} }

View file

@ -14,9 +14,10 @@ enum
MAP_NUM, // map number MAP_NUM, // map number
}; };
#define ROAMER (&gSaveBlock1Ptr->roamer) #define ROAMER(index) (&gSaveBlock1Ptr->roamer[index])
EWRAM_DATA static u8 sLocationHistory[3][2] = {0}; EWRAM_DATA static u8 sLocationHistory[ROAMER_COUNT][3][2] = {0};
EWRAM_DATA static u8 sRoamerLocation[2] = {0}; EWRAM_DATA static u8 sRoamerLocation[ROAMER_COUNT][2] = {0};
EWRAM_DATA u8 gEncounteredRoamerIndex = 0;
#define ___ MAP_NUM(UNDEFINED) // For empty spots in the location table #define ___ MAP_NUM(UNDEFINED) // For empty spots in the location table
@ -61,121 +62,165 @@ static const u8 sRoamerLocations[][6] =
#define NUM_LOCATION_SETS (ARRAY_COUNT(sRoamerLocations) - 1) #define NUM_LOCATION_SETS (ARRAY_COUNT(sRoamerLocations) - 1)
#define NUM_LOCATIONS_PER_SET (ARRAY_COUNT(sRoamerLocations[0])) #define NUM_LOCATIONS_PER_SET (ARRAY_COUNT(sRoamerLocations[0]))
void ClearRoamerData(void) void DeactivateAllRoamers(void)
{ {
memset(ROAMER, 0, sizeof(*ROAMER)); u32 i;
ROAMER->species = SPECIES_LATIAS;
for (i = 0; i < ROAMER_COUNT; i++)
SetRoamerInactive(i);
} }
void ClearRoamerLocationData(void) static void ClearRoamerLocationHistory(u32 roamerIndex)
{ {
u8 i; u32 i;
for (i = 0; i < ARRAY_COUNT(sLocationHistory); i++) for (i = 0; i < ARRAY_COUNT(sLocationHistory[roamerIndex]); i++)
{ {
sLocationHistory[i][MAP_GRP] = 0; sLocationHistory[roamerIndex][i][MAP_GRP] = 0;
sLocationHistory[i][MAP_NUM] = 0; sLocationHistory[roamerIndex][i][MAP_NUM] = 0;
}
}
void MoveAllRoamersToOtherLocationSets(void)
{
u32 i;
for (i = 0; i < ROAMER_COUNT; i++)
RoamerMoveToOtherLocationSet(i);
}
void MoveAllRoamers(void)
{
u32 i;
for (i = 0; i < ROAMER_COUNT; i++)
RoamerMove(i);
}
static void CreateInitialRoamerMon(u8 index, u16 species, u8 level)
{
ClearRoamerLocationHistory(index);
CreateMon(&gEnemyParty[0], species, level, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0);
ROAMER(index)->ivs = GetMonData(&gEnemyParty[0], MON_DATA_IVS);
ROAMER(index)->personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY);
ROAMER(index)->species = species;
ROAMER(index)->level = level;
ROAMER(index)->status = 0;
ROAMER(index)->hp = GetMonData(&gEnemyParty[0], MON_DATA_MAX_HP);
ROAMER(index)->cool = GetMonData(&gEnemyParty[0], MON_DATA_COOL);
ROAMER(index)->beauty = GetMonData(&gEnemyParty[0], MON_DATA_BEAUTY);
ROAMER(index)->cute = GetMonData(&gEnemyParty[0], MON_DATA_CUTE);
ROAMER(index)->smart = GetMonData(&gEnemyParty[0], MON_DATA_SMART);
ROAMER(index)->tough = GetMonData(&gEnemyParty[0], MON_DATA_TOUGH);
ROAMER(index)->active = TRUE;
sRoamerLocation[index][MAP_GRP] = ROAMER_MAP_GROUP;
sRoamerLocation[index][MAP_NUM] = sRoamerLocations[Random() % NUM_LOCATION_SETS][0];
}
static u8 GetFirstInactiveRoamerIndex(void)
{
u32 i;
for (i = 0; i < ROAMER_COUNT; i++)
{
if (!ROAMER(i)->active)
return i;
}
return ROAMER_COUNT;
}
bool8 TryAddRoamer(u16 species, u8 level)
{
u8 index = GetFirstInactiveRoamerIndex();
if (index < ROAMER_COUNT)
{
// Create the roamer and stop searching
CreateInitialRoamerMon(index, species, level);
return TRUE;
} }
sRoamerLocation[MAP_GRP] = 0; // Maximum active roamers found: do nothing and let the calling function know
sRoamerLocation[MAP_NUM] = 0; return FALSE;
}
static void CreateInitialRoamerMon(bool16 createLatios)
{
if (!createLatios)
ROAMER->species = SPECIES_LATIAS;
else
ROAMER->species = SPECIES_LATIOS;
CreateMon(&gEnemyParty[0], ROAMER->species, 40, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0);
ROAMER->level = 40;
ROAMER->status = 0;
ROAMER->active = TRUE;
ROAMER->ivs = GetMonData(&gEnemyParty[0], MON_DATA_IVS);
ROAMER->personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY);
ROAMER->hp = GetMonData(&gEnemyParty[0], MON_DATA_MAX_HP);
ROAMER->cool = GetMonData(&gEnemyParty[0], MON_DATA_COOL);
ROAMER->beauty = GetMonData(&gEnemyParty[0], MON_DATA_BEAUTY);
ROAMER->cute = GetMonData(&gEnemyParty[0], MON_DATA_CUTE);
ROAMER->smart = GetMonData(&gEnemyParty[0], MON_DATA_SMART);
ROAMER->tough = GetMonData(&gEnemyParty[0], MON_DATA_TOUGH);
sRoamerLocation[MAP_GRP] = ROAMER_MAP_GROUP;
sRoamerLocation[MAP_NUM] = sRoamerLocations[Random() % NUM_LOCATION_SETS][0];
} }
// gSpecialVar_0x8004 here corresponds to the options in the multichoice MULTI_TV_LATI (0 for 'Red', 1 for 'Blue') // gSpecialVar_0x8004 here corresponds to the options in the multichoice MULTI_TV_LATI (0 for 'Red', 1 for 'Blue')
void InitRoamer(void) void InitRoamer(void)
{ {
ClearRoamerData(); if (gSpecialVar_0x8004 == 0) // Red
ClearRoamerLocationData(); TryAddRoamer(SPECIES_LATIAS, 40);
CreateInitialRoamerMon(gSpecialVar_0x8004); else
TryAddRoamer(SPECIES_LATIOS, 40);
} }
void UpdateLocationHistoryForRoamer(void) void UpdateLocationHistoryForRoamer(void)
{ {
sLocationHistory[2][MAP_GRP] = sLocationHistory[1][MAP_GRP]; u32 i;
sLocationHistory[2][MAP_NUM] = sLocationHistory[1][MAP_NUM];
sLocationHistory[1][MAP_GRP] = sLocationHistory[0][MAP_GRP]; for (i = 0; i < ROAMER_COUNT; i++)
sLocationHistory[1][MAP_NUM] = sLocationHistory[0][MAP_NUM];
sLocationHistory[0][MAP_GRP] = gSaveBlock1Ptr->location.mapGroup;
sLocationHistory[0][MAP_NUM] = gSaveBlock1Ptr->location.mapNum;
}
void RoamerMoveToOtherLocationSet(void)
{
u8 mapNum = 0;
if (!ROAMER->active)
return;
sRoamerLocation[MAP_GRP] = ROAMER_MAP_GROUP;
// Choose a location set that starts with a map
// different from the roamer's current map
while (1)
{ {
mapNum = sRoamerLocations[Random() % NUM_LOCATION_SETS][0]; sLocationHistory[i][2][MAP_GRP] = sLocationHistory[i][1][MAP_GRP];
if (sRoamerLocation[MAP_NUM] != mapNum) sLocationHistory[i][2][MAP_NUM] = sLocationHistory[i][1][MAP_NUM];
{
sRoamerLocation[MAP_NUM] = mapNum; sLocationHistory[i][1][MAP_GRP] = sLocationHistory[i][0][MAP_GRP];
return; sLocationHistory[i][1][MAP_NUM] = sLocationHistory[i][0][MAP_NUM];
}
sLocationHistory[i][0][MAP_GRP] = gSaveBlock1Ptr->location.mapGroup;
sLocationHistory[i][0][MAP_NUM] = gSaveBlock1Ptr->location.mapNum;
} }
} }
void RoamerMove(void) void RoamerMoveToOtherLocationSet(u32 roamerIndex)
{
u8 mapNum = 0;
if (!ROAMER(roamerIndex)->active)
return;
sRoamerLocation[roamerIndex][MAP_GRP] = ROAMER_MAP_GROUP;
// Choose a location set that starts with a map
// different from the roamer's current map
do
{
mapNum = sRoamerLocations[Random() % NUM_LOCATION_SETS][0];
if (sRoamerLocation[roamerIndex][MAP_NUM] != mapNum)
{
sRoamerLocation[roamerIndex][MAP_NUM] = mapNum;
return;
}
} while (sRoamerLocation[roamerIndex][MAP_NUM] == mapNum);
sRoamerLocation[roamerIndex][MAP_NUM] = mapNum;
}
void RoamerMove(u32 roamerIndex)
{ {
u8 locSet = 0; u8 locSet = 0;
if ((Random() % 16) == 0) if ((Random() % 16) == 0)
{ {
RoamerMoveToOtherLocationSet(); RoamerMoveToOtherLocationSet(roamerIndex);
} }
else else
{ {
if (!ROAMER->active) if (!ROAMER(roamerIndex)->active)
return; return;
while (locSet < NUM_LOCATION_SETS) while (locSet < NUM_LOCATION_SETS)
{ {
// Find the location set that starts with the roamer's current map // Find the location set that starts with the roamer's current map
if (sRoamerLocation[MAP_NUM] == sRoamerLocations[locSet][0]) if (sRoamerLocation[roamerIndex][MAP_NUM] == sRoamerLocations[locSet][0])
{ {
u8 mapNum; u8 mapNum;
while (1) // Choose a new map (excluding the first) within this set
// Also exclude a map if the roamer was there 2 moves ago
do
{ {
// Choose a new map (excluding the first) within this set
// Also exclude a map if the roamer was there 2 moves ago
mapNum = sRoamerLocations[locSet][(Random() % (NUM_LOCATIONS_PER_SET - 1)) + 1]; mapNum = sRoamerLocations[locSet][(Random() % (NUM_LOCATIONS_PER_SET - 1)) + 1];
if (!(sLocationHistory[2][MAP_GRP] == ROAMER_MAP_GROUP } while ((sLocationHistory[roamerIndex][2][MAP_GRP] == ROAMER_MAP_GROUP
&& sLocationHistory[2][MAP_NUM] == mapNum) && sLocationHistory[roamerIndex][2][MAP_NUM] == mapNum)
&& mapNum != MAP_NUM(UNDEFINED)) || mapNum == MAP_NUM(UNDEFINED));
break; sRoamerLocation[roamerIndex][MAP_NUM] = mapNum;
}
sRoamerLocation[MAP_NUM] = mapNum;
return; return;
} }
locSet++; locSet++;
@ -183,64 +228,67 @@ void RoamerMove(void)
} }
} }
bool8 IsRoamerAt(u8 mapGroup, u8 mapNum) bool8 IsRoamerAt(u32 roamerIndex, u8 mapGroup, u8 mapNum)
{ {
if (ROAMER->active && mapGroup == sRoamerLocation[MAP_GRP] && mapNum == sRoamerLocation[MAP_NUM]) if (ROAMER(roamerIndex)->active && mapGroup == sRoamerLocation[roamerIndex][MAP_GRP] && mapNum == sRoamerLocation[roamerIndex][MAP_NUM])
return TRUE; return TRUE;
else else
return FALSE; return FALSE;
} }
void CreateRoamerMonInstance(void) void CreateRoamerMonInstance(u32 roamerIndex)
{ {
u32 status; u32 status;
struct Pokemon *mon = &gEnemyParty[0]; struct Pokemon *mon = &gEnemyParty[0];
ZeroEnemyPartyMons(); ZeroEnemyPartyMons();
CreateMonWithIVsPersonality(mon, ROAMER->species, ROAMER->level, ROAMER->ivs, ROAMER->personality); CreateMonWithIVsPersonality(mon, ROAMER(roamerIndex)->species, ROAMER(roamerIndex)->level, ROAMER(roamerIndex)->ivs, ROAMER(roamerIndex)->personality);
// The roamer's status field is u8, but SetMonData expects status to be u32, so will set the roamer's status // The roamer's status field is u16, but SetMonData expects status to be u32, so will set the roamer's status
// using the status field and the following 3 bytes (cool, beauty, and cute). // using the status field and the following 3 bytes (cool, beauty, and cute).
#ifdef BUGFIX #ifdef BUGFIX
status = ROAMER->status; status = ROAMER(roamerIndex)->status;
SetMonData(mon, MON_DATA_STATUS, &status); SetMonData(mon, MON_DATA_STATUS, &status);
#else #else
SetMonData(mon, MON_DATA_STATUS, &ROAMER->status); SetMonData(mon, MON_DATA_STATUS, &ROAMER->status);
#endif #endif
SetMonData(mon, MON_DATA_HP, &ROAMER->hp); SetMonData(mon, MON_DATA_HP, &ROAMER(roamerIndex)->hp);
SetMonData(mon, MON_DATA_COOL, &ROAMER->cool); SetMonData(mon, MON_DATA_COOL, &ROAMER(roamerIndex)->cool);
SetMonData(mon, MON_DATA_BEAUTY, &ROAMER->beauty); SetMonData(mon, MON_DATA_BEAUTY, &ROAMER(roamerIndex)->beauty);
SetMonData(mon, MON_DATA_CUTE, &ROAMER->cute); SetMonData(mon, MON_DATA_CUTE, &ROAMER(roamerIndex)->cute);
SetMonData(mon, MON_DATA_SMART, &ROAMER->smart); SetMonData(mon, MON_DATA_SMART, &ROAMER(roamerIndex)->smart);
SetMonData(mon, MON_DATA_TOUGH, &ROAMER->tough); SetMonData(mon, MON_DATA_TOUGH, &ROAMER(roamerIndex)->tough);
} }
bool8 TryStartRoamerEncounter(void) bool8 TryStartRoamerEncounter(void)
{ {
if (IsRoamerAt(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum) == TRUE && (Random() % 4) == 0) u32 i;
for (i = 0; i < ROAMER_COUNT; i++)
{ {
CreateRoamerMonInstance(); if (IsRoamerAt(i, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum) == TRUE && (Random() % 4) == 0)
return TRUE; {
} CreateRoamerMonInstance(i);
else gEncounteredRoamerIndex = i;
{ return TRUE;
return FALSE; }
} }
return FALSE;
} }
void UpdateRoamerHPStatus(struct Pokemon *mon) void UpdateRoamerHPStatus(struct Pokemon *mon)
{ {
ROAMER->hp = GetMonData(mon, MON_DATA_HP); ROAMER(gEncounteredRoamerIndex)->hp = GetMonData(mon, MON_DATA_HP);
ROAMER->status = GetMonData(mon, MON_DATA_STATUS); ROAMER(gEncounteredRoamerIndex)->status = GetMonData(mon, MON_DATA_STATUS);
RoamerMoveToOtherLocationSet(); RoamerMoveToOtherLocationSet(gEncounteredRoamerIndex);
} }
void SetRoamerInactive(void) void SetRoamerInactive(u32 roamerIndex)
{ {
ROAMER->active = FALSE; ROAMER(roamerIndex)->active = FALSE;
} }
void GetRoamerLocation(u8 *mapGroup, u8 *mapNum) void GetRoamerLocation(u32 roamerIndex, u8 *mapGroup, u8 *mapNum)
{ {
*mapGroup = sRoamerLocation[MAP_GRP]; *mapGroup = sRoamerLocation[roamerIndex][MAP_GRP];
*mapNum = sRoamerLocation[MAP_NUM]; *mapNum = sRoamerLocation[roamerIndex][MAP_NUM];
} }

View file

@ -680,9 +680,9 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior)
else if (WildEncounterCheck(gWildMonHeaders[headerId].landMonsInfo->encounterRate, FALSE) != TRUE) else if (WildEncounterCheck(gWildMonHeaders[headerId].landMonsInfo->encounterRate, FALSE) != TRUE)
return FALSE; return FALSE;
if (TryStartRoamerEncounter() == TRUE) if (TryStartRoamerEncounter())
{ {
roamer = &gSaveBlock1Ptr->roamer; roamer = &gSaveBlock1Ptr->roamer[gEncounteredRoamerIndex];
if (!IsWildLevelAllowedByRepel(roamer->level)) if (!IsWildLevelAllowedByRepel(roamer->level))
return FALSE; return FALSE;
@ -729,9 +729,9 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior)
else if (WildEncounterCheck(gWildMonHeaders[headerId].waterMonsInfo->encounterRate, FALSE) != TRUE) else if (WildEncounterCheck(gWildMonHeaders[headerId].waterMonsInfo->encounterRate, FALSE) != TRUE)
return FALSE; return FALSE;
if (TryStartRoamerEncounter() == TRUE) if (TryStartRoamerEncounter())
{ {
roamer = &gSaveBlock1Ptr->roamer; roamer = &gSaveBlock1Ptr->roamer[gEncounteredRoamerIndex];
if (!IsWildLevelAllowedByRepel(roamer->level)) if (!IsWildLevelAllowedByRepel(roamer->level))
return FALSE; return FALSE;
@ -831,7 +831,7 @@ bool8 SweetScentWildEncounter(void)
if (gWildMonHeaders[headerId].landMonsInfo == NULL) if (gWildMonHeaders[headerId].landMonsInfo == NULL)
return FALSE; return FALSE;
if (TryStartRoamerEncounter() == TRUE) if (TryStartRoamerEncounter())
{ {
BattleSetup_StartRoamerBattle(); BattleSetup_StartRoamerBattle();
return TRUE; return TRUE;
@ -852,7 +852,7 @@ bool8 SweetScentWildEncounter(void)
if (gWildMonHeaders[headerId].waterMonsInfo == NULL) if (gWildMonHeaders[headerId].waterMonsInfo == NULL)
return FALSE; return FALSE;
if (TryStartRoamerEncounter() == TRUE) if (TryStartRoamerEncounter())
{ {
BattleSetup_StartRoamerBattle(); BattleSetup_StartRoamerBattle();
return TRUE; return TRUE;