pokecrystal-board/docs/overworld_loop.md

15 KiB
Executable File

Overworld loop (pokecrystal)

<f>Primary functions</f> are denoted in red and using indentation
<j>*[j<num>]*</j> means jumping ahead
<k>*r<num>_<ret_value>*</k> means break/return from current function with a return value
Horizontal line means end of loop
Bold denotes not documented yet
<c>This denotes a comment</c>

wMapStatus == MAPSTATUS_START:
$$wScriptRunning <= 0
$
$wMapStatus ~ wMapStatusEnd <= 0

wMapStatus == MAPSTATUS_START or wMapStatus == MAPSTATUS_ENTER:
$$RunMapSetupScript
$
$hMapEntryMethod == MAPSETUP_CONNECTION:
$~~~~~~~~$wScriptFlags2 <= $ff
$$hMapEntryMethod <= 0
$
$wMapStatus <= MAPSTATUS_HANDLE


wMapStatus == MAPSTATUS_DONE:
$~~~~$Exit overworld loop


wMapStatus == MAPSTATUS_HANDLE: the remainder of the code goes at this level

wOverworldDelay <= 2 2 is MaxOverworldDelay
wMapEventStatus == MAPEVENTS_ON:
$$Get joypad update hJoyDown, hJoyReleased, hJoyPressed
$
$Refresh pals

HandleCmdQueue runs cmds queued by callbacks of type MAPCALLBACK_CMDQUEUE that execute the writecmdqueue script. Used only for stone tables, where any boulder from that table that is on a pit tile is made to disappear.

MapEvents (wMapEventStatus == MAPEVENTS_ON):

PlayerEvents (wScriptRunning == FALSE): wScriptRunning check not to interrupt a running script command with wait/delay mode like applymovement and deactivatefacing

CheckTrainerBattle:
if seen by trainer (if any visible sprite is a trainer not yet beaten facing the player within line of sight):
$$update wSeenTrainerDistance, wSeenTrainerDirection, wSeenTrainerBank, hLastTalked
$
$load trainer data to wTempTrainer ~ wTempTrainerEnd
$~~~~$[j1(PLAYEREVENT_SEENBYTRAINER)]

CheckTileEvent:
if warp, coord event, step event, or wild encounter:
$$[j1(PLAYEREVENT_CONNECTION / PLAYEREVENT_FALL / PLAYEREVENT_WARP / PLAYEREVENT_MAPSCRIPT / PLAYEREVENT_HATCH)] step events include: special phone call, repel, poison, happiness, egg, daycare, bike
$
$may also CallScript(<coord_event_script> / Script_ReceivePhoneCall / RepelWoreOffScript / Script_MonFaintedToPoison / WildBattleScript / BugCatchingContestBattleScript)

RunMemScript:
if any script at wMapReentryScript: [j1] used for phone scripts

RunSceneScript:
if scene event (wCurMapSceneScriptCount): [j1(PLAYEREVENT_MAPSCRIPT)]

CheckTimeEvents:
if any time event: [j1] used for bug contest, daily events

OWPlayerInput:

PlayerMovement:

DoPlayerMovement:

wCurInput <= hJoyDown if BIKEFLAGS_DOWNHILL_F and hJoyDown & D_PAD == 0, instead load D_DOWN
wMovementAnimation <= movement_step_sleep
wWalkingIntoEdgeWarp <= FALSE

Tile collision checks below consist on reading the current tile wPlayerTile and comparing it to a COLL_ constant or a range of COLL_ constants.
Tile permission checks below consist on reading the permissions of the tile that the player is walking into: wTilePermissions (applies only to COLL_WALLs) and wWalkingTile (LAND_TILE, WATER_TILE, or WALL_TILE for the tile in the walking direction; WALL_TILE permission is not the same as a COLL_WALL collision).
wPlayerState == PLAYER_NORMAL or wPlayerState = PLAYER_BIKE:
$$if on ice tile and wPlayerTurningDirection != 0: wCurInput <= current direction button
$
$update wWalkingDirection, wFacingDirection, wWalkingX, wWalkingY, wWalkingTile, based on wCurInput direction
$$if whirlpool tile: r1_player_movement = PLAYERMOVEMENT_FORCE_TURN
$
$if waterfall tile: wWalkingDirection <= direction, DoStep(STEP_WALK), r1_player_movement = PLAYERMOVEMENT_CONTINUE
$$if door/staircase/cave warp tile (non ladder/carpet): wWalkingDirection <= DOWN, DoStep(STEP_WALK), r1_player_movement = PLAYERMOVEMENT_CONTINUE
$
$if directions at wWalkingDirection and wPlayerDirection are not the same (turning): DoStep(STEP_TURN), r1_player_movement = PLAYERMOVEMENT_TURN
$$if no bump (land tile permissions or NPC): DoStep(STEP_WALK / STEP_BIKE / STEP_ICE)
$$if not leaving water: r1_player_movement = PLAYERMOVEMENT_FINISH
$
$if leaving water: wPlayerState <= PLAYER_NORMAL, reload music and sprites, and r1_player_movement = PLAYERMOVEMENT_EXIT_WATER
$
$if ledge tile: play sfx, DoStep(STEP_LEDGE), and r1_player_movement = PLAYERMOVEMENT_JUMP
$$if carpet warp tile matching wWalkingDirection: wWalkingIntoEdgeWarp <= TRUE
$~~~~~~~~$if directions at wWalkingDirection and wPlayerDirection are the same: load warp data, wPlayerTurningDirection <= 0, wMovementAnimation <= movement_step_sleep, and r1_player_movement = PLAYERMOVEMENT_WARP
$
$wWalkingDirection == STANDING: wPlayerTurningDirection <= 0, wMovementAnimation <= movement_step_sleep
$~~~~$wWalkingDirection != STANDING: if wWalkingIntoEdgeWarp == FALSE, play bump sound, wPlayerTurningDirection <= 0, wMovementAnimation <= movement_step_bump

wPlayerState == PLAYER_SURF:
$$if on ice tile and wPlayerTurningDirection != 0: wCurInput <= current direction button
$
$update wWalkingDirection, wFacingDirection: wWalkingX, wWalkingY, wWalkingTile, based on wCurInput direction
$$if whirlpool tile: r1_player_movement = PLAYERMOVEMENT_FORCE_TURN
$
$if waterfall tile: wWalkingDirection <= direction, DoStep(STEP_WALK), r1_player_movement = PLAYERMOVEMENT_CONTINUE
$$if door/staircase/cave warp tile (non ladder/carpet): wWalkingDirection <= DOWN, DoStep(STEP_WALK), r1_player_movement = PLAYERMOVEMENT_CONTINUE
$
$if directions at wWalkingDirection and wPlayerDirection are not the same (turning): DoStep(STEP_TURN), r1_player_movement = PLAYERMOVEMENT_TURN
$$if no bump (water tile permissions or NPC): DoStep(STEP_WALK / STEP_BIKE / STEP_ICE)
$$if not leaving water: r1_player_movement = PLAYERMOVEMENT_FINISH
$
$if leaving water: wPlayerState <= PLAYER_NORMAL, reload music and sprites, and r1_player_movement = PLAYERMOVEMENT_EXIT_WATER
$
$wWalkingDirection == STANDING: wPlayerTurningDirection <= 0, wMovementAnimation <= movement_step_sleep
$~~~~$wWalkingDirection != STANDING: if wWalkingIntoEdgeWarp == FALSE, play bump sound, wPlayerTurningDirection <= 0, wMovementAnimation <= movement_step_bump

wPlayerNextMovement <= wMovementAnimation
r1_player_movement = PLAYERMOVEMENT_NORMAL

r1_player_movement == PLAYERMOVEMENT_NORMAL or r1_player_movement == PLAYERMOVEMENT_JUMP or r1_player_movement == PLAYERMOVEMENT_FINISH: r2_player_event = 0
r1_player_movement == PLAYERMOVEMENT_WARP: r2_player_event = PLAYEREVENT_WARP
r1_player_movement == PLAYERMOVEMENT_TURN: r2_player_event = PLAYEREVENT_JOYCHANGEFACING
r1_player_movement == PLAYERMOVEMENT_FORCE_TURN: CallScript(Script_ForcedMovement), r2_player_event = PLAYEREVENT_MAPSCRIPT CallScript returns PLAYEREVENT_MAPSCRIPT always
r1_player_movement == PLAYERMOVEMENT_CONTINUE or r1_player_movement == PLAYERMOVEMENT_EXIT_WATER: r2_player_event = -1

r2_player_event == -1: r3_player_event = 0 in this case, apart from r2_player_event = -1, PlayerMovement has also returned nc
r2_player_event == 0: in this case, apart from r2_player_event = 0, PlayerMovement has also returned nc
$$if on ice tile and wPlayerTurningDirection != 0: [j2]
$
$if A_BUTTON in hJoyPressed:
$$if facing to object event: CallScript(<object's script>) and r3_player_event = PLAYEREVENT_MAPSCRIPT / r3_player_event = PLAYEREVENT_ITEMBALL / load trainer data and r3_player_event = PLAYEREVENT_TALKTOTRAINER includes rock and boulder objects (PLAYEREVENT_MAPSCRIPT case)
$
$if bg event (signpost) in current coords and facing, and event's flag set if any: CallScript(<event's script> / HiddenItemScript) and r3_player_event = PLAYEREVENT_MAPSCRIPT
$$if facing to collision event (use cut, whirlpool, waterfall, headbutt, surf): call TryXOW, which returns with CallScript(AskXOW / CantXOW) and thus r3_player_event = PLAYEREVENT_MAPSCRIPT
$~~~~$hJoyPressed[SELECT_F] == TRUE:
$
$CallScript(SelectMenuScript) and r3_player_event = PLAYEREVENT_MAPSCRIPT
$~~~~$hJoyPressed[START_F] == TRUE:
$~~~~~~~~$CallScript(StartMenuScript) and r3_player_event = PLAYEREVENT_MAPSCRIPT
r3_player_event = r2_player_event in these instances is where PlayerMovement returned carry, so OWPlayerInput returns early

r3_player_event == 0: [j2]

[j1]
wScriptMode <= SCRIPT_READ
wScriptRunning <= loaded script from whatever jumped straight to [j1] OR r3_player_event

DoPlayerEvent (wScriptRunning == TRUE and wScriptRunning != PLAYEREVENT_MAPSCRIPT): if there is a non-PLAYEREVENT_MAPSCRIPT script requested during this loop iteration, DoPlayerEvent pushes it to make it be executed by ScriptEvents. So the code up to [j2] below here is actually executed by ScriptEvents and NOT right now.
All scripts below finish with the end script unless otherwise stated (e.g. by the endall script)

wScriptRunning == PLAYEREVENT_SEENBYTRAINER:
$~~~~$SeenByTrainerScript + StartBattleWithMapTrainerScript

wScriptRunning == PLAYEREVENT_TALKTOTRAINER:
$~~~~$TalkToTrainerScript + StartBattleWithMapTrainerScript

wScriptRunning == PLAYEREVENT_ITEMBALL:
$~~~~$FindItemInBallScript

wScriptRunning == PLAYEREVENT_CONNECTION:
$$hMapEntryMethod <= MAPSETUP_CONNECTION
$
$wMapStatus <= MAPSTATUS_ENTER
$~~~~$wScriptFlags[SCRIPT_RUNNING] = FALSE

wScriptRunning == PLAYEREVENT_WARP:
$$play warp sound
$
$hMapEntryMethod <= MAPSETUP_DOOR
$$wMapStatus <= MAPSTATUS_ENTER
$
$wScriptFlags[SCRIPT_RUNNING] = FALSE this write is exactly what the 'end' script also does

wScriptRunning == PLAYEREVENT_FALL:
$$hMapEntryMethod <= MAPSETUP_FALL
$
$wMapStatus <= MAPSTATUS_ENTER
$$wScriptFlags[SCRIPT_RUNNING] = FALSE
$
$play fall sound 1
$$apply fall movement
$
$play fall sound 2

wScriptRunning == PLAYEREVENT_WHITEOUT:
$~~~~$OverworldWhiteoutScript + Script_Whiteout ends with hMapEntryMethod <= MAPSETUP_WARP + wMapStatus <= MAPSTATUS_ENTER + endall

wScriptRunning == PLAYEREVENT_HATCH:
$~~~~$OverworldHatchEgg

wScriptRunning == PLAYEREVENT_JOYCHANGEFACING:
$$wScriptDelay <= 3
$
$wScriptMode <= SCRIPT_WAIT
$$wScriptFlags[SCRIPT_RUNNING] = FALSE
$
$wScriptFlags2[4] == TRUE enable wild encounters

[j2]
wScriptFlags2 <= 0

ScriptEvents: executes scripts requested this loop by CallScript (PLAYEREVENT_MAPSCRIPT)

wScriptFlags[SCRIPT_RUNNING] = TRUE
while wScriptFlags[SCRIPT_RUNNING] == TRUE: breaks after end or similar script command

$~~~~$wScriptMode == SCRIPT_OFF:
$~~~~~~~~$wScriptFlags[SCRIPT_RUNNING] = FALSE

