diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..317d498 --- /dev/null +++ b/.classpath @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..4492ba2 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + MobArena + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/MobArena.jar b/MobArena.jar index c6a9474..b402b24 100644 Binary files a/MobArena.jar and b/MobArena.jar differ diff --git a/README b/README index e69de29..20207f1 100644 --- a/README +++ b/README @@ -0,0 +1 @@ +Go to the Wiki for more info: https://github.com/garbagemule/MobArena/wiki/dev-v0.94 \ No newline at end of file diff --git a/bin/com/garbagemule/MobArena/AbstractArena.class b/bin/com/garbagemule/MobArena/AbstractArena.class new file mode 100644 index 0000000..53a08d1 Binary files /dev/null and b/bin/com/garbagemule/MobArena/AbstractArena.class differ diff --git a/bin/com/garbagemule/MobArena/Arena$1.class b/bin/com/garbagemule/MobArena/Arena$1.class new file mode 100644 index 0000000..93acaa2 Binary files /dev/null and b/bin/com/garbagemule/MobArena/Arena$1.class differ diff --git a/bin/com/garbagemule/MobArena/Arena$2.class b/bin/com/garbagemule/MobArena/Arena$2.class new file mode 100644 index 0000000..303d9dc Binary files /dev/null and b/bin/com/garbagemule/MobArena/Arena$2.class differ diff --git a/bin/com/garbagemule/MobArena/Arena$3.class b/bin/com/garbagemule/MobArena/Arena$3.class new file mode 100644 index 0000000..547ab96 Binary files /dev/null and b/bin/com/garbagemule/MobArena/Arena$3.class differ diff --git a/bin/com/garbagemule/MobArena/Arena$4.class b/bin/com/garbagemule/MobArena/Arena$4.class new file mode 100644 index 0000000..7b12807 Binary files /dev/null and b/bin/com/garbagemule/MobArena/Arena$4.class differ diff --git a/bin/com/garbagemule/MobArena/Arena.class b/bin/com/garbagemule/MobArena/Arena.class new file mode 100644 index 0000000..0a93d08 Binary files /dev/null and b/bin/com/garbagemule/MobArena/Arena.class differ diff --git a/bin/com/garbagemule/MobArena/ArenaListener.class b/bin/com/garbagemule/MobArena/ArenaListener.class new file mode 100644 index 0000000..2b8b0de Binary files /dev/null and b/bin/com/garbagemule/MobArena/ArenaListener.class differ diff --git a/bin/com/garbagemule/MobArena/ArenaLog.class b/bin/com/garbagemule/MobArena/ArenaLog.class new file mode 100644 index 0000000..ce6f336 Binary files /dev/null and b/bin/com/garbagemule/MobArena/ArenaLog.class differ diff --git a/bin/com/garbagemule/MobArena/ArenaMaster.class b/bin/com/garbagemule/MobArena/ArenaMaster.class new file mode 100644 index 0000000..dcadb28 Binary files /dev/null and b/bin/com/garbagemule/MobArena/ArenaMaster.class differ diff --git a/bin/com/garbagemule/MobArena/ArenaPlayer.class b/bin/com/garbagemule/MobArena/ArenaPlayer.class new file mode 100644 index 0000000..fb61099 Binary files /dev/null and b/bin/com/garbagemule/MobArena/ArenaPlayer.class differ diff --git a/bin/com/garbagemule/MobArena/EntityPosition.class b/bin/com/garbagemule/MobArena/EntityPosition.class new file mode 100644 index 0000000..c72ef1e Binary files /dev/null and b/bin/com/garbagemule/MobArena/EntityPosition.class differ diff --git a/bin/com/garbagemule/MobArena/Lobby.class b/bin/com/garbagemule/MobArena/Lobby.class new file mode 100644 index 0000000..6ea0ea7 Binary files /dev/null and b/bin/com/garbagemule/MobArena/Lobby.class differ diff --git a/bin/com/garbagemule/MobArena/MABlockListener.class b/bin/com/garbagemule/MobArena/MABlockListener.class new file mode 100644 index 0000000..9cf7d09 Binary files /dev/null and b/bin/com/garbagemule/MobArena/MABlockListener.class differ diff --git a/bin/com/garbagemule/MobArena/MACommands$1.class b/bin/com/garbagemule/MobArena/MACommands$1.class new file mode 100644 index 0000000..f16e4a2 Binary files /dev/null and b/bin/com/garbagemule/MobArena/MACommands$1.class differ diff --git a/bin/com/garbagemule/MobArena/MACommands.class b/bin/com/garbagemule/MobArena/MACommands.class new file mode 100644 index 0000000..b4ae3cc Binary files /dev/null and b/bin/com/garbagemule/MobArena/MACommands.class differ diff --git a/bin/com/garbagemule/MobArena/MAEntityListener.class b/bin/com/garbagemule/MobArena/MAEntityListener.class new file mode 100644 index 0000000..5f2b77f Binary files /dev/null and b/bin/com/garbagemule/MobArena/MAEntityListener.class differ diff --git a/bin/com/garbagemule/MobArena/MAInventoryItem.class b/bin/com/garbagemule/MobArena/MAInventoryItem.class new file mode 100644 index 0000000..042cf79 Binary files /dev/null and b/bin/com/garbagemule/MobArena/MAInventoryItem.class differ diff --git a/bin/com/garbagemule/MobArena/MAListener$1.class b/bin/com/garbagemule/MobArena/MAListener$1.class new file mode 100644 index 0000000..75e5e85 Binary files /dev/null and b/bin/com/garbagemule/MobArena/MAListener$1.class differ diff --git a/bin/com/garbagemule/MobArena/MAListener.class b/bin/com/garbagemule/MobArena/MAListener.class new file mode 100644 index 0000000..2aa6d48 Binary files /dev/null and b/bin/com/garbagemule/MobArena/MAListener.class differ diff --git a/bin/com/garbagemule/MobArena/MAMessages$Msg.class b/bin/com/garbagemule/MobArena/MAMessages$Msg.class new file mode 100644 index 0000000..f4739d1 Binary files /dev/null and b/bin/com/garbagemule/MobArena/MAMessages$Msg.class differ diff --git a/bin/com/garbagemule/MobArena/MAMessages.class b/bin/com/garbagemule/MobArena/MAMessages.class new file mode 100644 index 0000000..d656679 Binary files /dev/null and b/bin/com/garbagemule/MobArena/MAMessages.class differ diff --git a/bin/com/garbagemule/MobArena/MAPlayerListener$1.class b/bin/com/garbagemule/MobArena/MAPlayerListener$1.class new file mode 100644 index 0000000..28b8ff2 Binary files /dev/null and b/bin/com/garbagemule/MobArena/MAPlayerListener$1.class differ diff --git a/bin/com/garbagemule/MobArena/MAPlayerListener.class b/bin/com/garbagemule/MobArena/MAPlayerListener.class new file mode 100644 index 0000000..072a7ae Binary files /dev/null and b/bin/com/garbagemule/MobArena/MAPlayerListener.class differ diff --git a/bin/com/garbagemule/MobArena/MASpawnThread.class b/bin/com/garbagemule/MobArena/MASpawnThread.class new file mode 100644 index 0000000..1b17ce2 Binary files /dev/null and b/bin/com/garbagemule/MobArena/MASpawnThread.class differ diff --git a/bin/com/garbagemule/MobArena/MAUtils.class b/bin/com/garbagemule/MobArena/MAUtils.class new file mode 100644 index 0000000..b0109f0 Binary files /dev/null and b/bin/com/garbagemule/MobArena/MAUtils.class differ diff --git a/bin/com/garbagemule/MobArena/MobArena.class b/bin/com/garbagemule/MobArena/MobArena.class new file mode 100644 index 0000000..9de2775 Binary files /dev/null and b/bin/com/garbagemule/MobArena/MobArena.class differ diff --git a/bin/com/garbagemule/MobArena/MobArenaHandler.class b/bin/com/garbagemule/MobArena/MobArenaHandler.class new file mode 100644 index 0000000..8e9e7ac Binary files /dev/null and b/bin/com/garbagemule/MobArena/MobArenaHandler.class differ diff --git a/bin/com/garbagemule/MobArena/MobArenaListener.class b/bin/com/garbagemule/MobArena/MobArenaListener.class new file mode 100644 index 0000000..36cfa60 Binary files /dev/null and b/bin/com/garbagemule/MobArena/MobArenaListener.class differ diff --git a/bin/com/garbagemule/MobArena/util/FileUtils$Libs.class b/bin/com/garbagemule/MobArena/util/FileUtils$Libs.class new file mode 100644 index 0000000..e56949d Binary files /dev/null and b/bin/com/garbagemule/MobArena/util/FileUtils$Libs.class differ diff --git a/bin/com/garbagemule/MobArena/util/FileUtils.class b/bin/com/garbagemule/MobArena/util/FileUtils.class new file mode 100644 index 0000000..6a5c699 Binary files /dev/null and b/bin/com/garbagemule/MobArena/util/FileUtils.class differ diff --git a/bin/com/garbagemule/MobArena/util/TextUtils.class b/bin/com/garbagemule/MobArena/util/TextUtils.class new file mode 100644 index 0000000..692f0de Binary files /dev/null and b/bin/com/garbagemule/MobArena/util/TextUtils.class differ diff --git a/bin/com/garbagemule/MobArena/util/WaveUtils$1.class b/bin/com/garbagemule/MobArena/util/WaveUtils$1.class new file mode 100644 index 0000000..453805c Binary files /dev/null and b/bin/com/garbagemule/MobArena/util/WaveUtils$1.class differ diff --git a/bin/com/garbagemule/MobArena/util/WaveUtils$2.class b/bin/com/garbagemule/MobArena/util/WaveUtils$2.class new file mode 100644 index 0000000..97c39dd Binary files /dev/null and b/bin/com/garbagemule/MobArena/util/WaveUtils$2.class differ diff --git a/bin/com/garbagemule/MobArena/util/WaveUtils.class b/bin/com/garbagemule/MobArena/util/WaveUtils.class new file mode 100644 index 0000000..d1db0d1 Binary files /dev/null and b/bin/com/garbagemule/MobArena/util/WaveUtils.class differ diff --git a/bin/com/garbagemule/MobArena/util/data/PlainText.class b/bin/com/garbagemule/MobArena/util/data/PlainText.class new file mode 100644 index 0000000..35a2169 Binary files /dev/null and b/bin/com/garbagemule/MobArena/util/data/PlainText.class differ diff --git a/bin/com/garbagemule/MobArena/util/data/Totals.class b/bin/com/garbagemule/MobArena/util/data/Totals.class new file mode 100644 index 0000000..97d116a Binary files /dev/null and b/bin/com/garbagemule/MobArena/util/data/Totals.class differ diff --git a/bin/com/garbagemule/MobArena/util/data/XML.class b/bin/com/garbagemule/MobArena/util/data/XML.class new file mode 100644 index 0000000..2af1485 Binary files /dev/null and b/bin/com/garbagemule/MobArena/util/data/XML.class differ diff --git a/bin/com/garbagemule/MobArena/waves/AbstractWave.class b/bin/com/garbagemule/MobArena/waves/AbstractWave.class new file mode 100644 index 0000000..c6f3156 Binary files /dev/null and b/bin/com/garbagemule/MobArena/waves/AbstractWave.class differ diff --git a/bin/com/garbagemule/MobArena/waves/BossWave.class b/bin/com/garbagemule/MobArena/waves/BossWave.class new file mode 100644 index 0000000..c954ea8 Binary files /dev/null and b/bin/com/garbagemule/MobArena/waves/BossWave.class differ diff --git a/bin/com/garbagemule/MobArena/waves/DefaultWave.class b/bin/com/garbagemule/MobArena/waves/DefaultWave.class new file mode 100644 index 0000000..29b2312 Binary files /dev/null and b/bin/com/garbagemule/MobArena/waves/DefaultWave.class differ diff --git a/bin/com/garbagemule/MobArena/waves/RecurrentWave.class b/bin/com/garbagemule/MobArena/waves/RecurrentWave.class new file mode 100644 index 0000000..e3e3f52 Binary files /dev/null and b/bin/com/garbagemule/MobArena/waves/RecurrentWave.class differ diff --git a/bin/com/garbagemule/MobArena/waves/SingleWave.class b/bin/com/garbagemule/MobArena/waves/SingleWave.class new file mode 100644 index 0000000..e564eab Binary files /dev/null and b/bin/com/garbagemule/MobArena/waves/SingleWave.class differ diff --git a/bin/com/garbagemule/MobArena/waves/Wave$BossAbility.class b/bin/com/garbagemule/MobArena/waves/Wave$BossAbility.class new file mode 100644 index 0000000..a8af6e1 Binary files /dev/null and b/bin/com/garbagemule/MobArena/waves/Wave$BossAbility.class differ diff --git a/bin/com/garbagemule/MobArena/waves/Wave$BossHealth.class b/bin/com/garbagemule/MobArena/waves/Wave$BossHealth.class new file mode 100644 index 0000000..587a69f Binary files /dev/null and b/bin/com/garbagemule/MobArena/waves/Wave$BossHealth.class differ diff --git a/bin/com/garbagemule/MobArena/waves/Wave$SwarmAmount.class b/bin/com/garbagemule/MobArena/waves/Wave$SwarmAmount.class new file mode 100644 index 0000000..cd7e573 Binary files /dev/null and b/bin/com/garbagemule/MobArena/waves/Wave$SwarmAmount.class differ diff --git a/bin/com/garbagemule/MobArena/waves/Wave$WaveBranch.class b/bin/com/garbagemule/MobArena/waves/Wave$WaveBranch.class new file mode 100644 index 0000000..a8a51a5 Binary files /dev/null and b/bin/com/garbagemule/MobArena/waves/Wave$WaveBranch.class differ diff --git a/bin/com/garbagemule/MobArena/waves/Wave$WaveGrowth.class b/bin/com/garbagemule/MobArena/waves/Wave$WaveGrowth.class new file mode 100644 index 0000000..0c54741 Binary files /dev/null and b/bin/com/garbagemule/MobArena/waves/Wave$WaveGrowth.class differ diff --git a/bin/com/garbagemule/MobArena/waves/Wave$WaveType.class b/bin/com/garbagemule/MobArena/waves/Wave$WaveType.class new file mode 100644 index 0000000..b5b16d1 Binary files /dev/null and b/bin/com/garbagemule/MobArena/waves/Wave$WaveType.class differ diff --git a/bin/com/garbagemule/MobArena/waves/Wave.class b/bin/com/garbagemule/MobArena/waves/Wave.class new file mode 100644 index 0000000..4190499 Binary files /dev/null and b/bin/com/garbagemule/MobArena/waves/Wave.class differ diff --git a/bin/com/garbagemule/register/payment/Method$MethodAccount.class b/bin/com/garbagemule/register/payment/Method$MethodAccount.class new file mode 100644 index 0000000..9dbce6f Binary files /dev/null and b/bin/com/garbagemule/register/payment/Method$MethodAccount.class differ diff --git a/bin/com/garbagemule/register/payment/Method$MethodBankAccount.class b/bin/com/garbagemule/register/payment/Method$MethodBankAccount.class new file mode 100644 index 0000000..9a3f124 Binary files /dev/null and b/bin/com/garbagemule/register/payment/Method$MethodBankAccount.class differ diff --git a/bin/com/garbagemule/register/payment/Method.class b/bin/com/garbagemule/register/payment/Method.class new file mode 100644 index 0000000..e754386 Binary files /dev/null and b/bin/com/garbagemule/register/payment/Method.class differ diff --git a/bin/com/garbagemule/register/payment/Methods.class b/bin/com/garbagemule/register/payment/Methods.class new file mode 100644 index 0000000..789ffde Binary files /dev/null and b/bin/com/garbagemule/register/payment/Methods.class differ diff --git a/bin/com/garbagemule/register/payment/methods/BOSE$BOSEAccount.class b/bin/com/garbagemule/register/payment/methods/BOSE$BOSEAccount.class new file mode 100644 index 0000000..02850a7 Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/BOSE$BOSEAccount.class differ diff --git a/bin/com/garbagemule/register/payment/methods/BOSE$BOSEBankAccount.class b/bin/com/garbagemule/register/payment/methods/BOSE$BOSEBankAccount.class new file mode 100644 index 0000000..904d21d Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/BOSE$BOSEBankAccount.class differ diff --git a/bin/com/garbagemule/register/payment/methods/BOSE.class b/bin/com/garbagemule/register/payment/methods/BOSE.class new file mode 100644 index 0000000..151360f Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/BOSE.class differ diff --git a/bin/com/garbagemule/register/payment/methods/BOSE6$BOSEAccount.class b/bin/com/garbagemule/register/payment/methods/BOSE6$BOSEAccount.class new file mode 100644 index 0000000..a1daef4 Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/BOSE6$BOSEAccount.class differ diff --git a/bin/com/garbagemule/register/payment/methods/BOSE6$BOSEBankAccount.class b/bin/com/garbagemule/register/payment/methods/BOSE6$BOSEBankAccount.class new file mode 100644 index 0000000..36f1dca Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/BOSE6$BOSEBankAccount.class differ diff --git a/bin/com/garbagemule/register/payment/methods/BOSE6.class b/bin/com/garbagemule/register/payment/methods/BOSE6.class new file mode 100644 index 0000000..545a2a1 Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/BOSE6.class differ diff --git a/bin/com/garbagemule/register/payment/methods/BOSE7$BOSEAccount.class b/bin/com/garbagemule/register/payment/methods/BOSE7$BOSEAccount.class new file mode 100644 index 0000000..f752dbb Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/BOSE7$BOSEAccount.class differ diff --git a/bin/com/garbagemule/register/payment/methods/BOSE7$BOSEBankAccount.class b/bin/com/garbagemule/register/payment/methods/BOSE7$BOSEBankAccount.class new file mode 100644 index 0000000..66d8a29 Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/BOSE7$BOSEBankAccount.class differ diff --git a/bin/com/garbagemule/register/payment/methods/BOSE7.class b/bin/com/garbagemule/register/payment/methods/BOSE7.class new file mode 100644 index 0000000..5b618fe Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/BOSE7.class differ diff --git a/bin/com/garbagemule/register/payment/methods/EE17$EEcoAccount.class b/bin/com/garbagemule/register/payment/methods/EE17$EEcoAccount.class new file mode 100644 index 0000000..aa0f3e4 Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/EE17$EEcoAccount.class differ diff --git a/bin/com/garbagemule/register/payment/methods/EE17.class b/bin/com/garbagemule/register/payment/methods/EE17.class new file mode 100644 index 0000000..b12b788 Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/EE17.class differ diff --git a/bin/com/garbagemule/register/payment/methods/iCo4$iCoAccount.class b/bin/com/garbagemule/register/payment/methods/iCo4$iCoAccount.class new file mode 100644 index 0000000..445e792 Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/iCo4$iCoAccount.class differ diff --git a/bin/com/garbagemule/register/payment/methods/iCo4.class b/bin/com/garbagemule/register/payment/methods/iCo4.class new file mode 100644 index 0000000..329ef90 Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/iCo4.class differ diff --git a/bin/com/garbagemule/register/payment/methods/iCo5$iCoAccount.class b/bin/com/garbagemule/register/payment/methods/iCo5$iCoAccount.class new file mode 100644 index 0000000..a1e3c09 Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/iCo5$iCoAccount.class differ diff --git a/bin/com/garbagemule/register/payment/methods/iCo5$iCoBankAccount.class b/bin/com/garbagemule/register/payment/methods/iCo5$iCoBankAccount.class new file mode 100644 index 0000000..666ab2f Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/iCo5$iCoBankAccount.class differ diff --git a/bin/com/garbagemule/register/payment/methods/iCo5.class b/bin/com/garbagemule/register/payment/methods/iCo5.class new file mode 100644 index 0000000..ad7ef6b Binary files /dev/null and b/bin/com/garbagemule/register/payment/methods/iCo5.class differ diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..2e56dab --- /dev/null +++ b/build.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/plugin.yml b/resources/plugin.yml new file mode 100644 index 0000000..717eb43 --- /dev/null +++ b/resources/plugin.yml @@ -0,0 +1,124 @@ +name: MobArena +main: com.garbagemule.MobArena.MobArena +version: 0.94 +softdepend: [Spout,Permissions,MultiVerse,XcraftGate,Towny] +commands: + ma: + description: Base command for MobArena + usage: | + /ma join - Join the arena. + /ma leave - Leave the arena. + /ma notready - List of players who aren't ready. + /ma spectate - Warp to the spectator area. + mobarena: + description: Base command for MobArena + usage: | + /mobarena join - Join the arena. + /mobarena leave - Leave the arena. + /mobarena notready - List of players who aren't ready. + /mobarena spectate - Warp to the spectator area. +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: + default: true + mobarena.use.spec: + default: true + mobarena.use.leave: + default: true + + mobarena.admin.*: + description: Gives access to admin commands + default: op + children: + 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.enable: + default: false + mobarena.admin.kick: + default: false + mobarena.admin.restore: + default: false + mobarena.admin.force.end: + default: false + mobarena.admin.force.start: + default: false + mobarena.admin.config.reload: + default: false + + mobarena.setup.*: + description: Gives access to setup commands + default: op + children: + mobarena.setup.arena: true + mobarena.setup.setarena: true + mobarena.setup.addarena: true + mobarena.setup.delarena: true + mobarena.setup.protect: true + mobarena.setup.editarena: true + mobarena.setup.setregion: true + mobarena.setup.expandregion: true + mobarena.setup.showregion: true + mobarena.setup.setlobbyregion: true + 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.checkdata: true + mobarena.setup.autogenerate: true + mobarena.setup.autodegenerate: true + mobarena.setup.arena: + default: false + mobarena.setup.setarena: + default: false + mobarena.setup.addarena: + default: false + mobarena.setup.delarena: + default: false + mobarena.setup.protect: + default: false + mobarena.setup.editarena: + default: false + mobarena.setup.setregion: + default: false + mobarena.setup.expandregion: + default: false + mobarena.setup.showregion: + default: false + mobarena.setup.setlobbyregion: + default: false + mobarena.setup.expandlobbyregion: + default: false + mobarena.setup.setwarp: + default: false + mobarena.setup.spawnpoints: + default: false + mobarena.setup.addspawn: + default: false + mobarena.setup.delspawn: + default: false + mobarena.setup.containers: + default: false + mobarena.setup.addcontainer: + default: false + mobarena.setup.delcontainer: + default: false + mobarena.setup.checkdata: + default: false + mobarena.setup.autogenerate: + default: false + mobarena.setup.autodegenerate: + default: false \ No newline at end of file diff --git a/resources/res/announcements.properties b/resources/res/announcements.properties new file mode 100644 index 0000000..3f101af --- /dev/null +++ b/resources/res/announcements.properties @@ -0,0 +1,51 @@ +ARENA_START=Let the slaughter begin! +ARENA_END=Arena finished. +ARENA_DOES_NOT_EXIST=That arena does not exist. Type /ma arenas for a list. +JOIN_PLAYER_JOINED= +JOIN_NOT_ENABLED=MobArena is not enabled. +JOIN_IN_OTHER_ARENA=You are already in an arena! Leave that one first. +JOIN_ARENA_NOT_ENABLED=This arena is not enabled. +JOIN_ARENA_NOT_SETUP=This arena has not been set up yet. +JOIN_ARENA_PERMISSION=You don't have permission to join this arena. +JOIN_FEE_REQUIRED=Insufficient funds. Price: % +JOIN_FEE_PAID=Price to join was: % +JOIN_ARENA_IS_RUNNING=This arena is in already progress. +JOIN_ALREADY_PLAYING=You are already playing! +JOIN_ARG_NEEDED=You must specify an arena. Type /ma arenas for a list. +JOIN_TOO_FAR=You are too far away from the arena to join/spectate. +JOIN_EMPTY_INV=You must empty your inventory to join the arena. +JOIN_PLAYER_LIMIT_REACHED=The player limit of this arena has been reached. +JOIN_STORE_INV_FAIL=Failed to store inventory. Try again. +LEAVE_PLAYER_LEFT=You left the arena. Thanks for playing! +LEAVE_NOT_PLAYING=You are not in the arena. +PLAYER_DIED=% died! +SPEC_PLAYER_SPECTATE=Enjoy the show! +SPEC_NOT_RUNNING=This arena isn't running. +SPEC_ARG_NEEDED=You must specify an arena. Type /ma arenas for a list. +SPEC_EMPTY_INV=Empty your inventory first! +SPEC_ALREADY_PLAYING=Can't spectate when in the arena! +NOT_READY_PLAYERS=Not ready: % +FORCE_START_STARTED=Forced arena start. +FORCE_START_RUNNING=Arena has already started. +FORCE_START_NOT_READY=Can't force start, no players are ready. +FORCE_END_ENDED=Forced arena end. +FORCE_END_EMPTY=No one is in the arena. +FORCE_END_IDLE=You weren't quick enough! +REWARDS_GIVE=Here are all of your rewards! +LOBBY_CLASS_PICKED=You have chosen % as your class! +LOBBY_CLASS_RANDOM=You will get a random class on arena start. +LOBBY_CLASS_PERMISSION=You don't have permission to use this class! +LOBBY_PLAYER_READY=You have been flagged as ready! +LOBBY_DROP_ITEM=No sharing before the arena starts! +LOBBY_PICK_CLASS=You must first pick a class! +LOBBY_RIGHT_CLICK=Punch the sign. Don't right-click. +WARP_TO_ARENA=Can't warp to the arena during battle! +WARP_FROM_ARENA=Warping not allowed in the arena! +WAVE_DEFAULT=Get ready for wave #%! +WAVE_SPECIAL=Get ready for wave #%! [SPECIAL] +WAVE_REWARD=You just earned a reward: % +MISC_LIST_ARENAS=Available arenas: % +MISC_LIST_PLAYERS=Live players: % +MISC_COMMAND_NOT_ALLOWED=You can't use that command in the arena! +MISC_NO_ACCESS=You don't have access to this command. +MISC_NONE= diff --git a/resources/res/config.yml b/resources/res/config.yml new file mode 100644 index 0000000..02c8644 --- /dev/null +++ b/resources/res/config.yml @@ -0,0 +1,113 @@ +### 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 diff --git a/resources/res/totals.txt b/resources/res/totals.txt new file mode 100644 index 0000000..bcbbbf4 --- /dev/null +++ b/resources/res/totals.txt @@ -0,0 +1,57 @@ +### General data +### Statistics for overall information about the arena, such as +### times played, durations, max players seen, total kills, etc. +general-info: + total-games-played: 0 + total-duration: 0:00:00 + longest-session-duration: 0:00:00 + most-players: 0 + highest-wave-reached: 0 + total-monsters-killed: 0 + +### Class specific data +### How many times has each class been played, and how many kills, how +### much damage, etc. does the class make/do/take. +classes: + overall-distribution: + # Percentages can be calculated with / total-count * 100. + total-count: 0 + +#classes: +# overall-distribution: +# total-count: 0 +# archer: +# kills: +# damage-done: +# damage-taken: +# accuracy: +# knight: +# kills: +# damage-done: +# damage-taken: +# accuracy: +# ... +### Reward data +### Totals for all rewards given, perhaps good for balancing? +rewards: + total-given: + # This might be useless information, I don't know. + # Might have to make it data values instead of Material names? + money: 0 + +### Player data +### Total games played, kills, damage, swings and hits (for accuracy), and how +### many times the player has played as each class. +players: {} +# garbagemule: +# games-played: 5 +# kills: 4 +# damage-done: 251 +# damage-taken: 102 +# swings: 1820 +# hits: 1214 +# classes-played: +# archer: 2 +# ... +# Agnate: +# ... \ No newline at end of file diff --git a/resources/res/totals.yml b/resources/res/totals.yml new file mode 100644 index 0000000..610f4a1 --- /dev/null +++ b/resources/res/totals.yml @@ -0,0 +1,57 @@ +### General data +### Statistics for overall information about the arena, such as +### times played, durations, max players seen, total kills, etc. +general-info: + total-games-played: 0 + total-duration: 0:00:00 + longest-session-duration: 0:00:00 + most-players: 0 + highest-wave-reached: 0 + total-monsters-killed: 0 + +### Class specific data +### How many times has each class been played, and how many kills, how +### much damage, etc. does the class make/do/take. +classes: + overall-distribution: + # Percentages can be calculated with / total-count * 100. + total-count: 0 + +#classes: +# overall-distribution: +# total-count: 0 +# archer: +# kills: +# damage-done: +# damage-taken: +# accuracy: +# knight: +# kills: +# damage-done: +# damage-taken: +# accuracy: +# ... +### Reward data +### Totals for all rewards given, perhaps good for balancing? +rewards: + total-given: + # This might be useless information, I don't know. + # Might have to make it data values instead of Material names? + money: 0 + +### Player data +### Total games played, kills, damage, swings and hits (for accuracy), and how +### many times the player has played as each class. +players: {} +# garbagemule: +# games-played: 5 +# kills: 4 +# damage-done: 251 +# damage-taken: 102 +# swings: 1820 +# hits: 1214 +# classes-played: +# archer: 2 +# ... +# Agnate: +# ... \ No newline at end of file diff --git a/src/com/garbagemule/MobArena/AbstractArena.java b/src/com/garbagemule/MobArena/AbstractArena.java deleted file mode 100644 index 59a36e4..0000000 --- a/src/com/garbagemule/MobArena/AbstractArena.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.garbagemule.MobArena; - -import org.bukkit.entity.Player; - -public abstract class AbstractArena -{ - /** - * Start the arena session. - * This method should warp all players to their respective warp points, start all - * needed timers, clear/populate all sets and lists, and flag all booleans. - */ - public abstract void startArena(); - - /** - * Force the arena to start. - * If some players are ready, this method will force all non-ready players to leave, - * and the arena will start with only the currently ready players. - * @return true, if the arena was successfully started, false otherwise - */ - public abstract boolean forceStart(); - - /** - * Stop the arena session. - * Distribute rewards, clean up arena floor and reset everything to how it was before - * the arena session was started, false otherwise - */ - public abstract void endArena(); - - /** - * Force the arena to end. - * Returns all players to their entry locations, distributes rewards, cleans the arena - * floor, as well as all lists, sets and maps. Calling this method will return the - * arena to the state it would be in right after MobArena has loaded. - * @return true, if the session was successfully ended. - */ - public abstract boolean forceEnd(); - - /** - * Player joins the arena/lobby. - * @param p A player - * @precondition Calling canJoin(p) for the given player must return true. - */ - public abstract void playerJoin(Player p); - - /** - * Player leaves the arena or lobby. - * @param p A player - * @precondition Calling canLeave(p) for the given player must return true. - */ - public abstract void playerLeave(Player p); - - /** - * Player joins the spectator area. - * @param p A player - * @precondition Calling canSpec(p) for the given player must return true. - */ - public abstract void playerSpec(Player p); - - /** - * Player dies in the arena. - */ - public abstract void playerDeath(Player p); - - /** - * Player signals that they are ready. - */ - public abstract void playerReady(Player p); - - /** - * Check if a player can join the arena. - * @param p A player - * @return true, if the player is eligible to join the arena. - */ - public abstract boolean canJoin(Player p); - - /** - * Check if a player can leave the arena. - * @param p A player - * @return true, if the player is eligible to leave the arena. - */ - public abstract boolean canLeave(Player p); - - /** - * Check if a player can spectate the arena. - * @param p A player - * @return true, if the player is eligible for spectating. - */ - public abstract boolean canSpec(Player p); - - /** - * Check if the arena is enabled. - * @return true, if the arena is enabled. - */ - public abstract boolean isEnabled(); - - /** - * Check if the arena is running. - * @return true, if the arena is running. - */ - public abstract boolean isRunning(); - - /** - * Check if the arena is set up and ready for use. - * @return true, if the arena is ready for use. - */ - public abstract boolean isSetup(); -} diff --git a/src/com/garbagemule/MobArena/Arena.java b/src/com/garbagemule/MobArena/Arena.java index 7e8cc0c..a42a65a 100644 --- a/src/com/garbagemule/MobArena/Arena.java +++ b/src/com/garbagemule/MobArena/Arena.java @@ -5,7 +5,8 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -13,6 +14,10 @@ import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.PriorityBlockingQueue; + +import net.minecraft.server.WorldServer; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -21,7 +26,11 @@ 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.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.entity.Creature; import org.bukkit.entity.CreatureType; import org.bukkit.entity.Entity; import org.bukkit.entity.Item; @@ -34,6 +43,14 @@ import org.bukkit.inventory.PlayerInventory; import org.bukkit.util.config.Configuration; import com.garbagemule.MobArena.MAMessages.Msg; +import com.garbagemule.MobArena.repairable.Repairable; +import com.garbagemule.MobArena.repairable.RepairableComparator; +import com.garbagemule.MobArena.repairable.RepairableContainer; +import com.garbagemule.MobArena.util.InventoryItem; +import com.garbagemule.MobArena.util.WaveUtils; +import com.garbagemule.MobArena.waves.BossWave; +import com.garbagemule.MobArena.waves.Wave; +import com.garbagemule.MobArena.waves.Wave.WaveBranch; public class Arena { @@ -42,29 +59,36 @@ public class Arena // Setup fields protected String name; protected World world; - protected boolean enabled, protect, logging, running, setup, lobbySetup, autoEquip, forceRestore, softRestore, softRestoreDrops, emptyInvJoin, emptyInvSpec, pvp, monsterInfight, allowWarp; + 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; protected Location p1, p2, l1, l2, arenaLoc, lobbyLoc, spectatorLoc; - protected Map spawnpoints; + protected Map spawnpoints, containers; + protected String logging; // Wave/reward/entryfee fields - protected int spawnTaskId, waveDelay, waveInterval, specialModulo, spawnMonstersInt, maxIdleTime; + protected int spawnTaskId, sheepTaskId, waveDelay, waveInterval, specialModulo, spawnMonstersInt, maxIdleTime; protected MASpawnThread spawnThread; protected Map> everyWaveMap, afterWaveMap; - protected Map distDefault, distSpecial; protected Map classMap; protected Map> classItems, classArmor; - protected Map>> classBonuses; - protected Map> rewardMap; protected List entryFee; + + // Player sets + protected Set arenaPlayers, lobbyPlayers, readyPlayers, specPlayers; + + // Wave stuff + protected TreeSet singleWaves, singleWavesInstance; + protected TreeSet recurrentWaves; + protected BossWave bossWave; // Arena sets/maps - protected Set arenaPlayers, lobbyPlayers, readyPlayers, specPlayers, hasPaid, rewardedPlayers, notifyPlayers, randoms; - protected Set monsters; - protected Set blocks; - protected Set pets; - protected Map petMap; - protected List repairList; + 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; @@ -78,14 +102,11 @@ public class Arena // Logging protected ArenaLog log; - protected List classDistribution = new LinkedList(); - protected Map waveMap = new HashMap(); - protected Map killMap = new HashMap(); - protected Timestamp startTime; - protected Timestamp endTime; protected MAListener eventListener; + protected PriorityBlockingQueue repairQueue; + /** * Primary constructor. Requires a name and a world. */ @@ -97,7 +118,6 @@ public class Arena this.name = name; this.world = world; plugin = (MobArena) Bukkit.getServer().getPluginManager().getPlugin("MobArena"); - log = new ArenaLog(plugin, this); arenaPlayers = new HashSet(); lobbyPlayers = new HashSet(); @@ -107,13 +127,16 @@ public class Arena 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(); - rewardMap = new HashMap>(); - repairList = new LinkedList(); + repairables = new LinkedList(); + containables = new LinkedList(); running = false; edit = false; @@ -123,6 +146,7 @@ public class Arena spawnMonsters = ((net.minecraft.server.World) ((CraftWorld) world).getHandle()).spawnMonsters; eventListener = new MAListener(this, plugin); + repairQueue = new PriorityBlockingQueue(100, new RepairableComparator()); } public boolean startArena() @@ -132,6 +156,9 @@ public class Arena return false; if (!softRestore && forceRestore && !serializeRegion()) return false; + + // Store all chest contents. + storeContainerContents(); // Populate arenaPlayers and clear the lobby. arenaPlayers.addAll(lobbyPlayers); @@ -149,28 +176,29 @@ public class Arena { p.teleport(arenaLoc); p.setHealth(20); - rewardMap.put(p, new LinkedList()); - waveMap.put(p, 0); - killMap.put(p, 0); } - // Spawn pets. - spawnPets(); - - // Start spawning monsters. - startSpawning(); - - // Start logging - if (logging) - log.start(); - // Set the boolean. running = true; + // Spawn pets (must happen after 'running = true;') + spawnPets(); + + // Copy the singleWaves Set. + singleWavesInstance = new TreeSet(singleWaves); + + // Start spawning monsters. + startSpawning(); + startBouncingSheep(); + + // Start logging + log = new ArenaLog(plugin, this); + log.start(); + // Announce and notify. - MAUtils.tellAll(this, MAMessages.get(Msg.ARENA_START)); + MAUtils.tellAll(this, Msg.ARENA_START); for (MobArenaListener listener : plugin.getAM().listeners) - listener.onArenaStart(); + listener.onArenaStart(this); return true; } @@ -184,13 +212,14 @@ public class Arena // Set the boolean. running = false; - // Stop logging. - if (logging) + // Finish logging + log.end(); + if (logging != null) { - log.end(); - log.serialize(); - log.clear(); + log.saveSessionData(); + log.updateArenaTotals(); } + log.clearSessionData(); // Stop spawning. stopSpawning(); @@ -200,25 +229,24 @@ public class Arena // Restore region. if (softRestore) - for (int[] buffer : repairList) - world.getBlockAt(buffer[0], buffer[1], buffer[2]).setTypeIdAndData(buffer[3], (byte) buffer[4], false); + restoreRegion(); else if (forceRestore) deserializeRegion(); + + // Restore chests + restoreContainerContents(); // Announce and clear sets. - MAUtils.tellAll(this, MAMessages.get(Msg.ARENA_END), true); + MAUtils.tellAll(this, Msg.ARENA_END, true); arenaPlayers.clear(); notifyPlayers.clear(); rewardedPlayers.clear(); classMap.clear(); - rewardMap.clear(); - waveMap.clear(); - killMap.clear(); spawnThread = null; // Notify listeners. for (MobArenaListener listener : plugin.getAM().listeners) - listener.onArenaEnd(); + listener.onArenaEnd(this); return true; } @@ -243,49 +271,38 @@ public class Arena public void forceEnd() { - if (!running) - return; + Bukkit.getServer().getScheduler().cancelTask(spawnTaskId); + Bukkit.getServer().getScheduler().cancelTask(sheepTaskId); for (Player p : getAllPlayers()) - { - plugin.getAM().arenaMap.remove(p); playerLeave(p); - } for (Entity e : monsters) e.remove(); + if (bossWave != null) + bossWave.clear(); + arenaPlayers.clear(); lobbyPlayers.clear(); readyPlayers.clear(); - rewardMap.clear(); - waveMap.clear(); - killMap.clear(); - monsters.clear(); - + cleanup(); + spawnTaskId = -1; + sheepTaskId = -1; } public void playerJoin(Player p, Location loc) { - // Store location and health. - if (!locations.containsKey(p)) - locations.put(p,loc); - if (!healthMap.containsKey(p)) - healthMap.put(p,p.getHealth()); - p.setHealth(20); - - // Update chunk. - updateChunk(lobbyLoc); - + storePlayerData(p, loc); MAUtils.sitPets(p); - lobbyPlayers.add(p); - p.teleport(lobbyLoc); - + p.setHealth(20); + movePlayerToLobby(p); + // Notify listeners. for (MobArenaListener listener : plugin.getAM().listeners) - listener.onPlayerJoin(p); + listener.onPlayerJoin(this, p); } public void playerReady(Player p) @@ -294,7 +311,7 @@ public class Arena if (minPlayers > 0 && lobbyPlayers.size() < minPlayers) { - MAUtils.tellPlayer(p, MAMessages.get(Msg.LOBBY_NOT_ENOUGH_PLAYERS, "" + minPlayers)); + MAUtils.tellPlayer(p, Msg.LOBBY_NOT_ENOUGH_PLAYERS, "" + minPlayers); return; } @@ -303,84 +320,56 @@ public class Arena public void playerLeave(Player p) { - // Clear class inventory, restore old inventory and fork over rewards. - restoreInvAndGiveRewards(p, (arenaPlayers.contains(p) || lobbyPlayers.contains(p))); + finishArenaPlayer(p); + movePlayerToEntry(p); + discardPlayer(p); - // Grab the player's entry location, and warp them there. - Location entry = locations.get(p); - if (entry != null) - { - updateChunk(entry); - p.teleport(entry); - } - locations.remove(p); - if (healthMap.containsKey(p)) p.setHealth(healthMap.remove(p)); + // Notify listeners. + for (MobArenaListener listener : plugin.getAM().listeners) + listener.onPlayerLeave(this, p); - // Remove from the arenaMap and all the sets. - plugin.getAM().arenaMap.remove(p); - removePlayer(p); - - // End the arena if conditions are met. + // End arena if possible. endArena(); } public void playerDeath(Player p) { - // If spectate-on-death: false, pass on to playerLeave. - if (!specOnDeath) + finishArenaPlayer(p); + + if (specOnDeath) { - playerLeave(p); - return; + clearPlayer(p); + movePlayerToSpec(p); } - - // Clear class inventory, restore old inventory and fork over rewards. - restoreInvAndGiveRewards(p, true); - - // Remove player from sets, warp to spectator area, then add to specPlayers. - p.teleport(spectatorLoc); - if (healthMap.containsKey(p)) p.setHealth(healthMap.remove(p)); - removePlayer(p); - specPlayers.add(p); - - // Update the monster targets. + else + { + movePlayerToEntry(p); + discardPlayer(p); + } + if (running && spawnThread != null) spawnThread.updateTargets(); - // Announce and notify - MAUtils.tellAll(this, MAMessages.get(Msg.PLAYER_DIED, p.getName())); + MAUtils.tellAll(this, Msg.PLAYER_DIED, p.getName()); + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, + new Runnable() + { + public void run() + { + endArena(); + } + }); + + // Notify listeners. for (MobArenaListener listener : plugin.getAM().listeners) - listener.onPlayerDeath(p); - - // End the arena if conditions are met. - endArena(); + listener.onPlayerDeath(this, p); } public void playerSpec(Player p, Location loc) { - if (!locations.containsKey(p)) - locations.put(p,loc); - if (!healthMap.containsKey(p)) - healthMap.put(p,p.getHealth()); - + storePlayerData(p, loc); MAUtils.sitPets(p); - specPlayers.add(p); - p.teleport(spectatorLoc); - } - - public void removePlayer(Player p) - { - // Heal and put out fire. - p.setFireTicks(0); - p.setHealth(20); - - // Remove pets. - removePets(p); - - // readyPlayers before lobbyPlayers because of startArena sanity-checks - readyPlayers.remove(p); - specPlayers.remove(p); - arenaPlayers.remove(p); - lobbyPlayers.remove(p); + movePlayerToSpec(p); } private void spawnPets() @@ -420,9 +409,11 @@ public class Arena // Stop the spawn thread. if (spawnThread != null) { - Bukkit.getServer().getScheduler().cancelTask(spawnThread.taskId); + Bukkit.getServer().getScheduler().cancelTask(spawnThread.getTaskId()); Bukkit.getServer().getScheduler().cancelTask(spawnTaskId); + Bukkit.getServer().getScheduler().cancelTask(sheepTaskId); spawnTaskId = -1; + sheepTaskId = -1; spawnThread = null; } @@ -430,6 +421,40 @@ public class Arena MAUtils.setSpawnFlags(plugin, world, spawnMonsters, allowMonsters, allowAnimals); } + private void startBouncingSheep() + { + sheepTaskId = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, + new Runnable() + { + public void run() + { + if (explodingSheep.isEmpty()) return; + + for (LivingEntity e : new LinkedList(explodingSheep)) + { + Creature c = (Creature) e; + if (c.getTarget() != null && e.getLocation().distanceSquared(c.getTarget().getLocation()) < 8) + { + CraftEntity ce = (CraftEntity) e; + CraftWorld cw = (CraftWorld) e.getWorld(); + WorldServer ws = cw.getHandle(); + ws.createExplosion(ce.getHandle(), e.getLocation().getX(), e.getLocation().getY() + 1, e.getLocation().getZ(), 2f, false); + e.remove(); + } + + if (e.isDead()) + { + explodingSheep.remove(e); + continue; + } + + if (Math.abs(e.getVelocity().getY()) < 1) + e.setVelocity(e.getVelocity().setY(0.5)); + } + } + }, waveDelay, 20); + } + private void updateChunk(Location loc) { if (!arenaPlayers.isEmpty() || !world.getName().equals(loc.getWorld().getName())) @@ -444,24 +469,22 @@ public class Arena public void playerKill(Player p) { - if (p == null || killMap.get(p) == null) + if (p == null || log.players.get(p) == null) return; - killMap.put(p, killMap.get(p) + 1); + log.players.get(p).kills++; } - public void restoreInvAndGiveRewards(final Player p, final boolean clear) - { - final List rewards = rewardMap.get(p); + public void restoreInvAndGiveRewards(final Player p) + { + final List rewards = log != null && log.players.get(p) != null ? log.players.get(p).rewards : new LinkedList(); final boolean hadRewards = rewardedPlayers.contains(p); - if (clear) MAUtils.clearInventory(p); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { public void run() - { + { if (!emptyInvJoin) MAUtils.restoreInventory(p); @@ -475,6 +498,147 @@ public class Arena }); } + public void restoreInvAndGiveRewardz(final Player p, List rewards, boolean hadRewards) + { + if (!p.isOnline()) + return; + + if (!emptyInvJoin) + MAUtils.restoreInventory(p); + + if (hadRewards) + return; + + MAUtils.giveRewards(p, rewards, plugin); + if (running) + rewardedPlayers.add(p);} + + public void storePlayerData(Player p, Location loc) + { + plugin.getAM().arenaMap.put(p, this); + + if (!locations.containsKey(p)) + locations.put(p, loc); + + if (!healthMap.containsKey(p)) + healthMap.put(p, p.getHealth()); + } + + public void storeContainerContents() + { + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, + new Runnable() + { + public void run() + { + for (Location loc : containers.values()) + { + BlockState state = world.getBlockAt(loc).getState(); + if (state instanceof ContainerBlock) + containables.add(new RepairableContainer(state, false)); + } + } + }); + } + + public void restoreContainerContents() + { + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, + new Runnable() + { + public void run() + { + for (Repairable r : containables) + r.repair(); + } + }); + } + + public void movePlayerToLobby(Player p) + { + updateChunk(lobbyLoc); + lobbyPlayers.add(p); + p.teleport(lobbyLoc); + } + + public void movePlayerToSpec(Player p) + { + updateChunk(spectatorLoc); + specPlayers.add(p); + p.teleport(spectatorLoc); + } + + public void movePlayerToEntry(Player p) + { + Location entry = locations.get(p); + if (entry == null) return; + + updateChunk(entry); + p.teleport(entry); + } + + private void clearPlayer(Player p) + { + if (healthMap.containsKey(p)) + p.setHealth(healthMap.remove(p)); + + // Put out fire. + p.setFireTicks(0); + + // Remove pets. + removePets(p); + + // readyPlayers before lobbyPlayers because of startArena sanity-checks + readyPlayers.remove(p); + specPlayers.remove(p); + arenaPlayers.remove(p); + lobbyPlayers.remove(p); + } + + /** + * Completely remove a player from the arena collections. + * @param p A player + */ + private void discardPlayer(Player p) + { + locations.remove(p); + plugin.getAM().arenaMap.remove(p); + clearPlayer(p); + } + + /** + * Give the player back his inventory and record his last wave. + * Called when a player dies or leaves prematurely. + * @param p + */ + private void finishArenaPlayer(Player p) + { + if (!arenaPlayers.contains(p) && !lobbyPlayers.contains(p)) + return; + + MAUtils.clearInventory(p); + restoreInvAndGiveRewards(p); + + if (log != null && spawnThread != null) + log.players.get(p).lastWave = spawnThread.getWave() - 1; + } + + public void repairBlocks() + { + //long start = System.nanoTime(); + //System.out.println(start + " - Attempting to repair things..."); + while (!repairQueue.isEmpty()) + { + repairQueue.poll().repair(); + } + //System.out.println(start + " - Repair finished!"); + } + + public void queueRepairable(Repairable r) + { + repairQueue.add(r); + } + /*//////////////////////////////////////////////////////////////////// @@ -515,7 +679,7 @@ public class Arena { if (classes.isEmpty()) { - System.out.println("[MobArena] ERROR! Player '" + p.getName() + "' has no class permissions!"); + MobArena.info("Player '" + p.getName() + "' has no class permissions!"); playerLeave(p); return; } @@ -523,7 +687,7 @@ public class Arena } assignClass(p, className); - MAUtils.tellPlayer(p, MAMessages.get(Msg.LOBBY_CLASS_PICKED, className)); + MAUtils.tellPlayer(p, Msg.LOBBY_CLASS_PICKED, className); } private void cleanup() @@ -533,14 +697,25 @@ public class Arena removePets(); removeEntities(); monsters.clear(); + explodingSheep.clear(); + plaguedPigs.clear(); + madCows.clear(); blocks.clear(); pets.clear(); } private void removeMonsters() { + if (bossWave != null) + bossWave.clear(); for (LivingEntity e : monsters) e.remove(); + for (LivingEntity e : explodingSheep) + e.remove(); + for (LivingEntity e : plaguedPigs) + e.remove(); + for (LivingEntity e : madCows) + e.remove(); } private void removeBlocks() @@ -573,6 +748,7 @@ public class Arena private void removeEntities() { + if (p1 == null || p2 == null) return; Chunk c1 = world.getChunkAt(p1); Chunk c2 = world.getChunkAt(p2); @@ -600,7 +776,7 @@ public class Arena enabled = config.getBoolean(arenaPath + "enabled", true); protect = config.getBoolean(arenaPath + "protect", true); - logging = config.getBoolean(arenaPath + "logging", false); + logging = config.getString(arenaPath + "logging", null); autoEquip = config.getBoolean(arenaPath + "auto-equip-armor", true); waveClear = config.getBoolean(arenaPath + "clear-wave-before-next", false); detCreepers = config.getBoolean(arenaPath + "detonate-creepers", false); @@ -625,9 +801,7 @@ public class Arena waveInterval = config.getInt(arenaPath + "wave-interval", 20) * 20; specialModulo = config.getInt(arenaPath + "special-modulo", 4); maxIdleTime = config.getInt(arenaPath + "max-idle-time", 0) * 20; - - distDefault = MAUtils.getArenaDistributions(config, configName, "default"); - distSpecial = MAUtils.getArenaDistributions(config, configName, "special"); + everyWaveMap = MAUtils.getArenaRewardMap(config, configName, "every"); afterWaveMap = MAUtils.getArenaRewardMap(config, configName, "after"); entryFee = MAUtils.getEntryFee(config, configName); @@ -640,6 +814,11 @@ public class Arena lobbyLoc = MAUtils.getArenaCoord(config, world, configName, "lobby"); spectatorLoc = MAUtils.getArenaCoord(config, world, configName, "spectator"); spawnpoints = MAUtils.getArenaSpawnpoints(config, world, configName); + containers = MAUtils.getArenaContainers(config, world, configName); + + // NEW WAVES + singleWaves = WaveUtils.getWaves(this, config, WaveBranch.SINGLE); + recurrentWaves = WaveUtils.getWaves(this, config, WaveBranch.RECURRENT); classes = plugin.getAM().classes; classItems = plugin.getAM().classItems; @@ -650,6 +829,14 @@ public class Arena lobbySetup = MAUtils.verifyLobby(this); } + public void restoreRegion() + { + Collections.sort(repairables, new RepairableComparator()); + + for (Repairable r : repairables) + r.repair(); + } + public void serializeConfig() { String coords = "arenas." + configName() + ".coords."; @@ -718,7 +905,7 @@ public class Arena } catch (Exception e) { - System.out.println("[MobArena] ERROR! Could not create region file. The arena will not be started!"); + MobArena.warning("Could not create region file. The arena will not be started!"); e.printStackTrace(); return false; } @@ -742,7 +929,7 @@ public class Arena } catch (Exception e) { - System.out.println("[MobArena] ERROR! Could not find region file. The arena cannot be restored!"); + MobArena.warning("Could not find region file. The arena cannot be restored!"); e.printStackTrace(); return false; } @@ -826,6 +1013,75 @@ public class Arena return name; } + public MobArena getPlugin() + { + return plugin; + } + + public World getWorld() + { + return world; + } + + public void setBossWave(BossWave bossWave) + { + this.bossWave = bossWave; + } + + public List getClasses() + { + return classes; + } + + public List getAllSpawnpoints() + { + return new ArrayList(spawnpoints.values()); + } + + public List getSpawnpoints() + { + List result = new ArrayList(spawnpoints.size()); + + for (Map.Entry entry : spawnpoints.entrySet()) + if (!entry.getKey().matches("^*boss*$")) + result.add(entry.getValue()); + + return !result.isEmpty() ? result : new ArrayList(spawnpoints.values()); + } + + public Location getBossSpawnpoint() + { + Location result = null; + + for (Map.Entry entry : spawnpoints.entrySet()) + { + if (!entry.getKey().matches("^*boss*$")) continue; + + result = entry.getValue(); + break; + } + + if (result != null) + return result; + + return WaveUtils.getValidSpawnpoints(this, arenaPlayers).iterator().next(); + } + + public int getPlayerCount() + { + return spawnThread.getPlayerCount(); + } + + public void addMonster(LivingEntity e) + { + monsters.add(e); + } + + public void addExplodingSheep(LivingEntity e) + { + explodingSheep.add(e); + } + public List getAllPlayers() { List result = new LinkedList(); @@ -850,41 +1106,55 @@ public class Arena return result; } + public Set getMonsters() + { + Set tmp = new HashSet(monsters); + tmp.addAll(explodingSheep); + return tmp; + } + + public Set getPets() + { + return pets; + } + public void resetIdleTimer() { if (maxIdleTime <= 0 || !running) return; // Reset the previousSize, cancel the previous timer, and start the new timer. - spawnThread.previousSize = monsters.size(); - Bukkit.getServer().getScheduler().cancelTask(spawnThread.taskId); - spawnThread.taskId = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, + spawnThread.setPreviousSize(getMonsters().size()); + Bukkit.getServer().getScheduler().cancelTask(spawnThread.getTaskId()); + int id = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { public void run() { // Make sure to remove any dead/removed entities first. - List tmp = new LinkedList(monsters); - for (Entity e : tmp) + List ms = new LinkedList(monsters); + for (Entity e : ms) if (e.isDead()) monsters.remove(e); // Compare the current size with the previous size. - if (monsters.size() < spawnThread.previousSize || spawnThread.previousSize == 0) + if (monsters.size() < spawnThread.getPreviousSize() || spawnThread.getPreviousSize() == 0 || bossWave != null) { resetIdleTimer(); return; } // Clear all player inventories, and "kill" all players. - for (Player p : arenaPlayers) + List ps = new LinkedList(arenaPlayers); + for (Player p : ps) { MAUtils.clearInventory(p); - MAUtils.tellPlayer(p, MAMessages.get(Msg.FORCE_END_IDLE)); + MAUtils.tellPlayer(p, Msg.FORCE_END_IDLE); playerDeath(p); } } }, maxIdleTime); + spawnThread.setTaskId(id); } public void addTrunkAndLeaves(Block b) @@ -941,54 +1211,75 @@ public class Arena return true; PlayerInventory inv = p.getInventory(); - for (ItemStack stack : entryFee) - { - // Economy money - if (stack.getTypeId() == MobArena.ECONOMY_MONEY_ID) - { - if (plugin.Methods.hasMethod() && !plugin.Method.getAccount(p.getName()).subtract(stack.getAmount())) - return false; - } + InventoryItem[] items = InventoryItem.parseItemStacks(inv.getContents()); + InventoryItem[] fee = InventoryItem.parseItemStacks(entryFee); - // Normal stack - else - { - int id = stack.getTypeId(); - int amount = stack.getAmount(); - - while (amount > 0) - { - int pos = inv.first(id); - if (pos == -1) return false; - - ItemStack is = inv.getItem(pos); - if (is.getAmount() > amount) - { - is.setAmount(is.getAmount() - amount); - amount = 0; - } - else - { - amount -= is.getAmount(); - inv.setItem(pos, null); - } - } - } - } + // Take some economy money + for (InventoryItem item : InventoryItem.extractAllFromArray(MobArena.ECONOMY_MONEY_ID, fee)) + if (plugin.Methods.hasMethod()) + plugin.Method.getAccount(p.getName()).subtract(item.getAmount()); + + // Take any other items + for (InventoryItem item : fee) + InventoryItem.removeItemFromArray(item, items); - hasPaid.add(p); + // Turn everything back into ItemStacks + for (int i = 0; i < items.length; i++) + inv.setItem(i, items[i].toItemStack()); + return true; } - public void refund(Player p) + public boolean canJoin(Player p) { - if (!hasPaid.contains(p)) - return; + if (!enabled) + MAUtils.tellPlayer(p, Msg.JOIN_ARENA_NOT_ENABLED); + else if (!setup || recurrentWaves.isEmpty()) + MAUtils.tellPlayer(p, Msg.JOIN_ARENA_NOT_SETUP); + else if (edit) + MAUtils.tellPlayer(p, Msg.JOIN_ARENA_EDIT_MODE); + else if (running && (notifyPlayers.contains(p) || notifyPlayers.add(p))) + MAUtils.tellPlayer(p, Msg.JOIN_ARENA_IS_RUNNING); + else if (arenaPlayers.contains(p) || lobbyPlayers.contains(p)) + MAUtils.tellPlayer(p, Msg.JOIN_ALREADY_PLAYING); + else if (!plugin.has(p, "mobarena.arenas." + configName())) + MAUtils.tellPlayer(p, Msg.JOIN_ARENA_PERMISSION); + else if (maxPlayers > 0 && lobbyPlayers.size() >= maxPlayers) + MAUtils.tellPlayer(p, Msg.JOIN_PLAYER_LIMIT_REACHED); + else if (joinDistance > 0 && !inRegionRadius(p.getLocation(), joinDistance)) + MAUtils.tellPlayer(p, Msg.JOIN_TOO_FAR); + else if (emptyInvJoin && !MAUtils.hasEmptyInventory(p)) + MAUtils.tellPlayer(p, Msg.JOIN_EMPTY_INV); + /*else if (!canAfford(p) || !takeFee(p)) + MAUtils.tellPlayer(p, Msg.JOIN_FEE_REQUIRED, MAUtils.listToString(entryFee, plugin)); + else if (emptyInvJoin && !MAUtils.hasEmptyInventory(p)) + MAUtils.tellPlayer(p, Msg.JOIN_EMPTY_INV); + else if (!emptyInvJoin && !MAUtils.storeInventory(p)) + MAUtils.tellPlayer(p, Msg.JOIN_STORE_INV_FAIL);*/ + else return true; - MAUtils.giveItems(p, entryFee, false, plugin); - hasPaid.remove(p); + return false; } + public boolean canSpec(Player p) + { + if (!enabled) + MAUtils.tellPlayer(p, Msg.JOIN_ARENA_NOT_ENABLED); + else if (!setup) + MAUtils.tellPlayer(p, Msg.JOIN_ARENA_NOT_SETUP); + else if (edit) + MAUtils.tellPlayer(p, Msg.JOIN_ARENA_EDIT_MODE); + else if (arenaPlayers.contains(p) || lobbyPlayers.contains(p)) + MAUtils.tellPlayer(p, Msg.SPEC_ALREADY_PLAYING); + else if (emptyInvSpec && !MAUtils.hasEmptyInventory(p)) + MAUtils.tellPlayer(p, Msg.SPEC_EMPTY_INV); + else if (joinDistance > 0 && !inRegionRadius(p.getLocation(), joinDistance)) + MAUtils.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. diff --git a/src/com/garbagemule/MobArena/ArenaListener.java b/src/com/garbagemule/MobArena/ArenaListener.java index 27a242d..e55211c 100644 --- a/src/com/garbagemule/MobArena/ArenaListener.java +++ b/src/com/garbagemule/MobArena/ArenaListener.java @@ -1,6 +1,7 @@ package com.garbagemule.MobArena; import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockIgniteEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityCombustEvent; @@ -20,7 +21,8 @@ import org.bukkit.event.player.PlayerTeleportEvent; public interface ArenaListener { public void onBlockBreak(BlockBreakEvent event); - public void onBlockPlace(BlockPlaceEvent event); + public void onBlockPlace(BlockPlaceEvent event); + public void onBlockIgnite(BlockIgniteEvent event); public void onCreatureSpawn(CreatureSpawnEvent event); public void onEntityExplode(EntityExplodeEvent event); public void onEntityCombust(EntityCombustEvent event); @@ -35,23 +37,4 @@ public interface ArenaListener public void onPlayerKick(PlayerKickEvent event); public void onPlayerTeleport(PlayerTeleportEvent event); public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event); - - /* - public abstract void onBlockBreak(BlockBreakEvent event); - public abstract void onBlockPlace(BlockPlaceEvent event); - public abstract void onCreatureSpawn(CreatureSpawnEvent event); - public abstract void onEntityExplode(EntityExplodeEvent event); - public abstract void onEntityCombust(EntityCombustEvent event); - public abstract void onEntityTarget(EntityTargetEvent event); - public abstract void onEntityRegainHealth(EntityRegainHealthEvent event); - public abstract void onEntityDeath(EntityDeathEvent event); - public abstract void onEntityDamage(EntityDamageEvent event); - public abstract void onPlayerDropItem(PlayerDropItemEvent event); - public abstract void onPlayerBucketEmpty(PlayerBucketEmptyEvent event); - public abstract void onPlayerInteract(PlayerInteractEvent event); - public abstract void onPlayerQuit(PlayerQuitEvent event); - public abstract void onPlayerKick(PlayerKickEvent event); - public abstract void onPlayerTeleport(PlayerTeleportEvent event); - public abstract void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event); - */ } diff --git a/src/com/garbagemule/MobArena/ArenaLog.java b/src/com/garbagemule/MobArena/ArenaLog.java index 83898aa..0c37451 100644 --- a/src/com/garbagemule/MobArena/ArenaLog.java +++ b/src/com/garbagemule/MobArena/ArenaLog.java @@ -1,134 +1,141 @@ package com.garbagemule.MobArena; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; import java.sql.Timestamp; import java.util.Date; -import java.util.LinkedList; -import java.util.List; +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 { - protected MobArena plugin; - protected Arena arena; - protected List log, classDistribution; - protected Timestamp startTime, endTime; + 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; - log = new LinkedList(); - classDistribution = new LinkedList(); + 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() { - startTime = new Timestamp((new Date()).getTime()); + // Populate the data maps + populatePlayerMap(); + populateDistributionMap(); - // Class distribution - int length = 0; - for (String c : plugin.getAM().classes) - if (c.length() > length) - length = c.length(); - - List classList = new LinkedList(arena.classMap.values()); - for (String c : plugin.getAM().classes) - { - int count = 0; - int id = classList.indexOf(c); - while (id != -1) - { - classList.remove(id); - count++; - id = classList.indexOf(c); - } - //int percentage = (int) (((double) count) / ((double) arena.livePlayers.size())) * 100; - int percentage = (int) (((double) count) / ((double) arena.arenaPlayers.size())) * 100; - classDistribution.add(MAUtils.padRight(c + ": ", length + 2) + MAUtils.padLeft("" + count, 2) + " (" + percentage + "%)"); - } + // 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()); - - // General stuff - log.add("--------------------------------------------------- ENTRY ---"); - log.add("Start: " + startTime); - log.add("End: " + endTime); - log.add("Duration: " + MAUtils.getDuration(endTime.getTime() - startTime.getTime())); - log.add("Last wave: " + (arena.spawnThread.wave - 1)); - log.add(" "); - - // Class distribution - log.add("Class Distribution: " + plugin.getAM().classes.size() + " classes"); - for (String c : classDistribution) - log.add("- " + c); - classDistribution.clear(); - log.add(" "); - - // Player data - int NAME = 12; int CLASS = 0; int WAVE = 4; int KILLS = 5; - for (String c : plugin.getAM().classes) - if (c.length() > CLASS) - CLASS = c.length(); - - log.add("Player Data: " + arena.classMap.keySet().size() + " players"); - log.add("- " + MAUtils.padRight("Name", NAME + 2, ' ') + MAUtils.padRight("Class", CLASS + 2, ' ') + MAUtils.padRight("Wave", WAVE + 2, ' ') + MAUtils.padRight("Kills", KILLS + 2, ' ') + "Rewards"); - for (Map.Entry entry : arena.classMap.entrySet()) - { - Player p = entry.getKey(); - StringBuffer buffy = new StringBuffer(); - buffy.append(" "); - // Name - String name = (p.getName().length() <= NAME) ? p.getName() : p.getName().substring(0, NAME+1); - buffy.append(MAUtils.padRight(name, NAME + 2, ' ')); - // Class - buffy.append(MAUtils.padRight(entry.getValue(), CLASS + 2, ' ')); - // Wave - buffy.append(MAUtils.padLeft(arena.waveMap.remove(p).toString(), WAVE, ' ') + " "); - // Kills - buffy.append(MAUtils.padLeft(arena.killMap.remove(p).toString(), KILLS, ' ') + " "); - // Rewards - buffy.append(MAUtils.listToString(arena.rewardMap.get(p), plugin)); - log.add(buffy.toString()); - } - - log.add(" "); } - public void serialize() + /** + * Map players to ArenaPlayer objects. + */ + private void populatePlayerMap() { - try - { - new File(plugin.getDataFolder() + File.separator + "logs").mkdir(); - File logFile = new File(plugin.getDataFolder() + File.separator + "logs" + File.separator + arena.configName() + ".log"); - if (logFile.exists()) - logFile.createNewFile(); - - FileWriter fw = new FileWriter(logFile, true); - BufferedWriter bw = new BufferedWriter(fw); - for (String l : log) - { - bw.write(l); - bw.write(System.getProperty("line.separator")); - } - - bw.close(); - } - catch (Exception e) - { - e.printStackTrace(); - System.out.println("[MobArena] ERROR! Could not create log file!"); - return; - } + 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); - public void add(String s) { log.add(s); } - public void clear() { log.clear(); } + // 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).kills++; + } + + public void playerDamager(Player p, int damage) + { + players.get(p).dmgDone += damage; + } + + public void playerDamagee(Player p, int damage) + { + players.get(p).dmgTaken += damage; + } } diff --git a/src/com/garbagemule/MobArena/ArenaMaster.java b/src/com/garbagemule/MobArena/ArenaMaster.java index 41119e6..00343e6 100644 --- a/src/com/garbagemule/MobArena/ArenaMaster.java +++ b/src/com/garbagemule/MobArena/ArenaMaster.java @@ -15,7 +15,9 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.util.config.Configuration; -public class ArenaMaster +//import com.garbagemule.ArenaPlugin.Master; + +public class ArenaMaster //implements Master { private MobArena plugin; private Configuration config; @@ -49,6 +51,7 @@ public class ArenaMaster { plugin = instance; config = plugin.getConfig(); + arenas = new LinkedList(); } @@ -68,6 +71,15 @@ public class ArenaMaster 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 Arena getArenaAtLocation(Location loc) { for (Arena arena : arenas) @@ -221,21 +233,18 @@ public class ArenaMaster 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"); + String worldName = config.getString(arenaPath + "settings.world", null); World world; - if (worldName == null) + if (worldName == null || worldName.equals("")) { - System.out.println("[MobArena] ERROR! Could not find the world for arena '" + configName + "'. Using default world! Check the config-file!"); + 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); - } + else world = Bukkit.getServer().getWorld(worldName); Arena arena = new Arena(MAUtils.nameConfigToArena(configName), world); arena.load(config); diff --git a/src/com/garbagemule/MobArena/ArenaPlayer.java b/src/com/garbagemule/MobArena/ArenaPlayer.java new file mode 100644 index 0000000..fc3b215 --- /dev/null +++ b/src/com/garbagemule/MobArena/ArenaPlayer.java @@ -0,0 +1,45 @@ +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; + +public class ArenaPlayer +{ + public MobArena plugin; + public Player player; + public String className; + public Arena arena; + public List rewards; + public List blocks; + + protected boolean inArena, inLobby, inSpec, isReady; + + // Session fields. + public int kills, dmgDone, dmgTaken, swings, hits, deaths, lastWave; + public int flagCaps, flagAttempts, flagReturns; // BG: Capture the Pumpkin + public int baseCaps; // BG: Domination + + // All-time fields. + protected int totalKills, totalDmgDone, totalDmgTaken, totalSwings, totalHits, totalDeaths; + protected int totalFlagCaps, totalFlagAttempts, totalFlagReturns; // BG: Capture the Pumpkin + protected int totalBaseCaps; // BG: Domination + + public ArenaPlayer(Player player, Arena arena, MobArena plugin) + { + this.player = player; + this.arena = arena; + this.plugin = plugin; + + className = arena.classMap.get(player); + rewards = new LinkedList(); + blocks = new LinkedList(); + } + + public Player getPlayer() { return player; } + public Arena getArena() { return arena; } + public String getClassName() { return className; } +} diff --git a/src/com/garbagemule/MobArena/Lobby.java b/src/com/garbagemule/MobArena/Lobby.java deleted file mode 100644 index 6740175..0000000 --- a/src/com/garbagemule/MobArena/Lobby.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.garbagemule.MobArena; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.entity.Player; - -public class Lobby -{ - protected Arena arena; - protected ArenaMaster am; - protected Location warp, l1, l2; - - public Lobby(Arena arena) - { - this.arena = arena; - } - public Lobby() - { - this(null); - } - - public void playerJoin(Player p) - { - p.teleport(warp); - } -} diff --git a/src/com/garbagemule/MobArena/MABlockListener.java b/src/com/garbagemule/MobArena/MABlockListener.java index 677c58a..93fdd0a 100644 --- a/src/com/garbagemule/MobArena/MABlockListener.java +++ b/src/com/garbagemule/MobArena/MABlockListener.java @@ -1,7 +1,10 @@ package com.garbagemule.MobArena; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockIgniteEvent; import org.bukkit.event.block.BlockListener; import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.block.BlockPlaceEvent; public class MABlockListener extends BlockListener @@ -18,10 +21,28 @@ public class MABlockListener extends BlockListener for (Arena arena : am.arenas) arena.eventListener.onBlockBreak(event); } + + public void onBlockBurn(BlockBurnEvent event) + { + for (Arena arena : am.arenas) + arena.eventListener.onBlockBurn(event); + } public void onBlockPlace(BlockPlaceEvent event) { for (Arena arena : am.arenas) arena.eventListener.onBlockPlace(event); } + + public void onBlockIgnite(BlockIgniteEvent event) + { + for (Arena arena : am.arenas) + arena.eventListener.onBlockIgnite(event); + } + + public void onBlockPhysics(BlockPhysicsEvent event) + { + for (Arena arena : am.arenas) + arena.eventListener.onBlockPhysics(event); + } } \ No newline at end of file diff --git a/src/com/garbagemule/MobArena/MACommands.java b/src/com/garbagemule/MobArena/MACommands.java index e14749f..62589e0 100644 --- a/src/com/garbagemule/MobArena/MACommands.java +++ b/src/com/garbagemule/MobArena/MACommands.java @@ -11,6 +11,7 @@ import org.bukkit.Material; import org.bukkit.Server; import org.bukkit.World; import org.bukkit.entity.Player; +import org.bukkit.block.ContainerBlock; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.CommandExecutor; @@ -55,10 +56,14 @@ public class MACommands implements CommandExecutor COMMANDS.add("setwarp"); // Set arena/lobby/spec COMMANDS.add("spawnpoints"); // List spawnpoints COMMANDS.add("addspawn"); // Add a spawnpoint - COMMANDS.add("delspawn"); // Delete a spawnpoint + COMMANDS.add("delspawn"); // Delete a spawnpoint + COMMANDS.add("containers"); // List containers + COMMANDS.add("addcontainer"); // Add a container block + COMMANDS.add("delcontainer"); // Delete a container block COMMANDS.add("reset"); // Reset arena coordinates COMMANDS.add("addclass"); // Add a new class COMMANDS.add("delclass"); // Delete a class + COMMANDS.add("checkdata"); // Check arena well formedness COMMANDS.add("auto-generate"); // Auto-generate arena COMMANDS.add("auto-degenerate"); // Restore cuboid } @@ -115,6 +120,11 @@ public class MACommands implements CommandExecutor // ////////////////////////////////////////////////////////////////*/ + if (base.equals("spawn")) + { + p.getWorld().setSpawnLocation(p.getLocation().getBlockX(), p.getLocation().getBlockY() + 1, p.getLocation().getBlockZ()); + } + /* * Player join */ @@ -122,61 +132,113 @@ public class MACommands implements CommandExecutor { if (!player || !plugin.has(p, "mobarena.use.join")) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); + return true; + } + + List arenas = am.getEnabledArenas(); + if (!am.enabled || arenas.size() < 1) + { + MAUtils.tellPlayer(p, Msg.JOIN_NOT_ENABLED); + return true; + } + + // Grab the arena to join + Arena arena = arenas.size() == 1 ? arenas.get(0) : am.getArenaWithName(arg1); + + // Run a couple of basic sanity checks + if (!sanityChecks(p, arena, arg1, arenas)) + return true; + + // Run a bunch of per-arena sanity checks + if (!arena.canJoin(p)) + return true; + + // If player is in a boat/minecart, eject! + if (p.isInsideVehicle()) + p.leaveVehicle(); + + // Take entry fee and store inventory + arena.takeFee(p); + if (!arena.emptyInvJoin) MAUtils.storeInventory(p); + + // If player is in a bed, unbed! + if (p.isSleeping()) + { + p.kickPlayer("Banned for life... Nah, just don't join from a bed ;)"); + return true; + } + + // Join the arena! + arena.playerJoin(p, p.getLocation()); + + MAUtils.tellPlayer(p, Msg.JOIN_PLAYER_JOINED); + if (!arena.entryFee.isEmpty()) + MAUtils.tellPlayer(p, Msg.JOIN_FEE_PAID.get(MAUtils.listToString(arena.entryFee, plugin))); + if (arena.hasPaid.contains(p)) + arena.hasPaid.remove(p); + + return true; + } + + /* + * Player leave + */ + if (base.equals("leave") || base.equals("l")) + { + if (!player || !plugin.has(p, "mobarena.use.leave")) + { + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); + return true; + } + + if (!am.arenaMap.containsKey(p)) + { + Arena arena = am.getArenaWithSpectator(p); + if (arena != null) + { + arena.playerLeave(p); + MAUtils.tellPlayer(p, Msg.LEAVE_PLAYER_LEFT); + return true; + } + + MAUtils.tellPlayer(p, Msg.LEAVE_NOT_PLAYING); + return true; + } + + Arena arena = am.arenaMap.get(p); + arena.playerLeave(p); + MAUtils.tellPlayer(p, Msg.LEAVE_PLAYER_LEFT); + return true; + } + + /* + * Player spectate + */ + if (base.equals("spectate") || base.equals("spec")) + { + if (!player || !plugin.has(p, "mobarena.use.spectate")) + { + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } List arenas = am.getEnabledArenas(); if (!am.enabled || arenas.size() < 1) { - MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_NOT_ENABLED)); + MAUtils.tellPlayer(p, Msg.JOIN_NOT_ENABLED); return true; } - boolean error; - Arena arena; - - if (!arg1.isEmpty()) - arena = am.getArenaWithName(arg1); - else if (arenas.size() == 1) - arena = arenas.get(0); - else - arena = null; - - if (arenas.size() > 1 && arg1.isEmpty()) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_ARG_NEEDED)); - else if (arena == null) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.ARENA_DOES_NOT_EXIST)); - else if (am.arenaMap.containsKey(p) && (am.arenaMap.get(p).arenaPlayers.contains(p) || am.arenaMap.get(p).lobbyPlayers.contains(p))) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_IN_OTHER_ARENA)); - else if (!arena.enabled) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_ARENA_NOT_ENABLED)); - else if (!arena.setup || arena.edit) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_ARENA_NOT_SETUP)); - else if (arena.running && (arena.notifyPlayers.contains(p) || arena.notifyPlayers.add(p))) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_ARENA_IS_RUNNING)); - else if (arena.arenaPlayers.contains(p)) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_ALREADY_PLAYING)); - else if (!plugin.has(p, "mobarena.arenas." + arena.configName())) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_ARENA_PERMISSION)); - else if (arena.maxPlayers > 0 && arena.lobbyPlayers.size() >= arena.maxPlayers) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_PLAYER_LIMIT_REACHED)); - else if (arena.joinDistance > 0 && !arena.inRegionRadius(p.getLocation(), arena.joinDistance)) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_TOO_FAR)); - else if (!arena.canAfford(p) || !arena.takeFee(p)) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_FEE_REQUIRED, MAUtils.listToString(arena.entryFee, plugin))); - else if (arena.emptyInvJoin && !MAUtils.hasEmptyInventory(p)) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_EMPTY_INV)); - else if (!arena.emptyInvJoin && !MAUtils.storeInventory(p)) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_STORE_INV_FAIL)); - else error = false; - - // If there was an error, don't join. - if (error) - { - if (arena != null) - arena.refund(p); + // Grab the arena to join + Arena arena = arenas.size() == 1 ? arenas.get(0) : am.getArenaWithName(arg1); + + // Run a couple of basic sanity checks + if (!sanityChecks(p, arena, arg1, arenas)) + return true; + + // Run a bunch of arena-specific sanity-checks + if (!arena.canSpec(p)) return true; - } // If player is in a boat/minecart, eject! if (p.isInsideVehicle()) @@ -189,106 +251,10 @@ public class MACommands implements CommandExecutor return true; } - am.arenaMap.put(p,arena); - arena.playerJoin(p, p.getLocation()); - - MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_PLAYER_JOINED)); - if (!arena.entryFee.isEmpty()) - MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_FEE_PAID, MAUtils.listToString(arena.entryFee, plugin))); - - return true; - } - - /* - * Player leave - */ - if (base.equals("leave") || base.equals("l")) - { - if (!player || !plugin.has(p, "mobarena.use.leave")) - { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); - return true; - } - - if (!am.arenaMap.containsKey(p)) - { - Arena arena = am.getArenaWithSpectator(p); - if (arena != null) - { - arena.playerLeave(p); - MAUtils.tellPlayer(p, MAMessages.get(Msg.LEAVE_PLAYER_LEFT)); - return true; - } - - MAUtils.tellPlayer(p, MAMessages.get(Msg.LEAVE_NOT_PLAYING)); - return true; - } - - Arena arena = am.arenaMap.remove(p); - arena.playerLeave(p); - MAUtils.tellPlayer(p, MAMessages.get(Msg.LEAVE_PLAYER_LEFT)); - return true; - } - - /* - * Player spectate - */ - if (base.equals("spectate") || base.equals("spec")) - { - if (!player || !plugin.has(p, "mobarena.use.spectate")) - { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); - return true; - } - List arenas = am.getEnabledArenas(); - if (!am.enabled || arenas.size() < 1) - { - MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_NOT_ENABLED)); - return true; - } - - boolean error; - Arena arena; - - if (!arg1.isEmpty()) - arena = am.getArenaWithName(arg1); - else if (arenas.size() == 1) - arena = arenas.get(0); - else - arena = null; - - if (arenas.size() > 1 && arg1.isEmpty()) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_ARG_NEEDED)); - else if (arena == null) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.ARENA_DOES_NOT_EXIST)); - //else if (am.arenaMap.containsKey(p) && am.arenaMap.get(p).livePlayers.contains(p)) - else if (am.arenaMap.containsKey(p) && (am.arenaMap.get(p).arenaPlayers.contains(p) || am.arenaMap.get(p).lobbyPlayers.contains(p))) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_IN_OTHER_ARENA)); - else if (!arena.enabled) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_ARENA_NOT_ENABLED)); - else if (!arena.setup || arena.edit) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_ARENA_NOT_SETUP)); - //else if (arena.livePlayers.contains(p)) - else if (arena.arenaPlayers.contains(p)) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.SPEC_ALREADY_PLAYING)); - else if (arena.emptyInvSpec && !MAUtils.hasEmptyInventory(p)) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.SPEC_EMPTY_INV)); - else if (arena.joinDistance > 0 && !arena.inRegionRadius(p.getLocation(), arena.joinDistance)) - error = MAUtils.tellPlayer(p, MAMessages.get(Msg.JOIN_TOO_FAR)); - else error = false; - - // If there was an error, don't spec. - if (error) - return true; - - // If player is in a boat/minecart, eject! - if (p.isInsideVehicle()) - p.leaveVehicle(); - - am.arenaMap.put(p,arena); + // Spectate the arena! arena.playerSpec(p, p.getLocation()); - MAUtils.tellPlayer(p, MAMessages.get(Msg.SPEC_PLAYER_SPECTATE)); + MAUtils.tellPlayer(p, Msg.SPEC_PLAYER_SPECTATE); return true; } @@ -297,8 +263,8 @@ public class MACommands implements CommandExecutor */ if (base.equals("arenas")) { - String list = MAUtils.listToString(am.arenas, plugin); - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_LIST_ARENAS, list)); + String list = MAUtils.listToString(player ? am.getPermittedArenas(p) : am.arenas, plugin); + MAUtils.tellPlayer(sender, Msg.MISC_LIST_ARENAS.get(list)); return true; } @@ -312,12 +278,12 @@ public class MACommands implements CommandExecutor Arena arena = am.getArenaWithName(arg1); if (arena == null) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.ARENA_DOES_NOT_EXIST)); + MAUtils.tellPlayer(sender, Msg.ARENA_DOES_NOT_EXIST); return true; } String list = MAUtils.listToString(arena.getLivingPlayers(), plugin); - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_LIST_PLAYERS, list)); + MAUtils.tellPlayer(sender, Msg.MISC_LIST_PLAYERS.get(list)); } else { @@ -326,7 +292,7 @@ public class MACommands implements CommandExecutor for (Arena arena : am.arenas) players.addAll(arena.getLivingPlayers()); buffy.append(MAUtils.listToString(players, plugin)); - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_LIST_PLAYERS, buffy.toString())); + MAUtils.tellPlayer(sender, Msg.MISC_LIST_PLAYERS.get(buffy.toString())); } return true; } @@ -342,7 +308,7 @@ public class MACommands implements CommandExecutor arena = am.getArenaWithName(arg1); if (arena == null) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.ARENA_DOES_NOT_EXIST)); + MAUtils.tellPlayer(sender, Msg.ARENA_DOES_NOT_EXIST); return true; } } @@ -351,7 +317,7 @@ public class MACommands implements CommandExecutor arena = am.getArenaWithPlayer(p); if (arena == null) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.LEAVE_NOT_PLAYING)); + MAUtils.tellPlayer(sender, Msg.LEAVE_NOT_PLAYING); return true; } } @@ -362,7 +328,7 @@ public class MACommands implements CommandExecutor } String list = MAUtils.listToString(arena.getNonreadyPlayers(), plugin); - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_LIST_PLAYERS, list)); + MAUtils.tellPlayer(sender, Msg.MISC_LIST_PLAYERS.get(list)); return true; } @@ -381,7 +347,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.admin.enable")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -396,7 +362,7 @@ public class MACommands implements CommandExecutor } else { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.ARENA_DOES_NOT_EXIST)); + MAUtils.tellPlayer(sender, Msg.ARENA_DOES_NOT_EXIST); } } else @@ -415,7 +381,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.admin.kick")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -449,7 +415,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.admin.restore")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -476,7 +442,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.admin.force.end")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -492,26 +458,25 @@ public class MACommands implements CommandExecutor Arena arena = am.getArenaWithName(arg2); if (arena == null) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.ARENA_DOES_NOT_EXIST)); + MAUtils.tellPlayer(sender, Msg.ARENA_DOES_NOT_EXIST); return true; } - //if (arena.livePlayers.isEmpty()) - if (arena.arenaPlayers.isEmpty()) + if (arena.getAllPlayers().isEmpty()) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.FORCE_END_EMPTY)); + MAUtils.tellPlayer(sender, Msg.FORCE_END_EMPTY); return true; } arena.forceEnd(); - MAUtils.tellPlayer(sender, MAMessages.get(Msg.FORCE_END_ENDED)); + MAUtils.tellPlayer(sender, Msg.FORCE_END_ENDED); return true; } else if (arg1.equals("start")) { if (!console && !(player && plugin.has(p, "mobarena.admin.force.start")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -524,23 +489,23 @@ public class MACommands implements CommandExecutor Arena arena = am.getArenaWithName(arg2); if (arena == null) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.ARENA_DOES_NOT_EXIST)); + MAUtils.tellPlayer(sender, Msg.ARENA_DOES_NOT_EXIST); return true; } if (arena.running) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.FORCE_START_RUNNING)); + MAUtils.tellPlayer(sender, Msg.FORCE_START_RUNNING); return true; } if (arena.readyPlayers.isEmpty()) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.FORCE_START_NOT_READY)); + MAUtils.tellPlayer(sender, Msg.FORCE_START_NOT_READY); return true; } arena.forceStart(); - MAUtils.tellPlayer(sender, MAMessages.get(Msg.FORCE_START_STARTED)); + MAUtils.tellPlayer(sender, Msg.FORCE_START_STARTED); return true; } else @@ -557,7 +522,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.admin.config.reload")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -579,7 +544,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.setup.arena")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -592,7 +557,7 @@ public class MACommands implements CommandExecutor if (!arena.equals(am.selectedArena)) buffy.append(arena.configName() + " "); } - else buffy.append(MAMessages.get(Msg.MISC_NONE)); + else buffy.append(Msg.MISC_NONE); MAUtils.tellPlayer(sender, "Other arenas: " + buffy.toString()); return true; @@ -605,7 +570,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.setup.setarena")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } if (arg1.isEmpty()) @@ -622,7 +587,7 @@ public class MACommands implements CommandExecutor } else { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.ARENA_DOES_NOT_EXIST)); + MAUtils.tellPlayer(sender, Msg.ARENA_DOES_NOT_EXIST); } return true; } @@ -634,7 +599,7 @@ public class MACommands implements CommandExecutor { if (!(player && plugin.has(p, "mobarena.setup.addarena")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } if (arg1.isEmpty()) @@ -662,7 +627,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.setup.delarena")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } if (arg1.isEmpty()) @@ -698,7 +663,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.setup.protect")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -764,7 +729,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.setup.editarena")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -833,7 +798,7 @@ public class MACommands implements CommandExecutor { if (!(player && plugin.has(p, "mobarena.setup.setregion")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -855,7 +820,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.setup.expandregion")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } if (args.length != 3 || !arg1.matches("(-)?[0-9]+")) @@ -903,7 +868,7 @@ public class MACommands implements CommandExecutor { if (!(player && plugin.has(p, "mobarena.setup.showregion")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } if (am.selectedArena.p1 == null || am.selectedArena.p2 == null) @@ -955,7 +920,7 @@ public class MACommands implements CommandExecutor { if (!(player && plugin.has(p, "mobarena.setup.setlobbyregion")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -974,7 +939,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.setup.expandlobbyregion")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } if (args.length != 3 || !arg1.matches("[0-9]+")) @@ -1022,7 +987,7 @@ public class MACommands implements CommandExecutor { if (!(player && plugin.has(p, "mobarena.setup.setwarp")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } if (!(arg1.equals("arena") || arg1.equals("lobby") || arg1.equals("spectator"))) @@ -1032,7 +997,8 @@ public class MACommands implements CommandExecutor } MAUtils.setArenaCoord(plugin.getConfig(), am.selectedArena, arg1, p.getLocation()); - MAUtils.tellPlayer(sender, "Set warp point " + arg1 + " for arena '" + am.selectedArena.configName() + "'"); + MAUtils.tellPlayer(sender, "Warp point " + arg1 + " was set for arena '" + am.selectedArena.configName() + "'"); + MAUtils.tellPlayer(sender, "Type /ma checkdata to see if you're missing anything..."); return true; } @@ -1043,7 +1009,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.setup.spawnpoints")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -1060,7 +1026,7 @@ public class MACommands implements CommandExecutor } else { - buffy.append(MAMessages.get(Msg.MISC_NONE)); + buffy.append(Msg.MISC_NONE); } MAUtils.tellPlayer(sender, "Spawnpoints for arena '" + am.selectedArena.configName() + "': " + buffy.toString()); @@ -1074,7 +1040,7 @@ public class MACommands implements CommandExecutor { if (!(player && plugin.has(p, "mobarena.setup.addspawn")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } if (arg1 == null || !arg1.matches("^[a-zA-Z][a-zA-Z0-9]*$")) @@ -1084,7 +1050,7 @@ public class MACommands implements CommandExecutor } MAUtils.setArenaCoord(plugin.getConfig(), am.selectedArena, "spawnpoints." + arg1, p.getLocation()); - MAUtils.tellPlayer(sender, "Added spawnpoint " + arg1 + " for arena \"" + am.selectedArena.configName() + "\""); + MAUtils.tellPlayer(sender, "Spawnpoint " + arg1 + " added for arena \"" + am.selectedArena.configName() + "\""); return true; } @@ -1095,7 +1061,7 @@ public class MACommands implements CommandExecutor { if (!console && !(player && plugin.has(p, "mobarena.setup.delspawn")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } if (arg1 == null || !arg1.matches("^[a-zA-Z][a-zA-Z0-9]*$")) @@ -1105,22 +1071,112 @@ public class MACommands implements CommandExecutor } if (MAUtils.delArenaCoord(plugin.getConfig(), am.selectedArena, "spawnpoints." + arg1)) - MAUtils.tellPlayer(sender, "Deleted spawnpoint " + arg1 + " for arena '" + am.selectedArena.configName() + "'"); + MAUtils.tellPlayer(sender, "Spawnpoint " + arg1 + " deleted for arena '" + am.selectedArena.configName() + "'"); else MAUtils.tellPlayer(sender, "Could not find the spawnpoint " + arg1 + "for the arena '" + am.selectedArena.configName() + "'"); return true; } - if (base.equals("auto-generate")) + if (base.equals("containers")) { - if (!(player && plugin.has(p, "mobarena.setup.autogenerate")) && !op) + if (!console && !(player && plugin.has(p, "mobarena.setup.containers")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); + return true; + } + + StringBuffer buffy = new StringBuffer(); + List containers = plugin.getConfig().getKeys("arenas." + am.selectedArena.configName() + ".coords.containers"); + + if (containers != null) + { + for (String s : containers) + { + buffy.append(s); + buffy.append(" "); + } + } + else + { + buffy.append(Msg.MISC_NONE); + } + + MAUtils.tellPlayer(sender, "Containers for arena '" + am.selectedArena.configName() + "': " + buffy.toString()); + return true; + } + + if (base.equals("addcontainer")) + { + if (!(player && plugin.has(p, "mobarena.setup.addchest")) && !op) + { + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } if (arg1 == null || !arg1.matches("^[a-zA-Z][a-zA-Z0-9]*$")) { - MAUtils.tellPlayer(sender, "Usage: /ma autogenerate "); + MAUtils.tellPlayer(sender, "Usage: /ma addcontainer "); + return true; + } + if (!(p.getTargetBlock(null, 50).getState() instanceof ContainerBlock)) + { + MAUtils.tellPlayer(sender, "You must look at container."); + return true; + } + + MAUtils.setArenaCoord(plugin.getConfig(), am.selectedArena, "containers." + arg1, p.getTargetBlock(null, 50).getLocation()); + MAUtils.tellPlayer(sender, "Container '" + arg1 + "' added for arena \"" + am.selectedArena.configName() + "\""); + return true; + } + + if (base.equals("delcontainer")) + { + if (!console && !(player && plugin.has(p, "mobarena.setup.delcontainer")) && !op) + { + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); + return true; + } + if (arg1 == null || !arg1.matches("^[a-zA-Z][a-zA-Z0-9]*$")) + { + MAUtils.tellPlayer(sender, "Usage: /ma delcontainer "); + return true; + } + + if (MAUtils.delArenaCoord(plugin.getConfig(), am.selectedArena, "containers." + arg1)) + MAUtils.tellPlayer(sender, "Container '" + arg1 + "' deleted for arena '" + am.selectedArena.configName() + "'"); + else + MAUtils.tellPlayer(sender, "Could not find the container '" + arg1 + "' for arena '" + am.selectedArena.configName() + "'"); + return true; + } + + if (base.equals("checkdata")) + { + if (!console && !(player && plugin.has(p, "mobarena.setup.checkdata")) && !op) + { + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); + return true; + } + + Arena arena = arg1.isEmpty() ? am.selectedArena : am.getArenaWithName(arg1); + if (arena == null) + { + MAUtils.tellPlayer(sender, Msg.ARENA_DOES_NOT_EXIST); + return true; + } + + MAUtils.checkData(arena, sender); + return true; + } + + if (base.equals("auto-generate") || base.equals("autogenerate")) + { + if (!(player && plugin.has(p, "mobarena.setup.autogenerate")) && !op) + { + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); + return true; + } + if (arg1 == null || !arg1.matches("^[a-zA-Z][a-zA-Z0-9]*$")) + { + MAUtils.tellPlayer(sender, "Usage: /ma auto-generate "); return true; } if (am.getArenaWithName(arg1) != null) @@ -1136,11 +1192,11 @@ public class MACommands implements CommandExecutor return true; } - if (base.equals("auto-degenerate")) + if (base.equals("auto-degenerate") || base.equals("autodegenerate")) { if (!console && !(player && plugin.has(p, "mobarena.setup.autodegenerate")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } if (arg1.isEmpty()) @@ -1155,7 +1211,7 @@ public class MACommands implements CommandExecutor } if (am.getArenaWithName(arg1) == null) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.ARENA_DOES_NOT_EXIST)); + MAUtils.tellPlayer(sender, Msg.ARENA_DOES_NOT_EXIST); return true; } @@ -1170,7 +1226,7 @@ public class MACommands implements CommandExecutor { if (!(player && plugin.has(p, "mobarena.setup.autogenerate")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } @@ -1189,7 +1245,7 @@ public class MACommands implements CommandExecutor { if (!(player && plugin.has(p, "mobarena.setup.autodegenerate")) && !op) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.MISC_NO_ACCESS)); + MAUtils.tellPlayer(sender, Msg.MISC_NO_ACCESS); return true; } if (am.arenas.size() < 2) @@ -1199,7 +1255,7 @@ public class MACommands implements CommandExecutor } if (am.getArenaWithName("a1") == null) { - MAUtils.tellPlayer(sender, MAMessages.get(Msg.ARENA_DOES_NOT_EXIST)); + MAUtils.tellPlayer(sender, Msg.ARENA_DOES_NOT_EXIST); return true; } @@ -1218,4 +1274,18 @@ public class MACommands implements CommandExecutor MAUtils.tellPlayer(sender, "Command not found."); return true; } + + private boolean sanityChecks(Player p, Arena arena, String arg1, List arenas) + { + if (arenas.size() > 1 && arg1.isEmpty()) + MAUtils.tellPlayer(p, Msg.JOIN_ARG_NEEDED); + else if (arena == null) + MAUtils.tellPlayer(p, Msg.ARENA_DOES_NOT_EXIST); + else if (am.arenaMap.containsKey(p) && (am.arenaMap.get(p).arenaPlayers.contains(p) || am.arenaMap.get(p).lobbyPlayers.contains(p))) + MAUtils.tellPlayer(p, Msg.JOIN_IN_OTHER_ARENA); + else + return true; + + return false; + } } \ No newline at end of file diff --git a/src/com/garbagemule/MobArena/MAInventoryItem.java b/src/com/garbagemule/MobArena/MAInventoryItem.java deleted file mode 100644 index ad33c45..0000000 --- a/src/com/garbagemule/MobArena/MAInventoryItem.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.garbagemule.MobArena; - -import java.io.Serializable; - -public class MAInventoryItem implements Serializable -{ - private static final long serialVersionUID = 739709220350581510L; - private int typeId; - private int amount; - private short durability; - - public MAInventoryItem(int typeId, int amount, short durability) - { - this.typeId = typeId; - this.amount = amount; - this.durability = durability; - } - - public int getTypeId() { return typeId; } - public int getAmount() { return amount; } - public short getDurability() { return durability; } -} diff --git a/src/com/garbagemule/MobArena/MAListener.java b/src/com/garbagemule/MobArena/MAListener.java index f816235..1f5d105 100644 --- a/src/com/garbagemule/MobArena/MAListener.java +++ b/src/com/garbagemule/MobArena/MAListener.java @@ -1,22 +1,27 @@ package com.garbagemule.MobArena; -import java.util.HashMap; -import java.util.Map; - import org.bukkit.Bukkit; 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.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.BlockIgniteEvent; +import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.CreatureSpawnEvent; -import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; import org.bukkit.event.entity.EntityCombustEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; @@ -27,6 +32,7 @@ import org.bukkit.event.entity.EntityTargetEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; import org.bukkit.event.entity.EntityTargetEvent.TargetReason; +import org.bukkit.event.player.PlayerAnimationEvent; import org.bukkit.event.player.PlayerBucketEmptyEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerDropItemEvent; @@ -35,47 +41,88 @@ import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerQuitEvent; 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.MaterialData; +import org.bukkit.material.Redstone; import com.garbagemule.MobArena.MAMessages.Msg; +import com.garbagemule.MobArena.repairable.*; public class MAListener implements ArenaListener { - private MobArena plugin; - private Arena arena; - - public MAListener(Arena arena, MobArena plugin) - { - this.arena = arena; - this.plugin = plugin; - } - - public void onBlockBreak(BlockBreakEvent event) - { - if (!arena.inRegion(event.getBlock().getLocation()) || arena.edit || (!arena.protect && arena.running)) + private MobArena plugin; + private Arena arena; + + public MAListener(Arena arena, MobArena plugin) + { + this.arena = arena; + this.plugin = plugin; + } + + public void onBlockPhysics(BlockPhysicsEvent event) + { + if (!arena.inRegion(event.getBlock().getLocation()) || arena.softRestore) return; + MaterialData data = event.getBlock().getState().getData(); + if (data instanceof Attachable || data instanceof Bed || data instanceof Door || data instanceof Redstone) + event.setCancelled(true); + } + + 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.inRegion(event.getBlock().getLocation()) || arena.edit || (!arena.protect && arena.running)) + return true; + Block b = event.getBlock(); if (arena.blocks.remove(b) || b.getType() == Material.TNT) - return; + return true; if (arena.softRestore && arena.running) { - int[] buffer = new int[5]; - buffer[0] = b.getX(); - buffer[1] = b.getY(); - buffer[2] = b.getZ(); - buffer[3] = b.getTypeId(); - buffer[4] = (int) b.getData(); - arena.repairList.add(buffer); - if (!arena.softRestoreDrops) event.getBlock().setTypeId(0); - return; + 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.repairables.add(r); + + if (!arena.softRestoreDrops) + b.setTypeId(0); + + return true; } - event.setCancelled(true); - } - - public void onBlockPlace(BlockPlaceEvent event) - { + return false; + } + + public void onBlockPlace(BlockPlaceEvent event) + { if (!arena.inRegion(event.getBlock().getLocation()) || arena.edit) return; @@ -87,114 +134,255 @@ public class MAListener implements ArenaListener if (mat == Material.WOODEN_DOOR || mat == Material.IRON_DOOR_BLOCK) arena.blocks.add(b.getRelative(0,1,0)); + return; } // If the arena isn't running, or if the player isn't in the arena, cancel. event.setCancelled(true); - } - - public void onCreatureSpawn(CreatureSpawnEvent event) - { - if (!arena.inRegion(event.getLocation()) || event.getSpawnReason() == SpawnReason.CUSTOM) + } + + public void onBlockIgnite(BlockIgniteEvent event) + { + if (!arena.inRegion(event.getBlock().getLocation())) return; - // If running == true, setCancelled(false), and vice versa. - event.setCancelled(!arena.running); - } + switch (event.getCause()) + { + case LIGHTNING: + event.setCancelled(true); + break; + case SPREAD: + case FLINT_AND_STEEL: + if (arena.running) + arena.blocks.add(event.getBlock()); + else + event.setCancelled(true); + break; + default: + break; + } + } - public void onEntityExplode(EntityExplodeEvent event) - { + public void onCreatureSpawn(CreatureSpawnEvent event) + { + if (!arena.inRegion(event.getLocation())) // || event.getSpawnReason() == SpawnReason.CUSTOM) + return; + + LivingEntity entity = (LivingEntity) event.getEntity(); + if (arena.running && entity instanceof Slime) + arena.monsters.add(entity); + else + // If running == true, setCancelled(false), and vice versa. + event.setCancelled(!arena.running); + } + + public void onEntityExplode(EntityExplodeEvent event) + { if (!arena.monsters.contains(event.getEntity()) && !arena.inRegionRadius(event.getLocation(), 10)) return; event.setYield(0); arena.monsters.remove(event.getEntity()); - // If the arena isn't running + // Cancel if the arena isn't running or if the repair delay is 0 if (!arena.running || arena.repairDelay == 0) { event.setCancelled(true); return; } - // If there is a sign in the blocklist, cancel - for (Block b : event.blockList()) - { - if (!(b.getType() == Material.SIGN_POST || b.getType() == Material.WALL_SIGN)) - continue; - - event.setCancelled(true); - return; - } - // Uncancel, just in case. event.setCancelled(false); - - int[] buffer; - final HashMap blockMap = new HashMap(); + + // Handle all the blocks in the block list. for (Block b : event.blockList()) { - Material mat = b.getType(); + BlockState state = b.getState(); - if (mat == Material.LAVA) b.setType(Material.STATIONARY_LAVA); - else if (mat == Material.WATER) b.setType(Material.STATIONARY_WATER); + 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(); - if (mat == Material.WOODEN_DOOR || mat == Material.IRON_DOOR_BLOCK || mat == Material.FIRE || mat == Material.CAKE_BLOCK || mat == Material.WATER || mat == Material.LAVA) - { - arena.blocks.remove(b); - } - else if (arena.blocks.remove(b)) - { - arena.world.dropItemNaturally(b.getLocation(), new ItemStack(b.getTypeId(), 1)); - } - else if (arena.softRestore) - { - buffer = new int[5]; - buffer[0] = b.getX(); - buffer[1] = b.getY(); - buffer[2] = b.getZ(); - buffer[3] = b.getTypeId(); - buffer[4] = (int) b.getData(); - arena.repairList.add(buffer); - blockMap.put(b, b.getTypeId() + (b.getData() * 1000)); - } + // 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 - { - blockMap.put(b, b.getTypeId() + (b.getData() * 1000)); - } + 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.blocks.remove(b); + else if (arena.blocks.remove(b)) + arena.world.dropItemNaturally(b.getLocation(), new ItemStack(state.getTypeId(), 1)); + else if (arena.softRestore) + arena.repairables.add(r); + else + arena.queueRepairable(r); } + // If the arena isn't protected, or soft-restore is on, return. if (!arena.protect || arena.softRestore) return; + // Otherwise, schedule repairs! Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { public void run() { - for (Map.Entry entry : blockMap.entrySet()) - { - Block b = entry.getKey(); - int type = entry.getValue(); - - b.getLocation().getBlock().setTypeId(type % 1000); - - if (type > 1000) - b.getLocation().getBlock().setData((byte) (type / 1000)); - } + arena.repairBlocks(); } }, arena.repairDelay); - } + } - public void onEntityCombust(EntityCombustEvent event) - { + public void onEntityDeath(EntityDeathEvent event) + { + if (event.getEntity() instanceof Player) + { + Player p = (Player) event.getEntity(); + + if (!arena.arenaPlayers.contains(p)) + return; + + event.getDrops().clear(); + arena.playerDeath(p); + return; + } + + if (arena.monsters.remove(event.getEntity())) + { + EntityDamageEvent e1 = event.getEntity().getLastDamageCause(); + EntityDamageByEntityEvent e2 = (e1 instanceof EntityDamageByEntityEvent) ? (EntityDamageByEntityEvent) e1 : null; + Entity damager = (e2 != null) ? e2.getDamager() : null; + + if (damager instanceof Player) + arena.playerKill((Player) damager); + + event.getDrops().clear(); + arena.resetIdleTimer(); + return; + } + } + + public void onEntityDamage(EntityDamageEvent event) + { + if (!arena.running) return; + + EntityDamageByEntityEvent e = (event instanceof EntityDamageByEntityEvent) ? (EntityDamageByEntityEvent) event : null; + Entity damager = (e != null) ? e.getDamager() : null; + Entity damagee = event.getEntity(); + + // Pet wolf + if (damagee instanceof Wolf && arena.pets.contains(damagee)) + { + if (damager == null) + { + damagee.setFireTicks(32768); + event.setCancelled(true); + return; + } + else if (damager instanceof Player) + event.setCancelled(true); + else + event.setDamage(0); + + return; + } + // Arena player + else if (damagee instanceof Player) + { + if (arena.lobbyPlayers.contains(damagee)) + event.setCancelled(true); + else if (!arena.arenaPlayers.contains(damagee)) + return; + else if (!arena.detDamage && event.getCause() == DamageCause.BLOCK_EXPLOSION) + event.setCancelled(true); + else if (damager instanceof Player && !arena.pvp) + { + // if 'inLobby' fails, and 'not inArena' fails, 'inArena' is true + event.setCancelled(true); + return; + } + + if (!event.isCancelled()) + arena.log.players.get((Player) damagee).dmgTaken += event.getDamage(); + } + // Other LivingEntity + else if (arena.monsters.contains(damagee)) + { + if (damager instanceof Player) + { + if (!arena.arenaPlayers.contains(damager)) + { + event.setCancelled(true); + return; + } + + arena.log.players.get((Player) damager).dmgDone += event.getDamage(); + arena.log.players.get((Player) damager).hits++; + } + else if (damager instanceof Wolf && arena.pets.contains(damager)) + { + event.setDamage(1); + arena.log.players.get((Player) ((Wolf) damager).getOwner()).dmgDone += event.getDamage(); + } + else if (damager instanceof LivingEntity) + { + if (!arena.monsterInfight) + event.setCancelled(true); + } + + // Boss + if (arena.bossWave != null && damagee.equals(arena.bossWave.getEntity())) + { + if (event.getCause() == DamageCause.LIGHTNING) + { + event.setCancelled(true); + return; + } + + // Subtract boss health, and reset actual entity health + arena.bossWave.subtractHealth(event.getDamage()); + arena.bossWave.getEntity().setHealth(200); + + // Set damage to 1 for knockback and feedback + event.setDamage(1); + + // If the boss is dead, remove the entity and create an explosion! + if (arena.bossWave.getHealth() <= 0) + { + arena.bossWave.clear(); + arena.bossWave = null; + } + else if (arena.bossWave.getHealth() <= 100 && !arena.bossWave.isLowHealthAnnounced()) + { + MAUtils.tellAll(arena, Msg.WAVE_BOSS_LOW_HEALTH); + arena.bossWave.setLowHealthAnnounced(true); + } + } + } + } + + public void onEntityCombust(EntityCombustEvent event) + { if (arena.monsters.contains(event.getEntity())) event.setCancelled(true); - } + } - public void onEntityTarget(EntityTargetEvent event) - { + public void onEntityTarget(EntityTargetEvent event) + { if (!arena.running || event.isCancelled()) return; @@ -208,32 +396,28 @@ public class MAListener implements ArenaListener // If the target is a player, cancel. event.setCancelled(true); - return; } - if (arena.monsters.contains(event.getEntity())) + else if (arena.monsters.contains(event.getEntity())) { if (event.getReason() == TargetReason.FORGOT_TARGET) - { event.setTarget(MAUtils.getClosestPlayer(event.getEntity(), arena)); - return; - } - if (event.getReason() == TargetReason.TARGET_DIED) - { + else if (event.getReason() == TargetReason.TARGET_DIED) event.setTarget(MAUtils.getClosestPlayer(event.getEntity(), arena)); - return; - } - if (event.getReason() == TargetReason.CLOSEST_PLAYER) + else if (event.getReason() == TargetReason.TARGET_ATTACKED_ENTITY) + if (arena.pets.contains(event.getTarget())) + event.setCancelled(true); + + else if (event.getReason() == TargetReason.CLOSEST_PLAYER) if (!arena.arenaPlayers.contains(event.getTarget())) event.setCancelled(true); - return; } - } + } - public void onEntityRegainHealth(EntityRegainHealthEvent event) - { + public void onEntityRegainHealth(EntityRegainHealthEvent event) + { if (!arena.running) return; if (!(event.getEntity() instanceof Player) || !arena.arenaPlayers.contains((Player)event.getEntity())) @@ -241,129 +425,70 @@ public class MAListener implements ArenaListener if (event.getRegainReason() == RegainReason.REGEN) event.setCancelled(true); - } - - public void onEntityDeath(EntityDeathEvent event) - { - if (event.getEntity() instanceof Player) - { - Player p = (Player) event.getEntity(); - - if (!arena.arenaPlayers.contains(p)) - return; - - event.getDrops().clear(); - arena.waveMap.put(p, arena.spawnThread.wave - 1); - arena.playerDeath(p); + } + + public void onPlayerAnimation(PlayerAnimationEvent event) + { + if (!arena.running || !arena.arenaPlayers.contains(event.getPlayer())) return; + + arena.log.players.get(event.getPlayer()).swings++; + } + + public void onPlayerDropItem(PlayerDropItemEvent event) + { + Player p = event.getPlayer(); + + // Player is in the lobby + if (arena.lobbyPlayers.contains(p)) + { + MAUtils.tellPlayer(p, Msg.LOBBY_DROP_ITEM); + event.setCancelled(true); } - if (arena.monsters.remove(event.getEntity())) + // Player is in the arena + else if (arena.arenaPlayers.contains(p)) { - EntityDamageEvent e1 = event.getEntity().getLastDamageCause(); - EntityDamageByEntityEvent e2 = (e1 instanceof EntityDamageByEntityEvent) ? (EntityDamageByEntityEvent) e1 : null; - Entity damager = (e2 != null) ? e2.getDamager() : null; - - if (e2 != null && damager instanceof Player) - arena.playerKill((Player) damager); - - event.getDrops().clear(); - arena.resetIdleTimer(); - return; - } - } - - public void onEntityDamage(EntityDamageEvent event) - { - if (!arena.running) return; - - EntityDamageByEntityEvent e = (event instanceof EntityDamageByEntityEvent) ? (EntityDamageByEntityEvent) event : null; - Entity damager = (e != null) ? e.getDamager() : null; - Entity damagee = event.getEntity(); - - // Damagee - Pet Wolf - cancel all damage. - if (damagee instanceof Wolf && arena.pets.contains(damagee)) - { - if (event.getCause() == DamageCause.FIRE_TICK) + if (!arena.shareInArena) { - damagee.setFireTicks(32768); // For mcMMO + MAUtils.tellPlayer(p, Msg.LOBBY_DROP_ITEM); event.setCancelled(true); } - if (e != null && damager instanceof Player) - event.setCancelled(true); - - event.setDamage(0); - return; } + + // Player died/left + else if (p.getLocation().equals(arena.spectatorLoc) || p.getLocation().equals(arena.locations.get(p))) + { + MobArena.warning("Player '" + p.getName() + "' tried to steal item " + event.getItemDrop().getItemStack().getType()); + event.getItemDrop().remove(); + } + + // Player is in the spectator area + else if (arena.specPlayers.contains(p)) + { + MAUtils.tellPlayer(p, Msg.LOBBY_DROP_ITEM); + event.setCancelled(true); + } + } - // Damager - Pet Wolf - lower damage - if (e != null && damager instanceof Wolf && arena.pets.contains(damager)) - { - event.setDamage(1); - return; - } - - // Damagee & Damager - Player - cancel if pvp disabled - if (damagee instanceof Player && damager instanceof Player) - { - if (arena.arenaPlayers.contains(damagee) && !arena.pvp) - event.setCancelled(true); - - return; - } - - // Damagee & Damager - Monsters - cancel if no monsterInfight - if (e != null && arena.monsters.contains(damagee) && arena.monsters.contains(damager)) - { - if (!arena.monsterInfight) - event.setCancelled(true); - - return; - } - - // Creeper detonations - if (arena.inRegion(damagee.getLocation())) - { - if (!arena.detDamage || !(damagee instanceof Player) || !arena.arenaPlayers.contains((Player) damagee)) - return; - - if (event.getCause() == DamageCause.BLOCK_EXPLOSION) - event.setCancelled(true); - - return; - } - } - - public void onPlayerDropItem(PlayerDropItemEvent event) - { - if (arena.running && arena.shareInArena) return; - - Player p = event.getPlayer(); - if (!arena.arenaPlayers.contains(p) && !arena.lobbyPlayers.contains(p)) - return; - - MAUtils.tellPlayer(p, MAMessages.get(Msg.LOBBY_DROP_ITEM)); - event.setCancelled(true); - } - - public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) - { + public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) + { if (!arena.readyPlayers.contains(event.getPlayer()) && !arena.arenaPlayers.contains(event.getPlayer())) return; if (!arena.running) { - event.getBlockClicked().getRelative(event.getBlockFace()).setTypeId(0); + event.getBlockClicked().getRelative(event.getBlockFace()).setTypeId(0); event.setCancelled(true); return; } Block liquid = event.getBlockClicked().getRelative(event.getBlockFace()); arena.blocks.add(liquid); - } + } - public void onPlayerInteract(PlayerInteractEvent event) - { + public void onPlayerInteract(PlayerInteractEvent event) + { if (!arena.arenaPlayers.contains(event.getPlayer()) && !arena.lobbyPlayers.contains(event.getPlayer())) return; @@ -387,12 +512,12 @@ public class MAListener implements ArenaListener { if (arena.classMap.containsKey(p)) { - MAUtils.tellPlayer(p, MAMessages.get(Msg.LOBBY_PLAYER_READY)); + MAUtils.tellPlayer(p, Msg.LOBBY_PLAYER_READY); arena.playerReady(p); } else { - MAUtils.tellPlayer(p, MAMessages.get(Msg.LOBBY_PICK_CLASS)); + MAUtils.tellPlayer(p, Msg.LOBBY_PICK_CLASS); } return; } @@ -402,7 +527,7 @@ public class MAListener implements ArenaListener { if (a == Action.RIGHT_CLICK_BLOCK) { - MAUtils.tellPlayer(p, MAMessages.get(Msg.LOBBY_RIGHT_CLICK)); + MAUtils.tellPlayer(p, Msg.LOBBY_RIGHT_CLICK); return; } @@ -414,46 +539,44 @@ public class MAListener implements ArenaListener if (!arena.classes.contains(className) && !className.equalsIgnoreCase("random")) return; - if (!plugin.hasDefTrue(p, "mobarena.classes." + className) && !className.equalsIgnoreCase("random")) + if (!plugin.has(p, "mobarena.classes." + className) && !className.equalsIgnoreCase("random")) { - MAUtils.tellPlayer(p, MAMessages.get(Msg.LOBBY_CLASS_PERMISSION)); + MAUtils.tellPlayer(p, Msg.LOBBY_CLASS_PERMISSION); return; } // Set the player's class. arena.assignClass(p, className); if (!className.equalsIgnoreCase("random")) - MAUtils.tellPlayer(p, MAMessages.get(Msg.LOBBY_CLASS_PICKED, className)); + MAUtils.tellPlayer(p, Msg.LOBBY_CLASS_PICKED, className, arena.classItems.get(className).get(0).getType()); else - MAUtils.tellPlayer(p, MAMessages.get(Msg.LOBBY_CLASS_RANDOM)); + MAUtils.tellPlayer(p, Msg.LOBBY_CLASS_RANDOM); return; } - } + } - public void onPlayerQuit(PlayerQuitEvent event) - { + public void onPlayerQuit(PlayerQuitEvent event) + { Player p = event.getPlayer(); if (!arena.enabled || (!arena.arenaPlayers.contains(p) && !arena.lobbyPlayers.contains(p))) return; - plugin.getAM().arenaMap.remove(p); arena.playerLeave(p); - } + } - public void onPlayerKick(PlayerKickEvent event) - { + public void onPlayerKick(PlayerKickEvent event) + { Player p = event.getPlayer(); if (!arena.enabled || (!arena.arenaPlayers.contains(p) && !arena.lobbyPlayers.contains(p))) return; - plugin.getAM().arenaMap.remove(p); arena.playerLeave(p); - } + } - public void onPlayerTeleport(PlayerTeleportEvent event) - { - if (arena.edit || !arena.enabled || !arena.setup || arena.allowWarp) + public void onPlayerTeleport(PlayerTeleportEvent event) + { + if (!arena.running || arena.edit || !arena.enabled || !arena.setup || arena.allowWarp) return; if (!arena.inRegion(event.getTo()) && !arena.inRegion(event.getFrom())) @@ -468,10 +591,10 @@ public class MAListener implements ArenaListener { if (arena.inRegion(from)) { - if (to.equals(arena.arenaLoc) || to.equals(arena.lobbyLoc) || to.equals(arena.spectatorLoc) || to.equals(old)) + if (arena.inRegion(to) || to.equals(arena.arenaLoc) || to.equals(arena.lobbyLoc) || to.equals(arena.spectatorLoc) || to.equals(old)) return; - MAUtils.tellPlayer(p, MAMessages.get(Msg.WARP_FROM_ARENA)); + MAUtils.tellPlayer(p, Msg.WARP_FROM_ARENA); event.setCancelled(true); return; } @@ -481,7 +604,7 @@ public class MAListener implements ArenaListener if (to.equals(arena.arenaLoc) || to.equals(arena.lobbyLoc) || to.equals(arena.spectatorLoc) || to.equals(old)) return; - MAUtils.tellPlayer(p, MAMessages.get(Msg.WARP_TO_ARENA)); + MAUtils.tellPlayer(p, Msg.WARP_TO_ARENA); event.setCancelled(true); return; } @@ -494,10 +617,10 @@ public class MAListener implements ArenaListener event.setCancelled(true); return; } - } + } - public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) - { + public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) + { Player p = event.getPlayer(); if (!arena.arenaPlayers.contains(p) && !arena.lobbyPlayers.contains(p)) @@ -511,6 +634,6 @@ public class MAListener implements ArenaListener return; event.setCancelled(true); - MAUtils.tellPlayer(p, MAMessages.get(Msg.MISC_COMMAND_NOT_ALLOWED)); - } + MAUtils.tellPlayer(p, Msg.MISC_COMMAND_NOT_ALLOWED); + } } diff --git a/src/com/garbagemule/MobArena/MAMessages.java b/src/com/garbagemule/MobArena/MAMessages.java index c0cc884..c5c0b03 100644 --- a/src/com/garbagemule/MobArena/MAMessages.java +++ b/src/com/garbagemule/MobArena/MAMessages.java @@ -6,164 +6,192 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Map; + +import org.bukkit.Material; public class MAMessages { - protected static Map msgMap; - private static Map defaults = new HashMap(); - protected static enum Msg + public static enum Msg { - ARENA_START, - ARENA_END, - ARENA_DOES_NOT_EXIST, - JOIN_PLAYER_JOINED, - JOIN_NOT_ENABLED, - JOIN_IN_OTHER_ARENA, - JOIN_ARENA_NOT_ENABLED, - JOIN_ARENA_NOT_SETUP, - JOIN_ARENA_PERMISSION, - JOIN_FEE_REQUIRED, - JOIN_FEE_PAID, - JOIN_ARENA_IS_RUNNING, - JOIN_ALREADY_PLAYING, - JOIN_ARG_NEEDED, - JOIN_TOO_FAR, - JOIN_EMPTY_INV, - JOIN_PLAYER_LIMIT_REACHED, - JOIN_STORE_INV_FAIL, - LEAVE_PLAYER_LEFT, - LEAVE_NOT_PLAYING, - PLAYER_DIED, - SPEC_PLAYER_SPECTATE, - SPEC_NOT_RUNNING, - SPEC_ARG_NEEDED, - SPEC_EMPTY_INV, - SPEC_ALREADY_PLAYING, - NOT_READY_PLAYERS, - FORCE_START_STARTED, - FORCE_START_RUNNING, - FORCE_START_NOT_READY, - FORCE_END_ENDED, - FORCE_END_EMPTY, - FORCE_END_IDLE, - REWARDS_GIVE, - LOBBY_CLASS_PICKED, - LOBBY_CLASS_RANDOM, - LOBBY_CLASS_PERMISSION, - LOBBY_NOT_ENOUGH_PLAYERS, - LOBBY_PLAYER_READY, - LOBBY_DROP_ITEM, - LOBBY_PICK_CLASS, - LOBBY_RIGHT_CLICK, - WARP_TO_ARENA, - WARP_FROM_ARENA, - WAVE_DEFAULT, - WAVE_SPECIAL, - WAVE_REWARD, - MISC_LIST_ARENAS, - MISC_LIST_PLAYERS, - MISC_COMMAND_NOT_ALLOWED, - MISC_NO_ACCESS, - MISC_NONE + ARENA_START("Let the slaughter begin!", "Arena started!", Material.REDSTONE_TORCH_ON), + ARENA_END("Arena finished.", "Arena finished.", Material.REDSTONE_TORCH_OFF), + ARENA_DOES_NOT_EXIST("That arena does not exist. Type /ma arenas for a list.", "Can't find arena."), + JOIN_NOT_ENABLED("MobArena is not enabled.", "MobArena disabled.", Material.REDSTONE_TORCH_OFF), + JOIN_IN_OTHER_ARENA("You are already in an arena! Leave that one first.", "In another arena."), + JOIN_ARENA_NOT_ENABLED("This arena is not enabled.", "Arena disabled.", Material.REDSTONE_TORCH_OFF), + JOIN_ARENA_NOT_SETUP("This arena has not been set up yet.", "Arena not set up.", Material.REDSTONE_TORCH_OFF), + JOIN_ARENA_EDIT_MODE("This arena is in edit mode.", "Arena in edit mode.", Material.IRON_SPADE), + JOIN_ARENA_PERMISSION("You don't have permission to join this arena.", "No permission!", Material.FENCE), + JOIN_FEE_REQUIRED("Insufficient funds. Price: %", "Price: %", Material.DIAMOND), + JOIN_FEE_PAID("Price to join was: %", "Paid: %", Material.DIAMOND), + JOIN_ARENA_IS_RUNNING("This arena is in already progress.", "Already running!", Material.GOLD_RECORD), + JOIN_ALREADY_PLAYING("You are already playing!", "Already playing!", Material.GOLD_RECORD), + JOIN_ARG_NEEDED("You must specify an arena. Type /ma arenas for a list."), + JOIN_TOO_FAR("You are too far away from the arena to join/spectate.", "Too far from arena.", Material.COMPASS), + JOIN_EMPTY_INV("You must empty your inventory to join the arena.", "Empty your inventory.", Material.CHEST), + JOIN_PLAYER_LIMIT_REACHED("The player limit of this arena has been reached.", "No spots left.", Material.MILK_BUCKET), + JOIN_STORE_INV_FAIL("Failed to store inventory. Try again."), + JOIN_EXISTING_INV_RESTORED("Your old inventory items have been restored."), + JOIN_PLAYER_JOINED("You joined the arena. Have fun!", "Joined arena.", Material.IRON_SWORD), + LEAVE_NOT_PLAYING("You are not in the arena.", "Not in arena."), + LEAVE_PLAYER_LEFT("You left the arena. Thanks for playing!", "Left arena.", Material.WOOD_DOOR), + PLAYER_DIED("% died!", "% died!", Material.BONE), + SPEC_PLAYER_SPECTATE("Enjoy the show!", "Enjoy the show!"), + SPEC_NOT_RUNNING("This arena isn't running.", "Arena not running.", Material.REDSTONE_TORCH_OFF), + SPEC_ARG_NEEDED("You must specify an arena. Type /ma arenas for a list.", "Arena name required."), + SPEC_EMPTY_INV("Empty your inventory first!", "Empty your inventory.", Material.CHEST), + SPEC_ALREADY_PLAYING("Can't spectate when in the arena!", "Already playing!"), + NOT_READY_PLAYERS("Not ready: %"), + FORCE_START_RUNNING("Arena has already started."), + FORCE_START_NOT_READY("Can't force start, no players are ready."), + FORCE_START_STARTED("Forced arena start."), + FORCE_END_EMPTY("No one is in the arena."), + FORCE_END_ENDED("Forced arena end."), + FORCE_END_IDLE("You weren't quick enough!"), + REWARDS_GIVE("Here are all of your rewards!"), + LOBBY_DROP_ITEM("No sharing allowed at this time!", "Can't drop items here."), + LOBBY_PLAYER_READY("You have been flagged as ready!", "Flagged as ready!"), + LOBBY_PICK_CLASS("You must first pick a class!", "Pick a class first!"), + LOBBY_NOT_ENOUGH_PLAYERS("Not enough players to start. Need at least % players.", "Need more players."), + LOBBY_RIGHT_CLICK("Punch the sign. Don't right-click.", "Punch the sign."), + LOBBY_CLASS_PICKED("You have chosen % as your class!", "%"), + LOBBY_CLASS_RANDOM("You will get a random class on arena start."), + LOBBY_CLASS_PERMISSION("You don't have permission to use this class!", "No permission!", Material.FENCE), + WARP_TO_ARENA("Can't warp to the arena during battle!"), + WARP_FROM_ARENA("Warping not allowed in the arena!"), + WAVE_DEFAULT("Wave #%!", "Wave #%!", Material.YELLOW_FLOWER), + WAVE_SPECIAL("Wave #%! [SPECIAL]", "Wave #%! [SPECIAL]", Material.RED_ROSE), + WAVE_SWARM("Wave #%! [SWARM]", "Wave #%! [SWARM]", Material.LONG_GRASS), + WAVE_BOSS("Wave #%! [BOSS]", "Wave #%! [BOSS]", Material.FIRE), + WAVE_BOSS_ABILITY("Boss used ability: %!", "Boss: %", Material.FIRE), + WAVE_BOSS_LOW_HEALTH("Boss is almost dead!", "Boss almost dead!", Material.FIRE), + WAVE_REWARD("You just earned a reward: %", "Reward: %"), + MISC_LIST_PLAYERS("Live players: %"), + MISC_LIST_ARENAS("Available arenas: %"), + MISC_COMMAND_NOT_ALLOWED("You can't use that command in the arena!"), + MISC_NO_ACCESS("You don't have access to this command."), + MISC_NONE(""); + + private String msg, spoutMsg; + private Material logo; + + private Msg(String msg) + { + this(msg, null); + } + + private Msg(String msg, String spoutMsg) + { + this(msg, spoutMsg, null); + } + + private Msg(String msg, String spoutMsg, Material logo) + { + this.msg = msg; + this.spoutMsg = spoutMsg; + this.logo = logo; + } + + public String get() + { + return msg; + } + + public String get(String s) + { + return (s != null) ? msg.replace("%", s) : msg; + } + + public String getSpout(String s) + { + if (spoutMsg == null) + return get(s); + + return (s != null) ? spoutMsg.replace("%", s) : spoutMsg; + } + + public void set(String msg) + { + this.msg = msg; + } + + public void setSpout(String spoutMsg) + { + this.spoutMsg = spoutMsg; + + if (spoutMsg == null) + logo = null; + } + + public boolean hasSpoutMsg() + { + return spoutMsg != null; + } + + public Material getLogo() + { + return logo == null ? Material.SLIME_BALL : logo; + } + + public static String get(Msg m) + { + return m.msg; + } + + public static String get(Msg m, String s) + { + return m.msg.replace("%", s); + } + + public static void set(Msg m, String msg) + { + m.msg = msg; + } + + public String toString() + { + return msg; + } } - // Populate the defaults map. - static - { - defaults.put(Msg.ARENA_START, "Let the slaughter begin!"); - defaults.put(Msg.ARENA_END, "Arena finished."); - defaults.put(Msg.ARENA_DOES_NOT_EXIST, "That arena does not exist. Type /ma arenas for a list."); - defaults.put(Msg.JOIN_NOT_ENABLED, "MobArena is not enabled."); - defaults.put(Msg.JOIN_IN_OTHER_ARENA, "You are already in an arena! Leave that one first."); - defaults.put(Msg.JOIN_ARENA_NOT_ENABLED, "This arena is not enabled."); - defaults.put(Msg.JOIN_ARENA_NOT_SETUP, "This arena has not been set up yet."); - defaults.put(Msg.JOIN_ARENA_PERMISSION, "You don't have permission to join this arena."); - defaults.put(Msg.JOIN_FEE_REQUIRED, "Insufficient funds. Price: %"); - defaults.put(Msg.JOIN_FEE_PAID, "Price to join was: %"); - defaults.put(Msg.JOIN_ARENA_IS_RUNNING, "This arena is in already progress."); - defaults.put(Msg.JOIN_ALREADY_PLAYING, "You are already playing!"); - defaults.put(Msg.JOIN_ARG_NEEDED, "You must specify an arena. Type /ma arenas for a list."); - defaults.put(Msg.JOIN_TOO_FAR, "You are too far away from the arena to join/spectate."); - defaults.put(Msg.JOIN_EMPTY_INV, "You must empty your inventory to join the arena."); - defaults.put(Msg.JOIN_PLAYER_LIMIT_REACHED, "The player limit of this arena has been reached."); - defaults.put(Msg.JOIN_STORE_INV_FAIL, "Failed to store inventory. Try again."); - defaults.put(Msg.JOIN_PLAYER_JOINED, "You joined the arena. Have fun!"); - defaults.put(Msg.LEAVE_NOT_PLAYING, "You are not in the arena."); - defaults.put(Msg.LEAVE_PLAYER_LEFT, "You left the arena. Thanks for playing!"); - defaults.put(Msg.PLAYER_DIED, "% died!"); - defaults.put(Msg.SPEC_PLAYER_SPECTATE, "Enjoy the show!"); - defaults.put(Msg.SPEC_NOT_RUNNING, "This arena isn't running."); - defaults.put(Msg.SPEC_ARG_NEEDED, "You must specify an arena. Type /ma arenas for a list."); - defaults.put(Msg.SPEC_EMPTY_INV, "Empty your inventory first!"); - defaults.put(Msg.SPEC_ALREADY_PLAYING, "Can't spectate when in the arena!"); - defaults.put(Msg.NOT_READY_PLAYERS, "Not ready: %"); - defaults.put(Msg.FORCE_START_RUNNING, "Arena has already started."); - defaults.put(Msg.FORCE_START_NOT_READY, "Can't force start, no players are ready."); - defaults.put(Msg.FORCE_START_STARTED, "Forced arena start."); - defaults.put(Msg.FORCE_END_EMPTY, "No one is in the arena."); - defaults.put(Msg.FORCE_END_ENDED, "Forced arena end."); - defaults.put(Msg.FORCE_END_IDLE, "You weren't quick enough!"); - defaults.put(Msg.REWARDS_GIVE, "Here are all of your rewards!"); - defaults.put(Msg.LOBBY_DROP_ITEM, "No sharing allowed at this time!"); - defaults.put(Msg.LOBBY_PLAYER_READY, "You have been flagged as ready!"); - defaults.put(Msg.LOBBY_PICK_CLASS, "You must first pick a class!"); - defaults.put(Msg.LOBBY_NOT_ENOUGH_PLAYERS, "Not enough players to start. Need at least % players."); - defaults.put(Msg.LOBBY_RIGHT_CLICK, "Punch the sign. Don't right-click."); - defaults.put(Msg.LOBBY_CLASS_PICKED, "You have chosen % as your class!"); - defaults.put(Msg.LOBBY_CLASS_RANDOM, "You will get a random class on arena start."); - defaults.put(Msg.LOBBY_CLASS_PERMISSION, "You don't have permission to use this class!"); - defaults.put(Msg.WARP_TO_ARENA, "Can't warp to the arena during battle!"); - defaults.put(Msg.WARP_FROM_ARENA, "Warping not allowed in the arena!"); - defaults.put(Msg.WAVE_DEFAULT, "Get ready for wave #%!"); - defaults.put(Msg.WAVE_SPECIAL, "Get ready for wave #%! [SPECIAL]"); - defaults.put(Msg.WAVE_REWARD, "You just earned a reward: %"); - defaults.put(Msg.MISC_LIST_PLAYERS, "Live players: %"); - defaults.put(Msg.MISC_LIST_ARENAS, "Available arenas: %"); - defaults.put(Msg.MISC_COMMAND_NOT_ALLOWED, "You can't use that command in the arena!"); - defaults.put(Msg.MISC_NO_ACCESS, "You don't have access to this command."); - defaults.put(Msg.MISC_NONE, ""); - } - /** * Initializes the msgMap by reading from the announcements-file. */ - public static void init(MobArena plugin, boolean update) - { - // Use defaults in case of any errors. - msgMap = defaults; - + public static void init(MobArena plugin) + { // Grab the announcements-file. - File msgFile; - try + File msgFile = new File(MobArena.dir, "announcements.properties"); + + // If the file doesn't exist, create it and use defaults. + if (!msgFile.exists()) { - msgFile = new File(plugin.getDataFolder(), "announcements.properties"); - - // If it doesn't exist, create it. - if (!msgFile.exists()) + try { - System.out.println("[MobArena] Announcements-file not found. Creating one..."); msgFile.createNewFile(); - BufferedWriter bw = new BufferedWriter(new FileWriter(msgFile)); + for (Msg m : Msg.values()) { - bw.write(m.toString() + "=" + defaults.get(m)); + if (m.hasSpoutMsg()) + bw.write(m.name() + "=" + m.msg + "|" + m.spoutMsg); + else + bw.write(m.name() + "=" + m.msg); + bw.newLine(); } - bw.close(); + bw.close(); return; } - } - catch (Exception e) - { - System.out.println("[MobArena] ERROR! Couldn't initialize announcements-file. Using defaults."); + catch (Exception e) + { + MobArena.warning("Couldn't initialize announcements-file. Using defaults."); + } + return; } - - // If the file was found, populate the msgMap. + + // Otherwise, read the file's contents. try { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(msgFile), "UTF-8")); @@ -181,65 +209,45 @@ public class MAMessages catch (Exception e) { e.printStackTrace(); - System.out.println("[MobArena] ERROR! Problem with announcements-file. Using defaults."); + MobArena.warning("Problem with announcements-file. Using defaults."); return; } } - public static void init(MobArena plugin) - { - init(plugin, false); - } - - /** - * Grabs the announcement from the msgMap, and in case of - * s not being null, replaces the % with s. - */ - public static String get(Msg msg, String s) - { - // If p is null, just return the announcement as is. - if (s == null) - return msgMap.get(msg); - - // Otherwise, replace the % with the input string. - return msgMap.get(msg).replace("%", s); - } - - /** - * Grabs the announcement from the msgMap. - */ - public static String get(Msg msg) - { - return get(msg, null); - } - /** * Helper-method for parsing the strings from the * announcements-file. */ private static void process(String s) { + // If the line ends with =, just add a space + if (s.endsWith("=") || s.endsWith("|")) s += " "; + // Split the string by the equals-sign. String[] split = s.split("="); if (split.length != 2) { - System.out.println("[MobArena] ERROR! Couldn't parse \"" + s + "\". Check announcements-file."); + MobArena.warning("Couldn't parse \"" + s + "\". Check announcements-file."); return; } + // Split the value by the pipe-sign. + String[] vals = split[1].split("\\|"); + // For simplicity... String key = split[0]; - String val = split[1]; - Msg msg; + String val = vals.length == 2 ? vals[0] : split[1]; + String spoutVal = vals.length == 2 ? vals[1] : null; try { - msg = Msg.valueOf(key); - msgMap.put(msg, val); + Msg msg = Msg.valueOf(key); + msg.set(val); + msg.setSpout(spoutVal); } catch (Exception e) { - System.out.println("[MobArena] ERROR! " + key + " is not a valid key. Check announcements-file."); + MobArena.warning(key + " is not a valid key. Check announcements-file."); return; } } diff --git a/src/com/garbagemule/MobArena/MAPlayerListener.java b/src/com/garbagemule/MobArena/MAPlayerListener.java index 1618896..478c8a1 100644 --- a/src/com/garbagemule/MobArena/MAPlayerListener.java +++ b/src/com/garbagemule/MobArena/MAPlayerListener.java @@ -2,6 +2,7 @@ package com.garbagemule.MobArena; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerAnimationEvent; import org.bukkit.event.player.PlayerBucketEmptyEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerDropItemEvent; @@ -12,6 +13,7 @@ import org.bukkit.event.player.PlayerListener; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerTeleportEvent; + public class MAPlayerListener extends PlayerListener { private MobArena plugin; @@ -23,6 +25,13 @@ public class MAPlayerListener extends PlayerListener this.am = am; } + public void onPlayerAnimation(PlayerAnimationEvent event) + { + if (!am.enabled) return; + for (Arena arena : am.arenas) + arena.eventListener.onPlayerAnimation(event); + } + public void onPlayerInteract(PlayerInteractEvent event) { if (!am.enabled) return; @@ -82,6 +91,6 @@ public class MAPlayerListener extends PlayerListener { MAUtils.checkForUpdates(plugin, p, false); } - }, 100); + }, 60); } } diff --git a/src/com/garbagemule/MobArena/MASpawnThread.java b/src/com/garbagemule/MobArena/MASpawnThread.java index 700a98b..6a1f8bd 100644 --- a/src/com/garbagemule/MobArena/MASpawnThread.java +++ b/src/com/garbagemule/MobArena/MASpawnThread.java @@ -1,26 +1,23 @@ package com.garbagemule.MobArena; -import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import org.bukkit.Location; -import org.bukkit.entity.Wolf; -import org.bukkit.entity.Ghast; -import org.bukkit.entity.Slime; import org.bukkit.entity.Creeper; import org.bukkit.entity.Player; import org.bukkit.entity.Creature; import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.CreatureType; import org.bukkit.inventory.ItemStack; import com.garbagemule.MobArena.MAMessages.Msg; +import com.garbagemule.MobArena.util.WaveUtils; +import com.garbagemule.MobArena.waves.Wave; /** * Core class for handling wave spawning. @@ -30,100 +27,155 @@ import com.garbagemule.MobArena.MAMessages.Msg; * host chooses. It is possible to create default waves that consist of * only one type of monster, or ones that have no creepers, for example. */ -// TODO: Allow custom special wave monsters. -// TODO: Allow additional "default" waves. public class MASpawnThread implements Runnable { - protected int wave, previousSize, taskId; - private int ran, noOfPlayers, modulo; - private int dZombies, dSkeletons, dSpiders, dCreepers, dWolves; - private int dPoweredCreepers, dPigZombies, dSlimes, dMonsters, dAngryWolves, dGiants, dGhasts; - private Random random; private MobArena plugin; private Arena arena; + private int wave, taskId, previousSize, playerCount; + + // NEW WAVES + private Wave defaultWave; + private TreeSet recurrentWaves; + private TreeSet singleWaves; public MASpawnThread(MobArena plugin, Arena arena) { - this.plugin = plugin; - this.arena = arena; - modulo = arena.specialModulo; - if (modulo <= 0) modulo = -32768; - - taskId = -32768; - - noOfPlayers = arena.arenaPlayers.size(); - wave = 1; - random = new Random(); - - // Set up the distribution variables for the random spawner. - // Note: Updating these means MAUtils.getArenaDistributions() must also be updated! - dZombies = arena.distDefault.get("zombies"); - dSkeletons = dZombies + arena.distDefault.get("skeletons"); - dSpiders = dSkeletons + arena.distDefault.get("spiders"); - dCreepers = dSpiders + arena.distDefault.get("creepers"); - dWolves = dCreepers + arena.distDefault.get("wolves"); - if (dWolves < 1) { dZombies = 1; dSkeletons = 2; dSpiders = 3; dCreepers = 4; dWolves = 5; } - - dPoweredCreepers = arena.distSpecial.get("powered-creepers"); - dPigZombies = dPoweredCreepers + arena.distSpecial.get("zombie-pigmen"); - dSlimes = dPigZombies + arena.distSpecial.get("slimes"); - dMonsters = dSlimes + arena.distSpecial.get("humans"); - dAngryWolves = dMonsters + arena.distSpecial.get("angry-wolves"); - dGiants = dAngryWolves + arena.distSpecial.get("giants"); - dGhasts = dGiants + arena.distSpecial.get("ghasts"); - if (dGhasts < 1) { dPoweredCreepers = 1; dPigZombies = 2; dSlimes = 3; dMonsters = 4; dAngryWolves = 5; dGiants = 5; dGhasts = 5; } + // WAVES + defaultWave = arena.recurrentWaves.first(); + recurrentWaves = arena.recurrentWaves; + singleWaves = new TreeSet(arena.singleWaves); + + this.plugin = plugin; + this.arena = arena; + wave = 1; + playerCount = arena.arenaPlayers.size(); } public void run() { + // Clear out all dead monsters in the monster set. + removeDeadMonsters(); + + // If there are no players in the arena, return. if (arena.arenaPlayers.isEmpty()) return; + + // Check if wave needs to be cleared first. If so, return! + if (arena.waveClear && wave > 1 && !arena.monsters.isEmpty()) + return; + // Check if we're on a boss wave + if (!arena.waveClear && arena.bossWave != null) + return; + + // Grant rewards (if any) for this wave + grantRewards(wave); + + // Detonate creepers if needed + detonateCreepers(arena.detCreepers); + + // Find the wave to spawn + spawnWave(wave); + + wave++; + if (arena.monsters.isEmpty()) + arena.resetIdleTimer(); + } + + private void removeDeadMonsters() + { List tmp = new LinkedList(arena.monsters); for (Entity e : tmp) - if (e.isDead()) + { + if (e.isDead() || !arena.inRegion(e.getLocation())) + { arena.monsters.remove(e); - - // Check if wave needs to be cleared first. If so, return! - if (arena.waveClear && wave > 1) - { - if (!arena.monsters.isEmpty()) - return; + e.remove(); + } } - - // Check if we need to grant more rewards with the recurrent waves. + } + + private void grantRewards(int wave) + { for (Map.Entry> entry : arena.everyWaveMap.entrySet()) if (wave % entry.getKey() == 0) addReward(entry.getValue()); - // Same deal, this time with the one-time waves. if (arena.afterWaveMap.containsKey(wave)) addReward(arena.afterWaveMap.get(wave)); + } + + private void spawnWave(int wave) + { + Wave w = null; - // Check if this is a special wave. - if (wave % modulo == 0) + // Check the first element of the single waves. + if (!singleWaves.isEmpty() && singleWaves.first().matches(wave)) { - MAUtils.tellAll(arena, MAMessages.get(Msg.WAVE_SPECIAL, ""+wave)); - detonateCreepers(arena.detCreepers); - specialWave(); - - // Notify listeners. - for (MobArenaListener listener : plugin.getAM().listeners) - listener.onSpecialWave(wave, wave/modulo); + w = singleWaves.pollFirst(); } else { - MAUtils.tellAll(arena, MAMessages.get(Msg.WAVE_DEFAULT, ""+wave)); - detonateCreepers(arena.detCreepers); - defaultWave(); - - // Notify listeners. - for (MobArenaListener listener : plugin.getAM().listeners) - listener.onDefaultWave(wave); + SortedSet matches = getMatchingRecurrentWaves(wave); + w = matches.isEmpty() ? defaultWave : matches.last(); } + + // Notify listeners. + for (MobArenaListener listener : plugin.getAM().listeners) + listener.onWave(arena, wave, w.getName(), w.getBranch(), w.getType()); + + w.spawn(wave); + } + + private SortedSet getMatchingRecurrentWaves(int wave) + { + TreeSet result = new TreeSet(WaveUtils.getRecurrentComparator()); + + for (Wave w : recurrentWaves) + { + if (w.matches(wave)) + result.add(w); + } + + return result; + } + - wave++; - if (arena.maxIdleTime > 0 && arena.monsters.isEmpty()) arena.resetIdleTimer(); + + /*//////////////////////////////////////////////////////////////////// + // + // Getters/setters + // + ////////////////////////////////////////////////////////////////////*/ + + public int getWave() + { + return wave; + } + + public int getTaskId() + { + return taskId; + } + + public int getPreviousSize() + { + return previousSize; + } + + public int getPlayerCount() + { + return playerCount; + } + + public void setTaskId(int taskId) + { + this.taskId = taskId; + } + + public void setPreviousSize(int previousSize) + { + this.previousSize = previousSize; } /** @@ -133,158 +185,30 @@ public class MASpawnThread implements Runnable { for (Player p : arena.arenaPlayers) { - if (arena.rewardMap.get(p) == null) + if (arena.log.players.get(p) == null) continue; ItemStack reward = MAUtils.getRandomReward(rewards); - arena.rewardMap.get(p).add(reward); + arena.log.players.get(p).rewards.add(reward); if (reward == null) { MAUtils.tellPlayer(p, "ERROR! Problem with economy rewards. Notify server host!"); - System.out.println("[MobArena] ERROR! Could not add null reward. Please check the config-file!"); + MobArena.warning("Could not add null reward. Please check the config-file!"); } else if (reward.getTypeId() == MobArena.ECONOMY_MONEY_ID) { if (plugin.Methods.hasMethod()) - MAUtils.tellPlayer(p, MAMessages.get(Msg.WAVE_REWARD, plugin.Method.format(reward.getAmount()))); - else System.out.println("[MobArena] ERROR! No economy plugin detected!"); + MAUtils.tellPlayer(p, Msg.WAVE_REWARD, plugin.Method.format(reward.getAmount())); + else MobArena.warning("Tried to add money, but no economy plugin detected!"); } else { - MAUtils.tellPlayer(p, MAMessages.get(Msg.WAVE_REWARD, MAUtils.toCamelCase(reward.getType().toString()) + ":" + reward.getAmount())); + MAUtils.tellPlayer(p, Msg.WAVE_REWARD, MAUtils.toCamelCase(reward.getType().toString()) + ":" + reward.getAmount(), reward.getType()); } } } - /** - * Spawns a default wave of monsters. - */ - private void defaultWave() - { - Location loc; - List spawnpoints = getValidSpawnpoints(); - int noOfSpawnpoints = spawnpoints.size(); - int count = wave + noOfPlayers; - CreatureType mob; - - for (int i = 0; i < count; i++) - { - loc = spawnpoints.get(i % noOfSpawnpoints); - ran = random.nextInt(dWolves); - - /* Because of the nature of the if-elseif-else statement, - * we're able to evaluate the random number in this way. - * If dSpiders = 0, then dSpiders = dSkeletons, which - * means if the random number is below that value, we will - * spawn a skeleton and break out of the statement. */ - if (ran < dZombies) mob = CreatureType.ZOMBIE; - else if (ran < dSkeletons) mob = CreatureType.SKELETON; - else if (ran < dSpiders) mob = CreatureType.SPIDER; - else if (ran < dCreepers) mob = CreatureType.CREEPER; - else if (ran < dWolves) mob = CreatureType.WOLF; - else continue; - - LivingEntity e = arena.world.spawnCreature(loc,mob); - arena.monsters.add(e); - - if (mob == CreatureType.WOLF) - ((Wolf)e).setOwner(null); - - // Grab a random target. - Creature c = (Creature) e; - c.setTarget(getClosestPlayer(e)); - } - } - - /** - * Spawns a special wave of monsters. - */ - private void specialWave() - { - Location loc; - List spawnpoints = getValidSpawnpoints(); - int noOfSpawnpoints = spawnpoints.size(); - CreatureType mob; - ran = random.nextInt(dGhasts); - - int count; - boolean slime = false; - boolean wolf = false; - boolean ghast = false; - boolean creeper = false; - - if (ran < dPoweredCreepers) mob = CreatureType.CREEPER; - else if (ran < dPigZombies) mob = CreatureType.PIG_ZOMBIE; - else if (ran < dSlimes) mob = CreatureType.SLIME; - else if (ran < dMonsters) mob = CreatureType.MONSTER; - else if (ran < dAngryWolves) mob = CreatureType.WOLF; - else if (ran < dGiants) mob = CreatureType.GIANT; - else if (ran < dGhasts) mob = CreatureType.GHAST; - else return; - - switch(mob) - { - case CREEPER: - count = noOfPlayers * 3; - creeper = true; - break; - case PIG_ZOMBIE: - count = noOfPlayers * 2; - break; - case SLIME: - count = noOfPlayers * 4; - slime = true; - break; - case MONSTER: - count = noOfPlayers + 1; - break; - case WOLF: - count = noOfPlayers * 3; - wolf = true; - break; - case GIANT: - count = 1; - break; - case GHAST: - count = 2; - ghast = true; - break; - default: - count = 50; - break; - } - - // Spawn the hippie monsters. - for (int i = 0; i < count; i++) - { - loc = spawnpoints.get(i % noOfSpawnpoints); - - LivingEntity e = arena.world.spawnCreature(loc,mob); - arena.monsters.add(e); - - if (slime) ((Slime)e).setSize(2); - if (wolf) { ((Wolf)e).setAngry(true); ((Wolf)e).setOwner(null); } - if (ghast) ((Ghast)e).setHealth(Math.min(noOfPlayers*25, 200)); - if (creeper) ((Creeper)e).setPowered(true); - - // Slimes can't have targets, apparently. - if (!(e instanceof Creature)) - continue; - - // Grab a random target. - Creature c = (Creature) e; - c.setTarget(getClosestPlayer(e)); - } - - if (!arena.lightning) - return; - - // Lightning, just for effect ;) - for (Location spawn : arena.spawnpoints.values()) - arena.world.strikeLightningEffect(spawn); - } - /** * "Detonates" all the Creepers in the monsterSet. */ @@ -312,74 +236,6 @@ public class MASpawnThread implements Runnable } } - /** - * Get all the spawnpoints that have players nearby. - */ - public List getValidSpawnpoints() - { - List result = new ArrayList(); - - for (Location s : arena.spawnpoints.values()) - { - //for (Player p : arena.livePlayers) - for (Player p : arena.arenaPlayers) - { - if (!arena.world.equals(p.getWorld())) - { - System.out.println("[MobArena] MASpawnThread:291: Player '" + p.getName() + "' is not in the right world. Force leaving..."); - arena.playerLeave(p); - MAUtils.tellPlayer(p, "You warped out of the arena world."); - continue; - } - - if (s.distanceSquared(p.getLocation()) > MobArena.MIN_PLAYER_DISTANCE) - continue; - - result.add(s); - break; - } - } - - // If no players are in range, just use all the spawnpoints. - if (result.isEmpty()) - result.addAll(arena.spawnpoints.values()); - - return result; - } - - /** - * Get the player closest to the input entity. - */ - // TODO: Move this into MAUtils - public Player getClosestPlayer(Entity e) - { - // Set up the comparison variable and the result. - double dist = 0; - double current = Double.POSITIVE_INFINITY; - Player result = null; - - /* Iterate through the ArrayList, and update current and result every - * time a squared distance smaller than current is found. */ - //for (Player p : arena.livePlayers) - for (Player p : arena.arenaPlayers) - { - if (!arena.world.equals(p.getWorld())) - { - System.out.println("[MobArena] MASpawnThread:329: Player '" + p.getName() + "' is not in the right world. Force leaving..."); - arena.playerLeave(p); - MAUtils.tellPlayer(p, "You warped out of the arena world."); - continue; - } - dist = p.getLocation().distanceSquared(e.getLocation()); - if (dist < current && dist < MobArena.MIN_PLAYER_DISTANCE) - { - current = dist; - result = p; - } - } - return result; - } - /** * Update the targets of all monsters, if their targets aren't alive. */ @@ -399,7 +255,7 @@ public class MASpawnThread implements Runnable if (target instanceof Player && arena.arenaPlayers.contains(target)) continue; - c.setTarget(getClosestPlayer(e)); + c.setTarget(MAUtils.getClosestPlayer(e, arena)); } } } \ No newline at end of file diff --git a/src/com/garbagemule/MobArena/MAUtils.java b/src/com/garbagemule/MobArena/MAUtils.java index 73b9000..fcf5180 100644 --- a/src/com/garbagemule/MobArena/MAUtils.java +++ b/src/com/garbagemule/MobArena/MAUtils.java @@ -7,6 +7,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -15,6 +16,8 @@ import java.util.Map; import java.util.HashMap; import java.util.Random; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import net.minecraft.server.WorldServer; @@ -33,8 +36,19 @@ import org.bukkit.entity.Wolf; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.util.config.Configuration; +import org.getspout.spoutapi.SpoutManager; +import org.getspout.spoutapi.player.SpoutPlayer; import com.garbagemule.MobArena.MAMessages.Msg; +import com.garbagemule.MobArena.util.EntityPosition; +import com.garbagemule.MobArena.util.InventoryItem; +import com.garbagemule.MobArena.util.jnbt.ByteTag; +import com.garbagemule.MobArena.util.jnbt.CompoundTag; +import com.garbagemule.MobArena.util.jnbt.ListTag; +import com.garbagemule.MobArena.util.jnbt.NBTInputStream; +import com.garbagemule.MobArena.util.jnbt.NBTOutputStream; +import com.garbagemule.MobArena.util.jnbt.ShortTag; +import com.garbagemule.MobArena.util.jnbt.Tag; public class MAUtils { @@ -131,6 +145,20 @@ public class MAUtils // ///////////////////////////////////////////////////////////////////// */ + public static Map getArenaContainers(Configuration config, World world, String arena) + { + Map containers = new HashMap(); + String arenaPath = "arenas." + arena + ".coords.containers"; + + if (config.getKeys(arenaPath) == null) + return containers; + + for (String point : config.getKeys(arenaPath)) + containers.put(point, makeLocation(world, config.getString(arenaPath + "." + point))); + + return containers; + } + /** * Grab all the spawnpoints for a specific arena. */ @@ -250,55 +278,6 @@ public class MAUtils return result; } - /** - * Grabs the distribution coefficients from the config-file. If - * no coefficients are found, defaults (10) are added. - */ - public static Map getArenaDistributions(Configuration config, String arena, String wave) - { - String arenaPath = "arenas." + arena + ".waves." + wave; - Map result = new HashMap(); - List dists = (config.getKeys(arenaPath) != null) ? config.getKeys(arenaPath) : new LinkedList(); - - String[] monsters = (wave.equals("default")) ? new String[]{"zombies", "skeletons", "spiders", "creepers", "wolves"} - : new String[]{"powered-creepers", "zombie-pigmen", "slimes", "humans", "angry-wolves", "giants", "ghasts"}; - boolean update = false; - for (String monster : monsters) - { - if (dists.contains(monster)) - continue; - - //if (config.getInt(arenaPath + "." + m, -1) == -1) - config.setProperty(arenaPath + "." + monster, (monster.equals("giants") || monster.equals("ghasts")) ? 0 : 10); - update = true; - } - - if (update) - { - config.save(); - dists = config.getKeys(arenaPath); - } - - for (String monster : dists) - { - int value = config.getInt(arenaPath + "." + monster, -1); - - // If no distribution value was found, set one. - if (value == -1) - { - value = 10; - if (monster.equals("giant") || monster.equals("ghast")) - value = 0; - - config.setProperty(arenaPath + "." + monster, value); - } - config.save(); - - result.put(monster, value); - } - return result; - } - public static List getAllowedCommands(Configuration config) { String commands = config.getString("global-settings.allowed-commands"); @@ -319,8 +298,7 @@ public class MAUtils INVENTORY AND REWARD METHODS // ///////////////////////////////////////////////////////////////////// */ - - /* Clears the players inventory and armor slots. */ + public static PlayerInventory clearInventory(Player p) { PlayerInventory inv = p.getInventory(); @@ -334,26 +312,28 @@ public class MAUtils public static boolean storeInventory(Player p) { - // Grab the contents. - ItemStack[] armor = p.getInventory().getArmorContents(); - ItemStack[] items = p.getInventory().getContents(); - + // Set up the files and paths String invPath = "plugins" + sep + "MobArena" + sep + "inventories"; new File(invPath).mkdir(); File backupFile = new File(invPath + sep + p.getName() + ".inv"); + // If a backup file already exists, restore the inventory first + if (backupFile.exists() && !restoreInventory(p)) + return false; + + // Grab the inventory contents. + ItemStack[] items = p.getInventory().getContents(); + ItemStack[] armor = p.getInventory().getArmorContents(); + try { - if (backupFile.exists() && !restoreInventory(p)) - return false; - backupFile.createNewFile(); - MAInventoryItem[] inv = new MAInventoryItem[armor.length + items.length]; - for (int i = 0; i < armor.length; i++) - inv[i] = stackToItem(armor[i]); + InventoryItem[] inv = new InventoryItem[items.length + armor.length]; for (int i = 0; i < items.length; i++) - inv[armor.length + i] = stackToItem(items[i]); + inv[i] = InventoryItem.parseItemStack(items[i]); + for (int i = 0; i < armor.length; i++) + inv[i + items.length] = InventoryItem.parseItemStack(armor[i]); FileOutputStream fos = new FileOutputStream(backupFile); ObjectOutputStream oos = new ObjectOutputStream(fos); @@ -363,7 +343,7 @@ public class MAUtils catch (Exception e) { e.printStackTrace(); - System.out.println("[MobArena] ERROR! Could not create backup file for " + p.getName() + "."); + MobArena.warning("Could not create backup file for " + p.getName() + "."); return false; } @@ -372,6 +352,33 @@ public class MAUtils } public static boolean restoreInventory(Player p) + { + // Grab the items from the MobArena .inv file + ItemStack[] stacks = getInventoryFile(p); + + // If the player isn't online, hack the playerName.dat file + if (!p.isOnline()) + return writeInventoryData(p, stacks); + + // Otherwise, restore the inventory directly + ItemStack[] items = new ItemStack[stacks.length-4]; + ItemStack[] armor = new ItemStack[4]; + + for (int i = 0; i < stacks.length - 4; i++) + items[i] = stacks[i]; + for (int i = 0; i < 4; i++) + armor[i] = stacks[stacks.length - 4 + i]; + + // Restore the inventory. + PlayerInventory inv = p.getInventory(); + inv.setArmorContents(armor); + for (int i = 0; i < items.length; i++) + inv.setItem(i, items[i]); + + return true; + } + + public static ItemStack[] getInventoryFile(Player p) { String invPath = "plugins" + sep + "MobArena" + sep + "inventories"; File backupFile = new File(invPath + sep + p.getName() + ".inv"); @@ -380,55 +387,111 @@ public class MAUtils { // If the backup-file couldn't be found, return. if (!backupFile.exists()) - return false; + return null; // Grab the MAInventoryItem array from the backup-file. FileInputStream fis = new FileInputStream(backupFile); ObjectInputStream ois = new ObjectInputStream(fis); - MAInventoryItem[] fromFile = (MAInventoryItem[]) ois.readObject(); + InventoryItem[] fromFile = (InventoryItem[]) ois.readObject(); ois.close(); - - // Split that shit. - ItemStack[] armor = new ItemStack[4]; - ItemStack[] items = new ItemStack[fromFile.length-4]; - - for (int i = 0; i < 4; i++) - armor[i] = itemToStack(fromFile[i]); - for (int i = 4; i < fromFile.length; i++) - items[i - 4] = itemToStack(fromFile[i]); - - // Restore the inventory. - PlayerInventory inv = p.getInventory(); - inv.setArmorContents(armor); - for (ItemStack stack : items) - if (stack != null) - inv.addItem(stack); - - // Remove the backup-file. + + // Delete the file backupFile.delete(); + + return InventoryItem.toItemStacks(fromFile); } catch (Exception e) { e.printStackTrace(); - System.out.println("[MobArena] ERROR! Could not restore inventory for " + p.getName()); + MobArena.warning("Could not restore inventory for " + p.getName()); + return null; + } + } + + public static ItemStack[] readInventoryData(Player p) + { + // Grab the data dir /players/ + File playerDir = new File(Bukkit.getServer().getWorlds().get(0).getName(), "players"); + + try + { + NBTInputStream in = new NBTInputStream(new FileInputStream(new File(playerDir, p.getName() + ".dat"))); + CompoundTag tag = (CompoundTag) in.readTag(); + in.close(); + + ListTag inventory = (ListTag) tag.getValue().get("Inventory"); + + ItemStack[] stacks = new ItemStack[40]; + for (int i = 0; i < inventory.getValue().size(); i++) + { + CompoundTag item = (CompoundTag) inventory.getValue().get(i); + byte count = ((ByteTag) item.getValue().get("Count")).getValue(); + byte slot = ((ByteTag) item.getValue().get("Slot")).getValue(); + short damage = ((ShortTag) item.getValue().get("Damage")).getValue(); + short id = ((ShortTag) item.getValue().get("id")).getValue(); + stacks[slot < 36 ? slot : 36 + 103-slot] = new ItemStack(id, count, damage); + } + return stacks; + } + catch (Exception e) + { + e.printStackTrace(); + MobArena.warning("Could not restore inventory for " + p.getName()); + return null; + } + } + + public static boolean writeInventoryData(Player p, ItemStack[] stacks) + { + // Abort if stacks is null + if (stacks == null) return false; + + // Grab the data dir /players/ + File playerDir = new File(Bukkit.getServer().getWorlds().get(0).getName(), "players"); + + try + { + NBTInputStream in = new NBTInputStream(new FileInputStream(new File(playerDir, p.getName() + ".dat"))); + CompoundTag tag = (CompoundTag) in.readTag(); + in.close(); + + ArrayList tagList = new ArrayList(); + + for (int i = 0; i < stacks.length; i++) + { + if (stacks[i] == null) continue; + + ByteTag count = new ByteTag("Count", (byte) stacks[i].getAmount()); + ByteTag slot = new ByteTag("Slot", (byte) (i < 36 ? i : 104-(stacks.length-i))); + ShortTag damage = new ShortTag("Damage", stacks[i].getDurability()); + ShortTag id = new ShortTag("id", (short) stacks[i].getTypeId()); + + HashMap tagMap = new HashMap(); + tagMap.put("Count", count); + tagMap.put("Slot", slot); + tagMap.put("Damage", damage); + tagMap.put("id", id); + + tagList.add(new CompoundTag("", tagMap)); + } + + ListTag inventory = new ListTag("Inventory", CompoundTag.class, tagList); + + HashMap tagCompound = new HashMap(tag.getValue()); + tagCompound.put("Inventory", inventory); + tag = new CompoundTag("Player", tagCompound); + + NBTOutputStream out = new NBTOutputStream(new FileOutputStream(new File(playerDir, p.getName() + ".dat"))); + out.writeTag(tag); + out.close(); + return true; + } + catch (Exception e) + { + e.printStackTrace(); + MobArena.warning("Could not restore inventory for " + p.getName()); return false; } - - return true; - } - - private static MAInventoryItem stackToItem(ItemStack stack) - { - if (stack == null) - return new MAInventoryItem(-1, -1, (short)0); - return new MAInventoryItem(stack.getTypeId(), stack.getAmount(), stack.getDurability()); - } - - private static ItemStack itemToStack(MAInventoryItem item) - { - if (item.getTypeId() == -1) - return null; - return new ItemStack(item.getTypeId(), item.getAmount(), item.getDurability()); } /* Checks if all inventory and armor slots are empty. */ @@ -456,6 +519,25 @@ public class MAUtils if (stacks == null) return; + if (!p.isOnline()) + { + ItemStack[] items = readInventoryData(p); + int currentSlot = 0; + for (ItemStack stack : stacks) + { + while (currentSlot < items.length && items[currentSlot] != null) + currentSlot++; + + if (currentSlot >= items.length) + break; + + items[currentSlot] = stack; + } + + writeInventoryData(p, items); + return; + } + PlayerInventory inv = p.getInventory(); for (ItemStack stack : stacks) { @@ -474,7 +556,8 @@ public class MAUtils // If these are rewards, don't tamper with them. if (rewards) { - inv.addItem(stack); + //inv.addItem(stack); + giveItem(inv, stack); continue; } @@ -489,19 +572,36 @@ public class MAUtils if (WEAPONS_TYPE.contains(stack.getType())) stack.setDurability((short) -32768); - inv.addItem(stack); + giveItem(inv, stack); + //inv.addItem(stack); } } - public static void giveRewards(Player p, List stacks, MobArena plugin) - { - giveItems(p, stacks, false, true, plugin); - } public static void giveItems(Player p, List stacks, boolean autoEquip, MobArena plugin) { giveItems(p, stacks, autoEquip, false, plugin); } + public static void giveItem(PlayerInventory inv, ItemStack stack) + { + int id = stack.getTypeId(); + int amount = stack.getAmount(); + + int times = amount / 64; + int remainder = amount % 64; + + for (int i = 0; i < times; i++) + inv.addItem(new ItemStack(id, 64)); + + if (remainder > 0) + inv.addItem(new ItemStack(id, remainder)); + } + + public static void giveRewards(Player p, List stacks, MobArena plugin) + { + giveItems(p, stacks, false, true, plugin); + } + public static int getPetAmount(Player p) { int result = 0; @@ -559,7 +659,7 @@ public class MAUtils } catch (Exception e) { - System.out.println("[MobArena] ERROR! Could not create item \"" + name + "\". Check config.yml"); + MobArena.warning("Could not create item \"" + name + "\". Check config.yml"); return null; } } @@ -592,10 +692,7 @@ public class MAUtils public static void sitPets(Player p) { if (p == null) - { - System.out.println("Player is null!"); return; - } List entities = p.getNearbyEntities(80, 40, 80); for (Entity e : entities) @@ -609,18 +706,6 @@ public class MAUtils } } - /** - * Removes all the pets belonging to this player. - *//* - public static void clearPets(Arena arena, Player p) - { - for (Wolf w : arena.pets) - { - if (w.getOwner().equals(p)) - w.remove(); - } - }*/ - /* ///////////////////////////////////////////////////////////////////// // @@ -629,6 +714,20 @@ public class MAUtils // ///////////////////////////////////////////////////////////////////// */ + /** + * Check if a Location is inside two points (x1,y1,z1) (x2,y2,z2) + */ + public static boolean inRegion(Location loc, double x1, double y1, double z1, double x2, double y2, double z2) + { + double x = loc.getBlockX(); + double y = loc.getBlockY(); + double z = loc.getBlockZ(); + + return x >= x1 && x <= x2 && + y >= y1 && y <= y2 && + z >= z1 && z <= z2; + } + /** * Create a frame spanned by the two input coordinates. * @return An int arry holding x,y,z and the original type IDs of each block. @@ -780,6 +879,10 @@ public class MAUtils arena.p1.setY(arena.p2.getY()); arena.p2.setY(tmp); } + + if (!arena.world.getName().equals(world.getName())) + arena.world = world; + arena.serializeConfig(); arena.load(config); } @@ -885,23 +988,84 @@ public class MAUtils // ///////////////////////////////////////////////////////////////////// */ - /** - * Sends a message to a player. - */ + public static boolean tellSpoutPlayer(Player p, Msg msg, String s, Material logo) + { + // Grab the SpoutPlayer. + SpoutPlayer sp = MobArena.hasSpout ? SpoutManager.getPlayer(p) : null; + + if (msg.hasSpoutMsg() && sp != null && sp.isSpoutCraftEnabled()) + { + // Grab the message text. + String text = msg.getSpout(s); + + // If more than 26 characters, truncate. + if (text.length() > 26) + text = text.substring(0, 26); + + // If the logo is null, use an iron sword. + if (logo == null) + logo = msg.getLogo(); + + // Send the notification. + sp.sendNotification("MobArena", text, logo, (short) 0, 2000); + return true; + } + else return tellPlayer(p, msg.get(s)); + } + + public static boolean tellSpoutPlayer(Player p, Msg msg, Material logo) + { + return tellSpoutPlayer(p, msg, null, logo); + } + + public static boolean tellSpoutPlayer(Player p, Msg msg, String s) + { + return tellSpoutPlayer(p, msg, s, null); + } + + public static boolean tellSpoutPlayer(Player p, Msg msg) + { + return tellSpoutPlayer(p, msg, null, null); + } + public static boolean tellPlayer(CommandSender p, String msg) { - if (p == null) + // If the input sender is null or the string is empty, return. + if (p == null || msg.equals(" ")) return false; + // Otherwise, send the message with the [MobArena] tag. p.sendMessage(ChatColor.GREEN + "[MobArena] " + ChatColor.WHITE + msg); return true; } + public static boolean tellPlayer(CommandSender p, Msg msg, String s, boolean spout, Material logo) + { + if (spout && p instanceof Player) + return tellSpoutPlayer((Player) p, msg, s, logo); + + return tellPlayer(p, msg.get(s)); + } + + public static boolean tellPlayer(CommandSender p, Msg msg, String s, Material logo) + { + return tellPlayer(p, msg, s, MobArena.hasSpout, logo); + } + + public static boolean tellPlayer(CommandSender p, Msg msg, String s) + { + return tellPlayer(p, msg, s, MobArena.hasSpout, null); + } + + public static boolean tellPlayer(CommandSender p, Msg msg) + { + return tellPlayer(p, msg, null, MobArena.hasSpout, null); + } + /** * Sends a message to all players in and around the arena. */ - public static void tellAll(Arena arena, String msg) { tellAll(arena, msg, false); } - public static void tellAll(Arena arena, String msg, boolean notifyPlayers) + public static void tellAll(Arena arena, Msg msg, String s, boolean notifyPlayers) { Set tmp = new HashSet(); tmp.addAll(arena.arenaPlayers); @@ -911,7 +1075,22 @@ public class MAUtils tmp.addAll(arena.specPlayers); if (notifyPlayers) tmp.addAll(arena.notifyPlayers); for (Player p : tmp) - tellPlayer(p, msg); + tellPlayer(p, msg, s); + } + + public static void tellAll(Arena arena, Msg msg, String s) + { + tellAll(arena, msg, s, false); + } + + public static void tellAll(Arena arena, Msg msg, boolean notifyPlayers) + { + tellAll(arena, msg, null, notifyPlayers); + } + + public static void tellAll(Arena arena, Msg msg) + { + tellAll(arena, msg, null, false); } public static Player getClosestPlayer(Entity e, Arena arena) @@ -927,8 +1106,8 @@ public class MAUtils { if (!arena.world.equals(p.getWorld())) { - System.out.println("[MobArena] MAUtils:908: Player '" + p.getName() + "' is not in the right world. Force leaving..."); - arena.playerLeave(p); + MobArena.info("Player '" + p.getName() + "' is not in the right world. Kicking..."); + p.kickPlayer("[MobArena] Cheater! (Warped out of the arena world.)"); tellPlayer(p, "You warped out of the arena world."); continue; } @@ -994,7 +1173,7 @@ public class MAUtils if (list == null || list.isEmpty()) { if (none) - return MAMessages.get(Msg.MISC_NONE); + return Msg.MISC_NONE.get(); else return ""; } @@ -1045,6 +1224,7 @@ public class MAUtils return buffy.toString().substring(0, buffy.length() - trimLength); } public static String listToString(List list, MobArena plugin) { return listToString(list, true, plugin); } + public static String listToString(List list) { return listToString(list, true, (MobArena) Bukkit.getServer().getPluginManager().getPlugin("MobArena")); } /** * Returns a String-list version of a comma-separated list. @@ -1080,7 +1260,7 @@ public class MAUtils * and spawnpoints are all set up. */ public static boolean verifyData(Arena arena) - { + { return ((arena.arenaLoc != null) && (arena.lobbyLoc != null) && (arena.spectatorLoc != null) && @@ -1094,6 +1274,24 @@ public class MAUtils return ((arena.l1 != null) && (arena.l2 != null)); } + + public static void checkData(Arena arena, CommandSender p) + { + if (arena.arenaLoc == null) + tellPlayer(p, "Missing warp: arena"); + if (arena.lobbyLoc == null) + tellPlayer(p, "Missing warp: lobby"); + if (arena.spectatorLoc == null) + tellPlayer(p, "Missing warp: spectator"); + if (arena.p1 == null) + tellPlayer(p, "Missing region point: p1"); + if (arena.p2 == null) + tellPlayer(p, "Missing region point: p2"); + if (arena.spawnpoints.size() <= 0) + tellPlayer(p, "Missing spawnpoints"); + if (arena.setup) + tellPlayer(p, "Arena is ready to be used!"); + } /** * Checks if there is a new update of MobArena and notifies the @@ -1109,6 +1307,7 @@ public class MAUtils // Open the connection and don't redirect. HttpURLConnection con = (HttpURLConnection) baseURI.toURL().openConnection(); + con.setConnectTimeout(5000); con.setInstanceFollowRedirects(false); String header = con.getHeaderField("Location"); @@ -1123,18 +1322,27 @@ public class MAUtils // Otherwise, grab the location header to get the real URI. String url = new URI(con.getHeaderField("Location")).toString(); - // If the current version is the same as the thread version. - if (url.contains(plugin.getDescription().getVersion().replace(".", "-"))) - { - if (!response) - return; - - tellPlayer(p, "Your version of MobArena is up to date!"); + // Set up the regex and matcher + Pattern regex = Pattern.compile("v([0-9]+-)*[0-9]+"); + Matcher matcher = regex.matcher(url); + if (!matcher.find()) return; + + // Split the version strings + String[] forumVersion = matcher.group().substring(1).split("-"); + String[] thisVersion = plugin.getDescription().getVersion().split("\\."); + + // If the current version is older than the forum version, notify. + for (int i = 0; i < Math.min(forumVersion.length, thisVersion.length); i++) + { + if (Integer.parseInt(forumVersion[i]) > Integer.parseInt(thisVersion[i])) + { + tellPlayer(p, "There is a new version of MobArena available!");; + return; + } } - // Otherwise, notify the player that there is a new version. - tellPlayer(p, "There is a new version of MobArena available!");; + if (response) tellPlayer(p, "Your version of MobArena is up to date!"); } catch (Exception e) { @@ -1162,27 +1370,16 @@ public class MAUtils long hrs = ((seconds - secs) - (mins * 60)) / 60 / 60; return hrs + ":" + ((mins < 10) ? "0" + mins : mins) + ":" + ((secs < 10) ? "0" + secs : secs); } - - public static String padRight(String s, int length) { return padRight(s, length, ' '); } - public static String padRight(String s, int length, char pad) - { - StringBuffer buffy = new StringBuffer(); - buffy.append(s); - for (int i = s.length(); i < length; i++) - buffy.append(pad); - return buffy.toString(); - } - - public static String padLeft(String s, int length) { return padLeft(s, length, ' '); } - public static String padLeft(String s, int length, char pad) - { - StringBuffer buffy = new StringBuffer(); - for (int i = 0; i < length - s.length(); i++) - buffy.append(pad); - buffy.append(s); - return buffy.toString(); - } + public static long parseDuration(String duration) + { + String[] elements = duration.split(":"); + long hrs = Long.parseLong(elements[0]) * 60 * 60; + long mins = Long.parseLong(elements[1]) * 60; + long secs = Long.parseLong(elements[2]); + + return (hrs + mins + secs) * 1000; + } /** * Stand back, I'm going to try science! @@ -1243,7 +1440,7 @@ public class MAUtils catch (Exception e) { e.printStackTrace(); - System.out.println("[MobArena] ERROR! Couldn't create backup file. Aborting auto-generate..."); + MobArena.warning("Couldn't create backup file. Aborting auto-generate..."); return false; } @@ -1376,7 +1573,7 @@ public class MAUtils } catch (Exception e) { - if (error) System.out.println("[MobArena] ERROR! Couldn't find backup file for arena '" + name + "'"); + if (error) MobArena.warning("Couldn't find backup file for arena '" + name + "'"); return false; } diff --git a/src/com/garbagemule/MobArena/MobArena.java b/src/com/garbagemule/MobArena/MobArena.java index be9a23d..762ca41 100644 --- a/src/com/garbagemule/MobArena/MobArena.java +++ b/src/com/garbagemule/MobArena/MobArena.java @@ -1,7 +1,9 @@ package com.garbagemule.MobArena; import java.io.File; +import java.util.Random; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.Event.Priority; @@ -16,14 +18,17 @@ import org.bukkit.util.config.Configuration; import com.nijiko.permissions.PermissionHandler; import com.nijikokun.bukkit.Permissions.Permissions; +import com.garbagemule.MobArena.util.FileUtils; import com.garbagemule.register.payment.Method; import com.garbagemule.register.payment.Methods; +//import com.garbagemule.ArenaPlugin.ArenaPlugin; +//import com.garbagemule.ArenaPlugin.Master; /** * MobArena * @author garbagemule */ -public class MobArena extends JavaPlugin +public class MobArena extends JavaPlugin// implements ArenaPlugin { private Configuration config; private ArenaMaster am; @@ -36,36 +41,76 @@ public class MobArena extends JavaPlugin protected Method Method; // Global variables - protected static final double MIN_PLAYER_DISTANCE = 256.0; - protected static final int ECONOMY_MONEY_ID = -29; + public static PluginDescriptionFile desc; + public static File dir, arenaDir; + public static final double MIN_PLAYER_DISTANCE = 15.0; + public static final double MIN_PLAYER_DISTANCE_SQUARED = MIN_PLAYER_DISTANCE * MIN_PLAYER_DISTANCE; + public static final int ECONOMY_MONEY_ID = -29; + public static boolean hasSpout; + public static Random random = new Random(); public void onEnable() { - PluginDescriptionFile pdfFile = this.getDescription(); + // Description file and data folders + desc = getDescription(); + dir = getDataFolder(); + arenaDir = new File(dir, "arenas"); + if (!dir.exists()) dir.mkdir(); + if (!arenaDir.exists()) arenaDir.mkdir(); - // Config, messages and ArenaMaster initialization + // Create default files and initialize config-file + FileUtils.extractDefaults("config.yml"); loadConfig(); - MAMessages.init(this); + + // Download external libraries if needed. + FileUtils.fetchLibs(config); + + // Set up permissions and economy + setupPermissions(); + setupRegister(); + setupSpout(); + + // Set up the ArenaMaster and the announcements am = new ArenaMaster(this); am.initialize(); + MAMessages.init(this); - // Permissions - setupPermissions(); + // Register event listeners + registerListeners(); - // Economy - Methods = new Methods(); - if (!Methods.hasMethod() && Methods.setMethod(this)) - { - Method = Methods.getMethod(); - System.out.println("[MobArena] Payment method found (" + Method.getName() + " version: " + Method.getVersion() + ")"); - } + // Announce enable! + info("v" + desc.getVersion() + " enabled."); + } + + public void onDisable() + { + // Disable Spout features. + hasSpout = false; - // Bind the /ma, /marena, and /mobarena commands to MACommands. + // Force all arenas to end. + if (am == null) return; + for (Arena arena : am.arenas) + arena.forceEnd(); + am.arenaMap.clear(); + + info("disabled."); + } + + private void loadConfig() + { + File file = new File(dir, "config.yml"); + config = new Configuration(file); + config.load(); + config.setHeader(getHeader()); + } + + private void registerListeners() + { + // Bind the /ma, /mobarena commands to MACommands. MACommands commandExecutor = new MACommands(this, am); - getCommand("ma").setExecutor(commandExecutor); - getCommand("marena").setExecutor(commandExecutor); - getCommand("mobarena").setExecutor(commandExecutor); - + getCommand("ma").setExecutor(commandExecutor); + getCommand("mobarena").setExecutor(commandExecutor); + // Create event listeners. PluginManager pm = getServer().getPluginManager(); PlayerListener playerListener = new MAPlayerListener(this, am); @@ -73,6 +118,11 @@ public class MobArena extends JavaPlugin BlockListener blockListener = new MABlockListener(am); // Register events. + pm.registerEvent(Event.Type.BLOCK_BREAK, blockListener, Priority.Highest, this); + pm.registerEvent(Event.Type.BLOCK_BURN, blockListener, Priority.Highest, this); + pm.registerEvent(Event.Type.BLOCK_PLACE, blockListener, Priority.Highest, this); + pm.registerEvent(Event.Type.BLOCK_PHYSICS, blockListener, Priority.Normal, this); + pm.registerEvent(Event.Type.BLOCK_IGNITE, blockListener, Priority.Highest, this); pm.registerEvent(Event.Type.PLAYER_INTERACT, playerListener, Priority.Normal, this); pm.registerEvent(Event.Type.PLAYER_DROP_ITEM, playerListener, Priority.Normal, this); pm.registerEvent(Event.Type.PLAYER_BUCKET_EMPTY, playerListener, Priority.Normal, this); @@ -80,8 +130,7 @@ public class MobArena extends JavaPlugin pm.registerEvent(Event.Type.PLAYER_QUIT, playerListener, Priority.Normal, this); pm.registerEvent(Event.Type.PLAYER_KICK, playerListener, Priority.Normal, this); pm.registerEvent(Event.Type.PLAYER_JOIN, playerListener, Priority.Normal, this); - pm.registerEvent(Event.Type.BLOCK_BREAK, blockListener, Priority.Highest, this); - pm.registerEvent(Event.Type.BLOCK_PLACE, blockListener, Priority.Highest, this); + pm.registerEvent(Event.Type.PLAYER_ANIMATION, playerListener, Priority.Normal, this); pm.registerEvent(Event.Type.ENTITY_DAMAGE, entityListener, Priority.High, this); // mcMMO is "Highest" pm.registerEvent(Event.Type.ENTITY_DEATH, entityListener, Priority.Lowest, this); // Lowest because of Tombstone pm.registerEvent(Event.Type.ENTITY_REGAIN_HEALTH, entityListener, Priority.Normal, this); @@ -89,93 +138,28 @@ public class MobArena extends JavaPlugin pm.registerEvent(Event.Type.ENTITY_COMBUST, entityListener, Priority.Normal, this); pm.registerEvent(Event.Type.ENTITY_TARGET, entityListener, Priority.Normal, this); pm.registerEvent(Event.Type.CREATURE_SPAWN, entityListener, Priority.Highest, this); - pm.registerEvent(Event.Type.PLAYER_COMMAND_PREPROCESS, playerListener, Priority.Monitor, this); - - System.out.println("[MobArena] v" + pdfFile.getVersion() + " enabled." ); - } - - public void onDisable() - { - // Force all arenas to end. - for (Arena arena : am.arenas) - arena.forceEnd(); - am.arenaMap.clear(); - - // Permissions & Economy - permissionHandler = null; - if (Methods != null && Methods.hasMethod()) - { - Methods = null; - System.out.println("[MobArena] Payment method was disabled. No longer accepting payments."); - } - - System.out.println("[MobArena] disabled."); - } - - /** - * Load the config-file and initialize the Configuration object. - */ - private void loadConfig() - { - File file = new File(this.getDataFolder(), "config.yml"); - if (!file.exists()) - { - try - { - this.getDataFolder().mkdir(); - file.createNewFile(); - } - catch (Exception e) - { - e.printStackTrace(); - return; - } - } - // TODO: Remove in v1.0 - else - { - Configuration tmp = new Configuration(file); - tmp.load(); - if (tmp.getKeys("global-settings") == null) - { - file.renameTo(new File(this.getDataFolder(), "config_OLD.yml")); - file = new File(this.getDataFolder(), "config.yml"); - try - { - this.getDataFolder().mkdir(); - file.createNewFile(); - } - catch (Exception e) - { - e.printStackTrace(); - return; - } - - config = new Configuration(file); - config.load(); - fixConfig(); - config.setHeader("# MobArena Configuration-file\r\n# Please go to https://github.com/garbagemule/MobArena/wiki/Installing-MobArena for more details."); - config.save(); - } - } - - config = new Configuration(file); - config.load(); - config.setHeader("# MobArena Configuration-file\r\n# Please go to https://github.com/garbagemule/MobArena/wiki/Installing-MobArena for more details."); - config.save(); + pm.registerEvent(Event.Type.PLAYER_COMMAND_PREPROCESS, playerListener, Priority.Monitor, this); // I know Monitor is bad, but other plugins suck! :( } // Permissions stuff public boolean has(Player p, String s) { - //return (permissionHandler != null && permissionHandler.has(p, s)); - return (permissionHandler == null || permissionHandler.has(p, s)); + // First check for NijikoPerms + if (permissionHandler != null) + return permissionHandler.has(p, s); + + // If the permission is set, check if player has permission + if (p.isPermissionSet(s)) + return p.hasPermission(s); + + // Otherwise, only allow commands that aren't admin/setup commands. + return !s.matches("^.*\\.setup\\..*$") && !s.matches("^.*\\.admin\\..*$"); } - public boolean hasDefTrue(Player p, String s) - { - return (permissionHandler == null || permissionHandler.has(p, s)); - } + // Console printing + public static void info(String msg) { Bukkit.getServer().getLogger().info("[MobArena] " + msg); } + public static void warning(String msg) { Bukkit.getServer().getLogger().warning("[MobArena] " + msg); } + public static void error(String msg) { Bukkit.getServer().getLogger().severe("[MobArena] " + msg); } private void setupPermissions() { @@ -188,89 +172,34 @@ public class MobArena extends JavaPlugin permissionHandler = ((Permissions) permissionsPlugin).getHandler(); } + private void setupRegister() + { + Methods = new Methods(); + if (!Methods.hasMethod() && Methods.setMethod(this)) + { + Method = Methods.getMethod(); + info("Payment method found (" + Method.getName() + " version: " + Method.getVersion() + ")"); + } + } + + private void setupSpout() + { + if (hasSpout) + return; + + Plugin spoutPlugin = this.getServer().getPluginManager().getPlugin("Spout"); + hasSpout = spoutPlugin != null; + } + public Configuration getConfig() { return config; } public ArenaMaster getAM() { return am; } // More convenient. public ArenaMaster getArenaMaster() { return am; } - // TODO: Remove in v1.0 - private void fixConfig() + private String getHeader() { - // If global-settings is sorted, don't do anything. - if (config.getKeys("global-settings") != null) - return; - - File oldFile = new File(this.getDataFolder(), "config_OLD.yml"); - if (!oldFile.exists()) - return; - - System.out.println("[MobArena] Config-file appears to be old. Trying to fix it..."); - - Configuration oldConfig = new Configuration(oldFile); - oldConfig.load(); - - config.setProperty("global-settings.enabled", true); - config.save(); - config.load(); - config.setProperty("global-settings.update-notification", true); - config.save(); - config.load(); - - // Copy classes - for (String s : oldConfig.getKeys("classes")) - { - config.setProperty("classes." + s + ".items", oldConfig.getString("classes." + s + ".items")); - config.setProperty("classes." + s + ".armor", oldConfig.getString("classes." + s + ".armor")); - } - config.save(); - - // Make the default arena node. - config.setProperty("arenas.default.settings.enabled", true); - config.save(); - config.load(); - config.setProperty("arenas.default.settings.world", oldConfig.getString("settings.world")); - config.save(); - config.load(); - - // Copy the coords. - for (String s : oldConfig.getKeys("coords")) - { - if (s.equals("spawnpoints")) - continue; - - StringBuffer buffy = new StringBuffer(); - buffy.append(oldConfig.getString("coords." + s + ".x")); - buffy.append(","); - buffy.append(oldConfig.getString("coords." + s + ".y")); - buffy.append(","); - buffy.append(oldConfig.getString("coords." + s + ".z")); - buffy.append(","); - buffy.append(oldConfig.getString("coords." + s + ".yaw")); - buffy.append(","); - buffy.append(oldConfig.getString("coords." + s + ".pitch")); - - config.setProperty("arenas.default.coords." + s, buffy.toString()); - } - config.save(); - config.load(); - - for (String s : oldConfig.getKeys("coords.spawnpoints")) - { - StringBuffer buffy = new StringBuffer(); - buffy.append(oldConfig.getString("coords.spawnpoints." + s + ".x")); - buffy.append(","); - buffy.append(oldConfig.getString("coords.spawnpoints." + s + ".y")); - buffy.append(","); - buffy.append(oldConfig.getString("coords.spawnpoints." + s + ".z")); - buffy.append(","); - buffy.append(oldConfig.getString("coords.spawnpoints." + s + ".yaw")); - buffy.append(","); - buffy.append(oldConfig.getString("coords.spawnpoints." + s + ".pitch")); - - config.setProperty("arenas.default.coords.spawnpoints." + s, buffy.toString()); - } - config.save(); - config.load(); - - System.out.println("[MobArena] Updated the config-file!"); + String sep = System.getProperty("line.separator"); + return "# MobArena v" + desc.getVersion() + " - Config-file" + sep + + "# Read the Wiki for details on how to set up this file: http://goo.gl/F5TTc" + sep + + "# Note: You -must- use spaces instead of tabs!"; } } \ No newline at end of file diff --git a/src/com/garbagemule/MobArena/MobArenaHandler.java b/src/com/garbagemule/MobArena/MobArenaHandler.java index e399183..1b429f8 100644 --- a/src/com/garbagemule/MobArena/MobArenaHandler.java +++ b/src/com/garbagemule/MobArena/MobArenaHandler.java @@ -3,13 +3,12 @@ package com.garbagemule.MobArena; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; public class MobArenaHandler { - MobArena plugin; - boolean ma = false; + private MobArena plugin; /** * Primary constructor. @@ -17,26 +16,24 @@ public class MobArenaHandler */ public MobArenaHandler() { - Plugin maPlugin = (MobArena) Bukkit.getServer().getPluginManager().getPlugin("MobArena"); - - if (maPlugin == null) - return; - - ma = true; - plugin = (MobArena) maPlugin; + plugin = (MobArena) Bukkit.getServer().getPluginManager().getPlugin("MobArena"); } + + + /*////////////////////////////////////////////////////////////////// + + REGION/LOCATION METHODS + + //////////////////////////////////////////////////////////////////*/ + /** - * Check if a Location is inside of an arena region. + * Check if a Location is inside of any arena region. * @param loc A location. * @return true, if the Location is inside of any arena region. */ public boolean inRegion(Location loc) { - // If the plugin doesn't exist, always return false. - if (!ma || plugin.getAM() == null) return false; - - // Return true if location is within just one arena's region. for (Arena arena : plugin.getAM().arenas) if (arena.inRegion(loc)) return true; @@ -45,26 +42,50 @@ public class MobArenaHandler } /** - * Check if a Location is inside of a specific arena region. + * Check if a Location is inside of a specific arena region (by arena object). * @param arena An Arena object * @param loc A location * @return true, if the Location is inside of the arena region. */ - public boolean inRegion(Arena arena, Location loc) { return (ma && arena != null && arena.inRegion(loc)); } + public boolean inRegion(Arena arena, Location loc) + { + return (arena != null && arena.inRegion(loc)); + } + + /** + * Check if a Location is inside of a specific arena region (by arena name). + * @param arenaName The name of an arena + * @param loc A location + * @return true, if the Location is inside of the arena region. + */ + public boolean inRegion(String arenaName, Location loc) + { + Arena arena = plugin.getAM().getArenaWithName(arenaName); + if (arena == null) + throw new NullPointerException("There is no arena with that name"); + + return arena.inRegion(loc); + } /** * Check if a Location is inside of the region of an arena that is currently running. * @param loc A location. * @return true, if the Location is inside of the region of an arena that is currently running. */ - public boolean inRunningRegion(Location loc) { return inRegion(loc, false, true); } + public boolean inRunningRegion(Location loc) + { + return inRegion(loc, false, true); + } /** * Check if a Location is inside of the region of an arena that is currently enabled. * @param loc A location. * @return true, if the Location is inside of the region of an arena that is currently enabled. */ - public boolean inEnabledRegion(Location loc) { return inRegion(loc, true, false); } + public boolean inEnabledRegion(Location loc) + { + return inRegion(loc, true, false); + } /** * Private helper method for inRunningRegion and inEnabledRegion @@ -76,7 +97,7 @@ public class MobArenaHandler private boolean inRegion(Location loc, boolean enabled, boolean running) { // If the plugin doesn't exist, always return false. - if (!ma || plugin.getAM() == null) return false; + if (plugin.getAM() == null) return false; // Return true if location is within just one arena's region. for (Arena arena : plugin.getAM().arenas) @@ -87,40 +108,124 @@ public class MobArenaHandler return false; } + + + /*////////////////////////////////////////////////////////////////// + + PLAYER/MONSTER/PET METHODS + + //////////////////////////////////////////////////////////////////*/ + + /** + * Check if a player is in a MobArena arena (by Player). + * @param player The player + * @return true, if the player is in an arena + */ + public boolean isPlayerInArena(Player player) + { + return (plugin.getAM().getArenaWithPlayer(player) != null); + } + + /** + * Check if a player is in a MobArena arena (by name). + * @param playerName The name of the player + * @return true, if the player is in an arena + */ + public boolean isPlayerInArena(String playerName) + { + return (plugin.getAM().getArenaWithPlayer(playerName) != null); + } + + /** + * Get the MobArena class of a given player. + * @param player The player + * @return The class name of the player if the player is in the arena, null otherwise + */ + public String getPlayerClass(Player player) + { + Arena arena = plugin.getAM().getArenaWithPlayer(player); + if (arena == null) return null; + + return arena.classMap.get(player); + } + + /** + * Get the MobArena class of a given player in a given arena. + * This method is faster than the above method, granted the Arena object is known. + * @param arena The MobArena arena to check in + * @param player The player to look up + * @return The class name of the player, if the player is in the arena, null otherwise + */ + public String getPlayerClass(Arena arena, Player player) + { + return arena.classMap.get(player); + } + + /** + * Check if a monster is in a MobArena arena. + * @param entity The monster entity + * @return true, if the monster is in an arena + */ + public boolean isMonsterInArena(LivingEntity entity) + { + return plugin.getAM().getArenaWithMonster(entity) != null; + } + + /** + * Check if a pet is in a MobArena arena. + * @param wolf The pet wolf + * @return true, if the pet is in an arena + */ + public boolean isPetInArena(LivingEntity wolf) + { + return plugin.getAM().getArenaWithPet(wolf) != null; + } + + + + /*////////////////////////////////////////////////////////////////// + + ARENA GETTERS + + //////////////////////////////////////////////////////////////////*/ + /** * Get an Arena object at the given location. * @param loc A location * @return an Arena object, or null */ - public Arena getArenaAtLocation(Location loc) { return (ma) ? plugin.getAM().getArenaAtLocation(loc) : null; } + public Arena getArenaAtLocation(Location loc) + { + return plugin.getAM().getArenaAtLocation(loc); + } /** * Get the Arena object that the given player is currently in. * @param p A player * @return an Arena object, or null */ - public Arena getArenaWithPlayer(Player p) { return (ma) ? plugin.getAM().getArenaWithPlayer(p) : null; } + public Arena getArenaWithPlayer(Player p) + { + return plugin.getAM().getArenaWithPlayer(p); + } /** * Get the Arena object that the given pet is currently in. * @param wolf A pet wolf * @return an Arena object, or null */ - public Arena getArenaWithPet(Entity wolf) { return (ma) ? plugin.getAM().getArenaWithPet(wolf) : null; } + public Arena getArenaWithPet(Entity wolf) + { + return plugin.getAM().getArenaWithPet(wolf); + } /** * Get the Arena object that the given monster is currently in. * @param monster A monster * @return an Arena object, or null */ - public Arena getArenaWithMonster(Entity monster) { return (ma) ? plugin.getAM().getArenaWithMonster(monster) : null; } - - /** - * Check if the server is running MobArena. - * @return true, if MobArena exists on the server. - */ - public boolean hasMA() + public Arena getArenaWithMonster(Entity monster) { - return ma; + return plugin.getAM().getArenaWithMonster(monster); } } diff --git a/src/com/garbagemule/MobArena/MobArenaListener.java b/src/com/garbagemule/MobArena/MobArenaListener.java index 1613774..5737773 100644 --- a/src/com/garbagemule/MobArena/MobArenaListener.java +++ b/src/com/garbagemule/MobArena/MobArenaListener.java @@ -3,6 +3,9 @@ package com.garbagemule.MobArena; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import com.garbagemule.MobArena.waves.Wave.WaveBranch; +import com.garbagemule.MobArena.waves.Wave.WaveType; + public class MobArenaListener { protected MobArena plugin; @@ -13,11 +16,10 @@ public class MobArenaListener plugin.getAM().listeners.add(this); } - public void onArenaStart() {} - public void onArenaEnd() {} - public void onDefaultWave(int waveNumber) {} - public void onSpecialWave(int waveNumber, int specialwaveNumber) {} - public void onPlayerJoin(Player p) {} - public void onPlayerLeave(Player p) {} - public void onPlayerDeath(Player p) {} + public void onArenaStart(Arena arena) {} + public void onArenaEnd(Arena arena) {} + public void onWave(Arena arena, int waveNumber, String waveName, WaveBranch waveBranch, WaveType waveType) {} + public void onPlayerJoin(Arena arena, Player p) {} + public void onPlayerLeave(Arena arena, Player p) {} + public void onPlayerDeath(Arena arena, Player p) {} } diff --git a/src/com/garbagemule/MobArena/repairable/Repairable.java b/src/com/garbagemule/MobArena/repairable/Repairable.java new file mode 100644 index 0000000..067f08c --- /dev/null +++ b/src/com/garbagemule/MobArena/repairable/Repairable.java @@ -0,0 +1,23 @@ +package com.garbagemule.MobArena.repairable; + +//import java.io.Serializable; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.BlockState; + +public interface Repairable// extends Serializable +{ + public void repair(); + + public BlockState getState(); + + public Material getType(); + public int getId(); + public byte getData(); + + public World getWorld(); + public int getX(); + public int getY(); + public int getZ(); +} diff --git a/src/com/garbagemule/MobArena/repairable/RepairableAttachable.java b/src/com/garbagemule/MobArena/repairable/RepairableAttachable.java new file mode 100644 index 0000000..678b49f --- /dev/null +++ b/src/com/garbagemule/MobArena/repairable/RepairableAttachable.java @@ -0,0 +1,35 @@ +package com.garbagemule.MobArena.repairable; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.material.Attachable; + +public class RepairableAttachable extends RepairableBlock +{ + private int x, y, z; + + public RepairableAttachable(BlockState state) + { + super(state); + + BlockState attached; + if (state.getData() instanceof Attachable) + attached = state.getBlock().getRelative(((Attachable) state.getData()).getAttachedFace()).getState(); + else + attached = state.getBlock().getRelative(BlockFace.DOWN).getState(); + + x = attached.getX(); + y = attached.getY(); + z = attached.getZ(); + } + + public void repair() + { + Block b = getWorld().getBlockAt(x,y,z); + if (b.getTypeId() == 0) + b.setTypeId(1); + + super.repair(); + } +} diff --git a/src/com/garbagemule/MobArena/repairable/RepairableBed.java b/src/com/garbagemule/MobArena/repairable/RepairableBed.java new file mode 100644 index 0000000..db1a642 --- /dev/null +++ b/src/com/garbagemule/MobArena/repairable/RepairableBed.java @@ -0,0 +1,24 @@ +package com.garbagemule.MobArena.repairable; + +import org.bukkit.block.BlockState; +import org.bukkit.material.Bed; + +public class RepairableBed extends RepairableBlock +{ + private BlockState other; + + public RepairableBed(BlockState state) + { + super(state); + other = state.getBlock().getRelative(((Bed) state.getData()).getFacing()).getState(); + } + + public void repair() + { + if (getWorld().getBlockAt(getX(), getY(), getZ()).getState().getData() instanceof Bed) + return; + + super.repair(); + other.getBlock().setTypeIdAndData(getId(), (byte) (getData() + 8), false); + } +} diff --git a/src/com/garbagemule/MobArena/repairable/RepairableBlock.java b/src/com/garbagemule/MobArena/repairable/RepairableBlock.java new file mode 100644 index 0000000..83a3de9 --- /dev/null +++ b/src/com/garbagemule/MobArena/repairable/RepairableBlock.java @@ -0,0 +1,114 @@ +package com.garbagemule.MobArena.repairable; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.BlockState; + +public class RepairableBlock implements Repairable +{ + private BlockState state; + private World world; + private int id, x, y, z; + private Material type; + private byte data; + + public RepairableBlock(BlockState state) + { + this.state = state; + + world = state.getWorld(); + + x = state.getX(); + y = state.getY(); + z = state.getZ(); + + id = state.getTypeId(); + type = state.getType(); + data = state.getRawData(); + } + + /** + * Repairs the block by setting the type and data + */ + public void repair() + { + world.getBlockAt(x,y,z).setTypeIdAndData(id, data, false); + } + + public BlockState getState() + { + return state; + } + + public World getWorld() + { + return world; + } + + public Material getType() + { + return type; + } + + public int getId() + { + return id; + } + + public byte getData() + { + return data; + } + + public int getX() + { + return x; + } + + public int getY() + { + return y; + } + + public int getZ() + { + return z; + } + + /* + public void setWorld(World world) + { + this.world = world; + } + + public void setType(Material type) + { + this.type = type; + } + + public void setId(int id) + { + this.id = id; + } + + public void setData(byte data) + { + this.data = data; + } + + public void setX(int x) + { + this.x = x; + } + + public void setY(int y) + { + this.y = y; + } + + public void setZ(int z) + { + this.z = z; + } + */ +} diff --git a/src/com/garbagemule/MobArena/repairable/RepairableComparator.java b/src/com/garbagemule/MobArena/repairable/RepairableComparator.java new file mode 100644 index 0000000..56db9a8 --- /dev/null +++ b/src/com/garbagemule/MobArena/repairable/RepairableComparator.java @@ -0,0 +1,35 @@ +package com.garbagemule.MobArena.repairable; + +import java.util.Comparator; + +import org.bukkit.Material; +import org.bukkit.material.Attachable; +import org.bukkit.material.Bed; +import org.bukkit.material.Door; +import org.bukkit.material.MaterialData; +import org.bukkit.material.Redstone; + +public class RepairableComparator implements Comparator +{ + public int compare(Repairable r1, Repairable r2) + { + if (restoreLast(r1)) + { + if (restoreLast(r2)) + return 0; + return 1; + } + else if (restoreLast(r2)) + return -1; + + return 0; + } + + private boolean restoreLast(Repairable r) + { + Material t = r.getType(); + MaterialData m = r.getState().getData(); + + return (m instanceof Attachable || m instanceof Redstone || m instanceof Door || m instanceof Bed || t == Material.STATIONARY_LAVA || t == Material.STATIONARY_WATER || t == Material.FIRE); + } +} diff --git a/src/com/garbagemule/MobArena/repairable/RepairableContainer.java b/src/com/garbagemule/MobArena/repairable/RepairableContainer.java new file mode 100644 index 0000000..f4b8245 --- /dev/null +++ b/src/com/garbagemule/MobArena/repairable/RepairableContainer.java @@ -0,0 +1,56 @@ +package com.garbagemule.MobArena.repairable; + +import org.bukkit.block.BlockState; +import org.bukkit.block.ContainerBlock; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import com.garbagemule.MobArena.util.InventoryItem; + +public class RepairableContainer extends RepairableBlock +{ + private InventoryItem[] items; + + public RepairableContainer(BlockState state, boolean clear) + { + super(state); + + // Grab the inventory and its contents + Inventory inv = ((ContainerBlock) state).getInventory(); + ItemStack[] contents = inv.getContents(); + + // Initialize the items array + items = new InventoryItem[contents.length]; + + // Turn every ItemStack into an InventoryItem + for (int i = 0; i < items.length; i++) + items[i] = InventoryItem.parseItemStack(contents[i]); + + // Clear the inventory if prompted + if (clear) inv.clear(); + } + + public RepairableContainer(BlockState state) + { + this(state, true); + } + + /** + * Repairs the container block by adding all the contents back in. + */ + public void repair() + { + super.repair(); + + // Grab the inventory + ContainerBlock cb = (ContainerBlock) getWorld().getBlockAt(getX(),getY(),getZ()).getState(); + Inventory inv = cb.getInventory(); + + // Turn every InventoryItem into an ItemStack + for (int i = 0; i < items.length; i++) + { + InventoryItem item = items[i]; + inv.setItem(i, item != null ? item.toItemStack() : null); + } + } +} diff --git a/src/com/garbagemule/MobArena/repairable/RepairableDoor.java b/src/com/garbagemule/MobArena/repairable/RepairableDoor.java new file mode 100644 index 0000000..c6fe66e --- /dev/null +++ b/src/com/garbagemule/MobArena/repairable/RepairableDoor.java @@ -0,0 +1,36 @@ +package com.garbagemule.MobArena.repairable; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.material.Door; + +public class RepairableDoor extends RepairableAttachable//RepairableBlock +{ + private BlockState other; + private int x, y, z; + + public RepairableDoor(BlockState state) + { + super(state); + other = state.getBlock().getRelative(BlockFace.UP).getState(); + + BlockState attached = state.getBlock().getRelative(BlockFace.DOWN).getState(); + x = attached.getX(); + y = attached.getY(); + z = attached.getZ(); + } + + public void repair() + { + if (getWorld().getBlockAt(getX(), getY(), getZ()).getState().getData() instanceof Door) + return; + + Block b = getWorld().getBlockAt(x,y,z); + if (b.getTypeId() == 0) + b.setTypeId(1); + + super.repair(); + other.getBlock().setTypeIdAndData(getId(), (byte) (getData() + 8), false); + } +} diff --git a/src/com/garbagemule/MobArena/repairable/RepairableSign.java b/src/com/garbagemule/MobArena/repairable/RepairableSign.java new file mode 100644 index 0000000..87ef956 --- /dev/null +++ b/src/com/garbagemule/MobArena/repairable/RepairableSign.java @@ -0,0 +1,32 @@ +package com.garbagemule.MobArena.repairable; + +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; + +public class RepairableSign extends RepairableAttachable +{ + private String[] lines = new String[4]; + + public RepairableSign(BlockState state) + { + super(state); + + Sign s = (Sign) state; + lines = s.getLines(); + } + + /** + * Repairs the sign block by restoring all the lines + */ + public void repair() + { + super.repair(); + + Sign s = (Sign) getWorld().getBlockAt(getX(),getY(),getZ()).getState(); + s.setLine(0, lines[0]); + s.setLine(1, lines[1]); + s.setLine(2, lines[2]); + s.setLine(3, lines[3]); + } + +} diff --git a/src/com/garbagemule/MobArena/EntityPosition.java b/src/com/garbagemule/MobArena/util/EntityPosition.java similarity index 97% rename from src/com/garbagemule/MobArena/EntityPosition.java rename to src/com/garbagemule/MobArena/util/EntityPosition.java index 5ee96bb..93c1498 100644 --- a/src/com/garbagemule/MobArena/EntityPosition.java +++ b/src/com/garbagemule/MobArena/util/EntityPosition.java @@ -1,4 +1,4 @@ -package com.garbagemule.MobArena; +package com.garbagemule.MobArena.util; import java.io.Serializable; import org.bukkit.Location; diff --git a/src/com/garbagemule/MobArena/util/FileUtils.java b/src/com/garbagemule/MobArena/util/FileUtils.java new file mode 100644 index 0000000..2fbfa2d --- /dev/null +++ b/src/com/garbagemule/MobArena/util/FileUtils.java @@ -0,0 +1,216 @@ +package com.garbagemule.MobArena.util; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLConnection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.bukkit.util.config.Configuration; + +import com.garbagemule.MobArena.MobArena; + +public class FileUtils +{ + public static enum Library + { + XML("jdom.jar", "http://mirrors.ibiblio.org/pub/mirrors/maven2/org/jdom/jdom/1.1/jdom-1.1.jar", "http://garbagemule.binhoster.com/Minecraft/MobArena/jdom-1.1.jar");//"org.jdom.Content"); + + public String filename, url, backup; + + private Library(String filename, String url, String backup) + { + this.filename = filename; + this.url = url; + this.backup = backup; + } + + public static Library fromString(String string) + { + return WaveUtils.getEnumFromString(Library.class, string); + } + } + + /** + * Download all necessary libraries. + * @param config The MobArena config-file + */ + public static void fetchLibs(Configuration config) + { + // Get all arenas + List arenas = config.getKeys("arenas"); + if (arenas == null) return; + + // Add all the logging types + Set libs = new HashSet(); + for (String a : arenas) + { + String type = config.getString("arenas." + a + ".settings.logging", "").toLowerCase(); + + Library lib = Library.fromString(type.toUpperCase()); + if (lib != null) + libs.add(lib); + } + + // Download all libraries + for (Library lib : libs) + if (!libraryExists(lib)) + fetchLib(lib); + } + + /** + * Download a given library. + * @param lib The Library to download + */ + private static synchronized void fetchLib(Library lib) + { + MobArena.info("Downloading library '" + lib.filename + "' for log-method '" + lib.name().toLowerCase() + "'..."); + + URLConnection con = null; + InputStream in = null; + OutputStream out = null; + + // Open a connection + try + { + con = new URL(lib.url).openConnection(); + con.setConnectTimeout(2000); + con.setUseCaches(true); + } + catch (Exception e) + { + if (lib.backup == null) + { + e.printStackTrace(); + System.out.println("Connection issues"); + return; + } + + try + { + con = new URL(lib.backup).openConnection(); + con.setConnectTimeout(2000); + con.setUseCaches(true); + } + catch (Exception e2) + { + e2.printStackTrace(); + System.out.println("Connection issues"); + return; + } + } + + try + { + File libdir = new File(MobArena.dir, "lib"); + libdir.mkdir(); + File file = new File(libdir, lib.filename); + + long startTime = System.currentTimeMillis(); + + // Set up the streams + in = con.getInputStream(); + out = new FileOutputStream(file); + if (in == null || out == null) return; + + byte[] buffer = new byte[65536]; + int length = 0; + + // Write the library to disk + while ((length = in.read(buffer)) > 0) + out.write(buffer, 0, length); + + // Announce successful download + MobArena.info(lib.filename + " downloaded in " + ((System.currentTimeMillis()-startTime)/1000.0) + " seconds."); + + addLibraryToClassLoader(file); + } + catch (Exception e) + { + e.printStackTrace(); + MobArena.warning("Couldn't download library: " + lib.filename); + } + finally + { + try + { + if (in != null) in.close(); + if (out != null) out.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + + private static boolean libraryExists(Library lib) + { + return new File(MobArena.dir + File.separator + "lib", lib.filename).exists(); + } + + private static void addLibraryToClassLoader(File file) + { + try + { + // Grab the class loader and its addURL method + URLClassLoader cl = (URLClassLoader) ClassLoader.getSystemClassLoader(); + Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{ URL.class }); + addURL.setAccessible(true); + + // Add the library + addURL.invoke(cl, new Object[]{file.toURI().toURL()}); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * Create default files from the res/ directory, if they exist. + * @param filenames Files to be created. + */ + public static void extractDefaults(String... filenames) + { + for (String filename : filenames) + extractFile(MobArena.dir, filename); + } + + public static void extractFile(File dir, String filename) + { + // Skip if file exists + File file = new File(dir, filename); + if (file.exists()) return; + + // Skip if there is no resource with that name + InputStream in = MobArena.class.getResourceAsStream("/res/" + filename); + if (in == null) return; + + try + { + // Set up an output stream + FileOutputStream out = new FileOutputStream(file); + byte[] buffer = new byte[8192]; + int length = 0; + + // Write the resource data to the file + while ((length = in.read(buffer)) > 0) + out.write(buffer, 0, length); + + if (in != null) in.close(); + if (out != null) out.close(); + } + catch (Exception e) + { + e.printStackTrace(); + MobArena.warning("Problem creating file '" + filename + "'!"); + } + } +} diff --git a/src/com/garbagemule/MobArena/util/InventoryItem.java b/src/com/garbagemule/MobArena/util/InventoryItem.java new file mode 100644 index 0000000..38f2757 --- /dev/null +++ b/src/com/garbagemule/MobArena/util/InventoryItem.java @@ -0,0 +1,269 @@ +package com.garbagemule.MobArena.util; + +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; + +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; + +public class InventoryItem implements Serializable +{ + private static final long serialVersionUID = 739709220350581510L; + private int id; + private int amount; + private Byte data; + private short durability; + + /** + * Default constructor. + * @param id The data value/type id of the ItemStack + * @param amount The amount of the stack + * @param data The MaterialData (possibly null) of the stack + * @param durability The durability of the stack + */ + public InventoryItem(int id, int amount, Byte data, short durability) + { + this.id = id; + this.amount = amount; + this.data = data; + this.durability = durability; + } + + /** + * ItemStack constructor. + * @param stack The ItemStack to base the InventoryItem off of. + */ + public InventoryItem(ItemStack stack) + { + if (stack == null) + id = -1; + + id = stack.getTypeId(); + amount = stack.getAmount(); + + // In case of "odd" items, don't attempt to get data and durability + if (id < 0) return; + + data = stack.getData() == null ? null : stack.getData().getData(); + durability = stack.getDurability(); + } + + /** + * Static method for turning a (possibly null) InventoryItem into an ItemStack. + * The method is useful if it is unknown whether an InventoryItem object is + * null or not. + * @param item The Inventory item to convert + * @return An ItemStack representation of the InventoryItem, or null + */ + public static ItemStack toItemStack(InventoryItem item) + { + if (item == null) + return null; + + return item.toItemStack(); + } + + public static ItemStack[] toItemStacks(InventoryItem[] items) + { + ItemStack[] result = new ItemStack[items.length]; + + for (int i = 0; i < items.length; i++) + result[i] = items[i].toItemStack(); + + return result; + } + + /** + * Static method for converting an ItemStack to an InventoryItems. + * @param stack The ItemStack to convert + * @return An InventoryItem representation of the ItemStack, or null + */ + public static InventoryItem parseItemStack(ItemStack stack) + { + if (stack == null) + return new InventoryItem(-1, -1, null, (short) 0); + + return new InventoryItem(stack); + } + + /** + * Static method for converting an ItemStack array to an InventoryItem array. + * @param stacks The ItemStack array + * @return An InventoryItem array + */ + public static InventoryItem[] parseItemStacks(ItemStack[] stacks) + { + InventoryItem[] items = new InventoryItem[stacks.length]; + + for (int i = 0; i < items.length; i++) + items[i] = parseItemStack(stacks[i]); + + return items; + } + + /** + * Convert a list of ItemStacks into an array of InventoryItems + * @param stacks List of ItemStacks to convert + * @return An InventoryItem array + */ + public static InventoryItem[] parseItemStacks(List stacks) + { + InventoryItem[] items = new InventoryItem[stacks.size()]; + + for (int i = 0; i < items.length; i++) + items[i] = parseItemStack(stacks.get(i)); + + return items; + } + + /** + * Static method for extracting all InventoryItems from an InventoryItem array. + * @param id The type id of the items to extract + * @param items The InventoryItem array + * @return A list of all InventoryItems removed + */ + public static List extractAllFromArray(int id, InventoryItem[] items) + { + List list = new LinkedList(); + + for (int i = 0; i < items.length; i++) + { + if (items[i].getTypeId() == id) + { + list.add(items[i]); + items[i].setTypeId(-1); + } + } + + return list; + } + + /** + * Static method for removing an InventoryItem from an InventoryItem array. + * @param item The InventoryItem to remove + * @param items The InventoryItem array to remove from + * @return true, if the item was removed successfully, false otherwise + */ + public static boolean removeItemFromArray(InventoryItem item, InventoryItem[] items) + { + // Grab the total amount to remove + int leftToRemove = item.getAmount(); + + for (int i = 0; i < items.length; i++) + { + if (items[i].getTypeId() != item.getTypeId()) + continue; + + // Grab the amount + int amount = items[i].getAmount(); + + // Reduce amount/nullify item + if (amount > leftToRemove) + { + items[i].setAmount(amount - leftToRemove); + leftToRemove = 0; + } + else + { + items[i].setTypeId(-1); + leftToRemove -= amount; + } + + // If nothing left to remove, return true. + if (leftToRemove == 0) + return true; + } + + return false; + } + + /** + * Get the data value/type id of the item + * @return A type id + */ + public int getTypeId() + { + return id; + } + + /** + * Set the data value/type id of the item + * @param id A type id + */ + public void setTypeId(int id) + { + this.id = id; + } + + /** + * Get the amount of the item + * @return An amount + */ + public int getAmount() + { + return amount; + } + + /** + * Set the amount of the item + * @param amount An amount + */ + public void setAmount(int amount) + { + this.amount = amount; + } + + /** + * Get the MaterialData of the item + * @return A MaterialData + */ + public MaterialData getData() + { + return new MaterialData(id, data == null ? (byte) 0 : data); + } + + /** + * Set the MaterialData of the item + * @param data A MaterialData + */ + public void setData(MaterialData data) + { + this.data = data.getData(); + } + + /** + * Get the durability of the item + * @return The item durability + */ + public short getDurability() + { + return durability; + } + + /** + * Set the durability of the item + * @param durability An item durability + */ + public void setDurability(short durability) + { + this.durability = durability; + } + + /** + * Convert this InventoryItem to an ItemStack representation + * @return An ItemStack representation of this InventoryItem, or null if the type id is -1 + */ + public ItemStack toItemStack() + { + if (id == -1) + return null; + + ItemStack stack = new ItemStack(id, amount, durability); + + if (data != null) + stack.setData(getData()); + + return stack; + } +} diff --git a/src/com/garbagemule/MobArena/util/TextUtils.java b/src/com/garbagemule/MobArena/util/TextUtils.java new file mode 100644 index 0000000..85cda7d --- /dev/null +++ b/src/com/garbagemule/MobArena/util/TextUtils.java @@ -0,0 +1,42 @@ +package com.garbagemule.MobArena.util; + +public class TextUtils +{ + /** + * Add character padding on the right side of a String. + * @param s String to add padding to + * @param length Total amount of characters in the returned String + * @param pad The padding character + * @return A padded String with the input length + */ + public static String padRight(String s, int length, char pad) + { + StringBuffer buffy = new StringBuffer(); + buffy.append(s); + for (int i = s.length(); i < length; i++) + buffy.append(pad); + return buffy.toString(); + } + public static String padRight(String s, int length) { return padRight(s, length, ' '); } + public static String padRight(int s, int length) { return padRight(Integer.toString(s), length, ' '); } + public static String padRight(double s, int length) { return padRight(Double.toString(s), length, ' '); } + + /** + * Add character padding on the left side of a String. + * @param s String to add padding to + * @param length Total amount of characters in the returned String + * @param pad The padding character + * @return A padded String with the input length + */ + public static String padLeft(String s, int length, char pad) + { + StringBuffer buffy = new StringBuffer(); + for (int i = 0; i < length - s.length(); i++) + buffy.append(pad); + buffy.append(s); + return buffy.toString(); + } + public static String padLeft(String s, int length) { return padLeft(s, length, ' '); } + public static String padLeft(int s, int length) { return padLeft(Integer.toString(s), length, ' '); } + public static String padLeft(double s, int length) { return padLeft(Double.toString(s), length, ' '); } +} diff --git a/src/com/garbagemule/MobArena/util/WaveUtils.java b/src/com/garbagemule/MobArena/util/WaveUtils.java new file mode 100644 index 0000000..cc880a5 --- /dev/null +++ b/src/com/garbagemule/MobArena/util/WaveUtils.java @@ -0,0 +1,559 @@ +package com.garbagemule.MobArena.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.TreeSet; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.util.config.Configuration; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.MobArena; +import com.garbagemule.MobArena.waves.*; +import com.garbagemule.MobArena.waves.Wave.*; + +public class WaveUtils +{ + /** + * Get all the spawnpoints that have players nearby. + */ + public static List getValidSpawnpoints(Arena arena, Collection players) + { + //long start = System.nanoTime(); + List result = new ArrayList(); + + //double x1 = Double.NaN, y1 = Double.NaN, z1 = Double.NaN, // Bottom + // x2 = Double.NaN, y2 = Double.NaN, z2 = Double.NaN; // Top + + for (Location l : arena.getSpawnpoints()) + { + for (Player p : players) + { + if (l.distanceSquared(p.getLocation()) >= MobArena.MIN_PLAYER_DISTANCE_SQUARED) + continue; + + result.add(l); + break; + } + } + /* + // Get the region that the players span. + for (Player p : players) + { + double x = p.getLocation().getBlockX(); + double y = p.getLocation().getBlockY(); + double z = p.getLocation().getBlockZ(); + + // Initialize the coordinates if they aren't already. + if (Double.isNaN(x1)) + { + x1 = x; y1 = y; z1 = z; // Bottom + x2 = x; y2 = y; z2 = z; // Top + continue; + } + + // Update x + if (x < x1) x1 = x; + else if (x > x2) x2 = x; + + // Update y + if (y < y1) y1 = y; + else if (y > y2) y2 = y; + + // Update z + if (z < z1) z1 = z; + else if (z > z2) z2 = z; + } + + // Expand by the minimum player distance. + x1 -= MobArena.MIN_PLAYER_DISTANCE; y1 -= MobArena.MIN_PLAYER_DISTANCE; z1 -= MobArena.MIN_PLAYER_DISTANCE; + x2 += MobArena.MIN_PLAYER_DISTANCE; y2 += MobArena.MIN_PLAYER_DISTANCE; z2 += MobArena.MIN_PLAYER_DISTANCE; + + for (Location s : arena.getAllSpawnpoints()) + if (MAUtils.inRegion(s, x1, y1, z1, x2, y2, z2)) + result.add(s); + */ + // If no players are in range, just use all the spawnpoints. + if (result.isEmpty()) + { + MobArena.warning("Spawnpoints of arena '" + arena.configName() + "' may be too far apart!"); + return arena.getAllSpawnpoints();//result.addAll(arena.getAllSpawnpoints()); + } + + // Else, return the valid spawnpoints. + return result; + } + + public static Player getClosestPlayer(Arena arena, Entity e) + { + // Set up the comparison variable and the result. + double dist = 0; + double current = Double.POSITIVE_INFINITY; + Player result = null; + + /* Iterate through the ArrayList, and update current and result every + * time a squared distance smaller than current is found. */ + for (Player p : arena.getLivingPlayers()) + { + if (!arena.getWorld().equals(p.getWorld())) + { + MobArena.info("Player '" + p.getName() + "' is not in the right world. Kicking..."); + p.kickPlayer("[MobArena] Cheater! (Warped out of the arena world.)"); + continue; + } + + dist = p.getLocation().distanceSquared(e.getLocation()); + if (dist < current && dist < MobArena.MIN_PLAYER_DISTANCE_SQUARED) + { + current = dist; + result = p; + } + } + return result; + } + + /** + * Grab and process all the waves in the config-file for the arena. + */ + public static TreeSet getWaves(Arena arena, Configuration config, WaveBranch branch) + { + // Determine the branch type of the wave, and grab the appropriate comparator + String b = branch.toString().toLowerCase(); + TreeSet result = new TreeSet(getComparator(branch)); + + // Grab the waves from the config-file + String path = "arenas." + arena.configName() + ".waves." + b; // waves.yml, change to either "waves." + b, or simply b + List waves = config.getKeys(path); + + // If there are any waves, process them + if (waves != null) + { + Wave wave; + for (String w : waves) + { + // ---------- path ----------- -- w -- + // path argument becomes: "arenas..waves..." + wave = getWave(arena, config, path + "." + w + ".", w, branch); + if (wave != null) + result.add(wave); + else MobArena.warning("Wave '" + w + "' in " + path + " was not added!"); + } + } + + // If there are no waves and the type is 'recurrent', add a couple of auto-generated waves. + if (branch == WaveBranch.RECURRENT && (result.isEmpty() || waves == null)) + { + MobArena.info("No valid recurrent waves detected for arena '" + arena.configName() + "'. Using defaults..."); + DefaultWave def = new DefaultWave(arena, "DEF_WAVE_AUTO", 1, 1, 1, config, path + ".DEF_WAVE_AUTO."); + SpecialWave spec = new SpecialWave(arena, "SPEC_WAVE_AUTO", 4, 4, 2, config, path + ".SPEC_WAVE_AUTO."); + result.add(def); + result.add(spec); + } + + return result; + } + + /** + * Get a single wave based on the config-file, the path, and branch + * @return A Wave object if it is well defined, null otherwise. + */ + private static Wave getWave(Arena arena, Configuration config, String path, String name, WaveBranch branch) + { + // Grab the wave type, if null or not well defined, return null + WaveType type = WaveType.fromString(config.getString(path + "type")); + if (type == null || !isWaveWellDefined(config, path, branch, type)) + return null; + + // + Wave result = null; + if (branch == WaveBranch.RECURRENT) + { + int frequency = config.getInt(path + "frequency", 0); + int priority = config.getInt(path + "priority", 0); + int wave = config.getInt(path + "wave", frequency); + + if (type == WaveType.DEFAULT) + result = new DefaultWave(arena, name, wave, frequency, priority, config, path); + else if (type == WaveType.SPECIAL) + result = new SpecialWave(arena, name, wave, frequency, priority, config, path); + else if (type == WaveType.SWARM) + result = new SwarmWave(arena, name, wave, frequency, priority, config, path); + else if (type == WaveType.BOSS) + result = new BossWave(arena, name, wave, frequency, priority, config, path); + } + else + { + int wave = config.getInt(path + "wave", 0); + + if (type == WaveType.DEFAULT) + result = new DefaultWave(arena, name, wave, config, path); + else if (type == WaveType.SPECIAL) + result = new SpecialWave(arena, name, wave, config, path); + else if (type == WaveType.SWARM) + result = new SwarmWave(arena, name, wave, config, path); + else if (type == WaveType.BOSS) + result = new BossWave(arena, name, wave, config, path); + } + return result; + } + + + + /*//////////////////////////////////////////////////////////////////// + // + // Well definedness checks + // + ////////////////////////////////////////////////////////////////////*/ + + /** + * Check if a wave in the config-file is well-defined. + * The method first checks if the wave is well-defined according to + * the branch-specific requirements. Recurrent waves must have the + * two nodes 'priority' and 'frequency', and single waves must have + * the node 'wave'. + * Any other requirements are type-specific, and thus we check if the + * type is well-defined. + * @param config Config-file Configuration + * @param path The absolute path of the wave + * @param branch The branch of the wave + * @param type The wave type + * @return true, only if the entire wave-node is well-defined. + */ + private static boolean isWaveWellDefined(Configuration config, String path, WaveBranch branch, WaveType type) + { + // This boolean is used in the "leaf methods" + boolean wellDefined = true; + + if (branch == WaveBranch.RECURRENT) + { + // REQUIRED: Priority and frequency + int priority = config.getInt(path + "priority", 0); + int frequency = config.getInt(path + "frequency", 0); + if (priority == 0) + { + MobArena.warning("Missing 'priority'-node in " + path); + wellDefined = false; + } + if (frequency == 0) + { + MobArena.warning("Missing 'frequency'-node in " + path); + wellDefined = false; + } + // OPTIONAL: Wave + int wave = config.getInt(path + "wave", 0); + if (wave < 0) + { + MobArena.warning("'wave' must be greater than 0 in " + path); + wellDefined = false; + } + } + else if (branch == WaveBranch.SINGLE) + { + // REQUIRED: Wave number + int wave = config.getInt(path + "wave", 0); + if (wave == 0) + { + MobArena.warning("Missing 'wave'-node in " + path); + wellDefined = false; + } + else if (wave < 0) + { + MobArena.warning("'wave' must be greater than 0 in " + path); + wellDefined = false; + } + } + else wellDefined = false; + + // Passed branch-checks; check type + return isTypeWellDefined(config, path, type, wellDefined); + } + + /** + * Check if a wave type in the config-file is well-defined. + * The method calls the appropriate sub-method to check if the type + * is well-defined. + * @param config Config-file Configuration + * @param path The absolute path of the wave + * @param type The wave type + * @param wellDefined Pass-through boolean for "leaf methods". + * @return true, only if the entire wave-node is well-defined. + */ + private static boolean isTypeWellDefined(Configuration config, String path, WaveType type, boolean wellDefined) + { + if (type == WaveType.DEFAULT) + return isDefaultWaveWellDefined(config, path, wellDefined); + else if (type == WaveType.SPECIAL) + return isSpecialWaveWellDefined(config, path, wellDefined); + else if (type == WaveType.BOSS) + return isBossWaveWellDefined(config, path, wellDefined); + else if (type == WaveType.SWARM) + return isSwarmWaveWellDefined(config, path, wellDefined); + + return false; + } + + /** + * Check if a default wave is well-defined. + * The default waves have an optional wave growth node. Otherwise, + * they share nodes with special waves. + * @param config Config-file Configuration + * @param path The absolute path of the wave + * @param wellDefined Pass-through boolean for "leaf methods". + * @return true, only if the entire wave-node is well-defined. + */ + private static boolean isDefaultWaveWellDefined(Configuration config, String path, boolean wellDefined) + { + // OPTIONAL: Wave growth + String growth = config.getString(path + "growth"); + if (growth != null && WaveGrowth.fromString(growth) == null) + { + MobArena.warning("Invalid wave growth '" + growth + "' in " + path); + wellDefined = false; + } + + return isNormalWaveWellDefined(config, path, wellDefined); + } + + /** + * Check if a special wave is well-defined. + * The special waves have no unique nodes. + * @param config Config-file Configuration + * @param path The absolute path of the wave + * @param wellDefined Pass-through boolean for "leaf methods". + * @return true, only if the entire wave-node is well-defined. + */ + private static boolean isSpecialWaveWellDefined(Configuration config, String path, boolean wellDefined) + { + return isNormalWaveWellDefined(config, path, wellDefined); + } + + /** + * Check if a default or special wave is well-defined. + * There are no REQUIRED nodes for default or special wave types, besides + * the ones for the branch they belong to. + * The only OPTIONAL node is (currently) 'monsters' + * @param config Config-file Configuration + * @param path The absolute path of the wave + * @param wellDefined Pass-through boolean for "leaf methods". + * @return true, wellDefined is true. + */ + private static boolean isNormalWaveWellDefined(Configuration config, String path, boolean wellDefined) + { + // OPTIONAL: Monsters + List monsters = config.getKeys(path + "monsters"); + if (monsters != null) + { + for (String monster : monsters) + { + if (getEnumFromString(MACreature.class, monster) != null) + continue; + + MobArena.warning("Invalid monster type '" + monster + "' in " + path); + wellDefined = false; + } + } + else MobArena.info("No monsters listed in " + path + ", using defaults..."); + + return wellDefined; + } + + /** + * Check if a swarm wave is well defined + * @param config Config-file Configuration + * @param path The absolute path of the wave + * @param wellDefined Pass-through boolean for "leaf methods". + * @return true, wellDefined is true. + */ + private static boolean isSwarmWaveWellDefined(Configuration config, String path, boolean wellDefined) + { + // REQUIRED: Monster type + String monster = config.getString(path + "monster"); + if (monster == null) + { + MobArena.warning("Missing monster type in '" + path); + wellDefined = false; + } + else if (getEnumFromString(MACreature.class, monster) == null) + { + MobArena.warning("Invalid monster type '" + monster + "' in " + path); + wellDefined = false; + } + + // OPTIONAL: Amount + String amount = config.getString(path + "amount"); + if (amount != null && SwarmAmount.fromString(amount) == null) + { + MobArena.warning("Invalid swarm amount '" + amount + "' in " + path); + wellDefined = false; + } + + return wellDefined; + } + + /** + * Check if a boss wave is well defined. + * @param config Config-file Configuration + * @param path The absolute path of the wave + * @param wellDefined Pass-through boolean for "leaf methods". + * @return true, wellDefined is true. + */ + private static boolean isBossWaveWellDefined(Configuration config, String path, boolean wellDefined) + { + // REQUIRED: Monster type + String monster = config.getString(path + "monster"); + if (monster == null) + { + MobArena.warning("Missing monster type in '" + path); + wellDefined = false; + } + else if (getEnumFromString(MACreature.class, monster) == null) + { + MobArena.warning("Invalid monster type '" + monster + "' in " + path); + wellDefined = false; + } + + // OPTIONAL: Abilities + String abilities = config.getString(path + "abilities"); + if (abilities != null) + { + for (String ability : abilities.split(",")) + { + if (BossAbility.fromString(ability.trim().replaceAll("[-_\\.]", "").toUpperCase()) != null) + continue; + + MobArena.warning("Invalid boss ability '" + ability + "' in " + path); + wellDefined = false; + } + } + + // OPTIONAL: Ability-interval + int abilityDelay = config.getInt(path + "ability-interval", 3); + if (abilityDelay <= 0) + { + MobArena.warning("Boss ability-delay must be greater than 0, " + path); + wellDefined = false; + } + + // OPTIONAL: Ability-announce + + // TODO: OPTIONAL: Adds + // Unsure about config-file implementation... + + // OPTIONAL: Health + String health = config.getString(path + "health"); + if (health != null && BossHealth.fromString(health) == null) + { + MobArena.warning("Invalid boss health '" + health + "' in " + path); + wellDefined = false; + } + + return wellDefined; + } + + + + /*//////////////////////////////////////////////////////////////////// + // + // Comparators + // + ////////////////////////////////////////////////////////////////////*/ + + /** + * Get a comparator based on the WaveBranch parameter. + */ + public static Comparator getComparator(WaveBranch branch) + { + if (branch == WaveBranch.SINGLE) + return getSingleComparator(); + else if (branch == WaveBranch.RECURRENT) + return getRecurrentComparator(); + else + return null; + } + + /** + * Get a Comparator that compares Wave objects by wave number. + * If the wave numbers are equal, the waves are equal. This is to + * DISALLOW "duplicates" in the SINGLE WAVES collection. + * @return Comparator whose compare()-method compares wave numbers. + */ + public static Comparator getSingleComparator() + { + return new Comparator() + { + public int compare(Wave w1, Wave w2) + { + if (w1.getWave() < w2.getWave()) + return -1; + else if (w1.getWave() > w2.getWave()) + return 1; + else return 0; + } + }; + } + + /** + * Get a Comparator that compares Wave objects by priority. + * If the priorities are equal, the names are compared. This is to + * ALLOW "duplicates" in the RECURRENT WAVES collection. + * @return Comparator whose compare()-method compares wave priorities. + */ + public static Comparator getRecurrentComparator() + { + return new Comparator() + { + public int compare(Wave w1, Wave w2) + { + if (w1.getPriority() < w2.getPriority()) + return -1; + else if (w1.getPriority() > w2.getPriority()) + return 1; + else return w1.getName().compareTo(w2.getName()); + } + }; + } + + + + /*//////////////////////////////////////////////////////////////////// + // + // Misc - Perhaps move into MAUtils? + // + ////////////////////////////////////////////////////////////////////*/ + + /** + * Get the num value of a string, def if it doesn't exist. + */ + public static > T getEnumFromString(Class c, String string, T def) + { + if (c != null && string != null) + { + try + { + return Enum.valueOf(c, string.trim().toUpperCase()); + } + catch (IllegalArgumentException ex) { } + } + return def; + } + + /** + * Get the enum value of a string, null if it doesn't exist. + */ + public static > T getEnumFromString(Class c, String string) + { + if(c != null && string != null) + { + try + { + return Enum.valueOf(c, string.trim().toUpperCase()); + } + catch(IllegalArgumentException ex) { } + } + return null; + } +} diff --git a/src/com/garbagemule/MobArena/util/data/PlainText.java b/src/com/garbagemule/MobArena/util/data/PlainText.java new file mode 100644 index 0000000..46c521e --- /dev/null +++ b/src/com/garbagemule/MobArena/util/data/PlainText.java @@ -0,0 +1,155 @@ +package com.garbagemule.MobArena.util.data; + +import java.io.File; +import java.io.FileWriter; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.bukkit.entity.Player; + +import com.garbagemule.MobArena.ArenaLog; +import com.garbagemule.MobArena.ArenaPlayer; +import com.garbagemule.MobArena.MAUtils; +import com.garbagemule.MobArena.MobArena; +import com.garbagemule.MobArena.util.TextUtils; + +public class PlainText +{ + public static void saveSessionData(ArenaLog log) + { + File dir = new File(MobArena.arenaDir, log.getArena().configName()); + if (!dir.exists()) dir.mkdirs(); + + // General information + List toWrite = new LinkedList(); + toWrite.add("Start: " + log.getStartTime()); + toWrite.add("End: " + log.getEndTime()); + toWrite.add("Duration: " + log.getDuration()); + toWrite.add("Last wave: " + log.getLastWave()); + toWrite.add(" "); + + int classLength = longestClassName(log.distribution.keySet()); + int classCount = log.distribution.keySet().size(); + int playerCount = log.players.keySet().size(); + + // Class distribution + toWrite.add("Class Distribution: " + classCount + " classes"); + toWrite.addAll(getClassDistribution(log.distribution, playerCount, classLength)); + toWrite.add(" "); + + // Player data + toWrite.add("Player Data: " + playerCount + " players"); + toWrite.addAll(getPlayerData(log.players, classLength)); + + // Serialize! + serialize(log, dir, "lastsession.txt", toWrite); + } + + public static void updateArenaTotals(ArenaLog log) + { + // Grab the Configuration + //Configuration totals = Totals.getArenaTotals(log.arena); + + // Parse shit + + + // Serialize + + } + + private static void serialize(ArenaLog log, File dir, String filename, List toWrite) + { + try + { + File file = new File(dir, filename); + if (!file.exists()) file.createNewFile(); + + FileWriter fw = new FileWriter(file); + String linebreak = System.getProperty("line.separator"); + for (String s : toWrite) + { + fw.write(s); + fw.write(linebreak); + } + fw.close(); + } + catch (Exception e) + { + e.printStackTrace(); + MobArena.warning("Problem saving session data for arena '" + log.getArena().configName() + "'"); + } + } + + private static List getClassDistribution(Map map, int playerCount, int classLength) + { + List result = new LinkedList(); + + // Add each entry - class: # (percentage%) + for (Map.Entry entry : map.entrySet()) + { + StringBuffer buffy = new StringBuffer(classLength + 15); + + buffy.append("- "); + buffy.append(TextUtils.padRight(entry.getKey() + ":", classLength + 3)); // : + buffy.append(TextUtils.padLeft(entry.getValue().toString(), 2)); // + buffy.append("(" + (entry.getValue()*100/playerCount) + "%)"); // (%) + + result.add(buffy.toString()); + } + + return result; + } + + private static List getPlayerData(Map map, int classLength) + { + List result = new LinkedList(); + String pad = " "; + + // Padding variables; +2 for extra padding + int NAME = 12, CLASS = classLength, WAVE = 4, KILLS = 5, DMGDONE = 7, DMGTAKEN = 8, ACCURACY = 8; + result.add(" " + TextUtils.padRight("NAME", NAME+2) + + TextUtils.padRight("CLASS", CLASS+2) + + TextUtils.padRight("WAVE", WAVE+2) + + TextUtils.padRight("KILLS", KILLS+2) + + TextUtils.padRight("DMGDONE", DMGDONE+2) + + TextUtils.padRight("DMGTAKEN", DMGTAKEN+2) + + TextUtils.padRight("ACCURACY", ACCURACY+2) + + "REWARDS"); + + // Add each player - name class wave dmgdone dmgtaken accuracy rewards + for (Map.Entry entry : map.entrySet()) + { + Player p = entry.getKey(); + ArenaPlayer ap = entry.getValue(); + StringBuffer buffy = new StringBuffer(80); + + String name = (p.getName().length() <= NAME) ? p.getName() : p.getName().substring(0, NAME+1); + buffy.append("- "); + buffy.append(TextUtils.padRight(name, NAME)); buffy.append(pad); + buffy.append(TextUtils.padRight(ap.getClassName(), CLASS)); buffy.append(pad); + buffy.append(TextUtils.padLeft(ap.lastWave, WAVE)); buffy.append(pad); + buffy.append(TextUtils.padLeft(ap.kills, KILLS)); buffy.append(pad); + buffy.append(TextUtils.padLeft(ap.dmgDone, DMGDONE)); buffy.append(pad); + buffy.append(TextUtils.padLeft(ap.dmgTaken, DMGTAKEN)); buffy.append(pad); + buffy.append(TextUtils.padLeft(((ap.swings != 0) ? ap.hits*100/ap.swings : 0), ACCURACY-1)); buffy.append("%"); buffy.append(pad); + buffy.append(MAUtils.listToString(ap.rewards)); + + result.add(buffy.toString()); + } + + return result; + } + + private static int longestClassName(Collection names) + { + int result = 0; + + for (String c : names) + if (c.length() > result) + result = c.length(); + + return result; + } +} diff --git a/src/com/garbagemule/MobArena/util/data/Totals.java b/src/com/garbagemule/MobArena/util/data/Totals.java new file mode 100644 index 0000000..ec04d91 --- /dev/null +++ b/src/com/garbagemule/MobArena/util/data/Totals.java @@ -0,0 +1,157 @@ +package com.garbagemule.MobArena.util.data; + +import java.io.File; + +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.config.Configuration; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.ArenaLog; +import com.garbagemule.MobArena.ArenaPlayer; +import com.garbagemule.MobArena.MAUtils; +import com.garbagemule.MobArena.MobArena; +import com.garbagemule.MobArena.util.FileUtils; + +public class Totals +{ + private static final String totals_yml = "totals.yml"; + + /** + * Update the totals-file with the session data from the ArenaLog object + * @param log ArenaLog to update with + */ + public static void updateArenaTotals(ArenaLog log) + { + // Grab the configuration + Configuration totals = getArenaTotals(log.arena); + totals.load(); + + // General data + updateInt(totals, "general-info.total-games-played", 1, true); + updateInt(totals, "general-info.most-players", log.players.keySet().size(), false); + updateInt(totals, "general-info.highest-wave-reached", log.lastWave, false); + updateInt(totals, "general-info.total-monsters-killed", countKills(log), true); + updateDuration(totals, "general-info.total-duration", log.getDurationLong(), true); + updateDuration(totals, "general-info.longest-session-duration", log.getDurationLong(), false); + + // Classes + for (String c : log.arena.getClasses()) + { + // Array {kills, dmgDone, dmgTaken} + int[] a = getKillsAndDamageByClass(log, c); + updateInt(totals, "classes." + c + ".kills", a[0], true); + updateInt(totals, "classes." + c + ".damage-done", a[1], true); + updateInt(totals, "classes." + c + ".damage-taken", a[2], true); + updateInt(totals, "classes." + c + ".played", log.distribution.get(c), true); + } + + // Rewards + for (ArenaPlayer ap : log.players.values()) + { + for (ItemStack stack : ap.rewards) + { + boolean money = stack.getTypeId() == MobArena.ECONOMY_MONEY_ID; + updateInt(totals, "rewards." + (money ? "money" : stack.getType().toString().toLowerCase()), stack.getAmount(), true); + } + } + + // Players + for (ArenaPlayer ap : log.players.values()) + { + // Basic values + updateInt(totals,"players." + ap.player.getName() + ".games-played", 1, true); + updateInt(totals,"players." + ap.player.getName() + ".kills", ap.kills, true); + updateInt(totals,"players." + ap.player.getName() + ".damage-done", ap.dmgDone, true); + updateInt(totals,"players." + ap.player.getName() + ".damage-taken", ap.dmgTaken, true); + updateInt(totals,"players." + ap.player.getName() + ".swings", ap.swings, true); + updateInt(totals,"players." + ap.player.getName() + ".hits", ap.hits, true); + + // Class count + updateInt(totals,"players." + ap.player.getName() + ".classes." + ap.className,1,true); + } + + // Save everything + totals.save(); + } + + /** + * Get or create a Configuration from the totals-file + * @param arena Arena to get or create a Configuration for + * @return Configuration from the arena's totals-file + */ + public static Configuration getArenaTotals(Arena arena) + { + // Create the folder if it doesn't exist. + File dir = new File(MobArena.arenaDir, arena.configName()); + if (!dir.exists()) dir.mkdirs(); + + File file = new File(dir, totals_yml); + if (!file.exists()) FileUtils.extractFile(dir, totals_yml); + + // Grab the totals-file and return the Configuration + return new Configuration(file); + } + + /** + * Update an integer in a config-file + * @param totals Configuration to alter + * @param node Node to alter + * @param b Integer for comparison + * @param increment If true, the node will be incremented by b, otherwise overwritten if greater + */ + private static void updateInt(Configuration totals, String node, int b, boolean increment) + { + int a = totals.getInt(node, 0); + + if (increment) totals.setProperty(node, a+b); + else if (b > a) totals.setProperty(node, b); + } + + /** + * Update a duration in a config-file + * @param totals Configuration to alter + * @param node Node to alter + * @param b Duration for comparison. This is a java.sql.Timestamp.getTime() long + * @param increment If true, the node will be incremented by b, otherwise overwritten if greater + */ + private static void updateDuration(Configuration totals, String node, long b, boolean increment) + { + long a = MAUtils.parseDuration(totals.getString(node, "0:00:00")); + + if (increment) totals.setProperty(node, MAUtils.getDuration(a+b)); + else if (b > a) totals.setProperty(node, MAUtils.getDuration(b)); + } + + /** + * Get total kills from an arena session + * @param log The ArenaLog to count kills in + * @return Total kills + */ + private static int countKills(ArenaLog log) + { + int kills = 0; + for (ArenaPlayer ap : log.players.values()) + kills += ap.kills; + return kills; + } + + /** + * Get a (dirty) int-array in the form {kills, damage done, damage taken} for a class + * @param log ArenaLog to count kills, damage done/taken from + * @param className The class to count for + * @return Resulting kills, damage done and damage taken in an int-array + */ + private static int[] getKillsAndDamageByClass(ArenaLog log, String className) + { + int kills = 0, dmgDone = 0, dmgTaken = 0; + for (ArenaPlayer ap : log.players.values()) + { + if (!ap.className.equals(className)) + continue; + kills += ap.kills; + dmgDone += ap.dmgDone; + dmgTaken += ap.dmgTaken; + } + return new int[]{kills, dmgDone, dmgTaken}; + } +} diff --git a/src/com/garbagemule/MobArena/util/data/XML.java b/src/com/garbagemule/MobArena/util/data/XML.java new file mode 100644 index 0000000..2ca86ac --- /dev/null +++ b/src/com/garbagemule/MobArena/util/data/XML.java @@ -0,0 +1,182 @@ +package com.garbagemule.MobArena.util.data; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.List; +import java.util.Map; + +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.config.Configuration; +import org.jdom.Attribute; +import org.jdom.Document; +import org.jdom.Element; +//import org.jdom.Namespace; +import org.jdom.output.XMLOutputter; + +import com.garbagemule.MobArena.ArenaLog; +import com.garbagemule.MobArena.ArenaPlayer; +import com.garbagemule.MobArena.MobArena; + +public class XML +{ + public static void saveSessionData(ArenaLog log) + { + File dir = new File(MobArena.arenaDir, log.getArena().configName()); + if (!dir.exists()) dir.mkdirs(); + + // It's here if people want it. + //Namespace w = Namespace.getNamespace("com.garbagemule.MobArena"); + + // General info + Element ge = new Element("general-info"); + ge.addContent(new Element("start-time").addContent(log.getStartTime().toString())); + ge.addContent(new Element("end-time").addContent(log.getEndTime().toString())); + ge.addContent(new Element("duration").addContent(log.getDuration().toString())); + ge.addContent(new Element("last-wave").addContent(log.getLastWave() + "")); + + // Class distribution + Element cd = new Element("class-distribution"); + for (Map.Entry entry : log.distribution.entrySet()) + cd.addContent(new Element(entry.getKey()).addContent(entry.getValue() + "")); + + // Player data + Element pd = new Element("player-data"); + for (Map.Entry entry : log.players.entrySet()) + { + // Name as attribute + Element p = new Element("player").setAttribute(new Attribute("name", entry.getKey().getName())); + ArenaPlayer ap = entry.getValue(); + + p.addContent(new Element("last-wave").addContent(ap.lastWave + "")); + p.addContent(new Element("kills").addContent(ap.lastWave + "")); + p.addContent(new Element("damage-done").addContent(ap.dmgDone + "")); + p.addContent(new Element("damage-taken").addContent(ap.dmgTaken + "")); + p.addContent(new Element("swings").addContent(ap.swings + "")); + p.addContent(new Element("hits").addContent(ap.hits + "")); + + // Rewards + Element rw = new Element("rewards"); + for (ItemStack stack : ap.rewards) + rw.addContent(getReward(stack)); + + p.addContent(rw); + pd.addContent(p); + } + + // Add the main nodes + Element root = new Element("last-session"); + root.addContent(ge); + root.addContent(cd); + root.addContent(pd); + + // Create a new document + Document doc = new Document(); + doc.addContent(root); + + // Serialize! + serialize(log, dir, doc, "lastsession.xml"); + } + + public static void updateArenaTotals(ArenaLog log) + { + Configuration totals = Totals.getArenaTotals(log.arena); + totals.load(); + + // General data + Element gd = new Element("general-info"); + gd.addContent(new Element("total-games-played").addContent(totals.getInt("general-info.total-games-played", 0) + "")); + gd.addContent(new Element("most-players").addContent(totals.getInt("general-info.most-players", 0) + "")); + gd.addContent(new Element("highest-wave-reached").addContent(totals.getInt("general-info.highest-wave-reached", 0) + "")); + gd.addContent(new Element("total-monsters-killed").addContent(totals.getInt("general-info.total-monsters-killed", 0) + "")); + gd.addContent(new Element("total-duration").addContent(totals.getString("general-info.total-duration", "0:00:00"))); + gd.addContent(new Element("longest-session-duration").addContent(totals.getString("general-info.longest-session-duration", "0:00:00"))); + + // Classes + Element cl = new Element("classes"); + for (String c : log.arena.getClasses()) + { + Element e = new Element("class").setAttribute(new Attribute("name", c)); + e.addContent(new Element("kills").addContent(totals.getInt("classes." + c + ".kills", 0) + "")); + e.addContent(new Element("damage-done").addContent(totals.getInt("classes." + c + ".damage-done", 0) + "")); + e.addContent(new Element("damage-taken").addContent(totals.getInt("classes." + c + ".damage-taken", 0) + "")); + cl.addContent(e); + } + + // Rewards + Element rw = new Element("rewards"); + for (ArenaPlayer ap : log.players.values()) + for (ItemStack stack : ap.rewards) + rw.addContent(getReward(stack)); + + // Players + Element pl = new Element("players"); + for (ArenaPlayer ap : log.players.values()) + { + String name = ap.player.getName(); + Element p = new Element("player").setAttribute(new Attribute("name", name)); + p.addContent(new Element("games-played").addContent(totals.getInt("players." + name + ".games-played", 1) + "")); + p.addContent(new Element("kills").addContent(totals.getInt("players." + name + ".kills", 1) + "")); + p.addContent(new Element("damage-done").addContent(totals.getInt("players." + name + ".damage-done", 1) + "")); + p.addContent(new Element("damage-taken").addContent(totals.getInt("players." + name + ".damage-taken", 1) + "")); + p.addContent(new Element("swings").addContent(totals.getInt("players." + name + ".swings", 1) + "")); + p.addContent(new Element("hits").addContent(totals.getInt("players." + name + ".hits", 1) + "")); + + List classes = totals.getKeys("players." + name + ".classes"); + Element pcl = new Element("classes"); + if (classes != null) + { + for (String c : classes) + pcl.addContent(new Element("class").setAttribute(new Attribute("name", c)).addContent(totals.getInt("players." + name + ".classes." + c, 0) + "")); + } + + p.addContent(pcl); + pl.addContent(p); + } + + // Add the main nodes + Element root = new Element("last-session"); + root.addContent(gd); + root.addContent(cl); + root.addContent(rw); + root.addContent(pl); + + // Create a new document + Document doc = new Document(); + doc.addContent(root); + + // Serialize! + File dir = new File(MobArena.arenaDir, log.getArena().configName()); + if (!dir.exists()) dir.mkdirs(); + serialize(log, dir, doc, "totals.xml"); + } + + private static Element getReward(ItemStack stack) + { + boolean money = stack.getTypeId() == MobArena.ECONOMY_MONEY_ID; + + Element result = new Element("reward"); + result.setAttribute(new Attribute("id", stack.getTypeId() + "")); + result.setAttribute(new Attribute("material", money ? "money" : stack.getType().toString().toLowerCase())); + result.setAttribute(new Attribute("data", money || stack.getData() == null ? "0" : stack.getData().toString().toLowerCase())); + result.setAttribute(new Attribute("amount", stack.getAmount() + "")); + + return result; + } + + private static void serialize(ArenaLog log, File dir, Document doc, String filename) + { + try + { + File file = new File(dir, filename); + FileOutputStream fop = new FileOutputStream(file); + XMLOutputter out = new XMLOutputter(); + out.output(doc, fop); + } + catch (Exception e) + { + e.printStackTrace(); + MobArena.warning("Problem saving session data for arena '" + log.getArena().configName() + "'"); + } + } +} diff --git a/src/com/garbagemule/MobArena/util/data/YAML.java b/src/com/garbagemule/MobArena/util/data/YAML.java new file mode 100644 index 0000000..7f04417 --- /dev/null +++ b/src/com/garbagemule/MobArena/util/data/YAML.java @@ -0,0 +1,63 @@ +package com.garbagemule.MobArena.util.data; + +import java.io.File; +import java.util.Map; + +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.config.Configuration; + +import com.garbagemule.MobArena.ArenaLog; +import com.garbagemule.MobArena.ArenaPlayer; +import com.garbagemule.MobArena.MobArena; + +public class YAML +{ + public static void saveSessionData(ArenaLog log) + { + File dir = new File(MobArena.arenaDir, log.getArena().configName()); + if (!dir.exists()) dir.mkdirs(); + + // Make Configuration object + Configuration config = new Configuration(new File(dir, "lastsession.yml")); + config.load(); + + // Reset any existing data + config.setProperty("general-info", null); + config.setProperty("class-distribution", null); + config.setProperty("player-data", null); + + // General information + config.setProperty("general-info.start-time", log.getStartTime()); + config.setProperty("general-info.end-time", log.getEndTime()); + config.setProperty("general-info.duration", log.getDuration()); + config.setProperty("general-info.last-wave", log.getLastWave()); + + // Class distribution + for (Map.Entry entry : log.distribution.entrySet()) + config.setProperty("class-distribution." + entry.getKey(), entry.getValue()); + + // Player data + for (Map.Entry entry : log.players.entrySet()) + { + String p = entry.getKey().getName(); + ArenaPlayer ap = entry.getValue(); + + config.setProperty("player-data." + p + ".last-wave", ap.lastWave); + config.setProperty("player-data." + p + ".kills", ap.kills); + config.setProperty("player-data." + p + ".damage-done", ap.dmgDone); + config.setProperty("player-data." + p + ".damage-taken", ap.dmgTaken); + config.setProperty("player-data." + p + ".swings", ap.swings); + config.setProperty("player-data." + p + ".hits", ap.hits); + for (ItemStack stack : ap.rewards) + { + boolean money = stack.getTypeId() == MobArena.ECONOMY_MONEY_ID; + String path = "player-data." + p + ".rewards." + (money ? "money" : stack.getType().toString().toLowerCase()); + config.setProperty(path, config.getInt(path, 0) + stack.getAmount()); + } + } + + // Save the file + config.save(); + } +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/ByteArrayTag.java b/src/com/garbagemule/MobArena/util/jnbt/ByteArrayTag.java new file mode 100644 index 0000000..219d1a4 --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/ByteArrayTag.java @@ -0,0 +1,81 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * The TAG_Byte_Array tag. + * @author Graham Edgecombe + * + */ +public final class ByteArrayTag extends Tag { + + /** + * The value. + */ + private final byte[] value; + + /** + * Creates the tag. + * @param name The name. + * @param value The value. + */ + public ByteArrayTag(String name, byte[] value) { + super(name); + this.value = value; + } + + @Override + public byte[] getValue() { + return value; + } + + @Override + public String toString() { + StringBuilder hex = new StringBuilder(); + for(byte b : value) { + String hexDigits = Integer.toHexString(b).toUpperCase(); + if(hexDigits.length() == 1) { + hex.append("0"); + } + hex.append(hexDigits).append(" "); + } + String name = getName(); + String append = ""; + if(name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Byte_Array" + append + ": " + hex.toString(); + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/ByteTag.java b/src/com/garbagemule/MobArena/util/jnbt/ByteTag.java new file mode 100644 index 0000000..b03463d --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/ByteTag.java @@ -0,0 +1,73 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * The TAG_Byte tag. + * @author Graham Edgecombe + * + */ +public final class ByteTag extends Tag { + + /** + * The value. + */ + private final byte value; + + /** + * Creates the tag. + * @param name The name. + * @param value The value. + */ + public ByteTag(String name, byte value) { + super(name); + this.value = value; + } + + @Override + public Byte getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if(name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Byte" + append + ": " + value; + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/CompoundTag.java b/src/com/garbagemule/MobArena/util/jnbt/CompoundTag.java new file mode 100644 index 0000000..3b59a9c --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/CompoundTag.java @@ -0,0 +1,82 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +import java.util.Collections; +import java.util.Map; + +/** + * The TAG_Compound tag. + * @author Graham Edgecombe + * + */ +public final class CompoundTag extends Tag { + + /** + * The value. + */ + private final Map value; + + /** + * Creates the tag. + * @param name The name. + * @param value The value. + */ + public CompoundTag(String name, Map value) { + super(name); + this.value = Collections.unmodifiableMap(value); + } + + @Override + public Map getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if(name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + StringBuilder bldr = new StringBuilder(); + bldr.append("TAG_Compound" + append + ": " + value.size() + " entries\r\n{\r\n"); + for(Map.Entry entry : value.entrySet()) { + bldr.append(" " + entry.getValue().toString().replaceAll("\r\n", "\r\n ") + "\r\n"); + } + bldr.append("}"); + return bldr.toString(); + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/DoubleTag.java b/src/com/garbagemule/MobArena/util/jnbt/DoubleTag.java new file mode 100644 index 0000000..f46dfc3 --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/DoubleTag.java @@ -0,0 +1,73 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * The TAG_Double tag. + * @author Graham Edgecombe + * + */ +public final class DoubleTag extends Tag { + + /** + * The value. + */ + private final double value; + + /** + * Creates the tag. + * @param name The name. + * @param value The value. + */ + public DoubleTag(String name, double value) { + super(name); + this.value = value; + } + + @Override + public Double getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if(name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Double" + append + ": " + value; + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/EndTag.java b/src/com/garbagemule/MobArena/util/jnbt/EndTag.java new file mode 100644 index 0000000..e9ff5fe --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/EndTag.java @@ -0,0 +1,60 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * The TAG_End tag. + * @author Graham Edgecombe + * + */ +public final class EndTag extends Tag { + + /** + * Creates the tag. + */ + public EndTag() { + super(""); + } + + @Override + public Object getValue() { + return null; + } + + @Override + public String toString() { + return "TAG_End"; + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/FloatTag.java b/src/com/garbagemule/MobArena/util/jnbt/FloatTag.java new file mode 100644 index 0000000..7ff0ad5 --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/FloatTag.java @@ -0,0 +1,73 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * The TAG_Float tag. + * @author Graham Edgecombe + * + */ +public final class FloatTag extends Tag { + + /** + * The value. + */ + private final float value; + + /** + * Creates the tag. + * @param name The name. + * @param value The value. + */ + public FloatTag(String name, float value) { + super(name); + this.value = value; + } + + @Override + public Float getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if(name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Float" + append + ": " + value; + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/IntTag.java b/src/com/garbagemule/MobArena/util/jnbt/IntTag.java new file mode 100644 index 0000000..f728a43 --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/IntTag.java @@ -0,0 +1,73 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * The TAG_Int tag. + * @author Graham Edgecombe + * + */ +public final class IntTag extends Tag { + + /** + * The value. + */ + private final int value; + + /** + * Creates the tag. + * @param name The name. + * @param value The value. + */ + public IntTag(String name, int value) { + super(name); + this.value = value; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if(name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Int" + append + ": " + value; + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/ListTag.java b/src/com/garbagemule/MobArena/util/jnbt/ListTag.java new file mode 100644 index 0000000..e5b6a18 --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/ListTag.java @@ -0,0 +1,97 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +import java.util.Collections; +import java.util.List; + +/** + * The TAG_List tag. + * @author Graham Edgecombe + * + */ +public final class ListTag extends Tag { + + /** + * The type. + */ + private final Class type; + + /** + * The value. + */ + private final List value; + + /** + * Creates the tag. + * @param name The name. + * @param type The type of item in the list. + * @param value The value. + */ + public ListTag(String name, Class type, List value) { + super(name); + this.type = type; + this.value = Collections.unmodifiableList(value); + } + + /** + * Gets the type of item in this list. + * @return The type of item in this list. + */ + public Class getType() { + return type; + } + + @Override + public List getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if(name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + StringBuilder bldr = new StringBuilder(); + bldr.append("TAG_List" + append + ": " + value.size() + " entries of type " + NBTUtils.getTypeName(type) + "\r\n{\r\n"); + for(Tag t : value) { + bldr.append(" " + t.toString().replaceAll("\r\n", "\r\n ") + "\r\n"); + } + bldr.append("}"); + return bldr.toString(); + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/LongTag.java b/src/com/garbagemule/MobArena/util/jnbt/LongTag.java new file mode 100644 index 0000000..14aa856 --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/LongTag.java @@ -0,0 +1,73 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * The TAG_Long tag. + * @author Graham Edgecombe + * + */ +public final class LongTag extends Tag { + + /** + * The value. + */ + private final long value; + + /** + * Creates the tag. + * @param name The name. + * @param value The value. + */ + public LongTag(String name, long value) { + super(name); + this.value = value; + } + + @Override + public Long getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if(name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Long" + append + ": " + value; + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/NBTConstants.java b/src/com/garbagemule/MobArena/util/jnbt/NBTConstants.java new file mode 100644 index 0000000..b03c11c --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/NBTConstants.java @@ -0,0 +1,72 @@ +package com.garbagemule.MobArena.util.jnbt; + +import java.nio.charset.Charset; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * A class which holds constant values. + * @author Graham Edgecombe + * + */ +public final class NBTConstants { + + /** + * The character set used by NBT (UTF-8). + */ + public static final Charset CHARSET = Charset.forName("UTF-8"); + + /** + * Tag type constants. + */ + public static final int TYPE_END = 0, + TYPE_BYTE = 1, + TYPE_SHORT = 2, + TYPE_INT = 3, + TYPE_LONG = 4, + TYPE_FLOAT = 5, + TYPE_DOUBLE = 6, + TYPE_BYTE_ARRAY = 7, + TYPE_STRING = 8, + TYPE_LIST = 9, + TYPE_COMPOUND = 10; + + /** + * Default private constructor. + */ + private NBTConstants() { + + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/NBTInputStream.java b/src/com/garbagemule/MobArena/util/jnbt/NBTInputStream.java new file mode 100644 index 0000000..6446dde --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/NBTInputStream.java @@ -0,0 +1,179 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +import java.io.Closeable; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +/** + *

This class reads NBT, or + * Named Binary Tag streams, and produces an object graph of + * subclasses of the Tag object.

+ * + *

The NBT format was created by Markus Persson, and the specification may + * be found at + * http://www.minecraft.net/docs/NBT.txt.

+ * @author Graham Edgecombe + * + */ +public final class NBTInputStream implements Closeable { + + /** + * The data input stream. + */ + private final DataInputStream is; + + /** + * Creates a new NBTInputStream, which will source its data + * from the specified input stream. + * @param is The input stream. + * @throws IOException if an I/O error occurs. + */ + public NBTInputStream(InputStream is) throws IOException { + this.is = new DataInputStream(new GZIPInputStream(is)); + } + + /** + * Reads an NBT tag from the stream. + * @return The tag that was read. + * @throws IOException if an I/O error occurs. + */ + public Tag readTag() throws IOException { + return readTag(0); + } + + /** + * Reads an NBT from the stream. + * @param depth The depth of this tag. + * @return The tag that was read. + * @throws IOException if an I/O error occurs. + */ + private Tag readTag(int depth) throws IOException { + int type = is.readByte() & 0xFF; + + String name; + if(type != NBTConstants.TYPE_END) { + int nameLength = is.readShort() & 0xFFFF; + byte[] nameBytes = new byte[nameLength]; + is.readFully(nameBytes); + name = new String(nameBytes, NBTConstants.CHARSET); + } else { + name = ""; + } + + return readTagPayload(type, name, depth); + } + + /** + * Reads the payload of a tag, given the name and type. + * @param type The type. + * @param name The name. + * @param depth The depth. + * @return The tag. + * @throws IOException if an I/O error occurs. + */ + private Tag readTagPayload(int type, String name, int depth) throws IOException { + switch(type) { + case NBTConstants.TYPE_END: + if(depth == 0) { + throw new IOException("TAG_End found without a TAG_Compound/TAG_List tag preceding it."); + } else { + return new EndTag(); + } + case NBTConstants.TYPE_BYTE: + return new ByteTag(name, is.readByte()); + case NBTConstants.TYPE_SHORT: + return new ShortTag(name, is.readShort()); + case NBTConstants.TYPE_INT: + return new IntTag(name, is.readInt()); + case NBTConstants.TYPE_LONG: + return new LongTag(name, is.readLong()); + case NBTConstants.TYPE_FLOAT: + return new FloatTag(name, is.readFloat()); + case NBTConstants.TYPE_DOUBLE: + return new DoubleTag(name, is.readDouble()); + case NBTConstants.TYPE_BYTE_ARRAY: + int length = is.readInt(); + byte[] bytes = new byte[length]; + is.readFully(bytes); + return new ByteArrayTag(name, bytes); + case NBTConstants.TYPE_STRING: + length = is.readShort(); + bytes = new byte[length]; + is.readFully(bytes); + return new StringTag(name, new String(bytes, NBTConstants.CHARSET)); + case NBTConstants.TYPE_LIST: + int childType = is.readByte(); + length = is.readInt(); + + List tagList = new ArrayList(); + for(int i = 0; i < length; i++) { + Tag tag = readTagPayload(childType, "", depth + 1); + if(tag instanceof EndTag) { + throw new IOException("TAG_End not permitted in a list."); + } + tagList.add(tag); + } + + return new ListTag(name, NBTUtils.getTypeClass(childType), tagList); + case NBTConstants.TYPE_COMPOUND: + Map tagMap = new HashMap(); + while(true) { + Tag tag = readTag(depth + 1); + if(tag instanceof EndTag) { + break; + } else { + tagMap.put(tag.getName(), tag); + } + } + + return new CompoundTag(name, tagMap); + default: + throw new IOException("Invalid tag type: " + type + "."); + } + } + + @Override + public void close() throws IOException { + is.close(); + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/NBTOutputStream.java b/src/com/garbagemule/MobArena/util/jnbt/NBTOutputStream.java new file mode 100644 index 0000000..7ddbf27 --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/NBTOutputStream.java @@ -0,0 +1,257 @@ +package com.garbagemule.MobArena.util.jnbt; + +import java.io.Closeable; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.zip.GZIPOutputStream; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + *

This class writes NBT, or + * Named Binary Tag Tag objects to an underlying + * OutputStream.

+ * + *

The NBT format was created by Markus Persson, and the specification may + * be found at + * http://www.minecraft.net/docs/NBT.txt.

+ * @author Graham Edgecombe + * + */ +public final class NBTOutputStream implements Closeable { + + /** + * The output stream. + */ + private final DataOutputStream os; + + /** + * Creates a new NBTOutputStream, which will write data to the + * specified underlying output stream. + * @param os The output stream. + * @throws IOException if an I/O error occurs. + */ + public NBTOutputStream(OutputStream os) throws IOException { + this.os = new DataOutputStream(new GZIPOutputStream(os)); + } + + /** + * Writes a tag. + * @param tag The tag to write. + * @throws IOException if an I/O error occurs. + */ + public void writeTag(Tag tag) throws IOException { + int type = NBTUtils.getTypeCode(tag.getClass()); + String name = tag.getName(); + byte[] nameBytes = name.getBytes(NBTConstants.CHARSET); + + os.writeByte(type); + os.writeShort(nameBytes.length); + os.write(nameBytes); + + if(type == NBTConstants.TYPE_END) { + throw new IOException("Named TAG_End not permitted."); + } + + writeTagPayload(tag); + } + + /** + * Writes tag payload. + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeTagPayload(Tag tag) throws IOException { + int type = NBTUtils.getTypeCode(tag.getClass()); + switch(type) { + case NBTConstants.TYPE_END: + writeEndTagPayload((EndTag) tag); + break; + case NBTConstants.TYPE_BYTE: + writeByteTagPayload((ByteTag) tag); + break; + case NBTConstants.TYPE_SHORT: + writeShortTagPayload((ShortTag) tag); + break; + case NBTConstants.TYPE_INT: + writeIntTagPayload((IntTag) tag); + break; + case NBTConstants.TYPE_LONG: + writeLongTagPayload((LongTag) tag); + break; + case NBTConstants.TYPE_FLOAT: + writeFloatTagPayload((FloatTag) tag); + break; + case NBTConstants.TYPE_DOUBLE: + writeDoubleTagPayload((DoubleTag) tag); + break; + case NBTConstants.TYPE_BYTE_ARRAY: + writeByteArrayTagPayload((ByteArrayTag) tag); + break; + case NBTConstants.TYPE_STRING: + writeStringTagPayload((StringTag) tag); + break; + case NBTConstants.TYPE_LIST: + writeListTagPayload((ListTag) tag); + break; + case NBTConstants.TYPE_COMPOUND: + writeCompoundTagPayload((CompoundTag) tag); + break; + default: + throw new IOException("Invalid tag type: " + type + "."); + } + } + + /** + * Writes a TAG_Byte tag. + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeByteTagPayload(ByteTag tag) throws IOException { + os.writeByte(tag.getValue()); + } + + /** + * Writes a TAG_Byte_Array tag. + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeByteArrayTagPayload(ByteArrayTag tag) throws IOException { + byte[] bytes = tag.getValue(); + os.writeInt(bytes.length); + os.write(bytes); + } + + /** + * Writes a TAG_Compound tag. + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeCompoundTagPayload(CompoundTag tag) throws IOException { + for(Tag childTag : tag.getValue().values()) { + writeTag(childTag); + } + os.writeByte((byte) 0); // end tag - better way? + } + + /** + * Writes a TAG_List tag. + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeListTagPayload(ListTag tag) throws IOException { + Class clazz = tag.getType(); + List tags = tag.getValue(); + int size = tags.size(); + + os.writeByte(NBTUtils.getTypeCode(clazz)); + os.writeInt(size); + for(int i = 0; i < size; i++) { + writeTagPayload(tags.get(i)); + } + } + + /** + * Writes a TAG_String tag. + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeStringTagPayload(StringTag tag) throws IOException { + byte[] bytes = tag.getValue().getBytes(NBTConstants.CHARSET); + os.writeShort(bytes.length); + os.write(bytes); + } + + /** + * Writes a TAG_Double tag. + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeDoubleTagPayload(DoubleTag tag) throws IOException { + os.writeDouble(tag.getValue()); + } + + /** + * Writes a TAG_Float tag. + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeFloatTagPayload(FloatTag tag) throws IOException { + os.writeFloat(tag.getValue()); + } + + /** + * Writes a TAG_Long tag. + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeLongTagPayload(LongTag tag) throws IOException { + os.writeLong(tag.getValue()); + } + + /** + * Writes a TAG_Int tag. + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeIntTagPayload(IntTag tag) throws IOException { + os.writeInt(tag.getValue()); + } + + /** + * Writes a TAG_Short tag. + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeShortTagPayload(ShortTag tag) throws IOException { + os.writeShort(tag.getValue()); + } + + /** + * Writes a TAG_Empty tag. + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeEndTagPayload(EndTag tag) { + /* empty */ + } + + @Override + public void close() throws IOException { + os.close(); + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/NBTUtils.java b/src/com/garbagemule/MobArena/util/jnbt/NBTUtils.java new file mode 100644 index 0000000..ce97632 --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/NBTUtils.java @@ -0,0 +1,152 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * A class which contains NBT-related utility methods. + * @author Graham Edgecombe + * + */ +public final class NBTUtils { + + /** + * Gets the type name of a tag. + * @param clazz The tag class. + * @return The type name. + */ + public static String getTypeName(Class clazz) { + if(clazz.equals(ByteArrayTag.class)) { + return "TAG_Byte_Array"; + } else if(clazz.equals(ByteTag.class)) { + return "TAG_Byte"; + } else if(clazz.equals(CompoundTag.class)) { + return "TAG_Compound"; + } else if(clazz.equals(DoubleTag.class)) { + return "TAG_Double"; + } else if(clazz.equals(EndTag.class)) { + return "TAG_End"; + } else if(clazz.equals(FloatTag.class)) { + return "TAG_Float"; + } else if(clazz.equals(IntTag.class)) { + return "TAG_Int"; + } else if(clazz.equals(ListTag.class)) { + return "TAG_List"; + } else if(clazz.equals(LongTag.class)) { + return "TAG_Long"; + } else if(clazz.equals(ShortTag.class)) { + return "TAG_Short"; + } else if(clazz.equals(StringTag.class)) { + return "TAG_String"; + } else { + throw new IllegalArgumentException("Invalid tag classs (" + clazz.getName() + ")."); + } + } + + /** + * Gets the type code of a tag class. + * @param clazz The tag class. + * @return The type code. + * @throws IllegalArgumentException if the tag class is invalid. + */ + public static int getTypeCode(Class clazz) { + if(clazz.equals(ByteArrayTag.class)) { + return NBTConstants.TYPE_BYTE_ARRAY; + } else if(clazz.equals(ByteTag.class)) { + return NBTConstants.TYPE_BYTE; + } else if(clazz.equals(CompoundTag.class)) { + return NBTConstants.TYPE_COMPOUND; + } else if(clazz.equals(DoubleTag.class)) { + return NBTConstants.TYPE_DOUBLE; + } else if(clazz.equals(EndTag.class)) { + return NBTConstants.TYPE_END; + } else if(clazz.equals(FloatTag.class)) { + return NBTConstants.TYPE_FLOAT; + } else if(clazz.equals(IntTag.class)) { + return NBTConstants.TYPE_INT; + } else if(clazz.equals(ListTag.class)) { + return NBTConstants.TYPE_LIST; + } else if(clazz.equals(LongTag.class)) { + return NBTConstants.TYPE_LONG; + } else if(clazz.equals(ShortTag.class)) { + return NBTConstants.TYPE_SHORT; + } else if(clazz.equals(StringTag.class)) { + return NBTConstants.TYPE_STRING; + } else { + throw new IllegalArgumentException("Invalid tag classs (" + clazz.getName() + ")."); + } + } + + /** + * Gets the class of a type of tag. + * @param type The type. + * @return The class. + * @throws IllegalArgumentException if the tag type is invalid. + */ + public static Class getTypeClass(int type) { + switch(type) { + case NBTConstants.TYPE_END: + return EndTag.class; + case NBTConstants.TYPE_BYTE: + return ByteTag.class; + case NBTConstants.TYPE_SHORT: + return ShortTag.class; + case NBTConstants.TYPE_INT: + return IntTag.class; + case NBTConstants.TYPE_LONG: + return LongTag.class; + case NBTConstants.TYPE_FLOAT: + return FloatTag.class; + case NBTConstants.TYPE_DOUBLE: + return DoubleTag.class; + case NBTConstants.TYPE_BYTE_ARRAY: + return ByteArrayTag.class; + case NBTConstants.TYPE_STRING: + return StringTag.class; + case NBTConstants.TYPE_LIST: + return ListTag.class; + case NBTConstants.TYPE_COMPOUND: + return CompoundTag.class; + default: + throw new IllegalArgumentException("Invalid tag type : " + type + "."); + } + } + + /** + * Default private constructor. + */ + private NBTUtils() { + + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/ShortTag.java b/src/com/garbagemule/MobArena/util/jnbt/ShortTag.java new file mode 100644 index 0000000..d94386b --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/ShortTag.java @@ -0,0 +1,73 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * The TAG_Short tag. + * @author Graham Edgecombe + * + */ +public final class ShortTag extends Tag { + + /** + * The value. + */ + private final short value; + + /** + * Creates the tag. + * @param name The name. + * @param value The value. + */ + public ShortTag(String name, short value) { + super(name); + this.value = value; + } + + @Override + public Short getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if(name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_Short" + append + ": " + value; + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/StringTag.java b/src/com/garbagemule/MobArena/util/jnbt/StringTag.java new file mode 100644 index 0000000..a894c8b --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/StringTag.java @@ -0,0 +1,73 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * The TAG_String tag. + * @author Graham Edgecombe + * + */ +public final class StringTag extends Tag { + + /** + * The value. + */ + private final String value; + + /** + * Creates the tag. + * @param name The name. + * @param value The value. + */ + public StringTag(String name, String value) { + super(name); + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if(name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + return "TAG_String" + append + ": " + value; + } + +} diff --git a/src/com/garbagemule/MobArena/util/jnbt/Tag.java b/src/com/garbagemule/MobArena/util/jnbt/Tag.java new file mode 100644 index 0000000..19a5daf --- /dev/null +++ b/src/com/garbagemule/MobArena/util/jnbt/Tag.java @@ -0,0 +1,70 @@ +package com.garbagemule.MobArena.util.jnbt; + +/* + * JNBT License + * + * Copyright (c) 2010 Graham Edgecombe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the JNBT team nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Represents a single NBT tag. + * @author Graham Edgecombe + * + */ +public abstract class Tag { + + /** + * The name of this tag. + */ + private final String name; + + /** + * Creates the tag with the specified name. + * @param name The name. + */ + public Tag(String name) { + this.name = name; + } + + /** + * Gets the name of this tag. + * @return The name of this tag. + */ + public final String getName() { + return name; + } + + /** + * Gets the value of this tag. + * @return The value of this tag. + */ + public abstract Object getValue(); + +} diff --git a/src/com/garbagemule/MobArena/waves/AbstractWave.java b/src/com/garbagemule/MobArena/waves/AbstractWave.java new file mode 100644 index 0000000..9942bc2 --- /dev/null +++ b/src/com/garbagemule/MobArena/waves/AbstractWave.java @@ -0,0 +1,199 @@ +package com.garbagemule.MobArena.waves; + +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Creature; +import org.bukkit.entity.LivingEntity; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.util.WaveUtils; + +public abstract class AbstractWave implements Wave +{ + private Arena arena; + private World world; + private String waveName; + private int wave, frequency, priority; + private WaveBranch branch; + private WaveType type; + private WaveGrowth growth; + + /** + * Basic wave constructor. + * @param name The config-file identifier + * @param wave Initial wave number. This is the first wave number this wave can spawn at. + * @param frequency How often the wave can spawn. + * @param priority The priority of the wave. + * @param branch The branch type (single, recurrent) + */ + public AbstractWave(Arena arena, String waveName, int wave, int frequency, int priority, WaveBranch branch) + { + this.arena = arena; + this.world = arena.getWorld(); + this.waveName = waveName; + this.wave = wave; + this.frequency = frequency; + this.priority = priority; + this.branch = branch; + } + + // Default recurrent wave constructor + public AbstractWave(Arena arena, String name, int wave, int frequency, int priority) + { + this(arena, name, wave, frequency, priority, WaveBranch.RECURRENT); + } + + // Default single wave constructor + public AbstractWave(Arena arena, String name, int wave) + { + this(arena, name, wave, 0, 0, WaveBranch.SINGLE); + } + + /** + * Check if a wave matches a wave number. + * SINGLE WAVES match, if their wave number is the same as the + * parameter. + * RECURRENT WAVES match, if their wave number subtracted from + * the parameter divides the frequency. The wave number must be + * greater than or equal to the parameter. + * @param wave The wave number to compare + * @return true, if the wave matches the wave number + */ + public boolean matches(int wave) + { + if (branch == WaveBranch.SINGLE) + return this.wave == wave; + + if (branch == WaveBranch.RECURRENT && wave >= this.wave) + return ((wave - this.wave) % frequency == 0); + + return false; + } + + /** + * Spawn an MACreature in the given location. The actual LivingEntity + * spawned is returned. + * @param creature The MACreature to spawn + * @param loc The location to spawn the MACreature in + * @return The resulting LivingEntity + */ + public LivingEntity spawnMonster(MACreature creature, Location loc) + { + // Spawn and add to collection + LivingEntity e = creature.spawn(getArena(), getWorld(), loc); + getArena().addMonster(e); + + // Grab a random target. + if (e instanceof Creature) + { + Creature c = (Creature) e; + c.setTarget(WaveUtils.getClosestPlayer(getArena(), e)); + } + + return e; + } + + /** + * Helper method for spawning a bunch of monsters over a + * collection of spawnpoints. Used by wave types DEFAULT, + * SPECIAL and SWARM. + * @param monsters A collection of monsters to spawn, and how many of each + * @param spawnpoints The spawnpoints to spawn the monsters over + */ + public void spawnAll(Map monsters, List spawnpoints) + { + Random random = new Random(); + int spawnpointCount = spawnpoints.size(); + int index = random.nextInt(spawnpointCount); + + for (Map.Entry entry : monsters.entrySet()) + { + for (int i = 0; i < entry.getValue(); i++) + { + spawnMonster(entry.getKey(), spawnpoints.get(index % spawnpointCount)); + index++; + } + } + } + + // GETTERS + public Arena getArena() + { + return arena; + } + + public World getWorld() + { + return world; + } + + public WaveBranch getBranch() + { + return branch; + } + + public WaveType getType() + { + return type; + } + + public WaveGrowth getGrowth() + { + return growth; + } + + public int getWave() + { + return wave; + } + + public int getFrequency() + { + return frequency; + } + + public int getPriority() + { + return priority; + } + + public String getName() + { + return waveName; + } + + // SETTERS + public void setBranch(WaveBranch branch) + { + this.branch = branch; + } + + public void setType(WaveType type) + { + this.type = type; + } + + public void setGrowth(WaveGrowth growth) + { + this.growth = growth; + } + + // MISC + public String toString() + { + if (branch == WaveBranch.RECURRENT) + return "[Wave type=" + type + + " name=" + waveName + + " branch=" + branch.toString().charAt(0) + + " freq=" + frequency + + " prio=" + priority + "]"; + return "[Wave type=" + type + + " name=" + waveName + + " branch=" + branch.toString().charAt(0) + + " wave=" + wave + "]"; + } +} diff --git a/src/com/garbagemule/MobArena/waves/BossAbility.java b/src/com/garbagemule/MobArena/waves/BossAbility.java new file mode 100644 index 0000000..6e66b2b --- /dev/null +++ b/src/com/garbagemule/MobArena/waves/BossAbility.java @@ -0,0 +1,316 @@ +package com.garbagemule.MobArena.waves; + +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Creature; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Fireball; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.util.WaveUtils; + + +public enum BossAbility +{ + ARROWS("Arrow") + { + public void run(Arena arena, LivingEntity boss) + { + boss.shootArrow(); + } + }, + FIREBALLS("Fireball") + { + public void run(Arena arena, LivingEntity boss) + { + Location bLoc = boss.getLocation(); + Location loc = bLoc.add(bLoc.getDirection().normalize().multiply(2).toLocation(boss.getWorld(), bLoc.getYaw(), bLoc.getPitch())); + Fireball fireball = boss.getWorld().spawn(loc, Fireball.class); + fireball.setIsIncendiary(false); + } + }, + FIREAURA("Fire Aura") + { + public void run(Arena arena, LivingEntity boss) + { + for (Player p : getNearbyPlayers(arena, boss, 5)) + p.setFireTicks(20); + } + }, + LIGHTNINGAURA("Lightning Aura") + { + public void run(Arena arena, LivingEntity boss) + { + Location base = boss.getLocation(); + Location ne = base.getBlock().getRelative( 2, 0, 2).getLocation(); + Location nw = base.getBlock().getRelative(-2, 0, 2).getLocation(); + Location se = base.getBlock().getRelative( 2, 0, -2).getLocation(); + Location sw = base.getBlock().getRelative(-2, 0, -2).getLocation(); + + arena.getWorld().strikeLightning(ne); + arena.getWorld().strikeLightning(nw); + arena.getWorld().strikeLightning(se); + arena.getWorld().strikeLightning(sw); + } + }, + DISORIENTTARGET("Disorient Target") + { + public void run(Arena arena, LivingEntity boss) + { + LivingEntity target = getTarget(boss); + if (target == null) return; + + Location loc = target.getLocation(); + loc.setYaw(target.getLocation().getYaw() + 45 + (new Random()).nextInt(270)); + target.teleport(loc); + } + }, + ROOTTARGET("Root Target") + { + public void run(final Arena arena, LivingEntity boss) + { + final LivingEntity target = getTarget(boss); + if (target == null) return; + + final Location loc = target.getLocation(); + final int freezeTaskId = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(arena.getPlugin(), + new Runnable() + { + public void run() + { + if (arena.getLivingPlayers().contains(target)) + target.teleport(loc); + } + }, 3, 3); + + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(arena.getPlugin(), + new Runnable() + { + public void run() + { + Bukkit.getServer().getScheduler().cancelTask(freezeTaskId); + } + }, 45); + } + }, + LIVINGBOMB("Living Bomb") + { + public void run(final Arena arena, LivingEntity boss) + { + final LivingEntity target = getTarget(boss); + if (target == null) return; + + // Set the target on fire + target.setFireTicks(60); + + // Create an explosion after 3 seconds + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(arena.getPlugin(), + new Runnable() + { + public void run() + { + if (!arena.getLivingPlayers().contains(target)) + return; + + arena.getWorld().createExplosion(target.getLocation(), 2F); + for(Player p : getNearbyPlayers(arena, target, 3)) + p.setFireTicks(40); + } + }, 61); + } + }, + CHAINLIGHTNING("Chain Lightning") + { + public void run(Arena arena, LivingEntity boss) + { + final LivingEntity target = getTarget(boss); + if (target == null) return; + + strikeLightning(arena, (Player) target, new LinkedList()); + } + + private void strikeLightning(final Arena arena, final Player p, final List done) + { + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(arena.getPlugin(), + new Runnable() + { + public void run() + { + if (!arena.getLivingPlayers().contains(p)) + return; + + // Smite the target + arena.getWorld().strikeLightning(p.getLocation()); + done.add(p); + + // Grab all nearby players + List nearby = getNearbyPlayers(arena, p, 4); + + // Remove all that are "done", and return if empty + nearby.removeAll(done); + if (nearby.isEmpty()) return; + + // Otherwise, smite the next target! + strikeLightning(arena, nearby.get(0), done); + } + }, 8); + } + }, + WARPTOPLAYER("Warp") + { + public void run(Arena arena, LivingEntity boss) + { + List list = arena.getLivingPlayers(); + boss.teleport(list.get((new Random()).nextInt(list.size()))); + } + }, + THROWTARGET("Throw Target") + { + public void run(Arena arena, LivingEntity boss) + { + LivingEntity target = getTarget(boss); + if (target == null) return; + + Location bLoc = boss.getLocation(); + Location loc = target.getLocation(); + Vector v = new Vector(loc.getX() - bLoc.getX(), 0, loc.getZ() - bLoc.getZ()); + target.setVelocity(v.normalize().setY(0.8)); + } + }, + THROWNEARBY("Throw Nearby Players") + { + public void run(Arena arena, LivingEntity boss) + { + for (Player p : getNearbyPlayers(arena, boss, 5)) + { + Location bLoc = boss.getLocation(); + Location loc = p.getLocation(); + Vector v = new Vector(loc.getX() - bLoc.getX(), 0, loc.getZ() - bLoc.getZ()); + p.setVelocity(v.normalize().setY(0.8)); + } + } + }, + THROWDISTANT("Throw Distant Players") + { + public void run(Arena arena, LivingEntity boss) + { + for (Player p : getDistantPlayers(arena, boss, 8)) + { + Location bLoc = boss.getLocation(); + Location loc = p.getLocation(); + Vector v = new Vector(loc.getX() - bLoc.getX(), 0, loc.getZ() - bLoc.getZ()); + p.setVelocity(v.normalize().setY(0.8)); + } + } + }, + FETCHTARGET("Fetch Target") + { + public void run(Arena arena, LivingEntity boss) + { + LivingEntity target = getTarget(boss); + if (target != null) target.teleport(boss); + } + }, + FETCHNEARBY("Fetch Nearby Players") + { + public void run(Arena arena, LivingEntity boss) + { + for (Player p : getNearbyPlayers(arena, boss, 5)) + p.teleport(boss); + } + }, + FETCHDISTANT("Fetch Distant Players") + { + public void run(Arena arena, LivingEntity boss) + { + for (Player p : getDistantPlayers(arena, boss, 8)) + p.teleport(boss); + } + }; + + private String name; + + private BossAbility(String name) + { + this.name = name; + } + + /** + * The run-method that all boss abilities must define. + * The method is called in the ability cycle for the given boss. + * @param arena The Arena the boss is in + * @param boss The boss entity + */ + public abstract void run(Arena arena, LivingEntity boss); + + /** + * Get the target player of the LivingEntity if possible. + * @param entity The entity whose target to get + * @return The target player, or null + */ + protected LivingEntity getTarget(LivingEntity entity) + { + if (entity instanceof Creature) + { + LivingEntity target = null; + try + { + target = ((Creature) entity).getTarget(); + } + catch (Exception e) {} + + if (target instanceof Player) + return target; + } + return null; + } + + /** + * Get a list of nearby players + * @param arena The arena + * @param boss The boss + * @param x The 'radius' in which to grab players + * @return A list of nearby players + */ + protected List getNearbyPlayers(Arena arena, Entity boss, int x) + { + List result = new LinkedList(); + for (Entity e : boss.getNearbyEntities(x, x, x)) + if (arena.getLivingPlayers().contains(e)) + result.add((Player) e); + return result; + } + + /** + * Get a list of distant players + * @param arena The arena + * @param boss The boss + * @param x The 'radius' in which to exclude players + * @return A list of distant players + */ + protected List getDistantPlayers(Arena arena, Entity boss, int x) + { + List result = new LinkedList(); + for (Player p : arena.getLivingPlayers()) + if (p.getLocation().distanceSquared(boss.getLocation()) > x*x) + result.add(p); + return result; + } + + public static BossAbility fromString(String string) + { + return WaveUtils.getEnumFromString(BossAbility.class, string); + } + + public String toString() + { + return name; + } +} diff --git a/src/com/garbagemule/MobArena/waves/BossWave.java b/src/com/garbagemule/MobArena/waves/BossWave.java new file mode 100644 index 0000000..2034d83 --- /dev/null +++ b/src/com/garbagemule/MobArena/waves/BossWave.java @@ -0,0 +1,179 @@ +package com.garbagemule.MobArena.waves; + +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import net.minecraft.server.WorldServer; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.entity.Creature; +import org.bukkit.entity.LivingEntity; +import org.bukkit.util.config.Configuration; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.MAUtils; +import com.garbagemule.MobArena.MAMessages.Msg; +import com.garbagemule.MobArena.util.WaveUtils; + +public class BossWave extends AbstractWave +{ + private MACreature boss; + private LivingEntity bossCreature; + private List abilities; + private Set adds; + private BossHealth bossHealth; + private int healthAmount, abilityTask, abilityInterval; + private boolean lowHealthAnnounced = false, abilityAnnounce; + + // Recurrent + public BossWave(Arena arena, String name, int wave, int frequency, int priority, Configuration config, String path) + { + super(arena, name, wave, frequency, priority); + load(config, path); + } + + // Single + public BossWave(Arena arena, String name, int wave, Configuration config, String path) + { + super(arena, name, wave); + load(config, path); + } + + private void load(Configuration config, String path) + { + setType(WaveType.BOSS); + abilityTask = -1; + abilities = new LinkedList(); + + // Get monster and health + boss = MACreature.fromString(config.getString(path + "monster")); + bossHealth = WaveUtils.getEnumFromString(BossHealth.class, config.getString(path + "health"), BossHealth.MEDIUM); + + // Get abilities + abilityInterval = config.getInt(path + "ability-interval", 3) * 20; + abilityAnnounce = config.getBoolean(path + "ability-announce", true); + String abilities = config.getString(path + "abilities"); + if (abilities != null) + { + for (String a : abilities.split(",")) + { + String ability = a.trim().replaceAll("[-_\\.]", "").toUpperCase(); + addAbility(BossAbility.fromString(ability)); + } + } + } + + public void spawn(int wave) + { + // Announce spawning + MAUtils.tellAll(getArena(), Msg.WAVE_BOSS, ""+wave); + + // Spawn the boss and set the arena + bossCreature = boss.spawn(getArena(), getWorld(), getArena().getBossSpawnpoint()); + if (bossCreature instanceof Creature) + ((Creature) bossCreature).setTarget(MAUtils.getClosestPlayer(bossCreature, getArena())); + getArena().addMonster(bossCreature); + getArena().setBossWave(this); + + // Set the health stuff + bossCreature.setHealth(200); + healthAmount = bossHealth.getAmount(getArena().getPlayerCount()); + + startAbilityTasks(); + } + + private void startAbilityTasks() + { + final int abilityCount = abilities.size(); + + // If there are no abilities, don't start the timer. + if (abilityCount == 0) + return; + + abilityTask = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(getArena().getPlugin(), + new Runnable() + { + private int counter = 0; + + public void run() + { + // Grab the next ability + BossAbility ability = abilities.get(counter % abilityCount); + + // Announce it + if (abilityAnnounce) + MAUtils.tellAll(getArena(), Msg.WAVE_BOSS_ABILITY, ability.toString()); + + // Activate! + ability.run(getArena(), bossCreature); + + // Increment counter + counter++; + } + }, 100, abilityInterval); + } + + public void cancelAbilityTask() + { + if (abilityTask != -1) + Bukkit.getServer().getScheduler().cancelTask(abilityTask); + } + + public void clear() + { + cancelAbilityTask(); + getArena().setBossWave(null); + + CraftEntity ce = (CraftEntity) bossCreature; + CraftWorld cw = (CraftWorld) getWorld(); + WorldServer ws = cw.getHandle(); + Location l = bossCreature.getLocation(); + ws.createExplosion(ce.getHandle(), l.getX(), l.getY() + 1, l.getZ(), 1f, false); + + bossCreature.remove(); + } + + public void addAbility(BossAbility ability) + { + abilities.add(ability); + } + + public void addAdds(Creature creature) + { + adds.add(creature); + } + + public LivingEntity getEntity() + { + return bossCreature; + } + + public int getHealth() + { + return healthAmount; + } + + public void setHealth(int healthAmount) + { + this.healthAmount = healthAmount; + } + + public void subtractHealth(int amount) + { + healthAmount -= amount; + } + + public boolean isLowHealthAnnounced() + { + return lowHealthAnnounced; + } + + public void setLowHealthAnnounced(boolean value) + { + lowHealthAnnounced = value; + } +} diff --git a/src/com/garbagemule/MobArena/waves/DefaultWave.java b/src/com/garbagemule/MobArena/waves/DefaultWave.java new file mode 100644 index 0000000..6a0d290 --- /dev/null +++ b/src/com/garbagemule/MobArena/waves/DefaultWave.java @@ -0,0 +1,79 @@ +package com.garbagemule.MobArena.waves; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.bukkit.Location; +import org.bukkit.util.config.Configuration; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.MAUtils; +import com.garbagemule.MobArena.MAMessages.Msg; +import com.garbagemule.MobArena.util.WaveUtils; + +public class DefaultWave extends NormalWave +{ + // Recurrent + public DefaultWave(Arena arena, String name, int wave, int frequency, int priority, Configuration config, String path) + { + super(arena, name, wave, frequency, priority, config, path); + load(config, path, WaveType.DEFAULT); + } + + // Single + public DefaultWave(Arena arena, String name, int wave, Configuration config, String path) + { + super(arena, name, wave, config, path); + load(config, path, WaveType.DEFAULT); + } + + /** + * Default waves spawn an amount of random monsters, picked from a + * map of probabilities. The amount to spawn depends on the wave + * number and player count. + */ + public void spawn(int wave) + { + // Announce spawning + MAUtils.tellAll(getArena(), Msg.WAVE_DEFAULT, ""+wave); + + // Get the valid spawnpoints, and initialize counter + List validSpawnpoints = WaveUtils.getValidSpawnpoints(getArena(), getArena().getLivingPlayers()); + + // Initialize the total amount of mobs to spawn + int totalToSpawn = getGrowth().getAmount(wave, getArena().getPlayerCount()); + + // Spawn all the monsters + spawnAll(getMonstersToSpawn(totalToSpawn), validSpawnpoints); + } + + private Map getMonstersToSpawn(int totalToSpawn) + { + Map result = new HashMap(); + Random random = new Random(); + int randomNumber; + MACreature creature; + + for (int i = 0; i < totalToSpawn; i++) + { + randomNumber = random.nextInt(getTotalProbability()); + + // Find the monster that corresponds to the random number, and increment its value + for (Map.Entry entry : getProbabilityMap().entrySet()) + { + if (randomNumber > entry.getKey()) continue; + + creature = entry.getValue(); + if (result.containsKey(entry.getValue())) + result.put(creature, result.get(creature) + 1); + else + result.put(creature, 1); + break; + } + } + + return result; + } +} diff --git a/src/com/garbagemule/MobArena/waves/MACreature.java b/src/com/garbagemule/MobArena/waves/MACreature.java new file mode 100644 index 0000000..ab7e274 --- /dev/null +++ b/src/com/garbagemule/MobArena/waves/MACreature.java @@ -0,0 +1,108 @@ +package com.garbagemule.MobArena.waves; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.CreatureType; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Slime; +import org.bukkit.entity.Wolf; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.MobArena; +import com.garbagemule.MobArena.util.WaveUtils; + +public enum MACreature +{ + // Default creatures + ZOMBIE(CreatureType.ZOMBIE), ZOMBIES(CreatureType.ZOMBIE), + SKELETON(CreatureType.SKELETON), SKELETONS(CreatureType.SKELETON), + SPIDER(CreatureType.SPIDER), SPIDERS(CreatureType.SPIDER), + CREEPER(CreatureType.CREEPER), CREEPERS(CreatureType.CREEPER), + WOLF(CreatureType.WOLF), WOLVES(CreatureType.WOLF), + + // Special creatures + ZOMBIE_PIGMAN(CreatureType.PIG_ZOMBIE), ZOMBIE_PIGMEN(CreatureType.PIG_ZOMBIE), + POWERED_CREEPER(CreatureType.CREEPER), POWERED_CREEPERS(CreatureType.CREEPER), + ANGRY_WOLF(CreatureType.WOLF), ANGRY_WOLVES(CreatureType.WOLF), + HUMAN(CreatureType.MONSTER), HUMANS(CreatureType.MONSTER), + GIANT(CreatureType.GIANT), GIANTS(CreatureType.GIANT), + GHAST(CreatureType.GHAST), GHASTS(CreatureType.GHAST), + + // Passive creatures + CHICKEN(CreatureType.CHICKEN), CHICKENS(CreatureType.CHICKEN), + COW(CreatureType.COW), COWS(CreatureType.COW), + PIG(CreatureType.PIG), PIGS(CreatureType.PIG), + SHEEP(CreatureType.SHEEP), + SQUID(CreatureType.SQUID), SQUIDS(CreatureType.SQUID), + + // Extended creatures + EXPLODING_SHEEP(CreatureType.SHEEP), + + // Slimes + SLIME(CreatureType.SLIME), SLIMES(CreatureType.SLIME), + SLIME_TINY(CreatureType.SLIME), SLIMES_TINY(CreatureType.SLIME), + SLIME_SMALL(CreatureType.SLIME), SLIMES_SMALL(CreatureType.SLIME), + SLIME_BIG(CreatureType.SLIME), SLIMES_BIG(CreatureType.SLIME), + SLIME_HUGE(CreatureType.SLIME), SLIMES_HUGE(CreatureType.SLIME); + + private CreatureType type; + + private MACreature(CreatureType type) + { + this.type = type; + } + + public CreatureType getType() + { + return type; + } + + public static MACreature fromString(String string) + { + return WaveUtils.getEnumFromString(MACreature.class, string); + } + + public LivingEntity spawn(Arena arena, World world, Location loc) + { + LivingEntity e = world.spawnCreature(loc, type); + + switch (this) + { + case SHEEP: + case EXPLODING_SHEEP: + arena.addExplodingSheep(e); + break; + case POWERED_CREEPERS: + ((Creeper) e).setPowered(true); + break; + case ANGRY_WOLVES: + ((Wolf) e).setAngry(true); + break; + case SLIME: + case SLIMES: + ((Slime) e).setSize( (1 + MobArena.random.nextInt(3)) ); + break; + case SLIME_TINY: + case SLIMES_TINY: + ((Slime) e).setSize(1); + break; + case SLIME_SMALL: + case SLIMES_SMALL: + ((Slime) e).setSize(2); + break; + case SLIME_BIG: + case SLIMES_BIG: + ((Slime) e).setSize(3); + break; + case SLIME_HUGE: + case SLIMES_HUGE: + ((Slime) e).setSize(4); + break; + default: + break; + } + + return e; + } +} diff --git a/src/com/garbagemule/MobArena/waves/NormalWave.java b/src/com/garbagemule/MobArena/waves/NormalWave.java new file mode 100644 index 0000000..65b2ed1 --- /dev/null +++ b/src/com/garbagemule/MobArena/waves/NormalWave.java @@ -0,0 +1,95 @@ +package com.garbagemule.MobArena.waves; + +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.bukkit.util.config.Configuration; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.util.WaveUtils; + +public abstract class NormalWave extends AbstractWave +{ + private int totalProbability = 0; + private Map probabilities = new TreeMap(); + + // Recurrent + public NormalWave(Arena arena, String name, int wave, int frequency, int priority, Configuration config, String path) + { + super(arena, name, wave, frequency, priority); + } + + // Single + public NormalWave(Arena arena, String name, int wave, Configuration config, String path) + { + super(arena, name, wave); + } + + /** + * Prepare the wave for spawning by initializing the variables and + * populating the collections needed. + * @param config The config-file + * @param path The absolute path of the wave + * @param type DEFAULT or SPECIAL + */ + public void load(Configuration config, String path, WaveType type) + { + // Set type and (for DEFAULT) growth. + setType(type); + if (type == WaveType.DEFAULT) + setGrowth(WaveUtils.getEnumFromString(WaveGrowth.class, config.getString(path + "growth"), WaveGrowth.OLD)); + + // Load monsters + int prob; + List monsters = config.getKeys(path + "monsters"); + if (monsters != null && !monsters.isEmpty()) + { + for (String m : config.getKeys(path + "monsters")) + { + prob = config.getInt(path + "monsters." + m, 1); + if (prob == 0) continue; + + incTotalProbability(prob); + probabilities.put(totalProbability, MACreature.fromString(m)); + } + } + else + { + if (type == WaveType.DEFAULT) + { + probabilities.put(10, MACreature.ZOMBIES); + probabilities.put(20, MACreature.SKELETONS); + probabilities.put(30, MACreature.SPIDERS); + probabilities.put(40, MACreature.CREEPERS); + probabilities.put(50, MACreature.WOLVES); + totalProbability = 50; + } + else if (type == WaveType.SPECIAL) + { + probabilities.put(10, MACreature.POWERED_CREEPERS); + probabilities.put(20, MACreature.ANGRY_WOLVES); + probabilities.put(30, MACreature.ZOMBIE_PIGMEN); + probabilities.put(40, MACreature.SLIMES); + totalProbability = 40; + } + } + } + + public int getTotalProbability() + { + return totalProbability; + } + + public void incTotalProbability(int value) + { + totalProbability += value; + } + + public Map getProbabilityMap() + { + return probabilities; + } + + public abstract void spawn(int wave); +} diff --git a/src/com/garbagemule/MobArena/waves/SpecialWave.java b/src/com/garbagemule/MobArena/waves/SpecialWave.java new file mode 100644 index 0000000..02bbe1f --- /dev/null +++ b/src/com/garbagemule/MobArena/waves/SpecialWave.java @@ -0,0 +1,73 @@ +package com.garbagemule.MobArena.waves; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.bukkit.Location; +import org.bukkit.util.config.Configuration; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.MAUtils; +import com.garbagemule.MobArena.MAMessages.Msg; +import com.garbagemule.MobArena.util.WaveUtils; + +public class SpecialWave extends NormalWave +{ + // Recurrent + public SpecialWave(Arena arena, String name, int wave, int frequency, int priority, Configuration config, String path) + { + super(arena, name, wave, frequency, priority, config, path); + load(config, path, WaveType.SPECIAL); + } + + // Single + public SpecialWave(Arena arena, String name, int wave, Configuration config, String path) + { + super(arena, name, wave, config, path); + load(config, path, WaveType.SPECIAL); + } + + public void spawn(int wave) + { + // Announce spawning + MAUtils.tellAll(getArena(), Msg.WAVE_SPECIAL, ""+wave); + + // Get the valid spawnpoints, and initialize counter + List validSpawnpoints = WaveUtils.getValidSpawnpoints(getArena(), getArena().getLivingPlayers()); + + // Spawn all the monsters + spawnAll(getMonstersToSpawn(getArena().getPlayerCount()), validSpawnpoints); + } + + private Map getMonstersToSpawn(int playerCount) + { + Map result = new HashMap(); + + Random random = new Random(); + int randomNumber = random.nextInt(getTotalProbability()); + for (Map.Entry entry : getProbabilityMap().entrySet()) + { + if (randomNumber > entry.getKey()) continue; + + int amount; + switch (entry.getValue()) + { + case POWERED_CREEPERS: + case ZOMBIE_PIGMEN: + case ANGRY_WOLVES: amount = playerCount * 2; break; + case SLIMES: amount = playerCount * 4; break; + case HUMANS: amount = playerCount + 2; break; + case GIANTS: + case GHASTS: amount = 2; + default: amount = playerCount + 1; break; + } + + result.put(entry.getValue(), amount); + break; + } + + return result; + } +} diff --git a/src/com/garbagemule/MobArena/waves/SwarmWave.java b/src/com/garbagemule/MobArena/waves/SwarmWave.java new file mode 100644 index 0000000..d69e382 --- /dev/null +++ b/src/com/garbagemule/MobArena/waves/SwarmWave.java @@ -0,0 +1,66 @@ +package com.garbagemule.MobArena.waves; + +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.entity.LivingEntity; +import org.bukkit.util.config.Configuration; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.MAUtils; +import com.garbagemule.MobArena.MAMessages.Msg; +import com.garbagemule.MobArena.util.WaveUtils; + +public class SwarmWave extends AbstractWave +{ + private MACreature monster; + private SwarmAmount amount; + + // Recurrent + public SwarmWave(Arena arena, String name, int wave, int frequency, int priority, Configuration config, String path) + { + super(arena, name, wave, frequency, priority); + load(config, path); + } + + // Single + public SwarmWave(Arena arena, String name, int wave, Configuration config, String path) + { + super(arena, name, wave); + load(config, path); + } + + private void load(Configuration config, String path) + { + // Set the wave type + setType(WaveType.SWARM); + + // Get the monster type + monster = MACreature.fromString(config.getString(path + "monster")); + + // And the amount + amount = WaveUtils.getEnumFromString(SwarmAmount.class, config.getString(path + "amount"), SwarmAmount.LOW); + } + + public void spawn(int wave) + { + // Announce spawning + MAUtils.tellAll(getArena(), Msg.WAVE_SWARM, ""+wave); + + // Get the valid spawnpoints, and initialize counter + List validSpawnpoints = WaveUtils.getValidSpawnpoints(getArena(), getArena().getLivingPlayers()); + + // Spawn the hellians! + spawnAll(monster, amount.getAmount(getArena().getPlayerCount()), validSpawnpoints); + } + + public void spawnAll(MACreature monster, int amount, List spawnpoints) + { + int spawnpointCount = spawnpoints.size(); + for (int i = 0; i < amount; i++) + { + LivingEntity e = spawnMonster(monster, spawnpoints.get(i % spawnpointCount)); + e.setHealth(1); + } + } +} diff --git a/src/com/garbagemule/MobArena/waves/Wave.java b/src/com/garbagemule/MobArena/waves/Wave.java new file mode 100644 index 0000000..e6d2690 --- /dev/null +++ b/src/com/garbagemule/MobArena/waves/Wave.java @@ -0,0 +1,163 @@ +package com.garbagemule.MobArena.waves; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.util.WaveUtils; + +public interface Wave +{ + public enum WaveBranch + { + SINGLE, RECURRENT; + } + + public enum WaveType + { + DEFAULT, SPECIAL, SWARM, BOSS; + + public static WaveType fromString(String string) + { + return WaveUtils.getEnumFromString(WaveType.class, string); + } + } + + public enum WaveGrowth + { + OLD(0), SLOW(0.5), MEDIUM(0.65), FAST(0.8), PSYCHO(1.2); + private double exp; + + private WaveGrowth(double exp) + { + this.exp = exp; + } + + public static WaveGrowth fromString(String string) + { + return WaveUtils.getEnumFromString(WaveGrowth.class, string, OLD); + } + + public int getAmount(int wave, int playerCount) + { + if (this == OLD) return wave + playerCount; + + double pc = (double) playerCount; + double w = (double) wave; + + double base = Math.min(Math.ceil(pc/2) + 1, 13); + return (int) ( base * Math.pow(w, exp) ); + } + } + + public enum BossHealth + { + LOW(8), MEDIUM(15), HIGH(25), PSYCHO(40); + private int multiplier; + + private BossHealth(int multiplier) + { + this.multiplier = multiplier; + } + + public int getAmount(int playerCount) + { + return (playerCount + 1) * 20 * multiplier; + } + + public static BossHealth fromString(String string) + { + return WaveUtils.getEnumFromString(BossHealth.class, string); + } + } + + public enum SwarmAmount + { + LOW(10), MEDIUM(20), HIGH(30), PSYCHO(60); + private int multiplier; + + private SwarmAmount(int multiplier) + { + this.multiplier = multiplier; + } + + public int getAmount(int playerCount) + { + return Math.max(1, playerCount / 2) * multiplier; + } + + public static SwarmAmount fromString(String string) + { + return WaveUtils.getEnumFromString(SwarmAmount.class, string); + } + } + + /** + * The spawn() method must spawn one or more monsters in + * the arena. The monster count, damage, health, etc. can + * be modified by the wave parameter. + * @param wave Wave number + */ + public void spawn(int wave); + + /** + * Get the type of wave. + * @return The WaveType of this Wave. + */ + public WaveType getType(); + + /** + * Get the branch type. + * @return The WaveBranch of this Wave. + */ + public WaveBranch getBranch(); + + /** + * Get the growth rate of this wave. + * @return The growth rate + */ + public WaveGrowth getGrowth(); + + /** + * Get the first wave number for this wave + * @return The wave number + */ + public int getWave(); + + /** + * Get the wave's frequency, i.e. wave number "modulo" + * @return The wave's frequency + */ + public int getFrequency(); + + /** + * Get the wave's priority value. + * @return The priority + */ + public int getPriority(); + + /** + * Get the wave's name. + * @return The name + */ + public String getName(); + + /** + * Get the arena of this wave. + * @return The arena + */ + public Arena getArena(); + + /** + * Set the wave's growth + * @param growth How fast the wave will grow + */ + public void setGrowth(WaveGrowth growth); + + /** + * Check if this wave matches the wave number. + * The SingleWave class does a simple check if its wave == the parameter. + * The RecurrentWave class is more complex in that it needs to do some + * calculations based on the initial wave and the frequency. + * @param wave The current wave number + * @return true, if the wave should spawn, false otherwise + */ + public boolean matches(int wave); +} \ No newline at end of file diff --git a/src/com/garbagemule/register/payment/Method.java b/src/com/garbagemule/register/payment/Method.java index 921b9cf..ec1481b 100644 --- a/src/com/garbagemule/register/payment/Method.java +++ b/src/com/garbagemule/register/payment/Method.java @@ -3,27 +3,121 @@ package com.garbagemule.register.payment; import org.bukkit.plugin.Plugin; /** - * Method.java - * Interface for all sub-methods for payment. + * Interface to be implemented by a payment method. * - * @author: Nijikokun (@nijikokun) - * @copyright: Copyright (C) 2011 - * @license: GNUv3 Affero License + * @author Nijikokun (@nijikokun) + * @copyright Copyright (C) 2011 + * @license AOL license */ public interface Method { + /** + * Encodes the Plugin into an Object disguised as the Plugin. + * If you want the original Plugin Class you must cast it to the correct + * Plugin, to do so you have to verify the name and or version then cast. + * + *
+     *  if(method.getName().equalsIgnoreCase("iConomy"))
+     *   iConomy plugin = ((iConomy)method.getPlugin());
+ * + * @return Object + * @see #getName() + * @see #getVersion() + */ public Object getPlugin(); + + /** + * Returns the actual name of this method. + * + * @return String Plugin name. + */ public String getName(); + + /** + * Returns the actual version of this method. + * + * @return String Plugin version. + */ public String getVersion(); + + + /** + * Formats amounts into this payment methods style of currency display. + * + * @param amount Double + * @return String - Formatted Currency Display. + */ public String format(double amount); + + /** + * Allows the verification of bank API existence in this payment method. + * + * @return boolean + */ public boolean hasBanks(); + + /** + * Determines the existence of a bank via name. + * + * @param bank Bank name + * @return boolean + * @see #hasBanks + */ public boolean hasBank(String bank); + + /** + * Determines the existence of an account via name. + * + * @param name Account name + * @return boolean + */ public boolean hasAccount(String name); + + /** + * Check to see if an account name is tied to a bank. + * + * @param bank Bank name + * @param name Account name + * @return boolean + */ public boolean hasBankAccount(String bank, String name); + + /** + * Returns a MethodAccount class for an account name. + * + * @param name Account name + * @return MethodAccount or Null + */ public MethodAccount getAccount(String name); + + + /** + * Returns a MethodBankAccount class for an account name. + * + * @param bank Bank name + * @param name Account name + * @return MethodBankAccount or Null + */ public MethodBankAccount getBankAccount(String bank, String name); + + /** + * Checks to verify the compatibility between this Method and a plugin. + * Internal usage only, for the most part. + * + * @param plugin Plugin + * @return boolean + */ public boolean isCompatible(Plugin plugin); + + /** + * Set Plugin data. + * + * @param plugin Plugin + */ public void setPlugin(Plugin plugin); + /** + * Contains Calculator and Balance functions for Accounts. + */ public interface MethodAccount { public double balance(); public boolean set(double amount); @@ -41,6 +135,9 @@ public interface Method { public String toString(); } + /** + * Contains Calculator and Balance functions for Bank Accounts. + */ public interface MethodBankAccount { public double balance(); public String getBankName(); diff --git a/src/com/garbagemule/register/payment/Methods.java b/src/com/garbagemule/register/payment/Methods.java index a8eb99f..117a236 100644 --- a/src/com/garbagemule/register/payment/Methods.java +++ b/src/com/garbagemule/register/payment/Methods.java @@ -1,21 +1,31 @@ package com.garbagemule.register.payment; -import com.garbagemule.register.payment.methods.BOSE6; -import com.garbagemule.register.payment.methods.BOSE7; - -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.PluginManager; - import java.util.HashSet; import java.util.Set; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; + /** - * Methods.java - * Controls the getting / setting of methods & the method of payment used. + * The Methods initializes Methods that utilize the Method interface + * based on a "first come, first served" basis. * - * @author: Nijikokun (@nijikokun) + * Allowing you to check whether a payment method exists or not. + * + *
+ *  Methods methods = new Methods();
+ * 
+ * + * Methods also allows you to set a preferred method of payment before it captures + * payment plugins in the initialization process. + * + *
+ *  Methods methods = new Methods("iConomy");
+ * 
+ * + * @author: Nijikokun (@nijikokun) * @copyright: Copyright (C) 2011 - * @license: GNUv3 Affero License + * @license: AOL license */ public class Methods { private boolean self = false; @@ -25,14 +35,18 @@ public class Methods { private Set Dependencies = new HashSet(); private Set Attachables = new HashSet(); + /** + * Initialize Method class + */ public Methods() { this._init(); } /** - * Allows you to set which economy plugin is most preferred. + * Initializes Methods class utilizing a "preferred" payment method check before + * returning the first method that was initialized. * - * @param preferred - preferred economy plugin + * @param preferred Payment method that is most preferred for this setup. */ public Methods(String preferred) { this._init(); @@ -42,18 +56,40 @@ public class Methods { } } + /** + * Implement all methods along with their respective name & class. + * + * @see #Methods() + * @see #Methods(java.lang.String) + */ private void _init() { - this.addMethod("iConomy", new com.garbagemule.register.payment.methods.iCo4()); + this.addMethod("iConomy", new com.garbagemule.register.payment.methods.iCo6()); this.addMethod("iConomy", new com.garbagemule.register.payment.methods.iCo5()); - this.addMethod("BOSEconomy", new BOSE6()); - this.addMethod("BOSEconomy", new BOSE7()); + this.addMethod("iConomy", new com.garbagemule.register.payment.methods.iCo4()); + this.addMethod("BOSEconomy", new com.garbagemule.register.payment.methods.BOSE6()); + this.addMethod("BOSEconomy", new com.garbagemule.register.payment.methods.BOSE7()); this.addMethod("Essentials", new com.garbagemule.register.payment.methods.EE17()); + this.addMethod("MultiCurrency", new com.garbagemule.register.payment.methods.MCUR()); } + /** + * Returns an array of payment method names that have been loaded + * through the _init method. + * + * @return Set - Array of payment methods that are loaded. + * @see #setMethod(org.bukkit.plugin.Plugin) + */ public Set getDependencies() { return Dependencies; } + /** + * Interprets Plugin class data to verify whether it is compatible with an existing payment + * method to use for payments and other various economic activity. + * + * @param plugin Plugin data from bukkit, Internal Class file. + * @return Method or Null + */ public Method createMethod(Plugin plugin) { for (Method method: Methods) { if (method.isCompatible(plugin)) { @@ -70,17 +106,31 @@ public class Methods { Methods.add(method); } + /** + * Verifies if Register has set a payment method for usage yet. + * + * @return boolean + * @see #setMethod(org.bukkit.plugin.Plugin) + * @see #checkDisabled(org.bukkit.plugin.Plugin) + */ public boolean hasMethod() { return (Method != null); } + /** + * Checks Plugin Class against a multitude of checks to verify it's usability + * as a payment method. + * + * @param method Plugin data from bukkit, Internal Class file. + * @return boolean True on success, False on failure. + */ public boolean setMethod(Plugin method) { if(hasMethod()) return true; if(self) { self = false; return false; } int count = 0; boolean match = false; - Plugin plugin; + Plugin plugin = null; PluginManager manager = method.getServer().getPluginManager(); for(String name: this.getDependencies()) { @@ -129,10 +179,22 @@ public class Methods { return hasMethod(); } + /** + * Grab the existing and initialized (hopefully) Method Class. + * + * @return Method or Null + */ public Method getMethod() { return Method; } + /** + * Verify is a plugin is disabled, only does this if we there is an existing payment + * method initialized in Register. + * + * @param method Plugin data from bukkit, Internal Class file. + * @return boolean + */ public boolean checkDisabled(Plugin method) { if(!hasMethod()) return true; if (Method.isCompatible(method)) Method = null; diff --git a/src/com/garbagemule/register/payment/methods/BOSE6.java b/src/com/garbagemule/register/payment/methods/BOSE6.java index 3ca97f6..efd6053 100644 --- a/src/com/garbagemule/register/payment/methods/BOSE6.java +++ b/src/com/garbagemule/register/payment/methods/BOSE6.java @@ -1,10 +1,16 @@ package com.garbagemule.register.payment.methods; import com.garbagemule.register.payment.Method; - import cosine.boseconomy.BOSEconomy; import org.bukkit.plugin.Plugin; +/** + * BOSEconomy 6 Implementation of Method + * + * @author Nijikokun (@nijikokun) + * @copyright (c) 2011 + * @license AOL license + */ public class BOSE6 implements Method { private BOSEconomy BOSEconomy; @@ -61,8 +67,8 @@ public class BOSE6 implements Method { } public class BOSEAccount implements MethodAccount { - private String name; - private BOSEconomy BOSEconomy; + private final String name; + private final BOSEconomy BOSEconomy; public BOSEAccount(String name, BOSEconomy bOSEconomy) { this.name = name; @@ -70,35 +76,35 @@ public class BOSE6 implements Method { } public double balance() { - return Double.valueOf(this.BOSEconomy.getPlayerMoney(this.name)); + return (double) this.BOSEconomy.getPlayerMoneyDouble(this.name); } public boolean set(double amount) { int IntAmount = (int)Math.ceil(amount); - return this.BOSEconomy.setPlayerMoney(this.name, IntAmount, false); + return this.BOSEconomy.setPlayerMoney(this.name, (double) IntAmount, false); } public boolean add(double amount) { int IntAmount = (int)Math.ceil(amount); - return this.BOSEconomy.addPlayerMoney(this.name, IntAmount, false); + return this.BOSEconomy.addPlayerMoney(this.name, (double) IntAmount, false); } public boolean subtract(double amount) { int IntAmount = (int)Math.ceil(amount); int balance = (int)this.balance(); - return this.BOSEconomy.setPlayerMoney(this.name, (balance - IntAmount), false); + return this.BOSEconomy.setPlayerMoney(this.name, (double) (balance - IntAmount), false); } public boolean multiply(double amount) { int IntAmount = (int)Math.ceil(amount); int balance = (int)this.balance(); - return this.BOSEconomy.setPlayerMoney(this.name, (balance * IntAmount), false); + return this.BOSEconomy.setPlayerMoney(this.name, (double) (balance * IntAmount), false); } public boolean divide(double amount) { int IntAmount = (int)Math.ceil(amount); int balance = (int)this.balance(); - return this.BOSEconomy.setPlayerMoney(this.name, (balance / IntAmount), false); + return this.BOSEconomy.setPlayerMoney(this.name, (double) (balance / IntAmount), false); } public boolean hasEnough(double amount) { @@ -123,8 +129,8 @@ public class BOSE6 implements Method { } public class BOSEBankAccount implements MethodBankAccount { - private String bank; - private BOSEconomy BOSEconomy; + private final String bank; + private final BOSEconomy BOSEconomy; public BOSEBankAccount(String bank, BOSEconomy bOSEconomy) { this.bank = bank; @@ -140,36 +146,36 @@ public class BOSE6 implements Method { } public double balance() { - return Double.valueOf(this.BOSEconomy.getBankMoney(bank)); + return (double) this.BOSEconomy.getBankMoneyDouble(bank); } public boolean set(double amount) { int IntAmount = (int)Math.ceil(amount); - return this.BOSEconomy.setBankMoney(bank, IntAmount, true); + return this.BOSEconomy.setBankMoney(bank, (double) IntAmount, true); } public boolean add(double amount) { int IntAmount = (int)Math.ceil(amount); int balance = (int)this.balance(); - return this.BOSEconomy.setBankMoney(bank, (balance + IntAmount), false); + return this.BOSEconomy.setBankMoney(bank, (double) (balance + IntAmount), false); } public boolean subtract(double amount) { int IntAmount = (int)Math.ceil(amount); int balance = (int)this.balance(); - return this.BOSEconomy.setBankMoney(bank, (balance - IntAmount), false); + return this.BOSEconomy.setBankMoney(bank, (double) (balance - IntAmount), false); } public boolean multiply(double amount) { int IntAmount = (int)Math.ceil(amount); int balance = (int)this.balance(); - return this.BOSEconomy.setBankMoney(bank, (balance * IntAmount), false); + return this.BOSEconomy.setBankMoney(bank, (double) (balance * IntAmount), false); } public boolean divide(double amount) { int IntAmount = (int)Math.ceil(amount); int balance = (int)this.balance(); - return this.BOSEconomy.setBankMoney(bank, (balance / IntAmount), false); + return this.BOSEconomy.setBankMoney(bank, (double) (balance / IntAmount), false); } public boolean hasEnough(double amount) { diff --git a/src/com/garbagemule/register/payment/methods/BOSE7.java b/src/com/garbagemule/register/payment/methods/BOSE7.java index 18e98fa..ab8c418 100644 --- a/src/com/garbagemule/register/payment/methods/BOSE7.java +++ b/src/com/garbagemule/register/payment/methods/BOSE7.java @@ -1,14 +1,17 @@ package com.garbagemule.register.payment.methods; import com.garbagemule.register.payment.Method; - import cosine.boseconomy.BOSEconomy; import org.bukkit.plugin.Plugin; /** + * BOSEconomy 7 Implementation of Method + * * @author Acrobot + * @author Nijikokun (@nijikokun) + * @copyright (c) 2011 + * @license AOL license */ - public class BOSE7 implements Method { private BOSEconomy BOSEconomy; diff --git a/src/com/garbagemule/register/payment/methods/EE17.java b/src/com/garbagemule/register/payment/methods/EE17.java index 388483f..e9abb75 100644 --- a/src/com/garbagemule/register/payment/methods/EE17.java +++ b/src/com/garbagemule/register/payment/methods/EE17.java @@ -4,11 +4,21 @@ import com.earth2me.essentials.Essentials; import com.earth2me.essentials.api.Economy; import com.earth2me.essentials.api.NoLoanPermittedException; import com.earth2me.essentials.api.UserDoesNotExistException; -import com.garbagemule.register.payment.Method; +import com.garbagemule.register.payment.Method; import org.bukkit.plugin.Plugin; +/** + * Essentials 17 Implementation of Method + * + * @author Nijikokun (@nijikokun) + * @author Snowleo + * @author Acrobot + * @author KHobbits + * @copyright (c) 2011 + * @license AOL license + */ public class EE17 implements Method { private Essentials Essentials; diff --git a/src/com/garbagemule/register/payment/methods/MCUR.java b/src/com/garbagemule/register/payment/methods/MCUR.java new file mode 100644 index 0000000..77c9f2a --- /dev/null +++ b/src/com/garbagemule/register/payment/methods/MCUR.java @@ -0,0 +1,120 @@ +package com.garbagemule.register.payment.methods; + +import com.garbagemule.register.payment.Method; + +import me.ashtheking.currency.Currency; +import me.ashtheking.currency.CurrencyList; + +import org.bukkit.plugin.Plugin; + +/** + * MultiCurrency Method implementation. + * + * @author Acrobot + * @copyright (c) 2011 + * @license AOL license + */ +public class MCUR implements Method { + private Currency currencyList; + + public Object getPlugin() { + return this.currencyList; + } + + public String getName() { + return "MultiCurrency"; + } + + public String getVersion() { + return "0.09"; + } + + public String format(double amount) { + return amount + " Currency"; + } + + public boolean hasBanks() { + return false; + } + + public boolean hasBank(String bank) { + return false; + } + + public boolean hasAccount(String name) { + return true; + } + + public boolean hasBankAccount(String bank, String name) { + return false; + } + + public MethodAccount getAccount(String name) { + return new MCurrencyAccount(name); + } + + public MethodBankAccount getBankAccount(String bank, String name) { + return null; + } + + public boolean isCompatible(Plugin plugin) { + return plugin.getDescription().getName().equalsIgnoreCase(getName()) && plugin instanceof Currency; + } + + public void setPlugin(Plugin plugin) { + currencyList = (Currency) plugin; + } + + public class MCurrencyAccount implements MethodAccount{ + private String name; + + public MCurrencyAccount(String name) { + this.name = name; + } + + public double balance() { + return CurrencyList.getValue((String) CurrencyList.maxCurrency(name)[0], name); + } + + public boolean set(double amount) { + CurrencyList.setValue((String) CurrencyList.maxCurrency(name)[0], name, amount); + return true; + } + + public boolean add(double amount) { + return CurrencyList.add(name, amount); + } + + public boolean subtract(double amount) { + return CurrencyList.subtract(name, amount); + } + + public boolean multiply(double amount) { + return CurrencyList.multiply(name, amount); + } + + public boolean divide(double amount) { + return CurrencyList.divide(name, amount); + } + + public boolean hasEnough(double amount) { + return CurrencyList.hasEnough(name, amount); + } + + public boolean hasOver(double amount) { + return CurrencyList.hasOver(name, amount); + } + + public boolean hasUnder(double amount) { + return CurrencyList.hasUnder(name, amount); + } + + public boolean isNegative() { + return CurrencyList.isNegative(name); + } + + public boolean remove() { + return CurrencyList.remove(name); + } + } +} diff --git a/src/com/garbagemule/register/payment/methods/iCo4.java b/src/com/garbagemule/register/payment/methods/iCo4.java index 2c96115..eb76708 100644 --- a/src/com/garbagemule/register/payment/methods/iCo4.java +++ b/src/com/garbagemule/register/payment/methods/iCo4.java @@ -1,12 +1,19 @@ package com.garbagemule.register.payment.methods; -import com.garbagemule.register.payment.Method; import com.nijiko.coelho.iConomy.iConomy; import com.nijiko.coelho.iConomy.system.Account; +import com.garbagemule.register.payment.Method; import org.bukkit.plugin.Plugin; +/** + * iConomy 4 Implementation of Method + * + * @author Nijikokun (@nijikokun) + * @copyright (c) 2011 + * @license AOL license + */ public class iCo4 implements Method { private iConomy iConomy; @@ -51,7 +58,7 @@ public class iCo4 implements Method { } public boolean isCompatible(Plugin plugin) { - return plugin.getDescription().getName().equalsIgnoreCase("iconomy") && !plugin.getClass().getName().equals("com.iConomy.iConomy") && plugin instanceof iConomy; + return plugin.getDescription().getName().equalsIgnoreCase("iconomy") && plugin.getClass().getName().equals("com.nijiko.coelho.iConomy.iConomy") && plugin instanceof iConomy; } public void setPlugin(Plugin plugin) { diff --git a/src/com/garbagemule/register/payment/methods/iCo5.java b/src/com/garbagemule/register/payment/methods/iCo5.java index 2e0ede8..318090e 100644 --- a/src/com/garbagemule/register/payment/methods/iCo5.java +++ b/src/com/garbagemule/register/payment/methods/iCo5.java @@ -1,15 +1,22 @@ package com.garbagemule.register.payment.methods; -import com.garbagemule.register.payment.Method; import com.iConomy.iConomy; import com.iConomy.system.Account; import com.iConomy.system.BankAccount; import com.iConomy.system.Holdings; import com.iConomy.util.Constants; +import com.garbagemule.register.payment.Method; import org.bukkit.plugin.Plugin; +/** + * iConomy 5 Implementation of Method + * + * @author Nijikokun (@nijikokun) + * @copyright (c) 2011 + * @license AOL license + */ public class iCo5 implements Method { private iConomy iConomy; @@ -34,7 +41,7 @@ public class iCo5 implements Method { } public boolean hasBank(String bank) { - return (!hasBanks()) ? false : this.iConomy.Banks.exists(bank); + return (hasBanks()) && this.iConomy.Banks.exists(bank); } public boolean hasAccount(String name) { @@ -42,7 +49,7 @@ public class iCo5 implements Method { } public boolean hasBankAccount(String bank, String name) { - return (!hasBank(bank)) ? false : this.iConomy.getBank(bank).hasAccount(name); + return (hasBank(bank)) && this.iConomy.getBank(bank).hasAccount(name); } public MethodAccount getAccount(String name) { @@ -52,7 +59,7 @@ public class iCo5 implements Method { public MethodBankAccount getBankAccount(String bank, String name) { return new iCoBankAccount(this.iConomy.getBank(bank).getAccount(name)); } - + public boolean isCompatible(Plugin plugin) { return plugin.getDescription().getName().equalsIgnoreCase("iconomy") && plugin.getClass().getName().equals("com.iConomy.iConomy") && plugin instanceof iConomy; } @@ -208,4 +215,4 @@ public class iCo5 implements Method { return true; } } -} +} \ No newline at end of file diff --git a/src/com/garbagemule/register/payment/methods/iCo6.java b/src/com/garbagemule/register/payment/methods/iCo6.java new file mode 100644 index 0000000..4950ee0 --- /dev/null +++ b/src/com/garbagemule/register/payment/methods/iCo6.java @@ -0,0 +1,142 @@ +package com.garbagemule.register.payment.methods; + +import com.iCo6.iConomy; +import com.iCo6.system.Account; +import com.iCo6.system.Accounts; +import com.iCo6.system.Holdings; + +import com.garbagemule.register.payment.Method; + +import org.bukkit.plugin.Plugin; + +/** + * iConomy 6 Implementation of Method + * + * @author Nijikokun (@nijikokun) + * @copyright (c) 2011 + * @license AOL license + */ +public class iCo6 implements Method { + private iConomy iConomy; + + public iConomy getPlugin() { + return this.iConomy; + } + + public String getName() { + return "iConomy"; + } + + public String getVersion() { + return "6"; + } + + public String format(double amount) { + return this.iConomy.format(amount); + } + + public boolean hasBanks() { + return false; + } + + public boolean hasBank(String bank) { + return false; + } + + public boolean hasAccount(String name) { + return (new Accounts()).exists(name); + } + + public boolean hasBankAccount(String bank, String name) { + return false; + } + + public MethodAccount getAccount(String name) { + return new iCoAccount((new Accounts()).get(name)); + } + + public MethodBankAccount getBankAccount(String bank, String name) { + return null; + } + + public boolean isCompatible(Plugin plugin) { + try { Class.forName("com.iCo6.IO"); } + catch(Exception e) { return false; } + + return plugin.getDescription().getName().equalsIgnoreCase("iconomy") && plugin.getClass().getName().equals("com.iCo6.iConomy") && plugin instanceof iConomy; + } + + public void setPlugin(Plugin plugin) { + iConomy = (iConomy)plugin; + } + + public class iCoAccount implements MethodAccount { + private Account account; + private Holdings holdings; + + public iCoAccount(Account account) { + this.account = account; + this.holdings = account.getHoldings(); + } + + public Account getiCoAccount() { + return account; + } + + public double balance() { + return this.holdings.getBalance(); + } + + public boolean set(double amount) { + if(this.holdings == null) return false; + this.holdings.setBalance(amount); + return true; + } + + public boolean add(double amount) { + if(this.holdings == null) return false; + this.holdings.add(amount); + return true; + } + + public boolean subtract(double amount) { + if(this.holdings == null) return false; + this.holdings.subtract(amount); + return true; + } + + public boolean multiply(double amount) { + if(this.holdings == null) return false; + this.holdings.multiply(amount); + return true; + } + + public boolean divide(double amount) { + if(this.holdings == null) return false; + this.holdings.divide(amount); + return true; + } + + public boolean hasEnough(double amount) { + return this.holdings.hasEnough(amount); + } + + public boolean hasOver(double amount) { + return this.holdings.hasOver(amount); + } + + public boolean hasUnder(double amount) { + return this.holdings.hasUnder(amount); + } + + public boolean isNegative() { + return this.holdings.isNegative(); + } + + public boolean remove() { + if(this.account == null) return false; + this.account.remove(); + return true; + } + } +}