/**
* 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.
*/
package megamek.common.actions;
import java.util.Enumeration;
import megamek.common.AmmoType;
import megamek.common.BattleArmor;
import megamek.common.Entity;
import megamek.common.Infantry;
import megamek.common.IGame;
import megamek.common.Mech;
import megamek.common.Mounted;
import megamek.common.PlanetaryConditions;
import megamek.common.Targetable;
import megamek.common.ToHitData;
import megamek.common.WeaponType;
/**
* Abstract superclass for any action where an entity is attacking another
* entity.
*/
public abstract class AbstractAttackAction extends AbstractEntityAction
implements AttackAction {
/**
*
*/
private static final long serialVersionUID = -897197664652217134L;
private int targetType;
private int targetId;
// default to attacking an entity, since this is what most of them are
public AbstractAttackAction(int entityId, int targetId) {
super(entityId);
this.targetType = Targetable.TYPE_ENTITY;
this.targetId = targetId;
}
public AbstractAttackAction(int entityId, int targetType, int targetId) {
super(entityId);
this.targetType = targetType;
this.targetId = targetId;
}
public int getTargetType() {
return targetType;
}
public int getTargetId() {
return targetId;
}
public void setTargetType(int targetType) {
this.targetType = targetType;
}
public void setTargetId(int targetId) {
this.targetId = targetId;
}
public Targetable getTarget(IGame g) {
return g.getTarget(getTargetType(), getTargetId());
}
public Entity getEntity(IGame g) {
Entity e = g.getEntity(getEntityId());
// if we have an artyattack, we might need to get an out-of-game entity
// if it died or fled
if (e == null) {
e = g.getOutOfGameEntity(getEntityId());
}
return e;
}
/**
* used by the toHit of derived classes atype may be null if not using an
* ammo based weapon
*/
public static ToHitData nightModifiers(IGame game, Targetable target,
AmmoType atype, Entity attacker, boolean isWeapon) {
ToHitData toHit = null;
Entity te = null;
if (target.getTargetType() == Targetable.TYPE_ENTITY) {
te = (Entity) target;
}
toHit = new ToHitData();
int lightCond = game.getPlanetaryConditions().getLight();
if(lightCond == PlanetaryConditions.L_DAY) {
//not nighttime so just return
return toHit;
}
// The base night penalty
int night_modifier = 0;
night_modifier = game.getPlanetaryConditions().getLightHitPenalty(isWeapon);
toHit.addModifier(night_modifier, game.getPlanetaryConditions().getLightCurrentName());
boolean illuminated = false;
if (te != null) {
illuminated = te.isIlluminated();
// hack for unresolved actions so client shows right BTH
if (!illuminated) {
for (Enumeration<EntityAction> actions = game.getActions(); actions
.hasMoreElements();) {
EntityAction a = actions.nextElement();
if (a instanceof SearchlightAttackAction) {
SearchlightAttackAction saa = (SearchlightAttackAction) a;
if (saa.willIlluminate(game, te)) {
illuminated = true;
break;
}
}
}
}
}
// Searchlights reduce the penalty to zero (or 1 for pitch-black) (except for dusk/dawn)
int searchlightMod = Math.min(3, night_modifier);
if(te != null && lightCond > PlanetaryConditions.L_DUSK) {
if (te.isUsingSpotlight()) {
toHit.addModifier(-searchlightMod, "target using searchlight");
night_modifier = night_modifier - searchlightMod;
} else if (illuminated) {
toHit.addModifier(-searchlightMod,"target illuminated by searchlight");
night_modifier = night_modifier - searchlightMod;
}
}
/*
// Ignored with EI system & implants
else if (attacker.hasActiveEiCockpit()) {
toHit.addModifier(-night_modifier, "EI system");
night_modifier = 0;
}
*/
// So do flares
else if (game.isPositionIlluminated(target.getPosition())) {
toHit.addModifier(-night_modifier,
"target illuminated by flare");
night_modifier = 0;
}
// Certain ammunitions reduce the penalty
else if (atype != null) {
if ((atype.getAmmoType() == AmmoType.T_AC || atype
.getAmmoType() == AmmoType.T_LAC)
&& (atype.getMunitionType() == AmmoType.M_INCENDIARY_AC || atype
.getMunitionType() == AmmoType.M_TRACER)) {
toHit.addModifier(-1, "incendiary/tracer ammo");
night_modifier--;
}
}
// Laser heatsinks
if (night_modifier > 0 && te != null && te instanceof Mech
&& ((Mech) te).hasLaserHeatSinks()) {
boolean lhsused = false;
if (te.heat > 0) {
toHit.addModifier(-night_modifier,
"target overheated with laser heatsinks");
night_modifier = 0;
}
// actions that generate heat give a -1 modifier
else if (te.heatBuildup > 0 || te.isStealthActive()) {
lhsused = true;
} else {
// Unfortunately, we can't just check weapons fired by the
// target
// because isUsedThisRound() is not valid if the attacker
// declared first.
// therefore, enumerate WeaponAttackActions...
for (Enumeration<EntityAction> actions = game.getActions(); actions
.hasMoreElements();) {
EntityAction a = actions.nextElement();
if (a instanceof WeaponAttackAction) {
WeaponAttackAction waa = (WeaponAttackAction) a;
if (waa.getEntityId() == te.getId()) {
Mounted weapon = te.getEquipment(waa
.getWeaponId());
WeaponType wtype = (WeaponType) weapon
.getType();
if (wtype.getHeat() != 0
|| weapon.isRapidfire()) {
// target fired a weapon that generates heat
lhsused = true;
break;
}
}
}
}
}
if (lhsused) {
toHit.addModifier(-1, "target uses laser heatsinks");
}
}
//now check for general hit bonuses for heat
if(te != null && !(attacker instanceof Infantry && !(attacker instanceof BattleArmor))) {
int heatBonus = game.getPlanetaryConditions().getLightHeatBonus(te.heat);
if(heatBonus < 0) {
toHit.addModifier(heatBonus, "target excess heat at night");
}
}
return toHit;
}
}