$~~~~$wScriptMode == SCRIPT_READ:
$~~~~~~~~$(...)

$~~~~$wScriptMode == SCRIPT_WAIT_MOVEMENT:
$~~~~~~~~$(...)

$~~~~$wScriptMode == SCRIPT_WAIT:
$~~~~~~~~$(...)

wMapStatus != MAPSTATUS_HANDLE: [j3] jump if any script during this iteration changed wMapStatus (some warp ocurred)

HandleMapObjects:

HandleNPCStep Includes player object! At the beginning of each object, clears wPlayerStepVectorX, wPlayerStepVectorY, and wPlayerStepFlags, and sets wPlayerStepDirection to STANDING. HandleObjectStep is called for each visible object. This calls HandleStepType, which processes StepTypesJumptable by STEP_TYPE_. These functions manipulate wPlayerStepFlags among other things.

_HandlePlayerStep (wPlayerStepFlags != 0):

wPlayerStepFlags(PLAYERSTEP_START_F) == TRUE:
$$wHandlePlayerStep <= 4
$
$Scroll map in the direction at wPlayerStepDirection
$$wHandlePlayerStep <= wHandlePlayerStep - 1 $$wPlayerBGMapOffsetX <= wPlayerBGMapOffsetX - wPlayerStepVectorX
$$wPlayerBGMapOffsetY <= wPlayerBGMapOffsetY - wPlayerStepVectorY
else wPlayerStepFlags(PLAYERSTEP_STOP_F) == TRUE:
$
$Increase or decrease wYCoord or wXCoord according to wPlayerStepDirection
$$wHandlePlayerStep <= wHandlePlayerStep - 1
$
$wHandlePlayerStep == 1: BufferScreen
$$wHandlePlayerStep == 0: GetMovementPermissions Update wPlayerTile, wTilePermissions, wTileDown, wTileUp, wTileLeft, and/or wTileRight
$
$wPlayerBGMapOffsetX <= wPlayerBGMapOffsetX - wPlayerStepVectorX
$$wPlayerBGMapOffsetY <= wPlayerBGMapOffsetY - wPlayerStepVectorY
else wPlayerStepFlags(PLAYERSTEP_CONTINUE_F) == TRUE: same as PLAYERSTEP_STOP_F case except don't update wYCoord or wXCoord
$
$wHandlePlayerStep <= wHandlePlayerStep - 1
$$wHandlePlayerStep == 1: BufferScreen
$
$wHandlePlayerStep == 0: GetMovementPermissions Update wPlayerTile, wTilePermissions, wTileDown, wTileUp, wTileLeft, and/or wTileRight
$$wPlayerBGMapOffsetX <= wPlayerBGMapOffsetX - wPlayerStepVectorX
$
$wPlayerBGMapOffsetY <= wPlayerBGMapOffsetY - wPlayerStepVectorY

CheckObjectEnteringVisibleRange (wPlayerStepFlags[PLAYERSTEP_STOP_F] == TRUE)

DelayFrames(wOverworldDelay)

HandleMapBackground UpdateActiveSprites + ScrollScreen

CheckPlayerState:
wPlayerStepFlags[PLAYERSTEP_CONTINUE_F] == FALSE:
$$wMapEventStatus <= MAPEVENTS_ON
wPlayerStepFlags[PLAYERSTEP_CONTINUE_F] == TRUE and (wPlayerStepFlags[PLAYERSTEP_STOP_F] == FALSE or wPlayerStepFlags[PLAYERSTEP_MIDAIR_F] == TRUE):
$
$wMapEventStatus <= MAPEVENTS_OFF
else:
$$wScriptFlags2 <= $ff
$
$wMapEventStatus <= MAPEVENTS_ON

[j3]


End of overworld loop. The remainder are intermediate functions


Every script executed by ScriptEvents finishes with the some form of the end command. It returns (by updating wScriptPos and wScriptBank) to a parent script if any, and otherwise:
wScriptRunning <= FALSE
wScriptMode <= SCRIPT_OFF
wScriptFlags[SCRIPT_RUNNING] = FALSE
The endall command is like end, but also finishes parent scripts regardless.


DoStep:

wWalkingDirection == STANDING:
$$wPlayerTurningDirection <= 0
$
$wMovementAnimation <= movement_step_sleep
else:
$$wMovementAnimation <= <step (type, direction)>
$
$wPlayerTurningDirection <= <direction> | 1 << 7
$~~~~$then always returns PLAYERMOVEMENT_FINISH but often is overwritten by caller