mirror of
https://github.com/pret/pmd-sky.git
synced 2026-10-02 15:18:16 -05:00
Decomped TargetRegularAttack
This commit is contained in:
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
.public CanAttackInDirection
|
||||
.public DIRECTIONS_XY
|
||||
.public DungeonRandInt
|
||||
.public GetTile
|
||||
.public GetTreatmentBetweenMonsters
|
||||
.public IqSkillIsEnabled
|
||||
.public IsBlinded
|
||||
.public ov29_0230F8D0
|
||||
.public WeightMoveWithIqSkills
|
||||
@@ -1,168 +0,0 @@
|
||||
.include "asm/macros.inc"
|
||||
.include "overlay_29_0231A460.inc"
|
||||
|
||||
.text
|
||||
|
||||
arm_func_start TargetRegularAttack
|
||||
TargetRegularAttack: ; 0x0231A460
|
||||
stmdb sp!, {r3, r4, r5, r6, r7, r8, sb, sl, fp, lr}
|
||||
sub sp, sp, #0x48
|
||||
mov sl, r0
|
||||
ldr r3, [sl, #0xb4]
|
||||
mov sb, r1
|
||||
str r2, [sp]
|
||||
mov r1, #1
|
||||
ldrb r6, [r3, #0x4c]
|
||||
mov r4, #0
|
||||
bl IsBlinded
|
||||
cmp r0, #0
|
||||
movne r7, #1
|
||||
bne _0231A4AC
|
||||
mov r0, sl
|
||||
mov r1, #0xf
|
||||
bl ov29_0230F8D0
|
||||
cmp r0, #0xff
|
||||
mov r7, #8
|
||||
movne r6, r0
|
||||
_0231A4AC:
|
||||
mov r0, sl
|
||||
mov r1, #9
|
||||
bl IqSkillIsEnabled
|
||||
cmp r0, #0
|
||||
bne _0231A4D4
|
||||
mov r0, sl
|
||||
mov r1, #0xa
|
||||
bl IqSkillIsEnabled
|
||||
cmp r0, #0
|
||||
beq _0231A4DC
|
||||
_0231A4D4:
|
||||
mov r2, #1
|
||||
b _0231A4E0
|
||||
_0231A4DC:
|
||||
mov r2, #0
|
||||
_0231A4E0:
|
||||
mov r0, sl
|
||||
mov r1, #8
|
||||
and fp, r2, #0xff
|
||||
bl IqSkillIsEnabled
|
||||
str r0, [sp, #4]
|
||||
mov r5, #0
|
||||
b _0231A5CC
|
||||
_0231A4FC:
|
||||
and r6, r6, #7
|
||||
ldr r1, _0231A690 ; =DIRECTIONS_XY
|
||||
mov r2, r6, lsl #2
|
||||
ldrsh r3, [r1, r2]
|
||||
add r1, r1, r6, lsl #2
|
||||
ldrsh r0, [sl, #4]
|
||||
ldrsh r2, [sl, #6]
|
||||
ldrsh r1, [r1, #2]
|
||||
add r0, r0, r3
|
||||
add r1, r2, r1
|
||||
bl GetTile
|
||||
ldr r8, [r0, #0xc]
|
||||
cmp r8, #0
|
||||
beq _0231A5C4
|
||||
ldr r0, [r8]
|
||||
cmp r0, #1
|
||||
bne _0231A5C4
|
||||
mov r0, sl
|
||||
mov r1, r6
|
||||
bl CanAttackInDirection
|
||||
cmp r0, #0
|
||||
beq _0231A5C4
|
||||
ldr r3, [sp]
|
||||
mov r0, sl
|
||||
mov r1, r8
|
||||
mov r2, #0
|
||||
bl GetTreatmentBetweenMonsters
|
||||
cmp r0, #1
|
||||
bne _0231A5C4
|
||||
ldr r0, [sp, #4]
|
||||
cmp r0, #0
|
||||
beq _0231A58C
|
||||
ldr r0, [r8, #0xb4]
|
||||
ldrb r0, [r0, #0xc4]
|
||||
cmp r0, #1
|
||||
beq _0231A5C4
|
||||
_0231A58C:
|
||||
add r0, sp, #0x28
|
||||
mov r1, #0
|
||||
str r6, [r0, r4, lsl #2]
|
||||
mov r2, r8
|
||||
mov r0, sl
|
||||
mov r3, r1
|
||||
bl WeightMoveWithIqSkills
|
||||
add r1, sp, #8
|
||||
cmp fp, #0
|
||||
str r0, [r1, r4, lsl #2]
|
||||
streq r6, [sb]
|
||||
moveq r0, #1
|
||||
beq _0231A688
|
||||
add r4, r4, #1
|
||||
_0231A5C4:
|
||||
add r5, r5, #1
|
||||
add r6, r6, #1
|
||||
_0231A5CC:
|
||||
cmp r5, r7
|
||||
blt _0231A4FC
|
||||
cmp r4, #0
|
||||
mov r0, #0
|
||||
beq _0231A688
|
||||
mov r5, r0
|
||||
mov r3, r0
|
||||
add r2, sp, #8
|
||||
b _0231A600
|
||||
_0231A5F0:
|
||||
ldr r1, [r2, r3, lsl #2]
|
||||
add r3, r3, #1
|
||||
cmp r5, r1
|
||||
movlt r5, r1
|
||||
_0231A600:
|
||||
cmp r3, r4
|
||||
blt _0231A5F0
|
||||
mov r6, #0
|
||||
mov r1, r6
|
||||
add r3, sp, #8
|
||||
b _0231A628
|
||||
_0231A618:
|
||||
ldr r2, [r3, r6, lsl #2]
|
||||
cmp r5, r2
|
||||
strne r1, [r3, r6, lsl #2]
|
||||
add r6, r6, #1
|
||||
_0231A628:
|
||||
cmp r6, r4
|
||||
blt _0231A618
|
||||
mov r3, #0
|
||||
add r2, sp, #8
|
||||
b _0231A648
|
||||
_0231A63C:
|
||||
ldr r1, [r2, r3, lsl #2]
|
||||
add r3, r3, #1
|
||||
add r0, r0, r1
|
||||
_0231A648:
|
||||
cmp r3, r4
|
||||
blt _0231A63C
|
||||
bl DungeonRandInt
|
||||
mov r3, #0
|
||||
add r2, sp, #8
|
||||
b _0231A670
|
||||
_0231A660:
|
||||
ldr r1, [r2, r3, lsl #2]
|
||||
subs r0, r0, r1
|
||||
bmi _0231A678
|
||||
add r3, r3, #1
|
||||
_0231A670:
|
||||
cmp r3, r4
|
||||
blt _0231A660
|
||||
_0231A678:
|
||||
add r1, sp, #0x28
|
||||
ldr r1, [r1, r3, lsl #2]
|
||||
mov r0, #1
|
||||
str r1, [sb]
|
||||
_0231A688:
|
||||
add sp, sp, #0x48
|
||||
ldmia sp!, {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc}
|
||||
.align 2, 0
|
||||
_0231A690: .word DIRECTIONS_XY
|
||||
arm_func_end TargetRegularAttack
|
||||
@@ -53,5 +53,19 @@ s32 TryAddTargetToAiTargetList(s32 current_num_targets, s32 move_ai_range, struc
|
||||
bool8 IsAiTargetEligible(s32 move_ai_range, struct entity *user, struct entity *target, struct move *move, bool8 check_all_conditions);
|
||||
// Calculates a move weight used for deciding which target the move should be used on. If the user is an ally, the target is an enemy Pokémon, and the user has Exp. Go-Getter, Efficiency Expert, or Weak-Type Picker enabled, this function calculates a move weight based on that IQ skill's functionality. Otherwise, this function returns a weight of 1.
|
||||
s32 WeightMoveWithIqSkills(struct entity *user, s32 move_ai_range, struct entity *target, enum type_id move_type);
|
||||
// Decides which direction the AI will use its regular attack in.
|
||||
// pokemon: User entity pointer
|
||||
// target_dir: [output] direction that the regular attack should be targeted at.
|
||||
// skip_petrified: If true, the AI will ignore enemies that are petrified. If false, the AI will include petrified enemies when targeting.
|
||||
// return: True if there is a target for the regular attack, false if there is no target.
|
||||
bool8 TargetRegularAttack(struct entity *pokemon, u32 *target_dir, bool8 skip_petrified);
|
||||
// Returns true if the target is within range of the user's move, false otherwise.
|
||||
// If the user does not have Course Checker, it simply checks if the distance between user and target is less or equal than the move range.
|
||||
// Otherwise, it will iterate through all tiles in the direction specified, checking for walls or other monsters in the way, and return false if they are found.
|
||||
// user: user pointer
|
||||
// target: target pointer
|
||||
// direction: direction ID
|
||||
// n_tiles: move range (in number of tiles)
|
||||
bool8 IsTargetInRange(struct entity *user, struct entity *target, s32 direction, s32 n_tiles);
|
||||
|
||||
#endif //PMDSKY_DUNGEON_AI_ATTACK_H
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef PMDSKY_DUNGEON_AI_ATTACK_1_H
|
||||
#define PMDSKY_DUNGEON_AI_ATTACK_1_H
|
||||
|
||||
#include "dungeon_mode.h"
|
||||
|
||||
// Returns true if the target is within range of the user's move, false otherwise.
|
||||
// If the user does not have Course Checker, it simply checks if the distance between user and target is less or equal than the move range.
|
||||
// Otherwise, it will iterate through all tiles in the direction specified, checking for walls or other monsters in the way, and return false if they are found.
|
||||
// user: user pointer
|
||||
// target: target pointer
|
||||
// direction: direction ID
|
||||
// n_tiles: move range (in number of tiles)
|
||||
bool8 IsTargetInRange(struct entity *user, struct entity *target, s32 direction, s32 n_tiles);
|
||||
|
||||
#endif //PMDSKY_DUNGEON_AI_ATTACK_1_H
|
||||
2
main.lsf
2
main.lsf
@@ -524,8 +524,6 @@ Overlay OVY_29
|
||||
Object src/inflict_status.o
|
||||
Object src/dungeon_ai_attack.o
|
||||
Object asm/overlay_29_bss_0237CA18.o
|
||||
Object asm/overlay_29_0231A460.o
|
||||
Object src/dungeon_ai_attack_1.o
|
||||
Object asm/overlay_29_0231A7A0.o
|
||||
Object src/overlay_29_0231A87C.o
|
||||
Object asm/overlay_29_0231A8A0.o
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "dungeon_ai_attack.h"
|
||||
#include "dg_random.h"
|
||||
#include "dungeon_ai_attack_1.h"
|
||||
#include "dungeon_ai_targeting_1.h"
|
||||
#include "dungeon_capabilities.h"
|
||||
#include "dungeon_capabilities_4.h"
|
||||
@@ -34,6 +33,8 @@ s32 AI_POTENTIAL_ATTACK_TARGET_WEIGHTS[NUM_DIRECTIONS] = {0};
|
||||
struct entity *AI_POTENTIAL_ATTACK_TARGETS[NUM_DIRECTIONS] = {0};
|
||||
#endif
|
||||
|
||||
extern enum direction_id ov29_0230F8D0(struct entity *pokemon, s32);
|
||||
|
||||
void ResetAiCanAttackInDirection()
|
||||
{
|
||||
for (s32 i = 0; i < NUM_DIRECTIONS; i++)
|
||||
@@ -932,3 +933,121 @@ s32 WeightMoveWithIqSkills(struct entity *user, s32 move_ai_range, struct entity
|
||||
|
||||
return weight;
|
||||
}
|
||||
|
||||
bool8 TargetRegularAttack(struct entity *pokemon, u32 *target_dir, bool8 skip_petrified)
|
||||
{
|
||||
s32 num_potential_targets = 0;
|
||||
s32 i;
|
||||
struct monster *pokemon_info = GetEntInfo(pokemon);
|
||||
s32 direction = pokemon_info->action.direction;
|
||||
s32 face_turn_limit;
|
||||
if (IsBlinded(pokemon, TRUE))
|
||||
face_turn_limit = 1;
|
||||
else
|
||||
{
|
||||
enum direction_id ov29_0230F8D0_result = ov29_0230F8D0(pokemon, 15);
|
||||
face_turn_limit = 8;
|
||||
if (ov29_0230F8D0_result != 0xFF)
|
||||
direction = ov29_0230F8D0_result;
|
||||
}
|
||||
|
||||
s32 potential_attack_target_directions[NUM_DIRECTIONS];
|
||||
s32 potential_attack_target_weights[NUM_DIRECTIONS];
|
||||
bool8 has_targeting_iq = IqSkillIsEnabled(pokemon, IQ_EXP_GO_GETTER) || IqSkillIsEnabled(pokemon, IQ_EFFICIENCY_EXPERT);
|
||||
bool8 has_status_checker = IqSkillIsEnabled(pokemon, IQ_STATUS_CHECKER);
|
||||
for (i = 0; i < face_turn_limit; i++, direction++)
|
||||
{
|
||||
struct entity *target;
|
||||
direction &= DIRECTION_MASK;
|
||||
target = GetTile(pokemon->pos.x + DIRECTIONS_XY[direction].x, pokemon->pos.y + DIRECTIONS_XY[direction].y)->monster;
|
||||
if (target != NULL &&
|
||||
GetEntityType(target) == ENTITY_MONSTER &&
|
||||
CanAttackInDirection(pokemon, direction) &&
|
||||
GetTreatmentBetweenMonsters(pokemon, target, FALSE, skip_petrified) == TREATMENT_TREAT_AS_ENEMY &&
|
||||
(!has_status_checker || GetEntInfo(target)->frozen_class_status.freeze != STATUS_FROZEN_FROZEN))
|
||||
{
|
||||
potential_attack_target_directions[num_potential_targets] = direction;
|
||||
potential_attack_target_weights[num_potential_targets] = WeightMoveWithIqSkills(pokemon, TARGET_ENEMIES, target, TYPE_NONE);
|
||||
if (!has_targeting_iq)
|
||||
{
|
||||
*target_dir = direction;
|
||||
return TRUE;
|
||||
}
|
||||
num_potential_targets++;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_potential_targets == 0)
|
||||
return FALSE;
|
||||
|
||||
s32 total_weight = 0;
|
||||
s32 max_weight = 0;
|
||||
s32 weight_counter;
|
||||
for (i = 0; i < num_potential_targets; i++)
|
||||
{
|
||||
if (max_weight < potential_attack_target_weights[i])
|
||||
max_weight = potential_attack_target_weights[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < num_potential_targets; i++)
|
||||
{
|
||||
if (max_weight != potential_attack_target_weights[i])
|
||||
potential_attack_target_weights[i] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_potential_targets; i++)
|
||||
{
|
||||
total_weight += potential_attack_target_weights[i];
|
||||
}
|
||||
|
||||
weight_counter = DungeonRandInt(total_weight);
|
||||
for (i = 0; i < num_potential_targets; i++)
|
||||
{
|
||||
weight_counter -= potential_attack_target_weights[i];
|
||||
if (weight_counter < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
*target_dir = potential_attack_target_directions[i];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool8 IsTargetInRange(struct entity *user, struct entity *target, s32 direction, s32 n_tiles)
|
||||
{
|
||||
s32 effective_max_range = Max(abs(user->pos.x - target->pos.x), abs(user->pos.y - target->pos.y));
|
||||
|
||||
if (effective_max_range > n_tiles)
|
||||
effective_max_range = n_tiles;
|
||||
|
||||
if (!IqSkillIsEnabled(user, IQ_COURSE_CHECKER))
|
||||
// BUG: effective_max_range is already capped at max_range, 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.
|
||||
return effective_max_range <= n_tiles;
|
||||
|
||||
s32 i;
|
||||
s32 current_pos_x = user->pos.x;
|
||||
s32 current_pos_y = user->pos.y;
|
||||
s32 adjacent_tile_offset_x = DIRECTIONS_XY[direction].x;
|
||||
s32 adjacent_tile_offset_y = DIRECTIONS_XY[direction].y;
|
||||
for (i = 0; i <= effective_max_range; i++)
|
||||
{
|
||||
current_pos_x += adjacent_tile_offset_x;
|
||||
current_pos_y += adjacent_tile_offset_y;
|
||||
if (current_pos_x < 1 || current_pos_y < 1 ||
|
||||
current_pos_x >= DUNGEON_MAX_SIZE_X - 1 || current_pos_y >= DUNGEON_MAX_SIZE_Y - 1)
|
||||
break;
|
||||
|
||||
const struct tile *map_tile = GetTile(current_pos_x, current_pos_y);
|
||||
if (GetTerrainType(map_tile) == TERRAIN_TYPE_WALL)
|
||||
return FALSE;
|
||||
|
||||
if (map_tile->monster == target)
|
||||
return TRUE;
|
||||
|
||||
if (map_tile->monster != NULL)
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#include "dungeon_ai_attack_1.h"
|
||||
#include "dungeon.h"
|
||||
#include "dungeon_map_access.h"
|
||||
#include "dungeon_pokemon_attributes.h"
|
||||
#include "dungeon_pokemon_attributes_1.h"
|
||||
#include "dungeon_util.h"
|
||||
#include "dungeon_util_static.h"
|
||||
#include "main_0208655C.h"
|
||||
#include "util.h"
|
||||
|
||||
bool8 IsTargetInRange(struct entity *user, struct entity *target, s32 direction, s32 n_tiles)
|
||||
{
|
||||
s32 effective_max_range = Max(abs(user->pos.x - target->pos.x), abs(user->pos.y - target->pos.y));
|
||||
|
||||
if (effective_max_range > n_tiles)
|
||||
effective_max_range = n_tiles;
|
||||
|
||||
if (!IqSkillIsEnabled(user, IQ_COURSE_CHECKER))
|
||||
// BUG: effective_max_range is already capped at max_range, 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.
|
||||
return effective_max_range <= n_tiles;
|
||||
|
||||
s32 i;
|
||||
s32 current_pos_x = user->pos.x;
|
||||
s32 current_pos_y = user->pos.y;
|
||||
s32 adjacent_tile_offset_x = DIRECTIONS_XY[direction].x;
|
||||
s32 adjacent_tile_offset_y = DIRECTIONS_XY[direction].y;
|
||||
for (i = 0; i <= effective_max_range; i++)
|
||||
{
|
||||
current_pos_x += adjacent_tile_offset_x;
|
||||
current_pos_y += adjacent_tile_offset_y;
|
||||
if (current_pos_x < 1 || current_pos_y < 1 ||
|
||||
current_pos_x >= DUNGEON_MAX_SIZE_X - 1 || current_pos_y >= DUNGEON_MAX_SIZE_Y - 1)
|
||||
break;
|
||||
|
||||
const struct tile *map_tile = GetTile(current_pos_x, current_pos_y);
|
||||
if (GetTerrainType(map_tile) == TERRAIN_TYPE_WALL)
|
||||
return FALSE;
|
||||
|
||||
if (map_tile->monster == target)
|
||||
return TRUE;
|
||||
|
||||
if (map_tile->monster != NULL)
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#define REGULAR_ATTACK_INDEX 4
|
||||
|
||||
extern bool8 TargetRegularAttack(struct entity *pokemon, u32 *target_dir, bool8 skip_petrified);
|
||||
extern void SetActionRegularAttack(struct action_data *monster_action, u8 direction);
|
||||
extern void SetActionStruggle(struct action_data *monster_action, u8 direction);
|
||||
extern bool8 CanAiUseMove(struct entity *monster, u32 move_index, bool8 extra_checks);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include "dg_random.h"
|
||||
#include "dungeon.h"
|
||||
#include "dungeon_ai_attack.h"
|
||||
#include "dungeon_ai_attack_1.h"
|
||||
#include "dungeon_ai_item_weight.h"
|
||||
#include "dungeon_ai_items.h"
|
||||
#include "dungeon_ai_targeting_1.h"
|
||||
|
||||
Reference in New Issue
Block a user