From a127777e494595295f061765a41669dee1204030 Mon Sep 17 00:00:00 2001 From: shmyga Date: Tue, 20 Feb 2018 00:01:35 +0300 Subject: [PATCH] [common] added GameSave --- src/client/haxe/ru/m/tankz/Client.hx | 11 ++++- .../haxe/ru/m/tankz/bundle}/ConfigBundle.hx | 11 +++-- .../haxe/ru/m/tankz/bundle/LevelBundle.hx | 15 ++++++ src/client/haxe/ru/m/tankz/frame/GameFrame.hx | 19 +++++--- .../haxe/ru/m/tankz/frame/LevelFrame.hx | 15 +++--- .../haxe/ru/m/tankz/frame/StartFrame.hx | 20 +++++++- .../haxe/ru/m/tankz/frame/StartFrame.yaml | 4 ++ .../haxe/ru/m/tankz/storage/SaveStorage.hx | 33 +++++++++++++ src/client/resources/classic/config.yaml | 2 +- .../haxe/ru/m/tankz/bundle/IConfigBundle.hx | 9 ++++ .../haxe/ru/m/tankz/bundle/ILevelBundle.hx | 9 ++++ src/common/haxe/ru/m/tankz/game/Game.hx | 47 ++++++++++++++++--- src/common/haxe/ru/m/tankz/game/GameSave.hx | 46 ++++++++++++++++++ src/common/haxe/ru/m/tankz/game/GameState.hx | 17 ++----- .../LevelBundle.hx => util/LevelUtil.hx} | 13 ++--- 15 files changed, 220 insertions(+), 51 deletions(-) rename src/{common/haxe/ru/m/tankz/config => client/haxe/ru/m/tankz/bundle}/ConfigBundle.hx (82%) create mode 100644 src/client/haxe/ru/m/tankz/bundle/LevelBundle.hx create mode 100644 src/client/haxe/ru/m/tankz/storage/SaveStorage.hx create mode 100644 src/common/haxe/ru/m/tankz/bundle/IConfigBundle.hx create mode 100644 src/common/haxe/ru/m/tankz/bundle/ILevelBundle.hx create mode 100644 src/common/haxe/ru/m/tankz/game/GameSave.hx rename src/common/haxe/ru/m/tankz/{config/LevelBundle.hx => util/LevelUtil.hx} (81%) diff --git a/src/client/haxe/ru/m/tankz/Client.hx b/src/client/haxe/ru/m/tankz/Client.hx index e30339a..7bdfcbd 100755 --- a/src/client/haxe/ru/m/tankz/Client.hx +++ b/src/client/haxe/ru/m/tankz/Client.hx @@ -13,11 +13,16 @@ import haxework.provider.Provider; import haxework.resources.IResources; import haxework.resources.Resources; import ru.m.connect.IConnection; +import ru.m.tankz.bundle.ConfigBundle; +import ru.m.tankz.bundle.IConfigBundle; +import ru.m.tankz.bundle.ILevelBundle; +import ru.m.tankz.bundle.LevelBundle; import ru.m.tankz.frame.StartFrame; import ru.m.tankz.game.ClassicGame; import ru.m.tankz.game.DotaGame; import ru.m.tankz.game.Game; import ru.m.tankz.PacketBuilder; +import ru.m.tankz.storage.SaveStorage; #if flash import haxework.log.JSLogger; #end #if debug import haxework.log.SocketLogger; #end @@ -71,7 +76,6 @@ class Client implements IConnectionHandler { Root.bind(view); view.content.stage.stageFocusRect = false; //view.logout.onPress = this; - view.switcher.change(StartFrame.ID); view.content.stage.addEventListener(KeyboardEvent.KEY_UP, function(event:KeyboardEvent):Void { if (event.keyCode == Keyboard.ESCAPE) { @@ -79,8 +83,13 @@ class Client implements IConnectionHandler { } }); + Provider.setFactory(IConfigBundle, ConfigBundle); + Provider.setFactory(ILevelBundle, LevelBundle); + Provider.setFactory(SaveStorage, SaveStorage); Provider.setFactory(Game, ClassicGame, ClassicGame.TYPE); Provider.setFactory(Game, DotaGame, DotaGame.TYPE); + + view.switcher.change(StartFrame.ID); } public function onPress(view:ButtonView):Void { diff --git a/src/common/haxe/ru/m/tankz/config/ConfigBundle.hx b/src/client/haxe/ru/m/tankz/bundle/ConfigBundle.hx similarity index 82% rename from src/common/haxe/ru/m/tankz/config/ConfigBundle.hx rename to src/client/haxe/ru/m/tankz/bundle/ConfigBundle.hx index e015b3d..b387b10 100644 --- a/src/common/haxe/ru/m/tankz/config/ConfigBundle.hx +++ b/src/client/haxe/ru/m/tankz/bundle/ConfigBundle.hx @@ -1,9 +1,10 @@ -package ru.m.tankz.config; +package ru.m.tankz.bundle; -import yaml.Parser; import openfl.Assets; -import yaml.Yaml; import ru.m.tankz.config.Config; +import ru.m.tankz.Type; +import yaml.Parser; +import yaml.Yaml; typedef ConfigSource = { @@ -16,13 +17,13 @@ typedef ConfigSource = { var bonuses: Array; } -class ConfigBundle { +class ConfigBundle implements IConfigBundle { private static function convert(raw:Dynamic):ConfigSource { return raw; } - public static function get(type:String):Config { + public function get(type:GameType):Config { var source = convert(Yaml.parse(Assets.getText('resources/${type}/config.yaml'), Parser.options().useObjects())); return new Config(type, source.game, source.map, source.bricks, source.presets, source.points, source.tanks, source.bonuses); } diff --git a/src/client/haxe/ru/m/tankz/bundle/LevelBundle.hx b/src/client/haxe/ru/m/tankz/bundle/LevelBundle.hx new file mode 100644 index 0000000..abe8a44 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/bundle/LevelBundle.hx @@ -0,0 +1,15 @@ +package ru.m.tankz.bundle; + +import openfl.Assets; +import ru.m.tankz.config.Config; +import ru.m.tankz.Type; +import ru.m.tankz.util.LevelUtil; + + +class LevelBundle implements ILevelBundle { + + public function get(type:GameType, config:Config, level:Int):LevelConfig { + var data:String = Assets.getText('resources/${type}/levels/level${LevelUtil.formatLevel(level)}.txt'); + return LevelUtil.loads(config, data); + } +} diff --git a/src/client/haxe/ru/m/tankz/frame/GameFrame.hx b/src/client/haxe/ru/m/tankz/frame/GameFrame.hx index b36dd56..89d8242 100755 --- a/src/client/haxe/ru/m/tankz/frame/GameFrame.hx +++ b/src/client/haxe/ru/m/tankz/frame/GameFrame.hx @@ -11,9 +11,11 @@ import haxework.provider.Provider; import protohx.Message; import ru.m.connect.IConnection; import ru.m.tankz.game.Game; +import ru.m.tankz.game.GameSave; import ru.m.tankz.game.GameState; import ru.m.tankz.proto.pack.GameUpdateResponse; import ru.m.tankz.render.Render; +import ru.m.tankz.storage.SaveStorage; interface GameFrameLayout { @@ -34,16 +36,16 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand public function init():Void {} public function onShow():Void { - start(Provider.get(GameState)); + start(Provider.get(GameSave)); } - private function start(s:GameState):Void { - game = Provider.build(Game, s.type); + private function start(save:GameSave):Void { + game = Provider.build(Game, save.state.type); if (game == null) { - throw 'Unsupported game type "${s.type}"'; + throw 'Unsupported game type "${save.state.type}"'; } game.engine.listeners.push(render); - game.start(s).then(onGameStateChange).endThen(onGameComplete); + game.start(save).then(onGameStateChange).endThen(onGameComplete); content.addEventListener(Event.ENTER_FRAME, redraw); //Provider.get(IConnection).packetHandler.addListener(this); render.draw(game.engine); @@ -67,6 +69,9 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand } private function stateString(game:Game):String { + if (game.state == null) { + return ''; + } var result:Array = []; result.push('Level: ${game.state.level}'); for (team in game.teams) { @@ -98,8 +103,10 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand } switch (game.next()) { case Option.Some(s): + var save = game.save(); + Provider.get(SaveStorage).write(save); stop(); - start(s); + start(save); case Option.None: Provider.get(IFrameSwitcher).change(StartFrame.ID); } diff --git a/src/client/haxe/ru/m/tankz/frame/LevelFrame.hx b/src/client/haxe/ru/m/tankz/frame/LevelFrame.hx index 44eb18b..639d59b 100644 --- a/src/client/haxe/ru/m/tankz/frame/LevelFrame.hx +++ b/src/client/haxe/ru/m/tankz/frame/LevelFrame.hx @@ -1,12 +1,13 @@ package ru.m.tankz.frame; -import ru.m.tankz.config.ConfigBundle; +import ru.m.tankz.game.GameSave; import haxework.gui.frame.IFrameSwitcher; -import ru.m.tankz.game.GameState; -import haxework.provider.Provider; import haxework.gui.list.ListView; -import haxework.gui.ViewBuilder; import haxework.gui.VGroupView; +import haxework.gui.ViewBuilder; +import haxework.provider.Provider; +import ru.m.tankz.bundle.IConfigBundle; +import ru.m.tankz.game.GameState; interface LevelFrameLayout { @@ -23,13 +24,13 @@ class LevelFrame extends VGroupView implements ViewBuilder implements LevelFrame } public function onShow():Void { - var state = Provider.get(GameState); - var c = ConfigBundle.get(state.type).game.levels; + var state:GameState = Provider.get(GameSave).state; + var c = Provider.get(IConfigBundle).get(state.type).game.levels; levels.data = [for (i in 0...c) i]; } public function onListItemClick(item:IListItemView):Void { - Provider.get(GameState).level = item.data; + Provider.get(GameSave).state.level = item.data; Provider.get(IFrameSwitcher).change(GameFrame.ID); } } diff --git a/src/client/haxe/ru/m/tankz/frame/StartFrame.hx b/src/client/haxe/ru/m/tankz/frame/StartFrame.hx index ff3dccf..a34f5a3 100644 --- a/src/client/haxe/ru/m/tankz/frame/StartFrame.hx +++ b/src/client/haxe/ru/m/tankz/frame/StartFrame.hx @@ -1,5 +1,7 @@ package ru.m.tankz.frame; +import ru.m.tankz.storage.SaveStorage; +import ru.m.tankz.game.GameSave; import haxework.gui.ButtonView; import haxework.gui.frame.IFrameSwitcher; import haxework.gui.VGroupView; @@ -14,6 +16,7 @@ import ru.m.tankz.Type; interface StartFrameLayout { var classic_1p(default, null):ButtonView; var classic_2p(default, null):ButtonView; + var classic_load(default, null):ButtonView; var dota_1p(default, null):ButtonView; var dota_2p_coop(default, null):ButtonView; var dota_2p_vs(default, null):ButtonView; @@ -24,20 +27,27 @@ class StartFrame extends VGroupView implements ViewBuilder implements StartFrame public static inline var ID = "start"; - public function init() { + public function init():Void { classic_1p.onPress = this; classic_2p.onPress = this; + classic_load.onPress = this; dota_1p.onPress = this; dota_2p_coop.onPress = this; dota_2p_vs.onPress = this; } + public function onShow():Void { + classic_load.visible = Provider.get(SaveStorage).read(ClassicGame.TYPE) != null; + } + public function onPress(view:ButtonView):Void { switch (view.id) { case 'classic_1p': startGame(ClassicGame.TYPE, ClassicGame.PLAYER1); case 'classic_2p': startGame(ClassicGame.TYPE, ClassicGame.PLAYER2); + case 'classic_load': + loadGame(ClassicGame.TYPE); case 'dota_1p': startGame(DotaGame.TYPE, DotaGame.PLAYER1); case 'dota_2p_coop': @@ -48,7 +58,13 @@ class StartFrame extends VGroupView implements ViewBuilder implements StartFrame } private function startGame(type:GameType, presetId:PresetId):Void { - Provider.set(GameState, new GameState(type, presetId)); + Provider.set(GameSave, new GameSave({type: type, presetId: presetId})); Provider.get(IFrameSwitcher).change(LevelFrame.ID); } + + private function loadGame(type:GameType):Void { + var save:GameSave = Provider.get(SaveStorage).read(type); + Provider.set(GameSave, save); + Provider.get(IFrameSwitcher).change(GameFrame.ID); + } } diff --git a/src/client/haxe/ru/m/tankz/frame/StartFrame.yaml b/src/client/haxe/ru/m/tankz/frame/StartFrame.yaml index f355d24..329e2a2 100644 --- a/src/client/haxe/ru/m/tankz/frame/StartFrame.yaml +++ b/src/client/haxe/ru/m/tankz/frame/StartFrame.yaml @@ -20,6 +20,10 @@ views: $type: haxework.gui.ButtonView text: 2 Player $style: button +- id: classic_load + $type: haxework.gui.ButtonView + text: Load + $style: button - $type: haxework.gui.LabelView $style: label fontSize: 20 diff --git a/src/client/haxe/ru/m/tankz/storage/SaveStorage.hx b/src/client/haxe/ru/m/tankz/storage/SaveStorage.hx new file mode 100644 index 0000000..e69a10f --- /dev/null +++ b/src/client/haxe/ru/m/tankz/storage/SaveStorage.hx @@ -0,0 +1,33 @@ +package ru.m.tankz.storage; + +import flash.net.SharedObject; +import ru.m.tankz.Type; +import ru.m.tankz.game.GameSave; + + +class SaveStorage { + + private static var TAG(default, never):String = 'SaveStorage'; + + private var so:SharedObject; + + public function new() { + so = SharedObject.getLocal('tankz'); + } + + public function read(type:GameType):Null { + var data:String = Reflect.getProperty(so.data, type); + L.d(TAG, 'read: ${data}'); + if (data != null) { + return GameSave.fromYaml(data); + } + return null; + } + + public function write(save:GameSave):Void { + var data:String = save.toYaml(); + L.d(TAG, 'write: ${data}'); + so.setProperty(save.state.type, data); + so.flush(); + } +} diff --git a/src/client/resources/classic/config.yaml b/src/client/resources/classic/config.yaml index f829a82..045c05a 100644 --- a/src/client/resources/classic/config.yaml +++ b/src/client/resources/classic/config.yaml @@ -42,7 +42,7 @@ presets: - {<<: *human, index: 0, color: 0xF5C040, life: 3} - id: bot spawnInterval: 3000 - life: 20 + life: 1 players: - {<<: *bot, index: 0} - {<<: *bot, index: 1} diff --git a/src/common/haxe/ru/m/tankz/bundle/IConfigBundle.hx b/src/common/haxe/ru/m/tankz/bundle/IConfigBundle.hx new file mode 100644 index 0000000..06c2c31 --- /dev/null +++ b/src/common/haxe/ru/m/tankz/bundle/IConfigBundle.hx @@ -0,0 +1,9 @@ +package ru.m.tankz.bundle; + +import ru.m.tankz.config.Config; +import ru.m.tankz.Type; + + +interface IConfigBundle { + public function get(type:GameType):Config; +} diff --git a/src/common/haxe/ru/m/tankz/bundle/ILevelBundle.hx b/src/common/haxe/ru/m/tankz/bundle/ILevelBundle.hx new file mode 100644 index 0000000..4a05504 --- /dev/null +++ b/src/common/haxe/ru/m/tankz/bundle/ILevelBundle.hx @@ -0,0 +1,9 @@ +package ru.m.tankz.bundle; + +import ru.m.tankz.config.Config; +import ru.m.tankz.Type; + + +interface ILevelBundle { + public function get(type:GameType, config:Config, level:Int):LevelConfig; +} diff --git a/src/common/haxe/ru/m/tankz/game/Game.hx b/src/common/haxe/ru/m/tankz/game/Game.hx index 84d13b7..b38a60c 100644 --- a/src/common/haxe/ru/m/tankz/game/Game.hx +++ b/src/common/haxe/ru/m/tankz/game/Game.hx @@ -1,15 +1,17 @@ package ru.m.tankz.game; +import ru.m.tankz.game.GameSave.PlayerSave; import haxe.ds.Option; import haxe.Timer; +import haxework.provider.Provider; 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.bundle.IConfigBundle; +import ru.m.tankz.bundle.ILevelBundle; 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.Bonus; @@ -40,7 +42,7 @@ class Game implements EngineListener { public function new(type:GameType) { this.type = type; - this.config = ConfigBundle.get(type); + this.config = Provider.get(IConfigBundle).get(type); this.engine = new Engine(config); engine.listeners.push(this); } @@ -73,11 +75,11 @@ class Game implements EngineListener { entity.rect.direction = Direction.fromString(point.direction); } - public function start(state:GameState):Stream { - this.state = state; + public function start(save:GameSave):Stream { + this.state = save.state; this.preset = config.getPreset(state.presetId); this.deferred = new Deferred(); - var level = LevelBundle.get(type, config, state.level); + var level:LevelConfig = Provider.get(ILevelBundle).get(type, config, state.level); points = level.points != null ? level.points : config.points; engine.map.setData(level.data); teams = new Map(); @@ -87,6 +89,16 @@ class Game implements EngineListener { var team:Team = new Team(teamConfig, teamPoints); teams[team.id] = team; for (player in team.players.iterator()) { + var playerSave:PlayerSave = save.getPlayer(player.id); + if (playerSave != null) { + player.life = playerSave.life; + if (playerSave.tank != null) { + player.config.tanks = [{ + type: playerSave.tank, + rate: 1, + }]; + } + } if (player.config.control != null) { var control = switch (player.config.control) { case Control.HUMAN: new HumanControl(player.id, humanControlIndex++); @@ -223,7 +235,7 @@ class Game implements EngineListener { var level = state.level + 1; state.level++; if (level >= config.game.levels) level = 0; - return Option.Some(new GameState(state.type, preset.id, level)); + return Option.Some({type: state.type, presetId: preset.id, level: level}); } public function dispose():Void { @@ -277,4 +289,25 @@ class Game implements EngineListener { engine.destroy(tank); // :-D } } + + public function save():GameSave { + var players:Array = []; + for (team in teams) { + for (player in team.players) { + if (player.config.control == Control.HUMAN) { + var tank:Tank = EntityTypeResolver.as(engine.entities[player.tankId], Tank); + players.push({ + id: player.id, + life: player.life + (player.tankId > 0 ? 1 : 0), + tank: tank != null ? tank.config.type : null + }); + } + } + } + return new GameSave({ + type: state.type, + presetId: state.presetId, + level: state.level + }, players); + } } diff --git a/src/common/haxe/ru/m/tankz/game/GameSave.hx b/src/common/haxe/ru/m/tankz/game/GameSave.hx new file mode 100644 index 0000000..e3f4aa2 --- /dev/null +++ b/src/common/haxe/ru/m/tankz/game/GameSave.hx @@ -0,0 +1,46 @@ +package ru.m.tankz.game; + +import yaml.Parser; +import yaml.Renderer; +import yaml.Yaml; +import ru.m.tankz.Type; + + +typedef PlayerSave = { + var id:PlayerId; + var tank:TankType; + var life:Int; +} + + +class GameSave { + + public var state:GameState; + public var players:Array; + + public function new(state:GameState, ?players:Array) { + this.state = state; + this.players = players != null ? players : []; + } + + public function getPlayer(id:PlayerId):PlayerSave { + for (player in players) { + if (player.id.team == id.team && player.id.index == id.index) { + return player; + } + } + return null; + } + + public function toYaml():String { + return Yaml.render({ + state: state, + players: players, + }, Renderer.options().setFlowLevel(0)); + } + + public static function fromYaml(value:String):GameSave { + var data:Dynamic = Yaml.parse(value, Parser.options().useObjects()); + return new GameSave(data.state, data.players); + } +} diff --git a/src/common/haxe/ru/m/tankz/game/GameState.hx b/src/common/haxe/ru/m/tankz/game/GameState.hx index c85cb29..04031a0 100644 --- a/src/common/haxe/ru/m/tankz/game/GameState.hx +++ b/src/common/haxe/ru/m/tankz/game/GameState.hx @@ -3,16 +3,9 @@ package ru.m.tankz.game; import ru.m.tankz.Type; -class GameState { - public var type:GameType; - public var level:Int; - public var presetId:PresetId; - public var loser:TeamId; - - public function new(type:GameType, presetId:PresetId, level:Int = 0) { - this.type = type; - this.presetId = presetId; - this.level = level; - this.loser = null; - } +typedef GameState = { + var type:GameType; + var presetId:PresetId; + @:optional var level:Int; + @:optional var loser:TeamId; } diff --git a/src/common/haxe/ru/m/tankz/config/LevelBundle.hx b/src/common/haxe/ru/m/tankz/util/LevelUtil.hx similarity index 81% rename from src/common/haxe/ru/m/tankz/config/LevelBundle.hx rename to src/common/haxe/ru/m/tankz/util/LevelUtil.hx index 7a5f631..de68b45 100644 --- a/src/common/haxe/ru/m/tankz/config/LevelBundle.hx +++ b/src/common/haxe/ru/m/tankz/util/LevelUtil.hx @@ -1,8 +1,6 @@ -package ru.m.tankz.config; +package ru.m.tankz.util; -import openfl.Assets; import ru.m.tankz.config.Config; -import ru.m.tankz.Type; import yaml.Parser; import yaml.Renderer; import yaml.Yaml; @@ -13,9 +11,9 @@ typedef LevelSource = { @:optional var points:Array; } -class LevelBundle { +class LevelUtil { - private static function formatLevel(level:Int):String { + public static function formatLevel(level:Int):String { var result = Std.string(level); while (result.length < 3) result = '0${result}'; return result; @@ -60,9 +58,4 @@ class LevelBundle { data: [for (i in 0...config.map.gridWidth * config.map.gridHeight) config.bricks[1]] } } - - public static function get(type:GameType, config:Config, level:Int):LevelConfig { - var data:String = Assets.getText('resources/${type}/levels/level${formatLevel(level)}.txt'); - return loads(config, data); - } }