View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.agent.navigation.loquenavigator;
2   
3   import java.util.logging.Level;
4   import java.util.logging.Logger;
5   
6   import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObject;
7   import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
8   import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
9   import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
10  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.AgentInfo;
11  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.Players;
12  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.Senses;
13  import cz.cuni.amis.pogamut.ut2004.agent.navigation.IUT2004PathRunner;
14  import cz.cuni.amis.pogamut.ut2004.bot.command.AdvancedLocomotion;
15  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
16  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Move;
17  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPointNeighbourLink;
18  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
19  import cz.cuni.amis.pogamut.ut2004.utils.LinkFlag;
20  import cz.cuni.amis.pogamut.ut2004.utils.UnrealUtils;
21  import cz.cuni.amis.utils.NullCheck;
22  
23  /**
24   * Responsible for direct running to location.
25   *
26   * <p>This class commands the agent directly to the given location. Silently
27   * tries to resolve incidental collisions, troubling pits, obstacles, etc.
28   * In other words, give me a destination and you'll be there in no time.</p>
29   *
30   * <h4>Precise jumper</h4>
31   *
32   * Most of the incident running problems and troubles can be solved by precise
33   * single-jumping or double-jumping. This class calculates the best spots for
34   * initiating such jumps and then follows jump sequences in order to nicely
35   * jump and then land exactly as it was desired.
36   *
37   * <h4>Pogamut troubles</h4>
38   *
39   * This class was supposed to use autotrace rays to scan the space and ground
40   * in from of the agent. However, results of depending on these traces were
41   * much worst than jumping whenever possible. Therefore, no autotrace is being
42   * used and the agent simply jumps a lot. Some human players do that as well.
43   * See {@link #runToLocation } for details.
44   *
45   * <h4>Speed</h4>
46   *
47   * The agent does not ever try to run faster than with speed of <i>1.0</i> as
48   * it is used by most of <i>body.runTo*()</i> methods. Anyway, speeding is not
49   * available to common players (AFAIK), so why should this agent cheat?
50   *
51   * <h4>Focus</h4>
52   *
53   * This class works with destination location as well as agent focal point.
54   * Since the agent can look at something else rather than the destination,
55   * this running API is also suitable for engaging in combat or escaping from
56   * battles.
57   * 
58   * <h4>Assumptions</h4>
59   * Following constants have been found out by playing around with UT2004:
60   * <ul>
61   * <li>single jump can jump 60 units up at max</li>
62   * <li>double jump can jump 120 units up at max, UT2004 can overcome obstacle of 5 units while jumping, so we should be able to get 125 units UP</li>
63   * <li>if not jumping, bot can overcome step 37 units high at max</li>
64   * <li>full speed is 440 units/sec</li>
65   * <li>second jump will always give us full speed of 445 units/sec</li>
66   * <li>single jump (if no falling/step is involved) takes 1 secs, i.e., it moves us forward the same amount of units as is our current velocity</li>
67   * <li>double jump (if no falling/step is involved) takes 1.5 secs, i.e., it moves us forward delay*our velocity + 1*445 forward</li>
68   * <li>how high we can jump with single/double does not depends on our velocity - we will always reach the peak of the jump</li>
69   * <li>we reach peak of the single jump in 0.5s</li>
70   * </ul> 
71   *
72   * @author Jimmy
73   * @author Juraj Simlovic [jsimlo@matfyz.cz]
74   */
75  public class KefikRunner implements IUT2004PathRunner {
76      
77  	// MAINTAINED CONTEXT
78  	
79  	/**
80       * Number of steps we have taken.
81       */
82      private int runnerStep = 0;
83  
84      /**
85       * Jumping sequence of a single-jumps.
86       */
87      private int jumpStep = 0;
88  
89      /**
90       * Collision counter.
91       */
92      private int collisionNum = 0;
93      
94      /**
95       * Collision location.
96       */
97      private Location collisionSpot = null;
98      
99      // COMPUTED CONTEXT OF THE runToLocation
100     
101     /**
102      * Current distance to the target, recalculated every {@link KefikRunner#runToLocation(Location, Location, ILocated, NavPointNeighbourLink, boolean)} invocation.
103      */
104     private double distance;
105     
106     /**
107      * Current 2D distance (only in x,y) to the target, recalculated every {@link KefikRunner#runToLocation(Location, Location, ILocated, NavPointNeighbourLink, boolean)} invocation.
108      */
109     private double distance2D;
110 
111     /**
112      * Current Z distance to the target (positive => target is higher than us, negative => target is lower than us), recalculated every {@link KefikRunner#runToLocation(Location, Location, ILocated, NavPointNeighbourLink, boolean)} invocation.
113      */
114     private double distanceZ;
115     
116     /**
117      * Current velocity of the bot, recalculated every {@link KefikRunner#runToLocation(Location, Location, ILocated, NavPointNeighbourLink, boolean)} invocation.
118      */
119     private double velocity;
120     
121     /**
122      * Current velocity in Z-coord (positive, we're going up / negative, we're going down), recalculated every {@link KefikRunner#runToLocation(Location, Location, ILocated, NavPointNeighbourLink, boolean)} invocation.
123      */
124     private double velocityZ;
125     
126     /**
127      * Whether the jump is required somewhere along the link, recalculated every {@link KefikRunner#runToLocation(Location, Location, ILocated, NavPointNeighbourLink, boolean)} invocation.
128      */
129     private boolean jumpRequired;
130     
131     /**
132      * In case of fall ({@link KefikRunner#distanceZ} < 0), how far can we get with normal fall.
133      */
134     private double fallDistance;
135     
136     // CONTEXT PASSED INTO runToLocation
137     
138     /**
139      * Current context of the {@link KefikRunner#runToLocation(Location, Location, Location, ILocated, NavPointNeighbourLink, boolean)}.
140      */
141     private Location runningFrom;
142     
143     /**
144      * Current context of the {@link KefikRunner#runToLocation(Location, Location, Location, ILocated, NavPointNeighbourLink, boolean)}.
145      */
146     private Location firstLocation;
147     
148     /**
149      * Current context of the {@link KefikRunner#runToLocation(Location, Location, Location, ILocated, NavPointNeighbourLink, boolean)}.
150      */
151     private Location secondLocation;
152     
153     /**
154      * Current context of the {@link KefikRunner#runToLocation(Location, Location, Location, ILocated, NavPointNeighbourLink, boolean)}.
155      */
156     private ILocated focus;
157     
158     /**
159      * Current context of the {@link KefikRunner#runToLocation(Location, Location, Location, ILocated, NavPointNeighbourLink, boolean)}.
160      */
161     private NavPointNeighbourLink link;
162     
163     /**
164      * Current context of the {@link KefikRunner#runToLocation(Location, Location, Location, ILocated, NavPointNeighbourLink, boolean)}.
165      */
166     private boolean reachable;
167     
168     /*========================================================================*/
169     
170     /**
171      * Returns link the bot is currently running on ... might not exist, always check against NULL!
172      */
173     public NavPointNeighbourLink getLink() {
174     	return link;
175     }
176     
177     /*========================================================================*/
178 
179     /**
180      * Initializes direct running to the given destination.
181      */
182     public void reset()
183     {
184         // reset working info
185         runnerStep = 0;
186         jumpStep = 0;
187         collisionNum = 0;
188         collisionSpot = null;
189         distance = 0;
190         distance2D = 0;
191         distanceZ = 0;
192         velocity = 0;
193         velocityZ = 0;
194         jumpRequired = false;
195     }
196 
197     /*========================================================================*/
198    
199     private void debug(String message) {
200     	if (log.isLoggable(Level.FINER)) log.finer("Runner: " + message);
201     }
202     
203     /**
204      * Return how far the normal falling will get us. (Using guessed consts...)
205      * @param distanceZ
206      * @return
207      */
208     private double getFallDistance(double distanceZ) {
209     	distanceZ = Math.abs(distanceZ);
210     	if (distanceZ == 60) return 160;
211     	if (distanceZ < 60) return 2.66667*distanceZ;
212     	return 1.3714 * distanceZ + 35.527;
213     }
214     
215     /**
216      * Returns how far the jump will get at max.
217      * 
218      * @param doubleJump
219      * @param jumpDelay
220      * @param jumpForce
221      * @param distanceZ to be jumped to (fallen to)
222      * @return
223      */
224     private double getMaxJumpDistance(boolean doubleJump, double jumpDelay, double jumpForce, double distanceZ, double velocity) {
225     	if (doubleJump) {
226     		jumpForce = Math.min(UnrealUtils.FULL_DOUBLEJUMP_FORCE, jumpForce);
227     	} else {
228     		jumpForce = Math.min(UnrealUtils.FULL_JUMP_FORCE, jumpForce);
229     	}
230     	jumpDelay = Math.min(0.75, jumpDelay);
231     	
232     	if (distanceZ >= -5) {
233     		// jumping up
234     		if (doubleJump) {
235     			return velocity * jumpDelay + (jumpForce / UnrealUtils.FULL_DOUBLEJUMP_FORCE) * 400 * (1 + jumpDelay);
236     		} else {
237     			return velocity * jumpForce;
238     		}
239     	} else {
240     		// falling down
241     		return getFallDistance(distanceZ) + getMaxJumpDistance(doubleJump, jumpDelay, jumpForce, 0, velocity);
242     	}
243     }
244     
245     /**
246      * Returns how far the jump will get us when we want to jump to the height of 'distanceZ'.
247      * <p><p>
248      * Assumes 'distanceZ' > 0
249      * 
250      * @param doubleJump whether we're using double jump
251      * @param jumpDelay (max 0.75)
252      * @param jumpForce (max {@link UnrealUtils#FULL_DOUBLEJUMP_FORCE} / {@link UnrealUtils#FULL_JUMP_FORCE})
253      * @param distanceZ to be jumped to (must be > 0)
254      * @return
255      */
256     private double getJumpUpDistance(boolean doubleJump, double jumpDelay, double jumpForce, double distanceZ, double velocity) {
257     	double jumpForceHeight;
258     	
259     	double result;
260     	
261     	if (doubleJump) {
262     		// COUNTING FOR DOUBLE JUMP
263     		
264     		jumpDelay = Math.min(0.75, jumpDelay);
265     		jumpForce = Math.min(UnrealUtils.FULL_DOUBLEJUMP_FORCE, jumpForce);
266     		
267     		// how high the jump force can get us (in distanceZ units)
268     		jumpForceHeight = (jumpForce / UnrealUtils.FULL_DOUBLEJUMP_FORCE) * 125;
269     		
270     		// total time of the jump in case of jumping to 'distanceZ = 0'
271     		double totalTimeOfTheJump = (jumpForce / UnrealUtils.FULL_DOUBLEJUMP_FORCE) + jumpDelay;
272     		
273     		if (jumpForceHeight > distanceZ) {
274     			// we're OK
275     			result = 
276     				// distance traveled when ascending with first jump
277     				velocity * jumpDelay
278     				// distance traveled when ascending with second jump
279       			  + UnrealUtils.MAX_VELOCITY * ((totalTimeOfTheJump-jumpDelay)/2)
280     			    // distance traveled when falling to 'distanceZ'
281     			  + UnrealUtils.MAX_VELOCITY * (((totalTimeOfTheJump-jumpDelay)/2) * (1-distanceZ/jumpForceHeight)); 
282     		} else {
283     			// we're doomed, we should return the distance of the peak of the jump
284     			result =
285     				// distance traveled when ascending with first jump
286     				velocity * jumpDelay
287     				// distance traveled when ascending with second jump
288     			  + ((totalTimeOfTheJump-jumpDelay)/2) * UnrealUtils.MAX_VELOCITY;
289     		}
290     	
291     	} else {
292     		// COUNTING FOR SINGLE JUMP
293     	
294     		jumpForce = Math.min(UnrealUtils.FULL_JUMP_FORCE, jumpForce);
295     		
296     		// how high the jump force can get us (in distanceZ units)
297     		jumpForceHeight = (jumpForce / UnrealUtils.FULL_JUMP_FORCE) * 55;
298     		
299     		// total time of the jump in case of jumping to 'distanceZ = 0'
300     		double totalTimeOfTheJump = jumpForce / UnrealUtils.FULL_JUMP_FORCE;
301     		
302     		if (jumpForceHeight > distanceZ) {
303     			// we're OK
304     			result = 
305         			   // distance we will travel when ascending
306     				   velocity * (totalTimeOfTheJump/2)
307     				   // distance we will travel when falling to the 'distanceZ' height
308     			     + velocity * ((totalTimeOfTheJump/2) * (1 - (distanceZ / jumpForceHeight)));
309     		} else {
310     			// we're doomed, we should return the PEAK of the jump
311     			result = velocity * (totalTimeOfTheJump/2);
312     		}
313     	}
314     	
315     	return result;
316     }
317     
318     /**
319      * Handles running directly to the specified location.
320      *
321      * <h4>Pogamut troubles</h4>
322      *
323      * <p>Reachchecks are buggy (they ignore most of the pits). Autotrace rays
324      * are buggy (they can not be used to scan the ground). Now, how's the agent
325      * supposed to travel along a map full of traps, when he is all blind, his
326      * guide-dogs are stupid and blind as well and his white walking stick is
327      * twisted?</p>
328      *
329      * <p>There is only one thing certain here (besides death and taxes): No
330      * navpoint is ever placed above a pit or inside map geometry. But, navpoint
331      * positions are usually the only places where we know the ground is safe.
332      * So, due to all this, the agent tries to jump whenever possible and still
333      * suitable for landing each jump on a navpoint. This still helps overcome
334      * most of the map troubles. Though it is counter-productive at times.</p>
335      *
336      * @param firstLocation Location to which to run.
337      * @param secondLocation Location where to continue (may be null).
338      * @param focus Location to which to look.
339      * @param reachable Whether the location is reachable.
340      * @return True, if no problem occured.
341      */
342     @Override
343     public boolean runToLocation(Location runningFrom, Location firstLocation, Location secondLocation, ILocated focus, NavPointNeighbourLink navPointsLink, boolean reachable)
344     {
345         // take another step
346         runnerStep++;
347     	
348     	// save context
349     	this.runningFrom = runningFrom;
350     	this.firstLocation = firstLocation;
351     	this.secondLocation = secondLocation;
352     	this.focus = focus;
353     	this.link = navPointsLink;
354     	this.reachable = reachable;
355     	
356         
357         // compute additional context
358         distance = memory.getLocation().getDistance(firstLocation);
359         distance2D = memory.getLocation().getDistance2D(firstLocation);
360         distanceZ = firstLocation.getDistanceZ(memory.getLocation());
361         if (distanceZ >= 0) fallDistance = 0;
362         else fallDistance = getFallDistance(distanceZ);
363         velocity = memory.getVelocity().size();
364         velocityZ = memory.getVelocity().z;
365         jumpRequired = 	
366         				!reachable ||
367         				(link != null 
368                           && (((link.getFlags() & LinkFlag.JUMP.get()) != 0) 
369           		              || (link.isForceDoubleJump())
370           		              || (link.getNeededJump() != null)
371           		             )
372           		        )
373         ; 
374     	
375         // DEBUG LOG
376         
377         if (log != null && log.isLoggable(Level.FINER)) {
378         	debug("KefikRunner!");
379         	debug("running to    = " + firstLocation + " and than to " + secondLocation + " and focusing to " + focus);
380         	debug("bot position  = " + memory.getLocation());
381         	debug("distance      = " + distance);
382         	debug("distance2D    = " + distance2D);
383     		debug("distanceZ     = " + distanceZ);
384     		debug("fallDistance  = " + fallDistance);
385     		debug("velocity      = " + velocity);
386     		debug("velocityZ     = " + velocityZ);
387     		debug("jumpRequired  = " + jumpRequired 
388     									+ (!reachable ? " NOT_REACHABLE" : "") 
389     									+ (link == null ? 
390     											"" 
391     										  : ( 
392     											    (link.getFlags() & LinkFlag.JUMP.get()) != 0 ? " JUMP_FLAG" : "") + (link.isForceDoubleJump() ? " DOUBLE_JUMP_FORCED" : "") + (link.getNeededJump() != null ? " AT[" + link.getNeededJump() + "]" : ""
393     											)
394     									  )   
395     			 );
396     		debug("reachable     = " + reachable);
397     		if (link != null) {
398     			debug("link          = " + link);
399     		} else {
400     			debug("LINK NOT PRESENT");
401     		}
402     		debug("collisionNum  = " + collisionNum);
403     		debug("collisionSpot = " + collisionSpot);
404     		debug("jumpStep      = " + jumpStep);
405     		debug("runnerStep    = " + runnerStep);
406         }
407         
408         // DELIBERATION
409         
410         if (runnerStep <= 1) {
411         	debug("FIRST STEP - start running towards new location");
412             move(firstLocation, secondLocation, focus);
413         }
414         
415         // are we jumping already?
416         if (jumpStep > 0)
417         {
418         	debug("we're already jumping");
419             return iterateJumpSequence();
420         }
421         
422         // collision experienced?
423         if (senses.isCollidingOnce())
424         {
425         	debug("sensing collision");
426             // try to resolve it
427             return resolveCollision();
428         } else {
429         	if (collisionSpot != null || collisionNum != 0) {
430         		debug("no collision, clearing collision data");
431         		collisionNum = 0;
432         		collisionSpot = null;
433         	}
434         }
435         
436         if (velocity < 5 && runnerStep > 1) {
437         	debug("velocity is zero and we're in the middle of running");
438         	if (link != null && (link.getFromNavPoint().isLiftCenter() || link.getFromNavPoint().isLiftExit())) {
439         		if (link.getFromNavPoint().isLiftCenter()) {
440         			debug("we're standing on the lift center, ok");
441         		} else {
442         			debug("we're standing on the lift exit, ok");
443         		}
444         	} else {
445         		debug("and we're not standing on the lift center");
446         		return initJump(true);
447         	}
448         }
449         
450         // check jump
451         if (jumpRequired) {
452         	debug("jump is required");
453 	        return resolveJump();
454         }
455         
456         // just continue with ordinary run
457         debug("keeping running to the target");
458         move(firstLocation, secondLocation, focus);
459         
460         return true;
461     }
462     
463     /*========================================================================*/
464 
465     /**
466      * Decision has been made, we need to jump ({@link KefikRunner#jumpRequired} is true (!reachable || jump flag / needed jump / force double jump) and no collision has interrupted us) ... 
467      * but we do not know from where/when and even if we should jump yet.
468      * <p><p>
469      * This methods checks whether it is a right time to initiate a jump sequence based on various distances.
470      * <p><p>
471      * Due to inevitability of ensuring of landing on destination locations,
472      * jumps may only be started, when it is appropriate. This method decides,
473      * whether jump is appropriate.
474      *
475      * @return True, if no problem occured.
476      */
477     private boolean resolveJump()
478     {    		
479     	debug("resolveJump(): called");
480     	
481     	// cut the jumping distance2D of the next jump, this is to allow to
482         // jump more than once per one runner request, while ensuring that
483         // the last jump will always land exactly on the destination..
484         int jumpDistance2D = ((int)distance2D) % 1000;
485                 
486 	    debug("resolveJump(): jumpDistance2D = " + jumpDistance2D);
487 	    
488 	    // follow the deliberation about the situation we're currently in
489 	    boolean jumpIndicated = false;      // whether we should jump now
490 	    boolean mustJumpIfIndicated = false; // whether we MUST jump NOW
491 	            
492         boolean goingToJump = false;
493         
494         // deliberation, whether we may jump
495         
496         if (link != null &&
497         	(	((link.getFlags() & LinkFlag.JUMP.get()) != 0) 
498           		              || (link.isForceDoubleJump())
499           		              || (link.getNeededJump() != null)
500            )) {
501         	debug("resolveJump(): deliberation - jumping condition present");
502         	jumpIndicated = true;
503         }
504         
505         if (jumpDistance2D < 250) {
506         	debug("resolveJump(): we've missed all jumping opportunities (jumpDistance2D < 250)");
507         	if (runnerStep > 1) {
508         		debug("resolveJump(): and runnerStep > 1, if indicated we will be forced to jump right now");
509                 mustJumpIfIndicated = true;
510             } else {
511             	debug("resolveJump(): but runnerStep <= 1, can't force jump yet");
512             }
513         }
514         
515         debug("resolveJump(): jumpIndicated       = " + jumpIndicated);
516         debug("resolveJump(): mustJumpIfIndicated = " + mustJumpIfIndicated);
517         
518         if (jumpIndicated && mustJumpIfIndicated) {
519         	if (distanceZ > 0) {
520         		debug("resolveJump(): we MUST jump!");
521         		return prepareJump(true); // true == forced jump
522         	} else {
523         		debug("resolveJump(): we MUST fall down with a jump!");
524         		return prepareJump(true); // true == forced jump
525         	}
526         } else
527         if (jumpIndicated) {
528         	debug("resolveJump(): we should jump");
529         	return prepareJump(false); // false == we're not forcing to jump immediatelly        	
530         } else {
531         	debug("resolveJump(): we do not need to jump, waiting to reach the right spot to jump from");
532         	// otherwise, wait for the right double-jump distance2D
533         	// meanwhile: keep running to the location..
534         	move(firstLocation, secondLocation, focus);
535         	return true;
536         }
537     }
538     
539     /*========================================================================*/
540     
541     /**
542      * This method is called from {@link KefikRunner#resolveJump()} that has decided that the jump is necessary to reach the 
543      * the target (it is already known that distanceZ > 0).
544      * <p><p>
545      * jumpForced == true ... we will try to run no matter what
546      * <p><p>
547      * jumpForced == false ... we will check whether the time is right for jumping assessing the {@link KefikRunner#distanceZ}.
548      * 
549      * @return whether we should reach the target
550      */
551     private boolean prepareJump(boolean jumpForced) {
552     	debug("prepareJump(): called");    	
553     	
554     	Location direction = Location.sub(firstLocation, memory.getLocation());
555     	direction.z = 0;
556     	direction = direction.getNormalized();
557 	    Location velocityDir = new Location(memory.getVelocity().asVector3d());
558 	    velocityDir.z = 0;
559 	    velocityDir = velocityDir.getNormalized();
560 	    Double jumpAngleDeviation = Math.acos(direction.dot(velocityDir));
561 	    
562 	    boolean angleSuitable = !jumpAngleDeviation.isNaN() && jumpAngleDeviation < (Math.PI / 9);
563 	    
564 	    debug("prepareJump(): jumpAngleDeviation = " + jumpAngleDeviation);
565 	    debug("prepareJump(): angleSuitable      = " + angleSuitable);
566     	
567     	if (jumpForced) {
568     		debug("prepareJump(): jump is forced, bypassing jump checks!");
569     	} else {
570 	    	debug("prepareJump(): jump is not forced, checking jump conditions");
571 	    	
572 	    	
573 	    	if (velocity < 200 && distance2D > getMaxJumpDistance(true, UnrealUtils.FULL_DOUBLEJUMP_DELAY, UnrealUtils.FULL_DOUBLEJUMP_FORCE, distanceZ, velocity)) {
574 	    		debug("prepareJump(): velocity is too low for jump (velocity < 200) and target is too far away to jump there with double jump");
575 	    		debug("prepareJump(): proceeding with the straight movement to gain speed");
576 	    		move(firstLocation, secondLocation, focus);
577 	    		return true;
578 	    	}
579 	    	
580 	    	if (!angleSuitable) {
581 	    		debug("prepareJump(): angle is not suitable for jumping (angle > 20 degrees)");
582 	    		debug("prepareJump(): proceeding with the straight movement to gain speed");
583 	    		move(firstLocation, secondLocation, focus);
584 	    		return true;
585 	    	}
586 	    	
587 	    	debug("prepareJump(): velocity & angle is OK!");
588 	    }
589     	
590 		if (distanceZ >= 0) {
591     		debug("prepareJump(): JUMP (distanceZ >= 0)");
592         	return initJump(jumpForced);
593     	} else {
594     		debug("prepareFall(): FALL (distanceZ < 0)");
595         	return initFall(jumpForced);
596     	}
597     }
598     
599     /*========================================================================*/
600 
601     private double adjustJumpForce(double distanceSafeZone, boolean doubleJump, double jumpForce, double jumpDelay) {
602     	double distanceJumped = getJumpUpDistance(doubleJump, jumpDelay, jumpForce, distanceZ, velocity);
603     	debug("initJump(): adjusting jumpForce...");
604 		while (distanceJumped-distanceSafeZone < distance2D && // jump distance is still not enough 
605 			   (    (doubleJump && jumpForce < UnrealUtils.FULL_DOUBLEJUMP_FORCE) 
606 			    || (!doubleJump && jumpForce < UnrealUtils.FULL_JUMP_FORCE)) // and we still have a room to make the jump longer
607 			   ) {
608 			jumpForce += 10;
609 			distanceJumped = getJumpUpDistance(doubleJump, jumpDelay, jumpForce, distanceZ, velocity);
610 		}
611 		// clamp the jumpForce
612 		if (doubleJump) {
613 			jumpForce = Math.min(jumpForce, UnrealUtils.FULL_DOUBLEJUMP_FORCE);
614 		} else {
615 			jumpForce = Math.min(jumpForce, UnrealUtils.FULL_JUMP_FORCE);
616 		}
617 		debug("initJump(): jumpForce = " + jumpForce);
618 		return jumpForce;
619     }
620     
621     /**
622      * We have to jump up (distanceZ > 0) if there is a possibility that we get there by jumping
623      * (i.e., params for jump exists that should get us there) or 'jumpForced is true'.
624      * <p><p>
625      * Decides whether we need single / double jump and computes
626      * the best args for jump command according to current velocity.
627      * 
628      * @param jumpForced
629      */
630     private boolean initJump(boolean jumpForced) {
631     	debug("initJump(): called");
632     	
633     	boolean shouldJump = true;
634     	
635     	boolean doubleJump = true;
636     	double jumpForce = UnrealUtils.FULL_DOUBLEJUMP_FORCE;
637     	double jumpDelay = UnrealUtils.FULL_DOUBLEJUMP_DELAY;
638     	
639     	if (distanceZ > 130) {
640     		debug("initJump(): jump could not be made (distanceZ = " + distanceZ + " > 130)");
641     		if (jumpForced) {
642     			debug("initJump(): but jump is being forced!");
643     		} else {
644     			debug("initJump(): jump is not forced ... we will wait till the bot reach the right jumping spot");
645     			move(firstLocation, secondLocation, focus);
646             	jumpStep = 0; // we have not performed the JUMP
647             	return true;
648     		}
649     	}
650     	
651     	if (distanceZ < 55 && distance2D < velocity * 0.85) {
652     		debug("initJump(): single jump suffices (distanceZ < 55 && distance2D = " + distance2D + " < " + (velocity*0.85) +" = velocity * 0.85))");
653     		doubleJump = false;
654     		jumpForce = UnrealUtils.FULL_JUMP_FORCE;
655     	}
656     	
657     	double jumpUp_force = 0;
658     	if (doubleJump) {
659     		jumpUp_force = UnrealUtils.FULL_DOUBLEJUMP_FORCE * ((distanceZ+5) / 125);
660     		jumpUp_force = Math.min(jumpUp_force, UnrealUtils.FULL_DOUBLEJUMP_FORCE);
661     	} else {
662     		jumpUp_force = UnrealUtils.FULL_JUMP_FORCE * ((distanceZ+5) / 60);
663     		// JUMP FORCE SHOULD BE ALWAYS OK HERE AS WE'VE CHECKED distanceZ BEFORE!
664     		jumpUp_force = Math.min(jumpUp_force, UnrealUtils.FULL_JUMP_FORCE);
665     	}
666     	debug("initJump(): minimum force to jump to height " + distanceZ + " with " + (doubleJump ? "double" : "single") + " is " + jumpUp_force);
667     	double distanceSafeZone = 0;    	
668     	debug("initJump(): adjusting force to match jumping distance = " + distance2D + " = distance2D (safe zone = " + distanceSafeZone + ")");
669     	jumpForce = adjustJumpForce(distanceSafeZone, doubleJump, jumpUp_force, jumpDelay);
670     	double distanceJumped = getJumpUpDistance(doubleJump, jumpDelay, jumpForce, distanceZ, velocity);    	
671 		if (distanceJumped-distanceSafeZone < distance2D) {
672 			debug("initJump(): too short! (distanceJumped-" + distanceSafeZone + " = " + (distanceJumped-distanceSafeZone) + " < " + distance2D + " = distance2D)");
673    			if (!doubleJump) {
674    				debug("initJump(): trying double jump");
675    				doubleJump = true;
676 				jumpUp_force = UnrealUtils.FULL_DOUBLEJUMP_FORCE * ((distanceZ+5) / 125);
677 	    		jumpUp_force = Math.min(jumpUp_force, UnrealUtils.FULL_DOUBLEJUMP_FORCE);
678     	    	debug("initJump(): minimum force to jump to height " + distanceZ + " with double jump is " + jumpUp_force);
679     	    	debug("initJump(): adjusting force to match jumping distance = " + distance2D + " = distance2D (safe zone = " + distanceSafeZone + ")");
680     	    	jumpForce = adjustJumpForce(distanceSafeZone, doubleJump, jumpUp_force, jumpDelay);
681     	    	distanceJumped = getMaxJumpDistance(doubleJump, jumpDelay, jumpForce, distanceZ, velocity);    
682     	    	if (distanceJumped-distanceSafeZone < distance2D) {
683     	    		debug("initJump(): still too short! (distanceJumped-" + distanceSafeZone + " = " + (distanceJumped-distanceSafeZone) + " < " + distance2D + " = distance2D)");
684     	    		shouldJump = false;
685     	    	} else {
686     	    		debug("initJump(): distance ok (distanceJumped-" + distanceSafeZone + " = " + (distanceJumped-distanceSafeZone) + " >= " + distance2D + " = distance2D)");
687     	    		shouldJump = true;
688     	    	}
689     		} else {
690     			shouldJump = false;
691     		}	    		
692 		} else {
693 			debug("initJump(): distance ok (distanceJumped-" + distanceSafeZone + " = " + (distanceJumped-distanceSafeZone) + " >= " + distance2D + " = distance2D)");
694     		shouldJump = true;
695 		}
696 		
697 		if (shouldJump || jumpForced) {
698 			if (jumpForced && !shouldJump) {
699 				debug("initJump(): we should not be jumping, but jump is FORCED!");
700 			}
701 			jumpStep = 1; // we have performed the JUMP
702 	   		return jump(true, jumpDelay, jumpForce);
703 		} else {
704 			debug("initJump(): jump is not forced ... we will wait till the bot reach the right jumping spot");
705 			move(firstLocation, secondLocation, focus);
706         	jumpStep = 0; // we have not performed the JUMP
707         	return true;
708 		}
709     }
710     
711     /**
712      * We have to jump to fall down (distanceZ < 0) right now. Decides whether we need single / double jump and computes
713      * the best args for jump command according to current velocity.
714      */
715     private boolean initFall(boolean jumpForced) {
716     	debug("initFall(): called");
717     	
718     	jumpStep = 1;
719     	
720     	log.finer("Runner.initDoubleJumpSequence(): FALLING DOWN! Adjusting parameters of the jump for falling...");
721     	// we're going to fall, thus we have to be careful not to overjump the target
722     	
723     	// remainind distance for which we need jumping
724     	double remainingDistance2D = distance2D - fallDistance;
725     	
726     	debug("initFall(): distance2D          = " + distance2D);
727     	debug("initFall(): falling will get us = " + fallDistance + " further");
728     	debug("initFall(): remainingDistance2D = " + remainingDistance2D);
729     	
730     	// FULL DOUBLE JUMP
731     	boolean doubleJump = true;
732     	double jumpZ = 705;    		
733     	
734     	// single jump will get us about 300 forward
735     	// double jump will get us about 450 forward
736     	// -- note that above two constants taking into account also a jump itself (it gets us higher so falling down will take us further),
737     	//    theoretically, we should compute much more complex equation but this seems to work OK
738     	if (remainingDistance2D < velocity) {
739     		debug("initFall(): single jump suffices (remainingDistance2D < velocity)");
740     		doubleJump = false;
741     		jumpZ = 340 * remainingDistance2D / 300;
742     	} else
743     	if (remainingDistance2D < 450) {
744     		log.finer("initFall(): smaller double jump is needed (remainingDistance2D < 450)");
745     		doubleJump = true;
746     		jumpZ = 340 + 365 * (remainingDistance2D - 220) * 150;
747     	} else {
748     		log.finer("Runner.initDoubleJumpSequence(): full double jump is needed (remainingDistance2D > 450)");
749     		doubleJump = true;
750     		jumpZ = 705; 
751     	}
752     	
753     	return jump(doubleJump, 0.39, jumpZ);
754     }
755     
756     /*========================================================================*/    
757     
758     /**
759      * Perform jump right here and now with provided args.
760      */
761     private boolean jump(boolean doubleJump, double delay, double force) {
762     	if (doubleJump) {
763     		debug("DOUBLE JUMPING (delay = " + delay + ", force = " + force + ")");
764     	} else {
765     		debug("JUMPING (delay = " + delay + ", force = " + force + ")");
766     	}
767     	body.jump(doubleJump, delay, force);
768     	
769     	return true;
770     }
771     
772     private void move(ILocated firstLocation, ILocated secondLocation, ILocated focus) {
773     	Move move = new Move();
774     	if (firstLocation != null) {
775     		move.setFirstLocation(firstLocation.getLocation());
776     	}
777     	if (secondLocation != null) {
778     		move.setSecondLocation(secondLocation.getLocation());
779     	}
780     	
781     	if (focus != null) {
782     		if (focus instanceof Player) {
783     			move.setFocusTarget((UnrealId)((IWorldObject)focus).getId());
784     		} else {	
785     			move.setFocusLocation(focus.getLocation());
786     		}
787     	}
788     	
789     	log.finer("MOVING: " + move);    	
790     	bot.getAct().act(move);
791     }
792     
793     /*========================================================================*/
794     
795     /**
796      * Tries to resolve collisions.
797      *
798      * <p>Only continuous collisions are resolved, first by a double jump, then
799      * by a single-jump.</p>
800      *
801      * @return True, if no problem occured.
802      */
803     private boolean resolveCollision()
804     {
805         // are we colliding at a new spot?
806         if (
807             // no collision yet
808             (collisionSpot == null)
809             // or the last collision is far away
810             || (memory.getLocation().getDistance2D(collisionSpot) > 120)
811         ) {
812             // setup new collision spot info
813         	if (log != null && log.isLoggable(Level.FINER)) log.finer("Runner.resolveCollision(): collision");
814             collisionSpot = memory.getLocation();
815             collisionNum = 1;
816             // meanwhile: keep running to the location..
817             move(firstLocation, secondLocation, focus);
818             return true;
819         }
820         // so, we were already colliding here before..
821         // try to solve the problem according to how long we're here..
822         else { 
823             return initJump(true);
824         }
825     }
826 
827     /*========================================================================*/
828 
829     /**
830      * Follows single-jump sequence steps.
831      * @return True, if no problem occured.
832      */
833     private boolean iterateJumpSequence()
834     {
835     	debug("iterateJumpSequence(): called");
836         // what phase of the jump sequence?
837         switch (jumpStep) {
838             // the first phase: wait for the jump
839             case 1:
840                 // did the agent started the jump already?
841                 if (velocityZ > 100)
842                 {
843                 	debug("iterateJumpSequence(): jumping in progress (velocityZ > 100), increasing jumpStep");
844                     jumpStep++;
845                 }
846                 // meanwhile: just wait for the jump to start
847                 debug("iterateJumpSequence(): issuing move command to the target (just to be sure)");
848                 move(firstLocation, secondLocation, focus);
849                 return true;
850 
851             //  the last phase: finish the jump
852             default:
853                 // did the agent started to fall already
854                 if (velocityZ <= 0.01)
855                 {
856                 	debug("iterateJumpSequence(): jump ascension has ended (velocityZ < 0.01)");
857                     jumpStep = 0;
858                 }
859                 debug("iterateJumpSequence(): continuing movement to the target");
860                 move(firstLocation, secondLocation, focus);
861                 return true;
862         }
863     }
864 
865     /*========================================================================*/
866 
867     /** Agent's bot. */
868     protected UT2004Bot bot;
869     /** Loque memory. */
870     protected AgentInfo memory;
871     /** Agent's body. */
872     protected AdvancedLocomotion body;
873     /** Agent's log. */
874     protected Logger log;
875     /** Base agent's senses. */
876 	protected Senses senses;
877 
878     /*========================================================================*/
879 
880     /**
881      * Constructor.
882      * @param bot Agent's bot.
883      * @param memory Loque memory.
884      */
885     public KefikRunner (UT2004Bot bot, AgentInfo agentInfo, AdvancedLocomotion locomotion, Logger log) {
886         // setup reference to agent
887     	NullCheck.check(bot, "bot");
888     	this.bot = bot;
889         NullCheck.check(agentInfo, "agentInfo");
890         this.memory = agentInfo;
891         NullCheck.check(locomotion, "locomotion");
892         this.body = new AdvancedLocomotion(bot, log);
893         this.senses = new Senses(bot, memory, new Players(bot), log);
894         this.log = log;
895     }
896     
897 }