Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Evolution Refactor #5785

Draft
wants to merge 32 commits into
base: upcoming
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
62965de
Initial version, gender condition
AsparagusEduardo Dec 7, 2024
32b2e05
Fixed evolution test
AsparagusEduardo Dec 7, 2024
ceccd30
Friendship condition
AsparagusEduardo Dec 7, 2024
648d736
Attack vs Defense condition
AsparagusEduardo Dec 7, 2024
7100c54
Added function for conditions
AsparagusEduardo Dec 7, 2024
982f09f
Time conditions
AsparagusEduardo Dec 7, 2024
cf95cde
Species and Type in Party conditionals
AsparagusEduardo Dec 7, 2024
563ff0a
Weather condition
AsparagusEduardo Dec 7, 2024
fd73b11
Personality conditions
AsparagusEduardo Dec 7, 2024
035b40d
Map conditions
AsparagusEduardo Dec 7, 2024
eee1e99
Knows Move conditions
AsparagusEduardo Dec 7, 2024
9475aa8
Nature condition
AsparagusEduardo Dec 7, 2024
ef73f47
Held item conditions
AsparagusEduardo Dec 8, 2024
8032cce
Fixed looping conditions
AsparagusEduardo Dec 8, 2024
d33fbd4
Beauty condition
AsparagusEduardo Dec 9, 2024
01f5f28
Overworld steps condition
AsparagusEduardo Dec 9, 2024
7b0c6e1
Merge branch '_RHH/upcoming' into _RHH/pr/upcoming/evolutionRefactor
AsparagusEduardo Jan 12, 2025
4eaae06
Evolution tracker conditions
AsparagusEduardo Jan 12, 2025
646f2ad
Karrablast/Shelmet evo
AsparagusEduardo Jan 15, 2025
26e70ea
Critical hits evo
AsparagusEduardo Jan 15, 2025
00e2fc9
Shedinja
AsparagusEduardo Jan 16, 2025
c912934
Evolution triggers
AsparagusEduardo Jan 17, 2025
d461b1c
Merge branch '_RHH/upcoming' into _RHH/pr/upcoming/evolutionRefactor
AsparagusEduardo Jan 21, 2025
909266a
Removed EVO_MODE_CANT_STOP
AsparagusEduardo Jan 21, 2025
d49cf2e
Added 2 args for conditinos
AsparagusEduardo Jan 22, 2025
46f286d
Gholdengo condition
AsparagusEduardo Jan 22, 2025
8f04226
Shedinja uses IF_BAG_ITEM_COUNT
AsparagusEduardo Jan 22, 2025
853cf2d
Starting to generate HGSS descriptions based on conditions
AsparagusEduardo Jan 22, 2025
275a2a2
More HGSS dex conditions
AsparagusEduardo Jan 22, 2025
8ecd200
More HGSS Dex
AsparagusEduardo Jan 23, 2025
c61963c
Wurmple
AsparagusEduardo Jan 23, 2025
46cc9b9
Adjust text automatically for conditions
AsparagusEduardo Jan 24, 2025
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
105 changes: 51 additions & 54 deletions include/constants/pokemon.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,65 +246,62 @@

#define EVOLUTIONS_END 0xFFFF // Not an actual evolution, used to mark the end of an evolution array.

