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:
load
: called once on the game, each room, and each tile when all assets are first loadedstart
: called once on the game after all rooms and tiles have handled theirload
evententer
: called on the game, the current room, and each tile in the current room every time the Player enters a new roomexit
: called on the game, the current room, and each tile in the current room every time the Player exits the current roomfinish
: called once on the game when the game is completedloop
: called on the game, 20 frames per second, before anything else is done that frame (except when asay
text box ormenu
is on screen)update
: called on the Player every time they move or interact with somethingbump
: called on the Player every time they bump into a solid world tileconfirm
: called on the Player when the A button is pressedcancel
: called on the Player when the B button is pressedcrank
: called on the Player when the crank is turneddock
: called on the Player when the crank is dockedundock
: called on the Player when the crank is undockeddraw
: called on the Player every frame before drawinginteract
: called on a specific Sprite when the Player bumps into or acts upon itcollect
: called on a specific Item when the Player steps onto or acts upon itchange
: called on the game when the cursor moves to a different option in amenu
orask
menu and when the menu first appearsselect
: called on the game when the player selects an option in amenu
orask
menudismiss
: called on the game when the player dismisses amenu
submenuinvalid
: called on the game when the player attempts to back out of anask
menu or selects an empty option
There is one more special catch-all event:
any
: called before any event the game or a tile may receive
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:
==
: equal to!=
: not equal to>
: greater than<
: less than>=
: greater than or equal to<=
: less than or equal to
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:
on back
(parallel to ground, screen facing up)on front
(parallel to ground, screen facing down)upside down
(perpendicular to ground, screen upside down)standing up
(perpendicular to ground, screen right-side up)on right
(perpendicular, right side of screen parallel to ground)on left
(perpendicular, left side of screen parallel to ground)
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:
year
: 1900-10,000year99
: 0-99month
: 1-12day
: 1-31weekday
: 0-6hour
: 0-23hour12
: 1-12minute
: 0-59second
: 0-59ampm
: the string “am” or “pm”timestamp
: seconds since midnight (hour 0), January 1 2000 UTC
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.