Preparations for customizable waves..

This commit is contained in:
Andreas Troelsen 2011-07-31 21:26:18 +02:00
parent a277bf2b33
commit 4c7d85193e
20 changed files with 1845 additions and 0 deletions

14
.classpath Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="S:/Java/Minecraft/lib/craftbukkit-0.0.1-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="S:/Java/Minecraft/lib/Permissions.jar"/>
<classpathentry kind="lib" path="S:/Java/Minecraft/lib/BOSEconomy7.jar"/>
<classpathentry kind="lib" path="S:/Java/Minecraft/lib/Essentials.jar"/>
<classpathentry kind="lib" path="S:/Java/Minecraft/lib/iCo4.jar"/>
<classpathentry kind="lib" path="S:/Java/Minecraft/lib/iCo5.jar"/>
<classpathentry kind="lib" path="MobArena.jar"/>
<classpathentry kind="lib" path="S:/Java/Minecraft/lib/jdom.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

17
.project Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>MobArena</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

41
build.xml Normal file
View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="MobArena" default="dist" basedir=".">
<property name="pluginname" value="MobArena"/>
<property name="server" location="S:\Minecraft Server\plugins"/>
<property name="src" location="src"/>
<property name="bin" location="bin"/>
<property name="lib" location="../lib"/>
<property name="res" location="resources"/>
<path id="classpath">
<fileset dir="${lib}" includes="**/*.jar"/>
<fileset dir="${res}" includes="**/*.*"/>
</path>
<target name="init">
<mkdir dir="${bin}"/>
</target>
<!-- Compile the source and put in the bin-folder -->
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${bin}" includeantruntime="false" classpathref="classpath"/>
</target>
<!-- Build a .jar and copy to server's plugins-folder -->
<target name="dist" depends="compile">
<delete file="${pluginname}.jar"/>
<jar jarfile="${pluginname}.jar">
<!-- Include the class-files (bin) and the resources (res) -->
<fileset dir="${bin}"/>
<fileset dir="${res}"/>
<!-- Set up the classpath so MobArena can find JDOM if needed -->
<manifest>
<attribute name="Main-Class" value="com.garbagemule.MobArena.MobArena"/>
<attribute name="Class-Path" value="plugins/MobArena/lib/jdom.jar MobArena/lib/jdom.jar"/>
</manifest>
</jar>
<!-- Copy to server and clean up -->
<copy file="${pluginname}.jar" tofile="${server}/${pluginname}.jar"/>
<delete dir="${bin}"/>
</target>
</project>

26
resources/plugin.yml Normal file
View File

@ -0,0 +1,26 @@
name: MobArena
main: com.garbagemule.MobArena.MobArena
version: 0.93.4
softdepend: [MultiVerse,XcraftGate,Towny]
commands:
ma:
description: Base command for MobArena
usage: |
/ma join - Join the arena.
/ma leave - Leave the arena.
/ma notready - List of players who aren't ready.
/ma spectate - Warp to the spectator area.
marena:
description: Base command for MobArena
usage: |
/marena join - Join the arena.
/marena leave - Leave the arena.
/marena notready - List of players who aren't ready.
/marena spectate - Warp to the spectator area.
mobarena:
description: Base command for MobArena
usage: |
/mobarena join - Join the arena.
/mobarena leave - Leave the arena.
/mobarena notready - List of players who aren't ready.
/mobarena spectate - Warp to the spectator area.

View File

@ -0,0 +1,51 @@
ARENA_START=Let the slaughter begin!
ARENA_END=Arena finished.
ARENA_DOES_NOT_EXIST=That arena does not exist. Type /ma arenas for a list.
JOIN_PLAYER_JOINED=
JOIN_NOT_ENABLED=MobArena is not enabled.
JOIN_IN_OTHER_ARENA=You are already in an arena! Leave that one first.
JOIN_ARENA_NOT_ENABLED=This arena is not enabled.
JOIN_ARENA_NOT_SETUP=This arena has not been set up yet.
JOIN_ARENA_PERMISSION=You don't have permission to join this arena.
JOIN_FEE_REQUIRED=Insufficient funds. Price: %
JOIN_FEE_PAID=Price to join was: %
JOIN_ARENA_IS_RUNNING=This arena is in already progress.
JOIN_ALREADY_PLAYING=You are already playing!
JOIN_ARG_NEEDED=You must specify an arena. Type /ma arenas for a list.
JOIN_TOO_FAR=You are too far away from the arena to join/spectate.
JOIN_EMPTY_INV=You must empty your inventory to join the arena.
JOIN_PLAYER_LIMIT_REACHED=The player limit of this arena has been reached.
JOIN_STORE_INV_FAIL=Failed to store inventory. Try again.
LEAVE_PLAYER_LEFT=You left the arena. Thanks for playing!
LEAVE_NOT_PLAYING=You are not in the arena.
PLAYER_DIED=% died!
SPEC_PLAYER_SPECTATE=Enjoy the show!
SPEC_NOT_RUNNING=This arena isn't running.
SPEC_ARG_NEEDED=You must specify an arena. Type /ma arenas for a list.
SPEC_EMPTY_INV=Empty your inventory first!
SPEC_ALREADY_PLAYING=Can't spectate when in the arena!
NOT_READY_PLAYERS=Not ready: %
FORCE_START_STARTED=Forced arena start.
FORCE_START_RUNNING=Arena has already started.
FORCE_START_NOT_READY=Can't force start, no players are ready.
FORCE_END_ENDED=Forced arena end.
FORCE_END_EMPTY=No one is in the arena.
FORCE_END_IDLE=You weren't quick enough!
REWARDS_GIVE=Here are all of your rewards!
LOBBY_CLASS_PICKED=You have chosen % as your class!
LOBBY_CLASS_RANDOM=You will get a random class on arena start.
LOBBY_CLASS_PERMISSION=You don't have permission to use this class!
LOBBY_PLAYER_READY=You have been flagged as ready!
LOBBY_DROP_ITEM=No sharing before the arena starts!
LOBBY_PICK_CLASS=You must first pick a class!
LOBBY_RIGHT_CLICK=Punch the sign. Don't right-click.
WARP_TO_ARENA=Can't warp to the arena during battle!
WARP_FROM_ARENA=Warping not allowed in the arena!
WAVE_DEFAULT=Get ready for wave #%!
WAVE_SPECIAL=Get ready for wave #%! [SPECIAL]
WAVE_REWARD=You just earned a reward: %
MISC_LIST_ARENAS=Available arenas: %
MISC_LIST_PLAYERS=Live players: %
MISC_COMMAND_NOT_ALLOWED=You can't use that command in the arena!
MISC_NO_ACCESS=You don't have access to this command.
MISC_NONE=<none>

107
resources/res/config.yml Normal file
View File

@ -0,0 +1,107 @@
### MobArena Configuration File
### Please visit the MobArena Wiki here: http://goo.gl/F5TTc for more details
### on how to set up this file.
### Note that you CAN'T use tabs in this file! Always use spaces!
### GLOBAL SETTINGS
# These settings are arena-independent and count in all arenas. Note that if
# enabled: false in the global settings, MobArena is disabled globally, and
# arena-specific enabled-values do not matter. If true, however, the per-arena
# enabled-values count.
global-settings:
enabled: true
allowed-commands: /list, /pl
update-notification: true
### CLASS SETUP
# Items can be written as either their data value (numbers) or their Material
# type (names); check the Wiki for links to lists of both.
# The notation is <item>:<amount>. If no amount is given, 1 is assumed. Sub-
# types can be created
# Item SUBTYPES (wool/dye colors) are written as <item>:<subtype>:<amount>,
# but here, the amount is REQUIRED, even if it is just 1.
# Note: If you want to specify only one item, but apostrophes around that one
# item, e.g.: items: '278'
# Note: For every bone a class has, one PET WOLF will spawn upon arena start,
# which will assist the player in the arena session.
classes:
Knight:
items: diamond_sword, grilled_pork:2
armor: 306,307,308,309
Tank:
items: iron_sword, grilled_pork:3, apple
armor: 310,311,312,313
Archer:
items: wood_sword, bow, arrow:64, arrow:64, grilled_pork, bone:2
armor: 298,299,300,301
Chef:
items: stone_sword, bread:6, grilled_pork:4, mushroom_soup, cake:5
armor: 314,315,316,317
Oddjob:
items: stone_sword, flint_and_steel, netherrack:2, tnt:4, pork:3
armor: 298,299,300,301
### ARENA SETUP
# The arena setup is split into 4 different sections per arena: settings,
# waves, rewards and coords (not shown until coords are set up).
# Please refer to the Wiki for descriptions of all these settings.
# Note: The REWARDS use the same notation as the items in the CLASS SETUP
# section. However, only -one- item from the list is picked (at random) when
# the rewards are given.
arenas:
default:
settings:
world: ''
enabled: true
protect: true
entry-fee: ''
logging: true
clear-wave-before-next: false
detonate-creepers: false
detonate-damage: false
lightning: true
auto-equip-armor: true
force-restore: false
soft-restore: false
soft-restore-drops: false
require-empty-inv-join: false
require-empty-inv-spec: false
hellhounds: false
pvp-enabled: false
monster-infight: false
allow-teleporting: false
spectate-on-death: true
share-items-in-arena: true
min-players: 0
max-players: 0
max-join-distance: 0
repair-delay: 5
first-wave-delay: 5
wave-interval: 20
special-modulo: 4
max-idle-time: 0
waves:
default:
zombies: 10
skeletons: 10
spiders: 10
creepers: 10
wolves: 10
special:
powered-creepers: 10
zombie-pigmen: 10
angry-wolves: 10
slimes: 10
humans: 10
giants: 0
ghasts: 0
rewards:
waves:
every:
'3': feather, bone, stick
'5': dirt:4, gravel:4, stone:4
'10': iron_ingot:10, gold_ingot:8
after:
'7': minecart, storage_minecart, powered_minecart
'13': iron_sword, iron_pickaxe, iron_spade
'16': diamond_sword

57
resources/res/totals.txt Normal file
View File

@ -0,0 +1,57 @@
### General data
### Statistics for overall information about the arena, such as
### times played, durations, max players seen, total kills, etc.
general-info:
total-games-played: 0
total-duration: 0:00:00
longest-session-duration: 0:00:00
most-players: 0
highest-wave-reached: 0
total-monsters-killed: 0
### Class specific data
### How many times has each class been played, and how many kills, how
### much damage, etc. does the class make/do/take.
classes:
overall-distribution:
# Percentages can be calculated with <classname> / total-count * 100.
total-count: 0
#classes:
# overall-distribution:
# total-count: 0
# archer:
# kills:
# damage-done:
# damage-taken:
# accuracy:
# knight:
# kills:
# damage-done:
# damage-taken:
# accuracy:
# ...
### Reward data
### Totals for all rewards given, perhaps good for balancing?
rewards:
total-given:
# This might be useless information, I don't know.
# Might have to make it data values instead of Material names?
money: 0
### Player data
### Total games played, kills, damage, swings and hits (for accuracy), and how
### many times the player has played as each class.
players: {}
# garbagemule:
# games-played: 5
# kills: 4
# damage-done: 251
# damage-taken: 102
# swings: 1820
# hits: 1214
# classes-played:
# archer: 2
# ...
# Agnate:
# ...

57
resources/res/totals.yml Normal file
View File

@ -0,0 +1,57 @@
### General data
### Statistics for overall information about the arena, such as
### times played, durations, max players seen, total kills, etc.
general-info:
total-games-played: 0
total-duration: 0:00:00
longest-session-duration: 0:00:00
most-players: 0
highest-wave-reached: 0
total-monsters-killed: 0
### Class specific data
### How many times has each class been played, and how many kills, how
### much damage, etc. does the class make/do/take.
classes:
overall-distribution:
# Percentages can be calculated with <classname> / total-count * 100.
total-count: 0
#classes:
# overall-distribution:
# total-count: 0
# archer:
# kills:
# damage-done:
# damage-taken:
# accuracy:
# knight:
# kills:
# damage-done:
# damage-taken:
# accuracy:
# ...
### Reward data
### Totals for all rewards given, perhaps good for balancing?
rewards:
total-given:
# This might be useless information, I don't know.
# Might have to make it data values instead of Material names?
money: 0
### Player data
### Total games played, kills, damage, swings and hits (for accuracy), and how
### many times the player has played as each class.
players: {}
# garbagemule:
# games-played: 5
# kills: 4
# damage-done: 251
# damage-taken: 102
# swings: 1820
# hits: 1214
# classes-played:
# archer: 2
# ...
# Agnate:
# ...

View File

@ -0,0 +1,42 @@
package com.garbagemule.MobArena;
import java.util.LinkedList;
import java.util.List;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class ArenaPlayer
{
public MobArena plugin;
public Player player;
public String className;
public Arena arena;
public List<ItemStack> rewards;
protected boolean inArena, inLobby, inSpec, isReady;
// Session fields.
public int kills, dmgDone, dmgTaken, swings, hits, deaths, lastWave;
public int flagCaps, flagAttempts, flagReturns; // BG: Capture the Pumpkin
public int baseCaps; // BG: Domination
// All-time fields.
protected int totalKills, totalDmgDone, totalDmgTaken, totalSwings, totalHits, totalDeaths;
protected int totalFlagCaps, totalFlagAttempts, totalFlagReturns; // BG: Capture the Pumpkin
protected int totalBaseCaps; // BG: Domination
public ArenaPlayer(Player player, Arena arena, MobArena plugin)
{
this.player = player;
this.arena = arena;
this.plugin = plugin;
className = arena.classMap.get(player);
rewards = new LinkedList<ItemStack>();
}
public Player getPlayer() { return player; }
public Arena getArena() { return arena; }
public String getClassName() { return className; }
}

View File

@ -0,0 +1,211 @@
package com.garbagemule.MobArena.util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bukkit.util.config.Configuration;
import com.garbagemule.MobArena.MobArena;
public class FileUtils
{
public static enum Libs
{
xml("jdom.jar", "http://mirrors.ibiblio.org/pub/mirrors/maven2/org/jdom/jdom/1.1/jdom-1.1.jar");
public String url, filename;
private Libs(String filename, String url)
{
this.filename = filename;
this.url = url;
}
public static Libs getLib(String filename)
{
for (Libs l : Libs.values())
if (l.filename.equals(filename))
return l;
return null;
}
}
/**
* Create default files from the res/ directory, if they exist.
* @param filenames Files to be created.
*/
public static void extractDefaults(String... filenames)
{
for (String filename : filenames)
extractFile(MobArena.dir, filename);
}
public static void extractFile(File dir, String filename)
{
// Skip if file exists
File file = new File(dir, filename);
if (file.exists()) return;
// Skip if there is no resource with that name
InputStream in = MobArena.class.getResourceAsStream("/res/" + filename);
if (in == null) return;
try
{
// Set up an output stream
FileOutputStream out = new FileOutputStream(file);
byte[] buffer = new byte[8192];
int length = 0;
// Write the resource data to the file
while ((length = in.read(buffer)) > 0)
out.write(buffer, 0, length);
if (in != null) in.close();
if (out != null) out.close();
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("[MobArena] ERROR! Problem creating file '" + filename + "'!");
}
}
/**
* Download all necessary libraries.
* @param config The MobArena config-file
*/
public static void fetchLibs(Configuration config)
{
// Get all arenas
List<String> arenas = config.getKeys("arenas");
if (arenas == null) return;
// Add all the logging types
Set<String> libs = new HashSet<String>();
for (String a : arenas)
{
String type = config.getString("arenas." + a + ".settings.logging", "").toLowerCase();
if (type.equals("xml"))
libs.add(type);
}
// Download all libraries
for (String lib : libs)
{
if (download(Libs.valueOf(lib)))
continue;
// If a library couldn't be downloaded, default to false.
for (String a : arenas)
{
if (!config.getString("arenas." + a + ".settings.logging", "").equalsIgnoreCase(lib))
continue;
System.out.println("[MobArena] ERROR! Unrecognized format for arena '" + a + "': " + lib + ". Logging disabled.");
config.setProperty("arenas." + a + ".logging", "false");
}
}
}
private static synchronized boolean download(Libs lib)
{
if (lib == null) return false;
InputStream in = null;
OutputStream out = null;
try
{
URLConnection con = new URL(lib.url).openConnection();
con.setUseCaches(false);
// Library folder: plugin/MobArena/lib
File libdir = new File(MobArena.dir, "lib");
libdir.mkdir();
// Create the file if it doesn't exist, if it does, return
File file = new File(libdir, lib.filename);
if (file.exists()) return true;
long startTime = System.currentTimeMillis();
System.out.println("[MobArena] Downloading library: " + lib.filename + "...");
// Set up the streams
in = con.getInputStream();
out = new FileOutputStream(file);
if (in == null || out == null) return false;
byte[] buffer = new byte[65536];
int length = 0;
// Write the library to disk
while ((length = in.read(buffer)) > 0)
out.write(buffer, 0, length);
System.out.println("[MobArena] " + lib.filename + " downloaded in " + ((System.currentTimeMillis()-startTime)/1000.0) + " seconds.");
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("[MobArena] ERROR! Couldn't download library: " + lib.filename);
return false;
}
finally
{
try
{
if (in != null) in.close();
if (out != null) out.close();
}
catch (Exception e) { e.printStackTrace(); }
}
return true;
}
public static File getMostRecent(String folder)
{
return getMostRecent(new File(folder));
}
public static File getMostRecent(File dir)
{
if (!dir.exists()) dir.mkdir();
if (!dir.isDirectory()) return null;
long mostRecent = 0;
File result = null;
for (File file : dir.listFiles())
{
if (file.isDirectory() || file.lastModified() > mostRecent)
continue;
result = file;
mostRecent = file.lastModified();
}
return result;
}
public static Configuration parseXML(File file)
{
return null;
}
public static Configuration parseCSV(File file)
{
return null;
}
public static Configuration parsePlainText(File file)
{
return null;
}
}

View File

@ -0,0 +1,42 @@
package com.garbagemule.MobArena.util;
public class TextUtils
{
/**
* Add character padding on the right side of a String.
* @param s String to add padding to
* @param length Total amount of characters in the returned String
* @param pad The padding character
* @return A padded String with the input length
*/
public static String padRight(String s, int length, char pad)
{
StringBuffer buffy = new StringBuffer();
buffy.append(s);
for (int i = s.length(); i < length; i++)
buffy.append(pad);
return buffy.toString();
}
public static String padRight(String s, int length) { return padRight(s, length, ' '); }
public static String padRight(int s, int length) { return padRight(Integer.toString(s), length, ' '); }
public static String padRight(double s, int length) { return padRight(Double.toString(s), length, ' '); }
/**
* Add character padding on the left side of a String.
* @param s String to add padding to
* @param length Total amount of characters in the returned String
* @param pad The padding character
* @return A padded String with the input length
*/
public static String padLeft(String s, int length, char pad)
{
StringBuffer buffy = new StringBuffer();
for (int i = 0; i < length - s.length(); i++)
buffy.append(pad);
buffy.append(s);
return buffy.toString();
}
public static String padLeft(String s, int length) { return padLeft(s, length, ' '); }
public static String padLeft(int s, int length) { return padLeft(Integer.toString(s), length, ' '); }
public static String padLeft(double s, int length) { return padLeft(Double.toString(s), length, ' '); }
}

View File

@ -0,0 +1,534 @@
package com.garbagemule.MobArena.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;
import org.bukkit.Location;
import org.bukkit.entity.CreatureType;
import org.bukkit.entity.Player;
import org.bukkit.util.config.Configuration;
import com.garbagemule.MobArena.MAUtils;
import com.garbagemule.MobArena.MobArena;
import com.garbagemule.MobArena.waves.RecurrentWave;
import com.garbagemule.MobArena.waves.SingleWave;
import com.garbagemule.MobArena.waves.Wave;
import com.garbagemule.MobArena.waves.Wave.BossAbility;
import com.garbagemule.MobArena.waves.Wave.BossHealth;
import com.garbagemule.MobArena.waves.Wave.SwarmAmount;
import com.garbagemule.MobArena.waves.Wave.WaveBranch;
import com.garbagemule.MobArena.waves.Wave.WaveGrowth;
import com.garbagemule.MobArena.waves.Wave.WaveType;
public class WaveUtils
{
/**
* Get all the spawnpoints that have players nearby.
*/
public static List<Location> getValidSpawnpoints(Collection<Location> spawnpoints, Collection<Player> players)
{
List<Location> result = new ArrayList<Location>();
for (Location s : spawnpoints)
{
for (Player p : players)
{
// If the player somehow got out of the arena world, kick him.
if (!s.getWorld().getName().equals(p.getWorld().getName()))
{
System.out.println("[MobArena] Player '" + p.getName() + "' is not in the right world. Kicking...");
p.kickPlayer("[MobArena] Cheater! (Warped out of the arena world.)");
continue;
}
if (s.distanceSquared(p.getLocation()) > MobArena.MIN_PLAYER_DISTANCE)
continue;
result.add(s);
break;
}
}
// If no players are in range, just use all the spawnpoints.
if (result.isEmpty())
result.addAll(spawnpoints);
// Else, return the valid spawnpoints.
return result;
}
/**
* Grab and process all the waves in the config-file for the arena.
*/
public static TreeSet<Wave> getWaves(Configuration config, String arena, WaveBranch branch)
{
// Determine the branch type of the wave, and grab the appropriate comparator
String b = branch.toString().toLowerCase();
TreeSet<Wave> result = new TreeSet<Wave>(getComparator(branch));
// Grab the waves from the config-file
String path = "arenas." + arena + ".waves." + b;
List<String> waves = config.getKeys(path);
// If there are any waves, process them
if (waves != null)
{
Wave wave;
for (String w : waves)
{
// path argument becomes: "arenas.<arena>.waves.<branch>.<wave>."
wave = getWave(config, path + "." + w + ".", w, branch);
if (wave != null) result.add(wave);
}
}
// If there are no waves and the type is 'recurrent', add a default wave.
if (branch == WaveBranch.RECURRENT && (result.isEmpty() || waves == null))
{
RecurrentWave def = new RecurrentWave("DEF_WAVE_AUTO", 1, 1, 1);
def.setType(WaveType.DEFAULT);
def.setGrowth(WaveGrowth.MEDIUM);
def.setDefault(true);
result.add(def);
}
return result;
}
/**
* Get a single wave based on the config-file, the path, and branch
* @return A Wave object if it is well defined, null otherwise.
*/
private static Wave getWave(Configuration config, String path, String name, WaveBranch branch)
{
// Grab the wave type, if null, return null
WaveType type = WaveType.fromString(config.getString(path + "type"));
if (type == null || !isWaveWellDefined(config, path, branch, type))
return null;
// TODO: Generate waves properly. These are place-holders!
Wave result;
if (branch == WaveBranch.RECURRENT)
{
int frequency = config.getInt(path + "frequency", 0);
int priority = config.getInt(path + "priority", 0);
int wave = config.getInt(path + "wave", frequency);
result = new RecurrentWave(name, wave, frequency, priority);
}
else
{
int wave = config.getInt(path + "wave", 0);
result = new SingleWave(name, wave);
}
return result;
}
/*
public static RecurrentWave getRecurrentWave(Configuration config, String arena, String w)
{
// Grab the path
String path = "arenas." + arena + ".waves.recurrent." + w + ".";
// Ensure that frequency and priority exist, otherwise return null
int frequency = config.getInt(path + "frequency", 0);
int priority = config.getInt(path + "priority", 0);
if (frequency == 0 || priority == 0) return null;
// Grab other variables
int wave = config.getInt(path + "wave", frequency);
WaveType type = WaveType.fromString(config.getString(path + "type", "default"));
WaveGrowth growth = WaveGrowth.fromString(config.getString(path + "growth", "medium"));
// Grab monster distribution
//Map<CreatureType,Integer> monsters = getWaveMonsters(config, arena, path, type);
// Create the wave
RecurrentWave result = new RecurrentWave(w, wave, frequency, priority, type, growth);
return result;
}
public static SingleWave getSingleWave(Configuration config, String arena, String w)
{
// Grab the path
String path = "arenas." + arena + ".waves.single." + w + ".";
// Ensure that the wave number exists, otherwise return null
int wave = config.getInt(path + "wave", 0);
if (wave == 0) return null;
// Grab other variables
SingleWave result = new SingleWave(w, wave);
return result;
}
public static Map<CreatureType,Integer> getWaveMonsters(Configuration config, String arena, String path, String type)
{
Map<CreatureType,Integer> result = new HashMap<CreatureType,Integer>();
List<String> monsters = config.getKeys(path + "monsters");
// If no monsters specified, make sure to add some
if (monsters == null)
{
if (type.equals("default"))
{
result.put(CreatureType.ZOMBIE, 10);
result.put(CreatureType.SKELETON, 10);
result.put(CreatureType.SPIDER, 10);
result.put(CreatureType.CREEPER, 10);
result.put(CreatureType.WOLF, 10);
}
else if (type.equals("default"))
{
}
}
return result;
}
*/
/*////////////////////////////////////////////////////////////////////
//
// Comparators
//
////////////////////////////////////////////////////////////////////*/
/**
* Get a comparator based on the WaveBranch parameter.
*/
public static Comparator<Wave> getComparator(WaveBranch branch)
{
if (branch == WaveBranch.SINGLE)
return getSingleComparator();
else if (branch == WaveBranch.RECURRENT)
return getRecurrentComparator();
else
return null;
}
/**
* Get a Comparator that compares Wave objects by wave number.
* If the wave numbers are equal, the waves are equal. This is to
* DISALLOW "duplicates" in the SINGLE WAVES collection.
* @return Comparator whose compare()-method compares wave numbers.
*/
public static Comparator<Wave> getSingleComparator()
{
return new Comparator<Wave>()
{
public int compare(Wave w1, Wave w2)
{
if (w1.getWave() < w2.getWave())
return -1;
else if (w1.getWave() > w2.getWave())
return 1;
else return 0;
}
};
}
/**
* Get a Comparator that compares Wave objects by priority.
* If the priorities are equal, the names are compared. This is to
* ALLOW "duplicates" in the RECURRENT WAVES collection.
* @return Comparator whose compare()-method compares wave priorities.
*/
public static Comparator<Wave> getRecurrentComparator()
{
return new Comparator<Wave>()
{
public int compare(Wave w1, Wave w2)
{
if (w1.getPriority() < w2.getPriority())
return -1;
else if (w1.getPriority() > w2.getPriority())
return 1;
else return w1.getName().compareTo(w2.getName());
}
};
}
/**
* Get all the single waves for the given arena.
*/
/*public static TreeSet<SingleWave> getSingleWaves(Configuration config, String arena)
{
TreeSet<SingleWave> result = new TreeSet<SingleWave>();
List<String> waves = config.getKeys("arenas." + arena + ".waves.single");
if (waves != null)
{
int wave;
for (String w : waves)
{
wave = config.getInt("arenas." + arena + ".waves.single." + w + ".wave", 0);
if (wave == 0) continue;
result.add(new SingleWave(w, wave));
}
}
return result;
}*/
/**
* Get all the recurrent waves for the given arena.
* If no waves are found, a default wave is added. This ensures that
* the arena always has monsters spawning, regardless of how badly the
* user messes up the config-file.
*/
/*public static TreeSet<RecurrentWave> getRecurrentWaves(Configuration config, String arena)
{
TreeSet<RecurrentWave> result = new TreeSet<RecurrentWave>();
List<String> waves = config.getKeys("arenas." + arena + ".waves.recurrent");
if (waves != null)
{
int wave, frequency, priority;
for (String w : waves)
{
frequency = config.getInt("arenas." + arena + ".waves.recurrent." + w + ".frequency", 0);
priority = config.getInt("arenas." + arena + ".waves.recurrent." + w + ".priority", 0);
if (frequency == 0 || priority == 0) continue;
wave = config.getInt("arenas." + arena + ".waves.single." + w + ".wave", frequency);
result.add(new RecurrentWave(w, wave, frequency, priority));
}
}
else
{
RecurrentWave def = new RecurrentWave("DEF_WAVE_AUTO", 1, 1, 1);
def.setType(WaveType.DEFAULT);
def.setGrowth(WaveGrowth.MEDIUM);
RecurrentWave spec = new RecurrentWave("SPEC_WAVE_AUTO", 4, 4, 4);
spec.setType(WaveType.SPECIAL);
result.add(def);
result.add(spec);
}
return result;
}*/
/*////////////////////////////////////////////////////////////////////
//
// Well definedness checks
//
////////////////////////////////////////////////////////////////////*/
/**
* Check if a wave in the config-file is well-defined.
* The method first checks if the wave is well-defined according to
* the branch-specific requirements. Recurrent waves must have the
* two nodes 'priority' and 'frequency', and single waves must have
* the node 'wave'.
* Any other requirements are type-specific, and thus we check if the
* type is well-defined.
* @param config Config-file Configuration
* @param path The absolute path of the wave
* @param branch The branch of the wave
* @param type The wave type
* @return true, if the wave is well-defined, false otherwise
*/
private static boolean isWaveWellDefined(Configuration config, String path, WaveBranch branch, WaveType type)
{
if (branch == WaveBranch.RECURRENT)
{
// REQUIRED: Priority and frequency
int priority = config.getInt(path + "priority", 0);
int frequency = config.getInt(path + "frequency", 0);
if (priority == 0 || frequency == 0)
return false;
// TODO: OPTIONAL: Wave growth, others?
}
else if (branch == WaveBranch.SINGLE)
{
// REQUIRED: Wave number
int wave = config.getInt(path + "wave", 0);
if (wave == 0)
return false;
}
else return false;
// Passed branch-checks; check type
return isTypeWellDefined(config, path, type);
}
/**
* Check if a wave type in the config-file is well-defined.
* The method calls the appropriate sub-method to check if the type
* is well-defined.
* @param config Config-file Configuration
* @param path The absolute path of the wave
* @param type The wave type
* @return true, if the wave type is well-defined, false otherwise
*/
private static boolean isTypeWellDefined(Configuration config, String path, WaveType type)
{
if (type == WaveType.DEFAULT || type == WaveType.SPECIAL)
return isNormalWaveWellDefined(config, path);
else if (type == WaveType.BOSS)
return isBossWaveWellDefined(config, path);
else if (type == WaveType.SWARM)
return isSwarmWaveWellDefined(config, path);
return false;
}
/**
* Check if a default or special wave is well-defined.
* There are no REQUIRED nodes for default or special wave types, besides
* the ones for the branch they belong to.
* The only OPTIONAL node is (currently) 'monsters'
* @param config Config-file Configuration
* @param path The absolute path of the wave
* @return true, if the wave type is well-defined, false otherwise
*/
private static boolean isNormalWaveWellDefined(Configuration config, String path)
{
// OPTIONAL: Monsters
List<String> monsters = config.getKeys(path + "monsters");
if (monsters == null)
return true;
for (String monster : monsters)
{
if (getEnumFromString(CreatureType.class, monster) != null)
continue;
MAUtils.error("Invalid monster type '" + monster + "' in " + path);
return false;
}
return true;
}
/**
* Check if a swarm wave is well defined
* @param config Config-file Configuration
* @param path The absolute path of the wave
* @return true, if the wave type is well-defined, false otherwise
*/
private static boolean isSwarmWaveWellDefined(Configuration config, String path)
{
// REQUIRED: Monster type
String monster = config.getString(path + "monster");
if (monster == null)
{
MAUtils.error("Missing monster type in '" + path);
return false;
}
else if (getEnumFromString(CreatureType.class, monster) == null)
{
MAUtils.error("Invalid monster type '" + monster + "' in " + path);
return false;
}
// OPTIONAL: Amount
String amount = config.getString(path + "amount");
if (amount != null && SwarmAmount.fromString(amount) == null)
return false;
return true;
}
/**
* Check if a boss wave is well defined.
* @param config Config-file Configuration
* @param path The absolute path of the wave
* @return true, if the wave type is well-defined, false otherwise
*/
private static boolean isBossWaveWellDefined(Configuration config, String path)
{
// REQUIRED: Monster type
String monster = config.getString(path + "monster");
if (monster == null)
{
MAUtils.error("Missing monster type in '" + path);
return false;
}
else if (getEnumFromString(CreatureType.class, monster) == null)
{
MAUtils.error("Invalid monster type '" + monster + "' in " + path);
return false;
}
// OPTIONAL: Abilities
String abilities = config.getString(path + "abilities");
if (abilities != null)
{
for (String ability : abilities.split(","))
{
if (BossAbility.fromString(ability.trim().toUpperCase()) != null)
continue;
MAUtils.error("Invalid boss ability '" + ability + "' in " + path);
return false;
}
}
// TODO: OPTIONAL: Adds
// Unsure about config-file implementation...
// OPTIONAL: Health
String health = config.getString(path + "health");
if (health != null && BossHealth.fromString(health) == null)
return false;
return true;
}
/*////////////////////////////////////////////////////////////////////
//
// Misc - Perhaps move into MAUtils?
//
////////////////////////////////////////////////////////////////////*/
/**
* Get the num value of a string, def if it doesn't exist.
*/
public static <T extends Enum<T>> T getEnumFromString(Class<T> c, String string, T def)
{
if (c != null && string != null)
{
try
{
return Enum.valueOf(c, string.trim().toUpperCase());
}
catch (IllegalArgumentException ex) { }
}
return def;
}
/**
* Get the enum value of a string, null if it doesn't exist.
*/
public static <T extends Enum<T>> T getEnumFromString(Class<T> c, String string)
{
if(c != null && string != null)
{
try
{
return Enum.valueOf(c, string.trim().toUpperCase());
}
catch(IllegalArgumentException ex) { }
}
return null;
}
}

View File

@ -0,0 +1,153 @@
package com.garbagemule.MobArena.util.data;
import java.io.File;
import java.io.FileWriter;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.bukkit.entity.Player;
import com.garbagemule.MobArena.ArenaLog;
import com.garbagemule.MobArena.ArenaPlayer;
import com.garbagemule.MobArena.MAUtils;
import com.garbagemule.MobArena.MobArena;
import com.garbagemule.MobArena.util.TextUtils;
public class PlainText
{
public static void saveSessionData(ArenaLog log)
{
File dir = new File(MobArena.arenaDir, log.getArena().configName());
if (!dir.exists()) dir.mkdirs();
// General information
List<String> toWrite = new LinkedList<String>();
toWrite.add("Start: " + log.getStartTime());
toWrite.add("End: " + log.getEndTime());
toWrite.add("Duration: " + log.getDuration());
toWrite.add("Last wave: " + log.getLastWave());
toWrite.add(" ");
int classLength = longestClassName(log.distribution.keySet());
int classCount = log.distribution.keySet().size();
int playerCount = log.players.keySet().size();
// Class distribution
toWrite.add("Class Distribution: " + classCount + " classes");
toWrite.addAll(getClassDistribution(log.distribution, playerCount, classLength));
toWrite.add(" ");
// Player data
toWrite.add("Player Data: " + playerCount + " players");
toWrite.addAll(getPlayerData(log.players, classLength));
// Serialize!
try
{
File file = new File(dir, "lastsession.txt");
if (!file.exists()) file.createNewFile();
FileWriter fw = new FileWriter(file);
for (String s : toWrite)
{
fw.write(s);
fw.write(System.getProperty("line.separator"));
}
fw.close();
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("[MobArena] ERROR! Problem saving session data for arena '" + log.getArena().configName() + "'");
}
}
public static void updateArenaTotals(ArenaLog log)
{
// Grab the Configuration
//Configuration totals = Totals.getArenaTotals(log.arena);
// Parse shit
// Serialize
}
/* public static void updateTotals(ArenaLog log, MobArena plugin)
{
File dir = new File(MobArena.arenaDir, log.getArena().configName());
if (!dir.exists()) dir.mkdir();
} */
private static List<String> getClassDistribution(Map<String,Integer> map, int playerCount, int classLength)
{
List<String> result = new LinkedList<String>();
// Add each entry - class: # (percentage%)
for (Map.Entry<String,Integer> entry : map.entrySet())
{
StringBuffer buffy = new StringBuffer(classLength + 15);
buffy.append("- ");
buffy.append(TextUtils.padRight(entry.getKey() + ":", classLength + 3)); // <classname>:
buffy.append(TextUtils.padLeft(entry.getValue().toString(), 2)); // <count>
buffy.append("(" + (entry.getValue()*100/playerCount) + "%)"); // (<count/playerCount>%)
result.add(buffy.toString());
}
return result;
}
private static List<String> getPlayerData(Map<Player,ArenaPlayer> map, int classLength)
{
List<String> result = new LinkedList<String>();
String pad = " ";
// Padding variables; +2 for extra padding
int NAME = 12, CLASS = classLength, WAVE = 4, KILLS = 5, DMGDONE = 7, DMGTAKEN = 8, ACCURACY = 8;
result.add(" " + TextUtils.padRight("NAME", NAME+2) +
TextUtils.padRight("CLASS", CLASS+2) +
TextUtils.padRight("WAVE", WAVE+2) +
TextUtils.padRight("KILLS", KILLS+2) +
TextUtils.padRight("DMGDONE", DMGDONE+2) +
TextUtils.padRight("DMGTAKEN", DMGTAKEN+2) +
TextUtils.padRight("ACCURACY", ACCURACY+2) +
"REWARDS");
// Add each player - name class wave dmgdone dmgtaken accuracy rewards
for (Map.Entry<Player,ArenaPlayer> entry : map.entrySet())
{
Player p = entry.getKey();
ArenaPlayer ap = entry.getValue();
StringBuffer buffy = new StringBuffer(80);
String name = (p.getName().length() <= NAME) ? p.getName() : p.getName().substring(0, NAME+1);
buffy.append("- ");
buffy.append(TextUtils.padRight(name, NAME)); buffy.append(pad);
buffy.append(TextUtils.padRight(ap.getClassName(), CLASS)); buffy.append(pad);
buffy.append(TextUtils.padLeft(ap.lastWave, WAVE)); buffy.append(pad);
buffy.append(TextUtils.padLeft(ap.kills, KILLS)); buffy.append(pad);
buffy.append(TextUtils.padLeft(ap.dmgDone, DMGDONE)); buffy.append(pad);
buffy.append(TextUtils.padLeft(ap.dmgTaken, DMGTAKEN)); buffy.append(pad);
buffy.append(TextUtils.padLeft(((ap.swings != 0) ? ap.hits*100/ap.swings : 0), ACCURACY-1)); buffy.append("%"); buffy.append(pad);
buffy.append(MAUtils.listToString(ap.rewards));
result.add(buffy.toString());
}
return result;
}
private static int longestClassName(Collection<String> names)
{
int result = 0;
for (String c : names)
if (c.length() > result)
result = c.length();
return result;
}
}

