economyUtils
- builtin
- by Takaro
- Takaro >=0.0.1
- all
Built-in modules ship with Takaro — no import needed.
A set of commands to allow players to manage their currency.
Configuration 2
Settings you fill in when installing the module on a server.
| Setting | Type | Default | Description |
|---|---|---|---|
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.
Argument Type Default Help receiverplayer — The player to grant currency to. amountnumber — 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.
Argument Type Default Help receiverplayer — The player to revoke currency from. amountnumber — 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.
Argument Type Default Help receiverplayer — The player to transfer money to. amountnumber — 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.
Argument Type Default Help allboolean 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.
Argument Type Default Help pagenumber 1 Display more items from the shop by specifying a page number. itemnumber 0 Select a specific item to view more details. actionstring 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.