[update] big update

This commit is contained in:
2020-02-18 16:58:54 +03:00
parent 6550975a4e
commit ec259bb9e2
19 changed files with 367 additions and 112 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "puzzlez", "name": "puzzlez",
"version": "0.3.1", "version": "0.3.2",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"dateformat": "^3.0.3", "dateformat": "^3.0.3",

44
src/haxe/ru/m/Device.hx Normal file
View File

@@ -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<String> = [
"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
}
}

View File

@@ -5,14 +5,18 @@ import haxework.log.TraceLogger;
import ru.m.puzzlez.storage.GameStorage; import ru.m.puzzlez.storage.GameStorage;
import ru.m.puzzlez.storage.ImageStorage; import ru.m.puzzlez.storage.ImageStorage;
import ru.m.puzzlez.view.PuzzlezAppView; import ru.m.puzzlez.view.PuzzlezAppView;
import ru.m.update.Updater;
class PuzzlezApp extends App { class PuzzlezApp extends App {
@:provide static var updater:Updater;
public static function main() { public static function main() {
// ToDo: fix @:provide macro // ToDo: fix @:provide macro
GameStorage; GameStorage;
ImageStorage; ImageStorage;
L.push(new TraceLogger()); 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 app = new PuzzlezApp(new PuzzlezTheme(), openfl.Assets.getBitmapData("resources/icon.png"));
var view = new PuzzlezAppView(); var view = new PuzzlezAppView();
app.start(view); app.start(view);

View File

@@ -3,9 +3,10 @@ package ru.m.puzzlez.core;
import haxework.geom.Point; import haxework.geom.Point;
import haxework.geom.Rectangle; import haxework.geom.Rectangle;
import ru.m.puzzlez.core.BoundType; 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.Id;
import ru.m.puzzlez.core.Part; import ru.m.puzzlez.core.Part;
import ru.m.puzzlez.core.PartLocation;
import ru.m.puzzlez.core.Side; import ru.m.puzzlez.core.Side;
class BoundsMap { class BoundsMap {
@@ -66,11 +67,13 @@ class BoundsMap {
class GameUtil { 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; 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); width = normilizeSize(width);
height = normilizeSize(height); height = normilizeSize(height);
var offsetX = 500; var offsetX = 500;
@@ -119,7 +122,7 @@ class GameUtil {
id: id, id: id,
gridX: x, gridX: x,
gridY: y, gridY: y,
location: PartLocation.TABLE(position), location: TABLE(position),
bounds: bounds, bounds: bounds,
rect: new Rectangle(0, 0, partWidth, partHeight), rect: new Rectangle(0, 0, partWidth, partHeight),
}); });
@@ -136,7 +139,7 @@ class GameUtil {
var complete = 0; var complete = 0;
for (part in state.parts) { for (part in state.parts) {
switch part.location { switch part.location {
case PartLocation.IMAGE: complete++; case IMAGE: complete++;
case _: case _:
} }
} }

View File

@@ -1,5 +1,6 @@
package ru.m.puzzlez.render; package ru.m.puzzlez.render;
import ru.m.puzzlez.render.RenderUtil;
import flash.display.BitmapData; import flash.display.BitmapData;
import haxe.Timer; import haxe.Timer;
import promhx.PublicStream; import promhx.PublicStream;
@@ -8,7 +9,7 @@ import ru.m.puzzlez.core.Part;
typedef Result = { typedef Result = {
var part:Part; var part:Part;
var image:BitmapData; var image:PartImage;
} }
class ImagePartBuilder { class ImagePartBuilder {
@@ -25,8 +26,8 @@ class ImagePartBuilder {
return; return;
} }
var part = parts[i]; var part = parts[i];
var partImage = RenderUtil.cropImagePart(image, part); var image = RenderUtil.cropImagePart(image, part);
stream.update({part: part, image: partImage}); stream.update({part: part, image: image});
} }
Timer.delay(() -> buildPart(index + count, count, parts, stream), 0); Timer.delay(() -> buildPart(index + count, count, parts, stream), 0);
} }

View File

@@ -1,11 +1,11 @@
package ru.m.puzzlez.render; package ru.m.puzzlez.render;
import flash.display.Bitmap; import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.PixelSnapping; import flash.display.PixelSnapping;
import flash.display.Sprite; import flash.display.Sprite;
import haxework.geom.Point; import haxework.geom.Point;
import ru.m.puzzlez.core.Part; import ru.m.puzzlez.core.Part;
import ru.m.puzzlez.render.RenderUtil;
class PartView extends Sprite { class PartView extends Sprite {
@@ -20,9 +20,9 @@ class PartView extends Sprite {
return position; 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; image = value;
redraw(); redraw();
refresh(); refresh();
@@ -44,14 +44,15 @@ class PartView extends Sprite {
private function refresh():Void { private function refresh():Void {
if (position != null && image != null) { if (position != null && image != null) {
x = position.x - (image.width - size.x) / 2; x = position.x - (image.borderedImage.width - size.x) / 2;
y = position.y - (image.height - size.y) / 2; y = position.y - (image.borderedImage.height - size.y) / 2;
} }
} }
public function complete():Void { public function complete():Void {
position = target; position = target;
completed = true; completed = true;
redraw();
} }
public static function factory(part:Part):PartView { public static function factory(part:Part):PartView {
@@ -62,6 +63,10 @@ class PartView extends Sprite {
class SpritePartView extends PartView { class SpritePartView extends PartView {
override private function redraw():Void { override private function redraw():Void {
if (image == null) {
return;
}
var image = completed ? image.borderedImage : image.shadedImage;
graphics.clear(); graphics.clear();
graphics.beginBitmapFill(image, null, false, true); graphics.beginBitmapFill(image, null, false, true);
graphics.drawRect(0, 0, image.width, image.height); graphics.drawRect(0, 0, image.width, image.height);
@@ -79,6 +84,6 @@ class BitmapPartView extends PartView {
} }
override private function redraw():Void { override private function redraw():Void {
bitmap.bitmapData = image; bitmap.bitmapData = completed ? image.borderedImage : image.shadedImage;
} }
} }

View File

@@ -1,6 +1,5 @@
package ru.m.puzzlez.render; package ru.m.puzzlez.render;
import haxework.geom.Point;
import flash.display.BitmapData; import flash.display.BitmapData;
import flash.display.PNGEncoderOptions; import flash.display.PNGEncoderOptions;
import flash.display.Sprite; import flash.display.Sprite;
@@ -9,12 +8,14 @@ import flash.geom.Point as FlashPoint;
import flash.geom.Rectangle; import flash.geom.Rectangle;
import flash.net.FileReference; import flash.net.FileReference;
import flash.utils.ByteArray; import flash.utils.ByteArray;
import haxework.geom.Point;
import haxework.signal.Signal; import haxework.signal.Signal;
import haxework.view.popup.AlertView; import haxework.view.popup.AlertView;
import haxework.view.SpriteView; import haxework.view.SpriteView;
import ru.m.puzzlez.core.GameEvent; import ru.m.puzzlez.core.GameEvent;
import ru.m.puzzlez.core.GameState; import ru.m.puzzlez.core.GameState;
import ru.m.puzzlez.core.PartLocation; import ru.m.puzzlez.core.PartLocation;
import ru.m.puzzlez.render.ImagePartBuilder;
import ru.m.puzzlez.storage.ImageStorage; import ru.m.puzzlez.storage.ImageStorage;
class Render extends SpriteView implements IRender { class Render extends SpriteView implements IRender {
@@ -110,7 +111,7 @@ class Render extends SpriteView implements IRender {
this.image = RenderUtil.cropImage(image, state.preset.imageRect); this.image = RenderUtil.cropImage(image, state.preset.imageRect);
var builder = new ImagePartBuilder(this.image); var builder = new ImagePartBuilder(this.image);
var i = 0; var i = 0;
builder.build(state.parts).then(function(result) { builder.build(state.parts).then((result:Result) -> {
parts[result.part.id].image = result.image; parts[result.part.id].image = result.image;
progress.setProgress(++i, state.parts.length); progress.setProgress(++i, state.parts.length);
if (i >= state.parts.length - 1) { if (i >= state.parts.length - 1) {
@@ -166,7 +167,7 @@ class Render extends SpriteView implements IRender {
private function save(part:PartView):Void { private function save(part:PartView):Void {
var file = new FileReference(); var file = new FileReference();
var image = part.image; var image = part.image.shadedImage;
var data = new ByteArray(); var data = new ByteArray();
image.encode(new Rectangle(0, 0, image.width, image.height), new PNGEncoderOptions(), data); image.encode(new Rectangle(0, 0, image.width, image.height), new PNGEncoderOptions(), data);
file.save(data, "icon.png"); file.save(data, "icon.png");

View File

@@ -1,17 +1,40 @@
package ru.m.puzzlez.render; package ru.m.puzzlez.render;
import ru.m.draw.DrawPath;
import flash.display.BitmapData; import flash.display.BitmapData;
import flash.display.Shape; import flash.display.Shape;
import flash.geom.Matrix; import flash.geom.Matrix;
import haxework.color.Color;
import haxework.geom.Point;
import haxework.geom.Rectangle; import haxework.geom.Rectangle;
import ru.m.draw.DrawPath;
import ru.m.puzzlez.core.Part; import ru.m.puzzlez.core.Part;
import ru.m.puzzlez.render.part.ClassicPartBuilder; import ru.m.puzzlez.render.part.ClassicPartBuilder;
import ru.m.puzzlez.render.part.IPartBuilder; import ru.m.puzzlez.render.part.IPartBuilder;
import ru.m.puzzlez.render.part.PartMask; 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 { class RenderUtil {
private static var builder:IPartBuilder = new ClassicPartBuilder(); public static var shadowSettings(default, null):Array<DrawSetting> = [
{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<DrawSetting> = [
{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 { public static function cropImage(source:BitmapData, rect:Rectangle):BitmapData {
var image = new BitmapData(Std.int(rect.width), Std.int(rect.height)); var image = new BitmapData(Std.int(rect.width), Std.int(rect.height));
@@ -37,17 +60,14 @@ class RenderUtil {
return {rect:rect, drawRect:drawRect}; return {rect:rect, drawRect:drawRect};
} }
private static function drawShadow(source:BitmapData, path:DrawPath):BitmapData { private static function drawShadow(source:BitmapData, path:DrawPath, values:Array<DrawSetting>):BitmapData {
var shadow = 2;
var opacity = 0.4;
var canvas = new Shape(); var canvas = new Shape();
canvas.cacheAsBitmap = true; canvas.cacheAsBitmap = true;
canvas.graphics.beginFill(0x000000, opacity); for (value in values) {
path.move(-shadow, -shadow).draw(canvas.graphics); canvas.graphics.beginFill(value.color, value.opacity);
canvas.graphics.endFill(); path.move(value.offset.x, value.offset.y).draw(canvas.graphics);
canvas.graphics.beginFill(0xffffff, opacity);
path.move(shadow, shadow).draw(canvas.graphics);
canvas.graphics.endFill(); canvas.graphics.endFill();
}
canvas.graphics.beginBitmapFill(source, null, false, true); canvas.graphics.beginBitmapFill(source, null, false, true);
canvas.graphics.drawRect(0, 0, source.width, source.height); canvas.graphics.drawRect(0, 0, source.width, source.height);
canvas.graphics.endFill(); canvas.graphics.endFill();
@@ -56,7 +76,23 @@ class RenderUtil {
return image; return image;
} }
public static function cropImagePart(source:BitmapData, part:Part):BitmapData { private static function drawBorder(source:BitmapData, path:DrawPath, values:Array<DrawSetting>):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 geometry = buildPartGeometry(part);
var path = builder.build(geometry.drawRect, part.bounds); var path = builder.build(geometry.drawRect, part.bounds);
var canvas:Shape = new Shape(); var canvas:Shape = new Shape();
@@ -69,7 +105,9 @@ class RenderUtil {
canvas.graphics.endFill(); canvas.graphics.endFill();
var image = new BitmapData(Std.int(geometry.rect.width), Std.int(geometry.rect.height), true, 0x00000000); var image = new BitmapData(Std.int(geometry.rect.width), Std.int(geometry.rect.height), true, 0x00000000);
image.draw(canvas, null, null, null, null, true); 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 { public static function containRectangle(source:Rectangle, target:Rectangle):Rectangle {

View File

@@ -10,8 +10,8 @@ class BasePartBuilder implements IPartBuilder {
public function new() { public function new() {
} }
private function createAngle(path:DrawPath, x:Float, y:Float):Void { private function createAngle(path:DrawPath, x:Float, y:Float, first: Bool = false):Void {
path.commands.push(LINE_TO(x, y)); 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 { 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 { public function build(rect:Rectangle, bounds:PartBounds):DrawPath {
var path = new 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); createBound(path, rect.left, rect.top, rect.right, rect.top, bounds.top);
createAngle(path, rect.right, rect.top); createAngle(path, rect.right, rect.top);
createBound(path, rect.right, rect.top, rect.right, rect.bottom, bounds.right); createBound(path, rect.right, rect.top, rect.right, rect.bottom, bounds.right);

View File

@@ -44,7 +44,10 @@ import ru.m.puzzlez.core.Id;
} }
public function delete(imageId:ImageId):Void { public function delete(imageId:ImageId):Void {
Reflect.deleteField(statusData.data, imageId); var gameData = SharedObject.getLocal('${path}/${imageId}');
gameData.clear();
var data:DynamicAccess<GameStatus> = statusData.data;
data.remove(imageId);
statusData.flush(); statusData.flush();
} }

View File

@@ -1,5 +1,6 @@
package ru.m.puzzlez.view; package ru.m.puzzlez.view;
import haxe.Timer;
import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameSwitcher;
import haxework.view.frame.FrameView; import haxework.view.frame.FrameView;
import ru.m.puzzlez.core.Game; import ru.m.puzzlez.core.Game;
@@ -17,7 +18,7 @@ import ru.m.puzzlez.storage.GameStorage;
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
@:provide var storage:GameStorage; @:provide var storage:GameStorage;
private var toSave:Bool; private var saveTimer:Timer;
public function new() { public function new() {
super(ID); super(ID);
@@ -33,6 +34,7 @@ import ru.m.puzzlez.storage.GameStorage;
} }
override public function onHide():Void { override public function onHide():Void {
save();
if (game != null) { if (game != null) {
render.signal.disconnect(game.signal.emit); render.signal.disconnect(game.signal.emit);
game.stop(); 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 { private function onGameEvent(event:GameEvent):Void {
switch event { switch event {
case START(state): case START(state):
storage.save(state); toSave();
case ACTION(PART_PUT(_, _)): case ACTION(PART_PUT(_, _)):
storage.save(game.state); toSave();
case _: case _:
} }
} }

View File

@@ -1,8 +1,10 @@
package ru.m.puzzlez.view; 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.FrameSwitcher;
import haxework.view.frame.FrameView; import haxework.view.frame.FrameView;
import haxework.view.list.ScrollBarView;
import ru.m.puzzlez.core.GameUtil; import ru.m.puzzlez.core.GameUtil;
import ru.m.puzzlez.core.Id; import ru.m.puzzlez.core.Id;
import ru.m.puzzlez.storage.ImageStorage; import ru.m.puzzlez.storage.ImageStorage;
@@ -11,9 +13,7 @@ import ru.m.puzzlez.storage.ImageStorage;
public static var ID = "preset"; public static var ID = "preset";
@:view("image") var imageView:PresetView; @:view("image") var imageView:PresetView;
@:view("scroll") var scrollView:ScrollBarView; @:view("sizes") var sizesView:DataView<IntPoint, ToggleButtonView>;
@:view("width") var widthView:InputView;
@:view("height") var heightView:InputView;
@:provide var imageStorage:ImageStorage; @:provide var imageStorage:ImageStorage;
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
@@ -22,32 +22,38 @@ import ru.m.puzzlez.storage.ImageStorage;
public function new() { public function new() {
super(ID); super(ID);
widthView.text = "8"; sizesView.data = [
heightView.text = "8"; new IntPoint(3, 2),
scrollView.position = 0.8; new IntPoint(4, 3),
widthView.update(); new IntPoint(5, 4),
heightView.update(); 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 { override public function onShow(data:ImageId):Void {
super.onShow(data); super.onShow(data);
imageId = data; imageId = data;
updatePreset(); selectSize(sizesView.data[sizesView.data.length - 1]);
}
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));
} }
private function start():Void { private function start():Void {
switcher.change(GameFrame.ID, GameUtil.buildState(imageView.preset)); switcher.change(GameFrame.ID, imageView.state);
} }
private function back():Void { private function back():Void {

View File

@@ -1,32 +1,25 @@
--- ---
style: frame style: frame
layout.margin: 10
views: views:
- $type: haxework.view.group.HGroupView - $type: haxework.view.group.HGroupView
geometry.width: 100% geometry.width: 100%
layout.hAlign: center layout.hAlign: center
layout.vAlign: middle layout.vAlign: middle
views: 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 - $type: haxework.view.form.ButtonView
geometry.margin.left: 15 geometry.margin.left: 15
text: Start text: Start
+onPress: ~start() +onPress: ~start()
- id: scroll - id: sizes
$type: haxework.view.list.HScrollBarView $type: haxework.view.data.DataView
geometry.width: 100% geometry.width: 100%
geometry.height: 20 layout:
ratio: 0.2 $type: haxework.view.layout.TailLayout
position: 0.8 hAlign: center
+onScroll: ~updateGrid margin: 5
factory: ~factory
+onDataSelect: ~selectSize
- id: image - id: image
$type: ru.m.puzzlez.view.PresetView $type: ru.m.puzzlez.view.PresetView
geometry.stretch: true geometry.stretch: true

View File

@@ -5,7 +5,7 @@ import flash.display.Graphics;
import flash.display.Shape; import flash.display.Shape;
import flash.geom.Matrix; import flash.geom.Matrix;
import haxework.view.group.GroupView; 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.render.RenderUtil;
import ru.m.puzzlez.storage.ImageStorage; import ru.m.puzzlez.storage.ImageStorage;
@@ -20,23 +20,23 @@ class PresetView extends GroupView {
private function set_scale(value:Float):Float { private function set_scale(value:Float):Float {
var result = table.scaleX = table.scaleY = value; var result = table.scaleX = table.scaleY = value;
table.x = (width - preset.tableRect.width * value) / 2; table.x = (width - state.preset.tableRect.width * value) / 2;
table.y = (height - preset.tableRect.height * value) / 2; table.y = (height - state.preset.tableRect.height * value) / 2;
return result; return result;
} }
public var preset(default, set):GamePreset; public var state(default, set):GameState;
private function set_preset(value:GamePreset) { private function set_state(value:GameState):GameState {
preset = value; state = value;
this.image = null; this.image = null;
table.graphics.clear(); table.graphics.clear();
loading.promise = imageStorage.resolve(preset.imageId).then(image -> { loading.promise = imageStorage.resolve(state.preset.imageId).then(image -> {
this.image = RenderUtil.cropImage(image, preset.imageRect); this.image = RenderUtil.cropImage(image, state.preset.imageRect);
toRedraw(); toRedraw();
toUpdate(); toUpdate();
}); });
return preset; return state;
} }
private var loading:LoadingWrapper; private var loading:LoadingWrapper;
@@ -52,9 +52,10 @@ class PresetView extends GroupView {
private var image:BitmapData; private var image:BitmapData;
override public function redraw():Void { override public function redraw():Void {
if (preset == null || image == null) { if (state == null || image == null) {
return; return;
} }
var preset = state.preset;
var partWidth = preset.imageRect.width / preset.grid.width; var partWidth = preset.imageRect.width / preset.grid.width;
var partHeight = preset.imageRect.height / preset.grid.height; var partHeight = preset.imageRect.height / preset.grid.height;
var graphics:Graphics = table.graphics; var graphics:Graphics = table.graphics;
@@ -64,32 +65,24 @@ class PresetView extends GroupView {
graphics.beginBitmapFill(image, matrix, false, true); graphics.beginBitmapFill(image, matrix, false, true);
graphics.drawRect(preset.imageRect.x, preset.imageRect.y, preset.imageRect.width, preset.imageRect.height); graphics.drawRect(preset.imageRect.x, preset.imageRect.y, preset.imageRect.width, preset.imageRect.height);
graphics.endFill(); graphics.endFill();
graphics.lineStyle(1, 0x555555);
for (x in 0...preset.grid.width + 1) { for (part in state.parts) {
graphics.moveTo(preset.imageRect.x + x * partWidth, preset.imageRect.y); var rect = part.rect.clone();
graphics.lineTo(preset.imageRect.x + x * partWidth, preset.imageRect.y + preset.imageRect.height); rect.x = preset.imageRect.x + part.gridX * part.rect.width;
} rect.y = preset.imageRect.y + part.gridY * part.rect.height;
for (y in 0...preset.grid.height + 1) { var path = RenderUtil.builder.build(rect, part.bounds);
graphics.moveTo(preset.imageRect.x, preset.imageRect.y + y * partHeight); for (value in RenderUtil.borderSettings) {
graphics.lineTo(preset.imageRect.x + preset.imageRect.width, preset.imageRect.y + y * partHeight); graphics.lineStyle(1, value.color, value.opacity);
} path.move(value.offset.x, value.offset.y).draw(graphics);
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(); graphics.lineStyle();
} }
}
}
override public function update():Void { override public function update():Void {
super.update(); super.update();
if (preset != null) { if (state != null) {
scale = Math.min(width / preset.tableRect.width, height / preset.tableRect.height); scale = Math.min(width / state.preset.tableRect.width, height / state.preset.tableRect.height);
} }
} }
} }

View File

@@ -1,5 +1,6 @@
package ru.m.puzzlez.view; package ru.m.puzzlez.view;
import ru.m.update.Updater;
import haxework.view.data.DataView; import haxework.view.data.DataView;
import haxework.view.form.ButtonView; import haxework.view.form.ButtonView;
import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameSwitcher;
@@ -18,10 +19,12 @@ import ru.m.puzzlez.storage.ImageStorage;
@:view var sources:DataView<ImageListSource<Dynamic>, ButtonView>; @:view var sources:DataView<ImageListSource<Dynamic>, ButtonView>;
@:view("load") var loadButton:ButtonView; @:view("load") var loadButton:ButtonView;
@:view("complete") var completeButton:ButtonView; @:view("complete") var completeButton:ButtonView;
@:view("update") var updateButton:ButtonView;
@:provide var storage:ImageStorage; @:provide var storage:ImageStorage;
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
@:provide var gameStorage:GameStorage; @:provide var gameStorage:GameStorage;
@:provide static var appUpdater:Updater;
public function new() { public function new() {
super(ID); super(ID);
@@ -45,6 +48,12 @@ import ru.m.puzzlez.storage.ImageStorage;
override public function onShow(data:Dynamic):Void { override public function onShow(data:Dynamic):Void {
refresh(); refresh();
appUpdater.check().then((info:Null<PackageInfo>) -> {
if (info != null) {
updateButton.visible = true;
updateButton.text = 'Update ${info.version}';
}
});
} }
private function sourceViewFactory(index:Int, source:ImageListSource<Dynamic>):ButtonView { private function sourceViewFactory(index:Int, source:ImageListSource<Dynamic>):ButtonView {

View File

@@ -38,6 +38,11 @@ views:
text: Clean text: Clean
+onPress: ~clean() +onPress: ~clean()
visible: false visible: false
- id: update
$type: haxework.view.form.ButtonView
style: button.active
+onPress: ~appUpdater.download()
visible: false
- $type: haxework.view.form.LabelView - $type: haxework.view.form.LabelView
text: $r:text:version text: $r:text:version
geometry.position: absolute geometry.position: absolute

View File

@@ -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<PackageInfo>;
}
class Updater {
private static inline var TAG = "Update";
public var type(get, null):PackageType;
public var bundle(get, null):Promise<PackagesBundle>;
private function get_bundle():Promise<PackagesBundle> {
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<Null<PackageInfo>> {
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<Bool> {
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;
});
}
}

View File

@@ -0,0 +1,52 @@
package ru.m.update;
abstract Version(Array<Int>) {
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" : ""}';
}
}

View File

@@ -1,6 +1,3 @@
* win event
* save game state
* load user images
* background settings * background settings
* grid settings * preset frame improve
* parts groups * parts groups