battlemovr
TypeScript icon, indicating that this package has built-in type declarations

0.8.15 • Public • Published

BattleMovr

Code Style: Prettier TypeScript: Strict NPM version Join the chat at https://gitter.im/FullScreenShenanigans/community

Drives RPG-like battles between two teams of actors.

Battles

BattleMovr coordinates "battles" in which two teams of actors take turns choosing and executing attacks. Each team has one actor considered "active" at any given time. Between turns, teams are given the option to choose the move they'll take the coming turn.

Consider these battles to follow the model of traditional RPGs such as Pokemon.

Actors

Each actor in a team may participate in the battle. They have at least the following attributes:

  • moves: Battle moves the actor knows.
  • nickname: Textual name for the actor.
  • statistics: Mutable in-battle statistics, which include at least health.
  • title: Sprite title the actor displays as.

Actors may be selected for battle as long as their .statistics.health.current isn't 0.

Actions

There are four types of actions a team may choose to take:

  • flee: The team attempts to leave the battle.
  • item: The team attempts to use an item.
  • move: The team's selected actor uses a battle move.
  • switch: The team switches which actor is selected.

Usage

Constructor

const battleMover = new BattleMovr({
    actionsOrderer(actions: UnderEachTeam<Action>): TeamAndAction<Action>[] {
        // Returns the actions in the order they should occur.
    },
    animations: {
        complete(outcome: BattleOutcome, onComplete?: () => void): void {
            // Animates the battle finishing; calls onComplete when done if provided.
        },
        opponent: {
            actions: {
                flee(teamAction: TeamAndAction<FleeAction>, onComplete: () => void): void {
                    // Animates the team choosing to flee.
                },
                item(teamAction: TeamAndAction<ItemAction>, onComplete: () => void): void {
                    // Animates the team choosing to use an item.
                },
                move(teamAction: TeamAndAction<MoveAction>, onComplete: () => void): void {
                    // Animates the team choosing to use an actor move.
                },
                switch(teamAction: TeamAndAction<SwitchAction>, onComplete: () => void): void {
                    // Animates the team choosing to switch actors.
                },
            },
            healthChange(health: number, onComplete: () => void): void {
                // Animates an actor's health changing.
            },
            introduction(onComplete: () => void): void {
                // Animates the team introducing itself.
            },
            switching: {
                enter(onComplete: () => void): void {
                    // Animates an actor entering battle.
                },
                exit(onComplete: () => void): void {
                    // Animates an actor exiting battle.
                },
                knockout(onComplete: () => void): void {
                    // Animates an actor getting knocked out of battle.
                },
                switch(teamAction: TeamAndAction<TAction>, onComplete: () => void): void {
                    // Animates a team switching actors.
                },
            },
        },
        player: {
            // (same as the opponent's properties, but for the player!)
        },
        start(onComplete: () => void): void {
            // Animation for a battle starting.
        },
    },
    selectorFactories: {},
});

actionsOrderer

Runs after each team has chosen their moves for an upcoming turn. It takes in the Action chosen for each team as an UnderEachTeam<Action> and returns the ordered array of when those actions should take place.

For example, an orderer that prioritizes flee, switch, item, and move in that order might look like:

const orders = {
    flee: 0,
    switch: 1,
    item: 2,
    move: 3,
};

const actionsOrderer: ActionsOrderer = (
    actions: UnderEachTeam<Action>
): TeamAndAction<Action>[] => {
    const playerOrder = orders[actions.player];
    const opponentOrder = order[actions.opponent];

    return playerOrder <= opponentOrder
        ? [actions.player, actions.opponent]
        : [actions.opponent, actions.player];
};

See src/Teams.ts for full type signatures.

animations

Animation callbacks for various battle activities. Two basic animation members are:

  • complete: Animation for when a battle is complete.
  • start: Animation for a battle starting.

