move pickpocket to back of moveend sequence

This commit is contained in:
Evan 2020-11-12 23:30:00 -07:00
parent e585be6f2e
commit 64bcac4589
7 changed files with 204 additions and 57 deletions

View File

@ -1755,6 +1755,14 @@
various \battler, VARIOUS_JUMP_IF_ABSENT
.4byte \ptr
.endm
.macro activateitemeffects battler:req
various \battler, VARIOUS_MOVEEND_ITEM_EFFECTS
.endm
.macro pickpocketsteal
various 0, VARIOUS_PICKPOCKET
.endm
@ helpful macros
.macro setstatchanger stat:req, stages:req, down:req

View File

@ -7691,7 +7691,11 @@ BattleScript_PrintPlayerForfeitedLinkBattle::
BattleScript_Pickpocket::
call BattleScript_AbilityPopUp
setmoveeffect MOVE_EFFECT_STEAL_ITEM
swapattackerwithtarget
seteffectsecondary
pickpocketsteal
call BattleScript_ItemSteal
swapattackerwithtarget
activateitemeffects BS_TARGET
return

View File

@ -557,6 +557,7 @@ struct BattleStruct
#define BATTLER_MAX_HP(battlerId)(gBattleMons[battlerId].hp == gBattleMons[battlerId].maxHP)
#define TARGET_TURN_DAMAGED ((gSpecialStatuses[gBattlerTarget].physicalDmg != 0 || gSpecialStatuses[gBattlerTarget].specialDmg != 0))
#define BATTLER_DAMAGED(battlerId) ((gSpecialStatuses[battlerId].physicalDmg != 0 || gSpecialStatuses[battlerId].specialDmg != 0))
#define IS_BATTLER_OF_TYPE(battlerId, type)((gBattleMons[battlerId].type1 == type || gBattleMons[battlerId].type2 == type || gBattleMons[battlerId].type3 == type))
#define SET_BATTLER_TYPE(battlerId, type) \

View File

@ -130,5 +130,8 @@ void ClearIllusionMon(u32 battlerId);
bool32 SetIllusionMon(struct Pokemon *mon, u32 battlerId);
bool8 ShouldGetStatBadgeBoost(u16 flagId, u8 battlerId);
u8 GetBattleMoveSplit(u32 moveId);
void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast);
bool32 TestSheerForceFlag(u8 battler, u16 move);
bool32 ItemCanBeStolen(u16 item, u8 battlerId);
#endif // GUARD_BATTLE_UTIL_H

View File

@ -165,6 +165,8 @@
#define VARIOUS_SET_LAST_USED_ITEM 99
#define VARIOUS_PARALYZE_TYPE_IMMUNITY 100
#define VARIOUS_JUMP_IF_ABSENT 101
#define VARIOUS_MOVEEND_ITEM_EFFECTS 102
#define VARIOUS_PICKPOCKET 103
// Cmd_manipulatedamage
#define DMG_CHANGE_SIGN 0
@ -189,7 +191,7 @@
#define STAT_CHANGE_ONLY_MULTIPLE 0x4
#define STAT_CHANGE_CANT_PREVENT 0x8
// cases for Cmd_moveend
// cases for Cmd_moveend - reference https://bulbapedia.bulbagarden.net/wiki/User:FIQ/Turn_sequence
#define MOVEEND_PROTECT_LIKE_EFFECT 0
#define MOVEEND_RAGE 1
#define MOVEEND_DEFROST 2
@ -212,10 +214,11 @@
#define MOVEEND_MIRROR_MOVE 19
#define MOVEEND_NEXT_TARGET 20
#define MOVEEND_LIFE_ORB 21
#define MOVEEND_DANCER 22
#define MOVEEND_EMERGENCY_EXIT 23
#define MOVEEND_CLEAR_BITS 24
#define MOVEEND_COUNT 25
#define MOVEEND_PICKPOCKET 22
#define MOVEEND_DANCER 23
#define MOVEEND_EMERGENCY_EXIT 24
#define MOVEEND_CLEAR_BITS 25
#define MOVEEND_COUNT 26
// stat flags for Cmd_playstatchangeanimation
#define BIT_HP 0x1

