From 73953b3e39353687b757fb760765cc3b66fef59e Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sat, 10 Aug 2024 14:43:50 -0700 Subject: [PATCH 01/65] Added removeallitem --- asm/macros/event.inc | 6 ++++++ src/scrcmd.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 891c7fd4ed..f5abad6f29 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2321,3 +2321,9 @@ .macro togglefakertc callnative Script_ToggleFakeRtc .endm + + @ Remove all of specified item from the player's bag + .macro removeallitem itemId:req + callnative RemoveAllItem + .2byte \itemId + .endm diff --git a/src/scrcmd.c b/src/scrcmd.c index 319e667566..955b5e00dd 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -2473,3 +2473,9 @@ void ScriptSetDoubleBattleFlag(struct ScriptContext *ctx) { sIsScriptedWildDouble = TRUE; } + +void RemoveAllItem(struct ScriptContext *ctx) +{ + u16 itemId = VarGet(ScriptReadHalfword(ctx)); + RemoveBagItem(itemId, CountTotalItemQuantityInBag(itemId)); +} From 811b5d286e02ec7f433cd3f11692c2a56370b9f4 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sat, 10 Aug 2024 21:17:50 -0700 Subject: [PATCH 02/65] Added getobjectposition --- asm/macros/event.inc | 7 +++++++ data/specials.inc | 1 + include/event_object_movement.h | 2 ++ src/event_object_movement.c | 16 +++++++--------- src/field_specials.c | 28 ++++++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 9 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index f5abad6f29..c6e6e8847a 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2327,3 +2327,10 @@ callnative RemoveAllItem .2byte \itemId .endm + + @ return current (posType = 0) or map (posType = 1) position of object to VAR_0x8007 (x), VAR_0x8008 (y) + .macro getobjectxy localId:req, posType:req + setvar VAR_0x8000, \localId + setvar VAR_0x8001, \posType + special GetObjectPosition + .endm diff --git a/data/specials.inc b/data/specials.inc index f02497d603..32f2f63308 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -554,3 +554,4 @@ gSpecials:: def_special Script_GetChosenMonDefensiveEVs def_special Script_GetChosenMonOffensiveIVs def_special Script_GetChosenMonDefensiveIVs + def_special GetObjectPosition diff --git a/include/event_object_movement.h b/include/event_object_movement.h index 2906789b37..92675f01f7 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -126,6 +126,7 @@ bool8 TryGetObjectEventIdByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroupId, u u8 GetObjectEventIdByXY(s16 x, s16 y); void SetObjectEventDirection(struct ObjectEvent *objectEvent, u8 direction); u8 GetFirstInactiveObjectEventId(void); +u8 GetObjectEventIdByLocalId(u8); void RemoveObjectEventByLocalIdAndMap(u8 localId, u8 mapNum, u8 mapGroup); void LoadSpecialObjectReflectionPalette(u16 tag, u8 slot); void TryMoveObjectEventToMapCoords(u8 localId, u8 mapNum, u8 mapGroup, s16 x, s16 y); @@ -210,6 +211,7 @@ void ObjectEventForceSetHeldMovement(struct ObjectEvent *objectEvent, u8 movemen bool8 ObjectEventIsMovementOverridden(struct ObjectEvent *objectEvent); u8 ObjectEventCheckHeldMovementStatus(struct ObjectEvent *objectEvent); u8 ObjectEventGetHeldMovementActionId(struct ObjectEvent *objectEvent); +const struct ObjectEventTemplate *FindObjectEventTemplateByLocalId(u8, const struct ObjectEventTemplate *, u8); void TryOverrideTemplateCoordsForObjectEvent(const struct ObjectEvent *objectEvent, u8 movementType); void OverrideTemplateCoordsForObjectEvent(const struct ObjectEvent *objectEvent); void ShiftStillObjectEventCoords(struct ObjectEvent *objEvent); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 5a6eb28fc3..e4732cac96 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -157,7 +157,6 @@ static void ApplyLevitateMovement(u8); static bool8 MovementType_Disguise_Callback(struct ObjectEvent *, struct Sprite *); static bool8 MovementType_Buried_Callback(struct ObjectEvent *, struct Sprite *); static void CreateReflectionEffectSprites(void); -static u8 GetObjectEventIdByLocalId(u8); static u8 GetObjectEventIdByLocalIdAndMapInternal(u8, u8, u8); static bool8 GetAvailableObjectEventId(u16, u8, u8, u8 *); static void SetObjectEventDynamicGraphicsId(struct ObjectEvent *); @@ -179,7 +178,6 @@ static void SpriteCB_CameraObject(struct Sprite *); static void CameraObject_Init(struct Sprite *); static void CameraObject_UpdateMove(struct Sprite *); static void CameraObject_UpdateFrozen(struct Sprite *); -static const struct ObjectEventTemplate *FindObjectEventTemplateByLocalId(u8, const struct ObjectEventTemplate *, u8); static void ObjectEventSetSingleMovement(struct ObjectEvent *, struct Sprite *, u8); static void SetSpriteDataForNormalStep(struct Sprite *, u8, u8); static void InitSpriteForFigure8Anim(struct Sprite *); @@ -1298,7 +1296,7 @@ static u8 GetObjectEventIdByLocalIdAndMapInternal(u8 localId, u8 mapNum, u8 mapG return OBJECT_EVENTS_COUNT; } -static u8 GetObjectEventIdByLocalId(u8 localId) +u8 GetObjectEventIdByLocalId(u8 localId) { u8 i; for (i = 0; i < OBJECT_EVENTS_COUNT; i++) @@ -1493,7 +1491,7 @@ static s16 ReallocSpriteTiles(struct Sprite *sprite, u32 byteSize) { i = -1; } - + sprite->invisible = wasVisible; return i; } @@ -1510,7 +1508,7 @@ u16 LoadSheetGraphicsInfo(const struct ObjectEventGraphicsInfo *info, u16 uuid, bool32 oldInvisible; if (tag == TAG_NONE) tag = COMP_OW_TILE_TAG_BASE + uuid; - + if (sprite) { oldInvisible = sprite->invisible; @@ -1547,7 +1545,7 @@ u16 LoadSheetGraphicsInfo(const struct ObjectEventGraphicsInfo *info, u16 uuid, { FieldEffectFreeTilesIfUnused(oldTiles); } - + if (sprite) { sprite->sheetTileStart = tileStart; @@ -1566,7 +1564,7 @@ u16 LoadSheetGraphicsInfo(const struct ObjectEventGraphicsInfo *info, u16 uuid, { sprite->oam.tileNum = sprite->sheetTileStart; sprite->usingSheet = FALSE; - + } else if (sprite && !sprite->sheetTileStart && sprite->oam.size != info->oam->size) { @@ -1925,7 +1923,7 @@ static u8 LoadDynamicFollowerPalette(u16 species, u8 form, bool32 shiny) spritePalette.data = gSpeciesInfo[species].overworldShinyPalette; else spritePalette.data = gSpeciesInfo[species].overworldPalette; - + // Check if pal data must be decompressed if (IsLZ77Data(spritePalette.data, PLTT_SIZE_4BPP, PLTT_SIZE_4BPP)) { @@ -3306,7 +3304,7 @@ static const struct ObjectEventTemplate *GetObjectEventTemplateByLocalIdAndMap(u return FindObjectEventTemplateByLocalId(localId, templates, count); } -static const struct ObjectEventTemplate *FindObjectEventTemplateByLocalId(u8 localId, const struct ObjectEventTemplate *templates, u8 count) +const struct ObjectEventTemplate *FindObjectEventTemplateByLocalId(u8 localId, const struct ObjectEventTemplate *templates, u8 count) { u8 i; diff --git a/src/field_specials.c b/src/field_specials.c index b44c8327aa..471d88132c 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4266,3 +4266,31 @@ void PreparePartyForSkyBattle(void) VarSet(B_VAR_SKY_BATTLE,participatingPokemonSlot); CompactPartySlots(); } + +// get position (0 for current, 1 for map) of object event, return to VAR_0x8007, VAR_0x8008 +void GetObjectPosition(void) +{ + u16 localId = gSpecialVar_0x8000; + u16 useTemplate = gSpecialVar_0x8001; + + u16 *x = &gSpecialVar_0x8007; + u16 *y = &gSpecialVar_0x8008; + + if (!useTemplate) + { + /* current position */ + const u16 objId = GetObjectEventIdByLocalId(localId); + const struct ObjectEvent *objEvent = &gObjectEvents[objId]; + *x = objEvent->currentCoords.x - 7; // subtract out camera size + *y = objEvent->currentCoords.y - 7; + } + else + { + const struct ObjectEventTemplate *objTemplate = + FindObjectEventTemplateByLocalId(localId, + gSaveBlock1Ptr->objectEventTemplates, + gMapHeader.events->objectEventCount); + *x = objTemplate->x; + *y = objTemplate->y; + } +} From 0784f9fa740f697ecf1ef32c809014304603a8a4 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sat, 10 Aug 2024 21:40:57 -0700 Subject: [PATCH 03/65] Added checkobjectat --- asm/macros/event.inc | 7 +++++++ data/specials.inc | 1 + src/field_specials.c | 16 ++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index c6e6e8847a..0fb6414f61 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2334,3 +2334,10 @@ setvar VAR_0x8001, \posType special GetObjectPosition .endm + + @ checks if there is any object at a given position + .macro checkobjectat x:req, y:req + setorcopyvar VAR_0x8005, \x + setorcopyvar VAR_0x8006, \y + specialvar VAR_RESULT, CheckObjectAtXY + .endm diff --git a/data/specials.inc b/data/specials.inc index 32f2f63308..aa994a31f6 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -555,3 +555,4 @@ gSpecials:: def_special Script_GetChosenMonOffensiveIVs def_special Script_GetChosenMonDefensiveIVs def_special GetObjectPosition + def_special CheckObjectAtXY diff --git a/src/field_specials.c b/src/field_specials.c index 471d88132c..3014d48407 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4294,3 +4294,19 @@ void GetObjectPosition(void) *y = objTemplate->y; } } + +// special to check if there is any object at a given position +u16 CheckObjectAtXY(void) +{ + u16 x = gSpecialVar_0x8005 + 7; + u16 y = gSpecialVar_0x8006 + 7; + u32 i; + + for (i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + if (gObjectEvents[i].active && gObjectEvents[i].currentCoords.x == x && gObjectEvents[i].currentCoords.y == y) + return TRUE; + } + return FALSE; +} + From fba1452c3b7169a4ea54fba3956299392c4a0d76 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sun, 11 Aug 2024 18:49:42 -0700 Subject: [PATCH 04/65] Added Seen/CaughtMon macros --- asm/macros/event.inc | 26 ++++++++++++++++++++++++++ data/specials.inc | 4 ++++ src/field_specials.c | 22 ++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 0fb6414f61..e3c47c04ea 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2341,3 +2341,29 @@ setorcopyvar VAR_0x8006, \y specialvar VAR_RESULT, CheckObjectAtXY .endm + + @ Checks the state of the Pokédex Seen flag of the specified Pokemon + @ The result is stored in VAR_RESULT + .macro getseenmon species:req + setvar VAR_TEMP_1, \species + specialvar VAR_RESULT, GetSeenMon + .endm + + @ Checks the state of the Pokédex Caught flag of the specified Pokemon + @ The result is stored in VAR_RESULT + .macro getcaughtmon species:req + setvar VAR_TEMP_1, \species + specialvar VAR_RESULT, GetCaughtMon + .endm + + @ Sets the Pokédex Seen flag of the specified Pokemon + .macro setseenmon species:req + setvar VAR_TEMP_1, \species + special SetSeenMon + .endm + + @ Sets the Pokédex Caught flag of the specified Pokemon + .macro setcaughtmon species:req + setvar VAR_TEMP_1, \species + special SetCaughtMon + .endm diff --git a/data/specials.inc b/data/specials.inc index aa994a31f6..8f17fd8e55 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -556,3 +556,7 @@ gSpecials:: def_special Script_GetChosenMonDefensiveIVs def_special GetObjectPosition def_special CheckObjectAtXY + def_special GetSeenMon + def_special GetCaughtMon + def_special SetSeenMon + def_special SetCaughtMon diff --git a/src/field_specials.c b/src/field_specials.c index 3014d48407..64330d2e6d 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -68,6 +68,7 @@ #include "constants/metatile_labels.h" #include "palette.h" #include "battle_util.h" +#include "pokedex.h" #define TAG_ITEM_ICON 5500 @@ -4310,3 +4311,24 @@ u16 CheckObjectAtXY(void) return FALSE; } +bool8 GetSeenMon(void) +{ + return GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_GET_SEEN); +} + +bool8 GetCaughtMon(void) +{ + return GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_GET_CAUGHT); +} + +void SetSeenMon(void) +{ + GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_SET_SEEN); +} + +void SetCaughtMon(void) +{ + GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_SET_SEEN); + GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_SET_CAUGHT); +} + From e746334e1e664f6e5eaa8defa6b3a4d73b5a9283 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sun, 11 Aug 2024 18:53:39 -0700 Subject: [PATCH 05/65] Added setmonball --- asm/macros/event.inc | 8 ++++++++ data/specials.inc | 1 + src/field_specials.c | 5 +++++ 3 files changed, 14 insertions(+) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index e3c47c04ea..7529edc55a 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2367,3 +2367,11 @@ setvar VAR_TEMP_1, \species special SetCaughtMon .endm + + @ Changes the caught ball of a selected Pokémon + .macro setmonball ballId:req + special ChoosePartyMon + waitstate + setvar VAR_TEMP_1, \ballId + special SetMonBall + .endm diff --git a/data/specials.inc b/data/specials.inc index 8f17fd8e55..67e82c0e18 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -560,3 +560,4 @@ gSpecials:: def_special GetCaughtMon def_special SetSeenMon def_special SetCaughtMon + def_special SetMonBall diff --git a/src/field_specials.c b/src/field_specials.c index 64330d2e6d..a574083ae4 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4332,3 +4332,8 @@ void SetCaughtMon(void) GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_SET_CAUGHT); } +void SetMonBall(void) +{ + u16 ballId = VarGet(VAR_TEMP_1); + SetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_POKEBALL, &ballId); +} From 262505589a69a71cf7995624ff67a00b0d2a6fb3 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sun, 11 Aug 2024 19:19:15 -0700 Subject: [PATCH 06/65] checkforspecies --- asm/macros/event.inc | 17 +++++++++++++++++ data/specials.inc | 1 + src/field_specials.c | 12 ++++++++++++ 3 files changed, 30 insertions(+) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 7529edc55a..da6935ed3c 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2375,3 +2375,20 @@ setvar VAR_TEMP_1, \ballId special SetMonBall .endm + + OPEN_PARTY_SCREEN = FALSE + NO_PARTY_SCREEN = TRUE + + @ Check if the Player has \speciesId in their party. + .macro checkforspecies speciesId:req, silent:req, script:req + .if \silent == OPEN_PARTY_SCREEN + special ChoosePartyMon + waitstate + specialvar VAR_RESULT, ScriptGetPartyMonSpecies + goto_if_eq VAR_RESULT, \speciesId, \script + .else + setvar VAR_TEMP_1, \speciesId + specialvar VAR_RESULT, CheckPartyForMon + goto_if_eq VAR_RESULT, TRUE, \script + .endif + .endm diff --git a/data/specials.inc b/data/specials.inc index 67e82c0e18..00a8c54029 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -561,3 +561,4 @@ gSpecials:: def_special SetSeenMon def_special SetCaughtMon def_special SetMonBall + def_special CheckPartyForMon diff --git a/src/field_specials.c b/src/field_specials.c index a574083ae4..78ac17678b 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4337,3 +4337,15 @@ void SetMonBall(void) u16 ballId = VarGet(VAR_TEMP_1); SetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_POKEBALL, &ballId); } + +bool8 CheckPartyForMon(void) +{ + int i; + for (i = 0; i < CalculatePlayerPartyCount(); i++) + { + if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) == VarGet(VAR_TEMP_1)) + return TRUE; + } + return FALSE; +} + From 7f5ecd8d0f5f95d284241d83b04a33df2b02322d Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sun, 11 Aug 2024 19:27:18 -0700 Subject: [PATCH 07/65] getobjectfacingdirection --- asm/macros/event.inc | 6 ++++++ data/specials.inc | 1 + src/event_object_movement.c | 7 +++++++ 3 files changed, 14 insertions(+) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index da6935ed3c..f29a2dfb06 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2392,3 +2392,9 @@ goto_if_eq VAR_RESULT, TRUE, \script .endif .endm + + @ Gets the facing direction of a given event object and stores it in the variable \dest. + .macro getobjectfacingdirection evObjId:req, dest:req + setvar VAR_TEMP_1, \evObjId + specialvar \dest, Script_GetObjectFacingDirection + .endm diff --git a/data/specials.inc b/data/specials.inc index 00a8c54029..5b0aa23ddd 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -562,3 +562,4 @@ gSpecials:: def_special SetCaughtMon def_special SetMonBall def_special CheckPartyForMon + def_special Script_GetObjectFacingDirection diff --git a/src/event_object_movement.c b/src/event_object_movement.c index e4732cac96..7facbab5c9 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -10632,3 +10632,10 @@ void GetDaycareGraphics(struct ScriptContext *ctx) } gSpecialVar_Result = i; } + +u8 Script_GetObjectFacingDirection(void) +{ + u8 objId = GetObjectEventIdByLocalId(VarGet(VAR_TEMP_1)); + return gObjectEvents[objId].facingDirection; +} + From 7869cb4137fcc48a4725110dd5767265aa812a42 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sun, 11 Aug 2024 19:31:52 -0700 Subject: [PATCH 08/65] Improved RemoveAllItem --- src/scrcmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scrcmd.c b/src/scrcmd.c index 955b5e00dd..12ec041895 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -2476,6 +2476,6 @@ void ScriptSetDoubleBattleFlag(struct ScriptContext *ctx) void RemoveAllItem(struct ScriptContext *ctx) { - u16 itemId = VarGet(ScriptReadHalfword(ctx)); + u32 itemId = VarGet(ScriptReadHalfword(ctx)); RemoveBagItem(itemId, CountTotalItemQuantityInBag(itemId)); } From ebae09f0a6b9132ad53c925d9717dcb4b07247f3 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sun, 11 Aug 2024 20:16:27 -0700 Subject: [PATCH 09/65] Improved checkobjectat --- asm/macros/event.inc | 5 +++- src/field_specials.c | 67 ++++++++++++++++++++++---------------------- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index f29a2dfb06..9375b7be9a 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2328,7 +2328,10 @@ .2byte \itemId .endm - @ return current (posType = 0) or map (posType = 1) position of object to VAR_0x8007 (x), VAR_0x8008 (y) + CURRENT_POSITION = FALSE + TEMPLATE_POSITION = TRUE + + @ Stores the CURRENT / TEMPLATE position of the given object in VAR_0x8007 (x) and VAR_0x8008 (y) .macro getobjectxy localId:req, posType:req setvar VAR_0x8000, \localId setvar VAR_0x8001, \posType diff --git a/src/field_specials.c b/src/field_specials.c index 78ac17678b..98a909d725 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4268,47 +4268,48 @@ void PreparePartyForSkyBattle(void) CompactPartySlots(); } -// get position (0 for current, 1 for map) of object event, return to VAR_0x8007, VAR_0x8008 void GetObjectPosition(void) { - u16 localId = gSpecialVar_0x8000; - u16 useTemplate = gSpecialVar_0x8001; + u32 localId = gSpecialVar_0x8000; + u32 useTemplate = gSpecialVar_0x8001; + u32 objectId; + struct ObjectEvent* objEvent; - u16 *x = &gSpecialVar_0x8007; - u16 *y = &gSpecialVar_0x8008; + u16 *x = &gSpecialVar_0x8007; + u16 *y = &gSpecialVar_0x8008; - if (!useTemplate) - { - /* current position */ - const u16 objId = GetObjectEventIdByLocalId(localId); - const struct ObjectEvent *objEvent = &gObjectEvents[objId]; - *x = objEvent->currentCoords.x - 7; // subtract out camera size - *y = objEvent->currentCoords.y - 7; - } - else - { - const struct ObjectEventTemplate *objTemplate = - FindObjectEventTemplateByLocalId(localId, - gSaveBlock1Ptr->objectEventTemplates, - gMapHeader.events->objectEventCount); - *x = objTemplate->x; - *y = objTemplate->y; - } + if (useTemplate) + { + const struct ObjectEventTemplate *objTemplate = FindObjectEventTemplateByLocalId(localId, gSaveBlock1Ptr->objectEventTemplates, gMapHeader.events->objectEventCount); + *x = objTemplate->x; + *y = objTemplate->y; + return; + } + + objectId = GetObjectEventIdByLocalId(localId); + objEvent = &gObjectEvents[objectId]; + *x = objEvent->currentCoords.x - 7; + *y = objEvent->currentCoords.y - 7; } -// special to check if there is any object at a given position -u16 CheckObjectAtXY(void) +bool32 CheckObjectAtXY(void) { - u16 x = gSpecialVar_0x8005 + 7; - u16 y = gSpecialVar_0x8006 + 7; - u32 i; + u32 x = gSpecialVar_0x8005 + 7; + u32 y = gSpecialVar_0x8006 + 7; + u32 i; - for (i = 0; i < OBJECT_EVENTS_COUNT; i++) - { - if (gObjectEvents[i].active && gObjectEvents[i].currentCoords.x == x && gObjectEvents[i].currentCoords.y == y) - return TRUE; - } - return FALSE; + for (i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + if (!gObjectEvents[i].active) + return FALSE; + + if (gObjectEvents[i].currentCoords.x != x) + return FALSE; + + if (gObjectEvents[i].currentCoords.y != y) + return FALSE; + } + return TRUE; } bool8 GetSeenMon(void) From abb17ea88b19579b66804408801cc433462bfc39 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sun, 11 Aug 2024 21:01:31 -0700 Subject: [PATCH 10/65] Improved dex flags functions --- asm/macros/event.inc | 20 ++++++++++++-------- data/specials.inc | 5 +---- src/field_specials.c | 22 ++++++---------------- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 9375b7be9a..c4bbda645f 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2348,27 +2348,31 @@ @ Checks the state of the Pokédex Seen flag of the specified Pokemon @ The result is stored in VAR_RESULT .macro getseenmon species:req - setvar VAR_TEMP_1, \species - specialvar VAR_RESULT, GetSeenMon + setvar VAR_0x8005, \species + setvar VAR_0x8006, 0 + specialvar VAR_RESULT, Script_GetSetPokedexFlag .endm @ Checks the state of the Pokédex Caught flag of the specified Pokemon @ The result is stored in VAR_RESULT .macro getcaughtmon species:req - setvar VAR_TEMP_1, \species - specialvar VAR_RESULT, GetCaughtMon + setvar VAR_0x8005, \species + setvar VAR_0x8006, 1 + specialvar VAR_RESULT, Script_GetSetPokedexFlag .endm @ Sets the Pokédex Seen flag of the specified Pokemon .macro setseenmon species:req - setvar VAR_TEMP_1, \species - special SetSeenMon + setvar VAR_0x8005, \species + setvar VAR_0x8006, 2 + specialvar VAR_RESULT, Script_GetSetPokedexFlag .endm @ Sets the Pokédex Caught flag of the specified Pokemon .macro setcaughtmon species:req - setvar VAR_TEMP_1, \species - special SetCaughtMon + setvar VAR_0x8005, \species + setvar VAR_0x8006, 3 + specialvar VAR_RESULT, Script_GetSetPokedexFlag .endm @ Changes the caught ball of a selected Pokémon diff --git a/data/specials.inc b/data/specials.inc index 5b0aa23ddd..7b391cc968 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -556,10 +556,7 @@ gSpecials:: def_special Script_GetChosenMonDefensiveIVs def_special GetObjectPosition def_special CheckObjectAtXY - def_special GetSeenMon - def_special GetCaughtMon - def_special SetSeenMon - def_special SetCaughtMon + def_special Script_GetSetPokedexFlag def_special SetMonBall def_special CheckPartyForMon def_special Script_GetObjectFacingDirection diff --git a/src/field_specials.c b/src/field_specials.c index 98a909d725..a161ba3c44 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4312,25 +4312,15 @@ bool32 CheckObjectAtXY(void) return TRUE; } -bool8 GetSeenMon(void) +bool32 Script_GetSetPokedexFlag(void) { - return GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_GET_SEEN); -} + u32 speciesId = SpeciesToNationalPokedexNum(gSpecialVar_0x8005); + bool32 desiredFlag = gSpecialVar_0x8006; -bool8 GetCaughtMon(void) -{ - return GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_GET_CAUGHT); -} + if (desiredFlag == FLAG_SET_CAUGHT) + GetSetPokedexFlag(speciesId,FLAG_SET_SEEN); -void SetSeenMon(void) -{ - GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_SET_SEEN); -} - -void SetCaughtMon(void) -{ - GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_SET_SEEN); - GetSetPokedexFlag(SpeciesToNationalPokedexNum(VarGet(VAR_TEMP_1)), FLAG_SET_CAUGHT); + return GetSetPokedexFlag(speciesId,desiredFlag); } void SetMonBall(void) From f0d5b68f70aabdc41af0f56f4fc6e29b25b37596 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sun, 11 Aug 2024 22:10:04 -0700 Subject: [PATCH 11/65] Improved checkspecies functions --- asm/macros/event.inc | 22 ++++++++++++++-------- data/specials.inc | 3 ++- src/field_specials.c | 19 ++++++++++++------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index c4bbda645f..399ad6104f 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2379,7 +2379,7 @@ .macro setmonball ballId:req special ChoosePartyMon waitstate - setvar VAR_TEMP_1, \ballId + setvar VAR_0x8005, \ballId special SetMonBall .endm @@ -2387,19 +2387,25 @@ NO_PARTY_SCREEN = TRUE @ Check if the Player has \speciesId in their party. - .macro checkforspecies speciesId:req, silent:req, script:req - .if \silent == OPEN_PARTY_SCREEN + .macro checkspecies speciesId:req, mode:req + setvar VAR_0x8005, \speciesId + .if \mode == OPEN_PARTY_SCREEN special ChoosePartyMon waitstate - specialvar VAR_RESULT, ScriptGetPartyMonSpecies - goto_if_eq VAR_RESULT, \speciesId, \script + specialvar VAR_RESULT, CheckChosenMonMatchDesiredSpecie .else - setvar VAR_TEMP_1, \speciesId - specialvar VAR_RESULT, CheckPartyForMon - goto_if_eq VAR_RESULT, TRUE, \script + specialvar VAR_RESULT, CheckPartyHasSpecie .endif .endm + .macro checkspecies_choose speciesId:req + checkspecies \speciesId, OPEN_PARTY_SCREEN + .endm + + .macro checkspecies_auto speciesId:req + checkspecies \speciesId, NO_PARTY_SCREEN + .endm + @ Gets the facing direction of a given event object and stores it in the variable \dest. .macro getobjectfacingdirection evObjId:req, dest:req setvar VAR_TEMP_1, \evObjId diff --git a/data/specials.inc b/data/specials.inc index 7b391cc968..ed88d42eae 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -558,5 +558,6 @@ gSpecials:: def_special CheckObjectAtXY def_special Script_GetSetPokedexFlag def_special SetMonBall - def_special CheckPartyForMon + def_special CheckPartyHasSpecie + def_special CheckChosenMonMatchDesiredSpecie def_special Script_GetObjectFacingDirection diff --git a/src/field_specials.c b/src/field_specials.c index a161ba3c44..e58d861f54 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4325,18 +4325,23 @@ bool32 Script_GetSetPokedexFlag(void) void SetMonBall(void) { - u16 ballId = VarGet(VAR_TEMP_1); + u32 ballId = gSpecialVar_0x8005; SetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_POKEBALL, &ballId); } -bool8 CheckPartyForMon(void) +bool32 CheckPartyHasSpecie(void) { - int i; - for (i = 0; i < CalculatePlayerPartyCount(); i++) - { - if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) == VarGet(VAR_TEMP_1)) + u32 partyIndex; + + for (partyIndex = 0; partyIndex < CalculatePlayerPartyCount(); partyIndex++) + if (GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPECIES) == gSpecialVar_0x8005) return TRUE; - } + return FALSE; } +bool32 CheckChosenMonMatchDesiredSpecie(void) +{ + return (GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPECIES) == gSpecialVar_0x8005); +} + From 881e54a52b965c87359774f80a516358f2001ecc Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Mon, 12 Aug 2024 06:51:09 -0700 Subject: [PATCH 12/65] Incremental improvement of getobjectfacing --- asm/macros/event.inc | 3 ++- src/event_object_movement.c | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 399ad6104f..9321f5b8c5 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2408,6 +2408,7 @@ @ Gets the facing direction of a given event object and stores it in the variable \dest. .macro getobjectfacingdirection evObjId:req, dest:req - setvar VAR_TEMP_1, \evObjId + setvar VAR_0x8005, \evObjId specialvar \dest, Script_GetObjectFacingDirection .endm + diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 7facbab5c9..4105aa42a5 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -10633,9 +10633,8 @@ void GetDaycareGraphics(struct ScriptContext *ctx) gSpecialVar_Result = i; } -u8 Script_GetObjectFacingDirection(void) +u32 Script_GetObjectFacingDirection(void) { - u8 objId = GetObjectEventIdByLocalId(VarGet(VAR_TEMP_1)); - return gObjectEvents[objId].facingDirection; + return gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_0x8005)].facingDirection; } From bdeedaa9499e8866251242b48d5959ac7ce72c76 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Wed, 14 Aug 2024 19:05:30 -0700 Subject: [PATCH 13/65] Renamed evObjId to localId --- asm/macros/event.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 9321f5b8c5..7407774578 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2407,8 +2407,8 @@ .endm @ Gets the facing direction of a given event object and stores it in the variable \dest. - .macro getobjectfacingdirection evObjId:req, dest:req - setvar VAR_0x8005, \evObjId + .macro getobjectfacingdirection localId:req, dest:req + setvar VAR_0x8005, \localId specialvar \dest, Script_GetObjectFacingDirection .endm From 458340e0439104da1310d675255837f68cb75896 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Wed, 14 Aug 2024 19:09:56 -0700 Subject: [PATCH 14/65] Moved pokedex special constants to header --- asm/macros/event.inc | 5 +---- include/constants/field_specials.h | 5 +++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 7407774578..1c5b004bbb 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2383,10 +2383,7 @@ special SetMonBall .endm - OPEN_PARTY_SCREEN = FALSE - NO_PARTY_SCREEN = TRUE - - @ Check if the Player has \speciesId in their party. + @ Check if the Player has \speciesId in their party. OPEN_PARTY_SCREEN will have the player select a mon from their party. NO_PARTY_SCREEN will automatically check every mon in the player's party. .macro checkspecies speciesId:req, mode:req setvar VAR_0x8005, \speciesId .if \mode == OPEN_PARTY_SCREEN diff --git a/include/constants/field_specials.h b/include/constants/field_specials.h index 1e08a47f95..35769ba3b1 100644 --- a/include/constants/field_specials.h +++ b/include/constants/field_specials.h @@ -86,4 +86,9 @@ #define DEOXYS_ROCK_SOLVED 2 #define DEOXYS_ROCK_COMPLETE 3 +enum { + OPEN_PARTY_SCREEN, + NO_PARTY_SCREEN +}; + #endif // GUARD_CONSTANTS_FIELD_SPECIALS_H From dc06321054d6f1fca891d26a2cb736241ac9caaf Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Wed, 14 Aug 2024 19:24:55 -0700 Subject: [PATCH 15/65] Moved pokedex flags to constants files andadded constants to header of event_scripts --- asm/macros/event.inc | 8 ++++---- data/event_scripts.s | 1 + include/constants/pokedex.h | 14 ++++++++++++++ include/pokedex.h | 14 -------------- src/field_specials.c | 2 +- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 1c5b004bbb..ac008119ef 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2349,7 +2349,7 @@ @ The result is stored in VAR_RESULT .macro getseenmon species:req setvar VAR_0x8005, \species - setvar VAR_0x8006, 0 + setvar VAR_0x8006, FLAG_GET_SEEN specialvar VAR_RESULT, Script_GetSetPokedexFlag .endm @@ -2357,21 +2357,21 @@ @ The result is stored in VAR_RESULT .macro getcaughtmon species:req setvar VAR_0x8005, \species - setvar VAR_0x8006, 1 + setvar VAR_0x8006, FLAG_GET_CAUGHT specialvar VAR_RESULT, Script_GetSetPokedexFlag .endm @ Sets the Pokédex Seen flag of the specified Pokemon .macro setseenmon species:req setvar VAR_0x8005, \species - setvar VAR_0x8006, 2 + setvar VAR_0x8006, FLAG_SET_SEEN specialvar VAR_RESULT, Script_GetSetPokedexFlag .endm @ Sets the Pokédex Caught flag of the specified Pokemon .macro setcaughtmon species:req setvar VAR_0x8005, \species - setvar VAR_0x8006, 3 + setvar VAR_0x8006, FLAG_SET_CAUGHT specialvar VAR_RESULT, Script_GetSetPokedexFlag .endm diff --git a/data/event_scripts.s b/data/event_scripts.s index d4393ac9d4..8ff14199f6 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -42,6 +42,7 @@ #include "constants/metatile_labels.h" #include "constants/moves.h" #include "constants/party_menu.h" +#include "constants/pokedex.h" #include "constants/pokemon.h" #include "constants/roulette.h" #include "constants/script_menu.h" diff --git a/include/constants/pokedex.h b/include/constants/pokedex.h index 8058f01e5c..241198e178 100644 --- a/include/constants/pokedex.h +++ b/include/constants/pokedex.h @@ -1327,4 +1327,18 @@ enum { #define DEX_HGSS_Y_BOTTOM_PADDING 4 #define DEX_HGSS_MEASUREMENT_X_PADDING 51 +enum +{ + DEX_MODE_HOENN, + DEX_MODE_NATIONAL +}; + +enum +{ + FLAG_GET_SEEN, + FLAG_GET_CAUGHT, + FLAG_SET_SEEN, + FLAG_SET_CAUGHT +}; + #endif // GUARD_CONSTANTS_POKEDEX_H diff --git a/include/pokedex.h b/include/pokedex.h index be861fe682..0bd91449c3 100644 --- a/include/pokedex.h +++ b/include/pokedex.h @@ -4,20 +4,6 @@ extern u8 gUnusedPokedexU8; extern void (*gPokedexVBlankCB)(void); -enum -{ - DEX_MODE_HOENN, - DEX_MODE_NATIONAL -}; - -enum -{ - FLAG_GET_SEEN, - FLAG_GET_CAUGHT, - FLAG_SET_SEEN, - FLAG_SET_CAUGHT -}; - void ResetPokedex(void); u16 GetNationalPokedexCount(u8); u16 GetHoennPokedexCount(u8); diff --git a/src/field_specials.c b/src/field_specials.c index e58d861f54..a99a21f6b7 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -30,6 +30,7 @@ #include "overworld.h" #include "party_menu.h" #include "pokeblock.h" +#include "pokedex.h" #include "pokemon.h" #include "pokemon_storage_system.h" #include "random.h" @@ -68,7 +69,6 @@ #include "constants/metatile_labels.h" #include "palette.h" #include "battle_util.h" -#include "pokedex.h" #define TAG_ITEM_ICON 5500 From 67f8a61eef101d197c1f95524f4f8682a7258d0c Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Wed, 14 Aug 2024 19:44:18 -0700 Subject: [PATCH 16/65] Moved current and template positions to headers --- asm/macros/event.inc | 17 +++++++++++++---- include/constants/field_specials.h | 5 +++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index ac008119ef..7b4bca1170 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2328,16 +2328,25 @@ .2byte \itemId .endm - CURRENT_POSITION = FALSE - TEMPLATE_POSITION = TRUE - - @ Stores the CURRENT / TEMPLATE position of the given object in VAR_0x8007 (x) and VAR_0x8008 (y) + @ Stores the position of the given object in VAR_0x8007 (x) and VAR_0x8008 (y). Mode CURRENT_POSITION will take the object's current position. Mode TEMPLATE_POSITION will takje the object's template position. .macro getobjectxy localId:req, posType:req setvar VAR_0x8000, \localId setvar VAR_0x8001, \posType special GetObjectPosition .endm + .macro getobjecttemplatexy localId:req + setvar VAR_0x8000, \localId + setvar VAR_0x8001, TEMPLATE_POSITION + special GetObjectPosition + .endm + + .macro getobjectcurrentxy localId:req + setvar VAR_0x8000, \localId + setvar VAR_0x8001, CURRENT_POSITION + special GetObjectPosition + .endm + @ checks if there is any object at a given position .macro checkobjectat x:req, y:req setorcopyvar VAR_0x8005, \x diff --git a/include/constants/field_specials.h b/include/constants/field_specials.h index 35769ba3b1..64a105a5fd 100644 --- a/include/constants/field_specials.h +++ b/include/constants/field_specials.h @@ -91,4 +91,9 @@ enum { NO_PARTY_SCREEN }; +enum { + CURRENT_POSITION, + TEMPLATE_POSITION +}; + #endif // GUARD_CONSTANTS_FIELD_SPECIALS_H From fed7fc4f0a6b871adef04c18411633e44a6ae1b0 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Wed, 14 Aug 2024 21:35:03 -0700 Subject: [PATCH 17/65] Fixed bug with checkobjectat --- src/field_specials.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/field_specials.c b/src/field_specials.c index a99a21f6b7..38e4fa4fde 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4301,15 +4301,16 @@ bool32 CheckObjectAtXY(void) for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { if (!gObjectEvents[i].active) - return FALSE; + continue; if (gObjectEvents[i].currentCoords.x != x) - return FALSE; + continue; if (gObjectEvents[i].currentCoords.y != y) - return FALSE; + continue; + return TRUE; } - return TRUE; + return FALSE; } bool32 Script_GetSetPokedexFlag(void) From 287f6baaa965ae9ec4ba66bdf162ba8a731d2014 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Wed, 14 Aug 2024 22:14:48 -0700 Subject: [PATCH 18/65] spaces over tabs please don't eat my ass --- include/constants/field_specials.h | 8 +-- src/event_object_movement.c | 44 ++++++++--------- src/field_specials.c | 78 +++++++++++++++--------------- 3 files changed, 65 insertions(+), 65 deletions(-) diff --git a/include/constants/field_specials.h b/include/constants/field_specials.h index 64a105a5fd..72966adeb8 100644 --- a/include/constants/field_specials.h +++ b/include/constants/field_specials.h @@ -87,13 +87,13 @@ #define DEOXYS_ROCK_COMPLETE 3 enum { - OPEN_PARTY_SCREEN, - NO_PARTY_SCREEN + OPEN_PARTY_SCREEN, + NO_PARTY_SCREEN }; enum { - CURRENT_POSITION, - TEMPLATE_POSITION + CURRENT_POSITION, + TEMPLATE_POSITION }; #endif // GUARD_CONSTANTS_FIELD_SPECIALS_H diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 4105aa42a5..8c069a895b 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -9494,30 +9494,30 @@ static void DoTracksGroundEffect_BikeTireTracks(struct ObjectEvent *objEvent, st static void DoTracksGroundEffect_SlitherTracks(struct ObjectEvent *objEvent, struct Sprite *sprite, u8 a) { - // Specifies which bike track shape to show next. - // For example, when the bike turns from up to right, it will show - // a track that curves to the right. - // Each 4-byte row corresponds to the initial direction of the bike, and - // each byte in that row is for the next direction of the bike in the order - // of down, up, left, right. - static const u8 slitherTracks_Transitions[4][4] = { - {1, 2, 7, 8}, - {1, 2, 6, 5}, - {5, 8, 3, 4}, - {6, 7, 3, 4}, - }; + // Specifies which bike track shape to show next. + // For example, when the bike turns from up to right, it will show + // a track that curves to the right. + // Each 4-byte row corresponds to the initial direction of the bike, and + // each byte in that row is for the next direction of the bike in the order + // of down, up, left, right. + static const u8 slitherTracks_Transitions[4][4] = { + {1, 2, 7, 8}, + {1, 2, 6, 5}, + {5, 8, 3, 4}, + {6, 7, 3, 4}, + }; - if (objEvent->currentCoords.x != objEvent->previousCoords.x || objEvent->currentCoords.y != objEvent->previousCoords.y) - { - gFieldEffectArguments[0] = objEvent->previousCoords.x; - gFieldEffectArguments[1] = objEvent->previousCoords.y; - gFieldEffectArguments[2] = 149; - gFieldEffectArguments[3] = 2; - gFieldEffectArguments[4] = - slitherTracks_Transitions[objEvent->previousMovementDirection][objEvent->facingDirection - 5]; + if (objEvent->currentCoords.x != objEvent->previousCoords.x || objEvent->currentCoords.y != objEvent->previousCoords.y) + { + gFieldEffectArguments[0] = objEvent->previousCoords.x; + gFieldEffectArguments[1] = objEvent->previousCoords.y; + gFieldEffectArguments[2] = 149; + gFieldEffectArguments[3] = 2; + gFieldEffectArguments[4] = + slitherTracks_Transitions[objEvent->previousMovementDirection][objEvent->facingDirection - 5]; gFieldEffectArguments[5] = objEvent->previousMetatileBehavior; - FieldEffectStart(FLDEFF_TRACKS_SLITHER); - } + FieldEffectStart(FLDEFF_TRACKS_SLITHER); + } } void GroundEffect_Ripple(struct ObjectEvent *objEvent, struct Sprite *sprite) diff --git a/src/field_specials.c b/src/field_specials.c index 38e4fa4fde..d0d370c566 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4270,58 +4270,58 @@ void PreparePartyForSkyBattle(void) void GetObjectPosition(void) { - u32 localId = gSpecialVar_0x8000; - u32 useTemplate = gSpecialVar_0x8001; - u32 objectId; - struct ObjectEvent* objEvent; + u32 localId = gSpecialVar_0x8000; + u32 useTemplate = gSpecialVar_0x8001; + u32 objectId; + struct ObjectEvent* objEvent; - u16 *x = &gSpecialVar_0x8007; - u16 *y = &gSpecialVar_0x8008; + u16 *x = &gSpecialVar_0x8007; + u16 *y = &gSpecialVar_0x8008; - if (useTemplate) - { - const struct ObjectEventTemplate *objTemplate = FindObjectEventTemplateByLocalId(localId, gSaveBlock1Ptr->objectEventTemplates, gMapHeader.events->objectEventCount); - *x = objTemplate->x; - *y = objTemplate->y; - return; - } + if (useTemplate) + { + const struct ObjectEventTemplate *objTemplate = FindObjectEventTemplateByLocalId(localId, gSaveBlock1Ptr->objectEventTemplates, gMapHeader.events->objectEventCount); + *x = objTemplate->x; + *y = objTemplate->y; + return; + } - objectId = GetObjectEventIdByLocalId(localId); - objEvent = &gObjectEvents[objectId]; - *x = objEvent->currentCoords.x - 7; - *y = objEvent->currentCoords.y - 7; + objectId = GetObjectEventIdByLocalId(localId); + objEvent = &gObjectEvents[objectId]; + *x = objEvent->currentCoords.x - 7; + *y = objEvent->currentCoords.y - 7; } bool32 CheckObjectAtXY(void) { - u32 x = gSpecialVar_0x8005 + 7; - u32 y = gSpecialVar_0x8006 + 7; - u32 i; + u32 x = gSpecialVar_0x8005 + 7; + u32 y = gSpecialVar_0x8006 + 7; + u32 i; - for (i = 0; i < OBJECT_EVENTS_COUNT; i++) - { - if (!gObjectEvents[i].active) - continue; + for (i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + if (!gObjectEvents[i].active) + continue; - if (gObjectEvents[i].currentCoords.x != x) - continue; + if (gObjectEvents[i].currentCoords.x != x) + continue; - if (gObjectEvents[i].currentCoords.y != y) - continue; - return TRUE; - } - return FALSE; + if (gObjectEvents[i].currentCoords.y != y) + continue; + return TRUE; + } + return FALSE; } bool32 Script_GetSetPokedexFlag(void) { - u32 speciesId = SpeciesToNationalPokedexNum(gSpecialVar_0x8005); - bool32 desiredFlag = gSpecialVar_0x8006; + u32 speciesId = SpeciesToNationalPokedexNum(gSpecialVar_0x8005); + bool32 desiredFlag = gSpecialVar_0x8006; - if (desiredFlag == FLAG_SET_CAUGHT) - GetSetPokedexFlag(speciesId,FLAG_SET_SEEN); + if (desiredFlag == FLAG_SET_CAUGHT) + GetSetPokedexFlag(speciesId,FLAG_SET_SEEN); - return GetSetPokedexFlag(speciesId,desiredFlag); + return GetSetPokedexFlag(speciesId,desiredFlag); } void SetMonBall(void) @@ -4332,9 +4332,9 @@ void SetMonBall(void) bool32 CheckPartyHasSpecie(void) { - u32 partyIndex; + u32 partyIndex; - for (partyIndex = 0; partyIndex < CalculatePlayerPartyCount(); partyIndex++) + for (partyIndex = 0; partyIndex < CalculatePlayerPartyCount(); partyIndex++) if (GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPECIES) == gSpecialVar_0x8005) return TRUE; @@ -4343,6 +4343,6 @@ bool32 CheckPartyHasSpecie(void) bool32 CheckChosenMonMatchDesiredSpecie(void) { - return (GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPECIES) == gSpecialVar_0x8005); + return (GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPECIES) == gSpecialVar_0x8005); } From f87440a25a518b1eb54df0b46b7350aa5832416f Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Thu, 12 Sep 2024 05:43:55 -0700 Subject: [PATCH 19/65] Removed setmonball per https://github.com/rh-hideout/pokeemerald-expansion/pull/5177\#issuecomment-2294980531 --- .swo | Bin 0 -> 4096 bytes asm/macros/event.inc | 8 -------- data/specials.inc | 1 - src/field_specials.c | 6 ------ 4 files changed, 15 deletions(-) create mode 100644 .swo diff --git a/.swo b/.swo new file mode 100644 index 0000000000000000000000000000000000000000..dac352690102655cc3a6e5f5a7fee17b83ce8ed2 GIT binary patch literal 4096 zcmYc?2=nw+u+%eP00IFJ0RcOh7#IqQ(_mb@w2N!7cZh$0uBAm-n5(H7UbUl=qaiRF z0s|ESoT0`>hTyEOtfZ(QEEGCW4jHv~Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU;sh@ E055zBfdBvi literal 0 HcmV?d00001 diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 986ed8305f..a4057b22db 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2399,14 +2399,6 @@ setvar VAR_0x8005, \species setvar VAR_0x8006, FLAG_SET_CAUGHT specialvar VAR_RESULT, Script_GetSetPokedexFlag - .endm - - @ Changes the caught ball of a selected Pokémon - .macro setmonball ballId:req - special ChoosePartyMon - waitstate - setvar VAR_0x8005, \ballId - special SetMonBall .endm @ Check if the Player has \speciesId in their party. OPEN_PARTY_SCREEN will have the player select a mon from their party. NO_PARTY_SCREEN will automatically check every mon in the player's party. diff --git a/data/specials.inc b/data/specials.inc index ed88d42eae..54a5218c37 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -557,7 +557,6 @@ gSpecials:: def_special GetObjectPosition def_special CheckObjectAtXY def_special Script_GetSetPokedexFlag - def_special SetMonBall def_special CheckPartyHasSpecie def_special CheckChosenMonMatchDesiredSpecie def_special Script_GetObjectFacingDirection diff --git a/src/field_specials.c b/src/field_specials.c index 4081e83757..d26f9219cf 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4334,12 +4334,6 @@ bool32 Script_GetSetPokedexFlag(void) return GetSetPokedexFlag(speciesId,desiredFlag); } -void SetMonBall(void) -{ - u32 ballId = gSpecialVar_0x8005; - SetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_POKEBALL, &ballId); -} - bool32 CheckPartyHasSpecie(void) { u32 partyIndex; From 979c9994f76c0823b3d221e4c94d67c87a07ee74 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Thu, 12 Sep 2024 05:47:12 -0700 Subject: [PATCH 20/65] Set VAR_RESULT to the number of removed items from removeallitem per https://github.com/rh-hideout/pokeemerald-expansion/pull/5177\#issuecomment-2294980531 --- .swo | Bin 4096 -> 0 bytes asm/macros/event.inc | 2 +- src/scrcmd.c | 4 +++- 3 files changed, 4 insertions(+), 2 deletions(-) delete mode 100644 .swo diff --git a/.swo b/.swo deleted file mode 100644 index dac352690102655cc3a6e5f5a7fee17b83ce8ed2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmYc?2=nw+u+%eP00IFJ0RcOh7#IqQ(_mb@w2N!7cZh$0uBAm-n5(H7UbUl=qaiRF z0s|ESoT0`>hTyEOtfZ(QEEGCW4jHv~Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU;sh@ E055zBfdBvi diff --git a/asm/macros/event.inc b/asm/macros/event.inc index a4057b22db..dff3faf83a 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2339,7 +2339,7 @@ callnative ScriptHideItemDescription .endm - @ Remove all of specified item from the player's bag + @ Remove all of specified item from the player's bag and return the number of removed items to VAR_RESULT .macro removeallitem itemId:req callnative RemoveAllItem .2byte \itemId diff --git a/src/scrcmd.c b/src/scrcmd.c index 12ec041895..7062721a94 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -2477,5 +2477,7 @@ void ScriptSetDoubleBattleFlag(struct ScriptContext *ctx) void RemoveAllItem(struct ScriptContext *ctx) { u32 itemId = VarGet(ScriptReadHalfword(ctx)); - RemoveBagItem(itemId, CountTotalItemQuantityInBag(itemId)); + u32 count = CountTotalItemQuantityInBag(itemId); + gSpecialVar_Result = count; + RemoveBagItem(itemId, count); } From 22b0fdf5c3cd5cb68f4f23a75b4d917e391e77ac Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Thu, 12 Sep 2024 18:18:41 -0700 Subject: [PATCH 21/65] Removed checkspecies_auto and made that the default behavior --- asm/macros/event.inc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index dff3faf83a..f3fa3a4027 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2402,7 +2402,7 @@ .endm @ Check if the Player has \speciesId in their party. OPEN_PARTY_SCREEN will have the player select a mon from their party. NO_PARTY_SCREEN will automatically check every mon in the player's party. - .macro checkspecies speciesId:req, mode:req + .macro checkspecies speciesId:req, mode=NO_PARTY_SCREEN setvar VAR_0x8005, \speciesId .if \mode == OPEN_PARTY_SCREEN special ChoosePartyMon @@ -2417,10 +2417,6 @@ checkspecies \speciesId, OPEN_PARTY_SCREEN .endm - .macro checkspecies_auto speciesId:req - checkspecies \speciesId, NO_PARTY_SCREEN - .endm - @ Gets the facing direction of a given event object and stores it in the variable \dest. .macro getobjectfacingdirection localId:req, dest:req setvar VAR_0x8005, \localId From 10bb349b8cc2b46ffd22cd9e36ce0c18a032d281 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Thu, 12 Sep 2024 18:41:42 -0700 Subject: [PATCH 22/65] Changed GetObjectPosition to callnative --- asm/macros/event.inc | 22 +++++++++++----------- data/specials.inc | 1 - src/field_specials.c | 24 ------------------------ src/scrcmd.c | 25 +++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index f3fa3a4027..b2a2d46159 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2347,21 +2347,21 @@ @ Stores the position of the given object in VAR_0x8007 (x) and VAR_0x8008 (y). Mode CURRENT_POSITION will take the object's current position. Mode TEMPLATE_POSITION will takje the object's template position. .macro getobjectxy localId:req, posType:req - setvar VAR_0x8000, \localId - setvar VAR_0x8001, \posType - special GetObjectPosition + callnative GetObjectPosition + .2byte \localId + .2byte \posType .endm - .macro getobjecttemplatexy localId:req - setvar VAR_0x8000, \localId - setvar VAR_0x8001, TEMPLATE_POSITION - special GetObjectPosition + .macro getobjecttemplatexy localId:req, posType = TEMPLATE_POSITION + callnative GetObjectPosition + .2byte \localId + .2byte \posType .endm - .macro getobjectcurrentxy localId:req - setvar VAR_0x8000, \localId - setvar VAR_0x8001, CURRENT_POSITION - special GetObjectPosition + .macro getobjectcurrentxy localId:req, posType = CURRENT_POSITION + callnative GetObjectPosition + .2byte \localId + .2byte \posType .endm @ checks if there is any object at a given position diff --git a/data/specials.inc b/data/specials.inc index 54a5218c37..3232dca7af 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -554,7 +554,6 @@ gSpecials:: def_special Script_GetChosenMonDefensiveEVs def_special Script_GetChosenMonOffensiveIVs def_special Script_GetChosenMonDefensiveIVs - def_special GetObjectPosition def_special CheckObjectAtXY def_special Script_GetSetPokedexFlag def_special CheckPartyHasSpecie diff --git a/src/field_specials.c b/src/field_specials.c index d26f9219cf..3c27ade570 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4278,30 +4278,6 @@ void PreparePartyForSkyBattle(void) CompactPartySlots(); } -void GetObjectPosition(void) -{ - u32 localId = gSpecialVar_0x8000; - u32 useTemplate = gSpecialVar_0x8001; - u32 objectId; - struct ObjectEvent* objEvent; - - u16 *x = &gSpecialVar_0x8007; - u16 *y = &gSpecialVar_0x8008; - - if (useTemplate) - { - const struct ObjectEventTemplate *objTemplate = FindObjectEventTemplateByLocalId(localId, gSaveBlock1Ptr->objectEventTemplates, gMapHeader.events->objectEventCount); - *x = objTemplate->x; - *y = objTemplate->y; - return; - } - - objectId = GetObjectEventIdByLocalId(localId); - objEvent = &gObjectEvents[objectId]; - *x = objEvent->currentCoords.x - 7; - *y = objEvent->currentCoords.y - 7; -} - bool32 CheckObjectAtXY(void) { u32 x = gSpecialVar_0x8005 + 7; diff --git a/src/scrcmd.c b/src/scrcmd.c index 7062721a94..7bcedb89bb 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -2481,3 +2481,28 @@ void RemoveAllItem(struct ScriptContext *ctx) gSpecialVar_Result = count; RemoveBagItem(itemId, count); } + +void GetObjectPosition(struct ScriptContext *ctx) +{ + u32 localId = VarGet(ScriptReadHalfword(ctx)); + u32 useTemplate = gSpecialVar_0x8001; + u32 objectId; + struct ObjectEvent* objEvent; + + u16 *x = &gSpecialVar_0x8007; + u16 *y = &gSpecialVar_0x8008; + + if (useTemplate) + { + const struct ObjectEventTemplate *objTemplate = FindObjectEventTemplateByLocalId(localId, gSaveBlock1Ptr->objectEventTemplates, gMapHeader.events->objectEventCount); + *x = objTemplate->x; + *y = objTemplate->y; + return; + } + + objectId = GetObjectEventIdByLocalId(localId); + objEvent = &gObjectEvents[objectId]; + *x = objEvent->currentCoords.x - 7; + *y = objEvent->currentCoords.y - 7; +} + From be7938f32221247547b4da30eb396fc999e1a69b Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Thu, 12 Sep 2024 18:55:11 -0700 Subject: [PATCH 23/65] Changed CheckObjectAtXY to callnative --- asm/macros/event.inc | 9 +++++---- data/specials.inc | 1 - src/field_specials.c | 21 --------------------- src/scrcmd.c | 27 ++++++++++++++++++++++++--- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index b2a2d46159..a4f22f817a 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2365,10 +2365,11 @@ .endm @ checks if there is any object at a given position - .macro checkobjectat x:req, y:req - setorcopyvar VAR_0x8005, \x - setorcopyvar VAR_0x8006, \y - specialvar VAR_RESULT, CheckObjectAtXY + .macro checkobjectat x:req, y:req, dest = VAR_RESULT + callnative CheckObjectAtXY + .2byte \x + .2byte \y + .2byte \dest .endm @ Checks the state of the Pokédex Seen flag of the specified Pokemon diff --git a/data/specials.inc b/data/specials.inc index 3232dca7af..91fed72d78 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -554,7 +554,6 @@ gSpecials:: def_special Script_GetChosenMonDefensiveEVs def_special Script_GetChosenMonOffensiveIVs def_special Script_GetChosenMonDefensiveIVs - def_special CheckObjectAtXY def_special Script_GetSetPokedexFlag def_special CheckPartyHasSpecie def_special CheckChosenMonMatchDesiredSpecie diff --git a/src/field_specials.c b/src/field_specials.c index 3c27ade570..0f1854b3b5 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4278,27 +4278,6 @@ void PreparePartyForSkyBattle(void) CompactPartySlots(); } -bool32 CheckObjectAtXY(void) -{ - u32 x = gSpecialVar_0x8005 + 7; - u32 y = gSpecialVar_0x8006 + 7; - u32 i; - - for (i = 0; i < OBJECT_EVENTS_COUNT; i++) - { - if (!gObjectEvents[i].active) - continue; - - if (gObjectEvents[i].currentCoords.x != x) - continue; - - if (gObjectEvents[i].currentCoords.y != y) - continue; - return TRUE; - } - return FALSE; -} - bool32 Script_GetSetPokedexFlag(void) { u32 speciesId = SpeciesToNationalPokedexNum(gSpecialVar_0x8005); diff --git a/src/scrcmd.c b/src/scrcmd.c index 7bcedb89bb..d68e2dbd86 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -2485,12 +2485,12 @@ void RemoveAllItem(struct ScriptContext *ctx) void GetObjectPosition(struct ScriptContext *ctx) { u32 localId = VarGet(ScriptReadHalfword(ctx)); - u32 useTemplate = gSpecialVar_0x8001; + u32 useTemplate = VarGet(ScriptReadHalfword(ctx)); + u16 *x = &gSpecialVar_0x8007; + u16 *y = &gSpecialVar_0x8008; u32 objectId; struct ObjectEvent* objEvent; - u16 *x = &gSpecialVar_0x8007; - u16 *y = &gSpecialVar_0x8008; if (useTemplate) { @@ -2506,3 +2506,24 @@ void GetObjectPosition(struct ScriptContext *ctx) *y = objEvent->currentCoords.y - 7; } +bool32 CheckObjectAtXY(struct ScriptContext *ctx) +{ + u32 x = VarGet(ScriptReadHalfword(ctx)) + 7; + u32 y = VarGet(ScriptReadHalfword(ctx)) + 7; + u32 i; + + for (i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + if (!gObjectEvents[i].active) + continue; + + if (gObjectEvents[i].currentCoords.x != x) + continue; + + if (gObjectEvents[i].currentCoords.y != y) + continue; + return TRUE; + } + return FALSE; +} + From c2db77c4f603f4053c4b08606c32b6a03203dba6 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Thu, 12 Sep 2024 20:15:55 -0700 Subject: [PATCH 24/65] Changed Script_GetSetPokedexFlag to callnative --- asm/macros/event.inc | 24 ++++++++++++------------ data/specials.inc | 1 - src/field_specials.c | 11 ----------- src/scrcmd.c | 12 ++++++++++++ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index a4f22f817a..b826b4c556 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2375,31 +2375,31 @@ @ Checks the state of the Pokédex Seen flag of the specified Pokemon @ The result is stored in VAR_RESULT .macro getseenmon species:req - setvar VAR_0x8005, \species - setvar VAR_0x8006, FLAG_GET_SEEN - specialvar VAR_RESULT, Script_GetSetPokedexFlag + callnative Script_GetSetPokedexFlag + .2byte \species + .2byte FLAG_GET_SEEN .endm @ Checks the state of the Pokédex Caught flag of the specified Pokemon @ The result is stored in VAR_RESULT .macro getcaughtmon species:req - setvar VAR_0x8005, \species - setvar VAR_0x8006, FLAG_GET_CAUGHT - specialvar VAR_RESULT, Script_GetSetPokedexFlag + callnative Script_GetSetPokedexFlag + .2byte \species + .2byte FLAG_GET_CAUGHT .endm @ Sets the Pokédex Seen flag of the specified Pokemon .macro setseenmon species:req - setvar VAR_0x8005, \species - setvar VAR_0x8006, FLAG_SET_SEEN - specialvar VAR_RESULT, Script_GetSetPokedexFlag + callnative Script_GetSetPokedexFlag + .2byte \species + .2byte FLAG_SET_SEEN .endm @ Sets the Pokédex Caught flag of the specified Pokemon .macro setcaughtmon species:req - setvar VAR_0x8005, \species - setvar VAR_0x8006, FLAG_SET_CAUGHT - specialvar VAR_RESULT, Script_GetSetPokedexFlag + callnative Script_GetSetPokedexFlag + .2byte \species + .2byte FLAG_SET_CAUGHT .endm @ Check if the Player has \speciesId in their party. OPEN_PARTY_SCREEN will have the player select a mon from their party. NO_PARTY_SCREEN will automatically check every mon in the player's party. diff --git a/data/specials.inc b/data/specials.inc index 91fed72d78..e6a8860096 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -554,7 +554,6 @@ gSpecials:: def_special Script_GetChosenMonDefensiveEVs def_special Script_GetChosenMonOffensiveIVs def_special Script_GetChosenMonDefensiveIVs - def_special Script_GetSetPokedexFlag def_special CheckPartyHasSpecie def_special CheckChosenMonMatchDesiredSpecie def_special Script_GetObjectFacingDirection diff --git a/src/field_specials.c b/src/field_specials.c index 0f1854b3b5..57bd0158a6 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4278,17 +4278,6 @@ void PreparePartyForSkyBattle(void) CompactPartySlots(); } -bool32 Script_GetSetPokedexFlag(void) -{ - u32 speciesId = SpeciesToNationalPokedexNum(gSpecialVar_0x8005); - bool32 desiredFlag = gSpecialVar_0x8006; - - if (desiredFlag == FLAG_SET_CAUGHT) - GetSetPokedexFlag(speciesId,FLAG_SET_SEEN); - - return GetSetPokedexFlag(speciesId,desiredFlag); -} - bool32 CheckPartyHasSpecie(void) { u32 partyIndex; diff --git a/src/scrcmd.c b/src/scrcmd.c index d68e2dbd86..f087d2d290 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -53,6 +53,7 @@ #include "list_menu.h" #include "malloc.h" #include "constants/event_objects.h" +#include "pokedex.h" typedef u16 (*SpecialFunc)(void); typedef void (*NativeFunc)(struct ScriptContext *ctx); @@ -2527,3 +2528,14 @@ bool32 CheckObjectAtXY(struct ScriptContext *ctx) return FALSE; } +void Script_GetSetPokedexFlag(struct ScriptContext *ctx) +{ + u32 speciesId = SpeciesToNationalPokedexNum(VarGet(ScriptReadHalfword(ctx))); + bool32 desiredFlag = VarGet(ScriptReadHalfword(ctx)); + + if (desiredFlag == FLAG_SET_CAUGHT) + GetSetPokedexFlag(speciesId,FLAG_SET_SEEN); + + gSpecialVar_Result = GetSetPokedexFlag(speciesId,desiredFlag); +} + From ecd435cd84be9b986f60d00655b96b2b2c2d93bc Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Thu, 12 Sep 2024 20:45:14 -0700 Subject: [PATCH 25/65] Changed CheckChosenMOnMatchDesiredSpecies and CheckPartyHasSpecies to callnative --- asm/macros/event.inc | 7 ++++--- data/specials.inc | 2 -- src/field_specials.c | 16 ---------------- src/scrcmd.c | 21 +++++++++++++++++++++ 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index b826b4c556..67b5e9b634 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2404,13 +2404,14 @@ @ Check if the Player has \speciesId in their party. OPEN_PARTY_SCREEN will have the player select a mon from their party. NO_PARTY_SCREEN will automatically check every mon in the player's party. .macro checkspecies speciesId:req, mode=NO_PARTY_SCREEN - setvar VAR_0x8005, \speciesId .if \mode == OPEN_PARTY_SCREEN special ChoosePartyMon waitstate - specialvar VAR_RESULT, CheckChosenMonMatchDesiredSpecie + callnative CheckChosenMonMatchDesiredSpecie + .2byte \speciesId .else - specialvar VAR_RESULT, CheckPartyHasSpecie + callnative CheckPartyHasSpecie + .2byte \speciesId .endif .endm diff --git a/data/specials.inc b/data/specials.inc index e6a8860096..8233deda59 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -554,6 +554,4 @@ gSpecials:: def_special Script_GetChosenMonDefensiveEVs def_special Script_GetChosenMonOffensiveIVs def_special Script_GetChosenMonDefensiveIVs - def_special CheckPartyHasSpecie - def_special CheckChosenMonMatchDesiredSpecie def_special Script_GetObjectFacingDirection diff --git a/src/field_specials.c b/src/field_specials.c index 57bd0158a6..982d7a2924 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4278,19 +4278,3 @@ void PreparePartyForSkyBattle(void) CompactPartySlots(); } -bool32 CheckPartyHasSpecie(void) -{ - u32 partyIndex; - - for (partyIndex = 0; partyIndex < CalculatePlayerPartyCount(); partyIndex++) - if (GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPECIES) == gSpecialVar_0x8005) - return TRUE; - - return FALSE; -} - -bool32 CheckChosenMonMatchDesiredSpecie(void) -{ - return (GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPECIES) == gSpecialVar_0x8005); -} - diff --git a/src/scrcmd.c b/src/scrcmd.c index f087d2d290..345d3b8bef 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -2539,3 +2539,24 @@ void Script_GetSetPokedexFlag(struct ScriptContext *ctx) gSpecialVar_Result = GetSetPokedexFlag(speciesId,desiredFlag); } +void CheckPartyHasSpecie(struct ScriptContext *ctx) +{ + u32 partyIndex; + u32 givenSpecies = VarGet(ScriptReadHalfword(ctx)); + u32 hasSpecies = FALSE; + + for (partyIndex = 0; partyIndex < CalculatePlayerPartyCount(); partyIndex++) + if (GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPECIES) == givenSpecies) + hasSpecies = TRUE; + + gSpecialVar_Result = hasSpecies; +} + +void CheckChosenMonMatchDesiredSpecie(struct ScriptContext *ctx) +{ + u32 givenSpecies = VarGet(ScriptReadHalfword(ctx)); + + gSpecialVar_Result = (GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPECIES) == givenSpecies); +} + + From 510af1bee6746ddf99bafa732b61da7193ec23a2 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Thu, 12 Sep 2024 20:55:37 -0700 Subject: [PATCH 26/65] Restored original functions and made script versions --- asm/macros/event.inc | 20 +++++++-------- include/field_specials.h | 3 +++ src/field_specials.c | 52 +++++++++++++++++++++++++++++++++++++ src/scrcmd.c | 55 ++++++---------------------------------- 4 files changed, 73 insertions(+), 57 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 67b5e9b634..73d0d07526 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2347,26 +2347,26 @@ @ Stores the position of the given object in VAR_0x8007 (x) and VAR_0x8008 (y). Mode CURRENT_POSITION will take the object's current position. Mode TEMPLATE_POSITION will takje the object's template position. .macro getobjectxy localId:req, posType:req - callnative GetObjectPosition + callnative Script_GetObjectPosition .2byte \localId .2byte \posType .endm .macro getobjecttemplatexy localId:req, posType = TEMPLATE_POSITION - callnative GetObjectPosition + callnative Script_GetObjectPosition .2byte \localId .2byte \posType .endm .macro getobjectcurrentxy localId:req, posType = CURRENT_POSITION - callnative GetObjectPosition + callnative Script_GetObjectPosition .2byte \localId .2byte \posType .endm @ checks if there is any object at a given position .macro checkobjectat x:req, y:req, dest = VAR_RESULT - callnative CheckObjectAtXY + callnative Script_CheckObjectAtXY .2byte \x .2byte \y .2byte \dest @@ -2375,7 +2375,7 @@ @ Checks the state of the Pokédex Seen flag of the specified Pokemon @ The result is stored in VAR_RESULT .macro getseenmon species:req - callnative Script_GetSetPokedexFlag + callnative Script_Script_GetSetPokedexFlag .2byte \species .2byte FLAG_GET_SEEN .endm @@ -2383,21 +2383,21 @@ @ Checks the state of the Pokédex Caught flag of the specified Pokemon @ The result is stored in VAR_RESULT .macro getcaughtmon species:req - callnative Script_GetSetPokedexFlag + callnative Script_Script_GetSetPokedexFlag .2byte \species .2byte FLAG_GET_CAUGHT .endm @ Sets the Pokédex Seen flag of the specified Pokemon .macro setseenmon species:req - callnative Script_GetSetPokedexFlag + callnative Script_Script_GetSetPokedexFlag .2byte \species .2byte FLAG_SET_SEEN .endm @ Sets the Pokédex Caught flag of the specified Pokemon .macro setcaughtmon species:req - callnative Script_GetSetPokedexFlag + callnative Script_Script_GetSetPokedexFlag .2byte \species .2byte FLAG_SET_CAUGHT .endm @@ -2407,10 +2407,10 @@ .if \mode == OPEN_PARTY_SCREEN special ChoosePartyMon waitstate - callnative CheckChosenMonMatchDesiredSpecie + callnative Script_CheckChosenMonMatchDesiredSpecie .2byte \speciesId .else - callnative CheckPartyHasSpecie + callnative Script_CheckPartyHasSpecie .2byte \speciesId .endif .endm diff --git a/include/field_specials.h b/include/field_specials.h index 95a91d543f..85db5ba40f 100644 --- a/include/field_specials.h +++ b/include/field_specials.h @@ -33,5 +33,8 @@ void ResetFanClub(void); bool8 ShouldShowBoxWasFullMessage(void); void SetPCBoxToSendMon(u8 boxId); void PreparePartyForSkyBattle(void); +void GetObjectPosition(u32, u32); +bool32 CheckObjectAtXY(u32, u32); +bool32 CheckPartyHasSpecie(u32); #endif // GUARD_FIELD_SPECIALS_H diff --git a/src/field_specials.c b/src/field_specials.c index 982d7a2924..94a98812b1 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4278,3 +4278,55 @@ void PreparePartyForSkyBattle(void) CompactPartySlots(); } +void GetObjectPosition(u32 localId, u32 useTemplate) +{ + u32 objectId; + struct ObjectEvent* objEvent; + + u16 *x = &gSpecialVar_0x8007; + u16 *y = &gSpecialVar_0x8008; + + if (useTemplate) + { + const struct ObjectEventTemplate *objTemplate = FindObjectEventTemplateByLocalId(localId, gSaveBlock1Ptr->objectEventTemplates, gMapHeader.events->objectEventCount); + *x = objTemplate->x; + *y = objTemplate->y; + return; + } + + objectId = GetObjectEventIdByLocalId(localId); + objEvent = &gObjectEvents[objectId]; + *x = objEvent->currentCoords.x - 7; + *y = objEvent->currentCoords.y - 7; +} + +bool32 CheckObjectAtXY(u32 x, u32 y) +{ + u32 i; + + for (i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + if (!gObjectEvents[i].active) + continue; + + if (gObjectEvents[i].currentCoords.x != x) + continue; + + if (gObjectEvents[i].currentCoords.y != y) + continue; + return TRUE; + } + return FALSE; +} + +bool32 CheckPartyHasSpecie(u32 givenSpecies) +{ + u32 partyIndex; + + for (partyIndex = 0; partyIndex < CalculatePlayerPartyCount(); partyIndex++) + if (GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPECIES) == givenSpecies) + return TRUE; + + return FALSE; +} + diff --git a/src/scrcmd.c b/src/scrcmd.c index 345d3b8bef..974b223f26 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -2483,79 +2483,40 @@ void RemoveAllItem(struct ScriptContext *ctx) RemoveBagItem(itemId, count); } -void GetObjectPosition(struct ScriptContext *ctx) +void Script_GetObjectPosition(struct ScriptContext *ctx) { u32 localId = VarGet(ScriptReadHalfword(ctx)); u32 useTemplate = VarGet(ScriptReadHalfword(ctx)); - u16 *x = &gSpecialVar_0x8007; - u16 *y = &gSpecialVar_0x8008; - u32 objectId; - struct ObjectEvent* objEvent; - - if (useTemplate) - { - const struct ObjectEventTemplate *objTemplate = FindObjectEventTemplateByLocalId(localId, gSaveBlock1Ptr->objectEventTemplates, gMapHeader.events->objectEventCount); - *x = objTemplate->x; - *y = objTemplate->y; - return; - } - - objectId = GetObjectEventIdByLocalId(localId); - objEvent = &gObjectEvents[objectId]; - *x = objEvent->currentCoords.x - 7; - *y = objEvent->currentCoords.y - 7; + GetObjectPosition(localId,useTemplate); } -bool32 CheckObjectAtXY(struct ScriptContext *ctx) +void Script_CheckObjectAtXY(struct ScriptContext *ctx) { u32 x = VarGet(ScriptReadHalfword(ctx)) + 7; u32 y = VarGet(ScriptReadHalfword(ctx)) + 7; - u32 i; - - for (i = 0; i < OBJECT_EVENTS_COUNT; i++) - { - if (!gObjectEvents[i].active) - continue; - - if (gObjectEvents[i].currentCoords.x != x) - continue; - - if (gObjectEvents[i].currentCoords.y != y) - continue; - return TRUE; - } - return FALSE; + gSpecialVar_Result = CheckObjectAtXY(x,y); } void Script_GetSetPokedexFlag(struct ScriptContext *ctx) { u32 speciesId = SpeciesToNationalPokedexNum(VarGet(ScriptReadHalfword(ctx))); bool32 desiredFlag = VarGet(ScriptReadHalfword(ctx)); + gSpecialVar_Result = GetSetPokedexFlag(speciesId,desiredFlag); if (desiredFlag == FLAG_SET_CAUGHT) GetSetPokedexFlag(speciesId,FLAG_SET_SEEN); - - gSpecialVar_Result = GetSetPokedexFlag(speciesId,desiredFlag); } -void CheckPartyHasSpecie(struct ScriptContext *ctx) +void Script_CheckPartyHasSpecie(struct ScriptContext *ctx) { - u32 partyIndex; u32 givenSpecies = VarGet(ScriptReadHalfword(ctx)); - u32 hasSpecies = FALSE; - - for (partyIndex = 0; partyIndex < CalculatePlayerPartyCount(); partyIndex++) - if (GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPECIES) == givenSpecies) - hasSpecies = TRUE; - - gSpecialVar_Result = hasSpecies; + gSpecialVar_Result = CheckPartyHasSpecie(givenSpecies); } -void CheckChosenMonMatchDesiredSpecie(struct ScriptContext *ctx) +void Script_CheckChosenMonMatchDesiredSpecie(struct ScriptContext *ctx) { u32 givenSpecies = VarGet(ScriptReadHalfword(ctx)); - gSpecialVar_Result = (GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPECIES) == givenSpecies); } From a509f28038663b63eaaec88ba665695833db54e9 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sat, 14 Sep 2024 06:32:47 -0700 Subject: [PATCH 27/65] Changed Scripot_GetObjectFacingDirection to callnative --- asm/macros/event.inc | 11 +++++++++-- data/specials.inc | 1 - src/event_object_movement.c | 5 ----- src/scrcmd.c | 6 ++++++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 73d0d07526..9a99ae3756 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2421,6 +2421,13 @@ @ Gets the facing direction of a given event object and stores it in the variable \dest. .macro getobjectfacingdirection localId:req, dest:req - setvar VAR_0x8005, \localId - specialvar \dest, Script_GetObjectFacingDirection + callnative Script_GetObjectFacingDirection + .2byte \localId + .byte \dest .endm + + @ .macro checkobjectat x:req, y:req, dest = VAR_RESULT + @ callnative Script_CheckObjectAtXY + @ .2byte \x + @ .2byte \y + @ .2byte \dest diff --git a/data/specials.inc b/data/specials.inc index 8233deda59..f02497d603 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -554,4 +554,3 @@ gSpecials:: def_special Script_GetChosenMonDefensiveEVs def_special Script_GetChosenMonOffensiveIVs def_special Script_GetChosenMonDefensiveIVs - def_special Script_GetObjectFacingDirection diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 646d2323fb..8fa688fdf6 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -10633,8 +10633,3 @@ void GetDaycareGraphics(struct ScriptContext *ctx) gSpecialVar_Result = i; } -u32 Script_GetObjectFacingDirection(void) -{ - return gObjectEvents[GetObjectEventIdByLocalId(gSpecialVar_0x8005)].facingDirection; -} - diff --git a/src/scrcmd.c b/src/scrcmd.c index 974b223f26..fc34720683 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -2520,4 +2520,10 @@ void Script_CheckChosenMonMatchDesiredSpecie(struct ScriptContext *ctx) gSpecialVar_Result = (GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPECIES) == givenSpecies); } +void Script_GetObjectFacingDirection(struct ScriptContext *ctx) +{ + u32 objectId = VarGet(ScriptReadHalfword(ctx)); + u16 *varPointer = GetVarPointer(ScriptReadHalfword(ctx)); + *varPointer = gObjectEvents[GetObjectEventIdByLocalId(objectId)].facingDirection; +} From 1665423e5214bc834c5f4cf8acc5c82bf3570347 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sat, 14 Sep 2024 07:11:04 -0700 Subject: [PATCH 28/65] Renamed scripts to match vanilla Cleaned up descriptions --- asm/macros/event.inc | 49 +++++++++++++++++++------------------------- src/scrcmd.c | 34 ++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 9a99ae3756..985ced862f 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2341,76 +2341,74 @@ @ Remove all of specified item from the player's bag and return the number of removed items to VAR_RESULT .macro removeallitem itemId:req - callnative RemoveAllItem + callnative ScrCmd_removeallitem .2byte \itemId .endm - @ Stores the position of the given object in VAR_0x8007 (x) and VAR_0x8008 (y). Mode CURRENT_POSITION will take the object's current position. Mode TEMPLATE_POSITION will takje the object's template position. + @ Stores the position of the given object in VAR_0x8007 (x) and VAR_0x8008 (y). Mode CURRENT_POSITION will take the object's current position. Mode TEMPLATE_POSITION will take the object's template position. .macro getobjectxy localId:req, posType:req - callnative Script_GetObjectPosition + callnative ScrCmd_getobjectxy .2byte \localId .2byte \posType .endm .macro getobjecttemplatexy localId:req, posType = TEMPLATE_POSITION - callnative Script_GetObjectPosition + callnative ScrCmd_getobjectxy .2byte \localId .2byte \posType .endm .macro getobjectcurrentxy localId:req, posType = CURRENT_POSITION - callnative Script_GetObjectPosition + callnative ScrCmd_getobjectxy .2byte \localId .2byte \posType .endm - @ checks if there is any object at a given position + @ Return TRUE to dest if there is an object at the position x and y. .macro checkobjectat x:req, y:req, dest = VAR_RESULT - callnative Script_CheckObjectAtXY + callnative ScrCmd_checkobjectat .2byte \x .2byte \y .2byte \dest .endm - @ Checks the state of the Pokédex Seen flag of the specified Pokemon - @ The result is stored in VAR_RESULT + @ Returns the state of the Pokedex Seen Flag to VAR_RESULT for the Pokemon with speciesId .macro getseenmon species:req - callnative Script_Script_GetSetPokedexFlag + callnative Scrcmd_getsetpokedexflag .2byte \species .2byte FLAG_GET_SEEN .endm - @ Checks the state of the Pokédex Caught flag of the specified Pokemon - @ The result is stored in VAR_RESULT + @ Returns the state of the Pokedex Caught Flag to VAR_RESULT for the Pokemon with speciesId .macro getcaughtmon species:req - callnative Script_Script_GetSetPokedexFlag + callnative Scrcmd_getsetpokedexflag .2byte \species .2byte FLAG_GET_CAUGHT .endm - @ Sets the Pokédex Seen flag of the specified Pokemon + @ Sets the Pokedex Seen Flag for the Pokemon with speciesId .macro setseenmon species:req - callnative Script_Script_GetSetPokedexFlag + callnative Scrcmd_getsetpokedexflag .2byte \species .2byte FLAG_SET_SEEN .endm - @ Sets the Pokédex Caught flag of the specified Pokemon + @ Sets the Pokedex Caught Flag for the Pokemon with speciesId .macro setcaughtmon species:req - callnative Script_Script_GetSetPokedexFlag + callnative Scrcmd_getsetpokedexflag .2byte \species .2byte FLAG_SET_CAUGHT .endm - @ Check if the Player has \speciesId in their party. OPEN_PARTY_SCREEN will have the player select a mon from their party. NO_PARTY_SCREEN will automatically check every mon in the player's party. + @ Check if the Player has speciesId in their party. OPEN_PARTY_SCREEN will have the player select a mon from their party. NO_PARTY_SCREEN will automatically check every mon in the player's party. .macro checkspecies speciesId:req, mode=NO_PARTY_SCREEN .if \mode == OPEN_PARTY_SCREEN special ChoosePartyMon waitstate - callnative Script_CheckChosenMonMatchDesiredSpecie + callnative Scrcmd_checkspecies_choose .2byte \speciesId .else - callnative Script_CheckPartyHasSpecie + callnative Scrcmd_checkspecies .2byte \speciesId .endif .endm @@ -2419,15 +2417,10 @@ checkspecies \speciesId, OPEN_PARTY_SCREEN .endm - @ Gets the facing direction of a given event object and stores it in the variable \dest. + @ Gets the facing direction of a given event object and stores it in the variable dest. .macro getobjectfacingdirection localId:req, dest:req - callnative Script_GetObjectFacingDirection + callnative Scrcmd_getobjectfacingdirection .2byte \localId - .byte \dest + .2byte \dest .endm - @ .macro checkobjectat x:req, y:req, dest = VAR_RESULT - @ callnative Script_CheckObjectAtXY - @ .2byte \x - @ .2byte \y - @ .2byte \dest diff --git a/src/scrcmd.c b/src/scrcmd.c index fc34720683..be5c3c9346 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -32,6 +32,7 @@ #include "mystery_event_script.h" #include "palette.h" #include "party_menu.h" +#include "pokedex.h" #include "pokemon_storage_system.h" #include "random.h" #include "overworld.h" @@ -53,7 +54,6 @@ #include "list_menu.h" #include "malloc.h" #include "constants/event_objects.h" -#include "pokedex.h" typedef u16 (*SpecialFunc)(void); typedef void (*NativeFunc)(struct ScriptContext *ctx); @@ -2475,30 +2475,38 @@ void ScriptSetDoubleBattleFlag(struct ScriptContext *ctx) sIsScriptedWildDouble = TRUE; } -void RemoveAllItem(struct ScriptContext *ctx) +bool8 ScrCmd_removeallitem(struct ScriptContext *ctx) { u32 itemId = VarGet(ScriptReadHalfword(ctx)); u32 count = CountTotalItemQuantityInBag(itemId); gSpecialVar_Result = count; RemoveBagItem(itemId, count); + + return FALSE; } -void Script_GetObjectPosition(struct ScriptContext *ctx) +bool8 ScrCmd_getobjectxy(struct ScriptContext *ctx) { u32 localId = VarGet(ScriptReadHalfword(ctx)); u32 useTemplate = VarGet(ScriptReadHalfword(ctx)); GetObjectPosition(localId,useTemplate); + + return FALSE; } -void Script_CheckObjectAtXY(struct ScriptContext *ctx) +bool8 ScrCmd_checkobjectat(struct ScriptContext *ctx) { u32 x = VarGet(ScriptReadHalfword(ctx)) + 7; u32 y = VarGet(ScriptReadHalfword(ctx)) + 7; - gSpecialVar_Result = CheckObjectAtXY(x,y); + u16 *varPointer = GetVarPointer(ScriptReadHalfword(ctx)); + + *varPointer = CheckObjectAtXY(x,y); + + return FALSE; } -void Script_GetSetPokedexFlag(struct ScriptContext *ctx) +bool8 Scrcmd_getsetpokedexflag(struct ScriptContext *ctx) { u32 speciesId = SpeciesToNationalPokedexNum(VarGet(ScriptReadHalfword(ctx))); bool32 desiredFlag = VarGet(ScriptReadHalfword(ctx)); @@ -2506,24 +2514,32 @@ void Script_GetSetPokedexFlag(struct ScriptContext *ctx) if (desiredFlag == FLAG_SET_CAUGHT) GetSetPokedexFlag(speciesId,FLAG_SET_SEEN); + + return FALSE; } -void Script_CheckPartyHasSpecie(struct ScriptContext *ctx) +bool8 Scrcmd_checkspecies(struct ScriptContext *ctx) { u32 givenSpecies = VarGet(ScriptReadHalfword(ctx)); gSpecialVar_Result = CheckPartyHasSpecie(givenSpecies); + + return FALSE; } -void Script_CheckChosenMonMatchDesiredSpecie(struct ScriptContext *ctx) +bool8 Scrcmd_checkspecies_choose(struct ScriptContext *ctx) { u32 givenSpecies = VarGet(ScriptReadHalfword(ctx)); gSpecialVar_Result = (GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPECIES) == givenSpecies); + + return FALSE; } -void Script_GetObjectFacingDirection(struct ScriptContext *ctx) +bool8 Scrcmd_getobjectfacingdirection(struct ScriptContext *ctx) { u32 objectId = VarGet(ScriptReadHalfword(ctx)); u16 *varPointer = GetVarPointer(ScriptReadHalfword(ctx)); *varPointer = gObjectEvents[GetObjectEventIdByLocalId(objectId)].facingDirection; + + return FALSE; } From 60fb0b34fddf3f9bf4d010f29348f7b9c57cafa4 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sat, 14 Sep 2024 07:48:24 -0700 Subject: [PATCH 29/65] Modified getobjectxy to allow for custom destinations --- asm/macros/event.inc | 14 ++++++++++---- include/field_specials.h | 2 +- src/field_specials.c | 13 +++++-------- src/scrcmd.c | 5 +++-- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 985ced862f..710514cda1 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2345,23 +2345,29 @@ .2byte \itemId .endm - @ Stores the position of the given object in VAR_0x8007 (x) and VAR_0x8008 (y). Mode CURRENT_POSITION will take the object's current position. Mode TEMPLATE_POSITION will take the object's template position. - .macro getobjectxy localId:req, posType:req + @ Stores the position of the given object in destX and destY. Mode CURRENT_POSITION will take the object's current position. Mode TEMPLATE_POSITION will take the object's template position. + .macro getobjectxy localId:req, posType:req, destX:req, destY:req callnative ScrCmd_getobjectxy .2byte \localId .2byte \posType + .2byte \destX + .2byte \destY .endm - .macro getobjecttemplatexy localId:req, posType = TEMPLATE_POSITION + .macro getobjecttemplatexy localId:req, posType = TEMPLATE_POSITION, destX:req, destY:req callnative ScrCmd_getobjectxy .2byte \localId .2byte \posType + .2byte \destX + .2byte \destY .endm - .macro getobjectcurrentxy localId:req, posType = CURRENT_POSITION + .macro getobjectcurrentxy localId:req, posType = CURRENT_POSITION, destX:req, destY:req callnative ScrCmd_getobjectxy .2byte \localId .2byte \posType + .2byte \destX + .2byte \destY .endm @ Return TRUE to dest if there is an object at the position x and y. diff --git a/include/field_specials.h b/include/field_specials.h index 85db5ba40f..f391a46ba5 100644 --- a/include/field_specials.h +++ b/include/field_specials.h @@ -33,7 +33,7 @@ void ResetFanClub(void); bool8 ShouldShowBoxWasFullMessage(void); void SetPCBoxToSendMon(u8 boxId); void PreparePartyForSkyBattle(void); -void GetObjectPosition(u32, u32); +void GetObjectPosition(u16*, u16*, u32, u32); bool32 CheckObjectAtXY(u32, u32); bool32 CheckPartyHasSpecie(u32); diff --git a/src/field_specials.c b/src/field_specials.c index 94a98812b1..8b2ac8de8d 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4278,26 +4278,23 @@ void PreparePartyForSkyBattle(void) CompactPartySlots(); } -void GetObjectPosition(u32 localId, u32 useTemplate) +void GetObjectPosition(u16* xPointer, u16* yPointer, u32 localId, u32 useTemplate) { u32 objectId; struct ObjectEvent* objEvent; - u16 *x = &gSpecialVar_0x8007; - u16 *y = &gSpecialVar_0x8008; - if (useTemplate) { const struct ObjectEventTemplate *objTemplate = FindObjectEventTemplateByLocalId(localId, gSaveBlock1Ptr->objectEventTemplates, gMapHeader.events->objectEventCount); - *x = objTemplate->x; - *y = objTemplate->y; + *xPointer = objTemplate->x; + *yPointer = objTemplate->y; return; } objectId = GetObjectEventIdByLocalId(localId); objEvent = &gObjectEvents[objectId]; - *x = objEvent->currentCoords.x - 7; - *y = objEvent->currentCoords.y - 7; + *xPointer = objEvent->currentCoords.x - 7; + *yPointer = objEvent->currentCoords.y - 7; } bool32 CheckObjectAtXY(u32 x, u32 y) diff --git a/src/scrcmd.c b/src/scrcmd.c index be5c3c9346..542f3d1dfc 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -2489,8 +2489,9 @@ bool8 ScrCmd_getobjectxy(struct ScriptContext *ctx) { u32 localId = VarGet(ScriptReadHalfword(ctx)); u32 useTemplate = VarGet(ScriptReadHalfword(ctx)); - - GetObjectPosition(localId,useTemplate); + u16 *pX = GetVarPointer(ScriptReadHalfword(ctx)); + u16 *pY = GetVarPointer(ScriptReadHalfword(ctx)); + GetObjectPosition(pX,pY,localId,useTemplate); return FALSE; } From 3b226fc2e240ef6a0967e1238a34ca07a4edea54 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sat, 14 Sep 2024 09:01:53 -0700 Subject: [PATCH 30/65] Removed swp --- .swp | Bin 4096 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .swp diff --git a/.swp b/.swp deleted file mode 100644 index 099c8db49858ca8579ae6d0417da70c45e4b4c51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmYc?2=nw+u+%eP00IFJ0RfJ6Obi9ZX)rEc+Ql{4JH$Ug*U};^%+=Hkui8<`(GVC7 zfq@DE&QN0`LvU7CR#H?D7786Ghm6`g8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFaRL{ E0G?C|hyVZp From d76ccddad3908789a1a611d057ec8be897900571 Mon Sep 17 00:00:00 2001 From: pkmnsnfrn Date: Sat, 14 Sep 2024 09:03:49 -0700 Subject: [PATCH 31/65] spaces to tabs in inc files --- asm/macros/event.inc | 122 +++++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 710514cda1..a67d5a751c 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2341,92 +2341,92 @@ @ Remove all of specified item from the player's bag and return the number of removed items to VAR_RESULT .macro removeallitem itemId:req - callnative ScrCmd_removeallitem + callnative ScrCmd_removeallitem .2byte \itemId .endm @ Stores the position of the given object in destX and destY. Mode CURRENT_POSITION will take the object's current position. Mode TEMPLATE_POSITION will take the object's template position. .macro getobjectxy localId:req, posType:req, destX:req, destY:req callnative ScrCmd_getobjectxy - .2byte \localId - .2byte \posType - .2byte \destX - .2byte \destY + .2byte \localId + .2byte \posType + .2byte \destX + .2byte \destY .endm .macro getobjecttemplatexy localId:req, posType = TEMPLATE_POSITION, destX:req, destY:req callnative ScrCmd_getobjectxy - .2byte \localId - .2byte \posType - .2byte \destX - .2byte \destY + .2byte \localId + .2byte \posType + .2byte \destX + .2byte \destY .endm .macro getobjectcurrentxy localId:req, posType = CURRENT_POSITION, destX:req, destY:req callnative ScrCmd_getobjectxy - .2byte \localId - .2byte \posType - .2byte \destX - .2byte \destY + .2byte \localId + .2byte \posType + .2byte \destX + .2byte \destY .endm - @ Return TRUE to dest if there is an object at the position x and y. + @ Return TRUE to dest if there is an object at the position x and y. .macro checkobjectat x:req, y:req, dest = VAR_RESULT - callnative ScrCmd_checkobjectat - .2byte \x - .2byte \y - .2byte \dest + callnative ScrCmd_checkobjectat + .2byte \x + .2byte \y + .2byte \dest .endm - @ Returns the state of the Pokedex Seen Flag to VAR_RESULT for the Pokemon with speciesId - .macro getseenmon species:req - callnative Scrcmd_getsetpokedexflag - .2byte \species - .2byte FLAG_GET_SEEN - .endm + @ Returns the state of the Pokedex Seen Flag to VAR_RESULT for the Pokemon with speciesId + .macro getseenmon species:req + callnative Scrcmd_getsetpokedexflag + .2byte \species + .2byte FLAG_GET_SEEN + .endm - @ Returns the state of the Pokedex Caught Flag to VAR_RESULT for the Pokemon with speciesId - .macro getcaughtmon species:req - callnative Scrcmd_getsetpokedexflag - .2byte \species - .2byte FLAG_GET_CAUGHT - .endm + @ Returns the state of the Pokedex Caught Flag to VAR_RESULT for the Pokemon with speciesId + .macro getcaughtmon species:req + callnative Scrcmd_getsetpokedexflag + .2byte \species + .2byte FLAG_GET_CAUGHT + .endm - @ Sets the Pokedex Seen Flag for the Pokemon with speciesId - .macro setseenmon species:req - callnative Scrcmd_getsetpokedexflag - .2byte \species - .2byte FLAG_SET_SEEN - .endm + @ Sets the Pokedex Seen Flag for the Pokemon with speciesId + .macro setseenmon species:req + callnative Scrcmd_getsetpokedexflag + .2byte \species + .2byte FLAG_SET_SEEN + .endm - @ Sets the Pokedex Caught Flag for the Pokemon with speciesId - .macro setcaughtmon species:req - callnative Scrcmd_getsetpokedexflag - .2byte \species - .2byte FLAG_SET_CAUGHT - .endm + @ Sets the Pokedex Caught Flag for the Pokemon with speciesId + .macro setcaughtmon species:req + callnative Scrcmd_getsetpokedexflag + .2byte \species + .2byte FLAG_SET_CAUGHT + .endm - @ Check if the Player has speciesId in their party. OPEN_PARTY_SCREEN will have the player select a mon from their party. NO_PARTY_SCREEN will automatically check every mon in the player's party. - .macro checkspecies speciesId:req, mode=NO_PARTY_SCREEN - .if \mode == OPEN_PARTY_SCREEN - special ChoosePartyMon - waitstate - callnative Scrcmd_checkspecies_choose - .2byte \speciesId - .else - callnative Scrcmd_checkspecies - .2byte \speciesId - .endif - .endm + @ Check if the Player has speciesId in their party. OPEN_PARTY_SCREEN will have the player select a mon from their party. NO_PARTY_SCREEN will automatically check every mon in the player's party. + .macro checkspecies speciesId:req, mode=NO_PARTY_SCREEN + .if \mode == OPEN_PARTY_SCREEN + special ChoosePartyMon + waitstate + callnative Scrcmd_checkspecies_choose + .2byte \speciesId + .else + callnative Scrcmd_checkspecies + .2byte \speciesId + .endif + .endm - .macro checkspecies_choose speciesId:req + .macro checkspecies_choose speciesId:req checkspecies \speciesId, OPEN_PARTY_SCREEN - .endm + .endm @ Gets the facing direction of a given event object and stores it in the variable dest. - .macro getobjectfacingdirection localId:req, dest:req - callnative Scrcmd_getobjectfacingdirection - .2byte \localId - .2byte \dest - .endm + .macro getobjectfacingdirection localId:req, dest:req + callnative Scrcmd_getobjectfacingdirection + .2byte \localId + .2byte \dest + .endm From 91325e83c185700d5cc65ef2f01dbba4b9c61bd4 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:00:12 +0100 Subject: [PATCH 32/65] Fixes to Protosynthesis, Quark Drive, Beast Boost, Orichalcum Pulse, Hadron Engine (#5447) * Protosynthesis & Quark Drive interactions; Fixes to Beast Boost/Protosynthesis/Quark Drive stat tie priority, Orichalcum Pulse and Hadron Engine stat boost * Protosynthesis + Quark Drive tests * Update src/battle_util.c * Update src/battle_util.c * Update src/battle_util.c * Update src/battle_util.c * Update src/battle_util.c * Update src/battle_util.c --------- Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- src/battle_main.c | 4 +- src/battle_script_commands.c | 5 +- src/battle_util.c | 40 ++++++--- test/battle/ability/protosynthesis.c | 125 ++++++++++++++++++++++++++- test/battle/ability/quark_drive.c | 106 +++++++++++++++++++++++ 5 files changed, 260 insertions(+), 20 deletions(-) diff --git a/src/battle_main.c b/src/battle_main.c index a9336c3f8b..3f4f140f65 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4743,9 +4743,9 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect) speed *= 2; else if (ability == ABILITY_SLOW_START && gDisableStructs[battler].slowStartTimer != 0) speed /= 2; - else if (ability == ABILITY_PROTOSYNTHESIS && (gBattleWeather & B_WEATHER_SUN || gBattleStruct->boosterEnergyActivates & gBitTable[battler])) + else if (ability == ABILITY_PROTOSYNTHESIS && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && ((gBattleWeather & B_WEATHER_SUN && WEATHER_HAS_EFFECT) || gBattleStruct->boosterEnergyActivates & (1u << battler))) speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed; - else if (ability == ABILITY_QUARK_DRIVE && (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gBattleStruct->boosterEnergyActivates & gBitTable[battler])) + else if (ability == ABILITY_QUARK_DRIVE && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gBattleStruct->boosterEnergyActivates & (1u << battler))) speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed; // stat stages diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index aa92a7b884..f16ee0bf76 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8777,12 +8777,15 @@ u32 GetHighestStatId(u32 battler) for (i = STAT_DEF; i < NUM_STATS; i++) { u16 *statVal = &gBattleMons[battler].attack + (i - 1); - if (*statVal > highestStat) + if (*statVal > highestStat && i != STAT_SPEED) { highestStat = *statVal; highestId = i; } } + if (gBattleMons[battler].speed > highestStat) + highestId = STAT_SPEED; + return highestId; } diff --git a/src/battle_util.c b/src/battle_util.c index 86e846d09c..2e463eaf58 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6329,7 +6329,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_PROTOSYNTHESIS: - if (!gDisableStructs[battler].weatherAbilityDone && IsBattlerWeatherAffected(battler, B_WEATHER_SUN)) + if (!gDisableStructs[battler].weatherAbilityDone + && (gBattleWeather & B_WEATHER_SUN) && WEATHER_HAS_EFFECT + && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) + && !(gBattleStruct->boosterEnergyActivates & (1u << battler))) { gDisableStructs[battler].weatherAbilityDone = TRUE; PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler)); @@ -6355,7 +6358,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_QUARK_DRIVE: - if (!gDisableStructs[battler].terrainAbilityDone && IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN)) + if (!gDisableStructs[battler].terrainAbilityDone + && gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN + && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) + && !(gBattleStruct->boosterEnergyActivates & (1u << battler))) { gDisableStructs[battler].terrainAbilityDone = TRUE; PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler)); @@ -7594,7 +7600,8 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) break; case HOLD_EFFECT_BOOSTER_ENERGY: if (!(gBattleStruct->boosterEnergyActivates & gBitTable[battler]) - && (((GetBattlerAbility(battler) == ABILITY_PROTOSYNTHESIS) && !(gBattleWeather & B_WEATHER_SUN)) + && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) + && (((GetBattlerAbility(battler) == ABILITY_PROTOSYNTHESIS) && !((gBattleWeather & B_WEATHER_SUN) && WEATHER_HAS_EFFECT)) || ((GetBattlerAbility(battler) == ABILITY_QUARK_DRIVE) && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)))) { PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler)); @@ -7861,7 +7868,8 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) break; case HOLD_EFFECT_BOOSTER_ENERGY: if (!(gBattleStruct->boosterEnergyActivates & gBitTable[battler]) - && (((GetBattlerAbility(battler) == ABILITY_PROTOSYNTHESIS) && !(gBattleWeather & B_WEATHER_SUN)) + && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) + && (((GetBattlerAbility(battler) == ABILITY_PROTOSYNTHESIS) && !((gBattleWeather & B_WEATHER_SUN) && WEATHER_HAS_EFFECT)) || ((GetBattlerAbility(battler) == ABILITY_QUARK_DRIVE) && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)))) { PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler)); @@ -9285,25 +9293,27 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32 case ABILITY_PROTOSYNTHESIS: { u8 atkHighestStat = GetHighestStatId(battlerAtk); - if ((weather & B_WEATHER_SUN || gBattleStruct->boosterEnergyActivates & gBitTable[battlerAtk]) - && ((IS_MOVE_PHYSICAL(move) && atkHighestStat == STAT_ATK) || (IS_MOVE_SPECIAL(move) && atkHighestStat == STAT_SPATK))) + if (((weather & B_WEATHER_SUN && WEATHER_HAS_EFFECT) || gBattleStruct->boosterEnergyActivates & (1u << battlerAtk)) + && ((IS_MOVE_PHYSICAL(move) && atkHighestStat == STAT_ATK) || (IS_MOVE_SPECIAL(move) && atkHighestStat == STAT_SPATK)) + && !(gBattleMons[battlerAtk].status2 & STATUS2_TRANSFORMED)) modifier = uq4_12_multiply(modifier, UQ_4_12(1.3)); } break; case ABILITY_QUARK_DRIVE: { u8 atkHighestStat = GetHighestStatId(battlerAtk); - if ((gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gBattleStruct->boosterEnergyActivates & gBitTable[battlerAtk]) - && ((IS_MOVE_PHYSICAL(move) && atkHighestStat == STAT_ATK) || (IS_MOVE_SPECIAL(move) && atkHighestStat == STAT_SPATK))) + if ((gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gBattleStruct->boosterEnergyActivates & (1u << battlerAtk)) + && ((IS_MOVE_PHYSICAL(move) && atkHighestStat == STAT_ATK) || (IS_MOVE_SPECIAL(move) && atkHighestStat == STAT_SPATK)) + && !(gBattleMons[battlerAtk].status2 & STATUS2_TRANSFORMED)) modifier = uq4_12_multiply(modifier, UQ_4_12(1.3)); } break; case ABILITY_ORICHALCUM_PULSE: - if (weather & B_WEATHER_SUN) + if (weather & B_WEATHER_SUN && WEATHER_HAS_EFFECT && IS_MOVE_PHYSICAL(move)) modifier = uq4_12_multiply(modifier, UQ_4_12(1.3)); break; case ABILITY_HADRON_ENGINE: - if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && IS_MOVE_SPECIAL(move)) modifier = uq4_12_multiply(modifier, UQ_4_12(1.3)); break; case ABILITY_SHARPNESS: @@ -9363,16 +9373,18 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32 case ABILITY_PROTOSYNTHESIS: { u8 defHighestStat = GetHighestStatId(battlerDef); - if ((weather & B_WEATHER_SUN || gBattleStruct->boosterEnergyActivates & gBitTable[battlerDef]) - && ((IS_MOVE_PHYSICAL(move) && defHighestStat == STAT_DEF) || (IS_MOVE_SPECIAL(move) && defHighestStat == STAT_SPDEF))) + if (((weather & B_WEATHER_SUN && WEATHER_HAS_EFFECT) || gBattleStruct->boosterEnergyActivates & (1u << battlerDef)) + && ((IS_MOVE_PHYSICAL(move) && defHighestStat == STAT_DEF) || (IS_MOVE_SPECIAL(move) && defHighestStat == STAT_SPDEF)) + && !(gBattleMons[battlerDef].status2 & STATUS2_TRANSFORMED)) modifier = uq4_12_multiply(modifier, UQ_4_12(0.7)); } break; case ABILITY_QUARK_DRIVE: { u8 defHighestStat = GetHighestStatId(battlerDef); - if ((gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gBattleStruct->boosterEnergyActivates & gBitTable[battlerDef]) - && ((IS_MOVE_PHYSICAL(move) && defHighestStat == STAT_DEF) || (IS_MOVE_SPECIAL(move) && defHighestStat == STAT_SPDEF))) + if ((gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gBattleStruct->boosterEnergyActivates & (1u << battlerDef)) + && ((IS_MOVE_PHYSICAL(move) && defHighestStat == STAT_DEF) || (IS_MOVE_SPECIAL(move) && defHighestStat == STAT_SPDEF)) + && !(gBattleMons[battlerDef].status2 & STATUS2_TRANSFORMED)) modifier = uq4_12_multiply(modifier, UQ_4_12(0.7)); } break; diff --git a/test/battle/ability/protosynthesis.c b/test/battle/ability/protosynthesis.c index 58f10b366f..2be9f81d28 100644 --- a/test/battle/ability/protosynthesis.c +++ b/test/battle/ability/protosynthesis.c @@ -100,6 +100,125 @@ SINGLE_BATTLE_TEST("Protosynthesis activates on switch-in") } } -TO_DO_BATTLE_TEST("Protosynthesis activates in sun before Booster Energy"); -TO_DO_BATTLE_TEST("Protosynthesis activates even if the Pokémon is holding an Utility Umbrella"); -TO_DO_BATTLE_TEST("Protosynthesis doesn't activate if Cloud Nine/Air Lock is on the field"); +SINGLE_BATTLE_TEST("Protosynthesis boosts Attack 1st in case of a stat tie") +{ + GIVEN { + PLAYER(SPECIES_GREAT_TUSK) { Ability(ABILITY_PROTOSYNTHESIS); Attack(5); Defense(5); SpAttack(5); SpDefense(5); Speed(5); } + OPPONENT(SPECIES_GROUDON) { Ability(ABILITY_DROUGHT); Speed(5); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_DROUGHT); + ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + MESSAGE("Great Tusk's Attack was heightened!"); + } +} + +SINGLE_BATTLE_TEST("Protosynthesis boosts Defense 2nd in case of a stat tie") +{ + GIVEN { + PLAYER(SPECIES_GREAT_TUSK) { Ability(ABILITY_PROTOSYNTHESIS); Attack(4); Defense(5); SpAttack(5); SpDefense(5); Speed(5); } + OPPONENT(SPECIES_GROUDON) { Ability(ABILITY_DROUGHT); Speed(5); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_DROUGHT); + ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + MESSAGE("Great Tusk's Defense was heightened!"); + } +} + +SINGLE_BATTLE_TEST("Protosynthesis boosts Special Attack 3rd in case of a stat tie") +{ + GIVEN { + PLAYER(SPECIES_GREAT_TUSK) { Ability(ABILITY_PROTOSYNTHESIS); Attack(4); Defense(4); SpAttack(5); SpDefense(5); Speed(5); } + OPPONENT(SPECIES_GROUDON) { Ability(ABILITY_DROUGHT); Speed(5); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_DROUGHT); + ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + MESSAGE("Great Tusk's Sp. Atk was heightened!"); + } +} + +SINGLE_BATTLE_TEST("Protosynthesis boosts Special Defense 4th in case of a stat tie") +{ + GIVEN { + PLAYER(SPECIES_GREAT_TUSK) { Ability(ABILITY_PROTOSYNTHESIS); Attack(4); Defense(4); SpAttack(4); SpDefense(5); Speed(5); } + OPPONENT(SPECIES_GROUDON) { Ability(ABILITY_DROUGHT); Speed(5); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_DROUGHT); + ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + MESSAGE("Great Tusk's Sp. Def was heightened!"); + } +} + +SINGLE_BATTLE_TEST("Protosynthesis activates in Sun before Booster Energy") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_GREAT_TUSK) { Ability(ABILITY_PROTOSYNTHESIS); Item(ITEM_BOOSTER_ENERGY); } + OPPONENT(SPECIES_NINETALES) { Ability(ABILITY_DROUGHT); } + } WHEN { + TURN { SWITCH(player, 1); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_DROUGHT); + ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + } THEN { + EXPECT_EQ(player->item, ITEM_BOOSTER_ENERGY); + } +} + +SINGLE_BATTLE_TEST("Protosynthesis doesn't activate for a transformed battler") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_GREAT_TUSK) { Ability(ABILITY_PROTOSYNTHESIS); Item(ITEM_BOOSTER_ENERGY); } + OPPONENT(SPECIES_NINETALES) { Ability(ABILITY_DROUGHT); Item(ITEM_BOOSTER_ENERGY); } + } WHEN { + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TRANSFORM); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_DROUGHT); + ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, opponent); + NOT ABILITY_POPUP(opponent, ABILITY_PROTOSYNTHESIS); + } THEN { + EXPECT_EQ(player->item, ITEM_BOOSTER_ENERGY); + EXPECT_EQ(opponent->item, ITEM_BOOSTER_ENERGY); + EXPECT_EQ(opponent->ability, ABILITY_PROTOSYNTHESIS); + } +} + +SINGLE_BATTLE_TEST("Protosynthesis activates even if the Pokémon is holding an Utility Umbrella") +{ + GIVEN { + PLAYER(SPECIES_GREAT_TUSK) { Ability(ABILITY_PROTOSYNTHESIS); Item(ITEM_UTILITY_UMBRELLA); } + OPPONENT(SPECIES_NINETALES) { Ability(ABILITY_DROUGHT); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_DROUGHT); + ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + } +} + +SINGLE_BATTLE_TEST("Protosynthesis doesn't activate if Cloud Nine/Air Lock is on the field") +{ + u32 species, ability; + PARAMETRIZE { species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; } + PARAMETRIZE { species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; } + + GIVEN { + PLAYER(SPECIES_GREAT_TUSK) { Ability(ABILITY_PROTOSYNTHESIS); } + OPPONENT(species) { Ability(ability); } + } WHEN { + TURN { MOVE(opponent, MOVE_SUNNY_DAY); } + } SCENE { + ABILITY_POPUP(opponent, ability); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent); + NOT ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + } +} diff --git a/test/battle/ability/quark_drive.c b/test/battle/ability/quark_drive.c index 4e81b012ae..928ee45eb5 100644 --- a/test/battle/ability/quark_drive.c +++ b/test/battle/ability/quark_drive.c @@ -100,3 +100,109 @@ SINGLE_BATTLE_TEST("Quark Drive activates on switch-in") MESSAGE("Iron Moth's Sp. Atk was heightened!"); } } + +SINGLE_BATTLE_TEST("Quark Drive activates on Electric Terrain even if not grounded") +{ + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_IRON_JUGULIS].types[0] == TYPE_FLYING || gSpeciesInfo[SPECIES_IRON_JUGULIS].types[1] == TYPE_FLYING); + PLAYER(SPECIES_IRON_JUGULIS) { Ability(ABILITY_QUARK_DRIVE); } + OPPONENT(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); }; + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_ELECTRIC_SURGE); + ABILITY_POPUP(player, ABILITY_QUARK_DRIVE); + } +} + +SINGLE_BATTLE_TEST("Quark Drive boosts Attack 1st in case of a stat tie") +{ + GIVEN { + PLAYER(SPECIES_IRON_TREADS) { Ability(ABILITY_QUARK_DRIVE); Attack(5); Defense(5); SpAttack(5); SpDefense(5); Speed(5); } + OPPONENT(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); Speed(5); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_ELECTRIC_SURGE); + ABILITY_POPUP(player, ABILITY_QUARK_DRIVE); + MESSAGE("Iron Treads's Attack was heightened!"); + } +} + +SINGLE_BATTLE_TEST("Quark Drive boosts Defense 2nd in case of a stat tie") +{ + GIVEN { + PLAYER(SPECIES_IRON_TREADS) { Ability(ABILITY_QUARK_DRIVE); Attack(4); Defense(5); SpAttack(5); SpDefense(5); Speed(5); } + OPPONENT(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); Speed(5); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_ELECTRIC_SURGE); + ABILITY_POPUP(player, ABILITY_QUARK_DRIVE); + MESSAGE("Iron Treads's Defense was heightened!"); + } +} + +SINGLE_BATTLE_TEST("Quark Drive boosts Special Attack 3rd in case of a stat tie") +{ + GIVEN { + PLAYER(SPECIES_IRON_TREADS) { Ability(ABILITY_QUARK_DRIVE); Attack(4); Defense(4); SpAttack(5); SpDefense(5); Speed(5); } + OPPONENT(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); Speed(5); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_ELECTRIC_SURGE); + ABILITY_POPUP(player, ABILITY_QUARK_DRIVE); + MESSAGE("Iron Treads's Sp. Atk was heightened!"); + } +} + +SINGLE_BATTLE_TEST("Quark Drive boosts Special Defense 4th in case of a stat tie") +{ + GIVEN { + PLAYER(SPECIES_IRON_TREADS) { Ability(ABILITY_QUARK_DRIVE); Attack(4); Defense(4); SpAttack(4); SpDefense(5); Speed(5); } + OPPONENT(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); Speed(5); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_ELECTRIC_SURGE); + ABILITY_POPUP(player, ABILITY_QUARK_DRIVE); + MESSAGE("Iron Treads's Sp. Def was heightened!"); + } +} + +SINGLE_BATTLE_TEST("Quark Drive activates in Electric Terrain before Booster Energy") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_IRON_TREADS) { Ability(ABILITY_QUARK_DRIVE); Item(ITEM_BOOSTER_ENERGY); } + OPPONENT(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); } + } WHEN { + TURN { SWITCH(player, 1); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_ELECTRIC_SURGE); + ABILITY_POPUP(player, ABILITY_QUARK_DRIVE); + } THEN { + EXPECT_EQ(player->item, ITEM_BOOSTER_ENERGY); + } +} + +SINGLE_BATTLE_TEST("Quark Drive doesn't activate for a transformed battler") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_IRON_TREADS) { Ability(ABILITY_QUARK_DRIVE); Item(ITEM_BOOSTER_ENERGY); } + OPPONENT(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); Item(ITEM_BOOSTER_ENERGY); } + } WHEN { + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TRANSFORM); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_ELECTRIC_SURGE); + ABILITY_POPUP(player, ABILITY_QUARK_DRIVE); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, opponent); + NOT ABILITY_POPUP(opponent, ABILITY_QUARK_DRIVE); + } THEN { + EXPECT_EQ(player->item, ITEM_BOOSTER_ENERGY); + EXPECT_EQ(opponent->item, ITEM_BOOSTER_ENERGY); + EXPECT_EQ(opponent->ability, ABILITY_QUARK_DRIVE); + } +} From 6f59d26753cc79a9526713d8935cc67b264704c8 Mon Sep 17 00:00:00 2001 From: wiz1989 <80073265+wiz1989@users.noreply.github.com> Date: Fri, 4 Oct 2024 18:42:15 +0200 Subject: [PATCH 33/65] updated Conversion 2 mechanics to 5+ (#5453) * updated Conversion 2 mechanics and added the toggle B_UPDATED_CONVERSION_2 * fixes and added new test cases * bugfixing and added EWRAM u16 gLastUsedMoveType * update after Pawkkie review --------- Co-authored-by: wiz1989 --- include/battle.h | 1 + include/config/battle.h | 1 + src/battle_main.c | 4 + src/battle_script_commands.c | 148 ++++++++++++----- src/data/moves_info.h | 2 +- test/battle/gimmick/terastal.c | 2 + test/battle/move_effect/conversion_2.c | 213 +++++++++++++++++++++++-- 7 files changed, 318 insertions(+), 53 deletions(-) diff --git a/include/battle.h b/include/battle.h index 582dcedfc2..636eb51cdb 100644 --- a/include/battle.h +++ b/include/battle.h @@ -1070,6 +1070,7 @@ extern u16 gLastPrintedMoves[MAX_BATTLERS_COUNT]; extern u16 gLastMoves[MAX_BATTLERS_COUNT]; extern u16 gLastLandedMoves[MAX_BATTLERS_COUNT]; extern u16 gLastHitByType[MAX_BATTLERS_COUNT]; +extern u16 gLastUsedMoveType[MAX_BATTLERS_COUNT]; extern u16 gLastResultingMoves[MAX_BATTLERS_COUNT]; extern u16 gLockedMoves[MAX_BATTLERS_COUNT]; extern u16 gLastUsedMove; diff --git a/include/config/battle.h b/include/config/battle.h index b00eb199a9..fbc41be6ba 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -70,6 +70,7 @@ #define B_RECOIL_IF_MISS_DMG GEN_LATEST // In Gen5+, Jump Kick and High Jump Kick will always do half of the user's max HP when missing. #define B_KLUTZ_FLING_INTERACTION GEN_LATEST // In Gen5+, Pokémon with the Klutz ability can't use Fling. #define B_UPDATED_CONVERSION GEN_LATEST // In Gen6+, Conversion changes the user's type to match their first move's. Before, it would choose a move at random. +#define B_UPDATED_CONVERSION_2 GEN_LATEST // In Gen5+, Conversion 2 changes the user's type to a type that resists the last move used by the selected target. Before, it would consider the last move being successfully hit by. Additionally, Struggle is considered Normal type before Gen 5. #define B_PP_REDUCED_BY_SPITE GEN_LATEST // In Gen4+, Spite reduces the foe's last move's PP by 4, instead of 2 to 5. #define B_EXTRAPOLATED_MOVE_FLAGS TRUE // Adds move flags to moves that they don't officially have but would likely have if they were in the latest core series game. diff --git a/src/battle_main.c b/src/battle_main.c index 3f4f140f65..e0ec3023ec 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -181,6 +181,7 @@ EWRAM_DATA u16 gLastPrintedMoves[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u16 gLastMoves[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u16 gLastLandedMoves[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u16 gLastHitByType[MAX_BATTLERS_COUNT] = {0}; +EWRAM_DATA u16 gLastUsedMoveType[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u16 gLastResultingMoves[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u16 gLockedMoves[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u16 gLastUsedMove = 0; @@ -3029,6 +3030,7 @@ static void BattleStartClearSetData(void) gLastMoves[i] = MOVE_NONE; gLastLandedMoves[i] = MOVE_NONE; gLastHitByType[i] = 0; + gLastUsedMoveType[i] = 0; gLastResultingMoves[i] = MOVE_NONE; gLastHitBy[i] = 0xFF; gLockedMoves[i] = MOVE_NONE; @@ -3207,6 +3209,7 @@ void SwitchInClearSetData(u32 battler) gLastMoves[battler] = MOVE_NONE; gLastLandedMoves[battler] = MOVE_NONE; gLastHitByType[battler] = 0; + gLastUsedMoveType[battler] = 0; gLastResultingMoves[battler] = MOVE_NONE; gLastPrintedMoves[battler] = MOVE_NONE; gLastHitBy[battler] = 0xFF; @@ -3336,6 +3339,7 @@ const u8* FaintClearSetData(u32 battler) gLastMoves[battler] = MOVE_NONE; gLastLandedMoves[battler] = MOVE_NONE; gLastHitByType[battler] = 0; + gLastUsedMoveType[battler] = 0; gLastResultingMoves[battler] = MOVE_NONE; gLastPrintedMoves[battler] = MOVE_NONE; gLastHitBy[battler] = 0xFF; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index f16ee0bf76..ba372d1c54 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5916,12 +5916,14 @@ static void Cmd_moveend(void) gLastMoves[gBattlerAttacker] = gChosenMove; RecordKnownMove(gBattlerAttacker, gChosenMove); gLastResultingMoves[gBattlerAttacker] = gCurrentMove; + GET_MOVE_TYPE(gCurrentMove, gLastUsedMoveType[gBattlerAttacker]); } } else { gLastMoves[gBattlerAttacker] = MOVE_UNAVAILABLE; gLastResultingMoves[gBattlerAttacker] = MOVE_UNAVAILABLE; + gLastUsedMoveType[gBattlerAttacker] = 0; } if (!(gHitMarker & HITMARKER_FAINTED(gBattlerTarget))) @@ -12953,60 +12955,122 @@ static void Cmd_settypetorandomresistance(void) { CMD_ARGS(const u8 *failInstr); - if (gLastLandedMoves[gBattlerAttacker] == MOVE_NONE - || gLastLandedMoves[gBattlerAttacker] == MOVE_UNAVAILABLE) + // Before Gen 5 Conversion 2 only worked on a move the attacker was actually hit by. + // This changed later to the last move used by the selected target. + if (B_UPDATED_CONVERSION_2 < GEN_5) { - gBattlescriptCurrInstr = cmd->failInstr; - } - else if (gBattleMoveEffects[gMovesInfo[gLastLandedMoves[gBattlerAttacker]].effect].twoTurnEffect - && gBattleMons[gLastHitBy[gBattlerAttacker]].status2 & STATUS2_MULTIPLETURNS) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else if (gLastHitByType[gBattlerAttacker] == TYPE_STELLAR || gLastHitByType[gBattlerAttacker] == TYPE_MYSTERY) - { - gBattlescriptCurrInstr = cmd->failInstr; + if (gLastLandedMoves[gBattlerAttacker] == MOVE_NONE + || gLastLandedMoves[gBattlerAttacker] == MOVE_UNAVAILABLE) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (gBattleMoveEffects[gMovesInfo[gLastLandedMoves[gBattlerAttacker]].effect].twoTurnEffect + && gBattleMons[gLastHitBy[gBattlerAttacker]].status2 & STATUS2_MULTIPLETURNS) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (gLastHitByType[gBattlerAttacker] == TYPE_STELLAR || gLastHitByType[gBattlerAttacker] == TYPE_MYSTERY) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else + { + u32 i, resistTypes = 0; + u32 hitByType = gLastHitByType[gBattlerAttacker]; + + for (i = 0; i < NUMBER_OF_MON_TYPES; i++) // Find all types that resist. + { + switch (GetTypeModifier(hitByType, i)) + { + case UQ_4_12(0): + case UQ_4_12(0.5): + resistTypes |= gBitTable[i]; + break; + } + } + + while (resistTypes != 0) + { + i = Random() % NUMBER_OF_MON_TYPES; + if (resistTypes & gBitTable[i]) + { + if (IS_BATTLER_OF_TYPE(gBattlerAttacker, i)) + { + resistTypes &= ~(gBitTable[i]); // Type resists, but the user is already of this type. + } + else + { + SET_BATTLER_TYPE(gBattlerAttacker, i); + PREPARE_TYPE_BUFFER(gBattleTextBuff1, i); + gBattlescriptCurrInstr = cmd->nextInstr; + return; + } + } + } + + gBattlescriptCurrInstr = cmd->failInstr; + } } else { - u32 i, resistTypes = 0; - u32 hitByType = gLastHitByType[gBattlerAttacker]; - - for (i = 0; i < NUMBER_OF_MON_TYPES; i++) // Find all types that resist. + if (gLastResultingMoves[gBattlerTarget] == MOVE_NONE + || gLastResultingMoves[gBattlerTarget] == MOVE_UNAVAILABLE + || gLastResultingMoves[gBattlerTarget] == MOVE_STRUGGLE) { - switch (GetTypeModifier(hitByType, i)) - { - case UQ_4_12(0): - case UQ_4_12(0.5): - resistTypes |= gBitTable[i]; - break; - } + gBattlescriptCurrInstr = cmd->failInstr; } - - while (resistTypes != 0) + else if (IsSemiInvulnerable(gBattlerTarget, gCurrentMove)) { - i = Random() % NUMBER_OF_MON_TYPES; - if (resistTypes & gBitTable[i]) + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (gLastUsedMoveType[gBattlerTarget] == TYPE_NONE || gLastUsedMoveType[gBattlerTarget] == TYPE_STELLAR || gLastUsedMoveType[gBattlerTarget] == TYPE_MYSTERY) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else + { + u32 i, resistTypes = 0; + + for (i = 0; i < NUMBER_OF_MON_TYPES; i++) // Find all types that resist. { - if (IS_BATTLER_OF_TYPE(gBattlerAttacker, i)) + switch (GetTypeModifier(gLastUsedMoveType[gBattlerTarget], i)) { - resistTypes &= ~(gBitTable[i]); // Type resists, but the user is already of this type. - } - else - { - SET_BATTLER_TYPE(gBattlerAttacker, i); - PREPARE_TYPE_BUFFER(gBattleTextBuff1, i); - gBattlescriptCurrInstr = cmd->nextInstr; - return; + case UQ_4_12(0): + case UQ_4_12(0.5): + resistTypes |= gBitTable[i]; + break; } } - } - gBattlescriptCurrInstr = cmd->failInstr; + while (resistTypes != 0) + { + i = Random() % NUMBER_OF_MON_TYPES; + if (resistTypes & gBitTable[i]) + { + if (IS_BATTLER_OF_TYPE(gBattlerAttacker, i)) + { + resistTypes &= ~(gBitTable[i]); // Type resists, but the user is already of this type. + } + else + { + SET_BATTLER_TYPE(gBattlerAttacker, i); + PREPARE_TYPE_BUFFER(gBattleTextBuff1, i); + gBattlescriptCurrInstr = cmd->nextInstr; + return; + } + } + } + + gBattlescriptCurrInstr = cmd->failInstr; + } } } diff --git a/src/data/moves_info.h b/src/data/moves_info.h index b6d0aaf3ca..cb23dcf5af 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -4511,7 +4511,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .type = TYPE_NORMAL, .accuracy = 0, .pp = 30, - .target = MOVE_TARGET_USER, + .target = B_UPDATED_MOVE_DATA >= GEN_5 ? MOVE_TARGET_SELECTED : MOVE_TARGET_USER, .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_RECOVER_HP }, diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index 1b50bc4bcc..0484fcd497 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -509,6 +509,7 @@ SINGLE_BATTLE_TEST("(TERA) Revelation Dance uses a Stellar-type Pokemon's base t } } +#if B_UPDATED_CONVERSION_2 < GEN_5 SINGLE_BATTLE_TEST("(TERA) Conversion2 fails if last hit by a Stellar-type move") { GIVEN { @@ -526,6 +527,7 @@ SINGLE_BATTLE_TEST("(TERA) Conversion2 fails if last hit by a Stellar-type move" MESSAGE("But it failed!"); } } +#endif SINGLE_BATTLE_TEST("(TERA) Roost does not remove Flying-type ground immunity when Terastallized into the Stellar type") { diff --git a/test/battle/move_effect/conversion_2.c b/test/battle/move_effect/conversion_2.c index 2e81212ef1..6c2d4d7f54 100644 --- a/test/battle/move_effect/conversion_2.c +++ b/test/battle/move_effect/conversion_2.c @@ -1,14 +1,207 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Conversion 2 randomly changes the type of the user to a type that resists the last move that hit the user (Gen 3-4)"); -TO_DO_BATTLE_TEST("Conversion 2 randomly changes the type of the user to a type that resists the last move used by the target (Gen 5+)"); -TO_DO_BATTLE_TEST("Conversion 2's type change considers the type of moves called by other moves"); -TO_DO_BATTLE_TEST("Conversion 2's type change considers dynamic type moves"); // Eg. Weather Ball -TO_DO_BATTLE_TEST("Conversion 2's type change considers move types changed by Normalize and Electrify"); -TO_DO_BATTLE_TEST("Conversion 2's type change considers move types changed by Normalize"); -TO_DO_BATTLE_TEST("Conversion 2's type change considers Struggle to be Normal type (Gen 3-4)"); -TO_DO_BATTLE_TEST("Conversion 2 fails if the move used is of typeless damage (Gen 5+)"); -TO_DO_BATTLE_TEST("Conversion 2's type change considers status moves (Gen 5+)"); TO_DO_BATTLE_TEST("Conversion 2's type change considers Inverse Battles"); -TO_DO_BATTLE_TEST("Conversion 2 fails if the move used is Stellar Type"); + +#if B_UPDATED_CONVERSION_2 < GEN_5 +SINGLE_BATTLE_TEST("Conversion 2 randomly changes the type of the user to a type that resists the last move that hit the user (Gen 3-4)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_OMINOUS_WIND); MOVE(opponent, MOVE_CONVERSION_2); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Ominous Wind!"); + // turn 1 + ONE_OF { + MESSAGE("Foe Wobbuffet transformed into the Normal type!"); + MESSAGE("Foe Wobbuffet transformed into the Dark type!"); + } + } +} + +SINGLE_BATTLE_TEST("Conversion 2's type change considers Struggle to be Normal type (Gen 3-4)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_STRUGGLE); } + TURN { MOVE(player, MOVE_CONVERSION_2); } + } SCENE { + // turn 1 + MESSAGE("Foe Wobbuffet used Struggle!"); + // turn 2 + ONE_OF { + MESSAGE("Wobbuffet transformed into the Steel type!"); + MESSAGE("Wobbuffet transformed into the Rock type!"); + MESSAGE("Wobbuffet transformed into the Ghost type!"); + } + } +} +#endif + +#if B_UPDATED_CONVERSION_2 >= GEN_5 +SINGLE_BATTLE_TEST("Conversion 2 randomly changes the type of the user to a type that resists the last used target's move (Gen 5+)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_OMINOUS_WIND); MOVE(opponent, MOVE_CONVERSION_2); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Ominous Wind!"); + // turn 1 + ONE_OF { + MESSAGE("Foe Wobbuffet transformed into the Normal type!"); + MESSAGE("Foe Wobbuffet transformed into the Dark type!"); + } + } +} + +SINGLE_BATTLE_TEST("Conversion 2's type change considers status moves (Gen 5+)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_CURSE); } + TURN { MOVE(player, MOVE_CONVERSION_2); } + } SCENE { + // turn 1 + MESSAGE("Foe Wobbuffet used Curse!"); + // turn 2 + ONE_OF { + MESSAGE("Wobbuffet transformed into the Normal type!"); + MESSAGE("Wobbuffet transformed into the Dark type!"); + } + } +} + +SINGLE_BATTLE_TEST("Conversion 2's type change considers the type of moves called by other moves") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_OMINOUS_WIND); MOVE(opponent, MOVE_MIRROR_MOVE); } + TURN { MOVE(player, MOVE_CONVERSION_2); } + } SCENE { + // turn 1 + MESSAGE("Foe Wobbuffet used Mirror Move!"); + // turn 2 + ONE_OF { + MESSAGE("Wobbuffet transformed into the Normal type!"); + MESSAGE("Wobbuffet transformed into the Dark type!"); + } + } +} + +SINGLE_BATTLE_TEST("Conversion 2's type change considers dynamic type moves") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_HAIL); MOVE(opponent, MOVE_WEATHER_BALL); } + TURN { MOVE(player, MOVE_CONVERSION_2); } + } SCENE { + // turn 1 + MESSAGE("Foe Wobbuffet used Weather Ball!"); + // turn 2 + ONE_OF { + MESSAGE("Wobbuffet transformed into the Steel type!"); + MESSAGE("Wobbuffet transformed into the Fire type!"); + MESSAGE("Wobbuffet transformed into the Water type!"); + MESSAGE("Wobbuffet transformed into the Ice type!"); + } + } +} + +SINGLE_BATTLE_TEST("Conversion 2's type change considers move types changed by Normalize and Electrify") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_NORMALIZE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ELECTRIFY); MOVE(opponent, MOVE_POUND); } + TURN { MOVE(player, MOVE_CONVERSION_2); } + TURN { MOVE(player, MOVE_WATER_GUN); MOVE(opponent, MOVE_CONVERSION_2); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Electrify!"); + MESSAGE("Foe Wobbuffet used Pound!"); + // turn 2 + ONE_OF { + MESSAGE("Wobbuffet transformed into the Ground type!"); + MESSAGE("Wobbuffet transformed into the Dragon type!"); + MESSAGE("Wobbuffet transformed into the Grass type!"); + MESSAGE("Wobbuffet transformed into the Electric type!"); + } + // turn 3 + MESSAGE("Wobbuffet used Water Gun!"); + ONE_OF { + MESSAGE("Foe Wobbuffet transformed into the Steel type!"); + MESSAGE("Foe Wobbuffet transformed into the Rock type!"); + MESSAGE("Foe Wobbuffet transformed into the Ghost type!"); + } + } +} + +SINGLE_BATTLE_TEST("Conversion 2's type change fails targeting Struggle (Gen 5+)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_STRUGGLE); } + TURN { MOVE(player, MOVE_CONVERSION_2); } + } SCENE { + // turn 1 + MESSAGE("Foe Wobbuffet used Struggle!"); + // turn 2 + MESSAGE("Wobbuffet used Conversion 2!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Conversion 2 fails if the move used is of typeless damage (Gen 5+)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ENTEI); + } WHEN { + TURN { MOVE(opponent, MOVE_BURN_UP); } + TURN { MOVE(opponent, MOVE_REVELATION_DANCE); } + TURN { MOVE(player, MOVE_CONVERSION_2); } + } SCENE { + // turn 1 + MESSAGE("Foe Entei used Burn Up!"); + // turn 2 + MESSAGE("Foe Entei used Revelation Dance!"); + // turn 3 + MESSAGE("Wobbuffet used Conversion 2!"); + MESSAGE("But it failed!"); + } +} +#endif + +SINGLE_BATTLE_TEST("Conversion 2 fails if the targeted move is Stellar Type") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_CONVERSION_2); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + // turn 1 + MESSAGE("Foe Wobbuffet used Conversion 2!"); + MESSAGE("But it failed!"); + } +} From 06fefe33186845d3bd851a3bcf18f8eeb7d33346 Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Sun, 6 Oct 2024 15:05:29 -0400 Subject: [PATCH 34/65] Various AI fixes (#5474) * fix AI mirror move check viability call * fix EFFECT_ABSORB missing break in AI_CalcMoveEffectScore --------- Co-authored-by: ghoulslash --- src/battle_ai_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 903b0c7d9f..e41be6971c 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -3302,6 +3302,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_ABSORB: if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT && effectiveness >= AI_EFFECTIVENESS_x1) ADJUST_SCORE(DECENT_EFFECT); + break; case EFFECT_EXPLOSION: case EFFECT_MEMENTO: if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE && gBattleMons[battlerDef].statStages[STAT_EVASION] < 7) @@ -3316,7 +3317,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) break; case EFFECT_MIRROR_MOVE: if (predictedMove != MOVE_NONE) - return AI_CheckViability(battlerAtk, battlerDef, gLastMoves[battlerDef], score); + return AI_CheckViability(battlerAtk, battlerDef, predictedMove, score); break; case EFFECT_ATTACK_UP: case EFFECT_ATTACK_UP_USER_ALLY: From 0dcb28ba0ef07b2fa2bd71c01d7dd755af5b29b2 Mon Sep 17 00:00:00 2001 From: psf <77138753+pkmnsnfrn@users.noreply.github.com> Date: Mon, 7 Oct 2024 08:07:23 -0700 Subject: [PATCH 35/65] Add OW_UNION_DISABLE_CHECK and OW_FLAG_MOVE_UNION_ROOM_CHECK (#5448) * First addition of both checks * Updated config file * fixed spacing * Updated spacing * Fixed issues where feature was always on * Update overworld.h fixed spacing per https://github.com/rh-hideout/pokeemerald-expansion/pull/5448#discussion_r1789647611 --- data/scripts/cable_club.inc | 4 ++++ data/scripts/pkmn_center_nurse.inc | 5 +++++ include/config/overworld.h | 4 +++- include/link.h | 1 + src/link.c | 14 ++++++++++++++ src/union_room.c | 6 ++++++ 6 files changed, 33 insertions(+), 1 deletion(-) diff --git a/data/scripts/cable_club.inc b/data/scripts/cable_club.inc index e810e354a5..9120787ea9 100644 --- a/data/scripts/cable_club.inc +++ b/data/scripts/cable_club.inc @@ -1258,7 +1258,11 @@ EventScript_CloseMossdeepGameCornerBarrier:: CableClub_OnResume: special InitUnionRoom +.if OW_FLAG_MOVE_UNION_ROOM_CHECK != 0 + return +.else end +.endif MossdeepCity_GameCorner_1F_EventScript_InfoMan2:: lock diff --git a/data/scripts/pkmn_center_nurse.inc b/data/scripts/pkmn_center_nurse.inc index 5cef458749..4aca9d768b 100644 --- a/data/scripts/pkmn_center_nurse.inc +++ b/data/scripts/pkmn_center_nurse.inc @@ -36,6 +36,11 @@ EventScript_PkmnCenterNurse_TakeAndHealPkmn:: applymovement VAR_0x800B, Movement_PkmnCenterNurse_Turn @ Changed from Common_Movement_WalkInPlaceFasterLeft to force the follower to enter their Poké Ball waitmovement 0 dofieldeffect FLDEFF_POKECENTER_HEAL +.if OW_UNION_DISABLE_CHECK == FALSE && OW_FLAG_MOVE_UNION_ROOM_CHECK != 0 + setflag OW_FLAG_MOVE_UNION_ROOM_CHECK + call CableClub_OnResume + clearflag OW_FLAG_MOVE_UNION_ROOM_CHECK +.endif waitfieldeffect FLDEFF_POKECENTER_HEAL applymovement VAR_0x800B, Common_Movement_WalkInPlaceFasterDown waitmovement 0 diff --git a/include/config/overworld.h b/include/config/overworld.h index 3298a03209..dd109b7104 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -106,6 +106,8 @@ #define OW_POPUP_BW_ALPHA_BLEND FALSE // Enables alpha blending/transparency for the pop-ups. Mainly intended to be used with the black color option. // Pokémon Center -#define OW_IGNORE_EGGS_ON_HEAL GEN_LATEST // In Gen 4+, the nurse in the Pokémon Center does not heal Eggs on healing machine. +#define OW_IGNORE_EGGS_ON_HEAL GEN_LATEST // In Gen 4+, the nurse in the Pokémon Center does not heal Eggs on healing machine. +#define OW_UNION_DISABLE_CHECK FALSE // When TRUE, the nurse does not inform the player if there is a trainer waiting in the Union Room. This speeds up the loading of the Pokémon Center. +#define OW_FLAG_MOVE_UNION_ROOM_CHECK 0 // If this flag is set, the game will only check if players are in the Union Room while healing Pokémon, and not when players enter the Pokémon Center. This speeds up the loading of the Pokémon Center. This is ignored if OW_UNION_DISABLE_CHECK is TRUE. #endif // GUARD_CONFIG_OVERWORLD_H diff --git a/include/link.h b/include/link.h index 66dd5fecd2..6763065055 100644 --- a/include/link.h +++ b/include/link.h @@ -344,5 +344,6 @@ bool8 DoesLinkPlayerCountMatchSaved(void); void SetCloseLinkCallbackAndType(u16 type); bool32 IsSendingKeysToLink(void); u32 GetLinkRecvQueueLength(void); +bool32 ShouldCheckForUnionRoom(void); #endif // GUARD_LINK_H diff --git a/src/link.c b/src/link.c index f784a36c67..a78e69260a 100644 --- a/src/link.c +++ b/src/link.c @@ -2367,3 +2367,17 @@ void ResetRecvBuffer(void) } } } + +bool32 ShouldCheckForUnionRoom(void) +{ + if (OW_UNION_DISABLE_CHECK) + return FALSE; + + if (OW_FLAG_MOVE_UNION_ROOM_CHECK == 0) + return TRUE; + + if (FlagGet(OW_FLAG_MOVE_UNION_ROOM_CHECK)) + return TRUE; + + return FALSE; +} diff --git a/src/union_room.c b/src/union_room.c index f4ee01d1b4..ac4c61035a 100644 --- a/src/union_room.c +++ b/src/union_room.c @@ -3294,6 +3294,9 @@ void InitUnionRoom(void) { struct WirelessLink_URoom *data; + if (!ShouldCheckForUnionRoom()) + return; + sUnionRoomPlayerName[0] = EOS; CreateTask(Task_InitUnionRoom, 0); sWirelessLinkMain.uRoom = sWirelessLinkMain.uRoom; // Needed to match. @@ -3377,6 +3380,9 @@ static void Task_InitUnionRoom(u8 taskId) bool16 BufferUnionRoomPlayerName(void) { + if (!ShouldCheckForUnionRoom()) + return FALSE; + if (sUnionRoomPlayerName[0] != EOS) { StringCopy(gStringVar1, sUnionRoomPlayerName); From f8f4fc9afd92ce5df431714f4dfa4602dd84dd5b Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Tue, 8 Oct 2024 14:02:31 +0200 Subject: [PATCH 36/65] Removed old checksum matching workaround (#5480) Co-authored-by: Hedara --- .../rayquaza_scene/scene_3/rayquaza_tail.png | Bin 264 -> 268 bytes graphics_file_rules.mk | 4 ---- src/data/graphics/rayquaza_scene.h | 4 +--- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/graphics/rayquaza_scene/scene_3/rayquaza_tail.png b/graphics/rayquaza_scene/scene_3/rayquaza_tail.png index 7e0577df15569122e3974c00edaa5557dc66d8ab..796548e56a83ff88119681c857cea9acb8eff7db 100644 GIT binary patch delta 194 zcmV;z06qVR0*nHX7Z-pC0{{R3HA@)~00001b5ch_0Itp)>5(Bne~L*&K~zYI?UKm~ z!!Qg)wG7CY*8l&vRmdS)a%pKo3x3CkH4J8muEUIoIf`M7EQSjU0u1G0Nic92PjC@m zlnY{o#7q|=Gd1H>%khXTxCuxVnnTY*+D$FeZlI4@#Iu!8HwI{|V%zt~Jbp}fq4v4> w75->aZ9!BQM7jByAo$9rZkW_!u~@v#3z9qq5*qa2o&W#<07*qoM6N<$g7!vFSpWb4 delta 190 zcmV;v073tZ0*C^T7Yc|30{{R39O!m@kuE=flSxEDR5*=eU?35cGYCQ$6@`vaMx$c^ zm{B5P*bZfgcz}fpT|^)vh0YQXHNsBf5M9DbA0P}ysCf)5hEN70oZ$pxI71aPI14}- z!U|A^h(3$~)5c;8Rn1}tw*#uJ#MvH9vx~IrgB?^1WSA3RPz%s5=-?s701Gh&5dm~5 s1|ocoU<@aiL9k#YE9geSC>Uq}09~Uhl5dDBXaE2J07*qoM6N<$f^IrL!T> $@ - $(RAYQUAZAGFXDIR)/scene_4/streaks.4bpp: %.4bpp: %.png $(GFX) $< $@ -num_tiles 19 -Wnum_tiles diff --git a/src/data/graphics/rayquaza_scene.h b/src/data/graphics/rayquaza_scene.h index 660f07416c..e999e947fb 100644 --- a/src/data/graphics/rayquaza_scene.h +++ b/src/data/graphics/rayquaza_scene.h @@ -24,9 +24,7 @@ const u32 gRaySceneTakesFlight_Bg_Tilemap[] = INCBIN_U32("graphics/rayquaz // Scene 3 (RAY_ANIM_DESCENDS) const u32 gRaySceneDescends_Rayquaza_Gfx[] = INCBIN_U32("graphics/rayquaza_scene/scene_3/rayquaza.4bpp.lz"); -// for some reason there are an extra 0xC bytes at the end of the original rayquaza_tail.4bpp, so in order to produce the correct lz, -// we have to cat the bytes at the end with a make rule. not sure why those bytes are there, it may have been a bug in Game Freak's software. -const u32 gRaySceneDescends_RayquazaTail_Gfx[] = INCBIN_U32("graphics/rayquaza_scene/scene_3/rayquaza_tail_fix.4bpp.lz"); +const u32 gRaySceneDescends_RayquazaTail_Gfx[] = INCBIN_U32("graphics/rayquaza_scene/scene_3/rayquaza_tail.4bpp.lz"); const u32 gRaySceneDescends_Bg_Gfx[] = INCBIN_U32("graphics/rayquaza_scene/scene_3/bg.4bpp.lz"); const u32 gRaySceneDescends_Light_Gfx[] = INCBIN_U32("graphics/rayquaza_scene/scene_3/light.4bpp.lz"); // uses pal 2 of gRaySceneDescends_Bg_Pal const u32 gRaySceneDescends_Bg_Pal[] = INCBIN_U32("graphics/rayquaza_scene/scene_3/bg.gbapal.lz"); From efad9a32a972bf53f6a81b3d5ae8694c358a4625 Mon Sep 17 00:00:00 2001 From: Pawkkie <61265402+Pawkkie@users.noreply.github.com> Date: Tue, 8 Oct 2024 13:04:35 -0400 Subject: [PATCH 37/65] Fix AI tests using PASSES_RANDOMLY (#5486) --- include/test/battle.h | 14 ++++- test/battle/ai/ai_switching.c | 14 +---- test/test_runner_battle.c | 105 +++++++++++++++++----------------- 3 files changed, 65 insertions(+), 68 deletions(-) diff --git a/include/test/battle.h b/include/test/battle.h index c28410948d..041ebc1c72 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -645,6 +645,15 @@ struct AILogLine s16 score; }; +// Data which is updated by the test runner during a battle and needs to +// be reset between trials. +struct BattleTrialData +{ + u8 lastActionTurn; + u8 queuedEvent; + u8 aiActionsPlayed[MAX_BATTLERS_COUNT]; +}; + struct BattleTestData { u8 stack[BATTLE_TEST_STACK_SIZE]; @@ -676,20 +685,19 @@ struct BattleTestData u8 battleRecordSourceLineOffsets[MAX_BATTLERS_COUNT][BATTLER_RECORD_SIZE]; u16 recordIndexes[MAX_BATTLERS_COUNT]; struct BattlerTurn battleRecordTurns[MAX_TURNS][MAX_BATTLERS_COUNT]; - u8 lastActionTurn; u8 queuedEventsCount; u8 queueGroupType; u8 queueGroupStart; - u8 queuedEvent; struct QueuedEvent queuedEvents[MAX_QUEUED_EVENTS]; u8 expectedAiActionIndex[MAX_BATTLERS_COUNT]; - u8 aiActionsPlayed[MAX_BATTLERS_COUNT]; struct ExpectedAIAction expectedAiActions[MAX_BATTLERS_COUNT][MAX_EXPECTED_ACTIONS]; struct ExpectedAiScore expectedAiScores[MAX_BATTLERS_COUNT][MAX_TURNS][MAX_AI_SCORE_COMPARISION_PER_TURN]; // Max 4 comparisions per turn struct AILogLine aiLogLines[MAX_BATTLERS_COUNT][MAX_MON_MOVES][MAX_AI_LOG_LINES]; u8 aiLogPrintedForMove[MAX_BATTLERS_COUNT]; // Marks ai score log as printed for move, so the same log isn't displayed multiple times. u16 flagId; + + struct BattleTrialData trial; }; struct BattleTestRunnerState diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c index 3e97ec1f61..59a6d88493 100644 --- a/test/battle/ai/ai_switching.c +++ b/test/battle/ai/ai_switching.c @@ -378,28 +378,18 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if mon would AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if it can't deal damage to a mon with Wonder Guard 66% of the time") { - u32 aiOmniscientFlag = 0; - PARAMETRIZE { aiOmniscientFlag = 0; } - PARAMETRIZE { aiOmniscientFlag = AI_FLAG_OMNISCIENT; } PASSES_RANDOMLY(66, 100, RNG_AI_SWITCH_WONDER_GUARD); GIVEN { ASSUME(gSpeciesInfo[SPECIES_SHEDINJA].types[0] == TYPE_BUG); ASSUME(gSpeciesInfo[SPECIES_SHEDINJA].types[1] == TYPE_GHOST); ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL); ASSUME(gMovesInfo[MOVE_SHADOW_BALL].type == TYPE_GHOST); - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiOmniscientFlag); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_SHEDINJA) { Moves(MOVE_TACKLE); } OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_SHADOW_BALL); } } WHEN { - if(aiOmniscientFlag == 0) { - TURN { MOVE(player, MOVE_TACKLE) ; EXPECT_MOVE(opponent, MOVE_TACKLE); } - TURN { MOVE(player, MOVE_TACKLE) ; EXPECT_SWITCH(opponent, 1); } - } - else { - TURN { MOVE(player, MOVE_TACKLE) ; EXPECT_SWITCH(opponent, 1); } - } - + TURN { MOVE(player, MOVE_TACKLE) ; EXPECT_SWITCH(opponent, 1); } } } diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 6f318bbead..c8e4496030 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -567,19 +567,19 @@ void TestRunner_Battle_RecordAbilityPopUp(u32 battlerId, u32 ability) s32 match; struct QueuedEvent *event; - if (DATA.queuedEvent == DATA.queuedEventsCount) + if (DATA.trial.queuedEvent == DATA.queuedEventsCount) return; - event = &DATA.queuedEvents[DATA.queuedEvent]; + event = &DATA.queuedEvents[DATA.trial.queuedEvent]; switch (event->groupType) { case QUEUE_GROUP_NONE: case QUEUE_GROUP_ONE_OF: - if (TryAbilityPopUp(DATA.queuedEvent, event->groupSize, battlerId, ability) != -1) - DATA.queuedEvent += event->groupSize; + if (TryAbilityPopUp(DATA.trial.queuedEvent, event->groupSize, battlerId, ability) != -1) + DATA.trial.queuedEvent += event->groupSize; break; case QUEUE_GROUP_NONE_OF: - queuedEvent = DATA.queuedEvent; + queuedEvent = DATA.trial.queuedEvent; do { if ((match = TryAbilityPopUp(queuedEvent, event->groupSize, battlerId, ability)) != -1) @@ -598,7 +598,7 @@ void TestRunner_Battle_RecordAbilityPopUp(u32 battlerId, u32 ability) continue; if (TryAbilityPopUp(queuedEvent, event->groupSize, battlerId, ability) != -1) - DATA.queuedEvent = queuedEvent + event->groupSize; + DATA.trial.queuedEvent = queuedEvent + event->groupSize; } while (FALSE); break; } @@ -630,19 +630,19 @@ void TestRunner_Battle_RecordAnimation(u32 animType, u32 animId) s32 match; struct QueuedEvent *event; - if (DATA.queuedEvent == DATA.queuedEventsCount) + if (DATA.trial.queuedEvent == DATA.queuedEventsCount) return; - event = &DATA.queuedEvents[DATA.queuedEvent]; + event = &DATA.queuedEvents[DATA.trial.queuedEvent]; switch (event->groupType) { case QUEUE_GROUP_NONE: case QUEUE_GROUP_ONE_OF: - if (TryAnimation(DATA.queuedEvent, event->groupSize, animType, animId) != -1) - DATA.queuedEvent += event->groupSize; + if (TryAnimation(DATA.trial.queuedEvent, event->groupSize, animType, animId) != -1) + DATA.trial.queuedEvent += event->groupSize; break; case QUEUE_GROUP_NONE_OF: - queuedEvent = DATA.queuedEvent; + queuedEvent = DATA.trial.queuedEvent; do { if ((match = TryAnimation(queuedEvent, event->groupSize, animType, animId)) != -1) @@ -661,7 +661,7 @@ void TestRunner_Battle_RecordAnimation(u32 animType, u32 animId) continue; if (TryAnimation(queuedEvent, event->groupSize, animType, animId) != -1) - DATA.queuedEvent = queuedEvent + event->groupSize; + DATA.trial.queuedEvent = queuedEvent + event->groupSize; } while (FALSE); break; } @@ -720,19 +720,19 @@ void TestRunner_Battle_RecordHP(u32 battlerId, u32 oldHP, u32 newHP) s32 match; struct QueuedEvent *event; - if (DATA.queuedEvent == DATA.queuedEventsCount) + if (DATA.trial.queuedEvent == DATA.queuedEventsCount) return; - event = &DATA.queuedEvents[DATA.queuedEvent]; + event = &DATA.queuedEvents[DATA.trial.queuedEvent]; switch (event->groupType) { case QUEUE_GROUP_NONE: case QUEUE_GROUP_ONE_OF: - if (TryHP(DATA.queuedEvent, event->groupSize, battlerId, oldHP, newHP) != -1) - DATA.queuedEvent += event->groupSize; + if (TryHP(DATA.trial.queuedEvent, event->groupSize, battlerId, oldHP, newHP) != -1) + DATA.trial.queuedEvent += event->groupSize; break; case QUEUE_GROUP_NONE_OF: - queuedEvent = DATA.queuedEvent; + queuedEvent = DATA.trial.queuedEvent; do { if ((match = TryHP(queuedEvent, event->groupSize, battlerId, oldHP, newHP)) != -1) @@ -751,7 +751,7 @@ void TestRunner_Battle_RecordHP(u32 battlerId, u32 oldHP, u32 newHP) continue; if (TryHP(queuedEvent, event->groupSize, battlerId, oldHP, newHP) != -1) - DATA.queuedEvent = queuedEvent + event->groupSize; + DATA.trial.queuedEvent = queuedEvent + event->groupSize; } while (FALSE); break; } @@ -782,7 +782,7 @@ static u32 CountAiExpectMoves(struct ExpectedAIAction *expectedAction, u32 battl void TestRunner_Battle_CheckChosenMove(u32 battlerId, u32 moveId, u32 target) { const char *filename = gTestRunnerState.test->filename; - u32 id = DATA.aiActionsPlayed[battlerId]; + u32 id = DATA.trial.aiActionsPlayed[battlerId]; struct ExpectedAIAction *expectedAction = &DATA.expectedAiActions[battlerId][id]; if (!expectedAction->actionSet) @@ -845,13 +845,13 @@ void TestRunner_Battle_CheckChosenMove(u32 battlerId, u32 moveId, u32 target) } // Turn passed, clear logs from the turn ClearAiLog(battlerId); - DATA.aiActionsPlayed[battlerId]++; + DATA.trial.aiActionsPlayed[battlerId]++; } void TestRunner_Battle_CheckSwitch(u32 battlerId, u32 partyIndex) { const char *filename = gTestRunnerState.test->filename; - u32 id = DATA.aiActionsPlayed[battlerId]; + u32 id = DATA.trial.aiActionsPlayed[battlerId]; struct ExpectedAIAction *expectedAction = &DATA.expectedAiActions[battlerId][id]; if (!expectedAction->actionSet) @@ -865,7 +865,7 @@ void TestRunner_Battle_CheckSwitch(u32 battlerId, u32 partyIndex) if (expectedAction->target != partyIndex) Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Expected partyIndex %d, got %d", filename, expectedAction->sourceLine, expectedAction->target, partyIndex); } - DATA.aiActionsPlayed[battlerId]++; + DATA.trial.aiActionsPlayed[battlerId]++; } void TestRunner_Battle_InvalidNoHPMon(u32 battlerId, u32 partyIndex) @@ -1029,7 +1029,7 @@ void TestRunner_Battle_CheckAiMoveScores(u32 battlerId) } // We need to make sure that the expected move has the best score. We have to rule out a situation where the expected move is used, but it has the same number of points as some other moves. - aiAction = &DATA.expectedAiActions[battlerId][DATA.aiActionsPlayed[battlerId]]; + aiAction = &DATA.expectedAiActions[battlerId][DATA.trial.aiActionsPlayed[battlerId]]; if (aiAction->actionSet && !aiAction->pass) { s32 target = aiAction->target; @@ -1102,19 +1102,19 @@ void TestRunner_Battle_RecordExp(u32 battlerId, u32 oldExp, u32 newExp) s32 match; struct QueuedEvent *event; - if (DATA.queuedEvent == DATA.queuedEventsCount) + if (DATA.trial.queuedEvent == DATA.queuedEventsCount) return; - event = &DATA.queuedEvents[DATA.queuedEvent]; + event = &DATA.queuedEvents[DATA.trial.queuedEvent]; switch (event->groupType) { case QUEUE_GROUP_NONE: case QUEUE_GROUP_ONE_OF: - if (TryExp(DATA.queuedEvent, event->groupSize, battlerId, oldExp, newExp) != -1) - DATA.queuedEvent += event->groupSize; + if (TryExp(DATA.trial.queuedEvent, event->groupSize, battlerId, oldExp, newExp) != -1) + DATA.trial.queuedEvent += event->groupSize; break; case QUEUE_GROUP_NONE_OF: - queuedEvent = DATA.queuedEvent; + queuedEvent = DATA.trial.queuedEvent; do { if ((match = TryExp(queuedEvent, event->groupSize, battlerId, oldExp, newExp)) != -1) @@ -1133,7 +1133,7 @@ void TestRunner_Battle_RecordExp(u32 battlerId, u32 oldExp, u32 newExp) continue; if (TryExp(queuedEvent, event->groupSize, battlerId, oldExp, newExp) != -1) - DATA.queuedEvent = queuedEvent + event->groupSize; + DATA.trial.queuedEvent = queuedEvent + event->groupSize; } while (FALSE); break; } @@ -1191,19 +1191,19 @@ void TestRunner_Battle_RecordMessage(const u8 *string) s32 match; struct QueuedEvent *event; - if (DATA.queuedEvent == DATA.queuedEventsCount) + if (DATA.trial.queuedEvent == DATA.queuedEventsCount) return; - event = &DATA.queuedEvents[DATA.queuedEvent]; + event = &DATA.queuedEvents[DATA.trial.queuedEvent]; switch (event->groupType) { case QUEUE_GROUP_NONE: case QUEUE_GROUP_ONE_OF: - if (TryMessage(DATA.queuedEvent, event->groupSize, string) != -1) - DATA.queuedEvent += event->groupSize; + if (TryMessage(DATA.trial.queuedEvent, event->groupSize, string) != -1) + DATA.trial.queuedEvent += event->groupSize; break; case QUEUE_GROUP_NONE_OF: - queuedEvent = DATA.queuedEvent; + queuedEvent = DATA.trial.queuedEvent; do { if ((match = TryMessage(queuedEvent, event->groupSize, string)) != -1) @@ -1222,7 +1222,7 @@ void TestRunner_Battle_RecordMessage(const u8 *string) continue; if (TryMessage(queuedEvent, event->groupSize, string) != -1) - DATA.queuedEvent = queuedEvent + event->groupSize; + DATA.trial.queuedEvent = queuedEvent + event->groupSize; } while (FALSE); break; } @@ -1256,19 +1256,19 @@ void TestRunner_Battle_RecordStatus1(u32 battlerId, u32 status1) s32 match; struct QueuedEvent *event; - if (DATA.queuedEvent == DATA.queuedEventsCount) + if (DATA.trial.queuedEvent == DATA.queuedEventsCount) return; - event = &DATA.queuedEvents[DATA.queuedEvent]; + event = &DATA.queuedEvents[DATA.trial.queuedEvent]; switch (event->groupType) { case QUEUE_GROUP_NONE: case QUEUE_GROUP_ONE_OF: - if (TryStatus(DATA.queuedEvent, event->groupSize, battlerId, status1) != -1) - DATA.queuedEvent += event->groupSize; + if (TryStatus(DATA.trial.queuedEvent, event->groupSize, battlerId, status1) != -1) + DATA.trial.queuedEvent += event->groupSize; break; case QUEUE_GROUP_NONE_OF: - queuedEvent = DATA.queuedEvent; + queuedEvent = DATA.trial.queuedEvent; do { if ((match = TryStatus(queuedEvent, event->groupSize, battlerId, status1)) != -1) @@ -1287,7 +1287,7 @@ void TestRunner_Battle_RecordStatus1(u32 battlerId, u32 status1) continue; if (TryStatus(queuedEvent, event->groupSize, battlerId, status1) != -1) - DATA.queuedEvent = queuedEvent + event->groupSize; + DATA.trial.queuedEvent = queuedEvent + event->groupSize; } while (FALSE); break; } @@ -1307,22 +1307,22 @@ void TestRunner_Battle_AfterLastTurn(void) { const struct BattleTest *test = GetBattleTest(); - if (DATA.turns - 1 != DATA.lastActionTurn) + if (DATA.turns - 1 != DATA.trial.lastActionTurn) { const char *filename = gTestRunnerState.test->filename; - Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: %d TURNs specified, but %d ran", filename, SourceLine(0), DATA.turns, DATA.lastActionTurn + 1); + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: %d TURNs specified, but %d ran", filename, SourceLine(0), DATA.turns, DATA.trial.lastActionTurn + 1); } - while (DATA.queuedEvent < DATA.queuedEventsCount - && DATA.queuedEvents[DATA.queuedEvent].groupType == QUEUE_GROUP_NONE_OF) + while (DATA.trial.queuedEvent < DATA.queuedEventsCount + && DATA.queuedEvents[DATA.trial.queuedEvent].groupType == QUEUE_GROUP_NONE_OF) { - DATA.queuedEvent += DATA.queuedEvents[DATA.queuedEvent].groupSize; + DATA.trial.queuedEvent += DATA.queuedEvents[DATA.trial.queuedEvent].groupSize; } - if (DATA.queuedEvent != DATA.queuedEventsCount) + if (DATA.trial.queuedEvent != DATA.queuedEventsCount) { const char *filename = gTestRunnerState.test->filename; - u32 line = SourceLine(DATA.queuedEvents[DATA.queuedEvent].sourceLineOffset); - const char *macro = sEventTypeMacros[DATA.queuedEvents[DATA.queuedEvent].type]; + u32 line = SourceLine(DATA.queuedEvents[DATA.trial.queuedEvent].sourceLineOffset); + const char *macro = sEventTypeMacros[DATA.queuedEvents[DATA.trial.queuedEvent].type]; Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Unmatched %s", filename, line, macro); } @@ -1395,8 +1395,7 @@ static void CB2_BattleTest_NextTrial(void) PrintTestName(); gTestRunnerState.result = TEST_RESULT_PASS; DATA.recordedBattle.rngSeed = MakeRngValue(STATE->runTrial); - DATA.queuedEvent = 0; - DATA.lastActionTurn = 0; + memset(&DATA.trial, 0, sizeof(DATA.trial)); SetVariablesForRecordedBattle(&DATA.recordedBattle); SetMainCallback2(CB2_InitBattle); } @@ -1892,7 +1891,7 @@ void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordInde if (DATA.battleRecordTypes[battlerId][recordIndex] != RECORDED_BYTE) { - DATA.lastActionTurn = gBattleResults.battleTurnCounter; + DATA.trial.lastActionTurn = gBattleResults.battleTurnCounter; if (actionType != DATA.battleRecordTypes[battlerId][recordIndex]) { @@ -1926,7 +1925,7 @@ void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordInde } else { - if (DATA.lastActionTurn == gBattleResults.battleTurnCounter) + if (DATA.trial.lastActionTurn == gBattleResults.battleTurnCounter) { const char *filename = gTestRunnerState.test->filename; Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: TURN %d incomplete", filename, SourceLine(0), gBattleResults.battleTurnCounter + 1); From a76f7fe92e960039071109731f5e9f2721f106d7 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Tue, 8 Oct 2024 14:45:09 -0300 Subject: [PATCH 38/65] Version 1.9.3 (#5485) * Version 1.9.3 * Updated to latest master --- .../ISSUE_TEMPLATE/01_battle_engine_bugs.yaml | 3 +- .../ISSUE_TEMPLATE/02_battle_ai_issues.yaml | 3 +- .github/ISSUE_TEMPLATE/04_other_errors.yaml | 3 +- README.md | 4 +- docs/SUMMARY.md | 32 ++- docs/changelogs/1.9.x/1.9.3.md | 106 ++++++++++ docs/team_procedures/expansion_versions.md | 187 ++++++++++++++++++ docs/{ => tutorials}/ai_flags.md | 0 docs/{ => tutorials}/ai_logic.md | 0 .../how_to_battle_script_command_macro.md | 0 docs/{ => tutorials}/how_to_new_move.md | 0 .../how_to_new_pokemon_1_6_0.md | 0 .../how_to_new_pokemon_1_7_0.md | 0 .../how_to_new_pokemon_1_8_0.md | 0 .../how_to_new_pokemon_1_9_0.md | 0 docs/{ => tutorials}/how_to_testing_system.md | 0 docs/{ => tutorials}/how_to_trainer_class.md | 0 include/constants/expansion.h | 4 +- src/party_menu.c | 2 +- 19 files changed, 317 insertions(+), 27 deletions(-) create mode 100644 docs/changelogs/1.9.x/1.9.3.md create mode 100644 docs/team_procedures/expansion_versions.md rename docs/{ => tutorials}/ai_flags.md (100%) rename docs/{ => tutorials}/ai_logic.md (100%) rename docs/{ => tutorials}/how_to_battle_script_command_macro.md (100%) rename docs/{ => tutorials}/how_to_new_move.md (100%) rename docs/{ => tutorials}/how_to_new_pokemon_1_6_0.md (100%) rename docs/{ => tutorials}/how_to_new_pokemon_1_7_0.md (100%) rename docs/{ => tutorials}/how_to_new_pokemon_1_8_0.md (100%) rename docs/{ => tutorials}/how_to_new_pokemon_1_9_0.md (100%) rename docs/{ => tutorials}/how_to_testing_system.md (100%) rename docs/{ => tutorials}/how_to_trainer_class.md (100%) diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml index 9ffdc70fba..8240a56801 100644 --- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml +++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml @@ -23,9 +23,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.9.2 (Latest release) + - 1.9.3 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.9.2 - 1.9.1 - 1.9.0 - 1.8.6 diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml index ce50b0bffd..5688f8a7fb 100644 --- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml +++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml @@ -23,9 +23,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.9.2 (Latest release) + - 1.9.3 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.9.2 - 1.9.1 - 1.9.0 - 1.8.6 diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml index c78093d60e..add0633d95 100644 --- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml +++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml @@ -23,9 +23,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.9.2 (Latest release) + - 1.9.3 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.9.2 - 1.9.1 - 1.9.0 - 1.8.6 diff --git a/README.md b/README.md index 37ea8336b7..427caafd0a 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ pokeemerald-expansion is a decomp hack base project based off pret's [pokeemeral If you use pokeemerald-expansion in your hack, please add RHH (Rom Hacking Hideout) to your credits list. Optionally, you can list the version used, so it can help players know what features to expect. You can phrase it as the following: ``` -Based off RHH's pokeemerald-expansion 1.9.2 https://github.com/rh-hideout/pokeemerald-expansion/ +Based off RHH's pokeemerald-expansion 1.9.3 https://github.com/rh-hideout/pokeemerald-expansion/ ``` ## What features are included? @@ -178,7 +178,7 @@ With this, you'll get the latest version of pokeemerald-expansion, plus a couple - Check your current version. - You can check in the debug menu's `Utilities -> Expansion Version` option. - If the option is not available, you possibly have version 1.6.2 or older. In that case, please check the [changelogs](CHANGELOG.md) to determine your version based on the features available on your repository. -- Once you have your remote set up, run the command `git pull RHH expansion/X.Y.Z`, replacing X, Y and Z with the digits of the respective version you want to update to (eg, to update to 1.9.2, use `git pull RHH expansion/1.9.2`). +- Once you have your remote set up, run the command `git pull RHH expansion/X.Y.Z`, replacing X, Y and Z with the digits of the respective version you want to update to (eg, to update to 1.9.3, use `git pull RHH expansion/1.9.3`). - ***Important:*** If you are several versions behind, we recommend updating one minor version at a time, skipping directly to the latest patch version (eg, 1.5.3 -> 1.6.2 -> 1.7.4 and so on) - Alternatively, you can update to unreleased versions of the expansion. - ***master (stable):*** It contains unreleased **bugfixes** that will come in the next patch version. To merge, use `git pull RHH master`. diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index faeb069783..ac4698fdc3 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -5,20 +5,21 @@ - [Setting up WSL1 (Legacy Portion)](./legacy_WSL1_INSTALL.md) - [Run documentation site locally](local_mdbook/index.md) - [Ubuntu WSL1/WSL2](local_mdbook/ubuntu_WSL.md) -- [AI Flags](./ai_flags.md) - [Tutorials]() - - [How to add new AI Flags](./ai_logic.md) - - [How to add new battle script commands/macros](./how_to_battle_script_command_macro.md) - - [How to add a new move](./how_to_new_move.md) - - [How to add a new trainer class](./how_to_trainer_class.md) + - [What are AI Flags?](tutorials/ai_flags.md) + - [How to add new AI Flags](tutorials/ai_logic.md) + - [How to add new battle script commands/macros](tutorials/how_to_battle_script_command_macro.md) + - [How to add a new move](tutorials/how_to_new_move.md) + - [How to add a new trainer class](tutorials/how_to_trainer_class.md) - [How to add a new Pokémon]() - - [v1.9.x](./how_to_new_pokemon_1_9_0.md) - - [v1.8.x](./how_to_new_pokemon_1_8_0.md) - - [v1.7.x](./how_to_new_pokemon_1_7_0.md) - - [v1.6.x](./how_to_new_pokemon_1_6_0.md) - - [How to use the Testing System](./how_to_testing_system.md) + - [v1.9.x](tutorials/how_to_new_pokemon_1_9_0.md) + - [v1.8.x](tutorials/how_to_new_pokemon_1_8_0.md) + - [v1.7.x](tutorials/how_to_new_pokemon_1_7_0.md) + - [v1.6.x](tutorials/how_to_new_pokemon_1_6_0.md) + - [How to use the Testing System](tutorials/how_to_testing_system.md) - [Changelog](./CHANGELOG.md) - [1.9.x]() + - [Version 1.9.3](changelogs/1.9.x/1.9.3.md) - [Version 1.9.2](changelogs/1.9.x/1.9.2.md) - [Version 1.9.1](changelogs/1.9.x/1.9.1.md) - [Version 1.9.0](changelogs/1.9.x/1.9.0.md) @@ -30,43 +31,36 @@ - [Version 1.8.2](changelogs/1.8.x/1.8.2.md) - [Version 1.8.1](changelogs/1.8.x/1.8.1.md) - [Version 1.8.0](changelogs/1.8.x/1.8.0.md) - - [1.7.x]() - [Version 1.7.4](changelogs/1.7.x/1.7.4.md) - [Version 1.7.3](changelogs/1.7.x/1.7.3.md) - [Version 1.7.2](changelogs/1.7.x/1.7.2.md) - [Version 1.7.1](changelogs/1.7.x/1.7.1.md) - [Version 1.7.0](changelogs/1.7.x/1.7.0.md) - - [1.6.x]() - [Version 1.6.2](changelogs/1.6.x/1.6.2.md) - [Version 1.6.1](changelogs/1.6.x/1.6.1.md) - [Version 1.6.0](changelogs/1.6.x/1.6.0.md) - - [1.5.x]() - [Version 1.5.3](changelogs/1.5.x/1.5.3.md) - [Version 1.5.2](changelogs/1.5.x/1.5.2.md) - [Version 1.5.1](changelogs/1.5.x/1.5.1.md) - [Version 1.5.0](changelogs/1.5.x/1.5.0.md) - - [1.4.x]() - [Version 1.4.3](changelogs/1.4.x/1.4.3.md) - [Version 1.4.2](changelogs/1.4.x/1.4.2.md) - [Version 1.4.1](changelogs/1.4.x/1.4.1.md) - [Version 1.4.0](changelogs/1.4.x/1.4.0.md) - - [1.3.x]() - [Version 1.3.0](changelogs/1.3.x/1.3.0.md) - - [1.2.x]() - [Version 1.2.0](changelogs/1.2.x/1.2.0.md) - - [1.1.x]() - [Version 1.1.1](changelogs/1.1.x/1.1.1.md) - [Version 1.1.0](changelogs/1.1.x/1.1.0.md) - - [1.0.x]() - [Version 1.0.0](changelogs/1.0.x/1.0.0.md) - - [Pre-1.0.x]() - [Version 0.9.0](changelogs/0.9.x/0.9.0.md) +- [Team Procedures]() + - [How to make an Expansion version](team_procedures/expansion_versions.md) diff --git a/docs/changelogs/1.9.x/1.9.3.md b/docs/changelogs/1.9.x/1.9.3.md new file mode 100644 index 0000000000..2fe981435e --- /dev/null +++ b/docs/changelogs/1.9.x/1.9.3.md @@ -0,0 +1,106 @@ +# Version 1.9.3 + +```md +## How to update +- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`. +- Once you have your remote set up, run the command `git pull RHH expansion/1.9.3`. +``` + +## 🌋 *REFACTORS* 🌋 +📜 = Uses a migration script. +* Converted `settotemboost` command to `callnative` in [#5418](https://github.com/rh-hideout/pokeemerald-expansion/pull/5418) +* Removed unused `RESOURCE_FLAG_TRACED` in [#5430](https://github.com/rh-hideout/pokeemerald-expansion/pull/5430) +* Changed `MOVEEND_` defines to an enum in [#5449](https://github.com/rh-hideout/pokeemerald-expansion/pull/5449) + +## ✨ Feature Branches ✨ +### ***merrp/aarant's Followers*** +#### Fixed +* Fixed Expansion-exclusive issue that caused trainers to not play their "pointing" animation when Followers were out during battle intro by @kittenchilly in [#5406](https://github.com/rh-hideout/pokeemerald-expansion/pull/5406) + +## ⚔️ Battle General ⚔️ ## +### Changed +* Improved Mega evolution animation to make it a little smoother by @kleenxfeu in [#4816](https://github.com/rh-hideout/pokeemerald-expansion/pull/4816) +### Fixed +* Fixed affection check for exp multiplier by @Bassoonian in [#5421](https://github.com/rh-hideout/pokeemerald-expansion/pull/5421) +* Fixed multiple Primal Reversions not occurring if multiple battlers fainted on the previous turn by @PhallenTree in [#5430](https://github.com/rh-hideout/pokeemerald-expansion/pull/5430) + +## 🤹 Moves 🤹 +### Added +* Added missing `B_AFTER_YOU_TURN_ORDER` config by @PhallenTree in [#5400](https://github.com/rh-hideout/pokeemerald-expansion/pull/5400) + * Gen 5-7: After You fails if the order remains the same after using After You. + * Gen 8+: After You no longer fails if the turn order remains the same after using After You. +* Added missing `B_QUASH_TURN_ORDER` config by @PhallenTree in [#5400](https://github.com/rh-hideout/pokeemerald-expansion/pull/5400) + * Gen 5-7: If multiple Pokémon are affected by Quash, they move in the order they were affected by Quash. + * Gen 8+: If multiple Pokémon are affected by Quash, they now move fastest to slowest. +* Added missing updated `B_UPDATED_CONVERSION_2` by @wiz1989 in [#5453](https://github.com/rh-hideout/pokeemerald-expansion/pull/5453) + * Gens 2-4: Conversion 2 changes the user's type to a type that is resistant/immune to the last move the user was hit by. + * Gen 5+: Conversion 2 changes the user's type to a type that resists the last move used by the selected target. +### Fixed +* Fixed Scale Shot corrupting the move used on the next turn by @AlexOn1ine in [#5397](https://github.com/rh-hideout/pokeemerald-expansion/pull/5397) +* Fixed Growth's description not being updated based on `B_GROWTH_STAT_RAISE` by @nescioquid in [#5398](https://github.com/rh-hideout/pokeemerald-expansion/pull/5398) +* Fixed Quash not updating the battlers' actions correctly by @PhallenTree in [#5400](https://github.com/rh-hideout/pokeemerald-expansion/pull/5400) + * Cleanup by @PhallenTree in [#5430](https://github.com/rh-hideout/pokeemerald-expansion/pull/5430) +* Fixed Snatched Swallow not recovering HP if the Snatcher is not under the effect of Stockpile (should still heal) by @PhallenTree in [#5430](https://github.com/rh-hideout/pokeemerald-expansion/pull/5430) +* Fixed Counter users being damaged by Spiky Shield by @AlexOn1ine in [#5402](https://github.com/rh-hideout/pokeemerald-expansion/pull/5402) +* Fixed Electrified Dragon Darts not correctly avoiding targets with ability immunity (Volt Absorb, Motor Drive) by @PhallenTree in [#5430](https://github.com/rh-hideout/pokeemerald-expansion/pull/5430) +* Fixed Trace not activating a switch-in ability it traces (eg. Intimidate) by @PhallenTree in [#5430](https://github.com/rh-hideout/pokeemerald-expansion/pull/5430) + * Removed unused `RESOURCE_FLAG_TRACED`. +* Fixed recoil damage not triggering healing berries by @AlexOn1ine in [#5449](https://github.com/rh-hideout/pokeemerald-expansion/pull/5449) + * Also changed `MOVEEND_` defines to an enum. +## 🎭 Abilities 🎭 +### Fixed +* Fixed Dancer activating even if the dance move is stolen by Snatch by @PhallenTree in [#5430](https://github.com/rh-hideout/pokeemerald-expansion/pull/5430) +* Fixed Ability popup when multiple Pokémon faint at the same time by @PhallenTree in [#5430](https://github.com/rh-hideout/pokeemerald-expansion/pull/5430) +* Multiple ability fixes by @PhallenTree in [#5447](https://github.com/rh-hideout/pokeemerald-expansion/pull/5447) + * Fixed Protosynthesis/Quark Drive sometimes not activating ability popup despite still gaining the boost as they should. + * Fixed Protosynthesis/Quark Drive activating on Transformed battlers. + * Fixed Protosynthesis activating despite Cloud Nine being present on the field. + * Fixed Quark Drive not activating if the battler is not grounded. + * Fixed Protosynthesis/Quark Drive/Beast Boost stat raise priority when multiple stats are tied for the highest one. + * Before: `Attack, Defense, Speed, Special Attack, Special Defense`. + * After: `Attack, Defense, Special Attack, Special Defense, Speed`. + +## 🧶 Items 🧶 +### Fixed +* Fixed Ogerpon's Masks not increasing the power of moves by 20% by @AlexOn1ine in [#5391](https://github.com/rh-hideout/pokeemerald-expansion/pull/5391) +* Fixed Jubilife Muffin not working by @kittenchilly in [#5444](https://github.com/rh-hideout/pokeemerald-expansion/pull/5444) +* Fixed duplicating flute bug in double battles by @ghoulslash in [#5436](https://github.com/rh-hideout/pokeemerald-expansion/pull/5436) + +## 🤖 Battle AI 🤖 +### Fixed +* Fixed Trick/Switcheroo giving AI score even if the opponent has no held item by @kittenchilly in [#5412](https://github.com/rh-hideout/pokeemerald-expansion/pull/5412) +* Various AI fixes in `AI_CalcMoveEffectScore` by @ghoulslash in [#5474](https://github.com/rh-hideout/pokeemerald-expansion/pull/5474) + * Missing break from `EFFECT_ABSORB` switch case. + * Using last used move for Mirror Move instead of predicted move. + +## 🧹 Other Cleanup 🧹 +### Changed +* Converted `settotemboost` command to `callnative` by @ghoulslash in [#5418](https://github.com/rh-hideout/pokeemerald-expansion/pull/5418) +* Removed trailing whitespace by @kittenchilly in [#5455](https://github.com/rh-hideout/pokeemerald-expansion/pull/5455) +* Removed binary match workaround for Rayquaza's tail in Sootopolis' cutscene by @hedara90 in https://github.com/rh-hideout/pokeemerald-expansion/pull/5480 +### Fixed +* Fixed potential uninitialized behavior in `ChangeOrderTargetAfterAttacker` by @AlexOn1ine in [#5393](https://github.com/rh-hideout/pokeemerald-expansion/pull/5393) +* Fallback on default BW map pop-up theme to reduce potential for error by @ravepossum in [#5392](https://github.com/rh-hideout/pokeemerald-expansion/pull/5392) +* Multiple typo fixes by @nescioquid in [#5398](https://github.com/rh-hideout/pokeemerald-expansion/pull/5398) +* VS Seeker documentation fix by @Bassoonian in [#5415](https://github.com/rh-hideout/pokeemerald-expansion/pull/5415) + +## 🧪 Test Runner 🧪 +### Added +* Added missing After You and Quash tests by @PhallenTree in [#5400](https://github.com/rh-hideout/pokeemerald-expansion/pull/5400) +### Changed +* Improved Tangling Hair test to make sure that chained effects do not overwrite relevant battler IDs by @ghoulslash in [#5423](https://github.com/rh-hideout/pokeemerald-expansion/pull/5423) +* Improved Full Heal item tests by @kittenchilly in [#5444](https://github.com/rh-hideout/pokeemerald-expansion/pull/5444) +### Fixed +* Fixed Wake-Up Slap test typo by @Pawkkie in [#5442](https://github.com/rh-hideout/pokeemerald-expansion/pull/5442) +* Fixed test assumption fail summary fields using more memory than needed by @AsparagusEduardo in [#5443](https://github.com/rh-hideout/pokeemerald-expansion/pull/5443) +* Fixed issue with `PASSES_RANDOMLY` in AI tests by @Pawkkie in https://github.com/rh-hideout/pokeemerald-expansion/pull/5486 + +## New Contributors +* @nescioquid made their first contribution in [#5398](https://github.com/rh-hideout/pokeemerald-expansion/pull/5398) +* @kleenxfeu made their first contribution in [#4816](https://github.com/rh-hideout/pokeemerald-expansion/pull/4816) +* @wiz1989 made their first contribution in [#5453](https://github.com/rh-hideout/pokeemerald-expansion/pull/5453) + +**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.9.2...expansion/1.9.3 + + + diff --git a/docs/team_procedures/expansion_versions.md b/docs/team_procedures/expansion_versions.md new file mode 100644 index 0000000000..b53631bfa5 --- /dev/null +++ b/docs/team_procedures/expansion_versions.md @@ -0,0 +1,187 @@ +# How to make an Expansion version + +*If you need instructions on how to release a new Expansion version, check out the enclosed instruction book. - AsparagusEduardo* + +## 1.- Autogenerating a changelog for the `master` branch. + *Requires Write access to the repo.* + +If the changelog you're making is for a minor version (Eg. 1.3.0), make sure to sync the `upcoming` branch with `master` before starting. Keep in mind that if there are unreleased changes in `master`, they should be made into a patch version released alongside minor version. +- Go to https://github.com/rh-hideout/pokeemerald-expansion/releases. +- Press the option "Draft a new release". +- Fill the following fields: + - In "Choose a tag", write the name of the tag for the desired release number. + - Eg. for version 1.2.3, write `expansion/1.2.3`. ***It cannot be an existing tag.*** + - In "Target" choose `master`. + - In "Previous tag", select the latest version released. +- Press "Generate release notes". This will fill the description with a list of PRs made to the `master` branch since the lastest release. +- You may copy the contents to your editor of choice for step 2. +- If the changelog you're making is for a minor version (Eg. 1.3.0), proceed to step 1B, without closing the current window. + +## 1B.- Autogenerating a changelog for the `upcoming` branch. +- Delete the text and title generated in the site. +- Change "Target" to `upcoming`. +- Press "Generate release notes". This will autogenerate the description with changes from both branches, as they've been merged. +- Use a comparison tool to remove any shared PRs from `master`. (I use [WinMerge](https://winmerge.org/) for this). + - Also remove any shared "New Contributor" entries. +- You should now have a text that only contains PRs made to `upcoming`. + +## 2.- Sorting PRs in the changelog +- Copy the `docs/changelogs/template.md` file into a new file with the corresponding version for file and folder. Eg, for 1.2.3, you copy the file into `docs/changelogs/1.2.x/1.2.3.md`. +- Use the following Regex to adapt all PR links into the format currently used: + - Search + ``` + in https://github\.com/rh-hideout/pokeemerald-expansion/pull/(\d+) + ``` + - Replace + ``` + in [#$1](https://github.com/rh-hideout/pokeemerald-expansion/pull/$1) + ``` +- At the bottom of the template, replace the "____" in `` with the number of the last PR in your autogenerated changelog. This will help you keep track of any new PRs that are merged in before you're finished with your changelog. +- Sort the PRs by the categories present in the PR taking the following considerations: + - Keep the language of the changelog as newbie friendly as possible, explaining how the PR affects them. + - The format of the main line is the following: + ``` + {{Description of the change}} by @{{GitHub user}} in {{PR link}} + ``` + - If a PR reverted by a different PR, both should be removed. + - If the reverted PR's contents are then re-added it should be treated as if it's the first time added, with all the proper documentation needed. + - If a PR's content fits into multiple categories, like fixing two unrelated Ability and Move effects, you may duplicate the PR line and put it in both categories, changing the description of it accordingly. + - When refering to an element present in the code, always enclose them with ` to easily tell them apart. Eg: + - "Converted `settotemboost` command to `callnative`" + - "Changed `MOVEEND_` defines to an enum" + - A PR's technical changes that may conflict with user changes should have an entry in the "**REFACTORS**" section at the beginning of the changelog. Any of these changes that requires a migration script should also be marked as such by appending the corresponding emoji listed there. The section should include, but it's not limited to: + - Massive data format and/or structuring changes. + - Converting defines to enums. + - Renaming structs and/or their fields. + - Removing enums/defines/structs. + - For fixes: + - Once on their respective categories, if a PR fixes multiple issues at once in that category, you may make a sublist of those issues to properly detail each one and avoid just writing "Fixed multiple ability issues" or an overly verbose sentence for the PR. + - Try to fit the contents of a PR as a single sentence in past tense that. + - ***DON'T*** write something like "Fixed Parental Bond", as this doesn't explain *what* was fixed in Parental Bond. Instead, write something like "Fixed Parental Bond not affecting Snore". + - For new configs: + - Add the name of the config as part of the sentence, with subitems explaining what they do based on their generation if it's a generational config. Eg: + ``` + * Added missing `B_AFTER_YOU_TURN_ORDER` config by @PhallenTree in [#5400](https://github.com/rh-hideout/pokeemerald-expansion/pull/5400) + * Gen 5-7: After You fails if the order remains the same after using After You. + * Gen 8+: After You no longer fails if the turn order remains the same after using After You. + ``` + - For other new features: + - Explain how to use the feature completely, listing all its features as needed. For now, this is the only source of documentation for these features, so it's important to be thorough. + +# 3.- Preparing the code for the new version +- Search for all non-changelog instances of the previous released version and update them to the newer version. As of writing, it's these files: + - `README.md`: + ```diff + -Based off RHH's pokeemerald-expansion 1.2.2 https://github.com/rh-hideout/pokeemerald-expansion/ + +Based off RHH's pokeemerald-expansion 1.2.3 https://github.com/rh-hideout/pokeemerald-expansion/ + ``` + ```diff + -- Once you have your remote set up, run the command `git pull RHH expansion/X.Y.Z`, replacing X, Y and Z with the digits of the respective version you want to update to (eg, to update to 1.2.2, use `git pull RHH expansion/1.2.2`). + +- Once you have your remote set up, run the command `git pull RHH expansion/X.Y.Z`, replacing X, Y and Z with the digits of the respective version you want to update to (eg, to update to 1.2.3, use `git pull RHH expansion/1.2.3`). + ``` + - `.github/ISSUE_TEMPLATE/*.yaml` + - Patch version: + ```diff + options: + - 1.2.2 (Latest release) + + 1.2.3 (Latest release) + master (default, unreleased bugfixes) + upcoming (Edge) + + 1.2.2 + 1.2.1 + 1.2.0 + ``` + - Minor Version: + ```diff + options: + - 1.2.2 (Latest release) + + 1.2.3 (Latest release) + master (default, unreleased bugfixes) + upcoming (Edge) + + 1.2.2 + 1.2.1 + 1.2.0 + - 1.1.6 + - 1.1.5 + - 1.1.4 + - 1.1.3 + - 1.1.2 + - 1.1.1 + - 1.1.0 + - pre-1.1.0 + + pre-1.2.0 + ``` + - `docs/SUMMARY.md`: + - Patch version: + ```diff + [Changelog](./CHANGELOG.md) + - [1.2.x]() + + - [Version 1.2.3](changelogs/1.2.x/1.2.3.md) + - [Version 1.2.2](changelogs/1.2.x/1.2.2.md) + - [Version 1.2.1](changelogs/1.2.x/1.2.1.md) + - [Version 1.2.0](changelogs/1.2.x/1.2.0.md) + ``` + - Minor version: + ```diff + [Changelog](./CHANGELOG.md) + + - [1.3.x]() + + - [Version 1.3.0](changelogs/1.3.x/1.3.0.md) + - [1.2.x]() + - [Version 1.2.2](changelogs/1.2.x/1.2.2.md) + - [Version 1.2.1](changelogs/1.2.x/1.2.1.md) + - [Version 1.2.0](changelogs/1.2.x/1.2.0.md) + ``` + - `include/constants/expansion.h`: + - The defines should already have the version we're targetting as part of the previous changelog process. + ```diff + -// Last version: 1.2.2 + +// Last version: 1.2.3 + #define EXPANSION_VERSION_MAJOR 1 + #define EXPANSION_VERSION_MINOR 2 + #define EXPANSION_VERSION_PATCH 3 + + // FALSE if this this version of Expansion is not a tagged commit, i.e. + // it contains unreleased changes. + -#define EXPANSION_TAGGED_RELEASE FALSE + +#define EXPANSION_TAGGED_RELEASE TRUE + ``` +# 4.- Make a PR with the changes listed in Steps 2/3. +- Also post the changelog for feedback from contributors and maintainers. +- Once all feedback has been addresses, you may merge. +# 5.- Create the release on GitHub +- Copy the contents of your manual changelog into the description of your release. +- Modify the title to "Version {{version}}", eg. "Version 1.2.3" +- Be sure to have the "Set as the latest release" option set, +- Press "Publish Release". +- This will create a new tag in the repo that users can pull from like they can with branches. +# 6.- Post-release handling +- ***DON'T ACCEPT ANY NEW PRs YET.*** We have to prepare for the next cycle first. +- Go to `include/constants/expansion.h` and merge the following changes to the repo (PR or direct commit, doesn't matter much): + - Patch version: + ```diff + // Last version: 1.2.3 + #define EXPANSION_VERSION_MAJOR 1 + #define EXPANSION_VERSION_MINOR 2 + - #define EXPANSION_VERSION_PATCH 3 + + #define EXPANSION_VERSION_PATCH 4 + + // FALSE if this this version of Expansion is not a tagged commit, i.e. + // it contains unreleased changes. + -#define EXPANSION_TAGGED_RELEASE TRUE + +#define EXPANSION_TAGGED_RELEASE FALSE + ``` +With this, the repo is ready again to receive new PRs. + +# Now you're ready to make the announcements! +- We tend to post on the following Discord Servers: + - [RH-Hideout](https://discord.gg/6CzjAG6GZk) + - Requires role to post in #announcements channel. + - [Team Aqua's Hideout](https://discord.gg/team-aqua-s-hideout-976252009114140682) + - Requires role to post in #romhacking-updates channel. + - [What a Hack!](https://discord.gg/whack-a-hack-292436944670162955) + - Announcements are done in Spanish, but not the changelogs themselves. + - Requires role to ping "Decompilaciones" role. + - [pret](https://discord.gg/R4c3FA95dP) + - [PokéCommunity](https://discord.gg/pokecommunity) + +You can make a highlight of changes in the announcement itself, but it's not needed. (I also like using Discord emotes to highlight certain features during announcements, but gain, it's not needed). diff --git a/docs/ai_flags.md b/docs/tutorials/ai_flags.md similarity index 100% rename from docs/ai_flags.md rename to docs/tutorials/ai_flags.md diff --git a/docs/ai_logic.md b/docs/tutorials/ai_logic.md similarity index 100% rename from docs/ai_logic.md rename to docs/tutorials/ai_logic.md diff --git a/docs/how_to_battle_script_command_macro.md b/docs/tutorials/how_to_battle_script_command_macro.md similarity index 100% rename from docs/how_to_battle_script_command_macro.md rename to docs/tutorials/how_to_battle_script_command_macro.md diff --git a/docs/how_to_new_move.md b/docs/tutorials/how_to_new_move.md similarity index 100% rename from docs/how_to_new_move.md rename to docs/tutorials/how_to_new_move.md diff --git a/docs/how_to_new_pokemon_1_6_0.md b/docs/tutorials/how_to_new_pokemon_1_6_0.md similarity index 100% rename from docs/how_to_new_pokemon_1_6_0.md rename to docs/tutorials/how_to_new_pokemon_1_6_0.md diff --git a/docs/how_to_new_pokemon_1_7_0.md b/docs/tutorials/how_to_new_pokemon_1_7_0.md similarity index 100% rename from docs/how_to_new_pokemon_1_7_0.md rename to docs/tutorials/how_to_new_pokemon_1_7_0.md diff --git a/docs/how_to_new_pokemon_1_8_0.md b/docs/tutorials/how_to_new_pokemon_1_8_0.md similarity index 100% rename from docs/how_to_new_pokemon_1_8_0.md rename to docs/tutorials/how_to_new_pokemon_1_8_0.md diff --git a/docs/how_to_new_pokemon_1_9_0.md b/docs/tutorials/how_to_new_pokemon_1_9_0.md similarity index 100% rename from docs/how_to_new_pokemon_1_9_0.md rename to docs/tutorials/how_to_new_pokemon_1_9_0.md diff --git a/docs/how_to_testing_system.md b/docs/tutorials/how_to_testing_system.md similarity index 100% rename from docs/how_to_testing_system.md rename to docs/tutorials/how_to_testing_system.md diff --git a/docs/how_to_trainer_class.md b/docs/tutorials/how_to_trainer_class.md similarity index 100% rename from docs/how_to_trainer_class.md rename to docs/tutorials/how_to_trainer_class.md diff --git a/include/constants/expansion.h b/include/constants/expansion.h index 8a86282619..f59976a1cd 100644 --- a/include/constants/expansion.h +++ b/include/constants/expansion.h @@ -1,13 +1,13 @@ #ifndef GUARD_CONSTANTS_EXPANSION_H #define GUARD_CONSTANTS_EXPANSION_H -// Last version: 1.9.2 +// Last version: 1.9.3 #define EXPANSION_VERSION_MAJOR 1 #define EXPANSION_VERSION_MINOR 9 #define EXPANSION_VERSION_PATCH 3 // FALSE if this this version of Expansion is not a tagged commit, i.e. // it contains unreleased changes. -#define EXPANSION_TAGGED_RELEASE FALSE +#define EXPANSION_TAGGED_RELEASE TRUE #endif diff --git a/src/party_menu.c b/src/party_menu.c index 3fef465804..b4c973b601 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -4672,7 +4672,7 @@ void ItemUseCB_Medicine(u8 taskId, TaskFunc task) if (canHeal == TRUE) { if (hp == 0) - AnimatePartySlot(gPartyMenu.slotId, 1); + AnimatePartySlot(gPartyMenu.slotId, 1); PartyMenuModifyHP(taskId, gPartyMenu.slotId, 1, GetMonData(mon, MON_DATA_HP) - hp, Task_DisplayHPRestoredMessage); ResetHPTaskData(taskId, 0, hp); return; From 541ca2619b2f86e2367e322d9c781c3015ae1d62 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Tue, 8 Oct 2024 14:47:32 -0300 Subject: [PATCH 39/65] Start 1.9.4 cycle --- include/constants/expansion.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/constants/expansion.h b/include/constants/expansion.h index f59976a1cd..44b1169bcd 100644 --- a/include/constants/expansion.h +++ b/include/constants/expansion.h @@ -4,10 +4,10 @@ // Last version: 1.9.3 #define EXPANSION_VERSION_MAJOR 1 #define EXPANSION_VERSION_MINOR 9 -#define EXPANSION_VERSION_PATCH 3 +#define EXPANSION_VERSION_PATCH 4 // FALSE if this this version of Expansion is not a tagged commit, i.e. // it contains unreleased changes. -#define EXPANSION_TAGGED_RELEASE TRUE +#define EXPANSION_TAGGED_RELEASE FALSE #endif From feeccb9198c8396c64c4b75f55c136169824df83 Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Wed, 9 Oct 2024 03:38:21 -0400 Subject: [PATCH 40/65] Simplify BS_FAINTED_MULTIPLE_1 double battle logic in openpartyscreen (#5435) * simplify BS_FAINTED_MULTIPLE_1 doubles logic * simplify openpartyscreen BS_FAINTED_MULTIPLE_2 checks, too * Update src/battle_script_commands.c Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> * Update src/battle_script_commands.c Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> * Update src/battle_script_commands.c Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> * more readable OpponentHandleChoosePokemon * Update src/battle_controller_opponent.c * Update src/battle_controller_opponent.c --------- Co-authored-by: ghoulslash Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- src/battle_controller_opponent.c | 30 ++--- src/battle_script_commands.c | 194 +++++++++---------------------- 2 files changed, 69 insertions(+), 155 deletions(-) diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 57ebccf7fa..01ec74f9c2 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -661,7 +661,7 @@ static void OpponentHandleChoosePokemon(u32 battler) chosenMonId = gSelectedMonPartyId = GetFirstFaintedPartyIndex(battler); } // Switching out - else if (*(gBattleStruct->AI_monToSwitchIntoId + battler) == PARTY_SIZE) + else if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE) { chosenMonId = GetMostSuitableMonToSwitchInto(battler, TRUE); if (chosenMonId == PARTY_SIZE) @@ -680,27 +680,27 @@ static void OpponentHandleChoosePokemon(u32 battler) } GetAIPartyIndexes(battler, &firstId, &lastId); - for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--) { - if (IsValidForBattle(&gEnemyParty[chosenMonId]) - && chosenMonId != gBattlerPartyIndexes[battler1] - && chosenMonId != gBattlerPartyIndexes[battler2] - && (!(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_ACE_POKEMON) - || chosenMonId != CalculateEnemyPartyCount() - 1 - || CountAIAliveNonEggMonsExcept(PARTY_SIZE) == pokemonInBattle)) - { - break; - } + if (!IsValidForBattle(&gEnemyParty[chosenMonId])) + continue; + if (chosenMonId == gBattlerPartyIndexes[battler1] + || chosenMonId == gBattlerPartyIndexes[battler2]) + continue; + if ((AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_ACE_POKEMON) + && ((chosenMonId != CalculateEnemyPartyCount() - 1) || CountAIAliveNonEggMonsExcept(PARTY_SIZE) == pokemonInBattle)) + continue; + // mon is valid + break; } } - *(gBattleStruct->monToSwitchIntoId + battler) = chosenMonId; + gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; } else { - chosenMonId = *(gBattleStruct->AI_monToSwitchIntoId + battler); - *(gBattleStruct->AI_monToSwitchIntoId + battler) = PARTY_SIZE; - *(gBattleStruct->monToSwitchIntoId + battler) = chosenMonId; + chosenMonId = gBattleStruct->AI_monToSwitchIntoId[battler]; + gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE; + gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; } #if TESTING TestRunner_Battle_CheckSwitch(battler, chosenMonId); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 5c7be23e7d..da1a8754e1 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6997,126 +6997,53 @@ static void Cmd_openpartyscreen(void) } else if (IsDoubleBattle()) { - bool8 hasReplacement_0, hasReplacement_1, hasReplacement_2, hasReplacement_3; - + bool32 hasReplacement; + hitmarkerFaintBits = gHitMarker >> 28; - - if (1u & hitmarkerFaintBits) + for (i = 0; i < gBattlersCount; i++) { - battler = 0; - if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE)) + if (((1u << i) & hitmarkerFaintBits)) { - gAbsentBattlerFlags |= 1u << battler; - gHitMarker &= ~HITMARKER_FAINTED(battler); - BtlController_EmitCantSwitch(battler, BUFFER_A); - MarkBattlerForControllerExec(battler); - } - else if (!gSpecialStatuses[battler].faintedHasReplacement) - { - ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[2]); - gSpecialStatuses[battler].faintedHasReplacement = TRUE; - } - else - { - BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); - MarkBattlerForControllerExec(battler); - flags |= 1; + if (i > 1 && ((1u << BATTLE_PARTNER(i)) & hitmarkerFaintBits)) + continue; + + battler = i; + if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE)) + { + gAbsentBattlerFlags |= 1u << battler; + gHitMarker &= ~HITMARKER_FAINTED(battler); + BtlController_EmitCantSwitch(battler, BUFFER_A); + MarkBattlerForControllerExec(battler); + } + else if (!gSpecialStatuses[battler].faintedHasReplacement) + { + ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[BATTLE_PARTNER(battler)]); + gSpecialStatuses[battler].faintedHasReplacement = TRUE; + } + else if (battler < 2 || (battler > 1 && !(flags & BATTLE_PARTNER(battler)))) + { + BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); + MarkBattlerForControllerExec(battler); + flags |= battler; + } } } - if (4u & hitmarkerFaintBits && !(1u & hitmarkerFaintBits)) + + for (i = 0; i < NUM_BATTLE_SIDES; i++) { - battler = 2; - if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE)) + if (!(gSpecialStatuses[i].faintedHasReplacement)) { - gAbsentBattlerFlags |= 1u << battler; - gHitMarker &= ~HITMARKER_FAINTED(battler); - BtlController_EmitCantSwitch(battler, BUFFER_A); - MarkBattlerForControllerExec(battler); - } - else if (!gSpecialStatuses[battler].faintedHasReplacement) - { - ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[0]); - gSpecialStatuses[battler].faintedHasReplacement = TRUE; - } - else if (!(flags & 1)) - { - BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); - MarkBattlerForControllerExec(battler); - } - } - if (2 & hitmarkerFaintBits) - { - battler = 1; - if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE)) - { - gAbsentBattlerFlags |= 1u << battler; - gHitMarker &= ~HITMARKER_FAINTED(battler); - BtlController_EmitCantSwitch(battler, BUFFER_A); - MarkBattlerForControllerExec(battler); - } - else if (!gSpecialStatuses[battler].faintedHasReplacement) - { - ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[3]); - gSpecialStatuses[battler].faintedHasReplacement = TRUE; - } - else - { - BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); - MarkBattlerForControllerExec(battler); - flags |= 2; - } - } - if (8 & hitmarkerFaintBits && !(2 & hitmarkerFaintBits)) - { - battler = 3; - if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE)) - { - gAbsentBattlerFlags |= 1u << battler; - gHitMarker &= ~HITMARKER_FAINTED(battler); - BtlController_EmitCantSwitch(battler, BUFFER_A); - MarkBattlerForControllerExec(battler); - } - else if (!gSpecialStatuses[battler].faintedHasReplacement) - { - ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[1]); - gSpecialStatuses[battler].faintedHasReplacement = TRUE; - } - else if (!(flags & 2)) - { - BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); - MarkBattlerForControllerExec(battler); - } - } + hasReplacement = gSpecialStatuses[BATTLE_PARTNER(i)].faintedHasReplacement; + if (!hasReplacement && hitmarkerFaintBits != 0) + { + if (gAbsentBattlerFlags & (1 << i)) + battler = BATTLE_PARTNER(i); + else + battler = i; - hasReplacement_0 = gSpecialStatuses[0].faintedHasReplacement; - if (!hasReplacement_0) - { - hasReplacement_2 = gSpecialStatuses[2].faintedHasReplacement; - if (!hasReplacement_2 && hitmarkerFaintBits != 0) - { - if (gAbsentBattlerFlags & 1) - battler = 2; - else - battler = 0; - - BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); - MarkBattlerForControllerExec(battler); - } - - } - hasReplacement_1 = gSpecialStatuses[1].faintedHasReplacement; - if (!hasReplacement_1) - { - hasReplacement_3 = gSpecialStatuses[3].faintedHasReplacement; - if (!hasReplacement_3 && hitmarkerFaintBits != 0) - { - if (gAbsentBattlerFlags & 2) - battler = 3; - else - battler = 1; - - BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); - MarkBattlerForControllerExec(battler); + BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); + MarkBattlerForControllerExec(battler); + } } } } @@ -7129,36 +7056,23 @@ static void Cmd_openpartyscreen(void) if (IsDoubleBattle()) { hitmarkerFaintBits = gHitMarker >> 28; - if (4 & hitmarkerFaintBits && 1 & hitmarkerFaintBits) + for (i = 0; i < NUM_BATTLE_SIDES; i++) { - battler = 2; - if (HasNoMonsToSwitch(battler, gBattleResources->bufferB[0][1], PARTY_SIZE)) + if ((1 << BATTLE_PARTNER(i)) & hitmarkerFaintBits && (1 << i) & hitmarkerFaintBits) { - gAbsentBattlerFlags |= 1u << battler; - gHitMarker &= ~HITMARKER_FAINTED(battler); - BtlController_EmitCantSwitch(battler, BUFFER_A); - MarkBattlerForControllerExec(battler); - } - else if (!gSpecialStatuses[battler].faintedHasReplacement) - { - ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[0]); - gSpecialStatuses[battler].faintedHasReplacement = TRUE; - } - } - if (8u & hitmarkerFaintBits && hitmarkerFaintBits & 2u) - { - battler = 3; - if (HasNoMonsToSwitch(battler, gBattleResources->bufferB[1][1], PARTY_SIZE)) - { - gAbsentBattlerFlags |= 1u << battler; - gHitMarker &= ~HITMARKER_FAINTED(battler); - BtlController_EmitCantSwitch(battler, BUFFER_A); - MarkBattlerForControllerExec(battler); - } - else if (!gSpecialStatuses[battler].faintedHasReplacement) - { - ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[1]); - gSpecialStatuses[battler].faintedHasReplacement = TRUE; + battler = BATTLE_PARTNER(i); + if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE)) + { + gAbsentBattlerFlags |= (1u << battler); + gHitMarker &= ~(HITMARKER_FAINTED(battler)); + BtlController_EmitCantSwitch(battler, BUFFER_A); + MarkBattlerForControllerExec(battler); + } + else if (!gSpecialStatuses[battler].faintedHasReplacement) + { + ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[i]); + gSpecialStatuses[battler].faintedHasReplacement = TRUE; + } } } gBattlescriptCurrInstr = cmd->nextInstr; From 7fc5502afd583c3ce404d281413b303dfef72e88 Mon Sep 17 00:00:00 2001 From: Pawkkie <61265402+Pawkkie@users.noreply.github.com> Date: Wed, 9 Oct 2024 03:38:49 -0400 Subject: [PATCH 41/65] Don't clear move data on turn end (#5488) --- src/battle_util.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 2e463eaf58..b572acc7c1 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -738,8 +738,6 @@ void HandleAction_ActionFinished(void) gMoveResultFlags = 0; gBattleScripting.animTurn = 0; gBattleScripting.animTargetsHit = 0; - gLastLandedMoves[gBattlerAttacker] = 0; - gLastHitByType[gBattlerAttacker] = 0; gBattleStruct->dynamicMoveType = 0; gBattleScripting.moveendState = 0; gBattleCommunication[3] = 0; From 708f64247f8abbe22d5cc6f44ede30b333dd030c Mon Sep 17 00:00:00 2001 From: SonikkuA-DatH <58025603+SonikkuA-DatH@users.noreply.github.com> Date: Wed, 9 Oct 2024 01:43:04 -0700 Subject: [PATCH 42/65] Heart Swap Move Animation (#5460) --- data/battle_anim_scripts.s | 38 +++++++ graphics/battle_anims/sprites/pinkvio_orb.png | Bin 0 -> 5019 bytes include/constants/battle_anim.h | 1 + include/graphics.h | 2 + src/battle_anim_effects_2.c | 12 +++ src/battle_anim_psychic.c | 100 +++++++++++++++++- src/data/battle_anim.h | 2 + src/graphics.c | 3 + 8 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 graphics/battle_anims/sprites/pinkvio_orb.png diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 98ac1bfda7..c16a5c7859 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -934,6 +934,44 @@ gBattleAnimMove_ToxicSpikes:: end gBattleAnimMove_HeartSwap:: + loadspritegfx ANIM_TAG_RED_HEART + loadspritegfx ANIM_TAG_PINKVIO_ORB + loadspritegfx ANIM_TAG_SPARKLE_2 + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 0, 8, RGB(31, 24, 26) + createvisualtask AnimTask_HeartSwap, 3, ANIM_TARGET + createvisualtask AnimTask_BlendMonInAndOut, 5, ANIM_TARGET, RGB_WHITE, 12, 3, 1 + loopsewithpan SE_M_TAIL_WHIP, SOUND_PAN_ATTACKER, 10, 8 + delay 16 + createvisualtask AnimTask_HeartSwap, 3, ANIM_ATTACKER + createvisualtask AnimTask_BlendMonInAndOut, 5, ANIM_ATTACKER, RGB_WHITE, 12, 3, 1 + waitforvisualfinish + createsprite gGrantingStarsSpriteTemplate, ANIM_ATTACKER, 2, -15, 0, 0, 0, 32, 60 + createsprite gGrantingStarsSpriteTemplate, ANIM_TARGET, 2, -15, 0, 0, 0, 32, 60 + delay 8 + createsprite gGrantingStarsSpriteTemplate, ANIM_ATTACKER, 2, 12, -5, 0, 0, 32, 60 + createsprite gGrantingStarsSpriteTemplate, ANIM_TARGET, 2, 12, -5, 0, 0, 32, 60 + delay 4 + playsewithpan SE_SHINY, SOUND_PAN_ATTACKER + createvisualtask AnimTask_BlendMonInAndOut, 5, ANIM_ATTACKER, RGB(31, 25, 27), 12, 3, 1 + createvisualtask AnimTask_ScaleMonAndRestore, 5, -3, -3, 16, ANIM_ATTACKER, 0 + createvisualtask AnimTask_BlendMonInAndOut, 5, ANIM_TARGET, RGB(31, 25, 27), 12, 3, 1 + createvisualtask AnimTask_ScaleMonAndRestore, 5, -3, -3, 16, ANIM_TARGET, 0 + createsprite gRedHeartBurstSpriteTemplate, ANIM_TARGET, 3, 160, -32 + createsprite gRedHeartBurstSpriteTemplate, ANIM_TARGET, 3, -256, -40 + createsprite gRedHeartBurstSpriteTemplate, ANIM_TARGET, 3, 128, -16 + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 8, 0, RGB(31, 24, 26) + createsprite gRedHeartCharmSpriteTemplate, ANIM_ATTACKER, 3, 0, 20 + playsewithpan SE_M_MORNING_SUN, SOUND_PAN_ATTACKER + delay 15 + createsprite gRedHeartCharmSpriteTemplate, ANIM_ATTACKER, 3, -20, 20 + playsewithpan SE_M_CHARM, SOUND_PAN_ATTACKER + delay 15 + createsprite gRedHeartCharmSpriteTemplate, ANIM_ATTACKER, 3, 20, 20 + playsewithpan SE_M_CHARM, SOUND_PAN_ATTACKER + waitforvisualfinish + clearmonbg ANIM_ATTACKER + clearmonbg ANIM_TARGET + blendoff end gBattleAnimMove_AquaRing:: diff --git a/graphics/battle_anims/sprites/pinkvio_orb.png b/graphics/battle_anims/sprites/pinkvio_orb.png new file mode 100644 index 0000000000000000000000000000000000000000..d0d7c927549bb8590999f1f5184bc587c67501f8 GIT binary patch literal 5019 zcmeHKdsGu=7M}nT5=3YL6}BxQNCj(>$@^g4#Fq%fJ8K!iL<0tCCDE7V6p z3a;1|aVcUcsIUcE5mZ_gs@7s36j2aV>{=0p^#O&V(n&zYvpsu`=WPEoCzH(F@7~|N z_jm7ilW)5+e5o7Jmk0oWn_MP|z`i|fmy;v*d8fM<6F81+P_3~o}cf2yQWl->OdH%;RBiHuZ*>t z;?K_c{PGTeikWo9U9#X#|5*=1;dDt^%%`P1H-f=Y<16c`o$1HEO!cp?svcWrIjR*N z>N%BZb&va(VC0n>Z-&kkO5X&0>I3C4@Ex77sio=;ddIzOwo5~A-x0FM5U&00@W#l0 zCw$bODL9CxhZlIBeOy5sJA0&U(`%U{7wXzC^^Rr6fAE>VpeAIX=iqQk>l$h88>+E} zij3ey*ZYih(>s=Jx&gHXZ1EZjdPVw5`?ON!2ers9^**TL#~tkUIc^`d9&ytQicucoFg)eh6THBPf+a~gnQ~-7&E@C6^-qjGkJu%y((jy2bcy$}o3p}4ZO?H_4MrV+F$H<%yQ|2uQ z<(B&G@!4hU4P3M*h@f8E?-6oho;rx1e1ZQJwI{%K}!;4;#`EkKVu8LiJ z)v|f(+bcUhKJeGbpMx&4Vjj>XJLu(3C4}9cP2nHrN8FfyNwf6QFZt}##l(}JUhI93 zR+_LqaAkEw%|=zMxZO8#eYyXxB3cWbte15J*7V3uq1R9&soN09n=IlPt!4P zH(vT8pvUX=Zyqh<5{JXNk5zo%ACFgFF4M0*(4IWKaAgg}6`O=#_h4jJ)co%6w$pZ_HS;v(Sw%jP33)xbCI$Zo!OY( z|9V(s^CjYa8VhqoVmy$;XKY?+Ivy8u!TFG|8Eq zxyCDtm#?^#NYNQqF3ViAQ{kCfJkYn~T5DWX=k;F&cPmrV{Ps47ocYZ&V}Z_cF1mB{ z`}48cqYaXW@@!^H?DV#yKP_|&o7=qFA=W|E@gU&dEL__=ULp9f5K^WO_$aTJ_LarQ z%dgof#<>OG7rN2Ebya8|ZNE=Q_roh;_448RuJ-<;-Wg2|nI(yX1Ecu)za@P4y%?YOFZB9IK;{BEb zSvhspGpaX}MsU5iukOD|4tz`t{(Rfrt4I6>2SfusxFuyLRdtE4D%s~`EwGwXKX3RL z8Ng+2Q%lt4|CG@*SNTl?*RJ=+WXGqMjF}$+RX6!h1gGZg2{a1}BUB;tOvzq;n)2@}3SF;tM!YD=)w6YB*G;%|@3f_ zNqV!uDk9r)1=zh!Oe2%*DwaeMd5uCz5*th?iA7~mL5kF>O=XZ55=lZ6q7g($LMI`x zClNWpVlfJ6w6wG|Y8sPjFvZj8d_JEBGH4721=FCI)Abg}O3|AGY!DL|64VTvv_^~8 zpeNZdA+;gJA|jKqb<&ggbVh~ZDZJi1$pXd)%?cT5bSg;G>1b0e%ob@X1~M7YA6l4I z*iRcR0yP^_OfV`kA1@T&) z(Qbv2{hX#nt9eG&bFtZ0?CDGm1T%k%`<(WZ-0jMkmO>$r7~m9Jcyft|Z1XQb46qgv z*l$^Mh{J^tkiu0%bP5aQuqiwcpS>h{NZjQ=sH}vjx(_s0|7Or)n`Am`-OS zEGC`8=c!Q&i$T{=AeNd*f!S;|lf~Dt8QiH5D@2hLy=X(FgH+}O zvaK)zEFFwm$kwSCfZYPiMj$q!ki}q984O7xvMnf*&C=fBB;iC^WLh((k#6hx$*zw? zlP8`gHbIiszC?UN`aKUC3?dh3SgsUP$?6cm3b!B0f9rpnB{dP#Sia zQKXsoU7;Ssf4sAEYdbMpf>E~C3;?d4whIRo z70data[10] = -10; task->data[11] = GetBattlerSpriteCoordAttr(gBattleAnimTarget, BATTLER_COORD_ATTR_LEFT) + 8; @@ -1023,6 +1036,55 @@ void AnimTask_SkillSwap(u8 taskId) task->func = AnimTask_SkillSwap_Step; } +// Copy of Skill Swap's function to get position of the user and target +// arg 0: move target +void AnimTask_HeartSwap(u8 taskId) +{ + struct Task *task = &gTasks[taskId]; + + if (IsContest()) + { + if (gBattleAnimArgs[0] == ANIM_TARGET) + { + task->data[10] = -10; + task->data[11] = GetBattlerSpriteCoordAttr(gBattleAnimTarget, BATTLER_COORD_ATTR_RIGHT) - 8; + task->data[12] = GetBattlerSpriteCoordAttr(gBattleAnimTarget, BATTLER_COORD_ATTR_TOP) + 8; + task->data[13] = GetBattlerSpriteCoordAttr(gBattleAnimAttacker, BATTLER_COORD_ATTR_RIGHT) - 8; + task->data[14] = GetBattlerSpriteCoordAttr(gBattleAnimAttacker, BATTLER_COORD_ATTR_TOP) + 8; + } + else + { + task->data[10] = 10; + task->data[11] = GetBattlerSpriteCoordAttr(gBattleAnimAttacker, BATTLER_COORD_ATTR_LEFT) + 8; + task->data[12] = GetBattlerSpriteCoordAttr(gBattleAnimAttacker, BATTLER_COORD_ATTR_BOTTOM) - 8; + task->data[13] = GetBattlerSpriteCoordAttr(gBattleAnimTarget, BATTLER_COORD_ATTR_LEFT) + 8; + task->data[14] = GetBattlerSpriteCoordAttr(gBattleAnimTarget, BATTLER_COORD_ATTR_BOTTOM) - 8; + } + } + else + { + if (gBattleAnimArgs[0] == ANIM_TARGET) + { + task->data[10] = -10; + task->data[11] = GetBattlerSpriteCoordAttr(gBattleAnimTarget, BATTLER_COORD_ATTR_LEFT) + 8; + task->data[12] = GetBattlerSpriteCoordAttr(gBattleAnimTarget, BATTLER_COORD_ATTR_TOP) + 8; + task->data[13] = GetBattlerSpriteCoordAttr(gBattleAnimAttacker, BATTLER_COORD_ATTR_LEFT) + 8; + task->data[14] = GetBattlerSpriteCoordAttr(gBattleAnimAttacker, BATTLER_COORD_ATTR_TOP) + 8; + } + else + { + task->data[10] = 10; + task->data[11] = GetBattlerSpriteCoordAttr(gBattleAnimAttacker, BATTLER_COORD_ATTR_RIGHT) - 8; + task->data[12] = GetBattlerSpriteCoordAttr(gBattleAnimAttacker, BATTLER_COORD_ATTR_BOTTOM) - 8; + task->data[13] = GetBattlerSpriteCoordAttr(gBattleAnimTarget, BATTLER_COORD_ATTR_RIGHT) - 8; + task->data[14] = GetBattlerSpriteCoordAttr(gBattleAnimTarget, BATTLER_COORD_ATTR_BOTTOM) - 8; + } + } + + task->data[1] = 6; + task->func = AnimTask_HeartSwap_Step; +} + static void AnimTask_SkillSwap_Step(u8 taskId) { u8 spriteId; @@ -1057,6 +1119,42 @@ static void AnimTask_SkillSwap_Step(u8 taskId) } } +// Copy of Skill Swap's function to vault the series of orbs between the user and target +// CreateSprite modified so it uses the pink orbs instead of the blue/green ones +static void AnimTask_HeartSwap_Step(u8 taskId) +{ + u8 spriteId; + struct Task *task = &gTasks[taskId]; + + switch (task->data[0]) + { + case 0: + if (++task->data[1] > 6) + { + task->data[1] = 0; + spriteId = CreateSprite(&gHeartSwapOrbSpriteTemplate, task->data[11], task->data[12], 0); + if (spriteId != MAX_SPRITES) + { + gSprites[spriteId].data[0] = 16; + gSprites[spriteId].data[2] = task->data[13]; + gSprites[spriteId].data[4] = task->data[14]; + gSprites[spriteId].data[5] = task->data[10]; + + InitAnimArcTranslation(&gSprites[spriteId]); + StartSpriteAffineAnim(&gSprites[spriteId], task->data[2] & 3); + } + + if (++task->data[2] == 12) + task->data[0]++; + } + break; + case 1: + if (++task->data[1] > 17) + DestroyAnimVisualTask(taskId); + break; + } +} + static void AnimSkillSwapOrb(struct Sprite *sprite) { if (TranslateAnimHorizontalArc(sprite)) diff --git a/src/data/battle_anim.h b/src/data/battle_anim.h index 2ab982d2d7..a09c8d72c8 100644 --- a/src/data/battle_anim.h +++ b/src/data/battle_anim.h @@ -1465,6 +1465,7 @@ const struct CompressedSpriteSheet gBattleAnimPicTable[] = {gBattleAnimSpriteGfx_RedExplosion, 0x0800, ANIM_TAG_RED_EXPLOSION}, {gBattleAnimSpriteGfx_Beam, 0x0800, ANIM_TAG_BEAM}, {gBattleAnimSpriteGfx_PurpleChain, 0x1000, ANIM_TAG_PURPLE_CHAIN}, + {gBattleAnimSpriteGfx_PinkVioletOrb, 0x0080, ANIM_TAG_PINKVIO_ORB}, }; const struct CompressedSpritePalette gBattleAnimPaletteTable[] = @@ -1931,6 +1932,7 @@ const struct CompressedSpritePalette gBattleAnimPaletteTable[] = {gBattleAnimSpritePal_RedExplosion, ANIM_TAG_RED_EXPLOSION}, {gBattleAnimSpritePal_Beam, ANIM_TAG_BEAM}, {gBattleAnimSpritePal_PurpleChain, ANIM_TAG_PURPLE_CHAIN}, + {gBattleAnimSpritePal_PinkVioletOrb, ANIM_TAG_PINKVIO_ORB}, }; const struct BattleAnimBackground gBattleAnimBackgroundTable[] = diff --git a/src/graphics.c b/src/graphics.c index 231aec4158..5aafa8ae45 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -1263,6 +1263,9 @@ const u32 gBattleAnimSpriteGfx_XSign[] = INCBIN_U32("graphics/battle_anims/sprit const u32 gBattleAnimSpriteGfx_BluegreenOrb[] = INCBIN_U32("graphics/battle_anims/sprites/bluegreen_orb.4bpp.lz"); const u32 gBattleAnimSpritePal_BluegreenOrb[] = INCBIN_U32("graphics/battle_anims/sprites/bluegreen_orb.gbapal.lz"); +const u32 gBattleAnimSpriteGfx_PinkVioletOrb[] = INCBIN_U32("graphics/battle_anims/sprites/pinkvio_orb.4bpp.lz"); +const u32 gBattleAnimSpritePal_PinkVioletOrb[] = INCBIN_U32("graphics/battle_anims/sprites/pinkvio_orb.gbapal.lz"); + const u32 gBattleAnimSpriteGfx_PawPrint[] = INCBIN_U32("graphics/battle_anims/sprites/paw_print.4bpp.lz"); const u32 gBattleAnimSpritePal_PawPrint[] = INCBIN_U32("graphics/battle_anims/sprites/paw_print.gbapal.lz"); From 14546443768cb457a8e548e3f43a7d613ebb7bfd Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:50:59 +0200 Subject: [PATCH 43/65] Removes duplicate code in AI functions (#5457) * Make functions CanAbilityAbsorbMove and CanAbilityBlockMove * clean up * Replace ai code with CanAbilityBlockMove / CanAbilityAbsorbMove * Adds CanPartnerAbilityBlockMove * Use switches instead of if-blocks * solve potential bug in singles with spread attacks * fix test * revert other fix * flash fire does not increase a stat * left a comment for a future test and addition * remove more checks * comment was incorrect. it only applies to storm drain and lightning rod * revert ndebug * revert minor change * Update src/battle_util.c --- include/battle.h | 2 +- include/battle_util.h | 20 +++ src/battle_ai_main.c | 47 ++----- src/battle_ai_util.c | 61 +++----- src/battle_main.c | 1 + src/battle_util.c | 318 ++++++++++++++++++++++++------------------ 6 files changed, 230 insertions(+), 219 deletions(-) diff --git a/include/battle.h b/include/battle.h index 2d1d658419..894b74ffde 100644 --- a/include/battle.h +++ b/include/battle.h @@ -378,6 +378,7 @@ struct AiLogicData u8 ejectPackSwitch:1; // Tracks whether current switch out was from Eject Pack u8 padding:5; u8 shouldSwitch; // Stores result of ShouldSwitch, which decides whether a mon should be switched out + u8 aiCalcInProgress:1; }; struct AI_ThinkingStruct @@ -716,7 +717,6 @@ struct BattleStruct } multiBuffer; u8 wishPerishSongState; u8 wishPerishSongBattlerId; - u8 aiCalcInProgress:1; u8 overworldWeatherDone:1; u8 startingStatusDone:1; u8 isAtkCancelerForCalledMove:1; // Certain cases in atk canceler should only be checked once, when the original move is called, however others need to be checked the twice. diff --git a/include/battle_util.h b/include/battle_util.h index c4727d8a12..504c3be3ad 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -20,6 +20,23 @@ #define MOVE_LIMITATION_PLACEHOLDER (1 << 15) #define MOVE_LIMITATIONS_ALL 0xFFFF +enum MoveBlocked +{ + MOVE_BLOCKED_BY_NO_ABILITY, + MOVE_BLOCKED_BY_SOUNDPROOF_OR_BULLETPROOF, + MOVE_BLOCKED_BY_DAZZLING, + MOVE_BLOCKED_BY_PARTNER_DAZZLING, + MOVE_BLOCKED_BY_GOOD_AS_GOLD, +}; + +enum MoveAbsorbed +{ + MOVE_ABSORBED_BY_NO_ABILITY, + MOVE_ABSORBED_BY_DRAIN_HP_ABILITY, + MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY, + MOVE_ABSORBED_BY_BOOST_FLASH_FIRE, +}; + enum { ABILITYEFFECT_ON_SWITCHIN, ABILITYEFFECT_ENDTURN, @@ -161,6 +178,9 @@ void SetAtkCancellerForCalledMove(void); u8 AtkCanceller_UnableToUseMove2(void); bool32 HasNoMonsToSwitch(u32 battler, u8 r1, u8 r2); bool32 TryChangeBattleWeather(u32 battler, u32 weatherEnumId, bool32 viaAbility); +u32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 move, u32 abilityDef); +u32 CanPartnerAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 move, u32 abilityDef); +u32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 move, u32 moveType); u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg); bool32 TryPrimalReversion(u32 battler); bool32 IsNeutralizingGasOnField(void); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 5ab8e652c6..a08ca0f71f 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -873,6 +873,12 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) // target ability checks if (!DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move)) { + if (CanAbilityBlockMove(battlerAtk, battlerDef, move, aiData->abilities[battlerDef])) + RETURN_SCORE_MINUS(20); + + if (CanAbilityAbsorbMove(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, moveType)) + RETURN_SCORE_MINUS(20); + switch (aiData->abilities[battlerDef]) { case ABILITY_MAGIC_GUARD: @@ -903,12 +909,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) && (moveType == TYPE_DARK || moveType == TYPE_GHOST || moveType == TYPE_BUG)) RETURN_SCORE_MINUS(10); break; - case ABILITY_DAZZLING: - case ABILITY_QUEENLY_MAJESTY: - case ABILITY_ARMOR_TAIL: - if (atkPriority > 0) - RETURN_SCORE_MINUS(10); - break; case ABILITY_AROMA_VEIL: if (IsAromaVeilProtectedMove(move)) RETURN_SCORE_MINUS(10); @@ -972,37 +972,14 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) && IsNonVolatileStatusMoveEffect(moveEffect)) RETURN_SCORE_MINUS(10); break; - case ABILITY_LIGHTNING_ROD: - if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5) - break; - // Fallthrough - case ABILITY_MOTOR_DRIVE: - case ABILITY_VOLT_ABSORB: - if (moveType == TYPE_ELECTRIC) - RETURN_SCORE_MINUS(20); - break; - case ABILITY_STORM_DRAIN: - if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5) - break; - // Fallthrough - case ABILITY_WATER_ABSORB: - case ABILITY_DRY_SKIN: - if (moveType == TYPE_WATER) - RETURN_SCORE_MINUS(20); - break; - case ABILITY_FLASH_FIRE: - if (moveType == TYPE_FIRE) - RETURN_SCORE_MINUS(20); - break; - case ABILITY_EARTH_EATER: - if (moveType == TYPE_GROUND) - RETURN_SCORE_MINUS(20); - break; } // def ability checks // target partner ability checks & not attacking partner if (isDoubleBattle) { + if (CanPartnerAbilityBlockMove(battlerAtk, battlerDef, move, aiData->abilities[BATTLE_PARTNER(battlerDef)])) + RETURN_SCORE_MINUS(20); + switch (aiData->abilities[BATTLE_PARTNER(battlerDef)]) { case ABILITY_LIGHTNING_ROD: @@ -1029,12 +1006,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) if (IsAromaVeilProtectedMove(move)) RETURN_SCORE_MINUS(10); break; - case ABILITY_DAZZLING: - case ABILITY_QUEENLY_MAJESTY: - case ABILITY_ARMOR_TAIL: - if (atkPriority > 0) - RETURN_SCORE_MINUS(10); - break; } } // def partner ability checks } // ignore def ability check diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 612e89c9ce..bf84f98b4d 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -402,12 +402,19 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef) { struct AiLogicData *aiData = AI_DATA; u32 battlerDefAbility; + u32 partnerBattlerDefAbility; u32 moveType = GetMoveType(move); if (DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move)) + { battlerDefAbility = ABILITY_NONE; + partnerBattlerDefAbility = ABILITY_NONE; + } else + { battlerDefAbility = aiData->abilities[battlerDef]; + partnerBattlerDefAbility = aiData->abilities[BATTLE_PARTNER(battlerDef)]; + } if (battlerDef == BATTLE_PARTNER(battlerAtk)) battlerDefAbility = aiData->abilities[battlerDef]; @@ -415,47 +422,14 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef) if (gBattleStruct->commandingDondozo & (1u << battlerDef)) return TRUE; - switch (battlerDefAbility) - { - case ABILITY_LIGHTNING_ROD: - if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5) - break; - // Fallthrough - case ABILITY_VOLT_ABSORB: - case ABILITY_MOTOR_DRIVE: - if (moveType == TYPE_ELECTRIC) - return TRUE; - break; - case ABILITY_STORM_DRAIN: - if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5) - break; - // Fallthrough - case ABILITY_WATER_ABSORB: - case ABILITY_DRY_SKIN: - if (moveType == TYPE_WATER) - return TRUE; - break; - case ABILITY_FLASH_FIRE: - if (moveType == TYPE_FIRE) - return TRUE; - break; - case ABILITY_SOUNDPROOF: - if (gMovesInfo[move].soundMove) - return TRUE; - break; - case ABILITY_BULLETPROOF: - if (gMovesInfo[move].ballisticMove) - return TRUE; - break; - case ABILITY_SAP_SIPPER: - if (moveType == TYPE_GRASS) - return TRUE; - break; - case ABILITY_EARTH_EATER: - if (moveType == TYPE_GROUND) - return TRUE; - break; - } + if (CanAbilityBlockMove(battlerAtk, battlerDef, move, aiData->abilities[battlerDef])) + return TRUE; + + if (CanPartnerAbilityBlockMove(battlerAtk, battlerDef, move, partnerBattlerDefAbility)) + return TRUE; + + if (CanAbilityAbsorbMove(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, moveType)) + return TRUE; switch (gMovesInfo[move].effect) { @@ -526,7 +500,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u bool32 isDamageMoveUnusable = FALSE; bool32 toggledGimmick = FALSE; struct AiLogicData *aiData = AI_DATA; - gBattleStruct->aiCalcInProgress = TRUE; + AI_DATA->aiCalcInProgress = TRUE; if (moveEffect == EFFECT_NATURE_POWER) move = GetNaturePowerMove(battlerAtk); @@ -736,12 +710,11 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u *typeEffectiveness = AI_GetEffectiveness(effectivenessMultiplier); // Undo temporary settings - gBattleStruct->aiCalcInProgress = FALSE; gBattleStruct->swapDamageCategory = FALSE; gBattleStruct->zmove.baseMoves[battlerAtk] = MOVE_NONE; if (toggledGimmick) SetActiveGimmick(battlerAtk, GIMMICK_NONE); - + AI_DATA->aiCalcInProgress = FALSE; return simDamage; } diff --git a/src/battle_main.c b/src/battle_main.c index c1974e0f6a..6e8a0d0e8f 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4202,6 +4202,7 @@ static void HandleTurnActionSelectionState(void) AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, TRUE); else AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, FALSE); + gBattleStruct->aiMoveOrAction[battler] = ComputeBattleAiScores(battler); } // fallthrough diff --git a/src/battle_util.c b/src/battle_util.c index e1f10d4eda..01b41aaf7a 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4107,12 +4107,125 @@ static void ChooseStatBoostAnimation(u32 battler) #undef ANIM_STAT_ACC #undef ANIM_STAT_EVASION +u32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 move, u32 abilityDef) +{ + enum MoveBlocked effect = MOVE_BLOCKED_BY_NO_ABILITY; + + switch (abilityDef) + { + case ABILITY_SOUNDPROOF: + if (gMovesInfo[move].soundMove && !(GetBattlerMoveTargetType(battlerAtk, move) & MOVE_TARGET_USER)) + effect = MOVE_BLOCKED_BY_SOUNDPROOF_OR_BULLETPROOF; + break; + case ABILITY_BULLETPROOF: + if (gMovesInfo[move].ballisticMove) + effect = MOVE_BLOCKED_BY_SOUNDPROOF_OR_BULLETPROOF; + break; + case ABILITY_DAZZLING: + case ABILITY_QUEENLY_MAJESTY: + case ABILITY_ARMOR_TAIL: + if (GetBattlerSide(battlerAtk) != GetBattlerSide(battlerDef)) + { + u32 priority = AI_DATA->aiCalcInProgress ? GetMovePriority(battlerAtk, move) : GetChosenMovePriority(battlerAtk); + if (priority > 0) + effect = MOVE_BLOCKED_BY_DAZZLING; + } + break; + case ABILITY_GOOD_AS_GOLD: + if (IS_MOVE_STATUS(move)) + { + u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move); + if (!(moveTarget & MOVE_TARGET_OPPONENTS_FIELD) && !(moveTarget & MOVE_TARGET_ALL_BATTLERS)) + effect = MOVE_BLOCKED_BY_GOOD_AS_GOLD; + } + break; + } + + if (!effect) + effect = CanPartnerAbilityBlockMove(battlerAtk, battlerDef, move, GetBattlerAbility(BATTLE_PARTNER(battlerDef))); + + return effect; +} + +u32 CanPartnerAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 move, u32 abilityDef) +{ + switch (abilityDef) + { + case ABILITY_DAZZLING: + case ABILITY_QUEENLY_MAJESTY: + case ABILITY_ARMOR_TAIL: + if (GetBattlerSide(battlerAtk) != GetBattlerSide(battlerDef)) + { + s32 priority = AI_DATA->aiCalcInProgress ? GetMovePriority(battlerAtk, move) : GetChosenMovePriority(battlerAtk); + if (priority > 0) + return MOVE_BLOCKED_BY_PARTNER_DAZZLING; + } + break; + } + return MOVE_BLOCKED_BY_NO_ABILITY; +} + +u32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 move, u32 moveType) +{ + enum MoveAbsorbed effect = MOVE_ABSORBED_BY_NO_ABILITY; + + switch (abilityDef) + { + default: + effect = MOVE_ABSORBED_BY_NO_ABILITY; + break; + case ABILITY_VOLT_ABSORB: + if (moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS) + effect = MOVE_ABSORBED_BY_DRAIN_HP_ABILITY; + break; + case ABILITY_WATER_ABSORB: + case ABILITY_DRY_SKIN: + if (moveType == TYPE_WATER) + effect = MOVE_ABSORBED_BY_DRAIN_HP_ABILITY; + break; + case ABILITY_EARTH_EATER: + if (moveType == TYPE_GROUND) + effect = MOVE_ABSORBED_BY_DRAIN_HP_ABILITY; + break; + case ABILITY_MOTOR_DRIVE: + if (moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS) // Potential bug in singles (might be solved with simu hp reudction) + effect = MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY; + break; + case ABILITY_LIGHTNING_ROD: + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS) // Potential bug in singles (might be solved with simu hp reudction) + effect = MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY; + break; + case ABILITY_STORM_DRAIN: + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && moveType == TYPE_WATER) + effect = MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY; + break; + case ABILITY_SAP_SIPPER: + if (moveType == TYPE_GRASS) + effect = MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY; + break; + case ABILITY_WELL_BAKED_BODY: + if (moveType == TYPE_FIRE) + effect = MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY; + break; + case ABILITY_WIND_RIDER: + if (gMovesInfo[move].windMove && !(GetBattlerMoveTargetType(battlerAtk, move) & MOVE_TARGET_USER)) + effect = MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY; + break; + case ABILITY_FLASH_FIRE: + if (moveType == TYPE_FIRE && (B_FLASH_FIRE_FROZEN >= GEN_5 || !(gBattleMons[battlerDef].status1 & STATUS1_FREEZE))) + effect = MOVE_ABSORBED_BY_BOOST_FLASH_FIRE; + break; + } + + return effect; +} + u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg) { u32 effect = 0; - u32 moveType, move; - u32 side; - u32 i, j; + u32 moveType = 0, move = 0; + u32 side = 0; + u32 i = 0, j = 0; u32 partner = 0; struct Pokemon *mon; @@ -5206,153 +5319,87 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } } break; - case ABILITYEFFECT_MOVES_BLOCK: case ABILITYEFFECT_WOULD_BLOCK: + effect = CanAbilityBlockMove(gBattlerAttacker, battler, move, gLastUsedAbility); + if (effect && gLastUsedAbility != 0xFFFF) + RecordAbilityBattle(battler, gLastUsedAbility); + break; + case ABILITYEFFECT_MOVES_BLOCK: { - u16 moveTarget = GetBattlerMoveTargetType(battler, move); + effect = CanAbilityBlockMove(gBattlerAttacker, battler, move, gLastUsedAbility); const u8 * battleScriptBlocksMove = NULL; - - switch (gLastUsedAbility) - { - case ABILITY_SOUNDPROOF: - if (gMovesInfo[move].soundMove && !(moveTarget & MOVE_TARGET_USER)) - effect = 1; - break; - case ABILITY_BULLETPROOF: - if (gMovesInfo[move].ballisticMove) - effect = 1; - break; - case ABILITY_DAZZLING: - case ABILITY_QUEENLY_MAJESTY: - case ABILITY_ARMOR_TAIL: - if (GetChosenMovePriority(gBattlerAttacker) > 0 && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(battler)) - effect = 2; - break; - case ABILITY_GOOD_AS_GOLD: - if (IS_MOVE_STATUS(gCurrentMove) - && !(moveTarget & MOVE_TARGET_OPPONENTS_FIELD) - && !(moveTarget & MOVE_TARGET_ALL_BATTLERS)) - effect = 3; - break; - } - - if (!effect) - { - switch (GetBattlerAbility(BATTLE_PARTNER(battler))) - { - case ABILITY_DAZZLING: - case ABILITY_QUEENLY_MAJESTY: - case ABILITY_ARMOR_TAIL: - if (GetChosenMovePriority(gBattlerAttacker) > 0 && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(battler)) - effect = 4; - break; - } - } - - if (effect == 1) + switch (effect) { + case MOVE_BLOCKED_BY_SOUNDPROOF_OR_BULLETPROOF: if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS) gHitMarker |= HITMARKER_NO_PPDEDUCT; battleScriptBlocksMove = BattleScript_SoundproofProtected; - } - else if (effect == 2 || effect == 4) - { - if (effect == 4) + break; + case MOVE_BLOCKED_BY_DAZZLING: + case MOVE_BLOCKED_BY_PARTNER_DAZZLING: + if (effect == MOVE_BLOCKED_BY_PARTNER_DAZZLING) gBattleScripting.battler = BATTLE_PARTNER(battler); else gBattleScripting.battler = battler; - if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS) gHitMarker |= HITMARKER_NO_PPDEDUCT; battleScriptBlocksMove = BattleScript_DazzlingProtected; - } - else if (effect == 3) - { + break; + case MOVE_BLOCKED_BY_GOOD_AS_GOLD: battleScriptBlocksMove = BattleScript_GoodAsGoldActivates; + break; + default: + if (GetChosenMovePriority(gBattlerAttacker) > 0 + && BlocksPrankster(move, gBattlerAttacker, gBattlerTarget, TRUE) + && !(IS_MOVE_STATUS(move) && (gLastUsedAbility == ABILITY_MAGIC_BOUNCE || gProtectStructs[gBattlerTarget].bounceMove))) + { + if (!IsDoubleBattle() + || !(GetBattlerMoveTargetType(gBattlerAttacker, move) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) + CancelMultiTurnMoves(gBattlerAttacker); // Don't cancel moves that can hit two targets bc one target might not be protected + gBattleScripting.battler = gBattlerAbility = gBattlerTarget; + battleScriptBlocksMove = BattleScript_DarkTypePreventsPrankster; + effect = 1; + } } - else if (GetChosenMovePriority(gBattlerAttacker) > 0 - && BlocksPrankster(move, gBattlerAttacker, gBattlerTarget, TRUE) - && !(IS_MOVE_STATUS(move) && (gLastUsedAbility == ABILITY_MAGIC_BOUNCE || gProtectStructs[gBattlerTarget].bounceMove))) - { - if (!IsDoubleBattle() || !(moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) - CancelMultiTurnMoves(gBattlerAttacker); // Don't cancel moves that can hit two targets bc one target might not be protected - gBattleScripting.battler = gBattlerAbility = gBattlerTarget; - battleScriptBlocksMove = BattleScript_DarkTypePreventsPrankster; - effect = 1; - } - if (caseID == ABILITYEFFECT_WOULD_BLOCK) - { - if (effect && gLastUsedAbility != 0xFFFF) - RecordAbilityBattle(battler, gLastUsedAbility); - - return effect; - } - else if (effect) - { + if (effect) gBattlescriptCurrInstr = battleScriptBlocksMove; - } - break; } - case ABILITYEFFECT_ABSORBING: + break; case ABILITYEFFECT_WOULD_ABSORB: - if (move != MOVE_NONE) + effect = CanAbilityAbsorbMove(gBattlerAttacker, battler, gLastUsedAbility, move, moveType); + gBattleStruct->pledgeMove = FALSE; + if (effect && gLastUsedAbility != 0xFFFF) + RecordAbilityBattle(battler, gLastUsedAbility); + return effect; + case ABILITYEFFECT_ABSORBING: { - u8 statId = 0; - u8 statAmount = 1; - switch (gLastUsedAbility) + u32 statId = 0; + u32 statAmount = 1; + effect = CanAbilityAbsorbMove(gBattlerAttacker, battler, gLastUsedAbility, move, moveType); + if (effect) { - case ABILITY_VOLT_ABSORB: - if (moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS) - effect = 1; - break; - case ABILITY_WATER_ABSORB: - case ABILITY_DRY_SKIN: - if (moveType == TYPE_WATER) - effect = 1; - break; - case ABILITY_MOTOR_DRIVE: - if (moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS) - effect = 2, statId = STAT_SPEED; - break; - case ABILITY_LIGHTNING_ROD: - if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS) - effect = 2, statId = STAT_SPATK; - break; - case ABILITY_STORM_DRAIN: - if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && moveType == TYPE_WATER) - effect = 2, statId = STAT_SPATK; - break; - case ABILITY_SAP_SIPPER: - if (moveType == TYPE_GRASS) - effect = 2, statId = STAT_ATK; - break; - case ABILITY_FLASH_FIRE: - if (moveType == TYPE_FIRE && (B_FLASH_FIRE_FROZEN >= GEN_5 || !(gBattleMons[battler].status1 & STATUS1_FREEZE))) - effect = 3; - break; - case ABILITY_WELL_BAKED_BODY: - if (moveType == TYPE_FIRE) - effect = 2, statId = STAT_DEF, statAmount = 2; - break; - case ABILITY_WIND_RIDER: - if (gMovesInfo[gCurrentMove].windMove && !(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove) & MOVE_TARGET_USER)) - effect = 2, statId = STAT_ATK; - break; - case ABILITY_EARTH_EATER: - if (moveType == TYPE_GROUND) - effect = 1; - break; + switch(gLastUsedAbility) + { + case ABILITY_MOTOR_DRIVE: + statId = STAT_SPEED; + break; + case ABILITY_LIGHTNING_ROD: + case ABILITY_STORM_DRAIN: + statId = STAT_SPATK; + break; + case ABILITY_SAP_SIPPER: + case ABILITY_WIND_RIDER: + statId = STAT_ATK; + break; + case ABILITY_WELL_BAKED_BODY: + statAmount = 2; + statId = STAT_DEF; + break; + } } - if (caseID == ABILITYEFFECT_WOULD_ABSORB) - { - gBattleStruct->pledgeMove = FALSE; - if (effect && gLastUsedAbility != 0xFFFF) - RecordAbilityBattle(battler, gLastUsedAbility); - - return effect; - } - else if (effect == 1) // Drain Hp ability. + switch (effect) { + case MOVE_ABSORBED_BY_DRAIN_HP_ABILITY: gBattleStruct->pledgeMove = FALSE; if (BATTLER_MAX_HP(battler) || (B_HEAL_BLOCKING >= GEN_5 && gStatuses3[battler] & STATUS3_HEAL_BLOCK)) { @@ -5373,9 +5420,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 gBattleMoveDamage = 1; gBattleMoveDamage *= -1; } - } - else if (effect == 2) // Boost Stat ability; - { + break; + case MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY: gBattleStruct->pledgeMove = FALSE; if (!CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) { @@ -5395,9 +5441,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (B_ABSORBING_ABILITY_STRING < GEN_5) PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); } - } - else if (effect == 3) - { + break; + case MOVE_ABSORBED_BY_BOOST_FLASH_FIRE: gBattleStruct->pledgeMove = FALSE; if (!(gBattleResources->flags->flags[battler] & RESOURCE_FLAG_FLASH_FIRE)) { @@ -5416,10 +5461,11 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 else gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss; } + break; } - if (effect) gMultiHitCounter = 0; // Prevent multi-hit moves from hitting more than once after move has been absorbed. + } break; case ABILITYEFFECT_MOVE_END: // Think contact abilities. @@ -10371,7 +10417,7 @@ static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 move mod = UQ_4_12(1.0); } - if (gBattleStruct->distortedTypeMatchups & (1u << battlerDef) || (gBattleStruct->aiCalcInProgress && ShouldTeraShellDistortTypeMatchups(move, battlerDef))) + if (gBattleStruct->distortedTypeMatchups & (1u << battlerDef) || (AI_DATA->aiCalcInProgress && ShouldTeraShellDistortTypeMatchups(move, battlerDef))) { mod = UQ_4_12(0.5); if (recordAbilities) From 3c3ce38378792223e95dd1e2a0e8f7e69336baef Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:18:08 +0200 Subject: [PATCH 44/65] Remove one redundant call of SetAiLogicDataForTurn in DoBattleIntro (#5491) This is already run at the end of `TryDoEventsBeforeFirstTurn` which makes the first calc redundant. I've had this removed in my project for a while and didn't notice any problems --- src/battle_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/battle_main.c b/src/battle_main.c index e0ec3023ec..df39e745ac 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3750,7 +3750,6 @@ static void DoBattleIntro(void) gBattleStruct->eventsBeforeFirstTurnState = 0; gBattleStruct->switchInBattlerCounter = 0; gBattleStruct->overworldWeatherDone = FALSE; - SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers Ai_InitPartyStruct(); // Save mons party counts, and first 2/4 mons on the battlefield. // Try to set a status to start the battle with From 806321f3c8e497b245000c078b1284a67df5872f Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Wed, 9 Oct 2024 18:24:39 +0200 Subject: [PATCH 45/65] Update shed_tail.c (#5494) --- test/battle/move_effect/shed_tail.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/battle/move_effect/shed_tail.c b/test/battle/move_effect/shed_tail.c index d69993b88b..3b2417f7c7 100644 --- a/test/battle/move_effect/shed_tail.c +++ b/test/battle/move_effect/shed_tail.c @@ -88,6 +88,7 @@ SINGLE_BATTLE_TEST("Shed Tail's HP cost doesn't trigger effects that trigger on AI_SINGLE_BATTLE_TEST("AI will use Shed Tail to pivot to another mon while in damage stalemate with player") { + KNOWN_FAILING; // missing AI code GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_WOBBUFFET) { Speed(100); Ability(ABILITY_RUN_AWAY); Moves(MOVE_TACKLE, MOVE_CELEBRATE); } From d0a97970baae433f24e3e62a4a3b3e942157ea76 Mon Sep 17 00:00:00 2001 From: AlexOn1ine Date: Thu, 10 Oct 2024 09:55:25 +0200 Subject: [PATCH 46/65] Unify GetBattlerAbility/TerrainAffected to remove duplicate ai function --- include/battle_ai_util.h | 2 -- src/battle_ai_main.c | 16 +++++++++------- src/battle_ai_util.c | 36 ------------------------------------ src/battle_main.c | 2 ++ src/battle_util.c | 4 ++-- 5 files changed, 13 insertions(+), 47 deletions(-) diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 69cf92ed0d..c87a35e750 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -52,8 +52,6 @@ bool32 DoesBattlerIgnoreAbilityChecks(u32 atkAbility, u32 move); u32 AI_GetWeather(struct AiLogicData *aiData); bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits); bool32 CanIndexMoveFaintTarget(u32 battlerAtk, u32 battlerDef, u32 index, u32 numHits); -bool32 AI_IsTerrainAffected(u32 battlerId, u32 flags); -bool32 AI_IsBattlerGrounded(u32 battlerId); bool32 HasDamagingMove(u32 battlerId); bool32 HasDamagingMoveOfType(u32 battlerId, u32 type); u32 GetBattlerSecondaryDamage(u32 battlerId); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index a08ca0f71f..1b2f24e635 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -477,6 +477,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) // get/assume all battler data and simulate AI damage battlersCount = gBattlersCount; + AI_DATA->aiCalcInProgress = TRUE; for (battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++) { if (!IsBattlerAlive(battlerAtk)) @@ -492,6 +493,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) SetBattlerAiMovesData(aiData, battlerAtk, battlersCount); } + AI_DATA->aiCalcInProgress = FALSE; } static bool32 AI_SwitchMonIfSuitable(u32 battler, bool32 doubleBattle) @@ -1017,19 +1019,19 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) RETURN_SCORE_MINUS(10); // terrain & effect checks - if (AI_IsTerrainAffected(battlerDef, STATUS_FIELD_ELECTRIC_TERRAIN)) + if (IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_ELECTRIC_TERRAIN)) { if (moveEffect == EFFECT_SLEEP || moveEffect == EFFECT_YAWN) RETURN_SCORE_MINUS(20); } - if (AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) + if (IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) { if (IsNonVolatileStatusMoveEffect(moveEffect) || IsConfusionMoveEffect(moveEffect)) RETURN_SCORE_MINUS(20); } - if (AI_IsTerrainAffected(battlerAtk, STATUS_FIELD_PSYCHIC_TERRAIN) && atkPriority > 0) + if (IsBattlerTerrainAffected(battlerAtk, STATUS_FIELD_PSYCHIC_TERRAIN) && atkPriority > 0) { RETURN_SCORE_MINUS(20); } @@ -1250,10 +1252,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) if (isDoubleBattle) { if (!(IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GRASS) - && AI_IsBattlerGrounded(battlerAtk) + && IsBattlerGrounded(battlerAtk) && (BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) || BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPATK))) && !(IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerAtk), TYPE_GRASS) - && AI_IsBattlerGrounded(BATTLE_PARTNER(battlerAtk)) + && IsBattlerGrounded(BATTLE_PARTNER(battlerAtk)) && aiData->abilities[BATTLE_PARTNER(battlerAtk)] != ABILITY_CONTRARY && (BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), aiData->abilities[BATTLE_PARTNER(battlerAtk)], STAT_ATK) || BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), aiData->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPATK)))) @@ -1262,7 +1264,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) } } else if (!(IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GRASS) - && AI_IsBattlerGrounded(battlerAtk) + && IsBattlerGrounded(battlerAtk) && (BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) || BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPATK)))) { ADJUST_SCORE(-10); @@ -3947,7 +3949,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(DECENT_EFFECT); break; case EFFECT_SAFEGUARD: - if (!AI_IsTerrainAffected(battlerAtk, STATUS_FIELD_MISTY_TERRAIN) || !IsBattlerGrounded(battlerAtk)) + if (!IsBattlerTerrainAffected(battlerAtk, STATUS_FIELD_MISTY_TERRAIN) || !IsBattlerGrounded(battlerAtk)) ADJUST_SCORE(DECENT_EFFECT); // TODO: check if opp has status move? //if (CountUsablePartyMons(battlerDef) != 0) //ADJUST_SCORE(8); diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index bf84f98b4d..1288ed1201 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1344,42 +1344,6 @@ u32 AI_DecideHoldEffectForTurn(u32 battlerId) return holdEffect; } -bool32 AI_IsTerrainAffected(u32 battlerId, u32 flags) -{ - if (gStatuses3[battlerId] & STATUS3_SEMI_INVULNERABLE) - return FALSE; - else if (!(gFieldStatuses & flags)) - return FALSE; - return AI_IsBattlerGrounded(battlerId); -} - -// different from IsBattlerGrounded in that we don't always know battler's hold effect or ability -bool32 AI_IsBattlerGrounded(u32 battlerId) -{ - u32 holdEffect = AI_DATA->holdEffects[battlerId]; - - if (holdEffect == HOLD_EFFECT_IRON_BALL) - return TRUE; - else if (gFieldStatuses & STATUS_FIELD_GRAVITY) - return TRUE; - else if (gStatuses3[battlerId] & STATUS3_ROOTED) - return TRUE; - else if (gStatuses3[battlerId] & STATUS3_SMACKED_DOWN) - return TRUE; - else if (gStatuses3[battlerId] & STATUS3_TELEKINESIS) - return FALSE; - else if (gStatuses3[battlerId] & STATUS3_MAGNET_RISE) - return FALSE; - else if (holdEffect == HOLD_EFFECT_AIR_BALLOON) - return FALSE; - else if (AI_DATA->abilities[battlerId] == ABILITY_LEVITATE) - return FALSE; - else if (IS_BATTLER_OF_TYPE(battlerId, TYPE_FLYING)) - return FALSE; - else - return TRUE; -} - bool32 DoesBattlerIgnoreAbilityChecks(u32 atkAbility, u32 move) { if (AI_THINKING_STRUCT->aiFlags[sBattler_AI] & AI_FLAG_NEGATE_UNAWARE) diff --git a/src/battle_main.c b/src/battle_main.c index 6e8a0d0e8f..b2b6e14447 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4196,6 +4196,7 @@ static void HandleTurnActionSelectionState(void) if ((gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart()) && (BattlerHasAi(battler) && !(gBattleTypeFlags & BATTLE_TYPE_PALACE))) { + AI_DATA->aiCalcInProgress = TRUE; if (ShouldSwitch(battler, FALSE)) AI_DATA->shouldSwitch |= (1u << battler); if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_RISKY) // Risky AI switches aggressively even mid battle @@ -4204,6 +4205,7 @@ static void HandleTurnActionSelectionState(void) AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, FALSE); gBattleStruct->aiMoveOrAction[battler] = ComputeBattleAiScores(battler); + AI_DATA->aiCalcInProgress = FALSE; } // fallthrough case STATE_BEFORE_ACTION_CHOSEN: // Choose an action. diff --git a/src/battle_util.c b/src/battle_util.c index 01b41aaf7a..d98ce5cbb8 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6394,7 +6394,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_PROTOSYNTHESIS: - if (!gDisableStructs[battler].weatherAbilityDone + if (!gDisableStructs[battler].weatherAbilityDone && (gBattleWeather & B_WEATHER_SUN) && WEATHER_HAS_EFFECT && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && !(gBattleStruct->boosterEnergyActivates & (1u << battler))) @@ -8683,7 +8683,7 @@ static bool32 IsBattlerGrounded2(u32 battler, bool32 considerInverse) return FALSE; if (holdEffect == HOLD_EFFECT_AIR_BALLOON) return FALSE; - if (GetBattlerAbility(battler) == ABILITY_LEVITATE) + if ((AI_DATA->aiCalcInProgress ? AI_DATA->abilities[battler] : GetBattlerAbility(battler)) == ABILITY_LEVITATE) return FALSE; if (IS_BATTLER_OF_TYPE(battler, TYPE_FLYING) && (!considerInverse || !FlagGet(B_FLAG_INVERSE_BATTLE))) return FALSE; From ab50009526e258994bd30fd891b7c0643d3b8e95 Mon Sep 17 00:00:00 2001 From: AlexOn1ine Date: Thu, 10 Oct 2024 10:04:44 +0200 Subject: [PATCH 47/65] rename IsBattlerGround2 --- src/battle_util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index d98ce5cbb8..83ed114c07 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8665,7 +8665,7 @@ bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move) } // Only called directly when calculating damage type effectiveness -static bool32 IsBattlerGrounded2(u32 battler, bool32 considerInverse) +static bool32 IsBattlerGroundedInverseCheck(u32 battler, bool32 considerInverse) { u32 holdEffect = GetBattlerHoldEffect(battler, TRUE); @@ -8692,7 +8692,7 @@ static bool32 IsBattlerGrounded2(u32 battler, bool32 considerInverse) bool32 IsBattlerGrounded(u32 battler) { - return IsBattlerGrounded2(battler, FALSE); + return IsBattlerGroundedInverseCheck(battler, FALSE); } bool32 IsBattlerAlive(u32 battler) @@ -10484,7 +10484,7 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov if (B_GLARE_GHOST < GEN_4 && move == MOVE_GLARE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST)) modifier = UQ_4_12(0.0); } - else if (moveType == TYPE_GROUND && !IsBattlerGrounded2(battlerDef, TRUE) && !(gMovesInfo[move].ignoreTypeIfFlyingAndUngrounded)) + else if (moveType == TYPE_GROUND && !IsBattlerGroundedInverseCheck(battlerDef, TRUE) && !(gMovesInfo[move].ignoreTypeIfFlyingAndUngrounded)) { modifier = UQ_4_12(0.0); if (recordAbilities && defAbility == ABILITY_LEVITATE) From 2afc7f6138969c48a5bcf7a3eed0df54d488e5bf Mon Sep 17 00:00:00 2001 From: Pawkkie <61265402+Pawkkie@users.noreply.github.com> Date: Thu, 10 Oct 2024 06:54:35 -0400 Subject: [PATCH 48/65] ShouldPivot type cleanup (#5441) * Cleanup ShouldPivot type * PIVOT to SHOULD_PIVOT --- include/battle_ai_util.h | 9 ++++++++- src/battle_ai_main.c | 6 +++--- src/battle_ai_util.c | 43 ++++++++++++++++++---------------------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 69cf92ed0d..6d94c0b992 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -15,6 +15,13 @@ enum DamageRollType DMG_ROLL_HIGHEST, }; +enum AIPivot +{ + DONT_PIVOT, + CAN_TRY_PIVOT, + SHOULD_PIVOT, +}; + bool32 AI_IsFaster(u32 battlerAi, u32 battlerDef, u32 move); bool32 AI_IsSlower(u32 battlerAi, u32 battlerDef, u32 move); bool32 AI_RandLessThan(u32 val); @@ -65,7 +72,7 @@ u32 GetBattlerSideSpeedAverage(u32 battler); bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, u32 move, s32 damage); bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent); bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, u32 moveEffect); -bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 moveIndex); +enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 moveIndex); bool32 IsRecycleEncouragedItem(u32 item); bool32 ShouldRestoreHpBerry(u32 battlerAtk, u32 item); bool32 IsStatBoostingBerry(u32 item); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index a08ca0f71f..47f73aaea4 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -3634,12 +3634,12 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) { switch (ShouldPivot(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, movesetIndex)) { - case 0: // no + case DONT_PIVOT: ADJUST_SCORE(-10); // technically should go in CheckBadMove, but this is easier/less computationally demanding break; - case 1: // maybe + case CAN_TRY_PIVOT: break; - case 2: // yes + case SHOULD_PIVOT: ADJUST_SCORE(BEST_EFFECT); break; } diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index bf84f98b4d..12a3a633a7 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2685,12 +2685,7 @@ static bool32 PartyBattlerShouldAvoidHazards(u32 currBattler, u32 switchBattler) return FALSE; } -enum { - DONT_PIVOT, - CAN_TRY_PIVOT, - PIVOT, -}; -bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 moveIndex) +enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 moveIndex) { bool32 hasStatBoost = AnyUsefulStatIsRaised(battlerAtk) || gBattleMons[battlerDef].statStages[STAT_EVASION] >= 9; //Significant boost in evasion for any class u32 battlerToSwitch; @@ -2707,7 +2702,7 @@ bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 //TODO - predict opponent switching /*if (IsPredictedToSwitch(battlerDef, battlerAtk) && !hasStatBoost) - return PIVOT; // Try pivoting so you can switch to a better matchup to counter your new opponent*/ + return SHOULD_PIVOT; // Try pivoting so you can switch to a better matchup to counter your new opponent*/ if (AI_IsFaster(battlerAtk, battlerDef, move)) // Attacker goes first { @@ -2717,14 +2712,14 @@ bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 { // attacker can kill target in two hits (theoretically) if (CanTargetFaintAi(battlerDef, battlerAtk)) - return PIVOT; // Won't get the two turns, pivot + return SHOULD_PIVOT; // Won't get the two turns, pivot if (!IS_MOVE_STATUS(move) && ((AI_DATA->shouldSwitch & (1u << battlerAtk)) || (AtMaxHp(battlerDef) && (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_FOCUS_SASH || (B_STURDY >= GEN_5 && defAbility == ABILITY_STURDY) || defAbility == ABILITY_MULTISCALE || defAbility == ABILITY_SHADOW_SHIELD)))) - return PIVOT; // pivot to break sash/sturdy/multiscale + return SHOULD_PIVOT; // pivot to break sash/sturdy/multiscale } else if (!hasStatBoost) { @@ -2732,17 +2727,17 @@ bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 || (B_STURDY >= GEN_5 && defAbility == ABILITY_STURDY) || defAbility == ABILITY_MULTISCALE || defAbility == ABILITY_SHADOW_SHIELD))) - return PIVOT; // pivot to break sash/sturdy/multiscale + return SHOULD_PIVOT; // pivot to break sash/sturdy/multiscale if (AI_DATA->shouldSwitch & (1u << battlerAtk)) - return PIVOT; + return SHOULD_PIVOT; /* TODO - check if switchable mon unafffected by/will remove hazards if (gSideStatuses[battlerAtk] & SIDE_STATUS_SPIKES && switchScore >= SWITCHING_INCREASE_CAN_REMOVE_HAZARDS) - return PIVOT;*/ + return SHOULD_PIVOT;*/ /*if (BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->abilities[battlerAtk]) && switchScore >= SWITCHING_INCREASE_WALLS_FOE) - return PIVOT;*/ + return SHOULD_PIVOT;*/ /*if (IsClassDamager(class) && switchScore >= SWITCHING_INCREASE_HAS_SUPER_EFFECTIVE_MOVE) { @@ -2753,20 +2748,20 @@ bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 if (physMoveInMoveset && !specMoveInMoveset) { if (STAT_STAGE_ATK < 6) - return PIVOT; + return SHOULD_PIVOT; } else if (!physMoveInMoveset && specMoveInMoveset) { if (STAT_STAGE_SPATK < 6) - return PIVOT; + return SHOULD_PIVOT; } else if (physMoveInMoveset && specMoveInMoveset) { if (STAT_STAGE_ATK < 6 && STAT_STAGE_SPATK < 6) - return PIVOT; + return SHOULD_PIVOT; } - return CAN_TRY_PIVOT; + return SHOULD_PIVOT; }*/ } } @@ -2789,7 +2784,7 @@ bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 } else // Can't KO the foe { - return PIVOT; + return SHOULD_PIVOT; } } else // Foe can 3HKO+ AI @@ -2815,17 +2810,17 @@ bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 else { //if (IsClassDamager(class) && switchScore >= SWITCHING_INCREASE_KO_FOE) - //return PIVOT; //Only switch if way better matchup + //return SHOULD_PIVOT; //Only switch if way better matchup if (!hasStatBoost) { // TODO - check if switching prevents/removes hazards //if (gSideStatuses[battlerAtk] & SIDE_STATUS_SPIKES && switchScore >= SWITCHING_INCREASE_CAN_REMOVE_HAZARDS) - //return PIVOT; + //return SHOULD_PIVOT; // TODO - not always a good idea //if (BattlerWillFaintFromSecondaryDamage(battlerAtk) && switchScore >= SWITCHING_INCREASE_HAS_SUPER_EFFECTIVE_MOVE) - //return PIVOT; + //return SHOULD_PIVOT; /*if (IsClassDamager(class) && switchScore >= SWITCHING_INCREASE_HAS_SUPER_EFFECTIVE_MOVE) { @@ -2836,17 +2831,17 @@ bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 if (physMoveInMoveset && !specMoveInMoveset) { if (STAT_STAGE_ATK < 6) - return PIVOT; + return SHOULD_PIVOT; } else if (!physMoveInMoveset && specMoveInMoveset) { if (STAT_STAGE_SPATK < 6) - return PIVOT; + return SHOULD_PIVOT; } else if (physMoveInMoveset && specMoveInMoveset) { if (STAT_STAGE_ATK < 6 && STAT_STAGE_SPATK < 6) - return PIVOT; + return SHOULD_PIVOT; } }*/ From d0379b9b55ccc185d7ea80d152d7998d8cfabdd5 Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Thu, 10 Oct 2024 14:23:59 +0200 Subject: [PATCH 49/65] Turn item hold effects into an enum (#5498) * Turn item hold effects into an enum * Rename to ItemHoldEffect --- include/battle_ai_util.h | 3 - include/constants/hold_effects.h | 324 +++++++++++++++---------------- 2 files changed, 160 insertions(+), 167 deletions(-) diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 6d94c0b992..d900f4dd69 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -87,7 +87,6 @@ bool32 AnyStatIsRaised(u32 battlerId); bool32 ShouldLowerStat(u32 battler, u32 battlerAbility, u32 stat); bool32 BattlerStatCanRise(u32 battler, u32 battlerAbility, u32 stat); bool32 AreBattlersStatsMaxed(u32 battler); -bool32 BattlerHasAnyStatRaised(u32 battlerId); u32 CountPositiveStatStages(u32 battlerId); u32 CountNegativeStatStages(u32 battlerId); bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u32 defAbility); @@ -127,7 +126,6 @@ bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool bool32 HasAnyKnownMove(u32 battlerId); bool32 IsAromaVeilProtectedMove(u32 move); bool32 IsNonVolatileStatusMoveEffect(u32 moveEffect); -bool32 IsStatLoweringMoveEffect(u32 moveEffect); bool32 IsMoveRedirectionPrevented(u32 move, u32 atkAbility); bool32 IsMoveEncouragedToHit(u32 battlerAtk, u32 battlerDef, u32 move); bool32 IsHazardMoveEffect(u32 moveEffect); @@ -180,7 +178,6 @@ bool32 AI_IsBattlerAsleepOrComatose(u32 battlerId); #define IS_TARGETING_PARTNER(battlerAtk, battlerDef)((battlerAtk) == (battlerDef ^ BIT_FLANK)) u32 GetAllyChosenMove(u32 battlerId); bool32 IsValidDoubleBattle(u32 battlerAtk); -bool32 IsTargetingPartner(u32 battlerAtk, u32 battlerDef); bool32 DoesPartnerHaveSameMoveEffect(u32 battlerAtkPartner, u32 battlerDef, u32 move, u32 partnerMove); bool32 PartnerHasSameMoveEffectWithoutTarget(u32 battlerAtkPartner, u32 move, u32 partnerMove); bool32 PartnerMoveEffectIsStatusSameTarget(u32 battlerAtkPartner, u32 battlerDef, u32 partnerMove); diff --git a/include/constants/hold_effects.h b/include/constants/hold_effects.h index a159102932..c85ec82302 100644 --- a/include/constants/hold_effects.h +++ b/include/constants/hold_effects.h @@ -1,171 +1,167 @@ #ifndef GUARD_HOLD_EFFECTS_H #define GUARD_HOLD_EFFECTS_H -#define HOLD_EFFECT_NONE 0 -#define HOLD_EFFECT_RESTORE_HP 1 -#define HOLD_EFFECT_CURE_PAR 2 -#define HOLD_EFFECT_CURE_SLP 3 -#define HOLD_EFFECT_CURE_PSN 4 -#define HOLD_EFFECT_CURE_BRN 5 -#define HOLD_EFFECT_CURE_FRZ 6 -#define HOLD_EFFECT_RESTORE_PP 7 -#define HOLD_EFFECT_CURE_CONFUSION 8 -#define HOLD_EFFECT_CURE_STATUS 9 -#define HOLD_EFFECT_CONFUSE_SPICY 10 -#define HOLD_EFFECT_CONFUSE_DRY 11 -#define HOLD_EFFECT_CONFUSE_SWEET 12 -#define HOLD_EFFECT_CONFUSE_BITTER 13 -#define HOLD_EFFECT_CONFUSE_SOUR 14 -#define HOLD_EFFECT_ATTACK_UP 15 -#define HOLD_EFFECT_DEFENSE_UP 16 -#define HOLD_EFFECT_SPEED_UP 17 -#define HOLD_EFFECT_SP_ATTACK_UP 18 -#define HOLD_EFFECT_SP_DEFENSE_UP 19 -#define HOLD_EFFECT_CRITICAL_UP 20 -#define HOLD_EFFECT_RANDOM_STAT_UP 21 -#define HOLD_EFFECT_EVASION_UP 22 -#define HOLD_EFFECT_RESTORE_STATS 23 -#define HOLD_EFFECT_MACHO_BRACE 24 -#define HOLD_EFFECT_EXP_SHARE 25 -#define HOLD_EFFECT_QUICK_CLAW 26 -#define HOLD_EFFECT_FRIENDSHIP_UP 27 -#define HOLD_EFFECT_MENTAL_HERB 28 -#define HOLD_EFFECT_CHOICE_BAND 29 -#define HOLD_EFFECT_FLINCH 30 -#define HOLD_EFFECT_BUG_POWER 31 -#define HOLD_EFFECT_DOUBLE_PRIZE 32 -#define HOLD_EFFECT_REPEL 33 -#define HOLD_EFFECT_SOUL_DEW 34 -#define HOLD_EFFECT_DEEP_SEA_TOOTH 35 -#define HOLD_EFFECT_DEEP_SEA_SCALE 36 -#define HOLD_EFFECT_CAN_ALWAYS_RUN 37 -#define HOLD_EFFECT_PREVENT_EVOLVE 38 -#define HOLD_EFFECT_FOCUS_BAND 39 -#define HOLD_EFFECT_LUCKY_EGG 40 -#define HOLD_EFFECT_SCOPE_LENS 41 -#define HOLD_EFFECT_STEEL_POWER 42 -#define HOLD_EFFECT_LEFTOVERS 43 -#define HOLD_EFFECT_DRAGON_SCALE 44 -#define HOLD_EFFECT_LIGHT_BALL 45 -#define HOLD_EFFECT_GROUND_POWER 46 -#define HOLD_EFFECT_ROCK_POWER 47 -#define HOLD_EFFECT_GRASS_POWER 48 -#define HOLD_EFFECT_DARK_POWER 49 -#define HOLD_EFFECT_FIGHTING_POWER 50 -#define HOLD_EFFECT_ELECTRIC_POWER 51 -#define HOLD_EFFECT_WATER_POWER 52 -#define HOLD_EFFECT_FLYING_POWER 53 -#define HOLD_EFFECT_POISON_POWER 54 -#define HOLD_EFFECT_ICE_POWER 55 -#define HOLD_EFFECT_GHOST_POWER 56 -#define HOLD_EFFECT_PSYCHIC_POWER 57 -#define HOLD_EFFECT_FIRE_POWER 58 -#define HOLD_EFFECT_DRAGON_POWER 59 -#define HOLD_EFFECT_NORMAL_POWER 60 -#define HOLD_EFFECT_UPGRADE 61 -#define HOLD_EFFECT_SHELL_BELL 62 -#define HOLD_EFFECT_LUCKY_PUNCH 63 -#define HOLD_EFFECT_METAL_POWDER 64 -#define HOLD_EFFECT_THICK_CLUB 65 -#define HOLD_EFFECT_LEEK 66 +enum ItemHoldEffect +{ + HOLD_EFFECT_NONE, + HOLD_EFFECT_RESTORE_HP, + HOLD_EFFECT_CURE_PAR, + HOLD_EFFECT_CURE_SLP, + HOLD_EFFECT_CURE_PSN, + HOLD_EFFECT_CURE_BRN, + HOLD_EFFECT_CURE_FRZ, + HOLD_EFFECT_RESTORE_PP, + HOLD_EFFECT_CURE_CONFUSION, + HOLD_EFFECT_CURE_STATUS, + HOLD_EFFECT_CONFUSE_SPICY, + HOLD_EFFECT_CONFUSE_DRY, + HOLD_EFFECT_CONFUSE_SWEET, + HOLD_EFFECT_CONFUSE_BITTER, + HOLD_EFFECT_CONFUSE_SOUR, + HOLD_EFFECT_ATTACK_UP, + HOLD_EFFECT_DEFENSE_UP, + HOLD_EFFECT_SPEED_UP, + HOLD_EFFECT_SP_ATTACK_UP, + HOLD_EFFECT_SP_DEFENSE_UP, + HOLD_EFFECT_CRITICAL_UP, + HOLD_EFFECT_RANDOM_STAT_UP, + HOLD_EFFECT_EVASION_UP, + HOLD_EFFECT_RESTORE_STATS, + HOLD_EFFECT_MACHO_BRACE, + HOLD_EFFECT_EXP_SHARE, + HOLD_EFFECT_QUICK_CLAW, + HOLD_EFFECT_FRIENDSHIP_UP, + HOLD_EFFECT_MENTAL_HERB, + HOLD_EFFECT_CHOICE_BAND, + HOLD_EFFECT_FLINCH, + HOLD_EFFECT_BUG_POWER, + HOLD_EFFECT_DOUBLE_PRIZE, + HOLD_EFFECT_REPEL, + HOLD_EFFECT_SOUL_DEW, + HOLD_EFFECT_DEEP_SEA_TOOTH, + HOLD_EFFECT_DEEP_SEA_SCALE, + HOLD_EFFECT_CAN_ALWAYS_RUN, + HOLD_EFFECT_PREVENT_EVOLVE, + HOLD_EFFECT_FOCUS_BAND, + HOLD_EFFECT_LUCKY_EGG, + HOLD_EFFECT_SCOPE_LENS, + HOLD_EFFECT_STEEL_POWER, + HOLD_EFFECT_LEFTOVERS, + HOLD_EFFECT_DRAGON_SCALE, + HOLD_EFFECT_LIGHT_BALL, + HOLD_EFFECT_GROUND_POWER, + HOLD_EFFECT_ROCK_POWER, + HOLD_EFFECT_GRASS_POWER, + HOLD_EFFECT_DARK_POWER, + HOLD_EFFECT_FIGHTING_POWER, + HOLD_EFFECT_ELECTRIC_POWER, + HOLD_EFFECT_WATER_POWER, + HOLD_EFFECT_FLYING_POWER, + HOLD_EFFECT_POISON_POWER, + HOLD_EFFECT_ICE_POWER, + HOLD_EFFECT_GHOST_POWER, + HOLD_EFFECT_PSYCHIC_POWER, + HOLD_EFFECT_FIRE_POWER, + HOLD_EFFECT_DRAGON_POWER, + HOLD_EFFECT_NORMAL_POWER, + HOLD_EFFECT_UPGRADE, + HOLD_EFFECT_SHELL_BELL, + HOLD_EFFECT_LUCKY_PUNCH, + HOLD_EFFECT_METAL_POWDER, + HOLD_EFFECT_THICK_CLUB, + HOLD_EFFECT_LEEK, + // Gen4 hold effects. + HOLD_EFFECT_CHOICE_SCARF, + HOLD_EFFECT_CHOICE_SPECS, + HOLD_EFFECT_DAMP_ROCK, + HOLD_EFFECT_GRIP_CLAW, + HOLD_EFFECT_HEAT_ROCK, + HOLD_EFFECT_ICY_ROCK, + HOLD_EFFECT_LIGHT_CLAY, + HOLD_EFFECT_SMOOTH_ROCK, + HOLD_EFFECT_POWER_HERB, + HOLD_EFFECT_BIG_ROOT, + HOLD_EFFECT_EXPERT_BELT, + HOLD_EFFECT_LIFE_ORB, + HOLD_EFFECT_METRONOME, + HOLD_EFFECT_MUSCLE_BAND, + HOLD_EFFECT_WIDE_LENS, + HOLD_EFFECT_WISE_GLASSES, + HOLD_EFFECT_ZOOM_LENS, + HOLD_EFFECT_LAGGING_TAIL, + HOLD_EFFECT_FOCUS_SASH, + HOLD_EFFECT_FLAME_ORB, + HOLD_EFFECT_TOXIC_ORB, + HOLD_EFFECT_STICKY_BARB, + HOLD_EFFECT_IRON_BALL, + HOLD_EFFECT_BLACK_SLUDGE, + HOLD_EFFECT_DESTINY_KNOT, + HOLD_EFFECT_SHED_SHELL, + HOLD_EFFECT_QUICK_POWDER, + HOLD_EFFECT_ADAMANT_ORB, + HOLD_EFFECT_LUSTROUS_ORB, + HOLD_EFFECT_GRISEOUS_ORB, + HOLD_EFFECT_ENIGMA_BERRY, + HOLD_EFFECT_RESIST_BERRY, + HOLD_EFFECT_POWER_ITEM, + HOLD_EFFECT_RESTORE_PCT_HP, + HOLD_EFFECT_MICLE_BERRY, + HOLD_EFFECT_CUSTAP_BERRY, + HOLD_EFFECT_JABOCA_BERRY, + HOLD_EFFECT_ROWAP_BERRY, + HOLD_EFFECT_KEE_BERRY, + HOLD_EFFECT_MARANGA_BERRY, + HOLD_EFFECT_PLATE, + // Gen5 hold effects + HOLD_EFFECT_FLOAT_STONE, + HOLD_EFFECT_EVIOLITE, + HOLD_EFFECT_ASSAULT_VEST, + HOLD_EFFECT_DRIVE, + HOLD_EFFECT_GEMS, + HOLD_EFFECT_ROCKY_HELMET, + HOLD_EFFECT_AIR_BALLOON, + HOLD_EFFECT_RED_CARD, + HOLD_EFFECT_RING_TARGET, + HOLD_EFFECT_BINDING_BAND, + HOLD_EFFECT_EJECT_BUTTON, + HOLD_EFFECT_ABSORB_BULB, + HOLD_EFFECT_CELL_BATTERY, + // Gen6 hold effects + HOLD_EFFECT_FAIRY_POWER, + HOLD_EFFECT_MEGA_STONE, + HOLD_EFFECT_SAFETY_GOGGLES, + HOLD_EFFECT_LUMINOUS_MOSS, + HOLD_EFFECT_SNOWBALL, + HOLD_EFFECT_WEAKNESS_POLICY, + HOLD_EFFECT_PRIMAL_ORB, + // Gen7 hold effects + HOLD_EFFECT_PROTECTIVE_PADS, + HOLD_EFFECT_TERRAIN_EXTENDER, + HOLD_EFFECT_SEEDS, + HOLD_EFFECT_ADRENALINE_ORB, + HOLD_EFFECT_MEMORY, + HOLD_EFFECT_Z_CRYSTAL, + // Gen8 hold effects + HOLD_EFFECT_UTILITY_UMBRELLA, + HOLD_EFFECT_EJECT_PACK, + HOLD_EFFECT_ROOM_SERVICE, + HOLD_EFFECT_BLUNDER_POLICY, + HOLD_EFFECT_HEAVY_DUTY_BOOTS, + HOLD_EFFECT_THROAT_SPRAY, + // Gen9 hold effects + HOLD_EFFECT_ABILITY_SHIELD, + HOLD_EFFECT_CLEAR_AMULET, + HOLD_EFFECT_MIRROR_HERB, + HOLD_EFFECT_PUNCHING_GLOVE, + HOLD_EFFECT_COVERT_CLOAK, + HOLD_EFFECT_LOADED_DICE, + HOLD_EFFECT_BOOSTER_ENERGY, + HOLD_EFFECT_OGERPON_MASK, + // Gen2 hold effect + HOLD_EFFECT_BERSERK_GENE, +}; -// Gen4 hold effects. -#define HOLD_EFFECT_CHOICE_SCARF 67 -#define HOLD_EFFECT_CHOICE_SPECS 68 -#define HOLD_EFFECT_DAMP_ROCK 69 -#define HOLD_EFFECT_GRIP_CLAW 70 -#define HOLD_EFFECT_HEAT_ROCK 71 -#define HOLD_EFFECT_ICY_ROCK 72 -#define HOLD_EFFECT_LIGHT_CLAY 73 -#define HOLD_EFFECT_SMOOTH_ROCK 74 -#define HOLD_EFFECT_POWER_HERB 75 -#define HOLD_EFFECT_BIG_ROOT 76 -#define HOLD_EFFECT_EXPERT_BELT 77 -#define HOLD_EFFECT_LIFE_ORB 78 -#define HOLD_EFFECT_METRONOME 79 -#define HOLD_EFFECT_MUSCLE_BAND 80 -#define HOLD_EFFECT_WIDE_LENS 81 -#define HOLD_EFFECT_WISE_GLASSES 82 -#define HOLD_EFFECT_ZOOM_LENS 83 -#define HOLD_EFFECT_LAGGING_TAIL 84 -#define HOLD_EFFECT_FOCUS_SASH 85 -#define HOLD_EFFECT_FLAME_ORB 86 -#define HOLD_EFFECT_TOXIC_ORB 87 -#define HOLD_EFFECT_STICKY_BARB 88 -#define HOLD_EFFECT_IRON_BALL 89 -#define HOLD_EFFECT_BLACK_SLUDGE 90 -#define HOLD_EFFECT_DESTINY_KNOT 91 -#define HOLD_EFFECT_SHED_SHELL 92 -#define HOLD_EFFECT_QUICK_POWDER 93 -#define HOLD_EFFECT_ADAMANT_ORB 94 -#define HOLD_EFFECT_LUSTROUS_ORB 95 -#define HOLD_EFFECT_GRISEOUS_ORB 96 -#define HOLD_EFFECT_ENIGMA_BERRY 97 -#define HOLD_EFFECT_RESIST_BERRY 98 -#define HOLD_EFFECT_POWER_ITEM 99 -#define HOLD_EFFECT_RESTORE_PCT_HP 100 -#define HOLD_EFFECT_MICLE_BERRY 101 -#define HOLD_EFFECT_CUSTAP_BERRY 102 -#define HOLD_EFFECT_JABOCA_BERRY 103 -#define HOLD_EFFECT_ROWAP_BERRY 104 -#define HOLD_EFFECT_KEE_BERRY 105 -#define HOLD_EFFECT_MARANGA_BERRY 106 -#define HOLD_EFFECT_PLATE 107 - -// Gen5 hold effects -#define HOLD_EFFECT_FLOAT_STONE 117 -#define HOLD_EFFECT_EVIOLITE 118 -#define HOLD_EFFECT_ASSAULT_VEST 119 -#define HOLD_EFFECT_DRIVE 120 -#define HOLD_EFFECT_GEMS 121 -#define HOLD_EFFECT_ROCKY_HELMET 122 -#define HOLD_EFFECT_AIR_BALLOON 123 -#define HOLD_EFFECT_RED_CARD 124 -#define HOLD_EFFECT_RING_TARGET 125 -#define HOLD_EFFECT_BINDING_BAND 126 -#define HOLD_EFFECT_EJECT_BUTTON 127 -#define HOLD_EFFECT_ABSORB_BULB 128 -#define HOLD_EFFECT_CELL_BATTERY 129 - -// Gen6 hold effects -#define HOLD_EFFECT_FAIRY_POWER 139 -#define HOLD_EFFECT_MEGA_STONE 140 -#define HOLD_EFFECT_SAFETY_GOGGLES 141 -#define HOLD_EFFECT_LUMINOUS_MOSS 142 -#define HOLD_EFFECT_SNOWBALL 143 -#define HOLD_EFFECT_WEAKNESS_POLICY 144 -#define HOLD_EFFECT_PRIMAL_ORB 145 - -// Gen7 hold effects -#define HOLD_EFFECT_PROTECTIVE_PADS 154 -#define HOLD_EFFECT_TERRAIN_EXTENDER 155 -#define HOLD_EFFECT_SEEDS 156 -#define HOLD_EFFECT_ADRENALINE_ORB 157 -#define HOLD_EFFECT_MEMORY 158 -#define HOLD_EFFECT_Z_CRYSTAL 159 - -// Gen8 hold effects -#define HOLD_EFFECT_UTILITY_UMBRELLA 169 -#define HOLD_EFFECT_EJECT_PACK 170 -#define HOLD_EFFECT_ROOM_SERVICE 171 -#define HOLD_EFFECT_BLUNDER_POLICY 172 -#define HOLD_EFFECT_HEAVY_DUTY_BOOTS 173 -#define HOLD_EFFECT_THROAT_SPRAY 174 - -// Gen9 hold effects -#define HOLD_EFFECT_ABILITY_SHIELD 175 -#define HOLD_EFFECT_CLEAR_AMULET 176 -#define HOLD_EFFECT_MIRROR_HERB 177 -#define HOLD_EFFECT_PUNCHING_GLOVE 178 -#define HOLD_EFFECT_COVERT_CLOAK 179 -#define HOLD_EFFECT_LOADED_DICE 180 -#define HOLD_EFFECT_BOOSTER_ENERGY 181 -#define HOLD_EFFECT_OGERPON_MASK 182 - -// Gen2 hold effect -#define HOLD_EFFECT_BERSERK_GENE 184 - -#define HOLD_EFFECT_CHOICE(holdEffect)((holdEffect == HOLD_EFFECT_CHOICE_BAND || holdEffect == HOLD_EFFECT_CHOICE_SCARF || holdEffect == HOLD_EFFECT_CHOICE_SPECS)) +#define HOLD_EFFECT_CHOICE(holdEffect) ((holdEffect == HOLD_EFFECT_CHOICE_BAND || holdEffect == HOLD_EFFECT_CHOICE_SCARF || holdEffect == HOLD_EFFECT_CHOICE_SPECS)) // Terrain seed params #define HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN 0 From 8d2d62273bd0d3565c4edb211ae4cf6a6b35b258 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Thu, 10 Oct 2024 17:36:01 +0100 Subject: [PATCH 50/65] Updated ability popups for Skill Swap, Mummy/Lingering Aroma, Worry Seed, Simple Beam, fix Doodle/Role Play bugs (#5493) * Updated ability popups of Skill Swap, Mummy/Lingering Aroma, Worry Seed, Simple Beam, Fix Doodle and Role Play issues * More Doodle fixes * Add tests * Fix Doodle not activating if partner is fainted and its ability cannot be suppressed * Fix tests compile * Commander cantBeOverwritten * Add battle script for Overwrite then Regular ability --- data/battle_scripts_1.s | 94 +++++++++++++--------- src/battle_message.c | 4 +- src/battle_script_commands.c | 12 ++- src/battle_util.c | 3 +- src/data/abilities.h | 1 + test/battle/ability/mummy.c | 12 +-- test/battle/move_effect/doodle.c | 40 +++++++++- test/battle/move_effect/role_play.c | 74 ++++++++++++++--- test/battle/move_effect/simple_beam.c | 74 +++++++++++++++++ test/battle/move_effect/skill_swap.c | 111 ++++++++++++++++++++++++++ test/battle/move_effect/worry_seed.c | 74 +++++++++++++++++ 11 files changed, 432 insertions(+), 67 deletions(-) create mode 100644 test/battle/move_effect/simple_beam.c create mode 100644 test/battle/move_effect/skill_swap.c create mode 100644 test/battle/move_effect/worry_seed.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 94f5736317..fa56c3ff7a 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -214,27 +214,25 @@ BattleScript_EffectDoodle:: attackcanceler attackstring ppreduce + trycopyability BS_ATTACKER, BattleScript_ButItFailed attackanimation waitanimation setbyte gBattleCommunication, 0 + goto BattleScript_EffectDoodle_AfterCopy BattleScript_EffectDoodle_CopyAbility: - trycopyability BS_ATTACKER, BattleScript_ButItFailed + trycopyability BS_ATTACKER, BattleScript_MoveEnd +BattleScript_EffectDoodle_AfterCopy: .if B_ABILITY_POP_UP == TRUE - setbyte sFIXED_ABILITY_POPUP, TRUE - showabilitypopup BS_ATTACKER - pause 60 - sethword sABILITY_OVERWRITE, 0 - updateabilitypopup BS_ATTACKER - pause 20 - destroyabilitypopup - pause 40 + copybyte gBattlerAbility, gBattlerAttacker + call BattleScript_AbilityPopUpOverwriteThenNormal .endif + recordability BS_ATTACKER printstring STRINGID_PKMNCOPIEDFOE waitmessage B_WAIT_TIME_LONG switchinabilities BS_ATTACKER jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0x0, BattleScript_MoveEnd addbyte gBattleCommunication, 1 - jumpifnoally BS_TARGET, BattleScript_MoveEnd + jumpifnoally BS_ATTACKER, BattleScript_MoveEnd setallytonextattacker BattleScript_EffectDoodle_CopyAbility goto BattleScript_MoveEnd @@ -2346,6 +2344,11 @@ BattleScript_EffectSimpleBeam:: setabilitysimple BS_TARGET, BattleScript_ButItFailed attackanimation waitanimation +.if B_ABILITY_POP_UP == TRUE + copybyte gBattlerAbility, gBattlerTarget + call BattleScript_AbilityPopUpOverwriteThenNormal +.endif + recordability BS_TARGET printstring STRINGID_PKMNACQUIREDSIMPLE waitmessage B_WAIT_TIME_LONG trytoclearprimalweather @@ -2444,11 +2447,17 @@ BattleScript_EffectWorrySeed:: tryworryseed BattleScript_ButItFailed attackanimation waitanimation +.if B_ABILITY_POP_UP == TRUE + copybyte gBattlerAbility, gBattlerTarget + call BattleScript_AbilityPopUpOverwriteThenNormal +.endif + recordability BS_TARGET printstring STRINGID_PKMNACQUIREDABILITY waitmessage B_WAIT_TIME_LONG trytoclearprimalweather tryrevertweatherform flushtextbox + tryendneutralizinggas BS_TARGET goto BattleScript_MoveEnd BattleScript_EffectPowerSplit:: @@ -5034,15 +5043,10 @@ BattleScript_EffectRolePlay:: attackanimation waitanimation .if B_ABILITY_POP_UP == TRUE - setbyte sFIXED_ABILITY_POPUP, TRUE - showabilitypopup BS_ATTACKER - pause 60 - sethword sABILITY_OVERWRITE, 0 - updateabilitypopup BS_ATTACKER - pause 20 - destroyabilitypopup - pause 40 + copybyte gBattlerAbility, gBattlerAttacker + call BattleScript_AbilityPopUpOverwriteThenNormal .endif + recordability BS_ATTACKER printstring STRINGID_PKMNCOPIEDFOE waitmessage B_WAIT_TIME_LONG switchinabilities BS_ATTACKER @@ -5185,12 +5189,17 @@ BattleScript_EffectSkillSwap:: tryswapabilities BattleScript_ButItFailed attackanimation waitanimation + jumpiftargetally BattleScript_EffectSkillSwap_AfterAbilityPopUp .if B_ABILITY_POP_UP == TRUE - call BattleScript_AbilityPopUpTarget - pause 20 copybyte gBattlerAbility, gBattlerAttacker - call BattleScript_AbilityPopUp + call BattleScript_AbilityPopUpOverwriteThenNormal + copybyte gBattlerAbility, gBattlerTarget + copyhword sABILITY_OVERWRITE, gLastUsedAbility + call BattleScript_AbilityPopUpOverwriteThenNormal .endif +BattleScript_EffectSkillSwap_AfterAbilityPopUp: + recordability BS_ATTACKER + recordability BS_TARGET printstring STRINGID_PKMNSWAPPEDABILITIES waitmessage B_WAIT_TIME_LONG .if B_SKILL_SWAP >= GEN_4 @@ -7605,6 +7614,18 @@ BattleScript_AbilityPopUpScripting: sethword sABILITY_OVERWRITE, 0 return +BattleScript_AbilityPopUpOverwriteThenNormal: + setbyte sFIXED_ABILITY_POPUP, TRUE + showabilitypopup BS_ABILITY_BATTLER + pause 60 + sethword sABILITY_OVERWRITE, 0 + updateabilitypopup BS_ABILITY_BATTLER + pause 20 + recordability BS_ABILITY_BATTLER + destroyabilitypopup + pause 40 + return + BattleScript_SpeedBoostActivates:: statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_SpeedBoostActivatesEnd call BattleScript_AbilityPopUp @@ -8366,33 +8387,30 @@ BattleScript_CursedBodyActivates:: return BattleScript_MummyActivates:: - call BattleScript_AbilityPopUp +.if B_ABILITY_POP_UP == TRUE + call BattleScript_AbilityPopUpTarget + setbyte sFIXED_ABILITY_POPUP, TRUE + copybyte gBattlerAbility, gBattlerAttacker + copyhword sABILITY_OVERWRITE, gLastUsedAbility + call BattleScript_AbilityPopUpOverwriteThenNormal +.endif + recordability BS_TARGET + recordability BS_ATTACKER printstring STRINGID_ATTACKERACQUIREDABILITY waitmessage B_WAIT_TIME_LONG return BattleScript_WanderingSpiritActivates:: .if B_ABILITY_POP_UP == TRUE - setbyte sFIXED_ABILITY_POPUP, TRUE + copybyte gBattlerAbility, gBattlerTarget sethword sABILITY_OVERWRITE, ABILITY_WANDERING_SPIRIT - showabilitypopup BS_TARGET - pause 60 - sethword sABILITY_OVERWRITE, 0 - updateabilitypopup BS_TARGET - pause 20 - destroyabilitypopup - pause 40 + call BattleScript_AbilityPopUpOverwriteThenNormal copybyte gBattlerAbility, gBattlerAttacker - setbyte sFIXED_ABILITY_POPUP, TRUE copyhword sABILITY_OVERWRITE, gLastUsedAbility - showabilitypopup BS_ATTACKER - pause 60 - sethword sABILITY_OVERWRITE, 0 - updateabilitypopup BS_ATTACKER - pause 20 - destroyabilitypopup - pause 40 + call BattleScript_AbilityPopUpOverwriteThenNormal .endif + recordability BS_TARGET + recordability BS_ATTACKER printstring STRINGID_SWAPPEDABILITIES waitmessage B_WAIT_TIME_LONG switchinabilities BS_ATTACKER diff --git a/src/battle_message.c b/src/battle_message.c index ecb2932124..52c6dec6ae 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -640,7 +640,7 @@ static const u8 sText_HealingWishCameTrue[] = _("The healing wish came true\nfor static const u8 sText_HealingWishHealed[] = _("{B_ATK_NAME_WITH_PREFIX} regained health!"); static const u8 sText_LunarDanceCameTrue[] = _("{B_ATK_NAME_WITH_PREFIX} became cloaked\nin mystical moonlight!"); static const u8 sText_CursedBodyDisabled[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_BUFF1} was disabled\nby {B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY}!"); -static const u8 sText_AttackerAquiredAbility[] = _("{B_ATK_NAME_WITH_PREFIX} acquired\n{B_LAST_ABILITY}!"); +static const u8 sText_AttackerAcquiredAbility[] = _("{B_ATK_NAME_WITH_PREFIX} acquired\n{B_ATK_ABILITY}!"); static const u8 sText_TargetStatWontGoHigher[] = _("{B_DEF_NAME_WITH_PREFIX}'s {B_BUFF1}\nwon't go higher!"); static const u8 sText_PkmnMoveBouncedViaAbility[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_CURRENT_MOVE} was\nbounced back by {B_DEF_NAME_WITH_PREFIX}'s\l{B_DEF_ABILITY}!"); static const u8 sText_ImposterTransform[] = _("{B_ATK_NAME_WITH_PREFIX} transformed into\n{B_DEF_NAME_WITH_PREFIX} using {B_LAST_ABILITY}!"); @@ -1477,7 +1477,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_HEALINGWISHHEALED - BATTLESTRINGS_TABLE_START] = sText_HealingWishHealed, [STRINGID_LUNARDANCECAMETRUE - BATTLESTRINGS_TABLE_START] = sText_LunarDanceCameTrue, [STRINGID_CUSEDBODYDISABLED - BATTLESTRINGS_TABLE_START] = sText_CursedBodyDisabled, - [STRINGID_ATTACKERACQUIREDABILITY - BATTLESTRINGS_TABLE_START] = sText_AttackerAquiredAbility, + [STRINGID_ATTACKERACQUIREDABILITY - BATTLESTRINGS_TABLE_START] = sText_AttackerAcquiredAbility, [STRINGID_TARGETABILITYSTATLOWER - BATTLESTRINGS_TABLE_START] = sText_TargetAbilityLoweredStat, [STRINGID_TARGETSTATWONTGOHIGHER - BATTLESTRINGS_TABLE_START] = sText_TargetStatWontGoHigher, [STRINGID_PKMNMOVEBOUNCEDABILITY - BATTLESTRINGS_TABLE_START] = sText_PkmnMoveBouncedViaAbility, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ba372d1c54..618164dd18 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9782,6 +9782,7 @@ static void Cmd_various(void) if (gBattleMons[gBattlerTarget].ability == ABILITY_NEUTRALIZING_GAS) gSpecialStatuses[gBattlerTarget].neutralizingGasRemoved = TRUE; + gBattleScripting.abilityPopupOverwrite = gBattleMons[gBattlerTarget].ability; gBattleMons[gBattlerTarget].ability = gBattleStruct->overwrittenAbilities[gBattlerTarget] = ABILITY_SIMPLE; gBattlescriptCurrInstr = cmd->nextInstr; } @@ -14369,7 +14370,7 @@ static void Cmd_trycopyability(void) if (gBattleMons[battler].ability == defAbility || defAbility == ABILITY_NONE || gAbilitiesInfo[gBattleMons[battler].ability].cantBeSuppressed - || gAbilitiesInfo[gBattleMons[BATTLE_PARTNER(battler)].ability].cantBeSuppressed + || (IsBattlerAlive(BATTLE_PARTNER(battler)) && gAbilitiesInfo[gBattleMons[BATTLE_PARTNER(battler)].ability].cantBeSuppressed && gMovesInfo[gCurrentMove].effect == EFFECT_DOODLE) || gAbilitiesInfo[defAbility].cantBeCopied) { gBattlescriptCurrInstr = cmd->failInstr; @@ -14565,9 +14566,11 @@ static void Cmd_tryswapabilities(void) } else { - u16 abilityAtk = gBattleMons[gBattlerAttacker].ability; - gBattleMons[gBattlerAttacker].ability = gBattleStruct->overwrittenAbilities[gBattlerAttacker] = gBattleMons[gBattlerTarget].ability; - gBattleMons[gBattlerTarget].ability = gBattleStruct->overwrittenAbilities[gBattlerTarget] = abilityAtk; + if (GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)) + gBattleScripting.abilityPopupOverwrite = gBattleMons[gBattlerAttacker].ability; + gLastUsedAbility = gBattleMons[gBattlerTarget].ability; + gBattleMons[gBattlerTarget].ability = gBattleStruct->overwrittenAbilities[gBattlerTarget] = gBattleMons[gBattlerAttacker].ability; + gBattleMons[gBattlerAttacker].ability = gBattleStruct->overwrittenAbilities[gBattlerAttacker] = gLastUsedAbility; gBattlescriptCurrInstr = cmd->nextInstr; } @@ -15859,6 +15862,7 @@ static void Cmd_tryworryseed(void) } else { + gBattleScripting.abilityPopupOverwrite = gBattleMons[gBattlerTarget].ability; gBattleMons[gBattlerTarget].ability = gBattleStruct->overwrittenAbilities[gBattlerTarget] = ABILITY_INSOMNIA; gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_util.c b/src/battle_util.c index b572acc7c1..e5d8c5f041 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5546,7 +5546,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 break; } - gLastUsedAbility = gBattleMons[gBattlerAttacker].ability = gBattleStruct->overwrittenAbilities[gBattlerAttacker] = gBattleMons[gBattlerTarget].ability; + gLastUsedAbility = gBattleMons[gBattlerAttacker].ability; + gBattleMons[gBattlerAttacker].ability = gBattleStruct->overwrittenAbilities[gBattlerAttacker] = gBattleMons[gBattlerTarget].ability; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MummyActivates; effect++; diff --git a/src/data/abilities.h b/src/data/abilities.h index a85f114fec..ba768a0a51 100644 --- a/src/data/abilities.h +++ b/src/data/abilities.h @@ -2273,6 +2273,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .cantBeSwapped = TRUE, .cantBeTraced = TRUE, .cantBeSuppressed = TRUE, + .cantBeOverwritten = TRUE, }, [ABILITY_ELECTROMORPHOSIS] = diff --git a/test/battle/ability/mummy.c b/test/battle/ability/mummy.c index ed80a178e6..74461ec2ee 100644 --- a/test/battle/ability/mummy.c +++ b/test/battle/ability/mummy.c @@ -70,19 +70,19 @@ SINGLE_BATTLE_TEST("Mummy doesn't replace abilities that can't be suppressed") PARAMETRIZE { species = SPECIES_ARCEUS; ability = ABILITY_MULTITYPE; } PARAMETRIZE { species = SPECIES_AEGISLASH; ability = ABILITY_STANCE_CHANGE; } - PARAMETRIZE { species = SPECIES_WISHIWASHI; ability = ABILITY_SCHOOLING; } - PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } PARAMETRIZE { species = SPECIES_MINIOR; ability = ABILITY_SHIELDS_DOWN; } + PARAMETRIZE { species = SPECIES_WISHIWASHI; ability = ABILITY_SCHOOLING; } PARAMETRIZE { species = SPECIES_MIMIKYU; ability = ABILITY_DISGUISE; } - PARAMETRIZE { species = SPECIES_SILVALLY; ability = ABILITY_RKS_SYSTEM; } PARAMETRIZE { species = SPECIES_GRENINJA_BATTLE_BOND; ability = ABILITY_BATTLE_BOND; } PARAMETRIZE { species = SPECIES_ZYGARDE; ability = ABILITY_POWER_CONSTRUCT; } - PARAMETRIZE { species = SPECIES_EISCUE; ability = ABILITY_ICE_FACE; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_SILVALLY; ability = ABILITY_RKS_SYSTEM; } PARAMETRIZE { species = SPECIES_CRAMORANT; ability = ABILITY_GULP_MISSILE; } + PARAMETRIZE { species = SPECIES_EISCUE; ability = ABILITY_ICE_FACE; } + PARAMETRIZE { species = SPECIES_CALYREX_ICE; ability = ABILITY_AS_ONE_ICE_RIDER; } + PARAMETRIZE { species = SPECIES_CALYREX_SHADOW; ability = ABILITY_AS_ONE_SHADOW_RIDER; } PARAMETRIZE { species = SPECIES_PALAFIN_ZERO; ability = ABILITY_ZERO_TO_HERO; } PARAMETRIZE { species = SPECIES_TATSUGIRI; ability = ABILITY_COMMANDER; } - PARAMETRIZE { species = SPECIES_CALYREX_SHADOW_RIDER; ability = ABILITY_AS_ONE_SHADOW_RIDER; } - PARAMETRIZE { species = SPECIES_CALYREX_ICE_RIDER; ability = ABILITY_AS_ONE_ICE_RIDER; } GIVEN { PLAYER(SPECIES_YAMASK); diff --git a/test/battle/move_effect/doodle.c b/test/battle/move_effect/doodle.c index bf7e208646..ba7729a895 100644 --- a/test/battle/move_effect/doodle.c +++ b/test/battle/move_effect/doodle.c @@ -35,8 +35,8 @@ DOUBLE_BATTLE_TEST("Doodle can't copy a banned ability") } WHEN { TURN { MOVE(playerLeft, MOVE_DOODLE, target: opponentLeft); } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_DOODLE, playerLeft); NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOODLE, playerLeft); MESSAGE("Wynaut copied Foe Great Tusk's Protosynthesis!"); MESSAGE("Wynaut copied Foe Great Tusk's Protosynthesis!"); } @@ -56,7 +56,7 @@ DOUBLE_BATTLE_TEST("Doodle fails if user has a banned Ability") } WHEN { TURN { MOVE(playerLeft, MOVE_DOODLE, target: opponentLeft); } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_DOODLE, playerLeft); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_DOODLE, playerLeft); MESSAGE("But it failed!"); } THEN { EXPECT(playerLeft->ability == ABILITY_GULP_MISSILE); @@ -74,10 +74,44 @@ DOUBLE_BATTLE_TEST("Doodle fails if partner has a banned Ability") } WHEN { TURN { MOVE(playerLeft, MOVE_DOODLE, target: opponentLeft); } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_DOODLE, playerLeft); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_DOODLE, playerLeft); MESSAGE("But it failed!"); } THEN { EXPECT(playerLeft->ability == ABILITY_SHADOW_TAG); EXPECT(playerRight->ability == ABILITY_GULP_MISSILE); } } + +DOUBLE_BATTLE_TEST("Doodle fails if ally's ability can't be suppressed") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_ARCEUS; ability = ABILITY_MULTITYPE; } + PARAMETRIZE { species = SPECIES_DARMANITAN; ability = ABILITY_ZEN_MODE; } + PARAMETRIZE { species = SPECIES_AEGISLASH; ability = ABILITY_STANCE_CHANGE; } + PARAMETRIZE { species = SPECIES_MINIOR; ability = ABILITY_SHIELDS_DOWN; } + PARAMETRIZE { species = SPECIES_WISHIWASHI; ability = ABILITY_SCHOOLING; } + PARAMETRIZE { species = SPECIES_MIMIKYU; ability = ABILITY_DISGUISE; } + PARAMETRIZE { species = SPECIES_GRENINJA_BATTLE_BOND; ability = ABILITY_BATTLE_BOND; } + PARAMETRIZE { species = SPECIES_ZYGARDE; ability = ABILITY_POWER_CONSTRUCT; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_SILVALLY; ability = ABILITY_RKS_SYSTEM; } + PARAMETRIZE { species = SPECIES_CRAMORANT; ability = ABILITY_GULP_MISSILE; } + PARAMETRIZE { species = SPECIES_EISCUE; ability = ABILITY_ICE_FACE; } + PARAMETRIZE { species = SPECIES_CALYREX_ICE; ability = ABILITY_AS_ONE_ICE_RIDER; } + PARAMETRIZE { species = SPECIES_CALYREX_SHADOW; ability = ABILITY_AS_ONE_SHADOW_RIDER; } + PARAMETRIZE { species = SPECIES_PALAFIN_ZERO; ability = ABILITY_ZERO_TO_HERO; } + PARAMETRIZE { species = SPECIES_TATSUGIRI; ability = ABILITY_COMMANDER; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + OPPONENT(species) { Ability(ability); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_DOODLE, target: playerLeft); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_DOODLE, opponentLeft); + MESSAGE("But it failed!"); + } +} diff --git a/test/battle/move_effect/role_play.c b/test/battle/move_effect/role_play.c index 46452ec3ff..ab0d801ee9 100644 --- a/test/battle/move_effect/role_play.c +++ b/test/battle/move_effect/role_play.c @@ -1,25 +1,65 @@ #include "global.h" #include "test/battle.h" -// Technically also covers Skill Swap and Doodle since both moves use the same command as Role Play ASSUMPTIONS { ASSUME(gMovesInfo[MOVE_ROLE_PLAY].effect == EFFECT_ROLE_PLAY); } -SINGLE_BATTLE_TEST("Role Play fails if target has a banned ability") +SINGLE_BATTLE_TEST("Role Play copies target's ability") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + OPPONENT(SPECIES_CHARMANDER) { Ability(ABILITY_BLAZE); } + }WHEN { + TURN { MOVE(player, MOVE_ROLE_PLAY); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ROLE_PLAY, player); + ABILITY_POPUP(player, ABILITY_TELEPATHY); + } THEN { + EXPECT_EQ(player->ability, ABILITY_BLAZE); + EXPECT_EQ(opponent->ability, ABILITY_BLAZE); + } +} + +DOUBLE_BATTLE_TEST("Role Play copies target's current ability even if it changed during that turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + PLAYER(SPECIES_CHARMANDER) { Ability(ABILITY_BLAZE); } + OPPONENT(SPECIES_BULBASAUR) { Ability(ABILITY_OVERGROW); } + OPPONENT(SPECIES_SQUIRTLE) { Ability(ABILITY_TORRENT); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_ROLE_PLAY, target: opponentLeft); MOVE(opponentRight, MOVE_ROLE_PLAY, target: playerLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ROLE_PLAY, playerLeft); + ABILITY_POPUP(playerLeft, ABILITY_TELEPATHY); + if (MOVE_ROLE_PLAY == MOVE_DOODLE) + ABILITY_POPUP(playerRight, ABILITY_BLAZE); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ROLE_PLAY, opponentRight); + ABILITY_POPUP(opponentRight, ABILITY_TORRENT); + NOT ABILITY_POPUP(opponentLeft, ABILITY_OVERGROW); // Already has ability (Doodle) + } THEN { + EXPECT_EQ(playerLeft->ability, ABILITY_OVERGROW); + if (MOVE_ROLE_PLAY == MOVE_DOODLE) + EXPECT_EQ(playerRight->ability, ABILITY_OVERGROW); + EXPECT_EQ(opponentLeft->ability, ABILITY_OVERGROW); + EXPECT_EQ(opponentRight->ability, ABILITY_OVERGROW); + } +} + +SINGLE_BATTLE_TEST("Role Play and Doodle fail if target's ability can't be copied'") { u32 species, ability; PARAMETRIZE { species = SPECIES_SHEDINJA; ability = ABILITY_WONDER_GUARD; } PARAMETRIZE { species = SPECIES_CASTFORM; ability = ABILITY_FORECAST; } - PARAMETRIZE { species = SPECIES_CHERRIM; ability = ABILITY_FLOWER_GIFT; } PARAMETRIZE { species = SPECIES_ARCEUS; ability = ABILITY_MULTITYPE; } + PARAMETRIZE { species = SPECIES_CHERRIM; ability = ABILITY_FLOWER_GIFT; } PARAMETRIZE { species = SPECIES_ZORUA; ability = ABILITY_ILLUSION; } PARAMETRIZE { species = SPECIES_DARMANITAN; ability = ABILITY_ZEN_MODE; } - PARAMETRIZE { species = SPECIES_DITTO; ability = ABILITY_IMPOSTER; } PARAMETRIZE { species = SPECIES_AEGISLASH; ability = ABILITY_STANCE_CHANGE; } - PARAMETRIZE { species = SPECIES_MUK_ALOLAN; ability = ABILITY_POWER_OF_ALCHEMY; } + PARAMETRIZE { species = SPECIES_MUK_ALOLA; ability = ABILITY_POWER_OF_ALCHEMY; } PARAMETRIZE { species = SPECIES_PASSIMIAN; ability = ABILITY_RECEIVER; } PARAMETRIZE { species = SPECIES_WISHIWASHI; ability = ABILITY_SCHOOLING; } PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } @@ -38,29 +78,34 @@ SINGLE_BATTLE_TEST("Role Play fails if target has a banned ability") PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } OPPONENT(species) { Ability(ability); } } WHEN { - TURN { MOVE(player,MOVE_ROLE_PLAY); } + TURN { MOVE(player, MOVE_ROLE_PLAY); } + TURN { MOVE(player, MOVE_DOODLE); } } SCENE { NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ROLE_PLAY, player); MESSAGE("But it failed!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_DOODLE, player); + MESSAGE("But it failed!"); } } -SINGLE_BATTLE_TEST("Role Play fails if user has a banned ability") +SINGLE_BATTLE_TEST("Role Play fails if user's ability can't be suppressed") { u32 species, ability; PARAMETRIZE { species = SPECIES_ARCEUS; ability = ABILITY_MULTITYPE; } PARAMETRIZE { species = SPECIES_DARMANITAN; ability = ABILITY_ZEN_MODE; } PARAMETRIZE { species = SPECIES_AEGISLASH; ability = ABILITY_STANCE_CHANGE; } - PARAMETRIZE { species = SPECIES_WISHIWASHI; ability = ABILITY_SCHOOLING; } - PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } PARAMETRIZE { species = SPECIES_MINIOR; ability = ABILITY_SHIELDS_DOWN; } + PARAMETRIZE { species = SPECIES_WISHIWASHI; ability = ABILITY_SCHOOLING; } PARAMETRIZE { species = SPECIES_MIMIKYU; ability = ABILITY_DISGUISE; } - PARAMETRIZE { species = SPECIES_SILVALLY; ability = ABILITY_RKS_SYSTEM; } - PARAMETRIZE { species = SPECIES_ZYGARDE; ability = ABILITY_POWER_CONSTRUCT; } PARAMETRIZE { species = SPECIES_GRENINJA_BATTLE_BOND; ability = ABILITY_BATTLE_BOND; } - PARAMETRIZE { species = SPECIES_EISCUE; ability = ABILITY_ICE_FACE; } + PARAMETRIZE { species = SPECIES_ZYGARDE; ability = ABILITY_POWER_CONSTRUCT; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_SILVALLY; ability = ABILITY_RKS_SYSTEM; } PARAMETRIZE { species = SPECIES_CRAMORANT; ability = ABILITY_GULP_MISSILE; } + PARAMETRIZE { species = SPECIES_EISCUE; ability = ABILITY_ICE_FACE; } + PARAMETRIZE { species = SPECIES_CALYREX_ICE; ability = ABILITY_AS_ONE_ICE_RIDER; } + PARAMETRIZE { species = SPECIES_CALYREX_SHADOW; ability = ABILITY_AS_ONE_SHADOW_RIDER; } PARAMETRIZE { species = SPECIES_PALAFIN_ZERO; ability = ABILITY_ZERO_TO_HERO; } PARAMETRIZE { species = SPECIES_TATSUGIRI; ability = ABILITY_COMMANDER; } @@ -68,9 +113,12 @@ SINGLE_BATTLE_TEST("Role Play fails if user has a banned ability") PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } OPPONENT(species) { Ability(ability); } } WHEN { - TURN { MOVE(opponent,MOVE_ROLE_PLAY); } + TURN { MOVE(opponent, MOVE_ROLE_PLAY); } + TURN { MOVE(opponent, MOVE_DOODLE); } } SCENE { NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ROLE_PLAY, opponent); MESSAGE("But it failed!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_DOODLE, opponent); + MESSAGE("But it failed!"); } } diff --git a/test/battle/move_effect/simple_beam.c b/test/battle/move_effect/simple_beam.c new file mode 100644 index 0000000000..e91bf0b8ce --- /dev/null +++ b/test/battle/move_effect/simple_beam.c @@ -0,0 +1,74 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_SIMPLE_BEAM].effect == EFFECT_SIMPLE_BEAM); +} + +SINGLE_BATTLE_TEST("Simple Beam replaces target's ability with Simple") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + OPPONENT(SPECIES_CHARMANDER) { Ability(ABILITY_BLAZE); } + }WHEN { + TURN { MOVE(player, MOVE_SIMPLE_BEAM); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SIMPLE_BEAM, player); + ABILITY_POPUP(opponent, ABILITY_BLAZE); + } THEN { + EXPECT_EQ(opponent->ability, ABILITY_SIMPLE); + } +} + +DOUBLE_BATTLE_TEST("Simple Beam fails if the target already has Simple") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + PLAYER(SPECIES_CHARMANDER) { Ability(ABILITY_BLAZE); } + OPPONENT(SPECIES_BULBASAUR) { Ability(ABILITY_OVERGROW); } + OPPONENT(SPECIES_SQUIRTLE) { Ability(ABILITY_TORRENT); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_SIMPLE_BEAM, target: opponentLeft); MOVE(playerRight, MOVE_SIMPLE_BEAM, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SIMPLE_BEAM, playerLeft); + ABILITY_POPUP(opponentLeft, ABILITY_OVERGROW); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SIMPLE_BEAM, playerRight); + ABILITY_POPUP(opponentLeft, ABILITY_SIMPLE); + } + } THEN { + EXPECT_EQ(opponentLeft->ability, ABILITY_SIMPLE); + } +} + +SINGLE_BATTLE_TEST("Simple Beam fails if target has an ability that can't be overwritten") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_ARCEUS; ability = ABILITY_MULTITYPE; } + PARAMETRIZE { species = SPECIES_AEGISLASH; ability = ABILITY_STANCE_CHANGE; } + PARAMETRIZE { species = SPECIES_MINIOR; ability = ABILITY_SHIELDS_DOWN; } + PARAMETRIZE { species = SPECIES_WISHIWASHI; ability = ABILITY_SCHOOLING; } + PARAMETRIZE { species = SPECIES_MIMIKYU; ability = ABILITY_DISGUISE; } + PARAMETRIZE { species = SPECIES_GRENINJA_BATTLE_BOND; ability = ABILITY_BATTLE_BOND; } + PARAMETRIZE { species = SPECIES_ZYGARDE; ability = ABILITY_POWER_CONSTRUCT; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_SILVALLY; ability = ABILITY_RKS_SYSTEM; } + PARAMETRIZE { species = SPECIES_CRAMORANT; ability = ABILITY_GULP_MISSILE; } + PARAMETRIZE { species = SPECIES_EISCUE; ability = ABILITY_ICE_FACE; } + PARAMETRIZE { species = SPECIES_CALYREX_ICE; ability = ABILITY_AS_ONE_ICE_RIDER; } + PARAMETRIZE { species = SPECIES_CALYREX_SHADOW; ability = ABILITY_AS_ONE_SHADOW_RIDER; } + PARAMETRIZE { species = SPECIES_PALAFIN_ZERO; ability = ABILITY_ZERO_TO_HERO; } + PARAMETRIZE { species = SPECIES_TATSUGIRI; ability = ABILITY_COMMANDER; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + OPPONENT(species) { Ability(ability); } + } WHEN { + TURN { MOVE(player, MOVE_SIMPLE_BEAM); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SIMPLE_BEAM, player); + MESSAGE("But it failed!"); + } +} diff --git a/test/battle/move_effect/skill_swap.c b/test/battle/move_effect/skill_swap.c new file mode 100644 index 0000000000..5f2196fe7e --- /dev/null +++ b/test/battle/move_effect/skill_swap.c @@ -0,0 +1,111 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP); +} + +SINGLE_BATTLE_TEST("Skill Swap swaps user and target's abilities") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + OPPONENT(SPECIES_CHARMANDER) { Ability(ABILITY_BLAZE); } + }WHEN { + TURN { MOVE(player, MOVE_SKILL_SWAP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, player); + ABILITY_POPUP(player, ABILITY_TELEPATHY); + ABILITY_POPUP(opponent, ABILITY_BLAZE); + } THEN { + EXPECT_EQ(player->ability, ABILITY_BLAZE); + EXPECT_EQ(opponent->ability, ABILITY_TELEPATHY); + } +} + +DOUBLE_BATTLE_TEST("Skill Swap only swaps user's ability with target's ability") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + PLAYER(SPECIES_CHARMANDER) { Ability(ABILITY_BLAZE); } + OPPONENT(SPECIES_BULBASAUR) { Ability(ABILITY_OVERGROW); } + OPPONENT(SPECIES_SQUIRTLE) { Ability(ABILITY_TORRENT); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_SKILL_SWAP, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, playerLeft); + ABILITY_POPUP(playerLeft, ABILITY_TELEPATHY); + ABILITY_POPUP(opponentLeft, ABILITY_OVERGROW); + } THEN { + EXPECT_EQ(playerLeft->ability, ABILITY_OVERGROW); + EXPECT_EQ(playerRight->ability, ABILITY_BLAZE); + EXPECT_EQ(opponentLeft->ability, ABILITY_TELEPATHY); + EXPECT_EQ(opponentRight->ability, ABILITY_TORRENT); + } +} + +DOUBLE_BATTLE_TEST("Skill Swap doesn't display ability popups when swapping with an ally") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + PLAYER(SPECIES_CHARMANDER) { Ability(ABILITY_BLAZE); } + OPPONENT(SPECIES_BULBASAUR) { Ability(ABILITY_OVERGROW); } + OPPONENT(SPECIES_SQUIRTLE) { Ability(ABILITY_TORRENT); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_SKILL_SWAP, target: playerRight); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, playerLeft); + NONE_OF { + ABILITY_POPUP(playerLeft, ABILITY_TELEPATHY); + ABILITY_POPUP(playerRight, ABILITY_BLAZE); + } + } THEN { + EXPECT_EQ(playerLeft->ability, ABILITY_BLAZE); + EXPECT_EQ(playerRight->ability, ABILITY_TELEPATHY); + } +} + +SINGLE_BATTLE_TEST("Skill Swap fails if user or target has an ability that can't be swapped") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_SHEDINJA; ability = ABILITY_WONDER_GUARD; } + PARAMETRIZE { species = SPECIES_ARCEUS; ability = ABILITY_MULTITYPE; } + PARAMETRIZE { species = SPECIES_ZORUA; ability = ABILITY_ILLUSION; } + PARAMETRIZE { species = SPECIES_DARMANITAN; ability = ABILITY_ZEN_MODE; } + PARAMETRIZE { species = SPECIES_AEGISLASH; ability = ABILITY_STANCE_CHANGE; } + PARAMETRIZE { species = SPECIES_MINIOR; ability = ABILITY_SHIELDS_DOWN; } + PARAMETRIZE { species = SPECIES_WISHIWASHI; ability = ABILITY_SCHOOLING; } + PARAMETRIZE { species = SPECIES_MIMIKYU; ability = ABILITY_DISGUISE; } + PARAMETRIZE { species = SPECIES_GRENINJA_BATTLE_BOND; ability = ABILITY_BATTLE_BOND; } + PARAMETRIZE { species = SPECIES_ZYGARDE; ability = ABILITY_POWER_CONSTRUCT; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_SILVALLY; ability = ABILITY_RKS_SYSTEM; } + PARAMETRIZE { species = SPECIES_EISCUE; ability = ABILITY_ICE_FACE; } + PARAMETRIZE { species = SPECIES_KOFFING; ability = ABILITY_NEUTRALIZING_GAS; } + PARAMETRIZE { species = SPECIES_MORPEKO; ability = ABILITY_HUNGER_SWITCH; } + PARAMETRIZE { species = SPECIES_CALYREX_ICE; ability = ABILITY_AS_ONE_ICE_RIDER; } + PARAMETRIZE { species = SPECIES_CALYREX_SHADOW; ability = ABILITY_AS_ONE_SHADOW_RIDER; } + PARAMETRIZE { species = SPECIES_PALAFIN_ZERO; ability = ABILITY_ZERO_TO_HERO; } + PARAMETRIZE { species = SPECIES_TATSUGIRI; ability = ABILITY_COMMANDER; } + PARAMETRIZE { species = SPECIES_GREAT_TUSK; ability = ABILITY_PROTOSYNTHESIS; } + PARAMETRIZE { species = SPECIES_IRON_TREADS; ability = ABILITY_QUARK_DRIVE; } + PARAMETRIZE { species = SPECIES_OGERPON_TEAL_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_TEAL_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_WELLSPRING_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK; } + PARAMETRIZE { species = SPECIES_TERAPAGOS_TERASTAL; ability = ABILITY_TERA_SHELL; } + PARAMETRIZE { species = SPECIES_TERAPAGOS_STELLAR; ability = ABILITY_TERAFORM_ZERO; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + OPPONENT(species) { Ability(ability); } + } WHEN { + TURN { MOVE(player, MOVE_SKILL_SWAP); MOVE(opponent, MOVE_SKILL_SWAP); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, player); + MESSAGE("But it failed!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponent); + MESSAGE("But it failed!"); + } +} diff --git a/test/battle/move_effect/worry_seed.c b/test/battle/move_effect/worry_seed.c new file mode 100644 index 0000000000..3e74c883e7 --- /dev/null +++ b/test/battle/move_effect/worry_seed.c @@ -0,0 +1,74 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_WORRY_SEED].effect == EFFECT_WORRY_SEED); +} + +SINGLE_BATTLE_TEST("Worry Seed replaces target's ability with Insomnia") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + OPPONENT(SPECIES_CHARMANDER) { Ability(ABILITY_BLAZE); } + }WHEN { + TURN { MOVE(player, MOVE_WORRY_SEED); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WORRY_SEED, player); + ABILITY_POPUP(opponent, ABILITY_BLAZE); + } THEN { + EXPECT_EQ(opponent->ability, ABILITY_INSOMNIA); + } +} + +DOUBLE_BATTLE_TEST("Worry Seed fails if the target already has Insomnia") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + PLAYER(SPECIES_CHARMANDER) { Ability(ABILITY_BLAZE); } + OPPONENT(SPECIES_BULBASAUR) { Ability(ABILITY_OVERGROW); } + OPPONENT(SPECIES_SQUIRTLE) { Ability(ABILITY_TORRENT); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_WORRY_SEED, target: opponentLeft); MOVE(playerRight, MOVE_WORRY_SEED, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WORRY_SEED, playerLeft); + ABILITY_POPUP(opponentLeft, ABILITY_OVERGROW); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WORRY_SEED, playerRight); + ABILITY_POPUP(opponentLeft, ABILITY_INSOMNIA); + } + } THEN { + EXPECT_EQ(opponentLeft->ability, ABILITY_INSOMNIA); + } +} + +SINGLE_BATTLE_TEST("Worry Seed fails if target has an ability that can't be overwritten") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_ARCEUS; ability = ABILITY_MULTITYPE; } + PARAMETRIZE { species = SPECIES_AEGISLASH; ability = ABILITY_STANCE_CHANGE; } + PARAMETRIZE { species = SPECIES_MINIOR; ability = ABILITY_SHIELDS_DOWN; } + PARAMETRIZE { species = SPECIES_WISHIWASHI; ability = ABILITY_SCHOOLING; } + PARAMETRIZE { species = SPECIES_MIMIKYU; ability = ABILITY_DISGUISE; } + PARAMETRIZE { species = SPECIES_GRENINJA_BATTLE_BOND; ability = ABILITY_BATTLE_BOND; } + PARAMETRIZE { species = SPECIES_ZYGARDE; ability = ABILITY_POWER_CONSTRUCT; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_SILVALLY; ability = ABILITY_RKS_SYSTEM; } + PARAMETRIZE { species = SPECIES_CRAMORANT; ability = ABILITY_GULP_MISSILE; } + PARAMETRIZE { species = SPECIES_EISCUE; ability = ABILITY_ICE_FACE; } + PARAMETRIZE { species = SPECIES_CALYREX_ICE; ability = ABILITY_AS_ONE_ICE_RIDER; } + PARAMETRIZE { species = SPECIES_CALYREX_SHADOW; ability = ABILITY_AS_ONE_SHADOW_RIDER; } + PARAMETRIZE { species = SPECIES_PALAFIN_ZERO; ability = ABILITY_ZERO_TO_HERO; } + PARAMETRIZE { species = SPECIES_TATSUGIRI; ability = ABILITY_COMMANDER; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); } + OPPONENT(species) { Ability(ability); } + } WHEN { + TURN { MOVE(player, MOVE_WORRY_SEED); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WORRY_SEED, player); + MESSAGE("But it failed!"); + } +} From 8890500b770d4712b2456d1addefbe0b44993793 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 10 Oct 2024 14:26:46 -0500 Subject: [PATCH 51/65] Fix P_FRIENDSHIP_EVO_THRESHOLD not checking for Gen 8 (#5503) --- src/pokemon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pokemon.c b/src/pokemon.c index 0583939a85..356ca64ac2 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -60,7 +60,7 @@ #include "constants/weather.h" #include "wild_encounter.h" -#define FRIENDSHIP_EVO_THRESHOLD ((P_FRIENDSHIP_EVO_THRESHOLD >= GEN_9) ? 160 : 220) +#define FRIENDSHIP_EVO_THRESHOLD ((P_FRIENDSHIP_EVO_THRESHOLD >= GEN_8) ? 160 : 220) struct SpeciesItem { From 5b4acea6519096b7b53020ee43b7ea22aabdc550 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 10 Oct 2024 14:27:50 -0500 Subject: [PATCH 52/65] Add config to change Vivillon's breeding form (#4813) * Add config to change Vivillon's default form * Update daycare.c * Remove the default form stuff * Update daycare.c * Clean up * Assert and fancy --- include/config/pokemon.h | 19 ++++++++++--------- src/daycare.c | 4 ++++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/include/config/pokemon.h b/include/config/pokemon.h index 15db4881b5..cabaea2c7c 100644 --- a/include/config/pokemon.h +++ b/include/config/pokemon.h @@ -22,15 +22,16 @@ #define P_FRIENDSHIP_EVO_THRESHOLD GEN_LATEST // Since Gen 8, Pokémon that evolve by friendship evolve at or above 160 friendship instead of 220. // Breeding settings -#define P_NIDORAN_M_DITTO_BREED GEN_LATEST // Since Gen 5, when Nidoran♂ breeds with Ditto it can produce Nidoran♀ offspring. Before, it would only yield male offspring. This change also applies to Volbeat. -#define P_INCENSE_BREEDING GEN_LATEST // Since Gen 9, cross-generation Baby Pokémon don't require Incense being held by the parents to be obtained via breeding. -#define P_EGG_HATCH_LEVEL GEN_LATEST // Since Gen 4, Pokémon will hatch from eggs at level 1 instead of 5. -#define P_BALL_INHERITING GEN_LATEST // Since Gen 6, Eggs from the Daycare will inherit the Poké Ball from their mother. From Gen 7 onwards, the father can pass it down as well, as long as it's of the same species as the mother. -#define P_TM_INHERITANCE GEN_LATEST // Since Gen 6, the father no longer passes down TMs to the baby. -#define P_MOTHER_EGG_MOVE_INHERITANCE GEN_LATEST // Since Gen 6, the mother can also pass down Egg Moves. -#define P_NATURE_INHERITANCE GEN_LATEST // In Gen 3, Everstone grants Ditto and mothers a 50% chance to pass on Nature. Since Gen 4, anyone can pass on nature. Since Gen 5, the chance is 100%. -#define P_ABILITY_INHERITANCE GEN_LATEST // In B2W2, a female Pokémon has an 80% chance of passing down their ability if bred with a male. Since Gen 6, the chance is 80% for normal ability and 60% for Hidden Ability, and anyone can pass down their abilities if bred with Ditto. NOTE: BW's effect: 60% chance to pass down HA and random for normal ability has been omitted. -#define P_EGG_MOVE_TRANSFER GEN_LATEST // Starting in Gen 8, if two Pokémon of the same species are together in the Daycare, one knows an Egg Move, and the other has an empty slot, the other Pokémon will receive the Egg Move in the empty slot. In Gen 9, if a Pokémon holds a Mirror Herb, it will receive Egg Moves from the other regardless of species. +#define P_NIDORAN_M_DITTO_BREED GEN_LATEST // Since Gen 5, when Nidoran♂ breeds with Ditto it can produce Nidoran♀ offspring. Before, it would only yield male offspring. This change also applies to Volbeat. +#define P_INCENSE_BREEDING GEN_LATEST // Since Gen 9, cross-generation Baby Pokémon don't require Incense being held by the parents to be obtained via breeding. +#define P_EGG_HATCH_LEVEL GEN_LATEST // Since Gen 4, Pokémon will hatch from eggs at level 1 instead of 5. +#define P_BALL_INHERITING GEN_LATEST // Since Gen 6, Eggs from the Daycare will inherit the Poké Ball from their mother. From Gen 7 onwards, the father can pass it down as well, as long as it's of the same species as the mother. +#define P_TM_INHERITANCE GEN_LATEST // Since Gen 6, the father no longer passes down TMs to the baby. +#define P_MOTHER_EGG_MOVE_INHERITANCE GEN_LATEST // Since Gen 6, the mother can also pass down Egg Moves. +#define P_NATURE_INHERITANCE GEN_LATEST // In Gen 3, Everstone grants Ditto and mothers a 50% chance to pass on Nature. Since Gen 4, anyone can pass on nature. Since Gen 5, the chance is 100%. +#define P_ABILITY_INHERITANCE GEN_LATEST // In B2W2, a female Pokémon has an 80% chance of passing down their ability if bred with a male. Since Gen 6, the chance is 80% for normal ability and 60% for Hidden Ability, and anyone can pass down their abilities if bred with Ditto. NOTE: BW's effect: 60% chance to pass down HA and random for normal ability has been omitted. +#define P_EGG_MOVE_TRANSFER GEN_LATEST // Starting in Gen 8, if two Pokémon of the same species are together in the Daycare, one knows an Egg Move, and the other has an empty slot, the other Pokémon will receive the Egg Move in the empty slot. In Gen 9, if a Pokémon holds a Mirror Herb, it will receive Egg Moves from the other regardless of species. +#define P_SCATTERBUG_LINE_FORM_BREED SPECIES_SCATTERBUG_FANCY // Choose the Scatterbug form all Vivillon/Spewpa/Scatterbug will breed into, basically aligning with the "location" of the player's game. // Species-specific settings #define P_SHEDINJA_BALL GEN_LATEST // Since Gen 4, Shedinja requires a Poké Ball for its evolution. In Gen 3, Shedinja inherits Nincada's Ball. diff --git a/src/daycare.c b/src/daycare.c index 7a31f15b27..780e69e44d 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -996,6 +996,8 @@ static void GiveMoveIfItem(struct Pokemon *mon, struct DayCare *daycare) } } +STATIC_ASSERT(P_SCATTERBUG_LINE_FORM_BREED == SPECIES_SCATTERBUG_ICY_SNOW || (P_SCATTERBUG_LINE_FORM_BREED >= SPECIES_SCATTERBUG_POLAR && P_SCATTERBUG_LINE_FORM_BREED <= SPECIES_SCATTERBUG_POKEBALL), ScatterbugLineFormBreedMustBeAValidScatterbugForm); + static u16 DetermineEggSpeciesAndParentSlots(struct DayCare *daycare, u8 *parentSlots) { u16 i; @@ -1030,6 +1032,8 @@ static u16 DetermineEggSpeciesAndParentSlots(struct DayCare *daycare, u8 *parent eggSpecies = SPECIES_PHIONE; else if (GET_BASE_SPECIES_ID(eggSpecies) == SPECIES_ROTOM) eggSpecies = SPECIES_ROTOM; + else if (GET_BASE_SPECIES_ID(eggSpecies) == SPECIES_SCATTERBUG) + eggSpecies = P_SCATTERBUG_LINE_FORM_BREED; else if (GET_BASE_SPECIES_ID(eggSpecies) == SPECIES_FURFROU) eggSpecies = SPECIES_FURFROU; else if (eggSpecies == SPECIES_SINISTEA_ANTIQUE) From 130d6042e0cd0fcf56d713c8c5901d87637da3fb Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 10 Oct 2024 15:37:57 -0500 Subject: [PATCH 53/65] Add Ion Deluge animation (#5467) --- data/battle_anim_scripts.s | 11 ++++++ src/battle_anim_electric.c | 71 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index c16a5c7859..215b8deaff 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -7710,6 +7710,17 @@ gBattleAnimMove_NobleRoar:: end gBattleAnimMove_IonDeluge:: + loadspritegfx ANIM_TAG_IONS + loopsewithpan SE_M_THUNDERBOLT2, SOUND_PAN_ATTACKER, 10, 12 + createvisualtask AnimTask_BlendBattleAnimPal, 10, (F_PAL_BG | F_PAL_BATTLERS_2), 2, 0, 4, RGB_YELLOW + waitforvisualfinish + createvisualtask AnimTask_CreateIons, 2, 0, 3, 120 + createvisualtask AnimTask_CreateIons, 2, 0, 3, 120 + delay 120 + delay 30 + waitforvisualfinish + createvisualtask AnimTask_BlendBattleAnimPal, 10, (F_PAL_BG | F_PAL_BATTLERS_2), 2, 4, 0, RGB_YELLOW + waitforvisualfinish end gBattleAnimMove_ParabolicCharge:: diff --git a/src/battle_anim_electric.c b/src/battle_anim_electric.c index de6d820264..64114227e1 100644 --- a/src/battle_anim_electric.c +++ b/src/battle_anim_electric.c @@ -29,6 +29,8 @@ static bool8 CreateShockWaveBoltSprite(struct Task *task, u8 taskId); static void AnimShockWaveProgressingBolt(struct Sprite *); static bool8 CreateShockWaveLightningSprite(struct Task *task, u8 taskId); static void AnimShockWaveLightning(struct Sprite *sprite); +static void AnimIon(struct Sprite *); +static void AnimIon_Step(struct Sprite *); static const union AnimCmd sAnim_Lightning[] = { @@ -559,6 +561,34 @@ const struct SpriteTemplate gSeedFlareGreenChargeTemplate = .callback = AnimGrowingChargeOrb }; +static const union AnimCmd sAnim_Ion[] = +{ + ANIMCMD_FRAME(0, 2), + ANIMCMD_FRAME(8, 2), + ANIMCMD_FRAME(16, 2), + ANIMCMD_FRAME(24, 6), + ANIMCMD_FRAME(32, 2), + ANIMCMD_FRAME(40, 2), + ANIMCMD_FRAME(48, 2), + ANIMCMD_END, +}; + +static const union AnimCmd *const sAnims_Ion[] = +{ + sAnim_Ion, +}; + +const struct SpriteTemplate gIonSpriteTemplate = +{ + .tileTag = ANIM_TAG_IONS, + .paletteTag = ANIM_TAG_IONS, + .oam = &gOamData_AffineOff_ObjNormal_16x32, + .anims = sAnims_Ion, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimIon, +}; + // functions static void AnimLightning(struct Sprite *sprite) { @@ -1452,3 +1482,44 @@ static void AnimShockWaveLightning(struct Sprite *sprite) DestroySprite(sprite); } } + +// Copy of Rain Dance's function but displays the ion sprite instead +// arg 0: initial step +// arg 1: amount (?) +// arg 2: duration +void AnimTask_CreateIons(u8 taskId) +{ + u8 x, y; + + if (gTasks[taskId].data[0] == 0) + { + gTasks[taskId].data[1] = gBattleAnimArgs[0]; + gTasks[taskId].data[2] = gBattleAnimArgs[1]; + gTasks[taskId].data[3] = gBattleAnimArgs[2]; + } + gTasks[taskId].data[0]++; + if (gTasks[taskId].data[0] % gTasks[taskId].data[2] == 1) + { + x = Random2() % DISPLAY_WIDTH; + y = Random2() % (DISPLAY_HEIGHT / 2); + CreateSprite(&gIonSpriteTemplate, x, y, 4); + } + if (gTasks[taskId].data[0] == gTasks[taskId].data[3]) + DestroyAnimVisualTask(taskId); +} + +static void AnimIon(struct Sprite *sprite) +{ + sprite->callback = AnimIon_Step; +} + +static void AnimIon_Step(struct Sprite *sprite) +{ + if (++sprite->data[0] <= 13) + { + sprite->x2++; + sprite->y2 += 4; + } + if (sprite->animEnded) + DestroySprite(sprite); +} From cac815164af74818671b6e8c6756d654ff867406 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Fri, 11 Oct 2024 12:14:14 +0200 Subject: [PATCH 54/65] Cleanup extraneous function in header (#5506) --- include/battle_anim.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/battle_anim.h b/include/battle_anim.h index 6ff7e9ff22..36b21bc012 100644 --- a/include/battle_anim.h +++ b/include/battle_anim.h @@ -347,7 +347,6 @@ extern const union AnimCmd *const gAnims_WaterPulseBubble[]; // battle_anim_flying.c void DestroyAnimSpriteAfterTimer(struct Sprite *sprite); -void sub_810E2C8(struct Sprite *sprite); void AnimAirWaveCrescent(struct Sprite *sprite); void AnimFlyBallUp(struct Sprite *sprite); void AnimFlyBallAttack(struct Sprite *sprite); From b5c884504c2ebee68804a614bc764a53ae565f6f Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Fri, 11 Oct 2024 19:49:24 +0200 Subject: [PATCH 55/65] Fix negative mutation value (#5504) --- src/berry.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/berry.c b/src/berry.c index 9f187bd1e2..b1e29be6db 100644 --- a/src/berry.c +++ b/src/berry.c @@ -2384,6 +2384,8 @@ static u8 GetTreeMutationValue(u8 id) myMutation.asField.a = tree->mutationA; myMutation.asField.b = tree->mutationB; myMutation.asField.unused = 0; + if (myMutation.value == 0) // no mutation + return 0; return sBerryMutations[myMutation.value - 1][2]; #else return 0; From d7c5bfee685bf64341b46c1ef49374efc3e8fa6d Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Fri, 11 Oct 2024 19:50:41 +0200 Subject: [PATCH 56/65] Clean up Shedinja code (#5501) --- src/evolution_scene.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/evolution_scene.c b/src/evolution_scene.c index 98879430cb..6119494907 100644 --- a/src/evolution_scene.c +++ b/src/evolution_scene.c @@ -545,19 +545,13 @@ static void CB2_TradeEvolutionSceneUpdate(void) static void CreateShedinja(u16 preEvoSpecies, struct Pokemon *mon) { u32 data = 0; - #if P_SHEDINJA_BALL >= GEN_4 - u16 ball = ITEM_POKE_BALL; - #endif + u16 ball = ITEM_POKE_BALL; const struct Evolution *evolutions = GetSpeciesEvolutions(preEvoSpecies); if (evolutions == NULL) return; - if (evolutions[0].method == EVO_LEVEL_NINJASK && gPlayerPartyCount < PARTY_SIZE - #if P_SHEDINJA_BALL >= GEN_4 - && (CheckBagHasItem(ball, 1)) - #endif - ) + if (evolutions[0].method == EVO_LEVEL_NINJASK && gPlayerPartyCount < PARTY_SIZE && (P_SHEDINJA_BALL < GEN_4 || CheckBagHasItem(ball, 1))) { s32 i; struct Pokemon *shedinja = &gPlayerParty[gPlayerPartyCount]; @@ -567,10 +561,11 @@ static void CreateShedinja(u16 preEvoSpecies, struct Pokemon *mon) SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_NICKNAME, GetSpeciesName(evolutions[1].targetSpecies)); SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_HELD_ITEM, &data); SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_MARKINGS, &data); - #if P_SHEDINJA_BALL >= GEN_4 - SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_POKEBALL, &ball); - RemoveBagItem(ball, 1); - #endif + if (P_SHEDINJA_BALL >= GEN_4) + { + SetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_POKEBALL, &ball); + RemoveBagItem(ball, 1); + } for (i = MON_DATA_COOL_RIBBON; i < MON_DATA_COOL_RIBBON + CONTEST_CATEGORIES_COUNT; i++) SetMonData(&gPlayerParty[gPlayerPartyCount], i, &data); From 464c90a862f6f60b254a53d5a60e6cd544c6fe9d Mon Sep 17 00:00:00 2001 From: Pawkkie <61265402+Pawkkie@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:57:19 -0400 Subject: [PATCH 57/65] ShouldSwitchIfGameStatePrompt Tests (#5462) * Weird tests plz help <3 * better RNG tag names? * DATA.trial for data to reset between trials * Fix Wonder Guard test * Unused errors * Finish new tests * Split fix into its own PR * Spacing * Rename RNG_AI_SWITCH_TOXICD * Forgot to save file lol --------- Co-authored-by: Martin Griffin --- include/random.h | 4 +++ src/battle_ai_switch_items.c | 24 +++++--------- test/battle/ai/ai_switching.c | 62 +++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 16 deletions(-) diff --git a/include/random.h b/include/random.h index 6e210bcbb4..d41e478543 100644 --- a/include/random.h +++ b/include/random.h @@ -163,6 +163,10 @@ enum RandomTag RNG_AI_ABILITY, RNG_AI_SWITCH_HASBADODDS, RNG_AI_SWITCH_WONDER_GUARD, + RNG_AI_SWITCH_BADLY_POISONED, + RNG_AI_SWITCH_CURSED, + RNG_AI_SWITCH_NIGHTMARE, + RNG_AI_SWITCH_SEEDED, RNG_SHELL_SIDE_ARM, }; diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 9731a6393b..0400f18b53 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -180,7 +180,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult) && gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 4))) { // 50% chance to stay in regardless - if (!RandomPercentage(RNG_AI_SWITCH_HASBADODDS, 50)) + if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, 50)) return FALSE; // Switch mon out @@ -203,7 +203,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult) return FALSE; // 50% chance to stay in regardless - if (!RandomPercentage(RNG_AI_SWITCH_HASBADODDS, 50)) + if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, 50)) return FALSE; // Switch mon out @@ -462,16 +462,12 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult) u16 holdEffect = AI_DATA->holdEffects[battler]; u8 opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler)); u8 opposingBattler = GetBattlerAtPosition(opposingPosition); - s32 moduloChance = 4; //25% Chance Default - s32 chanceReducer = 1; //No Reduce default. Increase to reduce + bool32 hasStatRaised = AnyStatIsRaised(battler); s32 firstId; s32 lastId; s32 i; struct Pokemon *party; - if (AnyStatIsRaised(battler)) - chanceReducer = 5; // Reduce switchout probability by factor of 5 if setup - //Perish Song if (gStatuses3[battler] & STATUS3_PERISH_SONG && gDisableStructs[battler].perishSongTimer == 0 @@ -567,28 +563,24 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult) && !AiExpectsToFaintPlayer(battler)) { //Toxic - moduloChance = 2; //50% if (((gBattleMons[battler].status1 & STATUS1_TOXIC_COUNTER) >= STATUS1_TOXIC_TURN(2)) && gBattleMons[battler].hp >= (gBattleMons[battler].maxHP / 3) - && (Random() % (moduloChance*chanceReducer)) == 0) + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_BADLY_POISONED, 20) : RandomPercentage(RNG_AI_SWITCH_BADLY_POISONED, 50))) switchMon = TRUE; //Cursed - moduloChance = 2; //50% if (gBattleMons[battler].status2 & STATUS2_CURSED - && (Random() % (moduloChance*chanceReducer)) == 0) + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_CURSED, 20) : RandomPercentage(RNG_AI_SWITCH_CURSED, 50))) switchMon = TRUE; //Nightmare - moduloChance = 3; //33.3% if (gBattleMons[battler].status2 & STATUS2_NIGHTMARE - && (Random() % (moduloChance*chanceReducer)) == 0) + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NIGHTMARE, 15) : RandomPercentage(RNG_AI_SWITCH_NIGHTMARE, 33))) switchMon = TRUE; //Leech Seed - moduloChance = 4; //25% if (gStatuses3[battler] & STATUS3_LEECHSEED - && (Random() % (moduloChance*chanceReducer)) == 0) + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_SEEDED, 10) : RandomPercentage(RNG_AI_SWITCH_SEEDED, 25))) switchMon = TRUE; } @@ -606,7 +598,7 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult) if (FindMonThatAbsorbsOpponentsMove(battler, FALSE)) // Switch if absorber found. Note: FindMonThatAbsorbsOpponentsMove already provides id of the mon to switch into to gBattleStruct->AI_monToSwitchIntoId. switchMon = TRUE, monIdChosen = TRUE; if (!AI_OpponentCanFaintAiWithMod(battler, 0) - && AnyStatIsRaised(battler)) + && hasStatRaised) switchMon = FALSE; if (AiExpectsToFaintPlayer(battler) && AI_IsSlower(battler, opposingBattler, 0) diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c index f3b2429dd8..a70163af18 100644 --- a/test/battle/ai/ai_switching.c +++ b/test/battle/ai/ai_switching.c @@ -486,3 +486,65 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it can't d TURN { MOVE(player, MOVE_TACKLE) ; EXPECT_SWITCH(opponent, 1); } } } + +AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has been Toxic'd for at least two turns 50% of the time with more than 1/3 HP remaining") +{ + PASSES_RANDOMLY(50, 100, RNG_AI_SWITCH_BADLY_POISONED); + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); + PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE, MOVE_CELEBRATE, MOVE_TOXIC); } + OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } + OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } + } WHEN { + TURN { MOVE(player, MOVE_TOXIC); EXPECT_MOVE(opponent, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVE(opponent, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_TACKLE); EXPECT_SWITCH(opponent, 1); } + } +} + +AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has been Curse'd 50% of the time") +{ + PASSES_RANDOMLY(50, 100, RNG_AI_SWITCH_CURSED); + GIVEN { + ASSUME(gMovesInfo[MOVE_CURSE].effect == EFFECT_CURSE); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); + PLAYER(SPECIES_DUSCLOPS) { Moves(MOVE_FIRE_PUNCH, MOVE_CURSE); } + PLAYER(SPECIES_MILOTIC) { Moves(MOVE_WATER_GUN); } + OPPONENT(SPECIES_DUSCLOPS) { Moves(MOVE_SHADOW_BALL); } + OPPONENT(SPECIES_DUSCLOPS) { Moves(MOVE_SHADOW_BALL); } + } WHEN { + TURN { MOVE(player, MOVE_CURSE) ; EXPECT_MOVE(opponent, MOVE_SHADOW_BALL); } + TURN { MOVE(player, MOVE_FIRE_PUNCH); EXPECT_SWITCH(opponent, 1); } + } +} + +AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has been Nightmare'd 33% of the time") +{ + PASSES_RANDOMLY(33, 100, RNG_AI_SWITCH_NIGHTMARE); + GIVEN { + ASSUME(gMovesInfo[MOVE_NIGHTMARE].effect == EFFECT_NIGHTMARE); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); + PLAYER(SPECIES_GENGAR) { Moves(MOVE_NIGHTMARE); } + OPPONENT(SPECIES_DUSCLOPS) { Moves(MOVE_SHADOW_BALL); Status1(STATUS1_SLEEP); } + OPPONENT(SPECIES_DUSCLOPS) { Moves(MOVE_SHADOW_BALL); } + } WHEN { + TURN { MOVE(player, MOVE_NIGHTMARE) ; EXPECT_MOVE(opponent, MOVE_SHADOW_BALL); } + TURN { MOVE(player, MOVE_NIGHTMARE) ; EXPECT_SWITCH(opponent, 1); } + } +} + +AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has been Leech Seed'd 25% of the time") +{ + PASSES_RANDOMLY(25, 100, RNG_AI_SWITCH_SEEDED); + GIVEN { + ASSUME(gMovesInfo[MOVE_LEECH_SEED].effect == EFFECT_LEECH_SEED); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); + PLAYER(SPECIES_WHIMSICOTT) { Moves(MOVE_LEECH_SEED); } + OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } + OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } + } WHEN { + TURN { MOVE(player, MOVE_LEECH_SEED) ; EXPECT_MOVE(opponent, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_LEECH_SEED); EXPECT_SWITCH(opponent, 1); } + } +} From 2db1f071fa1348959ccaae88c88bd80fbd8ef129 Mon Sep 17 00:00:00 2001 From: SarnPoke <77281351+SarnPoke@users.noreply.github.com> Date: Fri, 11 Oct 2024 21:59:50 +0200 Subject: [PATCH 58/65] =?UTF-8?q?Add=20battle=20flag=20that=20prevents=20r?= =?UTF-8?q?unning=20from=20wild=20Pok=C3=A9mon=20(#5502)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add No Running Battle Flag Adds a flag that if set prevents the player from being able to run from wild battles. * Formatting battle_util.c Co-authored-by: Bassoonian * Adjust for pre-Gen 8 Teleport Addresses the edge case with Teleport when used with under Gen 8 mechanics. --------- Co-authored-by: Bassoonian --- include/config/battle.h | 1 + src/battle_main.c | 9 ++++++++- src/battle_script_commands.c | 3 +++ src/battle_util.c | 4 ++++ src/overworld.c | 1 + 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/config/battle.h b/include/config/battle.h index ab2178eacc..14d2b47d32 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -187,6 +187,7 @@ #define B_SMART_WILD_AI_FLAG 0 // If not 0, you can set this flag in a script to enable smart wild pokemon #define B_FLAG_NO_BAG_USE 0 // If this flag is set, the ability to use the bag in battle is disabled. #define B_FLAG_NO_CATCHING 0 // If this flag is set, the ability to catch wild Pokémon is disabled. +#define B_FLAG_NO_RUNNING 0 // If this flag is set, the ability to escape from wild battles is disabled. Also makes Roar/Whirlwind and Teleport (under Gen8) fail. #define B_FLAG_AI_VS_AI_BATTLE 0 // If this flag is set, the player's mons will be controlled by the ai next battles. #define B_FLAG_DYNAMAX_BATTLE 0 // If this flag is set, the ability to Dynamax in battle is enabled for all trainers. #define B_FLAG_TERA_ORB_CHARGED 0 // If this flag is set, the Tera Orb is charged. It is automatically set upon healing and cleared upon Terastallizing once configured. diff --git a/src/battle_main.c b/src/battle_main.c index b2b6e14447..ab84ab9079 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4073,6 +4073,12 @@ u8 IsRunningFromBattleImpossible(u32 battler) { u32 holdEffect, i; + if (FlagGet(B_FLAG_NO_RUNNING)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CANT_ESCAPE; + return BATTLE_RUN_FORBIDDEN; + } + if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY_E_READER) holdEffect = gEnigmaBerries[battler].holdEffect; else @@ -4430,8 +4436,9 @@ static void HandleTurnActionSelectionState(void) BattleScriptExecute(BattleScript_PrintCantRunFromTrainer); gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN; } - else if (IsRunningFromBattleImpossible(battler) != BATTLE_RUN_SUCCESS + else if ((IsRunningFromBattleImpossible(battler) != BATTLE_RUN_SUCCESS && gBattleResources->bufferB[battler][1] == B_ACTION_RUN) + || (FlagGet(B_FLAG_NO_RUNNING) == TRUE && gBattleResources->bufferB[battler][1] == B_ACTION_RUN)) { gSelectionBattleScripts[battler] = BattleScript_PrintCantEscapeFromBattle; gBattleCommunication[battler] = STATE_SELECTION_SCRIPT; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index da1a8754e1..83f7bb4999 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9076,6 +9076,7 @@ static void Cmd_various(void) { // Roar will fail in a double wild battle when used by the player against one of the two alive wild mons. // Also when an opposing wild mon uses it againt its partner. + // Also when B_FLAG_NO_RUNNING is enabled. case VARIOUS_JUMP_IF_ROAR_FAILS: { VARIOUS_ARGS(const u8 *jumpInstr); @@ -9088,6 +9089,8 @@ static void Cmd_various(void) && GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT && GetBattlerSide(gBattlerTarget) == B_SIDE_OPPONENT) gBattlescriptCurrInstr = cmd->jumpInstr; + else if (FlagGet(B_FLAG_NO_RUNNING)) + gBattlescriptCurrInstr = cmd->jumpInstr; else gBattlescriptCurrInstr = cmd->nextInstr; return; diff --git a/src/battle_util.c b/src/battle_util.c index 83ed114c07..03c3758684 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -450,6 +450,10 @@ bool32 TryRunFromBattle(u32 battler) u8 pyramidMultiplier; u8 speedVar; + // If this flag is set, running will never be successful under any circumstances. + if (FlagGet(B_FLAG_NO_RUNNING)) + return effect; + if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY_E_READER) holdEffect = gEnigmaBerries[battler].holdEffect; else diff --git a/src/overworld.c b/src/overworld.c index 7dea410395..c06751f6f3 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -423,6 +423,7 @@ void Overworld_ResetBattleFlagsAndVars(void) FlagClear(B_SMART_WILD_AI_FLAG); FlagClear(B_FLAG_NO_BAG_USE); FlagClear(B_FLAG_NO_CATCHING); + FlagClear(B_FLAG_NO_RUNNING); FlagClear(B_FLAG_DYNAMAX_BATTLE); FlagClear(B_FLAG_SKY_BATTLE); } From 353915ef646ba7ddb27a92be060119da144cdb78 Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Fri, 11 Oct 2024 17:47:46 -0400 Subject: [PATCH 59/65] Add various tests, add RNG_RANDOM_TARGET (#5438) * burn dmg test depends on config * burn and frostbite tests use B_BURN_DAMAGE config for denom val * update kings shield test with config * add RNG_RANDOM_TARGET, use SetRandomTarget in HandleAction_UseMove target acquisition, update uproar test to PASSES_RANDOMLY since test will fail if you target the soundproof voltorb. Slightly faster UproarWakeUpCheck * add sticky web+contrary test * add EXPECT_EQ to contrary+sticky web test * Update src/battle_script_commands.c Co-authored-by: Bassoonian * Update src/battle_util.c Co-authored-by: Bassoonian * Update test/battle/move_effect/uproar.c Co-authored-by: Bassoonian * fix test * syntax fix --------- Co-authored-by: ghoulslash Co-authored-by: Bassoonian --- include/random.h | 1 + src/battle_script_commands.c | 3 ++- src/battle_util.c | 39 +++++----------------------- test/battle/ability/contrary.c | 20 ++++++++++++++ test/battle/move_effect/belly_drum.c | 26 ++++++++++++++++++- test/battle/move_effect/protect.c | 6 ++++- test/battle/move_effect/uproar.c | 1 + test/battle/status1/burn.c | 5 ++-- test/battle/status1/frostbite.c | 4 +-- 9 files changed, 64 insertions(+), 41 deletions(-) diff --git a/include/random.h b/include/random.h index d41e478543..215472377e 100644 --- a/include/random.h +++ b/include/random.h @@ -168,6 +168,7 @@ enum RandomTag RNG_AI_SWITCH_NIGHTMARE, RNG_AI_SWITCH_SEEDED, RNG_SHELL_SIDE_ARM, + RNG_RANDOM_TARGET, }; #define RandomWeighted(tag, ...) \ diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 83f7bb4999..f55eff3b7b 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -11405,10 +11405,11 @@ static void Cmd_setmiracleeye(void) bool8 UproarWakeUpCheck(u8 battler) { s32 i; + bool32 hasSoundproof = (B_UPROAR_IGNORE_SOUNDPROOF < GEN_5 && GetBattlerAbility(battler) == ABILITY_SOUNDPROOF); for (i = 0; i < gBattlersCount; i++) { - if (!(gBattleMons[i].status2 & STATUS2_UPROAR) || (GetBattlerAbility(battler) == ABILITY_SOUNDPROOF && B_UPROAR_IGNORE_SOUNDPROOF < GEN_5)) + if (!(gBattleMons[i].status2 & STATUS2_UPROAR) || hasSoundproof) continue; gBattleScripting.battler = i; diff --git a/src/battle_util.c b/src/battle_util.c index 03c3758684..2971b36840 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -251,20 +251,7 @@ void HandleAction_UseMove(void) { if (moveTarget & MOVE_TARGET_RANDOM) { - if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) - { - if (Random() & 1) - gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); - else - gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); - } - else - { - if (Random() & 1) - gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); - else - gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); - } + gBattlerTarget = SetRandomTarget(gBattlerAttacker); } else if (moveTarget & MOVE_TARGET_FOES_AND_ALLY) { @@ -311,21 +298,7 @@ void HandleAction_UseMove(void) } else if (IsDoubleBattle() && moveTarget & MOVE_TARGET_RANDOM) { - if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) - { - if (Random() & 1) - gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); - else - gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); - } - else - { - if (Random() & 1) - gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); - else - gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); - } - + gBattlerTarget = SetRandomTarget(gBattlerAttacker); if (gAbsentBattlerFlags & (1u << gBattlerTarget) && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)) { @@ -2664,7 +2637,7 @@ u8 DoBattlerEndTurnEffects(void) for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) { if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_SOUNDPROOF) + && (B_UPROAR_IGNORE_SOUNDPROOF >= GEN_5 || GetBattlerAbility(gBattlerAttacker) != ABILITY_SOUNDPROOF)) { gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_SLEEP; gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE; @@ -8341,7 +8314,7 @@ void HandleAction_RunBattleScript(void) // identical to RunBattleScriptCommands gBattleScriptingCommandsTable[*gBattlescriptCurrInstr](); } -u32 SetRandomTarget(u32 battler) +u32 SetRandomTarget(u32 battlerAtk) { u32 target; static const u8 targets[2][2] = @@ -8352,13 +8325,13 @@ u32 SetRandomTarget(u32 battler) if (IsDoubleBattle()) { - target = GetBattlerAtPosition(targets[GetBattlerSide(battler)][Random() % 2]); + target = GetBattlerAtPosition(targets[GetBattlerSide(battlerAtk)][RandomUniform(RNG_RANDOM_TARGET, 0, 1)]); if (!IsBattlerAlive(target)) target ^= BIT_FLANK; } else { - target = GetBattlerAtPosition(targets[GetBattlerSide(battler)][0]); + target = GetBattlerAtPosition(targets[GetBattlerSide(battlerAtk)][0]); } return target; diff --git a/test/battle/ability/contrary.c b/test/battle/ability/contrary.c index d257a7a344..824f2bf6c2 100644 --- a/test/battle/ability/contrary.c +++ b/test/battle/ability/contrary.c @@ -221,3 +221,23 @@ SINGLE_BATTLE_TEST("Contrary lowers a stat after using a move which would normal EXPECT_MUL_EQ(results[1].damageBefore, UQ_4_12(4.0), results[1].damageAfter); } } + +SINGLE_BATTLE_TEST("Sticky Web raises Speed by 1 for Contrary mon on switch-in") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SNIVY) { Ability(ABILITY_CONTRARY); } + } WHEN { + TURN { MOVE(player, MOVE_STICKY_WEB); } + TURN { SWITCH(opponent, 1); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, player); + MESSAGE("A sticky web spreads out on the ground around the opposing team!"); + MESSAGE("2 sent out Snivy!"); + MESSAGE("Foe Snivy was caught in a Sticky Web!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Snivy's Speed rose!"); + } +} diff --git a/test/battle/move_effect/belly_drum.c b/test/battle/move_effect/belly_drum.c index 82abaf30c4..612d4005c3 100644 --- a/test/battle/move_effect/belly_drum.c +++ b/test/battle/move_effect/belly_drum.c @@ -105,7 +105,31 @@ SINGLE_BATTLE_TEST("Belly Drum's HP cost doesn't trigger effects that trigger on } } +SINGLE_BATTLE_TEST("Belly Drum minimizes the user's Attack stat with Contrary", s16 damage) +{ + bool32 raiseAttack; + PARAMETRIZE { raiseAttack = FALSE; } + PARAMETRIZE { raiseAttack = TRUE; } + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_CONTRARY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (raiseAttack) TURN { MOVE(player, MOVE_BELLY_DRUM); } + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + if (raiseAttack) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BELLY_DRUM, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet cut its own HP and maximized ATTACK!"); // Message unaffected by Contrary + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[1].damage, Q_4_12(4), results[0].damage); + } +} + TO_DO_BATTLE_TEST("Belly Drum maximizes the user's Attack stat, even when below 0"); -TO_DO_BATTLE_TEST("Belly Drum minimizes the user's Attack stat if it has Contrary"); // Should still say "maximized attack" TO_DO_BATTLE_TEST("Belly Drum fails if the user's Attack is already at +6, even with Contrary"); TO_DO_BATTLE_TEST("Belly Drum deducts HP if the user has contrary and is at -6"); diff --git a/test/battle/move_effect/protect.c b/test/battle/move_effect/protect.c index 60b55ebb75..e5e75550e8 100644 --- a/test/battle/move_effect/protect.c +++ b/test/battle/move_effect/protect.c @@ -64,7 +64,7 @@ SINGLE_BATTLE_TEST("King's Shield, Silk Trap and Obstruct protect from damaging u32 j; static const u16 protectMoves[][3] = { // Move Stat Stages - {MOVE_KINGS_SHIELD, STAT_ATK, 1}, + {MOVE_KINGS_SHIELD, STAT_ATK, (B_KINGS_SHIELD_LOWER_ATK >= GEN_8) ? 1 : 2}, {MOVE_SILK_TRAP, STAT_SPEED, 1}, {MOVE_OBSTRUCT, STAT_DEF, 2}, }; @@ -99,7 +99,11 @@ SINGLE_BATTLE_TEST("King's Shield, Silk Trap and Obstruct protect from damaging NOT HP_BAR(opponent); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); if (statId == STAT_ATK) { + #if B_KINGS_SHIELD_LOWER_ATK >= GEN_8 MESSAGE("Wobbuffet's Attack fell!"); + #else + MESSAGE("Wobbuffet's Attack harshly fell!"); + #endif } else if (statId == STAT_SPEED) { MESSAGE("Wobbuffet's Speed fell!"); } else if (statId == STAT_DEF) { diff --git a/test/battle/move_effect/uproar.c b/test/battle/move_effect/uproar.c index fe6a4c9931..c463a8eaa1 100644 --- a/test/battle/move_effect/uproar.c +++ b/test/battle/move_effect/uproar.c @@ -8,6 +8,7 @@ ASSUMPTIONS DOUBLE_BATTLE_TEST("Uproar status causes sleeping pokemon to wake up during an attack") { + PASSES_RANDOMLY(1, 2, RNG_RANDOM_TARGET); // test fails if we target soundproof mon GIVEN { PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); } diff --git a/test/battle/status1/burn.c b/test/battle/status1/burn.c index 4da40589fb..63d6506fb6 100644 --- a/test/battle/status1/burn.c +++ b/test/battle/status1/burn.c @@ -1,11 +1,10 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Burn deals 1/16th damage per turn") +SINGLE_BATTLE_TEST("Burn deals 1/16th (Gen7+) or 1/8th damage per turn") { u32 j; GIVEN { - ASSUME(B_BURN_DAMAGE >= GEN_LATEST); PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_BURN); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -14,7 +13,7 @@ SINGLE_BATTLE_TEST("Burn deals 1/16th damage per turn") } SCENE { s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); for (j = 0; j < 4; j++) - HP_BAR(player, damage: maxHP / 16); + HP_BAR(player, damage: maxHP / ((B_BURN_DAMAGE >= GEN_7) ? 16 : 8)); } } diff --git a/test/battle/status1/frostbite.c b/test/battle/status1/frostbite.c index bf4b1f7fcd..fc5fba69b1 100644 --- a/test/battle/status1/frostbite.c +++ b/test/battle/status1/frostbite.c @@ -23,7 +23,7 @@ SINGLE_BATTLE_TEST("Frostbite reduces the special attack by 50 percent") } THEN { EXPECT_EQ(reducedDamage * 2, normaleDamage); } } -SINGLE_BATTLE_TEST("Frostbite deals 1/16 damage to effected pokemon") +SINGLE_BATTLE_TEST("Frostbite deals 1/16th (Gen7+) or 1/8th damage to affected pokemon") { s16 frostbiteDamage; @@ -36,7 +36,7 @@ SINGLE_BATTLE_TEST("Frostbite deals 1/16 damage to effected pokemon") MESSAGE("Foe Wobbuffet is hurt by its frostbite!"); ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); HP_BAR(opponent, captureDamage: &frostbiteDamage); - } THEN { EXPECT_EQ(frostbiteDamage, opponent->maxHP / 16); } + } THEN { EXPECT_EQ(frostbiteDamage, opponent->maxHP / ((B_BURN_DAMAGE >= GEN_7) ? 16 : 8)); } } SINGLE_BATTLE_TEST("Frostbite is healed if hit with a thawing move") From ef462d9d9b74121b7c75cbae5dd9001828e434f2 Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Thu, 3 Oct 2024 00:41:04 -0400 Subject: [PATCH 60/65] meta: `remote_build` no longer untracks new files after build --- remote_build.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/remote_build.sh b/remote_build.sh index 835520290d..300ac51ba5 100644 --- a/remote_build.sh +++ b/remote_build.sh @@ -25,7 +25,8 @@ git push build --force-with-lease if [[ $retVal -eq 0 ]]; then commit_msg=$(git log -1 --pretty=%B) if [[ "$commit_msg" == "$temp_commit_msg" ]]; then - git reset "$old_head" &>/dev/null + # Keep i(N)tent to add + git reset --mixed -N "$old_head" &>/dev/null fi fi set -x From d80190fe105eee12bbf74ae29647ac909084d35c Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:45:40 -0400 Subject: [PATCH 61/65] fix: Dig in Sealed Chamber no longer freezes follower. Fixed #5482 rhh --- data/scripts/field_move_scripts.inc | 18 ++++++++++++++++++ include/event_scripts.h | 1 + src/braille_puzzles.c | 1 - src/fldeff_dig.c | 7 +++++-- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/data/scripts/field_move_scripts.inc b/data/scripts/field_move_scripts.inc index e15115f826..dc161eb560 100644 --- a/data/scripts/field_move_scripts.inc +++ b/data/scripts/field_move_scripts.inc @@ -376,6 +376,24 @@ EventScript_EndSurface:: releaseall end +EventScript_DigCommon: + callfunc ScrFunc_IsFollowerFieldMoveUser + .2byte VAR_0x8004 + setfieldeffectargument 3, VAR_0x8004 @ skip pose if true + dofieldeffect FLDEFF_USE_DIG + waitstate +EventScript_DigSealedChamber:: @ fallthrough + setflag FLAG_SAFE_FOLLOWER_MOVEMENT + call_if_eq VAR_0x8004, TRUE, EventScript_FollowerFieldMove + callnative DoBrailleDigEffect + releaseall + end + +@ Use Dig from party menu +EventScript_UseDig:: + lockall + goto EventScript_DigCommon + Text_CantDive: .string "The sea is deep here. A POKéMON\n" .string "may be able to go underwater.$" diff --git a/include/event_scripts.h b/include/event_scripts.h index 2fa4468c74..ff920d62ae 100644 --- a/include/event_scripts.h +++ b/include/event_scripts.h @@ -404,6 +404,7 @@ extern const u8 EventScript_FailSweetScent[]; extern const u8 EventScript_UseFlash[]; extern const u8 EventScript_UseCut[]; extern const u8 EventScript_UseRockSmash[]; +extern const u8 EventScript_UseDig[]; //player pc extern const u8 LittlerootTown_BrendansHouse_2F_EventScript_TurnOffPlayerPC[]; diff --git a/src/braille_puzzles.c b/src/braille_puzzles.c index ab0f610fd7..757cac43b8 100644 --- a/src/braille_puzzles.c +++ b/src/braille_puzzles.c @@ -87,7 +87,6 @@ void DoBrailleDigEffect(void) DrawWholeMapView(); PlaySE(SE_BANG); FlagSet(FLAG_SYS_BRAILLE_DIG); - UnlockPlayerFieldControls(); } bool8 CheckRelicanthWailord(void) diff --git a/src/fldeff_dig.c b/src/fldeff_dig.c index c3ab989cd8..63eb848bf2 100644 --- a/src/fldeff_dig.c +++ b/src/fldeff_dig.c @@ -1,11 +1,13 @@ #include "global.h" #include "braille_puzzles.h" +#include "event_scripts.h" #include "field_effect.h" #include "field_player_avatar.h" #include "fldeff.h" #include "item_use.h" #include "overworld.h" #include "party_menu.h" +#include "script.h" #include "sprite.h" #include "constants/field_effects.h" @@ -31,8 +33,8 @@ bool8 SetUpFieldMove_Dig(void) static void FieldCallback_Dig(void) { Overworld_ResetStateAfterDigEscRope(); - FieldEffectStart(FLDEFF_USE_DIG); gFieldEffectArguments[0] = GetCursorSelectionMonId(); + ScriptContext_SetupScript(EventScript_UseDig); } bool8 FldEff_UseDig(void) @@ -53,7 +55,8 @@ static void StartDigFieldEffect(void) FieldEffectActiveListRemove(FLDEFF_USE_DIG); if (ShouldDoBrailleDigEffect()) { - DoBrailleDigEffect(); + // EventScript_DigSealedChamber handles DoBrailleDigEffect call + ScriptContext_Enable(); } else { From eedeaf1ead3d476f85556407184d1d28c306811c Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Sat, 12 Oct 2024 19:34:05 +0200 Subject: [PATCH 62/65] Clean up scrcmd PR (#5511) Renamed CheckPartyHasSpecie to CheckPartyHasSpecies and cleaned up function calls --- include/field_specials.h | 2 +- src/field_specials.c | 2 +- src/scrcmd.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/field_specials.h b/include/field_specials.h index f391a46ba5..975a57970b 100644 --- a/include/field_specials.h +++ b/include/field_specials.h @@ -35,6 +35,6 @@ void SetPCBoxToSendMon(u8 boxId); void PreparePartyForSkyBattle(void); void GetObjectPosition(u16*, u16*, u32, u32); bool32 CheckObjectAtXY(u32, u32); -bool32 CheckPartyHasSpecie(u32); +bool32 CheckPartyHasSpecies(u32); #endif // GUARD_FIELD_SPECIALS_H diff --git a/src/field_specials.c b/src/field_specials.c index b3f5a63332..cf9af51cbd 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -4316,7 +4316,7 @@ bool32 CheckObjectAtXY(u32 x, u32 y) return FALSE; } -bool32 CheckPartyHasSpecie(u32 givenSpecies) +bool32 CheckPartyHasSpecies(u32 givenSpecies) { u32 partyIndex; diff --git a/src/scrcmd.c b/src/scrcmd.c index bef273b0ba..399562f9a0 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -2496,7 +2496,7 @@ bool8 ScrCmd_getobjectxy(struct ScriptContext *ctx) u32 useTemplate = VarGet(ScriptReadHalfword(ctx)); u16 *pX = GetVarPointer(ScriptReadHalfword(ctx)); u16 *pY = GetVarPointer(ScriptReadHalfword(ctx)); - GetObjectPosition(pX,pY,localId,useTemplate); + GetObjectPosition(pX, pY, localId, useTemplate); return FALSE; } @@ -2507,7 +2507,7 @@ bool8 ScrCmd_checkobjectat(struct ScriptContext *ctx) u32 y = VarGet(ScriptReadHalfword(ctx)) + 7; u16 *varPointer = GetVarPointer(ScriptReadHalfword(ctx)); - *varPointer = CheckObjectAtXY(x,y); + *varPointer = CheckObjectAtXY(x, y); return FALSE; } @@ -2516,10 +2516,10 @@ bool8 Scrcmd_getsetpokedexflag(struct ScriptContext *ctx) { u32 speciesId = SpeciesToNationalPokedexNum(VarGet(ScriptReadHalfword(ctx))); bool32 desiredFlag = VarGet(ScriptReadHalfword(ctx)); - gSpecialVar_Result = GetSetPokedexFlag(speciesId,desiredFlag); + gSpecialVar_Result = GetSetPokedexFlag(speciesId, desiredFlag); if (desiredFlag == FLAG_SET_CAUGHT) - GetSetPokedexFlag(speciesId,FLAG_SET_SEEN); + GetSetPokedexFlag(speciesId, FLAG_SET_SEEN); return FALSE; } @@ -2527,7 +2527,7 @@ bool8 Scrcmd_getsetpokedexflag(struct ScriptContext *ctx) bool8 Scrcmd_checkspecies(struct ScriptContext *ctx) { u32 givenSpecies = VarGet(ScriptReadHalfword(ctx)); - gSpecialVar_Result = CheckPartyHasSpecie(givenSpecies); + gSpecialVar_Result = CheckPartyHasSpecies(givenSpecies); return FALSE; } From 743dc4d7654170468fcae277217a783438a9033f Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sat, 12 Oct 2024 12:37:11 -0500 Subject: [PATCH 63/65] Add text fitting tests for move, ability, item, and pokedex descriptions (#5505) --- src/data/abilities.h | 6 +- src/data/items.h | 150 +++++++++--------- src/data/moves_info.h | 30 ++-- .../pokemon/species_info/gen_2_families.h | 4 +- .../pokemon/species_info/gen_3_families.h | 6 +- .../pokemon/species_info/gen_4_families.h | 16 +- .../pokemon/species_info/gen_5_families.h | 22 +-- .../pokemon/species_info/gen_6_families.h | 20 +-- .../pokemon/species_info/gen_7_families.h | 18 +-- .../pokemon/species_info/gen_8_families.h | 30 ++-- .../pokemon/species_info/gen_9_families.h | 12 +- .../pokemon/species_info/shared_dex_text.h | 12 +- test/text.c | 51 ++++++ 13 files changed, 212 insertions(+), 165 deletions(-) diff --git a/src/data/abilities.h b/src/data/abilities.h index ba768a0a51..5c28e17c05 100644 --- a/src/data/abilities.h +++ b/src/data/abilities.h @@ -1550,7 +1550,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = [ABILITY_SLUSH_RUSH] = { .name = _("Slush Rush"), - .description = COMPOUND_STRING("Raises Speed in Hail or Snow."), + .description = COMPOUND_STRING("Raises Speed in Hail/Snow."), .aiRating = 5, }, @@ -2573,7 +2573,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = [ABILITY_TERA_SHELL] = { .name = _("Tera Shell"), - .description = COMPOUND_STRING("Resistant to types at full HP."), + .description = COMPOUND_STRING("Resists all at full HP."), .aiRating = 10, .cantBeCopied = TRUE, .cantBeSwapped = TRUE, @@ -2588,7 +2588,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = #else .name = _("TeraformZero"), #endif - .description = COMPOUND_STRING("Removes weather and terrain."), + .description = COMPOUND_STRING("Zeroes weather and terrain."), .aiRating = 10, .cantBeCopied = TRUE, .cantBeSwapped = TRUE, diff --git a/src/data/items.h b/src/data/items.h index 75629d579f..bffe48d725 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -538,9 +538,9 @@ const struct Item gItemsInfo[] = .name = _("Sport Ball"), .price = (I_PRICE < GEN_3 || I_PRICE >= GEN_9) ? 0 : 300, .description = COMPOUND_STRING( - "A special Ball used\n" - "in the Bug-Catching\n" - "Contest."), + "A special Ball\n" + "used in the Bug-\n" + "Catching Contest."), .pocket = POCKET_POKE_BALLS, .type = ITEM_USE_BAG_MENU, .battleUsage = EFFECT_ITEM_THROW_BALL, @@ -3176,9 +3176,9 @@ const struct Item gItemsInfo[] = .name = _("Jaw Fossil"), .price = (I_PRICE >= GEN_7) ? 7000: 1000, .description = COMPOUND_STRING( - "A piece of a prehis-\n" - "toric Pokémon's\n" - "large jaw."), + "A piece of a\n" + "prehistoric Poké-\n" + "mon's large jaw."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -3192,9 +3192,9 @@ const struct Item gItemsInfo[] = .name = _("Sail Fossil"), .price = (I_PRICE >= GEN_7) ? 7000: 1000, .description = COMPOUND_STRING( - "A piece of a prehis-\n" - "toric Pokémon's\n" - "skin sail."), + "A piece of a\n" + "prehistoric Poké-\n" + "mon's skin sail."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -4169,8 +4169,8 @@ const struct Item gItemsInfo[] = .price = (I_PRICE >= GEN_7) ? 2000 * TREASURE_FACTOR : 2100, .description = COMPOUND_STRING( "Loved by a certain\n" - "Pokémon. Imbued with\n" - "spiritual energy."), + "Pokémon. Imbued\n" + "with spirit energy."), .pocket = POCKET_ITEMS, .type = EVO_HELD_ITEM_TYPE, .fieldUseFunc = EVO_HELD_ITEM_FIELD_FUNC, @@ -4186,8 +4186,8 @@ const struct Item gItemsInfo[] = .price = (I_PRICE >= GEN_9) ? 3000 : ((I_PRICE >= GEN_7) ? 2000 : 500), .description = COMPOUND_STRING( "A mysterious scale\n" - "that evolves certain\n" - "Pokémon. It shines."), + "that evolves a\n" + "certain Pokémon."), .pocket = POCKET_ITEMS, .type = EVO_HELD_ITEM_TYPE, .fieldUseFunc = EVO_HELD_ITEM_FIELD_FUNC, @@ -4220,8 +4220,8 @@ const struct Item gItemsInfo[] = .name = _("Sachet"), .price = (I_PRICE >= GEN_7) ? 2000 * TREASURE_FACTOR : 2100, .description = COMPOUND_STRING( - "A sachet filled with\n" - "perfumes loved by\n" + "A sachet of strong\n" + "perfumes, loved by\n" "a certain Pokémon."), .pocket = POCKET_ITEMS, .type = EVO_HELD_ITEM_TYPE, @@ -4237,9 +4237,9 @@ const struct Item gItemsInfo[] = .name = _("Oval Stone"), .price = (I_PRICE >= GEN_7) ? 2000 : 2100, .description = COMPOUND_STRING( - "Makes a certain\n" - "Pokémon evolve. It's\n" - "shaped like an egg."), + "Peculiar stone\n" + "that evolves a\n" + "certain Pokémon."), .pocket = POCKET_ITEMS, .type = EVO_HELD_ITEM_TYPE, .fieldUseFunc = EVO_HELD_ITEM_FIELD_FUNC, @@ -4749,8 +4749,8 @@ const struct Item gItemsInfo[] = .holdEffect = HOLD_EFFECT_PLATE, .holdEffectParam = 20, .description = COMPOUND_STRING( - "A stone tablet that\n" - "boosts the power of\n" + "A tablet that ups\n" + "the power of\n" "Fairy-type moves."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4945,7 +4945,7 @@ const struct Item gItemsInfo[] = .holdEffect = HOLD_EFFECT_MEMORY, .holdEffectParam = 0, .description = COMPOUND_STRING( - "A disc with Fighting\n" + "Disc with Fighting\n" "type data. It swaps\n" "Silvally's type."), .pocket = POCKET_ITEMS, @@ -7053,9 +7053,9 @@ const struct Item gItemsInfo[] = .price = (I_PRICE >= GEN_7) ? 1000 : 10, .holdEffect = HOLD_EFFECT_QUICK_POWDER, .description = COMPOUND_STRING( - "An item to be held\n" - "by Ditto. This odd\n" - "powder boosts Speed."), + "A hold item that\n" + "raises the Speed\n" + "of Ditto."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -7132,9 +7132,9 @@ const struct Item gItemsInfo[] = .holdEffect = HOLD_EFFECT_ADAMANT_ORB, .holdEffectParam = 20, .description = COMPOUND_STRING( - "Boosts the power of\n" - "Dialga's Dragon and\n" - "Steel-type moves."), + "Powers up Dialga's\n" + "Dragon and Steel-\n" + "type moves."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -7150,9 +7150,9 @@ const struct Item gItemsInfo[] = .holdEffect = HOLD_EFFECT_LUSTROUS_ORB, .holdEffectParam = 20, .description = COMPOUND_STRING( - "Boosts the power of\n" - "Palkia's Dragon and\n" - "Water-type moves."), + "Powers up Palkia's\n" + "Dragon and Water-\n" + "type moves."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -7168,7 +7168,7 @@ const struct Item gItemsInfo[] = .holdEffect = HOLD_EFFECT_GRISEOUS_ORB, .holdEffectParam = 20, .description = COMPOUND_STRING( - "Powers up Giratina's\n" + "Boosts Giratina's\n" "Dragon and Ghost-\n" "type moves."), .pocket = POCKET_ITEMS, @@ -8107,9 +8107,9 @@ const struct Item gItemsInfo[] = .holdEffect = HOLD_EFFECT_CELL_BATTERY, .holdEffectParam = 0, .description = COMPOUND_STRING( - "Raises Atk if the\n" - "holder is hit by an\n" - "Electric-type move."), + "Raises Attack if\n" + "the holder is hit by\n" + "an Electric move."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -8454,8 +8454,8 @@ const struct Item gItemsInfo[] = .holdEffectParam = 10, .description = COMPOUND_STRING( "A headband that\n" - "boosts the power of\n" - "physical moves."), + "boosts the power\n" + "of physical moves."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -8525,9 +8525,9 @@ const struct Item gItemsInfo[] = .price = (I_PRICE >= GEN_9) ? 50000 : ((I_PRICE >= GEN_7) ? 4000 : 200), .holdEffect = HOLD_EFFECT_LIFE_ORB, .description = COMPOUND_STRING( - "Boosts the power of\n" - "moves at the cost\n" - "of some HP per turn."), + "Boosts move power\n" + "but holder loses HP\n" + "with each attack."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -8614,9 +8614,9 @@ const struct Item gItemsInfo[] = .price = (I_PRICE >= GEN_9) ? 20000 : ((I_PRICE >= GEN_7) ? 4000 : 200), .holdEffect = HOLD_EFFECT_IRON_BALL, .description = COMPOUND_STRING( - "Cuts Speed and lets\n" - "Flying-types be hit\n" - "by Ground moves."), + "Cuts Speed and\n" + "becomes vulnerable\n" + "to Ground moves."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -8663,9 +8663,9 @@ const struct Item gItemsInfo[] = .price = (I_PRICE >= GEN_9) ? 10000 : ((I_PRICE >= GEN_7) ? 4000 : 200), .holdEffect = HOLD_EFFECT_BLACK_SLUDGE, .description = COMPOUND_STRING( - "Gradually restores\n" - "HP of Poison-types.\n" - "Damages others."), + "Restores HP for\n" + "Poison-types.\n" + "Damages all others."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -8680,9 +8680,9 @@ const struct Item gItemsInfo[] = .price = (I_PRICE >= GEN_9) ? 10000 : ((I_PRICE >= GEN_7) ? 4000 : 200), .holdEffect = HOLD_EFFECT_GRIP_CLAW, .description = COMPOUND_STRING( - "Makes binding moves\n" - "used by the holder\n" - "go on for 7 turns."), + "A held item that\n" + "extends binding\n" + "moves like Wrap."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -8698,8 +8698,8 @@ const struct Item gItemsInfo[] = .holdEffect = HOLD_EFFECT_STICKY_BARB, .description = COMPOUND_STRING( "Damages the holder\n" - "each turn. May latch\n" - "on to foes."), + "each turn. May\n" + "latch on to foes."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -8714,9 +8714,9 @@ const struct Item gItemsInfo[] = .price = (I_PRICE >= GEN_9) ? 20000 : ((I_PRICE >= GEN_7) ? 4000 : 100), .holdEffect = HOLD_EFFECT_SHED_SHELL, .description = COMPOUND_STRING( - "Enables the holder\n" - "to switch out of\n" - "battle without fail."), + "Allows the holder\n" + "to switch out\n" + "without fail."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -8733,7 +8733,7 @@ const struct Item gItemsInfo[] = .holdEffectParam = 30, .description = COMPOUND_STRING( "A held item that\n" - "boosts the power of\n" + "ups the power of\n" "HP-stealing moves."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -8837,9 +8837,9 @@ const struct Item gItemsInfo[] = .holdEffect = HOLD_EFFECT_AIR_BALLOON, .holdEffectParam = 0, .description = COMPOUND_STRING( - "Elevates the holder\n" - "in the air. If hit,\n" - "this item will burst."), + "Makes the holder\n" + "float but bursts\n" + "if hit by an attack."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -8873,9 +8873,9 @@ const struct Item gItemsInfo[] = .holdEffect = HOLD_EFFECT_RING_TARGET, .holdEffectParam = 0, .description = COMPOUND_STRING( - "Moves that wouldn't\n" - "have effect will\n" - "land on its holder."), + "Moves that usually\n" + "have no effect will\n" + "hit the holder."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -8927,9 +8927,9 @@ const struct Item gItemsInfo[] = .holdEffect = HOLD_EFFECT_WEAKNESS_POLICY, .holdEffectParam = 0, .description = COMPOUND_STRING( - "If hit by a Super\n" - "Effective move, ups\n" - "Atk and Sp. Atk."), + "If hit by a super-\n" + "effective move,\n" + "ups Atk and Sp. Atk."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -8980,9 +8980,9 @@ const struct Item gItemsInfo[] = .price = (I_PRICE >= GEN_9) ? 5000 : ((I_PRICE >= GEN_8) ? 4000 : 300), .holdEffect = HOLD_EFFECT_ADRENALINE_ORB, .description = COMPOUND_STRING( - "Boosts Speed if the\n" - "user is intimidated,\n" - "but only one time."), + "This orb boosts\n" + "Speed if the holder\n" + "is intimidated."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -13042,9 +13042,9 @@ const struct Item gItemsInfo[] = .price = 20000, .holdEffect = HOLD_EFFECT_COVERT_CLOAK, .description = COMPOUND_STRING( - "Protects the holder\n" - "from secondary\n" - "move effects."), + "Protects holder\n" + "from additional\n" + "effects of moves."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -14009,11 +14009,11 @@ const struct Item gItemsInfo[] = .description = COMPOUND_STRING( #if B_X_ITEMS_BUFF >= GEN_7 "Sharply raises\n" - "offenses & defenses\n" + "offense & defense\n" "during one battle."), #else - "Raises offenses\n" - "and defenses during\n" + "Raises offense\n" + "and defense during\n" "one battle."), #endif .pocket = POCKET_ITEMS, @@ -14068,9 +14068,9 @@ const struct Item gItemsInfo[] = .name = _("Pokéshi Doll"), .price = 2000, .description = COMPOUND_STRING( - "A wooden toy carved\n" - "in the image of a\n" - "Pokémon. Can be sold."), + "A wooden toy\n" + "resembling a Poké-.\n" + "mon. Can be sold."), .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, diff --git a/src/data/moves_info.h b/src/data/moves_info.h index cb23dcf5af..478f0dc426 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -14198,8 +14198,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = { .name = HANDLE_EXPANDED_MOVE_NAME("ParabolcChrg", "Parabolic Charge"), .description = COMPOUND_STRING( - "Damages adjacent Pokémon and\n" - "heals up by half of it."), + "Damages adjacent Pokémon\n" + "and heals up by half of it."), .effect = EFFECT_ABSORB, .power = B_UPDATED_MOVE_DATA >= GEN_7 ? 65 : 50, .type = TYPE_ELECTRIC, @@ -16757,7 +16757,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .name = COMPOUND_STRING("Plasma Fists"), .description = COMPOUND_STRING( "Hits with electrical fists.\n" - "Normal moves become Electric."), + "Normal moves turn Electric."), .effect = EFFECT_PLASMA_FISTS, .power = 100, .type = TYPE_ELECTRIC, @@ -17116,8 +17116,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = { .name = HANDLE_EXPANDED_MOVE_NAME("DynamxCannon", "Dynamax Cannon"), .description = COMPOUND_STRING( - "Fires a strong beam. Deals\n" - "2x damage to Dynamaxed foes."), + "Unleashes core energy.\n" + "2x against Dynamaxed foes."), .effect = EFFECT_DYNAMAX_DOUBLE_DMG, .power = 100, .type = TYPE_DRAGON, @@ -18637,8 +18637,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = { .name = HANDLE_EXPANDED_MOVE_NAME("GlacialLance", "Glacial Lance"), .description = COMPOUND_STRING( - "Strikes by hurling a blizzard-\n" - "cloaked icicle lance at foes."), + "Hurls a blizzard-cloaked\n" + "icicle lance at foes."), .effect = EFFECT_HIT, .power = B_UPDATED_MOVE_DATA >= GEN_9 ? 120 : 130, .type = TYPE_ICE, @@ -18659,8 +18659,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = { .name = HANDLE_EXPANDED_MOVE_NAME("AstrlBarrage", "Astral Barrage"), .description = COMPOUND_STRING( - "Strikes by sending a frightful\n" - "amount of ghosts at foes."), + "Sends a frightful amount\n" + "of small ghosts at foes."), .effect = EFFECT_HIT, .power = 120, .type = TYPE_GHOST, @@ -20381,8 +20381,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = { .name = COMPOUND_STRING("Electro Shot"), .description = COMPOUND_STRING( - "Absorbs electricity in one turn,\n" - "then attacks next turn."), + "Gathers electricity, then\n" + "fires a high-voltage shot."), .effect = EFFECT_TWO_TURNS_ATTACK, .power = 130, .type = TYPE_ELECTRIC, @@ -20404,8 +20404,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = { .name = HANDLE_EXPANDED_MOVE_NAME("TeraStarstrm", "Tera Starstorm"), .description = COMPOUND_STRING( - "Damages all opponents if user is\n" - "Stellar form Terapagos."), + "In Terapagos's Stellar\n" + "Form, it hits all foes."), .effect = EFFECT_TERA_STARSTORM, .power = 120, .type = TYPE_NORMAL, @@ -20552,8 +20552,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = { .name = HANDLE_EXPANDED_MOVE_NAME("AllurngVoice", "Alluring Voice"), .description = COMPOUND_STRING( - "Confuses the target if their\n" - "stats were boosted this turn."), + "Confuses foe if its stats\n" + "were boosted this turn."), .effect = EFFECT_HIT, .power = 80, .type = TYPE_FAIRY, diff --git a/src/data/pokemon/species_info/gen_2_families.h b/src/data/pokemon/species_info/gen_2_families.h index 5dcabda292..530c92f411 100644 --- a/src/data/pokemon/species_info/gen_2_families.h +++ b/src/data/pokemon/species_info/gen_2_families.h @@ -3990,8 +3990,8 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .weight = 474, .description = COMPOUND_STRING( "A recent study uncovered that the\n" - "number of segments a\n" - "Dudunsparce's body has is determined by the\n" + "number of segments a Dudunsparce's\n" + "body has is determined by the\n" "Pokémon's genes."), .pokemonScale = 356, .pokemonOffset = 17, diff --git a/src/data/pokemon/species_info/gen_3_families.h b/src/data/pokemon/species_info/gen_3_families.h index 8a23bb05fd..d0bc37e5fc 100644 --- a/src/data/pokemon/species_info/gen_3_families.h +++ b/src/data/pokemon/species_info/gen_3_families.h @@ -4570,9 +4570,9 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .weight = 315, .description = COMPOUND_STRING( "It uses its amped-up willpower to create\n" - "additional arms for itself. The more it has\n" - "trained its spirit, the more realistic and\n" - "dexterous these self-created arms become."), + "additional arms for itself. The more it\n" + "has trained its spirit, the more realistic\n" + "and dexterous these arms become."), .pokemonScale = 298, .pokemonOffset = 5, .trainerScale = 256, diff --git a/src/data/pokemon/species_info/gen_4_families.h b/src/data/pokemon/species_info/gen_4_families.h index c0fdd1ecec..c88dd71ddd 100644 --- a/src/data/pokemon/species_info/gen_4_families.h +++ b/src/data/pokemon/species_info/gen_4_families.h @@ -561,10 +561,10 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .height = 17, .weight = 845, .description = COMPOUND_STRING( - "The three horns that extend from its beak\n" - "attest to its power. It avoids unnecessary\n" - "disputes, but it will decimate anything\n" - "that threatens its pride."), + "The three horns that extend from its\n" + "beak attest to its power. It avoids\n" + "unnecessary disputes, but it will decimate\n" + "anything that threatens its pride."), .pokemonScale = 259, .pokemonOffset = 0, .trainerScale = 290, @@ -4873,10 +4873,10 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .height = 3, .weight = 3, .description = COMPOUND_STRING( - "If the convection microwave oven is not\n" - "working properly, then the Rotom inhabiting\n" - "it will become lethargic. It will gleefully\n" - "burn your favorite outfit in mischief."), + "If the convection microwave oven is\n" + "not working properly, then the Rotom\n" + "inhabiting it will become lethargic. It\n" + "makes mischief by turning up the heat."), .pokemonScale = 530, .pokemonOffset = 13, .trainerScale = 256, diff --git a/src/data/pokemon/species_info/gen_5_families.h b/src/data/pokemon/species_info/gen_5_families.h index e8907999fe..50ee5b1fec 100644 --- a/src/data/pokemon/species_info/gen_5_families.h +++ b/src/data/pokemon/species_info/gen_5_families.h @@ -10333,10 +10333,10 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .height = 14, .weight = 580, .description = COMPOUND_STRING( - "It draws in air through its tail, transforms\n" - "it into fire, and uses it like a tongue.\n" - "They burn through Durant's steel bodies\n" - "and consume their insides."), + "It draws in air through its tail,\n" + "transforms it into fire, and uses it like\n" + "a tongue. They burn through Durant's steel\n" + "bodies and consume their insides."), .pokemonScale = 265, .pokemonOffset = 2, .trainerScale = 262, @@ -11154,10 +11154,10 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .height = 30, .weight = 610, .description = COMPOUND_STRING( - "It pulverizes foes into\n" - "nothingness with showers of devastatingly\n" - "powerful lightning bolts launched from\n" - "the string of orbs on its tail."), + "It pulverizes foes into nothingness\n" + "with showers of devastatingly\n" + "powerful lightning bolts launched\n" + "from the string of orbs on its tail."), .pokemonScale = 268, .pokemonOffset = 2, .trainerScale = 271, @@ -11348,9 +11348,9 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .height = 15, .weight = 680, .description = COMPOUND_STRING( - "The energy that comes pouring from its tail\n" - "increases the nutrition in the soil, making\n" - "crops grow to great size. It has been\n" + "The energy that comes pouring from its\n" + "tail increases the nutrition in the soil,\n" + "granting bountiful crops. It has been\n" "hailed as “The Guardian of the Fields.”"), .pokemonScale = 268, .pokemonOffset = 2, diff --git a/src/data/pokemon/species_info/gen_6_families.h b/src/data/pokemon/species_info/gen_6_families.h index 5946ac5c44..ac77faa806 100644 --- a/src/data/pokemon/species_info/gen_6_families.h +++ b/src/data/pokemon/species_info/gen_6_families.h @@ -640,11 +640,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .categoryName = _("Ninja"), .height = 15, .weight = 400, - .description = COMPOUND_STRING( - "It appears and vanishes with a ninja's\n" - "grace. It toys with its enemies using swift\n" - "movements, while slicing them with throwing\n" - "stars made of compressed water."), + .description = gGreninjaPokedexText, .pokemonScale = 268, .pokemonOffset = 2, .trainerScale = 271, @@ -1806,8 +1802,8 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = { FLORGES_MISC_INFO(Red, 0), .description = COMPOUND_STRING( - "This Pokémon creates an\n" - "impressive flower garden in its territory. It\n" + "This Pokémon creates an impressive\n" + "flower garden in its territory. It\n" "draws forth the power of the red\n" "flowers around its neck."), }, @@ -4213,8 +4209,8 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .height = 17, .weight = 3341, .description = COMPOUND_STRING( - "It loathes solitude and is\n" - "extremely clingy-it will fume and run riot if\n" + "It loathes solitude and is extremely\n" + "clingy--it will fume and run riot if\n" "those dearest to it ever leave its\n" "side."), .pokemonScale = 261, @@ -5239,9 +5235,9 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .weight = 850, .description = COMPOUND_STRING( "They fly around on moonless nights and\n" - "attack careless prey. The ultrasonic waves\n" - "it emits from its ears can reduce a large\n" - "boulder to pebbles."), + "attack careless prey. The ultrasonic\n" + "waves it emits from its ears can reduce\n" + "a large boulder to pebbles."), .pokemonScale = 268, .pokemonOffset = 2, .trainerScale = 271, diff --git a/src/data/pokemon/species_info/gen_7_families.h b/src/data/pokemon/species_info/gen_7_families.h index dfee119ed5..d69b362104 100644 --- a/src/data/pokemon/species_info/gen_7_families.h +++ b/src/data/pokemon/species_info/gen_7_families.h @@ -3823,8 +3823,8 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .height = 5, .weight = 700, .description = COMPOUND_STRING( - "It takes control of anyone who puts a hand\n" - "in its mouth, to add to the accumulation\n" + "It takes control of anyone who puts a\n" + "hand in its mouth, to add to the pile\n" "of its sand-mound body. This Pokémon\n" "embodies the grudges of the departed."), .pokemonScale = 432, @@ -6208,10 +6208,10 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .height = 38, .weight = 4600, .description = COMPOUND_STRING( - "This is its form while it is\n" - "devouring the light of Solgaleo. It pounces\n" - "on foes and then slashes them with\n" - "the claws on its four limbs and back."), + "This is its form while it is devouring\n" + "the light of Solgaleo. It pounces on\n" + "foes and then slashes them with the\n" + "claws on its four limbs and back."), .pokemonScale = 256, .pokemonOffset = 3, .trainerScale = 369, @@ -6974,9 +6974,9 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .height = 25, .weight = 800, .description = COMPOUND_STRING( - "Revered long ago for its capacity to create\n" - "iron from nothing, for some reason it has\n" - "come back to life after 3,000 years."), + "Revered long ago for its capacity to\n" + "create iron from nothing, for some reason\n" + "it has come back to life after 3,000 years."), .pokemonScale = 257, .pokemonOffset = 10, .trainerScale = 423, diff --git a/src/data/pokemon/species_info/gen_8_families.h b/src/data/pokemon/species_info/gen_8_families.h index 3f8257d966..46057b3847 100644 --- a/src/data/pokemon/species_info/gen_8_families.h +++ b/src/data/pokemon/species_info/gen_8_families.h @@ -885,8 +885,8 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .description = COMPOUND_STRING( "It will bravely challenge any opponent,\n" "no matter how powerful. This Pokémon\n" - "benefits from every battle--even a defeat\n" - "increases its strength a bit."), + "benefits from every battle--even a\n" + "defeat increases its strength a bit."), .pokemonScale = 682, .pokemonOffset = 24, .trainerScale = 256, @@ -1318,8 +1318,8 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .height = 140, .weight = 0, .description = COMPOUND_STRING( - "Its brain has grown to a\n" - "gargantuan size, as has the rest of its body.\n" + "Its brain has grown to a gargantuan\n" + "size, as has the rest of its body.\n" "This Pokémon's intellect and\n" "psychic abilities are overpowering."), .pokemonScale = 491, @@ -3621,9 +3621,9 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .weight = 0, .description = COMPOUND_STRING( "The heat that comes off a\n" - "Gigantamax Centiskorch may destabilize air\n" - "currents. Sometimes it can even\n" - "cause storms."), + "Gigantamax Centiskorch may\n" + "destabilize air currents. Sometimes\n" + "it can even cause storms."), .pokemonScale = 275, .pokemonOffset = 7, .trainerScale = 256, @@ -4294,9 +4294,9 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .weight = 55, .description = COMPOUND_STRING( "Through its nose, it sucks in the\n" - "emanations produced by people and Pokémon\n" - "when they feel annoyed. It thrives off\n" - "this negative energy."), + "emanations produced by people and\n" + "Pokémon when they feel annoyed. It\n" + "thrives off this negative energy."), .pokemonScale = 491, .pokemonOffset = 12, .trainerScale = 256, @@ -5577,9 +5577,9 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .height = 230, .weight = 0, .description = COMPOUND_STRING( - "After this Pokémon has\n" - "Gigantamaxed, its massive nose can utterly\n" - "demolish large structures with a single\n" + "After this Pokémon has Gigantamaxed,\n" + "its massive nose can utterly demolish\n" + "large structures with a single\n" "smashing blow."), .pokemonScale = 275, .pokemonOffset = 7, @@ -6255,8 +6255,8 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .weight = 1100, .description = COMPOUND_STRING( "Known as a legendary hero, this Pokémon\n" - "absorbs metal particles, transforming them\n" - "into a weapon it uses to battle."), + "absorbs metal particles, transforming\n" + "them into a weapon it uses to battle."), .pokemonScale = 275, .pokemonOffset = 7, .trainerScale = 256, diff --git a/src/data/pokemon/species_info/gen_9_families.h b/src/data/pokemon/species_info/gen_9_families.h index 5a616bf4ff..6638676a8d 100644 --- a/src/data/pokemon/species_info/gen_9_families.h +++ b/src/data/pokemon/species_info/gen_9_families.h @@ -1019,8 +1019,8 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .height = 3, .weight = 25, .description = COMPOUND_STRING( - "The pads of its paws are\n" - "electricity-discharging organs. Pawmi fires\n" + "The pads of its paws are electricity-\n" + "discharging organs. Pawmi fires\n" "electricity from its forepaws while\n" "standing unsteadily on its hind legs."), .pokemonScale = 356, @@ -3939,8 +3939,8 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .height = 13, .weight = 602, .description = COMPOUND_STRING( - "This Pokémon changes its\n" - "appearance if it hears its allies calling for\n" + "This Pokémon changes its appearance\n" + "if it hears its allies calling for\n" "help. Palafin will never show\n" "anybody its moment of transformation."), .pokemonScale = 356, @@ -4002,8 +4002,8 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .weight = 974, .description = COMPOUND_STRING( "This Pokémon's ancient genes have\n" - "awakened. It is now so\n" - "extraordinarily strong that it can easily lift a\n" + "awakened. It is now so extraordinarily\n" + "strong that it can easily lift a\n" "cruise ship with one fin."), .pokemonScale = 356, .pokemonOffset = 17, diff --git a/src/data/pokemon/species_info/shared_dex_text.h b/src/data/pokemon/species_info/shared_dex_text.h index 0c591bd811..bf7e12aefb 100644 --- a/src/data/pokemon/species_info/shared_dex_text.h +++ b/src/data/pokemon/species_info/shared_dex_text.h @@ -73,9 +73,9 @@ const u8 gGenesectPokedexText[] = _( // Gen 6 families const u8 gGreninjaPokedexText[] = _( "It appears and vanishes with a ninja's\n" - "grace. It toys with its enemies using swift\n" - "movements, while slicing them with throwing\n" - "stars made of compressed water."); + "grace. It toys with its enemies using\n" + "swift movements, while slicing them with\n" + "throwing stars made of compressed water."); const u8 gScatterbugPokedexText[] = _( "When under attack from bird Pokémon,\n" @@ -207,7 +207,7 @@ const u8 gKommoOPokedexText[] = _( // Gen 8 families const u8 gAlcremieVanillaCreamPokedexText[] = _( "If Alcremie is content, the secreted cream\n" - "from its hands becomes sweeter and richer." + "from its hands becomes sweeter and richer.\n" "When it trusts a Trainer, it will treat\n" "them to berries it's decorated with cream."); @@ -279,8 +279,8 @@ const u8 gOgerponWellspringMaskPokedexText[] = _( const u8 gOgerponHearthflameMaskPokedexText[] = _( "This form is the most aggressive,\n" - "bombarding enemies with the\n" - "intensity of flames blazing within a hearth."); + "bombarding enemies with the intensity\n" + "of flames blazing within a hearth."); const u8 gOgerponCornerstoneMaskPokedexText[] = _( "In this form, it draws on the power\n" diff --git a/test/text.c b/test/text.c index 0e6900edad..7713784198 100644 --- a/test/text.c +++ b/test/text.c @@ -79,6 +79,18 @@ TEST("Move names fit on Move Relearner Screen") EXPECT_LE(GetStringWidth(fontId, gMovesInfo[move].name, 0), widthPx); } +TEST("Move descriptions fit on Pokemon Summary Screen") +{ + u32 i; + const u32 fontId = FONT_NORMAL, widthPx = 152; + u32 move = MOVE_NONE; + for (i = 1; i < MOVES_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gMovesInfo[i].description) { move = i; } + } + EXPECT_LE(GetStringWidth(fontId, gMovesInfo[move].description, 0), widthPx); +} + TEST("Item names fit on Bag Screen (list)") { u32 i; @@ -272,6 +284,18 @@ TEST("Item names fit on Shop Screen") EXPECT_LE(GetStringWidth(fontId, gItemsInfo[item].name, 0), widthPx); } +TEST("Item descriptions fit on Bag and Shop Screen") +{ + u32 i; + const u32 fontId = FONT_NORMAL, widthPx = 102; + u32 item = ITEM_NONE; + for (i = 1; i < ITEMS_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gItemsInfo[i].description) { item = i; } + } + EXPECT_LE(GetStringWidth(fontId, gItemsInfo[item].description, 0), widthPx); +} + TEST("Species names fit on Battle Screen HP box") { u32 i, genderWidthPx; @@ -520,6 +544,21 @@ TEST("Species names fit on Battle Screen HP box for vanilla mons with the defaul EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); } +TEST("Species dex entries fit on Pokedex Screen") +{ + u32 i; + const u32 fontId = FONT_NORMAL, widthPx = 224; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].description) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].description, 0), widthPx); +} + TEST("Ability names fit on Pokemon Summary Screen") { u32 i; @@ -544,6 +583,18 @@ TEST("Ability names fit on Ability Pop-Up") EXPECT_LE(GetStringWidth(fontId, gAbilitiesInfo[ability].name, 0), widthPx); } +TEST("Ability descriptions fit on Pokemon Summary Screen") +{ + u32 i; + const u32 fontId = FONT_NORMAL, widthPx = 146; + u32 ability = ABILITY_NONE; + for (i = 1; i < ABILITIES_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gAbilitiesInfo[i].description) { ability = i; } + } + EXPECT_LE(GetStringWidth(fontId, gAbilitiesInfo[ability].description, 0), widthPx); +} + TEST("Type names fit on Battle Screen") { u32 i; From a01f9b4708f35a0a97541450c1f703c73967d70c Mon Sep 17 00:00:00 2001 From: aronson Date: Sat, 12 Oct 2024 13:41:26 -0500 Subject: [PATCH 64/65] Align EWRAM and IWRAM data-filled sections to 4 bytes (#5512) --- ld_script.ld | 2 ++ ld_script_modern.ld | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ld_script.ld b/ld_script.ld index a0c069c965..6c8467a002 100644 --- a/ld_script.ld +++ b/ld_script.ld @@ -18,6 +18,7 @@ SECTIONS { { __ewram_start = .; *(.ewram*) + . = ALIGN(4); __ewram_end = .; } > EWRAM @@ -38,6 +39,7 @@ SECTIONS { { __iwram_start = .; *(.iwram*); + . = ALIGN(4); __iwram_end = .; } > IWRAM diff --git a/ld_script_modern.ld b/ld_script_modern.ld index fd35a1ca31..11e53db63d 100644 --- a/ld_script_modern.ld +++ b/ld_script_modern.ld @@ -19,6 +19,7 @@ SECTIONS { { __ewram_start = .; *(.ewram*) + . = ALIGN(4); __ewram_end = .; } > EWRAM @@ -34,6 +35,7 @@ SECTIONS { { __iwram_start = .; *(.iwram*); + . = ALIGN(4); __iwram_end = .; } > IWRAM From b91656d1c32e4b55227695a4e2301eee0eb5a17d Mon Sep 17 00:00:00 2001 From: aronson Date: Sun, 13 Oct 2024 01:31:31 -0500 Subject: [PATCH 65/65] Update test LD script to respect 4 byte data section alignment (#5517) --- ld_script_test.ld | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ld_script_test.ld b/ld_script_test.ld index ec99609a7e..a9a2434c57 100644 --- a/ld_script_test.ld +++ b/ld_script_test.ld @@ -27,6 +27,7 @@ SECTIONS { src/*.o(.sbss); gflib/*.o(.sbss); test/*.o(.sbss); + . = ALIGN(4); } > EWRAM .iwram ORIGIN(IWRAM) : AT (__iwram_lma) @@ -34,6 +35,7 @@ SECTIONS { { __iwram_start = .; *(.iwram*); + . = ALIGN(4); __iwram_end = .; } > IWRAM @@ -55,6 +57,7 @@ SECTIONS { data/*.o(COMMON); test/*.o(COMMON); *libc.a:sbrkr.o(COMMON); + . = ALIGN(4); /* .persistent starts at 0x3007F00 */ /* WARNING: This is the end of the IRQ stack, if there's too