Registering listeners

There are two ways how to register listeners. Either you may annotate any method with one of the annotation EventListener, ObjectEventListener, ObjectClassEventListener, ObjectClassEventListener or ObjectListener, or you may manually register a listener via IWorldView interface using one of the getWorldView().addEventListener() or getWorldView().addObjectListener() methods.

To see how the listeners are registered on bot's world view, find prepareBot() method. In the body of this method there is an example of manual listener registration.

	public void prepareBot(UT2004Bot bot) {
		// register the botDamagedListener that we have previously created
		getWorldView().addEventListener(BotDamaged.class, botDamagedListener);
	}

Simillarly to getAct() the getWorldView() returns the IWorldView implementation associated with this bot. The first line adds a listener for BotDamaged event, this event is raised when the bot is hurt by somebody or something. The listener itself is referenced by botDamagedListener variable.

Now let's explore implementation of the listener that was just registered. Click the botDamagedListener variable while holding Ctrl key to see the listener definition:

	/**
	 * Listener that is manually created and manually hooked to the {@link ResponsiveBot#getWorldView()} 
	 * via {@link IWorldView#addEventListener(Class, IWorldEventListener)} method
	 * inside {@link ResponsiveBot#prepareBot(UT2004Bot)}.
	 */
	IWorldEventListener<BotDamaged> botDamagedListener = new IWorldEventListener<BotDamaged>() {
		@Override
		public void notify(BotDamaged event) {
			// the bot was injured - let's move around the level and hope that we will find a health pack
			// note that we have to acquire "SECOND" nearest navpoint, as the first one is the navpoint we're standing at
			NavPoint secondNav = DistanceUtils.getSecondNearest(getWorldView().getAll(NavPoint.class).values(), info.getLocation());
			// always check even for improbable conditions
			if (secondNav == null) {
				// try to locate some navpoint
				move.turnVertical(30);
			} else {
				// move to it
				move.moveTo(secondNav);
			}            
		}
	};

The botDamagedListener variable is of type IWorldEventListener parameterized by the BotDamaged class. IWorldEventListener interface declares one abstract method notify(...) that is called each time the event occurs with the event as the method's parameter. Body of this method implements a way how to find the second nearest navpoint and run to it in hope it will be reachable and far from the danger.

Now return back to the code block showing the listeners' registration. You can press Alt + ← to get to the previous position in the source code. Then again use Ctrl + LMB to go to the botDamagedListener definition.

Now for the second way of listener registration - more easier - annotations. There are three methods that are called when some event occurs: bumped(Bumped event), playerAppeared(WorldObjectAppearedEvent<Player> event) and playerUpdated(WorldObjectUpdatedEvent<Player> event). Let's check how this magic - auto method calling - happens.

The class UT2004BotModuleController that is an ancestor of the ResponsiveBot (and all other examples as well) is auto-initializing the AnnotationListenerRegistrator. Now, because you know how we may manually hook a custom listener to the world view, you might already have guessed what the registrator is doing. It simply iterates through all methods the ResponsiveBot is declaring and searches for methods annotated either with EventListener or ObjectEventListener or ObjectClassEventListener or ObjectClassEventListener or ObjectListener annotation (that is the magical @EventLister that is present above bumped(Bumped event) method for instance). Every such annotation refers to a certain category of events (it corresponds with methods of the world view that you may manually use to register a listener):

For the concrete implementations:

	/**
	 * Listener called when someone/something bumps into the bot. The bot
	 * responds by moving in the opposite direction than the bump come from.
	 *
	 * We're using {@link EventListener} here that is registered by the {@link AnnotationListenerRegistrator} to listen
	 * for {@link Bumped} events.
	 */
	@EventListener(eventClass = Bumped.class)
	protected void bumped(Bumped event) {
		// schema of the vector computations
		//
		//  e<->a<------>t
		//  |   |   v    |
		//  |   |        target - bot will be heading there
		//  |   getLocation()
		//  event.getLocation()
		
		Location v = event.getLocation().sub(bot.getLocation()).scale(5);
		Location target = bot.getLocation().sub(v);
		
		// make the bot to go to the computed location while facing the bump source
		move.strafeTo(target, event.getLocation());
	}

