diff --git a/data/event_scripts.s b/data/event_scripts.s index 513bd3f590a3..4d5e0a339384 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -1151,3 +1151,4 @@ EventScript_VsSeekerChargingDone:: .include "data/scripts/follower.inc" .include "data/text/save.inc" .include "data/text/birch_speech.inc" + .include "data/scripts/dexnav.inc" diff --git a/data/field_effect_scripts.s b/data/field_effect_scripts.s index baa0ddb5037f..042525223224 100644 --- a/data/field_effect_scripts.s +++ b/data/field_effect_scripts.s @@ -25,9 +25,9 @@ gFieldEffectScriptPointers:: .4byte gFieldEffectScript_JumpSmallSplash @ FLDEFF_JUMP_SMALL_SPLASH .4byte gFieldEffectScript_LongGrass @ FLDEFF_LONG_GRASS .4byte gFieldEffectScript_JumpLongGrass @ FLDEFF_JUMP_LONG_GRASS - .4byte gFieldEffectScript_UnusedGrass @ FLDEFF_UNUSED_GRASS - .4byte gFieldEffectScript_UnusedGrass2 @ FLDEFF_UNUSED_GRASS_2 - .4byte gFieldEffectScript_UnusedSand @ FLDEFF_UNUSED_SAND + .4byte gFieldEffectScript_ShakingGrass @ FLDEFF_SHAKING_GRASS + .4byte gFieldEffectScript_ShakingGrass2 @ FLDEFF_SHAKING_LONG_GRASS + .4byte gFieldEffectScript_UnusedSand @ FLDEFF_SAND_HOLE .4byte gFieldEffectScript_WaterSurfacing @ FLDEFF_WATER_SURFACING .4byte gFieldEffectScript_BerryTreeGrowthSparkle @ FLDEFF_BERRY_TREE_GROWTH_SPARKLE .4byte gFieldEffectScript_DeepSandFootprints @ FLDEFF_DEEP_SAND_FOOTPRINTS @@ -79,7 +79,8 @@ gFieldEffectScriptPointers:: .4byte gFieldEffectScript_TracksSlither @ FLDEFF_TRACKS_SLITHER .4byte gFieldEffectScript_TracksBug @ FLDEFF_TRACKS_BUG .4byte gFieldEffectScript_TracksSpot @ FLDEFF_TRACKS_SPOT - + .4byte gFieldEffectScript_CaveDust @ FLDEFF_CAVE_DUST + gFieldEffectScript_ExclamationMarkIcon1:: field_eff_callnative FldEff_ExclamationMarkIcon field_eff_end @@ -156,12 +157,12 @@ gFieldEffectScript_JumpLongGrass:: field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_JumpLongGrass field_eff_end -gFieldEffectScript_UnusedGrass:: - field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_UnusedGrass +gFieldEffectScript_ShakingGrass:: + field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_ShakingGrass field_eff_end -gFieldEffectScript_UnusedGrass2:: - field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_UnusedGrass2 +gFieldEffectScript_ShakingGrass2:: + field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect1, FldEff_ShakingGrass2 field_eff_end gFieldEffectScript_UnusedSand:: @@ -374,3 +375,7 @@ gFieldEffectScript_TracksSpot:: gFieldEffectScript_TracksSlither:: field_eff_loadfadedpal_callnative gSpritePalette_GeneralFieldEffect0, FldEff_TracksSlither field_eff_end + +gFieldEffectScript_CaveDust:: + field_eff_loadfadedpal_callnative gSpritePalette_CaveDust FldEff_CaveDust + field_eff_end diff --git a/data/scripts/dexnav.inc b/data/scripts/dexnav.inc new file mode 100644 index 000000000000..698f5019360c --- /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 000000000000..2ed8b40ca982 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 000000000000..0b64cf95b870 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 000000000000..77939a5aba0b --- /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 000000000000..0f62abbef9ce 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 000000000000..4e3327b8eee2 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 000000000000..2ad2e22e08d0 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 000000000000..d462ddef9311 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 000000000000..a26515b5b17e 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 000000000000..fdc5ef71130b 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 000000000000..5d4b034a45fc 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 000000000000..74b6a14eed3e 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 000000000000..ed31236c4e36 --- /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 000000000000..1f477c7803d4 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 000000000000..cb6acc371d35 --- /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/include/config/dexnav.h b/include/config/dexnav.h new file mode 100644 index 000000000000..20d05f7a23f7 --- /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 FLAG_SYS_DEXNAV_SEARCH 0 // Searching for mon +#define FLAG_SYS_DEXNAV_GET 0 // DexNav shows in start menu +#define FLAG_SYS_DETECTOR_MODE 0 // Allow player to find hidden mons +#define VAR_DEXNAV_SPECIES 0 // Registered DexNav species +#define VAR_DEXNAV_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 f6e6106d90a3..48406195f0ba 100644 --- a/include/constants/field_effects.h +++ b/include/constants/field_effects.h @@ -20,9 +20,9 @@ #define FLDEFF_JUMP_SMALL_SPLASH 16 #define FLDEFF_LONG_GRASS 17 #define FLDEFF_JUMP_LONG_GRASS 18 -#define FLDEFF_UNUSED_GRASS 19 -#define FLDEFF_UNUSED_GRASS_2 20 -#define FLDEFF_UNUSED_SAND 21 +#define FLDEFF_SHAKING_GRASS 19 +#define FLDEFF_SHAKING_LONG_GRASS 20 +#define FLDEFF_SAND_HOLE 21 #define FLDEFF_WATER_SURFACING 22 #define FLDEFF_BERRY_TREE_GROWTH_SPARKLE 23 #define FLDEFF_DEEP_SAND_FOOTPRINTS 24 @@ -75,6 +75,7 @@ #define FLDEFF_TRACKS_SLITHER 70 #define FLDEFF_TRACKS_SPOT 71 #define FLDEFF_TRACKS_BUG 72 +#define FLDEFF_CAVE_DUST 73 #define FLDEFFOBJ_SHADOW_S 0 #define FLDEFFOBJ_SHADOW_M 1 @@ -116,6 +117,7 @@ #define FLDEFFOBJ_TRACKS_SLITHER 37 #define FLDEFFOBJ_TRACKS_SPOT 38 #define FLDEFFOBJ_TRACKS_BUG 39 +#define FLDEFFOBJ_CAVE_DUST 40 #define FLDEFF_PAL_TAG_CUT_GRASS 0x1000 #define FLDEFF_PAL_TAG_SECRET_POWER_TREE 0x1003 @@ -129,5 +131,6 @@ #define FLDEFF_PAL_TAG_SMALL_SPARKLE 0x100F #define FLDEFF_PAL_TAG_HOF_MONITOR 0x1010 #define FLDEFF_PAL_TAG_UNKNOWN 0x1011 +#define FLDEFF_PAL_TAG_CAVE_DUST 0x1012 #endif // GUARD_FIELD_EFFECT_CONSTANTS_H diff --git a/include/constants/game_stat.h b/include/constants/game_stat.h index 5796afbaec6d..17e328d5beaf 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_PLAYED_BERRY_CRUSH 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 89508e374d82..3476bd3680d2 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -8,6 +8,7 @@ #include "config/caps.h" #include "config/pokemon.h" #include "config/overworld.h" +#include "config/dexnav.h" // Invalid Versions show as "----------" in Gen 4 and Gen 5's summary screen. // In Gens 6 and 7, invalid versions instead show "a distant land" in the summary screen. diff --git a/include/constants/wild_encounter.h b/include/constants/wild_encounter.h index ae669a2c35a2..364d18350fc4 100644 --- a/include/constants/wild_encounter.h +++ b/include/constants/wild_encounter.h @@ -5,6 +5,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 diff --git a/include/daycare.h b/include/daycare.h index 4d5b470f8b3e..a7ef5581f91f 100644 --- a/include/daycare.h +++ b/include/daycare.h @@ -34,5 +34,6 @@ void ShowDaycareLevelMenu(void); void ChooseSendDaycareMon(void); u8 GetEggMovesBySpecies(u16 species, u16 *eggMoves); bool8 SpeciesCanLearnEggMove(u16 species, u16 move); +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 000000000000..baa8f8c5a74f --- /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(u16 dexNum); +void ResetDexNavSearch(void); +bool8 TryFindHiddenPokemon(void); +u32 CalculateDexNavShinyRolls(void); +void IncrementDexNavChain(void); + +extern bool8 gDexNavBattle; + +#endif // GUARD_DEXNAV_H diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 4b1b3e1f9ff3..f6aa18280f78 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -316,6 +316,7 @@ u8 GetJumpMovementAction(u32); u8 GetJump2MovementAction(u32); u8 CopySprite(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); u8 CreateCopySpriteAt(struct Sprite *sprite, s16 x, s16 y, u8 subpriority); +bool8 IsElevationMismatchAt(u8, s16, s16); u8 MovementType_WanderAround_Step0(struct ObjectEvent *, struct Sprite *); u8 MovementType_WanderAround_Step1(struct ObjectEvent *, struct Sprite *); diff --git a/include/event_scripts.h b/include/event_scripts.h index 60a8d6123e55..133d9fa98bb9 100644 --- a/include/event_scripts.h +++ b/include/event_scripts.h @@ -653,5 +653,13 @@ extern const u8 Common_Movement_FollowerSafeEnd[]; extern const u8 EventScript_CancelMessageBox[]; extern const u8 Common_EventScript_ShowPokemonCenterSign[]; extern const u8 Common_EventScript_ShowPokemartSign[]; +// 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_control_avatar.h b/include/field_control_avatar.h index da23fe433683..9a55f9e7bf06 100644 --- a/include/field_control_avatar.h +++ b/include/field_control_avatar.h @@ -11,7 +11,7 @@ struct FieldInput bool8 heldDirection2:1; bool8 tookStep:1; bool8 pressedBButton:1; - bool8 input_field_1_0:1; + bool8 pressedRButton:1; bool8 input_field_1_1:1; bool8 input_field_1_2:1; bool8 input_field_1_3:1; diff --git a/include/field_effect.h b/include/field_effect.h index 93a74f1ba274..3db86bc21975 100644 --- a/include/field_effect.h +++ b/include/field_effect.h @@ -48,5 +48,7 @@ void MultiplyPaletteRGBComponents(u16 i, u8 r, u8 g, u8 b); void FreeResourcesAndDestroySprite(struct Sprite *sprite, u8 spriteId); u8 CreateMonSprite_PicBox(u16 species, s16 x, s16 y, u8 subpriority); void StartEscapeRopeFieldEffect(void); +void FieldEffectFreeGraphicsResources(struct Sprite *sprite); +void FieldEff_CaveDust(void); #endif // GUARD_FIELD_EFFECTS_H diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index d686a624a235..8ca0422cc806 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -323,7 +323,8 @@ struct PlayerAvatar { /*0x00*/ u8 flags; /*0x01*/ u8 transitionFlags; // used to be named bike, but its definitely not that. seems to be some 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 687281eaa7d8..3e5e984547c5 100644 --- a/include/global.h +++ b/include/global.h @@ -211,7 +211,11 @@ 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; +}; /* max size 1624 bytes */ extern struct SaveBlock3 *gSaveBlock3Ptr; diff --git a/include/party_menu.h b/include/party_menu.h index a34f06cec6bb..5236dd27a702 100644 --- a/include/party_menu.h +++ b/include/party_menu.h @@ -27,6 +27,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_icon.h b/include/pokemon_icon.h index 9e51e1bc4d60..3986f8948b0c 100644 --- a/include/pokemon_icon.h +++ b/include/pokemon_icon.h @@ -24,5 +24,6 @@ void LoadMonIconPalettePersonality(u16 species, u32 personality); void SpriteCB_MonIcon(struct Sprite *sprite); void SetPartyHPBarSprite(struct Sprite *sprite, u8 animNum); u8 GetMonIconPaletteIndexFromSpecies(u16 species); +void SafeFreeMonIconPalette(u16 species); #endif // GUARD_POKEMON_ICON_H diff --git a/include/random.h b/include/random.h index 000bc1302ced..886094733a9f 100644 --- a/include/random.h +++ b/include/random.h @@ -176,6 +176,7 @@ enum RandomTag RNG_RANDOM_TARGET, RNG_AI_PREDICT_ABILITY, RNG_HEALER, + RNG_DEXNAV_ENCOUNTER_LEVEL, }; #define RandomWeighted(tag, ...) \ diff --git a/include/strings.h b/include/strings.h index 8e1f797079e1..06a3a658a25b 100644 --- a/include/strings.h +++ b/include/strings.h @@ -209,6 +209,7 @@ extern const u8 gText_MenuOption[]; extern const u8 gText_MenuExit[]; extern const u8 gText_MenuRetire[]; extern const u8 gText_MenuRest[]; +extern const u8 gText_MenuDexNav[]; extern const u8 gText_Floor1[]; extern const u8 gText_Floor2[]; extern const u8 gText_Floor3[]; diff --git a/include/text_window.h b/include/text_window.h index a292d1309e71..98776193f649 100644 --- a/include/text_window.h +++ b/include/text_window.h @@ -25,5 +25,6 @@ void rbox_fill_rectangle(u8 windowId); const u16 *GetTextWindowPalette(u8 id); const u16 *GetOverworldTextboxPalettePtr(void); void LoadSignPostWindowFrameGfx(void); +void LoadDexNavWindowGfx(u8 windowId, u16 destOffset, u8 palOffset); #endif // GUARD_TEXT_WINDOW_H diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 63289f081ccc..c7abb72aa1fa 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -23,6 +23,7 @@ struct WildPokemonHeader const struct WildPokemonInfo *landMonsInfo; const struct WildPokemonInfo *waterMonsInfo; const struct WildPokemonInfo *rockSmashMonsInfo; + const struct WildPokemonInfo *hiddenMonsInfo; const struct WildPokemonInfo *fishingMonsInfo; }; @@ -43,5 +44,11 @@ bool8 UpdateRepelCounter(void); bool8 TryDoDoubleWildBattle(void); bool8 StandardWildEncounter_Debug(void); u32 CalculateChainFishingShinyRolls(void); +void CreateWildMon(u16 species, u8 level); +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 e2b5d1c43c31..c4b5f7484145 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -20,6 +20,7 @@ #include "data.h" #include "debug.h" #include "decompress.h" +#include "dexnav.h" #include "dma3.h" #include "event_data.h" #include "evolution_scene.h" @@ -5689,6 +5690,12 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void) { gIsFishingEncounter = FALSE; gIsSurfingEncounter = FALSE; + if (gDexNavBattle && (gBattleOutcome == B_OUTCOME_WON || gBattleOutcome == B_OUTCOME_CAUGHT)) + IncrementDexNavChain(); + else + gSaveBlock3Ptr->dexNavChain = 0; + + gDexNavBattle = FALSE; ResetSpriteData(); if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK diff --git a/src/data/field_effects/field_effect_object_template_pointers.h b/src/data/field_effects/field_effect_object_template_pointers.h index 14113d3a43bb..ee00023a51f7 100755 --- a/src/data/field_effects/field_effect_object_template_pointers.h +++ b/src/data/field_effects/field_effect_object_template_pointers.h @@ -38,6 +38,7 @@ extern const struct SpriteTemplate gFieldEffectObjectTemplate_Rayquaza; extern const struct SpriteTemplate gFieldEffectObjectTemplate_SlitherTracks; extern const struct SpriteTemplate gFieldEffectObjectTemplate_BugTracks; extern const struct SpriteTemplate gFieldEffectObjectTemplate_SpotTracks; +extern const struct SpriteTemplate gFieldEffectObjectTemplate_CaveDust; const struct SpriteTemplate *const gFieldEffectObjectTemplatePointers[] = { [FLDEFFOBJ_SHADOW_S] = &gFieldEffectObjectTemplate_ShadowSmall, @@ -80,4 +81,5 @@ const struct SpriteTemplate *const gFieldEffectObjectTemplatePointers[] = { [FLDEFFOBJ_TRACKS_SLITHER] = &gFieldEffectObjectTemplate_SlitherTracks, [FLDEFFOBJ_TRACKS_SPOT] = &gFieldEffectObjectTemplate_SpotTracks, [FLDEFFOBJ_TRACKS_BUG] = &gFieldEffectObjectTemplate_BugTracks, + [FLDEFFOBJ_CAVE_DUST] = &gFieldEffectObjectTemplate_CaveDust, }; diff --git a/src/data/field_effects/field_effect_objects.h b/src/data/field_effects/field_effect_objects.h index 1a169007c53a..ecea0350c572 100755 --- a/src/data/field_effects/field_effect_objects.h +++ b/src/data/field_effects/field_effect_objects.h @@ -1336,3 +1336,23 @@ const struct SpriteTemplate gFieldEffectObjectTemplate_Rayquaza = { }; static const struct SpritePalette sSpritePalette_Unused = {gObjectEventPal_Npc3, FLDEFF_PAL_TAG_UNKNOWN}; + +// 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 1f08b77599f9..577263e77b91 100755 --- a/src/data/object_events/object_event_graphics.h +++ b/src/data/object_events/object_event_graphics.h @@ -454,3 +454,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 3bc86593fa01..e6cb13ee6d83 100644 --- a/src/data/party_menu.h +++ b/src/data/party_menu.h @@ -838,7 +838,7 @@ static const u8 *const sUnionRoomTradeMessages[] = }; 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 = { @@ -875,14 +875,14 @@ static const union AnimCmd *const sSpriteAnimTable_HeldItem[] = sSpriteAnim_HeldMail, }; -static const struct SpriteSheet sSpriteSheet_HeldItem = +const struct SpriteSheet gSpriteSheet_HeldItem = { .data = sHeldItemGfx, .size = sizeof(sHeldItemGfx), .tag = TAG_HELD_ITEM }; static const struct SpritePalette sSpritePalette_HeldItem = { - .data = sHeldItemPalette, .tag = TAG_HELD_ITEM + .data = gHeldItemPalette, .tag = TAG_HELD_ITEM }; static const struct SpriteTemplate sSpriteTemplate_HeldItem = diff --git a/src/data/wild_encounters.json.txt b/src/data/wild_encounters.json.txt index bf848249f018..20ac1c788958 100755 --- a/src/data/wild_encounters.json.txt +++ b/src/data/wild_encounters.json.txt @@ -64,8 +64,19 @@ const struct WildPokemon {{ encounter.base_label }}_FishingMons[] = const struct WildPokemonInfo {{ encounter.base_label }}_FishingMonsInfo = { {{encounter.fishing_mons.encounter_rate}}, {{ encounter.base_label }}_FishingMons }; {% 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 }}[] = { ## for encounter in wild_encounter_group.encounters @@ -76,6 +87,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 %}, }, ## endfor { @@ -85,6 +97,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 4997f4efe913..83dd24af0791 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -33,7 +33,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); // RAM buffers used to assist with BuildEggMoveset() @@ -751,7 +750,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 000000000000..86c99b1f4325 --- /dev/null +++ b/src/dexnav.c @@ -0,0 +1,2688 @@ +#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(FLAG_SYS_DEXNAV_SEARCH != 0); +STATIC_ASSERT(FLAG_SYS_DETECTOR_MODE != 0); +STATIC_ASSERT(VAR_DEXNAV_SPECIES != 0); +STATIC_ASSERT(VAR_DEXNAV_STEP_COUNTER != 0); +#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 bool8 gDexNavBattle = FALSE; + +//// 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(); + u8 tileBehaviour; + 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) + { + tileBehaviour = MapGridGetMetatileBehaviorAt(topX, topY); + //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 (MetatileBehavior_IsLandWildEncounter(tileBehaviour)) + { + 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 (MetatileBehavior_IsSurfableWaterOrUnderwater(tileBehaviour)) + { + 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_IsSandOrDeepSand(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_IsSandOrDeepSand(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_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(SpeciesToNationalPokedexNum(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(FLAG_SYS_DEXNAV_SEARCH); + 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_EmptyString2; + } + 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(VAR_DEXNAV_SPECIES); + + if (FlagGet(FLAG_SYS_DEXNAV_SEARCH) || (val & DEXNAV_MASK_SPECIES) == SPECIES_NONE) + return FALSE; + + HideMapNamePopUpWindow(); + 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(FLAG_SYS_DEXNAV_SEARCH); + 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) + { + gDexNavBattle = TRUE; + CreateDexNavWildMon(sDexNavSearchDataPtr->species, sDexNavSearchDataPtr->potential, sDexNavSearchDataPtr->monLevel, + sDexNavSearchDataPtr->abilityNum, sDexNavSearchDataPtr->heldItem, sDexNavSearchDataPtr->moves); + + FlagClear(FLAG_SYS_DEXNAV_SEARCH); + 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); // 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); + + // 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 = 20 + (24 * (i % 6)); + y = ROW_LAND_TOP_ICON_Y + (i > 5 ? 28 : 0); + TryDrawIconInSlot(species, x, y); + } + + for (i = 0; i < WATER_WILD_COUNT; i++) + { + species = sDexNavUiDataPtr->waterSpecies[i]; + x = 30 + 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(FLAG_SYS_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(FLAG_SYS_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_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); +} + +static void PrintMapName(void) +{ + GetMapName(gStringVar3, GetCurrentRegionMapSectionId(), 0); + AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 108 + + 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(VAR_DEXNAV_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_RG_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_RG_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_RG_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_RG_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(VAR_DEXNAV_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(VAR_DEXNAV_STEP_COUNTER); + + if (DEXNAV_ENABLED == 0 + || !FlagGet(FLAG_SYS_DETECTOR_MODE) + || FlagGet(FLAG_SYS_DEXNAV_SEARCH) + || GetFlashLevel() > 0) + { + (*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; + HideMapNamePopUpWindow(); + 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(u16 dexNum) +{ +#if USE_DEXNAV_SEARCH_LEVELS == TRUE + if (gMapHeader.regionMapSectionId != MAPSEC_BATTLE_FRONTIER && gSaveBlock3Ptr->dexNavSearchLevels[dexNum] < 255) + gSaveBlock3Ptr->dexNavSearchLevels[dexNum]++; +#endif +} + +void ResetDexNavSearch(void) +{ + gSaveBlock3Ptr->dexNavChain = 0; //reset dex nav chaining on new map + VarSet(VAR_DEXNAV_STEP_COUNTER, 0); //reset hidden pokemon step counter + if (FlagGet(FLAG_SYS_DEXNAV_SEARCH)) + 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 0645d7073a02..9392a81d8685 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -200,7 +200,6 @@ static bool8 GetFollowerInfo(u16 *species, u8 *form, u8 *shiny); static u8 LoadDynamicFollowerPalette(u16 species, u8 form, bool32 shiny); static const struct ObjectEventGraphicsInfo *SpeciesToGraphicsInfo(u16 species, u8 form); static bool8 NpcTakeStep(struct Sprite *); -static bool8 IsElevationMismatchAt(u8, s16, s16); static bool8 AreElevationsCompatible(u8, u8); static void CopyObjectGraphicsInfoToSpriteTemplate_WithMovementType(u16 graphicsId, u16 movementType, struct SpriteTemplate *spriteTemplate, const struct SubspriteTable **subspriteTables); @@ -9580,7 +9579,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 05da7d2041d9..eabbc202c864 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 "faraway_island.h" #include "event_data.h" #include "event_object_movement.h" @@ -91,7 +92,7 @@ void FieldClearPlayerInput(struct FieldInput *input) input->heldDirection2 = FALSE; input->tookStep = FALSE; input->pressedBButton = FALSE; - input->input_field_1_0 = FALSE; + input->pressedRButton = FALSE; input->input_field_1_1 = FALSE; input->input_field_1_2 = FALSE; input->input_field_1_3 = FALSE; @@ -116,6 +117,8 @@ void FieldGetPlayerInput(struct FieldInput *input, u16 newKeys, u16 heldKeys) input->pressedAButton = TRUE; if (newKeys & B_BUTTON) input->pressedBButton = TRUE; + if (newKeys & R_BUTTON && !FlagGet(FLAG_SYS_DEXNAV_SEARCH)) + input->pressedRButton = TRUE; } if (heldKeys & (DPAD_UP | DPAD_DOWN | DPAD_LEFT | DPAD_RIGHT)) @@ -222,8 +225,15 @@ int ProcessPlayerFieldInput(struct FieldInput *input) ShowStartMenu(); return TRUE; } + + if (input->tookStep && TryFindHiddenPokemon()) + return TRUE; + if (input->pressedSelectButton && UseRegisteredKeyItemOnField() == 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 501a249ee481..dda30138b61d 100644 --- a/src/field_effect.c +++ b/src/field_effect.c @@ -3932,6 +3932,21 @@ static void Task_MoveDeoxysRock(u8 taskId) } } +u8 FldEff_CaveDust(void) +{ + u8 spriteId; + + SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 8); + spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[FLDEFFOBJ_CAVE_DUST], gFieldEffectArguments[0], gFieldEffectArguments[1], 0xFF); + if (spriteId != MAX_SPRITES) + { + gSprites[spriteId].coordOffsetEnabled = TRUE; + gSprites[spriteId].data[0] = 22; + } + + return spriteId; +} + #undef tState #undef tSpriteId #undef tTargetX diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 1991892a5088..c17803d0f243 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1015,7 +1015,7 @@ void UpdateHotSpringsWaterFieldEffect(struct Sprite *sprite) #undef sPrevX #undef sPrevY -u32 FldEff_UnusedGrass(void) +u32 FldEff_ShakingGrass(void) { u8 spriteId; @@ -1026,12 +1026,13 @@ u32 FldEff_UnusedGrass(void) struct Sprite *sprite = &gSprites[spriteId]; sprite->coordOffsetEnabled = TRUE; sprite->oam.priority = gFieldEffectArguments[3]; - sprite->sWaitFldEff = FLDEFF_UNUSED_GRASS; + sprite->sWaitFldEff = FLDEFF_SHAKING_GRASS; } - return 0; + + return spriteId; } -u32 FldEff_UnusedGrass2(void) +u32 FldEff_ShakingGrass2(void) { u8 spriteId; @@ -1042,9 +1043,10 @@ u32 FldEff_UnusedGrass2(void) struct Sprite *sprite = &gSprites[spriteId]; sprite->coordOffsetEnabled = TRUE; sprite->oam.priority = gFieldEffectArguments[3]; - sprite->sWaitFldEff = FLDEFF_UNUSED_GRASS_2; + sprite->sWaitFldEff = FLDEFF_SHAKING_LONG_GRASS; } - return 0; + + return spriteId; } u32 FldEff_UnusedSand(void) @@ -1058,9 +1060,9 @@ u32 FldEff_UnusedSand(void) struct Sprite *sprite = &gSprites[spriteId]; sprite->coordOffsetEnabled = TRUE; sprite->oam.priority = gFieldEffectArguments[3]; - sprite->sWaitFldEff = FLDEFF_UNUSED_SAND; + sprite->sWaitFldEff = FLDEFF_SAND_HOLE; } - return 0; + return spriteId; } u32 FldEff_WaterSurfacing(void) @@ -1076,7 +1078,8 @@ u32 FldEff_WaterSurfacing(void) sprite->oam.priority = gFieldEffectArguments[3]; sprite->sWaitFldEff = FLDEFF_WATER_SURFACING; } - return 0; + + return spriteId; } // Sprite data for FLDEFF_ASH @@ -1474,7 +1477,7 @@ u32 FldEff_BerryTreeGrowthSparkle(void) UpdateSpritePaletteByTemplate(gFieldEffectObjectTemplatePointers[FLDEFFOBJ_SPARKLE], sprite); sprite->sWaitFldEff = FLDEFF_BERRY_TREE_GROWTH_SPARKLE; } - return 0; + return spriteId; } // Sprite data for FLDEFF_TREE_DISGUISE / FLDEFF_MOUNTAIN_DISGUISE / FLDEFF_SAND_DISGUISE @@ -1602,7 +1605,7 @@ u32 FldEff_Sparkle(void) gSprites[spriteId].oam.priority = gFieldEffectArguments[2]; gSprites[spriteId].coordOffsetEnabled = TRUE; } - return 0; + return spriteId; } void UpdateSparkleFieldEffect(struct Sprite *sprite) @@ -1866,27 +1869,3 @@ static void UpdateGrassFieldEffectSubpriority(struct Sprite *sprite, u8 elevatio } } -// Unused, duplicates of data in event_object_movement.c -static const s8 sFigure8XOffsets[FIGURE_8_LENGTH] = { - 1, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 1, 2, 2, 1, 2, - 2, 1, 2, 2, 1, 2, 1, 1, - 2, 1, 1, 2, 1, 1, 2, 1, - 1, 2, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 0, 1, 1, 0, - 1, 0, 1, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, -}; - -static const s8 sFigure8YOffsets[FIGURE_8_LENGTH] = { - 0, 0, 1, 0, 0, 1, 0, 0, - 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, - 0, 0, 1, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, -1, 0, 0, -1, 0, 0, - -1, 0, -1, -1, 0, -1, -1, 0, - -1, -1, -1, -1, -1, -1, -1, -2, -}; diff --git a/src/field_player_avatar.c b/src/field_player_avatar.c index ebf61bdaa3c5..9abb81a18e5a 100644 --- a/src/field_player_avatar.c +++ b/src/field_player_avatar.c @@ -665,11 +665,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(FLAG_SYS_DEXNAV_SEARCH) && (heldKeys & A_BUTTON)) + { + gPlayerAvatar.creeping = TRUE; + PlayerWalkSlow(direction); + } + else + { + // speed 2 is fast, same speed as running + PlayerWalkFast(direction); + } return; } @@ -684,6 +693,11 @@ static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys) gPlayerAvatar.flags |= PLAYER_AVATAR_FLAG_DASH; return; } + else if (FlagGet(FLAG_SYS_DEXNAV_SEARCH) && (heldKeys & A_BUTTON)) + { + gPlayerAvatar.creeping = TRUE; + PlayerWalkSlow(direction); + } else { if (ObjectMovingOnRockStairs(&gObjectEvents[gPlayerAvatar.objectEventId], direction)) diff --git a/src/new_game.c b/src/new_game.c index 4bce5a5b96a2..0837475971ff 100644 --- a/src/new_game.c +++ b/src/new_game.c @@ -44,6 +44,7 @@ #include "berry_powder.h" #include "mystery_gift.h" #include "union_room_chat.h" +#include "constants/map_groups.h" #include "constants/items.h" extern const u8 EventScript_ResetAllMapFlags[]; @@ -52,6 +53,7 @@ static void ClearFrontierRecord(void); static void WarpToTruck(void); static void ResetMiniGamesRecords(void); static void ResetItemFlags(void); +static void ResetDexNav(void); EWRAM_DATA bool8 gDifferentSaveFile = FALSE; EWRAM_DATA bool8 gEnableContestDebugging = FALSE; @@ -206,6 +208,7 @@ void NewGameInitData(void) ResetTrainerHillResults(); ResetContestLinkResults(); ResetItemFlags(); + ResetDexNav(); } static void ResetMiniGamesRecords(void) @@ -222,3 +225,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 6bfb49fce90d..443d10295be6 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -6,6 +6,7 @@ #include "bg.h" #include "cable_club.h" #include "clock.h" +#include "dexnav.h" #include "event_data.h" #include "event_object_movement.h" #include "event_scripts.h" @@ -838,6 +839,7 @@ void LoadMapFromCameraTransition(u8 mapGroup, u8 mapNum) LoadObjEventTemplatesFromHeader(); TrySetMapSaveWarpStatus(); ClearTempFieldEventData(); + ResetDexNavSearch(); ResetCyclingRoadChallengeData(); RestartWildEncounterImmunitySteps(); #if FREE_MATCH_CALL == FALSE @@ -902,6 +904,7 @@ static void LoadMapFromWarp(bool32 a1) CheckLeftFriendsSecretBase(); TrySetMapSaveWarpStatus(); ClearTempFieldEventData(); + ResetDexNavSearch(); ResetCyclingRoadChallengeData(); RestartWildEncounterImmunitySteps(); #if FREE_MATCH_CALL == FALSE diff --git a/src/party_menu.c b/src/party_menu.c index 5f71697621f8..416bbd7898e2 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -4259,7 +4259,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 57b08ad180b3..241372f64a32 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -12,6 +12,7 @@ #include "battle_tower.h" #include "battle_z_move.h" #include "data.h" +#include "dexnav.h" #include "event_data.h" #include "event_object_movement.h" #include "evolution_scene.h" @@ -1162,6 +1163,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(); while (GET_SHINY_VALUE(value, personality) >= SHINY_ODDS && totalRerolls > 0) { diff --git a/src/start_menu.c b/src/start_menu.c index 0c364bb07dea..004403e659e7 100644 --- a/src/start_menu.c +++ b/src/start_menu.c @@ -44,6 +44,8 @@ #include "trainer_card.h" #include "window.h" #include "union_room.h" +#include "dexnav.h" +#include "wild_encounter.h" #include "constants/battle_frontier.h" #include "constants/rgb.h" #include "constants/songs.h" @@ -65,6 +67,7 @@ enum MENU_ACTION_RETIRE_FRONTIER, MENU_ACTION_PYRAMID_BAG, MENU_ACTION_DEBUG, + MENU_ACTION_DEXNAV, }; // Save status @@ -106,6 +109,7 @@ static bool8 StartMenuLinkModePlayerNameCallback(void); static bool8 StartMenuBattlePyramidRetireCallback(void); static bool8 StartMenuBattlePyramidBagCallback(void); static bool8 StartMenuDebugCallback(void); +static bool8 StartMenuDexNavCallback(void); // Menu callbacks static bool8 SaveStartCallback(void); @@ -200,6 +204,7 @@ static const struct MenuAction sStartMenuItems[] = [MENU_ACTION_RETIRE_FRONTIER] = {gText_MenuRetire, {.u8_void = StartMenuBattlePyramidRetireCallback}}, [MENU_ACTION_PYRAMID_BAG] = {gText_MenuBag, {.u8_void = StartMenuBattlePyramidBagCallback}}, [MENU_ACTION_DEBUG] = {sText_MenuDebug, {.u8_void = StartMenuDebugCallback}}, + [MENU_ACTION_DEXNAV] = {gText_MenuDexNav, {.u8_void = StartMenuDexNavCallback}}, }; static const struct BgTemplate sBgTemplates_LinkBattleSave[] = @@ -324,22 +329,20 @@ static void AddStartMenuAction(u8 action) } static void BuildNormalStartMenu(void) -{ +{ if (FlagGet(FLAG_SYS_POKEDEX_GET) == TRUE) - { AddStartMenuAction(MENU_ACTION_POKEDEX); - } + + if (FLAG_SYS_DEXNAV_GET != 0 && FlagGet(FLAG_SYS_DEXNAV_GET)) + AddStartMenuAction(MENU_ACTION_DEXNAV); + if (FlagGet(FLAG_SYS_POKEMON_GET) == TRUE) - { AddStartMenuAction(MENU_ACTION_POKEMON); - } AddStartMenuAction(MENU_ACTION_BAG); if (FlagGet(FLAG_SYS_POKENAV_GET) == TRUE) - { AddStartMenuAction(MENU_ACTION_POKENAV); - } AddStartMenuAction(MENU_ACTION_PLAYER); AddStartMenuAction(MENU_ACTION_SAVE); @@ -638,7 +641,10 @@ static bool8 HandleStartMenuInput(void) if (GetNationalPokedexCount(FLAG_GET_SEEN) == 0) return FALSE; } - + if (sCurrentStartMenuActions[sStartMenuCursorPos] == MENU_ACTION_DEXNAV + && MapHasNoEncounterData()) + return FALSE; + gMenuCallback = sStartMenuItems[sCurrentStartMenuActions[sStartMenuCursorPos]].func.u8_void; if (gMenuCallback != StartMenuSaveCallback @@ -663,7 +669,7 @@ static bool8 HandleStartMenuInput(void) return FALSE; } -static bool8 StartMenuPokedexCallback(void) +bool8 StartMenuPokedexCallback(void) { if (!gPaletteFade.active) { @@ -1485,3 +1491,9 @@ void AppendToList(u8 *list, u8 *pos, u8 newEntry) list[*pos] = newEntry; (*pos)++; } + +static bool8 StartMenuDexNavCallback(void) +{ + CreateTask(Task_OpenDexNavFromStartMenu, 0); + return TRUE; +} diff --git a/src/strings.c b/src/strings.c index fb3c96eb9db8..37feea0da091 100644 --- a/src/strings.c +++ b/src/strings.c @@ -1023,6 +1023,7 @@ const u8 gText_MenuOption[] = _("OPTION"); const u8 gText_MenuExit[] = _("EXIT"); const u8 gText_MenuRetire[] = _("RETIRE"); const u8 gText_MenuRest[] = _("REST"); +const u8 gText_MenuDexNav[] = _("DEXNAV"); const u8 gText_SafariBallStock[] = _("SAFARI BALLS\nStock: {STR_VAR_1}"); const u8 gText_BattlePyramidFloor[] = _("Battle Pyramid\n{STR_VAR_1}"); const u8 gText_Floor1[] = _("Floor 1"); diff --git a/src/text_window.c b/src/text_window.c index efd087977eb8..a1bae0d123d5 100644 --- a/src/text_window.c +++ b/src/text_window.c @@ -82,6 +82,9 @@ static const struct TilesPal sWindowFrames[WINDOW_FRAMES_COUNT] = {sTextWindowFrame20_Gfx, sTextWindowFrame20_Pal} }; +static const u16 sTextWindowDexNavFrame[] = INCBIN_U16("graphics/text_window/dexnav_pal.gbapal"); +static const struct TilesPal sDexNavWindowFrame = {gTextWindowFrame1_Gfx, sTextWindowDexNavFrame}; + // code const struct TilesPal *GetWindowFrameTilesPal(u8 id) { @@ -202,3 +205,9 @@ void LoadUserWindowBorderGfxOnBg(u8 bg, u16 destOffset, u8 palOffset) LoadBgTiles(bg, sWindowFrames[gSaveBlock2Ptr->optionsWindowFrameType].tiles, 0x120, destOffset); LoadPalette(GetWindowFrameTilesPal(gSaveBlock2Ptr->optionsWindowFrameType)->pal, palOffset, PLTT_SIZE_4BPP); } + +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 033eec56daef..bd00e58fcd20 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -186,7 +186,7 @@ static void FeebasSeedRng(u16 seed) } // LAND_WILD_COUNT -static u8 ChooseWildMonIndex_Land(void) +u8 ChooseWildMonIndex_Land(void) { u8 wildMonIndex = 0; bool8 swap = FALSE; @@ -227,7 +227,7 @@ static u8 ChooseWildMonIndex_Land(void) } // ROCK_WILD_COUNT / WATER_WILD_COUNT -static u8 ChooseWildMonIndex_WaterRock(void) +u8 ChooseWildMonIndex_WaterRock(void) { u8 wildMonIndex = 0; bool8 swap = FALSE; @@ -354,7 +354,7 @@ static u8 ChooseWildMonLevel(const struct WildPokemon *wildPokemon, u8 wildMonIn } } -static u16 GetCurrentMapWildMonHeaderId(void) +u16 GetCurrentMapWildMonHeaderId(void) { u16 i; @@ -417,7 +417,7 @@ u8 PickWildMonNature(void) return Random() % NUM_NATURES; } -static void CreateWildMon(u16 species, u8 level) +void CreateWildMon(u16 species, u8 level) { bool32 checkCuteCharm = TRUE; @@ -1134,3 +1134,24 @@ bool8 StandardWildEncounter_Debug(void) DoStandardWildBattle_Debug(); return TRUE; } + +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); +}