About support functions

These functions are called by creatures during gameplay. Full disassembly and pseudocode are not yet complete.

Attack

func Attack(int16 ParamIdx, int16 X, int16 Y)

If the tile at (X, Y) is the player, reduce their health and say “Ouch!” For other tiles, remove their parameter record.

CheckBitmap

func CheckBitmap(uint8 Value, uint8[32] Bitmap) -> bool

Given a 256 bit (32 byte) Bitmap, return true if the Valueth bit is set in the Bitmap.

CheckTimeElapsed

func CheckTimeElapsed(uint16* LastTime, uint16 Duration) -> uint8

Check if Duration centiseconds have elapsed since the value pointed to by LastTime. The LastTime value should be a value in centiseconds less than 6000. If this function returns 1 (true), the value pointed to by LastTime is updated to the current centiseconds offset.

If the time has not elapsed yet, 0 is returned.

Convey

Rotate pushable tiles around a point. Used by conveyors.

In the Direction parameter, 1 is clockwise and -1 is counterclockwise.

Although this is implemented as a single procedure in ZZT, I have broken it into several smaller functions for clarity.

func Convey(int16 X, int16 Y, int16 Direction) {
    // Iterate over SW, S, SE, E, NE, N, NW, W; reverse it for counterclockwise.
    var Offsets = [(-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0)]
    if Direction != 1 {
        Offsets = reversed(Offsets)
    }

    // Get all the tiles around this conveyor.  Keep track of whether the last tile is
    // "movable": empty or pushable.
    var PrevTileMovable = true
    var Tiles = Tile[8]
    for (i, (XOffset, YOffset)) in enumerate(Offsets) {
        Tiles[i] = BoardTiles[X + XOffset][Y + YOffset]
        if Tiles[i].Type == TTEmpty {
            PrevTileMovable = true
        } else if TileTypes[Tiles[i]].Pushable == 0 {
            PrevTileMovable = false
        }
    }

    // Start rotating as soon as the previous tile is movable.
    for Index in len(Offsets) {
        var Tile = Tiles[i]
        if !PrevTileMovable {
            // Couldn't rotate this time.  If this tile is movable, we can rotate next time.
            PrevTileMovable = IsTileMovable(Tile)
        } else {
            // The previous tile can be moved.  As long as the current tile can be pushed
            // out of the way, we can rotate it into this space.
            if TileTypes[Tile.Type].Pushable {
                Rotate(X, Y, Direction, Offsets, Tiles, Index)
                // If the next tile isn't pushable, clear out the (now vacated) current tile.
                // This is not necessary if the next tile is pushable, since it will be pushed
                // into this spot.
                let NextTile = Tiles[(Index + Direction) % len(Tiles)]
                if TileTypes[NextTile.Type].Pushable == 0 {
                    let (OffsetX, OffsetY) = Offsets[Index]
                    BoardTiles[X + OffsetX][Y + OffsetY].Type = TTEmpty
                    DrawTile(X + OffsetX, Y + OffsetY)
                }
            } else {
                // We can't rotate this time since the destination isn't movable, and we won't
                // be able to rotate next time since the source won't be movable.
                PrevTileMovable = false
            }
        }
    }
}


func Rotate(int16 X, int16 Y, int16 Direction, int16 Offsets, int16 Tiles, int16 Index) {
    // Calculate source (Index) and destination (Index-1) locations.
    let (SrcXOffset, SrcYOffset) = Offsets[Index]
    let (SrcX, SrcY) = (X + SrcXOffset, Y + SrcYOffset)
    let (DestXOffset, DestYOffset) = Offsets[(Index - Direction) % len(Offsets)]
    let (DestX, DestY) = (X + DestXOffset, Y + DestYOffset)
    
    // If the current tile has params, move it.
    if TileTypes[Tiles[Index].Type].Cycle > -1 {
        let SrcTile = BoardTiles[SrcX][SrcY]
        let SrcParamIdx = ParamIdxForXY(SrcX, SrcY)
        // Set the correct tile types at the source and destination, then move.
        BoardTiles[SrcX][SrcY] = Tiles[Index]
        BoardTiles[DestX][DestY].Type = TTEmpty
        MoveTileWithIdx(SrcParamIdx, DestX, DestY)
    } else {
        // No params to move, just copy the tile.
        BoardTiles[DestX][DestY] = Tiles[Index]
        DrawTile(DestX, DestY)
    }
}


//
// Check if a tile is movable for a conveyor belt.  Empty and pushable tiles are movable.
//
func IsTileMovable(Tile tile) -> Bool {
    if Tile.Type == TTEmpty {
        return true
    } else {
        return (TileTypes[Tile.Type].Pushable != 0)
    }
}

Destroy

func Destroy(int16 X, int16 Y)

If the tile at (X, Y) has a parameter record, attack it. Otherwise, replace it with empty.

DieAttackingTile

func DieAttackingTile(int16 ParamIdx, int16 X, int16 Y)

Attack the tile at (X, Y) and kill the tile with the given parameter record.

Distance

func Distance(int16 A, int16 B) -> int16

Calculate the distance between two scalar values, i.e. abs(A - B).

DrawTile

func DrawTile(int16 X, int16 Y)

Redraw the tile at (X, Y).

Explode

func Explode(int16 CenterX, int16 CenterY, int16 Mode)

Manipulate a circle of tiles around a center point. Normally used for bomb explosions, but can also be used to draw the circle of visible tiles around the player in a dark board.

The Mode parameter selects what to do with the circle. It takes the following values:

Value Enumeration Description
0 EMRedrawOnly Redraw tiles
1 EMBomb Send BOMBED, destroy destructibles, replace empty with breakable
2 EMCleanUp Replace breakable with empty

