diff options
Diffstat (limited to 'src/trainer_see.c')
-rw-r--r-- | src/trainer_see.c | 751 |
1 files changed, 751 insertions, 0 deletions
diff --git a/src/trainer_see.c b/src/trainer_see.c new file mode 100644 index 000000000..3dbfbdf38 --- /dev/null +++ b/src/trainer_see.c @@ -0,0 +1,751 @@ +#include "global.h" +#include "battle_setup.h" +#include "event_object_movement.h" +#include "field_effect.h" +#include "field_player_avatar.h" +#include "quest_log.h" +#include "script.h" +#include "task.h" +#include "util.h" +#include "constants/battle_setup.h" +#include "constants/event_object_movement.h" +#include "constants/event_objects.h" + +typedef u8 (*TrainerApproachFunc)(struct ObjectEvent *, s16, s16, s16); +typedef bool8 (*TrainerSeeFunc)(u8, struct Task *, struct ObjectEvent *); + +/*static*/ bool8 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); +/*static*/ u8 GetTrainerApproachDistanceWest(struct ObjectEvent * trainerObj, s16 range, s16 x, s16 y); +/*static*/ u8 GetTrainerApproachDistanceEast(struct ObjectEvent * trainerObj, s16 range, s16 x, s16 y); +/*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*/ void Task_DestroyTrainerApproachTask(u8 taskId); +/*static*/ void SpriteCB_TrainerIcons(struct Sprite * sprite); +/*static*/ void SetIconSpriteData(struct Sprite *sprite, u16 fldEffId, u8 spriteAnimNum); + +/*static*/ const u16 sGfx_Emoticons[] = INCBIN_U16("graphics/object_events/emoticons.4bpp"); + +// u8 func(struct ObjectEvent * trainerObj, s16 range, s16 x, s16 y) +// range is the maximum distance the trainer can see +// x and y are the player's coordinates +// Returns distance to walk if trainer has unobstructed view of player +// Returns 0 if trainer can't see player +/*static*/ const TrainerApproachFunc sDirectionalApproachDistanceFuncs[] = { + GetTrainerApproachDistanceSouth, + GetTrainerApproachDistanceNorth, + GetTrainerApproachDistanceWest, + GetTrainerApproachDistanceEast +}; + +// 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 +}; + +/*static*/ const TrainerSeeFunc sTrainerSeeFuncList2[] = { + TrainerSeeFunc_TrainerInAshFacesPlayer, + TrainerSeeFunc_BeginJumpOutOfAsh, + TrainerSeeFunc_WaitJumpOutOfAsh, + TrainerSeeFunc_EndJumpOutOfAsh +}; + +bool8 CheckForTrainersWantingBattle(void) +{ + u8 i; + if (sub_8111C2C() == TRUE) + return FALSE; + + for (i = 0; i < OBJECT_EVENTS_COUNT; i++) + { + if (gObjectEvents[i].active + && ( + gObjectEvents[i].trainerType == 1 + || gObjectEvents[i].trainerType == 3 + ) + && CheckTrainer(i) + ) + return TRUE; + } + return FALSE; +} + +/*static*/ bool8 CheckTrainer(u8 trainerObjId) +{ + const u8 *script = GetObjectEventScriptPointerByObjectEventId(trainerObjId); + u8 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); + TrainerApproachPlayer(&gObjectEvents[trainerObjId], approachDistance - 1); + return TRUE; + } + return FALSE; +} + +/*static*/ u8 GetTrainerApproachDistance(struct ObjectEvent *trainerObj) +{ + s16 x, y; + u8 i; + u8 approachDistance; + + PlayerGetDestCoords(&x, &y); + if (trainerObj->trainerType == 1) // can only see in one direction + { + approachDistance = sDirectionalApproachDistanceFuncs[trainerObj->facingDirection - 1](trainerObj, trainerObj->trainerRange_berryTreeId, x, y); + return CheckPathBetweenTrainerAndPlayer(trainerObj, approachDistance, trainerObj->facingDirection); + } + else // can see in all directions + { + for (i = 0; i < 4; i++) + { + approachDistance = sDirectionalApproachDistanceFuncs[i](trainerObj, trainerObj->trainerRange_berryTreeId, x, y); + if (CheckPathBetweenTrainerAndPlayer(trainerObj, approachDistance, i + 1)) // directions are 1-4 instead of 0-3. south north west east + return approachDistance; + } + } + + return 0; +} + +// Returns how far south the player is from trainer. 0 if out of trainer's sight. +/*static*/ u8 GetTrainerApproachDistanceSouth(struct ObjectEvent *trainerObj, s16 range, s16 x, s16 y) +{ + if (trainerObj->currentCoords.x == x + && y > trainerObj->currentCoords.y + && y <= trainerObj->currentCoords.y + range) + { + if (range > 3 && GetFirstInactiveObjectEventId() == OBJECT_EVENTS_COUNT) + return 0; + return (y - trainerObj->currentCoords.y); + } + else + return 0; +} + +// Returns how far north the player is from trainer. 0 if out of trainer's sight. +/*static*/ u8 GetTrainerApproachDistanceNorth(struct ObjectEvent *trainerObj, s16 range, s16 x, s16 y) +{ + if (trainerObj->currentCoords.x == x + && y < trainerObj->currentCoords.y + && y >= trainerObj->currentCoords.y - range) + return (trainerObj->currentCoords.y - y); + else + return 0; +} + +// Returns how far west the player is from trainer. 0 if out of trainer's sight. +/*static*/ u8 GetTrainerApproachDistanceWest(struct ObjectEvent *trainerObj, s16 range, s16 x, s16 y) +{ + if (trainerObj->currentCoords.y == y + && x < trainerObj->currentCoords.x + && x >= trainerObj->currentCoords.x - range) + return (trainerObj->currentCoords.x - x); + else + return 0; +} + +// Returns how far east the player is from trainer. 0 if out of trainer's sight. +/*static*/ u8 GetTrainerApproachDistanceEast(struct ObjectEvent *trainerObj, s16 range, s16 x, s16 y) +{ + if (trainerObj->currentCoords.y == y + && x > trainerObj->currentCoords.x + && x <= trainerObj->currentCoords.x + range) + return (x - trainerObj->currentCoords.x); + else + return 0; +} + +#define COLLISION_MASK (~1) + +/*static*/ u8 CheckPathBetweenTrainerAndPlayer(struct ObjectEvent *trainerObj, u8 approachDistance, u8 direction) +{ + s16 x, y; + u8 unk19_temp; + u8 unk19b_temp; + u8 i; + u8 collision; + + if (approachDistance == 0) + return 0; + + x = trainerObj->currentCoords.x; + y = trainerObj->currentCoords.y; + + for (i = 0; i <= approachDistance - 1; i++, MoveCoords(direction, &x, &y)) + { + collision = GetCollisionFlagsAtCoords(trainerObj, x, y, direction); + if (collision != 0 && (collision & COLLISION_MASK)) + return 0; + } + + // preserve mapobj_unk_19 before clearing. + unk19_temp = trainerObj->range.as_nybbles.x; + unk19b_temp = trainerObj->range.as_nybbles.y; + trainerObj->range.as_nybbles.x = 0; + trainerObj->range.as_nybbles.y = 0; + + collision = GetCollisionAtCoords(trainerObj, x, y, direction); + + trainerObj->range.as_nybbles.x = unk19_temp; + trainerObj->range.as_nybbles.y = unk19b_temp; + if (collision == 4) + return approachDistance; + + return 0; +} + +#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) + +/*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; + task->tTrainerRange = approachDistance; +} + +/*static*/ void StartTrainerApproachWithFollowupTask(TaskFunc taskFunc) +{ + u8 taskId = FindTaskIdByFunc(Task_RunTrainerSeeFuncList); + SetTaskFuncWithFollowupFunc(taskId, Task_RunTrainerSeeFuncList, taskFunc); + gTasks[taskId].tFuncId = 1; + Task_RunTrainerSeeFuncList(taskId); +} + +/*static*/ void Task_RunTrainerSeeFuncList(u8 taskId) +{ + struct Task * task = &gTasks[taskId]; + struct ObjectEvent * trainerObj; + TaskGetTrainerObj(trainerObj, task); + + if (!trainerObj->active) + { + SwitchTaskToFollowupFunc(taskId); + } + else + { + while (sTrainerSeeFuncList[task->tFuncId](taskId, task, trainerObj)) + ; + } +} + +// TrainerSeeFuncs + +/*static*/ bool8 TrainerSeeFunc_Dummy(u8 taskId, struct Task * task, struct ObjectEvent * trainerObj) +{ + return FALSE; +} + +/*static*/ bool8 TrainerSeeFunc_StartExclMark(u8 taskId, struct Task * task, struct ObjectEvent * trainerObj) +{ + u8 action; + // FRLG introduces trainers who can see the player from offscreen above. + // Handle this case here. + if (trainerObj->facingDirection == DIR_SOUTH && task->tTrainerRange > 2) + { + task->tFuncId = 12; + } + else + { + ObjectEventGetLocalIdAndMap(trainerObj, (u8 *)&gFieldEffectArguments[0], (u8 *)&gFieldEffectArguments[1], (u8 *)&gFieldEffectArguments[2]); + FieldEffectStart(FLDEFF_EXCLAMATION_MARK_ICON); + action = GetFaceDirectionMovementAction(trainerObj->facingDirection); + ObjectEventSetHeldMovement(trainerObj, action); + task->tFuncId++; + } + return TRUE; +} + +/*static*/ bool8 TrainerSeeFunc_WaitExclMark(u8 taskId, struct Task * task, struct ObjectEvent * trainerObj) +{ + if (FieldEffectActiveListContains(FLDEFF_EXCLAMATION_MARK_ICON)) + { + return FALSE; + } + else + { + task->tFuncId++; + if (trainerObj->movementType == MOVEMENT_TYPE_TREE_DISGUISE || trainerObj->movementType == MOVEMENT_TYPE_MOUNTAIN_DISGUISE) + task->tFuncId = 6; + if (trainerObj->movementType == MOVEMENT_TYPE_HIDDEN) + task->tFuncId = 8; + return TRUE; + } +} + +/*static*/ bool8 TrainerSeeFunc_TrainerApproach(u8 taskId, struct Task * task, struct ObjectEvent * trainerObj) +{ + if (!ObjectEventIsMovementOverridden(trainerObj) || ObjectEventClearHeldMovementIfFinished(trainerObj)) + { + if (task->tTrainerRange) + { + ObjectEventSetHeldMovement(trainerObj, GetWalkNormalMovementAction(trainerObj->facingDirection)); + task->tTrainerRange--; + } + else + { + ObjectEventSetHeldMovement(trainerObj, MOVEMENT_ACTION_FACE_PLAYER); + task->tFuncId++; + } + } + return FALSE; +} + +/*static*/ bool8 TrainerSeeFunc_PrepareToEngage(u8 taskId, struct Task * task, struct ObjectEvent * trainerObj) +{ + struct ObjectEvent *playerObj; + + if (ObjectEventIsMovementOverridden(trainerObj) && !ObjectEventClearHeldMovementIfFinished(trainerObj)) + return FALSE; + + SetTrainerMovementType(trainerObj, GetTrainerFacingDirectionMovementType(trainerObj->facingDirection)); + OverrideMovementTypeForObjectEvent(trainerObj, GetTrainerFacingDirectionMovementType(trainerObj->facingDirection)); + OverrideTemplateCoordsForObjectEvent(trainerObj); + + playerObj = &gObjectEvents[gPlayerAvatar.objectEventId]; + if (ObjectEventIsMovementOverridden(playerObj) && !ObjectEventClearHeldMovementIfFinished(playerObj)) + return FALSE; + + sub_805C774(); + // Uncomment to have player turn to face their opponent + // ObjectEventSetHeldMovement(&gObjectEvents[gPlayerAvatar.objectEventId], GetFaceDirectionMovementAction(GetOppositeDirection(trainerObj->facingDirection))); + task->tFuncId++; + return FALSE; +} + +/*static*/ bool8 TrainerSeeFunc_End(u8 taskId, struct Task * task, struct ObjectEvent * trainerObj) +{ + struct ObjectEvent *playerObj = &gObjectEvents[gPlayerAvatar.objectEventId]; + + if (!ObjectEventIsMovementOverridden(playerObj) + || ObjectEventClearHeldMovementIfFinished(playerObj)) + SwitchTaskToFollowupFunc(taskId); // This ends the trainer walk routine. + return FALSE; +} + +// Jumps here if disguised. Not used in FRLG. +/*static*/ bool8 TrainerSeeFunc_BeginRemoveDisguise(u8 taskId, struct Task * task, struct ObjectEvent * trainerObj) +{ + if (!ObjectEventIsMovementOverridden(trainerObj) + || ObjectEventClearHeldMovementIfFinished(trainerObj)) + { + ObjectEventSetHeldMovement(trainerObj, MOVEMENT_ACTION_REVEAL_TRAINER); + task->tFuncId++; + } + return FALSE; +} + +/*static*/ bool8 TrainerSeeFunc_WaitRemoveDisguise(u8 taskId, struct Task * task, struct ObjectEvent * trainerObj) +{ + if (ObjectEventClearHeldMovementIfFinished(trainerObj)) + task->tFuncId = 3; + + return FALSE; +} + +// Jump here if hidden in ash. Not used in FRLG. +/*static*/ bool8 TrainerSeeFunc_TrainerInAshFacesPlayer(u8 taskId, struct Task * task, struct ObjectEvent * trainerObj) +{ + if (!ObjectEventIsMovementOverridden(trainerObj) + || ObjectEventClearHeldMovementIfFinished(trainerObj)) + { + ObjectEventSetHeldMovement(trainerObj, MOVEMENT_ACTION_FACE_PLAYER); + task->tFuncId++; + } + return FALSE; +} + +/*static*/ bool8 TrainerSeeFunc_BeginJumpOutOfAsh(u8 taskId, struct Task * task, struct ObjectEvent * trainerObj) +{ + if (ObjectEventCheckHeldMovementStatus(trainerObj)) + { + gFieldEffectArguments[0] = trainerObj->currentCoords.x; + gFieldEffectArguments[1] = trainerObj->currentCoords.y; + gFieldEffectArguments[2] = gSprites[trainerObj->spriteId].subpriority - 1; + gFieldEffectArguments[3] = 2; + task->tOutOfAshSpriteId = FieldEffectStart(FLDEFF_POP_OUT_OF_ASH); + task->tFuncId++; + } + return FALSE; +} + +/*static*/ bool8 TrainerSeeFunc_WaitJumpOutOfAsh(u8 taskId, struct Task * task, struct ObjectEvent * trainerObj) +{ + struct Sprite *sprite; + + if (gSprites[task->tOutOfAshSpriteId].animCmdIndex == 2) + { + trainerObj->fixedPriority = FALSE; + trainerObj->triggerGroundEffectsOnMove = TRUE; + + sprite = &gSprites[trainerObj->spriteId]; + sprite->oam.priority = 2; + ObjectEventClearHeldMovementIfFinished(trainerObj); + ObjectEventSetHeldMovement(trainerObj, GetJumpInPlaceMovementAction(trainerObj->facingDirection)); + task->tFuncId++; + } + + return FALSE; +} + +/*static*/ bool8 TrainerSeeFunc_EndJumpOutOfAsh(u8 taskId, struct Task *task, struct ObjectEvent *trainerObj) +{ + if (!FieldEffectActiveListContains(FLDEFF_POP_OUT_OF_ASH)) + task->tFuncId = 3; + + return FALSE; +} + +// FRLG exclusive: Scroll the camera up to reveal an offscreen above trainer +/*static*/ bool8 TrainerSeeFunc_OffscreenAboveTrainerCreateCameraObj(u8 taskId, struct Task *task, struct ObjectEvent *trainerObj) +{ + int 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; + CameraObjectSetFollowedObjectId(gObjectEvents[specialObjectId].spriteId); + task->tFuncId++; + return FALSE; +} + +/*static*/ bool8 TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveUp(u8 taskId, struct Task *task, struct ObjectEvent *trainerObj) +{ + u8 specialObjectId; + TryGetObjectEventIdByLocalIdAndMap(OBJ_EVENT_ID_CAMERA, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &specialObjectId); + + if (ObjectEventIsMovementOverridden(&gObjectEvents[specialObjectId]) && !ObjectEventClearHeldMovementIfFinished(&gObjectEvents[specialObjectId])) + return FALSE; + + if (task->tData5 != task->tTrainerRange - 1) + { + ObjectEventSetHeldMovement(&gObjectEvents[specialObjectId], GetWalkFastMovementAction(DIR_NORTH)); + task->tData5++; + } + else + { + ObjectEventGetLocalIdAndMap(trainerObj, (u8 *)&gFieldEffectArguments[0], (u8 *)&gFieldEffectArguments[1], (u8 *)&gFieldEffectArguments[2]); + FieldEffectStart(FLDEFF_EXCLAMATION_MARK_ICON); + task->tData5 = 0; + task->tFuncId++; + } + return FALSE; +} + +/*static*/ bool8 TrainerSeeFunc_OffscreenAboveTrainerCameraObjMoveDown(u8 taskId, struct Task * task, struct ObjectEvent * trainerObj) +{ + u8 specialObjectId; + TryGetObjectEventIdByLocalIdAndMap(OBJ_EVENT_ID_CAMERA, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &specialObjectId); + + if (FieldEffectActiveListContains(FLDEFF_EXCLAMATION_MARK_ICON)) + return FALSE; + + if (ObjectEventIsMovementOverridden(&gObjectEvents[specialObjectId]) && !ObjectEventClearHeldMovementIfFinished(&gObjectEvents[specialObjectId])) + return FALSE; + + if (task->tData5 != task->tTrainerRange - 1) + { + ObjectEventSetHeldMovement(&gObjectEvents[specialObjectId], GetWalkFastMovementAction(DIR_SOUTH)); + task->tData5++; + } + else + { + CameraObjectSetFollowedObjectId(GetPlayerAvatarObjectId()); + RemoveObjectEventByLocalIdAndMap(OBJ_EVENT_ID_CAMERA, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); + task->tData5 = 0; + task->tFuncId = 2; + } + return FALSE; +} + +#undef tData5 +#undef tOutOfAshSpriteId +#undef tTrainerRange +#undef tTrainerObjLo +#undef tTrainerObjHi +#undef tFuncId + +/*static*/ void Task_RevealTrainer_RunTrainerSeeFuncList(u8 taskId) +{ + struct Task * task = &gTasks[taskId]; + struct ObjectEvent * trainerObj; + + // another objEvent loaded into by loadword? + LoadWordFromTwoHalfwords((u16 *)&task->data[1], (uintptr_t *)&trainerObj); + if (!task->data[7]) + { + ObjectEventClearHeldMovement(trainerObj); + task->data[7]++; + } + sTrainerSeeFuncList2[task->data[0]](taskId, task, trainerObj); + if (task->data[0] == 3 && !FieldEffectActiveListContains(FLDEFF_POP_OUT_OF_ASH)) + { + SetTrainerMovementType(trainerObj, GetTrainerFacingDirectionMovementType(trainerObj->facingDirection)); + OverrideMovementTypeForObjectEvent(trainerObj, GetTrainerFacingDirectionMovementType(trainerObj->facingDirection)); + DestroyTask(taskId); + } + else + { + trainerObj->heldMovementFinished = FALSE; + } +} + +void MovementAction_RevealTrainer_RunTrainerSeeFuncList(struct ObjectEvent *var) +{ + StoreWordInTwoHalfwords((u16 *)&gTasks[CreateTask(Task_RevealTrainer_RunTrainerSeeFuncList, 0)].data[1], (u32)var); +} + +void EndTrainerApproach(void) +{ + StartTrainerApproachWithFollowupTask(Task_DestroyTrainerApproachTask); +} + +/*static*/ void Task_DestroyTrainerApproachTask(u8 taskId) +{ + DestroyTask(taskId); + EnableBothScriptContexts(); +} + +// Trainer See Excl Mark Field Effect + +#define sLocalId data[0] +#define sMapNum data[1] +#define sMapGroup data[2] +#define sData3 data[3] +#define sData4 data[4] +#define sFldEffId data[7] + +/*static*/ const struct OamData sOamData_Emoticons = { + .y = 0, + .affineMode = ST_OAM_AFFINE_OFF, + .objMode = ST_OAM_OBJ_NORMAL, + .mosaic = 0, + .bpp = ST_OAM_4BPP, + .shape = SPRITE_SHAPE(16x16), + .x = 0, + .matrixNum = 0, + .size = SPRITE_SIZE(16x16), + .tileNum = 0, + .priority = 1, + .paletteNum = 0, + .affineParam = 0, +}; + +/*static*/ const struct SpriteFrameImage sSpriteImages_Emoticons[] = { + {sGfx_Emoticons + 0x000, 0x80}, + {sGfx_Emoticons + 0x040, 0x80}, + {sGfx_Emoticons + 0x080, 0x80}, + + {sGfx_Emoticons + 0x180, 0x80}, + {sGfx_Emoticons + 0x1C0, 0x80}, + {sGfx_Emoticons + 0x200, 0x80}, + + {sGfx_Emoticons + 0x0C0, 0x80}, + {sGfx_Emoticons + 0x100, 0x80}, + {sGfx_Emoticons + 0x140, 0x80}, + + {sGfx_Emoticons + 0x240, 0x80}, + {sGfx_Emoticons + 0x280, 0x80}, + {sGfx_Emoticons + 0x2C0, 0x80}, + + {sGfx_Emoticons + 0x300, 0x80}, + {sGfx_Emoticons + 0x340, 0x80}, + {sGfx_Emoticons + 0x380, 0x80}, +}; + +/*static*/ const union AnimCmd sAnimCmd_ExclamationMark1[] = { + ANIMCMD_FRAME( 0, 4), + ANIMCMD_FRAME( 1, 4), + ANIMCMD_FRAME( 2, 52), + ANIMCMD_END +}; + +/*static*/ const union AnimCmd sAnimCmd_DoubleExclMark[] = { + ANIMCMD_FRAME( 6, 4), + ANIMCMD_FRAME( 7, 4), + ANIMCMD_FRAME( 8, 52), + ANIMCMD_END +}; + +/*static*/ const union AnimCmd sAnimCmd_X[] = { + ANIMCMD_FRAME( 3, 4), + ANIMCMD_FRAME( 4, 4), + ANIMCMD_FRAME( 5, 52), + ANIMCMD_END +}; + +/*static*/ const union AnimCmd sAnimCmd_SmileyFace[] = { + ANIMCMD_FRAME( 9, 4), + ANIMCMD_FRAME(10, 4), + ANIMCMD_FRAME(11, 52), + ANIMCMD_END +}; + +/*static*/ const union AnimCmd sAnimCmd_QuestionMark[] = { + ANIMCMD_FRAME(12, 4), + ANIMCMD_FRAME(13, 4), + ANIMCMD_FRAME(14, 52), + ANIMCMD_END +}; + +/*static*/ const union AnimCmd *const sSpriteAnimTable_Emoticons[] = { + sAnimCmd_ExclamationMark1, + sAnimCmd_DoubleExclMark, + sAnimCmd_X, + sAnimCmd_SmileyFace, + sAnimCmd_QuestionMark +}; + +/*static*/ const struct SpriteTemplate sSpriteTemplate_Emoticons = { + .tileTag = 0xFFFF, + .paletteTag = 0xFFFF, + .oam = &sOamData_Emoticons, + .anims = sSpriteAnimTable_Emoticons, + .images = sSpriteImages_Emoticons, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCB_TrainerIcons +}; + +u8 FldEff_ExclamationMarkIcon1(void) +{ + u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_Emoticons, 0, 0, 0x53); + + if (spriteId != MAX_SPRITES) + SetIconSpriteData(&gSprites[spriteId], FLDEFF_EXCLAMATION_MARK_ICON, 0); + + return 0; +} + +u8 FldEff_DoubleExclMarkIcon(void) +{ + u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_Emoticons, 0, 0, 0x52); + + if (spriteId != MAX_SPRITES) + SetIconSpriteData(&gSprites[spriteId], FLDEFF_DOUBLE_EXCL_MARK_ICON, 1); + + return 0; +} + +u8 FldEff_XIcon(void) +{ + u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_Emoticons, 0, 0, 0x52); + + if (spriteId != MAX_SPRITES) + SetIconSpriteData(&gSprites[spriteId], FLDEFF_X_ICON, 2); + + return 0; +} + +u8 FldEff_SmileyFaceIcon(void) +{ + u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_Emoticons, 0, 0, 0x52); + + if (spriteId != MAX_SPRITES) + SetIconSpriteData(&gSprites[spriteId], FLDEFF_SMILEY_FACE_ICON, 3); + + return 0; +} + +u8 FldEff_QuestionMarkIcon(void) +{ + u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_Emoticons, 0, 0, 0x52); + + if (spriteId != MAX_SPRITES) + SetIconSpriteData(&gSprites[spriteId], FLDEFF_QUESTION_MARK_ICON, 4); + + return 0; +} + +/*static*/ void SetIconSpriteData(struct Sprite *sprite, u16 fldEffId, u8 spriteAnimNum) +{ + sprite->oam.priority = 1; + sprite->coordOffsetEnabled = 1; + + sprite->sLocalId = gFieldEffectArguments[0]; + sprite->sMapNum = gFieldEffectArguments[1]; + sprite->sMapGroup = gFieldEffectArguments[2]; + sprite->sData3 = -5; + sprite->sFldEffId = fldEffId; + + StartSpriteAnim(sprite, spriteAnimNum); +} + +/*static*/ void SpriteCB_TrainerIcons(struct Sprite *sprite) +{ + u8 objEventId; + + if (TryGetObjectEventIdByLocalIdAndMap(sprite->sLocalId, sprite->sMapNum, sprite->sMapGroup, &objEventId) + || sprite->animEnded) + { + FieldEffectStop(sprite, sprite->sFldEffId); + } + else + { + struct Sprite *objEventSprite = &gSprites[gObjectEvents[objEventId].spriteId]; + sprite->sData4 += sprite->sData3; + sprite->pos1.x = objEventSprite->pos1.x; + sprite->pos1.y = objEventSprite->pos1.y - 16; + sprite->pos2.x = objEventSprite->pos2.x; + sprite->pos2.y = objEventSprite->pos2.y + sprite->sData4; + if (sprite->sData4) + sprite->sData3++; + else + sprite->sData3 = 0; + } +} + +#undef sLocalId +#undef sMapNum +#undef sMapGroup +#undef sData3 +#undef sData4 +#undef sFldEffId |