add battle pyramid wild reqs (#2581)

* add battle pyramid wild reqs

* fix comment

* fix ice type round to match vanilla. move config to overworld

* some fixes

* BATTLE_PYRAMID_RANDOM_ENCOUNTERS define to FALSE defualt

* fix GetSpeciesName

* fix GetTotalBaseStat define, gEvolutionTable targetSpecies call

* fix wildMons call

* fix call to CheckBattlePyramidEvoRequirement

* fix arg 2 in CheckBattlePyramidEvoRequirement

* free->Free and fix evoItems ptr

* move sBurningMoves

* fix comment

fix ice type round to match vanilla. move config to overworld

some fixes

BATTLE_PYRAMID_RANDOM_ENCOUNTERS define to FALSE defualt

fix GetSpeciesName

fix GetTotalBaseStat define, gEvolutionTable targetSpecies call

fix wildMons call

fix call to CheckBattlePyramidEvoRequirement

fix arg 2 in CheckBattlePyramidEvoRequirement

free->Free and fix evoItems ptr

move sBurningMoves

BATTLE_PYRAMID_RANDOM_ENCOUNTERS defaults to false

* fix errors

* initial movesCount, abilitiesCount

---------

Co-authored-by: ghoulslash <pokevoyager0@gmail.com>
Co-authored-by: Bassoonian <iasperbassoonian@gmail.com>
This commit is contained in:
ghoulslash 2023-11-17 10:04:41 -05:00 committed by GitHub
parent efe9f6d8ab
commit bf8c9609ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 447 additions and 2 deletions

View file

@ -31,4 +31,6 @@
#define OW_FLAG_NO_TRAINER_SEE 0 // If this flag is set, trainers will not battle the player unless they're talked to.
#define OW_FLAG_NO_COLLISION 0 // If this flag is set, the player will be able to walk over tiles with collision. Mainly intended for debugging purposes.
#define BATTLE_PYRAMID_RANDOM_ENCOUNTERS FALSE // If set to TRUE, battle pyramid Pokemon will be generated randomly based on the round's challenge instead of hardcoded in src/data/battle_frontier/battle_pyramid_level_50_wild_mons.h (or open_level_wild_mons.h)
#endif // GUARD_CONFIG_OVERWORLD_H

View file

@ -27,6 +27,7 @@
#include "overworld.h"
#include "event_scripts.h"
#include "graphics.h"
#include "wild_encounter.h"
#include "constants/battle_frontier.h"
#include "constants/battle_pyramid.h"
#include "constants/event_objects.h"
@ -101,8 +102,13 @@ static bool8 TrySetPyramidObjectEventPositionAtCoords(bool8, u8, u8, u8 *, u8, u
// Const rom data.
#define ABILITY_RANDOM 2 // For wild mons data.
#include "data/battle_frontier/battle_pyramid_level_50_wild_mons.h"
#include "data/battle_frontier/battle_pyramid_open_level_wild_mons.h"
#if BATTLE_PYRAMID_RANDOM_ENCOUNTERS == TRUE
#include "data/battle_frontier/battle_pyramid_wild_requirements.h"
#else
#include "data/battle_frontier/battle_pyramid_level_50_wild_mons.h"
#include "data/battle_frontier/battle_pyramid_open_level_wild_mons.h"
#endif
static const struct PyramidFloorTemplate sPyramidFloorTemplates[] =
{
@ -1340,6 +1346,205 @@ static void MarkPyramidTrainerAsBattled(u16 trainerId)
gObjectEvents[gSelectedObjectEvent].initialCoords.y = gObjectEvents[gSelectedObjectEvent].currentCoords.y;
}
#if BATTLE_PYRAMID_RANDOM_ENCOUNTERS == TRUE
// check if given species evolved from a specific evolutionary stone
// if nItems is passed as 0, it will check for any EVO_ITEM case
extern struct Evolution gEvolutionTable[][EVOS_PER_MON];
static bool32 CheckBattlePyramidEvoRequirement(u16 species, const u16 *evoItems, u8 nItems)
{
u32 i, j, k;
for (i = 0; i < NUM_SPECIES; i++)
{
for (j = 0; j < EVOS_PER_MON; j++)
{
if (gEvolutionTable[i][j].targetSpecies == species
&& (gEvolutionTable[i][j].method == EVO_ITEM || gEvolutionTable[i][j].method == EVO_ITEM_MALE || gEvolutionTable[i][j].method == EVO_ITEM_FEMALE))
{
if (nItems == 0)
{
// Any EVO_ITEM case will do
return TRUE;
}
else
{
// Otherwise, need to match specific set provided
for (k = 0; k < nItems; k++) {
if (gEvolutionTable[i][j].param == evoItems[k]) {
return TRUE;
}
}
}
}
}
}
return FALSE;
}
extern u32 GetTotalBaseStat(u32 species);
void GenerateBattlePyramidWildMon(void)
{
u8 name[POKEMON_NAME_LENGTH + 1];
int i, j;
u32 id;
u32 lvl = gSaveBlock2Ptr->frontier.lvlMode;
u16 round = (gSaveBlock2Ptr->frontier.pyramidWinStreaks[lvl] / 7) % TOTAL_PYRAMID_ROUNDS;
const struct BattlePyramidRequirement *reqs = &sBattlePyramidRequirementsByRound[round];
u16 species;
u32 bstLim;
u16 *moves = NULL;
u16 *abilities = NULL;
int moveCount = 0, abilityCount = 0;
if (reqs->nMoves != 0)
moves = AllocZeroed(sizeof(u16) * reqs->nMoves);
if (reqs->nAbilities != 0)
abilities = AllocZeroed(sizeof(u16) * reqs->nAbilities);
if (round >= TOTAL_PYRAMID_ROUNDS)
round = TOTAL_PYRAMID_ROUNDS - 1;
id = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, NULL) - 1; // index in table (0-11) -> higher index is lower probability
bstLim = 450 + (25*round) + (5*id); // higher BST limit for 'rarer' wild mon rolls
while (1)
{
species = Random() % FORMS_START;
// check type
if (reqs->type != TYPE_MYSTERY && gSpeciesInfo[species].types[0] != reqs->type && gSpeciesInfo[species].types[1] != reqs->type)
continue;
// check base stat total
if (GetTotalBaseStat(species) > bstLim)
continue;
// check moves
if (reqs->nMoves != 0)
{
moveCount = 0;
// get list of moves that can be learned
for (i = 0; i < reqs->nMoves; i++)
{
if (CanLearnTeachableMove(species, reqs->moves[i]))
{
moves[moveCount] = reqs->moves[i];
moveCount++;
}
}
if (moveCount == 0)
continue;
}
// check abilities
if (reqs->nAbilities != 0)
{
abilityCount = 0;
// get list of moves that can be learned
for (i = 0; i < reqs->nAbilities; i++)
{
for (j = 0; j < NUM_ABILITY_SLOTS; j++)
{
if (gSpeciesInfo[species].abilities[j] == reqs->abilities[i])
{
abilities[abilityCount] = reqs->abilities[i];
abilityCount++;
break;
}
}
}
if (abilityCount == 0)
continue;
}
// check evos
if (reqs->evoItems[0] != 0 && !CheckBattlePyramidEvoRequirement(species, reqs->evoItems, reqs->nEvoItems))
continue;
// we found a species we can use!
break;
}
// Set species, name
SetMonData(&gEnemyParty[0], MON_DATA_SPECIES, &species);
StringCopy(name, gSpeciesNames[species]);
SetMonData(&gEnemyParty[0], MON_DATA_NICKNAME, &name);
// set level
if (lvl != FRONTIER_LVL_50)
{
lvl = SetFacilityPtrsGetLevel();
lvl -= (5 + (Random() % (TOTAL_PYRAMID_ROUNDS - round)/2));
}
else
{
lvl = 50 - (5 + (Random() % (TOTAL_PYRAMID_ROUNDS - round)/4));
}
SetMonData(&gEnemyParty[0],
MON_DATA_EXP,
&gExperienceTables[gSpeciesInfo[species].growthRate][lvl]);
// Give initial moves and replace one with desired move
GiveBoxMonInitialMoveset(&gEnemyParty[0].box);
if (moves != NULL)
{
// get a random move to give
i = 0;
while (1)
{
id = moves[Random() % moveCount];
if (!MonKnowsMove(&gEnemyParty[0], id))
{
// replace random move
SetMonData(&gEnemyParty[0], MON_DATA_MOVE1 + Random() % MAX_MON_MOVES, &id);
break;
}
i++;
if (i == 20)
break;
}
Free(moves);
}
// Initialize a random ability num
if (gSpeciesInfo[species].abilities[1])
{
i = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY, NULL) % 2;
SetMonData(&gEnemyParty[0], MON_DATA_ABILITY_NUM, &i);
}
else
{
i = 0;
SetMonData(&gEnemyParty[0], MON_DATA_ABILITY_NUM, &i);
}
// Try to replace with desired ability
if (abilities != NULL)
{
i = 0;
while (1)
{
id = abilities[Random() % abilityCount];
for (j = 0; j < NUM_ABILITY_SLOTS; j++)
{
if (id == gSpeciesInfo[species].abilities[j])
{
// Set this ability num
SetMonData(&gEnemyParty[0], MON_DATA_ABILITY_NUM, &id);
}
}
}
Free(abilities);
}
if (gSaveBlock2Ptr->frontier.pyramidWinStreaks[gSaveBlock2Ptr->frontier.lvlMode] >= 140)
{
id = (Random() % 17) + 15;
for (i = 0; i < NUM_STATS; i++)
SetMonData(&gEnemyParty[0], MON_DATA_HP_IV + i, &id);
}
CalculateMonStats(&gEnemyParty[0]);
}
#else
void GenerateBattlePyramidWildMon(void)
{
u8 name[POKEMON_NAME_LENGTH + 1];
@ -1412,6 +1617,7 @@ void GenerateBattlePyramidWildMon(void)
}
CalculateMonStats(&gEnemyParty[0]);
}
#endif
u8 GetPyramidRunMultiplier(void)
{

View file

@ -0,0 +1,237 @@
#if BATTLE_PYRAMID_RANDOM_ENCOUNTERS == TRUE
#include "constants/abilities.h"
#include "constants/battle_move_effects.h"
struct BattlePyramidRequirement {
const u16 *moves; /* use moves instead of effects so we don't need to find moves with said effect in our loop */
u16 abilities[10];
u8 nAbilities;
u8 type;
u8 nMoves;
const u16 *evoItems;
u8 nEvoItems;
};
// EFFECT_PARALYZE, EFFECT_PARALYZE_HIT (30% or more)
static const u16 sParalyzingMoves[] = {
//MOVE_THUNDER_PUNCH,
MOVE_BODY_SLAM,
MOVE_STUN_SPORE,
//MOVE_THUNDER_SHOCK,
//MOVE_THUNDERBOLT,
MOVE_THUNDER_WAVE,
MOVE_LICK,
MOVE_GLARE,
MOVE_ZAP_CANNON,
MOVE_SPARK,
MOVE_DRAGON_BREATH,
MOVE_FORCE_PALM,
MOVE_DISCHARGE,
//MOVE_BOLT_STRIKE,
MOVE_NUZZLE,
MOVE_SPLISHY_SPLASH,
MOVE_BUZZY_BUZZ,
MOVE_COMBAT_TORQUE,
};
// EFFECT_POISON_HIT (30% or more), EFFECT_POISON, EFFECT_POISON_FANG, EFFECT_TOXIC, EFFECT_TOXIC_THREAD
static const u16 sPoisoningMoves[] = {
MOVE_POISON_STING,
//MOVE_TWINEEDLE,
MOVE_SMOG,
MOVE_SLUDGE,
MOVE_SLUDGE_BOMB,
//MOVE_POISON_TAIL,
MOVE_POISON_JAB,
//MOVE_CROSS_POISON,
MOVE_GUNK_SHOT,
//MOVE_SLUDGE_WAVE,
MOVE_NOXIOUS_TORQUE,
//MOVE_ACID,
MOVE_POISON_POWDER,
MOVE_TOXIC,
MOVE_POISON_GAS,
MOVE_POISON_FANG,
MOVE_BANEFUL_BUNKER,
MOVE_TOXIC_THREAD,
};
// EFFECT_BURN_HIT, EFFECT_WILL_O_WISP
static const u16 sBurningMoves[] = {
MOVE_WILL_O_WISP,
//MOVE_EMBER,
//MOVE_FLAMETHROWER,
//MOVE_FIRE_BLAST,
//MOVE_HEAT_WAVE,
//MOVE_BLAZE_KICK,
MOVE_LAVA_PLUME,
MOVE_SCALD,
MOVE_INFERNO,
MOVE_SEARING_SHOT,
MOVE_BLUE_FLARE,
MOVE_STEAM_ERUPTION,
MOVE_SIZZLY_SLIDE,
//MOVE_PYRO_BALL,
MOVE_BURNING_JEALOUSY,
MOVE_SCORCHING_SANDS,
MOVE_SANDSEAR_STORM,
MOVE_BLAZING_TORQUE,
};
// EFFECT_FREEZE, EFFECT_FREEZE_HIT
static const u16 sFrostbiteMoves[] = {
MOVE_ICE_PUNCH,
MOVE_ICE_BEAM,
MOVE_BLIZZARD,
MOVE_POWDER_SNOW,
MOVE_FREEZING_GLARE,
};
// EFFECT_GRUDGE, EFFECT_SPITE, EFFECT_EERIE_SPELL
static const u16 sPPReducingMoves[] = {
MOVE_GRUDGE,
MOVE_SPITE,
MOVE_EERIE_SPELL,
};
// EFFECT_EXPLOSION
static const u16 sExplosionMoves[] = {
MOVE_SELF_DESTRUCT,
MOVE_EXPLOSION,
MOVE_MISTY_EXPLOSION,
};
// EFFECT_RAIN_DANCE, EFFECT_SANDSTORM, EFFECT_HAIL, EFFECT_SUNNY_DAY,
static const u16 sWeatherChangingMoves[] = {
MOVE_RAIN_DANCE,
MOVE_SANDSTORM,
MOVE_HAIL,
MOVE_SUNNY_DAY,
};
// EFFECT_RECHARGE, EFFECT_RECOIL_33
static const u16 sPowerfulNormalMoves[] = {
MOVE_HYPER_BEAM,
MOVE_GIGA_IMPACT,
MOVE_THRASH,
MOVE_BODY_SLAM,
MOVE_DOUBLE_EDGE,
};
static const u16 sEvoItems[] = {ITEM_FIRE_STONE, ITEM_WATER_STONE, ITEM_THUNDER_STONE};
static const struct BattlePyramidRequirement sBattlePyramidRequirementsByRound[] = {
[0] = /* pokemon with moves that paraylze */
{
.type = TYPE_MYSTERY, // no type limitation
.moves = sParalyzingMoves,
.nMoves = NELEMS(sParalyzingMoves),
.abilities = { ABILITY_STATIC },
.nAbilities = 1,
},
[1] = /* pokemon with moves that poison */
{
.type = TYPE_MYSTERY,
.moves = sPoisoningMoves,
.nMoves = NELEMS(sPoisoningMoves),
.abilities = { ABILITY_POISON_POINT },
},
[2] = /* Pokemon with moves that burn */
{
.type = TYPE_MYSTERY,
.moves = sBurningMoves,
.nMoves = NELEMS(sBurningMoves),
.abilities = { ABILITY_FLAME_BODY },
.nAbilities = 1,
},
[3] = /* pokemon with moves that waste PP */
{
.type = TYPE_MYSTERY,
.moves = sPPReducingMoves,
.nMoves = NELEMS(sPPReducingMoves),
.abilities = { ABILITY_PRESSURE },
.nAbilities = 1,
},
[4] = /* pokemon with Levitate */
{
.type = TYPE_MYSTERY,
.abilities = { ABILITY_LEVITATE },
.nAbilities = 1,
},
[5] = /* pokemon with trapping abilities */
{
.type = TYPE_MYSTERY,
.abilities = { ABILITY_SHADOW_TAG, ABILITY_ARENA_TRAP }, // TODO magnet pull?
.nAbilities = 2,
},
[6] = /* ice types */
{
.type = TYPE_ICE,
},
[7] = /* pokemon with explosion effects */
{
.type = TYPE_MYSTERY,
.moves = sExplosionMoves,
.nMoves = NELEMS(sExplosionMoves),
},
[8] = /* psychic types */
{
.type = TYPE_PSYCHIC,
},
[9] = /* rock types */
{
.type = TYPE_ROCK,
},
[10] = /* fighting types */
{
.type = TYPE_FIGHTING,
},
[11] = /* pokemon with weather-altering effects */
{
.type = TYPE_MYSTERY,
.moves = sWeatherChangingMoves,
.nMoves = NELEMS(sWeatherChangingMoves),
.abilities = { ABILITY_SAND_SPIT, ABILITY_DRIZZLE, ABILITY_SNOW_WARNING, ABILITY_DROUGHT, ABILITY_SAND_STREAM },
.nAbilities = 5,
},
[12] = /* bug types */
{
.type = TYPE_BUG,
},
[13] = /* dark types */
{
.type = TYPE_DARK,
},
[14] = /* water types */
{
.type = TYPE_WATER,
},
[15] = /* ghost types */
{
.type = TYPE_GHOST,
},
[16] = /* steel types */
{
.type = TYPE_STEEL,
},
[17] = /* flying/dragon types */
{
.type = TYPE_DRAGON,
},
[18] = /* evolve via water/thunder/fire stone */
{
.type = TYPE_MYSTERY,
.evoItems = sEvoItems,
.nEvoItems = 3,
},
[19] = /* normal with powerful moves */
{
.type = TYPE_NORMAL,
.moves = sPowerfulNormalMoves,
.nMoves = NELEMS(sPowerfulNormalMoves),
},
};
#endif