View Javadoc

1   package nl.tudelft.pogamut.ut2004.agent.module.shooting.weapon;
2   
3   import nl.tudelft.pogamut.unreal.agent.module.sensor.Projectiles;
4   import nl.tudelft.pogamut.unreal.agent.module.shooting.AbstractWeaponShooting;
5   import nl.tudelft.pogamut.unreal.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.UT2004ItemType;
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(UT2004ItemType.SHOCK_RIFLE, true);
60  
61  	/**
62  	 * Secondary mode preference.
63  	 */
64  	protected static final WeaponPref SHOCK_RIFLE_SECONDARY = new WeaponPref(UT2004ItemType.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 		// See if we have an orb to shoot down.
116 		// Will also try to catch hidden players in blast.
117 		if (!hasTarget() && shootCombo(lastLocation)) {
118 			return;
119 		}
120 
121 		// Reset last location, we've either shot
122 		// a combo or there is no possibility of a combo.
123 		lastLocation = null;
124 
125 		// No target, no shoot.
126 		if (!hasTarget()) {
127 			shoot.stopShooting();
128 			return;
129 		}
130 
131 		// Reset focus
132 		this.focus.setFocus(target);
133 
134 		boolean safeToSHoot = isSafeToShoot(target, weaponPref);
135 		boolean facing = FacingUtil.isFacing2D(info, target, FACING_ANGLE);
136 
137 		// Don't blow orbs into walls.
138 		if (!(target instanceof Player)) {
139 			shootTarget(safeToSHoot, facing);
140 			return;
141 		}
142 
143 		Player player = (Player) target;
144 		
145 		// See if we have an orb to shoot down.
146 		// Will also try to catch hidden players in blast.
147 		if (shootCombo(player)) {
148 			return;
149 		}
150 		
151 		// Target not visible, hold fire.
152 		if (!player.isVisible()) {
153 			shoot.stopShooting();
154 			return;
155 		}
156 
157 		// Store last known location. We'll use this to flush out hidden
158 		// players.
159 		lastLocation = player.getLocation();
160 
161 		boolean targetFacing = FacingUtil.isFacing2D(player, agent, SHIELD_GUN_DEFLECT_ANGLE);
162 		boolean targetShieldGun = UT2004ItemType.SHIELD_GUN.equals(UT2004ItemType.getItemType(player.getWeapon()));
163 		boolean targetShielded = player.getFiring() == 2;
164 		boolean targetCanDeflect = targetShieldGun && targetFacing && targetShielded;
165 
166 		// Shoot if we are facing the right way.
167 		// Don't want to blow an orb on a wall.
168 		// Don't want to get our stuff defelcted back.
169 
170 		shootPlayer(safeToSHoot, targetCanDeflect, facing);
171 
172 	}
173 
174 	/**
175 	 * Shoot without getting splash damage, without our projectiles being
176 	 * deflected and while looking in the right direction.
177 	 * 
178 	 * @param safeToSHoot
179 	 * @param canDeflect
180 	 * @param facing
181 	 */
182 	protected void shootPlayer(boolean safeToSHoot, boolean canDeflect, boolean facing) {
183 		if (!canDeflect) {
184 			shootTarget(safeToSHoot, facing);
185 		} else {
186 			shootShieldedPlayer(safeToSHoot, facing);
187 		}
188 	}
189 
190 	protected void shootShieldedPlayer(boolean safeToSHoot, boolean facing) {
191 		boolean inComboRange = target.getLocation().getDistance(agent.getLocation()) >= SHOCK_RIFLE_MIN_DISTANCE_COMBO
192 				- SHOCK_RIFLE_COMBO_RADIUS;
193 
194 		log.fine(String.format("Shooting shielded player. safeToSHoot=%s facing=%s inComboRange=%s", safeToSHoot, facing, inComboRange));
195 		if (inComboRange && safeToSHoot && facing && weaponPref.isSecondary()) {
196 			shoot.shoot(weaponPref, target.getLocation().add(ABOVE_PLAYER_OFFSET));
197 		} else {
198 			shoot.stopShooting();
199 		}
200 
201 	}
202 
203 	/**
204 	 * Shoot without getting splash damage and while looking in the right
205 	 * direction.
206 	 * 
207 	 * @param safeToSHoot
208 	 * @param facing
209 	 */
210 	protected void shootTarget(boolean safeToSHoot, boolean facing) {
211 		log.fine(String.format("Shooting target. safeToSHoot=%s facing=%s ", safeToSHoot, facing));
212 		if (safeToSHoot && facing) {
213 			shoot.shoot(weaponPref, target);
214 		} else if (facing) {
215 			shoot.shoot(SHOCK_RIFLE_PRIMARY, target);
216 		} else {
217 			shoot.stopShooting();
218 		}
219 	}
220 
221 	/**
222 	 * Don't shoot unless we are far away enough to avoid damaging ourselves, or
223 	 * healthy enough to survive the damage, or not using the secondary mode.
224 	 * 
225 	 * @param target
226 	 *            to shoot
227 	 * @return true iff we won't kill ourselves.
228 	 */
229 	protected boolean isSafeToShoot(ILocated target, WeaponPref weaponPref) {
230 		boolean primary = weaponPref.isPrimary();
231 
232 		double distance = info.getLocation().getDistance(target.getLocation());
233 		boolean safeDistance = distance > SHOCK_RIFLE_PROJECTILE_SPLASH_RADIUS;
234 		boolean healty = info.getHealth() > SHOCK_RIFLE_PROJECTILE_DAMAGE;
235 
236 		return (primary || safeDistance || healty);
237 	}
238 
239 	protected boolean shootCombo(ILocated target) {
240 		if (target == null || target.getLocation() == null) {
241 			log.fine("No valid target");
242 			return false;
243 		}
244 
245 		IncomingProjectile projectile = projectiles.getNearestProjectile(target, UT2004ItemType.SHOCK_RIFLE_PROJECTILE);
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 }