View File

@ -2419,9 +2419,7 @@ void SetMoveEffect(bool32 primary, u32 certain)
&& !primary && gBattleScripting.moveEffect <= 7)
INCREMENT_RESET_RETURN
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_SHEER_FORCE
&& gBattleMoves[gCurrentMove].flags & FLAG_SHEER_FORCE_BOOST
&& affectsUser != MOVE_EFFECT_AFFECTS_USER)
if (TestSheerForceFlag(gBattlerAttacker, gCurrentMove) && affectsUser != MOVE_EFFECT_AFFECTS_USER)
INCREMENT_RESET_RETURN
if (gBattleMons[gEffectBattler].hp == 0
@ -2924,36 +2922,12 @@ void SetMoveEffect(bool32 primary, u32 certain)
break;
case MOVE_EFFECT_STEAL_ITEM:
{
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL)
if (!ItemCanBeStolen(gBattleMons[gBattlerTarget].item, gBattlerAttacker))
{
gBattlescriptCurrInstr++;
break;
}
side = GetBattlerSide(gBattlerAttacker);
if (gLastUsedAbility != ABILITY_PICKPOCKET //we need to swap attacker and target so this check otherwise fails
&& GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT
&& !(gBattleTypeFlags &
(BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_FRONTIER
| BATTLE_TYPE_LINK
| BATTLE_TYPE_x2000000
| BATTLE_TYPE_SECRET_BASE)))
{
gBattlescriptCurrInstr++;
}
else if (!(gBattleTypeFlags &
(BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_FRONTIER
| BATTLE_TYPE_LINK
| BATTLE_TYPE_x2000000
| BATTLE_TYPE_SECRET_BASE))
&& (gWishFutureKnock.knockedOffMons[side] & gBitTable[gBattlerPartyIndexes[gBattlerAttacker]]))
{
gBattlescriptCurrInstr++;
}
else if (gBattleMons[gBattlerTarget].item
&& gBattleMons[gBattlerTarget].ability == ABILITY_STICKY_HOLD)
else if (gBattleMons[gBattlerTarget].item && gBattleMons[gBattlerTarget].ability == ABILITY_STICKY_HOLD)
{
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_NoItemSteal;
@ -2972,7 +2946,11 @@ void SetMoveEffect(bool32 primary, u32 certain)
{
gLastUsedItem = gBattleStruct->changedItems[gBattlerAttacker] = gBattleMons[gBattlerTarget].item;
gBattleMons[gBattlerTarget].item = 0;
RecordItemEffectBattle(gBattlerTarget, 0);
RecordItemEffectBattle(gBattlerAttacker, ItemId_GetHoldEffect(gLastUsedItem));
//item assignment doesn't happen yet
CheckSetUnburden(gBattlerTarget);
gBattleResources->flags->flags[gBattlerAttacker] &= ~(RESOURCE_FLAG_UNBURDEN);
@ -5010,7 +4988,7 @@ static void Cmd_moveend(void)
case MOVEEND_LIFE_ORB:
if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LIFE_ORB
&& IsBattlerAlive(gBattlerAttacker)
&& !(GetBattlerAbility(gBattlerAttacker) == ABILITY_SHEER_FORCE && gBattleMoves[gCurrentMove].flags & FLAG_SHEER_FORCE_BOOST)
&& !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove))
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD
&& gSpecialStatuses[gBattlerAttacker].damagedMons)
{
@ -5024,6 +5002,38 @@ static void Cmd_moveend(void)
}
gBattleScripting.moveendState++;
break;
case MOVEEND_PICKPOCKET:
if (IsBattlerAlive(gBattlerAttacker)
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_STICKY_HOLD
&& 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
for (i = 0; i < gBattlersCount; i++)
{
u8 battler = battlers[i];
if (battler != gBattlerAttacker //cannot pickpocket yourself
&& GetBattlerAbility(battler) == ABILITY_PICKPOCKET //'target' must have pickpocket ability
&& BATTLER_DAMAGED(battler) //obviously battler needs to have been damaged as well
&& !DoesSubstituteBlockMove(gCurrentMove, gBattlerAttacker, battler) //subsitute unaffected
&& IsBattlerAlive(battler) //battler must be alive to be pickpocketed
&& gBattleMons[battler].item == ITEM_NONE //pickpocketer can't have an item already
&& ItemCanBeStolen(gBattleMons[gBattlerAttacker].item, battler)) //cannot steal plates, mega stones, etc
{
gBattlerTarget = gBattlerAbility = battler;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_Pickpocket;
effect = TRUE;
break; // pickpocket activates on fastest mon, so exit loop.
}
}
}
gBattleScripting.moveendState++;
break;
case MOVEEND_DANCER: // Special case because it's so annoying
if (gBattleMoves[gCurrentMove].flags & FLAG_DANCE)
{
@ -8301,6 +8311,35 @@ static void Cmd_various(void)
gBattlescriptCurrInstr += 7;
}
return;
case VARIOUS_MOVEEND_ITEM_EFFECTS:
ItemBattleEffects(1, gActiveBattler, FALSE);
break;
case VARIOUS_PICKPOCKET:
{
// different from MOVE_EFFECT_STEAL_ITEM in that it immediately assigns the stolen item to the 'attacker'
gEffectBattler = gBattlerTarget;
gBattleScripting.battler = gBattlerAttacker;
gLastUsedItem = gBattleMons[gEffectBattler].item;
gBattleMons[gEffectBattler].item = 0;
gBattleMons[gBattlerAttacker].item = gLastUsedItem;
RecordItemEffectBattle(gBattlerTarget, 0);
RecordItemEffectBattle(gBattlerAttacker, ItemId_GetHoldEffect(gLastUsedItem));
CheckSetUnburden(gEffectBattler); //Give target Unburden boost
gBattleResources->flags->flags[gBattlerTarget] &= ~(RESOURCE_FLAG_UNBURDEN); //remove attacker boost
gActiveBattler = gBattlerAttacker;
BtlController_EmitSetMonData(0, REQUEST_HELDITEM_BATTLE, 0, 2, &gLastUsedItem);
MarkBattlerForControllerExec(gActiveBattler);
gActiveBattler = gEffectBattler;
BtlController_EmitSetMonData(0, REQUEST_HELDITEM_BATTLE, 0, 2, &gBattleMons[gActiveBattler].item);
MarkBattlerForControllerExec(gActiveBattler);
gBattleStruct->choicedMove[gEffectBattler] = 0;
}
break;
}
gBattlescriptCurrInstr += 3;

