diff --git a/src/client/haxe/ru/m/tankz/Init.hx b/src/client/haxe/ru/m/tankz/Init.hx index a521519..df61182 100644 --- a/src/client/haxe/ru/m/tankz/Init.hx +++ b/src/client/haxe/ru/m/tankz/Init.hx @@ -18,6 +18,7 @@ import ru.m.tankz.proto.pack.Response; import ru.m.tankz.sound.SoundManager; import ru.m.tankz.storage.GameStorage; import ru.m.tankz.storage.MultiplayerStorage; +import ru.m.tankz.storage.RecordStorage; import ru.m.tankz.storage.SettingsStorage; #if flash import flash.Lib; @@ -33,6 +34,7 @@ class Init { @:provide static var settingsStorage:SettingsStorage; @:provide static var multiplayerStorage:MultiplayerStorage; @:provide static var gameStorage:GameStorage; + @:provide static var recordStorage:RecordStorage; @:provide static var soundManager:SoundManager; @:provide static var networkManager:NetworkManager; @:provide static var controlFactory:IControlFactory; @@ -62,10 +64,15 @@ class Init { settingsStorage = new SettingsStorage(); multiplayerStorage = new MultiplayerStorage(); gameStorage = new GameStorage(); + recordStorage = new RecordStorage(); soundManager = new SoundManager(); controlFactory = new ClientControlFactory(); popupManager = new PopupManager(); + for (record in recordStorage) { + L.w("RECORD", '${record.id}. ${record.date}'); + } + popupManager.showAnimateFactory = function(v) return new UnFadeAnimate(v, 100); popupManager.closeAnimateFactory = function(v) return new FadeAnimate(v, 100); diff --git a/src/client/haxe/ru/m/tankz/render/Render.hx b/src/client/haxe/ru/m/tankz/render/Render.hx index dd096f8..9dcf0fc 100755 --- a/src/client/haxe/ru/m/tankz/render/Render.hx +++ b/src/client/haxe/ru/m/tankz/render/Render.hx @@ -1,5 +1,6 @@ package ru.m.tankz.render; +import ru.m.tankz.core.EntityType; import flash.display.DisplayObjectContainer; import flash.display.Graphics; import flash.display.Sprite; @@ -15,7 +16,7 @@ import ru.m.tankz.game.GameEvent; import ru.m.tankz.game.IGame; import ru.m.tankz.render.RenderItem; -class Render extends SpriteView implements GameListener { +class Render extends SpriteView implements GameListener implements EngineListener { private var backgroundLayer:Sprite; private var groundLayer:Sprite; @@ -97,6 +98,23 @@ class Render extends SpriteView implements GameListener { clearLayer(upperLayer); } + public function onSpawn(entity:EntityType):Void { + switch entity { + case EAGLE(eagle): + var item = new EagleItem(eagle); + items.set(eagle.key, item); + entryLayer.addChild(item.view); + item.update(); + case _: + } + } + + public function onCollision(entity:EntityType, with:EntityType):Void { + } + + public function onMove(entity:EntityType):Void { + } + public function onGameEvent(event:GameEvent):Void { switch event { case SPAWN(TANK(tank)): @@ -110,11 +128,11 @@ class Render extends SpriteView implements GameListener { items.set(bullet.key, item); entryLayer.addChild(item.view); item.update(); - case SPAWN(EAGLE(eagle)): + /*case SPAWN(EAGLE(eagle)): var item = new EagleItem(eagle); items.set(eagle.key, item); entryLayer.addChild(item.view); - item.update(); + item.update();*/ case SPAWN(BONUS(bonus)): var item = new BonusItem(bonus); items.set(bonus.key, item); diff --git a/src/client/haxe/ru/m/tankz/storage/RecordStorage.hx b/src/client/haxe/ru/m/tankz/storage/RecordStorage.hx new file mode 100644 index 0000000..e239208 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/storage/RecordStorage.hx @@ -0,0 +1,28 @@ +package ru.m.tankz.storage; + +import haxe.DynamicAccess; +import haxework.storage.SharedObjectStorage; +import ru.m.tankz.game.record.GameRecord; + +@:yield class RecordStorage extends SharedObjectStorage { + + private static inline var VERSION = 1; + + public function new() { + super('record_${VERSION}'); + } + + public function save(record:GameRecord):Void { + trace(record.id); + trace(record.date); + trace(record.events); + write(record.id, record); + } + + public function iterator():Iterator { + var data:DynamicAccess = so.data; + for (id in data.keys()) { + @yield return read(id); + } + } +} diff --git a/src/client/haxe/ru/m/tankz/view/common/GameFrame.hx b/src/client/haxe/ru/m/tankz/view/common/GameFrame.hx index b60c8a3..8152987 100644 --- a/src/client/haxe/ru/m/tankz/view/common/GameFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/common/GameFrame.hx @@ -14,6 +14,7 @@ import ru.m.tankz.network.NetworkManager; import ru.m.tankz.render.Render; import ru.m.tankz.sound.SoundManager; import ru.m.tankz.storage.GameStorage; +import ru.m.tankz.storage.RecordStorage; import ru.m.tankz.view.common.IGamePanel; class GameFrame extends GroupView implements GameListener { @@ -28,7 +29,8 @@ class GameFrame extends GroupView implements GameListener { @:provide var state:GameState; @:provide("result") var result:GameState; @:provide var switcher:FrameSwitcher; - @:provide var storage:GameStorage; + @:provide var gameStorage:GameStorage; + @:provide var recordStorage:RecordStorage; private var game:IGame; private var runner:GameRunner; @@ -49,6 +51,7 @@ class GameFrame extends GroupView implements GameListener { private function start(state:GameState):Void { game = new Game(state.type); game.connect(render); + game.engine.connect(render); game.connect(soundManager); game.connect(this); if (panel != null) { @@ -79,14 +82,15 @@ class GameFrame extends GroupView implements GameListener { public function onGameEvent(event:GameEvent):Void { switch event { case GameEvent.COMPLETE(state, winner): - L.w("RECORD", Std.string(recorder.data)); + // ToDo: + //recordStorage.save(recorder.record); result = state; this.state = switch runner.next() { case Some(s): // ToDo: - var progress = storage.get(game.type); + var progress = gameStorage.get(game.type); progress.completeLevel(result.levelId, result.presetId); - storage.set(progress); + gameStorage.set(progress); s; case None: null; } diff --git a/src/common/haxe/ru/m/tankz/config/Config.hx b/src/common/haxe/ru/m/tankz/config/Config.hx index e1fb6ce..ecbf78a 100644 --- a/src/common/haxe/ru/m/tankz/config/Config.hx +++ b/src/common/haxe/ru/m/tankz/config/Config.hx @@ -213,4 +213,13 @@ class Config { } return -1; } + + public function getPoint(teamId:TeamId, type:String, index:Int = -1):SpawnPoint { + for (point in points) { + if (point.team == teamId && point.type == type && point.index == index) { + return point; + } + } + return null; + } } diff --git a/src/common/haxe/ru/m/tankz/engine/Engine.hx b/src/common/haxe/ru/m/tankz/engine/Engine.hx index ef5268f..46b7a40 100755 --- a/src/common/haxe/ru/m/tankz/engine/Engine.hx +++ b/src/common/haxe/ru/m/tankz/engine/Engine.hx @@ -1,5 +1,6 @@ package ru.m.tankz.engine; +import ru.m.geom.Direction; import ru.m.geom.Line; import ru.m.geom.Point; import ru.m.tankz.config.Config; @@ -30,11 +31,24 @@ import ru.m.tankz.map.LevelMap; public function spawn(entity:Entity):Void { entities.set(entity.id, entity); + spawnSignal.emit(EntityTypeResolver.of(entity)); } - public function destroy(entity:Entity):Void { - if (entities.exists(entity.id)) { - entities.remove(entity.id); + public function destroy(entityId:Int):Void { + if (entities.exists(entityId)) { + entities.remove(entityId); + } + } + + public function move(entityId:Int, direction:Direction):Void { + if (entities.exists(entityId)) { + cast(entities.get(entityId), MobileEntity).move(direction); + } + } + + public function stop(entityId:Int):Void { + if (entities.exists(entityId)) { + cast(entities.get(entityId), MobileEntity).stop(); } } diff --git a/src/common/haxe/ru/m/tankz/engine/IEngine.hx b/src/common/haxe/ru/m/tankz/engine/IEngine.hx index 4718d92..bf75149 100644 --- a/src/common/haxe/ru/m/tankz/engine/IEngine.hx +++ b/src/common/haxe/ru/m/tankz/engine/IEngine.hx @@ -1,6 +1,7 @@ package ru.m.tankz.engine; import haxework.signal.Signal; +import ru.m.geom.Direction; import ru.m.tankz.config.Config; import ru.m.tankz.core.Entity; import ru.m.tankz.core.EntityType; @@ -12,12 +13,17 @@ interface IEngine { public var config(default, default):Config; public var map(default, null):LevelMap; + public var spawnSignal(default, null):Signal1; public var collisionSignal(default, null):Signal2; public var moveSignal(default, null):Signal1; public function spawn(entity:Entity):Void; - public function destroy(entity:Entity):Void; + public function move(entityId:Int, direction:Direction):Void; + + public function stop(entityId:Int):Void; + + public function destroy(entityId:Int):Void; public function update():Void; @@ -31,6 +37,7 @@ interface IEngine { } interface EngineListener { + public function onSpawn(entity:EntityType):Void; public function onCollision(entity:EntityType, with:EntityType):Void; public function onMove(entity:EntityType):Void; -} \ No newline at end of file +} diff --git a/src/common/haxe/ru/m/tankz/game/EntityBuilder.hx b/src/common/haxe/ru/m/tankz/game/EntityBuilder.hx new file mode 100644 index 0000000..3e2f190 --- /dev/null +++ b/src/common/haxe/ru/m/tankz/game/EntityBuilder.hx @@ -0,0 +1,31 @@ +package ru.m.tankz.game; + +import ru.m.geom.Point; +import ru.m.tankz.config.Config; +import ru.m.tankz.core.Eagle; +import ru.m.tankz.core.Entity; +import ru.m.tankz.Type; + +class EntityBuilder { + + private var config:Config; + + public function new(config:Config) { + this.config = config; + } + + private function applyPoint(entity:Entity, point:SpawnPoint):Void { + entity.rect.center = new Point((point.x + 1) * config.map.cellWidth, (point.y + 1) * config.map.cellHeight); + entity.rect.direction = point.direction; + } + + public function buildEagle(teamId:TeamId):Eagle { + var eageleConfig = config.getTeam(teamId).eagle; + var eaglePoint = config.getPoint(teamId, "eagle"); + var eagle = new Eagle(teamId, eageleConfig); + eagle.color = config.getColor(new PlayerId(teamId, -1)); + applyPoint(eagle, eaglePoint); + return eagle; + } +} + diff --git a/src/common/haxe/ru/m/tankz/game/Game.hx b/src/common/haxe/ru/m/tankz/game/Game.hx index 9209cc3..8cd3d40 100644 --- a/src/common/haxe/ru/m/tankz/game/Game.hx +++ b/src/common/haxe/ru/m/tankz/game/Game.hx @@ -2,7 +2,9 @@ package ru.m.tankz.game; import ru.m.tankz.bundle.IConfigBundle; import ru.m.tankz.config.Config; +import ru.m.tankz.control.Control; import ru.m.tankz.core.EntityType; +import ru.m.tankz.core.Tank; import ru.m.tankz.engine.Engine; import ru.m.tankz.engine.IEngine; import ru.m.tankz.game.GameState; @@ -21,6 +23,8 @@ import ru.m.tankz.Type; public var state(default, null):GameState; + private var builder:EntityBuilder; + @:provide var configBundle:IConfigBundle; public function new(type:GameType) { @@ -28,6 +32,7 @@ import ru.m.tankz.Type; this.teams = new Map(); this.config = configBundle.get(type); this.engine = new Engine(config); + this.builder = new EntityBuilder(config); connect(this); } @@ -46,7 +51,9 @@ import ru.m.tankz.Type; case GameEvent.COMPLETE(state, team): this.state = state; this.winner = team.id; - case GameEvent.SPAWN(EAGLE(eagle)): + case GameEvent.SPAWN(EAGLE(teamId)): + var eagle = builder.buildEagle(teamId); + getTeam(teamId).eagleId = eagle.id; engine.spawn(eagle); case GameEvent.SPAWN(TANK(tank)): engine.spawn(tank); @@ -55,11 +62,15 @@ import ru.m.tankz.Type; case GameEvent.SPAWN(BONUS(bonus)): engine.spawn(bonus); case GameEvent.DESTROY(TANK(tank, who, wherewith, score)): - engine.destroy(tank); + engine.destroy(tank.id); case GameEvent.DESTROY(BONUS(bonus, who, score)): - engine.destroy(bonus); + engine.destroy(bonus.id); case GameEvent.DESTROY(BULLET(bullet)): - engine.destroy(bullet); + engine.destroy(bullet.id); + case GameEvent.ACTION(tankId, MOVE(direction)): + engine.move(tankId, direction); + case GameEvent.ACTION(tankId, STOP): + engine.stop(tankId); case _: } } diff --git a/src/common/haxe/ru/m/tankz/game/GameEvent.hx b/src/common/haxe/ru/m/tankz/game/GameEvent.hx index 63b8b4e..eff5275 100644 --- a/src/common/haxe/ru/m/tankz/game/GameEvent.hx +++ b/src/common/haxe/ru/m/tankz/game/GameEvent.hx @@ -1,14 +1,15 @@ package ru.m.tankz.game; -import ru.m.tankz.control.Control.TankAction; +import ru.m.tankz.control.Control; import ru.m.tankz.core.Bonus; +import ru.m.tankz.core.Bullet; import ru.m.tankz.core.Eagle; import ru.m.tankz.core.Tank; -import ru.m.tankz.core.Bullet; import ru.m.tankz.map.Grid; +import ru.m.tankz.Type.TeamId; enum SpawnEvent { - EAGLE(eagle:Eagle); + EAGLE(teamId:TeamId); TANK(tank:Tank); BULLET(bullet:Bullet); BONUS(bonus:Bonus); diff --git a/src/common/haxe/ru/m/tankz/game/GameRunner.hx b/src/common/haxe/ru/m/tankz/game/GameRunner.hx index e9221fe..5f6fec2 100644 --- a/src/common/haxe/ru/m/tankz/game/GameRunner.hx +++ b/src/common/haxe/ru/m/tankz/game/GameRunner.hx @@ -46,22 +46,6 @@ class GameRunner implements EngineListener implements GameListener { game.engine.update(); } - private function applyAction(tankId:Int, action:TankAction):Void { - if (!game.engine.entities.exists(tankId)) return; - var tank:Tank = cast game.engine.entities.get(tankId); - switch (action) { - case MOVE(direction): - tank.move(direction); - case STOP: - tank.stop(); - case SHOT: - var bullet = tank.shot(); - if (bullet != null) { - gameEventSignal.emit(GameEvent.SPAWN(BULLET(bullet))); - } - } - } - private function buildTank(task:SpawnTask):Tank { var player = game.getPlayer(task.playerId); var tankType:TankType = if (task.tankType != null) { @@ -110,13 +94,8 @@ class GameRunner implements EngineListener implements GameListener { team.spawner.push(player.id, player.state.tank); } } - var eaglePoint = team.spawner.getPoint("eagle"); - if (eaglePoint != null) { - var eagle = new Eagle(team.id, team.config.eagle); - eagle.color = game.config.getColor(new PlayerId(eagle.team, -1)); - team.eagleId = eagle.id; - applyPoint(eagle, eaglePoint); - gameEventSignal.emit(GameEvent.SPAWN(EAGLE(eagle))); + if (team.config.eagle != null) { + gameEventSignal.emit(GameEvent.SPAWN(EAGLE(team.id))); } } gameEventSignal.emit(GameEvent.START(state)); @@ -178,6 +157,9 @@ class GameRunner implements EngineListener implements GameListener { }, 5000); } + public function onSpawn(entity:EntityType):Void { + } + public function onCollision(entity:EntityType, with:EntityType):Void { switch entity { case EntityType.TANK(tank): @@ -317,8 +299,12 @@ class GameRunner implements EngineListener implements GameListener { timer.stop(); timer = null; } - case GameEvent.ACTION(tankId, action): - applyAction(tankId, action); + case GameEvent.ACTION(tankId, SHOT): + var tank:Tank = cast game.engine.entities.get(tankId); + var bullet = tank.shot(); + if (bullet != null) { + gameEventSignal.emit(GameEvent.SPAWN(BULLET(bullet))); + } case GameEvent.SPAWN(TANK(tank)): game.getPlayer(tank.playerId).control.start(); case GameEvent.DESTROY(EAGLE(eagle, who, wherewith, score)): diff --git a/src/common/haxe/ru/m/tankz/game/GameTracer.hx b/src/common/haxe/ru/m/tankz/game/GameTracer.hx index bbad7fc..e2d8380 100644 --- a/src/common/haxe/ru/m/tankz/game/GameTracer.hx +++ b/src/common/haxe/ru/m/tankz/game/GameTracer.hx @@ -1,8 +1,9 @@ package ru.m.tankz.game; -import ru.m.tankz.game.Game; +import ru.m.tankz.game.IGame; class GameTracer implements GameListener { + public function new() { } diff --git a/src/common/haxe/ru/m/tankz/game/record/EventItem.hx b/src/common/haxe/ru/m/tankz/game/record/EventItem.hx deleted file mode 100644 index 686d643..0000000 --- a/src/common/haxe/ru/m/tankz/game/record/EventItem.hx +++ /dev/null @@ -1,6 +0,0 @@ -package ru.m.tankz.game.record; - -typedef EventItem = { - frame:Int, - event:GameEvent -} diff --git a/src/common/haxe/ru/m/tankz/game/record/GamePlayer.hx b/src/common/haxe/ru/m/tankz/game/record/GamePlayer.hx index 8e599a5..c7531bb 100644 --- a/src/common/haxe/ru/m/tankz/game/record/GamePlayer.hx +++ b/src/common/haxe/ru/m/tankz/game/record/GamePlayer.hx @@ -2,17 +2,20 @@ package ru.m.tankz.game.record; import flash.events.Event; import flash.Lib; +import ru.m.tankz.game.record.GameRecord; class GamePlayer { private var frame:Int; private var game:IGame; + private var record:GameRecord; private var data:Array; - public function new(game:IGame, data:Array) { + public function new(game:IGame, record:GameRecord) { frame = 0; this.game = game; - this.data = data; + this.record = record; + this.data = null; } public function onGameEvent(event:GameEvent):Void { @@ -24,6 +27,7 @@ class GamePlayer { public function start():Void { frame = 0; + data = record.events.slice(0); Lib.current.stage.addEventListener(Event.ENTER_FRAME, onEnterFrame); } diff --git a/src/common/haxe/ru/m/tankz/game/record/GameRecord.hx b/src/common/haxe/ru/m/tankz/game/record/GameRecord.hx new file mode 100644 index 0000000..c9edaca --- /dev/null +++ b/src/common/haxe/ru/m/tankz/game/record/GameRecord.hx @@ -0,0 +1,21 @@ +package ru.m.tankz.game.record; + +import com.hurlant.crypto.extra.UUID; +import com.hurlant.crypto.prng.Random; + +typedef EventItem = { + frame:Int, + event:GameEvent +} + +class GameRecord { + public var id(default, default):String; + public var date(default, default):Date; + public var events(default, default):Array; + + public function new() { + this.id = UUID.generateRandom(new Random()).toString(); + this.date = null; + this.events = []; + } +} diff --git a/src/common/haxe/ru/m/tankz/game/record/GameRecorder.hx b/src/common/haxe/ru/m/tankz/game/record/GameRecorder.hx index 2c76104..a71f267 100644 --- a/src/common/haxe/ru/m/tankz/game/record/GameRecorder.hx +++ b/src/common/haxe/ru/m/tankz/game/record/GameRecorder.hx @@ -7,11 +7,11 @@ import ru.m.tankz.game.IGame; class GameRecorder implements GameListener { private var frame:Int; - public var data(default, null):Array; + public var record(default, null):GameRecord; public function new() { frame = 0; - data = []; + record = new GameRecord(); } public function onGameEvent(event:GameEvent):Void { @@ -22,11 +22,12 @@ class GameRecorder implements GameListener { stop(); case _: } - data.push({frame: frame, event: event}); + record.events.push({frame: frame, event: event}); } public function start():Void { frame = 0; + record.date = Date.now(); Lib.current.stage.addEventListener(Event.ENTER_FRAME, onEnterFrame); } diff --git a/src/common/resources/classic/config.yaml b/src/common/resources/classic/config.yaml index 4122850..14a65ec 100644 --- a/src/common/resources/classic/config.yaml +++ b/src/common/resources/classic/config.yaml @@ -34,6 +34,14 @@ player: - {type: bot2, rate: 0.27} - {type: bot3, rate: 0.15} +team: + human: &team_human + id: human + players: + - {<<: *human, index: 0, color: 0xFFFF00, control: human-0} + eagle: + score: 0 + points: - {team: human, type: eagle, index: -1, direction: right, x: 12, y: 24} - {team: human, type: tank, index: 0, direction: top, x: 8, y: 24} @@ -152,12 +160,10 @@ presets: - id: 0 name: easy teams: - - id: human - players: - - {<<: *human, index: 0, color: 0xFFFF00, control: human-0} + - {<<: *team_human} - id: bot spawnInterval: 3000 - life: 10 + life: 1 players: - {<<: *bot, index: 0, control: bot-stupid} - {<<: *bot, index: 1, control: bot-stupid} @@ -165,9 +171,7 @@ presets: - id: 1 name: normal teams: - - id: human - players: - - {<<: *human, index: 0, color: 0xFFFF00, control: human-0} + - {<<: *team_human} - id: bot spawnInterval: 3000 life: 20 @@ -180,9 +184,7 @@ presets: - id: 2 name: hard teams: - - id: human - players: - - {<<: *human, index: 0, color: 0xFFFF00, control: human-0} + - {<<: *team_human} - id: bot spawnInterval: 1000 life: 30