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,
97  			Projectiles projectiles) {
98  		super(bot, info, shoot, weaponry);
99  
100 		this.weaponry = weaponry;
101 		this.projectiles = projectiles;
102 
103 	}
104 
105 	/**
106 	 * The actual shooting.
107 	 */
108 	@Override
109 	protected void shoot() {
110 
111 		// We should have the shock rifle ready.
112 		if (!isWeaponReady()) {
113 			return;
114 		}
115 
116 		// No target, no shoot.
117 		if (!hasTarget()) {
118 			return;
119 		}
120 
121 		// Reset focus
122 		this.focus.setFocus(target);
123 
124 		boolean safeToSHoot = isSafeToShoot(target, weaponPref);
125 		boolean facing = FacingUtil.isFacing2D(info, target, FACING_ANGLE);
126 
127 		// Don't blow orbs into walls.
128 		if (!(target instanceof Player)) {
129 			shootTarget(safeToSHoot, facing);
130 			return;
131 		}
132 
133 		Player player = (Player) target;
134 
135 		// See if we have an orb to shoot down.
136 		// Will also try to catch hidden players in blast.
137 		if (shootCombo(player)) {
138 			return;
139 		}
140 
141 		// Target not visible, hold fire.
142 		if (!player.isVisible()) {
143 			shoot.stopShooting();
144 			return;
145 		}
146 
147 		// Store last known location. We'll use this to flush out hidden
148 		// players.
149 		lastLocation = player.getLocation();
150 
151 		boolean targetFacing = FacingUtil.isFacing2D(player, agent, SHIELD_GUN_DEFLECT_ANGLE);
152 		boolean targetShieldGun = ItemType.SHIELD_GUN.equals(ItemType.getItemType(player.getWeapon()));
153 		boolean targetShielded = player.getFiring() == 2;
154 		boolean targetCanDeflect = targetShieldGun && targetFacing && targetShielded;
155 
156 		// Shoot if we are facing the right way.
157 		// Don't want to blow an orb on a wall.
158 		// Don't want to get our stuff defelcted back.
159 
160 		shootPlayer(safeToSHoot, targetCanDeflect, facing);
161 
162 	}
163 
164 	/**
165 	 * Shoot without getting splash damage, without our projectiles being
166 	 * deflected and while looking in the right direction.
167 	 * 
168 	 * @param safeToSHoot
169 	 * @param canDeflect
170 	 * @param facing
171 	 */
172 	protected void shootPlayer(boolean safeToSHoot, boolean canDeflect, boolean facing) {
173 		if (!canDeflect) {
174 			shootTarget(safeToSHoot, facing);
175 		} else {
176 			shootShieldedPlayer(safeToSHoot, facing);
177 		}
178 	}
179 
180 	protected void shootShieldedPlayer(boolean safeToSHoot, boolean facing) {
181 		boolean inComboRange = target.getLocation().getDistance(agent.getLocation()) >= SHOCK_RIFLE_MIN_DISTANCE_COMBO
182 				- SHOCK_RIFLE_COMBO_RADIUS;
183 
184 		log.fine(String.format("Shooting shielded player. safeToSHoot=%s facing=%s inComboRange=%s", safeToSHoot,
185 				facing, inComboRange));
186 		if (inComboRange && safeToSHoot && facing && weaponPref.isSecondary()) {
187 			shoot.shoot(weaponPref, target.getLocation().add(ABOVE_PLAYER_OFFSET));
188 		} else {
189 			shoot.stopShooting();
190 		}
191 
192 	}
193 
194 	/**
195 	 * Shoot without getting splash damage and while looking in the right
196 	 * direction.
197 	 * 
198 	 * @param safeToSHoot
199 	 * @param facing
200 	 */
201 	protected void shootTarget(boolean safeToSHoot, boolean facing) {
202 		log.fine(String.format("Shooting target. safeToSHoot=%s facing=%s ", safeToSHoot, facing));
203 		if (safeToSHoot && facing) {
204 			shoot.shoot(weaponPref, target);
205 		} else if (facing) {
206 			shoot.shoot(SHOCK_RIFLE_PRIMARY, target);
207 		} else {
208 			shoot.stopShooting();
209 		}
210 	}
211 
212 	/**
213 	 * Don't shoot unless we are far away enough to avoid damaging ourselves, or
214 	 * healthy enough to survive the damage, or not using the secondary mode.
215 	 * 
216 	 * @param target
217 	 *            to shoot
218 	 * @return true iff we won't kill ourselves.
219 	 */
220 	protected boolean isSafeToShoot(ILocated target, WeaponPref weaponPref) {
221 		boolean primary = weaponPref.isPrimary();
222 
223 		double distance = info.getLocation().getDistance(target.getLocation());
224 		boolean safeDistance = distance > SHOCK_RIFLE_PROJECTILE_SPLASH_RADIUS;
225 		boolean healty = info.getHealth() > SHOCK_RIFLE_PROJECTILE_DAMAGE;
226 
227 		return (primary || safeDistance || healty);
228 	}
229 
230 	/**
231 	 * Tries to shoot a projectile to create a shock combo.
232 	 * 
233 	 * @param target
234 	 *            to shoot.
235 	 * @return if there is a projectile we could create a shock combo with.
236 	 */
237 	protected boolean shootCombo(Player target) {
238 
239 		IncomingProjectile projectile;
240 
241 		// If target has hidden himself, try see if we can catch him in the
242 		// shock blast.
243 		if (target.isVisible()) {
244 			projectile = projectiles.getNearestProjectile(target, ItemType.SHOCK_RIFLE_PROJECTILE);
245 		} else {
246 			projectile = projectiles.getNearestProjectile(lastLocation, ItemType.SHOCK_RIFLE_PROJECTILE);
247 		}
248 
249 		if (projectile != null) {
250 
251 			if (projectile.getImpactTime() < 0) {
252 				System.out.println("BUG!");
253 			}
254 
255 			double distanceOrbTarget = projectile.getLocation().getDistance(target.getLocation());
256 			double distanceSelfTarget = info.getLocation().getDistance(target.getLocation());
257 			double distanceSelfOrb = info.getLocation().getDistance(projectile.getLocation());
258 
259 			boolean orbPassedTarget = distanceSelfOrb > distanceSelfTarget + SHOCK_RIFLE_COMBO_RADIUS;
260 			boolean targetInComboRadius = distanceOrbTarget < SHOCK_RIFLE_COMBO_RADIUS;
261 
262 			log.fine(String.format("Selected projectile: %s", projectile));
263 			log.fine(String.format("Projectile has passed target? %s", orbPassedTarget));
264 			log.fine(String.format("Target in combo radius? %s", targetInComboRadius));
265 
266 			// Only shoot when we have a chance of blowing up the orb on our
267 			// target. We'll wait for it to move into place.
268 			// TODO: We might be waiting for an orb that is moving towards us.
269 
270 			if (!orbPassedTarget && !targetInComboRadius) {
271 				log.fine("Waiting for projectile to close in on target");
272 				shoot.stopShooting();
273 				focus.setFocus((ILocated) projectile);
274 				return true;
275 			}
276 			// Shoot orb
277 			else if (!orbPassedTarget) {
278 				log.fine("Shoot rojectile");
279 				// FIXME: Use unreal ID
280 				shoot.shoot(SHOCK_RIFLE_PRIMARY, projectile.getId());
281 				// shoot.shoot(SHOCK_RIFLE_PRIMARY, projectile.getLocation());
282 				return true;
283 			}
284 		}
285 		log.fine("No valid projectile to shoot");
286 		return false;
287 	}
288 
289 	@Override
290 	protected WeaponPref getDefaultWeaponPref() {
291 		return SHOCK_RIFLE_SECONDARY;
292 	}
293 
294 }