This plugin creates a malicious trap: whenever a player opens a door, a block of floating, player-seeking TNT is spawned. If the TNT tags the player, they are instantly eliminated.
1. Project Files Structure
2. Plugin Metadata: plugin.yml
This file is mandatory and tells the server where to find your plugin's entry point.
YAML
# The name of the plugin name: EvilDoorTrap # The current version of the plugin version: 1.0 # The main class that extends JavaPlugin. # ***CRITICAL: Replace com.yourname.evildoortrap with your actual package structure.*** main: com.yourname.evildoortrap.EvilDoorTrap # The API version for compatibility (e.g., 1.18 or 1.20) api-version: 1.20 # Optional command (useful for a simple on/off switch later) commands: toggletrap: description: Toggles the Evil Door Trap plugin. usage: /toggletrap
3. Main Plugin Class: EvilDoorTrap.java
This is the entry point for your plugin. It handles startup and shutdown tasks.
Java
package com.yourname.evildoortrap;
import org.bukkit.plugin.java.JavaPlugin;
public class EvilDoorTrap extends JavaPlugin {
// A variable to hold the ID of a repeating task if we use one globally.
// Not strictly needed for this design, but good practice for other tasks.
// Initialized to -1 to signify 'no task running'.
private int tntFollowTaskID = -1;
/**
* Called when the plugin is enabled (the server starts or the plugin is loaded).
*/
@Override
public void onEnable() {
// Registers the DoorListener class so it can receive events from the server.
getServer().getPluginManager().registerEvents(new DoorListener(this), this);
getLogger().info("EvilDoorTrap enabled and watching for door opens!");
}
/**
* Called when the plugin is disabled (the server stops or the plugin is unloaded).
*/
@Override
public void onDisable() {
// Best practice: if any global repeating tasks were running, stop them here.
if (tntFollowTaskID != -1) {
getServer().getScheduler().cancelTask(tntFollowTaskID);
}
getLogger().info("EvilDoorTrap disabled.");
}
// Getters and Setters (omitted for brevity in blog post, but good practice).
}
4. Event Listener: DoorListener.java
This class listens for players interacting with blocks, specifically doors.
Java
package com.yourname.evildoortrap;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
public class DoorListener implements Listener {
private final EvilDoorTrap plugin;
public DoorListener(EvilDoorTrap plugin) {
this.plugin = plugin;
}
/**
* This method is called every time a player interacts with the world (e.g., clicks a block).
* @param event The PlayerInteractEvent triggered by the player.
*/
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
// 1. Check if the player is right-clicking a block (necessary for opening doors).
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return;
}
Block clickedBlock = event.getClickedBlock();
if (clickedBlock == null) {
return;
}
// 2. Check if the block is a door (covers wood, iron, fence gates, etc., if they have "_DOOR" in their name)
// This uses a string check for flexibility across different door types/versions.
if (clickedBlock.getType().name().contains("_DOOR")) {
Player player = event.getPlayer();
// 3. Trigger the TNT spawning and tracking logic in a separate helper class.
// We spawn the TNT slightly above and centered on the door block.
TNTTracker.startTracking(
plugin,
player,
clickedBlock.getLocation().add(0.5, 1.0, 0.5)
);
}
}
}
5. TNT Tracking Logic: TNTTracker.java
This is the core mechanic, using a repeating task to make the TNT follow the player.
Java
package com.yourname.evildoortrap; import org.bukkit.Location; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.entity.TNTPrimed; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; public class TNTTracker { // TNT must be within 1.0 block distance (squared) to trigger the kill. private static final double ACTIVATION_DISTANCE_SQUARED = 1.0; // Controls how fast the TNT moves toward the player. Adjust this value! private static final double SPEED_MULTIPLIER = 0.2; public static void startTracking(EvilDoorTrap plugin, Player target, Location spawnLocation) { // --- TNT Setup --- // Spawn the TNTPrimed entity at the door location TNTPrimed tnt = (TNTPrimed) spawnLocation.getWorld().spawnEntity(spawnLocation, EntityType.TNT); // Set the fuse to be extremely long so it doesn't explode naturally tnt.setFuseTicks(Integer.MAX_VALUE); // Disable gravity to make it float/fly towards the player tnt.setGravity(false); // --- Tracking Logic (The Repeating Task) --- // BukkitRunnable creates a task that can be scheduled to run repeatedly. new BukkitRunnable() { @Override public void run() { // Check if the player is still online or if the TNT was removed if (!target.isOnline() || tnt.isDead()) { tnt.remove(); this.cancel(); // Stop the repeating task return; } // 1. Check for player collision (detonation) Location tntLoc = tnt.getLocation(); Location playerLoc = target.getLocation(); // Check distance squared (more efficient than distance check) if (tntLoc.distanceSquared(playerLoc) < ACTIVATION_DISTANCE_SQUARED) { // Kill Logic: TNT is close enough - BOOM! tnt.remove(); // Remove the tracking entity target.setHealth(0.0); // Instantly kill the player // Optional: Send a message to the victim target.sendMessage("§cYou were killed by the Evil Door Trap!"); this.cancel(); // Stop tracking, task finished return; } // 2. Movement Logic (Follow the Player) // Calculate the direction vector from the TNT to the Player Vector direction = playerLoc.toVector().subtract(tntLoc.toVector()).normalize(); // Apply velocity, scaled by the speed multiplier tnt.setVelocity(direction.multiply(SPEED_MULTIPLIER)); } // runTaskTimer(plugin, delay, period) // delay: 0L (start immediately) // period: 2L (run every 2 ticks, or 10 times per second - this makes movement smooth) }.runTaskTimer(plugin, 0L, 2L); } }