From b2012f83b5e54e48359f7d20a54773a8e99fc0aa Mon Sep 17 00:00:00 2001 From: BuildTools Date: Sat, 15 Feb 2020 21:18:31 -0700 Subject: [PATCH] Adds secret horses and their config. - Adds secret horses, which can be acquired... secretly. --- .../horseenhancer/CommandHandler.java | 13 ++- .../horseenhancer/HorseSpawnEventHandler.java | 108 +++++++++++++----- .../horseenhancer/util/SpecialHorses.java | 85 ++++++++++++++ 3 files changed, 179 insertions(+), 27 deletions(-) create mode 100644 src/com/nevakanezah/horseenhancer/util/SpecialHorses.java diff --git a/src/com/nevakanezah/horseenhancer/CommandHandler.java b/src/com/nevakanezah/horseenhancer/CommandHandler.java index 5487e78..21e0c28 100644 --- a/src/com/nevakanezah/horseenhancer/CommandHandler.java +++ b/src/com/nevakanezah/horseenhancer/CommandHandler.java @@ -18,6 +18,7 @@ import org.bukkit.entity.Player; import com.nevakanezah.horseenhancer.util.ArgumentParser; +import com.nevakanezah.horseenhancer.util.SpecialHorses; import com.nevakanezah.horseenhancer.util.StorableHashMap; import net.md_5.bungee.api.ChatColor; @@ -426,13 +427,23 @@ private boolean horseSummon(CommandSender sender, String[] args) { type = EntityType.LLAMA; break; case "skeleton": + case "skeleton_horse": type = EntityType.SKELETON_HORSE; gender = "UNDEAD"; break; case "zombie": + case "zombie_horse": type = EntityType.ZOMBIE_HORSE; gender = "UNDEAD"; break; + case "maximule": + HorseData maximuleData = new HorseData(); + SpecialHorses.spawnMaximule(((Entity)sender).getLocation(), maximuleData); + return true; + case "invincible": + HorseData invincibleData = new HorseData(); + SpecialHorses.spawnInvincible(((Entity)sender).getLocation(), invincibleData); + return true; case "help": showSummonUsage(sender); return true; @@ -648,7 +659,7 @@ private boolean horseUpdate(CommandSender sender, String[] args) { ArrayList matches = new ArrayList<>(); for(HorseData horseData : horseList.values()) { AbstractHorse candidate = (AbstractHorse)Bukkit.getEntity(horseData.getUniqueID()); - String name = candidate.getCustomName(); + String name = candidate.getCustomName() == null ? "" : candidate.getCustomName(); if(horseData.getHorseID().equalsIgnoreCase(searchParam) || (name != null && name.equalsIgnoreCase(searchParam)) diff --git a/src/com/nevakanezah/horseenhancer/HorseSpawnEventHandler.java b/src/com/nevakanezah/horseenhancer/HorseSpawnEventHandler.java index ae80c19..498dcb7 100644 --- a/src/com/nevakanezah/horseenhancer/HorseSpawnEventHandler.java +++ b/src/com/nevakanezah/horseenhancer/HorseSpawnEventHandler.java @@ -7,8 +7,10 @@ import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.attribute.Attribute; import org.bukkit.entity.AbstractHorse; +import org.bukkit.entity.Donkey; import org.bukkit.entity.EntityType; import org.bukkit.entity.Horse; import org.bukkit.entity.Player; @@ -18,8 +20,10 @@ import org.bukkit.event.Listener; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.potion.PotionEffectType; import org.bukkit.event.entity.EntityBreedEvent; +import com.nevakanezah.horseenhancer.util.SpecialHorses; import com.nevakanezah.horseenhancer.util.StorableHashMap; import net.md_5.bungee.api.ChatColor; @@ -30,7 +34,7 @@ public class HorseSpawnEventHandler implements Listener { static final String MOVE_SPEED = "GENERIC_MOVEMENT_SPEED"; static final String MAX_HEALTH = "GENERIC_MAX_HEALTH"; - static final String JUMP_STRENGTH = "JUMP_STRENGTH"; + static final String JUMP_STRENGTH = "HORSE_JUMP_STRENGTH"; public HorseSpawnEventHandler(HorseEnhancerPlugin plugin) { this.plugin = plugin; @@ -101,7 +105,8 @@ public void onHorseSpawn(CreatureSpawnEvent event) { * @param childData The data for the child that we will operate on * @return The modified childData */ - private HorseData handleBreedingEvent(CreatureSpawnEvent event, HorseData childData) { + private HorseData handleBreedingEvent(CreatureSpawnEvent event, HorseData childData) { + final boolean secretHorsesEnabled = plugin.getConfig().getBoolean("enable-secret-horses"); // Declare the horses & data containers involved AbstractHorse child = (AbstractHorse) event.getEntity(); AbstractHorse father = (AbstractHorse) Bukkit.getEntity(childData.getFatherID()); @@ -141,29 +146,22 @@ private HorseData handleBreedingEvent(CreatureSpawnEvent event, HorseData childD Location loc = mother.getLocation(); child.remove(); - AbstractHorse newChild = (AbstractHorse)loc.getWorld().spawnEntity(loc, EntityType.ZOMBIE_HORSE); - childData.setUniqueID(newChild.getUniqueId()); - childData.setType(EntityType.ZOMBIE_HORSE); + AbstractHorse newChild = SpecialHorses.spawnInbred(loc, childData); - newChild.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.1); - newChild.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(10); - newChild.setTamed(true); - newChild.setAge(-9999999); - newChild.setJumpStrength(0.3); - newChild.setAgeLock(true); - newChild.setBreed(false); - - childData.setGender("INBRED"); registerHorse(newChild.getUniqueId(), childData); return null; } + if(secretHorsesEnabled && handleSecretHorses(child, father, mother, childData, fatherData, motherData)) + return childData; + //Apply modified stats to the child child.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue( Math.max( 0.1125, Math.min( 0.3375, getAttributeFromParents(father, mother, MOVE_SPEED )))); child.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue( Math.max( 15, Math.min( 30, getAttributeFromParents(father, mother, MAX_HEALTH )))); - child.setJumpStrength( Math.max( 0.4, Math.min( 1.0, getAttributeFromParents(father, mother, JUMP_STRENGTH ) ))); + child.getAttribute(Attribute.HORSE_JUMP_STRENGTH).setBaseValue( + Math.max( 0.4, Math.min( 1.0, getAttributeFromParents(father, mother, JUMP_STRENGTH ) ))); // Coat colouration handling if(child.getType().equals(EntityType.HORSE)) { @@ -173,7 +171,7 @@ private HorseData handleBreedingEvent(CreatureSpawnEvent event, HorseData childD return childData; } - + /** * Determine the colour of the child based loosely on the colours of the parents, * instead of randomly. @@ -220,16 +218,9 @@ protected double getAttributeFromParents(AbstractHorse father, AbstractHorse mot double bias = Math.random(); double result = 0; - if(attr.contentEquals(MOVE_SPEED) - || attr.contentEquals(MAX_HEALTH)) { - result = (father.getAttribute(Attribute.valueOf(attr)).getBaseValue() * bias) - + (mother.getAttribute(Attribute.valueOf(attr)).getBaseValue() * (1-bias)); - } - else if(attr.contentEquals(JUMP_STRENGTH)) { - result = (father.getJumpStrength() * bias) - + (mother.getJumpStrength() * (1-bias)); - } - + result = (father.getAttribute(Attribute.valueOf(attr)).getBaseValue() * bias) + + (mother.getAttribute(Attribute.valueOf(attr)).getBaseValue() * (1-bias)); + // Convert skew to its value as a % of result, then apply that value to the result skew *= result; result += skew; @@ -252,4 +243,69 @@ private String registerHorse(UUID uuid, HorseData horseData) { return output; } + /** + * Evaluates whether to spawn one of the easter egg horse varieties, and handles + * spawning and attribute assignment. Secret horses are acquired through breeding + * parents with particular circumstances, and only one secret horse may exist + * at a time on the server. + * @param child The new horse entity to update or replace + * @param father The horse's father + * @param mother The horse's mother + * @param childData The horse's data container to be updated + * @param fatherData The father's data container, for evaluating conditions + * @param motherData The mother's data container, for evaluating conditions + * @return true if a secret horse was born, false otherwise. + */ + private boolean handleSecretHorses(AbstractHorse child, AbstractHorse father, AbstractHorse mother, + HorseData childData, HorseData fatherData, HorseData motherData) { + Location loc = child.getLocation(); + double fSpeed = father.getAttribute(Attribute.valueOf(MOVE_SPEED)).getBaseValue(); + double fHealth = father.getAttribute(Attribute.valueOf(MAX_HEALTH)).getBaseValue(); + double fJump = father.getAttribute(Attribute.valueOf(JUMP_STRENGTH)).getBaseValue(); + double mSpeed = mother.getAttribute(Attribute.valueOf(MOVE_SPEED)).getBaseValue(); + double mHealth = mother.getAttribute(Attribute.valueOf(MAX_HEALTH)).getBaseValue(); + double mJump = mother.getAttribute(Attribute.valueOf(JUMP_STRENGTH)).getBaseValue(); + + // A better-than-vanilla horse born to an unlikely couple + boolean maximule = false; + + // Why is it called invincible if I can still see it? Well not anymore! + boolean invincible = false; + + for(HorseData horseData : horseList.values()) { + if(horseData.getGenderName().equalsIgnoreCase("UNIQUE")) + return false; + } + + if(((father instanceof Horse && mother instanceof Donkey) + && (0.1125 <= fSpeed && fSpeed <= 0.135) + && (0.4 <= fJump && fJump <= 0.46) + && (15 <= fHealth && fHealth < 17) + && (28.5 <= mHealth && mHealth <= 30)) + || ((father instanceof Donkey && mother instanceof Horse) + && (0.1125 <= mSpeed && mSpeed <= 0.135) + && (0.4 <= mJump && mJump <= 0.46) + && (15 <= mHealth && mHealth < 17) + && (28.5 <= fHealth && fHealth <= 30))) { + maximule = true; + } + else if(father.hasPotionEffect(PotionEffectType.INVISIBILITY) + && ((Horse)father).getInventory().getArmor().getType().equals(Material.GOLD_BARDING) + && mother.hasPotionEffect(PotionEffectType.INVISIBILITY) + && ((Horse)mother).getInventory().getArmor().getType().equals(Material.GOLD_BARDING)) { + invincible = true; + } + + if(maximule || invincible) + child.remove(); + + if(maximule) + SpecialHorses.spawnMaximule(loc, childData); + + if(invincible) + SpecialHorses.spawnInvincible(loc, childData); + + return true; + } + } diff --git a/src/com/nevakanezah/horseenhancer/util/SpecialHorses.java b/src/com/nevakanezah/horseenhancer/util/SpecialHorses.java new file mode 100644 index 0000000..73c7a32 --- /dev/null +++ b/src/com/nevakanezah/horseenhancer/util/SpecialHorses.java @@ -0,0 +1,85 @@ +package com.nevakanezah.horseenhancer.util; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.attribute.Attribute; +import org.bukkit.entity.AbstractHorse; +import org.bukkit.entity.EntityType; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import com.nevakanezah.horseenhancer.HorseData; + +import net.md_5.bungee.api.ChatColor; + +public class SpecialHorses { + + private SpecialHorses() { + super(); + } + + /** + * Spawn a horse whose parents were maybe a little too close. + * @param loc Location to spawn the new entity + * @param horseData Data container to be attached to the new entity + * @return the newly-spawned horse + */ + public static AbstractHorse spawnInbred(Location loc, HorseData horseData) { + AbstractHorse horse = (AbstractHorse)loc.getWorld().spawnEntity(loc, EntityType.ZOMBIE_HORSE); + horseData.setUniqueID(horse.getUniqueId()); + horseData.setType(EntityType.ZOMBIE_HORSE); + + horse.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.1); + horse.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(10); + horse.getAttribute(Attribute.HORSE_JUMP_STRENGTH).setBaseValue(0.3); + horse.setTamed(true); + horse.setAge(-9999999); + horse.setAgeLock(true); + horse.setBreed(false); + + horseData.setGender("INBRED"); + + return horse; + } + + public static AbstractHorse spawnMaximule(Location loc, HorseData horseData) { + AbstractHorse horse = (AbstractHorse)loc.getWorld().spawnEntity(loc, EntityType.MULE); + horseData.setUniqueID(horse.getUniqueId()); + horseData.setType(EntityType.MULE); + + horse.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.35); + horse.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(35); + horse.getAttribute(Attribute.HORSE_JUMP_STRENGTH).setBaseValue(1.18); + horse.setCustomName(ChatColor.DARK_RED + "MA"+ ChatColor.GOLD + "XI" + ChatColor.DARK_BLUE + "MU" + ChatColor.DARK_GREEN + "LE"); + horse.setBreed(false); + horse.setAge(0); + horse.setAgeLock(true); + horse.playEffect(EntityEffect.FIREWORK_EXPLODE); + + horseData.setGender("UNIQUE"); + + return horse; + } + + public static AbstractHorse spawnInvincible(Location loc, HorseData horseData) { + AbstractHorse horse = (AbstractHorse)loc.getWorld().spawnEntity(loc, EntityType.HORSE); + horseData.setUniqueID(horse.getUniqueId()); + horseData.setType(EntityType.HORSE); + + horse.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.38); + horse.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(15); + horse.getAttribute(Attribute.HORSE_JUMP_STRENGTH).setBaseValue(0.565); + horse.setCustomName(ChatColor.DARK_BLUE + "Invincible"); + horse.setAge(0); + horse.setAgeLock(true); + horse.setTamed(true); + horse.setBreed(false); + horse.playEffect(EntityEffect.FIREWORK_EXPLODE); + PotionEffect invis = new PotionEffect(PotionEffectType.INVISIBILITY, 2147483000, 1, false, false); + horse.addPotionEffect(invis, true); + horseData.setGender("UNIQUE"); + + return horse; + } + +}