├── .gitattributes
├── .gitignore
├── LICENSE
├── config
├── config.yml
└── plugin.yml
├── javadoc
├── data.js
├── index.html
├── nav.js
└── style.css
├── src
└── com
│ └── sucy
│ └── enchant
│ ├── EnchantmentAPI.java
│ ├── api
│ ├── Cooldowns.java
│ ├── CustomEnchantment.java
│ ├── EnchantPlugin.java
│ ├── EnchantmentRegistry.java
│ ├── Enchantments.java
│ ├── GlowEffects.java
│ ├── ItemSet.java
│ ├── Settings.java
│ └── Tasks.java
│ ├── cmd
│ ├── CmdAdd.java
│ ├── CmdBook.java
│ ├── CmdGraph.java
│ ├── CmdReload.java
│ ├── CmdRemove.java
│ └── Commands.java
│ ├── data
│ ├── ConfigKey.java
│ ├── Configuration.java
│ ├── Enchantability.java
│ ├── Path.java
│ ├── Permission.java
│ └── PlayerEquips.java
│ ├── listener
│ ├── AnvilListener.java
│ ├── BaseListener.java
│ ├── EnchantListener.java
│ ├── FishingListener.java
│ └── ItemListener.java
│ ├── mechanics
│ ├── EnchantResult.java
│ ├── EnchantingMechanics.java
│ └── EnchantmentMerger.java
│ ├── skillapi
│ ├── SkillAPIHook.java
│ └── SkillEnchantment.java
│ ├── util
│ ├── LoreReader.java
│ ├── RomanNumerals.java
│ └── Utils.java
│ └── vanilla
│ ├── Vanilla.java
│ ├── VanillaData.java
│ └── VanillaEnchantment.java
└── tst
└── com
└── sucy
└── enchant
├── TestUtils.java
├── api
├── CooldownsTest.java
├── CustomEnchantmentTest.java
├── EnchantmentsTest.java
└── ItemSetTest.java
├── cmd
└── CmdGraphTest.java
└── util
├── LoreReaderTest.java
└── RomanNumeralsTest.java
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | out/
3 | *.iml
4 | modules/
5 |
6 | #################
7 | ## Eclipse
8 | #################
9 |
10 | *.pydevproject
11 | .project
12 | .metadata
13 | bin/
14 | tmp/
15 | *.tmp
16 | *.bak
17 | *.swp
18 | *~.nib
19 | local.properties
20 | .classpath
21 | .settings/
22 | .loadpath
23 |
24 | # External tool builders
25 | .externalToolBuilders/
26 |
27 | # Locally stored "Eclipse launch configurations"
28 | *.launch
29 |
30 | # CDT-specific
31 | .cproject
32 |
33 | # PDT-specific
34 | .buildpath
35 |
36 |
37 | #################
38 | ## Visual Studio
39 | #################
40 |
41 | ## Ignore Visual Studio temporary files, build results, and
42 | ## files generated by popular Visual Studio add-ons.
43 |
44 | # User-specific files
45 | *.suo
46 | *.user
47 | *.sln.docstates
48 |
49 | # Build results
50 |
51 | [Dd]ebug/
52 | [Rr]elease/
53 | x64/
54 | build/
55 | [Bb]in/
56 | [Oo]bj/
57 |
58 | # MSTest test Results
59 | [Tt]est[Rr]esult*/
60 | [Bb]uild[Ll]og.*
61 |
62 | *_i.c
63 | *_p.c
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.pch
68 | *.pdb
69 | *.pgc
70 | *.pgd
71 | *.rsp
72 | *.sbr
73 | *.tlb
74 | *.tli
75 | *.tlh
76 | *.tmp
77 | *.tmp_proj
78 | *.log
79 | *.vspscc
80 | *.vssscc
81 | .builds
82 | *.pidb
83 | *.log
84 | *.scc
85 |
86 | # Visual C++ cache files
87 | ipch/
88 | *.aps
89 | *.ncb
90 | *.opensdf
91 | *.sdf
92 | *.cachefile
93 |
94 | # Visual Studio profiler
95 | *.psess
96 | *.vsp
97 | *.vspx
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | *.ncrunch*
114 | .*crunch*.local.xml
115 |
116 | # Installshield output folder
117 | [Ee]xpress/
118 |
119 | # DocProject is a documentation generator add-in
120 | DocProject/buildhelp/
121 | DocProject/Help/*.HxT
122 | DocProject/Help/*.HxC
123 | DocProject/Help/*.hhc
124 | DocProject/Help/*.hhk
125 | DocProject/Help/*.hhp
126 | DocProject/Help/Html2
127 | DocProject/Help/html
128 |
129 | # Click-Once directory
130 | publish/
131 |
132 | # Publish Web Output
133 | *.Publish.xml
134 | *.pubxml
135 |
136 | # NuGet Packages Directory
137 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
138 | #packages/
139 |
140 | # Windows Azure Build Output
141 | csx
142 | *.build.csdef
143 |
144 | # Windows Store app package directory
145 | AppPackages/
146 |
147 | # Others
148 | sql/
149 | *.Cache
150 | ClientBin/
151 | [Ss]tyle[Cc]op.*
152 | ~$*
153 | *~
154 | *.dbmdl
155 | *.[Pp]ublish.xml
156 | *.pfx
157 | *.publishsettings
158 |
159 | # RIA/Silverlight projects
160 | Generated_Code/
161 |
162 | # Backup & report files from converting an old project file to a newer
163 | # Visual Studio version. Backup files are not needed, because we have git ;-)
164 | _UpgradeReport_Files/
165 | Backup*/
166 | UpgradeLog*.XML
167 | UpgradeLog*.htm
168 |
169 | # SQL Server files
170 | App_Data/*.mdf
171 | App_Data/*.ldf
172 |
173 | #############
174 | ## Windows detritus
175 | #############
176 |
177 | # Windows image file caches
178 | Thumbs.db
179 | ehthumbs.db
180 |
181 | # Folder config file
182 | Desktop.ini
183 |
184 | # Recycle Bin used on file shares
185 | $RECYCLE.BIN/
186 |
187 | # Mac crap
188 | .DS_Store
189 |
190 |
191 | #############
192 | ## Python
193 | #############
194 |
195 | *.py[co]
196 |
197 | # Packages
198 | *.egg
199 | *.egg-info
200 | dist/
201 | build/
202 | eggs/
203 | parts/
204 | var/
205 | sdist/
206 | develop-eggs/
207 | .installed.cfg
208 |
209 | # Installer logs
210 | pip-log.txt
211 |
212 | # Unit test / coverage reports
213 | .coverage
214 | .tox
215 |
216 | #Translations
217 | *.mo
218 |
219 | #Mr Developer
220 | .mr.developer.cfg
221 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Steven
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/config/config.yml:
--------------------------------------------------------------------------------
1 | # Max number of enchantments someone can receive
2 | # when using an enchantment table
3 | max-enchantments: 3
4 | #
5 | # Max number of enchantments someone can receive
6 | # when using an anvil
7 | max-merged-enchantments: 5
8 | #
9 | # Whether or not to let players use colors when
10 | # renaming items in anvils
11 | colored-names-in-anvils: true
12 | #
13 | # Whether or not to allow normally non-enchantable
14 | # items to be enchanted (e.g. blaze rods). This
15 | # will cause them to show up as books when placed
16 | # in an enchanting table.
17 | non-enchantables: true
18 | #
19 | # Global level enchantments can be combined up to.
20 | # Any enchantments with a higher max level than this
21 | # will use their max level instead.
22 | global-anvil-level: 0
23 | #
24 | # Whether or not custom enchantments can be obtained
25 | # on items received while fishing
26 | custom-fishing: true
27 | #
28 | # The enchantment level used to enchant items received
29 | # while fishing. Requires "custom-fishing" to be enabled.
30 | fishing-enchanting-level: 30
--------------------------------------------------------------------------------
/config/plugin.yml:
--------------------------------------------------------------------------------
1 | name: EnchantmentAPI
2 | main: com.sucy.enchant.EnchantmentAPI
3 | version: 1.0.6
4 | depend: [MCCore]
5 | softdepend: [SkillAPI]
6 | api-version: 1.13
7 |
8 | permissions:
9 | EnchantmentAPI.list:
10 | description: basic list of enchantments
11 | default: true
12 | EnchantmentAPI.book:
13 | description: gives a book with enchantment descriptions
14 | default: true
15 | EnchantmentAPI.admin:
16 | description: applying enchantments
17 | default: op
18 | EnchantmentAPI.table:
19 | description: getting custom enchantments from the table
20 | default: true
21 | EnchantmentAPI.enchant:
22 | description: access to all enchantments
23 | default: true
24 | EnchantmentAPI.enchant.vanilla:
25 | description: access to all vanilla enchantments
26 | default: true
--------------------------------------------------------------------------------
/javadoc/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Javadocs
6 |
7 |
8 |
9 |
10 |
11 |
13 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/javadoc/nav.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var packageIndex = PACKAGES.length;
4 | var classIndex = -1;
5 | var targetElement = window.location.href.split('?')[0].split('#')[1];
6 | var url = window.location.href.split('?')[0].split('#')[0];
7 | var py, cy;
8 |
9 | // Initializes page elements when the page loads
10 | window.onload = function () {
11 | try {
12 | getIndices();
13 | }
14 | catch (err) { }
15 | setupSideNavigation();
16 | setupContent();
17 | scrollToElement(targetElement);
18 | }
19 |
20 | // Gets the package and class indices from the window location
21 | function getIndices() {
22 | if (window.location.href.indexOf('?') < 0) return;
23 | var page = window.location.href.split('?')[1];
24 | if (page.indexOf(';') > 0) {
25 | var split = page.split(';');
26 | page = split[1];
27 | split = split[0].split(':');
28 | py = parseInt(split[0]);
29 | cy = parseInt(split[1]);
30 | setTimeout(function () {
31 | document.querySelector('#packageList').scrollTop = py;
32 | document.querySelector('#classList').scrollTop = cy;
33 | console.log(py + ", " + cy + " -> "
34 | + document.querySelector('#packageList').scrollTop + ", "
35 | + document.querySelector('#classList').scrollTop);
36 | }, 1);
37 |
38 | }
39 | if (page != '*') {
40 | var parts = page.split(':');
41 | var i;
42 | for (i = 0; i < PACKAGES.length; i++) {
43 | if (PACKAGES[i] == parts[0]) {
44 | packageIndex = i;
45 | break;
46 | }
47 | }
48 | if (parts[1]) {
49 | classIndex = getClassId(parts[1]);
50 | }
51 | }
52 | }
53 |
54 | function linkElement(name, id) {
55 | if (!id) id = name;
56 | var a = document.createElement('a');
57 | a.addEventListener('click', elementClicked);
58 | a['eId'] = id;
59 | a.innerHTML = name;
60 | return a;
61 | }
62 |
63 | function elementClicked() {
64 | window.location.href = url + '#' + this.eId + '?' + getScrollData() + PACKAGES[packageIndex] + ':' + getClass(classIndex).name;
65 | }
66 |
67 | function scrollToElement(element) {
68 | var target = document.querySelector('#' + element);
69 | if (target) {
70 | document.querySelector('#main').scrollTop = target.offsetTop;
71 | }
72 | }
73 |
74 | // Links the class to it's URL
75 | function linkClass(name) {
76 | var lt = name.indexOf('<');
77 | if (lt > 0) {
78 | var container = document.createElement('span');
79 | container.appendChild(linkClass(name.substr(0, lt)));
80 | container.appendChild(linkClass("<"));
81 | var comma = name.indexOf(",");
82 | if (comma > lt) {
83 | container.appendChild(linkClass(name.substring(lt + 1, comma)));
84 | container.appendChild(linkClass(", "));
85 | container.appendChild(linkClass(name.substring(comma + 1, name.length - 1)));
86 | }
87 | else container.appendChild(linkClass(name.substring(lt + 1, name.length - 1)));
88 | container.appendChild(linkClass(">"));
89 | return container;
90 | }
91 | var link = linkClassList(name, INTERFACES);
92 | if (!link) link = linkClassList(name, CLASSES);
93 | if (!link) link = linkClassList(name, ENUMS);
94 | if (!link) link = linkClassList(name, ANNOTATIONS);
95 | if (!link) link = linkClassList(name, EXCEPTIONS);
96 | if (link) return link;
97 | var e = document.createElement('span');
98 | e.innerHTML = name;
99 | return e;
100 | }
101 |
102 | // Searches through a list to find the URL of the class
103 | function linkClassList(name, list) {
104 | for (var i = 0; i < list.length; i++) {
105 | var subList = list[i];
106 | for (var j = 0; j < subList.length; j++) {
107 | if (subList[j].name == name) {
108 | var l = document.createElement('a');
109 | l.addEventListener('click', linkClick);
110 | l['linkKey'] = PACKAGES[i] + ':' + name;
111 | l.innerHTML = name;
112 | return l;
113 | }
114 | }
115 | }
116 | return false;
117 | }
118 |
119 | function linkClick() {
120 | window.location.href = url + '?' + getScrollData() + this.linkKey;
121 | }
122 |
123 | // Sets up the sidebar navigation
124 | function setupSideNavigation() {
125 | if (PACKAGES.length == 0) return;
126 | var pList = document.querySelector('#packageList');
127 | var allLink = document.createElement('a');
128 | allLink.innerHTML = 'All Classes';
129 | allLink.className = 'packageLink';
130 | allLink.packageIndex = PACKAGES.length;
131 | if (packageIndex == PACKAGES.length) allLink.className += ' selectedPackage';
132 | allLink.onclick = linkClicked;
133 | pList.appendChild(allLink);
134 | addTag(pList, 'p', 'Packages');
135 | for (var i = 0; i < PACKAGES.length; i++) {
136 | var link = document.createElement('a');
137 | link.innerHTML = PACKAGES[i];
138 | link.className = 'packageLink';
139 | link.packageIndex = i
140 | if (i == packageIndex) link.className += ' selectedPackage';
141 | link.onclick = linkClicked;
142 | pList.appendChild(link);
143 | }
144 |
145 | updateClasses();
146 | }
147 |
148 | function linkClicked() {
149 | window.location.href = url + '?' + getScrollData() +
150 | (this.innerHTML == 'All Classes' ? '*'
151 | : this.className.charAt(0) != 'c' ? PACKAGES[this.packageIndex]
152 | : packageIndex == PACKAGES.length ? this.package + ':' + this.innerHTML
153 | : PACKAGES[packageIndex] + ':' + this.innerHTML);
154 | }
155 |
156 | function getScrollData() {
157 | return document.querySelector('#packageList').scrollTop + ':'
158 | + document.querySelector('#classList').scrollTop + ';';
159 | }
160 |
161 | // Sets up the content in the center panel
162 | function setupContent() {
163 | if (packageIndex == PACKAGES.length) setupOverview();
164 | else if (classIndex == -1) setupPackage();
165 | else setupClass();
166 | }
167 |
168 | // Sets up the content for the overview
169 | function setupOverview() {
170 | var content = getMainSection();
171 |
172 | // General data
173 | if (NAME) addTag(content, 'h1', NAME);
174 | if (DESCRIPTION) addTag(content, 'p', DESCRIPTION);
175 |
176 | // Authors
177 | var row;
178 | if (AUTHORS.length > 0) {
179 | addTag(content, 'header', 'Authors');
180 | var table = addTag(content, 'table');
181 | for (var i = 0; i < AUTHORS.length; i++) {
182 | row = addTag(table, 'tr');
183 | addTag(row, 'td', AUTHORS[i]);
184 | }
185 | }
186 |
187 | // Depends
188 | var row;
189 | if (DEPENDS.length > 0) {
190 | addTag(content, 'header', 'Dependencies');
191 | var table = addTag(content, 'table');
192 | for (var i = 0; i < DEPENDS.length; i++) {
193 | row = addTag(table, 'tr');
194 | addTag(row, 'td', DEPENDS[i]);
195 | }
196 | }
197 |
198 | // Soft Depends
199 | var row;
200 | if (SOFTDEPENDS.length > 0) {
201 | addTag(content, 'header', 'Soft Dependencies');
202 | var table = addTag(content, 'table');
203 | for (var i = 0; i < SOFTDEPENDS.length; i++) {
204 | row = addTag(table, 'tr');
205 | addTag(row, 'td', SOFTDEPENDS[i]);
206 | }
207 | }
208 |
209 | // Load Before
210 | var row;
211 | if (LOAD_BEFORE.length > 0) {
212 | addTag(content, 'header', 'Loads Before');
213 | var table = addTag(content, 'table');
214 | for (var i = 0; i < LOAD_BEFORE.length; i++) {
215 | row = addTag(table, 'tr');
216 | addTag(row, 'td', LOAD_BEFORE[i]);
217 | }
218 | }
219 |
220 | // Package table
221 | addTag(content, 'header', 'Packages');
222 | var table = addTag(content, 'table');
223 | for (var i = 0; i < PACKAGES.length; i++) {
224 | row = addTag(table, 'tr');
225 | var td = addTag(row, 'td', PACKAGES[i]);
226 | td.className = 'packageLink';
227 | td.packageIndex = i;
228 | td.onclick = linkClicked;
229 | //addTag(row, 'td', 'Not set so this is placeholder');
230 | }
231 | }
232 |
233 | // Sets up the content for a package
234 | function setupPackage() {
235 | var content = getMainSection();
236 | addTag(content, 'h2', 'Package ' + PACKAGES[packageIndex]);
237 |
238 | addClassTable(content, 'Interfaces', INTERFACES[packageIndex]);
239 | addClassTable(content, 'Classes', CLASSES[packageIndex]);
240 | addClassTable(content, 'Enums', ENUMS[packageIndex]);
241 | addClassTable(content, 'Exceptions', EXCEPTIONS[packageIndex]);
242 | addClassTable(content, 'Annotations', ANNOTATIONS[packageIndex]);
243 | }
244 |
245 | // Sets up the content for a class
246 | function setupClass() {
247 | var content = getMainSection();
248 | var c = getClass(classIndex);
249 |
250 | // Main information
251 | var header = c.type.charAt(0).toUpperCase() + c.type.slice(1) + ' ' + c.name;
252 | addTag(content, 'h2', header);
253 | addTag(content, 'code', c.scope + ' ' + (c.isStatic ? 'static ' : '') + (c.isFinal ? 'final ' : '') + (c.isAbstract ? 'abstract ' : '') + c.type + ' ' + c.name);
254 | if (c.ext) {
255 | var tag = addTag(content, 'code', 'extends ');
256 | tag.appendChild(linkClass(c.ext));
257 | }
258 | if (c.impl.length > 0) {
259 | var tag = addTag(content, 'code', 'implements ');
260 | for (var k = 0; k < c.impl.length; k++) {
261 | tag.appendChild(linkClass(c.impl[k]));
262 | if (k != c.impl.length - 1) tag.appendChild(linkClass(', '));
263 | }
264 | }
265 | addTag(content, 'hr');
266 | addTag(content, 'p', c.description);
267 |
268 | var table, row, td, text, i, j;
269 |
270 | // Fields
271 | if (c.fields.length > 1) {
272 | if (c.type == 'enum') {
273 | addTag(content, 'header', 'Enum Constants');
274 | table = addTag(content, 'table');
275 | for (i = 1; i < c.fields.length; i++) {
276 | row = addTag(table, 'tr');
277 | td = addTag(row, 'td');
278 | td.appendChild(linkElement(c.fields[i].name));
279 | }
280 | }
281 | else {
282 | addTag(content, 'header', 'Fields');
283 | table = addTag(content, 'table');
284 | for (i = 1; i < c.fields.length; i++) {
285 | row = addTag(table, 'tr');
286 | td = addTag(row, 'td');
287 | td.appendChild(linkClass(c.fields[i].type));
288 | td = addTag(row, 'td');
289 | td.appendChild(linkElement(c.fields[i].name));
290 | }
291 | }
292 | }
293 |
294 | // Constructors
295 | if (c.constructors.length > 0) {
296 | addTag(content, 'header', 'Constructors');
297 | table = addTag(content, 'table');
298 | for (i = 0; i < c.constructors.length; i++) {
299 | row = addTag(table, 'tr');
300 | td = addTag(row, 'td');
301 | td.appendChild(linkElement(c.constructors[i].name, c.constructors[i].name + i));
302 | addTag(td, 'span', '(');
303 | for (j = 0; j < c.constructors[i].params.length; j++) {
304 | td.appendChild(linkClass(c.constructors[i].params[j].type));
305 | addTag(td, 'span', ' ' + c.constructors[i].params[j].name + (j != c.constructors[i].params.length - 1 ? ', ' : ''));
306 | }
307 | addTag(td, 'span', ')');
308 | }
309 | }
310 |
311 | // Make sure there's at least one method and static method
312 | var oneMethod, oneStaticMethod;
313 | for (i = 0; i < c.methods.length; i++) {
314 | if (c.methods[i].isStatic) oneStaticMethod = true;
315 | else oneMethod = true;
316 | }
317 |
318 | // Methods
319 | if (oneMethod) {
320 |
321 | // Annotations
322 | if (c.type == 'annotation') {
323 | addTag(content, 'header', 'Optional Elements');
324 | table = addTag(content, 'table');
325 | for (i = 0; i < c.methods.length; i++) {
326 | if (c.methods[i].isStatic) continue;
327 | row = addTag(table, 'tr');
328 | td = addTag(row, 'td');
329 | td.appendChild(linkClass(c.methods[i].returnValue.type));
330 | td = addTag(row, 'td');
331 | td.appendChild(linkElement(c.methods[i].name, c.methods[i].name + i));
332 | }
333 | }
334 |
335 | // Normal methods
336 | else {
337 | addTag(content, 'header', 'Methods');
338 | table = addTag(content, 'table');
339 | for (i = 0; i < c.methods.length; i++) {
340 | if (c.methods[i].isStatic) continue;
341 | row = addTag(table, 'tr');
342 | td = addTag(row, 'td');
343 | td.appendChild(linkClass(c.methods[i].returnValue.type));
344 | td = addTag(row, 'td');
345 | td.appendChild(linkElement(c.methods[i].name, c.methods[i].name + i));
346 | addTag(td, 'span', '(');
347 | for (j = 0; j < c.methods[i].params.length; j++) {
348 | td.appendChild(linkClass(c.methods[i].params[j].type));
349 | addTag(td, 'span', ' ' + c.methods[i].params[j].name + (j != c.methods[i].params.length - 1 ? ', ' : ''));
350 | }
351 | addTag(td, 'span', ')');
352 | }
353 | }
354 | }
355 |
356 | // Static Methods
357 | if (oneStaticMethod) {
358 | addTag(content, 'header', 'Static Methods');
359 | table = addTag(content, 'table');
360 | for (i = 0; i < c.methods.length; i++) {
361 | if (!c.methods[i].isStatic) continue;
362 | row = addTag(table, 'tr');
363 | var td = addTag(row, 'td');
364 | td.appendChild(linkClass(c.methods[i].returnValue.type));
365 | td = addTag(row, 'td');
366 | td.appendChild(linkElement(c.methods[i].name, c.methods[i].name + i));
367 | addTag(td, 'span', '(');
368 | for (j = 0; j < c.methods[i].params.length; j++) {
369 | td.appendChild(linkClass(c.methods[i].params[j].type));
370 | addTag(td, 'span', ' ' + c.methods[i].params[j].name + (j != c.methods[i].params.length - 1 ? ', ' : ''));
371 | }
372 | addTag(td, 'span', ')');
373 | }
374 | }
375 |
376 | ///////////////////////////////////////////////
377 | // //
378 | // Full Element Descriptions and Summaries //
379 | // //
380 | ///////////////////////////////////////////////
381 |
382 | // Fields
383 | if (c.fields.length > 1) {
384 | if (c.type == 'enum') {
385 | addTag(content, 'header', 'Enum Constants Detail');
386 | for (i = 1; i < c.fields.length; i++) {
387 | var header = addTag(content, 'h3', c.fields[i].name);
388 | header.id = c.fields[i].name;
389 | addTag(header, 'code', 'public static final ' + c.name + ' ' + c.fields[i].name);
390 | addTag(header, 'p', c.fields[i].description);
391 | if (i != c.fields.length - 1) addTag(content, 'hr');
392 | }
393 | }
394 | else {
395 | addTag(content, 'header', 'Fields Detail');
396 | for (i = 1; i < c.fields.length; i++) {
397 | var header = addTag(content, 'h3', c.fields[i].name);
398 | header.id = c.fields[i].name;
399 | addTag(header, 'code', c.fields[i].scope + ' '
400 | + (c.fields[i].isStatic ? 'static ' : '')
401 | + (c.fields[i].isFinal ? 'final ' : '')
402 | + (c.fields[i].isAbstract ? 'abstract ' : '')
403 | + c.fields[i].type + ' ' + c.fields[i].name);
404 | addTag(header, 'p', c.fields[i].description);
405 | if (i != c.fields.length - 1) addTag(content, 'hr');
406 | }
407 | }
408 | }
409 |
410 | // Constructors
411 | if (c.constructors.length > 0) {
412 | addTag(content, 'header', 'Constructors Detail');
413 | for (i = 0; i < c.constructors.length; i++) {
414 | var header = addTag(content, 'h3', c.name);
415 | header.id = c.name + i;
416 | text = c.constructors[i].scope + ' ' + c.name + '(';
417 | for (j = 0; j < c.constructors[i].params.length; j++) {
418 | text += c.constructors[i].params[j].type + ' ' + c.constructors[i].params[j].name;
419 | if (j != c.constructors[i].params.length - 1) text += ', ';
420 | }
421 | addTag(header, 'code', text + ')');
422 | addTag(header, 'p', c.constructors[i].description);
423 | if (c.constructors[i].params.length > 0) {
424 | var subHeader = addTag(header, 'h4', 'params:');
425 | for (j = 0; j < c.constructors[i].params.length; j++) {
426 | addTag(subHeader, 'p', '' + c.constructors[i].params[j].name + ' - ' + c.constructors[i].params[j].description);
427 | }
428 | }
429 | if (i != c.constructors.length - 1) addTag(content, 'hr');
430 | }
431 | }
432 |
433 | // Methods
434 | if (oneMethod) {
435 |
436 | // Annotations
437 | if (c.type == "annotation") {
438 | for (i = 0; i < c.methods.length; i++) {
439 | if (c.methods[i].isStatic) continue;
440 | var header = addTag(content, 'h3', c.methods[i].name);
441 | header.id = c.methods[i].name + i;
442 | addTag(header, 'code', c.methods[i].scope + ' abstract ' + c.methods[i].returnValue.type + ' ' + c.methods[i].name);
443 | addTag(header, 'p', c.methods[i].description);
444 | var subHeader = addTag(header, 'h4', 'Returns:');
445 | addTag(subHeader, 'p', c.methods[i].returnValue.description);
446 | addTag(content, 'hr');
447 | }
448 | }
449 |
450 | // Normal methods
451 | else {
452 | addTag(content, 'header', 'Methods Detail');
453 | for (i = 0; i < c.methods.length; i++) {
454 | if (c.methods[i].isStatic) continue;
455 | var header = addTag(content, 'h3', c.methods[i].name);
456 | header.id = c.methods[i].name + i;
457 | text = c.methods[i].scope + ' ' + c.methods[i].returnValue.type + ' ' + c.methods[i].name + '(';
458 | for (j = 0; j < c.methods[i].params.length; j++) {
459 | text += c.methods[i].params[j].type + ' ' + c.methods[i].params[j].name;
460 | if (j != c.methods[i].params.length - 1) text += ', ';
461 | }
462 | addTag(header, 'code', text + ')');
463 | addTag(header, 'p', c.methods[i].description);
464 | if (c.methods[i].params.length > 0) {
465 | var subHeader = addTag(header, 'h4', 'Parameters:');
466 | for (j = 0; j < c.methods[i].params.length; j++) {
467 | addTag(subHeader, 'p', '' + c.methods[i].params[j].name + ' - ' + c.methods[i].params[j].description);
468 | }
469 | }
470 | if (c.methods[i].returnValue.type != 'void') {
471 | var subHeader = addTag(header, 'h4', 'Returns:');
472 | addTag(subHeader, 'p', c.methods[i].returnValue.description);
473 | }
474 | addTag(content, 'hr');
475 | }
476 | }
477 | content.removeChild(content.lastChild);
478 | }
479 |
480 | // Static Methods
481 | if (oneStaticMethod) {
482 | addTag(content, 'header', 'Static Methods Detail');
483 | for (i = 0; i < c.methods.length; i++) {
484 | if (!c.methods[i].isStatic) continue;
485 | var header = addTag(content, 'h3', c.methods[i].name);
486 | header.id = c.methods[i].name + i;
487 | text = c.methods[i].scope + ' ' + c.methods[i].returnValue.type + ' ' + c.methods[i].name + '(';
488 | for (j = 0; j < c.methods[i].params.length; j++) {
489 | text += c.methods[i].params[j].type + ' ' + c.methods[i].params[j].name;
490 | if (j != c.methods[i].params.length - 1) text += ', ';
491 | }
492 | addTag(header, 'code', text + ')');
493 | addTag(header, 'p', c.methods[i].description);
494 | if (c.methods[i].params.length > 0) {
495 | var subHeader = addTag(header, 'h4', 'params:');
496 | for (j = 0; j < c.methods[i].params.length; j++) {
497 | addTag(subHeader, 'p', '' + c.methods[i].params[j].name + ' - ' + c.methods[i].params[j].description);
498 | }
499 | }
500 | if (c.methods[i].returnValue.type != 'void') {
501 | var subHeader = addTag(header, 'h4', 'Returns:');
502 | addTag(subHeader, 'p', c.methods[i].returnValue.description);
503 | }
504 | addTag(content, 'hr');
505 | }
506 | content.removeChild(content.lastChild);
507 | }
508 | }
509 |
510 | // Retrieves the main HTML section
511 | function getMainSection() {
512 | return document.querySelector('#main');
513 | }
514 |
515 | // Updates the list of classes in the navigation
516 | var classId;
517 | function updateClasses() {
518 | var cList = document.querySelector('#classList');
519 | while (cList.hasChildNodes()) {
520 | cList.removeChild(cList.lastChild);
521 | }
522 | classId = 0;
523 | if (packageIndex == PACKAGES.length) {
524 | addTag(cList, 'p', 'Classes');
525 | for (var i = 0; i < ALL_CLASSES.length; i++, classId++) {
526 | var parts = ALL_CLASSES[i].split(':');
527 | var link = addTag(cList, 'a', parts[parts.length - 1]);
528 | link.className = 'classLink';
529 | link.package = parts[0];
530 | link.onclick = linkClicked;
531 | cList.appendChild(link);
532 | }
533 | }
534 | else {
535 | addClassList(cList, 'Interfaces', INTERFACES[packageIndex]);
536 | addClassList(cList, 'Classes', CLASSES[packageIndex]);
537 | addClassList(cList, 'Enums', ENUMS[packageIndex]);
538 | addClassList(cList, 'Exceptions', EXCEPTIONS[packageIndex]);
539 | addClassList(cList, 'Annotations', ANNOTATIONS[packageIndex]);
540 | }
541 | }
542 |
543 | function addClassList(element, title, list) {
544 | if (list.length == 0) return;
545 | addTag(element, 'p', title);
546 | for (var i = 0; i < list.length; i++, classId++) {
547 | var link = document.createElement('a');
548 | link.innerHTML = list[i].name;
549 | link.className = 'classLink';
550 | if (classId == classIndex) {
551 | link.className += ' selectedClass';
552 | }
553 | link.onclick = linkClicked;
554 | element.appendChild(link);
555 | }
556 | }
557 |
558 | function addClassTable(element, title, list) {
559 | if (list.length == 0) return;
560 | addTag(element, 'header', title);
561 | var table = addTag(element, 'table');
562 | var row;
563 | for (var i = 0; i < list.length; i++, classId++) {
564 | row = addTag(table, 'tr');
565 | var link = addTag(row, 'td', list[i].name);
566 | link.className = 'classLink';
567 | link.onclick = linkClicked;
568 | var desc = list[i].description;
569 | var copyright = desc.indexOf('©');
570 | var space = desc.indexOf(' ', copyright + 10);
571 | if (copyright > 0 && space > 0) desc = desc.substr(space + 1);
572 | else if (copyright > 0) desc = 'No description provided';
573 | while (desc.indexOf('
') == 0) desc = desc.substr(4);
574 | var desc = addTag(row, 'td', desc.split('')[0].replace('', '').split('. ')[0] + '.');
575 | link.style.maxHeight = link.style.height = (desc.clientHeight - 14) + 'px';
576 | }
577 | }
578 |
579 | function getClassId(name) {
580 | classId = 0;
581 | var id = checkClassId(name, INTERFACES[packageIndex]);
582 | if (id == -1) id = checkClassId(name, CLASSES[packageIndex]);
583 | if (id == -1) id = checkClassId(name, ENUMS[packageIndex]);
584 | if (id == -1) id = checkClassId(name, EXCEPTIONS[packageIndex]);
585 | if (id == -1) id = checkClassId(name, ANNOTATIONS[packageIndex]);
586 | return id;
587 | }
588 |
589 | function getClass(id) {
590 | classId = 0;
591 | var valid = checkClass(id, INTERFACES[packageIndex]);
592 | if (!valid) valid = checkClass(id, CLASSES[packageIndex]);
593 | if (!valid) valid = checkClass(id, ENUMS[packageIndex]);
594 | if (!valid) valid = checkClass(id, EXCEPTIONS[packageIndex]);
595 | if (!valid) valid = checkClass(id, ANNOTATIONS[packageIndex]);
596 | return valid;
597 | }
598 |
599 | function checkClassId(name, list) {
600 | for (var i = 0; i < list.length; i++, classId++) {
601 | if (list[i].name == name) return classId;
602 | }
603 | return -1;
604 | }
605 |
606 | function checkClass(id, list) {
607 | if (classId + list.length <= id) {
608 | classId += list.length;
609 | return false;
610 | }
611 | else return list[id - classId];
612 | }
613 |
614 | // Adds a title to the element using the 'p' tag
615 | function addTag(element, tag, text) {
616 | var p = document.createElement(tag);
617 | if (text) p.innerHTML = text;
618 | element.appendChild(p);
619 | return p;
620 | }
621 |
--------------------------------------------------------------------------------
/javadoc/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | padding: 0;
3 | margin: 0;
4 | }
5 |
6 | a, a:visited {
7 | color: #0ce3ac;
8 | display: inline-block;
9 | padding: 1px 3px;
10 | text-decoration: none;
11 | cursor: pointer;
12 | border-radius: 5px;
13 | }
14 |
15 | a:hover {
16 | color: white;
17 | background: #008966;
18 | }
19 |
20 | body {
21 | overflow: hidden;
22 | background-color: #333;
23 | color: White;
24 | height: 100%;
25 | width: 100%;
26 | }
27 |
28 | html {
29 | height: 100%;
30 | width: 100%;
31 | }
32 |
33 | /***********
34 | * General *
35 | ***********/
36 |
37 | h1 {
38 | font-weight: bold;
39 | font-size: 42px;
40 | padding: 10px;
41 | }
42 |
43 | h2
44 | {
45 | font-weight: bold;
46 | font-size: 36px;
47 | padding: 10px;
48 | }
49 |
50 | h3
51 | {
52 | font-weight: bold;
53 | font-size: 24px;
54 | padding: 10px;
55 | }
56 |
57 | h4
58 | {
59 | font-size: 20px;
60 | padding-left: 30px;
61 | }
62 |
63 | header
64 | {
65 | background-color: #0d58a6;
66 | font-size: 25px;
67 | font-weight: bold;
68 | text-align: left;
69 | padding: 3px 10px;
70 | border: 2px solid #666;
71 | }
72 |
73 | p {
74 | font-size: 25px;
75 | font-weight: bold;
76 | padding: 10px 9px;
77 | }
78 |
79 | table
80 | {
81 | width: 100%;
82 | border: 2px solid #666;
83 | border-collapse: collapse;
84 | margin-bottom: 20px;
85 | border-top: none;
86 | }
87 |
88 | td
89 | {
90 | font-size: 18px;
91 | font-weight: normal;
92 | padding: 6px 5px;
93 | background-color: #222;
94 | border: 2px solid #666;
95 | border-radius: 10px;
96 | margin: 2px;
97 | }
98 |
99 | td p
100 | {
101 | font-size: 18px;
102 | font-weight: normal;
103 | padding: 0;
104 | }
105 |
106 | ul
107 | {
108 | list-style-position: inside;
109 | }
110 |
111 | ul li
112 | {
113 | margin-bottom: 10px;
114 | }
115 |
116 | /***********
117 | * Classes *
118 | ***********/
119 |
120 | .inline {
121 | display: inline-block;
122 | }
123 |
124 | .packageLink, .packageLink:visited, .classLink, .classLink:visited {
125 | color: white;
126 | display: block;
127 | margin: 2px;
128 | padding: 5px;
129 | background-color: #222;
130 | border: none;
131 | border-radius: 0;
132 | font-size: 18px;
133 | overflow: hidden;
134 | transition: background-color 500ms;
135 | cursor: pointer;
136 | text-decoration: none;
137 | }
138 |
139 | .packageLink:hover, .classLink:hover {
140 | background-color: #0c9;
141 | color: white;
142 | text-decoration: none;
143 | }
144 |
145 | .selectedPackage, .selectedPackage:visited, .selectedPackage:hover, .selectedClass, .selectedClass:visited, .selectedClass:hover {
146 | background-color: #008966;
147 | color: white;
148 | text-decoration: none;
149 | cursor: default;
150 | }
151 |
152 | /***************
153 | * Identifiers *
154 | ***************/
155 |
156 | #leftPanel
157 | {
158 | position: absolute;
159 | width: 250px;
160 | height: 100%;
161 | top: 0;
162 | left: 0;
163 | }
164 |
165 | #packageList {
166 | border-bottom: 2px solid #666;
167 | height: calc(38% - 2px);
168 | overflow-x: auto;
169 | overflow-y: scroll;
170 | }
171 |
172 | #classList {
173 | height: 62%;
174 | overflow-x: auto;
175 | overflow-y: scroll;
176 | }
177 |
178 | #main
179 | {
180 | width: calc(100% - 250px);
181 | max-width: 100%;
182 | height: 100%;
183 | max-height: 100%;
184 | padding-left: 250px;
185 | overflow-y: auto;
186 | }
187 |
188 | #main p
189 | {
190 | font-size: 18px;
191 | font-weight: normal;
192 | }
193 |
194 | #main code
195 | {
196 | font-size: 16px;
197 | padding-left: 10px;
198 | padding-bottom: 15px;
199 | display: block;
200 | max-width: 100%;
201 | }
202 |
203 | #main h4 p
204 | {
205 | padding: 3px 0;
206 | padding-left: 20px;
207 |
208 | }
--------------------------------------------------------------------------------
/src/com/sucy/enchant/EnchantmentAPI.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant;
2 |
3 | import com.rit.sucy.commands.CommandManager;
4 | import com.sucy.enchant.api.CustomEnchantment;
5 | import com.sucy.enchant.api.EnchantPlugin;
6 | import com.sucy.enchant.api.EnchantmentRegistry;
7 | import com.sucy.enchant.api.Enchantments;
8 | import com.sucy.enchant.cmd.Commands;
9 | import com.sucy.enchant.data.ConfigKey;
10 | import com.sucy.enchant.data.Configuration;
11 | import com.sucy.enchant.data.Enchantability;
12 | import com.sucy.enchant.listener.AnvilListener;
13 | import com.sucy.enchant.listener.BaseListener;
14 | import com.sucy.enchant.listener.EnchantListener;
15 | import com.sucy.enchant.listener.FishingListener;
16 | import com.sucy.enchant.listener.ItemListener;
17 | import com.sucy.enchant.skillapi.SkillAPIHook;
18 | import com.sucy.enchant.vanilla.VanillaData;
19 | import org.bukkit.Bukkit;
20 | import org.bukkit.event.HandlerList;
21 | import org.bukkit.plugin.Plugin;
22 | import org.bukkit.plugin.java.JavaPlugin;
23 |
24 | import java.util.ArrayList;
25 | import java.util.Collection;
26 | import java.util.HashMap;
27 | import java.util.List;
28 | import java.util.Map;
29 | import java.util.Objects;
30 |
31 | /**
32 | * EnchantmentAPI © 2017
33 | * com.sucy.enchant.EnchantmentAPI
34 | */
35 | public class EnchantmentAPI extends JavaPlugin implements EnchantmentRegistry {
36 |
37 | private static final Map ENCHANTMENTS = new HashMap<>();
38 |
39 | private final List listeners = new ArrayList<>();
40 |
41 | private static EnchantmentAPI enabled;
42 |
43 | @Override
44 | public void onEnable() {
45 | if (enabled != null) throw new IllegalStateException("Cannot enable multiple times!");
46 | enabled = this;
47 |
48 | Configuration.reload(this);
49 | Enchantability.init(this);
50 |
51 | registerEnchantments();
52 | register(new ItemListener(), true);
53 | register(new EnchantListener(), true);
54 | register(new AnvilListener(), true);
55 | register(new FishingListener(), Configuration.using(ConfigKey.CUSTOM_FISHING));
56 | Commands.init(this);
57 | }
58 |
59 | @Override
60 | public void onDisable() {
61 | if (enabled == null) throw new IllegalStateException("Plugin not enabled!");
62 | enabled = null;
63 |
64 | CommandManager.unregisterCommands(this);
65 | ENCHANTMENTS.clear();
66 | listeners.forEach(listener -> listener.cleanUp(this));
67 | listeners.clear();
68 | HandlerList.unregisterAll(this);
69 | Enchantments.clearAllEquipmentData();
70 | }
71 |
72 | private void register(final BaseListener listener, final boolean condition) {
73 | if (condition) {
74 | listeners.add(listener);
75 | listener.init(this);
76 | getServer().getPluginManager().registerEvents(listener, this);
77 | }
78 | }
79 |
80 | /**
81 | * @param name enchantment name
82 | * @return true if the enchantment is registered successfully, false otherwise
83 | */
84 | public static boolean isRegistered(final String name) {
85 | return name != null && ENCHANTMENTS.containsKey(name.toLowerCase());
86 | }
87 |
88 | /**
89 | * @param name name of the enchantment (not case-sensitive)
90 | * @return enchantment with the provided name
91 | */
92 | public static CustomEnchantment getEnchantment(final String name) {
93 | return name == null ? null : ENCHANTMENTS.get(name.toLowerCase());
94 | }
95 |
96 | /**
97 | * @return collection of all registered enchantments including vanilla enchantments
98 | */
99 | public static Collection getRegisteredEnchantments() {
100 | return ENCHANTMENTS.values();
101 | }
102 |
103 | /**
104 | * Registers enchantments with the API
105 | *
106 | * @param enchantments enchantments to register
107 | */
108 | @Override
109 | public void register(final CustomEnchantment... enchantments) {
110 | for (final CustomEnchantment enchantment : enchantments) {
111 | final String key = enchantment.getName().toLowerCase();
112 | if (ENCHANTMENTS.containsKey(key)) {
113 | getLogger().warning("Duplicate enchantment name \"" + enchantment.getName() + "\" was found");
114 | continue;
115 | }
116 |
117 | Objects.requireNonNull(enchantment, "Cannot register a null enchantment");
118 | enchantment.load(this);
119 | enchantment.save(this);
120 | ENCHANTMENTS.put(key, enchantment);
121 | }
122 | }
123 |
124 | private void registerEnchantments() {
125 | for (final VanillaData vanillaData : VanillaData.values()) {
126 | if (vanillaData.doesExist()) {
127 | register(vanillaData.getEnchantment());
128 | }
129 | }
130 |
131 | for (final Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
132 | if (plugin instanceof EnchantPlugin) {
133 | try {
134 | ((EnchantPlugin) plugin).registerEnchantments(this);
135 | } catch (final Exception ex) {
136 | getLogger().warning(plugin.getName() + " failed to register enchantments. Send the error to the author.");
137 | ex.printStackTrace();
138 | }
139 | }
140 | }
141 |
142 | SkillAPIHook.getEnchantments(this).forEach(this::register);
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/api/Cooldowns.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | import org.bukkit.entity.LivingEntity;
4 |
5 | import java.time.Clock;
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | /**
10 | * EnchantmentAPI © 2017
11 | * com.sucy.enchant.api.Cooldowns
12 | *
13 | * Helper class for managing cooldowns for enchantments
14 | */
15 | public class Cooldowns {
16 | private static final String COOLDOWN = "cooldown";
17 |
18 | private static final Map cooldowns = new HashMap<>();
19 |
20 | private static final Clock clock = Clock.systemUTC();
21 |
22 | /**
23 | * Configures the cooldown for an enchantment
24 | *
25 | * @param settings settings of the enchantment
26 | * @param base base value to configure
27 | * @param scale scaling value to configure
28 | */
29 | public static void configure(final Settings settings, final double base, final double scale) {
30 | settings.set(COOLDOWN, base, scale);
31 | }
32 |
33 | /**
34 | * Computes the number of seconds left on the enchantment's cooldown,
35 | * rounded up to the nearest second.
36 | *
37 | * @param enchant enchantment
38 | * @param user player using the enchantment
39 | * @param settings settings of the enchantment
40 | * @param level enchantment level
41 | * @return seconds left
42 | */
43 | public static int secondsLeft(final CustomEnchantment enchant, final LivingEntity user, final Settings settings, final int level) {
44 | final String key = makeKey(enchant, user);
45 | final long time = cooldowns.getOrDefault(key, 0L);
46 | return (int)Math.ceil(settings.get(COOLDOWN, level) - (clock.millis() - time) / 1000.0);
47 | }
48 |
49 | /**
50 | * Checks whether or not the enchantment is on cooldown
51 | *
52 | * @param enchant enchantment to check
53 | * @param user player using the enchantment
54 | * @param settings settings of the enchantment
55 | * @param level enchantment level
56 | * @return true if on cooldown, false otherwise
57 | */
58 | public static boolean onCooldown(final CustomEnchantment enchant, final LivingEntity user, final Settings settings, final int level) {
59 | return secondsLeft(enchant, user, settings, level) > 0;
60 | }
61 |
62 | /**
63 | * Starts the cooldown of an enchantment
64 | *
65 | * @param enchant enchantment to start for
66 | * @param user player using the enchantment
67 | */
68 | public static void start(final CustomEnchantment enchant, final LivingEntity user) {
69 | final String key = makeKey(enchant, user);
70 | cooldowns.put(key, clock.millis());
71 | }
72 |
73 | /**
74 | * Reduces the remaining time of an enchantment's cooldown.
75 | *
76 | * @param enchant enchantment to reduce the time for
77 | * @param user player using the enchantment
78 | * @param millis time to reduce by in milliseconds
79 | */
80 | public static void reduce(final CustomEnchantment enchant, final LivingEntity user, final long millis) {
81 | final String key = makeKey(enchant, user);
82 | cooldowns.computeIfPresent(key, (k, time) -> time - millis);
83 | }
84 |
85 | private static String makeKey(final CustomEnchantment enchant, final LivingEntity user) {
86 | return enchant.getName() + user.getUniqueId();
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/api/CustomEnchantment.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import com.rit.sucy.config.CommentedConfig;
5 | import com.rit.sucy.config.parse.DataSection;
6 | import com.rit.sucy.text.TextFormatter;
7 | import com.sucy.enchant.EnchantmentAPI;
8 | import com.sucy.enchant.data.Permission;
9 | import com.sucy.enchant.util.LoreReader;
10 | import org.apache.commons.lang.Validate;
11 | import org.bukkit.Material;
12 | import org.bukkit.block.Block;
13 | import org.bukkit.enchantments.Enchantment;
14 | import org.bukkit.entity.LivingEntity;
15 | import org.bukkit.entity.Player;
16 | import org.bukkit.event.block.BlockEvent;
17 | import org.bukkit.event.entity.EntityDamageByEntityEvent;
18 | import org.bukkit.event.entity.EntityDamageEvent;
19 | import org.bukkit.event.entity.ProjectileLaunchEvent;
20 | import org.bukkit.event.player.PlayerInteractEntityEvent;
21 | import org.bukkit.event.player.PlayerInteractEvent;
22 | import org.bukkit.inventory.ItemStack;
23 | import org.bukkit.inventory.meta.ItemMeta;
24 | import org.bukkit.permissions.Permissible;
25 |
26 | import java.util.ArrayList;
27 | import java.util.Arrays;
28 | import java.util.HashMap;
29 | import java.util.HashSet;
30 | import java.util.List;
31 | import java.util.Map;
32 | import java.util.Objects;
33 | import java.util.Set;
34 | import java.util.stream.Collectors;
35 |
36 | import static com.sucy.enchant.util.Utils.isPresent;
37 |
38 | /**
39 | * EnchantmentAPI © 2017
40 | * com.sucy.enchant.api.CustomEnchantment
41 | */
42 | public abstract class CustomEnchantment implements Comparable {
43 |
44 | /**
45 | * Default conflict group for enchantments. Using this group causes the
46 | * enchantment not to conflict with any other enchantments.
47 | */
48 | public static final String DEFAULT_GROUP = "Default";
49 |
50 | private final Map materialWeights = new HashMap<>();
51 |
52 | private final Set naturalItems = new HashSet<>();
53 | private final Set anvilItems = new HashSet<>();
54 |
55 | private String key;
56 | private String name;
57 | private String description;
58 |
59 | private String group;
60 | private boolean enabled;
61 | private boolean tableEnabled;
62 | private boolean stacks;
63 | private double enchantLevelScaleFactor;
64 | private double minEnchantingLevel;
65 | private double enchantLevelBuffer;
66 | private double weight;
67 | private int maxLevel;
68 | private int maxTableLevel;
69 | private int combineCostPerLevel;
70 |
71 | private boolean setFactors = false;
72 |
73 | protected Settings settings = new Settings();
74 |
75 | protected CustomEnchantment(final String name, final String description) {
76 | Validate.notEmpty(name, "The name must be present and not empty");
77 | Validate.notEmpty(description, "The description must be present and not empty");
78 |
79 | this.key = name.trim();
80 | this.name = name.trim();
81 | this.description = description.trim();
82 |
83 | enabled = true;
84 | tableEnabled = true;
85 | group = DEFAULT_GROUP;
86 | maxLevel = 1;
87 | maxTableLevel = 1;
88 | minEnchantingLevel = 1;
89 | enchantLevelScaleFactor = 60;
90 | enchantLevelBuffer = 0;
91 | stacks = false;
92 | combineCostPerLevel = 1;
93 |
94 | weight = 5.0;
95 | }
96 |
97 | // ---- Getters/Setters ---- //
98 |
99 | /**
100 | * @return name of the enchantment that shows up in the lore
101 | */
102 | public String getName() {
103 | return name;
104 | }
105 |
106 | /**
107 | * @return details for the enchantment to show in the details book
108 | */
109 | public String getDescription() {
110 | return description;
111 | }
112 |
113 | /**
114 | * @return whether or not having the enchantment on multiple items stacks their effects
115 | */
116 | public boolean canStack() {
117 | return stacks;
118 | }
119 |
120 | /** @see CustomEnchantment#canStack() */
121 | public void setCanStack(final boolean stacks) {
122 | this.stacks = stacks;
123 | }
124 |
125 | /**
126 | * @return whether or not the enchantment is obtainable without commands
127 | */
128 | public boolean isEnabled() {
129 | return enabled;
130 | }
131 |
132 | /**
133 | * @return whether or not the enchantment is obtainable
134 | */
135 | public boolean isTableEnabled() {
136 | return enabled && tableEnabled;
137 | }
138 |
139 | /** @see CustomEnchantment#isTableEnabled() */
140 | public void setTableEnabled(final boolean tableEnabled) {
141 | this.tableEnabled = tableEnabled;
142 | }
143 |
144 | /**
145 | * @return the max level the enchantment can normally reach via combining enchantments
146 | */
147 | public int getMaxLevel() {
148 | return maxLevel;
149 | }
150 |
151 | /**
152 | * @return the max level the enchantment can be from an enchanting table
153 | */
154 | public int getMaxTableLevel() {
155 | return maxTableLevel;
156 | }
157 |
158 | /**
159 | * Sets both the max table level and combine level to the given amount.
160 | *
161 | * @param maxLevel max normally obtainable level
162 | */
163 | public void setMaxLevel(final int maxLevel) {
164 | setMaxLevel(maxLevel, maxLevel);
165 | if (!setFactors) {
166 | enchantLevelScaleFactor = 60 / maxLevel;
167 | }
168 | }
169 |
170 | /**
171 | * @see CustomEnchantment#getMaxLevel()
172 | * @see CustomEnchantment#getMaxTableLevel()
173 | */
174 | public void setMaxLevel(final int maxLevel, final int maxTableLevel) {
175 | Validate.isTrue(maxTableLevel > 0, "Max table level must be at least 1");
176 | Validate.isTrue(maxLevel >= maxTableLevel, "Max level must be at least 1");
177 | this.maxLevel = maxLevel;
178 | this.maxTableLevel = maxTableLevel;
179 | }
180 |
181 | /**
182 | * @return minimum modified enchantment level needed to receive this enchantment
183 | * @apiNote modified enchantment level is normally between 2 and 48
184 | */
185 | public double getMinEnchantingLevel() {
186 | return minEnchantingLevel;
187 | }
188 |
189 | public void setMinEnchantingLevel(final double minEnchantingLevel) {
190 | setFactors = true;
191 | this.minEnchantingLevel = minEnchantingLevel;
192 | }
193 |
194 | public double getEnchantLevelScaleFactor() {
195 | return enchantLevelScaleFactor;
196 | }
197 |
198 | public void setEnchantLevelScaleFactor(final double enchantLevelScaleFactor) {
199 | Validate.isTrue(enchantLevelScaleFactor >= 0, "Scale factor must be a non-negative number");
200 | setFactors = true;
201 | this.enchantLevelScaleFactor = enchantLevelScaleFactor;
202 | }
203 |
204 | public double getEnchantLevelBuffer() {
205 | return enchantLevelBuffer;
206 | }
207 |
208 | public void setEnchantLevelBuffer(final double enchantLevelBuffer) {
209 | Validate.isTrue(enchantLevelBuffer >= 0, "Buffer cannot be negative");
210 | setFactors = true;
211 | this.enchantLevelBuffer = enchantLevelBuffer;
212 | }
213 |
214 | public int getCombineCostPerLevel() {
215 | return combineCostPerLevel;
216 | }
217 |
218 | public void setCombineCostPerLevel(final int combineCostPerLevel) {
219 | Validate.isTrue(combineCostPerLevel >= 0, "Combine cost cannot be negative");
220 | this.combineCostPerLevel = combineCostPerLevel;
221 | }
222 |
223 | public Set getNaturalItems() {
224 | return naturalItems;
225 | }
226 |
227 | public void addNaturalItems(final Material... materials) {
228 | for (Material material : materials) {
229 | Objects.requireNonNull(material, "Cannot add a null natural material");
230 | naturalItems.add(material);
231 | }
232 | }
233 |
234 | public Set getAnvilItems() {
235 | return anvilItems;
236 | }
237 |
238 | public void addAnvilItems(final Material... materials) {
239 | for (Material material : materials) {
240 | Objects.requireNonNull(material, "Cannot add a null natural material");
241 | anvilItems.add(material);
242 | }
243 | }
244 |
245 | public double getWeight(final Material material) {
246 | return materialWeights.getOrDefault(material, weight);
247 | }
248 |
249 | public void setWeight(final double weight) {
250 | this.weight = weight;
251 | }
252 |
253 | public void setWeight(final Material material, final double weight) {
254 | Validate.isTrue(weight > 0, "Weight must be a positive number");
255 | this.materialWeights.put(material, weight);
256 | }
257 |
258 | public String getGroup() {
259 | return group;
260 | }
261 |
262 | public void setGroup(final String group) {
263 | Validate.notEmpty(group, "Group cannot be empty or missing");
264 | this.group = group;
265 | }
266 |
267 | // --- Functional Methods --- //
268 |
269 | /**
270 | * @param expLevel enchanting level (from enchanting table)
271 | * @return enchantment level
272 | */
273 | public int computeLevel(final int expLevel) {
274 | final int level = Math.min(1 + (int)Math.floor((expLevel - minEnchantingLevel) / enchantLevelScaleFactor), this.maxTableLevel);
275 | final double cap = minEnchantingLevel + level * enchantLevelScaleFactor + enchantLevelBuffer;
276 | return expLevel <= cap ? Math.max(0, level) : 0;
277 | }
278 |
279 | /**
280 | * @param item item to check
281 | * @return true if can go onto the item, not including conflicts with other enchantments
282 | */
283 | public boolean canEnchantOnto(final ItemStack item) {
284 | if (!isPresent(item)) {
285 | return false;
286 | }
287 |
288 | final Material material = item.getType();
289 | return material == Material.BOOK || material == Material.ENCHANTED_BOOK || naturalItems.contains(material);
290 | }
291 |
292 | /**
293 | * @param item item to check
294 | * @return true if can merge onto the item, not including conflicts with other enchantments
295 | */
296 | public boolean canMergeOnto(final ItemStack item) {
297 | if (!isPresent(item)) {
298 | return false;
299 | }
300 |
301 | final Material material = item.getType();
302 | return material == Material.BOOK || material == Material.ENCHANTED_BOOK
303 | || naturalItems.contains(material) || anvilItems.contains(material);
304 | }
305 |
306 | /**
307 | * Checks whether or not this enchantment works with the given enchantment
308 | *
309 | * @param other enchantment to check against
310 | * @param same whether or not to allow the same enchantment (in case of merging)
311 | * @return true if there is a conflict, false otherwise
312 | */
313 | public boolean conflictsWith(final CustomEnchantment other, final boolean same) {
314 | Objects.requireNonNull(other, "Cannot check against a null item");
315 | if (other == this) {
316 | return same;
317 | }
318 | return !group.equals(DEFAULT_GROUP) && group.equals(other.getGroup());
319 | }
320 |
321 | /**
322 | * Checks whether or not this enchantment works with all of the given enchantments
323 | *
324 | * @param enchantments enchantments to check against
325 | * @param same whether or not to allow the same enchantment (in case of merging)
326 | * @return true if there is a conflict, false otherwise
327 | */
328 | public boolean conflictsWith(final List enchantments, final boolean same) {
329 | Objects.requireNonNull(enchantments, "Cannot check a null enchantment list");
330 | return enchantments.stream().anyMatch(enchant -> conflictsWith(this, same));
331 | }
332 |
333 | /**
334 | * Checks whether or not this enchantment works with all of the given enchantments
335 | *
336 | * @param same whether or not to allow the same enchantment (in case of merging)
337 | * @param enchantments enchantments to check against
338 | * @return true if there is a conflict, false otherwise
339 | */
340 | public boolean conflictsWith(final boolean same, final CustomEnchantment... enchantments) {
341 | return Arrays.stream(enchantments).anyMatch(enchant -> conflictsWith(this, same));
342 | }
343 |
344 | /**
345 | * @param item item to add to
346 | * @param level enchantment level
347 | * @return item with the enchantment
348 | */
349 | public ItemStack addToItem(final ItemStack item, final int level) {
350 | Objects.requireNonNull(item, "Item cannot be null");
351 | Validate.isTrue(level > 0, "Level must be at least 1");
352 |
353 | if (item.getType() == Material.BOOK) {
354 | item.setType(Material.ENCHANTED_BOOK);
355 | }
356 |
357 | final ItemMeta meta = item.getItemMeta();
358 | final List lore = meta.hasLore() ? meta.getLore() : new ArrayList<>();
359 |
360 | final int lvl = Enchantments.getCustomEnchantments(item).getOrDefault(this, 0);
361 | if (lvl > 0) {
362 | lore.remove(LoreReader.formatEnchantment(this, lvl));
363 | }
364 |
365 | lore.add(0, LoreReader.formatEnchantment(this, level));
366 | meta.setLore(lore);
367 | item.setItemMeta(meta);
368 | return item;
369 | }
370 |
371 | /**
372 | * @param item item to remove from
373 | * @return item without this enchantment
374 | */
375 | public ItemStack removeFromItem(final ItemStack item) {
376 | Objects.requireNonNull(item, "Item cannot be null");
377 |
378 | final int lvl = Enchantments.getCustomEnchantments(item).getOrDefault(this, 0);
379 | if (lvl > 0) {
380 | final ItemMeta meta = item.getItemMeta();
381 | final List lore = meta.getLore();
382 | lore.remove(LoreReader.formatEnchantment(this, lvl));
383 | meta.setLore(lore);
384 | item.setItemMeta(meta);
385 | }
386 |
387 | return item;
388 | }
389 |
390 | public void addToEnchantment(final Map enchantments, final ItemStack result, final int level) {
391 | addToItem(result, level);
392 | }
393 |
394 | /**
395 | * @param permissible person to receive the enchantment
396 | * @return true if they have permission, false otherwise
397 | */
398 | public boolean hasPermission(final Permissible permissible) {
399 | return permissible == null
400 | || permissible.hasPermission(Permission.ENCHANT)
401 | || permissible.hasPermission(getPermission());
402 | }
403 |
404 | /**
405 | * @return permission used by the enchantment
406 | */
407 | public String getPermission() {
408 | return Permission.ENCHANT + "." + getName().replace(" ", "");
409 | }
410 |
411 | // ---- API for effects ---- //
412 |
413 | /**
414 | * Applies the enchantment affect when attacking someone
415 | *
416 | * @param user the entity that has the enchantment
417 | * @param target the entity that was struck by the enchantment
418 | * @param level the level of the used enchantment
419 | * @param event the event details
420 | */
421 | public void applyOnHit(final LivingEntity user, final LivingEntity target, final int level, final EntityDamageByEntityEvent event) { }
422 |
423 | /**
424 | * Applies the enchantment defensively (when taking damage)
425 | *
426 | * @param user the entity hat has the enchantment
427 | * @param target the entity that attacked the enchantment, can be null
428 | * @param level the level of the used enchantment
429 | * @param event the event details (EntityDamageByEntityEvent, EntityDamageByBlockEvent, or just EntityDamageEvent)
430 | */
431 | public void applyDefense(final LivingEntity user, final LivingEntity target, final int level, final EntityDamageEvent event) { }
432 |
433 | /**
434 | * Applies effects while breaking blocks (for tool effects)
435 | *
436 | * @param user the player with the enchantment
437 | * @param block the block being broken
438 | * @param event the event details (either BlockBreakEvent or BlockDamageEvent)
439 | */
440 | public void applyBreak(final LivingEntity user, final Block block, final int level, final BlockEvent event) { }
441 |
442 | /**
443 | * Applies effects when the item is equipped
444 | *
445 | * @param user the player that equipped it
446 | * @param level the level of enchantment
447 | */
448 | public void applyEquip(final LivingEntity user, final int level) { }
449 |
450 | /**
451 | * Applies effects when the item is unequipped
452 | *
453 | * @param user the player that unequipped it
454 | * @param level the level of enchantment
455 | */
456 | public void applyUnequip(final LivingEntity user, final int level) { }
457 |
458 | /**
459 | * Applies effects when the player left or right clicks (For other kinds of enchantments like spells)
460 | *
461 | * @param user the player with the enchantment
462 | * @param event the event details
463 | */
464 | public void applyInteractBlock(final Player user, final int level, final PlayerInteractEvent event) { }
465 |
466 | /**
467 | * Applies effects when the player interacts with an entity
468 | *
469 | * @param user player with the enchantment
470 | * @param level enchantment level
471 | * @param event the event details
472 | */
473 | public void applyInteractEntity(final Player user, final int level, final PlayerInteractEntityEvent event) { }
474 |
475 | /**
476 | * Applies effects when firing a projectile
477 | *
478 | * @param user entity firing the projectile
479 | * @param level enchantment level
480 | * @param event the event details
481 | */
482 | public void applyProjectile(final LivingEntity user, final int level, final ProjectileLaunchEvent event) { }
483 |
484 | // ---- Object operations ---- //
485 |
486 | @Override
487 | public String toString() {
488 | return name;
489 | }
490 |
491 | @Override
492 | public boolean equals(final Object other) {
493 | return other instanceof CustomEnchantment && ((CustomEnchantment) other).name.equals(name);
494 | }
495 |
496 | @Override
497 | public int hashCode() {
498 | return name.hashCode();
499 | }
500 |
501 | @Override
502 | public int compareTo(final CustomEnchantment other) {
503 | return name.compareTo(other.name);
504 | }
505 |
506 | // ---- IO ---- /
507 |
508 | private static final String SAVE_FOLDER = "enchant/custom/";
509 |
510 | private static final String
511 | COMBINE_COST_PER_LEVEL = "combine-cost-per-level",
512 | DESCRIPTION = "description",
513 | ENABLED = "enabled",
514 | ENCHANT_LEVEL_SCALE_FACTOR = "enchant-level-scale-factor",
515 | ENCHANT_LEVEL_BUFFER = "enchant-level-buffer",
516 | GROUP = "group",
517 | MAX_LEVEL = "max-level",
518 | MAX_TABLE_LEVEL = "max-table-level",
519 | MIN_ENCHANTING_LEVEL = "min-enchanting-level",
520 | NAME = "name",
521 | NATURAL_ITEMS = "natural-items",
522 | ANVIL_ITEMS = "anvil-items",
523 | STACKS = "stacks",
524 | TABLE_ENABLED = "table-enabled",
525 | WEIGHT = "weight",
526 | MATERIAL = "material-weights",
527 | EFFECT = "effect";
528 |
529 | private static final List
530 | BUFFER_COMMENT = ImmutableList.of("", " How many enchantment levels beyond the max can still yield the enchantment"),
531 | COMBINE_COMMENT = ImmutableList.of("", " Level cost per level of the enchantment"),
532 | LEVEL_SCALE_COMMENT = ImmutableList.of("", " Higher numbers result in requiring higher enchanting levels", " to get higher ranks"),
533 | GROUP_COMMENT = ImmutableList.of("", " When something other than default, prevents multiple enchantments", " in the same group being on the same item"),
534 | MAX_COMMENT = ImmutableList.of("", " Max attainable level from anvils"),
535 | MAX_TABLE_COMMENT = ImmutableList.of("", " Max attainable level from enchanting"),
536 | MIN_COMMENT = ImmutableList.of("", " Minimum enchanting level to receive the enchantment.", " Negatives make it easier to get higher ranks"),
537 | ITEM_COMMENT = ImmutableList.of("", " Items that can receive the enchantment from enchanting or anvils"),
538 | ANVIL_COMMENT = ImmutableList.of("", " Additional items that can only receive the enchantment through anvils"),
539 | STACK_COMMENT = ImmutableList.of("", " Whether or not the same enchantment stacks if on multiple items.", " When false, the highest level is applied"),
540 | TABLE_COMMENT = ImmutableList.of("", " Whether or not this enchantment can be achieved from enchanting"),
541 | WEIGHT_COMMENT = ImmutableList.of("", " How common the enchantment is. Higher numbers are more common."),
542 | MATERIAL_COMMENT = ImmutableList.of("", " Weights for specific materials"),
543 | EFFECT_COMMENT = ImmutableList.of("", " Extra settings specific to the enchantment");
544 |
545 | protected String getSaveFolder() {
546 | return SAVE_FOLDER;
547 | }
548 |
549 | public void save(final EnchantmentAPI plugin) {
550 | final CommentedConfig config = new CommentedConfig(plugin, getSaveFolder() + key);
551 | final DataSection data = config.getConfig();
552 | data.clear();
553 |
554 | data.set(NAME, name);
555 | data.set(DESCRIPTION, description);
556 | data.set(ENABLED, enabled);
557 |
558 | data.set(MAX_LEVEL, maxLevel);
559 | data.setComments(MAX_LEVEL, MAX_COMMENT);
560 |
561 | data.set(MAX_TABLE_LEVEL, maxTableLevel);
562 | data.setComments(MAX_TABLE_LEVEL, MAX_TABLE_COMMENT);
563 |
564 | data.set(GROUP, group);
565 | data.setComments(GROUP, GROUP_COMMENT);
566 |
567 | data.set(NATURAL_ITEMS, naturalItems.stream().map(Material::name).collect(Collectors.toList()));
568 | data.setComments(NATURAL_ITEMS, ITEM_COMMENT);
569 |
570 | data.set(ANVIL_ITEMS, anvilItems.stream().map(Material::name).collect(Collectors.toList()));
571 | data.setComments(ANVIL_ITEMS, ANVIL_COMMENT);
572 |
573 | data.set(WEIGHT, weight);
574 | data.setComments(WEIGHT, WEIGHT_COMMENT);
575 |
576 | final DataSection weightData = data.createSection(MATERIAL);
577 | materialWeights.forEach((material, weight) -> weightData.set(material.name(), weight));
578 | data.setComments(MATERIAL, MATERIAL_COMMENT);
579 |
580 | data.set(MIN_ENCHANTING_LEVEL, minEnchantingLevel);
581 | data.setComments(MIN_ENCHANTING_LEVEL, MIN_COMMENT);
582 |
583 | data.set(ENCHANT_LEVEL_SCALE_FACTOR, enchantLevelScaleFactor);
584 | data.setComments(ENCHANT_LEVEL_SCALE_FACTOR, LEVEL_SCALE_COMMENT);
585 |
586 | data.set(ENCHANT_LEVEL_BUFFER, enchantLevelBuffer);
587 | data.setComments(ENCHANT_LEVEL_BUFFER, BUFFER_COMMENT);
588 |
589 | data.set(COMBINE_COST_PER_LEVEL, combineCostPerLevel);
590 | data.setComments(COMBINE_COST_PER_LEVEL, COMBINE_COMMENT);
591 |
592 | data.set(STACKS, stacks);
593 | data.setComments(STACKS, STACK_COMMENT);
594 |
595 | data.set(TABLE_ENABLED, tableEnabled);
596 | data.setComments(TABLE_ENABLED, TABLE_COMMENT);
597 |
598 | data.set(EFFECT, settings);
599 | data.setComments(EFFECT, EFFECT_COMMENT);
600 |
601 | config.save();
602 | }
603 |
604 | public void load(final EnchantmentAPI plugin) {
605 | final CommentedConfig config = new CommentedConfig(plugin, getSaveFolder() + key);
606 | if (!config.getConfigFile().exists()) {
607 | return;
608 | }
609 |
610 | final DataSection data = config.getConfig();
611 | name = TextFormatter.colorString(data.getString(NAME, name));
612 | description = TextFormatter.colorString(data.getString(DESCRIPTION, description));
613 | enabled = data.getBoolean(ENABLED, enabled);
614 | setMaxLevel(data.getInt(MAX_LEVEL, maxLevel), data.getInt(MAX_TABLE_LEVEL, maxTableLevel));
615 | setGroup(data.getString(GROUP, group));
616 | setMinEnchantingLevel(data.getDouble(MIN_ENCHANTING_LEVEL, minEnchantingLevel));
617 | setEnchantLevelScaleFactor(data.getDouble(ENCHANT_LEVEL_SCALE_FACTOR, enchantLevelScaleFactor));
618 | setEnchantLevelBuffer(data.getDouble(ENCHANT_LEVEL_BUFFER, enchantLevelBuffer));
619 | setCombineCostPerLevel(data.getInt(COMBINE_COST_PER_LEVEL, combineCostPerLevel));
620 | setCanStack(data.getBoolean(STACKS, stacks));
621 | setTableEnabled(data.getBoolean(TABLE_ENABLED, tableEnabled));
622 | setWeight(data.getDouble(WEIGHT, weight));
623 |
624 | loadItems(naturalItems, data, NATURAL_ITEMS);
625 | loadItems(anvilItems, data, ANVIL_ITEMS);
626 |
627 | if (data.isSection(MATERIAL)) {
628 | materialWeights.clear();
629 | final DataSection weightData = data.getSection(MATERIAL);
630 | weightData.keys().forEach(key -> {
631 | final Material material = Material.matchMaterial(key);
632 | if (material == null) {
633 | plugin.getLogger().warning(key + " is not a valid material (in material weights for " + name);
634 | } else {
635 | setWeight(material, weightData.getDouble(key));
636 | }
637 | });
638 | }
639 |
640 | if (data.isSection(EFFECT)) {
641 | final Settings settings = new Settings();
642 | final DataSection file = data.getSection(EFFECT);
643 | file.keys().forEach(key -> settings.set(key, file.get(key)));
644 | this.settings.keys().forEach(key -> settings.checkDefault(key, this.settings.get(key)));
645 | this.settings = settings;
646 | }
647 | }
648 |
649 | private void loadItems(final Set destination, final DataSection data, final String key) {
650 | if (!data.has(key)) return;
651 |
652 | destination.clear();
653 | destination.addAll(data.getList(key).stream()
654 | .map(line -> line.toUpperCase().replace(' ', '_'))
655 | .map(Material::matchMaterial)
656 | .filter(Objects::nonNull)
657 | .collect(Collectors.toList()));
658 | }
659 | }
660 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/api/EnchantPlugin.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | /**
4 | * EnchantmentAPI © 2017
5 | * com.sucy.enchant.api.EnchantPlugin
6 | *
7 | * Interface to be implemented by plugins adding new enchantments to the server.
8 | * EnchantmentAPI will find any plugins implementing this class and call the
9 | * registration method on enable.
10 | */
11 | public interface EnchantPlugin {
12 |
13 | /**
14 | * This is where you should register your enchantments.
15 | *
16 | * @param registry registry to register enchantments with
17 | */
18 | void registerEnchantments(EnchantmentRegistry registry);
19 | }
20 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/api/EnchantmentRegistry.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | /**
4 | * EnchantmentAPI © 2017
5 | * com.sucy.enchant.api.EnchantmentRegistry
6 | */
7 | public interface EnchantmentRegistry {
8 |
9 | /**
10 | * Registers enchantments with the API, allowing them to be available
11 | * in the enchanting tables, commands, and anvils.
12 | *
13 | * @param enchantments enchantments to register
14 | */
15 | void register(CustomEnchantment... enchantments);
16 | }
17 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/api/Enchantments.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | import com.sucy.enchant.EnchantmentAPI;
4 | import com.sucy.enchant.data.PlayerEquips;
5 | import com.sucy.enchant.util.LoreReader;
6 | import org.bukkit.Bukkit;
7 | import org.bukkit.Material;
8 | import org.bukkit.enchantments.Enchantment;
9 | import org.bukkit.entity.Player;
10 | import org.bukkit.inventory.ItemFlag;
11 | import org.bukkit.inventory.ItemStack;
12 | import org.bukkit.inventory.meta.EnchantmentStorageMeta;
13 | import org.bukkit.inventory.meta.ItemMeta;
14 |
15 | import java.util.HashMap;
16 | import java.util.List;
17 | import java.util.Map;
18 | import java.util.UUID;
19 | import java.util.stream.Collectors;
20 |
21 | import static com.sucy.enchant.util.Utils.isPresent;
22 |
23 | /**
24 | * EnchantmentAPI © 2017
25 | * com.sucy.enchant.api.Enchantments
26 | */
27 | public class Enchantments {
28 |
29 | private static final Map EQUIPMENT = new HashMap<>();
30 |
31 | /**
32 | * @param player player to get enchantments for
33 | * @return combined enchantments on all active equipment the player has
34 | */
35 | public static Map getEnchantments(final Player player) {
36 | return getEquipmentData(player).getEnchantments();
37 | }
38 |
39 | /**
40 | * @param player player to get equipment data for
41 | * @return the equipment data tracking the player's enchantments
42 | */
43 | public static PlayerEquips getEquipmentData(final Player player) {
44 | return EQUIPMENT.computeIfAbsent(player.getUniqueId(), uuid -> new PlayerEquips(player));
45 | }
46 |
47 | /**
48 | * @param player player to clear equipment data for (will refresh next access)
49 | */
50 | public static void clearEquipmentData(final Player player) {
51 | EQUIPMENT.remove(player.getUniqueId()).clear(player);
52 | }
53 |
54 | /**
55 | * Clears equipment data for all players, forcing all equipment to refresh
56 | */
57 | public static void clearAllEquipmentData() {
58 | EQUIPMENT.forEach((id, data) -> data.clear(Bukkit.getPlayer(id)));
59 | EQUIPMENT.clear();
60 | }
61 |
62 | /**
63 | * @param item item to grab the enchantments from
64 | * @return list of custom enchantments (does not include vanilla enchantments)
65 | */
66 | public static Map getCustomEnchantments(final ItemStack item) {
67 |
68 | final HashMap list = new HashMap();
69 | if (!isPresent(item)) return list;
70 |
71 | final ItemMeta meta = item.getItemMeta();
72 | if (meta == null || !meta.hasLore()) return list;
73 |
74 | final List lore = meta.getLore();
75 | for (final String line : lore) {
76 | final String name = LoreReader.parseEnchantmentName(line);
77 | if (EnchantmentAPI.isRegistered(name)) {
78 | final CustomEnchantment enchant = EnchantmentAPI.getEnchantment(name);
79 | final int level = LoreReader.parseEnchantmentLevel(line);
80 | if (level > 0) {
81 | list.put(enchant, level);
82 | }
83 | }
84 |
85 | // Short-circuit if we aren't finding valid formatted enchantments
86 | // since all enchantments should be added at the top.
87 | else if (name.isEmpty()) {
88 | return list;
89 | }
90 | }
91 | return list;
92 | }
93 |
94 | /**
95 | * Gets all enchantments on an item, including vanilla enchantments. This wraps
96 | * vanilla enchantments in the CustomEnchantment class for more visibility on
97 | * their settings.
98 | *
99 | * @param item item to get enchantments for.
100 | * @return all enchantments on the item and their levels
101 | */
102 | public static Map getAllEnchantments(final ItemStack item) {
103 | final Map result = getCustomEnchantments(item);
104 | if (item.hasItemMeta()) {
105 | final ItemMeta meta = item.getItemMeta();
106 | if (!meta.hasItemFlag(ItemFlag.HIDE_ENCHANTS)) {
107 | merge(meta.getEnchants(), result);
108 | if (item.getType() == Material.ENCHANTED_BOOK) {
109 | merge(((EnchantmentStorageMeta) meta).getStoredEnchants(), result);
110 | }
111 | }
112 | }
113 | return result;
114 | }
115 |
116 | /**
117 | * Checks whether or not the item has the enchantment on it
118 | *
119 | * @param item item to check
120 | * @param enchantmentName name of the enchantment to check for
121 | * @return true if it has the enchantment, false otherwise
122 | */
123 | public static boolean hasCustomEnchantment(final ItemStack item, final String enchantmentName) {
124 | if (!item.hasItemMeta()) return false;
125 | final ItemMeta meta = item.getItemMeta();
126 | return meta.hasLore() && meta.getLore().stream().anyMatch(LoreReader::isEnchantment);
127 | }
128 |
129 | /**
130 | * Removes all enchantments from the item, including vanilla enchantments
131 | *
132 | * @param item item to remove all enchantments from
133 | * @return item with all enchantments removed
134 | */
135 | public static ItemStack removeAllEnchantments(final ItemStack item) {
136 | item.getEnchantments().forEach((enchant, level) -> item.removeEnchantment(enchant));
137 | return removeCustomEnchantments(item);
138 | }
139 |
140 | /**
141 | * Removes all custom enchantments from an item
142 | *
143 | * @param item item to remove enchantments from
144 | * @return item without custom enchantments
145 | */
146 | public static ItemStack removeCustomEnchantments(final ItemStack item) {
147 | final ItemMeta meta = item.getItemMeta();
148 | if (meta.hasLore()) {
149 | meta.setLore(meta.getLore().stream()
150 | .filter(line -> !LoreReader.isEnchantment(line))
151 | .collect(Collectors.toList()));
152 | }
153 | return item;
154 | }
155 |
156 | private static void merge(final Map source, final Map result) {
157 | source.forEach((enchant, level) -> result.put(EnchantmentAPI.getEnchantment(enchant.getName()), level));
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/api/GlowEffects.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | import org.bukkit.Material;
4 | import org.bukkit.enchantments.Enchantment;
5 | import org.bukkit.inventory.ItemFlag;
6 | import org.bukkit.inventory.ItemStack;
7 | import org.bukkit.inventory.meta.ItemMeta;
8 |
9 | /**
10 | * EnchantmentAPI © 2017
11 | * com.sucy.enchant.api.GlowEffects
12 | */
13 | public class GlowEffects {
14 |
15 | /**
16 | * Ensures an item has a glowing effect if it has any enchantments, custom or not.
17 | *
18 | * @param item item to give glowing effects to
19 | * @return item with the glowing effect
20 | */
21 | public static ItemStack finalize(final ItemStack item) {
22 | // Enchanted books always glow
23 | if (item.getType() == Material.ENCHANTED_BOOK) return item;
24 |
25 | final ItemMeta meta = item.getItemMeta();
26 |
27 | // Hide enchantments and add a non-impactful enchantment to get the glowing effect
28 | if (item.getEnchantments().isEmpty() && !Enchantments.getCustomEnchantments(item).isEmpty()) {
29 | meta.addEnchant(chooseHidden(item), 1, true);
30 | meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
31 | }
32 |
33 | // Stop hiding enchantments if a normal vanilla enchantment was added
34 | else if (meta.hasItemFlag(ItemFlag.HIDE_ENCHANTS)) {
35 | final Enchantment hidden = chooseHidden(item);
36 | if (meta.getEnchants().getOrDefault(hidden, 0) == 1 && meta.getEnchants().size() > 1) {
37 | meta.removeEnchant(hidden);
38 | }
39 | if (!meta.getEnchants().containsKey(hidden)) {
40 | meta.removeItemFlags(ItemFlag.HIDE_ENCHANTS);
41 | }
42 | }
43 |
44 | item.setItemMeta(meta);
45 | return item;
46 | }
47 |
48 | private static Enchantment chooseHidden(final ItemStack item) {
49 | if (item.getType() == Material.ENCHANTED_BOOK) {
50 | return Enchantment.BINDING_CURSE;
51 | } else if (item.getType() == Material.BOW) {
52 | return Enchantment.LUCK;
53 | } else {
54 | return Enchantment.ARROW_INFINITE;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/api/ItemSet.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | import org.bukkit.Material;
4 |
5 | import java.util.Arrays;
6 |
7 | /**
8 | * EnchantmentAPI © 2017
9 | * com.sucy.enchant.api.ItemSet
10 | */
11 | public enum ItemSet {
12 |
13 | BOOK_AND_QUILL("BOOK_AND_QUILL", "WRITTEN_BOOK"),
14 | INK_SACK("INK_SAC", "INK_SACK"),
15 |
16 | AXES("_AXE"),
17 | BOOTS("BOOTS"),
18 | BOWS(Material.BOW),
19 | CHESTPLATES("CHESTPLATE"),
20 | FISHING(Material.FISHING_ROD),
21 | GLIDERS(Material.ELYTRA),
22 | HELMETS("HELMET"),
23 | HOES("_HOE"),
24 | LEGGINGS("LEGGINGS"),
25 | MISCELLANEOUS("SKELETON_SKULL", "SKULL_ITEM", "PUMPKIN"),
26 | PICKAXES("PICKAXE"),
27 | SHEARS(Material.SHEARS),
28 | SHIELDS(Material.SHIELD),
29 | SHOVELS("SHOVEL", "SPADE"),
30 | SWORDS("SWORD"),
31 | TRIDENT("TRIDENT"),
32 | UTILITY("SHEARS", "FLINT_AND_STEEL", "CARROT_STICK", "CARROT_ON_A_STICK"),
33 |
34 | ARMOR(CHESTPLATES, HELMETS, BOOTS, LEGGINGS),
35 | WEAPONS(SWORDS, AXES),
36 | TOOLS(AXES, SHOVELS, PICKAXES),
37 | DURABILITY(SWORDS, TOOLS, BOWS, FISHING, ARMOR),
38 | DURABILITY_SECONDARY(UTILITY, HOES, GLIDERS, SHIELDS),
39 | DURABILITY_ALL(DURABILITY, DURABILITY_SECONDARY),
40 |
41 | VANILLA_ENCHANTABLES(SWORDS, TRIDENT, TOOLS, BOWS, FISHING, ARMOR, UTILITY, GLIDERS, MISCELLANEOUS),
42 |
43 | NONE,
44 | ALL(DURABILITY, DURABILITY_SECONDARY, MISCELLANEOUS);
45 |
46 | private final Material[] items;
47 |
48 | ItemSet() {
49 | items = new Material[0];
50 | }
51 |
52 | ItemSet(final String... suffixes) {
53 | items = Arrays.stream(Material.values())
54 | .filter(material -> !material.name().startsWith("LEGACY")
55 | && Arrays.stream(suffixes).anyMatch(material.name()::endsWith))
56 | .toArray(Material[]::new);
57 | }
58 |
59 | ItemSet(final ItemSet... sets) {
60 | items = Arrays.stream(sets)
61 | .map(ItemSet::getItems)
62 | .flatMap(Arrays::stream)
63 | .toArray(Material[]::new);
64 | }
65 |
66 | ItemSet(final Material... items) {
67 | this.items = items;
68 | }
69 |
70 | public Material[] getItems() {
71 | return items;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/api/Settings.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | import com.rit.sucy.config.parse.DataSection;
4 |
5 | /**
6 | * EnchantmentAPI © 2017
7 | * com.sucy.enchant.api.Settings
8 | */
9 | public class Settings extends DataSection {
10 |
11 | private static final String BASE = "-base";
12 | private static final String SCALE = "-scale";
13 |
14 | /**
15 | * Sets a scaling configuration setting
16 | *
17 | * @param key setting key
18 | * @param base base value (at enchantment level 1)
19 | * @param scale value scale (extra per enchantment level)
20 | */
21 | public void set(final String key, final double base, final double scale) {
22 | set(key + BASE, base);
23 | set(key + SCALE, scale);
24 | }
25 |
26 | /**
27 | * Gets a scaling setting based on the provided level
28 | *
29 | * @param key setting key
30 | * @param level enchantment level
31 | * @return scaled setting value
32 | */
33 | public double get(final String key, final int level) {
34 | return getDouble(key + BASE, 0) + getDouble(key + SCALE, 0) * (level - 1);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/api/Tasks.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | import com.sucy.enchant.EnchantmentAPI;
4 | import org.bukkit.scheduler.BukkitRunnable;
5 | import org.bukkit.scheduler.BukkitTask;
6 |
7 | import java.util.Objects;
8 |
9 | /**
10 | * EnchantmentAPI © 2017
11 | * com.sucy.enchant.api.Tasks
12 | *
13 | * Utility class providing ways to register tasks through EnchantmentAPI
14 | */
15 | public class Tasks {
16 |
17 | private static EnchantmentAPI plugin = EnchantmentAPI.getPlugin(EnchantmentAPI.class);
18 |
19 | /**
20 | * Schedules a task to run next tick
21 | *
22 | * @param runnable runnable to execute
23 | * @return task handling the runnable
24 | */
25 | public static BukkitTask schedule(final Runnable runnable) {
26 | return schedule(runnable, 1);
27 | }
28 |
29 | /**
30 | * Schedules a task to run after the specified number of ticks
31 | *
32 | * @param runnable runnable to execute
33 | * @param delay number of ticks to wait
34 | * @return task handling the runnable
35 | */
36 | public static BukkitTask schedule(final Runnable runnable, final int delay) {
37 | Objects.requireNonNull(runnable, "Runnable cannot be null");
38 |
39 | return plugin.getServer().getScheduler().runTaskLater(plugin, runnable, delay);
40 | }
41 |
42 | /**
43 | * Schedules a task to run continuously
44 | *
45 | * @param runnable runnable to execute
46 | * @param delay delay in ticks before running the task the first time
47 | * @param interval time in ticks between each subsequent execution
48 | * @return task handling the runnable
49 | */
50 | public static BukkitTask schedule(final Runnable runnable, final int delay, final int interval) {
51 | Objects.requireNonNull(runnable, "Runnable cannot be null");
52 |
53 | return plugin.getServer().getScheduler().runTaskTimer(plugin, runnable, delay, interval);
54 | }
55 |
56 | /**
57 | * Schedules a task to run a given number of times
58 | *
59 | * @param runnable runnable to execute
60 | * @param delay delay in ticks before running the task the first time
61 | * @param interval time in ticks between each subsequent execution
62 | * @param repetitions number of times the task should run
63 | * @return task handling the runnable
64 | */
65 | public static BukkitTask schedule(final Runnable runnable, final int delay, final int interval, int repetitions) {
66 | Objects.requireNonNull(runnable, "Runnable cannot be null");
67 |
68 | return new RepeatTask(runnable, repetitions).runTaskTimer(plugin, delay, interval);
69 | }
70 |
71 | private static class RepeatTask extends BukkitRunnable {
72 | private final Runnable runnable;
73 | private int repetitions;
74 | public RepeatTask(final Runnable runnable, final int repetitions) {
75 | this.runnable = runnable;
76 | this.repetitions = repetitions;
77 | }
78 | @Override
79 | public void run() {
80 | runnable.run();
81 | repetitions--;
82 | if (repetitions <= 0) {
83 | cancel();
84 | }
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/cmd/CmdAdd.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.cmd;
2 |
3 | import com.rit.sucy.commands.CommandManager;
4 | import com.rit.sucy.commands.ConfigurableCommand;
5 | import com.rit.sucy.commands.IFunction;
6 | import com.sucy.enchant.EnchantmentAPI;
7 | import com.sucy.enchant.api.CustomEnchantment;
8 | import com.sucy.enchant.api.Enchantments;
9 | import com.sucy.enchant.api.GlowEffects;
10 | import com.sucy.enchant.api.Tasks;
11 | import com.sucy.enchant.data.PlayerEquips;
12 | import org.bukkit.ChatColor;
13 | import org.bukkit.command.CommandSender;
14 | import org.bukkit.entity.Player;
15 | import org.bukkit.plugin.Plugin;
16 |
17 | import static com.sucy.enchant.util.Utils.isPresent;
18 |
19 | /**
20 | * EnchantmentAPI © 2017
21 | * com.sucy.enchant.cmd.CmdAdd
22 | */
23 | public class CmdAdd implements IFunction {
24 |
25 | private static final String PLAYER_ONLY = "player-only";
26 | private static final String NOT_ENCHANTMENT = "not-enchantment";
27 | private static final String NOT_LEVEL = "not-level";
28 | private static final String NO_ITEM = "no-item";
29 | private static final String SUCCESS = "success";
30 |
31 | @Override
32 | public void execute(
33 | final ConfigurableCommand command,
34 | final Plugin plugin,
35 | final CommandSender sender,
36 | final String[] args) {
37 |
38 | if (!(sender instanceof Player)) {
39 | command.sendMessage(sender, PLAYER_ONLY, ChatColor.DARK_RED + "Only players can use this command");
40 | return;
41 | }
42 |
43 | if (args.length < 2) {
44 | CommandManager.displayUsage(command, sender);
45 | return;
46 | }
47 |
48 | final Player player = (Player)sender;
49 | if (!isPresent(player.getEquipment().getItemInMainHand())) {
50 | command.sendMessage(sender, NO_ITEM, ChatColor.DARK_RED + "You are not holding an item to enchant");
51 | }
52 |
53 | final StringBuilder builder = new StringBuilder(args[0]);
54 | for (int i = 1; i < args.length - 1; i++) {
55 | builder.append(' ');
56 | builder.append(args[i]);
57 | }
58 |
59 | final CustomEnchantment enchantment = EnchantmentAPI.getEnchantment(builder.toString());
60 | if (enchantment == null) {
61 | command.sendMessage(sender, NOT_ENCHANTMENT, ChatColor.GOLD + builder.toString() + ChatColor.DARK_RED + " is not an enchantment");
62 | return;
63 | }
64 |
65 | final int level;
66 | try {
67 | level = Integer.parseInt(args[args.length - 1]);
68 | } catch (final Exception ex) {
69 | command.sendMessage(sender, NOT_LEVEL, ChatColor.GOLD + args[args.length - 1] + ChatColor.DARK_RED + " is not a number");
70 | return;
71 | }
72 |
73 | enchantment.addToItem(player.getInventory().getItemInMainHand(), level);
74 | GlowEffects.finalize(player.getInventory().getItemInMainHand());
75 | final PlayerEquips equips = Enchantments.getEquipmentData(player);
76 | Tasks.schedule(() -> equips.updateWeapon((player).getInventory()));
77 | command.sendMessage(sender, SUCCESS, ChatColor.DARK_GREEN + "Added the enchantment successfully");
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/cmd/CmdBook.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.cmd;
2 |
3 | import com.rit.sucy.commands.ConfigurableCommand;
4 | import com.rit.sucy.commands.IFunction;
5 | import com.rit.sucy.items.ItemManager;
6 | import com.sucy.enchant.EnchantmentAPI;
7 | import com.sucy.enchant.api.CustomEnchantment;
8 | import com.sucy.enchant.api.ItemSet;
9 | import com.sucy.enchant.vanilla.VanillaEnchantment;
10 | import org.bukkit.Material;
11 | import org.bukkit.command.CommandSender;
12 | import org.bukkit.entity.Player;
13 | import org.bukkit.inventory.ItemStack;
14 | import org.bukkit.inventory.meta.BookMeta;
15 | import org.bukkit.plugin.Plugin;
16 |
17 | import java.util.ArrayList;
18 | import java.util.Collections;
19 |
20 | /**
21 | * EnchantmentAPI © 2017
22 | * com.sucy.enchant.cmd.CmdBook
23 | */
24 | public class CmdBook implements IFunction {
25 |
26 | private static final String NOT_PLAYER = "not-player";
27 | private static final String SUCCESS = "success";
28 |
29 | @Override
30 | public void execute(
31 | final ConfigurableCommand command,
32 | final Plugin plugin,
33 | final CommandSender sender,
34 | final String[] args) {
35 |
36 | if (!(sender instanceof Player)) {
37 | command.sendMessage(sender, NOT_PLAYER, "&4You must be a player to use this command");
38 | return;
39 | }
40 |
41 | final ItemStack book = new ItemStack(ItemSet.BOOK_AND_QUILL.getItems()[0]);
42 | final BookMeta meta = (BookMeta)book.getItemMeta();
43 | meta.addPage("EnchantmentAPI\nBy Eniripsa96\n\n Enchantment details");
44 | meta.setAuthor("Eniripsa96");
45 | meta.setTitle("EnchantmentAPI");
46 |
47 | final ArrayList enchants = new ArrayList<>(EnchantmentAPI.getRegisteredEnchantments());
48 | Collections.sort(enchants);
49 |
50 | for (final CustomEnchantment enchantment : enchants) {
51 | if (enchantment instanceof VanillaEnchantment) continue;
52 | if (enchantment.getDescription() == null) continue;
53 | StringBuilder page = new StringBuilder();
54 | page.append(enchantment.getName()).append(" - ").append(enchantment.getDescription()).append("\n\nItems: ");
55 |
56 | boolean first = true;
57 | for (Material item : enchantment.getNaturalItems()) {
58 | if (first) first = false;
59 | else page.append(", ");
60 | page.append(ItemManager.getVanillaName(item));
61 | }
62 | if (first) page.append("None");
63 | meta.addPage(page.toString());
64 | }
65 | book.setItemMeta(meta);
66 | ((Player)sender).getInventory().addItem(book);
67 | command.sendMessage(sender, SUCCESS, "&2You have received a book with all enchantment details");
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/cmd/CmdGraph.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.cmd;
2 |
3 | import com.rit.sucy.commands.ConfigurableCommand;
4 | import com.rit.sucy.commands.IFunction;
5 | import com.sucy.enchant.EnchantmentAPI;
6 | import com.sucy.enchant.api.CustomEnchantment;
7 | import com.sucy.enchant.data.ConfigKey;
8 | import com.sucy.enchant.data.Configuration;
9 | import com.sucy.enchant.data.Enchantability;
10 | import com.sucy.enchant.mechanics.EnchantingMechanics;
11 | import com.sucy.enchant.vanilla.Vanilla;
12 | import org.bukkit.ChatColor;
13 | import org.bukkit.Material;
14 | import org.bukkit.command.CommandSender;
15 | import org.bukkit.entity.Player;
16 | import org.bukkit.inventory.ItemStack;
17 | import org.bukkit.plugin.Plugin;
18 |
19 | import java.text.DecimalFormat;
20 | import java.util.ArrayList;
21 | import java.util.Comparator;
22 | import java.util.HashSet;
23 | import java.util.List;
24 | import java.util.Map;
25 | import java.util.Set;
26 | import java.util.stream.Collectors;
27 |
28 | /**
29 | * EnchantmentAPI © 2017
30 | * com.sucy.enchant.cmd.CmdGraph
31 | */
32 | public class CmdGraph implements IFunction {
33 |
34 | private static final String INVALID_MATERIAL = "invalid-material";
35 | private static final String INVALID_ENCHANTMENT = "invalid-enchantment";
36 | private static final String INCOMPATIBLE_ENCHANTMENT = "incompatible-enchantment";
37 |
38 | private static final DecimalFormat FORMAT = new DecimalFormat("##0.0");
39 |
40 | private final EnchantingMechanics MECHANICS = new EnchantingMechanics();
41 |
42 | private final int maxEnchantsSetting = Configuration.amount(ConfigKey.MAX_ENCHANTMENTS);
43 |
44 | @Override
45 | public void execute(
46 | final ConfigurableCommand command,
47 | final Plugin plugin,
48 | final CommandSender sender,
49 | final String[] args) {
50 |
51 | if (args.length >= 2) {
52 | try {
53 |
54 | // Parse the item
55 | final Material mat = Material.getMaterial(args[0].toUpperCase());
56 | if (mat == null) {
57 | command.sendMessage(sender, INVALID_MATERIAL, "That is not a valid material");
58 | return;
59 | }
60 |
61 | final ItemStack item = new ItemStack(mat);
62 |
63 | // Make sure the enchantment can work on the item
64 | String name = args[1];
65 | for (int i = 2; i < args.length; i++) name += " " + args[i];
66 | final CustomEnchantment enchant = Vanilla.getEnchantment(name).orElse(EnchantmentAPI.getEnchantment(name));
67 | if (enchant == null) {
68 | command.sendMessage(sender, INVALID_ENCHANTMENT, "&4That is not a valid enchantment");
69 | return;
70 | }
71 | else if (!enchant.canEnchantOnto(item)) {
72 | command.sendMessage(sender, INCOMPATIBLE_ENCHANTMENT, "&4That enchantment doesn't work on that item");
73 | return;
74 | }
75 |
76 | // Run the computation task (usually takes 5-10ms)
77 | plugin.getServer()
78 | .getScheduler()
79 | .runTaskAsynchronously(plugin, () -> compute(enchant, sender, item));
80 | }
81 | catch (final Exception e) {
82 | // Do nothing
83 | }
84 | }
85 | }
86 |
87 | private int next;
88 | private int getNext() {
89 | return next++;
90 | }
91 |
92 | private double total(final Map> grouped, final String key, final Material material) {
93 | return grouped.get(key).stream().mapToDouble(e -> e.getWeight(material)).sum();
94 | }
95 |
96 | void compute(final CustomEnchantment enchant, final CommandSender sender, final ItemStack item) {
97 | final Player enchanter = (sender instanceof Player) ? (Player)sender : null;
98 | final int enchantability = Enchantability.determine(item.getType());
99 |
100 | // Part 1 - Selecting n weighted random enchantments
101 | final int maxModified = MECHANICS.maxLevel(30, enchantability);
102 | final List enchantWeights = new ArrayList<>();
103 | for (int i = 2; i <= maxModified; i++) {
104 |
105 | final List enchants = MECHANICS.getAllValidEnchants(item, enchanter, i, true);
106 | next = 0;
107 | final Map> grouped = enchants.stream().collect(
108 | Collectors.groupingBy(e -> e.getGroup().equals(CustomEnchantment.DEFAULT_GROUP) ? "d" + getNext() : e.getGroup()));
109 | final List groups = grouped.keySet().stream()
110 | .filter(key -> !grouped.get(key).contains(enchant))
111 | .collect(Collectors.toList());
112 | final List groupWeights = groups.stream()
113 | .map(key -> total(grouped, key, item.getType()))
114 | .collect(Collectors.toList());
115 |
116 | final double totalWeight = enchants.stream().mapToDouble(e -> e.getWeight(item.getType())).sum();
117 | final double targetGroupWeight = enchant.getGroup().equals(CustomEnchantment.DEFAULT_GROUP)
118 | ? enchant.getWeight(item.getType())
119 | : total(grouped, enchant.getGroup(), item.getType());
120 | final double enchantGroupWeight = enchant.getWeight(item.getType()) / targetGroupWeight;
121 |
122 | // Number of enchantment odds
123 | final int maxEnchants = Math.min(maxEnchantsSetting, grouped.size());
124 | final double[] numWeights = new double[maxEnchants];
125 | double remaining = 1.0;
126 | int lvl = i;
127 | for (int numEnchants = 0; numEnchants < maxEnchants; numEnchants++) {
128 | final double chance = 1 - (lvl + 1) * 0.02;
129 | numWeights[numEnchants] = remaining * (numEnchants < maxEnchants - 1 ? chance : 1);
130 | remaining *= 1 - chance;
131 | lvl /= 2;
132 | }
133 |
134 | // Selecting the enchantment odds
135 | double current = targetGroupWeight / totalWeight;
136 | double total = current * numWeights[0];
137 | final Set set = new HashSet<>();
138 | for (int numEnchants = 1; numEnchants < maxEnchants; numEnchants++) {
139 | current += compute(groupWeights, totalWeight, targetGroupWeight, numEnchants, set);
140 | total += current * numWeights[numEnchants];
141 | }
142 | enchantWeights.add(total * enchantGroupWeight);
143 | }
144 |
145 | // Part 2 - Enchantability distribution
146 | final Map enchantabilitySpread = MECHANICS.enchantabilitySpread(enchantability);
147 | final int enchantabilityWeight = MECHANICS.enchantabilityWeight(enchantability);
148 | final List modifierWeights = enchantabilitySpread.entrySet().stream()
149 | .sorted(Comparator.comparingInt(Map.Entry::getKey))
150 | .map(e -> (double)e.getValue() / enchantabilityWeight)
151 | .collect(Collectors.toList());
152 |
153 | // Part 3 - Uniform triangular distributed multiplier
154 | final List result = new ArrayList<>();
155 | for (int i = 1; i <= 30; i++) {
156 | final double[] weights = new double[enchant.getMaxTableLevel()];
157 | for (int k = 0; k < modifierWeights.size(); k++) {
158 | final double joined = enchantWeights.get(i + k - 1) * modifierWeights.get(k);
159 | for (int j = 1; j < weights.length; j++) {
160 | weights[j - 1] += MECHANICS.chance(i + 1 + k, enchant, j) * joined;
161 | }
162 | }
163 | result.add(weights);
164 | }
165 |
166 | // Results - render graph
167 | render(sender, enchant, result);
168 | }
169 |
170 | double compute(final List weights, final double totalWeight, final double targetWeight, final int enchants, final Set indices) {
171 | if (enchants == 0) return targetWeight / totalWeight;
172 |
173 | double total = 0;
174 | for (int i = 0; i < weights.size(); i++) {
175 | if (!indices.contains(i)) {
176 | indices.add(i);
177 | total += weights.get(i) * compute(weights, totalWeight - weights.get(i), targetWeight, enchants - 1, indices) / totalWeight;
178 | indices.remove(i);
179 | }
180 | }
181 | return total;
182 | }
183 |
184 | void render(final CommandSender sender, final CustomEnchantment enchant, final List probabilities) {
185 |
186 | // Get the maximum probability
187 | double max = 0;
188 | for (final double[] data : probabilities) {
189 | for (final double prob : data) {
190 | max = Math.max(prob, max);
191 | }
192 | }
193 | max = Math.ceil(max * 1000) / 1000;
194 |
195 | // Construct the graph
196 | for (int i = 8; i >= 0; i--) {
197 |
198 | final ChatColor lc = i == 0 ? ChatColor.GRAY : ChatColor.DARK_GRAY;
199 |
200 | // Label the intervals
201 | final StringBuilder line = new StringBuilder(99)
202 | .append(ChatColor.GOLD).append(FORMAT.format(i * max * 100 / 9))
203 | .append("-").append(FORMAT.format((i + 1) * max * 100 / 9))
204 | .append("%").append(lc).append("_");
205 | while (line.length() < 16) line.append("_");
206 | line.append(ChatColor.GRAY).append("|");
207 |
208 | // Print out the points
209 | for (final double[] data : probabilities) {
210 |
211 | // Search if any of the levels fell into the interval for the given level
212 | String piece = lc + "_";
213 | for (int k = 0; k < data.length; k++) {
214 | if (data[k] > i * max / 9 && data[k] <= (i + 1) * max / 9) {
215 | piece = ChatColor.getByChar((char)(49 + k % 6)) + "X";
216 | }
217 | }
218 | line.append(piece);
219 | }
220 |
221 | // Send the line of the graph to the sender
222 | sender.sendMessage(line.toString());
223 | }
224 |
225 | // Output the level scale at the bottom
226 | sender.sendMessage(ChatColor.DARK_GRAY + "||__________" + ChatColor.GRAY + "|" + ChatColor.DARK_GRAY
227 | + "____" + ChatColor.GRAY + "5" + ChatColor.DARK_GRAY + "____" + ChatColor.GRAY + "10"
228 | + ChatColor.DARK_GRAY + "___" + ChatColor.GRAY + "15" + ChatColor.DARK_GRAY + "___"
229 | + ChatColor.GRAY + "20" + ChatColor.DARK_GRAY + "___" + ChatColor.GRAY + "25" + ChatColor.DARK_GRAY
230 | + "___" + ChatColor.GRAY + "30");
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/cmd/CmdReload.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.cmd;
2 |
3 | import com.rit.sucy.commands.ConfigurableCommand;
4 | import com.rit.sucy.commands.IFunction;
5 | import com.sucy.enchant.EnchantmentAPI;
6 | import org.bukkit.command.CommandSender;
7 | import org.bukkit.plugin.Plugin;
8 | import org.bukkit.plugin.java.JavaPlugin;
9 |
10 | /**
11 | * EnchantmentAPI © 2017
12 | * com.sucy.enchant.cmd.CmdReload
13 | */
14 | public class CmdReload implements IFunction {
15 |
16 | @Override
17 | public void execute(
18 | final ConfigurableCommand configurableCommand,
19 | final Plugin plugin,
20 | final CommandSender commandSender,
21 | final String[] strings) {
22 |
23 | final EnchantmentAPI enchantmentAPI = JavaPlugin.getPlugin(EnchantmentAPI.class);
24 | enchantmentAPI.onDisable();
25 | enchantmentAPI.onEnable();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/cmd/CmdRemove.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.cmd;
2 |
3 | import com.rit.sucy.commands.ConfigurableCommand;
4 | import com.rit.sucy.commands.IFunction;
5 | import com.sucy.enchant.api.CustomEnchantment;
6 | import com.sucy.enchant.api.Enchantments;
7 | import org.bukkit.command.CommandSender;
8 | import org.bukkit.entity.Player;
9 | import org.bukkit.inventory.ItemStack;
10 | import org.bukkit.plugin.Plugin;
11 |
12 | import java.util.Map;
13 |
14 | import static com.sucy.enchant.util.Utils.isPresent;
15 |
16 | /**
17 | * EnchantmentAPI © 2017
18 | * com.sucy.enchant.cmd.CmdRemove
19 | */
20 | public class CmdRemove implements IFunction {
21 |
22 | private static final String NOT_PLAYER = "not-player";
23 | private static final String NO_ITEM = "no-item";
24 | private static final String NO_ENCHANTS = "no-enchants";
25 | private static final String REMOVED = "removed";
26 |
27 | @Override
28 | public void execute(
29 | final ConfigurableCommand command,
30 | final Plugin plugin,
31 | final CommandSender sender,
32 | final String[] strings) {
33 |
34 | if (!(sender instanceof Player)) {
35 | command.sendMessage(sender, NOT_PLAYER, "&4Only players can use this command");
36 | return;
37 | }
38 |
39 | final Player player = (Player)sender;
40 | final ItemStack item = player.getEquipment().getItemInMainHand();
41 | if (!isPresent(item)) {
42 | command.sendMessage(sender, NO_ITEM, "&4You don't have an item in your hand");
43 | return;
44 | }
45 |
46 | final Map enchantments = Enchantments.getAllEnchantments(item);
47 | if (enchantments.isEmpty()) {
48 | command.sendMessage(sender, NO_ENCHANTS, "&4That item doesn't have any enchantments");
49 | return;
50 | }
51 |
52 | Enchantments.removeAllEnchantments(item);
53 | command.sendMessage(sender, REMOVED, "&2 Removed all enchantments from your held item");
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/cmd/Commands.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.cmd;
2 |
3 | import com.rit.sucy.commands.CommandManager;
4 | import com.rit.sucy.commands.ConfigurableCommand;
5 | import com.rit.sucy.commands.SenderType;
6 | import com.sucy.enchant.EnchantmentAPI;
7 |
8 | /**
9 | * EnchantmentAPI © 2017
10 | * com.sucy.enchant.cmd.Commands
11 | */
12 | public class Commands {
13 |
14 | public static void init(final EnchantmentAPI plugin) {
15 | final ConfigurableCommand root = new ConfigurableCommand(plugin, "enchants", SenderType.ANYONE);
16 | root.addSubCommands(
17 | new ConfigurableCommand(
18 | plugin,
19 | "add",
20 | SenderType.PLAYER_ONLY,
21 | new CmdAdd(),
22 | "enchants held item",
23 | " ",
24 | "EnchantmentAPI.admin"),
25 | new ConfigurableCommand(
26 | plugin,
27 | "remove",
28 | SenderType.PLAYER_ONLY,
29 | new CmdRemove(),
30 | "removes enchants",
31 | "",
32 | "EnchantmentAPI.admin"),
33 | new ConfigurableCommand(
34 | plugin,
35 | "reload",
36 | SenderType.ANYONE,
37 | new CmdReload(),
38 | "reloads the plugin",
39 | "",
40 | "EnchantmentAPI.admin"),
41 | new ConfigurableCommand(
42 | plugin,
43 | "graph",
44 | SenderType.ANYONE,
45 | new CmdGraph(),
46 | "graphs probabilities",
47 | " ",
48 | "EnchantmentAPI.admin"),
49 | new ConfigurableCommand(
50 | plugin,
51 | "book",
52 | SenderType.PLAYER_ONLY,
53 | new CmdBook(),
54 | "detailed book",
55 | "",
56 | "EnchantmentAPI.admin")
57 | );
58 | CommandManager.registerCommand(root);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/data/ConfigKey.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.data;
2 |
3 | /**
4 | * EnchantmentAPI © 2017
5 | * com.sucy.enchant.data.ConfigKey
6 | */
7 | public enum ConfigKey {
8 | COLORED_NAMES_IN_ANVILS,
9 | CUSTOM_FISHING,
10 | FISHING_ENCHANTING_LEVEL,
11 | GLOBAL_ANVIL_LEVEL,
12 | MAX_ENCHANTMENTS,
13 | MAX_MERGED_ENCHANTMENTS,
14 | NON_ENCHANTABLES;
15 |
16 | private final String key = name().toLowerCase().replace('_', '-');
17 |
18 | public String getKey() {
19 | return key;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/data/Configuration.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.data;
2 |
3 | import com.rit.sucy.config.CommentedConfig;
4 | import com.rit.sucy.config.parse.DataSection;
5 | import com.sucy.enchant.EnchantmentAPI;
6 |
7 | /**
8 | * EnchantmentAPI © 2017
9 | * com.sucy.enchant.data.Configuration
10 | */
11 | public class Configuration {
12 |
13 | private static DataSection data;
14 |
15 | public static void reload(final EnchantmentAPI plugin) {
16 | final CommentedConfig config = new CommentedConfig(plugin, "config");
17 | config.saveDefaultConfig();
18 | config.checkDefaults();
19 | config.trim();
20 | config.save();
21 | data = config.getConfig();
22 | }
23 |
24 | public static boolean using(final ConfigKey configKey) {
25 | return data.getBoolean(configKey.getKey());
26 | }
27 |
28 | public static int amount(final ConfigKey configKey) {
29 | return data.getInt(configKey.getKey());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/data/Enchantability.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.data;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import com.rit.sucy.config.CommentedConfig;
5 | import com.rit.sucy.config.parse.DataSection;
6 | import com.rit.sucy.version.VersionManager;
7 | import com.sucy.enchant.EnchantmentAPI;
8 | import org.bukkit.Material;
9 |
10 | import java.util.HashMap;
11 | import java.util.List;
12 | import java.util.Map;
13 | import java.util.stream.Collectors;
14 |
15 | /**
16 | * EnchantmentAPI © 2017
17 | * com.sucy.enchant.data.Enchantability
18 | */
19 | public class Enchantability {
20 |
21 | private static final String TYPES = "types";
22 | private static final String ENCHANTABILITY = "enchantability";
23 | private static final String DEFAULT = "default";
24 |
25 | private static final Map VALUES = new HashMap<>();
26 |
27 | private static int defaultValue = 10;
28 |
29 | public static int determine(final Material material) {
30 | return VALUES.getOrDefault(material, defaultValue);
31 | }
32 |
33 | public static void init(final EnchantmentAPI enchantmentAPI) {
34 | final CommentedConfig config = new CommentedConfig(enchantmentAPI, "enchantability");
35 | checkDefaults(config);
36 |
37 | final DataSection data = config.getConfig();
38 | defaultValue = data.getInt(DEFAULT, 10);
39 | for (final String key : data.keys()) {
40 | if (data.isSection(key)) {
41 | final DataSection section = data.getSection(key);
42 | if (!section.has(TYPES) || !section.has(ENCHANTABILITY)) {
43 | enchantmentAPI.getLogger().warning(key + " in enchantability.yml is missing required values");
44 | } else {
45 | final int value = section.getInt(ENCHANTABILITY, 0);
46 | section.getList(TYPES).forEach(type -> {
47 | final Material mat = Material.matchMaterial(type);
48 | if (mat == null) {
49 | enchantmentAPI.getLogger().warning(type + " is not a valid material (under " + key + " in enchantability.yml)");
50 | } else {
51 | VALUES.put(mat, value);
52 | }
53 | });
54 | }
55 | } else if (!key.equals(DEFAULT)) {
56 | enchantmentAPI.getLogger().warning(key + " in enchantability.yml is not formatted properly");
57 | }
58 | }
59 | }
60 |
61 | private static void checkDefaults(final CommentedConfig config) {
62 | final DataSection data = config.getConfig();
63 | if (!config.getConfigFile().exists()) {
64 | for (final MaterialClass materialClass : MaterialClass.values()) {
65 | populate(data, ARMOR, materialClass.name, "armor", materialClass.armor);
66 | populate(data, WEAPON, materialClass.name, "tool", materialClass.weapon);
67 | }
68 | }
69 | if (!data.has(DEFAULT)) {
70 | data.set(DEFAULT, 1);
71 | config.save();
72 | }
73 | }
74 |
75 | private static void populate(
76 | final DataSection data,
77 | final List names,
78 | final String material,
79 | final String category,
80 | final int value) {
81 |
82 | if (value == 0) {
83 | return;
84 | }
85 |
86 | final List types = names.stream().map(type -> material + "_" + type).collect(Collectors.toList());
87 | final DataSection section = data.createSection(material.toLowerCase() + "-" + category);
88 | section.set(TYPES, types);
89 | section.set(ENCHANTABILITY, value);
90 | }
91 |
92 | private static final List ARMOR = ImmutableList.builder()
93 | .add("BOOTS")
94 | .add("CHESTPLATE")
95 | .add("HELMET")
96 | .add("LEGGINGS")
97 | .build();
98 |
99 | private static final List WEAPON = ImmutableList.builder()
100 | .add("AXE")
101 | .add("HOE")
102 | .add("PICKAXE")
103 | .add(VersionManager.isVersionAtLeast(11300) ? "SHOVEL" : "SPADE")
104 | .add("SWORD")
105 | .build();
106 |
107 | private enum MaterialClass {
108 | WOOD(0, 15, VersionManager.isVersionAtLeast(11300) ? "WOODEN" : "WOOD"),
109 | STONE(0, 5),
110 | IRON(9, 14),
111 | GOLD(25, 22, VersionManager.isVersionAtLeast(11300) ? "GOLDEN" : "GOLD"),
112 | DIAMOND(10, 10),
113 | LEATHER(15, 0),
114 | CHAINMAIL(12, 0);
115 |
116 | private final int armor;
117 | private final int weapon;
118 | private final String name;
119 |
120 | MaterialClass(final int armor, final int weapon) {
121 | this.armor = armor;
122 | this.weapon = weapon;
123 | this.name = this.name();
124 | }
125 |
126 | MaterialClass(final int armor, final int weapon, final String name) {
127 | this.armor = armor;
128 | this.weapon = weapon;
129 | this.name = name;
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/data/Path.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.data;
2 |
3 | /**
4 | * EnchantmentAPI © 2017
5 | * com.sucy.enchant.data.Path
6 | */
7 | public class Path {
8 | public static final String DATA_FOLDER = "data/";
9 | }
10 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/data/Permission.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.data;
2 |
3 | /**
4 | * EnchantmentAPI © 2017
5 | * com.sucy.enchant.data.Permission
6 | */
7 | public class Permission {
8 | private static final String PREFIX = "EnchantmentAPI.";
9 |
10 | public static final String ENCHANT = PREFIX + "enchant";
11 | public static final String ENCHANT_VANILLA = ENCHANT + ".vanilla";
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/data/PlayerEquips.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.data;
2 |
3 | import com.google.common.collect.ImmutableSet;
4 | import com.sucy.enchant.api.CustomEnchantment;
5 | import com.sucy.enchant.api.Enchantments;
6 | import org.bukkit.Material;
7 | import org.bukkit.entity.Player;
8 | import org.bukkit.inventory.ItemStack;
9 | import org.bukkit.inventory.PlayerInventory;
10 |
11 | import java.util.HashMap;
12 | import java.util.Map;
13 | import java.util.Set;
14 |
15 | import static com.sucy.enchant.util.Utils.isPresent;
16 |
17 | /**
18 | * EnchantmentAPI © 2017
19 | * com.sucy.enchant.data.PlayerEquips
20 | */
21 | public class PlayerEquips {
22 |
23 | private static final ItemData EMPTY = new ItemData();
24 | private static ItemData[] CLEAR = new ItemData[] { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY };
25 |
26 | private ItemData[] equipped = CLEAR;
27 | private int[] slots = new int[] { 36, 37, 38, 39, 40 };
28 |
29 | private Map enchantments = new HashMap<>();
30 |
31 | public PlayerEquips(final Player player) {
32 | update(player);
33 | }
34 |
35 | public Map getEnchantments() {
36 | return enchantments;
37 | }
38 |
39 | public void clear(final Player player) {
40 | swap(player, CLEAR);
41 | }
42 |
43 | public void update(final Player player) {
44 | final PlayerInventory inv = player.getInventory();
45 | final ItemData[] updated = new ItemData[equipped.length];
46 | for (int i = 0; i < updated.length; i++) {
47 | final ItemStack item = i < slots.length ? inv.getItem(slots[i]) : inv.getItemInMainHand();
48 | updated[i] = ItemData.from(item);
49 | }
50 | swap(player, updated);
51 | }
52 |
53 | public void clearWeapon(final Player player) {
54 | final ItemData[] copy = new ItemData[equipped.length];
55 | System.arraycopy(equipped, 0, copy, 0, equipped.length - 1);
56 | copy[copy.length - 1] = EMPTY;
57 | swap(player, copy);
58 | }
59 |
60 | public void updateWeapon(final PlayerInventory inventory) {
61 | final ItemData[] copy = new ItemData[equipped.length];
62 | System.arraycopy(equipped, 0, copy, 0, equipped.length - 1);
63 | copy[copy.length - 1] = ItemData.from(inventory.getItemInMainHand());
64 | swap((Player)inventory.getHolder(), copy);
65 | }
66 |
67 | private void swap(final Player player, final ItemData[] updatedEquips) {
68 | final Map newEnchants = new HashMap<>();
69 | for (final ItemData updatedEquip : updatedEquips) {
70 | mergeEnchantments(updatedEquip, newEnchants);
71 | }
72 |
73 | enchantments.forEach((enchant, level) -> {
74 | if (!newEnchants.containsKey(enchant)) {
75 | enchant.applyUnequip(player, level);
76 | }
77 | });
78 | newEnchants.forEach((enchant, level) -> {
79 | if (!enchantments.containsKey(enchant)) {
80 | enchant.applyEquip(player, level);
81 | } else {
82 | final int previous = enchantments.get(enchant);
83 | if (previous != level) {
84 | enchant.applyUnequip(player, previous);
85 | enchant.applyEquip(player, level);
86 | }
87 | }
88 | });
89 |
90 | equipped = updatedEquips;
91 | enchantments = newEnchants;
92 | }
93 |
94 | private void mergeEnchantments(final ItemData itemData, final Map enchantments) {
95 | itemData.enchantments.forEach((enchant, level) -> {
96 | final int previous = enchantments.getOrDefault(enchant, 0);
97 | final int merged = enchant.canStack() ? previous + level : Math.max(previous, level);
98 | enchantments.put(enchant, merged);
99 | });
100 | }
101 |
102 | private static class ItemData {
103 | private Map enchantments;
104 |
105 | private static ItemData from(final ItemStack item) {
106 | return !isPresent(item) || item.getType() == Material.ENCHANTED_BOOK ? EMPTY : new ItemData(item);
107 | }
108 |
109 | private ItemData() {
110 | enchantments = new HashMap<>();
111 | }
112 |
113 | private ItemData(final ItemStack item) {
114 | enchantments = Enchantments.getCustomEnchantments(item);
115 | }
116 | }
117 |
118 | public static final Set ARMOR_TYPES = getArmorMaterials();
119 |
120 | private static Set getArmorMaterials() {
121 | final Set armorSuffixes = ImmutableSet.of("BOOTS", "LEGGINGS", "CHESTPLATE", "HELMET");
122 | final ImmutableSet.Builder builder = ImmutableSet.builder();
123 | for (Material material : Material.values()) {
124 | final int index = material.name().lastIndexOf('_') + 1;
125 | final String suffix = material.name().substring(index);
126 | if (armorSuffixes.contains(suffix)) {
127 | builder.add(material);
128 | }
129 | }
130 | return builder.build();
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/listener/AnvilListener.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.listener;
2 |
3 | import com.rit.sucy.text.TextFormatter;
4 | import com.sucy.enchant.data.ConfigKey;
5 | import com.sucy.enchant.data.Configuration;
6 | import com.sucy.enchant.mechanics.EnchantmentMerger;
7 | import org.bukkit.ChatColor;
8 | import org.bukkit.GameMode;
9 | import org.bukkit.Material;
10 | import org.bukkit.entity.Player;
11 | import org.bukkit.event.EventHandler;
12 | import org.bukkit.event.inventory.InventoryClickEvent;
13 | import org.bukkit.event.inventory.InventoryType;
14 | import org.bukkit.event.inventory.PrepareAnvilEvent;
15 | import org.bukkit.inventory.AnvilInventory;
16 | import org.bukkit.inventory.ItemStack;
17 | import org.bukkit.inventory.meta.ItemMeta;
18 |
19 | import static com.sucy.enchant.util.Utils.isPresent;
20 |
21 | /**
22 | * EnchantmentAPI © 2017
23 | * com.sucy.enchant.listener.AnvilListener
24 | */
25 | public class AnvilListener extends BaseListener {
26 |
27 | private final boolean colored = Configuration.using(ConfigKey.COLORED_NAMES_IN_ANVILS);
28 |
29 | @EventHandler
30 | public void onCombine(final PrepareAnvilEvent event) {
31 | final ItemStack first = event.getInventory().getItem(0);
32 | final ItemStack second = event.getInventory().getItem(1);
33 | if (isSingle(first) && isSingle(second)) {
34 | final ItemStack result = select(first, second, true).clone();
35 | final EnchantmentMerger merger = getMerger(event.getInventory());
36 | if (merger.getCustomCost() != 0 || merger.getVanillaCost() != 0) {
37 | event.setResult(merger.apply(result));
38 | event.getInventory().setRepairCost(merger.getCost());
39 | }
40 | }
41 |
42 | if (isPresent(event.getResult())) {
43 | String text = event.getInventory().getRenameText();
44 |
45 | final ItemStack primary = select(first, second, true);
46 | if (primary.hasItemMeta()) {
47 | final ItemMeta primaryMeta = primary.getItemMeta();
48 | if (primaryMeta.hasDisplayName()) {
49 | final String withoutColorChar = primaryMeta.getDisplayName().replace("" + ChatColor.COLOR_CHAR, "");
50 | if (withoutColorChar.equals(event.getInventory().getRenameText())) {
51 | text = primaryMeta.getDisplayName();
52 | }
53 | }
54 | }
55 |
56 |
57 | final ItemMeta meta = event.getResult().getItemMeta();
58 | meta.setDisplayName(colored ? TextFormatter.colorString(text) : text);
59 | event.getResult().setItemMeta(meta);
60 | }
61 | }
62 |
63 | private EnchantmentMerger getMerger(final AnvilInventory anvil) {
64 | final ItemStack result = select(anvil.getItem(0), anvil.getItem(1), true).clone();
65 | final ItemStack supplement = select(anvil.getItem(0), anvil.getItem(1), false);
66 | return new EnchantmentMerger()
67 | .merge(result, false)
68 | .merge(supplement, true);
69 | }
70 |
71 | @EventHandler
72 | public void onClick(InventoryClickEvent event) {
73 | if (event.getInventory().getType() == InventoryType.ANVIL && event.getRawSlot() == 2) {
74 | final AnvilInventory anvil = (AnvilInventory) event.getInventory();
75 | if (anvil.getRepairCost() == 0
76 | && isPresent(anvil.getItem(2))
77 | && !isPresent(event.getWhoClicked().getItemOnCursor()) || !event.getAction().name().startsWith("PICKUP")) {
78 | final Player player = (Player)event.getWhoClicked();
79 | if (player.getGameMode() == GameMode.CREATIVE || checkLevels(player, anvil)) {
80 | player.setItemOnCursor(anvil.getItem(2));
81 | anvil.clear();
82 | }
83 | }
84 | }
85 | }
86 |
87 | private boolean checkLevels(final Player player, final AnvilInventory anvil) {
88 | final EnchantmentMerger merger = getMerger(anvil);
89 | if (player.getLevel() >= merger.getCost()) {
90 | player.setLevel(player.getLevel() - merger.getCost());
91 | return true;
92 | }
93 | return false;
94 | }
95 |
96 | private ItemStack select(final ItemStack first, final ItemStack second, final boolean result) {
97 | return (!isBook(first) || isBook(second)) == result ? first : second;
98 | }
99 |
100 | private boolean isBook(final ItemStack item) {
101 | return !isPresent(item) || item.getType() == Material.BOOK || item.getType() == Material.ENCHANTED_BOOK;
102 | }
103 |
104 | private boolean isSingle(final ItemStack item) {
105 | return isPresent(item) && item.getAmount() == 1;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/listener/BaseListener.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.listener;
2 |
3 | import com.sucy.enchant.EnchantmentAPI;
4 | import org.bukkit.entity.LivingEntity;
5 | import org.bukkit.entity.Projectile;
6 | import org.bukkit.event.Listener;
7 | import org.bukkit.event.entity.EntityDamageByEntityEvent;
8 |
9 | /**
10 | * EnchantmentAPI © 2017
11 | * com.sucy.enchant.listener.BaseListener
12 | */
13 | public abstract class BaseListener implements Listener {
14 | public void init(final EnchantmentAPI plugin) { }
15 | public void cleanUp(final EnchantmentAPI plugin) { }
16 |
17 | /**
18 | * Retrieves a damager from an entity damage event which will get the
19 | * shooter of projectiles if it was a projectile hitting them or
20 | * converts the Entity damager to a LivingEntity if applicable.
21 | *
22 | * @param event event to grab the damager from
23 | *
24 | * @return LivingEntity damager of the event or null if not found
25 | */
26 | LivingEntity getDamager(final EntityDamageByEntityEvent event)
27 | {
28 | if (event.getDamager() instanceof LivingEntity)
29 | {
30 | return (LivingEntity) event.getDamager();
31 | }
32 | else if (event.getDamager() instanceof Projectile)
33 | {
34 | final Projectile projectile = (Projectile) event.getDamager();
35 | if (projectile.getShooter() instanceof LivingEntity)
36 | {
37 | return (LivingEntity) projectile.getShooter();
38 | }
39 | }
40 | return null;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/listener/EnchantListener.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.listener;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import com.google.common.collect.ImmutableSet;
5 | import com.rit.sucy.config.CommentedConfig;
6 | import com.rit.sucy.config.parse.DataSection;
7 | import com.rit.sucy.items.ItemManager;
8 | import com.sucy.enchant.EnchantmentAPI;
9 | import com.sucy.enchant.api.Enchantments;
10 | import com.sucy.enchant.api.GlowEffects;
11 | import com.sucy.enchant.api.ItemSet;
12 | import com.sucy.enchant.api.Tasks;
13 | import com.sucy.enchant.data.ConfigKey;
14 | import com.sucy.enchant.data.Configuration;
15 | import com.sucy.enchant.data.Path;
16 | import com.sucy.enchant.mechanics.EnchantResult;
17 | import com.sucy.enchant.mechanics.EnchantingMechanics;
18 | import org.bukkit.ChatColor;
19 | import org.bukkit.GameMode;
20 | import org.bukkit.Material;
21 | import org.bukkit.enchantments.EnchantmentOffer;
22 | import org.bukkit.entity.Player;
23 | import org.bukkit.event.EventHandler;
24 | import org.bukkit.event.EventPriority;
25 | import org.bukkit.event.enchantment.EnchantItemEvent;
26 | import org.bukkit.event.enchantment.PrepareItemEnchantEvent;
27 | import org.bukkit.event.inventory.InventoryClickEvent;
28 | import org.bukkit.event.inventory.InventoryCloseEvent;
29 | import org.bukkit.event.inventory.InventoryType;
30 | import org.bukkit.event.player.PlayerJoinEvent;
31 | import org.bukkit.event.player.PlayerQuitEvent;
32 | import org.bukkit.inventory.ItemStack;
33 | import org.bukkit.inventory.meta.ItemMeta;
34 |
35 | import java.util.ArrayList;
36 | import java.util.HashMap;
37 | import java.util.List;
38 | import java.util.Map;
39 | import java.util.Random;
40 | import java.util.Set;
41 | import java.util.UUID;
42 |
43 | import static com.sucy.enchant.util.Utils.isPresent;
44 |
45 | /**
46 | * EnchantmentAPI © 2017
47 | * com.sucy.enchant.listener.EnchantListener
48 | */
49 | public class EnchantListener extends BaseListener {
50 |
51 | private static final String DATA_FILE = "seeds";
52 |
53 | private final Map enchantSeeds = new HashMap<>();
54 | private final Map placeholders = new HashMap<>();
55 | private final Map offers = new HashMap<>();
56 |
57 | private final EnchantingMechanics mechanics = new EnchantingMechanics();
58 |
59 | private final Random random = new Random();
60 |
61 | private final boolean nonEnchantables = Configuration.using(ConfigKey.NON_ENCHANTABLES);
62 |
63 | @Override
64 | public void init(final EnchantmentAPI plugin) {
65 | final DataSection data = new CommentedConfig(plugin, Path.DATA_FOLDER + DATA_FILE).getConfig();
66 | data.keys().forEach(key -> enchantSeeds.put(UUID.fromString(key), Long.parseLong(data.getString(key))));
67 | }
68 |
69 | @Override
70 | public void cleanUp(final EnchantmentAPI plugin) {
71 | final CommentedConfig config = new CommentedConfig(plugin, Path.DATA_FOLDER + DATA_FILE);
72 | final DataSection data = config.getConfig();
73 | data.clear();
74 |
75 | enchantSeeds.forEach((uuid, seed) -> data.set(uuid.toString(), Long.toString(seed)));
76 | config.save();
77 | }
78 |
79 | @EventHandler
80 | public void onJoin(final PlayerJoinEvent event) {
81 | enchantSeeds.computeIfAbsent(event.getPlayer().getUniqueId(), uuid -> random.nextLong());
82 | }
83 |
84 | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
85 | public void onPrepareEnchant(final PrepareItemEnchantEvent event) {
86 | final ItemStack item = getItem(event.getItem(), event.getEnchanter());
87 | final long seed = enchantSeeds.get(event.getEnchanter().getUniqueId());
88 | offers.put(event.getEnchanter().getUniqueId(), transformOffers(event.getOffers()));
89 | for (final EnchantmentOffer offer : event.getOffers()) {
90 | if (offer == null) continue;
91 | final EnchantResult result = mechanics.generateEnchantments(
92 | event.getEnchanter(), item, offer.getCost(), true, seed);
93 | result.getFirstVanillaEnchantment().ifPresent(enchant -> {
94 | offer.setEnchantment(enchant.getEnchantment());
95 | offer.setEnchantmentLevel(result.getFirstVanillaLevel());
96 | });
97 | }
98 | }
99 |
100 | private int[] transformOffers(final EnchantmentOffer[] offers) {
101 | final List result = new ArrayList<>(6);
102 | for (int i = 0; i < offers.length; i++) {
103 | if (offers[i] == null) continue;
104 |
105 | result.add(offers[i].getCost());
106 | result.add(i + 1);
107 | }
108 | return result.stream().mapToInt(Integer::intValue).toArray();
109 | }
110 |
111 | private ItemStack getItem(final ItemStack fromEvent, final Player enchanter) {
112 | return placeholders.getOrDefault(enchanter.getUniqueId(), fromEvent);
113 | }
114 |
115 | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
116 | public void onEnchant(final EnchantItemEvent event) {
117 | final ItemStack item = getItem(event.getItem(), event.getEnchanter());
118 | final long seed = enchantSeeds.get(event.getEnchanter().getUniqueId());
119 | final EnchantResult result = mechanics.generateEnchantments(
120 | event.getEnchanter(), item, event.getExpLevelCost(), true, seed);
121 |
122 | placeholders.remove(event.getEnchanter().getUniqueId());
123 | event.getEnchantsToAdd().clear();
124 | result.getEnchantments().forEach((enchant, level) -> enchant.addToItem(item, level));
125 | GlowEffects.finalize(item);
126 | enchantSeeds.put(event.getEnchanter().getUniqueId(), random.nextLong());
127 | event.getInventory().setItem(0, item);
128 | event.setCancelled(true);
129 |
130 | if (event.getEnchanter().getGameMode() != GameMode.CREATIVE) {
131 | int cost = 0;
132 | final int[] tiers = offers.get(event.getEnchanter().getUniqueId());
133 | for (int i = 0; i < tiers.length; i += 2) {
134 | if (tiers[i] == event.getExpLevelCost()) cost = tiers[i + 1];
135 | }
136 | event.getEnchanter().setLevel(event.getEnchanter().getLevel() - cost);
137 | event.getInventory().removeItem(new ItemStack(ItemSet.INK_SACK.getItems()[0], cost, (short) 4));
138 | }
139 | }
140 |
141 | @EventHandler
142 | public void onClick(final InventoryClickEvent event) {
143 | if (!nonEnchantables) {
144 | return;
145 | }
146 |
147 | if (event.getInventory().getType() == InventoryType.ENCHANTING) {
148 | if (event.getRawSlot() == 0 && placeholders.containsKey(event.getWhoClicked().getUniqueId())) {
149 | event.getInventory().setItem(0, placeholders.remove(event.getWhoClicked().getUniqueId()));
150 | }
151 |
152 | Tasks.schedule(() -> {
153 | final ItemStack item = event.getInventory().getItem(0);
154 | if (isPresent(item) && !ENCHANTABLES.contains(item.getType())
155 | && Enchantments.getAllEnchantments(item).isEmpty()
156 | && mechanics.hasValidEnchantments(item)
157 | && !placeholders.containsKey(event.getWhoClicked().getUniqueId())) {
158 | placeholders.put(event.getWhoClicked().getUniqueId(), item);
159 | event.getInventory().setItem(0, wrap(item));
160 | }
161 | });
162 | }
163 | }
164 |
165 | @EventHandler
166 | public void onClose(final InventoryCloseEvent event) {
167 | if (placeholders.containsKey(event.getPlayer().getUniqueId())) {
168 | event.getInventory().setItem(0, placeholders.remove(event.getPlayer().getUniqueId()));
169 | }
170 | }
171 |
172 | @EventHandler
173 | public void onQuit(final PlayerQuitEvent event) {
174 | offers.remove(event.getPlayer().getUniqueId());
175 | if (placeholders.containsKey(event.getPlayer().getUniqueId())) {
176 | event.getPlayer().closeInventory();
177 | }
178 | }
179 |
180 | private ItemStack wrap(final ItemStack item) {
181 | final ItemStack wrapper = new ItemStack(Material.BOOK);
182 | final ItemMeta meta = wrapper.getItemMeta();
183 | meta.setDisplayName(ChatColor.LIGHT_PURPLE + "Enchanting");
184 | meta.setLore(ImmutableList.of(ItemManager.getVanillaName(item)));
185 | wrapper.setItemMeta(meta);
186 | return wrapper;
187 | }
188 |
189 | private static final Set ENCHANTABLES = ImmutableSet.builder()
190 | .add(ItemSet.VANILLA_ENCHANTABLES.getItems())
191 | .add(Material.BOOK)
192 | .add(Material.ENCHANTED_BOOK)
193 | .build();
194 | }
195 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/listener/FishingListener.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.listener;
2 |
3 | import com.sucy.enchant.api.Enchantments;
4 | import com.sucy.enchant.data.ConfigKey;
5 | import com.sucy.enchant.data.Configuration;
6 | import com.sucy.enchant.mechanics.EnchantingMechanics;
7 | import org.bukkit.entity.Item;
8 | import org.bukkit.event.EventHandler;
9 | import org.bukkit.event.player.PlayerFishEvent;
10 | import org.bukkit.inventory.ItemStack;
11 |
12 | import java.util.Random;
13 |
14 | /**
15 | * EnchantmentAPI © 2017
16 | * com.sucy.enchant.listener.FishingListener
17 | */
18 | public class FishingListener extends BaseListener {
19 |
20 | private final Random random = new Random();
21 |
22 | private final EnchantingMechanics mechanics = new EnchantingMechanics();
23 |
24 | private final int bookLevel = Configuration.amount(ConfigKey.FISHING_ENCHANTING_LEVEL);
25 |
26 | @EventHandler
27 | public void onCatch(final PlayerFishEvent event) {
28 | if (!(event.getCaught() instanceof Item))
29 | return;
30 |
31 | final Item caught = (Item)event.getCaught();
32 | final ItemStack item = caught.getItemStack();
33 | if (!Enchantments.getAllEnchantments(item).isEmpty()) {
34 | Enchantments.removeAllEnchantments(item);
35 | mechanics.generateEnchantments(event.getPlayer(), item, bookLevel, false, random.nextLong())
36 | .getEnchantments().forEach((enchant, level) -> enchant.addToItem(item, level));
37 | caught.setItemStack(item);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/listener/ItemListener.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.listener;
2 |
3 | import com.sucy.enchant.api.Enchantments;
4 | import com.sucy.enchant.api.Tasks;
5 | import com.sucy.enchant.data.PlayerEquips;
6 | import org.bukkit.entity.LivingEntity;
7 | import org.bukkit.entity.Player;
8 | import org.bukkit.event.EventHandler;
9 | import org.bukkit.event.EventPriority;
10 | import org.bukkit.event.block.Action;
11 | import org.bukkit.event.block.BlockBreakEvent;
12 | import org.bukkit.event.entity.EntityDamageByEntityEvent;
13 | import org.bukkit.event.entity.EntityDamageEvent;
14 | import org.bukkit.event.entity.EntityPickupItemEvent;
15 | import org.bukkit.event.entity.ProjectileLaunchEvent;
16 | import org.bukkit.event.inventory.InventoryCloseEvent;
17 | import org.bukkit.event.player.PlayerDropItemEvent;
18 | import org.bukkit.event.player.PlayerInteractEntityEvent;
19 | import org.bukkit.event.player.PlayerInteractEvent;
20 | import org.bukkit.event.player.PlayerItemBreakEvent;
21 | import org.bukkit.event.player.PlayerItemHeldEvent;
22 | import org.bukkit.event.player.PlayerJoinEvent;
23 | import org.bukkit.event.player.PlayerQuitEvent;
24 | import org.bukkit.inventory.ItemStack;
25 |
26 | import java.util.HashMap;
27 | import java.util.UUID;
28 |
29 | import static com.sucy.enchant.util.Utils.isPresent;
30 |
31 | /**
32 | * EnchantmentAPI © 2017
33 | * com.sucy.enchant.listener.ItemListener
34 | */
35 | public class ItemListener extends BaseListener {
36 |
37 | private static final HashMap LAST_INTERACT_BLOCK = new HashMap<>();
38 | private static final HashMap LAST_INTERACT_ENTITY = new HashMap<>();
39 |
40 | private static final int INTERACT_DELAY_MILLIS = 250;
41 |
42 | // ---- Tracking enchantments ---- //
43 |
44 | @EventHandler
45 | public void onJoin(final PlayerJoinEvent event) {
46 | Enchantments.getEquipmentData(event.getPlayer());
47 | }
48 |
49 | @EventHandler
50 | public void onQuit(final PlayerQuitEvent event) {
51 | Enchantments.clearEquipmentData(event.getPlayer());
52 | LAST_INTERACT_BLOCK.remove(event.getPlayer().getUniqueId());
53 | LAST_INTERACT_ENTITY.remove(event.getPlayer().getUniqueId());
54 | }
55 |
56 | @EventHandler
57 | public void onDrop(final PlayerDropItemEvent event) {
58 | Enchantments.getEquipmentData(event.getPlayer()).clearWeapon(event.getPlayer());
59 | }
60 |
61 | @EventHandler
62 | public void onBreak(final PlayerItemBreakEvent event) {
63 | final PlayerEquips equips = Enchantments.getEquipmentData(event.getPlayer());
64 | Tasks.schedule(() -> equips.updateWeapon(event.getPlayer().getInventory()));
65 | }
66 |
67 | @EventHandler
68 | public void onPickup(final EntityPickupItemEvent event) {
69 | if (event.getEntity() instanceof Player) {
70 | final PlayerEquips equips = Enchantments.getEquipmentData((Player) event.getEntity());
71 | Tasks.schedule(() -> equips.updateWeapon(((Player) event.getEntity()).getInventory()));
72 | }
73 | }
74 |
75 | @EventHandler
76 | public void onHeld(final PlayerItemHeldEvent event) {
77 | final PlayerEquips equips = Enchantments.getEquipmentData(event.getPlayer());
78 | Tasks.schedule(() -> equips.updateWeapon(event.getPlayer().getInventory()));
79 | }
80 |
81 | @EventHandler
82 | public void onClose(final InventoryCloseEvent event) {
83 | final PlayerEquips equips = Enchantments.getEquipmentData((Player) event.getPlayer());
84 | Tasks.schedule(() -> equips.update((Player) event.getPlayer()));
85 | }
86 |
87 | @EventHandler
88 | public void onInteract(final PlayerInteractEvent event) {
89 | final ItemStack item = event.getPlayer().getInventory().getItemInMainHand();
90 | if ((event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK)
91 | && isPresent(item) && PlayerEquips.ARMOR_TYPES.contains(item.getType())) {
92 | final PlayerEquips equips = Enchantments.getEquipmentData(event.getPlayer());
93 | Tasks.schedule(() -> equips.update(event.getPlayer()));
94 | }
95 | }
96 |
97 | // ---- API Methods ---- //
98 |
99 | private boolean running = false;
100 |
101 | @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
102 | public void onHit(final EntityDamageByEntityEvent event) {
103 | if (running || event.getCause() == EntityDamageEvent.DamageCause.CUSTOM) {
104 | return;
105 | }
106 |
107 | running = true;
108 | try {
109 | final LivingEntity damager = getDamager(event);
110 | if (damager instanceof Player && event.getEntity() instanceof LivingEntity) {
111 | Enchantments.getEnchantments((Player) damager).forEach(
112 | (enchant, level) -> enchant.applyOnHit(
113 | damager,
114 | (LivingEntity) event.getEntity(),
115 | level,
116 | event));
117 | }
118 |
119 | if (event.getEntity() instanceof Player && damager != null) {
120 | Enchantments.getEnchantments((Player) event.getEntity()).forEach(
121 | (enchant, level) -> enchant.applyDefense((Player) event.getEntity(), damager, level, event));
122 | }
123 | } catch (final Exception ex) {
124 | ex.printStackTrace();
125 | }
126 | running = false;
127 | }
128 |
129 | @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
130 | public void onShoot(final ProjectileLaunchEvent event) {
131 | if (!(event.getEntity().getShooter() instanceof Player)) {
132 | return;
133 | }
134 | final Player shooter = (Player) event.getEntity().getShooter();
135 | Enchantments.getEnchantments(shooter)
136 | .forEach((enchant, level) -> enchant.applyProjectile(shooter, level, event));
137 | }
138 |
139 | @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
140 | public void onBreak(final BlockBreakEvent event) {
141 | Enchantments.getEnchantments(event.getPlayer()).forEach(
142 | (enchant, level) -> enchant.applyBreak(event.getPlayer(), event.getBlock(), level, event));
143 | }
144 |
145 | @EventHandler(priority = EventPriority.HIGH)
146 | public void onInteractBlock(final PlayerInteractEvent event) {
147 | if (LAST_INTERACT_BLOCK.getOrDefault(event.getPlayer().getUniqueId(), 0L) > System.currentTimeMillis()) {
148 | return;
149 | }
150 |
151 | LAST_INTERACT_BLOCK.put(event.getPlayer().getUniqueId(), System.currentTimeMillis() + INTERACT_DELAY_MILLIS);
152 | Enchantments.getEnchantments(event.getPlayer()).forEach(
153 | (enchant, level) -> enchant.applyInteractBlock(event.getPlayer(), level, event));
154 | }
155 |
156 | @EventHandler(priority = EventPriority.HIGH)
157 | public void onInteractEntity(final PlayerInteractEntityEvent event) {
158 | if (LAST_INTERACT_ENTITY.getOrDefault(event.getPlayer().getUniqueId(), 0L) > System.currentTimeMillis()) {
159 | return;
160 | }
161 |
162 | LAST_INTERACT_ENTITY.put(event.getPlayer().getUniqueId(), System.currentTimeMillis() + INTERACT_DELAY_MILLIS);
163 | Enchantments.getEnchantments(event.getPlayer()).forEach(
164 | (enchant, level) -> enchant.applyInteractEntity(event.getPlayer(), level, event));
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/mechanics/EnchantResult.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.mechanics;
2 |
3 | import com.sucy.enchant.api.CustomEnchantment;
4 | import com.sucy.enchant.vanilla.VanillaEnchantment;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 | import java.util.Optional;
9 |
10 | /**
11 | * EnchantmentAPI © 2017
12 | * com.sucy.enchant.mechanics.EnchantResult
13 | */
14 | public class EnchantResult {
15 | private final Map enchantments = new HashMap<>();
16 |
17 | private VanillaEnchantment firstVanilla;
18 | private int firstVanillaLevel;
19 | private int enchantLevel;
20 | private int enchantabilityModifier;
21 |
22 | public EnchantResult(final int enchantLevel, final int modifier) {
23 | this.enchantLevel = enchantLevel;
24 | this.enchantabilityModifier = modifier;
25 | }
26 |
27 | void addEnchantment(final CustomEnchantment enchant, final Integer level) {
28 | if (enchant instanceof VanillaEnchantment) {
29 | firstVanilla = (VanillaEnchantment) enchant;
30 | firstVanillaLevel = level;
31 | }
32 | enchantments.put(enchant, level);
33 | }
34 |
35 | public int getEnchantabilityModifier() {
36 | return enchantabilityModifier;
37 | }
38 |
39 | public int getEnchantLevel() {
40 | return enchantLevel;
41 | }
42 |
43 | public Optional getFirstVanillaEnchantment() {
44 | return Optional.ofNullable(firstVanilla);
45 | }
46 |
47 | public int getFirstVanillaLevel() {
48 | return firstVanillaLevel;
49 | }
50 |
51 | public Map getEnchantments() {
52 | return enchantments;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/mechanics/EnchantingMechanics.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.mechanics;
2 |
3 | import com.sucy.enchant.EnchantmentAPI;
4 | import com.sucy.enchant.api.CustomEnchantment;
5 | import com.sucy.enchant.data.ConfigKey;
6 | import com.sucy.enchant.data.Configuration;
7 | import com.sucy.enchant.data.Enchantability;
8 | import org.bukkit.Material;
9 | import org.bukkit.entity.Player;
10 | import org.bukkit.inventory.ItemStack;
11 | import org.bukkit.permissions.Permissible;
12 |
13 | import java.util.Collection;
14 | import java.util.HashMap;
15 | import java.util.List;
16 | import java.util.Map;
17 | import java.util.Random;
18 | import java.util.stream.Collectors;
19 |
20 | /**
21 | * EnchantmentAPI © 2017
22 | * com.sucy.enchant.mechanics.EnchantingMechanics
23 | */
24 | public class EnchantingMechanics {
25 |
26 | private final int maxEnchants = Configuration.amount(ConfigKey.MAX_ENCHANTMENTS);
27 |
28 | /**
29 | * Generates an enchantment result for the given item
30 | *
31 | * @param enchanter player enchanting the item (use null to bypass permissions)
32 | * @param item item to enchant
33 | * @param enchantLevel enchantment level (1-30 from the table)
34 | * @param table whether or not it is a table enchantment. If not a table enchantment maxLevel is used over maxTableLevel
35 | * @param seed enchanter's random seed for enchanting
36 | * @return enchanting result
37 | */
38 | public EnchantResult generateEnchantments(
39 | final Player enchanter, final ItemStack item, final int enchantLevel, final boolean table, final long seed) {
40 |
41 | final Random random = new Random(seed);
42 |
43 | final int modifier = getModifier(Enchantability.determine(item.getType()), random);
44 | final int adjustedLevel = modifyLevel(enchantLevel, modifier, random);
45 | final EnchantResult result = new EnchantResult(adjustedLevel, modifier);
46 |
47 | List available = getAllValidEnchants(item, enchanter, adjustedLevel, table);
48 | if (available.isEmpty()) {
49 | return result;
50 | }
51 |
52 | CustomEnchantment toAdd = weightedRandom(available, item.getType(), random);
53 | result.addEnchantment(toAdd, toAdd.computeLevel(adjustedLevel));
54 | int repeatFactor = adjustedLevel;
55 | while (random.nextInt(50) <= repeatFactor && result.getEnchantments().size() < maxEnchants) {
56 | final CustomEnchantment previous = toAdd;
57 | available = available.stream()
58 | .filter(enchant -> !previous.conflictsWith(enchant, true))
59 | .collect(Collectors.toList());
60 | if (available.isEmpty()) {
61 | break;
62 | }
63 |
64 | toAdd = weightedRandom(available, item.getType(), random);
65 | result.addEnchantment(toAdd, toAdd.computeLevel(adjustedLevel));
66 | repeatFactor >>= 1;
67 | }
68 |
69 | return result;
70 | }
71 |
72 | private CustomEnchantment weightedRandom(
73 | final Collection enchantments, final Material material, final Random random) {
74 |
75 | final double totalWeight = enchantments.stream()
76 | .mapToDouble(enchant -> enchant.getWeight(material))
77 | .sum();
78 |
79 | double roll = random.nextDouble() * totalWeight;
80 | for (final CustomEnchantment enchantment : enchantments) {
81 | roll -= enchantment.getWeight(material);
82 | if (roll < 0) {
83 | return enchantment;
84 | }
85 | }
86 | throw new IllegalStateException("Should not call weighted random without any entries");
87 | }
88 |
89 | private int getModifier(final int enchantability, final Random random) {
90 | final int modifiedEnchantability = enchantability / 4 + 1;
91 | return random.nextInt(modifiedEnchantability) + random.nextInt(modifiedEnchantability);
92 | }
93 |
94 | private int modifyLevel(final int level, final int modifier, final Random random) {
95 | final int modified = level + 1 + modifier;
96 | final float percentage = 0.85f + (random.nextFloat() + random.nextFloat()) * 0.15f;
97 | return (int)Math.max(1, modified * percentage + 0.5f);
98 | }
99 |
100 | /**
101 | * Get all enchantments which can be applied to the given ItemStack
102 | *
103 | * @param item item to check for applicable enchantments
104 | * @param level modified enchantment level
105 | * @return List of all applicable enchantments
106 | */
107 | public List getAllValidEnchants(final ItemStack item, final Permissible enchanter, final int level, final boolean table) {
108 | return EnchantmentAPI.getRegisteredEnchantments().stream()
109 | .filter(CustomEnchantment::isEnabled)
110 | .filter(enchant -> !table || enchant.isTableEnabled())
111 | .filter(enchant -> item.getType() == Material.BOOK || enchant.canEnchantOnto(item))
112 | .filter(enchant -> enchant.hasPermission(enchanter))
113 | .filter(enchant -> enchant.computeLevel(level) >= 1)
114 | .collect(Collectors.toList());
115 | }
116 |
117 | public boolean hasValidEnchantments(final ItemStack item) {
118 | return EnchantmentAPI.getRegisteredEnchantments().stream()
119 | .anyMatch(enchant -> enchant.canEnchantOnto(item));
120 | }
121 |
122 | // --- Computing statistics --- //
123 |
124 | public int maxLevel(final int level, final int enchantability) {
125 | return (int)Math.max(1, (level + 1 + enchantability / 2) * 1.15f + 0.5f);
126 | }
127 |
128 | public Map enchantabilitySpread(final int enchantability) {
129 | final int val = enchantability / 4 + 1;
130 | final Map result = new HashMap<>(val * 2 - 1);
131 | for (int i = 0; i < val; i++) {
132 | result.put(i, i + 1);
133 | result.put(val * 2 - 2 - i, i + 1);
134 | }
135 | return result;
136 | }
137 |
138 | public int enchantabilityWeight(final int enchantability) {
139 | final int val = enchantability / 4 + 1;
140 | return val * val;
141 | }
142 |
143 | public double chance(final int level, final CustomEnchantment enchant, final int target) {
144 | final int min = (int)Math.ceil(enchant.getMinEnchantingLevel() + enchant.getEnchantLevelScaleFactor() * (target - 1));
145 | final int max = (int)Math.ceil(enchant.getMinEnchantingLevel() + enchant.getEnchantLevelScaleFactor() * target
146 | + (target == enchant.getMaxTableLevel() ? enchant.getEnchantLevelBuffer() : 0));
147 |
148 | if (min == max) {
149 | return 0;
150 | }
151 |
152 | final double minRand = Math.max(0, Math.min(1, 0.5 + (min - level) / 0.3));
153 | final double maxRand = Math.max(0, Math.min(1, 0.5 + (max - level) / 0.3));
154 |
155 | return triangularChance(maxRand) - triangularChance(minRand);
156 | }
157 |
158 | // Compute chance using symmetric triangular distribution formula
159 | // https://en.wikipedia.org/wiki/Triangular_distribution#Symmetric_triangular_distribution
160 | // 2x^2 (0 < x < 0.5), 2x^2 - (2x - 1)^2 (0.5 < x < 1)
161 | private double triangularChance(final double rand) {
162 | if (rand <= 0.5) return 2 * (rand * rand);
163 | final double sub = 2 * rand - 1;
164 | return 2 * (rand * rand) - sub * sub;
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/mechanics/EnchantmentMerger.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.mechanics;
2 |
3 | import com.sucy.enchant.api.CustomEnchantment;
4 | import com.sucy.enchant.api.Enchantments;
5 | import com.sucy.enchant.api.GlowEffects;
6 | import com.sucy.enchant.data.ConfigKey;
7 | import com.sucy.enchant.data.Configuration;
8 | import com.sucy.enchant.vanilla.VanillaEnchantment;
9 | import org.bukkit.Material;
10 | import org.bukkit.inventory.ItemStack;
11 |
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | /**
16 | * EnchantmentAPI © 2017
17 | * com.sucy.enchant.mechanics.EnchantmentMerger
18 | */
19 | public class EnchantmentMerger {
20 |
21 | private final Map enchants;
22 |
23 | private int customCost;
24 | private int vanillaCost;
25 | private int repairCost = 0;
26 |
27 | private final int maxEnchants = Configuration.amount(ConfigKey.MAX_MERGED_ENCHANTMENTS);
28 | private final int maxLevel = Configuration.amount(ConfigKey.GLOBAL_ANVIL_LEVEL);
29 |
30 | public EnchantmentMerger() {
31 | enchants = new HashMap<>();
32 | }
33 |
34 | public int getCost() {
35 | return repairCost + customCost + vanillaCost;
36 | }
37 |
38 | public int getVanillaCost() {
39 | return vanillaCost;
40 | }
41 |
42 | public int getCustomCost() {
43 | return customCost;
44 | }
45 |
46 | public EnchantmentMerger merge(final ItemStack item, final boolean addCost) {
47 | if (item.getDurability() < item.getType().getMaxDurability()) {
48 | repairCost = 2;
49 | }
50 |
51 | Enchantments.getAllEnchantments(item).forEach(
52 | (enchant, level) -> this.merge(enchant, level, addCost, item.getType() == Material.ENCHANTED_BOOK));
53 | return this;
54 | }
55 |
56 | public void merge(final CustomEnchantment enchantment, final int level, final boolean addCost, final boolean book) {
57 |
58 | // Ignore conflicting enchantments
59 | if (enchants.keySet().stream().anyMatch(enchant -> enchant.conflictsWith(enchantment, false))
60 | || (enchants.containsKey(enchantment) && enchants.size() == maxEnchants)) {
61 | if (addCost) {
62 | vanillaCost++;
63 | }
64 | return;
65 | }
66 |
67 | final int primaryLevel = enchants.getOrDefault(enchantment, 0);
68 | final int combinedLevel = primaryLevel == level
69 | ? Math.min(Math.max(maxLevel, enchantment.getMaxLevel()), level + 1)
70 | : Math.max(primaryLevel, level);
71 | enchants.put(enchantment, combinedLevel);
72 |
73 | if (addCost) {
74 | final int costPerLevel = Math.max(1, enchantment.getCombineCostPerLevel() >> (book ? 1 : 0));
75 | if (enchantment instanceof VanillaEnchantment) {
76 | final int vanillaMax = ((VanillaEnchantment) enchantment).getEnchantment().getMaxLevel();
77 | vanillaCost += Math.min(vanillaMax, combinedLevel) * costPerLevel;
78 | customCost += Math.max(0, combinedLevel - vanillaMax) * costPerLevel;
79 | }
80 | else {
81 | customCost += combinedLevel * costPerLevel;
82 | }
83 | }
84 | }
85 |
86 | public ItemStack apply(final ItemStack item) {
87 | final ItemStack result = Enchantments.removeAllEnchantments(item);
88 | enchants.entrySet().stream()
89 | .filter(e -> e.getKey().canMergeOnto(item))
90 | .forEach(e -> e.getKey().addToItem(result, e.getValue()));
91 | return GlowEffects.finalize(result);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/skillapi/SkillAPIHook.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.skillapi;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import com.rit.sucy.config.CommentedConfig;
5 | import com.sucy.enchant.EnchantmentAPI;
6 | import com.sucy.enchant.api.CustomEnchantment;
7 | import org.bukkit.Bukkit;
8 |
9 | import java.io.File;
10 | import java.util.List;
11 |
12 | /**
13 | * EnchantmentAPI © 2017
14 | * com.sucy.enchant.skillapi.SkillAPIHook
15 | */
16 | public class SkillAPIHook {
17 |
18 | public static List getEnchantments(final EnchantmentAPI plugin) {
19 | if (Bukkit.getPluginManager().isPluginEnabled("SkillAPI")) {
20 | return loadAll(plugin);
21 | }
22 | plugin.getLogger().info("SkillAPI not enabled, skipping skill-based enchantments");
23 | return ImmutableList.of();
24 | }
25 |
26 | private static List loadAll(final EnchantmentAPI plugin) {
27 | final String path = plugin.getDataFolder().getAbsolutePath() + "/" + SkillEnchantment.SAVE_FOLDER;
28 | final File directory = new File(path);
29 | if (!directory.exists()) {
30 | if (!directory.mkdirs()) {
31 | plugin.getLogger().severe("Unable to create folder at: " + path);
32 | }
33 | }
34 |
35 | final File[] files = directory.listFiles();
36 | if (files == null) {
37 | plugin.getLogger().warning("Folder \"" + path + "\" does not exist");
38 | return ImmutableList.of();
39 | }
40 |
41 | final ImmutableList.Builder result = ImmutableList.builder();
42 | for (final File file : files) {
43 | if (!file.getName().endsWith(".yml")) {
44 | plugin.getLogger().warning(file.getName() + " is not a .yml file but is in the skill enchantments folder");
45 | continue;
46 | }
47 |
48 | try {
49 | final String fileName = file.getName().replace(".yml", "");
50 | final CommentedConfig config = new CommentedConfig(plugin, SkillEnchantment.SAVE_FOLDER + fileName);
51 | result.add(new SkillEnchantment(fileName, config.getConfig()));
52 | } catch (final Exception ex) {
53 | // Do nothing
54 | plugin.getLogger().warning("Failed to load " + file.getName() + ", make sure it points to a valid skill");
55 | ex.printStackTrace();
56 | }
57 | }
58 | return result.build();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/skillapi/SkillEnchantment.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.skillapi;
2 |
3 | import com.rit.sucy.config.parse.DataSection;
4 | import com.sucy.enchant.api.Cooldowns;
5 | import com.sucy.enchant.api.CustomEnchantment;
6 | import com.sucy.skill.SkillAPI;
7 | import com.sucy.skill.api.skills.PassiveSkill;
8 | import com.sucy.skill.api.skills.Skill;
9 | import com.sucy.skill.api.skills.SkillAttribute;
10 | import com.sucy.skill.api.skills.SkillShot;
11 | import com.sucy.skill.api.skills.TargetSkill;
12 | import org.bukkit.entity.LivingEntity;
13 | import org.bukkit.entity.Player;
14 | import org.bukkit.event.block.Action;
15 | import org.bukkit.event.player.PlayerInteractEntityEvent;
16 | import org.bukkit.event.player.PlayerInteractEvent;
17 |
18 | /**
19 | * EnchantmentAPI © 2017
20 | * com.sucy.enchant.skillapi.SkillEnchantment
21 | */
22 | public class SkillEnchantment extends CustomEnchantment {
23 |
24 | private static final String SKILL = "skill";
25 | private static final String EFFECT = "effect.";
26 | private static final String CLICK = "right-click";
27 |
28 | private final Skill skill;
29 |
30 | SkillEnchantment(final String key, final DataSection data) {
31 | super(key, "No description provided");
32 |
33 | String skillName = data.getString(SKILL, data.getString(EFFECT + SKILL));
34 | settings.set(SKILL, skillName);
35 | settings.set(CLICK, true);
36 |
37 | skill = SkillAPI.getSkill(skillName);
38 | if (skill == null) System.out.println(data.getString(SKILL) + " is not a skill");
39 | setMaxLevel(skill.getMaxLevel());
40 | Cooldowns.configure(settings,
41 | skill.getSettings().getBase(SkillAttribute.COOLDOWN),
42 | skill.getSettings().getScale(SkillAttribute.COOLDOWN));
43 | }
44 |
45 | @Override
46 | public void applyEquip(final LivingEntity user, final int level) {
47 | if (skill instanceof PassiveSkill) {
48 | ((PassiveSkill) skill).initialize(user, level);
49 | }
50 | }
51 |
52 | @Override
53 | public void applyUnequip(final LivingEntity user, final int level) {
54 | if (skill instanceof PassiveSkill) {
55 | ((PassiveSkill) skill).stopEffects(user, level);
56 | }
57 | }
58 |
59 | @Override
60 | public void applyInteractBlock(
61 | final Player user, final int level, final PlayerInteractEvent event) {
62 |
63 | if (event.getAction() == Action.PHYSICAL) return;
64 | final boolean isRightClick = event.getAction() == Action.RIGHT_CLICK_AIR
65 | || event.getAction() == Action.RIGHT_CLICK_BLOCK;
66 |
67 | if (isRightClick == settings.getBoolean(CLICK, true)) {
68 | applySkillShot(user, level);
69 | }
70 | }
71 |
72 | @Override
73 | public void applyInteractEntity(
74 | final Player user, final int level, final PlayerInteractEntityEvent event) {
75 | if (skill instanceof TargetSkill && event.getRightClicked() instanceof LivingEntity) {
76 | if (!Cooldowns.onCooldown(this, user, settings, level)) {
77 | final LivingEntity target = (LivingEntity)event.getRightClicked();
78 | if (((TargetSkill) skill).cast(user, target, level, SkillAPI.getSettings().isAlly(user, target))) {
79 | Cooldowns.start(this, user);
80 | }
81 | }
82 | } else applySkillShot(user, level);
83 | }
84 |
85 | private void applySkillShot(final Player user, final int level) {
86 | if (skill instanceof SkillShot && !Cooldowns.onCooldown(this, user, settings, level)) {
87 | if (((SkillShot) skill).cast(user, level)) {
88 | Cooldowns.start(this, user);
89 | }
90 | }
91 | }
92 |
93 | static final String SAVE_FOLDER = "enchant/skill/";
94 |
95 | @Override
96 | public String getSaveFolder() {
97 | return SAVE_FOLDER;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/util/LoreReader.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.util;
2 |
3 | import com.sucy.enchant.EnchantmentAPI;
4 | import com.sucy.enchant.api.CustomEnchantment;
5 | import org.bukkit.ChatColor;
6 |
7 | import java.util.Objects;
8 |
9 | /**
10 | * EnchantmentAPI © 2017
11 | * com.sucy.enchant.util.LoreReader
12 | */
13 | public class LoreReader {
14 |
15 | /**
16 | * Parses an enchantment name from a bit of text, assuming the format
17 | * is "{color}{enchantment} {level}"
18 | *
19 | * @param text text to parse from
20 | * @return enchantment name
21 | */
22 | public static String parseEnchantmentName(final String text) {
23 | Objects.requireNonNull(text, "Text cannot be null");
24 | if (!text.startsWith(ChatColor.GRAY.toString())) return "";
25 |
26 | final String noColor = text.substring(2);
27 | final int index = noColor.lastIndexOf(' ');
28 | final int level = RomanNumerals.fromNumerals(noColor.substring(index + 1));
29 | return level > 0 ? noColor.substring(0, index)
30 | : noColor;
31 | }
32 |
33 | /**
34 | * Parses a level from a lore line. This expects valid roman numerals to be at the end of the line
35 | * with no spaces after it, matching the format for enchantments.
36 | *
37 | * @param text text to parse the level from
38 | * @return parsed level or 1 if not found
39 | */
40 | public static int parseEnchantmentLevel(final String text) {
41 | Objects.requireNonNull(text, "Text cannot be null");
42 |
43 | final int index = text.lastIndexOf(' ');
44 | final int level = RomanNumerals.fromNumerals(text.substring(index + 1));
45 | return level > 0 ? level : 1;
46 | }
47 |
48 | /**
49 | * Formats an enchantment name for appending to an item's lore.
50 | *
51 | * @param enchantment enchantment name
52 | * @param level level of the enchantment
53 | * @return lore string for the enchantment
54 | */
55 | public static String formatEnchantment(final CustomEnchantment enchantment, final int level) {
56 | return ChatColor.GRAY + enchantment.getName() + (enchantment.getMaxLevel() > 1 ? " " + RomanNumerals.toNumerals(level) : "");
57 | }
58 |
59 | /**
60 | * Checks whether or not the lore line is the line for the given enchantment
61 | *
62 | * @param line line to check
63 | * @return true if the line matches, false otherwise
64 | */
65 | public static boolean isEnchantment(final String line) {
66 | return EnchantmentAPI.isRegistered(parseEnchantmentName(line));
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/util/RomanNumerals.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.util;
2 |
3 | import com.google.common.collect.ImmutableMap;
4 | import org.apache.commons.lang.Validate;
5 |
6 | import java.util.Arrays;
7 | import java.util.Map;
8 |
9 | /**
10 | * EnchantmentAPI © 2017
11 | * com.sucy.enchant.util.RomanNumerals
12 | */
13 | public class RomanNumerals {
14 |
15 | /**
16 | * Supported roman numeral characters
17 | */
18 | private enum RomanNumber {
19 | //It is important that the order is in reverse
20 | M(1000), CM(900), D(500), CD(400), C(100), XC(90), L(50), XL(40), X(10), IX(9), V(5), IV(4), I(1);
21 |
22 | private final int value;
23 |
24 | RomanNumber(final int value) {
25 | this.value = value;
26 | }
27 |
28 | public int getValue() {
29 | return value;
30 | }
31 | }
32 |
33 | /**
34 | * Gets the Roman Numeral string representing the given value
35 | *
36 | * @param value value to be converted
37 | * @return Roman Numeral String
38 | */
39 | public static String toNumerals(int value) {
40 | Validate.isTrue(value > 0, "Roman numbers can't express zero or negative numbers");
41 | final StringBuilder builder = new StringBuilder();
42 | for (final RomanNumber romanNumber : RomanNumber.values()) {
43 | while (value >= romanNumber.getValue()) {
44 | value -= romanNumber.getValue();
45 | builder.append(romanNumber.name());
46 | }
47 | }
48 | return builder.toString();
49 | }
50 |
51 | /**
52 | * Parses a Roman Numeral string into an integer
53 | *
54 | * @param romanNumeral Roman Numeral string to parse
55 | * @return integer value (0 if invalid string)
56 | */
57 | public static int fromNumerals(final String romanNumeral) {
58 | if (romanNumeral == null || romanNumeral.isEmpty()) return 0;
59 |
60 | int result = 0;
61 | int next = getNumeralValue(romanNumeral.charAt(0));
62 | for (int i = 1; i < romanNumeral.length(); i++) {
63 | final int value = next;
64 | next = getNumeralValue(romanNumeral.charAt(i));
65 | // Invalid characters hint that it isn't a roman numeral string
66 | if (next == 0) return 0;
67 | result += next > value ? -value : value;
68 | }
69 | return result + next;
70 | }
71 |
72 | /**
73 | * Gets the value of the given Roman Numeral character
74 | *
75 | * @param numeral Roman Numeral character
76 | * @return value of the character
77 | */
78 | private static int getNumeralValue(char numeral) {
79 | return CHAR_TO_NUMBER.getOrDefault(numeral, 0);
80 | }
81 |
82 | private static final Map CHAR_TO_NUMBER = Arrays.stream(RomanNumber.values())
83 | .filter(num -> num.name().length() == 1)
84 | .collect(ImmutableMap.toImmutableMap(num -> num.name().charAt(0), RomanNumber::getValue));
85 | }
86 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/util/Utils.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.util;
2 |
3 | import org.bukkit.Material;
4 | import org.bukkit.inventory.ItemStack;
5 |
6 | /**
7 | * EnchantmentAPI © 2017
8 | * com.sucy.enchant.util.Utils
9 | */
10 | public class Utils {
11 |
12 | /**
13 | * @param item item to check
14 | * @return true if isPresent, false otherwise
15 | */
16 | public static boolean isPresent(final ItemStack item) {
17 | return item != null && item.getType() != Material.AIR;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/vanilla/Vanilla.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.vanilla;
2 |
3 | import com.sucy.enchant.EnchantmentAPI;
4 | import com.sucy.enchant.api.CustomEnchantment;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 | import java.util.Optional;
9 |
10 | /**
11 | * EnchantmentAPI © 2017
12 | * com.sucy.enchant.vanilla.Vanilla
13 | */
14 | public class Vanilla {
15 |
16 | public static final Optional getEnchantment(final String name) {
17 | final String conversion = ENCHANTS.getOrDefault(name.toLowerCase(), name).replace(" ", "_");
18 | return Optional.ofNullable(EnchantmentAPI.getEnchantment(conversion));
19 | }
20 |
21 | private static final Map ENCHANTS = new HashMap() {{
22 | put("knockback", "KNOCKBACK");
23 | put("looting", "LOOT_BONUS_MOBS");
24 | put("sharpness", "DAMAGE_ALL");
25 | put("smite", "DAMAGE_UNDEAD");
26 | put("bane of arthropods", "DAMAGE_ARTHROPODS");
27 | put("fire aspect", "FIRE_ASPECT");
28 | put("infinity", "ARROW_INFINITE");
29 | put("flame", "ARROW_FIRE");
30 | put("punch", "ARROW_KNOCKBACK");
31 | put("power", "ARROW_DAMAGE");
32 | put("respiration", "OXYGEN");
33 | put("aqua affinity", "WATER_WORKER");
34 | put("feather falling", "PROTECTION_FALL");
35 | put("thorns", "THORNS");
36 | put("protection", "PROTECTION_ENVIRONMENTAL");
37 | put("fire protection", "PROTECTION_FIRE");
38 | put("blast protection", "PROTECTION_EXPLOSIONS");
39 | put("projectile protection", "PROTECTION_PROJECTILE");
40 | put("efficiency", "DIG_SPEED");
41 | put("unbreaking", "DURABILITY");
42 | put("fortune", "LOOT_BONUS_BLOCKS");
43 | put("silk touch", "SILK_TOUCH");
44 | }};
45 | }
46 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/vanilla/VanillaData.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.vanilla;
2 |
3 | import com.sucy.enchant.api.ItemSet;
4 | import org.bukkit.enchantments.Enchantment;
5 |
6 | import static com.sucy.enchant.api.CustomEnchantment.DEFAULT_GROUP;
7 | import static com.sucy.enchant.api.ItemSet.ALL;
8 | import static com.sucy.enchant.api.ItemSet.ARMOR;
9 | import static com.sucy.enchant.api.ItemSet.AXES;
10 | import static com.sucy.enchant.api.ItemSet.BOOTS;
11 | import static com.sucy.enchant.api.ItemSet.BOWS;
12 | import static com.sucy.enchant.api.ItemSet.CHESTPLATES;
13 | import static com.sucy.enchant.api.ItemSet.DURABILITY_ALL;
14 | import static com.sucy.enchant.api.ItemSet.DURABILITY_SECONDARY;
15 | import static com.sucy.enchant.api.ItemSet.FISHING;
16 | import static com.sucy.enchant.api.ItemSet.HELMETS;
17 | import static com.sucy.enchant.api.ItemSet.NONE;
18 | import static com.sucy.enchant.api.ItemSet.SHEARS;
19 | import static com.sucy.enchant.api.ItemSet.SWORDS;
20 | import static com.sucy.enchant.api.ItemSet.TOOLS;
21 | import static com.sucy.enchant.api.ItemSet.TRIDENT;
22 | import static com.sucy.enchant.api.ItemSet.WEAPONS;
23 |
24 | /**
25 | * EnchantmentAPI © 2017
26 | * com.sucy.enchant.vanilla.VanillaData
27 | */
28 | public enum VanillaData {
29 |
30 | // ---- Armor ---- //
31 | BINDING_CURSE(ALL, 1, 1, 25, 0, 50, 8, false),
32 | DEPTH_STRIDER(BOOTS, NONE, 3, 2, 10, 10, 5, 4, true, "stride"),
33 | FROST_WALKER(BOOTS, NONE, 2, 2, 10, 10, 5, 4, false, "stride"),
34 | OXYGEN(HELMETS, 3, 2, 10, 10, 20, 4, true),
35 | PROTECTION_ENVIRONMENTAL(ARMOR, NONE, 4, 10, 1, 11, 0, 1, true, "protection"),
36 | PROTECTION_EXPLOSIONS(ARMOR, NONE, 4, 2, 5, 8, 0, 4, true, "protection"),
37 | PROTECTION_FALL(BOOTS, NONE, 4, 5, 5, 6, 0, 2, true, "protection"),
38 | PROTECTION_FIRE(ARMOR, NONE, 4, 5, 10, 8, 0, 2, true, "protection"),
39 | PROTECTION_PROJECTILE(ARMOR, NONE, 4, 5, 3, 6, 0, 2, true, "protection"),
40 | THORNS(CHESTPLATES, ARMOR, 3, 1, -10, 20, 30, 8, true, DEFAULT_GROUP),
41 | WATER_WORKER(HELMETS, 1, 2, 1, 0, 40, 4, true),
42 |
43 | // ---- Swords ---- //
44 | DAMAGE_ALL(WEAPONS, AXES, 5, 10, 1, 11, 9, 1, true, "damage"),
45 | DAMAGE_ARTHROPODS(WEAPONS, AXES, 5, 5, 5, 8, 12, 2, true, "damage"),
46 | DAMAGE_UNDEAD(WEAPONS, AXES, 5, 5, 5, 8, 12, 2, true, "damage"),
47 | FIRE_ASPECT(SWORDS, 2, 2, 10, 20, 30, 4, true),
48 | KNOCKBACK(SWORDS, 2, 5, 5, 20, 30, 2, true),
49 | LOOT_BONUS_MOBS(SWORDS, 3, 2, 15, 9, 41, 4, true),
50 | SWEEPING_EDGE(SWORDS, 3, 1, 5, 9, 6, 4, true),
51 |
52 | // --- Tools --- //
53 | DIG_SPEED(TOOLS, SHEARS, 5, 10, 1, 10, 40, 1, true, DEFAULT_GROUP),
54 | LOOT_BONUS_BLOCKS(TOOLS, NONE, 3, 2, 15, 9, 41, 4, true, "blocks"),
55 | SILK_TOUCH(TOOLS, NONE, 1, 1, 15, 0, 50, 8, true, "blocks"),
56 |
57 | // ---- Bows ---- //
58 | ARROW_DAMAGE(BOWS, 5, 10, 1, 10, 5, 1, true),
59 | ARROW_FIRE(BOWS, 1, 2, 20, 0, 50, 4, true),
60 | ARROW_INFINITE(BOWS, NONE, 1, 1, 20, 0, 50, 8, true, "infinite"),
61 | ARROW_KNOCKBACK(BOWS, 2, 2, 12, 20, 5, 4, true),
62 |
63 | // ---- Fishing ---- //
64 | LUCK(FISHING, 3, 2, 15, 9, 41, 4, true),
65 | LURE(FISHING, 3, 2, 15, 9, 41, 4, true),
66 |
67 | // ---- Trident ---- //
68 | LOYALTY(TRIDENT, 3, 5, 5, 7, 43, 2, true),
69 | IMPALING(TRIDENT, 5, 2, 1, 8, 12, 4, true),
70 | RIPTIDE(TRIDENT, 3, 2, 10, 7, 43, 4, true),
71 | CHANNELING(TRIDENT, 1, 1, 25, 0, 50, 8, true),
72 |
73 | // ---- All ---- //
74 | DURABILITY(ItemSet.DURABILITY, DURABILITY_SECONDARY, 3, 5, 5, 8, 42, 2, true, DEFAULT_GROUP),
75 | MENDING(DURABILITY_ALL, NONE, 1, 2, 0, 25, 25, 4, false, "infinite"),
76 | VANISHING_CURSE(ALL, 1, 1, 25, 0, 50, 8, false);
77 |
78 | private VanillaEnchantment enchantment;
79 |
80 | public VanillaEnchantment getEnchantment() {
81 | return enchantment;
82 | }
83 |
84 | public boolean doesExist() {
85 | return enchantment != null;
86 | }
87 |
88 | VanillaData(
89 | final ItemSet itemSet,
90 | final int maxLevel,
91 | final int weight,
92 | final int minEnchantLevel,
93 | final int enchantLevelScale,
94 | final int maxBuffer,
95 | final int costPerLevel,
96 | final boolean tableEnabled) {
97 | this(itemSet, NONE, maxLevel, weight, minEnchantLevel, enchantLevelScale, maxBuffer, costPerLevel, tableEnabled, DEFAULT_GROUP);
98 | }
99 |
100 | VanillaData(
101 | final ItemSet itemSet,
102 | final ItemSet secondary,
103 | final int maxLevel,
104 | final int weight,
105 | final int minEnchantLevel,
106 | final int enchantLevelScale,
107 | final int maxBuffer,
108 | final int costPerLevel,
109 | final boolean tableEnabled,
110 | final String group) {
111 |
112 | final Enchantment enchant = Enchantment.getByName(name());
113 | if (enchant == null) return;
114 |
115 | enchantment = new VanillaEnchantment(enchant);
116 | enchantment.addNaturalItems(itemSet.getItems());
117 | enchantment.addAnvilItems(secondary.getItems());
118 | enchantment.setMaxLevel(maxLevel);
119 | enchantment.setWeight(weight);
120 | enchantment.setMinEnchantingLevel(minEnchantLevel);
121 | enchantment.setEnchantLevelScaleFactor(enchantLevelScale);
122 | enchantment.setEnchantLevelBuffer(maxBuffer);
123 | enchantment.setCombineCostPerLevel(costPerLevel);
124 | enchantment.setTableEnabled(tableEnabled);
125 | enchantment.setGroup(group);
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/com/sucy/enchant/vanilla/VanillaEnchantment.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.vanilla;
2 |
3 | import com.sucy.enchant.api.CustomEnchantment;
4 | import com.sucy.enchant.data.Permission;
5 | import org.bukkit.Material;
6 | import org.bukkit.enchantments.Enchantment;
7 | import org.bukkit.inventory.ItemStack;
8 | import org.bukkit.inventory.meta.EnchantmentStorageMeta;
9 | import org.bukkit.permissions.Permissible;
10 |
11 | import java.util.Map;
12 |
13 | /**
14 | * EnchantmentAPI © 2017
15 | * com.sucy.enchant.vanilla.VanillaEnchantment
16 | */
17 | public class VanillaEnchantment extends CustomEnchantment {
18 |
19 | private final Enchantment enchantment;
20 |
21 | VanillaEnchantment(final Enchantment vanilla) {
22 | super(vanilla.getName(), vanilla.getName() + " vanilla enchantment");
23 | this.enchantment = vanilla;
24 | }
25 |
26 | public Enchantment getEnchantment() {
27 | return enchantment;
28 | }
29 |
30 | @Override
31 | public ItemStack addToItem(final ItemStack item, final int level) {
32 | if (item.getType() == Material.BOOK) {
33 | item.setType(Material.ENCHANTED_BOOK);
34 | }
35 | if (item.getType() == Material.ENCHANTED_BOOK) {
36 | final EnchantmentStorageMeta meta = (EnchantmentStorageMeta)item.getItemMeta();
37 | meta.addStoredEnchant(enchantment, level, true);
38 | item.setItemMeta(meta);
39 | return item;
40 | }
41 | item.addUnsafeEnchantment(enchantment, level);
42 | return item;
43 | }
44 |
45 | @Override
46 | public void addToEnchantment(final Map enchantments, final ItemStack result, final int level) {
47 | enchantments.put(enchantment, level);
48 | }
49 |
50 | @Override
51 | public ItemStack removeFromItem(final ItemStack item) {
52 | if (item.getType() == Material.ENCHANTED_BOOK) {
53 | final EnchantmentStorageMeta meta = (EnchantmentStorageMeta)item.getItemMeta();
54 | meta.removeStoredEnchant(enchantment);
55 | item.setItemMeta(meta);
56 | }
57 | item.removeEnchantment(enchantment);
58 | return item;
59 | }
60 |
61 | @Override
62 | public boolean hasPermission(final Permissible permissible) {
63 | return permissible == null
64 | || permissible.hasPermission(Permission.ENCHANT_VANILLA)
65 | || permissible.hasPermission(getPermission());
66 | }
67 |
68 | @Override
69 | public String getSaveFolder() {
70 | return "enchant/vanilla/";
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/tst/com/sucy/enchant/TestUtils.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant;
2 |
3 | import com.sucy.enchant.api.CustomEnchantment;
4 | import com.sucy.enchant.vanilla.VanillaEnchantment;
5 | import org.bukkit.enchantments.Enchantment;
6 |
7 | import java.lang.reflect.Field;
8 | import java.lang.reflect.Modifier;
9 | import java.util.Map;
10 |
11 | import static org.mockito.Matchers.anyVararg;
12 | import static org.mockito.Mockito.doCallRealMethod;
13 | import static org.mockito.Mockito.mock;
14 | import static org.mockito.Mockito.when;
15 |
16 | /**
17 | * EnchantmentAPI © 2017
18 | * com.sucy.enchant.TestUtils
19 | */
20 | public class TestUtils {
21 |
22 | public static final EnchantmentAPI API = wrapAPI();
23 |
24 | public static Enchantment createVanillaEnchantment(final String name) {
25 | final VanillaEnchantment enchant = mock(VanillaEnchantment.class);
26 | when(enchant.getName()).thenReturn(name);
27 | API.register(enchant);
28 |
29 | final Enchantment enchantment = mock(Enchantment.class);
30 | when(enchantment.getName()).thenReturn(name);
31 | return enchantment;
32 | }
33 |
34 | public static CustomEnchantment createEnchantment(final String name) {
35 | final CustomEnchantment enchant = mock(CustomEnchantment.class);
36 | when(enchant.getName()).thenReturn(name);
37 | API.register(enchant);
38 | return enchant;
39 | }
40 |
41 | public static void tearDown() throws Exception {
42 | getRegistry().clear();
43 | }
44 |
45 | @SuppressWarnings("unchecked")
46 | public static Map getRegistry() throws Exception {
47 | return ((Map)getField(EnchantmentAPI.class, "ENCHANTMENTS").get(null));
48 | }
49 |
50 | public static void register(final CustomEnchantment enchantment) throws Exception {
51 | getRegistry().put(enchantment.getName(), enchantment);
52 | }
53 |
54 | public static void set(final Class> clazz, final String fieldName, final Object value) throws Exception {
55 | getField(clazz, fieldName).set(null, value);
56 | }
57 |
58 | public static Field getField(final Class clazz, final String name) throws Exception {
59 | final Field field = clazz.getDeclaredField(name);
60 | field.setAccessible(true);
61 |
62 | final Field modifier = Field.class.getDeclaredField("modifiers");
63 | modifier.setAccessible(true);
64 | modifier.setInt(field, field.getModifiers() & ~Modifier.FINAL);
65 |
66 | return field;
67 | }
68 |
69 | private static EnchantmentAPI wrapAPI() {
70 | final EnchantmentAPI api = mock(EnchantmentAPI.class);
71 | doCallRealMethod().when(api).register(anyVararg());
72 | return api;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/tst/com/sucy/enchant/api/CooldownsTest.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | import com.sucy.enchant.TestUtils;
4 | import org.bukkit.entity.LivingEntity;
5 | import org.junit.After;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 | import org.mockito.Mock;
10 | import org.mockito.runners.MockitoJUnitRunner;
11 |
12 | import java.time.Clock;
13 | import java.util.UUID;
14 |
15 | import static org.junit.Assert.assertEquals;
16 | import static org.junit.Assert.assertFalse;
17 | import static org.junit.Assert.assertTrue;
18 | import static org.mockito.Mockito.verify;
19 | import static org.mockito.Mockito.when;
20 |
21 | /**
22 | * EnchantmentAPI © 2017
23 | * com.sucy.enchant.api.CooldownsTest
24 | */
25 | @RunWith(MockitoJUnitRunner.class)
26 | public class CooldownsTest {
27 |
28 | private static final String ENCHANT_NAME = "enchantName";
29 | private static final UUID USER_ID = UUID.randomUUID();
30 |
31 | @Mock
32 | private Clock clock;
33 | @Mock
34 | private CustomEnchantment enchantment;
35 | @Mock
36 | private LivingEntity user;
37 | @Mock
38 | private Settings settings;
39 |
40 | @Before
41 | public void setUp() throws Exception {
42 | TestUtils.set(Cooldowns.class, "clock", clock);
43 |
44 | when(enchantment.getName()).thenReturn(ENCHANT_NAME);
45 | when(user.getUniqueId()).thenReturn(USER_ID);
46 | }
47 |
48 | @After
49 | public void tearDown() throws Exception {
50 | }
51 |
52 | @Test
53 | public void configure() throws Exception {
54 | Cooldowns.configure(settings, 12, 23);
55 |
56 | verify(settings).set("cooldown", 12, 23);
57 | }
58 |
59 | @Test
60 | public void secondsLeft() throws Exception {
61 | when(clock.millis()).thenReturn(100L, 1100L);
62 | when(settings.get("cooldown", 2)).thenReturn(15.0);
63 |
64 | Cooldowns.start(enchantment, user);
65 | final int result = Cooldowns.secondsLeft(enchantment, user, settings, 2);
66 | assertEquals(result, 14);
67 | }
68 |
69 | @Test
70 | public void secondsLeft_roundedUp() throws Exception {
71 | when(clock.millis()).thenReturn(100L, 1000L);
72 | when(settings.get("cooldown", 2)).thenReturn(1.0);
73 |
74 | Cooldowns.start(enchantment, user);
75 | final int result = Cooldowns.secondsLeft(enchantment, user, settings, 2);
76 | assertEquals(result, 1);
77 | }
78 |
79 | @Test
80 | public void onCooldown() throws Exception {
81 | when(clock.millis()).thenReturn(100L, 1100L);
82 | when(settings.get("cooldown", 2)).thenReturn(15.0);
83 |
84 | Cooldowns.start(enchantment, user);
85 | final boolean result = Cooldowns.onCooldown(enchantment, user, settings, 2);
86 | assertTrue(result);
87 | }
88 |
89 | @Test
90 | public void onCooldown_exact() throws Exception {
91 | when(clock.millis()).thenReturn(100L, 15100L);
92 | when(settings.get("cooldown", 2)).thenReturn(15.0);
93 |
94 | Cooldowns.start(enchantment, user);
95 | final boolean result = Cooldowns.onCooldown(enchantment, user, settings, 2);
96 | assertFalse(result);
97 | }
98 |
99 | @Test
100 | public void onCooldown_offCooldown() throws Exception {
101 | when(clock.millis()).thenReturn(100L, 20100L);
102 | when(settings.get("cooldown", 2)).thenReturn(15.0);
103 |
104 | Cooldowns.start(enchantment, user);
105 | final boolean result = Cooldowns.onCooldown(enchantment, user, settings, 2);
106 | assertFalse(result);
107 | }
108 |
109 | @Test
110 | public void reduce() throws Exception {
111 | when(clock.millis()).thenReturn(100L, 10100L);
112 | when(settings.get("cooldown", 2)).thenReturn(15.0);
113 |
114 | Cooldowns.start(enchantment, user);
115 | Cooldowns.reduce(enchantment, user, 6000);
116 | final boolean result = Cooldowns.onCooldown(enchantment, user, settings, 2);
117 | assertFalse(result);
118 | }
119 |
120 | @Test
121 | public void reduceNegative() throws Exception {
122 | when(clock.millis()).thenReturn(100L, 20100L);
123 | when(settings.get("cooldown", 2)).thenReturn(15.0);
124 |
125 | Cooldowns.start(enchantment, user);
126 | Cooldowns.reduce(enchantment, user, -6000);
127 | final boolean result = Cooldowns.onCooldown(enchantment, user, settings, 2);
128 | assertTrue(result);
129 | }
130 | }
--------------------------------------------------------------------------------
/tst/com/sucy/enchant/api/CustomEnchantmentTest.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import com.sucy.enchant.TestUtils;
5 | import org.bukkit.ChatColor;
6 | import org.bukkit.Material;
7 | import org.bukkit.inventory.ItemStack;
8 | import org.bukkit.inventory.meta.ItemMeta;
9 | import org.junit.After;
10 | import org.junit.Before;
11 | import org.junit.Test;
12 | import org.junit.runner.RunWith;
13 | import org.mockito.Mock;
14 | import org.mockito.runners.MockitoJUnitRunner;
15 |
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | import static org.junit.Assert.assertEquals;
20 | import static org.junit.Assert.assertFalse;
21 | import static org.junit.Assert.assertTrue;
22 | import static org.mockito.Mockito.verify;
23 | import static org.mockito.Mockito.when;
24 |
25 | /**
26 | * EnchantmentAPI © 2017
27 | * com.sucy.enchant.api.CustomEnchantmentTest
28 | */
29 | @RunWith(MockitoJUnitRunner.class)
30 | public class CustomEnchantmentTest {
31 |
32 | @Mock
33 | private ItemMeta meta;
34 | @Mock
35 | private ItemStack item;
36 |
37 | private List lore;
38 |
39 | private CustomEnchantment subject;
40 |
41 | @Before
42 | public void setUp() throws Exception {
43 | subject = new TestEnchant();
44 | TestUtils.register(subject);
45 |
46 | lore = new ArrayList<>();
47 | when(item.getType()).thenReturn(Material.DIAMOND_SWORD);
48 | when(item.getItemMeta()).thenReturn(meta);
49 | when(meta.getLore()).thenReturn(lore);
50 | when(meta.hasLore()).thenReturn(true);
51 | }
52 |
53 | @After
54 | public void tearDown() throws Exception {
55 | }
56 |
57 | @Test
58 | public void computeLevel() throws Exception {
59 | // Use data from ARROW_DAMAGE to have a solid baseline to compare against
60 | subject.setMaxLevel(10, 5);
61 | subject.setMinEnchantingLevel(1);
62 | subject.setEnchantLevelScaleFactor(10);
63 | subject.setEnchantLevelBuffer(5);
64 |
65 | assertEquals(0, subject.computeLevel(0));
66 | assertEquals(1, subject.computeLevel(1));
67 | assertEquals(1, subject.computeLevel(10));
68 | assertEquals(2, subject.computeLevel(11));
69 | assertEquals(2, subject.computeLevel(20));
70 | assertEquals(3, subject.computeLevel(21));
71 | assertEquals(3, subject.computeLevel(30));
72 | assertEquals(4, subject.computeLevel(31));
73 | assertEquals(4, subject.computeLevel(40));
74 | assertEquals(5, subject.computeLevel(41));
75 | assertEquals(5, subject.computeLevel(50));
76 | assertEquals(5, subject.computeLevel(56));
77 | assertEquals(0, subject.computeLevel(57));
78 | }
79 |
80 | @Test
81 | public void canEnchantOnto() throws Exception {
82 | subject.addNaturalItems(ItemSet.AXES.getItems());
83 |
84 | assertTrue(subject.canEnchantOnto(new ItemStack(Material.DIAMOND_AXE)));
85 | assertTrue(subject.canEnchantOnto(new ItemStack(Material.STONE_AXE)));
86 | assertTrue(subject.canEnchantOnto(new ItemStack(Material.BOOK)));
87 | assertTrue(subject.canEnchantOnto(new ItemStack(Material.ENCHANTED_BOOK)));
88 | assertFalse(subject.canEnchantOnto(new ItemStack(Material.DIAMOND_SWORD)));
89 | assertFalse(subject.canEnchantOnto(new ItemStack(Material.LAPIS_BLOCK)));
90 | assertFalse(subject.canEnchantOnto(new ItemStack(Material.BOW)));
91 | assertFalse(subject.canEnchantOnto(new ItemStack(Material.STONE_PICKAXE)));
92 | }
93 |
94 | @Test
95 | public void conflictsWith_default() throws Exception {
96 | final TestEnchant other = new TestEnchant();
97 | assertFalse(subject.conflictsWith(other, true));
98 | assertFalse(subject.conflictsWith(other, false));
99 | }
100 |
101 | @Test
102 | public void conflictsWith_same() throws Exception {
103 | assertTrue(subject.conflictsWith(subject, true));
104 | assertFalse(subject.conflictsWith(subject, false));
105 | }
106 |
107 | @Test
108 | public void conflictsWith_differentGroups() throws Exception {
109 | final TestEnchant other = new TestEnchant();
110 | subject.setGroup("subject");
111 | other.setGroup("other");
112 |
113 | assertFalse(subject.conflictsWith(other, true));
114 | assertFalse(subject.conflictsWith(other, false));
115 | }
116 |
117 | @Test
118 | public void conflictsWith_sameGroups() throws Exception {
119 | final TestEnchant other = new TestEnchant();
120 | subject.setGroup("same");
121 | other.setGroup("same");
122 |
123 | assertTrue(subject.conflictsWith(other, true));
124 | assertTrue(subject.conflictsWith(other, false));
125 | }
126 |
127 | @Test
128 | public void conflictsWith_list() throws Exception {
129 | assertTrue(subject.conflictsWith(ImmutableList.of(subject), true));
130 | assertFalse(subject.conflictsWith(ImmutableList.of(), true));
131 | }
132 |
133 | @Test
134 | public void conflictsWith_array() throws Exception {
135 | assertTrue(subject.conflictsWith(true, subject));
136 | assertFalse(subject.conflictsWith(true));
137 | }
138 |
139 | @Test
140 | public void addToItem_happyCase() throws Exception {
141 | subject.addToItem(item, 2);
142 | assertEquals(1, lore.size());
143 | assertEquals(ChatColor.GRAY + "test II", lore.get(0));
144 | verify(meta).setLore(lore);
145 | verify(item).setItemMeta(meta);
146 | }
147 |
148 | @Test
149 | public void addToItem_toTop() throws Exception {
150 | lore.add("Line");
151 | lore.add("Another Line");
152 | subject.addToItem(item, 2);
153 | assertEquals(3, lore.size());
154 | assertEquals(ChatColor.GRAY + "test II", lore.get(0));
155 | verify(meta).setLore(lore);
156 | verify(item).setItemMeta(meta);
157 | }
158 |
159 | @Test
160 | public void addToItem_alreadyExists() throws Exception {
161 | lore.add(ChatColor.GRAY + "test IV");
162 | lore.add("junk");
163 | lore.add("Another Line");
164 | subject.addToItem(item, 2);
165 | assertEquals(3, lore.size());
166 | assertEquals(ChatColor.GRAY + "test II", lore.get(0));
167 | assertEquals("junk", lore.get(1));
168 | assertEquals("Another Line", lore.get(2));
169 | verify(meta).setLore(lore);
170 | verify(item).setItemMeta(meta);
171 | }
172 |
173 | @Test
174 | public void addToItem_noLore() throws Exception {
175 | when(meta.hasLore()).thenReturn(false);
176 | lore.add(ChatColor.GRAY + "test IV");
177 | lore.add("junk");
178 | lore.add("Another Line");
179 | subject.addToItem(item, 2);
180 | verify(meta).setLore(ImmutableList.of(ChatColor.GRAY + "test II"));
181 | verify(item).setItemMeta(meta);
182 | }
183 |
184 | @Test
185 | public void addToEnchantment() throws Exception {
186 | }
187 |
188 | @Test
189 | public void removeFromItem() throws Exception {
190 | }
191 |
192 | @Test
193 | public void hasPermission() throws Exception {
194 | }
195 |
196 | @Test
197 | public void getPermission() throws Exception {
198 | }
199 |
200 | @Test
201 | public void applyOnHit() throws Exception {
202 | }
203 |
204 | @Test
205 | public void applyDefense() throws Exception {
206 | }
207 |
208 | @Test
209 | public void applyBreak() throws Exception {
210 | }
211 |
212 | @Test
213 | public void applyEquip() throws Exception {
214 | }
215 |
216 | @Test
217 | public void applyUnequip() throws Exception {
218 | }
219 |
220 | @Test
221 | public void applyInteractBlock() throws Exception {
222 | }
223 |
224 | @Test
225 | public void applyInteractEntity() throws Exception {
226 | }
227 |
228 | @Test
229 | public void applyProjectile() throws Exception {
230 | }
231 |
232 | @Test
233 | public void equals() throws Exception {
234 | }
235 |
236 | @Test
237 | public void compareTo() throws Exception {
238 | }
239 |
240 | @Test
241 | public void getSaveFolder() throws Exception {
242 | }
243 |
244 | @Test
245 | public void save() throws Exception {
246 | }
247 |
248 | @Test
249 | public void load() throws Exception {
250 | }
251 |
252 | private static class TestEnchant extends CustomEnchantment {
253 | TestEnchant() { super("test", "description"); }
254 | }
255 | }
--------------------------------------------------------------------------------
/tst/com/sucy/enchant/api/EnchantmentsTest.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | import com.google.common.collect.ImmutableMap;
4 | import com.sucy.enchant.EnchantmentAPI;
5 | import com.sucy.enchant.TestUtils;
6 | import org.bukkit.enchantments.Enchantment;
7 | import org.bukkit.inventory.ItemStack;
8 | import org.junit.After;
9 | import org.junit.Before;
10 | import org.junit.Test;
11 |
12 | import java.util.Map;
13 | import java.util.Random;
14 |
15 | import static org.mockito.Mockito.mock;
16 | import static org.mockito.Mockito.when;
17 |
18 | /**
19 | * EnchantmentAPI © 2017
20 | * com.sucy.enchant.api.EnchantmentsTest
21 | */
22 | public class EnchantmentsTest {
23 |
24 | private static final Map ENCHANTS = ImmutableMap.of(
25 | TestUtils.createVanillaEnchantment("TEST"), 1,
26 | TestUtils.createVanillaEnchantment("SAMPLE"), 3);
27 |
28 | private ItemStack item;
29 |
30 | @Before
31 | public void setUp() throws Exception {
32 | item = mock(ItemStack.class);
33 | }
34 |
35 | @After
36 | public void tearDown() throws Exception {
37 | TestUtils.tearDown();
38 | }
39 |
40 | @Test
41 | public void getCustomEnchantments() throws Exception {
42 |
43 | }
44 |
45 | @Test
46 | public void getAllEnchantments() throws Exception {
47 | when(item.getEnchantments()).thenReturn(ENCHANTS);
48 | final Map result = Enchantments.getAllEnchantments(item);
49 | final Map expected = ImmutableMap.of(
50 | EnchantmentAPI.getEnchantment("TEST"), 1,
51 | EnchantmentAPI.getEnchantment("SAMPLE"), 3);
52 | }
53 |
54 | @Test
55 | public void hasCustomEnchantment() throws Exception {
56 | }
57 |
58 | @Test
59 | public void removeAllEnchantments() throws Exception {
60 | }
61 |
62 | @Test
63 | public void removeCustomEnchantments() throws Exception {
64 | }
65 |
66 | @Test
67 | public void roll() {
68 | final Random random = new Random();
69 | final int runs = 1000000;
70 | final double increment = 100.0 / runs;
71 | double chance = 0;
72 | for (int i = 0; i < 1000000; i++) {
73 | if (random.nextInt(5) == 0 || random.nextInt(4) == 0) {
74 | chance += increment;
75 | }
76 | }
77 | System.out.println("Chance: " + chance);
78 | }
79 | }
--------------------------------------------------------------------------------
/tst/com/sucy/enchant/api/ItemSetTest.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.api;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | /**
8 | * EnchantmentAPI © 2018
9 | * com.sucy.enchant.api.ItemSetTest
10 | */
11 | public class ItemSetTest {
12 |
13 | @Test
14 | public void getItems() {
15 | assertEquals(1, ItemSet.TRIDENT.getItems().length);
16 | }
17 | }
--------------------------------------------------------------------------------
/tst/com/sucy/enchant/cmd/CmdGraphTest.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.cmd;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import com.rit.sucy.config.parse.DataSection;
5 | import com.sucy.enchant.TestUtils;
6 | import com.sucy.enchant.data.ConfigKey;
7 | import com.sucy.enchant.data.Configuration;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 |
11 | import java.util.HashSet;
12 | import java.util.List;
13 |
14 | import static org.junit.Assert.assertEquals;
15 | import static org.mockito.Mockito.mock;
16 | import static org.mockito.Mockito.when;
17 |
18 | /**
19 | * EnchantmentAPI © 2017
20 | * com.sucy.enchant.cmd.CmdGraphTest
21 | */
22 | public class CmdGraphTest {
23 |
24 | private static final List WEIGHTS = ImmutableList.of(2.0, 2.0, 4.0, 4.0, 8.0);
25 | private static final double TOTAL = WEIGHTS.stream().mapToDouble(Double::doubleValue).sum() + 4;
26 |
27 | private CmdGraph subject;
28 | private DataSection data;
29 |
30 | @Before
31 | public void setUp() throws Exception {
32 | data = mock(DataSection.class);
33 | when(data.getInt(ConfigKey.MAX_ENCHANTMENTS.getKey())).thenReturn(3);
34 | TestUtils.set(Configuration.class, "data", data);
35 | subject = new CmdGraph();
36 | }
37 |
38 | @Test
39 | public void compute() {
40 | double one = 4.0 / TOTAL;
41 | double two = 1.0/33 + 1.0/15 + 1.0/12;
42 |
43 | assertEquals(one, subject.compute(WEIGHTS, TOTAL, 4, 0, new HashSet<>()), 0.00000001);
44 | assertEquals(two, subject.compute(WEIGHTS, TOTAL, 4, 1, new HashSet<>()), 0.00000001);
45 |
46 | double total = 0;
47 | for (int i = 0; i <= WEIGHTS.size(); i++) {
48 | total += subject.compute(WEIGHTS, TOTAL, 4, i, new HashSet<>());
49 | }
50 | assertEquals(1, total, 0.0000000001);
51 | }
52 | }
--------------------------------------------------------------------------------
/tst/com/sucy/enchant/util/LoreReaderTest.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.util;
2 |
3 | import com.sucy.enchant.TestUtils;
4 | import com.sucy.enchant.api.CustomEnchantment;
5 | import org.bukkit.ChatColor;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.mockito.Mock;
9 | import org.mockito.runners.MockitoJUnitRunner;
10 |
11 | import static org.junit.Assert.assertEquals;
12 | import static org.junit.Assert.assertFalse;
13 | import static org.junit.Assert.assertTrue;
14 | import static org.mockito.Mockito.when;
15 |
16 | /**
17 | * EnchantmentAPI © 2017
18 | * com.sucy.enchant.util.LoreReaderTest
19 | */
20 | @RunWith(MockitoJUnitRunner.class)
21 | public class LoreReaderTest {
22 |
23 | @Mock
24 | private CustomEnchantment enchantment;
25 |
26 | @Test
27 | public void parseEnchantmentName() {
28 | assertEquals("Test", LoreReader.parseEnchantmentName(ChatColor.GRAY + "Test IX"));
29 | assertEquals("With Spaces", LoreReader.parseEnchantmentName(ChatColor.GRAY + "With Spaces MLCD"));
30 | assertEquals("No Numerals Here", LoreReader.parseEnchantmentName(ChatColor.GRAY + "No Numerals Here"));
31 | assertEquals("NoSpace", LoreReader.parseEnchantmentName(ChatColor.GRAY + "NoSpace"));
32 | assertEquals("A Test", LoreReader.parseEnchantmentName(ChatColor.GRAY + "A Test"));
33 | assertEquals(" FrontSpace", LoreReader.parseEnchantmentName(ChatColor.GRAY + " FrontSpace"));
34 | assertEquals("EndSpace ", LoreReader.parseEnchantmentName(ChatColor.GRAY + "EndSpace "));
35 | assertEquals("", LoreReader.parseEnchantmentName("Test IX"));
36 | assertEquals("", LoreReader.parseEnchantmentName(ChatColor.GRAY.toString()));
37 | assertEquals("", LoreReader.parseEnchantmentName(""));
38 | }
39 |
40 | @Test
41 | public void parseEnchantmentLevel() {
42 | assertEquals(5, LoreReader.parseEnchantmentLevel("Test V"));
43 | assertEquals(9, LoreReader.parseEnchantmentLevel("With Spaces IX"));
44 | assertEquals(1, LoreReader.parseEnchantmentLevel("No Numerals"));
45 | assertEquals(1, LoreReader.parseEnchantmentLevel("A Test"));
46 | assertEquals(1, LoreReader.parseEnchantmentLevel(" FrontSpace"));
47 | assertEquals(1, LoreReader.parseEnchantmentLevel("EndSpace "));
48 | assertEquals(1, LoreReader.parseEnchantmentLevel(""));
49 | }
50 |
51 | @Test
52 | public void formatEnchantmentWithLevel() {
53 | when(enchantment.getName()).thenReturn("Test");
54 | when(enchantment.getMaxLevel()).thenReturn(10);
55 | assertEquals(ChatColor.GRAY + "Test IX", LoreReader.formatEnchantment(enchantment, 9));
56 | }
57 |
58 | @Test
59 | public void formatEnchantmentWithoutLevel() {
60 | when(enchantment.getName()).thenReturn("Test");
61 | when(enchantment.getMaxLevel()).thenReturn(1);
62 | assertEquals(ChatColor.GRAY + "Test", LoreReader.formatEnchantment(enchantment, 9));
63 | }
64 |
65 | @Test
66 | public void isEnchantment() {
67 | TestUtils.createEnchantment("Test");
68 | assertTrue(LoreReader.isEnchantment(ChatColor.GRAY + "Test IX"));
69 | assertTrue(LoreReader.isEnchantment(ChatColor.GRAY + "Test"));
70 | assertFalse(LoreReader.isEnchantment("Test IX"));
71 | assertFalse(LoreReader.isEnchantment(ChatColor.GRAY + "Different IX"));
72 | assertFalse(LoreReader.isEnchantment(""));
73 | assertFalse(LoreReader.isEnchantment(""));
74 | }
75 | }
--------------------------------------------------------------------------------
/tst/com/sucy/enchant/util/RomanNumeralsTest.java:
--------------------------------------------------------------------------------
1 | package com.sucy.enchant.util;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | /**
8 | * EnchantmentAPI © 2017
9 | * com.sucy.enchant.util.RomanNumeralsTest
10 | */
11 | public class RomanNumeralsTest {
12 | @Test
13 | public void toNumerals() throws Exception {
14 | assertEquals("I", RomanNumerals.toNumerals(1));
15 | assertEquals("III", RomanNumerals.toNumerals(3));
16 | assertEquals("IV", RomanNumerals.toNumerals(4));
17 | assertEquals("V", RomanNumerals.toNumerals(5));
18 | assertEquals("IX", RomanNumerals.toNumerals(9));
19 | assertEquals("X", RomanNumerals.toNumerals(10));
20 | assertEquals("XXXVII", RomanNumerals.toNumerals(37));
21 | assertEquals("XL", RomanNumerals.toNumerals(40));
22 | assertEquals("L", RomanNumerals.toNumerals(50));
23 | assertEquals("XCII", RomanNumerals.toNumerals(92));
24 | assertEquals("CIV", RomanNumerals.toNumerals(104));
25 | assertEquals("CD", RomanNumerals.toNumerals(400));
26 | assertEquals("DXXXI", RomanNumerals.toNumerals(531));
27 | assertEquals("CM", RomanNumerals.toNumerals(900));
28 | assertEquals("MCCXXXIV", RomanNumerals.toNumerals(1234));
29 |
30 | }
31 |
32 | @Test
33 | public void fromNumerals() throws Exception {
34 | assertEquals(1, RomanNumerals.fromNumerals("I"));
35 | assertEquals(3, RomanNumerals.fromNumerals("III"));
36 | assertEquals(4, RomanNumerals.fromNumerals("IV"));
37 | assertEquals(5, RomanNumerals.fromNumerals("V"));
38 | assertEquals(9, RomanNumerals.fromNumerals("IX"));
39 | assertEquals(10, RomanNumerals.fromNumerals("X"));
40 | assertEquals(37, RomanNumerals.fromNumerals("XXXVII"));
41 | assertEquals(40, RomanNumerals.fromNumerals("XL"));
42 | assertEquals(50, RomanNumerals.fromNumerals("L"));
43 | assertEquals(92, RomanNumerals.fromNumerals("XCII"));
44 | assertEquals(104, RomanNumerals.fromNumerals("CIV"));
45 | assertEquals(400, RomanNumerals.fromNumerals("CD"));
46 | assertEquals(531, RomanNumerals.fromNumerals("DXXXI"));
47 | assertEquals(900, RomanNumerals.fromNumerals("CM"));
48 | assertEquals(1234, RomanNumerals.fromNumerals("MCCXXXIV"));
49 | }
50 | }
--------------------------------------------------------------------------------