diff --git a/src/core/Battle.ts b/src/core/Battle.ts index f86dc09..04a916b 100644 --- a/src/core/Battle.ts +++ b/src/core/Battle.ts @@ -61,6 +61,14 @@ module TK.SpaceTac { return bool(this.outcome); } + /** + * Apply a turn plan through a resolution + */ + applyTurnPlan(plan: TurnPlan): void { + const resolution = new TurnResolution(this, plan); + resolution.resolve(); + } + /** * Apply a list of diffs to the game state, and add them to the log. * diff --git a/src/core/Ship.ts b/src/core/Ship.ts index f2b1687..1de7755 100644 --- a/src/core/Ship.ts +++ b/src/core/Ship.ts @@ -111,8 +111,9 @@ module TK.SpaceTac { } // Make an initiative throw, to resolve play order in a battle - throwInitiative(gen: RandomGenerator): void { + throwInitiative(gen: RandomGenerator): number { this.play_priority = gen.random() * this.attributes.initiative.get(); + return this.play_priority; } /** diff --git a/src/core/TurnResolution.spec.ts b/src/core/TurnResolution.spec.ts new file mode 100644 index 0000000..7e9ea60 --- /dev/null +++ b/src/core/TurnResolution.spec.ts @@ -0,0 +1,53 @@ +module TK.SpaceTac { + testing("TurnResolution", test => { + test.case("defines play order by initiative throws", check => { + const fleet1 = new Fleet(); + const fleet2 = new Fleet(); + + const ship1 = new Ship(fleet1, "F1S1"); + TestTools.setAttribute(ship1, "initiative", 2); + const ship2 = new Ship(fleet1, "F1S2"); + TestTools.setAttribute(ship2, "initiative", 4); + const ship3 = new Ship(fleet1, "F1S3"); + TestTools.setAttribute(ship3, "initiative", 1); + const ship4 = new Ship(fleet2, "F2S1"); + TestTools.setAttribute(ship4, "initiative", 8); + const ship5 = new Ship(fleet2, "F2S2"); + TestTools.setAttribute(ship5, "initiative", 2); + + const battle = new Battle(fleet1, fleet2); + const random = new SkewedRandomGenerator([1.0, 0.1, 1.0, 0.2, 0.6]); + const resolution = new TurnResolution(battle, { fleets: [] }, random); + + check.equals(resolution.play_order, []); + resolution.throwInitiative(); + check.equals(resolution.play_order, [ship1, ship4, ship5, ship3, ship2]); + }); + + test.case("logs a turn start diff", check => { + const battle = new Battle(); + const resolution = new TurnResolution(battle, { fleets: [] }); + const ships = [new Ship(), new Ship()] + resolution.play_order = ships; + + check.equals(battle.log.count(), 0); + resolution.logStart(); + check.equals(battle.log.count(), 1); + const diff = battle.log.get(0); + if (check.instance(diff, TurnStartDiff, "diff should be a TurnStartDiff")) { + check.equals(diff.play_order, [ships[0].id, ships[1].id]); + } + }); + + test.case("logs a turn end diff", check => { + const battle = new Battle(); + const resolution = new TurnResolution(battle, { fleets: [] }); + + check.equals(battle.log.count(), 0); + resolution.logEnd(); + check.equals(battle.log.count(), 1); + const diff = battle.log.get(0); + check.instance(diff, TurnEndDiff, "diff should be a TurnEndDiff"); + }); + }); +} diff --git a/src/core/TurnResolution.ts b/src/core/TurnResolution.ts new file mode 100644 index 0000000..df32a9f --- /dev/null +++ b/src/core/TurnResolution.ts @@ -0,0 +1,70 @@ +namespace TK.SpaceTac { + /** + * The machinery to apply a turn plan, in order to resolve the current turn + */ + export class TurnResolution { + play_order: Ship[] = [] + + constructor(readonly battle: Battle, readonly plan: TurnPlan, readonly random = new RandomGenerator()) { + } + + /** + * Perform the whole resolution + */ + resolve(): void { + this.throwInitiative(); + this.logStart(); + this.performActions(ActionCategory.PASSIVE); + this.performActions(ActionCategory.MOVE); + this.performActions(ActionCategory.ACTIVE); + this.logEnd(); + } + + /** + * Perform an initiative throw, to obtain an order in which the ships will play + */ + throwInitiative(): void { + const with_thrown = this.battle.ships.list().map(ship => [ship, ship.throwInitiative(this.random)] as [Ship, number]); + const sorted_by_thrown = sortedBy(with_thrown, ([_, thrown]) => thrown); + this.play_order = sorted_by_thrown.reverse().map(([ship, _]) => ship); + } + + /** + * Log a turn start diff + */ + logStart(): void { + this.battle.applyDiffs([new TurnStartDiff(this.play_order)]); + } + + /** + * Perform all actions of a given category, for all ships in the initiative order + */ + performActions(category: ActionCategory): void { + this.play_order.forEach(ship => this.performActionsForShip(ship, category)); + } + + /** + * Perform all actions of a given category, for one given ship + */ + performActionsForShip(ship: Ship, category: ActionCategory): void { + this.plan.fleets.forEach(fleetplan => { + fleetplan.ships.forEach(shipplan => { + if (ship.is(shipplan.ship)) { + shipplan.actions.forEach(actionplan => { + if (actionplan.category === category) { + this.battle.applyOneAction(actionplan.action, actionplan.target); + } + }); + } + }); + }); + } + + /** + * Log a turn end diff + */ + logEnd(): void { + this.battle.applyDiffs([new TurnEndDiff()]); + } + } +} diff --git a/src/core/diffs/TurnEndDiff.ts b/src/core/diffs/TurnEndDiff.ts new file mode 100644 index 0000000..b88333f --- /dev/null +++ b/src/core/diffs/TurnEndDiff.ts @@ -0,0 +1,9 @@ +/// + +module TK.SpaceTac { + /** + * A turn has ended + */ + export class TurnEndDiff extends BaseBattleDiff { + } +} diff --git a/src/core/diffs/TurnStartDiff.ts b/src/core/diffs/TurnStartDiff.ts new file mode 100644 index 0000000..b0d5811 --- /dev/null +++ b/src/core/diffs/TurnStartDiff.ts @@ -0,0 +1,16 @@ +/// + +module TK.SpaceTac { + /** + * A turn has started + */ + export class TurnStartDiff extends BaseBattleDiff { + readonly play_order: RObjectId[] + + constructor(play_order: Ship[]) { + super(); + + this.play_order = play_order.map(ship => ship.id); + } + } +} diff --git a/src/ui/battle/BattleInfoBar.spec.ts b/src/ui/battle/BattleInfoBar.spec.ts index a4534e7..d500675 100644 --- a/src/ui/battle/BattleInfoBar.spec.ts +++ b/src/ui/battle/BattleInfoBar.spec.ts @@ -4,7 +4,7 @@ module TK.SpaceTac.UI.Specs { test.case("shows turn counter and current phase", check => { const parent = testgame.view.getLayer("test"); - const bar = new BattleInfoBar(parent.getBuilder()); + const bar = new BattleInfoBar(parent.getBuilder(), { startResolution: () => null }); expect(collectTexts(parent)).toEqual(["Battle"]); diff --git a/src/ui/battle/BattleInfoBar.ts b/src/ui/battle/BattleInfoBar.ts index 659446c..6f9ecdb 100644 --- a/src/ui/battle/BattleInfoBar.ts +++ b/src/ui/battle/BattleInfoBar.ts @@ -26,20 +26,7 @@ module TK.SpaceTac.UI { this.title = builder.in(this.container).text("Battle", 0, 0, { center: true, size: 30, shadow: true, color: "#dbeff9" }); - this.planning = new BattleInfoBarPlanning(this.container, () => { - }); - } - - startResolution(): void { - const startResolution = this.config.startResolution; - if (startResolution) { - const message = "Validate all your ship's planning, and proceed to turn resolution?"; - UIConfirmDialog.ask(this.builder.view, message).then(ok => { - if (ok) { - startResolution(); - } - }); - } + this.planning = new BattleInfoBarPlanning(this.container, config.startResolution); } setPhase(turn: number, phase: BattleInfoBarPhase): void { @@ -60,16 +47,18 @@ module TK.SpaceTac.UI { container: UIContainer turns: UIText - constructor(parent: UIContainer, startResolution: Function) { + constructor(parent: UIContainer, startResolution?: Function) { this.container = parent.getBuilder().container("planning", 0, 0, false); const builder = this.container.getBuilder(); - builder.button("battle-buttons-medium", 604, 0, startResolution, "Confirm your planning and start the turn resolution", undefined, { - center: true, - text: "Ready", - text_x: 12, - text_style: { size: 24, color: "#9fc4d6" } - }); + if (startResolution) { + builder.button("battle-buttons-medium", 604, 0, startResolution, "Confirm your planning and start the turn resolution", undefined, { + center: true, + text: "Ready", + text_x: 12, + text_style: { size: 24, color: "#9fc4d6" } + }); + } builder.image("battle-infobar-turncounter", 450, 0, true); this.turns = builder.text("Turn", 450, 0, { center: true, size: 24, color: "#9fc4d6" }); diff --git a/src/ui/battle/BattleView.ts b/src/ui/battle/BattleView.ts index 3bd6a0f..8c06352 100644 --- a/src/ui/battle/BattleView.ts +++ b/src/ui/battle/BattleView.ts @@ -225,9 +225,15 @@ module TK.SpaceTac.UI { * Start the turn resolution */ startResolution(): void { - // TODO Check we are in planning phase - // TODO Wait for AI - // TODO + const message = "Validate your whole fleet planning, and proceed to turn resolution?"; + UIConfirmDialog.ask(this, message).then(ok => { + if (ok) { + // TODO Check we are in planning phase + // TODO Wait for AI + // TODO Merge plans + this.actual_battle.applyTurnPlan(this.turn_plannings[0].getTurnPlan()); + } + }); } /** diff --git a/src/ui/common/UIConfirmDialog.ts b/src/ui/common/UIConfirmDialog.ts index bfe40e6..6f94d3d 100644 --- a/src/ui/common/UIConfirmDialog.ts +++ b/src/ui/common/UIConfirmDialog.ts @@ -9,7 +9,7 @@ module TK.SpaceTac.UI { constructor(view: BaseView, message: string) { super(view); - this.content.text(message, this.width * 0.5, this.height * 0.4, { color: "#9FC4D6", size: 32, shadow: true }); + this.content.text(message, this.width * 0.5, this.height * 0.4, { color: "#9FC4D6", size: 32, shadow: true, width: 800 }); this.result = new Promise(resolve => { this.result_resolver = resolve;