View File

@ -4227,7 +4227,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u8 ability, u8 special, u16 moveA
&& gBattleStruct->hpBefore[battler] > gBattleMons[battler].maxHP / 2
&& gBattleMons[battler].hp < gBattleMons[battler].maxHP / 2
&& (gMultiHitCounter == 0 || gMultiHitCounter == 1)
&& !(GetBattlerAbility(gBattlerAttacker) == ABILITY_SHEER_FORCE && gBattleMoves[gCurrentMove].flags & FLAG_SHEER_FORCE_BOOST)
&& !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove))
&& gBattleMons[battler].statStages[STAT_SPATK] != 12)
{
SET_STATCHANGER(STAT_SPATK, 1, FALSE);
@ -4245,7 +4245,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u8 ability, u8 special, u16 moveA
&& gBattleStruct->hpBefore[battler] > gBattleMons[battler].maxHP / 2
&& gBattleMons[battler].hp < gBattleMons[battler].maxHP / 2
&& (gMultiHitCounter == 0 || gMultiHitCounter == 1)
&& !(GetBattlerAbility(gBattlerAttacker) == ABILITY_SHEER_FORCE && gBattleMoves[gCurrentMove].flags & FLAG_SHEER_FORCE_BOOST)
&& !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove))
&& (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
&& !(gBattleTypeFlags & BATTLE_TYPE_ARENA))
{
@ -4518,24 +4518,6 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u8 ability, u8 special, u16 moveA
effect++;
}
break;
case ABILITY_PICKPOCKET:
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
&& IsBattlerAlive(gBattlerAttacker)
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& (gBattleMoves[move].flags & FLAG_MAKES_CONTACT)
&& TARGET_TURN_DAMAGED
&& IsBattlerAlive(gBattlerTarget)
&& gBattleMons[gBattlerAttacker].item != ITEM_NONE
&& gBattleMons[gBattlerTarget].item == ITEM_NONE
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_STICKY_HOLD)
{
gBattleScripting.moveEffect = MOVE_EFFECT_STEAL_ITEM;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_Pickpocket;
gHitMarker |= HITMARKER_IGNORE_SAFEGUARD;
effect++;
}
break;
}
break;
case ABILITYEFFECT_MOVE_END_ATTACKER: // Same as above, but for attacker
@ -7762,3 +7744,110 @@ u8 GetBattleMoveSplit(u32 moveId)
else
return SPLIT_SPECIAL;
}
// 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, key, keyBank;
u16 speeds[4] = {0};
for (i = 0; i < gBattlersCount; i++)
speeds[i] = GetBattlerTotalSpeedStat(battlers[i]);
for (i = 1; i < gBattlersCount; i++)
{
keyBank = battlers[i];
key = speeds[i];
j = i - 1;
if (slowToFast)
{
while (j >= 0 && speeds[j] > key)
{
battlers[j + 1] = battlers[j];
speeds[j + 1] = speeds[j];
j = j - 1;
}
}
else
{
while (j >= 0 && speeds[j] < key)
{
battlers[j + 1] = battlers[j];
speeds[j + 1] = speeds[j];
j = j - 1;
}
}
battlers[j + 1] = keyBank;
speeds[j + 1] = key;
}
}
bool32 TestSheerForceFlag(u8 battler, u16 move)
{
if (GetBattlerAbility(battler) == ABILITY_SHEER_FORCE && gBattleMoves[move].flags & FLAG_SHEER_FORCE_BOOST)
return TRUE;
else
return FALSE;
}
bool32 ItemCanBeStolen(u16 item, u8 battlerId)
{
u8 effect = ItemId_GetHoldEffect(item);
if (item == ITEM_ENIGMA_BERRY)
return FALSE;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL)
return FALSE;
if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT
&& !(gBattleTypeFlags &
(BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_FRONTIER
| BATTLE_TYPE_LINK
| BATTLE_TYPE_x2000000
| BATTLE_TYPE_SECRET_BASE)))
{
return FALSE;
}
else if (!(gBattleTypeFlags &
(BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_FRONTIER
| BATTLE_TYPE_LINK
| BATTLE_TYPE_x2000000
| BATTLE_TYPE_SECRET_BASE))
&& (gWishFutureKnock.knockedOffMons[GetBattlerSide(battlerId)] & gBitTable[gBattlerPartyIndexes[battlerId]]))
{
return FALSE;
}
if (IS_ITEM_MAIL(item))
return FALSE;
switch (effect)
{
case HOLD_EFFECT_MEGA_STONE:
#ifdef HOLD_EFFECT_MEMORY
case HOLD_EFFECT_MEMORY:
#endif
#ifdef HOLD_EFFECT_Z_CRYSTAL
case HOLD_EFFECT_Z_CRYSTAL:
#endif
#ifdef HOLD_EFFECT_DRIVE
case HOLD_EFFECT_DRIVE:
#endif
#ifdef HOLD_EFFECT_GEMS
case HOLD_EFFECT_GEMS:
#endif
#ifdef HOLD_EFFECT_GRISEOUS_ORB
case HOLD_EFFECT_GRISEOUS_ORB:
#endif
return FALSE;
}
return TRUE;
}