mirror of
https://github.com/pret/pmd-red.git
synced 2026-04-13 16:47:22 -05:00
875 lines
26 KiB
C
875 lines
26 KiB
C
#include "global.h"
|
|
#include "math.h"
|
|
#include "constants/status.h"
|
|
#include "constants/type.h"
|
|
#include "constants/weather.h"
|
|
#include "dungeon_engine.h"
|
|
#include "structs/str_dungeon.h"
|
|
#include "dungeon_items.h"
|
|
#include "dungeon_map_access.h"
|
|
#include "dungeon_pokemon_attributes.h"
|
|
#include "dungeon_util.h"
|
|
#include "dungeon_visibility.h"
|
|
#include "move_checks.h"
|
|
#include "moves.h"
|
|
#include "number_util.h"
|
|
#include "status.h"
|
|
#include "status_actions.h"
|
|
#include "status_checks_1.h"
|
|
#include "text_util.h"
|
|
#include "tile_types.h"
|
|
#include "trap.h"
|
|
#include "weather.h"
|
|
#include "dungeon_config.h"
|
|
|
|
bool8 CanUseOnSelfWithStatusChecker(Entity *pokemon, Move *move)
|
|
{
|
|
EntityInfo *pokemonInfo = GetEntInfo(pokemon);
|
|
switch (move->id)
|
|
{
|
|
case MOVE_HAIL:
|
|
if (GetApparentWeather(pokemon) == WEATHER_HAIL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_RAGE:
|
|
if (pokemonInfo->bideClassStatus.status == STATUS_ENRAGED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_COUNTER:
|
|
case MOVE_PURSUIT:
|
|
if (pokemonInfo->reflectClassStatus.status == STATUS_COUNTER)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_MIRROR_MOVE:
|
|
if (pokemonInfo->reflectClassStatus.status == STATUS_MIRROR_MOVE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_HOWL:
|
|
case MOVE_MEDITATE:
|
|
case MOVE_SHARPEN:
|
|
if (pokemonInfo->offensiveStages[STAT_STAGE_ATK] >= MAX_STAT_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_BELLY_DRUM:
|
|
if (pokemonInfo->offensiveStages[STAT_STAGE_ATK] >= MAX_STAT_STAGE || FixedPointToInt(pokemonInfo->belly) <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_ACID_ARMOR:
|
|
case MOVE_BARRIER:
|
|
case MOVE_DEFENSE_CURL:
|
|
case MOVE_HARDEN:
|
|
case MOVE_IRON_DEFENSE:
|
|
case MOVE_WITHDRAW:
|
|
if (pokemonInfo->defensiveStages[STAT_STAGE_DEF] >= MAX_STAT_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_BIDE:
|
|
case MOVE_REVENGE:
|
|
if (pokemonInfo->bideClassStatus.status == STATUS_BIDE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_AGILITY:
|
|
{
|
|
u32 r1;
|
|
#ifndef NONMATCHING
|
|
asm("":"=r"(r1));
|
|
#else
|
|
r1 = 0;
|
|
#endif
|
|
if (GetEntInfo(pokemon)->speedStage >= MAX_SPEED_STAGE)
|
|
{
|
|
r1 = !r1;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
case MOVE_LOCK_ON:
|
|
case MOVE_MIND_READER:
|
|
if (pokemonInfo->sureShotClassStatus.status == STATUS_SURE_SHOT)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_COSMIC_POWER:
|
|
if (pokemonInfo->defensiveStages[STAT_STAGE_DEF] >= MAX_STAT_STAGE && pokemonInfo->defensiveStages[STAT_STAGE_SP_DEF] >= MAX_STAT_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_ENDURE:
|
|
if (pokemonInfo->reflectClassStatus.status == STATUS_ENDURING)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_CHARGE:
|
|
if (pokemonInfo->bideClassStatus.status == STATUS_CHARGING)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_MIST:
|
|
if (pokemonInfo->reflectClassStatus.status == STATUS_MIST)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_LIGHT_SCREEN:
|
|
if (pokemonInfo->reflectClassStatus.status == STATUS_LIGHT_SCREEN)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_MINIMIZE:
|
|
if (pokemonInfo->hitChanceStages[STAT_STAGE_EVASION] >= MAX_STAT_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_INGRAIN:
|
|
if (pokemonInfo->frozenClassStatus.status == STATUS_INGRAIN || pokemonInfo->maxHPStat / 2 < pokemonInfo->HP)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SWALLOW:
|
|
if (pokemonInfo->maxHPStat <= pokemonInfo->HP || pokemonInfo->stockpileStage == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SPIT_UP:
|
|
if (pokemonInfo->stockpileStage == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_DOOM_DESIRE:
|
|
case MOVE_FUTURE_SIGHT:
|
|
if (pokemonInfo->sureShotClassStatus.status == STATUS_SET_DAMAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_BULK_UP:
|
|
if (pokemonInfo->offensiveStages[STAT_STAGE_ATK] >= MAX_STAT_STAGE && pokemonInfo->defensiveStages[STAT_STAGE_DEF] >= MAX_STAT_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_CAMOUFLAGE:
|
|
if (MonsterIsType(pokemon, gDungeonCamouflageTypes[gDungeon->tileset]))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_TAIL_GLOW:
|
|
if (pokemonInfo->offensiveStages[STAT_STAGE_SP_ATK] >= MAX_STAT_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_DESTINY_BOND:
|
|
if (pokemonInfo->leechSeedClassStatus.status == STATUS_DESTINY_BOND)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_MIRROR_COAT:
|
|
if (pokemonInfo->reflectClassStatus.status == STATUS_MIRROR_COAT)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_REFLECT:
|
|
if (pokemonInfo->reflectClassStatus.status == STATUS_REFLECT)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_DRAGON_DANCE:
|
|
if (pokemonInfo->offensiveStages[STAT_STAGE_ATK] >= MAX_STAT_STAGE && GetEntInfo(pokemon)->speedStage >= MAX_SPEED_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_MAGIC_COAT:
|
|
if (pokemonInfo->reflectClassStatus.status == STATUS_MAGIC_COAT)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_DETECT:
|
|
case MOVE_PROTECT:
|
|
if (pokemonInfo->reflectClassStatus.status == STATUS_PROTECT)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_RAIN_DANCE:
|
|
if (GetApparentWeather(pokemon) == WEATHER_RAIN)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SANDSTORM:
|
|
if (GetApparentWeather(pokemon) == WEATHER_SANDSTORM)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SUNNY_DAY:
|
|
if (GetApparentWeather(pokemon) == WEATHER_SUNNY)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SAFEGUARD:
|
|
if (pokemonInfo->reflectClassStatus.status == STATUS_SAFEGUARD)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_INVISIFY:
|
|
if (pokemonInfo->invisibleClassStatus.status == STATUS_INVISIBLE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_FOCUS_ENERGY:
|
|
if (pokemonInfo->sureShotClassStatus.status == STATUS_FOCUS_ENERGY)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_TAKEAWAY:
|
|
if (pokemonInfo->heldItem.flags & ITEM_FLAG_EXISTS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_REST:
|
|
if (!HasLowHealth(pokemon) && !HasNegativeStatus(pokemon))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_DIVE:
|
|
if (IsTileGround(GetTileAtEntitySafe(pokemon)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_DIG:
|
|
{
|
|
Tile *tile = GetTileAtEntitySafe(pokemon);
|
|
if (!IsTileGround(tile) || (tile->terrainType & (TERRAIN_TYPE_NORMAL | TERRAIN_TYPE_SECONDARY)) != TERRAIN_TYPE_NORMAL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
case MOVE_TRAP_BUSTER:
|
|
{
|
|
Entity *object = GetTileAtEntitySafe(pokemon)->object;
|
|
if (object == NULL || GetEntityType(object) != ENTITY_TRAP)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
case MOVE_MUD_SPORT:
|
|
if (gDungeon->weather.mudSportTurns > 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_WATER_SPORT:
|
|
if (gDungeon->weather.waterSportTurns > 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_GRUDGE:
|
|
if (pokemonInfo->grudge)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_DECOY_MAKER:
|
|
case MOVE_FOLLOW_ME:
|
|
case MOVE_SUBSTITUTE:
|
|
if (gDungeon->decoyIsActive)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_STOCKPILE:
|
|
if (pokemonInfo->stockpileStage >= MAX_STOCKPILE_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_CLEANSE:
|
|
if (pokemonInfo->heldItem.flags & ITEM_FLAG_EXISTS &&
|
|
!(pokemonInfo->heldItem.flags & ITEM_FLAG_STICKY))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_DOUBLE_TEAM:
|
|
if (pokemonInfo->hitChanceStages[STAT_STAGE_EVASION] >= MAX_STAT_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_GROWTH:
|
|
if (pokemonInfo->offensiveStages[STAT_STAGE_SP_ATK] >= MAX_STAT_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SWORDS_DANCE:
|
|
if (pokemonInfo->offensiveStages[STAT_STAGE_ATK] >= MAX_STAT_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_WISH:
|
|
if (pokemonInfo->isNotTeamMember || pokemonInfo->reflectClassStatus.status == STATUS_WISH)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_TRANSFORM:
|
|
if (pokemonInfo->invisibleClassStatus.status == STATUS_TRANSFORMED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SPIKES:
|
|
if (!CanLayTrap(&pokemon->pos))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_CALM_MIND:
|
|
if (pokemonInfo->offensiveStages[STAT_STAGE_SP_ATK] < MAX_STAT_STAGE)
|
|
{
|
|
break;
|
|
}
|
|
case MOVE_AMNESIA:
|
|
if (pokemonInfo->defensiveStages[STAT_STAGE_SP_DEF] >= MAX_STAT_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SNATCH:
|
|
if (pokemonInfo->curseClassStatus.status == STATUS_SNATCH)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_BEAT_UP:
|
|
case MOVE_BLOWBACK:
|
|
case MOVE_HURL:
|
|
case MOVE_MEMENTO:
|
|
case MOVE_ROAR:
|
|
case MOVE_STAY_AWAY:
|
|
case MOVE_SWITCHER:
|
|
case MOVE_TELEPORT:
|
|
case MOVE_VITAL_THROW:
|
|
case MOVE_WARP:
|
|
case MOVE_WHIRLWIND:
|
|
if (IsBossFight())
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_CONVERSION_2:
|
|
if (pokemonInfo->reflectClassStatus.status == STATUS_CONVERSION2)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_HELPING_HAND:
|
|
if (pokemonInfo->isNotTeamMember)
|
|
{
|
|
s32 i;
|
|
for (i = 0; i < DUNGEON_MAX_WILD_POKEMON; i++)
|
|
{
|
|
Entity *target = gDungeon->wildPokemon[i];
|
|
if (EntityExists(target) && target != pokemon && CanSeeTarget(pokemon, target))
|
|
{
|
|
if (GetEntInfo(target)->offensiveStages[STAT_STAGE_ATK] >= MAX_STAT_STAGE)
|
|
{
|
|
continue;
|
|
}
|
|
if (GetEntInfo(target)->offensiveStages[STAT_STAGE_SP_ATK] < MAX_STAT_STAGE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (i == DUNGEON_MAX_WILD_POKEMON)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
s32 i;
|
|
for (i = 0; i < MAX_TEAM_MEMBERS; i++)
|
|
{
|
|
Entity *target = gDungeon->teamPokemon[i];
|
|
if (EntityExists(target) && target != pokemon && CanSeeTarget(pokemon, target))
|
|
{
|
|
if (GetEntInfo(target)->offensiveStages[STAT_STAGE_ATK] >= MAX_STAT_STAGE)
|
|
{
|
|
continue;
|
|
}
|
|
if (GetEntInfo(target)->offensiveStages[STAT_STAGE_SP_ATK] < MAX_STAT_STAGE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (i == MAX_TEAM_MEMBERS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
bool8 CanUseOnTargetWithStatusChecker(Entity *user, Entity *target, Move *move)
|
|
{
|
|
EntityInfo *userData = GetEntInfo(user);
|
|
EntityInfo *targetData = GetEntInfo(target);
|
|
s32 i;
|
|
if (targetData->frozenClassStatus.status == STATUS_FROZEN && MoveCannotHitFrozen(move))
|
|
{
|
|
return FALSE;
|
|
}
|
|
switch (move->id)
|
|
{
|
|
case MOVE_GRASSWHISTLE:
|
|
case MOVE_HYPNOSIS:
|
|
case MOVE_LOVELY_KISS:
|
|
case MOVE_SING:
|
|
case MOVE_SLEEP_POWDER:
|
|
case MOVE_SPORE:
|
|
if (IsSleeping(target))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_YAWN:
|
|
if (targetData->sleepClassStatus.status == STATUS_YAWNING)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (IsSleeping(target))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_NIGHTMARE:
|
|
if (targetData->sleepClassStatus.status == STATUS_NIGHTMARE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SWEET_SCENT:
|
|
if (targetData->hitChanceStages[STAT_STAGE_EVASION] <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_CHARM:
|
|
if (F248LessThanFloat(targetData->offensiveMultipliers[STAT_STAGE_ATK], STAT_MULTIPLIER_THRESHOLD))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_ENCORE:
|
|
if (targetData->cringeClassStatus.status == STATUS_ENCORE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (!HasLastUsedMove(targetData->moves.moves))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SUPER_FANG:
|
|
if (targetData->HP <= 1)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_PAIN_SPLIT:
|
|
if (targetData->HP <= userData->HP)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_TORMENT:
|
|
if (HasDisabledMove(targetData->moves.moves) || !HasLastUsedMove(targetData->moves.moves))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_COTTON_SPORE:
|
|
case MOVE_SCARY_FACE:
|
|
case MOVE_STRING_SHOT:
|
|
if (GetEntInfo(target)->speedStage <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SCREECH:
|
|
if (F248LessThanFloat(targetData->defensiveMultipliers[STAT_STAGE_DEF], STAT_MULTIPLIER_THRESHOLD))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_FAKE_TEARS:
|
|
if (targetData->defensiveStages[STAT_STAGE_SP_DEF] <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SPITE:
|
|
if (LastUsedMoveOutOfPP(targetData->moves.moves))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_SMOKESCREEN:
|
|
if (targetData->sureShotClassStatus.status == STATUS_WHIFFER)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_MEMENTO:
|
|
if (F248LessThanFloat(targetData->offensiveMultipliers[STAT_STAGE_ATK], STAT_MULTIPLIER_THRESHOLD) &&
|
|
F248LessThanFloat(targetData->offensiveMultipliers[STAT_STAGE_SP_ATK], STAT_MULTIPLIER_THRESHOLD))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_WILL_O_WISP:
|
|
if (targetData->burnClassStatus.status == STATUS_BURN)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_FORESIGHT:
|
|
case MOVE_ODOR_SLEUTH:
|
|
if (targetData->types[0] == TYPE_GHOST || targetData->types[1] == TYPE_GHOST)
|
|
{
|
|
if (!targetData->exposed)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (targetData->hitChanceStages[STAT_STAGE_EVASION] <= DEFAULT_STAT_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_DISABLE:
|
|
case MOVE_GLARE:
|
|
case MOVE_STUN_SPORE:
|
|
case MOVE_THUNDER_WAVE:
|
|
if (targetData->burnClassStatus.status == STATUS_PARALYSIS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_ENDEAVOR:
|
|
if (targetData->HP - userData->HP <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_LEER:
|
|
case MOVE_TAIL_WHIP:
|
|
if (targetData->defensiveStages[STAT_STAGE_DEF] <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_METAL_SOUND:
|
|
if (targetData->defensiveStages[STAT_STAGE_SP_DEF] <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_TICKLE:
|
|
if (targetData->offensiveStages[STAT_STAGE_ATK] <= 0 &&
|
|
targetData->defensiveStages[STAT_STAGE_DEF] <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_BLOCK:
|
|
case MOVE_MEAN_LOOK:
|
|
case MOVE_SPIDER_WEB:
|
|
if (targetData->frozenClassStatus.status == STATUS_SHADOW_HOLD)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_HAZE:
|
|
{
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
if (targetData->offensiveStages[i] < DEFAULT_STAT_STAGE) break;
|
|
if (targetData->defensiveStages[i] < DEFAULT_STAT_STAGE) break;
|
|
if (targetData->hitChanceStages[i] < DEFAULT_STAT_STAGE ||
|
|
F248LessThanInt(targetData->offensiveMultipliers[i], 1) ||
|
|
F248LessThanInt(targetData->defensiveMultipliers[i], 1))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i == 2)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
case MOVE_UPROAR:
|
|
if (targetData->sleepClassStatus.status == STATUS_SLEEPLESS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_PSYCH_UP:
|
|
{
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
if (userData->offensiveStages[i] < targetData->offensiveStages[i]) break;
|
|
if (userData->defensiveStages[i] < targetData->defensiveStages[i] ||
|
|
userData->hitChanceStages[i] < targetData->hitChanceStages[i] ||
|
|
userData->offensiveMultipliers[i].raw < targetData->offensiveMultipliers[i].raw ||
|
|
userData->defensiveMultipliers[i].raw < targetData->defensiveMultipliers[i].raw)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i == 2)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
case MOVE_FLASH:
|
|
case MOVE_KINESIS:
|
|
case MOVE_SAND_ATTACK:
|
|
if (targetData->hitChanceStages[STAT_STAGE_ACCURACY] <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_TAUNT:
|
|
if (targetData->cringeClassStatus.status == STATUS_TAUNTED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_TRICK:
|
|
if (!(userData->heldItem.flags & ITEM_FLAG_EXISTS) ||
|
|
!(targetData->heldItem.flags & ITEM_FLAG_EXISTS))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_KNOCK_OFF:
|
|
if (!(targetData->heldItem.flags & ITEM_FLAG_EXISTS))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_FEATHERDANCE:
|
|
case MOVE_GROWL:
|
|
if (targetData->offensiveStages[STAT_STAGE_ATK] <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_ROLE_PLAY:
|
|
if (userData->abilities[0] == targetData->abilities[0] &&
|
|
userData->abilities[1] == targetData->abilities[1])
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_CURSE:
|
|
if (MonsterIsType(user, TYPE_GHOST))
|
|
{
|
|
if (targetData->curseClassStatus.status == STATUS_CURSED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (userData->offensiveStages[STAT_STAGE_ATK] >= MAX_STAT_STAGE &&
|
|
userData->defensiveStages[STAT_STAGE_DEF] >= MAX_STAT_STAGE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case MOVE_IMPRISON:
|
|
case MOVE_OBSERVER:
|
|
if (targetData->cringeClassStatus.status == STATUS_PAUSED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_FALSE_SWIPE:
|
|
if (targetData->HP <= 1)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_LEECH_SEED:
|
|
if (targetData->leechSeedClassStatus.status == STATUS_LEECH_SEED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_PERISH_SONG:
|
|
if (targetData->perishSongTurns != 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_MIMIC:
|
|
case MOVE_SKETCH:
|
|
if (!HasLastUsedMove(targetData->moves.moves))
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_ATTRACT:
|
|
if (targetData->cringeClassStatus.status == STATUS_INFATUATED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_WRAP:
|
|
if (targetData->frozenClassStatus.status == STATUS_WRAP)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (targetData->frozenClassStatus.status == STATUS_WRAPPED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_TOXIC:
|
|
if (targetData->burnClassStatus.status == STATUS_BADLY_POISONED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_POISON_GAS:
|
|
case MOVE_POISONPOWDER:
|
|
if (targetData->burnClassStatus.status == STATUS_POISONED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (targetData->burnClassStatus.status == STATUS_BADLY_POISONED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case MOVE_CONFUSE_RAY:
|
|
case MOVE_FLATTER:
|
|
case MOVE_SUPERSONIC:
|
|
case MOVE_SWAGGER:
|
|
case MOVE_SWEET_KISS:
|
|
case MOVE_TEETER_DANCE:
|
|
case MOVE_TOTTER:
|
|
if (targetData->cringeClassStatus.status == STATUS_CONFUSED)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
bool8 HasDisabledMove(Move *moves)
|
|
{
|
|
s32 i;
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
{
|
|
if (moves[i].moveFlags & MOVE_FLAG_EXISTS && moves[i].moveFlags & MOVE_FLAG_DISABLED)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
if (moves[STRUGGLE_MOVE_INDEX].moveFlags & MOVE_FLAG_DISABLED)
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool8 LastUsedMoveOutOfPP(Move *moves)
|
|
{
|
|
s32 i;
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
{
|
|
if (moves[i].moveFlags & MOVE_FLAG_EXISTS &&
|
|
moves[i].moveFlags & MOVE_FLAG_LAST_USED &&
|
|
moves[i].PP == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool8 HasLastUsedMove(Move *moves)
|
|
{
|
|
s32 i;
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
{
|
|
if (moves[i].moveFlags & MOVE_FLAG_EXISTS && moves[i].moveFlags & MOVE_FLAG_LAST_USED)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
if (moves[STRUGGLE_MOVE_INDEX].moveFlags & MOVE_FLAG_LAST_USED)
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|