From 131e7c9a90d7a19a43044c4bedcfe88474115634 Mon Sep 17 00:00:00 2001 From: Charlie Wang Date: Mon, 17 Aug 2015 12:48:07 -0400 Subject: [PATCH 1/8] refactor, pentatonic, jeb, getopt Add a PentatonicParty, which is a party that plays pentatonic notes as music. Broke API by changing format of -g flag. Use a Java port of GNU Getopt for command parsing. Overhaul guests in code to be whitelist instead of blacklist; current guest list works without issue. Remove broken disco floor code. --- .../ca/gibstick/discosheep/DiscoCommands.java | 315 ---------- .../ca/gibstick/discosheep/DiscoParty.java | 554 ------------------ .../ca/gibstick/discosheep/DiscoSheep.java | 247 -------- .../ca/gibstick/discosheep/GlobalEvents.java | 31 - src/me/cwang/discosheep/AbstractParty.java | 272 +++++++++ src/me/cwang/discosheep/BasicDiscoParty.java | 263 +++++++++ src/me/cwang/discosheep/DiscoCommands.java | 137 +++++ src/me/cwang/discosheep/DiscoDecorator.java | 106 ++++ src/me/cwang/discosheep/DiscoSheep.java | 351 +++++++++++ src/me/cwang/discosheep/GlobalEvents.java | 35 ++ src/me/cwang/discosheep/JebParty.java | 26 + src/me/cwang/discosheep/LightningParty.java | 61 ++ src/me/cwang/discosheep/PartyBuilder.java | 133 +++++ .../cwang}/discosheep/PartyEvents.java | 80 +-- src/me/cwang/discosheep/PartyGuests.java | 19 + src/me/cwang/discosheep/PentatonicParty.java | 36 ++ src/{main/resources => }/plugin.yml | 14 +- 17 files changed, 1491 insertions(+), 1189 deletions(-) delete mode 100644 src/main/java/ca/gibstick/discosheep/DiscoCommands.java delete mode 100644 src/main/java/ca/gibstick/discosheep/DiscoParty.java delete mode 100644 src/main/java/ca/gibstick/discosheep/DiscoSheep.java delete mode 100644 src/main/java/ca/gibstick/discosheep/GlobalEvents.java create mode 100644 src/me/cwang/discosheep/AbstractParty.java create mode 100644 src/me/cwang/discosheep/BasicDiscoParty.java create mode 100644 src/me/cwang/discosheep/DiscoCommands.java create mode 100644 src/me/cwang/discosheep/DiscoDecorator.java create mode 100644 src/me/cwang/discosheep/DiscoSheep.java create mode 100644 src/me/cwang/discosheep/GlobalEvents.java create mode 100644 src/me/cwang/discosheep/JebParty.java create mode 100644 src/me/cwang/discosheep/LightningParty.java create mode 100644 src/me/cwang/discosheep/PartyBuilder.java rename src/{main/java/ca/gibstick => me/cwang}/discosheep/PartyEvents.java (54%) create mode 100644 src/me/cwang/discosheep/PartyGuests.java create mode 100644 src/me/cwang/discosheep/PentatonicParty.java rename src/{main/resources => }/plugin.yml (92%) 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.*: From 5daee8b5f183e1da0b447a9b763e6b7d87027e48 Mon Sep 17 00:00:00 2001 From: Charlie Wang Date: Mon, 17 Aug 2015 13:22:29 -0400 Subject: [PATCH 2/8] fix plugin.yml main, remove netbeans cruft --- .gitignore | 7 ++- manifest.mf | 3 -- nb-configuration.xml | 19 -------- pom.xml | 105 ------------------------------------------- src/plugin.yml | 4 +- 5 files changed, 5 insertions(+), 133 deletions(-) delete mode 100644 manifest.mf delete mode 100644 nb-configuration.xml delete mode 100644 pom.xml diff --git a/.gitignore b/.gitignore index 5f886e6..3fd2336 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ -/target/ *.jar -/build/ -/dist/ -/server/ \ No newline at end of file +/out/ +.idea +*.iml diff --git a/manifest.mf b/manifest.mf deleted file mode 100644 index 328e8e5..0000000 --- a/manifest.mf +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -X-COMMENT: Main-Class will be added automatically by build - diff --git a/nb-configuration.xml b/nb-configuration.xml deleted file mode 100644 index c5bbdab..0000000 --- a/nb-configuration.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - JDK_1.7 - true - - diff --git a/pom.xml b/pom.xml deleted file mode 100644 index a6c2b59..0000000 --- a/pom.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - 4.0.0 - ca.gibstick - DiscoSheep - 1.1.2 - jar - - - repo.oc.tc - http://repo.oc.tc/content/repositories/public/ - - - spigot-repo - https://hub.spigotmc.org/nexus/content/groups/public/ - - - - - com.sk89q - command-framework-bukkit - 0.5-SNAPSHOT - - - org.bukkit - bukkit - 1.8-R0.1-SNAPSHOT - - - - DiscoSheep - src/main/java - - - . - true - src/main/resources/ - - plugin.yml - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.1 - - - -Xlint:deprecation - - true - - - - org.apache.maven.plugins - maven-source-plugin - 2.3 - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - - org.apache.maven.plugins - maven-deploy-plugin - 2.8.1 - - - org.apache.maven.plugins - maven-shade-plugin - 2.3 - - - package - - shade - - - DiscoSheep - true - - ${project.basedir} - - - com.sk89q:command-framework-bukkit - - - false - true - shaded - - - - - - - - UTF-8 - 1.7 - 1.7 - - DiscoSheep - \ No newline at end of file diff --git a/src/plugin.yml b/src/plugin.yml index 124a17e..04f9692 100644 --- a/src/plugin.yml +++ b/src/plugin.yml @@ -1,7 +1,7 @@ name: DiscoSheep -main: DiscoSheep +main: me.cwang.discosheep.DiscoSheep authors: [Gibstick, RangerMauve] -version: 1.2 +version: 2.0 commands: ds: description: "Main DiscoSheep command" From 5abaffeee753d01099dcf7855f9c6ab99b90f93f Mon Sep 17 00:00:00 2001 From: Charlie Wang Date: Mon, 17 Aug 2015 13:24:02 -0400 Subject: [PATCH 3/8] add BabyParty, update helpCommand --- src/me/cwang/discosheep/BabyParty.java | 45 ++++++++++++++++++++++ src/me/cwang/discosheep/DiscoCommands.java | 4 +- src/me/cwang/discosheep/PartyBuilder.java | 14 +++++-- 3 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 src/me/cwang/discosheep/BabyParty.java diff --git a/src/me/cwang/discosheep/BabyParty.java b/src/me/cwang/discosheep/BabyParty.java new file mode 100644 index 0000000..88c8b80 --- /dev/null +++ b/src/me/cwang/discosheep/BabyParty.java @@ -0,0 +1,45 @@ +package me.cwang.discosheep; + +import org.bukkit.entity.Ageable; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Sheep; + +/** + * Created by Charlie on 2015-08-17. + */ +public class BabyParty extends DiscoDecorator { + private int guestBabyCount; + private int sheepBabyCount; + + public BabyParty(AbstractParty p, int babyness) { + super(p); + sheepBabyCount = (int) ((babyness / 100.0d) * getSheep()); + int totalGuests = 0; + for (int i : getGuestMap().values()) { + totalGuests += i; + } + guestBabyCount = (int) ((babyness / 100.0d) * totalGuests); + } + + @Override + protected Entity spawnGuest(EntityType type) { + Entity guest = super.spawnGuest(type); + if (guest instanceof Ageable && guestBabyCount > 0) { + Ageable baby = (Ageable) guest; + baby.setBaby(); + --guestBabyCount; + } + return guest; + } + + @Override + protected Sheep spawnSheep() { + Sheep sheep = super.spawnSheep(); + if (sheepBabyCount > 0) { + sheep.setBaby(); + --sheepBabyCount; + } + return sheep; + } +} diff --git a/src/me/cwang/discosheep/DiscoCommands.java b/src/me/cwang/discosheep/DiscoCommands.java index f7407bf..994ac64 100644 --- a/src/me/cwang/discosheep/DiscoCommands.java +++ b/src/me/cwang/discosheep/DiscoCommands.java @@ -60,7 +60,7 @@ public class DiscoCommands implements CommandExecutor { 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"); + Getopt g = new Getopt(args[0], Arrays.copyOfRange(args, 1, args.length), "n:t:p:r:g:lwjPb:"); int c; while ((c = g.getopt()) != -1) { @@ -111,6 +111,8 @@ public class DiscoCommands implements CommandExecutor { case 'P': builder.pentatonic(); break; + case 'b': + builder.baby(Integer.parseInt(g.getOptarg())); } } catch (IllegalArgumentException e) { sender.sendMessage("Bad command: " + e.getMessage()); diff --git a/src/me/cwang/discosheep/PartyBuilder.java b/src/me/cwang/discosheep/PartyBuilder.java index f21cefe..a004ff7 100644 --- a/src/me/cwang/discosheep/PartyBuilder.java +++ b/src/me/cwang/discosheep/PartyBuilder.java @@ -22,6 +22,7 @@ public class PartyBuilder { private boolean lightning = false; private boolean jeb = false; private boolean pentatonic = false; + private int babyness = 0; public PartyBuilder(Player player) { this.player = player; @@ -115,10 +116,19 @@ public class PartyBuilder { if (lightning) party = new LightningParty(party); if (jeb) party = new JebParty(party); if (pentatonic) party = new PentatonicParty(party); + if (babyness > 0) party = new BabyParty(party, babyness); return party; } + public void pentatonic() { + pentatonic = true; + } + + public void baby(int babyness) { + this.babyness = babyness; + } + public AbstractParty buildOther(Player newPlayer) { Player oldPlayer = player; player = newPlayer; @@ -127,7 +137,5 @@ public class PartyBuilder { return otherParty; } - public void pentatonic() { - pentatonic = true; - } + } From 2bcbbe187881f5a533cadc5d6b447b8a06558017 Mon Sep 17 00:00:00 2001 From: Charlie Wang Date: Mon, 17 Aug 2015 13:32:39 -0400 Subject: [PATCH 4/8] add fireworks, fix pentatonicParty volume --- src/me/cwang/discosheep/AbstractParty.java | 2 + src/me/cwang/discosheep/BasicDiscoParty.java | 37 ++---------- src/me/cwang/discosheep/DiscoCommands.java | 4 ++ src/me/cwang/discosheep/DiscoDecorator.java | 3 + src/me/cwang/discosheep/DiscoSheep.java | 4 +- src/me/cwang/discosheep/FireworkParty.java | 61 ++++++++++++++++++++ src/me/cwang/discosheep/PartyBuilder.java | 10 ++-- src/me/cwang/discosheep/PentatonicParty.java | 2 +- 8 files changed, 85 insertions(+), 38 deletions(-) create mode 100644 src/me/cwang/discosheep/FireworkParty.java diff --git a/src/me/cwang/discosheep/AbstractParty.java b/src/me/cwang/discosheep/AbstractParty.java index fb53836..05acf41 100644 --- a/src/me/cwang/discosheep/AbstractParty.java +++ b/src/me/cwang/discosheep/AbstractParty.java @@ -68,6 +68,8 @@ public abstract class AbstractParty { protected abstract int getState(); + protected abstract float getVolumeMultiplier(); + protected abstract Location getLocation(); protected abstract Sheep spawnSheep(); diff --git a/src/me/cwang/discosheep/BasicDiscoParty.java b/src/me/cwang/discosheep/BasicDiscoParty.java index 20651ee..fa3097f 100644 --- a/src/me/cwang/discosheep/BasicDiscoParty.java +++ b/src/me/cwang/discosheep/BasicDiscoParty.java @@ -3,7 +3,6 @@ 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; @@ -121,6 +120,11 @@ public class BasicDiscoParty extends AbstractParty { return state; } + @Override + protected float getVolumeMultiplier() { + return volumeMultiplier; + } + @Override protected Location getLocation() { return partyLocation; @@ -152,37 +156,6 @@ public class BasicDiscoParty extends AbstractParty { 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. diff --git a/src/me/cwang/discosheep/DiscoCommands.java b/src/me/cwang/discosheep/DiscoCommands.java index 994ac64..59f5afc 100644 --- a/src/me/cwang/discosheep/DiscoCommands.java +++ b/src/me/cwang/discosheep/DiscoCommands.java @@ -101,6 +101,9 @@ public class DiscoCommands implements CommandExecutor { } } break; + case 'w': + builder.fireworks(); + break; case 'l': if (sender.hasPermission(DiscoSheep.PERMISSION_LIGHTNING)) builder.lightning(); @@ -113,6 +116,7 @@ public class DiscoCommands implements CommandExecutor { break; case 'b': builder.baby(Integer.parseInt(g.getOptarg())); + break; } } catch (IllegalArgumentException e) { sender.sendMessage("Bad command: " + e.getMessage()); diff --git a/src/me/cwang/discosheep/DiscoDecorator.java b/src/me/cwang/discosheep/DiscoDecorator.java index 53d02c3..dc0edfb 100644 --- a/src/me/cwang/discosheep/DiscoDecorator.java +++ b/src/me/cwang/discosheep/DiscoDecorator.java @@ -53,6 +53,9 @@ public class DiscoDecorator extends AbstractParty { @Override protected int getState() { return decoratedParty.getState(); } + @Override + protected float getVolumeMultiplier() { return decoratedParty.getVolumeMultiplier(); } + @Override protected Location getLocation() { return decoratedParty.getLocation(); diff --git a/src/me/cwang/discosheep/DiscoSheep.java b/src/me/cwang/discosheep/DiscoSheep.java index c8edb2f..32ca98b 100644 --- a/src/me/cwang/discosheep/DiscoSheep.java +++ b/src/me/cwang/discosheep/DiscoSheep.java @@ -206,7 +206,9 @@ public final class DiscoSheep extends JavaPlugin { + "-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"); + + "-j: enables alternative method for setting sheep colours\n" + + "-b : spawns a percentage of mobs as babies, if possible\n" + + "-P: enables pentatonic backing track\n"); return true; } diff --git a/src/me/cwang/discosheep/FireworkParty.java b/src/me/cwang/discosheep/FireworkParty.java new file mode 100644 index 0000000..db09cd4 --- /dev/null +++ b/src/me/cwang/discosheep/FireworkParty.java @@ -0,0 +1,61 @@ +package me.cwang.discosheep; + +import org.bukkit.Color; +import org.bukkit.FireworkEffect; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Firework; +import org.bukkit.entity.Sheep; +import org.bukkit.inventory.meta.FireworkMeta; + +import java.util.Random; + +/** + * Created by Charlie on 2015-08-17. + */ +public class FireworkParty extends DiscoDecorator { + Random r; + + public FireworkParty(AbstractParty p) { + super(p); + r = new Random(); + } + + + 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); + } + + private void spawnRandomFireworkAtSheep(Sheep sheep) { + Firework firework = (Firework) sheep.getWorld().spawnEntity(sheep.getEyeLocation(), EntityType.FIREWORK); + randomizeFirework(firework); + } + + @Override + protected void updateSheep(Sheep sheep) { + super.updateSheep(sheep); + if (getState() % 8 == 0 && r.nextDouble() < 0.5) { + spawnRandomFireworkAtSheep(sheep); + } + } +} diff --git a/src/me/cwang/discosheep/PartyBuilder.java b/src/me/cwang/discosheep/PartyBuilder.java index a004ff7..5613237 100644 --- a/src/me/cwang/discosheep/PartyBuilder.java +++ b/src/me/cwang/discosheep/PartyBuilder.java @@ -87,6 +87,11 @@ public class PartyBuilder { return this; } + public PartyBuilder fireworks() { + fireworks = true; + return this; + } + public PartyBuilder jeb() { jeb = true; return this; @@ -109,10 +114,7 @@ public class PartyBuilder { radius = denseRadius; } AbstractParty party = new BasicDiscoParty(player, duration, radius, period, sheep, guests); - if (fireworks) { - // do stuff - } - + if (fireworks) party = new FireworkParty(party); if (lightning) party = new LightningParty(party); if (jeb) party = new JebParty(party); if (pentatonic) party = new PentatonicParty(party); diff --git a/src/me/cwang/discosheep/PentatonicParty.java b/src/me/cwang/discosheep/PentatonicParty.java index e2ecb4c..d8ffc16 100644 --- a/src/me/cwang/discosheep/PentatonicParty.java +++ b/src/me/cwang/discosheep/PentatonicParty.java @@ -31,6 +31,6 @@ public class PentatonicParty extends DiscoDecorator { @Override protected void playSounds() { super.playSounds(); - getLocation().getWorld().playSound(getLocation(), Sound.NOTE_PIANO, 1.0f, getPentatonicNote()); + getLocation().getWorld().playSound(getLocation(), Sound.NOTE_PIANO, getVolumeMultiplier() * 1.0f, getPentatonicNote()); } } From b50ebf35e7876fdeee3c737f9515cc6154f2090d Mon Sep 17 00:00:00 2001 From: Charlie Wang Date: Mon, 17 Aug 2015 14:55:34 -0400 Subject: [PATCH 5/8] Update README.md --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9bd0c59..c45aee9 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,17 @@ DiscoSheep ========== -A plugin for [Bukkit](http://bukkit.org/)/Spigot, the Minecraft server plugin API. This is a from-scratch remake of the fabulous [DiscoSheep plugin](http://forums.bukkit.org/threads/inactive-fun-discosheep-v0-3-uninvited-guesssts-permissions-and-source-code-522.7106/) which spawns a dance party of sheep at your whim. +A plugin for [Bukkit](http://bukkit.org/)/[Spigot](https://www.spigotmc.org/), the Minecraft server plugin API. This is a from-scratch remake of the fabulous [DiscoSheep plugin](http://forums.bukkit.org/threads/inactive-fun-discosheep-v0-3-uninvited-guesssts-permissions-and-source-code-522.7106/) which spawns a dance party of sheep at your whim. [Demo](https://www.youtube.com/watch?v=Ls4L9Cjp-As) -###A Note on UUIDs### -DiscoSheep doesn't store anything after a party, and disco-party-on-join is handled by a permissions plugin. Thus, we don't need to worry about the migration to UUIDs. - ###A Note on Performance### DiscoSheep can be a fairly resource intensive plugin, but only when it is running. The events are unregistered when there are no parties. To prevent server slowdowns, set sensible limits in your config.yml, though the defaults are already fairly conservative. ###[Documentation](https://github.com/Gibstick/DiscoSheep/wiki)### ###Versions### -Tested up to CraftBukkit build #3092 (MC: 1.7.9); can be built with Java 7. -Also works as-is with Spigot for 1.8. +Last tested on CraftBukkit version git-Bukkit-53fac9f (MC: 1.8.8) (Implementing API version 1.8.8-R0.1-SNAPSHOT) AKA Spigot for 1.8.8. ###[~~BukkitDev~~](http://dev.bukkit.org/bukkit-plugins/superdiscosheep/)### No longer updated. From eee74991c46a438a7787bbf7efb18fd27f30b79d Mon Sep 17 00:00:00 2001 From: Charlie Wang Date: Mon, 17 Aug 2015 15:02:26 -0400 Subject: [PATCH 6/8] change fireworks flag from -w to -f --- src/me/cwang/discosheep/DiscoCommands.java | 4 ++-- src/me/cwang/discosheep/DiscoSheep.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/me/cwang/discosheep/DiscoCommands.java b/src/me/cwang/discosheep/DiscoCommands.java index 59f5afc..2517c20 100644 --- a/src/me/cwang/discosheep/DiscoCommands.java +++ b/src/me/cwang/discosheep/DiscoCommands.java @@ -60,7 +60,7 @@ public class DiscoCommands implements CommandExecutor { 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:lwjPb:"); + Getopt g = new Getopt(args[0], Arrays.copyOfRange(args, 1, args.length), "n:t:p:r:g:lfjPb:"); int c; while ((c = g.getopt()) != -1) { @@ -101,7 +101,7 @@ public class DiscoCommands implements CommandExecutor { } } break; - case 'w': + case 'f': builder.fireworks(); break; case 'l': diff --git a/src/me/cwang/discosheep/DiscoSheep.java b/src/me/cwang/discosheep/DiscoSheep.java index 32ca98b..abafb0d 100644 --- a/src/me/cwang/discosheep/DiscoSheep.java +++ b/src/me/cwang/discosheep/DiscoSheep.java @@ -205,7 +205,7 @@ public final class DiscoSheep extends JavaPlugin { + "-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" + + "-f: enables fireworks\n" + "-j: enables alternative method for setting sheep colours\n" + "-b : spawns a percentage of mobs as babies, if possible\n" + "-P: enables pentatonic backing track\n"); From 78cba2b29af157bb8357c426e5e573593ab30aaf Mon Sep 17 00:00:00 2001 From: Charlie Wang Date: Wed, 19 Aug 2015 12:01:30 -0400 Subject: [PATCH 7/8] increase volume of all parties --- src/me/cwang/discosheep/BasicDiscoParty.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/me/cwang/discosheep/BasicDiscoParty.java b/src/me/cwang/discosheep/BasicDiscoParty.java index fa3097f..82c1940 100644 --- a/src/me/cwang/discosheep/BasicDiscoParty.java +++ b/src/me/cwang/discosheep/BasicDiscoParty.java @@ -43,7 +43,7 @@ public class BasicDiscoParty extends AbstractParty { this.r = new Random(); this.guestList = new ArrayList(); this.partyLocation = player.getLocation(); - this.volumeMultiplier = Math.max(this.radius / 10, 1.0f); + this.volumeMultiplier = 2.0f * Math.max(this.radius / 10, 1.0f); } @Override From e8b7c3b6cffc3563da4d6eec710c5cc8440ba267 Mon Sep 17 00:00:00 2001 From: Charlie Wang Date: Wed, 19 Aug 2015 12:02:27 -0400 Subject: [PATCH 8/8] consolidate error handling for commands --- src/me/cwang/discosheep/DiscoCommands.java | 21 +++++++++++++++++---- src/me/cwang/discosheep/DiscoSheep.java | 10 +++------- src/me/cwang/discosheep/PartyBuilder.java | 12 ++++++------ 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/me/cwang/discosheep/DiscoCommands.java b/src/me/cwang/discosheep/DiscoCommands.java index 2517c20..6129e4c 100644 --- a/src/me/cwang/discosheep/DiscoCommands.java +++ b/src/me/cwang/discosheep/DiscoCommands.java @@ -57,11 +57,15 @@ public class DiscoCommands implements CommandExecutor { } } + if (args.length == 0) { + return plugin.helpCommand(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:lfjPb:"); - + g.setOpterr(false); // do own error handling int c; while ((c = g.getopt()) != -1) { try { @@ -95,8 +99,8 @@ public class DiscoCommands implements CommandExecutor { try { String[] pair = tokens.nextToken().split(":"); builder.guests(pair[0], Integer.parseInt(pair[1])); - } catch (Exception e) { - throw new IllegalArgumentException("Bad guest arguments"); + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException("a valid list of guest:number pairs was not found."); } } } @@ -117,9 +121,18 @@ public class DiscoCommands implements CommandExecutor { case 'b': builder.baby(Integer.parseInt(g.getOptarg())); break; + case '?': // handle invalid switch + // need to cast getOptopt() to char because it returns a string representation of an integer + sender.sendMessage(String.format("Invalid switch '%s' was found", (char)g.getOptopt())); + return false; } - } catch (IllegalArgumentException e) { + } catch (NumberFormatException e) { + String badNumber = e.getMessage().replace("For input string: ", ""); + sender.sendMessage(String.format("Error: %s is not a valid number", badNumber)); + return false; + } catch (IllegalArgumentException e) { // handle invalid arguments sender.sendMessage("Bad command: " + e.getMessage()); + return false; } } diff --git a/src/me/cwang/discosheep/DiscoSheep.java b/src/me/cwang/discosheep/DiscoSheep.java index abafb0d..5e4bd81 100644 --- a/src/me/cwang/discosheep/DiscoSheep.java +++ b/src/me/cwang/discosheep/DiscoSheep.java @@ -175,13 +175,9 @@ public final class DiscoSheep extends JavaPlugin { } } - public boolean hasParty(String name) { - return parties.containsKey(name); - } + public boolean hasParty(String name) { return parties.containsKey(name); } - public AbstractParty getParty(String name) { - return parties.get(name); - } + public AbstractParty getParty(String name) { return parties.get(name); } public void removeParty(String name) { if (this.hasParty(name)) { @@ -234,7 +230,7 @@ public final class DiscoSheep extends JavaPlugin { if (!hasParty(s.getName())) { builder.build().startDisco(); } else { - s.sendMessage(ChatColor.RED + "You already have a party. Are you underground?"); + s.sendMessage(ChatColor.RED + "You already have a party."); } return true; } diff --git a/src/me/cwang/discosheep/PartyBuilder.java b/src/me/cwang/discosheep/PartyBuilder.java index 5613237..e48fb74 100644 --- a/src/me/cwang/discosheep/PartyBuilder.java +++ b/src/me/cwang/discosheep/PartyBuilder.java @@ -30,7 +30,7 @@ public class PartyBuilder { public PartyBuilder duration(int duration) throws IllegalArgumentException { if (duration < 0 || duration > AbstractParty.maxDuration) { - throw new IllegalArgumentException("Invalid duration."); + throw new IllegalArgumentException("nvalid duration"); } this.duration = duration; return this; @@ -38,7 +38,7 @@ public class PartyBuilder { public PartyBuilder radius(int radius) throws IllegalArgumentException { if (radius < 0 || radius > AbstractParty.maxRadius) { - throw new IllegalArgumentException("Invalid radius."); + throw new IllegalArgumentException("Invalid radius"); } this.radius = radius; return this; @@ -51,7 +51,7 @@ public class PartyBuilder { public PartyBuilder period(int period) throws IllegalArgumentException { if (period < 0 || period > AbstractParty.maxPeriod) { - throw new IllegalArgumentException("Invalid period."); + throw new IllegalArgumentException("invalid period"); } this.period = period; return this; @@ -59,7 +59,7 @@ public class PartyBuilder { public PartyBuilder sheep(int sheep) throws IllegalArgumentException { if (sheep < 0 || sheep > AbstractParty.maxSheep) { - throw new IllegalArgumentException("Invalid sheep number"); + throw new IllegalArgumentException("invalid sheep number"); } this.sheep = sheep; return this; @@ -71,7 +71,7 @@ public class PartyBuilder { try { type = EntityType.valueOf(key); } catch (IllegalArgumentException e) { - throw new IllegalArgumentException(String.format("Invalid guest: %s", key)); + 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 @@ -79,7 +79,7 @@ public class PartyBuilder { return this; } } - throw new IllegalArgumentException(String.format("Invalid number for %s", key)); + throw new IllegalArgumentException(String.format("invalid number for %s", key)); } public PartyBuilder noGuests() {