enum EvolutionConditions {
// Gen 2
IF_GENDER, // The Pokémon is of specific gender.
IF_TIME, // It is currently the specific time of day.
IF_NOT_TIME, // It is NOT currently the specific time of day.
IF_MIN_FRIENDSHIP, // The Pokémon has the defined amount of Friendship.
IF_ATK_GT_DEF, // The Pokémon's Attack is greater than its Defense stat.
IF_ATK_EQ_DEF, // The Pokémon's Attack is equal to its Defense stat.
IF_ATK_LT_DEF, // The Pokémon's Attack is lower than its Defense stat.
IF_HOLD_ITEM, // The Pokémon is holding a specific item.
// Gen 3
IF_PID_UPPER_MODULO_10_GT, // The Pokémon's upper personality value's modulo by 10 is greater than the defined value.
IF_PID_UPPER_MODULO_10_LE, // The Pokémon's upper personality value's modulo by 10 is lower or equal than the defined value.
IF_MIN_BEAUTY, // The Pokémon has the defined amount of Beauty.
// Gen 4
IF_SPECIES_IN_PARTY, // The party contains a Pokémon of the specified species.
IF_IN_MAP, // The player is currently in the specific map.
IF_IN_MAPSEC, // The player is currently in the specific map sector.
IF_KNOWS_MOVE, // The Pokémon knows specific move.
// Gen 5
IF_TRADE_PARTNER_SPECIES, // The Pokémon is traded for a specific species.
// Gen 6
IF_TYPE_IN_PARTY, // The party contains a Pokémon of the specified type.
IF_WEATHER, // It is currently the specific weather in the current map.
IF_KNOWS_MOVE_TYPE, // The Pokémon knows a move with a specific type.
// Gen 8
IF_NATURE, // The Pokémon has a specific nature.
IF_AMPED_NATURE, // The Pokémon has one of the following natures: Hardy, Brave, Adamant, Naughty, Docile, Impish, Lax, Hasty, Jolly, Naive, Rash, Sassy, or Quirky.
IF_LOW_KEY_NATURE, // The Pokémon has one of the following natures: Lonely, Bold, Relaxed, Timid, Serious, Modest, Mild, Quiet, Bashful, Calm, Gentle, or Careful.
IF_RECOIL_DAMAGE_GE, // The Pokémon suffered at least certain amount of non-fainting recoil damage.
IF_CURRENT_DAMAGE_GE, // The Pokémon has the specified difference of HP from its Max HP.
IF_CRITICAL_HITS_GE, // The Pokémon performed the specified number of critical hits in one battle at least.
IF_USED_MOVE_X_TIMES, // The Pokémon has used a move for at least X amount of times.
// Gen 9
IF_DEFEAT_X_WITH_ITEMS, // The Pokémon defeated X amount of Pokémon of the specified species that are holding the specified item.
IF_PID_MODULO_100_GT, // The Pokémon's personality value's modulo by 100 is greater than the defined value.
IF_PID_MODULO_100_EQ, // The Pokémon's personality value's modulo by 100 is equal than the defined value.
IF_PID_MODULO_100_LT, // The Pokémon's personality value's modulo by 100 is lower than the defined value.
IF_MIN_OVERWORLD_STEPS, // The Player has taken a specific amount of steps in the overworld with the Pokémon following them or in the first slot of the party.
IF_BAG_ITEM_COUNT, // The Player has the specific amount of an item in the bag. It then removes those items.
CONDITIONS_END
};

enum EvolutionMethods {
EVO_NONE, // Not an actual evolution, used to generate offspring that can't evolve into the specified species, like regional forms.
EVO_FRIENDSHIP, // Pokémon levels up with friendship ≥ 220
EVO_FRIENDSHIP_DAY, // Pokémon levels up during the day with friendship ≥ 220
EVO_FRIENDSHIP_NIGHT, // Pokémon levels up at night with friendship ≥ 220
EVO_LEVEL, // Pokémon reaches the specified level
EVO_TRADE, // Pokémon is traded
EVO_TRADE_ITEM, // Pokémon is traded while it's holding the specified item
EVO_ITEM, // specified item is used on Pokémon
EVO_LEVEL_ATK_GT_DEF, // Pokémon reaches the specified level with attack > defense
EVO_LEVEL_ATK_EQ_DEF, // Pokémon reaches the specified level with attack = defense
EVO_LEVEL_ATK_LT_DEF, // Pokémon reaches the specified level with attack < defense
EVO_LEVEL_SILCOON, // Pokémon reaches the specified level with a Silcoon personality value
EVO_LEVEL_CASCOON, // Pokémon reaches the specified level with a Cascoon personality value
EVO_LEVEL_NINJASK, // Pokémon reaches the specified level (special value for Ninjask)
EVO_LEVEL_SHEDINJA, // Pokémon reaches the specified level (special value for Shedinja)
EVO_BEAUTY, // Pokémon levels up with beauty ≥ specified value
EVO_LEVEL_FEMALE, // Pokémon reaches the specified level, is female
EVO_LEVEL_MALE, // Pokémon reaches the specified level, is male
EVO_LEVEL_NIGHT, // Pokémon reaches the specified level, is night
EVO_LEVEL_DAY, // Pokémon reaches the specified level, is day
EVO_LEVEL_DUSK, // Pokémon reaches the specified level, is dusk (5-6 P.M)
EVO_ITEM_HOLD_DAY, // Pokémon levels up, holds specified item at day
EVO_ITEM_HOLD_NIGHT, // Pokémon levels up, holds specified item at night
EVO_MOVE, // Pokémon levels up, knows specified move
EVO_FRIENDSHIP_MOVE_TYPE, // Pokémon levels up with friendship ≥ 220, knows move with specified type
EVO_MAPSEC, // Pokémon levels up on specified mapsec
EVO_ITEM_MALE, // specified item is used on a male Pokémon
EVO_ITEM_FEMALE, // specified item is used on a female Pokémon
EVO_LEVEL_RAIN, // Pokémon reaches the specified level during rain in the overworld
EVO_SPECIFIC_MON_IN_PARTY, // Pokémon levels up with a specified Pokémon in party
EVO_LEVEL_DARK_TYPE_MON_IN_PARTY, // Pokémon reaches the specified level with a Dark Type Pokémon in party
EVO_TRADE_SPECIFIC_MON, // Pokémon is traded for a specified Pokémon
EVO_SPECIFIC_MAP, // Pokémon levels up on specified map
EVO_LEVEL_NATURE_AMPED, // Pokémon reaches the specified level, it has a Hardy, Brave, Adamant, Naughty, Docile, Impish, Lax, Hasty, Jolly, Naive, Rash, Sassy, or Quirky nature.
EVO_LEVEL_NATURE_LOW_KEY, // Pokémon reaches the specified level, it has a Lonely, Bold, Relaxed, Timid, Serious, Modest, Mild, Quiet, Bashful, Calm, Gentle, or Careful nature.
EVO_CRITICAL_HITS, // Pokémon performs specified number of critical hits in one battle
EVO_SCRIPT_TRIGGER_DMG, // Pokémon has specified HP below max, then player interacts trigger
EVO_DARK_SCROLL, // interacts with Scroll of Darkness
EVO_WATER_SCROLL, // interacts with Scroll of Waters
EVO_ITEM_NIGHT, // specified item is used on Pokémon, is night
EVO_ITEM_DAY, // specified item is used on Pokémon, is day
EVO_ITEM_HOLD, // Pokémon levels up, holds specified item
EVO_LEVEL_FOG, // Pokémon reaches the specified level during fog in the overworld
EVO_MOVE_TWO_SEGMENT, // Pokémon levels up, knows specified move, has a personality value with a modulus of 0
EVO_MOVE_THREE_SEGMENT, // Pokémon levels up, knows specified move, has a personality value with a modulus of 1-99
EVO_LEVEL_FAMILY_OF_THREE, // Pokémon reaches the specified level in battle with a personality value with a modulus of 0
EVO_LEVEL_FAMILY_OF_FOUR, // Pokémon reaches the specified level in battle with a personality value with a modulus of 1-99
EVO_USE_MOVE_TWENTY_TIMES, // Pokémon levels up after having used a move for at least 20 times
EVO_RECOIL_DAMAGE_MALE, // Pokémon levels up after having suffered specified amount of non-fainting recoil damage as a male
EVO_RECOIL_DAMAGE_FEMALE, // Pokémon levels up after having suffered specified amount of non-fainting recoil damage as a female
EVO_ITEM_COUNT_999, // Pokémon levels up after trainer has collected 999 of a specific item
EVO_DEFEAT_THREE_WITH_ITEM, // Pokémon levels up after having defeat 3 Pokémon of the same species holding the specified item
EVO_OVERWORLD_STEPS, // Pokémon levels up after having taken a specific amount of steps in the overworld
EVO_NONE, // Not an actual evolution, used to generate offspring that can't evolve into the specified species, like regional forms.
EVO_LEVEL, // Pokémon reaches the specified level
EVO_TRADE, // Pokémon is traded
EVO_ITEM, // specified item is used on Pokémon
EVO_SPLIT_FROM_EVO, // A clone is generated and evolved when another evolution happens
EVO_SCRIPT_TRIGGER, // Player interacts with an overworld trigger
EVO_LEVEL_BATTLE_ONLY, // Pokémon reaches the specified level, in battle only
EVO_BATTLE_END, // Battle ends, doesn't need to level up
};

