diff --git a/include/battle.h b/include/battle.h index fcca03cd2..79d2f2016 100644 --- a/include/battle.h +++ b/include/battle.h @@ -548,7 +548,7 @@ struct BattleStruct u8 sameMoveTurns[MAX_BATTLERS_COUNT]; // For Metronome, number of times the same moves has been SUCCESFULLY used. u16 moveEffect2; // For Knock Off u16 changedSpecies[PARTY_SIZE]; // For Zygarde or future forms when multiple mons can change into the same pokemon. - struct StolenItem itemStolen[PARTY_SIZE]; //player's team that had items stolen (bit per party member) + struct StolenItem itemStolen[PARTY_SIZE]; // Player's team that had items stolen (byte per party member) }; #define GET_MOVE_TYPE(move, typeArg) \ diff --git a/include/constants/battle_config.h b/include/constants/battle_config.h index 631138def..e057b85ce 100644 --- a/include/constants/battle_config.h +++ b/include/constants/battle_config.h @@ -141,13 +141,16 @@ #define B_CRITICAL_CAPTURE TRUE // If set to TRUE, Critical Capture will be enabled. #define B_CATCHING_CHARM_BOOST 20 // % boost in Critical Capture odds if player has the Catching Charm. +// Item Theft Settings +#define B_TRAINERS_KNOCK_OFF_ITEMS TRUE // If TRUE, trainers can steal/swap your items (non-berries are restored after battle). In vanilla games trainers cannot steal items. +#define B_KEEP_STOLEN_TRAINER_ITEMS GEN_5 // In Gen5+, you do not keep items stolen from trainers. Wild Pokemon still always have their items permanently stolen. + // Other #define B_DOUBLE_WILD_CHANCE 0 // % chance of encountering two Pokémon in a Wild Encounter. #define B_SLEEP_TURNS GEN_6 // In Gen5+, sleep lasts for 1-3 turns instead of 2-5 turns. #define B_PARALYZE_ELECTRIC GEN_6 // In Gen6+, Electric-type Pokémon can't be paralyzed. #define B_POWDER_GRASS GEN_6 // In Gen6+, Grass-type Pokémon are immune to powder and spore moves. #define B_STEEL_RESISTANCES GEN_6 // In Gen6+, Steel-type Pokémon are no longer resistant to Dark and Ghost moves. -#define B_TRAINERS_KNOCK_OFF_ITEMS TRUE // If TRUE, trainers can steal/swap your items (non-berries are restored after battle). In vanilla games trainers cannot steal items. // Animation Settings #define B_NEW_SWORD_PARTICLE TRUE // If set to TRUE, it updates Swords Dance's particle. diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 26325f1a8..5259e5c32 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2969,9 +2969,9 @@ void SetMoveEffect(bool32 primary, u32 certain) } else { - StealTargetItem(gBattlerAttacker, gBattlerTarget); //attacker steals target item - gBattleMons[gBattlerAttacker].item = 0; //item assigned later on with thief (see MOVEEND_CHANGED_ITEMS) - gBattleStruct->changedItems[gBattlerAttacker] = gLastUsedItem; // stolen item to be assigned later + StealTargetItem(gBattlerAttacker, gBattlerTarget); // Attacker steals target item + gBattleMons[gBattlerAttacker].item = 0; // Item assigned later on with thief (see MOVEEND_CHANGED_ITEMS) + gBattleStruct->changedItems[gBattlerAttacker] = gLastUsedItem; // Stolen item to be assigned later BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_ItemSteal; } @@ -5012,35 +5012,35 @@ static void Cmd_moveend(void) break; case MOVEEND_PICKPOCKET: if (IsBattlerAlive(gBattlerAttacker) - && gBattleMons[gBattlerAttacker].item != ITEM_NONE // attacker must be holding an item - && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) // pickpocket doesn't activate for sheer force - && IsMoveMakingContact(gCurrentMove, gBattlerAttacker) // pickpocket requires contact - && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) // obviously attack needs to have worked + && gBattleMons[gBattlerAttacker].item != ITEM_NONE // Attacker must be holding an item + && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) // Pickpocket doesn't activate for sheer force + && IsMoveMakingContact(gCurrentMove, gBattlerAttacker) // Pickpocket requires contact + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) // Obviously attack needs to have worked { u8 battlers[4] = {0, 1, 2, 3}; - SortBattlersBySpeed(battlers, FALSE); // pickpocket activates for fastest mon without item + SortBattlersBySpeed(battlers, FALSE); // Pickpocket activates for fastest mon without item for (i = 0; i < gBattlersCount; i++) { u8 battler = battlers[i]; - // attacker is mon who made contact, battler is mon with pickpocket - if (battler != gBattlerAttacker // cannot pickpocket yourself - && GetBattlerAbility(battler) == ABILITY_PICKPOCKET // 'target' must have pickpocket ability - && BATTLER_DAMAGED(battler) // target needs to have been damaged - && !DoesSubstituteBlockMove(gCurrentMove, gBattlerAttacker, battler) // subsitute unaffected - && IsBattlerAlive(battler) // battler must be alive to pickpocket - && gBattleMons[battler].item == ITEM_NONE // pickpocketer can't have an item already - && CanStealItem(battler, gBattlerAttacker, gBattleMons[gBattlerAttacker].item)) // cannot steal plates, mega stones, etc + // Attacker is mon who made contact, battler is mon with pickpocket + if (battler != gBattlerAttacker // Cannot pickpocket yourself + && GetBattlerAbility(battler) == ABILITY_PICKPOCKET // Target must have pickpocket ability + && BATTLER_DAMAGED(battler) // Target needs to have been damaged + && !DoesSubstituteBlockMove(gCurrentMove, gBattlerAttacker, battler) // Subsitute unaffected + && IsBattlerAlive(battler) // Battler must be alive to pickpocket + && gBattleMons[battler].item == ITEM_NONE // Pickpocketer can't have an item already + && CanStealItem(battler, gBattlerAttacker, gBattleMons[gBattlerAttacker].item)) // Cannot steal plates, mega stones, etc { gBattlerTarget = gBattlerAbility = battler; - // battle scripting is super brittle so we shall do the item exchange now (if possible) + // Battle scripting is super brittle so we shall do the item exchange now (if possible) if (GetBattlerAbility(gBattlerAttacker) != ABILITY_STICKY_HOLD) - StealTargetItem(gBattlerTarget, gBattlerAttacker); // target takes attacker's item + StealTargetItem(gBattlerTarget, gBattlerAttacker); // Target takes attacker's item gEffectBattler = gBattlerAttacker; BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_Pickpocket; // includes sticky hold check to print separate string + gBattlescriptCurrInstr = BattleScript_Pickpocket; // Includes sticky hold check to print separate string effect = TRUE; - break; // pickpocket activates on fastest mon, so exit loop. + break; // Pickpocket activates on fastest mon, so exit loop. } } } diff --git a/src/battle_util.c b/src/battle_util.c index a503dfc48..34e26e679 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5858,9 +5858,9 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) && IsBattlerAlive(gBattlerAttacker) && gBattleMons[gBattlerAttacker].item == ITEM_NONE) { - //no sticky hold checks. item is already known so no CanStealItem checks - gEffectBattler = battlerId; //effect battler = target - StealTargetItem(gBattlerAttacker, gBattlerTarget); //attacker takes target's barb + // No sticky hold checks. item is already known so no CanStealItem checks + gEffectBattler = battlerId; // gEffectBattler = target + StealTargetItem(gBattlerAttacker, gBattlerTarget); // Attacker takes target's barb BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_StickyBarbTransfer; effect = ITEM_EFFECT_OTHER; @@ -5894,7 +5894,7 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) RecordItemEffectBattle(battlerId, battlerHoldEffect); } break; - case HOLD_EFFECT_STICKY_BARB: //not an orb per-say, but similar effect, and needs to NOT activate with pickpocket + case HOLD_EFFECT_STICKY_BARB: // Not an orb per se, but similar effect, and needs to NOT activate with pickpocket if (GetBattlerAbility(battlerId) != ABILITY_MAGIC_GUARD) { gBattleMoveDamage = gBattleMons[battlerId].maxHP / 8; @@ -7859,8 +7859,8 @@ u8 GetBattleMoveSplit(u32 moveId) return SPLIT_SPECIAL; } -// sort an array of battlers by speed -// useful for effects like pickpocket, eject button, red card, dancer +// Sort an array of battlers by speed +// Useful for effects like pickpocket, eject button, red card, dancer void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast) { int i, j, currSpeed, currBattler; @@ -7918,7 +7918,7 @@ void TryRestoreStolenItems(void) { stolenItem = gBattleStruct->itemStolen[i].originalItem; if (stolenItem != ITEM_NONE && ItemId_GetPocket(stolenItem) != POCKET_BERRIES) - SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &stolenItem); //restore stolen non-berry items + SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &stolenItem); // Restore stolen non-berry items } } } @@ -7930,7 +7930,7 @@ bool32 CanStealItem(u8 battlerStealing, u8 battlerItem, u16 item) if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL) return FALSE; - // check if the battler trying to steal should be able to + // Check if the battler trying to steal should be able to if (stealerSide == B_SIDE_OPPONENT && !(gBattleTypeFlags & (BATTLE_TYPE_EREADER_TRAINER @@ -7956,14 +7956,17 @@ bool32 CanStealItem(u8 battlerStealing, u8 battlerItem, u16 item) return FALSE; } - // check if battler with the item can lose it - return CanBattlerGetOrLoseItem(battlerItem, item); + if (!CanBattlerGetOrLoseItem(battlerItem, item) // Battler with item cannot have it stolen + ||!CanBattlerGetOrLoseItem(battlerStealing, item)) // Stealer cannot take the item + return FALSE; + + return TRUE; } void TrySaveExchangedItem(u8 battlerId, u16 stolenItem) { - // because BtlController_EmitSetMonData does SetMonData, we need to save the stolen item only if it matches the battler's original - // so, if the player steals an item during battle and has it stolen from it, it will not end the battle with it (naturally) + // Because BtlController_EmitSetMonData does SetMonData, we need to save the stolen item only if it matches the battler's original + // So, if the player steals an item during battle and has it stolen from it, it will not end the battle with it (naturally) #if B_TRAINERS_KNOCK_OFF_ITEMS == TRUE // If regular trainer battle and mon's original item matches what is being stolen, save it to be restored at end of battle if (gBattleTypeFlags & BATTLE_TYPE_TRAINER