diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 5f7e476d2..f6c2c557b 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -1741,6 +1741,17 @@ .2byte \quantity .endm + @ Sets the selected object to the id of the currently approaching trainer. + .macro selectapproachingtrainer + .byte 0xd5 + .endm + + @ Freezes all objects immediately except the player and the approaching trainers. + @ The player and trainers are frozen once their movement is finished. + .macro lockfortrainer + .byte 0xd6 + .endm + .macro _dynmultichoice left:req, top:req, ignoreBPress:req, maxBeforeScroll:req, shouldSort:req, initialSelected:req, callbacks:req argv:vararg .byte 0xe3 .2byte \left diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 16ef08046..95fabc215 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -23,12 +23,23 @@ .align 2 BattleScript_LocalTrainerBattleWon:: + jumpifbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_LocalTwoTrainersDefeated printstring STRINGID_PLAYERDEFEATEDTRAINER1 + goto BattleScript_LocalBattleWonLoseTexts +BattleScript_LocalTwoTrainersDefeated:: + printstring STRINGID_TWOENEMIESDEFEATED +BattleScript_LocalBattleWonLoseTexts:: trainerslidein BS_ATTACKER waitstate printstring STRINGID_TRAINER1LOSETEXT - getmoneyreward + jumpifnotbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_LocalTrainerBattleWonGotMoney + trainerslideout B_POSITION_OPPONENT_LEFT + waitstate + trainerslidein BS_FAINTED + waitstate + printstring STRINGID_TRAINER2LOSETEXT BattleScript_LocalTrainerBattleWonGotMoney:: + getmoneyreward printstring STRINGID_PLAYERGOTMONEY waitmessage B_WAIT_TIME_LONG BattleScript_PayDayMoneyAndPickUpItems:: diff --git a/data/event_scripts.s b/data/event_scripts.s index 6bc678845..d41399a64 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -886,10 +886,6 @@ Text_GiveNicknameToReceivedMon:: .string "Want to give a nickname to the\n" .string "{STR_VAR_2} you received?$" -gText_PkmnFainted3:: - .string "{STR_VAR_1} fainted…\p" - .string "$" - Text_WelcomeWantToHealPkmn:: .string "Welcome to our POKéMON CENTER!\p" .string "Would you like me to heal your\n" diff --git a/data/script_cmd_table.inc b/data/script_cmd_table.inc index 79c33c171..2263c7d01 100644 --- a/data/script_cmd_table.inc +++ b/data/script_cmd_table.inc @@ -214,6 +214,8 @@ gScriptCmdTable:: .4byte ScrCmd_setmonmetlocation @ 0xd2 .4byte ScrCmd_getbraillestringwidth @ 0xd3 .4byte ScrCmd_bufferitemnameplural @ 0xd4 + .4byte ScrCmd_selectapproachingtrainer @ 0xd5 + .4byte ScrCmd_lockfortrainer @ 0xd6 gScriptCmdTableEnd:: .4byte ScrCmd_nop diff --git a/data/scripts/trainer_battle.inc b/data/scripts/trainer_battle.inc index a98099262..9921b9642 100644 --- a/data/scripts/trainer_battle.inc +++ b/data/scripts/trainer_battle.inc @@ -1,9 +1,11 @@ EventScript_DoTrainerBattleFromApproach:: - lock + selectapproachingtrainer + lockfortrainer +EventScript_TrainerApproach:: special PlayTrainerEncounterMusic special EndTrainerApproach waitstate - goto EventScript_DoTrainerBattle + goto EventScript_ShowTrainerIntroMsg EventScript_TryDoNormalTrainerBattle:: lock @@ -16,7 +18,7 @@ EventScript_TryDoNormalTrainerBattle:: goto_if_ne VAR_RESULT, FALSE, EventScript_NoTrainerBattle special PlayTrainerEncounterMusic special SetUpTrainerMovement - goto EventScript_DoTrainerBattle + goto EventScript_ShowTrainerIntroMsg EventScript_NoTrainerBattle:: gotopostbattlescript @@ -31,7 +33,7 @@ EventScript_TryDoDoubleTrainerBattle:: goto_if_ne VAR_RESULT, PLAYER_HAS_TWO_USABLE_MONS, EventScript_NotEnoughMonsForDoubleBattle special PlayTrainerEncounterMusic special SetUpTrainerMovement - goto EventScript_DoTrainerBattle + goto EventScript_ShowTrainerIntroMsg EventScript_NotEnoughMonsForDoubleBattle:: special ShowTrainerCantBattleSpeech @@ -112,10 +114,15 @@ Movement_RevealTrainer:: reveal_trainer step_end -EventScript_DoTrainerBattle:: +EventScript_ShowTrainerIntroMsg:: special ShowTrainerIntroSpeech waitmessage waitbuttonpress + special TryPrepareSecondApproachingTrainer + goto_if_eq VAR_RESULT, TRUE, EventScript_TrainerApproach + goto EventScript_DoTrainerBattle + +EventScript_DoTrainerBattle:: goto_if_questlog EventScript_EndQuestLogBattle dotrainerbattle specialvar VAR_RESULT, GetTrainerBattleMode diff --git a/data/specials.inc b/data/specials.inc index f1d8f6827..8255c3512 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -456,4 +456,5 @@ gSpecials:: def_special GetSprayId def_special GetLastUsedSprayType def_special TrySkyBattle + def_special TryPrepareSecondApproachingTrainer gSpecialsEnd:: diff --git a/include/battle.h b/include/battle.h index 22e9d948e..5a9e74528 100644 --- a/include/battle.h +++ b/include/battle.h @@ -995,8 +995,6 @@ extern u8 gBattleOutcome; extern u16 gLastUsedItem; extern u32 gBattleTypeFlags; extern struct MonSpritesGfx *gMonSpritesGfxPtr; -extern u16 gTrainerBattleOpponent_A; -extern u16 gTrainerBattleOpponent_B; extern u16 gMoveToLearn; extern u16 gBattleMovePower; extern struct BattleEnigmaBerry gEnigmaBerries[MAX_BATTLERS_COUNT]; diff --git a/include/battle_controllers.h b/include/battle_controllers.h index 67a808831..a01fed44a 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -266,7 +266,7 @@ void BattleControllerComplete(u32 battler); // Can be used for all the controlle void BtlController_Empty(u32 battler); // Empty command, does nothing, only completes the execution. void BtlController_TerminatorNop(u32 battler); // Dummy function at the end of the table. void BattleControllerDummy(u32 battler); -void StartSendOutAnim(u32 battler, bool32 dontClearSubstituteBit); +void StartSendOutAnim(u32 battler, bool32 dontClearSubstituteBit, bool32 doSlideIn); void Controller_WaitForString(u32 battler); // handlers diff --git a/include/battle_interface.h b/include/battle_interface.h index 6c66fe336..0404fe05c 100644 --- a/include/battle_interface.h +++ b/include/battle_interface.h @@ -38,15 +38,11 @@ enum #define TAG_HEALTHBOX_PALS_1 55049 #define TAG_HEALTHBOX_PALS_2 55050 #define TAG_HEALTHBOX_SAFARI_TILE 55051 -#define TAG_PARTY_SUMMARY_BAR_PLAYER_TILE 55052 -#define TAG_PARTY_SUMMARY_BAR_OPPONENT_TILE 55053 +#define TAG_PARTY_SUMMARY_BAR_TILE 55052 -#define TAG_PARTY_SUMMARY_BAR_PLAYER_PAL 55056 -#define TAG_PARTY_SUMMARY_BAR_OPPONENT_PAL 55057 -#define TAG_PARTY_SUMMARY_BALL_PLAYER_PAL 55058 -#define TAG_PARTY_SUMMARY_BALL_OPPONENT_PAL 55059 -#define TAG_PARTY_SUMMARY_BALL_PLAYER_TILE 55060 -#define TAG_PARTY_SUMMARY_BALL_OPPONENT_TILE 55061 +#define TAG_PARTY_SUMMARY_BAR_PAL 55056 +#define TAG_PARTY_SUMMARY_BALL_PAL 55058 +#define TAG_PARTY_SUMMARY_BALL_TILE 55060 #define TAG_HEALTHBOX_PAL TAG_HEALTHBOX_PLAYER1_TILE #define TAG_HEALTHBAR_PAL TAG_HEALTHBAR_PLAYER1_TILE diff --git a/include/battle_main.h b/include/battle_main.h index f10b41120..9a84f1ffc 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -92,6 +92,7 @@ void SpriteCB_FaintSlideAnim(struct Sprite *sprite); void DoBounceEffect(u8 battler, u8 which, s8 delta, s8 amplitude); void EndBounceEffect(u8 battler, u8 which); void SpriteCB_PlayerMonFromBall(struct Sprite *sprite); +void SpriteCB_PlayerMonSlideIn(struct Sprite *sprite); void SpriteCB_PlayerThrowInit(struct Sprite *sprite); void UpdatePlayerPosInThrowAnim(struct Sprite *sprite); void BeginBattleIntroDummy(void); diff --git a/include/battle_setup.h b/include/battle_setup.h index d221633df..3325939f2 100644 --- a/include/battle_setup.h +++ b/include/battle_setup.h @@ -1,8 +1,8 @@ #ifndef GUARD_BATTLE_SETUP_H #define GUARD_BATTLE_SETUP_H -#include "global.h" - +extern u16 gTrainerBattleOpponent_A; +extern u16 gTrainerBattleOpponent_B; extern u16 gPartnerTrainerId; void StartWildBattle(void); @@ -17,8 +17,11 @@ void StartGroudonKyogreBattle(void); void StartRegiBattle(void); u8 BattleSetup_GetTerrainId(void); u8 BattleSetup_GetBattleTowerBattleTransition(void); +void ResetTrainerOpponentIds(void); const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data); void ConfigureAndSetUpOneTrainerBattle(u8 trainerEventObjId, const u8 *trainerScript); +void ConfigureTwoTrainersBattle(u8 trainerObjEventId, const u8 *trainerScript); +void SetUpTwoTrainersBattle(void); bool32 GetTrainerFlagFromScriptPointer(const u8 *data); void SetUpTrainerMovement(void); u8 GetTrainerBattleMode(void); @@ -27,7 +30,7 @@ void SetBattledTrainerFlag(void); bool8 HasTrainerBeenFought(u16 trainerId); void SetTrainerFlag(u16 trainerId); void ClearTrainerFlag(u16 trainerId); -void StartTrainerBattle(void); +void BattleSetup_StartTrainerBattle(void); void StartRematchBattle(void); void ShowTrainerIntroSpeech(void); const u8 *BattleSetup_GetScriptAddrAfterBattle(void); @@ -35,6 +38,7 @@ const u8 *BattleSetup_GetTrainerPostBattleScript(void); void ShowTrainerCantBattleSpeech(void); void PlayTrainerEncounterMusic(void); const u8 *GetTrainerALoseText(void); +const u8 *GetTrainerBLoseText(void); const u8 *GetTrainerWonSpeech(void); void BattleSetup_StartTrainerBattle_Debug(void); diff --git a/include/battle_tower.h b/include/battle_tower.h index 16164eccf..f10f21fa7 100644 --- a/include/battle_tower.h +++ b/include/battle_tower.h @@ -29,7 +29,7 @@ extern const u16 gBattleTowerBannedSpecies[]; void ClearEReaderTrainer(struct BattleTowerEReaderTrainer *); void ValidateEReaderTrainer(void); -u8 GetBattleTowerTrainerFrontSpriteId(void); +u8 GetBattleTowerTrainerFrontSpriteId(u16 trainerId); u8 GetEreaderTrainerFrontSpriteId(void); void CopyEReaderTrainerName5(u8 *dest); void GetBattleTowerTrainerName(u8 *text); diff --git a/include/config/overworld.h b/include/config/overworld.h index 77ad62f65..b66642699 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -6,7 +6,6 @@ // Other settings #define OW_POISON_DAMAGE GEN_LATEST // In Gen4, Pokémon no longer faint from Poison in the overworld. In Gen5+, they no longer take damage at all. -#define OW_TIMES_OF_DAY GEN_LATEST // Different generations have the times of day change at different times. #define OW_DOUBLE_APPROACH_WITH_ONE_MON FALSE // If enabled, you can be spotted by two trainers at the same time even if you only have one eligible Pokémon in your party. #define OW_HIDE_REPEAT_MAP_POPUP FALSE // If enabled, map popups will not appear if entering a map with the same Map Section Id as the last. @@ -44,7 +43,7 @@ // (You should not use 48x48 sprites/tables for compressed gfx) // 16x32, 32x32, 64x64 etc are fine // Follower Pokémon -#define OW_FOLLOWERS_ENABLED TRUE // Enables follower Pokémon, HGSS style. Requires OW_POKEMON_OBJECT_EVENTS. +#define OW_FOLLOWERS_ENABLED FALSE // Enables follower Pokémon, HGSS style. Requires OW_POKEMON_OBJECT_EVENTS. Note that additional scripting may be required for them to be fully supported! #define OW_FOLLOWERS_BOBBING TRUE // If true, follower pokemon will bob up and down during their idle & walking animations #define OW_FOLLOWERS_POKEBALLS TRUE // Followers will emerge from the pokeball they are stored in, instead of a normal pokeball @@ -59,9 +58,18 @@ #define OW_STORM_DRAIN GEN_LATEST // In Gen8+, if a Pokémon with Storm Drain is leading the party, there is a 50% chance to encounter a Water-type Pokémon. #define OW_FLASH_FIRE GEN_LATEST // In Gen8+, if a Pokémon with Flash Fire is leading the party, there is a 50% chance to encounter a Fire-type Pokémon. +// These generational defines only make a distinction for OW_ALTERED_TIME_RATIO +#define GEN_8_PLA GEN_LATEST + 2 + +//Time +#define OW_TIMES_OF_DAY GEN_LATEST // Different generations have the times of day change at different times. +#define OW_USE_FAKE_RTC FALSE // When TRUE, seconds on the in-game clock will only advance once every 60 playTimeVBlanks (every 60 frames). +#define OW_ALTERED_TIME_RATIO GEN_LATEST // In GEN_8_PLA, the time in game moves forward 60 seconds for every second in the RTC. In GEN_9, it is 20 seconds. This has no effect if OW_USE_FAKE_RTC is FALSE. + // Overworld flags // To use the following features in scripting, replace the 0s with the flag ID you're assigning it to. // Eg: Replace with FLAG_UNUSED_0x264 so you can use that flag to toggle the feature. +#define OW_FLAG_PAUSE_TIME 0 // If this flag is set and OW_USE_FAKE_RTC is enabled, seconds on the in-game clock will not advance. #define OW_FLAG_NO_ENCOUNTER 0 // If this flag is set, wild encounters will be disabled. #define OW_FLAG_NO_TRAINER_SEE 0 // If this flag is set, trainers will not battle the player unless they're talked to. #define OW_FLAG_NO_COLLISION 0 // If this flag is set, the player will be able to walk over tiles with collision. Mainly intended for debugging purposes. @@ -82,8 +90,11 @@ #define OW_POPUP_BW_COLOR_WHITE 1 // White pop-up from W2 // Configuration -#define OW_POPUP_BW_COLOR OW_POPUP_BW_COLOR_BLACK // B2W2 use different colors for their map pop-ups. +#define OW_POPUP_BW_COLOR OW_POPUP_BW_COLOR_BLACK // B2W2 use different colors for their map pop-ups. #define OW_POPUP_BW_TIME_MODE OW_POPUP_BW_TIME_NONE // Determines what type of time is shown. #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. + #endif // GUARD_CONFIG_OVERWORLD_H diff --git a/include/event_object_lock.h b/include/event_object_lock.h index 43ce97173..26d794993 100644 --- a/include/event_object_lock.h +++ b/include/event_object_lock.h @@ -7,8 +7,9 @@ bool8 IsFreezePlayerFinished(void); void FreezeObjects_WaitForPlayer(void); bool8 IsFreezeSelectedObjectAndPlayerFinished(void); void FreezeObjects_WaitForPlayerAndSelected(void); +void FreezeForApproachingTrainers(void); void ClearPlayerHeldMovementAndUnfreezeObjectEvents(void); -bool8 walkrun_is_standing_still(void); +bool8 IsPlayerStandingStill(void); void UnionRoom_UnlockPlayerAndChatPartner(void); #endif // GUARD_EVENT_OBJECT_LOCK_H diff --git a/include/event_object_movement.h b/include/event_object_movement.h index c4b99e26a..068ae4c57 100644 --- a/include/event_object_movement.h +++ b/include/event_object_movement.h @@ -1,6 +1,10 @@ #ifndef GUARD_EVENT_OBJECT_MOVEMENT_H #define GUARD_EVENT_OBJECT_MOVEMENT_H +#if OW_POKEMON_OBJECT_EVENTS == FALSE && OW_FOLLOWERS_ENABLED == TRUE +#error "OW_POKEMON_OBJECT_EVENTS needs to be TRUE in order for OW_FOLLOWERS_ENABLED to work." +#endif + // Palette slots for overworld NPCs. // The same standard set of palettes for overworld objects are normally always loaded at the same // time while walking around the overworld. The only exceptions are the palettes for the player and @@ -108,7 +112,7 @@ struct ObjectEvent *GetFollowerObject(void); u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevation, u8 direction); u8 CreateObjectGraphicsSprite(u16 graphicsId, SpriteCallback callback, s16 x, s16 y, u8 subpriority); u8 TrySpawnObjectEvent(u8 localId, u8 mapNum, u8 mapGroup); -int SpawnSpecialObjectEventParameterized(u16 graphicsId, u8 movementBehavior, u8 localId, s16 x, s16 y, u8 elevation); +u32 SpawnSpecialObjectEventParameterized(u16 graphicsId, u8 movementBehavior, u8 localId, s16 x, s16 y, u8 elevation); u8 SpawnSpecialObjectEvent(struct ObjectEventTemplate *); void CameraObjectReset1(void); void CameraObjectReset2(void); @@ -178,6 +182,7 @@ void FreezeObjectEvents(void); bool8 FreezeObjectEvent(struct ObjectEvent *); void UnfreezeObjectEvent(struct ObjectEvent *); void FreezeObjectEventsExceptOne(u8 objEventId); +void FreezeObjectEventsExceptTwo(u8 objectEventId1, u8 objectEventId2); void SetVirtualObjectGraphics(u8 virtualObjId, u16 graphicsId); void SetVirtualObjectInvisibility(u8 virtualObjId, bool32 invisible); bool32 IsVirtualObjectInvisible(u8 virtualObjId); diff --git a/include/pokeball.h b/include/pokeball.h index 9e354a399..44917ac31 100644 --- a/include/pokeball.h +++ b/include/pokeball.h @@ -50,6 +50,7 @@ extern const struct SpriteTemplate gBallSpriteTemplates[]; #define POKEBALL_PLAYER_SENDOUT 0xFF #define POKEBALL_OPPONENT_SENDOUT 0xFE +#define POKEBALL_PLAYER_SLIDEIN 0xFD u8 DoPokeballSendOutAnimation(u32 battler, s16 pan, u8 kindOfThrow); void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriortiy, u8 delay, u32 fadePalettes); diff --git a/include/pokemon.h b/include/pokemon.h index 6573db154..8d2a17522 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -762,6 +762,7 @@ u8 GiveMonToPlayer(struct Pokemon *mon); u8 CalculatePlayerPartyCount(void); u8 CalculateEnemyPartyCount(void); u8 GetMonsStateToDoubles(void); +u8 GetMonsStateToDoubles_2(void); u16 GetAbilityBySpecies(u16 species, u8 abilityNum); u16 GetMonAbility(struct Pokemon *mon); u8 GetSecretBaseTrainerPicIndex(void); diff --git a/include/recorded_battle.h b/include/recorded_battle.h index 628547f0c..dde7008ca 100644 --- a/include/recorded_battle.h +++ b/include/recorded_battle.h @@ -64,7 +64,6 @@ void RecordedBattle_ClearBattlerAction(u8 battlerId, u8 bytesToClear); u8 RecordedBattle_GetBattlerAction(u32 actionType, u8 battlerId); u8 RecordedBattle_BufferNewBattlerData(u8 *dst); void RecordedBattle_RecordAllBattlerData(u8 *data); -bool32 MoveRecordedBattleToSaveData(void); void SetPartiesFromRecordedSave(struct RecordedBattleSave *src); void SetVariablesForRecordedBattle(struct RecordedBattleSave *); void RecordedBattle_SaveParties(void); diff --git a/include/script.h b/include/script.h index ce10c8726..5a0087c7e 100644 --- a/include/script.h +++ b/include/script.h @@ -72,6 +72,9 @@ bool8 CanWalkAwayToCancelMsgBox(void); void SetWalkingIntoSignVars(void); bool8 IsMsgBoxWalkawayDisabled(void); +// srccmd.h +void SetMovingNpcId(u16 npcId); + extern const u8 *gRamScriptRetAddr; extern u8 gWalkAwayFromSignInhibitTimer; diff --git a/include/strings.h b/include/strings.h index f7c8d0b38..38f39047b 100644 --- a/include/strings.h +++ b/include/strings.h @@ -83,7 +83,7 @@ extern const u8 gText_RegisteredDataDeleted[]; extern const u8 gText_Confirm3[]; extern const u8 gText_Cancel4[]; extern const u8 gText_IsThisTheCorrectTime[]; -extern const u8 gText_PkmnFainted3[]; +extern const u8 gText_PkmnFainted_FldPsn[]; extern const u8 gText_Coins[]; extern const u8 gText_EggNickname[]; extern const u8 gText_MenuPokemon[]; diff --git a/include/trainer_see.h b/include/trainer_see.h index ced4ff550..e94e535e5 100644 --- a/include/trainer_see.h +++ b/include/trainer_see.h @@ -1,10 +1,19 @@ #ifndef GUARD_TRAINER_SEE_H #define GUARD_TRAINER_SEE_H -#include "global.h" +struct ApproachingTrainer +{ + u8 objectEventId; + u8 radius; // plus 1 + const u8 *trainerScriptPtr; + u8 taskId; +}; extern u16 gWhichTrainerToFaceAfterBattle; +extern struct ApproachingTrainer gApproachingTrainers[2]; extern u8 gNoOfApproachingTrainers; +extern bool8 gTrainerApproachedPlayer; +extern u8 gApproachingTrainerId; bool8 CheckForTrainersWantingBattle(void); void MovementAction_RevealTrainer_RunTrainerSeeFuncList(struct ObjectEvent *var); @@ -13,5 +22,8 @@ u8 FldEff_DoubleExclMarkIcon(void); u8 FldEff_XIcon(void); u8 FldEff_SmileyFaceIcon(void); u8 FldEff_QuestionMarkIcon(void); +u8 GetCurrentApproachingTrainerObjectEventId(void); +u8 GetChosenApproachingTrainerObjectEventId(u8 arrayId); +void TryPrepareSecondApproachingTrainer(void); #endif //GUARD_TRAINER_SEE_H diff --git a/src/battle_bg.c b/src/battle_bg.c index 555697f97..1c58cdab2 100644 --- a/src/battle_bg.c +++ b/src/battle_bg.c @@ -4,6 +4,7 @@ #include "battle_anim.h" #include "battle_bg.h" #include "battle_message.h" +#include "battle_setup.h" #include "battle_terrain.h" #include "decompress.h" #include "graphics.h" diff --git a/src/battle_controller_link_opponent.c b/src/battle_controller_link_opponent.c index f3dc2225a..59dbe5678 100644 --- a/src/battle_controller_link_opponent.c +++ b/src/battle_controller_link_opponent.c @@ -4,6 +4,7 @@ #include "battle_controllers.h" #include "battle_interface.h" #include "battle_message.h" +#include "battle_setup.h" #include "gflib.h" #include "data.h" #include "link.h" diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 0e5cf7611..1826f8417 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -8,14 +8,15 @@ #include "random.h" #include "battle.h" #include "battle_ai_main.h" +#include "battle_ai_switch_items.h" #include "battle_ai_util.h" #include "battle_anim.h" #include "battle_controllers.h" -#include "battle_message.h" -#include "battle_interface.h" -#include "battle_tower.h" #include "battle_gfx_sfx_util.h" -#include "battle_ai_switch_items.h" +#include "battle_interface.h" +#include "battle_message.h" +#include "battle_setup.h" +#include "battle_tower.h" #include "battle_z_move.h" #include "link.h" #include "party_menu.h" @@ -136,32 +137,60 @@ static void Intro_DelayAndEnd(u32 battler) static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) { - bool8 var = FALSE; + bool8 healthboxAnimDone = FALSE; + bool8 twoMons; - if (!IsDoubleBattle() || ((IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI)))) + twoMons = TwoOpponentIntroMons(battler); + if (!twoMons || ((twoMons && (gBattleTypeFlags & BATTLE_TYPE_MULTI) && !BATTLE_TWO_VS_ONE_OPPONENT) || (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))) { if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy) - var = TRUE; + healthboxAnimDone = TRUE; + twoMons = FALSE; } - else if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy - && gSprites[gHealthboxSpriteIds[BATTLE_PARTNER(battler)]].callback == gSprites[gHealthboxSpriteIds[battler]].callback) + else { - var = TRUE; + if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy + && gSprites[gHealthboxSpriteIds[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy) + healthboxAnimDone = TRUE; + twoMons = TRUE; } - if (IsCryPlayingOrClearCrySongs()) - var = FALSE; - if (var && gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim && gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim) + + if (healthboxAnimDone) { - gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; - gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; - gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim = FALSE; - gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim = FALSE; - FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); - FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); - if (gBattleTypeFlags & BATTLE_TYPE_MULTI) - m4aMPlayContinue(&gMPlayInfo_BGM); + if (twoMons == TRUE) + { + if (gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim + && gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim) + { + gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; + gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; + gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim = FALSE; + gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim = FALSE; + FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); + FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); + } + else + return; + } + else if (gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) + { + if (GetBattlerPosition(battler) == 3) + { + if (!gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim + && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim) + { + FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); + FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); + } + else + return; + } + gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; + gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; + } else - m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 256); + return; + gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 3; gBattlerControllerFuncs[battler] = Intro_DelayAndEnd; } @@ -169,30 +198,93 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) static void Intro_TryShinyAnimShowHealthbox(u32 battler) { - if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) + bool32 bgmRestored = FALSE; + bool32 battlerAnimsDone = FALSE; + bool32 twoMons; + + if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim + && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive + && !gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) TryShinyAnimation(battler, &gEnemyParty[gBattlerPartyIndexes[battler]]); - if (!gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive) + + twoMons = TwoOpponentIntroMons(battler); + if (!(gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) + && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT) + && twoMons + && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim + && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive + && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim) TryShinyAnimation(BATTLE_PARTNER(battler), &gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]]); + if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive) { - if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + if (!gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted) + { + if (twoMons && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT)) + { + UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battler)], &gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]], HEALTHBOX_ALL); + StartHealthboxSlideIn(BATTLE_PARTNER(battler)); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[BATTLE_PARTNER(battler)]); + } + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gEnemyParty[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL); + StartHealthboxSlideIn(battler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); + } + gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted = TRUE; + } + + if (!gBattleSpritesDataPtr->healthBoxesData[battler].waitForCry + && gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted + && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].waitForCry + && !IsCryPlayingOrClearCrySongs()) + { + if (!gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored) + { + if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_LINK) + { + if (GetBattlerPosition(battler) == 1) + m4aMPlayContinue(&gMPlayInfo_BGM); + } + else + m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100); + } + gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored = TRUE; + bgmRestored = TRUE; + } + + if (!twoMons || (twoMons && gBattleTypeFlags & BATTLE_TYPE_MULTI && !BATTLE_TWO_VS_ONE_OPPONENT)) + { + if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy + && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) + { + battlerAnimsDone = TRUE; + } + } + else + { + if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy + && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy + && gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy + && gSprites[gBattlerSpriteIds[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy) + { + battlerAnimsDone = TRUE; + } + } + + if (bgmRestored && battlerAnimsDone) + { + if (twoMons && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT)) { DestroySprite(&gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]]); - UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battler)], - &gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]], - HEALTHBOX_ALL); - StartHealthboxSlideIn(BATTLE_PARTNER(battler)); - SetHealthboxSpriteVisible(gHealthboxSpriteIds[BATTLE_PARTNER(battler)]); SetBattlerShadowSpriteCallback(BATTLE_PARTNER(battler), GetMonData(&gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]], MON_DATA_SPECIES)); } + DestroySprite(&gSprites[gBattleControllerData[battler]]); - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], - &gEnemyParty[gBattlerPartyIndexes[battler]], - HEALTHBOX_ALL); - StartHealthboxSlideIn(battler); - SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); SetBattlerShadowSpriteCallback(battler, GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES)); gBattleSpritesDataPtr->animationData->introAnimActive = FALSE; + gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored = FALSE; + gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted = FALSE; + gBattlerControllerFuncs[battler] = Intro_WaitForShinyAnimAndHealthbox; } } @@ -298,11 +390,30 @@ static u32 OpponentGetTrainerPicId(u32 battlerId) // TODO: trainer refactoring u32 trainerPicId; if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) - trainerPicId = GetBattleTowerTrainerFrontSpriteId(); + { + if (gBattleTypeFlags & (BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_TOWER_LINK_MULTI)) + { + if (battlerId == 1) + trainerPicId = GetBattleTowerTrainerFrontSpriteId(gTrainerBattleOpponent_A); + else + trainerPicId = GetBattleTowerTrainerFrontSpriteId(gTrainerBattleOpponent_B); + } + else + { + trainerPicId = GetBattleTowerTrainerFrontSpriteId(gTrainerBattleOpponent_A); + } + } else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) trainerPicId = GetTrainerTowerTrainerFrontSpriteId(); else if (gBattleTypeFlags & BATTLE_TYPE_EREADER_TRAINER) trainerPicId = GetEreaderTrainerFrontSpriteId(); + else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) + { + if (battlerId != 1) + trainerPicId = GetTrainerPicFromId(gTrainerBattleOpponent_B); + else + trainerPicId = GetTrainerPicFromId(gTrainerBattleOpponent_A); + } else trainerPicId = GetTrainerPicFromId(gTrainerBattleOpponent_A); @@ -336,7 +447,7 @@ static void OpponentHandleTrainerSlide(u32 battler) } static void OpponentHandleTrainerSlideBack(u32 battler) -{ +{ BtlController_HandleTrainerSlideBack(battler, 35, FALSE); } diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c index 14cf4def1..aa69f5212 100644 --- a/src/battle_controller_recorded_opponent.c +++ b/src/battle_controller_recorded_opponent.c @@ -396,7 +396,10 @@ static void RecordedOpponentHandleDrawTrainerPic(u32 battler) if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) { - trainerPicId = GetBattleTowerTrainerFrontSpriteId(); + if (battler == B_POSITION_OPPONENT_LEFT) + trainerPicId = GetBattleTowerTrainerFrontSpriteId(gTrainerBattleOpponent_A); + else + trainerPicId = GetBattleTowerTrainerFrontSpriteId(gTrainerBattleOpponent_B); } else { diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 4efdd4edc..a8ef1b784 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -6,7 +6,9 @@ #include "battle_controllers.h" #include "battle_interface.h" #include "battle_message.h" +#include "battle_setup.h" #include "cable_club.h" +#include "event_object_movement.h" #include "link.h" #include "link_rfu.h" #include "palette.h" @@ -19,9 +21,9 @@ #include "test_runner.h" #include "text.h" #include "util.h" +#include "constants/abilities.h" #include "constants/songs.h" #include "constants/sound.h" -#include "constants/abilities.h" static EWRAM_DATA u8 sLinkSendTaskId = 0; static EWRAM_DATA u8 sLinkReceiveTaskId = 0; @@ -1834,7 +1836,22 @@ static void SetBattlerMonData(u32 battler, struct Pokemon *party, u32 monId) HandleLowHpMusicChange(&party[gBattlerPartyIndexes[battler]], battler); } -void StartSendOutAnim(u32 battler, bool32 dontClearSubstituteBit) +// In normal singles, if follower pokemon is out, have it slide in instead of being thrown +static bool8 ShouldDoSlideInAnim(void) +{ + struct ObjectEvent *followerObj = GetFollowerObject(); + if (!followerObj || followerObj->invisible) + return FALSE; + if (gBattleTypeFlags & ( + BATTLE_TYPE_LINK | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_FIRST_BATTLE | + BATTLE_TYPE_SAFARI | BATTLE_TYPE_OLD_MAN_TUTORIAL | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TWO_OPPONENTS | + BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_RECORDED | BATTLE_TYPE_POKEDUDE | BATTLE_TYPE_TRAINER_TOWER) + ) + return FALSE; + return TRUE; +} + +void StartSendOutAnim(u32 battler, bool32 dontClearSubstituteBit, bool32 doSlideIn) { u16 species; u32 side = GetBattlerSide(battler); @@ -1865,7 +1882,7 @@ void StartSendOutAnim(u32 battler, bool32 dontClearSubstituteBit) gSprites[gBattleControllerData[battler]].data[1] = gBattlerSpriteIds[battler]; gSprites[gBattleControllerData[battler]].data[2] = battler; - gSprites[gBattleControllerData[battler]].data[0] = DoPokeballSendOutAnimation(battler, 0, (side == B_SIDE_OPPONENT) ? POKEBALL_OPPONENT_SENDOUT : POKEBALL_PLAYER_SENDOUT); + gSprites[gBattleControllerData[battler]].data[0] = DoPokeballSendOutAnimation(battler, 0, (side == B_SIDE_OPPONENT) ? POKEBALL_OPPONENT_SENDOUT : (doSlideIn ? POKEBALL_PLAYER_SLIDEIN : POKEBALL_PLAYER_SENDOUT)); } static void FreeMonSprite(u32 battler) @@ -2213,7 +2230,7 @@ void BtlController_HandleSwitchInAnim(u32 battler, bool32 isPlayerSide, void (*c gBattlerPartyIndexes[battler] = gBattleResources->bufferA[battler][1]; if (isPlayerSide) BattleLoadMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battler]], battler); - StartSendOutAnim(battler, gBattleResources->bufferA[battler][2]); + StartSendOutAnim(battler, gBattleResources->bufferA[battler][2], FALSE); gBattlerControllerFuncs[battler] = controllerCallback; } @@ -2719,17 +2736,17 @@ static void Task_StartSendOutAnim(u8 taskId) if (TwoMonsAtSendOut(battler)) { gBattleResources->bufferA[battler][1] = gBattlerPartyIndexes[battler]; - StartSendOutAnim(battler, FALSE); + StartSendOutAnim(battler, FALSE, ShouldDoSlideInAnim()); battlerPartner = BATTLE_PARTNER(battler); gBattleResources->bufferA[battlerPartner][1] = gBattlerPartyIndexes[battlerPartner]; BattleLoadMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battlerPartner]], battlerPartner); - StartSendOutAnim(battlerPartner, FALSE); + StartSendOutAnim(battlerPartner, FALSE, ShouldDoSlideInAnim()); } else { gBattleResources->bufferA[battler][1] = gBattlerPartyIndexes[battler]; - StartSendOutAnim(battler, FALSE); + StartSendOutAnim(battler, FALSE, ShouldDoSlideInAnim()); } gBattlerControllerFuncs[battler] = (void*)(GetWordTaskArg(taskId, tControllerFunc_1)); DestroyTask(taskId); diff --git a/src/battle_debug.c b/src/battle_debug.c index ae1dff3fc..b0a670db5 100644 --- a/src/battle_debug.c +++ b/src/battle_debug.c @@ -697,8 +697,7 @@ void CB2_BattleDebugMenu(void) break; case 3: LoadPalette(sBgColor, 0, 2); - // LoadPalette(GetOverworldTextboxPalettePtr(), 0xf0, 16); // TODO: debug - LoadPalette(GetTextWindowPalette(2), 0xC0, 0x20); + LoadPalette(GetTextWindowPalette(2), 0xf0, 16); gMain.state++; break; case 4: @@ -1213,6 +1212,7 @@ static void Task_DebugMenuProcessInput(u8 taskId) { data->currentSecondaryListItemId = listItemId; data->modifyWindowId = AddWindow(&sModifyWindowTemplate); + ClearStdWindowAndFrameToTransparent(data->modifyWindowId, TRUE); // FRLG change: fixes number background PutWindowTilemap(data->modifyWindowId); CopyWindowToVram(data->modifyWindowId, COPYWIN_FULL); SetUpModifyArrows(data); diff --git a/src/battle_interface.c b/src/battle_interface.c index 8e097ef55..22b6bfe13 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -367,24 +367,30 @@ static const struct SubspriteTable sStatusSummaryBar_SubspriteTable_Exit[] = { static const u16 sBattleInterface_Unused[] = INCBIN_U16("graphics/battle_interface/unused.4bpp"); -static const struct CompressedSpriteSheet sPartySummaryBarSpriteSheets[] = { - {gBattleInterface_PartySummaryBar_Gfx, 16 * TILE_SIZE_4BPP, TAG_PARTY_SUMMARY_BAR_PLAYER_TILE}, - {gBattleInterface_PartySummaryBar_Gfx, 16 * TILE_SIZE_4BPP, TAG_PARTY_SUMMARY_BAR_OPPONENT_TILE} +static const struct CompressedSpriteSheet sStatusSummaryBarSpriteSheet = +{ + .data = gBattleInterface_PartySummaryBar_Gfx, + .size = 16 * TILE_SIZE_4BPP, + .tag = TAG_PARTY_SUMMARY_BAR_TILE, }; -static const struct SpritePalette sPartySummaryBarSpritePals[] = { - {gBattleInterface_Healthbox_Pal, TAG_PARTY_SUMMARY_BAR_PLAYER_PAL}, - {gBattleInterface_Healthbox_Pal, TAG_PARTY_SUMMARY_BAR_OPPONENT_PAL} +static const struct SpritePalette sPartySummaryBarSpritePals = +{ + .data = gBattleInterface_Healthbox_Pal, + .tag = TAG_PARTY_SUMMARY_BAR_PAL, }; -static const struct SpritePalette sPartySummaryBallSpritePals[] = { - {gBattleInterface_Healthbar_Pal, TAG_PARTY_SUMMARY_BALL_PLAYER_PAL}, - {gBattleInterface_Healthbar_Pal, TAG_PARTY_SUMMARY_BALL_OPPONENT_PAL} +static const struct SpritePalette sPartySummaryBallSpritePals = +{ + .data = gBattleInterface_Healthbar_Pal, + .tag = TAG_PARTY_SUMMARY_BALL_PAL, }; -static const struct SpriteSheet sPartySummaryBallSpriteSheets[] = { - {gBattleInterface_Gfx + B_INTERFACE_GFX_BALL_PARTY_SUMMARY, 4 * TILE_SIZE_4BPP, TAG_PARTY_SUMMARY_BALL_PLAYER_TILE}, - {gBattleInterface_Gfx + B_INTERFACE_GFX_BALL_PARTY_SUMMARY, 4 * TILE_SIZE_4BPP, TAG_PARTY_SUMMARY_BALL_OPPONENT_TILE} +static const struct SpriteSheet sPartySummaryBallSpriteSheets = +{ + .data = gBattleInterface_Gfx + B_INTERFACE_GFX_BALL_PARTY_SUMMARY, + .size = 4 * TILE_SIZE_4BPP, + .tag = TAG_PARTY_SUMMARY_BALL_TILE, }; // Unused @@ -402,15 +408,15 @@ static const struct OamData sOamData_PartySummaryBall = { static const struct SpriteTemplate sPartySummaryBarSpriteTemplates[] = { { - .tileTag = TAG_PARTY_SUMMARY_BAR_PLAYER_TILE, - .paletteTag = TAG_PARTY_SUMMARY_BAR_PLAYER_PAL, + .tileTag = TAG_PARTY_SUMMARY_BAR_TILE, + .paletteTag = TAG_PARTY_SUMMARY_BAR_PAL, .oam = &sOamData_Healthbox, .anims = gDummySpriteAnimTable, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_PartySummaryBar }, { - .tileTag = TAG_PARTY_SUMMARY_BAR_OPPONENT_TILE, - .paletteTag = TAG_PARTY_SUMMARY_BAR_OPPONENT_PAL, + .tileTag = TAG_PARTY_SUMMARY_BAR_TILE, + .paletteTag = TAG_PARTY_SUMMARY_BAR_PAL, .oam = &sOamData_Healthbox, .anims = gDummySpriteAnimTable, .affineAnims = gDummySpriteAffineAnimTable, @@ -420,15 +426,15 @@ static const struct SpriteTemplate sPartySummaryBarSpriteTemplates[] = { static const struct SpriteTemplate sPartySummaryBallSpriteTemplates[] = { { - .tileTag = TAG_PARTY_SUMMARY_BALL_PLAYER_TILE, - .paletteTag = TAG_PARTY_SUMMARY_BALL_PLAYER_PAL, + .tileTag = TAG_PARTY_SUMMARY_BALL_TILE, + .paletteTag = TAG_PARTY_SUMMARY_BALL_PAL, .oam = &sOamData_PartySummaryBall, .anims = gDummySpriteAnimTable, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_PartySummaryBall_OnBattleStart }, { - .tileTag = TAG_PARTY_SUMMARY_BALL_OPPONENT_TILE, - .paletteTag = TAG_PARTY_SUMMARY_BALL_OPPONENT_PAL, + .tileTag = TAG_PARTY_SUMMARY_BALL_TILE, + .paletteTag = TAG_PARTY_SUMMARY_BALL_PAL, .oam = &sOamData_PartySummaryBall, .anims = gDummySpriteAnimTable, .affineAnims = gDummySpriteAffineAnimTable, @@ -1125,10 +1131,10 @@ u8 CreatePartyStatusSummarySprites(u8 battlerId, struct HpAndStatus *partyInfo, nValidMons++; } - LoadCompressedSpriteSheetUsingHeap(&sPartySummaryBarSpriteSheets[isOpponent]); - LoadSpriteSheet(&sPartySummaryBallSpriteSheets[isOpponent]); - LoadSpritePalette(&sPartySummaryBarSpritePals[isOpponent]); - LoadSpritePalette(&sPartySummaryBallSpritePals[isOpponent]); + LoadCompressedSpriteSheetUsingHeap(&sStatusSummaryBarSpriteSheet); + LoadSpriteSheet(&sPartySummaryBallSpriteSheets); + LoadSpritePalette(&sPartySummaryBarSpritePals); + LoadSpritePalette(&sPartySummaryBallSpritePals); summaryBarSpriteId = CreateSprite(&sPartySummaryBarSpriteTemplates[isOpponent], x, y, 10); SetSubspriteTables(&gSprites[summaryBarSpriteId], sStatusSummaryBar_SubspriteTable_Enter); diff --git a/src/battle_intro.c b/src/battle_intro.c index 12e3d762f..a2ebe3b64 100644 --- a/src/battle_intro.c +++ b/src/battle_intro.c @@ -90,6 +90,9 @@ s32 GetAnimBgAttribute(u8 bgId, u8 attributeId) return 0; } +#define tState data[0] +#define tTerrain data[1] + void HandleIntroSlide(u8 terrain) { u8 taskId; @@ -107,8 +110,9 @@ void HandleIntroSlide(u8 terrain) { taskId = CreateTask(sBattleIntroSlideFuncs[terrain], 0); } - gTasks[taskId].data[0] = 0; - gTasks[taskId].data[1] = terrain; + + gTasks[taskId].tState = 0; + gTasks[taskId].tTerrain = terrain; gTasks[taskId].data[2] = 0; gTasks[taskId].data[3] = 0; gTasks[taskId].data[4] = 0; diff --git a/src/battle_main.c b/src/battle_main.c index fa5b2492f..308c59033 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -50,6 +50,7 @@ #include "constants/moves.h" #include "constants/pokemon.h" #include "constants/songs.h" +#include "constants/sound.h" #include "constants/trainers.h" static void HandleEndTurn_ContinueBattle(void); @@ -2145,6 +2146,37 @@ void SpriteCB_PlayerMonFromBall(struct Sprite *sprite) BattleAnimateBackSprite(sprite, sprite->sSpeciesId); } +void SpriteCB_PlayerMonSlideIn(struct Sprite *sprite) +{ + if (sprite->data[3] == 0) + { + PlaySE(SE_BALL_TRAY_ENTER); + sprite->data[3]++; + } + else if (sprite->data[3] == 1) + { + if (sprite->animEnded) + return; + sprite->data[4] = sprite->x; + sprite->x = -33; + sprite->invisible = FALSE; + sprite->data[3]++; + } + else if (sprite->data[3] < 27) + { + sprite->x += 4; + sprite->data[3]++; + } + else + { + sprite->data[3] = 0; + sprite->x = sprite->data[4]; + sprite->data[4] = 0; + sprite->callback = SpriteCB_PlayerMonFromBall; + PlayCry_ByMode(sprite->sSpeciesId, -25, CRY_MODE_NORMAL); + } +} + void SpriteCB_PlayerThrowInit(struct Sprite *sprite) { StartSpriteAnim(sprite, 1); diff --git a/src/battle_message.c b/src/battle_message.c index 89bd1dc25..cf3a4dd96 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -3178,12 +3178,22 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) } break; case B_TXT_TRAINER2_LOSE_TEXT: - GetTrainerTowerOpponentLoseText(gStringVar4, 1); - toCpy = gStringVar4; + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) + { + GetTrainerTowerOpponentLoseText(gStringVar4, 1); + toCpy = gStringVar4; + } + else + { + toCpy = GetTrainerBLoseText(); + } break; case B_TXT_TRAINER2_WIN_TEXT: - GetTrainerTowerOpponentWinText(gStringVar4, 1); - toCpy = gStringVar4; + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) + { + GetTrainerTowerOpponentWinText(gStringVar4, 1); + toCpy = gStringVar4; + } break; case B_TXT_26: // ? HANDLE_NICKNAME_STRING_CASE(gBattleScripting.battler) @@ -3240,7 +3250,8 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) // TODO: implement partner trainers? // if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) // toCpy = gTrainerClasses[GetFrontierOpponentClass(gPartnerTrainerId)].name; - toCpy = gTrainerClasses[TRAINER_CLASS_NONE].name; + if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) + toCpy = gTrainerClasses[gPartnerTrainerId].name; break; case B_POSITION_OPPONENT_LEFT: toCpy = BattleStringGetOpponentClassByTrainerId(gTrainerBattleOpponent_A); @@ -3267,7 +3278,6 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) break; case B_TXT_DEF_NAME: // target name HANDLE_NICKNAME_STRING_CASE(gBattlerTarget) - // GetBattlerNick(gBattlerTarget, text); toCpy = text; break; case B_TXT_DEF_TEAM1: @@ -3284,12 +3294,15 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) break; } - // missing if (toCpy != NULL) check - while (*toCpy != EOS) + if (toCpy != NULL) { - dst[dstId++] = *toCpy; - toCpy++; + while (*toCpy != EOS) + { + dst[dstId++] = *toCpy; + toCpy++; + } } + if (*src == B_TXT_TRAINER1_LOSE_TEXT || *src == B_TXT_TRAINER1_WIN_TEXT || *src == B_TXT_TRAINER2_LOSE_TEXT || *src == B_TXT_TRAINER2_WIN_TEXT) { diff --git a/src/battle_setup.c b/src/battle_setup.c index 7781da23d..1e5155fb1 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -77,11 +77,14 @@ EWRAM_DATA u16 gTrainerBattleOpponent_B = 0; EWRAM_DATA u16 gPartnerTrainerId = 0; static EWRAM_DATA u16 sTrainerObjectEventLocalId = 0; static EWRAM_DATA u8 *sTrainerAIntroSpeech = NULL; +EWRAM_DATA static u8 *sTrainerBIntroSpeech = NULL; static EWRAM_DATA u8 *sTrainerADefeatSpeech = NULL; +EWRAM_DATA static u8 *sTrainerBDefeatSpeech = NULL; static EWRAM_DATA u8 *sTrainerVictorySpeech = NULL; static EWRAM_DATA u8 *sTrainerCannotBattleSpeech = NULL; static EWRAM_DATA u8 *sTrainerBattleEndScript = NULL; static EWRAM_DATA u8 *sTrainerABattleScriptRetAddr = NULL; +EWRAM_DATA static u8 *sTrainerBBattleScriptRetAddr = NULL; static EWRAM_DATA u16 sRivalBattleFlags = 0; EWRAM_DATA static bool8 sShouldCheckTrainerBScript = FALSE; EWRAM_DATA static u8 sNoOfPossibleTrainerRetScripts = 0; @@ -182,6 +185,49 @@ static const struct TrainerBattleParameter sContinueScriptDoubleBattleParams[] = {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR}, }; +static const struct TrainerBattleParameter sTrainerBOrdinaryBattleParams[] = +{ + {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT}, + {&gTrainerBattleOpponent_B, TRAINER_PARAM_LOAD_VAL_16BIT}, + {&sTrainerObjectEventLocalId, TRAINER_PARAM_LOAD_VAL_16BIT}, + {&sTrainerBIntroSpeech, TRAINER_PARAM_LOAD_VAL_32BIT}, + {&sTrainerBDefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT}, + {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT}, + {&sTrainerCannotBattleSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT}, + {&sTrainerBBattleScriptRetAddr, TRAINER_PARAM_CLEAR_VAL_32BIT}, + {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR}, +}; + +static const struct TrainerBattleParameter sTrainerBContinueScriptBattleParams[] = +{ + {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT}, + {&gTrainerBattleOpponent_B, TRAINER_PARAM_LOAD_VAL_16BIT}, + {&sTrainerObjectEventLocalId, TRAINER_PARAM_LOAD_VAL_16BIT}, + {&sTrainerBIntroSpeech, TRAINER_PARAM_LOAD_VAL_32BIT}, + {&sTrainerBDefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT}, + {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT}, + {&sTrainerCannotBattleSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT}, + {&sTrainerBBattleScriptRetAddr, TRAINER_PARAM_LOAD_VAL_32BIT}, + {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR}, +}; + +// two trainers, each with a defeat speech +static const struct TrainerBattleParameter sTrainerTwoTrainerBattleParams[] = +{ + {&sTrainerBattleMode, TRAINER_PARAM_LOAD_VAL_8BIT}, + {&sTrainerObjectEventLocalId, TRAINER_PARAM_CLEAR_VAL_16BIT}, + {&gTrainerBattleOpponent_A, TRAINER_PARAM_LOAD_VAL_16BIT}, + {&sTrainerAIntroSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT}, + {&sTrainerADefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT}, + {&gTrainerBattleOpponent_B, TRAINER_PARAM_LOAD_VAL_16BIT}, + {&sTrainerBIntroSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT}, + {&sTrainerBDefeatSpeech, TRAINER_PARAM_LOAD_VAL_32BIT}, + {&sTrainerVictorySpeech, TRAINER_PARAM_CLEAR_VAL_32BIT}, + {&sTrainerCannotBattleSpeech, TRAINER_PARAM_CLEAR_VAL_32BIT}, + {&sTrainerBBattleScriptRetAddr, TRAINER_PARAM_CLEAR_VAL_32BIT}, + {&sTrainerBattleEndScript, TRAINER_PARAM_LOAD_SCRIPT_RET_ADDR}, +}; + #define tState data[0] #define tTransition data[1] @@ -705,6 +751,11 @@ static u16 GetTrainerAFlag(void) return TRAINER_FLAGS_START + gTrainerBattleOpponent_A; } +static u16 GetTrainerBFlag(void) +{ + return TRAINER_FLAGS_START + gTrainerBattleOpponent_B; +} + static bool32 IsPlayerDefeated(u32 battleOutcome) { switch (battleOutcome) @@ -723,17 +774,33 @@ static bool32 IsPlayerDefeated(u32 battleOutcome) } } +void ResetTrainerOpponentIds(void) +{ + gTrainerBattleOpponent_A = 0; + gTrainerBattleOpponent_B = 0; +} + static void InitTrainerBattleVariables(void) { sTrainerBattleMode = 0; - gTrainerBattleOpponent_A = 0; + + if (gApproachingTrainerId == 0) + { + sTrainerAIntroSpeech = NULL; + sTrainerADefeatSpeech = NULL; + sTrainerABattleScriptRetAddr = NULL; + } + else + { + sTrainerBIntroSpeech = NULL; + sTrainerBDefeatSpeech = NULL; + sTrainerBBattleScriptRetAddr = NULL; + } + sTrainerObjectEventLocalId = 0; - sTrainerAIntroSpeech = NULL; - sTrainerADefeatSpeech = NULL; sTrainerVictorySpeech = NULL; sTrainerCannotBattleSpeech = NULL; sTrainerBattleEndScript = NULL; - sTrainerABattleScriptRetAddr = NULL; sRivalBattleFlags = 0; } @@ -840,8 +907,15 @@ const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data) TrainerBattleLoadArgs(sEarlyRivalBattleParams, data); return EventScript_DoNoIntroTrainerBattle; default: - TrainerBattleLoadArgs(sOrdinaryBattleParams, data); - SetMapVarsToTrainer(); + if (gApproachingTrainerId == 0) + { + TrainerBattleLoadArgs(sOrdinaryBattleParams, data); + SetMapVarsToTrainer(); + } + else + { + TrainerBattleLoadArgs(sTrainerBOrdinaryBattleParams, data); + } return EventScript_TryDoNormalTrainerBattle; } } @@ -855,6 +929,19 @@ void ConfigureAndSetUpOneTrainerBattle(u8 trainerEventObjId, const u8 *trainerSc LockPlayerFieldControls(); } +void ConfigureTwoTrainersBattle(u8 trainerObjEventId, const u8 *trainerScript) +{ + gSelectedObjectEvent = trainerObjEventId; + gSpecialVar_LastTalked = gObjectEvents[trainerObjEventId].localId; + BattleSetup_ConfigureTrainerBattle(trainerScript + 1); +} + +void SetUpTwoTrainersBattle(void) +{ + ScriptContext_SetupScript(EventScript_DoTrainerBattleFromApproach); + LockPlayerFieldControls(); +} + bool32 GetTrainerFlagFromScriptPointer(const u8 *data) { u32 flag = TrainerBattleLoadArg16(data + 2); @@ -886,6 +973,8 @@ u16 Script_HasTrainerBeenFought(void) void SetBattledTrainerFlag(void) { + if (gTrainerBattleOpponent_B != 0) + FlagSet(GetTrainerBFlag()); FlagSet(GetTrainerAFlag()); } @@ -910,6 +999,9 @@ void BattleSetup_StartTrainerBattle(void) gBattleTypeFlags = (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_TRAINER); else gBattleTypeFlags = (BATTLE_TYPE_TRAINER); + + if (GetTrainerBattleMode() == TRAINER_BATTLE_EARLY_RIVAL && GetRivalBattleFlags() & RIVAL_BATTLE_TUTORIAL) + gBattleTypeFlags |= BATTLE_TYPE_FIRST_BATTLE; sNoOfPossibleTrainerRetScripts = gNoOfApproachingTrainers; gNoOfApproachingTrainers = 0; @@ -935,16 +1027,6 @@ void BattleSetup_StartTrainerBattle_Debug(void) ScriptContext_Stop(); } -void StartTrainerBattle(void) -{ - gBattleTypeFlags = BATTLE_TYPE_TRAINER; - if (GetTrainerBattleMode() == TRAINER_BATTLE_EARLY_RIVAL && GetRivalBattleFlags() & RIVAL_BATTLE_TUTORIAL) - gBattleTypeFlags |= BATTLE_TYPE_FIRST_BATTLE; - gMain.savedCallback = CB2_EndTrainerBattle; - DoTrainerBattle(); - ScriptContext_Stop(); -} - static void SaveChangesToPlayerParty(void) { u8 i = 0, j = 0; @@ -1078,13 +1160,19 @@ void ShowTrainerCantBattleSpeech(void) void PlayTrainerEncounterMusic(void) { + u16 trainerId; u16 music; + if (gApproachingTrainerId == 0) + trainerId = gTrainerBattleOpponent_A; + else + trainerId = gTrainerBattleOpponent_B; + if (!QL_IS_PLAYBACK_STATE && sTrainerBattleMode != TRAINER_BATTLE_CONTINUE_SCRIPT_NO_MUSIC && sTrainerBattleMode != TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC) { - switch (GetTrainerEncounterMusicId(gTrainerBattleOpponent_A)) + switch (GetTrainerEncounterMusicId(trainerId)) { case TRAINER_ENCOUNTER_MUSIC_FEMALE: case TRAINER_ENCOUNTER_MUSIC_GIRL: @@ -1119,7 +1207,10 @@ static const u8 *ReturnEmptyStringIfNull(const u8 *string) static const u8 *GetIntroSpeechOfApproachingTrainer(void) { - return ReturnEmptyStringIfNull(sTrainerAIntroSpeech); + if (gApproachingTrainerId == 0) + return ReturnEmptyStringIfNull(sTrainerAIntroSpeech); + else + return ReturnEmptyStringIfNull(sTrainerBIntroSpeech); } const u8 *GetTrainerALoseText(void) @@ -1130,6 +1221,12 @@ const u8 *GetTrainerALoseText(void) return gStringVar4; } +const u8 *GetTrainerBLoseText(void) +{ + StringExpandPlaceholders(gStringVar4, ReturnEmptyStringIfNull(sTrainerBDefeatSpeech)); + return gStringVar4; +} + const u8 *GetTrainerWonSpeech(void) { StringExpandPlaceholders(gStringVar4, ReturnEmptyStringIfNull(sTrainerVictorySpeech)); diff --git a/src/battle_tower.c b/src/battle_tower.c index 61ee37b44..d914328b5 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -404,20 +404,19 @@ void SetEReaderTrainerGfxId(void) SetBattleTowerTrainerGfxId(BATTLE_TOWER_EREADER_TRAINER_ID); } -u8 GetBattleTowerTrainerFrontSpriteId(void) +u8 GetBattleTowerTrainerFrontSpriteId(u16 trainerId) { - if (gSaveBlock2Ptr->battleTower.battleTowerTrainerId == BATTLE_TOWER_EREADER_TRAINER_ID) + if (trainerId == BATTLE_TOWER_EREADER_TRAINER_ID) { return gFacilityClassToPicIndex[gSaveBlock2Ptr->battleTower.ereaderTrainer.trainerClass]; } - else if (gSaveBlock2Ptr->battleTower.battleTowerTrainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) + else if (trainerId < BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID) { - // TODO: fix? - return gFacilityClassToPicIndex[sBattleTowerTrainers[gSaveBlock2Ptr->battleTower.battleTowerTrainerId].trainerClass]; + return gFacilityClassToPicIndex[sBattleTowerTrainers[trainerId].trainerClass]; } else { - return gFacilityClassToPicIndex[gSaveBlock2Ptr->battleTower.records[gSaveBlock2Ptr->battleTower.battleTowerTrainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].trainerClass]; + return gFacilityClassToPicIndex[gSaveBlock2Ptr->battleTower.records[trainerId - BATTLE_TOWER_RECORD_MIXING_TRAINER_BASE_ID].trainerClass]; } } diff --git a/src/bike.c b/src/bike.c index be9cd8e50..772e65a28 100644 --- a/src/bike.c +++ b/src/bike.c @@ -252,12 +252,10 @@ bool8 RS_IsRunningDisallowed(u8 r0) bool32 IsRunningDisallowed(u8 metatileBehavior) { - if (!gMapHeader.allowRunning) - return TRUE; - if (MetatileBehaviorForbidsBiking(metatileBehavior) != TRUE) - return FALSE; - else + if ((OW_RUNNING_INDOORS == GEN_3 && !gMapHeader.allowRunning) || MetatileBehaviorForbidsBiking(metatileBehavior) == TRUE) return TRUE; + + return FALSE; } static bool8 MetatileBehaviorForbidsBiking(u8 metatileBehavior) diff --git a/src/cable_club.c b/src/cable_club.c index 31c4ff635..dd486fb73 100644 --- a/src/cable_club.c +++ b/src/cable_club.c @@ -2,6 +2,7 @@ #include "gflib.h" #include "battle.h" #include "battle_records.h" +#include "battle_setup.h" #include "cable_club.h" #include "event_data.h" #include "event_scripts.h" diff --git a/src/event_object_lock.c b/src/event_object_lock.c index e9adbb680..d38e7f9a6 100644 --- a/src/event_object_lock.c +++ b/src/event_object_lock.c @@ -1,14 +1,15 @@ #include "global.h" -#include "task.h" -#include "field_player_avatar.h" -#include "event_object_movement.h" -#include "script_movement.h" #include "event_data.h" +#include "event_object_movement.h" +#include "field_player_avatar.h" +#include "script_movement.h" +#include "task.h" +#include "trainer_see.h" #include "constants/event_objects.h" -bool8 walkrun_is_standing_still(void) +bool8 IsPlayerStandingStill(void) { - if (gPlayerAvatar.tileTransitionState == 1) + if (gPlayerAvatar.tileTransitionState == T_TILE_TRANSITION) return FALSE; else return TRUE; @@ -16,7 +17,7 @@ bool8 walkrun_is_standing_still(void) void Task_WaitPlayerStopMoving(u8 taskId) { - if (walkrun_is_standing_still()) + if (IsPlayerStandingStill()) { HandleEnforcedLookDirectionOnPlayerStopMoving(); DestroyTask(taskId); @@ -44,7 +45,7 @@ void Task_WaitPlayerAndTargetNPCStopMoving(u8 taskId) { struct Task *task = &gTasks[taskId]; - if (task->data[0] == 0 && walkrun_is_standing_still() == TRUE) + if (task->data[0] == 0 && IsPlayerStandingStill() == TRUE) { HandleEnforcedLookDirectionOnPlayerStopMoving(); task->data[0] = 1; @@ -112,3 +113,78 @@ void Script_ClearHeldMovement(void) { ObjectEventClearHeldMovementIfActive(&gObjectEvents[gSelectedObjectEvent]); } + +#define tPlayerFrozen data[0] +#define tObjectFrozen data[1] +#define tObjectId data[2] + +// Freeze designated object and player once their movement is finished +static void Task_FreezeObjectAndPlayer(u8 taskId) +{ + struct Task *task = &gTasks[taskId]; + u8 objectEventId = task->tObjectId; + + if (!task->tPlayerFrozen && IsPlayerStandingStill() == TRUE) + { + HandleEnforcedLookDirectionOnPlayerStopMoving(); + task->tPlayerFrozen = TRUE; + } + if (!task->tObjectFrozen && !gObjectEvents[objectEventId].singleMovementActive) + { + FreezeObjectEvent(&gObjectEvents[objectEventId]); + task->tObjectFrozen = TRUE; + } + if (task->tPlayerFrozen && task->tObjectFrozen) + DestroyTask(taskId); +} + +// Freeze all objects immediately except the player and the approaching trainers. +// The approaching trainers and player are frozen once their movement is finished +void FreezeForApproachingTrainers(void) +{ + u8 trainerObjectId1, trainerObjectId2, taskId; + struct ObjectEvent *followerObj = GetFollowerObject(); + trainerObjectId1 = GetChosenApproachingTrainerObjectEventId(0); + + if (gNoOfApproachingTrainers == 2) + { + // Get second trainer, freeze all other objects + trainerObjectId2 = GetChosenApproachingTrainerObjectEventId(1); + FreezeObjectEventsExceptTwo(trainerObjectId1, trainerObjectId2); + + // Start task to freeze trainer 1 (and player) after movement + taskId = CreateTask(Task_FreezeObjectAndPlayer, 80); + gTasks[taskId].tObjectId = trainerObjectId1; + if (!gObjectEvents[trainerObjectId1].singleMovementActive) + { + FreezeObjectEvent(&gObjectEvents[trainerObjectId1]); + gTasks[taskId].tObjectFrozen = TRUE; + } + + // Start task to freeze trainer 2 after movement + taskId = CreateTask(Task_FreezeObjectAndPlayer, 81); + gTasks[taskId].tObjectId = trainerObjectId2; + if (!gObjectEvents[trainerObjectId2].singleMovementActive) + { + FreezeObjectEvent(&gObjectEvents[trainerObjectId2]); + gTasks[taskId].tObjectFrozen = TRUE; + } + } + else + { + FreezeObjectEventsExceptOne(trainerObjectId1); + taskId = CreateTask(Task_FreezeObjectAndPlayer, 80); + gTasks[taskId].tObjectId = trainerObjectId1; + if (!gObjectEvents[trainerObjectId1].singleMovementActive) + { + FreezeObjectEvent(&gObjectEvents[trainerObjectId1]); + gTasks[taskId].tObjectFrozen = TRUE; + } + } + if (followerObj) // Unfreeze follower so it can move behind player + UnfreezeObjectEvent(followerObj); +} + +#undef tPlayerFrozen +#undef tObjectFrozen +#undef tObjectId diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 61597eefc..6f737bc45 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1828,7 +1828,7 @@ u8 SpawnSpecialObjectEvent(struct ObjectEventTemplate *objectEventTemplate) return TrySpawnObjectEventTemplate(objectEventTemplate, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, cameraX, cameraY); } -int SpawnSpecialObjectEventParameterized(u16 graphicsId, u8 movementBehavior, u8 localId, s16 x, s16 y, u8 elevation) +u32 SpawnSpecialObjectEventParameterized(u16 graphicsId, u8 movementBehavior, u8 localId, s16 x, s16 y, u8 elevation) { struct ObjectEventTemplate objectEventTemplate; @@ -10281,6 +10281,19 @@ void FreezeObjectEventsExceptOne(u8 noFreeze) } } +// Used to freeze other objects except two trainers approaching for battle +void FreezeObjectEventsExceptTwo(u8 objectEventId1, u8 objectEventId2) +{ + u8 i; + + for(i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + if(i != objectEventId1 && i != objectEventId2 && + gObjectEvents[i].active && i != gPlayerAvatar.objectEventId) + FreezeObjectEvent(&gObjectEvents[i]); + } +} + void UnfreezeObjectEvent(struct ObjectEvent *objectEvent) { if (objectEvent->active && objectEvent->frozen) diff --git a/src/field_fadetransition.c b/src/field_fadetransition.c index a10c59a14..2c5e4b9d5 100644 --- a/src/field_fadetransition.c +++ b/src/field_fadetransition.c @@ -351,7 +351,7 @@ static void Task_ExitDoor(u8 taskId) } break; case 9: - if (FieldFadeTransitionBackgroundEffectIsFinished() && walkrun_is_standing_still() && !FieldIsDoorAnimationRunning() && !FuncIsActiveTask(Task_BarnDoorWipe)) + if (FieldFadeTransitionBackgroundEffectIsFinished() && IsPlayerStandingStill() && !FieldIsDoorAnimationRunning() && !FuncIsActiveTask(Task_BarnDoorWipe)) { ObjectEventClearHeldMovementIfFinished(&gObjectEvents[GetObjectEventIdByLocalIdAndMap(OBJ_EVENT_ID_PLAYER, 0, 0)]); task->data[0] = 4; @@ -367,7 +367,7 @@ static void Task_ExitDoor(u8 taskId) } break; case 2: - if (walkrun_is_standing_still()) + if (IsPlayerStandingStill()) { task->data[1] = FieldAnimateDoorClose(*x, *y); ObjectEventClearHeldMovementIfFinished(&gObjectEvents[GetObjectEventIdByLocalIdAndMap(OBJ_EVENT_ID_PLAYER, 0, 0)]); @@ -409,7 +409,7 @@ static void Task_ExitNonAnimDoor(u8 taskId) } break; case 2: - if (walkrun_is_standing_still()) + if (IsPlayerStandingStill()) { task->data[0] = 3; } @@ -747,7 +747,7 @@ static void Task_DoorWarp(u8 taskId) } break; case 2: - if (walkrun_is_standing_still()) + if (IsPlayerStandingStill()) { task->data[1] = FieldAnimateDoorClose(*xp, *yp - 1); ObjectEventClearHeldMovementIfFinished(&gObjectEvents[GetObjectEventIdByLocalIdAndMap(OBJ_EVENT_ID_PLAYER, 0, 0)]); diff --git a/src/field_poison.c b/src/field_poison.c index d44e32b63..7eb21d956 100644 --- a/src/field_poison.c +++ b/src/field_poison.c @@ -7,8 +7,10 @@ #include "event_data.h" #include "fldeff.h" #include "party_menu.h" +#include "pokemon.h" #include "field_poison.h" #include "constants/battle.h" +#include "constants/form_change_types.h" static bool32 IsMonValidSpecies(struct Pokemon *pokemon) { @@ -33,7 +35,8 @@ static void FaintFromFieldPoison(u8 partyIdx) { struct Pokemon *pokemon = gPlayerParty + partyIdx; u32 status = STATUS1_NONE; - AdjustFriendship(pokemon, FRIENDSHIP_EVENT_FAINT_OUTSIDE_BATTLE); + if (OW_POISON_DAMAGE < GEN_4) + AdjustFriendship(pokemon, FRIENDSHIP_EVENT_FAINT_OUTSIDE_BATTLE); SetMonData(pokemon, MON_DATA_STATUS, &status); GetMonData(pokemon, MON_DATA_NICKNAME, gStringVar1); StringGet_Nickname(gStringVar1); @@ -42,7 +45,7 @@ static void FaintFromFieldPoison(u8 partyIdx) static bool32 MonFaintedFromPoison(u8 partyIdx) { struct Pokemon *pokemon = gPlayerParty + partyIdx; - if (IsMonValidSpecies(pokemon) && !GetMonData(pokemon, MON_DATA_HP) && GetAilmentFromStatus(GetMonData(pokemon, MON_DATA_STATUS)) == AILMENT_PSN) + if (IsMonValidSpecies(pokemon) && GetMonData(pokemon, MON_DATA_HP) == ((OW_POISON_DAMAGE < GEN_4) ? 0 : 1) && GetAilmentFromStatus(GetMonData(pokemon, MON_DATA_STATUS)) == AILMENT_PSN) return TRUE; return FALSE; } @@ -61,7 +64,7 @@ static void Task_TryFieldPoisonWhiteOut(u8 taskId) if (MonFaintedFromPoison(tPartyId)) { FaintFromFieldPoison(tPartyId); - ShowFieldMessage(gText_PkmnFainted3); + ShowFieldMessage(gText_PkmnFainted_FldPsn); tState++; return; } @@ -101,9 +104,16 @@ s32 DoPoisonFieldEffect(void) { if (GetMonData(pokemon, MON_DATA_SANITY_HAS_SPECIES) && GetAilmentFromStatus(GetMonData(pokemon, MON_DATA_STATUS)) == AILMENT_PSN) { + // Apply poison damage hp = GetMonData(pokemon, MON_DATA_HP); - if (hp == 0 || --hp == 0) + if (OW_POISON_DAMAGE < GEN_4 && (hp == 0 || --hp == 0)) + { + TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_FAINT); numFainted++; + } + else if (OW_POISON_DAMAGE >= GEN_4 && (hp == 1 || --hp == 1)) + numFainted++; + SetMonData(pokemon, MON_DATA_HP, &hp); numPoisoned++; } diff --git a/src/field_specials.c b/src/field_specials.c index c3e2dd89d..59d9c1c8d 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -355,7 +355,7 @@ void AnimatePcTurnOff() void SpawnCameraObject(void) { - u8 objectEventId = SpawnSpecialObjectEventParameterized(OBJ_EVENT_GFX_YOUNGSTER, 8, OBJ_EVENT_ID_CAMERA, gSaveBlock1Ptr->pos.x + MAP_OFFSET, gSaveBlock1Ptr->pos.y + MAP_OFFSET, 3); + u32 objectEventId = SpawnSpecialObjectEventParameterized(OBJ_EVENT_GFX_YOUNGSTER, 8, OBJ_EVENT_ID_CAMERA, gSaveBlock1Ptr->pos.x + MAP_OFFSET, gSaveBlock1Ptr->pos.y + MAP_OFFSET, 3); gObjectEvents[objectEventId].invisible = TRUE; CameraObjectSetFollowedObjectId(gObjectEvents[objectEventId].spriteId); } diff --git a/src/pokeball.c b/src/pokeball.c index c9ec674d9..d9b1897b3 100644 --- a/src/pokeball.c +++ b/src/pokeball.c @@ -546,13 +546,8 @@ u8 DoPokeballSendOutAnimation(u32 battler, s16 pan, u8 kindOfThrow) static void Task_DoPokeballSendOutAnim(u8 taskId) { - u16 throwCaseId; - u8 battlerId; - u16 itemId, ballId; - u8 ballSpriteId; - bool8 notSendOut = FALSE; - s16 x, y; - u32 gender; + u32 throwCaseId, battlerId, ballId, ballSpriteId; + bool32 notSendOut = FALSE; u32 throwXoffset = (B_ENEMY_THROW_BALLS >= GEN_6) ? 24 : 0; s32 throwYoffset = (B_ENEMY_THROW_BALLS >= GEN_6) ? -16 : 24; @@ -564,20 +559,8 @@ static void Task_DoPokeballSendOutAnim(u8 taskId) throwCaseId = gTasks[taskId].tThrowId; battlerId = gTasks[taskId].tBattler; - - if (GetBattlerSide(battlerId) != B_SIDE_PLAYER) - itemId = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL); - else - itemId = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL); - - ballId = ItemIdToBallId(itemId); + ballId = ItemIdToBallId(GetBattlerPokeballItemId(battlerId)); LoadBallGfx(ballId); - - if (gBattleTypeFlags & BATTLE_TYPE_LINK) - gender = gLinkPlayers[GetBattlerMultiplayerId(battlerId)].gender; - else - gender = gSaveBlock2Ptr->playerGender; - ballSpriteId = CreateSprite(&gBallSpriteTemplates[ballId], 32, 80, 29); gSprites[ballSpriteId].data[0] = 0x80; gSprites[ballSpriteId].data[1] = 0; @@ -585,22 +568,15 @@ static void Task_DoPokeballSendOutAnim(u8 taskId) switch (throwCaseId) { - case POKEBALL_PLAYER_SENDOUT: - if (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE) - { - x = 32; - y = 64; - } - else - { - gender = !!gender; // something unknown got optimized out - x = 48; - y = 70; - } - + case POKEBALL_PLAYER_SLIDEIN: // don't actually send out, trigger the slide-in animation gBattlerTarget = battlerId; - gSprites[ballSpriteId].x = x; - gSprites[ballSpriteId].y = y; + gSprites[ballSpriteId].callback = HandleBallAnimEnd; + gSprites[ballSpriteId].invisible = TRUE; + break; + case POKEBALL_PLAYER_SENDOUT: + gBattlerTarget = battlerId; + gSprites[ballSpriteId].x = (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE) ? 32 : 48; + gSprites[ballSpriteId].y = (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE) ? 64 : 70; gSprites[ballSpriteId].callback = SpriteCB_MonSendOut_1; DoPokeballSendOutSoundEffect(battlerId); break; @@ -1085,6 +1061,13 @@ static void HandleBallAnimEnd(struct Sprite *sprite) bool8 affineAnimEnded = FALSE; u8 battlerId = sprite->sBattler; + if (sprite->data[7] == POKEBALL_PLAYER_SLIDEIN) + { + gSprites[gBattlerSpriteIds[sprite->sBattler]].callback = SpriteCB_PlayerMonSlideIn; + AnimateSprite(&gSprites[gBattlerSpriteIds[sprite->sBattler]]); + gSprites[gBattlerSpriteIds[sprite->sBattler]].data[1] = 0x1000; + } + gSprites[gBattlerSpriteIds[battlerId]].invisible = FALSE; if (sprite->animEnded) sprite->invisible = TRUE; diff --git a/src/pokemon.c b/src/pokemon.c index 664a692ec..77e0330f2 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -8,6 +8,7 @@ #include "data.h" #include "battle.h" #include "battle_anim.h" +#include "battle_setup.h" #include "item.h" #include "event_data.h" #include "util.h" @@ -3255,22 +3256,45 @@ u8 GetMonsStateToDoubles(void) s32 i; CalculatePlayerPartyCount(); + if (OW_DOUBLE_APPROACH_WITH_ONE_MON) + return PLAYER_HAS_TWO_USABLE_MONS; + if (gPlayerPartyCount == 1) return gPlayerPartyCount; // PLAYER_HAS_ONE_MON for (i = 0; i < gPlayerPartyCount; i++) { - // FRLG changed the order of these checks, but there's no point to doing that - // because of the requirement of all 3 of these checks. - if (GetMonData(&gPlayerParty[i], MON_DATA_HP, NULL) != 0 - && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG, NULL) != SPECIES_NONE - && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG, NULL) != SPECIES_EGG) + if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG, NULL) != SPECIES_EGG + && GetMonData(&gPlayerParty[i], MON_DATA_HP, NULL) != 0 + && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG, NULL) != SPECIES_NONE) aliveCount++; } return (aliveCount > 1) ? PLAYER_HAS_TWO_USABLE_MONS : PLAYER_HAS_ONE_USABLE_MON; } +u8 GetMonsStateToDoubles_2(void) +{ + s32 aliveCount = 0; + s32 i; + + if (OW_DOUBLE_APPROACH_WITH_ONE_MON) + return PLAYER_HAS_TWO_USABLE_MONS; + + for (i = 0; i < PARTY_SIZE; i++) + { + u32 species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG, NULL); + if (species != SPECIES_EGG && species != SPECIES_NONE + && GetMonData(&gPlayerParty[i], MON_DATA_HP, NULL) != 0) + aliveCount++; + } + + if (aliveCount == 1) + return PLAYER_HAS_ONE_MON; // may have more than one, but only one is alive + + return (aliveCount > 1) ? PLAYER_HAS_TWO_USABLE_MONS : PLAYER_HAS_ONE_USABLE_MON; +} + u16 GetAbilityBySpecies(u16 species, u8 abilityNum) { int i; diff --git a/src/quest_log_battle.c b/src/quest_log_battle.c index d5a0786f3..73aac57e7 100644 --- a/src/quest_log_battle.c +++ b/src/quest_log_battle.c @@ -2,6 +2,7 @@ #include "gflib.h" #include "battle.h" #include "battle_anim.h" +#include "battle_setup.h" #include "link.h" #include "overworld.h" #include "quest_log.h" diff --git a/src/scrcmd.c b/src/scrcmd.c index da2aa2b7a..6f6a3ef01 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -23,6 +23,7 @@ #include "field_message_box.h" #include "new_menu_helpers.h" #include "script_menu.h" +#include "trainer_see.h" #include "data.h" #include "field_specials.h" #include "constants/items.h" @@ -1887,7 +1888,7 @@ bool8 ScrCmd_trainerbattle(struct ScriptContext * ctx) bool8 ScrCmd_dotrainerbattle(struct ScriptContext * ctx) { - StartTrainerBattle(); + BattleSetup_StartTrainerBattle(); return TRUE; } @@ -2235,6 +2236,29 @@ bool8 ScrCmd_normalmsg(struct ScriptContext * ctx) return FALSE; } +bool8 ScrCmd_selectapproachingtrainer(struct ScriptContext *ctx) +{ + gSelectedObjectEvent = GetCurrentApproachingTrainerObjectEventId(); + return FALSE; +} + +bool8 ScrCmd_lockfortrainer(struct ScriptContext *ctx) +{ + if (IsUpdateLinkStateCBActive()) + { + return FALSE; + } + else + { + if (gObjectEvents[gSelectedObjectEvent].active) + { + FreezeForApproachingTrainers(); + SetupNativeScript(ctx, IsFreezePlayerFinished); + } + return TRUE; + } +} + // This command will set a Pokémon's modernFatefulEncounter bit; there is no similar command to clear it. bool8 ScrCmd_setmonmodernfatefulencounter(struct ScriptContext * ctx) { @@ -2262,3 +2286,8 @@ bool8 ScrCmd_setmonmetlocation(struct ScriptContext * ctx) SetMonData(&gPlayerParty[partyIndex], MON_DATA_MET_LOCATION, &location); return FALSE; } + +void SetMovingNpcId(u16 npcId) +{ + sMovingNpcId = npcId; +} diff --git a/src/strings.c b/src/strings.c index c370d2387..39512cd1a 100644 --- a/src/strings.c +++ b/src/strings.c @@ -318,6 +318,11 @@ const u8 gText_PkmnGainedExp[] = _("{STR_VAR_1} gained {STR_VAR_2} Exp. Points!{ const u8 gText_PkmnGainedExpAndElevatedToLvVar3[] = _("{STR_VAR_1} gained {STR_VAR_2} Exp. Points\nand was elevated to Lv. {STR_VAR_3}!"); const u8 gText_PkmnSnappedOutOfConfusion[] = _("{STR_VAR_1} snapped out of its\nconfusion.{PAUSE_UNTIL_PRESS}"); const u8 gText_PkmnGotOverInfatuation[] = _("{STR_VAR_1} got over its\ninfatuation.{PAUSE_UNTIL_PRESS}"); +#if OW_POISON_DAMAGE < GEN_4 +const u8 gText_PkmnFainted_FldPsn[] = _("{STR_VAR_1} fainted…\p\n"); +#else +const u8 gText_PkmnFainted_FldPsn[] = _("{STR_VAR_1} survived the poisoning.\nThe poison faded away!\p"); +#endif const u8 gText_OnlyPkmnForBattle[] = _("That's your only\nPOKéMON for battle."); ALIGNED(4) const u8 gText_PkmnCantBeTradedNow[] = _("That POKéMON can't be traded\nnow."); ALIGNED(4) const u8 gText_EggCantBeTradedNow[] = _("An EGG can't be traded now."); diff --git a/src/trainer_see.c b/src/trainer_see.c index 3495caaf2..d669a0608 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -1,11 +1,14 @@ #include "global.h" #include "battle_setup.h" +#include "event_data.h" #include "event_object_movement.h" #include "field_effect.h" #include "field_player_avatar.h" #include "quest_log.h" #include "script.h" +#include "script_movement.h" #include "task.h" +#include "trainer_see.h" #include "util.h" #include "constants/battle_setup.h" #include "constants/event_object_movement.h" @@ -13,9 +16,9 @@ #include "constants/trainer_types.h" typedef u8 (*TrainerApproachFunc)(struct ObjectEvent *, s16, s16, s16); -typedef bool8 (*TrainerSeeFunc)(u8, struct Task *, struct ObjectEvent *); +typedef bool32 (*TrainerSeeFunc)(u8, struct Task *, struct ObjectEvent *); -static bool8 CheckTrainer(u8 trainerObjId); +static u32 CheckTrainer(u8 trainerObjId); static u8 GetTrainerApproachDistance(struct ObjectEvent * trainerObj); static u8 GetTrainerApproachDistanceSouth(struct ObjectEvent * trainerObj, s16 range, s16 x, s16 y); static u8 GetTrainerApproachDistanceNorth(struct ObjectEvent * trainerObj, s16 range, s16 x, s16 y); @@ -24,21 +27,21 @@ static u8 GetTrainerApproachDistanceEast(struct ObjectEvent * trainerObj, s16 ra static u8 CheckPathBetweenTrainerAndPlayer(struct ObjectEvent * trainerObj, u8 approachDistance, u8 facingDirection); static void TrainerApproachPlayer(struct ObjectEvent * trainerObj, u8 approachDistance); static void Task_RunTrainerSeeFuncList(u8 taskId); -static bool8 TrainerSeeFunc_Dummy(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_StartExclMark(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_WaitExclMark(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_TrainerApproach(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_PrepareToEngage(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_End(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_BeginRemoveDisguise(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_WaitRemoveDisguise(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_TrainerInAshFacesPlayer(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_BeginJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_WaitJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_EndJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_OffscreenAboveTrainerCreateCameraObj(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveUp(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); -static bool8 TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveDown(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_Dummy(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_StartExclMark(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_WaitExclMark(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_TrainerApproach(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_PrepareToEngage(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_End(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_BeginRemoveDisguise(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_WaitRemoveDisguise(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_TrainerInAshFacesPlayer(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_BeginJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_WaitJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_EndJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_OffscreenAboveTrainerCreateCameraObj(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveUp(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); +static bool32 TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveDown(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj); static void Task_DestroyTrainerApproachTask(u8 taskId); static void SpriteCB_TrainerIcons(struct Sprite *sprite); static void SetIconSpriteData(struct Sprite *sprite, u16 fldEffId, u8 spriteAnimNum); @@ -46,6 +49,11 @@ static void SetIconSpriteData(struct Sprite *sprite, u16 fldEffId, u8 spriteAnim // IWRAM common u16 gWhichTrainerToFaceAfterBattle; u8 gNoOfApproachingTrainers; +struct ApproachingTrainer gApproachingTrainers[2]; +bool8 gTrainerApproachedPlayer; + +// EWRAM +EWRAM_DATA u8 gApproachingTrainerId = 0; static const u16 sGfx_Emoticons[] = INCBIN_U16("graphics/misc/emoticons.4bpp"); static const u8 sEmotion_Gfx[] = INCBIN_U8("graphics/misc/emotes.4bpp"); @@ -62,25 +70,43 @@ static const TrainerApproachFunc sDirectionalApproachDistanceFuncs[] = { GetTrainerApproachDistanceEast }; +enum { + TRSEE_NONE, + TRSEE_EXCLAMATION, + TRSEE_EXCLAMATION_WAIT, + TRSEE_MOVE_TO_PLAYER, + TRSEE_PLAYER_FACE, + TRSEE_PLAYER_FACE_WAIT, + TRSEE_REVEAL_DISGUISE, + TRSEE_REVEAL_DISGUISE_WAIT, + TRSEE_REVEAL_BURIED, + TRSEE_BURIED_POP_OUT, + TRSEE_BURIED_JUMP, + TRSEE_REVEAL_BURIED_WAIT, + TRSEE_OFFSCREEN_CREATE_CAMERA, + TRSEE_OFFSCREEN_MOVE_CAMERA_UP, + TRSEE_OFFSCREEN_MOVE_CAMERA_DOWN, +}; + // bool8 func(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) // Returns TRUE to run the next func immediately // Returns FALSE to delay the next func to the next frame static const TrainerSeeFunc sTrainerSeeFuncList[] = { - TrainerSeeFunc_Dummy, - TrainerSeeFunc_StartExclMark, - TrainerSeeFunc_WaitExclMark, - TrainerSeeFunc_TrainerApproach, - TrainerSeeFunc_PrepareToEngage, - TrainerSeeFunc_End, - TrainerSeeFunc_BeginRemoveDisguise, - TrainerSeeFunc_WaitRemoveDisguise, - TrainerSeeFunc_TrainerInAshFacesPlayer, - TrainerSeeFunc_BeginJumpOutOfAsh, - TrainerSeeFunc_WaitJumpOutOfAsh, - TrainerSeeFunc_EndJumpOutOfAsh, - TrainerSeeFunc_OffscreenAboveTrainerCreateCameraObj, - TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveUp, - TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveDown + [TRSEE_NONE] = TrainerSeeFunc_Dummy, + [TRSEE_EXCLAMATION] = TrainerSeeFunc_StartExclMark, + [TRSEE_EXCLAMATION_WAIT] = TrainerSeeFunc_WaitExclMark, + [TRSEE_MOVE_TO_PLAYER] = TrainerSeeFunc_TrainerApproach, + [TRSEE_PLAYER_FACE] = TrainerSeeFunc_PrepareToEngage, + [TRSEE_PLAYER_FACE_WAIT] = TrainerSeeFunc_End, + [TRSEE_REVEAL_DISGUISE] = TrainerSeeFunc_BeginRemoveDisguise, + [TRSEE_REVEAL_DISGUISE_WAIT] = TrainerSeeFunc_WaitRemoveDisguise, + [TRSEE_REVEAL_BURIED] = TrainerSeeFunc_TrainerInAshFacesPlayer, + [TRSEE_BURIED_POP_OUT] = TrainerSeeFunc_BeginJumpOutOfAsh, + [TRSEE_BURIED_JUMP] = TrainerSeeFunc_WaitJumpOutOfAsh, + [TRSEE_REVEAL_BURIED_WAIT] = TrainerSeeFunc_EndJumpOutOfAsh, + [TRSEE_OFFSCREEN_CREATE_CAMERA] = TrainerSeeFunc_OffscreenAboveTrainerCreateCameraObj, + [TRSEE_OFFSCREEN_MOVE_CAMERA_UP] = TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveUp, + [TRSEE_OFFSCREEN_MOVE_CAMERA_DOWN] = TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveDown, }; static const TrainerSeeFunc sTrainerSeeFuncList2[] = { @@ -96,33 +122,87 @@ bool8 CheckForTrainersWantingBattle(void) if (QL_IsTrainerSightDisabled() == TRUE) return FALSE; + if (FlagGet(OW_FLAG_NO_TRAINER_SEE)) + return FALSE; + + gNoOfApproachingTrainers = 0; + gApproachingTrainerId = 0; + for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { - if (gObjectEvents[i].active - && (gObjectEvents[i].trainerType == TRAINER_TYPE_NORMAL - || gObjectEvents[i].trainerType == TRAINER_TYPE_BURIED) - && CheckTrainer(i)) - return TRUE; + u8 numTrainers; + + if (!gObjectEvents[i].active) + continue; + if (gObjectEvents[i].trainerType != TRAINER_TYPE_NORMAL && gObjectEvents[i].trainerType != TRAINER_TYPE_BURIED) + continue; + + numTrainers = CheckTrainer(i); + if (numTrainers == 2) + break; + + if (numTrainers == 0) + continue; + + if (gNoOfApproachingTrainers > 1) + break; + if (GetMonsStateToDoubles_2() != PLAYER_HAS_TWO_USABLE_MONS) // one trainer found and cant have a double battle + break; + } + + if (gNoOfApproachingTrainers == 1) + { + ResetTrainerOpponentIds(); + ConfigureAndSetUpOneTrainerBattle(gApproachingTrainers[gNoOfApproachingTrainers - 1].objectEventId, + gApproachingTrainers[gNoOfApproachingTrainers - 1].trainerScriptPtr); + gTrainerApproachedPlayer = TRUE; + return TRUE; + } + else if (gNoOfApproachingTrainers == 2) + { + ResetTrainerOpponentIds(); + for (i = 0; i < gNoOfApproachingTrainers; i++, gApproachingTrainerId++) + { + ConfigureTwoTrainersBattle(gApproachingTrainers[i].objectEventId, + gApproachingTrainers[i].trainerScriptPtr); + } + SetUpTwoTrainersBattle(); + gApproachingTrainerId = 0; + gTrainerApproachedPlayer = TRUE; + return TRUE; + } + else + { + gTrainerApproachedPlayer = FALSE; + return FALSE; } - return FALSE; } -static bool8 CheckTrainer(u8 trainerObjId) +static u32 CheckTrainer(u8 trainerObjId) { const u8 *script = GetObjectEventScriptPointerByObjectEventId(trainerObjId); - u8 approachDistance; + u32 numTrainers = 1; + u32 approachDistance; if (GetTrainerFlagFromScriptPointer(script)) return FALSE; approachDistance = GetTrainerApproachDistance(&gObjectEvents[trainerObjId]); if (approachDistance != 0) { if (script[1] == TRAINER_BATTLE_DOUBLE && GetMonsStateToDoubles()) - return FALSE; - ConfigureAndSetUpOneTrainerBattle(trainerObjId, script); + { + if (GetMonsStateToDoubles_2() != PLAYER_HAS_TWO_USABLE_MONS) + return 0; + numTrainers = 2; + } + + gApproachingTrainers[gNoOfApproachingTrainers].objectEventId = trainerObjId; + gApproachingTrainers[gNoOfApproachingTrainers].trainerScriptPtr = script; + gApproachingTrainers[gNoOfApproachingTrainers].radius = approachDistance; TrainerApproachPlayer(&gObjectEvents[trainerObjId], approachDistance - 1); - return TRUE; + gNoOfApproachingTrainers++; + return numTrainers; } - return FALSE; + return 0; } static u8 GetTrainerApproachDistance(struct ObjectEvent *trainerObj) @@ -238,38 +318,41 @@ static u8 CheckPathBetweenTrainerAndPlayer(struct ObjectEvent *trainerObj, u8 ap } #define tFuncId data[0] -#define tTrainerObjHi data[1] -#define tTrainerObjLo data[2] #define tTrainerRange data[3] #define tOutOfAshSpriteId data[4] #define tData5 data[5] - -#define TaskGetTrainerObj(dest, task) do { \ - (dest) = (struct ObjectEvent *)(((task)->tTrainerObjHi << 16) | ((u16)(task)->tTrainerObjLo)); \ -} while (0) +#define tTrainerObjectEventId data[7] static void TrainerApproachPlayer(struct ObjectEvent * trainerObj, u8 approachDistance) { - u8 taskId = CreateTask(Task_RunTrainerSeeFuncList, 80); - struct Task *task = &gTasks[taskId]; - task->tTrainerObjHi = ((uintptr_t)trainerObj) >> 16; - task->tTrainerObjLo = (uintptr_t)trainerObj; + struct Task *task; + + gApproachingTrainers[gNoOfApproachingTrainers].taskId = CreateTask(Task_RunTrainerSeeFuncList, 80); + task = &gTasks[gApproachingTrainers[gNoOfApproachingTrainers].taskId]; task->tTrainerRange = approachDistance; + task->tTrainerObjectEventId = gApproachingTrainers[gNoOfApproachingTrainers].objectEventId; } -static void StartTrainerApproachWithFollowupTask(TaskFunc taskFunc) +static void StartTrainerApproachWithFollowupTask(TaskFunc followupFunc) { - u8 taskId = FindTaskIdByFunc(Task_RunTrainerSeeFuncList); - SetTaskFuncWithFollowupFunc(taskId, Task_RunTrainerSeeFuncList, taskFunc); + u8 taskId; + TaskFunc taskFunc; + + if (gApproachingTrainerId == 0) + taskId = gApproachingTrainers[0].taskId; + else + taskId = gApproachingTrainers[1].taskId; + + taskFunc = Task_RunTrainerSeeFuncList; + SetTaskFuncWithFollowupFunc(taskId, taskFunc, followupFunc); gTasks[taskId].tFuncId = 1; - Task_RunTrainerSeeFuncList(taskId); + taskFunc(taskId); } static void Task_RunTrainerSeeFuncList(u8 taskId) { struct Task *task = &gTasks[taskId]; - struct ObjectEvent * trainerObj; - TaskGetTrainerObj(trainerObj, task); + struct ObjectEvent *trainerObj = &gObjectEvents[task->tTrainerObjectEventId]; if (!trainerObj->active) { @@ -277,19 +360,18 @@ static void Task_RunTrainerSeeFuncList(u8 taskId) } else { - while (sTrainerSeeFuncList[task->tFuncId](taskId, task, trainerObj)) - ; + while (sTrainerSeeFuncList[task->tFuncId](taskId, task, trainerObj)); } } // TrainerSeeFuncs -static bool8 TrainerSeeFunc_Dummy(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) +static bool32 TrainerSeeFunc_Dummy(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) { return FALSE; } -static bool8 TrainerSeeFunc_StartExclMark(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) +static bool32 TrainerSeeFunc_StartExclMark(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) { u8 action; // FRLG introduces trainers who can see the player from offscreen above. @@ -309,7 +391,7 @@ static bool8 TrainerSeeFunc_StartExclMark(u8 taskId, struct Task *task, struct O return TRUE; } -static bool8 TrainerSeeFunc_WaitExclMark(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) +static bool32 TrainerSeeFunc_WaitExclMark(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) { if (FieldEffectActiveListContains(FLDEFF_EXCLAMATION_MARK_ICON)) { @@ -326,7 +408,7 @@ static bool8 TrainerSeeFunc_WaitExclMark(u8 taskId, struct Task *task, struct Ob } } -static bool8 TrainerSeeFunc_TrainerApproach(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) +static bool32 TrainerSeeFunc_TrainerApproach(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) { if (!ObjectEventIsMovementOverridden(trainerObj) || ObjectEventClearHeldMovementIfFinished(trainerObj)) { @@ -344,7 +426,7 @@ static bool8 TrainerSeeFunc_TrainerApproach(u8 taskId, struct Task *task, struct return FALSE; } -static bool8 TrainerSeeFunc_PrepareToEngage(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) +static bool32 TrainerSeeFunc_PrepareToEngage(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) { struct ObjectEvent *playerObj; @@ -360,13 +442,12 @@ static bool8 TrainerSeeFunc_PrepareToEngage(u8 taskId, struct Task *task, struct return FALSE; CancelPlayerForcedMovement(); - // Uncomment to have player turn to face their opponent - // ObjectEventSetHeldMovement(&gObjectEvents[gPlayerAvatar.objectEventId], GetFaceDirectionMovementAction(GetOppositeDirection(trainerObj->facingDirection))); + ObjectEventSetHeldMovement(&gObjectEvents[gPlayerAvatar.objectEventId], GetFaceDirectionMovementAction(GetOppositeDirection(trainerObj->facingDirection))); task->tFuncId++; return FALSE; } -static bool8 TrainerSeeFunc_End(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) +static bool32 TrainerSeeFunc_End(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) { struct ObjectEvent *playerObj = &gObjectEvents[gPlayerAvatar.objectEventId]; @@ -377,7 +458,7 @@ static bool8 TrainerSeeFunc_End(u8 taskId, struct Task *task, struct ObjectEvent } // Jumps here if disguised. Not used in FRLG. -static bool8 TrainerSeeFunc_BeginRemoveDisguise(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) +static bool32 TrainerSeeFunc_BeginRemoveDisguise(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) { if (!ObjectEventIsMovementOverridden(trainerObj) || ObjectEventClearHeldMovementIfFinished(trainerObj)) @@ -388,7 +469,7 @@ static bool8 TrainerSeeFunc_BeginRemoveDisguise(u8 taskId, struct Task *task, st return FALSE; } -static bool8 TrainerSeeFunc_WaitRemoveDisguise(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) +static bool32 TrainerSeeFunc_WaitRemoveDisguise(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) { if (ObjectEventClearHeldMovementIfFinished(trainerObj)) task->tFuncId = 3; @@ -397,7 +478,7 @@ static bool8 TrainerSeeFunc_WaitRemoveDisguise(u8 taskId, struct Task *task, str } // Jump here if buried in ash. Not used in FRLG. -static bool8 TrainerSeeFunc_TrainerInAshFacesPlayer(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) +static bool32 TrainerSeeFunc_TrainerInAshFacesPlayer(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) { if (!ObjectEventIsMovementOverridden(trainerObj) || ObjectEventClearHeldMovementIfFinished(trainerObj)) @@ -408,7 +489,7 @@ static bool8 TrainerSeeFunc_TrainerInAshFacesPlayer(u8 taskId, struct Task *task return FALSE; } -static bool8 TrainerSeeFunc_BeginJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) +static bool32 TrainerSeeFunc_BeginJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) { if (ObjectEventCheckHeldMovementStatus(trainerObj)) { @@ -422,7 +503,7 @@ static bool8 TrainerSeeFunc_BeginJumpOutOfAsh(u8 taskId, struct Task *task, stru return FALSE; } -static bool8 TrainerSeeFunc_WaitJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) +static bool32 TrainerSeeFunc_WaitJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) { struct Sprite *sprite; @@ -441,7 +522,7 @@ static bool8 TrainerSeeFunc_WaitJumpOutOfAsh(u8 taskId, struct Task *task, struc return FALSE; } -static bool8 TrainerSeeFunc_EndJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent *trainerObj) +static bool32 TrainerSeeFunc_EndJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent *trainerObj) { if (!FieldEffectActiveListContains(FLDEFF_POP_OUT_OF_ASH)) task->tFuncId = 3; @@ -450,9 +531,9 @@ static bool8 TrainerSeeFunc_EndJumpOutOfAsh(u8 taskId, struct Task *task, struct } // FRLG exclusive: Scroll the camera up to reveal an offscreen above trainer -static bool8 TrainerSeeFunc_OffscreenAboveTrainerCreateCameraObj(u8 taskId, struct Task *task, struct ObjectEvent *trainerObj) +static bool32 TrainerSeeFunc_OffscreenAboveTrainerCreateCameraObj(u8 taskId, struct Task *task, struct ObjectEvent *trainerObj) { - int specialObjectId; + u32 specialObjectId; task->tData5 = 0; specialObjectId = SpawnSpecialObjectEventParameterized(OBJ_EVENT_GFX_YOUNGSTER, 7, OBJ_EVENT_ID_CAMERA, gSaveBlock1Ptr->pos.x + 7, gSaveBlock1Ptr->pos.y + 7, 3); gObjectEvents[specialObjectId].invisible = TRUE; @@ -461,7 +542,7 @@ static bool8 TrainerSeeFunc_OffscreenAboveTrainerCreateCameraObj(u8 taskId, stru return FALSE; } -static bool8 TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveUp(u8 taskId, struct Task *task, struct ObjectEvent *trainerObj) +static bool32 TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveUp(u8 taskId, struct Task *task, struct ObjectEvent *trainerObj) { u8 specialObjectId; TryGetObjectEventIdByLocalIdAndMap(OBJ_EVENT_ID_CAMERA, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &specialObjectId); @@ -484,7 +565,7 @@ static bool8 TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveUp(u8 taskId, stru return FALSE; } -static bool8 TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveDown(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) +static bool32 TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveDown(u8 taskId, struct Task *task, struct ObjectEvent * trainerObj) { u8 specialObjectId; TryGetObjectEventIdByLocalIdAndMap(OBJ_EVENT_ID_CAMERA, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &specialObjectId); @@ -921,3 +1002,44 @@ static void SpriteCB_TrainerIcons(struct Sprite *sprite) #undef sData3 #undef sData4 #undef sFldEffId + +u8 GetCurrentApproachingTrainerObjectEventId(void) +{ + if (gApproachingTrainerId == 0) + return gApproachingTrainers[0].objectEventId; + else + return gApproachingTrainers[1].objectEventId; +} + +u8 GetChosenApproachingTrainerObjectEventId(u8 arrayId) +{ + if (arrayId >= ARRAY_COUNT(gApproachingTrainers)) + return 0; + else if (arrayId == 0) + return gApproachingTrainers[0].objectEventId; + else + return gApproachingTrainers[1].objectEventId; +} + +void TryPrepareSecondApproachingTrainer(void) +{ + if (gNoOfApproachingTrainers == 2) + { + if (gApproachingTrainerId == 0) + { + gApproachingTrainerId++; + gSpecialVar_Result = TRUE; + UnfreezeObjectEvents(); + FreezeObjectEventsExceptOne(gApproachingTrainers[1].objectEventId); + } + else + { + gApproachingTrainerId = 0; + gSpecialVar_Result = FALSE; + } + } + else + { + gSpecialVar_Result = FALSE; + } +} diff --git a/src/union_room_battle.c b/src/union_room_battle.c index 5c31746a9..0d43b5741 100644 --- a/src/union_room_battle.c +++ b/src/union_room_battle.c @@ -1,6 +1,7 @@ #include "global.h" #include "gflib.h" #include "battle.h" +#include "battle_setup.h" #include "link.h" #include "menu.h" #include "new_menu_helpers.h" diff --git a/src/vs_seeker.c b/src/vs_seeker.c index 0fa7099f4..c82277a17 100644 --- a/src/vs_seeker.c +++ b/src/vs_seeker.c @@ -603,7 +603,7 @@ static void Task_ResetObjectsRematchWantedState(u8 taskId) struct Task *task = &gTasks[taskId]; u8 i; - if (task->data[0] == 0 && walkrun_is_standing_still() == TRUE) + if (task->data[0] == 0 && IsPlayerStandingStill() == TRUE) { HandleEnforcedLookDirectionOnPlayerStopMoving(); task->data[0] = 1;