diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/Bloodmoon.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/Bloodmoon.java index c7a5394..be62263 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/Bloodmoon.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/Bloodmoon.java @@ -1,54 +1,136 @@ package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.bloodmoon; import eu.mhsl.craftattack.spawn.core.appliance.Appliance; +import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; +import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.bloodmoon.commands.BloodmoonCommand; import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.bloodmoon.listener.BloodmoonMonsterDeathListener; +import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.bloodmoon.listener.BloodmoonPlayerJoinListener; +import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarFlag; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.List; -import java.util.Set; +import java.util.Map; import java.util.concurrent.ThreadLocalRandom; public class Bloodmoon extends Appliance { // für alle Dimensionen? einstellbar machen? - public final Set affectedMobs = Set.of( - EntityType.ZOMBIE, - EntityType.SKELETON, - EntityType.SPIDER, - EntityType.CREEPER, - EntityType.HUSK, - EntityType.DROWNED, - EntityType.WITCH, - EntityType.ZOMBIE_VILLAGER, - EntityType.PHANTOM, - EntityType.ENDERMAN + public final Map affectedMobEffectMap = Map.of( + EntityType.ZOMBIE, new PotionEffect[]{ + new PotionEffect(PotionEffectType.WITHER, 7 * 20, 1) + }, + EntityType.SKELETON, new PotionEffect[]{ + new PotionEffect(PotionEffectType.SLOWNESS, (int) (3.5 * 20), 1) + }, + EntityType.SPIDER, new PotionEffect[]{ + new PotionEffect(PotionEffectType.POISON, 4 * 20, 1), + new PotionEffect(PotionEffectType.NAUSEA, 6 * 20, 10) + }, + EntityType.CREEPER, new PotionEffect[]{ + new LightningPotionEffect() + }, + EntityType.HUSK, new PotionEffect[]{ + new PotionEffect(PotionEffectType.WITHER, 7 * 20, 1) + }, + EntityType.DROWNED, new PotionEffect[]{ + new PotionEffect(PotionEffectType.WITHER, 7 * 20, 1) + }, + EntityType.WITCH, new PotionEffect[]{}, + EntityType.ZOMBIE_VILLAGER, new PotionEffect[]{ + new PotionEffect(PotionEffectType.WITHER, 7 * 20, 1) + }, + EntityType.PHANTOM, new PotionEffect[]{ + new PotionEffect(PotionEffectType.LEVITATION, (int) (1.5 * 20), 3) + }, + EntityType.ENDERMAN, new PotionEffect[]{ + new PotionEffect(PotionEffectType.SLOWNESS, (int) (2.5 * 20), 2) + } ); public final int expMultiplier = 4; - public final boolean summonLightning = true; + public final boolean lightningsActive = true; + private boolean isActive = false; + private final BossBar bossBar = Bukkit.createBossBar( + "Bloodmoon", + BarColor.RED, + BarStyle.SEGMENTED_12, + BarFlag.CREATE_FOG, + BarFlag.DARKEN_SKY + ); + private final int bloodMoonLegth = 12000; + private final int daysBetweenBloodmoons = 1; private final int minBonusDrops = 1; private final int maxBonusDrops = 4; + private final Map bonusDropWeightMap = Map.of( + new ItemStack(Material.IRON_INGOT, 5), 10, + new ItemStack(Material.GOLD_INGOT, 2), 5, + new ItemStack(Material.DIAMOND, 1), 1, + new ItemStack(Material.IRON_BLOCK, 1), 5, + new ItemStack(Material.GOLD_BLOCK, 1), 2 + ); public boolean bloodmoonIsActive() { - return true; + return this.isActive; } - public Set getRandomBonusDrops() { + public void startBloodmoon() { + this.isActive = true; + Bukkit.getOnlinePlayers().forEach(player -> this.bossBar.addPlayer(player)); + } + + public void stopBloodmoon() { + this.isActive = false; + this.bossBar.removeAll(); + } + + public void addPlayerToBossBar(Player p) { + this.bossBar.addPlayer(p); + } + + public List getRandomBonusDrops() { int itemCount = ThreadLocalRandom.current().nextInt(this.minBonusDrops, this.maxBonusDrops + 1); - return Set.of( - new ItemStack(Material.DIAMOND_BLOCK, 2), - new ItemStack(Material.IRON_BARS, 3) - ); + List result = new ArrayList<>(); + for(int i = 0; i < itemCount; i++) { + result.add(this.getRandomBonusDrop()); + } + return result; + } + + private ItemStack getRandomBonusDrop() { + int totalWeight = this.bonusDropWeightMap.values().stream().mapToInt(value -> value).sum(); + int randomInt = ThreadLocalRandom.current().nextInt(0, totalWeight + 1); + int cumulativeWeight = 0; + for(Map.Entry entry : this.bonusDropWeightMap.entrySet()) { + cumulativeWeight += entry.getValue(); + if(randomInt <= cumulativeWeight) return entry.getKey(); + } + return null; } @Override protected @NotNull List listeners() { return List.of( - new BloodmoonMonsterDeathListener() + new BloodmoonMonsterDeathListener(), + new BloodmoonPlayerJoinListener() + ); + } + + @Override + protected @NotNull List> commands() { + return List.of( + new BloodmoonCommand() ); } } diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/LightningPotionEffect.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/LightningPotionEffect.java new file mode 100644 index 0000000..297f74c --- /dev/null +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/LightningPotionEffect.java @@ -0,0 +1,10 @@ +package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.bloodmoon; + +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public class LightningPotionEffect extends PotionEffect { + public LightningPotionEffect() { + super(PotionEffectType.SLOWNESS, 0, 0); + } +} diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/commands/BloodmoonCommand.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/commands/BloodmoonCommand.java new file mode 100644 index 0000000..11340f4 --- /dev/null +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/commands/BloodmoonCommand.java @@ -0,0 +1,44 @@ +package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.bloodmoon.commands; + +import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; +import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.bloodmoon.Bloodmoon; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class BloodmoonCommand extends ApplianceCommand { + public BloodmoonCommand() { + super("bloodmoon"); + } + + @Override + protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { + if(args.length == 0) { + sender.sendMessage("Bloodmoon is currently " + (this.getAppliance().bloodmoonIsActive() ? "active." : "inactive.")); + return; + } + + switch(args[0]) { + case "start": { + this.getAppliance().startBloodmoon(); + sender.sendMessage("Started bloodmoon."); + break; + } + case "stop": { + this.getAppliance().stopBloodmoon(); + sender.sendMessage("Stopped bloodmoon."); + break; + } + default: throw new Error("No such option: '" + args[0] + "' !"); + } + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if(args.length == 1) return List.of("start", "stop"); + return null; + } +} diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/listener/BloodmoonEntityDamageListener.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/listener/BloodmoonEntityDamageListener.java new file mode 100644 index 0000000..ac2c619 --- /dev/null +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/listener/BloodmoonEntityDamageListener.java @@ -0,0 +1,25 @@ +package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.bloodmoon.listener; + +import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; +import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.bloodmoon.Bloodmoon; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.entity.EntityDamageByEntityEvent; + +public class BloodmoonEntityDamageListener extends ApplianceListener { + @EventHandler + public void onEntityDamage(EntityDamageByEntityEvent event) { + if(!this.getAppliance().bloodmoonIsActive()) return; + Entity damagerEntity = event.getDamager(); + if(damagerEntity instanceof Projectile projectile) { + if(!(projectile.getShooter() instanceof Entity shooter)) return; + damagerEntity = shooter; + } + if(!(damagerEntity instanceof LivingEntity damager && event.getEntity() instanceof LivingEntity receiver)) return; + if(event.getFinalDamage() == 0) return; + + // TODO: damage increase / reduction and effects + } +} diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/listener/BloodmoonMonsterDeathListener.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/listener/BloodmoonMonsterDeathListener.java index 32cec35..6ff70cd 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/listener/BloodmoonMonsterDeathListener.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/listener/BloodmoonMonsterDeathListener.java @@ -11,9 +11,10 @@ public class BloodmoonMonsterDeathListener extends ApplianceListener public void onMonsterDeath(EntityDeathEvent event) { if(!this.getAppliance().bloodmoonIsActive()) return; LivingEntity entity = event.getEntity(); - if(!this.getAppliance().affectedMobs.contains(entity.getType())) return; + if(!this.getAppliance().affectedMobEffectMap.containsKey(entity.getType())) return; event.setDroppedExp(event.getDroppedExp() * this.getAppliance().expMultiplier); event.getDrops().addAll(this.getAppliance().getRandomBonusDrops()); + if(this.getAppliance().lightningsActive) event.getEntity().getWorld().strikeLightningEffect(event.getEntity().getLocation()); } } diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/listener/BloodmoonPlayerJoinListener.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/listener/BloodmoonPlayerJoinListener.java new file mode 100644 index 0000000..61da747 --- /dev/null +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/gameplay/bloodmoon/listener/BloodmoonPlayerJoinListener.java @@ -0,0 +1,14 @@ +package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.bloodmoon.listener; + +import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; +import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.bloodmoon.Bloodmoon; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; + +public class BloodmoonPlayerJoinListener extends ApplianceListener { + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + if(!this.getAppliance().bloodmoonIsActive()) return; + this.getAppliance().addPlayerToBossBar(event.getPlayer()); + } +}