package net.sf.colossus.game; import java.util.HashSet; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import net.sf.colossus.variant.BattleHex; import net.sf.colossus.variant.CreatureType; /** * Class BattleUnit represents a Creature in a specific Battle. * GUI aspects moved to new Class GUIBattleChit. * * TODO This should at some point extend Creature, or perhaps Creature can * take care of all so no extend is needed; but right now, Creature handles * some things (e.g. how to change the hexes) differently than how it's done * here, so can not "just delegate it" - needs investigation and checking. * * @author David Ripton * @author Clemens Katzer (strip GUI issues out, to own new Class) */ @SuppressWarnings("serial") public final class BattleUnit implements BattleCritter { private static final Logger LOGGER = Logger.getLogger(BattleUnit.class .getName()); private final int tag; private final String id; private final boolean defender; private final CreatureType creatureType; private final Legion legion; private int hits = 0; private int poisonDamage = 0; private int poison = 0; private int slows = 0; private int slowed = 0; private BattleHex currentHex; private BattleHex startingHex; private boolean moved; private boolean struck; private boolean dead; /** Listeners to be informed when something changes, e.g. right now only * GUIBattleChit that needs to repaint if dead or hits change. */ private final Set<Listener> listeners = new HashSet<Listener>(); public BattleUnit(String id, boolean defender, int tag, BattleHex currentHex, CreatureType type, Legion legion) { if (id == null) { LOGGER.log(Level.WARNING, "Created BattleUnit with null id!"); } this.tag = tag; this.id = id; this.defender = defender; this.currentHex = currentHex; this.creatureType = type; this.legion = legion; } public Legion getLegion() { return legion; } public int getTag() { return tag; } public int getHits() { return hits; } public void setHits(int hits) { this.hits = hits; notifyListeners(); } public void setPoison(int damage) { this.poison = damage; } public void setPoisonDamage(int damage) { this.poisonDamage = damage; } public void addPoisonDamage(int damage) { // Poison damage is cumulative, so add to existing value this.poisonDamage += damage; } public void setSlowed(int slowValue) { this.slowed = slowValue; } public void addSlowed(int slowValue) { // Slowing is cumulative, so add to existing value this.slowed += slowValue; } public void setSlows(int slowValue) { this.slows = slowValue; } public boolean wouldDieFrom(int hits) { // TODO what if critter / creature is already dead anyway? return (hits + getHits() >= getPower()); } public void setDead(boolean dead) { this.dead = dead; if (dead) { // TODO is this "if dead, set hits to 0" still needed? // Probably originated from the GUI issue, don't paint a damage nr // on a dead chit, but nowadays GUIBattleChit takes care of that // by itself (I believe). setHits(0); // setHits() calls notifyListeners } else { // otherwise we need to call here notifyListeners(); } } public boolean isDead() { return dead; } public BattleHex getCurrentHex() { return currentHex; } public BattleHex getStartingHex() { return startingHex; } public void setCurrentHex(BattleHex hex) { this.currentHex = hex; } public void moveToHex(BattleHex hex) { startingHex = currentHex; currentHex = hex; } // TODO make package private public boolean hasMoved() { return moved; } // TODO make package private public void setMoved(boolean moved) { this.moved = moved; } public boolean hasStruck() { return struck; } public void setStruck(boolean struck) { this.struck = struck; } public CreatureType getType() { return creatureType; } public boolean isDefender() { return defender; } public String getId() { return id; } public boolean isTitan() { return getType().isTitan(); } public boolean isLord() { return getType().isLord(); } public boolean isDemiLord() { return getType().isDemiLord(); } public int getPower() { if (isTitan()) { return getTitanPower(); } else { return getType().getPower(); } } // TODO copied from Chit - replace or let Creature deal with it // TODO this validation against "via legion and player" (that's how // Creature.java is doing it) is only temporary; on the long run get // rid of the parsing based method. public int getTitanPower() { int parsedPower = getIdBasedTitanPower(); int playerBasedPower = getTitanPowerViaLegionAndPlayer(); if (playerBasedPower != parsedPower) { LOGGER.warning("id/parsing based power is " + parsedPower + ", but Power via Legion and Player is " + playerBasedPower); } return playerBasedPower; } public int getIdBasedTitanPower() { if (!id.startsWith("Titan-")) { LOGGER.warning("Asked Titan Power from non-Titan BattleUnit '" + getType() + "'!"); return -1; } String[] parts = id.split("-"); return Integer.parseInt(parts[1]); } public int getTitanPowerViaLegionAndPlayer() { Player player = legion.getPlayer(); if (player != null) { return player.getTitanPower(); } else { // Just in case player is dead. LOGGER.warning("asked for Titan power of dead (null) player!"); return 6; } } public int getSkill() { return getType().getSkill(); } public int getPointValue() { return getPower() * getSkill(); } public int getPoison() { return poison; } public int getPoisonDamage() { return poisonDamage; } public int getSlowed() { return slowed; } public int getSlows() { return slows; } public boolean isRangestriker() { return getType().isRangestriker(); } public boolean useMagicMissile() { return getType().useMagicMissile(); } // TODO does this give plain Titan name or user specific one? public String getDescription() { return getType().getName() + " in " + getCurrentHex().getLabel(); } @Override public String toString() { return getDescription(); } /** * Listeners who needs to be notified if (currently) hits or dead values * change, to trigger repaint: a GUIBattleChit representing this creature */ abstract public class Listener { abstract public void actOnHitOrDeadChanged(); } public void addListener(Listener listener) { listeners.add(listener); } public void removeListener(Listener listener) { listeners.remove(listener); } public void notifyListeners() { for (Listener listener : listeners) { listener.actOnHitOrDeadChanged(); } } }