9
0
mirror of https://github.com/Auxilor/EcoMobs.git synced 2025-12-21 16:09:24 +00:00

Nearing completion

This commit is contained in:
Auxilor
2022-02-06 12:51:33 +00:00
parent 474b9bcffd
commit e575df9f37
41 changed files with 716 additions and 1007 deletions

View File

@@ -27,7 +27,6 @@ import com.willfp.ecobosses.bosses.util.obj.EquipmentPiece;
import com.willfp.ecobosses.bosses.util.obj.ExperienceOptions;
import com.willfp.ecobosses.bosses.util.obj.ImmunityOptions;
import com.willfp.ecobosses.bosses.util.obj.OptionedSound;
import com.willfp.ecobosses.bosses.util.obj.SpawnTotem;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;

View File

@@ -1,103 +0,0 @@
package com.willfp.ecobosses.bosses.effects;
import com.willfp.ecobosses.bosses.EcoBoss;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects;
public abstract class Effect implements BossTicker {
/**
* The effect args.
*/
@Getter
private final List<String> args;
/**
* Create a new effect.
*
* @param args The args.
*/
protected Effect(@NotNull final List<String> args) {
this.args = args;
}
/**
* Show a config error.
*
* @param message The error message.
*/
public void showConfigError(@NotNull final String message) {
Bukkit.getLogger().warning("An effect is configured incorrectly!");
Bukkit.getLogger().warning(message);
Bukkit.getLogger().warning("Usage: " + getUsage());
}
/**
* Get effect usage.
*
* @return The usage.
*/
public abstract String getUsage();
/**
* Handle the boss attacking a player.
*
* @param boss The boss.
* @param entity The boss entity.
* @param player The player.
*/
public void onAttack(@NotNull final EcoBoss boss,
@NotNull final LivingEntity entity,
@NotNull final Player player) {
// Override when needed.
}
/**
* Tick the effect.
*
* @param boss The boss.
* @param entity The boss entity.
* @param tick The current tick: counts up from zero.
*/
public void tick(@NotNull final EcoBoss boss,
@NotNull final LivingEntity entity,
final long tick) {
// Override when needed.
}
/**
* On boss death.
*
* @param boss The boss.
* @param entity The boss entity.
* @param tick The current tick: counts up from zero.
*/
@Override
public void onDeath(@NotNull final EcoBoss boss,
@Nullable final LivingEntity entity,
final long tick) {
// Override when needed.
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Effect effect)) {
return false;
}
return Objects.equals(getArgs(), effect.getArgs());
}
@Override
public int hashCode() {
return Objects.hash(getArgs());
}
}

View File

@@ -1,53 +0,0 @@
package com.willfp.ecobosses.bosses.effects;
import com.willfp.ecobosses.bosses.effects.effects.*;
import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@UtilityClass
public class Effects {
/**
* Registered effects.
*/
private static final Map<String, Function<List<String>, Effect>> EFFECTS = new HashMap<>();
static {
register("damage-nearby-players", EffectDamageNearbyPlayers::new);
register("lightning-nearby-entities", EffectLightningNearbyEntities::new);
register("summon", EffectSummon::new);
register("give-potion-effect", EffectGivePotionEffect::new);
register("shuffle-hotbar", EffectShuffleHotbar::new);
register("teleport", EffectTeleport::new);
}
/**
* Register new effect.
*
* @param name The effect name.
* @param creator Function to create an instance of the effect given args.
*/
public void register(@NotNull final String name,
@NotNull final Function<List<String>, Effect> creator) {
EFFECTS.put(name, creator);
}
/**
* Get effect matching name.
*
* @param name The effect name.
* @param args The args.
* @return The found effect, or null.
*/
@Nullable
public Effect getEffect(@NotNull final String name,
@NotNull final List<String> args) {
Function<List<String>, Effect> found = EFFECTS.get(name.toLowerCase());
return found == null ? null : found.apply(args);
}
}

View File

@@ -1,46 +0,0 @@
package com.willfp.ecobosses.bosses.effects.effects;
import com.willfp.ecobosses.bosses.EcoBoss;
import com.willfp.ecobosses.bosses.effects.Effect;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class EffectDamageNearbyPlayers extends Effect {
private final int frequency;
private final double damage;
private final double radius;
public EffectDamageNearbyPlayers(@NotNull final List<String> args) {
super(args);
if (args.size() < 3) {
showConfigError("Incorrect amount of arguments!");
}
frequency = Integer.parseInt(args.get(0));
radius = Double.parseDouble(args.get(1));
damage = Double.parseDouble(args.get(2));
}
@Override
public String getUsage() {
return "damage-nearby-players:<frequency>:<damage>:<radius>";
}
@Override
public void tick(@NotNull final EcoBoss boss,
@NotNull final LivingEntity entity,
final long tick) {
if (tick % frequency == 0) {
for (Entity nearbyEntity : entity.getNearbyEntities(radius, radius, radius)) {
if (nearbyEntity instanceof Player) {
((Player) nearbyEntity).damage(damage, entity);
}
}
}
}
}

View File

@@ -1,48 +0,0 @@
package com.willfp.ecobosses.bosses.effects.effects;
import com.willfp.eco.util.NumberUtils;
import com.willfp.ecobosses.bosses.EcoBoss;
import com.willfp.ecobosses.bosses.effects.Effect;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class EffectGivePotionEffect extends Effect {
private final PotionEffectType type;
private final double chance;
private final int duration;
private final int strength;
public EffectGivePotionEffect(@NotNull final List<String> args) {
super(args);
if (args.size() < 4) {
showConfigError("Incorrect amount of arguments!");
}
type = PotionEffectType.getByName(args.get(0).toUpperCase());
chance = Double.parseDouble(args.get(1));
duration = Integer.parseInt(args.get(2));
strength = Integer.parseInt(args.get(3));
}
@Override
public String getUsage() {
return "give-potion-effect:<effect>:<chance>:<duration>:<strength>";
}
@Override
public void onAttack(@NotNull final EcoBoss boss,
@NotNull final LivingEntity entity,
@NotNull final Player player) {
if (NumberUtils.randFloat(0, 100) > this.chance) {
return;
}
player.addPotionEffect(new PotionEffect(type, duration, strength - 1));
}
}

View File

@@ -1,51 +0,0 @@
package com.willfp.ecobosses.bosses.effects.effects;
import com.willfp.eco.util.LightningUtils;
import com.willfp.eco.util.NumberUtils;
import com.willfp.ecobosses.bosses.EcoBoss;
import com.willfp.ecobosses.bosses.effects.Effect;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class EffectLightningNearbyEntities extends Effect {
private final int frequency;
private final double chance;
private final double damage;
private final double radius;
public EffectLightningNearbyEntities(@NotNull final List<String> args) {
super(args);
if (args.size() < 4) {
showConfigError("Incorrect amount of arguments!");
}
frequency = Integer.parseInt(args.get(0));
chance = Double.parseDouble(args.get(1));
radius = Double.parseDouble(args.get(2));
damage = Double.parseDouble(args.get(3));
}
@Override
public String getUsage() {
return "lightning-nearby-entities:<frequency>:<chance>:<damage>:<radius>";
}
@Override
public void tick(@NotNull final EcoBoss boss,
@NotNull final LivingEntity entity,
final long tick) {
if (tick % frequency == 0) {
for (Entity nearbyEntity : entity.getNearbyEntities(radius, radius, radius)) {
if (NumberUtils.randFloat(0, 100) < chance) {
if (nearbyEntity instanceof LivingEntity) {
LightningUtils.strike((LivingEntity) nearbyEntity, damage);
}
}
}
}
}
}

View File

@@ -1,54 +0,0 @@
package com.willfp.ecobosses.bosses.effects.effects;
import com.willfp.eco.util.NumberUtils;
import com.willfp.ecobosses.bosses.EcoBoss;
import com.willfp.ecobosses.bosses.effects.Effect;
import org.bukkit.Sound;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class EffectShuffleHotbar extends Effect {
private final double chance;
public EffectShuffleHotbar(@NotNull final List<String> args) {
super(args);
if (args.size() < 1) {
showConfigError("Incorrect amount of arguments!");
}
chance = Double.parseDouble(args.get(0));
}
@Override
public String getUsage() {
return "shuffle-hotbar:<chance>";
}
@Override
public void onAttack(@NotNull final EcoBoss boss,
@NotNull final LivingEntity entity,
@NotNull final Player player) {
if (NumberUtils.randFloat(0, 100) > this.chance) {
return;
}
List<ItemStack> hotbar = new ArrayList<>();
for (int i = 0; i < 9; i++) {
hotbar.add(player.getInventory().getItem(i));
}
Collections.shuffle(hotbar);
int i2 = 0;
for (ItemStack item : hotbar) {
player.getInventory().setItem(i2, item);
i2++;
}
player.playSound(player.getLocation(), Sound.ENTITY_ENDER_PEARL_THROW, 1, 0.5f);
}
}

View File

@@ -1,65 +0,0 @@
package com.willfp.ecobosses.bosses.effects.effects;
import com.willfp.eco.core.entities.Entities;
import com.willfp.eco.core.entities.TestableEntity;
import com.willfp.eco.util.NumberUtils;
import com.willfp.ecobosses.bosses.EcoBoss;
import com.willfp.ecobosses.bosses.effects.Effect;
import com.willfp.ecobosses.bosses.util.obj.OptionedSound;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Mob;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class EffectSummon extends Effect {
private final TestableEntity type;
private final double chance;
public EffectSummon(@NotNull final List<String> args) {
super(args);
if (args.size() < 2) {
showConfigError("Incorrect amount of arguments!");
}
type = Entities.lookup(args.get(0));
chance = Double.parseDouble(args.get(1));
}
@Override
public String getUsage() {
return "summon:<entity>:<chance>";
}
@Override
public void onAttack(@NotNull final EcoBoss boss,
@NotNull final LivingEntity entity,
@NotNull final Player player) {
if (NumberUtils.randFloat(0, 100) > this.chance) {
return;
}
Location loc = player.getLocation().add(NumberUtils.randInt(2, 6), 0, NumberUtils.randInt(2, 6));
for (int i = 0; i < 15; i++) {
if (loc.getBlock().getType() == Material.AIR) {
break;
}
loc.add(0, 1, 0);
}
Entity summonedEntity = type.spawn(loc);
if (summonedEntity instanceof Mob) {
((Mob) summonedEntity).setTarget(player);
}
for (OptionedSound sound : boss.getSummonSounds()) {
player.getWorld().playSound(entity.getLocation(), sound.sound(), sound.volume(), sound.pitch());
}
}
}

View File

@@ -1,75 +0,0 @@
package com.willfp.ecobosses.bosses.effects.effects;
import com.willfp.eco.util.NumberUtils;
import com.willfp.ecobosses.bosses.EcoBoss;
import com.willfp.ecobosses.bosses.effects.Effect;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class EffectTeleport extends Effect {
private final int range;
private final double chance;
public EffectTeleport(@NotNull final List<String> args) {
super(args);
if (args.size() < 2) {
showConfigError("Incorrect amount of arguments!");
}
range = Integer.parseInt(args.get(0));
chance = Double.parseDouble(args.get(1));
}
@Override
public String getUsage() {
return "teleport:<range>:<chance>";
}
@Override
public void onAttack(@NotNull final EcoBoss boss,
@NotNull final LivingEntity entity,
@NotNull final Player player) {
if (NumberUtils.randFloat(0, 100) > this.chance) {
return;
}
List<Location> valid = new ArrayList<>();
for (int x = -range; x <= range; x++) {
for (int y = -range; y <= range; y++) {
for (int z = -range; z <= range; z++) {
Location location = entity.getLocation().clone();
location.setX(location.getX() + x);
location.setY(location.getY() + y);
location.setZ(location.getZ() + z);
Block block = location.getBlock();
if (block.getType() == Material.AIR
&& block.getRelative(BlockFace.UP).getType() == Material.AIR
&& !(block.getRelative(BlockFace.DOWN).isLiquid() || block.getRelative(BlockFace.DOWN).getType() == Material.AIR)) {
valid.add(location);
}
}
}
}
if (valid.isEmpty()) {
return;
}
Collections.shuffle(valid);
Location location = valid.get(0);
entity.teleport(location);
}
}

View File

@@ -4,7 +4,6 @@ import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginDependent;
import com.willfp.ecobosses.bosses.EcoBoss;
import com.willfp.ecobosses.bosses.EcoBosses;
import com.willfp.ecobosses.bosses.util.obj.SpawnTotem;
import com.willfp.ecobosses.events.EcoBossSpawnEggEvent;
import com.willfp.ecobosses.events.EcoBossSpawnTotemEvent;
import org.bukkit.Material;

View File

@@ -1,9 +0,0 @@
package com.willfp.ecobosses.bosses.util.obj;
import lombok.Data;
import org.bukkit.Material;
public record SpawnTotem(Material bottom,
Material middle,
Material top) {
}

View File

@@ -1,29 +0,0 @@
package com.willfp.ecobosses.commands;
import com.willfp.eco.core.command.impl.PluginCommand;
import com.willfp.ecobosses.EcoBossesPlugin;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class CommandEcobosses extends PluginCommand {
/**
* Instantiate a new executor for /ebdrop.
*
* @param plugin The plugin to manage the execution for.
*/
public CommandEcobosses(@NotNull final EcoBossesPlugin plugin) {
super(plugin, "ecobosses", "ecobosses.command.ecobosses", false);
this.addSubcommand(new CommandReload(plugin))
.addSubcommand(new CommandKillall(plugin))
.addSubcommand(new CommandSpawn(plugin))
.addSubcommand(new CommandGive(plugin));
}
@Override
public void onExecute(@NotNull final CommandSender sender,
@NotNull final List<String> args) {
sender.sendMessage(this.getPlugin().getLangYml().getMessage("invalid-command"));
}
}

View File

@@ -1,148 +0,0 @@
package com.willfp.ecobosses.commands;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.command.impl.Subcommand;
import com.willfp.eco.core.config.updating.ConfigUpdater;
import com.willfp.ecobosses.bosses.EcoBoss;
import com.willfp.ecobosses.bosses.EcoBosses;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class CommandGive extends Subcommand {
/**
* The cached names.
*/
private static final List<String> BOSS_NAMES = new ArrayList<>();
/**
* The cached numbers.
*/
private static final List<String> NUMBERS = Arrays.asList(
"1",
"2",
"3",
"4",
"5",
"10",
"32",
"64"
);
/**
* Instantiate a new command handler.
*
* @param plugin The plugin for the commands to listen for.
*/
public CommandGive(@NotNull final EcoPlugin plugin) {
super(plugin, "give", "ecobosses.command.give", false);
}
/**
* Called on reload.
*/
@ConfigUpdater
public static void reload() {
BOSS_NAMES.clear();
BOSS_NAMES.addAll(EcoBosses.values().stream()
.filter(boss -> boss.getSpawnEgg() != null)
.map(EcoBoss::getId)
.collect(Collectors.toList()));
}
@Override
public void onExecute(@NotNull final CommandSender sender,
@NotNull final List<String> args) {
if (args.isEmpty()) {
sender.sendMessage(this.getPlugin().getLangYml().getMessage("needs-player"));
return;
}
if (args.size() == 1) {
sender.sendMessage(this.getPlugin().getLangYml().getMessage("needs-boss"));
return;
}
int amount = 1;
if (args.size() > 2) {
try {
amount = Integer.parseInt(args.get(2));
} catch (NumberFormatException ignored) {
// do nothing
}
}
String recieverName = args.get(0);
Player reciever = Bukkit.getPlayer(recieverName);
if (reciever == null) {
sender.sendMessage(this.getPlugin().getLangYml().getMessage("invalid-player"));
return;
}
String key = args.get(1);
EcoBoss boss = EcoBosses.getByName(key);
if (boss == null || boss.getSpawnEgg() == null) {
sender.sendMessage(this.getPlugin().getLangYml().getMessage("invalid-boss"));
return;
}
String message = this.getPlugin().getLangYml().getMessage("give-success");
message = message.replace("%boss%", boss.getId()).replace("%recipient%", reciever.getName());
sender.sendMessage(message);
ItemStack itemStack = boss.getSpawnEgg();
itemStack.setAmount(amount);
reciever.getInventory().addItem(itemStack);
}
@Override
public List<String> tabComplete(@NotNull final CommandSender sender,
@NotNull final List<String> args) {
List<String> completions = new ArrayList<>();
if (args.isEmpty()) {
// Currently, this case is not ever reached
return BOSS_NAMES;
}
if (args.size() == 1) {
StringUtil.copyPartialMatches(args.get(0), Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList()), completions);
return completions;
}
if (args.size() == 2) {
StringUtil.copyPartialMatches(args.get(1), BOSS_NAMES, completions);
Collections.sort(completions);
return completions;
}
if (args.size() == 3) {
StringUtil.copyPartialMatches(args.get(2), NUMBERS, completions);
completions.sort((s1, s2) -> {
int t1 = Integer.parseInt(s1);
int t2 = Integer.parseInt(s2);
return t1 - t2;
});
return completions;
}
return new ArrayList<>(0);
}
}

View File

@@ -1,31 +0,0 @@
package com.willfp.ecobosses.commands;
import com.willfp.eco.core.command.impl.Subcommand;
import com.willfp.ecobosses.EcoBossesPlugin;
import com.willfp.ecobosses.bosses.util.BossUtils;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class CommandKillall extends Subcommand {
/**
* Instantiate a new executor for /ebspawn.
*
* @param plugin The plugin to manage the execution for.
*/
public CommandKillall(@NotNull final EcoBossesPlugin plugin) {
super(plugin, "killall", "ecobosses.command.killall", false);
}
@Override
public void onExecute(@NotNull final CommandSender sender,
@NotNull final List<String> args) {
boolean force = false;
if (args.size() == 1) {
force = args.get(0).equalsIgnoreCase("force");
}
sender.sendMessage(this.getPlugin().getLangYml().getMessage("killall").replace("%amount%", String.valueOf(BossUtils.killAllBosses(force))));
}
}

View File

@@ -1,27 +0,0 @@
package com.willfp.ecobosses.commands;
import com.willfp.eco.core.command.impl.Subcommand;
import com.willfp.ecobosses.EcoBossesPlugin;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class CommandReload extends Subcommand {
/**
* Instantiate a new executor for /ebreload.
*
* @param plugin The plugin to manage the execution for.
*/
public CommandReload(@NotNull final EcoBossesPlugin plugin) {
super(plugin, "reload", "ecobosses.command.reload", false);
}
@Override
public void onExecute(@NotNull final CommandSender sender,
@NotNull final List<String> args) {
this.getPlugin().reload();
this.getPlugin().reload();
sender.sendMessage(this.getPlugin().getLangYml().getMessage("reloaded"));
}
}

View File

@@ -1,206 +0,0 @@
package com.willfp.ecobosses.commands;
import com.willfp.eco.core.command.impl.Subcommand;
import com.willfp.eco.core.config.updating.ConfigUpdater;
import com.willfp.ecobosses.EcoBossesPlugin;
import com.willfp.ecobosses.bosses.EcoBoss;
import com.willfp.ecobosses.bosses.EcoBosses;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class CommandSpawn extends Subcommand {
/**
* The cached names.
*/
private static final List<String> BOSS_NAMES = new ArrayList<>();
/**
* The cached numbers.
*/
private static final List<String> TILDE = Arrays.asList(
"~"
);
/**
* Instantiate a new executor for /ebspawn.
*
* @param plugin The plugin to manage the execution for.
*/
public CommandSpawn(@NotNull final EcoBossesPlugin plugin) {
super(plugin, "spawn", "ecobosses.command.spawn", false);
reload();
}
/**
* Called on reload.
*/
@ConfigUpdater
public static void reload() {
BOSS_NAMES.clear();
BOSS_NAMES.addAll(EcoBosses.values().stream().map(EcoBoss::getId).collect(Collectors.toList()));
}
@Override
public void onExecute(@NotNull final CommandSender sender,
@NotNull final List<String> args) {
if (args.isEmpty()) {
sender.sendMessage(this.getPlugin().getLangYml().getMessage("specify-boss"));
return;
}
String bossName = args.get(0);
EcoBoss boss = EcoBosses.getByName(bossName.toLowerCase());
if (boss == null) {
sender.sendMessage(this.getPlugin().getLangYml().getMessage("specify-boss"));
return;
}
Location location = null;
if (sender instanceof Player) {
location = ((Player) sender).getLocation();
}
if (args.size() >= 4) {
String xString = args.get(1);
String yString = args.get(2);
String zString = args.get(3);
double xPos;
double yPos;
double zPos;
if (xString.startsWith("~") && sender instanceof Player) {
String xDiff = xString.replace("~", "");
String yDiff = yString.replace("~", "");
String zDiff = zString.replace("~", "");
if (xDiff.isEmpty()) {
xPos = ((Player) sender).getLocation().getX();
} else {
try {
xPos = ((Player) sender).getLocation().getX() + Double.parseDouble(xDiff);
} catch (NumberFormatException e) {
xPos = ((Player) sender).getLocation().getX();
}
}
if (yDiff.isEmpty()) {
yPos = ((Player) sender).getLocation().getY();
} else {
try {
yPos = ((Player) sender).getLocation().getY() + Double.parseDouble(yDiff);
} catch (NumberFormatException e) {
yPos = ((Player) sender).getLocation().getY();
}
}
if (zDiff.isEmpty()) {
zPos = ((Player) sender).getLocation().getZ();
} else {
try {
zPos = ((Player) sender).getLocation().getZ() + Double.parseDouble(yDiff);
} catch (NumberFormatException e) {
zPos = ((Player) sender).getLocation().getZ();
}
}
} else {
try {
xPos = Double.parseDouble(xString);
yPos = Double.parseDouble(yString);
zPos = Double.parseDouble(zString);
} catch (NumberFormatException e) {
sender.sendMessage(this.getPlugin().getLangYml().getMessage("invalid-location"));
return;
}
}
location = new Location(null, xPos, yPos, zPos);
}
World world = null;
if (sender instanceof Player) {
world = ((Player) sender).getWorld();
}
if (args.size() >= 5) {
world = Bukkit.getWorld(args.get(4));
}
if (location == null) {
sender.sendMessage(this.getPlugin().getLangYml().getMessage("invalid-location"));
return;
}
location.setWorld(world);
if (world == null) {
sender.sendMessage(this.getPlugin().getLangYml().getMessage("invalid-world"));
return;
}
boss.spawn(location);
sender.sendMessage(this.getPlugin().getLangYml().getMessage("spawned"));
}
@Override
public List<String> tabComplete(@NotNull final CommandSender sender,
@NotNull final List<String> args) {
List<String> completions = new ArrayList<>();
if (args.isEmpty()) {
// Currently, this case is not ever reached
return new ArrayList<>();
}
if (args.size() == 1) {
StringUtil.copyPartialMatches(args.get(0), BOSS_NAMES, completions);
Collections.sort(completions);
return completions;
}
if (args.size() == 2) {
StringUtil.copyPartialMatches(args.get(1), TILDE, completions);
Collections.sort(completions);
return completions;
}
if (args.size() == 3) {
StringUtil.copyPartialMatches(args.get(2), TILDE, completions);
Collections.sort(completions);
return completions;
}
if (args.size() == 4) {
StringUtil.copyPartialMatches(args.get(3), TILDE, completions);
Collections.sort(completions);
return completions;
}
if (args.size() == 5) {
StringUtil.copyPartialMatches(args.get(4), Bukkit.getWorlds().stream().map(World::getName).collect(Collectors.toList()), completions);
Collections.sort(completions);
return completions;
}
return new ArrayList<>(0);
}
}

View File

@@ -1,7 +1,6 @@
package com.willfp.ecobosses.events;
import com.willfp.ecobosses.bosses.EcoBoss;
import com.willfp.ecobosses.bosses.util.obj.SpawnTotem;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;

View File

@@ -11,8 +11,10 @@ import com.willfp.ecobosses.defence.ImmunitiesHandler
import com.willfp.ecobosses.defence.MountHandler
import com.willfp.ecobosses.defence.PickupHandler
import com.willfp.ecobosses.integrations.levelledmobs.IntegrationLevelledMobs
import com.willfp.ecobosses.lifecycle.CompatibilityListeners
import com.willfp.ecobosses.lifecycle.LifecycleHandlers
import com.willfp.ecobosses.spawn.SpawnEggHandler
import com.willfp.ecobosses.spawn.SpawnTotemHandler
import com.willfp.ecobosses.util.DiscoverRecipeListener
import com.willfp.ecobosses.util.TopDamagerListener
import com.willfp.libreforge.LibReforgePlugin
@@ -46,7 +48,9 @@ class EcoBossesPlugin : LibReforgePlugin(525, 10635, "&9") {
DamageMultiplierHandler(),
MountHandler(),
PickupHandler(),
ImmunitiesHandler()
ImmunitiesHandler(),
CompatibilityListeners(),
SpawnTotemHandler()
)
}

View File

