This commit is contained in:
2025-09-06 21:38:58 +02:00
commit dae45b7b50
8 changed files with 854 additions and 0 deletions

113
.gitignore vendored Normal file
View File

@@ -0,0 +1,113 @@
# User-specific stuff
.idea/
*.iml
*.ipr
*.iws
# IntelliJ
out/
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
.flattened-pom.xml
# Common working directory
run/

68
pom.xml Normal file
View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.rattatwinko</groupId>
<artifactId>mace</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>mace</name>
<properties>
<java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<defaultGoal>clean package</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>papermc-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,118 @@
// src/main/java/org.rattatwinko.mace/BossBarManager.java
// © rattatwinko 2025
package org.rattatwinko.mace;
import org.bukkit.Bukkit;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class BossBarManager {
private final Map<UUID, BossBar> playerBossBars = new HashMap<>();
private final Map<UUID, BukkitRunnable> activeTasks = new HashMap<>();
public void startCooldown(Player player) {
UUID playerId = player.getUniqueId();
// Remove existing boss bar and task if present
removeBossBar(player);
// Create new boss bar
BossBar bossBar = Bukkit.createBossBar(
"§c§lOne-Shot Cooldown: §e60s",
BarColor.RED,
BarStyle.SEGMENTED_20
);
bossBar.addPlayer(player);
bossBar.setProgress(1.0);
playerBossBars.put(playerId, bossBar);
// Create countdown task
BukkitRunnable task = new BukkitRunnable() {
int timeLeft = 60;
@Override
public void run() {
if (timeLeft <= 0) {
// Cooldown finished
bossBar.setTitle("§a§lOne-Shot Ready!");
bossBar.setColor(BarColor.GREEN);
bossBar.setProgress(1.0);
// Remove boss bar after 3 seconds
Bukkit.getScheduler().runTaskLater(Bukkit.getPluginManager().getPlugins()[0], () -> removeBossBar(player), 60L);
this.cancel();
activeTasks.remove(playerId);
return;
}
// Update boss bar
double progress = (double) timeLeft / 60.0;
bossBar.setProgress(progress);
String title;
if (timeLeft > 10) {
title = "§c§lOne-Shot Cooldown: §e" + timeLeft + "s";
bossBar.setColor(BarColor.RED);
} else if (timeLeft > 5) {
title = "§6§lOne-Shot Cooldown: §e" + timeLeft + "s";
bossBar.setColor(BarColor.YELLOW);
} else {
title = "§e§lOne-Shot Almost Ready: §a" + timeLeft + "s";
bossBar.setColor(BarColor.YELLOW);
}
bossBar.setTitle(title);
timeLeft--;
}
};
task.runTaskTimer(Bukkit.getPluginManager().getPlugins()[0], 0L, 20L);
activeTasks.put(playerId, task);
}
public void removeBossBar(Player player) {
UUID playerId = player.getUniqueId();
// Cancel existing task
if (activeTasks.containsKey(playerId)) {
activeTasks.get(playerId).cancel();
activeTasks.remove(playerId);
}
// Remove boss bar
if (playerBossBars.containsKey(playerId)) {
BossBar bossBar = playerBossBars.get(playerId);
bossBar.removePlayer(player);
playerBossBars.remove(playerId);
}
}
public void removeAllBossBars() {
// Cancel all tasks
for (BukkitRunnable task : activeTasks.values()) {
task.cancel();
}
activeTasks.clear();
// Remove all boss bars
for (BossBar bossBar : playerBossBars.values()) {
bossBar.removeAll();
}
playerBossBars.clear();
}
public boolean hasCooldownActive(Player player) {
return playerBossBars.containsKey(player.getUniqueId());
}
}

View File

@@ -0,0 +1,104 @@
// src/main/java/org.rattatwinko.mace/Mace.java
// © rattatwinko 2025
package org.rattatwinko.mace;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class Mace extends JavaPlugin {
private Map<UUID, Long> oneshotCooldowns = new HashMap<>();
private MaceEventListener eventListener;
private BossBarManager bossBarManager;
private PlayerGlowManager glowManager;
@Override
public void onEnable() {
getLogger().info("Mace Plugin has been enabled!");
// Initialize managers
this.bossBarManager = new BossBarManager();
this.glowManager = new PlayerGlowManager(this);
this.eventListener = new MaceEventListener(this, oneshotCooldowns, bossBarManager, glowManager);
// Register event listener
Bukkit.getPluginManager().registerEvents(eventListener, this);
// Register commands
getCommand("givemace").setExecutor(new MaceCommand(this));
// Start glow task
glowManager.startGlowTask();
getLogger().info("All Mace Plugin components initialized successfully!");
}
@Override
public void onDisable() {
// Clean up boss bars
if (bossBarManager != null) {
bossBarManager.removeAllBossBars();
}
// Clean up glow effects
if (glowManager != null) {
glowManager.cleanup();
}
getLogger().info("Mace Plugin has been disabled!");
}
public ItemStack createMaceItem() {
ItemStack mace = new ItemStack(Material.MACE);
ItemMeta meta = mace.getItemMeta();
if (meta != null) {
meta.setDisplayName("§6Mace");
// Add OP enchantments
meta.addEnchant(Enchantment.UNBREAKING, 10, true);
meta.addEnchant(Enchantment.WIND_BURST, 3, true);
meta.addEnchant(Enchantment.DENSITY, 5,true);
meta.addEnchant(Enchantment.BREACH, 4,true);
// Set custom model data to identify our special mace
meta.setCustomModelData(12345);
mace.setItemMeta(meta);
}
return mace;
}
public boolean isMaceItem(ItemStack item) {
if (item == null || item.getType() != Material.MACE) {
return false;
}
ItemMeta meta = item.getItemMeta();
return meta != null && meta.hasCustomModelData() && meta.getCustomModelData() == 12345;
}
public Map<UUID, Long> getOneshotCooldowns() {
return oneshotCooldowns;
}
public BossBarManager getBossBarManager() {
return bossBarManager;
}
public PlayerGlowManager getGlowManager() {
return glowManager;
}
}

View File

@@ -0,0 +1,72 @@
// src/main/java/org.rattatwinko.mace/MaceCommand.java
// © rattatwinko 2025
package org.rattatwinko.mace;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class MaceCommand implements CommandExecutor {
private final Mace plugin;
public MaceCommand(Mace plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!command.getName().equalsIgnoreCase("givemace")) {
return false;
}
// Check permissions
if (!sender.hasPermission("mace.give")) {
sender.sendMessage("§cYou don't have permission to use this command!");
return true;
}
Player targetPlayer;
// Determine target player
if (args.length == 0) {
// Give to command sender if they are a player
if (!(sender instanceof Player)) {
sender.sendMessage("§cConsole must specify a player name!");
return true;
}
targetPlayer = (Player) sender;
} else {
// Give to specified player
targetPlayer = Bukkit.getPlayer(args[0]);
if (targetPlayer == null) {
sender.sendMessage("§cPlayer not found: " + args[0]);
return true;
}
}
// Create and give the mace
ItemStack mace = plugin.createMaceItem();
// Check if player has space in inventory
if (targetPlayer.getInventory().firstEmpty() == -1) {
// Inventory full, drop at player's location
targetPlayer.getWorld().dropItem(targetPlayer.getLocation(), mace);
targetPlayer.sendMessage("§6§lUltimate Mace has been dropped at your location (inventory full)!");
} else {
// Give directly to player
targetPlayer.getInventory().addItem(mace);
}
// Send confirmation to command sender
if (!sender.equals(targetPlayer)) {
sender.sendMessage("§aGave Ultimate Mace to " + targetPlayer.getName());
}
return true;
}
}

View File

@@ -0,0 +1,199 @@
// src/main/java/org.rattatwinko.mace/MaceEventListener.java
// © rattatwinko 2025
package org.rattatwinko.mace;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import java.util.Map;
import java.util.UUID;
public class MaceEventListener implements Listener {
private final Mace plugin;
private final Map<UUID, Long> oneshotCooldowns;
private final BossBarManager bossBarManager;
private final PlayerGlowManager glowManager;
public MaceEventListener(Mace plugin, Map<UUID, Long> oneshotCooldowns,
BossBarManager bossBarManager, PlayerGlowManager glowManager) {
this.plugin = plugin;
this.oneshotCooldowns = oneshotCooldowns;
this.bossBarManager = bossBarManager;
this.glowManager = glowManager;
}
@EventHandler
public void onEntityDamage(EntityDamageByEntityEvent event) {
if (!(event.getDamager() instanceof Player)) return;
Player attacker = (Player) event.getDamager();
ItemStack weapon = attacker.getInventory().getItemInMainHand();
if (!plugin.isMaceItem(weapon)) return;
// Play attack sound and particles
playAttackEffects(attacker);
// Check for one-shot capability
UUID playerId = attacker.getUniqueId();
long currentTime = System.currentTimeMillis();
if (oneshotCooldowns.containsKey(playerId)) {
long lastUse = oneshotCooldowns.get(playerId);
long cooldownTime = 60000; // 1 minute in milliseconds
if (currentTime - lastUse < cooldownTime) {
// Still on cooldown, deal normal damage
return;
}
}
// One-shot is available
if (event.getEntity() instanceof LivingEntity) {
LivingEntity target = (LivingEntity) event.getEntity();
// Set damage to target's max health to ensure one-shot
event.setDamage(target.getMaxHealth() + 100);
// Update cooldown
oneshotCooldowns.put(playerId, currentTime);
// Start boss bar cooldown display
bossBarManager.startCooldown(attacker);
// Play one-shot effects
playOneshotEffects(attacker, target);
attacker.sendMessage("§6§lONE-SHOT KILL! §7Cooldown started");
}
}
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
if (!event.getAction().name().contains("RIGHT_CLICK")) return;
Player player = event.getPlayer();
ItemStack item = player.getInventory().getItemInMainHand();
if (!plugin.isMaceItem(item)) return;
event.setCancelled(true);
// Create wind charge effect
createWindCharge(player);
// Play wind charge effects
playWindChargeEffects(player);
}
@EventHandler
public void onItemHeld(PlayerItemHeldEvent event) {
Player player = event.getPlayer();
ItemStack newItem = player.getInventory().getItem(event.getNewSlot());
ItemStack oldItem = player.getInventory().getItem(event.getPreviousSlot());
// Check if switching to mace
if (plugin.isMaceItem(newItem)) {
glowManager.addPlayer(player);
playEquipEffects(player);
}
// Check if switching away from mace
if (plugin.isMaceItem(oldItem)) {
glowManager.removePlayer(player);
}
}
private void createWindCharge(Player player) {
Location eyeLocation = player.getEyeLocation();
Vector direction = eyeLocation.getDirection();
// Spawn wind charge
WindCharge windCharge = (WindCharge) player.getWorld().spawnEntity(
eyeLocation.add(direction.multiply(1.5)), EntityType.WIND_CHARGE
);
windCharge.setVelocity(direction.multiply(2.0));
windCharge.setShooter(player);
// Add knockback to nearby entities
for (Entity entity : player.getNearbyEntities(5, 5, 5)) {
if (entity instanceof LivingEntity && !entity.equals(player)) {
Vector knockback = entity.getLocation().toVector()
.subtract(player.getLocation().toVector())
.normalize()
.multiply(1.5)
.setY(0.8);
entity.setVelocity(knockback);
}
}
}
private void playAttackEffects(Player player) {
Location loc = player.getLocation();
World world = player.getWorld();
// Lightning particle effect
world.spawnParticle(Particle.ELECTRIC_SPARK, loc.add(0, 1, 0), 20, 0.5, 0.5, 0.5, 0.1);
world.spawnParticle(Particle.CRIT, loc, 15, 0.3, 0.3, 0.3, 0.1);
// Sound effects
world.playSound(loc, Sound.ENTITY_LIGHTNING_BOLT_THUNDER, 0.3f, 1.5f);
world.playSound(loc, Sound.ENTITY_PLAYER_ATTACK_CRIT, 1.0f, 0.8f);
}
private void playOneshotEffects(Player attacker, LivingEntity target) {
Location attackerLoc = attacker.getLocation();
Location targetLoc = target.getLocation();
World world = attacker.getWorld();
// Massive particle explosion at target
world.spawnParticle(Particle.EXPLOSION, targetLoc.add(0, 1, 0), 5, 1, 1, 1, 0);
world.spawnParticle(Particle.ELECTRIC_SPARK, targetLoc, 50, 2, 2, 2, 0.3);
world.spawnParticle(Particle.ENCHANT, targetLoc, 30, 1.5, 1.5, 1.5, 0.1);
// Lightning strike effect (visual only)
world.spawnParticle(Particle.ELECTRIC_SPARK, targetLoc.add(0, 10, 0), 100, 0, 10, 0, 0.1);
// Epic sound effects
world.playSound(attackerLoc, Sound.ENTITY_LIGHTNING_BOLT_THUNDER, 1.0f, 1.0f);
world.playSound(targetLoc, Sound.ENTITY_GENERIC_EXPLODE, 1.0f, 0.5f);
world.playSound(targetLoc, Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 2.0f);
}
private void playWindChargeEffects(Player player) {
Location loc = player.getLocation();
World world = player.getWorld();
// Wind particles
world.spawnParticle(Particle.CLOUD, loc.add(0, 1, 0), 20, 1, 1, 1, 0.1);
world.spawnParticle(Particle.SWEEP_ATTACK, loc, 3, 0.5, 0.5, 0.5, 0);
// Wind sound
world.playSound(loc, Sound.ENTITY_BREEZE_SHOOT, 1.0f, 1.2f);
world.playSound(loc, Sound.ENTITY_PLAYER_ATTACK_SWEEP, 0.8f, 1.5f);
}
private void playEquipEffects(Player player) {
Location loc = player.getLocation();
World world = player.getWorld();
// Enchantment particles
world.spawnParticle(Particle.ENCHANT, loc.add(0, 1, 0), 30, 0.5, 1, 0.5, 0.1);
world.spawnParticle(Particle.ELECTRIC_SPARK, loc, 10, 0.3, 0.5, 0.3, 0.05);
// Epic equip sound
world.playSound(loc, Sound.BLOCK_ANVIL_PLACE, 0.5f, 1.5f);
world.playSound(loc, Sound.ENTITY_PLAYER_LEVELUP, 0.3f, 2.0f);
}
}

View File

@@ -0,0 +1,154 @@
// src/main/java/org.rattatwinko.mace/PlayerGlowManager.java
// © rattatwinko 2025
package org.rattatwinko.mace;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class PlayerGlowManager {
private final Mace plugin;
private final Set<UUID> activePlayers = new HashSet<>();
private BukkitTask glowTask;
private Scoreboard scoreboard;
private Team glowTeam;
public PlayerGlowManager(Mace plugin) {
this.plugin = plugin;
setupScoreboard();
}
private void setupScoreboard() {
scoreboard = Bukkit.getScoreboardManager().getMainScoreboard();
// Try to get existing team or create new one
glowTeam = scoreboard.getTeam("mace_glow");
if (glowTeam == null) {
glowTeam = scoreboard.registerNewTeam("mace_glow");
}
glowTeam.setColor(ChatColor.RED);
glowTeam.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER);
}
public void addPlayer(Player player) {
activePlayers.add(player.getUniqueId());
}
public void removePlayer(Player player) {
activePlayers.remove(player.getUniqueId());
// Remove glow effect from all players for this viewer
for (Player target : Bukkit.getOnlinePlayers()) {
if (!target.equals(player)) {
removeGlowEffect(target, player);
}
}
}
public void startGlowTask() {
glowTask = new BukkitRunnable() {
@Override
public void run() {
for (UUID playerId : new HashSet<>(activePlayers)) {
Player player = Bukkit.getPlayer(playerId);
if (player == null || !player.isOnline()) {
activePlayers.remove(playerId);
continue;
}
// Check if player still has mace equipped
if (!plugin.isMaceItem(player.getInventory().getItemInMainHand())) {
removePlayer(player);
continue;
}
updateGlowEffects(player);
}
}
}.runTaskTimer(plugin, 0L, 10L); // Update every half second
}
private void updateGlowEffects(Player maceHolder) {
Set<Player> nearbyPlayers = new HashSet<>();
// Find players within 20 block radius
for (Player target : Bukkit.getOnlinePlayers()) {
if (target.equals(maceHolder)) continue;
if (!target.getWorld().equals(maceHolder.getWorld())) continue;
double distance = target.getLocation().distance(maceHolder.getLocation());
if (distance <= 20.0) {
nearbyPlayers.add(target);
applyGlowEffect(target, maceHolder);
} else {
removeGlowEffect(target, maceHolder);
}
}
}
private void applyGlowEffect(Player target, Player viewer) {
// Add target to glow team (makes them glow for everyone)
if (!glowTeam.hasEntry(target.getName())) {
glowTeam.addEntry(target.getName());
}
// Apply glowing potion effect specifically for the viewer
target.addPotionEffect(new PotionEffect(
PotionEffectType.GLOWING,
25, // 1.25 seconds (will be refreshed)
0,
false,
false,
false
));
}
private void removeGlowEffect(Player target, Player viewer) {
// Only remove from team if no other mace holders are near
boolean shouldKeepGlow = false;
for (UUID otherId : activePlayers) {
Player other = Bukkit.getPlayer(otherId);
if (other != null && !other.equals(viewer) && other.isOnline()) {
if (other.getWorld().equals(target.getWorld()) &&
other.getLocation().distance(target.getLocation()) <= 20.0) {
shouldKeepGlow = true;
break;
}
}
}
if (!shouldKeepGlow && glowTeam.hasEntry(target.getName())) {
glowTeam.removeEntry(target.getName());
target.removePotionEffect(PotionEffectType.GLOWING);
}
}
public void cleanup() {
if (glowTask != null) {
glowTask.cancel();
}
// Remove glow effects from all players
for (Player player : Bukkit.getOnlinePlayers()) {
if (glowTeam.hasEntry(player.getName())) {
glowTeam.removeEntry(player.getName());
player.removePotionEffect(PotionEffectType.GLOWING);
}
}
activePlayers.clear();
}
}

View File

@@ -0,0 +1,26 @@
name: Mace
version: 1.0.0
main: org.rattatwinko.mace.Mace
api-version: 1.20
author: rattatwinko
description: A powerful mace weapon with special abilities
commands:
givemace:
description: Give the Ultimate Mace to a player
usage: /givemace [player]
permission: mace.give
aliases: [mace, ultimatemace]
permissions:
mace.give:
description: Allows giving the Ultimate Mace
default: op
mace.*:
description: Grants all mace permissions
default: op
children:
mace.give: true
depend: []
softdepend: []