diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..695b0e9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+# We don't want to add the Eclipse files.
+.project
+.classpath
+
+# We can also do without .class files.
+*.class
+
+# And we definitely don't need OS files.
+.DS_Store*
+Thumbs.db
+
+# Exclude some folders
+bin/
+build/
+misc/
+lib/
+test/
+.settings/
\ No newline at end of file
diff --git a/MobArena.jar b/MobArena.jar
index 3d29c79..1ec76a8 100644
Binary files a/MobArena.jar and b/MobArena.jar differ
diff --git a/README b/README.md
similarity index 79%
rename from README
rename to README.md
index 082487a..f2765b7 100644
--- a/README
+++ b/README.md
@@ -1,3 +1 @@
-(Project is currently on hold until I can find more time.)
-
Please read the [Wiki](https://github.com/garbagemule/MobArena/wiki/Installing-MobArena) for details on how to set up MobArena, and remember to read the [FAQ](https://github.com/garbagemule/MobArena/wiki/FAQ) before posting questions!
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..5dbcaa3
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/language files/cz/announcements.properties b/language files/cz/announcements.properties
new file mode 100644
index 0000000..a2e961b
--- /dev/null
+++ b/language files/cz/announcements.properties
@@ -0,0 +1,58 @@
+ARENA_START=Necht zacne masakr!|Arena zacala!
+ARENA_END=Arena skoncila.|Arena skoncila.
+ARENA_DOES_NOT_EXIST=Takova arena neexistuje. Napis /ma arenas pro seznam aren.|Arena nesla najit.
+JOIN_NOT_ENABLED=Areny jsou v tuto chvili vypnute.|Areny nejsou zapnute.
+JOIN_IN_OTHER_ARENA=Uz jsi v arene! Musis tuhle opustit nez pujdes do jine.|Jsi v jine arene.
+JOIN_ARENA_NOT_ENABLED=Tato arena neni dostupna.|Tato arena je vypnuta.
+JOIN_ARENA_NOT_SETUP=Tato arena jeste nebyla nastavena.|Arena neni nastavena.
+JOIN_ARENA_EDIT_MODE=Tato arena se zrovna upravuje.|Arena se upravuje.
+JOIN_ARENA_PERMISSION=Nemas opravneni ke vstupu do teto areny.|K tomuhle nemas opravneni!
+JOIN_FEE_REQUIRED=Insufficient funds. Price: %|Price: %
+JOIN_FEE_PAID=Price to join was: %|Paid: %
+JOIN_ARENA_IS_RUNNING=Tato arena uz zacala, musis pockat az skonci.|Arena uz probiha, zkus to pozdeji!
+JOIN_ALREADY_PLAYING=Uz hrajes!|Uz hrajes!
+JOIN_ARG_NEEDED=Musis upresnit arenu. Napis /ma arenas pro seznam aren.
+JOIN_TOO_FAR=YJsi prilis daleko od areny aby ses mohl pripojit/sledovat.|Prilis daleko od areny.
+JOIN_EMPTY_INV=Musis si nejdriv vyprazdnit inventar.|Vyprazdni si inventar.
+JOIN_PLAYER_LIMIT_REACHED=V arene je uz maximum hracu.|Uz nejsou volna mista.
+JOIN_STORE_INV_FAIL=Tvuj inventar se nepodarilo ulozit. Zkus to znovu.
+JOIN_EXISTING_INV_RESTORED=Tvuj inventar byl navracen.
+JOIN_PLAYER_JOINED=Pridal ses do areny. Uzi si to!|Pridal ses do areny.
+LEAVE_NOT_PLAYING=Nejsi v arene.|Nejsi v arene.
+LEAVE_PLAYER_LEFT=Opustils arenu. Diky za boj!|Opustils arenu.
+PLAYER_DIED=% zemrel!|% padl v boji!
+SPEC_PLAYER_SPECTATE=Uzi si show!|Uzi si podivanou!
+SPEC_NOT_RUNNING=Tato arena neprobiha.|Arena neprobiha.
+SPEC_ARG_NEEDED=Musis upresnit arenu. Napis /ma arenas pro seznam aren.|Je pozadovano jmeno areny.
+SPEC_EMPTY_INV=Musis si nejdriv vyprazdnit inventar.|Vyprazdni si inventar.
+SPEC_ALREADY_PLAYING=Nemuzes byt divakem kdyz jsi bojovnikem!|Vzdyt uz bojujes!
+NOT_READY_PLAYERS=Nepripraveni hraci: %
+FORCE_START_RUNNING=Arena uz zacala.
+FORCE_START_NOT_READY=Boj nemuze zacit, ani jeden hrac neni pripraveny.
+FORCE_START_STARTED=Arena zacala, at se vam to libi nebo ne.
+FORCE_END_EMPTY=V arene nikdo neni.
+FORCE_END_ENDED=Arena ukoncena.
+FORCE_END_IDLE=Nebyl jsi dost rychly!
+REWARDS_GIVE=Tady jsou vsechyn tve odmeny!
+LOBBY_DROP_ITEM=Ne ne ne, tady se o veci delit nesmi!|Veci se tu hazet nesmi.
+LOBBY_PLAYER_READY=Jsi pripraven!|Jsi pripraven!
+LOBBY_PICK_CLASS=Nejdriv si vyber typ bojovnika!|Vyber si typ bojovnika!
+LOBBY_NOT_ENOUGH_PLAYERS=Tak malo bojovniku do areny nemuze. Je potreba alespon % hracu.|Je potreba vice bojovniku!
+LOBBY_RIGHT_CLICK=Klikni levym, ne pravym.|Klikni levym.
+LOBBY_CLASS_PICKED=Rozhodl ses, ze budes %!|%
+LOBBY_CLASS_RANDOM=Na zacatku boje ti bude nahodne pridelen typ bojovnika.
+LOBBY_CLASS_PERMISSION=Nemas opravneni byt timto typem bojovnika!|Smula, ale k tomu nemas opravneni!
+WARP_TO_ARENA=Nemuzes se objevit v arene uprostred boje!
+WARP_FROM_ARENA=Warping not allowed in the arena!
+Vlna_DEFAULT=Vlna #%!|Vlna #%!
+Vlna_SPECIAL=Vlna #%! [SPECIAL]|Vlna #%! [SPECIAL]
+Vlna_SWARM=Vlna #%! [SWARM]|Vlna #%! [SWARM]
+Vlna_BOSS=Vlna #%! [BOSS]|Vlna #%! [BOSS]
+Vlna_BOSS_ABILITY=Boss pouzil schopnost: %!|Boss: %
+Vlna_BOSS_LOW_HEALTH=Boss je temer mrtvy!|Bossovi uz nezbyva moc zivota!
+Vlna_REWARD=Ziskal jsi tuto odmenu: %|Byl jsi odmenen: %
+MISC_LIST_PLAYERS=Bojovnici stale na nohou: %
+MISC_LIST_ARENAS=Dostupne areny: %
+MISC_COMMAND_NOT_ALLOWED=Tenhle prikaz v arene pouzit nemuzes!
+MISC_NO_ACCESS=K tomuto prikazu nemas pristup.
+MISC_NONE=
diff --git a/language files/da/announcements.properties b/language files/da/announcements.properties
new file mode 100644
index 0000000..42d61f2
--- /dev/null
+++ b/language files/da/announcements.properties
@@ -0,0 +1,51 @@
+ARENA_START=Lad massakren begynde!
+ARENA_END=Arena slut.
+ARENA_DOES_NOT_EXIST=Arenaen findes ikke. Skriv /ma arenas for en liste.
+JOIN_PLAYER_JOINED=Du joinede arenaen!
+JOIN_NOT_ENABLED=MobArena er ikke aktiveret.
+JOIN_IN_OTHER_ARENA=Du er allerede i en arena. Forlad den først.
+JOIN_ARENA_NOT_ENABLED=Arenaen er deaktiveret.
+JOIN_ARENA_NOT_SETUP=Arenaen er ikke blevet sat op endnu.
+JOIN_ARENA_PERMISSION=Du har ikke rettigheder til den arena!
+JOIN_FEE_REQUIRED=Du er for fattig. Pris: %
+JOIN_FEE_PAID=Prisen for at joine var: %
+JOIN_ARENA_IS_RUNNING=Arenaen er allerede startet.
+JOIN_ALREADY_PLAYING=Du spiller allerede!
+JOIN_ARG_NEEDED=Arenanavn påkrævet. Skriv /ma arenas for en liste.
+JOIN_TOO_FAR=Du er for langt fra denne arena til at kunne joine.
+JOIN_EMPTY_INV=Du skal tømme dit inventory før du kan joine arenaen.
+JOIN_PLAYER_LIMIT_REACHED=Der kan ikke være flere spillere i arenaen.
+JOIN_STORE_INV_FAIL=Kunne ikke gemme dit inventory. Prøv igen.
+LEAVE_PLAYER_LEFT=Du forlod arenaen.
+LEAVE_NOT_PLAYING=Du er ikke i en arena.
+PLAYER_DIED=% døde!
+SPEC_PLAYER_SPECTATE=Nyd forestillingen!
+SPEC_NOT_RUNNING=Arenaen er ikke startet.
+SPEC_ARG_NEEDED=Arenanavn påkrævet. Skriv /ma arenas for en liste.
+SPEC_EMPTY_INV=Du skal tømme dit inventory først.
+SPEC_ALREADY_PLAYING=Du spiller allerede!
+NOT_READY_PLAYERS=Ikke klar: %
+FORCE_START_STARTED=Arena tvunget til at starte.
+FORCE_START_RUNNING=Arenaen er allerede startet.
+FORCE_START_NOT_READY=Ingen spillere er klar.
+FORCE_END_ENDED=Arena tvunget til at slutte.
+FORCE_END_EMPTY=Der er ingen i arenaen.
+FORCE_END_IDLE=Du var ikke hurtig nok!
+REWARDS_GIVE=Her er alle dine præmier!
+LOBBY_CLASS_PICKED=Du har valgt % som din klasse!
+LOBBY_CLASS_RANDOM=Du får tildelt en klasse ved arena start.
+LOBBY_CLASS_PERMISSION=Du har ikke rettigheder til den klasse!
+LOBBY_PLAYER_READY=Du er markeret som klar!
+LOBBY_DROP_ITEM=Du kan ikke dele items i lobbyen!
+LOBBY_PICK_CLASS=Du skal først vælge en klasse!
+LOBBY_RIGHT_CLICK=Slå skiltene; ikke højre-klik.
+WARP_TO_ARENA=Du kan ikke warpe til arenaen efter, den er startet.
+WARP_FROM_ARENA=Du kan ikke warpe fra arenaen.
+WAVE_DEFAULT=Bølge #%!
+WAVE_SPECIAL=Bølge #%! [SPECIEL]
+WAVE_REWARD=Du har lige vundet en præmie: %
+MISC_LIST_ARENAS=Tilgængelige arenaer: %
+MISC_LIST_PLAYERS=Levende spillere: %
+MISC_COMMAND_NOT_ALLOWED=Du kan ikke bruge den kommando i arenaen.
+MISC_NO_ACCESS=Du har ikke adgang til den kommando.
+MISC_NONE=
diff --git a/language files/de/announcements.properties b/language files/de/announcements.properties
new file mode 100644
index 0000000..04993f3
--- /dev/null
+++ b/language files/de/announcements.properties
@@ -0,0 +1,53 @@
+ARENA_START=Lasst die Spiele beginnen
+ARENA_END=Arena beendet
+ARENA_DOES_NOT_EXIST=Diese Arena existiert nicht tippe /ma um alle Arenen anzuzeigen.
+JOIN_PLAYER_JOINED=Du bist nun in der Arena.
+Viel Spass!
+JOIN_NOT_ENABLED=Die Mobarena wurde deaktiviert
+JOIN_IN_OTHER_ARENA=Du bist momentan in einer Arena, verlasse diese jetzt.
+JOIN_ARENA_NOT_ENABLED=Diese Arena ist nicht aktiviert.
+JOIN_ARENA_NOT_SETUP=Diese Arena wurde noch nicht eingerichtet.
+JOIN_ARENA_PERMISSION=Du bist nicht berechtigt diese Arena zu betretten.
+JOIN_FEE_REQUIRED=Ungenügender Kontostand. Preis: %
+JOIN_FEE_PAID=Der Preis um bei zu tretten: %
+JOIN_ARENA_IS_RUNNING=Der Kampf in dieser Arena ist bereits zu gange.
+JOIN_ALREADY_PLAYING=Du spielst bereits!
+JOIN_ARG_NEEDED=Du must den Namen der Arena eingeben. Gib /ma arenas ein für eine Liste.
+JOIN_TOO_FAR=Du bist zu weit weg um bei zu tretten oder zu zuschauen.
+JOIN_EMPTY_INV=Du must dein Inventar leeren um der Arena bei zu tretten.
+JOIN_PLAYER_LIMIT_REACHED=Die Spieler Limite ist erreicht.
+JOIN_STORE_INV_FAIL=Momentan nicht möglich dein Inventar zu speichern. Probiere es noch einmal.
+LEAVE_PLAYER_LEFT=Du hast die Arena verlassen. Vielen Dank fürs Spielen!
+LEAVE_NOT_PLAYING=Du kannst die Arena nicht verlassen wenn du nich in der Arena bist! Du Depp.
+PLAYER_DIED=% ist gestorben!
+SPEC_PLAYER_SPECTATE=Amüsier dich am Kampf!
+SPEC_NOT_RUNNING=Diese Arena läuft im Moment nicht.
+SPEC_ARG_NEEDED=Du must den Namen der Arena eingeben. Gib /ma arenas ein für eine Liste.
+SPEC_EMPTY_INV=Du must dein Inventar leeren um der Arena bei zu tretten!
+SPEC_ALREADY_PLAYING=Du kannst dir nicht selber zuschauen wie du kämpfst!
+NOT_READY_PLAYERS=Noch nicht bereit sind: %
+FORCE_START_STARTED=Gezwungener Start des Kampfes.
+FORCE_START_RUNNING=Die Arena wurde bereits gestartet.
+FORCE_START_NOT_READY=Kann den Start des Kampfes nicht erzwingen, keine Spieler vorhanden.
+FORCE_END_ENDED=Gezwunges Kampf Ende.
+FORCE_END_EMPTY=Niemand ist in der Arena.
+FORCE_END_IDLE=Du warst nicht schnell genug!
+REWARDS_GIVE=Hier sind deine Preise!
+LOBBY_CLASS_PICKED=Du hast % als deine Kampfklasse gewählt!
+LOBBY_CLASS_RANDOM=Du wirst beim Start des Spiels einer zufälligen Kampfklasse zugeortnet.
+LOBBY_CLASS_PERMISSION=Du hast keine Berechtigung diese Kampfklasse zu wählen!
+LOBBY_NOT_ENOUGH_PLAYERS=Nicht genung Spieler um den Kampf zu starten. Es benötigt mindestens % Spieler.
+LOBBY_PLAYER_READY=Du hast dich als bereit gemeldet!
+LOBBY_DROP_ITEM=Du darfts momentant deine Tools nicht an andere weitergeben!
+LOBBY_PICK_CLASS=Du must zuerst eine Kampfklasse auswählen!
+LOBBY_RIGHT_CLICK=Du must das Schild schlagen. Nicht rechtsklicken.
+WARP_TO_ARENA=Du kannst dich jetzt nich in die Arena teleportieren!
+WARP_FROM_ARENA=Du darfst dich jetzt nicht aus der Arena teleportiern!
+WAVE_DEFAULT=Sei bereit für die %. Welle!
+WAVE_SPECIAL=Mach dich für eine spezielle %. Welle bereit!
+WAVE_REWARD=Du hast einen Preis: %
+MISC_LIST_ARENAS=Verfügbare Arenas: %
+MISC_LIST_PLAYERS=Live Spieler: %
+MISC_COMMAND_NOT_ALLOWED=Du darfst diesen Befehl nicht in der Arena benutzen!
+MISC_NO_ACCESS=Du hast keinen Zugriff auf diesen Befehl.
+MISC_NONE=
diff --git a/language files/fr/announcements.properties b/language files/fr/announcements.properties
new file mode 100644
index 0000000..6f2b9d3
--- /dev/null
+++ b/language files/fr/announcements.properties
@@ -0,0 +1,51 @@
+ARENA_START=Que le massacre commence !
+ARENA_END=L'arène est terminée.
+ARENA_DOES_NOT_EXIST=Cette arène n'existe pas. Entrez "/ma arenas" pour la liste.
+JOIN_PLAYER_JOINED=Vous avez rejoint l'arène. Prennez plaisir !
+JOIN_NOT_ENABLED=MobArena n'est pas actif.
+JOIN_IN_OTHER_ARENA=Vous êtes déjà dans l'arène! Quittez-la avant.
+JOIN_ARENA_NOT_ENABLED=Cetta arène n'est pas active.
+JOIN_ARENA_NOT_SETUP=Cette arène n'a pas encore été configurée.
+JOIN_ARENA_PERMISSION=Vous n'avez pas la permission pour rejoindre cette arène.
+JOIN_FEE_REQUIRED=Pas assez d'argent. Prix: %
+JOIN_FEE_PAID=Le prix pour rejoindre était de: %
+JOIN_ARENA_IS_RUNNING=L'arène est déjà occupée.
+JOIN_ALREADY_PLAYING=Vous êtes déjà en train de jouer.
+JOIN_ARG_NEEDED=Vous devez spécifier une arène. Entrez "/ma arenas" pour la liste.
+JOIN_TOO_FAR=Vous êtes trop loins de l'arène pour pouvoir joindre en tant que joueur ou specateur.
+JOIN_EMPTY_INV=Vous devez vider votre inventaire afin de joindre l'arène.
+JOIN_PLAYER_LIMIT_REACHED=La limite de joueur pour cette arène a été atteinte.
+JOIN_STORE_INV_FAIL=La sauvegarde de l'inventaire a échouée. Essayez encore.
+LEAVE_PLAYER_LEFT=Vous avez quitté l'arène. Merci d'avoir joué !
+LEAVE_NOT_PLAYING=Vous n'êtes pas dans l'arène.
+PLAYER_DIED=% est mort !
+SPEC_PLAYER_SPECTATE=Profitez bien du spectacle !
+SPEC_NOT_RUNNING=Cette arène n'est pas en cours.
+SPEC_ARG_NEEDED=Vous devez spécifier une arène. Entrez "/ma arenas" pour la liste.
+SPEC_EMPTY_INV=Vous devez vider votre inventaire.
+SPEC_ALREADY_PLAYING=Vous ne pouvez pas être spectateur en tant que joueur de l'arène !
+NOT_READY_PLAYERS=Non prêt: %
+FORCE_START_STARTED=Début forcé de l'arène.
+FORCE_START_RUNNING=L'arène a commencée.
+FORCE_START_NOT_READY=Ne peut pas forcer le début, il n'y a pas de joueurs prêts.
+FORCE_END_ENDED=Fin forcée de l'arène.
+FORCE_END_EMPTY=Il n'y a personne dans l'arène.
+FORCE_END_IDLE=Vous n'avez pas été assez rapide !
+REWARDS_GIVE=Voici vos récompenses !
+LOBBY_CLASS_PICKED=Vous avez choisi % comme classe !
+LOBBY_CLASS_RANDOM=Une classe aléatoire va vous être attribuée.
+LOBBY_CLASS_PERMISSION=Vous n'avez pas les permissions pour utiliser cette classe !
+LOBBY_PLAYER_READY=Vous avez été marqué comme prêt !
+LOBBY_DROP_ITEM=Pas de partage avant le début de l'arène !
+LOBBY_PICK_CLASS=Vous devez d'abord choisir une classe !
+LOBBY_RIGHT_CLICK=Vous devez frapper le panneau, pas cliquer du droit dessus.
+WARP_TO_ARENA=Vous ne pouvez pas vous téléporter à l'arène pendant la bataille !
+WARP_FROM_ARENA=La téléportation est interdite dans l'arène !
+WAVE_DEFAULT=Soyez prêt pour la vague #% !
+WAVE_SPECIAL=Soyez prêt pour la vague #% ! [SPECIALE]
+WAVE_REWARD=Vous avez gagné une récompense: %
+MISC_LIST_ARENAS=Les arènes disponibles: %
+MISC_LIST_PLAYERS=Les joueurs en live: %
+MISC_COMMAND_NOT_ALLOWED=Vous ne pouvez pas utiliser cette commande dans l'arène !
+MISC_NO_ACCESS=Vous n'avez pas accès à cette commande.
+MISC_NONE=
diff --git a/language files/nl/announcements.properties b/language files/nl/announcements.properties
new file mode 100644
index 0000000..64b86b5
--- /dev/null
+++ b/language files/nl/announcements.properties
@@ -0,0 +1,52 @@
+ARENA_START=Laat de slachting beginnen!
+ARENA_END=Arena voltooid.
+ARENA_DOES_NOT_EXIST=Die arena bestaat niet. Typ /ma arenas voor een lijst.
+JOIN_PLAYER_JOINED=U heeft toegetreden tot deze arena. Veel plezier!
+JOIN_NOT_ENABLED=MobArena staat niet aan.
+JOIN_IN_OTHER_ARENA=U bevindt zich al in een arena! Vertaal die eerst.
+JOIN_ARENA_NOT_ENABLED=Deze arena staat niet aan.
+JOIN_ARENA_PERMISSION=U heeft geen permissie om deze arena toe te treden.
+JOIN_ARENA_NOT_SETUP=Deze arena is nog niet ingesteld.
+JOIN_FEE_REQUIRED=Onvoldoende geld. Prijs: %
+JOIN_FEE_PAID=Prijs om toe te treden is: %
+JOIN_ARENA_IS_RUNNING=Deze arena is al bezig.
+JOIN_ALREADY_PLAYING=U bent al aan het spelen!
+JOIN_ARG_NEEDED=U moet een arena specificeren. Typ /ma arenas voor een lijst.
+JOIN_TOO_FAR=U bent te ver weg van de arena om toe te treden/toe te kijken.
+JOIN_EMPTY_INV=U moet uw inventaris eerst legen om toe te treden tot deze arena.
+JOIN_PLAYER_LIMIT_REACHED=De speler limiet van deze arena is berijkt.
+JOIN_STORE_INV_FAIL=Inventaris opslaan mislukt. Probeer het opnieuw.
+LEAVE_PLAYER_LEFT=U verliet de arena. Bedankt voor het spelen!
+LEAVE_NOT_PLAYING=U bent niet in de arena.
+PLAYER_DIED=% ging dood!
+SPEC_PLAYER_SPECTATE=Geniet van de show!
+SPEC_NOT_RUNNING=Deze arena is niet actief.
+SPEC_ARG_NEEDED=U moet een arena specificeren. Typ /ma arenas voor een lijst.
+SPEC_EMPTY_INV=Leeg uw inventaris eerst!
+SPEC_ALREADY_PLAYING=Kan niet toekijken als je in de arena zit!
+NOT_READY_PLAYERS=Niet klaar: %
+FORCE_START_STARTED=Geforceerd arena te starten.
+FORCE_START_RUNNING=Arena is al begonnen.
+FORCE_START_NOT_READY=Kan arena niet geforceerd starten, geen spelers zijn klaar.
+FORCE_END_ENDED=Geforceerd arena te beëindigen.
+FORCE_END_EMPTY=Niemand is in deze arena.
+FORCE_END_IDLE=U was niet snel genoeg!
+REWARDS_GIVE=Hier zijn al uw beloningen!
+LOBBY_CLASS_PICKED=U heeft % gekozen als uw klasse!
+LOBBY_CLASS_RANDOM=U krijgt een willekeurige klasse als de arena start.
+LOBBY_CLASS_PERMISSION=U heeft geen permissie om deze klasse te gebruiken!
+LOBBY_NOT_ENOUGH_PLAYERS=Niet genoeg spelers om te beginnen. Op zijn minst % spelers nodig.
+LOBBY_PLAYER_READY=U bent gemarkeerd als klaar!
+LOBBY_DROP_ITEM=Delen niet toegestaan op dit moment!
+LOBBY_PICK_CLASS=U moet eerst een klasse kiezen!
+LOBBY_RIGHT_CLICK=Sla het bord. Geen klik met de rechtermuisknop.
+WARP_TO_ARENA=Kan niet teleporteren naar een arena tijdens een gevecht!
+WARP_FROM_ARENA=Teleporteren niet toegestaan in de arena!
+WAVE_DEFAULT=Maak U klaar voor golf #%!
+WAVE_SPECIAL=Maak U klaar voor golf #%! [SPECIAAL]
+WAVE_REWARD=U heeft zojuist een beloning verdiend: %
+MISC_LIST_ARENAS=Beschikbare arenas: %
+MISC_LIST_PLAYERS=Live spelers: %
+MISC_COMMAND_NOT_ALLOWED=U kan dit commando niet gebruiken in de arena!
+MISC_NO_ACCESS=U heeft geen toegang tot dit commando.
+MISC_NONE=
\ No newline at end of file
diff --git a/language files/pirate/announcements.properties b/language files/pirate/announcements.properties
new file mode 100644
index 0000000..0afaf05
--- /dev/null
+++ b/language files/pirate/announcements.properties
@@ -0,0 +1,51 @@
+ARENA_START=Hearts alive, men!
+ARENA_END=Land ho!
+ARENA_DOES_NOT_EXIST=Thar be no ship by that name!
+JOIN_PLAYER_JOINED=Ahoy, matey!
+JOIN_NOT_ENABLED=We be swabbing down the decks on all ships, matey.
+JOIN_IN_OTHER_ARENA=Ye're already at sea, scallywag!
+JOIN_ARENA_NOT_ENABLED=We be swabbing down the decks on this ship, matey.
+JOIN_ARENA_NOT_SETUP=Arrrgh! We be missing the top sail!
+JOIN_ARENA_PERMISSION=Arrgh! Ye can't board this ship, scurvy dog!
+JOIN_FEE_REQUIRED=Ye don't own enough doubloons, matey! Price: %
+JOIN_FEE_PAID=Booty ye gave up to join: %
+JOIN_ARENA_IS_RUNNING=That ship has sailed, landlubber!
+JOIN_ALREADY_PLAYING=Ye be aboard already, ye scurvy dog!
+JOIN_ARG_NEEDED=Which ship are ye boarding? Type /ma arenas for a list, arrgh!
+JOIN_TOO_FAR=Ye be too far from the docks, landlubber!
+JOIN_EMPTY_INV=Throw ye belongings overboard, scallywag!
+JOIN_PLAYER_LIMIT_REACHED=Thar be too many men aboard this ship already, landlubber!
+JOIN_STORE_INV_FAIL=Ye belongings didn't fit in the storage deck, arrgh!
+LEAVE_PLAYER_LEFT=Ye abandonned the ship! Arrrgh!
+LEAVE_NOT_PLAYING=Ye're already on land, matey!
+PLAYER_DIED=% walked the plank!
+SPEC_PLAYER_SPECTATE=Welcome to the Crow's Nest! Grab ye spyglass!
+SPEC_NOT_RUNNING=Thar be no one aboard that ship!
+SPEC_ARG_NEEDED=Which ship are ye boarding? Type /ma arenas for a list, arrgh!
+SPEC_EMPTY_INV=Throw ye belongings overboard, scallywag!
+SPEC_ALREADY_PLAYING=Ye don't need ye spyglass aboard this ship, harr!
+NOT_READY_PLAYERS=Landlubbers: %
+FORCE_START_STARTED=Set sail!
+FORCE_START_RUNNING=The ship has already set sail!
+FORCE_START_NOT_READY=Thar be no scurvy dogs ready to set sail!
+FORCE_END_ENDED=Forced the men to walk the plank!
+FORCE_END_EMPTY=Thar be no one aboard the ship!
+FORCE_END_IDLE=Arrrgh! Too slow, matey!
+REWARDS_GIVE=Here's ye booty, seadog!
+LOBBY_CLASS_PICKED=Ye be the %!
+LOBBY_CLASS_RANDOM=
+LOBBY_CLASS_PERMISSION=Ye can't use that cutlass, scurvy dog!
+LOBBY_PLAYER_READY=Ready to sail! Arrrgh!
+LOBBY_DROP_ITEM=Don't be sharing ye rum yet, scallywag!
+LOBBY_PICK_CLASS=Grab ye cutlass first, matey!
+LOBBY_RIGHT_CLICK=Ye can't be right-clickin', landlubber.
+WARP_TO_ARENA=Ye can't board this ship, wench!
+WARP_FROM_ARENA=Walk the plank if ye want to leave, arrrgh!
+WAVE_DEFAULT=Wave %: Blow them all down!
+WAVE_SPECIAL=Wave %: Thar she blows!
+WAVE_REWARD=Ye earned yerself some booty: %
+MISC_LIST_ARENAS=Ships: %
+MISC_LIST_PLAYERS=Buccaneers: %
+MISC_COMMAND_NOT_ALLOWED=Ye can't be usin' that command on the ship, landlubber!
+MISC_NO_ACCESS=Only the captain uses these commands, wench!
+MISC_NONE=
diff --git a/resources/plugin.yml b/resources/plugin.yml
index eab0b29..c4ceb5c 100644
--- a/resources/plugin.yml
+++ b/resources/plugin.yml
@@ -1,7 +1,8 @@
name: MobArena
+author: garbagemule
main: com.garbagemule.MobArena.MobArena
-version: 0.94.4.4
-softdepend: [Spout,Permissions,MultiVerse,XcraftGate,Towny,Heroes,MagicSpells]
+version: 0.94.4.49
+softdepend: [Spout,MultiVerse,XcraftGate,Towny,Heroes,MagicSpells,Vault]
commands:
ma:
description: Base command for MobArena
@@ -18,18 +19,20 @@ commands:
http://pastebin.com, and then paste the link
in the IRC or on the forums.
permissions:
- mobarena.use:
- description: Gives access to usage commands
- default: true
- children:
- mobarena.use.join: true
- mobarena.use.spec: true
- mobarena.use.leave: true
mobarena.use.join:
+ description: Join command
default: true
mobarena.use.spec:
+ description: Spec command
default: true
mobarena.use.leave:
+ description: Leave command
+ default: true
+ mobarena.use.arenalist:
+ description: Arena list command
+ default: true
+ mobarena.use.playerlist:
+ description: Arena list command
default: true
mobarena.admin:
@@ -39,9 +42,8 @@ permissions:
mobarena.admin.enable: true
mobarena.admin.kick: true
mobarena.admin.restore: true
- mobarena.admin.force.end: true
- mobarena.admin.force.start: true
- mobarena.admin.config.reload: true
+ mobarena.admin.force: true
+ mobarena.admin.teleport: true
mobarena.admin.enable:
description: Enable and disable MobArena and/or arenas.
default: false
@@ -51,14 +53,11 @@ permissions:
mobarena.admin.restore:
description: Restore a player's inventory.
default: false
- mobarena.admin.force.end:
- description: Force end an arena.
+ mobarena.admin.force:
+ description: Force start or end an arena.
default: false
- mobarena.admin.force.start:
- description: Force start an arena.
- default: false
- mobarena.admin.config.reload:
- description: Reload the config-file.
+ mobarena.admin.teleport:
+ description: Immune to teleport blocking rules.
default: false
mobarena.setup:
@@ -68,7 +67,7 @@ permissions:
mobarena.setup.arena: true
mobarena.setup.setarena: true
mobarena.setup.addarena: true
- mobarena.setup.delarena: true
+ mobarena.setup.removearena: true
mobarena.setup.protect: true
mobarena.setup.editarena: true
mobarena.setup.setregion: true
@@ -78,13 +77,11 @@ permissions:
mobarena.setup.expandlobbyregion: true
mobarena.setup.setwarp: true
mobarena.setup.spawnpoints: true
- mobarena.setup.addspawn: true
- mobarena.setup.delspawn: true
mobarena.setup.containers: true
- mobarena.setup.addcontainer: true
- mobarena.setup.delcontainer: true
mobarena.setup.leaderboards: true
mobarena.setup.checkdata: true
+ mobarena.setup.classes: true
+ mobarena.setup.config: true
mobarena.setup.autogenerate: true
mobarena.setup.autodegenerate: true
mobarena.setup.arena:
@@ -96,8 +93,8 @@ permissions:
mobarena.setup.addarena:
description: Add a new arena node.
default: false
- mobarena.setup.delarena:
- description: Delete an arena.
+ mobarena.setup.removearena:
+ description: Remove an arena.
default: false
mobarena.setup.protect:
description: Turn arena protection on/off.
@@ -126,27 +123,21 @@ permissions:
mobarena.setup.spawnpoints:
description: List all spawnpoints.
default: false
- mobarena.setup.addspawn:
- description: Add a new spawnpoint.
- default: false
- mobarena.setup.delspawn:
- description: Delete a spawnpoint.
- default: false
mobarena.setup.containers:
description: List all containers.
default: false
- mobarena.setup.addcontainer:
- description: Add a new container.
- default: false
- mobarena.setup.delcontainer:
- description: Delete a container.
- default: false
mobarena.setup.leaderboards:
description: Set up leaderboards.
default: false
mobarena.setup.checkdata:
description: Check which points need to be set up.
default: false
+ mobarena.setup.classes:
+ description: Allows setting up classes from in-game
+ default: false
+ mobarena.setup.config:
+ description: Save or reload the config-file
+ default: false
mobarena.setup.autogenerate:
description: Auto-generate an arena.
default: false
diff --git a/resources/res/classes.yml b/resources/res/classes.yml
new file mode 100644
index 0000000..d9c6bf4
--- /dev/null
+++ b/resources/res/classes.yml
@@ -0,0 +1,15 @@
+Knight:
+ items: diamond_sword, potion:8197:3
+ armor: 306,307,308,309
+Tank:
+ items: iron_sword, potion:8229:2
+ armor: 310,311,312,313
+Archer:
+ items: wood_sword, bow, arrow:256, potion:8197:3, bone
+ armor: 298,299,300,301
+Chemist:
+ items: stone_sword, potion:16428:30, potion:16388:8, potion:16389:20, potion:8197:3, potion:8226:1
+ armor: 314,315,316,317
+Oddjob:
+ items: stone_sword, flint_and_steel, netherrack:2, tnt:4, potion:8197:3
+ armor: 298,299,300,301
\ No newline at end of file
diff --git a/resources/res/config.yml b/resources/res/config.yml
index 02c8644..f8ac1a9 100644
--- a/resources/res/config.yml
+++ b/resources/res/config.yml
@@ -1,113 +1,4 @@
-### 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 - :. If no amount is given, 1 is assumed. Sub-
-# types can be created
-# Item SUBTYPES (wool/dye colors) are written as
- ::,
-# 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:256, grilled_pork, bone
- 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:
- recurrent:
- def1:
- type: default
- priority: 1
- frequency: 1
- monsters:
- zombies: 10
- skeletons: 10
- spiders: 10
- creepers: 10
- wolves: 10
- spec1:
- type: special
- priority: 2
- frequency: 4
- monsters:
- powered-creepers: 10
- zombie-pigmen: 10
- angry-wolves: 10
- humans: 10
- 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
\ No newline at end of file
+# Layout
+global-settings: {}
+classes: {}
+arenas: {}
\ No newline at end of file
diff --git a/resources/res/global-settings.yml b/resources/res/global-settings.yml
new file mode 100644
index 0000000..146e44b
--- /dev/null
+++ b/resources/res/global-settings.yml
@@ -0,0 +1,3 @@
+enabled: true
+allowed-commands: /list, /pl
+update-notification: true
\ No newline at end of file
diff --git a/resources/res/magicspells.yml b/resources/res/magicspells.yml
index 996d8fb..b33693e 100644
--- a/resources/res/magicspells.yml
+++ b/resources/res/magicspells.yml
@@ -1,17 +1,16 @@
-# MobArena disabled MagicSpells spells
+# MobArena disabled MagicSpells spells
# Use this file to disable the MagicSpells spells you don't want your players
# to use during arena sessions. Make sure to put a hyphen (-) before every
# spell, and try to keep the indentation proper.
-
# Spells that will be disabled for the entire arena session.
disabled-spells:
- carpet
- blink
# Spells that will be disabled only while a boss is present.
-disabled-only-on-bosses:
+disabled-on-bosses:
- purge
# Spells that will be disabled only during swarm waves.
-disabled-only-on-swarms:
+disabled-on-swarms:
- purge
\ No newline at end of file
diff --git a/resources/res/rewards.yml b/resources/res/rewards.yml
new file mode 100644
index 0000000..ef24487
--- /dev/null
+++ b/resources/res/rewards.yml
@@ -0,0 +1,9 @@
+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
\ No newline at end of file
diff --git a/resources/res/settings.yml b/resources/res/settings.yml
new file mode 100644
index 0000000..a93c5f7
--- /dev/null
+++ b/resources/res/settings.yml
@@ -0,0 +1,33 @@
+world: ''
+enabled: true
+protect: true
+logging: true
+entry-fee: ''
+clear-wave-before-next: false
+clear-boss-before-next: false
+lightning: true
+auto-equip-armor: true
+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
+auto-respawn: true
+share-items-in-arena: true
+min-players: 0
+max-players: 0
+max-join-distance: 0
+first-wave-delay: 5
+wave-interval: 15
+final-wave: 0
+monster-limit: 100
+monster-exp: false
+keep-exp: true
+food-regen: false
+lock-food-level: true
+spout-class-select: false
+player-time-in-arena: world
\ No newline at end of file
diff --git a/resources/res/waves.yml b/resources/res/waves.yml
new file mode 100644
index 0000000..ea946e3
--- /dev/null
+++ b/resources/res/waves.yml
@@ -0,0 +1,32 @@
+recurrent:
+ def1:
+ type: default
+ priority: 1
+ frequency: 1
+ monsters:
+ zombies: 10
+ skeletons: 10
+ spiders: 10
+ creepers: 10
+ wolves: 10
+ spec1:
+ type: special
+ priority: 2
+ frequency: 4
+ monsters:
+ powered-creepers: 10
+ zombie-pigmen: 10
+ angry-wolves: 10
+ blazes: 10
+single:
+ swarm1:
+ type: swarm
+ wave: 6
+ monster: slime
+ amount: low
+ boss1:
+ type: boss
+ wave: 10
+ monster: cave_spider
+ health: high
+ abilities: arrows, root-target, throw-nearby
\ No newline at end of file
diff --git a/src/com/garbagemule/MobArena/Arena.java b/src/com/garbagemule/MobArena/Arena.java
deleted file mode 100644
index d5fcc2e..0000000
--- a/src/com/garbagemule/MobArena/Arena.java
+++ /dev/null
@@ -1,302 +0,0 @@
-package com.garbagemule.MobArena;
-
-import java.util.*;
-import java.util.concurrent.PriorityBlockingQueue;
-
-import org.bukkit.Bukkit;
-import org.bukkit.GameMode;
-import org.bukkit.Location;
-import org.bukkit.World;
-import org.bukkit.block.Block;
-import org.bukkit.entity.LivingEntity;
-import org.bukkit.entity.Player;
-import org.bukkit.entity.Wolf;
-import org.bukkit.inventory.ItemStack;
-import org.bukkit.permissions.PermissionAttachment;
-//import org.bukkit.util.config.Configuration;
-
-import com.garbagemule.MobArena.leaderboards.Leaderboard;
-import com.garbagemule.MobArena.repairable.Repairable;
-import com.garbagemule.MobArena.repairable.RepairableComparator;
-import com.garbagemule.MobArena.util.Config;
-import com.garbagemule.MobArena.waves.BossWave;
-import com.garbagemule.MobArena.waves.Wave;
-
-public abstract class Arena
-{
- private MobArena plugin;
-
- // Setup fields
- protected String name;
- protected World world;
- protected boolean enabled, protect, running, setup, lobbySetup, autoEquip, forceRestore, softRestore, softRestoreDrops, emptyInvJoin, emptyInvSpec, pvp, monsterInfight, allowWarp;
-
- protected boolean edit, waveClear, detCreepers, detDamage, lightning, hellhounds, specOnDeath, shareInArena, spoutSelect;
- protected Location p1, p2, l1, l2, arenaLoc, lobbyLoc, spectatorLoc;
- protected Map spawnpoints, spawnpointsBoss, containers;
- protected String logging;
-
- // Wave/reward/entryfee fields
- protected int spawnTaskId, sheepTaskId, waveDelay, waveInterval, specialModulo, spawnMonstersInt, maxIdleTime, finalWave;
- protected MASpawnThread spawnThread;
- protected Map> everyWaveMap, afterWaveMap;
- protected Map classMap;
- protected Map> classItems, classArmor;
- protected Map> classPerms;
- protected Map attachments;
- protected List entryFee;
-
- // Player sets
- protected Set arenaPlayers, lobbyPlayers, readyPlayers, specPlayers;
-
- // Wave stuff
- protected TreeSet singleWaves, singleWavesInstance;
- protected TreeSet recurrentWaves;
- protected BossWave bossWave;
- protected Wave currentWave;
-
- // Arena sets/maps
- protected Set hasPaid, rewardedPlayers, notifyPlayers, randoms;
- protected Set monsters, explodingSheep, plaguedPigs, madCows;
- protected Set blocks;
- protected Set pets;
- protected Map petMap;
- protected LinkedList repairables, containables;
-
- // Spawn overriding
- protected int spawnMonsters;
- protected boolean allowMonsters, allowAnimals;
-
- // Other settings
- protected int minPlayers, maxPlayers, joinDistance;
- protected Set classes = new HashSet();
- protected Map locations = new HashMap();
- protected Map healthMap = new HashMap();
- protected Map hungerMap = new HashMap();
- protected Map modeMap = new HashMap();
-
- // Logging
- protected ArenaLog log;
- protected Map arenaPlayerMap;
- protected Leaderboard leaderboard;
-
- protected MAListener eventListener;
-
- protected PriorityBlockingQueue repairQueue;
-
- public Arena() {
-
- }
-
- /**
- * Primary constructor. Requires a name and a world.
- */
- public Arena(String name, World world)
- {
- if (world == null)
- throw new NullPointerException("[MobArena] ERROR! World for arena '" + name + "' does not exist!");
-
- this.name = name;
- this.world = world;
- plugin = (MobArena) Bukkit.getServer().getPluginManager().getPlugin("MobArena");
-
- arenaPlayers = new HashSet();
- lobbyPlayers = new HashSet();
- notifyPlayers = new HashSet();
- readyPlayers = new HashSet();
- specPlayers = new HashSet();
- rewardedPlayers = new HashSet();
- hasPaid = new HashSet();
- monsters = new HashSet();
- explodingSheep = new HashSet();
- plaguedPigs = new HashSet();
- madCows = new HashSet();
- blocks = new HashSet();
- pets = new HashSet();
- petMap = new HashMap();
- classMap = new HashMap();
- randoms = new HashSet();
- repairables = new LinkedList();
- containables = new LinkedList();
- attachments = new HashMap();
- arenaPlayerMap = new HashMap();
-
- running = false;
- edit = false;
-
- allowMonsters = world.getAllowMonsters();
- allowAnimals = world.getAllowAnimals();
- spawnMonsters = MAUtils.getSpawnMonsters(world);
-
- eventListener = new MAListener(this, plugin);
- repairQueue = new PriorityBlockingQueue(100, new RepairableComparator());
- }
-
- public abstract boolean startArena();
-
- public abstract boolean endArena();
-
- public abstract void forceStart();
-
- public abstract void forceEnd();
-
- public abstract void playerJoin(Player p, Location loc);
-
- public abstract void playerReady(Player p);
-
- public abstract void playerLeave(Player p);
-
- public abstract void playerDeath(Player p);
-
- public abstract void playerSpec(Player p, Location loc);
-
- public abstract void playerKill(Player p);
-
- public abstract void restoreInvAndGiveRewardsDelayed(final Player p);
-
- public abstract void restoreInvAndGiveRewards(final Player p);
-
- public abstract void storePlayerData(Player p, Location loc);
-
- public abstract void storeContainerContents();
-
- public abstract void restoreContainerContents();
-
- public abstract void movePlayerToLobby(Player p);
-
- public abstract void movePlayerToSpec(Player p);
-
- public abstract void movePlayerToEntry(Player p);
-
- public abstract void repairBlocks();
-
- public abstract void queueRepairable(Repairable r);
-
-
-
- /*////////////////////////////////////////////////////////////////////
- //
- // Items & Cleanup
- //
- ////////////////////////////////////////////////////////////////////*/
-
- public abstract void assignClass(Player p, String className);
-
- public abstract void assignRandomClass(Player p);
-
- public abstract void assignClassPermissions(Player p);
-
- public abstract void removeClassPermissions(Player p);
-
-
-
- /*////////////////////////////////////////////////////////////////////
- //
- // Initialization & Checks
- //
- ////////////////////////////////////////////////////////////////////*/
-
- public abstract void load(Config config);
-
- public abstract void restoreRegion();
-
- public abstract void serializeConfig();
-
- public abstract void deserializeConfig();
-
- public abstract boolean serializeRegion();
-
- public abstract boolean deserializeRegion();
-
- /**
- * Check if a location is inside of the cuboid region
- * that p1 and p2 span.
- */
- public abstract boolean inRegion(Location loc);
-
- /**
- * Check if a location is inside of the arena region, expanded
- * by 'radius' blocks. Used with explosions.
- */
- public abstract boolean inRegionRadius(Location loc, int radius);
-
-
-
- /*////////////////////////////////////////////////////////////////////
- //
- // Getters & Misc
- //
- ////////////////////////////////////////////////////////////////////*/
-
- public abstract boolean inArena(Player p);
-
- public abstract boolean inLobby(Player p);
-
- public abstract boolean isEnabled();
-
- public abstract boolean isRunning();
-
- public abstract boolean isPvpEnabled();
-
- public abstract boolean isLightningEnabled();
-
- public abstract boolean isBossWave();
-
- public abstract String configName();
-
- public abstract String arenaName();
-
- public abstract MobArena getPlugin();
-
- public abstract World getWorld();
-
- public abstract Wave getWave();
-
- public abstract void setWave(Wave wave);
-
- public abstract void setBossWave(BossWave bossWave);
-
- public abstract Set getClasses();
-
- public abstract List getAllSpawnpoints();
-
- public abstract List getSpawnpoints();
-
- public abstract Location getBossSpawnpoint();
-
- public abstract int getPlayerCount();
-
- public abstract void addBlock(Block b);
-
- public abstract void addMonster(LivingEntity e);
-
- public abstract void addExplodingSheep(LivingEntity e);
-
- public abstract List getAllPlayers();
-
- public abstract List getLivingPlayers();
-
- public abstract Set getArenaPlayers();
-
- public abstract Collection getArenaPlayerSet();
-
- public abstract List getArenaPlayerStatistics(Comparator comparator);
-
- public abstract List getNonreadyPlayers();
-
- public abstract Set getMonsters();
-
- public abstract Set getPets();
-
- public abstract void resetIdleTimer();
-
- public abstract void addTrunkAndLeaves(Block b);
-
- public abstract boolean canAfford(Player p);
-
- public abstract boolean takeFee(Player p);
-
- public abstract boolean canJoin(Player p);
-
- public abstract boolean canSpec(Player p);
-}
diff --git a/src/com/garbagemule/MobArena/ArenaClass.java b/src/com/garbagemule/MobArena/ArenaClass.java
new file mode 100644
index 0000000..0af5dbb
--- /dev/null
+++ b/src/com/garbagemule/MobArena/ArenaClass.java
@@ -0,0 +1,283 @@
+package com.garbagemule.MobArena;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+import org.bukkit.permissions.PermissionAttachment;
+
+public class ArenaClass
+{
+ private String name;
+ private ItemStack helmet, chestplate, leggings, boots;
+ private List items, armor;
+ private Map perms;
+ private int pets;
+
+ /**
+ * Create a new, empty arena class with the given name.
+ * @param name the class name
+ */
+ public ArenaClass(String name) {
+ this.name = name;
+
+ this.items = new ArrayList();
+ this.armor = new ArrayList(4);
+ this.perms = new HashMap();
+ this.pets = 0;
+ }
+
+ /**
+ * Get the name of the arena class.
+ * @return the class name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the Material type of the first item in the items list.
+ * If the items list is empty, the method returns Material.STONE
+ * @return the type of the first item, or STONE if the list is empty
+ */
+ public Material getLogo() {
+ if (items.isEmpty()) {
+ return Material.STONE;
+ }
+ return items.get(0).getType();
+ }
+
+ /**
+ * Set the helmet slot for the class.
+ * @param helmet an item
+ */
+ public void setHelmet(ItemStack helmet) {
+ this.helmet = helmet;
+ }
+
+ /**
+ * Set the chestplate slot for the class.
+ * @param chestplate an item
+ */
+ public void setChestplate(ItemStack chestplate) {
+ this.chestplate = chestplate;
+ }
+
+ /**
+ * Set the leggings slot for the class.
+ * @param leggings an item
+ */
+ public void setLeggings(ItemStack leggings) {
+ this.leggings = leggings;
+ }
+
+ /**
+ * Set the boots slot for the class.
+ * @param boots an item
+ */
+ public void setBoots(ItemStack boots) {
+ this.boots = boots;
+ }
+
+ /**
+ * Add an item to the items list.
+ * If the item is a weapon-type, its durability will be set to "infinite".
+ * If the item is a bone, the pets counter will be incremented.
+ * @param stack an item
+ */
+ public void addItem(ItemStack stack) {
+ if (stack == null) return;
+
+ if (isWeapon(stack)) {
+ stack.setDurability(Short.MIN_VALUE);
+ }
+ else if (stack.getType() == Material.BONE) {
+ pets += stack.getAmount();
+ }
+
+ items.add(stack);
+ }
+
+ /**
+ * Replace the current items list with a new list of all the items in the given list.
+ * This method uses the addItem() method for each item to ensure consistency.
+ * @param stacks a list of items
+ */
+ public void setItems(List stacks) {
+ this.items = new ArrayList(stacks.size());
+ for (ItemStack stack : stacks) {
+ addItem(stack);
+ }
+ }
+
+ /**
+ * Replace the current armor list with the given list.
+ * @param armor a list of items
+ */
+ public void setArmor(List armor) {
+ this.armor = armor;
+ }
+
+ /**
+ * Grants all of the class items and armor to the given player.
+ * The normal items will be added to the inventory normally, while the
+ * armor items will be verified as armor items and placed in their
+ * appropriate slots. If any specific armor slots are specified, they
+ * will overwrite any items in the armor list.
+ * @param p a player
+ */
+ public void grantItems(Player p) {
+ PlayerInventory inv = p.getInventory();
+
+ // Fork over the items.
+ for (ItemStack stack : items) {
+ inv.addItem(stack);
+ }
+
+ // Check for legacy armor-node items
+ if (!armor.isEmpty()) {
+ for (ItemStack piece : armor) {
+ ArmorType type = ArmorType.getType(piece);
+ if (type == null) continue;
+
+ switch (type) {
+ case HELMET:
+ inv.setHelmet(piece);
+ break;
+ case CHESTPLATE:
+ inv.setChestplate(piece);
+ break;
+ case LEGGINGS:
+ inv.setLeggings(piece);
+ break;
+ case BOOTS:
+ inv.setBoots(piece);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Check type specifics.
+ if (helmet != null) inv.setHelmet(helmet);
+ if (chestplate != null) inv.setChestplate(chestplate);
+ if (leggings != null) inv.setLeggings(leggings);
+ if (boots != null) inv.setBoots(boots);
+ }
+
+ /**
+ * Add a permission value to the class.
+ * @param perm the permission
+ * @param value the value
+ */
+ public void addPermission(String perm, boolean value) {
+ perms.put(perm, value);
+ }
+
+ /**
+ * Get an unmodifiable map of permissions and values for the class.
+ * @return a map of permissions and values
+ */
+ public Map getPermissions() {
+ return Collections.unmodifiableMap(perms);
+ }
+
+ /**
+ * Grant the given player all the permissions of the class.
+ * All permissions will be attached to a PermissionAttachment object, which
+ * will be returned to the caller.
+ * @param plugin a MobArena instance
+ * @param p a player
+ * @return the PermissionAttachment with all the permissions
+ */
+ public PermissionAttachment grantPermissions(MobArena plugin, Player p) {
+ if (perms.isEmpty()) return null;
+
+ PermissionAttachment pa = p.addAttachment(plugin);
+
+ for (Entry entry : perms.entrySet()) {
+ try {
+ pa.setPermission(entry.getKey(), entry.getValue());
+ }
+ catch (Exception e) {
+ String perm = entry.getKey() + ":" + entry.getValue();
+ String player = p.getName();
+
+ plugin.warning("[PERM00] Failed to attach permission '" + perm + "' to player '" + player + " with class " + this.name
+ + "'.\nPlease verify that your class permissions are well-formed.");
+ }
+ }
+ return pa;
+ }
+
+ /**
+ * Get the amount of pets this class is given upon starting the arena.
+ * @return the number of pets this class has
+ */
+ public int getPetAmount() {
+ return pets;
+ }
+
+ /**
+ * Used by isWeapon() to determine if an ItemStack is a weapon type.
+ */
+ private static int[] weaponTypes = new int[]{256,257,258,267,268,269,270,271,272,273,274,275,276,277,278,279,283,284,285,286,290,291,292,293,294};
+
+ /**
+ * Returns true, if the ItemStack appears to be a weapon, in which case
+ * the addItem() method will set the weapon durability to the absolute
+ * maximum, as to give them "infinite" durability.
+ * @param stack an ItemStack
+ * @return true, if the item is a weapon
+ */
+ private boolean isWeapon(ItemStack stack) {
+ int id = stack.getTypeId();
+
+ for (int type : weaponTypes) {
+ if (id == type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Used by the grantItems() method to determine the armor type of a given
+ * ItemStack. Armor pieces are auto-equipped.
+ * Note: This enum is only necessary for backward-compatibility with the
+ * 'armor'-node.
+ */
+ public enum ArmorType {
+ HELMET (298,302,306,310,314),
+ CHESTPLATE (299,303,307,311,315),
+ LEGGINGS (300,304,308,312,316),
+ BOOTS (301,305,309,313,317);
+
+ private int[] types;
+
+ private ArmorType(int... types) {
+ this.types = types;
+ }
+
+ public static ArmorType getType(ItemStack stack) {
+ int id = stack.getTypeId();
+
+ for (ArmorType armorType : ArmorType.values()) {
+ for (int type : armorType.types) {
+ if (id == type) {
+ return armorType;
+ }
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/com/garbagemule/MobArena/ArenaImpl.java b/src/com/garbagemule/MobArena/ArenaImpl.java
new file mode 100644
index 0000000..f401b40
--- /dev/null
+++ b/src/com/garbagemule/MobArena/ArenaImpl.java
@@ -0,0 +1,1270 @@
+package com.garbagemule.MobArena;
+
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.concurrent.PriorityBlockingQueue;
+
+import org.bukkit.ChatColor;
+import org.bukkit.Chunk;
+import org.bukkit.GameMode;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.ContainerBlock;
+import org.bukkit.entity.CreatureType;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.ExperienceOrb;
+import org.bukkit.entity.Item;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Slime;
+import org.bukkit.entity.Vehicle;
+import org.bukkit.entity.Wolf;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+import org.bukkit.permissions.PermissionAttachment;
+import org.bukkit.potion.PotionEffect;
+
+import com.garbagemule.MobArena.events.*;
+import com.garbagemule.MobArena.framework.Arena;
+import com.garbagemule.MobArena.leaderboards.Leaderboard;
+import com.garbagemule.MobArena.region.ArenaRegion;
+import com.garbagemule.MobArena.repairable.*;
+import com.garbagemule.MobArena.spout.Spouty;
+import com.garbagemule.MobArena.time.Time;
+import com.garbagemule.MobArena.time.TimeStrategy;
+import com.garbagemule.MobArena.time.TimeStrategyLocked;
+import com.garbagemule.MobArena.time.TimeStrategyNull;
+import com.garbagemule.MobArena.util.*;
+import com.garbagemule.MobArena.util.config.Config;
+import com.garbagemule.MobArena.util.config.ConfigSection;
+import com.garbagemule.MobArena.util.inventory.InventoryManager;
+import com.garbagemule.MobArena.util.inventory.InventoryUtils;
+import com.garbagemule.MobArena.waves.*;
+
+public class ArenaImpl implements Arena
+{
+ // General stuff
+ private MobArena plugin;
+ private String name;
+ private World world;
+
+ // Settings section of the config-file for this arena.
+ private ConfigSection settings;
+
+ // Run-time settings and critical config settings
+ private boolean enabled, protect, running, edit;
+
+ // World stuff
+ private boolean allowMonsters, allowAnimals;
+ //private Difficulty spawnMonsters;
+
+ // Warps, points and locations
+ private ArenaRegion region;
+ private Leaderboard leaderboard;
+
+ // Player stuff
+ private InventoryManager inventoryManager;
+ private RewardManager rewardManager;
+ private Map arenaPlayerMap;
+ private Map playerData = new HashMap();
+
+ private Set arenaPlayers, lobbyPlayers, readyPlayers, specPlayers, deadPlayers;
+ private Set randoms;
+
+ // Classes stuff
+ private Map classes;
+ private Map attachments;
+
+ // Blocks and pets
+ private PriorityBlockingQueue repairQueue;
+ private Set blocks;
+ private LinkedList repairables, containables;
+
+ // Monster stuff
+ private MonsterManager monsterManager;
+
+ // Wave stuff
+ private WaveManager waveManager;
+ private Wave currentWave;
+ private MASpawnThread spawnThread;
+ private SheepBouncer sheepBouncer;
+ private Map> everyWaveMap, afterWaveMap;
+
+ // Misc
+ private ArenaListenerImpl eventListener;
+ private List entryFee;
+ //private ArenaLog log;
+ private TimeStrategy timeStrategy;
+
+ /**
+ * Primary constructor. Requires a name and a world.
+ */
+ public ArenaImpl(MobArena plugin, Config config, String name, World world) {
+ if (world == null)
+ throw new NullPointerException("[MobArena] ERROR! World for arena '" + name + "' does not exist!");
+
+ this.name = name;
+ this.world = world;
+ this.plugin = plugin;
+ this.settings = new ConfigSection(config, "arenas." + name + ".settings");
+ this.region = new ArenaRegion(new ConfigSection(config, "arenas." + name + ".coords"), this);
+
+ this.enabled = settings.getBoolean("enabled", false);
+ this.protect = settings.getBoolean("protect", true);
+ this.running = false;
+ this.edit = false;
+
+ this.inventoryManager = new InventoryManager(this);
+ this.rewardManager = new RewardManager(this);
+
+ // Warps, points and locations
+ this.leaderboard = new Leaderboard(plugin, this, region.getLeaderboard());
+
+ // Player stuff
+ this.arenaPlayerMap = new HashMap();
+ this.arenaPlayers = new HashSet();
+ this.lobbyPlayers = new HashSet();
+ this.readyPlayers = new HashSet();
+ this.specPlayers = new HashSet();
+ this.deadPlayers = new HashSet();
+ this.randoms = new HashSet();
+
+ // Classes, items and permissions
+ this.classes = plugin.getArenaMaster().getClasses();
+ this.attachments = new HashMap();
+
+ // Blocks and pets
+ this.repairQueue = new PriorityBlockingQueue(100, new RepairableComparator());
+ this.blocks = new HashSet();
+ this.repairables = new LinkedList();
+ this.containables = new LinkedList();
+
+ // Monster stuff
+ this.monsterManager = new MonsterManager();
+
+ // Wave stuff
+ this.waveManager = new WaveManager(this, config);
+ this.everyWaveMap = MAUtils.getArenaRewardMap(plugin, config, name, "every");
+ this.afterWaveMap = MAUtils.getArenaRewardMap(plugin, config, name, "after");
+
+ // Misc
+ this.eventListener = new ArenaListenerImpl(this, plugin);
+ this.entryFee = ItemParser.parseItems(settings.getString("entry-fee", ""));
+ this.allowMonsters = world.getAllowMonsters();
+ this.allowAnimals = world.getAllowAnimals();
+
+ String timeString = settings.getString("player-time-in-arena", "world");
+ Time time = Enums.getEnumFromString(Time.class, timeString);
+ this.timeStrategy = (time != null ? new TimeStrategyLocked(time) : new TimeStrategyNull());
+ }
+
+
+
+ /*/////////////////////////////////////////////////////////////////////////
+ //
+ // NEW METHODS IN REFACTORING
+ //
+ /////////////////////////////////////////////////////////////////////////*/
+
+ @Override
+ public ConfigSection getSettings() {
+ return settings;
+ }
+
+ @Override
+ public World getWorld() {
+ return world;
+ }
+
+ @Override
+ public void setWorld(World world) {
+ this.world = world;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public void setEnabled(boolean value) {
+ enabled = value;
+ }
+
+ @Override
+ public boolean isProtected() {
+ return protect;
+ }
+
+ @Override
+ public void setProtected(boolean value) {
+ protect = value;
+ }
+
+ @Override
+ public boolean isRunning() {
+ return running;
+ }
+
+ @Override
+ public boolean inEditMode() {
+ return edit;
+ }
+
+ @Override
+ public void setEditMode(boolean value) {
+ edit = value;
+ }
+
+ private int getMinPlayers() {
+ return settings.getInt("min-players");
+ }
+
+ private int getMaxPlayers() {
+ return settings.getInt("max-players");
+ }
+
+ private int getJoinDistance() {
+ return settings.getInt("max-join-distance");
+ }
+
+ @Override
+ public Material getClassLogo(String classname) {
+ ArenaClass arenaClass = classes.get(classname);
+ if (arenaClass == null) return Material.STONE;
+
+ return arenaClass.getLogo();
+ }
+
+ @Override
+ public List getEntryFee() {
+ return entryFee;
+ }
+
+ @Override
+ public Set>> getEveryWaveEntrySet() {
+ return everyWaveMap.entrySet();
+ }
+
+ @Override
+ public List getAfterWaveReward(int wave) {
+ return afterWaveMap.get(wave);
+ }
+
+ @Override
+ public Set getPlayersInArena() {
+ return Collections.unmodifiableSet(arenaPlayers);
+ }
+
+ @Override
+ public Set getPlayersInLobby() {
+ return Collections.unmodifiableSet(lobbyPlayers);
+ }
+
+ @Override
+ public Set getReadyPlayersInLobby() {
+ return Collections.unmodifiableSet(readyPlayers);
+ }
+
+ @Override
+ public Set getSpectators() {
+ return Collections.unmodifiableSet(specPlayers);
+ }
+
+ @Override
+ public MASpawnThread getSpawnThread() {
+ return spawnThread;
+ }
+
+ @Override
+ public WaveManager getWaveManager() {
+ return waveManager;
+ }
+
+ /*@Override
+ public ArenaLog getLog() {
+ return log;
+ }*/
+
+ @Override
+ public Location getPlayerEntry(Player p) {
+ PlayerData mp = playerData.get(p);
+ return (mp != null ? mp.entry() : null);
+ }
+
+ @Override
+ public ArenaListenerImpl getEventListener() {
+ return eventListener;
+ }
+
+ @Override
+ public void setLeaderboard(Leaderboard leaderboard) {
+ this.leaderboard = leaderboard;
+ }
+
+ @Override
+ public ArenaPlayer getArenaPlayer(Player p) {
+ return arenaPlayerMap.get(p);
+ }
+
+ @Override
+ public Set getBlocks() {
+ return blocks;
+ }
+
+ @Override
+ public void addBlock(Block b) {
+ blocks.add(b);
+ }
+
+ @Override
+ public boolean removeBlock(Block b) {
+ return blocks.remove(b);
+ }
+
+ @Override
+ public boolean hasPet(Entity e) {
+ return monsterManager.hasPet(e);
+ }
+
+ @Override
+ public void addRepairable(Repairable r) {
+ repairables.add(r);
+ }
+
+ @Override
+ public ArenaRegion getRegion() {
+ return region;
+ }
+
+ @Override
+ public InventoryManager getInventoryManager() {
+ return inventoryManager;
+ }
+
+ @Override
+ public RewardManager getRewardManager() {
+ return rewardManager;
+ }
+
+ @Override
+ public MonsterManager getMonsterManager() {
+ return monsterManager;
+ }
+
+
+
+
+
+
+
+
+
+
+
+ @Override
+ public boolean startArena() {
+ // Sanity-checks
+ if (running || lobbyPlayers.isEmpty() || !readyPlayers.containsAll(lobbyPlayers)) {
+ return false;
+ }
+
+ // Fire the event and check if it's been cancelled.
+ ArenaStartEvent event = new ArenaStartEvent(this);
+ plugin.getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ // Store all chest contents.
+ storeContainerContents();
+
+ // Populate arenaPlayers and clear the lobby.
+ arenaPlayers.addAll(lobbyPlayers);
+ lobbyPlayers.clear();
+ readyPlayers.clear();
+
+ // Assign random classes.
+ for (Player p : randoms) {
+ assignRandomClass(p);
+ }
+
+ // Then check if there are still players left.
+ if (arenaPlayers.isEmpty()) {
+ return false;
+ }
+
+ // Teleport players, give full health, initialize map
+ for (Player p : arenaPlayers) {
+ p.teleport(region.getArenaWarp());
+ //movePlayerToLocation(p, region.getArenaWarp());
+ setHealth(p, 20);
+ p.setFoodLevel(20);
+ assignClassPermissions(p);
+ arenaPlayerMap.get(p).resetStats();
+ }
+
+ // Start spawning monsters (must happen before 'running = true;')
+ startSpawner();
+ startBouncingSheep();
+
+ // Set the boolean.
+ running = true;
+
+ // Spawn pets (must happen after 'running = true;')
+ spawnPets();
+
+ // Start logging
+ rewardManager.reset();
+ //log = new ArenaLog(plugin, this);
+ //log.start();
+
+ // Initialize leaderboards and start displaying info.
+ leaderboard.initialize();
+ leaderboard.startTracking();
+
+ Messenger.tellAll(this, Msg.ARENA_START);
+
+ return true;
+ }
+
+ @Override
+ public boolean endArena() {
+ // Sanity-checks.
+ if (!running || !arenaPlayers.isEmpty()) {
+ return false;
+ }
+
+ // Fire the event and check if it's been cancelled.
+ ArenaEndEvent event = new ArenaEndEvent(this);
+ plugin.getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ // Set the running boolean and disable arena if not disabled.
+ boolean en = enabled;
+ enabled = false;
+ running = false;
+
+ // Stop tracking leaderboards
+ leaderboard.stopTracking();
+ leaderboard.update();
+
+ // Finish logging
+ //log.end();
+ /*if (logging != null) { TODO: FIX LOGGING!
+ log.saveSessionData();
+ log.updateArenaTotals();
+ }*/
+ //log.clearSessionData();
+
+ // Stop spawning.
+ stopSpawner();
+
+ // Announce and clean arena floor, etc.
+ Messenger.tellAll(this, Msg.ARENA_END, true);
+ cleanup();
+
+ // Restore region.
+ if (settings.getBoolean("soft-restore", false)) {
+ restoreRegion();
+ }
+
+ // Restore chests
+ restoreContainerContents();
+
+ // Restore enabled status.
+ enabled = en;
+
+ return true;
+ }
+
+ @Override
+ public void forceStart()
+ {
+ if (running)
+ return;
+
+ // Set operations.
+ Set tmp = new HashSet();
+ tmp.addAll(lobbyPlayers);
+ tmp.removeAll(readyPlayers);
+
+ // Force leave.
+ for (Player p : tmp) {
+ playerLeave(p);
+ }
+
+ startArena();
+ }
+
+ @Override
+ public void forceEnd() {
+ for (Player p : getAllPlayers()) {
+ playerLeave(p);
+ }
+
+ cleanup();
+ }
+
+ @Override
+ public boolean playerJoin(Player p, Location loc)
+ {
+ // Fire the event and check if it's been cancelled.
+ ArenaPlayerJoinEvent event = new ArenaPlayerJoinEvent(p, this);
+ plugin.getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ takeFee(p);
+ storePlayerData(p, loc);
+ removePotionEffects(p);
+ MAUtils.sitPets(p);
+ setHealth(p, 20);
+ p.setFoodLevel(20);
+ p.setGameMode(GameMode.SURVIVAL);
+ movePlayerToLobby(p);
+
+ arenaPlayerMap.put(p, new ArenaPlayer(p, this, plugin));
+
+ if (MobArena.hasSpout && settings.getBoolean("spout-class-select"))
+ Spouty.classSelectionScreen(plugin, this, p);
+
+ Messenger.tellPlayer(p, Msg.JOIN_PLAYER_JOINED);
+ return true;
+ }
+
+ @Override
+ public void playerReady(Player p)
+ {
+ readyPlayers.add(p);
+
+ int minPlayers = getMinPlayers();
+ if (minPlayers > 0 && lobbyPlayers.size() < minPlayers)
+ {
+ Messenger.tellPlayer(p, Msg.LOBBY_NOT_ENOUGH_PLAYERS, "" + minPlayers);
+ return;
+ }
+
+ startArena();
+ }
+
+ @Override
+ //@SuppressWarnings("deprecation")
+ public boolean playerLeave(Player p)
+ {
+ // Fire the event and check if it's been cancelled.
+ ArenaPlayerLeaveEvent event = new ArenaPlayerLeaveEvent(p, this);
+ plugin.getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ removeClassPermissions(p);
+ removePotionEffects(p);
+
+ if (inLobby(p) || inArena(p)) {
+ inventoryManager.clearInventory(p);
+ inventoryManager.restoreInventory(p);
+ rewardManager.grantRewards(p);
+ refund(p);
+ //p.updateInventory();
+ }
+ else if (inSpec(p)) {
+ inventoryManager.restoreInventory(p);
+ //p.updateInventory();
+ }
+
+ movePlayerToEntry(p);
+ discardPlayer(p);
+
+ endArena();
+ return true;
+ }
+
+ @Override
+ public void playerDeath(Player p)
+ {
+ // Fire the event
+ ArenaPlayerDeathEvent event = new ArenaPlayerDeathEvent(p, this);
+ plugin.getServer().getPluginManager().callEvent(event);
+
+ arenaPlayers.remove(p);
+
+ if (!settings.getBoolean("auto-respawn", true)) {
+ deadPlayers.add(p);
+ endArena();
+ return;
+ }
+
+ p.setHealth(20);
+ Delays.revivePlayer(plugin, this, p);
+ endArena();
+ }
+
+ @Override
+ public void playerRespawn(Player p) {
+ if (settings.getBoolean("auto-respawn", true)) {
+ return;
+ }
+
+ deadPlayers.remove(p);
+ revivePlayer(p);
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void revivePlayer(Player p) {
+ Delays.douse(plugin, p, 1);
+ removeClassPermissions(p);
+ removePotionEffects(p);
+
+ if (settings.getBoolean("spectate-on-death", true)) {
+ movePlayerToSpec(p);
+ restoreInvAndExp(p);
+ } else {
+ restoreInvAndExp(p);
+ movePlayerToEntry(p);
+ discardPlayer(p);
+ }
+ p.updateInventory();
+ }
+
+ @Override
+ public Location getRespawnLocation(Player p) {
+ Location l = null;
+ if (settings.getBoolean("spectate-on-death", true)) {
+ l = region.getSpecWarp();
+ } else {
+ //l = locations.get(p);
+ l = playerData.get(p).entry();
+ }
+ return l;
+ }
+
+ @Override
+ public void playerSpec(Player p, Location loc)
+ {
+ storePlayerData(p, loc);
+ MAUtils.sitPets(p);
+ movePlayerToSpec(p);
+ }
+
+ private void spawnPets()
+ {
+ for (Map.Entry entry : arenaPlayerMap.entrySet()) {
+ ArenaClass arenaClass = entry.getValue().getArenaClass();
+ int petAmount = arenaClass.getPetAmount();
+
+ if (petAmount <= 0) {
+ continue;
+ }
+
+ // Remove the bones from the inventory.
+ Player p = entry.getKey();
+ p.getInventory().removeItem(new ItemStack(Material.BONE, petAmount));
+
+ for (int i = 0; i < petAmount; i++) {
+ Wolf wolf = (Wolf) world.spawnCreature(p.getLocation(), CreatureType.WOLF);
+ wolf.setTamed(true);
+ wolf.setOwner(p);
+ wolf.setHealth(wolf.getMaxHealth());
+ if (settings.getBoolean("hellhounds"))
+ wolf.setFireTicks(32768);
+ monsterManager.addPet(wolf);
+ }
+ }
+ }
+
+ private void removePotionEffects(Player p) {
+ for (PotionEffect effect : p.getActivePotionEffects()) {
+ p.removePotionEffect(effect.getType());
+ }
+ }
+
+ private void startSpawner()
+ {
+ // Set the spawn flags to enable monster spawning.
+ world.setSpawnFlags(true, true);
+ //world.setDifficulty(Difficulty.NORMAL);
+
+ // Create a spawner if one doesn't exist, otherwise reset it
+ if (spawnThread == null) {
+ spawnThread = new MASpawnThread(plugin, this);
+ } else {
+ spawnThread.reset();
+ }
+
+ // Schedule it for the initial first wave delay.
+ scheduleTask(spawnThread, settings.getInt("first-wave-delay", 5) * 20);
+ }
+
+ /**
+ * Schedule a Runnable to be executed after the given delay in
+ * server ticks. The method is used by the MASpawnThread to
+ * repeatedly spawn new mobs instead of a scheduled repeating
+ * tasks, as well as the sheep bouncer.
+ */
+ @Override
+ public void scheduleTask(Runnable r, int delay) {
+ plugin.getServer().getScheduler().scheduleSyncDelayedTask(
+ plugin,
+ r,
+ delay);
+ }
+
+ private void stopSpawner() {
+ world.setSpawnFlags(allowMonsters, allowAnimals);
+ //world.setDifficulty(spawnMonsters);
+ }
+
+ private void startBouncingSheep()
+ {
+ // Create a new bouncer if necessary.
+ if (sheepBouncer == null) {
+ sheepBouncer = new SheepBouncer(this);
+ }
+
+ // Start bouncing!
+ scheduleTask(sheepBouncer, settings.getInt("first-wave-delay", 5) * 20);
+ }
+
+ private void updateChunk(Location loc)
+ {
+ if (!arenaPlayers.isEmpty() || !world.getName().equals(loc.getWorld().getName()))
+ return;
+
+ Chunk chunk = world.getChunkAt(loc);
+ if (!world.isChunkLoaded(chunk))
+ world.loadChunk(chunk);
+ else
+ world.refreshChunk(chunk.getX(), chunk.getZ());
+ }
+
+ @Override
+ public void storePlayerData(Player p, Location loc)
+ {
+ plugin.getArenaMaster().addPlayer(p, this);
+
+ PlayerData mp = playerData.get(p);
+
+ // If there's no player stored, create a new one!
+ if (mp == null) {
+ mp = new PlayerData(p);
+ playerData.put(p, mp);
+ }
+
+ // At any rate, update the data.
+ mp.update();
+
+ // And update the inventory as well.
+ inventoryManager.storeInventory(p);
+ }
+
+ @Override
+ public void storeContainerContents()
+ {
+ plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin,
+ new Runnable() {
+ public void run() {
+ for (Location loc : region.getContainers()) {
+ BlockState state = world.getBlockAt(loc).getState();
+ if (state instanceof ContainerBlock) {
+ containables.add(new RepairableContainer(state, false));
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void restoreContainerContents()
+ {
+ plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin,
+ new Runnable() {
+ public void run() {
+ for (Repairable r : containables) {
+ r.repair();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void movePlayerToLobby(Player p)
+ {
+ updateChunk(region.getLobbyWarp());
+ specPlayers.remove(p); // If joining from spec area
+ lobbyPlayers.add(p);
+ p.teleport(region.getLobbyWarp());
+ timeStrategy.setPlayerTime(p);
+ //movePlayerToLocation(p, region.getLobbyWarp());
+ }
+
+ @Override
+ public void movePlayerToSpec(Player p)
+ {
+ updateChunk(region.getSpecWarp());
+ specPlayers.add(p);
+ p.teleport(region.getSpecWarp());
+ timeStrategy.setPlayerTime(p);
+ //movePlayerToLocation(p, region.getSpecWarp());
+ }
+
+ @Override
+ public void movePlayerToEntry(Player p)
+ {
+ Location entry = playerData.get(p).entry();
+ if (entry == null || p.isDead()) return;
+
+ updateChunk(entry);
+ p.teleport(entry);
+ timeStrategy.resetPlayerTime(p);
+ //movePlayerToLocation(p, entry);
+
+ p.setGameMode(playerData.get(p).getMode());
+ p.addPotionEffects(playerData.get(p).getPotionEffects());
+ }
+
+ /*private void movePlayerToLocation(Player p, Location l) {
+ WorldServer fromWorld = ((CraftWorld) p.getWorld()).getHandle();
+ WorldServer toWorld = ((CraftWorld) l.getWorld()).getHandle();
+ EntityPlayer player = ((CraftPlayer) p).getHandle();
+
+ if (fromWorld == toWorld) {
+ player.netServerHandler.teleport(l);
+ }
+ else {
+ if (player.activeContainer != player.defaultContainer) {
+ player.closeInventory();
+ }
+ CraftServer cs = ((CraftServer) plugin.getServer());
+ cs.getHandle().moveToWorld(player, toWorld.dimension, true, l);
+ }
+ }*/
+
+ private void restoreInvAndExp(Player p) {
+ inventoryManager.clearInventory(p);
+ inventoryManager.restoreInventory(p);
+ rewardManager.grantRewards(p);
+
+ if (!settings.getBoolean("keep-exp", false)) {
+ playerData.get(p).restoreData();
+ }
+ }
+
+ @Override
+ public void discardPlayer(Player p)
+ {
+ plugin.getArenaMaster().removePlayer(p);
+ clearPlayer(p);
+ }
+
+ private void clearPlayer(Player p)
+ {
+ // Remove the player data completely.
+ PlayerData mp = playerData.remove(p);
+
+ // Health must be handled in a certain way because of Heroes
+ setHealth(p, mp.health());
+
+ // Put out fire.
+ Delays.douse(plugin, p, 3);
+
+ // Remove pets.
+ monsterManager.removePets(p);
+
+ // readyPlayers before lobbyPlayers because of startArena sanity-checks
+ readyPlayers.remove(p);
+ specPlayers.remove(p);
+ arenaPlayers.remove(p);
+ lobbyPlayers.remove(p);
+ arenaPlayerMap.remove(p);
+ }
+
+ private void setHealth(Player p, int health) {
+ plugin.getHealthStrategy().setHealth(p, health);
+ }
+
+ @Override
+ public void repairBlocks()
+ {
+ while (!repairQueue.isEmpty())
+ repairQueue.poll().repair();
+ }
+
+ @Override
+ public void queueRepairable(Repairable r)
+ {
+ repairQueue.add(r);
+ }
+
+
+
+ /*////////////////////////////////////////////////////////////////////
+ //
+ // Items & Cleanup
+ //
+ ////////////////////////////////////////////////////////////////////*/
+
+ @Override
+ public void assignClass(Player p, String className) {
+ ArenaPlayer arenaPlayer = arenaPlayerMap.get(p);
+ ArenaClass arenaClass = classes.get(className);
+
+ if (arenaPlayer == null || arenaClass == null) {
+ return;
+ }
+
+ inventoryManager.clearInventory(p);
+
+ arenaPlayer.setArenaClass(arenaClass);
+ arenaClass.grantItems(p);
+ }
+
+ @Override
+ public void assignRandomClass(Player p)
+ {
+ Random r = new Random();
+ List classes = new LinkedList(this.classes.keySet());
+
+ String className = classes.remove(r.nextInt(classes.size()));
+ while (!plugin.has(p, "mobarena.classes." + className))
+ {
+ if (classes.isEmpty())
+ {
+ plugin.info("Player '" + p.getName() + "' has no class permissions!");
+ playerLeave(p);
+ return;
+ }
+ className = classes.remove(r.nextInt(classes.size()));
+ }
+
+ assignClass(p, className);
+ Messenger.tellPlayer(p, Msg.LOBBY_CLASS_PICKED, className);
+ }
+
+ @Override
+ public void assignClassPermissions(Player p)
+ {
+ PermissionAttachment pa = arenaPlayerMap.get(p).getArenaClass().grantPermissions(plugin, p);
+ if (pa == null) return;
+
+ attachments.put(p, pa);
+ p.recalculatePermissions();
+ }
+
+ @Override
+ public void removeClassPermissions(Player p)
+ {
+ PermissionAttachment pa = attachments.remove(p);
+ if (pa == null) return;
+
+ try {
+ p.removeAttachment(pa);
+ }
+ catch (Exception e) {
+ for (Entry entry : pa.getPermissions().entrySet()) {
+ String perm = entry.getKey() + ":" + entry.getValue();
+ String name = p.getName();
+
+ plugin.warning("[PERM01] Failed to remove permission attachment '" + perm + "' from player '" + name
+ + "'.\nThis should not be a big issue, but please verify that the player doesn't have any permissions they shouldn't have.");
+ }
+ }
+ p.recalculatePermissions();
+ }
+
+ private void cleanup() {
+ removeMonsters();
+ removeBlocks();
+ removeEntities();
+ clearPlayers();
+ }
+
+ private void removeMonsters() {
+ monsterManager.clear();
+ }
+
+ private void removeBlocks() {
+ for (Block b : blocks) {
+ b.setTypeId(0);
+ }
+ blocks.clear();
+ }
+
+ private void removeEntities() {
+ List chunks = region.getChunks();
+
+ for (Chunk c : chunks) {
+ for (Entity e : c.getEntities()) {
+ if (!(e instanceof Item || e instanceof Vehicle || e instanceof Slime || e instanceof ExperienceOrb)) {
+ continue;
+ }
+
+ if (e != null) {
+ e.remove();
+ }
+ }
+ }
+ }
+
+ private void clearPlayers() {
+ arenaPlayers.clear();
+ arenaPlayerMap.clear();
+ lobbyPlayers.clear();
+ readyPlayers.clear();
+ }
+
+
+
+ /*////////////////////////////////////////////////////////////////////
+ //
+ // Initialization & Checks
+ //
+ ////////////////////////////////////////////////////////////////////*/
+
+ @Override
+ public void restoreRegion()
+ {
+ Collections.sort(repairables, new RepairableComparator());
+
+ for (Repairable r : repairables)
+ r.repair();
+ }
+
+
+
+ /*////////////////////////////////////////////////////////////////////
+ //
+ // Getters & Misc
+ //
+ ////////////////////////////////////////////////////////////////////*/
+
+ @Override
+ public boolean inArena(Player p) {
+ return arenaPlayers.contains(p);
+ }
+
+ @Override
+ public boolean inLobby(Player p) {
+ return lobbyPlayers.contains(p);
+ }
+
+ @Override
+ public boolean inSpec(Player p) {
+ return specPlayers.contains(p);
+ }
+
+ @Override
+ public boolean isDead(Player p) {
+ return deadPlayers.contains(p);
+ }
+
+ @Override
+ public String configName()
+ {
+ return name;
+ }
+
+ @Override
+ public String arenaName()
+ {
+ return MAUtils.nameConfigToArena(name);
+ }
+
+ @Override
+ public MobArena getPlugin()
+ {
+ return plugin;
+ }
+
+ @Override
+ public Wave getWave()
+ {
+ return currentWave;
+ }
+
+ @Override
+ public Map getClasses()
+ {
+ return classes;
+ }
+
+ @Override
+ public int getPlayerCount()
+ {
+ return spawnThread.getPlayerCount();
+ }
+
+ @Override
+ public List getAllPlayers()
+ {
+ List result = new LinkedList();
+ result.addAll(arenaPlayers);
+ result.addAll(lobbyPlayers);
+ result.addAll(specPlayers);
+
+ return result;
+ }
+
+ @Override
+ public Collection getArenaPlayerSet()
+ {
+ return arenaPlayerMap.values();
+ }
+
+ /*@Override
+ public List getArenaPlayerStatistics(Comparator comparator)
+ {
+ List list = new ArrayList();
+
+ for (ArenaPlayer ap : arenaPlayerMap.values())
+ list.add(ap.getStats());
+
+ Collections.sort(list, comparator);
+ return list;
+ }*/
+
+ @Override
+ public List getNonreadyPlayers()
+ {
+ List result = new LinkedList();
+ result.addAll(lobbyPlayers);
+ result.removeAll(readyPlayers);
+ return result;
+ }
+
+ @Override
+ public boolean canAfford(Player p) {
+ if (entryFee.isEmpty()) return true;
+
+ PlayerInventory inv = p.getInventory();
+ for (ItemStack stack : entryFee) {
+ // Economy money
+ if (stack.getTypeId() == MobArena.ECONOMY_MONEY_ID) {
+ if (!plugin.hasEnough(p, stack.getAmount())) {
+ return false;
+ }
+ }
+ // Normal stack
+ else {
+ if (!inv.contains(stack.getTypeId(), stack.getAmount())) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean takeFee(Player p) {
+ if (entryFee.isEmpty()) return true;
+
+ PlayerInventory inv = p.getInventory();
+
+ // Take some economy money
+ for (ItemStack stack : InventoryUtils.extractAll(MobArena.ECONOMY_MONEY_ID, entryFee)) {
+ plugin.takeMoney(p, stack.getAmount());
+ }
+
+ // Take any other items
+ for (ItemStack stack : entryFee) {
+ inv.removeItem(stack);
+ }
+
+ Messenger.tellPlayer(p, Msg.JOIN_FEE_PAID.toString(MAUtils.listToString(entryFee, plugin)));
+ return true;
+ }
+
+ @Override
+ public boolean refund(Player p) {
+ if (entryFee.isEmpty()) return true;
+ if (!inLobby(p)) return false;
+
+ // Refund economy money
+ for (ItemStack stack : InventoryUtils.extractAll(MobArena.ECONOMY_MONEY_ID, entryFee)) {
+ plugin.giveMoney(p, stack.getAmount());
+ }
+
+ // Refund other items.
+ for (ItemStack stack : entryFee) {
+ if (stack.getTypeId() > 0) {
+ p.getInventory().addItem(stack);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean canJoin(Player p) {
+ if (!enabled)
+ Messenger.tellPlayer(p, Msg.JOIN_ARENA_NOT_ENABLED);
+ else if (!region.isSetup() || waveManager.getRecurrentWaves().isEmpty())
+ Messenger.tellPlayer(p, Msg.JOIN_ARENA_NOT_SETUP);
+ else if (edit)
+ Messenger.tellPlayer(p, Msg.JOIN_ARENA_EDIT_MODE);
+ else if (arenaPlayers.contains(p) || lobbyPlayers.contains(p))
+ Messenger.tellPlayer(p, Msg.JOIN_ALREADY_PLAYING);
+ else if (running)
+ Messenger.tellPlayer(p, Msg.JOIN_ARENA_IS_RUNNING);
+ else if (!plugin.has(p, "mobarena.arenas." + configName()))
+ Messenger.tellPlayer(p, Msg.JOIN_ARENA_PERMISSION);
+ else if (getMaxPlayers() > 0 && lobbyPlayers.size() >= getMaxPlayers())
+ Messenger.tellPlayer(p, Msg.JOIN_PLAYER_LIMIT_REACHED);
+ else if (getJoinDistance() > 0 && !region.contains(p.getLocation(), getJoinDistance()))
+ Messenger.tellPlayer(p, Msg.JOIN_TOO_FAR);
+ else if (settings.getBoolean("require-empty-inv-join", true) && !InventoryManager.hasEmptyInventory(p))
+ Messenger.tellPlayer(p, Msg.JOIN_EMPTY_INV);
+ else if (!canAfford(p))
+ Messenger.tellPlayer(p, Msg.JOIN_FEE_REQUIRED, MAUtils.listToString(entryFee, plugin));
+ else return true;
+
+ return false;
+ }
+
+ @Override
+ public boolean canSpec(Player p) {
+ if (!enabled)
+ Messenger.tellPlayer(p, Msg.JOIN_ARENA_NOT_ENABLED);
+ else if (!region.isSetup())
+ Messenger.tellPlayer(p, Msg.JOIN_ARENA_NOT_SETUP);
+ else if (edit)
+ Messenger.tellPlayer(p, Msg.JOIN_ARENA_EDIT_MODE);
+ else if (arenaPlayers.contains(p) || lobbyPlayers.contains(p))
+ Messenger.tellPlayer(p, Msg.SPEC_ALREADY_PLAYING);
+ else if (settings.getBoolean("require-empty-inv-spec", true) && !InventoryManager.hasEmptyInventory(p))
+ Messenger.tellPlayer(p, Msg.SPEC_EMPTY_INV);
+ else if (getJoinDistance() > 0 && !region.contains(p.getLocation(), getJoinDistance()))
+ Messenger.tellPlayer(p, Msg.JOIN_TOO_FAR);
+ else return true;
+
+ return false;
+ }
+
+ /**
+ * The "perfect equals method" cf. "Object-Oriented Design and Patterns"
+ * by Cay S. Horstmann.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if (other == null) return false;
+ if (getClass() != other.getClass()) return false;
+
+ // Arenas must have different names.
+ if (other instanceof ArenaImpl && ((ArenaImpl)other).name.equals(name))
+ return true;
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return ((enabled && region.isSetup()) ? ChatColor.GREEN : ChatColor.GRAY) + configName();
+ }
+}
diff --git a/src/com/garbagemule/MobArena/ArenaListenerImpl.java b/src/com/garbagemule/MobArena/ArenaListenerImpl.java
new file mode 100644
index 0000000..e716a5e
--- /dev/null
+++ b/src/com/garbagemule/MobArena/ArenaListenerImpl.java
@@ -0,0 +1,789 @@
+package com.garbagemule.MobArena;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import org.bukkit.ChatColor;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.ContainerBlock;
+import org.bukkit.block.Sign;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Projectile;
+import org.bukkit.entity.Slime;
+import org.bukkit.entity.Wolf;
+import org.bukkit.event.Event.Result;
+import org.bukkit.event.block.Action;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.block.BlockBurnEvent;
+import org.bukkit.event.block.BlockEvent;
+import org.bukkit.event.block.BlockFormEvent;
+import org.bukkit.event.block.BlockIgniteEvent;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.block.SignChangeEvent;
+import org.bukkit.event.entity.CreatureSpawnEvent;
+import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
+import org.bukkit.event.entity.EntityChangeBlockEvent;
+import org.bukkit.event.entity.EntityCombustEvent;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+import org.bukkit.event.entity.EntityDamageEvent;
+import org.bukkit.event.entity.EntityDeathEvent;
+import org.bukkit.event.entity.EntityExplodeEvent;
+import org.bukkit.event.entity.EntityRegainHealthEvent;
+import org.bukkit.event.entity.EntityTargetEvent;
+import org.bukkit.event.entity.FoodLevelChangeEvent;
+import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
+import org.bukkit.event.entity.EntityTargetEvent.TargetReason;
+import org.bukkit.event.entity.PlayerDeathEvent;
+import org.bukkit.event.player.PlayerAnimationEvent;
+import org.bukkit.event.player.PlayerBucketEmptyEvent;
+import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+import org.bukkit.event.player.PlayerDropItemEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.event.player.PlayerKickEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.event.player.PlayerRespawnEvent;
+import org.bukkit.event.player.PlayerTeleportEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.material.Attachable;
+import org.bukkit.material.Bed;
+import org.bukkit.material.Door;
+import org.bukkit.material.Redstone;
+
+import com.garbagemule.MobArena.MAUtils;
+import com.garbagemule.MobArena.MobArena;
+import com.garbagemule.MobArena.Msg;
+import com.garbagemule.MobArena.framework.Arena;
+import com.garbagemule.MobArena.framework.ArenaListener;
+import com.garbagemule.MobArena.leaderboards.Leaderboard;
+import com.garbagemule.MobArena.region.ArenaRegion;
+import com.garbagemule.MobArena.region.RegionPoint;
+import com.garbagemule.MobArena.repairable.*;
+import com.garbagemule.MobArena.util.TextUtils;
+import com.garbagemule.MobArena.util.config.ConfigSection;
+import com.garbagemule.MobArena.waves.MABoss;
+
+public class ArenaListenerImpl implements ArenaListener
+{
+ private MobArena plugin;
+ private Arena arena;
+ private ArenaRegion region;
+ private MonsterManager monsters;
+
+ private boolean softRestore,
+ softRestoreDrops,
+ protect;
+ private boolean monsterExp,
+ monsterInfight,
+ pvpEnabled,
+ foodRegen,
+ lockFoodLevel;
+ private boolean allowTeleport,
+ canShare,
+ allowMonsters;
+
+ private Set banned;
+
+ public ArenaListenerImpl(Arena arena, MobArena plugin) {
+ this.plugin = plugin;
+ this.arena = arena;
+ this.region = arena.getRegion();
+ this.monsters = arena.getMonsterManager();
+
+ /*
+ * TODO: Figure out if this is really a good idea + It saves needing all
+ * those methods in Arena.java + It is relatively simple + It would be
+ * fairly easy to implement an observer pattern - More private fields -
+ * Uglier code
+ */
+ ConfigSection s = arena.getSettings();
+ this.softRestore = s.getBoolean("soft-restore", false);
+ this.softRestoreDrops = s.getBoolean("soft-restore-drops", false);
+ this.protect = s.getBoolean("protect", true);
+ this.monsterExp = s.getBoolean("monster-exp", false);
+ this.monsterInfight = s.getBoolean("monster-infight", false);
+ this.pvpEnabled = s.getBoolean("pvp-enabled", false);
+ this.foodRegen = s.getBoolean("food-regen", false);
+ this.lockFoodLevel = s.getBoolean("lock-food-level", true);
+ this.allowTeleport = s.getBoolean("allow-teleporting", false);
+ this.canShare = s.getBoolean("share-items-in-arena", true);
+
+ this.allowMonsters = arena.getWorld().getAllowMonsters();
+
+ this.banned = new HashSet();
+ }
+
+ public void onBlockBreak(BlockBreakEvent event) {
+ if (onBlockDestroy(event))
+ return;
+
+ event.setCancelled(true);
+ }
+
+ public void onBlockBurn(BlockBurnEvent event) {
+ if (onBlockDestroy(event))
+ return;
+
+ event.setCancelled(true);
+ }
+
+ private boolean onBlockDestroy(BlockEvent event) {
+ if (!arena.getRegion().contains(event.getBlock().getLocation()) || arena.inEditMode() || (!arena.isProtected() && arena.isRunning()))
+ return true;
+
+ Block b = event.getBlock();
+ if (arena.removeBlock(b) || b.getType() == Material.TNT)
+ return true;
+
+ if (softRestore && arena.isRunning()) {
+ BlockState state = b.getState();
+
+ Repairable r = null;
+ if (state instanceof ContainerBlock)
+ r = new RepairableContainer(state);
+ else if (state instanceof Sign)
+ r = new RepairableSign(state);
+ else if (state.getData() instanceof Attachable)
+ r = new RepairableAttachable(state);
+ else
+ r = new RepairableBlock(state);
+
+ arena.addRepairable(r);
+
+ if (!softRestoreDrops)
+ b.setTypeId(0);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public void onBlockPlace(BlockPlaceEvent event) {
+ Block b = event.getBlock();
+
+ // If the event didn't happen in the region, or if in edit mode, ignore
+ if (!arena.getRegion().contains(b.getLocation()) || arena.inEditMode()) {
+ return;
+ }
+
+ // If the arena isn't running, or if the player isn't in the arena,
+ // cancel.
+ if (!arena.isRunning() || !arena.inArena(event.getPlayer())) {
+ event.setCancelled(true);
+ return;
+ }
+
+ // Otherwise, block was placed during a session.
+ arena.addBlock(b);
+
+ switch (b.getType()){
+ // For doors, add the block just above (so we get both halves)
+ case WOODEN_DOOR:
+ case IRON_DOOR_BLOCK:
+ arena.addBlock(b.getRelative(0, 1, 0));
+ break;
+ }
+ }
+
+ public void onBlockForm(BlockFormEvent event) {
+ if (!arena.getRegion().contains(event.getBlock().getLocation()))
+ return;
+
+ // If a snowman forms some snow on its path, add the block
+ if (event.getNewState().getType() == Material.SNOW)
+ arena.addBlock(event.getBlock());
+ }
+
+ public void onBlockIgnite(BlockIgniteEvent event) {
+ if (!arena.getRegion().contains(event.getBlock().getLocation()))
+ return;
+
+ switch (event.getCause()){
+ case LIGHTNING:
+ case SPREAD:
+ event.setCancelled(true);
+ break;
+ case FLINT_AND_STEEL:
+ if (arena.isRunning())
+ arena.addBlock(event.getBlock().getRelative(BlockFace.UP));
+ else
+ event.setCancelled(true);
+ break;
+ default:
+ break;
+ }
+ }
+
+ public void onSignChange(SignChangeEvent event) {
+ arena.setLeaderboard(new Leaderboard(plugin, arena, event.getBlock().getLocation()));
+ arena.getRegion().set(RegionPoint.LEADERBOARD, event.getBlock().getLocation());
+
+ Messenger.tellPlayer(event.getPlayer(), "Leaderboard made. Now set up the stat signs!");
+ }
+
+ public void onCreatureSpawn(CreatureSpawnEvent event) {
+ if (!arena.getRegion().contains(event.getLocation())) {
+ if (!event.isCancelled()) {
+ event.setCancelled(!allowMonsters);
+ }
+ return;
+ }
+
+ if (event.getSpawnReason() != SpawnReason.CUSTOM) {
+ event.setCancelled(true);
+ return;
+ }
+
+ LivingEntity entity = (LivingEntity) event.getEntity();
+ if (arena.isRunning() && entity instanceof Slime)
+ monsters.addMonster(entity);
+
+ // If running == true, setCancelled(false), and vice versa.
+ event.setCancelled(!arena.isRunning());
+ }
+
+ public void onEntityExplode(EntityExplodeEvent event) {
+ if (!monsters.getMonsters().contains(event.getEntity()) && !arena.getRegion().contains(event.getLocation(), 10))
+ return;
+
+ monsters.removeMonster(event.getEntity());
+
+ // Cancel if the arena isn't running
+ if (!arena.isRunning()) {
+ event.setCancelled(true);
+ return;
+ }
+
+ // Uncancel, just in case.
+ event.setCancelled(false);
+
+ // If the arena isn't destructible, just clear the blocklist.
+ if (!softRestore && protect) {
+ List blocks = new LinkedList(arena.getBlocks());
+ event.blockList().retainAll(blocks);
+ return;
+ }
+
+ if (!softRestoreDrops)
+ event.setYield(0);
+
+ // Handle all the blocks in the block list.
+ for (Block b : event.blockList()) {
+ BlockState state = b.getState();
+
+ if (state.getData() instanceof Door && ((Door) state.getData()).isTopHalf()) {
+ state = b.getRelative(BlockFace.DOWN).getState();
+ }
+ else if (state.getData() instanceof Bed && ((Bed) state.getData()).isHeadOfBed()) {
+ state = b.getRelative(((Bed) state.getData()).getFacing().getOppositeFace()).getState();
+ }
+
+ // Create a Repairable from the block.
+ Repairable r = null;
+ if (state instanceof ContainerBlock)
+ r = new RepairableContainer(state);
+ else if (state instanceof Sign)
+ r = new RepairableSign(state);
+ else if (state.getData() instanceof Bed)
+ r = new RepairableBed(state);
+ else if (state.getData() instanceof Door)
+ r = new RepairableDoor(state);
+ else if (state.getData() instanceof Attachable || state.getData() instanceof Redstone)
+ r = new RepairableAttachable(state);
+ else
+ r = new RepairableBlock(state);
+
+ // Cakes and liquids should just get removed. If player-placed block, drop as item.
+ Material mat = state.getType();
+ if (mat == Material.CAKE_BLOCK || mat == Material.WATER || mat == Material.LAVA)
+ arena.removeBlock(b);
+ else if (arena.removeBlock(b))
+ arena.getWorld().dropItemNaturally(b.getLocation(), new ItemStack(state.getTypeId(), 1));
+ else if (softRestore)
+ arena.addRepairable(r);
+ else
+ arena.queueRepairable(r);
+ }
+ }
+
+ /******************************************************
+ *
+ * DEATH LISTENERS
+ *
+ ******************************************************/
+
+ public void onEntityDeath(EntityDeathEvent event) {
+ if (event instanceof PlayerDeathEvent) {
+ onPlayerDeath((PlayerDeathEvent) event, (Player) event.getEntity());
+ }
+ else if (monsters.removeMonster(event.getEntity())) {
+ onMonsterDeath(event);
+ }
+ }
+
+ private void onPlayerDeath(PlayerDeathEvent event, Player player) {
+ if (arena.inArena(player) || arena.inLobby(player)) {
+ event.getDrops().clear();
+ event.setDroppedExp(0);
+ event.setKeepLevel(true);
+ arena.playerDeath(player);
+ }
+ }
+
+ public boolean onPlayerRespawn(PlayerRespawnEvent event) {
+ Player p = event.getPlayer();
+ if (!arena.isDead(p)) {
+ return false;
+ }
+
+ Location loc = arena.getRespawnLocation(p);
+ event.setRespawnLocation(loc);
+
+ arena.playerRespawn(p);
+ return true;
+ }
+
+ private void onMonsterDeath(EntityDeathEvent event) {
+ EntityDamageEvent e1 = event.getEntity().getLastDamageCause();
+ EntityDamageByEntityEvent e2 = (e1 instanceof EntityDamageByEntityEvent) ? (EntityDamageByEntityEvent) e1 : null;
+ Entity damager = (e2 != null) ? e2.getDamager() : null;
+
+ // Make sure to grab the owner of a projectile/pet
+ if (damager instanceof Projectile) {
+ damager = ((Projectile) damager).getShooter();
+ }
+ else if (damager instanceof Wolf && arena.hasPet(damager)) {
+ damager = (Player) ((Wolf) damager).getOwner();
+ }
+
+ // If the damager was a player, add to kills.
+ if (damager instanceof Player) {
+ ArenaPlayer ap = arena.getArenaPlayer((Player) damager);
+ if (ap != null) {
+ ArenaPlayerStatistics stats = ap.getStats();
+ if (stats != null) {
+ ap.getStats().inc("kills");
+ }
+ }
+ }
+
+ if (!monsterExp)
+ event.setDroppedExp(0);
+
+ event.getDrops().clear();
+
+ List loot = monsters.getLoot(event.getEntity());
+ if (loot != null && !loot.isEmpty()) {
+ event.getDrops().add(getRandomItem(loot));
+ }
+
+ return;
+ }
+
+ private ItemStack getRandomItem(List stacks) {
+ return stacks.get((new Random()).nextInt(stacks.size()));
+ }
+
+ /******************************************************
+ *
+ * DAMAGE LISTENERS
+ *
+ ******************************************************/
+
+ public void onEntityDamage(EntityDamageEvent event) {
+ Entity damagee = event.getEntity();
+ if (!arena.isRunning() || !arena.getRegion().contains(damagee.getLocation())) {
+ return;
+ }
+
+ EntityDamageByEntityEvent edbe = (event instanceof EntityDamageByEntityEvent) ? (EntityDamageByEntityEvent) event : null;
+ Entity damager = null;
+
+ if (edbe != null) {
+ damager = edbe.getDamager();
+
+ if (damager instanceof Projectile) {
+ damager = ((Projectile) damager).getShooter();
+ }
+ else if (damager instanceof Wolf && arena.hasPet(damager)) {
+ damager = (Player) ((Wolf) damager).getOwner();
+ }
+ }
+
+ // Pet wolf
+ if (damagee instanceof Wolf && arena.hasPet(damagee)) {
+ onPetDamage(event, (Wolf) damagee, damager);
+ }
+ // Player
+ else if (damagee instanceof Player) {
+ onPlayerDamage(event, (Player) damagee, damager);
+ }
+ // Boss
+ else if (monsters.getBossMonsters().contains(damagee)) {
+ onBossDamage(event, (LivingEntity) damagee, damager);
+ }
+ // Regular monster
+ else if (monsters.getMonsters().contains(damagee)) {
+ onMonsterDamage(event, damagee, damager);
+ }
+ }
+
+ private void onPlayerDamage(EntityDamageEvent event, Player player, Entity damager) {
+ // Cancel all damage in the lobby and spec area
+ if (arena.inLobby(player) || arena.inSpec(player)) {
+ event.setCancelled(true);
+ return;
+ }
+ // If PvP is disabled and damager is a player, cancel damage
+ else if (arena.inArena(player)) {
+ if (!pvpEnabled && damager instanceof Player) {
+ event.setCancelled(true);
+ return;
+ }
+ event.setCancelled(false);
+ arena.getArenaPlayer(player).getStats().add("dmgTaken", event.getDamage());
+ }
+ }
+
+ private void onPetDamage(EntityDamageEvent event, Wolf pet, Entity damager) {
+ event.setCancelled(true);
+ }
+
+ private void onMonsterDamage(EntityDamageEvent event, Entity monster, Entity damager) {
+ if (damager instanceof Player) {
+ Player p = (Player) damager;
+ if (!arena.inArena(p)) {
+ event.setCancelled(true);
+ return;
+ }
+
+ ArenaPlayerStatistics aps = arena.getArenaPlayer(p).getStats();
+ aps.add("dmgDone", event.getDamage());
+ aps.inc("hits");
+ }
+ else if (damager instanceof Wolf && arena.hasPet(damager)) {
+ event.setDamage(1);
+ Player p = (Player) ((Wolf) damager).getOwner();
+ ArenaPlayerStatistics aps = arena.getArenaPlayer(p).getStats();
+ aps.add("dmgDone", event.getDamage());
+ // arena.getArenaPlayer(p).getStats().dmgDone += event.getDamage();
+ }
+ else if (damager instanceof LivingEntity) {
+ if (!monsterInfight)
+ event.setCancelled(true);
+ }
+ }
+
+ private void onBossDamage(EntityDamageEvent event, LivingEntity monster, Entity damager) {
+ // Health the boss back up.
+ monster.setHealth(monster.getMaxHealth());
+
+ // Damage the underlying MABoss.
+ MABoss boss = monsters.getBoss(monster);
+ boss.damage(event.getDamage());
+
+ // If it died, remove it from the arena.
+ if (boss.isDead()) {
+ monsters.removeBoss(monster);
+ monster.damage(10000);
+ }
+
+ // And "cancel out" the damage.
+ event.setDamage(1);
+ }
+
+ public void onEntityCombust(EntityCombustEvent event) {
+ if (monsters.getMonsters().contains(event.getEntity()))
+ event.setCancelled(true);
+ }
+
+ public void onEntityTarget(EntityTargetEvent event) {
+ if (!arena.isRunning() || event.isCancelled())
+ return;
+
+ if (arena.hasPet(event.getEntity())) {
+ if (event.getReason() != TargetReason.TARGET_ATTACKED_OWNER && event.getReason() != TargetReason.OWNER_ATTACKED_TARGET)
+ return;
+
+ if (!(event.getTarget() instanceof Player))
+ return;
+
+ // If the target is a player, cancel.
+ event.setCancelled(true);
+ }
+
+ else if (monsters.getMonsters().contains(event.getEntity())) {
+ if (event.getReason() == TargetReason.FORGOT_TARGET)
+ event.setTarget(MAUtils.getClosestPlayer(plugin, event.getEntity(), arena));
+
+ else if (event.getReason() == TargetReason.TARGET_DIED)
+ event.setTarget(MAUtils.getClosestPlayer(plugin, event.getEntity(), arena));
+
+ else if (event.getReason() == TargetReason.TARGET_ATTACKED_ENTITY)
+ if (arena.hasPet(event.getTarget()))
+ event.setCancelled(true);
+
+ else if (event.getReason() == TargetReason.CLOSEST_PLAYER)
+ if (!arena.inArena((Player) event.getTarget()))
+ event.setCancelled(true);
+ }
+ }
+
+ public void onEntityChangeBlock(EntityChangeBlockEvent event) {
+ if (arena.getRegion().contains(event.getBlock().getLocation()))
+ event.setCancelled(true);
+ }
+
+ public void onEntityRegainHealth(EntityRegainHealthEvent event) {
+ if (!arena.isRunning())
+ return;
+
+ if (!(event.getEntity() instanceof Player) || !arena.inArena((Player) event.getEntity()))
+ return;
+
+ if (!foodRegen && event.getRegainReason() == RegainReason.SATIATED) {
+ event.setCancelled(true);
+ }
+ }
+
+ public void onFoodLevelChange(FoodLevelChangeEvent event) {
+ if (!arena.isRunning())
+ return;
+
+ if (!(event.getEntity() instanceof Player) || !arena.inArena((Player) event.getEntity()))
+ return;
+
+ // If the food level is locked, cancel all changes.
+ if (lockFoodLevel)
+ event.setCancelled(true);
+ }
+
+ public void onPlayerAnimation(PlayerAnimationEvent event) {
+ if (!arena.isRunning() || !arena.inArena(event.getPlayer()))
+ return;
+
+ arena.getArenaPlayer(event.getPlayer()).getStats().inc("swings");
+ }
+
+ public void onPlayerDropItem(PlayerDropItemEvent event) {
+ Player p = event.getPlayer();
+
+ // If the player is active in the arena, only cancel if sharing is not
+ // allowed
+ if (arena.inArena(p)) {
+ if (!canShare) {
+ Messenger.tellPlayer(p, Msg.LOBBY_DROP_ITEM);
+ event.setCancelled(true);
+ }
+ }
+
+ // Else, if the player is in the lobby or a spectator, just cancel
+ else if (arena.inLobby(p) || arena.inSpec(p)) {
+ Messenger.tellPlayer(p, Msg.LOBBY_DROP_ITEM);
+ event.setCancelled(true);
+ }
+
+ /*
+ * If the player is not in the arena in any way (as arena player, lobby
+ * player or a spectator), but they -are- in the region, it must mean
+ * they are trying to drop items when not allowed
+ */
+ else if (region.contains(p.getLocation())) {
+ Messenger.tellPlayer(p, Msg.LOBBY_DROP_ITEM);
+ event.setCancelled(true);
+ }
+
+ /*
+ * If the player is in the banned set, it means they got kicked or
+ * disconnected during a session, meaning they are more than likely
+ * trying to steal items, if a PlayerDropItemEvent is fired.
+ */
+ else if (banned.contains(p)) {
+ plugin.warning("Player " + p.getName() + " tried to steal class items!");
+ event.setCancelled(true);
+ }
+ }
+
+ public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) {
+ if (!arena.getReadyPlayersInLobby().contains(event.getPlayer()) && !arena.inArena(event.getPlayer()))
+ return;
+
+ if (!arena.isRunning()) {
+ event.getBlockClicked().getRelative(event.getBlockFace()).setTypeId(0);
+ event.setCancelled(true);
+ return;
+ }
+
+ Block liquid = event.getBlockClicked().getRelative(event.getBlockFace());
+ arena.addBlock(liquid);
+ }
+
+ public void onPlayerInteract(PlayerInteractEvent event) {
+ Player p = event.getPlayer();
+ if (arena.inArena(p) || !arena.inLobby(p))
+ return;
+
+ // Player is in the lobby, so disallow using items.
+ Action a = event.getAction();
+ if (a == Action.RIGHT_CLICK_AIR || a == Action.RIGHT_CLICK_BLOCK) {
+ event.setUseItemInHand(Result.DENY);
+ event.setCancelled(true);
+ }
+
+ // If there's no block involved, just return.
+ if (!event.hasBlock())
+ return;
+
+ // Iron block
+ if (event.getClickedBlock().getTypeId() == 42) {
+ handleReadyBlock(p);
+ }
+ // Sign
+ else if (event.getClickedBlock().getState() instanceof Sign) {
+ Sign sign = (Sign) event.getClickedBlock().getState();
+ handleSign(sign, p);
+ }
+ }
+
+ private void handleReadyBlock(Player p) {
+ if (arena.getArenaPlayer(p).getArenaClass() != null) {
+ Messenger.tellPlayer(p, Msg.LOBBY_PLAYER_READY);
+ arena.playerReady(p);
+ }
+ else {
+ Messenger.tellPlayer(p, Msg.LOBBY_PICK_CLASS);
+ }
+ }
+
+ private void handleSign(Sign sign, Player p) {
+ // Check if the first line is a class name.
+ String className = ChatColor.stripColor(sign.getLine(0)).toLowerCase();
+
+ if (!arena.getClasses().containsKey(className) && !className.equals("random"))
+ return;
+
+ // Check for permission.
+ if (!plugin.has(p, "mobarena.classes." + className) && !className.equals("random")) {
+ Messenger.tellPlayer(p, Msg.LOBBY_CLASS_PERMISSION);
+ return;
+ }
+
+ // Delay the inventory stuff to ensure that right-clicking works.
+ delayAssignClass(p, className);
+ }
+
+ private void delayAssignClass(final Player p, final String className) {
+ plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin,new Runnable() {
+ public void run() {
+ arena.assignClass(p, className);
+
+ if (!className.equalsIgnoreCase("random"))
+ Messenger.tellPlayer(p, Msg.LOBBY_CLASS_PICKED, TextUtils.camelCase(className), arena.getClassLogo(className));
+ else
+ Messenger.tellPlayer(p, Msg.LOBBY_CLASS_RANDOM);
+ }
+ });
+ }
+
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ Player p = event.getPlayer();
+ if (!arena.isEnabled() || (!arena.inArena(p) && !arena.inLobby(p)))
+ return;
+
+ arena.playerLeave(p);
+ banned.add(p);
+ scheduleUnban(p, 20);
+ }
+
+ public void onPlayerKick(PlayerKickEvent event) {
+ Player p = event.getPlayer();
+ if (!arena.isEnabled() || (!arena.inArena(p) && !arena.inLobby(p))) {
+ return;
+ }
+
+ arena.playerLeave(p);
+ banned.add(p);
+ scheduleUnban(p, 20);
+ }
+
+ private void scheduleUnban(final Player p, int ticks) {
+ plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
+ public void run() {
+ banned.remove(p);
+ }
+ }, ticks);
+ }
+
+ public TeleportResponse onPlayerTeleport(PlayerTeleportEvent event) {
+ if (!arena.isEnabled() || !region.isSetup() || arena.inEditMode() || allowTeleport) {
+ return TeleportResponse.IDGAF;
+ }
+
+ Location to = event.getTo();
+ Location from = event.getFrom();
+ Player p = event.getPlayer();
+
+ if (region.contains(from)) {
+ // Players not in the arena are free to warp out.
+ if (!arena.inArena(p) && !arena.inLobby(p) && !arena.inSpec(p)) {
+ return TeleportResponse.ALLOW;
+ }
+
+ // Covers the case in which both locations are in the arena.
+ if (region.contains(to) || region.isWarp(to) || to.equals(arena.getPlayerEntry(p))) {
+ return TeleportResponse.ALLOW;
+ }
+
+ Messenger.tellPlayer(p, Msg.WARP_FROM_ARENA);
+ return TeleportResponse.REJECT;
+ }
+ else if (region.contains(to)) {
+ if (region.isWarp(from) || region.isWarp(to) || to.equals(arena.getPlayerEntry(p))) {
+ return TeleportResponse.ALLOW;
+ }
+
+ Messenger.tellPlayer(p, Msg.WARP_TO_ARENA);
+ return TeleportResponse.REJECT;
+ }
+
+ return TeleportResponse.IDGAF;
+ }
+
+ public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
+ Player p = event.getPlayer();
+
+ if (event.isCancelled() || (!arena.inArena(p) && !arena.inLobby(p))) {
+ return;
+ }
+
+ // This is safe, because commands will always have at least one element.
+ String base = event.getMessage().split(" ")[0];
+
+ // Check if the entire base command is allowed.
+ if (plugin.getArenaMaster().isAllowed(base)) {
+ return;
+ }
+
+ // If not, check if the specific command is allowed.
+ String noslash = event.getMessage().substring(1);
+ if (plugin.getArenaMaster().isAllowed(noslash)) {
+ return;
+ }
+
+ // This is dirty, but it ensures that commands are indeed blocked.
+ event.setMessage("/");
+
+ // Cancel the event regardless.
+ event.setCancelled(true);
+ Messenger.tellPlayer(p, Msg.MISC_COMMAND_NOT_ALLOWED);
+ }
+}
diff --git a/src/com/garbagemule/MobArena/ArenaLog.java b/src/com/garbagemule/MobArena/ArenaLog.java
deleted file mode 100644
index 0c78f57..0000000
--- a/src/com/garbagemule/MobArena/ArenaLog.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package com.garbagemule.MobArena;
-
-import java.sql.Timestamp;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.bukkit.entity.Player;
-
-import com.garbagemule.MobArena.util.data.PlainText;
-import com.garbagemule.MobArena.util.data.Totals;
-import com.garbagemule.MobArena.util.data.XML;
-import com.garbagemule.MobArena.util.data.YAML;
-
-public class ArenaLog
-{
- public MobArena plugin;
- public Arena arena;
- public Timestamp startTime, endTime;
- public int lastWave;
-
- public Map players;
- public Map distribution;
-
- /**
- * Create a new ArenaLog.
- * @param plugin MobArena instance
- * @param arena The arena
- */
- public ArenaLog(MobArena plugin, Arena arena)
- {
- this.plugin = plugin;
- this.arena = arena;
- this.players = new HashMap(arena.arenaPlayers.size());
- this.distribution = new HashMap(arena.classes.size());
- }
-
- /**
- * Start logging by creating ArenaPlayer objects and recording
- * the class distributions and the start time.
- */
- public void start()
- {
- // Populate the data maps
- populatePlayerMap();
- populateDistributionMap();
-
- // Grab the current timestamp
- startTime = new Timestamp((new Date()).getTime());
- }
-
- /**
- * End logging by recording the last wave and the end time.
- */
- public void end()
- {
- lastWave = arena.spawnThread.getWave() - 1;
- endTime = new Timestamp((new Date()).getTime());
- }
-
- /**
- * Map players to ArenaPlayer objects.
- */
- private void populatePlayerMap()
- {
- for (Player p : arena.arenaPlayers)
- players.put(p, new ArenaPlayer(p, arena, plugin));
- }
-
- /**
- * Map classes to amounts of players playing as the classes.
- */
- private void populateDistributionMap()
- {
- // Initialize the map
- for (String c : arena.classes)
- distribution.put(c,0);
-
- // Count occurrences
- for (String c : arena.classMap.values())
- distribution.put(c,distribution.get(c) + 1);
- }
-
- /**
- * Save the data of the current session according to the logging type.
- */
- public void saveSessionData()
- {
- if (arena.logging.equals("xml"))
- XML.saveSessionData(this);
- else if (arena.logging.equals("yml") || arena.logging.equals("yaml"))
- YAML.saveSessionData(this);
- else
- PlainText.saveSessionData(this);
- //CSV.saveSessionData(this, plugin);
- //YAML.saveSessionData(this);
- }
-
- /**
- * Update the totals-file
- */
- public void updateArenaTotals()
- {
- Totals.updateArenaTotals(this);
-
- if (arena.logging.equals("xml"))
- XML.updateArenaTotals(this);
- //PlainText.updateArenaTotals(this);
- }
-
- /**
- * Clear maps.
- */
- public void clearSessionData()
- {
- players.clear();
- distribution.clear();
- }
-
- public Arena getArena() { return arena; }
- public Timestamp getStartTime() { return startTime; }
- public Timestamp getEndTime() { return endTime; }
- public String getDuration() { return MAUtils.getDuration(endTime.getTime() - startTime.getTime()); }
- public long getDurationLong() { return endTime.getTime() - startTime.getTime(); }
- public int getLastWave() { return lastWave; }
-
- public void playerKill(Player p)
- {
- players.get(p).getStats().kills++;
- }
-
- public void playerDamager(Player p, int damage)
- {
- players.get(p).getStats().dmgDone += damage;
- }
-
- public void playerDamagee(Player p, int damage)
- {
- players.get(p).getStats().dmgTaken += damage;
- }
-}
diff --git a/src/com/garbagemule/MobArena/ArenaMaster.java b/src/com/garbagemule/MobArena/ArenaMaster.java
deleted file mode 100644
index 537fa4b..0000000
--- a/src/com/garbagemule/MobArena/ArenaMaster.java
+++ /dev/null
@@ -1,147 +0,0 @@
-package com.garbagemule.MobArena;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.bukkit.Location;
-import org.bukkit.World;
-import org.bukkit.entity.Entity;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.ItemStack;
-//import org.bukkit.util.config.Configuration;
-
-//import com.garbagemule.ArenaPlugin.Master;
-
-public abstract class ArenaMaster //implements Master
-{
- protected Arena selectedArena;
-
- // Settings
- protected boolean enabled, updateNotify;
-
- // Classes
- protected Set classes;
- protected Map> classItems, classArmor;
- protected Map> classPerms;
- //protected Map>> classBonuses;
- protected Map arenaMap;
-
- // Location map
- protected Map locations = new HashMap();
-
- // Arena list
- protected List arenas;
-
- // Listeners
- protected Set listeners = new HashSet();
-
-
-
- /*/////////////////////////////////////////////////////////////////////////
- //
- // Arena getters
- //
- /////////////////////////////////////////////////////////////////////////*/
-
-
- public abstract List getEnabledArenas();
-
- public abstract List getPermittedArenas(Player p);
-
- public abstract List getEnabledAndPermittedArenas(Player p);
-
- public abstract Arena getArenaAtLocation(Location loc);
-
- public abstract List getArenasInWorld(World world);
-
- public abstract List getAllPlayers();
-
- public abstract List getAllPlayersInArena(String arenaName);
-
- public abstract List getAllLivingPlayers();
-
- public abstract List getLivingPlayersInArena(String arenaName);
-
- public abstract Arena getArenaWithPlayer(Player p);
-
- public abstract Arena getArenaWithPlayer(String playerName);
-
- public abstract Arena getArenaWithSpectator(Player p);
-
- public abstract Arena getArenaWithMonster(Entity e);
-
- public abstract Arena getArenaWithPet(Entity e);
-
- public abstract Arena getArenaWithName(String configName);
-
- public abstract Arena getArenaWithName(Collection arenas, String configName);
-
-
-
- /*/////////////////////////////////////////////////////////////////////////
- //
- // Initialization
- //
- /////////////////////////////////////////////////////////////////////////*/
-
- public abstract void initialize();
-
- /**
- * Load the global settings.
- */
- public abstract void loadSettings();
-
- /**
- * Load all class-related stuff.
- */
- public abstract void loadClasses();
-
- /**
- * Load all arena-related stuff.
- */
- public abstract void loadArenas();
-
- public abstract Arena createArenaNode(String configName, World world);
-
- public abstract void removeArenaNode(String configName);
-
-
-
- /*/////////////////////////////////////////////////////////////////////////
- //
- // Update and serialization methods
- //
- /////////////////////////////////////////////////////////////////////////*/
-
- /**
- * Update one, two or all three of global settings, classes
- * and arenas (arenas with deserialization).
- */
- public abstract void update(boolean settings, boolean classes, boolean arenalist);
-
- /**
- * Serialize the global settings.
- */
- public abstract void serializeSettings();
-
- /**
- * Serialize all arena configs.
- */
- public abstract void serializeArenas();
-
- /**
- * Deserialize all arena configs. Updates the arena list to
- * include only the current arenas (not ones added in the
- * actual file) that are also in the config-file.
- */
- public abstract void deserializeArenas();
-
- public abstract void updateSettings();
- public abstract void updateClasses();
- public abstract void updateArenas();
- public abstract void updateAll();
-}
diff --git a/src/com/garbagemule/MobArena/ArenaMasterImpl.java b/src/com/garbagemule/MobArena/ArenaMasterImpl.java
new file mode 100644
index 0000000..7a61223
--- /dev/null
+++ b/src/com/garbagemule/MobArena/ArenaMasterImpl.java
@@ -0,0 +1,591 @@
+package com.garbagemule.MobArena;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+import org.bukkit.permissions.Permission;
+import org.bukkit.permissions.PermissionDefault;
+import org.bukkit.plugin.PluginManager;
+
+import com.garbagemule.MobArena.ArenaImpl;
+import com.garbagemule.MobArena.MobArena;
+import com.garbagemule.MobArena.ArenaClass.ArmorType;
+import com.garbagemule.MobArena.framework.Arena;
+import com.garbagemule.MobArena.framework.ArenaMaster;
+import com.garbagemule.MobArena.util.ItemParser;
+import com.garbagemule.MobArena.util.TextUtils;
+import com.garbagemule.MobArena.util.config.Config;
+import com.garbagemule.MobArena.util.config.ConfigSection;
+import com.garbagemule.MobArena.util.config.ConfigUtils;
+
+public class ArenaMasterImpl implements ArenaMaster
+{
+ private MobArena plugin;
+ private Config config;
+
+ private List arenas;
+ private Map arenaMap;
+ private Arena selectedArena;
+
+ private Map classes;
+
+ private Set allowedCommands;
+
+ /**
+ * Default constructor.
+ */
+ public ArenaMasterImpl(MobArena plugin) {
+ this.plugin = plugin;
+ this.config = plugin.getMAConfig();
+
+ this.arenas = new LinkedList();
+ this.arenaMap = new HashMap();
+
+ this.classes = new HashMap();
+
+ this.allowedCommands = new HashSet();
+ }
+
+ /*
+ * /////////////////////////////////////////////////////////////////////////
+ * // // NEW METHODS IN REFACTORING //
+ * /////////////////////////////////////////////////////////////////////////
+ */
+
+ public MobArena getPlugin() {
+ return plugin;
+ }
+
+ public boolean isEnabled() {
+ return config.getBoolean("global-settings.enabled", true);
+ }
+
+ public void setEnabled(boolean value) {
+ config.set("global-settings.enabled", value);
+ }
+
+ public boolean notifyOnUpdates() {
+ return config.getBoolean("global-settings.update-notification", false);
+ }
+
+ public Arena getSelectedArena() {
+ return selectedArena;
+ }
+
+ public void setSelectedArena(Arena arena) {
+ selectedArena = arena;
+ }
+
+ public List getArenas() {
+ return arenas;
+ }
+
+ public Map getClasses() {
+ return classes;
+ }
+
+ public void addPlayer(Player p, Arena arena) {
+ arenaMap.put(p, arena);
+ }
+
+ public Arena removePlayer(Player p) {
+ return arenaMap.remove(p);
+ }
+
+ public void resetArenaMap() {
+ arenaMap.clear();
+ }
+
+ public boolean isAllowed(String command) {
+ return allowedCommands.contains(command);
+ }
+
+ /*
+ * /////////////////////////////////////////////////////////////////////////
+ * // // Arena getters //
+ * /////////////////////////////////////////////////////////////////////////
+ */
+
+ public List getEnabledArenas() {
+ List result = new LinkedList();
+ for (Arena arena : arenas)
+ if (arena.isEnabled())
+ result.add(arena);
+ return result;
+ }
+
+ public List getPermittedArenas(Player p) {
+ List result = new LinkedList();
+ for (Arena arena : arenas)
+ if (plugin.has(p, "mobarena.arenas." + arena.configName()))
+ result.add(arena);
+ return result;
+ }
+
+ public List getEnabledAndPermittedArenas(Player p) {
+ List result = new LinkedList();
+ for (Arena arena : arenas)
+ if (arena.isEnabled() && plugin.has(p, "mobarena.arenas." + arena.configName()))
+ result.add(arena);
+ return result;
+ }
+
+ public Arena getArenaAtLocation(Location loc) {
+ for (Arena arena : arenas)
+ if (arena.getRegion().contains(loc))
+ return arena;
+ return null;
+ }
+
+ public List getArenasInWorld(World world) {
+ List result = new LinkedList();
+ for (Arena arena : arenas)
+ if (arena.getWorld().equals(world))
+ result.add(arena);
+ return result;
+ }
+
+ public List getAllPlayers() {
+ List result = new LinkedList();
+ for (Arena arena : arenas)
+ result.addAll(arena.getAllPlayers());
+ return result;
+ }
+
+ public List getAllPlayersInArena(String arenaName) {
+ Arena arena = getArenaWithName(arenaName);
+ return (arena != null) ? new LinkedList(arena.getPlayersInArena()) : new LinkedList();
+ }
+
+ public List getAllLivingPlayers() {
+ List result = new LinkedList();
+ for (Arena arena : arenas)
+ result.addAll(arena.getPlayersInArena());
+ return result;
+ }
+
+ public List getLivingPlayersInArena(String arenaName) {
+ Arena arena = getArenaWithName(arenaName);
+ return (arena != null) ? new LinkedList(arena.getPlayersInArena()) : new LinkedList();
+ }
+
+ public Arena getArenaWithPlayer(Player p) {
+ return arenaMap.get(p);
+ }
+
+ public Arena getArenaWithPlayer(String playerName) {
+ return arenaMap.get(plugin.getServer().getPlayer(playerName));
+ }
+
+ public Arena getArenaWithSpectator(Player p) {
+ for (Arena arena : arenas) {
+ if (arena.getSpectators().contains(p))
+ return arena;
+ }
+ return null;
+ }
+
+ public Arena getArenaWithMonster(Entity e) {
+ for (Arena arena : arenas)
+ if (arena.getMonsterManager().getMonsters().contains(e))
+ return arena;
+ return null;
+ }
+
+ public Arena getArenaWithPet(Entity e) {
+ for (Arena arena : arenas)
+ if (arena.hasPet(e))
+ return arena;
+ return null;
+ }
+
+ public Arena getArenaWithName(String configName) {
+ return getArenaWithName(this.arenas, configName);
+ }
+
+ public Arena getArenaWithName(Collection arenas, String configName) {
+ for (Arena arena : arenas)
+ if (arena.configName().equals(configName))
+ return arena;
+ return null;
+ }
+
+ /*
+ * /////////////////////////////////////////////////////////////////////////
+ * // // Initialization //
+ * /////////////////////////////////////////////////////////////////////////
+ */
+
+ public void initialize() {
+ config.load();
+ loadSettings();
+ loadClasses();
+ loadArenas();
+ config.save();
+ }
+
+ /**
+ * Load the global settings.
+ */
+ public void loadSettings() {
+ ConfigUtils.replaceAllNodes(plugin, config, "global-settings", "global-settings.yml");
+ ConfigSection section = config.getConfigSection("global-settings");
+
+ // Grab the commands string
+ String cmds = section.getString("allowed-commands", "");
+
+ // Split by commas
+ String[] parts = cmds.split(",");
+
+ // Add in the /ma command.
+ allowedCommands.add("/ma");
+
+ // Add in each command
+ for (String part : parts) {
+ allowedCommands.add(part.trim().toLowerCase());
+ }
+ }
+
+ /**
+ * Load all class-related stuff.
+ */
+ public void loadClasses() {
+ Set classNames = config.getKeys("classes");
+
+ // If no classes were found, load the defaults.
+ if (classNames == null || classNames.isEmpty()) {
+ loadDefaultClasses();
+ classNames = config.getKeys("classes");
+ }
+
+ // Establish the map.
+ classes = new HashMap();
+
+ // Load each individual class.
+ for (String className : classNames) {
+ loadClass(className.toLowerCase());
+ }
+ }
+
+ /**
+ * Loads the classes in res/classes.yml into the config-file.
+ */
+ public void loadDefaultClasses() {
+ ConfigUtils.addMissingNodes(plugin, config, "classes", "classes.yml");
+ }
+
+ /**
+ * Helper method for loading a single class.
+ */
+ private ArenaClass loadClass(String className) {
+ // Grab the class section.
+ ConfigSection section = config.getConfigSection("classes." + TextUtils.camelCase(className));
+
+ // If the section doesn't exist, the class doesn't either.
+ if (section == null) {
+ plugin.error("Failed to load class '" + TextUtils.camelCase(className) + "'.");
+ return null;
+ }
+
+ ArenaClass arenaClass = new ArenaClass(className);
+
+ // Parse the items-node
+ String items = section.getString("items", "");
+ if (!items.equals("")) {
+ List stacks = ItemParser.parseItems(items);
+ arenaClass.setItems(stacks);
+ }
+
+ // And the legacy armor-node
+ String armor = section.getString("armor", "");
+ if (!armor.equals("")) {
+ List stacks = ItemParser.parseItems(armor);
+ arenaClass.setArmor(stacks);
+ }
+
+ // Get armor strings
+ String head = section.getString("helmet", null);
+ String chest = section.getString("chestplate", null);
+ String legs = section.getString("leggings", null);
+ String feet = section.getString("boots", null);
+
+ // Parse to ItemStacks
+ ItemStack helmet = ItemParser.parseItem(head);
+ ItemStack chestplate = ItemParser.parseItem(chest);
+ ItemStack leggings = ItemParser.parseItem(legs);
+ ItemStack boots = ItemParser.parseItem(feet);
+
+ // Set in ArenaClass
+ arenaClass.setHelmet(helmet);
+ arenaClass.setChestplate(chestplate);
+ arenaClass.setLeggings(leggings);
+ arenaClass.setBoots(boots);
+
+ // Per-class permissions
+ loadClassPermissions(arenaClass, section);
+
+ // Register the permission.
+ registerPermission("mobarena.classes." + className, PermissionDefault.TRUE).addParent("mobarena.classes", true);
+
+ // Finally add the class to the classes map.
+ classes.put(className, arenaClass);
+ return arenaClass;
+ }
+
+ private void loadClassPermissions(ArenaClass arenaClass, ConfigSection section) {
+ List perms = section.getStringList("permissions", null);
+ if (perms.isEmpty())
+ return;
+
+ for (String perm : perms) {
+ // If the permission starts with - or ^, it must be revoked.
+ boolean value = true;
+ if (perm.startsWith("-") || perm.startsWith("^")) {
+ perm = perm.substring(1).trim();
+ value = false;
+ }
+ arenaClass.addPermission(perm, value);
+ }
+ }
+
+ public ArenaClass createClassNode(String className, PlayerInventory inv, boolean safe) {
+ String path = "classes." + TextUtils.camelCase(className);
+ if (safe && config.getConfigSection(path) != null) {
+ return null;
+ }
+
+ // Create the node.
+ config.set(path, "");
+
+ // Grab the section.
+ ConfigSection section = config.getConfigSection(path);
+
+ // Take the current items and armor.
+ section.set("items", ItemParser.parseString(inv.getContents()));
+ section.set("armor", ItemParser.parseString(inv.getArmorContents()));
+
+ // If the helmet isn't a real helmet, set it explicitly.
+ ItemStack helmet = inv.getHelmet();
+ if (helmet.getTypeId() != 0 && ArmorType.getType(helmet) != ArmorType.HELMET) {
+ section.set("helmet", ItemParser.parseString(helmet));
+ }
+
+ // Save changes.
+ config.save();
+
+ // Load the class
+ return loadClass(className.toLowerCase());
+ }
+
+ public void removeClassNode(String className) {
+ if (!classes.containsKey(className.toLowerCase()))
+ throw new IllegalArgumentException("Class does not exist!");
+
+ // Remove the class from the config-file and save it.
+ config.remove("classes." + className);
+ config.save();
+
+ // Remove the class from the map.
+ classes.remove(className.toLowerCase());
+
+ unregisterPermission("mobarena.arenas." + className.toLowerCase());
+ }
+
+ public boolean addClassPermission(String className, String perm) {
+ return addRemoveClassPermission(className, perm, true);
+ }
+
+ public boolean removeClassPermission(String className, String perm) {
+ return addRemoveClassPermission(className, perm, false);
+ }
+
+ private boolean addRemoveClassPermission(String className, String perm, boolean add) {
+ String path = "classes." + TextUtils.camelCase(className);
+ if (config.getConfigSection(path) == null)
+ return false;
+
+ // Grab the class section
+ ConfigSection section = config.getConfigSection(path);
+
+ // Get any previous nodes
+ List nodes = section.getStringList("permissions", null);
+ if (nodes.contains(perm))
+ return false;
+
+ // Add or remove.
+ if (add) {
+ removeContradictions(nodes, perm);
+ nodes.add(perm);
+ }
+ else {
+ nodes.remove(perm);
+ }
+
+ // Replace the set.
+ section.set("permissions", nodes);
+ config.save();
+
+ // Reload the class.
+ loadClass(className);
+ return true;
+ }
+
+ /**
+ * Removes any nodes that would contradict the permission, e.g. if the node
+ * 'mobarena.use' is in the set, and the perm node is '-mobarena.use', the
+ * '-mobarena.use' node is removed as to not contradict the new
+ * 'mobarena.use' node.
+ */
+ private void removeContradictions(List nodes, String perm) {
+ if (perm.startsWith("^") || perm.startsWith("-")) {
+ nodes.remove(perm.substring(1).trim());
+ }
+ else {
+ nodes.remove("^" + perm);
+ nodes.remove("-" + perm);
+ }
+ }
+
+ /**
+ * Load all arena-related stuff.
+ */
+ public void loadArenas() {
+ Set arenaNames = config.getKeys("arenas");
+
+ // If no arenas were found, create a default node.
+ if (arenaNames == null || arenaNames.isEmpty()) {
+ createArenaNode("default", plugin.getServer().getWorlds().get(0));
+ arenaNames = config.getKeys("arenas");
+ }
+
+ // Establish the list.
+ arenas = new LinkedList();
+
+ for (String arenaName : arenaNames) {
+ loadArena(arenaName);
+ }
+
+ selectedArena = arenas.get(0);
+ }
+
+ private Arena loadArena(String arenaName) {
+ String path = "arenas." + arenaName;
+ String worldName = config.getString(path + ".settings.world", "");
+ World world = null;
+
+ // If a string was found, try to fetch the world from the server.
+ if (!worldName.equals("")) {
+ world = plugin.getServer().getWorld(worldName);
+
+ if (world == null) {
+ plugin.error("The world '" + worldName + "' for arena '" + arenaName + "' does not exist!");
+ return null;
+ }
+ }
+ // Otherwise, use the default world.
+ else {
+ world = plugin.getServer().getWorlds().get(0);
+ plugin.warning("Could not find the world for arena '" + arenaName + "'. Using default world ('" + world.getName() + "')! Check the config-file!");
+ }
+
+ // Assert all settings nodes.
+ ConfigUtils.replaceAllNodes(plugin, config, path + ".settings", "settings.yml");
+
+ // Create an Arena with the name and world.
+ Arena arena = new ArenaImpl(plugin, config, arenaName, world);
+
+ // Register the permission
+ registerPermission("mobarena.arenas." + arenaName, PermissionDefault.TRUE);
+
+ // Finally, add it to the arena list.
+ arenas.add(arena);
+ return arena;
+ }
+
+ public Arena createArenaNode(String arenaName, World world) {
+ String path = "arenas." + arenaName;
+ if (config.getConfigSection(path) != null)
+ throw new IllegalArgumentException("Arena already exists!");
+
+ // Extract the default settings and update the world-node.
+ ConfigUtils.replaceAllNodes(plugin, config, path + ".settings", "settings.yml");
+ config.set(path + ".settings.world", world.getName());
+
+ // Extract the default waves.
+ ConfigUtils.replaceAllNodes(plugin, config, path + ".waves", "waves.yml");
+
+ // Extract the default rewards.
+ ConfigUtils.replaceAllNodes(plugin, config, path + ".rewards", "rewards.yml");
+
+ // Save the changes.
+ config.save();
+
+ // Load the arena
+ return loadArena(arenaName);
+ }
+
+ public void removeArenaNode(Arena arena) {
+ // Remove the arena from the config-file and save it.
+ config.remove("arenas." + arena.configName());
+ config.save();
+
+ // Remove the arena from the list.
+ arenas.remove(arena);
+
+ unregisterPermission("mobarena.arenas." + arena.configName());
+ }
+
+ public void reloadConfig() {
+ boolean wasEnabled = isEnabled();
+
+ // If MobArena was enabled, disable it before updating.
+ if (wasEnabled) {
+ setEnabled(false);
+ }
+
+ for (Arena a : arenas) {
+ a.forceEnd();
+ }
+
+ config.load();
+ loadSettings();
+ loadClasses();
+ loadArenas();
+
+ // If MobArena was enabled, re-enable it after updating.
+ if (wasEnabled) {
+ setEnabled(true);
+ }
+ }
+
+ public void saveConfig() {
+ config.save();
+ }
+
+ private Permission registerPermission(String permString, PermissionDefault value) {
+ PluginManager pm = plugin.getServer().getPluginManager();
+
+ Permission perm = pm.getPermission(permString);
+ if (perm == null) {
+ perm = new Permission(permString);
+ perm.setDefault(value);
+ pm.addPermission(perm);
+ }
+ return perm;
+ }
+
+ private void unregisterPermission(String s) {
+ plugin.getServer().getPluginManager().removePermission(s);
+ }
+}
diff --git a/src/com/garbagemule/MobArena/ArenaMasterStandard.java b/src/com/garbagemule/MobArena/ArenaMasterStandard.java
deleted file mode 100644
index e89fff6..0000000
--- a/src/com/garbagemule/MobArena/ArenaMasterStandard.java
+++ /dev/null
@@ -1,388 +0,0 @@
-package com.garbagemule.MobArena;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-import org.bukkit.Bukkit;
-import org.bukkit.Location;
-import org.bukkit.World;
-import org.bukkit.entity.Entity;
-import org.bukkit.entity.Player;
-
-import com.garbagemule.MobArena.util.Config;
-
-public class ArenaMasterStandard extends ArenaMaster
-{
- private MobArena plugin;
- private Config config;
-
-
-
- /**
- * Default constructor.
- */
- public ArenaMasterStandard(MobArenaPlugin instance)
- {
- plugin = (MobArena) instance;
- config = plugin.getMAConfig();
- arenas = new LinkedList();
- arenaMap = new HashMap();
- }
-
-
-
- /*/////////////////////////////////////////////////////////////////////////
- //
- // Arena getters
- //
- /////////////////////////////////////////////////////////////////////////*/
-
- public List getEnabledArenas()
- {
- List result = new LinkedList();
- for (Arena arena : arenas)
- if (arena.enabled)
- result.add(arena);
- return result;
- }
-
- public List getPermittedArenas(Player p)
- {
- List result = new LinkedList();
- for (Arena arena : arenas)
- if (plugin.has(p, "mobarena.arenas." + arena.configName()))
- result.add(arena);
- return result;
- }
-
- public List getEnabledAndPermittedArenas(Player p)
- {
- List result = new LinkedList();
- for (Arena arena : arenas)
- if (arena.enabled && plugin.has(p, "mobarena.arenas." + arena.configName()))
- result.add(arena);
- return result;
- }
-
- public Arena getArenaAtLocation(Location loc)
- {
- for (Arena arena : arenas)
- if (arena.inRegion(loc))
- return arena;
- return null;
- }
-
- public List getArenasInWorld(World world)
- {
- List result = new LinkedList();
- for (Arena arena : arenas)
- if (arena.world.equals(world))
- result.add(arena);
- return result;
- }
-
- public List getAllPlayers()
- {
- List result = new LinkedList();
- for (Arena arena : arenas)
- result.addAll(arena.getAllPlayers());
- return result;
- }
-
- public List getAllPlayersInArena(String arenaName)
- {
- Arena arena = getArenaWithName(arenaName);
- return (arena != null) ? arena.getLivingPlayers() : new LinkedList();
- }
-
- public List getAllLivingPlayers()
- {
- List result = new LinkedList();
- for (Arena arena : arenas)
- result.addAll(arena.getLivingPlayers());
- return result;
- }
-
- public List getLivingPlayersInArena(String arenaName)
- {
- Arena arena = getArenaWithName(arenaName);
- return (arena != null) ? arena.getLivingPlayers() : new LinkedList();
- }
-
- public Arena getArenaWithPlayer(Player p)
- {
- return arenaMap.get(p);
- }
-
- public Arena getArenaWithPlayer(String playerName)
- {
- return arenaMap.get(Bukkit.getServer().getPlayer(playerName));
- }
-
- public Arena getArenaWithSpectator(Player p)
- {
- for (Arena arena : arenas)
- {
- if (arena.specPlayers.contains(p))
- return arena;
- }
- return null;
- }
-
- public Arena getArenaWithMonster(Entity e)
- {
- for (Arena arena : arenas)
- if (arena.monsters.contains(e))
- return arena;
- return null;
- }
-
- public Arena getArenaWithPet(Entity e)
- {
- for (Arena arena : arenas)
- if (arena.pets.contains(e))
- return arena;
- return null;
- }
-
- public Arena getArenaWithName(String configName)
- {
- return getArenaWithName(this.arenas, configName);
- }
-
- public Arena getArenaWithName(Collection arenas, String configName)
- {
- for (Arena arena : arenas)
- if (arena.configName().equals(configName))
- return arena;
- return null;
- }
-
-
-
- /*/////////////////////////////////////////////////////////////////////////
- //
- // Initialization
- //
- /////////////////////////////////////////////////////////////////////////*/
-
- public void initialize()
- {
- config.load();
- loadSettings();
- loadClasses();
- loadArenas();
- config.save();
- }
-
- /**
- * Load the global settings.
- */
- public void loadSettings()
- {
- if (config.getKeys("global-settings") == null)
- {
- config.setProperty("global-settings.enabled", true);
- config.setProperty("global-settings.update-notification", true);
- }
-
- enabled = config.getBoolean("global-settings.enabled", true);
- updateNotify = config.getBoolean("global-settings.update-notification", true);
- }
-
- /**
- * Load all class-related stuff.
- */
- public void loadClasses()
- {
- classes = config.getKeys("classes");
- if (classes == null)
- {
- config.setProperty("classes.Archer.items", "wood_sword, bow, arrow:128, grilled_pork");
- config.setProperty("classes.Archer.armor", "298,299,300,301");
- config.setProperty("classes.Knight.items", "diamond_sword, grilled_pork:2");
- config.setProperty("classes.Knight.armor", "306,307,308,309");
- config.setProperty("classes.Tank.items", "iron_sword, grilled_pork:3, apple");
- config.setProperty("classes.Tank.armor", "310,311,312,313");
- config.setProperty("classes.Oddjob.items", "stone_sword, flint_and_steel, netherrack:2, wood_pickaxe, tnt:4, fishing_rod, apple, grilled_pork:3");
- config.setProperty("classes.Oddjob.armor", "298,299,300,301");
- config.setProperty("classes.Chef.items", "stone_sword, bread:6, grilled_pork:4, mushroom_soup, cake:3, cookie:12");
- config.setProperty("classes.Chef.armor", "314,315,316,317");
- classes = config.getKeys("classes");
- }
- classItems = MAUtils.getClassItems(config, "items");
- classArmor = MAUtils.getClassItems(config, "armor");
- classPerms = MAUtils.getClassPerms(config);
- }
-
- /**
- * Load all arena-related stuff.
- */
- public void loadArenas()
- {
- arenas = new LinkedList();
-
- if (config.getKeys("arenas") == null)
- createArenaNode("default", Bukkit.getServer().getWorlds().get(0));
-
- for (String configName : config.getKeys("arenas"))
- {
- String arenaPath = "arenas." + configName + ".";
- String worldName = config.getString(arenaPath + "settings.world", null);
- World world;
- if (worldName == null || worldName.equals(""))
- {
- MobArena.warning("Could not find the world for arena '" + configName + "'. Using default world! Check the config-file!");
- world = Bukkit.getServer().getWorlds().get(0);
- }
- else world = Bukkit.getServer().getWorld(worldName);
-
- Arena arena = new ArenaStandard(MAUtils.nameConfigToArena(configName), world);
- arena.load(config);
- arenas.add(arena);
- }
-
- selectedArena = arenas.get(0);
- }
-
- public Arena createArenaNode(String configName, World world)
- {
- config.setProperty("arenas." + configName + ".settings.world", world.getName());
- config.save();
- config.load();
- config.setProperty("arenas." + configName + ".settings.enabled", true);
- config.save();
- config.load();
- config.setProperty("arenas." + configName + ".settings.protect", true);
- config.save();
- config.load();
- config.setProperty("arenas." + configName + ".settings.entry-fee", "");
- config.save();
- config.load();
- config.setProperty("arenas." + configName + ".settings.logging", false);
- config.setProperty("arenas." + configName + ".settings.clear-wave-before-next", false);
- config.setProperty("arenas." + configName + ".settings.detonate-creepers", false);
- config.setProperty("arenas." + configName + ".settings.detonate-damage", false);
- config.setProperty("arenas." + configName + ".settings.lightning", true);
- config.setProperty("arenas." + configName + ".settings.auto-equip-armor", true);
- config.setProperty("arenas." + configName + ".settings.force-restore", false);
- config.setProperty("arenas." + configName + ".settings.soft-restore", false);
- config.setProperty("arenas." + configName + ".settings.soft-restore-drops", false);
- config.setProperty("arenas." + configName + ".settings.require-empty-inv-join", true);
- config.setProperty("arenas." + configName + ".settings.require-empty-inv-spec", true);
- config.setProperty("arenas." + configName + ".settings.hellhounds", false);
- config.setProperty("arenas." + configName + ".settings.pvp-enabled", false);
- config.setProperty("arenas." + configName + ".settings.monster-infight", false);
- config.setProperty("arenas." + configName + ".settings.allow-teleporting", false);
- config.setProperty("arenas." + configName + ".settings.spectate-on-death", true);
- config.setProperty("arenas." + configName + ".settings.share-items-in-arena", true);
- config.save();
- config.load();
- config.setProperty("arenas." + configName + ".settings.player-limit", 0);
- config.setProperty("arenas." + configName + ".settings.max-join-distance", 0);
- config.save();
- config.load();
- config.setProperty("arenas." + configName + ".settings.repair-delay", 5);
- config.setProperty("arenas." + configName + ".settings.first-wave-delay", 5);
- config.setProperty("arenas." + configName + ".settings.wave-interval", 20);
- config.setProperty("arenas." + configName + ".settings.special-modulo", 4);
- config.setProperty("arenas." + configName + ".settings.max-idle-time", 0);
- config.save();
- config.load();
-
- Arena arena = new ArenaStandard(MAUtils.nameConfigToArena(configName), world);
- arena.load(config);
- return arena;
- }
-
- public void removeArenaNode(String configName)
- {
- config.removeProperty("arenas." + configName);
- config.save();
- }
-
-
-
- /*/////////////////////////////////////////////////////////////////////////
- //
- // Update and serialization methods
- //
- /////////////////////////////////////////////////////////////////////////*/
-
- /**
- * Update one, two or all three of global settings, classes
- * and arenas (arenas with deserialization).
- */
- public void update(boolean settings, boolean classes, boolean arenalist)
- {
- boolean tmp = enabled;
- enabled = false;
-
- for (Arena arena : arenas)
- arena.forceEnd();
-
- config.load();
- if (settings) loadSettings();
- if (classes) loadClasses();
- if (arenalist) deserializeArenas();
- config.save();
-
- enabled = tmp;
- }
-
- /**
- * Serialize the global settings.
- */
- public void serializeSettings()
- {
- String settings = "global-settings.";
- config.setProperty(settings + "enabled", enabled);
- config.save();
- }
-
- /**
- * Serialize all arena configs.
- */
- public void serializeArenas()
- {
- for (Arena arena : arenas)
- arena.serializeConfig();
- }
-
- /**
- * Deserialize all arena configs. Updates the arena list to
- * include only the current arenas (not ones added in the
- * actual file) that are also in the config-file.
- */
- public void deserializeArenas()
- {
- // Get only the arenas in the config.
- Set strings = config.getKeys("arenas");
- if (strings == null)
- return;
-
- // Get their Arena objects.
- List configArenas = new LinkedList();
- for (String s : strings)
- if (getArenaWithName(s) != null)
- configArenas.add(getArenaWithName(s));
-
- // Remove all Arenas no longer in the config.
- arenas.retainAll(configArenas);
-
- for (Arena arena : arenas)
- arena.deserializeConfig();
-
- // Make sure to update the selected arena to a valid one.
- if (!arenas.contains(selectedArena) && arenas.size() >= 1)
- selectedArena = arenas.get(0);
- }
-
- public void updateSettings() { update(true, false, false); }
- public void updateClasses() { update(false, true, false); }
- public void updateArenas() { update(false, false, true); }
- public void updateAll() { update(true, true, true); }
-}
diff --git a/src/com/garbagemule/MobArena/ArenaPlayer.java b/src/com/garbagemule/MobArena/ArenaPlayer.java
index 359eae8..9d84cb2 100644
--- a/src/com/garbagemule/MobArena/ArenaPlayer.java
+++ b/src/com/garbagemule/MobArena/ArenaPlayer.java
@@ -1,59 +1,63 @@
package com.garbagemule.MobArena;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.bukkit.block.Block;
import org.bukkit.entity.Player;
-import org.bukkit.inventory.ItemStack;
+
+import com.garbagemule.MobArena.ArenaPlayerStatistics;
+import com.garbagemule.MobArena.MobArena;
+import com.garbagemule.MobArena.framework.Arena;
public class ArenaPlayer
{
- public MobArena plugin;
- public Player player;
- public String className;
- public Arena arena;
- public List rewards;
- public List blocks;
-
+ private Player player;
+
+ private ArenaClass arenaClass;
private ArenaPlayerStatistics stats;
-
- protected boolean isDead, inArena, inLobby, inSpec, isReady;
-
- public ArenaPlayer(Player player, Arena arena, MobArena plugin)
- {
+ private boolean isDead;
+
+ //private List rewards;
+ //private List blocks;
+
+ 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();
- blocks = new LinkedList();
-
- stats = new ArenaPlayerStatistics(this);
}
-
- public Player getPlayer() { return player; }
- public Arena getArena() { return arena; }
- public String getClassName() { return className; }
-
+
+ public Player getPlayer() {
+ return player;
+ }
+
+ public ArenaClass getArenaClass() {
+ return arenaClass;
+ }
+
+ public void setArenaClass(ArenaClass arenaClass) {
+ this.arenaClass = arenaClass;
+ }
+
/**
* Check if the player is "dead", i.e. died or not.
* @return true, if the player is either a spectator or played and died, false otherwise
*/
- public boolean isDead()
- {
+ public boolean isDead() {
return isDead;
}
-
+
/**
* Set the player's death status.
* @param value true, if the player is dead, false otherwise
*/
- public void setDead(boolean value)
- {
+ public void setDead(boolean value) {
isDead = value;
}
-
- public ArenaPlayerStatistics getStats() { return stats; }
+
+ public void resetStats() {
+ if (stats != null) {
+ stats.reset();
+ return;
+ }
+ stats = new ArenaPlayerStatistics(this);
+ }
+
+ public ArenaPlayerStatistics getStats() {
+ return stats;
+ }
}
diff --git a/src/com/garbagemule/MobArena/ArenaPlayerStatistics.java b/src/com/garbagemule/MobArena/ArenaPlayerStatistics.java
index 8b0289e..30792cf 100644
--- a/src/com/garbagemule/MobArena/ArenaPlayerStatistics.java
+++ b/src/com/garbagemule/MobArena/ArenaPlayerStatistics.java
@@ -1,91 +1,130 @@
package com.garbagemule.MobArena;
import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
import org.bukkit.entity.Player;
+import com.garbagemule.MobArena.ArenaPlayer;
+import com.garbagemule.MobArena.ArenaPlayerStatistics;
+import com.garbagemule.MobArena.MobArena;
+import com.garbagemule.MobArena.framework.Arena;
+import com.garbagemule.MobArena.util.MutableInt;
+
public class ArenaPlayerStatistics
{
- private String playerName, className;
private ArenaPlayer player;
- public int kills, dmgDone, dmgTaken, swings, hits, lastWave;
-
- public ArenaPlayerStatistics(ArenaPlayer player)
- {
- this.player = player;
+ private String playerName, className;
+ private Map ints;
+
+ public ArenaPlayerStatistics(ArenaPlayer player) {
+ this.player = player;
this.playerName = player.getPlayer().getName();
- this.className = player.getClassName();
+ this.className = player.getArenaClass().getName();
+ reset();
}
-
- public ArenaPlayerStatistics(Player p, Arena arena, MobArena plugin)
- {
+
+ public void reset() {
+ if (ints == null) {
+ ints = new HashMap();
+ }
+
+ ints.clear();
+
+ ints.put("kills", new MutableInt());
+ ints.put("dmgDone", new MutableInt());
+ ints.put("dmgTaken", new MutableInt());
+ ints.put("swings", new MutableInt());
+ ints.put("hits", new MutableInt());
+ ints.put("lastWave", new MutableInt());
+ }
+
+ public ArenaPlayerStatistics(Player p, Arena arena, MobArena plugin) {
this(new ArenaPlayer(p, arena, plugin));
}
-
- public ArenaPlayer getArenaPlayer()
- {
+
+ public ArenaPlayer getArenaPlayer() {
return player;
}
-
- public static Comparator killComparator()
- {
- return new Comparator()
- {
- public int compare(ArenaPlayerStatistics s1, ArenaPlayerStatistics s2)
- {
- if (s1.kills > s2.kills)
- return -1;
- else if (s1.kills < s2.kills)
- return 1;
+
+ public String getPlayerName() {
+ return playerName;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public int getInt(String s) {
+ return ints.get(s).value();
+ }
+
+ public void inc(String s) {
+ ints.get(s).inc();
+ }
+
+ public void add(String s, int amount) {
+ ints.get(s).add(amount);
+ }
+
+ public static Comparator killComparator() {
+ return new Comparator() {
+ public int compare(ArenaPlayerStatistics s1, ArenaPlayerStatistics s2) {
+ int s1kills = s1.getInt("kills");
+ int s2kills = s2.getInt("kills");
+
+ if (s1kills == s2kills)
return 0;
- }
- };
+
+ return (s1kills > s2kills ? -1 : 1);
+ }
+ };
}
-
- private static int compareKills(ArenaPlayerStatistics s1, ArenaPlayerStatistics s2)
- {
- if (s1.kills > s2.kills)
- return -1;
- else if (s1.kills < s2.kills)
- return 1;
- return 0;
+
+ public static Comparator waveComparator() {
+ return new Comparator() {
+ public int compare(ArenaPlayerStatistics s1, ArenaPlayerStatistics s2) {
+ int result = compareWaves(s1, s2);
+ if (result != 0)
+ return result;
+
+ return compareKills(s1, s2);
+ }
+ };
}
-
- public static Comparator waveComparator()
- {
- return new Comparator()
- {
- public int compare(ArenaPlayerStatistics s1, ArenaPlayerStatistics s2)
- {
- int result = compareWaves(s1, s2);
- if (result != 0) return result;
-
- return compareKills(s1, s2);
- }
- };
- }
-
- private static int compareWaves(ArenaPlayerStatistics s1, ArenaPlayerStatistics s2)
- {
- if (s1.lastWave > s2.lastWave)
- return -1;
- else if (s1.lastWave < s2.lastWave)
- return 1;
- return 0;
- }
-
- public static Comparator dmgDoneComparator()
- {
- return new Comparator()
- {
- public int compare(ArenaPlayerStatistics s1, ArenaPlayerStatistics s2)
- {
- if (s1.dmgDone > s2.dmgDone)
- return -1;
- else if (s1.dmgDone < s2.dmgDone)
- return 1;
+
+ public static Comparator dmgDoneComparator() {
+ return new Comparator