View File

@ -0,0 +1,159 @@
package com.garbagemule.MobArena.util.data;
import java.io.File;
import org.bukkit.util.config.Configuration;
import com.garbagemule.MobArena.Arena;
import com.garbagemule.MobArena.ArenaLog;
import com.garbagemule.MobArena.ArenaPlayer;
import com.garbagemule.MobArena.MAUtils;
import com.garbagemule.MobArena.MobArena;
import com.garbagemule.MobArena.util.FileUtils;
public class Totals
{
private static final String totals_yml = "totals.yml";
/**
* Update the totals-file with the session data from the ArenaLog object
* @param log ArenaLog to update with
*/
public static void updateArenaTotals(ArenaLog log)
{
// Grab the configuration
Configuration totals = getArenaTotals(log.arena);
totals.load();
// General data
updateInt(totals, "general-info.total-games-played", 1, true);
updateInt(totals, "general-info.most-players", log.players.keySet().size(), false);
updateInt(totals, "general-info.highest-wave-reached", log.lastWave, false);
updateInt(totals, "general-info.total-monsters-killed", countKills(log), true);
updateDuration(totals, "general-info.total-duration", log.getDurationLong(), true);
updateDuration(totals, "general-info.longest-session-duration", log.getDurationLong(), false);
// Classes
updateInt(totals, "classes.overall-distribution.total-count", log.players.keySet().size(), true);
for (String c : log.arena.getClasses())
{
// Array {kills, dmgDone, dmgTaken}
int[] a = getKillsAndDamageByClass(log, c);
updateInt(totals,"classes." + c + ".kills", a[0],true);
updateInt(totals,"classes." + c + ".damage-done", a[1],true);
updateInt(totals,"classes." + c + ".damage-taken",a[2],true);
}
// Rewards
// Players
for (ArenaPlayer ap : log.players.values())
{
// Basic values
updateInt(totals,"players." + ap.player.getName() + ".games-played", 1, true);
updateInt(totals,"players." + ap.player.getName() + ".kills", ap.kills, true);
updateInt(totals,"players." + ap.player.getName() + ".damage-done", ap.dmgDone, true);
updateInt(totals,"players." + ap.player.getName() + ".damage-taken", ap.dmgTaken, true);
updateInt(totals,"players." + ap.player.getName() + ".swings", ap.swings, true);
updateInt(totals,"players." + ap.player.getName() + ".hits", ap.hits, true);
// Class count
updateInt(totals,"players." + ap.player.getName() + ".classes." + ap.className,1,true);
}
// Save everything
totals.save();
}
/**
* Get or create a Configuration from the totals-file
* @param arena Arena to get or create a Configuration for
* @return Configuration from the arena's totals-file
*/
public static Configuration getArenaTotals(Arena arena)
{
// Create the folder if it doesn't exist.
File dir = new File(MobArena.arenaDir, arena.configName());
if (!dir.exists()) dir.mkdirs();
File file = new File(dir, totals_yml);
if (!file.exists()) FileUtils.extractFile(dir, totals_yml);
// Grab the totals-file and return the Configuration
return new Configuration(file);
}
/**
* Update an integer in a config-file
* @param totals Configuration to alter
* @param node Node to alter
* @param b Integer for comparison
* @param increment If true, the node will be incremented by b, otherwise overwritten if greater
*/
private static void updateInt(Configuration totals, String node, int b, boolean increment)
{
int a = totals.getInt(node, 0);
if (increment) totals.setProperty(node, a+b);
else if (b > a) totals.setProperty(node, b);
}
/**
* Update a duration in a config-file
* @param totals Configuration to alter
* @param node Node to alter
* @param b Duration for comparison. This is a java.sql.Timestamp.getTime() long
* @param increment If true, the node will be incremented by b, otherwise overwritten if greater
*/
private static void updateDuration(Configuration totals, String node, long b, boolean increment)
{
long a = MAUtils.parseDuration(totals.getString(node, "0:00:00"));
if (increment) totals.setProperty(node, MAUtils.getDuration(a+b));
else if (b > a) totals.setProperty(node, MAUtils.getDuration(b));
}
/**
* Get total kills from an arena session
* @param log The ArenaLog to count kills in
* @return Total kills
*/
private static int countKills(ArenaLog log)
{
int kills = 0;
for (ArenaPlayer ap : log.players.values())
kills += ap.kills;
return kills;
}
/*
private static int getKillsByClass(ArenaLog log, String className)
{
int kills = 0;
for (ArenaPlayer ap : log.players.values())
if (ap.className.equals(className))
kills += ap.kills;
return kills;
}
*/
/**
* Get a (dirty) int-array in the form {kills, damage done, damage taken} for a class
* @param log ArenaLog to count kills, damage done/taken from
* @param className The class to count for
* @return Resulting kills, damage done and damage taken in an int-array
*/
private static int[] getKillsAndDamageByClass(ArenaLog log, String className)
{
int kills = 0, dmgDone = 0, dmgTaken = 0;
for (ArenaPlayer ap : log.players.values())
{
if (!ap.className.equals(className))
continue;
kills += ap.kills;
dmgDone += ap.dmgDone;
dmgTaken += ap.dmgTaken;
}
return new int[]{kills, dmgDone, dmgTaken};
}
}

