Merge pull request #6 from Gibstick/2.0

2.0
This commit is contained in:
Charlie Wang 2015-12-20 01:41:56 -05:00
commit 4841885835
24 changed files with 1608 additions and 1327 deletions

7
.gitignore vendored
View File

@ -1,5 +1,4 @@
/target/
*.jar
/build/
/dist/
/server/
/out/
.idea
*.iml

View File

@ -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.

View File

@ -1,3 +0,0 @@
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<netbeans.hint.jdkPlatform>JDK_1.7</netbeans.hint.jdkPlatform>
<org-netbeans-modules-javascript2-requirejs.enabled>true</org-netbeans-modules-javascript2-requirejs.enabled>
</properties>
</project-shared-configuration>

105
pom.xml
View File

@ -1,105 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ca.gibstick</groupId>
<artifactId>DiscoSheep</artifactId>
<version>1.1.2</version>
<packaging>jar</packaging>
<repositories>
<repository>
<id>repo.oc.tc</id>
<url>http://repo.oc.tc/content/repositories/public/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.sk89q</groupId>
<artifactId>command-framework-bukkit</artifactId>
<version>0.5-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<version>1.8-R0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<finalName>DiscoSheep</finalName>
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<targetPath>.</targetPath>
<filtering>true</filtering>
<directory>src/main/resources/</directory>
<includes>
<include>plugin.yml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerArgs>
<arg>-Xlint:deprecation</arg>
</compilerArgs>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>DiscoSheep</finalName>
<useBaseVersion>true</useBaseVersion>
<shadedClassifierName/>
<outputDirectory>${project.basedir}</outputDirectory>
<artifactSet>
<includes>
<include>com.sk89q:command-framework-bukkit</include>
</includes>
</artifactSet>
<generateUniqueDependencyReducedPom>false</generateUniqueDependencyReducedPom>
<shadedArtifactAttached>true</shadedArtifactAttached> <!-- Makes Netbeans shut up -->
<shadedClassifierName>shaded</shadedClassifierName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<name>DiscoSheep</name>
</project>

View File

@ -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 <players>: 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 <integer>: set the number of sheep per player that spawn\n"
+ "-t <seconds>: set the party duration in seconds\n"
+ "-p <ticks>: set the number of ticks between each disco beat\n"
+ "-r <radius>: set radius of the area in which sheep can spawn\n"
+ "-g <mob> <number>: 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");
}
}

View File

@ -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<String, Integer> defaultGuestNumbers = new HashMap<>();
private static HashMap<String, Integer> maxGuestNumbers = new HashMap<>();
private static final EnumSet<Material> 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<Sheep> sheepList = new ArrayList<>();
private final HashSet<Sheep> sheepSet = new HashSet<>();
private final ArrayList<Entity> guestList = new ArrayList<>();
private final HashSet<Entity> guestSet = new HashSet<>();
private final ArrayList<BlockState> floorBlockCache = new ArrayList<>();
private final ArrayList<Block> floorBlocks = new ArrayList<>();
private HashMap<String, Integer> 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<Sheep> getSheepSet() {
return sheepSet;
}
HashSet<Entity> getGuestSet() {
return guestSet;
}
ArrayList<Sheep> getSheepList() {
return sheepList;
}
ArrayList<Entity> getGuestList() {
return guestList;
}
ArrayList<BlockState> getFloorCache() {
return this.floorBlockCache;
}
ArrayList<Block> getFloorBlocks() {
return this.floorBlocks;
}
public int getRadius() {
return radius;
}
public static HashMap<String, Integer> getDefaultGuestNumbers() {
return defaultGuestNumbers;
}
public HashMap<String, Integer> getGuestNumbers() {
return guestNumbers;
}
public static HashMap<String, Integer> 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();
}
}
}

View File

@ -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<String, DiscoParty> parties = new HashMap<>();
private CommandsManager<CommandSender> commands;
public static DiscoSheep getInstance() {
if (instance == null) {
instance = new DiscoSheep();
return instance;
}
return instance;
}
private void setupCommands() {
this.commands = new CommandsManager<CommandSender>() {
@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<String, Integer> 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<String, DiscoParty> getPartyMap() {
return this.parties;
}
public synchronized ArrayList<DiscoParty> 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<Player> getPlayersWithin(Player player, int distance) {
List<Player> 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;
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,274 @@
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<EntityType, Integer> 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<EntityType, Integer> maxGuestNumbers = new HashMap<>();
protected BukkitRunnable updater;
public AbstractParty() {
}
static HashMap<EntityType, Integer> getDefaultGuestNumbers() {
return defaultGuestNumbers;
}
static HashMap<EntityType, Integer> getMaxGuestNumbers() {
return maxGuestNumbers;
}
protected abstract Player getPlayer();
protected abstract void jump(Entity entity);
public abstract int getSheep();
protected abstract List<Sheep> getSheepList();
protected abstract List<LivingEntity> getGuestList();
protected abstract HashMap<EntityType, Integer> getGuestMap();
protected abstract int getPeriod();
protected abstract int getState();
protected abstract float getVolumeMultiplier();
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<EntityType, Integer> 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();
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,236 @@
package me.cwang.discosheep;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.event.HandlerList;
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<Sheep> sheepList;
private ArrayList<LivingEntity> guestList;
private HashMap<EntityType, Integer> 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<EntityType, Integer> 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<Sheep>();
this.r = new Random();
this.guestList = new ArrayList<LivingEntity>();
this.partyLocation = player.getLocation();
this.volumeMultiplier = 2.0f * 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<Sheep> getSheepList() {
return sheepList;
}
@Override
protected List<LivingEntity> getGuestList() {
return guestList;
}
@Override
protected HashMap<EntityType, Integer> 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 float getVolumeMultiplier() {
return volumeMultiplier;
}
@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;
}
/**
* 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<EntityType, Integer>(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);
}
}

View File

@ -0,0 +1,156 @@
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);
}
}
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 {
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 (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException("a valid list of guest:number pairs was not found.");
}
}
}
break;
case 'f':
builder.fireworks();
break;
case 'l':
if (sender.hasPermission(DiscoSheep.PERMISSION_LIGHTNING))
builder.lightning();
break;
case 'j':
builder.jeb();
break;
case 'P':
builder.pentatonic();
break;
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 (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;
}
}
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;
}
}

View File

@ -0,0 +1,109 @@
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<Sheep> getSheepList() {
return decoratedParty.getSheepList();
}
@Override
protected List<LivingEntity> getGuestList() {
return decoratedParty.getGuestList();
}
@Override
protected HashMap<EntityType, Integer> getGuestMap() {
return decoratedParty.getGuestMap();
}
@Override
protected int getPeriod() { return decoratedParty.getPeriod(); }
@Override
protected int getState() { return decoratedParty.getState(); }
@Override
protected float getVolumeMultiplier() { return decoratedParty.getVolumeMultiplier(); }
@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();
}
}

View File

@ -0,0 +1,349 @@
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<EntityType> 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<String, AbstractParty> 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<EntityType, Integer> 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<AbstractParty> 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 <players>: 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 <integer>: set the number of sheep per player that spawn\n"
+ "-t <integer>: set the party duration in seconds\n"
+ "-p <ticks>: set the number of ticks between each disco beat\n"
+ "-r <integer>: set radius of the area in which sheep can spawn\n"
+ "-g <mob:integer, mob:integer...>: set spawns for other mobs, eg. -g cow:5,pig:2\n"
+ "-l: enables lightning\n"
+ "-f: enables fireworks\n"
+ "-j: enables alternative method for setting sheep colours\n"
+ "-b <integer>: spawns a percentage of mobs as babies, if possible\n"
+ "-P: enables pentatonic backing track\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.");
}
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);
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}
}

View File

@ -0,0 +1,143 @@
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<EntityType, Integer> guests = AbstractParty.defaultGuestNumbers;
private boolean dense = false;
private boolean fireworks = false;
private boolean lightning = false;
private boolean jeb = false;
private boolean pentatonic = false;
private int babyness = 0;
public PartyBuilder(Player player) {
this.player = player;
}
public PartyBuilder duration(int duration) throws IllegalArgumentException {
if (duration < 0 || duration > AbstractParty.maxDuration) {
throw new IllegalArgumentException("nvalid 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 fireworks() {
fireworks = true;
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) party = new FireworkParty(party);
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;
AbstractParty otherParty = build();
player = oldPlayer;
return otherParty;
}
}

View File

@ -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())) {
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())) {
if (isFromParty(e.getEntity())) {
party.jump(e.getEntity()); // for kicks
e.setCancelled(true);
}
}
if (party.getGuestSet().contains(e.getEntity())) {
party.jump(e.getEntity());
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);
}
}
}

View File

@ -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 {
}

View File

@ -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, getVolumeMultiplier() * 1.0f, getPentatonicNote());
}
}

View File

@ -1,14 +1,14 @@
name: DiscoSheep
main: ca.gibstick.discosheep.DiscoSheep
main: me.cwang.discosheep.DiscoSheep
authors: [Gibstick, RangerMauve]
version: 1.2
version: 2.0
commands:
#ds:
#description: "Main DiscoSheep command"
#usage: |
#<command> <subcommand> [arguments]
#Use /ds help for more information
#To stop your party, use /ds stop.
ds:
description: "Main DiscoSheep command"
usage: |
<command> <subcommand> [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.*: