economyUtils

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

Built-in modules ship with Takaro — no import 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.