summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/code_8040094.c5
-rw-r--r--src/code_80521D0_1.c6
-rw-r--r--src/code_8057824_1.c2
-rw-r--r--src/dungeon_ai_attack.c39
-rw-r--r--src/dungeon_ai_attack_1.c189
-rw-r--r--src/dungeon_ai_attack_2.c76
-rw-r--r--src/dungeon_ai_item_weight.c6
-rw-r--r--src/dungeon_ai_items.c14
-rw-r--r--src/dungeon_ai_movement.c4
-rw-r--r--src/dungeon_ai_targeting.c62
-rw-r--r--src/dungeon_ai_targeting_1.c (renamed from src/dungeon_ai.c)2
-rw-r--r--src/dungeon_ai_targeting_2.c (renamed from src/dungeon_ai_1.c)2
-rw-r--r--src/dungeon_capabilities_1.c10
-rw-r--r--src/dungeon_engine.c11
-rw-r--r--src/dungeon_map_access.c20
-rw-r--r--src/dungeon_range.c2
-rw-r--r--src/dungeon_util.c10
-rw-r--r--src/moves.c6
-rw-r--r--src/status_checker.c537
-rw-r--r--src/status_checks_1.c16
-rw-r--r--src/targeting_flags.c2
-rw-r--r--src/tile_types.c114
-rw-r--r--src/trap.c27
-rw-r--r--src/weather.c16
24 files changed, 1071 insertions, 107 deletions
diff --git a/src/code_8040094.c b/src/code_8040094.c
index c0e7eac..f7374e3 100644
--- a/src/code_8040094.c
+++ b/src/code_8040094.c
@@ -3,6 +3,7 @@
#include "code_80521D0.h"
#include "dungeon_entity.h"
#include "dungeon_global_data.h"
+#include "dungeon_map_access.h"
#include "file_system.h"
#include "play_time.h"
@@ -26,8 +27,6 @@ extern void sub_8040A84();
extern const char *gUnknown_80FD040; // It became brighter on the floor
-extern struct MapTile *GetMapEntity(u32, u32);
-
// Luminous Orb???
void HandleLuminousOrbAction(struct DungeonEntity *param_1)
{
@@ -41,7 +40,7 @@ void HandleLuminousOrbAction(struct DungeonEntity *param_1)
{
for(XCoord = 0; XCoord < DUNGEON_MAX_SIZE_X; XCoord++)
{
- mapTile = GetMapEntity(XCoord, YCoord);
+ mapTile = GetMapTile_2(XCoord, YCoord);
mapTile->unk4 = mapTile->unk4 | 1;
}
}
diff --git a/src/code_80521D0_1.c b/src/code_80521D0_1.c
index 2b23a14..53f5586 100644
--- a/src/code_80521D0_1.c
+++ b/src/code_80521D0_1.c
@@ -3,6 +3,7 @@
#include "constants/friend_area.h"
#include "dungeon_global_data.h"
#include "dungeon_entity.h"
+#include "dungeon_map_access.h"
#include "dungeon_random.h"
#include "dungeon_util.h"
#include "friend_area.h"
@@ -138,7 +139,6 @@ extern void sub_8049ED4();
extern void sub_8040A84();
extern void sub_8086A54(struct DungeonEntity *);
-extern struct MapTile *GetMapEntity(u32, u32);
extern void sub_806BFC0(struct DungeonEntityData *, u32);
void sub_808BBA8(struct DungeonEntity *param_1)
@@ -207,7 +207,7 @@ void sub_808BCE4(void)
{
struct MapTile *puVar1;
- puVar1 = GetMapEntity(gDungeonGlobalData->unkE23C, gDungeonGlobalData->unkE23E);
+ puVar1 = GetMapTile_2(gDungeonGlobalData->unkE23C, gDungeonGlobalData->unkE23E);
puVar1->tileType &= ~(TILE_TYPE_FLOOR | TILE_TYPE_LIQUID);
puVar1->tileType |= TILE_TYPE_MAP_EDGE;
puVar1->tileType &= ~TILE_TYPE_STAIRS;
@@ -221,7 +221,7 @@ void sub_808BD38(void)
{
struct MapTile *puVar1;
- puVar1 = GetMapEntity(gDungeonGlobalData->unkE23C, gDungeonGlobalData->unkE23E);
+ puVar1 = GetMapTile_2(gDungeonGlobalData->unkE23C, gDungeonGlobalData->unkE23E);
puVar1->tileType &= ~(TILE_TYPE_FLOOR | TILE_TYPE_LIQUID);
puVar1->tileType |= TILE_TYPE_FLOOR;
puVar1->tileType &= ~TILE_TYPE_MAP_EDGE;
diff --git a/src/code_8057824_1.c b/src/code_8057824_1.c
index f3a7db2..0d48f71 100644
--- a/src/code_8057824_1.c
+++ b/src/code_8057824_1.c
@@ -3318,7 +3318,7 @@ void sub_808B1CC(u8 r0)
"\tldrsh r0, [r0, r2]\n"
"\tmovs r3, 0x2\n"
"\tldrsh r1, [r5, r3]\n"
- "\tbl GetMapEntity\n"
+ "\tbl GetMapTile_2\n"
"\tldrh r2, [r0]\n"
"\tmovs r3, 0x80\n"
"\tlsls r3, 2\n"
diff --git a/src/dungeon_ai_attack.c b/src/dungeon_ai_attack.c
index 30e5d3a..835d207 100644
--- a/src/dungeon_ai_attack.c
+++ b/src/dungeon_ai_attack.c
@@ -10,8 +10,10 @@
#include "constants/type.h"
#include "charge_move.h"
#include "dungeon_action.h"
-#include "dungeon_ai.h"
+#include "dungeon_ai_targeting.h"
+#include "dungeon_ai_targeting_1.h"
#include "dungeon_ai_attack_1.h"
+#include "dungeon_ai_attack_2.h"
#include "dungeon_capabilities_1.h"
#include "dungeon_global_data.h"
#include "dungeon_map_access.h"
@@ -22,6 +24,7 @@
#include "dungeon_visibility.h"
#include "moves.h"
#include "position_util.h"
+#include "status_checker.h"
#include "status_checks.h"
#include "targeting.h"
#include "targeting_flags.h"
@@ -32,17 +35,13 @@ const s16 gRegularAttackWeights[] = {100, 20, 30, 40, 50};
extern bool8 gCanAttackInDirection[NUM_DIRECTIONS];
extern s32 gNumPotentialTargets;
-extern s32 gPotentialTargetWeights_2[NUM_DIRECTIONS];
+extern s32 gPotentialAttackTargetWeights[NUM_DIRECTIONS];
extern u8 gPotentialAttackTargetDirections[NUM_DIRECTIONS];
extern struct DungeonEntity *gPotentialTargets[NUM_DIRECTIONS];
-extern bool8 IsMoveUsable(struct DungeonEntity*, s32, bool8);
+extern bool8 IsMoveUsable_1(struct DungeonEntity*, s32, bool8);
extern bool8 TargetRegularAttack(struct DungeonEntity*, u32*, bool8);
-extern bool8 CanUseWithStatusChecker(struct DungeonEntity*, struct PokemonMove*);
-extern bool8 CanAttackInFront(struct DungeonEntity*, s32);
-extern s32 WeightMoveIfUsable(s32, s32, struct DungeonEntity*, struct DungeonEntity*, struct PokemonMove*, bool8);
extern bool8 IsTargetInLineRange(struct DungeonEntity*, struct DungeonEntity*, s32);
-extern bool8 CanUseStatusMove(s32, struct DungeonEntity*, struct DungeonEntity*, struct PokemonMove*, bool8);
extern s32 WeightMove(struct DungeonEntity*, s32, struct DungeonEntity*, u8);
void DecideAttack(struct DungeonEntity *pokemon)
@@ -182,7 +181,7 @@ void DecideAttack(struct DungeonEntity *pokemon)
move = &pokemonData->moves[i];
if (move->moveFlags & MOVE_FLAG_EXISTS &&
willNotUnlinkMove[i] &&
- IsMoveUsable(pokemon, i, hasPPChecker) &&
+ IsMoveUsable_1(pokemon, i, hasPPChecker) &&
move->moveFlags & MOVE_FLAG_ENABLED)
{
moveTargetResults[i].moveUsable = TRUE;
@@ -349,7 +348,7 @@ s32 FindMoveTarget(struct MoveTargetResults *moveTargetResults, struct DungeonEn
hasStatusChecker = HasIQSkill(pokemon, IQ_SKILL_STATUS_CHECKER);
moveTargetResults->moveUsable = FALSE;
if ((pokemonData->volatileStatus == VOLATILE_STATUS_TAUNTED && !MoveDealsDirectDamage(move)) ||
- (hasStatusChecker && !CanUseWithStatusChecker(pokemon, move)))
+ (hasStatusChecker && !CanUseOnSelfWithStatusChecker(pokemon, move)))
{
return 1;
}
@@ -366,7 +365,7 @@ s32 FindMoveTarget(struct MoveTargetResults *moveTargetResults, struct DungeonEn
{
gCanAttackInDirection[i] = TRUE;
gPotentialAttackTargetDirections[numPotentialTargets] = i;
- gPotentialTargetWeights_2[numPotentialTargets] = 99;
+ gPotentialAttackTargetWeights[numPotentialTargets] = 99;
gPotentialTargets[numPotentialTargets] = NULL;
numPotentialTargets++;
}
@@ -377,7 +376,7 @@ s32 FindMoveTarget(struct MoveTargetResults *moveTargetResults, struct DungeonEn
{
// Double assignment to fix a regswap.
s16 rangeTargetingFlags = rangeTargetingFlags2 = targetingFlags & 0xF0;
- struct MapTile *adjacentTile = GetMapTileAtPosition(pokemon->posWorld.x + gAdjacentTileOffsets[i].x,
+ struct MapTile *adjacentTile = GetMapTile_1(pokemon->posWorld.x + gAdjacentTileOffsets[i].x,
pokemon->posWorld.y + gAdjacentTileOffsets[i].y);
struct DungeonEntity *adjacentPokemon = adjacentTile->pokemon;
if (adjacentPokemon != NULL && GetEntityType(adjacentPokemon) == ENTITY_POKEMON)
@@ -411,7 +410,7 @@ s32 FindMoveTarget(struct MoveTargetResults *moveTargetResults, struct DungeonEn
{
for (i = 0; i < NUM_DIRECTIONS; i++)
{
- struct MapTile *targetTile = GetMapTileAtPosition(pokemon->posWorld.x + gAdjacentTileOffsets[i].x,
+ struct MapTile *targetTile = GetMapTile_1(pokemon->posWorld.x + gAdjacentTileOffsets[i].x,
pokemon->posWorld.y + gAdjacentTileOffsets[i].y);
if (CanAttackInFront(pokemon, i))
{
@@ -425,7 +424,7 @@ s32 FindMoveTarget(struct MoveTargetResults *moveTargetResults, struct DungeonEn
continue;
}
}
- targetTile = GetMapTileAtPosition(pokemon->posWorld.x + gAdjacentTileOffsets[i].x * 2,
+ targetTile = GetMapTile_1(pokemon->posWorld.x + gAdjacentTileOffsets[i].x * 2,
pokemon->posWorld.y + gAdjacentTileOffsets[i].y * 2);
targetPokemon = targetTile->pokemon;
if (targetPokemon != NULL && GetEntityType(targetPokemon) == ENTITY_POKEMON)
@@ -457,7 +456,7 @@ s32 FindMoveTarget(struct MoveTargetResults *moveTargetResults, struct DungeonEn
{
gCanAttackInDirection[facingDir] = TRUE;
gPotentialAttackTargetDirections[numPotentialTargets] = facingDir;
- gPotentialTargetWeights_2[numPotentialTargets] = WeightMove(pokemon, targetingFlags, target, GetMoveTypeForPokemon(pokemon, move));
+ gPotentialAttackTargetWeights[numPotentialTargets] = WeightMove(pokemon, targetingFlags, target, GetMoveTypeForPokemon(pokemon, move));
gPotentialTargets[numPotentialTargets] = target;
numPotentialTargets++;
}
@@ -492,27 +491,27 @@ s32 FindMoveTarget(struct MoveTargetResults *moveTargetResults, struct DungeonEn
s32 i;
for (i = 0; i < numPotentialTargets; i++)
{
- if (maxWeight < gPotentialTargetWeights_2[i])
+ if (maxWeight < gPotentialAttackTargetWeights[i])
{
- maxWeight = gPotentialTargetWeights_2[i];
+ maxWeight = gPotentialAttackTargetWeights[i];
}
}
for (i = 0; i < numPotentialTargets; i++)
{
- if (maxWeight != gPotentialTargetWeights_2[i])
+ if (maxWeight != gPotentialAttackTargetWeights[i])
{
- gPotentialTargetWeights_2[i] = 0;
+ gPotentialAttackTargetWeights[i] = 0;
}
}
moveWeight = maxWeight;
for (i = 0; i < numPotentialTargets; i++)
{
- totalWeight += gPotentialTargetWeights_2[i];
+ totalWeight += gPotentialAttackTargetWeights[i];
}
weightCounter = DungeonRandomCapped(totalWeight);
for (i = 0; i < numPotentialTargets; i++)
{
- weightCounter -= gPotentialTargetWeights_2[i];
+ weightCounter -= gPotentialAttackTargetWeights[i];
if (weightCounter < 0)
{
break;
diff --git a/src/dungeon_ai_attack_1.c b/src/dungeon_ai_attack_1.c
index 8a186ea..023df14 100644
--- a/src/dungeon_ai_attack_1.c
+++ b/src/dungeon_ai_attack_1.c
@@ -1,76 +1,175 @@
#include "global.h"
#include "dungeon_ai_attack_1.h"
-#include "constants/iq_skill.h"
-#include "dungeon_global_data.h"
-#include "dungeon_map_access.h"
+#include "constants/direction.h"
+#include "constants/targeting.h"
+#include "constants/type.h"
+#include "dungeon_ai_targeting_2.h"
#include "dungeon_pokemon_attributes.h"
-#include "dungeon_util.h"
+#include "dungeon_random.h"
+#include "moves.h"
+#include "position_util.h"
+#include "status_checks_1.h"
-bool8 IsTargetStraightAhead(struct DungeonEntity *pokemon, struct DungeonEntity *targetPokemon, s32 facingDir, s32 maxRange)
+extern bool8 gCanAttackInDirection[NUM_DIRECTIONS];
+extern s32 gPotentialAttackTargetWeights[NUM_DIRECTIONS];
+extern u8 gPotentialAttackTargetDirections[NUM_DIRECTIONS];
+extern struct DungeonEntity *gPotentialTargets[NUM_DIRECTIONS];
+
+extern s32 WeightMove(struct DungeonEntity*, s32, struct DungeonEntity*, u8);
+extern bool8 CanUseOnTargetWithStatusChecker(struct DungeonEntity*, struct DungeonEntity*, struct PokemonMove*);
+
+s32 WeightMoveIfUsable(s32 numPotentialTargets, s32 targetingFlags, struct DungeonEntity *user, struct DungeonEntity *target, struct PokemonMove *move, bool32 hasStatusChecker)
{
- s32 posDiffX = pokemon->posWorld.x - targetPokemon->posWorld.x;
- s32 effectiveMaxRange;
- if (posDiffX < 0)
+ s32 facingDir;
+ s32 targetingFlags2 = (s16) targetingFlags;
+ bool8 hasStatusChecker2 = hasStatusChecker;
+ struct DungeonEntityData *userData = user->entityData;
+ if ((user->posWorld.x == target->posWorld.x && user->posWorld.y == target->posWorld.y) ||
+ (targetingFlags2 & 0xF0) == TARGETING_FLAG_TARGET_ROOM ||
+ (targetingFlags2 & 0xF0) == TARGETING_FLAG_TARGET_FLOOR ||
+ (targetingFlags2 & 0xF0) == TARGETING_FLAG_TARGET_SELF)
{
- posDiffX = -posDiffX;
+ facingDir = userData->action.facingDir;
}
- effectiveMaxRange = pokemon->posWorld.y - targetPokemon->posWorld.y;
- if (effectiveMaxRange < 0)
+ else
{
- effectiveMaxRange = -effectiveMaxRange;
+ facingDir = CalculateFacingDir(&user->posWorld, &target->posWorld);
}
- if (effectiveMaxRange < posDiffX)
+ if (!gCanAttackInDirection[facingDir] &&
+ CanUseStatusMove(targetingFlags2, user, target, move, hasStatusChecker2))
{
- effectiveMaxRange = posDiffX;
+ gCanAttackInDirection[facingDir] = TRUE;
+ do { gPotentialAttackTargetDirections[numPotentialTargets] = facingDir; } while (0);
+ gPotentialAttackTargetWeights[numPotentialTargets] = WeightMove(user, targetingFlags2, target, GetMoveTypeForPokemon(user, move));
+ gPotentialTargets[numPotentialTargets] = target;
+ numPotentialTargets++;
}
- if (effectiveMaxRange > maxRange)
+ return numPotentialTargets;
+}
+
+bool8 CanUseStatusMove(s32 targetingFlags, struct DungeonEntity *user, struct DungeonEntity *target, struct PokemonMove *move, bool32 hasStatusChecker)
+{
+ struct DungeonEntityData *targetData;
+ s32 targetingFlags2 = (s16) targetingFlags;
+ bool8 hasStatusChecker2 = hasStatusChecker;
+ bool8 hasTarget = FALSE;
+ u32 categoryTargetingFlags = targetingFlags2 & 0xF;
+ u32 *categoryTargetingFlags2 = &categoryTargetingFlags; // Fixes a regswap.
+ if (*categoryTargetingFlags2 == TARGETING_FLAG_TARGET_OTHER)
{
- effectiveMaxRange = maxRange;
+ if (CanTarget(user, target, FALSE, TRUE) == TARGET_CAPABILITY_CAN_TARGET)
+ {
+ hasTarget = TRUE;
+ }
}
- if (!HasIQSkill(pokemon, IQ_SKILL_COURSE_CHECKER))
+ else if (categoryTargetingFlags == TARGETING_FLAG_HEAL_TEAM)
{
- // BUG: effectiveMaxRange is already capped at maxRange, so this condition always evaluates to TRUE.
- // The AI also has range checks elsewhere, so this doesn't become an issue in most cases.
- // If the AI has the Long Toss or Pierce statuses and Course Checker is disabled,
- // this incorrect check causes the AI to throw items at targets further than 10 tiles away.
- if (effectiveMaxRange <= maxRange)
+ goto checkCanTarget;
+ }
+ else if (categoryTargetingFlags == TARGETING_FLAG_LONG_RANGE)
+ {
+ targetData = target->entityData;
+ goto checkThirdParty;
+ }
+ else if (categoryTargetingFlags == TARGETING_FLAG_ATTACK_ALL)
+ {
+ targetData = target->entityData;
+ if (user == target)
+ {
+ goto returnFalse;
+ }
+ checkThirdParty:
+ hasTarget = TRUE;
+ if (targetData->shopkeeperMode == SHOPKEEPER_FRIENDLY ||
+ targetData->clientType == CLIENT_TYPE_DONT_MOVE ||
+ targetData->clientType == CLIENT_TYPE_CLIENT)
{
- return TRUE;
+ returnFalse:
+ return FALSE;
}
}
- else
+ else if (categoryTargetingFlags == TARGETING_FLAG_BOOST_TEAM)
+ {
+ if (user == target)
+ {
+ goto returnFalse;
+ }
+ checkCanTarget:
+ if (CanTarget(user, target, FALSE, TRUE) == TARGET_CAPABILITY_CANNOT_ATTACK)
+ {
+ hasTarget = TRUE;
+ }
+ }
+ else if ((u16) (categoryTargetingFlags - 3) <= 1) // categoryTargetingFlags == TARGETING_FLAG_ITEM
{
- s32 currentPosX = pokemon->posWorld.x;
- s32 currentPosY = pokemon->posWorld.y;
- s32 adjacentTileOffsetX = gAdjacentTileOffsets[facingDir].x;
- s32 adjacentTileOffsetY = gAdjacentTileOffsets[facingDir].y;
- s32 i;
- for (i = 0; i <= effectiveMaxRange; i++)
+ hasTarget = TRUE;
+ }
+
+ if (hasTarget)
+ {
+ if (hasStatusChecker2)
{
- struct MapTile *mapTile;
- currentPosX += adjacentTileOffsetX;
- currentPosY += adjacentTileOffsetY;
- if (currentPosX <= 0 || currentPosY <= 0 ||
- currentPosX >= DUNGEON_MAX_SIZE_X - 1 || currentPosY >= DUNGEON_MAX_SIZE_Y - 1)
+ if (!CanUseOnTargetWithStatusChecker(user, target, move))
+ {
+ goto returnFalse;
+ }
+ if ((targetingFlags2 & 0xF00) == TARGETING_FLAG_SET_TRAP)
{
- break;
+ goto rollMoveUseChance;
}
- while (0); // Extra label needed to swap branch locations in ASM.
- mapTile = GetMapTileAtPosition(currentPosX, currentPosY);
- if (!(mapTile->tileType & (TILE_TYPE_FLOOR | TILE_TYPE_LIQUID)))
+ else if ((targetingFlags2 & 0xF00) == TARGETING_FLAG_HEAL_HP)
{
- break;
+ if (!HasQuarterHPOrLess(target))
+ {
+ if (*categoryTargetingFlags2);
+ goto returnFalse;
+ }
}
- if (mapTile->pokemon == targetPokemon)
+ else if ((targetingFlags2 & 0xF00) == TARGETING_FLAG_HEAL_STATUS)
{
- return TRUE;
+ if (!HasNegativeStatus(target))
+ {
+ if (*categoryTargetingFlags2); // Flips the conditional.
+ goto returnFalse;
+ }
}
- if (mapTile->pokemon != NULL)
+ else if ((targetingFlags2 & 0xF00) == TARGETING_FLAG_DREAM_EATER)
+ {
+ if (!IsSleeping(target))
+ {
+ if (*categoryTargetingFlags2); // Flips the conditional.
+ goto returnFalse;
+ }
+ }
+ else if ((targetingFlags2 & 0xF00) == TARGETING_FLAG_EXPOSE)
+ {
+ targetData = target->entityData;
+ if ((targetData->type1 != TYPE_GHOST && targetData->type2 != TYPE_GHOST) || targetData->exposedStatus)
+ {
+ if (*categoryTargetingFlags2); // Flips the conditional.
+ goto returnFalse;
+ }
+ }
+ else if ((targetingFlags2 & 0xF00) == TARGETING_FLAG_HEAL_ALL)
+ {
+ if (!HasNegativeStatus(target) && !HasQuarterHPOrLess(target))
+ {
+ if (*categoryTargetingFlags2); // Flips the conditional.
+ goto returnFalse;
+ }
+ }
+ }
+ else if ((targetingFlags2 & 0xF00) == TARGETING_FLAG_SET_TRAP)
+ {
+ s32 useChance;
+ rollMoveUseChance:
+ useChance = GetMoveAccuracy(move, ACCURACY_TYPE_USE_CHANCE);
+ if (DungeonRandomCapped(100) >= useChance)
{
- break;
+ goto returnFalse;
}
}
}
- return FALSE;
+ return hasTarget;
}
diff --git a/src/dungeon_ai_attack_2.c b/src/dungeon_ai_attack_2.c
new file mode 100644
index 0000000..41e924a
--- /dev/null
+++ b/src/dungeon_ai_attack_2.c
@@ -0,0 +1,76 @@
+#include "global.h"
+#include "dungeon_ai_attack_2.h"
+
+#include "constants/iq_skill.h"
+#include "dungeon_global_data.h"
+#include "dungeon_map_access.h"
+#include "dungeon_pokemon_attributes.h"
+#include "dungeon_util.h"
+
+bool8 IsTargetStraightAhead(struct DungeonEntity *pokemon, struct DungeonEntity *targetPokemon, s32 facingDir, s32 maxRange)
+{
+ s32 posDiffX = pokemon->posWorld.x - targetPokemon->posWorld.x;
+ s32 effectiveMaxRange;
+ if (posDiffX < 0)
+ {
+ posDiffX = -posDiffX;
+ }
+ effectiveMaxRange = pokemon->posWorld.y - targetPokemon->posWorld.y;
+ if (effectiveMaxRange < 0)
+ {
+ effectiveMaxRange = -effectiveMaxRange;
+ }
+ if (effectiveMaxRange < posDiffX)
+ {
+ effectiveMaxRange = posDiffX;
+ }
+ if (effectiveMaxRange > maxRange)
+ {
+ effectiveMaxRange = maxRange;
+ }
+ if (!HasIQSkill(pokemon, IQ_SKILL_COURSE_CHECKER))
+ {
+ // BUG: effectiveMaxRange is already capped at maxRange, so this condition always evaluates to TRUE.
+ // The AI also has range checks elsewhere, so this doesn't become an issue in most cases.
+ // If the AI has the Long Toss or Pierce statuses and Course Checker is disabled,
+ // this incorrect check causes the AI to throw items at targets further than 10 tiles away.
+ if (effectiveMaxRange <= maxRange)
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ s32 currentPosX = pokemon->posWorld.x;
+ s32 currentPosY = pokemon->posWorld.y;
+ s32 adjacentTileOffsetX = gAdjacentTileOffsets[facingDir].x;
+ s32 adjacentTileOffsetY = gAdjacentTileOffsets[facingDir].y;
+ s32 i;
+ for (i = 0; i <= effectiveMaxRange; i++)
+ {
+ struct MapTile *mapTile;
+ currentPosX += adjacentTileOffsetX;
+ currentPosY += adjacentTileOffsetY;
+ if (currentPosX <= 0 || currentPosY <= 0 ||
+ currentPosX >= DUNGEON_MAX_SIZE_X - 1 || currentPosY >= DUNGEON_MAX_SIZE_Y - 1)
+ {
+ break;
+ }
+ while (0); // Extra label needed to swap branch locations in ASM.
+ mapTile = GetMapTile_1(currentPosX, currentPosY);
+ if (!(mapTile->tileType & (TILE_TYPE_FLOOR | TILE_TYPE_LIQUID)))
+ {
+ break;
+ }
+ if (mapTile->pokemon == targetPokemon)
+ {
+ return TRUE;
+ }
+ if (mapTile->pokemon != NULL)
+ {
+ break;
+ }
+ }
+ }
+ return FALSE;
+}
diff --git a/src/dungeon_ai_item_weight.c b/src/dungeon_ai_item_weight.c
index 0fe019f..ce79ad4 100644
--- a/src/dungeon_ai_item_weight.c
+++ b/src/dungeon_ai_item_weight.c
@@ -3,7 +3,7 @@
#include "constants/status.h"
#include "constants/targeting.h"
-#include "dungeon_ai_1.h"
+#include "dungeon_ai_targeting_2.h"
#include "dungeon_map_access.h"
#include "dungeon_pokemon_attributes.h"
#include "dungeon_util.h"
@@ -185,7 +185,7 @@ u32 EvaluateItem(struct DungeonEntity *targetPokemon, struct ItemSlot *item, u32
}
break;
case ITEM_ID_QUICK_SEED:
- if (targetPokemon->entityData->movementSpeed <= 3)
+ if (targetPokemon->entityData->movementSpeed < MAX_MOVEMENT_SPEED)
{
if (CanTargetAdjacentPokemon(targetPokemon))
{
@@ -448,7 +448,7 @@ bool8 CanTargetAdjacentPokemon(struct DungeonEntity *pokemon)
s32 facingDir;
for (facingDir = 0; facingDir < NUM_DIRECTIONS; facingDir++)
{
- struct MapTile *mapTile = GetMapTileAtPosition(pokemon->posWorld.x + gAdjacentTileOffsets[facingDir].x, pokemon->posWorld.y + gAdjacentTileOffsets[facingDir].y);
+ struct MapTile *mapTile = GetMapTile_1(pokemon->posWorld.x + gAdjacentTileOffsets[facingDir].x, pokemon->posWorld.y + gAdjacentTileOffsets[facingDir].y);
struct DungeonEntity *adjacentPokemon = mapTile->pokemon;
if (adjacentPokemon != NULL && GetEntityType(adjacentPokemon) != ENTITY_NONE &&
CanTarget(pokemon, adjacentPokemon, FALSE, TRUE) == TARGET_CAPABILITY_CAN_TARGET)
diff --git a/src/dungeon_ai_items.c b/src/dungeon_ai_items.c
index 183d143..31d57ff 100644
--- a/src/dungeon_ai_items.c
+++ b/src/dungeon_ai_items.c
@@ -6,10 +6,10 @@
#include "constants/status.h"
#include "constants/targeting.h"
#include "dungeon_action.h"
-#include "dungeon_ai_1.h"
-#include "dungeon_ai_attack_1.h"
+#include "dungeon_ai_attack_2.h"
#include "dungeon_ai_item_weight.h"
#include "dungeon_ai_items.h"
+#include "dungeon_ai_targeting_2.h"
#include "dungeon_capabilities.h"
#include "dungeon_capabilities_1.h"
#include "dungeon_entity.h"
@@ -39,7 +39,7 @@ enum ItemTargetFlag
extern void sub_8077274(struct DungeonEntity *, struct DungeonEntity *);
extern s32 gNumPotentialTargets;
-extern u32 gPotentialTargetWeights[NUM_DIRECTIONS];
+extern u32 gPotentialItemTargetWeights[NUM_DIRECTIONS];
extern u32 gPotentialItemTargetDirections[NUM_DIRECTIONS];
extern bool8 gTargetAhead[NUM_DIRECTIONS];
extern struct TeamInventory *gTeamInventory_203B460;
@@ -95,7 +95,7 @@ void DecideUseItem(struct DungeonEntity *pokemon)
FindStraightThrowableTargets(pokemon, 2, item, 1);
for (targetIndex = 0; targetIndex < gNumPotentialTargets; targetIndex++)
{
- if (RollPercentChance(gPotentialTargetWeights[targetIndex]))
+ if (RollPercentChance(gPotentialItemTargetWeights[targetIndex]))
{
SetAction(&pokemonData->action, DUNGEON_ACTION_THROW_ITEM_AI);
pokemonData->action.actionUseIndex = selectedToolboxIndex;
@@ -158,7 +158,7 @@ void DecideUseItem(struct DungeonEntity *pokemon)
else if (toolboxIndex == 0)
{
// This seems unused. toolboxIndex can never be 0.
- struct MapTile *mapTile = GetMapTileAtPosition(pokemon->posWorld.x, pokemon->posWorld.y);
+ struct MapTile *mapTile = GetMapTile_1(pokemon->posWorld.x, pokemon->posWorld.y);
struct DungeonEntity *mapObject = mapTile->mapObject;
if (mapObject != null)
{
@@ -229,7 +229,7 @@ void DecideUseItem(struct DungeonEntity *pokemon)
s32 thrownAIFlag;
for (thrownAIFlag = ITEM_AI_FLAG_TARGET_ALLY; thrownAIFlag <= ITEM_AI_FLAG_TARGET_ENEMY; thrownAIFlag++)
{
- potentialTargetWeights = gPotentialTargetWeights;
+ potentialTargetWeights = gPotentialItemTargetWeights;
if (GetItemAIFlag(item->itemIndex, thrownAIFlag))
{
u8 itemType = GetItemType(item->itemIndex);
@@ -422,7 +422,7 @@ void TargetThrownItem(struct DungeonEntity *pokemon, struct DungeonEntity *targe
u32 *targetWeight;
gTargetAhead[targetDirection] = TRUE;
gPotentialItemTargetDirections[gNumPotentialTargets] = targetDirection;
- targetWeight = &gPotentialTargetWeights[gNumPotentialTargets];
+ targetWeight = &gPotentialItemTargetWeights[gNumPotentialTargets];
itemWeight = !ignoreRollChance ? EvaluateItem(targetPokemon, item, targetingFlags) : 100;
*targetWeight = itemWeight;
gNumPotentialTargets++;
diff --git a/src/dungeon_ai_movement.c b/src/dungeon_ai_movement.c
index 5fa6d87..49609d7 100644
--- a/src/dungeon_ai_movement.c
+++ b/src/dungeon_ai_movement.c
@@ -49,7 +49,7 @@ u32 sub_8075818(struct DungeonEntity *entity)
entityData = entity->entityData;
if(EntityExists(entity))
{
- tile = GetMapEntityForDungeonEntity(entity);
+ tile = GetMapTileForDungeonEntity_2(entity);
if(HasIQSkill(entity, IQ_SKILL_SUPER_MOBILE))
if(!(tile->tileType & (TILE_TYPE_FLOOR | TILE_TYPE_LIQUID)))
return 1;
@@ -136,7 +136,7 @@ void sub_8075900(struct DungeonEntity *pokemon, u8 r1)
{
if(!gDungeonGlobalData->monsterHouseActive)
{
- if((GetMapEntityForDungeonEntity(pokemon)->tileType & TILE_TYPE_MONSTER_HOUSE))
+ if((GetMapTileForDungeonEntity_2(pokemon)->tileType & TILE_TYPE_MONSTER_HOUSE))
{
// It's a monster house!
SendMessage(GetLeaderEntity(), gPtrItsaMonsterHouseMessage);
diff --git a/src/dungeon_ai_targeting.c b/src/dungeon_ai_targeting.c
new file mode 100644
index 0000000..ec5abd9
--- /dev/null
+++ b/src/dungeon_ai_targeting.c
@@ -0,0 +1,62 @@
+#include "global.h"
+#include "dungeon_ai_targeting.h"
+
+#include "constants/iq_skill.h"
+#include "constants/item.h"
+#include "constants/status.h"
+#include "dungeon_engine.h"
+#include "dungeon_items.h"
+#include "dungeon_map_access.h"
+#include "dungeon_movement.h"
+#include "dungeon_pokemon_attributes.h"
+#include "dungeon_util.h"
+#include "map.h"
+
+const u8 gDirectionBitMasks_2[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
+
+bool8 CanAttackInFront(struct DungeonEntity *pokemon, s32 direction)
+{
+ u8 crossableTerrain = GetCrossableTerrain(pokemon->entityData->entityID);
+ struct MapTile *tile;
+ if (crossableTerrain < CROSSABLE_TERRAIN_CREVICE)
+ {
+ crossableTerrain = CROSSABLE_TERRAIN_CREVICE;
+ }
+ tile = GetMapTile_1(pokemon->posWorld.x + gAdjacentTileOffsets[direction].x,
+ pokemon->posWorld.y + gAdjacentTileOffsets[direction].y);
+ if (!(tile->tileType & TILE_TYPE_MAP_EDGE) &&
+ (tile->pokemon == NULL || GetEntityType(tile->pokemon) == ENTITY_POKEMON))
+ {
+ if (!IsFixedDungeon())
+ {
+ if (pokemon->entityData->transformStatus == TRANSFORM_STATUS_MOBILE ||
+ HasItem(pokemon, ITEM_ID_MOBILE_SCARF))
+ {
+ crossableTerrain = CROSSABLE_TERRAIN_WALL;
+ }
+ else if (HasIQSkill(pokemon, IQ_SKILL_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 (HasIQSkill(pokemon, IQ_SKILL_SUPER_MOBILE))
+ {
+ if ((direction & 1) != 0)
+ {
+ crossableTerrain = CROSSABLE_TERRAIN_CREVICE;
+ }
+ else
+ {
+ crossableTerrain = CROSSABLE_TERRAIN_WALL;
+ }
+ }
+ }
+ tile = GetMapTile_1(pokemon->posWorld.x, pokemon->posWorld.y);
+ if (tile->canMoveAdjacent[crossableTerrain] & gDirectionBitMasks_2[direction & DIRECTION_MASK])
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
diff --git a/src/dungeon_ai.c b/src/dungeon_ai_targeting_1.c
index b2e3b2e..5ff76b0 100644
--- a/src/dungeon_ai.c
+++ b/src/dungeon_ai_targeting_1.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "dungeon_ai.h"
+#include "dungeon_ai_targeting_1.h"
#include "constants/ability.h"
#include "constants/tactic.h"
diff --git a/src/dungeon_ai_1.c b/src/dungeon_ai_targeting_2.c
index 78c899f..83319e1 100644
--- a/src/dungeon_ai_1.c
+++ b/src/dungeon_ai_targeting_2.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "dungeon_ai_1.h"
+#include "dungeon_ai_targeting_2.h"
#include "constants/status.h"
#include "constants/targeting.h"
diff --git a/src/dungeon_capabilities_1.c b/src/dungeon_capabilities_1.c
index e6ec6fa..ef566fe 100644
--- a/src/dungeon_capabilities_1.c
+++ b/src/dungeon_capabilities_1.c
@@ -5,7 +5,7 @@
#include "constants/iq_skill.h"
#include "constants/status.h"
#include "charge_move.h"
-#include "dungeon_ai.h"
+#include "dungeon_ai_targeting_1.h"
#include "dungeon_capabilities.h"
#include "dungeon_engine.h"
#include "dungeon_items.h"
@@ -15,7 +15,7 @@
#include "dungeon_util.h"
#include "map.h"
-const u8 gDirectionBitMasks[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
+const u8 gDirectionBitMasks_1[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
static inline bool8 JoinLocationCannotUseItems(struct DungeonEntityData *pokemonData)
{
@@ -89,7 +89,7 @@ bool8 CannotAttack(struct DungeonEntity *pokemon, bool8 skipSleep)
bool8 CanMoveInDirection(struct DungeonEntity *pokemon, u32 facingDir)
{
u8 crossableTerrain = GetCrossableTerrain(pokemon->entityData->entityID);
- struct MapTile *currentMapTile = GetMapTileAtPosition(pokemon->posWorld.x + gAdjacentTileOffsets[facingDir].x,
+ struct MapTile *currentMapTile = GetMapTile_1(pokemon->posWorld.x + gAdjacentTileOffsets[facingDir].x,
pokemon->posWorld.y + gAdjacentTileOffsets[facingDir].y);
if (currentMapTile->tileType & TILE_TYPE_MAP_EDGE || currentMapTile->pokemon != NULL)
{
@@ -118,8 +118,8 @@ bool8 CanMoveInDirection(struct DungeonEntity *pokemon, u32 facingDir)
}
}
}
- currentMapTile = GetMapTileAtPosition(pokemon->posWorld.x, pokemon->posWorld.y);
- if (!(currentMapTile->canMoveAdjacent[crossableTerrain] & gDirectionBitMasks[facingDir & DIRECTION_MASK]))
+ currentMapTile = GetMapTile_1(pokemon->posWorld.x, pokemon->posWorld.y);
+ if (!(currentMapTile->canMoveAdjacent[crossableTerrain] & gDirectionBitMasks_1[facingDir & DIRECTION_MASK]))
{
return FALSE;
}
diff --git a/src/dungeon_engine.c b/src/dungeon_engine.c
index dd25441..20c3a50 100644
--- a/src/dungeon_engine.c
+++ b/src/dungeon_engine.c
@@ -3,6 +3,15 @@
#include "constants/dungeon.h"
#include "dungeon_global_data.h"
+bool8 IsBossBattle()
+{
+ if (gDungeonGlobalData->bossBattleIndex != 0 && gDungeonGlobalData->bossBattleIndex <= 0x31)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
bool8 IsFixedDungeon()
{
if (gDungeonGlobalData->tileset > DUNGEON_OUT_ON_RESCUE)
@@ -10,4 +19,4 @@ bool8 IsFixedDungeon()
return TRUE;
}
return FALSE;
-}
+} \ No newline at end of file
diff --git a/src/dungeon_map_access.c b/src/dungeon_map_access.c
index 43e0e4d..9ffa633 100644
--- a/src/dungeon_map_access.c
+++ b/src/dungeon_map_access.c
@@ -3,13 +3,25 @@
#include "dungeon_global_data.h"
-extern struct MapTile *gUnknown_203B430;
+extern struct unkStruct_202F190 gUnknown_202F190;
+extern struct unkStruct_202F190 *gUnknown_203B430;
-struct MapTile* GetMapTileAtPosition(s32 x, s32 y)
+struct MapTile* GetMapTile_1(s32 x, s32 y)
{
if (x >= 0 && y >= 0 && x < DUNGEON_MAX_SIZE_X && y < DUNGEON_MAX_SIZE_Y)
{
- return gDungeonGlobalData->mapEntityPointers[y][x];
+ return gDungeonGlobalData->mapTilePointers[y][x];
}
- return gUnknown_203B430;
+ return (struct MapTile*) gUnknown_203B430->unk0;
+}
+
+struct MapTile* GetMapTile_2(s32 x, s32 y)
+{
+ if (x < 0 || y < 0 || x >= DUNGEON_MAX_SIZE_X || y >= DUNGEON_MAX_SIZE_Y)
+ {
+ struct MapTile* tile = (struct MapTile*) gUnknown_202F190.unk0;
+ gUnknown_202F190 = *gUnknown_203B430;
+ return tile;
+ }
+ return gDungeonGlobalData->mapTilePointers[y][x];
}
diff --git a/src/dungeon_range.c b/src/dungeon_range.c
index facd71d..00b827d 100644
--- a/src/dungeon_range.c
+++ b/src/dungeon_range.c
@@ -14,7 +14,7 @@ bool8 InSameRoom_2(struct Position *pos1, struct Position *pos2)
{
visibility = 2;
}
- tile1 = GetMapTileAtPosition(pos1->x, pos1->y);
+ tile1 = GetMapTile_1(pos1->x, pos1->y);
pos1RoomIndex = tile1->roomIndex;
if (pos1RoomIndex == CORRIDOR_ROOM_INDEX)
{
diff --git a/src/dungeon_util.c b/src/dungeon_util.c
index c5b5695..1d443c0 100644
--- a/src/dungeon_util.c
+++ b/src/dungeon_util.c
@@ -14,8 +14,6 @@ const struct Position gAdjacentTileOffsets[] = {
{-1, 1}
};
-extern struct MapTile* GetMapEntity(s16, s16);
-
bool8 EntityExists(struct DungeonEntity *entity)
{
if (!entity)
@@ -55,12 +53,12 @@ struct DungeonEntityData* sub_8045110(struct DungeonEntity *entity)
return entity->entityData;
}
-struct MapTile* GetMapTileForDungeonEntity(struct DungeonEntity *entity)
+struct MapTile* GetMapTileForDungeonEntity_1(struct DungeonEntity *entity)
{
- return GetMapTileAtPosition(entity->posWorld.x, entity->posWorld.y);
+ return GetMapTile_1(entity->posWorld.x, entity->posWorld.y);
}
-struct MapTile* GetMapEntityForDungeonEntity(struct DungeonEntity *entity)
+struct MapTile* GetMapTileForDungeonEntity_2(struct DungeonEntity *entity)
{
- return GetMapEntity(entity->posWorld.x, entity->posWorld.y);
+ return GetMapTile_2(entity->posWorld.x, entity->posWorld.y);
}
diff --git a/src/moves.c b/src/moves.c
index b11622d..2fcbfd0 100644
--- a/src/moves.c
+++ b/src/moves.c
@@ -157,7 +157,7 @@ void InitZeroedPPPokemonMove(struct PokemonMove *move, u16 moveID)
move->PP = 0;
}
-s16 GetMoveTargetingFlags(struct PokemonMove *move, u32 isAI)
+s16 GetMoveTargetingFlags(struct PokemonMove *move, bool32 isAI)
{
return gMovesData[move->moveID].targetingFlags[isAI];
}
@@ -250,9 +250,9 @@ s32 GetMovePower(struct PokemonMove *move)
return gMovesData[move->moveID].power;
}
-u8 GetMoveAccuracy(struct PokemonMove *move, u32 r1)
+s32 GetMoveAccuracy(struct PokemonMove *move, u32 accuracyType)
{
- return gMovesData[move->moveID].accuracy[r1];
+ return gMovesData[move->moveID].accuracy[accuracyType];
}
u32 GetMoveMaxPP(struct PokemonMove *move)
diff --git a/src/status_checker.c b/src/status_checker.c
new file mode 100644
index 0000000..07d6c7f
--- /dev/null
+++ b/src/status_checker.c
@@ -0,0 +1,537 @@
+#include "global.h"
+#include "status_checker.h"
+
+#include "constants/move_id.h"
+#include "constants/status.h"
+#include "constants/type.h"
+#include "constants/weather.h"
+#include "dungeon_engine.h"
+#include "dungeon_global_data.h"
+#include "dungeon_pokemon_attributes.h"
+#include "dungeon_util.h"
+#include "dungeon_visibility.h"
+#include "map.h"
+#include "number_util.h"
+#include "status_checks_1.h"
+#include "tile_types.h"
+#include "trap.h"
+#include "weather.h"
+
+// Array indices correspond to the current dungeon tileset.
+const u8 gDungeonCamouflageTypes[76] = {
+ TYPE_WATER,
+ TYPE_GRASS,
+ TYPE_ROCK,
+ TYPE_ROCK,
+ TYPE_ROCK,
+ TYPE_ROCK,
+ TYPE_ROCK,
+ TYPE_ROCK,
+ TYPE_NORMAL,
+ TYPE_ROCK,
+ TYPE_GRASS,
+ TYPE_GROUND,
+ TYPE_NORMAL,
+ TYPE_NORMAL,
+ TYPE_GRASS,
+ TYPE_ICE,
+ TYPE_NORMAL,
+ TYPE_ROCK,
+ TYPE_ICE,
+ TYPE_ICE,
+ TYPE_GRASS,
+ TYPE_GROUND,
+ TYPE_ROCK,
+ TYPE_NORMAL,
+ TYPE_ICE,
+ TYPE_GRASS,
+ TYPE_GRASS,
+ TYPE_NORMAL,
+ TYPE_ROCK,
+ TYPE_GRASS,
+ TYPE_ROCK,
+ TYPE_ROCK,
+ TYPE_GRASS,
+ TYPE_GRASS,
+ TYPE_ROCK,
+ TYPE_NORMAL,
+ TYPE_ICE,
+ TYPE_ROCK,
+ TYPE_ROCK,
+ TYPE_ROCK,
+ TYPE_ROCK,
+ TYPE_GRASS,
+ TYPE_ROCK,
+ TYPE_ROCK,
+ TYPE_GROUND,
+ TYPE_NORMAL,
+ TYPE_ROCK,
+ TYPE_ICE,
+ TYPE_ROCK,
+ TYPE_WATER,
+ TYPE_GRASS,
+ TYPE_GRASS,
+ TYPE_GROUND,
+ TYPE_WATER,
+ TYPE_ROCK,
+ TYPE_ROCK,
+ TYPE_GRASS,
+ TYPE_NORMAL,
+ TYPE_NORMAL,
+ TYPE_ROCK,
+ TYPE_GRASS,
+ TYPE_ROCK,
+ TYPE_GRASS,
+ TYPE_GRASS,
+ TYPE_ROCK,
+ TYPE_GRASS,
+ TYPE_ROCK,
+ TYPE_ROCK,
+ TYPE_NORMAL,
+ TYPE_NORMAL,
+ TYPE_ROCK,
+ TYPE_NORMAL,
+ TYPE_ICE,
+ TYPE_WATER,
+ TYPE_WATER,
+ TYPE_ROCK
+};
+
+bool8 CanUseOnSelfWithStatusChecker(struct DungeonEntity *pokemon, struct PokemonMove *move)
+{
+ struct DungeonEntityData *pokemonData = pokemon->entityData;
+ switch (move->moveID)
+ {
+ case MOVE_HAIL:
+ if (GetWeather(pokemon) == WEATHER_HAIL)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_RAGE:
+ if (pokemonData->chargingStatus == CHARGING_STATUS_RAGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_COUNTER:
+ case MOVE_PURSUIT:
+ if (pokemonData->protectionStatus == PROTECTION_STATUS_COUNTER)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_MIRROR_MOVE:
+ if (pokemonData->protectionStatus == PROTECTION_STATUS_MIRROR_MOVE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_HOWL:
+ case MOVE_MEDITATE:
+ case MOVE_SHARPEN:
+ if (pokemonData->attackStage >= MAX_STAT_STAGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_BELLY_DRUM:
+ if (pokemonData->attackStage >= MAX_STAT_STAGE || RoundUpFixedPoint(pokemonData->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 (pokemonData->defenseStage >= MAX_STAT_STAGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_BIDE:
+ case MOVE_REVENGE:
+ if (pokemonData->chargingStatus == CHARGING_STATUS_BIDE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_AGILITY:
+ {
+ u32 r1;
+#ifndef NONMATCHING
+ asm("":"=r"(r1));
+#else
+ r1 = 0;
+#endif
+ if (pokemon->entityData->movementSpeed >= MAX_MOVEMENT_SPEED)
+ {
+ r1 = !r1;
+ return FALSE;
+ }
+ break;
+ }
+ case MOVE_LOCK_ON:
+ case MOVE_MIND_READER:
+ if (pokemonData->moveStatus == MOVE_STATUS_SURE_SHOT)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_COSMIC_POWER:
+ if (pokemonData->defenseStage >= MAX_STAT_STAGE && pokemonData->specialDefenseStage >= MAX_STAT_STAGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_ENDURE:
+ if (pokemonData->protectionStatus == PROTECTION_STATUS_ENDURE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_CHARGE:
+ if (pokemonData->chargingStatus == CHARGING_STATUS_CHARGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_MIST:
+ if (pokemonData->protectionStatus == PROTECTION_STATUS_MIST)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_LIGHT_SCREEN:
+ if (pokemonData->protectionStatus == PROTECTION_STATUS_LIGHT_SCREEN)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_MINIMIZE:
+ if (pokemonData->evasionStage >= MAX_STAT_STAGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_INGRAIN:
+ if (pokemonData->immobilizeStatus == IMMOBILIZE_STATUS_INGRAIN || pokemonData->maxHP / 2 < pokemonData->HP)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_SWALLOW:
+ if (pokemonData->maxHP <= pokemonData->HP || pokemonData->stockpileCount == 0)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_SPIT_UP:
+ if (pokemonData->stockpileCount == 0)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_DOOM_DESIRE:
+ case MOVE_FUTURE_SIGHT:
+ if (pokemonData->moveStatus == MOVE_STATUS_SET_DAMAGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_BULK_UP:
+ if (pokemonData->attackStage >= MAX_STAT_STAGE && pokemonData->defenseStage >= MAX_STAT_STAGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_CAMOUFLAGE:
+ if (HasType(pokemon, gDungeonCamouflageTypes[gDungeonGlobalData->tileset]))
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_TAIL_GLOW:
+ if (pokemonData->specialAttackStage >= MAX_STAT_STAGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_DESTINY_BOND:
+ if (pokemonData->linkedStatus == LINKED_STATUS_DESTINY_BOND)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_MIRROR_COAT:
+ if (pokemonData->protectionStatus == PROTECTION_STATUS_MIRROR_COAT)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_REFLECT:
+ if (pokemonData->protectionStatus == PROTECTION_STATUS_REFLECT)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_DRAGON_DANCE:
+ if (pokemonData->attackStage >= MAX_STAT_STAGE && pokemon->entityData->movementSpeed >= MAX_MOVEMENT_SPEED)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_MAGIC_COAT:
+ if (pokemonData->protectionStatus == PROTECTION_STATUS_MAGIC_COAT)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_DETECT:
+ case MOVE_PROTECT:
+ if (pokemonData->protectionStatus == PROTECTION_STATUS_PROTECT)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_RAIN_DANCE:
+ if (GetWeather(pokemon) == WEATHER_RAIN)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_SANDSTORM:
+ if (GetWeather(pokemon) == WEATHER_SANDSTORM)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_SUNNY_DAY:
+ if (GetWeather(pokemon) == WEATHER_SUNNY)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_SAFEGUARD:
+ if (pokemonData->protectionStatus == PROTECTION_STATUS_SAFEGUARD)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_INVISIFY:
+ if (pokemonData->transformStatus == TRANSFORM_STATUS_INVISIBLE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_FOCUS_ENERGY:
+ if (pokemonData->moveStatus == MOVE_STATUS_FOCUS_ENERGY)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_TAKEAWAY:
+ if (pokemonData->heldItem.itemFlags & ITEM_FLAG_EXISTS)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_REST:
+ if (!HasQuarterHPOrLess(pokemon) && !HasNegativeStatus(pokemon))
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_DIVE:
+ if (IsTileGround(GetMapTileForDungeonEntity_2(pokemon)))
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_DIG:
+ {
+ struct MapTile *tile = GetMapTileForDungeonEntity_2(pokemon);
+ if (!IsTileGround(tile) || (tile->tileType & (TILE_TYPE_FLOOR | TILE_TYPE_LIQUID)) != TILE_TYPE_FLOOR)
+ {
+ return FALSE;
+ }
+ break;
+ }
+ case MOVE_TRAP_BUSTER:
+ {
+ struct DungeonEntity *mapObject = GetMapTileForDungeonEntity_2(pokemon)->mapObject;
+ if (mapObject == NULL || GetEntityType(mapObject) != ENTITY_TRAP)
+ {
+ return FALSE;
+ }
+ break;
+ }
+ case MOVE_MUD_SPORT:
+ if (gDungeonGlobalData->mudSportTurnsLeft > 0)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_WATER_SPORT:
+ if (gDungeonGlobalData->waterSportTurnsLeft > 0)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_GRUDGE:
+ if (pokemonData->grudgeStatus)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_DECOY_MAKER:
+ case MOVE_FOLLOW_ME:
+ case MOVE_SUBSTITUTE:
+ if (gDungeonGlobalData->decoyActive)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_STOCKPILE:
+ if (pokemonData->stockpileCount >= MAX_STOCKPILE_COUNT)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_CLEANSE:
+ if (pokemonData->heldItem.itemFlags & ITEM_FLAG_EXISTS &&
+ !(pokemonData->heldItem.itemFlags & ITEM_FLAG_STICKY))
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_DOUBLE_TEAM:
+ if (pokemonData->evasionStage >= MAX_STAT_STAGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_GROWTH:
+ if (pokemonData->specialAttackStage >= MAX_STAT_STAGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_SWORDS_DANCE:
+ if (pokemonData->attackStage >= MAX_STAT_STAGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_WISH:
+ if (pokemonData->isEnemy || pokemonData->protectionStatus == PROTECTION_STATUS_WISH)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_TRANSFORM:
+ if (pokemonData->transformStatus == TRANSFORM_STATUS_TRANSFORMED)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_SPIKES:
+ if (!CanLayTrap(&pokemon->posWorld))
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_CALM_MIND:
+ if (pokemonData->specialAttackStage < MAX_STAT_STAGE)
+ {
+ break;
+ }
+ case MOVE_AMNESIA:
+ if (pokemonData->specialDefenseStage >= MAX_STAT_STAGE)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_SNATCH:
+ if (pokemonData->waitingStatus == WAITING_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 (IsBossBattle())
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_CONVERSION_2:
+ if (pokemonData->protectionStatus == PROTECTION_STATUS_CONVERSION_2)
+ {
+ return FALSE;
+ }
+ break;
+ case MOVE_HELPING_HAND:
+ if (pokemonData->isEnemy)
+ {
+ s32 i;
+ for (i = 0; i < DUNGEON_MAX_WILD_POKEMON; i++)
+ {
+ struct DungeonEntity *target = gDungeonGlobalData->wildPokemon[i];
+ if (EntityExists(target) && target != pokemon && CanSee(pokemon, target))
+ {
+ if (target->entityData->attackStage >= MAX_STAT_STAGE)
+ {
+ continue;
+ }
+ if (target->entityData->specialAttackStage < MAX_STAT_STAGE)
+ {
+ break;
+ }
+ }
+ }
+ if (i == DUNGEON_MAX_WILD_POKEMON)
+ {
+ return FALSE;
+ }
+ break;
+ }
+ else
+ {
+ s32 i;
+ for (i = 0; i < MAX_TEAM_MEMBERS; i++)
+ {
+ struct DungeonEntity *target = gDungeonGlobalData->teamPokemon[i];
+ if (EntityExists(target) && target != pokemon && CanSee(pokemon, target))
+ {
+ if (target->entityData->attackStage >= MAX_STAT_STAGE)
+ {
+ continue;
+ }
+ if (target->entityData->specialAttackStage < MAX_STAT_STAGE)
+ {
+ break;
+ }
+ }
+ }
+ if (i == MAX_TEAM_MEMBERS)
+ {
+ return FALSE;
+ }
+ break;
+ }
+ }
+ return TRUE;
+}
diff --git a/src/status_checks_1.c b/src/status_checks_1.c
index 9d0bfe8..c49e127 100644
--- a/src/status_checks_1.c
+++ b/src/status_checks_1.c
@@ -53,3 +53,19 @@ bool8 IsSleeping(struct DungeonEntity *pokemon)
}
return TRUE;
}
+
+bool8 HasQuarterHPOrLess(struct DungeonEntity* pokemon)
+{
+ struct DungeonEntityData *pokemonData = pokemon->entityData;
+ struct DungeonEntityData *pokemonData2 = pokemon->entityData;
+ s32 maxHP = pokemonData->maxHP;
+ if (maxHP < 0)
+ {
+ maxHP += 3;
+ }
+ if (pokemonData2->HP <= maxHP >> 2)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/src/targeting_flags.c b/src/targeting_flags.c
index f315cdf..bc6010b 100644
--- a/src/targeting_flags.c
+++ b/src/targeting_flags.c
@@ -6,7 +6,7 @@
#include "dungeon_pokemon_attributes.h"
#include "moves.h"
-s16 GetMoveTargetingFlagsForPokemon(struct DungeonEntity *pokemon, struct PokemonMove *move, u32 isAI)
+s16 GetMoveTargetingFlagsForPokemon(struct DungeonEntity *pokemon, struct PokemonMove *move, bool32 isAI)
{
if (move->moveID == MOVE_CURSE && !isAI && !HasType(pokemon, TYPE_GHOST))
{
diff --git a/src/tile_types.c b/src/tile_types.c
new file mode 100644
index 0000000..cad63fa
--- /dev/null
+++ b/src/tile_types.c
@@ -0,0 +1,114 @@
+#include "global.h"
+#include "tile_types.h"
+
+#include "dungeon_global_data.h"
+
+const u8 gDungeonWaterType[] = {
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_WATER,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_WATER,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_LAVA,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_LAVA,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_LAVA,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_LAVA,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+ DUNGEON_WATER_TYPE_NONE,
+};
+
+bool8 IsTileGround(struct MapTile* tile)
+{
+ bool8 isGround = FALSE;
+ if (IsWaterTileset())
+ {
+ if (!(tile->tileType & (TILE_TYPE_FLOOR | TILE_TYPE_LIQUID)))
+ {
+ isGround = TRUE;
+ }
+ }
+ else if ((tile->tileType & (TILE_TYPE_FLOOR | TILE_TYPE_LIQUID)) != TILE_TYPE_LIQUID ||
+ gDungeonWaterType[gDungeonGlobalData->tileset] == DUNGEON_WATER_TYPE_LAVA)
+ {
+ isGround = TRUE;
+ }
+ return isGround;
+}
+
+bool8 IsWaterTileset()
+{
+ if (gDungeonGlobalData->tileset == 0 ||
+ gDungeonGlobalData->tileset == 0x31 ||
+ gDungeonGlobalData->tileset == 0x20 ||
+ gDungeonGlobalData->tileset == 0x21 ||
+ gDungeonGlobalData->tileset == 0x36)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/src/trap.c b/src/trap.c
new file mode 100644
index 0000000..e054706
--- /dev/null
+++ b/src/trap.c
@@ -0,0 +1,27 @@
+#include "global.h"
+#include "trap.h"
+
+#include "dungeon_map_access.h"
+#include "dungeon_util.h"
+#include "map.h"
+
+bool8 CanLayTrap(struct Position *pos)
+{
+ struct MapTile *tile = GetMapTile_2(pos->x, pos->y);
+ if (tile->tileType & TILE_TYPE_STAIRS ||
+ tile->roomIndex == CORRIDOR_ROOM_INDEX ||
+ tile->tileType & TILE_TYPE_ROOM_EXIT)
+ {
+ return FALSE;
+ }
+ if (tile->tileType & TILE_TYPE_SHOP)
+ {
+ return FALSE;
+ }
+ if ((tile->tileType & (TILE_TYPE_FLOOR | TILE_TYPE_LIQUID)) != TILE_TYPE_FLOOR ||
+ (tile->mapObject != NULL && GetEntityType(tile->mapObject) != ENTITY_TRAP))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
diff --git a/src/weather.c b/src/weather.c
new file mode 100644
index 0000000..3989790
--- /dev/null
+++ b/src/weather.c
@@ -0,0 +1,16 @@
+#include "global.h"
+#include "weather.h"
+
+#include "constants/item.h"
+#include "constants/weather.h"
+#include "dungeon_global_data.h"
+#include "dungeon_items.h"
+
+u8 GetWeather(struct DungeonEntity* pokemon)
+{
+ if (pokemon != NULL && HasItem(pokemon, ITEM_ID_WEATHER_BAND))
+ {
+ return WEATHER_CLEAR;
+ }
+ return gDungeonGlobalData->weather;
+}