New Feature: ORAS Dowsing (#7211)

This commit is contained in:
Bivurnum 2025-10-27 11:41:07 -05:00 committed by GitHub
parent a32da780c4
commit 6ddabde582
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 721 additions and 12 deletions

View File

@ -2313,6 +2313,11 @@
callnative ScriptSetDoubleBattleFlag, requests_effects=1
.endm
@ Stop using the ORAS dowsing machine.
.macro stoporasdowsing
callnative EndORASDowsing
.endm
@ ============================ @
@ FAKE RTC MACROS
@ Will only function if OW_USE_FAKE_RTC is true. If it has any additional requirements, it will be listed accordingly.

View File

@ -83,6 +83,7 @@ gFieldEffectScriptPointers::
.4byte gFieldEffectScript_Defog @ FLDEFF_DEFOG
.4byte gFieldEffectScript_UseRockClimb @ FLDEFF_USE_ROCK_CLIMB
.4byte gFieldEffectScript_RockClimbDust @ FLDEFF_ROCK_CLIMB_DUST
.4byte gFieldEffectScript_ORASDowse @ FLDEFF_ORAS_DOWSE
gFieldEffectScript_ExclamationMarkIcon1::
field_eff_callnative FldEff_ExclamationMarkIcon
@ -386,6 +387,7 @@ gFieldEffectScript_CaveDust::
gFieldEffectScript_Defog::
field_eff_callnative FldEff_Defog
field_eff_end
gFieldEffectScript_UseRockClimb:: @ 82DBC3F
field_eff_callnative FldEff_UseRockClimb
field_eff_end
@ -394,3 +396,7 @@ gFieldEffectScript_RockClimbDust:: @ 82DBB28
field_eff_loadfadedpal_callnative gSpritePalette_BigDust, FldEff_RockClimbDust
field_eff_end
gFieldEffectScript_ORASDowse::
field_eff_callnative FldEff_ORASDowsing
field_eff_end

View File

@ -205,6 +205,7 @@ EventScript_FoundHiddenItem::
end
EventScript_PutHiddenItemInPocket::
callnative Script_ClearDowsingColor
delay 10
showitemdescription
waitmessage
@ -215,6 +216,7 @@ EventScript_PutHiddenItemInPocket::
hideitemdescription
special TryPutTreasureInvestigatorsOnAir
special SetHiddenItemFlag
callnative Script_UpdateDowseState
releaseall
end

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
115 197 164
0 0 0
255 255 255
255 255 255
255 255 255
255 255 255
255 255 255
255 255 255
255 255 255
255 255 255
255 255 255
255 255 255
255 255 255
255 255 255
255 255 255
255 0 255

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

View File

@ -38,4 +38,15 @@
// Vs. Seeker
#define I_VS_SEEKER_CHARGING 0 // If this flag is assigned, the Vs Seeker functionality will be enabled. When the player has the Vs. Seeker, Match Call rematch functions will stop working.
// ORAS Dowsing Machine
#define I_ORAS_DOWSING_FLAG 0 // Replace 0 with an unused flag to enable the Dowsing Machine mechanic from ORAS.
#define I_ORAS_DOWSING_SOUNDS TRUE // If TRUE, the Dowsing Machine will make sounds based on how far away the hidden item is.
#define I_ORAS_DOWSING_COLOR_PAL 15 // The color within the palette that will change based on proximity to the item.
// Color values for the ORAS dowsing distances/anims
#define I_ORAS_DOWSING_COLOR_NONE RGB_GRAY
#define I_ORAS_DOWSING_COLOR_SLOW RGB2GBA(56, 120, 255)
#define I_ORAS_DOWSING_COLOR_NORMAL RGB2GBA(24, 216, 24)
#define I_ORAS_DOWSING_COLOR_FAST RGB2GBA(255, 255, 40)
#define I_ORAS_DOWSING_COLOR_FASTER RGB_RED
#endif // GUARD_CONFIG_ITEM_H

View File

@ -79,6 +79,7 @@
#define FLDEFF_DEFOG 74
#define FLDEFF_USE_ROCK_CLIMB 75
#define FLDEFF_ROCK_CLIMB_DUST 76
#define FLDEFF_ORAS_DOWSE 77
#define FLDEFFOBJ_SHADOW_S 0
#define FLDEFFOBJ_SHADOW_M 1
@ -123,6 +124,8 @@
#define FLDEFFOBJ_CAVE_DUST 40
#define FLDEFFOBJ_ROCK_CLIMB_BLOB 41
#define FLDEFFOBJ_ROCK_CLIMB_DUST 42
#define FLDEFFOBJ_ORAS_DOWSE_BRENDAN 43
#define FLDEFFOBJ_ORAS_DOWSE_MAY 44
#define FLDEFF_PAL_TAG_CUT_GRASS 0x1000
#define FLDEFF_PAL_TAG_SECRET_POWER_TREE 0x1003
@ -138,6 +141,7 @@
#define FLDEFF_PAL_TAG_UNKNOWN 0x1011
#define FLDEFF_PAL_TAG_CAVE_DUST 0x1012
#define FLDEFF_PAL_TAG_DUST_CLOUD 0x1013
#define FLDEFF_PAL_TAG_ORAS_DOWSE 0x1014
#define FLDEFF_PAL_TAG_FIELD_MOVE_MON 0x8400
// tile tags, for field effects that may have many copies on screen at once

View File

@ -49,6 +49,8 @@ void Task_ItemUse_CloseMessageBoxAndReturnToField_VsSeeker(u8 taskId);
void DisplayDadsAdviceCannotUseItemMessage(u8 taskId, bool8 isUsingRegisteredKeyItemOnField);
void ItemUseOutOfBattle_PokeFlute(u8 taskId);
void ItemUseOutOfBattle_TownMap(u8 taskId);
bool8 ItemfinderCheckForHiddenItems(const struct MapEvents *, u8);
u8 GetDirectionToHiddenItem(s16, s16);
enum {
BALL_THROW_UNABLE_TWO_MONS,

39
include/oras_dowse.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef GUARD_ORAS_DOWSE_H
#define GUARD_ORAS_DOWSE_H
// States for ORAS Dowsing
enum
{
ORASD_WIGGLE_NONE,
ORASD_WIGGLE_SLOW,
ORASD_WIGGLE_NORMAL,
ORASD_WIGGLE_FAST,
ORASD_WIGGLE_FASTER
};
#define ANIM_ORAS_DOWSE_WIGGLE_SOUTH_SLOW (ANIM_STD_FACE_EAST + 1)
#define ANIM_ORAS_DOWSE_WIGGLE_NORTH_SLOW (ANIM_STD_FACE_EAST + 2)
#define ANIM_ORAS_DOWSE_WIGGLE_WEST_SLOW (ANIM_STD_FACE_EAST + 3)
#define ANIM_ORAS_DOWSE_WIGGLE_EAST_SLOW (ANIM_STD_FACE_EAST + 4)
#define ANIM_ORAS_DOWSE_WIGGLE_SOUTH (ANIM_STD_FACE_EAST + 5)
#define ANIM_ORAS_DOWSE_WIGGLE_NORTH (ANIM_STD_FACE_EAST + 6)
#define ANIM_ORAS_DOWSE_WIGGLE_WEST (ANIM_STD_FACE_EAST + 7)
#define ANIM_ORAS_DOWSE_WIGGLE_EAST (ANIM_STD_FACE_EAST + 8)
#define ANIM_ORAS_DOWSE_WIGGLE_SOUTH_FAST (ANIM_STD_FACE_EAST + 9)
#define ANIM_ORAS_DOWSE_WIGGLE_NORTH_FAST (ANIM_STD_FACE_EAST + 10)
#define ANIM_ORAS_DOWSE_WIGGLE_WEST_FAST (ANIM_STD_FACE_EAST + 11)
#define ANIM_ORAS_DOWSE_WIGGLE_EAST_FAST (ANIM_STD_FACE_EAST + 12)
#define ANIM_ORAS_DOWSE_WIGGLE_SOUTH_FASTER (ANIM_STD_FACE_EAST + 13)
#define ANIM_ORAS_DOWSE_WIGGLE_NORTH_FASTER (ANIM_STD_FACE_EAST + 14)
#define ANIM_ORAS_DOWSE_WIGGLE_WEST_FASTER (ANIM_STD_FACE_EAST + 15)
#define ANIM_ORAS_DOWSE_WIGGLE_EAST_FASTER (ANIM_STD_FACE_EAST + 16)
extern const u16 gFieldEffectPal_ORASDowsing[];
void Task_UseORASDowsingMachine(u8 taskId);
void ResumeORASDowseFieldEffect(void);
void UpdateDowseState(struct Sprite *sprite);
void UpdateDowsingAnimDirection(struct Sprite *sprite, struct ObjectEvent *playerObj);
void EndORASDowsing(void);
#endif // GUARD_ORAS_DOWSE_H

View File

@ -693,6 +693,12 @@ $(FLDEFFGFXDIR)/surf_blob.4bpp: %.4bpp: %.png
$(FLDEFFGFXDIR)/rock_climb_blob.4bpp: %.4bpp: %.png
$(GFX) $< $@ -mwidth 4 -mheight 4
$(FLDEFFGFXDIR)/oras_dowsing_brendan.4bpp: %.4bpp: %.png
$(GFX) $< $@ -mwidth 2 -mheight 4
$(FLDEFFGFXDIR)/oras_dowsing_may.4bpp: %.4bpp: %.png
$(GFX) $< $@ -mwidth 2 -mheight 4
$(FLDEFFGFXDIR)/tall_grass.4bpp: %.4bpp: %.png
$(GFX) $< $@ -mwidth 2 -mheight 2

View File

@ -5,6 +5,7 @@
#include "fieldmap.h"
#include "field_specials.h"
#include "metatile_behavior.h"
#include "oras_dowse.h"
#include "overworld.h"
#include "sound.h"
#include "constants/songs.h"
@ -1003,6 +1004,7 @@ void GetOnOffBike(u8 transitionFlags)
}
else
{
EndORASDowsing();
SetPlayerAvatarTransitionFlags(transitionFlags);
Overworld_SetSavedMusic(MUS_CYCLING);
Overworld_ChangeMusicTo(MUS_CYCLING);

View File

@ -42,6 +42,8 @@ extern const struct SpriteTemplate gFieldEffectObjectTemplate_SpotTracks;
extern const struct SpriteTemplate gFieldEffectObjectTemplate_CaveDust;
extern const struct SpriteTemplate gFieldEffectObjectTemplate_RockClimbBlob;
extern const struct SpriteTemplate gFieldEffectObjectTemplate_RockClimbDust;
extern const struct SpriteTemplate gFieldEffectObjectTemplate_ORASDowsingBrendan;
extern const struct SpriteTemplate gFieldEffectObjectTemplate_ORASDowsingMay;
const struct SpriteTemplate *const gFieldEffectObjectTemplatePointers[] = {
[FLDEFFOBJ_SHADOW_S] = &gFieldEffectObjectTemplate_ShadowSmall,
@ -87,4 +89,6 @@ const struct SpriteTemplate *const gFieldEffectObjectTemplatePointers[] = {
[FLDEFFOBJ_CAVE_DUST] = &gFieldEffectObjectTemplate_CaveDust,
[FLDEFFOBJ_ROCK_CLIMB_BLOB] = &gFieldEffectObjectTemplate_RockClimbBlob,
[FLDEFFOBJ_ROCK_CLIMB_DUST] = &gFieldEffectObjectTemplate_RockClimbDust,
[FLDEFFOBJ_ORAS_DOWSE_BRENDAN] = &gFieldEffectObjectTemplate_ORASDowsingBrendan,
[FLDEFFOBJ_ORAS_DOWSE_MAY] = &gFieldEffectObjectTemplate_ORASDowsingMay,
};

View File

@ -19,6 +19,7 @@
#include "mirage_tower.h"
#include "menu.h"
#include "metatile_behavior.h"
#include "oras_dowse.h"
#include "overworld.h"
#include "palette.h"
#include "party_menu.h"
@ -1679,6 +1680,7 @@ void StartEscalatorWarp(u8 metatileBehavior, u8 priority)
{
gTasks[taskId].tGoingUp = TRUE;
}
EndORASDowsing();
}
static void Task_EscalatorWarpOut(u8 taskId)
@ -2065,6 +2067,7 @@ static bool8 DiveFieldEffect_TryWarp(struct Task *task)
void StartLavaridgeGymB1FWarp(u8 priority)
{
EndORASDowsing();
CreateTask(Task_LavaridgeGymB1FWarp, priority);
}
@ -2273,6 +2276,7 @@ void SpriteCB_AshLaunch(struct Sprite *sprite)
void StartLavaridgeGym1FWarp(u8 priority)
{
EndORASDowsing();
CreateTask(Task_LavaridgeGym1FWarp, priority);
}
@ -2395,6 +2399,7 @@ void StartEscapeRopeFieldEffect(void)
LockPlayerFieldControls();
FreezeObjectEvents();
HideFollowerForFieldEffect(); // hide follower before warping
EndORASDowsing();
CreateTask(Task_EscapeRopeWarpOut, 80);
}
@ -2589,6 +2594,7 @@ static void TeleportWarpOutFieldEffect_Init(struct Task *task)
LockPlayerFieldControls();
FreezeObjectEvents();
CameraObjectFreeze();
EndORASDowsing();
task->data[15] = GetPlayerFacingDirection();
task->tState++;
}