@@ -12,12 +12,14 @@ val Player.bossHolders: Iterable<Holder>
val holders = mutableListOf<Holder>()
for (boss in Bosses.values()) {
for (entity in boss.getAllAlive()) {
for (livingBoss in boss.getAllAlive()) {
val entity = livingBoss.entity ?: continue
if (entity.world != this.world) {
continue
}
if (entity.location.distanceSquared(this.location) <= boss.influenceRadius.pow(2)) {
if (entity.location.distanceSquared(this.location) <= boss.influence.pow(2)) {
holders.add(boss)
}
}

View File

@@ -84,8 +84,8 @@ object Bosses {
* @return All living bosses.
*/
@JvmStatic
fun getAllAlive(): Set<LivingEntity> {
val entities = mutableSetOf<LivingEntity>()
fun getAllAlive(): Set<LivingEcoBoss> {
val entities = mutableSetOf<LivingEcoBoss>()
for (boss in values()) {
entities.addAll(boss.getAllAlive())

View File

@@ -4,19 +4,25 @@ import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.entities.Entities
import com.willfp.eco.core.entities.TestableEntity
import com.willfp.eco.core.items.CustomItem
import com.willfp.eco.core.items.Items
import com.willfp.eco.core.recipe.Recipes
import com.willfp.eco.core.recipe.recipes.CraftingRecipe
import com.willfp.eco.util.toComponent
import com.willfp.ecobosses.events.BossKillEvent
import com.willfp.ecobosses.lifecycle.BossLifecycle
import com.willfp.ecobosses.tick.BossBarTicker
import com.willfp.ecobosses.tick.BossTicker
import com.willfp.ecobosses.tick.DisplayNameTicker
import com.willfp.ecobosses.tick.LifespanTicker
import com.willfp.ecobosses.tick.TargetTicker
import com.willfp.ecobosses.tick.TeleportHandler
import com.willfp.ecobosses.util.BossDrop
import com.willfp.ecobosses.util.CommandReward
import com.willfp.ecobosses.util.ConfiguredSound
import com.willfp.ecobosses.util.LocalBroadcast
import com.willfp.ecobosses.util.PlayableSound
import com.willfp.ecobosses.util.SpawnTotem
import com.willfp.ecobosses.util.XpReward
import com.willfp.ecobosses.util.topDamagers
import com.willfp.libreforge.Holder
@@ -25,9 +31,10 @@ import com.willfp.libreforge.effects.Effects
import net.kyori.adventure.bossbar.BossBar
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
import org.bukkit.event.entity.EntityDeathEvent
import org.bukkit.inventory.ItemStack
import java.util.Objects
import java.util.UUID
@@ -41,7 +48,7 @@ class EcoBoss(
val lifespan = config.getInt("lifespan")
val influenceRadius = config.getDouble("influenceRadius")
val influence = config.getDouble("influence")
val targetRange = config.getDouble("target.range")
@@ -65,6 +72,74 @@ class EcoBoss(
val projectileDamageMultiplier = config.getDouble("defence.projectileDamageMultiplier")
val canTeleport = config.getBool("defence.teleportation.enabled")
val teleportRange = config.getInt("defence.teleportation.range")
val teleportInterval = config.getInt("defence.teleportation.interval")
private val spawnEggBacker: ItemStack? = run {
val enabled = config.getBool("spawn.egg.enabled")
if (!enabled) {
return@run null
}
val item = Items.lookup("spawn.egg.item").item.apply {
bossEgg = this@EcoBoss
}
val key = plugin.namespacedKeyFactory.create("${this.id}_spawn_egg")
Items.registerCustomItem(
key,
CustomItem(
key,
{ it.bossEgg == this },
item
)
)
item
}
val spawnEgg: ItemStack?
get() = this.spawnEggBacker?.clone()
val recipe: CraftingRecipe? = run {
if (spawnEggBacker == null || !config.getBool("spawn.egg.craftable")) {
return@run null
}
val recipe = Recipes.createAndRegisterRecipe(
this@EcoBoss.plugin,
"${this.id}_spawn_egg",
spawnEggBacker,
config.getStrings("spawn.egg.recipe")
)
recipe
}
val totem: SpawnTotem? = run {
if (!config.getBool("spawn.totem.enabled")) {
return@run null
}
SpawnTotem(
Material.getMaterial(config.getString("config.totem.top")) ?: return@run null,
Material.getMaterial(config.getString("config.totem.middle")) ?: return@run null,
Material.getMaterial(config.getString("config.totem.bottom")) ?: return@run null
)
}
val disabledTotemWorlds: List<String> = config.getStrings("config.totem.notInWorlds")
val autoSpawnInterval = config.getInt("spawn.autospawn.interval")
val autoSpawnLocations: List<Location> = run {
}
private val bossBarColor = BossBar.Color.valueOf(config.getString("bossBar.color").uppercase())
private val bossBarStyle = BossBar.Overlay.valueOf(config.getString("bossBar.style").uppercase())
@@ -176,8 +251,8 @@ class EcoBoss(
return currentlyAlive[entity.uniqueId]
}
fun getAllAlive(): Set<LivingEntity> {
return currentlyAlive.values.mapNotNull { it.entity }.toSet()
fun getAllAlive(): Set<LivingEcoBoss> {
return currentlyAlive.values.toSet()
}
fun spawn(location: Location) {
@@ -186,15 +261,16 @@ class EcoBoss(
plugin,
mob.uniqueId,
this,
createTickersFor(mob)
createTickers()
)
}
private fun createTickersFor(entity: LivingEntity): Set<BossTicker> {
private fun createTickers(): Set<BossTicker> {
val tickers = mutableSetOf(
LifespanTicker(),
DisplayNameTicker(),
TargetTicker()
TargetTicker(),
TeleportHandler()
)
if (isBossBarEnabled) {
@@ -213,17 +289,21 @@ class EcoBoss(
return tickers
}
fun handleLifecycle(lifecycle: BossLifecycle, location: Location) {
fun handleLifecycle(lifecycle: BossLifecycle, location: Location, entity: LivingEntity?) {
sounds[lifecycle]?.play(location)
messages[lifecycle]?.forEach { it.broadcast(location) }
messages[lifecycle]?.forEach { it.broadcast(location, entity?.topDamagers ?: emptyList()) }
}
fun processRewards(player: Player?, location: Location, entity: LivingEntity, event: EntityDeathEvent) {
fun processRewards(event: BossKillEvent) {
val entity = event.boss.entity ?: return
val location = entity.location
val player = event.killer
for (drop in drops) {
drop.drop(location, player)
}
xp.modify(event)
xp.modify(event.event)
for ((index, damager) in entity.topDamagers.withIndex()) {
val rewards = commandRewards[index] ?: continue

View File

@@ -1,7 +1,6 @@
package com.willfp.ecobosses.bosses
import com.willfp.eco.core.EcoPlugin
import com.willfp.ecobosses.lifecycle.BossLifecycle
import com.willfp.ecobosses.tick.BossTicker
import org.bukkit.Bukkit
import org.bukkit.entity.Mob
@@ -13,13 +12,11 @@ class LivingEcoBoss(
val boss: EcoBoss,
val tickers: Set<BossTicker>
) {
init {
plugin.runnableFactory.create {
if (tick()) {
it.cancel()
}
private val ticker = plugin.runnableFactory.create {
if (tick()) {
it.cancel()
}
}
}.apply { runTaskTimer(1, 1) }
val entity: Mob?
get() = Bukkit.getEntity(uuid) as? Mob
@@ -30,10 +27,7 @@ class LivingEcoBoss(
private fun tick(): Boolean {
if (entity == null || entity?.isDead == true) {
for (ticker in tickers) {
ticker.onDeath(this, currentTick)
}
boss.markDead(uuid)
remove()
return true
}
@@ -44,12 +38,11 @@ class LivingEcoBoss(
return false
}
fun kill(reason: BossLifecycle) {
if (reason == BossLifecycle.SPAWN) {
throw IllegalArgumentException("Spawn is not a death lifecycle!")
}
fun remove() {
ticker.cancel()
entity?.remove()
tickers.forEach { it.onDeath(this, currentTick) }
boss.markDead(uuid)
}
}

View File

@@ -0,0 +1,26 @@
package com.willfp.ecobosses.commands
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.ecobosses.EcoBossesPlugin
import org.bukkit.command.CommandSender
class CommandEcobosses(plugin: EcoBossesPlugin) : PluginCommand(
plugin,
"ecobosses",
"ecobosses.command.ecobosses",
false
) {
override fun onExecute(
sender: CommandSender,
args: List<String>
) {
sender.sendMessage(plugin.langYml.getMessage("invalid-command"))
}
init {
addSubcommand(CommandReload(plugin))
.addSubcommand(CommandKillall(plugin))
.addSubcommand(CommandSpawn(plugin))
.addSubcommand(CommandGive(plugin))
}
}

View File

@@ -0,0 +1,115 @@
package com.willfp.ecobosses.commands
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.command.impl.Subcommand
import com.willfp.ecobosses.bosses.Bosses
import com.willfp.ecobosses.bosses.EcoBosses
import org.bukkit.Bukkit
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import org.bukkit.util.StringUtil
import java.util.stream.Collectors
class CommandGive(plugin: EcoPlugin) : Subcommand(
plugin,
"give",
"ecobosses.command.give",
false
) {
override fun onExecute(
sender: CommandSender,
args: List<String>
) {
if (args.isEmpty()) {
sender.sendMessage(plugin.langYml.getMessage("needs-player"))
return
}
if (args.size == 1) {
sender.sendMessage(plugin.langYml.getMessage("needs-boss"))
return
}
var amount = 1
if (args.size > 2) {
amount = args[2].toIntOrNull() ?: amount
}
val recieverName = args[0]
val reciever = Bukkit.getPlayer(recieverName)
if (reciever == null) {
sender.sendMessage(plugin.langYml.getMessage("invalid-player"))
return
}
val key = args[1]
val boss = EcoBosses.getByName(key)
if (boss?.spawnEgg == null) {
sender.sendMessage(plugin.langYml.getMessage("invalid-boss"))
return
}
var message = plugin.langYml.getMessage("give-success")
message = message.replace("%boss%", boss.id).replace("%recipient%", reciever.name)
sender.sendMessage(message)
val itemStack = boss.spawnEgg!!
itemStack.amount = amount
reciever.inventory.addItem(itemStack)
}
override fun tabComplete(
sender: CommandSender,
args: List<String>
): List<String> {
val completions = mutableListOf<String>()
if (args.isEmpty()) {
return BOSS_NAMES
}
if (args.size == 1) {
StringUtil.copyPartialMatches(args[0], Bukkit.getOnlinePlayers().stream().map { obj: Player -> obj.name }
.collect(Collectors.toList()), completions)
return completions
}
if (args.size == 2) {
StringUtil.copyPartialMatches(args[1], BOSS_NAMES, completions)
completions.sort()
return completions
}
if (args.size == 3) {
StringUtil.copyPartialMatches(args[2], NUMBERS, completions)
completions.sortWith { s1: String, s2: String ->
val t1 = s1.toInt()
val t2 = s2.toInt()
t1 - t2
}
return completions
}
return ArrayList(0)
}
companion object {
/**
* The cached names.
*/
private val BOSS_NAMES: List<String>
get() = Bosses.values().map { it.id }
/**
* The cached numbers.
*/
private val NUMBERS = listOf(
"1",
"2",
"3",
"4",
"5",
"10",
"32",
"64"
)
}
}

View File

@@ -0,0 +1,28 @@
package com.willfp.ecobosses.commands
import com.willfp.eco.core.command.impl.Subcommand
import com.willfp.ecobosses.EcoBossesPlugin
import com.willfp.ecobosses.bosses.Bosses
import org.bukkit.command.CommandSender
class CommandKillall(plugin: EcoBossesPlugin) : Subcommand(
plugin,
"killall",
"ecobosses.command.killall",
false
) {
override fun onExecute(
sender: CommandSender,
args: List<String>
) {
val alive = Bosses.getAllAlive()
for (boss in alive) {
boss.remove()
}
sender.sendMessage(
plugin.langYml.getMessage("killall").replace("%amount%", alive.size.toString())
)
}
}

View File

@@ -0,0 +1,20 @@
package com.willfp.ecobosses.commands
import com.willfp.eco.core.command.impl.Subcommand
import com.willfp.ecobosses.EcoBossesPlugin
import org.bukkit.command.CommandSender
class CommandReload(plugin: EcoBossesPlugin) : Subcommand(
plugin,
"reload",
"ecobosses.command.reload",
false
) {
override fun onExecute(
sender: CommandSender,
args: List<String>
) {
plugin.reload()
sender.sendMessage(plugin.langYml.getMessage("reloaded"))
}
}

View File

@@ -0,0 +1,178 @@
package com.willfp.ecobosses.commands
import com.willfp.eco.core.command.impl.Subcommand
import com.willfp.ecobosses.EcoBossesPlugin
import com.willfp.ecobosses.bosses.Bosses
import com.willfp.ecobosses.bosses.EcoBosses
import com.willfp.ecobosses.events.BossSpawnEvent
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.World
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import org.bukkit.util.StringUtil
class CommandSpawn(plugin: EcoBossesPlugin) : Subcommand(
plugin,
"spawn",
"ecobosses.command.spawn",
false
) {
override fun onExecute(
sender: CommandSender,
args: List<String>
) {
if (args.isEmpty()) {
sender.sendMessage(plugin.langYml.getMessage("specify-boss"))
return
}
val bossName = args[0]
val boss = EcoBosses.getByName(bossName.lowercase())
if (boss == null) {
sender.sendMessage(plugin.langYml.getMessage("specify-boss"))
return
}
var location: Location? = null
if (sender is Player) {
location = sender.location
}
if (args.size >= 4) {
val xString = args[1]
val yString = args[2]
val zString = args[3]
val xPos: Double
val yPos: Double
val zPos: Double
if (xString.startsWith("~") && sender is Player) {
val xDiff = xString.replace("~", "")
val yDiff = yString.replace("~", "")
val zDiff = zString.replace("~", "")
xPos = if (xDiff.isEmpty()) {
sender.location.x
} else {
try {
sender.location.x + xDiff.toDouble()
} catch (e: NumberFormatException) {
sender.location.x
}
}
yPos = if (yDiff.isEmpty()) {
sender.location.y
} else {
try {
sender.location.y + yDiff.toDouble()
} catch (e: NumberFormatException) {
sender.location.y
}
}
zPos = if (zDiff.isEmpty()) {
sender.location.z
} else {
try {
sender.location.z + yDiff.toDouble()
} catch (e: NumberFormatException) {
sender.location.z
}
}
} else {
try {
xPos = xString.toDouble()
yPos = yString.toDouble()
zPos = zString.toDouble()
} catch (e: NumberFormatException) {
sender.sendMessage(plugin.langYml.getMessage("invalid-location"))
return
}
}
location = Location(null, xPos, yPos, zPos)
}
var world: World? = null
if (sender is Player) {
world = sender.world
}
if (args.size >= 5) {
world = Bukkit.getWorld(args[4])
}
if (location == null) {
sender.sendMessage(plugin.langYml.getMessage("invalid-location"))
return
}
location.world = world
if (world == null) {
sender.sendMessage(plugin.langYml.getMessage("invalid-world"))
return
}
val event = BossSpawnEvent(
boss,
location,
BossSpawnEvent.SpawnReason.COMMAND
)
Bukkit.getPluginManager().callEvent(event)
if (!event.isCancelled) {
boss.spawn(location)
}
sender.sendMessage(plugin.langYml.getMessage("spawned"))
}
override fun tabComplete(
sender: CommandSender,
args: List<String>
): List<String> {
val completions = mutableListOf<String>()
if (args.isEmpty()) {
return emptyList()
}
if (args.size == 1) {
StringUtil.copyPartialMatches(args[0], BOSS_NAMES, completions)
completions.sort()
return completions
}
if (args.size == 2) {
StringUtil.copyPartialMatches(args[1], TILDE, completions)
completions.sort()
return completions
}
if (args.size == 3) {
StringUtil.copyPartialMatches(args[2], TILDE, completions)
completions.sort()
return completions
}
if (args.size == 4) {
StringUtil.copyPartialMatches(args[3], TILDE, completions)
completions.sort()
return completions
}
if (args.size == 5) {
StringUtil.copyPartialMatches(args[4], Bukkit.getWorlds().map { it.name }, completions)
completions.sort()
return completions
}
return ArrayList(0)
}
companion object {
/**
* The cached names.
*/
private val BOSS_NAMES: List<String>
get() = Bosses.values().map { it.id }
/**
* The cached numbers.
*/
private val TILDE = listOf(
"~"
)
}
}

View File

@@ -18,10 +18,18 @@ class DamageMultiplierHandler : Listener {
if (event.damager is Player) {
event.damage *= boss.meleeDamageMultiplier
if (boss.meleeDamageMultiplier == 0.0) {
event.isCancelled = true
}
}
if (event.damager is Projectile) {
event.damage *= boss.projectileDamageMultiplier
if (boss.projectileDamageMultiplier == 0.0) {
event.isCancelled = true
}
}
}
}

View File

@@ -4,9 +4,10 @@ import com.willfp.ecobosses.bosses.LivingEcoBoss
import com.willfp.ecobosses.lifecycle.BossLifecycle
import org.bukkit.event.Event
import org.bukkit.event.HandlerList
import org.bukkit.event.entity.EntityDeathEvent
abstract class BossDeathEvent(
val boss: LivingEcoBoss,
val boss: LivingEcoBoss
) : Event() {
override fun getHandlers(): HandlerList {
return HANDLERS

View File

@@ -4,10 +4,12 @@ import com.willfp.ecobosses.bosses.LivingEcoBoss
import com.willfp.ecobosses.lifecycle.BossLifecycle
import org.bukkit.entity.Player
import org.bukkit.event.HandlerList
import org.bukkit.event.entity.EntityDeathEvent
class BossKillEvent(
boss: LivingEcoBoss,
killer: Player?
val killer: Player?,
val event: EntityDeathEvent
) : BossDeathEvent(boss) {
override fun getHandlers(): HandlerList {
return HANDLERS

View File

@@ -1,8 +1,10 @@
package com.willfp.ecobosses.lifecycle
enum class BossLifecycle {
SPAWN,
KILL,
DESPAWN,
INJURE
enum class BossLifecycle(
val isDeath: Boolean
) {
SPAWN(false),
KILL(true),
DESPAWN(true),
INJURE(false)
}

View File

@@ -0,0 +1,16 @@
package com.willfp.ecobosses.lifecycle
import com.willfp.ecobosses.bosses.Bosses
import org.bukkit.entity.LivingEntity
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.entity.SlimeSplitEvent
class CompatibilityListeners : Listener {
@EventHandler
fun handle(event: SlimeSplitEvent) {
if (Bosses[event.entity as? LivingEntity ?: return] != null) {
event.isCancelled = true
}
}
}

View File

@@ -9,16 +9,16 @@ import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.entity.EntityDeathEvent
class DeathListeners: Listener {
class DeathListeners : Listener {
@EventHandler(
ignoreCancelled = true
)
fun handle(event: EntityDeathByEntityEvent) {
val boss = Bosses[event.victim] ?: return
boss.kill(BossLifecycle.KILL)
boss.remove(BossLifecycle.KILL)
val deathEvent = BossKillEvent(boss, event.killer.tryAsPlayer())
val deathEvent = BossKillEvent(boss, event.killer.tryAsPlayer(), event.deathEvent)
Bukkit.getPluginManager().callEvent(deathEvent)
}
@@ -28,9 +28,16 @@ class DeathListeners: Listener {
fun handle(event: EntityDeathEvent) {
val boss = Bosses[event.entity] ?: return
boss.kill(BossLifecycle.KILL)
boss.remove(BossLifecycle.KILL)
val deathEvent = BossKillEvent(boss, null)
val deathEvent = BossKillEvent(boss, null, event)
Bukkit.getPluginManager().callEvent(deathEvent)
}
@EventHandler(
ignoreCancelled = true
)
fun handle(event: BossKillEvent) {
event.boss.boss.processRewards(event)
}
}

View File

@@ -18,7 +18,7 @@ class LifecycleHandlers : Listener {
val entity = event.entity as? LivingEntity ?: return
val boss = Bosses[entity] ?: return
boss.boss.handleLifecycle(BossLifecycle.INJURE, entity.location)
boss.boss.handleLifecycle(BossLifecycle.INJURE, entity.location, entity)
}
@EventHandler(
@@ -26,7 +26,9 @@ class LifecycleHandlers : Listener {
priority = EventPriority.MONITOR
)
fun onInjure(event: BossDeathEvent) {
event.boss.boss.handleLifecycle(event.lifecycle, event.boss.entity?.location ?: return)
val entity = event.boss.entity ?: return
event.boss.boss.handleLifecycle(event.lifecycle, entity.location, entity)
}
@EventHandler(
@@ -34,6 +36,6 @@ class LifecycleHandlers : Listener {
priority = EventPriority.MONITOR
)
fun onInjure(event: BossSpawnEvent) {
event.boss.handleLifecycle(BossLifecycle.SPAWN, event.location)
event.boss.handleLifecycle(BossLifecycle.SPAWN, event.location, null)
}
}

View File

@@ -0,0 +1,64 @@
package com.willfp.ecobosses.spawn
import com.willfp.eco.util.containsIgnoreCase
import com.willfp.ecobosses.bosses.EcoBosses
import com.willfp.ecobosses.events.BossSpawnEvent
import com.willfp.ecobosses.util.SpawnTotem
import org.bukkit.Material
import org.bukkit.block.Block
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.block.BlockPlaceEvent
class SpawnTotemHandler : Listener {
@EventHandler(
ignoreCancelled = true
)
fun handle(event: BlockPlaceEvent) {
for (i in 0..2) {
lateinit var block1: Block
lateinit var block2: Block
lateinit var block3: Block
// I know this code sucks ass, but I can't be arsed to write it nicely
when (i) {
0 -> {
block3 = event.block
block2 = event.block.getRelative(0, -1, 0)
block1 = event.block.getRelative(0, -2, 0)
}
1 -> {
block1 = event.block
block2 = event.block.getRelative(0, 1, 0)
block3 = event.block.getRelative(0, 2, 0)
}
2 -> {
block2 = event.block
block1 = event.block.getRelative(0, -1, 0)
block3 = event.block.getRelative(0, 1, 0)
}
}
val placedTotem = SpawnTotem(block1.type, block2.type, block3.type)
for (boss in EcoBosses.values()) {
if (boss.totem == null || boss.disabledTotemWorlds.containsIgnoreCase(event.block.world.name)) {
continue
}
if (boss.totem != placedTotem) {
continue
}
val spawnEvent = BossSpawnEvent(boss, event.block.location, BossSpawnEvent.SpawnReason.TOTEM)
if (!spawnEvent.isCancelled) {
block1.type = Material.AIR
block2.type = Material.AIR
block3.type = Material.AIR
boss.spawn(event.block.location.add(0.0, 1.5, 0.0))
}
}
}
}
}

View File

@@ -8,7 +8,7 @@ class LifespanTicker : BossTicker {
val timeLeft = (boss.deathTime - System.currentTimeMillis()) / 1000.0
if (timeLeft <= 0) {
boss.kill(BossLifecycle.DESPAWN)
boss.remove(BossLifecycle.DESPAWN)
}
}
}

View File

@@ -0,0 +1,49 @@
package com.willfp.ecobosses.tick
import com.willfp.ecobosses.bosses.LivingEcoBoss
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.block.BlockFace
class TeleportHandler : BossTicker {
override fun tick(boss: LivingEcoBoss, tick: Int) {
val entity = boss.entity ?: return
if (!boss.boss.canTeleport) {
return
}
if (tick % boss.boss.teleportInterval != 0) {
return
}
val range = boss.boss.teleportRange
val validLocations = mutableListOf<Location>()
for (x in -range..range) {
for (y in -range..range) {
for (z in -range..range) {
val location = entity.location.clone().apply {
this.x += x
this.y += y
this.z += z
}
val block = location.block
if (block.type == Material.AIR && block.getRelative(BlockFace.UP).type == Material.AIR && !(block.getRelative(
BlockFace.DOWN
).isLiquid || block.getRelative(BlockFace.DOWN).type == Material.AIR)
) {
validLocations.add(location)
}
}
}
}
if (validLocations.isEmpty()) {
return
}
entity.teleport(validLocations.random())
}
}

View File

@@ -1,6 +1,8 @@
package com.willfp.ecobosses.util
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.util.formatEco
import com.willfp.eco.util.savedDisplayName
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.entity.Player
@@ -9,23 +11,34 @@ data class LocalBroadcast(
val messages: Iterable<String>,
val radius: Double
) {
fun broadcast(location: Location) {
fun broadcast(location: Location, topDamagers: List<Damager>) {
val toBroadcast = messages.toMutableList()
toBroadcast.replaceAll {
var message = it
for ((index, damager) in topDamagers.withIndex()) {
message = message.replace("%damage_${index + 1}%", damager.damage.toString())
.replace("%damage_${index + 1}_player%", Bukkit.getOfflinePlayer(damager.uuid).savedDisplayName)
}
message.formatEco()
}
if (radius < 0) {
for (message in messages) {
for (message in toBroadcast) {
Bukkit.broadcastMessage(message)
}
} else {
val world = location.world ?: return
world.getNearbyEntities(location, radius, radius, radius)
.filterIsInstance<Player>()
.forEach { player -> messages.forEach { player.sendMessage(it) } }
.forEach { player -> toBroadcast.forEach { player.sendMessage(it) } }
}
}
companion object {
fun fromConfig(config: Config): LocalBroadcast {
return LocalBroadcast(
config.getFormattedStrings("message"),
config.getStrings("message"),
config.getDouble("radius")
)
}

View File

@@ -0,0 +1,9 @@
package com.willfp.ecobosses.util
import org.bukkit.Material
data class SpawnTotem(
val top: Material,
val middle: Material,
val bottom: Material
)

View File

@@ -1,7 +1,7 @@
enabled: true
name: "&8Steel Golem &7| &c%health%♥ &7| &e%time%" # Display name
base-mob: iron_golem
base-mob: iron_golem attack-damage:90 movement-speed:1.5 follow-range:16 health:1200
baby: false # If set to true: will make the boss mob baby (if possible)
bossbar:

View File

@@ -20,8 +20,9 @@ chains:
bosses:
- id: steel_golem
mob: iron_golem
mob: iron_golem attack-damage:90 movement-speed:1.5 follow-range:16 health:1200
displayName: "&8Steel Golem &7| &c%health%♥ &7| &e%time%"
influence: 40
effects: [ ]
defence:
preventMounts: true
@@ -32,18 +33,23 @@ bosses:
meleeDamageMultiplier: 0.8
projectileDamageMultiplier: 0.2
teleportation:
enabled: true
interval: 100
range: 20
rewards:
xp:
minimum: 30000
maximum: 60000
topDamagerCommands:
1: []
2: []
3: []
1: [ ]
2: [ ]
3: [ ]
nearbyPlayerCommands:
radius: 10
commands: []
drops: []
commands: [ ]
drops: [ ]
target:
mode: highest_health
range: 40
@@ -52,6 +58,38 @@ bosses:
color: white
style: solid
radius: 120
spawn:
autospawn:
interval: -1
locations:
- world: world
x: 100
y: 100
z: 100
totem:
enabled: false
top: netherite_block
middle: iron_block
bottom: magma_block
notInWorlds: [ ]
egg:
enabled: true
item: evoker_spawn_egg unbreaking:1 hide_enchants name:"&8Steel Golem&f Spawn Egg"
lore:
- ""
- "&8&oPlace on the ground to"
- "&8&osummon a &8Steel Golem"
craftable: true
recipe:
- iron_block
- netherite_block
- iron_block
- air
- ecoitems:boss_core ? nether_star
- air
- iron_block
- netherite_block
- iron_block
messages:
spawn:
- message: