View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.agent.module.sensor;
2   
3   import java.io.File;
4   import java.io.FileWriter;
5   import java.io.IOException;
6   import java.io.PrintWriter;
7   import java.util.ArrayList;
8   import java.util.HashMap;
9   import java.util.List;
10  import java.util.logging.Logger;
11  
12  import javax.vecmath.Vector3d;
13  
14  import cz.cuni.amis.pogamut.base.agent.module.SensorModule;
15  import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
16  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
17  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
18  import cz.cuni.amis.pogamut.base.component.controller.ComponentDependencies;
19  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
20  import cz.cuni.amis.pogamut.base3d.worldview.object.Rotation;
21  import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
22  import cz.cuni.amis.pogamut.ut2004.agent.navigation.floydwarshall.FloydWarshallMap;
23  import cz.cuni.amis.pogamut.ut2004.bot.IUT2004BotController;
24  import cz.cuni.amis.pogamut.ut2004.bot.IUT2004BotLogicController;
25  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
26  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004BotModuleController;
27  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.GameInfo;
28  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
29  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPointMessage;
30  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPointNeighbourLink;
31  import cz.cuni.amis.pogamut.ut2004.utils.LinkFlag;
32  import cz.cuni.amis.pogamut.ut2004.utils.UnrealUtils;
33  import cz.cuni.amis.utils.NullCheck;
34  import cz.cuni.amis.utils.exception.PogamutException;
35  import cz.cuni.amis.utils.exception.PogamutIOException;
36  
37  /**
38   * This class can be used to manually improve the navigation graph of the UT2004 by manually adding/removing edges from it.
39   * <p><p>
40   * Note that NavigationGraphBuilder is automatically prefixing all navpoint ids with "mapName.", which means, that you do not
41   * need to specify the id of navpoints as (e.g.) "DM-1on1-Albatross.PathNode2", "PathNode2" suffices. If you want to change this behavior
42   * call {@link NavigationGraphBuilder#setAutoPrefix(boolean)} with "false". Autoprefixing is good as it solves the problem with case-sensitivity
43   * of navpoint ids (i.e., you may run map dm-1on1-albatross as well as DM-1on1-Albatross!), it also makes the work faster as you do not have to repeat
44   * yourself.
45   * <p><p>
46   * Note that even if auto-prefixing enabled you may prefix ids of navpoints with map name, the auto-prefixing implemented by {@link NavigationGraphBuilder#autoPrefix(String)} 
47   * will detects that and auto-correct upper/lower case of this existing prefix if needed. Also you may use it as a validation feature because
48   * the {@link NavigationGraphBuilder#autoPrefix(String)} will raise an exception if the prefix does not match the current map name.
49   * <p><p>
50   * As all {@link SensorModule} it should be initialized in {@link IUT2004BotController#prepareBot(UT2004Bot)}. Note
51   * that {@link UT2004BotModuleController} has it auto-initialized inside {@link UT2004BotModuleController#initializeModules(UT2004Bot)}.
52   * <p><p>
53   * Best utilized in {@link IUT2004BotController#botInitialized(cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.GameInfo, cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.ConfigChange, cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.InitedMessage)} method.
54   * 
55   * 
56   * @author Jimmy
57   *
58   */
59  public class NavigationGraphBuilder extends SensorModule<UT2004Bot> {
60  
61  	/**
62  	 * Builder encloses the creation of the new navpoint.
63  	 * <p><p>
64  	 * You must call {@link NewNavPointBuilder#createNavPoint()} after you set it up to truly create the navpoint inside bot's {@link IWorldView}.
65  	 * 
66  	 * @author Jimmy
67  	 */
68  	public class NewNavPointBuilder {
69  		
70  		private String id;
71  		private Location location;
72  		private List<NewNavPointEdgeBuilder> edges = new ArrayList<NewNavPointEdgeBuilder>();
73  
74  		protected NewNavPointBuilder() {			
75  		}
76  		
77  		/**
78  		 * Sets the ID to be used for the new navpoint, corresponds to {@link NavPoint#getId()}.
79  		 * @param id will be auto-prefixed (if enabled, which is default)
80  		 * @return
81  		 */
82  		public NewNavPointBuilder setId(String id) {
83  			NullCheck.check(id, "id");
84  			this.id = autoPrefix(id);
85  			return this;
86  		}
87  		
88  		/**
89  		 * Sets the location of the new navpoint, corresponds to {@link NavPoint#getLocation()}.
90  		 * 
91  		 * @param x
92  		 * @param y
93  		 * @param z
94  		 * @return
95  		 */
96  		public NewNavPointBuilder setLocation(double x, double y, double z) {
97  			this.location = new Location(x,y,z);
98  			return this;
99  		}
100 		
101 		/**
102 		 * Finalizing method that will insert the navpoint into the underlying {@link IWorldView} of the bot.
103 		 * <p><p>
104 		 * You must have ID/Location set via {@link NewNavPointBuilder#setId(String)} and {@link NewNavPointBuilder#setLocation(double, double, double)}
105 		 * otherwise an exception will be thrown.
106 		 * <p><p>
107 		 * WARNING: IF USED INSIDE THE {@link IUT2004BotLogicController#logic()}, THAN THE NEW NAVPOINT WON'T BE ACCESSIBLE IMMEDIATELLY, IT WILL BE ACCESSIBLE FROM THE NEXT LOGIC ITERATION!
108 		 */
109 		public void createNavPoint() {
110 			if (id == null) throw new PogamutException("Could not create navpoint, id is null, you must set it using setId() before calling this method.", this);
111 			if (location == null) throw new PogamutException("Could not create navpoint (" + id + "), location is null, you must set it using setLocation() before calling this method.", this);
112 			NavPoint newNavPoint = 
113 				new NavPointMessage(UnrealId.get(id), location, null, false, null, null, false, false, null, null, false, false, false, false, 255, false, 255, false, false, false, false, false, false, false, false, new Rotation(0,0,0), false, false, null, new HashMap<UnrealId, NavPointNeighbourLink>(), new HashMap<UnrealId, NavPointNeighbourLink>(), null);
114 			
115 			for (NewNavPointEdgeBuilder edge : edges) {
116 				Object np = agent.getWorldView().get(edge.toNavPointId);
117 				if (np == null) {
118 					throw new PogamutException("Could not create navpoint (" + id + ") as the remote end (" + edge.toNavPointId + ") of one of its edges could not be found in the bot's worldview. Warning, id is case-sensitive the upper/lower cases of the id depends on the concrete spelling of the map that was passed to the GB2004 during startup (either from the command line or by the UT2004).", this);
119 				}
120 				if (!(np instanceof NavPoint)) {
121 					throw new PogamutException("Could not create navpoint (" + id + ") as the remote end (" + edge.toNavPointId + ") of one of its edges is not an instance of NavPoint, but " + np.getClass().getSimpleName() + ". Wrong id used?", this);
122 				}
123 				NavPoint toNavPoint = (NavPoint)np; 
124 				NavPointNeighbourLink link = new NavPointNeighbourLink(UnrealId.get(id), edge.flags, edge.collisionR, edge.collisionH, 0, null, false, edge.forceDoubleJump, edge.neededJump, false, false, 0, newNavPoint, toNavPoint);
125 				newNavPoint.getOutgoingEdges().put(link.getToNavPoint().getId(), link);
126 				link.getToNavPoint().getIncomingEdges().put(newNavPoint.getId(), link);
127 			}
128 			
129 			agent.getWorldView().notifyImmediately(newNavPoint);
130 		}
131 		
132 		/**
133 		 * Creates new edge builder for the navpoint you're creating.
134 		 * <p><p>
135 		 * After setting up the edge properties, use {@link NewNavPointEdgeBuilder#createEdge()} to return to this object (and truly creates the edge!).
136 		 * 
137 		 * @return edge builder
138 		 */
139 		public NewNavPointEdgeBuilder<NewNavPointBuilder> newEdge() {
140 			return new NewNavPointEdgeBuilder<NewNavPointBuilder>(this);
141 		}
142 		
143 		/**
144 		 * Creates new edge (to 'navPointId') builder for the navpoint you're creating.
145 		 * <p><p>
146 		 * After setting up the edge properties, use {@link NewNavPointEdgeBuilder#createEdge()} to return to this object (and truly creates the edge!).
147 		 * 
148 		 * @param navPointId will be auto-prefixed
149 		 * @return edge builder
150 		 */
151 		public NewNavPointEdgeBuilder<NewNavPointBuilder> newEdgeTo(String navPointId) {
152 			NullCheck.check(navPointId, "navPointId");
153 			NewNavPointEdgeBuilder<NewNavPointBuilder> edgeBuilder = new NewNavPointEdgeBuilder<NewNavPointBuilder>(this);
154 			edgeBuilder.setTo(navPointId);
155 			return edgeBuilder;
156 		}
157 		
158 		/**
159 		 * Creates simple edge that leads from the navpoint you're currently creating to 'navPointId'
160 		 * @param navPointId will be auto-prefixed (if enabled, which is default)
161 		 */
162 		public void createSimpleEdgeTo(String navPointId) {
163 			NullCheck.check(navPointId, "navPointId");
164 			newEdgeTo(navPointId).createEdge();
165 		}
166 
167 		/**
168 		 * Adds new edge into {@link NewNavPointBuilder#edges}. Their true creation is postponed until {@link NewNavPointBuilder#createNavPoint()}.
169 		 * 
170 		 * @param newNavPointEdgeBuilder
171 		 */
172 		protected void addEdge(NewNavPointEdgeBuilder newNavPointEdgeBuilder) {
173 			this.edges.add(newNavPointEdgeBuilder);
174 		}
175 		
176 	}
177 	
178 	/**
179 	 * Represents the edge of the navpoint you're newly creating.
180 	 * <p><p>
181 	 * WARNING: the created edge is oriented! Its counterpart (from the remote navpoint to newly created one) must be created manually! (If needed.)
182 	 * 
183 	 * @author Jimmy
184 	 */
185 	public class NewNavPointEdgeBuilder<OWNER> {
186 		
187 		protected OWNER owner;
188 		protected UnrealId toNavPointId;
189 		protected int collisionR = (int)UnrealUtils.CHARACTER_COLLISION_RADIUS * 2;
190 		protected int collisionH = (int)UnrealUtils.CHARACTER_HEIGHT_STANDING + 10;
191 		protected boolean forceDoubleJump = false;
192 		protected Vector3d neededJump;
193 		protected int flags = 0;
194 		
195 		protected NewNavPointEdgeBuilder(OWNER owner) {
196 			this.owner = owner;	
197 			NullCheck.check(this.owner, "owner");
198 		}
199 		
200 		/**
201 		 * Sets the remote end of the edge (i.e., navpoint id where the edge is leading to), corresponds to {@link NavPointNeighbourLink#getToNavPoint()}.
202 		 * 
203 		 * @param navPointId will be auto-prefixed (if enabled, which is default)
204 		 * @return
205 		 */
206 		public NewNavPointEdgeBuilder<OWNER> setTo(String navPointId) {
207 			NullCheck.check(navPointId, "navPointId");
208 			this.toNavPointId = UnrealId.get(autoPrefix(navPointId));
209 			return this;
210 		}
211 		
212 		/**
213 		 * Sets the remote end of the edge (i.e., navpoint id where the edge is leading to), corresponds to {@link NavPointNeighbourLink#getToNavPoint()}.
214 		 * 
215 		 * @param navPointId WON'T BE AUTO-PREFIXED AS IT IS ALREADY EXISTING ID!!!
216 		 * @return
217 		 */
218 		public NewNavPointEdgeBuilder<OWNER> setTo(UnrealId navPointId) {
219 			this.toNavPointId = navPointId;
220 			return this;
221 		}
222 		
223 		/**
224 		 * Sets collision radius of the edge, corresponds to {@link NavPointNeighbourLink#getCollisionR()}.
225 		 * 
226 		 * @param collisionRadius
227 		 * @return
228 		 */
229 		public NewNavPointEdgeBuilder<OWNER> setCollisionRadius(int collisionRadius) {
230 			this.collisionR = collisionRadius;
231 			return this;
232 		}
233 		
234 		/**
235 		 * Sets collision height of the edge, corresponds to {@link NavPointNeighbourLink#getCollisionH()}.
236 		 * 
237 		 * @param collisionHeight
238 		 * @return
239 		 */
240 		public NewNavPointEdgeBuilder<OWNER> setCollisionHeight(int collisionHeight) {
241 			this.collisionH = collisionHeight;
242 			return this;
243 		}
244 		
245 		/**
246 		 * Sets the location from where the bot should jump to reach the target, corresponds to {@link NavPointNeighbourLink#getNeededJump()}.
247 		 * 
248 		 * @param x
249 		 * @param y
250 		 * @param z
251 		 * @return
252 		 */
253 		public NewNavPointEdgeBuilder<OWNER> setNeededJump(double x, double y, double z) {
254 			this.neededJump = new Vector3d(x,y,z);
255 			return this;
256 		}
257 		
258 		/**
259 		 * Sets the flag "double jump is needed" to true, corresponds to {@link NavPointNeighbourLink#isForceDoubleJump()}.
260 		 * 
261 		 * @param neededJump
262 		 * @return
263 		 */
264 		public NewNavPointEdgeBuilder<OWNER> setDoubleJump() {
265 			this.forceDoubleJump = true;
266 			return this;
267 		}
268 		
269 		
270 		/**
271 		 * Sets {@link LinkFlag#WALK} flag into {@link NewNavPointEdgeBuilder#flags} of the new navpoint edge,
272 		 * corresponds to {@link NavPointNeighbourLink#getFlags()}.
273 		 * 
274 		 * @return
275 		 */
276 		public NewNavPointEdgeBuilder<OWNER> setWalkFlag() {
277 			flags |= LinkFlag.WALK.get();
278 			return this;
279 		}
280 		
281 		/**
282 		 * Sets {@link LinkFlag#FLY} flag into {@link NewNavPointEdgeBuilder#flags} of the new navpoint edge,
283 		 * corresponds to {@link NavPointNeighbourLink#getFlags()}.
284 		 * 
285 		 * @return
286 		 */
287 		public NewNavPointEdgeBuilder<OWNER> setFlyFlag() {
288 			flags |= LinkFlag.FLY.get();
289 			return this;
290 		}
291 		
292 		/**
293 		 * Sets {@link LinkFlag#SWIM} flag into {@link NewNavPointEdgeBuilder#flags} of the new navpoint edge,
294 		 * corresponds to {@link NavPointNeighbourLink#getFlags()}.
295 		 * 
296 		 * @return
297 		 */
298 		public NewNavPointEdgeBuilder<OWNER> setSwimFlag() {
299 			flags |= LinkFlag.SWIM.get();
300 			return this;
301 		}
302 		
303 		/**
304 		 * Sets {@link LinkFlag#JUMP} flag into {@link NewNavPointEdgeBuilder#flags} of the new navpoint edge,
305 		 * corresponds to {@link NavPointNeighbourLink#getFlags()}.
306 		 * 
307 		 * @return
308 		 */
309 		public NewNavPointEdgeBuilder<OWNER> setJumpFlag() {
310 			flags |= LinkFlag.JUMP.get();
311 			return this;
312 		}
313 		
314 		/**
315 		 * Sets {@link LinkFlag#DOOR} flag into {@link NewNavPointEdgeBuilder#flags} of the new navpoint edge,
316 		 * corresponds to {@link NavPointNeighbourLink#getFlags()}.
317 		 * 
318 		 * @return
319 		 */
320 		public NewNavPointEdgeBuilder<OWNER> setDoorFlag() {
321 			flags |= LinkFlag.DOOR.get();
322 			return this;
323 		}
324 		
325 		/**
326 		 * Sets {@link LinkFlag#SPECIAL} flag into {@link NewNavPointEdgeBuilder#flags} of the new navpoint edge,
327 		 * corresponds to {@link NavPointNeighbourLink#getFlags()}.
328 		 * 
329 		 * @return
330 		 */
331 		public NewNavPointEdgeBuilder<OWNER> setSpecialFlag() {
332 			flags |= LinkFlag.SPECIAL.get();
333 			return this;
334 		}
335 		
336 		/**
337 		 * Sets {@link LinkFlag#LADDER} flag into {@link NewNavPointEdgeBuilder#flags} of the new navpoint edge,
338 		 * corresponds to {@link NavPointNeighbourLink#getFlags()}.
339 		 * 
340 		 * @return
341 		 */
342 		public NewNavPointEdgeBuilder<OWNER> setLadderFlag() {
343 			flags |= LinkFlag.LADDER.get();
344 			return this;
345 		}
346 		
347 		/**
348 		 * Sets {@link LinkFlag#PROSCRIBED} flag into {@link NewNavPointEdgeBuilder#flags} of the new navpoint edge,
349 		 * corresponds to {@link NavPointNeighbourLink#getFlags()}.
350 		 * 
351 		 * @return
352 		 */
353 		public NewNavPointEdgeBuilder<OWNER> setProscribedFlag() {
354 			flags |= LinkFlag.PROSCRIBED.get();
355 			return this;
356 		}
357 		
358 		/**
359 		 * Sets {@link LinkFlag#FORCED} flag into {@link NewNavPointEdgeBuilder#flags} of the new navpoint edge,
360 		 * corresponds to {@link NavPointNeighbourLink#getFlags()}.
361 		 * 
362 		 * @return
363 		 */
364 		public NewNavPointEdgeBuilder<OWNER> setForcedFlag() {
365 			flags |= LinkFlag.FORCED.get();
366 			return this;
367 		}
368 		
369 		/**
370 		 * Sets {@link LinkFlag#PLAYERONLYK} flag into {@link NewNavPointEdgeBuilder#flags} of the new navpoint edge,
371 		 * corresponds to {@link NavPointNeighbourLink#getFlags()}.
372 		 * 
373 		 * @return
374 		 */
375 		public NewNavPointEdgeBuilder<OWNER> setPlayerOnlyFlag() {
376 			flags |= LinkFlag.PLAYERONLY.get();
377 			return this;
378 		}
379 		
380 		/**
381 		 * Finalizes the creation of the edge.
382 		 * <p><p>
383 		 * Edge remote end must be set via {@link NewNavPointEdgeBuilder#setTo(String)} before otherwise an exception is thrown.
384 		 * <p><p>
385 		 * WARNING: the created edge is oriented! Its counterpart (from the remote navpoint to newly created one) must be created manually! (If needed.)
386 		 * 
387 		 * @return nav point the edge is going from (i.e., one you're building)
388 		 */
389 		public OWNER createEdge() {
390 			if (toNavPointId == null) {
391 				throw new PogamutException("Could not create edge - toNavPoint not specified, you must call setTo() with non-null argument (to specify the other end of the edge) before calling this method.", this);
392 			}
393 			((NewNavPointBuilder)owner).addEdge(this);
394 			return ((OWNER)owner);
395 		}
396 
397 	}
398 	
399 	/**
400 	 * Builder that allows you to modify edges of existing navpoint using {@link ExistingNavPointModifier#createEdge()}, 
401 	 * {@link ExistingNavPointModifier#createEdgeTo(String)}, {@link ExistingNavPointModifier#modifyEdgeTo(String)} methods.
402 	 * 
403 	 * @author Jimmy
404 	 */
405 	public class ExistingNavPointModifier {
406 		
407 		private NavPoint navPoint;
408 
409 		protected ExistingNavPointModifier(NavPoint navPoint) {
410 			this.navPoint = navPoint;
411 			NullCheck.check(this.navPoint, "navPoint");
412 		}
413 		
414 		/**
415 		 * Removes edge that is leading from this navpoint to 'navPointId'. Does not do anything if such edge does not exist.
416 		 * <p><p>
417 		 * Removes only one edge, if the edge with opposite direction exists, it leaves it there.
418 		 * 
419 		 * @param navPointId will be auto-prefixed (if enabled, which is default)
420 		 */
421 		public void removeEdgeTo(String navPointId) {
422 			NullCheck.check(navPointId, "navPointId");
423 			navPointId = autoPrefix(navPointId);
424 			UnrealId navPointUnrealId = UnrealId.get(navPointId);
425 			navPoint.getOutgoingEdges().remove(navPointUnrealId);
426 			Object np = agent.getWorldView().get(navPointUnrealId);
427 			if (np != null && (np instanceof NavPoint)) {
428 				((NavPoint)np).getIncomingEdges().remove(navPoint.getId());
429 			}
430 		}
431 		
432 		/**
433 		 * Removes edge that is leading from this navpoint to 'navPointId'. Does not do anything if such edge does not exist.
434 		 * <p><p>
435 		 * Removes both edges, if the edge with opposite direction exists, it is deleted as well.
436 		 * 
437 		 * @param navPointId will be auto-prefixed (if enabled, which is default)
438 		 */
439 		public void removeEdgesBetween(String navPointId) {
440 			NullCheck.check(navPointId, "navPointId");
441 			navPointId = autoPrefix(navPointId);
442 			UnrealId toNavPointUnrealId = UnrealId.get(navPointId);
443 			
444 			Object np = agent.getWorldView().get(toNavPointUnrealId);
445 			NavPoint toNavPoint = null;
446 			if (np != null && (np instanceof NavPoint)) {
447 				toNavPoint = (NavPoint)np;
448 			}
449 			
450 			navPoint.getOutgoingEdges().remove(toNavPointUnrealId);
451 			navPoint.getIncomingEdges().remove(toNavPointUnrealId);
452 			
453 			if (toNavPoint != null) {
454 				toNavPoint.getOutgoingEdges().remove(navPoint.getId());
455 				toNavPoint.getIncomingEdges().remove(navPoint.getId());
456 			}
457 		}
458 		
459 		/**
460 		 * Returns a builder that will allow you to modify properties of the edge that is leading to 'navPointId'.
461 		 * <p><p>
462 		 * If no previous edge (leading to the same navpoint) exists, new one is created automatically.
463 		 * <p><p>
464 		 * Call {@link ExistingNavPointEdgeBuilder#modifyEdge()} when done specifying edge properties.
465 		 * 
466 		 * @param navPointId will be auto-prefixed (if enabled, which is default)
467 		 * @return
468 		 */
469 		public ExistingNavPointEdgeBuilder modifyEdgeTo(String navPointId) {
470 			NullCheck.check(navPointId, "navPointId");
471 			navPointId = autoPrefix(navPointId);
472 			UnrealId navPointUnrealId = UnrealId.get(navPointId);
473 			NavPointNeighbourLink link = navPoint.getOutgoingEdges().get(navPointUnrealId);
474 			if (link != null) {
475 				return new ExistingNavPointEdgeBuilder(this, link);
476 			} else {
477 				return new ExistingNavPointEdgeBuilder(this).setTo(navPointId);
478 			}
479 		}
480 		
481 		/**
482 		 * Returns a builder that will allow you to create new edge that is leading to 'navPointId'. Note that the returned {@link ExistingNavPointEdgeBuilder}
483 		 * will have only {@link ExistingNavPointEdgeBuilder#setTo(String)} filled. It also has different behavior than in the case
484 		 * of {@link ExistingNavPointModifier#modifyEdgeTo(String)} as this new edge builder won't have any properties (except {@link ExistingNavPointEdgeBuilder#setTo(String)}) filled.
485 		 * <p><p>
486 		 * If same edge (leading to the same navpoint) exists, it is replaced automatically.
487 		 * <p><p>
488 		 * Call {@link ExistingNavPointEdgeBuilder#createEdge()} when done specifying edge properties.
489 		 * 
490 		 * @param navPointId will be auto-prefixed (if enabled, which is default)
491 		 * @return
492 		 */
493 		public ExistingNavPointEdgeBuilder createEdgeTo(String navPointId) {
494 			NullCheck.check(navPointId, "navPointId");
495 			navPointId = autoPrefix(navPointId);
496 			ExistingNavPointEdgeBuilder builder = new ExistingNavPointEdgeBuilder(this, null);
497 			builder.setTo(navPointId);
498 			return builder;
499 		}
500 		
501 		/**
502 		 * Returns a builder that will allow you to create new edge. Note that the returned {@link ExistingNavPointEdgeBuilder}
503 		 * is empty, you must set the remote end of the edge manually using {@link ExistingNavPointEdgeBuilder#setTo(String)}.
504 		 * It also has different behavior than in the case
505 		 * of {@link ExistingNavPointModifier#modifyEdgeTo(String)} as this new edge builder won't have any properties (except {@link ExistingNavPointEdgeBuilder#setTo(String)}) filled.
506 		 * <p><p>
507 		 * If same edge (leading to the same navpoint) exists, it is replaced automatically.
508 		 * <p><p>
509 		 * Call {@link ExistingNavPointEdgeBuilder#createEdge()} when done specifying edge properties.
510 		 * 
511 		 * @return
512 		 */
513 		public ExistingNavPointEdgeBuilder createEdge() {
514 			return new ExistingNavPointEdgeBuilder(this, null);
515 		}
516 		
517 		/**
518 		 * Creates simple edge that leads from the navpoint you're currently creating to 'navPointId'.
519 		 * <p><p>
520 		 * If edge exists, it won't be modified.
521 		 * 
522 		 * @param navPointId will be auto-prefixed (if enabled, which is default)
523 		 */
524 		public void createSimpleEdgeTo(String navPointId) {
525 			NullCheck.check(navPointId, "navPointId");
526 			createEdgeTo(navPointId).createEdge();
527 		}
528 		
529 		/**
530 		 * Creates two simple edges between this navpoint and the navpoint with 'navPointId'.
531 		 * <p><p>
532 		 * If any of those two edges exists, it won't be modified.
533 		 * 
534 		 * @param navPointId will be auto-prefixed (if enabled, which is default)
535 		 */
536 		public void createSimpleEdgesBetween(String navPointId) {
537 			NullCheck.check(navPointId, "navPointId");
538 			createEdgeTo(navPointId).createEdge();
539 			modifyNavPoint(navPointId).createSimpleEdgeTo(navPoint.getId().getStringId());
540 		}
541 		
542 	}
543 	
544 	public class ExistingNavPointEdgeBuilder extends NewNavPointEdgeBuilder<ExistingNavPointModifier> {
545 
546 		private NavPointNeighbourLink parentLink;
547 
548 		protected ExistingNavPointEdgeBuilder(ExistingNavPointModifier navPointModifier) {
549 			super(navPointModifier);
550 		}
551 		
552 		protected ExistingNavPointEdgeBuilder(ExistingNavPointModifier navPointModifier, NavPointNeighbourLink parent) {
553 			super(navPointModifier);
554 			this.parentLink = parent;
555 			if (this.parentLink != null) {
556 				this.collisionH = this.parentLink.getCollisionH();
557 				this.collisionR = this.parentLink.getCollisionR();
558 				this.flags = this.parentLink.getFlags();
559 				this.forceDoubleJump = this.parentLink.isForceDoubleJump();
560 				this.neededJump = this.parentLink.getNeededJump();
561 				this.toNavPointId = this.parentLink.getToNavPoint().getId();
562 			}
563 		}
564 		
565 		@Override
566 		public ExistingNavPointEdgeBuilder setTo(String navPointId) {
567 			super.setTo(navPointId);
568 			return this;
569 		}
570 		
571 		@Override
572 		public ExistingNavPointEdgeBuilder setTo(UnrealId navPointId) {
573 			super.setTo(navPointId);
574 			return this;
575 		}
576 		
577 		@Override
578 		public ExistingNavPointEdgeBuilder setCollisionRadius(int collisionRadius) {
579 			super.setCollisionRadius(collisionRadius);
580 			return this;
581 		}
582 		
583 		@Override
584 		public ExistingNavPointEdgeBuilder setCollisionHeight(int collisionHeight) {
585 			super.setCollisionHeight(collisionHeight);
586 			return this;
587 		}
588 		
589 		@Override
590 		public ExistingNavPointEdgeBuilder setNeededJump(double x, double y, double z) {
591 			super.setNeededJump(x, y, z);
592 			return this;
593 		}
594 		
595 		/**
596 		 * Removes "needed jump at location" from the edge.
597 		 * @return
598 		 */
599 		public ExistingNavPointEdgeBuilder removeNeededJump() {
600 			this.neededJump = null;
601 			return this;
602 		}
603 		
604 		@Override
605 		public ExistingNavPointEdgeBuilder setDoubleJump() {
606 			super.setDoubleJump();
607 			return this;
608 		}
609 		
610 		/**
611 		 * Removes "requires double jump" from the edge.
612 		 * @return
613 		 */
614 		public ExistingNavPointEdgeBuilder removeDoubleJump() {
615 			this.forceDoubleJump = false;
616 			return this;
617 		}
618 		
619 		
620 		@Override
621 		public ExistingNavPointEdgeBuilder setWalkFlag() {
622 			super.setWalkFlag();
623 			return this;
624 		}
625 		
626 		/**
627 		 * Removes {@link LinkFlag#WALK} flag from edge flags.
628 		 * 
629 		 * @return
630 		 */
631 		public ExistingNavPointEdgeBuilder removeWalkFlag() {
632 			this.flags = (this.flags | LinkFlag.WALK.get()) ^ LinkFlag.WALK.get(); 
633 			return this;
634 		}
635 		
636 		@Override
637 		public ExistingNavPointEdgeBuilder setFlyFlag() {
638 			super.setFlyFlag();
639 			return this;
640 		}
641 		
642 		/**
643 		 * Removes {@link LinkFlag#FLY} flag from edge flags.
644 		 * 
645 		 * @return
646 		 */
647 		public ExistingNavPointEdgeBuilder removeFlyFlag() {
648 			this.flags = (this.flags | LinkFlag.FLY.get()) ^ LinkFlag.FLY.get(); 
649 			return this;
650 		}
651 		
652 		@Override
653 		public ExistingNavPointEdgeBuilder setSwimFlag() {
654 			super.setSwimFlag();
655 			return this;
656 		}
657 		
658 		/**
659 		 * Removes {@link LinkFlag#SWIM} flag from edge flags.
660 		 * 
661 		 * @return
662 		 */
663 		public ExistingNavPointEdgeBuilder removeSwimFlag() {
664 			this.flags = (this.flags | LinkFlag.SWIM.get()) ^ LinkFlag.SWIM.get(); 
665 			return this;
666 		}
667 		
668 		@Override
669 		public ExistingNavPointEdgeBuilder setJumpFlag() {
670 			super.setJumpFlag();
671 			return this;
672 		}
673 		
674 		/**
675 		 * Removes {@link LinkFlag#JUMP} flag from edge flags.
676 		 * 
677 		 * @return
678 		 */
679 		public ExistingNavPointEdgeBuilder removeJumpFlag() {
680 			this.flags = (this.flags | LinkFlag.JUMP.get()) ^ LinkFlag.JUMP.get(); 
681 			return this;
682 		}
683 		
684 		@Override
685 		public ExistingNavPointEdgeBuilder setDoorFlag() {
686 			super.setDoorFlag();
687 			return this;
688 		}
689 		
690 		/**
691 		 * Removes {@link LinkFlag#DOOR} flag from edge flags.
692 		 * 
693 		 * @return
694 		 */
695 		public ExistingNavPointEdgeBuilder removeDoorFlag() {
696 			this.flags = (this.flags | LinkFlag.DOOR.get()) ^ LinkFlag.DOOR.get(); 
697 			return this;
698 		}
699 		
700 		@Override
701 		public ExistingNavPointEdgeBuilder setSpecialFlag() {
702 			super.setSpecialFlag();
703 			return this;
704 		}
705 		
706 		/**
707 		 * Removes {@link LinkFlag#SPECIAL} flag from edge flags.
708 		 * 
709 		 * @return
710 		 */
711 		public ExistingNavPointEdgeBuilder removeSpecialFlag() {
712 			this.flags = (this.flags | LinkFlag.SPECIAL.get()) ^ LinkFlag.SPECIAL.get(); 
713 			return this;
714 		}
715 		
716 		@Override
717 		public ExistingNavPointEdgeBuilder setLadderFlag() {
718 			super.setLadderFlag();
719 			return this;
720 		}
721 		
722 		/**
723 		 * Removes {@link LinkFlag#LADDER} flag from edge flags.
724 		 * 
725 		 * @return
726 		 */
727 		public ExistingNavPointEdgeBuilder removeLadderFlag() {
728 			this.flags = (this.flags | LinkFlag.LADDER.get()) ^ LinkFlag.LADDER.get(); 
729 			return this;
730 		}
731 		
732 		@Override
733 		public ExistingNavPointEdgeBuilder setProscribedFlag() {
734 			super.setProscribedFlag();
735 			return this;
736 		}
737 		
738 		/**
739 		 * Removes {@link LinkFlag#PROSCRIBED} flag from edge flags.
740 		 * 
741 		 * @return
742 		 */
743 		public ExistingNavPointEdgeBuilder removeProscribedFlag() {
744 			this.flags = (this.flags | LinkFlag.PROSCRIBED.get()) ^ LinkFlag.PROSCRIBED.get(); 
745 			return this;
746 		}
747 		
748 		@Override
749 		public ExistingNavPointEdgeBuilder setForcedFlag() {
750 			super.setForcedFlag();
751 			return this;
752 		}
753 		
754 		/**
755 		 * Removes {@link LinkFlag#FORCED} flag from edge flags.
756 		 * 
757 		 * @return
758 		 */
759 		public ExistingNavPointEdgeBuilder removeForcedFlag() {
760 			this.flags = (this.flags | LinkFlag.FORCED.get()) ^ LinkFlag.FORCED.get(); 
761 			return this;
762 		}
763 		
764 		@Override
765 		public ExistingNavPointEdgeBuilder setPlayerOnlyFlag() {
766 			super.setPlayerOnlyFlag();
767 			return this;
768 		}
769 		
770 		/**
771 		 * Removes {@link LinkFlag#PLAYERONLY} flag from edge flags.
772 		 * 
773 		 * @return
774 		 */
775 		public ExistingNavPointEdgeBuilder removePlayerOnlyFlag() {
776 			this.flags = (this.flags | LinkFlag.PLAYERONLY.get()) ^ LinkFlag.PLAYERONLY.get(); 
777 			return this;
778 		}
779 		
780 		public ExistingNavPointEdgeBuilder setFlags(Integer flags) {
781 			this.flags = flags;
782 			return this;
783 		}
784 		
785 		/**
786 		 * Clears all flags to 0.
787 		 * 
788 		 * @return
789 		 */
790 		public ExistingNavPointEdgeBuilder clearFlags() {
791 			this.flags = 0;
792 			return this;
793 		}
794 		
795 		/**
796 		 * Immediately creates a new edge. Checks whether the same edge does not already exist (if so, replaces it).
797 		 * <p><p>
798 		 * WARNING: the created edge is oriented! Its counterpart (from the remote navpoint to one that is being modified) must be created manually! (If needed.)
799 		 *
800 		 * @return previously used navpoint modifier
801 		 */
802 		@Override
803 		public ExistingNavPointModifier createEdge() {
804 			if (toNavPointId == null) {
805 				throw new PogamutException("Could not create/modify edge from navpoint '" + owner.navPoint.getId().getStringId() + "' as toNavPoint not specified, you must call setTo() with non-null argument (to specify the other end of the edge) before calling this method.", this);
806 			}
807 			Object np = agent.getWorldView().get(toNavPointId);
808 			if (np == null) {
809 				throw new PogamutException("Could not create/modify navpoint edge from '" + owner.navPoint.getId().getStringId() + "' as the remote end (" + toNavPointId.getStringId() + ") could not be found in the bot's worldview. Warning, id is case-sensitive the upper/lower cases of the id depends on the concrete spelling of the map that was passed to the GB2004 during startup (either from the command line or by the UT2004).", this);
810 			}
811 			if (!(np instanceof NavPoint)) {
812 				throw new PogamutException("Could not create/modify navpoint edge from '" + owner.navPoint.getId().getStringId() + "' as the remote end '" + toNavPointId.getStringId() + "' is not an instance of NavPoint but " + np.getClass().getSimpleName() + ". Wrong id specified?", this);
813 			}
814 			
815 			NavPoint toNavPoint = (NavPoint)np;
816 			
817 			NavPointNeighbourLink link = null;
818 			
819 			if (parentLink == null) {
820 				link = new NavPointNeighbourLink(owner.navPoint.getId(), flags, collisionR, collisionH, 0, null, false, forceDoubleJump, neededJump, false, false, 0, owner.navPoint, toNavPoint);				
821 			} else {
822 				link = new NavPointNeighbourLink(owner.navPoint.getId(), flags, collisionR, collisionH, parentLink.getTranslocZOffset(), parentLink.getTranslocTargetTag(), parentLink.isOnlyTranslocator(), forceDoubleJump, neededJump, parentLink.isNeverImpactJump(), parentLink.isNoLowGrav(), parentLink.getCalculatedGravityZ(), owner.navPoint, toNavPoint);
823 			}
824 			
825 			owner.navPoint.getOutgoingEdges().put(link.getToNavPoint().getId(), link);
826 			link.getToNavPoint().getIncomingEdges().put(owner.navPoint.getId(), link);
827 			
828 			return owner;
829 		}
830 		
831 		/**
832 		 * Alias for {@link ExistingNavPointEdgeBuilder#createEdge()}. 
833 		 * <p><p>
834 		 * WARNING: the modify edge is oriented! Its counterpart (from the remote navpoint to one that is being modifier) must be modified manually! (If needed.)
835 		 *
836 		 * @return previously used navpoint modifier
837 		 */
838 		public ExistingNavPointModifier modifyEdge() {
839 			return createEdge();
840 		}
841 
842 	}
843 	
844 	/**
845 	 * GameInfo listener.
846 	 */
847 	private class GameInfoListener implements IWorldObjectEventListener<GameInfo, IWorldObjectEvent<GameInfo>>
848 	{
849 		@Override
850 		public void notify(IWorldObjectEvent<GameInfo> event)
851 		{
852 			lastGameInfo = event.getObject();
853 			if (lastGameInfo.getLevel() == null) {
854 				throw new PogamutException("GameInfo.getLevel() is null!!!", this);
855 			}
856 			mapNameLowerChar = lastGameInfo.getLevel().toLowerCase();
857 		}
858 
859 		/**
860 		 * Constructor. Registers itself on the given WorldView object.
861 		 * @param worldView WorldView object to listent to.
862 		 */
863 		public GameInfoListener(IWorldView worldView)
864 		{
865 			worldView.addObjectListener(GameInfo.class, this);
866 		}
867 	}
868 
869 	/** GameInfo listener */
870 	GameInfoListener gameInfoListener;
871 	
872 	/** Las info about the game the bot is in */
873 	GameInfo lastGameInfo = null;
874 	
875 	String mapNameLowerChar = null;
876 	
877 	public NavigationGraphBuilder(UT2004Bot bot) {
878 		this(bot, null);
879 	}
880 	
881 	public NavigationGraphBuilder(UT2004Bot bot, Logger log) {
882 		this(bot, log, null);
883 	}
884 	
885 	public NavigationGraphBuilder(UT2004Bot bot, Logger log, ComponentDependencies dependencies) {
886 		super(bot, log, dependencies);
887 		gameInfoListener = new GameInfoListener(bot.getWorldView());
888 	}
889 	
890 	@Override
891 	protected void cleanUp() {
892 		super.cleanUp();
893 		lastGameInfo = null;
894 		mapNameLowerChar = null;
895 	}
896 	
897 	/**
898 	 * Returns name of the map the UT2004 is currently running.
899 	 * <p><p>
900 	 * The name is used as a prefix to all IDs in the game. IDs in the world view are case-sensitive!
901 	 * <p><p>
902 	 * Note that NavigationGraphBuilder is automatically prefixing all navpoint ids with "mapName.", which means, that you do not
903 	 * need to specify the id of navpoints as (e.g.) "DM-1on1-Albatross.PathNode2", "PathNode2" suffices. If you want to change this behavior
904 	 * call {@link NavigationGraphBuilder#setAutoPrefix(boolean)} with "false".
905 	 * 
906 	 * @return
907 	 */
908 	public String getMapName() {
909 		if (lastGameInfo == null) return null;
910 		return lastGameInfo.getLevel();
911 	}
912 	
913 	/**
914 	 * Tells, whether the UT2004 is currently running map with name 'name'.
915 	 * @param name
916 	 * @return
917 	 */
918 	public boolean isMapName(String name) {
919 		if (lastGameInfo == null) return false;
920 		if (name == null) return false;
921 		return lastGameInfo.getLevel().toLowerCase().equals(name.toLowerCase());
922 	}
923 	
924 	private boolean autoPrefix = true;
925 
926 	/**
927 	 * Whether this instance has been used to alter the navigation graph.
928 	 */
929 	private boolean used;
930 	
931 	/**
932 	 * Whether {@link NavigationGraphBuilder} is auto prefixing all navpoint ids with current map name.
933 	 * <p><p>
934 	 * As default, auto-prefixing is enabled.
935 	 * <p><p>
936      * Note that even if auto-prefixing enabled you may prefix ids of navpoints with map name, the auto-prefixing implemented by {@link NavigationGraphBuilder#autoPrefix(String)} 
937      * will detects that and auto-correct upper/lower case of this existing prefix if needed. Also you may use it as a validation feature because
938      * the {@link NavigationGraphBuilder#autoPrefix(String)} will raise an exception if the prefix does not match the current map name.
939      * 
940 	 * @return whether auto-prefixing is enabled
941 	 */
942 	public boolean isAutoPrefix() {
943 		return autoPrefix;
944 	}
945 
946 	/**
947 	 * Enables (== true), disables (== false) navpoint ids auto prefixing feature.
948 	 * <p><p>
949 	 * As default, auto-prefixing is enabled.
950 	 * <p><p>
951      * Note that even if auto-prefixing enabled you may prefix ids of navpoints with map name, the auto-prefixing implemented by {@link NavigationGraphBuilder#autoPrefix(String)} 
952      * will detects that and auto-correct upper/lower case of this existing prefix if needed. Also you may use it as a validation feature because
953      * the {@link NavigationGraphBuilder#autoPrefix(String)} will raise an exception if the prefix does not match the current map name.
954      * 
955 	 * @param autoPrefix
956 	 */
957 	public void setAutoPrefix(boolean autoPrefix) {
958 		this.autoPrefix = autoPrefix;
959 	}
960 	
961 	/**
962 	 * It returns 'navPointId' prefixed with "{@link NavigationGraphBuilder#getMapName()}.".
963 	 * <p><p>
964      * Note that you may pass prefixed navPointId into this method, it will detect it that and auto-correct upper/lower case of this existing prefix if needed. 
965      * Also you may use it as a validation feature because
966      * the {@link NavigationGraphBuilder#autoPrefix(String)} will raise an exception if the prefix does not match the current map name.
967 	 * 
968 	 * @param navPointId will be auto-prefixed (if enabled, which is default)
969 	 * @return
970 	 */
971 	public String getPrefixed(String navPointId) {
972 		// auto prefixing is enabled
973 		if (getMapName() == null) {
974 			throw new PogamutException("GameInfo was not received yet, can't auto-prefix name of the navpoint '" + navPointId + "'.", this);
975 		}
976 		if (navPointId.toLowerCase().startsWith(mapNameLowerChar + ".")) {
977 			// already prefixed!
978 			if (!navPointId.startsWith(getMapName())) {
979 				// but wrong upper/lower case detected, replace!
980 				navPointId = getMapName() + navPointId.substring(mapNameLowerChar.length());
981 			}
982 			// correctly prefixed, just return it
983 			return navPointId;
984 		} else {
985 			// not correctly prefixed, check whether there is any prefix at all?
986 			if (navPointId.contains(".")) {
987 				// yes there is -> map name validation fails!
988 				throw new PogamutException("navPointId '" + navPointId + "' is already prefixed with '" + navPointId.substring(0, navPointId.indexOf(".")) + "' which is different from current map name '" + getMapName() + "', map name validation fails!", this);
989 			}
990 			// no there is not ... so prefix it!
991 			return getMapName() + "." + navPointId;
992 		}
993 	}
994 	
995 	/**
996 	 * If {@link NavigationGraphBuilder#isAutoPrefix()} is on (== true), it returns 'navPointId' prefixed with "{@link NavigationGraphBuilder#getMapName()}.".
997 	 * Otherwise it just returns 'navPointId' as is.
998 	 * <p><p>
999      * Uses {@link NavigationGraphBuilder#getPrefixed(String)} for prefixing.
1000 	 * 
1001 	 * @param navPointId will be auto-prefixed (if enabled, which is default)
1002 	 * @return
1003 	 */
1004 	public String autoPrefix(String navPointId) {
1005 		NullCheck.check(navPointId, "navPointId");
1006 		if (autoPrefix) {
1007 			return getPrefixed(navPointId);
1008 		} else {
1009 			// auto prefixing is disabled
1010 			return navPointId;
1011 		}
1012 	}
1013 
1014 	/**
1015 	 * Creates a builder for the specification of the new navpoint you want to create and insert into the worldview.
1016 	 * <p><p>
1017 	 * Use {@link NewNavPointBuilder#createNavPoint()} when done specifying the navpoint.
1018 	 * 
1019 	 * @return navpoint builder
1020 	 */
1021 	public NewNavPointBuilder newNavPoint() {
1022 		used = true;
1023 		return new NewNavPointBuilder();
1024 	}
1025 	
1026 	/**
1027 	 * Creates a builder for the specification of the new navpoint you want to create and insert into the worldview. The builder
1028 	 * will have the id of the navpoint filled (i.e., it calls {@link NewNavPointBuilder#setId(String)} for you.
1029 	 * <p><p>
1030 	 * Use {@link NewNavPointBuilder#createNavPoint()} when done specifying the navpoint.
1031 	 * 
1032 	 * @param navPointId will be auto-prefixed (if enabled, which is default)
1033 	 * @return navpoint builder
1034 	 */
1035 	public NewNavPointBuilder newNavPoint(String navPointId) {
1036 		used = true;
1037 		NullCheck.check(navPointId, "navPointId");
1038 		return new NewNavPointBuilder().setId(navPointId);
1039 	}
1040 	
1041 	/**
1042 	 * Creates a modifier for already existing {@link NavPoint} instance, if navpoint of specified id is not found, an exception is thrown.
1043 	 * <p><p>
1044 	 * The modifier allows you to change existing edges or add new ones.
1045 	 * 
1046 	 * @param navPointId will be auto-prefixed (if enabled, which is default)
1047 	 * @return navpoint modifier for the 'navPointId'
1048 	 */
1049 	public ExistingNavPointModifier modifyNavPoint(String navPointId) {
1050 		used = true;
1051 		NullCheck.check(navPointId, "navPointId");
1052 		navPointId = autoPrefix(navPointId);
1053 		
1054 		Object np = agent.getWorldView().get(UnrealId.get(navPointId));
1055 		if (np == null) {
1056 			throw new PogamutException("Could not modify navpoint '" + navPointId + "' as it was not found in the worldview. No object under this id exists in worldview. Warning, id is case-sensitive the upper/lower cases of the id depends on the concrete spelling of the map that was passed to the GB2004 during startup (either from the command line or by the UT2004).", this);
1057 		}
1058 		if (!(np instanceof NavPoint)) {
1059 			throw new PogamutException("Could not modify navpoint '" + navPointId + "' it does not point to an NavPoint instance but " + np.getClass().getSimpleName() + ". Wrong id specified?", this);
1060 		}
1061 		return new ExistingNavPointModifier((NavPoint)np);
1062 	}
1063 	
1064 	/**
1065 	 * Creates simple (non-altered == no flags == no needed jump, etc.) leading 'fromNavPointId' to 'toNavPointId' (only one edge is created).
1066 	 * <p><p>
1067 	 * If uses {@link ExistingNavPointModifier#modifyEdgeTo(String)} for creation of new edge, so it won't replace an existing edge if such exist.
1068 	 * 
1069 	 * @param fromNavPointId will be auto-prefixed (if enabled, which is default)
1070 	 * @param toNavPointId will be auto-prefixed (if enabled, which is default)
1071 	 */
1072 	public void createSimpleEdge(String fromNavPointId, String toNavPointId) {
1073 		used = true;
1074 		NullCheck.check(fromNavPointId, "fromNavPointId");
1075 		NullCheck.check(toNavPointId, "toNavPointId");
1076 		fromNavPointId = autoPrefix(fromNavPointId);
1077 		toNavPointId = autoPrefix(toNavPointId);
1078 		modifyNavPoint(fromNavPointId).modifyEdgeTo(toNavPointId).createEdge();
1079 	}
1080 	
1081 	/**
1082 	 * Creates simple (non-altered == no flags == no needed jump, etc.) edges between specified navpoints (in both directions).
1083 	 * <p><p>
1084 	 * If uses {@link ExistingNavPointModifier#modifyEdgeTo(String)} for creation of new edge, so it won't replace an existing edge if such exist.
1085 	 * 
1086 	 * @param firstNavPointId will be auto-prefixed (if enabled, which is default)
1087 	 * @param secondNavPointId will be auto-prefixed (if enabled, which is default)
1088 	 */
1089 	public void createSimpleEdgesBetween(String firstNavPointId, String secondNavPointId) {
1090 		used = true;
1091 		NullCheck.check(firstNavPointId, "firstNavPointId");
1092 		NullCheck.check(secondNavPointId, "secondNavPointId");
1093 		firstNavPointId = autoPrefix(firstNavPointId);
1094 		secondNavPointId = autoPrefix(secondNavPointId);
1095 		modifyNavPoint(firstNavPointId).modifyEdgeTo(secondNavPointId).createEdge();
1096 		modifyNavPoint(secondNavPointId).modifyEdgeTo(firstNavPointId).createEdge();
1097 	}
1098 	
1099 	/**
1100 	 * Deletes edge that is leading from 'fromNavPointId' to 'toNavPointId'.
1101 	 * @param fromNavPointId will be auto-prefixed (if enabled, which is default)
1102 	 * @param toNavPointId will be auto-prefixed (if enabled, which is default)
1103 	 */
1104 	public void removeEdge(String fromNavPointId, String toNavPointId) {
1105 		used = true;
1106 		NullCheck.check(fromNavPointId, "fromNavPointId");
1107 		NullCheck.check(toNavPointId, "toNavPointId");
1108 		fromNavPointId = autoPrefix(fromNavPointId);
1109 		toNavPointId = autoPrefix(toNavPointId);
1110 		modifyNavPoint(fromNavPointId).removeEdgeTo(toNavPointId);
1111 	}
1112 	
1113 	/**
1114 	 * Removes both edges between two specified navpoints.
1115 	 * @param firstNavPointId will be auto-prefixed (if enabled, which is default)
1116 	 * @param secondNavPointId will be auto-prefixed (if enabled, which is default)
1117 	 */
1118 	public void removeEdgesBetween(String firstNavPointId, String secondNavPointId) {
1119 		used = true;
1120 		NullCheck.check(firstNavPointId, "firstNavPointId");
1121 		NullCheck.check(secondNavPointId, "secondNavPointId");
1122 		firstNavPointId = autoPrefix(firstNavPointId);
1123 		secondNavPointId = autoPrefix(secondNavPointId);
1124 		modifyNavPoint(firstNavPointId).removeEdgeTo(secondNavPointId);
1125 		modifyNavPoint(secondNavPointId).removeEdgeTo(firstNavPointId);
1126 	}
1127 
1128 	/**
1129 	 * Whether this instance has been used to alter navigation graph. This might interest you in case you're using {@link FloydWarshallMap}
1130 	 * as you will need to {@link FloydWarshallMap#refreshPathMatrix()} after all changes done to the navigation graph.
1131 	 * @return
1132 	 */
1133 	public boolean isUsed() {
1134 		return used;
1135 	}
1136 
1137 	/**
1138 	 * Raises / drops "used" flag, see {@link NavigationGraphBuilder#isUsed()}.
1139 	 * @param used
1140 	 */
1141 	public void setUsed(boolean used) {
1142 		this.used = used;
1143 	}
1144 	
1145 	//
1146 	// EXPORT
1147 	//
1148 	
1149 	/**
1150 	 * Export full navigation graph into XML into Target File ... for more options see {@link MapExport} where you can use {@link MapExport#getXStream()}.
1151 	 * @param targetXMLFile
1152 	 */
1153 	public void exportAsXML(File targetXMLFile) {
1154 		MapExport map = new MapExport(getMapName(), worldView.getAll(NavPoint.class).values());
1155 		map.saveXML(targetXMLFile);
1156 	}
1157 	
1158 	/**
1159 	 * Loads navigation graph data ... DOES NOT APPLY THEM YET TO WORLDVIEW! See {@link NavigationGraphBuilder#apply(MapExport)}.
1160 	 * @param sourceXMLFile
1161 	 */
1162 	public MapExport importFromXML(File sourceXMLFile) {
1163 		return MapExport.loadXML(sourceXMLFile);
1164 	}
1165 	
1166 	public void apply(MapExport export) {
1167 		List<NavPointExport> newNavPoints = new ArrayList<NavPointExport>();
1168 		// FIRST CHECK / CREATE NAV POINTS ONLY
1169 		for (NavPointExport sourceNav : export.navPoints) {
1170 			NavPoint targetNav = (NavPoint)worldView.get(sourceNav.getUnrealId());
1171 			
1172 			if (targetNav == null) {
1173 				NewNavPointBuilder builder = newNavPoint();	
1174 				builder.setId(sourceNav.Id);
1175 				if (sourceNav.Location != null) {
1176 					Location loc = new Location(sourceNav.Location);
1177 					builder.setLocation(loc.x, loc.y, loc.z);
1178 				}
1179 				builder.createNavPoint();
1180 				
1181 				newNavPoints.add(sourceNav);
1182 			} else {
1183 				ExistingNavPointModifier builder = modifyNavPoint(sourceNav.Id);
1184 				// NOT SUPPORTED YET!				
1185 			}						
1186 		}
1187 		// THEN CHECK / CREATE NAV POINT LINKS
1188 		for (NavPointExport sourceNav : export.navPoints) {
1189 			addLinks(sourceNav);
1190 		}
1191 	}
1192 
1193 	private void addLinks(NavPointExport newNav) {
1194 		NavPoint targetNav = (NavPoint)worldView.get(newNav.getUnrealId());
1195 		for (NavPointLinkExport newLink : newNav.outgoingEdges) {
1196 			if (newLink.ToNavPoint == null) {
1197 				// IGNORE!
1198 				continue;
1199 			}
1200 			
1201 			NavPointNeighbourLink link = targetNav.getOutgoingEdges().get(newLink.getUnrealId());			
1202 			
1203 			ExistingNavPointModifier navBuilder = modifyNavPoint(newNav.Id);
1204 			ExistingNavPointEdgeBuilder edgeBuilder;
1205 			
1206 			if (link == null) {
1207 				// NEW LINK
1208 				edgeBuilder = navBuilder.createEdgeTo(newLink.ToNavPoint);
1209 			} else {
1210 				// MODIFY LINK
1211 				edgeBuilder = navBuilder.modifyEdgeTo(newLink.ToNavPoint);				
1212 			}
1213 			
1214 			if (newLink.CollisionH != null) edgeBuilder.setCollisionHeight(newLink.CollisionH);
1215 			if (newLink.CollisionR != null) edgeBuilder.setCollisionRadius(newLink.CollisionR);
1216 			if (newLink.NeededJump != null) {
1217 				Location neededJump = new Location(newLink.NeededJump);
1218 				edgeBuilder.setNeededJump(neededJump.x, neededJump.y, neededJump.z);
1219 				
1220 			}
1221 			if (newLink.Flags != null) edgeBuilder.setFlags(newLink.Flags);
1222 			edgeBuilder.setTo(newLink.getUnrealId());
1223 			
1224 			if (link == null) {
1225 				// NEW LINK
1226 				edgeBuilder.createEdge();
1227 			} else {
1228 				// MODIFY LINK
1229 				edgeBuilder.modifyEdge();
1230 			}
1231 		}
1232 	}
1233 
1234 	private void add(NavPointExport sourceNav) {
1235 		Location loc = new Location(sourceNav.Location);
1236 		NewNavPointBuilder builder = newNavPoint();
1237 		builder.setId(sourceNav.Id);
1238 		builder.setLocation(loc.x, loc.y, loc.z);
1239 		builder.createNavPoint();
1240 	}
1241 	
1242 }
1243