mirror of
https://github.com/songoda/UltimateTimber.git
synced 2024-09-27 13:22:39 +02:00
Compare commits
264 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
bbdcdfbc36 | ||
|
412d96fe18 | ||
|
4b1fa2b37c | ||
|
ee570e4719 | ||
|
6a82c260b5 | ||
|
f65872827d | ||
|
139b15cc8d | ||
|
b533be2c26 | ||
|
3455ad87e3 | ||
|
7553a3c897 | ||
|
3847ff9dc5 | ||
|
01355d3e13 | ||
|
963875d5bf | ||
|
12c5c78a1d | ||
|
2523487543 | ||
|
5c84ee51e8 | ||
|
490f55388d | ||
|
096a1f25b4 | ||
|
2858427d86 | ||
|
b1cb59b7e8 | ||
|
ea940d573d | ||
|
3e9f7d2eec | ||
|
d243aba6af | ||
|
16091dcb59 | ||
|
e04a4af615 | ||
|
3bb28b8d2d | ||
|
ca8d830ee5 | ||
|
5cb3e2e56d | ||
|
79276c0eba | ||
|
101b783c36 | ||
|
84e8fc8eb6 | ||
|
5637a0d85e | ||
|
75202b8007 | ||
|
f4bd38f794 | ||
|
228deb9f17 | ||
|
94d8a27e4a | ||
|
b3f11ff819 | ||
|
04ebcb4a52 | ||
|
48a4cf416b | ||
|
59ef36fe1c | ||
|
0f3f8f0e7b | ||
|
1c4146abff | ||
|
c39f3cc1bd | ||
|
ad153088eb | ||
|
7479ed36b7 | ||
|
fc5e102e0c | ||
|
7f1c9ab3a3 | ||
|
7cff210782 | ||
|
5c5f938f3c | ||
|
07c6afdad2 | ||
|
f4f8540aec | ||
|
30923352a5 | ||
|
23bd964898 | ||
|
a97feabe74 | ||
|
bc405fb3fa | ||
|
7d39c8d967 | ||
|
9011160798 | ||
|
e2a32dfdee | ||
|
7fc7c11808 | ||
|
b5bb178569 | ||
|
d9eb494a6f | ||
|
564bfa6e8c | ||
|
ef7c7ac4fa | ||
|
5d8600df12 | ||
|
5cc632f625 | ||
|
1e2a41fd90 | ||
|
24a556eacf | ||
|
c94ec4c055 | ||
|
3e1fa6f937 | ||
|
49c9a1a42e | ||
|
daa4e8dcc2 | ||
|
b600ca6bac | ||
|
fb9d299eee | ||
|
c234f202bc | ||
|
cfc53d5b72 | ||
|
5833234da4 | ||
|
4d78edcb45 | ||
|
76e3b1fc3b | ||
|
2da515f31f | ||
|
c706ce3e6c | ||
|
5355e227ab | ||
|
6096047bae | ||
|
ac8d2a9714 | ||
|
7817beb831 | ||
|
e174ebc5bf | ||
|
3585ce75bb | ||
|
fdd4286195 | ||
|
c445e132e2 | ||
|
ae1d967628 | ||
|
99af64c422 | ||
|
6fe148a667 | ||
|
33b6c14811 | ||
|
8be488173f | ||
|
4f079a384e | ||
|
5dcb528eea | ||
|
9d58e513b4 | ||
|
a6926de3a2 | ||
|
ffb7657a78 | ||
|
065a5b809b | ||
|
488bb6b7b4 | ||
|
ee6439fe65 | ||
|
f59cc05d9d | ||
|
ff862c56d4 | ||
|
23e7643eb9 | ||
|
9feb8752a7 | ||
|
151b443a80 | ||
|
d3e2d27913 | ||
|
7256b314f6 | ||
|
04b23f5e1d | ||
|
e020a96488 | ||
|
b18a8cf469 | ||
|
5e1a913ac4 | ||
|
ef31edb1ee | ||
|
3464574b74 | ||
|
06e741536f | ||
|
6922ed36d3 | ||
|
43cd0be611 | ||
|
7f9ee381f6 | ||
|
1e493e8f00 | ||
|
8cf9161abf | ||
|
41d2f5ff29 | ||
|
46d1f7b01d | ||
|
7b45e94d59 | ||
|
60aa7a3157 | ||
|
4a121e924e | ||
|
daabc4f70e | ||
|
9903170d00 | ||
|
0bf1b4c263 | ||
|
bcde04159d | ||
|
588f210698 | ||
|
e8ef575d2a | ||
|
712cfeb998 | ||
|
d38ed65eed | ||
|
281423031f | ||
|
0bd7c3f22d | ||
|
8d7a4bb675 | ||
|
2524442911 | ||
|
0b57057b4d | ||
|
8988679f2a | ||
|
119e54ad28 | ||
|
cac222813d | ||
|
20b124a990 | ||
|
c98377bb93 | ||
|
a7787caa84 | ||
|
03cb32cf5c | ||
|
cffdf696c3 | ||
|
7d716fe62b | ||
|
90ba6bbf02 | ||
|
53ba40fb65 | ||
|
4b35f0ee4e | ||
|
a5b8fab267 | ||
|
0e1afcef84 | ||
|
5ce176841e | ||
|
a52ba616fe | ||
|
31923fa0b2 | ||
|
8ff4e315eb | ||
|
6e34b1cb6b | ||
|
0db463378b | ||
|
314d6fcb2b | ||
|
ae7c7e425d | ||
|
bf0078e794 | ||
|
bd3097422e | ||
|
1c17042835 | ||
|
2114d395e0 | ||
|
7b2ddb1562 | ||
|
26c567bb56 | ||
|
d47ebbb64b | ||
|
86dbb57ac2 | ||
|
e94a0c6259 | ||
|
120cb29c3b | ||
|
d00668bfbb | ||
|
fdac6ef040 | ||
|
f6a22966da | ||
|
06b570c8c5 | ||
|
5d83a2bd27 | ||
|
d8bd19a08a | ||
|
8026babcdf | ||
|
94ffdcc5f5 | ||
|
e3bbc814f3 | ||
|
9329cc1fb6 | ||
|
671236f57d | ||
|
a267bd745c | ||
|
4192e5ba74 | ||
|
665ece06da | ||
|
7bd1157f07 | ||
|
25a524c230 | ||
|
cece22e247 | ||
|
0aad72bc2c | ||
|
0ed8bef901 | ||
|
b9389137cf | ||
|
f9cac11d7a | ||
|
5c3ac55fde | ||
|
82e8c064f4 | ||
|
9610cebb39 | ||
|
319298968f | ||
|
d9f7c60836 | ||
|
1b370a73dd | ||
|
69d7a665f4 | ||
|
6c788c216e | ||
|
bcfdeabb2c | ||
|
d0cedf4ced | ||
|
a766b780cb | ||
|
00b1bfc82a | ||
|
c9fdb9fe5c | ||
|
78e7b4cd7c | ||
|
83ceca627c | ||
|
5cad56e436 | ||
|
2b27db5805 | ||
|
9ccd0e31f6 | ||
|
8f11177280 | ||
|
407fbdccc0 | ||
|
041c265024 | ||
|
08d304444e | ||
|
2ff0745a32 | ||
|
5ea8dfe9e6 | ||
|
cbf4f1ba0e | ||
|
bab00aed4d | ||
|
b84a5e39fe | ||
|
d32bbb4a9e | ||
|
9c8229a9dd | ||
|
92d4899cd9 | ||
|
4813fc9032 | ||
|
14753fab86 | ||
|
e2593d3422 | ||
|
1d93f15491 | ||
|
9495d15d67 | ||
|
5af145615b | ||
|
e485388b3d | ||
|
17cb5b6b76 | ||
|
624211fe0b | ||
|
765d3c41b8 | ||
|
a044d6971c | ||
|
269d7567f0 | ||
|
6123a7465b | ||
|
a845d30f7b | ||
|
c9ed661469 | ||
|
3a0b05c987 | ||
|
a52c2ad0be | ||
|
743df4d385 | ||
|
fcb5d84b5b | ||
|
be223b7fee | ||
|
84e7c34097 | ||
|
1873945a40 | ||
|
6c6ded977f | ||
|
dcd9b46a5c | ||
|
bca27ee82e | ||
|
3acf7f0984 | ||
|
68dab68ede | ||
|
10cf409818 | ||
|
1d6809936e | ||
|
bfdf2a2dca | ||
|
0b1aa35a61 | ||
|
2ab7c56de3 | ||
|
f483e9dea2 | ||
|
6918b4ad4d | ||
|
ee0f95cfbe | ||
|
2773986039 | ||
|
54ab6c9690 | ||
|
8aa7c177c1 | ||
|
e349e54a03 | ||
|
a235d3d803 | ||
|
cca1f83281 | ||
|
920b437e6d | ||
|
ad250f421e |
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
## JetBrains IDEs
|
||||
/.idea/
|
||||
*.iml
|
||||
|
||||
## Maven
|
||||
/**/target/
|
||||
/dependency-reduced-pom.xml
|
||||
|
||||
## Misc.
|
||||
.DS_Store
|
@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="UltimateTimber" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
<bytecodeTargetLevel>
|
||||
<module name="UltimateTimber" target="1.8" />
|
||||
</bytecodeTargetLevel>
|
||||
</component>
|
||||
</project>
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/classes" />
|
||||
</component>
|
||||
</project>
|
@ -1,124 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -1,631 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="b5e32c07-ebf4-41aa-9c50-de0c623af54f" name="Default Changelist" comment="Version 0.0.3 - Fully implemented reload command">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/pom.xml" beforeDir="false" afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/UltimateTimber.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/UltimateTimber.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/commands/ReloadCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/commands/ReloadCommand.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/configurations/DefaultConfig.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/configurations/DefaultConfig.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/EventFilter.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/EventFilter.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeFallAnimation.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeFallAnimation.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeFallEvent.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeFallEvent.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeReplant.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeReplant.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/resources/plugin.yml" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/plugin.yml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/target/maven-archiver/pom.properties" beforeDir="false" afterPath="$PROJECT_DIR$/target/maven-archiver/pom.properties" afterDir="false" />
|
||||
</list>
|
||||
<ignored path="$PROJECT_DIR$/target/" />
|
||||
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FUSProjectUsageTrigger">
|
||||
<session id="-76130593">
|
||||
<usages-collector id="statistics.lifecycle.project">
|
||||
<counts>
|
||||
<entry key="project.closed" value="3" />
|
||||
<entry key="project.open.time.1" value="1" />
|
||||
<entry key="project.open.time.10" value="1" />
|
||||
<entry key="project.open.time.4" value="1" />
|
||||
<entry key="project.open.time.7" value="1" />
|
||||
<entry key="project.opened" value="4" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.extensions.open">
|
||||
<counts>
|
||||
<entry key="class" value="1" />
|
||||
<entry key="java" value="27" />
|
||||
<entry key="xml" value="2" />
|
||||
<entry key="yml" value="2" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.types.open">
|
||||
<counts>
|
||||
<entry key="CLASS" value="1" />
|
||||
<entry key="JAVA" value="27" />
|
||||
<entry key="XML" value="2" />
|
||||
<entry key="YAML" value="2" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.extensions.edit">
|
||||
<counts>
|
||||
<entry key="java" value="7957" />
|
||||
<entry key="txt" value="1147" />
|
||||
<entry key="xml" value="6" />
|
||||
<entry key="yml" value="619" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
<usages-collector id="statistics.file.types.edit">
|
||||
<counts>
|
||||
<entry key="JAVA" value="7957" />
|
||||
<entry key="PLAIN_TEXT" value="1147" />
|
||||
<entry key="XML" value="6" />
|
||||
<entry key="YAML" value="619" />
|
||||
</counts>
|
||||
</usages-collector>
|
||||
</session>
|
||||
</component>
|
||||
<component name="FileEditorManager">
|
||||
<splitter split-orientation="horizontal" split-proportion="0.515656">
|
||||
<split-first>
|
||||
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/UltimateTimber.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="528">
|
||||
<caret line="42" column="49" selection-start-line="42" selection-start-column="49" selection-end-line="42" selection-end-column="49" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/commands/CommandHandler.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="112">
|
||||
<caret line="9" column="101" selection-start-line="9" selection-start-column="101" selection-end-line="9" selection-end-column="101" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/commands/ReloadCommand.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="128">
|
||||
<caret line="9" column="45" selection-start-line="9" selection-start-column="45" selection-end-line="9" selection-end-column="45" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeLoot.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state>
|
||||
<caret column="44" selection-start-column="44" selection-end-column="44" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="true">
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeReplant.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="144">
|
||||
<caret line="35" column="63" lean-forward="true" selection-start-line="35" selection-start-column="63" selection-end-line="35" selection-end-column="63" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
</leaf>
|
||||
</split-first>
|
||||
<split-second>
|
||||
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/configurations/DefaultConfig.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="448">
|
||||
<caret line="31" column="22" selection-start-line="31" selection-start-column="22" selection-end-line="31" selection-end-column="22" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="true">
|
||||
<entry file="file://$PROJECT_DIR$/src/main/resources/plugin.yml">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="96">
|
||||
<caret line="6" column="10" selection-start-line="6" selection-start-column="10" selection-end-line="6" selection-end-column="10" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/pom.xml">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="192">
|
||||
<caret line="12" column="17" lean-forward="true" selection-start-line="12" selection-start-column="17" selection-end-line="12" selection-end-column="17" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/src/main/resources/config.yml">
|
||||
<provider selected="true" editor-type-id="text-editor" />
|
||||
</entry>
|
||||
</file>
|
||||
</leaf>
|
||||
</split-second>
|
||||
</splitter>
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Class" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="FindInProjectRecents">
|
||||
<findStrings>
|
||||
<find>inventory</find>
|
||||
<find>air</find>
|
||||
<find>event.getPlayer()</find>
|
||||
<find>axe</find>
|
||||
</findStrings>
|
||||
<replaceStrings>
|
||||
<replace>player</replace>
|
||||
</replaceStrings>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="IdeDocumentHistory">
|
||||
<option name="CHANGED_PATHS">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/DefaultConfig.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/TreeFallHandler.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeFallHandler.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeChecker.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeEntityDamage.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/AxeDurability.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeLoot.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/commands/CommandHandler.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeImpactSound.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeSounds.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/configurations/DefaultConfig.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/UltimateTimber.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/commands/ReloadCommand.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeReplant.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeFallAnimation.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeFallEvent.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/EventFilter.java" />
|
||||
<option value="$PROJECT_DIR$/src/main/resources/plugin.yml" />
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
|
||||
<component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" />
|
||||
<component name="JsGulpfileManager">
|
||||
<detection-done>true</detection-done>
|
||||
<sorting>DEFINITION_ORDER</sorting>
|
||||
</component>
|
||||
<component name="MavenImportPreferences">
|
||||
<option name="importingSettings">
|
||||
<MavenImportingSettings>
|
||||
<option name="importAutomatically" value="true" />
|
||||
</MavenImportingSettings>
|
||||
</option>
|
||||
</component>
|
||||
<component name="MavenProjectNavigator">
|
||||
<treeState>
|
||||
<expand>
|
||||
<path>
|
||||
<item name="" type="16c1761:MavenProjectsStructure$RootNode" />
|
||||
<item name="UltimateTimber" type="9519ce18:MavenProjectsStructure$ProjectNode" />
|
||||
</path>
|
||||
</expand>
|
||||
<select />
|
||||
</treeState>
|
||||
</component>
|
||||
<component name="ProjectFrameBounds" extendedState="6">
|
||||
<option name="x" value="516" />
|
||||
<option name="y" value="93" />
|
||||
<option name="width" value="2576" />
|
||||
<option name="height" value="1288" />
|
||||
</component>
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
||||
<component name="ProjectView">
|
||||
<navigator proportions="" version="1">
|
||||
<foldersAlwaysOnTop value="true" />
|
||||
</navigator>
|
||||
<panes>
|
||||
<pane id="ProjectPane">
|
||||
<subPane>
|
||||
<expand>
|
||||
<path>
|
||||
<item name="UltimateTimber" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="UltimateTimber" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="UltimateTimber" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="UltimateTimber" type="462c0819:PsiDirectoryNode" />
|
||||
<item name=".idea" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="UltimateTimber" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="UltimateTimber" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="src" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="UltimateTimber" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="UltimateTimber" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="src" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="main" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="UltimateTimber" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="UltimateTimber" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="src" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="main" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="java" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="UltimateTimber" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="UltimateTimber" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="src" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="main" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="java" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="ultimatetimber" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="UltimateTimber" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="UltimateTimber" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="src" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="main" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="java" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="ultimatetimber" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="commands" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="UltimateTimber" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="UltimateTimber" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="src" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="main" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="java" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="ultimatetimber" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="configurations" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="UltimateTimber" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="UltimateTimber" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="src" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="main" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="java" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="ultimatetimber" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="treefall" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="UltimateTimber" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="UltimateTimber" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="src" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="main" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="resources" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
</expand>
|
||||
<select />
|
||||
</subPane>
|
||||
</pane>
|
||||
<pane id="Scope" />
|
||||
<pane id="PackagesPane" />
|
||||
<pane id="AndroidView" />
|
||||
</panes>
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="SearchEverywhereHistoryKey" value="pom.xml	FILE	file://C:/Users/Tiago/Desktop/MineCraftProjects/UltimateTimber/pom.xml replan	FILE	file://C:/Users/Tiago/Desktop/MineCraftProjects/UltimateTimber/src/main/java/com/songoda/ultimatetimber/treefall/TreeReplant.java loot	FILE	file://C:/Users/Tiago/Desktop/MineCraftProjects/UltimateTimber/src/main/java/com/songoda/ultimatetimber/treefall/TreeLoot.java treean	FILE	file://C:/Users/Tiago/Desktop/MineCraftProjects/UltimateTimber/src/main/java/com/songoda/ultimatetimber/treefall/TreeFallAnimation.java treech	FILE	file://C:/Users/Tiago/Desktop/MineCraftProjects/UltimateTimber/src/main/java/com/songoda/ultimatetimber/treefall/TreeChecker.java hasnear	PSI	JAVA://com.songoda.ultimatetimber.treefall.TreeFallAnimation#hasNearbySolidBlock hasnea	PSI	JAVA://com.songoda.ultimatetimber.treefall.TreeFallAnimation#hasNearbySolidBlock" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="aspect.path.notification.shown" value="true" />
|
||||
<property name="com.android.tools.idea.instantapp.provision.ProvisionBeforeRunTaskProvider.myTimeStamp" value="1541391329355" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
|
||||
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
|
||||
</component>
|
||||
<component name="RunDashboard">
|
||||
<option name="ruleStates">
|
||||
<list>
|
||||
<RuleState>
|
||||
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
|
||||
</RuleState>
|
||||
<RuleState>
|
||||
<option name="name" value="StatusDashboardGroupingRule" />
|
||||
</RuleState>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="SvnConfiguration">
|
||||
<configuration />
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="b5e32c07-ebf4-41aa-9c50-de0c623af54f" name="Default Changelist" comment="" />
|
||||
<created>1541287795357</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1541287795357</updated>
|
||||
<workItem from="1541287799885" duration="1592000" />
|
||||
<workItem from="1541371591623" duration="19803000" />
|
||||
<workItem from="1541449315641" duration="1228000" />
|
||||
<workItem from="1541451864953" duration="569000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="Version 0.0.1 - Initial commit">
|
||||
<created>1541288017614</created>
|
||||
<option name="number" value="00001" />
|
||||
<option name="presentableId" value="LOCAL-00001" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1541288017614</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00002" summary="Version 0.0.1 - Initial commit">
|
||||
<created>1541288193654</created>
|
||||
<option name="number" value="00002" />
|
||||
<option name="presentableId" value="LOCAL-00002" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1541288193654</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00003" summary="Version 0.0.2 - Added smoke particles to fallen trees - Integrated all the config checks - Added more classes to give the codebase more structure - Added a number of config options - Added some safeguards for the falling blocks - Added all the necessary loot - Added permission nodes - Added custom tree falling sounds - Converted the animation from single-phase to 2-phase, trees now slowly tip over and then pick up speed - Added various sapling spreading mechanics - Added damage to entities caught in the tree falling - Added silk touch compatibility - Added creative mode filter, doesn't affect players in creative mode - Added per-world filter - Started adding reload command, not currently functional due to bad configuration file reloading - Added axe filter, only applies effect to players wielding axes - Tweaked the search pyramid to better fit various trees generated by minecraft">
|
||||
<created>1541391319179</created>
|
||||
<option name="number" value="00003" />
|
||||
<option name="presentableId" value="LOCAL-00003" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1541391319179</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="4" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TimeTrackingManager">
|
||||
<option name="totallyTimeSpent" value="23192000" />
|
||||
</component>
|
||||
<component name="ToolWindowManager">
|
||||
<frame x="-8" y="-8" width="3456" height="1426" extended-state="6" />
|
||||
<editor active="true" />
|
||||
<layout>
|
||||
<window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.09452297" />
|
||||
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
|
||||
<window_info id="Designer" order="2" />
|
||||
<window_info id="Image Layers" order="3" />
|
||||
<window_info id="UI Designer" order="4" />
|
||||
<window_info id="Capture Tool" order="5" />
|
||||
<window_info id="Favorites" order="6" side_tool="true" />
|
||||
<window_info anchor="bottom" id="Message" order="0" />
|
||||
<window_info anchor="bottom" id="Find" order="1" />
|
||||
<window_info anchor="bottom" id="Run" order="2" visible="true" weight="0.3003096" />
|
||||
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
|
||||
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
|
||||
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
|
||||
<window_info anchor="bottom" id="TODO" order="6" />
|
||||
<window_info anchor="bottom" id="Version Control" order="7" weight="0.32972136" />
|
||||
<window_info anchor="bottom" id="Database Changes" order="8" show_stripe_button="false" />
|
||||
<window_info anchor="bottom" id="Statistic" order="9" />
|
||||
<window_info anchor="bottom" id="Terminal" order="10" />
|
||||
<window_info anchor="bottom" id="Event Log" order="11" side_tool="true" />
|
||||
<window_info anchor="right" id="Commander" order="0" weight="0.4" />
|
||||
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
|
||||
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
|
||||
<window_info anchor="right" id="Palette" order="3" />
|
||||
<window_info anchor="right" id="Capture Analysis" order="4" />
|
||||
<window_info anchor="right" id="Theme Preview" order="5" />
|
||||
<window_info anchor="right" id="Palette	" order="6" />
|
||||
<window_info anchor="right" id="Database" order="7" />
|
||||
<window_info active="true" anchor="right" id="Maven Projects" order="8" visible="true" weight="0.07037692" />
|
||||
</layout>
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="1" />
|
||||
</component>
|
||||
<component name="Vcs.Log.History.Properties">
|
||||
<option name="COLUMN_ORDER">
|
||||
<list>
|
||||
<option value="0" />
|
||||
<option value="2" />
|
||||
<option value="3" />
|
||||
<option value="1" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="VcsContentAnnotationSettings">
|
||||
<option name="myLimit" value="2678400000" />
|
||||
</component>
|
||||
<component name="VcsManagerConfiguration">
|
||||
<MESSAGE value="Version 0.0.1 - Initial commit" />
|
||||
<MESSAGE value="Version 0.0.2 - Added smoke particles to fallen trees - Integrated all the config checks - Added more classes to give the codebase more structure - Added a number of config options - Added some safeguards for the falling blocks - Added all the necessary loot - Added permission nodes - Added custom tree falling sounds - Converted the animation from single-phase to 2-phase, trees now slowly tip over and then pick up speed - Added various sapling spreading mechanics - Added damage to entities caught in the tree falling - Added silk touch compatibility - Added creative mode filter, doesn't affect players in creative mode - Added per-world filter - Started adding reload command, not currently functional due to bad configuration file reloading - Added axe filter, only applies effect to players wielding axes - Tweaked the search pyramid to better fit various trees generated by minecraft" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Version 0.0.2 - Added smoke particles to fallen trees - Integrated all the config checks - Added more classes to give the codebase more structure - Added a number of config options - Added some safeguards for the falling blocks - Added all the necessary loot - Added permission nodes - Added custom tree falling sounds - Converted the animation from single-phase to 2-phase, trees now slowly tip over and then pick up speed - Added various sapling spreading mechanics - Added damage to entities caught in the tree falling - Added silk touch compatibility - Added creative mode filter, doesn't affect players in creative mode - Added per-world filter - Started adding reload command, not currently functional due to bad configuration file reloading - Added axe filter, only applies effect to players wielding axes - Tweaked the search pyramid to better fit various trees generated by minecraft" />
|
||||
</component>
|
||||
<component name="antWorkspaceConfiguration">
|
||||
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
|
||||
<option name="FILTER_TARGETS" value="false" />
|
||||
</component>
|
||||
<component name="editorHistoryManager">
|
||||
<entry file="jar://$MAVEN_REPOSITORY$/org/spigotmc/spigot-api/1.13-R0.1-SNAPSHOT/spigot-api-1.13-R0.1-20180826.040111-146.jar!/org/bukkit/block/Block.class">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="182">
|
||||
<caret line="57" column="44" selection-start-line="57" selection-start-column="40" selection-end-line="57" selection-end-column="44" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeChecker.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="241">
|
||||
<caret line="103" column="81" selection-start-line="103" selection-start-column="81" selection-end-line="103" selection-end-column="81" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeEntityDamage.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="176">
|
||||
<caret line="11" lean-forward="true" selection-start-line="11" selection-end-line="11" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/UltimateTimber.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="528">
|
||||
<caret line="42" column="49" selection-start-line="42" selection-start-column="49" selection-end-line="42" selection-end-column="49" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/commands/CommandHandler.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="112">
|
||||
<caret line="9" column="101" selection-start-line="9" selection-start-column="101" selection-end-line="9" selection-end-column="101" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/commands/ReloadCommand.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="128">
|
||||
<caret line="9" column="45" selection-start-line="9" selection-start-column="45" selection-end-line="9" selection-end-column="45" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeLoot.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state>
|
||||
<caret column="44" selection-start-column="44" selection-end-column="44" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/configurations/DefaultConfig.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="448">
|
||||
<caret line="31" column="22" selection-start-line="31" selection-start-column="22" selection-end-line="31" selection-end-column="22" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/AxeDurability.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="288">
|
||||
<caret line="25" column="57" selection-start-line="25" selection-start-column="57" selection-end-line="25" selection-end-column="57" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeSounds.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="336">
|
||||
<caret line="23" selection-start-line="23" selection-end-line="23" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeFallAnimation.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="1568">
|
||||
<caret line="109" column="57" selection-start-line="109" selection-start-column="57" selection-end-line="109" selection-end-column="57" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/EventFilter.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="544">
|
||||
<caret line="38" column="45" selection-start-line="38" selection-start-column="45" selection-end-line="38" selection-end-column="45" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeFallEvent.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="144">
|
||||
<caret line="17" column="7" selection-start-line="17" selection-start-column="7" selection-end-line="17" selection-end-column="7" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/java/com/songoda/ultimatetimber/treefall/TreeReplant.java">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="144">
|
||||
<caret line="35" column="63" lean-forward="true" selection-start-line="35" selection-start-column="63" selection-end-line="35" selection-end-column="63" />
|
||||
<folding>
|
||||
<element signature="imports" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/resources/config.yml">
|
||||
<provider selected="true" editor-type-id="text-editor" />
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/pom.xml">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="192">
|
||||
<caret line="12" column="17" lean-forward="true" selection-start-line="12" selection-start-column="17" selection-end-line="12" selection-end-column="17" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/main/resources/plugin.yml">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="96">
|
||||
<caret line="6" column="10" selection-start-line="6" selection-start-column="10" selection-end-line="6" selection-end-column="10" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</component>
|
||||
<component name="masterDetails">
|
||||
<states>
|
||||
<state key="ProjectJDKs.UI">
|
||||
<settings>
|
||||
<last-edited>1.8</last-edited>
|
||||
<splitter-proportions>
|
||||
<option name="proportions">
|
||||
<list>
|
||||
<option value="0.2" />
|
||||
</list>
|
||||
</option>
|
||||
</splitter-proportions>
|
||||
</settings>
|
||||
</state>
|
||||
</states>
|
||||
</component>
|
||||
</project>
|
327
LICENSE
Normal file
327
LICENSE
Normal file
@ -0,0 +1,327 @@
|
||||
Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
|
||||
International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution-NonCommercial-NoDerivatives 4.0 International Public
|
||||
License ("Public License"). To the extent this Public License may be
|
||||
interpreted as a contract, You are granted the Licensed Rights in
|
||||
consideration of Your acceptance of these terms and conditions, and the
|
||||
Licensor grants You such rights in consideration of benefits the
|
||||
Licensor receives from making the Licensed Material available under
|
||||
these terms and conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
c. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
d. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
e. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
f. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
g. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
h. NonCommercial means not primarily intended for or directed towards
|
||||
commercial advantage or monetary compensation. For purposes of
|
||||
this Public License, the exchange of the Licensed Material for
|
||||
other material subject to Copyright and Similar Rights by digital
|
||||
file-sharing or similar means is NonCommercial provided there is
|
||||
no payment of monetary compensation in connection with the
|
||||
exchange.
|
||||
|
||||
i. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
j. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
k. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part, for NonCommercial purposes only; and
|
||||
|
||||
b. produce and reproduce, but not Share, Adapted Material
|
||||
for NonCommercial purposes only.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties, including when
|
||||
the Licensed Material is used other than for NonCommercial
|
||||
purposes.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material, You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
For the avoidance of doubt, You do not have permission under
|
||||
this Public License to Share Adapted Material.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database for NonCommercial purposes
|
||||
only and provided You do not Share Adapted Material;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material; and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
=======================================================================
|
44
README.md
Normal file
44
README.md
Normal file
@ -0,0 +1,44 @@
|
||||
<!--suppress HtmlDeprecatedAttribute -->
|
||||
<div align="center">
|
||||
<img src="docs/Logo.png" width="128px">
|
||||
|
||||
# UltimateTimber
|
||||
**Give your players a new and exciting way to chop down trees.**
|
||||
**Includes animated block falling, excellent tree detection, custom drops, realistic sounds, and more.**
|
||||
|
||||
|
||||
[![Discord][Discord shield]][Discord invite]
|
||||
<br>
|
||||
[![Latest version][Latest version shield]][Plugin page]
|
||||
[![bStats Servers][bStats shield]][bStats page]
|
||||
</div>
|
||||
|
||||
|
||||
## Download (Marketplace)
|
||||
You can visit [our marketplace][Plugin page] to download UltimateTimber as well as take a
|
||||
look at many other fantastic plugins which are sure to catch your eye.
|
||||
|
||||
## Documentation
|
||||
You can find all the information about UltimateTimber, including dependencies, commands, permissions and incompatible
|
||||
plugins on [our wiki][Plugin wiki].
|
||||
|
||||
Feel free to also contribute to the wiki as a way to help others in the community with using the plugin.
|
||||
|
||||
## Support
|
||||
If you encounter any issues while using the plugin, feel free to contact us on
|
||||
[our Discord server][Discord invite].
|
||||
|
||||
## Suggestions
|
||||
For suggestions about features you think should be added to the plugin to increase its functionality, feel free to
|
||||
create a thread over on [our Discord server][Discord invite].
|
||||
|
||||
|
||||
[Plugin page]: https://songoda.com/product/2
|
||||
[Plugin wiki]: https://songoda.notion.site/UltimateTimber-95103806d7f84353bb308335bd8ce37e
|
||||
[Discord invite]: https://discord.gg/7TXM8xr2Ng
|
||||
|
||||
[Discord shield]: https://img.shields.io/discord/1214289374506917889?color=5865F2&label=Discord&logo=discord&logoColor=5865F2
|
||||
[Latest version shield]: https://img.shields.io/badge/dynamic/xml?style=flat&color=blue&logo=github&logoColor=white&label=Latest&url=https%3A%2F%2Fraw.githubusercontent.com%2Fcraftaro%2FUltimateTimber%2Fmaster%2Fpom.xml&query=%2F*%5Blocal-name()%3D'project'%5D%2F*%5Blocal-name()%3D'version'%5D
|
||||
|
||||
[bStats page]: https://bstats.org/plugin/bukkit/UltimateTimber/4184
|
||||
[bStats shield]: https://img.shields.io/bstats/servers/4184?label=Servers
|
BIN
docs/Logo.png
Normal file
BIN
docs/Logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 177 KiB |
123
pom.xml
123
pom.xml
@ -4,51 +4,130 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ultimatetimber</groupId>
|
||||
<groupId>com.craftaro</groupId>
|
||||
<artifactId>UltimateTimber</artifactId>
|
||||
<version>0.0.3</version>
|
||||
<version>3.0.0</version>
|
||||
|
||||
<name>UltimateTimber</name>
|
||||
<description>Give your players a new and exciting way to chop down trees</description>
|
||||
<url>https://craftaro.com/marketplace/product/18</url>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.release>8</maven.compiler.release>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<issueManagement>
|
||||
<url>https://discord.gg/craftaro</url>
|
||||
<system>Discord server</system>
|
||||
</issueManagement>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/craftaro/UltimateTimber</url>
|
||||
<connection>scm:git:git://github.com/craftaro/UltimateTimber.git</connection>
|
||||
</scm>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<defaultGoal>clean resources:resources package</defaultGoal>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.6.2</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
|
||||
<configuration>
|
||||
<finalName>${project.name}-${project.version}</finalName>
|
||||
|
||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||
<useDependencyReducedPomInJar>true</useDependencyReducedPomInJar>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>com.craftaro.core</pattern>
|
||||
<shadedPattern>com.craftaro.ultimatetimber.core</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
|
||||
<excludes>
|
||||
<exclude>META-INF/**</exclude>
|
||||
<exclude>LICENSE</exclude>
|
||||
<exclude>LICENSE.**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
|
||||
<filter>
|
||||
<artifact>com.craftaro:CraftaroCore</artifact>
|
||||
<excludeDefaults>false</excludeDefaults>
|
||||
<includes>
|
||||
<include>**/nms/v*/**</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>**/third_party/org/apache/**</exclude>
|
||||
<exclude>**/third_party/net/kyori/**</exclude>
|
||||
<exclude>**/third_party/com/zaxxer/**</exclude>
|
||||
<exclude>**/third_party/org/jooq/**</exclude>
|
||||
<exclude>**/third_party/org/mariadb/**</exclude>
|
||||
<exclude>**/third_party/com/h2database/**</exclude>
|
||||
<exclude>**/third_party/org/h2/**</exclude>
|
||||
<exclude>**/third_party/com/cryptomorin/**</exclude>
|
||||
<exclude>**/third_party/org/reactivestreams/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<id>craftaro-minecraft-plugins</id>
|
||||
<url>https://repo.craftaro.com/repository/minecraft-plugins/</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>SpigotMC</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!--Spigot API-->
|
||||
<dependency>
|
||||
<groupId>com.craftaro</groupId>
|
||||
<artifactId>CraftaroCore</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- TODO: Check if spigot-api can be downgraded to 1.8 -->
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.13-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!--Bukkit API-->
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<version>1.13-R0.1-SNAPSHOT</version>
|
||||
<version>1.18-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
158
src/main/java/com/craftaro/ultimatetimber/UltimateTimber.java
Normal file
158
src/main/java/com/craftaro/ultimatetimber/UltimateTimber.java
Normal file
@ -0,0 +1,158 @@
|
||||
package com.craftaro.ultimatetimber;
|
||||
|
||||
import com.craftaro.core.SongodaCore;
|
||||
import com.craftaro.core.SongodaPlugin;
|
||||
import com.craftaro.core.commands.CommandManager;
|
||||
import com.craftaro.core.configuration.Config;
|
||||
import com.craftaro.core.dependency.Dependency;
|
||||
import com.craftaro.core.hooks.LogManager;
|
||||
import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial;
|
||||
import com.craftaro.ultimatetimber.commands.CommandGiveAxe;
|
||||
import com.craftaro.ultimatetimber.commands.CommandReload;
|
||||
import com.craftaro.ultimatetimber.commands.CommandToggle;
|
||||
import com.craftaro.ultimatetimber.manager.PlacedBlockManager;
|
||||
import com.craftaro.ultimatetimber.manager.TreeDetectionManager;
|
||||
import com.craftaro.ultimatetimber.manager.ChoppingManager;
|
||||
import com.craftaro.ultimatetimber.manager.ConfigurationManager;
|
||||
import com.craftaro.ultimatetimber.manager.Manager;
|
||||
import com.craftaro.ultimatetimber.manager.SaplingManager;
|
||||
import com.craftaro.ultimatetimber.manager.TreeAnimationManager;
|
||||
import com.craftaro.ultimatetimber.manager.TreeDefinitionManager;
|
||||
import com.craftaro.ultimatetimber.manager.TreeFallManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class UltimateTimber extends SongodaPlugin {
|
||||
private final Set<Manager> managers = new HashSet<>();
|
||||
|
||||
private ChoppingManager choppingManager;
|
||||
private ConfigurationManager configurationManager;
|
||||
private PlacedBlockManager placedBlockManager;
|
||||
private SaplingManager saplingManager;
|
||||
private TreeAnimationManager treeAnimationManager;
|
||||
private TreeDefinitionManager treeDefinitionManager;
|
||||
private TreeDetectionManager treeDetectionManager;
|
||||
private TreeFallManager treeFallManager;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getPlugin(Class)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static UltimateTimber getInstance() {
|
||||
return getPlugin(UltimateTimber.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<Dependency> getDependencies() {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginLoad() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginEnable() {
|
||||
// Run Songoda Updater
|
||||
SongodaCore.registerPlugin(this, 18, XMaterial.IRON_AXE);
|
||||
|
||||
// Load hooks
|
||||
LogManager.load();
|
||||
|
||||
// Setup plugin commands
|
||||
CommandManager commandManager = new CommandManager(this);
|
||||
commandManager.addMainCommand("ut")
|
||||
.addSubCommands(
|
||||
new CommandReload(this),
|
||||
new CommandToggle(this),
|
||||
new CommandGiveAxe(this)
|
||||
);
|
||||
|
||||
// Register managers
|
||||
this.choppingManager = this.registerManager(ChoppingManager.class);
|
||||
this.configurationManager = new ConfigurationManager(this);
|
||||
this.placedBlockManager = this.registerManager(PlacedBlockManager.class);
|
||||
this.saplingManager = this.registerManager(SaplingManager.class);
|
||||
this.treeAnimationManager = this.registerManager(TreeAnimationManager.class);
|
||||
this.treeDefinitionManager = this.registerManager(TreeDefinitionManager.class);
|
||||
this.treeDetectionManager = this.registerManager(TreeDetectionManager.class);
|
||||
this.treeFallManager = this.registerManager(TreeFallManager.class);
|
||||
|
||||
this.reloadConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginDisable() {
|
||||
this.configurationManager.disable();
|
||||
this.managers.forEach(Manager::disable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataLoad() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigReload() {
|
||||
this.configurationManager.reload();
|
||||
this.managers.forEach(Manager::reload);
|
||||
this.setLocale(getConfig().getString("locale"), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Config> getExtraConfig() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a manager
|
||||
*
|
||||
* @param managerClass The class of the manager to create a new instance of
|
||||
* @param <T> extends Manager
|
||||
* @return A new instance of the given manager class
|
||||
*/
|
||||
private <T extends Manager> T registerManager(Class<T> managerClass) {
|
||||
try {
|
||||
T newManager = managerClass.getConstructor(UltimateTimber.class).newInstance(this);
|
||||
this.managers.add(newManager);
|
||||
return newManager;
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ChoppingManager getChoppingManager() {
|
||||
return this.choppingManager;
|
||||
}
|
||||
|
||||
public ConfigurationManager getConfigurationManager() {
|
||||
return this.configurationManager;
|
||||
}
|
||||
|
||||
public PlacedBlockManager getPlacedBlockManager() {
|
||||
return this.placedBlockManager;
|
||||
}
|
||||
|
||||
public SaplingManager getSaplingManager() {
|
||||
return this.saplingManager;
|
||||
}
|
||||
|
||||
public TreeAnimationManager getTreeAnimationManager() {
|
||||
return this.treeAnimationManager;
|
||||
}
|
||||
|
||||
public TreeDefinitionManager getTreeDefinitionManager() {
|
||||
return this.treeDefinitionManager;
|
||||
}
|
||||
|
||||
public TreeDetectionManager getTreeDetectionManager() {
|
||||
return this.treeDetectionManager;
|
||||
}
|
||||
|
||||
public TreeFallManager getTreeFallManager() {
|
||||
return this.treeFallManager;
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
package com.craftaro.ultimatetimber.animation;
|
||||
|
||||
import com.craftaro.core.compatibility.CompatibleHand;
|
||||
import com.craftaro.core.compatibility.CompatibleMaterial;
|
||||
import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial;
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import com.craftaro.ultimatetimber.tree.DetectedTree;
|
||||
import com.craftaro.ultimatetimber.tree.FallingTreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlockSet;
|
||||
import com.craftaro.ultimatetimber.utils.BlockUtils;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public abstract class TreeAnimation {
|
||||
protected final TreeAnimationType treeAnimationType;
|
||||
protected final DetectedTree detectedTree;
|
||||
protected final Player player;
|
||||
protected final boolean hasSilkTouch;
|
||||
protected TreeBlockSet<FallingBlock> fallingTreeBlocks;
|
||||
|
||||
TreeAnimation(TreeAnimationType treeAnimationType, DetectedTree detectedTree, Player player) {
|
||||
this.treeAnimationType = treeAnimationType;
|
||||
this.detectedTree = detectedTree;
|
||||
this.player = player;
|
||||
|
||||
ItemStack itemInHand = CompatibleHand.getHand(CompatibleHand.MAIN_HAND).getItem(player);
|
||||
this.hasSilkTouch = itemInHand != null && itemInHand.hasItemMeta() && itemInHand.getItemMeta().hasEnchant(Enchantment.SILK_TOUCH);
|
||||
|
||||
this.fallingTreeBlocks = new TreeBlockSet<>(); // Should be overridden in any subclasses that need to use it
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays this tree topple animation
|
||||
*
|
||||
* @param whenFinished The runnable to run when the animation is done
|
||||
*/
|
||||
public abstract void playAnimation(Runnable whenFinished);
|
||||
|
||||
/**
|
||||
* Gets the type of tree animation that this is
|
||||
*
|
||||
* @return The TreeAnimationType
|
||||
*/
|
||||
public TreeAnimationType getTreeAnimationType() {
|
||||
return this.treeAnimationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the detected tree
|
||||
*
|
||||
* @return The detected tree
|
||||
*/
|
||||
public DetectedTree getDetectedTree() {
|
||||
return this.detectedTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player who started this tree animation
|
||||
*
|
||||
* @return The player who started this tree animation
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this tree animation has silk touch
|
||||
*
|
||||
* @return True if this animation has silk touch, otherwise false
|
||||
*/
|
||||
public boolean hasSilkTouch() {
|
||||
return this.hasSilkTouch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a TreeBlockSet of the active falling tree blocks
|
||||
* May return null if the animation type does not use falling blocks
|
||||
*
|
||||
* @return A tree block set
|
||||
*/
|
||||
public TreeBlockSet<FallingBlock> getFallingTreeBlocks() {
|
||||
return this.fallingTreeBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a TreeBlock into a FallingTreeBlock
|
||||
*
|
||||
* @param treeBlock The TreeBlock to convert
|
||||
* @return A FallingTreeBlock that has been converted from a TreeBlock
|
||||
*/
|
||||
protected FallingTreeBlock convertToFallingBlock(TreeBlock treeBlock) {
|
||||
Location location = treeBlock.getLocation().clone().add(0.5, 0, 0.5);
|
||||
Block block = treeBlock.getBlock();
|
||||
XMaterial material = CompatibleMaterial.getMaterial(block.getType()).get();
|
||||
if (CompatibleMaterial.isAir(material)) {
|
||||
this.replaceBlock(treeBlock);
|
||||
return null;
|
||||
}
|
||||
|
||||
FallingBlock fallingBlock = BlockUtils.spawnFallingBlock(location, material);
|
||||
BlockUtils.configureFallingBlock(fallingBlock);
|
||||
|
||||
FallingTreeBlock fallingTreeBlock = new FallingTreeBlock(fallingBlock, treeBlock.getTreeBlockType());
|
||||
this.replaceBlock(treeBlock);
|
||||
return fallingTreeBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a given block with a new one
|
||||
*
|
||||
* @param treeBlock The tree block to replace
|
||||
*/
|
||||
public void replaceBlock(TreeBlock treeBlock) {
|
||||
treeBlock.getBlock().setType(Material.AIR);
|
||||
UltimateTimber.getInstance().getSaplingManager().replantSapling(this.detectedTree.getTreeDefinition(), treeBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a falling block from the animation
|
||||
*
|
||||
* @param fallingBlock The FallingBlock to remove
|
||||
*/
|
||||
public void removeFallingBlock(FallingBlock fallingBlock) {
|
||||
for (ITreeBlock<FallingBlock> fallingTreeBlock : this.fallingTreeBlocks.getAllTreeBlocks()) {
|
||||
if (fallingTreeBlock.getBlock().equals(fallingBlock)) {
|
||||
this.fallingTreeBlocks.remove(fallingTreeBlock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package com.craftaro.ultimatetimber.animation;
|
||||
|
||||
import com.craftaro.core.compatibility.CompatibleMaterial;
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import com.craftaro.ultimatetimber.manager.ConfigurationManager;
|
||||
import com.craftaro.ultimatetimber.tree.DetectedTree;
|
||||
import com.craftaro.ultimatetimber.tree.FallingTreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlockSet;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlockType;
|
||||
import com.craftaro.ultimatetimber.tree.TreeDefinition;
|
||||
import com.craftaro.ultimatetimber.utils.BlockUtils;
|
||||
import com.craftaro.ultimatetimber.utils.ParticleUtils;
|
||||
import com.craftaro.ultimatetimber.utils.SoundUtils;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class TreeAnimationCrumble extends TreeAnimation {
|
||||
public TreeAnimationCrumble(DetectedTree detectedTree, Player player) {
|
||||
super(TreeAnimationType.CRUMBLE, detectedTree, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playAnimation(Runnable whenFinished) {
|
||||
UltimateTimber ultimateTimber = UltimateTimber.getInstance();
|
||||
|
||||
boolean useCustomSound = ConfigurationManager.Setting.USE_CUSTOM_SOUNDS.getBoolean();
|
||||
boolean useCustomParticles = ConfigurationManager.Setting.USE_CUSTOM_PARTICLES.getBoolean();
|
||||
|
||||
// Order blocks by y-axis, lowest first, but shuffled randomly
|
||||
int currentY = -1;
|
||||
List<List<ITreeBlock<Block>>> treeBlocks = new ArrayList<>();
|
||||
List<ITreeBlock<Block>> currentPartition = new ArrayList<>();
|
||||
List<ITreeBlock<Block>> orderedDetectedTreeBlocks = new ArrayList<>(this.detectedTree.getDetectedTreeBlocks().getAllTreeBlocks());
|
||||
orderedDetectedTreeBlocks.sort(Comparator.comparingInt(x -> x.getLocation().getBlockY()));
|
||||
for (ITreeBlock<Block> treeBlock : orderedDetectedTreeBlocks) {
|
||||
if (currentY != treeBlock.getLocation().getBlockY()) {
|
||||
Collections.shuffle(currentPartition);
|
||||
treeBlocks.add(new ArrayList<>(currentPartition));
|
||||
currentPartition.clear();
|
||||
currentY = treeBlock.getLocation().getBlockY();
|
||||
}
|
||||
currentPartition.add(treeBlock);
|
||||
}
|
||||
|
||||
Collections.shuffle(currentPartition);
|
||||
treeBlocks.add(new ArrayList<>(currentPartition));
|
||||
|
||||
TreeDefinition td = this.detectedTree.getTreeDefinition();
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!treeBlocks.isEmpty()) {
|
||||
List<ITreeBlock<Block>> partition = treeBlocks.get(0);
|
||||
for (int i = 0; i < 3 && !partition.isEmpty(); i++) {
|
||||
ITreeBlock<Block> treeBlock = partition.remove(0);
|
||||
if (treeBlock.getTreeBlockType() == TreeBlockType.LOG) {
|
||||
if (td.getLogMaterial().stream().noneMatch(x -> x == CompatibleMaterial.getMaterial(treeBlock.getBlock().getType()).orElse(null))) {
|
||||
continue;
|
||||
}
|
||||
} else if (treeBlock.getTreeBlockType() == TreeBlockType.LEAF) {
|
||||
if (td.getLeafMaterial().stream().noneMatch(x -> x == CompatibleMaterial.getMaterial(treeBlock.getBlock().getType()).orElse(null))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
FallingTreeBlock fallingTreeBlock = TreeAnimationCrumble.this.convertToFallingBlock((TreeBlock) treeBlock);
|
||||
if (fallingTreeBlock == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockUtils.toggleGravityFallingBlock(fallingTreeBlock.getBlock(), true);
|
||||
fallingTreeBlock.getBlock().setVelocity(Vector.getRandom().setY(0).subtract(new Vector(0.5, 0, 0.5)).multiply(0.15));
|
||||
TreeAnimationCrumble.this.fallingTreeBlocks.add(fallingTreeBlock);
|
||||
|
||||
if (TreeAnimationCrumble.this.fallingTreeBlocks == null) {
|
||||
TreeAnimationCrumble.this.fallingTreeBlocks = new TreeBlockSet<>(fallingTreeBlock);
|
||||
}
|
||||
|
||||
if (useCustomSound) {
|
||||
SoundUtils.playLandingSound(treeBlock);
|
||||
}
|
||||
if (useCustomParticles) {
|
||||
ParticleUtils.playFallingParticles(treeBlock);
|
||||
}
|
||||
}
|
||||
|
||||
if (partition.isEmpty()) {
|
||||
treeBlocks.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (treeBlocks.isEmpty() && TreeAnimationCrumble.this.fallingTreeBlocks.getAllTreeBlocks().isEmpty()) {
|
||||
whenFinished.run();
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(ultimateTimber, 0, 1);
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package com.craftaro.ultimatetimber.animation;
|
||||
|
||||
import com.craftaro.core.compatibility.CompatibleMaterial;
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import com.craftaro.ultimatetimber.manager.TreeDefinitionManager;
|
||||
import com.craftaro.ultimatetimber.manager.ConfigurationManager;
|
||||
import com.craftaro.ultimatetimber.tree.DetectedTree;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlockType;
|
||||
import com.craftaro.ultimatetimber.tree.TreeDefinition;
|
||||
import com.craftaro.ultimatetimber.utils.ParticleUtils;
|
||||
import com.craftaro.ultimatetimber.utils.SoundUtils;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class TreeAnimationDisintegrate extends TreeAnimation {
|
||||
public TreeAnimationDisintegrate(DetectedTree detectedTree, Player player) {
|
||||
super(TreeAnimationType.DISINTEGRATE, detectedTree, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playAnimation(Runnable whenFinished) {
|
||||
UltimateTimber ultimateTimber = UltimateTimber.getInstance();
|
||||
TreeDefinitionManager treeDefinitionManager = ultimateTimber.getTreeDefinitionManager();
|
||||
|
||||
boolean useCustomSound = ConfigurationManager.Setting.USE_CUSTOM_SOUNDS.getBoolean();
|
||||
boolean useCustomParticles = ConfigurationManager.Setting.USE_CUSTOM_PARTICLES.getBoolean();
|
||||
|
||||
List<ITreeBlock<Block>> orderedLogBlocks = new ArrayList<>(this.detectedTree.getDetectedTreeBlocks().getLogBlocks());
|
||||
orderedLogBlocks.sort(Comparator.comparingInt(x -> x.getLocation().getBlockY()));
|
||||
|
||||
List<ITreeBlock<Block>> leafBlocks = new ArrayList<>(this.detectedTree.getDetectedTreeBlocks().getLeafBlocks());
|
||||
Collections.shuffle(leafBlocks);
|
||||
|
||||
Player p = this.player;
|
||||
TreeDefinition td = this.detectedTree.getTreeDefinition();
|
||||
boolean hst = this.hasSilkTouch;
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<ITreeBlock<Block>> toDestroy = new ArrayList<>();
|
||||
|
||||
if (!orderedLogBlocks.isEmpty()) {
|
||||
ITreeBlock<Block> treeBlock = orderedLogBlocks.remove(0);
|
||||
toDestroy.add(treeBlock);
|
||||
} else if (!leafBlocks.isEmpty()) {
|
||||
ITreeBlock<Block> treeBlock = leafBlocks.remove(0);
|
||||
toDestroy.add(treeBlock);
|
||||
|
||||
if (!leafBlocks.isEmpty()) {
|
||||
treeBlock = leafBlocks.remove(0);
|
||||
toDestroy.add(treeBlock);
|
||||
}
|
||||
}
|
||||
|
||||
for (ITreeBlock<FallingBlock> fallingTreeBlock : TreeAnimationDisintegrate.this.fallingTreeBlocks.getAllTreeBlocks()) {
|
||||
FallingBlock fallingBlock = fallingTreeBlock.getBlock();
|
||||
fallingBlock.setVelocity(fallingBlock.getVelocity().clone().subtract(new Vector(0, 0.05, 0)));
|
||||
}
|
||||
|
||||
if (!toDestroy.isEmpty()) {
|
||||
ITreeBlock<Block> first = toDestroy.get(0);
|
||||
if (useCustomSound) {
|
||||
SoundUtils.playLandingSound(first);
|
||||
}
|
||||
|
||||
for (ITreeBlock<Block> treeBlock : toDestroy) {
|
||||
if (treeBlock.getTreeBlockType() == TreeBlockType.LOG) {
|
||||
if (td.getLogMaterial().stream().noneMatch(x -> x == CompatibleMaterial.getMaterial(treeBlock.getBlock().getType()).orElse(null))) {
|
||||
continue;
|
||||
}
|
||||
} else if (treeBlock.getTreeBlockType() == TreeBlockType.LEAF) {
|
||||
if (td.getLeafMaterial().stream().noneMatch(x -> x == CompatibleMaterial.getMaterial(treeBlock.getBlock().getType()).orElse(null))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (useCustomParticles) {
|
||||
ParticleUtils.playFallingParticles(treeBlock);
|
||||
}
|
||||
treeDefinitionManager.dropTreeLoot(td, treeBlock, p, hst, false);
|
||||
TreeAnimationDisintegrate.this.replaceBlock((TreeBlock) treeBlock);
|
||||
}
|
||||
} else {
|
||||
this.cancel();
|
||||
whenFinished.run();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(ultimateTimber, 0, 1);
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package com.craftaro.ultimatetimber.animation;
|
||||
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import com.craftaro.ultimatetimber.manager.ConfigurationManager;
|
||||
import com.craftaro.ultimatetimber.manager.TreeAnimationManager;
|
||||
import com.craftaro.ultimatetimber.tree.DetectedTree;
|
||||
import com.craftaro.ultimatetimber.tree.FallingTreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlockSet;
|
||||
import com.craftaro.ultimatetimber.utils.BlockUtils;
|
||||
import com.craftaro.ultimatetimber.utils.ParticleUtils;
|
||||
import com.craftaro.ultimatetimber.utils.SoundUtils;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class TreeAnimationFancy extends TreeAnimation {
|
||||
public TreeAnimationFancy(DetectedTree detectedTree, Player player) {
|
||||
super(TreeAnimationType.FANCY, detectedTree, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playAnimation(Runnable whenFinished) {
|
||||
UltimateTimber ultimateTimber = UltimateTimber.getInstance();
|
||||
|
||||
boolean useCustomSound = ConfigurationManager.Setting.USE_CUSTOM_SOUNDS.getBoolean();
|
||||
boolean useCustomParticles = ConfigurationManager.Setting.USE_CUSTOM_PARTICLES.getBoolean();
|
||||
|
||||
ITreeBlock<Block> initialTreeBlock = this.detectedTree.getDetectedTreeBlocks().getInitialLogBlock();
|
||||
FallingTreeBlock initialFallingBlock = this.convertToFallingBlock((TreeBlock) this.detectedTree.getDetectedTreeBlocks().getInitialLogBlock());
|
||||
|
||||
if (useCustomSound) {
|
||||
SoundUtils.playFallingSound(initialTreeBlock);
|
||||
}
|
||||
|
||||
Vector velocityVector = initialTreeBlock.getLocation().clone().subtract(this.player.getLocation().clone()).toVector().normalize().setY(0);
|
||||
|
||||
this.fallingTreeBlocks = new TreeBlockSet<>(initialFallingBlock);
|
||||
for (ITreeBlock<Block> treeBlock : this.detectedTree.getDetectedTreeBlocks().getAllTreeBlocks()) {
|
||||
FallingTreeBlock fallingTreeBlock = this.convertToFallingBlock((TreeBlock) treeBlock);
|
||||
if (fallingTreeBlock == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FallingBlock fallingBlock = fallingTreeBlock.getBlock();
|
||||
this.fallingTreeBlocks.add(fallingTreeBlock);
|
||||
|
||||
if (useCustomParticles) {
|
||||
ParticleUtils.playFallingParticles(treeBlock);
|
||||
}
|
||||
|
||||
double multiplier = (treeBlock.getLocation().getY() - this.player.getLocation().getY()) * 0.05;
|
||||
fallingBlock.setVelocity(velocityVector.clone().multiply(multiplier));
|
||||
fallingBlock.setVelocity(fallingBlock.getVelocity().multiply(0.3));
|
||||
}
|
||||
|
||||
new BukkitRunnable() {
|
||||
int timer = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (this.timer == 0) {
|
||||
for (ITreeBlock<FallingBlock> fallingTreeBlock : TreeAnimationFancy.this.fallingTreeBlocks.getAllTreeBlocks()) {
|
||||
FallingBlock fallingBlock = fallingTreeBlock.getBlock();
|
||||
BlockUtils.toggleGravityFallingBlock(fallingBlock, true);
|
||||
fallingBlock.setVelocity(fallingBlock.getVelocity().multiply(1.5));
|
||||
}
|
||||
}
|
||||
|
||||
if (TreeAnimationFancy.this.fallingTreeBlocks.getAllTreeBlocks().isEmpty()) {
|
||||
whenFinished.run();
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
for (ITreeBlock<FallingBlock> fallingTreeBlock : TreeAnimationFancy.this.fallingTreeBlocks.getAllTreeBlocks()) {
|
||||
FallingBlock fallingBlock = fallingTreeBlock.getBlock();
|
||||
fallingBlock.setVelocity(fallingBlock.getVelocity().clone().subtract(new Vector(0, 0.05, 0)));
|
||||
}
|
||||
|
||||
this.timer++;
|
||||
|
||||
if (this.timer > 4 * 20) {
|
||||
TreeAnimationManager treeAnimationManager = ultimateTimber.getTreeAnimationManager();
|
||||
for (ITreeBlock<FallingBlock> fallingTreeBlock : TreeAnimationFancy.this.fallingTreeBlocks.getAllTreeBlocks()) {
|
||||
treeAnimationManager.runFallingBlockImpact(TreeAnimationFancy.this, fallingTreeBlock);
|
||||
}
|
||||
whenFinished.run();
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(ultimateTimber, 20L, 1L);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.craftaro.ultimatetimber.animation;
|
||||
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import com.craftaro.ultimatetimber.manager.TreeDefinitionManager;
|
||||
import com.craftaro.ultimatetimber.manager.ConfigurationManager;
|
||||
import com.craftaro.ultimatetimber.tree.DetectedTree;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlock;
|
||||
import com.craftaro.ultimatetimber.utils.ParticleUtils;
|
||||
import com.craftaro.ultimatetimber.utils.SoundUtils;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class TreeAnimationNone extends TreeAnimation {
|
||||
public TreeAnimationNone(DetectedTree detectedTree, Player player) {
|
||||
super(TreeAnimationType.NONE, detectedTree, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playAnimation(Runnable whenFinished) {
|
||||
TreeDefinitionManager treeDefinitionManager = UltimateTimber.getInstance().getTreeDefinitionManager();
|
||||
|
||||
if (ConfigurationManager.Setting.USE_CUSTOM_SOUNDS.getBoolean())
|
||||
SoundUtils.playFallingSound(this.detectedTree.getDetectedTreeBlocks().getInitialLogBlock());
|
||||
|
||||
if (ConfigurationManager.Setting.USE_CUSTOM_PARTICLES.getBoolean())
|
||||
for (ITreeBlock<Block> treeBlock : this.detectedTree.getDetectedTreeBlocks().getAllTreeBlocks())
|
||||
ParticleUtils.playFallingParticles(treeBlock);
|
||||
|
||||
for (ITreeBlock<Block> treeBlock : this.detectedTree.getDetectedTreeBlocks().getAllTreeBlocks()) {
|
||||
treeDefinitionManager.dropTreeLoot(this.detectedTree.getTreeDefinition(), treeBlock, this.player, this.hasSilkTouch, false);
|
||||
this.replaceBlock((TreeBlock) treeBlock);
|
||||
}
|
||||
|
||||
whenFinished.run();
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.craftaro.ultimatetimber.animation;
|
||||
|
||||
/**
|
||||
* The types of tree animations that are available
|
||||
*/
|
||||
public enum TreeAnimationType {
|
||||
FANCY, DISINTEGRATE, CRUMBLE, NONE;
|
||||
|
||||
/**
|
||||
* Gets a TreeAnimationType from a given string
|
||||
*
|
||||
* @param string The string
|
||||
* @return The TreeAnimationType, returns FANCY if the string is an invalid type
|
||||
*/
|
||||
public static TreeAnimationType fromString(String string) {
|
||||
for (TreeAnimationType value : values()) {
|
||||
if (value.name().equalsIgnoreCase(string)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return TreeAnimationType.FANCY;
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package com.craftaro.ultimatetimber.commands;
|
||||
|
||||
import com.craftaro.core.commands.AbstractCommand;
|
||||
import com.craftaro.core.utils.PlayerUtils;
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CommandGiveAxe extends AbstractCommand {
|
||||
private final UltimateTimber plugin;
|
||||
|
||||
public CommandGiveAxe(UltimateTimber plugin) {
|
||||
super(CommandType.CONSOLE_OK, true, "give");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
if (args.length < 1) {
|
||||
return ReturnType.SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
Player player = Bukkit.getPlayer(args[0]);
|
||||
if (player == null) {
|
||||
if (args[0].trim().equalsIgnoreCase("me")) {
|
||||
if (!(sender instanceof Player)) {
|
||||
return ReturnType.NEEDS_PLAYER;
|
||||
}
|
||||
player = (Player) sender;
|
||||
} else {
|
||||
this.plugin.getLocale().getMessageOrDefault("command.give.not-a-player", "&cNot a player.")
|
||||
.sendPrefixedMessage(sender);
|
||||
return ReturnType.FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
ItemStack axe = this.plugin.getTreeDefinitionManager().getRequiredAxe();
|
||||
|
||||
if (axe == null) {
|
||||
this.plugin.getLocale().getMessageOrDefault("command.give.no-axe", "&cThe axe could not be loaded.")
|
||||
.sendPrefixedMessage(sender);
|
||||
return ReturnType.FAILURE;
|
||||
}
|
||||
|
||||
player.getInventory().addItem(axe);
|
||||
this.plugin.getLocale().getMessageOrDefault("command.give.given", "&fAxe given to &a%player%")
|
||||
.processPlaceholder("player", player.getName())
|
||||
.sendPrefixedMessage(sender);
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(CommandSender sender, String... args) {
|
||||
List<String> suggestions = null;
|
||||
if (args.length == 1) {
|
||||
suggestions = PlayerUtils.getVisiblePlayerNames(sender, args[0]);
|
||||
suggestions.add("me");
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "ultimatetimber.give";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "give <player/me>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Give a required axe.";
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.craftaro.ultimatetimber.commands;
|
||||
|
||||
import com.craftaro.core.commands.AbstractCommand;
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CommandReload extends AbstractCommand {
|
||||
private final UltimateTimber plugin;
|
||||
|
||||
public CommandReload(UltimateTimber plugin) {
|
||||
super(CommandType.CONSOLE_OK, "reload");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
this.plugin.reloadConfig();
|
||||
this.plugin.getLocale().getMessage("command.reload.reloaded").sendPrefixedMessage(sender);
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(CommandSender sender, String... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "ultimatetimber.reload";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "reload";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return this.plugin.getLocale().getMessage("command.reload.description").getMessage();
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.craftaro.ultimatetimber.commands;
|
||||
|
||||
import com.craftaro.core.commands.AbstractCommand;
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CommandToggle extends AbstractCommand {
|
||||
private final UltimateTimber plugin;
|
||||
|
||||
public CommandToggle(UltimateTimber plugin) {
|
||||
super(CommandType.CONSOLE_OK, "toggle");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
if (this.plugin.getChoppingManager().togglePlayer((Player) sender)) {
|
||||
this.plugin.getLocale().getMessage("command.toggle.enabled").sendPrefixedMessage(sender);
|
||||
} else {
|
||||
this.plugin.getLocale().getMessage("command.toggle.disabled").sendPrefixedMessage(sender);
|
||||
}
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(CommandSender sender, String... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "ultimatetimber.toggle";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "toggle";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return this.plugin.getLocale().getMessage("command.toggle.description").getMessage();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package com.craftaro.ultimatetimber.events;
|
||||
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
|
||||
public class TreeDamageEvent extends PlayerEvent implements Cancellable {
|
||||
|
||||
private boolean cancelled = false;
|
||||
private FallingBlock blockAttacker = null;
|
||||
private Player playerAttacker = null;
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
/**
|
||||
* Represents a TreeDamage event.
|
||||
*/
|
||||
public TreeDamageEvent(FallingBlock attacker, Player victim) {
|
||||
super(victim);
|
||||
this.blockAttacker = attacker;
|
||||
}
|
||||
|
||||
// Hoping this one is used whenever possible
|
||||
/**
|
||||
* Represents a TreeDamage event.
|
||||
*/
|
||||
public TreeDamageEvent(Player attacker, Player victim) {
|
||||
super(victim);
|
||||
this.playerAttacker = attacker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the attacker as either FallingBlock or Player
|
||||
*/
|
||||
public Entity getAttacker() {
|
||||
if (this.playerAttacker != null)
|
||||
return this.playerAttacker;
|
||||
if (this.blockAttacker != null)
|
||||
return this.blockAttacker;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Player damaged by this event. This method is only here for clarification
|
||||
*/
|
||||
public Player getVictim() {
|
||||
return this.getPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {return handlers;}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelState) {this.cancelled = cancelState;}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.craftaro.ultimatetimber.events;
|
||||
|
||||
import com.craftaro.ultimatetimber.tree.DetectedTree;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
|
||||
/**
|
||||
* Abstract tree event containing tree's blocks and broke block
|
||||
*/
|
||||
public abstract class TreeEvent extends PlayerEvent {
|
||||
protected final DetectedTree detectedTree;
|
||||
|
||||
public TreeEvent(Player who, DetectedTree detectedTree) {
|
||||
super(who);
|
||||
this.detectedTree = detectedTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tree blocks
|
||||
*
|
||||
* @return The blocks that are part of the tree
|
||||
*/
|
||||
public DetectedTree getDetectedTree() {
|
||||
return this.detectedTree;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.craftaro.ultimatetimber.events;
|
||||
|
||||
import com.craftaro.ultimatetimber.tree.DetectedTree;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* Called when a tree will fall
|
||||
*/
|
||||
public class TreeFallEvent extends TreeEvent implements Cancellable {
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
||||
private boolean cancelled = false;
|
||||
|
||||
public TreeFallEvent(Player who, DetectedTree detectedTree) {
|
||||
super(who, detectedTree);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLERS;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.craftaro.ultimatetimber.events;
|
||||
|
||||
import com.craftaro.ultimatetimber.tree.DetectedTree;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* Called when a tree fell
|
||||
*/
|
||||
public class TreeFellEvent extends TreeEvent {
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
||||
public TreeFellEvent(Player who, DetectedTree detectedTree) {
|
||||
super(who, detectedTree);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLERS;
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package com.craftaro.ultimatetimber.manager;
|
||||
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ChoppingManager extends Manager {
|
||||
private final Set<UUID> disabledPlayers;
|
||||
private final Map<UUID, Boolean> cooldownedPlayers;
|
||||
private boolean useCooldown;
|
||||
private int cooldownAmount;
|
||||
|
||||
public ChoppingManager(UltimateTimber plugin) {
|
||||
super(plugin);
|
||||
this.disabledPlayers = new HashSet<>();
|
||||
this.cooldownedPlayers = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
this.useCooldown = ConfigurationManager.Setting.PLAYER_TREE_TOPPLE_COOLDOWN.getBoolean();
|
||||
this.cooldownAmount = ConfigurationManager.Setting.PLAYER_TREE_TOPPLE_COOLDOWN_LENGTH.getInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.disabledPlayers.clear();
|
||||
this.cooldownedPlayers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a player's chopping status
|
||||
*
|
||||
* @param player The player to toggle
|
||||
* @return True if the player has chopping enabled, or false if they have it disabled
|
||||
*/
|
||||
public boolean togglePlayer(Player player) {
|
||||
if (this.disabledPlayers.contains(player.getUniqueId())) {
|
||||
this.disabledPlayers.remove(player.getUniqueId());
|
||||
return true;
|
||||
} else {
|
||||
this.disabledPlayers.add(player.getUniqueId());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player has chopping enabled
|
||||
*
|
||||
* @param player The player to check
|
||||
* @return True if the player has chopping enabled, or false if they have it disabled
|
||||
*/
|
||||
public boolean isChopping(Player player) {
|
||||
return !this.disabledPlayers.contains(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a player into cooldown
|
||||
*
|
||||
* @param player The player to cooldown
|
||||
*/
|
||||
public void cooldownPlayer(Player player) {
|
||||
if (!this.useCooldown || player.hasPermission("ultimatetimber.bypasscooldown"))
|
||||
return;
|
||||
|
||||
this.cooldownedPlayers.put(player.getUniqueId(), false);
|
||||
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(this.plugin, () -> this.cooldownedPlayers.remove(player.getUniqueId()), this.cooldownAmount * 20L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player is in cooldown
|
||||
*
|
||||
* @param player The player to check
|
||||
* @return True if the player can topple trees, otherwise false
|
||||
*/
|
||||
public boolean isInCooldown(Player player) {
|
||||
boolean cooldowned = this.useCooldown && this.cooldownedPlayers.containsKey(player.getUniqueId());
|
||||
if (cooldowned && !this.cooldownedPlayers.get(player.getUniqueId())) {
|
||||
this.plugin.getLocale().getMessage("event.on.cooldown").sendPrefixedMessage(player);
|
||||
this.cooldownedPlayers.replace(player.getUniqueId(), true);
|
||||
}
|
||||
return cooldowned;
|
||||
}
|
||||
}
|
@ -0,0 +1,211 @@
|
||||
package com.craftaro.ultimatetimber.manager;
|
||||
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class ConfigurationManager extends Manager {
|
||||
public enum Setting {
|
||||
SERVER_TYPE(SettingType.STRING),
|
||||
LOCALE(SettingType.STRING),
|
||||
DISABLED_WORLDS(SettingType.STRING_LIST),
|
||||
MAX_LOGS_PER_CHOP(SettingType.INT),
|
||||
DESTROY_LEAVES(SettingType.BOOLEAN),
|
||||
LEAVES_REQUIRED_FOR_TREE(SettingType.INT),
|
||||
REALISTIC_TOOL_DAMAGE(SettingType.BOOLEAN),
|
||||
PROTECT_TOOL(SettingType.BOOLEAN),
|
||||
APPLY_SILK_TOUCH(SettingType.BOOLEAN),
|
||||
APPLY_SILK_TOUCH_TOOL_DAMAGE(SettingType.BOOLEAN),
|
||||
ALWAYS_REPLANT_SAPLING(SettingType.BOOLEAN),
|
||||
BREAK_ENTIRE_TREE_BASE(SettingType.BOOLEAN),
|
||||
DESTROY_INITIATED_BLOCK(SettingType.BOOLEAN),
|
||||
ONLY_DETECT_LOGS_UPWARDS(SettingType.BOOLEAN),
|
||||
ONLY_TOPPLE_WHILE(SettingType.STRING),
|
||||
ALLOW_CREATIVE_MODE(SettingType.BOOLEAN),
|
||||
REQUIRE_CHOP_PERMISSION(SettingType.BOOLEAN),
|
||||
PLAYER_TREE_TOPPLE_COOLDOWN(SettingType.BOOLEAN),
|
||||
PLAYER_TREE_TOPPLE_COOLDOWN_LENGTH(SettingType.INT),
|
||||
IGNORE_REQUIRED_TOOLS(SettingType.BOOLEAN),
|
||||
REPLANT_SAPLINGS(SettingType.BOOLEAN),
|
||||
REPLANT_SAPLINGS_COOLDOWN(SettingType.INT),
|
||||
FALLING_BLOCKS_REPLANT_SAPLINGS(SettingType.BOOLEAN),
|
||||
FALLING_BLOCKS_REPLANT_SAPLINGS_CHANCE(SettingType.DOUBLE),
|
||||
FALLING_BLOCKS_DEAL_DAMAGE(SettingType.BOOLEAN),
|
||||
FALLING_BLOCK_DAMAGE(SettingType.INT),
|
||||
ADD_ITEMS_TO_INVENTORY(SettingType.BOOLEAN),
|
||||
USE_CUSTOM_SOUNDS(SettingType.BOOLEAN),
|
||||
USE_CUSTOM_PARTICLES(SettingType.BOOLEAN),
|
||||
BONUS_LOOT_MULTIPLIER(SettingType.DOUBLE),
|
||||
IGNORE_PLACED_BLOCKS(SettingType.BOOLEAN),
|
||||
IGNORE_PLACED_BLOCKS_MEMORY_SIZE(SettingType.INT),
|
||||
HOOKS_APPLY_EXPERIENCE(SettingType.BOOLEAN),
|
||||
HOOKS_APPLY_EXTRA_DROPS(SettingType.BOOLEAN),
|
||||
HOOKS_REQUIRE_ABILITY_ACTIVE(SettingType.BOOLEAN),
|
||||
TREE_ANIMATION_TYPE(SettingType.STRING),
|
||||
SCATTER_TREE_BLOCKS_ON_GROUND(SettingType.BOOLEAN),
|
||||
FRAGILE_BLOCKS(SettingType.STRING_LIST);
|
||||
|
||||
private final SettingType settingType;
|
||||
private Object value = null;
|
||||
|
||||
Setting(SettingType settingType) {
|
||||
this.settingType = settingType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the setting as a boolean
|
||||
*
|
||||
* @return The setting as a boolean
|
||||
*/
|
||||
public boolean getBoolean() {
|
||||
this.loadValue();
|
||||
return (boolean) this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the setting as an int
|
||||
*
|
||||
* @return The setting as an int
|
||||
*/
|
||||
public int getInt() {
|
||||
this.loadValue();
|
||||
return (int) this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the setting as a double
|
||||
*
|
||||
* @return The setting a double
|
||||
*/
|
||||
public double getDouble() {
|
||||
this.loadValue();
|
||||
return (double) this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the setting as a String
|
||||
*
|
||||
* @return The setting a String
|
||||
*/
|
||||
public String getString() {
|
||||
this.loadValue();
|
||||
return (String) this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the setting as a string list
|
||||
*
|
||||
* @return The setting as a string list
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<String> getStringList() {
|
||||
this.loadValue();
|
||||
return (List<String>) this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the cached value
|
||||
*/
|
||||
public void reset() {
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the value from the config and caches it if it isn't set yet
|
||||
*/
|
||||
private void loadValue() {
|
||||
if (this.value != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileConfiguration config = UltimateTimber.getPlugin(UltimateTimber.class).getConfigurationManager().getConfig();
|
||||
switch (this.settingType) {
|
||||
case BOOLEAN:
|
||||
this.value = config.getBoolean(this.getNameAsKey());
|
||||
break;
|
||||
case INT:
|
||||
this.value = config.getInt(this.getNameAsKey());
|
||||
break;
|
||||
case DOUBLE:
|
||||
this.value = config.getDouble(this.getNameAsKey());
|
||||
break;
|
||||
case STRING:
|
||||
this.value = config.getString(this.getNameAsKey());
|
||||
break;
|
||||
case STRING_LIST:
|
||||
this.value = config.getStringList(this.getNameAsKey());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this Setting as a FileConfiguration-compatible key
|
||||
*
|
||||
* @return The key for a FileConfiguration
|
||||
*/
|
||||
private String getNameAsKey() {
|
||||
return this.name().replace("_", "-").toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
private enum SettingType {
|
||||
BOOLEAN,
|
||||
INT,
|
||||
DOUBLE,
|
||||
STRING,
|
||||
STRING_LIST
|
||||
}
|
||||
|
||||
private YamlConfiguration configuration;
|
||||
|
||||
public ConfigurationManager(UltimateTimber plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
this.plugin.getCoreConfig().load();
|
||||
|
||||
File configFile = new File(this.plugin.getDataFolder() + "/config.yml");
|
||||
|
||||
// If an old config still exists, rename it, so it doesn't interfere
|
||||
if (configFile.exists() && this.plugin.getConfig().get("server-type") == null) {
|
||||
File renameConfigTo = new File(this.plugin.getDataFolder() + "/config-old.yml");
|
||||
configFile.renameTo(renameConfigTo);
|
||||
configFile = new File(this.plugin.getDataFolder() + "/config.yml");
|
||||
}
|
||||
|
||||
// Create the new config if it doesn't exist
|
||||
if (!configFile.exists()) {
|
||||
String newConfigName = "config.yml";
|
||||
File newConfigFile = new File(this.plugin.getDataFolder() + "/" + newConfigName);
|
||||
this.plugin.saveResource(newConfigName, false);
|
||||
newConfigFile.renameTo(configFile);
|
||||
}
|
||||
|
||||
this.configuration = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
for (Setting setting : Setting.values()) {
|
||||
setting.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
for (Setting setting : Setting.values()) {
|
||||
setting.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the config.yml as a YamlConfiguration
|
||||
*
|
||||
* @return The YamlConfiguration of the config.yml
|
||||
*/
|
||||
public YamlConfiguration getConfig() {
|
||||
return this.configuration;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.craftaro.ultimatetimber.manager;
|
||||
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
|
||||
public abstract class Manager {
|
||||
protected UltimateTimber plugin;
|
||||
|
||||
Manager(UltimateTimber plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the Manager's settings
|
||||
*/
|
||||
public abstract void reload();
|
||||
|
||||
/**
|
||||
* Cleans up the Manager's resources
|
||||
*/
|
||||
public abstract void disable();
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
package com.craftaro.ultimatetimber.manager;
|
||||
|
||||
import com.craftaro.core.compatibility.CompatibleMaterial;
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import com.craftaro.ultimatetimber.events.TreeFellEvent;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.block.LeavesDecayEvent;
|
||||
import org.bukkit.event.world.StructureGrowEvent;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class PlacedBlockManager extends Manager implements Listener {
|
||||
private Set<Location> placedBlocks;
|
||||
private boolean ignorePlacedBlocks;
|
||||
private int maxPlacedBlockMemorySize;
|
||||
|
||||
public PlacedBlockManager(UltimateTimber plugin) {
|
||||
super(plugin);
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
this.ignorePlacedBlocks = ConfigurationManager.Setting.IGNORE_PLACED_BLOCKS.getBoolean();
|
||||
this.maxPlacedBlockMemorySize = ConfigurationManager.Setting.IGNORE_PLACED_BLOCKS_MEMORY_SIZE.getInt();
|
||||
|
||||
// Ensures the oldest entry is removed if it exceeds the limit
|
||||
this.placedBlocks = Collections.newSetFromMap(new LinkedHashMap<Location, Boolean>() {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Location, Boolean> eldest) {
|
||||
return this.size() > PlacedBlockManager.this.maxPlacedBlockMemorySize;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.placedBlocks.clear();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockPlaced(BlockPlaceEvent event) {
|
||||
if (!this.ignorePlacedBlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore stripping logs
|
||||
if (event.getBlockPlaced().getType().name().contains("STRIPPED") && !CompatibleMaterial.isAir(CompatibleMaterial.getMaterial(event.getBlockReplacedState().getType()).get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.internalProtect(event.getBlock(), true);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockBreak(BlockBreakEvent event) {
|
||||
if (!this.ignorePlacedBlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.internalProtect(event.getBlock(), false);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onLeafDecay(LeavesDecayEvent event) {
|
||||
if (!this.ignorePlacedBlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.internalProtect(event.getBlock(), false);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onStructureGrow(StructureGrowEvent event) {
|
||||
if (!this.ignorePlacedBlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (BlockState blockState : event.getBlocks()) {
|
||||
this.internalProtect(blockState.getBlock(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onTreeFell(TreeFellEvent event) {
|
||||
if (!this.ignorePlacedBlocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ITreeBlock<Block> treeBlock : event.getDetectedTree().getDetectedTreeBlocks().getAllTreeBlocks()) {
|
||||
this.internalProtect(treeBlock.getBlock(), false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles when a block is placed/broken
|
||||
*/
|
||||
private void internalProtect(Block block, boolean isPlaced) {
|
||||
if (isPlaced) {
|
||||
if (this.isBlockPlaced(block)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.placedBlocks.add(block.getLocation());
|
||||
} else {
|
||||
this.placedBlocks.remove(block.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if a block is placed
|
||||
*
|
||||
* @param block The Block to check
|
||||
* @return True if the block is placed, otherwise false
|
||||
*/
|
||||
public boolean isBlockPlaced(Block block) {
|
||||
return this.placedBlocks.contains(block.getLocation());
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
package com.craftaro.ultimatetimber.manager;
|
||||
|
||||
import com.craftaro.core.compatibility.CompatibleMaterial;
|
||||
import com.craftaro.third_party.com.cryptomorin.xseries.XBlock;
|
||||
import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial;
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlockType;
|
||||
import com.craftaro.ultimatetimber.tree.TreeDefinition;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
public class SaplingManager extends Manager {
|
||||
private final Random random;
|
||||
private final Set<Location> protectedSaplings;
|
||||
|
||||
public SaplingManager(UltimateTimber plugin) {
|
||||
super(plugin);
|
||||
this.random = new Random();
|
||||
this.protectedSaplings = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Replants a sapling given a TreeDefinition and Location
|
||||
* Takes into account config settings
|
||||
*
|
||||
* @param treeDefinition The TreeDefinition of the sapling
|
||||
* @param treeBlock The ITreeBlock to replant for
|
||||
*/
|
||||
public void replantSapling(TreeDefinition treeDefinition, ITreeBlock treeBlock) {
|
||||
if (!ConfigurationManager.Setting.REPLANT_SAPLINGS.getBoolean()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Block block = treeBlock.getLocation().getBlock();
|
||||
if (block.getType() != Material.AIR || treeBlock.getTreeBlockType() == TreeBlockType.LEAF) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(this.plugin, () -> this.internalReplant(treeDefinition, treeBlock), 1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly replants a sapling given a TreeDefinition and Location
|
||||
* Takes into account config settings
|
||||
*
|
||||
* @param treeDefinition The TreeDefinition of the sapling
|
||||
* @param treeBlock The ITreeBlock to replant for
|
||||
*/
|
||||
public void replantSaplingWithChance(TreeDefinition treeDefinition, ITreeBlock treeBlock) {
|
||||
if (!ConfigurationManager.Setting.FALLING_BLOCKS_REPLANT_SAPLINGS.getBoolean() || !CompatibleMaterial.isAir(CompatibleMaterial.getMaterial(treeBlock.getLocation().getBlock().getType()).get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
double chance = ConfigurationManager.Setting.FALLING_BLOCKS_REPLANT_SAPLINGS_CHANCE.getDouble();
|
||||
if (this.random.nextDouble() > chance / 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(this.plugin, () -> this.internalReplant(treeDefinition, treeBlock), 1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replants a sapling given a TreeDefinition and Location
|
||||
*
|
||||
* @param treeDefinition The TreeDefinition of the sapling
|
||||
* @param treeBlock The ITreeBlock to replant for
|
||||
*/
|
||||
private void internalReplant(TreeDefinition treeDefinition, ITreeBlock treeBlock) {
|
||||
TreeDefinitionManager treeDefinitionManager = this.plugin.getTreeDefinitionManager();
|
||||
|
||||
Block block = treeBlock.getLocation().getBlock();
|
||||
Block blockBelow = block.getRelative(BlockFace.DOWN);
|
||||
boolean isValidSoil = false;
|
||||
for (XMaterial soilMaterial : treeDefinitionManager.getPlantableSoilMaterial(treeDefinition)) {
|
||||
if (soilMaterial == CompatibleMaterial.getMaterial(blockBelow.getType()).orElse(null)) {
|
||||
isValidSoil = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValidSoil) {
|
||||
return;
|
||||
}
|
||||
|
||||
XMaterial material = treeDefinition.getSaplingMaterial();
|
||||
XBlock.setType(block, material);
|
||||
|
||||
int cooldown = ConfigurationManager.Setting.REPLANT_SAPLINGS_COOLDOWN.getInt();
|
||||
if (cooldown != 0) {
|
||||
this.protectedSaplings.add(block.getLocation());
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(this.plugin, () -> this.protectedSaplings.remove(block.getLocation()), cooldown * 20L);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if a sapling is protected
|
||||
*
|
||||
* @param block The Block to check
|
||||
* @return True if the sapling is protected, otherwise false
|
||||
*/
|
||||
public boolean isSaplingProtected(Block block) {
|
||||
return this.protectedSaplings.contains(block.getLocation());
|
||||
}
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
package com.craftaro.ultimatetimber.manager;
|
||||
|
||||
import com.craftaro.core.compatibility.CompatibleMaterial;
|
||||
import com.craftaro.core.compatibility.ServerVersion;
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import com.craftaro.ultimatetimber.animation.TreeAnimation;
|
||||
import com.craftaro.ultimatetimber.animation.TreeAnimationCrumble;
|
||||
import com.craftaro.ultimatetimber.animation.TreeAnimationDisintegrate;
|
||||
import com.craftaro.ultimatetimber.animation.TreeAnimationFancy;
|
||||
import com.craftaro.ultimatetimber.animation.TreeAnimationNone;
|
||||
import com.craftaro.ultimatetimber.animation.TreeAnimationType;
|
||||
import com.craftaro.ultimatetimber.events.TreeDamageEvent;
|
||||
import com.craftaro.ultimatetimber.tree.DetectedTree;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeDefinition;
|
||||
import com.craftaro.ultimatetimber.utils.ParticleUtils;
|
||||
import com.craftaro.ultimatetimber.utils.SoundUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityChangeBlockEvent;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class TreeAnimationManager extends Manager implements Listener, Runnable {
|
||||
private final Set<TreeAnimation> activeAnimations;
|
||||
private final int taskId;
|
||||
|
||||
public TreeAnimationManager(UltimateTimber plugin) {
|
||||
super(plugin);
|
||||
this.activeAnimations = new HashSet<>();
|
||||
this.taskId = -1;
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
Bukkit.getScheduler().runTaskTimer(this.plugin, this, 0, 1L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
this.activeAnimations.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.activeAnimations.clear();
|
||||
Bukkit.getScheduler().cancelTask(this.taskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (TreeAnimation treeAnimation : this.activeAnimations) {
|
||||
Set<ITreeBlock<FallingBlock>> groundedBlocks = new HashSet<>();
|
||||
for (ITreeBlock<FallingBlock> fallingTreeBlock : treeAnimation.getFallingTreeBlocks().getAllTreeBlocks()) {
|
||||
FallingBlock fallingBlock = fallingTreeBlock.getBlock();
|
||||
if (fallingBlock.isDead() || ServerVersion.isServerVersionAtLeast(ServerVersion.V1_17) && fallingBlock.isOnGround()) {
|
||||
groundedBlocks.add(fallingTreeBlock);
|
||||
}
|
||||
}
|
||||
|
||||
for (ITreeBlock<FallingBlock> fallingBlock : groundedBlocks) {
|
||||
this.runFallingBlockImpact(treeAnimation, fallingBlock);
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_17)) {
|
||||
fallingBlock.getBlock().remove();
|
||||
}
|
||||
treeAnimation.getFallingTreeBlocks().remove(fallingBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays an animation for toppling a tree
|
||||
*
|
||||
* @param detectedTree The DetectedTree
|
||||
* @param player The Player who toppled the tree
|
||||
*/
|
||||
public void runAnimation(DetectedTree detectedTree, Player player) {
|
||||
switch (TreeAnimationType.fromString(ConfigurationManager.Setting.TREE_ANIMATION_TYPE.getString())) {
|
||||
case FANCY:
|
||||
this.registerTreeAnimation(new TreeAnimationFancy(detectedTree, player));
|
||||
break;
|
||||
case DISINTEGRATE:
|
||||
this.registerTreeAnimation(new TreeAnimationDisintegrate(detectedTree, player));
|
||||
break;
|
||||
case CRUMBLE:
|
||||
this.registerTreeAnimation(new TreeAnimationCrumble(detectedTree, player));
|
||||
break;
|
||||
case NONE:
|
||||
this.registerTreeAnimation(new TreeAnimationNone(detectedTree, player));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given block is in an animation
|
||||
*
|
||||
* @param block The block to check
|
||||
*/
|
||||
public boolean isBlockInAnimation(Block block) {
|
||||
for (TreeAnimation treeAnimation : this.activeAnimations) {
|
||||
for (ITreeBlock<Block> treeBlock : treeAnimation.getDetectedTree().getDetectedTreeBlocks().getAllTreeBlocks()) {
|
||||
if (treeBlock.getBlock().equals(block)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given falling block is in an animation
|
||||
*
|
||||
* @param fallingBlock The falling block to check
|
||||
*/
|
||||
public boolean isBlockInAnimation(FallingBlock fallingBlock) {
|
||||
for (TreeAnimation treeAnimation : this.activeAnimations) {
|
||||
for (ITreeBlock<FallingBlock> treeBlock : treeAnimation.getFallingTreeBlocks().getAllTreeBlocks()) {
|
||||
if (treeBlock.getBlock().equals(fallingBlock)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a TreeAnimation that a given falling block is in
|
||||
*
|
||||
* @return A TreeAnimation
|
||||
*/
|
||||
private TreeAnimation getAnimationForBlock(FallingBlock fallingBlock) {
|
||||
for (TreeAnimation treeAnimation : this.activeAnimations) {
|
||||
for (ITreeBlock<FallingBlock> treeBlock : treeAnimation.getFallingTreeBlocks().getAllTreeBlocks()) {
|
||||
if (treeBlock.getBlock().equals(fallingBlock)) {
|
||||
return treeAnimation;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers and runs a tree animation
|
||||
*/
|
||||
private void registerTreeAnimation(TreeAnimation treeAnimation) {
|
||||
this.activeAnimations.add(treeAnimation);
|
||||
treeAnimation.playAnimation(() -> this.activeAnimations.remove(treeAnimation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to a falling block hitting the ground
|
||||
*
|
||||
* @param treeAnimation The tree animation for the falling block
|
||||
* @param treeBlock The tree block to impact
|
||||
*/
|
||||
public void runFallingBlockImpact(TreeAnimation treeAnimation, ITreeBlock<FallingBlock> treeBlock) {
|
||||
TreeDefinitionManager treeDefinitionManager = this.plugin.getTreeDefinitionManager();
|
||||
boolean useCustomSound = ConfigurationManager.Setting.USE_CUSTOM_SOUNDS.getBoolean();
|
||||
boolean useCustomParticles = ConfigurationManager.Setting.USE_CUSTOM_PARTICLES.getBoolean();
|
||||
TreeDefinition treeDefinition = treeAnimation.getDetectedTree().getTreeDefinition();
|
||||
|
||||
if (useCustomParticles) {
|
||||
ParticleUtils.playLandingParticles(treeBlock);
|
||||
}
|
||||
if (useCustomSound) {
|
||||
SoundUtils.playLandingSound(treeBlock);
|
||||
}
|
||||
|
||||
Block block = treeBlock.getLocation().subtract(0, 1, 0).getBlock();
|
||||
if (ConfigurationManager.Setting.FRAGILE_BLOCKS.getStringList().contains(block.getType().toString())) {
|
||||
block.getWorld().dropItemNaturally(block.getLocation(), CompatibleMaterial.getMaterial(block.getType()).get().parseItem());
|
||||
block.breakNaturally();
|
||||
}
|
||||
|
||||
treeDefinitionManager.dropTreeLoot(treeDefinition, treeBlock, treeAnimation.getPlayer(), treeAnimation.hasSilkTouch(), false);
|
||||
this.plugin.getSaplingManager().replantSaplingWithChance(treeDefinition, treeBlock);
|
||||
treeAnimation.getFallingTreeBlocks().remove(treeBlock);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onFallingBlockLand(EntityChangeBlockEvent event) {
|
||||
if (event.getEntityType() != EntityType.FALLING_BLOCK) {
|
||||
return;
|
||||
}
|
||||
|
||||
FallingBlock fallingBlock = (FallingBlock) event.getEntity();
|
||||
if (!this.isBlockInAnimation(fallingBlock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConfigurationManager.Setting.FALLING_BLOCKS_DEAL_DAMAGE.getBoolean()) {
|
||||
int damage = ConfigurationManager.Setting.FALLING_BLOCK_DAMAGE.getInt();
|
||||
for (Entity entity : fallingBlock.getNearbyEntities(0.5, 0.5, 0.5)) {
|
||||
if (!(entity instanceof LivingEntity)) {
|
||||
continue;
|
||||
}
|
||||
if (entity instanceof Player) {
|
||||
Player p = ((Player) entity).getPlayer();
|
||||
TreeDamageEvent treeDamageEvent = new TreeDamageEvent(fallingBlock, p);
|
||||
Bukkit.getServer().getPluginManager().callEvent(treeDamageEvent);
|
||||
if (!treeDamageEvent.isCancelled())
|
||||
((LivingEntity) entity).damage(damage, fallingBlock);
|
||||
} else
|
||||
((LivingEntity) entity).damage(damage, fallingBlock);
|
||||
}
|
||||
}
|
||||
|
||||
if (ConfigurationManager.Setting.SCATTER_TREE_BLOCKS_ON_GROUND.getBoolean()) {
|
||||
TreeAnimation treeAnimation = this.getAnimationForBlock(fallingBlock);
|
||||
if (treeAnimation != null) {
|
||||
treeAnimation.removeFallingBlock(fallingBlock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
@ -0,0 +1,543 @@
|
||||
package com.craftaro.ultimatetimber.manager;
|
||||
|
||||
import com.craftaro.core.compatibility.CompatibleMaterial;
|
||||
import com.craftaro.core.compatibility.ServerVersion;
|
||||
import com.craftaro.core.hooks.McMMOHook;
|
||||
import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial;
|
||||
import com.craftaro.core.third_party.de.tr7zw.nbtapi.NBTItem;
|
||||
import com.craftaro.core.utils.TextUtils;
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import com.google.common.base.Strings;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlockType;
|
||||
import com.craftaro.ultimatetimber.tree.TreeDefinition;
|
||||
import com.craftaro.ultimatetimber.tree.TreeLoot;
|
||||
import com.craftaro.ultimatetimber.utils.BlockUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TreeDefinitionManager extends Manager {
|
||||
private final Random random;
|
||||
private final Set<TreeDefinition> treeDefinitions;
|
||||
private final Set<XMaterial> globalPlantableSoil;
|
||||
private final Set<TreeLoot> globalLogLoot, globalLeafLoot, globalEntireTreeLoot;
|
||||
private final Set<ItemStack> globalRequiredTools;
|
||||
|
||||
private boolean globalAxeRequired;
|
||||
private ItemStack requiredAxe;
|
||||
private String requiredAxeKey;
|
||||
|
||||
public TreeDefinitionManager(UltimateTimber plugin) {
|
||||
super(plugin);
|
||||
this.random = new Random();
|
||||
this.treeDefinitions = new HashSet<>();
|
||||
this.globalPlantableSoil = new HashSet<>();
|
||||
this.globalLogLoot = new HashSet<>();
|
||||
this.globalLeafLoot = new HashSet<>();
|
||||
this.globalEntireTreeLoot = new HashSet<>();
|
||||
this.globalRequiredTools = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
this.treeDefinitions.clear();
|
||||
this.globalPlantableSoil.clear();
|
||||
this.globalLogLoot.clear();
|
||||
this.globalLeafLoot.clear();
|
||||
this.globalEntireTreeLoot.clear();
|
||||
this.globalRequiredTools.clear();
|
||||
|
||||
ConfigurationManager configurationManager = this.plugin.getConfigurationManager();
|
||||
YamlConfiguration config = configurationManager.getConfig();
|
||||
|
||||
// Load tree settings
|
||||
ConfigurationSection treeSection = config.getConfigurationSection("trees");
|
||||
top:
|
||||
for (String key : treeSection.getKeys(false)) {
|
||||
ConfigurationSection tree = treeSection.getConfigurationSection(key);
|
||||
|
||||
Set<XMaterial> logMaterials = new HashSet<>();
|
||||
Set<XMaterial> leafMaterials = new HashSet<>();
|
||||
XMaterial saplingMaterial;
|
||||
Set<XMaterial> plantableSoilMaterial = new HashSet<>();
|
||||
double maxLogDistanceFromTrunk;
|
||||
int maxLeafDistanceFromLog;
|
||||
boolean detectLeavesDiagonally;
|
||||
boolean dropOriginalLog;
|
||||
boolean dropOriginalLeaf;
|
||||
Set<TreeLoot> logLoot = new HashSet<>();
|
||||
Set<TreeLoot> leafLoot = new HashSet<>();
|
||||
Set<TreeLoot> entireTreeLoot = new HashSet<>();
|
||||
Set<ItemStack> requiredTools = new HashSet<>();
|
||||
boolean requiredAxe;
|
||||
|
||||
for (String materialString : tree.getStringList("logs")) {
|
||||
Optional<XMaterial> material = CompatibleMaterial.getMaterial(materialString);
|
||||
if (!material.isPresent() || !material.get().isSupported()) {
|
||||
continue top;
|
||||
}
|
||||
logMaterials.add(material.get());
|
||||
}
|
||||
|
||||
for (String materialString : tree.getStringList("leaves")) {
|
||||
Optional<XMaterial> material = CompatibleMaterial.getMaterial(materialString);
|
||||
if (!material.isPresent() || !material.get().isSupported()) {
|
||||
continue top;
|
||||
}
|
||||
leafMaterials.add(material.get());
|
||||
}
|
||||
|
||||
saplingMaterial = CompatibleMaterial.getMaterial(tree.getString("sapling")).get();
|
||||
|
||||
for (String materialString : tree.getStringList("plantable-soil")) {
|
||||
Optional<XMaterial> material = CompatibleMaterial.getMaterial(materialString);
|
||||
if (!material.isPresent() || !material.get().isSupported()) {
|
||||
continue top;
|
||||
}
|
||||
plantableSoilMaterial.add(material.get());
|
||||
}
|
||||
|
||||
maxLogDistanceFromTrunk = tree.getDouble("max-log-distance-from-trunk");
|
||||
maxLeafDistanceFromLog = tree.getInt("max-leaf-distance-from-log");
|
||||
detectLeavesDiagonally = tree.getBoolean("search-for-leaves-diagonally");
|
||||
dropOriginalLog = tree.getBoolean("drop-original-log");
|
||||
dropOriginalLeaf = tree.getBoolean("drop-original-leaf");
|
||||
|
||||
ConfigurationSection logLootSection = tree.getConfigurationSection("log-loot");
|
||||
if (logLootSection != null) {
|
||||
for (String lootKey : logLootSection.getKeys(false)) {
|
||||
logLoot.add(this.getTreeLootEntry(TreeBlockType.LOG, logLootSection.getConfigurationSection(lootKey)));
|
||||
}
|
||||
}
|
||||
|
||||
ConfigurationSection leafLootSection = tree.getConfigurationSection("leaf-loot");
|
||||
if (leafLootSection != null) {
|
||||
for (String lootKey : leafLootSection.getKeys(false)) {
|
||||
leafLoot.add(this.getTreeLootEntry(TreeBlockType.LEAF, leafLootSection.getConfigurationSection(lootKey)));
|
||||
}
|
||||
}
|
||||
|
||||
ConfigurationSection entireTreeLootSection = tree.getConfigurationSection("entire-tree-loot");
|
||||
if (entireTreeLootSection != null) {
|
||||
for (String lootKey : entireTreeLootSection.getKeys(false)) {
|
||||
entireTreeLoot.add(this.getTreeLootEntry(TreeBlockType.LEAF, entireTreeLootSection.getConfigurationSection(lootKey)));
|
||||
}
|
||||
}
|
||||
|
||||
for (String itemStackString : tree.getStringList("required-tools")) {
|
||||
Optional<XMaterial> material = CompatibleMaterial.getMaterial(itemStackString);
|
||||
if (!material.isPresent()) {
|
||||
continue top;
|
||||
}
|
||||
requiredTools.add(material.get().parseItem());
|
||||
}
|
||||
|
||||
requiredAxe = tree.getBoolean("required-axe", false);
|
||||
|
||||
this.treeDefinitions.add(new TreeDefinition(key, logMaterials, leafMaterials, saplingMaterial, plantableSoilMaterial, maxLogDistanceFromTrunk,
|
||||
maxLeafDistanceFromLog, detectLeavesDiagonally, dropOriginalLog, dropOriginalLeaf, logLoot, leafLoot, entireTreeLoot, requiredTools, requiredAxe));
|
||||
}
|
||||
|
||||
// Load global plantable soil
|
||||
for (String material : config.getStringList("global-plantable-soil")) {
|
||||
this.globalPlantableSoil.add(CompatibleMaterial.getMaterial(material).get());
|
||||
}
|
||||
|
||||
// Load global log drops
|
||||
ConfigurationSection logSection = config.getConfigurationSection("global-log-loot");
|
||||
if (logSection != null) {
|
||||
for (String lootKey : logSection.getKeys(false)) {
|
||||
this.globalLogLoot.add(this.getTreeLootEntry(TreeBlockType.LOG, logSection.getConfigurationSection(lootKey)));
|
||||
}
|
||||
}
|
||||
|
||||
// Load global leaf drops
|
||||
ConfigurationSection leafSection = config.getConfigurationSection("global-leaf-loot");
|
||||
if (leafSection != null) {
|
||||
for (String lootKey : leafSection.getKeys(false)) {
|
||||
this.globalLeafLoot.add(this.getTreeLootEntry(TreeBlockType.LEAF, leafSection.getConfigurationSection(lootKey)));
|
||||
}
|
||||
}
|
||||
|
||||
// Load global entire tree drops
|
||||
ConfigurationSection entireTreeSection = config.getConfigurationSection("global-entire-tree-loot");
|
||||
if (entireTreeSection != null) {
|
||||
for (String lootKey : entireTreeSection.getKeys(false)) {
|
||||
this.globalEntireTreeLoot.add(this.getTreeLootEntry(TreeBlockType.LOG, entireTreeSection.getConfigurationSection(lootKey)));
|
||||
}
|
||||
}
|
||||
|
||||
// Load global tools
|
||||
for (String itemStackString : config.getStringList("global-required-tools")) {
|
||||
Optional<XMaterial> tool = CompatibleMaterial.getMaterial(itemStackString);
|
||||
if (!tool.isPresent()) {
|
||||
continue;
|
||||
}
|
||||
this.globalRequiredTools.add(tool.get().parseItem());
|
||||
}
|
||||
|
||||
this.globalAxeRequired = config.getBoolean("global-required-axe", false);
|
||||
|
||||
// Load required axe
|
||||
if (config.contains("required-axe")) {
|
||||
loadAxe(config);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAxe(YamlConfiguration config) {
|
||||
// Reset the axe loaded, but load the NBT anyway in case someone wanted to use another plugin to give it.
|
||||
this.requiredAxeKey = config.getString("required-axe.nbt");
|
||||
this.requiredAxe = null;
|
||||
|
||||
String typeString = config.getString("required-axe.type");
|
||||
|
||||
if (Strings.isNullOrEmpty(typeString)) {
|
||||
this.plugin.getLogger().warning("Required-axe has to have a material set.");
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<XMaterial> material = CompatibleMaterial.getMaterial(typeString);
|
||||
if (!material.isPresent()) {
|
||||
this.plugin.getLogger().warning("Material " + typeString + " is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack item = material.get().parseItem();
|
||||
|
||||
// Add display name and lore
|
||||
String displayName = TextUtils.formatText(config.getString("required-axe.name"));
|
||||
List<String> lore = config.getStringList("required-axe.lore").stream()
|
||||
.map(TextUtils::formatText)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
meta.setDisplayName(displayName);
|
||||
meta.setLore(lore);
|
||||
|
||||
// Enchants
|
||||
for (String enchantString : config.getStringList("required-axe.enchants")) {
|
||||
String[] arr = enchantString.split(":");
|
||||
int level = arr.length > 1 ? Math.max(1, parseInt(arr[1])) : 1;
|
||||
|
||||
// Enchantment#getKey is not present on versions below 1.13
|
||||
Enchantment enchantment;
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
|
||||
NamespacedKey key = NamespacedKey.minecraft(arr[0].trim().toLowerCase());
|
||||
enchantment = Enchantment.getByKey(key);
|
||||
|
||||
// Try to fall back to #getByName() if someone uses the old names.
|
||||
if (enchantment == null) {
|
||||
enchantment = Enchantment.getByName(arr[0].trim());
|
||||
}
|
||||
} else {
|
||||
enchantment = Enchantment.getByName(arr[0].trim());
|
||||
}
|
||||
|
||||
if (enchantment == null) {
|
||||
this.plugin.getLogger().warning("Enchantment " + arr[0].trim() + " is invalid.");
|
||||
continue;
|
||||
}
|
||||
|
||||
meta.addEnchant(enchantment, level, true);
|
||||
}
|
||||
|
||||
item.setItemMeta(meta);
|
||||
|
||||
// Apply NBT
|
||||
NBTItem nbtItem = new NBTItem(item);
|
||||
nbtItem.setBoolean(this.requiredAxeKey, true);
|
||||
item = nbtItem.getItem();
|
||||
|
||||
this.requiredAxe = item;
|
||||
}
|
||||
|
||||
private int parseInt(String str) {
|
||||
try {
|
||||
return Integer.parseInt(str.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public ItemStack getRequiredAxe() {
|
||||
return this.requiredAxe;
|
||||
}
|
||||
|
||||
public boolean isGlobalAxeRequired() {
|
||||
return this.globalAxeRequired;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.treeDefinitions.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Set of possible TreeDefinitions that match the given Block
|
||||
*
|
||||
* @param block The Block to check
|
||||
* @return A Set of TreeDefinitions for the given Block
|
||||
*/
|
||||
public Set<TreeDefinition> getTreeDefinitionsForLog(Block block) {
|
||||
return this.narrowTreeDefinition(this.treeDefinitions, block, TreeBlockType.LOG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Narrows a Set of TreeDefinitions down to ones matching the given Block and TreeBlockType
|
||||
*
|
||||
* @param possibleTreeDefinitions The possible TreeDefinitions
|
||||
* @param block The Block to narrow to
|
||||
* @param treeBlockType The TreeBlockType of the given Block
|
||||
* @return A Set of TreeDefinitions narrowed down
|
||||
*/
|
||||
public Set<TreeDefinition> narrowTreeDefinition(Set<TreeDefinition> possibleTreeDefinitions, Block block, TreeBlockType treeBlockType) {
|
||||
Set<TreeDefinition> matchingTreeDefinitions = new HashSet<>();
|
||||
switch (treeBlockType) {
|
||||
case LOG:
|
||||
for (TreeDefinition treeDefinition : possibleTreeDefinitions) {
|
||||
for (XMaterial material : treeDefinition.getLogMaterial()) {
|
||||
if (material == CompatibleMaterial.getMaterial(block.getType()).orElse(null)) {
|
||||
matchingTreeDefinitions.add(treeDefinition);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LEAF:
|
||||
for (TreeDefinition treeDefinition : possibleTreeDefinitions) {
|
||||
for (XMaterial material : treeDefinition.getLeafMaterial()) {
|
||||
if (material == CompatibleMaterial.getMaterial(block.getType()).orElse(null)) {
|
||||
matchingTreeDefinitions.add(treeDefinition);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return matchingTreeDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given tool is valid for any tree definitions, also takes into account global tools
|
||||
*
|
||||
* @param tool The tool to check
|
||||
* @return True if the tool is allowed for toppling any trees
|
||||
*/
|
||||
public boolean isToolValidForAnyTreeDefinition(ItemStack tool) {
|
||||
if (ConfigurationManager.Setting.IGNORE_REQUIRED_TOOLS.getBoolean()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (TreeDefinition treeDefinition : this.treeDefinitions) {
|
||||
if (treeDefinition.isRequiredAxe() || isGlobalAxeRequired()) {
|
||||
if (tool != null && !tool.getType().isAir() && new NBTItem(tool).hasTag(this.requiredAxeKey)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (TreeDefinition treeDefinition : this.treeDefinitions) {
|
||||
for (ItemStack requiredTool : treeDefinition.getRequiredTools()) {
|
||||
if (tool != null && requiredTool.getType() == tool.getType()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ItemStack requiredTool : this.globalRequiredTools) {
|
||||
if (tool != null && requiredTool.getType() == tool.getType()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given tool is valid for a given tree definition, also takes into account global tools
|
||||
*
|
||||
* @param treeDefinition The TreeDefinition to use
|
||||
* @param tool The tool to check
|
||||
* @return True if the tool is allowed for toppling the given TreeDefinition
|
||||
*/
|
||||
public boolean isToolValidForTreeDefinition(TreeDefinition treeDefinition, ItemStack tool) {
|
||||
if (ConfigurationManager.Setting.IGNORE_REQUIRED_TOOLS.getBoolean()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the tree definition requires the custom axe, don't allow any other checks to pass.
|
||||
if (treeDefinition.isRequiredAxe() || isGlobalAxeRequired()) {
|
||||
return tool != null && !tool.getType().isAir() && new NBTItem(tool).hasTag(this.requiredAxeKey);
|
||||
}
|
||||
|
||||
for (ItemStack requiredTool : treeDefinition.getRequiredTools()) {
|
||||
if (requiredTool.getType() == tool.getType()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (ItemStack requiredTool : this.globalRequiredTools) {
|
||||
if (requiredTool.getType() == tool.getType()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to spawn loot for a given TreeBlock with the given TreeDefinition for a given Player
|
||||
*
|
||||
* @param treeDefinition The TreeDefinition to use
|
||||
* @param treeBlock The TreeBlock to drop for
|
||||
* @param player The Player to drop for
|
||||
* @param isForEntireTree If the loot is for the entire tree
|
||||
*/
|
||||
public void dropTreeLoot(TreeDefinition treeDefinition, ITreeBlock treeBlock, Player player, boolean hasSilkTouch, boolean isForEntireTree) {
|
||||
boolean addToInventory = ConfigurationManager.Setting.ADD_ITEMS_TO_INVENTORY.getBoolean();
|
||||
boolean hasBonusChance = player.hasPermission("ultimatetimber.bonusloot");
|
||||
List<ItemStack> lootedItems = new ArrayList<>();
|
||||
List<String> lootedCommands = new ArrayList<>();
|
||||
|
||||
// Get the loot that we should try to drop
|
||||
List<TreeLoot> toTry = new ArrayList<>();
|
||||
if (isForEntireTree) {
|
||||
toTry.addAll(treeDefinition.getEntireTreeLoot());
|
||||
toTry.addAll(this.globalEntireTreeLoot);
|
||||
} else {
|
||||
if (ConfigurationManager.Setting.APPLY_SILK_TOUCH.getBoolean() && hasSilkTouch) {
|
||||
if (ConfigurationManager.Setting.HOOKS_APPLY_EXTRA_DROPS.getBoolean()
|
||||
&& McMMOHook.hasWoodcuttingDoubleDrops(player)) {
|
||||
lootedItems.addAll(BlockUtils.getBlockDrops(treeBlock));
|
||||
}
|
||||
lootedItems.addAll(BlockUtils.getBlockDrops(treeBlock));
|
||||
} else {
|
||||
switch (treeBlock.getTreeBlockType()) {
|
||||
case LOG:
|
||||
toTry.addAll(treeDefinition.getLogLoot());
|
||||
toTry.addAll(this.globalLogLoot);
|
||||
if (treeDefinition.shouldDropOriginalLog()) {
|
||||
if (ConfigurationManager.Setting.HOOKS_APPLY_EXTRA_DROPS.getBoolean()
|
||||
&& McMMOHook.hasWoodcuttingDoubleDrops(player)) {
|
||||
lootedItems.addAll(BlockUtils.getBlockDrops(treeBlock));
|
||||
}
|
||||
lootedItems.addAll(BlockUtils.getBlockDrops(treeBlock));
|
||||
}
|
||||
break;
|
||||
case LEAF:
|
||||
toTry.addAll(treeDefinition.getLeafLoot());
|
||||
toTry.addAll(this.globalLeafLoot);
|
||||
if (treeDefinition.shouldDropOriginalLeaf()) {
|
||||
if (ConfigurationManager.Setting.HOOKS_APPLY_EXTRA_DROPS.getBoolean()
|
||||
&& McMMOHook.hasWoodcuttingDoubleDrops(player)) {
|
||||
lootedItems.addAll(BlockUtils.getBlockDrops(treeBlock));
|
||||
}
|
||||
lootedItems.addAll(BlockUtils.getBlockDrops(treeBlock));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Roll the dice
|
||||
double bonusLootMultiplier = ConfigurationManager.Setting.BONUS_LOOT_MULTIPLIER.getDouble();
|
||||
for (TreeLoot treeLoot : toTry) {
|
||||
if (treeLoot == null) {
|
||||
continue;
|
||||
}
|
||||
double chance = hasBonusChance ? treeLoot.getChance() * bonusLootMultiplier : treeLoot.getChance();
|
||||
if (this.random.nextDouble() > chance / 100) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (treeLoot.hasItem()) {
|
||||
if (ConfigurationManager.Setting.HOOKS_APPLY_EXTRA_DROPS.getBoolean()
|
||||
&& McMMOHook.hasWoodcuttingDoubleDrops(player)) {
|
||||
lootedItems.add(treeLoot.getItem());
|
||||
}
|
||||
lootedItems.add(treeLoot.getItem());
|
||||
}
|
||||
|
||||
if (treeLoot.hasCommand()) {
|
||||
if (ConfigurationManager.Setting.HOOKS_APPLY_EXTRA_DROPS.getBoolean()
|
||||
&& McMMOHook.hasWoodcuttingDoubleDrops(player)) {
|
||||
lootedCommands.add(treeLoot.getCommand());
|
||||
}
|
||||
lootedCommands.add(treeLoot.getCommand());
|
||||
}
|
||||
}
|
||||
|
||||
// Add to inventory or drop on ground
|
||||
if (addToInventory && player.getWorld().equals(treeBlock.getLocation().getWorld())) {
|
||||
List<ItemStack> extraItems = new ArrayList<>();
|
||||
for (ItemStack lootedItem : lootedItems) {
|
||||
extraItems.addAll(player.getInventory().addItem(lootedItem).values());
|
||||
}
|
||||
Location location = player.getLocation().clone().subtract(0.5, 0, 0.5);
|
||||
for (ItemStack extraItem : extraItems) {
|
||||
location.getWorld().dropItemNaturally(location, extraItem);
|
||||
}
|
||||
} else {
|
||||
Location location = treeBlock.getLocation().clone().add(0.5, 0.5, 0.5);
|
||||
for (ItemStack lootedItem : lootedItems) {
|
||||
location.getWorld().dropItemNaturally(location, lootedItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Run looted commands
|
||||
for (String lootedCommand : lootedCommands) {
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(),
|
||||
lootedCommand.replace("%player%", player.getName())
|
||||
.replace("%type%", treeDefinition.getKey())
|
||||
.replace("%xPos%", String.valueOf(treeBlock.getLocation().getBlockX()))
|
||||
.replace("%yPos%", String.valueOf(treeBlock.getLocation().getBlockY()))
|
||||
.replace("%zPos%", String.valueOf(treeBlock.getLocation().getBlockZ())));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all possible plantable soil blocks for the given tree definition
|
||||
*
|
||||
* @param treeDefinition The TreeDefinition
|
||||
* @return A Set of IBlockData of plantable soil
|
||||
*/
|
||||
public Set<XMaterial> getPlantableSoilMaterial(TreeDefinition treeDefinition) {
|
||||
Set<XMaterial> plantableSoilBlockData = new HashSet<>();
|
||||
plantableSoilBlockData.addAll(treeDefinition.getPlantableSoilMaterial());
|
||||
plantableSoilBlockData.addAll(this.globalPlantableSoil);
|
||||
return plantableSoilBlockData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a TreeLoot entry from a ConfigurationSection
|
||||
*
|
||||
* @param treeBlockType The TreeBlockType to use
|
||||
* @param configurationSection The ConfigurationSection
|
||||
* @return A TreeLoot entry from the section
|
||||
*/
|
||||
private TreeLoot getTreeLootEntry(TreeBlockType treeBlockType, ConfigurationSection configurationSection) {
|
||||
String material = configurationSection.getString("material");
|
||||
Optional<XMaterial> compatibleMaterial = material == null ? Optional.empty() : CompatibleMaterial.getMaterial(material);
|
||||
|
||||
ItemStack item = compatibleMaterial.map(XMaterial::parseItem).orElse(null);
|
||||
String command = configurationSection.getString("command");
|
||||
double chance = configurationSection.getDouble("chance");
|
||||
return new TreeLoot(treeBlockType, item, command, chance);
|
||||
}
|
||||
}
|
@ -0,0 +1,323 @@
|
||||
package com.craftaro.ultimatetimber.manager;
|
||||
|
||||
import com.craftaro.core.compatibility.CompatibleMaterial;
|
||||
import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial;
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import com.craftaro.ultimatetimber.tree.DetectedTree;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlockSet;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlockType;
|
||||
import com.craftaro.ultimatetimber.tree.TreeDefinition;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class TreeDetectionManager extends Manager {
|
||||
private final Set<Vector> VALID_TRUNK_OFFSETS, VALID_BRANCH_OFFSETS, VALID_LEAF_OFFSETS;
|
||||
|
||||
private TreeDefinitionManager treeDefinitionManager;
|
||||
private PlacedBlockManager placedBlockManager;
|
||||
private int numLeavesRequiredForTree;
|
||||
private boolean onlyBreakLogsUpwards, entireTreeBase, destroyLeaves;
|
||||
|
||||
public TreeDetectionManager(UltimateTimber plugin) {
|
||||
super(plugin);
|
||||
|
||||
this.VALID_BRANCH_OFFSETS = new HashSet<>();
|
||||
this.VALID_TRUNK_OFFSETS = new HashSet<>();
|
||||
this.VALID_LEAF_OFFSETS = new HashSet<>();
|
||||
|
||||
// 3x2x3 centered around log, excluding -y axis
|
||||
for (int y = 0; y <= 1; y++) {
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int z = -1; z <= 1; z++) {
|
||||
this.VALID_BRANCH_OFFSETS.add(new Vector(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3x3x3 centered around log
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int z = -1; z <= 1; z++) {
|
||||
this.VALID_TRUNK_OFFSETS.add(new Vector(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adjacent blocks to log
|
||||
for (int i = -1; i <= 1; i += 2) {
|
||||
this.VALID_LEAF_OFFSETS.add(new Vector(i, 0, 0));
|
||||
this.VALID_LEAF_OFFSETS.add(new Vector(0, i, 0));
|
||||
this.VALID_LEAF_OFFSETS.add(new Vector(0, 0, i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
this.treeDefinitionManager = this.plugin.getTreeDefinitionManager();
|
||||
this.placedBlockManager = this.plugin.getPlacedBlockManager();
|
||||
this.numLeavesRequiredForTree = ConfigurationManager.Setting.LEAVES_REQUIRED_FOR_TREE.getInt();
|
||||
this.onlyBreakLogsUpwards = ConfigurationManager.Setting.ONLY_DETECT_LOGS_UPWARDS.getBoolean();
|
||||
this.entireTreeBase = ConfigurationManager.Setting.BREAK_ENTIRE_TREE_BASE.getBoolean();
|
||||
this.destroyLeaves = ConfigurationManager.Setting.DESTROY_LEAVES.getBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects a tree given an initial starting block
|
||||
*
|
||||
* @param initialBlock The starting Block of the detection
|
||||
* @return A DetectedTree if one was found, otherwise null
|
||||
*/
|
||||
public DetectedTree detectTree(Block initialBlock) {
|
||||
TreeDefinitionManager treeDefinitionManager = this.plugin.getTreeDefinitionManager();
|
||||
|
||||
TreeBlock initialTreeBlock = new TreeBlock(initialBlock, TreeBlockType.LOG);
|
||||
TreeBlockSet<Block> detectedTreeBlocks = new TreeBlockSet<>(initialTreeBlock);
|
||||
Set<TreeDefinition> possibleTreeDefinitions = this.treeDefinitionManager.getTreeDefinitionsForLog(initialBlock);
|
||||
|
||||
if (possibleTreeDefinitions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Detect tree trunk
|
||||
List<Block> trunkBlocks = new ArrayList<>();
|
||||
trunkBlocks.add(initialBlock);
|
||||
Block targetBlock = initialBlock;
|
||||
while (this.isValidLogType(possibleTreeDefinitions, null, (targetBlock = targetBlock.getRelative(BlockFace.UP)))) {
|
||||
trunkBlocks.add(targetBlock);
|
||||
possibleTreeDefinitions.retainAll(this.treeDefinitionManager.narrowTreeDefinition(possibleTreeDefinitions, targetBlock, TreeBlockType.LOG));
|
||||
}
|
||||
|
||||
if (!this.onlyBreakLogsUpwards) {
|
||||
targetBlock = initialBlock;
|
||||
while (this.isValidLogType(possibleTreeDefinitions, null, (targetBlock = targetBlock.getRelative(BlockFace.DOWN)))) {
|
||||
trunkBlocks.add(targetBlock);
|
||||
possibleTreeDefinitions.retainAll(this.treeDefinitionManager.narrowTreeDefinition(possibleTreeDefinitions, targetBlock, TreeBlockType.LOG));
|
||||
}
|
||||
}
|
||||
|
||||
// Lowest blocks at the front of the list
|
||||
Collections.reverse(trunkBlocks);
|
||||
|
||||
// Detect branches off the main trunk
|
||||
for (Block trunkBlock : trunkBlocks) {
|
||||
this.recursiveBranchSearch(possibleTreeDefinitions, trunkBlocks, detectedTreeBlocks, trunkBlock, initialBlock.getLocation().getBlockY());
|
||||
}
|
||||
|
||||
// Detect leaves off the trunk/branches
|
||||
Set<ITreeBlock<Block>> branchBlocks = new HashSet<>(detectedTreeBlocks.getLogBlocks());
|
||||
for (ITreeBlock<Block> branchBlock : branchBlocks) {
|
||||
this.recursiveLeafSearch(possibleTreeDefinitions, detectedTreeBlocks, branchBlock.getBlock(), new HashSet<>());
|
||||
}
|
||||
|
||||
// Use the first tree definition in the set
|
||||
TreeDefinition actualTreeDefinition = possibleTreeDefinitions.iterator().next();
|
||||
|
||||
// Trees need at least a certain number of leaves
|
||||
if (detectedTreeBlocks.getLeafBlocks().size() < this.numLeavesRequiredForTree) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove leaves if we don't care about the leaves
|
||||
if (!this.destroyLeaves) {
|
||||
detectedTreeBlocks.removeAll(TreeBlockType.LEAF);
|
||||
}
|
||||
|
||||
// Check that the tree isn't on the ground if enabled
|
||||
if (this.entireTreeBase) {
|
||||
Set<Block> groundBlocks = new HashSet<>();
|
||||
for (ITreeBlock<Block> treeBlock : detectedTreeBlocks.getLogBlocks()) {
|
||||
if (treeBlock != detectedTreeBlocks.getInitialLogBlock() && treeBlock.getLocation().getBlockY() == initialBlock.getLocation().getBlockY()) {
|
||||
groundBlocks.add(treeBlock.getBlock());
|
||||
}
|
||||
}
|
||||
|
||||
for (Block block : groundBlocks) {
|
||||
Block blockBelow = block.getRelative(BlockFace.DOWN);
|
||||
boolean blockBelowIsLog = this.isValidLogType(possibleTreeDefinitions, null, blockBelow);
|
||||
boolean blockBelowIsSoil = false;
|
||||
for (XMaterial material : treeDefinitionManager.getPlantableSoilMaterial(actualTreeDefinition)) {
|
||||
if (material == CompatibleMaterial.getMaterial(blockBelow.getType()).orElse(null)) {
|
||||
blockBelowIsSoil = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (blockBelowIsLog || blockBelowIsSoil) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new DetectedTree(actualTreeDefinition, detectedTreeBlocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively searches for branches off a given block
|
||||
*
|
||||
* @param treeDefinitions The possible tree definitions
|
||||
* @param trunkBlocks The tree trunk blocks
|
||||
* @param treeBlocks The detected tree blocks
|
||||
* @param block The next block to check for a branch
|
||||
* @param startingBlockY The Y coordinate of the initial block
|
||||
*/
|
||||
private void recursiveBranchSearch(Set<TreeDefinition> treeDefinitions, List<Block> trunkBlocks, TreeBlockSet<Block> treeBlocks, Block block, int startingBlockY) {
|
||||
for (Vector offset : this.onlyBreakLogsUpwards ? this.VALID_BRANCH_OFFSETS : this.VALID_TRUNK_OFFSETS) {
|
||||
Block targetBlock = block.getRelative(offset.getBlockX(), offset.getBlockY(), offset.getBlockZ());
|
||||
TreeBlock treeBlock = new TreeBlock(targetBlock, TreeBlockType.LOG);
|
||||
if (this.isValidLogType(treeDefinitions, trunkBlocks, targetBlock) && !treeBlocks.contains(treeBlock)) {
|
||||
treeBlocks.add(treeBlock);
|
||||
treeDefinitions.retainAll(this.treeDefinitionManager.narrowTreeDefinition(treeDefinitions, targetBlock, TreeBlockType.LOG));
|
||||
if (!this.onlyBreakLogsUpwards || targetBlock.getLocation().getBlockY() > startingBlockY) {
|
||||
this.recursiveBranchSearch(treeDefinitions, trunkBlocks, treeBlocks, targetBlock, startingBlockY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively searches for leaves that are next to this tree
|
||||
*
|
||||
* @param treeDefinitions The possible tree definitions
|
||||
* @param treeBlocks The detected tree blocks
|
||||
* @param block The next block to check for a leaf
|
||||
*/
|
||||
private void recursiveLeafSearch(Set<TreeDefinition> treeDefinitions, TreeBlockSet<Block> treeBlocks, Block block, Set<Block> visitedBlocks) {
|
||||
boolean detectLeavesDiagonally = treeDefinitions.stream().anyMatch(TreeDefinition::shouldDetectLeavesDiagonally);
|
||||
|
||||
for (Vector offset : !detectLeavesDiagonally ? this.VALID_LEAF_OFFSETS : this.VALID_TRUNK_OFFSETS) {
|
||||
Block targetBlock = block.getRelative(offset.getBlockX(), offset.getBlockY(), offset.getBlockZ());
|
||||
if (visitedBlocks.contains(targetBlock)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visitedBlocks.add(targetBlock);
|
||||
TreeBlock treeBlock = new TreeBlock(targetBlock, TreeBlockType.LEAF);
|
||||
if (this.isValidLeafType(treeDefinitions, treeBlocks, targetBlock) && !treeBlocks.contains(treeBlock) && !this.doesLeafBorderInvalidLog(treeDefinitions, treeBlocks, targetBlock)) {
|
||||
treeBlocks.add(treeBlock);
|
||||
treeDefinitions.retainAll(this.treeDefinitionManager.narrowTreeDefinition(treeDefinitions, targetBlock, TreeBlockType.LEAF));
|
||||
this.recursiveLeafSearch(treeDefinitions, treeBlocks, targetBlock, visitedBlocks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a leaf is bordering a log that isn't part of this tree
|
||||
*
|
||||
* @param treeDefinitions The possible tree definitions
|
||||
* @param treeBlocks The detected tree blocks
|
||||
* @param block The block to check
|
||||
* @return True if the leaf borders an invalid log, otherwise false
|
||||
*/
|
||||
private boolean doesLeafBorderInvalidLog(Set<TreeDefinition> treeDefinitions, TreeBlockSet<Block> treeBlocks, Block block) {
|
||||
for (Vector offset : this.VALID_TRUNK_OFFSETS) {
|
||||
Block targetBlock = block.getRelative(offset.getBlockX(), offset.getBlockY(), offset.getBlockZ());
|
||||
if (this.isValidLogType(treeDefinitions, null, targetBlock) && !treeBlocks.contains(new TreeBlock(targetBlock, TreeBlockType.LOG))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given block is valid for the given TreeDefinitions
|
||||
*
|
||||
* @param treeDefinitions The Set of TreeDefinitions to compare against
|
||||
* @param trunkBlocks The trunk blocks of the tree for checking the distance
|
||||
* @param block The Block to check
|
||||
* @return True if the block is a valid log type, otherwise false
|
||||
*/
|
||||
private boolean isValidLogType(Set<TreeDefinition> treeDefinitions, List<Block> trunkBlocks, Block block) {
|
||||
// Check if block is placed
|
||||
if (this.placedBlockManager.isBlockPlaced(block)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if it matches the tree definition
|
||||
boolean isCorrectType = false;
|
||||
for (TreeDefinition treeDefinition : treeDefinitions) {
|
||||
for (XMaterial material : treeDefinition.getLogMaterial()) {
|
||||
if (material == CompatibleMaterial.getMaterial(block.getType()).orElse(null)) {
|
||||
isCorrectType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCorrectType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that it is close enough to the trunk
|
||||
if (trunkBlocks == null || trunkBlocks.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Location location = block.getLocation();
|
||||
for (TreeDefinition treeDefinition : treeDefinitions) {
|
||||
double maxDistance = treeDefinition.getMaxLogDistanceFromTrunk() * treeDefinition.getMaxLogDistanceFromTrunk();
|
||||
if (!this.onlyBreakLogsUpwards) // Help detect logs more often if the tree isn't broken at the base
|
||||
{
|
||||
maxDistance *= 1.5;
|
||||
}
|
||||
for (Block trunkBlock : trunkBlocks) {
|
||||
if (location.distanceSquared(trunkBlock.getLocation()) < maxDistance) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given block is valid for the given TreeDefinitions
|
||||
*
|
||||
* @param treeDefinitions The Set of TreeDefinitions to compare against
|
||||
* @param treeBlocks The detected blocks of the tree for checking leaf distance
|
||||
* @param block The Block to check
|
||||
* @return True if the block is a valid log type, otherwise false
|
||||
*/
|
||||
private boolean isValidLeafType(Set<TreeDefinition> treeDefinitions, TreeBlockSet<Block> treeBlocks, Block block) {
|
||||
// Check if block is placed
|
||||
if (this.placedBlockManager.isBlockPlaced(block)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if it matches the tree definition
|
||||
boolean isCorrectType = false;
|
||||
for (TreeDefinition treeDefinition : treeDefinitions) {
|
||||
for (XMaterial material : treeDefinition.getLeafMaterial()) {
|
||||
if (material == CompatibleMaterial.getMaterial(block.getType()).orElse(null)) {
|
||||
isCorrectType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCorrectType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that it is close enough to a log
|
||||
if (treeBlocks == null || treeBlocks.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int maxDistanceFromLog = treeDefinitions.stream().map(TreeDefinition::getMaxLeafDistanceFromLog).max(Integer::compareTo).orElse(0);
|
||||
return treeBlocks.getLogBlocks().stream().anyMatch(x -> x.getLocation().distanceSquared(block.getLocation()) < maxDistanceFromLog * maxDistanceFromLog);
|
||||
}
|
||||
}
|
@ -0,0 +1,226 @@
|
||||
package com.craftaro.ultimatetimber.manager;
|
||||
|
||||
import com.craftaro.core.compatibility.CompatibleHand;
|
||||
import com.craftaro.core.compatibility.ServerVersion;
|
||||
import com.craftaro.core.hooks.JobsHook;
|
||||
import com.craftaro.core.hooks.LogManager;
|
||||
import com.craftaro.core.hooks.McMMOHook;
|
||||
import com.craftaro.core.utils.ItemUtils;
|
||||
import com.craftaro.core.world.SItemStack;
|
||||
import com.craftaro.ultimatetimber.UltimateTimber;
|
||||
import com.craftaro.ultimatetimber.events.TreeFallEvent;
|
||||
import com.craftaro.ultimatetimber.events.TreeFellEvent;
|
||||
import com.craftaro.ultimatetimber.tree.DetectedTree;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlockSet;
|
||||
import com.craftaro.ultimatetimber.misc.OnlyToppleWhile;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.Damageable;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TreeFallManager extends Manager implements Listener {
|
||||
private int maxLogBlocksAllowed;
|
||||
|
||||
public TreeFallManager(UltimateTimber plugin) {
|
||||
super(plugin);
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
this.maxLogBlocksAllowed = ConfigurationManager.Setting.MAX_LOGS_PER_CHOP.getInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onBlockBreak(BlockBreakEvent event) {
|
||||
TreeDefinitionManager treeDefinitionManager = this.plugin.getTreeDefinitionManager();
|
||||
TreeDetectionManager treeDetectionManager = this.plugin.getTreeDetectionManager();
|
||||
TreeAnimationManager treeAnimationManager = this.plugin.getTreeAnimationManager();
|
||||
ChoppingManager choppingManager = this.plugin.getChoppingManager();
|
||||
SaplingManager saplingManager = this.plugin.getSaplingManager();
|
||||
|
||||
Player player = event.getPlayer();
|
||||
Block block = event.getBlock();
|
||||
ItemStack tool = CompatibleHand.getHand(event).getItem(player);
|
||||
|
||||
// Protect saplings
|
||||
if (saplingManager.isSaplingProtected(block)) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Condition checks
|
||||
boolean isValid = true;
|
||||
|
||||
if (ConfigurationManager.Setting.DISABLED_WORLDS.getStringList().contains(player.getWorld().getName())) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (!ConfigurationManager.Setting.ALLOW_CREATIVE_MODE.getBoolean() && player.getGameMode().equals(GameMode.CREATIVE)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (!this.checkToppleWhile(player)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (ConfigurationManager.Setting.REQUIRE_CHOP_PERMISSION.getBoolean() && !player.hasPermission("ultimatetimber.chop")) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (!choppingManager.isChopping(player)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (choppingManager.isInCooldown(player)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (treeAnimationManager.isBlockInAnimation(block)) {
|
||||
isValid = false;
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
if (!treeDefinitionManager.isToolValidForAnyTreeDefinition(tool)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (ConfigurationManager.Setting.HOOKS_REQUIRE_ABILITY_ACTIVE.getBoolean() && !McMMOHook.isUsingTreeFeller(player)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
boolean alwaysReplantSapling = ConfigurationManager.Setting.ALWAYS_REPLANT_SAPLING.getBoolean();
|
||||
if (!isValid && !alwaysReplantSapling) {
|
||||
return;
|
||||
}
|
||||
|
||||
DetectedTree detectedTree = treeDetectionManager.detectTree(block);
|
||||
if (detectedTree == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (alwaysReplantSapling) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(this.plugin, () ->
|
||||
saplingManager.replantSapling(detectedTree.getTreeDefinition(), detectedTree.getDetectedTreeBlocks().getInitialLogBlock()));
|
||||
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!treeDefinitionManager.isToolValidForTreeDefinition(detectedTree.getTreeDefinition(), tool)) {
|
||||
return;
|
||||
}
|
||||
|
||||
short toolDamage = this.getToolDamage(detectedTree.getDetectedTreeBlocks(), tool.containsEnchantment(Enchantment.SILK_TOUCH));
|
||||
if (ConfigurationManager.Setting.PROTECT_TOOL.getBoolean() && !ItemUtils.hasEnoughDurability(tool, toolDamage)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Trigger fall event
|
||||
TreeFallEvent treeFallEvent = new TreeFallEvent(player, detectedTree);
|
||||
Bukkit.getPluginManager().callEvent(treeFallEvent);
|
||||
if (treeFallEvent.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Valid tree and meets all conditions past this point
|
||||
event.setCancelled(true);
|
||||
|
||||
detectedTree.getDetectedTreeBlocks().sortAndLimit(this.maxLogBlocksAllowed);
|
||||
|
||||
choppingManager.cooldownPlayer(player);
|
||||
|
||||
|
||||
// Destroy initiated block if enabled
|
||||
if (ConfigurationManager.Setting.DESTROY_INITIATED_BLOCK.getBoolean()) {
|
||||
detectedTree.getDetectedTreeBlocks().getInitialLogBlock().getBlock().setType(Material.AIR);
|
||||
detectedTree.getDetectedTreeBlocks().remove(detectedTree.getDetectedTreeBlocks().getInitialLogBlock());
|
||||
}
|
||||
|
||||
boolean isCreative = player.getGameMode() == GameMode.CREATIVE;
|
||||
|
||||
if (!isCreative) {
|
||||
SItemStack sstack = new SItemStack(tool);
|
||||
sstack.addDamage(player, toolDamage, true);
|
||||
|
||||
//Destroy item if durability is less than 0
|
||||
ItemMeta meta = sstack.getItem().getItemMeta();
|
||||
if (meta instanceof Damageable) {
|
||||
Damageable damageable = (Damageable)meta;
|
||||
int damage = damageable.getDamage();
|
||||
if (damage >= sstack.getItem().getType().getMaxDurability()) {
|
||||
//Break tool
|
||||
player.getInventory().setItemInMainHand(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ConfigurationManager.Setting.HOOKS_APPLY_EXPERIENCE.getBoolean()) {
|
||||
McMMOHook.addWoodcutting(player, detectedTree.getDetectedTreeBlocks().getAllTreeBlocks().stream()
|
||||
.map(ITreeBlock::getBlock).collect(Collectors.toList()));
|
||||
|
||||
if (!isCreative && JobsHook.isEnabled()) {
|
||||
for (ITreeBlock<Block> treeBlock : detectedTree.getDetectedTreeBlocks().getLogBlocks()) {
|
||||
JobsHook.breakBlock(player, treeBlock.getBlock());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ITreeBlock<Block> treeBlock : detectedTree.getDetectedTreeBlocks().getAllTreeBlocks()) {
|
||||
LogManager.logRemoval(player, treeBlock.getBlock());
|
||||
}
|
||||
|
||||
treeAnimationManager.runAnimation(detectedTree, player);
|
||||
treeDefinitionManager.dropTreeLoot(detectedTree.getTreeDefinition(), detectedTree.getDetectedTreeBlocks().getInitialLogBlock(), player, false, true);
|
||||
|
||||
// Trigger fell event
|
||||
TreeFellEvent treeFellEvent = new TreeFellEvent(player, detectedTree);
|
||||
Bukkit.getPluginManager().callEvent(treeFellEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player is doing a certain action required to topple a tree
|
||||
*
|
||||
* @param player The player to check
|
||||
* @return True if the check passes, otherwise false
|
||||
*/
|
||||
private boolean checkToppleWhile(Player player) {
|
||||
switch (OnlyToppleWhile.fromString(ConfigurationManager.Setting.ONLY_TOPPLE_WHILE.getString())) {
|
||||
case SNEAKING:
|
||||
return player.isSneaking();
|
||||
case NOT_SNEAKING:
|
||||
return !player.isSneaking();
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private short getToolDamage(TreeBlockSet<Block> treeBlocks, boolean hasSilkTouch) {
|
||||
if (!ConfigurationManager.Setting.REALISTIC_TOOL_DAMAGE.getBoolean()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ConfigurationManager.Setting.APPLY_SILK_TOUCH_TOOL_DAMAGE.getBoolean() && hasSilkTouch) {
|
||||
return (short) treeBlocks.size();
|
||||
} else {
|
||||
return (short) treeBlocks.getLogBlocks().size();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.craftaro.ultimatetimber.misc;
|
||||
|
||||
public enum OnlyToppleWhile {
|
||||
SNEAKING, NOT_SNEAKING, ALWAYS;
|
||||
|
||||
/**
|
||||
* Gets an OnlyToppleWhile from a given string
|
||||
*
|
||||
* @return The TreeAnimationType, returns {@link #ALWAYS} if the string is an invalid type
|
||||
*/
|
||||
public static OnlyToppleWhile fromString(String string) {
|
||||
for (OnlyToppleWhile value : values()) {
|
||||
if (value.name().equalsIgnoreCase(string)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return OnlyToppleWhile.ALWAYS;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.craftaro.ultimatetimber.tree;
|
||||
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
public class DetectedTree {
|
||||
private final TreeDefinition treeDefinition;
|
||||
private final TreeBlockSet<Block> detectedTreeBlocks;
|
||||
|
||||
public DetectedTree(TreeDefinition treeDefinition, TreeBlockSet<Block> detectedTreeBlocks) {
|
||||
this.treeDefinition = treeDefinition;
|
||||
this.detectedTreeBlocks = detectedTreeBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the TreeDefinition of this detected tree
|
||||
*
|
||||
* @return The TreeDefinition of this detected tree
|
||||
*/
|
||||
public TreeDefinition getTreeDefinition() {
|
||||
return this.treeDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the blocks that were detected as part of this tree
|
||||
*
|
||||
* @return A TreeBlockSet of detected Blocks
|
||||
*/
|
||||
public TreeBlockSet<Block> getDetectedTreeBlocks() {
|
||||
return this.detectedTreeBlocks;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.craftaro.ultimatetimber.tree;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
|
||||
public class FallingTreeBlock implements ITreeBlock<FallingBlock> {
|
||||
private final FallingBlock fallingBlock;
|
||||
private final TreeBlockType treeBlockType;
|
||||
|
||||
public FallingTreeBlock(FallingBlock fallingBlock, TreeBlockType treeBlockType) {
|
||||
this.fallingBlock = fallingBlock;
|
||||
this.treeBlockType = treeBlockType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FallingBlock getBlock() {
|
||||
return this.fallingBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return this.fallingBlock.getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeBlockType getTreeBlockType() {
|
||||
return this.treeBlockType;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.craftaro.ultimatetimber.tree;
|
||||
|
||||
import org.bukkit.Location;
|
||||
|
||||
public interface ITreeBlock<BlockType> {
|
||||
/**
|
||||
* Gets the block this TreeBlock represents
|
||||
*
|
||||
* @return The Block for this TreeBlock
|
||||
*/
|
||||
BlockType getBlock();
|
||||
|
||||
/**
|
||||
* Gets the location of this TreeBlock
|
||||
*
|
||||
* @return The Location of this TreeBlock
|
||||
*/
|
||||
Location getLocation();
|
||||
|
||||
/**
|
||||
* Gets what type of TreeBlock this is
|
||||
*
|
||||
* @return The TreeBlockType
|
||||
*/
|
||||
TreeBlockType getTreeBlockType();
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.craftaro.ultimatetimber.tree;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class TreeBlock implements ITreeBlock<Block> {
|
||||
private final Block block;
|
||||
private final TreeBlockType treeBlockType;
|
||||
|
||||
public TreeBlock(Block block, TreeBlockType treeBlockType) {
|
||||
this.block = block;
|
||||
this.treeBlockType = treeBlockType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getBlock() {
|
||||
return this.block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return this.block.getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeBlockType getTreeBlockType() {
|
||||
return this.treeBlockType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.block, this.treeBlockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof TreeBlock)) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TreeBlock oTreeBlock = (TreeBlock) obj;
|
||||
return oTreeBlock.block.equals(this.block) && oTreeBlock.treeBlockType == this.treeBlockType;
|
||||
}
|
||||
}
|
233
src/main/java/com/craftaro/ultimatetimber/tree/TreeBlockSet.java
Normal file
233
src/main/java/com/craftaro/ultimatetimber/tree/TreeBlockSet.java
Normal file
@ -0,0 +1,233 @@
|
||||
package com.craftaro.ultimatetimber.tree;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TreeBlockSet<BlockType> implements Collection {
|
||||
private final ITreeBlock<BlockType> initialLogBlock;
|
||||
private List<ITreeBlock<BlockType>> logBlocks;
|
||||
private final List<ITreeBlock<BlockType>> leafBlocks;
|
||||
|
||||
public TreeBlockSet() {
|
||||
this.initialLogBlock = null;
|
||||
this.logBlocks = new LinkedList<>();
|
||||
this.leafBlocks = new LinkedList<>();
|
||||
}
|
||||
|
||||
public TreeBlockSet(ITreeBlock<BlockType> initialLogBlock) {
|
||||
this.initialLogBlock = initialLogBlock;
|
||||
this.logBlocks = new LinkedList<>();
|
||||
this.leafBlocks = new LinkedList<>();
|
||||
|
||||
if (initialLogBlock != null) {
|
||||
this.logBlocks.add(initialLogBlock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the TreeBlock that initiated the tree topple
|
||||
*
|
||||
* @return The TreeBlock of the initial topple point
|
||||
*/
|
||||
public ITreeBlock<BlockType> getInitialLogBlock() {
|
||||
return this.initialLogBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all logs in this TreeBlockSet
|
||||
*
|
||||
* @return A Set of TreeBlocks
|
||||
*/
|
||||
public List<ITreeBlock<BlockType>> getLogBlocks() {
|
||||
return Collections.unmodifiableList(this.logBlocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all leaves in this TreeBlockSet
|
||||
*
|
||||
* @return A Set of TreeBlocks
|
||||
*/
|
||||
public List<ITreeBlock<BlockType>> getLeafBlocks() {
|
||||
return Collections.unmodifiableList(this.leafBlocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all blocks in this TreeBlockSet
|
||||
*
|
||||
* @return A Set of all TreeBlocks
|
||||
*/
|
||||
public Set<ITreeBlock<BlockType>> getAllTreeBlocks() {
|
||||
Set<ITreeBlock<BlockType>> treeBlocks = new HashSet<>();
|
||||
treeBlocks.addAll(this.logBlocks);
|
||||
treeBlocks.addAll(this.leafBlocks);
|
||||
return treeBlocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.logBlocks.size() + this.leafBlocks.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.logBlocks.isEmpty() && this.leafBlocks.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return this.logBlocks.contains(o) || this.leafBlocks.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator iterator() {
|
||||
return this.getAllTreeBlocks().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return this.getAllTreeBlocks().toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean add(Object o) {
|
||||
if (!(o instanceof ITreeBlock)) {
|
||||
return false;
|
||||
}
|
||||
ITreeBlock treeBlock = (ITreeBlock) o;
|
||||
switch (treeBlock.getTreeBlockType()) {
|
||||
case LOG:
|
||||
return this.logBlocks.add(treeBlock);
|
||||
case LEAF:
|
||||
return this.leafBlocks.add(treeBlock);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
if (!(o instanceof ITreeBlock)) {
|
||||
return false;
|
||||
}
|
||||
ITreeBlock treeBlock = (ITreeBlock) o;
|
||||
switch (treeBlock.getTreeBlockType()) {
|
||||
case LOG:
|
||||
return this.logBlocks.remove(treeBlock);
|
||||
case LEAF:
|
||||
return this.leafBlocks.remove(treeBlock);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection c) {
|
||||
boolean allAdded = true;
|
||||
for (Object o : c) {
|
||||
if (!this.add(o)) {
|
||||
allAdded = false;
|
||||
}
|
||||
}
|
||||
return allAdded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.logBlocks.clear();
|
||||
this.leafBlocks.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection c) {
|
||||
boolean retainedAll = true;
|
||||
for (Object o : c) {
|
||||
if (!this.contains(o)) {
|
||||
this.remove(o);
|
||||
} else {
|
||||
retainedAll = false;
|
||||
}
|
||||
}
|
||||
return retainedAll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection c) {
|
||||
boolean removedAll = true;
|
||||
for (Object o : c) {
|
||||
if (this.contains(o)) {
|
||||
this.remove(o);
|
||||
} else {
|
||||
removedAll = false;
|
||||
}
|
||||
}
|
||||
return removedAll;
|
||||
}
|
||||
|
||||
public void sortAndLimit(int max) {
|
||||
if (this.logBlocks.size() < max) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logBlocks = this.logBlocks.stream().sorted(Comparator.comparingInt(b -> b.getLocation().getBlockY()))
|
||||
.limit(max).collect(Collectors.toList());
|
||||
|
||||
int highest = this.logBlocks.get(this.logBlocks.size() - 1).getLocation().getBlockY();
|
||||
|
||||
if (this.logBlocks.size() >= max) {
|
||||
for (ITreeBlock<BlockType> leafBlock : new LinkedList<>(this.leafBlocks)) {
|
||||
if (leafBlock.getLocation().getY() > highest) {
|
||||
this.leafBlocks.remove(leafBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all tree blocks of a given type
|
||||
*
|
||||
* @param treeBlockType The type of tree block to remove
|
||||
* @return If any blocks were removed
|
||||
*/
|
||||
public boolean removeAll(TreeBlockType treeBlockType) {
|
||||
if (treeBlockType == TreeBlockType.LOG) {
|
||||
boolean removedAny = !this.logBlocks.isEmpty();
|
||||
this.logBlocks.clear();
|
||||
return removedAny;
|
||||
}
|
||||
if (treeBlockType == TreeBlockType.LEAF) {
|
||||
boolean removedAny = !this.leafBlocks.isEmpty();
|
||||
this.leafBlocks.clear();
|
||||
return removedAny;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection c) {
|
||||
for (Object o : c) {
|
||||
if (!this.contains(o)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object[] toArray(Object[] a) {
|
||||
Set<ITreeBlock<BlockType>> treeBlocks = new HashSet<>();
|
||||
for (Object o : a) {
|
||||
if (o instanceof ITreeBlock) {
|
||||
treeBlocks.add((ITreeBlock<BlockType>) o);
|
||||
}
|
||||
}
|
||||
|
||||
return treeBlocks.toArray();
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.craftaro.ultimatetimber.tree;
|
||||
|
||||
public enum TreeBlockType {
|
||||
LOG, LEAF
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
package com.craftaro.ultimatetimber.tree;
|
||||
|
||||
import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public class TreeDefinition {
|
||||
private final String key;
|
||||
private final Set<XMaterial> logMaterial, leafMaterial, plantableSoilMaterial;
|
||||
private final XMaterial saplingMaterial;
|
||||
private final double maxLogDistanceFromTrunk;
|
||||
private final int maxLeafDistanceFromLog;
|
||||
private final boolean detectLeavesDiagonally;
|
||||
private final boolean dropOriginalLog, dropOriginalLeaf;
|
||||
private final Set<TreeLoot> logLoot, leafLoot, entireTreeLoot;
|
||||
private final Set<ItemStack> requiredTools;
|
||||
private final boolean requiredAxe;
|
||||
|
||||
public TreeDefinition(String key, Set<XMaterial> logMaterial, Set<XMaterial> leafMaterial, XMaterial saplingMaterial,
|
||||
Set<XMaterial> plantableSoilMaterial, double maxLogDistanceFromTrunk, int maxLeafDistanceFromLog,
|
||||
boolean detectLeavesDiagonally, boolean dropOriginalLog, boolean dropOriginalLeaf, Set<TreeLoot> logLoot,
|
||||
Set<TreeLoot> leafLoot, Set<TreeLoot> entireTreeLoot, Set<ItemStack> requiredTools, boolean requiredAxe) {
|
||||
this.key = key;
|
||||
this.logMaterial = logMaterial;
|
||||
this.leafMaterial = leafMaterial;
|
||||
this.saplingMaterial = saplingMaterial;
|
||||
this.plantableSoilMaterial = plantableSoilMaterial;
|
||||
this.maxLogDistanceFromTrunk = maxLogDistanceFromTrunk;
|
||||
this.maxLeafDistanceFromLog = maxLeafDistanceFromLog;
|
||||
this.detectLeavesDiagonally = detectLeavesDiagonally;
|
||||
this.dropOriginalLog = dropOriginalLog;
|
||||
this.dropOriginalLeaf = dropOriginalLeaf;
|
||||
this.logLoot = logLoot;
|
||||
this.leafLoot = leafLoot;
|
||||
this.entireTreeLoot = entireTreeLoot;
|
||||
this.requiredTools = requiredTools;
|
||||
this.requiredAxe = requiredAxe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key of this TreeDefinition in the config
|
||||
*
|
||||
* @return The key
|
||||
*/
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set of valid log block data for this TreeDefinition
|
||||
*
|
||||
* @return A Set of CompatibleMaterial
|
||||
*/
|
||||
public Set<XMaterial> getLogMaterial() {
|
||||
return Collections.unmodifiableSet(this.logMaterial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set of valid leaf block data for this TreeDefinition
|
||||
*
|
||||
* @return A Set of CompatibleMaterial
|
||||
*/
|
||||
public Set<XMaterial> getLeafMaterial() {
|
||||
return Collections.unmodifiableSet(this.leafMaterial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sapling block data of this TreeDefinition
|
||||
*
|
||||
* @return An CompatibleMaterial instance for the sapling
|
||||
*/
|
||||
public XMaterial getSaplingMaterial() {
|
||||
return this.saplingMaterial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set of plantable soil block data for this TreeDefinition
|
||||
*
|
||||
* @return A Set of CompatibleMaterial
|
||||
*/
|
||||
public Set<XMaterial> getPlantableSoilMaterial() {
|
||||
return Collections.unmodifiableSet(this.plantableSoilMaterial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the max distance away a log can be from the tree trunk in order to be part of the tree
|
||||
*
|
||||
* @return The max distance a log can be from the tree trunk
|
||||
*/
|
||||
public double getMaxLogDistanceFromTrunk() {
|
||||
return this.maxLogDistanceFromTrunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the max distance away a leaf can be from a log in order to be part of the tree
|
||||
*
|
||||
* @return The max distance a leaf can be from a log
|
||||
*/
|
||||
public int getMaxLeafDistanceFromLog() {
|
||||
return this.maxLeafDistanceFromLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if tree detection should check for leaves diagonally
|
||||
*
|
||||
* @return True if leaves should be searched for diagonally, otherwise false
|
||||
*/
|
||||
public boolean shouldDetectLeavesDiagonally() {
|
||||
return this.detectLeavesDiagonally;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the logs of this tree should drop their original block
|
||||
*
|
||||
* @return True if the original log block should be dropped, otherwise false
|
||||
*/
|
||||
public boolean shouldDropOriginalLog() {
|
||||
return this.dropOriginalLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the leaves of this tree should drop their original block
|
||||
*
|
||||
* @return True if the original leaf block should be dropped, otherwise false
|
||||
*/
|
||||
public boolean shouldDropOriginalLeaf() {
|
||||
return this.dropOriginalLeaf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the log loot for this TreeDefinition
|
||||
*
|
||||
* @return A Set of TreeLoot
|
||||
*/
|
||||
public Set<TreeLoot> getLogLoot() {
|
||||
return Collections.unmodifiableSet(this.logLoot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the leaf loot for this TreeDefinition
|
||||
*
|
||||
* @return A Set of TreeLoot
|
||||
*/
|
||||
public Set<TreeLoot> getLeafLoot() {
|
||||
return Collections.unmodifiableSet(this.leafLoot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the loot for this TreeDefinition
|
||||
*
|
||||
* @return A Set of TreeLoot
|
||||
*/
|
||||
public Set<TreeLoot> getEntireTreeLoot() {
|
||||
return Collections.unmodifiableSet(this.entireTreeLoot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tools that can be used to activate this tree topple
|
||||
*
|
||||
* @return A Set of ItemStacks
|
||||
*/
|
||||
public Set<ItemStack> getRequiredTools() {
|
||||
return Collections.unmodifiableSet(this.requiredTools);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this TreeDefinition requires a custom axe.
|
||||
*
|
||||
* @return True if the TreeDefinition requires a custom axe
|
||||
*/
|
||||
public boolean isRequiredAxe() {
|
||||
return this.requiredAxe;
|
||||
}
|
||||
}
|
81
src/main/java/com/craftaro/ultimatetimber/tree/TreeLoot.java
Normal file
81
src/main/java/com/craftaro/ultimatetimber/tree/TreeLoot.java
Normal file
@ -0,0 +1,81 @@
|
||||
package com.craftaro.ultimatetimber.tree;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class TreeLoot {
|
||||
private final TreeBlockType treeBlockType;
|
||||
private final ItemStack item;
|
||||
private final String command;
|
||||
private final double chance;
|
||||
|
||||
public TreeLoot(TreeBlockType treeBlockType, ItemStack item, String command, double chance) {
|
||||
this.treeBlockType = treeBlockType;
|
||||
this.item = item;
|
||||
this.command = command;
|
||||
this.chance = chance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tree block type this loot is for
|
||||
*
|
||||
* @return The tree block type this loot is for
|
||||
*/
|
||||
public TreeBlockType getTreeBlockType() {
|
||||
return this.treeBlockType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this TreeLoot has an item to drop
|
||||
*
|
||||
* @return True if an item exists, otherwise false
|
||||
*/
|
||||
public boolean hasItem() {
|
||||
return this.item != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item that this tree loot can drop
|
||||
*
|
||||
* @return An ItemStack this tree loot can drop
|
||||
*/
|
||||
public ItemStack getItem() {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this TreeLoot has a command to run
|
||||
*
|
||||
* @return True if a command exists, otherwise false
|
||||
*/
|
||||
public boolean hasCommand() {
|
||||
return this.command != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the command that this tree loot can run
|
||||
*
|
||||
* @return The command that this tree loot can run
|
||||
*/
|
||||
public String getCommand() {
|
||||
return this.command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the percent chance this tree loot will drop
|
||||
*
|
||||
* @return The percent chance this tree loot can drop
|
||||
*/
|
||||
public double getChance() {
|
||||
return this.chance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TreeLoot{" +
|
||||
"treeBlockType=" + this.treeBlockType +
|
||||
", item=" + this.item +
|
||||
", command='" + this.command + '\'' +
|
||||
", chance=" + this.chance +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.craftaro.ultimatetimber.utils;
|
||||
|
||||
import com.craftaro.core.compatibility.CompatibleMaterial;
|
||||
import com.craftaro.core.compatibility.ServerVersion;
|
||||
import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class BlockUtils {
|
||||
public static Collection<ItemStack> getBlockDrops(ITreeBlock treeBlock) {
|
||||
Set<ItemStack> drops = new HashSet<>();
|
||||
if (treeBlock.getBlock() instanceof Block) {
|
||||
Block block = (Block) treeBlock.getBlock();
|
||||
Optional<XMaterial> material = CompatibleMaterial.getMaterial(block.getType());
|
||||
if (!material.isPresent() || CompatibleMaterial.isAir(material.get())) {
|
||||
return drops;
|
||||
}
|
||||
drops.add(material.get().parseItem());
|
||||
} else if (treeBlock.getBlock() instanceof FallingBlock) {
|
||||
Optional<XMaterial> material = CompatibleMaterial.getMaterial(((FallingBlock) treeBlock.getBlock()).getBlockData().getMaterial());
|
||||
if (!material.isPresent()) {
|
||||
return drops;
|
||||
}
|
||||
drops.add(material.get().parseItem());
|
||||
}
|
||||
return drops;
|
||||
}
|
||||
|
||||
public static void toggleGravityFallingBlock(FallingBlock fallingBlock, boolean applyGravity) {
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_9)) {
|
||||
fallingBlock.setGravity(applyGravity);
|
||||
}
|
||||
}
|
||||
|
||||
public static FallingBlock spawnFallingBlock(Location location, XMaterial material) {
|
||||
return location.getWorld().spawnFallingBlock(location, material.parseMaterial(), material.getData());
|
||||
}
|
||||
|
||||
public static void configureFallingBlock(FallingBlock fallingBlock) {
|
||||
toggleGravityFallingBlock(fallingBlock, false);
|
||||
fallingBlock.setDropItem(false);
|
||||
fallingBlock.setHurtEntities(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package com.craftaro.ultimatetimber.utils;
|
||||
|
||||
import com.craftaro.core.compatibility.ServerVersion;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import org.bukkit.Effect;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class ParticleUtils {
|
||||
public static void playFallingParticles(ITreeBlock treeBlock) {
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
|
||||
BlockData blockData;
|
||||
if (treeBlock.getBlock() instanceof Block) {
|
||||
blockData = ((Block) treeBlock.getBlock()).getBlockData();
|
||||
} else if (treeBlock.getBlock() instanceof FallingBlock) {
|
||||
blockData = ((FallingBlock) treeBlock.getBlock()).getBlockData();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
Location location = treeBlock.getLocation().clone().add(0.5, 0.5, 0.5);
|
||||
location.getWorld().spawnParticle(Particle.BLOCK_DUST, location, 10, blockData);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<ItemStack> blockDrops = BlockUtils.getBlockDrops(treeBlock);
|
||||
if (!blockDrops.iterator().hasNext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Location location = treeBlock.getLocation().clone().add(0.5, 0.5, 0.5);
|
||||
if (ServerVersion.isServerVersion(ServerVersion.V1_8)) {
|
||||
location.getWorld().playEffect(location, Effect.SMOKE, 4);
|
||||
} else {
|
||||
location.getWorld().spawnParticle(Particle.BLOCK_DUST, location, 10, blockDrops.iterator().next().getData());
|
||||
}
|
||||
}
|
||||
|
||||
public static void playLandingParticles(ITreeBlock treeBlock) {
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
|
||||
BlockData blockData;
|
||||
if (treeBlock.getBlock() instanceof Block) {
|
||||
blockData = ((Block) treeBlock.getBlock()).getBlockData();
|
||||
} else if (treeBlock.getBlock() instanceof FallingBlock) {
|
||||
blockData = ((FallingBlock) treeBlock.getBlock()).getBlockData();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
Location location = treeBlock.getLocation().clone().add(0.5, 0.5, 0.5);
|
||||
location.getWorld().spawnParticle(Particle.BLOCK_CRACK, location, 10, blockData);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<ItemStack> blockDrops = BlockUtils.getBlockDrops(treeBlock);
|
||||
if (!blockDrops.iterator().hasNext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Location location = treeBlock.getLocation().clone().add(0.5, 0.5, 0.5);
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_9)) {
|
||||
location.getWorld().spawnParticle(Particle.BLOCK_CRACK, location, 10, blockDrops.iterator().next().getData());
|
||||
} else {
|
||||
location.getWorld().playEffect(location, Effect.SMOKE, 4);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.craftaro.ultimatetimber.utils;
|
||||
|
||||
import com.craftaro.third_party.com.cryptomorin.xseries.XSound;
|
||||
import com.craftaro.ultimatetimber.tree.ITreeBlock;
|
||||
import com.craftaro.ultimatetimber.tree.TreeBlockType;
|
||||
import org.bukkit.Location;
|
||||
|
||||
public class SoundUtils {
|
||||
public static void playFallingSound(ITreeBlock block) {
|
||||
Location location = block.getLocation();
|
||||
if (location.getWorld() == null) {
|
||||
return;
|
||||
}
|
||||
XSound.BLOCK_CHEST_OPEN.play(location, 2, .1f);
|
||||
}
|
||||
|
||||
public static void playLandingSound(ITreeBlock block) {
|
||||
Location location = block.getLocation();
|
||||
if (location.getWorld() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (block.getTreeBlockType() == TreeBlockType.LOG) {
|
||||
XSound.BLOCK_WOOD_FALL.play(location, 2, .1f);
|
||||
} else {
|
||||
XSound.BLOCK_GRASS_BREAK.play(location, .5f, .75f);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
package com.songoda.ultimatetimber;
|
||||
|
||||
import com.songoda.ultimatetimber.commands.CommandHandler;
|
||||
import com.songoda.ultimatetimber.configurations.DefaultConfig;
|
||||
import com.songoda.ultimatetimber.treefall.TreeFallEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
Note: In this plugin, I have called the act of a tree falling over with pseudo-physics "toppling over". This is reflected
|
||||
in the documentation, config files and variable names.
|
||||
PS: MagmaGuy was here
|
||||
*/
|
||||
|
||||
public class UltimateTimber extends JavaPlugin {
|
||||
|
||||
public static Plugin plugin;
|
||||
public static List<World> validWorlds = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
|
||||
plugin = this;
|
||||
/*
|
||||
Register the main event that handles toppling down trees
|
||||
*/
|
||||
Bukkit.getServer().getPluginManager().registerEvents(new TreeFallEvent(), this);
|
||||
|
||||
/*
|
||||
Initialize and cache config
|
||||
*/
|
||||
DefaultConfig.initialize();
|
||||
|
||||
/*
|
||||
Cache valid worlds for later use
|
||||
*/
|
||||
for (World world : Bukkit.getWorlds())
|
||||
if (UltimateTimber.plugin.getConfig().getBoolean(DefaultConfig.VALID_WORLDS + world.getName()))
|
||||
validWorlds.add(world);
|
||||
|
||||
this.getCommand("ultimatetimber").setExecutor(new CommandHandler());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
|
||||
validWorlds.clear();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package com.songoda.ultimatetimber.commands;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class CommandHandler implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender commandSender, Command command, String s, String[] args) {
|
||||
|
||||
if (args.length > 0)
|
||||
if (args[0].equalsIgnoreCase("reload")) {
|
||||
ReloadCommand.reloadConfig(commandSender);
|
||||
return true;
|
||||
}
|
||||
|
||||
commandSender.sendMessage("[UltimateTimber] Command usage: /em reload");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package com.songoda.ultimatetimber.commands;
|
||||
|
||||
import com.songoda.ultimatetimber.UltimateTimber;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class ReloadCommand {
|
||||
|
||||
public static void reloadConfig(CommandSender commandSender) {
|
||||
|
||||
UltimateTimber.plugin.reloadConfig();
|
||||
commandSender.sendMessage("[UltimateTimber] - Configuration reloaded");
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package com.songoda.ultimatetimber.configurations;
|
||||
|
||||
import com.songoda.ultimatetimber.UltimateTimber;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
|
||||
public class DefaultConfig {
|
||||
|
||||
/*
|
||||
This value is just cached so it can easily and safely be accessed during runtime
|
||||
*/
|
||||
// public static Configuration configuration;
|
||||
|
||||
/*
|
||||
Storing these values in final strings makes it so you can change the keys or refactor their names later on without
|
||||
ever having to alter any code directly.
|
||||
Also they are easier to refer to using an IDE.
|
||||
*/
|
||||
public static final String AXES_ONLY = "Only topple down trees cut down using axes";
|
||||
public static final String ACCURATE_AXE_DURABILITY = "Lower durability proportionately to the amount of blocks toppled down";
|
||||
public static final String CREATIVE_DISALLOWED = "Players in creative mode can't topple down trees";
|
||||
public static final String PERMISSIONS_ONLY = "Only allow players with the permission node to topple down trees";
|
||||
public static final String VALID_WORLDS = "Valid worlds.";
|
||||
public static final String DAMAGE_PLAYERS = "Damage players when trees fall on them";
|
||||
public static final String REPLANT_SAPLING = "Replant sapling when tree is cut down";
|
||||
public static final String REPLANT_FROM_LEAVES = "Fallen leaves have a chance to plant saplings";
|
||||
public static final String CUSTOM_AUDIO = "Use custom sounds for trees falling";
|
||||
|
||||
public static void initialize() {
|
||||
|
||||
Configuration configuration = UltimateTimber.plugin.getConfig();
|
||||
|
||||
configuration.addDefault(AXES_ONLY, true);
|
||||
configuration.addDefault(ACCURATE_AXE_DURABILITY, true);
|
||||
configuration.addDefault(CREATIVE_DISALLOWED, true);
|
||||
configuration.addDefault(PERMISSIONS_ONLY, true);
|
||||
configuration.addDefault(DAMAGE_PLAYERS, true);
|
||||
configuration.addDefault(REPLANT_SAPLING, true);
|
||||
configuration.addDefault(REPLANT_FROM_LEAVES, true);
|
||||
configuration.addDefault(CUSTOM_AUDIO, true);
|
||||
|
||||
/*
|
||||
Add all worlds that exist in the world at startup
|
||||
*/
|
||||
for (World world : Bukkit.getServer().getWorlds())
|
||||
configuration.addDefault(VALID_WORLDS + world.getName(), true);
|
||||
|
||||
configuration.options().copyDefaults(true);
|
||||
|
||||
UltimateTimber.plugin.saveConfig();
|
||||
UltimateTimber.plugin.saveDefaultConfig();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package com.songoda.ultimatetimber.treefall;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.Damageable;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class AxeDurability {
|
||||
|
||||
public static void adjustAxeDamage(ArrayList<Block> blocks, Player player) {
|
||||
|
||||
if (!(player.getInventory().getItemInMainHand().getType().equals(Material.DIAMOND_AXE) ||
|
||||
player.getInventory().getItemInMainHand().getType().equals(Material.GOLDEN_AXE) ||
|
||||
player.getInventory().getItemInMainHand().getType().equals(Material.IRON_AXE) ||
|
||||
player.getInventory().getItemInMainHand().getType().equals(Material.STONE_AXE) ||
|
||||
player.getInventory().getItemInMainHand().getType().equals(Material.WOODEN_AXE))) return;
|
||||
|
||||
ItemStack itemStack = player.getInventory().getItemInMainHand();
|
||||
ItemMeta itemMeta = itemStack.getItemMeta();
|
||||
Damageable damageableMeta = (Damageable) itemMeta;
|
||||
damageableMeta.setDamage(damageableMeta.getDamage() + blocks.size());
|
||||
itemStack.setItemMeta((ItemMeta) damageableMeta);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package com.songoda.ultimatetimber.treefall;
|
||||
|
||||
import com.songoda.ultimatetimber.UltimateTimber;
|
||||
import com.songoda.ultimatetimber.configurations.DefaultConfig;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
|
||||
public class EventFilter {
|
||||
|
||||
/*
|
||||
Incorporate all checks that would disqualify this event from happening
|
||||
Mostly config settings, also permissions
|
||||
*/
|
||||
public static boolean eventIsValid(BlockBreakEvent event) {
|
||||
/*
|
||||
General catchers
|
||||
*/
|
||||
if (event.isCancelled()) return false;
|
||||
|
||||
if (!UltimateTimber.validWorlds.contains(event.getPlayer().getWorld())) return false;
|
||||
|
||||
if (!TreeChecker.validMaterials.contains(event.getBlock().getType())) return false;
|
||||
/*
|
||||
Config-based catchers
|
||||
*/
|
||||
if (UltimateTimber.plugin.getConfig().getBoolean(DefaultConfig.CREATIVE_DISALLOWED) &&
|
||||
event.getPlayer().getGameMode().equals(GameMode.CREATIVE))
|
||||
return false;
|
||||
|
||||
if (UltimateTimber.plugin.getConfig().getBoolean(DefaultConfig.AXES_ONLY) &&
|
||||
!(event.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.DIAMOND_AXE) ||
|
||||
event.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.GOLDEN_AXE) ||
|
||||
event.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.IRON_AXE) ||
|
||||
event.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.STONE_AXE) ||
|
||||
event.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.WOODEN_AXE)))
|
||||
return false;
|
||||
|
||||
if (UltimateTimber.plugin.getConfig().getBoolean(DefaultConfig.PERMISSIONS_ONLY) &&
|
||||
!event.getPlayer().hasPermission("ultimatetimber.chop"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
package com.songoda.ultimatetimber.treefall;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class TreeChecker {
|
||||
|
||||
/*
|
||||
Used to check if a tree is a tree
|
||||
*/
|
||||
public static List<Material> validMaterials = new ArrayList<>(Arrays.asList(
|
||||
Material.ACACIA_LOG,
|
||||
Material.STRIPPED_ACACIA_LOG,
|
||||
Material.BIRCH_LOG,
|
||||
Material.STRIPPED_BIRCH_LOG,
|
||||
Material.DARK_OAK_LOG,
|
||||
Material.STRIPPED_DARK_OAK_LOG,
|
||||
Material.JUNGLE_LOG,
|
||||
Material.STRIPPED_JUNGLE_LOG,
|
||||
Material.OAK_LOG,
|
||||
Material.STRIPPED_OAK_LOG,
|
||||
Material.SPRUCE_LOG,
|
||||
Material.STRIPPED_SPRUCE_LOG
|
||||
));
|
||||
|
||||
/*
|
||||
Used to limit the blocks that constitute a tree
|
||||
*/
|
||||
private static List<Material> validSurroundingMaterials = new ArrayList<>(Arrays.asList(
|
||||
Material.ACACIA_LEAVES,
|
||||
Material.BIRCH_LEAVES,
|
||||
Material.DARK_OAK_LEAVES,
|
||||
Material.JUNGLE_LEAVES,
|
||||
Material.OAK_LEAVES,
|
||||
Material.SPRUCE_LEAVES,
|
||||
Material.COCOA_BEANS
|
||||
));
|
||||
|
||||
public static ArrayList<Block> parseTree(Block block) {
|
||||
/*
|
||||
Check if material is valid
|
||||
*/
|
||||
if (!validMaterials.contains(block.getType())) return null;
|
||||
|
||||
ArrayList<Block> blocks = new ArrayList<>();
|
||||
|
||||
boolean containsSecondaryBlock = false;
|
||||
|
||||
/*
|
||||
Check if block is surrounded by air (bottom blocks are)
|
||||
Every third block expand one block laterally until the main block line reaches air or the max height is reached
|
||||
*/
|
||||
int offset = 1;
|
||||
int maxHeight = 31;
|
||||
|
||||
for (int i = 0; i < maxHeight; i++) {
|
||||
|
||||
if ((i + 1) % 2 == 0 && offset < 6)
|
||||
offset++;
|
||||
|
||||
/*
|
||||
This expands the inverted search pyramid vertically
|
||||
*/
|
||||
int xOffset = -offset;
|
||||
for (int j = xOffset - 1; j < offset; j++) {
|
||||
|
||||
int zOffset = -offset;
|
||||
|
||||
for (int k = zOffset - 1; k < offset; k++) {
|
||||
|
||||
Block thisBlock = block.getLocation().clone().add(new Vector(xOffset, i, zOffset)).getBlock();
|
||||
|
||||
/*
|
||||
This exclusion list should include everything you may find near trees as to not invalidate trees
|
||||
in natural forests and such
|
||||
Construction blocks aren't included because that would bust buildings.
|
||||
*/
|
||||
if (!thisBlock.getType().equals(block.getType()) &&
|
||||
!validSurroundingMaterials.contains(thisBlock.getType()) &&
|
||||
!thisBlock.getType().equals(Material.AIR) &&
|
||||
!thisBlock.getType().equals(Material.VINE) &&
|
||||
!thisBlock.getType().equals(Material.ROSE_BUSH) &&
|
||||
!thisBlock.getType().equals(Material.ORANGE_TULIP) &&
|
||||
!thisBlock.getType().equals(Material.PINK_TULIP) &&
|
||||
!thisBlock.getType().equals(Material.RED_TULIP) &&
|
||||
!thisBlock.getType().equals(Material.WHITE_TULIP) &&
|
||||
!thisBlock.getType().equals(Material.TALL_GRASS) &&
|
||||
!thisBlock.getType().equals(Material.FERN) &&
|
||||
!thisBlock.getType().equals(Material.LARGE_FERN) &&
|
||||
!thisBlock.getType().equals(Material.DEAD_BUSH) &&
|
||||
!thisBlock.getType().equals(Material.BROWN_MUSHROOM) &&
|
||||
!thisBlock.getType().equals(Material.RED_MUSHROOM) &&
|
||||
!thisBlock.getType().equals(Material.GRASS) &&
|
||||
!thisBlock.getType().equals(Material.SPRUCE_SAPLING) &&
|
||||
!thisBlock.getType().equals(Material.OAK_SAPLING) &&
|
||||
!thisBlock.getType().equals(Material.JUNGLE_SAPLING) &&
|
||||
!thisBlock.getType().equals(Material.ACACIA_SAPLING) &&
|
||||
!thisBlock.getType().equals(Material.BIRCH_SAPLING) &&
|
||||
!thisBlock.getType().equals(Material.DARK_OAK_SAPLING))
|
||||
return null;
|
||||
|
||||
if (validSurroundingMaterials.contains(thisBlock.getType()))
|
||||
containsSecondaryBlock = true;
|
||||
|
||||
if (!thisBlock.getType().equals(Material.AIR))
|
||||
blocks.add(thisBlock);
|
||||
|
||||
zOffset++;
|
||||
|
||||
}
|
||||
|
||||
xOffset++;
|
||||
|
||||
}
|
||||
|
||||
if (block.getLocation().clone().add(new Vector(0, i, 0)).getBlock().getType().equals(Material.AIR))
|
||||
if (i > 1)
|
||||
break;
|
||||
else
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
If there are no leaves, don't see it as a tree
|
||||
*/
|
||||
if (!containsSecondaryBlock) return null;
|
||||
|
||||
return blocks;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package com.songoda.ultimatetimber.treefall;
|
||||
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
|
||||
public class TreeEntityDamage {
|
||||
|
||||
public static void runDamage(FallingBlock fallingBlock) {
|
||||
|
||||
for (Entity entity : fallingBlock.getNearbyEntities(0.5, 0.5, 0.5)) {
|
||||
|
||||
if (entity instanceof LivingEntity) {
|
||||
|
||||
((LivingEntity) entity).damage(1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
package com.songoda.ultimatetimber.treefall;
|
||||
|
||||
import com.songoda.ultimatetimber.UltimateTimber;
|
||||
import com.songoda.ultimatetimber.configurations.DefaultConfig;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class TreeFallAnimation {
|
||||
|
||||
/*
|
||||
This animation has multiple phases.
|
||||
Initially, the tree will start slowly toppling over.
|
||||
After a short while, it goes over the tipping point and the fall accelerates.
|
||||
*/
|
||||
public static void startAnimation(Block originalBlock, ArrayList<Block> blocks, Player player) {
|
||||
|
||||
/*
|
||||
This vector makes sure that the entire tree falls in the same direction from the same reference point
|
||||
*/
|
||||
Vector velocityVector = originalBlock.getLocation().clone().subtract(player.getLocation().clone()).toVector().normalize().setY(0);
|
||||
|
||||
for (Block block : blocks) {
|
||||
|
||||
if (block.getType().equals(Material.AIR)) continue;
|
||||
|
||||
FallingBlock fallingBlock = block.getWorld().spawnFallingBlock(block.getLocation(), block.getBlockData());
|
||||
|
||||
/*
|
||||
Remove original block
|
||||
*/
|
||||
TreeReplant.replaceOriginalBlock(block);
|
||||
|
||||
/*
|
||||
Set tipping over effect
|
||||
The horizontal velocity going away from the player increases as the Y moves away from the player
|
||||
*/
|
||||
double multiplier = (block.getLocation().getY() - player.getLocation().getY()) * 0.1;
|
||||
|
||||
startPhaseOneAnimation(fallingBlock, velocityVector, multiplier, player);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Phase one of the animation, the tree starts slowly tipping over
|
||||
*/
|
||||
private static void startPhaseOneAnimation(FallingBlock fallingBlock, Vector velocityVector, double multiplier, Player player) {
|
||||
|
||||
/*
|
||||
Vertical offset so top of the tree sways faster than the base
|
||||
*/
|
||||
fallingBlock.setVelocity(velocityVector.clone().multiply(multiplier));
|
||||
fallingBlock.setDropItem(true);
|
||||
/*
|
||||
No gravity helps with the initial surrounding block detection (somehow) and with the initial trunk rigidity aspect
|
||||
required for the effect to look convincing
|
||||
*/
|
||||
fallingBlock.setGravity(false);
|
||||
|
||||
fallingBlock.setVelocity(fallingBlock.getVelocity().multiply(0.2));
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
fallingBlock.setGravity(true);
|
||||
|
||||
/*
|
||||
Phase 2 has to be launched from here as to not override effects
|
||||
*/
|
||||
runPhaseTwoAnimation(fallingBlock, player);
|
||||
|
||||
}
|
||||
}.runTaskLater(UltimateTimber.plugin, 20);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Phase two of the animation, the tree picks up speed until it is on the ground
|
||||
For safety's sake, it disintegrates after a 4 seconds
|
||||
*/
|
||||
private static void runPhaseTwoAnimation(FallingBlock fallingBlock, Player player) {
|
||||
|
||||
new BukkitRunnable() {
|
||||
int counter = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (counter < 10)
|
||||
fallingBlock.setVelocity(fallingBlock.getVelocity().multiply(1.3));
|
||||
if (counter > 20 * 3 || hasNearbySolidBlock(fallingBlock)) {
|
||||
cancel();
|
||||
TreeLoot.convertFallingBlock(fallingBlock, player);
|
||||
fallingBlock.remove();
|
||||
fallingBlock.getLocation().getWorld().spawnParticle(Particle.SMOKE_LARGE, fallingBlock.getLocation(), 3, 0.2, 0.2, 0.2, 0.05);
|
||||
if (UltimateTimber.plugin.getConfig().getBoolean(DefaultConfig.REPLANT_FROM_LEAVES))
|
||||
TreeReplant.leafFallReplant(fallingBlock);
|
||||
if (UltimateTimber.plugin.getConfig().getBoolean(DefaultConfig.DAMAGE_PLAYERS))
|
||||
TreeEntityDamage.runDamage(fallingBlock);
|
||||
if (UltimateTimber.plugin.getConfig().getBoolean(DefaultConfig.CUSTOM_AUDIO))
|
||||
TreeSounds.fallNoise(fallingBlock, counter);
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}.runTaskTimer(UltimateTimber.plugin, 0, 1);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Since the block accelerates as it falls, increase the search radius the longer it has been falling for
|
||||
*/
|
||||
private static boolean hasNearbySolidBlock(FallingBlock fallingBlock) {
|
||||
|
||||
if (!fallingBlock.getLocation().clone().subtract(new Vector(0, 1, 0)).getBlock().getType().equals(Material.AIR))
|
||||
return true;
|
||||
|
||||
/*
|
||||
Lower comparative intensity by predicting the blocks through which the falling block will pass by by checking
|
||||
its velocity
|
||||
*/
|
||||
Location predictedLocation = fallingBlock.getLocation().clone().add(fallingBlock.getVelocity().multiply(2));
|
||||
|
||||
if (predictedLocation.getY() < 1) return true;
|
||||
|
||||
return !predictedLocation.getBlock().getType().equals(Material.AIR);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package com.songoda.ultimatetimber.treefall;
|
||||
|
||||
import com.songoda.ultimatetimber.UltimateTimber;
|
||||
import com.songoda.ultimatetimber.configurations.DefaultConfig;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class TreeFallEvent implements Listener {
|
||||
|
||||
/*
|
||||
This is the starting point for the whole effect
|
||||
It's been broken up instead of chained in order to make step-by-step debugging easier
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onTreeBreak(BlockBreakEvent event) {
|
||||
|
||||
if (!EventFilter.eventIsValid(event)) return;
|
||||
ArrayList<Block> blocks = TreeChecker.parseTree(event.getBlock());
|
||||
if (blocks == null) return;
|
||||
if (UltimateTimber.plugin.getConfig().getBoolean(DefaultConfig.ACCURATE_AXE_DURABILITY))
|
||||
AxeDurability.adjustAxeDamage(blocks, event.getPlayer());
|
||||
if (UltimateTimber.plugin.getConfig().getBoolean(DefaultConfig.CUSTOM_AUDIO))
|
||||
TreeSounds.tipOverNoise(event.getBlock().getLocation());
|
||||
TreeFallAnimation.startAnimation(event.getBlock(), blocks, event.getPlayer());
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
package com.songoda.ultimatetimber.treefall;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class TreeLoot {
|
||||
|
||||
public static void convertFallingBlock(FallingBlock fallingBlock, Player player) {
|
||||
|
||||
Material material;
|
||||
|
||||
switch (fallingBlock.getBlockData().getMaterial()) {
|
||||
|
||||
case ACACIA_LEAVES:
|
||||
material = Material.ACACIA_SAPLING;
|
||||
break;
|
||||
case BIRCH_LEAVES:
|
||||
material = Material.BIRCH_SAPLING;
|
||||
break;
|
||||
case DARK_OAK_LEAVES:
|
||||
material = Material.DARK_OAK_SAPLING;
|
||||
break;
|
||||
case JUNGLE_LEAVES:
|
||||
material = Material.JUNGLE_SAPLING;
|
||||
break;
|
||||
case OAK_LEAVES:
|
||||
material = Material.OAK_SAPLING;
|
||||
break;
|
||||
case SPRUCE_LEAVES:
|
||||
material = Material.SPRUCE_SAPLING;
|
||||
break;
|
||||
default:
|
||||
material = fallingBlock.getBlockData().getMaterial();
|
||||
|
||||
}
|
||||
|
||||
if (material.equals(Material.VINE))
|
||||
return;
|
||||
|
||||
if (player.getInventory().getItemInMainHand().getType().equals(Material.DIAMOND_AXE) ||
|
||||
player.getInventory().getItemInMainHand().getType().equals(Material.GOLDEN_AXE) ||
|
||||
player.getInventory().getItemInMainHand().getType().equals(Material.IRON_AXE) ||
|
||||
player.getInventory().getItemInMainHand().getType().equals(Material.STONE_AXE) ||
|
||||
player.getInventory().getItemInMainHand().getType().equals(Material.WOODEN_AXE))
|
||||
if (player.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.SILK_TOUCH))
|
||||
fallingBlock.getWorld().dropItem(fallingBlock.getLocation(), new ItemStack(fallingBlock.getBlockData().getMaterial(), 1));
|
||||
|
||||
if (material.equals(Material.ACACIA_SAPLING) ||
|
||||
material.equals(Material.BIRCH_SAPLING) ||
|
||||
material.equals(Material.DARK_OAK_SAPLING) ||
|
||||
material.equals(Material.JUNGLE_SAPLING) ||
|
||||
material.equals(Material.OAK_SAPLING) ||
|
||||
material.equals(Material.SPRUCE_SAPLING))
|
||||
if (ThreadLocalRandom.current().nextDouble() < 0.05) {
|
||||
if (player.hasPermission("ultimatetimber.bonusloot"))
|
||||
fallingBlock.getWorld().dropItem(fallingBlock.getLocation(), new ItemStack(material, 1));
|
||||
fallingBlock.getWorld().dropItem(fallingBlock.getLocation(), new ItemStack(material, 1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.hasPermission("ultimatetimber.bonusloot"))
|
||||
fallingBlock.getWorld().dropItem(fallingBlock.getLocation(), new ItemStack(material, 1));
|
||||
fallingBlock.getWorld().dropItem(fallingBlock.getLocation(), new ItemStack(material, 1));
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
package com.songoda.ultimatetimber.treefall;
|
||||
|
||||
import com.songoda.ultimatetimber.UltimateTimber;
|
||||
import com.songoda.ultimatetimber.configurations.DefaultConfig;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class TreeReplant {
|
||||
|
||||
public static void replaceOriginalBlock(Block block) {
|
||||
|
||||
if (!UltimateTimber.plugin.getConfig().getBoolean(DefaultConfig.REPLANT_SAPLING)) {
|
||||
block.setType(Material.AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!block.getLocation().clone().subtract(new Vector(0, 1, 0)).getBlock().getType().equals(Material.DIRT) &&
|
||||
!block.getLocation().clone().subtract(new Vector(0, 1, 0)).getBlock().getType().equals(Material.COARSE_DIRT)) {
|
||||
block.setType(Material.AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
Material material = block.getType();
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
switch (material) {
|
||||
case ACACIA_LOG:
|
||||
case STRIPPED_ACACIA_LOG:
|
||||
block.setType(Material.ACACIA_SAPLING);
|
||||
return;
|
||||
case BIRCH_LOG:
|
||||
case STRIPPED_BIRCH_LOG:
|
||||
block.setType(Material.BIRCH_SAPLING);
|
||||
return;
|
||||
case DARK_OAK_LOG:
|
||||
case STRIPPED_DARK_OAK_LOG:
|
||||
block.setType(Material.DARK_OAK_SAPLING);
|
||||
return;
|
||||
case JUNGLE_LOG:
|
||||
case STRIPPED_JUNGLE_LOG:
|
||||
block.setType(Material.JUNGLE_SAPLING);
|
||||
return;
|
||||
case OAK_LOG:
|
||||
case STRIPPED_OAK_LOG:
|
||||
block.setType(Material.OAK_SAPLING);
|
||||
return;
|
||||
case SPRUCE_LOG:
|
||||
case STRIPPED_SPRUCE_LOG:
|
||||
block.setType(Material.SPRUCE_SAPLING);
|
||||
return;
|
||||
default:
|
||||
block.setType(Material.AIR);
|
||||
}
|
||||
}
|
||||
}.runTaskLater(UltimateTimber.plugin, 1);
|
||||
|
||||
}
|
||||
|
||||
public static void leafFallReplant(FallingBlock fallingBlock) {
|
||||
|
||||
Material material;
|
||||
|
||||
switch (fallingBlock.getBlockData().getMaterial()) {
|
||||
|
||||
case ACACIA_LEAVES:
|
||||
material = Material.ACACIA_SAPLING;
|
||||
break;
|
||||
case BIRCH_LEAVES:
|
||||
material = Material.BIRCH_SAPLING;
|
||||
break;
|
||||
case DARK_OAK_LEAVES:
|
||||
material = Material.DARK_OAK_SAPLING;
|
||||
break;
|
||||
case JUNGLE_LEAVES:
|
||||
material = Material.JUNGLE_SAPLING;
|
||||
break;
|
||||
case OAK_LEAVES:
|
||||
material = Material.OAK_SAPLING;
|
||||
break;
|
||||
case SPRUCE_LEAVES:
|
||||
material = Material.SPRUCE_SAPLING;
|
||||
break;
|
||||
default:
|
||||
material = null;
|
||||
|
||||
}
|
||||
|
||||
if (material == null) return;
|
||||
|
||||
if (ThreadLocalRandom.current().nextDouble() > 0.05) return;
|
||||
|
||||
Block block = fallingBlock.getLocation().clone().subtract(new Vector(0, 1, 0)).getBlock();
|
||||
|
||||
if (block.getType().equals(Material.DIRT) || block.getType().equals(Material.COARSE_DIRT) || block.getType().equals(Material.GRASS_BLOCK)) {
|
||||
Block blockAbove = block.getLocation().clone().add(new Vector(0, 1, 0)).getBlock();
|
||||
if (blockAbove.getType().equals(Material.AIR))
|
||||
fallingBlock.getLocation().getBlock().setType(material);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package com.songoda.ultimatetimber.treefall;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
|
||||
public class TreeSounds {
|
||||
|
||||
public static void tipOverNoise(Location location) {
|
||||
|
||||
location.getWorld().playSound(location, Sound.BLOCK_CHEST_OPEN, 3F, 0.1F);
|
||||
|
||||
}
|
||||
|
||||
public static void fallNoise(FallingBlock fallingBlock, int counter) {
|
||||
if (counter < 20)
|
||||
fallingBlock.getWorld().playSound(fallingBlock.getLocation(), Sound.BLOCK_ANVIL_FALL, 3F, 0.1F);
|
||||
else
|
||||
fallingBlock.getWorld().playSound(fallingBlock.getLocation(), Sound.BLOCK_WOOD_FALL, 3F, 0.1F);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,580 @@
|
||||
# ____ ___ __ __ __ __ ___________ __ ___
|
||||
# | | \ |_/ |_|__| _____ _____ _/ |_ ___\__ ___/|__| _____\_ |__ ___________
|
||||
# | | / |\ __\ |/ \\__ \\ __\/ __ \| | | |/ \| __ \_/ __ \_ __ \
|
||||
# | | /| |_| | | | Y Y \/ __ \| | \ ___/| | | | Y Y \ \_\ \ ___/| | \/
|
||||
# |______/ |____/__| |__|__|_| (____ /__| \___ |____| |__|__|_| /___ /\___ >__|
|
||||
|
||||
# The type of server you are running in relation to this plugin
|
||||
# Do not change this value
|
||||
# Default: CURRENT
|
||||
server-type: CURRENT
|
||||
|
||||
# The locale to use in the /locale folder
|
||||
# Default: en_US
|
||||
locale: en_US
|
||||
|
||||
# A list of worlds that the plugin is disabled in
|
||||
# Default:
|
||||
# - disabled_world_name
|
||||
disabled-worlds:
|
||||
- disabled_world_name
|
||||
|
||||
# The max number of logs that can be broken at one time
|
||||
# Default: 150
|
||||
max-logs-per-chop: 150
|
||||
|
||||
# The minimum number of leaves required for something to be considered a tree
|
||||
# Default: 5
|
||||
leaves-required-for-tree: 5
|
||||
|
||||
# If leaves should be destroyed
|
||||
# Default: true
|
||||
destroy-leaves: true
|
||||
|
||||
# Apply realistic damage to the tools based on the number of logs chopped
|
||||
# If false, only one durability will be removed from the tool
|
||||
# Default: true
|
||||
realistic-tool-damage: true
|
||||
|
||||
# Protect the tool used to chop down the tree from breaking
|
||||
# Prevents the tree from being toppled if the tool would break
|
||||
# Default: false
|
||||
protect-tool: false
|
||||
|
||||
# Use the silk touch enchantment if the tool has it
|
||||
# Logs and leaves will drop their original block 100% of the time
|
||||
# Default: true
|
||||
apply-silk-touch: true
|
||||
|
||||
# Damage the tool extra for each leaf block broken, this is vanilla behavior but can be disabled here
|
||||
# Does nothing if realistic-tool-damage is false
|
||||
# Default: true
|
||||
apply-silk-touch-tool-damage: true
|
||||
|
||||
# Require the entire base of the tree to be broken before it topples
|
||||
# Default: false
|
||||
break-entire-tree-base: false
|
||||
|
||||
# Don't drop a block for the block that initiates the tree fall
|
||||
# Default: false
|
||||
destroy-initiated-block: false
|
||||
|
||||
# Only detect logs above the initiated block
|
||||
# Default: true
|
||||
only-detect-logs-upwards: true
|
||||
|
||||
# Only topple trees while the player is doing something
|
||||
# Valid values: SNEAKING, NOT_SNEAKING, ALWAYS
|
||||
# Default: ALWAYS
|
||||
only-topple-while: ALWAYS
|
||||
|
||||
# Allow toppling trees in creative mode
|
||||
# Default: true
|
||||
allow-creative-mode: true
|
||||
|
||||
# Require the player to have the permission 'ultimatetimber.chop' to topple trees
|
||||
# Default: false
|
||||
require-chop-permission: false
|
||||
|
||||
# If a player should only be allowed to chop one tree per cooldown length
|
||||
# Default: false
|
||||
player-tree-topple-cooldown: false
|
||||
|
||||
# The amount of seconds a player has to wait before they can chop a tree again
|
||||
# Does nothing if player-tree-topple-cooldown is false
|
||||
# The time is in seconds and must be a postive whole number
|
||||
# Default: 5
|
||||
player-tree-topple-cooldown-length: 5
|
||||
|
||||
# Allow players to topple trees regardless of what they are holding in their hand
|
||||
# Default: false
|
||||
ignore-required-tools: false
|
||||
|
||||
# Automatically replant saplings when a tree is toppled
|
||||
# Default: true
|
||||
replant-saplings: true
|
||||
|
||||
# Always replant saplings for base tree blocks, regardless of player permissions
|
||||
# Default: false
|
||||
always-replant-sapling: false
|
||||
|
||||
# How many seconds to prevent players from breaking replanted saplings
|
||||
# Set to 0 to disable
|
||||
# Does nothing if replant-saplings is false
|
||||
# The time is in seconds and must be a postive whole number
|
||||
# Default: 3
|
||||
replant-saplings-cooldown: 3
|
||||
|
||||
# Give fallen leaf blocks a chance to replant saplings when they hit the ground
|
||||
# Default: true
|
||||
falling-blocks-replant-saplings: true
|
||||
|
||||
# The percent chance that fallen leaves have of planting a sapling
|
||||
# Does nothing if falling-blocks-replant-saplings is false
|
||||
# The chance is out of 100 and may contain decimals
|
||||
# Default: 1
|
||||
falling-blocks-replant-saplings-chance: 1
|
||||
|
||||
# Make falling tree blocks deal damage to players if they get hit
|
||||
# Default: true
|
||||
falling-blocks-deal-damage: true
|
||||
|
||||
# The amount of damage that falling tree blocks do
|
||||
# This does nothing if falling-blocks-deal-damage is false
|
||||
# Default: 1
|
||||
falling-block-damage: 1
|
||||
|
||||
# Automatically add tree blocks to the player's inventory instead of dropping them
|
||||
# Default: false
|
||||
add-items-to-inventory: false
|
||||
|
||||
# Use custom sounds when toppling trees
|
||||
# Default: true
|
||||
use-custom-sounds: true
|
||||
|
||||
# Use custom particles when toppling trees
|
||||
# Default: true
|
||||
use-custom-particles: true
|
||||
|
||||
# The bonus loot multiplier when a player has the permission ultimatetimber.bonusloot
|
||||
# Multiplies the chance of tree drops by this value
|
||||
# Decimal values are allowed
|
||||
# Default: 2
|
||||
bonus-loot-multiplier: 2
|
||||
|
||||
# If placed blocks should be ignored for toppling trees
|
||||
# Note: This only keeps track of blocks placed during the current server load
|
||||
# If your server restarts, the placed tree blocks could be toppled again
|
||||
# Default: true
|
||||
ignore-placed-blocks: true
|
||||
|
||||
# The maximum number of blocks to keep track of in memory at once
|
||||
# Use a lower number if this starts to take up too much memory or trees start taking too long to detect
|
||||
# Default: 5000
|
||||
ignore-placed-blocks-memory-size: 5000
|
||||
|
||||
# Applies experience when using Jobs/mcMMO
|
||||
# Only does something if Jobs or mcMMO is installed
|
||||
# Default: true
|
||||
hooks-apply-experience: true
|
||||
|
||||
# Applies extra drops passive ability when using mcMMO
|
||||
# Only does something if mcMMO is installed
|
||||
# Default: true
|
||||
hooks-apply-extra-drops: true
|
||||
|
||||
# Requires the tree feller ability in mcMMO to be active to use timber
|
||||
# Only does something if mcMMO is installed
|
||||
# Default: false
|
||||
hooks-require-ability-active: false
|
||||
|
||||
# The type of animation to use for tree toppling
|
||||
# Types: FANCY, DISINTEGRATE, CRUMBLE, NONE
|
||||
tree-animation-type: FANCY
|
||||
|
||||
# If the tree-animation-type is FANCY or CRUMBLE, make the blocks stick to the ground
|
||||
# Does nothing if tree-animation-type is not FANCY or CRUMBLE
|
||||
# Default: false
|
||||
scatter-tree-blocks-on-ground: false
|
||||
|
||||
# Tree configuration
|
||||
# Allows for extreme fine-tuning of tree detection and what are considered trees
|
||||
# Multiple log and leaf types are allowed, only one sapling type is allowed
|
||||
# You can add your own custom tree types here, just add a new section
|
||||
trees:
|
||||
oak:
|
||||
logs:
|
||||
- OAK_LOG
|
||||
- STRIPPED_OAK_LOG
|
||||
- OAK_WOOD
|
||||
- STRIPPED_OAK_WOOD
|
||||
leaves:
|
||||
- OAK_LEAVES
|
||||
- VINE
|
||||
sapling: OAK_SAPLING
|
||||
plantable-soil: [ ]
|
||||
max-log-distance-from-trunk: 6
|
||||
max-leaf-distance-from-log: 6
|
||||
search-for-leaves-diagonally: false
|
||||
drop-original-log: true
|
||||
drop-original-leaf: false
|
||||
log-loot: [ ]
|
||||
leaf-loot:
|
||||
0:
|
||||
material: OAK_SAPLING
|
||||
chance: 5
|
||||
1:
|
||||
material: APPLE
|
||||
chance: 0.5
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
spruce:
|
||||
logs:
|
||||
- SPRUCE_LOG
|
||||
- STRIPPED_SPRUCE_LOG
|
||||
- SPRUCE_WOOD
|
||||
- STRIPPED_SPRUCE_WOOD
|
||||
leaves:
|
||||
- SPRUCE_LEAVES
|
||||
- VINE
|
||||
sapling: SPRUCE_SAPLING
|
||||
plantable-soil: [ ]
|
||||
max-log-distance-from-trunk: 2
|
||||
max-leaf-distance-from-log: 6
|
||||
search-for-leaves-diagonally: false
|
||||
drop-original-log: true
|
||||
drop-original-leaf: false
|
||||
log-loot: [ ]
|
||||
leaf-loot:
|
||||
0:
|
||||
material: SPRUCE_SAPLING
|
||||
chance: 5
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
birch:
|
||||
logs:
|
||||
- BIRCH_LOG
|
||||
- STRIPPED_BIRCH_LOG
|
||||
- BIRCH_WOOD
|
||||
- STRIPPED_BIRCH_WOOD
|
||||
leaves:
|
||||
- BIRCH_LEAVES
|
||||
- VINE
|
||||
sapling: BIRCH_SAPLING
|
||||
plantable-soil: [ ]
|
||||
max-log-distance-from-trunk: 1
|
||||
max-leaf-distance-from-log: 4
|
||||
search-for-leaves-diagonally: false
|
||||
drop-original-log: true
|
||||
drop-original-leaf: false
|
||||
log-loot: [ ]
|
||||
leaf-loot:
|
||||
0:
|
||||
material: BIRCH_SAPLING
|
||||
chance: 5
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
jungle:
|
||||
logs:
|
||||
- JUNGLE_LOG
|
||||
- STRIPPED_JUNGLE_LOG
|
||||
- JUNGLE_WOOD
|
||||
- STRIPPED_JUNGLE_WOOD
|
||||
leaves:
|
||||
- JUNGLE_LEAVES
|
||||
- VINE
|
||||
sapling: JUNGLE_SAPLING
|
||||
plantable-soil: [ ]
|
||||
max-log-distance-from-trunk: 6
|
||||
max-leaf-distance-from-log: 6
|
||||
search-for-leaves-diagonally: false
|
||||
drop-original-log: true
|
||||
drop-original-leaf: false
|
||||
log-loot: [ ]
|
||||
leaf-loot:
|
||||
0:
|
||||
material: JUNGLE_SAPLING
|
||||
chance: 2.5
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
acacia:
|
||||
logs:
|
||||
- ACACIA_LOG
|
||||
- STRIPPED_ACACIA_LOG
|
||||
- ACACIA_WOOD
|
||||
- STRIPPED_ACACIA_WOOD
|
||||
leaves:
|
||||
- ACACIA_LEAVES
|
||||
sapling: ACACIA_SAPLING
|
||||
plantable-soil: [ ]
|
||||
max-log-distance-from-trunk: 4
|
||||
max-leaf-distance-from-log: 5
|
||||
search-for-leaves-diagonally: false
|
||||
drop-original-log: true
|
||||
drop-original-leaf: false
|
||||
log-loot: [ ]
|
||||
leaf-loot:
|
||||
0:
|
||||
material: ACACIA_SAPLING
|
||||
chance: 5
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
dark_oak:
|
||||
logs:
|
||||
- DARK_OAK_LOG
|
||||
- STRIPPED_DARK_OAK_LOG
|
||||
- DARK_OAK_WOOD
|
||||
- STRIPPED_DARK_OAK_WOOD
|
||||
leaves:
|
||||
- DARK_OAK_LEAVES
|
||||
- VINE
|
||||
sapling: DARK_OAK_SAPLING
|
||||
plantable-soil: [ ]
|
||||
max-log-distance-from-trunk: 3
|
||||
max-leaf-distance-from-log: 5
|
||||
search-for-leaves-diagonally: false
|
||||
drop-original-log: true
|
||||
drop-original-leaf: false
|
||||
log-loot: [ ]
|
||||
leaf-loot:
|
||||
0:
|
||||
material: DARK_OAK_SAPLING
|
||||
chance: 5
|
||||
1:
|
||||
material: APPLE
|
||||
chance: 0.5
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
azalea:
|
||||
logs:
|
||||
- OAK_LOG
|
||||
- STRIPPED_OAK_LOG
|
||||
- OAK_WOOD
|
||||
- STRIPPED_OAK_WOOD
|
||||
leaves:
|
||||
- AZALEA_LEAVES
|
||||
- FLOWERING_AZALEA_LEAVES
|
||||
- VINE
|
||||
sapling: AZALEA
|
||||
plantable-soil: [ ]
|
||||
max-log-distance-from-trunk: 3
|
||||
max-leaf-distance-from-log: 4
|
||||
search-for-leaves-diagonally: true
|
||||
drop-original-log: true
|
||||
drop-original-leaf: false
|
||||
log-loot: [ ]
|
||||
leaf-loot:
|
||||
0:
|
||||
material: AZALEA
|
||||
chance: 5
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
cherry:
|
||||
logs:
|
||||
- CHERRY_LOG
|
||||
- STRIPPED_CHERRY_LOG
|
||||
- CHERRY_WOOD
|
||||
- STRIPPED_CHERRY_WOOD
|
||||
leaves:
|
||||
- CHERRY_LEAVES
|
||||
sapling: CHERRY_SAPLING
|
||||
plantable-soil: [ ]
|
||||
max-log-distance-from-trunk: 10
|
||||
max-leaf-distance-from-log: 6
|
||||
search-for-leaves-diagonally: false
|
||||
drop-original-log: true
|
||||
drop-original-leaf: false
|
||||
log-loot: [ ]
|
||||
leaf-loot:
|
||||
0:
|
||||
material: CHERRY_SAPLING
|
||||
chance: 5
|
||||
1:
|
||||
material: STICK
|
||||
chance: 2.5
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
mangrove:
|
||||
logs:
|
||||
- MANGROVE_LOG
|
||||
- STRIPPED_MANGROVE_LOG
|
||||
- MANGROVE_WOOD
|
||||
- STRIPPED_MANGROVE_WOOD
|
||||
leaves:
|
||||
- MANGROVE_LEAVES
|
||||
- MANGROVE_ROOTS
|
||||
- MOSS_CARPET
|
||||
- MANGROVE_PROPAGULE
|
||||
- VINE
|
||||
sapling: MANGROVE_PROPAGULE
|
||||
plantable-soil: [ ]
|
||||
max-log-distance-from-trunk: 30
|
||||
max-leaf-distance-from-log: 10
|
||||
search-for-leaves-diagonally: true
|
||||
drop-original-log: true
|
||||
drop-original-leaf: false
|
||||
log-loot: [ ]
|
||||
leaf-loot:
|
||||
0:
|
||||
material: MANGROVE_PROPAGULE
|
||||
chance: 5
|
||||
1:
|
||||
material: APPLE
|
||||
chance: 0.5
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
brown_mushroom:
|
||||
logs:
|
||||
- MUSHROOM_STEM
|
||||
leaves:
|
||||
- BROWN_MUSHROOM_BLOCK
|
||||
sapling: BROWN_MUSHROOM
|
||||
plantable-soil:
|
||||
- MYCELIUM
|
||||
max-log-distance-from-trunk: 4
|
||||
max-leaf-distance-from-log: 4
|
||||
search-for-leaves-diagonally: false
|
||||
drop-original-log: false
|
||||
drop-original-leaf: false
|
||||
log-loot: [ ]
|
||||
leaf-loot:
|
||||
0:
|
||||
material: BROWN_MUSHROOM
|
||||
chance: 25
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
red_mushroom:
|
||||
logs:
|
||||
- MUSHROOM_STEM
|
||||
leaves:
|
||||
- RED_MUSHROOM_BLOCK
|
||||
sapling: RED_MUSHROOM
|
||||
plantable-soil:
|
||||
- MYCELIUM
|
||||
max-log-distance-from-trunk: 4
|
||||
max-leaf-distance-from-log: 4
|
||||
search-for-leaves-diagonally: true
|
||||
drop-original-log: false
|
||||
drop-original-leaf: false
|
||||
log-loot: [ ]
|
||||
leaf-loot:
|
||||
0:
|
||||
material: RED_MUSHROOM
|
||||
chance: 25
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
huge_crimson_fungus:
|
||||
logs:
|
||||
- CRIMSON_STEM
|
||||
- STRIPPED_CRIMSON_STEM
|
||||
- CRIMSON_HYPHAE
|
||||
- STRIPPED_CRIMSON_HYPHAE
|
||||
leaves:
|
||||
- NETHER_WART_BLOCK
|
||||
- SHROOMLIGHT
|
||||
sapling: CRIMSON_FUNGUS
|
||||
plantable-soil:
|
||||
- CRIMSON_NYLIUM
|
||||
max-log-distance-from-trunk: 27
|
||||
max-leaf-distance-from-log: 5
|
||||
search-for-leaves-diagonally: false
|
||||
drop-original-log: true
|
||||
drop-original-leaf: true
|
||||
log-loot: [ ]
|
||||
leaf-loot: [ ]
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
huge_warpped_fungus:
|
||||
logs:
|
||||
- WARPED_STEM
|
||||
- STRIPPED_WARPED_STEM
|
||||
- WARPED_HYPHAE
|
||||
- STRIPPED_WARPED_HYPHAE
|
||||
leaves:
|
||||
- WARPED_WART_BLOCK
|
||||
- SHROOMLIGHT
|
||||
sapling: WARPED_FUNGUS
|
||||
plantable-soil:
|
||||
- WARPED_NYLIUM
|
||||
max-log-distance-from-trunk: 27
|
||||
max-leaf-distance-from-log: 5
|
||||
search-for-leaves-diagonally: false
|
||||
drop-original-log: true
|
||||
drop-original-leaf: true
|
||||
log-loot: [ ]
|
||||
leaf-loot: [ ]
|
||||
entire-tree-loot: [ ]
|
||||
required-tools: [ ]
|
||||
required-axe: false
|
||||
|
||||
# All soil types that the tree type's saplings can be planted on
|
||||
global-plantable-soil:
|
||||
- GRASS_BLOCK
|
||||
- DIRT
|
||||
- COARSE_DIRT
|
||||
- PODZOL
|
||||
- ROOTED_DIRT
|
||||
|
||||
# Custom loot that is available for all tree types
|
||||
# The loot applies to each log broken in the tree
|
||||
# To add more, increment the number by 1
|
||||
# The chance is out of 100 and can contain decimals
|
||||
# The default examples here are to show what you can do with custom loot
|
||||
# Valid command placeholders: %player%, %type%, %xPos%, %yPos%, %zPos%
|
||||
global-log-loot:
|
||||
0:
|
||||
material: DIAMOND
|
||||
chance: 0
|
||||
1:
|
||||
command: 'eco give %player% 5'
|
||||
chance: 0
|
||||
2:
|
||||
material: GOLDEN_APPLE
|
||||
command: 'broadcast %player% found a golden apple in a %type% tree at %xPos% %yPos% %zPos%!'
|
||||
chance: 0
|
||||
|
||||
# Custom loot that is available for all tree types
|
||||
# The loot applies to each leaf broken in the tree
|
||||
# To add more, increment the number by 1
|
||||
# The chance is out of 100 and can contain decimals
|
||||
# Valid command placeholders: %player%, %type%, %xPos%, %yPos%, %zPos%
|
||||
global-leaf-loot:
|
||||
0:
|
||||
material: GOLDEN_APPLE
|
||||
chance: 0.1
|
||||
|
||||
# Custom entire tree loot that is available for all tree types
|
||||
# The loot will be dropped only one time for the entire tree
|
||||
# To add more, increment the number by 1
|
||||
# The chance is out of 100 and can contain decimals
|
||||
# Valid command placeholders: %player%, %type%, %xPos%, %yPos%, %zPos%
|
||||
global-entire-tree-loot:
|
||||
0:
|
||||
material: DIAMOND
|
||||
chance: 0
|
||||
|
||||
# Tools that must be used to topple over a tree
|
||||
# Applies to all tree types
|
||||
global-required-tools:
|
||||
- WOODEN_AXE
|
||||
- STONE_AXE
|
||||
- IRON_AXE
|
||||
- GOLDEN_AXE
|
||||
- DIAMOND_AXE
|
||||
- NETHERITE_AXE
|
||||
|
||||
# Require the custom axe
|
||||
# Applies to all tree types
|
||||
global-required-axe: false
|
||||
|
||||
# Axe item
|
||||
required-axe:
|
||||
type: DIAMOND_AXE
|
||||
name: '&aAn Epic Axe'
|
||||
lore:
|
||||
- "&7This axe... it's awesome."
|
||||
- "&7It can chop down trees real fast."
|
||||
enchants:
|
||||
- 'DURABILITY:3'
|
||||
- 'DIG_SPEED:5'
|
||||
# NBT to identify the axe by.
|
||||
nbt: 'ultimatetimber_axe'
|
||||
|
||||
# If a tree lands on these blocks they will be destroyed.
|
||||
fragile-blocks:
|
||||
- GLASS
|
||||
- ICE
|
||||
- PACKED_ICE
|
||||
- BLUE_ICE
|
27
src/main/resources/en_US.lang
Normal file
27
src/main/resources/en_US.lang
Normal file
@ -0,0 +1,27 @@
|
||||
# General Messages
|
||||
|
||||
general:
|
||||
nametag:
|
||||
prefix: '&8[&6UltimateTimber&8] '
|
||||
nopermission: '&cYou don''t have permission for that!'
|
||||
|
||||
# Command Messages
|
||||
|
||||
command:
|
||||
reload:
|
||||
description: 'Reloads the config.'
|
||||
reloaded: '&7Configuration and locale files have been reloaded.'
|
||||
toggle:
|
||||
description: 'Toggles your chopping mode'
|
||||
enabled: '&7Chopping Mode: &aEnabled'
|
||||
disabled: '&7Chopping Mode: &cDisabled'
|
||||
give:
|
||||
not-a-player: '&cNot a player.'
|
||||
given: '&fGiven to player &a%player%'
|
||||
no-axe: '&cAxe could not be loaded.'
|
||||
|
||||
# Event Messages
|
||||
|
||||
event:
|
||||
'on':
|
||||
cooldown: '&eYou are on cooldown and cannot topple trees right now.'
|
@ -1,25 +1,45 @@
|
||||
name: UltimateTimber
|
||||
version: 0.0.3
|
||||
author: Songoda
|
||||
main: com.songoda.ultimatetimber.UltimateTimber
|
||||
name: ${project.name}
|
||||
description: ${project.description}
|
||||
version: ${project.version}
|
||||
api-version: 1.13
|
||||
|
||||
main: com.craftaro.ultimatetimber.UltimateTimber
|
||||
softdepend:
|
||||
- mcMMO
|
||||
- Jobs
|
||||
- CoreProtect
|
||||
|
||||
author: Craftaro
|
||||
website: ${project.url}
|
||||
|
||||
# TODO: cleanup commands and permissions sections
|
||||
commands:
|
||||
ultimatetimber:
|
||||
ut:
|
||||
description: Reloads the configuration file
|
||||
usage: /ultimatetimber reload
|
||||
aliases: [ut]
|
||||
aliases: [ ultimatetimber ]
|
||||
|
||||
permissions:
|
||||
ultimatetimber.*:
|
||||
description: Inherits all plugin permissions
|
||||
children:
|
||||
ultimatetimber.chop: true
|
||||
ultimatetimber.bonusloot: true
|
||||
ultimatetimber.reload: true
|
||||
ultimatetimber.bypasscooldown: true
|
||||
|
||||
ultimatetimber.chop:
|
||||
description: Allows players to trigger the trees toppling down effect
|
||||
default: op
|
||||
|
||||
ultimatetimber.bonusloot:
|
||||
description: Doubles the loot obtained from trees
|
||||
default: op
|
||||
|
||||
ultimatetimber.reload:
|
||||
description: Reloads the configuration file
|
||||
default: op
|
||||
default: op
|
||||
|
||||
ultimatetimber.bypasscooldown:
|
||||
description: Allows a player to bypass the tree topple cooldown
|
||||
default: op
|
||||
|
@ -1,25 +0,0 @@
|
||||
name: UltimateTimber
|
||||
version: 0.0.2
|
||||
author: Songoda
|
||||
main: com.songoda.ultimatetimber.UltimateTimber
|
||||
api-version: 1.13
|
||||
commands:
|
||||
ultimatetimber:
|
||||
description: Reloads the configuration file
|
||||
usage: /ultimatetimber reload
|
||||
aliases: [ut]
|
||||
permissions:
|
||||
ultimatetimber.*:
|
||||
description: Inherits all plugin permissions
|
||||
children:
|
||||
ultimatetimber.chop: true
|
||||
ultimatetimber.bonusloot: true
|
||||
ultimatetimber.chop:
|
||||
description: Allows players to trigger the trees toppling down effect
|
||||
default: op
|
||||
ultimatetimber.bonusloot:
|
||||
description: Doubles the loot obtained from trees
|
||||
default: op
|
||||
ultimatetimber.reload:
|
||||
description: Reloads the configuration file
|
||||
default: op
|
@ -1,5 +0,0 @@
|
||||
#Generated by Maven
|
||||
#Mon Nov 05 21:06:51 GMT 2018
|
||||
version=1.0-SNAPSHOT
|
||||
groupId=ultimatetimber
|
||||
artifactId=UltimateTimber
|
@ -1,18 +0,0 @@
|
||||
com\songoda\ultimatetimber\treefall\TreeReplant$1.class
|
||||
com\songoda\ultimatetimber\treefall\TreeSounds.class
|
||||
com\songoda\ultimatetimber\treefall\TreeReplant$2.class
|
||||
com\songoda\ultimatetimber\treefall\AxeDurability.class
|
||||
com\songoda\ultimatetimber\treefall\TreeReplant.class
|
||||
com\songoda\ultimatetimber\configurations\DefaultConfig.class
|
||||
com\songoda\ultimatetimber\treefall\TreeEntityDamage.class
|
||||
com\songoda\ultimatetimber\treefall\TreeFallAnimation$1.class
|
||||
com\songoda\ultimatetimber\treefall\TreeFallEvent.class
|
||||
com\songoda\ultimatetimber\commands\CommandHandler.class
|
||||
com\songoda\ultimatetimber\treefall\TreeFallAnimation$2.class
|
||||
com\songoda\ultimatetimber\treefall\EventFilter.class
|
||||
com\songoda\ultimatetimber\treefall\TreeLoot$1.class
|
||||
com\songoda\ultimatetimber\treefall\TreeLoot.class
|
||||
com\songoda\ultimatetimber\commands\ReloadCommand.class
|
||||
com\songoda\ultimatetimber\treefall\TreeFallAnimation.class
|
||||
com\songoda\ultimatetimber\UltimateTimber.class
|
||||
com\songoda\ultimatetimber\treefall\TreeChecker.class
|
@ -1,13 +0,0 @@
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\treefall\TreeFallEvent.java
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\treefall\AxeDurability.java
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\configurations\DefaultConfig.java
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\treefall\TreeSounds.java
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\commands\ReloadCommand.java
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\treefall\TreeEntityDamage.java
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\commands\CommandHandler.java
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\treefall\TreeLoot.java
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\treefall\EventFilter.java
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\treefall\TreeReplant.java
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\UltimateTimber.java
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\treefall\TreeChecker.java
|
||||
C:\Users\Tiago\Desktop\MineCraftProjects\UltimateTimber\src\main\java\com\songoda\ultimatetimber\treefall\TreeFallAnimation.java
|
Loading…
Reference in New Issue
Block a user