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:
parent
efe9f6d8ab
commit
bf8c9609ae
3 changed files with 447 additions and 2 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
237
src/data/battle_frontier/battle_pyramid_wild_requirements.h
Normal file
237
src/data/battle_frontier/battle_pyramid_wild_requirements.h
Normal 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
|
Loading…
Reference in a new issue