diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 135960c12..b1c42e491 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1759,6 +1759,11 @@ .macro destroyabilitypopup various BS_ABILITY_BATTLER, VARIOUS_DESTROY_ABILITY_POPUP .endm + + .macro gettotemboost ptr:req + various BS_ATTACKER, VARIOUS_TOTEM_BOOST + .4byte \ptr + .endm @ helpful macros .macro setstatchanger stat:req, stages:req, down:req diff --git a/asm/macros/event.inc b/asm/macros/event.inc index d0600a97b..7b5163bd8 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -1784,3 +1784,65 @@ setfieldeffectargument 2, \priority dofieldeffect FLDEFF_SPARKLE .endm + + @ Set up a totem boost for the next battle. + @ 'battler' is the position of the mon you want to gain a boost. see B_POSITION_xx in include/constants/battle.h. + @ The rest of the arguments are the stat change values to each stat. + @ For example, giving the first opponent +1 to atk and -2 to speed would be: settotemboost B_POSITION_OPPONENT_LEFT, 1, 0, -2 + .macro settotemboost battler:req, atk=0,def=0,speed=0,spatk=0,spdef=0,acc=0,evas=0 + setvar VAR_0x8000, \battler + setvar VAR_0x8001, \atk + setvar VAR_0x8002, \def + setvar VAR_0x8003, \speed + setvar VAR_0x8004, \spatk + setvar VAR_0x8005, \spdef + setvar VAR_0x8006, \acc + setvar VAR_0x8007, \evas + special SetTotemBoost + .endm + + @ useful totem boost macros + .macro totemboost_atk1 battler:req + settotemboost \battler, 1 + .endm + .macro totemboost_def1 battler:req + settotemboost \battler, 0, 1 + .endm + .macro totemboost_speed1 battler:req + settotemboost \battler, 0, 0, 1 + .endm + .macro totemboost_spatk1 battler:req + settotemboost \battler, 0, 0, 0, 1 + .endm + .macro totemboost_spdef1 battler:req + settotemboost \battler, 0, 0, 0, 0, 1 + .endm + .macro totemboost_acc1 battler:req + settotemboost \battler, 0, 0, 0, 0, 0, 1 + .endm + .macro totemboost_evas1 battler:req + settotemboost \battler, 0, 0, 0, 0, 0, 0, 1 + .endm + + .macro totemboost_atk2 battler:req + settotemboost \battler, 2 + .endm + .macro totemboost_def2 battler:req + settotemboost \battler, 0, 2 + .endm + .macro totemboost_speed2 battler:req + settotemboost \battler, 0, 0, 2 + .endm + .macro totemboost_spatk2 battler:req + settotemboost \battler, 0, 0, 0, 2 + .endm + .macro totemboost_spdef2 battler:req + settotemboost \battler, 0, 0, 0, 0, 2 + .endm + .macro totemboost_acc2 battler:req + settotemboost \battler, 0, 0, 0, 0, 0, 2 + .endm + .macro totemboost_evas2 battler:req + settotemboost \battler, 0, 0, 0, 0, 0, 0, 2 + .endm + diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index c78be244d..16be607f0 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -825,6 +825,7 @@ gBattleAnims_General:: .4byte General_FormChange .4byte General_SlideOffScreen .4byte General_RestoreBg + .4byte General_TotemFlare .align 2 gBattleAnims_Special:: @@ -24400,6 +24401,36 @@ General_RestoreBg: waitbgfadein end +General_TotemFlare:: + loadspritegfx ANIM_TAG_FOCUS_ENERGY + loadspritegfx ANIM_TAG_WHIP_HIT @green color + loadspritegfx ANIM_TAG_SWEAT_BEAD @blue color + loadspritegfx ANIM_TAG_PAW_PRINT @yellow color + monbg ANIM_ATTACKER + setblends 0x80c + playsewithpan SE_M_DRAGON_RAGE, SOUND_PAN_ATTACKER + launchtask AnimTask_BlendColorCycle 0x2 0x6 ANIM_PAL_ATK 0x0 0x6 0x0 0xb 0x1f + call RainbowEndureEffect + call RainbowEndureEffect + call RainbowEndureEffect + call RainbowEndureEffect + call RainbowEndureEffect + waitforvisualfinish + blendoff + clearmonbg ANIM_ATTACKER + end + +RainbowEndureEffect: + launchtemplate gBlueEndureEnergySpriteTemplate 0x2 0x4 0x0 0xffe8 0x1a 0x2 + delay 0x3 + launchtemplate gEndureEnergySpriteTemplate 0x2 0x4 0x0 0xe 0x1c 0x1 @Red Buff + delay 0x3 + launchtemplate gGreenEndureEnergySpriteTemplate 0x2 0x4 0x0 0xfffb 0xa 0x2 + delay 0x3 + launchtemplate gYellowEndureEnergySpriteTemplate 0x2 0x4 0x0 0x1c 0x1a 0x3 + delay 0x3 + return + SnatchMoveTrySwapFromSubstitute: createvisualtask AnimTask_IsAttackerBehindSubstitute, 2 jumprettrue SnatchMoveSwapSubstituteForMon diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 159410dcf..f4b2ab7a7 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7767,6 +7767,25 @@ BattleScript_PrintPlayerForfeitedLinkBattle:: waitmessage 0x40 end2 +BattleScript_TotemFlaredToLife:: + playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE, NULL + printstring STRINGID_AURAFLAREDTOLIFE + waitmessage 0x40 + goto BattleScript_ApplyTotemVarBoost + +BattleScript_TotemVar:: + gettotemboost BattleScript_ApplyTotemVarBoost +BattleScript_TotemVarEnd: + end2 +BattleScript_ApplyTotemVarBoost: + statbuffchange STAT_BUFF_ALLOW_PTR, BattleScript_TotemVarEnd + setgraphicalstatchangevalues + playanimation BS_SCRIPTING, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 +BattleScript_TotemVarPrintStatMsg: + printfromtable gStatUpStringIds + waitmessage 0x40 + goto BattleScript_TotemVar @loop until stats bitfield is empty + BattleScript_AnnounceAirLockCloudNine:: call BattleScript_AbilityPopUp printstring STRINGID_AIRLOCKACTIVATES diff --git a/data/specials.inc b/data/specials.inc index 77e7d5c0a..fe596a0f0 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -535,3 +535,4 @@ gSpecials:: @ 81DBA64 def_special RemoveRecordsWindow def_special CloseDeptStoreElevatorWindow def_special TrySetBattleTowerLinkType + def_special SetTotemBoost diff --git a/include/battle.h b/include/battle.h index 8db0b3195..5917a4ab3 100644 --- a/include/battle.h +++ b/include/battle.h @@ -715,6 +715,12 @@ struct MonSpritesGfx u16 *buffer; }; +struct TotemBoost +{ + u8 stats; // bitfield for each battle stat that is set if the stat changes + s8 statChanges[NUM_BATTLE_STATS - 1]; // highest bit being set decreases the stat +}; /* size = 8 */ + // All battle variables are declared in battle_main.c extern u16 gBattle_BG0_X; extern u16 gBattle_BG0_Y; @@ -823,6 +829,7 @@ extern u32 gFieldStatuses; extern struct FieldTimer gFieldTimers; extern u8 gBattlerAbility; extern u16 gPartnerSpriteId; +extern struct TotemBoost gTotemBoosts[MAX_BATTLERS_COUNT]; extern void (*gPreBattleCallback1)(void); extern void (*gBattleMainFunc)(void); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 2d4fb2ed2..009590f3a 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -349,6 +349,8 @@ extern const u8 BattleScript_EmergencyExitNoPopUp[]; extern const u8 BattleScript_EmergencyExitWild[]; extern const u8 BattleScript_EmergencyExitWildNoPopUp[]; extern const u8 BattleScript_CheekPouchActivates[]; +extern const u8 BattleScript_TotemVar[]; +extern const u8 BattleScript_TotemFlaredToLife[]; extern const u8 BattleScript_AnnounceAirLockCloudNine[]; extern const u8 BattleScript_BattlerAbilityStatRaiseOnSwitchIn[]; extern const u8 BattleScript_CottonDownActivates[]; diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index b9ba08515..90766f9d5 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -526,6 +526,7 @@ #define B_ANIM_FORM_CHANGE 0x1D #define B_ANIM_SLIDE_OFFSCREEN 0x1E // for Emergency Exit #define B_ANIM_RESTORE_BG 0x1F // for Terrain Endings +#define B_ANIM_TOTEM_FLARE 0x20 // Totem boosts aura flare // special animations table #define B_ANIM_LVL_UP 0x0 diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 2f52da319..424009d20 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -167,6 +167,7 @@ #define VARIOUS_PARALYZE_TYPE_IMMUNITY 100 #define VARIOUS_JUMP_IF_ABSENT 101 #define VARIOUS_DESTROY_ABILITY_POPUP 102 +#define VARIOUS_TOTEM_BOOST 103 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index a57297501..73b91c708 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -563,8 +563,9 @@ #define STRINGID_ASANDSTORMKICKEDUP 559 #define STRINGID_PKMNSWILLPERISHIN3TURNS 560 #define STRINGID_ABILITYRAISEDSTATDRASTICALLY 561 +#define STRINGID_AURAFLAREDTOLIFE 562 -#define BATTLESTRINGS_COUNT 562 +#define BATTLESTRINGS_COUNT 563 //// multichoice message IDs // switch in ability message diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c index b241e7d90..6b528c71e 100644 --- a/src/battle_anim_effects_1.c +++ b/src/battle_anim_effects_1.c @@ -1926,6 +1926,39 @@ const struct SpriteTemplate gEndureEnergySpriteTemplate = .callback = AnimEndureEnergy, }; +const struct SpriteTemplate gBlueEndureEnergySpriteTemplate = +{ + .tileTag = ANIM_TAG_FOCUS_ENERGY, + .paletteTag = ANIM_TAG_SWEAT_BEAD, + .oam = &gOamData_AffineOff_ObjNormal_16x32, + .anims = gEndureEnergyAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimEndureEnergy, +}; + +const struct SpriteTemplate gGreenEndureEnergySpriteTemplate = +{ + .tileTag = ANIM_TAG_FOCUS_ENERGY, + .paletteTag = ANIM_TAG_WHIP_HIT, + .oam = &gOamData_AffineOff_ObjNormal_16x32, + .anims = gEndureEnergyAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimEndureEnergy, +}; + +const struct SpriteTemplate gYellowEndureEnergySpriteTemplate = +{ + .tileTag = ANIM_TAG_FOCUS_ENERGY, + .paletteTag = ANIM_TAG_PAW_PRINT, + .oam = &gOamData_AffineOff_ObjNormal_16x32, + .anims = gEndureEnergyAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimEndureEnergy, +}; + const union AnimCmd gSharpenSphereAnimCmds[] = { ANIMCMD_FRAME(0, 18), diff --git a/src/battle_main.c b/src/battle_main.c index 048ba0157..f48129acb 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -228,6 +228,7 @@ EWRAM_DATA u32 gFieldStatuses = 0; EWRAM_DATA struct FieldTimer gFieldTimers = {0}; EWRAM_DATA u8 gBattlerAbility = 0; EWRAM_DATA u16 gPartnerSpriteId = 0; +EWRAM_DATA struct TotemBoost gTotemBoosts[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA bool8 gHasFetchedBall = FALSE; EWRAM_DATA u8 gLastUsedBall = 0; @@ -3488,6 +3489,19 @@ static void TryDoEventsBeforeFirstTurn(void) gBattleStruct->overworldWeatherDone = TRUE; return; } + + // Totem boosts + for (i = 0; i < gBattlersCount; i++) + { + if (gTotemBoosts[i].stats != 0) + { + gBattlerAttacker = i; + BattleScriptExecute(BattleScript_TotemVar); + return; + } + } + memset(gTotemBoosts, 0, sizeof(gTotemBoosts)); // erase all totem boosts just to be safe + // Check all switch in abilities happening from the fastest mon to slowest. while (gBattleStruct->switchInAbilitiesCounter < gBattlersCount) { @@ -3505,6 +3519,7 @@ static void TryDoEventsBeforeFirstTurn(void) if (ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, gBattlerByTurnOrder[gBattleStruct->switchInItemsCounter++], FALSE)) return; } + for (i = 0; i < MAX_BATTLERS_COUNT; i++) { *(gBattleStruct->monToSwitchIntoId + i) = PARTY_SIZE; @@ -5054,3 +5069,23 @@ void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk) gSpecialStatuses[battlerAtk].gemBoost = 1; } } + +// special to set a field's totem boost(s) +// inputs: +// var8000: battlerId +// var8001 - var8007: stat changes +void SetTotemBoost(void) +{ + u8 battlerId = gSpecialVar_0x8000; + u8 i; + + for (i = 0; i < (NUM_BATTLE_STATS - 1); i++) + { + if (*(&gSpecialVar_0x8001 + i)) + { + gTotemBoosts[battlerId].stats |= (1 << i); + gTotemBoosts[battlerId].statChanges[i] = *(&gSpecialVar_0x8001 + i); + gTotemBoosts[battlerId].stats |= 0x80; // used as a flag for the "totem flared to life" script + } + } +} diff --git a/src/battle_message.c b/src/battle_message.c index 71101608a..36db7b450 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -678,6 +678,7 @@ static const u8 sText_NoOneWillBeAbleToRun[] = _("No one will be able to run awa static const u8 sText_DestinyKnotActivates[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} fell in love\nfrom the {B_LAST_ITEM}!"); static const u8 sText_CloakedInAFreezingLight[] = _("{B_ATK_NAME_WITH_PREFIX} became cloaked\nin a freezing light!"); static const u8 sText_StatWasNotLowered[] = _("{B_DEF_NAME_WITH_PREFIX}'s {B_BUFF1}\nwas not lowered!"); +static const u8 sText_AuraFlaredToLife[] = _("{B_DEF_NAME_WITH_PREFIX}'s aura flared to life!"); static const u8 sText_AirLockActivates[] = _("The effects of weather\ndisappeared."); static const u8 sText_PressureActivates[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is exerting its\npressure!"); static const u8 sText_DarkAuraActivates[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is radiating\na dark aura!"); @@ -1236,6 +1237,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_GRASSYTERRAINHEALS - 12] = sText_GrassyTerrainHeals, [STRINGID_ELECTRICTERRAINPREVENTS - 12] = sText_ElectricTerrainPreventsSleep, [STRINGID_PSYCHICTERRAINPREVENTS - 12] = sText_PsychicTerrainPreventsPriority, + [STRINGID_AURAFLAREDTOLIFE - 12] = sText_AuraFlaredToLife, [STRINGID_AIRLOCKACTIVATES - 12] = sText_AirLockActivates, [STRINGID_PRESSUREENTERS - 12] = sText_PressureActivates, [STRINGID_DARKAURAENTERS - 12] = sText_DarkAuraActivates, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index c7866694a..6c5f37a7d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8298,6 +8298,41 @@ static void Cmd_various(void) case VARIOUS_DESTROY_ABILITY_POPUP: DestroyAbilityPopUp(gActiveBattler); break; + case VARIOUS_TOTEM_BOOST: + gActiveBattler = gBattlerAttacker; + if (gTotemBoosts[gActiveBattler].stats == 0) + { + gBattlescriptCurrInstr += 7; // stats done, exit + } + else + { + for (i = 0; i < (NUM_BATTLE_STATS - 1); i++) + { + if (gTotemBoosts[gActiveBattler].stats & (1 << i)) + { + if (gTotemBoosts[gActiveBattler].statChanges[i] <= -1) + SET_STATCHANGER(i + 1, abs(gTotemBoosts[gActiveBattler].statChanges[i]), TRUE); + else + SET_STATCHANGER(i + 1, gTotemBoosts[gActiveBattler].statChanges[i], FALSE); + + gTotemBoosts[gActiveBattler].stats &= ~(1 << i); + gBattleScripting.battler = gActiveBattler; + gBattlerTarget = gActiveBattler; + if (gTotemBoosts[gActiveBattler].stats & 0x80) + { + gTotemBoosts[gActiveBattler].stats &= ~0x80; // set 'aura flared to life' flag + gBattlescriptCurrInstr = BattleScript_TotemFlaredToLife; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); // do boost + } + return; + } + } + gBattlescriptCurrInstr += 7; // exit if loop failed (failsafe) + } + return; } gBattlescriptCurrInstr += 3;