git @ Cat's Eye Technologies Cosmos-Boulders / master src / player.js
master

Tree @master (Download .tar.gz)

player.js @masterraw · history · blame

// SPDX-FileCopyrightText: In 2019, Chris Pressey, the original author of this work, placed it into the public domain.
// SPDX-License-Identifier: Unlicense
// For more information, please refer to <https://unlicense.org/>


import { concoctJaft as concoct } from '../contrib/jaft-0.2/jaftConcoctor.js';
import { newMap, get, set, setr, update } from './immutable.js';
import {
    isUndefined, error, cos, sin,
    screenWidth, screenHeight, decr, cap, degreesToRadians,
    countDown, countDownToMode, fireWentUp, updatePosition
} from './utils.js';


/* --[ Player objects ]-------------------------------------------------------- */


// h (heading) is measured in degrees.  So, E is 0, S is 90, W is 180, and N is 270.
// f (force) is in the direction of h
// timer is, generally speaking, how many more steps it shall remain in the present mode
const resetPlayer = concoct({ set, screenWidth, screenHeight }, `(player) =>
    let player = set(player, "x", screenWidth() / 2);
    let player = set(player, "y", screenHeight() / 2);
    let player = set(player, "vx", 0.0);
    let player = set(player, "vy", 0.0);
    let player = set(player, "ax", 0.0);
    let player = set(player, "ay", 0.0);
    let player = set(player, "h", 270);
    let player = set(player, "dh", 0);
    let player = set(player, "f", 0);
    let player = set(player, "mode", "GET_READY");
    let player = set(player, "timer", 200);
    player
`);


const makePlayer = concoct({ newMap, set, resetPlayer }, `() =>
    let m = newMap();
    let m = set(m, "score", 0);
    let m = set(m, "lives", 2);
    let m = set(m, "mass", 1.0);
    resetPlayer(m)
`);


// Utils

const updateAcceleration = concoct({ get, setr, cos, sin, degreesToRadians }, `(player) =>
    const f = get(player, "f");
    f === 0 ? setr("ay", 0, setr("ax", 0, player)) : (
        const theta = degreesToRadians(get(player, "h"));
        const fx = cos(theta) * f;
        const fy = sin(theta) * f;
        const m = get(player, "mass");
        setr("ay", fy * m, setr("ax", fx * m, player))
    )
`);


const updatePlayerDeath = concoct({ get, setr, update, resetPlayer, decr }, `(player) =>
    get(player, "lives") > 0
        ? resetPlayer(update(player, "lives", decr))
        : setr("mode", "GONE", player)
`);


const updatePlayerStep = concoct({ error, get, setr, cap, countDown, countDownToMode, updatePosition, updateAcceleration, updatePlayerDeath }, `(player) =>
    get(player, "mode") === "GET_READY" ? countDownToMode(player, "PLAYING") : (
      get(player, "mode") === "PLAYING" ? (
        let player = updatePosition(player);
        let player = setr("vx", cap(get(player, "vx") + get(player, "ax"), 6), player);
        let player = setr("vy", cap(get(player, "vy") + get(player, "ay"), 6), player);
        let player = setr("h", get(player, "h") + get(player, "dh"), player);
        updateAcceleration(player)
      ) : (
        get(player, "mode") === "EXPLODING" ? countDown(player, updatePlayerDeath) : error("Unhandled mode", get(player, "mode"))
      )
    )
`);


const updatePlayerControlsChanged = concoct({ get, setr, fireWentUp }, `(player, action) =>
    get(player, "mode") === "PLAYING" ? (
        let dh = action.leftPressed ? 0-5 : (action.rightPressed ? 5 : 0),
            f = action.thrustPressed ? 0.05 : 0.0;
        setr("dh", dh, setr("f", f, player))
    ) : (
        get(player, "mode") === "GET_READY" && fireWentUp(action) ? (
            setr("dh", 0, setr("f", 0, setr("mode", "PLAYING", player)))
        ) : (
            setr("dh", 0, setr("f", 0, player))
        )
    )
`);


// Reducer that takes a Player and an Action and returns a new Player.
// Action must be one of: STEP | SCORE_POINTS | EXPLODE | CONTROLS_CHANGED
const updatePlayer = concoct({ error, get, set, isUndefined, makePlayer, updatePlayerStep, updatePlayerControlsChanged }, `(player, action) =>
    isUndefined(player) ? makePlayer() : (
      action.type === "STEP" ? updatePlayerStep(player) : (
        action.type === "SCORE_POINTS" ? set(player, "score", get(player, "score") + 10) : (
          action.type === "EXPLODE" ? set(set(player, "timer", 50), "mode", "EXPLODING") : (
            action.type === "CONTROLS_CHANGED" ? updatePlayerControlsChanged(player, action) : error("Unhandled action", action.type)
          )
        )
      )
    )
`);

export { makePlayer, resetPlayer, updatePlayer };