PulpScript

PulpScript is a friendly scripting language that allows you to add interactivity to your Pulp games. Its syntax is terse but powerful. A good rule of thumb when writing PulpScript is do less, less often.

General

Comments

You can comment your PulpScripts with two slashes:

// this is a comment

Comments can appear on their own line or after a valid expression.

Handling events

Event handlers are written like so:

on eventName do
    // something
end

All game logic must occur inside an event handler.

In addition to a handful of built-in events you can declare custom events and trigger them for the current tile with:

call "eventName"

or for all tiles that implement a particular event with:

emit "eventName"

These are the built-in events:

There is one more special catch-all event:

This is useful with the mimic function to allow one tile to behave exactly like another without having to copy/paste behavior.

Variables

All variables are globally scoped. You do not need to initialize a variable before using. All uninitialized variables have a default value of 0. Variables can hold a number or a string. Strings must be wrapped in double quotes. Variables are assigned like so:

varName = 0

Math

PulpScript supports simple arithmetic operations. Compound expressions are not supported.

Variables can be incremented or decremented like so:

varName++ // now equals 1
varName-- // equals 0 again

You can add to or subtract from a variable like so:

varName += 4 // now equals 4
varName -= 3 // now equals 1

You can also multiply or divide a variable like so:

varName *= 6 // now equals 6
varName /= 3 // now equals 2

Control Flow

Conditionals

Conditionals in PulpScript take the following form:

if varName==0 then
    // handle 0
elseif varName==1 then
    // handle 1
else
    // handle all other values
end

Both the elseif [condition] then and else are optional. The following boolean comparisons are available:

Compound expressions are not supported. String comparisons are case-sensitive. This also applies to event, room, and tile names when used as arguments for functions.

Loops

PulpScript also includes a while loop which can be thought of as a repeating if:

while varName==0 do
    // repeat until varName is set to another value
end

Returning

PulpScript does not have user-defined functions and event handlers cannot return values but callbacks and event handlers can be exited early with the done statement:

on loop do
    ticks += 1
    if ticks<interval then
        done // return from the "loop" event handler
    end

    ticks -= interval

    // do something at a regular interval
end

Persistent storage

Variables can be persisted across launches using the store and restore functions. A game’s persistent storage file can be found in the following locations, for the Playdate Simulator:

<SDKRoot>/Disk/Data/pulp.<author>.<name>/store.json

and on the hardware:

/Disk/Data/pulp.<author>.<name>/store.json

The web player stores persistent storage in localStorage which accessible from the browser’s console with:

localStorage.getItem("store-<id>")

event

There is also a read-only event variable.

In the update, interact, collect, confirm, and cancel events, it will have its dx and dy members set to -1, 0, or 1 reflecting the Player’s most recent attempted movement direction along each axis, as well as its tx and ty members set to the room coordinates of the target tile.

For example you can limit the Player to only interacting with a chest from below like so:

on interact do
    if event.dy==-1 then // player just moved up
        call "open"
    end
end

In events being handled by an instance of a tile, its x and y members will be set to the coordinates of the tile handling the event.

In events being handled by a tile, its tile member will be set to the name of the tile handling the event.

So you could move the Player to a specific tile’s location as soon as they enter a room like so:

on enter do
    goto event.x,event.y
end

In every event except load, its room member set to the name of the current room.

In every event except load, its px and py members set to the current coordinates of the Player.

In every event, its aa and ra members will be set to the current absolute angle and the amount of change since the last frame of the crank in degrees (relative angle). Also, its frame member will be set to the number of frames that have elapsed since starting the game.

In every event, its ax, ay, and az members will be set to the accelerometer’s x, y, and z values. Also, its orientation member is set to one of the following strings describing the hardware’s current orientation:

In the change and select events, its option member will be set to currently selected option’s name.

Its game, room, and player members can be used to tell the game, current room, or player respectively to perform actions. They can also be used to insert the name of the game, current room, or canonical player tile in a string. For example:

