diff options
Diffstat (limited to 'core/src/ch/asynk/creepingarmor/game')
42 files changed, 6106 insertions, 0 deletions
diff --git a/core/src/ch/asynk/creepingarmor/game/Army.java b/core/src/ch/asynk/creepingarmor/game/Army.java new file mode 100644 index 0000000..dbf5656 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/Army.java @@ -0,0 +1,30 @@ +package ch.asynk.creepingarmor.game; + +import ch.asynk.creepingarmor.engine.Faction; + +public enum Army implements Faction +{ + NONE("None"), + GE("German"), + US("US"), + USSR("Soviet"), + EN("English"); + + private String s; + + Army(String s) { + this.s = s; + } + + @Override + public String toString() + { + return s; + } + + @Override + public boolean isEnemy(Faction other) + { + return (this != other); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/Battle.java b/core/src/ch/asynk/creepingarmor/game/Battle.java new file mode 100644 index 0000000..9a4a0c3 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/Battle.java @@ -0,0 +1,40 @@ +package ch.asynk.creepingarmor.game; + +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Player; +import ch.asynk.creepingarmor.game.battles.Factory.MapType; +import ch.asynk.creepingarmor.ui.Position; + +public interface Battle +{ + public void init(); + + public String getName(); + + public String getDescription(); + + public Player getPlayer(); + + public Player opponent(Player player); + + public MapType getMapType(); + + public Map getMap(); + + public Player checkVictory(Ctrl ctrl); + + public boolean getReinforcement(Ctrl ctrl, Map map); + + public Zone getEntryZone(Unit unit); + + public Zone getExitZone(Unit unit); + + public Position getHudPosition(Player player); + + public State.StateType getState(Player player); + + public boolean deploymentDone(Player player); + + public void setup(Ctrl ctrl, Map map); +} diff --git a/core/src/ch/asynk/creepingarmor/game/Command.java b/core/src/ch/asynk/creepingarmor/game/Command.java new file mode 100644 index 0000000..34af71b --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/Command.java @@ -0,0 +1,225 @@ +package ch.asynk.creepingarmor.game; + +import java.util.List; + +import com.badlogic.gdx.utils.Pool; +import com.badlogic.gdx.utils.Json; +import com.badlogic.gdx.utils.JsonValue; + +import ch.asynk.creepingarmor.engine.Order; +import ch.asynk.creepingarmor.engine.Move; +import ch.asynk.creepingarmor.engine.Pawn; +import ch.asynk.creepingarmor.engine.Tile; + +public class Command extends Order +{ + public enum CommandType implements Order.OrderType + { + NONE, + MOVE, + ENGAGE, + PROMOTE, + END_OF_TURN; + } + + private static final Pool<Command> commandPool = new Pool<Command>() + { + @Override + protected Command newObject() { + return new Command(); + } + }; + + public static void clearPool() + { + commandPool.clear(); + } + + public static Command get(Player player) + { + Command c = commandPool.obtain(); + c.player = player; + c.ap = player.getAp(); + c.turn = player.getCurrentTurn(); + return c; + } + + public CommandType type; + public Player player; + public int ap; + public int turn; + public Unit unit; + public Unit.UnitId unitId; + public Unit.UnitType unitType; + public Tile unitTile; + public Move move; + public Engagement engagement; + + private Command() + { + reset(); + } + + @Override + public void dispose() + { + commandPool.free(this); + } + + @Override + public void reset() + { + this.type = CommandType.NONE; + this.player = null; + this.unit = null; + if (this.move != null) { + this.move.dispose(); + this.move = null; + } + if (this.engagement != null) { + this.engagement.dispose(); + this.engagement = null; + } + } + + @Override + public int compareTo(Pawn pawn) + { + if (pawn == unit) + return 0; + return 1; + } + + @Override + public boolean isA(OrderType type) + { + return (type == this.type); + } + + @Override + public String toString() + { + return String.format("%s : %s", type, unit.id); + } + + public void setMove(Unit unit, Move move) + { + this.type = CommandType.MOVE; + this.move = move; + setUnit(unit); + } + + public void setPromote(Unit unit) + { + this.type = CommandType.PROMOTE; + setUnit(unit); + } + + public void setEngage(Unit unit, Unit target) + { + this.type = CommandType.ENGAGE; + this.engagement = Engagement.get(unit, target); + setUnit(unit); + } + + private void setUnit(Unit unit) + { + this.unit = unit; + this.unitId = unit.id; + this.unitType = unit.type; + this.unitTile = unit.getTile(); + } + + @Override + public void write(Json json) + { + json.writeValue("type", type); + json.writeObjectStart("player"); + json.writeValue("army", player.getName()); + json.writeValue("turn", turn); + json.writeValue("aps", ap); + json.writeObjectEnd(); + json.writeObjectStart("unit"); + json.writeValue("id", unitId); + json.writeValue("type", unitType); + json.writeValue("ace", unit.ace); + writeTile(json, "tile", unitTile); + json.writeObjectEnd(); + if (move != null) writeMove(json, "move", move); + if (engagement != null) writeEngagement(json, "engagement", engagement); + } + + private void writeMove(Json json, String key, Move m) + { + json.writeObjectStart(key); + json.writeValue("type", move.type); + writeTile(json, "from", move.from); + writeTile(json, "to", move.to); + json.writeValue("orientation", move.orientation.r()); + writeTiles(json, "path", move.tiles); + json.writeObjectEnd(); + } + + private void writeEngagement(Json json, String key, Engagement e) + { + json.writeObjectStart(key); + writeUnit(json, "attacker", e.attacker); + writeUnit(json, "defender", e.defender); + writeUnits(json, "assists", e.assists); + json.writeObjectStart("dice"); + json.writeValue("d1", e.d1); + json.writeValue("d2", e.d2); + json.writeValue("d3", e.d3); + json.writeValue("d4", e.d4); + json.writeObjectEnd(); + json.writeObjectStart("results"); + json.writeValue("success", e.success); + json.writeValue("attackSum", e.attackSum); + json.writeValue("defenseSum", e.defenseSum); + json.writeObjectEnd(); + json.writeObjectEnd(); + } + + private void writeUnit(Json json, String key, Unit u) + { + if (key != null) json.writeObjectStart(key); + else json.writeObjectStart(); + json.writeValue("id", u.id); + json.writeValue("ace", u.ace); + json.writeValue("army", u.getArmy()); + writeTile(json, "tile", u.getTile()); + json.writeObjectEnd(); + } + + private void writeUnits(Json json, String key, List<Unit> units) + { + json.writeArrayStart(key); + for (Unit u : units) + writeUnit(json, null, u); + json.writeArrayEnd(); + } + + private void writeTile(Json json, String key, Tile t) + { + if (t == null) return; + if (key != null) json.writeObjectStart(key); + else json.writeObjectStart(); + json.writeValue("col", t.getCol()); + json.writeValue("row", t.getRow()); + json.writeObjectEnd(); + } + + private void writeTiles(Json json, String key, List<Tile> tiles) + { + json.writeArrayStart(key); + for (Tile t : tiles) + writeTile(json, null, t); + json.writeArrayEnd(); + } + + @Override + public void read(Json json, JsonValue jsonMap) + { + // FIXME Command.read(Json, JsonValue); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/Config.java b/core/src/ch/asynk/creepingarmor/game/Config.java new file mode 100644 index 0000000..88a7fe2 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/Config.java @@ -0,0 +1,76 @@ +package ch.asynk.creepingarmor.game; + +public class Config +{ + public enum Graphics { + MINE("mine", 0), + ORIGINAL("original", 1); + public String s; + public int i; + Graphics(String s, int i) + { + this.s = s; + this.i = i; + } + public Graphics next() + { + if (this == ORIGINAL) + return MINE; + return ORIGINAL; + } + }; + + public enum GameMode + { + SOLO("Solo", 0), + PVE("Player vs AI", 1), + PVP("Player vs Player", 2); + public String s; + public int i; + GameMode(String s, int i) + { + this.s = s; + this.i = i; + } + public GameMode next() + { + if (this == SOLO) + return PVE; + if(this == PVE) + return PVP; + return SOLO; + } + }; + + public GameMode gameMode; + public boolean showMoves; + public boolean showTargets; + public boolean showMoveAssists; + public boolean canCancel; + public boolean mustValidate; + public boolean showEnemyPossibilities; + public boolean debug; + public Battle battle; + public float fxVolume; + public Graphics graphics; + + public Config() + { + this.gameMode = GameMode.SOLO; + this.debug = false; + this.showMoves = true; + this.showTargets = true; + this.showMoveAssists = true; + this.canCancel = false; + this.mustValidate = false; + this.showEnemyPossibilities = false; + this.graphics = Graphics.MINE; + this.battle = null; + this.fxVolume = 1f; + } + + public boolean gameModeImplemented() + { + return (gameMode == GameMode.SOLO); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/Ctrl.java b/core/src/ch/asynk/creepingarmor/game/Ctrl.java new file mode 100644 index 0000000..f4696f1 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/Ctrl.java @@ -0,0 +1,336 @@ +package ch.asynk.creepingarmor.game; + +import com.badlogic.gdx.utils.Disposable; + +import com.badlogic.gdx.math.Vector3; + +import ch.asynk.creepingarmor.CreepingArmor; +import ch.asynk.creepingarmor.ui.Position; +import ch.asynk.creepingarmor.game.State.StateType; +import ch.asynk.creepingarmor.game.states.StateSelect; +import ch.asynk.creepingarmor.game.states.StateMove; +import ch.asynk.creepingarmor.game.states.StateRotate; +import ch.asynk.creepingarmor.game.states.StatePromote; +import ch.asynk.creepingarmor.game.states.StateEngage; +import ch.asynk.creepingarmor.game.states.StateBreak; +import ch.asynk.creepingarmor.game.states.StateAnimation; +import ch.asynk.creepingarmor.game.states.StateReinforcement; +import ch.asynk.creepingarmor.game.states.StateDeployment; +import ch.asynk.creepingarmor.game.states.StateWithdraw; + +public class Ctrl implements Disposable +{ + private final CreepingArmor game; + public final Battle battle; + + public Map map; + public Hud hud; + public Config cfg; + public Player player; + public Player opponent; + public boolean blockMap; + public boolean blockHud; + + public Vector3 mapTouch = new Vector3(); + public Vector3 hudTouch = new Vector3(); + + private State selectState; + private State pathState; + private State rotateState; + private State promoteState; + private State engageState; + private State breakState; + private State animationState; + private State reinforcementState; + private State deploymentState; + private State withdrawState; + + private int animationCount = 0; + + private State state; + private StateType stateType; + private StateType stateAfterAnimation; + + public Ctrl(final CreepingArmor game, final Battle battle) + { + this.game = game; + this.battle = battle; + this.cfg = game.config; + game.ctrl = this; + + battle.init(); + + this.map = battle.getMap(); + battle.setup(this, map); + this.map.init(); + this.player = battle.getPlayer(); + this.opponent = battle.opponent(player); + + this.selectState = new StateSelect(this, map); + this.pathState = new StateMove(); + this.rotateState = new StateRotate(); + this.promoteState = new StatePromote(); + this.engageState = new StateEngage(); + this.breakState = new StateBreak(); + this.animationState = new StateAnimation(); + this.reinforcementState = new StateReinforcement(); + this.deploymentState = new StateDeployment(); + this.withdrawState = new StateWithdraw(); + + this.state = selectState; + this.stateType = StateType.DONE; + + this.hud = new Hud(this, game); + this.blockMap = false; + this.blockHud = false; + + hud.notify(battle.toString(), 2, Position.MIDDLE_CENTER, false); + startPlayerTurn(); + } + + @Override + public void dispose() + { + hud.dispose(); + map.dispose(); + } + + public Player getPlayer(Army army) + { + return (player.is(army) ? player : opponent); + } + + public boolean isInAction() + { + return (state != selectState); + } + + public void animationsOver() + { + if (hud.dialogActive()) + return; + if (stateType == StateType.ANIMATION) + leaveAnimationState(); + } + + private void leaveAnimationState() + { + + StateType tmp = stateAfterAnimation; + stateAfterAnimation = StateType.DONE; + setState(tmp); + } + + private void startPlayerTurn() + { + player.turnStart(); + // hud.notify(player.getName() + "'s turn", 2, Position.MIDDLE_CENTER, true); + if (battle.getReinforcement(this, map)) + hud.notify("You have reinforcement", 2, Position.MIDDLE_CENTER, true); + hud.update(); + setState(battle.getState(player)); + } + + private void endPlayerTurn() + { + player.turnEnd(); + Player winner = battle.checkVictory(this); + if (winner != null) + hud.victory(winner, ((winner == player) ? opponent : player)); + } + + private StateType actionAborted() + { + hud.notify("Action canceled"); + StateType nextState = this.state.abort(); + + if (nextState == StateType.ABORT) + nextState = battle.getState(player); + + return nextState; + } + + private void turnDone() + { + map.turnDone(); + endPlayerTurn(); + player = battle.getPlayer(); + opponent = battle.opponent(player); + startPlayerTurn(); + } + + private StateType actionDone() + { + StateType nextState = this.state.execute(); + + if (nextState == StateType.DONE) { + map.actionDone(); + if (map.activatedUnits.size() > 0) { + CreepingArmor.debug("Ctrl", "burn down 1AP"); + hud.notify("1 Action Point burnt", 0.6f, Position.BOTTOM_CENTER, false); + player.burnDownOneAp(); + hud.update(); + } + if (player.apExhausted()) + hud.notifyNoMoreAP(); + } + + if (nextState == StateType.DONE) + nextState = battle.getState(player); + + return nextState; + } + + private StateType deploymentDone() + { + map.actionDone(); + return this.state.execute(); + } + + public void setState(StateType nextState) + { + if (nextState == StateType.ABORT) + nextState = actionAborted(); + else if (nextState == StateType.DONE) { + if (stateType == StateType.DEPLOYMENT) + nextState = deploymentDone(); + else + nextState = actionDone(); + } + + if (stateType == StateType.ANIMATION) { + this.blockMap = hud.dialogActive(); + } + hud.playerInfo.blockEndOfTurn(nextState != StateType.SELECT); + + this.state.leave(nextState); + + CreepingArmor.debug("Ctrl", String.format(" %s -> %s : %s", stateType, nextState, player)); + + switch(nextState) { + case SELECT: + this.state = selectState; + break; + case MOVE: + this.state = pathState; + break; + case ROTATE: + this.state = rotateState; + break; + case PROMOTE: + this.state = promoteState; + break; + case ENGAGE: + this.state = engageState; + break; + case BREAK: + this.state = breakState; + break; + case WITHDRAW: + this.state = withdrawState; + break; + case ANIMATION: + this.blockMap = true; + this.state = animationState; + break; + case REINFORCEMENT: + this.state = reinforcementState; + break; + case DEPLOYMENT: + this.state = deploymentState; + break; + default: + break; + } + + StateType tmp = stateType; + stateType = nextState; + + this.state.enter(tmp); + + } + + public void touchDown() + { + if (!blockHud && hud.touchDown(hudTouch.x, hudTouch.y)) + return; + + if (!blockMap && state.downInMap(mapTouch.x, mapTouch.y)) + state.touchDown(); + } + + public void touchUp() + { + if (!blockHud && hud.touchUp(hudTouch.x, hudTouch.y)) + return; + + if (!blockMap && state.upInMap(mapTouch.x, mapTouch.y)) + state.touchUp(); + } + + public void stateTouchUp() + { + state.downInMap(-1, -1); + state.upInMap(-1, -1); + state.touchUp(); + } + + public boolean isInAnimation() + { + return (this.stateType == StateType.ANIMATION); + } + + public void setAfterAnimationState(StateType after) + { + stateAfterAnimation = after; + } + + public boolean checkDeploymentDone() + { + boolean done = battle.deploymentDone(player); + if (done) + hud.askEndDeployment(); + return done; + } + + public void reinforcementHit() + { + if (this.stateType == StateType.SELECT) + setState(StateType.REINFORCEMENT); + else if (this.stateType == StateType.REINFORCEMENT) + setState(StateType.SELECT); + } + + // Hud callbacks + public void engagementPanelClosed() + { + if (animationCount == 0) + leaveAnimationState(); + } + + public void endDeployment() + { + setState(StateType.DONE); + turnDone(); + } + + public void endGame() + { + game.switchToMenu(); + } + + public void endPlayerTurn(boolean abort) + { + if (abort) + state.abort(); + turnDone(); + } + + public void exitBoard(boolean doit) + { + if (doit) + setState(StateType.DONE); + else + setState(StateType.ABORT); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/Engagement.java b/core/src/ch/asynk/creepingarmor/game/Engagement.java new file mode 100644 index 0000000..1468181 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/Engagement.java @@ -0,0 +1,114 @@ +package ch.asynk.creepingarmor.game; + +import java.util.List; +import java.util.LinkedList; +import java.util.Random; + +import com.badlogic.gdx.utils.Pool; +import com.badlogic.gdx.utils.Disposable; + +public class Engagement implements Disposable, Pool.Poolable +{ + private static Random rand = new Random(); + + private static final Pool<Engagement> engagementPool = new Pool<Engagement>() { + @Override + protected Engagement newObject() { + return new Engagement(); + } + }; + + public static Engagement get(Unit attacker, Unit defender) + { + Engagement e = engagementPool.obtain(); + e.attacker = attacker; + e.defender = defender; + e.diceRoll(); + + return e; + } + + public static void clearPool() + { + engagementPool.clear(); + } + + public Unit attacker; + public Unit defender; + public List<Unit> assists; + public boolean success; + public int d1; + public int d2; + public int d3; + public int d4; + public int unitCount; + public int flankBonus; + public int unitDefense; + public int terrainDefense; + public int weatherDefense; + public int attackSum; + public int defenseSum; + + public Engagement() + { + assists = new LinkedList<Unit>(); + reset(); + } + + @Override + public void reset() + { + attacker = null; + defender = null; + assists.clear(); + } + + @Override + public void dispose() + { + assists.clear(); + engagementPool.free(this); + } + + public void addAssist(Unit unit) + { + assists.add(unit); + } + + private void diceRoll() + { + d1 = rand.nextInt(6) + 1; + d2 = rand.nextInt(6) + 1; + d3 = rand.nextInt(6) + 1; + d4 = rand.nextInt(6) + 1; + } + + public void set(int cnt, int flk, int def, int tdf, int wdf) + { + this.unitCount = cnt; + this.flankBonus = flk; + this.unitDefense = def; + this.terrainDefense = tdf; + this.weatherDefense = wdf; + if (d3 == 0) + this.attackSum = (d1 + d2 + unitCount + flankBonus); + else + this.attackSum = (d3 + d4 + unitCount + flankBonus); + this.defenseSum = (unitDefense + terrainDefense + weatherDefense); + } + + + @Override + public String toString() + { + int a, b; + if (d3 == 0) { + a = d1; + b = d2; + } else { + a = d3; + b = d4; + } + return String.format("Engagement : (%d + %d + %d + %d) vs (%d + %d + %d) -> %b", a, b, unitCount, flankBonus, unitDefense, terrainDefense, weatherDefense, success); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/Hex.java b/core/src/ch/asynk/creepingarmor/game/Hex.java new file mode 100644 index 0000000..cca4fad --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/Hex.java @@ -0,0 +1,138 @@ +package ch.asynk.creepingarmor.game; + +import java.util.List; +import java.util.Iterator; + +import com.badlogic.gdx.graphics.g2d.TextureAtlas; + +import ch.asynk.creepingarmor.engine.Board; +import ch.asynk.creepingarmor.engine.Pawn; +import ch.asynk.creepingarmor.engine.Tile; +import ch.asynk.creepingarmor.engine.Orientation; +import ch.asynk.creepingarmor.game.Unit.UnitType; + +public class Hex extends Tile +{ + public enum Terrain implements TileTerrain + { + OFFMAP, + BLOCKED, + CLEAR, + HILLS, + WOODS, + TOWN + } + + public static final int FOG = 0; + public static final int SELECT = 1; + public static final int AREA = 2; + public static final int MOVE = 3; + public static final int DIRECTIONS = 4; + public static final int ORIENTATION = 5; + public static final int OBJECTIVE = 6; + public static final int OBJECTIVE_HOLD = 7; + public static final int OBJECTIVE_GE = 8; + public static final int OBJECTIVE_US = 9; + public static final int EXIT = 10; + + public Terrain terrain; + public int roads; + + public String toString() + { + return String.format("(%d;%d) [%f;%f] t:%s r:%d", col, row, x, y, terrain, roads); + } + + public String toShort() + { + return String.format("(%d;%d)", col, row); + } + + public Hex(float x, float y, int col, int row, TextureAtlas atlas) + { + super(x, y, col, row, atlas); + this.terrain = Terrain.CLEAR; + this.roads = 0; + } + + public Unit getUnit() + { + return (Unit) stack.peekFirst(); + } + + @Override + public boolean isA(TileTerrain terrain) + { + return (this.terrain == terrain); + } + + @Override + public boolean isOffMap() + { + return isA(Terrain.OFFMAP); + } + + @Override + public boolean blockLineOfSightFrom(Tile tile) + { + if (isA(Terrain.CLEAR) && !hasUnits()) + return false; + + if (tile.isA(Terrain.HILLS) && isA(Terrain.CLEAR)) + return false; + + return true; + } + + @Override + public boolean atLeastOneMove(Pawn pawn) + { + if (hasUnits() || isA(Terrain.BLOCKED) || isA(Terrain.OFFMAP)) + return false; + return true; + } + + @Override + public boolean road(Orientation side) + { + return (side.s == (roads & side.s)); + } + + @Override + public int exitCost() + { + return 1; + } + + @Override + public int costFrom(Pawn pawn, Orientation side) + { + if (side == Orientation.KEEP) return 0; + if (hasUnits()) return (Integer.MAX_VALUE / 2); + if (road(side)) return 1; + + int c = 0; + switch(terrain) { + case CLEAR: + case HILLS: + c = 1; + break; + case WOODS: + case TOWN: + c = 2; + break; + case OFFMAP: + case BLOCKED: + c = (Integer.MAX_VALUE / 2); + break; + } + + return c; + } + + @Override + public int defense() + { + return (isA(Terrain.TOWN) ? 1 : 0); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/HexSet.java b/core/src/ch/asynk/creepingarmor/game/HexSet.java new file mode 100644 index 0000000..bcbfa2d --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/HexSet.java @@ -0,0 +1,29 @@ +package ch.asynk.creepingarmor.game; + +import java.util.Collection; +import java.util.LinkedHashSet; + +import ch.asynk.creepingarmor.engine.Tile; + +public class HexSet extends LinkedHashSet<Hex> +{ + private final Map map; + + public HexSet(Map map, int n) + { + super(n); + this.map = map; + } + + public void enable(int i, boolean enable) + { + for (Hex hex : this) + map.enableOverlayOn(hex, i, enable); + } + + @SuppressWarnings("unchecked") + public Collection<Tile> asTiles() + { + return (Collection) this; + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/Hud.java b/core/src/ch/asynk/creepingarmor/game/Hud.java new file mode 100644 index 0000000..1220a2d --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/Hud.java @@ -0,0 +1,307 @@ +package ch.asynk.creepingarmor.game; + +import java.util.LinkedList; + +import com.badlogic.gdx.Gdx; + +import com.badlogic.gdx.utils.Disposable; + +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; + +import ch.asynk.creepingarmor.engine.gfx.Animation; +import ch.asynk.creepingarmor.game.State.StateType; +import ch.asynk.creepingarmor.ui.Position; +import ch.asynk.creepingarmor.ui.Msg; +import ch.asynk.creepingarmor.ui.OkCancel; +import ch.asynk.creepingarmor.ui.Widget; +import ch.asynk.creepingarmor.game.hud.PlayerInfo; +import ch.asynk.creepingarmor.game.hud.ActionButtons; +import ch.asynk.creepingarmor.game.hud.StatisticsPanel; +import ch.asynk.creepingarmor.game.hud.EngagementPanel; + +import ch.asynk.creepingarmor.CreepingArmor; + +public class Hud implements Disposable, Animation +{ + public static final float OFFSET = 10f; + public static final float NOTIFY_DURATION = 2f; + + private final CreepingArmor game; + private final Ctrl ctrl; + + private Object hit; + + public PlayerInfo playerInfo; + public ActionButtons actionButtons; + + private Msg msg; + private StatisticsPanel stats; + private EngagementPanel engagement; + private OkCancel okCancel; + private LinkedList<Widget> dialogs = new LinkedList<Widget>(); + + public enum OkCancelAction + { + EXIT_BOARD, + ABORT_TURN, + END_TURN, + END_DEPLOYMENT, + } + private OkCancelAction okCancelAction; + + public Hud(final Ctrl ctrl, final CreepingArmor game) + { + this.game = game; + this.ctrl = ctrl; + + TextureAtlas hudAtlas = game.factory.hudAtlas; + playerInfo = new PlayerInfo(ctrl, game.fontW, game.uiAtlas, hudAtlas); + actionButtons = new ActionButtons(ctrl, game.uiAtlas, hudAtlas); + actionButtons.hide(); + msg = new Msg(game.fontB, game.uiAtlas); + okCancel = new OkCancel(game.fontB, game.uiAtlas); + stats = new StatisticsPanel(game.fontB, game.uiAtlas); + engagement = new EngagementPanel(game.fontB, game.uiAtlas, hudAtlas); + } + + @Override + public void dispose() + { + playerInfo.dispose(); + actionButtons.dispose(); + msg.dispose(); + okCancel.dispose(); + engagement.dispose(); + stats.dispose(); + } + + public void resize(int left, int bottom, int width, int height) + { + Position.update(left, bottom, width, height); + playerInfo.updatePosition(); + actionButtons.updatePosition(); + msg.updatePosition(); + stats.updatePosition(); + engagement.updatePosition(); + okCancel.updatePosition(); + } + + public void update() + { + Position position = ctrl.battle.getHudPosition(ctrl.player); + playerInfo.update(ctrl.player, position); + actionButtons.update(position.horizontalMirror()); + } + + @Override + public boolean animate(float delta) + { + msg.animate(delta); + playerInfo.animate(delta); + engagement.animate(delta); + return false; + } + + public void draw(Batch batch, boolean debug) + { + draw(batch); + if (debug) + game.fontB.draw(batch, String.format("FPS: %d", Gdx.graphics.getFramesPerSecond()), 80, 25); + } + + @Override + public void draw(Batch batch) + { + playerInfo.draw(batch); + actionButtons.draw(batch); + msg.draw(batch); + okCancel.draw(batch); + engagement.draw(batch); + stats.draw(batch); + } + + @Override + public void drawDebug(ShapeRenderer debugShapes) + { + playerInfo.drawDebug(debugShapes); + actionButtons.drawDebug(debugShapes); + msg.drawDebug(debugShapes); + okCancel.drawDebug(debugShapes); + engagement.drawDebug(debugShapes); + stats.drawDebug(debugShapes); + } + + public void pushNotify(String s) + { + notify(s, NOTIFY_DURATION, Position.TOP_CENTER, true); + } + + public void notify(String s) + { + notify(s, NOTIFY_DURATION, Position.TOP_CENTER, false); + } + + public void notify(String s, float duration, Position position, boolean push) + { + if (push) msg.pushWrite(s, duration, position); + else msg.write(s, duration, position); + } + + public boolean touchDown(float x, float y) + { + hit = null; + + if (dialogs.size() > 0) { + Widget dialog = dialogs.getFirst(); + if (dialog.hit(x, y)) { + hit = dialog; + return true; + } + return false; + } + + if (ctrl.isInAnimation()) + return false; + + if (hit == null) { + if (actionButtons.touchDown(x, y)) + hit = actionButtons; + else if (playerInfo.touchDown(x, y)) + hit = playerInfo; + } + + return (hit != null); + } + + public boolean touchUp(float x, float y) + { + if (hit == null) + return false; + + if (dialogs.size() > 0) { + Widget dialog = dialogs.getFirst(); + if (hit == dialog) { + if (dialog.hit(x, y)) + closeDialog(); + hit = null; + } + } else { + if (hit == actionButtons) { + actionButtons.touchUp(x, y); + } + else if (hit == playerInfo) { + playerInfo.touchUp(x, y); + } + + hit = null; + } + + return true; + } + + private void closeDialog() + { + Widget dialog = dialogs.removeFirst(); + dialog.visible = false; + + if (dialog == okCancel) + closeOkCancel(); + else if (dialog == stats) + ctrl.endGame(); + else if (dialog == engagement) + ctrl.engagementPanelClosed(); + + if (dialogs.size() > 0) + dialogs.getFirst().visible = true; + else + ctrl.blockMap = false; + } + + private void closeOkCancel() + { + boolean ok = okCancel.ok; + + switch(okCancelAction) { + case EXIT_BOARD: + ctrl.exitBoard(ok); + break; + case END_TURN: + if (ok) + ctrl.endPlayerTurn(false); + break; + case ABORT_TURN: + if (ok) + ctrl.endPlayerTurn(true); + break; + case END_DEPLOYMENT: + if (ok) + ctrl.endDeployment(); + break; + } + } + + public boolean dialogActive() + { + return (dialogs.size() > 0); + } + + private void pushDialog(Widget dialog) + { + ctrl.blockMap = true; + if (dialogs.size() != 0) + dialog.visible = false; + dialogs.addLast(dialog); + } + + public void notifyDeploymentDone() + { + this.okCancelAction = OkCancelAction.END_TURN; + okCancel.show("Deployment Phase completed."); + okCancel.noCancel(); + pushDialog(okCancel); + } + + public void notifyNoMoreAP() + { + this.okCancelAction = OkCancelAction.END_TURN; + okCancel.show("No more Action Point left."); + okCancel.noCancel(); + pushDialog(okCancel); + } + + public void askExitBoard() + { + this.okCancelAction = OkCancelAction.EXIT_BOARD; + okCancel.show("Do you want this unit to escape the battle field ?"); + pushDialog(okCancel); + } + + public void askEndOfTurn() + { + this.okCancelAction = OkCancelAction.ABORT_TURN; + okCancel.show("You still have Action Points left.\nEnd your Turn anyway ?"); + pushDialog(okCancel); + } + + public void askEndDeployment() + { + this.okCancelAction = OkCancelAction.END_DEPLOYMENT; + okCancel.show("Deployment unit count reached.\nEnd Deployment phase ?"); + pushDialog(okCancel); + } + + public void engagementSummary(Engagement e, float volume) + { + engagement.show(e, Position.BOTTOM_CENTER, volume); + pushDialog(engagement); + } + + public void victory(Player winner, Player loser) + { + stats.show(winner, loser, Position.MIDDLE_CENTER); + pushDialog(stats); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/Map.java b/core/src/ch/asynk/creepingarmor/game/Map.java new file mode 100644 index 0000000..00d720b --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/Map.java @@ -0,0 +1,659 @@ +package ch.asynk.creepingarmor.game; + +import com.badlogic.gdx.audio.Sound; +import com.badlogic.gdx.assets.AssetManager; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; + +import ch.asynk.creepingarmor.CreepingArmor; +import ch.asynk.creepingarmor.engine.Pawn; +import ch.asynk.creepingarmor.engine.Board; +import ch.asynk.creepingarmor.engine.Tile; +import ch.asynk.creepingarmor.engine.Faction; +import ch.asynk.creepingarmor.engine.Move; +import ch.asynk.creepingarmor.engine.SelectedTile; +import ch.asynk.creepingarmor.engine.ObjectiveSet; +import ch.asynk.creepingarmor.engine.OrderList; +import ch.asynk.creepingarmor.engine.Orientation; +import ch.asynk.creepingarmor.engine.Meteorology; +import ch.asynk.creepingarmor.engine.PathBuilder; +import ch.asynk.creepingarmor.engine.gfx.Moveable; +import ch.asynk.creepingarmor.engine.gfx.Animation; +import ch.asynk.creepingarmor.engine.gfx.animations.AnimationSequence; +import ch.asynk.creepingarmor.engine.gfx.animations.DiceAnimation; +import ch.asynk.creepingarmor.engine.gfx.animations.FireAnimation; +import ch.asynk.creepingarmor.engine.gfx.animations.TankFireAnimation; +import ch.asynk.creepingarmor.engine.gfx.animations.InfantryFireAnimation; +import ch.asynk.creepingarmor.engine.gfx.animations.PromoteAnimation; +import ch.asynk.creepingarmor.engine.gfx.animations.DestroyAnimation; +import ch.asynk.creepingarmor.engine.gfx.animations.SoundAnimation; +import ch.asynk.creepingarmor.engine.gfx.animations.RunnableAnimation; +import ch.asynk.creepingarmor.engine.gfx.animations.MoveToAnimation.MoveToAnimationCb; + +import ch.asynk.creepingarmor.ui.Position; + +public abstract class Map extends Board implements MoveToAnimationCb, ObjectiveSet.ObjectiveCb +{ + private final Ctrl ctrl; + + public final HexSet possibleMoves; + public final PathBuilder pathBuilder; + + public final UnitList moveableUnits; + public final UnitList possibleTargets; + public final UnitList engagementAssists; + public final UnitList activatedUnits; + public final UnitList breakUnits; + public final ObjectiveSet objectives; + + public final Meteorology meteorology; + + private final DestroyAnimation destroy; + private final Sound tankMoveSound; + private final Sound infantryMoveSound; + private Sound sound; + private long soundId = -1; + + private OrderList commands; + + protected abstract void setup(); + + public Map(final CreepingArmor game, Board.Config cfg, String textureName) + { + super(game.factory, cfg, game.manager.get(textureName, Texture.class), + new SelectedTile(game.manager.get("data/hex.png", Texture.class), new float[] {.2f, .1f, .1f, .1f, .2f, .1f} )); + this.ctrl = game.ctrl; + this.destroy = new DestroyAnimation(); + this.tankMoveSound = game.manager.get("sounds/tank_move.mp3", Sound.class); + this.infantryMoveSound = game.manager.get("sounds/infantry_move.mp3", Sound.class); + DiceAnimation.init(game.manager.get("data/dice.png", Texture.class), 16, 9, game.manager.get("sounds/dice.mp3", Sound.class)); + PromoteAnimation.init(game.manager.get("data/hud.atlas", TextureAtlas.class), + game.manager.get("sounds/promote_us.mp3", Sound.class), + game.manager.get("sounds/promote_ge.mp3", Sound.class)); + FireAnimation.init( + game.manager.get("data/infantry_fire.png", Texture.class), 1, 8, + game.manager.get("data/tank_fire.png", Texture.class), 1, 8, + game.manager.get("data/explosions.png", Texture.class), 16, 8, + game.manager.get("sounds/infantry_fire.mp3", Sound.class), + game.manager.get("sounds/tank_fire.mp3", Sound.class), + game.manager.get("sounds/tank_fire_short.mp3", Sound.class), + game.manager.get("sounds/explosion.mp3", Sound.class), + game.manager.get("sounds/explosion_short.mp3", Sound.class) + ); + + setup(); + + possibleMoves = new HexSet(this, 40); + pathBuilder = new PathBuilder(this, 10, 20, 5, 10); + moveableUnits = new UnitList(6); + + possibleTargets = new UnitList(10); + engagementAssists = new UnitList(6); + activatedUnits = new UnitList(7); + breakUnits = new UnitList(4); + + objectives = new ObjectiveSet(this, 4); + + meteorology = new Meteorology(); + commands = new OrderList(); + } + + @Override + public void dispose() + { + super.dispose(); + clearAll(); + destroy.dispose(); + pathBuilder.dispose(); + DiceAnimation.free(); + PromoteAnimation.free(); + FireAnimation.free(); + commands.dispose(); + Command.clearPool(); + Engagement.clearPool(); + } + + public void clearAll() + { + possibleMoves.clear(); + possibleTargets.clear(); + pathBuilder.clear(); + moveableUnits.clear(); + engagementAssists.clear(); + activatedUnits.clear(); + breakUnits.clear(); + } + + public Hex getHexAt(float x, float y) + { + return (Hex) getTileAt(x, y); + } + + public Hex getHex(int col, int row) + { + return (Hex) getTile(col, row); + } + + public void addObjective(int col, int row, Army army) + { + addObjective(col, row, army, true); + } + + public void addHoldObjective(int col, int row, Army army) + { + addObjective(col, row, army, false); + } + + private void addObjective(int col, int row, Army army, boolean persistent) + { + Hex hex = getHex(col, row); + objectives.add(hex, army, persistent); + showObjective(hex, army, !persistent); + } + + private void claim(Hex hex, Army army) + { + showObjective(hex, objectives.claim(hex, army)); + } + + private void unclaim(Hex hex) + { + showObjective(hex, objectives.unclaim(hex)); + } + + public int collectPossibleMoves(Unit unit) + { + if (!unit.canMove()) { + possibleMoves.clear(); + return 0; + } + return collectPossibleMoves(unit, possibleMoves.asTiles()); + } + + public int togglePathBuilderHex(Hex hex) + { + return pathBuilder.toggleCtrlTile(hex); + } + + public int collectPossibleTargets(Unit unit, UnitList foes) + { + if (!unit.canEngage()) { + possibleTargets.clear(); + return 0; + } + // return collectPossibleTargets(unit, possibleTargets); + return collectPossibleTargets(unit, foes.asPawns(), possibleTargets.asPawns()); + } + + public int collectMoveableUnits(Unit unit) + { + if (unit.canHQMove()) { + collectMoveAssists(unit, moveableUnits.asPawns()); + } else { + moveableUnits.clear(); + } + if (unit.canMove()) + moveableUnits.add(unit); + return moveableUnits.size(); + } + + public int collectAttackAssists(Unit unit, Unit target, UnitList units) + { + int s = collectAttackAssists(unit, target, units.asPawns(), engagementAssists.asPawns()); + activatedUnits.add(unit); + return s; + } + + public boolean toggleAttackAssist(Unit unit) + { + if (activatedUnits.contains(unit)) { + activatedUnits.remove(unit); + unit.hideAttack(); + unit.showAttackAssist(); + return false; + } else { + activatedUnits.add(unit); + unit.showAttack(); + unit.hideAttackAssist(); + return true; + } + } + + public void collectAndShowMovesAndAssits(Unit unit) + { + hidePossibleMoves(); + hideMoveableUnits(); + collectPossibleMoves(unit); + collectMoveableUnits(unit); + showPossibleMoves(); + showMoveableUnits(); + activatedUnits.clear(); + } + + // -> implement MoveToAnimationCb + + @Override + public void moveToAnimationEnter(Moveable moveable, float x, float y, float r) + { + claim(getHexAt(x, y), (Army) moveable.getFaction()); + } + + @Override + public void moveToAnimationLeave(Moveable moveable, float x, float y, float r) + { + unclaim(getHexAt(x, y)); + } + + @Override + public void moveToAnimationDone(Moveable moveable, float x, float y, float r) + { + } + + // <- implement MoveToAnimationCb + + private int process(Unit unit, Move move) + { + CreepingArmor.debug(" Move", String.format("%s %s", move.type, move.toString())); + + int r = 1; + + switch(move.type) { + case REGULAR: + initMove(unit); + movePawn(unit, move, this); + r = moveableUnits.size(); + break; + case EXIT: + initMove(unit); + movePawn(unit, move, this); + ctrl.player.unitWithdraw(unit); + r = moveableUnits.size(); + break; + case SET: + setPawnOnto(unit, move); + ctrl.player.unitEntry(unit); + claim((Hex) move.to, unit.getArmy()); + break; + case ENTER: + enterPawn(unit, move); + ctrl.player.unitEntry(unit); + claim((Hex) move.to, unit.getArmy()); + break; + default: + System.err.println(String.format("process wrong Move type %s", move.type)); + r = -1; + break; + } + + return r; + } + + private int promoteUnit(final Unit unit, final Player player) + { + activatedUnits.add(unit); + + Hex hex = unit.getHex(); + AnimationSequence seq = AnimationSequence.get(2); + seq.addAnimation(PromoteAnimation.get((unit.getArmy() == Army.US), hex.getX(), hex.getY(), ctrl.cfg.fxVolume)); + seq.addAnimation ( RunnableAnimation.get(unit, new Runnable() { + @Override + public void run() { + player.promote(unit); + } + })); + addAnimation(seq); + return 1; + } + + private int process(Command cmd) + { + CreepingArmor.debug("Command", cmd.toString()); + + int r = 1; + + switch(cmd.type) { + case MOVE: + r = process(cmd.unit, cmd.move); + break; + case PROMOTE: + r = promoteUnit(cmd.unit, cmd.player); + break; + case ENGAGE: + resolveEngagement(cmd.engagement); + r = doEngagement(cmd.engagement); + break; + default: + System.err.println(String.format("process wrong Command type %s", cmd.type)); + r = -1; + break; + } + + if (r != -1) + commands.add(cmd); + + return r; + } + + // Ctrl Methods + + public void init() + { + actionDone(); + } + + public void turnDone() + { + CreepingArmor.debug("TurnDone", String.format(" Processed Commands : %d", commands.size())); + + if (objectives.modifiedCount() > 0) + throw new RuntimeException("objectives not cleared"); + + // FIXME do something with these Commands + commands.dispose(); + } + + public void actionDone() + { + objectives.forget(); + } + + // STATES ENTRY -> + + public void showOnBoard(final Unit unit, Hex to, Orientation o) + { + setPawnOnto(unit, to, o); + } + + public boolean setOnBoard(final Unit unit, Hex to, Orientation entry) + { + commands.dispose(unit); + return (process(getMoveCommand(unit, Move.getSet(unit, to, entry))) == 1); + } + + public boolean enterBoard(final Unit unit, Hex to, int allowedMoves) + { + Orientation entry = findBestEntry(unit, to, allowedMoves); + if (entry == Orientation.KEEP) + return false; + + return (process(getMoveCommand(unit, Move.getEnter(unit, to, entry))) == 1); + } + + public int exitBoard(final Unit unit) + { + return process(getMoveCommand(unit, pathBuilder.getExitMove())); + } + + public int moveUnit(final Unit unit) + { + return process(getMoveCommand(unit, pathBuilder.getMove())); + } + + public void revertMoves() + { + for (Unit unit: activatedUnits) { + CreepingArmor.debug(" revertMove() " + unit); + revertLastPawnMove(unit); + commands.dispose(unit, Command.CommandType.MOVE); + } + activatedUnits.clear(); + objectives.revert(this); + } + + public void revertEnter(final Unit unit) + { + CreepingArmor.debug(" revertEnter() "+ unit); + removePawn(unit); + objectives.revert(this); + ctrl.player.revertUnitEntry(unit); + commands.dispose(unit); + unit.reset(); + } + + public boolean engageUnit(final Unit unit, final Unit target) + { + attack(unit, target, true); + + Command cmd = Command.get(ctrl.player); + cmd.setEngage(unit, target); + return (process(cmd) == 1); + } + + public void promoteUnit(final Unit unit) + { + Command cmd = Command.get(ctrl.player); + cmd.setPromote(unit); + process(cmd); + } + + // STATES ENTRY <- + + private Command getMoveCommand(Unit unit, Move move) + { + Command cmd = Command.get(ctrl.player); + cmd.setMove(unit, move); + return cmd; + } + + private void initMove(Unit unit) + { + moveableUnits.remove(unit); + activatedUnits.add(unit); + playMoveSound(unit); + } + + private void playMoveSound(Unit unit) + { + if (unit.isA(Unit.UnitType.INFANTRY)) + sound = infantryMoveSound; + else + sound = tankMoveSound; + soundId = sound.play(ctrl.cfg.fxVolume); + } + + @Override + protected void animationsOver() + { + if (soundId >= 0) { + addAnimation( SoundAnimation.get(SoundAnimation.Action.FADE_OUT, sound, soundId, ctrl.cfg.fxVolume, 0.5f)); + soundId = -1; + return; + } + ctrl.animationsOver(); + } + + private void addEngagementAnimation(Unit target) + { + FireAnimation.reset(); + Hex to = target.getHex(); + for (Unit u : activatedUnits) { + Hex from = u.getHex(); + float halfWidth = (u.getWidth() / 2f); + if (u.isA(Unit.UnitType.INFANTRY)) + addAnimation(InfantryFireAnimation.get(ctrl.cfg.fxVolume, from.getX(), from.getY(), to.getX(), to.getY(), halfWidth)); + else + addAnimation(TankFireAnimation.get(ctrl.cfg.fxVolume, from.getX(), from.getY(), to.getX(), to.getY(), halfWidth)); + } + } + + private void resolveEngagement(Engagement e) + { + int dice = e.d1 + e.d2; + + int distance = 0; + boolean mayReroll = false; + boolean night = (meteorology.day == Meteorology.Day.NIGHT); + boolean flankAttack = false; + boolean terrainBonus = true; + + for (Unit unit : activatedUnits) { + if (unit != e.attacker) + e.addAssist(unit); + if (unit.isAce()) + mayReroll = true; + if (unit.isFlankAttack()) + flankAttack = true; + if (unit.isA(Unit.UnitType.INFANTRY)) + terrainBonus = false; + if (night) { + if (distance < unit.attackDistance()) + distance = unit.attackDistance(); + } + } + + int cnt = activatedUnits.size(); + int def = e.defender.getDefense(e.attacker.getTile()); + int flk = (flankAttack ? Unit.FLANK_ATTACK_BONUS : 0); + int tdf = (terrainBonus ? e.defender.getTile().defense() : 0); + int wdf = 0; + if (night) { + if (distance > 3) + wdf = 3; + else if (distance > 2) + wdf = 2; + else if (distance > 1) + wdf = 1; + } + int s1 = (dice + cnt + flk); + int s2 = (def + tdf + wdf); + + boolean success = false; + if (dice == 2) { + success = false; + } else if (dice == 12) { + success = true; + } else { + success = (s1 >= s2); + } + if (!success && mayReroll) { + dice = e.d3 + e.d4; + s1 = (dice + cnt + flk); + if (dice == 2) { + success = false; + } else if (dice == 12) { + success = true; + } else { + success = (s1 >= s2); + } + } else { + e.d3 = 0; + e.d4 = 0; + } + + e.set(cnt, flk, def, tdf, wdf); + e.success = success; + } + + private int doEngagement(Engagement e) + { + breakUnits.clear(); + activatedUnits.clear(); + + activatedUnits.add(e.attacker); + for (Unit u : e.assists) + activatedUnits.add(u); + + for (Unit u : activatedUnits) { + u.engage(); + if (u.isA(Unit.UnitType.INFANTRY)) + breakUnits.add(u); + } + + if (e.success) { + unclaim(e.defender.getHex()); + removePawn(e.defender); + destroy.set(2f, e.defender); + addAnimation(destroy); + } + + if ((activatedUnits.size() == 1) && e.attacker.isA(Unit.UnitType.AT_GUN) && e.defender.isHardTarget()) + activatedUnits.clear(); + + ctrl.hud.engagementSummary(e, ctrl.cfg.fxVolume); + addEngagementAnimation(e.defender); + + return (e.success ? 1 : 0); + } + + // SHOW / HIDE + + public void togglePathOverlay(Hex hex) + { + boolean enable= !hex.isOverlayEnabled(Hex.MOVE); + enableOverlayOn(hex, Hex.MOVE, enable); + } + + private void showUnitsOverlay(UnitList units, int overlay, boolean on) + { + for (Unit unit : units) + unit.enableOverlay(overlay, on); + } + + public void showMoveableUnits() { showUnitsOverlay(moveableUnits, Unit.MOVE, true); } + public void hideMoveableUnits() { showUnitsOverlay(moveableUnits, Unit.MOVE, false); } + public void showPossibleTargets() { showUnitsOverlay(possibleTargets, Unit.TARGET, true); } + public void hidePossibleTargets() { showUnitsOverlay(possibleTargets, Unit.TARGET, false); } + public void showAttackAssists() { showUnitsOverlay(engagementAssists, Unit.MAY_FIRE, true); } + public void hideAttackAssists() { showUnitsOverlay(engagementAssists, Unit.FIRE, false); + showUnitsOverlay(engagementAssists, Unit.MAY_FIRE, false); } + public void showBreakUnits() { showUnitsOverlay(breakUnits, Unit.MOVE, true); } + public void hideBreakUnits() { showUnitsOverlay(breakUnits, Unit.MOVE, false); } + + public void showPossibleMoves() { possibleMoves.enable(Hex.AREA, true); } + public void hidePossibleMoves() { possibleMoves.enable(Hex.AREA, false); } + public void showPathBuilder() { pathBuilder.enable(Hex.AREA, true); } + public void hidePathBuilder() { pathBuilder.enable(Hex.AREA, false); } + public void showPath(Hex dst) { pathBuilder.enable(Hex.MOVE, true); showMove(dst); } + public void hidePath(Hex dst) { pathBuilder.enable(Hex.MOVE, false); hideMove(dst); } + + public void selectHex(Hex hex) { selectedTile.set(hex); } + public void unselectHex(Hex hex) { selectedTile.hide(); } + public void showMove(Hex hex) { enableOverlayOn(hex, Hex.MOVE, true); } + public void hideMove(Hex hex) { enableOverlayOn(hex, Hex.MOVE, false); } + public void showDirections(Hex hex) { enableOverlayOn(hex, Hex.DIRECTIONS, true); } + public void hideDirections(Hex hex) { enableOverlayOn(hex, Hex.DIRECTIONS, false); } + public void showOrientation(Hex hex, Orientation o) { enableOverlayOn(hex, Hex.ORIENTATION, o, true); } + public void hideOrientation(Hex hex) { enableOverlayOn(hex, Hex.ORIENTATION, false); } + public void showExit(Hex hex) { enableOverlayOn(hex, Hex.EXIT, true); } + public void hideExit(Hex hex) { enableOverlayOn(hex, Hex.EXIT, false); } + + public void showObjective(Hex hex, Army army, boolean hold) + { + if (hold) + enableOverlayOn(hex, Hex.OBJECTIVE_HOLD, true); + else + enableOverlayOn(hex, Hex.OBJECTIVE, true); + } + + + // -> implement ObjectiveSet.ObjectiveCb + + public void showObjective(Tile tile, Faction faction) + { + showObjective((Hex) tile, (Army) faction); + } + + // <- implement MoveToAnimationCb + + public void showObjective(Hex hex, Army army) + { + if (army == null) + army = Army.NONE; + switch(army) { + case GE: + enableOverlayOn(hex, Hex.OBJECTIVE_GE, true); + enableOverlayOn(hex, Hex.OBJECTIVE_US, false); + break; + case US: + enableOverlayOn(hex, Hex.OBJECTIVE_GE, false); + enableOverlayOn(hex, Hex.OBJECTIVE_US, true); + break; + case NONE: + default: + enableOverlayOn(hex, Hex.OBJECTIVE_GE, false); + enableOverlayOn(hex, Hex.OBJECTIVE_US, false); + break; + } + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/Player.java b/core/src/ch/asynk/creepingarmor/game/Player.java new file mode 100644 index 0000000..020118a --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/Player.java @@ -0,0 +1,213 @@ +package ch.asynk.creepingarmor.game; + +import java.util.Random; +import java.util.List; + +import ch.asynk.creepingarmor.CreepingArmor; + +public class Player +{ + private static final float MOVE_TIME = 0.4f; + + private static Random rand = new Random(); + + private int turn; + private int apSpent; + private int actionPoints; + private boolean deploymentDone; + + public Army army; + public UnitList units; + public UnitList casualties; + public UnitList reinforcement; + public UnitList withdrawed; + + public int actionCount; + public int lostEngagementCount; + public int wonEngagementCount; + + public Player(final CreepingArmor game, Army army, int n) + { + this.army = army; + this.units = new UnitList(n); + this.casualties = new UnitList(n); + this.reinforcement = new UnitList(n); + this.withdrawed = new UnitList(n); + this.turn = 0; + this.apSpent = 0; + this.actionPoints = 0; + this.deploymentDone = false; + this.actionCount = 0; + this.lostEngagementCount = 0; + this.wonEngagementCount = 0; + } + + public String getName() + { + return army.toString(); + } + + public String toString() + { + return String.format("%s Turn:%d AP:%d units:%d casualties:%d", army, turn, actionPoints, units.size(), casualties.size()); + } + + public String getStats() + { + return String.format("%s\n%4d\n%4d\n%4d\n%4d\n%4d\n%4d", getName(), actionCount, unitsLeft(), withdrawed(), casualties(), wonEngagementCount, lostEngagementCount); + } + + public boolean is(Army army) + { + return (this.army == army); + } + + public boolean isEnemy(Unit unit) + { + return unit.isEnemy(army); + } + + public boolean isEnemy(Army other) + { + return army.isEnemy(other); + } + + public int unitsLeft() + { + return (units.size() + reinforcement.size()); + } + + public int reinforcement() + { + return reinforcement.size(); + } + + public int casualties() + { + return casualties.size(); + } + + public int withdrawed() + { + return withdrawed.size(); + } + + public void addUnit(Unit unit) + { + units.add(unit); + } + + public void addReinforcement(Unit unit) + { + reinforcement.add(unit); + } + + public void unitEntry(Unit unit) + { + reinforcement.remove(unit); + units.add(unit); + } + + public void revertUnitEntry(Unit unit) + { + units.remove(unit); + reinforcement.add(unit); + } + + public void casualty(Unit unit) + { + units.remove(unit); + casualties.add(unit); + } + + public void unitWithdraw(Unit unit) + { + units.remove(unit); + withdrawed.add(unit); + } + + public int getAp() + { + return ((apSpent < actionPoints) ? (apSpent + 1) : apSpent); + } + + public int getTurnDone() + { + return turn; + } + + public int getCurrentTurn() + { + return (turn + 1); + } + + public boolean apExhausted() + { + return (apSpent == actionPoints); + } + + public boolean isDeploymentDone() + { + return (deploymentDone || (reinforcement.size() == 0)); + } + + public void burnDownOneAp() + { + apSpent += 1; + actionCount += 1; + if (apSpent > actionPoints) CreepingArmor.debug("ERROR: spent too much AP, please report"); + } + + public void turnEnd() + { + if (deploymentDone) + turn += 1; + else + deploymentDone = (reinforcement.size() == 0); + for (Unit unit : units) + unit.reset(); + } + + public void turnStart() + { + if (isDeploymentDone()) + computeActionPoints(); + } + + public int d6() + { + return rand.nextInt(6) + 1; + } + + private void computeActionPoints() + { + this.actionPoints = 2; + if (d6() > 2) { + this.actionPoints += 1; + if (d6() > 3) + this.actionPoints += 1; + } + apSpent = 0; + } + + public boolean canPromote(Unit unit) + { + if (unit.isHq()) return false; + for (Unit p: casualties) + if (p.isHqOf(unit)) return true; + return false; + } + + public boolean promote(Unit unit) + { + for (Unit p: casualties) { + if (p.isHqOf(unit)) { + unit.promote(); + p.degrade(); + return true; + } + } + + return false; + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/State.java b/core/src/ch/asynk/creepingarmor/game/State.java new file mode 100644 index 0000000..a0af2e2 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/State.java @@ -0,0 +1,35 @@ +package ch.asynk.creepingarmor.game; + +public interface State +{ + enum StateType { + SELECT, + MOVE, + ROTATE, + ENGAGE, + BREAK, + PROMOTE, + ANIMATION, + REINFORCEMENT, + DEPLOYMENT, + WITHDRAW, + ABORT, + DONE + }; + + public void enter(StateType prevState); + + public void leave(StateType nextState); + + public StateType abort(); + + public StateType execute(); + + public void touchDown(); + + public void touchUp(); + + public boolean downInMap(float x, float y); + + public boolean upInMap(float x, float y); +} diff --git a/core/src/ch/asynk/creepingarmor/game/Unit.java b/core/src/ch/asynk/creepingarmor/game/Unit.java new file mode 100644 index 0000000..f209ea2 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/Unit.java @@ -0,0 +1,361 @@ +package ch.asynk.creepingarmor.game; + +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; + +import ch.asynk.creepingarmor.engine.Pawn; +import ch.asynk.creepingarmor.engine.Tile; +import ch.asynk.creepingarmor.engine.HeadedPawn; +import ch.asynk.creepingarmor.engine.Orientation; +import ch.asynk.creepingarmor.game.Hex.Terrain; + +import ch.asynk.creepingarmor.CreepingArmor; + +public class Unit extends HeadedPawn +{ + public static final int MOVE = 0; + public static final int TARGET = 1; + public static final int FIRE = 2; + public static final int MAY_FIRE = 3; + public static final int ACE = 4; + public static final int HQ = 5; + public static final int HAS_FIRED = 6; + public static final int HAS_MOVED = 7; + + public static final int FLANK_ATTACK_BONUS = 1; + + public enum UnitType implements Pawn.PawnType + { + HARD_TARGET, + HARD_TARGET_HQ, + INFANTRY, + AT_GUN, + ARTILLERY + } + + public enum UnitId implements Pawn.PawnId + { + GE_AT_GUN("German Anti-Tank Gun"), + GE_INFANTRY("German Infantry"), + GE_KINGTIGER("German King Tiger"), + GE_PANZER_IV("German Panzer IV"), + GE_PANZER_IV_HQ("German Panzer IV HQ"), + GE_TIGER("German Tiger"), + GE_WESPE("German Wespe"), + + US_AT_GUN("US Anti-Tank Gun"), + US_INFANTRY("US Infantry"), + US_PERSHING("US Pershing"), + US_PERSHING_HQ("US Pershing HQ"), + US_PRIEST("US Priest"), + US_SHERMAN("US Sherman"), + US_SHERMAN_HQ("US Sherman HQ"), + US_WOLVERINE("US Wolverine"); + + private String s; + UnitId(String s) { this.s = s; } + public String toString() { return s; } + } + + public int rng; + public int def; + public int cdef; + public int mp; + public int mpLeft; + public UnitType type; + public UnitId id; + public boolean ace; + private boolean hasMoved; + private boolean hasFired; + + protected Unit(Army army, String pawn, String head, TextureAtlas pawns, TextureAtlas overlays) + { + super(army, pawn, head, pawns, overlays); + ace = false; + + } + + private void commonSetup() + { + mpLeft = mp; + enableOverlay(HQ, isHq()); + this.hasMoved = false; + this.hasFired = false; + updateDescr(); + } + + private void updateDescr() + { + if (cdef == -1) + this.descr = id.toString() + (ace ? " Ace " : "") + " (" + rng + "-" + def + "-" + mp + ")"; + else + this.descr = id.toString() + (ace ? " Ace " : "") + " (" + rng + "-" + def + "/" + cdef + "-" + mp + ")"; + } + + // hard tager + public Unit(Army army, UnitId id, UnitType type, int range, int defense, int movementPoints, String unit, String head, TextureAtlas pawns, TextureAtlas overlays) + { + this(army, unit, head, pawns, overlays); + this.rng = range; + this.def = defense; + this.cdef = -1; + this.mp = movementPoints; + this.id = id; + this.type = type; + commonSetup(); + } + + // soft tager + public Unit(Army army, UnitId id, UnitType type, int range, int defense, int concealedDefense, int movementPoints, String unit, String head, TextureAtlas pawns, TextureAtlas overlays) + { + this(army, unit, head, pawns, overlays); + this.rng = range; + this.def = defense; + this.cdef = concealedDefense; + this.mp = movementPoints; + this.id = id; + this.type = type; + commonSetup(); + } + + public Army getArmy() + { + return (Army) getFaction(); + } + + public Hex getHex() + { + return (Hex) getTile(); + } + + public boolean isAce() + { + return ace; + } + + public void setAce(boolean ace) + { + this.ace = ace; + updateDescr(); + enableOverlay(ACE, ace); + } + + @Override + public int getMovementPoints() + { + return mpLeft; + } + + @Override + public int getRoadMarchBonus() + { + return 1; + } + + @Override + public int getEngagementRangeFrom(Tile tile) + { + if (!isA(UnitType.INFANTRY) && tile.isA(Terrain.HILLS)) + return rng + 1; + return rng; + } + + @Override + public int getAngleOfAttack() + { + return orientation.getFrontSides(); + } + + @Override + public int getFlankSides() + { + return orientation.getBackSides(); + } + + @Override + public int getDefense(Tile tile) + { + if (!isHardTarget() && (tile.isA(Terrain.HILLS) || tile.isA(Terrain.WOODS) || tile.isA(Terrain.TOWN))) + return cdef; + + return def; + } + + @Override + public boolean isUnit() + { + return true; + } + + @Override + public boolean isA(PawnId i) + { + return (id == i); + } + + @Override + public boolean isA(PawnType t) + { + return (type == t); + } + + @Override + public boolean isHq() + { + return isA(UnitType.HARD_TARGET_HQ); + } + + @Override + public boolean isHqOf(Pawn other) + { + if (isA(UnitId.GE_PANZER_IV_HQ) && other.isA(UnitId.GE_PANZER_IV)) return true; + if (isA(UnitId.US_PERSHING_HQ) && other.isA(UnitId.US_PERSHING)) return true; + if (isA(UnitId.US_SHERMAN_HQ) && other.isA(UnitId.US_SHERMAN)) return true; + return false; + } + + public void promote() + { + if (isA(UnitId.GE_PANZER_IV)) + id = UnitId.GE_PANZER_IV_HQ; + else if (isA(UnitId.US_PERSHING)) + id = UnitId.US_PERSHING_HQ; + else if (isA(UnitId.US_SHERMAN)) + id = UnitId.US_SHERMAN_HQ; + else + return; + + type = UnitType.HARD_TARGET_HQ; + enableOverlay(HQ, true); + updateDescr(); + } + + public void degrade() + { + if (isA(UnitId.GE_PANZER_IV_HQ)) + id = UnitId.GE_PANZER_IV; + else if (isA(UnitId.US_PERSHING_HQ)) + id = UnitId.US_PERSHING; + else if (isA(UnitId.US_SHERMAN_HQ)) + id = UnitId.US_SHERMAN; + else + return; + + type = UnitType.HARD_TARGET; + enableOverlay(HQ, false); + updateDescr(); + } + + @Override + public boolean isHardTarget() + { + return (isA(UnitType.HARD_TARGET) || isA(UnitType.HARD_TARGET_HQ) || isA(UnitType.ARTILLERY)); + } + + @Override + public boolean canRotate() + { + return canMove(); + } + + @Override + public boolean canMove() + { + if (isHardTarget()) return !hasMoved; + return (!hasMoved && !hasFired); + } + + @Override + public boolean canEngage() + { + if (isHardTarget()) return !hasFired; + return (!hasMoved && !hasFired); + } + + @Override + public boolean canAssistEngagementWithoutLos() + { + return isA(UnitType.ARTILLERY); + } + + @Override + public boolean canEngage(Pawn other) + { + return (isEnemy(other) && canEngage()); + } + + public boolean canHQMove() + { + return (isHq() && ((move == null) || (!move.isEnter()))); + } + + public void setMoved() + { + hasMoved = true; + updateOverlays(); + } + + @Override + public void move() + { + int cost = move.cost; + + if (move.roadMarch && (cost > mpLeft)) + cost -= getRoadMarchBonus(); + + if (cost > mpLeft) + CreepingArmor.debug("ERROR: Movement point exceeded: " + cost + "/" + mpLeft + " please report"); + + if (move.isFinal()) + setMoved(); + + mpLeft -= cost; + } + + @Override + public void engage() + { + hasFired = true; + updateOverlays(); + } + + @Override + public void reset() + { + super.reset(); + mpLeft = mp; + hasFired = false; + hasMoved = false; + hideHasMoved(); + hideHasFired(); + } + + @Override + public void revertLastMove() + { + hasMoved = false; + mpLeft = mp; + updateOverlays(); + move = null; + } + + private void updateOverlays() + { + enableOverlay(HAS_MOVED, !canMove()); + enableOverlay(HAS_FIRED, !canEngage()); + } + + // SHOW / HIDE + public void showMoveable() { enableOverlay(MOVE, true); } + public void hideMoveable() { enableOverlay(MOVE, false); } + public void showTarget() { enableOverlay(TARGET, true); } + public void hideTarget() { enableOverlay(TARGET, false); } + public void showAttack() { enableOverlay(FIRE, true); } + public void hideAttack() { enableOverlay(FIRE, false); } + public void showAttackAssist() { enableOverlay(MAY_FIRE, true); } + public void hideAttackAssist() { enableOverlay(MAY_FIRE, false); } + public void showHasMoved() { enableOverlay(HAS_MOVED, true); } + public void hideHasMoved() { enableOverlay(HAS_MOVED, false); } + public void showHasFired() { enableOverlay(HAS_FIRED, true); } + public void hideHasFired() { enableOverlay(HAS_FIRED, false); } +} diff --git a/core/src/ch/asynk/creepingarmor/game/UnitList.java b/core/src/ch/asynk/creepingarmor/game/UnitList.java new file mode 100644 index 0000000..2e4ee27 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/UnitList.java @@ -0,0 +1,20 @@ +package ch.asynk.creepingarmor.game; + +import java.util.Collection; +import java.util.ArrayList; + +import ch.asynk.creepingarmor.engine.Pawn; + +public class UnitList extends ArrayList<Unit> +{ + public UnitList(int n) + { + super(n); + } + + @SuppressWarnings("unchecked") + public Collection<Pawn> asPawns() + { + return (Collection) this; + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/Zone.java b/core/src/ch/asynk/creepingarmor/game/Zone.java new file mode 100644 index 0000000..f7c0a82 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/Zone.java @@ -0,0 +1,14 @@ +package ch.asynk.creepingarmor.game; + +import ch.asynk.creepingarmor.engine.Orientation; + +public class Zone extends HexSet +{ + public int allowedMoves; + public Orientation orientation; + + public Zone(Map map, int n) + { + super(map, n); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/battles/BattleCommon.java b/core/src/ch/asynk/creepingarmor/game/battles/BattleCommon.java new file mode 100644 index 0000000..fb1ab53 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/battles/BattleCommon.java @@ -0,0 +1,151 @@ +package ch.asynk.creepingarmor.game.battles; + +import java.util.Random; +import java.util.HashMap; +import java.util.ArrayList; + +import ch.asynk.creepingarmor.game.Army; +import ch.asynk.creepingarmor.game.Battle; +import ch.asynk.creepingarmor.game.Player; +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.HexSet; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Unit.UnitId; +import ch.asynk.creepingarmor.game.State.StateType; + +public abstract class BattleCommon implements Battle +{ + protected final static Random random = new Random(); + + protected Factory.MapType mapType; + protected String name; + protected String description; + protected Factory factory; + protected Player usPlayer; + protected Player gePlayer; + protected ArrayList<Zone> entryZone = new ArrayList<Zone>(); + protected ArrayList<Zone> exitZone = new ArrayList<Zone>(); + protected HashMap<Unit, Zone> unitEntry = new HashMap<Unit, Zone>(); + protected HashMap<Unit, Zone> unitExit = new HashMap<Unit, Zone>(); + + public BattleCommon(Factory factory) + { + this.factory = factory; + } + + @Override + public void init() + { + this.usPlayer = factory.getPlayer(Army.US); + this.gePlayer = factory.getPlayer(Army.GE); + } + + @Override + public String toString() + { + return getName(); + } + + @Override + public String getName() + { + return name; + } + + @Override + public String getDescription() + { + return description; + } + + @Override + public Factory.MapType getMapType() + { + return mapType; + } + + @Override + public Map getMap() + { + return factory.getMap(mapType); + } + + @Override + public Player opponent(Player player) + { + if (player == usPlayer) + return gePlayer; + return usPlayer; + } + + @Override + public boolean deploymentDone(Player player) + { + return player.isDeploymentDone(); + } + + @Override + public StateType getState(Player player) + { + if (!player.isDeploymentDone()) + return StateType.DEPLOYMENT; + return StateType.SELECT; + } + + @Override + public boolean getReinforcement(Ctrl ctrl, Map map) + { + return false; + } + + @Override + public Zone getEntryZone(Unit unit) + { + return unitEntry.get(unit); + } + + @Override + public Zone getExitZone(Unit unit) + { + return unitExit.get(unit); + } + + public void addEntryZone(Zone entry) + { + entryZone.add(entry); + } + + public void addExitZone(Zone exit) + { + exitZone.add(exit); + exit.enable(Hex.EXIT, true); + } + + public void addReinforcement(Player player, Zone entryZone, UnitId unitId) + { + addReinforcement(player, entryZone, unitId, false); + } + + public void addReinforcement(Player player, Zone entryZone, Zone exitZone, UnitId unitId) + { + addReinforcement(player, entryZone, exitZone, unitId, false); + } + + public void addReinforcement(Player player, Zone entryZone, UnitId unitId, boolean ace) + { + addReinforcement(player, entryZone, null, unitId, ace); + } + + public void addReinforcement(Player player, Zone entryZone, Zone exitZone, UnitId unitId, boolean ace) + { + Unit unit = factory.getUnit(unitId); + unit.setAce(ace); + player.addReinforcement(unit); + unitEntry.put(unit, entryZone); + if (exitZone != null) + unitExit.put(unit, exitZone); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/battles/BattleCounterAttack.java b/core/src/ch/asynk/creepingarmor/game/battles/BattleCounterAttack.java new file mode 100644 index 0000000..a06d7cd --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/battles/BattleCounterAttack.java @@ -0,0 +1,150 @@ +package ch.asynk.creepingarmor.game.battles; + +import ch.asynk.creepingarmor.game.Army; +import ch.asynk.creepingarmor.game.Player; +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Unit.UnitId; +import ch.asynk.creepingarmor.ui.Position; +import ch.asynk.creepingarmor.engine.Orientation; + +public class BattleCounterAttack extends BattleCommon +{ + public BattleCounterAttack(Factory factory) + { + super(factory); + name = "Counterattack"; + mapType = Factory.MapType.MAP_B; + } + + @Override + public Position getHudPosition(Player player) + { + return (player.is(Army.US) ? Position.TOP_RIGHT: Position.TOP_LEFT); + } + + @Override + public Player getPlayer() + { + if (!gePlayer.isDeploymentDone()) + return gePlayer; + if (!usPlayer.isDeploymentDone()) + return usPlayer; + if (gePlayer.getTurnDone() == usPlayer.getTurnDone()) + return gePlayer; + return usPlayer; + } + + public Player checkVictory(Ctrl ctrl) + { + if (ctrl.opponent.unitsLeft() == 0) + return ctrl.player; + + if (gePlayer.withdrawed() >= 3) + return gePlayer; + + if ((ctrl.player.getTurnDone() < 9) || (ctrl.opponent.getTurnDone() < 9)) + return null; + + return usPlayer; + } + + @Override + public boolean getReinforcement(Ctrl ctrl, Map map) + { + if (ctrl.player.is(Army.GE)) + return false; + if (ctrl.player.getCurrentTurn() != 5) + return false; + + // hex row 1 + Zone usEntry = new Zone(map, 9); + usEntry.allowedMoves = (Orientation.SOUTH.s | Orientation.SOUTH_EAST.s | Orientation.SOUTH_WEST.s); + usEntry.add(map.getHex(9, 0)); + usEntry.add(map.getHex(9, 1)); + usEntry.add(map.getHex(10, 2)); + usEntry.add(map.getHex(10, 3)); + usEntry.add(map.getHex(11, 4)); + usEntry.add(map.getHex(11, 5)); + usEntry.add(map.getHex(12, 6)); + usEntry.add(map.getHex(12, 7)); + usEntry.add(map.getHex(13, 8)); + addEntryZone(usEntry); + + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_PRIEST); + + return true; + } + + @Override + public void setup(Ctrl ctrl, Map map) + { + // hex row 1 + Zone geExit = new Zone(map, 9); + geExit.orientation = Orientation.NORTH; + geExit.add(map.getHex(9, 0)); + geExit.add(map.getHex(9, 1)); + geExit.add(map.getHex(10, 2)); + geExit.add(map.getHex(10, 3)); + geExit.add(map.getHex(11, 4)); + geExit.add(map.getHex(11, 5)); + geExit.add(map.getHex(12, 6)); + geExit.add(map.getHex(12, 7)); + geExit.add(map.getHex(13, 8)); + addExitZone(geExit); + + // hex rows 8-9 + Zone geEntry = new Zone(map, 18); + geEntry.orientation = Orientation.NORTH; + for (int i = 0; i < 2; i++) { + geEntry.add(map.getHex((1 + i), 0)); + geEntry.add(map.getHex((1 + i), 1)); + geEntry.add(map.getHex((2 + i), 2)); + geEntry.add(map.getHex((2 + i), 3)); + geEntry.add(map.getHex((3 + i), 4)); + geEntry.add(map.getHex((3 + i), 5)); + geEntry.add(map.getHex((4 + i), 6)); + geEntry.add(map.getHex((4 + i), 7)); + geEntry.add(map.getHex((5 + i), 8)); + } + addEntryZone(geEntry); + + addReinforcement(gePlayer, geEntry, geExit, UnitId.GE_PANZER_IV_HQ); + addReinforcement(gePlayer, geEntry, geExit, UnitId.GE_PANZER_IV_HQ); + addReinforcement(gePlayer, geEntry, geExit, UnitId.GE_TIGER); + addReinforcement(gePlayer, geEntry, geExit, UnitId.GE_TIGER); + addReinforcement(gePlayer, geEntry, geExit, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, geExit, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, geExit, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, geExit, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, geExit, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, geExit, UnitId.GE_WESPE); + + // hex rows 1-4 + Zone usEntry = new Zone(map, 36); + usEntry.orientation = Orientation.SOUTH; + for (int i = 0; i < 4; i++) { + usEntry.add(map.getHex((6 + i), 0)); + usEntry.add(map.getHex((6 + i), 1)); + usEntry.add(map.getHex((7 + i), 2)); + usEntry.add(map.getHex((7 + i), 3)); + usEntry.add(map.getHex((8 + i), 4)); + usEntry.add(map.getHex((8 + i), 5)); + usEntry.add(map.getHex((9 + i), 6)); + usEntry.add(map.getHex((9 + i), 7)); + usEntry.add(map.getHex((10 + i), 8)); + } + addEntryZone(usEntry); + + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN_HQ); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN_HQ); + addReinforcement(usPlayer, usEntry, UnitId.US_WOLVERINE); + addReinforcement(usPlayer, usEntry, UnitId.US_WOLVERINE); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/battles/BattleFrontalAssault.java b/core/src/ch/asynk/creepingarmor/game/battles/BattleFrontalAssault.java new file mode 100644 index 0000000..a7f5726 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/battles/BattleFrontalAssault.java @@ -0,0 +1,124 @@ +package ch.asynk.creepingarmor.game.battles; + +import ch.asynk.creepingarmor.game.Army; +import ch.asynk.creepingarmor.game.Player; +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.HexSet; +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Unit.UnitId; +import ch.asynk.creepingarmor.ui.Position; +import ch.asynk.creepingarmor.engine.Orientation; + +public class BattleFrontalAssault extends BattleCommon +{ + public BattleFrontalAssault(Factory factory) + { + super(factory); + name = "Frontal Assault"; + mapType = Factory.MapType.MAP_A; + } + + + @Override + public Player getPlayer() + { + if (!gePlayer.isDeploymentDone()) { + int n = gePlayer.reinforcement(); + if (n > 4) + return gePlayer; + else { + if (usPlayer.isDeploymentDone()) + return gePlayer; + else + return usPlayer; + } + } + if (gePlayer.getTurnDone() == usPlayer.getTurnDone()) + return usPlayer; + return gePlayer; + } + + @Override + public Position getHudPosition(Player player) + { + return (player.is(Army.US) ? Position.TOP_RIGHT: Position.TOP_LEFT); + } + + @Override + public boolean deploymentDone(Player player) + { + if (player.isDeploymentDone()) + return true; + return ((player.is(Army.GE) && (gePlayer.reinforcement.size() == 4))); + } + + @Override + public Player checkVictory(Ctrl ctrl) + { + if (ctrl.opponent.unitsLeft() == 0) + return ctrl.player; + + if ((ctrl.player.getTurnDone() < 10) || (ctrl.opponent.getTurnDone() < 10)) + return null; + + if (ctrl.map.objectives.count(Army.US) >= 2) + return usPlayer; + else + return gePlayer; + } + + @Override + public void setup(Ctrl ctrl, Map map) + { + // G9, E6, H4 + map.addObjective(2, 2, Army.NONE); + map.addObjective(6, 4, Army.NONE); + map.addObjective(6, 1, Army.NONE); + + // hex rows E-H + Zone geEntry = new Zone(map, 38); + geEntry.orientation = Orientation.NORTH_WEST; + for (int i = 2; i < 12; i++) + geEntry.add(map.getHex(i, 4)); + for (int i = 2; i < 11; i++) + geEntry.add(map.getHex(i, 3)); + for (int i = 1; i < 11; i++) + geEntry.add(map.getHex(i, 2)); + for (int i = 1; i < 10; i++) + geEntry.add(map.getHex(i, 1)); + addEntryZone(geEntry); + + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV_HQ); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV_HQ); + addReinforcement(gePlayer, geEntry, UnitId.GE_TIGER); + addReinforcement(gePlayer, geEntry, UnitId.GE_TIGER); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + + // hex rows A-B + Zone usEntry = new Zone(map, 19); + usEntry.orientation = Orientation.SOUTH_EAST; + for (int i = 4; i < 14; i++) + usEntry.add(map.getHex(i, 8)); + for (int i = 4; i < 13; i++) + usEntry.add(map.getHex(i, 7)); + addEntryZone(usEntry); + + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN_HQ); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN_HQ); + addReinforcement(usPlayer, usEntry, UnitId.US_WOLVERINE); + addReinforcement(usPlayer, usEntry, UnitId.US_WOLVERINE); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_PRIEST); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/battles/BattleHeadToHead.java b/core/src/ch/asynk/creepingarmor/game/battles/BattleHeadToHead.java new file mode 100644 index 0000000..26d2b1d --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/battles/BattleHeadToHead.java @@ -0,0 +1,118 @@ +package ch.asynk.creepingarmor.game.battles; + +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.Army; +import ch.asynk.creepingarmor.game.Player; +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.HexSet; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Unit.UnitId; +import ch.asynk.creepingarmor.ui.Position; +import ch.asynk.creepingarmor.engine.Orientation; + +public class BattleHeadToHead extends BattleCommon +{ + private Army firstArmy; + + public BattleHeadToHead(Factory factory) + { + super(factory); + name = "Head To Head"; + firstArmy = ((random.nextInt(2) == 0) ? Army.US : Army.GE); + mapType = Factory.MapType.MAP_A; + } + + @Override + public Player getPlayer() + { + if (gePlayer.getTurnDone() == usPlayer.getTurnDone()) + return ((firstArmy == Army.US) ? usPlayer : gePlayer); + else + return ((firstArmy == Army.US) ? gePlayer : usPlayer); + } + + @Override + public Position getHudPosition(Player player) + { + return (player.is(Army.US) ? Position.TOP_RIGHT: Position.TOP_LEFT); + } + + @Override + public Player checkVictory(Ctrl ctrl) + { + if (ctrl.opponent.unitsLeft() == 0) + return ctrl.player; + + if ((ctrl.player.getTurnDone() < 10) || (ctrl.opponent.getTurnDone() < 10)) + return null; + + if (ctrl.map.objectives.count(Army.US) >= 2) + return usPlayer; + if (ctrl.map.objectives.count(Army.GE) >= 2) + return gePlayer; + + return null; + } + + @Override + public void setup(Ctrl ctrl, Map map) + { + // end deployment + usPlayer.turnEnd(); + gePlayer.turnEnd(); + + // B6, E6, H4 + map.addObjective(7, 7, Army.NONE); + map.addObjective(6, 4, Army.NONE); + map.addObjective(6, 1, Army.NONE); + + // southern hex row + Zone geEntry = new Zone(map, 9); + geEntry.allowedMoves = (Orientation.NORTH.s | Orientation.NORTH_EAST.s | Orientation.NORTH_WEST.s); + geEntry.add(map.getHex(0, 0)); + geEntry.add(map.getHex(1, 1)); + geEntry.add(map.getHex(1, 2)); + geEntry.add(map.getHex(2, 3)); + geEntry.add(map.getHex(2, 4)); + geEntry.add(map.getHex(3, 5)); + geEntry.add(map.getHex(3, 6)); + geEntry.add(map.getHex(4, 7)); + geEntry.add(map.getHex(4, 8)); + addEntryZone(geEntry); + + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV_HQ); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV_HQ); + addReinforcement(gePlayer, geEntry, UnitId.GE_TIGER); + addReinforcement(gePlayer, geEntry, UnitId.GE_TIGER); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + + // northern hex row + Zone usEntry = new Zone(map, 9); + usEntry.allowedMoves = (Orientation.SOUTH.s | Orientation.SOUTH_EAST.s | Orientation.SOUTH_WEST.s); + usEntry.add(map.getHex(9, 0)); + usEntry.add(map.getHex(9, 1)); + usEntry.add(map.getHex(10, 2)); + usEntry.add(map.getHex(10, 3)); + usEntry.add(map.getHex(11, 4)); + usEntry.add(map.getHex(11, 5)); + usEntry.add(map.getHex(12, 6)); + usEntry.add(map.getHex(12, 7)); + usEntry.add(map.getHex(13, 8)); + addEntryZone(usEntry); + + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN_HQ); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN_HQ); + addReinforcement(usPlayer, usEntry, UnitId.US_WOLVERINE); + addReinforcement(usPlayer, usEntry, UnitId.US_WOLVERINE); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_PRIEST); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/battles/BattleLastStand.java b/core/src/ch/asynk/creepingarmor/game/battles/BattleLastStand.java new file mode 100644 index 0000000..6d7e035 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/battles/BattleLastStand.java @@ -0,0 +1,136 @@ +package ch.asynk.creepingarmor.game.battles; + +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.Army; +import ch.asynk.creepingarmor.game.Player; +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.HexSet; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Unit.UnitId; +import ch.asynk.creepingarmor.ui.Position; +import ch.asynk.creepingarmor.engine.Orientation; + +public class BattleLastStand extends BattleCommon +{ + public BattleLastStand(Factory factory) + { + super(factory); + name = "Last Stand"; + mapType = Factory.MapType.MAP_B; + } + + @Override + public Position getHudPosition(Player player) + { + return (player.is(Army.US) ? Position.TOP_RIGHT: Position.TOP_LEFT); + } + + @Override + public Player getPlayer() + { + if (!gePlayer.isDeploymentDone()) + return gePlayer; + if (!usPlayer.isDeploymentDone()) + return usPlayer; + if (gePlayer.getTurnDone() == usPlayer.getTurnDone()) + return usPlayer; + return gePlayer; + } + + public Player checkVictory(Ctrl ctrl) + { + if (ctrl.opponent.unitsLeft() == 0) + return ctrl.player; + + if ((ctrl.player.getTurnDone() < 8) || (ctrl.opponent.getTurnDone() < 8)) + return null; + + int gePoints = usPlayer.casualties(); + int usPoints = gePlayer.casualties(); + usPoints += ctrl.map.objectives.count(Army.US); + for (Unit unit : gePlayer.casualties) { + if (unit.isAce()) + usPoints += 1; + } + + if (usPoints > gePoints) + return usPlayer; + else + return gePlayer; + } + + @Override + public void setup(Ctrl ctrl, Map map) + { + // A7, E6, F6, G10 + map.addObjective(7, 8, Army.NONE); + map.addObjective(6, 4, Army.NONE); + map.addObjective(5, 3, Army.NONE); + map.addObjective(1, 2, Army.NONE); + + // 1 hex of E7 + Zone geEntry = new Zone(map, 7); + geEntry.orientation = Orientation.NORTH; + geEntry.add(map.getHex(5, 5)); + geEntry.add(map.getHex(4, 4)); + geEntry.add(map.getHex(4, 3)); + geEntry.add(map.getHex(5, 3)); + geEntry.add(map.getHex(6, 4)); + geEntry.add(map.getHex(6, 5)); + geEntry.add(map.getHex(5, 4)); + addEntryZone(geEntry); + + addReinforcement(gePlayer, geEntry, UnitId.GE_TIGER, true); + addReinforcement(gePlayer, geEntry, UnitId.GE_TIGER); + addReinforcement(gePlayer, geEntry, UnitId.GE_TIGER); + + // hex rows 7-10 + geEntry = new Zone(map, 32); + geEntry.orientation = Orientation.NORTH; + for (int i = 0; i < 4; i++) { + geEntry.add(map.getHex(i, 0)); + geEntry.add(map.getHex((i + 1), 2)); + geEntry.add(map.getHex((i + 2), 4)); + geEntry.add(map.getHex((i + 3), 6)); + geEntry.add(map.getHex((i + 4), 8)); + } + for (int i = 0; i < 3; i++) { + geEntry.add(map.getHex((i + 1), 1)); + geEntry.add(map.getHex((i + 2), 3)); + geEntry.add(map.getHex((i + 3), 5)); + geEntry.add(map.getHex((i + 4), 7)); + } + addEntryZone(geEntry); + + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_WESPE); + + // hex rows hex row 1 + E2 + C2 + Zone usEntry = new Zone(map, 11); + usEntry.orientation = Orientation.SOUTH; + usEntry.add(map.getHex(9, 0)); + usEntry.add(map.getHex(9, 1)); + usEntry.add(map.getHex(10, 2)); + usEntry.add(map.getHex(10, 3)); + usEntry.add(map.getHex(11, 4)); + usEntry.add(map.getHex(11, 5)); + usEntry.add(map.getHex(12, 6)); + usEntry.add(map.getHex(12, 7)); + usEntry.add(map.getHex(13, 8)); + usEntry.add(map.getHex(10, 4)); + usEntry.add(map.getHex(11, 6)); + addEntryZone(usEntry); + + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN_HQ); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN_HQ); + addReinforcement(usPlayer, usEntry, UnitId.US_WOLVERINE); + addReinforcement(usPlayer, usEntry, UnitId.US_WOLVERINE); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/battles/BattleNightAction.java b/core/src/ch/asynk/creepingarmor/game/battles/BattleNightAction.java new file mode 100644 index 0000000..965327a --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/battles/BattleNightAction.java @@ -0,0 +1,153 @@ +package ch.asynk.creepingarmor.game.battles; + +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.Army; +import ch.asynk.creepingarmor.game.Player; +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.HexSet; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Unit.UnitId; +import ch.asynk.creepingarmor.ui.Position; +import ch.asynk.creepingarmor.engine.Orientation; +import ch.asynk.creepingarmor.engine.Meteorology; + +public class BattleNightAction extends BattleCommon +{ + public BattleNightAction(Factory factory) + { + super(factory); + name = "Night Action"; + mapType = Factory.MapType.MAP_B; + } + + @Override + public Position getHudPosition(Player player) + { + return (player.is(Army.US) ? Position.TOP_RIGHT: Position.TOP_LEFT); + } + + @Override + public Player getPlayer() + { + if (!gePlayer.isDeploymentDone() || gePlayer.getCurrentTurn() == 1) + return gePlayer; + if (gePlayer.getTurnDone() > usPlayer.getTurnDone()) + return usPlayer; + return gePlayer; + } + + private boolean isClear(Map map, int col, int row) + { + Hex hex = map.getHex(col, row); + Unit unit = hex.getUnit(); + if ((unit != null) && unit.is(Army.GE)) { + map.selectHex(hex); + return false; + } + map.showMove(hex); + return true; + } + + public Player checkVictory(Ctrl ctrl) + { + if (ctrl.opponent.unitsLeft() == 0) + return ctrl.player; + + if ((ctrl.player.getTurnDone() < 9) || (ctrl.opponent.getTurnDone() < 9)) + return null; + + Map map = ctrl.map; + boolean clear = true; + clear &= isClear(map, 4, 8); + clear &= isClear(map, 5, 8); + clear &= isClear(map, 6, 8); + clear &= isClear(map, 7, 8); + clear &= isClear(map, 8, 8); + clear &= isClear(map, 8, 7); + clear &= isClear(map, 8, 6); + boolean upLeft = clear; + clear = true; + clear &= isClear(map, 8, 6); + clear &= isClear(map, 9, 6); + clear &= isClear(map, 10, 6); + clear &= isClear(map, 11, 6); + clear &= isClear(map, 12, 6); + boolean upRight = clear; + clear = true; + clear &= isClear(map, 1, 2); + clear &= isClear(map, 2, 3); + clear &= isClear(map, 3, 3); + clear &= isClear(map, 4, 3); + clear &= isClear(map, 5, 3); + clear &= isClear(map, 6, 4); + clear &= isClear(map, 7, 4); + clear &= isClear(map, 8, 4); + boolean bottomLeft = clear; + clear &= isClear(map, 8, 4); + clear &= isClear(map, 9, 4); + clear &= isClear(map, 10, 4); + clear &= isClear(map, 11, 4); + clear = true; + boolean bottomRight = clear; + // clear &= isClear(map, 8, 6); + // clear &= isClear(map, 8, 5); + // clear &= isClear(map, 8, 4); + // clear = true; + // boolean link = clear; + + if ((!upLeft || !upRight) && (!bottomLeft || !bottomRight)) + return gePlayer; + return usPlayer; + } + + @Override + public void setup(Ctrl ctrl, Map map) + { + map.meteorology.day = Meteorology.Day.NIGHT; + + // hex row I + Zone geEntry = new Zone(map, 10); + geEntry.orientation = Orientation.NORTH_EAST; + for (int i = 0; i < 10; i++) + geEntry.add(map.getHex(i, 0)); + addEntryZone(geEntry); + + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV_HQ); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV_HQ); + addReinforcement(gePlayer, geEntry, UnitId.GE_TIGER); + addReinforcement(gePlayer, geEntry, UnitId.GE_TIGER); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_INFANTRY); + addReinforcement(gePlayer, geEntry, UnitId.GE_INFANTRY); + + // hex rows A-B + Zone usEntry = new Zone(map, 19); + usEntry.orientation = Orientation.SOUTH; + for (int i = 0; i < 10; i++) { + usEntry.add(map.getHex((4 + i), 8)); + usEntry.add(map.getHex((3 + i), 6)); + usEntry.add(map.getHex((2 + i), 4)); + usEntry.add(map.getHex((1 + i), 2)); + } + for (int i = 0; i < 9; i++) { + usEntry.add(map.getHex((4 + i), 7)); + usEntry.add(map.getHex((3 + i), 5)); + usEntry.add(map.getHex((2 + i), 3)); + } + addEntryZone(usEntry); + + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN_HQ); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN_HQ); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, UnitId.US_WOLVERINE); + addReinforcement(usPlayer, usEntry, UnitId.US_AT_GUN); + addReinforcement(usPlayer, usEntry, UnitId.US_INFANTRY); + addReinforcement(usPlayer, usEntry, UnitId.US_INFANTRY); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/battles/BattleStabToTheFlank.java b/core/src/ch/asynk/creepingarmor/game/battles/BattleStabToTheFlank.java new file mode 100644 index 0000000..d2d1457 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/battles/BattleStabToTheFlank.java @@ -0,0 +1,143 @@ +package ch.asynk.creepingarmor.game.battles; + +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.Army; +import ch.asynk.creepingarmor.game.Player; +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.HexSet; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Unit.UnitId; +import ch.asynk.creepingarmor.ui.Position; +import ch.asynk.creepingarmor.engine.Orientation; + +public class BattleStabToTheFlank extends BattleCommon +{ + public BattleStabToTheFlank(Factory factory) + { + super(factory); + name = "Stab To The Flank"; + mapType = Factory.MapType.MAP_B; + } + + @Override + public Position getHudPosition(Player player) + { + return (player.is(Army.US) ? Position.TOP_RIGHT: Position.TOP_LEFT); + } + + @Override + public Player getPlayer() + { + if (!usPlayer.isDeploymentDone() || usPlayer.getCurrentTurn() == 1) + return usPlayer; + if (usPlayer.getTurnDone() > gePlayer.getTurnDone()) + return gePlayer; + return usPlayer; + } + + public Player checkVictory(Ctrl ctrl) + { + if (ctrl.opponent.unitsLeft() == 0) + return ctrl.player; + + if ((ctrl.player.getTurnDone() < 9) || (ctrl.opponent.getTurnDone() < 9)) + return null; + + int gePoints = usPlayer.casualties(); + int usPoints = gePlayer.casualties(); + usPoints += ctrl.map.objectives.count(Army.US); + + int withdrawed = usPlayer.withdrawed(); + if (withdrawed == 0) + gePoints += 1; + else + usPoints += withdrawed; + + if (usPoints > gePoints) + return usPlayer; + else + return gePlayer; + } + + @Override + public boolean getReinforcement(Ctrl ctrl, Map map) + { + if (ctrl.player.is(Army.US)) + return false; + if (ctrl.player.getCurrentTurn() != 3) + return false; + + // hex rows I + Zone geEntry = new Zone(map, 9); + geEntry.allowedMoves = (Orientation.SOUTH_WEST.s | Orientation.NORTH_WEST.s); + for (int i = 0; i < 10; i++) + geEntry.add(map.getHex(i, 0)); + addEntryZone(geEntry); + + addReinforcement(gePlayer, geEntry, UnitId.GE_TIGER, true); + + return true; + } + + @Override + public void setup(Ctrl ctrl, Map map) + { + // F6, E6 + map.addHoldObjective(5, 3, Army.NONE); + map.addObjective(6, 4, Army.NONE); + + // hex rows D-I + Zone geEntry = new Zone(map, 57); + geEntry.orientation = Orientation.NORTH; + for (int i = 3; i < 12; i++) + geEntry.add(map.getHex(i, 5)); + for (int i = 2; i < 12; i++) + geEntry.add(map.getHex(i, 4)); + for (int i = 2; i < 11; i++) + geEntry.add(map.getHex(i, 3)); + for (int i = 1; i < 11; i++) + geEntry.add(map.getHex(i, 2)); + for (int i = 1; i < 10; i++) + geEntry.add(map.getHex(i, 1)); + for (int i = 0; i < 10; i++) + geEntry.add(map.getHex(i, 0)); + + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV_HQ); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV_HQ); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_PANZER_IV); + addReinforcement(gePlayer, geEntry, UnitId.GE_AT_GUN); + addReinforcement(gePlayer, geEntry, UnitId.GE_INFANTRY); + + // hex row I + Zone usExit = new Zone(map, 10); + usExit.orientation = Orientation.NORTH_EAST; + for (int i = 0; i < 10; i++) + geEntry.add(map.getHex(i, 0)); + addExitZone(usExit); + + // hex rows A-B + Zone usEntry = new Zone(map, 19); + usEntry.orientation = Orientation.SOUTH_EAST; + for (int i = 4; i < 13; i++) { + usEntry.add(map.getHex(i, 8)); + usEntry.add(map.getHex(i, 7)); + } + usEntry.add(map.getHex(13, 8)); + addEntryZone(usEntry); + + addReinforcement(usPlayer, usEntry, usExit, UnitId.US_SHERMAN_HQ); + addReinforcement(usPlayer, usEntry, usExit, UnitId.US_SHERMAN_HQ); + addReinforcement(usPlayer, usEntry, usExit, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, usExit, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, usExit, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, usExit, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, usExit, UnitId.US_SHERMAN); + addReinforcement(usPlayer, usEntry, usExit, UnitId.US_INFANTRY); + addReinforcement(usPlayer, usEntry, usExit, UnitId.US_INFANTRY); + addReinforcement(usPlayer, usEntry, usExit, UnitId.US_PRIEST); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/battles/BattleTest.java b/core/src/ch/asynk/creepingarmor/game/battles/BattleTest.java new file mode 100644 index 0000000..6c1d6c1 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/battles/BattleTest.java @@ -0,0 +1,128 @@ +package ch.asynk.creepingarmor.game.battles; + +import ch.asynk.creepingarmor.game.Army; +import ch.asynk.creepingarmor.game.Player; +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.HexSet; +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Unit.UnitId; +import ch.asynk.creepingarmor.ui.Position; +import ch.asynk.creepingarmor.engine.Orientation; + +public class BattleTest extends BattleCommon +{ + private Zone usExit; + + public BattleTest(Factory factory) + { + super(factory); + name = "*** Test ***"; + mapType = Factory.MapType.MAP_B; + } + + @Override + public Player getPlayer() + { + if (!gePlayer.isDeploymentDone()) + return gePlayer; + + if (gePlayer.getTurnDone() == usPlayer.getTurnDone()) + return usPlayer; + return gePlayer; + } + + @Override + public Position getHudPosition(Player player) + { + return (player.is(Army.US) ? Position.TOP_RIGHT: Position.TOP_LEFT); + } + + @Override + public Player checkVictory(Ctrl ctrl) + { + if (usPlayer.getTurnDone() > 2) + return usPlayer; + return null; + } + + @Override + public boolean getReinforcement(Ctrl ctrl, Map map) + { + if (ctrl.player.is(Army.GE)) + return false; + if (ctrl.player.getCurrentTurn() != 2) + return false; + + Zone usEntry = new Zone(map, 1); + usEntry.allowedMoves = (Orientation.SOUTH.s | Orientation.SOUTH_EAST.s | Orientation.SOUTH_WEST.s); + usEntry.add(map.getHex(12, 6)); + addEntryZone(usEntry); + addReinforcement(usPlayer, usEntry, usExit, UnitId.US_WOLVERINE); + + return true; + } + + private Unit setUnit(Map map, Player player, UnitId unitId, int col, int row, Orientation orientation, Zone exitZone) + { + return setUnit(map, player, unitId, col, row, orientation, false, exitZone); + } + + private Unit setUnit(Map map, Player player, UnitId unitId, int col, int row, Orientation orientation, boolean ace, Zone exitZone) + { + Unit u = factory.getUnit(unitId); + u.setAce(ace); + if (exitZone != null) + unitExit.put(u, exitZone); + map.setOnBoard(u, map.getHex(col, row), orientation); + return u; + } + + @Override + public void setup(Ctrl ctrl, Map map) + { + map.addObjective(6, 4, Army.NONE); + map.addHoldObjective(5, 3, Army.NONE); + map.addObjective(3, 4, Army.NONE); + map.addHoldObjective(3, 3, Army.NONE); + + ctrl.player = gePlayer; + setUnit(map, gePlayer, UnitId.GE_WESPE, 5, 8, Orientation.NORTH, null); + setUnit(map, gePlayer, UnitId.GE_TIGER, 6, 4, Orientation.NORTH, null); + setUnit(map, gePlayer, UnitId.GE_PANZER_IV, 4, 5, Orientation.NORTH_WEST, null); + setUnit(map, gePlayer, UnitId.GE_INFANTRY, 1, 2, Orientation.NORTH_WEST, null); + setUnit(map, gePlayer, UnitId.GE_KINGTIGER, 1, 1, Orientation.NORTH_WEST, null); + Zone geEntry = new Zone(map, 6); + geEntry.orientation = Orientation.NORTH; + geEntry.add(map.getHex(1, 2)); + geEntry.add(map.getHex(1, 1)); + geEntry.add(map.getHex(3, 3)); + geEntry.add(map.getHex(3, 4)); + geEntry.add(map.getHex(4, 0)); + geEntry.add(map.getHex(5, 0)); + addEntryZone(geEntry); + addReinforcement(gePlayer, geEntry, UnitId.GE_AT_GUN); + + usExit = new Zone(map, 9); + usExit.orientation = Orientation.NORTH; + usExit.add(map.getHex(11, 4)); + usExit.add(map.getHex(11, 5)); + usExit.add(map.getHex(12, 6)); + addExitZone(usExit); + + ctrl.player = usPlayer; + usPlayer.casualty(factory.getUnit(UnitId.US_SHERMAN_HQ)); + setUnit(map, usPlayer, UnitId.US_PRIEST, 10, 8, Orientation.SOUTH_EAST, usExit); + setUnit(map, usPlayer, UnitId.US_SHERMAN, 7, 3, Orientation.SOUTH, true, usExit); + setUnit(map, usPlayer, UnitId.US_SHERMAN_HQ, 8, 4, Orientation.SOUTH, usExit); + setUnit(map, usPlayer, UnitId.US_WOLVERINE, 9, 7, Orientation.SOUTH_EAST, usExit); + setUnit(map, usPlayer, UnitId.US_PERSHING, 6, 6, Orientation.NORTH_EAST, usExit); + setUnit(map, usPlayer, UnitId.US_INFANTRY, 5, 3, Orientation.NORTH_WEST, usExit); + setUnit(map, usPlayer, UnitId.US_AT_GUN, 10, 3, Orientation.SOUTH, usExit); + usPlayer.turnEnd(); + map.init(); + map.turnDone(); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/battles/Factory.java b/core/src/ch/asynk/creepingarmor/game/battles/Factory.java new file mode 100644 index 0000000..4af2536 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/battles/Factory.java @@ -0,0 +1,192 @@ +package ch.asynk.creepingarmor.game.battles; + +import com.badlogic.gdx.utils.Disposable; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; + +import ch.asynk.creepingarmor.CreepingArmor; +import ch.asynk.creepingarmor.engine.Board; +import ch.asynk.creepingarmor.game.Player; +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.Army; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Unit.UnitId; +import ch.asynk.creepingarmor.game.Unit.UnitType; +import ch.asynk.creepingarmor.game.Battle; +import ch.asynk.creepingarmor.game.battles.BattleHeadToHead; +import ch.asynk.creepingarmor.game.battles.BattleFrontalAssault; +import ch.asynk.creepingarmor.game.battles.BattleLastStand; +import ch.asynk.creepingarmor.game.battles.BattleCounterAttack; +import ch.asynk.creepingarmor.game.battles.BattleStabToTheFlank; +import ch.asynk.creepingarmor.game.battles.BattleNightAction; +import ch.asynk.creepingarmor.game.battles.BattleTest; + +public class Factory implements Board.TileBuilder, Disposable +{ + public enum MapType + { + MAP_A, + MAP_B + } + + public enum Scenarios + { + FAKE + } + + public boolean assetsLoaded; + public TextureAtlas hudAtlas; + public TextureAtlas pawnsAtlas; + public TextureAtlas pawnOverlaysAtlas; + public TextureAtlas tileOverlaysAtlas; + public Battle battles[]; + private final CreepingArmor game; + + public Factory(final CreepingArmor game) + { + this.game = game; + this.assetsLoaded = false; + battles = new Battle[] { + new BattleHeadToHead(this), + new BattleFrontalAssault(this), + new BattleLastStand(this), + new BattleCounterAttack(this), + new BattleStabToTheFlank(this), + new BattleNightAction(this), + new BattleTest(this), + }; + } + + public void assetsLoaded() + { + if (assetsLoaded) return; + int i = game.config.graphics.i; + this.hudAtlas = game.manager.get("data/hud.atlas", TextureAtlas.class); + this.tileOverlaysAtlas = game.manager.get("data/hex-overlays.atlas", TextureAtlas.class); + this.pawnsAtlas = game.manager.get(String.format("data/units%d.atlas", i), TextureAtlas.class); + this.pawnOverlaysAtlas = game.manager.get(String.format("data/unit-overlays%d.atlas", i), TextureAtlas.class); + this.assetsLoaded = true; + } + + @Override + public void dispose() + { + if (!assetsLoaded) return; + hudAtlas.dispose(); + pawnsAtlas.dispose(); + pawnOverlaysAtlas.dispose(); + tileOverlaysAtlas.dispose(); + this.assetsLoaded = false; + } + + private Board.Config config() + { + Board.Config cfg = new Board.Config(); + cfg.cols = 10; + cfg.rows = 9; + cfg.x0 = 86; + cfg.y0 = 182; + cfg.w = 189; + cfg.dw = 94; + cfg.s = 110; + cfg.dh = 53.6f; + cfg.h = cfg.s + cfg.dh; + cfg.slope = (cfg.dh / (float) cfg.dw); + + return cfg; + } + + public Map getMap(MapType t) + { + Board.Config cfg = config(); + + Map m = null; + switch(t) { + case MAP_A: + m = new MapA(game, config(), "data/map_a.png"); + break; + case MAP_B: + m = new MapB(game, config(), "data/map_b.png"); + break; + } + + return m; + } + + public Player getPlayer(Army army) + { + if (army == Army.US) + return new Player(game, Army.US, 10); + else + return new Player(game, Army.GE, 10); + } + + public Unit getUnit(UnitId id) + { + Unit u = null; + UnitType ut = UnitType.HARD_TARGET; + UnitType utHq = UnitType.HARD_TARGET_HQ; + switch(id) { + case GE_AT_GUN: + ut = UnitType.AT_GUN; + u = new Unit(Army.GE, id, ut, 3, 8, 9, 1, "ge-at-gun", "ge-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case GE_INFANTRY: + ut = UnitType.INFANTRY; + u = new Unit(Army.GE, id, ut, 1, 7, 10, 1, "ge-infantry", "ge-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case GE_KINGTIGER: + u = new Unit(Army.GE, id, ut, 3, 12, 1, "ge-kingtiger", "ge-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case GE_PANZER_IV: + u = new Unit(Army.GE, id, ut, 2, 9, 2, "ge-panzer-iv", "ge-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case GE_PANZER_IV_HQ: + u = new Unit(Army.GE, id, utHq, 2, 9, 2, "ge-panzer-iv", "ge-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case GE_TIGER: + u = new Unit(Army.GE, id, ut, 3, 11, 1, "ge-tiger", "ge-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case GE_WESPE: + ut = UnitType.ARTILLERY; + u = new Unit(Army.GE, id, ut, 5, 8, 1, "ge-wespe", "ge-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case US_AT_GUN: + ut = UnitType.AT_GUN; + u = new Unit(Army.US, id, ut, 1, 7, 10, 1, "us-at-gun", "us-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case US_INFANTRY: + ut = UnitType.INFANTRY; + u = new Unit(Army.US, id, ut, 1, 7, 10, 1, "us-infantry", "us-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case US_PERSHING: + u = new Unit(Army.US, id, ut, 3, 10, 2, "us-pershing", "us-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case US_PERSHING_HQ: + u = new Unit(Army.US, id, utHq, 3, 10, 2, "us-pershing", "us-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case US_PRIEST: + ut = UnitType.ARTILLERY; + u = new Unit(Army.US, id, ut, 5, 8, 1, "us-priest", "us-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case US_SHERMAN: + u = new Unit(Army.US, id, ut, 2, 9, 2, "us-sherman", "us-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case US_SHERMAN_HQ: + u = new Unit(Army.US, id, utHq, 2, 9, 2, "us-sherman", "us-head", pawnsAtlas, pawnOverlaysAtlas); + break; + case US_WOLVERINE: + u = new Unit(Army.US, id, ut, 3, 8, 3, "us-wolverine", "us-head", pawnsAtlas, pawnOverlaysAtlas); + break; + } + + return u; + } + + public Hex getNewTile(float x, float y, int col, int row, boolean offmap) + { + Hex hex = new Hex(x, y, col, row, tileOverlaysAtlas); + if (offmap) hex.terrain = Hex.Terrain.OFFMAP; + return hex; + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/battles/MapA.java b/core/src/ch/asynk/creepingarmor/game/battles/MapA.java new file mode 100644 index 0000000..11c2ddd --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/battles/MapA.java @@ -0,0 +1,77 @@ +package ch.asynk.creepingarmor.game.battles; + +import ch.asynk.creepingarmor.CreepingArmor; +import ch.asynk.creepingarmor.engine.Board; +import ch.asynk.creepingarmor.engine.Orientation; +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Hex; + +public class MapA extends Map +{ + public MapA(final CreepingArmor game, Board.Config cfg, String textureName) + { + super(game, cfg, textureName); + } + + @Override + protected void setup() + { + getHex(5, 1).terrain = Hex.Terrain.HILLS; + getHex(7, 3).terrain = Hex.Terrain.HILLS; + getHex(7, 8).terrain = Hex.Terrain.HILLS; + getHex(8, 8).terrain = Hex.Terrain.HILLS; + + getHex(5, 0).terrain = Hex.Terrain.WOODS; + getHex(6, 0).terrain = Hex.Terrain.WOODS; + getHex(3, 3).terrain = Hex.Terrain.WOODS; + getHex(4, 3).terrain = Hex.Terrain.WOODS; + getHex(10, 7).terrain = Hex.Terrain.WOODS; + getHex(11, 7).terrain = Hex.Terrain.WOODS; + getHex(11, 8).terrain = Hex.Terrain.WOODS; + + getHex(6, 1).terrain = Hex.Terrain.TOWN; + getHex(2, 2).terrain = Hex.Terrain.TOWN; + getHex(6, 4).terrain = Hex.Terrain.TOWN; + getHex(10, 5).terrain = Hex.Terrain.TOWN; + getHex(7, 7).terrain = Hex.Terrain.TOWN; + getHex(4, 6).terrain = Hex.Terrain.TOWN; + + getHex(10, 1).terrain = Hex.Terrain.OFFMAP; + getHex(11, 3).terrain = Hex.Terrain.OFFMAP; + getHex(12, 5).terrain = Hex.Terrain.OFFMAP; + getHex(13, 7).terrain = Hex.Terrain.OFFMAP; + + int N = Orientation.NORTH.s; + int S = Orientation.SOUTH.s; + int NE = Orientation.NORTH_EAST.s; + int NW = Orientation.NORTH_WEST.s; + int SE = Orientation.SOUTH_EAST.s; + int SW = Orientation.SOUTH_WEST.s; + + getHex(6, 1).roads = (NW | SW); + for (int i = 1; i < 11; i++) { + if (i == 6) + getHex(i, 2).roads = (NE | S | SW); + else if (i == 7) + getHex(i, 2).roads = (N | SE); + else + getHex(i, 2).roads = (N | S); + } + getHex(6, 3).roads = (NE | SW); + getHex(6, 4).roads = (N | NE | SW); + getHex(7, 4).roads = (N | S); + getHex(8, 4).roads = (NW | S); + getHex(6, 5).roads = (NE | SW); + getHex(8, 5).roads = (N | SW); + getHex(9, 5).roads = (N | S | NE); + getHex(10, 5).roads = (N | S); + getHex(11, 5).roads = (N | S); + getHex(3, 6).roads = (N | S); + getHex(4, 6).roads = (N | S); + getHex(5, 6).roads = (N | S); + getHex(6, 6).roads = (NE | NW | S); + getHex(8, 6).roads = (NE | SW); + getHex(7, 7).roads = (N | SE); + getHex(8, 7).roads = (NE | S); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/battles/MapB.java b/core/src/ch/asynk/creepingarmor/game/battles/MapB.java new file mode 100644 index 0000000..1248cea --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/battles/MapB.java @@ -0,0 +1,75 @@ +package ch.asynk.creepingarmor.game.battles; + +import ch.asynk.creepingarmor.CreepingArmor; +import ch.asynk.creepingarmor.engine.Board; +import ch.asynk.creepingarmor.engine.Orientation; +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Hex; + +public class MapB extends Map +{ + public MapB(final CreepingArmor game, Board.Config cfg, String textureName) + { + super(game, cfg, textureName); + } + + @Override + protected void setup() + { + getHex(4, 0).terrain = Hex.Terrain.HILLS; + getHex(5, 0).terrain = Hex.Terrain.HILLS; + getHex(1, 1).terrain = Hex.Terrain.HILLS; + getHex(9, 7).terrain = Hex.Terrain.HILLS; + getHex(10, 7).terrain = Hex.Terrain.HILLS; + + getHex(3, 0).terrain = Hex.Terrain.WOODS; + getHex(6, 0).terrain = Hex.Terrain.WOODS; + getHex(8, 1).terrain = Hex.Terrain.WOODS; + getHex(9, 2).terrain = Hex.Terrain.WOODS; + getHex(4, 5).terrain = Hex.Terrain.WOODS; + getHex(5, 6).terrain = Hex.Terrain.WOODS; + getHex(6, 6).terrain = Hex.Terrain.WOODS; + getHex(11, 8).terrain = Hex.Terrain.WOODS; + + getHex(1, 2).terrain = Hex.Terrain.TOWN; + getHex(5, 3).terrain = Hex.Terrain.TOWN; + getHex(6, 4).terrain = Hex.Terrain.TOWN; + getHex(7, 8).terrain = Hex.Terrain.TOWN; + + getHex(10, 1).terrain = Hex.Terrain.OFFMAP; + getHex(11, 3).terrain = Hex.Terrain.OFFMAP; + getHex(12, 5).terrain = Hex.Terrain.OFFMAP; + getHex(13, 7).terrain = Hex.Terrain.OFFMAP; + + int N = Orientation.NORTH.s; + int S = Orientation.SOUTH.s; + int NE = Orientation.NORTH_EAST.s; + int NW = Orientation.NORTH_WEST.s; + int SE = Orientation.SOUTH_EAST.s; + int SW = Orientation.SOUTH_WEST.s; + + getHex(1, 2).roads = (S | NW); + getHex(2, 3).roads = (SE | N); + getHex(3, 3).roads = (S | N); + getHex(4, 3).roads = (S | N); + getHex(5, 3).roads = (S | NW); + getHex(6, 4).roads = (SE | N); + getHex(7, 4).roads = (S | N); + getHex(8, 4).roads = (S | SW | N); + getHex(9, 4).roads = (S | N); + getHex(10, 4).roads = (S | N); + getHex(11, 4).roads = (S | N); + getHex(4, 8).roads = (S | N); + getHex(5, 8).roads = (S | N); + getHex(6, 8).roads = (S | N); + getHex(7, 8).roads = (S | N); + getHex(8, 8).roads = (S | NE); + getHex(8, 7).roads = (SW | NE); + getHex(8, 6).roads = (SW | NE | N); + getHex(8, 5).roads = (SW | NE); + getHex(9, 6).roads = (S | N); + getHex(10, 6).roads = (S | N); + getHex(11, 6).roads = (S | N); + getHex(12, 6).roads = (S | N); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/hud/ActionButtons.java b/core/src/ch/asynk/creepingarmor/game/hud/ActionButtons.java new file mode 100644 index 0000000..1e85a5e --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/hud/ActionButtons.java @@ -0,0 +1,185 @@ +package ch.asynk.creepingarmor.game.hud; + +import com.badlogic.gdx.graphics.g2d.Sprite; +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; + +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.State.StateType; +import ch.asynk.creepingarmor.ui.Widget; +import ch.asynk.creepingarmor.ui.Bg; +import ch.asynk.creepingarmor.ui.Position; + +public class ActionButtons extends Widget +{ + public static int PADDING = 5; + + private final Ctrl ctrl; + + public enum Buttons { + NONE(-1, 0), + PROMOTE(0, 1), + DONE(1, 2), + ABORT(2, 4), + LAST(3, 0); + + Buttons(int i, int b) + { + this.i = i; + this.b = b; + } + + public int i; + public int b; + } + + private Sprite bg; + private int idx; + private Bg buttons []; + private StateType states []; + + public ActionButtons(Ctrl ctrl, TextureAtlas uiAtlas, TextureAtlas hudAtlas) + { + this.bg = new Sprite(uiAtlas.findRegion("disabled")); + this.ctrl = ctrl; + this.visible = false; + this.position = Position.BOTTOM_RIGHT; + this.idx = Buttons.NONE.i; + + + this.buttons = new Bg[Buttons.LAST.i]; + this.buttons[Buttons.DONE.i] = new Bg(uiAtlas.findRegion("ok")); + this.buttons[Buttons.ABORT.i] = new Bg(uiAtlas.findRegion("cancel")); + this.buttons[Buttons.PROMOTE.i] = new Bg(hudAtlas.findRegion("promote")); + + this.states = new StateType[Buttons.LAST.i]; + this.states[Buttons.DONE.i] = StateType.DONE; + this.states[Buttons.ABORT.i] = StateType.ABORT; + this.states[Buttons.PROMOTE.i] = StateType.PROMOTE; + } + + @Override + public void dispose() + { + for (int i = 0; i < Buttons.LAST.i; i++) + buttons[i].dispose(); + } + + public void update(Position position) + { + setPosition(position); + updatePosition(); + } + + public void updatePosition() + { + if (!visible) return; + float dx = (position.getX(rect.width) - rect.x); + float dy = (position.getY(rect.height) - rect.y); + translate(dx, dy); + for (int i = 0; i < Buttons.LAST.i; i++) + buttons[i].translate(dx, dy); + } + + public void hide() + { + for (int i = 0; i < Buttons.LAST.i; i++) + buttons[i].visible = false; + this.visible = false; + } + + private float setButton(Bg btn, float x, float y) + { + btn.visible = true; + btn.setPosition(x, y); + return (y + btn.getHeight() + PADDING); + } + + public void show(int bits) + { + int b = bits; + int count = 0; + while (b > 0) { + if ((b & 0x01) == 1) + count += 1; + b /= 2; + } + + if (count == 0) { + this.visible = false; + return; + } + + rect.width = (buttons[0].getWidth() + (2 * PADDING)); + rect.height = ((buttons[0].getHeight() * count) + ((count + 1) * PADDING)); + rect.x = position.getX(rect.width); + rect.y = position.getY(rect.height); + + float x = (rect.x + PADDING); + float y = (rect.y + PADDING); + + b = 1; + for (int i = 0; i < Buttons.LAST.i; i++) { + if ((bits & b) == b) + y = setButton(buttons[i], x, y); + else + buttons[i].visible = false; + b *= 2; + } + + this.visible = true; + } + + public boolean touchDown(float x, float y) + { + idx = Buttons.NONE.i; + + if (!super.hit(x,y)) + return false; + + for (int i = 0; i < Buttons.LAST.i; i++) { + if (buttons[i].hit(x, y)) { + idx = i; + break; + } + } + + return (idx != Buttons.NONE.i); + } + + public boolean touchUp(float x, float y) + { + if (idx == Buttons.NONE.i) + return false; + + boolean ret = false; + + if (super.hit(x,y) && buttons[idx].hit(x, y)) { + ctrl.setState(states[idx]); + ret = true; + } + + idx = Buttons.NONE.i; + + return ret; + } + + @Override + public void draw(Batch batch) + { + if (!visible) return; + batch.draw(bg, rect.x, rect.y, rect.width, rect.height); + for (int i = 0; i < Buttons.LAST.i; i++) + buttons[i].draw(batch); + } + + @Override + public void drawDebug(ShapeRenderer shapes) + { + if (!visible) return; + super.drawDebug(shapes); + for (int i = 0; i < Buttons.LAST.i; i++) + buttons[i].drawDebug(shapes); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/hud/EngagementPanel.java b/core/src/ch/asynk/creepingarmor/game/hud/EngagementPanel.java new file mode 100644 index 0000000..5471dfb --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/hud/EngagementPanel.java @@ -0,0 +1,255 @@ +package ch.asynk.creepingarmor.game.hud; + +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.Sprite; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; + +import ch.asynk.creepingarmor.game.Engagement; +import ch.asynk.creepingarmor.game.Army; +import ch.asynk.creepingarmor.engine.gfx.Animation; +import ch.asynk.creepingarmor.engine.gfx.animations.DiceAnimation; +import ch.asynk.creepingarmor.ui.Bg; +import ch.asynk.creepingarmor.ui.Label; +import ch.asynk.creepingarmor.ui.Patch; +import ch.asynk.creepingarmor.ui.Position; + +public class EngagementPanel extends Patch implements Animation +{ + private enum State { ROLL1, MOVE, ROLL2, RESULT }; + + public static int FLAG_HEIGHT = 24; + public static int OK_OFFSET = 10; + public static int PADDING = 20; + public static int VSPACING = 10; + public static int HSPACING = 5; + public static float MOVE_STEP = 2f; + + private State state; + private boolean reroll; + private float rerollY; + private Sprite usFlag; + private Sprite geFlag; + private Sprite winner; + private Sprite attackImg; + private Sprite defenseImg; + private Label attack; + private Label defense; + private Label attackR; + private Label defenseR; + private Bg okBtn; + private DiceAnimation d1Animation; + private DiceAnimation d2Animation; + private DiceAnimation d3Animation; + private DiceAnimation d4Animation; + + public EngagementPanel(BitmapFont font, TextureAtlas uiAtlas, TextureAtlas hudAtlas) + { + super(uiAtlas.createPatch("typewriter")); + usFlag = new Sprite(hudAtlas.findRegion("us-flag")); + geFlag = new Sprite(hudAtlas.findRegion("ge-flag")); + attackImg = new Sprite(hudAtlas.findRegion("attack")); + defenseImg = new Sprite(hudAtlas.findRegion("defense")); + this.attack = new Label(font); + this.defense = new Label(font); + this.attackR = new Label(font); + this.defenseR = new Label(font); + this.okBtn = new Bg(uiAtlas.findRegion("ok")); + this.visible = false; + this.d1Animation = new DiceAnimation(); + this.d2Animation = new DiceAnimation(); + this.d3Animation = new DiceAnimation(); + this.d4Animation = new DiceAnimation(); + } + + public void updatePosition() + { + if (!visible) return; + float dx = (position.getX(rect.width) - rect.x); + float dy = (position.getY(rect.height) - rect.y); + translate(dx, dy); + winner.translate(dx, dy); + attackImg.translate(dx, dy); + defenseImg.translate(dx, dy); + attack.translate(dx, dy); + defense.translate(dx, dy); + attackR.translate(dx, dy); + defenseR.translate(dx, dy); + okBtn.translate(dx, dy); + d1Animation.translate(dx, dy); + d2Animation.translate(dx, dy); + d3Animation.translate(dx, dy); + d4Animation.translate(dx, dy); + } + + public void show(Engagement e, Position position, float volume) + { + DiceAnimation.initSound(volume); + attack.write(String.format(" + %d + %d =", e.unitCount, e.flankBonus)); + if (e.weatherDefense == 0) + defense.write(String.format("%d + %d =", e.unitDefense, e.terrainDefense)); + else + defense.write(String.format("%d + %d + %d =", e.unitDefense, e.terrainDefense, e.weatherDefense)); + attackR.write(String.format(" %2d", e.attackSum)); + defenseR.write(String.format(" %2d", e.defenseSum)); + if (e.success) + winner = ((e.attacker.getArmy() == Army.US) ? usFlag : geFlag); + else + winner = ((e.attacker.getArmy() == Army.US) ? geFlag : usFlag); + + this.position = position; + placeElements(); + + state = State.ROLL1; + reroll = (e.d3 != 0); + + d1Animation.set(e.d1); + d2Animation.set(e.d2); + if (reroll) { + d3Animation.set(e.d3); + d4Animation.set(e.d4); + } + + visible = true; + } + + private void placeElements() + { + float w = attackR.getWidth(); + float w2 = defenseR.getWidth(); + if (w2 > w) + w = w2; + float height = (okBtn.getHeight() + attackImg.getHeight() + defenseImg.getHeight() + (2 * VSPACING) + (2 * PADDING)); + float width = (attackImg.getWidth() + (2 * d1Animation.getWidth()) + attack.getWidth() + w + (4 * HSPACING) + (2 * PADDING)); + float x = position.getX(width); + float y = position.getY(height); + setPosition(x, y, width, height); + + okBtn.setPosition((x + width - okBtn.getWidth() + OK_OFFSET), (y - OK_OFFSET)); + + x = getX() + PADDING; + y = getY() + PADDING; + winner.setPosition((getX() + (width / 2f) - (winner.getWidth() / 2f)), y); + y += (winner.getHeight() + VSPACING); + + defenseImg.setPosition(x, y); + y = (y + (defenseImg.getHeight() / 2f) - (defense.getHeight() / 2f)); + defenseR.setPosition((getX() + width - w - PADDING), y); + // x += (defenseImg.getWidth() + HSPACING); + defense.setPosition((defenseR.getX() - defense.getWidth() - HSPACING), y); + + x = getX() + PADDING; + y += defenseImg.getHeight() + VSPACING; + attackImg.setPosition(x, y); + x += (attackImg.getWidth() + HSPACING); + d1Animation.setPosition(x, y); + d3Animation.setPosition(x, y); + x += (d1Animation.getWidth() + HSPACING); + d2Animation.setPosition(x, (y)); + d4Animation.setPosition(x, y); + x += (d1Animation.getWidth() + HSPACING); + y = (y + (attackImg.getHeight() / 2f) - (attack.getHeight() / 2f)); + attack.setPosition(x, y); + attackR.setPosition(defenseR.getX(), y); + + rerollY = (d1Animation.getY() + d1Animation.getHeight() + VSPACING); + } + + @Override + public boolean hit(float x, float y) + { + return rect.contains(x, y); + } + + @Override + public boolean animate(float delta) + { + if (!visible) return true; + if (state == State.ROLL1) { + d1Animation.animate(delta); + d2Animation.animate(delta); + if (d1Animation.isDone() && d2Animation.isDone()) { + if (reroll) + state = State.MOVE; + else + state = State.RESULT; + } + } + + if (state == State.MOVE) { + float y = (d1Animation.getY() + MOVE_STEP); + if (y >= rerollY) { + y = rerollY; + state = State.ROLL2; + } + setPosition(getX(), getY(), getWidth(), (y + d1Animation.getHeight() + VSPACING - getY())); + d1Animation.setPosition(d1Animation.getX(), y); + d2Animation.setPosition(d2Animation.getX(), y); + } + + if (state == State.ROLL2) { + if (d1Animation.getY() < rerollY) { + d1Animation.setPosition(d1Animation.getX(), (d1Animation.getY() + d1Animation.getHeight() + VSPACING)); + d2Animation.setPosition(d2Animation.getX(), (d2Animation.getY() + d2Animation.getHeight() + VSPACING)); + } else { + d3Animation.animate(delta); + d4Animation.animate(delta); + if (d3Animation.isDone() && d4Animation.isDone()) + state = State.RESULT; + } + } + + return false; + } + + @Override + public void dispose() + { + super.dispose(); + attack.dispose(); + defense.dispose(); + attackR.dispose(); + defenseR.dispose(); + d1Animation.dispose(); + d2Animation.dispose(); + d3Animation.dispose(); + d4Animation.dispose(); + okBtn.dispose(); + } + + @Override + public void draw(Batch batch) + { + if (!visible) return; + super.draw(batch); + attackImg.draw(batch); + d1Animation.draw(batch); + d2Animation.draw(batch); + if ((state == State.ROLL2) || (reroll && (state == State.RESULT))) { + d3Animation.draw(batch); + d4Animation.draw(batch); + } + attack.draw(batch); + defenseImg.draw(batch); + defense.draw(batch); + defenseR.draw(batch); + okBtn.draw(batch); + if (state == State.RESULT) { + attackR.draw(batch); + winner.draw(batch); + } + } + + @Override + public void drawDebug(ShapeRenderer shapes) + { + if (!visible) return; + super.drawDebug(shapes); + attack.drawDebug(shapes); + defense.drawDebug(shapes); + attackR.drawDebug(shapes); + defenseR.drawDebug(shapes); + okBtn.drawDebug(shapes); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/hud/PlayerInfo.java b/core/src/ch/asynk/creepingarmor/game/hud/PlayerInfo.java new file mode 100644 index 0000000..fbd843b --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/hud/PlayerInfo.java @@ -0,0 +1,202 @@ +package ch.asynk.creepingarmor.game.hud; + +import com.badlogic.gdx.utils.Disposable; + +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.Sprite; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; + +import ch.asynk.creepingarmor.engine.gfx.Animation; +import ch.asynk.creepingarmor.engine.gfx.Drawable; + +import ch.asynk.creepingarmor.game.State.StateType; +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.Hud; +import ch.asynk.creepingarmor.game.Army; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Player; +import ch.asynk.creepingarmor.ui.LabelImage; +import ch.asynk.creepingarmor.ui.Position; + +public class PlayerInfo implements Disposable, Drawable, Animation +{ + public static int PADDING = 5; + + private final Ctrl ctrl; + + private Object hit; + + private Sprite flag; + private Sprite usFlag; + private Sprite geFlag; + private LabelImage turns; + private LabelImage aps; + private LabelImage reinforcement; + public UnitDock unitDock; + private Position position; + + public PlayerInfo(Ctrl ctrl, BitmapFont font, TextureAtlas uiAtlas, TextureAtlas hudAtlas) + { + this.ctrl = ctrl; + this.position = Position.MIDDLE_CENTER; + usFlag = new Sprite(hudAtlas.findRegion("us-flag")); + geFlag = new Sprite(hudAtlas.findRegion("ge-flag")); + turns = new LabelImage(hudAtlas.findRegion("turns"), font, 5f); + aps = new LabelImage(hudAtlas.findRegion("aps"), font, 5f); + reinforcement = new LabelImage(hudAtlas.findRegion("reinforcement"), font, 5f); + unitDock = new UnitDock(ctrl, uiAtlas.findRegion("disabled"), hudAtlas.findRegion("reinforcement-selected"), 10f); + } + + @Override + public void dispose() + { + turns.dispose(); + aps.dispose(); + reinforcement.dispose(); + unitDock.dispose(); + } + + public void updatePosition() + { + float dx = (position.getX(usFlag.getWidth()) - usFlag.getX()); + float dy = (position.getY(usFlag.getHeight()) - usFlag.getY()); + usFlag.translate(dx, dy); + geFlag.translate(dx, dy); + turns.translate(dx, dy); + aps.translate(dx, dy); + reinforcement.translate(dx, dy); + unitDock.translate(dx, dy); + } + + public void setPosition(Position position) + { + if (this.position == position) + return; + this.position = position; + + float width = (usFlag.getWidth() + turns.getWidth() + aps.getWidth() + (2 * PADDING)); + float height = (usFlag.getHeight() + reinforcement.getHeight() + (1 * PADDING)); + float x = position.getX(width); + float y = position.getY(height); + + if (position.isLeft()) { + reinforcement.setPosition(x, y); + y += (reinforcement.getHeight() + PADDING); + usFlag.setPosition(x, y); + geFlag.setPosition(x, y); + x += (usFlag.getWidth() + PADDING); + turns.setPosition(x, y); + x += (turns.getWidth() + PADDING); + aps.setPosition(x, y); + } else { + x = (x + width); + reinforcement.setPosition((x - reinforcement.getWidth()), y); + y += (reinforcement.getHeight() + PADDING); + x -= usFlag.getWidth(); + usFlag.setPosition(x, y); + geFlag.setPosition(x, y); + x -= (turns.getWidth() + PADDING); + turns.setPosition(x, y); + x -= (aps.getWidth() + PADDING); + aps.setPosition(x, y); + } + aps.setLabelPosition(Position.TOP_RIGHT); + turns.setLabelPosition(Position.MIDDLE_CENTER); + reinforcement.setLabelPosition(Position.TOP_LEFT); + unitDock.setPosition(position, reinforcement.getY() - PADDING); + } + + public void update(Player player, Position position) + { + unitDock.hide(); + turns.write(String.format("%d", player.getCurrentTurn())); + aps.write(String.format("%d", player.getAp())); + int r = player.reinforcement(); + if (r == 0) { + reinforcement.visible = false; + } else { + reinforcement.visible = true; + reinforcement.write(String.format("%d", r)); + } + + if (player.is(Army.GE)) + flag = geFlag; + else + flag = usFlag; + + setPosition(position); + } + + public void blockEndOfTurn(boolean blocked) + { + turns.blocked = blocked; + } + + public boolean touchDown(float x, float y) + { + hit = null; + + if (reinforcement.hit(x, y)) + hit = reinforcement; + else if (unitDock.hit(x, y)) + hit = unitDock; + else if (turns.hit(x,y)) + hit = turns; + + return (hit != null); + } + + public boolean touchUp(float x, float y) + { + if (hit == null) + return false; + + if (hit == turns) { + if (turns.hit(x, y)) + ctrl.hud.askEndOfTurn(); + } + else if (hit == reinforcement) { + if (reinforcement.hit(x, y)) + ctrl.reinforcementHit(); + } + else if (hit == unitDock) { + if (unitDock.hit(x, y)) { + ctrl.hud.notify(unitDock.select(x, y).toString()); + ctrl.stateTouchUp(); + } + } + + hit = null; + + return true; + } + + @Override + public boolean animate(float delta) + { + unitDock.animate(delta); + return false; + } + + @Override + public void draw(Batch batch) + { + flag.draw(batch); + turns.draw(batch); + aps.draw(batch); + reinforcement.draw(batch); + unitDock.draw(batch); + } + + @Override + public void drawDebug(ShapeRenderer debugShapes) + { + turns.drawDebug(debugShapes); + aps.drawDebug(debugShapes); + reinforcement.drawDebug(debugShapes); + unitDock.drawDebug(debugShapes); + debugShapes.rect(flag.getX(), flag.getY(), flag.getWidth(), flag.getHeight()); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/hud/StatisticsPanel.java b/core/src/ch/asynk/creepingarmor/game/hud/StatisticsPanel.java new file mode 100644 index 0000000..e4a70ce --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/hud/StatisticsPanel.java @@ -0,0 +1,120 @@ +package ch.asynk.creepingarmor.game.hud; + +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; + +import ch.asynk.creepingarmor.game.Player; +import ch.asynk.creepingarmor.ui.Bg; +import ch.asynk.creepingarmor.ui.Label; +import ch.asynk.creepingarmor.ui.Patch; +import ch.asynk.creepingarmor.ui.Position; + +public class StatisticsPanel extends Patch +{ + public static int OK_OFFSET = 10; + public static int PADDING = 20; + public static int VSPACING = 10; + public static int HSPACING = 10; + + private Label title; + private Label header; + private Label stats1; + private Label stats2; + private Bg okBtn; + + public StatisticsPanel(BitmapFont font, TextureAtlas atlas) + { + super(atlas.createPatch("typewriter")); + this.title = new Label(font); + this.header = new Label(font); + this.stats1 = new Label(font); + this.stats2 = new Label(font); + this.okBtn = new Bg(atlas.findRegion("ok")); + this.visible = false; + this.header.write("\nActions\nUnits Left\nUnits Withrawed\nCasualties\nWon Attacks\nLost Attacks"); + } + + public void updatePosition() + { + if (!visible) return; + float dx = (position.getX(rect.width) - rect.x); + float dy = (position.getY(rect.height) - rect.y); + translate(dx, dy); + title.translate(dx, dy); + header.translate(dx, dy); + stats1.translate(dx, dy); + stats2.translate(dx, dy); + okBtn.translate(dx, dy); + } + + public void show(Player winner, Player loser, Position position) + { + title.write(winner.getName() + " player won the battle in " + winner.getTurnDone() + " turns."); + stats1.write(winner.getStats()); + stats2.write(loser.getStats()); + + float height = (title.getHeight() + header.getHeight() + (2 * PADDING) + (1 * VSPACING)); + float width = (header.getWidth() + stats1.getWidth() + stats2.getWidth() + (2 * PADDING) + (4 * HSPACING)); + float w2 = (title.getWidth() + (2 * PADDING)); + if (w2 > width) width = w2; + float x = position.getX(width); + float y = position.getY(height); + setPosition(x, y, width, height); + + okBtn.setPosition((x + width - okBtn.getWidth() + OK_OFFSET), (y - OK_OFFSET)); + + y += PADDING; + x += PADDING; + header.setPosition(x, y); + stats1.setPosition((x + header.getWidth() + (2 * HSPACING)), y); + stats2.setPosition((stats1.getX() + stats1.getWidth() + (2 * HSPACING)), y); + y += (header.getHeight() + VSPACING); + title.setPosition(x, y); + visible = true; + } + + @Override + public boolean hit(float x, float y) + { + if (okBtn.hit(x, y)) + return true; + return false; + } + + @Override + public void dispose() + { + super.dispose(); + title.dispose(); + header.dispose(); + stats1.dispose(); + stats2.dispose(); + okBtn.dispose(); + } + + @Override + public void draw(Batch batch) + { + if (!visible) return; + super.draw(batch); + title.draw(batch); + header.draw(batch); + stats1.draw(batch); + stats2.draw(batch); + okBtn.draw(batch); + } + + @Override + public void drawDebug(ShapeRenderer shapes) + { + if (!visible) return; + super.drawDebug(shapes); + title.drawDebug(shapes); + header.drawDebug(shapes); + stats1.drawDebug(shapes); + stats2.drawDebug(shapes); + okBtn.drawDebug(shapes); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/hud/UnitDock.java b/core/src/ch/asynk/creepingarmor/game/hud/UnitDock.java new file mode 100644 index 0000000..b4a2f6f --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/hud/UnitDock.java @@ -0,0 +1,226 @@ +package ch.asynk.creepingarmor.game.hud; + +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.graphics.g2d.Sprite; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; +import com.badlogic.gdx.math.Vector3; +import com.badlogic.gdx.math.Matrix4; +import com.badlogic.gdx.math.Rectangle; + +import ch.asynk.creepingarmor.engine.gfx.Animation; +import ch.asynk.creepingarmor.engine.Orientation; +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.UnitList; +import ch.asynk.creepingarmor.ui.Bg; +import ch.asynk.creepingarmor.ui.Position; + +public class UnitDock extends Bg implements Animation +{ + private static final float SCALE = 0.4f; + private static final float STEP = 5f; + private final Ctrl ctrl; + + private int n; + private float y; + private float to; + private float dx; + private float step; + private boolean show; + private boolean mvtDone; + public Unit selectedUnit; + private Sprite selected; + private UnitList units; + private Vector3 point; + private Matrix4 saved; + private Matrix4 transform; + private Rectangle scaledRect; + + public UnitDock(Ctrl ctrl, TextureRegion region, TextureRegion selected, float padding) + { + super(region); + this.ctrl = ctrl; + this.padding = padding; + this.mvtDone = true; + this.point = new Vector3(); + this.saved = new Matrix4(); + this.transform = new Matrix4(); + this.scaledRect = new Rectangle(); + this.selected = new Sprite(selected); + this.visible = false; + this.dx = 0f; + } + + @Override + public void translate(float _dx, float _dy) + { + this.y += _dy; + if (!visible) return; + super.translate(_dx, _dy); + for (Unit unit : units) + unit.translate(_dx, _dy); + to = position.getX(rect.width * SCALE); + transform.idt(); + transform.translate((rect.x + dx), (rect.y + rect.height), 0).scale(SCALE, SCALE, 0).translate(-rect.x, - (rect.y + rect.height), 0); + point.set(rect.x, rect.y, 0).mul(transform); + scaledRect.x = point.x; + scaledRect.y = point.y; + point.set((rect.x + rect.width), (rect.y + rect.height), 0).mul(transform); + scaledRect.width = point.x - scaledRect.x; + scaledRect.height = point.y - scaledRect.y; + } + + public void setPosition(Position position, float y) + { + if (this.position == position) + return; + this.position = position; + this.y = y; + this.step = (position.isLeft() ? STEP : -STEP); + this.mvtDone = true; + this.visible = false; + this.dx = 0f; + } + + @Override + public void dispose() + { + super.dispose(); + } + + @Override + public boolean hit(float x, float y) + { + return (visible && scaledRect.contains(x, y)); + } + + public Unit select(float x, float y) + { + int i = (int) ((scaledRect.y + scaledRect.height - y) / (scaledRect.height / units.size())); + selectedUnit = units.get(i); + return selectedUnit; + } + + public void hide() + { + if (!visible) return; + resize(); + to = rect.x; + + show = false; + mvtDone = false; + selectedUnit = null; + } + + public void show() + { + if (!resize()) + return; + to = position.getX(rect.width * SCALE); + + show = true; + mvtDone = false; + selectedUnit = null; + visible = true; + } + + private boolean resize() + { + int count = ctrl.player.reinforcement(); + if (count == 0) { + n = 0; + return false; + } + if (count == n) return true; + n = count; + + units = ctrl.player.reinforcement; + rect.width = units.get(0).getWidth() + (2 * padding); + rect.height = ((units.get(0).getHeight() * n) + ((n + 1) * padding)); + float scaledWidth = (rect.width * SCALE); + to = position.getX(scaledWidth); + rect.x = to + (position.isLeft() ? -scaledWidth : scaledWidth); + rect.y = y - rect.height; + + float px = rect.x; + float py = rect.y + rect.height; + float ph = units.get(0).getHeight(); + for (Unit unit : units) { + py -= (ph + padding); + // unit.setPosition(px, py, Orientation.SOUTH.r()); + unit.centerOn((px + (rect.width / 2)), py + (ph / 2)); + unit.setRotation(position.isLeft() ? Orientation.NORTH.r() : Orientation.SOUTH.r()); + } + + return true; + } + + @Override + public boolean animate(float delta) + { + if (!visible) return true; + if (mvtDone) return true; + + float x = (rect.x + dx); + if (show) { + if ((position.isLeft() && (x < to)) || (!position.isLeft() && x > to)) + dx += step; + else { + dx = (to - rect.x); + mvtDone = true; + } + } else { + if ((position.isLeft() && (x > to)) || (!position.isLeft() && x < to)) + dx -= step; + else { + dx = (to - rect.x); + mvtDone = true; + visible = false; + } + } + + transform.idt(); + transform.translate((rect.x + dx), (rect.y + rect.height), 0).scale(SCALE, SCALE, 0).translate(-rect.x, - (rect.y + rect.height), 0); + point.set(rect.x, rect.y, 0).mul(transform); + scaledRect.x = point.x; + scaledRect.y = point.y; + point.set((rect.x + rect.width), (rect.y + rect.height), 0).mul(transform); + scaledRect.width = point.x - scaledRect.x; + scaledRect.height = point.y - scaledRect.y; + return false; + } + + @Override + public void draw(Batch batch) + { + if (!visible) return; + + saved.set(batch.getTransformMatrix()); + batch.setTransformMatrix(transform); + + super.draw(batch); + for (Unit unit : units) { + unit.draw(batch); + if (unit == selectedUnit) { + selected.setCenter((unit.getX() + (unit.getWidth() / 2)), (unit.getY() + (unit.getHeight() / 2))); + selected.draw(batch); + } + } + + batch.setTransformMatrix(saved); + } + + @Override + public void drawDebug(ShapeRenderer shapes) + { + if (!visible) return; + + saved.set(shapes.getTransformMatrix()); + shapes.setTransformMatrix(transform); + + shapes.rect(rect.x, rect.y, rect.width, rect.height); + + shapes.setTransformMatrix(saved); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/states/StateAnimation.java b/core/src/ch/asynk/creepingarmor/game/states/StateAnimation.java new file mode 100644 index 0000000..512169a --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/states/StateAnimation.java @@ -0,0 +1,37 @@ +package ch.asynk.creepingarmor.game.states; + +public class StateAnimation extends StateCommon +{ + @Override + public void enter(StateType prevState) + { + ctrl.hud.actionButtons.hide(); + } + + @Override + public void leave(StateType nextState) + { + } + + @Override + public StateType abort() + { + return StateType.ABORT; + } + + @Override + public StateType execute() + { + return StateType.DONE; + } + + @Override + public void touchDown() + { + } + + @Override + public void touchUp() + { + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/states/StateBreak.java b/core/src/ch/asynk/creepingarmor/game/states/StateBreak.java new file mode 100644 index 0000000..3b474a5 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/states/StateBreak.java @@ -0,0 +1,90 @@ +package ch.asynk.creepingarmor.game.states; + +import ch.asynk.creepingarmor.engine.Orientation; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.hud.ActionButtons.Buttons; + +import ch.asynk.creepingarmor.CreepingArmor; + +public class StateBreak extends StateCommon +{ + private Orientation o = Orientation.KEEP; + + @Override + public void enter(StateType prevState) + { + activeUnit = null; + ctrl.hud.actionButtons.show(Buttons.DONE.b); + ctrl.hud.pushNotify("Break move possible"); + map.showBreakUnits(); + } + + @Override + public void leave(StateType nextState) + { + map.hideBreakUnits(); + map.hideMove(to); + map.hideDirections(to); + map.hideOrientation(to); + if (activeUnit != null) map.hideMove(activeUnit.getHex()); + } + + @Override + public StateType abort() + { + return StateType.ABORT; + } + + @Override + public StateType execute() + { + return StateType.DONE; + } + + @Override + public void touchDown() + { + } + + @Override + public void touchUp() + { + // TODO : cancel preview move before showing rotation + if (activeUnit == null) { + Unit unit = upHex.getUnit(); + if (map.breakUnits.contains(unit)) { + activeUnit = unit; + map.showMove(upHex); + map.showMove(to); + map.showDirections(to); + map.hideBreakUnits(); + } + } else { + o = Orientation.fromAdj(to, upHex); + + if (o == Orientation.KEEP) return; + + if (ctrl.cfg.mustValidate) { + map.hideDirections(to); + map.showOrientation(to, o); + ctrl.hud.actionButtons.show(Buttons.DONE.b); + } else { + doRotation(o); + ctrl.setState(StateType.ANIMATION); + } + } + } + + private void doRotation(Orientation o) + { + if (activeUnit == null) return; + + map.pathBuilder.init(activeUnit); + if (map.pathBuilder.build(to) == 1) { + map.pathBuilder.orientation = o; + map.moveUnit(activeUnit); + ctrl.setAfterAnimationState(StateType.DONE); + } else + CreepingArmor.debug("That's very wrong there should be only one path"); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/states/StateCommon.java b/core/src/ch/asynk/creepingarmor/game/states/StateCommon.java new file mode 100644 index 0000000..8ac4ab7 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/states/StateCommon.java @@ -0,0 +1,68 @@ +package ch.asynk.creepingarmor.game.states; + +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.State; + +import ch.asynk.creepingarmor.CreepingArmor; + +public abstract class StateCommon implements State +{ + protected static Ctrl ctrl; + protected static Map map; + + protected static Hex selectedHex = null; + protected static Hex downHex = null; + protected static Hex upHex = null; + protected static Hex to = null; + + protected boolean isEnemy; + protected static Unit activeUnit; + protected static Unit selectedUnit; + + protected StateCommon() + { + } + + public StateCommon(Ctrl ctrl, Map map) + { + this.ctrl = ctrl; + this.map = map; + } + + @Override + public boolean downInMap(float x, float y) + { + downHex = map.getHexAt(x, y); + return (downHex != null); + } + + @Override + public boolean upInMap(float x, float y) + { + upHex = map.getHexAt(x, y); + return (upHex != null); + } + + protected boolean hasUnit() + { + return (selectedUnit != null); + } + + protected void showPossibilities(Unit unit) + { + if (ctrl.cfg.showMoves && unit.canMove()) map.showPossibleMoves(); + if (ctrl.cfg.showTargets && unit.canEngage()) map.showPossibleTargets(); + if (ctrl.cfg.showMoveAssists && unit.canMove()) map.showMoveableUnits(); + unit.enableOverlay(Unit.MOVE, false); + } + + protected void hidePossibilities() + { + map.hidePossibleMoves(); + map.hidePossibleTargets(); + map.hideMoveableUnits(); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/states/StateDeployment.java b/core/src/ch/asynk/creepingarmor/game/states/StateDeployment.java new file mode 100644 index 0000000..891156f --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/states/StateDeployment.java @@ -0,0 +1,138 @@ +package ch.asynk.creepingarmor.game.states; + +import ch.asynk.creepingarmor.engine.Orientation; +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.UnitList; +import ch.asynk.creepingarmor.game.hud.ActionButtons.Buttons; + +import ch.asynk.creepingarmor.CreepingArmor; + +public class StateDeployment extends StateCommon +{ + private boolean completed; + private Zone entryZone; + private UnitList deployedUnits = new UnitList(10); + + @Override + public void enter(StateType prevState) + { + if (selectedHex != null) + map.unselectHex(selectedHex); + completed = false; + entryZone = null; + selectedHex = null; + selectedUnit = null; + ctrl.hud.actionButtons.hide(); + ctrl.hud.playerInfo.unitDock.show(); + } + + @Override + public void leave(StateType nextState) + { + selectedUnit = null; + if (selectedHex != null) + map.unselectHex(selectedHex); + if (entryZone != null) + entryZone.enable(Hex.AREA, false); + ctrl.hud.playerInfo.unitDock.hide(); + } + + @Override + public StateType abort() + { + if (activeUnit != null) + undo(); + return StateType.DEPLOYMENT; + } + + @Override + public StateType execute() + { + deployedUnits.clear(); + return StateType.DONE; + } + + @Override + public void touchDown() + { + } + + @Override + public void touchUp() + { + Unit unit = ctrl.hud.playerInfo.unitDock.selectedUnit; + if (!completed && (unit != null) && (unit != activeUnit)) { + showEntryZone(unit); + } else if (selectedUnit != null) { + doRotation(Orientation.fromAdj(selectedHex, upHex)); + } else if (!completed && (entryZone != null) && (upHex != null)) { + if (upHex.isEmpty() && entryZone.contains(upHex)) + unitEnter(activeUnit); + } else { + unit = downHex.getUnit(); + if (deployedUnits.contains(unit)) { + showRotation(unit, downHex); + activeUnit = unit; + } + } + } + + private void showEntryZone(Unit unit) + { + activeUnit = unit; + if (entryZone != null) entryZone.enable(Hex.AREA, false); + entryZone = ctrl.battle.getEntryZone(activeUnit); + entryZone.enable(Hex.AREA, true); + } + + private void undo() + { + map.unselectHex(selectedHex); + map.hideDirections(selectedHex); + map.revertEnter(activeUnit); + activeUnit = null; + selectedUnit = null; + ctrl.hud.update(); + } + + private void unitEnter(Unit unit) + { + selectedUnit = unit; + selectedHex = upHex; + ctrl.player.reinforcement.remove(unit); + map.showOnBoard(unit, upHex, entryZone.orientation); + deployedUnits.add(unit); + entryZone.enable(Hex.AREA, false); + showRotation(unit, upHex); + ctrl.hud.update(); + } + + private void showRotation(Unit unit, Hex hex) + { + selectedUnit = unit; + selectedHex = hex; + map.selectHex(selectedHex); + map.showDirections(selectedHex); + ctrl.hud.playerInfo.unitDock.hide(); + ctrl.hud.actionButtons.show(Buttons.ABORT.b); + } + + private void doRotation(Orientation o) + { + map.unselectHex(selectedHex); + map.hideDirections(selectedHex); + + if (o != Orientation.KEEP) + map.setOnBoard(selectedUnit, selectedHex, o); + + ctrl.hud.actionButtons.hide(); + ctrl.hud.playerInfo.unitDock.show(); + entryZone = null; + activeUnit = null; + selectedUnit = null; + if (ctrl.checkDeploymentDone()) + completed = true; + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/states/StateEngage.java b/core/src/ch/asynk/creepingarmor/game/states/StateEngage.java new file mode 100644 index 0000000..a9dc074 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/states/StateEngage.java @@ -0,0 +1,105 @@ +package ch.asynk.creepingarmor.game.states; + +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.hud.ActionButtons.Buttons; + +import ch.asynk.creepingarmor.CreepingArmor; + +public class StateEngage extends StateCommon +{ + @Override + public void enter(StateType prevState) + { + map.possibleTargets.clear(); + ctrl.hud.actionButtons.show(ctrl.cfg.canCancel ? Buttons.ABORT.b : 0); + + // activeUnit is the target + if (prevState == StateType.SELECT) { + activeUnit = null; + // use selectedHex and selectedUnit + map.hidePossibleTargets(); + map.collectPossibleTargets(selectedUnit, ctrl.opponent.units); + map.showPossibleTargets(); + if (to != null) { + // quick fire -> replay touchUp + upHex = to; + touchUp(); + } + selectedUnit.showAttack(); + map.selectHex(selectedHex); + } else + CreepingArmor.debug("should not happen"); + } + + @Override + public void leave(StateType nextState) + { + selectedUnit.hideAttack(); + map.hideAttackAssists(); + map.hidePossibleTargets(); + map.unselectHex(selectedHex); + if (to != null) + map.unselectHex(to); + } + + @Override + public StateType abort() + { + map.activatedUnits.clear(); + return StateType.ABORT; + } + + @Override + public StateType execute() + { + StateType nextState = StateType.DONE; + if (map.engageUnit(selectedUnit, activeUnit)) { + ctrl.player.wonEngagementCount += 1; + ctrl.opponent.casualty(activeUnit); + if (map.breakUnits.size() > 0) { + nextState = StateType.BREAK; + } + } else { + ctrl.player.lostEngagementCount += 1; + } + + activeUnit.showTarget(); + ctrl.setAfterAnimationState(nextState); + return StateType.ANIMATION; + } + + @Override + public void touchDown() + { + } + + @Override + public void touchUp() + { + Unit unit = upHex.getUnit(); + + // activeUnit is the target, selectedTarget is the engagement leader + if (unit == selectedUnit) { + ctrl.setState(StateType.ABORT); + } else if ((activeUnit == null) && map.possibleTargets.contains(unit)) { + // ctrl.hud.notify("Engage " + unit); + map.hidePossibleTargets(); + to = upHex; + activeUnit = unit; + activeUnit.showTarget(); + map.collectAttackAssists(selectedUnit, activeUnit, ctrl.player.units); + map.showAttackAssists(); + ctrl.hud.actionButtons.show((ctrl.cfg.mustValidate ? Buttons.DONE.b : 0) | (ctrl.cfg.canCancel ? Buttons.ABORT.b : 0)); + } + else if (unit == activeUnit) { + ctrl.setState(StateType.DONE); + } + else if ((activeUnit != null) && map.engagementAssists.contains(unit)) { + map.toggleAttackAssist(unit); + // if(map.toggleAttackAssist(unit)) + // ctrl.hud.notify(unit + " will fire"); + // else + // ctrl.hud.notify(unit + " wont fire"); + } + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/states/StateMove.java b/core/src/ch/asynk/creepingarmor/game/states/StateMove.java new file mode 100644 index 0000000..9a586e3 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/states/StateMove.java @@ -0,0 +1,193 @@ +package ch.asynk.creepingarmor.game.states; + +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.hud.ActionButtons.Buttons; + +public class StateMove extends StateCommon +{ + @Override + public void enter(StateType prevState) + { + ctrl.hud.actionButtons.show( + ((map.activatedUnits.size() > 0) ? Buttons.DONE.b : 0) + | (ctrl.cfg.canCancel ? Buttons.ABORT.b : 0)); + + if (prevState == StateType.WITHDRAW) { + if (map.pathBuilder.size() == 1) + ctrl.setState(StateType.ROTATE); + return; + } + + map.pathBuilder.clear(); + + if (prevState == StateType.SELECT) { + // use selectedHex and selectedUnit + activeUnit = selectedUnit; + activeUnit.showMoveable(); + map.pathBuilder.init(activeUnit); + map.collectAndShowMovesAndAssits(activeUnit); + if (to != null) { + // quick move -> replay touchUp + upHex = to; + touchUp(); + } else + checkExit(activeUnit, activeUnit.getHex()); + } else { + // back from rotation -> chose next Pawn + if (selectedUnit.canMove()) { + changeUnit(selectedUnit); + } else { + changeUnit(map.moveableUnits.get(0)); + } + } + + activeUnit.enableOverlay(Unit.MOVE, false); + } + + @Override + public void leave(StateType nextState) + { + if (nextState == StateType.WITHDRAW) + return; + + // hide all but assists : want them when in rotation + activeUnit.hideMoveable(); + map.hidePossibleMoves(); + map.unselectHex(activeUnit.getHex()); + if (to != null) + map.hidePath(to); + + if (nextState != StateType.SELECT) { + if (to == null) + to = activeUnit.getHex(); + } + } + + @Override + public StateType abort() + { + hideAssists(); + if (activeUnit.justEntered()) { + map.revertEnter(activeUnit); + return StateType.ABORT; + } + int n = map.activatedUnits.size(); + if (n == 0) + return StateType.ABORT; + map.revertMoves(); + return StateType.ANIMATION; + } + + @Override + public StateType execute() + { + hideAssists(); + // be sure that the hq is activated + if (selectedUnit.canMove() && (map.activatedUnits.size() > 0)) + selectedUnit.setMoved(); + + return StateType.DONE; + } + + @Override + public void touchDown() + { + } + + @Override + public void touchUp() + { + if (upHex == activeUnit.getHex()) { + if (to != null) + map.hidePath(to); + to = null; + map.pathBuilder.clear(); + ctrl.setState(StateType.ROTATE); + return; + } + + int s = map.pathBuilder.size(); + + Unit unit = upHex.getUnit(); + + if (map.moveableUnits.contains(unit)) { + if(unit != activeUnit) + changeUnit(unit); + } else if ((s == 0) && map.possibleMoves.contains(upHex)) { + s = collectPaths(upHex); + } else if (map.pathBuilder.contains(upHex)) { + s = togglePoint(downHex, s); + } + + if (s == 1) { + if (!checkExit(activeUnit, upHex)) + ctrl.setState(StateType.ROTATE); + } + } + + private void hideAssists() + { + map.hideMoveableUnits(); + } + + private void changeUnit(Unit unit) + { + if (activeUnit != null ) { + map.unselectHex(activeUnit.getHex()); + if (activeUnit.canMove()) + activeUnit.enableOverlay(Unit.MOVE, true); + } + activeUnit = unit; + Hex hex = activeUnit.getHex(); + map.pathBuilder.init(activeUnit, hex); + activeUnit.showMoveable(); + map.hidePossibleMoves(); + map.collectPossibleMoves(activeUnit); + map.showPossibleMoves(); + map.selectHex(hex); + activeUnit.enableOverlay(Unit.MOVE, false); + ctrl.hud.notify(activeUnit.toString()); + checkExit(activeUnit, hex); + } + + private int collectPaths(Hex hex) + { + to = hex; + int s = map.pathBuilder.build(to); + map.showMove(to); + map.hidePossibleMoves(); + map.showPathBuilder(); + return s; + } + + private int togglePoint(Hex hex, int s) + { + if (hex == activeUnit.getHex()) { + // + } else if (hex == to) { + // + } else { + map.hidePathBuilder(); + map.togglePathOverlay(hex); + s = map.togglePathBuilderHex(hex); + map.showPathBuilder(); + } + + return s; + } + + private boolean checkExit(Unit unit, Hex hex) + { + if ((hex == unit.getHex()) && (unit.justEntered())) + return false; + Zone exitZone = ctrl.battle.getExitZone(unit); + if ((exitZone == null) || !exitZone.contains(hex)) + return false; + if ((unit.getHex() != hex) && !map.pathBuilder.canExit(exitZone.orientation)) + return false; + ctrl.setState(StateType.WITHDRAW); + return true; + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/states/StatePromote.java b/core/src/ch/asynk/creepingarmor/game/states/StatePromote.java new file mode 100644 index 0000000..92a0d7b --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/states/StatePromote.java @@ -0,0 +1,42 @@ +package ch.asynk.creepingarmor.game.states; + +import ch.asynk.creepingarmor.game.Unit; + +public class StatePromote extends StateCommon +{ + @Override + public void enter(StateType prevState) + { + ctrl.setAfterAnimationState(StateType.DONE); + ctrl.setState(StateType.ANIMATION); + map.promoteUnit(selectedUnit); + } + + @Override + public void leave(StateType nextState) + { + map.unselectHex(selectedHex); + } + + @Override + public StateType abort() + { + return StateType.ABORT; + } + + @Override + public StateType execute() + { + return StateType.DONE; + } + + @Override + public void touchDown() + { + } + + @Override + public void touchUp() + { + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/states/StateReinforcement.java b/core/src/ch/asynk/creepingarmor/game/states/StateReinforcement.java new file mode 100644 index 0000000..4d0cce4 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/states/StateReinforcement.java @@ -0,0 +1,87 @@ +package ch.asynk.creepingarmor.game.states; + +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.hud.ActionButtons.Buttons; + +public class StateReinforcement extends StateCommon +{ + private Zone entryZone; + + @Override + public void enter(StateType prevState) + { + map.clearAll(); + if (selectedHex != null) + map.unselectHex(selectedHex); + entryZone = null; + selectedHex = null; + ctrl.hud.playerInfo.unitDock.show(); + } + + @Override + public void leave(StateType nextState) + { + if (selectedHex != null) + map.unselectHex(selectedHex); + if (entryZone != null) + entryZone.enable(Hex.AREA, false); + ctrl.hud.playerInfo.unitDock.hide(); + } + + @Override + public StateType abort() + { + return StateType.ABORT; + } + + @Override + public StateType execute() + { + return StateType.DONE; + } + + @Override + public void touchDown() + { + } + + @Override + public void touchUp() + { + Unit unit = ctrl.hud.playerInfo.unitDock.selectedUnit; + if ((unit != null) && (unit != activeUnit)) + changeUnit(unit); + else if ((entryZone != null) && upHex.isEmpty() && entryZone.contains(upHex)) + unitEnter(activeUnit); + else + ctrl.setState(StateType.SELECT); + } + + private void changeUnit(Unit unit) + { + activeUnit = unit; + if (entryZone != null) + entryZone.enable(Hex.AREA, false); + entryZone = ctrl.battle.getEntryZone(activeUnit); + entryZone.enable(Hex.AREA, true); + ctrl.hud.actionButtons.show(((ctrl.cfg.canCancel) ? Buttons.ABORT.b : 0)); + } + + private void unitEnter(Unit unit) + { + selectedUnit = unit; + selectedHex = upHex; + map.selectHex(selectedHex); + entryZone.enable(Hex.AREA, false); + if (map.enterBoard(unit, upHex, entryZone.allowedMoves)) { + if (unit.getMovementPoints() > 0) + ctrl.setState(StateType.MOVE); + else + ctrl.setState(StateType.ROTATE); + } else { + ctrl.hud.notify("Can not enter the map at that position"); + } + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/states/StateRotate.java b/core/src/ch/asynk/creepingarmor/game/states/StateRotate.java new file mode 100644 index 0000000..4f8cf39 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/states/StateRotate.java @@ -0,0 +1,111 @@ +package ch.asynk.creepingarmor.game.states; + +import ch.asynk.creepingarmor.engine.Orientation; +import ch.asynk.creepingarmor.game.hud.ActionButtons.Buttons; + +import ch.asynk.creepingarmor.CreepingArmor; + +public class StateRotate extends StateCommon +{ + private boolean rotateOnly; + private boolean rotationSet; + + @Override + public void enter(StateType prevState) + { + ctrl.hud.actionButtons.show((ctrl.cfg.canCancel && (map.moveableUnits.size() > 1))? Buttons.ABORT.b : 0); + + if (activeUnit == null) + activeUnit = selectedUnit; + if (to == null) + to = activeUnit.getHex(); + + if (!map.pathBuilder.isSet()) { + map.pathBuilder.init(activeUnit); + map.pathBuilder.build(to); + } + + if (map.pathBuilder.size() != 1) + CreepingArmor.debug("ERROR: pathBuilder.size() == " + map.pathBuilder.size()); + + rotateOnly = (to == activeUnit.getHex()); + + if (!rotateOnly) + map.showPath(to); + map.selectHex(activeUnit.getHex()); + map.showDirections(to); + + rotationSet = false; + } + + @Override + public void leave(StateType nextState) + { + map.unselectHex(activeUnit.getHex()); + map.hidePath(to); + map.hideDirections(to); + map.hideOrientation(to); + map.pathBuilder.clear(); + to = null; + } + + @Override + public StateType abort() + { + StateType nextState = StateType.ABORT; + ctrl.hud.actionButtons.hide(); + if (activeUnit.justEntered()) { + map.revertEnter(activeUnit); + nextState = StateType.ABORT; + } else if (map.activatedUnits.size() == 0) { + map.hideMoveableUnits(); + } else { + nextState = StateType.MOVE; + } + return nextState; + } + + @Override + public StateType execute() + { + StateType whenDone = StateType.DONE; + + if (map.moveUnit(activeUnit) > 0) + whenDone = StateType.MOVE; + + ctrl.setAfterAnimationState(whenDone); + return StateType.ANIMATION; + } + + @Override + public void touchDown() + { + } + + @Override + public void touchUp() + { + if (rotationSet) return; + + Orientation o = Orientation.fromAdj(to, upHex); + if (o == Orientation.KEEP) { + ctrl.setState(StateType.ABORT); + return; + } + + if (!activeUnit.justEntered() && rotateOnly && (o == activeUnit.getOrientation())) + return; + + map.pathBuilder.orientation = o; + rotationSet = true; + + if (ctrl.cfg.mustValidate) { + map.hideDirections(to); + map.showOrientation(to, o); + ctrl.hud.actionButtons.show(Buttons.DONE.b | ((ctrl.cfg.canCancel) ? Buttons.ABORT.b : 0)); + } else { + execute(); + ctrl.setState(StateType.ANIMATION); + } + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/states/StateSelect.java b/core/src/ch/asynk/creepingarmor/game/states/StateSelect.java new file mode 100644 index 0000000..a0863e1 --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/states/StateSelect.java @@ -0,0 +1,132 @@ +package ch.asynk.creepingarmor.game.states; + +import ch.asynk.creepingarmor.game.Map; +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.Unit; +import ch.asynk.creepingarmor.game.Ctrl; +import ch.asynk.creepingarmor.game.hud.ActionButtons.Buttons; + +import ch.asynk.creepingarmor.CreepingArmor; + +public class StateSelect extends StateCommon +{ + public StateSelect(Ctrl ctrl, Map map) + { + super(ctrl, map); + } + + @Override + public void enter(StateType prevState) + { + to = null; + selectedHex = null; + selectedUnit = null; + activeUnit = null; + map.clearAll(); + ctrl.hud.actionButtons.hide(); + } + + @Override + public void leave(StateType nextState) + { + hidePossibilities(); + } + + @Override + public StateType abort() + { + if (selectedHex != null) + map.unselectHex(selectedHex); + hidePossibilities(); + map.clearAll(); + return StateType.ABORT; + } + + @Override + public StateType execute() + { + return StateType.DONE; + } + + @Override + public void touchDown() + { + } + + @Override + public void touchUp() + { + if (!isEnemy) { + if (map.possibleMoves.contains(upHex)) { + // quick move + to = upHex; + ctrl.setState(StateType.MOVE); + return; + } + if (map.possibleTargets.contains(upHex.getUnit())) { + // quick fire + to = upHex; + ctrl.setState(StateType.ENGAGE); + return; + } + } + + if (selectedHex != null) + map.unselectHex(selectedHex); + + hidePossibilities(); + if (upHex.isOffMap()) { + selectedUnit = null; + return; + } + + Unit unit = upHex.getUnit(); + + if (unit == null) { + isEnemy = false; + ctrl.hud.actionButtons.hide(); + map.clearAll(); + selectedUnit = null; + return; + } + + isEnemy = ctrl.player.isEnemy(unit); + if (!isEnemy && (unit == selectedUnit) && unit.canMove()) { + if (unit.isHq()) { + ctrl.hud.notify("HQ activation"); + select(upHex, unit, isEnemy); + ctrl.setState(StateType.MOVE); + } else { + // quick rotate + to = upHex; + ctrl.setState(StateType.ROTATE); + } + } else { + select(upHex, unit, isEnemy); + ctrl.hud.notify(selectedUnit.toString()); + } + } + + private void select(Hex hex, Unit unit, boolean isEnemy) + { + selectedHex = hex; + selectedUnit = unit; + + if (isEnemy && !ctrl.cfg.showEnemyPossibilities) + return; + + int moves = map.collectPossibleMoves(selectedUnit); + int targets = map.collectPossibleTargets(selectedUnit, (isEnemy ? ctrl.player.units : ctrl.opponent.units)); + + if (moves > 0) + map.collectMoveableUnits(selectedUnit); + + if ((moves > 0) || (targets > 0)) { + map.selectHex(selectedHex); + showPossibilities(selectedUnit); + } + + ctrl.hud.actionButtons.show((ctrl.player.canPromote(selectedUnit)) ? Buttons.PROMOTE.b : 0 ); + CreepingArmor.debug("Select", selectedHex.toString() + " " + selectedUnit + (isEnemy ? " enemy " : " friend ")); + } +} diff --git a/core/src/ch/asynk/creepingarmor/game/states/StateWithdraw.java b/core/src/ch/asynk/creepingarmor/game/states/StateWithdraw.java new file mode 100644 index 0000000..3d0dfdd --- /dev/null +++ b/core/src/ch/asynk/creepingarmor/game/states/StateWithdraw.java @@ -0,0 +1,71 @@ +package ch.asynk.creepingarmor.game.states; + +import ch.asynk.creepingarmor.game.Zone; +import ch.asynk.creepingarmor.game.Hex; +import ch.asynk.creepingarmor.game.Unit; + +public class StateWithdraw extends StateCommon +{ + @Override + public void enter(StateType prevState) + { + ctrl.hud.askExitBoard(); + } + + @Override + public void leave(StateType nextState) + { + } + + @Override + public StateType abort() + { + return StateType.MOVE; + } + + @Override + public StateType execute() + { + if (activeUnit == null) + activeUnit = selectedUnit; + + ctrl.setAfterAnimationState(withdraw(activeUnit)); + return StateType.ANIMATION; + } + + @Override + public void touchDown() + { + } + + @Override + public void touchUp() + { + } + + private StateType withdraw(Unit unit) + { + Zone exitZone = ctrl.battle.getExitZone(unit); + Hex hex = unit.getHex(); + + // rotation + if (map.pathBuilder.to == null) + map.pathBuilder.build(hex); + + Hex exitHex = (Hex) map.pathBuilder.to; + if (!exitZone.contains(exitHex)) + throw new RuntimeException(String.format("%s not in exitZone", exitHex)); + + map.pathBuilder.setExit(exitZone.orientation); + + unit.hideMoveable(); + if (to != null) + map.hidePath(to); + map.hidePossibleMoves(); + map.unselectHex(hex); + + if (map.exitBoard(unit) > 0) + return StateType.MOVE; + return StateType.DONE; + } +} |