The bumped(Bumped event) method is annotated by the @EventListener(eventClass = Bumped.class) that means that the method gets called whenever the Bumped message is sent by the GameBots2004 to the bot (note how the field eventClass inside the annotation is used to specify which event you want to listen to and then the same class bumped appears in the method declaration.

The method just performs simple vector arithmetic to obtain a vector where to run to to stay away from the source of the collision.

	/**
	 * Listener called when a player appears.
	 *
	 * We're using {@link ObjectClassEventListener} here that is registered by the {@link AnnotationListenerRegistrator}
	 * to listen on all {@link WorldObjectAppearedEvent} that happens on any object of the class {@link Player}. I.e., 
	 * whenever the GameBots2004 sends an update about arbitrary {@link Player} in the game notifying us that the player
	 * has become visible (it's {@link Player#isVisible()} is switched to true and the {@link WorldObjectAppearedEvent}
	 * is generated), this method is called.
	 */
	@ObjectClassEventListener(eventClass = WorldObjectAppearedEvent.class, objectClass = Player.class)
	protected void playerAppeared(WorldObjectAppearedEvent<Player> event) {
		// greet player when he appears
		body.getCommunication().sendGlobalTextMessage("Hello " + event.getObject().getName() + "!");
	}

The playerAppeared(WorldObjectAppearedEvent<Player> event) method is annotated by the @ObjectClassEventListener(eventClass = WorldObjectAppearedEvent.class, objectClass = Player.class) again see how the fields eventClass and objectClass are used and how they correspond with the method parameters declaration.

The method is truly simple. You can see that each time a player appears, he is greeted by our bot. You should already be familiar with the body module that contains the most commands for the bot.

Now inspect the last listener:

	/**
	 * Listener called each time a player is updated.
	 *
	 * Again, we're using {@link ObjectClassEventListener} that is registered by the {@link AnnotationListenerRegistrator}
	 * to listen on all {@link WorldObjectUpdatedEvent} that happens on any object of the class {@link Player}. I.e., 
	 * whenever the GameBots2004 sends an update about arbitrary {@link Player} in the game notifying us that some information
	 * about the player has changed (the {@link WorldObjectUpdatedEvent} is generated), this method is called.
	 */
	@ObjectClassEventListener(eventClass = WorldObjectUpdatedEvent.class, objectClass = Player.class)
	protected void playerUpdated(WorldObjectUpdatedEvent<Player> event) {
		// Check whether the player is closer than 5 bot diameters.
		// Notice the use of the UnrealUtils class.
		// It contains many auxiliary constants and methods.
		Player player = event.getObject();
		// First player objects are received in HandShake - at that time we don't have Self message yet or players location!!
		if (player.getLocation() == null || info.getLocation() == null) return;
		if (player.getLocation().getDistance(info.getLocation()) < (UnrealUtils.CHARACTER_COLLISION_RADIUS * 10)) {
		    // If the player wasn't close enough the last time this listener was called,
		    // then ask him what does he want.
		    if (!wasCloseBefore) {
		    	body.getCommunication().sendGlobalTextMessage("What do you want " + player.getName() + "?");
		        // Set proximity flag to true.
		        wasCloseBefore = true;
		    }
		} else {
		    // Otherwise set the proximity flag to false.
		    wasCloseBefore = false;
		}
	}

This listener is called each time a player is updated, it uses the same annotation as the previous one with different arguments. When the player that is visible changes it locatuion (or any other associated property) this method gets called. When the update event is raised we check whether the player is closer than certain threshold, in this case UnrealUtils.CHARACTER_COLLISION_RADIUS * 10. If this condition holds and it didn't hold last time the listener was called then it means that the player came closer to the bot than he was a while before. In that case the bot will ask him what does he want.