# Mechanic recipes

## ASCII map with collection

```js
const level = GameLib.defineMap([
  '########',
  '#@..*..#',
  '#....>.#',
  '########',
])
// init
const state = {...level.start, coins: GameLib.mapTiles(level, '*').map(t => t.id)}
// step
const motion = GameLib.moveInWorld(level, state, input.vector(), dt, {speed: 4})
const coins = state.coins.filter(id => {
  const tile = level.tiles.find(t => t.id === id)
  return GameLib.distance(tile, motion) > 0.5
})
// draw
GameLib.drawWorld(g, level, {objects: [
  ...level.tiles.filter(t => coins.includes(t.id)).map(t => ({sprite: 'coin', x: t.x, y: t.y})),
  {sprite: 'hero', ...motion, pose: motion.moving ? 'walk' : 'idle'},
]})
```

## Doors that open

Add the door from state instead of editing the map. Pass the same scene to movement and drawing.

```js
const door = GameLib.mapTiles(map, 'door')[0]
const scene = {...map, objects: [...map.objects,
  {...door, sprite: state.key ? 'door' : 'door-closed', solid: !state.key}]}
const motion = GameLib.moveOnGrid(scene, state, input.vector(), dt)
```

## Side-scrolling platformer

```js
const scene = GameLib.defineWorld({view: 'side', floor: false, columns: 40, rows: 16})
const platforms = [{x: 0, y: 14, w: 40}, {x: 6, y: 11, w: 3, oneWay: true}]
// step
const motion = GameLib.movePlatformer(state,
  {x: input.vector().x, jump: input.pressed('a'), hold: input.down('a')}, platforms, dt)
const slime = GameLib.hopPatrol(world.time, {from: 6, to: 9})
const camera = GameLib.followCamera(state.camera, {x: motion.x * 16, y: 128}, dt, {worldWidth: 640})
// draw
GameLib.drawPlatforms(g, scene, platforms, {camera})
GameLib.drawWorld(g, scene, {camera, objects: [
  {sprite: slime.sprite, flip: slime.flip, x: slime.x, y: 14},
  {sprite: 'hero', x: motion.x, y: motion.y, direction: motion.direction,
    pose: motion.grounded ? (motion.moving ? 'walk' : 'idle') : 'jump'},
]})
```

Keep `camera` and every field of `motion` in state. The defaults are tuned; do not pass physics options unless the game needs a different feel. Start with `{x: 2, y: 14, camera: {x: 0, y: 0}}` (feet on the ground platform).

## Particles and HUD

```js
// step: on pickup
const particles = [...GameLib.stepParticles(state.particles, dt), ...GameLib.burst(x, y, {seed: world.tick})]
// draw
GameLib.drawParticles(g, state.particles, state.camera)
GameLib.drawHud(g, {score: 3, total: 5, hint: 'J: JUMP', status: ''})
```

Particle positions are px. Keep the game below the 40 px HUD.

## Hit, damage and effects

```js
// step: enemy defeated / player hit
fx.push({n: 'poof', x: enemy.x, y: enemy.y, t: world.time})
state = {...state, hurt: world.time, shook: world.time, hearts: state.hearts - 1}
const canHurt = world.time - state.hurt > 1
// draw
const q = GameLib.shake(world.time, state.shook), camera = {x: state.camera.x + q.x, y: state.camera.y + q.y}
const objects = state.fx.map(f => ({sprite: GameLib.effect(f.n, f.t, world.time), x: f.x, y: f.y})).filter(o => o.sprite)
if (GameLib.blink(world.time, state.hurt)) objects.push({sprite: 'hero', x, y, pose: world.time - state.hurt < 0.4 ? 'hurt' : 'idle'})
```

Start `hurt` and `shook` at `-9` so nothing flickers or shakes at the start. Enemies have a hurt frame too (`slime-hurt`).

## Randomness

```js
const x = Math.floor(GameLib.random(world.seed, world.tick) * 240)
```

Same seed and index always give the same value.
