@takaro/modules 0.0.11 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,18 @@
1
1
  import { takaro, data, TakaroUserError } from '@takaro/helpers';
2
2
  async function main() {
3
- const { arguments: args, player, gameServerId, user } = data;
3
+ const { arguments: args, player, gameServerId } = data;
4
4
  const { page, item, action } = args;
5
+ const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;
6
+ // If command is called without any arguments
7
+ const messageWithoutPrefix = data.chatMessage.msg.slice(prefix.length).trim();
8
+ if (!messageWithoutPrefix.includes(' ')) {
9
+ await player.pm('This command allows you to browse the shop and view available items.');
10
+ await player.pm(`Usage: ${prefix}shop [page] [item] [action]`);
11
+ await player.pm(`${prefix}shop 2 - View the second page of shop items`);
12
+ await player.pm(`${prefix}shop 1 3 - View details about the third item on the first page`);
13
+ await player.pm(`${prefix}shop 1 3 buy - Purchase the third item on the first page`);
14
+ return;
15
+ }
5
16
  const shopItems = await takaro.shopListing.shopListingControllerSearch({
6
17
  limit: 5,
7
18
  page: page - 1,
@@ -18,12 +29,14 @@ async function main() {
18
29
  }
19
30
  const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', data.gameServerId)).data.data;
20
31
  if (!item) {
21
- // List the shop items
32
+ // List the shop items with index
33
+ let index = 1;
22
34
  for (const listing of shopItems.data.data) {
23
35
  const items = listing.items.slice(0, 3).map((item) => {
24
36
  return `${item.amount}x ${item.item.name}`;
25
37
  });
26
- await player.pm(`- ${listing.name} - ${listing.price} ${currencyName.value}. ${items.join(', ')}`);
38
+ await player.pm(`${index} - ${listing.name} - ${listing.price} ${currencyName.value}. ${items.join(', ')}`);
39
+ index++;
27
40
  }
28
41
  return;
29
42
  }
@@ -41,14 +54,13 @@ async function main() {
41
54
  return;
42
55
  }
43
56
  if (action === 'buy') {
44
- if (!user)
45
- throw new TakaroUserError('You must link your account to Takaro to use this command.');
46
- await takaro.shopOrder.shopOrderControllerCreate({
57
+ const orderRes = await takaro.shopOrder.shopOrderControllerCreate({
47
58
  amount: 1,
48
59
  listingId: selectedItem.id,
49
- userId: user.id,
60
+ playerId: player.id,
50
61
  });
51
- await player.pm(`You have purchased ${selectedItem.name} for ${selectedItem.price} ${currencyName.value}. You can now claim your items.`);
62
+ await player.pm(`You have purchased ${selectedItem.name} for ${selectedItem.price} ${currencyName.value}.`);
63
+ await takaro.shopOrder.shopOrderControllerClaim(orderRes.data.data.id);
52
64
  return;
53
65
  }
54
66
  throw new TakaroUserError('Invalid action. Valid actions are "buy".');
@@ -1 +1 @@
1
- {"version":3,"file":"shop.js","sourceRoot":"","sources":["../../../../src/modules/economyUtils/commands/shop.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shop.js","sourceRoot":"","sources":["../../../../src/modules/economyUtils/commands/shop.js"],"names":[],"mappings":"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"}
@@ -34,7 +34,7 @@ async function main() {
34
34
  currency: args.amount,
35
35
  });
36
36
  }
37
- catch (_e) {
37
+ catch {
38
38
  throw new TakaroUserError(`Failed to transfer ${args.amount} ${currencyName} to ${receiverName}. Are you sure you have enough balance?`);
39
39
  }
40
40
  const messageToReceiver = takaro.gameserver.gameServerControllerSendMessage(gameServerId, {
@@ -1 +1 @@
1
- {"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../../../src/modules/economyUtils/commands/transfer.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../../../src/modules/economyUtils/commands/transfer.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEhE,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAEzE,MAAM,YAAY,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;IAEpH,MAAM,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;IAE/G,wHAAwH;IACxH,gJAAgJ;IAChJ,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC/B,MAAM,UAAU,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IAChG,MAAM,YAAY,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IAEpG,IAAI,GAAG,CAAC,UAAU,CAAC,aAAa,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC;QACtF,sDAAsD;QACtD,mFAAmF;QACnF,MAAM,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAAC;YAC7C,GAAG,EAAE,iBAAiB;YACtB,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC;gBACpB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,QAAQ,EAAE;oBACR,EAAE,EAAE,QAAQ,CAAC,EAAE;oBACf,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;iBAC5B;aACF,CAAC;YACF,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,YAAY;SACb,CAAC,CAAC;QAEH,+IAA+I;QAC/I,MAAM,MAAM,CAAC,EAAE,CACb,yBAAyB,IAAI,CAAC,MAAM,IAAI,YAAY,OAAO,YAAY,+BAA+B,MAAM,kBAAkB,CAC/H,CAAC;QACF,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,kBAAkB,CAAC,kDAAkD,CAChF,MAAM,CAAC,YAAY,EACnB,MAAM,CAAC,EAAE,EACT,QAAQ,CAAC,EAAE,EACX;YACE,QAAQ,EAAE,IAAI,CAAC,MAAM;SACtB,CACF,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,eAAe,CACvB,sBAAsB,IAAI,CAAC,MAAM,IAAI,YAAY,OAAO,YAAY,yCAAyC,CAC9G,CAAC;IACJ,CAAC;IAED,MAAM,iBAAiB,GAAG,MAAM,CAAC,UAAU,CAAC,+BAA+B,CAAC,YAAY,EAAE;QACxF,OAAO,EAAE,gBAAgB,IAAI,CAAC,MAAM,IAAI,YAAY,SAAS,UAAU,EAAE;QACzE,IAAI,EAAE;YACJ,SAAS,EAAE;gBACT,MAAM,EAAE,QAAQ,CAAC,MAAM;aACxB;SACF;KACF,CAAC,CAAC;IAEH,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,MAAM,CAAC,EAAE,CAAC,gCAAgC,IAAI,CAAC,MAAM,IAAI,YAAY,OAAO,YAAY,EAAE,CAAC;QAC3F,iBAAiB;KAClB,CAAC,CAAC;IAEH,OAAO;AACT,CAAC;AAED,MAAM,IAAI,EAAE,CAAC"}
@@ -0,0 +1,66 @@
1
+ import { data, takaro, checkPermission } from '@takaro/helpers';
2
+ const VARIABLE_KEY = 'lastZombieKillReward';
3
+ async function main() {
4
+ const { gameServerId, module: mod } = data;
5
+ const lastRunRes = (await takaro.variable.variableControllerSearch({
6
+ filters: {
7
+ key: [VARIABLE_KEY],
8
+ gameServerId: [gameServerId],
9
+ moduleId: [mod.moduleId],
10
+ },
11
+ })).data.data;
12
+ // We last ran the rewards script at this time
13
+ // If this is the first time we run it, just get the last 5 minutes
14
+ const lastRun = lastRunRes.length ? new Date(JSON.parse(lastRunRes[0].value)) : new Date(Date.now() - 5 * 60 * 1000);
15
+ // Fetch all the kill events since the last time we gave out rewards
16
+ const killEvents = (await takaro.event.eventControllerSearch({
17
+ filters: { eventName: ['entity-killed'], gameserverId: [gameServerId] },
18
+ greaterThan: { createdAt: lastRun.toISOString() },
19
+ limit: 1000,
20
+ })).data.data;
21
+ console.log(`Found ${killEvents.length} kill events since ${lastRun.toISOString()}`);
22
+ // Group the events by player
23
+ const playerKills = {};
24
+ for (const killEvent of killEvents) {
25
+ if (!playerKills[killEvent.playerId]) {
26
+ playerKills[killEvent.playerId] = [];
27
+ }
28
+ playerKills[killEvent.playerId].push(killEvent);
29
+ }
30
+ // Give each player their reward
31
+ // We use Promise.allSettled to run this concurrently
32
+ const results = await Promise.allSettled(Object.entries(playerKills).map(async ([playerId, kills]) => {
33
+ const pog = (await takaro.playerOnGameserver.playerOnGameServerControllerGetOne(gameServerId, playerId)).data
34
+ .data;
35
+ const hasPermission = checkPermission(pog, 'ZOMBIE_KILL_REWARD_OVERRIDE');
36
+ const defaultReward = mod.userConfig.zombieKillReward;
37
+ const reward = hasPermission && hasPermission.count != null ? hasPermission.count : defaultReward;
38
+ const totalReward = reward * kills.length;
39
+ return takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, playerId, {
40
+ currency: totalReward,
41
+ });
42
+ }));
43
+ // Log any errors
44
+ for (const result of results) {
45
+ if (result.status === 'rejected') {
46
+ console.error(result.reason);
47
+ throw new Error(`Failed to give rewards: ${result.reason}`);
48
+ }
49
+ }
50
+ // Update the last run time
51
+ if (lastRunRes.length) {
52
+ await takaro.variable.variableControllerUpdate(lastRunRes[0].id, {
53
+ value: JSON.stringify(new Date()),
54
+ });
55
+ }
56
+ else {
57
+ await takaro.variable.variableControllerCreate({
58
+ key: VARIABLE_KEY,
59
+ value: JSON.stringify(new Date()),
60
+ moduleId: mod.moduleId,
61
+ gameServerId,
62
+ });
63
+ }
64
+ }
65
+ await main();
66
+ //# sourceMappingURL=zombieKillReward.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zombieKillReward.js","sourceRoot":"","sources":["../../../../src/modules/economyUtils/cronJobs/zombieKillReward.js"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { BuiltinModule, ICommand, IPermission } from '../../BuiltinModule.js';
1
+ import { BuiltinModule, ICommand, ICronJob, IPermission } from '../../BuiltinModule.js';
2
2
  export class EconomyUtils extends BuiltinModule {
3
3
  constructor() {
4
4
  super('economyUtils', 'A set of commands to allow players to manage their currency.', JSON.stringify({
@@ -11,6 +11,12 @@ export class EconomyUtils extends BuiltinModule {
11
11
  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.',
12
12
  default: 0,
13
13
  },
14
+ zombieKillReward: {
15
+ title: 'Zombie kill reward',
16
+ type: 'number',
17
+ description: 'The default amount of currency a player receives for killing a zombie. This can be overridden by roles.',
18
+ default: 1,
19
+ },
14
20
  },
15
21
  required: [],
16
22
  additionalProperties: false,
@@ -22,6 +28,19 @@ export class EconomyUtils extends BuiltinModule {
22
28
  description: 'Allows players to manage currency of other players. This includes granting and revoking currency.',
23
29
  canHaveCount: false,
24
30
  }),
31
+ new IPermission({
32
+ permission: 'ZOMBIE_KILL_REWARD_OVERRIDE',
33
+ friendlyName: 'Zombie kill reward override',
34
+ description: 'Allows a role to override the amount of currency a player receives for killing a entity.',
35
+ canHaveCount: true,
36
+ }),
37
+ ];
38
+ this.cronJobs = [
39
+ new ICronJob({
40
+ function: this.loadFn('cronJobs', 'zombieKillReward'),
41
+ name: 'zombieKillReward',
42
+ temporalValue: '*/5 * * * *',
43
+ }),
25
44
  ];
26
45
  this.commands = [
27
46
  new ICommand({
@@ -64,7 +83,7 @@ export class EconomyUtils extends BuiltinModule {
64
83
  function: this.loadFn('commands', 'revokeCurrency'),
65
84
  name: 'revokeCurrency',
66
85
  trigger: 'revokecurrency',
67
- helpText: 'Grant money to a player. The money is not taken from your own balance but is new currency.',
86
+ helpText: 'Revokes money from a player. The money disappears.',
68
87
  arguments: [
69
88
  {
70
89
  name: 'receiver',
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/economyUtils/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/economyUtils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAExF,MAAM,OAAO,YAAa,SAAQ,aAA2B;IAC3D;QACE,KAAK,CACH,cAAc,EACd,8DAA8D,EAC9D,IAAI,CAAC,SAAS,CAAC;YACb,OAAO,EAAE,yCAAyC;YAClD,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,aAAa,EAAE;oBACb,KAAK,EAAE,gBAAgB;oBACvB,IAAI,EAAE,QAAQ;oBACd,WAAW,EACT,kIAAkI;oBACpI,OAAO,EAAE,CAAC;iBACX;gBACD,gBAAgB,EAAE;oBAChB,KAAK,EAAE,oBAAoB;oBAC3B,IAAI,EAAE,QAAQ;oBACd,WAAW,EACT,yGAAyG;oBAC3G,OAAO,EAAE,CAAC;iBACX;aACF;YACD,QAAQ,EAAE,EAAE;YACZ,oBAAoB,EAAE,KAAK;SAC5B,CAAC,CACH,CAAC;QAEF,IAAI,CAAC,WAAW,GAAG;YACjB,IAAI,WAAW,CAAC;gBACd,UAAU,EAAE,+BAA+B;gBAC3C,YAAY,EAAE,iBAAiB;gBAC/B,WAAW,EACT,mGAAmG;gBACrG,YAAY,EAAE,KAAK;aACpB,CAAC;YACF,IAAI,WAAW,CAAC;gBACd,UAAU,EAAE,6BAA6B;gBACzC,YAAY,EAAE,6BAA6B;gBAC3C,WAAW,EAAE,0FAA0F;gBACvG,YAAY,EAAE,IAAI;aACnB,CAAC;SACH,CAAC;QAEF,IAAI,CAAC,QAAQ,GAAG;YACd,IAAI,QAAQ,CAAC;gBACX,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,kBAAkB,CAAC;gBACrD,IAAI,EAAE,kBAAkB;gBACxB,aAAa,EAAE,aAAa;aAC7B,CAAC;SACH,CAAC;QAEF,IAAI,CAAC,QAAQ,GAAG;YACd,IAAI,QAAQ,CAAC;gBACX,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC;gBAC5C,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,SAAS;gBAClB,QAAQ,EAAE,qBAAqB;gBAC/B,SAAS,EAAE,EAAE;aACd,CAAC;YACF,IAAI,QAAQ,CAAC;gBACX,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC;gBAChD,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,aAAa;gBACtB,QAAQ,EAAE,kDAAkD;gBAC5D,SAAS,EAAE,EAAE;aACd,CAAC;YACF,IAAI,QAAQ,CAAC;gBACX,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC;gBAClD,IAAI,EAAE,eAAe;gBACrB,OAAO,EAAE,eAAe;gBACxB,QAAQ,EAAE,4FAA4F;gBACtG,SAAS,EAAE;oBACT;wBACE,IAAI,EAAE,UAAU;wBAChB,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,kCAAkC;wBAC5C,QAAQ,EAAE,CAAC;wBACX,YAAY,EAAE,IAAI;qBACnB;oBACD;wBACE,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,sBAAsB;wBAChC,QAAQ,EAAE,CAAC;wBACX,YAAY,EAAE,IAAI;qBACnB;iBACF;aACF,CAAC;YACF,IAAI,QAAQ,CAAC;gBACX,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC;gBACnD,IAAI,EAAE,gBAAgB;gBACtB,OAAO,EAAE,gBAAgB;gBACzB,QAAQ,EAAE,oDAAoD;gBAC9D,SAAS,EAAE;oBACT;wBACE,IAAI,EAAE,UAAU;wBAChB,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,qCAAqC;wBAC/C,QAAQ,EAAE,CAAC;wBACX,YAAY,EAAE,IAAI;qBACnB;oBACD;wBACE,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,sBAAsB;wBAChC,QAAQ,EAAE,CAAC;wBACX,YAAY,EAAE,IAAI;qBACnB;iBACF;aACF,CAAC;YACF,IAAI,QAAQ,CAAC;gBACX,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAAC;gBACpD,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,iBAAiB;gBAC1B,QAAQ,EAAE,8BAA8B;gBACxC,SAAS,EAAE,EAAE;aACd,CAAC;YACF,IAAI,QAAQ,CAAC;gBACX,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC;gBAC7C,IAAI,EAAE,UAAU;gBAChB,OAAO,EAAE,UAAU;gBACnB,QAAQ,EAAE,mCAAmC;gBAC7C,SAAS,EAAE;oBACT;wBACE,IAAI,EAAE,UAAU;wBAChB,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,kCAAkC;wBAC5C,QAAQ,EAAE,CAAC;wBACX,YAAY,EAAE,IAAI;qBACnB;oBACD;wBACE,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,kCAAkC;wBAC5C,QAAQ,EAAE,CAAC;wBACX,YAAY,EAAE,IAAI;qBACnB;iBACF;aACF,CAAC;YACF,IAAI,QAAQ,CAAC;gBACX,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC;gBAC1C,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,OAAO;gBAChB,QAAQ,EAAE,iCAAiC;gBAC3C,SAAS,EAAE;oBACT;wBACE,IAAI,EAAE,KAAK;wBACX,IAAI,EAAE,SAAS;wBACf,QAAQ,EAAE,wEAAwE;wBAClF,QAAQ,EAAE,CAAC;wBACX,YAAY,EAAE,OAAO;qBACtB;iBACF;aACF,CAAC;YACF,IAAI,QAAQ,CAAC;gBACX,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC;gBACzC,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,2CAA2C;gBACrD,SAAS,EAAE;oBACT;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,+DAA+D;wBACzE,QAAQ,EAAE,CAAC;wBACX,YAAY,EAAE,GAAG;qBAClB;oBACD;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,8CAA8C;wBACxD,QAAQ,EAAE,CAAC;wBACX,YAAY,EAAE,GAAG;qBAClB;oBACD;wBACE,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,4EAA4E;wBACtF,QAAQ,EAAE,CAAC;wBACX,YAAY,EAAE,MAAM;qBACrB;iBACF;aACF,CAAC;SACH,CAAC;IACJ,CAAC;CACF"}
@@ -12,7 +12,7 @@ async function main() {
12
12
  await takaro.gameserver.gameServerControllerGiveItem(data.gameServerId, data.player.id, {
13
13
  name: randomOption,
14
14
  amount: 1,
15
- quality: 0,
15
+ quality: '0',
16
16
  });
17
17
  await data.player.pm(`You received ${randomOption}!`);
18
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gimme.js","sourceRoot":"","sources":["../../../../src/modules/gimme/commands/gimme.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEhE,KAAK,UAAU,IAAI;IACjB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC;IAEjD,IAAI,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,eAAe,CACvB,iGAAiG,CAClG,CAAC;IACJ,CAAC;IAED,oFAAoF;IACpF,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACjF,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAC;IAEzD,IAAI,WAAW,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,MAAM,CAAC,UAAU,CAAC,4BAA4B,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE;YACtF,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,CAAC;YACT,OAAO,EAAE,CAAC;SACX,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,gBAAgB,YAAY,GAAG,CAAC,CAAC;IACxD,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,CAAC,UAAU,CAAC,kCAAkC,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;IAC3G,CAAC;AACH,CAAC;AAED,MAAM,IAAI,EAAE,CAAC"}
1
+ {"version":3,"file":"gimme.js","sourceRoot":"","sources":["../../../../src/modules/gimme/commands/gimme.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEhE,KAAK,UAAU,IAAI;IACjB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC;IAEjD,IAAI,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,eAAe,CACvB,iGAAiG,CAClG,CAAC;IACJ,CAAC;IAED,oFAAoF;IACpF,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACjF,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAC;IAEzD,IAAI,WAAW,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,MAAM,CAAC,UAAU,CAAC,4BAA4B,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE;YACtF,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,CAAC;YACT,OAAO,EAAE,GAAG;SACb,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,gBAAgB,YAAY,GAAG,CAAC,CAAC;IACxD,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,CAAC,UAAU,CAAC,kCAAkC,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;IAC3G,CAAC;AACH,CAAC;AAED,MAAM,IAAI,EAAE,CAAC"}
package/dist/modules.json CHANGED
@@ -272,7 +272,7 @@
272
272
  {
273
273
  "commands": [
274
274
  {
275
- "function": "import { takaro, data, TakaroUserError } from '@takaro/helpers';\nasync function main() {\n const items = data.module.userConfig.items;\n const commands = data.module.userConfig.commands;\n if (items.length + commands.length === 0) {\n throw new TakaroUserError('No items or commands configured, please ask your server administrator to configure this module.');\n }\n // pick a random item between 0 and the length of both the items and commands arrays\n const randomIndex = Math.floor(Math.random() * (items.length + commands.length));\n const randomOption = items.concat(commands)[randomIndex];\n if (randomIndex < items.length) {\n await takaro.gameserver.gameServerControllerGiveItem(data.gameServerId, data.player.id, {\n name: randomOption,\n amount: 1,\n quality: 0,\n });\n await data.player.pm(`You received ${randomOption}!`);\n }\n else {\n await takaro.gameserver.gameServerControllerExecuteCommand(data.gameServerId, { command: randomOption });\n }\n}\nawait main();\n//# sourceMappingURL=gimme.js.map",
275
+ "function": "import { takaro, data, TakaroUserError } from '@takaro/helpers';\nasync function main() {\n const items = data.module.userConfig.items;\n const commands = data.module.userConfig.commands;\n if (items.length + commands.length === 0) {\n throw new TakaroUserError('No items or commands configured, please ask your server administrator to configure this module.');\n }\n // pick a random item between 0 and the length of both the items and commands arrays\n const randomIndex = Math.floor(Math.random() * (items.length + commands.length));\n const randomOption = items.concat(commands)[randomIndex];\n if (randomIndex < items.length) {\n await takaro.gameserver.gameServerControllerGiveItem(data.gameServerId, data.player.id, {\n name: randomOption,\n amount: 1,\n quality: '0',\n });\n await data.player.pm(`You received ${randomOption}!`);\n }\n else {\n await takaro.gameserver.gameServerControllerExecuteCommand(data.gameServerId, { command: randomOption });\n }\n}\nawait main();\n//# sourceMappingURL=gimme.js.map",
276
276
  "name": "gimme",
277
277
  "trigger": "gimme",
278
278
  "helpText": "Randomly selects item from a list of items and entities.",
@@ -347,7 +347,7 @@
347
347
  "function": "import { takaro, data, checkPermission, TakaroUserError } from '@takaro/helpers';\nasync function main() {\n const { pog: revoker, arguments: args, gameServerId } = data;\n // args.receiver has an argument type of \"player\". Arguments of this type are automatically resolved to the player's id.\n // 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.\n const receiver = args.receiver;\n if (!checkPermission(revoker, 'ECONOMY_UTILS_MANAGE_CURRENCY')) {\n throw new TakaroUserError('You do not have permission to use revoke currency command.');\n }\n const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', gameServerId)).data.data.value;\n const revokerName = (await takaro.player.playerControllerGetOne(revoker.playerId)).data.data.name;\n const receiverName = (await takaro.player.playerControllerGetOne(receiver.playerId)).data.data.name;\n await takaro.playerOnGameserver.playerOnGameServerControllerDeductCurrency(receiver.gameServerId, receiver.playerId, {\n currency: args.amount,\n });\n const messageToReceiver = takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n message: `${args.amount} ${currencyName} were revoked by ${revokerName}`,\n opts: {\n recipient: {\n gameId: receiver.gameId,\n },\n },\n });\n await Promise.all([\n revoker.pm(`You successfully revoked ${args.amount} ${currencyName} of ${receiverName}'s balance`),\n messageToReceiver,\n ]);\n return;\n}\nawait main();\n//# sourceMappingURL=revokeCurrency.js.map",
348
348
  "name": "revokeCurrency",
349
349
  "trigger": "revokecurrency",
350
- "helpText": "Grant money to a player. The money is not taken from your own balance but is new currency.",
350
+ "helpText": "Revokes money from a player. The money disappears.",
351
351
  "arguments": [
352
352
  {
353
353
  "name": "receiver",
@@ -373,7 +373,7 @@
373
373
  "arguments": []
374
374
  },
375
375
  {
376
- "function": "import { takaro, data, TakaroUserError } from '@takaro/helpers';\nasync function main() {\n const { pog: sender, arguments: args, gameServerId, module: mod } = data;\n const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', gameServerId)).data.data.value;\n const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;\n // args.receiver has an argument type of \"player\". Arguments of this type are automatically resolved to the player's id.\n // 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.\n const receiver = args.receiver;\n const senderName = (await takaro.player.playerControllerGetOne(sender.playerId)).data.data.name;\n const receiverName = (await takaro.player.playerControllerGetOne(receiver.playerId)).data.data.name;\n if (mod.userConfig.pendingAmount !== 0 && args.amount >= mod.userConfig.pendingAmount) {\n // create a variable to store confirmation requirement\n // TODO: in the future, we should probably add an expiration date to this variable.\n await takaro.variable.variableControllerCreate({\n key: 'confirmTransfer',\n value: JSON.stringify({\n amount: args.amount,\n receiver: {\n id: receiver.id,\n gameId: receiver.gameId,\n playerId: receiver.playerId,\n },\n }),\n moduleId: mod.moduleId,\n playerId: sender.playerId,\n gameServerId,\n });\n // 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.\n await sender.pm(`You are about to send ${args.amount} ${currencyName} to ${receiverName}. (Please confirm by typing ${prefix}confirmtransfer)`);\n return;\n }\n try {\n await takaro.playerOnGameserver.playerOnGameServerControllerTransactBetweenPlayers(sender.gameServerId, sender.id, receiver.id, {\n currency: args.amount,\n });\n }\n catch (_e) {\n throw new TakaroUserError(`Failed to transfer ${args.amount} ${currencyName} to ${receiverName}. Are you sure you have enough balance?`);\n }\n const messageToReceiver = takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n message: `You received ${args.amount} ${currencyName} from ${senderName}`,\n opts: {\n recipient: {\n gameId: receiver.gameId,\n },\n },\n });\n await Promise.all([\n sender.pm(`You successfully transferred ${args.amount} ${currencyName} to ${receiverName}`),\n messageToReceiver,\n ]);\n return;\n}\nawait main();\n//# sourceMappingURL=transfer.js.map",
376
+ "function": "import { takaro, data, TakaroUserError } from '@takaro/helpers';\nasync function main() {\n const { pog: sender, arguments: args, gameServerId, module: mod } = data;\n const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', gameServerId)).data.data.value;\n const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;\n // args.receiver has an argument type of \"player\". Arguments of this type are automatically resolved to the player's id.\n // 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.\n const receiver = args.receiver;\n const senderName = (await takaro.player.playerControllerGetOne(sender.playerId)).data.data.name;\n const receiverName = (await takaro.player.playerControllerGetOne(receiver.playerId)).data.data.name;\n if (mod.userConfig.pendingAmount !== 0 && args.amount >= mod.userConfig.pendingAmount) {\n // create a variable to store confirmation requirement\n // TODO: in the future, we should probably add an expiration date to this variable.\n await takaro.variable.variableControllerCreate({\n key: 'confirmTransfer',\n value: JSON.stringify({\n amount: args.amount,\n receiver: {\n id: receiver.id,\n gameId: receiver.gameId,\n playerId: receiver.playerId,\n },\n }),\n moduleId: mod.moduleId,\n playerId: sender.playerId,\n gameServerId,\n });\n // 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.\n await sender.pm(`You are about to send ${args.amount} ${currencyName} to ${receiverName}. (Please confirm by typing ${prefix}confirmtransfer)`);\n return;\n }\n try {\n await takaro.playerOnGameserver.playerOnGameServerControllerTransactBetweenPlayers(sender.gameServerId, sender.id, receiver.id, {\n currency: args.amount,\n });\n }\n catch {\n throw new TakaroUserError(`Failed to transfer ${args.amount} ${currencyName} to ${receiverName}. Are you sure you have enough balance?`);\n }\n const messageToReceiver = takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n message: `You received ${args.amount} ${currencyName} from ${senderName}`,\n opts: {\n recipient: {\n gameId: receiver.gameId,\n },\n },\n });\n await Promise.all([\n sender.pm(`You successfully transferred ${args.amount} ${currencyName} to ${receiverName}`),\n messageToReceiver,\n ]);\n return;\n}\nawait main();\n//# sourceMappingURL=transfer.js.map",
377
377
  "name": "transfer",
378
378
  "trigger": "transfer",
379
379
  "helpText": "Transfer money to another player.",
@@ -410,7 +410,7 @@
410
410
  ]
411
411
  },
412
412
  {
413
- "function": "import { takaro, data, TakaroUserError } from '@takaro/helpers';\nasync function main() {\n const { arguments: args, player, gameServerId, user } = data;\n const { page, item, action } = args;\n const shopItems = await takaro.shopListing.shopListingControllerSearch({\n limit: 5,\n page: page - 1,\n sortBy: 'name',\n sortDirection: 'asc',\n filters: {\n gameServerId: [gameServerId],\n draft: false,\n },\n });\n if (shopItems.data.data.length === 0) {\n await player.pm('No items found.');\n return;\n }\n const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', data.gameServerId)).data.data;\n if (!item) {\n // List the shop items\n for (const listing of shopItems.data.data) {\n const items = listing.items.slice(0, 3).map((item) => {\n return `${item.amount}x ${item.item.name}`;\n });\n await player.pm(`- ${listing.name} - ${listing.price} ${currencyName.value}. ${items.join(', ')}`);\n }\n return;\n }\n const selectedItem = shopItems.data.data[item - 1];\n if (!selectedItem)\n throw new TakaroUserError(`Item not found. Please select an item from the list, valid options are 1-${shopItems.data.data.length}.`);\n if (action === 'none') {\n // Display more info about the item\n await player.pm(`Listing ${selectedItem.name} - ${selectedItem.price} ${currencyName.value}`);\n await Promise.all(selectedItem.items.map((item) => {\n const quality = item.quality ? `Quality: ${item.quality}` : '';\n const description = (item.item.description ? `Description: ${item.item.description}` : '').replaceAll('\\\\n', ' ');\n return player.pm(`- ${item.amount}x ${item.item.name}. ${quality} ${description}`);\n }));\n return;\n }\n if (action === 'buy') {\n if (!user)\n throw new TakaroUserError('You must link your account to Takaro to use this command.');\n await takaro.shopOrder.shopOrderControllerCreate({\n amount: 1,\n listingId: selectedItem.id,\n userId: user.id,\n });\n await player.pm(`You have purchased ${selectedItem.name} for ${selectedItem.price} ${currencyName.value}. You can now claim your items.`);\n return;\n }\n throw new TakaroUserError('Invalid action. Valid actions are \"buy\".');\n}\nawait main();\n//# sourceMappingURL=shop.js.map",
413
+ "function": "import { takaro, data, TakaroUserError } from '@takaro/helpers';\nasync function main() {\n const { arguments: args, player, gameServerId } = data;\n const { page, item, action } = args;\n const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;\n // If command is called without any arguments\n const messageWithoutPrefix = data.chatMessage.msg.slice(prefix.length).trim();\n if (!messageWithoutPrefix.includes(' ')) {\n await player.pm('This command allows you to browse the shop and view available items.');\n await player.pm(`Usage: ${prefix}shop [page] [item] [action]`);\n await player.pm(`${prefix}shop 2 - View the second page of shop items`);\n await player.pm(`${prefix}shop 1 3 - View details about the third item on the first page`);\n await player.pm(`${prefix}shop 1 3 buy - Purchase the third item on the first page`);\n return;\n }\n const shopItems = await takaro.shopListing.shopListingControllerSearch({\n limit: 5,\n page: page - 1,\n sortBy: 'name',\n sortDirection: 'asc',\n filters: {\n gameServerId: [gameServerId],\n draft: false,\n },\n });\n if (shopItems.data.data.length === 0) {\n await player.pm('No items found.');\n return;\n }\n const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', data.gameServerId)).data.data;\n if (!item) {\n // List the shop items with index\n let index = 1;\n for (const listing of shopItems.data.data) {\n const items = listing.items.slice(0, 3).map((item) => {\n return `${item.amount}x ${item.item.name}`;\n });\n await player.pm(`${index} - ${listing.name} - ${listing.price} ${currencyName.value}. ${items.join(', ')}`);\n index++;\n }\n return;\n }\n const selectedItem = shopItems.data.data[item - 1];\n if (!selectedItem)\n throw new TakaroUserError(`Item not found. Please select an item from the list, valid options are 1-${shopItems.data.data.length}.`);\n if (action === 'none') {\n // Display more info about the item\n await player.pm(`Listing ${selectedItem.name} - ${selectedItem.price} ${currencyName.value}`);\n await Promise.all(selectedItem.items.map((item) => {\n const quality = item.quality ? `Quality: ${item.quality}` : '';\n const description = (item.item.description ? `Description: ${item.item.description}` : '').replaceAll('\\\\n', ' ');\n return player.pm(`- ${item.amount}x ${item.item.name}. ${quality} ${description}`);\n }));\n return;\n }\n if (action === 'buy') {\n const orderRes = await takaro.shopOrder.shopOrderControllerCreate({\n amount: 1,\n listingId: selectedItem.id,\n playerId: player.id,\n });\n await player.pm(`You have purchased ${selectedItem.name} for ${selectedItem.price} ${currencyName.value}.`);\n await takaro.shopOrder.shopOrderControllerClaim(orderRes.data.data.id);\n return;\n }\n throw new TakaroUserError('Invalid action. Valid actions are \"buy\".');\n}\nawait main();\n//# sourceMappingURL=shop.js.map",
414
414
  "name": "shop",
415
415
  "trigger": "shop",
416
416
  "helpText": "Browse the shop and view available items.",
@@ -440,7 +440,13 @@
440
440
  }
441
441
  ],
442
442
  "hooks": [],
443
- "cronJobs": [],
443
+ "cronJobs": [
444
+ {
445
+ "function": "import { data, takaro, checkPermission } from '@takaro/helpers';\nconst VARIABLE_KEY = 'lastZombieKillReward';\nasync function main() {\n const { gameServerId, module: mod } = data;\n const lastRunRes = (await takaro.variable.variableControllerSearch({\n filters: {\n key: [VARIABLE_KEY],\n gameServerId: [gameServerId],\n moduleId: [mod.moduleId],\n },\n })).data.data;\n // We last ran the rewards script at this time\n // If this is the first time we run it, just get the last 5 minutes\n const lastRun = lastRunRes.length ? new Date(JSON.parse(lastRunRes[0].value)) : new Date(Date.now() - 5 * 60 * 1000);\n // Fetch all the kill events since the last time we gave out rewards\n const killEvents = (await takaro.event.eventControllerSearch({\n filters: { eventName: ['entity-killed'], gameserverId: [gameServerId] },\n greaterThan: { createdAt: lastRun.toISOString() },\n limit: 1000,\n })).data.data;\n console.log(`Found ${killEvents.length} kill events since ${lastRun.toISOString()}`);\n // Group the events by player\n const playerKills = {};\n for (const killEvent of killEvents) {\n if (!playerKills[killEvent.playerId]) {\n playerKills[killEvent.playerId] = [];\n }\n playerKills[killEvent.playerId].push(killEvent);\n }\n // Give each player their reward\n // We use Promise.allSettled to run this concurrently\n const results = await Promise.allSettled(Object.entries(playerKills).map(async ([playerId, kills]) => {\n const pog = (await takaro.playerOnGameserver.playerOnGameServerControllerGetOne(gameServerId, playerId)).data\n .data;\n const hasPermission = checkPermission(pog, 'ZOMBIE_KILL_REWARD_OVERRIDE');\n const defaultReward = mod.userConfig.zombieKillReward;\n const reward = hasPermission && hasPermission.count != null ? hasPermission.count : defaultReward;\n const totalReward = reward * kills.length;\n return takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, playerId, {\n currency: totalReward,\n });\n }));\n // Log any errors\n for (const result of results) {\n if (result.status === 'rejected') {\n console.error(result.reason);\n throw new Error(`Failed to give rewards: ${result.reason}`);\n }\n }\n // Update the last run time\n if (lastRunRes.length) {\n await takaro.variable.variableControllerUpdate(lastRunRes[0].id, {\n value: JSON.stringify(new Date()),\n });\n }\n else {\n await takaro.variable.variableControllerCreate({\n key: VARIABLE_KEY,\n value: JSON.stringify(new Date()),\n moduleId: mod.moduleId,\n gameServerId,\n });\n }\n}\nawait main();\n//# sourceMappingURL=zombieKillReward.js.map",
446
+ "name": "zombieKillReward",
447
+ "temporalValue": "*/5 * * * *"
448
+ }
449
+ ],
444
450
  "functions": [],
445
451
  "permissions": [
446
452
  {
@@ -448,11 +454,17 @@
448
454
  "friendlyName": "Manage currency",
449
455
  "description": "Allows players to manage currency of other players. This includes granting and revoking currency.",
450
456
  "canHaveCount": false
457
+ },
458
+ {
459
+ "permission": "ZOMBIE_KILL_REWARD_OVERRIDE",
460
+ "friendlyName": "Zombie kill reward override",
461
+ "description": "Allows a role to override the amount of currency a player receives for killing a entity.",
462
+ "canHaveCount": true
451
463
  }
452
464
  ],
453
465
  "name": "economyUtils",
454
466
  "description": "A set of commands to allow players to manage their currency.",
455
- "configSchema": "{\"$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}},\"required\":[],\"additionalProperties\":false}",
467
+ "configSchema": "{\"$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}",
456
468
  "uiSchema": "{}"
457
469
  },
458
470
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takaro/modules",
3
- "version": "0.0.11",
3
+ "version": "0.0.14",
4
4
  "description": "Built-in modules for Takaro",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/main.d.ts",
@@ -30,5 +30,4 @@ async function main() {
30
30
  await writeFile('../e2e/src/web-main/fixtures/community-modules.json', communityModulesJson);
31
31
  }
32
32
 
33
- // eslint-disable-next-line no-console
34
33
  main().catch(console.error);
@@ -172,6 +172,59 @@ const tests = [
172
172
  expect((await eventsAfter)[0].data.meta.msg).to.be.eq('pong');
173
173
  },
174
174
  }),
175
+ /**
176
+ * Repro for https://github.com/gettakaro/takaro/issues/1405
177
+ * When providing invalid code, Takaro trips over itself and throws a nasty error
178
+ * Instead, it should catch it properly and return an actionable message for the user
179
+ */
180
+ new IntegrationTest<IModuleTestsSetupData>({
181
+ group,
182
+ snapshot: false,
183
+ name: 'Bug repro: invalid module code throws bad error #1405',
184
+ setup: modulesTestSetup,
185
+ test: async function () {
186
+ const mod = (
187
+ await this.client.module.moduleControllerCreate({
188
+ name: 'Test module',
189
+ })
190
+ ).data.data;
191
+ await this.client.command.commandControllerCreate({
192
+ name: 'testcmd',
193
+ moduleId: mod.id,
194
+ trigger: 'test',
195
+ // Yes, very bad code! This is what a user might accidentally do
196
+ function: `import { data, takaro } from '@takaro/helpers';
197
+
198
+ const messed = (await takaro.variable.variableControllerSearch({ search: { key: [\`object Object\`] } })).data.data
199
+ for (const thisVar of messed) {
200
+ let originalString = thisVar.key;
201
+ let newString = originalString.replace("[object Object]", "2024-09-20");
202
+
203
+ await takaro.variable.variableControllerUpdate(thisVar.id, { key: newString, value: thisVar.value });
204
+
205
+ }
206
+ data.player.pm(\`done\`).
207
+ `,
208
+ });
209
+
210
+ await this.client.gameserver.gameServerControllerInstallModule(this.setupData.gameserver.id, mod.id, {
211
+ userConfig: JSON.stringify({}),
212
+ systemConfig: JSON.stringify({}),
213
+ });
214
+
215
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.COMMAND_EXECUTED, 1);
216
+
217
+ await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
218
+ msg: '/test',
219
+ playerId: this.setupData.players[0].id,
220
+ });
221
+
222
+ console.log(JSON.stringify((await events)[0].data.meta.result, null, 2));
223
+ const result = (await events)[0].data.meta.result;
224
+ expect(result.success).to.be.false;
225
+ expect(result.reason).to.include('SyntaxError: Unexpected end of input.');
226
+ },
227
+ }),
175
228
  ];
176
229
 
177
230
  describe(group, function () {
@@ -8,17 +8,22 @@ const tests = [
8
8
  group,
9
9
  snapshot: false,
10
10
  setup: shopSetup,
11
- name: 'Can view the first page of shop items',
11
+ name: 'Calling /shop without arguments displays help information',
12
12
  test: async function () {
13
- const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 2);
13
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 5);
14
14
  await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
15
15
  msg: '/shop',
16
16
  playerId: this.setupData.players[0].id,
17
17
  });
18
18
 
19
- expect(await events).to.have.length(2);
20
- expect((await events)[0].data.meta.msg).to.eq('- Test item - 100 test coin. 1x Stone');
21
- expect((await events)[1].data.meta.msg).to.eq('- Test item 2 - 33 test coin. 1x Wood');
19
+ expect(await events).to.have.length(5);
20
+ expect((await events)[0].data.meta.msg).to.eq(
21
+ 'This command allows you to browse the shop and view available items.',
22
+ );
23
+ expect((await events)[1].data.meta.msg).to.eq('Usage: /shop [page] [item] [action]');
24
+ expect((await events)[2].data.meta.msg).to.eq('/shop 2 - View the second page of shop items');
25
+ expect((await events)[3].data.meta.msg).to.eq('/shop 1 3 - View details about the third item on the first page');
26
+ expect((await events)[4].data.meta.msg).to.eq('/shop 1 3 buy - Purchase the third item on the first page');
22
27
  },
23
28
  }),
24
29
  new IntegrationTest<IShopSetup>({
@@ -30,7 +35,7 @@ const tests = [
30
35
  await this.setupData.createListings(this.client, { gameServerId: this.setupData.gameserver.id, amount: 5 });
31
36
  const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 5);
32
37
  await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
33
- msg: '/shop',
38
+ msg: '/shop 1',
34
39
  playerId: this.setupData.players[0].id,
35
40
  });
36
41
 
@@ -122,23 +127,21 @@ const tests = [
122
127
  );
123
128
  },
124
129
  }),
125
-
126
130
  new IntegrationTest<IShopSetup>({
127
131
  group,
128
132
  snapshot: false,
129
133
  setup: shopSetup,
130
134
  name: 'Can buy an item',
131
135
  test: async function () {
132
- const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 1);
136
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CHAT_MESSAGE, 2);
133
137
  await this.client.command.commandControllerTrigger(this.setupData.gameserver.id, {
134
138
  msg: '/shop 1 1 buy',
135
139
  playerId: this.setupData.players[0].id,
136
140
  });
137
141
 
138
- expect(await events).to.have.length(1);
139
- expect((await events)[0].data.meta.msg).to.equal(
140
- 'You have purchased Test item for 100 test coin. You can now claim your items.',
141
- );
142
+ expect(await events).to.have.length(2);
143
+ expect((await events)[0].data.meta.msg).to.equal('You have purchased Test item for 100 test coin.');
144
+ expect((await events)[1].data.meta.msg).to.equal('You have received items from a shop order.');
142
145
  },
143
146
  }),
144
147
  ];
@@ -0,0 +1,296 @@
1
+ import { EventsAwaiter, expect, IntegrationTest, IShopSetup, shopSetup } from '@takaro/test';
2
+ import { HookEvents } from '../../dto/index.js';
3
+ import { Client, PlayerOutputDTO } from '@takaro/apiclient';
4
+
5
+ const group = 'EconomyUtils:ZombieKillReward';
6
+
7
+ async function triggerKills(client: Client, player: PlayerOutputDTO, gameServerId: string, count: number) {
8
+ const events = (await new EventsAwaiter().connect(client)).waitForEvents(HookEvents.ENTITY_KILLED, count);
9
+
10
+ if (!player.playerOnGameServers) throw new Error('Player has no pogs');
11
+ const pog = player.playerOnGameServers.find((pog) => pog.gameServerId === gameServerId);
12
+ if (!pog) throw new Error('Player not found on game server');
13
+
14
+ await Promise.all(
15
+ Array.from({ length: count }, () => {
16
+ return client.gameserver.gameServerControllerExecuteCommand(gameServerId, {
17
+ command: `triggerKill ${pog.gameId}`,
18
+ });
19
+ }),
20
+ );
21
+
22
+ await events;
23
+ return events;
24
+ }
25
+
26
+ const tests = [
27
+ new IntegrationTest<IShopSetup>({
28
+ group,
29
+ snapshot: false,
30
+ setup: shopSetup,
31
+ name: 'Simple happy path, killing one zombie gives one currency',
32
+ test: async function () {
33
+ const pogBefore = (
34
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
35
+ this.setupData.gameserver.id,
36
+ this.setupData.players[0].id,
37
+ )
38
+ ).data.data;
39
+
40
+ (
41
+ await this.client.gameserver.gameServerControllerInstallModule(
42
+ this.setupData.gameserver.id,
43
+ this.setupData.economyUtilsModule.id,
44
+ )
45
+ ).data.data;
46
+ const zombieKillCronjob = this.setupData.economyUtilsModule.cronJobs.find(
47
+ (cronjob) => cronjob.name === 'zombieKillReward',
48
+ );
49
+ if (!zombieKillCronjob) throw new Error('Cronjob not found');
50
+ await triggerKills(this.client, this.setupData.players[0], this.setupData.gameserver.id, 1);
51
+ await this.client.cronjob.cronJobControllerTrigger({
52
+ gameServerId: this.setupData.gameserver.id,
53
+ moduleId: this.setupData.economyUtilsModule.id,
54
+ cronjobId: zombieKillCronjob.id,
55
+ });
56
+
57
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CRONJOB_EXECUTED, 1);
58
+ await events;
59
+
60
+ const pogAfter = (
61
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
62
+ this.setupData.gameserver.id,
63
+ this.setupData.players[0].id,
64
+ )
65
+ ).data.data;
66
+ expect(pogAfter.currency).to.equal(pogBefore.currency + 1);
67
+ },
68
+ }),
69
+ /**
70
+ * First kill 3 zombies, then trigger the cronjob
71
+ * Check if the player has 3 currency
72
+ * Then kill 4 zombies
73
+ * Check if the player has 7 currency
74
+ */
75
+ new IntegrationTest<IShopSetup>({
76
+ group,
77
+ snapshot: false,
78
+ setup: shopSetup,
79
+ name: 'Killing multiple zombies',
80
+ test: async function () {
81
+ const pogBefore = (
82
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
83
+ this.setupData.gameserver.id,
84
+ this.setupData.players[0].id,
85
+ )
86
+ ).data.data;
87
+
88
+ (
89
+ await this.client.gameserver.gameServerControllerInstallModule(
90
+ this.setupData.gameserver.id,
91
+ this.setupData.economyUtilsModule.id,
92
+ )
93
+ ).data.data;
94
+ const zombieKillCronjob = this.setupData.economyUtilsModule.cronJobs.find(
95
+ (cronjob) => cronjob.name === 'zombieKillReward',
96
+ );
97
+ if (!zombieKillCronjob) throw new Error('Cronjob not found');
98
+ await triggerKills(this.client, this.setupData.players[0], this.setupData.gameserver.id, 3);
99
+ await this.client.cronjob.cronJobControllerTrigger({
100
+ gameServerId: this.setupData.gameserver.id,
101
+ moduleId: this.setupData.economyUtilsModule.id,
102
+ cronjobId: zombieKillCronjob.id,
103
+ });
104
+
105
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CRONJOB_EXECUTED, 1);
106
+ await events;
107
+
108
+ let pogAfter = (
109
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
110
+ this.setupData.gameserver.id,
111
+ this.setupData.players[0].id,
112
+ )
113
+ ).data.data;
114
+ expect(pogAfter.currency).to.equal(pogBefore.currency + 3);
115
+
116
+ await triggerKills(this.client, this.setupData.players[0], this.setupData.gameserver.id, 4);
117
+ await this.client.cronjob.cronJobControllerTrigger({
118
+ gameServerId: this.setupData.gameserver.id,
119
+ moduleId: this.setupData.economyUtilsModule.id,
120
+ cronjobId: zombieKillCronjob.id,
121
+ });
122
+
123
+ const events2 = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CRONJOB_EXECUTED, 1);
124
+ await events2;
125
+
126
+ pogAfter = (
127
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
128
+ this.setupData.gameserver.id,
129
+ this.setupData.players[0].id,
130
+ )
131
+ ).data.data;
132
+ expect(pogAfter.currency).to.equal(pogBefore.currency + 7);
133
+ },
134
+ }),
135
+ new IntegrationTest<IShopSetup>({
136
+ group,
137
+ snapshot: false,
138
+ setup: shopSetup,
139
+ name: 'Different players killing zombies assigns the right amount to the right players',
140
+ test: async function () {
141
+ const pogBefore1 = (
142
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
143
+ this.setupData.gameserver.id,
144
+ this.setupData.players[0].id,
145
+ )
146
+ ).data.data;
147
+ const pogBefore2 = (
148
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
149
+ this.setupData.gameserver.id,
150
+ this.setupData.players[1].id,
151
+ )
152
+ ).data.data;
153
+
154
+ (
155
+ await this.client.gameserver.gameServerControllerInstallModule(
156
+ this.setupData.gameserver.id,
157
+ this.setupData.economyUtilsModule.id,
158
+ )
159
+ ).data.data;
160
+ const zombieKillCronjob = this.setupData.economyUtilsModule.cronJobs.find(
161
+ (cronjob) => cronjob.name === 'zombieKillReward',
162
+ );
163
+ if (!zombieKillCronjob) throw new Error('Cronjob not found');
164
+ await triggerKills(this.client, this.setupData.players[0], this.setupData.gameserver.id, 3);
165
+ await triggerKills(this.client, this.setupData.players[1], this.setupData.gameserver.id, 4);
166
+ await this.client.cronjob.cronJobControllerTrigger({
167
+ gameServerId: this.setupData.gameserver.id,
168
+ moduleId: this.setupData.economyUtilsModule.id,
169
+ cronjobId: zombieKillCronjob.id,
170
+ });
171
+
172
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CRONJOB_EXECUTED, 1);
173
+ await events;
174
+
175
+ const pogAfter1 = (
176
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
177
+ this.setupData.gameserver.id,
178
+ this.setupData.players[0].id,
179
+ )
180
+ ).data.data;
181
+ const pogAfter2 = (
182
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
183
+ this.setupData.gameserver.id,
184
+ this.setupData.players[1].id,
185
+ )
186
+ ).data.data;
187
+ expect(pogAfter1.currency).to.equal(pogBefore1.currency + 3);
188
+ expect(pogAfter2.currency).to.equal(pogBefore2.currency + 4);
189
+ },
190
+ }),
191
+ new IntegrationTest<IShopSetup>({
192
+ group,
193
+ snapshot: false,
194
+ setup: shopSetup,
195
+ name: 'Can configure a custom amount of currency per kill',
196
+ test: async function () {
197
+ const pogBefore = (
198
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
199
+ this.setupData.gameserver.id,
200
+ this.setupData.players[0].id,
201
+ )
202
+ ).data.data;
203
+ (
204
+ await this.client.gameserver.gameServerControllerInstallModule(
205
+ this.setupData.gameserver.id,
206
+ this.setupData.economyUtilsModule.id,
207
+ {
208
+ userConfig: JSON.stringify({ zombieKillReward: 5 }),
209
+ },
210
+ )
211
+ ).data.data;
212
+ const zombieKillCronjob = this.setupData.economyUtilsModule.cronJobs.find(
213
+ (cronjob) => cronjob.name === 'zombieKillReward',
214
+ );
215
+ if (!zombieKillCronjob) throw new Error('Cronjob not found');
216
+ await triggerKills(this.client, this.setupData.players[0], this.setupData.gameserver.id, 1);
217
+ await this.client.cronjob.cronJobControllerTrigger({
218
+ gameServerId: this.setupData.gameserver.id,
219
+ moduleId: this.setupData.economyUtilsModule.id,
220
+ cronjobId: zombieKillCronjob.id,
221
+ });
222
+
223
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CRONJOB_EXECUTED, 1);
224
+ await events;
225
+
226
+ const pogAfter = (
227
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
228
+ this.setupData.gameserver.id,
229
+ this.setupData.players[0].id,
230
+ )
231
+ ).data.data;
232
+ expect(pogAfter.currency).to.equal(pogBefore.currency + 5);
233
+ },
234
+ }),
235
+ new IntegrationTest<IShopSetup>({
236
+ group,
237
+ snapshot: false,
238
+ setup: shopSetup,
239
+ name: 'Can override amount earned per role',
240
+ test: async function () {
241
+ const zombieKillEarnerPermission = await this.client.permissionCodesToInputs(['ZOMBIE_KILL_REWARD_OVERRIDE']);
242
+ await this.client.role.roleControllerUpdate(this.setupData.role.id, {
243
+ permissions: [
244
+ {
245
+ permissionId: zombieKillEarnerPermission[0].permissionId,
246
+ count: 3,
247
+ },
248
+ ],
249
+ });
250
+
251
+ const pogBefore = (
252
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
253
+ this.setupData.gameserver.id,
254
+ this.setupData.players[0].id,
255
+ )
256
+ ).data.data;
257
+ (
258
+ await this.client.gameserver.gameServerControllerInstallModule(
259
+ this.setupData.gameserver.id,
260
+ this.setupData.economyUtilsModule.id,
261
+ {
262
+ userConfig: JSON.stringify({ zombieKillReward: 5 }),
263
+ },
264
+ )
265
+ ).data.data;
266
+
267
+ const zombieKillCronjob = this.setupData.economyUtilsModule.cronJobs.find(
268
+ (cronjob) => cronjob.name === 'zombieKillReward',
269
+ );
270
+ if (!zombieKillCronjob) throw new Error('Cronjob not found');
271
+ await triggerKills(this.client, this.setupData.players[0], this.setupData.gameserver.id, 1);
272
+ await this.client.cronjob.cronJobControllerTrigger({
273
+ gameServerId: this.setupData.gameserver.id,
274
+ moduleId: this.setupData.economyUtilsModule.id,
275
+ cronjobId: zombieKillCronjob.id,
276
+ });
277
+
278
+ const events = (await new EventsAwaiter().connect(this.client)).waitForEvents(HookEvents.CRONJOB_EXECUTED, 1);
279
+ await events;
280
+
281
+ const pogAfter = (
282
+ await this.client.playerOnGameserver.playerOnGameServerControllerGetOne(
283
+ this.setupData.gameserver.id,
284
+ this.setupData.players[0].id,
285
+ )
286
+ ).data.data;
287
+ expect(pogAfter.currency).to.equal(pogBefore.currency + 3);
288
+ },
289
+ }),
290
+ ];
291
+
292
+ describe(group, function () {
293
+ tests.forEach((test) => {
294
+ test.run();
295
+ });
296
+ });
@@ -1,9 +1,20 @@
1
1
  import { takaro, data, TakaroUserError } from '@takaro/helpers';
