diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 41a51efeb..d7d85ff6e 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1755,6 +1755,11 @@ various \battler, VARIOUS_JUMP_IF_ABSENT .4byte \ptr .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..62f59ecd9 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -1784,3 +1784,61 @@ setfieldeffectargument 2, \priority dofieldeffect FLDEFF_SPARKLE .endm + + .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 70ed22c9e..4675451e5 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -815,6 +815,7 @@ gBattleAnims_General:: .4byte General_IllusionOff .4byte General_FormChange .4byte General_SlideOffScreen + .4byte General_TotemFlare .align 2 gBattleAnims_Special:: @@ -24320,6 +24321,36 @@ General_TerrainElectric: General_TerrainPsychic: 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 504896d63..5a52e0f64 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7688,3 +7688,23 @@ BattleScript_PrintPlayerForfeitedLinkBattle:: atk57 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 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 13fe13171..53324b31b 100644 --- a/include/battle.h +++ b/include/battle.h @@ -709,6 +709,12 @@ struct MonSpritesGfx u16 *buffer; }; +struct TotemBoost +{ + u8 stats; //bitfield for each battle stat + u8 statChanges[NUM_BATTLE_STATS - 1]; //highest bit is decrease +}; /* size = 8 */ + // All battle variables are declared in battle_main.c extern u16 gBattle_BG0_X; extern u16 gBattle_BG0_Y; @@ -817,6 +823,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 84a7ba2ef..df4b6db5a 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -349,5 +349,7 @@ 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[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index 871aa1e35..5596f28bf 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -525,6 +525,7 @@ #define B_ANIM_ILLUSION_OFF 0x1C #define B_ANIM_FORM_CHANGE 0x1D #define B_ANIM_SLIDE_OFFSCREEN 0x1E // for Emergency Exit +#define B_ANIM_TOTEM_FLARE 0x1F // 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 b081907ec..45f679d62 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -165,6 +165,7 @@ #define VARIOUS_SET_LAST_USED_ITEM 99 #define VARIOUS_PARALYZE_TYPE_IMMUNITY 100 #define VARIOUS_JUMP_IF_ABSENT 101 +#define VARIOUS_TOTEM_BOOST 102 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 3c6d74b48..68d8fbc30 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -551,7 +551,8 @@ #define STRINGID_CLOAKEDINAFREEZINGLIGHT 547 #define STRINGID_STATWASNOTLOWERED 548 #define STRINGID_FERVENTWISHREACHED 549 +#define STRINGID_AURAFLAREDTOLIFE 550 -#define BATTLESTRINGS_COUNT 550 +#define BATTLESTRINGS_COUNT 551 #endif // GUARD_CONSTANTS_BATTLE_STRING_IDS_H 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 793a37022..3e9228997 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}; // IWRAM common vars void (*gPreBattleCallback1)(void); @@ -3500,6 +3501,18 @@ static void TryDoEventsBeforeFirstTurn(void) if (ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, gBattlerByTurnOrder[gBattleStruct->switchInItemsCounter++], FALSE)) return; } + // Totem + 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 + for (i = 0; i < MAX_BATTLERS_COUNT; i++) { *(gBattleStruct->monToSwitchIntoId + i) = PARTY_SIZE; @@ -5049,3 +5062,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 9e86d2774..dbbc89983 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!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { @@ -1219,6 +1220,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, }; const u16 gTerrainStringIds[] = diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index d0811e77d..adf072636 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8300,6 +8300,40 @@ static void Cmd_various(void) gBattlescriptCurrInstr += 7; } return; + 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)) + { + bool8 negative = (gTotemBoosts[gActiveBattler].statChanges[i] & 0x80) ? TRUE : FALSE; + u8 change = gTotemBoosts[gActiveBattler].statChanges[i] & 0x7F; + + gTotemBoosts[gActiveBattler].stats &= ~(1 << i); + SET_STATCHANGER(i + 1, change, negative); + gBattleScripting.battler = gActiveBattler; + gBattlerTarget = gActiveBattler; + if (gTotemBoosts[gActiveBattler].stats & 0x80) + { + gTotemBoosts[gActiveBattler].stats &= ~0x80; + gBattlescriptCurrInstr = BattleScript_TotemFlaredToLife; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); //do boost + } + return; + } + } + gBattlescriptCurrInstr += 7; //exit if loop failed (failsafe) + } + return; } gBattlescriptCurrInstr += 3;