Port pokefirered's Vs. Seeker to pokeemerald (#3256)
* First version of Vs. Seeker * Update movement.inc98f7e9978d (r1306721924)
* Update field_effect_scripts.s https://github.com/rh-hideout/pokeemerald-expansion/pull/3256/files#r1306722004 * Update field_effect_scripts.s https://github.com/rh-hideout/pokeemerald-expansion/pull/3256/files#r1306722024 * Update item_use.h https://github.com/rh-hideout/pokeemerald-expansion/pull/3256/files#r1306722401 * Update movement_action_func_tables.h https://github.com/rh-hideout/pokeemerald-expansion/pull/3256/files#r1306722828 * Update event_object_movement.c https://github.com/rh-hideout/pokeemerald-expansion/pull/3256/files#r1306722887 * Update overworld.c https://github.com/rh-hideout/pokeemerald-expansion/pull/3256/files#r1306723396 * Update vs_seeker.h https://github.com/rh-hideout/pokeemerald-expansion/pull/3256/files#r1306724158 * Update vs_seeker.c Addressed some cleanup comments from SBird * Update UpdateRandomTrainerRematches Fixed typo in ClearAllTrainerRematchStates Fixed types in GetRematchableTrainerLocalId * Updated UseVsSeekerEffect_2 * Updated UseVsSeekerEffect_3 * Updated UseVsSeekerEffect_4 * Fixed bug that allowed Vs Seeker to be used indoors in correct places Moved VsSeeker function declarations into header * Refactored FieldUseFunc_VsSeeker * Added curly braces to else case in FieldUseFunc_VsSeeker * renamed data[x] in Task_ResetObjectsRematchWantedState * Refactored Task_ResetObjectsRematchWantedState * Refactored VsSeekerResetObjectMovementAfterChargeComplete * Refactored ResetMovementOfRematchableTrainers * Refactored GatherNearbyTrainerInfo * Refactored Task_VsSeeker_3 * CanUseVsSeeker * Refactored GetVsSeekerResponseInArea * GetCurVsSeekerResponse refactored * Cleaned up GetTrainerFlagFromScript * Gave sensible names to Task_VsSeeker * Fixed two bugs where player would not have the right gfx state after using VsSeeker on a Bike or Underwater * Renamed UseVsSeeker Functions * Added I_VS_SEEKER_CHARGING to make Vs. Seeker broken until flag is assigned Removed extra VsSeeker animation code * Addressed PR feedback * Fixed issue with building non-modern * Refactored GetRunningBehaviorFromGraphicsId and renamed to GetResponseMovementTypeFromTrainerGraphicsId * Addresses Lunos's PR feedback: https://github.com/rh-hideout/pokeemerald-expansion/pull/3256\#pullrequestreview-1623547850 Removed the check to see if a map was not indoors to improve readability Made IsValidLocationForVsSeeker into a static function * Added changes in response to Jasper's feedback https://github.com/rh-hideout/pokeemerald-expansion/pull/3256\#pullrequestreview-1725276522 * Updated with Edu's discord feedback https://discord.com/channels/419213663107416084/1135040810082123907/1176872015085453392 * Removed ifdef tags around the repo unless neededb5dc744ced
This commit is contained in:
parent
493478e94b
commit
fe16a2cdee
28 changed files with 1155 additions and 23 deletions
|
@ -162,5 +162,7 @@
|
|||
create_movement_action figure_8, MOVEMENT_ACTION_FIGURE_8
|
||||
create_movement_action fly_up, MOVEMENT_ACTION_FLY_UP
|
||||
create_movement_action fly_down, MOVEMENT_ACTION_FLY_DOWN
|
||||
create_movement_action emote_double_exclamation_mark, MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK
|
||||
create_movement_action emote_x, MOVEMENT_ACTION_EMOTE_X
|
||||
|
||||
create_movement_action step_end, MOVEMENT_ACTION_STEP_END
|
||||
|
|
|
@ -1005,6 +1005,13 @@ Common_EventScript_LegendaryFlewAway::
|
|||
release
|
||||
end
|
||||
|
||||
EventScript_VsSeekerChargingDone::
|
||||
special VsSeekerFreezeObjectsAfterChargeComplete
|
||||
waitstate
|
||||
special VsSeekerResetObjectMovementAfterChargeComplete
|
||||
releaseall
|
||||
end
|
||||
|
||||
.include "data/scripts/pc_transfer.inc"
|
||||
.include "data/scripts/questionnaire.inc"
|
||||
.include "data/scripts/abnormal_weather.inc"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "config/item.h"
|
||||
.include "asm/macros.inc"
|
||||
.include "constants/constants.inc"
|
||||
|
||||
|
@ -72,6 +73,9 @@ gFieldEffectScriptPointers::
|
|||
.4byte gFieldEffectScript_RayquazaSpotlight @ FLDEFF_RAYQUAZA_SPOTLIGHT
|
||||
.4byte gFieldEffectScript_DestroyDeoxysRock @ FLDEFF_DESTROY_DEOXYS_ROCK
|
||||
.4byte gFieldEffectScript_MoveDeoxysRock @ FLDEFF_MOVE_DEOXYS_ROCK
|
||||
.4byte gFldEffScript_UseVsSeeker @ FLDEFF_USE_VS_SEEKER
|
||||
.4byte gFldEffScript_XIcon @ FLDEFF_X_ICON
|
||||
.4byte gFldEffScript_DoubleExclMarkIcon @ FLDEFF_DOUBLE_EXCL_MARK_ICON
|
||||
|
||||
gFieldEffectScript_ExclamationMarkIcon1::
|
||||
field_eff_callnative FldEff_ExclamationMarkIcon
|
||||
|
@ -343,3 +347,15 @@ gFieldEffectScript_DestroyDeoxysRock::
|
|||
gFieldEffectScript_MoveDeoxysRock::
|
||||
field_eff_callnative FldEff_MoveDeoxysRock
|
||||
field_eff_end
|
||||
|
||||
gFldEffScript_UseVsSeeker::
|
||||
field_eff_callnative FldEff_UseVsSeeker
|
||||
field_eff_end
|
||||
|
||||
gFldEffScript_XIcon::
|
||||
field_eff_callnative FldEff_XIcon
|
||||
field_eff_end
|
||||
|
||||
gFldEffScript_DoubleExclMarkIcon::
|
||||
field_eff_callnative FldEff_DoubleExclMarkIcon
|
||||
field_eff_end
|
||||
|
|
|
@ -540,3 +540,5 @@ gSpecials::
|
|||
def_special GetSprayId
|
||||
def_special GetLastUsedSprayType
|
||||
def_special TrySkyBattle
|
||||
def_special VsSeekerResetObjectMovementAfterChargeComplete
|
||||
def_special VsSeekerFreezeObjectsAfterChargeComplete
|
|
@ -4779,3 +4779,18 @@ Route134_Text_HudsonPostBattle:
|
|||
.string "Our boat drifted out to sea.\p"
|
||||
.string "My buddy's a timid fellow, so I'm\n"
|
||||
.string "worried about him.$"
|
||||
|
||||
VSSeeker_Text_BatteryNotChargedNeedXSteps::
|
||||
.string "The battery isn't charged enough.\p"
|
||||
.string "No. of steps required to fully\n"
|
||||
.string "charge the battery: {STR_VAR_1}{PAUSE_UNTIL_PRESS}$"
|
||||
|
||||
VSSeeker_Text_NoTrainersWithinRange::
|
||||
.string "There are no Trainers within range\n"
|
||||
.string "who can battle…\p"
|
||||
.string "The VS Seeker was turned off.{PAUSE_UNTIL_PRESS}$"
|
||||
|
||||
VSSeeker_Text_TrainersNotReady::
|
||||
.string "The other Trainers don't appear\n"
|
||||
.string "to be ready for battle.\p"
|
||||
.string "Let's wait till later.{PAUSE_UNTIL_PRESS}$"
|
||||
|
|
BIN
graphics/field_effects/pics/emote_x.png
Normal file
BIN
graphics/field_effects/pics/emote_x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 190 B |
BIN
graphics/field_effects/pics/emotion_double_exclamation.png
Normal file
BIN
graphics/field_effects/pics/emotion_double_exclamation.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 173 B |
|
@ -70,5 +70,8 @@ u16 CountBattledRematchTeams(u16 trainerId);
|
|||
|
||||
void DoStandardWildBattle_Debug(void);
|
||||
void BattleSetup_StartTrainerBattle_Debug(void);
|
||||
s32 TrainerIdToRematchTableId(const struct RematchTrainer *table, u16 trainerId);
|
||||
s32 FirstBattleTrainerIdToRematchTableId(const struct RematchTrainer *table, u16 trainerId);
|
||||
u16 GetRematchTrainerIdFromTable(const struct RematchTrainer *table, u16 firstBattleTrainerId);
|
||||
|
||||
#endif // GUARD_BATTLE_SETUP_H
|
||||
|
|
|
@ -29,4 +29,7 @@
|
|||
#define VAR_LAST_REPEL_LURE_USED 0 // If this var has been assigned, last Repel/Lure used will be saved and the player will get prompted with the vanilla repel YES/NO option, unless I_REPEL_LURE_MENU is set to TRUE.
|
||||
#define I_REPEL_LURE_MENU TRUE // If TRUE, the player is able to choose which Repel/Lure to use once the previous one runs out. Cursor position is saved by VAR_LAST_REPEL_LURE_USED if not 0.
|
||||
|
||||
// Vs. Seeker
|
||||
#define I_VS_SEEKER_CHARGING 0 // If this flag is assigned, the Vs Seeker will functionlity will be enabled. When the player has the Vs. Seeker, Match Call rematch functions will stop working.
|
||||
|
||||
#endif // GUARD_CONFIG_ITEM_H
|
||||
|
|
|
@ -242,6 +242,8 @@
|
|||
#define MOVEMENT_ACTION_FIGURE_8 0x9B
|
||||
#define MOVEMENT_ACTION_FLY_UP 0x9C
|
||||
#define MOVEMENT_ACTION_FLY_DOWN 0x9D
|
||||
#define MOVEMENT_ACTION_EMOTE_X 0x9E
|
||||
#define MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK 0x9F
|
||||
|
||||
#define MOVEMENT_ACTION_STEP_END 0xFE
|
||||
#define MOVEMENT_ACTION_NONE 0xFF
|
||||
|
@ -300,6 +302,7 @@
|
|||
#define ANIM_GET_ON_OFF_POKEMON_EAST (ANIM_STD_COUNT + 3)
|
||||
|
||||
#define ANIM_NURSE_BOW (ANIM_STD_COUNT + 0)
|
||||
#define ANIM_RAISE_HAND (ANIM_STD_COUNT + 0)
|
||||
|
||||
#define ANIM_FIELD_MOVE 0
|
||||
|
||||
|
|
|
@ -68,6 +68,9 @@
|
|||
#define FLDEFF_RAYQUAZA_SPOTLIGHT 64
|
||||
#define FLDEFF_DESTROY_DEOXYS_ROCK 65
|
||||
#define FLDEFF_MOVE_DEOXYS_ROCK 66
|
||||
#define FLDEFF_USE_VS_SEEKER 67
|
||||
#define FLDEFF_X_ICON 68
|
||||
#define FLDEFF_DOUBLE_EXCL_MARK_ICON 69
|
||||
|
||||
#define FLDEFFOBJ_SHADOW_S 0
|
||||
#define FLDEFFOBJ_SHADOW_M 1
|
||||
|
|
|
@ -440,4 +440,7 @@ bool32 IsVirtualObjectInvisible(u8 virtualObjId);
|
|||
void SetVirtualObjectSpriteAnim(u8 virtualObjId, u8 animNum);
|
||||
bool32 IsVirtualObjectAnimating(u8 virtualObjId);
|
||||
|
||||
bool8 MovementAction_EmoteX_Step0(struct ObjectEvent *, struct Sprite *);
|
||||
bool8 MovementAction_EmoteDoubleExclamationMark_Step0(struct ObjectEvent *, struct Sprite *);
|
||||
|
||||
#endif //GUARD_EVENT_OBJECT_MOVEMENT_H
|
||||
|
|
|
@ -612,4 +612,9 @@ extern const u8 EventScript_TradeCenter_Chair0[];
|
|||
extern const u8 EventScript_ConfirmLeaveCableClubRoom[];
|
||||
extern const u8 EventScript_TerminateLink[];
|
||||
|
||||
extern const u8 VSSeeker_Text_BatteryNotChargedNeedXSteps[];
|
||||
extern const u8 VSSeeker_Text_NoTrainersWithinRange[];
|
||||
extern const u8 VSSeeker_Text_TrainersNotReady[];
|
||||
extern const u8 EventScript_VsSeekerChargingDone[];
|
||||
|
||||
#endif // GUARD_EVENT_SCRIPTS_H
|
||||
|
|
|
@ -254,6 +254,7 @@ enum {
|
|||
PLAYER_AVATAR_STATE_FIELD_MOVE,
|
||||
PLAYER_AVATAR_STATE_FISHING,
|
||||
PLAYER_AVATAR_STATE_WATERING,
|
||||
PLAYER_AVATAR_STATE_VSSEEKER,
|
||||
};
|
||||
|
||||
#define PLAYER_AVATAR_FLAG_ON_FOOT (1 << 0)
|
||||
|
|
|
@ -41,6 +41,8 @@ void ItemUseInBattle_PartyMenuChooseMove(u8 taskId);
|
|||
void Task_UseDigEscapeRopeOnField(u8 taskId);
|
||||
u8 CanUseDigOrEscapeRopeOnCurMap(void);
|
||||
u8 CheckIfItemIsTMHMOrEvolutionStone(u16 itemId);
|
||||
void FieldUseFunc_VsSeeker(u8 taskId);
|
||||
void Task_ItemUse_CloseMessageBoxAndReturnToField_VsSeeker(u8 taskId);
|
||||
|
||||
enum {
|
||||
BALL_THROW_UNABLE_TWO_MONS,
|
||||
|
|
|
@ -26,5 +26,7 @@ u8 FldEff_HeartIcon(void);
|
|||
u8 GetCurrentApproachingTrainerObjectEventId(void);
|
||||
u8 GetChosenApproachingTrainerObjectEventId(u8 arrayId);
|
||||
void PlayerFaceTrainerAfterBattle(void);
|
||||
u8 FldEff_DoubleExclMarkIcon(void);
|
||||
u8 FldEff_XIcon(void);
|
||||
|
||||
#endif // GUARD_TRAINER_SEE_H
|
||||
|
|
14
include/vs_seeker.h
Normal file
14
include/vs_seeker.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef GUARD_VS_SEEKER_H
|
||||
#define GUARD_VS_SEEKER_H
|
||||
|
||||
#include "global.h"
|
||||
|
||||
void Task_InitVsSeekerAndCheckForTrainersOnScreen(u8 taskId);
|
||||
bool8 UpdateVsSeekerStepCounter(void);
|
||||
void MapResetTrainerRematches(u16 mapGroup, u16 mapNum);
|
||||
void ClearRematchMovementByTrainerId(void);
|
||||
u16 GetRematchTrainerIdVSSeeker(u16 trainerId);
|
||||
|
||||
#define VSSEEKER_RECHARGE_STEPS 100
|
||||
|
||||
#endif //GUARD_VS_SEEKER_H
|
|
@ -38,6 +38,8 @@
|
|||
#include "mirage_tower.h"
|
||||
#include "field_screen_effect.h"
|
||||
#include "data.h"
|
||||
#include "vs_seeker.h"
|
||||
#include "item.h"
|
||||
#include "constants/battle_frontier.h"
|
||||
#include "constants/battle_setup.h"
|
||||
#include "constants/game_stat.h"
|
||||
|
@ -1627,7 +1629,7 @@ static const u8 *GetTrainerCantBattleSpeech(void)
|
|||
return ReturnEmptyStringIfNull(sTrainerCannotBattleSpeech);
|
||||
}
|
||||
|
||||
static s32 FirstBattleTrainerIdToRematchTableId(const struct RematchTrainer *table, u16 trainerId)
|
||||
s32 FirstBattleTrainerIdToRematchTableId(const struct RematchTrainer *table, u16 trainerId)
|
||||
{
|
||||
s32 i;
|
||||
|
||||
|
@ -1640,7 +1642,7 @@ static s32 FirstBattleTrainerIdToRematchTableId(const struct RematchTrainer *tab
|
|||
return -1;
|
||||
}
|
||||
|
||||
static s32 TrainerIdToRematchTableId(const struct RematchTrainer *table, u16 trainerId)
|
||||
s32 TrainerIdToRematchTableId(const struct RematchTrainer *table, u16 trainerId)
|
||||
{
|
||||
s32 i, j;
|
||||
|
||||
|
@ -1686,30 +1688,42 @@ static void SetRematchIdForTrainer(const struct RematchTrainer *table, u32 table
|
|||
gSaveBlock1Ptr->trainerRematches[tableId] = i;
|
||||
}
|
||||
|
||||
static bool32 DoesCurrentMapMatchRematchTrainerMap(s32 i, const struct RematchTrainer *table, u16 mapGroup, u16 mapNum)
|
||||
{
|
||||
return table[i].mapGroup == mapGroup && table[i].mapNum == mapNum;
|
||||
}
|
||||
|
||||
bool32 TrainerIsMatchCallRegistered(s32 i)
|
||||
{
|
||||
return FlagGet(FLAG_MATCH_CALL_REGISTERED + i);
|
||||
}
|
||||
|
||||
static bool32 UpdateRandomTrainerRematches(const struct RematchTrainer *table, u16 mapGroup, u16 mapNum)
|
||||
{
|
||||
s32 i;
|
||||
bool32 ret = FALSE;
|
||||
|
||||
if (CheckBagHasItem(ITEM_VS_SEEKER, 1) && I_VS_SEEKER_CHARGING != 0)
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i <= REMATCH_SPECIAL_TRAINER_START; i++)
|
||||
{
|
||||
if (table[i].mapGroup == mapGroup && table[i].mapNum == mapNum && !IsRematchForbidden(i))
|
||||
if (DoesCurrentMapMatchRematchTrainerMap(i,table,mapGroup,mapNum) && !IsRematchForbidden(i))
|
||||
continue;
|
||||
|
||||
if (gSaveBlock1Ptr->trainerRematches[i] != 0)
|
||||
{
|
||||
if (gSaveBlock1Ptr->trainerRematches[i] != 0)
|
||||
{
|
||||
// Trainer already wants a rematch. Don't bother updating it.
|
||||
ret = TRUE;
|
||||
}
|
||||
else if (FlagGet(FLAG_MATCH_CALL_REGISTERED + i)
|
||||
&& (Random() % 100) <= 30) // 31% chance of getting a rematch.
|
||||
{
|
||||
SetRematchIdForTrainer(table, i);
|
||||
ret = TRUE;
|
||||
}
|
||||
// Trainer already wants a rematch. Don't bother updating it.
|
||||
return TRUE;
|
||||
}
|
||||
else if (TrainerIsMatchCallRegistered(i) && ((Random() % 100) <= 30))
|
||||
// 31% chance of getting a rematch.
|
||||
{
|
||||
SetRematchIdForTrainer(table, i);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void UpdateRematchIfDefeated(s32 rematchTableId)
|
||||
|
@ -1772,7 +1786,7 @@ static bool8 IsTrainerReadyForRematch_(const struct RematchTrainer *table, u16 t
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static u16 GetRematchTrainerIdFromTable(const struct RematchTrainer *table, u16 firstBattleTrainerId)
|
||||
u16 GetRematchTrainerIdFromTable(const struct RematchTrainer *table, u16 firstBattleTrainerId)
|
||||
{
|
||||
const struct RematchTrainer *trainerEntry;
|
||||
s32 i;
|
||||
|
@ -1877,7 +1891,9 @@ static bool32 HasAtLeastFiveBadges(void)
|
|||
|
||||
void IncrementRematchStepCounter(void)
|
||||
{
|
||||
if (HasAtLeastFiveBadges())
|
||||
if (HasAtLeastFiveBadges()
|
||||
&& (I_VS_SEEKER_CHARGING != 0)
|
||||
&& (!CheckBagHasItem(ITEM_VS_SEEKER, 1)))
|
||||
{
|
||||
if (gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX)
|
||||
gSaveBlock1Ptr->trainerRematchStepCounter = STEP_COUNTER_MAX;
|
||||
|
@ -1912,7 +1928,10 @@ bool32 IsRematchTrainerIn(u16 mapGroup, u16 mapNum)
|
|||
|
||||
static u16 GetRematchTrainerId(u16 trainerId)
|
||||
{
|
||||
return GetRematchTrainerIdFromTable(gRematchTable, trainerId);
|
||||
if (FlagGet(I_VS_SEEKER_CHARGING) && (I_VS_SEEKER_CHARGING != 0))
|
||||
return GetRematchTrainerIdVSSeeker(trainerId);
|
||||
else
|
||||
return GetRematchTrainerIdFromTable(gRematchTable, trainerId);
|
||||
}
|
||||
|
||||
u16 GetLastBeatenRematchTrainerId(u16 trainerId)
|
||||
|
@ -1935,6 +1954,9 @@ bool8 IsTrainerReadyForRematch(void)
|
|||
|
||||
static void HandleRematchVarsOnBattleEnd(void)
|
||||
{
|
||||
if ((gBattleTypeFlags & BATTLE_TYPE_TRAINER) && (I_VS_SEEKER_CHARGING != 0))
|
||||
ClearRematchMovementByTrainerId();
|
||||
|
||||
ClearTrainerWantRematchState(gRematchTable, gTrainerBattleOpponent_A);
|
||||
SetBattledTrainersFlags();
|
||||
}
|
||||
|
|
|
@ -8723,7 +8723,11 @@ const struct Item gItems[] =
|
|||
.importance = 1,
|
||||
.pocket = POCKET_KEY_ITEMS,
|
||||
.type = ITEM_USE_FIELD,
|
||||
#if I_VS_SEEKER_CHARGING != 0
|
||||
.fieldUseFunc = FieldUseFunc_VsSeeker,
|
||||
#else
|
||||
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
|
||||
#endif
|
||||
},
|
||||
|
||||
[ITEM_TM_CASE] =
|
||||
|
|
|
@ -420,6 +420,8 @@ u8 (*const gMovementActionFuncs_StopLevitateAtTop[])(struct ObjectEvent *, struc
|
|||
u8 (*const gMovementActionFuncs_Figure8[])(struct ObjectEvent *, struct Sprite *);
|
||||
u8 (*const gMovementActionFuncs_FlyUp[])(struct ObjectEvent *, struct Sprite *);
|
||||
u8 (*const gMovementActionFuncs_FlyDown[])(struct ObjectEvent *, struct Sprite *);
|
||||
u8 (*const gMovementActionFuncs_EmoteX[])(struct ObjectEvent *, struct Sprite *);
|
||||
u8 (*const gMovementActionFuncs_EmoteDoubleExclMark[])(struct ObjectEvent *, struct Sprite *);
|
||||
|
||||
u8 (*const *const gMovementActionFuncs[])(struct ObjectEvent *, struct Sprite *) = {
|
||||
[MOVEMENT_ACTION_FACE_DOWN] = gMovementActionFuncs_FaceDown,
|
||||
|
@ -580,6 +582,8 @@ u8 (*const *const gMovementActionFuncs[])(struct ObjectEvent *, struct Sprite *)
|
|||
[MOVEMENT_ACTION_FIGURE_8] = gMovementActionFuncs_Figure8,
|
||||
[MOVEMENT_ACTION_FLY_UP] = gMovementActionFuncs_FlyUp,
|
||||
[MOVEMENT_ACTION_FLY_DOWN] = gMovementActionFuncs_FlyDown,
|
||||
[MOVEMENT_ACTION_EMOTE_X] = gMovementActionFuncs_EmoteX,
|
||||
[MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK] = gMovementActionFuncs_EmoteDoubleExclMark,
|
||||
};
|
||||
|
||||
u8 (*const gMovementActionFuncs_FaceDown[])(struct ObjectEvent *, struct Sprite *) = {
|
||||
|
@ -1519,3 +1523,13 @@ u8 (*const gMovementActionFuncs_StopLevitateAtTop[])(struct ObjectEvent *, struc
|
|||
MovementAction_StopLevitateAtTop_Step0,
|
||||
MovementAction_Finish,
|
||||
};
|
||||
|
||||
u8 (*const gMovementActionFuncs_EmoteX[])(struct ObjectEvent *, struct Sprite *) = {
|
||||
MovementAction_EmoteX_Step0,
|
||||
MovementAction_Finish,
|
||||
};
|
||||
|
||||
u8 (*const gMovementActionFuncs_EmoteDoubleExclMark[])(struct ObjectEvent *, struct Sprite *) = {
|
||||
MovementAction_EmoteDoubleExclamationMark_Step0,
|
||||
MovementAction_Finish,
|
||||
};
|
||||
|
|
|
@ -8965,3 +8965,19 @@ u8 MovementAction_Fly_Finish(struct ObjectEvent *objectEvent, struct Sprite *spr
|
|||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool8 MovementAction_EmoteX_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite)
|
||||
{
|
||||
ObjectEventGetLocalIdAndMap(objectEvent, &gFieldEffectArguments[0], &gFieldEffectArguments[1], &gFieldEffectArguments[2]);
|
||||
FieldEffectStart(FLDEFF_X_ICON);
|
||||
sprite->sActionFuncId = 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool8 MovementAction_EmoteDoubleExclamationMark_Step0(struct ObjectEvent *objectEvent, struct Sprite *sprite)
|
||||
{
|
||||
ObjectEventGetLocalIdAndMap(objectEvent, &gFieldEffectArguments[0], &gFieldEffectArguments[1], &gFieldEffectArguments[2]);
|
||||
FieldEffectStart(FLDEFF_DOUBLE_EXCL_MARK_ICON);
|
||||
sprite->sActionFuncId = 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "start_menu.h"
|
||||
#include "trainer_see.h"
|
||||
#include "trainer_hill.h"
|
||||
#include "vs_seeker.h"
|
||||
#include "wild_encounter.h"
|
||||
#include "constants/event_bg.h"
|
||||
#include "constants/event_objects.h"
|
||||
|
@ -615,6 +616,11 @@ static bool8 TryStartStepCountScript(u16 metatileBehavior)
|
|||
ScriptContext_SetupScript(MossdeepCity_SpaceCenter_2F_EventScript_RivalRayquazaCall);
|
||||
return TRUE;
|
||||
}
|
||||
if (UpdateVsSeekerStepCounter())
|
||||
{
|
||||
ScriptContext_SetupScript(EventScript_VsSeekerChargingDone);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (SafariZoneTakeStep() == TRUE)
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "constants/metatile_behaviors.h"
|
||||
#include "constants/rgb.h"
|
||||
#include "constants/songs.h"
|
||||
#include "constants/map_types.h"
|
||||
|
||||
#define subsprite_table(ptr) {.subsprites = ptr, .subspriteCount = (sizeof ptr) / (sizeof(struct Subsprite))}
|
||||
|
||||
|
@ -231,6 +232,12 @@ static void SpriteCB_DeoxysRockFragment(struct Sprite *sprite);
|
|||
|
||||
static void Task_MoveDeoxysRock(u8 taskId);
|
||||
|
||||
static void Task_FldEffUseVsSeeker(u8 taskId);
|
||||
static void UseVsSeeker_StopPlayerMovement(struct Task *task);
|
||||
static void UseVsSeeker_DoPlayerAnimation(struct Task *task);
|
||||
static void UseVsSeeker_ResetPlayerGraphics(struct Task *task);
|
||||
static void UseVsSeeker_CleanUpFieldEffect(struct Task *task);
|
||||
|
||||
// Static RAM declarations
|
||||
|
||||
static u8 sActiveList[32];
|
||||
|
@ -3911,3 +3918,68 @@ static void Task_MoveDeoxysRock(u8 taskId)
|
|||
#undef tVelocityY
|
||||
#undef tMoveSteps
|
||||
#undef tObjEventId
|
||||
|
||||
static void (*const sUseVsSeekerEffectFuncs[])(struct Task *task) = {
|
||||
UseVsSeeker_StopPlayerMovement,
|
||||
UseVsSeeker_DoPlayerAnimation,
|
||||
UseVsSeeker_ResetPlayerGraphics,
|
||||
UseVsSeeker_CleanUpFieldEffect
|
||||
};
|
||||
|
||||
u32 FldEff_UseVsSeeker(void)
|
||||
{
|
||||
CreateTask(Task_FldEffUseVsSeeker, 0xFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Task_FldEffUseVsSeeker(u8 taskId)
|
||||
{
|
||||
sUseVsSeekerEffectFuncs[gTasks[taskId].data[0]](&gTasks[taskId]);
|
||||
}
|
||||
|
||||
static void UseVsSeeker_StopPlayerMovement(struct Task *task)
|
||||
{
|
||||
LockPlayerFieldControls();
|
||||
FreezeObjectEvents();
|
||||
gPlayerAvatar.preventStep = TRUE;
|
||||
task->data[0]++;
|
||||
}
|
||||
|
||||
static void UseVsSeeker_DoPlayerAnimation(struct Task *task)
|
||||
{
|
||||
struct ObjectEvent * playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
|
||||
if ((ObjectEventIsMovementOverridden(playerObj) && (!(ObjectEventClearHeldMovementIfFinished(playerObj)))))
|
||||
return;
|
||||
|
||||
if (gMapHeader.mapType != MAP_TYPE_UNDERWATER)
|
||||
{
|
||||
SetPlayerAvatarFieldMove();
|
||||
ObjectEventSetHeldMovement(playerObj, MOVEMENT_ACTION_START_ANIM_IN_DIRECTION);
|
||||
}
|
||||
task->data[0]++;
|
||||
}
|
||||
|
||||
static void UseVsSeeker_ResetPlayerGraphics(struct Task *task) {
|
||||
struct ObjectEvent* playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
|
||||
|
||||
if (!ObjectEventClearHeldMovementIfFinished(playerObj))
|
||||
return;
|
||||
|
||||
if (gMapHeader.mapType != MAP_TYPE_UNDERWATER)
|
||||
{
|
||||
ObjectEventSetGraphicsId(&gObjectEvents[gPlayerAvatar.objectEventId], GetPlayerAvatarGraphicsIdByCurrentState());
|
||||
ObjectEventForceSetHeldMovement(playerObj, GetFaceDirectionMovementAction(playerObj->facingDirection));
|
||||
}
|
||||
task->data[0]++;
|
||||
}
|
||||
|
||||
static void UseVsSeeker_CleanUpFieldEffect(struct Task *task)
|
||||
{
|
||||
struct ObjectEvent * playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
|
||||
if (!ObjectEventClearHeldMovementIfFinished(playerObj))
|
||||
return;
|
||||
|
||||
gPlayerAvatar.preventStep = FALSE;
|
||||
FieldEffectActiveListRemove(FLDEFF_USE_VS_SEEKER);
|
||||
DestroyTask(FindTaskIdByFunc(Task_FldEffUseVsSeeker));
|
||||
}
|
||||
|
|
|
@ -241,7 +241,8 @@ static const u8 sRivalAvatarGfxIds[][2] =
|
|||
[PLAYER_AVATAR_STATE_UNDERWATER] = {OBJ_EVENT_GFX_BRENDAN_UNDERWATER, OBJ_EVENT_GFX_MAY_UNDERWATER},
|
||||
[PLAYER_AVATAR_STATE_FIELD_MOVE] = {OBJ_EVENT_GFX_RIVAL_BRENDAN_FIELD_MOVE, OBJ_EVENT_GFX_RIVAL_MAY_FIELD_MOVE},
|
||||
[PLAYER_AVATAR_STATE_FISHING] = {OBJ_EVENT_GFX_BRENDAN_FISHING, OBJ_EVENT_GFX_MAY_FISHING},
|
||||
[PLAYER_AVATAR_STATE_WATERING] = {OBJ_EVENT_GFX_BRENDAN_WATERING, OBJ_EVENT_GFX_MAY_WATERING}
|
||||
[PLAYER_AVATAR_STATE_WATERING] = {OBJ_EVENT_GFX_BRENDAN_WATERING, OBJ_EVENT_GFX_MAY_WATERING},
|
||||
[PLAYER_AVATAR_STATE_VSSEEKER] = {OBJ_EVENT_GFX_RIVAL_BRENDAN_FIELD_MOVE, OBJ_EVENT_GFX_RIVAL_MAY_FIELD_MOVE},
|
||||
};
|
||||
|
||||
static const u8 sPlayerAvatarGfxIds[][2] =
|
||||
|
@ -254,6 +255,7 @@ static const u8 sPlayerAvatarGfxIds[][2] =
|
|||
[PLAYER_AVATAR_STATE_FIELD_MOVE] = {OBJ_EVENT_GFX_BRENDAN_FIELD_MOVE, OBJ_EVENT_GFX_MAY_FIELD_MOVE},
|
||||
[PLAYER_AVATAR_STATE_FISHING] = {OBJ_EVENT_GFX_BRENDAN_FISHING, OBJ_EVENT_GFX_MAY_FISHING},
|
||||
[PLAYER_AVATAR_STATE_WATERING] = {OBJ_EVENT_GFX_BRENDAN_WATERING, OBJ_EVENT_GFX_MAY_WATERING},
|
||||
[PLAYER_AVATAR_STATE_VSSEEKER] = {OBJ_EVENT_GFX_BRENDAN_FIELD_MOVE, OBJ_EVENT_GFX_MAY_FIELD_MOVE},
|
||||
};
|
||||
|
||||
static const u8 sFRLGAvatarGfxIds[GENDER_COUNT] =
|
||||
|
|
|
@ -38,11 +38,13 @@
|
|||
#include "string_util.h"
|
||||
#include "task.h"
|
||||
#include "text.h"
|
||||
#include "vs_seeker.h"
|
||||
#include "constants/event_bg.h"
|
||||
#include "constants/event_objects.h"
|
||||
#include "constants/item_effects.h"
|
||||
#include "constants/items.h"
|
||||
#include "constants/songs.h"
|
||||
#include "constants/map_types.h"
|
||||
|
||||
static void SetUpItemUseCallback(u8);
|
||||
static void FieldCB_UseItemOnField(void);
|
||||
|
@ -75,6 +77,7 @@ static void Task_CloseCantUseKeyItemMessage(u8);
|
|||
static void SetDistanceOfClosestHiddenItem(u8, s16, s16);
|
||||
static void CB2_OpenPokeblockFromBag(void);
|
||||
static void ItemUseOnFieldCB_Honey(u8 taskId);
|
||||
static bool32 IsValidLocationForVsSeeker(void);
|
||||
static bool32 CannotUseBagBattleItem(u16 itemId);
|
||||
|
||||
// EWRAM variables
|
||||
|
@ -1315,7 +1318,7 @@ void ItemUseOutOfBattle_FormChange_ConsumedOnUse(u8 taskId)
|
|||
}
|
||||
|
||||
void ItemUseOutOfBattle_RotomCatalog(u8 taskId)
|
||||
{
|
||||
{
|
||||
if (!gTasks[taskId].tUsingRegisteredKeyItem)
|
||||
{
|
||||
gItemUseCB = ItemUseCB_RotomCatalog;
|
||||
|
@ -1330,7 +1333,7 @@ void ItemUseOutOfBattle_RotomCatalog(u8 taskId)
|
|||
}
|
||||
|
||||
void ItemUseOutOfBattle_ZygardeCube(u8 taskId)
|
||||
{
|
||||
{
|
||||
if (!gTasks[taskId].tUsingRegisteredKeyItem)
|
||||
{
|
||||
gItemUseCB = ItemUseCB_ZygardeCube;
|
||||
|
@ -1379,4 +1382,68 @@ void ItemUseOutOfBattle_CannotUse(u8 taskId)
|
|||
DisplayDadsAdviceCannotUseItemMessage(taskId, gTasks[taskId].tUsingRegisteredKeyItem);
|
||||
}
|
||||
|
||||
static bool32 IsValidLocationForVsSeeker(void)
|
||||
{
|
||||
u16 mapGroup = gSaveBlock1Ptr->location.mapGroup;
|
||||
u16 mapNum = gSaveBlock1Ptr->location.mapNum;
|
||||
u16 mapType = gMapHeader.mapType;
|
||||
|
||||
typedef struct {
|
||||
u16 mapGroup;
|
||||
u16 mapNum;
|
||||
} Location;
|
||||
|
||||
u32 i;
|
||||
Location validIndoorLocations[] =
|
||||
{
|
||||
{ MAP_GROUP(MT_PYRE_SUMMIT), MAP_NUM(MT_PYRE_SUMMIT) },
|
||||
{ MAP_GROUP(SAFARI_ZONE_NORTH), MAP_NUM(SAFARI_ZONE_NORTH) },
|
||||
{ MAP_GROUP(SAFARI_ZONE_NORTHEAST), MAP_NUM(SAFARI_ZONE_NORTHEAST) },
|
||||
{ MAP_GROUP(SAFARI_ZONE_NORTHWEST), MAP_NUM(SAFARI_ZONE_NORTHWEST) },
|
||||
{ MAP_GROUP(SAFARI_ZONE_SOUTH), MAP_NUM(SAFARI_ZONE_SOUTH) },
|
||||
{ MAP_GROUP(SAFARI_ZONE_SOUTHEAST), MAP_NUM(SAFARI_ZONE_SOUTHEAST) },
|
||||
{ MAP_GROUP(SAFARI_ZONE_SOUTHWEST), MAP_NUM(SAFARI_ZONE_SOUTHWEST) },
|
||||
{ MAP_GROUP(SKY_PILLAR_TOP), MAP_NUM(SKY_PILLAR_TOP) },
|
||||
{ MAP_GROUP(SOUTHERN_ISLAND_EXTERIOR), MAP_NUM(SOUTHERN_ISLAND_EXTERIOR) },
|
||||
{ MAP_GROUP(SOUTHERN_ISLAND_INTERIOR), MAP_NUM(SOUTHERN_ISLAND_INTERIOR) },
|
||||
{ MAP_GROUP(RUSTBORO_CITY_GYM), MAP_NUM(RUSTBORO_CITY_GYM) },
|
||||
{ MAP_GROUP(DEWFORD_TOWN_GYM), MAP_NUM(DEWFORD_TOWN_GYM) },
|
||||
{ MAP_GROUP(MAUVILLE_CITY_GYM), MAP_NUM(MAUVILLE_CITY_GYM) },
|
||||
{ MAP_GROUP(LAVARIDGE_TOWN_GYM_1F), MAP_NUM(LAVARIDGE_TOWN_GYM_1F) },
|
||||
{ MAP_GROUP(LAVARIDGE_TOWN_GYM_B1F), MAP_NUM(LAVARIDGE_TOWN_GYM_B1F) },
|
||||
{ MAP_GROUP(PETALBURG_CITY_GYM), MAP_NUM(PETALBURG_CITY_GYM) },
|
||||
{ MAP_GROUP(FORTREE_CITY_GYM), MAP_NUM(FORTREE_CITY_GYM) },
|
||||
{ MAP_GROUP(MOSSDEEP_CITY_GYM), MAP_NUM(MOSSDEEP_CITY_GYM) },
|
||||
{ MAP_GROUP(SOOTOPOLIS_CITY_GYM_1F), MAP_NUM(SOOTOPOLIS_CITY_GYM_1F) },
|
||||
{ MAP_GROUP(SOOTOPOLIS_CITY_GYM_B1F), MAP_NUM(SOOTOPOLIS_CITY_GYM_B1F) },
|
||||
};
|
||||
|
||||
if (IsMapTypeOutdoors(mapType))
|
||||
return TRUE;
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(validIndoorLocations); i++)
|
||||
{
|
||||
if (mapNum == validIndoorLocations[i].mapNum && mapGroup == validIndoorLocations[i].mapGroup)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void FieldUseFunc_VsSeeker(u8 taskId)
|
||||
{
|
||||
if (IsValidLocationForVsSeeker())
|
||||
{
|
||||
sItemUseOnFieldCB = Task_InitVsSeekerAndCheckForTrainersOnScreen;
|
||||
SetUpItemUseOnFieldCallback(taskId);
|
||||
}
|
||||
else
|
||||
DisplayDadsAdviceCannotUseItemMessage(taskId, gTasks[taskId].data[3]);
|
||||
}
|
||||
|
||||
void Task_ItemUse_CloseMessageBoxAndReturnToField_VsSeeker(u8 taskId)
|
||||
{
|
||||
Task_CloseCantUseKeyItemMessage(taskId);
|
||||
}
|
||||
|
||||
#undef tUsingRegisteredKeyItem
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include "tv.h"
|
||||
#include "scanline_effect.h"
|
||||
#include "wild_encounter.h"
|
||||
#include "vs_seeker.h"
|
||||
#include "frontier_util.h"
|
||||
#include "constants/abilities.h"
|
||||
#include "constants/layouts.h"
|
||||
|
@ -818,6 +819,10 @@ void LoadMapFromCameraTransition(u8 mapGroup, u8 mapNum)
|
|||
ResetCyclingRoadChallengeData();
|
||||
RestartWildEncounterImmunitySteps();
|
||||
TryUpdateRandomTrainerRematches(mapGroup, mapNum);
|
||||
|
||||
if (I_VS_SEEKER_CHARGING != 0)
|
||||
MapResetTrainerRematches(mapGroup, mapNum);
|
||||
|
||||
DoTimeBasedEvents();
|
||||
SetSavedWeatherFromCurrMapHeader();
|
||||
ChooseAmbientCrySpecies();
|
||||
|
@ -868,6 +873,10 @@ static void LoadMapFromWarp(bool32 a1)
|
|||
ResetCyclingRoadChallengeData();
|
||||
RestartWildEncounterImmunitySteps();
|
||||
TryUpdateRandomTrainerRematches(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum);
|
||||
|
||||
if (I_VS_SEEKER_CHARGING != 0)
|
||||
MapResetTrainerRematches(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum);
|
||||
|
||||
if (a1 != TRUE)
|
||||
DoTimeBasedEvents();
|
||||
SetSavedWeatherFromCurrMapHeader();
|
||||
|
|
|
@ -62,6 +62,8 @@ EWRAM_DATA u8 gApproachingTrainerId = 0;
|
|||
static const u8 sEmotion_ExclamationMarkGfx[] = INCBIN_U8("graphics/field_effects/pics/emotion_exclamation.4bpp");
|
||||
static const u8 sEmotion_QuestionMarkGfx[] = INCBIN_U8("graphics/field_effects/pics/emotion_question.4bpp");
|
||||
static const u8 sEmotion_HeartGfx[] = INCBIN_U8("graphics/field_effects/pics/emotion_heart.4bpp");
|
||||
static const u8 sEmotion_DoubleExclamationMarkGfx[] = INCBIN_U8("graphics/field_effects/pics/emotion_double_exclamation.4bpp");
|
||||
static const u8 sEmotion_XGfx[] = INCBIN_U8("graphics/field_effects/pics/emote_x.4bpp");
|
||||
|
||||
static u8 (*const sDirectionalApproachDistanceFuncs[])(struct ObjectEvent *trainerObj, s16 range, s16 x, s16 y) =
|
||||
{
|
||||
|
@ -136,6 +138,14 @@ static const struct SpriteFrameImage sSpriteImageTable_ExclamationQuestionMark[]
|
|||
{
|
||||
.data = sEmotion_QuestionMarkGfx,
|
||||
.size = sizeof(sEmotion_QuestionMarkGfx)
|
||||
},
|
||||
{
|
||||
.data = sEmotion_DoubleExclamationMarkGfx,
|
||||
.size = sizeof(sEmotion_DoubleExclamationMarkGfx)
|
||||
},
|
||||
{
|
||||
.data = sEmotion_XGfx,
|
||||
.size = sizeof(sEmotion_XGfx)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -159,10 +169,25 @@ static const union AnimCmd sSpriteAnim_Icons2[] =
|
|||
ANIMCMD_END
|
||||
};
|
||||
|
||||
|
||||
static const union AnimCmd sSpriteAnim_Icons3[] =
|
||||
{
|
||||
ANIMCMD_FRAME(2, 60),
|
||||
ANIMCMD_END
|
||||
};
|
||||
|
||||
static const union AnimCmd sSpriteAnim_Icons4[] =
|
||||
{
|
||||
ANIMCMD_FRAME(3, 60),
|
||||
ANIMCMD_END
|
||||
};
|
||||
|
||||
static const union AnimCmd *const sSpriteAnimTable_Icons[] =
|
||||
{
|
||||
sSpriteAnim_Icons1,
|
||||
sSpriteAnim_Icons2
|
||||
sSpriteAnim_Icons2,
|
||||
sSpriteAnim_Icons3,
|
||||
sSpriteAnim_Icons4
|
||||
};
|
||||
|
||||
static const struct SpriteTemplate sSpriteTemplate_ExclamationQuestionMark =
|
||||
|
@ -731,6 +756,27 @@ u8 FldEff_HeartIcon(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
u8 FldEff_DoubleExclMarkIcon(void)
|
||||
{
|
||||
u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_ExclamationQuestionMark, 0, 0, 0x53);
|
||||
|
||||
if (spriteId != MAX_SPRITES)
|
||||
SetIconSpriteData(&gSprites[spriteId], FLDEFF_EXCLAMATION_MARK_ICON, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 FldEff_XIcon(void)
|
||||
{
|
||||
u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_ExclamationQuestionMark, 0, 0, 0x53);
|
||||
|
||||
if (spriteId != MAX_SPRITES)
|
||||
SetIconSpriteData(&gSprites[spriteId], FLDEFF_EXCLAMATION_MARK_ICON, 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SetIconSpriteData(struct Sprite *sprite, u16 fldEffId, u8 spriteAnimNum)
|
||||
{
|
||||
sprite->oam.priority = 1;
|
||||
|
|
793
src/vs_seeker.c
Normal file
793
src/vs_seeker.c
Normal file
|
@ -0,0 +1,793 @@
|
|||
#include "global.h"
|
||||
#include "task.h"
|
||||
#include "event_object_movement.h"
|
||||
#include "item_use.h"
|
||||
#include "event_scripts.h"
|
||||
#include "event_data.h"
|
||||
#include "script.h"
|
||||
#include "event_object_lock.h"
|
||||
#include "field_specials.h"
|
||||
#include "item.h"
|
||||
#include "item_menu.h"
|
||||
#include "field_effect.h"
|
||||
#include "script_movement.h"
|
||||
#include "battle.h"
|
||||
#include "battle_setup.h"
|
||||
#include "random.h"
|
||||
#include "field_player_avatar.h"
|
||||
#include "vs_seeker.h"
|
||||
#include "menu.h"
|
||||
#include "string_util.h"
|
||||
#include "tv.h"
|
||||
#include "malloc.h"
|
||||
#include "field_screen_effect.h"
|
||||
#include "gym_leader_rematch.h"
|
||||
#include "sound.h"
|
||||
#include "constants/event_object_movement.h"
|
||||
#include "constants/event_objects.h"
|
||||
#include "constants/items.h"
|
||||
#include "constants/maps.h"
|
||||
#include "constants/songs.h"
|
||||
#include "constants/trainer_types.h"
|
||||
#include "constants/field_effects.h"
|
||||
|
||||
enum
|
||||
{
|
||||
VSSEEKER_NOT_CHARGED,
|
||||
VSSEEKER_NO_ONE_IN_RANGE,
|
||||
VSSEEKER_CAN_USE,
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VSSEEKER_SINGLE_RESP_RAND,
|
||||
VSSEEKER_SINGLE_RESP_NO,
|
||||
VSSEEKER_SINGLE_RESP_YES
|
||||
} VsSeekerSingleRespCode;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VSSEEKER_RESPONSE_NO_RESPONSE,
|
||||
VSSEEKER_RESPONSE_UNFOUGHT_TRAINERS,
|
||||
VSSEEKER_RESPONSE_FOUND_REMATCHES
|
||||
} VsSeekerResponseCode;
|
||||
|
||||
struct VsSeekerTrainerInfo
|
||||
{
|
||||
const u8 *script;
|
||||
u16 trainerIdx;
|
||||
u8 localId;
|
||||
u8 objectEventId;
|
||||
s16 xCoord;
|
||||
s16 yCoord;
|
||||
u8 graphicsId;
|
||||
};
|
||||
|
||||
struct VsSeekerStruct
|
||||
{
|
||||
struct VsSeekerTrainerInfo trainerInfo[OBJECT_EVENTS_COUNT];
|
||||
u16 trainerIdxArray[OBJECT_EVENTS_COUNT];
|
||||
u8 runningBehaviourEtcArray[OBJECT_EVENTS_COUNT];
|
||||
u8 numRematchableTrainers;
|
||||
u8 trainerHasNotYetBeenFought:1;
|
||||
u8 trainerDoesNotWantRematch:1;
|
||||
u8 trainerWantsRematch:1;
|
||||
u8 responseCode:5;
|
||||
};
|
||||
|
||||
// static declarations
|
||||
static EWRAM_DATA struct VsSeekerStruct *sVsSeeker = NULL;
|
||||
|
||||
static void VsSeekerResetInBagStepCounter(void);
|
||||
static void VsSeekerResetChargingStepCounter(void);
|
||||
static void Task_ResetObjectsRematchWantedState(u8 taskId);
|
||||
static void ResetMovementOfRematchableTrainers(void);
|
||||
static void Task_VsSeekerFrameCountdown(u8 taskId);
|
||||
static void Task_VsSeeker_PlaySoundAndGetResponseCode(u8 taskId);
|
||||
static void GatherNearbyTrainerInfo(void);
|
||||
static void Task_VsSeeker_ShowResponseToPlayer(u8 taskId);
|
||||
static bool8 CanUseVsSeeker(void);
|
||||
static u8 GetVsSeekerResponseInArea(void);
|
||||
static u8 GetResponseMovementTypeFromTrainerGraphicsId(u8 graphicsId);
|
||||
static u16 GetTrainerFlagFromScript(const u8 * script);
|
||||
static void ClearAllTrainerRematchStates(void);
|
||||
static bool8 IsTrainerVisibleOnScreen(struct VsSeekerTrainerInfo * trainerInfo);
|
||||
static u32 GetRematchableTrainerLocalId(void);
|
||||
static void StartTrainerObjectMovementScript(struct VsSeekerTrainerInfo * trainerInfo, const u8 * script);
|
||||
static u8 GetCurVsSeekerResponse(s32 vsSeekerIdx, u16 trainerIdx);
|
||||
static void StartAllRespondantIdleMovements(void);
|
||||
static bool8 ObjectEventIdIsSane(u8 objectEventId);
|
||||
static u8 GetRandomFaceDirectionMovementType();
|
||||
|
||||
static const u8 sMovementScript_Wait48[] = {
|
||||
MOVEMENT_ACTION_DELAY_16,
|
||||
MOVEMENT_ACTION_DELAY_16,
|
||||
MOVEMENT_ACTION_DELAY_16,
|
||||
MOVEMENT_ACTION_STEP_END
|
||||
};
|
||||
|
||||
static const u8 sMovementScript_TrainerUnfought[] = {
|
||||
MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK,
|
||||
MOVEMENT_ACTION_STEP_END
|
||||
};
|
||||
|
||||
static const u8 sMovementScript_TrainerNoRematch[] = {
|
||||
MOVEMENT_ACTION_EMOTE_X,
|
||||
MOVEMENT_ACTION_STEP_END
|
||||
};
|
||||
|
||||
static const u8 sMovementScript_TrainerRematch[] = {
|
||||
MOVEMENT_ACTION_WALK_IN_PLACE_FASTER_DOWN,
|
||||
MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK,
|
||||
MOVEMENT_ACTION_STEP_END
|
||||
};
|
||||
|
||||
static const u8 sFaceDirectionMovementTypeByFacingDirection[] = {
|
||||
MOVEMENT_TYPE_FACE_DOWN,
|
||||
MOVEMENT_TYPE_FACE_DOWN,
|
||||
MOVEMENT_TYPE_FACE_UP,
|
||||
MOVEMENT_TYPE_FACE_LEFT,
|
||||
MOVEMENT_TYPE_FACE_RIGHT
|
||||
};
|
||||
|
||||
void VsSeekerFreezeObjectsAfterChargeComplete(void)
|
||||
{
|
||||
CreateTask(Task_ResetObjectsRematchWantedState, 80);
|
||||
}
|
||||
|
||||
#define tIsPlayerFrozen data[0]
|
||||
#define tAreObjectsFrozen data[1]
|
||||
|
||||
static void Task_ResetObjectsRematchWantedState(u8 taskId)
|
||||
{
|
||||
struct Task *task = &gTasks[taskId];
|
||||
u32 i;
|
||||
|
||||
if ((!task->tIsPlayerFrozen) && IsPlayerStandingStill())
|
||||
{
|
||||
PlayerFreeze();
|
||||
task->tIsPlayerFrozen = TRUE;
|
||||
}
|
||||
|
||||
if (!task->tAreObjectsFrozen)
|
||||
{
|
||||
for (i = 0; i < OBJECT_EVENTS_COUNT; i++)
|
||||
{
|
||||
if (!ObjectEventIdIsSane(i))
|
||||
continue;
|
||||
|
||||
if (gObjectEvents[i].singleMovementActive)
|
||||
return;
|
||||
|
||||
FreezeObjectEvent(&gObjectEvents[i]);
|
||||
}
|
||||
}
|
||||
|
||||
task->tAreObjectsFrozen = TRUE;
|
||||
if (task->tIsPlayerFrozen)
|
||||
{
|
||||
DestroyTask(taskId);
|
||||
StopPlayerAvatar();
|
||||
ScriptContext_Enable();
|
||||
}
|
||||
}
|
||||
#undef tIsPlayerFrozen
|
||||
#undef tAreObjectsFrozen
|
||||
|
||||
u16 VsSeekerConvertLocalIdToTableId(u16 localId)
|
||||
{
|
||||
u32 localIdIndex = 0;
|
||||
u32 trainerId = 0;
|
||||
|
||||
for (localIdIndex = 0; localIdIndex < OBJECT_EVENTS_COUNT ; localIdIndex++)
|
||||
{
|
||||
if (sVsSeeker->trainerInfo[localIdIndex].localId == localId)
|
||||
{
|
||||
trainerId = sVsSeeker->trainerInfo[localIdIndex].trainerIdx;
|
||||
return TrainerIdToRematchTableId(gRematchTable,trainerId);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void VsSeekerResetObjectMovementAfterChargeComplete(void)
|
||||
{
|
||||
struct ObjectEventTemplate * templates = gSaveBlock1Ptr->objectEventTemplates;
|
||||
u32 i;
|
||||
u32 movementType;
|
||||
u8 objEventId;
|
||||
struct ObjectEvent * objectEvent;
|
||||
|
||||
for (i = 0; i < gMapHeader.events->objectEventCount; i++)
|
||||
{
|
||||
if (templates[i].trainerType != TRAINER_TYPE_NORMAL
|
||||
&& templates[i].trainerType != TRAINER_TYPE_BURIED)
|
||||
continue;
|
||||
|
||||
if (templates[i].movementType != MOVEMENT_TYPE_ROTATE_CLOCKWISE)
|
||||
continue;
|
||||
|
||||
movementType = GetRandomFaceDirectionMovementType();
|
||||
TryGetObjectEventIdByLocalIdAndMap(templates[i].localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &objEventId);
|
||||
objectEvent = &gObjectEvents[objEventId];
|
||||
|
||||
if (!ObjectEventIdIsSane(objEventId))
|
||||
continue;
|
||||
|
||||
SetTrainerMovementType(objectEvent, movementType);
|
||||
templates[i].movementType = movementType;
|
||||
}
|
||||
}
|
||||
|
||||
bool8 UpdateVsSeekerStepCounter(void)
|
||||
{
|
||||
u8 x = 0;
|
||||
|
||||
if (!I_VS_SEEKER_CHARGING) return FALSE;
|
||||
|
||||
if (CheckBagHasItem(ITEM_VS_SEEKER, 1))
|
||||
{
|
||||
if ((gSaveBlock1Ptr->trainerRematchStepCounter & 0xFF) < VSSEEKER_RECHARGE_STEPS)
|
||||
gSaveBlock1Ptr->trainerRematchStepCounter++;
|
||||
}
|
||||
|
||||
if (FlagGet(I_VS_SEEKER_CHARGING))
|
||||
{
|
||||
if (((gSaveBlock1Ptr->trainerRematchStepCounter >> 8) & 0xFF) < VSSEEKER_RECHARGE_STEPS)
|
||||
{
|
||||
x = (((gSaveBlock1Ptr->trainerRematchStepCounter >> 8) & 0xFF) + 1);
|
||||
gSaveBlock1Ptr->trainerRematchStepCounter = (gSaveBlock1Ptr->trainerRematchStepCounter & 0xFF) | (x << 8);
|
||||
}
|
||||
if (((gSaveBlock1Ptr->trainerRematchStepCounter >> 8) & 0xFF) == VSSEEKER_RECHARGE_STEPS)
|
||||
{
|
||||
FlagClear(I_VS_SEEKER_CHARGING);
|
||||
VsSeekerResetChargingStepCounter();
|
||||
ClearAllTrainerRematchStates();
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void MapResetTrainerRematches(u16 mapGroup, u16 mapNum)
|
||||
{
|
||||
if (!I_VS_SEEKER_CHARGING) return;
|
||||
|
||||
FlagClear(I_VS_SEEKER_CHARGING);
|
||||
VsSeekerResetChargingStepCounter();
|
||||
ClearAllTrainerRematchStates();
|
||||
ResetMovementOfRematchableTrainers();
|
||||
}
|
||||
|
||||
static void ResetMovementOfRematchableTrainers(void)
|
||||
{
|
||||
u32 i;
|
||||
u8 movementType = 0;
|
||||
|
||||
for (i = 0; i < OBJECT_EVENTS_COUNT; i++)
|
||||
{
|
||||
struct ObjectEvent * objectEvent = &gObjectEvents[i];
|
||||
if (objectEvent->movementType != MOVEMENT_TYPE_ROTATE_CLOCKWISE)
|
||||
continue;
|
||||
|
||||
movementType = GetRandomFaceDirectionMovementType();
|
||||
|
||||
if (!objectEvent->active || gSprites[objectEvent->spriteId].data[0] != i)
|
||||
continue;
|
||||
|
||||
gSprites[objectEvent->spriteId].x2 = 0;
|
||||
gSprites[objectEvent->spriteId].y2 = 0;
|
||||
SetTrainerMovementType(objectEvent, movementType);
|
||||
}
|
||||
}
|
||||
|
||||
static void VsSeekerResetInBagStepCounter(void)
|
||||
{
|
||||
gSaveBlock1Ptr->trainerRematchStepCounter &= 0xFF00;
|
||||
}
|
||||
|
||||
static void VsSeekerResetChargingStepCounter(void)
|
||||
{
|
||||
gSaveBlock1Ptr->trainerRematchStepCounter &= 0x00FF;
|
||||
}
|
||||
|
||||
void Task_InitVsSeekerAndCheckForTrainersOnScreen(u8 taskId)
|
||||
{
|
||||
u32 i;
|
||||
u32 respval;
|
||||
|
||||
if (!I_VS_SEEKER_CHARGING) return;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
gTasks[taskId].data[i] = 0;
|
||||
|
||||
sVsSeeker = AllocZeroed(sizeof(struct VsSeekerStruct));
|
||||
GatherNearbyTrainerInfo();
|
||||
respval = CanUseVsSeeker();
|
||||
if (respval == VSSEEKER_NOT_CHARGED)
|
||||
{
|
||||
Free(sVsSeeker);
|
||||
DisplayItemMessageOnField(taskId, VSSeeker_Text_BatteryNotChargedNeedXSteps, Task_ItemUse_CloseMessageBoxAndReturnToField_VsSeeker);
|
||||
}
|
||||
else if (respval == VSSEEKER_NO_ONE_IN_RANGE)
|
||||
{
|
||||
Free(sVsSeeker);
|
||||
DisplayItemMessageOnField(taskId, VSSeeker_Text_NoTrainersWithinRange, Task_ItemUse_CloseMessageBoxAndReturnToField_VsSeeker);
|
||||
}
|
||||
else if (respval == VSSEEKER_CAN_USE)
|
||||
{
|
||||
FieldEffectStart(FLDEFF_USE_VS_SEEKER);
|
||||
gTasks[taskId].func = Task_VsSeekerFrameCountdown;
|
||||
gTasks[taskId].data[0] = 15;
|
||||
}
|
||||
}
|
||||
|
||||
static void Task_VsSeekerFrameCountdown(u8 taskId)
|
||||
{
|
||||
if (--gTasks[taskId].data[0] == 0)
|
||||
{
|
||||
gTasks[taskId].func = Task_VsSeeker_PlaySoundAndGetResponseCode;
|
||||
gTasks[taskId].data[1] = 16;
|
||||
}
|
||||
}
|
||||
|
||||
static void Task_VsSeeker_PlaySoundAndGetResponseCode(u8 taskId)
|
||||
{
|
||||
s16 * data = gTasks[taskId].data;
|
||||
|
||||
if (data[2] != 2 && --data[1] == 0)
|
||||
{
|
||||
PlaySE(SE_CONTEST_MONS_TURN);
|
||||
data[1] = 11;
|
||||
data[2]++;
|
||||
}
|
||||
|
||||
if (!FieldEffectActiveListContains(FLDEFF_USE_VS_SEEKER))
|
||||
{
|
||||
data[1] = 0;
|
||||
data[2] = 0;
|
||||
VsSeekerResetInBagStepCounter();
|
||||
sVsSeeker->responseCode = GetVsSeekerResponseInArea();
|
||||
ScriptMovement_StartObjectMovementScript(0xFF, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, sMovementScript_Wait48);
|
||||
gTasks[taskId].func = Task_VsSeeker_ShowResponseToPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
static void GatherNearbyTrainerInfo(void)
|
||||
{
|
||||
struct ObjectEventTemplate *templates = gSaveBlock1Ptr->objectEventTemplates;
|
||||
u8 objectEventId = 0;
|
||||
u8 vsSeekerObjectIdx = 0;
|
||||
s32 objectEventIdx;
|
||||
|
||||
for (objectEventIdx = 0; objectEventIdx < gMapHeader.events->objectEventCount; objectEventIdx++)
|
||||
{
|
||||
if (templates[objectEventIdx].trainerType != TRAINER_TYPE_NORMAL && templates[objectEventIdx].trainerType != TRAINER_TYPE_BURIED)
|
||||
continue;
|
||||
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].script = templates[objectEventIdx].script;
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].trainerIdx = GetTrainerFlagFromScript(templates[objectEventIdx].script);
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].localId = templates[objectEventIdx].localId;
|
||||
TryGetObjectEventIdByLocalIdAndMap(templates[objectEventIdx].localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &objectEventId);
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].objectEventId = objectEventId;
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].xCoord = gObjectEvents[objectEventId].currentCoords.x - 7;
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].yCoord = gObjectEvents[objectEventId].currentCoords.y - 7;
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].graphicsId = templates[objectEventIdx].graphicsId;
|
||||
vsSeekerObjectIdx++;
|
||||
}
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].localId = 0xFF;
|
||||
}
|
||||
|
||||
static void Task_VsSeeker_ShowResponseToPlayer(u8 taskId)
|
||||
{
|
||||
if (!ScriptMovement_IsObjectMovementFinished(0xFF, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup))
|
||||
return;
|
||||
|
||||
if (sVsSeeker->responseCode == VSSEEKER_RESPONSE_NO_RESPONSE)
|
||||
{
|
||||
DisplayItemMessageOnField(taskId, VSSeeker_Text_TrainersNotReady, Task_ItemUse_CloseMessageBoxAndReturnToField_VsSeeker);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sVsSeeker->responseCode == VSSEEKER_RESPONSE_FOUND_REMATCHES)
|
||||
StartAllRespondantIdleMovements();
|
||||
|
||||
ClearDialogWindowAndFrame(0, TRUE);
|
||||
ScriptUnfreezeObjectEvents();
|
||||
UnlockPlayerFieldControls();
|
||||
DestroyTask(taskId);
|
||||
}
|
||||
Free(sVsSeeker);
|
||||
}
|
||||
|
||||
static u8 CanUseVsSeeker(void)
|
||||
{
|
||||
u8 vsSeekerChargeSteps = gSaveBlock1Ptr->trainerRematchStepCounter;
|
||||
|
||||
if ((vsSeekerChargeSteps == VSSEEKER_RECHARGE_STEPS) && (GetRematchableTrainerLocalId() == 0xFF))
|
||||
return VSSEEKER_NO_ONE_IN_RANGE;
|
||||
|
||||
if (vsSeekerChargeSteps == VSSEEKER_RECHARGE_STEPS)
|
||||
return VSSEEKER_CAN_USE;
|
||||
|
||||
ConvertIntToDecimalStringN(gStringVar1, (VSSEEKER_RECHARGE_STEPS - vsSeekerChargeSteps), STR_CONV_MODE_LEFT_ALIGN, 3);
|
||||
return VSSEEKER_NOT_CHARGED;
|
||||
}
|
||||
|
||||
static u8 GetVsSeekerResponseInArea(void)
|
||||
{
|
||||
u16 trainerIdx = 0;
|
||||
u8 response = 0, rematchTrainerIdx;
|
||||
s32 vsSeekerIdx = 0, randomValue = 0;
|
||||
|
||||
while (sVsSeeker->trainerInfo[vsSeekerIdx].localId != 0xFF)
|
||||
{
|
||||
if (!IsTrainerVisibleOnScreen(&sVsSeeker->trainerInfo[vsSeekerIdx]))
|
||||
{
|
||||
vsSeekerIdx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
trainerIdx = sVsSeeker->trainerInfo[vsSeekerIdx].trainerIdx;
|
||||
if (!HasTrainerBeenFought(trainerIdx))
|
||||
{
|
||||
StartTrainerObjectMovementScript(&sVsSeeker->trainerInfo[vsSeekerIdx], sMovementScript_TrainerUnfought);
|
||||
sVsSeeker->trainerHasNotYetBeenFought = 1;
|
||||
vsSeekerIdx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
rematchTrainerIdx = GetRematchTrainerIdFromTable(gRematchTable, trainerIdx);
|
||||
if (rematchTrainerIdx == 0)
|
||||
{
|
||||
StartTrainerObjectMovementScript(&sVsSeeker->trainerInfo[vsSeekerIdx], sMovementScript_TrainerNoRematch);
|
||||
sVsSeeker->trainerDoesNotWantRematch = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
randomValue = Random() % 100; // Even if it's overwritten below, it progresses the RNG.
|
||||
response = GetCurVsSeekerResponse(vsSeekerIdx, trainerIdx);
|
||||
|
||||
if (response == VSSEEKER_SINGLE_RESP_YES)
|
||||
{
|
||||
randomValue = 100; // Definitely yes
|
||||
}
|
||||
else if (response == VSSEEKER_SINGLE_RESP_NO)
|
||||
{
|
||||
randomValue = 0; // Definitely no
|
||||
}
|
||||
else if (randomValue < 30)
|
||||
{
|
||||
StartTrainerObjectMovementScript(&sVsSeeker->trainerInfo[vsSeekerIdx], sMovementScript_TrainerNoRematch);
|
||||
sVsSeeker->trainerDoesNotWantRematch = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
gSaveBlock1Ptr->trainerRematches[VsSeekerConvertLocalIdToTableId(sVsSeeker->trainerInfo[vsSeekerIdx].localId)] = rematchTrainerIdx;
|
||||
ShiftStillObjectEventCoords(&gObjectEvents[sVsSeeker->trainerInfo[vsSeekerIdx].objectEventId]);
|
||||
StartTrainerObjectMovementScript(&sVsSeeker->trainerInfo[vsSeekerIdx], sMovementScript_TrainerRematch);
|
||||
sVsSeeker->trainerIdxArray[sVsSeeker->numRematchableTrainers] = trainerIdx;
|
||||
sVsSeeker->runningBehaviourEtcArray[sVsSeeker->numRematchableTrainers] = GetResponseMovementTypeFromTrainerGraphicsId(sVsSeeker->trainerInfo[vsSeekerIdx].graphicsId);
|
||||
sVsSeeker->numRematchableTrainers++;
|
||||
sVsSeeker->trainerWantsRematch = 1;
|
||||
}
|
||||
}
|
||||
vsSeekerIdx++;
|
||||
}
|
||||
|
||||
if (sVsSeeker->trainerWantsRematch)
|
||||
{
|
||||
PlaySE(SE_PIN);
|
||||
FlagSet(I_VS_SEEKER_CHARGING);
|
||||
VsSeekerResetChargingStepCounter();
|
||||
return VSSEEKER_RESPONSE_FOUND_REMATCHES;
|
||||
}
|
||||
|
||||
if (sVsSeeker->trainerHasNotYetBeenFought)
|
||||
return VSSEEKER_RESPONSE_UNFOUGHT_TRAINERS;
|
||||
|
||||
return VSSEEKER_RESPONSE_NO_RESPONSE;
|
||||
}
|
||||
|
||||
void ClearRematchMovementByTrainerId(void)
|
||||
{
|
||||
s32 i;
|
||||
u8 objEventId = 0;
|
||||
struct ObjectEventTemplate *objectEventTemplates = gSaveBlock1Ptr->objectEventTemplates;
|
||||
struct ObjectEvent *objectEvent;
|
||||
|
||||
int vsSeekerDataIdx = TrainerIdToRematchTableId(gRematchTable, gTrainerBattleOpponent_A);
|
||||
|
||||
if (!I_VS_SEEKER_CHARGING) return;
|
||||
|
||||
if (vsSeekerDataIdx == -1)
|
||||
return;
|
||||
|
||||
for (i = 0; i < gMapHeader.events->objectEventCount; i++)
|
||||
{
|
||||
if ((objectEventTemplates[i].trainerType != TRAINER_TYPE_NORMAL
|
||||
&& objectEventTemplates[i].trainerType != TRAINER_TYPE_BURIED)
|
||||
|| vsSeekerDataIdx != TrainerIdToRematchTableId(gRematchTable, GetTrainerFlagFromScript(objectEventTemplates[i].script)))
|
||||
continue;
|
||||
|
||||
TryGetObjectEventIdByLocalIdAndMap(objectEventTemplates[i].localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &objEventId);
|
||||
objectEvent = &gObjectEvents[objEventId];
|
||||
GetRandomFaceDirectionMovementType(&objectEventTemplates[i]);
|
||||
TryOverrideTemplateCoordsForObjectEvent(objectEvent, sFaceDirectionMovementTypeByFacingDirection[objectEvent->facingDirection]);
|
||||
|
||||
if (gSelectedObjectEvent == objEventId)
|
||||
objectEvent->movementType = sFaceDirectionMovementTypeByFacingDirection[objectEvent->facingDirection];
|
||||
else
|
||||
objectEvent->movementType = MOVEMENT_TYPE_FACE_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 GetGameProgressFlags()
|
||||
{
|
||||
const u32 gameProgressFlags[] = {
|
||||
FLAG_VISITED_LAVARIDGE_TOWN,
|
||||
FLAG_VISITED_FORTREE_CITY,
|
||||
FLAG_SYS_GAME_CLEAR,
|
||||
FLAG_DEFEATED_METEOR_FALLS_STEVEN
|
||||
};
|
||||
u32 i = 0, numGameProgressFlags = 0;
|
||||
u32 maxGameProgressFlags = ARRAY_COUNT(gameProgressFlags);
|
||||
|
||||
for (i = 0; i < maxGameProgressFlags; i++)
|
||||
{
|
||||
if (FlagGet(gameProgressFlags[i]))
|
||||
numGameProgressFlags++;
|
||||
}
|
||||
|
||||
return numGameProgressFlags;
|
||||
}
|
||||
|
||||
u16 GetRematchTrainerIdVSSeeker(u16 trainerId)
|
||||
{
|
||||
u32 tableId = FirstBattleTrainerIdToRematchTableId(gRematchTable, trainerId);
|
||||
u32 rematchTrainerIdx = GetGameProgressFlags();
|
||||
|
||||
if (!I_VS_SEEKER_CHARGING) return 0;
|
||||
|
||||
while (!HasTrainerBeenFought(gRematchTable[tableId].trainerIds[rematchTrainerIdx-1]))
|
||||
{
|
||||
if (rematchTrainerIdx== 0)
|
||||
break;
|
||||
|
||||
rematchTrainerIdx--;
|
||||
}
|
||||
|
||||
return gRematchTable[tableId].trainerIds[rematchTrainerIdx];
|
||||
}
|
||||
|
||||
static bool8 ObjectEventIdIsSane(u8 objectEventId)
|
||||
{
|
||||
struct ObjectEvent *objectEvent = &gObjectEvents[objectEventId];
|
||||
|
||||
if (objectEvent->active && gMapHeader.events->objectEventCount >= objectEvent->localId && gSprites[objectEvent->spriteId].data[0] == objectEventId)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static u8 GetRandomFaceDirectionMovementType()
|
||||
{
|
||||
u16 randomFacingDirection = Random() % 4;
|
||||
|
||||
switch (randomFacingDirection)
|
||||
{
|
||||
case 0:
|
||||
return MOVEMENT_TYPE_FACE_UP;
|
||||
case 1:
|
||||
return MOVEMENT_TYPE_FACE_DOWN;
|
||||
case 2:
|
||||
return MOVEMENT_TYPE_FACE_LEFT;
|
||||
case 3:
|
||||
return MOVEMENT_TYPE_FACE_RIGHT;
|
||||
default:
|
||||
return MOVEMENT_TYPE_FACE_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static bool32 IsRegularLandTrainer(u8 graphicsId)
|
||||
{
|
||||
u32 i;
|
||||
u16 regularTrainersOnLand[] =
|
||||
{
|
||||
OBJ_EVENT_GFX_AQUA_MEMBER_F,
|
||||
OBJ_EVENT_GFX_AQUA_MEMBER_M,
|
||||
OBJ_EVENT_GFX_BEAUTY,
|
||||
OBJ_EVENT_GFX_BLACK_BELT,
|
||||
OBJ_EVENT_GFX_BOY_1,
|
||||
OBJ_EVENT_GFX_BOY_2,
|
||||
OBJ_EVENT_GFX_BOY_3,
|
||||
OBJ_EVENT_GFX_BUG_CATCHER,
|
||||
OBJ_EVENT_GFX_CAMPER,
|
||||
OBJ_EVENT_GFX_CYCLING_TRIATHLETE_F,
|
||||
OBJ_EVENT_GFX_CYCLING_TRIATHLETE_M,
|
||||
OBJ_EVENT_GFX_EXPERT_F,
|
||||
OBJ_EVENT_GFX_EXPERT_M,
|
||||
OBJ_EVENT_GFX_FAT_MAN,
|
||||
OBJ_EVENT_GFX_FISHERMAN,
|
||||
OBJ_EVENT_GFX_GENTLEMAN,
|
||||
OBJ_EVENT_GFX_GIRL_1,
|
||||
OBJ_EVENT_GFX_GIRL_2,
|
||||
OBJ_EVENT_GFX_GIRL_3,
|
||||
OBJ_EVENT_GFX_HEX_MANIAC,
|
||||
OBJ_EVENT_GFX_HIKER,
|
||||
OBJ_EVENT_GFX_LASS,
|
||||
OBJ_EVENT_GFX_LITTLE_BOY,
|
||||
OBJ_EVENT_GFX_LITTLE_GIRL,
|
||||
OBJ_EVENT_GFX_MAGMA_MEMBER_F,
|
||||
OBJ_EVENT_GFX_MAGMA_MEMBER_M,
|
||||
OBJ_EVENT_GFX_MAN_3,
|
||||
OBJ_EVENT_GFX_MAN_4,
|
||||
OBJ_EVENT_GFX_MAN_5,
|
||||
OBJ_EVENT_GFX_MANIAC,
|
||||
OBJ_EVENT_GFX_NINJA_BOY,
|
||||
OBJ_EVENT_GFX_PICNICKER,
|
||||
OBJ_EVENT_GFX_POKEFAN_F,
|
||||
OBJ_EVENT_GFX_POKEFAN_M,
|
||||
OBJ_EVENT_GFX_PSYCHIC_M,
|
||||
OBJ_EVENT_GFX_RICH_BOY,
|
||||
OBJ_EVENT_GFX_RUNNING_TRIATHLETE_F,
|
||||
OBJ_EVENT_GFX_RUNNING_TRIATHLETE_M,
|
||||
OBJ_EVENT_GFX_SAILOR,
|
||||
OBJ_EVENT_GFX_SCHOOL_KID_M,
|
||||
OBJ_EVENT_GFX_TUBER_F,
|
||||
OBJ_EVENT_GFX_TUBER_M,
|
||||
OBJ_EVENT_GFX_TWIN,
|
||||
OBJ_EVENT_GFX_WOMAN_1,
|
||||
OBJ_EVENT_GFX_WOMAN_2,
|
||||
OBJ_EVENT_GFX_WOMAN_4,
|
||||
OBJ_EVENT_GFX_WOMAN_5,
|
||||
OBJ_EVENT_GFX_YOUNGSTER
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(regularTrainersOnLand); i++)
|
||||
{
|
||||
if (graphicsId == regularTrainersOnLand[i])
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool32 IsRegularWaterTrainer(u8 graphicsId)
|
||||
{
|
||||
u32 i;
|
||||
u16 regularTrainersInWater[] =
|
||||
{
|
||||
OBJ_EVENT_GFX_SWIMMER_F,
|
||||
OBJ_EVENT_GFX_SWIMMER_M,
|
||||
OBJ_EVENT_GFX_TUBER_M_SWIMMING
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(regularTrainersInWater); i++)
|
||||
{
|
||||
if (graphicsId == regularTrainersInWater[i])
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static u8 GetResponseMovementTypeFromTrainerGraphicsId(u8 graphicsId)
|
||||
{
|
||||
if (IsRegularLandTrainer(graphicsId) || IsRegularWaterTrainer(graphicsId))
|
||||
return MOVEMENT_TYPE_ROTATE_CLOCKWISE;
|
||||
|
||||
return MOVEMENT_TYPE_FACE_DOWN;
|
||||
}
|
||||
|
||||
static u16 GetTrainerFlagFromScript(const u8 *script)
|
||||
/*
|
||||
* The trainer flag is a little-endian short located +2 from
|
||||
* the script pointer, assuming the trainerbattle command is
|
||||
* first in the script. Because scripts are unaligned, and
|
||||
* because the ARM processor requires shorts to be 16-bit
|
||||
* aligned, this function needs to perform explicit bitwise
|
||||
* operations to get the correct flag.
|
||||
*
|
||||
* 5c XX YY ZZ ...
|
||||
* -- --
|
||||
*/
|
||||
{
|
||||
u16 trainerFlag;
|
||||
|
||||
script += 2;
|
||||
trainerFlag = script[0];
|
||||
trainerFlag |= script[1] << 8;
|
||||
return trainerFlag;
|
||||
}
|
||||
|
||||
static void ClearAllTrainerRematchStates(void)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
if (!CheckBagHasItem(ITEM_VS_SEEKER, 1))
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(gSaveBlock1Ptr->trainerRematches); i++)
|
||||
gSaveBlock1Ptr->trainerRematches[i] = 0;
|
||||
}
|
||||
|
||||
static bool8 IsTrainerVisibleOnScreen(struct VsSeekerTrainerInfo * trainerInfo)
|
||||
{
|
||||
s16 x;
|
||||
s16 y;
|
||||
|
||||
PlayerGetDestCoords(&x, &y);
|
||||
x -= 7;
|
||||
y -= 7;
|
||||
|
||||
if ( x - 7 <= trainerInfo->xCoord
|
||||
&& x + 7 >= trainerInfo->xCoord
|
||||
&& y - 5 <= trainerInfo->yCoord
|
||||
&& y + 5 >= trainerInfo->yCoord
|
||||
&& ObjectEventIdIsSane(trainerInfo->objectEventId) == 1)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static u32 GetRematchableTrainerLocalId(void)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; sVsSeeker->trainerInfo[i].localId != 0xFF; i++)
|
||||
{
|
||||
if (IsTrainerVisibleOnScreen(&sVsSeeker->trainerInfo[i]) == 1)
|
||||
{
|
||||
if (HasTrainerBeenFought(sVsSeeker->trainerInfo[i].trainerIdx) != 1 || GetRematchTrainerIdFromTable(gRematchTable, sVsSeeker->trainerInfo[i].trainerIdx))
|
||||
return sVsSeeker->trainerInfo[i].localId;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static void StartTrainerObjectMovementScript(struct VsSeekerTrainerInfo * trainerInfo, const u8 * script)
|
||||
{
|
||||
UnfreezeObjectEvent(&gObjectEvents[trainerInfo->objectEventId]);
|
||||
ScriptMovement_StartObjectMovementScript(trainerInfo->localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, script);
|
||||
}
|
||||
|
||||
static u8 GetCurVsSeekerResponse(s32 vsSeekerIdx, u16 trainerIdx)
|
||||
{
|
||||
s32 i;
|
||||
s32 j;
|
||||
|
||||
for (i = 0; i < vsSeekerIdx; i++)
|
||||
{
|
||||
if (IsTrainerVisibleOnScreen(&sVsSeeker->trainerInfo[i]) != 1 || sVsSeeker->trainerInfo[i].trainerIdx != trainerIdx)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < sVsSeeker->numRematchableTrainers; j++)
|
||||
{
|
||||
if (sVsSeeker->trainerIdxArray[j] == sVsSeeker->trainerInfo[i].trainerIdx)
|
||||
return VSSEEKER_SINGLE_RESP_YES;
|
||||
}
|
||||
return VSSEEKER_SINGLE_RESP_NO;
|
||||
}
|
||||
return VSSEEKER_SINGLE_RESP_RAND;
|
||||
}
|
||||
|
||||
static void StartAllRespondantIdleMovements(void)
|
||||
{
|
||||
s32 i;
|
||||
s32 j;
|
||||
|
||||
for (i = 0; i < sVsSeeker->numRematchableTrainers; i++)
|
||||
{
|
||||
for (j = 0; sVsSeeker->trainerInfo[j].localId != 0xFF; j++)
|
||||
{
|
||||
if (sVsSeeker->trainerInfo[j].trainerIdx == sVsSeeker->trainerIdxArray[i])
|
||||
{
|
||||
struct ObjectEvent *objectEvent = &gObjectEvents[sVsSeeker->trainerInfo[j].objectEventId];
|
||||
|
||||
if (ObjectEventIdIsSane(sVsSeeker->trainerInfo[j].objectEventId) == 1)
|
||||
SetTrainerMovementType(objectEvent, sVsSeeker->runningBehaviourEtcArray[i]);
|
||||
TryOverrideTemplateCoordsForObjectEvent(objectEvent, sVsSeeker->runningBehaviourEtcArray[i]);
|
||||
gSaveBlock1Ptr->trainerRematches[VsSeekerConvertLocalIdToTableId(sVsSeeker->trainerInfo[j].localId)] = GetRematchTrainerIdFromTable(gRematchTable, sVsSeeker->trainerInfo[j].trainerIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue