Adds missing evolution methods (#4087)

* Add evolution tracker to BoxMon struct

* Add "use move 20 times, then lv up" evolution method

* Add recoil tracker

* Reduce to 9 bits

* Fix agbcc complaint

* Put MOVEEND_CLEAR_BITS at the end

* Remove battle argument from tryupdaterecoiltrackker

* Add null checks

* Fix upcoming merge

* Add requested formatting changes

* Condense evolution check into a single function for easier customisation later

* Incorporate review requests

* Update src/pokedex_plus_hgss.c

Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>

---------

Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
This commit is contained in:
Bassoonian 2024-02-01 22:35:38 +01:00 committed by GitHub
parent 09d12fb154
commit ccfebe5e05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 125 additions and 8 deletions

View File

@ -1587,6 +1587,10 @@
callnative BS_TryUpperHand
.4byte \failInstr
.endm
.macro tryupdaterecoiltracker
callnative BS_TryUpdateRecoilTracker
.endm
@ various command changed to more readable macros
.macro cancelmultiturnmoves battler:req

View File

@ -7704,6 +7704,7 @@ BattleScript_DoRecoil::
datahpupdate BS_ATTACKER
printstring STRINGID_PKMNHITWITHRECOIL
waitmessage B_WAIT_TIME_LONG
tryupdaterecoiltracker
tryfaintmon BS_ATTACKER
BattleScript_RecoilEnd::
return

View File

@ -316,8 +316,9 @@
#define MOVEEND_SYMBIOSIS 34
#define MOVEEND_OPPORTUNIST 35 // Occurs after other stat change items/abilities to try and copy the boosts
#define MOVEEND_SAME_MOVE_TURNS 36
#define MOVEEND_CLEAR_BITS 37
#define MOVEEND_COUNT 38
#define MOVEEND_SET_EVOLUTION_TRACKER 37
#define MOVEEND_CLEAR_BITS 38
#define MOVEEND_COUNT 39
// switch cases
#define B_SWITCH_NORMAL 0

View File

@ -291,6 +291,9 @@
#define EVO_MOVE_THREE_SEGMENT 44 // Pokémon levels up, knows specified move, has a personality value with a modulus of 1-99
#define EVO_LEVEL_FAMILY_OF_THREE 45 // Pokémon reaches the specified level with a personality value with a modulus of 0
#define EVO_LEVEL_FAMILY_OF_FOUR 46 // Pokémon reaches the specified level with a personality value with a modulus of 1-99
#define EVO_LEVEL_MOVE_TWENTY_TIMES 47 // Pokémon levels up after having used a move for at least 20 times
#define EVO_LEVEL_RECOIL_DAMAGE_MALE 48 // Pokémon levels up after having suffered specified amount of non-fainting recoil damage as a male
#define EVO_LEVEL_RECOIL_DAMAGE_FEMALE 49 // Pokémon levels up after having suffered specified amount of non-fainting recoil damage as a female
// Evolution 'modes,' for GetEvolutionTargetSpecies
#define EVO_MODE_NORMAL 0

View File

@ -113,6 +113,7 @@ enum {
MON_DATA_DYNAMAX_LEVEL,
MON_DATA_GIGANTAMAX_FACTOR,
MON_DATA_TERA_TYPE,
MON_DATA_EVOLUTION_TRACKER,
};
struct PokemonSubstruct0
@ -134,9 +135,10 @@ struct PokemonSubstruct0
struct PokemonSubstruct1
{
u16 move1:11; // 2047 moves.
u16 unused_00:5;
u16 evolutionTracker1:5;
u16 move2:11; // 2047 moves.
u16 unused_02:5;
u16 evolutionTracker2:4;
u16 unused_02:1;
u16 move3:11; // 2047 moves.
u16 unused_04:5;
u16 move4:11; // 2047 moves.

View File

@ -356,6 +356,7 @@ static void RemoveAllTerrains(void);
static bool8 CanAbilityPreventStatLoss(u16 abilityDef, bool8 isIntimidate);
static bool8 CanBurnHitThaw(u16 move);
static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent);
static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount);
static void Cmd_attackcanceler(void);
static void Cmd_accuracycheck(void);
@ -6201,6 +6202,12 @@ static void Cmd_moveend(void)
gBattleStruct->sameMoveTurns[gBattlerAttacker]++;
gBattleScripting.moveendState++;
break;
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_LEVEL_MOVE_TWENTY_TIMES, 1);
gBattleScripting.moveendState++;
break;
case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits.
if (gSpecialStatuses[gBattlerAttacker].instructedChosenTarget)
*(gBattleStruct->moveTarget + gBattlerAttacker) = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3;
@ -16588,3 +16595,45 @@ void BS_SetPhotonGeyserCategory(void)
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL);
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount)
{
u32 i;
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER
&& !(gBattleTypeFlags & (BATTLE_TYPE_LINK
| BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_RECORDED_LINK
| BATTLE_TYPE_TRAINER_HILL
| BATTLE_TYPE_FRONTIER)))
{
const struct Evolution *evolutions = GetSpeciesEvolutions(gBattleMons[gBattlerAttacker].species);
if (evolutions == NULL)
return;
for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++)
{
if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE)
continue;
if (evolutions[i].method == evolutionMethod)
{
// We only have 9 bits to use
u16 val = min(511, GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER) + upAmount);
// Reset progress if you faint for the recoil method.
if (gBattleMons[gBattlerAttacker].hp == 0 && (evolutionMethod == EVO_LEVEL_RECOIL_DAMAGE_MALE || evolutionMethod == EVO_LEVEL_RECOIL_DAMAGE_FEMALE))
val = 0;
SetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER, &val);
return;
}
}
}
}
void BS_TryUpdateRecoilTracker(void)
{
NATIVE_ARGS();
TryUpdateEvolutionTracker(EVO_LEVEL_RECOIL_DAMAGE_MALE, gBattleMoveDamage);
TryUpdateEvolutionTracker(EVO_LEVEL_RECOIL_DAMAGE_FEMALE, gBattleMoveDamage);
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -4508,7 +4508,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
ICON(Primeape, 2),
FOOTPRINT(Primeape)
LEARNSETS(Primeape),
.evolutions = EVOLUTION({EVO_MOVE, MOVE_RAGE_FIST, SPECIES_ANNIHILAPE}),
.evolutions = EVOLUTION({EVO_LEVEL_MOVE_TWENTY_TIMES, MOVE_RAGE_FIST, SPECIES_ANNIHILAPE}),
},
#if P_GEN_9_CROSS_EVOS