fin "Thanks for playing {event.game}!"

config

There is also a config variable which can be used to override some of Pulp’s default behaviors.

config.autoAct = 1

Controls whether the Player automatically interacts with sprites. The default value is 1. When set to 0 you can use the act function to interact with the sprite or collect the item directly in front of the Player (usually from the Player’s confirm event handler).

config.inputRepeat = 1

Controls whether button events fire repeatedly while held. The default value is 1. When set to 0 button events only fire once when first pressed.

config.inputRepeatDelay = 0.4

Controls the time from the initial press to the first repeat.

config.inputRepeatBetween = 0.2

Controls the time between all subsequent repeats.

config.follow = 0

Controls whether the camera follows the Player. The default value is 0. When set to 1 the Player is drawn in the center of the screen and all other tiles are drawn relative to its centered position.

config.followCenterX = 12
config.followCenterY = 7

Controls where the “center” of the screen is located. The default coordinates are the middle of the screen, 12,7.

config.followOverflowTile = "black"

Controls which tile is drawn beyond the room’s borders when the camera is following the Player. The default is the standard black tile.

The custom drawing functions are unaffected by the follow members (as they are primarily intended for stationary HUD elements). To offset the custom drawing functions subtract event.px and add config.followCenterX to the x coordinate and subtract event.py and add config.followCenterY to the y coordinate.

config.allowDismissRootMenu = 0

Controls whether the menu at the bottom of a menu stack can be dismissed. The default value is 0. When set to 1 the root menu can be dismissed by pressing the B button.

config.sayAdvanceDelay = 0.2

Controls the amount of time that must elapse before the player can advance or dismiss a text window. The default value is 0.2 seconds. This can help prevent the player from accidentally dismissing text before they have a chance to read it.

config.textSpeed = 20

Controls the rate at which text is drawn in text windows. (Higher means faster.) The default value is 20 characters per second.

config.textSkip = 1

Allows the player to fast-forward to the end of each text page by pressing any button or giving the crank half a turn. The default value is 1 (enabled); set to 0 to disable.

datetime

The read-only datetime variable has the following members reflecting the current time:

String formatting

You can include variable values in a string by wrapping the variable name in curly braces:

log "count is now set to {count}"

Variables can be padded on the left:

// left pad score with up to 6 zeros
label "SCORE {6,0:score}" at x,y

or right:

// right pad with up to 8 spaces
// left pad with up to 3 spaces 
label "{label:8, }{3, :value}" at x,y

You can also embed arbitrary tiles in a string with the special {embed:tileId} or {embed:tileName} syntax:

say "This is an apple: {embed:apple}"

Note that when used with a half-width font, only the left half of the tile is drawn (so you’ll need to use two tiles to simulate a full-width tile).

Strings used as arguments for say, fin, and ask may also contain the linefeed character \n to start a new line and form feed character \f to start a new page.

Functions

log

log "message"

Logs message to the console. You can include variable values in a string by wrapping the variable name in curly braces.

dump

dump

Logs the current values contained in the config, event, persistent storage, and game-specific custom variables.

say

say "message"

and

say "message" then
    // do something after the text box is dismissed
end

Displays message in a text box.

The text box can optionally be manually positioned and sized:

say "message" at x,y

or

say "message" at x,y,w,h

See also String formatting.

ask

ask "stringValue" then
    option "optionOne" then

    end
    option "optionTwo" then

    end
end

Displays stringValue in a text box. The player can then select from the provided options. The options are presented as a vertical list in another box overlaying the bottom right corner of the question box. ask requires at least one option, six or more options will be paginated, four to a page. By default values are limited to 8 characters (16 when using a half-width font). An example:

ask "Dost thou love me?" then
    option "Verily" then
        heartbroken = 0
    end
    option "Nay" then
        heartbroken = 1
    end
end

Its text box can optionally be manually positioned and sized:

ask "stringValue" at x,y then ...

or

ask "stringValue" at x,y,w,h then ...

menu

menu at x,y,w,h then
    option "One" then
        // do one thing
    end
    option "Two" then
        // do another
    end
end

Presents a paginated menu with one or more options at x,y. w and h are optional and specify the width and height of the menu minus chrome tiles and cursor. Menu options are selected by pressing the A button. Submenus can be dismissed by pressing the B button. By default the root menu cannot be dismissed. Set config.allowDismissRootMenu to 1 to allow the root menu to be dismissed by pressing B.

fin

fin "message"

Displays message in a text box over a black background and marks the game as finished. Also triggers the finish event. See also String formatting.

swap

swap tileId

or

swap "tileName"

Replaces the current instance of a tile with the tile matching tileId or tileName.

Note that when swap is used on the Player (either in update or with tell) it only swaps the representation of the Player so that all of the Player’s behaviors can live in a single Tile.

frame

varname = frame

Gets the current frame of the current instance of a tile. Frames are zero-based so the first frame is 0, and the last of 3 frames, for example, is 2.

frame frameIndex

Sets the frame of the current instance of a tile to frameIndex. Only affects tiles with an fps set to 0.

varname = frame x,y

Gets the current frame of the instance of the tile at x,y.

play

play tileId

or

play "tileName"

and

play tileId then
    // do something after the animation completes
end

or

play "tileName" then
    // do something after the animation completes
end

Replaces the current instance of a tile with the tile matching tileId or tileName, then plays all frames of that tile in sequence.

wait

wait duration then
    // do something after duration seconds
end

Note that this does not stop the run loop. If you do not want the player to be able to move during the pause you can call ignore before the wait call and listen at the end of its body. The timer does not elapse while a text or menu window is on-screen.

shake

shake duration

or

shake duration then
    // do something when shaking stops
end

“Shakes” the screen for the specified duration by changing the drawing offset to random values. Shaking pauses while a text or menu window is on-screen.

tell

tell x,y to
    // do something with the tile in the current room at x,y
end

or

tell tileId to
    // do something with the tile matching tileId
end

or

tell "tileName" to
    // do something with the tile named tileName
end

Allows one tile to make changes to another. Note that using x,y coordinates will always target a single instance of a tile in the current room while tileId and tileName will target the tile’s prototype. The top-left tile is 0,0 and the bottom-right is 24,14 Only x,y coordinates are compatible with swap and frame.

Also allows calling out to custom game or current room events with event.game or event.room. eg:

tell event.game to
    call "customEvent"
end

Note that you must use event.room to tell the current room to do something, "roomName" will be interpreted as a tile name. Using event.player is preferred to using the literal canonical player tile name.

call

call "eventName"

Calls the event handler for an event named eventName of the current instance of a tile, the current room or game. Can be targeted with tell, eg.

tell event.room to
    call "eventName"
end

emit

emit "eventName"

Calls the event handler of any active tile, current room, or the game that handles an event named eventName. You should use call with tell instead of emit if you already know exactly which tile(s) you want to handle the event. emit should be used sparingly as it has to check 378 objects (25x15 room tiles + game + current room + player), all of which could handle the event. The runtime overhead adds up quick.

mimic

mimic tileId

or

mimic "tileName"

Allows one tile to mimic the behavior of another. Use with the any event handler like so:

on any do
    mimic "idol"
end 

ignore/listen

ignore  

and

listen

Toggle whether the game should accept user input or not. Does not affect advancing or dismissing text.

act

act

Make the player interact with the sprite or collect the item one tile in front of them based on their last movement direction. Note that this function is only really useful if you’ve set config.autoAct to 0.

draw

draw tileId at x,y

or

draw "tileName" at x,y

Draws the requested tile at x,y. Can only be called from the Player’s draw event.

hide

hide

Prevents the Player from being drawn. Can only be called from the Player’s draw event.

