/
WanderGoal.java
240 lines (211 loc) · 7.91 KB
/
WanderGoal.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
package net.citizensnpcs.api.ai.goals;
import java.util.Random;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.event.Listener;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import ch.ethz.globis.phtree.PhTreeSolid;
import net.citizensnpcs.api.ai.Goal;
import net.citizensnpcs.api.ai.tree.Behavior;
import net.citizensnpcs.api.ai.tree.BehaviorGoalAdapter;
import net.citizensnpcs.api.ai.tree.BehaviorStatus;
import net.citizensnpcs.api.astar.pathfinder.MinecraftBlockExaminer;
import net.citizensnpcs.api.npc.NPC;
/**
* A sample {@link Goal}/{@link Behavior} that will wander within a certain radius or {@link QuadTree}.
*/
public class WanderGoal extends BehaviorGoalAdapter implements Listener {
private int delay;
private int delayedTicks;
private final Function<NPC, Location> fallback;
private boolean forceFinish;
private int movingTicks;
private final NPC npc;
private boolean pathfind;
private boolean paused;
private Location target;
private final Supplier<PhTreeSolid<Boolean>> tree;
private Object worldguardRegion;
private int xrange;
private int yrange;
private WanderGoal(NPC npc, boolean pathfind, int xrange, int yrange, Supplier<PhTreeSolid<Boolean>> tree,
Function<NPC, Location> fallback, Object worldguardRegion, int delay) {
this.npc = npc;
this.pathfind = pathfind;
this.worldguardRegion = worldguardRegion;
this.xrange = xrange;
this.yrange = yrange;
this.tree = tree;
this.fallback = fallback;
this.delay = delay;
}
private Location findRandomPosition() {
Location found = MinecraftBlockExaminer.findRandomValidLocation(npc.getEntity().getLocation(NPC_LOCATION),
pathfind ? xrange : 1, pathfind ? yrange : 1, new Function<Block, Boolean>() {
@Override
public Boolean apply(Block block) {
if ((MinecraftBlockExaminer.isLiquidOrInLiquid(block.getRelative(BlockFace.UP))
|| MinecraftBlockExaminer.isLiquidOrInLiquid(block.getRelative(0, 2, 0)))
&& npc.getNavigator().getDefaultParameters().avoidWater()) {
return false;
}
if (worldguardRegion != null) {
try {
if (!((ProtectedRegion) worldguardRegion)
.contains(BukkitAdapter.asBlockVector(block.getLocation())))
return false;
} catch (Throwable t) {
t.printStackTrace();
}
}
if (tree != null) {
long[] pt = { block.getX(), block.getY(), block.getZ() };
if (tree.get() != null && !tree.get().queryIntersect(pt, pt).hasNext()) {
return false;
}
}
return true;
}
}, RANDOM);
if (found == null && fallback != null) {
return fallback.apply(npc);
}
return found;
}
public void pause() {
this.paused = true;
}
@Override
public void reset() {
target = null;
movingTicks = 0;
delayedTicks = delay;
forceFinish = false;
}
@Override
public BehaviorStatus run() {
if (pathfind) {
if (!npc.getNavigator().isNavigating() || forceFinish) {
return BehaviorStatus.SUCCESS;
}
} else {
if (npc.getEntity().getLocation(NPC_LOCATION).distance(target) >= 0.1) {
npc.setMoveDestination(target);
} else {
return BehaviorStatus.SUCCESS;
}
if (movingTicks-- <= 0) {
npc.setMoveDestination(null);
return BehaviorStatus.SUCCESS;
}
}
return BehaviorStatus.RUNNING;
}
public void setDelay(int delayTicks) {
this.delay = delayTicks;
this.delayedTicks = delayTicks;
}
public void setPathfind(boolean pathfind) {
this.pathfind = pathfind;
}
public void setWorldGuardRegion(Object region) {
this.worldguardRegion = region;
}
public void setXYRange(int xrange, int yrange) {
this.xrange = xrange;
this.yrange = yrange;
}
@Override
public boolean shouldExecute() {
if (!npc.isSpawned() || npc.getNavigator().isNavigating() || paused)
return false;
if (delayedTicks-- > 0) {
return false;
}
Location dest = findRandomPosition();
if (dest == null)
return false;
if (pathfind) {
npc.getNavigator().setTarget(dest);
npc.getNavigator().getLocalParameters().addSingleUseCallback((reason) -> forceFinish = true);
} else {
Random random = new Random();
dest.setX(dest.getX() + random.nextDouble(0.5));
dest.setZ(dest.getZ() + random.nextDouble(0.5));
movingTicks = 20 + random.nextInt(20);
}
this.target = dest;
return true;
}
public void unpause() {
this.paused = false;
}
public static class Builder {
private int delay = 10;
private Function<NPC, Location> fallback;
private final NPC npc;
private boolean pathfind = true;
private Supplier<PhTreeSolid<Boolean>> tree;
private Object worldguardRegion;
private int xrange = 10;
private int yrange = 2;
private Builder(NPC npc) {
this.npc = npc;
this.tree = null;
this.fallback = null;
this.worldguardRegion = null;
}
public WanderGoal build() {
return new WanderGoal(npc, pathfind, xrange, yrange, tree, fallback, worldguardRegion, delay);
}
public Builder delay(int delay) {
this.delay = delay;
return this;
}
public Builder fallback(Function<NPC, Location> fallback) {
this.fallback = fallback;
return this;
}
public Builder pathfind(boolean pathfind) {
this.pathfind = pathfind;
return this;
}
public Builder regionCentres(Supplier<Iterable<Location>> supplier) {
this.tree = () -> {
PhTreeSolid<Boolean> tree = PhTreeSolid.create(3);
for (Location loc : supplier.get()) {
long[] lower = { loc.getBlockX() - xrange, loc.getBlockY() - yrange, loc.getBlockZ() - xrange };
long[] upper = { loc.getBlockX() + xrange, loc.getBlockY() + yrange, loc.getBlockZ() + xrange };
tree.put(lower, upper, true);
}
return tree;
};
return this;
}
public Builder tree(Supplier<PhTreeSolid<Boolean>> supplier) {
this.tree = supplier;
return this;
}
public Builder worldguardRegion(Object worldguardRegion) {
this.worldguardRegion = worldguardRegion;
return this;
}
public Builder xrange(int xrange) {
this.xrange = xrange;
return this;
}
public Builder yrange(int yrange) {
this.yrange = yrange;
return this;
}
}
public static Builder builder(NPC npc) {
return new Builder(npc);
}
private static final Location NPC_LOCATION = new Location(null, 0, 0, 0);
private static final Random RANDOM = new Random();
}