View File

@ -1903,4 +1903,3 @@ static void UpdateGrassFieldEffectSubpriority(struct Sprite *sprite, u8 elevatio
}
}
}

View File

@ -12,6 +12,7 @@
#include "follower_npc.h"
#include "menu.h"
#include "metatile_behavior.h"
#include "oras_dowse.h"
#include "overworld.h"
#include "party_menu.h"
#include "random.h"
@ -363,6 +364,8 @@ void PlayerStep(u8 direction, u16 newKeys, u16 heldKeys)
}
}
#define sCounter data[3]
static bool8 TryInterruptObjectEventSpecialAnim(struct ObjectEvent *playerObjEvent, u8 direction)
{
if (ObjectEventIsMovementOverridden(playerObjEvent)
@ -371,6 +374,8 @@ static bool8 TryInterruptObjectEventSpecialAnim(struct ObjectEvent *playerObjEve
u8 heldMovementActionId = ObjectEventGetHeldMovementActionId(playerObjEvent);
if (heldMovementActionId > MOVEMENT_ACTION_WALK_FAST_RIGHT && heldMovementActionId < MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_DOWN)
{
struct ObjectEvent *playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
if (direction == DIR_NONE)
{
return TRUE;
@ -378,12 +383,21 @@ static bool8 TryInterruptObjectEventSpecialAnim(struct ObjectEvent *playerObjEve
if (playerObjEvent->movementDirection != direction)
{
if (I_ORAS_DOWSING_FLAG != 0 && FlagGet(I_ORAS_DOWSING_FLAG))
gSprites[playerObj->fieldEffectSpriteId].sCounter = 0;
ObjectEventClearHeldMovement(playerObjEvent);
return FALSE;
}
if (CheckForPlayerAvatarStaticCollision(direction) == COLLISION_NONE)
{
if (I_ORAS_DOWSING_FLAG != 0 && FlagGet(I_ORAS_DOWSING_FLAG))
{
gSprites[playerObj->fieldEffectSpriteId].sCounter = 0;
gSprites[playerObj->fieldEffectSpriteId].y2 = 0;
}
ObjectEventClearHeldMovement(playerObjEvent);
return FALSE;
}
@ -395,6 +409,8 @@ static bool8 TryInterruptObjectEventSpecialAnim(struct ObjectEvent *playerObjEve
return FALSE;
}
#undef sCounter
static void npc_clear_strange_bits(struct ObjectEvent *objEvent)
{
objEvent->inanimate = FALSE;
@ -824,8 +840,12 @@ static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys)
return;
}
if (!(gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_UNDERWATER) && (heldKeys & B_BUTTON) && FlagGet(FLAG_SYS_B_DASH)
&& IsRunningDisallowed(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior) == 0 && !FollowerNPCComingThroughDoor())
if (!(gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_UNDERWATER)
&& (heldKeys & B_BUTTON)
&& FlagGet(FLAG_SYS_B_DASH)
&& IsRunningDisallowed(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior) == 0
&& !FollowerNPCComingThroughDoor()
&& (I_ORAS_DOWSING_FLAG == 0 || (I_ORAS_DOWSING_FLAG != 0 && !FlagGet(I_ORAS_DOWSING_FLAG))))
{
if (ObjectMovingOnRockStairs(&gObjectEvents[gPlayerAvatar.objectEventId], direction))
PlayerRunSlow(direction);
@ -1626,12 +1646,14 @@ void SetPlayerInvisibility(bool8 invisible)
void SetPlayerAvatarFieldMove(void)
{
EndORASDowsing();
ObjectEventSetGraphicsId(&gObjectEvents[gPlayerAvatar.objectEventId], GetPlayerAvatarGraphicsIdByStateId(PLAYER_AVATAR_STATE_FIELD_MOVE));
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], ANIM_FIELD_MOVE);
}
void SetPlayerAvatarFishing(u8 direction)
{
EndORASDowsing();
ObjectEventSetGraphicsId(&gObjectEvents[gPlayerAvatar.objectEventId], GetPlayerAvatarGraphicsIdByStateId(PLAYER_AVATAR_STATE_FISHING));
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], GetFishingDirectionAnimNum(direction));
}
@ -1645,6 +1667,7 @@ void PlayerUseAcroBikeOnBumpySlope(u8 direction)
void SetPlayerAvatarWatering(u8 direction)
{
EndORASDowsing();
ObjectEventSetGraphicsId(&gObjectEvents[gPlayerAvatar.objectEventId], GetPlayerAvatarGraphicsIdByStateId(PLAYER_AVATAR_STATE_WATERING));
StartSpriteAnim(&gSprites[gPlayerAvatar.spriteId], GetFaceDirectionAnimNum(direction));
}

View File

@ -24,6 +24,7 @@
#include "mirage_tower.h"
#include "metatile_behavior.h"
#include "palette.h"
#include "oras_dowse.h"
#include "overworld.h"
#include "scanline_effect.h"
#include "script.h"
@ -684,6 +685,7 @@ void Task_WarpAndLoadMap(u8 taskId)
case 0:
FreezeObjectEvents();
LockPlayerFieldControls();
EndORASDowsing();
task->tState++;
break;
case 1:
@ -745,6 +747,7 @@ void Task_DoDoorWarp(u8 taskId)
ObjectEventSetHeldMovement(followerObject, MOVEMENT_ACTION_ENTER_POKEBALL);
}
task->tDoorTask = FieldAnimateDoorOpen(*x, *y - 1);
EndORASDowsing();
task->tState = DOORWARP_START_WALK_UP;
break;
case DOORWARP_START_WALK_UP:

View File

@ -29,6 +29,7 @@
#include "menu.h"
#include "menu_helpers.h"
#include "metatile_behavior.h"
#include "oras_dowse.h"
#include "overworld.h"
#include "palette.h"
#include "party_menu.h"
@ -55,8 +56,6 @@ static void Task_UseItemfinder(u8);
static void Task_CloseItemfinderMessage(u8);
static void Task_HiddenItemNearby(u8);
static void Task_StandingOnHiddenItem(u8);
static bool8 ItemfinderCheckForHiddenItems(const struct MapEvents *, u8);
static u8 GetDirectionToHiddenItem(s16, s16);
static void PlayerFaceHiddenItem(u8);
static void CheckForHiddenItemsInMapConnection(u8);
static void Task_OpenRegisteredPokeblockCase(u8);
@ -368,10 +367,20 @@ void ItemUseOutOfBattle_Itemfinder(u8 var)
static void ItemUseOnFieldCB_Itemfinder(u8 taskId)
{
if (ItemfinderCheckForHiddenItems(gMapHeader.events, taskId) == TRUE)
gTasks[taskId].func = Task_UseItemfinder;
if (I_ORAS_DOWSING_FLAG != 0)
{
if (!TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING) && !TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_UNDERWATER))
gTasks[taskId].func = Task_UseORASDowsingMachine;
else
DisplayItemMessageOnField(taskId, gText_DadsAdvice, Task_CloseItemfinderMessage);
}
else
DisplayItemMessageOnField(taskId, sText_ItemFinderNothing, Task_CloseItemfinderMessage);
{
if (ItemfinderCheckForHiddenItems(gMapHeader.events, taskId) == TRUE)
gTasks[taskId].func = Task_UseItemfinder;
else
DisplayItemMessageOnField(taskId, sText_ItemFinderNothing, Task_CloseItemfinderMessage);
}
}
// Define itemfinder task data
@ -427,12 +436,15 @@ static void Task_CloseItemfinderMessage(u8 taskId)
DestroyTask(taskId);
}
static bool8 ItemfinderCheckForHiddenItems(const struct MapEvents *events, u8 taskId)
bool8 ItemfinderCheckForHiddenItems(const struct MapEvents *events, u8 taskId)
{
int itemX, itemY;
s16 playerX, playerY, i, distanceX, distanceY;
PlayerGetDestCoords(&playerX, &playerY);
gTasks[taskId].tItemFound = FALSE;
if (I_ORAS_DOWSING_FLAG != 0)
gSprites[gObjectEvents[gPlayerAvatar.objectEventId].fieldEffectSpriteId].tItemFound = FALSE;
else
gTasks[taskId].tItemFound = FALSE;
for (i = 0; i < events->bgEventCount; i++)
{
@ -452,7 +464,7 @@ static bool8 ItemfinderCheckForHiddenItems(const struct MapEvents *events, u8 ta
}
CheckForHiddenItemsInMapConnection(taskId);
if (gTasks[taskId].tItemFound == TRUE)
if (gTasks[taskId].tItemFound == TRUE || gSprites[gObjectEvents[gPlayerAvatar.objectEventId].fieldEffectSpriteId].tItemFound)
return TRUE;
else
return FALSE;
@ -551,6 +563,8 @@ static void SetDistanceOfClosestHiddenItem(u8 taskId, s16 itemDistanceX, s16 ite
{
s16 *data = gTasks[taskId].data;
s16 oldItemAbsX, oldItemAbsY, newItemAbsX, newItemAbsY;
if (I_ORAS_DOWSING_FLAG != 0)
data = gSprites[gObjectEvents[gPlayerAvatar.objectEventId].fieldEffectSpriteId].data;
if (tItemFound == FALSE)
{
@ -607,7 +621,7 @@ static void SetDistanceOfClosestHiddenItem(u8 taskId, s16 itemDistanceX, s16 ite
}
}
static u8 GetDirectionToHiddenItem(s16 itemDistanceX, s16 itemDistanceY)
u8 GetDirectionToHiddenItem(s16 itemDistanceX, s16 itemDistanceY)
{
s16 absX, absY;

562
src/oras_dowse.c Normal file
View File

@ -0,0 +1,562 @@
#include "global.h"
#include "oras_dowse.h"
#include "bike.h"
#include "event_data.h"
#include "event_object_lock.h"
#include "event_object_movement.h"
#include "field_effect.h"
#include "field_effect_helpers.h"
#include "field_player_avatar.h"
#include "fldeff.h"
#include "item_use.h"
#include "palette.h"
#include "script.h"
#include "sound.h"
#include "task.h"
#include "constants/field_effects.h"
#include "constants/songs.h"
#include "constants/rgb.h"
static void StartORASDowseFieldEffect(void);
static void UpdateORASDowsingFieldEffect(struct Sprite *sprite);
static void ChangeDowsingColor(u8 direction, struct Sprite *sprite);
static void ClearDowsingColor(struct Sprite *sprite);
static void PlayDowseSound(u32 dowseState);
const u32 gFieldEffectObjectPic_ORASDowsingBrendan[] = INCBIN_U32("graphics/field_effects/pics/oras_dowsing_brendan.4bpp");
const u32 gFieldEffectObjectPic_ORASDowsingMay[] = INCBIN_U32("graphics/field_effects/pics/oras_dowsing_may.4bpp");
const u16 gFieldEffectPal_ORASDowsing[] = INCBIN_U16("graphics/field_effects/palettes/oras_dowsing.gbapal");
static const struct SpriteFrameImage sPicTable_ORASDowsingBrendan[] = {
overworld_ascending_frames(gFieldEffectObjectPic_ORASDowsingBrendan, 2, 4),
};
static const struct SpriteFrameImage sPicTable_ORASDowsingMay[] = {
overworld_ascending_frames(gFieldEffectObjectPic_ORASDowsingMay, 2, 4),
};
static const union AnimCmd sAnim_FaceSouth[] =
{
ANIMCMD_FRAME(0, 16),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_FaceNorth[] =
{
ANIMCMD_FRAME(1, 16),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_FaceWest[] =
{
ANIMCMD_FRAME(2, 16),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_FaceEast[] =
{
ANIMCMD_FRAME(2, 16, .hFlip = TRUE),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleSouthSlow[] =
{
ANIMCMD_FRAME(0, 32),
ANIMCMD_FRAME(0, 32),
ANIMCMD_FRAME(4, 32),
ANIMCMD_FRAME(4, 32),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleNorthSlow[] =
{
ANIMCMD_FRAME(1, 32),
ANIMCMD_FRAME(1, 32),
ANIMCMD_FRAME(6, 32),
ANIMCMD_FRAME(6, 32),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleWestSlow[] =
{
ANIMCMD_FRAME(2, 32),
ANIMCMD_FRAME(2, 32),
ANIMCMD_FRAME(8, 32),
ANIMCMD_FRAME(8, 32),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleEastSlow[] =
{
ANIMCMD_FRAME(2, 32, .hFlip = TRUE),
ANIMCMD_FRAME(2, 32, .hFlip = TRUE),
ANIMCMD_FRAME(8, 32, .hFlip = TRUE),
ANIMCMD_FRAME(8, 32, .hFlip = TRUE),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleSouth[] =
{
ANIMCMD_FRAME(0, 32),
ANIMCMD_FRAME(3, 32),
ANIMCMD_FRAME(0, 32),
ANIMCMD_FRAME(4, 32),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleNorth[] =
{
ANIMCMD_FRAME(1, 32),
ANIMCMD_FRAME(5, 32),
ANIMCMD_FRAME(1, 32),
ANIMCMD_FRAME(6, 32),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleWest[] =
{
ANIMCMD_FRAME(2, 32),
ANIMCMD_FRAME(7, 32),
ANIMCMD_FRAME(2, 32),
ANIMCMD_FRAME(8, 32),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleEast[] =
{
ANIMCMD_FRAME(2, 32, .hFlip = TRUE),
ANIMCMD_FRAME(7, 32, .hFlip = TRUE),
ANIMCMD_FRAME(2, 32, .hFlip = TRUE),
ANIMCMD_FRAME(8, 32, .hFlip = TRUE),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleSouthFast[] =
{
ANIMCMD_FRAME(0, 16),
ANIMCMD_FRAME(3, 16),
ANIMCMD_FRAME(0, 16),
ANIMCMD_FRAME(4, 16),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleNorthFast[] =
{
ANIMCMD_FRAME(1, 16),
ANIMCMD_FRAME(5, 16),
ANIMCMD_FRAME(1, 16),
ANIMCMD_FRAME(6, 16),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleWestFast[] =
{
ANIMCMD_FRAME(2, 16),
ANIMCMD_FRAME(7, 16),
ANIMCMD_FRAME(2, 16),
ANIMCMD_FRAME(8, 16),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleEastFast[] =
{
ANIMCMD_FRAME(2, 16, .hFlip = TRUE),
ANIMCMD_FRAME(7, 16, .hFlip = TRUE),
ANIMCMD_FRAME(2, 16, .hFlip = TRUE),
ANIMCMD_FRAME(8, 16, .hFlip = TRUE),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleSouthFaster[] =
{
ANIMCMD_FRAME(0, 8),
ANIMCMD_FRAME(3, 8),
ANIMCMD_FRAME(0, 8),
ANIMCMD_FRAME(4, 8),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleNorthFaster[] =
{
ANIMCMD_FRAME(1, 8),
ANIMCMD_FRAME(5, 8),
ANIMCMD_FRAME(1, 8),
ANIMCMD_FRAME(6, 8),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleWestFaster[] =
{
ANIMCMD_FRAME(2, 8),
ANIMCMD_FRAME(7, 8),
ANIMCMD_FRAME(2, 8),
ANIMCMD_FRAME(8, 8),
ANIMCMD_JUMP(0),
};
static const union AnimCmd sAnim_ORASDowseWiggleEastFaster[] =
{
ANIMCMD_FRAME(2, 8, .hFlip = TRUE),
ANIMCMD_FRAME(7, 8, .hFlip = TRUE),
ANIMCMD_FRAME(2, 8, .hFlip = TRUE),
ANIMCMD_FRAME(8, 8, .hFlip = TRUE),
ANIMCMD_JUMP(0),
};
static const union AnimCmd *const sAnimTable_ORASDowsing[] =
{
[ANIM_STD_FACE_SOUTH] = sAnim_FaceSouth,
[ANIM_STD_FACE_NORTH] = sAnim_FaceNorth,
[ANIM_STD_FACE_WEST] = sAnim_FaceWest,
[ANIM_STD_FACE_EAST] = sAnim_FaceEast,
[ANIM_ORAS_DOWSE_WIGGLE_SOUTH_SLOW] = sAnim_ORASDowseWiggleSouthSlow,
[ANIM_ORAS_DOWSE_WIGGLE_NORTH_SLOW] = sAnim_ORASDowseWiggleNorthSlow,
[ANIM_ORAS_DOWSE_WIGGLE_WEST_SLOW] = sAnim_ORASDowseWiggleWestSlow,
[ANIM_ORAS_DOWSE_WIGGLE_EAST_SLOW] = sAnim_ORASDowseWiggleEastSlow,
[ANIM_ORAS_DOWSE_WIGGLE_SOUTH] = sAnim_ORASDowseWiggleSouth,
[ANIM_ORAS_DOWSE_WIGGLE_NORTH] = sAnim_ORASDowseWiggleNorth,
[ANIM_ORAS_DOWSE_WIGGLE_WEST] = sAnim_ORASDowseWiggleWest,
[ANIM_ORAS_DOWSE_WIGGLE_EAST] = sAnim_ORASDowseWiggleEast,
[ANIM_ORAS_DOWSE_WIGGLE_SOUTH_FAST] = sAnim_ORASDowseWiggleSouthFast,
[ANIM_ORAS_DOWSE_WIGGLE_NORTH_FAST] = sAnim_ORASDowseWiggleNorthFast,
[ANIM_ORAS_DOWSE_WIGGLE_WEST_FAST] = sAnim_ORASDowseWiggleWestFast,
[ANIM_ORAS_DOWSE_WIGGLE_EAST_FAST] = sAnim_ORASDowseWiggleEastFast,
[ANIM_ORAS_DOWSE_WIGGLE_SOUTH_FASTER] = sAnim_ORASDowseWiggleSouthFaster,
[ANIM_ORAS_DOWSE_WIGGLE_NORTH_FASTER] = sAnim_ORASDowseWiggleNorthFaster,
[ANIM_ORAS_DOWSE_WIGGLE_WEST_FASTER] = sAnim_ORASDowseWiggleWestFaster,
[ANIM_ORAS_DOWSE_WIGGLE_EAST_FASTER] = sAnim_ORASDowseWiggleEastFaster,
};
static const struct OamData gObjectEventOam_ORASDowse = {
.shape = SPRITE_SHAPE(16x32),
.size = SPRITE_SIZE(16x32),
.priority = 2
};
const struct SpriteTemplate gFieldEffectObjectTemplate_ORASDowsingBrendan = {
.tileTag = TAG_NONE,
.paletteTag = FLDEFF_PAL_TAG_ORAS_DOWSE,
.oam = &gObjectEventOam_ORASDowse,
.anims = sAnimTable_ORASDowsing,
.images = sPicTable_ORASDowsingBrendan,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = UpdateORASDowsingFieldEffect,
};
const struct SpriteTemplate gFieldEffectObjectTemplate_ORASDowsingMay = {
.tileTag = TAG_NONE,
.paletteTag = FLDEFF_PAL_TAG_ORAS_DOWSE,
.oam = &gObjectEventOam_ORASDowse,
.anims = sAnimTable_ORASDowsing,
.images = sPicTable_ORASDowsingMay,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = UpdateORASDowsingFieldEffect,
};
void Task_UseORASDowsingMachine(u8 taskId)
{
if (FlagGet(I_ORAS_DOWSING_FLAG))
{
EndORASDowsing();
}
else
{
if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE))
GetOnOffBike(0);
StartORASDowseFieldEffect();
}
ScriptUnfreezeObjectEvents();
UnlockPlayerFieldControls();
DestroyTask(taskId);
}
static void StartORASDowseFieldEffect(void)
{
struct ObjectEvent *playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
gFieldEffectArguments[0] = playerObj->currentCoords.x;
gFieldEffectArguments[1] = playerObj->currentCoords.y;
FieldEffectStart(FLDEFF_ORAS_DOWSE);
}
void ResumeORASDowseFieldEffect(void)
{
if (I_ORAS_DOWSING_FLAG != 0 && FlagGet(I_ORAS_DOWSING_FLAG))
StartORASDowseFieldEffect();
}
static const struct SpritePalette gSpritePalette_ORASDowsing = {gFieldEffectPal_ORASDowsing, FLDEFF_PAL_TAG_ORAS_DOWSE};
// Sprite data for ORAS Dowsing Machine
#define tItemDistanceX data[0]
#define tItemDistanceY data[1]
#define sItemFound data[2]
#define sCounter data[3]
#define sSoundTimer data[4]
#define sDowseState data[5]
#define sPrevDowseState data[6]
#define sMoveActive data[7]
#define fPlayerX gFieldEffectArguments[0]
#define fPlayerY gFieldEffectArguments[1]
// Create the ORAS Dowsing Machine sprite.
u32 FldEff_ORASDowsing(void)
{
struct ObjectEvent *playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
u32 spriteId;
u32 palNum;
FlagSet(I_ORAS_DOWSING_FLAG);
SetSpritePosToOffsetMapCoords((s16 *)&fPlayerX, (s16 *)&fPlayerY, 8, 0);
if (gPlayerAvatar.gender == MALE)
spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[FLDEFFOBJ_ORAS_DOWSE_BRENDAN], fPlayerX, fPlayerY, 1);
else
spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[FLDEFFOBJ_ORAS_DOWSE_MAY], fPlayerX, fPlayerY, 1);
if (spriteId != MAX_SPRITES)
{
struct Sprite *sprite = &gSprites[spriteId];
sprite->coordOffsetEnabled = TRUE;
palNum = LoadSpritePalette(&gSpritePalette_ORASDowsing);
if (palNum != 0xFF)
sprite->oam.paletteNum = palNum;
else
sprite->oam.paletteNum = LoadPlayerObjectEventPalette(gSaveBlock2Ptr->playerGender);
playerObj->fieldEffectSpriteId = spriteId;
sprite->sDowseState = ORASD_WIGGLE_NONE;
UpdateDowseState(sprite);
}
FieldEffectActiveListRemove(FLDEFF_ORAS_DOWSE);
return spriteId;
}
// Callback for ORAS Dowsing Machine sprite.
static void UpdateORASDowsingFieldEffect(struct Sprite *sprite)
{
struct ObjectEvent *playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
struct Sprite *playerSprite = &gSprites[playerObj->spriteId];
if (!FlagGet(I_ORAS_DOWSING_FLAG))
{
DestroySpriteAndFreeResources(sprite);
return;
}
sprite->x = playerSprite->x;
sprite->y = playerSprite->y;
sprite->x2 = playerSprite->x2;
sprite->y2 = playerSprite->y2;
if (playerSprite->anims[playerSprite->animNum][playerSprite->animCmdIndex].frame.imageValue > 2)
sprite->y2++;
if (playerObj->previousMovementDirection != playerObj->movementDirection)
UpdateDowsingAnimDirection(sprite, playerObj);
if (playerObj->movementActionId != MOVEMENT_ACTION_NONE)
{
if (playerObj->heldMovementFinished == FALSE)
{
if (sprite->sCounter == 0)
{
sprite->sMoveActive = TRUE;
sprite->sCounter++;
}
}
else if (playerObj->heldMovementFinished == TRUE && sprite->sMoveActive)
{
sprite->sMoveActive = FALSE;
sprite->sCounter = 0;
UpdateDowseState(sprite);
}
}
if (I_ORAS_DOWSING_SOUNDS && sprite->sDowseState == ORASD_WIGGLE_FASTER && playerObj->heldMovementFinished != FALSE)
{
if (++sprite->sSoundTimer == 70)
{
PlaySE(SE_ITEMFINDER);
sprite->sSoundTimer = 0;
}
}
sprite->oam.priority = playerSprite->oam.priority;
}
static const u8 sClockwiseDirections[] = {DIR_NORTH, DIR_EAST, DIR_SOUTH, DIR_WEST};
void UpdateDowseState(struct Sprite *sprite)
{
struct ObjectEvent *playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
sprite->tItemDistanceX = 0;
sprite->tItemDistanceY = 0;
sprite->sPrevDowseState = sprite->sDowseState;
if (ItemfinderCheckForHiddenItems(gMapHeader.events, TASK_NONE) == TRUE)
{
s8 distX = sprite->tItemDistanceX;
s8 distY = sprite->tItemDistanceY;
u8 directionToItem = CARDINAL_DIRECTION_COUNT;
u8 playerDirToItem = GetDirectionToHiddenItem(distX, distY);
if (playerDirToItem != DIR_NONE)
directionToItem = sClockwiseDirections[GetDirectionToHiddenItem(distX, distY) - 1];
if (distX < 0)
distX *= -1;
if (distY < 0)
distY *= -1;
// If the player is facing the item's direction.
if (directionToItem == playerObj->movementDirection)
{
ChangeDowsingColor(directionToItem, sprite);
}
// If x and y distances are equal, make sure item can bee seen from both facing directions.
else if (distX == distY && distX != 0)
{
if ((directionToItem == DIR_NORTH || directionToItem == DIR_SOUTH) && sprite->tItemDistanceX > 0 && playerObj->movementDirection == DIR_EAST)
ChangeDowsingColor(DIR_EAST, sprite);
else if ((directionToItem == DIR_NORTH || directionToItem == DIR_SOUTH) && sprite->tItemDistanceX < 0 && playerObj->movementDirection == DIR_WEST)
ChangeDowsingColor(DIR_WEST, sprite);
else
ClearDowsingColor(sprite);
}
else
{
ClearDowsingColor(sprite);
}
}
else
{
ClearDowsingColor(sprite);
}
UpdateDowsingAnimDirection(sprite, playerObj);
}
static void ChangeDowsingColor(u8 direction, struct Sprite *sprite)
{
s16 distance;
u16 color = I_ORAS_DOWSING_COLOR_NONE;
if (direction == DIR_NORTH || direction == DIR_SOUTH)
distance = sprite->tItemDistanceY;
else
distance = sprite->tItemDistanceX;
// Absolute value.
if (distance < 0)
distance *= -1;
switch (distance)
{
case 1:
if (sprite->tItemDistanceX == 0 || sprite->tItemDistanceY == 0)
{
color = I_ORAS_DOWSING_COLOR_FASTER;
sprite->sDowseState = ORASD_WIGGLE_FASTER;
break;
}
case 2:
color = I_ORAS_DOWSING_COLOR_FAST;
sprite->sDowseState = ORASD_WIGGLE_FAST;
break;
case 3:
case 4:
color = I_ORAS_DOWSING_COLOR_NORMAL;
sprite->sDowseState = ORASD_WIGGLE_NORMAL;
break;
case 5:
case 6:
case 7:
color = I_ORAS_DOWSING_COLOR_SLOW;
sprite->sDowseState = ORASD_WIGGLE_SLOW;
break;
}
if (I_ORAS_DOWSING_SOUNDS && sprite->sDowseState != sprite->sPrevDowseState)
{
sprite->sSoundTimer = 0;
PlayDowseSound(sprite->sDowseState);
}
FillPalette(color, (OBJ_PLTT_ID(IndexOfSpritePaletteTag(FLDEFF_PAL_TAG_ORAS_DOWSE)) + I_ORAS_DOWSING_COLOR_PAL), PLTT_SIZEOF(1));
UpdateSpritePaletteWithTime(IndexOfSpritePaletteTag(FLDEFF_PAL_TAG_ORAS_DOWSE));
}
static void ClearDowsingColor(struct Sprite *sprite)
{
sprite->sDowseState = ORASD_WIGGLE_NONE;
FillPalette(I_ORAS_DOWSING_COLOR_NONE, (OBJ_PLTT_ID(IndexOfSpritePaletteTag(FLDEFF_PAL_TAG_ORAS_DOWSE)) + I_ORAS_DOWSING_COLOR_PAL), PLTT_SIZEOF(1));
UpdateSpritePaletteWithTime(IndexOfSpritePaletteTag(FLDEFF_PAL_TAG_ORAS_DOWSE));
}
static void PlayDowseSound(u32 dowseState)
{
switch (dowseState)
{
case ORASD_WIGGLE_SLOW:
PlaySE(SE_CONTEST_ICON_CLEAR);
return;
case ORASD_WIGGLE_NORMAL:
PlaySE(SE_PIN);
return;
case ORASD_WIGGLE_FAST:
PlaySE(SE_SUCCESS);
return;
case ORASD_WIGGLE_FASTER:
PlaySE(SE_ITEMFINDER);
return;
}
}
void UpdateDowsingAnimDirection(struct Sprite *sprite, struct ObjectEvent *playerObj)
{
u32 anim = (playerObj->facingDirection - 1);
switch (sprite->sDowseState)
{
case ORASD_WIGGLE_SLOW:
anim += 4;
break;
case ORASD_WIGGLE_NORMAL:
anim += 8;
break;
case ORASD_WIGGLE_FAST:
anim += 12;
break;
case ORASD_WIGGLE_FASTER:
anim += 16;
break;
}
// Don't completely restart anim if wiggling didn't stop.
if (sprite->sPrevDowseState != ORASD_WIGGLE_NONE && sprite->sDowseState != ORASD_WIGGLE_NONE)
SetAndStartSpriteAnim(sprite, anim, sprite->animCmdIndex);
else
StartSpriteAnimIfDifferent(sprite, anim);
}
void EndORASDowsing(void)
{
if (I_ORAS_DOWSING_FLAG != 0 && FlagGet(I_ORAS_DOWSING_FLAG))
FlagClear(I_ORAS_DOWSING_FLAG);
}
void Script_ClearDowsingColor(void)
{
if (I_ORAS_DOWSING_FLAG != 0 && FlagGet(I_ORAS_DOWSING_FLAG))
{
struct Sprite *sprite = &gSprites[gObjectEvents[gPlayerAvatar.objectEventId].fieldEffectSpriteId];
ClearDowsingColor(sprite);
UpdateDowsingAnimDirection(sprite, &gObjectEvents[gPlayerAvatar.objectEventId]);
}
}
void Script_UpdateDowseState(void)
{
if (I_ORAS_DOWSING_FLAG != 0 && FlagGet(I_ORAS_DOWSING_FLAG))
UpdateDowseState(&gSprites[gObjectEvents[gPlayerAvatar.objectEventId].fieldEffectSpriteId]);
}

View File

@ -43,6 +43,7 @@
#include "mirage_tower.h"
#include "money.h"
#include "new_game.h"
#include "oras_dowse.h"
#include "palette.h"
#include "play_time.h"
#include "random.h"
@ -2240,6 +2241,7 @@ static bool32 ReturnToFieldLocal(u8 *state)
InitViewGraphics();
TryLoadTrainerHillEReaderPalette();
FollowerNPC_BindToSurfBlobOnReloadScreen();
ResumeORASDowseFieldEffect();
(*state)++;
break;
case 2: