killEarner

  • community
  • PvP
  • by Limon
  • Takaro main
  • all
Version
View export JSON

Rewards players with currency and messages for PvP kills. Configurable reward amounts, random messages for killer and victim, and option to send messages publicly or as PMs.

Configuration 6

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

SettingTypeDefaultDescription
killReward number 100 Currency reward for killing another player
enableKillerMessages boolean true Send messages to the killer
enableVictimMessages boolean true Send messages to the victim
usePublicMessages boolean false Send messages publicly instead of as PMs
killerMessages array ["Nice kill!","You're on fire!","Dominating!","Excellent work!","Keep it up!"] Random messages for killers
victimMessages array ["Better luck next time!","Ouch!","You'll get them next time!","Don't give up!","Respawn and try again!"] Random messages for victims
Raw config schema
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "killReward": {
      "type": "number",
      "description": "Currency reward for killing another player",
      "default": 100,
      "minimum": 0
    },
    "enableKillerMessages": {
      "type": "boolean",
      "description": "Send messages to the killer",
      "default": true
    },
    "enableVictimMessages": {
      "type": "boolean",
      "description": "Send messages to the victim",
      "default": true
    },
    "usePublicMessages": {
      "type": "boolean",
      "description": "Send messages publicly instead of as PMs",
      "default": false
    },
    "killerMessages": {
      "type": "array",
      "description": "Random messages for killers",
      "items": {
        "type": "string"
      },
      "default": [
        "Nice kill!",
        "You're on fire!",
        "Dominating!",
        "Excellent work!",
        "Keep it up!"
      ]
    },
    "victimMessages": {
      "type": "array",
      "description": "Random messages for victims",
      "items": {
        "type": "string"
      },
      "default": [
        "Better luck next time!",
        "Ouch!",
        "You'll get them next time!",
        "Don't give up!",
        "Respawn and try again!"
      ]
    }
  },
  "additionalProperties": false
}
Raw UI schema
{
  "killReward": {
    "ui:help": "Amount of currency to give to the killer"
  },
  "killerMessages": {
    "ui:help": "List of random messages to send to killers"
  },
  "victimMessages": {
    "ui:help": "List of random messages to send to victims"
  },
  "usePublicMessages": {
    "ui:help": "When enabled, messages are sent to global chat instead of private messages"
  }
}

Commands 1

Chat commands players trigger in game.

  • killstats

    Use /killstats to see your own stats, or /killstats [playername] to see another player's stats

    ArgumentTypeDefaultHelp
    player player The player to view stats for (optional - defaults to yourself)
    Command source
    import { takaro, data, TakaroUserError } from '@takaro/helpers';
    
    async function main() {
      const { player, gameServerId, module, arguments: args } = data;
      
      try {
        let targetPlayer = player;
        
        // If a player argument is provided, use that player
        if (args && args.player) {
          // Verify the player exists on this game server
          const pogResponse = await takaro.playerOnGameserver.playerOnGameServerControllerSearch({
            filters: {
              gameServerId: [gameServerId],
              playerId: [args.player.id]
            },
            limit: 1
          });
          
          if (pogResponse.data.data && pogResponse.data.data.length > 0) {
            targetPlayer = args.player;
          } else {
            await player.pm(`Player "${args.player.name}" not found on this server.`);
            return;
          }
        } else if (args && args.playerName) {
          // Fallback for string-based player lookup
          const playersResponse = await takaro.player.playerControllerSearch({
            filters: {
              name: [args.playerName]  // Use filters instead of search
            },
            limit: 1
          });
          
          if (!playersResponse.data.data || playersResponse.data.data.length === 0) {
            await player.pm(`Player "${args.playerName}" not found.`);
            return;
          }
          
          targetPlayer = playersResponse.data.data[0];
        }
        
        // Get kill count
        const killCountVar = await takaro.variable.variableControllerSearch({
          filters: {
            key: ['pvp_kills'],
            gameServerId: [gameServerId],
            moduleId: [module.moduleId],
            playerId: [targetPlayer.id]
          },
          limit: 1
        });
        
        const kills = killCountVar.data.data && killCountVar.data.data.length > 0 
          ? parseInt(killCountVar.data.data[0].value) || 0 
          : 0;
        
        // Get death count
        const deathCountVar = await takaro.variable.variableControllerSearch({
          filters: {
            key: ['pvp_deaths'],
            gameServerId: [gameServerId],
            moduleId: [module.moduleId],
            playerId: [targetPlayer.id]
          },
          limit: 1
        });
        
        const deaths = deathCountVar.data.data && deathCountVar.data.data.length > 0 
          ? parseInt(deathCountVar.data.data[0].value) || 0 
          : 0;
        
        // Calculate K/D ratio
        const kdRatio = deaths > 0 ? (kills / deaths).toFixed(2) : kills.toString();
        
        const statsMessage = targetPlayer.id === player.id 
          ? `🎯 Your PvP Stats:\n  Kills: ${kills}\n  Deaths: ${deaths}\n  K/D Ratio: ${kdRatio}`
          : `🎯 ${targetPlayer.name}'s PvP Stats:\n  Kills: ${kills}\n  Deaths: ${deaths}\n  K/D Ratio: ${kdRatio}`;
        
        await player.pm(statsMessage);
        
      } catch (error) {
        console.error(`Error in showKillStats: ${error.message}`);
        await player.pm('Error retrieving kill statistics.');
      }
    }
    
    await main();