enum EvolutionMode {
EVO_MODE_NORMAL,
EVO_MODE_CANT_STOP,
EVO_MODE_TRADE,
EVO_MODE_ITEM_USE,
EVO_MODE_ITEM_CHECK, // If an Everstone is being held, still want to show that the stone *could* be used on that Pokémon to evolve
Expand Down
12 changes: 11 additions & 1 deletion include/pokemon.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,11 +343,20 @@ struct BattlePokemon
/*0x5A*/ bool8 isShiny;
};

struct EvolutionParam
{
u16 condition;
u16 arg1;
u16 arg2;
u16 arg3;
};

struct Evolution
{
u16 method;
u16 param;
u16 targetSpecies;
const struct EvolutionParam *params;
};

struct SpeciesInfo /*0xC4*/
Expand Down Expand Up @@ -722,7 +731,8 @@ u8 *UseStatIncreaseItem(u16 itemId);
u8 GetNature(struct Pokemon *mon);
u8 GetNatureFromPersonality(u32 personality);
u32 GetGMaxTargetSpecies(u32 species);
u16 GetEvolutionTargetSpecies(struct Pokemon *mon, enum EvolutionMode mode, u16 evolutionItem, struct Pokemon *tradePartner);
bool32 DoesMonMeetAdditionalConditions(struct Pokemon *mon, const struct EvolutionParam *params, struct Pokemon *tradePartner, u32 partyId, bool32 *canStopEvo);
u32 GetEvolutionTargetSpecies(struct Pokemon *mon, enum EvolutionMode mode, u16 evolutionItem, struct Pokemon *tradePartner, bool32 *canStopEvo);
bool8 IsMonPastEvolutionLevel(struct Pokemon *mon);
u16 NationalPokedexNumToSpecies(u16 nationalNum);
u16 NationalToHoennOrder(u16 nationalNum);
Expand Down
14 changes: 4 additions & 10 deletions src/battle_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -5736,27 +5736,21 @@ static void TryEvolvePokemon(void)
{
if (!(sTriedEvolving & (1u << i)))
{
u16 species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_SPECIAL, i, NULL);
bool32 evoModeNormal = TRUE;
bool32 canStopEvo = TRUE;
u32 species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_SPECIAL, i, NULL, &canStopEvo);
sTriedEvolving |= 1u << i;

if (species == SPECIES_NONE && (gLeveledUpInBattle & (1u << i)))
{
gLeveledUpInBattle &= ~(1u << i);
species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_ONLY, gLeveledUpInBattle, NULL);
}

if (species == SPECIES_NONE)
{
species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_CANT_STOP, ITEM_NONE, NULL);
evoModeNormal = FALSE;
species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_ONLY, gLeveledUpInBattle, NULL, &canStopEvo);
}

if (species != SPECIES_NONE)
{
FreeAllWindowBuffers();
gBattleMainFunc = WaitForEvoSceneToFinish;
EvolutionScene(&gPlayerParty[i], species, evoModeNormal, i);
EvolutionScene(&gPlayerParty[i], species, canStopEvo, i);
return;
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/battle_pyramid.c
Original file line number Diff line number Diff line change
Expand Up @@ -1362,9 +1362,7 @@ static bool32 CheckBattlePyramidEvoRequirement(u16 species, const u16 *evoItems,
for (j = 0; evolutions[j].method != EVOLUTIONS_END; j++)
{
if (evolutions[j].targetSpecies == species
&& (evolutions[j].method == EVO_ITEM
|| evolutions[j].method == EVO_ITEM_MALE
|| evolutions[j].method == EVO_ITEM_FEMALE))
&& (evolutions[j].method == EVO_ITEM))
{
if (nItems == 0)
{
Expand Down
69 changes: 31 additions & 38 deletions src/battle_script_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ static void RemoveAllTerrains(void);
static bool8 CanAbilityPreventStatLoss(u16 abilityDef);
static bool8 CanBurnHitThaw(u16 move);
static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent);
static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 usedMove);
static void TryUpdateEvolutionTracker(u32 evolutionCondition, u32 upAmount, u16 usedMove);
static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr, u16 move);

static void Cmd_attackcanceler(void);
Expand Down Expand Up @@ -2014,7 +2014,7 @@ static void Cmd_critcalc(void)
}
}