View File

@ -5170,7 +5170,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
ICON(Stantler, 2),
FOOTPRINT(Stantler)
LEARNSETS(Stantler),
.evolutions = EVOLUTION({EVO_MOVE, MOVE_PSYSHIELD_BASH, SPECIES_WYRDEER}),
.evolutions = EVOLUTION({EVO_LEVEL_MOVE_TWENTY_TIMES, MOVE_PSYSHIELD_BASH, SPECIES_WYRDEER}),
},
#if P_GEN_8_CROSS_EVOS

View File

@ -3036,8 +3036,8 @@ const struct SpeciesInfo gSpeciesInfoGen5[] =
PALETTES(BasculinWhiteStriped),
ICON(BasculinWhiteStriped, 0),
LEARNSETS(BasculinWhiteStriped),
.evolutions = EVOLUTION({EVO_NONE, 0, SPECIES_BASCULEGION_MALE},
{EVO_NONE, 0, SPECIES_BASCULEGION_FEMALE}),
.evolutions = EVOLUTION({EVO_LEVEL_RECOIL_DAMAGE_MALE, 294, SPECIES_BASCULEGION_MALE},
{EVO_LEVEL_RECOIL_DAMAGE_FEMALE, 294, SPECIES_BASCULEGION_FEMALE}),
},
#define BASCULEGION_MISC_INFO \

View File

@ -244,6 +244,9 @@ static const u8 sText_EVO_WATER_SCROLL[] = _("ScrollOfWatrs is used");
static const u8 sText_EVO_ITEM_NIGHT[] = _("{STR_VAR_2} is used, night");
static const u8 sText_EVO_ITEM_DAY[] = _("{STR_VAR_2} is used, day");
static const u8 sText_EVO_ITEM_HOLD[] = _("{LV}{UP_ARROW}, holds {STR_VAR_2}");
static const u8 sText_EVO_LEVEL_MOVE_TWENTY_TIMES[] = _("{LV}{UP_ARROW} after 20x {STR_VAR_2}");
static const u8 sText_EVO_LEVEL_RECOIL_DAMAGE_MALE[] = _("{LV}{UP_ARROW} with {STR_VAR_2} recoil, male");
static const u8 sText_EVO_LEVEL_RECOIL_DAMAGE_FEMALE[] = _("{LV}{UP_ARROW} with {STR_VAR_2} recoil, female");
static const u8 sText_EVO_UNKNOWN[] = _("Method unknown");
static const u8 sText_EVO_NONE[] = _("{STR_VAR_1} has no evolution.");
@ -6770,6 +6773,18 @@ static u8 PrintEvolutionTargetSpeciesAndMethod(u8 taskId, u16 species, u8 depth,
CopyItemName(item, gStringVar2);
StringExpandPlaceholders(gStringVar4, sText_EVO_ITEM_HOLD );
break;
case EVO_LEVEL_MOVE_TWENTY_TIMES:
StringCopy(gStringVar2, GetMoveName(evolutions[i].param));
StringExpandPlaceholders(gStringVar4, sText_EVO_LEVEL_MOVE_TWENTY_TIMES );
break;
case EVO_LEVEL_RECOIL_DAMAGE_MALE:
ConvertIntToDecimalStringN(gStringVar2, evolutions[i].param, STR_CONV_MODE_LEADING_ZEROS, 3);
StringExpandPlaceholders(gStringVar4, sText_EVO_LEVEL_RECOIL_DAMAGE_MALE);
break;
case EVO_LEVEL_RECOIL_DAMAGE_FEMALE:
ConvertIntToDecimalStringN(gStringVar2, evolutions[i].param, STR_CONV_MODE_LEADING_ZEROS, 3);
StringExpandPlaceholders(gStringVar4, sText_EVO_LEVEL_RECOIL_DAMAGE_FEMALE);
break;
default:
StringExpandPlaceholders(gStringVar4, sText_EVO_UNKNOWN );
break;

View File

@ -2087,6 +2087,19 @@ u32 GetMonData2(struct Pokemon *mon, s32 field)
return GetMonData3(mon, field, NULL);
}
struct EvolutionTrackerBitfield
{
u16 a: 5;
u16 b: 4;
u16 unused: 7;
};
union EvolutionTracker
{
u16 value;
struct EvolutionTrackerBitfield asField;
};
/* GameFreak called GetBoxMonData with either 2 or 3 arguments, for type
* safety we have a GetBoxMonData macro (in include/pokemon.h) which
* dispatches to either GetBoxMonData2 or GetBoxMonData3 based on the
@ -2099,6 +2112,7 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
struct PokemonSubstruct1 *substruct1 = NULL;
struct PokemonSubstruct2 *substruct2 = NULL;
struct PokemonSubstruct3 *substruct3 = NULL;
union EvolutionTracker evoTracker;
// Any field greater than MON_DATA_ENCRYPT_SEPARATOR is encrypted and must be treated as such
if (field > MON_DATA_ENCRYPT_SEPARATOR)
@ -2479,6 +2493,11 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
}
break;
}
case MON_DATA_EVOLUTION_TRACKER:
evoTracker.asField.a = substruct1->evolutionTracker1;
evoTracker.asField.b = substruct1->evolutionTracker2;
retVal = evoTracker.value;
break;
default:
break;
}
@ -2894,6 +2913,16 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg)
substruct0->teraType = 1 + teraType;
break;
}
case MON_DATA_EVOLUTION_TRACKER:
{
union EvolutionTracker evoTracker;
u32 evoTrackerValue;
SET32(evoTrackerValue);
evoTracker.value = evoTrackerValue;
substruct1->evolutionTracker1 = evoTracker.asField.a;
substruct1->evolutionTracker2 = evoTracker.asField.b;
break;
}
default:
break;
}
@ -4047,6 +4076,7 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s
u16 upperPersonality = personality >> 16;
u32 holdEffect, currentMap, partnerSpecies, partnerHeldItem, partnerHoldEffect;
bool32 consumeItem = FALSE;
u16 evolutionTracker = GetMonData(mon, MON_DATA_EVOLUTION_TRACKER, 0);
const struct Evolution *evolutions = GetSpeciesEvolutions(species);
if (evolutions == NULL)
@ -4307,6 +4337,18 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s
consumeItem = TRUE;
}
break;
case EVO_LEVEL_MOVE_TWENTY_TIMES:
if (evolutionTracker >= 20)
targetSpecies = evolutions[i].targetSpecies;
break;
case EVO_LEVEL_RECOIL_DAMAGE_MALE:
if (evolutionTracker >= evolutions[i].param && GetMonGender(mon) == MON_MALE)
targetSpecies = evolutions[i].targetSpecies;
break;
case EVO_LEVEL_RECOIL_DAMAGE_FEMALE:
if (evolutionTracker >= evolutions[i].param && GetMonGender(mon) == MON_FEMALE)
targetSpecies = evolutions[i].targetSpecies;
break;
}
}
break;