Table of Contents
The Bug
A common bug that appears in binary rom hacks is where the free-moving camera (created with the SpawnCameraObject
special script call) causes ripples in the water or rustling in the grass. The vanilla game gets around this bug in the engine by making sure to only use the free-moving camera in places where no ground effects spawn, such as the roiling waters of the ocean while the legendaries fight in Sootopolis (note how the camera doesn't actually shift back the same way after the clear waters return after the fighting).
Fixed | Not Fixed |
---|---|
The Solution
It turns out, this is a simple fix in src/event_object_movement.c
: we simply need to add a check for if event objects are invisible. Because it turns out that all SpawnCameraObject
does is spawns a random youngster event object and make it invisible and attach the camera to that invisible object. This will also cover cases where the player is invisible on the map for whatever reason.
static void DoGroundEffects_OnSpawn(struct ObjectEvent *objEvent, struct Sprite *sprite)
{
u32 flags;
- if (objEvent->triggerGroundEffectsOnMove)
+ if (objEvent->triggerGroundEffectsOnMove && !objEvent->invisible)
{
flags = 0;
UpdateObjectEventZCoordAndPriority(objEvent, sprite);
GetAllGroundEffectFlags_OnSpawn(objEvent, &flags);
SetObjectEventSpriteOamTableForLongGrass(objEvent, sprite);
DoFlaggedGroundEffects(objEvent, sprite, flags);
objEvent->triggerGroundEffectsOnMove = 0;
objEvent->disableCoveringGroundEffects = 0;
}
}
static void DoGroundEffects_OnBeginStep(struct ObjectEvent *objEvent, struct Sprite *sprite)
{
u32 flags;
- if (objEvent->triggerGroundEffectsOnMove)
+ if (objEvent->triggerGroundEffectsOnMove && !objEvent->invisible)
{
flags = 0;
UpdateObjectEventZCoordAndPriority(objEvent, sprite);
GetAllGroundEffectFlags_OnBeginStep(objEvent, &flags);
SetObjectEventSpriteOamTableForLongGrass(objEvent, sprite);
filters_out_some_ground_effects(objEvent, &flags);
DoFlaggedGroundEffects(objEvent, sprite, flags);
objEvent->triggerGroundEffectsOnMove = 0;
objEvent->disableCoveringGroundEffects = 0;
}
}
static void DoGroundEffects_OnFinishStep(struct ObjectEvent *objEvent, struct Sprite *sprite)
{
u32 flags;
- if (objEvent->triggerGroundEffectsOnStop)
+ if (objEvent->triggerGroundEffectsOnStop && !objEvent->invisible)
{
flags = 0;
UpdateObjectEventZCoordAndPriority(objEvent, sprite);
GetAllGroundEffectFlags_OnFinishStep(objEvent, &flags);
SetObjectEventSpriteOamTableForLongGrass(objEvent, sprite);
FilterOutStepOnPuddleGroundEffectIfJumping(objEvent, &flags);
DoFlaggedGroundEffects(objEvent, sprite, flags);
objEvent->triggerGroundEffectsOnStop = 0;
objEvent->landingJump = 0;
}
}
Alternate Solution
If for some reason you still want other invisible objects to still do ground effects (maybe there's someone with a Cloak of Invisibility walking around, and that use of the ground effects is something you want to indicate where they are), instead of the above, you can check the localId of the object walking around. The camera spawned by SpawnCameraObject
always has the localId OBJ_EVENT_ID_CAMERA
, so we can check for that instead:
- if (objEvent->triggerGroundEffectsOnStop)
+ if (objEvent->triggerGroundEffectsOnStop && objEvent->localId != OBJ_EVENT_ID_CAMERA)