From dcdf9a880eb183a89cee43d32846f634253931f4 Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Sun, 22 Sep 2024 11:05:05 +0200 Subject: [PATCH] Jumping and 1.14 This should mark 2.0.6 --- README.md | 10 ++- pom.xml | 17 ++++- .../java/eu/m724/giants/Configuration.java | 40 ++++++++++-- .../java/eu/m724/giants/GiantProcessor.java | 65 +++++++++++++++++-- .../java/eu/m724/giants/GiantsCommand.java | 65 +++++++++++++++++-- .../java/eu/m724/giants/GiantsPlugin.java | 2 +- src/main/resources/config.yml | 21 +++++- 7 files changed, 200 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index ac48308..f4feca1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ # Giants -This plugin adds naturally spawning Giants with AI to your Minecraft server. \ No newline at end of file +This plugin adds naturally spawning Giants with AI to your Minecraft server. + +### Requested features +- Texture variation \ + I think this can be done, but would require a texture pack +- Boss bar \ + I don't want to get buried in that, so this would require an API, which, for performance reasons, should not be mandatory +- Hitboxes \ + I don't know if this is still a major issue, but a person reported that the hitbox doesn't cover the whole body \ No newline at end of file diff --git a/pom.xml b/pom.xml index 884a170..258f8e9 100644 --- a/pom.xml +++ b/pom.xml @@ -24,8 +24,23 @@ org.spigotmc spigot-api - 1.21.1-R0.1-SNAPSHOT + 1.14.4-R0.1-SNAPSHOT provided + + + + org.yaml + snakeyaml + + + com.google.guava + guava + + + com.google.code.gson + gson + + diff --git a/src/main/java/eu/m724/giants/Configuration.java b/src/main/java/eu/m724/giants/Configuration.java index de52b6f..1b34d40 100644 --- a/src/main/java/eu/m724/giants/Configuration.java +++ b/src/main/java/eu/m724/giants/Configuration.java @@ -20,14 +20,16 @@ public class Configuration { private final Logger logger; boolean ai; - boolean attack; - double chance; double attackDamage; int attackDelay; - Vector attackReach; + int jumpMode; + int jumpCondition; + int jumpDelay; + double jumpHeight = -1; + double chance; List worldBlacklist; Set effects = new HashSet<>(); Set drops = new HashSet<>(); @@ -41,15 +43,28 @@ public class Configuration { YamlConfiguration config = YamlConfiguration.loadConfiguration(file); ai = config.getBoolean("ai"); - attack = config.getBoolean("attack"); chance = config.getDouble("chance"); attackDamage = config.getDouble("attackDamage"); attackDelay = config.getInt("attackDelay"); + jumpMode = config.getInt("jumpMode"); + jumpCondition = config.getInt("jumpCondition"); + jumpDelay = config.getInt("jumpDelay"); + + if (jumpMode != 0) { + jumpHeight = config.getDouble("jumpHeight", -1); + if (jumpHeight == -1) { + jumpHeight = defaultJumpHeight(); + } + logger.info("Jumping is experimental."); + logger.info("Jump mode: " + jumpMode); + logger.info("Jump condition: " + jumpCondition); + logger.info("Jump height: " + jumpHeight); + } double _attackReach = config.getDouble("attackReach"); - double _expandUp = config.getDouble("expandUp"); - attackReach = new Vector(_attackReach, _expandUp, _attackReach); + double _attackVerticalReach = config.getDouble("attackVerticalReach"); + attackReach = new Vector(_attackReach, _attackVerticalReach, _attackReach); worldBlacklist = config.getStringList("blacklist"); @@ -99,4 +114,17 @@ public class Configuration { } } } + + public double defaultJumpHeight() { + switch (jumpMode) { + case 1: + case 2: + return 0.42; + case 3: + case 4: + return 1.2; + } + + return -1; + } } diff --git a/src/main/java/eu/m724/giants/GiantProcessor.java b/src/main/java/eu/m724/giants/GiantProcessor.java index 038fb3c..7aff656 100644 --- a/src/main/java/eu/m724/giants/GiantProcessor.java +++ b/src/main/java/eu/m724/giants/GiantProcessor.java @@ -1,12 +1,14 @@ package eu.m724.giants; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.event.entity.EntityTargetLivingEntityEvent; import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; @@ -14,6 +16,7 @@ import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.Vector; import java.util.*; import java.util.concurrent.ThreadLocalRandom; @@ -32,6 +35,8 @@ public class GiantProcessor implements Listener { // private final Set trackedGiants = new HashSet<>(); private final Set trackedHusks = new HashSet<>(); + private final Map giantLocationMap = new HashMap<>(); + private final Map giantLastJump = new HashMap<>(); private final ThreadLocalRandom random = ThreadLocalRandom.current(); private final NamespacedKey huskKey; @@ -50,7 +55,7 @@ public class GiantProcessor implements Listener { public void run() { for (Husk husk : Set.copyOf(trackedHusks)) { if (husk.isValid()) { - Location location = husk.getLocation(); + Location huskLocation = husk.getLocation(); Entity giant = husk.getVehicle(); if (giant instanceof Giant) { @@ -59,7 +64,23 @@ public class GiantProcessor implements Listener { giant.getBoundingBox().expand(configuration.attackReach), e -> (e instanceof Player && !e.isInvulnerable()) ).forEach(p -> ((Player) p).damage(configuration.attackDamage, giant)); - giant.setRotation(location.getYaw(), location.getPitch()); + giant.setRotation(huskLocation.getYaw(), huskLocation.getPitch()); + + // jumping + if (configuration.jumpMode != 0) { + // tracking location is only required for jumping + Location prevLocation = giantLocationMap.get(giant); + Location location = giant.getLocation(); + if (prevLocation == null) { + prevLocation = location; + } + giantLocationMap.put(giant, location); + + LivingEntity target = husk.getTarget(); + if (target != null) { + processJump(giant, prevLocation, location, target.getLocation()); + } + } } } else { // no vehicle means the giant doesn't exist anymore and the husk should also not exist @@ -80,6 +101,41 @@ public class GiantProcessor implements Listener { plugin.getServer().getPluginManager().registerEvents(this, plugin); } + private void processJump(Entity giant, Location prevLocation, Location location, Location targetLocation) { + long now = System.currentTimeMillis(); + if (now - giantLastJump.getOrDefault(giant, 0L) < configuration.jumpDelay) { + return; + } + + if (giant.isOnGround()) { + giantLastJump.put(giant, now); + if (configuration.jumpCondition == 0) { + if (targetLocation.subtract(location).getY() > 0) { + jump(giant); + } + } else if (configuration.jumpCondition == 1) { + Location delta = prevLocation.subtract(location); + if (targetLocation.subtract(location).getY() > 0 && (delta.getX() == 0 || delta.getZ() == 0)) { + jump(giant); + } + } else if (configuration.jumpCondition == 2) { + Location delta = prevLocation.subtract(location); + if (delta.getX() == 0 || delta.getZ() == 0) { + jump(giant); + } + + } // I could probably simplify that code + } + } + + private void jump(Entity giant) { + if (configuration.jumpMode == 1) { + giant.setVelocity(new Vector(0, configuration.jumpHeight, 0)); + } else if (configuration.jumpMode == 2) { + giant.teleport(giant.getLocation().add(0, configuration.jumpHeight, 0)); + } + } // TODO those should be moved + public LivingEntity spawnGiant(Location pos) { LivingEntity entity = (LivingEntity) pos.getWorld().spawnEntity(pos, EntityType.GIANT); @@ -92,6 +148,7 @@ public class GiantProcessor implements Listener { passenger.getPersistentDataContainer().set(huskKey, PersistentDataType.INTEGER, VERSION); entity.addPassenger(passenger); + trackedHusks.add((Husk) passenger); } @@ -109,7 +166,7 @@ public class GiantProcessor implements Listener { logger.fine("Chunk loaded: " + event.getChunk().getX() + " " + event.getChunk().getZ()); Husk[] husks = Arrays.stream(entities) - .filter(entity -> entity instanceof Husk && entity.getPersistentDataContainer().has(huskKey)) + .filter(entity -> entity instanceof Husk && entity.getPersistentDataContainer().has(huskKey, PersistentDataType.INTEGER)) .map(Husk.class::cast) .toArray(Husk[]::new); @@ -167,7 +224,7 @@ public class GiantProcessor implements Listener { } for (Entity passenger : entity.getPassengers()) { - if (passenger.getPersistentDataContainer().has(huskKey)) { + if (passenger.getPersistentDataContainer().has(huskKey, PersistentDataType.INTEGER)) { ((LivingEntity) passenger).setHealth(0); logger.fine("Killed a Husk"); } diff --git a/src/main/java/eu/m724/giants/GiantsCommand.java b/src/main/java/eu/m724/giants/GiantsCommand.java index 717c9df..a8337ef 100644 --- a/src/main/java/eu/m724/giants/GiantsCommand.java +++ b/src/main/java/eu/m724/giants/GiantsCommand.java @@ -9,6 +9,8 @@ import org.bukkit.inventory.ItemStack; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -16,9 +18,11 @@ import java.util.Map; public class GiantsCommand implements CommandExecutor { private final GiantsPlugin plugin; + private final Configuration configuration; - public GiantsCommand(GiantsPlugin plugin) { + public GiantsCommand(GiantsPlugin plugin, Configuration configuration) { this.plugin = plugin; + this.configuration = configuration; } @Override @@ -57,22 +61,73 @@ public class GiantsCommand implements CommandExecutor { list.add(map); YamlConfiguration yamlConfiguration = new YamlConfiguration(); - yamlConfiguration.set("v", list); - yamlConfiguration.setInlineComments("v", List.of("Copy the below content to your config.yml")); + try { + Method method = yamlConfiguration.getClass().getMethod("setInlineComments", String.class, List.class); + + yamlConfiguration.set("v", list); + method.invoke(yamlConfiguration, "v", List.of("Copy the below content to your config.yml")); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + yamlConfiguration.set("v", null); // two latter exceptions happen after setting + yamlConfiguration.set("copy_everything_below_to_config_yml", list); + } long now = System.currentTimeMillis(); String name = "item-" + now + ".yml"; + File file = new File(plugin.getDataFolder(), name); + try { - yamlConfiguration.save(new File(plugin.getDataFolder(), name)); + yamlConfiguration.save(file); sender.sendMessage("Saved to plugins/Giants/" + name + ". To add it as a drop, see instructions in the file."); } catch (IOException e) { - e.printStackTrace(); sender.sendMessage("Error saving file. See console for details."); + throw new RuntimeException("Error saving file to " + file.getName(), e); } } else { sender.sendMessage("Only players can use this command."); } + } else if (action.equals("jm")) { // TODO remove + if (args.length > 1) { + int mode = Integer.parseInt(args[1]); + configuration.jumpMode = mode; + + sender.sendMessage("Jump mode set to " + mode); + sender.sendMessage("This command doesn't check if it's valid, an invalid value turns off jumping."); + + if (configuration.jumpHeight == -1) { + configuration.jumpHeight = configuration.defaultJumpHeight(); + sender.sendMessage("Jump height set to " + configuration.jumpHeight + ". Modify it with /giants jumpheight"); + } + } else { + sender.sendMessage("Jump mode: " + configuration.jumpMode); + } + } else if (action.equals("jh")) { + if (args.length > 1) { + double height = Double.parseDouble(args[1]); + configuration.jumpHeight = height; + + sender.sendMessage("Jump height set to " + height); + } else { + sender.sendMessage("Jump height: " + configuration.jumpHeight); + } + } else if (action.equals("jc")) { + if (args.length > 1) { + int condition = Integer.parseInt(args[1]); + configuration.jumpCondition = condition; + + sender.sendMessage("Jump condition set to " + condition); + } else { + sender.sendMessage("Jump condition: " + configuration.jumpCondition); + } + } else if (action.equals("jd")) { + if (args.length > 1) { + int delay = Integer.parseInt(args[1]); + configuration.jumpDelay = delay; + + sender.sendMessage("Jump delay set to " + delay); + } else { + sender.sendMessage("Jump delay: " + configuration.jumpDelay); + } } return true; diff --git a/src/main/java/eu/m724/giants/GiantsPlugin.java b/src/main/java/eu/m724/giants/GiantsPlugin.java index 711a22f..9823dad 100644 --- a/src/main/java/eu/m724/giants/GiantsPlugin.java +++ b/src/main/java/eu/m724/giants/GiantsPlugin.java @@ -22,7 +22,7 @@ public class GiantsPlugin extends JavaPlugin implements CommandExecutor { configuration.load(); - getCommand("giants").setExecutor(new GiantsCommand(this)); + getCommand("giants").setExecutor(new GiantsCommand(this, configuration)); giantProcessor.start(); } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index fa9ca98..964dab4 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -4,6 +4,23 @@ # If disabled, the giant will not move or attack ai: true +# Makes giants jump. Very experimental. +# I prefer velocity mode +# 0 - disabled +# 1 - velocity mode +# 2 - tp mode +jumpMode: 0 +# 0 - when target is higher +# 1 - when target is higher and giant is stuck +# 2 - when giant is stuck +jumpCondition: 1 +# Delay before jumping again, counted from hitting the ground. In milliseconds +jumpDelay: 200 +# If velocity mode, y blocks/tick (default 0.42) +# If tp mode, +y to teleport (default 1.2) +# -1: auto +jumpHeight: -1 + # In hearts, 0.5 is half a heart attackDamage: 1.0 @@ -15,8 +32,8 @@ attackDelay: 20 # There's no wall check yet, so it will hit through walls attackReach: 2 -# The value above does not modify vertical reach, this value extends it upwards -expandUp: 0 +# Vertical reach +attackVerticalReach: 1 ###