Skip to content

Instantly share code, notes, and snippets.

@sergeysova
Created September 18, 2022 20:42

Revisions

  1. sergeysova created this gist Sep 18, 2022.
    152 changes: 152 additions & 0 deletions munchkin.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,152 @@
    import {
    $currentPlayerId,
    $gameMode,
    $nextPlayerId,
    $players,
    GameMode,
    NO_CURRENT_PLAYER,
    Player,
    Uuid,
    changeGameMode,
    common,
    gearDecrease,
    gearIncrease,
    gearReset,
    levelDown,
    levelUp,
    playerJoined,
    playerKill,
    playerLeft,
    playerNameChanged,
    playersReordered,
    restartGame,
    sexChange,
    turnNext,
    turnSet,
    } from '../common';
    import { sample, createEffect } from 'effector';

    export * from '../common';

    export const reportStoreUpdateFx = createEffect<
    { sid: string; name: string; value: unknown },
    void
    >();

    const MAX_LEVEL: { [Key in GameMode]: number } = {
    common: 10,
    legendary: 20,
    unlimited: Infinity,
    };

    const MIN_LEVEL = 1;

    for (const $store of common.history.stores) {
    sample({
    clock: $store,
    fn: (value) => ({ value, sid: $store.sid, name: $store.compositeName.fullName }),
    target: reportStoreUpdateFx,
    });
    }

    $gameMode.on(changeGameMode, (_, newMode) => newMode);

    $players.on(changeGameMode, (players, gameMode) =>
    players.map((player) => ({
    ...player,
    level: Math.min(Math.max(player.level, MIN_LEVEL), MAX_LEVEL[gameMode]),
    })),
    );

    $currentPlayerId.reset(restartGame);
    $players.on(restartGame, (players) => players.map((player) => ({ ...player, level: 1, gear: 0 })));

    $players
    .on(playerJoined, (list, player) => [...list, player])
    .on(playersReordered, (players, orderedIds) =>
    orderedIds.map((id) => players.find((player) => player.id === id)!),
    );

    const playerLeftGame = sample({
    clock: playerLeft,
    source: { current: $currentPlayerId, next: $nextPlayerId, list: $players },
    fn: ({ next, current, list }, leftId) => {
    let nextId = leftId === current ? next : current;
    if (list.length === 1) {
    nextId = NO_CURRENT_PLAYER;
    }
    return {
    next: nextId,
    list: list.filter((player) => player.id !== leftId),
    };
    },
    });

    $players.on(playerLeftGame, (_, { list }) => list);
    $currentPlayerId.on(playerLeftGame, (_, { next }) => next);

    $players.on(playerNameChanged, (players, { id, name }) =>
    players.map((player) => (player.id === id ? { ...player, name } : player)),
    );

    $currentPlayerId.on(turnSet, (_, playerId) => playerId);

    sample({
    clock: turnNext,
    source: $nextPlayerId,
    target: $currentPlayerId,
    });

    $players
    .on(
    sexChange,
    update((player) => ({ sex: player.sex === 'male' ? 'female' : 'male' })),
    )
    .on(
    gearIncrease,
    update((player) => ({ gear: player.gear + 1 })),
    )
    .on(
    gearDecrease,
    update((player) => ({ gear: player.gear - 1 })),
    )
    .on(
    gearReset,
    update(() => ({ gear: 0 })),
    )
    .on(
    playerKill,
    update(() => ({ gear: 0 })),
    );

    sample({
    clock: levelUp,
    source: { players: $players, gameMode: $gameMode },
    fn: ({ players, gameMode }, playerId) =>
    players.map((player) => {
    if (player.id === playerId) {
    return { ...player, level: Math.min(player.level + 1, MAX_LEVEL[gameMode]) };
    }
    return player;
    }),
    target: $players,
    });

    $players.on(
    levelDown,
    update((player) => ({ ...player, level: Math.max(player.level - 1, MIN_LEVEL) })),
    );

    function update(cb: (player: Player) => Partial<Player> | undefined) {
    return (list: Player[], id: Uuid) =>
    list.map((player) => {
    if (player.id === id) {
    const updated = cb(player);
    if (updated) {
    return { ...player, ...updated };
    }
    return player;
    }
    return player;
    });
    }