2
2
 
3
3
  async function main() {
4
- const { arguments: args, player, gameServerId, user } = data;
5
-
4
+ const { arguments: args, player, gameServerId } = data;
6
5
  const { page, item, action } = args;
6
+ const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;
7
+
8
+ // If command is called without any arguments
9
+ const messageWithoutPrefix = data.chatMessage.msg.slice(prefix.length).trim();
10
+ if (!messageWithoutPrefix.includes(' ')) {
11
+ await player.pm('This command allows you to browse the shop and view available items.');
12
+ await player.pm(`Usage: ${prefix}shop [page] [item] [action]`);
13
+ await player.pm(`${prefix}shop 2 - View the second page of shop items`);
14
+ await player.pm(`${prefix}shop 1 3 - View details about the third item on the first page`);
15
+ await player.pm(`${prefix}shop 1 3 buy - Purchase the third item on the first page`);
16
+ return;
17
+ }
7
18
 
8
19
  const shopItems = await takaro.shopListing.shopListingControllerSearch({
9
20
  limit: 5,
@@ -24,12 +35,14 @@ async function main() {
24
35
  const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', data.gameServerId)).data.data;
25
36
 
26
37
  if (!item) {
27
- // List the shop items
38
+ // List the shop items with index
39
+ let index = 1;
28
40
  for (const listing of shopItems.data.data) {
29
41
  const items = listing.items.slice(0, 3).map((item) => {
30
42
  return `${item.amount}x ${item.item.name}`;
31
43
  });
32
- await player.pm(`- ${listing.name} - ${listing.price} ${currencyName.value}. ${items.join(', ')}`);
44
+ await player.pm(`${index} - ${listing.name} - ${listing.price} ${currencyName.value}. ${items.join(', ')}`);
45
+ index++;
33
46
  }
34
47
  return;
35
48
  }
@@ -58,16 +71,13 @@ async function main() {
58
71
  }
59
72
 
60
73
  if (action === 'buy') {
61
- if (!user) throw new TakaroUserError('You must link your account to Takaro to use this command.');
62
-
63
- await takaro.shopOrder.shopOrderControllerCreate({
74
+ const orderRes = await takaro.shopOrder.shopOrderControllerCreate({
64
75
  amount: 1,
65
76
  listingId: selectedItem.id,
66
- userId: user.id,
77
+ playerId: player.id,
67
78
  });
68
- await player.pm(
69
- `You have purchased ${selectedItem.name} for ${selectedItem.price} ${currencyName.value}. You can now claim your items.`,
70
- );
79
+ await player.pm(`You have purchased ${selectedItem.name} for ${selectedItem.price} ${currencyName.value}.`);
80
+ await takaro.shopOrder.shopOrderControllerClaim(orderRes.data.data.id);
71
81
  return;
72
82
  }
73
83
 
@@ -47,7 +47,7 @@ async function main() {
47
47
  currency: args.amount,
48
48
  },
49
49
  );
50
- } catch (_e) {
50
+ } catch {
51
51
  throw new TakaroUserError(
52
52
  `Failed to transfer ${args.amount} ${currencyName} to ${receiverName}. Are you sure you have enough balance?`,
53
53
  );
@@ -0,0 +1,82 @@
1
+ import { data, takaro, checkPermission } from '@takaro/helpers';
2
+
3
+ const VARIABLE_KEY = 'lastZombieKillReward';
4
+
5
+ async function main() {
6
+ const { gameServerId, module: mod } = data;
7
+
8
+ const lastRunRes = (
9
+ await takaro.variable.variableControllerSearch({
10
+ filters: {
11
+ key: [VARIABLE_KEY],
12
+ gameServerId: [gameServerId],
13
+ moduleId: [mod.moduleId],
14
+ },
15
+ })
16
+ ).data.data;
17
+
18
+ // We last ran the rewards script at this time
19
+ // If this is the first time we run it, just get the last 5 minutes
20
+ const lastRun = lastRunRes.length ? new Date(JSON.parse(lastRunRes[0].value)) : new Date(Date.now() - 5 * 60 * 1000);
21
+
22
+ // Fetch all the kill events since the last time we gave out rewards
23
+ const killEvents = (
24
+ await takaro.event.eventControllerSearch({
25
+ filters: { eventName: ['entity-killed'], gameserverId: [gameServerId] },
26
+ greaterThan: { createdAt: lastRun.toISOString() },
27
+ limit: 1000,
28
+ })
29
+ ).data.data;
30
+
31
+ console.log(`Found ${killEvents.length} kill events since ${lastRun.toISOString()}`);
32
+
33
+ // Group the events by player
34
+ const playerKills = {};
35
+ for (const killEvent of killEvents) {
36
+ if (!playerKills[killEvent.playerId]) {
37
+ playerKills[killEvent.playerId] = [];
38
+ }
39
+
40
+ playerKills[killEvent.playerId].push(killEvent);
41
+ }
42
+
43
+ // Give each player their reward
44
+ // We use Promise.allSettled to run this concurrently
45
+ const results = await Promise.allSettled(
46
+ Object.entries(playerKills).map(async ([playerId, kills]) => {
47
+ const pog = (await takaro.playerOnGameserver.playerOnGameServerControllerGetOne(gameServerId, playerId)).data
48
+ .data;
49
+ const hasPermission = checkPermission(pog, 'ZOMBIE_KILL_REWARD_OVERRIDE');
50
+ const defaultReward = mod.userConfig.zombieKillReward;
51
+ const reward = hasPermission && hasPermission.count != null ? hasPermission.count : defaultReward;
52
+ const totalReward = reward * kills.length;
53
+ return takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, playerId, {
54
+ currency: totalReward,
55
+ });
56
+ }),
57
+ );
58
+
59
+ // Log any errors
60
+ for (const result of results) {
61
+ if (result.status === 'rejected') {
62
+ console.error(result.reason);
63
+ throw new Error(`Failed to give rewards: ${result.reason}`);
64
+ }
65
+ }
66
+
67
+ // Update the last run time
68
+ if (lastRunRes.length) {
69
+ await takaro.variable.variableControllerUpdate(lastRunRes[0].id, {
70
+ value: JSON.stringify(new Date()),
71
+ });
72
+ } else {
73
+ await takaro.variable.variableControllerCreate({
74
+ key: VARIABLE_KEY,
75
+ value: JSON.stringify(new Date()),
76
+ moduleId: mod.moduleId,
77
+ gameServerId,
78
+ });
79
+ }
80
+ }
81
+
82
+ await main();
@@ -1,4 +1,4 @@
1
- import { BuiltinModule, ICommand, IPermission } from '../../BuiltinModule.js';
1
+ import { BuiltinModule, ICommand, ICronJob, IPermission } from '../../BuiltinModule.js';
2
2
 
3
3
  export class EconomyUtils extends BuiltinModule<EconomyUtils> {
4
4
  constructor() {
@@ -16,6 +16,13 @@ export class EconomyUtils extends BuiltinModule<EconomyUtils> {
16
16
  'When a player transfers money, they must confirm the transfer when the amount is equal or above this value. Set to 0 to disable.',
17
17
  default: 0,
18
18
  },
19
+ zombieKillReward: {
20
+ title: 'Zombie kill reward',
21
+ type: 'number',
22
+ description:
23
+ 'The default amount of currency a player receives for killing a zombie. This can be overridden by roles.',
24
+ default: 1,
25
+ },
19
26
  },
20
27
  required: [],
21
28
  additionalProperties: false,
@@ -30,6 +37,20 @@ export class EconomyUtils extends BuiltinModule<EconomyUtils> {
30
37
  'Allows players to manage currency of other players. This includes granting and revoking currency.',
31
38
  canHaveCount: false,
32
39
  }),
40
+ new IPermission({
41
+ permission: 'ZOMBIE_KILL_REWARD_OVERRIDE',
42
+ friendlyName: 'Zombie kill reward override',
43
+ description: 'Allows a role to override the amount of currency a player receives for killing a entity.',
44
+ canHaveCount: true,
45
+ }),
46
+ ];
47
+
48
+ this.cronJobs = [
49
+ new ICronJob({
50
+ function: this.loadFn('cronJobs', 'zombieKillReward'),
51
+ name: 'zombieKillReward',
52
+ temporalValue: '*/5 * * * *',
53
+ }),
33
54
  ];
34
55
 
35
56
  this.commands = [
@@ -73,7 +94,7 @@ export class EconomyUtils extends BuiltinModule<EconomyUtils> {
73
94
  function: this.loadFn('commands', 'revokeCurrency'),
74
95
  name: 'revokeCurrency',
75
96
  trigger: 'revokecurrency',
76
- helpText: 'Grant money to a player. The money is not taken from your own balance but is new currency.',
97
+ helpText: 'Revokes money from a player. The money disappears.',
77
98
  arguments: [
78
99
  {
79
100
  name: 'receiver',
@@ -18,7 +18,7 @@ async function main() {
18
18
  await takaro.gameserver.gameServerControllerGiveItem(data.gameServerId, data.player.id, {
19
19
  name: randomOption,
20
20
  amount: 1,
21
- quality: 0,
21
+ quality: '0',
22
22
  });
23
23
  await data.player.pm(`You received ${randomOption}!`);
24
24
  } else {