diff --git a/package.json b/package.json index 0d2d477..f281feb 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tankz", - "version": "0.1.0", + "version": "0.2.0", "private": true, "devDependencies": { "ansi-colors": "^1.0.1", diff --git a/src/client/haxe/layout/frames/game.json b/src/client/haxe/layout/frames/game.json index 19ea442..338c97c 100644 --- a/src/client/haxe/layout/frames/game.json +++ b/src/client/haxe/layout/frames/game.json @@ -1,6 +1,10 @@ { "pWidth": 100, "pHeight": 100, "views": [ + { + "id": "state", "@type": "haxework.gui.LabelView", "@style": "label", + "pWidth": 100, "height": 20 + }, { "id": "render", "@type": "ru.m.tankz.render.Render", "contentSize": true diff --git a/src/client/haxe/ru/m/tankz/view/frames/GameFrame.hx b/src/client/haxe/ru/m/tankz/view/frames/GameFrame.hx index 5821acc..10e89c6 100755 --- a/src/client/haxe/ru/m/tankz/view/frames/GameFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/frames/GameFrame.hx @@ -1,19 +1,30 @@ package ru.m.tankz.view.frames; -import ru.m.tankz.game.GameState; +import haxe.ds.Option; +import haxe.ds.Option; import flash.events.Event; import haxe.Timer; +import haxework.gui.frame.IFrameSwitcher; +import haxework.gui.LabelView; import haxework.gui.VGroupView; import haxework.gui.ViewBuilder; import haxework.provider.Provider; import protohx.Message; import ru.m.connect.IConnection; import ru.m.tankz.game.Game; +import ru.m.tankz.game.GameState; import ru.m.tankz.proto.pack.GameUpdateResponse; +import ru.m.tankz.render.Render; + + +interface GameFrameLayout { + var state(default, null):LabelView; + var render(default, null):Render; +} @:template("layout/frames/game.json", "layout/styles.json") -class GameFrame extends VGroupView implements ViewBuilder implements IPacketHandler { +class GameFrame extends VGroupView implements ViewBuilder implements IPacketHandler implements GameFrameLayout { private static inline var TAG = "GameFrame"; @@ -26,18 +37,53 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand } public function onShow():Void { - var state:GameState = Provider.get(GameState); - game = Provider.build(Game, state.type); + var s:GameState = Provider.get(GameState); + game = Provider.build(Game, s.type); if (game == null) { - throw 'Unsupported game type "${state.type}"'; + throw 'Unsupported game type "${s.type}"'; } game.engine.listeners.push(render); - game.start(state); + game.start(s).then(onGameStateChange).endThen(onGameComplete); content.addEventListener(Event.ENTER_FRAME, redraw); //Provider.get(IConnection).packetHandler.addListener(this); render.draw(game.engine); timer = new Timer(10); timer.run = updateEngine; + state.text = stateString(s); + } + + private function stateString(state:GameState):String { + var result:Array = []; + for (teamId in state.teams.keys()) { + var ts:TeamState = state.teams[teamId]; + if (ts.lose) { + result.push('${teamId}: LOSE'); + } else if (ts.life > -1) { + result.push('${teamId}: ${ts.life}'); + } else { + for (index in ts.players.keys()) { + var ps:PlayerState = ts.players[index]; + if (ps.life > -1) { + result.push('${teamId}${index}: ${ps.life}'); + } + } + } + } + return result.join(' '); + } + + private function onGameStateChange(s:GameState):GameState { + state.text = stateString(s); + return s; + } + + private function onGameComplete(result:Option):Void { + switch (result) { + case Option.Some(s): + state.text = stateString(s); + case Option.None: + } + Provider.get(IFrameSwitcher).change(StartFrame.ID); } public function onHide():Void { diff --git a/src/common/haxe/ru/m/tankz/core/Eagle.hx b/src/common/haxe/ru/m/tankz/core/Eagle.hx index e1fb93b..16c6489 100644 --- a/src/common/haxe/ru/m/tankz/core/Eagle.hx +++ b/src/common/haxe/ru/m/tankz/core/Eagle.hx @@ -1,11 +1,15 @@ package ru.m.tankz.core; +import ru.m.tankz.game.Game; import ru.m.geom.Rectangle; class Eagle extends Entity { - public function new() { + public var team(default, null):TeamId; + + public function new(team:TeamId) { super(new Rectangle(0, 0, 44, 44)); + this.team = team; } } diff --git a/src/common/haxe/ru/m/tankz/game/ClassicGame.hx b/src/common/haxe/ru/m/tankz/game/ClassicGame.hx index b520eb2..f685c4c 100644 --- a/src/common/haxe/ru/m/tankz/game/ClassicGame.hx +++ b/src/common/haxe/ru/m/tankz/game/ClassicGame.hx @@ -19,10 +19,10 @@ class ClassicGame extends Game { var state = new GameState(); state.type = TYPE; state.level = level; - state.players[HUMAN] = new Map(); - state.players[BOT] = new Map(); + state.teams[HUMAN] = {life: -1, players: new Map(), lose: false}; + state.teams[BOT] = {life: 20, players: new Map(), lose: false}; for (i in 0...humans) { - state.players[HUMAN][i] = { + state.teams[HUMAN].players[i] = { index:i, tank:{ group: HUMAN, @@ -36,7 +36,7 @@ class ClassicGame extends Game { }; } for (i in 0...humans*2+2) { - state.players[BOT][i] = { + state.teams[BOT].players[i] = { index:i, tank:{ group: BOT, diff --git a/src/common/haxe/ru/m/tankz/game/DotaGame.hx b/src/common/haxe/ru/m/tankz/game/DotaGame.hx index c4ed959..723163b 100644 --- a/src/common/haxe/ru/m/tankz/game/DotaGame.hx +++ b/src/common/haxe/ru/m/tankz/game/DotaGame.hx @@ -21,10 +21,10 @@ class DotaGame extends Game { var state = new GameState(); state.type = TYPE; state.level = level; - state.players[RADIANT] = new Map(); - state.players[DIRE] = new Map(); + state.teams[RADIANT] = {life: 20, players: new Map(), lose: false}; + state.teams[DIRE] = {life: 20, players: new Map(), lose: false}; for (i in 0...TEAM_SIZE) { - state.players[RADIANT][i] = { + state.teams[RADIANT].players[i] = { index:i, tank:{ group: RADIANT, @@ -38,7 +38,7 @@ class DotaGame extends Game { }; } for (i in 0...TEAM_SIZE) { - state.players[DIRE][i] = { + state.teams[DIRE].players[i] = { index:i, tank:{ group: DIRE, diff --git a/src/common/haxe/ru/m/tankz/game/Game.hx b/src/common/haxe/ru/m/tankz/game/Game.hx index 26eb49a..700101e 100644 --- a/src/common/haxe/ru/m/tankz/game/Game.hx +++ b/src/common/haxe/ru/m/tankz/game/Game.hx @@ -1,19 +1,23 @@ package ru.m.tankz.game; -import ru.m.tankz.bot.BotControl; -import ru.m.tankz.control.HumanControl; -import ru.m.tankz.config.ConfigBundle; -import ru.m.tankz.config.LevelBundle; -import ru.m.tankz.game.Spawner; -import ru.m.tankz.core.Entity; -import ru.m.tankz.core.Eagle; +import haxe.Timer; +import promhx.Deferred; +import promhx.Stream; import ru.m.geom.Direction; import ru.m.geom.Point; +import ru.m.tankz.bot.BotControl; import ru.m.tankz.config.Config; +import ru.m.tankz.config.ConfigBundle; +import ru.m.tankz.config.LevelBundle; import ru.m.tankz.control.Control; +import ru.m.tankz.control.HumanControl; +import ru.m.tankz.core.Eagle; +import ru.m.tankz.core.Entity; import ru.m.tankz.core.EntityType; import ru.m.tankz.core.Tank; import ru.m.tankz.engine.Engine; +import ru.m.tankz.game.GameState; +import ru.m.tankz.game.Spawner; typedef GameType = String; @@ -35,6 +39,8 @@ class Game implements EngineListener { public var engine(default, null):Engine; private var spawners:Map; + private var deferred:Deferred; + private var stream:Stream; public function new(type:GameType) { this.type = type; @@ -58,7 +64,8 @@ class Game implements EngineListener { entity.rect.direction = Direction.fromString(point.direction); } - public function start(state:GameState):Void { + public function start(state:GameState):Stream { + this.deferred = new Deferred(); this.state = state; var bricks = LevelBundle.get(type, config, state.level); engine.map.setData(bricks); @@ -66,7 +73,7 @@ class Game implements EngineListener { spawners = new Map(); for (teamConfig in config.teams) { var team = new Team(teamConfig); - for (playerState in state.players.get(team.id)) { + for (playerState in state.teams.get(team.id).players) { var player = new Player({team:team.id, index:playerState.index}); team.players.push(player); teams.set(team.id, team); @@ -92,11 +99,13 @@ class Game implements EngineListener { } var eaglePoint = spawners.get(team.id).getPoint('eagle'); if (eaglePoint != null) { - var eagle = new Eagle(); + var eagle = new Eagle(team.id); applyPoint(eagle, eaglePoint); engine.spawn(eagle); } } + + return stream = deferred.stream(); } private function spawn(task:SpawnTask):Void { @@ -106,6 +115,20 @@ class Game implements EngineListener { player.tankId = tank.id; } + private function complete():Void { + for (team in teams.iterator()) { + for (player in team.players) { + player.control.dispose(); + } + } + var timer = new Timer(5000); + timer.run = function() { + timer.stop(); + deferred.resolve(state); + stream.end(); + } + } + public function setControl(playerId:PlayerId, control:Control):Void { for (team in teams.iterator()) { if (team.id == playerId.team) { @@ -134,10 +157,36 @@ class Game implements EngineListener { } } + private function calcTeamLife(ts:TeamState):Int { + return Lambda.fold(ts.players, function(ps, t) return t + ps.life, ts.life); + } + public function onDestroy(entity:EntityType):Void { switch (entity) { case EntityType.TANK(tank): - spawners.get(tank.playerId.team).push(tank.playerId); + var respawn:Bool = false; + var teamState:TeamState = state.teams[tank.playerId.team]; + if (teamState.life > 0) { + teamState.life--; + respawn = true; + } else { + var playerState:PlayerState = teamState.players[tank.playerId.index]; + if (playerState.life > 0) { + playerState.life--; + respawn = true; + } + } + if (respawn) { + spawners.get(tank.playerId.team).push(tank.playerId); + } else if (calcTeamLife(teamState) < 1) { + state.teams[tank.playerId.team].lose = true; + complete(); + } + deferred.resolve(state); + case EntityType.EAGLE(eagle): + state.teams[eagle.team].lose = true; + complete(); + deferred.resolve(state); case x: } } diff --git a/src/common/haxe/ru/m/tankz/game/GameState.hx b/src/common/haxe/ru/m/tankz/game/GameState.hx index a619a6f..ae192e7 100644 --- a/src/common/haxe/ru/m/tankz/game/GameState.hx +++ b/src/common/haxe/ru/m/tankz/game/GameState.hx @@ -21,14 +21,21 @@ typedef PlayerState = { } +typedef TeamState = { + var players:Map; + var life:Int; + var lose:Bool; +} + + class GameState { public var type:GameType; public var level:Int; - public var players:Map>; + public var teams:Map; public function new() { type = null; level = -1; - players = new Map(); + teams = new Map(); } }