Inspecting Rescues in Pulp
Tags: gamedev, beginners, playdate, pulp
Published: 2023-01-17 00:24:06

In a previous article I downloaded 21 games.

Two of those games made their `JSON` files available for download, too.

That means:
- Their game was built in Pulp
- I can import the file to inspect all of their game assets

In the last article I inspected one game, Snakes.

In this article, I'll inspect the other, Rescues.

## Inspecting Rescues
I'll study each view carefully.

### Font
Looks like a simple sans-serif font.

![Generic font](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v899e95m80b4p5i3i9d0.png)

### Sound
Three simple sounds: bounce, beep and ouch

![Sounds in the game](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q8i7cxt0aaz1vj63gkeb.png)

### Room
Aside from the title card, there's only one room: `start`.

![Start room](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sntaz51f7n05w79he4ws.png)

It has no exits.

It has no items.

It has about 1/3 of the world tiles as Snakes.

![World layer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eel8sk1tegax1n1b7nxy.png)

It has two sprites: `jumper` and `jumper ouch`

![Sprite tiles](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lxqz2uo4w4pmvye64iau.png)

It has two player tiles: `player` and `player down`

![Player tile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pgpiywhszuy85o68jzd6.png)

`player` has a script attached to it.

### Script

#### Player
There are five functions in the script attached to `player`.

![Player scripts](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fa9h8nmkj8arenlvocm1.png)

I've seen `cancel`, `confirm` and `update`.

`crank` and `draw` are new to me.

##### Revelation: `confirm` and `cancel` functions
In Rescues, you can only move two people and a stretcher left or right.

You can use:
- The left and right d-pad buttons
- The B and A buttons
- The crank

How the map to movement:
- D-pad right performs the same action as A
- D-pad left performs the same action as B
- Crank one way performs the same action as D-pad right
- Crank the other way performs the same action as D-pad left

I most menu screens, A is used to continue or `confirm` a prompt; B is used to go back or `cancel` a prompt.

It seems here that the game developer has hooked into the event listeners for the A and B buttons to perform the same movement logic as in the event listener for `update`, which I assume fires any time an button was pressed.

In each of the `on confirm do` and `on cancel do` functions, the developer knows which button was pressed.

In the `on update do` function, the developer does a check of whether `event.dx` is `1` or `-1` - seemingly to check for left or right movement.

In the `on crank do` function after a few lines that store values in variables, the developer updates the player's position based on whether a value is greater than or equal to a number...or less than or equal to a different number.

I assume those conditions determine which direction the crank was rotated.

##### `on draw do`
```
label "{points}" at 13,0
label "MISS" at 18,0
	
if misses>=1 then
  draw "miss" at 21,1
end
if misses>=2 then
  draw "miss" at 20,1
end
if misses>=3 then
  draw "miss" at 19,1
end
```

- `label ... at X,Y` is new
- Having played the game and seeing the use of coordinates, it seems this sets text from left-to-right starting at the given coordinate
- `misses` must be initialized somewhere else in this or another script
- It's odd that this logic moves some `miss` thing one more unit to the left depending on whether it is greater than 1, 2 or 3
- Wait, nevermind. Playing showed me that `miss` is a tile representing a missed rescue. Each miss gives a strike, drawn from right-to-left. It makes sense now.

The next series of commands place specific tiles at specific positions in the room.

##### `on update do`, `on confirm do`, `on cancel do`
As mentioned earlier:
- The main logic in each one consists of three clauses
- Each clause checks which of the four possibly-occupied spots the player was in, and moves the player and both guys to the correct - or only - adjacent spot

##### `on crank do`
The beginning of this event's code contains four statements:
```
crankRot += event.ra
sensitivity = 70
sensNeg = sensitivity
sensNeg *= -1
```

- `ra` must be a property of the `event` object pertaining to the crank's state
- The developer must need negative and positive versions of a value, hence the duplication and changing of sign

The next two conditions are:
```
if crankRot>=sensitivity then
elseif crankRot<=sensNeg then
```

Interesting. One checks for a number greater than 70. The other for a number less than -70.

#### Game
This script is almost 500 lines long!

At first glance it seems complicated and a bit redundant.

After analyzing a bit, I understand the redundancy:
- The game features four jumpers at its most difficult stage
- This script is responsible for managing the state of - and resetting - each jumper: via functions labeled `moveJumper` and `resetJumper`

##### `on load do`
Surprisingly this function does only three things, and two of them are given helpful context by the developer

