/*
* MegaMek - Copyright (C) 2000,2001,2002,2003,2004 Ben Mazur (bmazur@sev.org)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
/*
* RamAttackAction.java
*
* Created on May 28, 2008
*/
package megamek.common.actions;
import java.util.Enumeration;
import megamek.common.Aero;
import megamek.common.Compute;
import megamek.common.Coords;
import megamek.common.Dropship;
import megamek.common.Entity;
import megamek.common.FighterSquadron;
import megamek.common.IEntityMovementType;
import megamek.common.IGame;
import megamek.common.IHex;
import megamek.common.Jumpship;
import megamek.common.MovePath;
import megamek.common.MoveStep;
import megamek.common.Player;
import megamek.common.SpaceStation;
import megamek.common.TargetRoll;
import megamek.common.Targetable;
import megamek.common.ToHitData;
import megamek.common.Warship;
/**
* Represents one unit charging another. Stores information about where the
* target is supposed to be for the charge to be successful, as well as normal
* attack info.
*
* @author Ben Mazur
*/
public class RamAttackAction extends AbstractAttackAction {
/**
*
*/
private static final long serialVersionUID = -3549351664290057785L;
public RamAttackAction(Entity attacker, Targetable target) {
this(attacker.getId(), target.getTargetType(), target.getTargetId(),
target.getPosition());
}
public RamAttackAction(int entityId, int targetType, int targetId,
Coords targetPos) {
super(entityId, targetType, targetId);
}
/**
* To-hit number for a ram, assuming that movement has been handled
*/
public ToHitData toHit(IGame game) {
final Entity entity = game.getEntity(getEntityId());
return toHit(game, game.getTarget(getTargetType(), getTargetId()),
entity.getPosition(), entity.getElevation(),
entity.getPriorPosition(), entity.moved);
}
/**
* To-hit number for a ram, assuming that movement has been handled
*/
public ToHitData toHit(IGame game, Targetable target, Coords src,
int elevation, Coords priorSrc, int movement) {
final Entity ae = getEntity(game);
// arguments legal?
if (ae == null) {
throw new IllegalStateException("Attacker is null");
}
// Do to pretreatment of physical attacks, the target may be null.
if (target == null) {
return new ToHitData(TargetRoll.IMPOSSIBLE, "Target is null");
}
if(!(ae instanceof Aero)) {
return new ToHitData(TargetRoll.IMPOSSIBLE, "Attacker is not Aero");
}
if(!(target instanceof Aero)) {
return new ToHitData(TargetRoll.IMPOSSIBLE, "Target is not Aero");
}
if(ae instanceof FighterSquadron || target instanceof FighterSquadron) {
return new ToHitData(TargetRoll.IMPOSSIBLE, "fighter squadrons may not ram nor be the target of a ramming attc");
}
Entity te = null;
if (target.getTargetType() == Targetable.TYPE_ENTITY) {
te = (Entity) target;
}
if (!game.getOptions().booleanOption("friendly_fire")) {
// a friendly unit can never be the target of a direct attack.
if (target.getTargetType() == Targetable.TYPE_ENTITY
&& (((Entity)target).getOwnerId() == ae.getOwnerId()
|| (((Entity)target).getOwner().getTeam() != Player.TEAM_NONE
&& ae.getOwner().getTeam() != Player.TEAM_NONE
&& ae.getOwner().getTeam() == ((Entity)target).getOwner().getTeam())))
return new ToHitData(TargetRoll.IMPOSSIBLE, "A friendly unit can never be the target of a direct attack.");
}
IHex attHex = game.getBoard().getHex(src);
IHex targHex = game.getBoard().getHex(target.getPosition());
final int attackerElevation = elevation + attHex.getElevation();
final int targetElevation = target.getElevation()
+ targHex.getElevation();
ToHitData toHit = null;
// can't target yourself
if (ae.equals(te)) {
return new ToHitData(TargetRoll.IMPOSSIBLE,
"You can't target yourself");
}
// Can't target a transported entity.
if (te != null && Entity.NONE != te.getTransportId()) {
return new ToHitData(TargetRoll.IMPOSSIBLE,
"Target is a passenger.");
}
// check range
if (src.distance(target.getPosition()) > 0) {
return new ToHitData(TargetRoll.IMPOSSIBLE, "Target not in range");
}
// target must be at same elevation level
if (attackerElevation != targetElevation) {
return new ToHitData(TargetRoll.IMPOSSIBLE,
"Target must be at the same elevation level");
}
// can't attack Aero making a different ramming attack
if (te != null && te.isRamming()) {
return new ToHitData(TargetRoll.IMPOSSIBLE,
"Target is already making a ramming attack");
}
//attacker
// target must have moved already
if (te != null && !te.isDone()) {
return new ToHitData(TargetRoll.IMPOSSIBLE,
"Target must be done with movement");
}
//Set the base BTH
int base = 6 + te.getCrew().getPiloting() - ae.getCrew().getPiloting();
toHit = new ToHitData(base, "base");
Aero a = (Aero)ae;
//target type
if(target instanceof SpaceStation) {
toHit.addModifier(-1,"target is a space station");
} else if(target instanceof Warship) {
toHit.addModifier(+1,"target is a warship");
} else if(target instanceof Jumpship) {
toHit.addModifier(+0,"target is a jumpship");
} else if(target instanceof Dropship) {
toHit.addModifier(+2,"target is a dropship");
} else {
toHit.addModifier(+4,"target is a fighter/small craft");
}
//attacker type
if(a instanceof SpaceStation) {
toHit.addModifier(+0,"attacker is a space station");
} else if(a instanceof Warship) {
toHit.addModifier(+1,"attacker is a warship");
} else if(a instanceof Jumpship) {
toHit.addModifier(+0,"attacker is a jumpship");
} else if(a instanceof Dropship) {
toHit.addModifier(-1,"attacker is a dropship");
} else {
toHit.addModifier(-2,"attacker is a fighter/small craft");
}
//can the target unit move
if(target.isImmobile() || te.getWalkMP() == 0)
toHit.addModifier(-2,"target cannot spend thrust");
//sensor damage
if(a.getSensorHits() > 0)
toHit.addModifier(+1, "sensor damage");
//avionics damage
int avionics = a.getAvionicsHits();
if(avionics > 3)
avionics = 3;
if(avionics > 0)
toHit.addModifier(avionics, "avionics damage");
//evading bonuses
if (target.getTargetType() == Targetable.TYPE_ENTITY && te.isEvading()) {
toHit.addModifier(te.getEvasionBonus(), "target is evading");
}
//determine hit direction
toHit.setSideTable(te.sideTable(priorSrc));
toHit.setHitTable(ToHitData.HIT_NORMAL);
// done!
return toHit;
}
/**
* Checks if a ram can hit the target, taking account of movement
*/
public ToHitData toHit(IGame game, MovePath md) {
final Entity ae = game.getEntity(getEntityId());
final Targetable target = getTarget(game);
Coords ramSrc = ae.getPosition();
int ramEl = ae.getElevation();
Coords priorSrc = md.getSecondFinalPosition(ae.getPosition());
MoveStep ramStep = null;
// let's just check this
if (!md.contains(MovePath.STEP_RAM)) {
return new ToHitData(TargetRoll.IMPOSSIBLE,
"Ram action not found in movement path");
}
// determine last valid step
md.compile(game, ae);
for (final Enumeration<MoveStep> i = md.getSteps(); i.hasMoreElements();) {
final MoveStep step = i.nextElement();
if (step.getMovementType() == IEntityMovementType.MOVE_ILLEGAL) {
break;
}
if (step.getType() == MovePath.STEP_RAM) {
ramStep = step;
ramSrc = step.getPosition();
ramEl = step.getElevation();
}
}
// need to reach target
if (ramStep == null
|| !target.getPosition().equals(ramStep.getPosition())) {
return new ToHitData(TargetRoll.IMPOSSIBLE,
"Could not reach target with movement");
}
return toHit(game, target, ramSrc, ramEl, priorSrc, ramStep.getMovementType());
}
/**
* Damage that an Aero does on a successful ramming attack
*
*/
public static int getDamageFor(Aero entity, Aero target) {
int avel = entity.getCurrentVelocity();
int tvel = target.getCurrentVelocity();
return getDamageFor(entity, target, entity.getPriorPosition(), avel, tvel);
}
public static int getDamageFor(Aero entity, Aero target, Coords atthex, int avel, int tvel) {
int netv = Compute.getNetVelocity(atthex, target, avel, tvel);
return (int) Math.ceil(
(entity.getWeight() / 10.0) * netv);
}
/**
* Damage that an Aero suffers after a successful charge.
*/
public static int getDamageTakenBy(Aero entity, Aero target) {
int avel = entity.getCurrentVelocity();
int tvel = target.getCurrentVelocity();
return getDamageTakenBy(entity, target, entity.getPriorPosition(), avel, tvel);
}
public static int getDamageTakenBy(Aero entity, Aero target, Coords atthex, int avel, int tvel) {
int netv = Compute.getNetVelocity(atthex, target, avel, tvel);
return (int) Math.ceil(
(target.getWeight() / 10.0) * netv);
}
}