Also required are team-specific animations under both .opponent and .player:

  • actions: Animations for in-battle selected actions, keyed by their type names (see names above).
  • healthChange: Animation for when an actor's health changes.
  • introduction: Animation for a team introducing itself at the beginning of battle.
  • switching: Animations for actors switching positions, which are:
    • enter: Animation for an actor entering battle.
    • exit: Animation for an actor exiting battle.
    • knockout: Animation for an actor getting knocked out.
    • switch: Animation for actors being swapped.

See src/Animations.ts for full type signatures.

selectorFactories

An object containing methods that return action selectors usable by teams. An action selector contains methods for the team to choose their next action between battle turns. Each selector contains:

  • afterKnockout: Reacts to the selected actor having just been knocked out.
  • nextAction: Determines the next action while there is still a selected actor.

These are keyed by names that will be specified by teams entering battle. For example, a "cowardly" selector might always choose a flee action:

const selectorFactories = {
    cowardly: () => () => ({
        afterKnockout: () => {},
        nextAction: (_, _, onChoice) => {
            onChoice({
                type: "flee",
            });
        },
    }),
};

afterKnockout doesn't provide an onChoice; it might be refactored to animations.

See src/Selectors.ts for full type signatures.

inBattle

Returns whether there is a current battle. Subsequent methods will throw errors if not used in the correct battle state.

  • startBattle throws an error if a battle is already ongoing.
  • getBattleInfo, stopBattle, and switchSelectedActor throw if a battle isn't ongoing.

startBattle

Starting a battle requires passing two teams to participate in the battle. As with other containers, teams are named opponent and player. Each team must at least contain:

  • actors: Array of actors that will fight.
  • selector: How the team chooses their actions (see above).

Teams may also specify a teamLeader with an actor-like nickname and title. Team leaders, if they exist, are animated animated to show actors entering or leaving battle.

For example, simulating battles between two robots, where the player is piloted by a human:

FSP.battles.startBattle({
    teams: {
        opponent: {
            actors: [
                {
                    moves: [
                        {
                            title: "Bend",
                            remaining: 10,
                            uses: 10,
                        },
                    ],
                    nickname: "Flex".split(""),
                    statistics: {
                        health: {
                            current: 100,
                            normal: 100,
                        },
                    },
                    title: "BendingUnit22".split(""),
                },
            ],
            selector: "angry",
        },
        player: {
            actors: [
                {
                    moves: [
                        {
                            title: "Bend",
                            remaining: 10,
                            uses: 10,
                        },
                    ],
                    nickname: "Bender".split(""),
                    statistics: {
                        health: {
                            current: 100,
                            normal: 100,
                        },
                    },
                    title: "BendingUnit22".split(""),
                },
            ],
            leader: {
                nickname: "Fry".split(""),
                title: "LazyHuman".split(""),
            },
            selector: "angry",
        },
    },
});

stopBattle

Reports that the battle has completed with a particular outcome. This is typically used by one of the above animations in response to an action taken. For example, a flee action's animation would typically call it when complete:

const animateOpponentFlee = (
    teamAction: TeamAndAction<FleeAction>,
    onComplete: () => void
): void => {
    console.log("Opponent ran away!");
    battleMover.stopBattle(BattleOutcome.opponentFled, onComplete);
};

switchSelectedActor

Switches the selected actor for a team. This takes in the Team enum being switched and its new actor. It will call the relevant animations in sequential order.

The new actor is expected to be in that team's list of actors.

Development

This repository is a portion of the EightBittr monorepo. See its docs/Development.md for details on how to get started. 💖

Running Tests

yarn run test

Tests are written in Mocha and Chai. Their files are written using alongside source files under src/ and named *.test.ts?. Whenever you add, remove, or rename a *.test.t* file under src/, watch will re-run yarn run test:setup to regenerate the list of static test files in test/index.html. You can open that file in a browser to debug through the tests, or run yarn test:run to run them in headless Chrome.

Readme

Keywords

none

Package Sidebar

Install

npm i battlemovr

Weekly Downloads

40

Version

0.8.15

License

MIT

Unpacked Size

98.4 kB

Total Files

83

Last publish

Collaborators

  • joshuakgoldberg