From ec259bb9e22a97fc1eed81163557da4401206bf2 Mon Sep 17 00:00:00 2001 From: shmyga Date: Tue, 18 Feb 2020 16:58:54 +0300 Subject: [PATCH] [update] big update --- package.json | 2 +- src/haxe/ru/m/Device.hx | 44 ++++++++++ src/haxe/ru/m/puzzlez/PuzzlezApp.hx | 4 + src/haxe/ru/m/puzzlez/core/GameUtil.hx | 13 +-- .../ru/m/puzzlez/render/ImagePartBuilder.hx | 7 +- src/haxe/ru/m/puzzlez/render/PartView.hx | 17 ++-- src/haxe/ru/m/puzzlez/render/Render.hx | 7 +- src/haxe/ru/m/puzzlez/render/RenderUtil.hx | 64 +++++++++++--- .../m/puzzlez/render/part/BasePartBuilder.hx | 6 +- src/haxe/ru/m/puzzlez/storage/GameStorage.hx | 5 +- src/haxe/ru/m/puzzlez/view/GameFrame.hx | 24 +++++- src/haxe/ru/m/puzzlez/view/PresetFrame.hx | 52 +++++++----- src/haxe/ru/m/puzzlez/view/PresetFrame.yaml | 25 ++---- src/haxe/ru/m/puzzlez/view/PresetView.hx | 55 ++++++------ src/haxe/ru/m/puzzlez/view/StartFrame.hx | 9 ++ src/haxe/ru/m/puzzlez/view/StartFrame.yaml | 5 ++ src/haxe/ru/m/update/Updater.hx | 83 +++++++++++++++++++ src/haxe/ru/m/update/Version.hx | 52 ++++++++++++ work.md | 5 +- 19 files changed, 367 insertions(+), 112 deletions(-) create mode 100644 src/haxe/ru/m/Device.hx create mode 100644 src/haxe/ru/m/update/Updater.hx create mode 100644 src/haxe/ru/m/update/Version.hx diff --git a/package.json b/package.json index 80fbb58..bd07a0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "puzzlez", - "version": "0.3.1", + "version": "0.3.2", "private": true, "devDependencies": { "dateformat": "^3.0.3", diff --git a/src/haxe/ru/m/Device.hx b/src/haxe/ru/m/Device.hx new file mode 100644 index 0000000..99bf839 --- /dev/null +++ b/src/haxe/ru/m/Device.hx @@ -0,0 +1,44 @@ +package ru.m; + +@:enum abstract Platform(String) from String to String { + var ANDROID = "android"; + var LINUX = "linux"; + var WINDOWS = "windows"; +} + +class Device { + + public static var platform(get, null):Platform; + + private static function get_platform():Platform { + #if android + return ANDROID; + #elseif linux + return LINUX; + #elseif windows + return WINDOWS; + #else + return null; + #end + } + + private static var MOBILES(default, never):Array = [ + "Android", "webOS", "iPhone", "iPad", "iPod", "BlackBerry", "Windows Phone", + ]; + + public static function isMobile():Bool { + #if android + return true; + #elseif js + var userAgent = js.Browser.navigator.userAgent; + for (mobile in MOBILES) { + if (userAgent.indexOf(mobile) > -1) { + return true; + } + } + return false; + #else + return false; + #end + } +} diff --git a/src/haxe/ru/m/puzzlez/PuzzlezApp.hx b/src/haxe/ru/m/puzzlez/PuzzlezApp.hx index 6067d9f..410ac89 100644 --- a/src/haxe/ru/m/puzzlez/PuzzlezApp.hx +++ b/src/haxe/ru/m/puzzlez/PuzzlezApp.hx @@ -5,14 +5,18 @@ import haxework.log.TraceLogger; import ru.m.puzzlez.storage.GameStorage; import ru.m.puzzlez.storage.ImageStorage; import ru.m.puzzlez.view.PuzzlezAppView; +import ru.m.update.Updater; class PuzzlezApp extends App { + @:provide static var updater:Updater; + public static function main() { // ToDo: fix @:provide macro GameStorage; ImageStorage; L.push(new TraceLogger()); + updater = new Updater(Const.VERSION, "https://shmyga.ru/repo/puzzlez/packages.json"); var app = new PuzzlezApp(new PuzzlezTheme(), openfl.Assets.getBitmapData("resources/icon.png")); var view = new PuzzlezAppView(); app.start(view); diff --git a/src/haxe/ru/m/puzzlez/core/GameUtil.hx b/src/haxe/ru/m/puzzlez/core/GameUtil.hx index 1b6aeed..c90c112 100644 --- a/src/haxe/ru/m/puzzlez/core/GameUtil.hx +++ b/src/haxe/ru/m/puzzlez/core/GameUtil.hx @@ -3,9 +3,10 @@ package ru.m.puzzlez.core; import haxework.geom.Point; import haxework.geom.Rectangle; import ru.m.puzzlez.core.BoundType; -import ru.m.puzzlez.core.GameState.GameStatus; +import ru.m.puzzlez.core.GameState; import ru.m.puzzlez.core.Id; import ru.m.puzzlez.core.Part; +import ru.m.puzzlez.core.PartLocation; import ru.m.puzzlez.core.Side; class BoundsMap { @@ -66,11 +67,13 @@ class BoundsMap { class GameUtil { - private static function normilizeSize(size:Int, maxValue:Int = 8):Int { + private static inline var MAX_SIZE = 20; + + private static function normilizeSize(size:Int, maxValue:Int = MAX_SIZE):Int { return Math.isNaN(size) ? maxValue : size < 1 ? 1 : size > maxValue ? maxValue : size; } - public static function buildPreset(imageId:ImageId, width:Int = 8, height:Int = 8):GamePreset { + public static function buildPreset(imageId:ImageId, width:Int = 8, height:Int = MAX_SIZE):GamePreset { width = normilizeSize(width); height = normilizeSize(height); var offsetX = 500; @@ -119,7 +122,7 @@ class GameUtil { id: id, gridX: x, gridY: y, - location: PartLocation.TABLE(position), + location: TABLE(position), bounds: bounds, rect: new Rectangle(0, 0, partWidth, partHeight), }); @@ -136,7 +139,7 @@ class GameUtil { var complete = 0; for (part in state.parts) { switch part.location { - case PartLocation.IMAGE: complete++; + case IMAGE: complete++; case _: } } diff --git a/src/haxe/ru/m/puzzlez/render/ImagePartBuilder.hx b/src/haxe/ru/m/puzzlez/render/ImagePartBuilder.hx index fd26390..eb7b4d6 100644 --- a/src/haxe/ru/m/puzzlez/render/ImagePartBuilder.hx +++ b/src/haxe/ru/m/puzzlez/render/ImagePartBuilder.hx @@ -1,5 +1,6 @@ package ru.m.puzzlez.render; +import ru.m.puzzlez.render.RenderUtil; import flash.display.BitmapData; import haxe.Timer; import promhx.PublicStream; @@ -8,7 +9,7 @@ import ru.m.puzzlez.core.Part; typedef Result = { var part:Part; - var image:BitmapData; + var image:PartImage; } class ImagePartBuilder { @@ -25,8 +26,8 @@ class ImagePartBuilder { return; } var part = parts[i]; - var partImage = RenderUtil.cropImagePart(image, part); - stream.update({part: part, image: partImage}); + var image = RenderUtil.cropImagePart(image, part); + stream.update({part: part, image: image}); } Timer.delay(() -> buildPart(index + count, count, parts, stream), 0); } diff --git a/src/haxe/ru/m/puzzlez/render/PartView.hx b/src/haxe/ru/m/puzzlez/render/PartView.hx index 2a7b507..8e6303c 100644 --- a/src/haxe/ru/m/puzzlez/render/PartView.hx +++ b/src/haxe/ru/m/puzzlez/render/PartView.hx @@ -1,11 +1,11 @@ package ru.m.puzzlez.render; import flash.display.Bitmap; -import flash.display.BitmapData; import flash.display.PixelSnapping; import flash.display.Sprite; import haxework.geom.Point; import ru.m.puzzlez.core.Part; +import ru.m.puzzlez.render.RenderUtil; class PartView extends Sprite { @@ -20,9 +20,9 @@ class PartView extends Sprite { return position; } - public var image(default, set):BitmapData; + public var image(default, set):PartImage; - private function set_image(value:BitmapData):BitmapData { + private function set_image(value:PartImage):PartImage { image = value; redraw(); refresh(); @@ -44,14 +44,15 @@ class PartView extends Sprite { private function refresh():Void { if (position != null && image != null) { - x = position.x - (image.width - size.x) / 2; - y = position.y - (image.height - size.y) / 2; + x = position.x - (image.borderedImage.width - size.x) / 2; + y = position.y - (image.borderedImage.height - size.y) / 2; } } public function complete():Void { position = target; completed = true; + redraw(); } public static function factory(part:Part):PartView { @@ -62,6 +63,10 @@ class PartView extends Sprite { class SpritePartView extends PartView { override private function redraw():Void { + if (image == null) { + return; + } + var image = completed ? image.borderedImage : image.shadedImage; graphics.clear(); graphics.beginBitmapFill(image, null, false, true); graphics.drawRect(0, 0, image.width, image.height); @@ -79,6 +84,6 @@ class BitmapPartView extends PartView { } override private function redraw():Void { - bitmap.bitmapData = image; + bitmap.bitmapData = completed ? image.borderedImage : image.shadedImage; } } diff --git a/src/haxe/ru/m/puzzlez/render/Render.hx b/src/haxe/ru/m/puzzlez/render/Render.hx index 5ce20bd..b87191b 100644 --- a/src/haxe/ru/m/puzzlez/render/Render.hx +++ b/src/haxe/ru/m/puzzlez/render/Render.hx @@ -1,6 +1,5 @@ package ru.m.puzzlez.render; -import haxework.geom.Point; import flash.display.BitmapData; import flash.display.PNGEncoderOptions; import flash.display.Sprite; @@ -9,12 +8,14 @@ import flash.geom.Point as FlashPoint; import flash.geom.Rectangle; import flash.net.FileReference; import flash.utils.ByteArray; +import haxework.geom.Point; import haxework.signal.Signal; import haxework.view.popup.AlertView; import haxework.view.SpriteView; import ru.m.puzzlez.core.GameEvent; import ru.m.puzzlez.core.GameState; import ru.m.puzzlez.core.PartLocation; +import ru.m.puzzlez.render.ImagePartBuilder; import ru.m.puzzlez.storage.ImageStorage; class Render extends SpriteView implements IRender { @@ -110,7 +111,7 @@ class Render extends SpriteView implements IRender { this.image = RenderUtil.cropImage(image, state.preset.imageRect); var builder = new ImagePartBuilder(this.image); var i = 0; - builder.build(state.parts).then(function(result) { + builder.build(state.parts).then((result:Result) -> { parts[result.part.id].image = result.image; progress.setProgress(++i, state.parts.length); if (i >= state.parts.length - 1) { @@ -166,7 +167,7 @@ class Render extends SpriteView implements IRender { private function save(part:PartView):Void { var file = new FileReference(); - var image = part.image; + var image = part.image.shadedImage; var data = new ByteArray(); image.encode(new Rectangle(0, 0, image.width, image.height), new PNGEncoderOptions(), data); file.save(data, "icon.png"); diff --git a/src/haxe/ru/m/puzzlez/render/RenderUtil.hx b/src/haxe/ru/m/puzzlez/render/RenderUtil.hx index b0d88d2..134274b 100644 --- a/src/haxe/ru/m/puzzlez/render/RenderUtil.hx +++ b/src/haxe/ru/m/puzzlez/render/RenderUtil.hx @@ -1,17 +1,40 @@ package ru.m.puzzlez.render; -import ru.m.draw.DrawPath; import flash.display.BitmapData; import flash.display.Shape; import flash.geom.Matrix; +import haxework.color.Color; +import haxework.geom.Point; import haxework.geom.Rectangle; +import ru.m.draw.DrawPath; import ru.m.puzzlez.core.Part; import ru.m.puzzlez.render.part.ClassicPartBuilder; import ru.m.puzzlez.render.part.IPartBuilder; import ru.m.puzzlez.render.part.PartMask; +typedef DrawSetting = { + var offset:Point; + var color:Color; + var opacity:Float; +} + +typedef PartImage = { + var borderedImage:BitmapData; + var shadedImage:BitmapData; +} + class RenderUtil { - private static var builder:IPartBuilder = new ClassicPartBuilder(); + public static var shadowSettings(default, null):Array = [ + {offset: new Point(-2, -2), color: 0x000000, opacity: 0.4}, + {offset: new Point(2, 2), color: 0xffffff, opacity: 0.4}, + ]; + + public static var borderSettings(default, null):Array = [ + {offset: new Point(-1, -1), color: 0x555555, opacity: 0.4}, + {offset: new Point(1, 1), color: 0xcccccc, opacity: 0.4}, + ]; + + public static var builder(default, null):IPartBuilder = new ClassicPartBuilder(); public static function cropImage(source:BitmapData, rect:Rectangle):BitmapData { var image = new BitmapData(Std.int(rect.width), Std.int(rect.height)); @@ -37,17 +60,14 @@ class RenderUtil { return {rect:rect, drawRect:drawRect}; } - private static function drawShadow(source:BitmapData, path:DrawPath):BitmapData { - var shadow = 2; - var opacity = 0.4; + private static function drawShadow(source:BitmapData, path:DrawPath, values:Array):BitmapData { var canvas = new Shape(); canvas.cacheAsBitmap = true; - canvas.graphics.beginFill(0x000000, opacity); - path.move(-shadow, -shadow).draw(canvas.graphics); - canvas.graphics.endFill(); - canvas.graphics.beginFill(0xffffff, opacity); - path.move(shadow, shadow).draw(canvas.graphics); - canvas.graphics.endFill(); + for (value in values) { + canvas.graphics.beginFill(value.color, value.opacity); + path.move(value.offset.x, value.offset.y).draw(canvas.graphics); + canvas.graphics.endFill(); + } canvas.graphics.beginBitmapFill(source, null, false, true); canvas.graphics.drawRect(0, 0, source.width, source.height); canvas.graphics.endFill(); @@ -56,7 +76,23 @@ class RenderUtil { return image; } - public static function cropImagePart(source:BitmapData, part:Part):BitmapData { + private static function drawBorder(source:BitmapData, path:DrawPath, values:Array):BitmapData { + var canvas = new Shape(); + canvas.cacheAsBitmap = true; + for (value in values) { + canvas.graphics.lineStyle(2, value.color, value.opacity); + path.move(value.offset.x, value.offset.y).draw(canvas.graphics); + canvas.graphics.lineStyle(); + } + canvas.graphics.beginBitmapFill(source, null, false, true); + canvas.graphics.drawRect(0, 0, source.width, source.height); + canvas.graphics.endFill(); + var image = new BitmapData(source.width, source.height, true, 0x00000000); + image.draw(canvas, null, null, null, null, true); + return image; + } + + public static function cropImagePart(source:BitmapData, part:Part, completed:Bool = false):PartImage { var geometry = buildPartGeometry(part); var path = builder.build(geometry.drawRect, part.bounds); var canvas:Shape = new Shape(); @@ -69,7 +105,9 @@ class RenderUtil { canvas.graphics.endFill(); var image = new BitmapData(Std.int(geometry.rect.width), Std.int(geometry.rect.height), true, 0x00000000); image.draw(canvas, null, null, null, null, true); - return drawShadow(image, path); + var shadedImage = drawShadow(image, path, shadowSettings); + var borderedImage = drawBorder(image, path, borderSettings); + return {borderedImage: borderedImage, shadedImage: shadedImage}; } public static function containRectangle(source:Rectangle, target:Rectangle):Rectangle { diff --git a/src/haxe/ru/m/puzzlez/render/part/BasePartBuilder.hx b/src/haxe/ru/m/puzzlez/render/part/BasePartBuilder.hx index f0c430a..7480202 100644 --- a/src/haxe/ru/m/puzzlez/render/part/BasePartBuilder.hx +++ b/src/haxe/ru/m/puzzlez/render/part/BasePartBuilder.hx @@ -10,8 +10,8 @@ class BasePartBuilder implements IPartBuilder { public function new() { } - private function createAngle(path:DrawPath, x:Float, y:Float):Void { - path.commands.push(LINE_TO(x, y)); + private function createAngle(path:DrawPath, x:Float, y:Float, first: Bool = false):Void { + path.commands.push(first ? MOVE_TO(x, y) : LINE_TO(x, y)); } private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, bound:PartBound):Void { @@ -19,7 +19,7 @@ class BasePartBuilder implements IPartBuilder { public function build(rect:Rectangle, bounds:PartBounds):DrawPath { var path = new DrawPath(); - createAngle(path, rect.left, rect.top); + createAngle(path, rect.left, rect.top, true); createBound(path, rect.left, rect.top, rect.right, rect.top, bounds.top); createAngle(path, rect.right, rect.top); createBound(path, rect.right, rect.top, rect.right, rect.bottom, bounds.right); diff --git a/src/haxe/ru/m/puzzlez/storage/GameStorage.hx b/src/haxe/ru/m/puzzlez/storage/GameStorage.hx index bb69753..b436429 100644 --- a/src/haxe/ru/m/puzzlez/storage/GameStorage.hx +++ b/src/haxe/ru/m/puzzlez/storage/GameStorage.hx @@ -44,7 +44,10 @@ import ru.m.puzzlez.core.Id; } public function delete(imageId:ImageId):Void { - Reflect.deleteField(statusData.data, imageId); + var gameData = SharedObject.getLocal('${path}/${imageId}'); + gameData.clear(); + var data:DynamicAccess = statusData.data; + data.remove(imageId); statusData.flush(); } diff --git a/src/haxe/ru/m/puzzlez/view/GameFrame.hx b/src/haxe/ru/m/puzzlez/view/GameFrame.hx index b84d390..57bcd67 100644 --- a/src/haxe/ru/m/puzzlez/view/GameFrame.hx +++ b/src/haxe/ru/m/puzzlez/view/GameFrame.hx @@ -1,5 +1,6 @@ package ru.m.puzzlez.view; +import haxe.Timer; import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameView; import ru.m.puzzlez.core.Game; @@ -17,7 +18,7 @@ import ru.m.puzzlez.storage.GameStorage; @:provide var switcher:FrameSwitcher; @:provide var storage:GameStorage; - private var toSave:Bool; + private var saveTimer:Timer; public function new() { super(ID); @@ -33,6 +34,7 @@ import ru.m.puzzlez.storage.GameStorage; } override public function onHide():Void { + save(); if (game != null) { render.signal.disconnect(game.signal.emit); game.stop(); @@ -41,12 +43,28 @@ import ru.m.puzzlez.storage.GameStorage; } } + private function toSave():Void { + if (saveTimer != null) { + saveTimer = Timer.delay(save, 500); + } + } + + private function save():Void { + if (saveTimer != null) { + saveTimer.stop(); + saveTimer = null; + } + if (game != null) { + storage.save(game.state); + } + } + private function onGameEvent(event:GameEvent):Void { switch event { case START(state): - storage.save(state); + toSave(); case ACTION(PART_PUT(_, _)): - storage.save(game.state); + toSave(); case _: } } diff --git a/src/haxe/ru/m/puzzlez/view/PresetFrame.hx b/src/haxe/ru/m/puzzlez/view/PresetFrame.hx index e77e6ca..114fe4d 100644 --- a/src/haxe/ru/m/puzzlez/view/PresetFrame.hx +++ b/src/haxe/ru/m/puzzlez/view/PresetFrame.hx @@ -1,8 +1,10 @@ package ru.m.puzzlez.view; -import haxework.view.form.InputView; + +import haxework.geom.IntPoint; +import haxework.view.data.DataView; +import haxework.view.form.ToggleButtonView; import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameView; -import haxework.view.list.ScrollBarView; import ru.m.puzzlez.core.GameUtil; import ru.m.puzzlez.core.Id; import ru.m.puzzlez.storage.ImageStorage; @@ -11,9 +13,7 @@ import ru.m.puzzlez.storage.ImageStorage; public static var ID = "preset"; @:view("image") var imageView:PresetView; - @:view("scroll") var scrollView:ScrollBarView; - @:view("width") var widthView:InputView; - @:view("height") var heightView:InputView; + @:view("sizes") var sizesView:DataView; @:provide var imageStorage:ImageStorage; @:provide var switcher:FrameSwitcher; @@ -22,32 +22,38 @@ import ru.m.puzzlez.storage.ImageStorage; public function new() { super(ID); - widthView.text = "8"; - heightView.text = "8"; - scrollView.position = 0.8; - widthView.update(); - heightView.update(); + sizesView.data = [ + new IntPoint(3, 2), + new IntPoint(4, 3), + new IntPoint(5, 4), + new IntPoint(6, 5), + new IntPoint(7, 6), + new IntPoint(8, 7), + new IntPoint(10, 8), + ]; + } + + private function factory(index:Int, size:IntPoint):ToggleButtonView { + var result = new ToggleButtonView(); + result.text = '${size.x}x${size.y}'; + return result; + } + + private function selectSize(size:IntPoint):Void { + imageView.state = GameUtil.buildState(GameUtil.buildPreset(imageId, size.x, size.y)); + for (i in 0...sizesView.dataViews.length) { + sizesView.dataViews[i].on = size == sizesView.data[i]; + } } override public function onShow(data:ImageId):Void { super.onShow(data); imageId = data; - updatePreset(); - } - - private function updateGrid(value:Float):Void { - var size = 2 + Std.int(value * 8); - widthView.text = Std.string(size); - heightView.text = Std.string(size); - imageView.preset = GameUtil.buildPreset(imageId, size, size); - } - - private function updatePreset():Void { - imageView.preset = GameUtil.buildPreset(imageId, Std.parseInt(widthView.text), Std.parseInt(heightView.text)); + selectSize(sizesView.data[sizesView.data.length - 1]); } private function start():Void { - switcher.change(GameFrame.ID, GameUtil.buildState(imageView.preset)); + switcher.change(GameFrame.ID, imageView.state); } private function back():Void { diff --git a/src/haxe/ru/m/puzzlez/view/PresetFrame.yaml b/src/haxe/ru/m/puzzlez/view/PresetFrame.yaml index 30397ed..0eba2ec 100644 --- a/src/haxe/ru/m/puzzlez/view/PresetFrame.yaml +++ b/src/haxe/ru/m/puzzlez/view/PresetFrame.yaml @@ -1,32 +1,25 @@ --- style: frame +layout.margin: 10 views: - $type: haxework.view.group.HGroupView geometry.width: 100% layout.hAlign: center layout.vAlign: middle views: - - id: width - $type: haxework.view.form.InputView - geometry.width: 100 - +onChange: ~updatePreset() - - $type: haxework.view.form.LabelView - text: x - - id: height - $type: haxework.view.form.InputView - geometry.width: 100 - +onChange: ~updatePreset() - $type: haxework.view.form.ButtonView geometry.margin.left: 15 text: Start +onPress: ~start() - - id: scroll - $type: haxework.view.list.HScrollBarView + - id: sizes + $type: haxework.view.data.DataView geometry.width: 100% - geometry.height: 20 - ratio: 0.2 - position: 0.8 - +onScroll: ~updateGrid + layout: + $type: haxework.view.layout.TailLayout + hAlign: center + margin: 5 + factory: ~factory + +onDataSelect: ~selectSize - id: image $type: ru.m.puzzlez.view.PresetView geometry.stretch: true diff --git a/src/haxe/ru/m/puzzlez/view/PresetView.hx b/src/haxe/ru/m/puzzlez/view/PresetView.hx index 47ec55f..156f892 100644 --- a/src/haxe/ru/m/puzzlez/view/PresetView.hx +++ b/src/haxe/ru/m/puzzlez/view/PresetView.hx @@ -5,7 +5,7 @@ import flash.display.Graphics; import flash.display.Shape; import flash.geom.Matrix; import haxework.view.group.GroupView; -import ru.m.puzzlez.core.GamePreset; +import ru.m.puzzlez.core.GameState; import ru.m.puzzlez.render.RenderUtil; import ru.m.puzzlez.storage.ImageStorage; @@ -20,23 +20,23 @@ class PresetView extends GroupView { private function set_scale(value:Float):Float { var result = table.scaleX = table.scaleY = value; - table.x = (width - preset.tableRect.width * value) / 2; - table.y = (height - preset.tableRect.height * value) / 2; + table.x = (width - state.preset.tableRect.width * value) / 2; + table.y = (height - state.preset.tableRect.height * value) / 2; return result; } - public var preset(default, set):GamePreset; + public var state(default, set):GameState; - private function set_preset(value:GamePreset) { - preset = value; + private function set_state(value:GameState):GameState { + state = value; this.image = null; table.graphics.clear(); - loading.promise = imageStorage.resolve(preset.imageId).then(image -> { - this.image = RenderUtil.cropImage(image, preset.imageRect); + loading.promise = imageStorage.resolve(state.preset.imageId).then(image -> { + this.image = RenderUtil.cropImage(image, state.preset.imageRect); toRedraw(); toUpdate(); }); - return preset; + return state; } private var loading:LoadingWrapper; @@ -52,9 +52,10 @@ class PresetView extends GroupView { private var image:BitmapData; override public function redraw():Void { - if (preset == null || image == null) { + if (state == null || image == null) { return; } + var preset = state.preset; var partWidth = preset.imageRect.width / preset.grid.width; var partHeight = preset.imageRect.height / preset.grid.height; var graphics:Graphics = table.graphics; @@ -64,32 +65,24 @@ class PresetView extends GroupView { graphics.beginBitmapFill(image, matrix, false, true); graphics.drawRect(preset.imageRect.x, preset.imageRect.y, preset.imageRect.width, preset.imageRect.height); graphics.endFill(); - graphics.lineStyle(1, 0x555555); - for (x in 0...preset.grid.width + 1) { - graphics.moveTo(preset.imageRect.x + x * partWidth, preset.imageRect.y); - graphics.lineTo(preset.imageRect.x + x * partWidth, preset.imageRect.y + preset.imageRect.height); + + for (part in state.parts) { + var rect = part.rect.clone(); + rect.x = preset.imageRect.x + part.gridX * part.rect.width; + rect.y = preset.imageRect.y + part.gridY * part.rect.height; + var path = RenderUtil.builder.build(rect, part.bounds); + for (value in RenderUtil.borderSettings) { + graphics.lineStyle(1, value.color, value.opacity); + path.move(value.offset.x, value.offset.y).draw(graphics); + graphics.lineStyle(); + } } - for (y in 0...preset.grid.height + 1) { - graphics.moveTo(preset.imageRect.x, preset.imageRect.y + y * partHeight); - graphics.lineTo(preset.imageRect.x + preset.imageRect.width, preset.imageRect.y + y * partHeight); - } - graphics.lineStyle(1, 0xcccccc); - var offset = 1; - for (x in 0...preset.grid.width + 1) { - graphics.moveTo(offset + preset.imageRect.x + x * partWidth, preset.imageRect.y); - graphics.lineTo(offset + preset.imageRect.x + x * partWidth, preset.imageRect.y + preset.imageRect.height); - } - for (y in 0...preset.grid.height + 1) { - graphics.moveTo(preset.imageRect.x, offset + preset.imageRect.y + y * partHeight); - graphics.lineTo(preset.imageRect.x + preset.imageRect.width, offset + preset.imageRect.y + y * partHeight); - } - graphics.lineStyle(); } override public function update():Void { super.update(); - if (preset != null) { - scale = Math.min(width / preset.tableRect.width, height / preset.tableRect.height); + if (state != null) { + scale = Math.min(width / state.preset.tableRect.width, height / state.preset.tableRect.height); } } } diff --git a/src/haxe/ru/m/puzzlez/view/StartFrame.hx b/src/haxe/ru/m/puzzlez/view/StartFrame.hx index 2a2aaef..8db0a24 100644 --- a/src/haxe/ru/m/puzzlez/view/StartFrame.hx +++ b/src/haxe/ru/m/puzzlez/view/StartFrame.hx @@ -1,5 +1,6 @@ package ru.m.puzzlez.view; +import ru.m.update.Updater; import haxework.view.data.DataView; import haxework.view.form.ButtonView; import haxework.view.frame.FrameSwitcher; @@ -18,10 +19,12 @@ import ru.m.puzzlez.storage.ImageStorage; @:view var sources:DataView, ButtonView>; @:view("load") var loadButton:ButtonView; @:view("complete") var completeButton:ButtonView; + @:view("update") var updateButton:ButtonView; @:provide var storage:ImageStorage; @:provide var switcher:FrameSwitcher; @:provide var gameStorage:GameStorage; + @:provide static var appUpdater:Updater; public function new() { super(ID); @@ -45,6 +48,12 @@ import ru.m.puzzlez.storage.ImageStorage; override public function onShow(data:Dynamic):Void { refresh(); + appUpdater.check().then((info:Null) -> { + if (info != null) { + updateButton.visible = true; + updateButton.text = 'Update ${info.version}'; + } + }); } private function sourceViewFactory(index:Int, source:ImageListSource):ButtonView { diff --git a/src/haxe/ru/m/puzzlez/view/StartFrame.yaml b/src/haxe/ru/m/puzzlez/view/StartFrame.yaml index 0f3ceb1..a792521 100644 --- a/src/haxe/ru/m/puzzlez/view/StartFrame.yaml +++ b/src/haxe/ru/m/puzzlez/view/StartFrame.yaml @@ -38,6 +38,11 @@ views: text: Clean +onPress: ~clean() visible: false + - id: update + $type: haxework.view.form.ButtonView + style: button.active + +onPress: ~appUpdater.download() + visible: false - $type: haxework.view.form.LabelView text: $r:text:version geometry.position: absolute diff --git a/src/haxe/ru/m/update/Updater.hx b/src/haxe/ru/m/update/Updater.hx new file mode 100644 index 0000000..119d3ac --- /dev/null +++ b/src/haxe/ru/m/update/Updater.hx @@ -0,0 +1,83 @@ +package ru.m.update; + +import haxework.net.JsonLoader; +import openfl.Lib; +import openfl.net.URLRequest; +import promhx.Promise; +import ru.m.Device; +import ru.m.update.Version; + +@:enum abstract PackageType(String) from String to String { + var APK = "apk"; + var DEB = "deb"; + var EXE = "exe"; + var ARCHIVE = "archive"; +} + +typedef PackageInfo = { + var platform:Platform; + var type:PackageType; + var path:String; + var filename:String; + var url:String; + var version:String; +} + +typedef PackagesBundle = { + var name:String; + var version:String; + var packages:Array; +} + +class Updater { + + private static inline var TAG = "Update"; + + public var type(get, null):PackageType; + public var bundle(get, null):Promise; + + private function get_bundle():Promise { + if (bundle == null) { + bundle = new JsonLoader().GET(url); + } + return bundle; + } + + private var url:String; + private var version:Version; + + public function new(version:String, url:String) { + this.url = url; + this.version = version; + } + + private function get_type():PackageType { + return switch Device.platform { + case ANDROID: APK; + case LINUX: DEB; + case WINDOWS: EXE; + case _: null; + } + } + + public function check():Promise> { + return bundle.then((bundle:PackagesBundle) -> Lambda.find(bundle.packages, item -> ( + item.platform == Device.platform && + item.type == type && + version < Version.fromString(item.version) + ))); + } + + public function download():Promise { + return bundle.then((bundle:PackagesBundle) -> { + for (item in bundle.packages) { + if (item.platform == Device.platform && item.type == type) { + L.i(TAG, 'download: ${item.url}'); + Lib.getURL(new URLRequest(item.url)); + return true; + } + } + return false; + }); + } +} diff --git a/src/haxe/ru/m/update/Version.hx b/src/haxe/ru/m/update/Version.hx new file mode 100644 index 0000000..49af272 --- /dev/null +++ b/src/haxe/ru/m/update/Version.hx @@ -0,0 +1,52 @@ +package ru.m.update; + +abstract Version(Array) { + + public function new(mayor:Int, minor:Int, patch:Int, snapshot:Bool = false) { + this = [ + mayor, + minor, + patch, + snapshot ? -1 : 0, + ]; + } + + @:arrayAccess public inline function get(key:Int) return this[key]; + + public function compare(other:Version):Int { + if (other == null) { + return 1; + } + for (i in 0...4) { + var d = this[i] - other[i]; + if (d != 0) { + return d; + } + } + return 0; + } + + @:op(A > B) static function gt(a:Version, b:Version):Bool return a.compare(b) > 0; + + @:op(A == B) static function eq(a:Version, b:Version):Bool return a.compare(b) == 0; + + @:op(A < B) static function lt(a:Version, b:Version):Bool return a.compare(b) < 0; + + @:from public static function fromString(value:String):Version { + var r = ~/(\d+)\.(\d+)\.(\d+)(-SNAPSHOT)?/; + return if (r.match(value)) { + new Version( + Std.parseInt(r.matched(1)), + Std.parseInt(r.matched(2)), + Std.parseInt(r.matched(3)), + r.matched(4) != null + ); + } else { + new Version(0, 0, 0); + } + } + + @:to public function toString():String { + return '${this[0]}.${this[1]}.${this[2]}${this[3] < 0 ? "-SNAPSHOT" : ""}'; + } +} diff --git a/work.md b/work.md index 9c8bac4..694e008 100644 --- a/work.md +++ b/work.md @@ -1,6 +1,3 @@ -* win event -* save game state -* load user images * background settings -* grid settings +* preset frame improve * parts groups