diff --git a/asm/include/itcm_01FFB2C8.inc b/asm/include/itcm_01FFB2C8.inc deleted file mode 100644 index 935ee899..00000000 --- a/asm/include/itcm_01FFB2C8.inc +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -.public CanAiMonsterMoveInDirection -.public DIRECTIONS_XY -.public DUNGEON_PTR -.public DungeonRandInt -.public FACING_DIRECTION_INCREMENTS -.public GetTile -.public IsAtJunction diff --git a/asm/include/itcm_01FFB62C.inc b/asm/include/itcm_01FFB62C.inc new file mode 100644 index 00000000..6f70f09b --- /dev/null +++ b/asm/include/itcm_01FFB62C.inc @@ -0,0 +1 @@ +#pragma once diff --git a/asm/itcm_01FFB2C8.s b/asm/itcm_01FFB2C8.s deleted file mode 100644 index ce2ad275..00000000 --- a/asm/itcm_01FFB2C8.s +++ /dev/null @@ -1,280 +0,0 @@ - .include "asm/macros.inc" - .include "itcm_01FFB2C8.inc" - - .section .itcm,4,1,4 - - arm_func_start CalculateAiTargetPos -CalculateAiTargetPos: ; 0x01FFB2C8 - stmdb sp!, {r3, r4, r5, r6, r7, r8, sb, sl, fp, lr} - ldr r1, _01FFB61C ; =DUNGEON_PTR - mov sl, r0 - ldr r0, [r1] - ldr sb, [sl, #0xb4] - add r0, r0, #0x4000 -#ifdef JAPAN - ldrb r0, [r0, #0x36] -#else - ldrb r0, [r0, #0xda] -#endif - ldrb r4, [sl, #0x25] - mov r5, #0 - cmp r0, #0xa5 - beq _01FFB304 - cmp r0, #0xaa - blo _01FFB31C - cmp r0, #0xdc - bhs _01FFB31C -_01FFB304: - ldrsh r0, [sl, #4] - ldrsh r1, [sl, #6] - bl GetTile - ldrh r0, [r0] - tst r0, #0x100 - movne r5, #1 -_01FFB31C: - cmp r5, #0 - bne _01FFB32C - cmp r4, #0xff - bne _01FFB3F0 -_01FFB32C: - ldrb r1, [sb, #0x4c] - mov r0, sl - mov r7, #0 - add r1, r1, #4 - and r6, r1, #7 - bl IsAtJunction - cmp r0, #0 - beq _01FFB35C - mov r0, #8 - bl DungeonRandInt - strb r0, [sb, #0x4c] - mov r7, #1 -_01FFB35C: - mov r5, #0 - ldr r4, _01FFB620 ; =FACING_DIRECTION_INCREMENTS - add fp, sp, #1 - b _01FFB3E4 -_01FFB36C: - ldrb r1, [sb, #0x4c] - ldr r0, [r4, r5, lsl #2] - cmp r7, #0 - add r0, r1, r0 - and r8, r0, #7 - beq _01FFB38C - cmp r8, r6 - beq _01FFB3E0 -_01FFB38C: - mov r0, sl - mov r1, r8 - mov r2, fp - bl CanAiMonsterMoveInDirection - cmp r0, #0 - beq _01FFB3E0 - mov r0, #3 - strb r0, [sb, #0x7c] - ldr r0, _01FFB624 ; =DIRECTIONS_XY - mov r3, r8, lsl #2 - ldrsh r2, [r0, r3] - ldrsh r4, [sl, #4] - ldr r1, _01FFB628 ; =DIRECTIONS_XY + 2 - mov r0, #1 - add r2, r4, r2 - strh r2, [sb, #0x8c] - ldrsh r2, [sl, #6] - ldrsh r1, [r1, r3] - add r1, r2, r1 - strh r1, [sb, #0x8e] - ldmia sp!, {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc} -_01FFB3E0: - add r5, r5, #1 -_01FFB3E4: - cmp r5, #8 - blt _01FFB36C - b _01FFB5D8 -_01FFB3F0: - ldr r0, _01FFB61C ; =DUNGEON_PTR -#ifdef JAPAN - ldr r1, [sb, #0x11c] - ldr r2, [r0] - cmp r1, #0 - add r0, r2, r4, lsl #1 - add r1, r2, #0x204 - add r0, r0, #0xf100 - add r1, r1, #0xf000 - add r6, r1, r4, lsl #7 - ldrsh r5, [r0, #0xc4] -#else - ldr r1, [sb, #0x120] - ldr r2, [r0] - cmp r1, #0 - add r0, r2, r4, lsl #1 - add r1, r2, #0x2a8 - add r0, r0, #0xf200 - add r1, r1, #0xf000 - add r6, r1, r4, lsl #7 - ldrsh r5, [r0, #0x68] -#endif - beq _01FFB460 - mov r0, #8 - bl DungeonRandInt - mov r1, #6 - strb r1, [sb, #0x7c] - mov r3, r0, lsl #2 - ldr r0, _01FFB624 ; =DIRECTIONS_XY - ldrsh r4, [sl, #4] - ldrsh r2, [r0, r3] - ldr r1, _01FFB628 ; =DIRECTIONS_XY + 2 - mov r0, #1 - add r2, r4, r2 - strh r2, [sb, #0x8c] - ldrsh r2, [sl, #6] - ldrsh r1, [r1, r3] - add r1, r2, r1 - strh r1, [sb, #0x8e] - ldmia sp!, {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc} -_01FFB460: - ldrb r0, [sb, #0x7c] - cmp r0, #4 - beq _01FFB520 - cmp r5, #0 - bne _01FFB4B8 - mov r0, #8 - bl DungeonRandInt - mov r1, #6 - strb r1, [sb, #0x7c] - mov r3, r0, lsl #2 - ldr r0, _01FFB624 ; =DIRECTIONS_XY - ldrsh r4, [sl, #4] - ldrsh r2, [r0, r3] - ldr r1, _01FFB628 ; =DIRECTIONS_XY + 2 - mov r0, #1 - add r2, r4, r2 - strh r2, [sb, #0x8c] - ldrsh r2, [sl, #6] - ldrsh r1, [r1, r3] - add r1, r2, r1 - strh r1, [sb, #0x8e] - ldmia sp!, {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc} -_01FFB4B8: - mov r4, #0 - b _01FFB518 -_01FFB4C0: - mov r0, r5 - bl DungeonRandInt - mov r2, r0, lsl #2 - ldrsh r3, [sl, #4] - ldrsh r1, [r6, r2] - cmp r3, r1 - addeq r1, r6, r2 - ldreqsh r2, [sl, #6] - ldreqsh r1, [r1, #2] - cmpeq r2, r1 - beq _01FFB514 - mov r1, #4 - strb r1, [sb, #0x7c] - mov r1, r0, lsl #2 - ldrsh r2, [r6, r1] - add r1, r6, r0, lsl #2 - mov r0, #1 - strh r2, [sb, #0x8c] - ldrsh r1, [r1, #2] - strh r1, [sb, #0x8e] - ldmia sp!, {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc} -_01FFB514: - add r4, r4, #1 -_01FFB518: - cmp r4, #0xa - blt _01FFB4C0 -_01FFB520: - ldrsh r0, [sl, #4] - ldrsh r1, [sl, #6] - bl GetTile - ldrh r0, [r0] - tst r0, #8 - beq _01FFB5D0 - mov r0, #8 - bl DungeonRandInt - mov r8, r0 - mov r5, #0 - add fp, sp, #0 - ldr r4, _01FFB624 ; =DIRECTIONS_XY - b _01FFB5C8 -_01FFB554: - and r8, r8, #7 - mov r1, r8, lsl #2 - add r0, r4, r8, lsl #2 - ldrsh r6, [sl, #4] - ldrsh r3, [r4, r1] - ldrsh r2, [sl, #6] - ldrsh r1, [r0, #2] - add r6, r6, r3 - mov r0, r6 - add r7, r2, r1 - mov r1, r7 - bl GetTile - ldrb r0, [r0, #7] - cmp r0, #0xff - bne _01FFB5C0 - mov r0, sl - mov r1, r8 - mov r2, fp - bl CanAiMonsterMoveInDirection - cmp r0, #0 - beq _01FFB5C0 - mov r0, #3 - strb r0, [sb, #0x7c] - strh r6, [sb, #0x8c] - strh r7, [sb, #0x8e] - mov r0, #1 - ldmia sp!, {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc} -_01FFB5C0: - add r5, r5, #1 - add r8, r8, #1 -_01FFB5C8: - cmp r5, #8 - blt _01FFB554 -_01FFB5D0: - mov r0, #1 - ldmia sp!, {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc} -_01FFB5D8: - mov r0, #8 - bl DungeonRandInt - mov r1, #6 - strb r1, [sb, #0x7c] - mov r3, r0, lsl #2 - ldr r0, _01FFB624 ; =DIRECTIONS_XY - ldrsh r4, [sl, #4] - ldrsh r2, [r0, r3] - ldr r1, _01FFB628 ; =DIRECTIONS_XY + 2 - mov r0, #1 - add r2, r4, r2 - strh r2, [sb, #0x8c] - ldrsh r2, [sl, #6] - ldrsh r1, [r1, r3] - add r1, r2, r1 - strh r1, [sb, #0x8e] - ldmia sp!, {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc} - .align 2, 0 -_01FFB61C: .word DUNGEON_PTR -_01FFB620: .word FACING_DIRECTION_INCREMENTS -_01FFB624: .word DIRECTIONS_XY -_01FFB628: .word DIRECTIONS_XY + 2 - arm_func_end CalculateAiTargetPos - - arm_func_start sub_01FFB62C -sub_01FFB62C: ; 0x01FFB62C - ldr r3, [r0, #0xb4] - mov r2, #0 - strb r2, [r3, #0x7c] - ldrh r1, [r0, #4] - strh r1, [r3, #0x8c] - ldrh r0, [r0, #6] - strh r0, [r3, #0x8e] - str r2, [r3, #0x84] - strh r2, [r3, #0x80] - bx lr - arm_func_end sub_01FFB62C - - .global _01FFB654 -_01FFB654: - .byte 0x05, 0x03, 0x00, 0x00 diff --git a/asm/itcm_01FFB62C.s b/asm/itcm_01FFB62C.s new file mode 100644 index 00000000..6b8d4358 --- /dev/null +++ b/asm/itcm_01FFB62C.s @@ -0,0 +1,22 @@ + .include "asm/macros.inc" + .include "itcm_01FFB62C.inc" + + .section .itcm,4,1,4 + + arm_func_start sub_01FFB62C +sub_01FFB62C: ; 0x01FFB62C + ldr r3, [r0, #0xb4] + mov r2, #0 + strb r2, [r3, #0x7c] + ldrh r1, [r0, #4] + strh r1, [r3, #0x8c] + ldrh r0, [r0, #6] + strh r0, [r3, #0x8e] + str r2, [r3, #0x84] + strh r2, [r3, #0x80] + bx lr + arm_func_end sub_01FFB62C + + .global _01FFB654 +_01FFB654: + .byte 0x05, 0x03, 0x00, 0x00 diff --git a/include/dungeon_ai_movement.h b/include/dungeon_ai_movement.h index d1369904..0ef0a937 100644 --- a/include/dungeon_ai_movement.h +++ b/include/dungeon_ai_movement.h @@ -3,6 +3,11 @@ #include "dungeon_mode.h" +// Used by the AI to determine the direction in which a monster should move +// monster: Entity pointer +// show_run_away_effect: If the monster becomes terrified, this flag determines whether the "poof" visual effect will show. void AiMovement(struct entity *monster, bool8 show_run_away_effect); +// Calculates the target position of an AI-controlled monster and stores it in the monster's ai_target_pos field +bool8 CalculateAiTargetPos(struct entity *monster); #endif //PMDSKY_DUNGEON_AI_MOVEMENT_H diff --git a/main.lsf b/main.lsf index 8dae247e..83226942 100644 --- a/main.lsf +++ b/main.lsf @@ -143,7 +143,7 @@ Autoload ITCM Address 0x01FF8000 Object asm/itcm.o (.itcm) Object src/dungeon_ai_movement.o (.itcm) - Object asm/itcm_01FFB2C8.o (.itcm) + Object asm/itcm_01FFB62C.o (.itcm) Object src/dungeon_ai_itcm.o (.itcm) Object asm/itcm_01FFBD20.o (.itcm) } diff --git a/src/dungeon_ai_movement.c b/src/dungeon_ai_movement.c index 2e790bda..60bd35eb 100644 --- a/src/dungeon_ai_movement.c +++ b/src/dungeon_ai_movement.c @@ -40,7 +40,6 @@ struct can_move_in_direction_info const s32 FACING_DIRECTION_INCREMENTS[] = {0, 1, -1, 2, -2, 3, -3, 4}; -extern bool8 CalculateAiTargetPos(struct entity *monster); extern bool8 CanAiMonsterMoveInDirection(struct entity *monster, s32 direction, bool8 *out_monster_in_target_position); extern bool8 CanTargetEntity(struct entity *user, struct entity *target); extern bool8 CanTargetPosition(struct entity *monster, struct position *position); @@ -57,6 +56,7 @@ extern bool8 ShouldAvoidFirstHit(struct entity *monster, bool8 force_avoid); extern bool8 CanSeeTeammate(struct entity *monster); extern struct entity* GetLeaderIfVisible(struct entity *monster); extern bool8 ov29_02348D00(struct item*); +extern bool8 IsAtJunction(struct entity *monster); // https://decomp.me/scratch/2QnEr #ifdef NONMATCHING @@ -1691,3 +1691,124 @@ _01FFB2A4: ldmia sp!, {r3, r4, r5, r6, r7, r8, r9, r10, r11, pc} } #endif + +bool8 CalculateAiTargetPos(struct entity *monster) +{ + struct monster *pokemon_info = GetEntInfo(monster); + enum fixed_room_id fixed_room_id = DUNGEON_PTR[0]->gen_info.fixed_room_id; + s32 room = monster->room_idx; + bool8 is_sealed_chamber = FALSE; + if (fixed_room_id == FIXED_SEALED_CHAMBER || + fixed_room_id >= FIXED_CLEAR_SILK_CHAMBER && fixed_room_id < FIXED_LEGENDARY_EXCLUSIVE_CHAMBER_1) + { + if (GetTile(monster->pos.x, monster->pos.y)->terrain_flags & TERRAIN_TYPE_UNBREAKABLE) + is_sealed_chamber = TRUE; + } + + if (is_sealed_chamber || room == CORRIDOR_ROOM) + { + s32 i; + s32 opposite_facing_dir = (pokemon_info->action.direction + NUM_DIRECTIONS / 2) & DIRECTION_MASK; + bool8 is_at_junction = FALSE; + s32 target_facing_dir; + if (IsAtJunction(monster)) + { + pokemon_info->action.direction = DungeonRandInt(NUM_DIRECTIONS); + is_at_junction = TRUE; + } + + for (i = 0; i < NUM_DIRECTIONS; i++) + { + bool8 pokemon_in_front; + target_facing_dir = (pokemon_info->action.direction + FACING_DIRECTION_INCREMENTS[i]) & DIRECTION_MASK; + + if (is_at_junction && target_facing_dir == opposite_facing_dir) + { + continue; + } + + if (!CanAiMonsterMoveInDirection(monster, target_facing_dir, &pokemon_in_front)) + { + continue; + } + + pokemon_info->ai_target.ai_objective = AI_ROAM; + pokemon_info->ai_target.ai_target_pos.x = monster->pos.x + DIRECTIONS_XY[target_facing_dir].x; + pokemon_info->ai_target.ai_target_pos.y = monster->pos.y + DIRECTIONS_XY[target_facing_dir].y; + return TRUE; + } + } + else + { + s32 i; + s32 target_facing_dir; + s32 natural_junction_list_counts = DUNGEON_PTR[0]->natural_junction_list_counts[room]; + struct position *natural_junction_list = DUNGEON_PTR[0]->natural_junction_list[room]; + if (pokemon_info->random_movement) + { + target_facing_dir = DungeonRandInt(NUM_DIRECTIONS); + pokemon_info->ai_target.ai_objective = AI_STAND_STILL; + pokemon_info->ai_target.ai_target_pos.x = monster->pos.x + DIRECTIONS_XY[target_facing_dir].x; + pokemon_info->ai_target.ai_target_pos.y = monster->pos.y + DIRECTIONS_XY[target_facing_dir].y; + return TRUE; + } + else + { + if (pokemon_info->ai_target.ai_objective != AI_LEAVE_ROOM) + { + if (natural_junction_list_counts == 0) + { + target_facing_dir = DungeonRandInt(NUM_DIRECTIONS); + pokemon_info->ai_target.ai_objective = AI_STAND_STILL; + pokemon_info->ai_target.ai_target_pos.x = monster->pos.x + DIRECTIONS_XY[target_facing_dir].x; + pokemon_info->ai_target.ai_target_pos.y = monster->pos.y + DIRECTIONS_XY[target_facing_dir].y; + return TRUE; + } + + for (i = 0; i < 10; i++) + { + target_facing_dir = DungeonRandInt(natural_junction_list_counts); + if (monster->pos.x != natural_junction_list[target_facing_dir].x || + monster->pos.y != natural_junction_list[target_facing_dir].y) + { + pokemon_info->ai_target.ai_objective = AI_LEAVE_ROOM; + pokemon_info->ai_target.ai_target_pos.x = natural_junction_list[target_facing_dir].x; + pokemon_info->ai_target.ai_target_pos.y = natural_junction_list[target_facing_dir].y; + return TRUE; + } + } + // If the AI randomly picks the exit it's standing on 10 times, + // it gives up and exits the way it came. + // This occurs normally for one-exit rooms, but can happen rarely for multi-exit rooms. + } + if (GetTile(monster->pos.x, monster->pos.y)->terrain_flags & TERRAIN_TYPE_NATURAL_JUNCTION) + { + s32 i; + target_facing_dir = DungeonRandInt(NUM_DIRECTIONS); + for (i = 0; i < NUM_DIRECTIONS; i++, target_facing_dir++) + { + bool8 pokemon_in_front; + s32 forward_x, forward_y; + target_facing_dir &= DIRECTION_MASK; + forward_x = monster->pos.x + DIRECTIONS_XY[target_facing_dir].x; + forward_y = monster->pos.y + DIRECTIONS_XY[target_facing_dir].y; + if (GetTile(forward_x, forward_y)->room == CORRIDOR_ROOM && + CanAiMonsterMoveInDirection(monster, target_facing_dir, &pokemon_in_front)) + { + pokemon_info->ai_target.ai_objective = AI_ROAM; + pokemon_info->ai_target.ai_target_pos.x = forward_x; + pokemon_info->ai_target.ai_target_pos.y = forward_y; + return TRUE; + } + } + } + return TRUE; + } + } + + s32 target_facing_dir = DungeonRandInt(NUM_DIRECTIONS); + pokemon_info->ai_target.ai_objective = AI_STAND_STILL; + pokemon_info->ai_target.ai_target_pos.x = monster->pos.x + DIRECTIONS_XY[target_facing_dir].x; + pokemon_info->ai_target.ai_target_pos.y = monster->pos.y + DIRECTIONS_XY[target_facing_dir].y; + return TRUE; +} diff --git a/tools/extract_function/extract_function.py b/tools/extract_function/extract_function.py index 229eae02..36f2b440 100644 --- a/tools/extract_function/extract_function.py +++ b/tools/extract_function/extract_function.py @@ -57,6 +57,8 @@ with open(original_file_path, 'r') as original_file: if function_location.startswith('main'): file_prefix = 'main_' +elif function_location.startswith('itcm'): + file_prefix = 'itcm_' else: file_prefix = function_location[:len('overlay_00_')] if file_prefix[-1] != '_': @@ -130,7 +132,7 @@ for i, line in enumerate(lsf_lines): lsf_lines[i] = '' prev_line = lsf_lines[i - 1] if prev_line.startswith(SRC_LSF_PREFIX): - merge_prev_file = prev_line[len(SRC_LSF_PREFIX) : -3] + merge_prev_file = prev_line[len(SRC_LSF_PREFIX):] if not include_new_asm_file and merge_prev_file is None: next_line = lsf_lines[i + 1] if next_line.startswith(SRC_LSF_PREFIX): @@ -141,6 +143,12 @@ for i, line in enumerate(lsf_lines): lsf_lines[i] += f'\tObject asm/{new_asm_base_name}.o{lsf_suffix}\n' break +if merge_prev_file is not None: + line_end_index = len('.o\n') + if merge_prev_file.endswith('.o (.itcm)\n'): + line_end_index = len('.o (.itcm)\n') + merge_prev_file = merge_prev_file[:-line_end_index] + print('Updating', LSF_FILE_PATH) with open(LSF_FILE_PATH, 'w') as lsf_file: lsf_file.writelines(lsf_lines)