View File

@ -0,0 +1,6 @@
package com.garbagemule.MobArena.util.data;
public class XML
{
}

View File

@ -0,0 +1,90 @@
package com.garbagemule.MobArena.waves;
public abstract class AbstractWave implements Wave
{
private String name;
private int wave, frequency, priority;
private WaveType type;
private WaveGrowth growth;
/**
* Basic wave constructor.
* Constructs a wave with an initial wave number, a wave frequency, and
* a wave priority.
* @param wave Initial wave number. This is the first wave number this wave can spawn at.
* @param frequency How often the wave can spawn.
* @param priority The priority of the wave.
*/
public AbstractWave(String name, int wave, int frequency, int priority)
{
this.name = name;
this.wave = wave;
this.frequency = frequency;
this.priority = priority;
}
/**
* Default wave constructor.
* Constructs a basic wave with additional information in the type of
* wave and the wave growth.
* @param wave Initial wave number. This is the first wave number this wave can spawn at.
* @param frequency How often the wave can spawn.
* @param priority The priority of the wave.
* @param type The type of wave.
* @param growth The growth rate of the wave.
*/
public AbstractWave(String name, int wave, int frequency, int priority, WaveType type, WaveGrowth growth)
{
this(name, wave, frequency, priority);
this.type = type;
this.growth = growth;
}
public WaveType getType()
{
return type;
}
public void setType(WaveType type)
{
this.type = type;
}
public WaveGrowth getGrowth()
{
return growth;
}
public void setGrowth(WaveGrowth growth)
{
this.growth = growth;
}
public int getWave()
{
return wave;
}
public int getFrequency()
{
return frequency;
}
public int getPriority()
{
return priority;
}
public String getName()
{
return name;
}
public String toString()
{
return "[name=" + name +
", wave=" + wave +
", frequency=" + frequency +
", priority=" + priority + "]";
}
}

