Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions include/config/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@
#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.
#define I_FISHING_BITE_ODDS GEN_LATEST // In Gen 1, the Old Rod has a 100% chance for a bite, and the Good Rod and Super Rod both have a 50% chance for a bite. In Gen 2 and 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_ITEM_H
2 changes: 2 additions & 0 deletions include/random.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ enum RandomTag
RNG_AI_ASSUME_ALL_STATUS,
RNG_AI_REFRESH_TRICK_ROOM_ON_LAST_TURN,
RNG_AI_APPLY_TAILWIND_ON_LAST_TURN_OF_TRICK_ROOM,
RNG_FISHING_BITE,
RNG_FISHING_GEN3_STICKY,
};

#define RandomWeighted(tag, ...) \
Expand Down
171 changes: 43 additions & 128 deletions src/field_player_avatar.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,8 @@ 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 u32 CalculateFishingProximityBoost(void);
static u32 CalculateFishingTimeOfDayBoost(void);

static u8 TrySpinPlayerForWarp(struct ObjectEvent *, s16 *);

Expand Down Expand Up @@ -1942,20 +1936,21 @@ static void Task_WaitStopSurfing(u8 taskId)
#define tPlayerGfxId data[14]
#define tFishingRod data[15]

#define FISHING_PROXIMITY_BOOST 4
#define FISHING_STICKY_BOOST 36
#define FISHING_PROXIMITY_BOOST 20
#define FISHING_TIME_OF_DAY_BOOST 20
#define FISHING_GEN3_STICKY_CHANCE 85

#if I_FISHING_BITE_ODDS >= GEN_4
#define FISHING_OLD_ROD_ODDS 75
#define FISHING_OLD_ROD_ODDS 25
#define FISHING_GOOD_ROD_ODDS 50
#define FISHING_SUPER_ROD_ODDS 25
#elif I_FISHING_BITE_ODDS >= GEN_3
#define FISHING_SUPER_ROD_ODDS 75
#elif I_FISHING_BITE_ODDS >= GEN_2
#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_OLD_ROD_ODDS 100
#define FISHING_GOOD_ROD_ODDS 50
#define FISHING_SUPER_ROD_ODDS 50
#endif

Expand Down Expand Up @@ -2133,11 +2128,11 @@ static bool32 Fishing_CheckForBite(struct Task *task)

firstMonHasSuctionOrSticky = Fishing_DoesFirstMonInPartyHaveSuctionCupsOrStickyHold();

if(firstMonHasSuctionOrSticky)
bite = Fishing_RollForBite(task->tFishingRod, firstMonHasSuctionOrSticky);
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, FALSE);
bite = Fishing_RollForBite(task->tFishingRod, firstMonHasSuctionOrSticky);

if (!bite)
task->tStep = FISHING_NOT_EVEN_NIBBLE;
Expand Down Expand Up @@ -2361,7 +2356,7 @@ static bool32 Fishing_DoesFirstMonInPartyHaveSuctionCupsOrStickyHold(void)

static bool32 Fishing_RollForBite(u32 rod, bool32 isStickyHold)
{
return ((Random() % 100) > CalculateFishingBiteOdds(rod, isStickyHold));
return ((RandomUniform(RNG_FISHING_BITE, 1, 100)) <= CalculateFishingBiteOdds(rod, isStickyHold));
}

static u32 CalculateFishingBiteOdds(u32 rod, bool32 isStickyHold)
Expand All @@ -2375,18 +2370,14 @@ static u32 CalculateFishingBiteOdds(u32 rod, bool32 isStickyHold)
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 += CalculateFishingFollowerBoost();
odds += CalculateFishingProximityBoost();
odds += CalculateFishingTimeOfDayBoost();

odds -= CalculateFishingProximityBoost(odds);
if (isStickyHold && I_FISHING_STICKY_BOOST >= GEN_4)
odds *= 2;

odds = min(100, odds);
return odds;
}

Expand All @@ -2411,125 +2402,49 @@ static u32 CalculateFishingFollowerBoost()
return 0;
}

static u32 CalculateFishingProximityBoost(u32 odds)
static u32 CalculateFishingProximityBoost()
{
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;
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];

player[AXIS_X] = objectEvent->currentCoords.x;
player[AXIS_Y] = objectEvent->currentCoords.y;
bobber[AXIS_X] = objectEvent->currentCoords.x;
bobber[AXIS_Y] = objectEvent->currentCoords.y;
bobber_x = objectEvent->currentCoords.x;
bobber_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;
MoveCoords(facingDirection, &bobber_x, &bobber_y);

numQualifyingTile = 0;
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))
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;

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])
if (!MetatileBehavior_IsSurfableFishableWater(MapGridGetMetatileBehaviorAt(tile_x, tile_y)))
numQualifyingTile++;
else if (MapGridGetCollisionAt(tile_x, tile_y) == COLLISION_IMPASSABLE)
numQualifyingTile++;
}

return (numQualifyingTile < 2) ? 0 : numQualifyingTile;
}

static bool32 IsPlayerHere(s16 x, s16 y, s16 playerX, s16 playerY)
{
return ((x == playerX) && (y == playerY));
return (numQualifyingTile * FISHING_PROXIMITY_BOOST);
}

static bool32 IsMetatileBlocking(s16 x, s16 y, u32 collison)
static u32 CalculateFishingTimeOfDayBoost()
{
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;
}
if (!I_FISHING_TIME_OF_DAY_BOOST)
return 0;

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;
}
enum TimeOfDay timeOfDay = GetTimeOfDay();
if (timeOfDay == TIME_MORNING || timeOfDay == TIME_EVENING)
return FISHING_TIME_OF_DAY_BOOST;
return 0;
}

#undef tStep
Expand Down