// Counter for EVO_CRITICAL_HITS.
// Counter for IF_CRITICAL_HITS_GE evolution condition.
if (gSpecialStatuses[battlerDef].criticalHit && GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER
&& !(gBattleTypeFlags & BATTLE_TYPE_MULTI && GetBattlerPosition(gBattlerAttacker) == B_POSITION_PLAYER_LEFT))
gPartyCriticalHits[partySlot]++;
Expand Down Expand Up @@ -6951,7 +6951,7 @@ static void Cmd_moveend(void)
case MOVEEND_SET_EVOLUTION_TRACKER:
// If the Pokémon needs to keep track of move usage for its evolutions, do it
if (originallyUsedMove != MOVE_NONE)
TryUpdateEvolutionTracker(EVO_USE_MOVE_TWENTY_TIMES, 1, originallyUsedMove);
TryUpdateEvolutionTracker(IF_USED_MOVE_X_TIMES, 1, originallyUsedMove);
gBattleScripting.moveendState++;
break;
case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits.
Expand Down Expand Up @@ -17393,9 +17393,9 @@ void BS_RunStatChangeItems(void)
ItemBattleEffects(ITEMEFFECT_STATS_CHANGED, GetBattlerForBattleScript(cmd->battler), FALSE);
}

static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 usedMove)
static void TryUpdateEvolutionTracker(u32 evolutionCondition, u32 upAmount, u16 usedMove)
{
u32 i;
u32 i, j;

if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER
&& !(gBattleTypeFlags & (BATTLE_TYPE_LINK
Expand All @@ -17412,31 +17412,35 @@ static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 use
{
if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE)
continue;
if (evolutions[i].params == NULL)
continue;

if (evolutions[i].method == evolutionMethod)
for (j = 0; evolutions[i].params[j].condition != CONDITIONS_END; j++)
{
// We only have 10 bits to use
u16 val = min(1023, GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER) + upAmount);
// Reset progress if you faint for the recoil method.
switch (evolutionMethod)
if (evolutions[i].params[j].condition == evolutionCondition)
{
case EVO_USE_MOVE_TWENTY_TIMES:
if (evolutions[i].param == usedMove)
SetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER, &val);
break;
case EVO_RECOIL_DAMAGE_MALE:
case EVO_RECOIL_DAMAGE_FEMALE:
if (gBattleMons[gBattlerAttacker].hp == 0)
val = 0;
SetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER, &val);
break;
case EVO_DEFEAT_THREE_WITH_ITEM:
if (GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_SPECIES) == GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_SPECIES)
&& GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_HELD_ITEM) == evolutions[i].param)
// We only have 10 bits to use
u16 val = min(1023, GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER) + upAmount);
// Reset progress if you faint for the recoil method.
switch (evolutionCondition)
{
case IF_USED_MOVE_X_TIMES:
if (evolutions[i].params[j].arg1 == usedMove)
SetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER, &val);
break;
case IF_RECOIL_DAMAGE_GE:
if (gBattleMons[gBattlerAttacker].hp == 0)
val = 0;
SetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER, &val);
break;
break;
case IF_DEFEAT_X_WITH_ITEMS:
if (GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_SPECIES) == evolutions[i].params[j].arg1
&& GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_HELD_ITEM) == evolutions[i].params[j].arg2)
SetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER, &val);
break;
}
return;
}
return;
}
}
}
Expand All @@ -17445,25 +17449,14 @@ static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 use
void BS_TryUpdateRecoilTracker(void)
{
NATIVE_ARGS();
u8 gender = GetMonGender(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]]);

switch(gender)
{
case MON_MALE:
TryUpdateEvolutionTracker(EVO_RECOIL_DAMAGE_MALE, gBattleStruct->moveDamage[gBattlerAttacker], MOVE_NONE);
break;
case MON_FEMALE:
TryUpdateEvolutionTracker(EVO_RECOIL_DAMAGE_FEMALE, gBattleStruct->moveDamage[gBattlerAttacker], MOVE_NONE);
break;
}

TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->moveDamage[gBattlerAttacker], MOVE_NONE);
gBattlescriptCurrInstr = cmd->nextInstr;
}

void BS_TryUpdateLeadersCrestTracker(void)
{
NATIVE_ARGS();
TryUpdateEvolutionTracker(EVO_DEFEAT_THREE_WITH_ITEM, 1, MOVE_NONE);
TryUpdateEvolutionTracker(IF_DEFEAT_X_WITH_ITEMS, 1, MOVE_NONE);
gBattlescriptCurrInstr = cmd->nextInstr;
}

Expand Down
Loading
Loading