diff options
| author | Jérémy Zurcher <jeremy@asynk.ch> | 2015-12-08 15:28:46 +0100 | 
|---|---|---|
| committer | Jérémy Zurcher <jeremy@asynk.ch> | 2015-12-08 15:28:46 +0100 | 
| commit | 8922e2eae49a9ca7616620f93936d4f99dd47911 (patch) | |
| tree | cdc34326ede0ab17fe50405674309fd3e7712091 /core/src/ch | |
| parent | d592633b2f9dcf00ca8851d7d4a91147b0866fef (diff) | |
| download | RustAndDust-8922e2eae49a9ca7616620f93936d4f99dd47911.zip RustAndDust-8922e2eae49a9ca7616620f93936d4f99dd47911.tar.gz | |
Map: split into smaller inherited classes
Diffstat (limited to 'core/src/ch')
| -rw-r--r-- | core/src/ch/asynk/rustanddust/game/Map.java | 648 | ||||
| -rw-r--r-- | core/src/ch/asynk/rustanddust/game/battles/Map00.java | 1 | ||||
| -rw-r--r-- | core/src/ch/asynk/rustanddust/game/map/Map0Hex.java | 107 | ||||
| -rw-r--r-- | core/src/ch/asynk/rustanddust/game/map/Map1Units.java | 117 | ||||
| -rw-r--r-- | core/src/ch/asynk/rustanddust/game/map/Map2Moves.java | 91 | ||||
| -rw-r--r-- | core/src/ch/asynk/rustanddust/game/map/Map3Animations.java | 143 | ||||
| -rw-r--r-- | core/src/ch/asynk/rustanddust/game/map/Map4Commands.java | 236 | 
7 files changed, 778 insertions, 565 deletions
| diff --git a/core/src/ch/asynk/rustanddust/game/Map.java b/core/src/ch/asynk/rustanddust/game/Map.java index ecb24d0..b31410f 100644 --- a/core/src/ch/asynk/rustanddust/game/Map.java +++ b/core/src/ch/asynk/rustanddust/game/Map.java @@ -1,610 +1,130 @@  package ch.asynk.rustanddust.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.rustanddust.RustAndDust; -import ch.asynk.rustanddust.engine.Pawn; -import ch.asynk.rustanddust.engine.Board; -import ch.asynk.rustanddust.engine.Tile; -import ch.asynk.rustanddust.engine.Faction; -import ch.asynk.rustanddust.engine.Move; +  import ch.asynk.rustanddust.engine.SelectedTile; -import ch.asynk.rustanddust.engine.ObjectiveSet; -import ch.asynk.rustanddust.engine.OrderList; -import ch.asynk.rustanddust.engine.Orientation;  import ch.asynk.rustanddust.engine.Meteorology; -import ch.asynk.rustanddust.engine.PathBuilder; -import ch.asynk.rustanddust.engine.gfx.Moveable; -import ch.asynk.rustanddust.engine.gfx.Animation; -import ch.asynk.rustanddust.engine.gfx.animations.AnimationSequence; -import ch.asynk.rustanddust.engine.gfx.animations.DiceAnimation; -import ch.asynk.rustanddust.engine.gfx.animations.FireAnimation; -import ch.asynk.rustanddust.engine.gfx.animations.TankFireAnimation; -import ch.asynk.rustanddust.engine.gfx.animations.InfantryFireAnimation; -import ch.asynk.rustanddust.engine.gfx.animations.PromoteAnimation; -import ch.asynk.rustanddust.engine.gfx.animations.DestroyAnimation; -import ch.asynk.rustanddust.engine.gfx.animations.SoundAnimation; -import ch.asynk.rustanddust.engine.gfx.animations.RunnableAnimation; -import ch.asynk.rustanddust.engine.gfx.animations.MoveToAnimation.MoveToAnimationCb; - -import ch.asynk.rustanddust.ui.Position; +import ch.asynk.rustanddust.game.Unit; +import ch.asynk.rustanddust.game.Engagement; +import ch.asynk.rustanddust.game.map.Map4Commands; -public abstract class Map extends Board implements MoveToAnimationCb, ObjectiveSet.ObjectiveCb +public abstract class Map extends Map4Commands  { -    private final Ctrl ctrl; - -    protected final HexSet moves; -    protected final PathBuilder paths; - -    protected final UnitList moveableUnits; -    protected final UnitList targetUnits; -    protected final UnitList assistUnits; -    protected final UnitList breakthroughUnits; -    protected final UnitList activatedUnits; - -    protected final ObjectiveSet objectives;      protected 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; - -    public abstract void init(); -    public abstract void turnDone(); -    public abstract void actionDone(); -    protected abstract void setup(); -    protected abstract void resolveEngagement(Engagement e); -    protected abstract int engagementCost(Engagement e); -      public Map(final RustAndDust game, String map, String hex)      { -        super(game.factory, game.manager.get(map, Texture.class), -                new SelectedTile(game.manager.get(hex, Texture.class), new float[] {.1f, .1f, .1f, .1f, .3f, .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) +        super(game, +                game.manager.get(map, Texture.class), +                new SelectedTile(game.manager.get(hex, Texture.class), new float[] {.1f, .1f, .1f, .1f, .3f, .1f} )                  ); -        setup(); - -        moves = new HexSet(this, 40); -        paths = new PathBuilder(this, 10, 20, 5, 10); - -        moveableUnits = new UnitList(6); -        targetUnits = new UnitList(10); -        assistUnits = new UnitList(6); -        breakthroughUnits = new UnitList(4); -        activatedUnits = new UnitList(7); - -        objectives = new ObjectiveSet(this, 4); -          meteorology = new Meteorology(); -        commands = new OrderList(); -    } - -    @Override -    public void dispose() -    { -        super.dispose(); -        clearAll(); -        destroy.dispose(); -        paths.dispose(); -        DiceAnimation.free(); -        PromoteAnimation.free(); -        FireAnimation.free(); -        commands.dispose(); -        Command.clearPool(); -        Engagement.clearPool(); -    } - -    public void clearAll() -    { -        moves.clear(); -        paths.clear(); -        moveableUnits.clear(); -        targetUnits.clear(); -        assistUnits.clear(); -        breakthroughUnits.clear(); -        activatedUnits.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 boolean movesContains(Hex hex) -    { -        return moves.contains(hex); -    } - -    public int movesCollect(Unit unit) -    { -        if (unit.canMove()) -            return collectPossibleMoves(unit, moves.asTiles()); - -        moves.clear(); -        return 0;      } -    public void pathsClear()                        { paths.clear(); } - -    public int pathsSize()                          { return paths.size(); } - -    public void pathsInit(Unit unit)                { paths.init(unit); } - -    public void pathsInit(Unit unit, Hex hex)       { paths.init(unit, hex); } - -    public boolean pathsIsSet()                     { return paths.isSet(); } - -    public boolean pathsCanExit(Orientation o)      { return paths.canExit(o); } - -    public void pathsSetExit(Orientation o)         { paths.setExit(o); } - -    public int pathsBuild(Hex hex)                  { return pathsBuild(hex); } - -    public boolean pathsContains(Hex hex)           { return paths.contains(hex); } - -    public void pathsSetOrientation(Orientation o)  { paths.orientation = o; } - -    public Hex pathsTo()                            { return (Hex) paths.to; } - -    public int pathsChooseOne()                     { return paths.choosePath(); } - -    public int pathsToggleHex(Hex hex) +    public void init()      { -        boolean enable = !hex.isOverlayEnabled(Hex.MOVE); -        enableOverlayOn(hex, Hex.MOVE, enable); -        return paths.toggleCtrlTile(hex); +        actionDone();      } -    public int collectTargets(Unit unit, UnitList foes) +    public void turnDone()      { -        if (unit.canEngage()) -            return collectPossibleTargets(unit, foes.asPawns(), targetUnits.asPawns()); +        RustAndDust.debug("TurnDone", String.format(" Processed Commands : %d", commandsSize())); -        targetUnits.clear(); -        return 0; -    } - -    public int collectMoveable(Unit unit) -    { -        if (unit.canHQMove()) -                collectMoveAssists(unit, moveableUnits.asPawns()); -        else -            moveableUnits.clear(); +        if (objectives.modifiedCount() > 0) +            throw new RuntimeException("objectives not cleared"); -        if (unit.canMove()) -            moveableUnits.add(unit); - -        return moveableUnits.size(); +        // FIXME do something with these Commands +        commandsClear();      } -    public int collectAssists(Unit unit, Unit target, UnitList units) +    public void actionDone()      { -        int s = collectAttackAssists(unit, target, units.asPawns(), assistUnits.asPawns()); -        activatedUnits.add(unit); -        return s; +        objectives.forget();      } -    public boolean toggleAssist(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 collectUpdate(Unit unit) -    { -        movesHide(); -        unitsMoveableHide(); -        movesCollect(unit); -        collectMoveable(unit); -        movesShow(); -        unitsMoveableShow(); -        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) +    public void clearAll()      { -        unclaim(getHexAt(x, y)); +        clearMoves(); +        clearUnits();      }      @Override -    public void moveToAnimationDone(Moveable moveable, float x, float y, float r) -    { -    } - -    // <- implement MoveToAnimationCb - -    private int process(Unit unit, Move move) -    { -        RustAndDust.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) +    protected int engagementCost(Engagement e)      { -        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); +        if ((activatedUnits.size() == 1) && e.attacker.isA(Unit.UnitType.AT_GUN) && e.defender.isHardTarget()) +            return 0;          return 1;      } -    private int process(Command cmd) -    { -        RustAndDust.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; -    } - -    protected int commandsSize() { return commands.size(); } -    protected void commandsClear() { commands.dispose(); } - -    // 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, paths.getExitMove())); -    } - -    public int moveUnit(final Unit unit) -    { -        return process(getMoveCommand(unit, paths.getMove())); -    } - -    public void revertMoves() -    { -        for (Unit unit: activatedUnits) { -            RustAndDust.debug("    revertMove() " + unit); -            revertLastPawnMove(unit); -            commands.dispose(unit, Command.CommandType.MOVE); -        } -        activatedUnits.clear(); -        objectives.revert(this); -    } - -    public void revertEnter(final Unit unit) -    { -        RustAndDust.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; +    protected void resolveEngagement(Engagement e) +    { +        int dice = e.d1 + e.d2; + +        int distance = 0; +        boolean mayReroll = false; +        boolean night = meteorology.isNight(); +        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.preventDefenseOn(e.defender.getTile())) +                terrainBonus = false; +            if (night) { +                if (distance < unit.attackDistance()) +                    distance = unit.attackDistance(); +            }          } -        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)); +        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;          } -    } - -    private int doEngagement(Engagement e) -    { -        breakthroughUnits.clear(); -        activatedUnits.clear(); - -        activatedUnits.add(e.attacker); -        for (Unit u : e.assists) -            activatedUnits.add(u); - -        for (Unit u : activatedUnits) { -            u.engage(); -            if (u.canBreak()) -                breakthroughUnits.add(u); +        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 (e.success) { -            unclaim(e.defender.getHex()); -            removePawn(e.defender); -            destroy.set(2f, e.defender); -            addAnimation(destroy); +        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;          } -        ctrl.hud.engagementSummary(e); -        addEngagementAnimation(e.defender); - -        if (engagementCost(e) == 0) -            activatedUnits.clear(); - -        return (e.success ? 1 : 0); -    } - -    public Unit unitsMoveableGet(int i) { return moveableUnits.get(i); } - -    public void unitsTargetClear()      { targetUnits.clear(); } -    public void unitsActivatedClear()   { activatedUnits.clear(); } - -    public int unitsActivatedSize()     { return activatedUnits.size(); } -    public int unitsMoveableSize()      { return moveableUnits.size(); } -    public int unitsBreakThroughSize()  { return breakthroughUnits.size(); } - -    public boolean unitsTargetContains(Unit unit)       { return targetUnits.contains(unit); } -    public boolean unitsAssistContains(Unit unit)       { return assistUnits.contains(unit); } -    public boolean unitsMoveableContains(Unit unit)     { return moveableUnits.contains(unit); } -    public boolean unitsBreakThroughContains(Unit unit) { return breakthroughUnits.contains(unit); } - -    public void unitsTargetShow()       { unitsShowOverlay(targetUnits, Unit.TARGET, true); } -    public void unitsTargetHide()       { unitsShowOverlay(targetUnits, Unit.TARGET, false); } -    public void unitsAssistShow()       { unitsShowOverlay(assistUnits, Unit.MAY_FIRE, true); } -    public void unitsAssistHide()       { unitsShowOverlay(assistUnits, Unit.MAY_FIRE, false); unitsShowOverlay(assistUnits, Unit.FIRE, false); } -    public void unitsMoveableShow()     { unitsShowOverlay(moveableUnits, Unit.MOVE, true); } -    public void unitsMoveableHide()     { unitsShowOverlay(moveableUnits, Unit.MOVE, false); } -    public void unitsBreakThroughShow() { unitsShowOverlay(breakthroughUnits, Unit.MOVE, true); } -    public void unitsBreakThroughHide() { unitsShowOverlay(breakthroughUnits, Unit.MOVE, false); } - -    private void unitsShowOverlay(UnitList units, int overlay, boolean on) -    { -        for (Unit unit : units) -            unit.enableOverlay(overlay, on); -    } - -    public void movesShow()             { moves.enable(Hex.AREA, true); } -    public void movesHide()             { moves.enable(Hex.AREA, false); } -    public void pathsShow()             { paths.enable(Hex.AREA, true); } -    public void pathsHide()             { paths.enable(Hex.AREA, false); } -    public void pathShow(Hex dst)       { paths.enable(Hex.MOVE, true); hexMoveShow(dst); } -    public void pathHide(Hex dst)       { paths.enable(Hex.MOVE, false); hexMoveHide(dst); } - -    public void hexSelect(Hex hex)          { selectedTile.set(hex); } -    public void hexUnselect(Hex hex)        { selectedTile.hide(); } -    public void hexMoveShow(Hex hex)        { enableOverlayOn(hex, Hex.MOVE, true); } -    public void hexMoveHide(Hex hex)        { enableOverlayOn(hex, Hex.MOVE, false); } -    public void hexDirectionsShow(Hex hex)  { enableOverlayOn(hex, Hex.DIRECTIONS, true); } -    public void hexDirectionsHide(Hex hex)  { enableOverlayOn(hex, Hex.DIRECTIONS, false); } -    public void hexExitShow(Hex hex)        { enableOverlayOn(hex, Hex.EXIT, true); } -    public void hexExitHide(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; -        } +        e.set(cnt, flk, def, tdf, wdf); +        e.success = success;      }  } diff --git a/core/src/ch/asynk/rustanddust/game/battles/Map00.java b/core/src/ch/asynk/rustanddust/game/battles/Map00.java index a9126bd..da1b664 100644 --- a/core/src/ch/asynk/rustanddust/game/battles/Map00.java +++ b/core/src/ch/asynk/rustanddust/game/battles/Map00.java @@ -31,7 +31,6 @@ public class Map00 extends MapRules          return cfg;      } -    @Override      protected void setup()      {          getHex(6, 1).terrain = Hex.Terrain.HILLS; diff --git a/core/src/ch/asynk/rustanddust/game/map/Map0Hex.java b/core/src/ch/asynk/rustanddust/game/map/Map0Hex.java new file mode 100644 index 0000000..6ed0783 --- /dev/null +++ b/core/src/ch/asynk/rustanddust/game/map/Map0Hex.java @@ -0,0 +1,107 @@ +package ch.asynk.rustanddust.game.map; + +import com.badlogic.gdx.graphics.Texture; + +import ch.asynk.rustanddust.RustAndDust; +import ch.asynk.rustanddust.engine.Tile; +import ch.asynk.rustanddust.engine.Board; +import ch.asynk.rustanddust.engine.Faction; +import ch.asynk.rustanddust.engine.SelectedTile; +import ch.asynk.rustanddust.engine.ObjectiveSet; +import ch.asynk.rustanddust.game.Hex; +import ch.asynk.rustanddust.game.Army; + +public abstract class Map0Hex extends Board implements ObjectiveSet.ObjectiveCb +{ +    protected final RustAndDust game; +    protected final ObjectiveSet objectives; + +    public Map0Hex(final RustAndDust game, Texture map, SelectedTile hex) +    { +        super(game.factory, map, hex); + +        this.game = game; +        objectives = new ObjectiveSet(this, 4); +    } + +    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); +    } + +    protected void claim(Hex hex, Army army) +    { +        showObjective(hex, objectives.claim(hex, army)); +    } + +    protected void unclaim(Hex hex) +    { +        showObjective(hex, objectives.unclaim(hex)); +    } + +    public void hexSelect(Hex hex)          { selectedTile.set(hex); } +    public void hexUnselect(Hex hex)        { selectedTile.hide(); } +    public void hexMoveShow(Hex hex)        { enableOverlayOn(hex, Hex.MOVE, true); } +    public void hexMoveHide(Hex hex)        { enableOverlayOn(hex, Hex.MOVE, false); } +    public void hexDirectionsShow(Hex hex)  { enableOverlayOn(hex, Hex.DIRECTIONS, true); } +    public void hexDirectionsHide(Hex hex)  { enableOverlayOn(hex, Hex.DIRECTIONS, false); } +    public void hexExitShow(Hex hex)        { enableOverlayOn(hex, Hex.EXIT, true); } +    public void hexExitHide(Hex hex)        { enableOverlayOn(hex, Hex.EXIT, false); } + +    protected void showObjective(Hex hex, Army army, boolean hold) +    { +        if (hold) +            enableOverlayOn(hex, Hex.OBJECTIVE_HOLD, true); +        else +            enableOverlayOn(hex, Hex.OBJECTIVE, true); +    } + +    @Override +    public void showObjective(Tile tile, Faction faction) +    { +        showObjective((Hex) tile, (Army) faction); +    } + +    protected 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/rustanddust/game/map/Map1Units.java b/core/src/ch/asynk/rustanddust/game/map/Map1Units.java new file mode 100644 index 0000000..7812d50 --- /dev/null +++ b/core/src/ch/asynk/rustanddust/game/map/Map1Units.java @@ -0,0 +1,117 @@ +package ch.asynk.rustanddust.game.map; + +import com.badlogic.gdx.graphics.Texture; + +import ch.asynk.rustanddust.RustAndDust; +import ch.asynk.rustanddust.engine.SelectedTile; +import ch.asynk.rustanddust.game.Unit; +import ch.asynk.rustanddust.game.UnitList; + +public abstract class Map1Units extends Map0Hex +{ +    protected final UnitList moveableUnits; +    protected final UnitList targetUnits; +    protected final UnitList assistUnits; +    protected final UnitList breakthroughUnits; +    protected final UnitList activatedUnits; + +    public Map1Units(final RustAndDust game, Texture map, SelectedTile hex) +    { +        super(game, map, hex); + +        moveableUnits = new UnitList(6); +        targetUnits = new UnitList(10); +        assistUnits = new UnitList(6); +        breakthroughUnits = new UnitList(4); +        activatedUnits = new UnitList(7); +    } + +    @Override +    public void dispose() +    { +        clearUnits(); +        super.dispose(); +    } + +    public void clearUnits() +    { +        moveableUnits.clear(); +        targetUnits.clear(); +        assistUnits.clear(); +        breakthroughUnits.clear(); +        activatedUnits.clear(); +    } + +    public int collectMoveable(Unit unit) +    { +        if (unit.canHQMove()) +                collectMoveAssists(unit, moveableUnits.asPawns()); +        else +            moveableUnits.clear(); + +        if (unit.canMove()) +            moveableUnits.add(unit); + +        return moveableUnits.size(); +    } + +    public int collectTargets(Unit unit, UnitList foes) +    { +        if (unit.canEngage()) +            return collectPossibleTargets(unit, foes.asPawns(), targetUnits.asPawns()); + +        targetUnits.clear(); +        return 0; +    } + +    public int collectAssists(Unit unit, Unit target, UnitList units) +    { +        int s = collectAttackAssists(unit, target, units.asPawns(), assistUnits.asPawns()); +        activatedUnits.add(unit); +        return s; +    } + +    public boolean toggleAssist(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 Unit unitsMoveableGet(int i) { return moveableUnits.get(i); } + +    public void unitsTargetClear()      { targetUnits.clear(); } +    public void unitsActivatedClear()   { activatedUnits.clear(); } + +    public int unitsActivatedSize()     { return activatedUnits.size(); } +    public int unitsMoveableSize()      { return moveableUnits.size(); } +    public int unitsBreakThroughSize()  { return breakthroughUnits.size(); } + +    public boolean unitsTargetContains(Unit unit)       { return targetUnits.contains(unit); } +    public boolean unitsAssistContains(Unit unit)       { return assistUnits.contains(unit); } +    public boolean unitsMoveableContains(Unit unit)     { return moveableUnits.contains(unit); } +    public boolean unitsBreakThroughContains(Unit unit) { return breakthroughUnits.contains(unit); } + +    public void unitsTargetShow()       { unitsShowOverlay(targetUnits, Unit.TARGET, true); } +    public void unitsTargetHide()       { unitsShowOverlay(targetUnits, Unit.TARGET, false); } +    public void unitsAssistShow()       { unitsShowOverlay(assistUnits, Unit.MAY_FIRE, true); } +    public void unitsAssistHide()       { unitsShowOverlay(assistUnits, Unit.MAY_FIRE, false); unitsShowOverlay(assistUnits, Unit.FIRE, false); } +    public void unitsMoveableShow()     { unitsShowOverlay(moveableUnits, Unit.MOVE, true); } +    public void unitsMoveableHide()     { unitsShowOverlay(moveableUnits, Unit.MOVE, false); } +    public void unitsBreakThroughShow() { unitsShowOverlay(breakthroughUnits, Unit.MOVE, true); } +    public void unitsBreakThroughHide() { unitsShowOverlay(breakthroughUnits, Unit.MOVE, false); } + +    private void unitsShowOverlay(UnitList units, int overlay, boolean on) +    { +        for (Unit unit : units) +            unit.enableOverlay(overlay, on); +    } +} diff --git a/core/src/ch/asynk/rustanddust/game/map/Map2Moves.java b/core/src/ch/asynk/rustanddust/game/map/Map2Moves.java new file mode 100644 index 0000000..e626655 --- /dev/null +++ b/core/src/ch/asynk/rustanddust/game/map/Map2Moves.java @@ -0,0 +1,91 @@ +package ch.asynk.rustanddust.game.map; + +import com.badlogic.gdx.graphics.Texture; + +import ch.asynk.rustanddust.RustAndDust; +import ch.asynk.rustanddust.engine.SelectedTile; +import ch.asynk.rustanddust.engine.Orientation; +import ch.asynk.rustanddust.engine.PathBuilder; +import ch.asynk.rustanddust.game.Map; +import ch.asynk.rustanddust.game.Unit; +import ch.asynk.rustanddust.game.Hex; +import ch.asynk.rustanddust.game.HexSet; + +public abstract class Map2Moves extends Map1Units +{ +    protected final HexSet moves; +    protected final PathBuilder paths; + +    public Map2Moves(final RustAndDust game, Texture map, SelectedTile hex) +    { +        super(game, map, hex); + +        moves = new HexSet(this, 40); +        paths = new PathBuilder(this, 10, 20, 5, 10); +    } + +    @Override +    public void dispose() +    { +        clearMoves(); +        super.dispose(); +        paths.dispose(); +    } + +    public void clearMoves() +    { +        moves.clear(); +        paths.clear(); +    } + +    public boolean movesContains(Hex hex) +    { +        return moves.contains(hex); +    } + +    public int movesCollect(Unit unit) +    { +        if (unit.canMove()) +            return collectPossibleMoves(unit, moves.asTiles()); + +        moves.clear(); +        return 0; +    } + +    public void collectUpdate(Unit unit) +    { +        movesHide(); +        unitsMoveableHide(); +        movesCollect(unit); +        collectMoveable(unit); +        movesShow(); +        unitsMoveableShow(); +        activatedUnits.clear(); +    } + +    public int pathsSize()                          { return paths.size(); } +    public void pathsClear()                        { paths.clear(); } +    public void pathsInit(Unit unit)                { paths.init(unit); } +    public void pathsInit(Unit unit, Hex hex)       { paths.init(unit, hex); } +    public int pathsBuild(Hex hex)                  { return pathsBuild(hex); } +    public Hex pathsTo()                            { return (Hex) paths.to; } +    public void pathsSetOrientation(Orientation o)  { paths.orientation = o; } +    public boolean pathsIsSet()                     { return paths.isSet(); } +    public boolean pathsCanExit(Orientation o)      { return paths.canExit(o); } +    public void pathsSetExit(Orientation o)         { paths.setExit(o); } +    public boolean pathsContains(Hex hex)           { return paths.contains(hex); } +    public int pathsChooseOne()                     { return paths.choosePath(); } +    public int pathsToggleHex(Hex hex) +    { +        boolean enable = !hex.isOverlayEnabled(Hex.MOVE); +        enableOverlayOn(hex, Hex.MOVE, enable); +        return paths.toggleCtrlTile(hex); +    } + +    public void movesShow()             { moves.enable(Hex.AREA, true); } +    public void movesHide()             { moves.enable(Hex.AREA, false); } +    public void pathsShow()             { paths.enable(Hex.AREA, true); } +    public void pathsHide()             { paths.enable(Hex.AREA, false); } +    public void pathShow(Hex dst)       { paths.enable(Hex.MOVE, true); hexMoveShow(dst); } +    public void pathHide(Hex dst)       { paths.enable(Hex.MOVE, false); hexMoveHide(dst); } +} diff --git a/core/src/ch/asynk/rustanddust/game/map/Map3Animations.java b/core/src/ch/asynk/rustanddust/game/map/Map3Animations.java new file mode 100644 index 0000000..2b3badf --- /dev/null +++ b/core/src/ch/asynk/rustanddust/game/map/Map3Animations.java @@ -0,0 +1,143 @@ +package ch.asynk.rustanddust.game.map; + +import com.badlogic.gdx.audio.Sound; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; + +import ch.asynk.rustanddust.RustAndDust; +import ch.asynk.rustanddust.engine.SelectedTile; +import ch.asynk.rustanddust.engine.gfx.Moveable; +import ch.asynk.rustanddust.engine.gfx.animations.AnimationSequence; +import ch.asynk.rustanddust.engine.gfx.animations.DiceAnimation; +import ch.asynk.rustanddust.engine.gfx.animations.FireAnimation; +import ch.asynk.rustanddust.engine.gfx.animations.TankFireAnimation; +import ch.asynk.rustanddust.engine.gfx.animations.InfantryFireAnimation; +import ch.asynk.rustanddust.engine.gfx.animations.PromoteAnimation; +import ch.asynk.rustanddust.engine.gfx.animations.DestroyAnimation; +import ch.asynk.rustanddust.engine.gfx.animations.SoundAnimation; +import ch.asynk.rustanddust.engine.gfx.animations.RunnableAnimation; +import ch.asynk.rustanddust.engine.gfx.animations.MoveToAnimation.MoveToAnimationCb; +import ch.asynk.rustanddust.game.Hex; +import ch.asynk.rustanddust.game.Unit; +import ch.asynk.rustanddust.game.Ctrl; +import ch.asynk.rustanddust.game.Army; +import ch.asynk.rustanddust.game.Player; + +public abstract class Map3Animations extends Map2Moves implements MoveToAnimationCb +{ +    protected final DestroyAnimation destroyAnimation; +    private final Sound tankMoveSound; +    private final Sound infantryMoveSound; +    private Sound sound; +    private long soundId = -1; + +    public Map3Animations(final RustAndDust game, Texture map, SelectedTile hex) +    { +        super(game, map, hex); + +        this.destroyAnimation = 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) +                ); +    } + +    @Override +    public void dispose() +    { +        super.dispose(); +        destroyAnimation.dispose(); +        tankMoveSound.dispose(); +        infantryMoveSound.dispose(); +        DiceAnimation.free(); +        PromoteAnimation.free(); +        FireAnimation.free(); +    } + +    // -> 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 + +    protected void addPromoteAnimation(final Unit unit, final Player player, final Runnable after) +    { +        Hex hex = unit.getHex(); +        AnimationSequence seq = AnimationSequence.get(2); +        seq.addAnimation(PromoteAnimation.get((unit.getArmy() == Army.US), hex.getX(), hex.getY(), game.config.fxVolume)); +        seq.addAnimation(RunnableAnimation.get(unit, after)); +        addAnimation(seq); +    } + +    protected void addDestroyAnimation(Unit unit) +    { +        destroyAnimation.set(2f, unit); +        addAnimation(destroyAnimation); +    } + +    protected 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(game.config.fxVolume, from.getX(), from.getY(), to.getX(), to.getY(), halfWidth)); +            else +                addAnimation(TankFireAnimation.get(game.config.fxVolume, from.getX(), from.getY(), to.getX(), to.getY(), halfWidth)); +        } +    } + +    protected void playMoveSound(Unit unit) +    { +        if (unit.isA(Unit.UnitType.INFANTRY)) +            sound = infantryMoveSound; +        else +            sound = tankMoveSound; +        soundId = sound.play(game.config.fxVolume); +    } + +    @Override +    protected void animationsOver() +    { +        if (soundId >= 0) { +            addAnimation( SoundAnimation.get(SoundAnimation.Action.FADE_OUT, sound, soundId, game.config.fxVolume, 0.5f)); +            soundId = -1; +            return; +        } +        game.ctrl.animationsOver(); +    } +} diff --git a/core/src/ch/asynk/rustanddust/game/map/Map4Commands.java b/core/src/ch/asynk/rustanddust/game/map/Map4Commands.java new file mode 100644 index 0000000..b17f1e3 --- /dev/null +++ b/core/src/ch/asynk/rustanddust/game/map/Map4Commands.java @@ -0,0 +1,236 @@ +package ch.asynk.rustanddust.game.map; + +import com.badlogic.gdx.graphics.Texture; + +import ch.asynk.rustanddust.RustAndDust; +import ch.asynk.rustanddust.engine.Move; +import ch.asynk.rustanddust.engine.SelectedTile; +import ch.asynk.rustanddust.engine.OrderList; +import ch.asynk.rustanddust.engine.Orientation; +import ch.asynk.rustanddust.game.Hex; +import ch.asynk.rustanddust.game.Unit; +import ch.asynk.rustanddust.game.Player; +import ch.asynk.rustanddust.game.Command; +import ch.asynk.rustanddust.game.Engagement; + +public abstract class Map4Commands extends Map3Animations +{ +    private OrderList commands; + +    protected abstract int engagementCost(Engagement e); +    protected abstract void resolveEngagement(Engagement e); + +    public Map4Commands(final RustAndDust game, Texture map, SelectedTile hex) +    { +        super(game, map, hex); + +        commands = new OrderList(); +    } + +    @Override +    public void dispose() +    { +        super.dispose(); +        commands.dispose(); +        Command.clearPool(); +        Engagement.clearPool(); +    } + +    protected int commandsSize() { return commands.size(); } +    protected void commandsClear() { commands.dispose(); } + +    // 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, paths.getExitMove())); +    } + +    public int moveUnit(final Unit unit) +    { +        return process(getMoveCommand(unit, paths.getMove())); +    } + +    public void revertMoves() +    { +        for (Unit unit: activatedUnits) { +            RustAndDust.debug("    revertMove() " + unit); +            revertLastPawnMove(unit); +            commands.dispose(unit, Command.CommandType.MOVE); +        } +        activatedUnits.clear(); +        objectives.revert(this); +    } + +    public void revertEnter(final Unit unit) +    { +        RustAndDust.debug("    revertEnter() "+ unit); +        removePawn(unit); +        objectives.revert(this); +        game.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(game.ctrl.player); +        cmd.setEngage(unit, target); +        return (process(cmd) == 1); +    } + +    public void promoteUnit(final Unit unit) +    { +        Command cmd = Command.get(game.ctrl.player); +        cmd.setPromote(unit); +        process(cmd); +    } + +    // STATES ENTRY <- + +    private Command getMoveCommand(Unit unit, Move move) +    { +        Command cmd = Command.get(game.ctrl.player); +        cmd.setMove(unit, move); +        return cmd; +    } + +    private void initMove(Unit unit) +    { +        moveableUnits.remove(unit); +        activatedUnits.add(unit); +        playMoveSound(unit); +    } + +    private int promoteUnit(final Unit unit, final Player player) +    { +        activatedUnits.add(unit); +        addPromoteAnimation(unit, player, new Runnable() { +            @Override +            public void run() { +                player.promote(unit); +            } +        }); +        return 1; +    } + +    private int process(Command cmd) +    { +        RustAndDust.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: +                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; +    } + +    private int process(Unit unit, Move move) +    { +        RustAndDust.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); +                game.ctrl.player.unitWithdraw(unit); +                r = moveableUnits.size(); +                break; +            case SET: +                setPawnOnto(unit, move); +                game.ctrl.player.unitEntry(unit); +                claim((Hex) move.to, unit.getArmy()); +                break; +            case ENTER: +                enterPawn(unit, move); +                game.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 doEngagement(Engagement e) +    { +        resolveEngagement(e); + +        breakthroughUnits.clear(); +        activatedUnits.clear(); + +        activatedUnits.add(e.attacker); +        for (Unit u : e.assists) +            activatedUnits.add(u); + +        for (Unit u : activatedUnits) { +            u.engage(); +            if (u.canBreak()) +                breakthroughUnits.add(u); +        } + +        if (e.success) { +            unclaim(e.defender.getHex()); +            removePawn(e.defender); +            addDestroyAnimation(e.defender); +        } + +        game.ctrl.hud.engagementSummary(e); +        addEngagementAnimation(e.defender); + +        if (engagementCost(e) == 0) +            activatedUnits.clear(); + +        return (e.success ? 1 : 0); +    } + +} | 
