From fffb6a32149ba9e6ba2aa4005fa8fa63662874e5 Mon Sep 17 00:00:00 2001 From: shmyga Date: Wed, 23 Oct 2019 17:53:19 +0300 Subject: [PATCH] update --- .gitignore | 1 + WORK.md | 9 ++- package.json | 2 +- src/client/haxe/ru/m/tankz/render/Render.hx | 7 +- src/client/haxe/ru/m/tankz/view/ClientView.hx | 30 ++++---- src/client/haxe/ru/m/tankz/view/GameFrame.hx | 12 +++ src/common/haxe/ru/m/tankz/game/GameEvent.hx | 1 + src/common/haxe/ru/m/tankz/game/GameRunner.hx | 1 + src/common/haxe/ru/m/tankz/util/LevelUtil.hx | 21 ++++-- src/common/haxe/ru/m/yaml/SortingRenderer.hx | 71 ++++++++++++++++++ src/common/resources/level/classic_modern.zip | Bin 5955 -> 6669 bytes 11 files changed, 129 insertions(+), 26 deletions(-) create mode 100644 src/common/haxe/ru/m/yaml/SortingRenderer.hx diff --git a/.gitignore b/.gitignore index b697065..5e323f6 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +build/ target/ src-gen/ out/ diff --git a/WORK.md b/WORK.md index 8bdeeb8..38a0fa6 100644 --- a/WORK.md +++ b/WORK.md @@ -1,8 +1,7 @@ * death game bonus * tanks and bullets speed balancing -* clean player state on restart in classic type +* clean player state on lose in classic type * network series -* map pack version (or hash) * bot upgrade * game config validate * mine z-order @@ -13,6 +12,10 @@ * display ammo count (screen gamepad) * bonus ammo * game panel rework -* pause * game state: config, map, entities, players * game menu pack progress +* balloons: change mute, network, pause +* render: animations pause +* settings: mute, pause, reset device actions +* game view: pause button +* game view: toggle panel on mobile diff --git a/package.json b/package.json index 8e28731..d83e6cd 100755 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "gulp": "^4.0.0", "gulp-add": "0.0.2", "gulp-clean": "^0.4.0", - "gulp-haxetool": "0.1.1", + "gulp-haxetool": "0.1.2", "yargs": "^13.2.4" }, "haxeDependencies": { diff --git a/src/client/haxe/ru/m/tankz/render/Render.hx b/src/client/haxe/ru/m/tankz/render/Render.hx index 61b1ad6..48ea9c9 100755 --- a/src/client/haxe/ru/m/tankz/render/Render.hx +++ b/src/client/haxe/ru/m/tankz/render/Render.hx @@ -38,6 +38,7 @@ class Render extends SpriteView implements IRender { private var upperLayer:Sprite; private var items:Map; + private var paused:Bool; public function new() { super(); @@ -92,7 +93,9 @@ class Render extends SpriteView implements IRender { } private function onEnterFrame(event:Event):Void { - draw(); + if (!paused) { + draw(); + } } private function clearLayer(layer:DisplayObjectContainer) { @@ -120,6 +123,8 @@ class Render extends SpriteView implements IRender { content.addEventListener(Event.ENTER_FRAME, onEnterFrame); case COMPLETE(_): content.removeEventListener(Event.ENTER_FRAME, onEnterFrame); + case PAUSE(paused): + this.paused = paused; case SPAWN(BRICK(bricks)): drawBackground(); for (brick in bricks) { diff --git a/src/client/haxe/ru/m/tankz/view/ClientView.hx b/src/client/haxe/ru/m/tankz/view/ClientView.hx index 1a803aa..a55fb7a 100644 --- a/src/client/haxe/ru/m/tankz/view/ClientView.hx +++ b/src/client/haxe/ru/m/tankz/view/ClientView.hx @@ -1,11 +1,12 @@ package ru.m.tankz.view; -import flash.events.KeyboardEvent; import flash.ui.Keyboard; import haxework.resources.IResources; import haxework.view.frame.FrameSwitcher; import haxework.view.group.VGroupView; -import ru.m.tankz.game.IGame; +import ru.m.control.DeviceAction; +import ru.m.control.DeviceType; +import ru.m.control.IControlBus; import ru.m.tankz.network.NetworkManager; import ru.m.tankz.sound.SoundManager; @@ -16,7 +17,7 @@ import ru.m.tankz.sound.SoundManager; @:provide var resources:IResources; @:provide var switcher:FrameSwitcher; @:provide var soundManager:SoundManager; - @:provide var game:IGame; + @:provide static var bus:IControlBus; public function init():Void { resources.text.put('version', '${Const.VERSION}'); @@ -26,18 +27,17 @@ import ru.m.tankz.sound.SoundManager; public function launch():Void { content.stage.stageFocusRect = false; - content.stage.addEventListener(KeyboardEvent.KEY_UP, function(event:KeyboardEvent):Void { - switch event.keyCode { - case Keyboard.ESCAPE: - switcher.change(MenuFrame.ID); - case Keyboard.M: - soundManager.mute = !soundManager.mute; - case Keyboard.P: - if (game != null) { - game.pause = !game.pause; - } - } - }); + bus.signal.connect(onDeviceAction); switcher.change(MenuFrame.ID); } + + private function onDeviceAction(type:DeviceType, action:DeviceAction, state:Bool):Void { + switch [type, action, state] { + case [KEYBOARD, KEY(Keyboard.ESCAPE), true]: + switcher.change(MenuFrame.ID); + case [KEYBOARD, KEY(Keyboard.M), true]: + soundManager.mute = !soundManager.mute; + case _: + } + } } diff --git a/src/client/haxe/ru/m/tankz/view/GameFrame.hx b/src/client/haxe/ru/m/tankz/view/GameFrame.hx index 6bbb1d8..7aa11d4 100644 --- a/src/client/haxe/ru/m/tankz/view/GameFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/GameFrame.hx @@ -1,5 +1,7 @@ package ru.m.tankz.view; +import flash.ui.Keyboard; +import ru.m.control.DeviceAction; import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameView; import haxework.view.group.GroupView; @@ -81,9 +83,19 @@ import ru.m.tankz.view.gamepad.GamepadView; } } } + bus.signal.connect(onDeviceAction); + } + + private function onDeviceAction(type:DeviceType, action:DeviceAction, state:Bool):Void { + switch [type, action, state] { + case [KEYBOARD, KEY(Keyboard.P), true]: + game.pause = !game.pause; + case _: + } } private function stop():Void { + bus.signal.disconnect(onDeviceAction); if (game != null) { game.dispose(); game = null; diff --git a/src/common/haxe/ru/m/tankz/game/GameEvent.hx b/src/common/haxe/ru/m/tankz/game/GameEvent.hx index b9ac393..cd3150f 100644 --- a/src/common/haxe/ru/m/tankz/game/GameEvent.hx +++ b/src/common/haxe/ru/m/tankz/game/GameEvent.hx @@ -86,4 +86,5 @@ enum GameEvent { CHANGE(event:ChangeEvent); COMPLETE(result:Result); ACTION(tankId:Int, action:TankAction); + PAUSE(paused:Bool); } diff --git a/src/common/haxe/ru/m/tankz/game/GameRunner.hx b/src/common/haxe/ru/m/tankz/game/GameRunner.hx index 8344f5e..9557202 100644 --- a/src/common/haxe/ru/m/tankz/game/GameRunner.hx +++ b/src/common/haxe/ru/m/tankz/game/GameRunner.hx @@ -46,6 +46,7 @@ class GameRunner extends Game implements EngineListener { } else { engine.ticker.start(); } + gameEventSignal.emit(PAUSE(value)); } } diff --git a/src/common/haxe/ru/m/tankz/util/LevelUtil.hx b/src/common/haxe/ru/m/tankz/util/LevelUtil.hx index 5bcb5a9..e176e30 100644 --- a/src/common/haxe/ru/m/tankz/util/LevelUtil.hx +++ b/src/common/haxe/ru/m/tankz/util/LevelUtil.hx @@ -7,6 +7,7 @@ import haxe.zip.Entry; import haxe.zip.Writer; import ru.m.tankz.config.Config; import ru.m.tankz.Type; +import ru.m.yaml.SortingRenderer; import yaml.Parser; import yaml.Renderer; import yaml.Yaml; @@ -20,6 +21,14 @@ typedef LevelSource = { class LevelUtil { + private static function yamlParse(data:String):Dynamic { + return Yaml.parse(data, Parser.options().useObjects()); + } + + private static function yamlRender(data:Dynamic):String { + return new SortingRenderer().render(data, Renderer.options().setFlowLevel(1)); + } + public static function formatLevel(level:Int):String { var result = Std.string(level); while (result.length < 3) result = '0${result}'; @@ -27,7 +36,7 @@ class LevelUtil { } public static function loads(data:String):LevelConfig { - var obj:LevelSource = Yaml.parse(data, Parser.options().useObjects()); + var obj:LevelSource = yamlParse(data); return { data: obj.data.split('').map(function(c) return Std.parseInt(c)), points: obj.points, @@ -38,12 +47,12 @@ class LevelUtil { public static function dumps(level:LevelConfig):String { var bricksStr = level.data.join(''); - return Yaml.render({ + return yamlRender({ data: bricksStr, points: level.points, name: level.name, size: level.size, - }, Renderer.options().setFlowLevel(1)); + }); } public static function empty(size:GridSize, filler:BrickIndex):LevelConfig { @@ -58,7 +67,7 @@ class LevelUtil { for (entry in files) { if (entry.fileName == "meta.yml") { var content = ZipUtil.extractEntry(entry); - return Yaml.parse(content, Parser.options().useObjects()); + return yamlParse(content); } } return null; @@ -71,7 +80,7 @@ class LevelUtil { for (entry in files) { var content = ZipUtil.extractEntry(entry); if (entry.fileName == "meta.yml") { - meta = Yaml.parse(content, Parser.options().useObjects()); + meta = yamlParse(content); } else { var level:LevelConfig = LevelUtil.loads(content); level.packId = meta.id; @@ -91,7 +100,7 @@ class LevelUtil { public static function pack(pack:LevelPack):Bytes { var output = new BytesOutput(); var writer = new Writer(output); - var metaEntry:Entry = ZipUtil.createEntry("meta.yml", Yaml.render(pack.meta, Renderer.options().setFlowLevel(1))); + var metaEntry:Entry = ZipUtil.createEntry("meta.yml", yamlRender(pack.meta)); var levelEntries:Array = pack.data.map(function(level) return ZipUtil.createEntry('${formatLevel(level.id)}.yml', dumps(level))); writer.write(Lambda.list([metaEntry].concat(levelEntries))); return output.getBytes(); diff --git a/src/common/haxe/ru/m/yaml/SortingRenderer.hx b/src/common/haxe/ru/m/yaml/SortingRenderer.hx new file mode 100644 index 0000000..2788eec --- /dev/null +++ b/src/common/haxe/ru/m/yaml/SortingRenderer.hx @@ -0,0 +1,71 @@ +package ru.m.yaml; + +import yaml.Renderer; + +class SortingRenderer extends Renderer { + + private static function fields(object:Dynamic):Array { + var result = Reflect.fields(object); + result.sort(function(a, b) return a > b ? 1 : -1); + return result; + } + + override function writeObjectFlowMapping(level:Int, object:Dynamic) + { + var _result = ''; + var _tag = tag; + var index = 0; + var objectKey; + + for (objectKey in fields(object)) + { + if (0 != index++) + _result += ', '; + + var objectValue = Reflect.field(object, objectKey); + + writeNode(level, objectKey, false, false); + + if (result.length > 1024) + _result += '? '; + + _result += result + ': '; + writeNode(level, objectValue, false, false); + _result += result; + } + + tag = _tag; + result = '{' + _result + '}'; + } + + override function writeObjectBlockMapping(level:Int, object:Dynamic, compact:Bool) + { + var _result = ''; + var _tag = tag; + var index = 0; + + for (objectKey in fields(object)) + { + if (!compact || 0 != index++) + _result += generateNextLine(level); + + var objectValue = Reflect.field(object, objectKey); + writeNode(level + 1, objectKey, true, true); + var explicitPair = (null != tag && '?' != tag && result.length <= 1024); + + if (explicitPair) + _result += '? '; + + _result += result; + + if (explicitPair) + _result += generateNextLine(level); + + writeNode(level + 1, objectValue, true, explicitPair); + _result += ': ' + result; + } + + tag = _tag; + result = _result; + } +} diff --git a/src/common/resources/level/classic_modern.zip b/src/common/resources/level/classic_modern.zip index fca9f58a89fff44a7e532dec8e3d3640786a4fee..d61b8c74b65db71ae937c2acd4b02e0bf5cb6c47 100644 GIT binary patch literal 6669 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 MP>hcYuADyo4;-z7`2YX_