economyUtils

  • builtin
  • by Takaro
  • Takaro >=0.0.1
  • all
Version

Built-in modules ship with Takaro, so no import is needed.

View export JSON

A set of commands to allow players to manage their currency.

Configuration 2

Settings you fill in when installing the module on a server.

SettingTypeDefaultDescription
pendingAmount Pending amount number 0 When a player transfers money, they must confirm the transfer when the amount is equal or above this value. Set to 0 to disable.
zombieKillReward Zombie kill reward number 1 The default amount of currency a player receives for killing a zombie. This can be overridden by roles.
Raw config schema
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "pendingAmount": {
      "title": "Pending amount",
      "type": "number",
      "description": "When a player transfers money, they must confirm the transfer when the amount is equal or above this value. Set to 0 to disable.",
      "default": 0
    },
    "zombieKillReward": {
      "title": "Zombie kill reward",
      "type": "number",
      "description": "The default amount of currency a player receives for killing a zombie. This can be overridden by roles.",
      "default": 1
    }
  },
  "required": [],
  "additionalProperties": false
}
Raw UI schema
{}

Commands 8

Chat commands players trigger in game.

  • balance

    Check your balance.

    Command source
    import { takaro, data } from '@takaro/helpers';
    async function main() {
        const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', data.gameServerId)).data.data;
        await data.player.pm(`balance: ${data.pog.currency} ${currencyName.value}`);
    }
    await main();
    //# sourceMappingURL=balance.js.map
  • topCurrency

    List of the 10 players with the highest balance.

    Command source
    import { takaro, data } from '@takaro/helpers';
    async function main() {
        const richest = (await takaro.playerOnGameserver.playerOnGameServerControllerSearch({
            limit: 10,
            sortBy: 'currency',
            sortDirection: 'desc',
            extend: ['player'],
        })).data.data;
        const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', data.gameServerId)).data.data
            .value;
        // TODO: change this to name when it become available in playerOnGameServer
        const richestStrings = richest.map(async (pog, index) => {
            const playerName = (await takaro.player.playerControllerGetOne(pog.playerId)).data.data.name;
            return `${index + 1}. ${playerName} - ${pog.currency} ${currencyName}`;
        });
        await data.player.pm('Richest players:');
        for (const string of richestStrings) {
            await data.player.pm(await string);
        }
    }
    await main();
    //# sourceMappingURL=topCurrency.js.map
  • grantCurrency

    Grant money to a player. The money is not taken from your own balance but is new currency.

    ArgumentTypeDefaultHelp
    receiver player — The player to grant currency to.
    amount number — The amount of money.
    Command source
    import { takaro, data } from '@takaro/helpers';
    async function main() {
        const { pog: granter, arguments: args, gameServerId } = data;
        // args.receiver has an argument type of "player". Arguments of this type are automatically resolved to the player's id.
        // If the player doesn't exist or multiple players with the same name where found, it will have thrown an error before this command is executed.
        const receiver = args.receiver;
        const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', gameServerId)).data.data.value;
        const granterName = (await takaro.player.playerControllerGetOne(granter.playerId)).data.data.name;
        const receiverName = (await takaro.player.playerControllerGetOne(receiver.playerId)).data.data.name;
        await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(receiver.gameServerId, receiver.playerId, {
            currency: args.amount,
            reason: 'Admin grant',
        });
        const messageToReceiver = takaro.gameserver.gameServerControllerSendMessage(gameServerId, {
            message: `Granted ${args.amount} ${currencyName} by ${granterName}`,
            opts: {
                recipient: {
                    gameId: receiver.gameId,
                },
            },
        });
        await Promise.all([
            granter.pm(`You successfully granted ${args.amount} ${currencyName} to ${receiverName}`),
            messageToReceiver,
        ]);
        return;
    }
    await main();
    //# sourceMappingURL=grantCurrency.js.map
  • revokeCurrency

    Revokes money from a player. The money disappears.

    ArgumentTypeDefaultHelp
    receiver player — The player to revoke currency from.
    amount number — The amount of money.
    Command source
    import { takaro, data } from '@takaro/helpers';
    async function main() {
        const { pog: revoker, arguments: args, gameServerId } = data;
        // args.receiver has an argument type of "player". Arguments of this type are automatically resolved to the player's id.
        // If the player doesn't exist or multiple players with the same name where found, it will have thrown an error before this command is executed.
        const receiver = args.receiver;
        const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', gameServerId)).data.data.value;
        const revokerName = (await takaro.player.playerControllerGetOne(revoker.playerId)).data.data.name;
        const receiverName = (await takaro.player.playerControllerGetOne(receiver.playerId)).data.data.name;
        await takaro.playerOnGameserver.playerOnGameServerControllerDeductCurrency(receiver.gameServerId, receiver.playerId, {
            currency: args.amount,
            reason: 'Admin revoke',
        });
        const messageToReceiver = takaro.gameserver.gameServerControllerSendMessage(gameServerId, {
            message: `${args.amount} ${currencyName} were revoked by ${revokerName}`,
            opts: {
                recipient: {
                    gameId: receiver.gameId,
                },
            },
        });
        await Promise.all([
            revoker.pm(`You successfully revoked ${args.amount} ${currencyName} of ${receiverName}'s balance`),
            messageToReceiver,
        ]);
        return;
    }
    await main();
    //# sourceMappingURL=revokeCurrency.js.map
  • confirmTransfer

    Confirms a pending transfer.

    Command source
    import { takaro, data, TakaroUserError } from '@takaro/helpers';
    async function main() {
        const { gameServerId, pog: sender, module: mod } = data;
        // try to find a variable with key "confirmTransfer"
        const variables = (await takaro.variable.variableControllerSearch({
            filters: {
                key: ['confirmTransfer'],
                gameServerId: [gameServerId],
                moduleId: [mod.moduleId],
                playerId: [sender.playerId],
            },
        })).data.data;
        if (variables.length === 0) {
            throw new TakaroUserError('You have no pending transfer.');
        }
        // Remove the variable before potentially executing the transaction.
        await takaro.variable.variableControllerDelete(variables[0].id);
        const pendingTransfer = JSON.parse(variables[0].value);
        await takaro.playerOnGameserver.playerOnGameServerControllerTransactBetweenPlayers(sender.gameServerId, sender.id, pendingTransfer.receiver.id, {
            currency: pendingTransfer.amount,
            reason: 'Player transfer',
        });
        const receiverName = (await takaro.player.playerControllerGetOne(pendingTransfer.receiver.playerId)).data.data.name;
        const senderName = (await takaro.player.playerControllerGetOne(sender.playerId)).data.data.name;
        const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', gameServerId)).data.data.value;
        const messageToSender = sender.pm(`You successfully transferred ${pendingTransfer.amount} ${currencyName} to ${receiverName}`);
        const messageToReceiver = takaro.gameserver.gameServerControllerSendMessage(gameServerId, {
            message: `You received ${pendingTransfer.amount} ${currencyName} from ${senderName}`,
            opts: {
                recipient: {
                    gameId: pendingTransfer.receiver.gameId,
                },
            },
        });
        await Promise.all([messageToSender, messageToReceiver]);
        return;
    }
    await main();
    //# sourceMappingURL=confirmTransfer.js.map
  • transfer

    Transfer money to another player.

    ArgumentTypeDefaultHelp
    receiver player — The player to transfer money to.
    amount number — The amount of money to transfer.
    Command source
    import { takaro, data, TakaroUserError } from '@takaro/helpers';
    async function main() {
        const { pog: sender, arguments: args, gameServerId, module: mod } = data;
        const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', gameServerId)).data.data.value;
        const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;
        // args.receiver has an argument type of "player". Arguments of this type are automatically resolved to the player's id.
        // If the player doesn't exist or multiple players with the same name where found, it will have thrown an error before this command is executed.
        const receiver = args.receiver;
        const senderName = (await takaro.player.playerControllerGetOne(sender.playerId)).data.data.name;
        const receiverName = (await takaro.player.playerControllerGetOne(receiver.playerId)).data.data.name;
        if (mod.userConfig.pendingAmount !== 0 && args.amount >= mod.userConfig.pendingAmount) {
            // create a variable to store confirmation requirement
            // TODO: in the future, we should probably add an expiration date to this variable.
            await takaro.variable.variableControllerCreate({
                key: 'confirmTransfer',
                value: JSON.stringify({
                    amount: args.amount,
                    receiver: {
                        id: receiver.id,
                        gameId: receiver.gameId,
                        playerId: receiver.playerId,
                    },
                }),
                moduleId: mod.moduleId,
                playerId: sender.playerId,
                gameServerId,
            });
            // NOTE: we should maybe check if the player has enough balance to send the amount since this is only checked when the transaction is executed.
            await sender.pm(`You are about to send ${args.amount} ${currencyName} to ${receiverName}. (Please confirm by typing ${prefix}confirmtransfer)`);
            return;
        }
        try {
            await takaro.playerOnGameserver.playerOnGameServerControllerTransactBetweenPlayers(sender.gameServerId, sender.id, receiver.id, {
                currency: args.amount,
                reason: 'Player transfer',
            });
        }
        catch {
            throw new TakaroUserError(`Failed to transfer ${args.amount} ${currencyName} to ${receiverName}. Are you sure you have enough balance?`);
        }
        const messageToReceiver = takaro.gameserver.gameServerControllerSendMessage(gameServerId, {
            message: `You received ${args.amount} ${currencyName} from ${senderName}`,
            opts: {
                recipient: {
                    gameId: receiver.gameId,
                },
            },
        });
        await Promise.all([
            sender.pm(`You successfully transferred ${args.amount} ${currencyName} to ${receiverName}`),
            messageToReceiver,
        ]);
        return;
    }
    await main();
    //# sourceMappingURL=transfer.js.map
  • claim

    Claim your pending shop orders.

    ArgumentTypeDefaultHelp
    all boolean false If true, claim ALL pending orders. If false, claim only the first one.
    Command source
    import { takaro, data, TakaroUserError } from '@takaro/helpers';
    async function main() {
        const { user, player, arguments: args, gameServerId } = data;
        if (!user) {
            throw new TakaroUserError('You must link your account to Takaro to use this command.');
        }
        const filters = {
            userId: [user.id],
            status: ['PAID'],
        };
        // Only filter by gameServerId if it's available (should always be present in command context)
        if (gameServerId) {
            filters.gameServerId = [gameServerId];
        }
        const pendingOrdersRes = await takaro.shopOrder.shopOrderControllerSearch({
            filters,
            sortBy: 'createdAt',
            sortDirection: 'asc',
        });
        if (pendingOrdersRes.data.data.length === 0) {
            await player.pm('You have no pending orders.');
            return;
        }
        let ordersToClaim = [];
        if (args.all) {
            ordersToClaim = pendingOrdersRes.data.data;
        }
        else {
            ordersToClaim.push(pendingOrdersRes.data.data[0]);
        }
        for (const order of ordersToClaim) {
            await takaro.shopOrder.shopOrderControllerClaim(order.id);
        }
    }
    await main();
    //# sourceMappingURL=claim.js.map
  • shop

    Browse the shop and view available items.

    ArgumentTypeDefaultHelp
    page number 1 Display more items from the shop by specifying a page number.
    item number 0 Select a specific item to view more details.
    action string none Perform an action on the selected item. Currently only "buy" is supported.
    Command source
    import { takaro, data, TakaroUserError } from '@takaro/helpers';
    async function main() {
        const { arguments: args, player, gameServerId } = data;
        const { page, item, action } = args;
        const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;
        // If command is called without any arguments
        const messageWithoutPrefix = data.chatMessage.msg.slice(prefix.length).trim();
        if (!messageWithoutPrefix.includes(' ')) {
            await player.pm('This command allows you to browse the shop and view available items.');
            await player.pm(`Usage: ${prefix}shop [page] [item] [action]`);
            await player.pm(`${prefix}shop 2 - View the second page of shop items`);
            await player.pm(`${prefix}shop 1 3 - View details about the third item on the first page`);
            await player.pm(`${prefix}shop 1 3 buy - Purchase the third item on the first page`);
            return;
        }
        const shopItems = await takaro.shopListing.shopListingControllerSearch({
            limit: 5,
            page: page - 1,
            sortBy: 'name',
            sortDirection: 'asc',
            filters: {
                gameServerId: [gameServerId],
                draft: false,
            },
        });
        if (shopItems.data.data.length === 0) {
            await player.pm('No items found.');
            return;
        }
        const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', data.gameServerId)).data.data;
        if (!item) {
            // List the shop items with index
            let index = 1;
            for (const listing of shopItems.data.data) {
                const items = listing.items.slice(0, 3).map((item) => {
                    return `${item.amount}x ${item.item.name}`;
                });
                await player.pm(`${index} - ${listing.name} - ${listing.price} ${currencyName.value}. ${items.join(', ')}`);
                index++;
            }
            return;
        }
        const selectedItem = shopItems.data.data[item - 1];
        if (!selectedItem) {
            throw new TakaroUserError(`Item not found. Please select an item from the list, valid options are 1-${shopItems.data.data.length}.`);
        }
        if (action === 'none') {
            // Display more info about the item
            await player.pm(`Listing ${selectedItem.name} - ${selectedItem.price} ${currencyName.value}`);
            await Promise.all(selectedItem.items.map((item) => {
                const quality = item.quality ? `Quality: ${item.quality}` : '';
                const description = (item.item.description ? `Description: ${item.item.description}` : '').replaceAll('\\n', ' ');
                return player.pm(`- ${item.amount}x ${item.item.name}. ${quality} ${description}`);
            }));
            return;
        }
        if (action === 'buy') {
            const orderRes = await takaro.shopOrder.shopOrderControllerCreate({
                amount: 1,
                listingId: selectedItem.id,
                playerId: player.id,
            });
            await player.pm(`You have purchased ${selectedItem.name} for ${selectedItem.price} ${currencyName.value}.`);
            await takaro.shopOrder.shopOrderControllerClaim(orderRes.data.data.id);
            return;
        }
        throw new TakaroUserError('Invalid action. Valid actions are "buy".');
    }
    await main();
    //# sourceMappingURL=shop.js.map