View File

@ -0,0 +1,35 @@
package com.garbagemule.MobArena.waves;
import java.util.Set;
import org.bukkit.entity.Creature;
import com.garbagemule.MobArena.waves.Wave.BossAbility;
public class BossWave
{
private Creature boss;
private Set<BossAbility> abilities;
private Set<Creature> adds;
private int health;
public BossWave(Creature boss)
{
this.boss = boss;
}
public void addAbility(BossAbility ability)
{
abilities.add(ability);
}
public void addAdds(Creature creature)
{
adds.add(creature);
}
public void setHealth(int health)
{
this.health = health;
}
}

View File

@ -0,0 +1,47 @@
package com.garbagemule.MobArena.waves;
import java.util.Collection;
import org.bukkit.Location;
public class RecurrentWave extends AbstractWave
{
public RecurrentWave(String name, int wave, int frequency, int priority)
{
super(name, wave, frequency, priority);
}
public RecurrentWave(String name, int wave, int frequency, int priority, WaveType type, WaveGrowth growth)
{
super(name, wave, frequency, priority, type, growth);
}
public void spawn(int wave, Collection<Location> spawnpoints)
{
}
public void setDefault(boolean def)
{
}
public boolean matches(int wave)
{
return wave % getWave() + getFrequency() == 0;
}
/**
* Recurrent waves are sorted by their priorities.
* If the priorities are equal, the names are compared. This is to
* ALLOW "duplicates" in the RECURRENT WAVES collection.
*/
public int compareTo(Wave w)
{
if (getPriority() < w.getPriority())
return -1;
else if (getPriority() > w.getPriority())
return 1;
else return getName().compareTo(w.getName());
}
}

View File

@ -0,0 +1,37 @@
package com.garbagemule.MobArena.waves;
import java.util.Collection;
import org.bukkit.Location;
public class SingleWave extends AbstractWave
{
public SingleWave(String name, int wave)
{
super(name, wave, 0, 0);
}
public void spawn(int wave, Collection<Location> spawnpoints)
{
}
public boolean matches(int wave)
{
return getWave() == wave;
}
/**
* Single waves are compared by their wave numbers.
* If the wave numbers are equal, the waves are equal. This is to
* DISALLOW "duplicates" in the SINGLE WAVES collection.
*/
public int compareTo(Wave w)
{
if (this.getWave() < w.getWave())
return -1;
else if (this.getWave() > w.getWave())
return 1;
else return 0;
}
}

View File

@ -0,0 +1,119 @@
package com.garbagemule.MobArena.waves;
import java.util.Collection;
import org.bukkit.Location;
import com.garbagemule.MobArena.util.WaveUtils;
public interface Wave extends Comparable<Wave>
{
public enum WaveBranch
{
SINGLE, RECURRENT;
}
public enum WaveType
{
DEFAULT, SPECIAL, SWARM, BOSS;
public static WaveType fromString(String string)
{
return WaveUtils.getEnumFromString(WaveType.class, string, DEFAULT);
}
}
public enum WaveGrowth
{
SLOW, MEDIUM, FAST;
public static WaveGrowth fromString(String string)
{
return WaveUtils.getEnumFromString(WaveGrowth.class, string, MEDIUM);
}
}
public enum BossAbility
{
ARROWS, FIREBALLS, RING_OF_FIRE;
public static BossAbility fromString(String string)
{
return WaveUtils.getEnumFromString(BossAbility.class, string);
}
}
public enum BossHealth
{
LOW, MEDIUM, HIGH;
public static BossHealth fromString(String string)
{
return WaveUtils.getEnumFromString(BossHealth.class, string);
}
}
public enum SwarmAmount
{
LOW, MEDIUM, HIGH, PSYCHO;
public static SwarmAmount fromString(String string)
{
return WaveUtils.getEnumFromString(SwarmAmount.class, string);
}
}
/**
* The spawn() method must spawn one or more monsters in
* the arena. The monster count, damage, health, etc. can
* be modified by the wave parameter.
* @param wave Wave number
*/
public void spawn(int wave, Collection<Location> spawnpoints);
/**
* Get the type of wave.
* @return The WaveType of this Wave.
*/
public WaveType getType();
/**
* Get the growth rate of this wave.
* @return The growth rate
*/
public WaveGrowth getGrowth();
/**
* Get the first wave number for this wave
* @return The wave number
*/
public int getWave();
/**
* Get the wave's frequency, i.e. wave number "modulo"
* @return The wave's frequency
*/
public int getFrequency();
/**
* Get the wave's priority value.
* @return The priority
*/
public int getPriority();
/**
* Get the wave's name.
* @return The name
*/
public String getName();
/**
* Check if this wave matches the wave number.
* The SingleWave class does a simple check if its wave == the parameter.
* The RecurrentWave class is more complex in that it needs to do some
* calculations based on the initial wave and the frequency.
* @param wave The current wave number
* @return true, if the wave should spawn, false otherwise
*/
public boolean matches(int wave);
}