/
NavigatorParameters.java
575 lines (517 loc) · 16.7 KB
/
NavigatorParameters.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
package net.citizensnpcs.api.ai;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import net.citizensnpcs.api.ai.event.CancelReason;
import net.citizensnpcs.api.ai.event.NavigatorCallback;
import net.citizensnpcs.api.astar.AStarMachine;
import net.citizensnpcs.api.astar.pathfinder.BlockExaminer;
public class NavigatorParameters implements Cloneable {
private int attackDelayTicks = 20;
private double attackRange;
private AttackStrategy attackStrategy;
private boolean avoidWater;
private float baseSpeed = 1F;
private List<NavigatorCallback> callbacks = Lists.newArrayList();
private boolean debug;
private AttackStrategy defaultStrategy;
private double destinationTeleportMargin = -1;
private double distanceMargin = 2F;
private List<BlockExaminer> examiners = Lists.newArrayList();
private Function<Navigator, Location> lookAtFunction;
private Function<Entity, Location> mapper;
private double pathDistanceMargin = 1F;
private float range;
private List<Runnable> runCallbacks = Lists.newArrayList();
private float speedModifier = 1F;
private int stationaryTicks = -1;
private float straightLineTargetingDistance;
private StuckAction stuckAction;
private int updatePathRate;
private boolean useNewPathfinder;
/**
* Adds a {@link Runnable} callback that will be called every tick while the path is running.
*
* @param callback
* The callback to add
*/
public NavigatorParameters addRunCallback(Runnable callback) {
runCallbacks.add(callback);
return this;
}
/**
* Adds a {@link NavigatorCallback} that will be removed <em>immediately</em> after being called.
*
* @param callback
* The callback
*/
public NavigatorParameters addSingleUseCallback(NavigatorCallback callback) {
callbacks.add(callback);
return this;
}
/**
* @see #attackDelayTicks(int)
* @return The number of ticks to wait between attacks
*/
public int attackDelayTicks() {
return attackDelayTicks;
}
/**
* Sets the delay between attacks. When attacking a target using an aggressive target strategy, the NPC waits for a
* certain number of ticks between attacks to avoid spamming damage to the target. This determines the number of
* ticks to wait.
*
* @param ticks
* The new number of ticks to wait between attacks
*/
public NavigatorParameters attackDelayTicks(int ticks) {
attackDelayTicks = ticks;
return this;
}
/**
* @see #attackRange(double)
* @return The attack range, in blocks
*/
public double attackRange() {
return attackRange;
}
/**
* When using aggressive NPC navigation, the NPC will wait until close enough to the target before attempting to use
* the {@link #attackStrategy()}. This parameter determines the range in blocks squared before the target will be
* valid to attack.
*
* @param range
* The new attack range, in blocks
*/
public NavigatorParameters attackRange(double range) {
this.attackRange = range;
return this;
}
/**
* @return The {@link AttackStrategy} currently in use or the {@link #defaultAttackStrategy()} if not configured
* (may return null)
*/
public AttackStrategy attackStrategy() {
return attackStrategy == null ? defaultStrategy : attackStrategy;
}
/**
* Sets the {@link AttackStrategy} for use when attacking entity targets.
*
* @param strategy
* The strategy to use
*/
public void attackStrategy(AttackStrategy strategy) {
attackStrategy = strategy;
}
/**
* @return Whether to avoid water while pathfinding
*/
public boolean avoidWater() {
return avoidWater;
}
/**
* Sets whether to avoid water while pathfinding
*
* @param avoidWater
* Whether to avoid water
*/
public NavigatorParameters avoidWater(boolean avoidWater) {
this.avoidWater = avoidWater;
return this;
}
/**
* @return The base movement speed
*/
public float baseSpeed() {
return baseSpeed;
}
/**
* Sets the base movement speed of the {@link Navigator}. Note that this is mob-specific and may not always be sane.
* Using {@link #speedModifier()} is preferred.
*
* @see #speedModifier()
* @param speed
* The new movement speed
*/
public NavigatorParameters baseSpeed(float speed) {
this.baseSpeed = speed;
return this;
}
/**
* @return All callbacks currently registered
*/
public Iterable<NavigatorCallback> callbacks() {
return callbacks;
}
/**
* Clears all current {@link BlockExaminer}s.
*/
public NavigatorParameters clearExaminers() {
examiners.clear();
return this;
}
@Override
@SuppressWarnings("unchecked")
public NavigatorParameters clone() {
try {
NavigatorParameters clone = (NavigatorParameters) super.clone();
if (callbacks instanceof ArrayList) {
clone.callbacks = (List<NavigatorCallback>) ((ArrayList<NavigatorCallback>) callbacks).clone();
}
if (examiners instanceof ArrayList) {
clone.examiners = (List<BlockExaminer>) ((ArrayList<BlockExaminer>) examiners).clone();
}
if (runCallbacks instanceof ArrayList) {
clone.runCallbacks = (List<Runnable>) ((ArrayList<Runnable>) runCallbacks).clone();
}
return clone;
} catch (CloneNotSupportedException e) {
return null;
}
}
/**
* Returns whether this path will be debugged. Path debugging happens by repeatedly setting the next destination
* block to a client-sided flower.
*
* @return Whether the path is debugging
*/
public boolean debug() {
return this.debug;
}
/**
* Sets whether the path should be debugged.
*
* @see #debug()
*/
public NavigatorParameters debug(boolean debug) {
this.debug = debug;
return this;
}
/**
* Returns the configured <em>default</em> attack strategy, which tries to perform the most Minecraft-like attack on
* the target.
*
* @return The default strategy
*/
public AttackStrategy defaultAttackStrategy() {
return this.defaultStrategy;
}
/**
* Sets the default {@link AttackStrategy}.
*
* @param defaultStrategy
* The new default strategy
* @see #defaultAttackStrategy()
*/
public NavigatorParameters defaultAttackStrategy(AttackStrategy defaultStrategy) {
this.defaultStrategy = defaultStrategy;
return this;
}
/**
* @see #destinationTeleportMargin(double)
*/
public double destinationTeleportMargin() {
return destinationTeleportMargin;
}
/**
* Sets the distance (in blocks) after which the NPC will directly teleport to the destination or -1 if disabled.
* For example, if the destination teleport margin was 1.5 and the NPC reached 1.5 blocks from the target it would
* instantly teleport to the target location.
*
* @param margin
* Distance teleport margin
*/
public NavigatorParameters destinationTeleportMargin(double margin) {
destinationTeleportMargin = margin;
return this;
}
/**
* Returns the distance margin or leeway that the {@link Navigator} will be able to stop from the target
* destination. The margin will be measured against the block distance.
*
* For example: if the distance margin were 2, then the {@link Navigator} could stop moving towards the target when
* it is 2 blocks away from it.
*
* @return The distance margin
*/
public double distanceMargin() {
return distanceMargin;
}
/**
* Sets the distance margin.
*
* @see #distanceMargin()
* @param newMargin
* The new distance margin
*/
public NavigatorParameters distanceMargin(double newMargin) {
distanceMargin = newMargin;
return this;
}
/**
* Gets the target location mapper. This is a function that maps from a target entity to the location the NPC should
* pathfind to. The default mapper returns the location using {@link Entity#getLocation(Location)}.
*/
public Function<Entity, Location> entityTargetLocationMapper() {
return mapper != null ? mapper : DEFAULT_MAPPER;
}
/**
* Set the target location mapper.
*
* @param mapper
* The new mapper
* @see #entityTargetLocationMapper(Function)
*/
public NavigatorParameters entityTargetLocationMapper(Function<Entity, Location> mapper) {
this.mapper = mapper;
return this;
}
/**
* Adds the given {@link BlockExaminer}.
*
* @param examiner
* The BlockExaminer to add
*/
public NavigatorParameters examiner(BlockExaminer examiner) {
examiners.add(examiner);
return this;
}
/**
* Gets a copy of all current {@link BlockExaminer}s.
*
* @return An array of all current examiners
*/
public BlockExaminer[] examiners() {
return examiners.toArray(new BlockExaminer[examiners.size()]);
}
public boolean hasExaminer(Class<? extends BlockExaminer> clazz) {
return Arrays.asList(examiners).stream().anyMatch(e -> clazz.isAssignableFrom(e.getClass()));
}
/**
* @see #lookAtFunction(Function)
*/
public Function<Navigator, Location> lookAtFunction() {
return this.lookAtFunction;
}
/**
* Sets the position to look at during pathfinding, overriding the default 'look at target' behaviour.
*
* @param lookAt
* Where to look
*/
public NavigatorParameters lookAtFunction(Function<Navigator, Location> lookAt) {
this.lookAtFunction = lookAt;
return this;
}
/**
* Modifieds the given speed value based on the current parameters.
*
* @param toModify
* The speed value to modify
* @return The modified speed
*/
public float modifiedSpeed(float toModify) {
return toModify * speedModifier();
}
/**
* Gets the path distance margin.
*
* @see #pathDistanceMargin(double)
*/
public double pathDistanceMargin() {
return pathDistanceMargin;
}
/**
* Sets the path distance margin. This is how close the pathfinder should pathfind to the target in blocks. If you
* need to set the cutoff distance before the NPC considers their path completed, use
* {@link #distanceMargin(double)}.
*
* @param distance
* The distance margin
*/
public NavigatorParameters pathDistanceMargin(double distance) {
this.pathDistanceMargin = distance;
return this;
}
/**
* @return The pathfinding range of the navigator in blocks.
* @see #range(float)
*/
public float range() {
return range;
}
/**
* Sets the pathfinding range in blocks. The pathfinding range determines how far away the {@link Navigator} will
* attempt to pathfind before giving up to save computation.
*
* @param range
* The new range
*/
public NavigatorParameters range(float range) {
this.range = range;
return this;
}
/**
* Removes a previously added run callback.
*
* @see #addRunCallback(Runnable)
* @param runnable
* The run callback to remove
*/
public NavigatorParameters removeRunCallback(Runnable runnable) {
runCallbacks.remove(runnable);
return this;
}
/**
* FOR INTERNAL USE ONLY: ticks all {@link Runnable} callbacks.
*/
public void run() {
for (int i = 0; i < runCallbacks.size(); i++) {
runCallbacks.get(i).run();
}
}
/**
* @return The modified movement speed as given by {@link #baseSpeed()} multiplied by {@link #speedModifier()}
*/
public float speed() {
return modifiedSpeed(baseSpeed);
}
/**
* Sets the base movement speed of the {@link Navigator}. Note that this is mob-specific and may not always be sane.
* Using {@link #speedModifier()} is preferred.
*
* @see #speedModifier()
* @param speed
* The new movement speed
* @deprecated @see {@link #baseSpeed(float)}
*/
@Deprecated
public NavigatorParameters speed(float speed) {
this.baseSpeed = speed;
return this;
}
/**
* @return The movement speed modifier
* @see #speedModifier(float)
*/
public float speedModifier() {
return speedModifier;
}
/**
* Sets the movement speed modifier of the {@link Navigator}. This is a percentage modifier that alters the movement
* speed returned in {@link #speed()}.
*
* @param percent
* The new speed modifier
*/
public NavigatorParameters speedModifier(float percent) {
speedModifier = percent;
return this;
}
/**
* @return The number of stationary ticks
* @see #stationaryTicks(int)
*/
public int stationaryTicks() {
return stationaryTicks;
}
/**
* Sets the number of stationary ticks before navigation is cancelled with a {@link CancelReason} of STUCK.
*
* @param ticks
* The new number of stationary ticks
*/
public NavigatorParameters stationaryTicks(int ticks) {
stationaryTicks = ticks;
return this;
}
/**
* @see #straightLineTargetingDistance(float)
* @return The distance
*/
public float straightLineTargetingDistance() {
return straightLineTargetingDistance;
}
/**
* Sets the distance (in blocks) at which the entity targeter will switch to simply following a straight line to the
* target instead of pathfinding.
*
* @param distance
* The distance (in blocks)
*/
public NavigatorParameters straightLineTargetingDistance(float distance) {
straightLineTargetingDistance = distance;
return this;
}
/**
* Gets the {@link StuckAction} of these parameters. This will be run when the navigation is stuck and must either
* be fixed up or cancelled.
*
* @return The current stuck action
*/
public StuckAction stuckAction() {
return stuckAction;
}
/**
* Sets the {@link StuckAction} of the parameters.
*
* @param action
* The new stuck action
* @see #stuckAction()
*/
public NavigatorParameters stuckAction(StuckAction action) {
stuckAction = action;
return this;
}
/**
* @see #updatePathRate(int)
* @return The current path rate
*/
public int updatePathRate() {
return updatePathRate;
}
/**
* Sets the update path rate, in ticks (default 20). Mainly used for target following at this point - the NPC will
* recalculate its path to the target every {@code rate} ticks.
*
* @param rate
* The new rate in ticks to use
*/
public NavigatorParameters updatePathRate(int rate) {
updatePathRate = rate;
return this;
}
/**
* @see #useNewPathfinder(boolean)
* @return Whether to use the new pathfinder
*/
public boolean useNewPathfinder() {
return useNewPathfinder;
}
/**
* Sets whether or not to use an A* pathfinder defined in {@link AStarMachine} for pathfinding.
*
* If this is set to false, then the Minecraft pathfinder will be used, which may or may not be more consistent.
*
* Note that certain API features will not be possible if this is set to false - this includes
* {@link #examiner(BlockExaminer)} and {@link #distanceMargin(double)}.
*
* @param use
* Whether to use the A* pathfinder
*/
public NavigatorParameters useNewPathfinder(boolean use) {
useNewPathfinder = use;
return this;
}
private static final Function<org.bukkit.entity.Entity, Location> DEFAULT_MAPPER = new Function<Entity, Location>() {
Location location = new Location(null, 0, 0, 0);
@Override
public Location apply(Entity input) {
return input.getLocation(location);
}
};
}