MoveTileWithIdx

func MoveTileWithIdx(int16 ParamIdx, int16 X, int16 Y)

Move the tile with the given parameter index to (X, Y). The tile under the moved tile’s original location will be restored, and the background color of the tile will be updated to match the background at the destination location.

ParamIdxForXY

func ParamIdxForXY(int16 X, int16 Y) -> int16

Get the index into BoardParams of the tile at (X, Y). Return -1 if there are no parameters for that tile.

PlaySoundPriority

func PlaySoundPriority(int16 priority, string data)

Full analysis to come. Seems to overwrite the sound buffer based on a priority number.

PromptQuit

func PromptQuit()

Present a Y/N prompt to “End this game?” If the user selects Y, the global ShouldQuit flag is set.

PromptSaveWorld

func PromptSaveWorld(string* Prompt, string* Filename, string* Extension)

Prompt the user to save the current world. This is used both for saving worlds in the editor, as well as making saved games by replacing the “.ZZT” extension with “.SAV”. If accepted, the Filename is updated with the user’s input, and the world will be saved to the specified file.

Random

func Random(int16 Max) -> int16

Return a random integer starting at 0 and up to, but not including, Max.

RandomStep

Return a random cardinal direction. For each step, there is a 2/3 chance of moving horizontally and a 1/3 chance of moving vertically.

This function will never return a diagonal step.

func RandomStep() -> (int16 StepX, int16 StepY) {
    // Get a value between [-1,1] and assign it to StepX.
    let StepX = Random(3) - 1

    // Set StepY only if we got 0 for StepX.
    let StepY
    if StepX == 0 {
        // Get a value that's either -1 or 1 and assign it to StepY.
        StepY = 2*Random(2) - 1
    } else {
        StepY = 0
    }

    return (StepX, StepY)
}

RedrawBorder

func RedrawBorder()

Redraw the outermost tiles on the board.

RemoveParamIdx

func RemoveParamIdx(int16 Index)

Full analysis to come. Removes the object at this Index from BoardParams.

RunCodeCycle

func RunCodeCycle(int16 ParamIdx, uint8* InstructionPtr, string* DefaultTitle)

Run the ZZT-OOP interpreter starting on the given InstructionPtr, a pointer into the object’s code.

The DefaultTitle will be used as the default title if a text scroll is displayed, though this can be overridden by the Scroll or Object.

SayMessage

func SayMessage(int16 Duration, string* Message)

Remove any existing Messenger at (0, 0), and spawn a new Messenger at (0, 0) to say the given Message for Duration (in centiseconds).

ZZT does not calculate the total time accurately, causing it to be slightly fast or much slower depending on the user’s selected game speed.

SeekStep

Given the position (X, Y), return a cardinal direction to walk towards the player. There is a 50/50 chance of moving horizontally or vertically.

If the player is energized, return the opposite direction.

This function will never return a diagonal step.

func SeekStep(int16 X, int16 Y) -> (int16 StepX, int16 StepY) {
    var StepX = 0
    var StepY = 0

    // Pick randomly whether to move on the X or Y axis
    let PickedY = (Random(2) == 1)

    // If we picked Y but are already aligned on the Y axis, set X instead
    if !PickedY || PlayerY == Y {
        // Set StepX towards the player
        StepX = StepForDelta(PlayerX - X)
    }

    // If we didn't move on the X axis, move on the Y axis
    if StepX == 0 {
        // Set StepY towards the player
        StepY = StepForDelta(PlayerY - Y)
    }

    // Reverse the result if the player is energized
    if EnergizerCycles > 0 {
        StepX = -StepX
        StepY = -StepY
    }

    return (StepX, StepY)
}

Send

func Send(int16 Index, string Label, int16 OverrideLock) -> int16

For Objects and Scrolls, send the parameter record at BoardParams[Index] to the Label if they have it. If the label is something like “all:label”, the destination prefix will be used and all matching objects will be sent.

If the object has been locked, this call will be ignored unless OverrideLock is set. If the Index is negative, it won’t override locking. But if the Index is positive, it might. More analysis is needed.

Returns 1 if the message was sent and 0 if not.

Shoot

func Shoot(uint8 ShootType, int16 X, int16 Y, int16 StepX, int16 StepY, uint8 Owner) -> uint8

From the tile at (X, Y) attempt to shoot towards (StepX, StepY). ShootType should be TTBullet or TTStar, and Owner should be SOPlayer or SOEnemy.

If the tile is blocked, nothing will happen and 0 will be returned. On success, 1 is returned.

ShowDebugPrompt

func ShowDebugPrompt()

Prompt the user for debug/cheat commands, and execute them.

ShowHelpFile

func ShowHelpFile(string* Filename, string* Title)

Display a help file in a scroll, with the given title.

Spawn

func Spawn(int16 X, int16 Y, int16 Type, int16 Color, int16 Cycle, ParamRecord* SourceParams)

Spawn a new tile with a parameter record copied from a source record. If the source record has code, the code will be copied as well.

StepForDelta

func StepForDelta(int16 Delta) -> int16

Return -1 for a negative delta, 0 for 0, and 1 for a positive delta.

TryPush

func TryPush(int16 X, int16 Y, int16 StepX, int16 StepY)

Try to push an object at (X, Y) in the direction of (StepX, StepY). If the object is pushed into a transporter, try to transport it. If the object is pushed into a destructible tile, destroy that tile.

If the object is blocked, recursively try to push the blocking object.

UpdateSideBar

func UpdateSideBar()

Redraw the sidebar.