Hooks 1

Code that runs in reaction to a game or Takaro event.

  • PvP Kill Detector

    Detects when a player is killed by another player and triggers rewards and messages

    Hook source
    import { takaro, data, TakaroUserError } from '@takaro/helpers';
    
    async function main() {
      const { gameServerId, module, eventData } = data;
      
      try {
        // Extract event data (not eventMeta)
        if (!eventData || !eventData.victim || !eventData.attacker) {
          console.log('Missing required event data for PvP detection');
          return;
        }
    
        const victim = eventData.victim;
        const attacker = eventData.attacker;
        
        // Check if this is actually a PvP kill (player killed by another player)
        if (!attacker.gameId || attacker.gameId === victim.gameId) {
          console.log('Not a PvP kill - same player or invalid attacker');
          return;
        }
        
        // Check if attacker is a player (has gameId) and not an NPC
        if (!attacker.name || attacker.name.includes('zombie') || attacker.name.includes('bot')) {
          console.log('Attacker appears to be NPC, skipping reward');
          return;
        }
    
        console.log(`PvP Kill detected: ${attacker.name} killed ${victim.name}`);
        
        // Get module configuration
        const config = module.userConfig || {};
        const killReward = config.killReward || 100;
        const enableKillerMessages = config.enableKillerMessages !== false;
        const enableVictimMessages = config.enableVictimMessages !== false;
        const usePublicMessages = config.usePublicMessages || false;
        const killerMessages = config.killerMessages || ['Nice kill!', 'You\'re on fire!', 'Dominating!'];
        const victimMessages = config.victimMessages || ['Better luck next time!', 'Ouch!', 'You\'ll get them next time!'];
    
        // Find the killer player in Takaro system
        const killersResponse = await takaro.player.playerControllerSearch({
          filters: {
            name: [attacker.name]  // Use filters for exact matches
          },
          limit: 1
        });
        
        if (!killersResponse.data.data || killersResponse.data.data.length === 0) {
          console.log(`Could not find killer player: ${attacker.name}`);
          return;
        }
        
        const killerPlayer = killersResponse.data.data[0];
        
        // Find the victim player in Takaro system
        const victimsResponse = await takaro.player.playerControllerSearch({
          filters: {
            name: [victim.name]  // Use filters for exact matches
          },
          limit: 1
        });
        
        if (!victimsResponse.data.data || victimsResponse.data.data.length === 0) {
          console.log(`Could not find victim player: ${victim.name}`);
          return;
        }
        
        const victimPlayer = victimsResponse.data.data[0];
    
        // Award currency to killer if reward is configured
        if (killReward > 0) {
          // Fixed: Use correct API method name (lowercase 's')
          await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(
            gameServerId,
            killerPlayer.id,
            { currency: killReward }
          );
          console.log(`Awarded ${killReward} currency to ${killerPlayer.name}`);
        }
    
        // Update kill statistics
        try {
          // Get current kill count for killer
          const killCountVar = await takaro.variable.variableControllerSearch({
            filters: {
              key: ['pvp_kills'],
              gameServerId: [gameServerId],
              moduleId: [module.moduleId],
              playerId: [killerPlayer.id]
            },
            limit: 1
          });
          
          let currentKills = 0;
          if (killCountVar.data.data && killCountVar.data.data.length > 0) {
            currentKills = parseInt(killCountVar.data.data[0].value) || 0;
            // Update existing variable
            await takaro.variable.variableControllerUpdate(killCountVar.data.data[0].id, {
              value: (currentKills + 1).toString()
            });
          } else {
            // Create new variable
            await takaro.variable.variableControllerCreate({
              key: 'pvp_kills',
              value: '1',
              gameServerId,
              moduleId: module.moduleId,
              playerId: killerPlayer.id
            });
          }
          console.log(`Updated kill count for ${killerPlayer.name}: ${currentKills + 1}`);
          
          // Update death count for victim
          const deathCountVar = await takaro.variable.variableControllerSearch({
            filters: {
              key: ['pvp_deaths'],
              gameServerId: [gameServerId],
              moduleId: [module.moduleId],
              playerId: [victimPlayer.id]
            },
            limit: 1
          });
          
          let currentDeaths = 0;
          if (deathCountVar.data.data && deathCountVar.data.data.length > 0) {
            currentDeaths = parseInt(deathCountVar.data.data[0].value) || 0;
            await takaro.variable.variableControllerUpdate(deathCountVar.data.data[0].id, {
              value: (currentDeaths + 1).toString()
            });
          } else {
            await takaro.variable.variableControllerCreate({
              key: 'pvp_deaths',
              value: '1',
              gameServerId,
              moduleId: module.moduleId,
              playerId: victimPlayer.id
            });
          }
          console.log(`Updated death count for ${victimPlayer.name}: ${currentDeaths + 1}`);
          
        } catch (statsError) {
          console.log(`Error updating statistics: ${statsError.message}`);
        }
    
        // Send messages
        const promises = [];
        
        if (enableKillerMessages && killerMessages.length > 0) {
          const randomKillerMessage = killerMessages[Math.floor(Math.random() * killerMessages.length)];
          const killerMessage = `${randomKillerMessage} You earned ${killReward} currency for the kill!`;
          
          if (usePublicMessages) {
            promises.push(
              takaro.gameserver.gameServerControllerSendMessage(gameServerId, {
                message: `${killerPlayer.name}: ${killerMessage}`
              })
            );
          } else {
            promises.push(
              takaro.gameserver.gameServerControllerSendMessage(gameServerId, {
                message: killerMessage,
                opts: {
                  recipient: { gameId: attacker.gameId }
                }
              })
            );
          }
        }
        
        if (enableVictimMessages && victimMessages.length > 0) {
          const randomVictimMessage = victimMessages[Math.floor(Math.random() * victimMessages.length)];
          
          if (usePublicMessages) {
            promises.push(
              takaro.gameserver.gameServerControllerSendMessage(gameServerId, {
                message: `${victimPlayer.name}: ${randomVictimMessage}`
              })
            );
          } else {
            promises.push(
              takaro.gameserver.gameServerControllerSendMessage(gameServerId, {
                message: randomVictimMessage,
                opts: {
                  recipient: { gameId: victim.gameId }
                }
              })
            );
          }
        }
        
        // Execute all message sends in parallel
        if (promises.length > 0) {
          await Promise.all(promises);
          console.log('Messages sent successfully');
        }
        
      } catch (error) {
        console.error(`Error in handlePvPKill: ${error.message}`);
      }
    }
    
    await main();