View Javadoc

1   package nl.tudelft.pogamut.ut2004.agent.module.shooting.weapon;
2   
3   import nl.tudelft.pogamut.ut2004.agent.module.sensor.Projectiles;
4   import nl.tudelft.pogamut.ut2004.agent.module.shooting.AbstractWeaponShooting;
5   import nl.tudelft.pogamut.ut2004.agent.module.shooting.util.FacingUtil;
6   import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
7   import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
8   import cz.cuni.amis.pogamut.ut2004.agent.module.sensomotoric.Weaponry;
9   import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.AgentInfo;
10  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.WeaponPref;
11  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.WeaponPrefs;
12  import cz.cuni.amis.pogamut.ut2004.bot.command.AdvancedShooting;
13  import cz.cuni.amis.pogamut.ut2004.bot.command.ImprovedShooting;
14  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
15  import cz.cuni.amis.pogamut.ut2004.communication.messages.ItemType;
16  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.IncomingProjectile;
17  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
18  
19  /**
20   * <p>
21   * Module to work efficiently with the shock rifle. Will use shock combos when
22   * possible.
23   * </p>
24   * 
25   * <p>
26   * To create a shock combo
27   * {@link ShockRifleShooting#shoot(WeaponPref, ILocated)} must be called with a
28   * weaponPref that allows the secondary fire mode to be used. When used through
29   * {@link AdvancedShooting} the appropriate weapon preference must be set on
30   * {@link WeaponPrefs}.
31   * </p>
32   * 
33   * <p>
34   * To avoid self damage the module won't use the secondary firemode when the
35   * projectile may blow up in it's face and won't be able to survive the damage.
36   * Generally {@link WeaponPrefs} should handle this but it may not be within
37   * {@link ImprovedShooting#getChangeWeaponCooldown()}. When unable to use the
38   * secondary, the primary will be used.
39   * <p>
40   * <p>
41   * To avoid projectiles being deflected back by a shield gun, the module won't
42   * directly shoot at players who have an active shield gun facing them. It will
43   * however attempt to detonate a combo above the target if they are in range.
44   * </p>
45   * 
46   * @author mpkorstanje
47   * 
48   */
49  public class ShockRifleShooting extends AbstractWeaponShooting {
50  
51  	/**
52  	 * Angle at which shield gun can deflect projectiles.
53  	 */
54  	protected static final int SHIELD_GUN_DEFLECT_ANGLE = 90;
55  
56  	/**
57  	 * Primary mode preference.
58  	 */
59  	protected static final WeaponPref SHOCK_RIFLE_PRIMARY = new WeaponPref(ItemType.SHOCK_RIFLE, true);
60  
61  	/**
62  	 * Secondary mode preference.
63  	 */
64  	protected static final WeaponPref SHOCK_RIFLE_SECONDARY = new WeaponPref(ItemType.SHOCK_RIFLE, false);
65  
66  	/**
67  	 * Radius of the shock combo blast. Experimentally determined.
68  	 * 
69  	 */
70  	protected static final double SHOCK_RIFLE_COMBO_RADIUS = 300;
71  
72  	/**
73  	 * Radius of the shock orb splash damage. Experimentally determined.
74  	 */
75  	protected static final double SHOCK_RIFLE_PROJECTILE_SPLASH_RADIUS = 150;
76  
77  	/**
78  	 * Damage to self when hit by projectile. Experimentally determined.
79  	 */
80  	protected static final int SHOCK_RIFLE_PROJECTILE_DAMAGE = 35;
81  
82  	/**
83  	 * Minimum distance in UT units the orb will travel before reload.
84  	 * Experimentally determined.
85  	 */
86  	protected static final int SHOCK_RIFLE_MIN_DISTANCE_COMBO = 900;
87  
88  	protected Projectiles projectiles;
89  
90  	/**
91  	 * Last known location of our target. Used to detonate shock combos on
92  	 * targets that have hidden. They might still be close.
93  	 */
94  	protected Location lastLocation;
95  
96  	public ShockRifleShooting(UT2004Bot<?, ?, ?> bot, AgentInfo info, ImprovedShooting shoot, Weaponry weaponry, Projectiles projectiles) {
97  		super(bot, info, shoot, weaponry);
98  
99  		this.weaponry = weaponry;
100 		this.projectiles = projectiles;
101 
102 	}
103 
104 	/**
105 	 * The actual shooting.
106 	 */
107 	@Override
108 	protected void shoot() {
109 
110 		// We should have the shock rifle ready.
111 		if (!isWeaponReady()) {
112 			return;
113 		}
114 
115 		// No target, no shoot.
116 		if (!hasTarget()) {
117 			return;
118 		}
119 
120 		// Reset focus
121 		this.focus.setFocus(target);
122 
123 		boolean safeToSHoot = isSafeToShoot(target, weaponPref);
124 		boolean facing = FacingUtil.isFacing2D(info, target, FACING_ANGLE);
125 
126 		// Don't blow orbs into walls.
127 		if (!(target instanceof Player)) {
128 			shootTarget(safeToSHoot, facing);
129 			return;
130 		}
131 
132 		Player player = (Player) target;
133 
134 		// See if we have an orb to shoot down.
135 		// Will also try to catch hidden players in blast.
136 		if (shootCombo(player)) {
137 			return;
138 		}
139 
140 		// Target not visible, hold fire.
141 		if (!player.isVisible()) {
142 			shoot.stopShooting();
143 			return;
144 		}
145 
146 		// Store last known location. We'll use this to flush out hidden
147 		// players.
148 		lastLocation = player.getLocation();
149 
150 		boolean targetFacing = FacingUtil.isFacing2D(player, agent, SHIELD_GUN_DEFLECT_ANGLE);
151 		boolean targetShieldGun = ItemType.SHIELD_GUN.equals(ItemType.getItemType(player.getWeapon()));
152 		boolean targetShielded = player.getFiring() == 2;
153 		boolean targetCanDeflect = targetShieldGun && targetFacing && targetShielded;
154 
155 		// Shoot if we are facing the right way.
156 		// Don't want to blow an orb on a wall.
157 		// Don't want to get our stuff defelcted back.
158 
159 		shootPlayer(safeToSHoot, targetCanDeflect, facing);
160 
161 	}
162 
163 	/**
164 	 * Shoot without getting splash damage, without our projectiles being
165 	 * deflected and while looking in the right direction.
166 	 * 
167 	 * @param safeToSHoot
168 	 * @param canDeflect
169 	 * @param facing
170 	 */
171 	protected void shootPlayer(boolean safeToSHoot, boolean canDeflect, boolean facing) {
172 		if (!canDeflect) {
173 			shootTarget(safeToSHoot, facing);
174 		} else {
175 			shootShieldedPlayer(safeToSHoot, facing);
176 		}
177 	}
178 
179 	protected void shootShieldedPlayer(boolean safeToSHoot, boolean facing) {
180 		boolean inComboRange = target.getLocation().getDistance(agent.getLocation()) >= SHOCK_RIFLE_MIN_DISTANCE_COMBO
181 				- SHOCK_RIFLE_COMBO_RADIUS;
182 
183 		log.fine(String.format("Shooting shielded player. safeToSHoot=%s facing=%s inComboRange=%s", safeToSHoot, facing, inComboRange));
184 		if (inComboRange && safeToSHoot && facing && weaponPref.isSecondary()) {
185 			shoot.shoot(weaponPref, target.getLocation().add(ABOVE_PLAYER_OFFSET));
186 		} else {
187 			shoot.stopShooting();
188 		}
189 
190 	}
191 
192 	/**
193 	 * Shoot without getting splash damage and while looking in the right
194 	 * direction.
195 	 * 
196 	 * @param safeToSHoot
197 	 * @param facing
198 	 */
199 	protected void shootTarget(boolean safeToSHoot, boolean facing) {
200 		log.fine(String.format("Shooting target. safeToSHoot=%s facing=%s ", safeToSHoot, facing));
201 		if (safeToSHoot && facing) {
202 			shoot.shoot(weaponPref, target);
203 		} else if (facing) {
204 			shoot.shoot(SHOCK_RIFLE_PRIMARY, target);
205 		} else {
206 			shoot.stopShooting();
207 		}
208 	}
209 
210 	/**
211 	 * Don't shoot unless we are far away enough to avoid damaging ourselves, or
212 	 * healthy enough to survive the damage, or not using the secondary mode.
213 	 * 
214 	 * @param target
215 	 *            to shoot
216 	 * @return true iff we won't kill ourselves.
217 	 */
218 	protected boolean isSafeToShoot(ILocated target, WeaponPref weaponPref) {
219 		boolean primary = weaponPref.isPrimary();
220 
221 		double distance = info.getLocation().getDistance(target.getLocation());
222 		boolean safeDistance = distance > SHOCK_RIFLE_PROJECTILE_SPLASH_RADIUS;
223 		boolean healty = info.getHealth() > SHOCK_RIFLE_PROJECTILE_DAMAGE;
224 
225 		return (primary || safeDistance || healty);
226 	}
227 
228 	/**
229 	 * Tries to shoot a projectile to create a shock combo.
230 	 * 
231 	 * @param target
232 	 *            to shoot.
233 	 * @return if there is a projectile we could create a shock combo with.
234 	 */
235 	protected boolean shootCombo(Player target) {
236 
237 		IncomingProjectile projectile;
238 
239 		// If target has hidden himself, try see if we can catch him in the
240 		// shock blast.
241 		if (target.isVisible()) {
242 			projectile = projectiles.getNearestProjectile(target, ItemType.SHOCK_RIFLE_PROJECTILE);
243 		} else {
244 			projectile = projectiles.getNearestProjectile(lastLocation, ItemType.SHOCK_RIFLE_PROJECTILE);
245 		}
246 
247 		if (projectile == null) {
248 			log.fine("No valid projectile to shoot");
249 			return false;
250 		}
251 
252 		double distanceOrbTarget = projectile.getLocation().getDistance(target.getLocation());
253 		double distanceSelfTarget = info.getLocation().getDistance(target.getLocation());
254 		double distanceSelfOrb = info.getLocation().getDistance(projectile.getLocation());
255 
256 		boolean orbPassedTarget = distanceSelfOrb > distanceSelfTarget + SHOCK_RIFLE_COMBO_RADIUS;
257 		boolean targetInComboRadius = distanceOrbTarget < SHOCK_RIFLE_COMBO_RADIUS;
258 
259 		log.fine(String.format("Selected projectile: %s", projectile));
260 		log.fine(String.format("Projectile has passed target? %s", orbPassedTarget));
261 		log.fine(String.format("Target in combo radius? %s", targetInComboRadius));
262 
263 		// Only shoot when we have a chance of blowing up the orb on our
264 		// target. We'll wait for it to move into place.
265 		// TODO: We might be waiting for an orb that is moving towards us.
266 
267 		if (!orbPassedTarget && !targetInComboRadius) {
268 			log.fine("Waiting for projectile to close in on target");
269 			shoot.stopShooting();
270 			focus.setFocus((ILocated) projectile);
271 			return true;
272 		}
273 		// Shoot orb
274 		else if (!orbPassedTarget) {
275 			log.fine("Shoot rojectile");
276 			shoot.shoot(SHOCK_RIFLE_PRIMARY, projectile.getId());
277 			return true;
278 		}
279 
280 		return false;
281 	}
282 
283 	@Override
284 	protected WeaponPref getDefaultWeaponPref() {
285 		return SHOCK_RIFLE_SECONDARY;
286 	}
287 
288 }