/*
* MegaMek -
* Copyright (C) 2000-2008 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.client.ui.AWT;
import java.awt.Button;
import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.TreeSet;
import java.util.Vector;
import megamek.client.Client;
import megamek.client.event.BoardViewEvent;
import megamek.client.event.BoardViewListener;
import megamek.client.ui.Messages;
import megamek.client.ui.AWT.widget.IndexedCheckbox;
import megamek.common.Aero;
import megamek.common.AmmoType;
import megamek.common.BattleArmor;
import megamek.common.BipedMech;
import megamek.common.BombType;
import megamek.common.Building;
import megamek.common.BuildingTarget;
import megamek.common.Compute;
import megamek.common.Coords;
import megamek.common.Entity;
import megamek.common.GameTurn;
import megamek.common.GunEmplacement;
import megamek.common.IAimingModes;
import megamek.common.IGame;
import megamek.common.INarcPod;
import megamek.common.Infantry;
import megamek.common.LargeSupportTank;
import megamek.common.LosEffects;
import megamek.common.Mech;
import megamek.common.Mounted;
import megamek.common.Protomech;
import megamek.common.QuadMech;
import megamek.common.Tank;
import megamek.common.TargetRoll;
import megamek.common.Targetable;
import megamek.common.ToHitData;
import megamek.common.WeaponType;
import megamek.common.actions.ArtilleryAttackAction;
import megamek.common.actions.EntityAction;
import megamek.common.actions.FindClubAction;
import megamek.common.actions.FlipArmsAction;
import megamek.common.actions.RepairWeaponMalfunctionAction;
import megamek.common.actions.SearchlightAttackAction;
import megamek.common.actions.SpotAction;
import megamek.common.actions.TorsoTwistAction;
import megamek.common.actions.UnjamTurretAction;
import megamek.common.actions.WeaponAttackAction;
import megamek.common.event.GameListener;
import megamek.common.event.GamePhaseChangeEvent;
import megamek.common.event.GameTurnChangeEvent;
public class FiringDisplay extends StatusBarPhaseDisplay implements
BoardViewListener, GameListener, ActionListener, DoneButtoned,
KeyListener, ItemListener {
/**
*
*/
private static final long serialVersionUID = 1270378753030784714L;
private static final int NUM_BUTTON_LAYOUTS = 2;
// Action command names
public static final String FIRE_AIM = "fireAim"; //$NON-NLS-1$
public static final String FIRE_FIND_CLUB = "fireFindClub"; //$NON-NLS-1$
public static final String FIRE_FIRE = "fireFire"; //$NON-NLS-1$
public static final String FIRE_MODE = "fireMode"; //$NON-NLS-1$
public static final String FIRE_FLIP_ARMS = "fireFlipArms"; //$NON-NLS-1$
public static final String FIRE_MORE = "fireMore"; //$NON-NLS-1$
public static final String FIRE_NEXT = "fireNext"; //$NON-NLS-1$
public static final String FIRE_NEXT_TARG = "fireNextTarg"; //$NON-NLS-1$
public static final String FIRE_SKIP = "fireSkip"; //$NON-NLS-1$
public static final String FIRE_SPOT = "fireSpot"; //$NON-NLS-1$
public static final String FIRE_TWIST = "fireTwist"; //$NON-NLS-1$
public static final String FIRE_CANCEL = "fireCancel"; //$NON-NLS-1$
public static final String FIRE_SEARCHLIGHT = "fireSearchlight"; //$NON-NLS-1$
public static final String FIRE_CLEAR_TURRET = "fireClearTurret"; //$NON-NLS-1$
public static final String FIRE_CLEAR_WEAPON = "fireClearWeaponJam"; //$NON-NLS-1$
// parent game
public Client client;
private ClientGUI clientgui;
// buttons
private Container panButtons;
private Button butFire;
private Button butTwist;
private Button butSkip;
private Button butFindClub;
private Button butNextTarg;
private Button butFlipArms;
private Button butSpot;
private Button butSearchlight;
// private Button butReport;
private Button butSpace;
private Button butFireMode;
private Button butFireClearTurret;
private Button butFireClearWeaponJam;
private Button butNext;
private Button butDone;
private Button butMore;
private int buttonLayout;
// let's keep track of what we're shooting and at what, too
private int cen = Entity.NONE; // current entity number
private Targetable target; // target
// HACK : track when we wan to show the target choice dialog.
private boolean showTargetChoice = true;
// shots we have so far.
private Vector<EntityAction> attacks;
// is the shift key held?
private boolean shiftheld;
private boolean twisting;
private Entity[] visibleTargets = null;
private int lastTargetID = -1;
private AimedShotHandler ash;
/**
* Creates and lays out a new firing phase display for the specified client.
*/
public FiringDisplay(ClientGUI clientgui) {
client = clientgui.getClient();
this.clientgui = clientgui;
client.game.addGameListener(this);
clientgui.getBoardView().addBoardViewListener(this);
shiftheld = false;
// fire
attacks = new Vector<EntityAction>();
setupStatusBar(Messages
.getString("FiringDisplay.waitingForFiringPhase")); //$NON-NLS-1$
butFire = new Button(Messages.getString("FiringDisplay.Fire")); //$NON-NLS-1$
butFire.addActionListener(this);
butFire.addKeyListener(this);
butFire.setActionCommand(FIRE_FIRE);
butFire.setEnabled(false);
butSkip = new Button(Messages.getString("FiringDisplay.Skip")); //$NON-NLS-1$
butSkip.addActionListener(this);
butSkip.addKeyListener(this);
butSkip.setActionCommand(FIRE_SKIP);
butSkip.setEnabled(false);
butTwist = new Button(Messages.getString("FiringDisplay.Twist")); //$NON-NLS-1$
butTwist.addActionListener(this);
butTwist.addKeyListener(this);
butTwist.setActionCommand(FIRE_TWIST);
butTwist.setEnabled(false);
butFindClub = new Button(Messages.getString("FiringDisplay.FindClub")); //$NON-NLS-1$
butFindClub.addActionListener(this);
butFindClub.addKeyListener(this);
butFindClub.setActionCommand(FIRE_FIND_CLUB);
butFindClub.setEnabled(false);
butNextTarg = new Button(Messages.getString("FiringDisplay.NextTarget")); //$NON-NLS-1$
butNextTarg.addActionListener(this);
butNextTarg.addKeyListener(this);
butNextTarg.setActionCommand(FIRE_NEXT_TARG);
butNextTarg.setEnabled(false);
butFlipArms = new Button(Messages.getString("FiringDisplay.FlipArms")); //$NON-NLS-1$
butFlipArms.addActionListener(this);
butFlipArms.addKeyListener(this);
butFlipArms.setActionCommand(FIRE_FLIP_ARMS);
butFlipArms.setEnabled(false);
butSpot = new Button(Messages.getString("FiringDisplay.Spot")); //$NON-NLS-1$
butSpot.addActionListener(this);
butSpot.addKeyListener(this);
butSpot.setActionCommand(FIRE_SPOT);
butSpot.setEnabled(false);
butSearchlight = new Button(Messages
.getString("FiringDisplay.Searchlight")); //$NON-NLS-1$
butSearchlight.addActionListener(this);
butSearchlight.addKeyListener(this);
butSearchlight.setActionCommand(FIRE_SEARCHLIGHT);
butSearchlight.setEnabled(false);
butSpace = new Button("."); //$NON-NLS-1$
butSpace.setEnabled(false);
butFireMode = new Button(Messages.getString("FiringDisplay.Mode")); //$NON-NLS-1$
butFireMode.addActionListener(this);
butFireMode.addKeyListener(this);
butFireMode.setActionCommand(FIRE_MODE);
butFireMode.setEnabled(false);
butFireClearTurret = new Button(Messages
.getString("FiringDisplay.ClearTurret")); //$NON-NLS-1$
butFireClearTurret.addActionListener(this);
butFireClearTurret.addKeyListener(this);
butFireClearTurret.setActionCommand(FIRE_CLEAR_TURRET);
butFireClearTurret.setEnabled(false);
butFireClearWeaponJam = new Button(Messages
.getString("FiringDisplay.ClearWeaponJam")); //$NON-NLS-1$
butFireClearWeaponJam.addActionListener(this);
butFireClearWeaponJam.addKeyListener(this);
butFireClearWeaponJam.setActionCommand(FIRE_CLEAR_WEAPON);
butFireClearWeaponJam.setEnabled(false);
butDone = new Button(Messages.getString("FiringDisplay.Done")); //$NON-NLS-1$
butDone.addActionListener(this);
butDone.addKeyListener(this);
butDone.setEnabled(false);
butNext = new Button(Messages.getString("FiringDisplay.NextUnit")); //$NON-NLS-1$
butNext.addActionListener(this);
butNext.addKeyListener(this);
butNext.setActionCommand(FIRE_NEXT);
butNext.setEnabled(false);
butMore = new Button(Messages.getString("FiringDisplay.More")); //$NON-NLS-1$
butMore.addActionListener(this);
butMore.addKeyListener(this);
butMore.setActionCommand(FIRE_MORE);
butMore.setEnabled(false);
// layout button grid
panButtons = new Panel();
buttonLayout = 0;
setupButtonPanel();
// layout screen
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setLayout(gridbag);
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
c.insets = new Insets(1, 1, 1, 1);
// c.gridwidth = GridBagConstraints.REMAINDER;
// addBag(clientgui.bv, gridbag, c);
// c.weightx = 1.0; c.weighty = 0;
// c.gridwidth = 1;
// addBag(client.cb.getComponent(), gridbag, c);
c.gridwidth = GridBagConstraints.REMAINDER;
c.weightx = 0.0;
c.weighty = 0.0;
addBag(panButtons, gridbag, c);
c.weightx = 1.0;
c.weighty = 0.0;
c.gridwidth = GridBagConstraints.REMAINDER;
addBag(panStatus, gridbag, c);
clientgui.bv.addKeyListener(this);
addKeyListener(this);
// mech display.
clientgui.mechD.wPan.weaponList.addItemListener(this);
clientgui.mechD.wPan.weaponList.addKeyListener(this);
ash = new AimedShotHandler();
}
private void addBag(Component comp, GridBagLayout gridbag,
GridBagConstraints c) {
gridbag.setConstraints(comp, c);
add(comp);
comp.addKeyListener(this);
}
private void setupButtonPanel() {
panButtons.removeAll();
panButtons.setLayout(new GridLayout(0, 8));
switch (buttonLayout) {
case 0:
panButtons.add(butNext);
panButtons.add(butFire);
panButtons.add(butSkip);
panButtons.add(butNextTarg);
panButtons.add(butTwist);
panButtons.add(butFireMode);
panButtons.add(butFireClearWeaponJam);
panButtons.add(butMore);
break;
case 1:
panButtons.add(butNext);
panButtons.add(butFire);
panButtons.add(butFlipArms);
panButtons.add(butFindClub);
panButtons.add(butSpot);
panButtons.add(butSearchlight);
panButtons.add(butFireClearTurret);
panButtons.add(butMore);
break;
}
validate();
}
/**
* Selects an entity, by number, for movement.
*/
public void selectEntity(int en) {
// clear any previously considered attacks
if (en != cen) {
clearAttacks();
refreshAll();
}
if (client.game.getEntity(en) != null) {
cen = en;
clientgui.setSelectedEntityNum(en);
// If the selected entity is not on the board, use the next one.
// ASSUMPTION: there will always be *at least one* entity on map.
if (null == ce().getPosition()) {
// Walk through the list of entities for this player.
for (int nextId = client.getNextEntityNum(en); nextId != en; nextId = client
.getNextEntityNum(nextId)) {
if (null != client.game.getEntity(nextId).getPosition()) {
cen = nextId;
break;
}
} // Check the player's next entity.
// We were *supposed* to have found an on-board entity.
if (null == ce().getPosition()) {
System.err
.println("FiringDisplay: could not find an on-board entity: " + //$NON-NLS-1$
en);
return;
}
} // End ce()-not-on-board
int lastTarget = ce().getLastTarget();
if (ce() instanceof Mech) {
int grapple = ((Mech) ce()).getGrappled();
if (grapple != Entity.NONE) {
lastTarget = grapple;
}
}
Entity t = client.game.getEntity(lastTarget);
target(t);
if (!ce().isOffBoard()) {
clientgui.getBoardView().highlight(ce().getPosition());
}
clientgui.getBoardView().select(null);
clientgui.getBoardView().cursor(null);
refreshAll();
cacheVisibleTargets();
if (!clientgui.bv.isMovingUnits() && !ce().isOffBoard()) {
clientgui.bv.centerOnHex(ce().getPosition());
}
// Update the menu bar.
clientgui.getMenuBar().setEntity(ce());
// only twist if crew conscious
setTwistEnabled(ce().canChangeSecondaryFacing()
&& ce().getCrew().isActive());
setFindClubEnabled(FindClubAction.canMechFindClub(client.game, en));
setFlipArmsEnabled(ce().canFlipArms());
updateSearchlight();
updateClearTurret();
updateClearWeaponJam();
} else {
System.err
.println("FiringDisplay: tried to select non-existant entity: " + en); //$NON-NLS-1$
}
}
/**
* Does turn start stuff
*/
private void beginMyTurn() {
target = null;
selectEntity(client.getFirstEntityNum());
if (!clientgui.bv.isMovingUnits()) {
clientgui.setDisplayVisible(true);
}
GameTurn turn = client.getMyTurn();
// There's special processing for triggering AP Pods.
if ((turn instanceof GameTurn.TriggerAPPodTurn)
&& (null != ce())) {
disableButtons();
TriggerAPPodDialog dialog = new TriggerAPPodDialog(clientgui
.getFrame(), ce());
dialog.setVisible(true);
attacks.removeAllElements();
Enumeration<EntityAction> actions = dialog.getActions();
while (actions.hasMoreElements()) {
attacks.addElement(actions.nextElement());
}
ready();
} else if ((turn instanceof GameTurn.TriggerBPodTurn)
&& (null != ce())) {
disableButtons();
TriggerBPodDialog dialog = new TriggerBPodDialog(clientgui, ce(),
((GameTurn.TriggerBPodTurn)turn).getAttackType());
dialog.setVisible(true);
attacks.removeAllElements();
Enumeration<EntityAction> actions = dialog.getActions();
while (actions.hasMoreElements()) {
attacks.addElement(actions.nextElement());
}
ready();
} else {
setNextEnabled(true);
butDone.setEnabled(true);
butMore.setEnabled(true);
setFireModeEnabled(true);
clientgui.getBoardView().select(null);
}
}
/**
* Does end turn stuff.
*/
private void endMyTurn() {
// end my turn, then.
Entity next = client.game.getNextEntity(client.game.getTurnIndex());
if ((IGame.Phase.PHASE_FIRING == client.game.getPhase()) && (null != next)
&& (null != ce()) && (next.getOwnerId() != ce().getOwnerId())) {
clientgui.setDisplayVisible(false);
}
cen = Entity.NONE;
target(null);
clientgui.getBoardView().select(null);
clientgui.getBoardView().highlight(null);
clientgui.getBoardView().cursor(null);
clientgui.bv.clearMovementData();
disableButtons();
clearVisibleTargets();
}
/**
* Disables all buttons in the interface
*/
private void disableButtons() {
setFireEnabled(false);
setSkipEnabled(false);
setTwistEnabled(false);
setSpotEnabled(false);
setFindClubEnabled(false);
butMore.setEnabled(false);
setNextEnabled(false);
butDone.setEnabled(false);
setNextTargetEnabled(false);
setFlipArmsEnabled(false);
setFireModeEnabled(false);
setFireClearTurretEnabled(false);
setFireClearWeaponJamEnabled(false);
}
/**
* Fire Mode - Adds a Fire Mode Change to the current Attack Action
*/
private void changeMode() {
int wn = clientgui.mechD.wPan.getSelectedWeaponNum();
// Do nothing we have no unit selected.
if (null == ce()) {
return;
}
// If the weapon does not have modes, just exit.
Mounted m = ce().getEquipment(wn);
if ((m == null) || !m.getType().hasModes()) {
return;
}
// Aeros are not allowed to switch modes under standard rules
/*
* TODO: This may cause problems, but now with StratOps some modes are legal
if (ce() instanceof Aero) {
return;
}
*/
// send change to the server
int nMode = m.switchMode();
client.sendModeChange(cen, wn, nMode);
// notify the player
if (m.canInstantSwitch(nMode)) {
clientgui
.systemMessage(Messages
.getString(
"FiringDisplay.switched", new Object[] { m.getName(), m.curMode().getDisplayableName() })); //$NON-NLS-1$
} else {
clientgui
.systemMessage(Messages
.getString(
"FiringDisplay.willSwitch", new Object[] { m.getName(), m.pendingMode().getDisplayableName() })); //$NON-NLS-1$
}
updateTarget();
clientgui.mechD.wPan.displayMech(ce());
clientgui.mechD.wPan.selectWeapon(wn);
}
/**
* Cache the list of visible targets. This is used for the 'next target'
* button. We'll sort it by range to us.
*/
private void cacheVisibleTargets() {
clearVisibleTargets();
Vector<Entity> vec = client.game.getValidTargets(ce());
Comparator<Entity> sortComp = new Comparator<Entity>() {
public int compare(Entity entX, Entity entY) {
int rangeToX = ce().getPosition().distance(entX.getPosition());
int rangeToY = ce().getPosition().distance(entY.getPosition());
if (rangeToX == rangeToY) {
return ((entX.getId() < entY.getId()) ? -1 : 1);
}
return ((rangeToX < rangeToY) ? -1 : 1);
}
};
TreeSet<Entity> tree = new TreeSet<Entity>(sortComp);
visibleTargets = new Entity[vec.size()];
tree.addAll(vec);
Iterator<Entity> it = tree.iterator();
int count = 0;
while (it.hasNext()) {
visibleTargets[count++] = it.next();
}
setNextTargetEnabled(visibleTargets.length > 0);
}
private void clearVisibleTargets() {
visibleTargets = null;
lastTargetID = -1;
setNextTargetEnabled(false);
}
/**
* Get the next target. Return null if we don't have any targets.
*/
private Entity getNextTarget() {
if (null == visibleTargets) {
return null;
}
lastTargetID++;
if (lastTargetID >= visibleTargets.length) {
lastTargetID = 0;
}
return visibleTargets[lastTargetID];
}
/**
* Jump to our next target. If there isn't one, well, don't do anything.
*/
private void jumpToNextTarget() {
Entity targ = getNextTarget();
if (null == targ) {
return;
}
// HACK : don't show the choice dialog.
showTargetChoice = false;
clientgui.bv.centerOnHex(targ.getPosition());
clientgui.getBoardView().select(targ.getPosition());
// HACK : show the choice dialog again.
showTargetChoice = true;
target(targ);
}
/**
* Called when the current entity is done firing. Send out our attack queue
* to the server.
*/
private void ready() {
if (attacks.isEmpty()
&& GUIPreferences.getInstance().getNagForNoAction()) {
// comfirm this action
String title = Messages
.getString("FiringDisplay.DontFireDialog.title"); //$NON-NLS-1$
String body = Messages
.getString("FiringDisplay.DontFireDialog.message"); //$NON-NLS-1$
ConfirmDialog response = clientgui.doYesNoBotherDialog(title, body);
if (!response.getShowAgain()) {
GUIPreferences.getInstance().setNagForNoAction(false);
}
if (!response.getAnswer()) {
return;
}
}
// stop further input (hopefully)
disableButtons();
// remove temporary attacks from game & board
removeTempAttacks();
// For bug 1002223
// Re-compute the to-hit numbers by adding in correct order.
Vector<EntityAction> newAttacks = new Vector<EntityAction>();
for (EntityAction o : attacks) {
if (o instanceof ArtilleryAttackAction) {
newAttacks.addElement(o);
} else if (o instanceof WeaponAttackAction) {
WeaponAttackAction waa = (WeaponAttackAction) o;
Entity attacker = waa.getEntity(client.game);
Targetable target = waa.getTarget(client.game);
boolean curInFrontArc = Compute.isInArc(attacker.getPosition(),
attacker.getSecondaryFacing(), target.getPosition(),
Compute.ARC_FORWARD);
if (curInFrontArc) {
WeaponAttackAction waa2 = new WeaponAttackAction(waa
.getEntityId(), waa.getTargetType(), waa
.getTargetId(), waa.getWeaponId());
waa2.setAimedLocation(waa.getAimedLocation());
waa2.setAimingMode(waa.getAimingMode());
waa2.setOtherAttackInfo(waa.getOtherAttackInfo());
waa2.setAmmoId(waa.getAmmoId());
waa2.setBombPayload(waa.getBombPayload());
newAttacks.addElement(waa2);
}
} else {
newAttacks.addElement(o);
}
}
// now add the attacks in rear/arm arcs
for (EntityAction o : attacks) {
if (o instanceof ArtilleryAttackAction) {
continue;
} else if (o instanceof WeaponAttackAction) {
WeaponAttackAction waa = (WeaponAttackAction) o;
Entity attacker = waa.getEntity(client.game);
Targetable target = waa.getTarget(client.game);
boolean curInFrontArc = Compute.isInArc(attacker.getPosition(),
attacker.getSecondaryFacing(), target.getPosition(),
Compute.ARC_FORWARD);
if (!curInFrontArc) {
WeaponAttackAction waa2 = new WeaponAttackAction(waa
.getEntityId(), waa.getTargetType(), waa
.getTargetId(), waa.getWeaponId());
waa2.setAimedLocation(waa.getAimedLocation());
waa2.setAimingMode(waa.getAimingMode());
waa2.setOtherAttackInfo(waa.getOtherAttackInfo());
waa2.setAmmoId(waa.getAmmoId());
waa2.setBombPayload(waa.getBombPayload());
newAttacks.addElement(waa2);
}
}
}
// send out attacks
client.sendAttackData(cen, newAttacks);
// clear queue
attacks.removeAllElements();
// Clear the menu bar.
clientgui.getMenuBar().setEntity(null);
// close aimed shot display, if any
ash.closeDialog();
endMyTurn();
}
private void doClearTurret() {
String title = Messages.getString("FiringDisplay.ClearTurret.title"); //$NON-NLS-1$
String body = Messages.getString("FiringDisplay.ClearTurret.message"); //$NON-NLS-1$
if (!clientgui.doYesNoDialog(title, body)) {
return;
}
if ((attacks.size() == 0) && (ce() instanceof Tank)
&& ((Tank) ce()).isTurretJammed()) {
UnjamTurretAction uta = new UnjamTurretAction(ce().getId());
attacks.add(uta);
ready();
}
}
private void doClearWeaponJam() {
ArrayList<Mounted> weapons = ((Tank) ce()).getJammedWeapons();
String[] names = new String[weapons.size()];
for (int loop = 0; loop < names.length; loop++) {
names[loop] = weapons.get(loop).getDesc();
}
SingleChoiceDialog choiceDialog = new SingleChoiceDialog(
clientgui.frame, Messages
.getString("FiringDisplay.ClearWeaponJam.title"), //$NON-NLS-1$
Messages.getString("FiringDisplay.ClearWeaponJam.question"), //$NON-NLS-1$
names);
choiceDialog.setVisible(true);
if (choiceDialog.getAnswer() == true) {
RepairWeaponMalfunctionAction rwma = new RepairWeaponMalfunctionAction(
ce().getId(), ce().getEquipmentNum(
weapons.get(choiceDialog.getChoice())));
attacks.add(rwma);
ready();
}
}
private void doSearchlight() {
// validate
if ((ce() == null) || (target == null)) {
throw new IllegalArgumentException(
"current searchlight parameters are invalid"); //$NON-NLS-1$
}
if (!SearchlightAttackAction.isPossible(client.game, cen, target, null)) {
return;
}
// create and queue a searchlight action
SearchlightAttackAction saa = new SearchlightAttackAction(cen, target
.getTargetType(), target.getTargetId());
attacks.addElement(saa);
// and add it into the game, temporarily
client.game.addAction(saa);
clientgui.bv.addAttack(saa);
clientgui.minimap.drawMap();
// refresh weapon panel, as bth will have changed
updateTarget();
}
private int[] doSpaceBombing() {
int[] payload = new int[BombType.B_NUM];
if(!(ce() instanceof Aero)) {
return payload;
}
Vector<Mounted> bombs = ((Aero)ce()).getSpaceBombs();
String[] bnames = new String[bombs.size()];
for(int i = 0; i < bnames.length; i++) {
bnames[i] = bombs.elementAt(i).getDesc();
}
ChoiceDialog bombsDialog = new ChoiceDialog(clientgui.frame,
Messages
.getString("FiringDisplay.BombNumberDialog.title"), //$NON-NLS-1$
Messages
.getString("FiringDisplay.BombNumberDialog.message"), //$NON-NLS-1$
bnames);
bombsDialog.setVisible(true);
if(bombsDialog.getAnswer()) {
int[] choices = bombsDialog.getChoices();
for(int j = 0; j < choices.length; j++) {
int type = ((BombType)bombs.elementAt(choices[j]).getType()).getBombType();
payload[type] = payload[type] + 1;
}
}
return payload;
}
/**
* Adds a weapon attack with the currently selected weapon to the attack
* queue.
*/
public void fire() {
// get the selected weaponnum
int weaponNum = clientgui.mechD.wPan.getSelectedWeaponNum();
Mounted mounted = ce().getEquipment(weaponNum);
// validate
if ((ce() == null) || (target == null) || (mounted == null)
|| !(mounted.getType() instanceof WeaponType)) {
throw new IllegalArgumentException(
"current fire parameters are invalid"); //$NON-NLS-1$
}
// check if we now shoot at a target in the front arc and previously
// shot a target in side/rear arc that then was primary target
// if so, ask and tell the user that to-hits will change
if ((ce() instanceof Mech) || (ce() instanceof Tank)
|| (ce() instanceof Protomech)) {
EntityAction lastAction = null;
try {
lastAction = attacks.lastElement();
} catch (NoSuchElementException ex) {
}
if ((lastAction != null) && (lastAction instanceof WeaponAttackAction)) {
WeaponAttackAction oldWaa = (WeaponAttackAction) lastAction;
Targetable oldTarget = oldWaa.getTarget(client.game);
if (!oldTarget.equals(target)) {
boolean oldInFront = Compute.isInArc(ce().getPosition(),
ce().getSecondaryFacing(), oldTarget.getPosition(),
Compute.ARC_FORWARD);
boolean curInFront = Compute.isInArc(ce().getPosition(),
ce().getSecondaryFacing(), target.getPosition(),
Compute.ARC_FORWARD);
if (!oldInFront && curInFront) {
String title = Messages
.getString("FiringDisplay.SecondaryTargetToHitChange.title"); //$NON-NLS-1$
String body = Messages
.getString("FiringDisplay.SecondaryTargetToHitChange.message"); //$NON-NLS-1$
if (!clientgui.doYesNoDialog(title, body)) {
return;
}
}
}
}
}
// declare searchlight, if possible
if (GUIPreferences.getInstance().getAutoDeclareSearchlight()) {
doSearchlight();
}
WeaponAttackAction waa;
if (!mounted.getType().hasFlag(WeaponType.F_ARTILLERY)) {
waa = new WeaponAttackAction(cen, target.getTargetType(), target
.getTargetId(), weaponNum);
} else {
waa = new ArtilleryAttackAction(cen, target.getTargetType(), target
.getTargetId(), weaponNum, client.game);
}
//if this is a space bomb attack, then bring up the payload dialog
if(mounted.getType().hasFlag(WeaponType.F_SPACE_BOMB)) {
int[] payload = doSpaceBombing();
//TODO: return if the user cancels
waa.setBombPayload(payload);
}
if ((null != mounted.getLinked())
&& (((WeaponType) mounted.getType()).getAmmoType() != AmmoType.T_NA)) {
Mounted ammoMount = mounted.getLinked();
AmmoType ammoType = (AmmoType) ammoMount.getType();
waa.setAmmoId(ce().getEquipmentNum(ammoMount));
if (((ammoType.getMunitionType() == AmmoType.M_THUNDER_VIBRABOMB) && ((ammoType
.getAmmoType() == AmmoType.T_LRM) || (ammoType.getAmmoType() == AmmoType.T_MML)))
|| (ammoType.getMunitionType() == AmmoType.M_VIBRABOMB_IV))
{
VibrabombSettingDialog vsd = new VibrabombSettingDialog(
clientgui.frame);
vsd.setVisible(true);
waa.setOtherAttackInfo(vsd.getSetting());
}
}
if (ash.allowAimedShotWith(mounted) && ash.inAimingMode()
&& ash.isAimingAtLocation()) {
waa.setAimedLocation(ash.getAimingAt());
waa.setAimingMode(ash.getAimingMode());
} else {
waa.setAimedLocation(Entity.LOC_NONE);
waa.setAimingMode(IAimingModes.AIM_MODE_NONE);
}
// add the attack to our temporary queue
attacks.addElement(waa);
// and add it into the game, temporarily
client.game.addAction(waa);
clientgui.minimap.drawMap();
// set the weapon as used
mounted.setUsedThisRound(true);
// find the next available weapon
int nextWeapon = ce().getNextWeapon(weaponNum);
// we fired a weapon, can't clear turret jams or weapon jams anymore
updateClearTurret();
updateClearWeaponJam();
// check; if there are no ready weapons, you're done.
if ((nextWeapon == -1) && GUIPreferences.getInstance().getAutoEndFiring()) {
ready();
return;
}
// otherwise, display firing info for the next weapon
clientgui.mechD.wPan.displayMech(ce());
clientgui.mechD.wPan.selectWeapon(nextWeapon);
updateTarget();
}
/**
* Skips to the next weapon
*/
public void nextWeapon() {
int nextWeapon = ce().getNextWeapon(
clientgui.mechD.wPan.getSelectedWeaponNum());
// if there's no next weapon, forget about it
if (nextWeapon == -1) {
return;
}
clientgui.mechD.wPan.displayMech(ce());
clientgui.mechD.wPan.selectWeapon(nextWeapon);
updateTarget();
}
/**
* The entity spends the rest of its turn finding a club
*/
public void findClub() {
if (ce() == null) {
return;
}
// comfirm this action
String title = Messages.getString("FiringDisplay.FindClubDialog.title"); //$NON-NLS-1$
String body = Messages
.getString("FiringDisplay.FindClubDialog.message"); //$NON-NLS-1$
if (!clientgui.doYesNoDialog(title, body)) {
return;
}
attacks.removeAllElements();
attacks.addElement(new FindClubAction(cen));
ready();
}
/**
* The entity spends the rest of its turn spotting
*/
private void doSpot() {
if ((ce() == null) || (target == null)) {
return;
}
if (ce().isINarcedWith(INarcPod.HAYWIRE)) {
String title = Messages
.getString("FiringDisplay.CantSpotDialog.title"); //$NON-NLS-1$
String body = Messages
.getString("FiringDisplay.CantSpotDialog.message"); //$NON-NLS-1$
clientgui.doAlertDialog(title, body);
return;
}
// comfirm this action
String title = Messages
.getString("FiringDisplay.SpotForInderectDialog.title"); //$NON-NLS-1$
String body = Messages
.getString("FiringDisplay.SpotForInderectDialog.message"); //$NON-NLS-1$
if (!clientgui.doYesNoDialog(title, body)) {
return;
}
attacks.addElement(new SpotAction(cen, target.getTargetId()));
}
/**
* Removes all current fire
*/
private void clearAttacks() {
// We may not have an entity selected yet (race condition).
if (ce() == null) {
return;
}
// remove attacks, set weapons available again
Enumeration<EntityAction> i = attacks.elements();
while (i.hasMoreElements()) {
EntityAction ea = i.nextElement();
if (ea instanceof WeaponAttackAction) {
WeaponAttackAction waa = (WeaponAttackAction) ea;
ce().getEquipment(waa.getWeaponId()).setUsedThisRound(false);
}
}
attacks.removeAllElements();
// remove temporary attacks from game & board
removeTempAttacks();
// restore any other movement to default
ce().setSecondaryFacing(ce().getFacing());
ce().setArmsFlipped(false);
}
/**
* Removes temp attacks from the game and board
*/
private void removeTempAttacks() {
// remove temporary attacks from game & board
client.game.removeActionsFor(cen);
clientgui.bv.removeAttacksFor(ce());
}
/**
* removes the last action
*/
private void removeLastFiring() {
Object o = attacks.lastElement();
if (o instanceof WeaponAttackAction) {
WeaponAttackAction waa = (WeaponAttackAction) o;
ce().getEquipment(waa.getWeaponId()).setUsedThisRound(false);
attacks.removeElement(o);
clientgui.mechD.wPan.displayMech(ce());
client.game.removeAction(o);
clientgui.bv.refreshAttacks();
clientgui.minimap.drawMap();
}
}
/**
* Refeshes all displays.
*/
private void refreshAll() {
if (ce() == null) {
return;
}
clientgui.bv.redrawEntity(ce());
clientgui.mechD.displayEntity(ce());
clientgui.mechD.showPanel("weapons"); //$NON-NLS-1$
clientgui.mechD.wPan.selectWeapon(ce().getFirstWeapon());
updateTarget();
}
/**
* Targets something
*/
void target(Targetable t) {
target = t;
ash.setAimingMode();
updateTarget();
ash.showDialog();
}
/**
* Targets something
*/
protected void updateTarget() {
setFireEnabled(false);
// make sure we're showing the current entity in the mech display
if ((ce() != null) && !ce().equals(clientgui.mechD.getCurrentEntity())) {
clientgui.mechD.displayEntity(ce());
}
// allow spotting
if ((ce() != null) && ce().canSpot() && (target != null)
&& client.game.getOptions().booleanOption("indirect_fire")) { //$NON-NLS-1$)
setSpotEnabled(true);
}
// update target panel
final int weaponId = clientgui.mechD.wPan.getSelectedWeaponNum();
if ((target != null) && (target.getPosition() != null) && (weaponId != -1)
&& (ce() != null)) {
ToHitData toHit;
if (ash.inAimingMode()) {
Mounted weapon = ce().getEquipment(weaponId);
boolean aiming = ash.isAimingAtLocation()
&& ash.allowAimedShotWith(weapon);
ash.setEnableAll(aiming);
if (aiming) {
toHit = WeaponAttackAction.toHit(client.game, cen, target,
weaponId, ash.getAimingAt(), ash.getAimingMode());
clientgui.mechD.wPan.wTargetR.setText(target
.getDisplayName()
+ " (" + ash.getAimingLocation() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
toHit = WeaponAttackAction.toHit(client.game, cen, target,
weaponId, Entity.LOC_NONE,
IAimingModes.AIM_MODE_NONE);
clientgui.mechD.wPan.wTargetR.setText(target
.getDisplayName());
}
ash.setPartialCover(toHit.getCover());
} else {
toHit = WeaponAttackAction.toHit(client.game, cen, target,
weaponId, Entity.LOC_NONE, IAimingModes.AIM_MODE_NONE);
clientgui.mechD.wPan.wTargetR.setText(target.getDisplayName());
}
clientgui.mechD.wPan.wRangeR
.setText("" + ce().getPosition().distance(target.getPosition())); //$NON-NLS-1$
if((ce() instanceof Aero) && (target instanceof Aero) && client.game.getBoard().inAtmosphere()) {
//add altitude difference
int altdiff = Math.abs(ce().getElevation()-target.getElevation());
clientgui.mechD.wPan.wRangeR.setText("" + ce().getPosition().distance(target.getPosition()) + " + " + altdiff + " altitude difference"); //$NON-NLS-1$
}
Mounted m = ce().getEquipment(weaponId);
if (m.isUsedThisRound()) {
clientgui.mechD.wPan.wToHitR.setText(Messages
.getString("FiringDisplay.alreadyFired")); //$NON-NLS-1$
setFireEnabled(false);
} else if (m.getType().hasFlag(WeaponType.F_AUTO_TARGET)) {
clientgui.mechD.wPan.wToHitR.setText(Messages
.getString("FiringDisplay.autoFiringWeapon")); //$NON-NLS-1$
setFireEnabled(false);
} else if (toHit.getValue() == TargetRoll.IMPOSSIBLE) {
clientgui.mechD.wPan.wToHitR.setText(toHit.getValueAsString());
setFireEnabled(false);
} else if (toHit.getValue() == TargetRoll.AUTOMATIC_FAIL) {
clientgui.mechD.wPan.wToHitR.setText(toHit.getValueAsString());
setFireEnabled(true);
} else {
clientgui.mechD.wPan.wToHitR.setText(toHit.getValueAsString()
+ " (" + Compute.oddsAbove(toHit.getValue()) + "%)"); //$NON-NLS-1$ //$NON-NLS-2$
setFireEnabled(true);
}
clientgui.mechD.wPan.toHitText.setText(toHit.getDesc());
setSkipEnabled(true);
} else {
clientgui.mechD.wPan.wTargetR.setText("---"); //$NON-NLS-1$
clientgui.mechD.wPan.wRangeR.setText("---"); //$NON-NLS-1$
clientgui.mechD.wPan.wToHitR.setText("---"); //$NON-NLS-1$
clientgui.mechD.wPan.toHitText.setText(""); //$NON-NLS-1$
}
updateSearchlight();
}
/**
* Torso twist in the proper direction.
*/
public void torsoTwist(Coords target) {
int direction = ce().getFacing();
if (null != target) {
direction = ce().clipSecondaryFacing(
ce().getPosition().direction(target));
}
if (direction != ce().getSecondaryFacing()) {
clearAttacks();
attacks.addElement(new TorsoTwistAction(cen, direction));
ce().setSecondaryFacing(direction);
refreshAll();
}
}
/**
* Torso twist to the left or right
*
* @param target
* An <code>int</code> specifying wether we're twisting left or
* right, 0 if we're twisting to the left, 1 if to the right.
*/
public void torsoTwist(int target) {
int direction = ce().getSecondaryFacing();
if (target == 0) {
clearAttacks();
direction = ce().clipSecondaryFacing((direction + 5) % 6);
attacks.addElement(new TorsoTwistAction(cen, direction));
ce().setSecondaryFacing(direction);
refreshAll();
} else if (target == 1) {
clearAttacks();
direction = ce().clipSecondaryFacing((direction + 7) % 6);
attacks.addElement(new TorsoTwistAction(cen, direction));
ce().setSecondaryFacing(direction);
refreshAll();
}
}
/**
* Returns the current entity.
*/
private Entity ce() {
return client.game.getEntity(cen);
}
//
// BoardListener
//
@Override
public void hexMoused(BoardViewEvent b) {
// Are we ignoring events?
if (isIgnoringEvents()) {
return;
}
// ignore buttons other than 1
if (!client.isMyTurn()
|| ((b.getModifiers() & InputEvent.BUTTON1_MASK) == 0)) {
return;
}
// control pressed means a line of sight check.
// added ALT_MASK by kenn
if (((b.getModifiers() & InputEvent.CTRL_MASK) != 0)
|| ((b.getModifiers() & InputEvent.ALT_MASK) != 0)) {
return;
}
// check for shifty goodness
if (shiftheld != ((b.getModifiers() & InputEvent.SHIFT_MASK) != 0)) {
shiftheld = (b.getModifiers() & InputEvent.SHIFT_MASK) != 0;
}
if (b.getType() == BoardViewEvent.BOARD_HEX_DRAGGED) {
if (shiftheld || twisting) {
updateFlipArms(false);
torsoTwist(b.getCoords());
}
clientgui.getBoardView().cursor(b.getCoords());
} else if (b.getType() == BoardViewEvent.BOARD_HEX_CLICKED) {
twisting = false;
clientgui.getBoardView().select(b.getCoords());
}
}
@Override
public void hexSelected(BoardViewEvent b) {
// Are we ignoring events?
if (isIgnoringEvents()) {
return;
}
if (client.isMyTurn() && (b.getCoords() != null) && (ce() != null)
&& !b.getCoords().equals(ce().getPosition())) {
// HACK : sometimes we don't show the target choice window
Targetable targ = null;
if (showTargetChoice) {
targ = chooseTarget(b.getCoords());
}
if (shiftheld) {
updateFlipArms(false);
torsoTwist(b.getCoords());
} else if (targ != null) {
target(targ);
}
}
}
//
// GameListener
//
@Override
public void gameTurnChange(GameTurnChangeEvent e) {
// Are we ignoring events?
if (isIgnoringEvents()) {
return;
}
if (client.game.getPhase() == IGame.Phase.PHASE_FIRING) {
if (client.isMyTurn()) {
if(cen==Entity.NONE) {
beginMyTurn();
}
setStatusBarText(Messages
.getString("FiringDisplay.its_your_turn")); //$NON-NLS-1$
} else {
endMyTurn();
setStatusBarText(Messages
.getString(
"FiringDisplay.its_others_turn", new Object[] { e.getPlayer().getName() })); //$NON-NLS-1$
}
}
}
@Override
public void gamePhaseChange(GamePhaseChangeEvent e) {
// Are we ignoring events?
if (isIgnoringEvents()) {
return;
}
if (client.isMyTurn()
&& (client.game.getPhase() != IGame.Phase.PHASE_FIRING)) {
endMyTurn();
}
// if we're ending the firing phase, unregister stuff.
if (client.game.getPhase() == IGame.Phase.PHASE_FIRING) {
setStatusBarText(Messages
.getString("FiringDisplay.waitingForFiringPhase")); //$NON-NLS-1$
}
}
//
// ActionListener
//
public void actionPerformed(ActionEvent ev) {
// Are we ignoring events?
if (isIgnoringEvents()) {
return;
}
if (statusBarActionPerformed(ev, client)) {
return;
}
if (!client.isMyTurn()) {
return;
}
if (ev.getSource() == butDone) {
ready();
} else if (ev.getActionCommand().equalsIgnoreCase("viewGameOptions")) { //$NON-NLS-1$
// Make sure the game options dialog is not editable.
if (clientgui.getGameOptionsDialog().isEditable()) {
clientgui.getGameOptionsDialog().setEditable(false);
}
// Display the game options dialog.
clientgui.getGameOptionsDialog().update(client.game.getOptions());
clientgui.getGameOptionsDialog().setVisible(true);
} else if (ev.getActionCommand().equals(FIRE_FIRE)) {
fire();
} else if (ev.getActionCommand().equals(FIRE_SKIP)) {
nextWeapon();
} else if (ev.getActionCommand().equals(FIRE_TWIST)) {
twisting = true;
} else if (ev.getActionCommand().equals(FIRE_NEXT)) {
selectEntity(client.getNextEntityNum(cen));
} else if (ev.getActionCommand().equals(FIRE_MORE)) {
buttonLayout++;
buttonLayout %= NUM_BUTTON_LAYOUTS;
setupButtonPanel();
} else if (ev.getActionCommand().equals(FIRE_FIND_CLUB)) {
findClub();
} else if (ev.getActionCommand().equals(FIRE_SPOT)) {
doSpot();
} else if (ev.getActionCommand().equals(FIRE_NEXT_TARG)) {
jumpToNextTarget();
} else if (ev.getActionCommand().equals(FIRE_FLIP_ARMS)) {
updateFlipArms(!ce().getArmsFlipped());
} else if (ev.getActionCommand().equals(FIRE_MODE)) {
changeMode();
} else if ((ev.getActionCommand().equalsIgnoreCase("changeSinks"))
|| (ev.getActionCommand().equals(FIRE_CANCEL))) {
clearAttacks();
clientgui.getBoardView().select(null);
clientgui.getBoardView().cursor(null);
refreshAll();
} else if (ev.getActionCommand().equals(FIRE_SEARCHLIGHT)) {
doSearchlight();
} else if (ev.getActionCommand().equals(FIRE_CLEAR_TURRET)) {
doClearTurret();
} else if (ev.getActionCommand().equals(FIRE_CLEAR_WEAPON)) {
doClearWeaponJam();
}
}
public void updateFlipArms(boolean armsFlipped) {
if (ce() == null) {
return;
}
if (armsFlipped == ce().getArmsFlipped()) {
return;
}
twisting = false;
torsoTwist(null);
clearAttacks();
ce().setArmsFlipped(armsFlipped);
attacks.addElement(new FlipArmsAction(cen, armsFlipped));
updateTarget();
refreshAll();
}
private void updateSearchlight() {
setSearchlightEnabled((ce() != null)
&& (target != null)
&& ce().isUsingSpotlight()
&& ce().getCrew().isActive()
&& SearchlightAttackAction.isPossible(client.game, cen, target,
null) && !((ce() instanceof Tank) && (((Tank) ce()).getStunnedTurns() > 0)) );
}
private void updateClearTurret() {
setFireClearTurretEnabled((ce() instanceof Tank)
&& ((Tank) ce()).isTurretJammed() && (attacks.size() == 0)
&& !(((Tank) ce()).getStunnedTurns() > 0));
}
private void updateClearWeaponJam() {
setFireClearWeaponJamEnabled((ce() instanceof Tank)
&& (((Tank) ce()).getJammedWeapons().size() != 0)
&& (attacks.size() == 0)
&& !(((Tank) ce()).getStunnedTurns() > 0));
}
private void setFireEnabled(boolean enabled) {
butFire.setEnabled(enabled);
clientgui.getMenuBar().setFireFireEnabled(enabled);
}
private void setTwistEnabled(boolean enabled) {
butTwist.setEnabled(enabled);
clientgui.getMenuBar().setFireTwistEnabled(enabled);
}
private void setSkipEnabled(boolean enabled) {
butSkip.setEnabled(enabled);
clientgui.getMenuBar().setFireSkipEnabled(enabled);
}
private void setFindClubEnabled(boolean enabled) {
butFindClub.setEnabled(enabled);
clientgui.getMenuBar().setFireFindClubEnabled(enabled);
}
private void setNextTargetEnabled(boolean enabled) {
butNextTarg.setEnabled(enabled);
clientgui.getMenuBar().setFireNextTargetEnabled(enabled);
}
private void setFlipArmsEnabled(boolean enabled) {
butFlipArms.setEnabled(enabled);
clientgui.getMenuBar().setFireFlipArmsEnabled(enabled);
}
private void setSpotEnabled(boolean enabled) {
butSpot.setEnabled(enabled);
clientgui.getMenuBar().setFireSpotEnabled(enabled);
}
private void setSearchlightEnabled(boolean enabled) {
butSearchlight.setEnabled(enabled);
clientgui.getMenuBar().setFireSearchlightEnabled(enabled);
}
private void setFireModeEnabled(boolean enabled) {
butFireMode.setEnabled(enabled);
clientgui.getMenuBar().setFireModeEnabled(enabled);
}
private void setFireClearTurretEnabled(boolean enabled) {
butFireClearTurret.setEnabled(enabled);
clientgui.getMenuBar().setFireClearTurretEnabled(enabled);
}
private void setFireClearWeaponJamEnabled(boolean enabled) {
butFireClearWeaponJam.setEnabled(enabled);
clientgui.getMenuBar().setFireClearWeaponJamEnabled(enabled);
}
private void setNextEnabled(boolean enabled) {
butNext.setEnabled(enabled);
clientgui.getMenuBar().setFireNextEnabled(enabled);
}
//
// KeyListener
//
public void keyPressed(KeyEvent ev) {
// Are we ignoring events?
if (isIgnoringEvents()) {
return;
}
if (ev.getKeyCode() == KeyEvent.VK_ESCAPE) {
clearAttacks();
clientgui.getBoardView().select(null);
clientgui.getBoardView().cursor(null);
refreshAll();
}
if ((ev.getKeyCode() == KeyEvent.VK_ENTER) && ev.isControlDown()) {
if (client.isMyTurn()) {
//
}
}
if (ev.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
if (client.isMyTurn()) {
removeLastFiring();
}
}
if ((ev.getKeyCode() == KeyEvent.VK_SHIFT) && !shiftheld) {
shiftheld = true;
if (client.isMyTurn()
&& (clientgui.getBoardView().getLastCursor() != null)) {
updateFlipArms(false);
torsoTwist(clientgui.getBoardView().getLastCursor());
}
}
if ((ev.getKeyCode() == KeyEvent.VK_LEFT) && shiftheld) {
updateFlipArms(false);
torsoTwist(0);
}
if ((ev.getKeyCode() == KeyEvent.VK_RIGHT) && shiftheld) {
updateFlipArms(false);
torsoTwist(1);
}
/*
* if (ev.getKeyCode() == KeyEvent.VK_M) { changeMode(); }
*/}
public void keyReleased(KeyEvent ev) {
// Are we ignoring events?
if (isIgnoringEvents()) {
return;
}
if ((ev.getKeyCode() == KeyEvent.VK_SHIFT) && shiftheld) {
shiftheld = false;
}
}
public void keyTyped(KeyEvent ev) {
}
//
// ItemListener
//
public void itemStateChanged(ItemEvent ev) {
// Are we ignoring events?
if (isIgnoringEvents()) {
return;
}
if (ev.getItemSelectable() == clientgui.mechD.wPan.weaponList) {
// update target data in weapon display
updateTarget();
}
}
// board view listener
@Override
public void finishedMovingUnits(BoardViewEvent b) {
// Are we ignoring events?
if (isIgnoringEvents()) {
return;
}
if (client.isMyTurn() && (ce() != null)) {
clientgui.setDisplayVisible(true);
clientgui.bv.centerOnHex(ce().getPosition());
}
}
@Override
public void unitSelected(BoardViewEvent b) {
// Are we ignoring events?
if (isIgnoringEvents()) {
return;
}
Entity e = client.game.getEntity(b.getEntityId());
if (client.isMyTurn()) {
if (client.getMyTurn().isValidEntity(e, client.game)) {
selectEntity(e.getId());
}
} else {
clientgui.setDisplayVisible(true);
clientgui.mechD.displayEntity(e);
if (e.isDeployed()) {
clientgui.bv.centerOnHex(e.getPosition());
}
}
}
private class AimedShotHandler implements ActionListener, ItemListener {
private int aimingAt = Entity.LOC_NONE;
private int aimingMode = IAimingModes.AIM_MODE_NONE;
private int partialCover = LosEffects.COVER_NONE;
private AimedShotDialog asd;
public AimedShotHandler() {
}
public void showDialog() {
if (asd != null) {
int oldAimingMode = aimingMode;
closeDialog();
aimingMode = oldAimingMode;
}
if (inAimingMode()) {
String[] options;
boolean[] enabled;
if (target instanceof Mech) {
if (target instanceof BipedMech) {
options = BipedMech.LOCATION_NAMES;
enabled = createEnabledMask(options.length);
} else {
options = QuadMech.LOCATION_NAMES;
enabled = createEnabledMask(options.length);
}
if (aimingMode == IAimingModes.AIM_MODE_IMMOBILE) {
aimingAt = Mech.LOC_HEAD;
} else if (aimingMode == IAimingModes.AIM_MODE_TARG_COMP) {
aimingAt = Mech.LOC_CT;
}
} else if (target instanceof Tank) {
if (target instanceof LargeSupportTank) {
options = LargeSupportTank.LOCATION_NAMES;
aimingAt = LargeSupportTank.LOC_FRONT;
} else {
options = Tank.LOCATION_NAMES;
aimingAt = Tank.LOC_FRONT;
}
enabled = createEnabledMask(options.length);
} else if (target instanceof GunEmplacement) {
options = GunEmplacement.HIT_LOCATION_NAMES;
enabled = new boolean[] { true,
((GunEmplacement) target).hasTurret() };
aimingAt = GunEmplacement.LOC_BUILDING;
} else if (target instanceof Protomech) {
options = Protomech.LOCATION_NAMES;
enabled = createEnabledMask(options.length);
aimingAt = Protomech.LOC_TORSO;
} else if (target instanceof BattleArmor) {
options = ((BattleArmor) target).getLocationNames();
enabled = createEnabledMask(options.length);
aimingAt = BattleArmor.LOC_TROOPER_1;
} else {
return;
}
asd = new AimedShotDialog(
clientgui.frame,
Messages
.getString("FiringDisplay.AimedShotDialog.title"), //$NON-NLS-1$
Messages
.getString("FiringDisplay.AimedShotDialog.message"), //$NON-NLS-1$
options, enabled, aimingAt, this, this);
asd.setVisible(true);
updateTarget();
}
}
private boolean[] createEnabledMask(int length) {
boolean[] mask = new boolean[length];
for (int i = 0; i < length; i++) {
mask[i] = true;
}
int side = Compute.targetSideTable(ce(), target);
// on a tank, remove turret if its missing
// also, remove body
if (target instanceof Tank) {
mask[Tank.LOC_BODY] = false;
Tank tank = (Tank) target;
if (tank.hasNoTurret()) {
mask[Tank.LOC_TURRET] = false;
}
// remove non-visible sides
if (target instanceof LargeSupportTank) {
if (side == ToHitData.SIDE_FRONTLEFT) {
mask[LargeSupportTank.LOC_FRONTRIGHT] = false;
mask[LargeSupportTank.LOC_REARRIGHT] = false;
mask[LargeSupportTank.LOC_REAR] = false;
}
if (side == ToHitData.SIDE_FRONTRIGHT) {
mask[LargeSupportTank.LOC_FRONTLEFT] = false;
mask[LargeSupportTank.LOC_REARLEFT] = false;
mask[LargeSupportTank.LOC_REAR] = false;
}
if (side == ToHitData.SIDE_REARRIGHT) {
mask[LargeSupportTank.LOC_FRONTLEFT] = false;
mask[LargeSupportTank.LOC_REARLEFT] = false;
mask[LargeSupportTank.LOC_FRONT] = false;
}
if (side == ToHitData.SIDE_REARLEFT) {
mask[LargeSupportTank.LOC_REARRIGHT] = false;
mask[LargeSupportTank.LOC_FRONTRIGHT] = false;
mask[LargeSupportTank.LOC_FRONT] = false;
}
} else {
if (side == ToHitData.SIDE_LEFT) {
mask[Tank.LOC_RIGHT] = false;
}
if (side == ToHitData.SIDE_RIGHT) {
mask[Tank.LOC_LEFT] = false;
}
if (side == ToHitData.SIDE_REAR) {
mask[Tank.LOC_FRONT] = false;
}
if (side == ToHitData.SIDE_FRONT) {
mask[Tank.LOC_REAR] = false;
}
}
}
// remove main gun on protos that don't have one
if (target instanceof Protomech) {
if (!((Protomech) target).hasMainGun()) {
mask[Protomech.LOC_MAINGUN] = false;
}
}
// remove squad location on BAs
// also remove dead/non-existant troopers
if (target instanceof BattleArmor) {
mask[BattleArmor.LOC_SQUAD] = false;
}
if (target instanceof Mech) {
// remove locations hidden by partial cover
if (side == ToHitData.SIDE_FRONT) {
if ((partialCover & LosEffects.COVER_LOWLEFT) != 0) {
mask[Mech.LOC_RLEG] = false;
}
if ((partialCover & LosEffects.COVER_LOWRIGHT) != 0) {
mask[Mech.LOC_LLEG] = false;
}
if ((partialCover & LosEffects.COVER_LEFT) != 0) {
mask[Mech.LOC_RARM] = false;
mask[Mech.LOC_RT] = false;
}
if ((partialCover & LosEffects.COVER_RIGHT) != 0) {
mask[Mech.LOC_LARM] = false;
mask[Mech.LOC_LT] = false;
}
} else {
if ((partialCover & LosEffects.COVER_LOWLEFT) != 0) {
mask[Mech.LOC_LLEG] = false;
}
if ((partialCover & LosEffects.COVER_LOWRIGHT) != 0) {
mask[Mech.LOC_RLEG] = false;
}
if ((partialCover & LosEffects.COVER_LEFT) != 0) {
mask[Mech.LOC_LARM] = false;
mask[Mech.LOC_LT] = false;
}
if ((partialCover & LosEffects.COVER_RIGHT) != 0) {
mask[Mech.LOC_RARM] = false;
mask[Mech.LOC_RT] = false;
}
}
if (aimingMode == IAimingModes.AIM_MODE_TARG_COMP) {
// Can't target head withtargeting computer.
mask[Mech.LOC_HEAD] = false;
}
}
return mask;
}
public void closeDialog() {
if (asd != null) {
aimingAt = Entity.LOC_NONE;
aimingMode = IAimingModes.AIM_MODE_NONE;
asd.setVisible(false);
asd = null;
updateTarget();
}
}
/**
* Enables the radiobuttons in the dialog.
*/
public void setEnableAll(boolean enableAll) {
if (asd != null) {
asd.setEnableAll(enableAll);
}
}
public void setPartialCover(int partialCover) {
this.partialCover = partialCover;
}
public int getAimingAt() {
return aimingAt;
}
public int getAimingMode() {
return aimingMode;
}
/**
* Returns the name of aimed location.
*/
public String getAimingLocation() {
if ((target != null) && (aimingAt != Entity.LOC_NONE)
&& (aimingMode != IAimingModes.AIM_MODE_NONE)) {
if (target instanceof BipedMech) {
return BipedMech.LOCATION_NAMES[aimingAt];
} else if (target instanceof BipedMech) {
return QuadMech.LOCATION_NAMES[aimingAt];
} else if (target instanceof GunEmplacement) {
return GunEmplacement.HIT_LOCATION_NAMES[aimingAt];
}
}
return null;
}
/**
* Sets the aiming mode, depending on the target and the attacker.
* Against immobile mechs, targeting computer aiming mode will be used
* if turned on. (This is a hack, but it's the resolution suggested by
* the bug submitter, and I don't think it's half bad.
*/
public void setAimingMode() {
boolean allowAim;
// TC against a mech
allowAim = ((target != null) && (ce() != null) && ce().hasAimModeTargComp() && ((target instanceof Mech)
|| (target instanceof Tank) || (target instanceof BattleArmor) || (target instanceof Protomech)));
if (allowAim) {
aimingMode = IAimingModes.AIM_MODE_TARG_COMP;
return;
}
// immobile mech or gun emplacement
allowAim = ((target != null) && ((target.isImmobile() && ((target instanceof Mech) || (target instanceof Tank))) || (target instanceof GunEmplacement)));
if (allowAim) {
aimingMode = IAimingModes.AIM_MODE_IMMOBILE;
return;
}
aimingMode = IAimingModes.AIM_MODE_NONE;
}
/**
* If in aiming mode.
*/
public boolean inAimingMode() {
return aimingMode != IAimingModes.AIM_MODE_NONE;
}
/**
* If a hit location is currently selected.
*/
public boolean isAimingAtLocation() {
return aimingAt != Entity.LOC_NONE;
}
/**
* Determines if a certain weapon may aimed at a specific hit location.
*/
public boolean allowAimedShotWith(Mounted weapon) {
WeaponType wtype = (WeaponType) weapon.getType();
boolean isWeaponInfantry = wtype.hasFlag(WeaponType.F_INFANTRY);
boolean usesAmmo = (wtype.getAmmoType() != AmmoType.T_NA)
&& !isWeaponInfantry;
Mounted ammo = usesAmmo ? weapon.getLinked() : null;
AmmoType atype = ammo == null ? null : (AmmoType) ammo.getType();
// Leg and swarm attacks can't be aimed.
if ((wtype.getInternalName() == Infantry.LEG_ATTACK)
|| (wtype.getInternalName() == Infantry.SWARM_MEK)) {
return false;
}
switch (aimingMode) {
case (IAimingModes.AIM_MODE_NONE):
return false;
case (IAimingModes.AIM_MODE_IMMOBILE):
if (atype != null) {
switch (atype.getAmmoType()) {
case AmmoType.T_SRM_STREAK:
case AmmoType.T_LRM_STREAK:
case AmmoType.T_LRM:
case AmmoType.T_LRM_TORPEDO:
case AmmoType.T_SRM:
case AmmoType.T_SRM_TORPEDO:
case AmmoType.T_MRM:
case AmmoType.T_NARC:
case AmmoType.T_AMS:
case AmmoType.T_ARROW_IV:
case AmmoType.T_LONG_TOM:
case AmmoType.T_SNIPER:
case AmmoType.T_THUMPER:
case AmmoType.T_SRM_ADVANCED:
case AmmoType.T_LRM_TORPEDO_COMBO:
case AmmoType.T_ATM:
case AmmoType.T_MML:
case AmmoType.T_EXLRM:
case AmmoType.T_TBOLT_5:
case AmmoType.T_TBOLT_10:
case AmmoType.T_TBOLT_15:
case AmmoType.T_TBOLT_20:
case AmmoType.T_PXLRM:
case AmmoType.T_HSRM:
case AmmoType.T_MRM_STREAK:
return false;
}
if (((atype.getAmmoType() == AmmoType.T_AC_LBX) || (atype
.getAmmoType() == AmmoType.T_AC_LBX_THB))
&& (atype.getMunitionType() == AmmoType.M_CLUSTER)) {
return false;
}
}
break;
case (IAimingModes.AIM_MODE_TARG_COMP):
if (!wtype.hasFlag(WeaponType.F_DIRECT_FIRE)
|| wtype.hasFlag(WeaponType.F_PULSE)) {
return false;
}
if (weapon.getCurrentShots() > 1) {
return false;
}
if ((wtype.hasFlag(WeaponType.F_NO_AIM))
|| ((atype != null)
&& ((atype.getAmmoType() == AmmoType.T_AC_LBX) || (atype
.getAmmoType() == AmmoType.T_AC_LBX_THB)) && (atype
.getMunitionType() == AmmoType.M_CLUSTER))) {
return false;
}
break;
}
return true;
}
/**
* ActionListener, listens to the button in the dialog.
*/
public void actionPerformed(ActionEvent ev) {
closeDialog();
}
/**
* ItemListener, listens to the radiobuttons in the dialog.
*/
public void itemStateChanged(ItemEvent ev) {
IndexedCheckbox icb = (IndexedCheckbox) ev.getSource();
aimingAt = icb.getIndex();
updateTarget();
}
}
/**
* Retrieve the "Done" button of this object.
*
* @return the <code>java.awt.Button</code> that activates this object's
* "Done" action.
*/
public Button getDoneButton() {
return butDone;
}
/**
* Stop just ignoring events and actually stop listening to them.
*/
public void removeAllListeners() {
client.game.removeGameListener(this);
clientgui.getBoardView().removeBoardViewListener(this);
clientgui.mechD.wPan.weaponList.removeItemListener(this);
}
/**
* Have the player select a target from the entities at the given coords.
*
* @param pos -
* the <code>Coords</code> containing targets.
*/
private Targetable chooseTarget(Coords pos) {
boolean friendlyFire = client.game.getOptions().booleanOption(
"friendly_fire"); //$NON-NLS-1$
// Assume that we have *no* choice.
Targetable choice = null;
Enumeration<Entity> choices = null;
// Get the available choices, depending on friendly fire
if (friendlyFire) {
choices = client.game.getEntities(pos);
} else {
choices = client.game.getEnemyEntities(pos, ce());
}
// Convert the choices into a List of targets.
Vector<Targetable> targets = new Vector<Targetable>();
while (choices.hasMoreElements()) {
choice = choices.nextElement();
if (!ce().equals(choice)) {
targets.addElement(choice);
}
}
// Is there a building in the hex?
Building bldg = client.game.getBoard().getBuildingAt(pos);
if (bldg != null) {
targets.addElement(new BuildingTarget(pos, client.game.getBoard(),
false));
}
// Do we have a single choice?
if (targets.size() == 1) {
// Return that choice.
choice = targets.elementAt(0);
}
// If we have multiple choices, display a selection dialog.
else if (targets.size() > 1) {
String[] names = new String[targets.size()];
for (int loop = 0; loop < names.length; loop++) {
names[loop] = targets.elementAt(loop).getDisplayName();
}
SingleChoiceDialog choiceDialog = new SingleChoiceDialog(
clientgui.frame,
Messages
.getString("FiringDisplay.ChooseTargetDialog.title"), //$NON-NLS-1$
Messages
.getString(
"FiringDisplay.ChooseTargetDialog.message", new Object[] { pos.getBoardNum() }), //$NON-NLS-1$
names);
choiceDialog.setVisible(true);
if (choiceDialog.getAnswer() == true) {
choice = targets.elementAt(choiceDialog.getChoice());
}
} // End have-choices
// Return the chosen unit.
return choice;
} // End private Targetable chooseTarget( Coords )
}