window

window at x,y,w,h

Draws a window frame at x,y with dimensions w,h. (Coordinates and dimensions are in tiles, not pixels.) Can only be called from the Player’s draw event.

label

label "stringValue" at x,y,len,lines

Draws text stringValue at x,y. len is an optional maximum number of characters to draw. lines is an optional maximum number of lines to draw. Does not soft-wrap but does support the \n newline character for hard-wrapping. Can only be called from the Player’s draw event.

fill

fill "colorName" at x,y,w,h

Fills a rect with colorName (either “white” or “black”) at x,y with dimensions w,h. (Coordinates and dimensions are in pixels.) Can only be called from the Player’s draw event.

crop

crop to x,y,w,h

Limits drawing to just the room tiles within the rectangle defined by x,y,w,h. The default value is 0,0,25,15. Does not need to be called repeatedly, only when the target rectangle changes. Note that this crop only applies to automatically drawn room tiles. UI (like say or menu) and manual (like draw or label) drawing are not cropped. It also does not affect the player. To hide the player see hide.

goto

goto x,y

and

goto x,y in roomId

or

goto x,y in "roomName"

Moves the Player to x,y in the current room or in the room requested by roomId or roomName.

sound

sound soundId

or

sound "soundName"

Plays the sound matching soundId or soundName

once

once songId

or

once "songName"

or

once songId then
    // do something when the song ends
end

or

once "songName" then
    // do something when the song ends
end

Plays the song matching songId or songName then stops. Does nothing if the requested song is already playing.

loop

loop songId

or

loop "songName"

Plays the song matching songId or songName, looping back to the beginning (or the song’s loopFrom point if set in the editor) each time it completes. Does nothing if the requested song is already playing.

stop

stop

Stops the currently playing song.

bpm

bpm beatsPerMinute

Sets the bpm of the currently playing song to beatsPerMinute.

store

store "variableName"

or

store

Copies the current value of the variable into persistent storage. Persistent storage is written to disk between the exit and enter events when changing rooms or when the Player reaches an ending. When "variableName" is omitted all values in persistent storage are written to disk immediately.

restore

restore "variableName"

or

restore

Sets the variable to the corresponding value in persistent storage. When "variableName" is omitted all values are copied out of persistent storage.

toss

toss "variableName"

or

toss

Removes the variable from persistent storage. When "variableName" is omitted all values are removed from persistent storage. When the last variable is removed from storage the storage file is deleted.

Math functions

random

varname = random max

or

varname = random min,max

Returns an integer between the positive integers min (or 0) and max inclusive.

floor

varname = floor num

Returns the largest integer less than or equal to the given number.

ceil

varname = ceil num

Returns the next largest integer greater than or equal to the given number.

round

varname = round num

Returns the given number rounded to the nearest integer.

sine

varname = sine num

Returns the sine of the given number (in radians).

cosine

varname = cosine num

Returns the cosine of the given number (in radians).

tangent

varname = tangent num

Returns the tangent of the given number (in radians).

radians

varname = radians num

Returns the given number (in degrees) converted to radians.

degrees

varname = degrees num

Returns the given number (in radians) converted to degrees.

More functions (that return a value)

invert

invert

or

varname = invert

Causes all drawing to be inverted (white displays as black and vice versa) until called again. Returns 1 when drawing is inverted and 0 when drawing is not inverted.

solid

varname = solid x,y

or

varname = solid tileId

or

varname = solid "tileName"

Returns 1 if the tile identified by coordinates, id, or name is solid, otherwise returns 0.

type

varname = type x,y

or

varname = type tileId

or

varname = type "tileName"

Returns the type of the tile identified by coordinates, id, or name as a lowercase string.

id

varname = id x,y

or

varname = id "tileName"

Returns the id of the tile identified by coordinates or name.

name

varname = name x,y

or

varname = name tileId

Returns the name of the tile identified by coordinates or id.