#include "global.h" #include "field_specials.h" #include "event_data.h" #include "wild_encounter.h" #include "roamer.h" #include "overworld.h" #include "pokedex.h" #include "pokedex_area_markers.h" #include "constants/region_map_sections.h" #include "constants/maps.h" struct RoamerPair { u16 roamer; u16 starter; }; static s32 GetRoamerIndex(u16 species); static s32 GetRoamerPokedexAreaMarkers(u16 species, struct Subsprite * subsprites); static bool32 IsSpeciesOnMap(const struct WildPokemonHeader * data, s32 species); static bool32 IsSpeciesInEncounterTable(const struct WildPokemonInfo * pokemon, s32 species, s32 count); static u16 GetMapSecIdFromWildMonHeader(const struct WildPokemonHeader * header); static bool32 FindDexAreaByMapSec(u16 mapSecId, const u16 (*lut)[2], s32 count, s32 * lutIdx_p, u16 * tableIdx_p); static const u16 sDexAreas_Kanto[][2] = { { MAPSEC_PALLET_TOWN, DEX_AREA_PALLET_TOWN }, { MAPSEC_VIRIDIAN_CITY, DEX_AREA_VIRIDIAN_CITY }, { MAPSEC_PEWTER_CITY, DEX_AREA_PEWTER_CITY }, { MAPSEC_CERULEAN_CITY, DEX_AREA_CERULEAN_CITY }, { MAPSEC_LAVENDER_TOWN, DEX_AREA_LAVENDER_TOWN }, { MAPSEC_VERMILION_CITY, DEX_AREA_VERMILION_CITY }, { MAPSEC_CELADON_CITY, DEX_AREA_CELADON_CITY }, { MAPSEC_FUCHSIA_CITY, DEX_AREA_FUCHSIA_CITY }, { MAPSEC_CINNABAR_ISLAND, DEX_AREA_CINNABAR_ISLAND }, { MAPSEC_INDIGO_PLATEAU, DEX_AREA_INDIGO_PLATEAU }, { MAPSEC_SAFFRON_CITY, DEX_AREA_SAFFRON_CITY }, { MAPSEC_ROUTE_4_POKECENTER, DEX_AREA_ROUTE_4 }, { MAPSEC_ROUTE_10_POKECENTER, DEX_AREA_ROUTE_10 }, { MAPSEC_ROUTE_1, DEX_AREA_ROUTE_1 }, { MAPSEC_ROUTE_2, DEX_AREA_ROUTE_2 }, { MAPSEC_ROUTE_3, DEX_AREA_ROUTE_3 }, { MAPSEC_ROUTE_4, DEX_AREA_ROUTE_4 }, { MAPSEC_ROUTE_5, DEX_AREA_ROUTE_5 }, { MAPSEC_ROUTE_6, DEX_AREA_ROUTE_6 }, { MAPSEC_ROUTE_7, DEX_AREA_ROUTE_7 }, { MAPSEC_ROUTE_8, DEX_AREA_ROUTE_8 }, { MAPSEC_ROUTE_9, DEX_AREA_ROUTE_9 }, { MAPSEC_ROUTE_10, DEX_AREA_ROUTE_10 }, { MAPSEC_ROUTE_11, DEX_AREA_ROUTE_11 }, { MAPSEC_ROUTE_12, DEX_AREA_ROUTE_12 }, { MAPSEC_ROUTE_13, DEX_AREA_ROUTE_13 }, { MAPSEC_ROUTE_14, DEX_AREA_ROUTE_14 }, { MAPSEC_ROUTE_15, DEX_AREA_ROUTE_15 }, { MAPSEC_ROUTE_16, DEX_AREA_ROUTE_16 }, { MAPSEC_ROUTE_17, DEX_AREA_ROUTE_17 }, { MAPSEC_ROUTE_18, DEX_AREA_ROUTE_18 }, { MAPSEC_ROUTE_19, DEX_AREA_ROUTE_19 }, { MAPSEC_ROUTE_20, DEX_AREA_ROUTE_20 }, { MAPSEC_ROUTE_21, DEX_AREA_ROUTE_21 }, { MAPSEC_ROUTE_22, DEX_AREA_ROUTE_22 }, { MAPSEC_ROUTE_23, DEX_AREA_ROUTE_23 }, { MAPSEC_ROUTE_24, DEX_AREA_ROUTE_24 }, { MAPSEC_ROUTE_25, DEX_AREA_ROUTE_25 }, { MAPSEC_VIRIDIAN_FOREST, DEX_AREA_VIRIDIAN_FOREST }, { MAPSEC_MT_MOON, DEX_AREA_MT_MOON }, { MAPSEC_S_S_ANNE, DEX_AREA_VERMILION_CITY }, { MAPSEC_UNDERGROUND_PATH, DEX_AREA_SAFFRON_CITY }, { MAPSEC_UNDERGROUND_PATH_2, DEX_AREA_SAFFRON_CITY }, { MAPSEC_DIGLETTS_CAVE, DEX_AREA_DIGLETTS_CAVE }, { MAPSEC_KANTO_VICTORY_ROAD, DEX_AREA_VICTORY_ROAD }, { MAPSEC_ROCKET_HIDEOUT, DEX_AREA_CELADON_CITY }, { MAPSEC_SILPH_CO, DEX_AREA_SAFFRON_CITY }, { MAPSEC_POKEMON_MANSION, DEX_AREA_POKEMON_MANSION }, { MAPSEC_KANTO_SAFARI_ZONE, DEX_AREA_SAFARI_ZONE }, { MAPSEC_POKEMON_LEAGUE, DEX_AREA_VICTORY_ROAD }, { MAPSEC_ROCK_TUNNEL, DEX_AREA_ROCK_TUNNEL }, { MAPSEC_SEAFOAM_ISLANDS, DEX_AREA_SEAFOAM_ISLANDS }, { MAPSEC_POKEMON_TOWER, DEX_AREA_POKEMON_TOWER }, { MAPSEC_CERULEAN_CAVE, DEX_AREA_CERULEAN_CAVE }, { MAPSEC_POWER_PLANT, DEX_AREA_POWER_PLANT } }; static const u16 sDexAreas_Sevii1[][2] = { { MAPSEC_KINDLE_ROAD, DEX_AREA_KINDLE_ROAD }, { MAPSEC_TREASURE_BEACH, DEX_AREA_TREASURE_BEACH }, { MAPSEC_ONE_ISLAND, DEX_AREA_ONE_ISLAND }, { MAPSEC_MT_EMBER, DEX_AREA_MT_EMBER } }; static const u16 sDexAreas_Sevii2[][2] = { { MAPSEC_CAPE_BRINK, DEX_AREA_CAPE_BRINK }, { MAPSEC_TWO_ISLAND, DEX_AREA_TWO_ISLAND } }; static const u16 sDexAreas_Sevii3[][2] = { { MAPSEC_BOND_BRIDGE, DEX_AREA_BOND_BRIDGE }, { MAPSEC_THREE_ISLE_PORT, DEX_AREA_THREE_ISLE_PATH }, { MAPSEC_THREE_ISLAND, DEX_AREA_THREE_ISLAND }, { MAPSEC_BERRY_FOREST, DEX_AREA_BERRY_FOREST }, { MAPSEC_THREE_ISLE_PATH, DEX_AREA_THREE_ISLE_PATH } }; static const u16 sDexAreas_Sevii4[][2] = { { MAPSEC_FOUR_ISLAND, DEX_AREA_FOUR_ISLAND }, { MAPSEC_ICEFALL_CAVE, DEX_AREA_ICEFALL_CAVE } }; static const u16 sDexAreas_Sevii5[][2] = { { MAPSEC_RESORT_GORGEOUS, DEX_AREA_RESORT_GORGEOUS }, { MAPSEC_WATER_LABYRINTH, DEX_AREA_WATER_LABYRINTH }, { MAPSEC_FIVE_ISLE_MEADOW, DEX_AREA_FIVE_ISLE_MEADOW }, { MAPSEC_MEMORIAL_PILLAR, DEX_AREA_MEMORIAL_PILLAR }, { MAPSEC_FIVE_ISLAND, DEX_AREA_FIVE_ISLAND }, { MAPSEC_ROCKET_WAREHOUSE, DEX_AREA_FIVE_ISLE_MEADOW }, { MAPSEC_LOST_CAVE, DEX_AREA_LOST_CAVE } }; static const u16 sDexAreas_Sevii6[][2] = { { MAPSEC_OUTCAST_ISLAND, DEX_AREA_OUTCAST_ISLAND }, { MAPSEC_GREEN_PATH, DEX_AREA_GREEN_PATH }, { MAPSEC_WATER_PATH, DEX_AREA_WATER_PATH }, { MAPSEC_RUIN_VALLEY, DEX_AREA_RUIN_VALLEY }, { MAPSEC_DOTTED_HOLE, DEX_AREA_DOTTED_HOLE }, { MAPSEC_PATTERN_BUSH, DEX_AREA_PATTERN_BUSH }, { MAPSEC_ALTERING_CAVE, DEX_AREA_ALTERING_CAVE } }; static const u16 sDexAreas_Sevii7[][2] = { { MAPSEC_TRAINER_TOWER, DEX_AREA_TRAINER_TOWER }, { MAPSEC_CANYON_ENTRANCE, DEX_AREA_CANYON_ENTRANCE }, { MAPSEC_SEVAULT_CANYON, DEX_AREA_SEVAULT_CANYON }, { MAPSEC_TANOBY_RUINS, DEX_AREA_TANOBY_RUINS }, { MAPSEC_MONEAN_CHAMBER, DEX_AREA_TANOBY_CHAMBER }, { MAPSEC_LIPTOO_CHAMBER, DEX_AREA_TANOBY_CHAMBER }, { MAPSEC_WEEPTH_CHAMBER, DEX_AREA_TANOBY_CHAMBER }, { MAPSEC_DILFORD_CHAMBER, DEX_AREA_TANOBY_CHAMBER }, { MAPSEC_SCUFIB_CHAMBER, DEX_AREA_TANOBY_CHAMBER }, { MAPSEC_RIXY_CHAMBER, DEX_AREA_TANOBY_CHAMBER }, { MAPSEC_VIAPOIS_CHAMBER, DEX_AREA_TANOBY_CHAMBER } }; static const struct { const u16 (*table)[2]; s32 count; } sSeviiDexAreas[] = { { sDexAreas_Sevii1, ARRAY_COUNT(sDexAreas_Sevii1) }, { sDexAreas_Sevii2, ARRAY_COUNT(sDexAreas_Sevii2) }, { sDexAreas_Sevii3, ARRAY_COUNT(sDexAreas_Sevii3) }, { sDexAreas_Sevii4, ARRAY_COUNT(sDexAreas_Sevii4) }, { sDexAreas_Sevii5, ARRAY_COUNT(sDexAreas_Sevii5) }, { sDexAreas_Sevii6, ARRAY_COUNT(sDexAreas_Sevii6) }, { sDexAreas_Sevii7, ARRAY_COUNT(sDexAreas_Sevii7) } }; static const struct RoamerPair sRoamerPairs[] = { { SPECIES_ENTEI, SPECIES_BULBASAUR }, { SPECIES_SUICUNE, SPECIES_CHARMANDER }, { SPECIES_RAIKOU, SPECIES_SQUIRTLE } }; // Scans for the given species and populates 'subsprites' with the area markers. // Returns the number of areas where the species was found. s32 GetSpeciesPokedexAreaMarkers(u16 species, struct Subsprite * subsprites) { s32 areaCount; s32 j; s32 mapSecId; u16 dexArea; s32 tableIndex; s32 seviiAreas; s32 alteringCaveCount; s32 alteringCaveNum; s32 i; if (GetRoamerIndex(species) >= 0) return GetRoamerPokedexAreaMarkers(species, subsprites); seviiAreas = GetUnlockedSeviiAreas(); alteringCaveCount = 0; alteringCaveNum = VarGet(VAR_ALTERING_CAVE_WILD_SET); if (alteringCaveNum >= NUM_ALTERING_CAVE_TABLES) alteringCaveNum = 0; for (i = 0, areaCount = 0; gWildMonHeaders[i].mapGroup != MAP_GROUP(UNDEFINED); i++) { mapSecId = GetMapSecIdFromWildMonHeader(&gWildMonHeaders[i]); if (mapSecId == MAPSEC_ALTERING_CAVE) { alteringCaveCount++; if (alteringCaveNum != alteringCaveCount - 1) continue; } if (IsSpeciesOnMap(&gWildMonHeaders[i], species)) { // Search for all dex areas associated with this MAPSEC. // In the vanilla game each MAPSEC only has at most one DEX_AREA. tableIndex = 0; while (FindDexAreaByMapSec(mapSecId, sDexAreas_Kanto, ARRAY_COUNT(sDexAreas_Kanto), &tableIndex, &dexArea)) { if (dexArea != DEX_AREA_NONE) GetAreaMarkerSubsprite(areaCount++, dexArea, subsprites); } for (j = 0; j < ARRAY_COUNT(sSeviiDexAreas); j++) { if ((seviiAreas >> j) & 1) { // Search for all dex areas associated with this MAPSEC in this unlocked Sevii Island tableIndex = 0; while (FindDexAreaByMapSec(mapSecId, sSeviiDexAreas[j].table, sSeviiDexAreas[j].count, &tableIndex, &dexArea)) { if (dexArea != DEX_AREA_NONE) GetAreaMarkerSubsprite(areaCount++, dexArea, subsprites); } } } } } return areaCount; } static s32 GetRoamerIndex(u16 species) { s32 i; for (i = 0; i < ARRAY_COUNT(sRoamerPairs); i++) { if (sRoamerPairs[i].roamer == species) return i; } return -1; } static s32 GetRoamerPokedexAreaMarkers(u16 species, struct Subsprite * subsprites) { u16 mapSecId; s32 roamerIdx; u16 dexArea; s32 tableIndex; // Make sure that this is a roamer species, and that it corresponds to the player's starter. roamerIdx = GetRoamerIndex(species); if (roamerIdx < 0) return 0; if (sRoamerPairs[roamerIdx].starter != GetStarterSpecies()) return 0; mapSecId = GetRoamerLocationMapSectionId(); tableIndex = 0; if (FindDexAreaByMapSec(mapSecId, sDexAreas_Kanto, ARRAY_COUNT(sDexAreas_Kanto), &tableIndex, &dexArea)) { if (dexArea != DEX_AREA_NONE) { GetAreaMarkerSubsprite(0, dexArea, subsprites); return 1; } } return 0; } static bool32 IsSpeciesOnMap(const struct WildPokemonHeader * data, s32 species) { if (IsSpeciesInEncounterTable(data->landMonsInfo, species, LAND_WILD_COUNT)) return TRUE; if (IsSpeciesInEncounterTable(data->waterMonsInfo, species, WATER_WILD_COUNT)) return TRUE; // When searching the fishing encounters, this incorrectly uses the size of the land encounters. // As a result it's reading out of bounds of the fishing encounters tables. #ifdef BUGFIX if (IsSpeciesInEncounterTable(data->fishingMonsInfo, species, FISH_WILD_COUNT)) #else if (IsSpeciesInEncounterTable(data->fishingMonsInfo, species, LAND_WILD_COUNT)) #endif return TRUE; if (IsSpeciesInEncounterTable(data->rockSmashMonsInfo, species, ROCK_WILD_COUNT)) return TRUE; return FALSE; } static bool32 IsSpeciesInEncounterTable(const struct WildPokemonInfo * info, s32 species, s32 count) { s32 i; if (info != NULL) { for (i = 0; i < count; i++) { if (info->wildPokemon[i].species == species) return TRUE; } } return FALSE; } static u16 GetMapSecIdFromWildMonHeader(const struct WildPokemonHeader * header) { return Overworld_GetMapHeaderByGroupAndId(header->mapGroup, header->mapNum)->regionMapSectionId; } // Search a MAPSEC -> DEX_AREA table for the given mapsec. // Assigns the DEX_AREA (if found) to 'dexArea', and the first unread table index to 'index'. // Returns TRUE if DEX_AREA was found, FALSE otherwise. static bool32 FindDexAreaByMapSec(u16 mapSecId, const u16 (*table)[2], s32 count, s32 * index, u16 * dexArea) { s32 i; for (i = *index; i < count; i++) { if (table[i][0] == mapSecId) { *dexArea = table[i][1]; *index = i + 1; return TRUE; } } return FALSE; }