diff --git a/Makefile b/Makefile index 59e6cd46..567a9a7d 100644 --- a/Makefile +++ b/Makefile @@ -69,9 +69,9 @@ us: ; @$(MAKE) GAME_LANGUAGE=NORTH_AMERICA eu: ; @$(MAKE) GAME_LANGUAGE=EUROPE jp: ; @$(MAKE) GAME_LANGUAGE=JAPAN -us-nonmatching: ; @$(MAKE) GAME_LANGUAGE=NORTH_AMERICA NONMATCHING=TRUE -eu-nonmatching: ; @$(MAKE) GAME_LANGUAGE=EUROPE NONMATCHING=TRUE -jp-nonmatching: ; @$(MAKE) GAME_LANGUAGE=JAPAN NONMATCHING=TRUE +us-nonmatching: ; @$(MAKE) GAME_LANGUAGE=NORTH_AMERICA NONMATCHING=TRUE COMPARE=0 +eu-nonmatching: ; @$(MAKE) GAME_LANGUAGE=EUROPE NONMATCHING=TRUE COMPARE=0 +jp-nonmatching: ; @$(MAKE) GAME_LANGUAGE=JAPAN NONMATCHING=TRUE COMPARE=0 compare: @$(MAKE) COMPARE=1 diff --git a/asm/include/overlay_29_0231A0A4.inc b/asm/include/overlay_29_0231A364.inc similarity index 69% rename from asm/include/overlay_29_0231A0A4.inc rename to asm/include/overlay_29_0231A364.inc index d27a3e15..0000360b 100644 --- a/asm/include/overlay_29_0231A0A4.inc +++ b/asm/include/overlay_29_0231A364.inc @@ -4,16 +4,11 @@ .public DIRECTIONS_XY .public DUNGEON_PTR .public DungeonRandInt -.public GetMoveAccuracyOrAiChance .public GetTile .public GetTreatmentBetweenMonsters .public HasHeldItem .public IqSkillIsEnabled .public IsBlinded -.public IsMonsterSleeping -.public MonsterHasNegativeStatus .public _s32_div_f -.public ov29_023007DC .public ov29_0230B7D4 .public ov29_0230F8D0 -.public ov29_02333FD0 diff --git a/asm/overlay_29_0231A0A4.s b/asm/overlay_29_0231A364.s similarity index 54% rename from asm/overlay_29_0231A0A4.s rename to asm/overlay_29_0231A364.s index dcc923e2..d37d8bde 100644 --- a/asm/overlay_29_0231A0A4.s +++ b/asm/overlay_29_0231A364.s @@ -1,203 +1,8 @@ .include "asm/macros.inc" - .include "overlay_29_0231A0A4.inc" + .include "overlay_29_0231A364.inc" .text - arm_func_start IsAiTargetEligible -IsAiTargetEligible: ; 0x0231A0A4 - stmdb sp!, {r4, r5, r6, r7, r8, lr} - mov r8, r0 - mov r6, r2 - mov r2, #0 - mov r7, r1 - mov r5, r3 - mov r4, r2 - ands r0, r8, #0xf - bne _0231A0E4 - mov r0, r7 - mov r1, r6 - mov r3, #1 - bl GetTreatmentBetweenMonsters - cmp r0, #1 - moveq r4, #1 - b _0231A1B8 -_0231A0E4: - cmp r0, #1 - bne _0231A108 - mov r0, r7 - mov r1, r6 - mov r3, #1 - bl GetTreatmentBetweenMonsters - cmp r0, #0 - moveq r4, #1 - b _0231A1B8 -_0231A108: - cmp r0, #2 - bne _0231A13C - ldr r1, [r6, #0xb4] - mov r4, #1 - ldrb r0, [r1, #9] - cmp r0, #1 - moveq r0, r2 - ldmeqia sp!, {r4, r5, r6, r7, r8, pc} - ldrb r0, [r1, #0xbc] - cmp r0, #7 - bne _0231A1B8 - mov r0, r2 - ldmia sp!, {r4, r5, r6, r7, r8, pc} -_0231A13C: - cmp r0, #5 - bne _0231A17C - cmp r7, r6 - ldr r1, [r6, #0xb4] - moveq r0, r2 - ldmeqia sp!, {r4, r5, r6, r7, r8, pc} - ldrb r0, [r1, #9] - mov r4, #1 - cmp r0, #1 - moveq r0, r2 - ldmeqia sp!, {r4, r5, r6, r7, r8, pc} - ldrb r0, [r1, #0xbc] - cmp r0, #7 - bne _0231A1B8 - mov r0, r2 - ldmia sp!, {r4, r5, r6, r7, r8, pc} -_0231A17C: - cmp r0, #6 - bne _0231A1AC - cmp r7, r6 - moveq r0, r2 - ldmeqia sp!, {r4, r5, r6, r7, r8, pc} - mov r0, r7 - mov r1, r6 - mov r3, #1 - bl GetTreatmentBetweenMonsters - cmp r0, #0 - moveq r4, #1 - b _0231A1B8 -_0231A1AC: - sub r0, r0, #3 - cmp r0, #1 - movls r4, #1 -_0231A1B8: - cmp r4, #0 - beq _0231A358 - ldrb r0, [sp, #0x18] - cmp r0, #0 - beq _0231A328 - mov r0, r7 - mov r1, r6 - mov r2, r5 - bl ov29_02333FD0 - cmp r0, #0 - moveq r0, #0 - ldmeqia sp!, {r4, r5, r6, r7, r8, pc} - and r0, r8, #0xf00 - cmp r0, #0x100 - bne _0231A21C - mov r0, r5 - mov r1, #2 - bl GetMoveAccuracyOrAiChance - mov r5, r0 - mov r0, #0x64 - bl DungeonRandInt - cmp r0, r5 - blt _0231A358 - mov r0, #0 - ldmia sp!, {r4, r5, r6, r7, r8, pc} -_0231A21C: - cmp r0, #0x200 - bne _0231A23C - mov r0, r6 - bl ov29_023007DC - cmp r0, #0 - bne _0231A358 - mov r0, #0 - ldmia sp!, {r4, r5, r6, r7, r8, pc} -_0231A23C: - cmp r0, #0x300 - bne _0231A260 - mov r0, r6 - mov r1, #1 - bl MonsterHasNegativeStatus - cmp r0, #0 - bne _0231A358 - mov r0, #0 - ldmia sp!, {r4, r5, r6, r7, r8, pc} -_0231A260: - cmp r0, #0x400 - bne _0231A280 - mov r0, r6 - bl IsMonsterSleeping - cmp r0, #0 - bne _0231A358 - mov r0, #0 - ldmia sp!, {r4, r5, r6, r7, r8, pc} -_0231A280: - cmp r0, #0x600 - bne _0231A2B8 - ldr r1, [r6, #0xb4] - ldrb r0, [r1, #0x5e] - cmp r0, #0xe - ldrneb r0, [r1, #0x5f] - cmpne r0, #0xe - movne r0, #0 - ldmneia sp!, {r4, r5, r6, r7, r8, pc} - ldrb r0, [r1, #0xfe] - cmp r0, #0 - beq _0231A358 - mov r0, #0 - ldmia sp!, {r4, r5, r6, r7, r8, pc} -_0231A2B8: - cmp r0, #0x500 - bne _0231A358 - ldrh r1, [r5, #4] - ldr r0, _0231A360 ; =0x000001B7 - cmp r1, r0 - addne r0, r0, #0x57 - cmpne r1, r0 - mov r0, r6 - mov r1, #1 - bne _0231A304 - bl MonsterHasNegativeStatus - cmp r0, #0 - bne _0231A358 - mov r0, r6 - bl ov29_023007DC - cmp r0, #0 - bne _0231A358 - mov r0, #0 - ldmia sp!, {r4, r5, r6, r7, r8, pc} -_0231A304: - bl MonsterHasNegativeStatus - cmp r0, #0 - bne _0231A358 - mov r0, r6 - bl ov29_023007DC - cmp r0, #0 - bne _0231A358 - mov r0, #0 - ldmia sp!, {r4, r5, r6, r7, r8, pc} -_0231A328: - and r0, r8, #0xf00 - cmp r0, #0x100 - bne _0231A358 - mov r0, r5 - mov r1, #2 - bl GetMoveAccuracyOrAiChance - mov r5, r0 - mov r0, #0x64 - bl DungeonRandInt - cmp r0, r5 - movge r0, #0 - ldmgeia sp!, {r4, r5, r6, r7, r8, pc} -_0231A358: - mov r0, r4 - ldmia sp!, {r4, r5, r6, r7, r8, pc} - .align 2, 0 -_0231A360: .word 0x000001B7 - arm_func_end IsAiTargetEligible - arm_func_start WeightMoveWithIqSkills WeightMoveWithIqSkills: ; 0x0231A364 stmdb sp!, {r3, r4, r5, r6, r7, r8, sb, lr} diff --git a/asm/overlay_29_02333FD0.s b/asm/overlay_29_02333FD0.s index 14d602a9..b48f4dba 100644 --- a/asm/overlay_29_02333FD0.s +++ b/asm/overlay_29_02333FD0.s @@ -3,8 +3,8 @@ .text - arm_func_start ov29_02333FD0 -ov29_02333FD0: ; 0x02333FD0 + arm_func_start StatusCheckerCheckOnTarget +StatusCheckerCheckOnTarget: ; 0x02333FD0 #ifdef JAPAN #define OV29_02333FD0_OFFSET -4 #else @@ -956,7 +956,7 @@ _02334C9C: .align 2, 0 _02334CA4: .word 0x0000010A _02334CA8: .word 0x0000020D - arm_func_end ov29_02333FD0 + arm_func_end StatusCheckerCheckOnTarget arm_func_start ov29_02334CAC ov29_02334CAC: ; 0x02334CAC diff --git a/include/dungeon_ai_attack.h b/include/dungeon_ai_attack.h index 4b167c7a..3275b6ce 100644 --- a/include/dungeon_ai_attack.h +++ b/include/dungeon_ai_attack.h @@ -32,5 +32,13 @@ bool8 EntityIsValid__02319F8C(struct entity *entity); // check_all_conditions: check_all_conditions parameter to pass to IsAiTargetEligible // return: New number of targets in the target list s32 TryAddTargetToAiTargetList(s32 current_num_targets, s32 move_ai_range, struct entity *user, struct entity *target, struct move *move, bool8 check_all_conditions); +// Checks if a given target is eligible to be targeted by the AI with a certain move +// move_ai_range: Move's AI range field +// user: User entity pointer +// target: Target entity pointer +// move: Move pointer +// check_all_conditions True to check all the possible move_ai_condition values, false to only check for move_ai_condition::AI_CONDITION_RANDOM (if the move has a different ai condition, the result will be false). +// return: True if the target is eligible, false otherwise +bool8 IsAiTargetEligible(s32 move_ai_range, struct entity *user, struct entity *target, struct move *move, bool8 check_all_conditions); #endif //PMDSKY_DUNGEON_AI_ATTACK_H diff --git a/include/dungeon_projectile_throw.h b/include/dungeon_projectile_throw.h index 34b9ca53..9229c4b3 100644 --- a/include/dungeon_projectile_throw.h +++ b/include/dungeon_projectile_throw.h @@ -3,6 +3,7 @@ #include "dungeon_mode.h" +// Parameters used when throwing a projectile item. struct projectile_throw_info { u8 unk0; @@ -22,6 +23,12 @@ bool8 ItemIsActive__02347B50(struct entity *entity, enum item_id item_id); bool8 ExclusiveItemEffectIsActive__02347B80(struct entity *entity, enum exclusive_item_effect_id effect_id); // Checks if an entity pointer points to a valid entity (not entity type 0, which represents no entity). bool8 EntityIsValid__02347BA4(struct entity *entity); +// Throws an item in a curved arc, like a Gravelerock. +// thrower: monster entity throwing the item +// item: item being thrown +// start_pos: position to start throwing the item from +// target_pos: position to throw the item to +// projectile_throw_info: Metadata about the item being thrown void HandleCurvedProjectileThrow(struct entity *thrower, struct item *item, struct position *start_pos, struct position *target_pos, struct projectile_throw_info *projectile_throw_info); #endif //PMDSKY_OVERLAY_29_02347BA4_H diff --git a/include/move_data.h b/include/move_data.h index d3c48bd6..7da66ba2 100644 --- a/include/move_data.h +++ b/include/move_data.h @@ -50,8 +50,20 @@ enum move_ai_condition AI_CONDITION_HP_25 = 0x200, // Target has HP <= 25% AI_CONDITION_STATUS = 0x300, // Target has a negative status condition AI_CONDITION_ASLEEP = 0x400, // Target is asleep, napping or in a nightmare - AI_CONDITION_GHOST = 0x500, // Target is ghost-type and not exposed - AI_CONDITION_HP_25_OR_STATUS = 0x600, // Target has HP <= 25% or a negative status condition + AI_CONDITION_HP_25_OR_STATUS = 0x500, // Target has HP <= 25% or a negative status condition + AI_CONDITION_GHOST = 0x600, // Target is ghost-type and not exposed +}; + +enum accuracy_type +{ + // Accuracy used for all moves. + ACCURACY_1, + // Multiplied with the global accuracy for offensive moves (i.e., not status moves). + ACCURACY_2, + // Used by the AI to determine how often to use Spikes. + // Values exist for all other moves, though they seem to be unused. + ACCURACY_AI_CONDITION_RANDOM_CHANCE, + NUM_ACCURACY_TYPES }; #define MOVE_RANGE_STRING_USER 19 diff --git a/main.lsf b/main.lsf index b8a47569..a5f7b0ce 100644 --- a/main.lsf +++ b/main.lsf @@ -465,7 +465,7 @@ Overlay OVY_29 Object src/dungeon_ai_attack_2.o Object asm/overlay_29_02319880.o Object src/dungeon_ai_attack.o - Object asm/overlay_29_0231A0A4.o + Object asm/overlay_29_0231A364.o Object src/dungeon_ai_attack_1.o Object asm/overlay_29_0231A7A0.o Object src/overlay_29_0231A87C.o diff --git a/src/dungeon_ai_attack.c b/src/dungeon_ai_attack.c index 88e06c3a..d311e326 100644 --- a/src/dungeon_ai_attack.c +++ b/src/dungeon_ai_attack.c @@ -2,7 +2,10 @@ #include "dg_random.h" #include "dungeon_ai_attack_1.h" #include "dungeon_ai_attack_2.h" +#include "dungeon_ai_targeting_1.h" #include "dungeon_capabilities_4.h" +#include "dungeon_logic.h" +#include "dungeon_logic_4.h" #include "dungeon_map_access.h" #include "dungeon_pokemon_attributes_1.h" #include "dungeon_statuses.h" @@ -22,9 +25,12 @@ extern u8 AI_POTENTIAL_ATTACK_TARGET_DIRECTIONS[NUM_DIRECTIONS]; extern s32 AI_POTENTIAL_ATTACK_TARGET_WEIGHTS[NUM_DIRECTIONS]; extern struct entity *AI_POTENTIAL_ATTACK_TARGETS[NUM_DIRECTIONS]; -extern bool8 IsAiTargetEligible(s32 move_ai_range, struct entity *user, struct entity *target, struct move *move, bool8 check_all_conditions); extern enum type_id GetMoveTypeForMonster(struct entity *entity, struct move *move); extern s32 WeightMoveWithIqSkills(struct entity *user, s32 move_ai_range, struct entity *target, enum type_id move_type); +extern bool8 StatusCheckerCheckOnTarget(struct entity *attacker, struct entity *target, struct move *move); +extern bool8 ov29_023007DC(struct entity *entity); +extern bool8 IsMonsterSleeping(struct entity *monster); +extern u8 GetMoveAccuracyOrAiChance(struct move *move, u32 which); // https://decomp.me/scratch/jVfou #ifdef NONMATCHING @@ -283,3 +289,109 @@ s32 TryAddTargetToAiTargetList(s32 current_num_targets, s32 move_ai_range, struc } return current_num_targets; } + +bool8 IsAiTargetEligible(s32 move_ai_range, struct entity *user, struct entity *target, struct move *move, bool8 check_all_conditions) +{ + bool8 has_target = FALSE; + u32 move_ai_target = move_ai_range & 0xF; + if (move_ai_target == TARGET_ENEMIES) + { + if (GetTreatmentBetweenMonsters(user, target, FALSE, TRUE) == TREATMENT_TREAT_AS_ENEMY) + has_target = TRUE; + } + else if (move_ai_target == TARGET_PARTY) + { + if (GetTreatmentBetweenMonsters(user, target, FALSE, TRUE) == TREATMENT_TREAT_AS_ALLY) + has_target = TRUE; + } + else if (move_ai_target == TARGET_ALL) + { + struct monster *target_data = target_data = GetEntInfo(target); + has_target = TRUE; + if (target_data->shopkeeper == SHOPKEEPER_MODE_SHOPKEEPER) + return FALSE; + + if (target_data->monster_behavior == BEHAVIOR_RESCUE_TARGET) + return FALSE; + } + else if (move_ai_target == TARGET_ALL_EXCEPT_USER) + { + struct monster *target_data = target_data = GetEntInfo(target); + if (user == target) + return FALSE; + + has_target = TRUE; + if (target_data->shopkeeper == SHOPKEEPER_MODE_SHOPKEEPER) + return FALSE; + + if (target_data->monster_behavior == BEHAVIOR_RESCUE_TARGET) + return FALSE; + } + else if (move_ai_target == TARGET_TEAMMATES) + { + if (user == target) + return FALSE; + + if (GetTreatmentBetweenMonsters(user, target, FALSE, TRUE) == TREATMENT_TREAT_AS_ALLY) + has_target = TRUE; + } + else if (move_ai_target - 3 <= 1) // move_ai_target == TARGET_SPECIAL + has_target = TRUE; + + if (has_target) + { + if (check_all_conditions) + { + if (!StatusCheckerCheckOnTarget(user, target, move)) + return FALSE; + + if ((move_ai_range & 0xF00) == AI_CONDITION_RANDOM) + { + s32 use_chance = GetMoveAccuracyOrAiChance(move, ACCURACY_AI_CONDITION_RANDOM_CHANCE); + if (DungeonRandInt(100) >= use_chance) + return FALSE; + } + else if ((move_ai_range & 0xF00) == AI_CONDITION_HP_25) + { + if (!ov29_023007DC(target)) + return FALSE; + } + else if ((move_ai_range & 0xF00) == AI_CONDITION_STATUS) + { + if (!MonsterHasNegativeStatus(target, TRUE)) + return FALSE; + } + else if ((move_ai_range & 0xF00) == AI_CONDITION_ASLEEP) + { + if (!IsMonsterSleeping(target)) + return FALSE; + } + else if ((move_ai_range & 0xF00) == AI_CONDITION_GHOST) + { + struct monster *target_data = target_data = GetEntInfo(target); + if (target_data->types[0] != TYPE_GHOST && target_data->types[1] != TYPE_GHOST) + return FALSE; + + if (target_data->exposed) + return FALSE; + } + else if ((move_ai_range & 0xF00) == AI_CONDITION_HP_25_OR_STATUS) + { + if (move->id == MOVE_HEALING_WISH || move->id == MOVE_LUNAR_DANCE) + { + if (!MonsterHasNegativeStatus(target, TRUE) && !ov29_023007DC(target)) + return FALSE; + } + else if (!MonsterHasNegativeStatus(target, TRUE) && !ov29_023007DC(target)) + return FALSE; + } + } + else if ((move_ai_range & 0xF00) == AI_CONDITION_RANDOM) + { + s32 use_chance = GetMoveAccuracyOrAiChance(move, ACCURACY_AI_CONDITION_RANDOM_CHANCE); + if (DungeonRandInt(100) >= use_chance) + return FALSE; + } + } + return has_target; +}