package net.sf.colossus.gui;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import net.sf.colossus.guiutil.KDialog;
import net.sf.colossus.variant.BattleHex;
import net.sf.colossus.variant.CreatureType;
import net.sf.colossus.variant.HazardConstants;
import net.sf.colossus.variant.HazardHexside;
import net.sf.colossus.variant.HazardTerrain;
import net.sf.colossus.variant.Hazards;
import net.sf.colossus.variant.MasterHex;
import net.sf.colossus.variant.Variant;
/**
* Class BattleTerrainHazardWindow shows a GUI representation of the
* Hazard Chart
* This is still ALPHA.
* @version $Id: BattleTerrainHazardWindow.java 2975 2008-01-06 10:34:55Z peterbecker $
* @author Dranathi
*/
public class BattleTerrainHazardWindow extends KDialog
{
private static final int HEX_SIZE = 15;
private static final int EFFECT_SIZE = 20;
private static final int CREATURE_SIZE = 30;
private static final int STRIKE_SIZE = 15;
private final static GridBagConstraints GBC_DEFAULT = new GridBagConstraints();
static
{
GBC_DEFAULT.anchor = GridBagConstraints.NORTH;
GBC_DEFAULT.insets = new Insets(2, 2, 5, 2);
GBC_DEFAULT.weightx = 0.01;
}
private final static GridBagConstraints GBC_NORTHWEST = (GridBagConstraints)GBC_DEFAULT
.clone();
static
{
GBC_NORTHWEST.anchor = GridBagConstraints.NORTHWEST;
}
private final static GridBagConstraints GBC_NORTHEAST = (GridBagConstraints)GBC_DEFAULT
.clone();
static
{
GBC_NORTHEAST.anchor = GridBagConstraints.NORTHEAST;
}
private final MasterHex hex;
private final Variant variant;
private final SortedSet<CreatureType> creatures;
private Map<String, HazardTerrain> hazardsDisplayed;
private Map<String, HazardHexside> hexsidesDisplayed;
public BattleTerrainHazardWindow(JFrame frame, ClientGUI gui, MasterHex hex)
{
super(frame, "Battle Terrain Hazards for "
+ hex.getTerrain().getDisplayName(), false);
assert SwingUtilities.isEventDispatchThread() : "Constructor should be called only on the EDT";
this.hex = hex;
variant = gui.getGame().getVariant();
creatures = variant.getCreatureTypes();
getContentPane().setLayout(new GridBagLayout());
useSaveWindow(gui.getOptions(), "BattleTerrainHazard", null);
addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
dispose();
}
});
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setupHeader(getContentPane());
setupChart(getContentPane());
pack();
setVisible(true);
}
private void setupHeader(Container container)
{
// to save extra indirections with subpanels some labels will be across two columns
GridBagConstraints dblConstraints = (GridBagConstraints)GBC_DEFAULT
.clone();
dblConstraints.gridwidth = 2;
// add headers
container.add(new JPanel(), GBC_DEFAULT);
container.add(new JLabel("Hex"), GBC_DEFAULT);
container.add(new JLabel("Move"), dblConstraints);
container.add(new JLabel("Natives"), GBC_DEFAULT);
container.add(new JLabel("Strike"), dblConstraints);
container.add(new JLabel("Defence"), dblConstraints);
container.add(new JLabel("Special"), dblConstraints);
// add an empty cell to finalize the row and eat extra space
GridBagConstraints constraints = (GridBagConstraints)GBC_DEFAULT
.clone();
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.weightx = 1;
container.add(new JPanel(), constraints);
}
private void setupChart(Container container)
{
hazardsDisplayed = new HashMap<String, HazardTerrain>();
hexsidesDisplayed = new HashMap<String, HazardHexside>();
for (HazardTerrain hazard : HazardTerrain.getAllHazardTerrains())
{
if (hazardsDisplayed.containsKey(hazard.getName())
|| hex.getTerrain().getHazardCount(hazard) == 0)
{
// Ignore
}
else
{
hazardsDisplayed.put(hazard.getName(), hazard);
addHazard(container, hazard);
}
}
for (HazardHexside hazard : HazardHexside.getAllHazardHexsides())
{
if ("nothing".equalsIgnoreCase(hazard.getName())
|| hexsidesDisplayed.containsKey(hazard.getName())
|| hex.getTerrain().getHazardHexsideCount(hazard) == 0)
{
// Ignore
}
else
{
hexsidesDisplayed.put(hazard.getName(), hazard);
addHazard(container, hazard);
}
}
// add an empty row that can grow with spare vSpace
GridBagConstraints vFillConstraints = new GridBagConstraints();
vFillConstraints.gridx = 0;
vFillConstraints.gridwidth = GridBagConstraints.REMAINDER;
vFillConstraints.weighty = 1;
container.add(new JPanel(), vFillConstraints);
// add a row for info text
// TODO should be done better, this is just as first aid....
GridBagConstraints vFillConstraints2 = new GridBagConstraints();
vFillConstraints2.gridx = 0;
vFillConstraints2.gridwidth = GridBagConstraints.REMAINDER;
vFillConstraints2.anchor = GridBagConstraints.SOUTHWEST;
vFillConstraints2.weighty = 1;
JPanel textPanel = new JPanel();
JLabel label = new JLabel(
"Hold the mouse over a symbol for an explanatory popup text.");
textPanel.add(label);
container.add(textPanel, vFillConstraints);
}
private void addHazard(Container container, Hazards hazard)
{
// hex label is always first in row, aligned vCenter but left
GridBagConstraints hexLabelConstraints = (GridBagConstraints)GBC_DEFAULT
.clone();
hexLabelConstraints.gridx = 0;
hexLabelConstraints.anchor = GridBagConstraints.NORTHWEST;
container.add(new JLabel(hazard.getName()), hexLabelConstraints);
addHexImage(container, hazard);
addMovementInfo(container, hazard);
addNativesPanel(container, hazard);
addStrikeInfo(container, hazard);
addDefenderInfo(container, hazard);
addSpecialInfo(container, hazard);
// add an empty cell to finalize the row and eat extra space
GridBagConstraints constraints = new GridBagConstraints();
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.weightx = 1;
container.add(new JPanel(), constraints);
}
// Create GUI representation of Terrain
private void addHexImage(Container container, Hazards hazard)
{
GUIBattleHex hex = new GUIBattleHex(HEX_SIZE, 0, HEX_SIZE, container,
0, 0);
BattleHex model = hex.getHexModel();
if (hazard instanceof HazardTerrain)
{
model.setTerrain((HazardTerrain)hazard);
}
else
{
model.setTerrain(HazardTerrain.getDefaultTerrain());
// to see the hexsides (or at least most of them) we have to configure
// them on the neighbors
// TODO top is broken, three are still missing
// TODO for a full drawn one we would have to draw two hexes at least
// it might be easier (and better looking) to just draw the hexside
GUIBattleHex neighborTop = new GUIBattleHex(HEX_SIZE, -4
* HEX_SIZE, HEX_SIZE, container, 0, 0);
configureHexModel((HazardHexside)hazard, neighborTop.getHexModel());
hex.setNeighbor(0, neighborTop);
GUIBattleHex neighborTopRight = new GUIBattleHex(4 * HEX_SIZE, -2
* HEX_SIZE, HEX_SIZE, container, 0, 0);
configureHexModel((HazardHexside)hazard,
neighborTopRight.getHexModel());
hex.setNeighbor(1, neighborTopRight);
GUIBattleHex neighborBottomRight = new GUIBattleHex(4 * HEX_SIZE,
2 * HEX_SIZE, HEX_SIZE, container, 0, 0);
configureHexModel((HazardHexside)hazard,
neighborBottomRight.getHexModel());
hex.setNeighbor(2, neighborBottomRight);
}
hex.setHexModel(model);
// we give this one some extra space around it
GridBagConstraints constraints = (GridBagConstraints)GBC_DEFAULT
.clone();
constraints.insets = new Insets(5, 5, 5, 5);
container.add(hex, constraints);
}
private void configureHexModel(HazardHexside hazard, BattleHex model)
{
model.setTerrain(HazardTerrain.getDefaultTerrain());
for (int i = 0; i <= 5; i++)
{
model.setHexsideHazard(i, hazard);
}
}
// Show Native critters;
private void addNativesPanel(Container container, Hazards hazard)
{
JPanel nativePanel = new JPanel(new GridLayout(0, 6));
for (CreatureType creature : creatures)
{
if (hazard instanceof HazardTerrain)
{
if (creature.isNativeIn((HazardTerrain)hazard))
{
Chit chit = Chit.newCreatureChit(CREATURE_SIZE, creature);
chit.setToolTipText(creature.getName());
nativePanel.add(chit);
}
}
else
{
if ((hazard.equals(HazardHexside.DUNE) && creature
.isNativeDune())
|| (hazard.equals(HazardHexside.SLOPE) && creature
.isNativeSlope())
|| (hazard.equals(HazardHexside.RIVER) && creature
.isNativeRiver()))
{
Chit chit = Chit.newCreatureChit(CREATURE_SIZE, creature);
chit.setToolTipText(creature.getName());
nativePanel.add(chit);
}
}
}
container.add(nativePanel, GBC_DEFAULT);
}
// Effect on Movement
private void addMovementInfo(Container container, Hazards hazard)
{
Chit flySymbol = null;
if (hazard.effectOnFlyerMovement
.equals(HazardConstants.EffectOnMovement.BLOCKALL))
{
flySymbol = Chit.newSymbolChit(EFFECT_SIZE, "FlyingBlocked");
flySymbol.setToolTipText("FlyingBlocked");
}
else if (hazard.effectOnFlyerMovement
.equals(HazardConstants.EffectOnMovement.BLOCKFOREIGNER))
{
flySymbol = Chit.newSymbolChit(EFFECT_SIZE, "FlyingNativeOnly");
flySymbol.setToolTipText("Native Flyers Only");
}
else if (hazard.effectOnFlyerMovement
.equals(HazardConstants.EffectOnMovement.SLOWALL))
{
flySymbol = Chit.newSymbolChit(EFFECT_SIZE, "FlyingSlow");
flySymbol.setToolTipText("Slows Flying Creatures");
}
else if (hazard.effectOnFlyerMovement
.equals(HazardConstants.EffectOnMovement.SLOWFOREIGNER))
{
flySymbol = Chit.newSymbolChit(EFFECT_SIZE, "FlyingNativeSlow");
flySymbol.setToolTipText("Slows Non-Native Flying Creatures");
}
else
{
flySymbol = Chit.newSymbolChit(EFFECT_SIZE, "FlyingAll");
flySymbol.setToolTipText("No effect on Flying Creatures");
}
container.add(flySymbol, GBC_NORTHEAST);
Chit groundSymbol = null;
if (hazard.effectOnGroundMovement
.equals(HazardConstants.EffectOnMovement.BLOCKALL))
{
groundSymbol = Chit.newSymbolChit(EFFECT_SIZE, "GroundBlocked");
groundSymbol.setToolTipText("Blocks Ground Movement");
}
else if (hazard.effectOnGroundMovement
.equals(HazardConstants.EffectOnMovement.BLOCKFOREIGNER))
{
groundSymbol = Chit.newSymbolChit(EFFECT_SIZE, "GroundNativeOnly");
groundSymbol.setToolTipText("Only Natives may Occupy");
}
else if (hazard.effectOnGroundMovement
.equals(HazardConstants.EffectOnMovement.SLOWFOREIGNER))
{
groundSymbol = Chit.newSymbolChit(EFFECT_SIZE, "GroundNativeSlow");
groundSymbol.setToolTipText("NonNatives Slowed");
}
else if (hazard.effectOnGroundMovement
.equals(HazardConstants.EffectOnMovement.SLOWALL))
{
groundSymbol = Chit.newSymbolChit(EFFECT_SIZE, "GroundAllSlow");
groundSymbol.setToolTipText("Slows Ground Movement");
}
else
{
groundSymbol = Chit.newSymbolChit(EFFECT_SIZE, "GroundAll");
groundSymbol.setToolTipText("No Effect On Ground Movement");
}
container.add(groundSymbol, GBC_NORTHWEST);
}
private void addSpecialInfo(Container container, Hazards hazard)
{
Chit rangeStrikeSymbol;
if (hazard.rangeStrikeSpecial
.equals(HazardConstants.RangeStrikeSpecialEffect.RANGESTRIKEBLOCKED))
{
rangeStrikeSymbol = Chit.newSymbolChit(EFFECT_SIZE,
"RangeStrikeBlocked");
rangeStrikeSymbol
.setToolTipText("Blocks normal Rangestrikes-Magic is not blocked");
}
else if (hazard.rangeStrikeSpecial
.equals(HazardConstants.RangeStrikeSpecialEffect.RANGESTRIKEWALL))
{
rangeStrikeSymbol = Chit.newSymbolChit(EFFECT_SIZE,
"RangeStrikeWall");
rangeStrikeSymbol
.setToolTipText("Blocks Rangestrikes unless Hazard is"
+ "occupied by either the Rangestriker or the target.");
}
else if (hazard.rangeStrikeSpecial
.equals(HazardConstants.RangeStrikeSpecialEffect.RANGESTRIKESKILLPENALTY))
{
rangeStrikeSymbol = Chit.newSymbolChit(EFFECT_SIZE,
"RangeStrikeSkill");
rangeStrikeSymbol
.setToolTipText("Non Natives to this Hazard sill lose 1 Skill"
+ "for each hazard of this type being crossed.");
}
else
{
rangeStrikeSymbol = Chit.newSymbolChit(EFFECT_SIZE,
"RangeStrikeFree");
rangeStrikeSymbol.setToolTipText("No effect on Rangestrikes.");
}
container.add(rangeStrikeSymbol);
Chit special = null;
if (hazard.terrainSpecial
.equals(HazardConstants.SpecialEffect.HEALTHDRAIN))
{
special = Chit.newSymbolChit(EFFECT_SIZE, "HealthDrain");
special
.setToolTipText("Non natives suffer 1 damage per strike phase");
}
if (hazard.terrainSpecial
.equals(HazardConstants.SpecialEffect.HEALTHDRAIN_WATERDWELLER))
{
special = Chit.newSymbolChit(EFFECT_SIZE, "HealthDrain");
special.setToolTipText("Water Dweller lose 1 health per turn");
}
if (hazard.terrainSpecial
.equals(HazardConstants.SpecialEffect.HEALTHGAIN))
{
special = new Chit(EFFECT_SIZE, "HealthGain");
special
.setToolTipText("Heals 1 damage per strike phase. Cures slow and poison");
}
if (hazard.terrainSpecial
.equals(HazardConstants.SpecialEffect.PERMSLOW))
{
special = new Chit(EFFECT_SIZE, "PermanentSlow");
special
.setToolTipText("Creatures are slowed 1 per turn for rest of the battle");
}
if (special != null)
container.add(special);
}
private void addDefenderInfo(Container container, Hazards hazard)
{
container.add(makeStrikeEffect("Defending", hazard.defenseEffect),
GBC_NORTHEAST);
container.add(
makeStrikeEffect("Being Rangestruck", hazard.rangedDefenseEffect),
GBC_NORTHWEST);
}
private void addStrikeInfo(Container container, Hazards hazard)
{
container.add(makeStrikeEffect("Attacking", hazard.attackEffect),
GBC_NORTHEAST);
container.add(
makeStrikeEffect("Rangestriking", hazard.rangedAttackEffect),
GBC_NORTHWEST);
}
private Chit makeStrikeEffect(String strike, Hazards.CombatEffect e)
{
String[] overlay;
if ("Being Rangestruck".equals(strike)
|| "Rangestriking".equals(strike))
{
overlay = new String[1];
overlay[0] = "RangestrikeBase";
}
else
{
overlay = null;
}
Chit strikeSymbol;
if (e.effect.equals(HazardConstants.EffectOnStrike.BLOCKED))
{
strikeSymbol = new Chit(STRIKE_SIZE, "StrikeBlocked", overlay);
strikeSymbol.setToolTipText(strike
+ " Across Hazard is not Possible");
}
else if (e.effect.equals(HazardConstants.EffectOnStrike.SKILLBONUS)
|| e.effect.equals(HazardConstants.EffectOnStrike.SKILLPENALTY))
{
if (e.effect.equals(HazardConstants.EffectOnStrike.SKILLPENALTY))
{
strikeSymbol = new StrikeDie(STRIKE_SIZE, e.adjustment,
"Miss", overlay);
}
else
{
strikeSymbol = new StrikeDie(STRIKE_SIZE, e.adjustment, "Hit",
overlay);
}
StringBuilder tip = new StringBuilder();
if (e.scope
.equals(HazardConstants.ScopeOfEffectOnStrike.FOREIGNERS)
|| e.scope
.equals(HazardConstants.ScopeOfEffectOnStrike.IMPERIALS))
{
tip.append("Non-Natives ");
}
else if (e.scope
.equals(HazardConstants.ScopeOfEffectOnStrike.NATIVES)
|| e.scope
.equals(HazardConstants.ScopeOfEffectOnStrike.PATRIOTS))
{
tip.append("Natives ");
}
else
{
tip.append("Everyone ");
}
tip.append("have skill ");
if (e.effect.equals(HazardConstants.EffectOnStrike.SKILLPENALTY))
{
tip.append("decreased by ");
}
else
{
tip.append("increased by ");
}
tip.append(e.adjustment);
tip.append(" when " + strike);
if (e.scope
.equals(HazardConstants.ScopeOfEffectOnStrike.IMPERIALS))
{
tip.append("Natives");
}
else if (e.scope
.equals(HazardConstants.ScopeOfEffectOnStrike.PATRIOTS))
{
tip.append("Non-Natives");
}
strikeSymbol.setToolTipText(tip.toString());
}
else if (e.effect.equals(HazardConstants.EffectOnStrike.POWERBONUS)
|| e.effect.equals(HazardConstants.EffectOnStrike.POWERPENALTY))
{
strikeSymbol = new StrikeDie(STRIKE_SIZE, 1, "RedBlue", overlay);
StringBuilder tip = new StringBuilder();
if (e.scope
.equals(HazardConstants.ScopeOfEffectOnStrike.FOREIGNERS)
|| e.scope
.equals(HazardConstants.ScopeOfEffectOnStrike.IMPERIALS))
{
tip.append("Non-Natives ");
}
else if (e.scope
.equals(HazardConstants.ScopeOfEffectOnStrike.NATIVES)
|| e.scope
.equals(HazardConstants.ScopeOfEffectOnStrike.PATRIOTS))
{
tip.append("Natives ");
}
else
{
tip.append("Everyone ");
}
if (e.effect.equals(HazardConstants.EffectOnStrike.POWERPENALTY))
{
tip.append("loses ");
}
else
{
tip.append("gains ");
}
tip.append(e.adjustment);
tip.append(" dice when " + strike);
if (e.scope
.equals(HazardConstants.ScopeOfEffectOnStrike.IMPERIALS))
{
tip.append("Natives");
}
else if (e.scope
.equals(HazardConstants.ScopeOfEffectOnStrike.PATRIOTS))
{
tip.append("Non-Natives");
}
strikeSymbol.setToolTipText(tip.toString());
}
else
{
strikeSymbol = new StrikeDie(STRIKE_SIZE, 0, "RedBlue", overlay);
strikeSymbol.setToolTipText("Normal Strike");
}
return strikeSymbol;
}
}