37
37
import org .jetbrains .annotations .NotNull ;
38
38
import org .jetbrains .annotations .Nullable ;
39
39
40
+ import java .math .BigDecimal ;
41
+ import java .math .RoundingMode ;
42
+ import java .text .DecimalFormat ;
40
43
import java .util .*;
41
44
import java .util .stream .Collectors ;
42
45
@@ -54,6 +57,17 @@ public static class HuskTownsExpansion extends PlaceholderExpansion {
54
57
55
58
private static final String NOT_IN_TOWN_COLOR = "#aaaaaa" ;
56
59
private static final String WILDERNESS_COLOR = "#2e2e2e" ;
60
+ private static final BigDecimal THOUSAND = BigDecimal .valueOf (1000 );
61
+ private static final String DECIMAL_FORMAT = "#.#" ;
62
+ private static final TreeMap <BigDecimal , String > NUMBER_FORMAT = new TreeMap <>();
63
+
64
+ static {
65
+ NUMBER_FORMAT .put (THOUSAND , "k" );
66
+ NUMBER_FORMAT .put (THOUSAND .pow (2 ), "M" );
67
+ NUMBER_FORMAT .put (THOUSAND .pow (3 ), "G" );
68
+ NUMBER_FORMAT .put (THOUSAND .pow (4 ), "T" );
69
+ NUMBER_FORMAT .put (THOUSAND .pow (5 ), "Q" );
70
+ }
57
71
58
72
@ NotNull
59
73
private final HuskTowns plugin ;
@@ -68,6 +82,13 @@ public String onRequest(@Nullable OfflinePlayer offlinePlayer, @NotNull String p
68
82
return plugin .getLocales ().getNotApplicable ();
69
83
}
70
84
85
+ if (params .startsWith ("town_leaderboard_" )) {
86
+ if (params .length () == 17 ) {
87
+ return null ;
88
+ }
89
+ return getTownLeaderboard (params .substring (17 ));
90
+ }
91
+
71
92
// Ensure the player is online
72
93
if (offlinePlayer == null || !offlinePlayer .isOnline () || offlinePlayer .getPlayer () == null ) {
73
94
return plugin .getLocales ().getRawLocale ("placeholder_player_offline" )
@@ -76,135 +97,82 @@ public String onRequest(@Nullable OfflinePlayer offlinePlayer, @NotNull String p
76
97
77
98
// Return the requested placeholder
78
99
final OnlineUser player = BukkitUser .adapt (offlinePlayer .getPlayer (), plugin );
79
- return switch (params ) {
80
- case "town_name" -> plugin .getUserTown (player )
100
+
101
+ if (params .startsWith ("town_" )) {
102
+ if (params .length () == 5 ) {
103
+ return null ;
104
+ }
105
+ return getTown (player , params .substring (5 ));
106
+ }
107
+
108
+ if (params .startsWith ("current_location_" )) {
109
+ if (params .length () == 17 ) {
110
+ return null ;
111
+ }
112
+ return getCurrentLocation (player , params .substring (17 ));
113
+ }
114
+
115
+ return null ;
116
+ }
117
+
118
+ @ Nullable
119
+ private String getTown (@ NotNull OnlineUser player , @ NotNull String identifier ) {
120
+ return switch (identifier ) {
121
+ case "name" -> plugin .getUserTown (player )
81
122
.map (Member ::town )
82
123
.map (Town ::getName )
83
124
.orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
84
125
.orElse ("Not in town" ));
85
126
86
- case "town_role " -> plugin .getUserTown (player )
127
+ case "role " -> plugin .getUserTown (player )
87
128
.map (Member ::role )
88
129
.map (Role ::getName )
89
130
.orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
90
131
.orElse ("Not in town" ));
91
132
92
- case "town_mayor" -> plugin .getUserTown (player )
93
- .map (Member ::town )
94
- .map (town -> resolveTownMemberName (town , town .getMayor ()).orElse ("?" ))
95
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
96
- .orElse ("Not in town" ));
97
-
98
- case "town_color" -> plugin .getUserTown (player )
133
+ case "color" -> plugin .getUserTown (player )
99
134
.map (Member ::town )
100
135
.map (Town ::getColorRgb )
101
136
.orElse (NOT_IN_TOWN_COLOR );
102
137
103
- case "town_members" -> plugin .getUserTown (player )
104
- .map (Member ::town )
105
- .map (town -> town .getMembers ().keySet ().stream ()
106
- .map (uuid -> resolveTownMemberName (town , uuid ).orElse ("?" ))
107
- .collect (Collectors .joining (", " )))
108
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
109
- .orElse ("Not in town" ));
110
-
111
- case "town_member_count" -> plugin .getUserTown (player )
112
- .map (Member ::town )
113
- .map (Town ::getMembers )
114
- .map (members -> String .valueOf (members .size ()))
115
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
116
- .orElse ("Not in town" ));
117
-
118
- case "town_claim_count" -> plugin .getUserTown (player )
138
+ default -> plugin .getUserTown (player )
119
139
.map (Member ::town )
120
- .map (Town ::getClaimCount )
121
- .map (String ::valueOf )
122
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
123
- .orElse ("Not in town" ));
124
-
125
- case "town_max_claims" -> plugin .getUserTown (player )
126
- .map (Member ::town )
127
- .map (town -> town .getMaxClaims (plugin ))
128
- .map (String ::valueOf )
129
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
130
- .orElse ("Not in town" ));
131
-
132
- case "town_max_members" -> plugin .getUserTown (player )
133
- .map (Member ::town )
134
- .map (town -> town .getMaxMembers (plugin ))
135
- .map (String ::valueOf )
136
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
137
- .orElse ("Not in town" ));
138
-
139
- case "town_crop_growth_rate" -> plugin .getUserTown (player )
140
- .map (Member ::town )
141
- .map (town -> town .getCropGrowthRate (plugin ))
142
- .map (String ::valueOf )
143
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
144
- .orElse ("Not in town" ));
145
-
146
- case "town_mob_spawner_rate" -> plugin .getUserTown (player )
147
- .map (Member ::town )
148
- .map (town -> town .getMobSpawnerRate (plugin ))
149
- .map (String ::valueOf )
150
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
151
- .orElse ("Not in town" ));
152
-
153
- case "town_money" -> plugin .getUserTown (player )
154
- .map (Member ::town )
155
- .map (Town ::getMoney )
156
- .map (plugin ::formatMoney )
157
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
158
- .orElse ("Not in town" ));
159
-
160
- case "town_level_up_cost" -> plugin .getUserTown (player )
161
- .map (Member ::town )
162
- .map (town -> town .getLevel () >= plugin .getLevels ().getMaxLevel ()
163
- ? plugin .getLocales ().getNotApplicable ()
164
- : plugin .formatMoney (plugin .getLevels ().getLevelUpCost (town .getLevel ())))
165
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
166
- .orElse ("Not in town" ));
167
-
168
- case "town_level" -> plugin .getUserTown (player )
169
- .map (Member ::town )
170
- .map (Town ::getLevel )
171
- .map (String ::valueOf )
172
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
173
- .orElse ("Not in town" ));
174
-
175
- case "town_max_level" -> plugin .getUserTown (player )
176
- .map (Member ::town )
177
- .map (town -> plugin .getLevels ().getMaxLevel ())
140
+ .map (town -> resolveTownData (town , identifier ))
178
141
.map (String ::valueOf )
179
142
.orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
180
143
.orElse ("Not in town" ));
144
+ };
145
+ }
181
146
182
- case "current_location_town" -> plugin .getClaimAt (player .getPosition ())
147
+ @ Nullable
148
+ public String getCurrentLocation (@ NotNull OnlineUser player , @ NotNull String identifier ) {
149
+ return switch (identifier ) {
150
+ case "town" -> plugin .getClaimAt (player .getPosition ())
183
151
.map (TownClaim ::town )
184
152
.map (Town ::getName )
185
153
.orElse (plugin .getLocales ().getRawLocale ("placeholder_wilderness" )
186
154
.orElse ("Wilderness" ));
187
155
188
- case "current_location_can_build " -> getBooleanValue (!plugin
156
+ case "can_build " -> getBooleanValue (!plugin
189
157
.cancelOperation (Operation .of (player , OperationType .BLOCK_PLACE , player .getPosition (), true )));
190
158
191
- case "current_location_can_interact " -> getBooleanValue (!plugin .cancelOperation (
159
+ case "can_interact " -> getBooleanValue (!plugin .cancelOperation (
192
160
Operation .of (player , OperationType .BLOCK_INTERACT , player .getPosition (), true )
193
161
));
194
162
195
- case "current_location_can_open_containers " -> getBooleanValue (!plugin .cancelOperation (
163
+ case "can_open_containers " -> getBooleanValue (!plugin .cancelOperation (
196
164
Operation .of (player , OperationType .CONTAINER_OPEN , player .getPosition (), true )
197
165
));
198
166
199
- case "current_location_claim_type " -> plugin .getClaimAt (player .getPosition ())
167
+ case "claim_type " -> plugin .getClaimAt (player .getPosition ())
200
168
.map (TownClaim ::claim )
201
169
.map (Claim ::getType )
202
170
.map (Claim .Type ::name )
203
171
.map (String ::toLowerCase )
204
172
.orElse (plugin .getLocales ().getRawLocale ("placeholder_wilderness" )
205
173
.orElse ("Wilderness" ));
206
174
207
- case "current_location_plot_members " -> plugin .getClaimAt (player .getPosition ())
175
+ case "plot_members " -> plugin .getClaimAt (player .getPosition ())
208
176
.map (townClaim -> {
209
177
final Claim claim = townClaim .claim ();
210
178
if (claim .getType () != Claim .Type .PLOT ) {
@@ -219,7 +187,7 @@ public String onRequest(@Nullable OfflinePlayer offlinePlayer, @NotNull String p
219
187
.orElse (plugin .getLocales ().getRawLocale ("placeholder_not_claimed" )
220
188
.orElse ("Not claimed" ));
221
189
222
- case "current_location_plot_managers " -> plugin .getClaimAt (player .getPosition ())
190
+ case "plot_managers " -> plugin .getClaimAt (player .getPosition ())
223
191
.map (townClaim -> {
224
192
final Claim claim = townClaim .claim ();
225
193
if (claim .getType () != Claim .Type .PLOT ) {
@@ -235,87 +203,46 @@ public String onRequest(@Nullable OfflinePlayer offlinePlayer, @NotNull String p
235
203
.orElse (plugin .getLocales ().getRawLocale ("placeholder_not_claimed" )
236
204
.orElse ("Not claimed" ));
237
205
238
- case "current_location_town_color " -> plugin .getClaimAt (player .getPosition ())
206
+ case "town_color " -> plugin .getClaimAt (player .getPosition ())
239
207
.map (TownClaim ::town )
240
208
.map (Town ::getColorRgb )
241
209
.orElse (WILDERNESS_COLOR );
242
210
243
- case "current_location_town_money" -> plugin .getClaimAt (player .getPosition ())
244
- .map (TownClaim ::town )
245
- .map (Town ::getMoney )
246
- .map (plugin ::formatMoney )
247
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_claimed" )
248
- .orElse ("Not claimed" ));
249
-
250
- case "current_location_town_level" -> plugin .getClaimAt (player .getPosition ())
251
- .map (TownClaim ::town )
252
- .map (Town ::getLevel )
253
- .map (String ::valueOf )
254
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_claimed" )
255
- .orElse ("Not claimed" ));
256
-
257
- case "current_location_town_level_up_cost" -> plugin .getClaimAt (player .getPosition ())
258
- .map (TownClaim ::town )
259
- .map (town -> town .getLevel () >= plugin .getLevels ().getMaxLevel ()
260
- ? plugin .getLocales ().getNotApplicable ()
261
- : plugin .formatMoney (plugin .getLevels ().getLevelUpCost (town .getLevel ())))
262
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_in_town" )
263
- .orElse ("Not in town" ));
264
-
265
- case "current_location_town_max_claims" -> plugin .getClaimAt (player .getPosition ())
266
- .map (TownClaim ::town )
267
- .map (town -> town .getMaxClaims (plugin ))
268
- .map (String ::valueOf )
269
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_claimed" )
270
- .orElse ("Not claimed" ));
271
-
272
- case "current_location_town_max_members" -> plugin .getClaimAt (player .getPosition ())
273
- .map (TownClaim ::town )
274
- .map (town -> town .getMaxMembers (plugin ))
275
- .map (String ::valueOf )
276
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_claimed" )
277
- .orElse ("Not claimed" ));
278
-
279
- case "current_location_town_crop_growth_rate" -> plugin .getClaimAt (player .getPosition ())
280
- .map (TownClaim ::town )
281
- .map (town -> town .getCropGrowthRate (plugin ))
282
- .map (String ::valueOf )
283
- .orElse (plugin .getLocales ().getRawLocale ("placeholder_not_claimed" )
284
- .orElse ("Not claimed" ));
285
-
286
- case "current_location_town_mob_spawner_rate" -> plugin .getClaimAt (player .getPosition ())
211
+ default -> identifier .startsWith ("town_" ) ? plugin .getClaimAt (player .getPosition ())
287
212
.map (TownClaim ::town )
288
- .map (town -> town . getMobSpawnerRate ( plugin ))
213
+ .map (town -> resolveTownData ( town , identifier . substring ( 5 ) ))
289
214
.map (String ::valueOf )
290
215
.orElse (plugin .getLocales ().getRawLocale ("placeholder_not_claimed" )
291
- .orElse ("Not claimed" ));
292
-
293
- default -> params .startsWith ("town_leaderboard_" ) ? getTownLeaderboard (params .substring (17 )) : null ;
216
+ .orElse ("Not claimed" )) : null ;
294
217
};
295
218
}
296
219
297
220
// Get a town leaderboard
298
221
@ Nullable
299
222
private String getTownLeaderboard (@ NotNull String identifier ) {
300
- // Get the identifier up to the last underscore
301
- final int lastUnderscore = identifier .lastIndexOf ('_' );
302
- if (lastUnderscore == -1 ) {
223
+ final String [] split = identifier .split ("_" , 3 );
224
+ if (split .length < 2 ) {
303
225
return null ;
304
226
}
305
227
306
228
// Get the leaderboard index and return the sorted list element at the index
307
229
try {
308
- final int leaderboardIndex = Math .max (1 , Integer .parseInt (
309
- identifier .substring (lastUnderscore + 1 )
310
- ));
311
- final List <Town > towns = getSortedTownList (identifier .substring (0 , lastUnderscore ));
230
+ final int leaderboardIndex = Math .max (1 , Integer .parseInt (split [1 ]));
231
+ final List <Town > towns = getSortedTownList (split [0 ]);
312
232
if (towns == null ) {
313
233
return null ;
314
234
}
315
235
316
- return towns .size () >= leaderboardIndex
317
- ? towns .get (leaderboardIndex - 1 ).getName ()
318
- : plugin .getLocales ().getNotApplicable ();
236
+ if (towns .size () >= leaderboardIndex ) {
237
+ final Town town = towns .get (leaderboardIndex - 1 );
238
+ if (split .length > 2 ) {
239
+ return String .valueOf (resolveTownData (town , split [2 ]));
240
+ } else {
241
+ return town .getName ();
242
+ }
243
+ } else {
244
+ return plugin .getLocales ().getNotApplicable ();
245
+ }
319
246
} catch (NumberFormatException e ) {
320
247
return null ;
321
248
}
@@ -340,6 +267,16 @@ private List<Town> getSortedTownList(@NotNull String sortingKey) {
340
267
};
341
268
}
342
269
270
+ @ NotNull
271
+ public static String formatNumber (@ NotNull BigDecimal number ) {
272
+ final Map .Entry <BigDecimal , String > format = NUMBER_FORMAT .floorEntry (number );
273
+ if (format != null ) {
274
+ return new DecimalFormat (DECIMAL_FORMAT ).format (number .divide (format .getKey ().divide (THOUSAND , RoundingMode .DOWN ), RoundingMode .DOWN ).doubleValue () / 1000.0 ) + format .getValue ();
275
+ } else {
276
+ return number .toString ();
277
+ }
278
+ }
279
+
343
280
// Resolve a cached town member name from a UUID
344
281
private Optional <String > resolveTownMemberName (@ NotNull Town town , @ NotNull UUID uuid ) {
345
282
return town .getLog ().getActions ().values ().stream ()
@@ -349,6 +286,45 @@ private Optional<String> resolveTownMemberName(@NotNull Town town, @NotNull UUID
349
286
.findFirst ();
350
287
}
351
288
289
+ @ Nullable
290
+ public Object resolveTownData (@ NotNull Town town , @ NotNull String identifier ) {
291
+ return switch (identifier ) {
292
+ case "name" -> town .getName ();
293
+
294
+ case "mayor" -> resolveTownMemberName (town , town .getMayor ()).orElse ("?" );
295
+
296
+ case "members" -> town .getMembers ().keySet ().stream ()
297
+ .map (uuid -> resolveTownMemberName (town , uuid ).orElse ("?" ))
298
+ .collect (Collectors .joining (", " ));
299
+
300
+ case "member_count" -> town .getMembers ().size ();
301
+
302
+ case "claim_count" -> town .getClaimCount ();
303
+
304
+ case "max_claims" -> town .getMaxClaims (plugin );
305
+
306
+ case "max_members" -> town .getMaxMembers (plugin );
307
+
308
+ case "crop_growth_rate" -> town .getCropGrowthRate (plugin );
309
+
310
+ case "mob_spawner_rate" -> town .getMobSpawnerRate (plugin );
311
+
312
+ case "money" -> plugin .formatMoney (town .getMoney ());
313
+
314
+ case "money_formatted" -> formatNumber (town .getMoney ());
315
+
316
+ case "level_up_cost" -> town .getLevel () >= plugin .getLevels ().getMaxLevel ()
317
+ ? plugin .getLocales ().getNotApplicable ()
318
+ : plugin .formatMoney (plugin .getLevels ().getLevelUpCost (town .getLevel ()));
319
+
320
+ case "level" -> town .getLevel ();
321
+
322
+ case "max_level" -> plugin .getLevels ().getMaxLevel ();
323
+
324
+ default -> null ;
325
+ };
326
+ }
327
+
352
328
@ Override
353
329
public boolean persist () {
354
330
return true ;
0 commit comments