- 14
- Posts
- 6
- Years
- Seen Apr 28, 2025
In case it's useful to someone, here is the code for a teleport item which modifies the escape rope to teleport the player between two locations, the last heal location and a location which is dynamically set when the player uses the item. Think of it as vaguely analogous to something like the Mark and Recall spells in the Elder Scrolls:
Just replace the function of the same name in item_use.c. You will also either need to create a new "FLAG_WARP_DEVICE" or just use any other unused flag. Thanks Anon882 for the help.
Original post below.
________________________________________
I am trying to make an item which will warp the player between two locations. I am modifying the escape rope code to do this.
If used in the wilderness, it should teleport the player back to the central hub city. This bit I have done fine.
Then, when used in the city, I would like it to warp the player back to wherever they were in the wilderness.
I know how to alternate between the two warp destinations easily enough, first by setting and clearing a flag (in item_use.c):
And then checking this flag when the escape rope is choosing which location to warp to (field_effect.c, line 18 below):
I know what I need to do is to insert a line of code somewhere to save the player's map and coordinate data, then set this as the escape rope's warp destination (depending on flag status). But I am lost on how to do that. Usually I try to think of some occasion in the base game where something similar to what I want to achieve happens already, and use that code as a starting point. But this has stumped me.
Anyone able to help me with this? Or able to suggest an easier alternative?
Code:
static void EscapeRopeWarpOutEffect_Spin(struct Task *task)
{
struct ObjectEvent *objectEvent;
bool8 warpedYet = FlagGet(FLAG_WARP_DEVICE);
u8 spinDirections[5] = {DIR_SOUTH, DIR_WEST, DIR_EAST, DIR_NORTH, DIR_SOUTH};
if (task->tTimer != 0 && (--task->tTimer) == 0)
{
TryFadeOutOldMapMusic();
WarpFadeOutScreen();
}
objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if (!ObjectEventIsMovementOverridden(objectEvent) || ObjectEventClearHeldMovementIfFinished(objectEvent))
{
if (task->tTimer == 0 && !gPaletteFade.active && BGMusicStopped() == TRUE)
{
SetObjectEventDirection(objectEvent, task->tStartDir);
if (!warpedYet)
{
FlagSet(FLAG_WARP_DEVICE);
SetEscapeWarp(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, WARP_ID_NONE, gSaveBlock1Ptr->pos.x, gSaveBlock1Ptr->pos.y);
SetWarpDestinationToLastHealLocation();
}
else
{
FlagClear(FLAG_WARP_DEVICE);
SetWarpDestinationToEscapeWarp();
}
WarpIntoMap();
gFieldCallback = FieldCallback_EscapeRopeWarpIn;
SetMainCallback2(CB2_LoadMap);
DestroyTask(FindTaskIdByFunc(Task_EscapeRopeWarpOut));
}
else if (task->tSpinDelay == 0 || (--task->tSpinDelay) == 0)
{
ObjectEventSetHeldMovement(objectEvent, GetFaceDirectionMovementAction(spinDirections[objectEvent->facingDirection]));
if (gSaveBlock2Ptr->follower.inProgress)
{
ObjectEventClearHeldMovement(&gObjectEvents[gSaveBlock2Ptr->follower.objId]);
switch(objectEvent->facingDirection)
{
case DIR_SOUTH:
gSprites[gObjectEvents[gSaveBlock2Ptr->follower.objId].spriteId].x2 = 0;
ObjectEventSetHeldMovement(&gObjectEvents[gSaveBlock2Ptr->follower.objId], 25);
break;
case DIR_NORTH:
gSprites[gObjectEvents[gSaveBlock2Ptr->follower.objId].spriteId].x2 = 0;
ObjectEventSetHeldMovement(&gObjectEvents[gSaveBlock2Ptr->follower.objId], 26);
break;
case DIR_WEST:
gSprites[gObjectEvents[gSaveBlock2Ptr->follower.objId].spriteId].x2 = 8;
ObjectEventSetHeldMovement(&gObjectEvents[gSaveBlock2Ptr->follower.objId], 27);
break;
case DIR_EAST:
gSprites[gObjectEvents[gSaveBlock2Ptr->follower.objId].spriteId].x2 = -8;
ObjectEventSetHeldMovement(&gObjectEvents[gSaveBlock2Ptr->follower.objId], 28);
break;
}
}
if (task->tNumTurns < 12)
task->tNumTurns++;
task->tSpinDelay = 8 >> (task->tNumTurns >> 2);
}
}
}
Just replace the function of the same name in item_use.c. You will also either need to create a new "FLAG_WARP_DEVICE" or just use any other unused flag. Thanks Anon882 for the help.
Original post below.
________________________________________
I am trying to make an item which will warp the player between two locations. I am modifying the escape rope code to do this.
If used in the wilderness, it should teleport the player back to the central hub city. This bit I have done fine.
Then, when used in the city, I would like it to warp the player back to wherever they were in the wilderness.
I know how to alternate between the two warp destinations easily enough, first by setting and clearing a flag (in item_use.c):
Code:
void ItemUseOutOfBattle_EscapeRope(u8 taskId)
{
bool8 warpedYet = FlagGet(FLAG_WARP_DEVICE);
if (!warpedYet)
{
FlagSet (FLAG_WARP_DEVICE);
sItemUseOnFieldCB = ItemUseOnFieldCB_EscapeRope;
SetUpItemUseOnFieldCallback(taskId);
}
else
{
FlagClear(FLAG_WARP_DEVICE);
sItemUseOnFieldCB = ItemUseOnFieldCB_EscapeRope;
SetUpItemUseOnFieldCallback(taskId);
}
}
And then checking this flag when the escape rope is choosing which location to warp to (field_effect.c, line 18 below):
Code:
static void EscapeRopeWarpOutEffect_Spin(struct Task *task)
{
struct ObjectEvent *objectEvent;
+ bool8 warpedYet = FlagGet(FLAG_WARP_DEVICE);
u8 spinDirections[5] = {DIR_SOUTH, DIR_WEST, DIR_EAST, DIR_NORTH, DIR_SOUTH};
if (task->tTimer != 0 && (--task->tTimer) == 0)
{
TryFadeOutOldMapMusic();
WarpFadeOutScreen();
}
objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if (!ObjectEventIsMovementOverridden(objectEvent) || ObjectEventClearHeldMovementIfFinished(objectEvent))
{
if (task->tTimer == 0 && !gPaletteFade.active && BGMusicStopped() == TRUE)
{
SetObjectEventDirection(objectEvent, task->tStartDir);
SetWarpDestinationToLastHealLocation();
+ if (!warpedYet)
+ {
+ SetWarpDestinationToLastHealLocation();
+ }
WarpIntoMap();
gFieldCallback = FieldCallback_EscapeRopeWarpIn;
SetMainCallback2(CB2_LoadMap);
DestroyTask(FindTaskIdByFunc(Task_EscapeRopeWarpOut));
}
else if (task->tSpinDelay == 0 || (--task->tSpinDelay) == 0)
{
ObjectEventSetHeldMovement(objectEvent, GetFaceDirectionMovementAction(spinDirections[objectEvent->facingDirection]));
if (gSaveBlock2Ptr->follower.inProgress)
{
ObjectEventClearHeldMovement(&gObjectEvents[gSaveBlock2Ptr->follower.objId]);
switch(objectEvent->facingDirection)
{
case DIR_SOUTH:
gSprites[gObjectEvents[gSaveBlock2Ptr->follower.objId].spriteId].x2 = 0;
ObjectEventSetHeldMovement(&gObjectEvents[gSaveBlock2Ptr->follower.objId], 25);
break;
case DIR_NORTH:
gSprites[gObjectEvents[gSaveBlock2Ptr->follower.objId].spriteId].x2 = 0;
ObjectEventSetHeldMovement(&gObjectEvents[gSaveBlock2Ptr->follower.objId], 26);
break;
case DIR_WEST:
gSprites[gObjectEvents[gSaveBlock2Ptr->follower.objId].spriteId].x2 = 8;
ObjectEventSetHeldMovement(&gObjectEvents[gSaveBlock2Ptr->follower.objId], 27);
break;
case DIR_EAST:
gSprites[gObjectEvents[gSaveBlock2Ptr->follower.objId].spriteId].x2 = -8;
ObjectEventSetHeldMovement(&gObjectEvents[gSaveBlock2Ptr->follower.objId], 28);
break;
}
}
if (task->tNumTurns < 12)
task->tNumTurns++;
task->tSpinDelay = 8 >> (task->tNumTurns >> 2);
}
}
}
I know what I need to do is to insert a line of code somewhere to save the player's map and coordinate data, then set this as the escape rope's warp destination (depending on flag status). But I am lost on how to do that. Usually I try to think of some occasion in the base game where something similar to what I want to achieve happens already, and use that code as a starting point. But this has stumped me.
Anyone able to help me with this? Or able to suggest an easier alternative?
Last edited: