diff --git a/data/event_scripts.s b/data/event_scripts.s index c6692ebd9..8545cf621 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -1368,3 +1368,4 @@ Text_TestMsg:: .include "data/text/new_game_intro.inc" .include "data/text/pokedude.inc" .include "data/scripts/follower.inc" + .include "data/scripts/dexnav.inc" diff --git a/data/scripts/dexnav.inc b/data/scripts/dexnav.inc new file mode 100644 index 000000000..698f50193 --- /dev/null +++ b/data/scripts/dexnav.inc @@ -0,0 +1,47 @@ +EventScript_StartDexNavBattle:: + lock + playse SE_PIN + applymovement OBJ_EVENT_ID_PLAYER Common_Movement_ExclamationMark + waitmovement 0 + waitse + dowildbattle + release + end + +EventScript_NotFoundNearby:: + msgbox sText_NotFoundNearby, MSGBOX_SIGN + end + +EventScript_MovedTooFast:: + msgbox sText_TryMovingSlower, MSGBOX_SIGN + end + +EventScript_PokemonGotAway:: + msgbox sText_PokemonGotAway, MSGBOX_SIGN + end + +EventScript_LostSignal:: + msgbox sText_LostSignal, MSGBOX_SIGN + end + +EventScript_TooDark:: + msgbox sText_TooDark, MSGBOX_SIGN + end + +sText_NotFoundNearby: + .string "It couldn't be found nearby.\n" + .string "Try looking in a different area!$" + +sText_TryMovingSlower: + .string "The Pokémon got away!\n" + .string "Try moving more slowly.$" + +sText_PokemonGotAway: + .string "The Pokémon got away!$" + +sText_LostSignal: + .string "There is no reaction.\n" + .string "The signal was lost!$" + +sText_TooDark: + .string "It's too dark to search\nfor a Pokémon!$" diff --git a/graphics/dexnav/captured_all.png b/graphics/dexnav/captured_all.png new file mode 100644 index 000000000..2ed8b40ca Binary files /dev/null and b/graphics/dexnav/captured_all.png differ diff --git a/graphics/dexnav/cursor.png b/graphics/dexnav/cursor.png new file mode 100644 index 000000000..0b64cf95b Binary files /dev/null and b/graphics/dexnav/cursor.png differ diff --git a/graphics/dexnav/gui.pal b/graphics/dexnav/gui.pal new file mode 100644 index 000000000..77939a5ab --- /dev/null +++ b/graphics/dexnav/gui.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +255 255 255 +217 73 73 +4 4 4 +1 81 113 +1 121 193 +119 177 75 +93 97 101 +91 179 211 +153 32 32 +111 141 81 +173 173 173 +187 217 167 +75 147 189 +177 219 235 +105 22 22 diff --git a/graphics/dexnav/gui_tilemap.bin b/graphics/dexnav/gui_tilemap.bin new file mode 100644 index 000000000..0f62abbef Binary files /dev/null and b/graphics/dexnav/gui_tilemap.bin differ diff --git a/graphics/dexnav/gui_tiles.png b/graphics/dexnav/gui_tiles.png new file mode 100644 index 000000000..4e3327b8e Binary files /dev/null and b/graphics/dexnav/gui_tiles.png differ diff --git a/graphics/dexnav/hidden.png b/graphics/dexnav/hidden.png new file mode 100644 index 000000000..2ad2e22e0 Binary files /dev/null and b/graphics/dexnav/hidden.png differ diff --git a/graphics/dexnav/hidden_search.png b/graphics/dexnav/hidden_search.png new file mode 100644 index 000000000..d462ddef9 Binary files /dev/null and b/graphics/dexnav/hidden_search.png differ diff --git a/graphics/dexnav/no_data.png b/graphics/dexnav/no_data.png new file mode 100644 index 000000000..a26515b5b Binary files /dev/null and b/graphics/dexnav/no_data.png differ diff --git a/graphics/dexnav/owned_icon.png b/graphics/dexnav/owned_icon.png new file mode 100644 index 000000000..fdc5ef711 Binary files /dev/null and b/graphics/dexnav/owned_icon.png differ diff --git a/graphics/dexnav/star.png b/graphics/dexnav/star.png new file mode 100644 index 000000000..5d4b034a4 Binary files /dev/null and b/graphics/dexnav/star.png differ diff --git a/graphics/dexnav/vision.png b/graphics/dexnav/vision.png new file mode 100644 index 000000000..74b6a14ee Binary files /dev/null and b/graphics/dexnav/vision.png differ diff --git a/graphics/field_effects/palettes/cave_dust.pal b/graphics/field_effects/palettes/cave_dust.pal new file mode 100644 index 000000000..ed31236c4 --- /dev/null +++ b/graphics/field_effects/palettes/cave_dust.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +255 1 255 +159 122 85 +207 189 157 +199 181 149 +114 88 61 +132 101 70 +199 173 141 +225 209 193 +189 165 133 +181 149 115 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/field_effects/pics/cave_dust.png b/graphics/field_effects/pics/cave_dust.png new file mode 100644 index 000000000..1f477c780 Binary files /dev/null and b/graphics/field_effects/pics/cave_dust.png differ diff --git a/graphics/text_window/dexnav_pal.pal b/graphics/text_window/dexnav_pal.pal new file mode 100644 index 000000000..cb6acc371 --- /dev/null +++ b/graphics/text_window/dexnav_pal.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 6 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +46 46 46 +0 0 0 +255 255 255 diff --git a/graphics_file_rules.mk b/graphics_file_rules.mk index 814d2184a..e027f2ce0 100644 --- a/graphics_file_rules.mk +++ b/graphics_file_rules.mk @@ -33,7 +33,7 @@ NAMINGGFXDIR := graphics/naming_screen WALLPAPERGFXDIR := graphics/pokemon_storage/wallpapers JPCONTESTGFXDIR := graphics/contest/japanese -types := normal fight flying poison ground rock bug ghost steel mystery fire water grass electric psychic ice dragon dark +types := none normal fight flying poison ground rock bug ghost steel mystery fire water grass electric psychic ice dragon dark contest_types := cool beauty cute smart tough diff --git a/include/bg.h b/include/bg.h index 0862a1a6a..0b879b8ad 100644 --- a/include/bg.h +++ b/include/bg.h @@ -91,7 +91,7 @@ u16 GetBgAttribute(u8 bg, u8 attributeId); u32 ChangeBgX(u8 bg, u32 value, u8 op); u32 GetBgX(u8 bg); u32 ChangeBgY(u8 bg, u32 value, u8 op); -u32 ChangeBgY_ScreenOff(u8 bg, u32 value, u8 op); +s32 ChangeBgY_ScreenOff(u32 bg, s32 value, u8 op); u32 GetBgY(u8 bg); void SetBgAffine(u8 bg, u32 srcCenterX, u32 srcCenterY, s16 dispCenterX, s16 dispCenterY, s16 scaleX, s16 scaleY, u16 rotationAngle); u8 AdjustBgMosaic(u8 value, u8 mode); diff --git a/include/config/dexnav.h b/include/config/dexnav.h new file mode 100644 index 000000000..7b4e3ba28 --- /dev/null +++ b/include/config/dexnav.h @@ -0,0 +1,72 @@ +#ifndef GUARD_CONFIG_DEXNAV_H +#define GUARD_CONFIG_DEXNAV_H + +#define DEXNAV_ENABLED FALSE // Whether or not DexNav is enabled. If TRUE, flags/vars below must all be non-zero +#define USE_DEXNAV_SEARCH_LEVELS FALSE /* WARNING: POSSIBLY EXCEEDS SAVEBLOCK SPACE! REQUIRES 1 BYTE PER SPECIES */ + +// Flag/var defines +#define DN_FLAG_SEARCHING 0 // Searching for mon +#define DN_FLAG_DEXNAV_GET 0 // DexNav shows in start menu +#define DN_FLAG_DETECTOR_MODE 0 // Allow player to find hidden mons +#define DN_VAR_SPECIES 0 // Registered DexNav species +#define DN_VAR_STEP_COUNTER 0 // Steps for finding hidden pokemon + +// Search parameters +#define DEXNAV_TIMEOUT 15 // 15 seconds is the time out. Max of 1092 seconds allowed +#define SNEAKING_PROXIMITY 4 // Tile amount +#define CREEPING_PROXIMITY 2 +#define MAX_PROXIMITY 20 + +#define DEXNAV_CHAIN_MAX 100 // maximum chain value + +// hidden pokemon options - an approximation of values to due to lack of available data +#define HIDDEN_MON_STEP_COUNT 100 // Look for hidden pokemon every x steps +#define HIDDEN_MON_SEARCH_RATE 25 // x% chance of finding hidden pokemon every x steps +#define HIDDEN_MON_PROBABILTY 15 // x% chance of finding hidden mon compared to regular encounter data + +//// SEARCH PROBABILITIES +// See https://bulbapedia.bulbagarden.net/wiki/DexNav#Benefits +// Chance of encountering egg move at search levels +#define SEARCHLEVEL0_MOVECHANCE 0 +#define SEARCHLEVEL5_MOVECHANCE 21 +#define SEARCHLEVEL10_MOVECHANCE 46 +#define SEARCHLEVEL25_MOVECHANCE 58 +#define SEARCHLEVEL50_MOVECHANCE 63 +#define SEARCHLEVEL100_MOVECHANCE 83 +// Chance of encountering Hidden Abilities at search levels +#define SEARCHLEVEL0_ABILITYCHANCE 0 +#define SEARCHLEVEL5_ABILITYCHANCE 0 +#define SEARCHLEVEL10_ABILITYCHANCE 5 +#define SEARCHLEVEL25_ABILITYCHANCE 15 +#define SEARCHLEVEL50_ABILITYCHANCE 20 +#define SEARCHLEVEL100_ABILITYCHANCE 23 +// Chance of encountering held item +#define SEARCHLEVEL0_ITEM 0 +#define SEARCHLEVEL5_ITEM 0 +#define SEARCHLEVEL10_ITEM 1 +#define SEARCHLEVEL25_ITEM 7 +#define SEARCHLEVEL50_ITEM 6 +#define SEARCHLEVEL100_ITEM 12 +// Chance of encountering one star potential +#define SEARCHLEVEL0_ONESTAR 0 +#define SEARCHLEVEL5_ONESTAR 14 +#define SEARCHLEVEL10_ONESTAR 17 +#define SEARCHLEVEL25_ONESTAR 17 +#define SEARCHLEVEL50_ONESTAR 15 +#define SEARCHLEVEL100_ONESTAR 8 +// Chance of encountering two star potential +#define SEARCHLEVEL0_TWOSTAR 0 +#define SEARCHLEVEL5_TWOSTAR 1 +#define SEARCHLEVEL10_TWOSTAR 9 +#define SEARCHLEVEL25_TWOSTAR 16 +#define SEARCHLEVEL50_TWOSTAR 17 +#define SEARCHLEVEL100_TWOSTAR 24 +// Chance of encountering three star potential +#define SEARCHLEVEL0_THREESTAR 0 +#define SEARCHLEVEL5_THREESTAR 0 +#define SEARCHLEVEL10_THREESTAR 1 +#define SEARCHLEVEL25_THREESTAR 7 +#define SEARCHLEVEL50_THREESTAR 6 +#define SEARCHLEVEL100_THREESTAR 12 + +#endif // GUARD_CONFIG_DEXNAV_H diff --git a/include/constants/field_effects.h b/include/constants/field_effects.h index 704d2cf08..3f7fdb207 100644 --- a/include/constants/field_effects.h +++ b/include/constants/field_effects.h @@ -24,9 +24,9 @@ enum FieldEffect FLDEFF_JUMP_SMALL_SPLASH, FLDEFF_LONG_GRASS, FLDEFF_JUMP_LONG_GRASS, - FLDEFF_UNUSED_GRASS, - FLDEFF_UNUSED_GRASS_2, - FLDEFF_UNUSED_SAND, + FLDEFF_SHAKING_GRASS, + FLDEFF_SHAKING_LONG_GRASS, + FLDEFF_SAND_HOLE, FLDEFF_UNUSED_WATER_SURFACING, FLDEFF_BERRY_TREE_GROWTH_SPARKLE, FLDEFF_DEEP_SAND_FOOTPRINTS, @@ -84,6 +84,7 @@ enum FieldEffect FLDEFF_SNOW_TRACKS_SLITHER, FLDEFF_SNOW_TRACKS_SPOT, FLDEFF_SNOW_TRACKS_BUG, + FLDEFF_CAVE_DUST, FLDEFF_COUNT, }; @@ -95,5 +96,6 @@ enum FieldEffect #define FLDEFF_PAL_TAG_POKEBALL_TRAIL 0x1009 #define FLDEFF_PAL_TAG_ASH 0x100D #define FLDEFF_PAL_TAG_SMALL_SPARKLE 0x100F +#define FLDEFF_PAL_TAG_CAVE_DUST 0x1012 #endif // GUARD_CONSTANTS_FIELD_EFFECTS_H diff --git a/include/constants/game_stat.h b/include/constants/game_stat.h index 234091bff..0d3745212 100644 --- a/include/constants/game_stat.h +++ b/include/constants/game_stat.h @@ -53,8 +53,9 @@ #define GAME_STAT_ENTERED_HOT_SPRINGS 49 #define GAME_STAT_NUM_UNION_ROOM_BATTLES 50 #define GAME_STAT_BERRY_CRUSH_POINTS 51 +#define GAME_STAT_DEXNAV_SCANNED 52 -#define NUM_USED_GAME_STATS 52 +#define NUM_USED_GAME_STATS 53 #define NUM_GAME_STATS 64 #endif // GUARD_CONSTANTS_GAME_STAT_H diff --git a/include/constants/global.h b/include/constants/global.h index 48b77a3ff..d197d83c0 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -8,6 +8,7 @@ #include "config/item.h" #include "config/pokemon.h" #include "config/overworld.h" +#include "config/dexnav.h" #include "config/ai.h" // Invalid Versions show as "----------" in Gen 4 and Gen 5's summary screen. diff --git a/include/daycare.h b/include/daycare.h index 07d1b0cc4..dfb30d94d 100644 --- a/include/daycare.h +++ b/include/daycare.h @@ -33,5 +33,6 @@ bool8 ShouldEggHatch(void); u8 GetEggCyclesToSubtract(void); u8 GetEggMovesBySpecies(u16 species, u16 *eggMoves); void StorePokemonInDaycare(struct Pokemon *mon, struct DaycareMon *daycareMon); +u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves); #endif // GUARD_DAYCARE_H diff --git a/include/dexnav.h b/include/dexnav.h new file mode 100644 index 000000000..7720104c5 --- /dev/null +++ b/include/dexnav.h @@ -0,0 +1,77 @@ +#ifndef GUARD_DEXNAV_H +#define GUARD_DEXNAV_H + +#include "config/dexnav.h" + +// GUI Info +#define ROW_WATER 0 +#define ROW_LAND_TOP 1 +#define ROW_LAND_BOT 2 +#define ROW_HIDDEN 3 +#define ROWS_COUNT 4 + +#define ROW_WATER_ICON_X 30 +#define ROW_WATER_ICON_Y 35 + +#define ROW_LAND_ICON_X 20 +#define ROW_LAND_TOP_ICON_Y 72 +#define ROW_LAND_BOT_ICON_Y (ROW_LAND_TOP_ICON_Y + 28) + +#define ROW_HIDDEN_ICON_X 52 +#define ROW_HIDDEN_ICON_Y 138 + +#define ENCOUNTER_TYPE_LAND 0 +#define ENCOUNTER_TYPE_WATER 1 +#define ENCOUNTER_TYPE_HIDDEN 2 // Get from species + +#define COL_WATER_COUNT 5 +#define COL_LAND_COUNT 6 +#define COL_HIDDEN_COUNT 3 + +#define COL_WATER_MAX (COL_WATER_COUNT - 1) +#define COL_LAND_MAX (COL_LAND_COUNT - 1) +#define COL_HIDDEN_MAX (COL_HIDDEN_COUNT - 1) + +// SEARCH INFO +#define SCANSTART_X 0 +#define SCANSTART_Y 0 +#define SCANSIZE_X 12 +#define SCANSIZE_Y 12 + +#define SPECIES_INFO_Y 5 +#define TYPE_ICONS_Y (SPECIES_INFO_Y + 24) +#define SEARCH_LEVEL_Y (TYPE_ICONS_Y + 24) +#define HA_INFO_Y (SEARCH_LEVEL_Y + 24) +#define CHAIN_BONUS_Y (HA_INFO_Y + 24) + +#define MON_LEVEL_NONEXISTENT 255 // If mon not in area GetEncounterLevel returns this to exit the search + +// GUI tags +#define ICON_PAL_TAG 56000 +#define ICON_GFX_TAG 55130 +#define SELECTION_CURSOR_TAG 0x4005 +#define CAPTURED_ALL_TAG 0x4002 + +// Search tags +#define OWNED_ICON_TAG 0x4003 +#define HIDDEN_SEARCH_TAG SELECTION_CURSOR_TAG +#define HIDDEN_MON_ICON_TAG 0x4006 +#define LIT_STAR_TILE_TAG 0x4010 +#define HELD_ITEM_TAG 0xd750 + +// DexNav search variable +#define DEXNAV_MASK_SPECIES 0x3FFF // First 14 bits +#define DEXNAV_MASK_ENVIRONMENT 0xC000 // Last two bit + +void EndDexNavSearch(u8 taskId); +void Task_OpenDexNavFromStartMenu(u8 taskId); +bool8 TryStartDexNavSearch(void); +void TryIncrementSpeciesSearchLevel(void); +void ResetDexNavSearch(void); +bool8 TryFindHiddenPokemon(void); +u32 CalculateDexNavShinyRolls(void); +void IncrementDexNavChain(void); + +extern u16 gDexNavSpecies; + +#endif // GUARD_DEXNAV_H diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 88415683a..84ddf818a 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -223,6 +223,7 @@ u8 GetJumpSpecialMovementAction(u32 direction); void EnableObjectGroundEffectsByXY(s16 x, s16 y); void MoveObjectEventToMapCoords(struct ObjectEvent *objectEvent, s16 x, s16 y); u8 CreateCopySpriteAt(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); +bool8 IsElevationMismatchAt(u8 elevation, s16 x, s16 y); u16 GetObjectPaletteTag(u8 paletteIndex); void SetSpritePosToMapCoords(s16 x, s16 y, s16 *x2, s16 *y2); void UpdateObjectEventSpriteInvisibility(struct Sprite *sprite, bool8 invisible); diff --git a/include/event_scripts.h b/include/event_scripts.h index 5c71bbbaf..aede58ada 100644 --- a/include/event_scripts.h +++ b/include/event_scripts.h @@ -1299,4 +1299,12 @@ extern const u8 TradeCenter_EventScript_Chair1[]; extern const u8 TradeCenter_ConfirmLeaveRoom[]; extern const u8 TradeCenter_TerminateLink[]; +// DexNav +extern const u8 EventScript_StartDexNavBattle[]; +extern const u8 EventScript_NotFoundNearby[]; +extern const u8 EventScript_PokemonGotAway[]; +extern const u8 EventScript_LostSignal[]; +extern const u8 EventScript_TooDark[]; +extern const u8 EventScript_MovedTooFast[]; + #endif //GUARD_EVENT_SCRIPTS_H diff --git a/include/field_effect.h b/include/field_effect.h index b4b089618..df1c99e60 100644 --- a/include/field_effect.h +++ b/include/field_effect.h @@ -16,6 +16,7 @@ extern const struct SpritePalette gSpritePalette_GeneralFieldEffect1Winter; extern const struct SpritePalette gSpritePalette_GeneralFieldEffect2; extern const struct SpritePalette gSpritePalette_Ash; extern const struct SpritePalette gSpritePalette_SmallSparkle; +extern const struct SpritePalette gSpritePalette_CaveDust; extern const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowSmall; extern const struct SpriteTemplate gFieldEffectObjectTemplate_ShadowMedium; @@ -59,6 +60,7 @@ extern const struct SpriteTemplate gFieldEffectObjectTemplate_SmallSparkle; extern const struct SpriteTemplate gFieldEffectObjectTemplate_SlitherTracks; extern const struct SpriteTemplate gFieldEffectObjectTemplate_BugTracks; extern const struct SpriteTemplate gFieldEffectObjectTemplate_SpotTracks; +extern const struct SpriteTemplate gFieldEffectObjectTemplate_CaveDust; extern const struct SpriteTemplate gFieldEffectObjectTemplate_SnowFootprints; extern const struct SpriteTemplate gFieldEffectObjectTemplate_SnowBikeTireTracks; extern const struct SpriteTemplate gFieldEffectObjectTemplate_SnowSlitherTracks; @@ -70,6 +72,8 @@ bool8 FieldEffectActiveListContains(enum FieldEffect fldeff); void CreateTeleportFieldEffectTask(void); void FieldEffectActiveListRemove(enum FieldEffect fldeff); void StartEscapeRopeFieldEffect(void); +void FieldEffectFreeGraphicsResources(struct Sprite *sprite); +void FieldEff_CaveDust(void); void FieldEffectStop(struct Sprite *sprite, enum FieldEffect fldeff); u8 CreateTrainerSprite(u8 trainerSpriteID, s16 x, s16 y, u8 subpriority, u8 *buffer); void FieldCB_FallWarpExit(void); @@ -108,8 +112,8 @@ u32 FldEff_Splash(void); u32 FldEff_JumpSmallSplash(void); u32 FldEff_LongGrass(void); u32 FldEff_JumpLongGrass(void); -u32 FldEff_UnusedGrass(void); -u32 FldEff_UnusedGrass2(void); +u32 FldEff_ShakingGrass(void); +u32 FldEff_ShakingGrass2(void); u32 FldEff_UnusedSand(void); u32 FldEff_UnusedWaterSurfacing(void); u32 FldEff_BerryTreeGrowthSparkle(void); @@ -158,5 +162,6 @@ u32 FldEff_SnowTracksSlither(void); u32 FldEff_SnowTracksSlither(void); u32 FldEff_SnowTracksBug(void); u32 FldEff_SnowTracksSpot(void); +u32 FldEff_CaveDust(void); #endif //GUARD_FIELD_EFFECTS_H diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index b9fa3a067..684899766 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -376,7 +376,8 @@ struct PlayerAvatar { /*0x00*/ u8 flags; /*0x01*/ u8 transitionFlags; // used to be bike, but it's not that in Emerald and probably isn't here either. maybe transition flags? - /*0x02*/ u8 runningState; // this is a static running state. 00 is not moving, 01 is turn direction, 02 is moving. + /*0x02*/ u8 runningState:7; // this is a static running state. 00 is not moving, 01 is turn direction, 02 is moving. + u8 creeping:1; /*0x03*/ u8 tileTransitionState; // this is a transition running state: 00 is not moving, 01 is transition between tiles, 02 means you are on the frame in which you have centered on a tile but are about to keep moving, even if changing directions. 2 is also used for a ledge hop, since you are transitioning. /*0x04*/ u8 spriteId; /*0x05*/ u8 objectEventId; diff --git a/include/global.h b/include/global.h index a801d7652..b4290c801 100644 --- a/include/global.h +++ b/include/global.h @@ -225,6 +225,10 @@ struct SaveBlock3 #if OW_SHOW_ITEM_DESCRIPTIONS == OW_ITEM_DESCRIPTIONS_FIRST_TIME u8 itemFlags[ITEM_FLAGS_COUNT]; #endif +#if USE_DEXNAV_SEARCH_LEVELS == TRUE + u8 dexNavSearchLevels[NUM_SPECIES]; +#endif + u8 dexNavChain; }; extern struct SaveBlock3 *gSaveBlock3Ptr; diff --git a/include/graphics.h b/include/graphics.h index 1d5f6324a..a00572b2c 100644 --- a/include/graphics.h +++ b/include/graphics.h @@ -4913,6 +4913,8 @@ extern const u16 gFogPalette[]; extern const u32 gPartyMenuPokeball_Gfx[]; extern const u32 gPartyMenuPokeball_Pal[]; extern const u32 gPartyMenuPokeballSmall_Gfx[]; +extern const u32 gMoveTypes_Gfx[]; +extern const u32 gMoveTypes_Pal[]; extern const u32 gStatusGfx_Icons[]; extern const u32 gStatusPal_Icons[]; extern const u32 gPartyMenuBg_Gfx[]; diff --git a/include/overworld.h b/include/overworld.h index 68e88b642..84ce1c374 100644 --- a/include/overworld.h +++ b/include/overworld.h @@ -137,6 +137,7 @@ u32 ComputeWhiteOutMoneyLoss(void); void SetLastHealLocationWarp(u8 healLocaionId); void LoadMapFromCameraTransition(u8 mapGroup, u8 mapNum); void CB2_ReturnToFieldFromDiploma(void); +void CB2_Overworld(void); void CB2_OverworldBasic(void); void CB2_NewGame(void); bool8 IsMapTypeOutdoors(u8 mapType); diff --git a/include/party_menu.h b/include/party_menu.h index 3250043a6..fd154fff1 100644 --- a/include/party_menu.h +++ b/include/party_menu.h @@ -26,6 +26,9 @@ extern MainCallback gPostMenuFieldCallback; extern u8 gSelectedOrderFromParty[MAX_FRONTIER_PARTY_SIZE]; extern u8 gBattlePartyCurrentOrder[PARTY_SIZE / 2]; +extern const struct SpriteSheet gSpriteSheet_HeldItem; +extern const u16 gHeldItemPalette[]; + extern void (*gItemUseCB)(u8, TaskFunc); extern const struct SpriteTemplate gSpriteTemplate_StatusIcons; diff --git a/include/pokemon_summary_screen.h b/include/pokemon_summary_screen.h index 20680b592..561c5a49f 100644 --- a/include/pokemon_summary_screen.h +++ b/include/pokemon_summary_screen.h @@ -4,6 +4,8 @@ #include "main.h" extern const u8 gNotDoneYetDescription[]; +extern const struct SpriteTemplate gSpriteTemplate_MoveTypes; +extern const struct CompressedSpriteSheet gSpriteSheet_MoveTypes; extern const struct CompressedSpriteSheet gSpriteSheet_CategoryIcons; extern const struct SpritePalette gSpritePal_CategoryIcons; extern const struct SpriteTemplate gSpriteTemplate_CategoryIcons; diff --git a/include/strings.h b/include/strings.h index cab64fbf8..01dbd2d6d 100644 --- a/include/strings.h +++ b/include/strings.h @@ -628,6 +628,7 @@ extern const u8 gText_MenuOption[]; extern const u8 gText_MenuExit[]; extern const u8 gText_MenuRetire[]; extern const u8 gText_MenuSafariStats[]; +extern const u8 gText_MenuDexNav[]; extern const u8 gStartMenuDesc_Pokedex[]; extern const u8 gStartMenuDesc_Pokemon[]; extern const u8 gStartMenuDesc_Bag[]; diff --git a/include/text_window.h b/include/text_window.h index 9a78451d9..6eb9bcc41 100644 --- a/include/text_window.h +++ b/include/text_window.h @@ -21,6 +21,7 @@ void DrawTextBorderOuter(u8 windowId, u16 tileStart, u8 palette); void DrawTextBorderInner(u8 windowId, u16 tileNum, u8 palNum); void LoadHelpMessageWindowGfx(u8 windowId, u16 destOffset, u8 palIdx); void LoadSignpostWindowGfx(u8 windowId, u16 destOffset, u8 palIdx); +void LoadDexNavWindowGfx(u8 windowId, u16 destOffset, u8 palOffset); void LoadStdWindowTiles(u8 windowId, u16 destOffset); void LoadQuestLogWindowTiles(u8 windowId, u16 destOffset); diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 592b7b448..dab07d542 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -7,6 +7,7 @@ #define WATER_WILD_COUNT 5 #define ROCK_WILD_COUNT 5 #define FISH_WILD_COUNT 10 +#define HIDDEN_WILD_COUNT 3 #define NUM_ALTERING_CAVE_TABLES 9 @@ -33,6 +34,7 @@ struct WildPokemonHeader const struct WildPokemonInfo *landMonsInfo; const struct WildPokemonInfo *waterMonsInfo; const struct WildPokemonInfo *rockSmashMonsInfo; + const struct WildPokemonInfo *hiddenMonsInfo; const struct WildPokemonInfo *fishingMonsInfo; }; @@ -58,5 +60,11 @@ void ResetEncounterRateModifiers(void); bool8 TryStandardWildEncounter(u32 currMetatileAttrs); bool8 TryDoDoubleWildBattle(void); u32 CalculateChainFishingShinyRolls(void); +void CreateWildMon(u16 species, u8 level, u8 unownSlot); +u16 GetCurrentMapWildMonHeaderId(void); +u8 ChooseWildMonIndex_Land(void); +u8 ChooseWildMonIndex_WaterRock(void); +u8 ChooseHiddenMonIndex(void); +bool32 MapHasNoEncounterData(void); #endif // GUARD_WILD_ENCOUNTER_H diff --git a/src/battle_main.c b/src/battle_main.c index c4858acc6..4c1ce2dfc 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -20,7 +20,7 @@ #include "data.h" #include "debug.h" #include "decompress.h" -// #include "dexnav.h" +#include "dexnav.h" #include "dma3.h" #include "event_data.h" #include "evolution_scene.h" @@ -5346,6 +5346,15 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void) { gIsFishingEncounter = FALSE; gIsSurfingEncounter = FALSE; + if (gDexNavSpecies && (gBattleOutcome == B_OUTCOME_WON || gBattleOutcome == B_OUTCOME_CAUGHT)) + { + IncrementDexNavChain(); + TryIncrementSpeciesSearchLevel(); + } + else + gSaveBlock3Ptr->dexNavChain = 0; + + gDexNavSpecies = SPECIES_NONE; ResetSpriteData(); if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK diff --git a/src/bg.c b/src/bg.c index 2524c3f35..db8d5e036 100644 --- a/src/bg.c +++ b/src/bg.c @@ -736,6 +736,77 @@ u32 ChangeBgY(u8 bg, u32 value, u8 op) return sGpuBgConfigs2[bg].bg_y; } +s32 ChangeBgY_ScreenOff(u32 bg, s32 value, u8 op) +{ + u32 mode; + u16 temp1; + u16 temp2; + + if (IsInvalidBg(bg) || !GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE)) + { + return -1; + } + + switch (op) + { + case BG_COORD_SET: + default: + sGpuBgConfigs2[bg].bg_y = value; + break; + case BG_COORD_ADD: + sGpuBgConfigs2[bg].bg_y += value; + break; + case BG_COORD_SUB: + sGpuBgConfigs2[bg].bg_y -= value; + break; + } + + mode = GetBgMode(); + + switch (bg) + { + case 0: + temp1 = sGpuBgConfigs2[0].bg_y >> 0x8; + SetGpuReg_ForcedBlank(REG_OFFSET_BG0VOFS, temp1); + break; + case 1: + temp1 = sGpuBgConfigs2[1].bg_y >> 0x8; + SetGpuReg_ForcedBlank(REG_OFFSET_BG1VOFS, temp1); + break; + case 2: + if (mode == 0) + { + temp1 = sGpuBgConfigs2[2].bg_y >> 0x8; + SetGpuReg_ForcedBlank(REG_OFFSET_BG2VOFS, temp1); + + } + else + { + temp1 = sGpuBgConfigs2[2].bg_y >> 0x10; + temp2 = sGpuBgConfigs2[2].bg_y & 0xFFFF; + SetGpuReg_ForcedBlank(REG_OFFSET_BG2Y_H, temp1); + SetGpuReg_ForcedBlank(REG_OFFSET_BG2Y_L, temp2); + } + break; + case 3: + if (mode == 0) + { + temp1 = sGpuBgConfigs2[3].bg_y >> 0x8; + SetGpuReg_ForcedBlank(REG_OFFSET_BG3VOFS, temp1); + } + else if (mode == 2) + { + temp1 = sGpuBgConfigs2[3].bg_y >> 0x10; + temp2 = sGpuBgConfigs2[3].bg_y & 0xFFFF; + SetGpuReg_ForcedBlank(REG_OFFSET_BG3Y_H, temp1); + SetGpuReg_ForcedBlank(REG_OFFSET_BG3Y_L, temp2); + } + break; + } + + return sGpuBgConfigs2[bg].bg_y; +} + u32 GetBgY(u8 bg) { if (IsInvalidBg32(bg) != FALSE) diff --git a/src/data/field_effects/field_effect_objects.h b/src/data/field_effects/field_effect_objects.h index eb558ef8c..fe672ac4c 100644 --- a/src/data/field_effects/field_effect_objects.h +++ b/src/data/field_effects/field_effect_objects.h @@ -1523,3 +1523,24 @@ const struct SpriteTemplate gFieldEffectObjectTemplate_SmallSparkle = }; const struct SpritePalette gSpritePalette_SmallSparkle = { .data = gFieldEffectPal_SmallSparkle, .tag = FLDEFF_PAL_TAG_SMALL_SPARKLE }; + +// cave dust +static const struct SpriteFrameImage sPicTable_CaveDust[] = +{ + overworld_frame(gFieldEffectObjectPic_CaveDust, 2, 2, 0), + overworld_frame(gFieldEffectObjectPic_CaveDust, 2, 2, 1), + overworld_frame(gFieldEffectObjectPic_CaveDust, 2, 2, 2), + overworld_frame(gFieldEffectObjectPic_CaveDust, 2, 2, 3), +}; +const struct SpriteTemplate gFieldEffectObjectTemplate_CaveDust = { + .tileTag = 0xFFFF, + .paletteTag = FLDEFF_PAL_TAG_CAVE_DUST, + .oam = &gObjectEventBaseOam_16x16, + .anims = sAnimTable_WaterSurfacing, + .images = sPicTable_CaveDust, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = WaitFieldEffectSpriteAnim, +}; + +const struct SpritePalette gSpritePalette_CaveDust = {gFieldEffectObjectPalette_CaveDust, FLDEFF_PAL_TAG_CAVE_DUST}; + diff --git a/src/data/object_events/object_event_graphics.h b/src/data/object_events/object_event_graphics.h index 35f72f7bd..06c777e8b 100644 --- a/src/data/object_events/object_event_graphics.h +++ b/src/data/object_events/object_event_graphics.h @@ -371,3 +371,6 @@ const u16 gObjectEventPal_BeastBall[] = INCBIN_U16("graphics/object_events/pics/ const u16 gObjectEventPal_StrangeBall[] = INCBIN_U16("graphics/object_events/pics/misc/ball_strange.gbapal"); #endif //ITEM_STRANGE_BALL #endif //OW_FOLLOWERS_POKEBALLS + +const u32 gFieldEffectObjectPic_CaveDust[] = INCBIN_U32("graphics/field_effects/pics/cave_dust.4bpp"); +const u16 gFieldEffectObjectPalette_CaveDust[] = INCBIN_U16("graphics/field_effects/palettes/cave_dust.gbapal"); diff --git a/src/data/party_menu.h b/src/data/party_menu.h index c265e3af9..c832dfd71 100644 --- a/src/data/party_menu.h +++ b/src/data/party_menu.h @@ -683,7 +683,7 @@ static const u8 *const sDescriptionStringTable[] = }; static const u32 sHeldItemGfx[] = INCBIN_U32("graphics/party_menu/hold_icons.4bpp"); -static const u16 sHeldItemPalette[] = INCBIN_U16("graphics/party_menu/hold_icons.gbapal"); +const u16 gHeldItemPalette[] = INCBIN_U16("graphics/party_menu/hold_icons.gbapal"); static const struct OamData sOamData_HeldItem = { @@ -720,14 +720,14 @@ static const union AnimCmd *const sSpriteAnimTable_HeldItem[] = sSpriteAnim_HeldMail, }; -static const struct SpriteSheet sSpriteSheet_HeldItem = +const struct SpriteSheet gSpriteSheet_HeldItem = { sHeldItemGfx, sizeof(sHeldItemGfx), 0xD750 }; static const struct SpritePalette sSpritePalette_HeldItem = { - sHeldItemPalette, 0xD750 + gHeldItemPalette, 0xD750 }; static const struct SpriteTemplate sSpriteTemplate_HeldItem = diff --git a/src/data/wild_encounters.json.txt b/src/data/wild_encounters.json.txt index a972e88b1..af63f185e 100644 --- a/src/data/wild_encounters.json.txt +++ b/src/data/wild_encounters.json.txt @@ -73,6 +73,17 @@ const struct WildPokemonInfo {{ encounter.base_label }}_FishingMonsInfo = { {{en #endif {% endif %} +{% if existsIn(encounter, "hidden_mons") %} +const struct WildPokemon {{ encounter.base_label }}_HiddenMons[] = +{ +## for wild_mon in encounter.hidden_mons.mons + { {{ wild_mon.min_level }}, {{ wild_mon.max_level }}, {{ wild_mon.species }} }, +## endfor +}; + +const struct WildPokemonInfo {{ encounter.base_label }}_HiddenMonsInfo = { {{encounter.hidden_mons.encounter_rate}}, {{ encounter.base_label }}_HiddenMons }; +{% endif %} + ## endfor const struct WildPokemonHeader {{ wild_encounter_group.label }}[] = @@ -90,6 +101,7 @@ const struct WildPokemonHeader {{ wild_encounter_group.label }}[] = .waterMonsInfo = {% if existsIn(encounter, "water_mons") %}&{{ encounter.base_label }}_WaterMonsInfo{% else %}NULL{% endif %}, .rockSmashMonsInfo = {% if existsIn(encounter, "rock_smash_mons") %}&{{ encounter.base_label }}_RockSmashMonsInfo{% else %}NULL{% endif %}, .fishingMonsInfo = {% if existsIn(encounter, "fishing_mons") %}&{{ encounter.base_label }}_FishingMonsInfo{% else %}NULL{% endif %}, + .hiddenMonsInfo = {% if existsIn(encounter, "hidden_mons") %}&{{ encounter.base_label }}_HiddenMonsInfo{% else %}NULL{% endif %}, }, {% if contains(encounter.base_label, "FireRed") or contains(encounter.base_label, "LeafGreen") %} #endif @@ -102,6 +114,7 @@ const struct WildPokemonHeader {{ wild_encounter_group.label }}[] = .waterMonsInfo = NULL, .rockSmashMonsInfo = NULL, .fishingMonsInfo = NULL, + .hiddenMonsInfo = NULL, }, }; ## endfor diff --git a/src/daycare.c b/src/daycare.c index e6d47c48c..713fe4d1d 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -62,7 +62,6 @@ static void ClearDaycareMonMail(struct DayCareMail *mail); static void SetInitialEggData(struct Pokemon *mon, u16 species, struct DayCare *daycare); static void DaycarePrintMonInfo(u8 windowId, u32 daycareSlotId, u8 y); static u8 ModifyBreedingScoreForOvalCharm(u8 score); -static u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves); static u16 GetEggSpecies(u16 species); static void Task_EggHatch(u8 taskID); @@ -1023,7 +1022,7 @@ static void InheritAbility(struct Pokemon *egg, struct BoxPokemon *father, struc // Counts the number of egg moves a Pokémon learns and stores the moves in // the given array. -static u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves) +u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves) { u16 numEggMoves; u16 species; diff --git a/src/dexnav.c b/src/dexnav.c new file mode 100644 index 000000000..9ca846067 --- /dev/null +++ b/src/dexnav.c @@ -0,0 +1,2690 @@ +#include "global.h" +#include "battle_main.h" +#include "battle_setup.h" +#include "bg.h" +#include "data.h" +#include "daycare.h" +#include "decompress.h" +#include "dexnav.h" +#include "event_data.h" +#include "event_object_movement.h" +#include "event_scripts.h" +#include "field_effect.h" +#include "field_effect_helpers.h" +#include "field_message_box.h" +#include "field_player_avatar.h" +#include "field_screen_effect.h" +#include "fieldmap.h" +#include "gpu_regs.h" +#include "graphics.h" +#include "item.h" +#include "international_string_util.h" +#include "m4a.h" +#include "map_name_popup.h" +#include "main.h" +#include "malloc.h" +#include "menu.h" +#include "menu_helpers.h" +#include "metatile_behavior.h" +#include "move.h" +#include "overworld.h" +#include "palette.h" +#include "party_menu.h" +#include "pokedex.h" +#include "pokemon.h" +#include "pokemon_icon.h" +#include "pokemon_summary_screen.h" +#include "random.h" +#include "region_map.h" +#include "scanline_effect.h" +#include "script.h" +#include "script_pokemon_util.h" +#include "sound.h" +#include "sprite.h" +#include "start_menu.h" +#include "string_util.h" +#include "strings.h" +#include "task.h" +#include "text.h" +#include "text_window.h" +#include "wild_encounter.h" +#include "window.h" +#include "constants/map_types.h" +#include "constants/species.h" +#include "constants/maps.h" +#include "constants/field_effects.h" +#include "constants/items.h" +#include "constants/songs.h" +#include "constants/abilities.h" +#include "constants/rgb.h" +#include "constants/region_map_sections.h" +#include "gba/m4a_internal.h" + +#if DEXNAV_ENABLED +STATIC_ASSERT(DN_FLAG_SEARCHING != 0, DNFlagSearching_Must_Not_Be_Zero); +STATIC_ASSERT(DN_FLAG_DETECTOR_MODE != 0, DNFlagDetectorMode_Must_Not_Be_Zero); +STATIC_ASSERT(DN_VAR_SPECIES != 0, DNVarSpecies_Must_Not_Be_Zero); +STATIC_ASSERT(DN_VAR_STEP_COUNTER != 0, DNVarStepCounter_Must_Not_Be_Zero); +#endif + +// Defines +enum WindowIds +{ + WINDOW_INFO, + WINDOW_REGISTERED, + WINDOW_COUNT, +}; + +enum Statuses +{ + STATUS_INVALID_SEARCH, + STATUS_CHOOSE_MON, + STATUS_LOCKED, + STATUS_NO_DATA, + STATUS_INCORRECT_AREA, +}; + +struct DexNavSearch +{ + u16 species; + u16 moves[MAX_MON_MOVES]; + u16 heldItem; + u8 abilityNum; + u8 potential; + u8 searchLevel; + u8 monLevel; + u8 proximity; + u8 environment; + s16 tileX; + s16 tileY; + u8 fldEffSpriteId; + u8 fldEffId; + u8 movementCount; + u8 windowId; + u8 iconSpriteId; + u8 eyeSpriteId; + u8 itemSpriteId; + u8 starSpriteIds[3]; + u8 ownedIconSpriteId; + u8 exclamationSpriteId; + u8 hiddenSearch:1; + u8 isHiddenMon:1; + u8 unk:6; + u16 palBuffer[16]; +}; + +struct DexNavGUI +{ + MainCallback savedCallback; + u8 state; + u8 cursorSpriteId; + u16 landSpecies[LAND_WILD_COUNT]; + u16 waterSpecies[WATER_WILD_COUNT]; + u16 hiddenSpecies[HIDDEN_WILD_COUNT]; + u8 cursorRow; + u8 cursorCol; + u8 environment; + u8 potential; + u8 typeIconSpriteIds[2]; + u8 starSpriteIds[3]; +}; + +// RAM + +EWRAM_DATA static struct DexNavSearch *sDexNavSearchDataPtr = NULL; +EWRAM_DATA static struct DexNavGUI *sDexNavUiDataPtr = NULL; +EWRAM_DATA static u8 *sBg1TilemapBuffer = NULL; +EWRAM_DATA u16 gDexNavSpecies = SPECIES_NONE; + +//// Function Declarations +//GUI +static void Task_DexNavWaitFadeIn(u8 taskId); +static void Task_DexNavMain(u8 taskId); +static void PrintCurrentSpeciesInfo(void); +// SEARCH +static bool8 TryStartHiddenMonFieldEffect(u8 environment, u8 xSize, u8 ySize, bool8 smallScan); +static void DexNavGenerateMoveset(u16 species, u8 searchLevel, u8 encounterLevel, u16* moveDst); +static u16 DexNavGenerateHeldItem(u16 species, u8 searchLevel); +static u8 DexNavGetAbilityNum(u16 species, u8 searchLevel); +static u8 DexNavGeneratePotential(u8 searchLevel); +static u8 DexNavTryGenerateMonLevel(u16 species, u8 environment); +static u8 GetEncounterLevelFromMapData(u16 species, u8 environment); +static void CreateDexNavWildMon(u16 species, u8 potential, u8 level, u8 abilityNum, u16 item, u16* moves); +static u8 GetPlayerDistance(s16 x, s16 y); +static u8 DexNavPickTile(u8 environment, u8 xSize, u8 ySize, bool8 smallScan); +static void DexNavProximityUpdate(void); +static void DexNavDrawIcons(void); +static void DexNavUpdateSearchWindow(u8 proximity, u8 searchLevel); +static void Task_DexNavSearch(u8 taskId); +static void EndDexNavSearchSetupScript(const u8 *script, u8 taskId); +// HIDDEN MONS +static void DexNavDrawHiddenIcons(void); +static void DrawHiddenSearchWindow(u8 width); + +//// Const Data +// gui image data +static const u32 sDexNavGuiTiles[] = INCBIN_U32("graphics/dexnav/gui_tiles.4bpp.lz"); +static const u32 sDexNavGuiTilemap[] = INCBIN_U32("graphics/dexnav/gui_tilemap.bin.lz"); +static const u32 sDexNavGuiPal[] = INCBIN_U32("graphics/dexnav/gui.gbapal"); + +static const u32 sSelectionCursorGfx[] = INCBIN_U32("graphics/dexnav/cursor.4bpp.lz"); +static const u16 sSelectionCursorPal[] = INCBIN_U16("graphics/dexnav/cursor.gbapal"); +static const u32 sCapturedAllMonsTiles[] = INCBIN_U32("graphics/dexnav/captured_all.4bpp.lz"); //uses selection cursor pal + +static const u32 sNoDataGfx[] = INCBIN_U32("graphics/dexnav/no_data.4bpp.lz"); + +// searching image data +static const u32 sPotentialStarGfx[] = INCBIN_U32("graphics/dexnav/star.4bpp.lz"); +static const u32 sHiddenSearchIconGfx[] = INCBIN_U32("graphics/dexnav/hidden_search.4bpp.lz"); +static const u32 sOwnedIconGfx[] = INCBIN_U32("graphics/dexnav/owned_icon.4bpp.lz"); +static const u32 sHiddenMonIconGfx[] = INCBIN_U32("graphics/dexnav/hidden.4bpp.lz"); + +// strings +static const u8 sText_DexNav_NoInfo[] = _("--------"); +static const u8 sText_DexNav_CaptureToSee[] = _("Capture first!"); +static const u8 sText_DexNav_PressRToRegister[] = _("R TO REGISTER!"); +static const u8 sText_DexNav_SearchForRegisteredSpecies[] = _("Search {STR_VAR_1}"); +static const u8 sText_DexNav_NotFoundHere[] = _("This Pokémon cannot be found here!"); +static const u8 sText_ThreeQmarks[] = _("???"); +static const u8 sText_SearchLevel[] = _("SEARCH {LV}. {STR_VAR_1}"); +static const u8 sText_MonLevel[] = _("{LV}. {STR_VAR_1}"); +static const u8 sText_EggMove[] = _("MOVE: {STR_VAR_1}"); +static const u8 sText_HeldItem[] = _("{STR_VAR_1}"); +static const u8 sText_StartExit[] = _("{START_BUTTON} EXIT"); +static const u8 sText_DexNavChain[] = _("{NO} {STR_VAR_1}"); +static const u8 sText_DexNavChainLong[] = _("{NO}{STR_VAR_1}"); + +static const u8 sText_ArrowLeft[] = _("{LEFT_ARROW}"); +static const u8 sText_ArrowRight[] = _("{RIGHT_ARROW}"); +static const u8 sText_ArrowUp[] = _("{UP_ARROW}"); +static const u8 sText_ArrowDown[] = _("{DOWN_ARROW}"); + +static const struct WindowTemplate sDexNavGuiWindowTemplates[] = +{ + [WINDOW_INFO] = + { + .bg = 0, + .tilemapLeft = 21, + .tilemapTop = 5, + .width = 9, + .height = 15, + .paletteNum = 15, + .baseBlock = 1, + }, + [WINDOW_REGISTERED] = + { + .bg = 0, + .tilemapLeft = 4, + .tilemapTop = 0, + .width = 26, + .height = 2, + .paletteNum = 15, + .baseBlock = 200, + }, + DUMMY_WIN_TEMPLATE +}; + +//gui font +static const u8 sFontColor_Black[3] = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY}; +static const u8 sFontColor_White[3] = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_WHITE, TEXT_COLOR_DARK_GRAY}; +//search window font +static const u8 sSearchFontColor[3] = {0, 15, 13}; + +static const struct OamData sNoDataIconOam = +{ + .affineMode = ST_OAM_AFFINE_OFF, + .objMode = ST_OAM_OBJ_NORMAL, + .shape = SPRITE_SHAPE(32x32), + .size = SPRITE_SIZE(32x32), + .priority = 1, +}; + +static const struct OamData sHeldItemOam = +{ + .affineMode = ST_OAM_AFFINE_OFF, + .objMode = ST_OAM_OBJ_NORMAL, + .shape = SPRITE_SHAPE(8x8), + .size = SPRITE_SIZE(8x8), + .priority = 0, + .paletteNum = 13, +}; + +static const struct OamData sCapturedAllOam = +{ + .y = 0, + .affineMode = 1, + .objMode = 0, + .mosaic = 0, + .bpp = 0, + .shape = SPRITE_SHAPE(8x8), + .x = 0, + .matrixNum = 0, + .size = SPRITE_SIZE(8x8), + .tileNum = 0, + .priority = 0, //Highest + .paletteNum = 12, + .affineParam = 0, +}; + +static const struct OamData sSearchIconOam = +{ + .y = 0, + .affineMode = 0, + .objMode = 0, + .mosaic = 0, + .bpp = 0, + .shape = 0, + .x = 0, + .matrixNum = 0, + .size = SPRITE_SIZE(32x32), + .tileNum = 0, + .priority = 0, // above BG layers + .paletteNum = 13, + .affineParam = 0 +}; + +static const struct OamData sSelectionCursorOam = +{ + .y = 0, + .affineMode = 0, + .objMode = 0, + .mosaic = 0, + .bpp = 0, + .shape = 0, + .x = 0, + .matrixNum = 0, + .size = SPRITE_SIZE(32x32), + .tileNum = 0, + .priority = 0, // above BG layers + .paletteNum = 12, + .affineParam = 0 +}; + +static const struct OamData sSightOam = +{ + .affineMode = ST_OAM_AFFINE_OFF, + .objMode = ST_OAM_OBJ_NORMAL, + .shape = SPRITE_SHAPE(16x8), + .size = SPRITE_SIZE(16x8), + .priority = 0, +}; +static const union AnimCmd sAnimCmdSight0[] = +{ + ANIMCMD_FRAME(0, 1), + ANIMCMD_END +}; +static const union AnimCmd sAnimCmdSight1[] = +{ + ANIMCMD_FRAME(2, 1), + ANIMCMD_END +}; +static const union AnimCmd sAnimCmdSight2[] = +{ + ANIMCMD_FRAME(4, 1), + ANIMCMD_END +}; +static const union AnimCmd *const sAnimCmdTable_Sight[] = +{ + sAnimCmdSight0, + sAnimCmdSight1, + sAnimCmdSight2, +}; + +// gui sprite templates +static const struct SpriteTemplate sNoDataIconTemplate = +{ + .tileTag = ICON_GFX_TAG, + .paletteTag = ICON_PAL_TAG, + .oam = &sNoDataIconOam, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCallbackDummy, +}; + +static const struct SpriteTemplate sCaptureAllMonsSpriteTemplate = +{ + .tileTag = CAPTURED_ALL_TAG, + .paletteTag = 0xFFFF, + .oam = &sCapturedAllOam, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCallbackDummy, +}; + +static const struct SpriteTemplate sSelectionCursorSpriteTemplate = +{ + .tileTag = SELECTION_CURSOR_TAG, + .paletteTag = 0xFFFF, + .oam = &sSelectionCursorOam, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCallbackDummy, +}; + +// search window sprite templates +static const struct SpriteTemplate sHeldItemTemplate = +{ + .tileTag = HELD_ITEM_TAG, + .paletteTag = 0xFFFF, + .oam = &sHeldItemOam, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCallbackDummy, +}; + +static const struct SpriteTemplate sPotentialStarTemplate = +{ + .tileTag = LIT_STAR_TILE_TAG, + .paletteTag = 0xFFFF, //held item pal + .oam = &sHeldItemOam, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCallbackDummy, +}; + +static const struct SpriteTemplate sSearchIconSpriteTemplate = +{ + .tileTag = HIDDEN_SEARCH_TAG, + .paletteTag = 0xFFFF, //held item pal + .oam = &sSearchIconOam, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCallbackDummy, +}; + +static const struct SpriteTemplate sOwnedIconTemplate = +{ + .tileTag = OWNED_ICON_TAG, + .paletteTag = 0xFFFF, //held item pal + .oam = &sHeldItemOam, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCallbackDummy, +}; + +static const struct SpriteTemplate sHiddenMonIconTemplate = +{ + .tileTag = HIDDEN_MON_ICON_TAG, + .paletteTag = 0xFFFF, //held item pal + .oam = &sHeldItemOam, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCallbackDummy, +}; + +// gui sprite sheets +static const struct CompressedSpriteSheet sNoDataIconSpriteSheet = {sNoDataGfx, (32 * 32) / 2, ICON_GFX_TAG}; +static const struct CompressedSpriteSheet sCapturedAllPokemonSpriteSheet = {sCapturedAllMonsTiles, (8 * 8) / 2, CAPTURED_ALL_TAG}; +// search sprite sheets +static const struct CompressedSpriteSheet sPotentialStarSpriteSheet = {sPotentialStarGfx, (8 * 8) / 2, LIT_STAR_TILE_TAG}; +static const struct CompressedSpriteSheet sOwnedIconSpriteSheet = {sOwnedIconGfx, (8 * 8) / 2, OWNED_ICON_TAG}; +static const struct CompressedSpriteSheet sHiddenMonIconSpriteSheet = {sHiddenMonIconGfx, (8 * 8) / 2, HIDDEN_MON_ICON_TAG}; + +//// functions +/////////////////////// +//// DEXNAV SEARCH //// +/////////////////////// +static s16 GetSearchWindowY(void) +{ + return (GetWindowAttribute(sDexNavSearchDataPtr->windowId, WINDOW_TILEMAP_TOP) * 8); +} + +#define SPECIES_ICON_X 28 +static void DrawDexNavSearchMonIcon(u16 species, u8 *dst, bool8 owned) +{ + u8 spriteId; + + LoadMonIconPalette(species); + spriteId = CreateMonIcon(species, SpriteCB_MonIcon, SPECIES_ICON_X - 6, GetSearchWindowY() + 8, 0, 0xFFFFFFFF); + gSprites[spriteId].oam.priority = 0; + *dst = spriteId; + + if (owned) + sDexNavSearchDataPtr->ownedIconSpriteId = CreateSprite(&sOwnedIconTemplate, SPECIES_ICON_X + 6, GetSearchWindowY() + 4, 0); +} + +static void AddSearchWindow(u8 width) +{ + struct WindowTemplate template; + u16 y = 16; + + if (sDexNavSearchDataPtr->tileY > (gSaveBlock1Ptr->pos.y + 7)) + y = 1; //draw at top if chosen tile is below + + LoadDexNavWindowGfx(sDexNavSearchDataPtr->windowId, 0x1d5, 14 * 16); + + SetWindowTemplateFields(&template, 0, 1, y, width, 3, 14, 8); + + sDexNavSearchDataPtr->windowId = AddWindow(&template); + FillWindowPixelBuffer(sDexNavSearchDataPtr->windowId, PIXEL_FILL(1)); + PutWindowTilemap(sDexNavSearchDataPtr->windowId); + CopyWindowToVram(sDexNavSearchDataPtr->windowId, 3); + + DrawStdFrameWithCustomTileAndPalette(sDexNavSearchDataPtr->windowId, TRUE, 0x214, 14); +} + +#define WINDOW_COL_0 (SPECIES_ICON_X + 4) +#define WINDOW_COL_1 (WINDOW_COL_0 + (GetFontAttribute(sDexNavSearchDataPtr->windowId, FONTATTR_MAX_LETTER_WIDTH) * (POKEMON_NAME_LENGTH))) +#define WINDOW_MOVE_NAME_X (WINDOW_COL_1 + (GetFontAttribute(sDexNavSearchDataPtr->windowId, FONTATTR_MAX_LETTER_WIDTH) * 6)) +#define SEARCH_ARROW_X (WINDOW_MOVE_NAME_X + 90) +#define SEARCH_ARROW_Y 0 + +static void AddSearchWindowText(u16 species, u8 proximity, u8 searchLevel, bool8 hidden) +{ + u8 windowId = sDexNavSearchDataPtr->windowId; + + //species name - always present + if (hidden) + { + StringCopy(gStringVar4, sText_ThreeQmarks); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + return; + } + else + { + StringCopy(gStringVar1, GetSpeciesName(species)); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1); + } + + //level - always present + ConvertIntToDecimalStringN(gStringVar1, sDexNavSearchDataPtr->monLevel, STR_CONV_MODE_LEFT_ALIGN, 3); + StringExpandPlaceholders(gStringVar4, sText_MonLevel); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, WINDOW_COL_1, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + + if (proximity <= SNEAKING_PROXIMITY) + { + PlaySE(SE_POKENAV_ON); + // move + if (searchLevel > 1 && sDexNavSearchDataPtr->moves[0]) + { + StringCopy(gStringVar1, GetMoveName(sDexNavSearchDataPtr->moves[0])); + StringExpandPlaceholders(gStringVar4, sText_EggMove); + AddTextPrinterParameterized3(windowId, 0, WINDOW_MOVE_NAME_X, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + } + + if (searchLevel > 2) + { + // ability name + StringCopy(gStringVar1, gAbilitiesInfo[GetAbilityBySpecies(species, sDexNavSearchDataPtr->abilityNum)].name); + AddTextPrinterParameterized3(windowId, 0, WINDOW_COL_1 + 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1); + + // item name + if (sDexNavSearchDataPtr->heldItem) + { + CopyItemName(sDexNavSearchDataPtr->heldItem, gStringVar1); + StringExpandPlaceholders(gStringVar4, sText_HeldItem); + AddTextPrinterParameterized3(windowId, 0, WINDOW_COL_0, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + } + } + } + + //chain level - always present + ConvertIntToDecimalStringN(gStringVar1, gSaveBlock3Ptr->dexNavChain, STR_CONV_MODE_LEFT_ALIGN, 3); + if (gSaveBlock3Ptr->dexNavChain > 99) + StringExpandPlaceholders(gStringVar4, sText_DexNavChainLong); + else + StringExpandPlaceholders(gStringVar4, sText_DexNavChain); + AddTextPrinterParameterized3(windowId, 0, SEARCH_ARROW_X - 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + + CopyWindowToVram(sDexNavSearchDataPtr->windowId, 2); +} + +#define SEARCH_WINDOW_WIDTH 28 + +static void DrawSearchWindow(u16 species, u8 potential, bool8 hidden) +{ + u8 searchLevel = sDexNavSearchDataPtr->searchLevel; + + AddSearchWindow(SEARCH_WINDOW_WIDTH); + AddSearchWindowText(species, sDexNavSearchDataPtr->proximity, searchLevel, hidden); +} + +#undef SEARCH_WINDOW_WIDTH + +static void RemoveDexNavWindowAndGfx(void) +{ + u32 i; + + // try remove sprites + if (sDexNavSearchDataPtr->iconSpriteId != MAX_SPRITES) + DestroySprite(&gSprites[sDexNavSearchDataPtr->iconSpriteId]); + if (sDexNavSearchDataPtr->itemSpriteId != MAX_SPRITES) + DestroySprite(&gSprites[sDexNavSearchDataPtr->itemSpriteId]); + if (sDexNavSearchDataPtr->eyeSpriteId != MAX_SPRITES) + DestroySprite(&gSprites[sDexNavSearchDataPtr->eyeSpriteId]); + if (sDexNavSearchDataPtr->ownedIconSpriteId != MAX_SPRITES) + DestroySprite(&gSprites[sDexNavSearchDataPtr->ownedIconSpriteId]); + if (sDexNavSearchDataPtr->exclamationSpriteId != MAX_SPRITES) + DestroySprite(&gSprites[sDexNavSearchDataPtr->exclamationSpriteId]); + + for (i = 0; i < NELEMS(sDexNavSearchDataPtr->starSpriteIds); i++) + { + if (sDexNavSearchDataPtr->starSpriteIds[i] != MAX_SPRITES) + DestroySprite(&gSprites[sDexNavSearchDataPtr->starSpriteIds[i]]); + } + + FreeSpriteTilesByTag(HELD_ITEM_TAG); + FreeSpriteTilesByTag(OWNED_ICON_TAG); + FreeSpriteTilesByTag(HIDDEN_SEARCH_TAG); + FreeSpriteTilesByTag(HIDDEN_MON_ICON_TAG); + FreeSpriteTilesByTag(LIT_STAR_TILE_TAG); + FreeSpritePaletteByTag(HELD_ITEM_TAG); + SafeFreeMonIconPalette(sDexNavSearchDataPtr->species); + + // remove window + ClearStdWindowAndFrameToTransparent(sDexNavSearchDataPtr->windowId, FALSE); + CopyWindowToVram(sDexNavSearchDataPtr->windowId, 3); + RemoveWindow(sDexNavSearchDataPtr->windowId); +} + + +////////////////////// +////DEXNAV SEARCH///// +////////////////////// +static u8 GetPlayerDistance(s16 x, s16 y) +{ + u16 deltaX = abs(x - (gSaveBlock1Ptr->pos.x + 7)); + u16 deltaY = abs(y - (gSaveBlock1Ptr->pos.y + 7)); + return deltaX + deltaY; +} + +static void DexNavProximityUpdate(void) +{ + sDexNavSearchDataPtr->proximity = GetPlayerDistance(sDexNavSearchDataPtr->tileX, sDexNavSearchDataPtr->tileY); +} + +//Pick a specific tile based on environment +static bool8 DexNavPickTile(u8 environment, u8 areaX, u8 areaY, bool8 smallScan) +{ + // area of map to cover starting from camera position {-7, -7} + s16 topX = gSaveBlock1Ptr->pos.x - SCANSTART_X + (smallScan * 5); + s16 topY = gSaveBlock1Ptr->pos.y - SCANSTART_Y + (smallScan * 5); + s16 botX = topX + areaX; + s16 botY = topY + areaY; + u8 i; + bool8 nextIter; + u8 scale = 0; + u8 weight = 0; + u8 currMapType = GetCurrentMapType(); + u32 metatileAttributes; + u8 tileBuffer = 2; + u8 *xPos = AllocZeroed((botX - topX) * (botY - topY) * sizeof(u8)); + u8 *yPos = AllocZeroed((botX - topX) * (botY - topY) * sizeof(u8)); + u32 iter = 0; + bool32 ret = FALSE; + + // loop through every tile in area and evaluate + while (topY < botY) + { + while (topX < botX) + { + metatileAttributes = MapGridGetMetatileAttributeAt(topX, topY, METATILE_ATTRIBUTES_ALL); + //Check for objects + nextIter = FALSE; + if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE)) + tileBuffer = SNEAKING_PROXIMITY + 3; + else if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH)) + tileBuffer = SNEAKING_PROXIMITY + 1; + + if (GetPlayerDistance(topX, topY) <= tileBuffer) + { + // tile too close to player + topX++; + continue; + } + + for (i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + if (gObjectEvents[i].currentCoords.x == topX && gObjectEvents[i].currentCoords.y == topY) + { + // cannot be on a tile where an object exists + nextIter = TRUE; + break; + } + } + + if (nextIter) + { + topX++; + continue; + } + + weight = 0; // initiliaze weight + switch (environment) + { + case ENCOUNTER_TYPE_LAND: + if (ExtractMetatileAttribute(metatileAttributes, METATILE_ATTRIBUTE_ENCOUNTER_TYPE) == TILE_ENCOUNTER_LAND) + { + if (currMapType == MAP_TYPE_UNDERGROUND) + { + // inside (cave) + if (IsElevationMismatchAt(gObjectEvents[gPlayerAvatar.spriteId].currentElevation, topX, topY)) + break; //occurs at same z coord + + scale = 440 - (smallScan * 200) - (GetPlayerDistance(topX, topY) / 2) - (2 * (topX + topY)); + weight = ((Random() % scale) < 1) && !MapGridGetCollisionAt(topX, topY); + } + else + { + // outdoors: grass + scale = 100 - (GetPlayerDistance(topX, topY) * 2); + weight = (Random() % scale <= 5) && !MapGridGetCollisionAt(topX, topY); + } + } + break; + case ENCOUNTER_TYPE_WATER: + if (ExtractMetatileAttribute(metatileAttributes, METATILE_ATTRIBUTE_ENCOUNTER_TYPE) == TILE_ENCOUNTER_WATER) + { + u8 scale = 320 - (smallScan * 200) - (GetPlayerDistance(topX, topY) / 2); + if (IsElevationMismatchAt(gObjectEvents[gPlayerAvatar.spriteId].currentElevation, topX, topY)) + break; + + weight = (Random() % scale <= 1) && !MapGridGetCollisionAt(topX, topY); + } + break; + default: + break; + } + + if (weight > 0) + { + xPos[iter] = topX; + yPos[iter] = topY; + iter++; + } + + topX++; + } + + topY++; + topX = gSaveBlock1Ptr->pos.x - SCANSTART_X + (smallScan * 5); + } + + if (iter > 0) + { + i = Random() % iter; + sDexNavSearchDataPtr->tileX = xPos[i]; + sDexNavSearchDataPtr->tileY = yPos[i]; + ret = TRUE; + } + + Free(xPos); + Free(yPos); + + return ret; +} + + +static bool8 TryStartHiddenMonFieldEffect(u8 environment, u8 xSize, u8 ySize, bool8 smallScan) +{ + u8 currMapType = GetCurrentMapType(); + u8 fldEffId = 0; + + if (DexNavPickTile(environment, xSize, ySize, smallScan)) + { + u8 metatileBehaviour = MapGridGetMetatileBehaviorAt(sDexNavSearchDataPtr->tileX, sDexNavSearchDataPtr->tileY); + + switch (environment) + { + case ENCOUNTER_TYPE_LAND: + if (currMapType == MAP_TYPE_UNDERGROUND) + { + fldEffId = FLDEFF_CAVE_DUST; + } + else if (IsMapTypeIndoors(currMapType)) + { + if (MetatileBehavior_IsTallGrass(metatileBehaviour)) //Grass in cave + fldEffId = FLDEFF_SHAKING_GRASS; + else if (MetatileBehavior_IsLongGrass(metatileBehaviour)) //Really tall grass + fldEffId = FLDEFF_SHAKING_LONG_GRASS; + else if (MetatileBehavior_IsSand(metatileBehaviour)) + fldEffId = FLDEFF_SAND_HOLE; + else + fldEffId = FLDEFF_CAVE_DUST; + } + else //outdoor, underwater + { + if (MetatileBehavior_IsTallGrass(metatileBehaviour)) //Regular grass + fldEffId = FLDEFF_SHAKING_GRASS; + else if (MetatileBehavior_IsLongGrass(metatileBehaviour)) //Really tall grass + fldEffId = FLDEFF_SHAKING_LONG_GRASS; + else if (MetatileBehavior_IsSand(metatileBehaviour)) //Desert Sand + fldEffId = FLDEFF_SAND_HOLE; + else if (MetatileBehavior_IsMountain(metatileBehaviour)) //Rough Terrain + fldEffId = FLDEFF_CAVE_DUST; + else + fldEffId = FLDEFF_BERRY_TREE_GROWTH_SPARKLE; //default + } + break; + case ENCOUNTER_TYPE_WATER: + fldEffId = FLDEFF_UNUSED_WATER_SURFACING; + break; + default: + return FALSE; + } + + if (fldEffId != 0) + { + gFieldEffectArguments[0] = sDexNavSearchDataPtr->tileX; + gFieldEffectArguments[1] = sDexNavSearchDataPtr->tileY; + gFieldEffectArguments[2] = 0xFF; // subpriority + gFieldEffectArguments[3] = 2; //priority + sDexNavSearchDataPtr->fldEffSpriteId = FieldEffectStart(fldEffId); + if (sDexNavSearchDataPtr->fldEffSpriteId == MAX_SPRITES) + return FALSE; + + sDexNavSearchDataPtr->fldEffId = fldEffId; + return TRUE; + } + } + + return FALSE; +} + +static void DrawDexNavSearchHeldItem(u8* dst) +{ + *dst = CreateSprite(&sHeldItemTemplate, SPECIES_ICON_X + 6, GetSearchWindowY() + 18, 0); + if (*dst != MAX_SPRITES) + gSprites[*dst].invisible = TRUE; +} + +static void LoadSearchIconData(void) +{ + // palettes clash with mon icon, so must load manually + LoadSpriteSheet(&gSpriteSheet_HeldItem); + LoadPalette(gHeldItemPalette, 0x100 + (16 * sHeldItemOam.paletteNum), 32); + LoadCompressedSpriteSheetUsingHeap(&sPotentialStarSpriteSheet); + //LoadCompressedSpriteSheetUsingHeap(&sSightSpriteSheet); //eye replaced with arrow + LoadCompressedSpriteSheetUsingHeap(&sOwnedIconSpriteSheet); + LoadCompressedSpriteSheetUsingHeap(&sHiddenMonIconSpriteSheet); +} + +static u8 GetSearchLevel(u16 dexNum) +{ + u8 searchLevel; +#if USE_DEXNAV_SEARCH_LEVELS == TRUE + searchLevel = gSaveBlock3Ptr->dexNavSearchLevels[dexNum]; +#else + searchLevel = 0; +#endif + return searchLevel; +} + +#define tProximity data[0] +#define tFrameCount data[1] +#define tSpecies data[2] +#define tEnvironment data[3] +#define tRevealed data[4] + +static void Task_SetUpDexNavSearch(u8 taskId) +{ + struct Task *task = &gTasks[taskId]; + + u16 species = sDexNavSearchDataPtr->species; + u8 searchLevel = GetSearchLevel(species); + + // init sprites + sDexNavSearchDataPtr->iconSpriteId = MAX_SPRITES; + sDexNavSearchDataPtr->itemSpriteId = MAX_SPRITES; + sDexNavSearchDataPtr->eyeSpriteId = MAX_SPRITES; + sDexNavSearchDataPtr->starSpriteIds[0] = MAX_SPRITES; + sDexNavSearchDataPtr->starSpriteIds[1] = MAX_SPRITES; + sDexNavSearchDataPtr->starSpriteIds[2] = MAX_SPRITES; + sDexNavSearchDataPtr->ownedIconSpriteId = MAX_SPRITES; + sDexNavSearchDataPtr->exclamationSpriteId = MAX_SPRITES; + sDexNavSearchDataPtr->searchLevel = searchLevel; + + DexNavGenerateMoveset(species, searchLevel, sDexNavSearchDataPtr->monLevel, &sDexNavSearchDataPtr->moves[0]); + sDexNavSearchDataPtr->heldItem = DexNavGenerateHeldItem(species, searchLevel); + sDexNavSearchDataPtr->abilityNum = DexNavGetAbilityNum(species, searchLevel); + sDexNavSearchDataPtr->potential = DexNavGeneratePotential(searchLevel); + DexNavProximityUpdate(); + + LoadSearchIconData(); + if (sDexNavSearchDataPtr->hiddenSearch) + { + DexNavDrawHiddenIcons(); + } + else + { + DexNavDrawIcons(); + DexNavUpdateSearchWindow(sDexNavSearchDataPtr->proximity, searchLevel); + } + + FlagSet(DN_FLAG_SEARCHING); + gPlayerAvatar.creeping = TRUE; //initialize as true in case mon appears beside you + task->tProximity = gSprites[gPlayerAvatar.spriteId].x; + task->tFrameCount = 0; + task->func = Task_DexNavSearch; + IncrementGameStat(GAME_STAT_DEXNAV_SCANNED); +} + +static void DexNavSearchBail(u8 taskId, const u8 *script) +{ + TRY_FREE_AND_SET_NULL(sDexNavSearchDataPtr); + FreeMonIconPalettes(); + ScriptContext_SetupScript(script); + DestroyTask(taskId); +} + +static void Task_InitDexNavSearch(u8 taskId) +{ + struct Task *task = &gTasks[taskId]; + u16 species = task->tSpecies; + u8 environment = task->tEnvironment; + + sDexNavSearchDataPtr = AllocZeroed(sizeof(struct DexNavSearch)); + if (sDexNavSearchDataPtr == NULL) + { + DexNavSearchBail(taskId, EventScript_NotFoundNearby); + return; + } + + // assign non-objects to struct + sDexNavSearchDataPtr->species = species; + sDexNavSearchDataPtr->environment = environment; //updated in DexNavTryGenerateMonLevel if hidden mon + sDexNavSearchDataPtr->isHiddenMon = (environment == ENCOUNTER_TYPE_HIDDEN) ? TRUE : FALSE; + sDexNavSearchDataPtr->monLevel = DexNavTryGenerateMonLevel(species, environment); + + if (GetFlashLevel() > 0) + { + DexNavSearchBail(taskId, EventScript_TooDark); + return; + } + + if (sDexNavSearchDataPtr->monLevel == MON_LEVEL_NONEXISTENT || !TryStartHiddenMonFieldEffect(sDexNavSearchDataPtr->environment, 12, 12, FALSE)) + { + DexNavSearchBail(taskId, EventScript_NotFoundNearby); + return; + } + + sDexNavSearchDataPtr->hiddenSearch = FALSE; + task->tRevealed = TRUE; //search window revealed + task->func = Task_SetUpDexNavSearch; +} + +static void DexNavDrawPotentialStars(u8 potential, u8* dst) +{ + u8 spriteId; + u32 i; + + for (i = 0; i < NELEMS(sDexNavSearchDataPtr->starSpriteIds); i++) + { + spriteId = MAX_SPRITES; + if (potential > i) + spriteId = CreateSprite(&sPotentialStarTemplate, SPECIES_ICON_X - 20, GetSearchWindowY() + 4 + (i * 8), 0); + + dst[i] = spriteId; + if (spriteId != MAX_SPRITES) + gSprites[spriteId].invisible = TRUE; + } +} + +static void DexNavUpdateDirectionArrow(void) +{ + u16 tileX = sDexNavSearchDataPtr->tileX; + u16 tileY = sDexNavSearchDataPtr->tileY; + u16 playerX = gSaveBlock1Ptr->pos.x + 7; + u16 playerY = gSaveBlock1Ptr->pos.y + 7; + u16 deltaX = abs(tileX - playerX); + u16 deltaY = abs(tileY - playerY); + const u8 *str; + u8 windowId = sDexNavSearchDataPtr->windowId; + + FillWindowPixelRect(windowId, PIXEL_FILL(1), SEARCH_ARROW_X, SEARCH_ARROW_Y, 12, 12); + if (deltaX <= 1 && deltaY <= 1) + { + str = gText_EmptyString3; + } + else if (deltaX > deltaY) + { + if (playerX > tileX) + str = sText_ArrowLeft; //player to right + else + str = sText_ArrowRight; //player to left + } + else //greater Y diff + { + if (playerY > tileY) + str = sText_ArrowUp; //player below + else + str = sText_ArrowDown; //player above + } + + AddTextPrinterParameterized3(windowId, 1, SEARCH_ARROW_X, SEARCH_ARROW_Y, sSearchFontColor, TEXT_SKIP_DRAW, str); + CopyWindowToVram(windowId, 2); +} + +static void DexNavDrawIcons(void) +{ + u16 species = sDexNavSearchDataPtr->species; + + DrawSearchWindow(species, sDexNavSearchDataPtr->potential, FALSE); + DrawDexNavSearchMonIcon(species, &sDexNavSearchDataPtr->iconSpriteId, GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT)); + DrawDexNavSearchHeldItem(&sDexNavSearchDataPtr->itemSpriteId); + DexNavDrawPotentialStars(sDexNavSearchDataPtr->potential, &sDexNavSearchDataPtr->starSpriteIds[0]); + DexNavUpdateDirectionArrow(); +} + +///////////////////// +//// SEARCH TASK //// +///////////////////// +bool8 TryStartDexNavSearch(void) +{ + u8 taskId; + u16 val = VarGet(DN_VAR_SPECIES); + + if (FlagGet(DN_FLAG_SEARCHING) || (val & DEXNAV_MASK_SPECIES) == SPECIES_NONE) + return FALSE; + + DismissMapNamePopup(); + ChangeBgY_ScreenOff(0, 0, 0); + taskId = CreateTask(Task_InitDexNavSearch, 0); + gTasks[taskId].tSpecies = val & DEXNAV_MASK_SPECIES; + gTasks[taskId].tEnvironment = val >> 14; + PlaySE(SE_DEX_SEARCH); + return FALSE; //we dont actually want to enable the script context +} + +void EndDexNavSearch(u8 taskId) +{ + FlagClear(DN_FLAG_SEARCHING); + DestroyTask(taskId); + RemoveDexNavWindowAndGfx(); + FieldEffectStop(&gSprites[sDexNavSearchDataPtr->fldEffSpriteId], sDexNavSearchDataPtr->fldEffId); + Free(sDexNavSearchDataPtr); +} + +static void EndDexNavSearchSetupScript(const u8 *script, u8 taskId) +{ + gSaveBlock3Ptr->dexNavChain = 0; //reset chain + EndDexNavSearch(taskId); + ScriptContext_SetupScript(script); +} + +static u8 GetMovementProximityBySearchLevel(void) +{ + if (sDexNavSearchDataPtr->searchLevel < 20) + return 2; + else if (sDexNavSearchDataPtr->searchLevel < 50) + return 3; + else + return 4; +} + +static void Task_RevealHiddenMon(u8 taskId) +{ + struct Task *task = &gTasks[taskId]; + u16 species = sDexNavSearchDataPtr->species; + + // remove owned icon if it exists + if (sDexNavSearchDataPtr->ownedIconSpriteId != MAX_SPRITES) + { + DestroySprite(&gSprites[sDexNavSearchDataPtr->ownedIconSpriteId]); + sDexNavSearchDataPtr->ownedIconSpriteId = MAX_SPRITES; + } + + // remove exclamation if it exists + if (sDexNavSearchDataPtr->exclamationSpriteId != MAX_SPRITES) + { + DestroySprite(&gSprites[sDexNavSearchDataPtr->exclamationSpriteId]); + sDexNavSearchDataPtr->exclamationSpriteId = MAX_SPRITES; + } + + + if (!GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_SEEN)) + { + u8 index; + + //if not seen, hide name and whiteout mon + DrawSearchWindow(species, sDexNavSearchDataPtr->potential, TRUE); + DrawDexNavSearchMonIcon(species, &sDexNavSearchDataPtr->iconSpriteId, FALSE); + // whiteout icon + index = IndexOfSpritePaletteTag(gSprites[sDexNavSearchDataPtr->iconSpriteId].template->paletteTag); + CpuCopy16(&gPlttBufferUnfaded[0x100 + index * 16], sDexNavSearchDataPtr->palBuffer, 32); + TintPalette_CustomTone(sDexNavSearchDataPtr->palBuffer, 16, 510, 510, 510); + LoadPalette(sDexNavSearchDataPtr->palBuffer, 0x100 + index * 16, 32); + } + else + { + DrawSearchWindow(species, sDexNavSearchDataPtr->potential, FALSE); + DrawDexNavSearchMonIcon(species, &sDexNavSearchDataPtr->iconSpriteId, GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT)); + } + + DexNavUpdateDirectionArrow(); + task->func = Task_DexNavSearch; + task->tFrameCount = 0; //restart search clock +} + +static void Task_DexNavSearch(u8 taskId) +{ + struct Task *task = &gTasks[taskId]; + + if (sDexNavSearchDataPtr->proximity > MAX_PROXIMITY) + { // out of range + if (sDexNavSearchDataPtr->hiddenSearch && !task->tRevealed) + EndDexNavSearch(taskId); + else + EndDexNavSearchSetupScript(EventScript_LostSignal, taskId); + return; + } + + if (sDexNavSearchDataPtr->proximity <= CREEPING_PROXIMITY && !gPlayerAvatar.creeping && task->tFrameCount > 60) + { //should be creeping but player walks normally + if (sDexNavSearchDataPtr->hiddenSearch && !task->tRevealed) + EndDexNavSearch(taskId); + else + EndDexNavSearchSetupScript(EventScript_MovedTooFast, taskId); + return; + } + + if (sDexNavSearchDataPtr->proximity <= SNEAKING_PROXIMITY && TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_DASH | PLAYER_AVATAR_FLAG_BIKE)) + { // running/biking too close + //always do event script, even if player hasn't revealed a hidden mon. It's assumed they would be creeping towards it + EndDexNavSearchSetupScript(EventScript_MovedTooFast, taskId); + return; + } + + if (ArePlayerFieldControlsLocked() == TRUE) + { // check if script just executed + EndDexNavSearch(taskId); + return; + } + + if (gTasks[taskId].tFrameCount > DEXNAV_TIMEOUT * 60) + { // player took too long + if (sDexNavSearchDataPtr->hiddenSearch && !task->tRevealed) + EndDexNavSearch(taskId); + else + EndDexNavSearchSetupScript(EventScript_PokemonGotAway, taskId); + return; + } + + if (sDexNavSearchDataPtr->proximity < 1) + { + gDexNavSpecies = sDexNavSearchDataPtr->species; + CreateDexNavWildMon(sDexNavSearchDataPtr->species, sDexNavSearchDataPtr->potential, sDexNavSearchDataPtr->monLevel, + sDexNavSearchDataPtr->abilityNum, sDexNavSearchDataPtr->heldItem, sDexNavSearchDataPtr->moves); + + FlagClear(DN_FLAG_SEARCHING); + ScriptContext_SetupScript(EventScript_StartDexNavBattle); + Free(sDexNavSearchDataPtr); + DestroyTask(taskId); + return; + } + + if (sDexNavSearchDataPtr->hiddenSearch && !task->tRevealed && + (JOY_NEW(R_BUTTON) || (sDexNavSearchDataPtr->proximity < CREEPING_PROXIMITY))) + { + PlaySE(SE_DEX_SEARCH); + ClearStdWindowAndFrameToTransparent(sDexNavSearchDataPtr->windowId, FALSE); + CopyWindowToVram(sDexNavSearchDataPtr->windowId, 3); + RemoveWindow(sDexNavSearchDataPtr->windowId); + DestroySprite(&gSprites[sDexNavSearchDataPtr->iconSpriteId]); + task->tRevealed = TRUE; //regular dexnav search + //sDexNavSearchDataPtr->hiddenSearch = FALSE; //now its a regular dexnav search + task->func = Task_RevealHiddenMon; + return; + } + + //Caves and water the pokemon moves around + if ((sDexNavSearchDataPtr->environment == ENCOUNTER_TYPE_WATER || GetCurrentMapType() == MAP_TYPE_UNDERGROUND) + && sDexNavSearchDataPtr->proximity < GetMovementProximityBySearchLevel() && sDexNavSearchDataPtr->movementCount < 2 + && task->tRevealed) + { + FieldEffectStop(&gSprites[sDexNavSearchDataPtr->fldEffSpriteId], sDexNavSearchDataPtr->fldEffId); + + if (!TryStartHiddenMonFieldEffect(sDexNavSearchDataPtr->environment, 10, 10, TRUE)) + { + EndDexNavSearchSetupScript(EventScript_PokemonGotAway, taskId); + return; + } + + sDexNavSearchDataPtr->movementCount++; + } + + DexNavProximityUpdate(); + if (task->tProximity != sDexNavSearchDataPtr->proximity) + { + //player has moved + if (task->tRevealed) //update search window info only if hidden mon has been revealed (always true for search mode) + DexNavUpdateSearchWindow(sDexNavSearchDataPtr->proximity, sDexNavSearchDataPtr->searchLevel); + + task->tProximity = sDexNavSearchDataPtr->proximity; + } + + task->tFrameCount++; +} + +static void DexNavUpdateSearchWindow(u8 proximity, u8 searchLevel) +{ + bool8 hideName = FALSE; + + if (sDexNavSearchDataPtr->hiddenSearch && !GetSetPokedexFlag(SpeciesToNationalPokedexNum(sDexNavSearchDataPtr->species), FLAG_GET_SEEN)) + hideName = TRUE; //if a detector mode hidden search and player hasn't seen the mon, hide info + + FillWindowPixelBuffer(sDexNavSearchDataPtr->windowId, PIXEL_FILL(1)); //clear window + AddSearchWindowText(sDexNavSearchDataPtr->species, proximity, searchLevel, hideName); + + DexNavUpdateDirectionArrow(); + + //init hidden sprites + if (sDexNavSearchDataPtr->itemSpriteId != MAX_SPRITES) + gSprites[sDexNavSearchDataPtr->itemSpriteId].invisible = TRUE; + if (sDexNavSearchDataPtr->starSpriteIds[0] != MAX_SPRITES) + gSprites[sDexNavSearchDataPtr->starSpriteIds[0]].invisible = TRUE; + if (sDexNavSearchDataPtr->starSpriteIds[1] != MAX_SPRITES) + gSprites[sDexNavSearchDataPtr->starSpriteIds[1]].invisible = TRUE; + if (sDexNavSearchDataPtr->starSpriteIds[2] != MAX_SPRITES) + gSprites[sDexNavSearchDataPtr->starSpriteIds[2]].invisible = TRUE; + + if (proximity <= SNEAKING_PROXIMITY) + { + if (searchLevel > 2 && sDexNavSearchDataPtr->heldItem) + { + // toggle item view + if (sDexNavSearchDataPtr->itemSpriteId != MAX_SPRITES) + gSprites[sDexNavSearchDataPtr->itemSpriteId].invisible = FALSE; + } + + if (searchLevel > 4) + { + if (sDexNavSearchDataPtr->starSpriteIds[0] != MAX_SPRITES) + gSprites[sDexNavSearchDataPtr->starSpriteIds[0]].invisible = FALSE; + + if (sDexNavSearchDataPtr->starSpriteIds[1] != MAX_SPRITES) + gSprites[sDexNavSearchDataPtr->starSpriteIds[1]].invisible = FALSE; + + if (sDexNavSearchDataPtr->starSpriteIds[2] != MAX_SPRITES) + gSprites[sDexNavSearchDataPtr->starSpriteIds[2]].invisible = FALSE; + } + } +} + +////////////////////////////// +//// DEXNAV MON GENERATOR //// +////////////////////////////// +static void CreateDexNavWildMon(u16 species, u8 potential, u8 level, u8 abilityNum, u16 item, u16* moves) +{ + struct Pokemon* mon = &gEnemyParty[0]; + u8 iv[3] = {NUM_STATS}; + u8 i; + u8 perfectIv = 31; + + CreateWildMon(species, level, 0); // shiny rate bonus handled in CreateBoxMon + + // Pick random, unique IVs to set to 31. The number of perfect IVs that are assigned is equal to the potential + iv[0] = Random() % NUM_STATS; // choose 1st perfect stat + do { + iv[1] = Random() % NUM_STATS; + iv[2] = Random() % NUM_STATS; + } while ((iv[1] == iv[0]) // unique 2nd perfect stat + || (iv[2] == iv[0] || iv[2] == iv[1])); // unique 3rd perfect stat + + if (potential > 2 && iv[2] != NUM_STATS) + SetMonData(mon, MON_DATA_HP_IV + iv[2], &perfectIv); + if (potential > 1 && iv[1] != NUM_STATS) + SetMonData(mon, MON_DATA_HP_IV + iv[1], &perfectIv); + if (potential > 0 && iv[0] != NUM_STATS) + SetMonData(mon, MON_DATA_HP_IV + iv[0], &perfectIv); + + //Set ability + SetMonData(mon, MON_DATA_ABILITY_NUM, &abilityNum); + + // Set Held Item + if (item) + SetMonData(mon, MON_DATA_HELD_ITEM, &item); + + //Set moves + for (i = 0; i < MAX_MON_MOVES; i++) + SetMonMoveSlot(mon, moves[i], i); + + CalculateMonStats(mon); +} + +// gets a random level of the species based on map data. +//if it was a hidden encounter, updates the environment it is to be found from the wildheader encounterRate +static u8 DexNavTryGenerateMonLevel(u16 species, u8 environment) +{ + u8 levelBase = GetEncounterLevelFromMapData(species, environment); + u8 levelBonus = gSaveBlock3Ptr->dexNavChain / 5; + + if (levelBase == MON_LEVEL_NONEXISTENT) + return MON_LEVEL_NONEXISTENT; //species not found in the area + + if (Random() % 100 < 4) + levelBonus += 10; //4% chance of having a +10 level + + if (levelBase + levelBonus > MAX_LEVEL) + return MAX_LEVEL; + else + return levelBase + levelBonus; +} + +static void DexNavGenerateMoveset(u16 species, u8 searchLevel, u8 encounterLevel, u16* moveDst) +{ + bool8 genMove = FALSE; + u16 randVal = Random() % 100; + u16 i; + u16 eggMoveBuffer[EGG_MOVES_ARRAY_COUNT]; + + // see if first move slot should be an egg move + if (searchLevel < 5) + { + if (SEARCHLEVEL0_MOVECHANCE != 0 && randVal < SEARCHLEVEL0_MOVECHANCE) + genMove = TRUE; + } + else if (searchLevel < 10) + { + if (SEARCHLEVEL5_MOVECHANCE != 0 && randVal < SEARCHLEVEL5_MOVECHANCE) + genMove = TRUE; + } + else if (searchLevel < 25) + { + if (SEARCHLEVEL10_MOVECHANCE != 0 && randVal < SEARCHLEVEL10_MOVECHANCE) + genMove = TRUE; + } + else if (searchLevel < 50) + { + if (SEARCHLEVEL25_MOVECHANCE != 0 && randVal < SEARCHLEVEL25_MOVECHANCE) + genMove = TRUE; + } + else if (searchLevel < 100) + { + if (SEARCHLEVEL50_MOVECHANCE != 0 && randVal < SEARCHLEVEL50_MOVECHANCE) + genMove = TRUE; + } + else + { + if (SEARCHLEVEL100_MOVECHANCE != 0 && randVal < SEARCHLEVEL100_MOVECHANCE) + genMove = TRUE; + } + + // Generate a wild mon just to get the initial moveset (later overwritten by CreateDexNavWildMon) + CreateWildMon(species, encounterLevel, 0); + + // Store generated mon moves into Dex Nav Struct + for (i = 0; i < MAX_MON_MOVES; i++) + moveDst[i] = GetMonData(&gEnemyParty[0], MON_DATA_MOVE1 + i, NULL); + + // set first move slot to a random egg move if search level is good enough + if (genMove) + { + u8 numEggMoves = GetEggMoves(&gEnemyParty[0], eggMoveBuffer); + if (numEggMoves != 0) + moveDst[0] = eggMoveBuffer[Random() % numEggMoves]; + } +} + +static u16 DexNavGenerateHeldItem(u16 species, u8 searchLevel) +{ + u16 randVal = Random() % 100; + u8 searchLevelInfluence = searchLevel >> 1; + u16 item1 = gSpeciesInfo[species].itemCommon; + u16 item2 = gSpeciesInfo[species].itemRare; + + // if both are the same, 100% to hold + if (item1 == item2) + return item1; + + // if no items can be held, then yeah...no items + if (item2 == ITEM_NONE && item1 == ITEM_NONE) + return ITEM_NONE; + + // if only one entry, 50% chance + if (item2 == ITEM_NONE && item1 != ITEM_NONE) + return (randVal < 50) ? item1 : ITEM_NONE; + + // if both are distinct item1 = 50% + srclvl/2; item2 = 5% + srchlvl/2 + if (randVal < (50 + searchLevelInfluence + 5 + searchLevel)) + return (randVal > 5 + searchLevelInfluence) ? item1 : item2; + else + return ITEM_NONE; + + return ITEM_NONE; +} + +static u8 DexNavGetAbilityNum(u16 species, u8 searchLevel) +{ + bool8 genAbility = FALSE; + u16 randVal = Random() % 100; + u8 abilityNum = 0; + + if (searchLevel < 5) + { + #if (SEARCHLEVEL0_ABILITYCHANCE != 0) + if (randVal < SEARCHLEVEL0_ABILITYCHANCE) + genAbility = TRUE; + #endif + } + else if (searchLevel < 10) + { + #if (SEARCHLEVEL5_ABILITYCHANCE != 0) + if (randVal < SEARCHLEVEL5_ABILITYCHANCE) + genAbility = TRUE; + #endif + } + else if (searchLevel < 25) + { + #if (SEARCHLEVEL10_ABILITYCHANCE != 0) + if (randVal < SEARCHLEVEL10_ABILITYCHANCE) + genAbility = TRUE; + #endif + } + else if (searchLevel < 50) + { + #if (SEARCHLEVEL25_ABILITYCHANCE != 0) + if (randVal < SEARCHLEVEL25_ABILITYCHANCE) + genAbility = TRUE; + #endif + } + else if (searchLevel < 100) + { + #if (SEARCHLEVEL50_ABILITYCHANCE != 0) + if (randVal < SEARCHLEVEL50_ABILITYCHANCE) + genAbility = TRUE; + #endif + } + else + { + #if (SEARCHLEVEL100_ABILITYCHANCE != 0) + if (randVal < SEARCHLEVEL100_ABILITYCHANCE) + genAbility = TRUE; + #endif + } + + if (genAbility + && gSpeciesInfo[species].abilities[2] != ABILITY_NONE + && GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT)) + { + //Only give hidden ability if Pokemon has been caught before + abilityNum = 2; + } + else + { + //Pick a normal ability of that Pokemon + if (gSpeciesInfo[species].abilities[1] != ABILITY_NONE) + abilityNum = Random() & 1; + else + abilityNum = 0; + } + + return abilityNum; +} + +static u8 DexNavGeneratePotential(u8 searchLevel) +{ + u8 genChance = 0; + int randVal = Random() % 100; + + if (searchLevel < 5) + { + genChance = SEARCHLEVEL0_ONESTAR + SEARCHLEVEL0_TWOSTAR + SEARCHLEVEL0_THREESTAR; + if (randVal < genChance) + { + // figure out which star it is + if (randVal < SEARCHLEVEL0_ONESTAR) + return 1; + else if (randVal < (SEARCHLEVEL0_ONESTAR + SEARCHLEVEL0_TWOSTAR)) + return 2; + else + return 3; + } + } + else if (searchLevel < 10) + { + genChance = SEARCHLEVEL5_ONESTAR + SEARCHLEVEL5_TWOSTAR + SEARCHLEVEL5_THREESTAR; + if (randVal < genChance) + { + // figure out which star it is + if (randVal < SEARCHLEVEL5_ONESTAR) + return 1; + else if (randVal < (SEARCHLEVEL5_ONESTAR + SEARCHLEVEL5_TWOSTAR)) + return 2; + else + return 3; + } + } + else if (searchLevel < 25) + { + genChance = SEARCHLEVEL10_ONESTAR + SEARCHLEVEL10_TWOSTAR + SEARCHLEVEL10_THREESTAR; + if (randVal < genChance) + { + // figure out which star it is + if (randVal < SEARCHLEVEL10_ONESTAR) + return 1; + else if (randVal < (SEARCHLEVEL10_ONESTAR + SEARCHLEVEL10_TWOSTAR)) + return 2; + else + return 3; + } + } + else if (searchLevel < 50) + { + genChance = SEARCHLEVEL25_ONESTAR + SEARCHLEVEL25_TWOSTAR + SEARCHLEVEL25_THREESTAR; + if (randVal < genChance) + { + // figure out which star it is + if (randVal < SEARCHLEVEL25_ONESTAR) + return 1; + else if (randVal < (SEARCHLEVEL25_ONESTAR + SEARCHLEVEL25_TWOSTAR)) + return 2; + else + return 3; + } + } + else if (searchLevel < 100) + { + genChance = SEARCHLEVEL50_ONESTAR + SEARCHLEVEL50_TWOSTAR + SEARCHLEVEL50_THREESTAR; + if (randVal < genChance) + { + // figure out which star it is + if (randVal < SEARCHLEVEL50_ONESTAR) + return 1; + else if (randVal < (SEARCHLEVEL50_ONESTAR + SEARCHLEVEL50_TWOSTAR)) + return 2; + else + return 3; + } + } + else + { + genChance = SEARCHLEVEL100_ONESTAR + SEARCHLEVEL100_TWOSTAR + SEARCHLEVEL100_THREESTAR; + if (randVal < genChance) + { + // figure out which star it is + if (randVal < SEARCHLEVEL100_ONESTAR) + return 1; + else if (randVal < (SEARCHLEVEL100_ONESTAR + SEARCHLEVEL100_TWOSTAR)) + return 2; + else + return 3; + } + } + + return 0; // No potential +} + +static u8 GetEncounterLevelFromMapData(u16 species, u8 environment) +{ + u16 headerId = GetCurrentMapWildMonHeaderId(); + const struct WildPokemonInfo *landMonsInfo = gWildMonHeaders[headerId].landMonsInfo; + const struct WildPokemonInfo *waterMonsInfo = gWildMonHeaders[headerId].waterMonsInfo; + const struct WildPokemonInfo *hiddenMonsInfo = gWildMonHeaders[headerId].hiddenMonsInfo; + u8 min = 100; + u8 max = 0; + u8 i; + + switch (environment) + { + case ENCOUNTER_TYPE_LAND: // grass + if (landMonsInfo == NULL) + return MON_LEVEL_NONEXISTENT; //Hidden pokemon should only appear on walkable tiles or surf tiles + + for (i = 0; i < LAND_WILD_COUNT; i++) + { + if (landMonsInfo->wildPokemon[i].species == species) + { + min = (min < landMonsInfo->wildPokemon[i].minLevel) ? min : landMonsInfo->wildPokemon[i].minLevel; + max = (max > landMonsInfo->wildPokemon[i].maxLevel) ? max : landMonsInfo->wildPokemon[i].maxLevel; + } + } + break; + case ENCOUNTER_TYPE_WATER: //water + if (waterMonsInfo == NULL) + return MON_LEVEL_NONEXISTENT; //Hidden pokemon should only appear on walkable tiles or surf tiles + + for (i = 0; i < WATER_WILD_COUNT; i++) + { + if (waterMonsInfo->wildPokemon[i].species == species) + { + min = (min < waterMonsInfo->wildPokemon[i].minLevel) ? min : waterMonsInfo->wildPokemon[i].minLevel; + max = (max > waterMonsInfo->wildPokemon[i].maxLevel) ? max : waterMonsInfo->wildPokemon[i].maxLevel; + } + } + break; + case ENCOUNTER_TYPE_HIDDEN: + if (hiddenMonsInfo == NULL) + return MON_LEVEL_NONEXISTENT; + + for (i = 0; i < HIDDEN_WILD_COUNT; i++) + { + if (hiddenMonsInfo->wildPokemon[i].species == species) + { + min = (min < hiddenMonsInfo->wildPokemon[i].minLevel) ? min : hiddenMonsInfo->wildPokemon[i].minLevel; + max = (max > hiddenMonsInfo->wildPokemon[i].maxLevel) ? max : hiddenMonsInfo->wildPokemon[i].maxLevel; + } + } + + // use encounter rate to signify is hidden pokemon are on land or in water + if (hiddenMonsInfo->encounterRate == 1) + sDexNavSearchDataPtr->environment = ENCOUNTER_TYPE_WATER; + else + sDexNavSearchDataPtr->environment = ENCOUNTER_TYPE_LAND; + break; + default: + return MON_LEVEL_NONEXISTENT; + } + + if (max == 0) + return MON_LEVEL_NONEXISTENT; + + return RandomUniform(RNG_DEXNAV_ENCOUNTER_LEVEL, min, max); +} + + +/////////// +/// GUI /// +/////////// +static const struct BgTemplate sDexNavMenuBgTemplates[2] = +{ + { + .bg = 0, + .charBaseIndex = 0, + .mapBaseIndex = 31, + .priority = 0 + }, + { + .bg = 1, + .charBaseIndex = 3, + .mapBaseIndex = 30, + .priority = 1 + } +}; + +static void DexNav_VBlankCB(void) +{ + LoadOam(); + ProcessSpriteCopyRequests(); + TransferPlttBuffer(); +} + +static void DexNav_MainCB(void) +{ + RunTasks(); + AnimateSprites(); + BuildOamBuffer(); + DoScheduledBgTilemapCopiesToVram(); + UpdatePaletteFade(); +} + +static bool8 DexNav_InitBgs(void) +{ + ResetVramOamAndBgCntRegs(); + ResetAllBgsCoordinates(); + sBg1TilemapBuffer = Alloc(0x800); + if (sBg1TilemapBuffer == NULL) + return FALSE; + + memset(sBg1TilemapBuffer, 0, 0x800); + ResetBgsAndClearDma3BusyFlags(0); + InitBgsFromTemplates(0, sDexNavMenuBgTemplates, NELEMS(sDexNavMenuBgTemplates)); + SetBgTilemapBuffer(1, sBg1TilemapBuffer); + ScheduleBgCopyTilemapToVram(1); + SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_1D_MAP | DISPCNT_OBJ_ON); + SetGpuReg(REG_OFFSET_BLDCNT , 0); + ShowBg(0); + ShowBg(1); + return TRUE; +} + +static bool8 DexNav_LoadGraphics(void) +{ + switch (sDexNavUiDataPtr->state) + { + case 0: + ResetTempTileDataBuffers(); + DecompressAndCopyTileDataToVram(1, sDexNavGuiTiles, 0, 0, 0); + sDexNavUiDataPtr->state++; + break; + case 1: + if (FreeTempTileDataBuffersIfPossible() != TRUE) + { + LZDecompressWram(sDexNavGuiTilemap, sBg1TilemapBuffer); + sDexNavUiDataPtr->state++; + } + break; + case 2: + LoadPalette(sDexNavGuiPal, 0, 32); + sDexNavUiDataPtr->state++; + break; + default: + sDexNavUiDataPtr->state = 0; + return TRUE; + } + + return FALSE; +} + +static void UpdateCursorPosition(void) +{ + u16 x, y; + + switch (sDexNavUiDataPtr->cursorRow) + { + case ROW_WATER: + x = ROW_WATER_ICON_X + (24 * sDexNavUiDataPtr->cursorCol); + y = ROW_WATER_ICON_Y; + sDexNavUiDataPtr->environment = ENCOUNTER_TYPE_WATER; + break; + case ROW_LAND_TOP: //land 1 + x = ROW_LAND_ICON_X + (24 * sDexNavUiDataPtr->cursorCol); + y = ROW_LAND_TOP_ICON_Y; + sDexNavUiDataPtr->environment = ENCOUNTER_TYPE_LAND; + break; + case ROW_LAND_BOT: //land 2 + x = ROW_LAND_ICON_X + (24 * sDexNavUiDataPtr->cursorCol); + y = ROW_LAND_BOT_ICON_Y; + sDexNavUiDataPtr->environment = ENCOUNTER_TYPE_LAND; + break; + case ROW_HIDDEN: + x = ROW_HIDDEN_ICON_X + (24 * sDexNavUiDataPtr->cursorCol); + y = ROW_HIDDEN_ICON_Y; + sDexNavUiDataPtr->environment = ENCOUNTER_TYPE_HIDDEN; + break; + default: + return; + } + + gSprites[sDexNavUiDataPtr->cursorSpriteId].x = x; + gSprites[sDexNavUiDataPtr->cursorSpriteId].y = y; + + PrintCurrentSpeciesInfo(); +} + +static void CreateSelectionCursor(void) +{ + u8 spriteId; + struct CompressedSpriteSheet spriteSheet; + + spriteSheet.data = sSelectionCursorGfx; + spriteSheet.size = 0x200; + spriteSheet.tag = SELECTION_CURSOR_TAG; + LoadCompressedSpriteSheet(&spriteSheet); + + LoadPalette(sSelectionCursorPal, (16 * sSelectionCursorOam.paletteNum) + 0x100, 32); + + spriteId = CreateSprite(&sSelectionCursorSpriteTemplate, 12, 32, 0); + //gSprites[spriteId].data[1] = -1; + + sDexNavUiDataPtr->cursorSpriteId = spriteId; + UpdateCursorPosition(); +} + +static void CreateNoDataIcon(s16 x, s16 y) +{ + CreateSprite(&sNoDataIconTemplate, x, y, 0); +} + +static bool8 CapturedAllLandMons(u16 headerId) +{ + u16 i, species; + int count = 0; + const struct WildPokemonInfo* landMonsInfo = gWildMonHeaders[headerId].landMonsInfo; + + if (landMonsInfo != NULL) + { + for (i = 0; i < LAND_WILD_COUNT; ++i) + { + species = landMonsInfo->wildPokemon[i].species; + if (species != SPECIES_NONE) + { + if (!GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT)) + break; + + count++; + } + } + + if (i >= LAND_WILD_COUNT && count > 0) //All land mons caught + return TRUE; + } + else + { + return TRUE; //technically, no mon data means you caught them all + } + + return FALSE; +} + +//Checks if all Pokemon that can be encountered while surfing have been capture +static bool8 CapturedAllWaterMons(u16 headerId) +{ + u32 i; + u16 species; + u8 count = 0; + const struct WildPokemonInfo* waterMonsInfo = gWildMonHeaders[headerId].waterMonsInfo; + + if (waterMonsInfo != NULL) + { + for (i = 0; i < WATER_WILD_COUNT; ++i) + { + species = waterMonsInfo->wildPokemon[i].species; + if (species != SPECIES_NONE) + { + count++; + if (!GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT)) + break; + } + } + + if (i >= WATER_WILD_COUNT && count > 0) + return TRUE; + } + else + { + return TRUE; //technically, no mon data means you caught them all + } + + return FALSE; +} + +static bool8 CapturedAllHiddenMons(u16 headerId) +{ + u32 i; + u16 species; + u8 count = 0; + const struct WildPokemonInfo* hiddenMonsInfo = gWildMonHeaders[headerId].hiddenMonsInfo; + + if (hiddenMonsInfo != NULL) + { + for (i = 0; i < HIDDEN_WILD_COUNT; ++i) + { + species = hiddenMonsInfo->wildPokemon[i].species; + if (species != SPECIES_NONE) + { + count++; + if (!GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT)) + break; + } + } + + if (i >= HIDDEN_WILD_COUNT && count > 0) + return TRUE; + } + else + { + return TRUE; //technically, no mon data means you caught them all + } + + return FALSE; +} + +static void DexNavLoadCapturedAllSymbols(void) +{ + u16 headerId = GetCurrentMapWildMonHeaderId(); + + LoadCompressedSpriteSheetUsingHeap(&sCapturedAllPokemonSpriteSheet); + + if (CapturedAllLandMons(headerId)) + CreateSprite(&sCaptureAllMonsSpriteTemplate, 152, 58, 0); + + if (CapturedAllWaterMons(headerId)) + CreateSprite(&sCaptureAllMonsSpriteTemplate, 139, 17, 0); + + if (CapturedAllHiddenMons(headerId)) + CreateSprite(&sCaptureAllMonsSpriteTemplate, 114, 123, 0); +} + +//#define WIN_DETAILS_TILE 0x3a3 +static void DexNav_InitWindows(void) +{ + InitWindows(sDexNavGuiWindowTemplates); + DeactivateAllTextPrinters(); + ScheduleBgCopyTilemapToVram(0); +} + +static void DexNavGuiFreeResources(void) +{ + Free(sDexNavUiDataPtr); + Free(sBg1TilemapBuffer); + FreeAllWindowBuffers(); +} + +static void CB1_InitDexNavSearch(void) +{ + u8 taskId; + + if (!gPaletteFade.active && !ArePlayerFieldControlsLocked() && gMain.callback2 == CB2_Overworld) + { + SetMainCallback1(CB1_Overworld); + taskId = CreateTask(Task_InitDexNavSearch, 0); + gTasks[taskId].tSpecies = gSpecialVar_0x8000; + gTasks[taskId].tEnvironment = gSpecialVar_0x8001; + } +} + +static void CB1_DexNavSearchCallback(void) +{ + CB1_InitDexNavSearch(); +} + +static void Task_DexNavExitAndSearch(u8 taskId) +{ + DexNavGuiFreeResources(); + DestroyTask(taskId); + SetMainCallback1(CB1_DexNavSearchCallback); + SetMainCallback2(CB2_ReturnToField); +} + +static void Task_DexNavFadeAndExit(u8 taskId) +{ + if (!gPaletteFade.active) + { + SetMainCallback2(sDexNavUiDataPtr->savedCallback); + DexNavGuiFreeResources(); + DestroyTask(taskId); + } +} + +static void DexNavFadeAndExit(void) +{ + BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB_BLACK); + CreateTask(Task_DexNavFadeAndExit, 0); + SetVBlankCallback(DexNav_VBlankCB); + SetMainCallback2(DexNav_MainCB); +} + +static bool8 SpeciesInArray(u16 species, u8 section) +{ + u32 i; + u16 dexNum = SpeciesToNationalPokedexNum(species); + + switch (section) + { + case 0: //land + for (i = 0; i < LAND_WILD_COUNT; i++) + { + if (SpeciesToNationalPokedexNum(sDexNavUiDataPtr->landSpecies[i]) == dexNum) + return TRUE; + } + break; + case 1: //water + for (i = 0; i < WATER_WILD_COUNT; i++) + { + if (SpeciesToNationalPokedexNum(sDexNavUiDataPtr->waterSpecies[i]) == dexNum) + return TRUE; + } + break; + case 2: //hidden + for (i = 0; i < HIDDEN_WILD_COUNT; i++) + { + if (SpeciesToNationalPokedexNum(sDexNavUiDataPtr->hiddenSpecies[i]) == dexNum) + return TRUE; + } + break; + default: + break; + } + + return FALSE; +} + +// get unique wild encounters on current map +static void DexNavLoadEncounterData(void) +{ + u8 grassIndex = 0; + u8 waterIndex = 0; + u8 hiddenIndex = 0; + u16 species; + u32 i; + u16 headerId = GetCurrentMapWildMonHeaderId(); + const struct WildPokemonInfo* landMonsInfo = gWildMonHeaders[headerId].landMonsInfo; + const struct WildPokemonInfo* waterMonsInfo = gWildMonHeaders[headerId].waterMonsInfo; + const struct WildPokemonInfo* hiddenMonsInfo = gWildMonHeaders[headerId].hiddenMonsInfo; + + // nop struct data + memset(sDexNavUiDataPtr->landSpecies, 0, sizeof(sDexNavUiDataPtr->landSpecies)); + memset(sDexNavUiDataPtr->waterSpecies, 0, sizeof(sDexNavUiDataPtr->waterSpecies)); + memset(sDexNavUiDataPtr->hiddenSpecies, 0, sizeof(sDexNavUiDataPtr->hiddenSpecies)); + + // land mons + if (landMonsInfo != NULL && landMonsInfo->encounterRate != 0) + { + for (i = 0; i < LAND_WILD_COUNT; i++) + { + species = landMonsInfo->wildPokemon[i].species; + if (species != SPECIES_NONE && !SpeciesInArray(species, 0)) + sDexNavUiDataPtr->landSpecies[grassIndex++] = landMonsInfo->wildPokemon[i].species; + } + } + + // water mons + if (waterMonsInfo != NULL && waterMonsInfo->encounterRate != 0) + { + for (i = 0; i < WATER_WILD_COUNT; i++) + { + species = waterMonsInfo->wildPokemon[i].species; + if (species != SPECIES_NONE && !SpeciesInArray(species, 1)) + sDexNavUiDataPtr->waterSpecies[waterIndex++] = waterMonsInfo->wildPokemon[i].species; + } + } + + // hidden mons + if (hiddenMonsInfo != NULL) // no encounter rate check since 0 means land, 1 means water encounters + { + for (i = 0; i < HIDDEN_WILD_COUNT; i++) + { + species = hiddenMonsInfo->wildPokemon[i].species; + if (species != SPECIES_NONE && !SpeciesInArray(species, 2)) + sDexNavUiDataPtr->hiddenSpecies[hiddenIndex++] = hiddenMonsInfo->wildPokemon[i].species; + } + } +} + +static void TryDrawIconInSlot(u16 species, s16 x, s16 y) +{ + if (species == SPECIES_NONE || species > NUM_SPECIES) + CreateNoDataIcon(x, y); //'X' in slot + else if (!GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_SEEN)) + CreateMonIcon(SPECIES_NONE, SpriteCB_MonIcon, x, y, 0, 0xFFFFFFFF); //question mark + else + CreateMonIcon(species, SpriteCB_MonIcon, x, y, 0, 0xFFFFFFFF); +} + +static void DrawSpeciesIcons(void) +{ + s16 x, y; + u32 i; + u16 species; + + LoadCompressedSpriteSheetUsingHeap(&sNoDataIconSpriteSheet); + for (i = 0; i < LAND_WILD_COUNT; i++) + { + species = sDexNavUiDataPtr->landSpecies[i]; + x = ROW_LAND_ICON_X + (24 * (i % COL_LAND_COUNT)); + y = ROW_LAND_TOP_ICON_Y + (i > COL_LAND_MAX ? 28 : 0); + TryDrawIconInSlot(species, x, y); + } + + for (i = 0; i < WATER_WILD_COUNT; i++) + { + species = sDexNavUiDataPtr->waterSpecies[i]; + x = ROW_WATER_ICON_X + 24 * i; + y = ROW_WATER_ICON_Y; + TryDrawIconInSlot(species, x, y); + } + + for (i = 0; i < HIDDEN_WILD_COUNT; i++) + { + species = sDexNavUiDataPtr->hiddenSpecies[i]; + x = ROW_HIDDEN_ICON_X + 24 * i; + y = ROW_HIDDEN_ICON_Y; + if (FlagGet(DN_FLAG_DETECTOR_MODE)) + TryDrawIconInSlot(species, x, y); + else if (species == SPECIES_NONE || species > NUM_SPECIES) + CreateNoDataIcon(x, y); + else + CreateMonIcon(SPECIES_NONE, SpriteCB_MonIcon, x, y, 0, 0xFFFFFFFF); //question mark if detector mode inactive + } +} + +static u16 DexNavGetSpecies(void) +{ + u16 species; + + switch (sDexNavUiDataPtr->cursorRow) + { + case ROW_WATER: + species = sDexNavUiDataPtr->waterSpecies[sDexNavUiDataPtr->cursorCol]; + break; + case ROW_LAND_TOP: + species = sDexNavUiDataPtr->landSpecies[sDexNavUiDataPtr->cursorCol]; + break; + case ROW_LAND_BOT: + species = sDexNavUiDataPtr->landSpecies[sDexNavUiDataPtr->cursorCol + COL_LAND_COUNT]; + break; + case ROW_HIDDEN: + if (!FlagGet(DN_FLAG_DETECTOR_MODE)) + species = SPECIES_NONE; + else + species = sDexNavUiDataPtr->hiddenSpecies[sDexNavUiDataPtr->cursorCol]; + break; + default: + return SPECIES_NONE; + } + + if (!GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_SEEN)) + return SPECIES_NONE; + + return species; +} + +static void SetSpriteInvisibility(u8 spriteArrayId, bool8 invisible) +{ + gSprites[sDexNavUiDataPtr->typeIconSpriteIds[spriteArrayId]].invisible = invisible; +} + +// different from pokemon_summary_screen +#define TYPE_ICON_PAL_NUM_0 13 +#define TYPE_ICON_PAL_NUM_1 14 +#define TYPE_ICON_PAL_NUM_2 15 +static const u8 sMoveTypeToOamPaletteNum[NUMBER_OF_MON_TYPES] = +{ + [TYPE_NORMAL] = TYPE_ICON_PAL_NUM_0, + [TYPE_FIGHTING] = TYPE_ICON_PAL_NUM_0, + [TYPE_FLYING] = TYPE_ICON_PAL_NUM_1, + [TYPE_POISON] = TYPE_ICON_PAL_NUM_1, + [TYPE_GROUND] = TYPE_ICON_PAL_NUM_0, + [TYPE_ROCK] = TYPE_ICON_PAL_NUM_0, + [TYPE_BUG] = TYPE_ICON_PAL_NUM_2, + [TYPE_GHOST] = TYPE_ICON_PAL_NUM_1, + [TYPE_STEEL] = TYPE_ICON_PAL_NUM_0, + [TYPE_MYSTERY] = TYPE_ICON_PAL_NUM_2, + [TYPE_FIRE] = TYPE_ICON_PAL_NUM_0, + [TYPE_WATER] = TYPE_ICON_PAL_NUM_1, + [TYPE_GRASS] = TYPE_ICON_PAL_NUM_2, + [TYPE_ELECTRIC] = TYPE_ICON_PAL_NUM_0, + [TYPE_PSYCHIC] = TYPE_ICON_PAL_NUM_1, + [TYPE_ICE] = TYPE_ICON_PAL_NUM_1, + [TYPE_DRAGON] = TYPE_ICON_PAL_NUM_2, + [TYPE_DARK] = TYPE_ICON_PAL_NUM_0, + [TYPE_FAIRY] = TYPE_ICON_PAL_NUM_1, +}; +static void SetTypeIconPosAndPal(u8 typeId, u8 x, u8 y, u8 spriteArrayId) +{ + struct Sprite *sprite; + + sprite = &gSprites[sDexNavUiDataPtr->typeIconSpriteIds[spriteArrayId]]; + StartSpriteAnim(sprite, typeId); + sprite->oam.paletteNum = sMoveTypeToOamPaletteNum[typeId]; + sprite->x = x + 16; + sprite->y = y + 8; + SetSpriteInvisibility(spriteArrayId, FALSE); +} + +static void PrintCurrentSpeciesInfo(void) +{ + u16 species = DexNavGetSpecies(); + u16 dexNum = SpeciesToNationalPokedexNum(species); + u8 type1, type2; + + if (!GetSetPokedexFlag(dexNum, FLAG_GET_SEEN)) + species = SPECIES_NONE; + + // clear windows + FillWindowPixelBuffer(WINDOW_INFO, PIXEL_FILL(TEXT_COLOR_TRANSPARENT)); + + //species name + if (species == SPECIES_NONE) + AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SPECIES_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); + else + AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SPECIES_INFO_Y, sFontColor_Black, 0, GetSpeciesName(species)); + + //type icon(s) + type1 = gSpeciesInfo[species].types[0]; + type2 = gSpeciesInfo[species].types[1]; + if (species == SPECIES_NONE) + type1 = type2 = TYPE_MYSTERY; + + if (type1 == type2) + { + SetTypeIconPosAndPal(type1, 186, 69, 0); + SetSpriteInvisibility(1, TRUE); + } + else + { + SetTypeIconPosAndPal(type1, 168, 69, 0); + SetTypeIconPosAndPal(type2, 168 + 33, 69, 1); + } + + //search level + if (species == SPECIES_NONE) + { + AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); + } + else + { + ConvertIntToDecimalStringN(gStringVar4, GetSearchLevel(dexNum), 0, 4); + AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, gStringVar4); + } + + //hidden ability + if (species == SPECIES_NONE) + { + AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); + } + else if (GetSetPokedexFlag(dexNum, FLAG_GET_CAUGHT)) + { + if (gSpeciesInfo[species].abilities[2] != ABILITY_NONE) + AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, gAbilitiesInfo[gSpeciesInfo[species].abilities[2]].name); + else + AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, gText_PokeSum_Item_None); + } + else + { + AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_CaptureToSee); + } + + //current chain + ConvertIntToDecimalStringN(gStringVar1, gSaveBlock3Ptr->dexNavChain, STR_CONV_MODE_LEFT_ALIGN, 3); + AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, CHAIN_BONUS_Y, sFontColor_Black, 0, gStringVar1); + + CopyWindowToVram(WINDOW_INFO, 3); + PutWindowTilemap(WINDOW_INFO); +} + +#define MAP_NAME_LENGTH 16 +static void PrintMapName(void) +{ + GetMapName(gStringVar3, GetCurrentRegionMapSectionId(), 0); + AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 75 + + GetStringRightAlignXOffset(1, gStringVar3, MAP_NAME_LENGTH * GetFontAttribute(1, FONTATTR_MAX_LETTER_WIDTH)), + 0, sFontColor_White, 0, gStringVar3); + CopyWindowToVram(WINDOW_REGISTERED, 3); +} + +static void PrintSearchableSpecies(u16 species) +{ + FillWindowPixelBuffer(WINDOW_REGISTERED, PIXEL_FILL(TEXT_COLOR_TRANSPARENT)); + PutWindowTilemap(WINDOW_REGISTERED); + if (species == SPECIES_NONE) + { + AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, sText_DexNav_PressRToRegister); + } + else + { + StringCopy(gStringVar1, GetSpeciesName(species)); + StringExpandPlaceholders(gStringVar4, sText_DexNav_SearchForRegisteredSpecies); + AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, gStringVar4); + } + + PrintMapName(); +} + +static void CreateTypeIconSprites(void) +{ + u8 i; + + LoadCompressedSpriteSheet(&gSpriteSheet_MoveTypes); + LoadCompressedPalette(gMoveTypes_Pal, 0x1D0, 0x60); + for (i = 0; i < 2; i++) + { + if (sDexNavUiDataPtr->typeIconSpriteIds[i] == 0xFF) + sDexNavUiDataPtr->typeIconSpriteIds[i] = CreateSprite(&gSpriteTemplate_MoveTypes, 10, 10, 2); + + SetSpriteInvisibility(i, TRUE); + } +} + +static bool8 DexNav_DoGfxSetup(void) +{ + u8 taskId; + + switch (gMain.state) + { + case 0: + SetVBlankHBlankCallbacksToNull(); + ClearScheduledBgCopiesToVram(); + gMain.state++; + break; + case 1: + ScanlineEffect_Stop(); + gMain.state++; + break; + case 2: + FreeAllSpritePalettes(); + gMain.state++; + break; + case 3: + ResetPaletteFade(); + ResetSpriteData(); + ResetTasks(); + gMain.state++; + break; + case 4: + if (DexNav_InitBgs()) + { + sDexNavUiDataPtr->state = 0; + gMain.state++; + } + else + { + DexNavFadeAndExit(); + return TRUE; + } + break; + case 5: + if (DexNav_LoadGraphics() == TRUE) + gMain.state++; + break; + case 6: + DexNav_InitWindows(); + sDexNavUiDataPtr->cursorRow = ROW_LAND_TOP; + sDexNavUiDataPtr->cursorCol = 0; + sDexNavUiDataPtr->environment = ENCOUNTER_TYPE_LAND; + gMain.state++; + break; + case 7: + PrintSearchableSpecies(VarGet(DN_VAR_SPECIES) & DEXNAV_MASK_SPECIES); + DexNavLoadEncounterData(); + gMain.state++; + break; + case 8: + taskId = CreateTask(Task_DexNavWaitFadeIn, 0); + gTasks[taskId].tSpecies = 0; + gTasks[taskId].tEnvironment = sDexNavUiDataPtr->environment; + gMain.state++; + break; + case 9: + sDexNavUiDataPtr->typeIconSpriteIds[0] = 0xFF; + sDexNavUiDataPtr->typeIconSpriteIds[1] = 0xFF; + CreateTypeIconSprites(); + gMain.state++; + break; + case 10: + LoadMonIconPalettes(); + DrawSpeciesIcons(); + CreateSelectionCursor(); + DexNavLoadCapturedAllSymbols(); + gMain.state++; + break; + case 11: + BlendPalettes(0xFFFFFFFF, 16, RGB_BLACK); + gMain.state++; + break; + case 12: + BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB_BLACK); + gMain.state++; + break; + default: + SetVBlankCallback(DexNav_VBlankCB); + SetMainCallback2(DexNav_MainCB); + return TRUE; + } + + return FALSE; +} + +static void DexNav_RunSetup(void) +{ + while (!DexNav_DoGfxSetup()) {} +} + +// Entry point for the dexnav GUI +static void DexNavGuiInit(MainCallback callback) +{ + if ((sDexNavUiDataPtr = AllocZeroed(sizeof(struct DexNavGUI))) == NULL) + { + SetMainCallback2(callback); + return; + } + + sDexNavUiDataPtr->state = 0; + sDexNavUiDataPtr->savedCallback = callback; + SetMainCallback2(DexNav_RunSetup); +} + +void Task_OpenDexNavFromStartMenu(u8 taskId) +{ + if (DEXNAV_ENABLED == FALSE) + { // must have it enabled to enter + DebugPrintfLevel(MGBA_LOG_ERROR, "DexNav was opened when DEXNAV_ENABLED config was disabled! Check include/config/dexnav.h"); + DestroyTask(taskId); + } + else if (!gPaletteFade.active) + { + CleanupOverworldWindowsAndTilemaps(); + DexNavGuiInit(CB2_ReturnToFieldWithOpenMenu); + DestroyTask(taskId); + } +} + +static void Task_DexNavWaitFadeIn(u8 taskId) +{ + if (!gPaletteFade.active) + gTasks[taskId].func = Task_DexNavMain; +} + +static void Task_DexNavMain(u8 taskId) +{ + struct Task *task = &gTasks[taskId]; + u16 species; + + if (IsSEPlaying()) + return; + + if (JOY_NEW(B_BUTTON)) + { + PlaySE(SE_POKENAV_OFF); + BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB_BLACK); + task->func = Task_DexNavFadeAndExit; + } + else if (JOY_NEW(DPAD_UP)) + { + if (sDexNavUiDataPtr->cursorRow == ROW_WATER) + { + sDexNavUiDataPtr->cursorRow = ROW_HIDDEN; + if (sDexNavUiDataPtr->cursorCol >= COL_HIDDEN_COUNT) + sDexNavUiDataPtr->cursorCol = COL_HIDDEN_MAX; + } + else + { + if (sDexNavUiDataPtr->cursorRow == ROW_LAND_TOP && sDexNavUiDataPtr->cursorCol == COL_LAND_MAX) + sDexNavUiDataPtr->cursorCol = COL_WATER_MAX; + + sDexNavUiDataPtr->cursorRow--; + } + + PlaySE(SE_BAG_CURSOR); + UpdateCursorPosition(); + } + else if (JOY_NEW(DPAD_DOWN)) + { + if (sDexNavUiDataPtr->cursorRow == ROW_HIDDEN) + { + sDexNavUiDataPtr->cursorRow = ROW_WATER; + } + else if (sDexNavUiDataPtr->cursorRow == ROW_LAND_BOT) + { + if (sDexNavUiDataPtr->cursorCol >= COL_HIDDEN_COUNT) + sDexNavUiDataPtr->cursorCol = COL_HIDDEN_MAX; + + sDexNavUiDataPtr->cursorRow++; + } + else + { + sDexNavUiDataPtr->cursorRow++; + } + + PlaySE(SE_BAG_CURSOR); + UpdateCursorPosition(); + } + else if (JOY_NEW(DPAD_LEFT)) + { + if (sDexNavUiDataPtr->cursorCol == 0) + { + switch (sDexNavUiDataPtr->cursorRow) + { + case ROW_WATER: + sDexNavUiDataPtr->cursorCol = COL_WATER_MAX; + break; + case ROW_HIDDEN: + sDexNavUiDataPtr->cursorCol = COL_HIDDEN_MAX; + break; + default: + sDexNavUiDataPtr->cursorCol = COL_LAND_MAX; + break; + } + } + else + { + sDexNavUiDataPtr->cursorCol--; + } + + PlaySE(SE_BAG_CURSOR); + UpdateCursorPosition(); + } + else if (JOY_NEW(DPAD_RIGHT)) + { + switch (sDexNavUiDataPtr->cursorRow) + { + case ROW_WATER: + if (sDexNavUiDataPtr->cursorCol == COL_WATER_MAX) + sDexNavUiDataPtr->cursorCol = 0; + else + sDexNavUiDataPtr->cursorCol++; + break; + case ROW_HIDDEN: + if (sDexNavUiDataPtr->cursorCol == COL_HIDDEN_MAX) + sDexNavUiDataPtr->cursorCol = 0; + else + sDexNavUiDataPtr->cursorCol++; + break; + default: + if (sDexNavUiDataPtr->cursorCol == COL_LAND_MAX) + sDexNavUiDataPtr->cursorCol = 0; + else + sDexNavUiDataPtr->cursorCol++; + break; + } + + PlaySE(SE_BAG_CURSOR); + UpdateCursorPosition(); + } + else if (JOY_NEW(R_BUTTON)) + { + // check selection is valid. Play sound if invalid + species = DexNavGetSpecies(); + + if (species != SPECIES_NONE) + { + PrintSearchableSpecies(species); + //PlaySE(SE_DEX_SEARCH); + PlayCry_Script(species, 0); + + // create value to store in a var + VarSet(DN_VAR_SPECIES, ((sDexNavUiDataPtr->environment << 14) | species)); + } + else + { + PlaySE(SE_FAILURE); + } + } + else if (JOY_NEW(A_BUTTON)) + { + species = DexNavGetSpecies(); + if (species == SPECIES_NONE) + { + PlaySE(SE_FAILURE); + } + else + { + gSpecialVar_0x8000 = species; + gSpecialVar_0x8001 = sDexNavUiDataPtr->environment; + gSpecialVar_0x8002 = (sDexNavUiDataPtr->cursorRow == ROW_HIDDEN) ? TRUE : FALSE; + PlaySE(SE_DEX_SEARCH); + BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB_BLACK); + task->func = Task_DexNavExitAndSearch; + } + } +} + +///////////////////////// +//// HIDDEN POKEMON ///// +///////////////////////// +bool8 TryFindHiddenPokemon(void) +{ + u16 *stepPtr = GetVarPointer(DN_VAR_STEP_COUNTER); + + if (DEXNAV_ENABLED == 0 + || !FlagGet(DN_FLAG_DETECTOR_MODE) + || FlagGet(DN_FLAG_SEARCHING) + || GetFlashLevel() > 0) + { + if (stepPtr != NULL) + (*stepPtr) = 0; + return FALSE; + } + + (*stepPtr)++; + (*stepPtr) %= HIDDEN_MON_STEP_COUNT; + if ((*stepPtr) == 0 && (Random() % 100 < HIDDEN_MON_SEARCH_RATE)) + { + // hidden pokemon + u16 headerId = GetCurrentMapWildMonHeaderId(); + u8 index; + u16 species; + u8 environment; + u8 taskId; + const struct WildPokemonInfo* hiddenMonsInfo = gWildMonHeaders[headerId].hiddenMonsInfo; + bool8 isHiddenMon = FALSE; + + // while you can still technically find hidden pokemon if there are not hidden-only pokemon on a map, + // this prevents any potential lagging on maps you dont want hidden pokemon to appear on + if (hiddenMonsInfo == NULL) + return FALSE; + + // encounter rate signifies surfing (1) or land mons (0)! + // again, for simplicity + switch (hiddenMonsInfo->encounterRate) + { + case 0: // land + // there are surely better ways to do this, but this allows greatest flexibility + if (Random() % 100 < HIDDEN_MON_PROBABILTY) + { + index = ChooseHiddenMonIndex(); + if (index == 0xFF) + return FALSE;//no hidden info + species = hiddenMonsInfo->wildPokemon[index].species; + isHiddenMon = TRUE; + environment = ENCOUNTER_TYPE_HIDDEN; + } + else + { + species = gWildMonHeaders[headerId].landMonsInfo->wildPokemon[ChooseWildMonIndex_Land()].species; + environment = ENCOUNTER_TYPE_LAND; + } + break; + case 1: // water + if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING)) + { + if (Random() % 100 < HIDDEN_MON_PROBABILTY) + { + index = ChooseHiddenMonIndex(); + if (index == 0xFF) + return FALSE;//no hidden info + species = hiddenMonsInfo->wildPokemon[index].species; + isHiddenMon = TRUE; + environment = ENCOUNTER_TYPE_HIDDEN; + } + else + { + species = gWildMonHeaders[headerId].waterMonsInfo->wildPokemon[ChooseWildMonIndex_WaterRock()].species; + environment = ENCOUNTER_TYPE_WATER; + + } + } + else + { + // not surfing -> cant find hidden water mons + return FALSE; + } + break; + default: + return FALSE; + } + + if (species == SPECIES_NONE) + return FALSE; + + sDexNavSearchDataPtr = AllocZeroed(sizeof(struct DexNavSearch)); + + // init search data + sDexNavSearchDataPtr->isHiddenMon = isHiddenMon; + sDexNavSearchDataPtr->species = species; + sDexNavSearchDataPtr->hiddenSearch = TRUE; + sDexNavSearchDataPtr->environment = environment; // updated in DexNavTryGenerateMonLevel if hidden mon + sDexNavSearchDataPtr->monLevel = DexNavTryGenerateMonLevel(species, environment); + if (sDexNavSearchDataPtr->monLevel == MON_LEVEL_NONEXISTENT) + { + Free(sDexNavSearchDataPtr); + return FALSE; + } + + // find tile for hidden mon and start effect if possible + if (!TryStartHiddenMonFieldEffect(sDexNavSearchDataPtr->environment, 8, 8, TRUE)) + return FALSE; + + // exclamation mark over player + gFieldEffectArguments[0] = gSaveBlock1Ptr->pos.x; + gFieldEffectArguments[1] = gSaveBlock1Ptr->pos.y; + gFieldEffectArguments[2] = gSprites[gPlayerAvatar.spriteId].subpriority - 1; + gFieldEffectArguments[3] = 2; + ObjectEventGetLocalIdAndMap(&gObjectEvents[gPlayerAvatar.objectEventId], &gFieldEffectArguments[0], &gFieldEffectArguments[1], &gFieldEffectArguments[2]); + FieldEffectStart(FLDEFF_EXCLAMATION_MARK_ICON); + + PlayCry_Script(species, 0); + taskId = CreateTask(Task_SetUpDexNavSearch, 0); + gTasks[taskId].tSpecies = sDexNavSearchDataPtr->species; + gTasks[taskId].tEnvironment = sDexNavSearchDataPtr->environment; + gTasks[taskId].tRevealed = FALSE; + DismissMapNamePopup(); + ChangeBgY_ScreenOff(0, 0, 0); + return FALSE; // we dont actually want to enable the script context or the game will freeze + } + + return FALSE; +} + +static void DrawSearchIcon(void) +{ + struct CompressedSpriteSheet spriteSheet; + + spriteSheet.data = sHiddenSearchIconGfx; + spriteSheet.size = 0x200; + spriteSheet.tag = SELECTION_CURSOR_TAG; + LoadCompressedSpriteSheet(&spriteSheet); + sDexNavSearchDataPtr->iconSpriteId = CreateSprite(&sSearchIconSpriteTemplate, 18, GetSearchWindowY() + 12, 0); +} + +// the initial hidden icon window ONLY shows search icon, ??? instead of name, and the search level (and pokeball icon if owned) +// if the player presses R or moves close enough, the full search window will be created +// this way, if the player is not interested in hidden pokemon it will not be too intrusive +static void DrawHiddenSearchWindow(u8 width) +{ + AddSearchWindow(width); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, SPECIES_ICON_X + 4, 0, sSearchFontColor, TEXT_SKIP_DRAW, sText_ThreeQmarks); + + ConvertIntToDecimalStringN(gStringVar1, sDexNavSearchDataPtr->searchLevel, STR_CONV_MODE_LEFT_ALIGN, 2); + StringExpandPlaceholders(gStringVar4, sText_SearchLevel); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, SPECIES_ICON_X + 4, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + CopyWindowToVram(sDexNavSearchDataPtr->windowId, 2); +} + +static void DexNavDrawHiddenIcons(void) +{ + u16 species = sDexNavSearchDataPtr->species; + + DrawHiddenSearchWindow(12); + DrawSearchIcon(); + + if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT)) + sDexNavSearchDataPtr->ownedIconSpriteId = CreateSprite(&sOwnedIconTemplate, SPECIES_ICON_X + 6, GetSearchWindowY() + 2, 0); + + if (sDexNavSearchDataPtr->isHiddenMon) + sDexNavSearchDataPtr->exclamationSpriteId = CreateSprite(&sHiddenMonIconTemplate, SPECIES_ICON_X + 34, GetSearchWindowY() + 8, 0); +} + +///////////////////////// +//// GENERAL UTILITY //// +///////////////////////// +u32 CalculateDexNavShinyRolls(void) +{ + u32 chainBonus, rndBonus; + u8 chain = gSaveBlock3Ptr->dexNavChain; + + chainBonus = (chain >= 100) ? 10 : (chain >= 50) ? 5 : 0; + rndBonus = (Random() % 100 < 4) ? 4 : 0; + return chainBonus + rndBonus; +} + +void TryIncrementSpeciesSearchLevel() +{ +#if USE_DEXNAV_SEARCH_LEVELS == TRUE + if (gMapHeader.regionMapSectionId != MAPSEC_BATTLE_FRONTIER && gSaveBlock3Ptr->dexNavSearchLevels[gDexNavSpecies] < 255) + gSaveBlock3Ptr->dexNavSearchLevels[gDexNavSpecies]++; +#endif +} + +void ResetDexNavSearch(void) +{ + gSaveBlock3Ptr->dexNavChain = 0; //reset dex nav chaining on new map + VarSet(DN_VAR_STEP_COUNTER, 0); //reset hidden pokemon step counter + if (FlagGet(DN_FLAG_SEARCHING)) + EndDexNavSearch(FindTaskIdByFunc(Task_DexNavSearch)); //moving to new map ends dexnav search +} + +void IncrementDexNavChain(void) +{ + if (gSaveBlock3Ptr->dexNavChain < DEXNAV_CHAIN_MAX) + gSaveBlock3Ptr->dexNavChain++; +} diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 49207af0d..44f588bbd 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -96,7 +96,6 @@ static void ResetObjectEventFldEffData(struct ObjectEvent *); static u8 LoadSpritePaletteIfTagExists(const struct SpritePalette *); static u8 FindObjectEventPaletteIndexByTag(u16); static bool8 ObjectEventDoesElevationMatch(struct ObjectEvent *, u8); -static bool8 IsElevationMismatchAt(u8 elevation, s16 x, s16 y); static bool8 AreElevationsCompatible(u8 a, u8 b); static void ObjectCB_CameraObject(struct Sprite *); static void CameraObject_0(struct Sprite *); @@ -10053,7 +10052,7 @@ static void SetObjectEventSpriteOamTableForLongGrass(struct ObjectEvent *objEven sprite->subspriteTableNum = 5; } -static bool8 IsElevationMismatchAt(u8 elevation, s16 x, s16 y) +bool8 IsElevationMismatchAt(u8 elevation, s16 x, s16 y) { u8 mapElevation; diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 5ce892f50..e958a93df 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -4,6 +4,7 @@ #include "coord_event_weather.h" #include "daycare.h" #include "debug.h" +#include "dexnav.h" #include "event_data.h" #include "event_object_movement.h" #include "event_scripts.h" @@ -120,7 +121,7 @@ void FieldGetPlayerInput(struct FieldInput *input, u16 newKeys, u16 heldKeys) input->pressedAButton = TRUE; if (newKeys & B_BUTTON) input->pressedBButton = TRUE; - if (newKeys & R_BUTTON) + if (newKeys & R_BUTTON && !FlagGet(DN_FLAG_SEARCHING)) input->pressedRButton = TRUE; } } @@ -302,11 +303,18 @@ int ProcessPlayerFieldInput(struct FieldInput *input) ShowStartMenu(); return TRUE; } + + if (input->tookStep && TryFindHiddenPokemon()) + return TRUE; + if (input->pressedSelectButton && UseRegisteredKeyItemOnField() == TRUE) { gFieldInputRecord.pressedSelectButton = TRUE; return TRUE; } + + if (input->pressedRButton && TryStartDexNavSearch()) + return TRUE; if(input->input_field_1_2 && DEBUG_OVERWORLD_MENU && !DEBUG_OVERWORLD_IN_MENU) { diff --git a/src/field_effect.c b/src/field_effect.c index 4184c8152..59cdd8b98 100644 --- a/src/field_effect.c +++ b/src/field_effect.c @@ -91,9 +91,9 @@ static const u32 (*const sFieldEffectFuncs[FLDEFF_COUNT]) (void) = [FLDEFF_JUMP_SMALL_SPLASH] = FldEff_JumpSmallSplash, [FLDEFF_LONG_GRASS] = FldEff_LongGrass, [FLDEFF_JUMP_LONG_GRASS] = FldEff_JumpLongGrass, - [FLDEFF_UNUSED_GRASS] = FldEff_UnusedGrass, - [FLDEFF_UNUSED_GRASS_2] = FldEff_UnusedGrass2, - [FLDEFF_UNUSED_SAND] = FldEff_UnusedSand, + [FLDEFF_SHAKING_GRASS] = FldEff_ShakingGrass, + [FLDEFF_SHAKING_LONG_GRASS] = FldEff_ShakingGrass2, + [FLDEFF_SAND_HOLE] = FldEff_UnusedSand, [FLDEFF_UNUSED_WATER_SURFACING] = FldEff_UnusedWaterSurfacing, [FLDEFF_BERRY_TREE_GROWTH_SPARKLE] = FldEff_BerryTreeGrowthSparkle, [FLDEFF_DEEP_SAND_FOOTPRINTS] = FldEff_DeepSandFootprints, @@ -150,6 +150,7 @@ static const u32 (*const sFieldEffectFuncs[FLDEFF_COUNT]) (void) = [FLDEFF_SNOW_TRACKS_SLITHER] = FldEff_SnowTracksSlither, [FLDEFF_SNOW_TRACKS_BUG] = FldEff_SnowTracksBug, [FLDEFF_SNOW_TRACKS_SPOT] = FldEff_SnowTracksSpot, + [FLDEFF_CAVE_DUST] = FldEff_CaveDust, }; static const struct OamData sNewGameOakOamAttributes = { .y = 0, @@ -463,7 +464,7 @@ void FieldEffectScript_LoadPal(const struct SpritePalette * spritePalette) ApplyGlobalFieldPaletteTint(IndexOfSpritePaletteTag(spritePalette->tag)); } -static void FieldEffectFreeGraphicsResources(struct Sprite *sprite) +void FieldEffectFreeGraphicsResources(struct Sprite *sprite) { u16 tileStart = sprite->sheetTileStart; u8 paletteNum = sprite->oam.paletteNum; @@ -3762,6 +3763,22 @@ static void Task_MoveDeoxysRock_Step(u8 taskId) } } +u32 FldEff_CaveDust(void) +{ + u8 spriteId; + + FieldEffectScript_LoadFadedPal(&gSpritePalette_CaveDust); + SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 8); + spriteId = CreateSpriteAtEnd(&gFieldEffectObjectTemplate_CaveDust, gFieldEffectArguments[0], gFieldEffectArguments[1], 0xFF); + if (spriteId != MAX_SPRITES) + { + gSprites[spriteId].coordOffsetEnabled = TRUE; + gSprites[spriteId].data[0] = 22; + } + + return spriteId; +} + static void Task_DestroyDeoxysRock(u8 taskId); static void DestroyDeoxysRockEffect_CameraShake(s16 *data, u8 taskId); static void DestroyDeoxysRockEffect_RockFragments(s16 *data, u8 taskId); diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index c90013eb8..f9fd597f1 100644 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -386,6 +386,28 @@ void UpdateShadowFieldEffect(struct Sprite *sprite) #undef sMapGroup #undef sYOffset +static const struct SpritePalette* GetGeneralFieldPalette1() +{ + if (OW_SEASONS) + { + switch (gLoadedSeason) + { + case SEASON_SPRING: + default: + return &gSpritePalette_GeneralFieldEffect1; + case SEASON_SUMMER: + return &gSpritePalette_GeneralFieldEffect1Summer; + case SEASON_AUTUMN: + return &gSpritePalette_GeneralFieldEffect1Autumn; + case SEASON_WINTER: + return &gSpritePalette_GeneralFieldEffect1Winter; + + } + } + return &gSpritePalette_GeneralFieldEffect1; +} + + u32 FldEff_TallGrass(void) { s16 x; @@ -402,19 +424,15 @@ u32 FldEff_TallGrass(void) case SEASON_SPRING: default: spriteTemplate = &gFieldEffectObjectTemplate_TallGrass; - spritePalette = &gSpritePalette_GeneralFieldEffect1; break; case SEASON_SUMMER: spriteTemplate = &gFieldEffectObjectTemplate_TallGrassSummer; - spritePalette = &gSpritePalette_GeneralFieldEffect1Summer; break; case SEASON_AUTUMN: spriteTemplate = &gFieldEffectObjectTemplate_TallGrassAutumn; - spritePalette = &gSpritePalette_GeneralFieldEffect1Autumn; break; case SEASON_WINTER: spriteTemplate = &gFieldEffectObjectTemplate_TallGrassWinter; - spritePalette = &gSpritePalette_GeneralFieldEffect1Winter; break; } @@ -422,8 +440,8 @@ u32 FldEff_TallGrass(void) else { spriteTemplate = &gFieldEffectObjectTemplate_TallGrass; - spritePalette = &gSpritePalette_GeneralFieldEffect1; } + spritePalette = GetGeneralFieldPalette1(); FieldEffectScript_LoadFadedPal(spritePalette); x = gFieldEffectArguments[0]; @@ -506,19 +524,15 @@ u32 FldEff_JumpTallGrass(void) case SEASON_SPRING: default: spriteTemplate = &gFieldEffectObjectTemplate_JumpTallGrass; - spritePalette = &gSpritePalette_GeneralFieldEffect1; break; case SEASON_SUMMER: spriteTemplate = &gFieldEffectObjectTemplate_JumpTallGrass; - spritePalette = &gSpritePalette_GeneralFieldEffect1Summer; break; case SEASON_AUTUMN: spriteTemplate = &gFieldEffectObjectTemplate_JumpTallGrass; - spritePalette = &gSpritePalette_GeneralFieldEffect1Autumn; break; case SEASON_WINTER: spriteTemplate = &gFieldEffectObjectTemplate_JumpTallGrass; - spritePalette = &gSpritePalette_GeneralFieldEffect1Winter; break; } @@ -526,8 +540,8 @@ u32 FldEff_JumpTallGrass(void) else { spriteTemplate = &gFieldEffectObjectTemplate_JumpTallGrass; - spritePalette = &gSpritePalette_GeneralFieldEffect1; } + spritePalette = GetGeneralFieldPalette1(); FieldEffectScript_LoadFadedPal(spritePalette); SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 12); @@ -567,8 +581,9 @@ u32 FldEff_LongGrass(void) s16 y; u8 spriteId; struct Sprite *sprite; + const struct SpritePalette *spritePalette = GetGeneralFieldPalette1(); - FieldEffectScript_LoadFadedPal(&gSpritePalette_GeneralFieldEffect1); + FieldEffectScript_LoadFadedPal(spritePalette); x = gFieldEffectArguments[0]; y = gFieldEffectArguments[1]; SetSpritePosToOffsetMapCoords(&x, &y, 8, 8); @@ -633,8 +648,9 @@ u32 FldEff_JumpLongGrass(void) { u8 spriteId; struct Sprite *sprite; + const struct SpritePalette *spritePalette = GetGeneralFieldPalette1(); - FieldEffectScript_LoadFadedPal(&gSpritePalette_GeneralFieldEffect1); + FieldEffectScript_LoadFadedPal(spritePalette); SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 8); spriteId = CreateSpriteAtEnd(&gFieldEffectObjectTemplate_JumpLongGrass, gFieldEffectArguments[0], gFieldEffectArguments[1], 0); if (spriteId != MAX_SPRITES) @@ -654,8 +670,9 @@ u32 FldEff_ShortGrass(void) struct ObjectEvent * objectEvent; u8 spriteId; struct Sprite *sprite; + const struct SpritePalette *spritePalette = GetGeneralFieldPalette1(); - FieldEffectScript_LoadFadedPal(&gSpritePalette_GeneralFieldEffect1); + FieldEffectScript_LoadFadedPal(spritePalette); objectEventId = GetObjectEventIdByLocalIdAndMap(gFieldEffectArguments[0], gFieldEffectArguments[1], gFieldEffectArguments[2]); objectEvent = &gObjectEvents[objectEventId]; spriteId = CreateSpriteAtEnd(&gFieldEffectObjectTemplate_ShortGrass, 0, 0, 0); @@ -1095,8 +1112,9 @@ u32 FldEff_Ripple(void) { u8 spriteId; struct Sprite *sprite; + const struct SpritePalette *spritePalette = GetGeneralFieldPalette1(); - FieldEffectScript_LoadFadedPal(&gSpritePalette_GeneralFieldEffect1); + FieldEffectScript_LoadFadedPal(spritePalette); spriteId = CreateSpriteAtEnd(&gFieldEffectObjectTemplate_Ripple, gFieldEffectArguments[0], gFieldEffectArguments[1], gFieldEffectArguments[2]); if (spriteId != MAX_SPRITES) { @@ -1114,8 +1132,9 @@ u32 FldEff_HotSpringsWater(void) struct ObjectEvent * objectEvent; u8 spriteId; struct Sprite *sprite; + const struct SpritePalette *spritePalette = GetGeneralFieldPalette1(); - FieldEffectScript_LoadFadedPal(&gSpritePalette_GeneralFieldEffect1); + FieldEffectScript_LoadFadedPal(spritePalette); objectEventId = GetObjectEventIdByLocalIdAndMap(gFieldEffectArguments[0], gFieldEffectArguments[1], gFieldEffectArguments[2]); objectEvent = &gObjectEvents[objectEventId]; spriteId = CreateSpriteAtEnd(&gFieldEffectObjectTemplate_HotSpringsWater, 0, 0, 0); @@ -1154,12 +1173,13 @@ void UpdateHotSpringsWaterFieldEffect(struct Sprite *sprite) } } -u32 FldEff_UnusedGrass(void) +u32 FldEff_ShakingGrass(void) { u8 spriteId; struct Sprite *sprite; + const struct SpritePalette *spritePalette = GetGeneralFieldPalette1(); - FieldEffectScript_LoadFadedPal(&gSpritePalette_GeneralFieldEffect1); + FieldEffectScript_LoadFadedPal(spritePalette); SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 8); spriteId = CreateSpriteAtEnd(&gFieldEffectObjectTemplate_UnusedGrass, gFieldEffectArguments[0], gFieldEffectArguments[1], gFieldEffectArguments[2]); if (spriteId != MAX_SPRITES) @@ -1167,17 +1187,18 @@ u32 FldEff_UnusedGrass(void) sprite = &gSprites[spriteId]; sprite->coordOffsetEnabled = TRUE; sprite->oam.priority = gFieldEffectArguments[3]; - sprite->data[0] = FLDEFF_UNUSED_GRASS; + sprite->data[0] = FLDEFF_SHAKING_GRASS; } - return 0; + return spriteId; } -u32 FldEff_UnusedGrass2(void) +u32 FldEff_ShakingGrass2(void) { u8 spriteId; struct Sprite *sprite; + const struct SpritePalette *spritePalette = GetGeneralFieldPalette1(); - FieldEffectScript_LoadFadedPal(&gSpritePalette_GeneralFieldEffect1); + FieldEffectScript_LoadFadedPal(spritePalette); SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 8); spriteId = CreateSpriteAtEnd(&gFieldEffectObjectTemplate_UnusedGrass2, gFieldEffectArguments[0], gFieldEffectArguments[1], gFieldEffectArguments[2]); if (spriteId != MAX_SPRITES) @@ -1185,9 +1206,9 @@ u32 FldEff_UnusedGrass2(void) sprite = &gSprites[spriteId]; sprite->coordOffsetEnabled = TRUE; sprite->oam.priority = gFieldEffectArguments[3]; - sprite->data[0] = FLDEFF_UNUSED_GRASS_2; + sprite->data[0] = FLDEFF_SHAKING_LONG_GRASS; } - return 0; + return spriteId; } u32 FldEff_UnusedSand(void) @@ -1203,9 +1224,9 @@ u32 FldEff_UnusedSand(void) sprite = &gSprites[spriteId]; sprite->coordOffsetEnabled = TRUE; sprite->oam.priority = gFieldEffectArguments[3]; - sprite->data[0] = FLDEFF_UNUSED_SAND; + sprite->data[0] = FLDEFF_SAND_HOLE; } - return 0; + return spriteId; } u32 FldEff_UnusedWaterSurfacing(void) @@ -1223,7 +1244,7 @@ u32 FldEff_UnusedWaterSurfacing(void) sprite->oam.priority = gFieldEffectArguments[3]; sprite->data[0] = FLDEFF_UNUSED_WATER_SURFACING; } - return 0; + return spriteId; } void StartAshFieldEffect(s16 x, s16 y, u16 metatileId, s16 d) @@ -1243,8 +1264,9 @@ u32 FldEff_Ash(void) s16 y; u8 spriteId; struct Sprite *sprite; + const struct SpritePalette *spritePalette = GetGeneralFieldPalette1(); - FieldEffectScript_LoadFadedPal(&gSpritePalette_GeneralFieldEffect1); + FieldEffectScript_LoadFadedPal(spritePalette); x = gFieldEffectArguments[0]; y = gFieldEffectArguments[1]; @@ -1596,7 +1618,7 @@ u32 FldEff_BerryTreeGrowthSparkle(void) UpdateSpritePaletteByTemplate(&gFieldEffectObjectTemplate_Sparkle, sprite); sprite->sWaitFldEff = FLDEFF_BERRY_TREE_GROWTH_SPARKLE; } - return 0; + return spriteId; } u32 FldEff_TreeDisguise(void) @@ -1714,7 +1736,7 @@ u32 FldEff_Sparkle(void) gSprites[spriteId].oam.priority = gFieldEffectArguments[2]; gSprites[spriteId].coordOffsetEnabled = TRUE; } - return 0; + return spriteId; } void UpdateSparkleFieldEffect(struct Sprite *sprite) diff --git a/src/field_player_avatar.c b/src/field_player_avatar.c index 3ba2eba67..61da84ef6 100644 --- a/src/field_player_avatar.c +++ b/src/field_player_avatar.c @@ -521,11 +521,20 @@ static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys) } return; } - + + gPlayerAvatar.creeping = FALSE; if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING) { - // Same speed as running - PlayerWalkFast(direction); + if (FlagGet(DN_FLAG_SEARCHING) && (heldKeys & A_BUTTON)) + { + gPlayerAvatar.creeping = TRUE; + PlayerWalkSlow(direction); + } + else + { + // Same speed as running + PlayerWalkFast(direction); + } return; } @@ -539,6 +548,11 @@ static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys) gPlayerAvatar.flags |= PLAYER_AVATAR_FLAG_DASH; return; } + else if (FlagGet(DN_FLAG_SEARCHING) && (heldKeys & A_BUTTON)) + { + gPlayerAvatar.creeping = TRUE; + PlayerWalkSlow(direction); + } else { if (PlayerIsMovingOnRockStairs(direction)) diff --git a/src/graphics.c b/src/graphics.c index 4c98dd950..e173a8288 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -1479,6 +1479,9 @@ const u32 gBerryCrush_TextWindows_Tilemap[] = INCBIN_U32("graphics/berry_crush/t const u16 gCategoryIcons_Pal[] = INCBIN_U16("graphics/interface/category_icons.gbapal"); const u32 gCategoryIcons_Gfx[] = INCBIN_U32("graphics/interface/category_icons.4bpp.lz"); +const u32 gMoveTypes_Gfx[] = INCBIN_U32("graphics/types/move_types.4bpp.lz"); +const u32 gMoveTypes_Pal[] = INCBIN_U32("graphics/types/move_types.gbapal.lz"); + // New Battle anims Particles const u32 gBattleAnimSpriteGfx_WhiteStreak[] = INCBIN_U32("graphics/battle_anims/sprites/white_streak.4bpp.lz"); const u32 gBattleAnimSpritePal_WhiteStreak[] = INCBIN_U32("graphics/battle_anims/sprites/white_streak.gbapal.lz"); diff --git a/src/new_game.c b/src/new_game.c index 5207a7852..efa5adac8 100644 --- a/src/new_game.c +++ b/src/new_game.c @@ -36,6 +36,7 @@ // this file's functions static void ResetMiniGamesResults(void); static void ResetItemFlags(void); +static void ResetDexNav(void); // EWRAM vars EWRAM_DATA bool8 gDifferentSaveFile = FALSE; @@ -158,6 +159,7 @@ void NewGameInitData(void) StringCopy(gSaveBlock1Ptr->rivalName, rivalName); ResetTrainerTowerResults(); ResetItemFlags(); + ResetDexNav(); } static void ResetMiniGamesResults(void) @@ -174,3 +176,11 @@ static void ResetItemFlags(void) memset(&gSaveBlock3Ptr->itemFlags, 0, sizeof(gSaveBlock3Ptr->itemFlags)); #endif } + +static void ResetDexNav(void) +{ +#if USE_DEXNAV_SEARCH_LEVELS == TRUE + memset(gSaveBlock3Ptr->dexNavSearchLevels, 0, sizeof(gSaveBlock3Ptr->dexNavSearchLevels)); +#endif + gSaveBlock3Ptr->dexNavChain = 0; +} diff --git a/src/overworld.c b/src/overworld.c index ae0f60db7..a3d4f1727 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -4,6 +4,7 @@ #include "cable_club.h" #include "clock.h" #include "credits.h" +#include "dexnav.h" #include "event_data.h" #include "event_object_movement.h" #include "event_scripts.h" @@ -140,7 +141,6 @@ static void SetDefaultFlashLevel(void); static void Overworld_TryMapConnectionMusicTransition(void); static void ChooseAmbientCrySpecies(void); -static void CB2_Overworld(void); static void CB2_LoadMap2(void); static void CB2_LoadMapOnReturnToFieldCableClub(void); static void CB2_ReturnToFieldLocal(void); @@ -779,6 +779,7 @@ void LoadMapFromCameraTransition(u8 mapGroup, u8 mapNum) LoadObjEventTemplatesFromHeader(); TrySetMapSaveWarpStatus(); ClearTempFieldEventData(); + ResetDexNavSearch(); ResetCyclingRoadChallengeData(); RestartWildEncounterImmunitySteps(); MapResetTrainerRematches(mapGroup, mapNum); @@ -815,8 +816,9 @@ static void LoadMapFromWarp(bool32 unused) TrySetMapSaveWarpStatus(); ClearTempFieldEventData(); + ResetDexNavSearch(); // reset hours override on every warp - sHoursOverride = 0; + sHoursOverride = 0; ResetCyclingRoadChallengeData(); RestartWildEncounterImmunitySteps(); MapResetTrainerRematches(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum); @@ -1653,7 +1655,7 @@ void CB2_OverworldBasic(void) OverworldBasic(); } -static void CB2_Overworld(void) +void CB2_Overworld(void) { bool32 fading = !!gPaletteFade.active; if (fading) diff --git a/src/party_menu.c b/src/party_menu.c index e0c22ab1e..c74835dcb 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -2968,7 +2968,7 @@ static void ShowOrHideHeldItemSprite(u16 item, struct PartyMenuBox *menuBox) void LoadHeldItemIcons(void) { - LoadSpriteSheet(&sSpriteSheet_HeldItem); + LoadSpriteSheet(&gSpriteSheet_HeldItem); LoadSpritePalette(&sSpritePalette_HeldItem); } diff --git a/src/pokemon.c b/src/pokemon.c index 577f6fab0..bca5664d7 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -12,7 +12,7 @@ #include "battle_tower.h" #include "battle_z_move.h" #include "data.h" -// #include "dexnav.h" +#include "dexnav.h" #include "event_data.h" #include "event_object_movement.h" #include "evolution_scene.h" @@ -1323,8 +1323,8 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, totalRerolls += 1; if (I_FISHING_CHAIN && gIsFishingEncounter) totalRerolls += CalculateChainFishingShinyRolls(); - // if (gDexNavBattle) - // totalRerolls += CalculateDexNavShinyRolls(); + if (gDexNavSpecies) + totalRerolls += CalculateDexNavShinyRolls(); while (GET_SHINY_VALUE(value, personality) >= SHINY_ODDS && totalRerolls > 0) { diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 9bd59d29a..7a230821f 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -38,6 +38,9 @@ #include "constants/battle_move_effects.h" #include "constants/sound.h" +#define TAG_MOVE_TYPES 30002 +#define TAG_CATEGORY_ICONS 30004 + // needs conflicting header to match (curIndex is s8 in the function, but has to be defined as u8 here) extern s16 SeekToNextMonInBox(struct BoxPokemon * boxMons, u8 curIndex, u8 maxIndex, u8 flags); @@ -965,8 +968,6 @@ static const u16 * const sHpBarPals[] = sPokeSummary_HpBarPalRed, }; -#define TAG_CATEGORY_ICONS 30004 - static const struct OamData sOamData_CategoryIcons = { .size = SPRITE_SIZE(16x16), @@ -1023,6 +1024,172 @@ const struct SpriteTemplate gSpriteTemplate_CategoryIcons = .callback = SpriteCallbackDummy }; +static const struct OamData sOamData_MoveTypes = +{ + .y = 0, + .affineMode = ST_OAM_AFFINE_OFF, + .objMode = ST_OAM_OBJ_NORMAL, + .mosaic = FALSE, + .bpp = ST_OAM_4BPP, + .shape = SPRITE_SHAPE(32x16), + .x = 0, + .matrixNum = 0, + .size = SPRITE_SIZE(32x16), + .tileNum = 0, + .priority = 1, + .paletteNum = 0, + .affineParam = 0, +}; +static const union AnimCmd sSpriteAnim_TypeNone[] = { + ANIMCMD_FRAME(TYPE_NONE * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeNormal[] = { + ANIMCMD_FRAME(TYPE_NORMAL * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeFighting[] = { + ANIMCMD_FRAME(TYPE_FIGHTING * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeFlying[] = { + ANIMCMD_FRAME(TYPE_FLYING * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypePoison[] = { + ANIMCMD_FRAME(TYPE_POISON * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeGround[] = { + ANIMCMD_FRAME(TYPE_GROUND * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeRock[] = { + ANIMCMD_FRAME(TYPE_ROCK * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeBug[] = { + ANIMCMD_FRAME(TYPE_BUG * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeGhost[] = { + ANIMCMD_FRAME(TYPE_GHOST * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeSteel[] = { + ANIMCMD_FRAME(TYPE_STEEL * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeMystery[] = { + ANIMCMD_FRAME(TYPE_MYSTERY * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeFire[] = { + ANIMCMD_FRAME(TYPE_FIRE * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeWater[] = { + ANIMCMD_FRAME(TYPE_WATER * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeGrass[] = { + ANIMCMD_FRAME(TYPE_GRASS * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeElectric[] = { + ANIMCMD_FRAME(TYPE_ELECTRIC * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypePsychic[] = { + ANIMCMD_FRAME(TYPE_PSYCHIC * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeIce[] = { + ANIMCMD_FRAME(TYPE_ICE * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeDragon[] = { + ANIMCMD_FRAME(TYPE_DRAGON * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeDark[] = { + ANIMCMD_FRAME(TYPE_DARK * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeFairy[] = { + ANIMCMD_FRAME(TYPE_FAIRY * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_TypeStellar[] = { + ANIMCMD_FRAME(TYPE_STELLAR * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_CategoryCool[] = { + ANIMCMD_FRAME((CONTEST_CATEGORY_COOL + NUMBER_OF_MON_TYPES) * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_CategoryBeauty[] = { + ANIMCMD_FRAME((CONTEST_CATEGORY_BEAUTY + NUMBER_OF_MON_TYPES) * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_CategoryCute[] = { + ANIMCMD_FRAME((CONTEST_CATEGORY_CUTE + NUMBER_OF_MON_TYPES) * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_CategorySmart[] = { + ANIMCMD_FRAME((CONTEST_CATEGORY_SMART + NUMBER_OF_MON_TYPES) * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd sSpriteAnim_CategoryTough[] = { + ANIMCMD_FRAME((CONTEST_CATEGORY_TOUGH + NUMBER_OF_MON_TYPES) * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; +static const union AnimCmd *const sSpriteAnimTable_MoveTypes[NUMBER_OF_MON_TYPES + CONTEST_CATEGORIES_COUNT] = { + [TYPE_NONE] = sSpriteAnim_TypeNone, + [TYPE_NORMAL] = sSpriteAnim_TypeNormal, + [TYPE_FIGHTING] = sSpriteAnim_TypeFighting, + [TYPE_FLYING] = sSpriteAnim_TypeFlying, + [TYPE_POISON] = sSpriteAnim_TypePoison, + [TYPE_GROUND] = sSpriteAnim_TypeGround, + [TYPE_ROCK] = sSpriteAnim_TypeRock, + [TYPE_BUG] = sSpriteAnim_TypeBug, + [TYPE_GHOST] = sSpriteAnim_TypeGhost, + [TYPE_STEEL] = sSpriteAnim_TypeSteel, + [TYPE_MYSTERY] = sSpriteAnim_TypeMystery, + [TYPE_FIRE] = sSpriteAnim_TypeFire, + [TYPE_WATER] = sSpriteAnim_TypeWater, + [TYPE_GRASS] = sSpriteAnim_TypeGrass, + [TYPE_ELECTRIC] = sSpriteAnim_TypeElectric, + [TYPE_PSYCHIC] = sSpriteAnim_TypePsychic, + [TYPE_ICE] = sSpriteAnim_TypeIce, + [TYPE_DRAGON] = sSpriteAnim_TypeDragon, + [TYPE_DARK] = sSpriteAnim_TypeDark, + [TYPE_FAIRY] = sSpriteAnim_TypeFairy, + [TYPE_STELLAR] = sSpriteAnim_TypeStellar, + [NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_COOL] = sSpriteAnim_CategoryCool, + [NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_BEAUTY] = sSpriteAnim_CategoryBeauty, + [NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_CUTE] = sSpriteAnim_CategoryCute, + [NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_SMART] = sSpriteAnim_CategorySmart, + [NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_TOUGH] = sSpriteAnim_CategoryTough, +}; + +const struct CompressedSpriteSheet gSpriteSheet_MoveTypes = +{ + .data = gMoveTypes_Gfx, + .size = (NUMBER_OF_MON_TYPES + CONTEST_CATEGORIES_COUNT) * 0x100, + .tag = TAG_MOVE_TYPES +}; +const struct SpriteTemplate gSpriteTemplate_MoveTypes = +{ + .tileTag = TAG_MOVE_TYPES, + .paletteTag = TAG_MOVE_TYPES, + .oam = &sOamData_MoveTypes, + .anims = sSpriteAnimTable_MoveTypes, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCallbackDummy +}; + #define FREE_AND_SET_NULL_IF_SET(ptr) \ { \ diff --git a/src/start_menu.c b/src/start_menu.c index ab83bde0a..903b8c198 100644 --- a/src/start_menu.c +++ b/src/start_menu.c @@ -1,6 +1,7 @@ #include "global.h" #include "gflib.h" #include "debug.h" +#include "dexnav.h" #include "scanline_effect.h" #include "overworld.h" #include "link.h" @@ -35,6 +36,7 @@ #include "rtc.h" #include "save_menu_util.h" #include "help_system.h" +#include "wild_encounter.h" #include "constants/songs.h" #include "constants/field_weather.h" @@ -50,6 +52,7 @@ enum StartMenuOption STARTMENU_RETIRE, STARTMENU_PLAYER2, STARTMENU_DEBUG, + STARTMENU_DEXNAV, MAX_STARTMENU_ITEMS }; @@ -92,6 +95,7 @@ static bool8 StartMenuExitCallback(void); static bool8 StartMenuSafariZoneRetireCallback(void); static bool8 StartMenuLinkPlayerCallback(void); static bool8 StartMenuDebugCallback(void); +static bool8 StartMenuDexNavCallback(void); static bool8 StartCB_Save1(void); static bool8 StartCB_Save2(void); static void StartMenu_PrepareForSave(void); @@ -130,6 +134,7 @@ static const struct MenuAction sStartMenuActionTable[] = { [STARTMENU_RETIRE] = {gText_MenuRetire, {.u8_void = StartMenuSafariZoneRetireCallback}}, [STARTMENU_PLAYER2] = {gText_MenuPlayer, {.u8_void = StartMenuLinkPlayerCallback}}, [STARTMENU_DEBUG] = {sText_MenuDebug, {.u8_void = StartMenuDebugCallback}}, + [STARTMENU_DEXNAV] = {gText_MenuDexNav, {.u8_void = StartMenuDexNavCallback}}, }; static const struct WindowTemplate sTimeWindowTemplate = { @@ -251,6 +256,8 @@ static void SetUpStartMenu_NormalField(void) { if (FlagGet(FLAG_SYS_POKEDEX_GET) == TRUE) AppendToStartMenuItems(STARTMENU_POKEDEX); + if (DN_FLAG_DEXNAV_GET != 0 && FlagGet(DN_FLAG_DEXNAV_GET)) + AppendToStartMenuItems(STARTMENU_DEXNAV); if (FlagGet(FLAG_SYS_POKEMON_GET) == TRUE) AppendToStartMenuItems(STARTMENU_POKEMON); AppendToStartMenuItems(STARTMENU_BAG); @@ -537,6 +544,9 @@ static bool8 StartCB_HandleInput(void) PlaySE(SE_SELECT); if (!StartMenuPokedexSanityCheck()) return FALSE; + if (sStartMenuOrder[sStartMenuCursorPos] == STARTMENU_DEXNAV + && MapHasNoEncounterData()) + return FALSE; sStartMenuCallback = sStartMenuActionTable[sStartMenuOrder[sStartMenuCursorPos]].func.u8_void; StartMenu_FadeScreenIfLeavingOverworld(); return FALSE; @@ -567,7 +577,7 @@ static void StartMenu_FadeScreenIfLeavingOverworld(void) static bool8 StartMenuPokedexSanityCheck(void) { - if (sStartMenuActionTable[sStartMenuOrder[sStartMenuCursorPos]].func.u8_void == StartMenuPokedexCallback && GetNationalPokedexCount(0) == 0) + if (sStartMenuActionTable[sStartMenuOrder[sStartMenuCursorPos]].func.u8_void == StartMenuPokedexCallback && GetNationalPokedexCount(FLAG_GET_SEEN) == 0) return FALSE; return TRUE; } @@ -1153,3 +1163,9 @@ void AppendToList(u8 *list, u8 *cursor, u8 newEntry) list[*cursor] = newEntry; (*cursor)++; } + +static bool8 StartMenuDexNavCallback(void) +{ + CreateTask(Task_OpenDexNavFromStartMenu, 0); + return TRUE; +} diff --git a/src/strings.c b/src/strings.c index c0b3ada8a..f0c026a90 100644 --- a/src/strings.c +++ b/src/strings.c @@ -174,6 +174,7 @@ const u8 gText_MenuSave[] = _("SAVE"); const u8 gText_MenuOption[] = _("OPTION"); const u8 gText_MenuExit[] = _("EXIT"); const u8 gText_MenuRetire[] = _("RETIRE"); +const u8 gText_MenuDexNav[] = _("DEXNAV"); const u8 gText_MenuSafariStats[] = _("{STR_VAR_1}/{STR_VAR_2}\nBALLS {STR_VAR_3}"); const u8 gText_MenuTime[] = _("Time: {STR_VAR_1}:{STR_VAR_2}"); const u8 gText_MenuDay[] = _("{STR_VAR_1} Day {STR_VAR_2}"); diff --git a/src/text_window.c b/src/text_window.c index 286caea1e..ce0eb875a 100644 --- a/src/text_window.c +++ b/src/text_window.c @@ -55,6 +55,9 @@ static const struct TextWindowGraphics sWindowFrames[] = { {.tiles = sTextWindowFrame10_Gfx, .pal = sTextWindowFrame10_Pal}, }; +static const u16 sTextWindowDexNavFrame[] = INCBIN_U16("graphics/text_window/dexnav_pal.gbapal"); +static const struct TextWindowGraphics sDexNavWindowFrame = {sTextWindowFrame1_Gfx, sTextWindowDexNavFrame}; + const struct TextWindowGraphics *GetUserWindowGraphics(u8 id) { if (id >= ARRAY_COUNT(sWindowFrames)) @@ -198,3 +201,9 @@ const u16 *GetTextWindowPalette(u8 id) else return sTextWindowPalettes[id]; } + +void LoadDexNavWindowGfx(u8 windowId, u16 destOffset, u8 palOffset) +{ + LoadBgTiles(GetWindowAttribute(windowId, WINDOW_BG), sDexNavWindowFrame.tiles, 0x120, destOffset); + LoadPalette(sDexNavWindowFrame.pal, palOffset, 32); +} diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 746f38cf2..77e8da08e 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -91,7 +91,7 @@ void DisableWildEncounters(bool8 state) sWildEncountersDisabled = state; } -static u8 ChooseWildMonIndex_Land(void) +u8 ChooseWildMonIndex_Land(void) { u8 wildMonIndex = 0; bool8 swap = FALSE; @@ -131,7 +131,7 @@ static u8 ChooseWildMonIndex_Land(void) return wildMonIndex; } -static u8 ChooseWildMonIndex_WaterRock(void) +u8 ChooseWildMonIndex_WaterRock(void) { u8 wildMonIndex = 0; bool8 swap = FALSE; @@ -257,7 +257,7 @@ static u8 ChooseWildMonLevel(const struct WildPokemon *wildPokemon, u8 wildMonIn } } -static u16 GetCurrentMapWildMonHeaderId(void) +u16 GetCurrentMapWildMonHeaderId(void) { u16 i; @@ -322,7 +322,7 @@ u8 PickWildMonNature(void) return Random() % NUM_NATURES; } -static void CreateWildMon(u16 species, u8 level, u8 slot) +void CreateWildMon(u16 species, u8 level, u8 unownSlot) { u32 personality; s8 chamber; @@ -342,7 +342,7 @@ static void CreateWildMon(u16 species, u8 level, u8 slot) if (species == SPECIES_UNOWN) { chamber = gSaveBlock1Ptr->location.mapNum - MAP_NUM(SEVEN_ISLAND_TANOBY_RUINS_MONEAN_CHAMBER); - unownLetter = sUnownLetterSlots[chamber][slot]; + unownLetter = sUnownLetterSlots[chamber][unownSlot]; } if (checkCuteCharm @@ -1104,3 +1104,24 @@ bool8 TryDoDoubleWildBattle(void) return TRUE; return FALSE; } + +u8 ChooseHiddenMonIndex(void) +{ + #ifdef ENCOUNTER_CHANCE_HIDDEN_MONS_TOTAL + u8 rand = Random() % ENCOUNTER_CHANCE_HIDDEN_MONS_TOTAL; + + if (rand < ENCOUNTER_CHANCE_HIDDEN_MONS_SLOT_0) + return 0; + else if (rand >= ENCOUNTER_CHANCE_HIDDEN_MONS_SLOT_0 && rand < ENCOUNTER_CHANCE_HIDDEN_MONS_SLOT_1) + return 1; + else + return 2; + #else + return 0xFF; + #endif +} + +bool32 MapHasNoEncounterData(void) +{ + return (GetCurrentMapWildMonHeaderId() == HEADER_NONE); +}