Update fishing odds to match official games (#7574)
This commit is contained in:
parent
730352683c
commit
a595696086
13
include/config/fishing.h
Normal file
13
include/config/fishing.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef GUARD_CONFIG_FISHING_H
|
||||||
|
#define GUARD_CONFIG_FISHING_H
|
||||||
|
|
||||||
|
#define I_FISHING_BITE_ODDS GEN_LATEST // In Gen 1 and Gen 2, the Old Rod has a 100% chance for a bite, Good Rod has a 66% chance for a bite, and Super Rod has a 50% chance for a bite. In Gen 3, all rods have a base 50% chance for a bite. In Gen 4 onwards, the Old Rod has a base 25% chance for a bite, Good Rod has a 50% chance for a bite, and Super Rod has a 75% chance for a bite.
|
||||||
|
#define I_FISHING_MINIGAME GEN_3 // Each generation uses a variation of reeling in Pokémon once they have been hooked. NOTE: Only the Gen 1/2 and Gen 3 minigames are implemented right now!
|
||||||
|
#define I_FISHING_ENVIRONMENT GEN_LATEST // In Gen 3, the battle environment when fighting a hooked Pokémon is based on the tile the player is standing on. In Gen 4 onwards, the environment is based on tile that is being fished in, resulting in it usually being a water environment.
|
||||||
|
#define I_FISHING_STICKY_BOOST GEN_LATEST // In Gen 3, a Pokemon with Suction Cups or Sticky Hold in the first slot of the party causes the chance for a bite to increase by about 35%. In Gen 4 onwards, it doubles the base bite chance.
|
||||||
|
#define I_FISHING_FOLLOWER_BOOST FALSE // In HGSS, fishing bite odds are increased depending on the friendship of the current following Pokémon.
|
||||||
|
#define I_FISHING_CHAIN FALSE // Introduced in XY, hooking the same Pokémon repeatedly will increase the odds of that mon being shiny. NOTE: This implementation is an approximation of the actual feature, as XY have not been throughoutly documented or datamined.
|
||||||
|
#define I_FISHING_PROXIMITY FALSE // In XY, bite chance is boosted by the number of adjacent non-surfable tiles next to your fishing line
|
||||||
|
#define I_FISHING_TIME_OF_DAY_BOOST FALSE // In XY, bite chance is boosted during morning and evening
|
||||||
|
|
||||||
|
#endif // GUARD_CONFIG_FISHING_H
|
||||||
@ -38,13 +38,4 @@
|
|||||||
// Vs. Seeker
|
// Vs. Seeker
|
||||||
#define I_VS_SEEKER_CHARGING 0 // If this flag is assigned, the Vs Seeker functionality will be enabled. When the player has the Vs. Seeker, Match Call rematch functions will stop working.
|
#define I_VS_SEEKER_CHARGING 0 // If this flag is assigned, the Vs Seeker functionality will be enabled. When the player has the Vs. Seeker, Match Call rematch functions will stop working.
|
||||||
|
|
||||||
// Fishing
|
|
||||||
#define I_FISHING_BITE_ODDS GEN_LATEST // In Gen 1 and Gen 2, the Old Rod has a 100% chance for a bite, Good Rod has a 66% chance for a bite, and Super Rod has a 50% chance for a bite. In Gen 3, all rods have a base 50% chance for a bite. In Gen 4 onwards, the Old Rod has a base 25% chance for a bite, Good Rod has a 50% chance for a bite, and Super Rod has a 75% chance for a bite.
|
|
||||||
#define I_FISHING_MINIGAME GEN_3 // Each generation uses a variation of reeling in Pokémon once they have been hooked. NOTE: Only the Gen 1/2 and Gen 3 minigames are implemented right now!
|
|
||||||
#define I_FISHING_ENVIRONMENT GEN_LATEST // In Gen 3, the battle environment when fighting a hooked Pokémon is based on the tile the player is standing on. In Gen 4 onwards, the environment is based on tile that is being fished in, resulting in it usually being a water environment.
|
|
||||||
#define I_FISHING_STICKY_BOOST GEN_LATEST // In Gen 3, a Pokemon with Suction Cups or Sticky Hold in the first slot of the party causes the chance for a bite to increase by about 35%. In Gen 4 onwards, it doubles the base bite chance.
|
|
||||||
#define I_FISHING_FOLLOWER_BOOST FALSE // In HGSS, fishing bite odds are increased depending on the friendship of the current following Pokémon.
|
|
||||||
#define I_FISHING_CHAIN FALSE // Introduced in XY, hooking the same Pokémon repeatedly will increase the odds of that mon being shiny. NOTE: This implementation is an approximation of the actual feature, as XY have not been throughoutly documented or datamined.
|
|
||||||
#define I_FISHING_PROXIMITY FALSE // Introduced in XY, fishing away from other people in enclosed areas will increase the chances of a Pokémon being hooked. NOTE: This implementation is an approximation of the actual feature, as XY have not been throughoutly documented or datamined.
|
|
||||||
|
|
||||||
#endif // GUARD_CONFIG_ITEM_H
|
#endif // GUARD_CONFIG_ITEM_H
|
||||||
|
|||||||
@ -9,7 +9,4 @@
|
|||||||
|
|
||||||
#define NUM_ALTERING_CAVE_TABLES 9
|
#define NUM_ALTERING_CAVE_TABLES 9
|
||||||
|
|
||||||
#define FISHING_CHAIN_LENGTH_MAX 200
|
|
||||||
#define FISHING_CHAIN_SHINY_STREAK_MAX 20
|
|
||||||
|
|
||||||
#endif // GUARD_CONSTANTS_WILD_ENCOUNTER_H
|
#endif // GUARD_CONSTANTS_WILD_ENCOUNTER_H
|
||||||
|
|||||||
@ -64,7 +64,7 @@ bool32 IsPlayerSpinEntranceActive(void);
|
|||||||
bool32 IsPlayerSpinExitActive(void);
|
bool32 IsPlayerSpinExitActive(void);
|
||||||
void SetPlayerInvisibility(bool8 invisible);
|
void SetPlayerInvisibility(bool8 invisible);
|
||||||
u8 player_get_pos_including_state_based_drift(s16 *x, s16 *y);
|
u8 player_get_pos_including_state_based_drift(s16 *x, s16 *y);
|
||||||
void StartFishing(u8 rod);
|
void SetPlayerAvatarFishing(u8 direction);
|
||||||
bool8 ObjectMovingOnRockStairs(struct ObjectEvent *objectEvent, u8 direction);
|
bool8 ObjectMovingOnRockStairs(struct ObjectEvent *objectEvent, u8 direction);
|
||||||
//sideways stairs
|
//sideways stairs
|
||||||
u8 GetRightSideStairsDirection(u8 direction);
|
u8 GetRightSideStairsDirection(u8 direction);
|
||||||
|
|||||||
9
include/fishing.h
Normal file
9
include/fishing.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef GUARD_FISHING_H
|
||||||
|
#define GUARD_FISHING_H
|
||||||
|
|
||||||
|
void StartFishing(u8 rod);
|
||||||
|
void UpdateChainFishingStreak();
|
||||||
|
u32 CalculateChainFishingShinyRolls(void);
|
||||||
|
bool32 ShouldUseFishingEnvironmentInBattle();
|
||||||
|
|
||||||
|
#endif // GUARD_FISHING_H
|
||||||
@ -219,6 +219,8 @@ enum RandomTag
|
|||||||
RNG_BALLTHROW_SHAKE,
|
RNG_BALLTHROW_SHAKE,
|
||||||
RNG_PRESENT,
|
RNG_PRESENT,
|
||||||
RNG_MAGNITUDE,
|
RNG_MAGNITUDE,
|
||||||
|
RNG_FISHING_BITE,
|
||||||
|
RNG_FISHING_GEN3_STICKY,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define RandomWeighted(tag, ...) \
|
#define RandomWeighted(tag, ...) \
|
||||||
|
|||||||
@ -52,7 +52,7 @@
|
|||||||
#include "constants/trainers.h"
|
#include "constants/trainers.h"
|
||||||
#include "constants/trainer_hill.h"
|
#include "constants/trainer_hill.h"
|
||||||
#include "constants/weather.h"
|
#include "constants/weather.h"
|
||||||
#include "wild_encounter.h"
|
#include "fishing.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
TRANSITION_TYPE_NORMAL,
|
TRANSITION_TYPE_NORMAL,
|
||||||
@ -627,7 +627,7 @@ enum BattleEnvironments BattleSetup_GetEnvironmentId(void)
|
|||||||
u16 tileBehavior;
|
u16 tileBehavior;
|
||||||
s16 x, y;
|
s16 x, y;
|
||||||
|
|
||||||
if (I_FISHING_ENVIRONMENT >= GEN_4 && gIsFishingEncounter)
|
if (ShouldUseFishingEnvironmentInBattle())
|
||||||
GetXYCoordsOneStepInFrontOfPlayer(&x, &y);
|
GetXYCoordsOneStepInFrontOfPlayer(&x, &y);
|
||||||
else
|
else
|
||||||
PlayerGetDestCoords(&x, &y);
|
PlayerGetDestCoords(&x, &y);
|
||||||
|
|||||||
@ -146,40 +146,6 @@ static void CreateStopSurfingTask(u8);
|
|||||||
static void Task_StopSurfingInit(u8);
|
static void Task_StopSurfingInit(u8);
|
||||||
static void Task_WaitStopSurfing(u8);
|
static void Task_WaitStopSurfing(u8);
|
||||||
|
|
||||||
static void Task_Fishing(u8);
|
|
||||||
static bool32 Fishing_Init(struct Task *);
|
|
||||||
static bool32 Fishing_GetRodOut(struct Task *);
|
|
||||||
static bool32 Fishing_WaitBeforeDots(struct Task *);
|
|
||||||
static bool32 Fishing_InitDots(struct Task *);
|
|
||||||
static bool32 Fishing_ShowDots(struct Task *);
|
|
||||||
static bool32 Fishing_CheckForBite(struct Task *);
|
|
||||||
static bool32 Fishing_GotBite(struct Task *);
|
|
||||||
static bool32 Fishing_ChangeMinigame(struct Task *);
|
|
||||||
static bool32 Fishing_WaitForA(struct Task *);
|
|
||||||
static bool32 Fishing_APressNoMinigame(struct Task *);
|
|
||||||
static bool32 Fishing_CheckMoreDots(struct Task *);
|
|
||||||
static bool32 Fishing_MonOnHook(struct Task *);
|
|
||||||
static bool32 Fishing_StartEncounter(struct Task *);
|
|
||||||
static bool32 Fishing_NotEvenNibble(struct Task *);
|
|
||||||
static bool32 Fishing_GotAway(struct Task *);
|
|
||||||
static bool32 Fishing_NoMon(struct Task *);
|
|
||||||
static bool32 Fishing_PutRodAway(struct Task *);
|
|
||||||
static bool32 Fishing_EndNoMon(struct Task *);
|
|
||||||
static void AlignFishingAnimationFrames(void);
|
|
||||||
static bool32 DoesFishingMinigameAllowCancel(void);
|
|
||||||
static bool32 Fishing_DoesFirstMonInPartyHaveSuctionCupsOrStickyHold(void);
|
|
||||||
static bool32 Fishing_RollForBite(u32, bool32);
|
|
||||||
static u32 CalculateFishingBiteOdds(u32, bool32);
|
|
||||||
static u32 CalculateFishingFollowerBoost(void);
|
|
||||||
static u32 CalculateFishingProximityBoost(u32 odds);
|
|
||||||
static void GetCoordinatesAroundBobber(s16[], s16[][AXIS_COUNT], u32);
|
|
||||||
static u32 CountQualifyingTiles(s16[][AXIS_COUNT], s16 player[], u8 facingDirection, struct ObjectEvent *objectEvent, bool32 isTileLand[]);
|
|
||||||
static bool32 CheckTileQualification(s16 tile[], s16 player[], u32 facingDirection, struct ObjectEvent* objectEvent, bool32 isTileLand[], u32 direction);
|
|
||||||
static u32 CountLandTiles(bool32 isTileLand[]);
|
|
||||||
static bool32 IsPlayerHere(s16, s16, s16, s16);
|
|
||||||
static bool32 IsMetatileBlocking(s16, s16, u32);
|
|
||||||
static bool32 IsMetatileLand(s16, s16, u32);
|
|
||||||
|
|
||||||
static u8 TrySpinPlayerForWarp(struct ObjectEvent *, s16 *);
|
static u8 TrySpinPlayerForWarp(struct ObjectEvent *, s16 *);
|
||||||
|
|
||||||
static bool8 (*const sForcedMovementTestFuncs[NUM_FORCED_MOVEMENTS])(u8) =
|
static bool8 (*const sForcedMovementTestFuncs[NUM_FORCED_MOVEMENTS])(u8) =
|
||||||
@ -1663,7 +1629,7 @@ void SetPlayerAvatarFieldMove(void)
|
|||||||
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], ANIM_FIELD_MOVE);
|
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], ANIM_FIELD_MOVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetPlayerAvatarFishing(u8 direction)
|
void SetPlayerAvatarFishing(u8 direction)
|
||||||
{
|
{
|
||||||
ObjectEventSetGraphicsId(&gObjectEvents[gPlayerAvatar.objectEventId], GetPlayerAvatarGraphicsIdByStateId(PLAYER_AVATAR_STATE_FISHING));
|
ObjectEventSetGraphicsId(&gObjectEvents[gPlayerAvatar.objectEventId], GetPlayerAvatarGraphicsIdByStateId(PLAYER_AVATAR_STATE_FISHING));
|
||||||
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], GetFishingDirectionAnimNum(direction));
|
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], GetFishingDirectionAnimNum(direction));
|
||||||
@ -1933,644 +1899,6 @@ static void Task_WaitStopSurfing(u8 taskId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define tStep data[0]
|
|
||||||
#define tFrameCounter data[1]
|
|
||||||
#define tNumDots data[2]
|
|
||||||
#define tDotsRequired data[3]
|
|
||||||
#define tRoundsPlayed data[12]
|
|
||||||
#define tMinRoundsRequired data[13]
|
|
||||||
#define tPlayerGfxId data[14]
|
|
||||||
#define tFishingRod data[15]
|
|
||||||
|
|
||||||
#define FISHING_PROXIMITY_BOOST 4
|
|
||||||
#define FISHING_STICKY_BOOST 36
|
|
||||||
|
|
||||||
#if I_FISHING_BITE_ODDS >= GEN_4
|
|
||||||
#define FISHING_OLD_ROD_ODDS 75
|
|
||||||
#define FISHING_GOOD_ROD_ODDS 50
|
|
||||||
#define FISHING_SUPER_ROD_ODDS 25
|
|
||||||
#elif I_FISHING_BITE_ODDS >= GEN_3
|
|
||||||
#define FISHING_OLD_ROD_ODDS 50
|
|
||||||
#define FISHING_GOOD_ROD_ODDS 50
|
|
||||||
#define FISHING_SUPER_ROD_ODDS 50
|
|
||||||
#else
|
|
||||||
#define FISHING_OLD_ROD_ODDS 0
|
|
||||||
#define FISHING_GOOD_ROD_ODDS 33
|
|
||||||
#define FISHING_SUPER_ROD_ODDS 50
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
FISHING_INIT,
|
|
||||||
FISHING_GET_ROD_OUT,
|
|
||||||
FISHING_WAIT_BEFORE_DOTS,
|
|
||||||
FISHING_INIT_DOTS,
|
|
||||||
FISHING_SHOW_DOTS,
|
|
||||||
FISHING_CHECK_FOR_BITE,
|
|
||||||
FISHING_GOT_BITE,
|
|
||||||
FISHING_CHANGE_MINIGAME,
|
|
||||||
FISHING_WAIT_FOR_A,
|
|
||||||
FISHING_A_PRESS_NO_MINIGAME,
|
|
||||||
FISHING_CHECK_MORE_DOTS,
|
|
||||||
FISHING_MON_ON_HOOK,
|
|
||||||
FISHING_START_ENCOUNTER,
|
|
||||||
FISHING_NOT_EVEN_NIBBLE,
|
|
||||||
FISHING_GOT_AWAY,
|
|
||||||
FISHING_NO_MON,
|
|
||||||
FISHING_PUT_ROD_AWAY,
|
|
||||||
FISHING_END_NO_MON,
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool32 (*const sFishingStateFuncs[])(struct Task *) =
|
|
||||||
{
|
|
||||||
[FISHING_INIT] = Fishing_Init,
|
|
||||||
[FISHING_GET_ROD_OUT] = Fishing_GetRodOut,
|
|
||||||
[FISHING_WAIT_BEFORE_DOTS] = Fishing_WaitBeforeDots,
|
|
||||||
[FISHING_INIT_DOTS] = Fishing_InitDots,
|
|
||||||
[FISHING_SHOW_DOTS] = Fishing_ShowDots,
|
|
||||||
[FISHING_CHECK_FOR_BITE] = Fishing_CheckForBite,
|
|
||||||
[FISHING_GOT_BITE] = Fishing_GotBite,
|
|
||||||
[FISHING_CHANGE_MINIGAME] = Fishing_ChangeMinigame,
|
|
||||||
[FISHING_WAIT_FOR_A] = Fishing_WaitForA,
|
|
||||||
[FISHING_A_PRESS_NO_MINIGAME] = Fishing_APressNoMinigame,
|
|
||||||
[FISHING_CHECK_MORE_DOTS] = Fishing_CheckMoreDots,
|
|
||||||
[FISHING_MON_ON_HOOK] = Fishing_MonOnHook,
|
|
||||||
[FISHING_START_ENCOUNTER] = Fishing_StartEncounter,
|
|
||||||
[FISHING_NOT_EVEN_NIBBLE] = Fishing_NotEvenNibble,
|
|
||||||
[FISHING_GOT_AWAY] = Fishing_GotAway,
|
|
||||||
[FISHING_NO_MON] = Fishing_NoMon,
|
|
||||||
[FISHING_PUT_ROD_AWAY] = Fishing_PutRodAway,
|
|
||||||
[FISHING_END_NO_MON] = Fishing_EndNoMon,
|
|
||||||
};
|
|
||||||
|
|
||||||
void StartFishing(u8 rod)
|
|
||||||
{
|
|
||||||
u8 taskId = CreateTask(Task_Fishing, 0xFF);
|
|
||||||
|
|
||||||
gTasks[taskId].tFishingRod = rod;
|
|
||||||
Task_Fishing(taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Task_Fishing(u8 taskId)
|
|
||||||
{
|
|
||||||
while (sFishingStateFuncs[gTasks[taskId].tStep](&gTasks[taskId]))
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_Init(struct Task *task)
|
|
||||||
{
|
|
||||||
LockPlayerFieldControls();
|
|
||||||
gPlayerAvatar.preventStep = TRUE;
|
|
||||||
task->tStep = FISHING_GET_ROD_OUT;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_GetRodOut(struct Task *task)
|
|
||||||
{
|
|
||||||
struct ObjectEvent *playerObjEvent;
|
|
||||||
const s16 minRounds1[] = {
|
|
||||||
[OLD_ROD] = 1,
|
|
||||||
[GOOD_ROD] = 1,
|
|
||||||
[SUPER_ROD] = 1
|
|
||||||
};
|
|
||||||
const s16 minRounds2[] = {
|
|
||||||
[OLD_ROD] = 1,
|
|
||||||
[GOOD_ROD] = 3,
|
|
||||||
[SUPER_ROD] = 6
|
|
||||||
};
|
|
||||||
|
|
||||||
task->tRoundsPlayed = 0;
|
|
||||||
task->tMinRoundsRequired = minRounds1[task->tFishingRod] + (Random() % minRounds2[task->tFishingRod]);
|
|
||||||
task->tPlayerGfxId = gObjectEvents[gPlayerAvatar.objectEventId].graphicsId;
|
|
||||||
playerObjEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
|
|
||||||
ObjectEventClearHeldMovementIfActive(playerObjEvent);
|
|
||||||
playerObjEvent->enableAnim = TRUE;
|
|
||||||
SetPlayerAvatarFishing(playerObjEvent->facingDirection);
|
|
||||||
task->tStep = FISHING_WAIT_BEFORE_DOTS;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_WaitBeforeDots(struct Task *task)
|
|
||||||
{
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
|
|
||||||
// Wait one second
|
|
||||||
task->tFrameCounter++;
|
|
||||||
if (task->tFrameCounter >= 60)
|
|
||||||
task->tStep = FISHING_INIT_DOTS;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_InitDots(struct Task *task)
|
|
||||||
{
|
|
||||||
u32 randVal;
|
|
||||||
|
|
||||||
LoadMessageBoxAndFrameGfx(0, TRUE);
|
|
||||||
task->tStep = FISHING_SHOW_DOTS;
|
|
||||||
task->tFrameCounter = 0;
|
|
||||||
task->tNumDots = 0;
|
|
||||||
randVal = Random();
|
|
||||||
randVal %= 10;
|
|
||||||
task->tDotsRequired = randVal + 1;
|
|
||||||
if (task->tRoundsPlayed == 0)
|
|
||||||
task->tDotsRequired = randVal + 4;
|
|
||||||
if (task->tDotsRequired >= 10)
|
|
||||||
task->tDotsRequired = 10;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_ShowDots(struct Task *task)
|
|
||||||
{
|
|
||||||
const u8 dot[] = _("·");
|
|
||||||
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
task->tFrameCounter++;
|
|
||||||
if (JOY_NEW(A_BUTTON))
|
|
||||||
{
|
|
||||||
if (!DoesFishingMinigameAllowCancel())
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
task->tStep = FISHING_NOT_EVEN_NIBBLE;
|
|
||||||
if (task->tRoundsPlayed != 0)
|
|
||||||
task->tStep = FISHING_GOT_AWAY;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (task->tFrameCounter >= 20)
|
|
||||||
{
|
|
||||||
task->tFrameCounter = 0;
|
|
||||||
if (task->tNumDots >= task->tDotsRequired)
|
|
||||||
{
|
|
||||||
task->tStep = FISHING_CHECK_FOR_BITE;
|
|
||||||
if (task->tRoundsPlayed != 0)
|
|
||||||
task->tStep = FISHING_GOT_BITE;
|
|
||||||
task->tRoundsPlayed++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddTextPrinterParameterized(0, FONT_NORMAL, dot, task->tNumDots * 8, 1, 0, NULL);
|
|
||||||
task->tNumDots++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_CheckForBite(struct Task *task)
|
|
||||||
{
|
|
||||||
bool32 bite, firstMonHasSuctionOrSticky;
|
|
||||||
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
task->tStep = FISHING_GOT_BITE;
|
|
||||||
bite = FALSE;
|
|
||||||
|
|
||||||
if (!DoesCurrentMapHaveFishingMons())
|
|
||||||
{
|
|
||||||
task->tStep = FISHING_NOT_EVEN_NIBBLE;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstMonHasSuctionOrSticky = Fishing_DoesFirstMonInPartyHaveSuctionCupsOrStickyHold();
|
|
||||||
|
|
||||||
if(firstMonHasSuctionOrSticky)
|
|
||||||
bite = Fishing_RollForBite(task->tFishingRod, firstMonHasSuctionOrSticky);
|
|
||||||
|
|
||||||
if (!bite)
|
|
||||||
bite = Fishing_RollForBite(task->tFishingRod, FALSE);
|
|
||||||
|
|
||||||
if (!bite)
|
|
||||||
task->tStep = FISHING_NOT_EVEN_NIBBLE;
|
|
||||||
|
|
||||||
if (bite)
|
|
||||||
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], GetFishingBiteDirectionAnimNum(GetPlayerFacingDirection()));
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_GotBite(struct Task *task)
|
|
||||||
{
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
AddTextPrinterParameterized(0, FONT_NORMAL, gText_OhABite, 0, 17, 0, NULL);
|
|
||||||
task->tStep = FISHING_CHANGE_MINIGAME;
|
|
||||||
task->tFrameCounter = 0;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_ChangeMinigame(struct Task *task)
|
|
||||||
{
|
|
||||||
switch (I_FISHING_MINIGAME)
|
|
||||||
{
|
|
||||||
case GEN_1:
|
|
||||||
case GEN_2:
|
|
||||||
task->tStep = FISHING_A_PRESS_NO_MINIGAME;
|
|
||||||
break;
|
|
||||||
case GEN_3:
|
|
||||||
default:
|
|
||||||
task->tStep = FISHING_WAIT_FOR_A;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have a bite. Now, wait for the player to press A, or the timer to expire.
|
|
||||||
static bool32 Fishing_WaitForA(struct Task *task)
|
|
||||||
{
|
|
||||||
const s16 reelTimeouts[3] = {
|
|
||||||
[OLD_ROD] = 36,
|
|
||||||
[GOOD_ROD] = 33,
|
|
||||||
[SUPER_ROD] = 30
|
|
||||||
};
|
|
||||||
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
task->tFrameCounter++;
|
|
||||||
if (task->tFrameCounter >= reelTimeouts[task->tFishingRod])
|
|
||||||
task->tStep = FISHING_GOT_AWAY;
|
|
||||||
else if (JOY_NEW(A_BUTTON))
|
|
||||||
task->tStep = FISHING_CHECK_MORE_DOTS;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_APressNoMinigame(struct Task *task)
|
|
||||||
{
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
if (JOY_NEW(A_BUTTON))
|
|
||||||
task->tStep = FISHING_MON_ON_HOOK;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if we're going to play the dot game again
|
|
||||||
static bool32 Fishing_CheckMoreDots(struct Task *task)
|
|
||||||
{
|
|
||||||
const s16 moreDotsChance[][2] =
|
|
||||||
{
|
|
||||||
[OLD_ROD] = {0, 0},
|
|
||||||
[GOOD_ROD] = {40, 10},
|
|
||||||
[SUPER_ROD] = {70, 30}
|
|
||||||
};
|
|
||||||
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
task->tStep = FISHING_MON_ON_HOOK;
|
|
||||||
if (task->tRoundsPlayed < task->tMinRoundsRequired)
|
|
||||||
{
|
|
||||||
task->tStep = FISHING_INIT_DOTS;
|
|
||||||
}
|
|
||||||
else if (task->tRoundsPlayed < 2)
|
|
||||||
{
|
|
||||||
// probability of having to play another round
|
|
||||||
s16 probability = Random() % 100;
|
|
||||||
|
|
||||||
if (moreDotsChance[task->tFishingRod][task->tRoundsPlayed] > probability)
|
|
||||||
task->tStep = FISHING_INIT_DOTS;
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_MonOnHook(struct Task *task)
|
|
||||||
{
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
FillWindowPixelBuffer(0, PIXEL_FILL(1));
|
|
||||||
AddTextPrinterParameterized2(0, FONT_NORMAL, gText_PokemonOnHook, 1, 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY);
|
|
||||||
task->tStep = FISHING_START_ENCOUNTER;
|
|
||||||
task->tFrameCounter = 0;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_StartEncounter(struct Task *task)
|
|
||||||
{
|
|
||||||
if (task->tFrameCounter == 0)
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
|
|
||||||
RunTextPrinters();
|
|
||||||
|
|
||||||
if (task->tFrameCounter == 0)
|
|
||||||
{
|
|
||||||
if (!IsTextPrinterActive(0))
|
|
||||||
{
|
|
||||||
struct ObjectEvent *playerObjEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
|
|
||||||
|
|
||||||
ObjectEventSetGraphicsId(playerObjEvent, task->tPlayerGfxId);
|
|
||||||
ObjectEventTurn(playerObjEvent, playerObjEvent->movementDirection);
|
|
||||||
if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING)
|
|
||||||
SetSurfBlob_PlayerOffset(gObjectEvents[gPlayerAvatar.objectEventId].fieldEffectSpriteId, FALSE, 0);
|
|
||||||
gSprites[gPlayerAvatar.spriteId].x2 = 0;
|
|
||||||
gSprites[gPlayerAvatar.spriteId].y2 = 0;
|
|
||||||
ClearDialogWindowAndFrame(0, TRUE);
|
|
||||||
task->tFrameCounter++;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task->tFrameCounter != 0)
|
|
||||||
{
|
|
||||||
gPlayerAvatar.preventStep = FALSE;
|
|
||||||
UnlockPlayerFieldControls();
|
|
||||||
FishingWildEncounter(task->tFishingRod);
|
|
||||||
RecordFishingAttemptForTV(TRUE);
|
|
||||||
DestroyTask(FindTaskIdByFunc(Task_Fishing));
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_NotEvenNibble(struct Task *task)
|
|
||||||
{
|
|
||||||
gChainFishingDexNavStreak = 0;
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], GetFishingNoCatchDirectionAnimNum(GetPlayerFacingDirection()));
|
|
||||||
FillWindowPixelBuffer(0, PIXEL_FILL(1));
|
|
||||||
AddTextPrinterParameterized2(0, FONT_NORMAL, gText_NotEvenANibble, 1, 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY);
|
|
||||||
task->tStep = FISHING_NO_MON;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_GotAway(struct Task *task)
|
|
||||||
{
|
|
||||||
gChainFishingDexNavStreak = 0;
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], GetFishingNoCatchDirectionAnimNum(GetPlayerFacingDirection()));
|
|
||||||
FillWindowPixelBuffer(0, PIXEL_FILL(1));
|
|
||||||
AddTextPrinterParameterized2(0, FONT_NORMAL, gText_ItGotAway, 1, 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY);
|
|
||||||
task->tStep = FISHING_NO_MON;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_NoMon(struct Task *task)
|
|
||||||
{
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
task->tStep = FISHING_PUT_ROD_AWAY;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_PutRodAway(struct Task *task)
|
|
||||||
{
|
|
||||||
AlignFishingAnimationFrames();
|
|
||||||
if (gSprites[gPlayerAvatar.spriteId].animEnded)
|
|
||||||
{
|
|
||||||
struct ObjectEvent *playerObjEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
|
|
||||||
|
|
||||||
ObjectEventSetGraphicsId(playerObjEvent, task->tPlayerGfxId);
|
|
||||||
ObjectEventTurn(playerObjEvent, playerObjEvent->movementDirection);
|
|
||||||
if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING)
|
|
||||||
SetSurfBlob_PlayerOffset(gObjectEvents[gPlayerAvatar.objectEventId].fieldEffectSpriteId, FALSE, 0);
|
|
||||||
gSprites[gPlayerAvatar.spriteId].x2 = 0;
|
|
||||||
gSprites[gPlayerAvatar.spriteId].y2 = 0;
|
|
||||||
task->tStep = FISHING_END_NO_MON;
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_EndNoMon(struct Task *task)
|
|
||||||
{
|
|
||||||
RunTextPrinters();
|
|
||||||
if (!IsTextPrinterActive(0))
|
|
||||||
{
|
|
||||||
gPlayerAvatar.preventStep = FALSE;
|
|
||||||
UnlockPlayerFieldControls();
|
|
||||||
UnfreezeObjectEvents();
|
|
||||||
ClearDialogWindowAndFrame(0, TRUE);
|
|
||||||
RecordFishingAttemptForTV(FALSE);
|
|
||||||
DestroyTask(FindTaskIdByFunc(Task_Fishing));
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 DoesFishingMinigameAllowCancel(void)
|
|
||||||
{
|
|
||||||
switch(I_FISHING_MINIGAME)
|
|
||||||
{
|
|
||||||
case GEN_1:
|
|
||||||
case GEN_2:
|
|
||||||
return FALSE;
|
|
||||||
case GEN_3:
|
|
||||||
default:
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_DoesFirstMonInPartyHaveSuctionCupsOrStickyHold(void)
|
|
||||||
{
|
|
||||||
enum Ability ability;
|
|
||||||
|
|
||||||
if (GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
ability = GetMonAbility(&gPlayerParty[0]);
|
|
||||||
|
|
||||||
return (ability == ABILITY_SUCTION_CUPS || ability == ABILITY_STICKY_HOLD);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 Fishing_RollForBite(u32 rod, bool32 isStickyHold)
|
|
||||||
{
|
|
||||||
return ((Random() % 100) > CalculateFishingBiteOdds(rod, isStickyHold));
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 CalculateFishingBiteOdds(u32 rod, bool32 isStickyHold)
|
|
||||||
{
|
|
||||||
u32 odds;
|
|
||||||
|
|
||||||
if (rod == OLD_ROD)
|
|
||||||
odds = FISHING_OLD_ROD_ODDS;
|
|
||||||
if (rod == GOOD_ROD)
|
|
||||||
odds = FISHING_GOOD_ROD_ODDS;
|
|
||||||
if (rod == SUPER_ROD)
|
|
||||||
odds = FISHING_SUPER_ROD_ODDS;
|
|
||||||
|
|
||||||
odds -= CalculateFishingFollowerBoost();
|
|
||||||
|
|
||||||
if (isStickyHold)
|
|
||||||
{
|
|
||||||
if (I_FISHING_STICKY_BOOST >= GEN_4)
|
|
||||||
odds -= (100 - odds);
|
|
||||||
else
|
|
||||||
odds -= FISHING_STICKY_BOOST;
|
|
||||||
}
|
|
||||||
|
|
||||||
odds -= CalculateFishingProximityBoost(odds);
|
|
||||||
|
|
||||||
return odds;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 CalculateFishingFollowerBoost()
|
|
||||||
{
|
|
||||||
u32 friendship;
|
|
||||||
struct Pokemon *mon = GetFirstLiveMon();
|
|
||||||
|
|
||||||
if (!I_FISHING_FOLLOWER_BOOST || !mon)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
friendship = GetMonData(mon, MON_DATA_FRIENDSHIP);
|
|
||||||
if (friendship >= 250)
|
|
||||||
return 50;
|
|
||||||
else if (friendship >= 200)
|
|
||||||
return 40;
|
|
||||||
else if (friendship >= 150)
|
|
||||||
return 30;
|
|
||||||
else if (friendship >= 100)
|
|
||||||
return 20;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 CalculateFishingProximityBoost(u32 odds)
|
|
||||||
{
|
|
||||||
s16 player[AXIS_COUNT], bobber[AXIS_COUNT];
|
|
||||||
s16 surroundingTile[CARDINAL_DIRECTION_COUNT][AXIS_COUNT] = {{0, 0}};
|
|
||||||
bool32 isTileLand[CARDINAL_DIRECTION_COUNT] = {FALSE};
|
|
||||||
u32 facingDirection, numQualifyingTile = 0;
|
|
||||||
struct ObjectEvent *objectEvent;
|
|
||||||
|
|
||||||
if (!I_FISHING_PROXIMITY)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
|
|
||||||
|
|
||||||
player[AXIS_X] = objectEvent->currentCoords.x;
|
|
||||||
player[AXIS_Y] = objectEvent->currentCoords.y;
|
|
||||||
bobber[AXIS_X] = objectEvent->currentCoords.x;
|
|
||||||
bobber[AXIS_Y] = objectEvent->currentCoords.y;
|
|
||||||
|
|
||||||
facingDirection = GetPlayerFacingDirection();
|
|
||||||
MoveCoords(facingDirection, &bobber[AXIS_X], &bobber[AXIS_Y]);
|
|
||||||
|
|
||||||
GetCoordinatesAroundBobber(bobber, surroundingTile, facingDirection);
|
|
||||||
numQualifyingTile = CountQualifyingTiles(surroundingTile, player, facingDirection, objectEvent, isTileLand);
|
|
||||||
|
|
||||||
numQualifyingTile += CountLandTiles(isTileLand);
|
|
||||||
|
|
||||||
return (numQualifyingTile == 3) ? odds : (numQualifyingTile * FISHING_PROXIMITY_BOOST);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GetCoordinatesAroundBobber(s16 bobber[], s16 surroundingTile[][AXIS_COUNT], u32 facingDirection)
|
|
||||||
{
|
|
||||||
u32 direction;
|
|
||||||
|
|
||||||
for (direction = DIR_SOUTH; direction < CARDINAL_DIRECTION_COUNT; direction++)
|
|
||||||
{
|
|
||||||
surroundingTile[direction][AXIS_X] = bobber[AXIS_X];
|
|
||||||
surroundingTile[direction][AXIS_Y] = bobber[AXIS_Y];
|
|
||||||
MoveCoords(direction, &surroundingTile[direction][AXIS_X], &surroundingTile[direction][AXIS_Y]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 CountQualifyingTiles(s16 surroundingTile[][AXIS_COUNT], s16 player[], u8 facingDirection, struct ObjectEvent *objectEvent, bool32 isTileLand[])
|
|
||||||
{
|
|
||||||
u32 numQualifyingTile = 0;
|
|
||||||
s16 tile[AXIS_COUNT];
|
|
||||||
u8 direction = DIR_SOUTH;
|
|
||||||
|
|
||||||
for (direction = DIR_SOUTH; direction < CARDINAL_DIRECTION_COUNT; direction++)
|
|
||||||
{
|
|
||||||
tile[AXIS_X] = surroundingTile[direction][AXIS_X];
|
|
||||||
tile[AXIS_Y] = surroundingTile[direction][AXIS_Y];
|
|
||||||
|
|
||||||
if (!CheckTileQualification(tile, player, facingDirection, objectEvent, isTileLand, direction))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
numQualifyingTile++;
|
|
||||||
}
|
|
||||||
return numQualifyingTile;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 CheckTileQualification(s16 tile[], s16 player[], u32 facingDirection, struct ObjectEvent* objectEvent, bool32 isTileLand[], u32 direction)
|
|
||||||
{
|
|
||||||
u32 collison = GetCollisionAtCoords(objectEvent, tile[AXIS_X], tile[AXIS_Y], facingDirection);
|
|
||||||
|
|
||||||
if (IsPlayerHere(tile[AXIS_X], tile[AXIS_Y], player[AXIS_X], player[AXIS_Y]))
|
|
||||||
return FALSE;
|
|
||||||
else if (IsMetatileBlocking(tile[AXIS_X], tile[AXIS_Y], collison))
|
|
||||||
return TRUE;
|
|
||||||
else if (MetatileBehavior_IsSurfableFishableWater(MapGridGetMetatileBehaviorAt(tile[AXIS_X], tile[AXIS_Y])))
|
|
||||||
return FALSE;
|
|
||||||
else if (IsMetatileLand(tile[AXIS_X], tile[AXIS_Y], collison))
|
|
||||||
isTileLand[direction] = TRUE;
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 CountLandTiles(bool32 isTileLand[])
|
|
||||||
{
|
|
||||||
u32 direction, numQualifyingTile = 0;
|
|
||||||
|
|
||||||
for (direction = DIR_SOUTH; direction < CARDINAL_DIRECTION_COUNT; direction++)
|
|
||||||
if (isTileLand[direction])
|
|
||||||
numQualifyingTile++;
|
|
||||||
|
|
||||||
return (numQualifyingTile < 2) ? 0 : numQualifyingTile;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 IsPlayerHere(s16 x, s16 y, s16 playerX, s16 playerY)
|
|
||||||
{
|
|
||||||
return ((x == playerX) && (y == playerY));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 IsMetatileBlocking(s16 x, s16 y, u32 collison)
|
|
||||||
{
|
|
||||||
switch(collison)
|
|
||||||
{
|
|
||||||
case COLLISION_NONE:
|
|
||||||
case COLLISION_STOP_SURFING:
|
|
||||||
case COLLISION_ELEVATION_MISMATCH:
|
|
||||||
return FALSE;
|
|
||||||
default:
|
|
||||||
return TRUE;
|
|
||||||
case COLLISION_OBJECT_EVENT:
|
|
||||||
return (gObjectEvents[GetObjectEventIdByXY(x,y)].inanimate);
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool32 IsMetatileLand(s16 x, s16 y, u32 collison)
|
|
||||||
{
|
|
||||||
switch(collison)
|
|
||||||
{
|
|
||||||
case COLLISION_NONE:
|
|
||||||
case COLLISION_STOP_SURFING:
|
|
||||||
case COLLISION_ELEVATION_MISMATCH:
|
|
||||||
return TRUE;
|
|
||||||
default:
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef tStep
|
|
||||||
#undef tFrameCounter
|
|
||||||
#undef tFishingRod
|
|
||||||
|
|
||||||
static void AlignFishingAnimationFrames(void)
|
|
||||||
{
|
|
||||||
struct Sprite *playerSprite = &gSprites[gPlayerAvatar.spriteId];
|
|
||||||
u8 animCmdIndex;
|
|
||||||
u8 animType;
|
|
||||||
|
|
||||||
AnimateSprite(playerSprite);
|
|
||||||
playerSprite->x2 = 0;
|
|
||||||
playerSprite->y2 = 0;
|
|
||||||
animCmdIndex = playerSprite->animCmdIndex;
|
|
||||||
if (playerSprite->anims[playerSprite->animNum][animCmdIndex].type == -1)
|
|
||||||
{
|
|
||||||
animCmdIndex--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
playerSprite->animDelayCounter++;
|
|
||||||
if (playerSprite->anims[playerSprite->animNum][animCmdIndex].type == -1)
|
|
||||||
animCmdIndex--;
|
|
||||||
}
|
|
||||||
animType = playerSprite->anims[playerSprite->animNum][animCmdIndex].type;
|
|
||||||
if (animType == 1 || animType == 2 || animType == 3)
|
|
||||||
{
|
|
||||||
playerSprite->x2 = 8;
|
|
||||||
if (GetPlayerFacingDirection() == 3)
|
|
||||||
playerSprite->x2 = -8;
|
|
||||||
}
|
|
||||||
if (animType == 5)
|
|
||||||
playerSprite->y2 = -8;
|
|
||||||
if (animType == 10 || animType == 11)
|
|
||||||
playerSprite->y2 = 8;
|
|
||||||
if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING)
|
|
||||||
SetSurfBlob_PlayerOffset(gObjectEvents[gPlayerAvatar.objectEventId].fieldEffectSpriteId, TRUE, playerSprite->y2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetSpinStartFacingDir(u8 direction)
|
void SetSpinStartFacingDir(u8 direction)
|
||||||
{
|
{
|
||||||
sSpinStartFacingDir = direction;
|
sSpinStartFacingDir = direction;
|
||||||
|
|||||||
650
src/fishing.c
Normal file
650
src/fishing.c
Normal file
@ -0,0 +1,650 @@
|
|||||||
|
#include "global.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "event_object_movement.h"
|
||||||
|
#include "fieldmap.h"
|
||||||
|
#include "field_effect_helpers.h"
|
||||||
|
#include "field_player_avatar.h"
|
||||||
|
#include "menu.h"
|
||||||
|
#include "metatile_behavior.h"
|
||||||
|
#include "random.h"
|
||||||
|
#include "script.h"
|
||||||
|
#include "strings.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "text.h"
|
||||||
|
#include "tv.h"
|
||||||
|
#include "wild_encounter.h"
|
||||||
|
#include "config/fishing.h"
|
||||||
|
|
||||||
|
static void Task_Fishing(u8);
|
||||||
|
static bool32 Fishing_Init(struct Task *);
|
||||||
|
static bool32 Fishing_GetRodOut(struct Task *);
|
||||||
|
static bool32 Fishing_WaitBeforeDots(struct Task *);
|
||||||
|
static bool32 Fishing_InitDots(struct Task *);
|
||||||
|
static bool32 Fishing_ShowDots(struct Task *);
|
||||||
|
static bool32 Fishing_CheckForBite(struct Task *);
|
||||||
|
static bool32 Fishing_GotBite(struct Task *);
|
||||||
|
static bool32 Fishing_ChangeMinigame(struct Task *);
|
||||||
|
static bool32 Fishing_WaitForA(struct Task *);
|
||||||
|
static bool32 Fishing_APressNoMinigame(struct Task *);
|
||||||
|
static bool32 Fishing_CheckMoreDots(struct Task *);
|
||||||
|
static bool32 Fishing_MonOnHook(struct Task *);
|
||||||
|
static bool32 Fishing_StartEncounter(struct Task *);
|
||||||
|
static bool32 Fishing_NotEvenNibble(struct Task *);
|
||||||
|
static bool32 Fishing_GotAway(struct Task *);
|
||||||
|
static bool32 Fishing_NoMon(struct Task *);
|
||||||
|
static bool32 Fishing_PutRodAway(struct Task *);
|
||||||
|
static bool32 Fishing_EndNoMon(struct Task *);
|
||||||
|
static void AlignFishingAnimationFrames(void);
|
||||||
|
static bool32 DoesFishingMinigameAllowCancel(void);
|
||||||
|
static bool32 Fishing_DoesFirstMonInPartyHaveSuctionCupsOrStickyHold(void);
|
||||||
|
static bool32 Fishing_RollForBite(u32, bool32);
|
||||||
|
static u32 CalculateFishingBiteOdds(u32, bool32);
|
||||||
|
static u32 CalculateFishingFollowerBoost(void);
|
||||||
|
static u32 CalculateFishingProximityBoost(void);
|
||||||
|
static u32 CalculateFishingTimeOfDayBoost(void);
|
||||||
|
|
||||||
|
#define FISHING_PROXIMITY_BOOST 20 //Active if config I_FISHING_PROXIMITY is TRUE
|
||||||
|
#define FISHING_TIME_OF_DAY_BOOST 20 //Active if config I_FISHING_TIME_OF_DAY_BOOST is TRUE
|
||||||
|
#define FISHING_GEN3_STICKY_CHANCE 85 //Active if config I_FISHING_STICKY_BOOST is set to GEN_3 or lower
|
||||||
|
|
||||||
|
#if I_FISHING_BITE_ODDS >= GEN_4
|
||||||
|
#define FISHING_OLD_ROD_ODDS 25
|
||||||
|
#define FISHING_GOOD_ROD_ODDS 50
|
||||||
|
#define FISHING_SUPER_ROD_ODDS 75
|
||||||
|
#elif I_FISHING_BITE_ODDS >= GEN_3
|
||||||
|
#define FISHING_OLD_ROD_ODDS 50
|
||||||
|
#define FISHING_GOOD_ROD_ODDS 50
|
||||||
|
#define FISHING_SUPER_ROD_ODDS 50
|
||||||
|
#else
|
||||||
|
#define FISHING_OLD_ROD_ODDS 100
|
||||||
|
#define FISHING_GOOD_ROD_ODDS 33
|
||||||
|
#define FISHING_SUPER_ROD_ODDS 50
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct FriendshipHookChanceBoost
|
||||||
|
{
|
||||||
|
u8 threshold;
|
||||||
|
u8 bonus;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Needs to be defined in descending order and end with the 0 friendship boost
|
||||||
|
//Active if config I_FISHING_FOLLOWER_BOOST is TRUE
|
||||||
|
static const struct FriendshipHookChanceBoost sFriendshipHookChanceBoostArray[] =
|
||||||
|
{
|
||||||
|
{.threshold = 250, .bonus = 50},
|
||||||
|
{.threshold = 200, .bonus = 40},
|
||||||
|
{.threshold = 150, .bonus = 30},
|
||||||
|
{.threshold = 100, .bonus = 20},
|
||||||
|
{.threshold = 0, .bonus = 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FISHING_CHAIN_SHINY_STREAK_MAX 20
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
FISHING_INIT,
|
||||||
|
FISHING_GET_ROD_OUT,
|
||||||
|
FISHING_WAIT_BEFORE_DOTS,
|
||||||
|
FISHING_INIT_DOTS,
|
||||||
|
FISHING_SHOW_DOTS,
|
||||||
|
FISHING_CHECK_FOR_BITE,
|
||||||
|
FISHING_GOT_BITE,
|
||||||
|
FISHING_CHANGE_MINIGAME,
|
||||||
|
FISHING_WAIT_FOR_A,
|
||||||
|
FISHING_A_PRESS_NO_MINIGAME,
|
||||||
|
FISHING_CHECK_MORE_DOTS,
|
||||||
|
FISHING_MON_ON_HOOK,
|
||||||
|
FISHING_START_ENCOUNTER,
|
||||||
|
FISHING_NOT_EVEN_NIBBLE,
|
||||||
|
FISHING_GOT_AWAY,
|
||||||
|
FISHING_NO_MON,
|
||||||
|
FISHING_PUT_ROD_AWAY,
|
||||||
|
FISHING_END_NO_MON,
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool32 (*const sFishingStateFuncs[])(struct Task *) =
|
||||||
|
{
|
||||||
|
[FISHING_INIT] = Fishing_Init,
|
||||||
|
[FISHING_GET_ROD_OUT] = Fishing_GetRodOut,
|
||||||
|
[FISHING_WAIT_BEFORE_DOTS] = Fishing_WaitBeforeDots,
|
||||||
|
[FISHING_INIT_DOTS] = Fishing_InitDots,
|
||||||
|
[FISHING_SHOW_DOTS] = Fishing_ShowDots,
|
||||||
|
[FISHING_CHECK_FOR_BITE] = Fishing_CheckForBite,
|
||||||
|
[FISHING_GOT_BITE] = Fishing_GotBite,
|
||||||
|
[FISHING_CHANGE_MINIGAME] = Fishing_ChangeMinigame,
|
||||||
|
[FISHING_WAIT_FOR_A] = Fishing_WaitForA,
|
||||||
|
[FISHING_A_PRESS_NO_MINIGAME] = Fishing_APressNoMinigame,
|
||||||
|
[FISHING_CHECK_MORE_DOTS] = Fishing_CheckMoreDots,
|
||||||
|
[FISHING_MON_ON_HOOK] = Fishing_MonOnHook,
|
||||||
|
[FISHING_START_ENCOUNTER] = Fishing_StartEncounter,
|
||||||
|
[FISHING_NOT_EVEN_NIBBLE] = Fishing_NotEvenNibble,
|
||||||
|
[FISHING_GOT_AWAY] = Fishing_GotAway,
|
||||||
|
[FISHING_NO_MON] = Fishing_NoMon,
|
||||||
|
[FISHING_PUT_ROD_AWAY] = Fishing_PutRodAway,
|
||||||
|
[FISHING_END_NO_MON] = Fishing_EndNoMon,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define tStep data[0]
|
||||||
|
#define tFrameCounter data[1]
|
||||||
|
#define tNumDots data[2]
|
||||||
|
#define tDotsRequired data[3]
|
||||||
|
#define tRoundsPlayed data[12]
|
||||||
|
#define tMinRoundsRequired data[13]
|
||||||
|
#define tPlayerGfxId data[14]
|
||||||
|
#define tFishingRod data[15]
|
||||||
|
|
||||||
|
void StartFishing(u8 rod)
|
||||||
|
{
|
||||||
|
u8 taskId = CreateTask(Task_Fishing, 0xFF);
|
||||||
|
|
||||||
|
gTasks[taskId].tFishingRod = rod;
|
||||||
|
Task_Fishing(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Task_Fishing(u8 taskId)
|
||||||
|
{
|
||||||
|
while (sFishingStateFuncs[gTasks[taskId].tStep](&gTasks[taskId]))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_Init(struct Task *task)
|
||||||
|
{
|
||||||
|
LockPlayerFieldControls();
|
||||||
|
gPlayerAvatar.preventStep = TRUE;
|
||||||
|
task->tStep = FISHING_GET_ROD_OUT;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_GetRodOut(struct Task *task)
|
||||||
|
{
|
||||||
|
struct ObjectEvent *playerObjEvent;
|
||||||
|
const s16 minRounds1[] = {
|
||||||
|
[OLD_ROD] = 1,
|
||||||
|
[GOOD_ROD] = 1,
|
||||||
|
[SUPER_ROD] = 1
|
||||||
|
};
|
||||||
|
const s16 minRounds2[] = {
|
||||||
|
[OLD_ROD] = 1,
|
||||||
|
[GOOD_ROD] = 3,
|
||||||
|
[SUPER_ROD] = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
task->tRoundsPlayed = 0;
|
||||||
|
task->tMinRoundsRequired = minRounds1[task->tFishingRod] + (Random() % minRounds2[task->tFishingRod]);
|
||||||
|
task->tPlayerGfxId = gObjectEvents[gPlayerAvatar.objectEventId].graphicsId;
|
||||||
|
playerObjEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
|
||||||
|
ObjectEventClearHeldMovementIfActive(playerObjEvent);
|
||||||
|
playerObjEvent->enableAnim = TRUE;
|
||||||
|
SetPlayerAvatarFishing(playerObjEvent->facingDirection);
|
||||||
|
task->tStep = FISHING_WAIT_BEFORE_DOTS;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_WaitBeforeDots(struct Task *task)
|
||||||
|
{
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
|
||||||
|
// Wait one second
|
||||||
|
task->tFrameCounter++;
|
||||||
|
if (task->tFrameCounter >= 60)
|
||||||
|
task->tStep = FISHING_INIT_DOTS;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_InitDots(struct Task *task)
|
||||||
|
{
|
||||||
|
u32 randVal;
|
||||||
|
|
||||||
|
LoadMessageBoxAndFrameGfx(0, TRUE);
|
||||||
|
task->tStep = FISHING_SHOW_DOTS;
|
||||||
|
task->tFrameCounter = 0;
|
||||||
|
task->tNumDots = 0;
|
||||||
|
randVal = Random();
|
||||||
|
randVal %= 10;
|
||||||
|
task->tDotsRequired = randVal + 1;
|
||||||
|
if (task->tRoundsPlayed == 0)
|
||||||
|
task->tDotsRequired = randVal + 4;
|
||||||
|
if (task->tDotsRequired >= 10)
|
||||||
|
task->tDotsRequired = 10;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_ShowDots(struct Task *task)
|
||||||
|
{
|
||||||
|
const u8 dot[] = _("·");
|
||||||
|
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
task->tFrameCounter++;
|
||||||
|
if (JOY_NEW(A_BUTTON))
|
||||||
|
{
|
||||||
|
if (!DoesFishingMinigameAllowCancel())
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
task->tStep = FISHING_NOT_EVEN_NIBBLE;
|
||||||
|
if (task->tRoundsPlayed != 0)
|
||||||
|
task->tStep = FISHING_GOT_AWAY;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (task->tFrameCounter >= 20)
|
||||||
|
{
|
||||||
|
task->tFrameCounter = 0;
|
||||||
|
if (task->tNumDots >= task->tDotsRequired)
|
||||||
|
{
|
||||||
|
task->tStep = FISHING_CHECK_FOR_BITE;
|
||||||
|
if (task->tRoundsPlayed != 0)
|
||||||
|
task->tStep = FISHING_GOT_BITE;
|
||||||
|
task->tRoundsPlayed++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddTextPrinterParameterized(0, FONT_NORMAL, dot, task->tNumDots * 8, 1, 0, NULL);
|
||||||
|
task->tNumDots++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_CheckForBite(struct Task *task)
|
||||||
|
{
|
||||||
|
bool32 bite, firstMonHasSuctionOrSticky;
|
||||||
|
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
task->tStep = FISHING_GOT_BITE;
|
||||||
|
bite = FALSE;
|
||||||
|
|
||||||
|
if (!DoesCurrentMapHaveFishingMons())
|
||||||
|
{
|
||||||
|
task->tStep = FISHING_NOT_EVEN_NIBBLE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstMonHasSuctionOrSticky = Fishing_DoesFirstMonInPartyHaveSuctionCupsOrStickyHold();
|
||||||
|
|
||||||
|
if(firstMonHasSuctionOrSticky && I_FISHING_STICKY_BOOST < GEN_4)
|
||||||
|
bite = RandomPercentage(RNG_FISHING_GEN3_STICKY, FISHING_GEN3_STICKY_CHANCE);
|
||||||
|
|
||||||
|
if (!bite)
|
||||||
|
bite = Fishing_RollForBite(task->tFishingRod, firstMonHasSuctionOrSticky);
|
||||||
|
|
||||||
|
if (!bite)
|
||||||
|
task->tStep = FISHING_NOT_EVEN_NIBBLE;
|
||||||
|
|
||||||
|
if (bite)
|
||||||
|
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], GetFishingBiteDirectionAnimNum(GetPlayerFacingDirection()));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_GotBite(struct Task *task)
|
||||||
|
{
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
AddTextPrinterParameterized(0, FONT_NORMAL, gText_OhABite, 0, 17, 0, NULL);
|
||||||
|
task->tStep = FISHING_CHANGE_MINIGAME;
|
||||||
|
task->tFrameCounter = 0;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_ChangeMinigame(struct Task *task)
|
||||||
|
{
|
||||||
|
switch (I_FISHING_MINIGAME)
|
||||||
|
{
|
||||||
|
case GEN_1:
|
||||||
|
case GEN_2:
|
||||||
|
task->tStep = FISHING_A_PRESS_NO_MINIGAME;
|
||||||
|
break;
|
||||||
|
case GEN_3:
|
||||||
|
default:
|
||||||
|
task->tStep = FISHING_WAIT_FOR_A;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a bite. Now, wait for the player to press A, or the timer to expire.
|
||||||
|
static bool32 Fishing_WaitForA(struct Task *task)
|
||||||
|
{
|
||||||
|
const s16 reelTimeouts[3] = {
|
||||||
|
[OLD_ROD] = 36,
|
||||||
|
[GOOD_ROD] = 33,
|
||||||
|
[SUPER_ROD] = 30
|
||||||
|
};
|
||||||
|
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
task->tFrameCounter++;
|
||||||
|
if (task->tFrameCounter >= reelTimeouts[task->tFishingRod])
|
||||||
|
task->tStep = FISHING_GOT_AWAY;
|
||||||
|
else if (JOY_NEW(A_BUTTON))
|
||||||
|
task->tStep = FISHING_CHECK_MORE_DOTS;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_APressNoMinigame(struct Task *task)
|
||||||
|
{
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
if (JOY_NEW(A_BUTTON))
|
||||||
|
task->tStep = FISHING_MON_ON_HOOK;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if we're going to play the dot game again
|
||||||
|
static bool32 Fishing_CheckMoreDots(struct Task *task)
|
||||||
|
{
|
||||||
|
const s16 moreDotsChance[][2] =
|
||||||
|
{
|
||||||
|
[OLD_ROD] = {0, 0},
|
||||||
|
[GOOD_ROD] = {40, 10},
|
||||||
|
[SUPER_ROD] = {70, 30}
|
||||||
|
};
|
||||||
|
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
task->tStep = FISHING_MON_ON_HOOK;
|
||||||
|
if (task->tRoundsPlayed < task->tMinRoundsRequired)
|
||||||
|
{
|
||||||
|
task->tStep = FISHING_INIT_DOTS;
|
||||||
|
}
|
||||||
|
else if (task->tRoundsPlayed < 2)
|
||||||
|
{
|
||||||
|
// probability of having to play another round
|
||||||
|
s16 probability = Random() % 100;
|
||||||
|
|
||||||
|
if (moreDotsChance[task->tFishingRod][task->tRoundsPlayed] > probability)
|
||||||
|
task->tStep = FISHING_INIT_DOTS;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_MonOnHook(struct Task *task)
|
||||||
|
{
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
FillWindowPixelBuffer(0, PIXEL_FILL(1));
|
||||||
|
AddTextPrinterParameterized2(0, FONT_NORMAL, gText_PokemonOnHook, 1, 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY);
|
||||||
|
task->tStep = FISHING_START_ENCOUNTER;
|
||||||
|
task->tFrameCounter = 0;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_StartEncounter(struct Task *task)
|
||||||
|
{
|
||||||
|
if (task->tFrameCounter == 0)
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
|
||||||
|
RunTextPrinters();
|
||||||
|
|
||||||
|
if (task->tFrameCounter == 0)
|
||||||
|
{
|
||||||
|
if (!IsTextPrinterActive(0))
|
||||||
|
{
|
||||||
|
struct ObjectEvent *playerObjEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
|
||||||
|
|
||||||
|
ObjectEventSetGraphicsId(playerObjEvent, task->tPlayerGfxId);
|
||||||
|
ObjectEventTurn(playerObjEvent, playerObjEvent->movementDirection);
|
||||||
|
if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING)
|
||||||
|
SetSurfBlob_PlayerOffset(gObjectEvents[gPlayerAvatar.objectEventId].fieldEffectSpriteId, FALSE, 0);
|
||||||
|
gSprites[gPlayerAvatar.spriteId].x2 = 0;
|
||||||
|
gSprites[gPlayerAvatar.spriteId].y2 = 0;
|
||||||
|
ClearDialogWindowAndFrame(0, TRUE);
|
||||||
|
task->tFrameCounter++;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task->tFrameCounter != 0)
|
||||||
|
{
|
||||||
|
gPlayerAvatar.preventStep = FALSE;
|
||||||
|
UnlockPlayerFieldControls();
|
||||||
|
FishingWildEncounter(task->tFishingRod);
|
||||||
|
RecordFishingAttemptForTV(TRUE);
|
||||||
|
DestroyTask(FindTaskIdByFunc(Task_Fishing));
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_NotEvenNibble(struct Task *task)
|
||||||
|
{
|
||||||
|
gChainFishingDexNavStreak = 0;
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], GetFishingNoCatchDirectionAnimNum(GetPlayerFacingDirection()));
|
||||||
|
FillWindowPixelBuffer(0, PIXEL_FILL(1));
|
||||||
|
AddTextPrinterParameterized2(0, FONT_NORMAL, gText_NotEvenANibble, 1, 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY);
|
||||||
|
task->tStep = FISHING_NO_MON;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_GotAway(struct Task *task)
|
||||||
|
{
|
||||||
|
gChainFishingDexNavStreak = 0;
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], GetFishingNoCatchDirectionAnimNum(GetPlayerFacingDirection()));
|
||||||
|
FillWindowPixelBuffer(0, PIXEL_FILL(1));
|
||||||
|
AddTextPrinterParameterized2(0, FONT_NORMAL, gText_ItGotAway, 1, 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY);
|
||||||
|
task->tStep = FISHING_NO_MON;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_NoMon(struct Task *task)
|
||||||
|
{
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
task->tStep = FISHING_PUT_ROD_AWAY;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_PutRodAway(struct Task *task)
|
||||||
|
{
|
||||||
|
AlignFishingAnimationFrames();
|
||||||
|
if (gSprites[gPlayerAvatar.spriteId].animEnded)
|
||||||
|
{
|
||||||
|
struct ObjectEvent *playerObjEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
|
||||||
|
|
||||||
|
ObjectEventSetGraphicsId(playerObjEvent, task->tPlayerGfxId);
|
||||||
|
ObjectEventTurn(playerObjEvent, playerObjEvent->movementDirection);
|
||||||
|
if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING)
|
||||||
|
SetSurfBlob_PlayerOffset(gObjectEvents[gPlayerAvatar.objectEventId].fieldEffectSpriteId, FALSE, 0);
|
||||||
|
gSprites[gPlayerAvatar.spriteId].x2 = 0;
|
||||||
|
gSprites[gPlayerAvatar.spriteId].y2 = 0;
|
||||||
|
task->tStep = FISHING_END_NO_MON;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_EndNoMon(struct Task *task)
|
||||||
|
{
|
||||||
|
RunTextPrinters();
|
||||||
|
if (!IsTextPrinterActive(0))
|
||||||
|
{
|
||||||
|
gPlayerAvatar.preventStep = FALSE;
|
||||||
|
UnlockPlayerFieldControls();
|
||||||
|
UnfreezeObjectEvents();
|
||||||
|
ClearDialogWindowAndFrame(0, TRUE);
|
||||||
|
RecordFishingAttemptForTV(FALSE);
|
||||||
|
DestroyTask(FindTaskIdByFunc(Task_Fishing));
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 DoesFishingMinigameAllowCancel(void)
|
||||||
|
{
|
||||||
|
switch(I_FISHING_MINIGAME)
|
||||||
|
{
|
||||||
|
case GEN_1:
|
||||||
|
case GEN_2:
|
||||||
|
return FALSE;
|
||||||
|
case GEN_3:
|
||||||
|
default:
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_DoesFirstMonInPartyHaveSuctionCupsOrStickyHold(void)
|
||||||
|
{
|
||||||
|
enum Ability ability;
|
||||||
|
|
||||||
|
if (GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
ability = GetMonAbility(&gPlayerParty[0]);
|
||||||
|
|
||||||
|
return (ability == ABILITY_SUCTION_CUPS || ability == ABILITY_STICKY_HOLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 Fishing_RollForBite(u32 rod, bool32 isStickyHold)
|
||||||
|
{
|
||||||
|
return ((RandomUniform(RNG_FISHING_BITE, 1, 100)) <= CalculateFishingBiteOdds(rod, isStickyHold));
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 CalculateFishingBiteOdds(u32 rod, bool32 isStickyHold)
|
||||||
|
{
|
||||||
|
u32 odds;
|
||||||
|
|
||||||
|
if (rod == OLD_ROD)
|
||||||
|
odds = FISHING_OLD_ROD_ODDS;
|
||||||
|
if (rod == GOOD_ROD)
|
||||||
|
odds = FISHING_GOOD_ROD_ODDS;
|
||||||
|
if (rod == SUPER_ROD)
|
||||||
|
odds = FISHING_SUPER_ROD_ODDS;
|
||||||
|
|
||||||
|
odds += CalculateFishingFollowerBoost();
|
||||||
|
odds += CalculateFishingProximityBoost();
|
||||||
|
odds += CalculateFishingTimeOfDayBoost();
|
||||||
|
|
||||||
|
if (isStickyHold && I_FISHING_STICKY_BOOST >= GEN_4)
|
||||||
|
odds *= 2;
|
||||||
|
|
||||||
|
odds = min(100, odds);
|
||||||
|
DebugPrintf("Fishing odds: %d", odds);
|
||||||
|
return odds;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 CalculateFishingFollowerBoost()
|
||||||
|
{
|
||||||
|
u32 friendship;
|
||||||
|
struct Pokemon *mon = GetFirstLiveMon();
|
||||||
|
|
||||||
|
if (!I_FISHING_FOLLOWER_BOOST || !mon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
friendship = GetMonData(mon, MON_DATA_FRIENDSHIP);
|
||||||
|
for (u32 i = 0;; i++)
|
||||||
|
{
|
||||||
|
if (friendship >= sFriendshipHookChanceBoostArray[i].threshold)
|
||||||
|
return sFriendshipHookChanceBoostArray[i].bonus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 CalculateFishingProximityBoost()
|
||||||
|
{
|
||||||
|
s16 bobber_x, bobber_y, tile_x, tile_y;
|
||||||
|
u32 direction, facingDirection, numQualifyingTile = 0;
|
||||||
|
struct ObjectEvent *objectEvent;
|
||||||
|
|
||||||
|
if (!I_FISHING_PROXIMITY)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
|
||||||
|
|
||||||
|
bobber_x = objectEvent->currentCoords.x;
|
||||||
|
bobber_y = objectEvent->currentCoords.y;
|
||||||
|
|
||||||
|
facingDirection = GetPlayerFacingDirection();
|
||||||
|
MoveCoords(facingDirection, &bobber_x, &bobber_y);
|
||||||
|
|
||||||
|
numQualifyingTile = 0;
|
||||||
|
for (direction = DIR_SOUTH; direction < CARDINAL_DIRECTION_COUNT; direction++)
|
||||||
|
{
|
||||||
|
tile_x = bobber_x;
|
||||||
|
tile_y = bobber_y;
|
||||||
|
MoveCoords(direction, &tile_x, &tile_y);
|
||||||
|
if (tile_x == objectEvent->currentCoords.x && tile_y == objectEvent->currentCoords.y)
|
||||||
|
continue;
|
||||||
|
if (!MetatileBehavior_IsSurfableFishableWater(MapGridGetMetatileBehaviorAt(tile_x, tile_y)))
|
||||||
|
numQualifyingTile++;
|
||||||
|
else if (MapGridGetCollisionAt(tile_x, tile_y))
|
||||||
|
numQualifyingTile++;
|
||||||
|
else if (GetMapBorderIdAt(tile_x, tile_y) == -1)
|
||||||
|
numQualifyingTile++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (numQualifyingTile * FISHING_PROXIMITY_BOOST);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 CalculateFishingTimeOfDayBoost()
|
||||||
|
{
|
||||||
|
if (!I_FISHING_TIME_OF_DAY_BOOST)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
enum TimeOfDay timeOfDay = GetTimeOfDay();
|
||||||
|
if (timeOfDay == TIME_MORNING || timeOfDay == TIME_EVENING)
|
||||||
|
return FISHING_TIME_OF_DAY_BOOST;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef tStep
|
||||||
|
#undef tFrameCounter
|
||||||
|
#undef tNumDots
|
||||||
|
#undef tDotsRequired
|
||||||
|
#undef tRoundsPlayed
|
||||||
|
#undef tMinRoundsRequired
|
||||||
|
#undef tPlayerGfxId
|
||||||
|
#undef tFishingRod
|
||||||
|
|
||||||
|
static void AlignFishingAnimationFrames(void)
|
||||||
|
{
|
||||||
|
struct Sprite *playerSprite = &gSprites[gPlayerAvatar.spriteId];
|
||||||
|
u8 animCmdIndex;
|
||||||
|
u8 animType;
|
||||||
|
|
||||||
|
AnimateSprite(playerSprite);
|
||||||
|
playerSprite->x2 = 0;
|
||||||
|
playerSprite->y2 = 0;
|
||||||
|
animCmdIndex = playerSprite->animCmdIndex;
|
||||||
|
if (playerSprite->anims[playerSprite->animNum][animCmdIndex].type == -1)
|
||||||
|
{
|
||||||
|
animCmdIndex--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
playerSprite->animDelayCounter++;
|
||||||
|
if (playerSprite->anims[playerSprite->animNum][animCmdIndex].type == -1)
|
||||||
|
animCmdIndex--;
|
||||||
|
}
|
||||||
|
animType = playerSprite->anims[playerSprite->animNum][animCmdIndex].type;
|
||||||
|
if (animType == 1 || animType == 2 || animType == 3)
|
||||||
|
{
|
||||||
|
playerSprite->x2 = 8;
|
||||||
|
if (GetPlayerFacingDirection() == 3)
|
||||||
|
playerSprite->x2 = -8;
|
||||||
|
}
|
||||||
|
if (animType == 5)
|
||||||
|
playerSprite->y2 = -8;
|
||||||
|
if (animType == 10 || animType == 11)
|
||||||
|
playerSprite->y2 = 8;
|
||||||
|
if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING)
|
||||||
|
SetSurfBlob_PlayerOffset(gObjectEvents[gPlayerAvatar.objectEventId].fieldEffectSpriteId, TRUE, playerSprite->y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateChainFishingStreak()
|
||||||
|
{
|
||||||
|
if (!I_FISHING_CHAIN)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (gChainFishingDexNavStreak == MAX_u8)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gChainFishingDexNavStreak++;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 CalculateChainFishingShinyRolls(void)
|
||||||
|
{
|
||||||
|
if (!I_FISHING_CHAIN || !gIsFishingEncounter)
|
||||||
|
return 0;
|
||||||
|
u32 a = 2 * min(gChainFishingDexNavStreak, FISHING_CHAIN_SHINY_STREAK_MAX);
|
||||||
|
DebugPrintf("Total Shiny Rolls %d", a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool32 ShouldUseFishingEnvironmentInBattle()
|
||||||
|
{
|
||||||
|
return (I_FISHING_ENVIRONMENT >= GEN_4 && gIsFishingEncounter);
|
||||||
|
}
|
||||||
@ -18,6 +18,7 @@
|
|||||||
#include "field_player_avatar.h"
|
#include "field_player_avatar.h"
|
||||||
#include "field_screen_effect.h"
|
#include "field_screen_effect.h"
|
||||||
#include "field_weather.h"
|
#include "field_weather.h"
|
||||||
|
#include "fishing.h"
|
||||||
#include "fldeff.h"
|
#include "fldeff.h"
|
||||||
#include "follower_npc.h"
|
#include "follower_npc.h"
|
||||||
#include "item.h"
|
#include "item.h"
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
#include "field_player_avatar.h"
|
#include "field_player_avatar.h"
|
||||||
#include "field_specials.h"
|
#include "field_specials.h"
|
||||||
#include "field_weather.h"
|
#include "field_weather.h"
|
||||||
|
#include "fishing.h"
|
||||||
#include "follower_npc.h"
|
#include "follower_npc.h"
|
||||||
#include "graphics.h"
|
#include "graphics.h"
|
||||||
#include "item.h"
|
#include "item.h"
|
||||||
@ -66,7 +67,6 @@
|
|||||||
#include "constants/trainers.h"
|
#include "constants/trainers.h"
|
||||||
#include "constants/union_room.h"
|
#include "constants/union_room.h"
|
||||||
#include "constants/weather.h"
|
#include "constants/weather.h"
|
||||||
#include "wild_encounter.h"
|
|
||||||
|
|
||||||
#define FRIENDSHIP_EVO_THRESHOLD ((P_FRIENDSHIP_EVO_THRESHOLD >= GEN_8) ? 160 : 220)
|
#define FRIENDSHIP_EVO_THRESHOLD ((P_FRIENDSHIP_EVO_THRESHOLD >= GEN_8) ? 160 : 220)
|
||||||
|
|
||||||
@ -1090,8 +1090,7 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV,
|
|||||||
totalRerolls += I_SHINY_CHARM_ADDITIONAL_ROLLS;
|
totalRerolls += I_SHINY_CHARM_ADDITIONAL_ROLLS;
|
||||||
if (LURE_STEP_COUNT != 0)
|
if (LURE_STEP_COUNT != 0)
|
||||||
totalRerolls += 1;
|
totalRerolls += 1;
|
||||||
if (I_FISHING_CHAIN && gIsFishingEncounter)
|
totalRerolls += CalculateChainFishingShinyRolls();
|
||||||
totalRerolls += CalculateChainFishingShinyRolls();
|
|
||||||
if (gDexNavSpecies)
|
if (gDexNavSpecies)
|
||||||
totalRerolls += CalculateDexNavShinyRolls();
|
totalRerolls += CalculateDexNavShinyRolls();
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#include "pokemon.h"
|
#include "pokemon.h"
|
||||||
#include "metatile_behavior.h"
|
#include "metatile_behavior.h"
|
||||||
#include "fieldmap.h"
|
#include "fieldmap.h"
|
||||||
|
#include "fishing.h"
|
||||||
#include "follower_npc.h"
|
#include "follower_npc.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
#include "field_player_avatar.h"
|
#include "field_player_avatar.h"
|
||||||
@ -43,7 +44,6 @@ extern const u8 EventScript_SprayWoreOff[];
|
|||||||
|
|
||||||
static u16 FeebasRandom(void);
|
static u16 FeebasRandom(void);
|
||||||
static void FeebasSeedRng(u16 seed);
|
static void FeebasSeedRng(u16 seed);
|
||||||
static void UpdateChainFishingStreak();
|
|
||||||
static bool8 IsWildLevelAllowedByRepel(u8 level);
|
static bool8 IsWildLevelAllowedByRepel(u8 level);
|
||||||
static void ApplyFluteEncounterRateMod(u32 *encRate);
|
static void ApplyFluteEncounterRateMod(u32 *encRate);
|
||||||
static void ApplyCleanseTagEncounterRateMod(u32 *encRate);
|
static void ApplyCleanseTagEncounterRateMod(u32 *encRate);
|
||||||
@ -964,22 +964,6 @@ bool8 DoesCurrentMapHaveFishingMons(void)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 CalculateChainFishingShinyRolls(void)
|
|
||||||
{
|
|
||||||
return (2 * min(gChainFishingDexNavStreak, FISHING_CHAIN_SHINY_STREAK_MAX));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void UpdateChainFishingStreak()
|
|
||||||
{
|
|
||||||
if (!I_FISHING_CHAIN)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (gChainFishingDexNavStreak >= FISHING_CHAIN_LENGTH_MAX)
|
|
||||||
return;
|
|
||||||
|
|
||||||
gChainFishingDexNavStreak++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FishingWildEncounter(u8 rod)
|
void FishingWildEncounter(u8 rod)
|
||||||
{
|
{
|
||||||
u16 species;
|
u16 species;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user