├── .gitignore
├── LICENSE
├── pom.xml
└── src
└── main
└── java
└── de
└── domisum
└── lib
└── compitum
├── CompitumLib.java
├── UniversalPathfinder.java
├── block
├── BlockAStar.java
├── BlockPathSmoother.java
└── evaluator
│ ├── MaterialEvaluator.java
│ └── StairEvaluator.java
├── navmesh
├── NavMesh.java
├── NavMeshManager.java
├── geometry
│ ├── NavMeshPoint.java
│ └── NavMeshTriangle.java
├── json
│ ├── SerializationNavMesh.java
│ ├── SerializationNavMeshLadder.java
│ └── SerializationNavMeshTriangle.java
├── pathfinding
│ ├── NavMeshPathfinder.java
│ ├── NavMeshTriangleNode.java
│ ├── NavMeshTrianglePathfinder.java
│ └── traversal
│ │ └── NavMeshTriangleTraverser.java
└── transition
│ ├── NavMeshLadder.java
│ ├── NavMeshTrianglePortal.java
│ └── NavMeshTriangleTransition.java
└── path
├── BlockPath.java
├── Path.java
├── PathWaypoint.java
└── node
├── BlockPathNode.java
├── TransitionType.java
└── weighted
├── SortedWeightedNodeList.java
└── WeightedNode.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .classpath
2 | .project
3 | *.iml
4 |
5 | .settings/
6 | target/
7 | .idea/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Dominik Weissenseel
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 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 | 4.0.0
5 | de.domisum
6 | CompitumLib
7 | 2.0.0-SNAPSHOT
8 |
9 |
10 | UTF-8
11 | 1.8
12 | 1.8
13 |
14 |
15 |
16 |
17 | domisum-releases-public
18 | http://vps.domisum.de:8081/nexus/content/repositories/releases-public/
19 |
20 |
21 | domisum-snapshots-public
22 | http://vps.domisum.de:8081/nexus/content/repositories/snapshots-public/
23 |
24 |
25 |
26 |
27 |
28 | de.domisum
29 | AuxiliumLib
30 | 1.0.0-SNAPSHOT
31 |
32 |
33 |
34 | de.domisum
35 | AuxiliumSpigotLib
36 | 1.0.0-SNAPSHOT
37 |
38 |
39 |
40 |
41 | org.spigotmc
42 | spigot-api
43 | 1.9.2-R0.1-SNAPSHOT
44 | provided
45 |
46 |
47 |
48 | org.bukkit
49 | craftbukkit
50 | 1.9.2-R0.1-SNAPSHOT
51 | provided
52 |
53 |
54 |
55 |
56 | com.darkblade12.particleeffect
57 | ParticleLib
58 | 1.0.0
59 |
60 |
61 |
62 | org.projectlombok
63 | lombok
64 | 1.16.10
65 | provided
66 |
67 |
68 |
69 |
70 |
71 |
72 | maven-compiler-plugin
73 | 3.5.1
74 |
75 | 1.8
76 | 1.8
77 | UTF-8
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | vps
86 | vps.domisum.de-releases
87 | http://vps.domisum.de:8081/nexus/content/repositories/releases-public/
88 |
89 |
90 |
91 | vps
92 | vps.domisum.de-snapshots
93 | http://vps.domisum.de:8081/nexus/content/repositories/snapshots-public/
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/CompitumLib.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum;
2 |
3 | import de.domisum.lib.auxilium.util.java.annotations.API;
4 | import de.domisum.lib.auxiliumspigot.AuxiliumSpigotLib;
5 | import de.domisum.lib.compitum.block.evaluator.MaterialEvaluator;
6 | import de.domisum.lib.compitum.block.evaluator.StairEvaluator;
7 | import de.domisum.lib.compitum.navmesh.NavMeshManager;
8 | import org.bukkit.plugin.Plugin;
9 | import org.bukkit.plugin.java.JavaPlugin;
10 |
11 | import java.util.logging.Logger;
12 |
13 | @API
14 | public class CompitumLib
15 | {
16 |
17 | // SETTINGS
18 | private static boolean navMeshesEnabled = false;
19 |
20 | // REFERENCES
21 | private static CompitumLib instance;
22 | private Plugin plugin;
23 |
24 | private NavMeshManager navMeshManager;
25 |
26 |
27 | // INIT
28 | private CompitumLib(JavaPlugin plugin)
29 | {
30 | this.plugin = plugin;
31 | }
32 |
33 | @API public static void enable(JavaPlugin plugin)
34 | {
35 | if(instance != null)
36 | return;
37 |
38 | instance = new CompitumLib(plugin);
39 | instance.onEnable();
40 | }
41 |
42 | @API public static void disable()
43 | {
44 | if(instance == null)
45 | return;
46 |
47 | getInstance().onDisable();
48 | instance = null;
49 | }
50 |
51 | private void onEnable()
52 | {
53 | AuxiliumSpigotLib.enable(this.plugin);
54 |
55 | MaterialEvaluator.prepareEvaluation();
56 | StairEvaluator.prepareEvaluation();
57 |
58 | if(navMeshesEnabled)
59 | {
60 | this.navMeshManager = new NavMeshManager();
61 | this.navMeshManager.initiialize();
62 | }
63 |
64 | getLogger().info(this.getClass().getSimpleName()+" has been enabled");
65 | }
66 |
67 | private void onDisable()
68 | {
69 | if(this.navMeshManager != null)
70 | this.navMeshManager.terminate();
71 |
72 | AuxiliumSpigotLib.disable();
73 |
74 | getLogger().info(this.getClass().getSimpleName()+" has been disabled");
75 | }
76 |
77 |
78 | // GETTERS
79 | @API public static CompitumLib getInstance()
80 | {
81 | if(instance == null)
82 | throw new IllegalArgumentException(CompitumLib.class.getSimpleName()+" has to be initialized before usage");
83 |
84 | return instance;
85 | }
86 |
87 | public static Logger getLogger()
88 | {
89 | return getInstance().plugin.getLogger();
90 | }
91 |
92 |
93 | @API public static NavMeshManager getNavMeshManager()
94 | {
95 | if(!navMeshesEnabled)
96 | throw new IllegalStateException("The usage of NavMeshes has to be enabled first!");
97 |
98 | return getInstance().navMeshManager;
99 | }
100 |
101 | @API public static boolean areNavMeshesEnabled()
102 | {
103 | return navMeshesEnabled;
104 | }
105 |
106 |
107 | // SETTERS
108 | @API public static void enableNavMeshes()
109 | {
110 | if(instance != null)
111 | throw new IllegalStateException("NavMeshes have to be enabled before enabling CompitumLib");
112 |
113 | navMeshesEnabled = true;
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/UniversalPathfinder.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum;
2 |
3 |
4 | import de.domisum.lib.auxilium.util.java.annotations.API;
5 | import de.domisum.lib.compitum.block.BlockAStar;
6 | import de.domisum.lib.compitum.block.BlockPathSmoother;
7 | import de.domisum.lib.compitum.navmesh.NavMesh;
8 | import de.domisum.lib.compitum.navmesh.NavMeshManager;
9 | import de.domisum.lib.compitum.navmesh.pathfinding.NavMeshPathfinder;
10 | import de.domisum.lib.compitum.path.BlockPath;
11 | import de.domisum.lib.compitum.path.Path;
12 | import org.bukkit.Location;
13 |
14 | import java.util.Objects;
15 |
16 | @API
17 | public class UniversalPathfinder
18 | {
19 |
20 | // INPUT
21 | private Location start;
22 | private Location target;
23 |
24 | // PROPERTIES
25 |
26 |
27 | // OUTPUT
28 | private Path path;
29 |
30 | private String diagnose;
31 | private String failure;
32 |
33 |
34 | // INIT
35 | @API public UniversalPathfinder(Location start, Location target)
36 | {
37 | this.start = fixPathfindingLocation(start);
38 | this.target = fixPathfindingLocation(target);
39 | }
40 |
41 |
42 | // GETTERS
43 | @API public Path getPath()
44 | {
45 | return this.path;
46 | }
47 |
48 | @API public boolean isPathFound()
49 | {
50 | return this.path != null;
51 | }
52 |
53 |
54 | @API public String getDiagnose()
55 | {
56 | return this.diagnose;
57 | }
58 |
59 | @API public String getFailure()
60 | {
61 | return this.failure;
62 | }
63 |
64 |
65 | // PATHFINDING
66 | @API public void findPath()
67 | {
68 | navMeshCheck:
69 | if(CompitumLib.areNavMeshesEnabled())
70 | {
71 | NavMeshManager nmm = CompitumLib.getNavMeshManager();
72 | NavMesh meshAtStart = nmm.getNavMeshAt(this.start);
73 | if(meshAtStart == null)
74 | break navMeshCheck;
75 |
76 | NavMesh meshAtTarget = nmm.getNavMeshAt(this.target);
77 | if(meshAtTarget == null)
78 | break navMeshCheck;
79 |
80 | if(!Objects.equals(meshAtStart, meshAtTarget))
81 | break navMeshCheck;
82 |
83 | useNavMesh(meshAtStart);
84 | return;
85 | }
86 |
87 | useWorldAStar();
88 | }
89 |
90 |
91 | private void useWorldAStar()
92 | {
93 | BlockAStar pathfinder = new BlockAStar(this.start, this.target);
94 | pathfinder.findPath();
95 | this.diagnose = pathfinder.getDiagnose();
96 | if(!pathfinder.pathFound())
97 | {
98 | this.failure = pathfinder.getFailure();
99 | return;
100 | }
101 | BlockPath blockPath = pathfinder.getPath();
102 |
103 | BlockPathSmoother smoother = new BlockPathSmoother(blockPath);
104 | smoother.convert();
105 | this.path = smoother.getSmoothPath();
106 | }
107 |
108 | private void useNavMesh(NavMesh navMesh)
109 | {
110 | NavMeshPathfinder pathfinder = new NavMeshPathfinder(this.start, this.target, navMesh);
111 | pathfinder.findPath();
112 | this.path = pathfinder.getPath();
113 |
114 | //this.diagnose = pathfinder.getDiagnose();
115 | if(this.path == null)
116 | {
117 | this.failure = getFailure();
118 | return;
119 | }
120 |
121 | this.path = pathfinder.getPath();
122 | }
123 |
124 |
125 | @API public static Location fixPathfindingLocation(Location location)
126 | {
127 | location.setY(Math.floor(location.getY()));
128 |
129 | String materialName = location.getBlock().getType().name();
130 | if(materialName.contains("SLAB") || materialName.contains("STEP"))
131 | location.add(0, 1, 0);
132 |
133 | return location;
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/block/BlockAStar.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.block;
2 |
3 |
4 | import de.domisum.lib.auxilium.util.java.annotations.API;
5 | import de.domisum.lib.auxilium.util.math.MathUtil;
6 | import de.domisum.lib.compitum.block.evaluator.MaterialEvaluator;
7 | import de.domisum.lib.compitum.block.evaluator.StairEvaluator;
8 | import de.domisum.lib.compitum.path.node.TransitionType;
9 | import de.domisum.lib.compitum.path.node.BlockPathNode;
10 | import de.domisum.lib.compitum.path.BlockPath;
11 | import de.domisum.lib.compitum.path.node.weighted.SortedWeightedNodeList;
12 | import org.bukkit.Location;
13 | import org.bukkit.Material;
14 |
15 | import java.util.HashSet;
16 | import java.util.Set;
17 |
18 | @API
19 | public class BlockAStar
20 | {
21 |
22 | // CONSTANTS
23 | private static final double CLIMBING_EXPENSE = 2;
24 |
25 | // PROPERTIES
26 | private double heuristicImportance = 1.0;
27 | private int maxNodeVisits = 500;
28 |
29 | private boolean canUseDiagonalMovement = true;
30 | private boolean canUseLadders = false;
31 |
32 | // INPUT
33 | private Location startLocation;
34 | private Location endLocation;
35 |
36 | // STATUS
37 | private boolean moveDiagonally = true;
38 | private boolean useLadders = false;
39 |
40 | protected BlockPathNode endNode;
41 |
42 | private SortedWeightedNodeList unvisitedNodes = new SortedWeightedNodeList<>(this.maxNodeVisits*3);
43 | private Set visitedNodes = new HashSet<>(this.maxNodeVisits);
44 |
45 | private long pathfindingStartNano;
46 | private long pathfindingEndNano;
47 |
48 | // OUTPUT
49 | private BlockPath path;
50 | private String failure;
51 |
52 |
53 | // INIT
54 | @API public BlockAStar(Location startLocation, Location endLocation)
55 | {
56 | this.startLocation = startLocation;
57 | this.endLocation = endLocation;
58 | }
59 |
60 |
61 | // GETTERS
62 | @API public boolean pathFound()
63 | {
64 | return this.path != null;
65 | }
66 |
67 | @API public BlockPath getPath()
68 | {
69 | return this.path;
70 | }
71 |
72 |
73 | @API public String getFailure()
74 | {
75 | return this.failure;
76 | }
77 |
78 |
79 | private long getNanoDuration()
80 | {
81 | return this.pathfindingEndNano-this.pathfindingStartNano;
82 | }
83 |
84 | private double getMsDuration()
85 | {
86 | return MathUtil.round(getNanoDuration()/1000d/1000, 2);
87 | }
88 |
89 | @API public String getDiagnose()
90 | {
91 | String diagnose = "";
92 |
93 | diagnose += "found="+pathFound()+", ";
94 | /*if(pathFound())
95 | diagnose += "length="+getPath().getLength()+", ";*/
96 |
97 | diagnose += "visitedNodes="+this.visitedNodes.size()+", ";
98 | diagnose += "unvisitedNodes="+this.unvisitedNodes.getSize()+", ";
99 | diagnose += "durationMs="+getMsDuration()+", ";
100 |
101 | return diagnose;
102 | }
103 |
104 |
105 | // SETTERS
106 | @API public void setHeuristicImportance(double heuristicImportance)
107 | {
108 | this.heuristicImportance = heuristicImportance;
109 | }
110 |
111 | @API public void setCanUseDiagonalMovement(boolean canUseDiagonalMovement)
112 | {
113 | this.canUseDiagonalMovement = canUseDiagonalMovement;
114 | }
115 |
116 | @API public void setCanUseLadders(boolean canUseLadders)
117 | {
118 | this.canUseLadders = canUseLadders;
119 | }
120 |
121 |
122 | // PATHFINDING
123 | @API public void findPath()
124 | {
125 | // don't set the start time if it already exists, this way duration of retries are counted together
126 | if(this.pathfindingStartNano == 0)
127 | this.pathfindingStartNano = System.nanoTime();
128 |
129 | // validation
130 | if(this.startLocation.getWorld() != this.endLocation.getWorld())
131 | throw new IllegalArgumentException("The start and the end location are not in the same world!");
132 |
133 | // preparation
134 | BlockPathNode startNode = new BlockPathNode(this.startLocation.getBlockX(),
135 | this.startLocation.getBlockY(), this.startLocation.getBlockZ());
136 | // this is needed in case the start and end nodes are the same, so the transition type is set
137 | startNode.setParent(null, TransitionType.WALK, 0);
138 |
139 | this.endNode = new BlockPathNode(this.endLocation.getBlockX(), this.endLocation.getBlockY(),
140 | this.endLocation.getBlockZ());
141 |
142 | this.unvisitedNodes.addSorted(startNode);
143 |
144 | // pathfinding
145 | visitNodes();
146 |
147 |
148 | // pathfinding finalization
149 | // if the end node has a parent, a pathfinding has been found
150 | if(this.endNode.getParent() != null || startNode.equals(this.endNode))
151 | this.path = new BlockPath(this.endNode);
152 | else
153 | // this looks through the provided options and checks if an ability of the pathfinder is deactivated,
154 | // if so it activates it and reruns the pathfinding. if there are no other options available, it returns
155 | retry();
156 |
157 | this.pathfindingEndNano = System.nanoTime();
158 | }
159 |
160 | protected void visitNodes()
161 | {
162 | while(true)
163 | {
164 | if(this.unvisitedNodes.getSize() == 0)
165 | {
166 | // no unvisited nodes left, nowhere else to go ...
167 | this.failure = "No unvisted nodes left";
168 | break;
169 | }
170 |
171 | if(this.visitedNodes.size() >= this.maxNodeVisits)
172 | {
173 | // reached limit of nodes to search
174 | this.failure = "Number of nodes visited exceeds maximum";
175 | break;
176 | }
177 |
178 | BlockPathNode nodeToVisit = this.unvisitedNodes.getAndRemoveFirst();
179 | this.visitedNodes.add(nodeToVisit);
180 |
181 | // pathing reached end node
182 | if(isTargetReached(nodeToVisit))
183 | {
184 | this.endNode = nodeToVisit;
185 | break;
186 | }
187 |
188 | visitNode(nodeToVisit);
189 | }
190 | }
191 |
192 | protected boolean isTargetReached(BlockPathNode nodeToVisit)
193 | {
194 | return nodeToVisit.equals(this.endNode);
195 | }
196 |
197 |
198 | protected void visitNode(BlockPathNode node)
199 | {
200 | lookForWalkableNodes(node);
201 |
202 | if(this.useLadders)
203 | lookForLadderNodes(node);
204 | }
205 |
206 | protected void lookForWalkableNodes(BlockPathNode node)
207 | {
208 | for(int dX = -1; dX <= 1; dX++)
209 | for(int dZ = -1; dZ <= 1; dZ++)
210 | for(int dY = -1; dY <= 1; dY++)
211 | validateNodeOffset(node, dX, dY, dZ);
212 | }
213 |
214 | protected void validateNodeOffset(BlockPathNode node, int dX, int dY, int dZ)
215 | {
216 | // prevent movement to the same position
217 | if(dX == 0 && dY == 0 && dZ == 0)
218 | return;
219 |
220 | // prevent diagonal movement if specified
221 | if(!this.moveDiagonally && dX*dZ != 0)
222 | return;
223 |
224 | // prevent diagonal movement at the same time as moving up and down
225 | if(dX*dZ != 0 && dY != 0)
226 | return;
227 |
228 |
229 | BlockPathNode newNode = new BlockPathNode(node.x+dX, node.y+dY, node.z+dZ);
230 |
231 | if(doesNodeAlreadyExist(newNode))
232 | return;
233 |
234 | // check if player can stand at new node
235 | if(!isValid(newNode))
236 | return;
237 |
238 | // check if the diagonal movement is not prevented by blocks to the side
239 | if(dX*dZ != 0 && !isDiagonalMovementPossible(node, dX, dZ))
240 | return;
241 |
242 | // check if the player hits his head when going up/down
243 | if(dY == 1 && !isBlockUnobstructed(node.getLocation(this.startLocation.getWorld()).add(0, 2, 0)))
244 | return;
245 |
246 | if(dY == -1 && !isBlockUnobstructed(newNode.getLocation(this.startLocation.getWorld()).add(0, 2, 0)))
247 | return;
248 |
249 |
250 | // get transition type (walk up stairs, jump up blocks)
251 | int transitionType = TransitionType.WALK;
252 | if(dY == 1)
253 | {
254 | boolean isStair = StairEvaluator.isStair(node, newNode, this.startLocation.getWorld());
255 | if(!isStair)
256 | transitionType = TransitionType.JUMP;
257 | }
258 |
259 |
260 | // calculate weight
261 | // TODO punish 90° turns
262 | int sumAbs = Math.abs(dX)+Math.abs(dY)+Math.abs(dZ);
263 | double weight = 1;
264 | if(sumAbs == 2)
265 | weight = 1.41;
266 | else if(sumAbs == 3)
267 | weight = 1.73;
268 |
269 | // punish jumps to favor stair climbing
270 | if(transitionType == TransitionType.JUMP)
271 | weight += 0.5;
272 |
273 |
274 | // actually add the node to the pool
275 | newNode.setParent(node, transitionType, weight);
276 | addNode(newNode);
277 | }
278 |
279 | protected void lookForLadderNodes(BlockPathNode node)
280 | {
281 | Location feetLocation = node.getLocation(this.startLocation.getWorld());
282 |
283 | for(int dY = -1; dY <= 1; dY++)
284 | {
285 | Location location = feetLocation.clone().add(0, dY, 0);
286 | if(location.getBlock().getType() != Material.LADDER)
287 | continue;
288 |
289 | BlockPathNode newNode = new BlockPathNode(node.x, node.y+dY, node.z);
290 | newNode.setParent(node, TransitionType.CLIMB, CLIMBING_EXPENSE);
291 |
292 | if(doesNodeAlreadyExist(newNode))
293 | continue;
294 |
295 | addNode(newNode);
296 | }
297 | }
298 |
299 |
300 | protected boolean doesNodeAlreadyExist(BlockPathNode node)
301 | {
302 | if(this.visitedNodes.contains(node))
303 | return true;
304 |
305 | return this.unvisitedNodes.contains(node);
306 |
307 | }
308 |
309 | protected void addNode(BlockPathNode node)
310 | {
311 | node.setHeuristicWeight(getHeuristicWeight(node)*this.heuristicImportance);
312 | this.unvisitedNodes.addSorted(node);
313 | }
314 |
315 |
316 | // NODE VALIDATION
317 | protected boolean isValid(BlockPathNode node)
318 | {
319 | return canStandAt(node.getLocation(this.startLocation.getWorld()));
320 | }
321 |
322 | protected boolean isDiagonalMovementPossible(BlockPathNode node, int dX, int dZ)
323 | {
324 | if(!isUnobstructed(node.getLocation(this.startLocation.getWorld()).clone().add(dX, 0, 0)))
325 | return false;
326 |
327 | return isUnobstructed(node.getLocation(this.startLocation.getWorld()).clone().add(0, 0, dZ));
328 |
329 | }
330 |
331 | @SuppressWarnings("deprecation") protected boolean canStandAt(Location feetLocation)
332 | {
333 | if(!isUnobstructed(feetLocation))
334 | return false;
335 |
336 | return MaterialEvaluator.canStandOn(feetLocation.clone().add(0, -1, 0).getBlock().getTypeId());
337 |
338 | }
339 |
340 | protected boolean isUnobstructed(Location feetLocation)
341 | {
342 | if(!isBlockUnobstructed(feetLocation))
343 | return false;
344 |
345 | return isBlockUnobstructed(feetLocation.clone().add(0, 1, 0));
346 |
347 | }
348 |
349 |
350 | // LOCATION VALIDATION
351 | @SuppressWarnings("deprecation") protected boolean isBlockUnobstructed(Location location)
352 | {
353 | return MaterialEvaluator.canStandIn(location.getBlock().getTypeId());
354 | }
355 |
356 |
357 | // HEURISTIC
358 | protected double getHeuristicWeight(BlockPathNode node)
359 | {
360 | return getEuclideanDistance(node);
361 | }
362 |
363 | @SuppressWarnings("unused") protected double getManhattanDistance(BlockPathNode node)
364 | {
365 | return Math.abs(node.x-this.endNode.x)+Math.abs(node.y-this.endNode.y)+Math.abs(node.z-this.endNode.z);
366 | }
367 |
368 | protected double getEuclideanDistance(BlockPathNode node)
369 | {
370 | int dX = this.endNode.x-node.x;
371 | int dY = this.endNode.y-node.y;
372 | int dZ = this.endNode.z-node.z;
373 |
374 | return Math.sqrt(dX*dX+dY*dY+dZ*dZ);
375 | }
376 |
377 |
378 | // RETRY
379 | protected void retry()
380 | {
381 | lookForReason:
382 | {
383 | // the special abilities are sorted by likelyhood of appearance to further speed up the pathfinding
384 |
385 | if(this.canUseDiagonalMovement && !this.moveDiagonally)
386 | {
387 | this.moveDiagonally = true;
388 | break lookForReason;
389 | }
390 |
391 | if(this.canUseLadders && !this.useLadders)
392 | {
393 | this.useLadders = true;
394 | break lookForReason;
395 | }
396 |
397 | return;
398 | }
399 |
400 | reset();
401 | findPath();
402 | }
403 |
404 | protected void reset()
405 | {
406 | this.endNode = null;
407 |
408 | this.unvisitedNodes.clear();
409 | this.visitedNodes.clear();
410 |
411 | this.path = null;
412 | this.failure = null;
413 | }
414 |
415 | }
416 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/block/BlockPathSmoother.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.block;
2 |
3 | import de.domisum.lib.auxilium.data.container.math.Vector3D;
4 | import de.domisum.lib.compitum.path.node.BlockPathNode;
5 | import de.domisum.lib.compitum.path.BlockPath;
6 | import de.domisum.lib.compitum.path.Path;
7 | import de.domisum.lib.compitum.path.PathWaypoint;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | public class BlockPathSmoother
13 | {
14 |
15 | // INPUT
16 | private BlockPath blockPath;
17 |
18 | // OUTPUT
19 | private Path smoothPath;
20 |
21 |
22 | // INIT
23 | public BlockPathSmoother(BlockPath blockPath)
24 | {
25 | this.blockPath = blockPath;
26 | }
27 |
28 |
29 | // GETTERS
30 | public Path getSmoothPath()
31 | {
32 | return this.smoothPath;
33 | }
34 |
35 |
36 | // CONVERSION
37 | public void convert()
38 | {
39 | List pathWaypoints = new ArrayList<>();
40 | for(BlockPathNode blockPathNode : this.blockPath.getNodes())
41 | pathWaypoints.add(new PathWaypoint(new Vector3D(blockPathNode.x+0.5, blockPathNode.y, blockPathNode.z+0.5),
42 | blockPathNode.getTransitionType()));
43 |
44 | this.smoothPath = new Path(pathWaypoints);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/block/evaluator/MaterialEvaluator.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.block.evaluator;
2 |
3 | import org.bukkit.Material;
4 |
5 | @SuppressWarnings("deprecation")
6 | public class MaterialEvaluator
7 | {
8 |
9 | // REFERENCES
10 | private static boolean[] canStandOn;
11 | private static boolean[] canStandIn;
12 |
13 | // STATUS
14 | private static boolean ready = false;
15 |
16 |
17 | // INIT
18 | public static void prepareEvaluation()
19 | {
20 | int maxId = -1;
21 | for(Material mat : Material.values())
22 | if(mat.getId() > maxId)
23 | maxId = mat.getId();
24 |
25 | canStandOn = new boolean[maxId+1]; // value shift
26 | canStandIn = new boolean[maxId+1]; // value shift
27 |
28 | for(Material mat : Material.values())
29 | {
30 | int id = mat.getId();
31 |
32 | // general values
33 | canStandOn[id] = mat.isSolid();
34 | canStandIn[id] = !mat.isSolid();
35 |
36 | // you cannot stand on fences -> walking over fences not possible
37 | if(mat.name().contains("FENCE"))
38 | canStandOn[id] = false;
39 |
40 | // standing in signs is possible
41 | if(mat == Material.SIGN_POST)
42 | {
43 | canStandIn[id] = true;
44 | canStandOn[id] = false;
45 | }
46 | }
47 |
48 | ready = true;
49 | }
50 |
51 |
52 | // EVALUATION
53 | public static boolean canStandOn(int materialID)
54 | {
55 | if(!ready)
56 | throw new IllegalStateException("CompitumLib has to be anabled before usage!");
57 |
58 | return canStandOn[materialID];
59 | }
60 |
61 | public static boolean canStandIn(int materialID)
62 | {
63 | if(!ready)
64 | throw new IllegalStateException("CompitumLib has to be anabled before usage!");
65 |
66 | return canStandIn[materialID];
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/block/evaluator/StairEvaluator.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.block.evaluator;
2 |
3 | import de.domisum.lib.compitum.path.node.BlockPathNode;
4 | import org.bukkit.Location;
5 | import org.bukkit.Material;
6 | import org.bukkit.World;
7 | import org.bukkit.block.Block;
8 |
9 | import java.util.Arrays;
10 |
11 | @SuppressWarnings("deprecation")
12 | public class StairEvaluator
13 | {
14 |
15 | // REFERENCES
16 | /**
17 | * directions:
18 | * 0: +x
19 | * 1: +z
20 | * 2: -x
21 | * 3: -z
22 | */
23 | private static boolean[][][] isStair; //
24 |
25 | // STATUS
26 | private static boolean ready = false;
27 |
28 |
29 | // INIT
30 | public static void prepareEvaluation()
31 | {
32 | int maxId = -1;
33 | for(Material mat : Material.values())
34 | if(mat.getId() > maxId)
35 | maxId = mat.getId();
36 |
37 | int subIds = 16;
38 | isStair = new boolean[maxId+1][subIds][4];
39 |
40 | // general
41 | boolean[] none = new boolean[4];
42 | boolean[] all4 = new boolean[4];
43 | Arrays.fill(all4, true);
44 |
45 | boolean[][] noneForEverySubId = new boolean[subIds][4];
46 | Arrays.fill(noneForEverySubId, none);
47 |
48 | // slabs
49 | boolean[][] slabDefault = new boolean[subIds][4];
50 | for(int i = 0; i < 8; i++)
51 | slabDefault[i] = all4;
52 | for(int i = 8; i < subIds; i++)
53 | slabDefault[i] = none;
54 |
55 | // stairs
56 | boolean[] posX = new boolean[4];
57 | posX[0] = true;
58 | boolean[] posZ = new boolean[4];
59 | posZ[1] = true;
60 | boolean[] negX = new boolean[4];
61 | negX[2] = true;
62 | boolean[] negZ = new boolean[4];
63 | negZ[3] = true;
64 |
65 | boolean[][] stairDefault = new boolean[subIds][4];
66 | stairDefault[0] = posX;
67 | stairDefault[8] = posX;
68 | stairDefault[2] = posZ;
69 | stairDefault[2+8] = posZ;
70 | stairDefault[1] = negX;
71 | stairDefault[1+8] = negX;
72 | stairDefault[3] = negZ;
73 | stairDefault[3+8] = negZ;
74 | for(int i = 4; i < 8; i++)
75 | {
76 | stairDefault[i] = none;
77 | stairDefault[i+8] = none;
78 | }
79 |
80 | // looping over all materials
81 | for(Material mat : Material.values())
82 | {
83 | if(mat == Material.STEP || mat == Material.STONE_SLAB2 || mat == Material.WOOD_STEP || mat == Material.PURPUR_SLAB)
84 | isStair[mat.getId()] = slabDefault;
85 | else if(mat.name().endsWith("STAIRS"))
86 | isStair[mat.getId()] = stairDefault;
87 | else
88 | isStair[mat.getId()] = noneForEverySubId;
89 | }
90 |
91 | ready = true;
92 | }
93 |
94 |
95 | // EVALUATION
96 | public static boolean isStair(BlockPathNode from, BlockPathNode to, World world)
97 | {
98 | if(!ready)
99 | throw new IllegalStateException("CompitumLib has to be enabled before usage!");
100 |
101 | Location stairLocation = to.getLocation(world).add(0, -1, 0);
102 | Block stairBlock = stairLocation.getBlock();
103 | int stairBlockTypeId = stairBlock.getType().getId();
104 | int stairBlockSubId = stairBlock.getData();
105 |
106 | boolean[] directions = isStair[stairBlockTypeId][stairBlockSubId];
107 | int dX = to.x-from.x;
108 | int dZ = to.z-from.z;
109 |
110 | if(dX == 1 && directions[0])
111 | return true;
112 | if(dZ == 1 && directions[1])
113 | return true;
114 | if(dX == -1 && directions[2])
115 | return true;
116 | if(dZ == -1 && directions[3])
117 | return true;
118 |
119 | return false;
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/NavMesh.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh;
2 |
3 | import de.domisum.lib.auxilium.data.container.direction.Direction2D;
4 | import de.domisum.lib.auxilium.data.container.math.Vector3D;
5 | import de.domisum.lib.auxilium.util.java.annotations.API;
6 | import de.domisum.lib.auxilium.util.keys.Base64Key;
7 | import de.domisum.lib.auxiliumspigot.util.LocationUtil;
8 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshPoint;
9 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshTriangle;
10 | import de.domisum.lib.compitum.navmesh.transition.NavMeshLadder;
11 | import de.domisum.lib.compitum.navmesh.transition.NavMeshTrianglePortal;
12 | import org.bukkit.Location;
13 | import org.bukkit.World;
14 |
15 | import java.util.Collection;
16 | import java.util.HashMap;
17 | import java.util.HashSet;
18 | import java.util.Map;
19 | import java.util.Objects;
20 | import java.util.Set;
21 |
22 | public class NavMesh
23 | {
24 |
25 | // CONSTANTS
26 | private static final int KEY_LENGTH = 5;
27 |
28 | // PROPERTIES
29 | private String id;
30 | private Vector3D rangeCenter;
31 | private double range;
32 |
33 | // REFERENCES
34 | private World world;
35 | private Map points = new HashMap<>(); //
36 | private Map triangles = new HashMap<>(); //
37 |
38 |
39 | // INIT
40 | public NavMesh(String id, Vector3D ranceCenter, double range, World world, Collection points,
41 | Collection triangles)
42 | {
43 | this.id = id;
44 | this.rangeCenter = ranceCenter;
45 | this.range = range;
46 |
47 | this.world = world;
48 | for(NavMeshPoint p : points)
49 | this.points.put(p.getId(), p);
50 | for(NavMeshTriangle t : triangles)
51 | this.triangles.put(t.id, t);
52 |
53 | fillInNeighbors();
54 | determineHeuristicTriangleCenters();
55 | }
56 |
57 |
58 | // GETTERS
59 | // general
60 | @API public String getId()
61 | {
62 | return this.id;
63 | }
64 |
65 | public Vector3D getRangeCenter()
66 | {
67 | return this.rangeCenter;
68 | }
69 |
70 | public double getRange()
71 | {
72 | return this.range;
73 | }
74 |
75 | protected boolean isInRange(Location location)
76 | {
77 | if(location.getWorld() != this.world)
78 | return false;
79 |
80 | return LocationUtil.toVector3D(location).distanceToSquared(this.rangeCenter) < this.range*this.range;
81 | }
82 |
83 | public World getWorld()
84 | {
85 | return this.world;
86 | }
87 |
88 |
89 | // poin
90 | public Collection getPoints()
91 | {
92 | return this.points.values();
93 | }
94 |
95 | private NavMeshPoint getPoint(String id)
96 | {
97 | return this.points.get(id);
98 | }
99 |
100 |
101 | // triangle
102 | public Collection getTriangles()
103 | {
104 | return this.triangles.values();
105 | }
106 |
107 | @API public Set getTrianglesUsingPoint(NavMeshPoint point)
108 | {
109 | Set trianglesUsingPoint = new HashSet<>();
110 |
111 | for(NavMeshTriangle triangle : this.triangles.values())
112 | if(triangle.isUsingPoint(point))
113 | trianglesUsingPoint.add(triangle);
114 |
115 | return trianglesUsingPoint;
116 | }
117 |
118 |
119 | public NavMeshTriangle getTriangle(String id)
120 | {
121 | return this.triangles.get(id);
122 | }
123 |
124 | public NavMeshTriangle getTriangleAt(Location location)
125 | {
126 | // TODO optimize this, might become bottleneck with thousands of triangles
127 |
128 | for(NavMeshTriangle triangle : this.triangles.values())
129 | if(triangle.doesContain(location))
130 | return triangle;
131 |
132 | return null;
133 | }
134 |
135 |
136 | // POINT
137 | @API public NavMeshPoint createPoint(double x, double y, double z)
138 | {
139 | NavMeshPoint point = new NavMeshPoint(getUnusedId(), x, y, z);
140 |
141 | this.points.put(point.getId(), point);
142 | return point;
143 | }
144 |
145 | @API public void removePoint(NavMeshPoint point)
146 | {
147 | for(NavMeshTriangle t : getTrianglesUsingPoint(point))
148 | deleteTriangle(t);
149 |
150 | this.points.remove(point.getId());
151 | }
152 |
153 |
154 | // TRIANGLE
155 | @API public NavMeshTriangle createTriangle(NavMeshPoint point1, NavMeshPoint point2, NavMeshPoint point3)
156 | {
157 | NavMeshTriangle triangle = new NavMeshTriangle(getUnusedId(), point1, point2, point3);
158 | this.triangles.put(triangle.id, triangle);
159 |
160 | fillInNeighborsFor(triangle);
161 |
162 | return triangle;
163 | }
164 |
165 | @API public void deleteTriangle(NavMeshTriangle triangle)
166 | {
167 | this.triangles.remove(triangle.id);
168 | triangle.clearNeighbors();
169 | }
170 |
171 |
172 | // LADDER
173 | @API public void createLadder(NavMeshTriangle triangle1, Vector3D position1, NavMeshTriangle triangle2,
174 | Vector3D position2, Direction2D ladderDirection)
175 | {
176 | NavMeshLadder ladder;
177 | if(position1.y < position2.y)
178 | ladder = new NavMeshLadder(triangle1, position1, triangle2, position2, ladderDirection);
179 | else
180 | ladder = new NavMeshLadder(triangle2, position2, triangle1, position1, ladderDirection);
181 |
182 | triangle1.makeNeighbors(triangle2, ladder);
183 | }
184 |
185 | @API public void removeLadder(NavMeshLadder ladder)
186 | {
187 | ladder.getTriangleBottom().removeNeighbor(ladder.getTriangleTop());
188 | }
189 |
190 |
191 | // PATHFINDING
192 | private void fillInNeighbors()
193 | {
194 | for(NavMeshTriangle triangle : this.triangles.values())
195 | fillInNeighborsFor(triangle);
196 | }
197 |
198 | private void fillInNeighborsFor(NavMeshTriangle triangle)
199 | {
200 | for(NavMeshTriangle t : this.triangles.values())
201 | {
202 | if(Objects.equals(t, triangle))
203 | continue;
204 |
205 | Set commonPoints = getCommonPoints(triangle, t);
206 |
207 | if(commonPoints.size() == 2)
208 | {
209 | NavMeshTrianglePortal portal = new NavMeshTrianglePortal(triangle, t, commonPoints);
210 | triangle.makeNeighbors(t, portal);
211 | }
212 | }
213 | }
214 |
215 |
216 | private void determineHeuristicTriangleCenters()
217 | {
218 | for(int i = 0; i < 20; i++)
219 | reduceHeuristicCenterDistances(0.1);
220 | }
221 |
222 | private void reduceHeuristicCenterDistances(double factor)
223 | {
224 | for(NavMeshTriangle triangle : this.triangles.values())
225 | {
226 | Set neighbors = triangle.neighbors.keySet();
227 | if(neighbors.size() <= 1)
228 | continue;
229 |
230 | Vector3D neighborSum = new Vector3D();
231 | for(NavMeshTriangle n : neighbors)
232 | neighborSum = neighborSum.add(n.getHeuristicCenter());
233 |
234 | Vector3D neighborAverage = neighborSum.divide(neighbors.size());
235 | Vector3D currentHeuristicCenter = triangle.getHeuristicCenter();
236 | Vector3D fromCurrentToAverage = neighborAverage.subtract(currentHeuristicCenter);
237 |
238 | Vector3D newHeuristicCenter = currentHeuristicCenter
239 | .moveTowards(neighborAverage, fromCurrentToAverage.length()*factor);
240 | if(triangle.doesContain(newHeuristicCenter))
241 | triangle.setHeuristicCenter(newHeuristicCenter);
242 | }
243 | }
244 |
245 |
246 | // UTIL
247 | private String getUnusedId()
248 | {
249 | String id;
250 | do
251 | id = Base64Key.generate(KEY_LENGTH);
252 | while(getPoint(id) != null || getTriangle(id) != null);
253 |
254 | return id;
255 | }
256 |
257 | private Set getCommonPoints(NavMeshTriangle triangle1, NavMeshTriangle triangle2)
258 | {
259 | NavMeshPoint[] triangle1Points = new NavMeshPoint[] {triangle1.point1, triangle1.point2, triangle1.point3};
260 | NavMeshPoint[] triangle2Points = new NavMeshPoint[] {triangle2.point1, triangle2.point2, triangle2.point3};
261 |
262 | Set commonPoints = new HashSet<>();
263 | for(NavMeshPoint t1p : triangle1Points)
264 | for(NavMeshPoint t2p : triangle2Points)
265 | if(Objects.equals(t1p, t2p))
266 | commonPoints.add(t1p);
267 |
268 | return commonPoints;
269 | }
270 |
271 | }
272 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/NavMeshManager.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh;
2 |
3 | import de.domisum.lib.auxilium.util.FileUtil;
4 | import de.domisum.lib.auxilium.util.json.GsonUtil;
5 | import de.domisum.lib.compitum.CompitumLib;
6 | import de.domisum.lib.compitum.navmesh.json.SerializationNavMesh;
7 | import org.bukkit.Location;
8 |
9 | import java.io.File;
10 | import java.util.HashSet;
11 | import java.util.Set;
12 |
13 | public class NavMeshManager
14 | {
15 |
16 | // CONSTANTS
17 | private static final String NAV_MESHES_DIRECTORY = "navMeshes";
18 | private static final String NAV_MESH_FILE_EXTENSION = ".navMesh.json";
19 |
20 | // REFERENCES
21 | private Set meshes = new HashSet<>();
22 |
23 |
24 | /*
25 | // INITIALIZATION
26 | */
27 | public void initiialize()
28 | {
29 | loadMeshes();
30 | }
31 |
32 | public void terminate()
33 | {
34 | // don't save the meshes by default
35 | }
36 |
37 |
38 | // LOADING
39 | private void loadMeshes()
40 | {
41 | CompitumLib.getLogger().info("Loading NavMeshs...");
42 |
43 | File baseDir = new File(NAV_MESHES_DIRECTORY);
44 | // noinspection ResultOfMethodCallIgnored
45 | baseDir.mkdirs();
46 |
47 | for(File file : FileUtil.listFilesRecursively(baseDir, FileUtil.FileType.FILE))
48 | {
49 | if(!file.getName().endsWith(NAV_MESH_FILE_EXTENSION))
50 | {
51 | CompitumLib
52 | .getLogger()
53 | .warning("The file '"+file.getAbsolutePath()
54 | +" in the NavMesh directory was skipped since it doesn't end with '"+NAV_MESH_FILE_EXTENSION
55 | +"'.");
56 | continue;
57 | }
58 |
59 | String navMeshId = FileUtil.getNameWithoutCompositeExtension(file);
60 |
61 | String content = FileUtil.readString(file);
62 | NavMesh navMesh;
63 | try
64 | {
65 | SerializationNavMesh serializationNavMesh = GsonUtil.get().fromJson(content, SerializationNavMesh.class);
66 | navMesh = serializationNavMesh.convertToNavMesh(navMeshId);
67 | this.meshes.add(navMesh);
68 |
69 | CompitumLib
70 | .getLogger()
71 | .info("Loaded NavMesh '"+navMesh.getId()+"' with "+navMesh.getTriangles().size()+" triangles");
72 | }
73 | catch(Exception e)
74 | {
75 | e.printStackTrace();
76 | }
77 | }
78 |
79 | CompitumLib.getLogger().info("Loading NavMeshes complete: loaded "+this.meshes.size()+" NavMesh(es)");
80 | }
81 |
82 |
83 | // SAVING
84 | public void saveMeshes()
85 | {
86 | CompitumLib.getLogger().info("Saving NavMeshs...");
87 |
88 | File baseDir = new File(NAV_MESHES_DIRECTORY);
89 | // noinspection ResultOfMethodCallIgnored
90 | baseDir.mkdirs();
91 |
92 | for(NavMesh navMesh : this.meshes)
93 | {
94 | File file = new File(baseDir, navMesh.getId()+NAV_MESH_FILE_EXTENSION);
95 |
96 | try
97 | {
98 | SerializationNavMesh sNavMesh = new SerializationNavMesh(navMesh);
99 | String json = GsonUtil.getPretty().toJson(sNavMesh);
100 |
101 | FileUtil.writeString(file, json);
102 | }
103 | catch(Exception e)
104 | {
105 | e.printStackTrace();
106 | }
107 | }
108 |
109 | CompitumLib.getLogger().info("Saving NavMeshs complete: saved "+this.meshes.size()+" NavMesh(s)");
110 | }
111 |
112 |
113 | // GETTERS
114 | public NavMesh getNavMeshAt(Location location)
115 | {
116 | for(NavMesh g : this.meshes)
117 | if(g.isInRange(location))
118 | return g;
119 |
120 | return null;
121 | }
122 |
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/geometry/NavMeshPoint.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh.geometry;
2 |
3 | import de.domisum.lib.auxilium.data.container.math.Vector3D;
4 | import de.domisum.lib.auxilium.util.java.annotations.DeserializationNoArgsConstructor;
5 | import de.domisum.lib.auxilium.util.math.MathUtil;
6 |
7 | public class NavMeshPoint
8 | {
9 |
10 | // PROPERTIES
11 | private final String id;
12 |
13 | private double x;
14 | private double y;
15 | private double z;
16 |
17 |
18 | // INIT
19 | @DeserializationNoArgsConstructor public NavMeshPoint()
20 | {
21 | this.id = "";
22 | }
23 |
24 | public NavMeshPoint(String id, double x, double y, double z)
25 | {
26 | this.id = id;
27 |
28 | this.x = MathUtil.round(x, 2);
29 | this.y = MathUtil.round(y, 2);
30 | this.z = MathUtil.round(z, 2);
31 | }
32 |
33 | @Override public boolean equals(Object o)
34 | {
35 | if(!(o instanceof NavMeshPoint))
36 | return false;
37 |
38 | NavMeshPoint other = (NavMeshPoint) o;
39 | return this.id.equals(other.id);
40 | }
41 |
42 | @Override public int hashCode()
43 | {
44 | return this.id.hashCode();
45 | }
46 |
47 |
48 | // GETTERS
49 | public String getId()
50 | {
51 | return this.id;
52 | }
53 |
54 |
55 | public double getX()
56 | {
57 | return this.x;
58 | }
59 |
60 | public double getY()
61 | {
62 | return this.y;
63 | }
64 |
65 | public double getZ()
66 | {
67 | return this.z;
68 | }
69 |
70 | public Vector3D getPositionVector()
71 | {
72 | return new Vector3D(this.x, this.y, this.z);
73 | }
74 |
75 |
76 | // SETTERS
77 | public void setX(double x)
78 | {
79 | this.x = x;
80 | }
81 |
82 | public void setY(double y)
83 | {
84 | this.y = y;
85 | }
86 |
87 | public void setZ(double z)
88 | {
89 | this.z = z;
90 | }
91 |
92 | private void setLocation(double x, double y, double z)
93 | {
94 | this.x = x;
95 | this.y = y;
96 | this.z = z;
97 | }
98 |
99 | public void setLocation(Vector3D location)
100 | {
101 | setLocation(location.x, location.y, location.z);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/geometry/NavMeshTriangle.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh.geometry;
2 |
3 | import de.domisum.lib.auxilium.data.container.math.Vector3D;
4 | import de.domisum.lib.auxiliumspigot.util.LocationUtil;
5 | import de.domisum.lib.compitum.CompitumLib;
6 | import de.domisum.lib.compitum.navmesh.transition.NavMeshTriangleTransition;
7 | import lombok.Setter;
8 | import org.bukkit.Location;
9 |
10 | import java.util.HashMap;
11 | import java.util.HashSet;
12 | import java.util.Map;
13 | import java.util.Objects;
14 |
15 | public class NavMeshTriangle
16 | {
17 |
18 | // CONSTANTS
19 | private static final double CONTAINS_TOLERANCE = 0.00001;
20 |
21 | // PROPERTIES
22 | public final String id;
23 |
24 | public final NavMeshPoint point1;
25 | public final NavMeshPoint point2;
26 | public final NavMeshPoint point3;
27 |
28 | public final Map neighbors = new HashMap<>();
29 |
30 | // STATUS
31 | @Setter private Vector3D heuristicCenter;
32 |
33 |
34 | // INIT
35 | public NavMeshTriangle(String id, NavMeshPoint point1, NavMeshPoint point2, NavMeshPoint point3)
36 | {
37 | this.id = id;
38 |
39 | this.point1 = point1;
40 | this.point2 = point2;
41 | this.point3 = point3;
42 | }
43 |
44 | @Override public String toString()
45 | {
46 | return "triangle["+this.point1.getId()+","+this.point2.getId()+","+this.point3.getId()+"]";
47 | }
48 |
49 | @Override public int hashCode()
50 | {
51 | return this.id.hashCode();
52 | }
53 |
54 | @Override public boolean equals(Object o)
55 | {
56 | if(this == o)
57 | return true;
58 | if(o == null || getClass() != o.getClass())
59 | return false;
60 |
61 | NavMeshTriangle that = (NavMeshTriangle) o;
62 |
63 | if(!this.id.equals(that.id))
64 | return false;
65 |
66 | return true;
67 | }
68 |
69 |
70 | // GETTERS
71 | // intrinsic
72 | public Vector3D getCenter()
73 | {
74 | Vector3D sum = this.point1.getPositionVector().add(this.point2.getPositionVector().add(this.point3.getPositionVector()));
75 | return sum.divide(3);
76 | }
77 |
78 | public Vector3D getHeuristicCenter()
79 | {
80 | if(this.heuristicCenter == null)
81 | return getCenter();
82 |
83 | return this.heuristicCenter;
84 | }
85 |
86 |
87 | // relational
88 | public boolean isUsingPoint(NavMeshPoint point)
89 | {
90 | return this.point1 == point || this.point2 == point || this.point3 == point;
91 | }
92 |
93 | public NavMeshTriangleTransition getTransitionTo(NavMeshTriangle other)
94 | {
95 | return this.neighbors.get(other);
96 | }
97 |
98 |
99 | // world
100 | public boolean doesContain(Location location)
101 | {
102 | return doesContain(LocationUtil.toVector3D(location));
103 | }
104 |
105 | public boolean doesContain(Vector3D point)
106 | {
107 | if(Math.abs(getCenter().y-point.y) >= 2)
108 | return false;
109 |
110 | Vector3D a = this.point1.getPositionVector();
111 | Vector3D b = this.point2.getPositionVector();
112 | Vector3D c = this.point3.getPositionVector();
113 |
114 | double ab = sign(point, a, b);
115 | double bc = sign(point, b, c);
116 | double ca = sign(point, c, a);
117 |
118 | if(Math.abs(ab) < CONTAINS_TOLERANCE)
119 | {
120 | if(Math.abs(bc) < CONTAINS_TOLERANCE)
121 | return true;
122 | else if(Math.abs(ca) < CONTAINS_TOLERANCE)
123 | return true;
124 |
125 | return bc < 0 == ca < 0;
126 | }
127 | else if(Math.abs(bc) < CONTAINS_TOLERANCE)
128 | {
129 | if(Math.abs(ab) < CONTAINS_TOLERANCE)
130 | return true;
131 | else if(Math.abs(ca) < CONTAINS_TOLERANCE)
132 | return true;
133 |
134 | return ab < 0 == ca < 0;
135 | }
136 | else if(Math.abs(ca) < CONTAINS_TOLERANCE)
137 | {
138 | if(Math.abs(ab) < CONTAINS_TOLERANCE)
139 | return true;
140 | else if(Math.abs(bc) < CONTAINS_TOLERANCE)
141 | return true;
142 |
143 | return ab < 0 == bc < 0;
144 | }
145 |
146 | return (ab < 0 == bc < 0) && (bc < 0 == ca < 0);
147 | }
148 |
149 |
150 | // NEIGHBORS
151 | public void makeNeighbors(NavMeshTriangle other, NavMeshTriangleTransition transition)
152 | {
153 | if(this.neighbors.containsKey(other))
154 | {
155 | NavMeshTriangleTransition currentTransition = this.neighbors.get(other);
156 | if(Objects.equals(currentTransition, transition))
157 | CompitumLib.getLogger()
158 | .warning("Overridden NavMesh neighbor: "+currentTransition+"' overridden by '"+transition+"'");
159 | }
160 |
161 | this.neighbors.put(other, transition);
162 | other.neighbors.put(this, transition);
163 | }
164 |
165 | public void removeNeighbor(NavMeshTriangle other)
166 | {
167 | this.neighbors.remove(other);
168 | other.neighbors.remove(this);
169 | }
170 |
171 | public void clearNeighbors()
172 | {
173 | for(NavMeshTriangle n : new HashSet<>(this.neighbors.keySet()))
174 | removeNeighbor(n);
175 | }
176 |
177 |
178 | // UTIL
179 | private double sign(Vector3D p1, Vector3D p2, Vector3D p3)
180 | {
181 | return (p1.x-p3.x)*(p2.z-p3.z)-(p2.x-p3.x)*(p1.z-p3.z);
182 | }
183 |
184 | }
185 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/json/SerializationNavMesh.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh.json;
2 |
3 | import de.domisum.lib.auxilium.data.container.math.Vector3D;
4 | import de.domisum.lib.auxilium.util.java.annotations.DeserializationNoArgsConstructor;
5 | import de.domisum.lib.auxilium.util.java.annotations.InitByDeserialization;
6 | import de.domisum.lib.compitum.navmesh.NavMesh;
7 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshPoint;
8 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshTriangle;
9 | import de.domisum.lib.compitum.navmesh.transition.NavMeshLadder;
10 | import de.domisum.lib.compitum.navmesh.transition.NavMeshTriangleTransition;
11 | import org.bukkit.Bukkit;
12 |
13 | import java.util.ArrayList;
14 | import java.util.Comparator;
15 | import java.util.HashSet;
16 | import java.util.List;
17 | import java.util.Set;
18 |
19 | public class SerializationNavMesh
20 | {
21 |
22 | // PROPERTIES
23 | @InitByDeserialization private String worldName;
24 | @InitByDeserialization private Vector3D rangeCenter;
25 | @InitByDeserialization private double range;
26 |
27 | // REFERENCES
28 | @InitByDeserialization private List points = new ArrayList<>();
29 | @InitByDeserialization private List triangles = new ArrayList<>();
30 | @InitByDeserialization private List ladders = new ArrayList<>();
31 |
32 |
33 | // INIT
34 | @DeserializationNoArgsConstructor public SerializationNavMesh()
35 | {
36 |
37 | }
38 |
39 |
40 | // CONVERSION
41 | public SerializationNavMesh(NavMesh mesh)
42 | {
43 | this.worldName = mesh.getWorld().getName();
44 | this.rangeCenter = mesh.getRangeCenter();
45 | this.range = mesh.getRange();
46 |
47 | this.points.addAll(mesh.getPoints());
48 | for(NavMeshTriangle triangle : mesh.getTriangles())
49 | {
50 | this.triangles.add(new SerializationNavMeshTriangle(triangle));
51 |
52 | for(NavMeshTriangleTransition transition : triangle.neighbors.values())
53 | if(transition instanceof NavMeshLadder)
54 | {
55 | NavMeshLadder navMeshLadder = (NavMeshLadder) transition;
56 | // only add the ladder if the triangle currently being processed is the bottom one
57 | // in order to avoid duplicating the ladder
58 | if(navMeshLadder.getTriangleBottom() == triangle)
59 | this.ladders.add(new SerializationNavMeshLadder(navMeshLadder));
60 | }
61 | }
62 |
63 | this.points.sort(Comparator.comparing(NavMeshPoint::getId));
64 | this.triangles.sort(Comparator.comparing(SerializationNavMeshTriangle::getId));
65 | this.ladders.sort(Comparator.comparing(SerializationNavMeshLadder::getTriangleBottom));
66 | }
67 |
68 | public NavMesh convertToNavMesh(String id)
69 | {
70 | Set triangles = new HashSet<>();
71 | for(SerializationNavMeshTriangle serializationTriangle : this.triangles)
72 | triangles.add(serializationTriangle.getNavMeshTriangle(this.points));
73 |
74 | NavMesh navMesh = new NavMesh(id, this.rangeCenter, this.range, Bukkit.getWorld(this.worldName), this.points, triangles);
75 |
76 | for(SerializationNavMeshLadder serializationLadder : this.ladders)
77 | {
78 | NavMeshLadder ladder = serializationLadder.getNavMeshLadder(navMesh);
79 | ladder.getTriangleBottom().makeNeighbors(ladder.getTriangleTop(), ladder);
80 | }
81 |
82 | return navMesh;
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/json/SerializationNavMeshLadder.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh.json;
2 |
3 | import de.domisum.lib.auxilium.data.container.direction.Direction2D;
4 | import de.domisum.lib.auxilium.data.container.math.Vector3D;
5 | import de.domisum.lib.auxilium.util.java.annotations.DeserializationNoArgsConstructor;
6 | import de.domisum.lib.auxilium.util.java.annotations.InitByDeserialization;
7 | import de.domisum.lib.compitum.navmesh.NavMesh;
8 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshTriangle;
9 | import de.domisum.lib.compitum.navmesh.transition.NavMeshLadder;
10 | import lombok.Getter;
11 |
12 | class SerializationNavMeshLadder
13 | {
14 |
15 | // PROPERTIES
16 | @InitByDeserialization @Getter private String triangleBottom;
17 | @InitByDeserialization private Vector3D positionBottom;
18 |
19 | @InitByDeserialization private String triangleTop;
20 | @InitByDeserialization private Vector3D positionTop;
21 |
22 | @InitByDeserialization private Direction2D ladderDirection;
23 |
24 |
25 | // INIT
26 | @DeserializationNoArgsConstructor public SerializationNavMeshLadder()
27 | {
28 |
29 | }
30 |
31 | protected SerializationNavMeshLadder(NavMeshLadder navMeshLadder)
32 | {
33 | this.triangleBottom = navMeshLadder.getTriangleBottom().id;
34 | this.positionBottom = navMeshLadder.getPositionBottom();
35 |
36 | this.triangleTop = navMeshLadder.getTriangleTop().id;
37 | this.positionTop = navMeshLadder.getPositionTop();
38 |
39 | this.ladderDirection = navMeshLadder.getLadderDirection();
40 | }
41 |
42 |
43 | // GETTERS
44 | protected NavMeshLadder getNavMeshLadder(NavMesh navMesh)
45 | {
46 | NavMeshTriangle triangleBottom_ = navMesh.getTriangle(this.triangleBottom);
47 | NavMeshTriangle triangleTop_ = navMesh.getTriangle(this.triangleTop);
48 |
49 | return new NavMeshLadder(triangleBottom_, this.positionBottom, triangleTop_, this.positionTop, this.ladderDirection);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/json/SerializationNavMeshTriangle.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh.json;
2 |
3 | import de.domisum.lib.auxilium.util.java.annotations.DeserializationNoArgsConstructor;
4 | import de.domisum.lib.auxilium.util.java.annotations.InitByDeserialization;
5 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshPoint;
6 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshTriangle;
7 | import lombok.Getter;
8 |
9 | import java.util.Collection;
10 |
11 | class SerializationNavMeshTriangle
12 | {
13 |
14 | // PROPERTIES
15 | @InitByDeserialization @Getter private String id;
16 |
17 | @InitByDeserialization private String point1;
18 | @InitByDeserialization private String point2;
19 | @InitByDeserialization private String point3;
20 |
21 |
22 | // INIT
23 | @DeserializationNoArgsConstructor public SerializationNavMeshTriangle()
24 | {
25 |
26 | }
27 |
28 | SerializationNavMeshTriangle(NavMeshTriangle navMeshTriangle)
29 | {
30 | this.id = navMeshTriangle.id;
31 |
32 | this.point1 = navMeshTriangle.point1.getId();
33 | this.point2 = navMeshTriangle.point2.getId();
34 | this.point3 = navMeshTriangle.point3.getId();
35 | }
36 |
37 |
38 | // GETTERS
39 | protected NavMeshTriangle getNavMeshTriangle(Collection points)
40 | {
41 | return new NavMeshTriangle(this.id, getPoint(points, this.point1), getPoint(points, this.point2),
42 | getPoint(points, this.point3));
43 | }
44 |
45 | private NavMeshPoint getPoint(Collection points, String id)
46 | {
47 | for(NavMeshPoint point : points)
48 | if(point.getId().equals(id))
49 | return point;
50 |
51 | return null;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/pathfinding/NavMeshPathfinder.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh.pathfinding;
2 |
3 | import de.domisum.lib.auxilium.util.java.annotations.API;
4 | import de.domisum.lib.auxilium.util.time.ProfilerStopWatch;
5 | import de.domisum.lib.auxiliumspigot.util.LocationUtil;
6 | import de.domisum.lib.compitum.navmesh.NavMesh;
7 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshTriangle;
8 | import de.domisum.lib.compitum.navmesh.pathfinding.traversal.NavMeshTriangleTraverser;
9 | import de.domisum.lib.compitum.path.Path;
10 | import org.bukkit.Location;
11 |
12 | import java.util.List;
13 |
14 | @API
15 | public class NavMeshPathfinder
16 | {
17 |
18 | // INPUT
19 | private Location startLocation;
20 | private Location targetLocation;
21 | private NavMesh navMesh;
22 |
23 | // STATUS
24 | private ProfilerStopWatch stopWatch = new ProfilerStopWatch("pathfinding.navMesh");
25 | private ProfilerStopWatch triangleFindingStopWatch = new ProfilerStopWatch("pathfinding.navMesh.startTargetTriangles");
26 |
27 | // OUTPUT
28 | private Path path;
29 | private String failure;
30 |
31 |
32 | // INIT
33 | @API public NavMeshPathfinder(Location startLocation, Location targetLocation, NavMesh navMesh)
34 | {
35 | this.startLocation = startLocation;
36 | this.targetLocation = targetLocation;
37 |
38 | this.navMesh = navMesh;
39 | }
40 |
41 |
42 | // GETTERS
43 | public Path getPath()
44 | {
45 | return this.path;
46 | }
47 |
48 | @API public String getFailure()
49 | {
50 | return this.failure;
51 | }
52 |
53 |
54 | public ProfilerStopWatch getStopWatch()
55 | {
56 | return this.stopWatch;
57 | }
58 |
59 |
60 | // PATHFINDING
61 | @API public void findPath()
62 | {
63 | this.stopWatch.start();
64 | this.triangleFindingStopWatch.start();
65 | NavMeshTriangle startTriangle = this.navMesh.getTriangleAt(this.startLocation);
66 | if(startTriangle == null)
67 | {
68 | this.failure = "Start location is not on NavMesh";
69 | return;
70 | }
71 |
72 | NavMeshTriangle targetTriangle = this.navMesh.getTriangleAt(this.targetLocation);
73 | if(targetTriangle == null)
74 | {
75 | this.failure = "Target location is not on NavMesh";
76 | return;
77 | }
78 |
79 | this.triangleFindingStopWatch.stop();
80 |
81 | NavMeshTrianglePathfinder trianglePathfinder = new NavMeshTrianglePathfinder(startTriangle, targetTriangle);
82 | trianglePathfinder.findPath();
83 | List triangleSequence = trianglePathfinder.getTriangleSequence();
84 | if(triangleSequence == null)
85 | {
86 | this.failure = trianglePathfinder.getFailure();
87 | return;
88 | }
89 |
90 | NavMeshTriangleTraverser triangleTraverser = new NavMeshTriangleTraverser(LocationUtil.toVector3D(this.startLocation),
91 | LocationUtil.toVector3D(this.targetLocation), triangleSequence);
92 | triangleTraverser.traverseTriangles();
93 | this.path = triangleTraverser.getPath();
94 |
95 | this.stopWatch.stop();
96 |
97 |
98 | /*DebugUtil.say("");
99 | DebugUtil.say(getStopWatch());
100 | DebugUtil.say(this.triangleFindingStopWatch);
101 | DebugUtil.say(trianglePathfinder.getStopWatch());
102 | DebugUtil.say(triangleTraverser.getStopWatch());*/
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/pathfinding/NavMeshTriangleNode.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh.pathfinding;
2 |
3 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshTriangle;
4 | import de.domisum.lib.compitum.path.node.weighted.WeightedNode;
5 |
6 | public class NavMeshTriangleNode implements WeightedNode
7 | {
8 |
9 | // REFERENCES
10 | private NavMeshTriangle triangle;
11 | private NavMeshTriangleNode parent;
12 |
13 | // STATUS
14 | private double gValue = -1;
15 | private double heuristicValue = -1;
16 |
17 |
18 | // INIT
19 | protected NavMeshTriangleNode(NavMeshTriangle triangle, NavMeshTriangleNode parent, double heuristicValue)
20 | {
21 | this.triangle = triangle;
22 | this.parent = parent;
23 |
24 | this.heuristicValue = heuristicValue;
25 | }
26 |
27 | @Override public int hashCode()
28 | {
29 | return this.triangle.hashCode();
30 | }
31 |
32 | @Override public boolean equals(Object o)
33 | {
34 | if(!(o instanceof NavMeshTriangleNode))
35 | return false;
36 |
37 | NavMeshTriangleNode other = (NavMeshTriangleNode) o;
38 | return this.triangle.equals(other.triangle);
39 | }
40 |
41 |
42 | // GETTERS
43 | protected NavMeshTriangle getTriangle()
44 | {
45 | return this.triangle;
46 | }
47 |
48 | protected NavMeshTriangleNode getParent()
49 | {
50 | return this.parent;
51 | }
52 |
53 |
54 | @Override public double getGValue()
55 | {
56 | if(this.parent == null)
57 | return 0;
58 |
59 | if(this.gValue == -1)
60 | {
61 | double toParent = this.parent.getGValue();
62 | double fromParent = this.parent.getTriangle().getTransitionTo(this.triangle).getWeight();
63 |
64 | this.gValue = toParent+fromParent;
65 | }
66 |
67 | return this.gValue;
68 | }
69 |
70 | @Override public double getHValue()
71 | {
72 | return this.heuristicValue;
73 | }
74 |
75 | @Override public double getFValue()
76 | {
77 | return getGValue()+getHValue();
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/pathfinding/NavMeshTrianglePathfinder.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh.pathfinding;
2 |
3 | import de.domisum.lib.auxilium.util.java.annotations.API;
4 | import de.domisum.lib.auxilium.util.time.ProfilerStopWatch;
5 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshTriangle;
6 | import de.domisum.lib.compitum.navmesh.transition.NavMeshTriangleTransition;
7 | import de.domisum.lib.compitum.path.node.weighted.SortedWeightedNodeList;
8 |
9 | import java.util.ArrayList;
10 | import java.util.Collections;
11 | import java.util.HashSet;
12 | import java.util.List;
13 | import java.util.Map;
14 | import java.util.Set;
15 |
16 | @API
17 | public class NavMeshTrianglePathfinder
18 | {
19 |
20 | // PROPERTIES
21 | private int maxNodeVisits = 200;
22 |
23 | private boolean canUseLadders = true;
24 |
25 | // INPUT
26 | private NavMeshTriangle startTriangle;
27 | private NavMeshTriangle targetTriangle;
28 |
29 | // STATUS
30 | private Set visitedNodes = new HashSet<>(this.maxNodeVisits);
31 | private SortedWeightedNodeList unvisitedNodes = new SortedWeightedNodeList<>(this.maxNodeVisits*3);
32 |
33 | private ProfilerStopWatch stopWatch = new ProfilerStopWatch("pathfinding.navMesh.triangleSequence");
34 |
35 | // OUTPUT
36 | private List triangleSequence = null;
37 | private String failure;
38 |
39 |
40 | // INIT
41 | @API public NavMeshTrianglePathfinder(NavMeshTriangle startTriangle, NavMeshTriangle targetTriangle)
42 | {
43 | this.startTriangle = startTriangle;
44 | this.targetTriangle = targetTriangle;
45 | }
46 |
47 |
48 | // GETTERS
49 | public List getTriangleSequence()
50 | {
51 | return this.triangleSequence;
52 | }
53 |
54 | public String getFailure()
55 | {
56 | return this.failure;
57 | }
58 |
59 | @API public ProfilerStopWatch getStopWatch()
60 | {
61 | return this.stopWatch;
62 | }
63 |
64 |
65 | @API public boolean canUseLadders()
66 | {
67 | return this.canUseLadders;
68 | }
69 |
70 |
71 | // SETTERS
72 | @API public void setCanUseLadders(boolean canUseLadders)
73 | {
74 | this.canUseLadders = canUseLadders;
75 | }
76 |
77 |
78 | // PATHFINDING
79 | @API public void findPath()
80 | {
81 | this.stopWatch.start();
82 |
83 | NavMeshTriangleNode targetNode = null;
84 |
85 | // pathfinding
86 | this.unvisitedNodes
87 | .addSorted(new NavMeshTriangleNode(this.startTriangle, null, calculateHeuristicValue(this.startTriangle)));
88 | while(true)
89 | {
90 | if(this.visitedNodes.size() >= this.maxNodeVisits)
91 | {
92 | this.failure = "Too many nodes visited";
93 | break;
94 | }
95 |
96 | if(this.unvisitedNodes.getSize() == 0)
97 | {
98 | this.failure = "No unvisted nodes left";
99 | break;
100 | }
101 |
102 | NavMeshTriangleNode node = this.unvisitedNodes.getAndRemoveFirst();
103 | if(this.targetTriangle.equals(node.getTriangle()))
104 | {
105 | targetNode = node;
106 | break;
107 | }
108 |
109 | visitNode(node);
110 | this.visitedNodes.add(node);
111 | }
112 |
113 | // converting linked node list into triangle list
114 | if(targetNode != null)
115 | {
116 | this.triangleSequence = new ArrayList<>();
117 |
118 | NavMeshTriangleNode currentNode = targetNode;
119 | while(currentNode != null)
120 | {
121 | this.triangleSequence.add(currentNode.getTriangle());
122 | currentNode = currentNode.getParent();
123 | }
124 |
125 | Collections.reverse(this.triangleSequence);
126 | }
127 |
128 | this.stopWatch.stop();
129 | }
130 |
131 | private void visitNode(NavMeshTriangleNode node)
132 | {
133 | NavMeshTriangle triangle = node.getTriangle();
134 | for(Map.Entry entry : triangle.neighbors.entrySet())
135 | {
136 | NavMeshTriangleNode newNode = new NavMeshTriangleNode(entry.getKey(), node, calculateHeuristicValue(triangle));
137 | if(this.visitedNodes.contains(newNode))
138 | continue;
139 |
140 | if(this.unvisitedNodes.contains(newNode))
141 | continue;
142 |
143 | this.unvisitedNodes.addSorted(newNode);
144 | }
145 | }
146 |
147 |
148 | private double calculateHeuristicValue(NavMeshTriangle triangle)
149 | {
150 | double dX = this.targetTriangle.getHeuristicCenter().x-triangle.getHeuristicCenter().x;
151 | double dY = this.targetTriangle.getHeuristicCenter().y-triangle.getHeuristicCenter().y;
152 | double dZ = this.targetTriangle.getHeuristicCenter().z-triangle.getHeuristicCenter().z;
153 |
154 | double dXAbs = Math.abs(dX);
155 | double dYAbs = Math.abs(dY);
156 | double dZAbs = Math.abs(dZ);
157 |
158 | // diagonal distance of x and z
159 | double minD = Math.min(dXAbs, dZAbs);
160 | double maxD = Math.max(dXAbs, dZAbs);
161 | double diagonalDistance = (maxD-minD)+minD*1.414;
162 |
163 | // add dY to account for height difference
164 | return diagonalDistance+dYAbs;
165 | }
166 |
167 | }
168 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/pathfinding/traversal/NavMeshTriangleTraverser.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh.pathfinding.traversal;
2 |
3 | import de.domisum.lib.auxilium.data.container.math.LineSegment3D;
4 | import de.domisum.lib.auxilium.data.container.math.Vector3D;
5 | import de.domisum.lib.auxilium.util.time.ProfilerStopWatch;
6 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshTriangle;
7 | import de.domisum.lib.compitum.navmesh.transition.NavMeshLadder;
8 | import de.domisum.lib.compitum.navmesh.transition.NavMeshTrianglePortal;
9 | import de.domisum.lib.compitum.navmesh.transition.NavMeshTriangleTransition;
10 | import de.domisum.lib.compitum.path.Path;
11 | import de.domisum.lib.compitum.path.PathWaypoint;
12 | import de.domisum.lib.compitum.path.node.TransitionType;
13 |
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | public class NavMeshTriangleTraverser
18 | {
19 |
20 | // INPUT
21 | private Vector3D startPosition;
22 | private Vector3D targetPosition;
23 | private List triangleSequence;
24 |
25 | // STATUS
26 | private List pathWaypoints = new ArrayList<>();
27 | // triangle traversal
28 | private Vector3D currentPosition;
29 | private Vector3D visLeft;
30 | private Vector3D visRight;
31 | private int visLeftTriangleIndex;
32 | private int visRightTriangleIndex;
33 |
34 | private int currentTriangleIndex = 0;
35 | private NavMeshTriangle triangle;
36 | private NavMeshTriangle triangleAfter;
37 |
38 | private Vector3D portalEndpointLeft;
39 | private Vector3D portalEndpointRight;
40 |
41 | private ProfilerStopWatch stopWatch = new ProfilerStopWatch("pathfinding.navMesh.triangleTraversal");
42 |
43 | // OUTPUT
44 | private Path path;
45 |
46 |
47 | // INIT
48 | public NavMeshTriangleTraverser(Vector3D startPosition, Vector3D targetPosition, List triangleSequence)
49 | {
50 | this.startPosition = startPosition;
51 | this.targetPosition = targetPosition;
52 |
53 | this.triangleSequence = triangleSequence;
54 | }
55 |
56 |
57 | // GETTERS
58 | public Path getPath()
59 | {
60 | return this.path;
61 | }
62 |
63 | public ProfilerStopWatch getStopWatch()
64 | {
65 | return this.stopWatch;
66 | }
67 |
68 |
69 | private Vector3D getTowardsVisLeft()
70 | {
71 | return this.visLeft.subtract(this.currentPosition);
72 | }
73 |
74 | private Vector3D getTowardsVisRight()
75 | {
76 | return this.visRight.subtract(this.currentPosition);
77 | }
78 |
79 | private Vector3D getTowardsPortalEndpointLeft()
80 | {
81 | return this.portalEndpointLeft.subtract(this.currentPosition);
82 | }
83 |
84 | private Vector3D getTowardsPortalEndpointRight()
85 | {
86 | return this.portalEndpointRight.subtract(this.currentPosition);
87 | }
88 |
89 |
90 | // TRAVERSAL
91 | public void traverseTriangles()
92 | {
93 | this.stopWatch.start();
94 |
95 | this.currentPosition = this.startPosition;
96 |
97 | if(this.triangleSequence.size() == 1)
98 | this.pathWaypoints.add(new PathWaypoint(this.targetPosition, TransitionType.WALK));
99 | else
100 | {
101 | for(this.currentTriangleIndex = 0;
102 | this.currentTriangleIndex < this.triangleSequence.size(); this.currentTriangleIndex++)
103 | processTriangleTransition();
104 | }
105 |
106 | this.path = new Path(this.pathWaypoints);
107 | this.stopWatch.stop();
108 | }
109 |
110 |
111 | private void processTriangleTransition()
112 | {
113 | this.triangle = this.triangleSequence.get(this.currentTriangleIndex);
114 | this.triangleAfter = this.currentTriangleIndex+1 < this.triangleSequence.size() ?
115 | this.triangleSequence.get(this.currentTriangleIndex+1) :
116 | null;
117 |
118 | NavMeshTriangleTransition transition = this.triangle.getTransitionTo(this.triangleAfter);
119 |
120 | if(this.triangleAfter == null)
121 | traverseTrianglePortal();
122 | else if(transition.getTransitionType() == TransitionType.WALK)
123 | traverseTrianglePortal();
124 | else if(transition.getTransitionType() == TransitionType.CLIMB)
125 | useLadder();
126 | }
127 |
128 |
129 | // WALKING
130 | private void traverseTrianglePortal()
131 | {
132 | if(this.triangleAfter == null) // last triangle
133 | processMovementTowardsTargetPoint(this.targetPosition);
134 | // either first triangle processing or after new corner
135 | else if(this.visLeft == null) // if visLeft is null, then visRight is also null
136 | {
137 | findPortalEndpoints(this.triangle, this.triangleAfter);
138 | this.visLeft = this.portalEndpointLeft;
139 | this.visRight = this.portalEndpointRight;
140 | this.visLeftTriangleIndex = this.currentTriangleIndex;
141 | this.visRightTriangleIndex = this.currentTriangleIndex;
142 | }
143 | else
144 | {
145 | findPortalEndpoints(this.triangle, this.triangleAfter);
146 |
147 | boolean leftSame = isSame(this.visLeft, this.currentPosition);
148 | boolean rightSame = isSame(this.visRight, this.currentPosition);
149 |
150 | // check if portal is out on one side
151 | if(isLeftOf(getTowardsVisRight(), getTowardsPortalEndpointLeft(), true) && !leftSame && !rightSame) // right turn
152 | {
153 | newWaypoint(this.visRight, TransitionType.WALK);
154 |
155 | this.currentTriangleIndex = this.visRightTriangleIndex;
156 | return;
157 | }
158 | else if(isLeftOf(getTowardsPortalEndpointRight(), getTowardsVisLeft(), true) && !leftSame && !rightSame) // left turn
159 | {
160 | newWaypoint(this.visLeft, TransitionType.WALK);
161 |
162 | this.currentTriangleIndex = this.visLeftTriangleIndex;
163 | return;
164 | }
165 |
166 | // confine movement cone
167 | if(isLeftOf(getTowardsVisLeft(), getTowardsPortalEndpointLeft(), true)) // left
168 | {
169 | this.visLeft = this.portalEndpointLeft;
170 | this.visLeftTriangleIndex = this.currentTriangleIndex;
171 | }
172 | if(isLeftOf(getTowardsPortalEndpointRight(), getTowardsVisRight(), true)) // right
173 | {
174 | this.visRight = this.portalEndpointRight;
175 | this.visRightTriangleIndex = this.currentTriangleIndex;
176 | }
177 | }
178 | }
179 |
180 | private void processMovementTowardsTargetPoint(Vector3D targetPoint)
181 | {
182 | // the vis points can be null if the transition into the previous triangle was a turn
183 | // if this is the case, the target point is guaranteed to be in the cone
184 | if(this.visLeft != null)
185 | {
186 | Vector3D towardsTargetPoint = targetPoint.subtract(this.currentPosition);
187 |
188 | if(isLeftOf(getTowardsVisRight(), towardsTargetPoint, false)) // right turn
189 | {
190 | newWaypoint(this.visRight, TransitionType.WALK);
191 |
192 | this.currentTriangleIndex = this.visRightTriangleIndex;
193 | return;
194 | }
195 | else if(isLeftOf(towardsTargetPoint, getTowardsVisLeft(), false)) // left turn
196 | {
197 | newWaypoint(this.visLeft, TransitionType.WALK);
198 |
199 | this.currentTriangleIndex = this.visLeftTriangleIndex;
200 | return;
201 | }
202 | }
203 |
204 | this.pathWaypoints.add(new PathWaypoint(targetPoint, TransitionType.WALK));
205 | }
206 |
207 |
208 | // LADDER CLIMBING
209 | private void useLadder()
210 | {
211 | NavMeshTriangleTransition transition = this.triangle.getTransitionTo(this.triangleAfter);
212 | NavMeshLadder ladder = (NavMeshLadder) transition;
213 |
214 | boolean upwards = ladder.getTriangleBottom() == this.triangle;
215 | if(upwards)
216 | {
217 | processMovementTowardsTargetPoint(ladder.getPositionBottom());
218 | Vector3D climbingEndPosition = new Vector3D(ladder.getPositionBottom().x, ladder.getPositionTop().y,
219 | ladder.getPositionBottom().z);
220 |
221 | PathWaypoint climbPathWaypoint = newWaypoint(climbingEndPosition, TransitionType.CLIMB);
222 | climbPathWaypoint.setData("ladderDirection", ladder.getLadderDirection());
223 | newWaypoint(ladder.getPositionTop(), TransitionType.WALK);
224 | }
225 | else
226 | {
227 | Vector3D climbingStartPosition = new Vector3D(ladder.getPositionBottom().x, ladder.getPositionTop().y,
228 | ladder.getPositionBottom().z);
229 |
230 | processMovementTowardsTargetPoint(climbingStartPosition);
231 | PathWaypoint climbPathWaypoint = newWaypoint(ladder.getPositionBottom(), TransitionType.CLIMB);
232 | climbPathWaypoint.setData("ladderDirection", ladder.getLadderDirection());
233 | }
234 | }
235 |
236 |
237 | // SUBROUTINES
238 | private void findPortalEndpoints(NavMeshTriangle from, NavMeshTriangle to)
239 | {
240 | NavMeshTriangleTransition transition = from.getTransitionTo(to);
241 | LineSegment3D portalLineSegment = ((NavMeshTrianglePortal) transition).getFullLineSegment();
242 |
243 | this.portalEndpointLeft = portalLineSegment.a;
244 | this.portalEndpointRight = portalLineSegment.b;
245 |
246 | Vector3D fromCenter = from.getCenter();
247 | if(isLeftOf(this.portalEndpointRight.subtract(fromCenter), this.portalEndpointLeft.subtract(fromCenter), false))
248 | {
249 | Vector3D temp = this.portalEndpointLeft;
250 | this.portalEndpointLeft = this.portalEndpointRight;
251 | this.portalEndpointRight = temp;
252 | }
253 | }
254 |
255 | private PathWaypoint newWaypoint(Vector3D position, int transitionType)
256 | {
257 | PathWaypoint pathWaypoint = new PathWaypoint(position, transitionType);
258 | this.pathWaypoints.add(pathWaypoint);
259 |
260 | this.currentPosition = position;
261 | this.visLeft = null;
262 | this.visRight = null;
263 |
264 | return pathWaypoint;
265 | }
266 |
267 |
268 | // UTIL
269 | private static boolean isLeftOf(Vector3D v1, Vector3D v2, boolean onZero)
270 | {
271 | double crossY = v1.crossProduct(v2).y;
272 |
273 | if(crossY == 0)
274 | return onZero;
275 |
276 | return crossY < 0;
277 | }
278 |
279 | private static boolean isSame(Vector3D a, Vector3D b)
280 | {
281 | if(a == null)
282 | return b == null;
283 |
284 | return a.equals(b);
285 | }
286 |
287 | }
288 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/transition/NavMeshLadder.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh.transition;
2 |
3 | import de.domisum.lib.auxilium.data.container.direction.Direction2D;
4 | import de.domisum.lib.auxilium.data.container.math.Vector3D;
5 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshTriangle;
6 | import de.domisum.lib.compitum.path.node.TransitionType;
7 |
8 | public class NavMeshLadder implements NavMeshTriangleTransition
9 | {
10 |
11 | // CONSTANTS
12 | private static final double CLIMBING_EXPENSE = 2;
13 |
14 | // PROPERTIES
15 | private NavMeshTriangle triangleBottom;
16 | private Vector3D positionBottom;
17 |
18 | private NavMeshTriangle triangleTop;
19 | private Vector3D positionTop;
20 |
21 | private Direction2D ladderDirection;
22 |
23 |
24 | // INIT
25 | public NavMeshLadder(
26 | NavMeshTriangle triangleBottom,
27 | Vector3D positionBottom,
28 | NavMeshTriangle triangleTop,
29 | Vector3D positionTop,
30 | Direction2D ladderDirection)
31 | {
32 | this.triangleBottom = triangleBottom;
33 | this.positionBottom = positionBottom;
34 |
35 | this.triangleTop = triangleTop;
36 | this.positionTop = positionTop;
37 |
38 | this.ladderDirection = ladderDirection;
39 | }
40 |
41 |
42 | // GETTERS
43 | public NavMeshTriangle getTriangleBottom()
44 | {
45 | return this.triangleBottom;
46 | }
47 |
48 | public Vector3D getPositionBottom()
49 | {
50 | return this.positionBottom;
51 | }
52 |
53 |
54 | public NavMeshTriangle getTriangleTop()
55 | {
56 | return this.triangleTop;
57 | }
58 |
59 | public Vector3D getPositionTop()
60 | {
61 | return this.positionTop;
62 | }
63 |
64 |
65 | public Direction2D getLadderDirection()
66 | {
67 | return this.ladderDirection;
68 | }
69 |
70 |
71 | // TRANSITION
72 | @Override public int getTransitionType()
73 | {
74 | return TransitionType.CLIMB;
75 | }
76 |
77 | @Override public double getWeight()
78 | {
79 | double dY = Math.abs(this.positionTop.y-this.positionBottom.y);
80 | return dY*CLIMBING_EXPENSE;
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/transition/NavMeshTrianglePortal.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh.transition;
2 |
3 | import de.domisum.lib.auxilium.data.container.math.LineSegment3D;
4 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshPoint;
5 | import de.domisum.lib.compitum.navmesh.geometry.NavMeshTriangle;
6 | import de.domisum.lib.compitum.path.node.TransitionType;
7 |
8 | import java.util.Collection;
9 | import java.util.Iterator;
10 |
11 | public class NavMeshTrianglePortal implements NavMeshTriangleTransition
12 | {
13 |
14 | // REFERENCES
15 | private final NavMeshTriangle triangle1;
16 | private final NavMeshTriangle triangle2;
17 |
18 | private final NavMeshPoint point1;
19 | private final NavMeshPoint point2;
20 |
21 | // STATUS
22 | private LineSegment3D fullLineSegment;
23 | private double triangleHeuristicCenterDistance = -1;
24 |
25 |
26 | // INIT
27 | public NavMeshTrianglePortal(NavMeshTriangle triangle1, NavMeshTriangle triangle2, Collection commonPoints)
28 | {
29 | this.triangle1 = triangle1;
30 | this.triangle2 = triangle2;
31 |
32 | if(commonPoints.size() != 2)
33 | throw new IllegalArgumentException("The number of current points has to be 2 (was "+commonPoints.size()+")");
34 |
35 | Iterator iterator = commonPoints.iterator();
36 | this.point1 = iterator.next();
37 | this.point2 = iterator.next();
38 | }
39 |
40 |
41 | // GETTERS
42 | public LineSegment3D getFullLineSegment()
43 | {
44 | if(this.fullLineSegment == null)
45 | this.fullLineSegment = new LineSegment3D(this.point1.getPositionVector(), this.point2.getPositionVector());
46 |
47 | return this.fullLineSegment;
48 | }
49 |
50 | @Override public int getTransitionType()
51 | {
52 | return TransitionType.WALK;
53 | }
54 |
55 | @Override public double getWeight()
56 | {
57 | if(this.triangleHeuristicCenterDistance == -1)
58 | this.triangleHeuristicCenterDistance = this.triangle2.getHeuristicCenter()
59 | .subtract(this.triangle1.getHeuristicCenter()).length();
60 |
61 | return this.triangleHeuristicCenterDistance;
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/navmesh/transition/NavMeshTriangleTransition.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.navmesh.transition;
2 |
3 | public interface NavMeshTriangleTransition
4 | {
5 |
6 | // GETTERS
7 | int getTransitionType();
8 |
9 | double getWeight();
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/path/BlockPath.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.path;
2 |
3 | import de.domisum.lib.auxilium.util.java.annotations.API;
4 | import de.domisum.lib.compitum.path.node.BlockPathNode;
5 |
6 | import java.util.ArrayList;
7 | import java.util.Collections;
8 | import java.util.List;
9 |
10 | public class BlockPath
11 | {
12 |
13 | // PROPERTIES
14 | private List nodes = new ArrayList<>();
15 |
16 |
17 | // INIT
18 | public BlockPath(BlockPathNode endNode)
19 | {
20 | generatePath(endNode);
21 | }
22 |
23 | private void generatePath(BlockPathNode endNode)
24 | {
25 | BlockPathNode currentNode = endNode;
26 | while(currentNode != null)
27 | {
28 | this.nodes.add(currentNode);
29 | currentNode = currentNode.getParent();
30 | }
31 |
32 | Collections.reverse(this.nodes);
33 | }
34 |
35 |
36 | // GETTERS
37 | @API public List getNodes()
38 | {
39 | return this.nodes;
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/path/Path.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.path;
2 |
3 | import java.util.List;
4 |
5 | public class Path
6 | {
7 |
8 | private List pathWaypoints;
9 |
10 |
11 | // INIT
12 | public Path(List pathWaypoints)
13 | {
14 | this.pathWaypoints = pathWaypoints;
15 | }
16 |
17 |
18 | // GETTERS
19 | public int getNumberOfWaypoints()
20 | {
21 | return this.pathWaypoints.size();
22 | }
23 |
24 | public PathWaypoint getWaypoint(int index)
25 | {
26 | if(index >= this.pathWaypoints.size() || index < 0)
27 | return null;
28 |
29 | return this.pathWaypoints.get(index);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/path/PathWaypoint.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.path;
2 |
3 | import de.domisum.lib.auxilium.data.container.DataRegister;
4 | import de.domisum.lib.auxilium.data.container.math.Vector3D;
5 |
6 | public class PathWaypoint
7 | {
8 |
9 | // PROPERTIES
10 | private Vector3D position;
11 | private int transitionType;
12 |
13 | private DataRegister dataRegister = new DataRegister();
14 |
15 |
16 | // INIT
17 | public PathWaypoint(Vector3D position, int transitionType)
18 | {
19 | this.position = position;
20 | this.transitionType = transitionType;
21 | }
22 |
23 | public String toString()
24 | {
25 | return "waypoint[position="+this.position+";transitionType="+this.transitionType+"]";
26 | }
27 |
28 |
29 | // GETTERS
30 | public Vector3D getPosition()
31 | {
32 | return this.position;
33 | }
34 |
35 | public int getTransitionType()
36 | {
37 | return this.transitionType;
38 | }
39 |
40 | public Object getData(String key)
41 | {
42 | return this.dataRegister.get(key);
43 | }
44 |
45 |
46 | // SETTERS
47 | public void setData(String key, Object value)
48 | {
49 | this.dataRegister.set(key, value);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/path/node/BlockPathNode.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.path.node;
2 |
3 | import de.domisum.lib.compitum.path.node.weighted.WeightedNode;
4 | import org.bukkit.Location;
5 | import org.bukkit.World;
6 |
7 | public class BlockPathNode implements WeightedNode
8 | {
9 |
10 | // PROPERTIES
11 | public final int x;
12 | public final int y;
13 | public final int z;
14 |
15 | // SUCCESSOR
16 | private BlockPathNode parent;
17 | private int transitionType = 0;
18 |
19 | private double weightFromParent;
20 | private double heuristicWeight;
21 |
22 |
23 | // INIT
24 | public BlockPathNode(int x, int y, int z)
25 | {
26 | this.x = x;
27 | this.y = y;
28 | this.z = z;
29 | }
30 |
31 | @Override public boolean equals(Object other)
32 | {
33 | if(!(other instanceof BlockPathNode))
34 | return false;
35 |
36 | BlockPathNode o = (BlockPathNode) other;
37 |
38 | return o.x == this.x && o.y == this.y && o.z == this.z;
39 | }
40 |
41 | @Override public int hashCode()
42 | {
43 | int hash = 0;
44 | hash |= (this.x%4096)<<20; // 12 bits long, in [0;11]
45 | hash |= this.y<<12; // 8 bits (2^8 = 256) long, in [12;19]
46 | hash |= (this.z%4096); // 12 bits long, in [20;31]
47 |
48 | return hash;
49 | }
50 |
51 | @Override public String toString()
52 | {
53 | return "transitionalNode[x="+this.x+",y="+this.y+",z="+this.z+"]";
54 | }
55 |
56 |
57 | // GETTERS
58 | public BlockPathNode getParent()
59 | {
60 | return this.parent;
61 | }
62 |
63 | public int getTransitionType()
64 | {
65 | return this.transitionType;
66 | }
67 |
68 |
69 | @Override public double getGValue()
70 | {
71 | if(this.parent == null)
72 | return this.weightFromParent;
73 |
74 | return this.parent.getGValue()+this.weightFromParent;
75 | }
76 |
77 | @Override public double getHValue()
78 | {
79 | return this.heuristicWeight;
80 | }
81 |
82 | @Override public double getFValue()
83 | {
84 | return getGValue()+getHValue();
85 | }
86 |
87 |
88 | public Location getLocation(World world)
89 | {
90 | return new Location(world, this.x, this.y, this.z);
91 | }
92 |
93 |
94 | // SETTERS
95 | public void setParent(BlockPathNode parent, int transitionType, double additionalWeight)
96 | {
97 | this.parent = parent;
98 | this.transitionType = transitionType;
99 | this.weightFromParent = additionalWeight;
100 | }
101 |
102 | public void setHeuristicWeight(double heuristicWeight)
103 | {
104 | this.heuristicWeight = heuristicWeight;
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/path/node/TransitionType.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.path.node;
2 |
3 | public class TransitionType
4 | {
5 |
6 | public static final int WALK = 1;
7 | /**
8 | * This JUMP is a simple walking jump from a block below to another block above.
9 | * For a parcour jump over a hole LEAP is used.
10 | */
11 | public static final int JUMP = 2;
12 | public static final int CLIMB = 3;
13 | public static final int FALL = 4;
14 | public static final int LEAP = 5;
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/path/node/weighted/SortedWeightedNodeList.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.path.node.weighted;
2 |
3 | import de.domisum.lib.auxilium.util.java.annotations.API;
4 |
5 | import java.util.ArrayList;
6 | import java.util.Collections;
7 | import java.util.HashSet;
8 | import java.util.List;
9 | import java.util.Set;
10 |
11 | public class SortedWeightedNodeList
12 | {
13 |
14 | private List nodes;
15 | private Set nodesContainsTester;
16 |
17 |
18 | // INIT
19 | public SortedWeightedNodeList(int length)
20 | {
21 | this.nodes = new ArrayList<>(length);
22 | this.nodesContainsTester = new HashSet<>(length);
23 | }
24 |
25 |
26 | // GETTERS
27 | public T getAndRemoveFirst()
28 | {
29 | if(this.nodes.isEmpty())
30 | return null;
31 |
32 | T firstNode = this.nodes.remove(0);
33 | this.nodesContainsTester.remove(firstNode);
34 |
35 | return firstNode;
36 | }
37 |
38 | public boolean contains(T node)
39 | {
40 | return this.nodesContainsTester.contains(node);
41 | }
42 |
43 | public int getSize()
44 | {
45 | return this.nodes.size();
46 | }
47 |
48 | private double getValueToCompare(T node)
49 | {
50 | return node.getFValue();
51 | }
52 |
53 |
54 | @Deprecated public List getNodes()
55 | {
56 | return this.nodes;
57 | }
58 |
59 |
60 | // CHANGERS
61 | public void addSorted(T node)
62 | {
63 | // don't subtract one from the size since the element could be added after the last current entry
64 | this.nodesContainsTester.add(node);
65 | insertIntoList(node, 0, this.nodes.size());
66 | }
67 |
68 | private void insertIntoList(T node, int lowerBound, int upperBound)
69 | {
70 | if(lowerBound == upperBound)
71 | {
72 | this.nodes.add(lowerBound, node);
73 | return;
74 | }
75 |
76 | double nodeValue = getValueToCompare(node);
77 |
78 | int dividingIndex = (lowerBound+upperBound)/2;
79 | T dividingNode = this.nodes.get(dividingIndex);
80 | double dividingValue = getValueToCompare(dividingNode);
81 |
82 | if(nodeValue > dividingValue)
83 | insertIntoList(node, dividingIndex+1, upperBound);
84 | else
85 | insertIntoList(node, lowerBound, dividingIndex);
86 | }
87 |
88 | public void clear()
89 | {
90 | this.nodes.clear();
91 | this.nodesContainsTester.clear();
92 | }
93 |
94 |
95 | @API public void sort()
96 | {
97 | Collections.sort(this.nodes, (n1, n2)->getValueToCompare(n1) > getValueToCompare(n2) ? 1 : -1);
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/de/domisum/lib/compitum/path/node/weighted/WeightedNode.java:
--------------------------------------------------------------------------------
1 | package de.domisum.lib.compitum.path.node.weighted;
2 |
3 | public interface WeightedNode
4 | {
5 |
6 | // GETTERS
7 | double getGValue();
8 |
9 | double getHValue();
10 |
11 | double getFValue();
12 |
13 | }
14 |
--------------------------------------------------------------------------------