26
26
import java .util .Locale ;
27
27
import java .util .Objects ;
28
28
import java .util .function .Function ;
29
+ import java .util .function .UnaryOperator ;
29
30
30
31
import me .moros .bending .api .ability .element .Element ;
32
+ import me .moros .bending .api .util .KeyUtil ;
31
33
import net .kyori .adventure .key .Key ;
32
34
import net .kyori .adventure .key .Keyed ;
33
35
import net .kyori .adventure .text .Component ;
34
36
import net .kyori .adventure .text .TextComponent ;
35
37
import net .kyori .adventure .text .event .ClickEvent ;
36
38
import net .kyori .adventure .translation .Translatable ;
37
- import org .checkerframework .checker .nullness .qual .NonNull ;
38
39
import org .checkerframework .checker .nullness .qual .Nullable ;
39
40
40
41
/**
41
42
* AbilityDescription is immutable and thread-safe.
42
43
* Assume that all collections returning AbilityDescription are also immutable
43
44
*/
44
45
public sealed class AbilityDescription implements Keyed , Translatable permits AbilityDescription .Sequence {
45
- public static final String NAMESPACE = "bending.ability" ;
46
-
47
46
private final Key key ;
48
47
private final String name ;
49
- private final Function <AbilityDescription , ? extends Ability > constructor ;
50
48
private final Element element ;
49
+ private final Component displayName ;
50
+ private final Function <AbilityDescription , ? extends Ability > constructor ;
51
51
private final EnumSet <Activation > activations ;
52
52
private final Collection <String > requiredPermissions ;
53
- private final Component displayName ;
54
53
private final boolean canBind ;
55
54
private final boolean hidden ;
56
55
private final boolean bypassCooldown ;
57
56
private final int hashcode ;
58
57
59
58
private AbilityDescription (Builder builder ) {
59
+ key = builder .key ;
60
60
name = builder .name ;
61
- constructor = builder .constructor ;
62
61
element = builder .element ;
62
+ displayName = Component .text (name , element .color ());
63
+ constructor = builder .constructor ;
63
64
activations = builder .activations ;
64
65
requiredPermissions = List .copyOf (builder .requiredPermissions );
65
66
canBind = builder .canBind && !isActivatedBy (Activation .SEQUENCE );
66
67
hidden = builder .hidden ;
67
68
bypassCooldown = builder .bypassCooldown ;
68
- displayName = Component .text (name , element .color ());
69
- hashcode = Objects .hash (name , element , activations );
70
- key = Key .key (NAMESPACE , name .toLowerCase (Locale .ROOT ));
69
+ hashcode = Objects .hash (key , element , activations );
71
70
}
72
71
72
+ @ Deprecated (forRemoval = true )
73
73
public String name () {
74
74
return name ;
75
75
}
@@ -106,28 +106,32 @@ public Collection<String> permissions() {
106
106
return requiredPermissions ;
107
107
}
108
108
109
+ @ Deprecated (forRemoval = true )
109
110
public Component meta () {
110
- return displayName ().clickEvent (ClickEvent .runCommand ("/bending help " + name ()));
111
+ return displayName ().clickEvent (ClickEvent .runCommand ("/bending help " + key (). asString ()));
111
112
}
112
113
113
114
@ Override
114
- public @ NonNull Key key () {
115
+ public Key key () {
115
116
return key ;
116
117
}
117
118
118
119
@ Override
119
- public @ NonNull String translationKey () {
120
- return NAMESPACE + "." + key ().value ();
120
+ public String translationKey () {
121
+ return key (). namespace () + ".ability ." + key ().value ();
121
122
}
122
123
124
+ @ Deprecated (forRemoval = true )
123
125
public String descriptionKey () {
124
126
return translationKey () + ".description" ;
125
127
}
126
128
129
+ @ Deprecated (forRemoval = true )
127
130
public String instructionsKey () {
128
131
return translationKey () + ".instructions" ;
129
132
}
130
133
134
+ @ Deprecated (forRemoval = true )
131
135
public String deathKey () {
132
136
return translationKey () + ".death" ;
133
137
}
@@ -141,7 +145,7 @@ public boolean equals(Object obj) {
141
145
return false ;
142
146
}
143
147
AbilityDescription other = (AbilityDescription ) obj ;
144
- return name .equals (other .name ) && element == other .element && activations .equals (other .activations );
148
+ return key .equals (other .key ) && element == other .element && activations .equals (other .activations );
145
149
}
146
150
147
151
@ Override
@@ -150,18 +154,29 @@ public int hashCode() {
150
154
}
151
155
152
156
public static <T extends Ability > Builder builder (String name , Function <AbilityDescription , T > constructor ) {
157
+ return builder (KeyUtil .BENDING_NAMESPACE , name , constructor );
158
+ }
159
+
160
+ public static <T extends Ability > Builder builder (String namespace , String name , Function <AbilityDescription , T > constructor ) {
161
+ Objects .requireNonNull (namespace );
153
162
Objects .requireNonNull (name );
154
163
Objects .requireNonNull (constructor );
155
- if (name .isEmpty ()) {
156
- throw new IllegalArgumentException ("Ability name cannot be empty!" );
164
+ if (namespace .isEmpty ()) {
165
+ namespace = KeyUtil .BENDING_NAMESPACE ;
166
+ }
167
+ boolean validName = name .chars ().allMatch (c -> (c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ));
168
+ if (name .isEmpty () || !validName ) {
169
+ throw new IllegalArgumentException ("Name must be an alphabetical non-empty string!" );
157
170
}
158
- return new Builder (name , constructor );
171
+ return new Builder (namespace , name , constructor );
159
172
}
160
173
161
174
/**
162
175
* Immutable and thread-safe representation of a sequence
163
176
*/
164
177
public static final class Sequence extends AbilityDescription {
178
+ public static final int MAX_STEPS = 16 ;
179
+
165
180
private final List <SequenceStep > steps ;
166
181
private Component instructions ;
167
182
@@ -204,11 +219,11 @@ private Component generateInstructions() {
204
219
// Check if the next instruction is to release sneak.
205
220
SequenceStep next = steps .get (i + 1 );
206
221
if (desc .equals (next .ability ()) && next .activation () == Activation .SNEAK_RELEASE ) {
207
- key = Activation . NAMESPACE + " .sneak-tap" ;
222
+ key = "bending.activation .sneak-tap" ;
208
223
i ++;
209
224
}
210
225
}
211
- builder .append (Component . text ( desc .name () )).append (Component .text (" (" ))
226
+ builder .append (desc .displayName ( )).append (Component .text (" (" ))
212
227
.append (Component .translatable (key )).append (Component .text (")" ));
213
228
}
214
229
return builder .build ();
@@ -241,19 +256,21 @@ public boolean matches(List<SequenceStep> otherSteps) {
241
256
* Builder to create {@link AbilityDescription}.
242
257
*/
243
258
public static final class Builder {
259
+ private final Key key ;
244
260
private final String name ;
245
- private final Function <AbilityDescription , ? extends Ability > constructor ;
246
261
private Element element ;
262
+ private final Function <AbilityDescription , ? extends Ability > constructor ;
247
263
private EnumSet <Activation > activations ;
248
264
private Collection <String > requiredPermissions ;
249
265
private boolean canBind = true ;
250
266
private boolean hidden = false ;
251
267
private boolean bypassCooldown = false ;
252
268
253
- private <T extends Ability > Builder (String name , Function <AbilityDescription , T > constructor ) {
269
+ private <T extends Ability > Builder (String namespace , String name , Function <AbilityDescription , T > constructor ) {
270
+ this .key = Key .key (namespace , name .toLowerCase (Locale .ROOT ));
254
271
this .name = name ;
255
272
this .constructor = constructor ;
256
- this .requiredPermissions = List .of (AbilityDescription . NAMESPACE + "." + name );
273
+ this .requiredPermissions = List .of (defaultPermission () );
257
274
}
258
275
259
276
public Builder element (Element element ) {
@@ -273,7 +290,7 @@ public Builder activation(Activation method, Activation @Nullable ... methods) {
273
290
274
291
public Builder require (String @ Nullable ... permissions ) {
275
292
Collection <String > c = new ArrayList <>();
276
- c .add (AbilityDescription . NAMESPACE + "." + name );
293
+ c .add (defaultPermission () );
277
294
if (permissions != null ) {
278
295
c .addAll (List .of (permissions ));
279
296
}
@@ -304,6 +321,19 @@ public AbilityDescription build() {
304
321
return new AbilityDescription (this );
305
322
}
306
323
324
+ public Sequence buildSequence (UnaryOperator <SequenceBuilder > function ) {
325
+ validate ();
326
+ if (!activations .contains (Activation .SEQUENCE )) {
327
+ throw new IllegalStateException ("Ability must be activated by sequence" );
328
+ }
329
+ List <SequenceStep > sequenceSteps = function .apply (new SequenceBuilder ()).validateAndBuild ();
330
+ return new Sequence (this , sequenceSteps );
331
+ }
332
+
333
+ /**
334
+ * @deprecated use {@link #buildSequence(UnaryOperator)} instead
335
+ */
336
+ @ Deprecated (forRemoval = true )
307
337
public Sequence buildSequence (SequenceStep step1 , SequenceStep step2 , SequenceStep @ Nullable ... steps ) {
308
338
validate ();
309
339
if (!activations .contains (Activation .SEQUENCE )) {
@@ -325,5 +355,9 @@ private void validate() {
325
355
throw new IllegalStateException ("Activation methods cannot be empty" );
326
356
}
327
357
}
358
+
359
+ private String defaultPermission () {
360
+ return "bending.ability." + key .value ();
361
+ }
328
362
}
329
363
}
0 commit comments