diff --git a/src/main/java/ca/gibstick/discosheep/DiscoCommands.java b/src/main/java/ca/gibstick/discosheep/DiscoCommands.java deleted file mode 100644 index 728da95..0000000 --- a/src/main/java/ca/gibstick/discosheep/DiscoCommands.java +++ /dev/null @@ -1,315 +0,0 @@ -package ca.gibstick.discosheep; - -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.NestedCommand; -import java.util.Arrays; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandException; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -/** - * - * @author Charlie - */ -public class DiscoCommands { - - static final String PERMISSION_PARTY = "discosheep.party.me"; - static final String PERMISSION_ALL = "discosheep.party.all"; - static final String PERMISSION_FIREWORKS = "discosheep.party.fireworks"; - static final String PERMISSION_STOPALL = "discosheep.admin.stopall"; - static final String PERMISSION_RELOAD = "discosheep.admin.reload"; - static final String PERMISSION_OTHER = "discosheep.party.other"; - static final String PERMISSION_CHANGEPERIOD = "discosheep.party.changeperiod"; - static final String PERMISSION_CHANGEDEFAULTS = "discosheep.admin.changedefaults"; - static final String PERMISSION_SAVECONFIG = "discosheep.admin.saveconfig"; - static final String PERMISSION_ONJOIN = "discosheep.party.onjoin"; - static final String PERMISSION_SPAWNGUESTS = "discosheep.party.spawnguests"; - static final String PERMISSION_TOGGLEPARTYONJOIN = "discosheep.admin.toggleonjoin"; - static final String PERMISSION_LIGHTNING = "discosheep.party.lightning"; - static final String PERMISSION_DANCEFLOOR = "discosheep.party.dancefloor"; - - static final String FLAGS = "n:t:p:r:lfgd"; - - private static final DiscoSheep plugin = DiscoSheep.getInstance(); - - public static class ParentCommand { - - @Command(aliases = {"ds", "discosheep"}, desc = "Main Discosheep Command (see /ds help)", min = 0, max = -1) - @NestedCommand(DiscoCommands.class) - public static void DiscoCommand(final CommandContext args, CommandSender sender) throws CommandException { - } - } - - private static boolean getNextArg(String[] args, int i, String compare) { - if (i < args.length - 1) { - return args[i + 1].equalsIgnoreCase(compare); - } - return false; - } - - private static String getNextArg(String[] args, int i) { - if (i < args.length - 1) { - return args[i + 1]; - } else { - return null; - } - } - - // return portion of the array that contains space-separated args, - // stopping at the end of the array or the next -switch - private static String[] getNextArgs(String[] args, int i) { - int j = i; - while (j < args.length && !args[j].startsWith("-")) { - j++; - } - return Arrays.copyOfRange(args, i, j); - } - - private static int getNextIntArg(String[] args, int i) { - if (i < args.length - 1) { - try { - return Integer.parseInt(args[i + 1]); - } catch (NumberFormatException e) { - return -1; // so that it fails limit checks elsewhere - } - } - return -1; // ibid - } - - private static void parsePartyFlags(DiscoParty party, final CommandContext args, CommandSender sender) throws IllegalArgumentException { - party.setPeriod(args.getFlagInteger('p', DiscoParty.defaultPeriod)); - party.setSheep(args.getFlagInteger('n', DiscoParty.defaultSheep)); - - if (sender.hasPermission(PERMISSION_DANCEFLOOR)) { - party.setDoFloor(args.hasFlag('d')); - } - - // handle special case of duration conversion ugh - if (args.hasFlag('t')) { - int duration = args.getFlagInteger('t'); - party.setDuration(plugin.toTicks(duration)); - } - - // handle the special case of radius flag arg "dense" - String radiusArg = args.getFlag('r', Integer.toString(DiscoParty.defaultRadius)); - if ("dense".equals(radiusArg)) { - party.setDenseRadius(party.getSheep()); - } else { - party.setRadius(Integer.parseInt(radiusArg)); - } - - // party will still start if player doesn't have permission for extras - if (sender.hasPermission(PERMISSION_FIREWORKS)) { - party.setDoFireworks(args.hasFlag('f')); - } else { - plugin.noPermsMessage(sender, PERMISSION_FIREWORKS); - } - - if (sender.hasPermission(PERMISSION_LIGHTNING)) { - party.setDoLightning(args.hasFlag('l')); - } else { - plugin.noPermsMessage(sender, PERMISSION_LIGHTNING); - } - - // handle guests - if (args.hasFlag('g')) { - // stop if no permission for spawning guests - if (!sender.hasPermission(PERMISSION_SPAWNGUESTS)) { - plugin.noPermsMessage(sender, PERMISSION_SPAWNGUESTS); - return; - } - - String dirtyArgs[] = args.getParsedSlice(0); - //sender.sendMessage(dirtyArgs); - for (int i = 0; i < dirtyArgs.length; i++) { - if ("none".equals(dirtyArgs[0])) { // wtf?? - plugin.clearGuests(party); - return; - } - - String[] guests = dirtyArgs; - int j = 0; - while (j < guests.length - 1) { - try { - party.setGuestNumber(guests[j].toUpperCase(), getNextIntArg(guests, j)); - } catch (IllegalArgumentException e) { - sender.sendMessage(ChatColor.RED + "Invalid arguments: " + ChatColor.WHITE + guests[j] + ", " + guests[j + 1] - + ".\nEither a name typo or a number that is not within limits."); - } - j += 2; // skip over two arguments, since they come in pairs of entity-number - } - } - - } - } - - /*-- Actual commands begin here --*/ - @Command(aliases = "help", desc = "DiscoSheep help", usage = "No arguments", min = 0, max = -1) - public static void helpCommand(CommandContext args, CommandSender sender) { - sender.sendMessage(ChatColor.YELLOW - + "DiscoSheep Help\n" - + ChatColor.GRAY - + " Subcommands\n" - + ChatColor.WHITE + "me, stop, all, stopall, save, reload, togglejoin\n" - + "other : start a party for the space-delimited list of players\n" - + "defaults: Change the default settings for parties (takes normal arguments)\n" - + ChatColor.GRAY + " Arguments\n" - + ChatColor.WHITE + "-n : set the number of sheep per player that spawn\n" - + "-t : set the party duration in seconds\n" - + "-p : set the number of ticks between each disco beat\n" - + "-r : set radius of the area in which sheep can spawn\n" - + "-g : set spawns for other mobs\n" - + "-l: enables lightning\n" - + "-f: enables fireworks"); - } - - @Command(aliases = {"stop", "stoppls", "wtf"}, desc = "Stop your own disco party", usage = "No arguments", min = 0, max = 0) - public static void stopMeCommand(final CommandContext args, CommandSender sender) throws CommandException { - plugin.stopParty(sender.getName()); - } - - @Command(aliases = {"stopall"}, desc = "Stop all disco parties on the server", usage = "No arguments", min = 0, max = 0) - @CommandPermissions(value = PERMISSION_STOPALL) - public static void stopAllCommand(final CommandContext args, CommandSender sender) throws CommandException { - plugin.stopAllParties(); - } - - @Command(aliases = {"reload"}, desc = "Reload DiscoSheep configuration from file", usage = "No arguments", min = 0, max = 0) - public static void reloadCommand(final CommandContext args, CommandSender sender - ) { - plugin.reloadConfigFromDisk(); - sender.sendMessage(ChatColor.GREEN + "DiscoSheep config reloaded from file."); - } - - @Command( - aliases = {"me", "party", "partay", "turnup"}, - desc = "Start your own private DiscoParty", - usage = "[optional flags]", - min = 0, - max = -1, - flags = FLAGS - ) - @CommandPermissions(value = PERMISSION_PARTY) - public static void partyCommand(final CommandContext args, CommandSender sender) { - if (!(sender instanceof Player)) { - sender.sendMessage("You must be a player to have a party"); - return; - } - Player player = (Player) sender; - if (!plugin.hasParty(player.getName())) { - DiscoParty party = new DiscoParty(player); - parsePartyFlags(party, args, sender); - party.startDisco(); - } else { - player.sendMessage(ChatColor.RED + "You already have a party."); - } - } - - @SuppressWarnings("deprecation") - // UUIDs not necessary since DiscoSheep only lasts for one session at most - // and permissions will handle onJoin DiscoSheep - @Command( - aliases = {"other", "yall"}, - desc = "Start a party for other players", - usage = "[optional flags]", - min = 0, - max = -1, - flags = FLAGS - ) - @CommandPermissions(value = PERMISSION_OTHER) - public static void partyOtherCommand(CommandContext args, CommandSender sender) { - DiscoParty party = new DiscoParty(); - parsePartyFlags(party, args, sender); - String players[] = args.getParsedSlice(0); - - Player p; - for (String playerName : players) { - p = Bukkit.getServer().getPlayer(playerName); - if (p != null) { - if (!plugin.hasParty(p.getName())) { - DiscoParty individualParty = party.clone(p); - individualParty.startDisco(); - } - } else { - sender.sendMessage("Invalid player: " + playerName); - } - } - } - - @Command( - aliases = {"all", "allturnup"}, - desc = "Start a party for all players on the server", - usage = "[optional flags]", - min = 0, - max = -1, - flags = FLAGS - ) - @CommandPermissions(value = PERMISSION_ALL) - public static void partyAllCommand(final CommandContext args, CommandSender sender - ) { - DiscoParty party = new DiscoParty(); - parsePartyFlags(party, args, sender); - for (Player p : Bukkit.getServer().getOnlinePlayers()) { - if (!plugin.hasParty(p.getName())) { - DiscoParty individualParty = party.clone(p); - individualParty.startDisco(); - p.sendMessage(ChatColor.RED + "LET'S DISCO!!"); - } - } - } - - @Command( - aliases = {"togglejoin", "toggleonjoin"}, - desc = "Start a party for all players on the server", - usage = "[optional flags]", - min = 0, - max = -1, - flags = FLAGS - ) - @CommandPermissions(value = PERMISSION_TOGGLEPARTYONJOIN) - public static void togglePartyOnJoinCommand(final CommandContext args, CommandSender sender - ) { - boolean result = plugin.toggleOnJoin(); - if (result) { - sender.sendMessage(ChatColor.GREEN + "DiscoSheep party on join functionality enabled."); - } else { - sender.sendMessage(ChatColor.GREEN + "DiscoSheep party on join functionality disabled."); - } - } - - @Command( - aliases = {"defaults", "setdefaults"}, - desc = "Change the default party settings", - usage = "[optional flags]", - min = 0, - max = -1, - flags = FLAGS - ) - @CommandPermissions(value = PERMISSION_CHANGEDEFAULTS) - public static void setDefaultsCommand(final CommandContext args, CommandSender sender - ) { - DiscoParty party = new DiscoParty(); - parsePartyFlags(party, args, sender); - party.setDefaultsFromCurrent(); - sender.sendMessage(ChatColor.GREEN + "DiscoSheep configured with new defaults (not saved to disk yet)"); - } - - @Command( - aliases = {"defaults", "setdefaults"}, - desc = "Change the default party settings", - usage = "[optional flags]", - min = 0, - max = -1, - flags = FLAGS - ) - @CommandPermissions(value = PERMISSION_SAVECONFIG) - public static void saveConfigCommand(final CommandContext args, CommandSender sender - ) { - plugin.saveConfigToDisk(); - sender.sendMessage(ChatColor.GREEN + "DiscoSheep config saved to disk"); - } -} diff --git a/src/main/java/ca/gibstick/discosheep/DiscoParty.java b/src/main/java/ca/gibstick/discosheep/DiscoParty.java deleted file mode 100644 index a522b3a..0000000 --- a/src/main/java/ca/gibstick/discosheep/DiscoParty.java +++ /dev/null @@ -1,554 +0,0 @@ -package ca.gibstick.discosheep; - -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Random; -import org.bukkit.Color; -import org.bukkit.DyeColor; -import static org.bukkit.EntityEffect.*; -import org.bukkit.FireworkEffect; -import org.bukkit.FireworkEffect.Builder; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Firework; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; -import org.bukkit.entity.Sheep; -import org.bukkit.event.HandlerList; -import org.bukkit.inventory.meta.FireworkMeta; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.util.Vector; - -public class DiscoParty { - - static int defaultDuration = 300; // ticks for entire party - static int defaultPeriod = 10; // ticks per state change - static int defaultRadius = 5; - static int defaultSheep = 10; - static float defaultSheepJump = 0.35f; - static int maxFloorSize = 5; - static int maxDuration = 2400; // 120 seconds - static int maxSheep = 100; - static int maxRadius = 100; - static int minPeriod = 5; // 0.25 seconds - static int maxPeriod = 40; // 2.0 seconds - private static HashMap defaultGuestNumbers = new HashMap<>(); - private static HashMap maxGuestNumbers = new HashMap<>(); - private static final EnumSet floorExceptions = EnumSet.of( - //Material.STAINED_GLASS, - Material.FURNACE, - Material.CHEST, - Material.ENDER_CHEST, - Material.BURNING_FURNACE, - Material.ENDER_PORTAL, - Material.ENDER_PORTAL_FRAME, - Material.OBSIDIAN, - Material.BED, - Material.BED_BLOCK, - Material.SOIL - ); - - private static final DyeColor[] discoColours = { - DyeColor.RED, - DyeColor.ORANGE, - DyeColor.YELLOW, - DyeColor.GREEN, - DyeColor.BLUE, - DyeColor.LIGHT_BLUE, - DyeColor.PINK, - DyeColor.MAGENTA, - DyeColor.LIME, - DyeColor.CYAN, - DyeColor.PURPLE, - DyeColor.BLACK, - DyeColor.WHITE - }; - - private static final float[] pentatonicNotes = { - 1.0f, - 1.125f, - 1.25f, - 1.5f, - 1.667f, - 2.0f - }; - // Instance properties - private Random r = new Random(); - private PartyEvents partyEvents; - private final DiscoSheep plugin = DiscoSheep.getInstance(); - private Player player; - private final ArrayList sheepList = new ArrayList<>(); - private final HashSet sheepSet = new HashSet<>(); - private final ArrayList guestList = new ArrayList<>(); - private final HashSet guestSet = new HashSet<>(); - private final ArrayList floorBlockCache = new ArrayList<>(); - private final ArrayList floorBlocks = new ArrayList<>(); - private HashMap guestNumbers = new HashMap<>(); - private boolean doFireworks = false; - private final boolean doJump = true; - private boolean doLightning = false; - private boolean doFloor = false; - private int duration, period, radius, sheep; - private int state = 0; // basically our own tick system - private float volumeMultiplier; - private Location partyLocation; - private DiscoUpdater updater; - - public DiscoParty(Player player) { - this(); - this.player = player; - this.partyLocation = player.getLocation(); - } - - public DiscoParty() { - this.duration = DiscoParty.defaultDuration; - this.period = DiscoParty.defaultPeriod; - this.radius = DiscoParty.defaultRadius; - this.sheep = DiscoParty.defaultSheep; - this.guestNumbers = new HashMap<>(DiscoParty.defaultGuestNumbers); - r = new Random(); - } - - // copy but with new player - // used for /ds other and /ds all - public DiscoParty clone(Player player) { - DiscoParty newParty; - newParty = new DiscoParty(player); - newParty.doFireworks = this.doFireworks; - newParty.duration = this.duration; - newParty.period = this.period; - newParty.radius = this.radius; - newParty.sheep = this.sheep; - newParty.doLightning = this.doLightning; - newParty.guestNumbers = this.getGuestNumbers(); - return newParty; - } - - HashSet getSheepSet() { - return sheepSet; - } - - HashSet getGuestSet() { - return guestSet; - } - - ArrayList getSheepList() { - return sheepList; - } - - ArrayList getGuestList() { - return guestList; - } - - ArrayList getFloorCache() { - return this.floorBlockCache; - } - - ArrayList getFloorBlocks() { - return this.floorBlocks; - } - - public int getRadius() { - return radius; - } - - public static HashMap getDefaultGuestNumbers() { - return defaultGuestNumbers; - } - - public HashMap getGuestNumbers() { - return guestNumbers; - } - - public static HashMap getMaxGuestNumbers() { - return maxGuestNumbers; - } - - public int getSheep() { - return this.sheep; - } - - public DiscoParty setPlayer(Player player) { - this.player = player; - this.partyLocation = player.getLocation(); - return this; - } - - public DiscoParty setDuration(int duration) throws IllegalArgumentException { - if (duration <= DiscoParty.maxDuration && duration > 0) { - this.duration = duration; - return this; - } else { - throw new IllegalArgumentException(); - } - } - - public DiscoParty setPeriod(int period) throws IllegalArgumentException { - if (period >= DiscoParty.minPeriod && period <= DiscoParty.maxPeriod) { - this.period = period; - return this; - } else { - throw new IllegalArgumentException(); - } - } - - public DiscoParty setRadius(int radius) throws IllegalArgumentException { - if (radius <= DiscoParty.maxRadius && radius > 0) { - this.radius = radius; - return this; - } else { - throw new IllegalArgumentException(); - } - } - - public DiscoParty setDenseRadius(int sheepNo) throws IllegalArgumentException { - Integer rand = (int) Math.floor(Math.sqrt(sheep / Math.PI)); - if (rand > DiscoParty.maxRadius) { - rand = DiscoParty.maxRadius; - } - if (rand < 1) { - rand = 1; - } - - this.setRadius(rand); - return this; - } - - public DiscoParty setSheep(int sheep) throws IllegalArgumentException { - if (sheep <= DiscoParty.maxSheep && sheep > 0) { - this.sheep = sheep; - return this; - } else { - throw new IllegalArgumentException(); - } - } - - public DiscoParty setDoFireworks(boolean doFireworks) { - this.doFireworks = doFireworks; - return this; - } - - public DiscoParty setDoLightning(boolean doLightning) { - this.doLightning = doLightning; - return this; - } - - public void setDoFloor(boolean doFloor) { - this.doFloor = doFloor; - } - - public DiscoParty setGuestNumber(String key, int n) throws IllegalArgumentException { - if (getMaxGuestNumbers().containsKey(key.toUpperCase())) { - if (n <= getMaxGuestNumbers().get(key.toUpperCase()) && n >= 0) { // so that /ds defaults can take 0 as arg - getGuestNumbers().put(key, n); - - return this; - } - } - throw new IllegalArgumentException(); - } - - // use current settings as new defaults - public DiscoParty setDefaultsFromCurrent() { - DiscoParty.defaultDuration = this.duration; - DiscoParty.defaultPeriod = this.period; - DiscoParty.defaultRadius = this.radius; - DiscoParty.defaultSheep = this.sheep; - DiscoParty.defaultGuestNumbers = new HashMap<>(this.getGuestNumbers()); - return this; - } - - Location getRandomSpawnLocation(double x, double z, World world, int spawnRadius) { - Location loc; - - double y; - - /* random point on circle with polar coordinates - * random number must be square rooted to obtain uniform distribution - * otherwise the sheep are biased toward the centre */ - double rand = Math.sqrt(r.nextDouble()) * spawnRadius; - double azimuth = r.nextDouble() * 2 * Math.PI; // radians - x += rand * Math.cos(azimuth); - z += rand * Math.sin(azimuth); - y = partyLocation.getY(); - - loc = new Location(world, x, y, z); - loc.setPitch(r.nextFloat() * 360 - 180); - loc.setYaw(r.nextFloat() * 360 - 180); - - return loc; - } - - // Spawn some number of guests next to given player - void spawnAll(int sheep, int spawnRadius) { - Location loc; - World world = player.getWorld(); - - double x = partyLocation.getX(); - double z = partyLocation.getZ(); - for (int i = 0; i < sheep; i++) { - loc = getRandomSpawnLocation(x, z, world, spawnRadius); - spawnSheep(world, loc); - } - - // loop through hashmap of other guests and spawn accordingly - for (Map.Entry entry : guestNumbers.entrySet()) { - EntityType ent = EntityType.valueOf((String) entry.getKey()); - int num = (Integer) entry.getValue(); - - for (int i = 0; i < num; i++) { - loc = getRandomSpawnLocation(x, z, world, spawnRadius); - spawnGuest(world, loc, ent); - } - } - - if (doFloor) { - this.spawnFloor(world, new Location(world, partyLocation.getBlockX(), partyLocation.getBlockY() - 1, partyLocation.getBlockZ())); - } - } - - void spawnSheep(World world, Location loc) { - Sheep newSheep = (Sheep) world.spawnEntity(loc, EntityType.SHEEP); - //newSheep.setColor(discoColours[(r.nextInt(discoColours.length))]); - newSheep.setBreed(false); // this prevents breeding - no event listener required - newSheep.teleport(loc); // teleport is needed to set orientation - getSheepList().add(newSheep); - getSheepSet().add(newSheep); - if (doLightning) { - world.strikeLightningEffect(loc); - } - //newSheep.setCustomName("jeb_"); - //newSheep.setCustomNameVisible(false); - newSheep.setRemoveWhenFarAway(false); - } - - void spawnGuest(World world, Location loc, EntityType type) { - LivingEntity newGuest = (LivingEntity) loc.getWorld().spawnEntity(loc, type); - newGuest.setRemoveWhenFarAway(false); - getGuestList().add(newGuest); - getGuestSet().add(newGuest); - if (doLightning) { - world.strikeLightningEffect(loc); - } - } - - void spawnFloor(World world, Location loc) { - // First we'll save the floor state - for (int x = loc.getBlockX() - Math.min(this.radius, DiscoParty.maxFloorSize); x < loc.getX() + Math.min(this.radius, DiscoParty.maxFloorSize); ++x) { - for (int z = loc.getBlockZ() - Math.min(this.radius, DiscoParty.maxFloorSize); z < loc.getZ() + Math.min(this.radius, DiscoParty.maxFloorSize); ++z) { - Block block = world.getBlockAt(x, loc.getBlockY(), z); - if (!DiscoParty.floorExceptions.contains(block.getType()) - && block.getRelative(BlockFace.UP).getType() == Material.AIR - && (block.getType().isSolid() || block.getType() == Material.AIR)) { - this.getFloorCache().add(block.getState()); - //block.setType(Material.STAINED_GLASS); - this.getFloorBlocks().add(block); - } - } - } - } - - // Mark all guests for removal, then clear the array - void removeAll() { - for (Sheep sheeple : getSheepList()) { - sheeple.remove(); - } - for (Entity guest : getGuestList()) { - guest.remove(); - } - - for (BlockState block : this.floorBlockCache) { - block.update(true); - } - getSheepList().clear(); - getGuestList().clear(); - getSheepSet().clear(); - getGuestSet().clear(); - floorBlockCache.clear(); - } - - // Set a random colour for all sheep in array - void randomizeSheepColour(Sheep sheep) { - sheep.setColor(discoColours[(r.nextInt(discoColours.length))]); - } - - void randomizeFloor(Block block, int index) { - int to_color = (index + state) % discoColours.length; - block.setData(discoColours[to_color].getData()); - } - - void jump(Entity entity) { - Vector orgVel = entity.getVelocity(); - Vector newVel = (new Vector()).copy(orgVel); - newVel.add(new Vector(0, defaultSheepJump, 0)); - entity.setVelocity(newVel); - } - - Color[] getColor = { - Color.AQUA, - Color.BLACK, - Color.BLUE, - Color.FUCHSIA, - Color.GRAY, - Color.GREEN, - Color.LIME, - Color.MAROON, - Color.NAVY, - Color.OLIVE, - Color.ORANGE, - Color.PURPLE, - Color.RED, - Color.SILVER, - Color.TEAL, - Color.WHITE, - Color.YELLOW - }; - - void updateAll() { - for (Sheep sheeple : getSheepList()) { - //randomizeSheepColour(sheeple); - - if (state % 8 == 0) { - if (r.nextDouble() < 0.50 && doFireworks) { - spawnRandomFireworkAtSheep(sheeple); - } - sheeple.playEffect(SHEEP_EAT); - } - - if (doJump) { - if (state % 2 == 0 && r.nextDouble() < 0.5) { - jump(sheeple); - } - } - - } - - for (Entity guest : getGuestList()) { - if (doJump) { - if (state % 2 == 0 && r.nextDouble() < 0.5) { - jump(guest); - } - } - } - -// for (int i = 0; i < this.floorBlocks.size(); i++) { -// this.randomizeFloor(floorBlocks.get(i), i); -// } - } - - float getPentatonicNote() { - return DiscoParty.pentatonicNotes[r.nextInt(pentatonicNotes.length)]; - } - - void playSounds() { - - /*for (Sheep sheep : this.getSheepList()) { - sheep.getWorld().playSound(sheep.getLocation(), Sound.NOTE_BASS_DRUM, 0.75f, 1.0f); - - if (this.state % 2 == 0) { - sheep.getWorld().playSound(sheep.getLocation(), Sound.NOTE_SNARE_DRUM, 0.8f, 1.0f); - } - - if ((this.state + 1) % 8 == 0) { - sheep.getWorld().playSound(sheep.getLocation(), Sound.NOTE_STICKS, 1.0f, 1.0f); - } - - }*/ - partyLocation.getWorld().playSound(partyLocation, Sound.NOTE_BASS_DRUM, volumeMultiplier * 0.75f, 1.0f); - if (this.state % 2 == 0) { - partyLocation.getWorld().playSound(partyLocation, Sound.NOTE_SNARE_DRUM, volumeMultiplier * 0.8f, 1.0f); - } - - if ((this.state + 1) % 8 == 0) { - partyLocation.getWorld().playSound(partyLocation, Sound.NOTE_STICKS, volumeMultiplier * 1.0f, 1.0f); - } - - } - - void randomizeFirework(Firework firework) { - Builder effect = FireworkEffect.builder(); - FireworkMeta meta = firework.getFireworkMeta(); - - // construct [1, 3] random colours - int numColours = r.nextInt(3) + 1; - Color[] colourArray = new Color[numColours]; - for (int i = 0; i < numColours; i++) { - colourArray[i] = getColor[r.nextInt(17)]; - } - - // randomize effects - effect.withColor(colourArray); - effect.flicker(r.nextDouble() < 0.5); - effect.trail(r.nextDouble() < 0.5); - effect.with(FireworkEffect.Type.values()[r.nextInt(FireworkEffect.Type.values().length)]); - - // set random effect and randomize power - meta.addEffect(effect.build()); - meta.setPower(r.nextInt(2) + 1); - - // apply it to the given firework - firework.setFireworkMeta(meta); - } - - void spawnRandomFireworkAtSheep(Sheep sheep) { - Firework firework = (Firework) sheep.getWorld().spawnEntity(sheep.getEyeLocation(), EntityType.FIREWORK); - randomizeFirework(firework); - } - - void update() { - if (duration > 0) { - updateAll(); - playSounds(); - duration -= period; - this.scheduleUpdate(); - this.state = (this.state + 1) % 10000; - } else { - this.stopDisco(); - } - } - - void scheduleUpdate() { - updater = new DiscoUpdater(); - updater.runTaskLater(plugin, this.period); - } - - void startDisco() { - this.volumeMultiplier = Math.max(this.radius / 10, 1.0f); - this.spawnAll(sheep, radius); - this.scheduleUpdate(); - plugin.getPartyMap().put(this.player.getName(), this); - // start listening - this.partyEvents = new PartyEvents(this); - plugin.getServer().getPluginManager().registerEvents(this.partyEvents, this.plugin); - } - - void stopDisco() { - removeAll(); - this.duration = 0; - if (updater != null) { - updater.cancel(); - } - updater = null; - plugin.getPartyMap().remove(this.player.getName()); - // stop listening - HandlerList.unregisterAll(this.partyEvents); - } - - class DiscoUpdater extends BukkitRunnable { - - @Override - public void run() { - update(); - } - } -} diff --git a/src/main/java/ca/gibstick/discosheep/DiscoSheep.java b/src/main/java/ca/gibstick/discosheep/DiscoSheep.java deleted file mode 100644 index 5d00790..0000000 --- a/src/main/java/ca/gibstick/discosheep/DiscoSheep.java +++ /dev/null @@ -1,247 +0,0 @@ -package ca.gibstick.discosheep; - -import com.sk89q.bukkit.util.CommandsManagerRegistration; -import com.sk89q.minecraft.util.commands.CommandPermissionsException; -import com.sk89q.minecraft.util.commands.CommandUsageException; -import com.sk89q.minecraft.util.commands.CommandsManager; -import com.sk89q.minecraft.util.commands.MissingNestedCommandException; -import com.sk89q.minecraft.util.commands.WrappedCommandException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandException; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; - -public final class DiscoSheep extends JavaPlugin { - - private static DiscoSheep instance; - - boolean partyOnJoin = true; - Map parties = new HashMap<>(); - private CommandsManager commands; - - public static DiscoSheep getInstance() { - if (instance == null) { - instance = new DiscoSheep(); - return instance; - } - return instance; - } - - private void setupCommands() { - this.commands = new CommandsManager() { - @Override - public boolean hasPermission(CommandSender sender, String perm) { - return sender instanceof ConsoleCommandSender || sender.hasPermission(perm); - } - }; - CommandsManagerRegistration cmdRegister = new CommandsManagerRegistration(this, this.commands); - cmdRegister.register(DiscoCommands.ParentCommand.class); - } - - @Override - public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { - try { - this.commands.execute(cmd.getName(), args, sender, sender); - } catch (CommandPermissionsException e) { - sender.sendMessage(ChatColor.RED + "You don't have permission."); - } catch (MissingNestedCommandException e) { - sender.sendMessage(ChatColor.RED + e.getUsage()); - } catch (CommandUsageException e) { - sender.sendMessage(ChatColor.RED + e.getMessage()); - sender.sendMessage(ChatColor.RED + e.getUsage()); - } catch (WrappedCommandException e) { - if (e.getCause() instanceof NumberFormatException) { - sender.sendMessage(ChatColor.RED + "Number expected, string received instead."); - } else if (e.getCause() instanceof IllegalArgumentException) { - sender.sendMessage(ChatColor.RED + "Illegal argument (out of bounds or bad format)."); - } else { - sender.sendMessage(ChatColor.RED + "An error has occurred. See console."); - } - } catch (CommandException e) { - sender.sendMessage(ChatColor.RED + e.getMessage()); - } catch (com.sk89q.minecraft.util.commands.CommandException ex) { - Logger.getLogger(DiscoSheep.class.getName()).log(Level.SEVERE, null, ex); - } - return true; - } - - @Override - public void onEnable() { - instance = this; - setupCommands(); - getServer().getPluginManager().registerEvents(new GlobalEvents(), this); - - getConfig().addDefault("on-join.enabled", partyOnJoin); - getConfig().addDefault("max.sheep", DiscoParty.maxSheep); - getConfig().addDefault("max.radius", DiscoParty.maxRadius); - getConfig().addDefault("max.duration", toSeconds_i(DiscoParty.maxDuration)); - getConfig().addDefault("max.period-ticks", DiscoParty.maxPeriod); - getConfig().addDefault("min.period-ticks", DiscoParty.minPeriod); - getConfig().addDefault("default.sheep", DiscoParty.defaultSheep); - getConfig().addDefault("default.radius", DiscoParty.defaultRadius); - getConfig().addDefault("default.duration", toSeconds_i(DiscoParty.defaultDuration)); - getConfig().addDefault("default.period-ticks", DiscoParty.defaultPeriod); - getConfig().addDefault("max.floor_size", DiscoParty.maxFloorSize); - - /* - * Iterate through all live entities and create default configuration values for them - * excludes bosses and other mobs that throw NPE - */ - for (EntityType ent : EntityType.values()) { - if (ent.isAlive() - && !ent.equals(EntityType.ENDER_DRAGON) - && !ent.equals(EntityType.WITHER) - && !ent.equals(EntityType.PLAYER)) { - getConfig().addDefault("default.guests." + ent.toString(), 0); - getConfig().addDefault("max.guests." + ent.toString(), 5); - } - } - - loadConfigFromDisk(); - } - - void loadConfigFromDisk() { - getConfig().options().copyDefaults(true); - saveConfig(); - - partyOnJoin = getConfig().getBoolean("on-join.enabled"); - DiscoParty.maxSheep = getConfig().getInt("max.sheep"); - DiscoParty.maxRadius = getConfig().getInt("max.radius"); - DiscoParty.maxDuration = toTicks(getConfig().getInt("max.duration")); - DiscoParty.maxPeriod = getConfig().getInt("max.period-ticks"); - DiscoParty.minPeriod = getConfig().getInt("min.period-ticks"); - DiscoParty.defaultSheep = getConfig().getInt("default.sheep"); - DiscoParty.defaultRadius = getConfig().getInt("default.radius"); - DiscoParty.defaultDuration = toTicks(getConfig().getInt("default.duration")); - DiscoParty.defaultPeriod = getConfig().getInt("default.period-ticks"); - DiscoParty.maxFloorSize = getConfig().getInt("max.floor_size"); - - for (String key : getConfig().getConfigurationSection("default.guests").getKeys(false)) { - DiscoParty.getDefaultGuestNumbers().put(key, getConfig().getInt("default.guests." + key)); - } - - for (String key : getConfig().getConfigurationSection("max.guests").getKeys(false)) { - DiscoParty.getMaxGuestNumbers().put(key, getConfig().getInt("max.guests." + key)); - } - } - - void reloadConfigFromDisk() { - reloadConfig(); - loadConfigFromDisk(); - } - - void saveConfigToDisk() { - getConfig().set("on-join.enabled", partyOnJoin); - getConfig().set("default.sheep", DiscoParty.defaultSheep); - getConfig().set("default.radius", DiscoParty.defaultRadius); - getConfig().set("default.duration", toSeconds_i(DiscoParty.defaultDuration)); - getConfig().set("default.period-ticks", DiscoParty.defaultPeriod); - - for (Map.Entry entry : DiscoParty.getDefaultGuestNumbers().entrySet()) { - getConfig().set("default.guests." + entry.getKey(), entry.getValue()); - } - saveConfig(); - } - - @Override - public void onDisable() { - this.stopAllParties(); // or else the parties will continue FOREVER - instance = null; - commands = null; - } - - int toTicks(double seconds) { - return (int) Math.round(seconds * 20.0); - } - - double toSeconds(int ticks) { - return (double) Math.round(ticks / 20.0); - } - - int toSeconds_i(int ticks) { - return (int) Math.round(ticks / 20.0); - } - - public synchronized Map getPartyMap() { - return this.parties; - } - - public synchronized ArrayList getParties() { - return new ArrayList<>(this.getPartyMap().values()); - } - - public void stopParty(String name) { - if (this.hasParty(name)) { - this.getParty(name).stopDisco(); - } - - } - - public void stopAllParties() { - for (DiscoParty party : this.getParties()) { - party.stopDisco(); - } - } - - public boolean hasParty(String name) { - return this.getPartyMap().containsKey(name); - } - - public DiscoParty getParty(String name) { - return this.getPartyMap().get(name); - } - - public boolean toggleOnJoin() { - partyOnJoin = !partyOnJoin; - return partyOnJoin; - } - - public void removeParty(String name) { - if (this.hasParty(name)) { - this.getPartyMap().remove(name); - } - } - - void partyOnJoin(Player player) { - if (!partyOnJoin) { - return; - } - if (player.hasPermission(DiscoCommands.PERMISSION_ONJOIN)) { - DiscoParty party = new DiscoParty(player); - party.startDisco(); - } - } - - boolean clearGuests(DiscoParty party) { - party.getGuestNumbers().clear(); - return true; - } - - boolean noPermsMessage(CommandSender sender, String permission) { - sender.sendMessage(ChatColor.RED + "You do not have the permission node " + ChatColor.GRAY + permission); - return false; - } - - // From user "desht" on bukkit forums - public List getPlayersWithin(Player player, int distance) { - List res = new ArrayList<>(); - int d2 = distance * distance; - for (Player p : Bukkit.getServer().getOnlinePlayers()) { - if (p.getWorld() == player.getWorld() && p.getLocation().distanceSquared(player.getLocation()) <= d2) { - res.add(p); - } - } - return res; - } -} diff --git a/src/main/java/ca/gibstick/discosheep/GlobalEvents.java b/src/main/java/ca/gibstick/discosheep/GlobalEvents.java deleted file mode 100644 index aca1959..0000000 --- a/src/main/java/ca/gibstick/discosheep/GlobalEvents.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * BaaBaaBlockSheep have you any wool? - * Nope, event got cancelled. - * Also listens to other events, not just sheep events - */ -package ca.gibstick.discosheep; - -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; - -public class GlobalEvents implements Listener { - - DiscoSheep plugin = DiscoSheep.getInstance(); - - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerQuitEvent(PlayerQuitEvent e) { - String name = e.getPlayer().getName(); - plugin.stopParty(name); - // stop party on player quit or else it will CONTINUE FOR ETERNITY - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerJoinEvent(PlayerJoinEvent e) { - Player player = e.getPlayer(); - plugin.partyOnJoin(player); - } -} diff --git a/src/me/cwang/discosheep/AbstractParty.java b/src/me/cwang/discosheep/AbstractParty.java new file mode 100644 index 0000000..fb53836 --- /dev/null +++ b/src/me/cwang/discosheep/AbstractParty.java @@ -0,0 +1,272 @@ +package me.cwang.discosheep; + +import org.bukkit.*; +import org.bukkit.entity.*; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; + +/** + * Created by Charlie on 2015-08-13. + */ +public abstract class AbstractParty { + public static final DyeColor[] discoColours = { + DyeColor.RED, + DyeColor.ORANGE, + DyeColor.YELLOW, + DyeColor.GREEN, + DyeColor.BLUE, + DyeColor.LIGHT_BLUE, + DyeColor.PINK, + DyeColor.MAGENTA, + DyeColor.LIME, + DyeColor.CYAN, + DyeColor.PURPLE, + DyeColor.BLACK, + DyeColor.WHITE + }; + + // Static properties + static HashMap defaultGuestNumbers = new HashMap<>(); + static int defaultDuration = 300; // ticks for entire party + static int defaultPeriod = 10; // ticks per state change + static int defaultRadius = 5; + static int defaultSheep = 10; + static float defaultSheepJump = 0.35f; + static int maxDuration = 2400; // 120 seconds + static int maxSheep = 100; + static int maxRadius = 100; + static int minPeriod = 5; // 0.25 seconds + static int maxPeriod = 40; // 2.0 seconds + static HashMap maxGuestNumbers = new HashMap<>(); + protected BukkitRunnable updater; + + public AbstractParty() { + } + + static HashMap getDefaultGuestNumbers() { + return defaultGuestNumbers; + } + + static HashMap getMaxGuestNumbers() { + return maxGuestNumbers; + } + + protected abstract Player getPlayer(); + + protected abstract void jump(Entity entity); + + public abstract int getSheep(); + + protected abstract List getSheepList(); + + protected abstract List getGuestList(); + + protected abstract HashMap getGuestMap(); + + protected abstract int getPeriod(); + + protected abstract int getState(); + + protected abstract Location getLocation(); + + protected abstract Sheep spawnSheep(); + + protected abstract boolean isExpired(); + + protected abstract Entity spawnGuest(EntityType type); + + protected final void spawnAll() { + for (int i = 0; i < getSheep(); ++i) spawnSheep(); + + for (Map.Entry entry : getGuestMap().entrySet()) { + for (int i = 0; i < entry.getValue(); ++i) spawnGuest(entry.getKey()); + } + } + + protected final void removeAll() { + for (Sheep sheep : getSheepList()) { + sheep.removeMetadata(DiscoSheep.METADATA_KEY, DiscoSheep.getInstance()); + sheep.remove(); + } + + for (LivingEntity g : getGuestList()) { + g.removeMetadata(DiscoSheep.METADATA_KEY, DiscoSheep.getInstance()); + g.remove(); + } + + getSheepList().clear(); + getGuestList().clear(); + } + + /** + * Setter for the static default settings. Takes the values from the current instance + * and sets them as the defaults for all parties. + */ + abstract void setDefaultsFromCurrent(); + + + /** + * Map an integer i to a Color enum value + * + * @param i + * @return A Color enum + */ + public final Color getColor(int i) { + Color c = null; + switch (i) { + case 1: + c = Color.AQUA; + break; + case 2: + c = Color.BLACK; + break; + case 3: + c = Color.BLUE; + break; + case 4: + c = Color.FUCHSIA; + break; + case 5: + c = Color.GRAY; + break; + case 6: + c = Color.GREEN; + break; + case 7: + c = Color.LIME; + break; + case 8: + c = Color.MAROON; + break; + case 9: + c = Color.NAVY; + break; + case 10: + c = Color.OLIVE; + break; + case 11: + c = Color.ORANGE; + break; + case 12: + c = Color.PURPLE; + break; + case 13: + c = Color.RED; + break; + case 14: + c = Color.SILVER; + break; + case 15: + c = Color.TEAL; + break; + case 16: + c = Color.WHITE; + break; + case 17: + c = Color.YELLOW; + break; + } + + return c; + } + + /** + * Advance the internal tick state of the party by 1. + */ + protected abstract void updateState(); + + /** + * Method called on all sheep to update the sheep. Could involve colour update, + * a jump, etc. + * + * @param sheep the sheep to update + */ + protected abstract void updateSheep(Sheep sheep); + + /** + * Method called on all guests for updates. Could involve jumping, teleporting, etc. + * + * @param guest The mob to update. + */ + protected abstract void updateGuest(LivingEntity guest); + + /** + * Template method that updates all guests using the above functions. + */ + protected final void updateAllGuests() { + for (Sheep sheeple : getSheepList()) { + updateSheep(sheeple); + } + + for (LivingEntity guest : getGuestList()) { + updateGuest(guest); + } + } + + /** + * Schedule an update to run after a constant period. This drives the party. + */ + protected final void scheduleUpdate() { + updater = new BukkitRunnable() { + @Override + public void run() { + updateParty(); + } + }; + updater.runTaskLater(DiscoSheep.getInstance(), getPeriod()); + } + + /** + * Register the event handler to start listening for the party's events, such as sheep damage. + */ + protected abstract void startListening(); + + /** + * Unregister the event handler + */ + protected abstract void stopListening(); + + /** + * Plays the sound for a party. + */ + protected abstract void playSounds(); + + /** + * Template method that performs one update on the entire party. + */ + protected final void updateParty() { + if (!isExpired()) { + updateAllGuests(); + playSounds(); + scheduleUpdate(); + updateState(); + } else { + stopDisco(); + } + } + + + /** + * Initialize things and start a BasicDiscoParty. + */ + public final void startDisco() { + spawnAll(); + updateParty(); + DiscoSheep.getInstance().addParty(getPlayer().getName(), this); + startListening(); + } + + /** + * Stop a BasicDiscoParty. Can be called more than once. + */ + public final void stopDisco() { + removeAll(); + if (updater != null) { + updater.cancel(); + } + updater = null; + DiscoSheep.getInstance().removeParty(getPlayer().getName()); + stopListening(); + } +} diff --git a/src/me/cwang/discosheep/BasicDiscoParty.java b/src/me/cwang/discosheep/BasicDiscoParty.java new file mode 100644 index 0000000..20651ee --- /dev/null +++ b/src/me/cwang/discosheep/BasicDiscoParty.java @@ -0,0 +1,263 @@ +package me.cwang.discosheep; + +import org.bukkit.*; +import org.bukkit.entity.*; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.meta.FireworkMeta; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.util.Vector; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Random; + + +public class BasicDiscoParty extends AbstractParty { + + protected final DiscoSheep plugin; + protected final Player player; + // Instance properties + private Random r; + private ArrayList sheepList; + private ArrayList guestList; + private HashMap guestNumbers; + private int duration; + private final int radius; + private final int period; + private final int sheep; + private final Location partyLocation; + private PartyEvents partyEvents; + private int state = 0; // basically our own tick system + private final float volumeMultiplier; + + public BasicDiscoParty(Player player, int duration, int radius, int period, int sheep, HashMap guestNumbers) { + super(); + this.plugin = DiscoSheep.getInstance(); + this.player = player; + this.duration = duration; + this.radius = radius; + this.period = period; + this.sheep = sheep; + this.guestNumbers = guestNumbers; + this.sheepList = new ArrayList(); + this.r = new Random(); + this.guestList = new ArrayList(); + this.partyLocation = player.getLocation(); + this.volumeMultiplier = Math.max(this.radius / 10, 1.0f); + } + + @Override + protected Player getPlayer() { + return player; + } + + /** + * Make an entity jump (add some y velocity) + * + * @param entity + */ + @Override + protected void jump(Entity entity) { + Vector orgVel = entity.getVelocity(); + Vector newVel = (new Vector()).copy(orgVel); + newVel.add(new Vector(0, defaultSheepJump, 0)); + entity.setVelocity(newVel); + } + + @Override + public int getSheep() { + return sheep; + } + + @Override + protected List getSheepList() { + return sheepList; + } + + @Override + protected List getGuestList() { + return guestList; + } + + @Override + protected HashMap getGuestMap() { + return guestNumbers; + } + + @Override + protected void updateGuest(LivingEntity guest) { + if (state % 2 == 0 && r.nextDouble() < 0.5) { + jump(guest); + } + } + + @Override + protected void updateSheep(Sheep sheep) { + randomizeSheepColour(sheep); + if (state % 2 == 0 && r.nextDouble() < 0.5) { + jump(sheep); + } + } + + @Override + protected void updateState() { + duration -= period; + state = state > 10000 ? 1 : state + 1; + } + + @Override + protected boolean isExpired() { + return (duration <= 0); + } + + @Override + protected int getPeriod() { + return period; + } + + @Override + protected int getState() { + return state; + } + + @Override + protected Location getLocation() { + return partyLocation; + } + + @Override + protected Sheep spawnSheep() { + World world = partyLocation.getWorld(); + Location loc = getRandomSpawnLocation(partyLocation.getBlockX(), player.getLocation().getBlockY(), partyLocation.getBlockZ(), world, radius); + Sheep newSheep = (Sheep) world.spawnEntity(loc, EntityType.SHEEP); + //newSheep.setColor(discoColours[(r.nextInt(discoColours.length))]); + newSheep.setBreed(false); // this prevents breeding - no event listener required + newSheep.teleport(loc); // teleport is needed to set orientation + getSheepList().add(newSheep); + newSheep.setRemoveWhenFarAway(false); + newSheep.setMetadata(DiscoSheep.METADATA_KEY, new FixedMetadataValue(plugin, true)); + return newSheep; + } + + @Override + protected Entity spawnGuest(EntityType type) { + World world = partyLocation.getWorld(); + Location loc = getRandomSpawnLocation(partyLocation.getBlockX(), player.getLocation().getBlockY(), partyLocation.getBlockZ(), world, radius); + LivingEntity newGuest = (LivingEntity) world.spawnEntity(loc, type); + getGuestList().add(newGuest); + newGuest.setRemoveWhenFarAway(false); + newGuest.setMetadata(DiscoSheep.METADATA_KEY, new FixedMetadataValue(plugin, true)); + newGuest.setCanPickupItems(false); + return newGuest; + } + + + private void randomizeFirework(Firework firework) { + FireworkEffect.Builder effect = FireworkEffect.builder(); + FireworkMeta meta = firework.getFireworkMeta(); + + // construct [1, 3] random colours + int numColours = r.nextInt(3) + 1; + Color[] colourArray = new Color[numColours]; + for (int i = 0; i < numColours; i++) { + colourArray[i] = getColor(r.nextInt(17) + 1); + } + + // randomize effects + effect.withColor(colourArray); + effect.flicker(r.nextDouble() < 0.5); + effect.trail(r.nextDouble() < 0.5); + effect.with(FireworkEffect.Type.values()[r.nextInt(FireworkEffect.Type.values().length)]); + + // set random effect and randomize power + meta.addEffect(effect.build()); + meta.setPower(r.nextInt(2) + 1); + + // apply it to the given firework + firework.setFireworkMeta(meta); + } + + void spawnRandomFireworkAtSheep(Sheep sheep) { + Firework firework = (Firework) sheep.getWorld().spawnEntity(sheep.getEyeLocation(), EntityType.FIREWORK); + randomizeFirework(firework); + } + + /** + * Setter for the static default settings. Takes the values from the current instance + * and sets them as the defaults for all parties. + */ + @Override + public void setDefaultsFromCurrent() { + AbstractParty.defaultDuration = this.duration; + AbstractParty.defaultPeriod = this.period; + AbstractParty.defaultRadius = this.radius; + AbstractParty.defaultSheep = this.sheep; + AbstractParty.defaultGuestNumbers = new HashMap(getGuestMap()); + } + + /** + * Return a random Location within a radius around (x, y, z) + * where y is the player's current height. + * + * @param x party center + * @param z party center + * @param world + * @param radius + * @return + */ + public Location getRandomSpawnLocation(double x, double y, double z, World world, int radius) { + Location loc; + + /* random point on circle with polar coordinates + * random number must be square rooted to obtain uniform distribution + * otherwise the sheep are biased toward the centre */ + double rand = Math.sqrt(r.nextDouble()) * radius; + double azimuth = r.nextDouble() * 2 * Math.PI; // radians + x += rand * Math.cos(azimuth); + z += rand * Math.sin(azimuth); + + loc = new Location(world, x, y, z); + loc.setPitch(r.nextFloat() * 360 - 180); + loc.setYaw(0); + + return loc; + } + + /** + * Set a random colour for a sheep + * + * @param sheep + */ + protected void randomizeSheepColour(Sheep sheep) { + sheep.setColor(discoColours[(r.nextInt(discoColours.length))]); + } + + /** + * Play sounds at the party's starting location. + * These sounds are global and will play for all players. + */ + @Override + protected void playSounds() { + partyLocation.getWorld().playSound(partyLocation, Sound.NOTE_BASS_DRUM, volumeMultiplier * 0.75f, 1.0f); + if (this.state % 2 == 0) { + partyLocation.getWorld().playSound(partyLocation, Sound.NOTE_SNARE_DRUM, volumeMultiplier * 0.8f, 1.0f); + } + + if ((this.state + 1) % 8 == 0) { + partyLocation.getWorld().playSound(partyLocation, Sound.NOTE_STICKS, volumeMultiplier * 1.0f, 1.0f); + } + } + + @Override + protected void startListening() { + // start listening + partyEvents = new PartyEvents(this); + plugin.getServer().getPluginManager().registerEvents(this.partyEvents, this.plugin); + } + + @Override + protected void stopListening() { + HandlerList.unregisterAll(this.partyEvents); + } +} diff --git a/src/me/cwang/discosheep/DiscoCommands.java b/src/me/cwang/discosheep/DiscoCommands.java new file mode 100644 index 0000000..f7407bf --- /dev/null +++ b/src/me/cwang/discosheep/DiscoCommands.java @@ -0,0 +1,137 @@ +package me.cwang.discosheep; + +import gnu.getopt.Getopt; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.Arrays; +import java.util.StringTokenizer; + +public class DiscoCommands implements CommandExecutor { + + private DiscoSheep plugin; + + public DiscoCommands() { + this.plugin = DiscoSheep.getInstance(); + } + + // return portion of the array that contains space-separated args, + // stopping at the end of the array or the next -switch + private String[] getNextArgs(String[] args, int i) { + int j = i; + while (j < args.length && !args[j].startsWith("-")) { + j++; + } + return Arrays.copyOfRange(args, i, j); + } + + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + + Player player = null; + boolean isPlayer = false; + + if (sender instanceof Player) { + player = (Player) sender; + isPlayer = true; + } // check isPlayer before "stop" and "me" commands + + // no-arg subcommands + if (args.length == 1) { + if (args[0].equalsIgnoreCase("stopall")) { + return plugin.stopAllCommand(sender); + } else if (args[0].equalsIgnoreCase("stop") && isPlayer) { + return plugin.stopMeCommand(sender); + } else if (args[0].equalsIgnoreCase("help")) { + return plugin.helpCommand(sender); + } else if (args[0].equalsIgnoreCase("reload")) { + return plugin.reloadCommand(sender); + } else if (args[0].equalsIgnoreCase("save") || args[0].equalsIgnoreCase("saveconfig")) { + return plugin.saveConfigCommand(sender); + } else if (args[0].equalsIgnoreCase("togglejoin")) { + return plugin.togglePartyOnJoinCommand(sender); + } + } + + PartyBuilder builder = new PartyBuilder(player); + // ctor takes "program name" as first arg so we pass the sub-command as that + // args then start at args[1] so we slice args[1:] + Getopt g = new Getopt(args[0], Arrays.copyOfRange(args, 1, args.length), "n:t:p:r:g:lwjP"); + + int c; + while ((c = g.getopt()) != -1) { + try { + switch (c) { + case 'n': + builder.sheep(Integer.parseInt(g.getOptarg())); + break; + case 't': + builder.duration(DiscoSheep.toTicks(Integer.parseInt(g.getOptarg()))); + break; + case 'p': + builder.period(Integer.parseInt(g.getOptarg())); + break; + case 'r': + try { + int radius = Integer.parseInt(g.getOptarg()); + builder.radius(radius); + } catch (NumberFormatException e) { + if (g.getOptarg().equals("dense")) { + builder.dense(); + } else throw e; + } + break; + case 'g': + String monsters = g.getOptarg(); + if ("none".equals(monsters)) { + builder.noGuests(); + } else if (sender.hasPermission((DiscoSheep.PERMISSION_SPAWNGUESTS))) { + StringTokenizer tokens = new StringTokenizer(monsters,","); + while(tokens.hasMoreTokens()) { + try { + String[] pair = tokens.nextToken().split(":"); + builder.guests(pair[0], Integer.parseInt(pair[1])); + } catch (Exception e) { + throw new IllegalArgumentException("Bad guest arguments"); + } + } + } + break; + case 'l': + if (sender.hasPermission(DiscoSheep.PERMISSION_LIGHTNING)) + builder.lightning(); + break; + case 'j': + builder.jeb(); + break; + case 'P': + builder.pentatonic(); + break; + } + } catch (IllegalArgumentException e) { + sender.sendMessage("Bad command: " + e.getMessage()); + } + } + + if (args.length > 0) { + if (args[0].equalsIgnoreCase("all")) { + return plugin.partyAllCommand(sender, builder); + } else if (args[0].equalsIgnoreCase("me") && isPlayer) { + return plugin.partyCommand(player, builder); + } else if (args[0].equalsIgnoreCase("other")) { + return plugin.partyOtherCommand(getNextArgs(args, 1), sender, builder); + } else if (args[0].equalsIgnoreCase("defaults")) { + return plugin.setDefaultsCommand(sender, builder); + } else { + sender.sendMessage(ChatColor.RED + "Invalid argument (certain commands do not work from console)."); + return false; + } + } + + return false; + } +} diff --git a/src/me/cwang/discosheep/DiscoDecorator.java b/src/me/cwang/discosheep/DiscoDecorator.java new file mode 100644 index 0000000..53d02c3 --- /dev/null +++ b/src/me/cwang/discosheep/DiscoDecorator.java @@ -0,0 +1,106 @@ +package me.cwang.discosheep; + +import org.bukkit.Location; +import org.bukkit.entity.*; + +import java.util.HashMap; +import java.util.List; + +/** + * Created by Charlie on 2015-08-13. + */ +public class DiscoDecorator extends AbstractParty { + protected final AbstractParty decoratedParty; + + public DiscoDecorator(AbstractParty p) { + decoratedParty = p; + } + + + @Override + protected Player getPlayer() { + return decoratedParty.getPlayer(); + } + + @Override + protected void jump(Entity entity) { + decoratedParty.jump(entity); + } + + @Override + public int getSheep() { + return decoratedParty.getSheep(); + } + + @Override + protected List getSheepList() { + return decoratedParty.getSheepList(); + } + + @Override + protected List getGuestList() { + return decoratedParty.getGuestList(); + } + + @Override + protected HashMap getGuestMap() { + return decoratedParty.getGuestMap(); + } + + @Override + protected int getPeriod() { return decoratedParty.getPeriod(); } + + @Override + protected int getState() { return decoratedParty.getState(); } + + @Override + protected Location getLocation() { + return decoratedParty.getLocation(); + } + + @Override + protected Sheep spawnSheep() { + return decoratedParty.spawnSheep(); + } + + @Override + protected boolean isExpired() { + return decoratedParty.isExpired(); + } + + @Override + protected Entity spawnGuest(EntityType type) { + return decoratedParty.spawnGuest(type); + } + + @Override + void setDefaultsFromCurrent() { + decoratedParty.setDefaultsFromCurrent(); + } + + @Override + protected void updateState() { decoratedParty.updateState(); } + + @Override + protected void updateSheep(Sheep sheep) { + decoratedParty.updateSheep(sheep); + } + + @Override + protected void updateGuest(LivingEntity guest) { + decoratedParty.updateGuest(guest); + } + + @Override + protected void startListening() { + decoratedParty.startListening(); + } + + @Override + protected void stopListening() { decoratedParty.stopListening(); } + + @Override + protected void playSounds() { + decoratedParty.playSounds(); + } +} diff --git a/src/me/cwang/discosheep/DiscoSheep.java b/src/me/cwang/discosheep/DiscoSheep.java new file mode 100644 index 0000000..c8edb2f --- /dev/null +++ b/src/me/cwang/discosheep/DiscoSheep.java @@ -0,0 +1,351 @@ +package me.cwang.discosheep; + +import java.util.*; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +public final class DiscoSheep extends JavaPlugin { + + static final String PERMISSION_PARTY = "discosheep.party.me"; + static final String PERMISSION_ALL = "discosheep.party.all"; + static final String PERMISSION_FIREWORKS = "discosheep.party.fireworks"; + static final String PERMISSION_STOPALL = "discosheep.admin.stopall"; + static final String PERMISSION_RELOAD = "discosheep.admin.reload"; + static final String PERMISSION_OTHER = "discosheep.party.other"; + static final String PERMISSION_CHANGEPERIOD = "discosheep.party.changeperiod"; + static final String PERMISSION_CHANGEDEFAULTS = "discosheep.admin.changedefaults"; + static final String PERMISSION_SAVECONFIG = "discosheep.admin.saveconfig"; + static final String PERMISSION_ONJOIN = "discosheep.party.onjoin"; + static final String PERMISSION_SPAWNGUESTS = "discosheep.party.spawnguests"; + static final String PERMISSION_TOGGLEPARTYONJOIN = "discosheep.admin.toggleonjoin"; + static final String PERMISSION_LIGHTNING = "discosheep.party.lightning"; + static final String METADATA_KEY = "discosheep123u1ohads"; + static final HashSet VALID_GUESTS = new HashSet<>(Arrays.asList( // list of valid guests + EntityType.BAT, + EntityType.BLAZE, + EntityType.CAVE_SPIDER, + EntityType.CHICKEN, + EntityType.COW, + EntityType.CREEPER, + EntityType.ENDERMAN, + EntityType.ENDERMITE, + EntityType.GHAST, + EntityType.GIANT, + EntityType.IRON_GOLEM, + EntityType.HORSE, + EntityType.MAGMA_CUBE, + EntityType.MUSHROOM_COW, + EntityType.OCELOT, + EntityType.PIG_ZOMBIE, + EntityType.RABBIT, + EntityType.SKELETON, + EntityType.SLIME, + EntityType.SPIDER, + EntityType.SQUID, + EntityType.VILLAGER, + EntityType.WOLF, + EntityType.ZOMBIE + )); + static boolean partyOnJoin = true; + Map parties = new HashMap<>(); + static DiscoSheep instance = null; + + public static DiscoSheep getInstance() { + if (instance == null) { + instance = new DiscoSheep(); + } + return instance; + } + + @Override + public void onEnable() { + DiscoSheep.instance = this; + getCommand("ds").setExecutor(new DiscoCommands()); + getServer().getPluginManager().registerEvents(new GlobalEvents(this), this); + + getConfig().addDefault("on-join.enabled", partyOnJoin); + getConfig().addDefault("max.sheep", BasicDiscoParty.maxSheep); + getConfig().addDefault("max.radius", BasicDiscoParty.maxRadius); + getConfig().addDefault("max.duration", toSeconds_i(BasicDiscoParty.maxDuration)); + getConfig().addDefault("max.period-ticks", BasicDiscoParty.maxPeriod); + getConfig().addDefault("min.period-ticks", BasicDiscoParty.minPeriod); + getConfig().addDefault("default.sheep", BasicDiscoParty.defaultSheep); + getConfig().addDefault("default.radius", BasicDiscoParty.defaultRadius); + getConfig().addDefault("default.duration", toSeconds_i(BasicDiscoParty.defaultDuration)); + getConfig().addDefault("default.period-ticks", BasicDiscoParty.defaultPeriod); + + /* + * Iterate through all live entities and create default configuration values for them + */ + for (EntityType ent : EntityType.values()) { + if (VALID_GUESTS.contains(ent)) { + getConfig().addDefault("default.guests." + ent.toString(), 0); + getConfig().addDefault("max.guests." + ent.toString(), 0); + } + } + + loadConfigFromDisk(); + } + + void loadConfigFromDisk() { + getConfig().options().copyDefaults(true); + saveConfig(); + + partyOnJoin = getConfig().getBoolean("on-join.enabled"); + BasicDiscoParty.maxSheep = getConfig().getInt("max.sheep"); + BasicDiscoParty.maxRadius = getConfig().getInt("max.radius"); + BasicDiscoParty.maxDuration = toTicks(getConfig().getInt("max.duration")); + BasicDiscoParty.maxPeriod = getConfig().getInt("max.period-ticks"); + BasicDiscoParty.minPeriod = getConfig().getInt("min.period-ticks"); + BasicDiscoParty.defaultSheep = getConfig().getInt("default.sheep"); + BasicDiscoParty.defaultRadius = getConfig().getInt("default.radius"); + BasicDiscoParty.defaultDuration = toTicks(getConfig().getInt("default.duration")); + BasicDiscoParty.defaultPeriod = getConfig().getInt("default.period-ticks"); + + + // TODO: Catch IllegalArgumentException thrown by valueOf + for (String key : getConfig().getConfigurationSection("default.guests").getKeys(false)) { + AbstractParty.getDefaultGuestNumbers().put(EntityType.valueOf(key), getConfig().getInt("default.guests." + key)); + } + + for (String key : getConfig().getConfigurationSection("max.guests").getKeys(false)) { + AbstractParty.getMaxGuestNumbers().put(EntityType.valueOf(key), getConfig().getInt("max.guests." + key)); + } + + } + + void reloadConfigFromDisk() { + reloadConfig(); + loadConfigFromDisk(); + } + + void saveConfigToDisk() { + getConfig().set("on-join.enabled", partyOnJoin); + getConfig().set("default.sheep", BasicDiscoParty.defaultSheep); + getConfig().set("default.radius", BasicDiscoParty.defaultRadius); + getConfig().set("default.duration", toSeconds_i(BasicDiscoParty.defaultDuration)); + getConfig().set("default.period-ticks", BasicDiscoParty.defaultPeriod); + + for (Map.Entry entry : BasicDiscoParty.getDefaultGuestNumbers().entrySet()) { + getConfig().set("default.guests." + entry.getKey(), entry.getValue()); + } + + saveConfig(); + } + + @Override + public void onDisable() { + this.stopAllParties(); // or else the parties will continue FOREVER + } + + static int toTicks(double seconds) { + return (int) Math.round(seconds * 20.0); + } + + static double toSeconds(int ticks) { + return (double) Math.round(ticks / 20.0); + } + + static int toSeconds_i(int ticks) { + return (int) Math.round(ticks / 20.0); + } + + public void addParty(String player, AbstractParty party) { + parties.put(player, party); + } + + public synchronized ArrayList getParties() { + return new ArrayList<>(parties.values()); + } + + public void stopParty(String name) { + if (this.hasParty(name)) { + this.getParty(name).stopDisco(); + } + } + + public void stopAllParties() { + for (AbstractParty p : getParties()) { + p.stopDisco(); + } + } + + public boolean hasParty(String name) { + return parties.containsKey(name); + } + + public AbstractParty getParty(String name) { + return parties.get(name); + } + + public void removeParty(String name) { + if (this.hasParty(name)) { + parties.remove(name); + } + } + + /*-- Actual commands begin here --*/ + boolean helpCommand(CommandSender sender) { + sender.sendMessage(ChatColor.YELLOW + + "DiscoSheep Help\n" + + ChatColor.GRAY + + " Subcommands\n" + + ChatColor.WHITE + "me, stop, all, stopall, save, reload, togglejoin\n" + + "other : start a party for the space-delimited list of players\n" + + "defaults: Change the default settings for parties (takes normal arguments)\n" + + ChatColor.GRAY + " Arguments\n" + + ChatColor.WHITE + "-n : set the number of sheep per player that spawn\n" + + "-t : set the party duration in seconds\n" + + "-p : set the number of ticks between each disco beat\n" + + "-r : set radius of the area in which sheep can spawn\n" + + "-g : set spawns for other mobs, eg. -g cow:5,pig:2\n" + + "-l: enables lightning\n" + + "-w: enables fireworks\n" + + "-j: enables alternative method for setting sheep colours\n"); + return true; + } + + boolean stopMeCommand(CommandSender sender) { + stopParty(sender.getName()); + return true; + } + + boolean stopAllCommand(CommandSender sender) { + return checkPermissionsAndRun(new PermissibleCommand() { + @Override + public boolean run(CommandSender sender) { + stopAllParties(); + return true; + } + }, sender, PERMISSION_STOPALL); + } + + boolean partyCommand(Player player, final PartyBuilder builder) { + return checkPermissionsAndRun(new PermissibleCommand() { + @Override + public boolean run(CommandSender s) { + if (!hasParty(s.getName())) { + builder.build().startDisco(); + } else { + s.sendMessage(ChatColor.RED + "You already have a party. Are you underground?"); + } + return true; + } + } , player, PERMISSION_PARTY); + } + + boolean reloadCommand(CommandSender sender) { + return checkPermissionsAndRun(new PermissibleCommand() { + @Override + public boolean run(CommandSender s) { + reloadConfigFromDisk(); + s.sendMessage(ChatColor.GREEN + "DiscoSheep config reloaded from disk"); + return true; + } + }, sender, PERMISSION_RELOAD); + } + + boolean partyOtherCommand(final String[] players, CommandSender sender, final PartyBuilder builder) { + return checkPermissionsAndRun(new PermissibleCommand() { + @Override + public boolean run(CommandSender s) { + Player p; + for (String playerName : players) { + p = Bukkit.getServer().getPlayer(playerName); + if (p != null) { + if (!hasParty(p.getName())) { + builder.buildOther(p).startDisco(); + } + } else { + s.sendMessage("Invalid player: " + playerName); + } + } + return true; + } + }, sender, PERMISSION_OTHER); + } + + boolean partyAllCommand(CommandSender sender, final PartyBuilder builder) { + return checkPermissionsAndRun(new PermissibleCommand() { + @Override + public boolean run(CommandSender s) { + for (Player p : Bukkit.getServer().getOnlinePlayers()) { + if (!hasParty(p.getName())) { + builder.buildOther(p).startDisco(); + p.sendMessage(ChatColor.RED + "LET'S DISCO!!"); + } + } + return true; + } + } , sender, PERMISSION_ALL); + } + + void partyOnJoin(Player player) { + if (!partyOnJoin) { + return; + } + if (player.hasPermission(PERMISSION_ONJOIN)) { + PartyBuilder builder = new PartyBuilder(player); + builder.build().startDisco(); + } + } + + boolean togglePartyOnJoinCommand(CommandSender sender) { + return checkPermissionsAndRun(new PermissibleCommand() { + @Override + public boolean run(CommandSender s) { + partyOnJoin = !partyOnJoin; + if (partyOnJoin) { + s.sendMessage(ChatColor.GREEN + "DiscoSheep party on join functionality enabled."); + } else { + s.sendMessage(ChatColor.GREEN + "DiscoSheep party on join functionality disabled."); + } + return true; + } + }, sender, PERMISSION_TOGGLEPARTYONJOIN); + } + + boolean setDefaultsCommand(CommandSender sender, final PartyBuilder builder) { + return checkPermissionsAndRun(new PermissibleCommand() { + @Override + public boolean run(CommandSender s) { + builder.build().setDefaultsFromCurrent(); + s.sendMessage(ChatColor.GREEN + "DiscoSheep configured with new defaults (not yet saved to disk)"); + return true; + } + }, sender, PERMISSION_CHANGEDEFAULTS); + } + + boolean saveConfigCommand(CommandSender sender) { + return checkPermissionsAndRun(new PermissibleCommand() { + @Override + public boolean run(CommandSender s) { + saveConfigToDisk(); + s.sendMessage(ChatColor.GREEN + "DiscoSheep config saved to disk"); + return true; + } + }, sender, PERMISSION_SAVECONFIG); + } + + public interface PermissibleCommand { + boolean run(CommandSender sender); + } + + public boolean checkPermissionsAndRun(PermissibleCommand command, CommandSender sender, String permission) { + if (sender.hasPermission(permission)) { + return command.run(sender); + } else { + noPermsMessage(sender, permission); + return false; + } + } + + void noPermsMessage(CommandSender sender, String permission) { + sender.sendMessage(ChatColor.RED + "You do not have the permission node " + ChatColor.GRAY + permission); + } +} diff --git a/src/me/cwang/discosheep/GlobalEvents.java b/src/me/cwang/discosheep/GlobalEvents.java new file mode 100644 index 0000000..81fbdba --- /dev/null +++ b/src/me/cwang/discosheep/GlobalEvents.java @@ -0,0 +1,35 @@ +/* + * BaaBaaBlockSheep have you any wool? + * Nope, event got cancelled. + * Also listens to other events, not just sheep events + */ +package me.cwang.discosheep; + +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class GlobalEvents implements Listener { + + DiscoSheep parent; + + public GlobalEvents(DiscoSheep parent) { + this.parent = parent; + } + + @EventHandler (priority = EventPriority.MONITOR) + public void onPlayerQuitEvent(PlayerQuitEvent e) { + String name = e.getPlayer().getName(); + parent.stopParty(name); + // stop party on player quit or else it will CONTINUE FOR ETERNITY + } + + @EventHandler (priority = EventPriority.MONITOR) + public void onPlayerJoinEvent(PlayerJoinEvent e) { + Player player = e.getPlayer(); + parent.partyOnJoin(player); + } +} diff --git a/src/me/cwang/discosheep/JebParty.java b/src/me/cwang/discosheep/JebParty.java new file mode 100644 index 0000000..ad85b53 --- /dev/null +++ b/src/me/cwang/discosheep/JebParty.java @@ -0,0 +1,26 @@ +package me.cwang.discosheep; + +import org.bukkit.entity.Sheep; + +/** + * Created by Charlie on 2015-08-16. + * This DiscoParty toggles sheep colours by setting names to jeb_ + */ +public class JebParty extends DiscoDecorator { + public JebParty(AbstractParty p) { + super(p); + } + + /** + * Spawns a sheep and sets its name to "jeb_", using the easter egg to change colours + * This feature has been around for long enough so it should be safe to use. + * @return The sheep that was spawned + */ + @Override + protected Sheep spawnSheep() { + Sheep sheep = super.spawnSheep(); + sheep.setCustomName("jeb_"); + sheep.setCustomNameVisible(false); + return sheep; + } +} diff --git a/src/me/cwang/discosheep/LightningParty.java b/src/me/cwang/discosheep/LightningParty.java new file mode 100644 index 0000000..db28703 --- /dev/null +++ b/src/me/cwang/discosheep/LightningParty.java @@ -0,0 +1,61 @@ +package me.cwang.discosheep; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Sheep; + +import java.util.Random; + +/** + * Created by Charlie on 2015-08-13. + */ +public class LightningParty extends DiscoDecorator { + private int count; // maximum number of lightning strikes + + public LightningParty(AbstractParty p) { + super(p); + count = super.getSheep() / 4; + } + + /** + * Strike a certain percentage of the spawned sheep with lightning effects. + * The lightning effects should be purely visual, ie. not set anything on fire. + * @return The sheep that was spawned. + */ + @Override + protected Sheep spawnSheep() { + Sheep sheep = super.spawnSheep(); + if (count > 0) { + --count; + sheep.getWorld().strikeLightningEffect(sheep.getLocation()); + } + return sheep; + } + + /** + * Strike all the non-sheep guests with lightning effects. + * The lightning effects hsould be purely visual ie. not set anything on fire. + * @param type The type of entity to spawn. + * @return The spawned entity. + */ + @Override + protected Entity spawnGuest(EntityType type) { + Entity guest = super.spawnGuest(type); + guest.getWorld().strikeLightningEffect(guest.getLocation()); + return guest; + } + + /** + * Strike sheep with lightning 0.05% of the time. + */ + @Override + protected void updateSheep(Sheep sheep) { + super.updateSheep(sheep); + Random r = new Random(); + if (r.nextDouble() < 0.005) { + sheep.getWorld().strikeLightningEffect(sheep.getLocation()); + } + } + +} + diff --git a/src/me/cwang/discosheep/PartyBuilder.java b/src/me/cwang/discosheep/PartyBuilder.java new file mode 100644 index 0000000..f21cefe --- /dev/null +++ b/src/me/cwang/discosheep/PartyBuilder.java @@ -0,0 +1,133 @@ +package me.cwang.discosheep; + +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +import java.util.HashMap; + +/** + * Created by Charlie on 2015-08-15. + */ +public class PartyBuilder { + // required + protected Player player; + // optional + private int duration = AbstractParty.defaultDuration; + private int radius = AbstractParty.defaultRadius; + private int period = AbstractParty.defaultPeriod; + private int sheep = AbstractParty.defaultSheep; + private HashMap guests = AbstractParty.defaultGuestNumbers; + private boolean dense = false; + private boolean fireworks = false; + private boolean lightning = false; + private boolean jeb = false; + private boolean pentatonic = false; + + public PartyBuilder(Player player) { + this.player = player; + } + + public PartyBuilder duration(int duration) throws IllegalArgumentException { + if (duration < 0 || duration > AbstractParty.maxDuration) { + throw new IllegalArgumentException("Invalid duration."); + } + this.duration = duration; + return this; + } + + public PartyBuilder radius(int radius) throws IllegalArgumentException { + if (radius < 0 || radius > AbstractParty.maxRadius) { + throw new IllegalArgumentException("Invalid radius."); + } + this.radius = radius; + return this; + } + + public PartyBuilder dense() { + this.dense = true; + return this; + } + + public PartyBuilder period(int period) throws IllegalArgumentException { + if (period < 0 || period > AbstractParty.maxPeriod) { + throw new IllegalArgumentException("Invalid period."); + } + this.period = period; + return this; + } + + public PartyBuilder sheep(int sheep) throws IllegalArgumentException { + if (sheep < 0 || sheep > AbstractParty.maxSheep) { + throw new IllegalArgumentException("Invalid sheep number"); + } + this.sheep = sheep; + return this; + } + + public PartyBuilder guests(String key, int n) throws IllegalArgumentException { + key = key.toUpperCase(); + EntityType type = EntityType.UNKNOWN; + try { + type = EntityType.valueOf(key); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException(String.format("Invalid guest: %s", key)); + } + if (guests.containsKey(type)) { + if (n <= AbstractParty.getMaxGuestNumbers().get(type) && n >= 0) { // so that /ds defaults can take 0 as arg + guests.put(type, n); + return this; + } + } + throw new IllegalArgumentException(String.format("Invalid number for %s", key)); + } + + public PartyBuilder noGuests() { + guests.clear(); + return this; + } + + public PartyBuilder jeb() { + jeb = true; + return this; + } + + public PartyBuilder lightning() { + lightning = true; + return this; + } + + public AbstractParty build() { + if (dense) { + int denseRadius = (int) Math.floor(Math.sqrt(sheep / Math.PI)); + if (denseRadius > AbstractParty.maxRadius) { + denseRadius = AbstractParty.maxRadius; + } + if (denseRadius < 1) { + denseRadius = 1; + } + radius = denseRadius; + } + AbstractParty party = new BasicDiscoParty(player, duration, radius, period, sheep, guests); + if (fireworks) { + // do stuff + } + + if (lightning) party = new LightningParty(party); + if (jeb) party = new JebParty(party); + if (pentatonic) party = new PentatonicParty(party); + + return party; + } + + public AbstractParty buildOther(Player newPlayer) { + Player oldPlayer = player; + player = newPlayer; + AbstractParty otherParty = build(); + player = oldPlayer; + return otherParty; + } + + public void pentatonic() { + pentatonic = true; + } +} diff --git a/src/main/java/ca/gibstick/discosheep/PartyEvents.java b/src/me/cwang/discosheep/PartyEvents.java similarity index 54% rename from src/main/java/ca/gibstick/discosheep/PartyEvents.java rename to src/me/cwang/discosheep/PartyEvents.java index ac807c4..ad5c34b 100644 --- a/src/main/java/ca/gibstick/discosheep/PartyEvents.java +++ b/src/me/cwang/discosheep/PartyEvents.java @@ -1,12 +1,10 @@ -package ca.gibstick.discosheep; +package me.cwang.discosheep; -import org.bukkit.Instrument; -import org.bukkit.Sound; +import org.bukkit.entity.Entity; import org.bukkit.entity.Sheep; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityPortalEvent; import org.bukkit.event.entity.EntityTargetEvent; @@ -14,13 +12,12 @@ import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerShearEntityEvent; /** - * * @author Charlie */ public class PartyEvents implements Listener { DiscoSheep plugin = DiscoSheep.getInstance(); - DiscoParty party; + BasicDiscoParty party; /* * There will be multiple instances of PartyEvents, * and each instance will only listen for its own party. @@ -29,64 +26,77 @@ public class PartyEvents implements Listener { * unregister the listeners when no parties are running. */ - public PartyEvents(DiscoParty party) { + public PartyEvents(BasicDiscoParty party) { this.party = party; } - // prevent sheep shearing + /** + * Predicate that returns true if the Entity e is from the given DiscoParty. + * + * @param e The entity to check + * @return True if the Entity is in any DiscoParty + */ + private boolean isFromParty(Entity e) { + return e.hasMetadata(DiscoSheep.METADATA_KEY); + } + + /** + * Handler that prevents sheep shearing + * + * @param e The event + */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onPlayerShear(PlayerShearEntityEvent e) { - if (e.getEntity() instanceof Sheep) { - if (party.getSheepSet().contains((Sheep) e.getEntity())) { - e.setCancelled(true); - } + if (e.getEntity() instanceof Sheep && isFromParty(e.getEntity())) { + e.setCancelled(true); } } - // actually make sheep and other guests invincible + /** + * Make all party mobs invincible by cancelling the damage event. + * Mobs will instead jump when taking damage. + * @param e The damage event + */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onLivingEntityDamageEvent(EntityDamageEvent e) { - if (e.getEntity() instanceof Sheep) { - if (party.getSheepSet().contains((Sheep) e.getEntity())) { - party.jump(e.getEntity()); // for kicks - e.setCancelled(true); - } - } - if (party.getGuestSet().contains(e.getEntity())) { - party.jump(e.getEntity()); + if (isFromParty(e.getEntity())) { + party.jump(e.getEntity()); // for kicks e.setCancelled(true); } } - // prevent uninvited guests from targetting players + /** + * Pacify monsters that are part of parties by preventing them from targeting players. + * @param e The target event. + */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onEntityTargetLivingEntityEvent(EntityTargetEvent e) { - if (party.getGuestSet().contains(e.getEntity())) { + if (isFromParty(e.getEntity())) { e.setCancelled(true); } } - // prevent egg breeding + /** + * Prevent egg breeding and other shenanigans from party members by disabling all + * right click events. + * @param e The right click event + */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onPlayerInteractEntityEvent(PlayerInteractEntityEvent e) { - if (party.getSheepSet().contains(e.getRightClicked()) || party.getGuestSet().contains(e.getRightClicked())) { + if (isFromParty(e.getRightClicked())) { e.setCancelled(true); - e.getPlayer().playSound(e.getRightClicked().getLocation(), Sound.NOTE_BASS_GUITAR, 1.0f, party.getPentatonicNote()); } } - // prevent portal teleport + /** + * Prevent party members from going through portals (end, nether). + * @param e The portal event. + */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onEntityPortalEvent(EntityPortalEvent e) { - if (party.getSheepSet().contains(e.getEntity()) || party.getGuestSet().contains(e.getEntity())) { - e.setCancelled(true); - } - } - // prevent disco floor breaking - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onBlockBreakEvent(BlockBreakEvent e) { - if (party.getFloorBlocks().contains(e.getBlock())) { + if (isFromParty(e.getEntity())) { e.setCancelled(true); } } + } diff --git a/src/me/cwang/discosheep/PartyGuests.java b/src/me/cwang/discosheep/PartyGuests.java new file mode 100644 index 0000000..382f305 --- /dev/null +++ b/src/me/cwang/discosheep/PartyGuests.java @@ -0,0 +1,19 @@ +package me.cwang.discosheep; + +import org.bukkit.entity.Creature; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Sheep; + +import java.util.List; +import java.util.Map; + +/** + * A container class for storing and updating + * occupants of a single party. + * One AbstractParty will have one PartyGuests + */ +public abstract class PartyGuests { + + +} diff --git a/src/me/cwang/discosheep/PentatonicParty.java b/src/me/cwang/discosheep/PentatonicParty.java new file mode 100644 index 0000000..e2ecb4c --- /dev/null +++ b/src/me/cwang/discosheep/PentatonicParty.java @@ -0,0 +1,36 @@ +package me.cwang.discosheep; + +import org.bukkit.Sound; + +import java.util.Random; + +/** + * Created by Charlie on 2015-08-17. + */ +public class PentatonicParty extends DiscoDecorator { + + Random r = new Random(); + + private static final float[] pentatonicNotes = { + 1.0f, + 1.125f, + 1.25f, + 1.5f, + 1.667f, + 2.0f + }; + + public PentatonicParty(AbstractParty p) { + super(p); + } + + float getPentatonicNote() { + return pentatonicNotes[r.nextInt(pentatonicNotes.length)]; + } + + @Override + protected void playSounds() { + super.playSounds(); + getLocation().getWorld().playSound(getLocation(), Sound.NOTE_PIANO, 1.0f, getPentatonicNote()); + } +} diff --git a/src/main/resources/plugin.yml b/src/plugin.yml similarity index 92% rename from src/main/resources/plugin.yml rename to src/plugin.yml index 7d080fb..124a17e 100644 --- a/src/main/resources/plugin.yml +++ b/src/plugin.yml @@ -1,14 +1,14 @@ name: DiscoSheep -main: ca.gibstick.discosheep.DiscoSheep +main: DiscoSheep authors: [Gibstick, RangerMauve] version: 1.2 commands: - #ds: - #description: "Main DiscoSheep command" - #usage: | - # [arguments] - #Use /ds help for more information - #To stop your party, use /ds stop. + ds: + description: "Main DiscoSheep command" + usage: | + [arguments] + Use /ds help for more information + To stop your party, use /ds stop. permissions: # If default is set to false, console will not have permission! discosheep.*: