mirror of
https://github.com/pret/pmd-red.git
synced 2026-04-24 15:07:09 -05:00
571 lines
18 KiB
C
571 lines
18 KiB
C
#include "global.h"
|
|
#include "dungeon_ai_targeting.h"
|
|
|
|
#include "constants/ability.h"
|
|
#include "constants/dungeon.h"
|
|
#include "constants/iq_skill.h"
|
|
#include "constants/item.h"
|
|
#include "constants/status.h"
|
|
#include "constants/tactic.h"
|
|
#include "constants/targeting.h"
|
|
|
|
#include "dungeon_engine.h"
|
|
#include "dungeon_global_data.h"
|
|
#include "dungeon_items.h"
|
|
#include "dungeon_map_access.h"
|
|
#include "dungeon_movement.h"
|
|
#include "dungeon_pokemon_attributes.h"
|
|
#include "dungeon_random.h"
|
|
#include "dungeon_util.h"
|
|
#include "map.h"
|
|
#include "tile_types.h"
|
|
|
|
extern void ShowVisualFlags(struct Entity *r0);
|
|
|
|
const u8 gDirectionBitMasks_2[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
|
|
const u8 gDirectionBitMasks_3[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
|
|
const u8 gDirectionBitMasks_4[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
|
|
const u8 gDirectionBitMasks_5[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
|
|
const u8 gDirectionBitMasks_6[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
|
|
const u8 gDirectionBitMasks_7[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
|
|
|
|
const u8 gTargetingData[3][2][2][2] = {
|
|
{
|
|
{
|
|
{TARGET_CAPABILITY_CANNOT_ATTACK, TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET},
|
|
{TARGET_CAPABILITY_CAN_TARGET, TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET}
|
|
},
|
|
{
|
|
{TARGET_CAPABILITY_CAN_TARGET, TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET},
|
|
{TARGET_CAPABILITY_CANNOT_ATTACK, TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET}
|
|
}
|
|
},
|
|
{
|
|
{
|
|
{TARGET_CAPABILITY_CANNOT_ATTACK, TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET},
|
|
{TARGET_CAPABILITY_CAN_TARGET, TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET}
|
|
},
|
|
{
|
|
{TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET, TARGET_CAPABILITY_CAN_TARGET},
|
|
{TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET, TARGET_CAPABILITY_CAN_TARGET}
|
|
}
|
|
},
|
|
{
|
|
{
|
|
{TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET, TARGET_CAPABILITY_CAN_TARGET},
|
|
{TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET, TARGET_CAPABILITY_CAN_TARGET}
|
|
},
|
|
{
|
|
{TARGET_CAPABILITY_CAN_TARGET, TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET},
|
|
{TARGET_CAPABILITY_CANNOT_ATTACK, TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET}
|
|
}
|
|
}
|
|
};
|
|
|
|
bool8 sub_8070F3C(struct Entity * pokemon, struct Position *pos, s32 direction)
|
|
{
|
|
u8 terrain;
|
|
struct Tile *tile;
|
|
|
|
terrain = GetCrossableTerrain(pokemon->info->id);
|
|
|
|
tile = GetTile(pos->x + gAdjacentTileOffsets[direction].x, pos->y + gAdjacentTileOffsets[direction].y);
|
|
if ((!(tile->terrainType & TERRAIN_TYPE_IMPASSABLE_WALL)) &&
|
|
(((tile->monster == NULL || (GetEntityType(tile->monster) == ENTITY_MONSTER))))) {
|
|
if (!IsCurrentFixedRoomBossFight())
|
|
{
|
|
if (pokemon->info->transformStatus == STATUS_MOBILE ||
|
|
HasHeldItem(pokemon, ITEM_MOBILE_SCARF))
|
|
{
|
|
terrain = CROSSABLE_TERRAIN_WALL;
|
|
}
|
|
else if (IQSkillIsEnabled(pokemon, IQ_ALL_TERRAIN_HIKER))
|
|
{
|
|
// BUG: If the Pokémon is a Ghost type that can normally attack through walls,
|
|
// All-Terrain Hiker/Super Mobile may make the AI think it can't attack through walls.
|
|
terrain = CROSSABLE_TERRAIN_CREVICE;
|
|
}
|
|
else if (IQSkillIsEnabled(pokemon, IQ_SUPER_MOBILE))
|
|
{
|
|
if ((direction & 1) != 0)
|
|
{
|
|
terrain = CROSSABLE_TERRAIN_CREVICE;
|
|
}
|
|
else
|
|
{
|
|
terrain = CROSSABLE_TERRAIN_WALL;
|
|
}
|
|
}
|
|
}
|
|
|
|
tile = GetTile(pos->x, pos->y);
|
|
|
|
if (((tile->walkableNeighborFlags[terrain] & gDirectionBitMasks_2[direction & DIRECTION_MASK]))) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool8 sub_8070F14(struct Entity * pokemon, s32 direction)
|
|
{
|
|
struct Tile *tile;
|
|
|
|
|
|
tile = GetTile(pokemon->pos.x + gAdjacentTileOffsets[direction].x, pokemon->pos.y + gAdjacentTileOffsets[direction].y);
|
|
if ((!(tile->terrainType & TERRAIN_TYPE_IMPASSABLE_WALL)) &&
|
|
(((tile->monster == NULL)))) {
|
|
|
|
tile = GetTile(pokemon->pos.x, pokemon->pos.y);
|
|
|
|
if (((tile->walkableNeighborFlags[0] & gDirectionBitMasks_3[direction & DIRECTION_MASK]))) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool8 sub_8070F80(struct Entity * pokemon, s32 direction)
|
|
{
|
|
u8 terrain;
|
|
struct Tile *tile;
|
|
|
|
terrain = GetCrossableTerrain(pokemon->info->id);
|
|
|
|
tile = GetTile(pokemon->pos.x + gAdjacentTileOffsets[direction].x, pokemon->pos.y + gAdjacentTileOffsets[direction].y);
|
|
if ((!(tile->terrainType & TERRAIN_TYPE_IMPASSABLE_WALL)) &&
|
|
(((tile->monster == NULL || (GetEntityType(tile->monster) == ENTITY_MONSTER))))) {
|
|
if (!IsCurrentFixedRoomBossFight())
|
|
{
|
|
if (pokemon->info->transformStatus == STATUS_MOBILE ||
|
|
HasHeldItem(pokemon, ITEM_MOBILE_SCARF))
|
|
{
|
|
terrain = CROSSABLE_TERRAIN_WALL;
|
|
}
|
|
else if (IQSkillIsEnabled(pokemon, IQ_ALL_TERRAIN_HIKER))
|
|
{
|
|
// BUG: If the Pokémon is a Ghost type that can normally attack through walls,
|
|
// All-Terrain Hiker/Super Mobile may make the AI think it can't attack through walls.
|
|
terrain = CROSSABLE_TERRAIN_CREVICE;
|
|
}
|
|
else if (IQSkillIsEnabled(pokemon, IQ_SUPER_MOBILE))
|
|
{
|
|
if ((direction & 1) != 0)
|
|
{
|
|
terrain = CROSSABLE_TERRAIN_CREVICE;
|
|
}
|
|
else
|
|
{
|
|
terrain = CROSSABLE_TERRAIN_WALL;
|
|
}
|
|
}
|
|
}
|
|
|
|
tile = GetTile(pokemon->pos.x, pokemon->pos.y);
|
|
|
|
if (((tile->walkableNeighborFlags[terrain] & gDirectionBitMasks_4[direction & DIRECTION_MASK]))) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool8 sub_8071058(struct Entity * pokemon, s32 direction)
|
|
{
|
|
u8 terrain;
|
|
struct Tile *tile;
|
|
|
|
terrain = GetCrossableTerrain(pokemon->info->id);
|
|
|
|
tile = GetTile(pokemon->pos.x + gAdjacentTileOffsets[direction].x, pokemon->pos.y + gAdjacentTileOffsets[direction].y);
|
|
if ((!(tile->terrainType & TERRAIN_TYPE_IMPASSABLE_WALL)) &&
|
|
(((tile->monster == NULL || (GetEntityType(tile->monster) == ENTITY_MONSTER)) ||
|
|
(!tile->monster->info->isNotTeamMember)))) {
|
|
if (!IsCurrentFixedRoomBossFight())
|
|
{
|
|
if (pokemon->info->transformStatus == STATUS_MOBILE ||
|
|
HasHeldItem(pokemon, ITEM_MOBILE_SCARF))
|
|
{
|
|
terrain = CROSSABLE_TERRAIN_WALL;
|
|
}
|
|
else if (IQSkillIsEnabled(pokemon, IQ_ALL_TERRAIN_HIKER))
|
|
{
|
|
// BUG: If the Pokémon is a Ghost type that can normally attack through walls,
|
|
// All-Terrain Hiker/Super Mobile may make the AI think it can't attack through walls.
|
|
terrain = CROSSABLE_TERRAIN_CREVICE;
|
|
}
|
|
else if (IQSkillIsEnabled(pokemon, IQ_SUPER_MOBILE))
|
|
{
|
|
if ((direction & 1) != 0)
|
|
{
|
|
terrain = CROSSABLE_TERRAIN_CREVICE;
|
|
}
|
|
else
|
|
{
|
|
terrain = CROSSABLE_TERRAIN_WALL;
|
|
}
|
|
}
|
|
}
|
|
tile = GetTile(pokemon->pos.x, pokemon->pos.y);
|
|
|
|
if (((tile->walkableNeighborFlags[terrain] & gDirectionBitMasks_5[direction & DIRECTION_MASK]))) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool8 CanAttackInDirection(struct Entity *pokemon, s32 direction)
|
|
{
|
|
u8 crossableTerrain = GetCrossableTerrain(pokemon->info->id);
|
|
struct Tile *tile;
|
|
if (crossableTerrain < CROSSABLE_TERRAIN_CREVICE)
|
|
{
|
|
crossableTerrain = CROSSABLE_TERRAIN_CREVICE;
|
|
}
|
|
tile = GetTile(pokemon->pos.x + gAdjacentTileOffsets[direction].x,
|
|
pokemon->pos.y + gAdjacentTileOffsets[direction].y);
|
|
if (!(tile->terrainType & TERRAIN_TYPE_IMPASSABLE_WALL) &&
|
|
(tile->monster == NULL || GetEntityType(tile->monster) == ENTITY_MONSTER))
|
|
{
|
|
if (!IsCurrentFixedRoomBossFight())
|
|
{
|
|
if (pokemon->info->transformStatus == STATUS_MOBILE ||
|
|
HasHeldItem(pokemon, ITEM_MOBILE_SCARF))
|
|
{
|
|
crossableTerrain = CROSSABLE_TERRAIN_WALL;
|
|
}
|
|
else if (IQSkillIsEnabled(pokemon, IQ_ALL_TERRAIN_HIKER))
|
|
{
|
|
// BUG: If the Pokémon is a Ghost type that can normally attack through walls,
|
|
// All-Terrain Hiker/Super Mobile may make the AI think it can't attack through walls.
|
|
crossableTerrain = CROSSABLE_TERRAIN_CREVICE;
|
|
}
|
|
else if (IQSkillIsEnabled(pokemon, IQ_SUPER_MOBILE))
|
|
{
|
|
if ((direction & 1) != 0)
|
|
{
|
|
crossableTerrain = CROSSABLE_TERRAIN_CREVICE;
|
|
}
|
|
else
|
|
{
|
|
crossableTerrain = CROSSABLE_TERRAIN_WALL;
|
|
}
|
|
}
|
|
}
|
|
tile = GetTile(pokemon->pos.x, pokemon->pos.y);
|
|
if (tile->walkableNeighborFlags[crossableTerrain] & gDirectionBitMasks_6[direction & DIRECTION_MASK])
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool8 CanAIMonsterMoveInDirection(struct Entity *pokemon, s32 direction, bool8 *pokemonInFront)
|
|
{
|
|
u8 crossableTerrain = GetCrossableTerrain(pokemon->info->id);
|
|
struct Tile *frontTile, *currentTile;
|
|
*pokemonInFront = FALSE;
|
|
frontTile = GetTile(pokemon->pos.x + gAdjacentTileOffsets[direction].x,
|
|
pokemon->pos.y + gAdjacentTileOffsets[direction].y);
|
|
if (frontTile->terrainType & TERRAIN_TYPE_IMPASSABLE_WALL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (frontTile->terrainType & TERRAIN_TYPE_IN_MONSTER_HOUSE &&
|
|
!gDungeon->monsterHouseTriggered &&
|
|
IQSkillIsEnabled(pokemon, IQ_HOUSE_AVOIDER))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (frontTile->object != NULL &&
|
|
IQSkillIsEnabled(pokemon, IQ_TRAP_AVOIDER) &&
|
|
GetEntityType(frontTile->object) == ENTITY_TRAP &&
|
|
(frontTile->object->isVisible || pokemon->info->eyesightStatus == STATUS_EYEDROPS))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if ((frontTile->terrainType & (TERRAIN_TYPE_NORMAL | TERRAIN_TYPE_SECONDARY)) == TERRAIN_TYPE_SECONDARY &&
|
|
gDungeonWaterType[gDungeon->tileset] == DUNGEON_WATER_TYPE_LAVA &&
|
|
IQSkillIsEnabled(pokemon, IQ_LAVA_EVADER))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (!IsCurrentFixedRoomBossFight())
|
|
{
|
|
if (pokemon->info->transformStatus == STATUS_MOBILE ||
|
|
HasHeldItem(pokemon, ITEM_MOBILE_SCARF))
|
|
{
|
|
crossableTerrain = CROSSABLE_TERRAIN_WALL;
|
|
}
|
|
else if (IQSkillIsEnabled(pokemon, IQ_ALL_TERRAIN_HIKER))
|
|
{
|
|
// BUG: If the Pokémon is a Ghost type that can normally move through walls,
|
|
// All-Terrain Hiker/Super Mobile may make the AI think it can't move through walls.
|
|
crossableTerrain = CROSSABLE_TERRAIN_CREVICE;
|
|
}
|
|
else if (IQSkillIsEnabled(pokemon, IQ_SUPER_MOBILE))
|
|
{
|
|
if ((direction & 1) != 0)
|
|
{
|
|
crossableTerrain = CROSSABLE_TERRAIN_CREVICE;
|
|
}
|
|
else
|
|
{
|
|
crossableTerrain = CROSSABLE_TERRAIN_WALL;
|
|
}
|
|
}
|
|
}
|
|
currentTile = GetTile(pokemon->pos.x, pokemon->pos.y);
|
|
if (currentTile->walkableNeighborFlags[crossableTerrain] & gDirectionBitMasks_7[direction & DIRECTION_MASK])
|
|
{
|
|
if (frontTile->monster == NULL)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pokemonInFront = TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool8 IsAtJunction(struct Entity *pokemon)
|
|
{
|
|
u32 crossableTerrain = GetCrossableTerrain(pokemon->info->id);
|
|
if (!IsCurrentFixedRoomBossFight())
|
|
{
|
|
if (pokemon->info->transformStatus == STATUS_MOBILE || HasHeldItem(pokemon, ITEM_MOBILE_SCARF))
|
|
{
|
|
crossableTerrain = CROSSABLE_TERRAIN_WALL;
|
|
}
|
|
else if (IQSkillIsEnabled(pokemon, IQ_ALL_TERRAIN_HIKER))
|
|
{
|
|
crossableTerrain = CROSSABLE_TERRAIN_CREVICE;
|
|
}
|
|
else if (IQSkillIsEnabled(pokemon, IQ_SUPER_MOBILE))
|
|
{
|
|
crossableTerrain = CROSSABLE_TERRAIN_WALL;
|
|
}
|
|
}
|
|
if (crossableTerrain == CROSSABLE_TERRAIN_WALL)
|
|
{
|
|
struct EntityInfo *pokemonInfo = pokemon->info;
|
|
pokemonInfo->mobileTurnTimer += DungeonRandInt(100);
|
|
if (pokemonInfo->mobileTurnTimer < 200)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
pokemonInfo->mobileTurnTimer = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct Tile *mapTile;
|
|
char walkableNeighborFlags;
|
|
if (gDungeonWaterType[gDungeon->tileset] == DUNGEON_WATER_TYPE_LAVA
|
|
&& crossableTerrain == CROSSABLE_TERRAIN_LIQUID
|
|
&& IQSkillIsEnabled(pokemon, IQ_LAVA_EVADER))
|
|
{
|
|
crossableTerrain = CROSSABLE_TERRAIN_REGULAR;
|
|
}
|
|
mapTile = GetTile(pokemon->pos.x, pokemon->pos.y);
|
|
walkableNeighborFlags = mapTile->walkableNeighborFlags[crossableTerrain];
|
|
/*
|
|
Check for configurations of open tiles that are considered junctions; i.e., shaped like a 'T' or '+'.
|
|
X=Wall, O=Open
|
|
|
|
0x54 0x51 0x45 0x15 0x55
|
|
XOX XOX XXX XOX XOX
|
|
OOO OOX OOO XOO OOO
|
|
XXX XOX XOX XOX XOX
|
|
*/
|
|
if (walkableNeighborFlags != 0x54 && walkableNeighborFlags != 0x51 && walkableNeighborFlags != 0x45 && walkableNeighborFlags != 0x15 && walkableNeighborFlags != 0x55)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
bool8 ShouldAvoidFirstHit(struct Entity *pokemon, bool8 forceAvoid)
|
|
{
|
|
if (!HasTactic(pokemon, TACTIC_AVOID_THE_FIRST_HIT))
|
|
return FALSE;
|
|
if (!forceAvoid)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
bool8 ShouldMonsterRunAway(struct Entity *pokemon)
|
|
{
|
|
if (!EntityExists(pokemon))
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
struct EntityInfo *pokemonInfo = pokemon->info;
|
|
if (pokemonInfo->terrifiedTurns != 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
if (pokemonInfo->isTeamLeader)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (HasAbility(pokemon, ABILITY_RUN_AWAY))
|
|
{
|
|
bool8 runAwayActive = pokemonInfo->HP < pokemonInfo->maxHPStat / 2;
|
|
if (runAwayActive)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
if (HasTactic(pokemon, TACTIC_GET_AWAY) ||
|
|
(HasTactic(pokemon, TACTIC_AVOID_TROUBLE) && pokemonInfo->HP <= pokemonInfo->maxHPStat / 2))
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
bool8 ShouldMonsterRunAwayAndShowEffect(struct Entity *pokemon, bool8 showRunAwayEffect)
|
|
{
|
|
if (ShouldMonsterRunAway(pokemon))
|
|
{
|
|
CheckRunAwayVisualFlag(pokemon, showRunAwayEffect);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void CheckRunAwayVisualFlag(struct Entity *pokemon, bool8 showRunAwayEffect)
|
|
{
|
|
bool8 cVar1;
|
|
struct EntityInfo *iVar2;
|
|
struct EntityInfo *iVar3;
|
|
iVar2 = pokemon->info;
|
|
iVar3 = iVar2;
|
|
|
|
if (((!iVar2->isTeamLeader) && HasAbility(pokemon,ABILITY_RUN_AWAY) &&
|
|
(cVar1 = SetVisualFlags(iVar3,4,iVar2->HP <= iVar2->maxHPStat / 2), showRunAwayEffect)) &&
|
|
(cVar1)) {
|
|
ShowVisualFlags(pokemon);
|
|
}
|
|
}
|
|
|
|
u8 CanTarget(struct Entity *pokemon, struct Entity *targetPokemon, bool8 ignoreInvisible, bool8 checkPetrified)
|
|
{
|
|
struct EntityInfo *pokemonInfo = pokemon->info;
|
|
struct EntityInfo *targetData = targetPokemon->info;
|
|
u8 targetingDecoy;
|
|
u8 pokemonTargetingDecoy;
|
|
bool8 pokemonIsEnemy;
|
|
bool8 targetIsEnemy;
|
|
bool8 targetIsDecoy;
|
|
if (pokemon == targetPokemon)
|
|
{
|
|
return TARGET_CAPABILITY_CANNOT_ATTACK;
|
|
}
|
|
if (pokemonInfo->shopkeeper == SHOPKEEPER_MODE_SHOPKEEPER ||
|
|
targetData->shopkeeper == SHOPKEEPER_MODE_SHOPKEEPER ||
|
|
pokemonInfo->clientType == CLIENT_TYPE_DONT_MOVE ||
|
|
targetData->clientType == CLIENT_TYPE_DONT_MOVE ||
|
|
pokemonInfo->clientType == CLIENT_TYPE_CLIENT ||
|
|
targetData->clientType == CLIENT_TYPE_CLIENT ||
|
|
(checkPetrified && !pokemonInfo->isNotTeamMember && targetData->immobilizeStatus == STATUS_PETRIFIED) ||
|
|
(!ignoreInvisible && targetData->transformStatus == STATUS_INVISIBLE && !CanSeeInvisibleMonsters(pokemon)))
|
|
{
|
|
return TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET;
|
|
}
|
|
pokemonTargetingDecoy = pokemonInfo->targetingDecoy;
|
|
targetingDecoy = TARGETING_DECOY_NONE;
|
|
if (pokemonTargetingDecoy != TARGETING_DECOY_NONE)
|
|
{
|
|
targetingDecoy = TARGETING_DECOY_WILD;
|
|
if (pokemonTargetingDecoy == TARGETING_DECOY_TEAM)
|
|
{
|
|
targetingDecoy = TARGETING_DECOY_TEAM;
|
|
}
|
|
}
|
|
if (pokemonInfo->shopkeeper != SHOPKEEPER_MODE_NORMAL)
|
|
{
|
|
pokemonIsEnemy = FALSE;
|
|
if (pokemonInfo->shopkeeper == SHOPKEEPER_MODE_ATTACK_TEAM)
|
|
{
|
|
pokemonIsEnemy = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pokemonIsEnemy = pokemonInfo->isNotTeamMember ? TRUE : FALSE;
|
|
}
|
|
if (targetData->shopkeeper != SHOPKEEPER_MODE_NORMAL)
|
|
{
|
|
targetIsEnemy = FALSE;
|
|
if (targetData->shopkeeper == SHOPKEEPER_MODE_ATTACK_TEAM)
|
|
{
|
|
targetIsEnemy = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
targetIsEnemy = targetData->isNotTeamMember ? TRUE : FALSE;
|
|
}
|
|
targetIsDecoy = FALSE;
|
|
if (targetData->waitingStatus == STATUS_DECOY)
|
|
{
|
|
targetIsDecoy = TRUE;
|
|
}
|
|
return gTargetingData[targetingDecoy][pokemonIsEnemy][targetIsEnemy][targetIsDecoy];
|
|
}
|
|
|
|
static inline bool8 JoinLocationCannotUseItems_1(struct EntityInfo *pokemonInfo)
|
|
{
|
|
if (pokemonInfo->joinedAt == DUNGEON_JOIN_LOCATION_CLIENT_POKEMON)
|
|
{
|
|
return TRUE;
|
|
}
|
|
if (pokemonInfo->joinedAt == DUNGEON_RESCUE_TEAM_BASE)
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
u8 sub_807167C(struct Entity * pokemon, struct Entity * target)
|
|
{
|
|
bool8 cannotUseItems;
|
|
struct EntityInfo * targetEntityInfo;
|
|
struct EntityInfo * pokemonEntityData;
|
|
|
|
pokemonEntityData = pokemon->info;
|
|
targetEntityInfo = target->info;
|
|
if (pokemonEntityData->clientType != CLIENT_TYPE_CLIENT) {
|
|
cannotUseItems = JoinLocationCannotUseItems_1(pokemonEntityData);
|
|
if (!cannotUseItems && (pokemonEntityData->shopkeeper == SHOPKEEPER_MODE_NORMAL) && (targetEntityInfo->clientType != CLIENT_TYPE_CLIENT)) {
|
|
cannotUseItems = JoinLocationCannotUseItems_1(targetEntityInfo);
|
|
if (cannotUseItems || (targetEntityInfo->shopkeeper != SHOPKEEPER_MODE_NORMAL)) {
|
|
error:
|
|
return TARGET_CAPABILITY_CAN_ATTACK_NOT_TARGET;
|
|
}
|
|
else
|
|
{
|
|
if ((pokemonEntityData->isNotTeamMember) != (targetEntityInfo->isNotTeamMember)) {
|
|
return TARGET_CAPABILITY_CAN_TARGET;
|
|
}
|
|
else {
|
|
return TARGET_CAPABILITY_CANNOT_ATTACK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
goto error;
|
|
}
|