Cron jobs 1

Work the module runs on a schedule.

  • zombieKillReward

    Cron job source
    import { data, takaro, checkPermission } from '@takaro/helpers';
    const VARIABLE_KEY = 'lastZombieKillReward';
    async function main() {
        const { gameServerId, module: mod } = data;
        const lastRunRes = (await takaro.variable.variableControllerSearch({
            filters: {
                key: [VARIABLE_KEY],
                gameServerId: [gameServerId],
                moduleId: [mod.moduleId],
            },
        })).data.data;
        // We last ran the rewards script at this time
        // If this is the first time we run it, just get the last 5 minutes
        const lastRun = lastRunRes.length ? new Date(JSON.parse(lastRunRes[0].value)) : new Date(Date.now() - 5 * 60 * 1000);
        // Fetch all the kill events since the last time we gave out rewards
        const killEvents = (await takaro.event.eventControllerSearch({
            filters: { eventName: ['entity-killed'], gameserverId: [gameServerId] },
            greaterThan: { createdAt: lastRun.toISOString() },
            limit: 1000,
        })).data.data;
        console.log(`Found ${killEvents.length} kill events since ${lastRun.toISOString()}`);
        // Group the events by player
        const playerKills = {};
        for (const killEvent of killEvents) {
            if (!playerKills[killEvent.playerId]) {
                playerKills[killEvent.playerId] = [];
            }
            playerKills[killEvent.playerId].push(killEvent);
        }
        // Give each player their reward
        // We use Promise.allSettled to run this concurrently
        const results = await Promise.allSettled(Object.entries(playerKills).map(async ([playerId, kills]) => {
            const pog = (await takaro.playerOnGameserver.playerOnGameServerControllerGetOne(gameServerId, playerId)).data
                .data;
            const hasPermission = checkPermission(pog, 'ZOMBIE_KILL_REWARD_OVERRIDE');
            const defaultReward = mod.userConfig.zombieKillReward;
            const reward = hasPermission && hasPermission.count !== null && hasPermission.count !== undefined
                ? hasPermission.count
                : defaultReward;
            const totalReward = reward * kills.length;
            return takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, playerId, {
                currency: totalReward,
                reason: 'Zombie kill reward',
            });
        }));
        // Log any errors
        for (const result of results) {
            if (result.status === 'rejected') {
                console.error(result.reason);
                throw new Error(`Failed to give rewards: ${result.reason}`);
            }
        }
        // Update the last run time
        if (lastRunRes.length) {
            await takaro.variable.variableControllerUpdate(lastRunRes[0].id, {
                value: JSON.stringify(new Date()),
            });
        }
        else {
            await takaro.variable.variableControllerCreate({
                key: VARIABLE_KEY,
                value: JSON.stringify(new Date()),
                moduleId: mod.moduleId,
                gameServerId,
            });
        }
    }
    await main();
    //# sourceMappingURL=zombieKillReward.js.map

Permissions 2

Roles you can grant to decide who may use what.

  • Manage currency

    Allows players to manage currency of other players. This includes granting and revoking currency.

  • Zombie kill reward override

    Allows a role to override the amount of currency a player receives for killing a entity.