```
on load do
  // first time playing the game, this needs to be 0
  has_played = 0
	
  // if the game has been played, this will set has_played to 1
  restore
	
  call "init"
end
```

I assume this information is found in the documentation for Pulp or PulpScript. Still, I'm glad this developer reiterated it.

##### `on init do`
This function creates and sets the values of a slew of variables.

It makes use of `event.px` and `event.py`.

It uses strings that don't seem to reference tiles:
```
jumper1_y_direction = "down"
jumper1_x_direction = "straight"
```

It uses a handy `round` function:
```
jumper_speed = round jumper_speed
```

##### `on loop do`
More use of the `name` function to seemingly set a string from two values and store them as the value of another variable:
```
tile_name1 = name jumper1_x,jumper1_y
tile_name2 = name jumper2_x,jumper2_y
tile_name3 = name jumper3_x,jumper3_y
tile_name4 = name jumper4_x,jumper4_y
```

Next, `jumper_frames` is incremented. It is initially `0`.

```
jumper_frames += 1
```

Then, a nested set of conditions to determine which of the four jumpers to move.

```
if jumper_frames==jumper_speed then
  call "moveJumper1"
  if points>9 then
    call "moveJumper2"
  end
  if points>22 then
    call "moveJumper3"
  end
  if points>67 then
    call "moveJumper4"
  end
  jumper_frames = 1
end
```

I'm not exactly sure how the outer-most condition works.

But it seems like new jumpers are only introduced once a point threshold is reached.

Next, three other functions are invoked.

```
call "checkForMatches"
call "checkForMisses"
call "increaseSpeed"
```

Time to inspect each of those functions!

##### `on checkForMatches do`
This function seems to account for two jumpers being at the same height on their descent by adjusting one jumper's height by one to ensure the player can rescue each one with a fast enough movement.

Each of the six conditions has this structure:
```
if jumper1_y_direction=="down" then
  if jumper2_y_direction=="down" then
    if jumper1_y==jumper2_y then
      sameY_1 = 1
    else
      sameY_1 = 0
    end
  end
end
```

##### `on checkForMisses do`
This function checks whether the player has accrued three misses and, if so, displays the end-game screen which shows their high score: the greater number between the score this round and the largest number from all previous rounds.

```
on checkForMisses do
  if misses==3 then
    if has_played==0 then
      high_score = points
    else
      if points>high_score then
        high_score = points
      else
	high_score = high_score
      end
    end
    wait 2 then
      fin "HI SCORE: {high_score}"
    end
  end
end
```

##### `on increaseSpeed do`
Thanks to the comment, this function increases the speed after 30 points are accrued.

```
on increaseSpeed do
  if points>30 then
    jumper_speed_per_second = 2
  end
end
```

##### `on finish do`
Lastly, the high score is saved and the game is marked as having been played.

```
on finish do
  store "high_score"
  has_played = 1
  store "has_played"
end
```

##### `on moveJumper do`
Each of these four functions contains eight conditions:
1. Move the jumper up or down by one and play the `beep` sound
2. Move the jumper right by one
3. Increment `points` by one when the jumper has made it as far right the player can move
4. Update the direction of the jumper
5. Update the direction of the jumper
6. Update the direction of the jumper
7. Handle when the player doesn't successfully rescue the jumper: increment `misses`, play a sound, update the jumper tile, reset the jumper
8. Get a point when jumper reaches the ambulance

##### `on resetJumper do`
Set four values that control the position of the jumper and which direction it moves in.

## Keywords and syntax learned
- `restore` - or what it does, at least
- `event.ra`
- `round`
- `call` as an alternative to `emit`?
- `shake`
- `tell ... to`
- `label ... at X,Y` to display text in the UI
- `{...}` in a string as text interpolation
- `draw ... at X,Y` to replace a tile with another by name
- `variable,variable` to be evaluated as numbers when drawing a tile
- `goto` as a way to move the player to a new position
- Why events like `confirm` and `cancel` are useful during gameplay

## Before jumping away
- I'm glad this game was vastly different than Snakes
- It revealed even more of Playdate's (or PulpScript's?) APIs
- It made me recognize that I should strive for little or no redundancy in my code, but sometimes it may be unavoidable...and that's ok as long as the game works as intended

## What next?
- Hunt for more `JSON` files so I can inspect other developers' Pulp games?
- Try building a rudimentary game in Pulp that allows me to practice all these new keywords and syntax I've learned?
- Start reading the official PulpScript documentation?
- Try following one of the tutorials linked to by Playdate?

No matter what I choose, I'm sure it will help me become more comfortable and familiar with building games for Playdate!