diff --git a/ansible/deploy.yml b/ansible/deploy.yml index ed386b8..a153d32 100644 --- a/ansible/deploy.yml +++ b/ansible/deploy.yml @@ -9,6 +9,3 @@ - include_role: name: ansible-deploy tasks_from: complete.yml - - include_role: - name: service - tasks_from: restart.yml diff --git a/config.example.json b/config.example.json index 1dd03de..8153e77 100644 --- a/config.example.json +++ b/config.example.json @@ -2,7 +2,7 @@ "SdkDir": "C:\\sdk", "BuildDir": null, "PublishDir": "", - "PublishUrl": "https://shmyga.ru/repo/tankz", + "PublishUrl": "https://shmyga.ru/repo/puzzlez", "Develop": false, "Key": { "store": "keystore.jks", diff --git a/gulpfile.js b/gulpfile.js index e23b1ba..fa1dbc4 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -5,6 +5,7 @@ const packageInfo = require('./package.json'); const {System, Sdk, Haxe, Project} = require('gulp-haxetool'); const dateformat = require('dateformat'); const argv = require('yargs').argv; +const publish = require('./tasks/gulp-publish'); if (Config.SdkDir) { Sdk.dir = Config.SdkDir; @@ -22,7 +23,7 @@ const config = new Project.Config({ meta: { title: 'Puzzle\'z', filename: 'puzzlez', - icon: 'src/icon.png', + icon: 'src/resources/icon.png', pack: 'ru.m.puzzlez', author: 'shmyga ', company: 'MegaLoMania', @@ -54,12 +55,14 @@ const app = new Project( ], main: 'ru.m.puzzlez.PuzzlezApp', meta: { - width: 1024, + width: 1280, height: 768, }, }), ).bind(module, gulp); +module.exports.publish = publish(packageInfo.name, packageInfo.version, Config.PublishDir, Config.PublishUrl); + const defaultSeries = [ exports.clean, module.exports['app:flash:build'], @@ -85,4 +88,8 @@ if (System.isWindows) { ); } +defaultSeries.push( + exports.publish, +); + module.exports.default = gulp.series(defaultSeries); diff --git a/package.json b/package.json index deef85a..4f699d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "puzzlez", - "version": "0.0.1", + "version": "0.0.2", "private": true, "devDependencies": { "dateformat": "^3.0.3", diff --git a/src/haxe/ru/m/puzzlez/LinuxIcon.hx b/src/haxe/ru/m/puzzlez/LinuxIcon.hx new file mode 100644 index 0000000..f73d94b --- /dev/null +++ b/src/haxe/ru/m/puzzlez/LinuxIcon.hx @@ -0,0 +1,27 @@ +package ru.m.puzzlez; + +import flash.display.BitmapData; +import flash.filters.ColorMatrixFilter; +import flash.geom.Point; +import flash.Lib; +import openfl.Assets; + +class LinuxIcon { + + private static function prepareIcon(bitmap:BitmapData):BitmapData { + var matrix:Array = []; + matrix = matrix.concat([0, 0, 1, 0, 0]); + matrix = matrix.concat([0, 1, 0, 0, 0]); + matrix = matrix.concat([1, 0, 0, 0, 0]); + matrix = matrix.concat([0, 0, 0, 1, 0]); + var cmf:ColorMatrixFilter = new ColorMatrixFilter(matrix); + var bitmap:BitmapData = bitmap.clone(); + bitmap.applyFilter(bitmap, bitmap.rect, new Point(0, 0), cmf); + return bitmap; + } + + public static function apply() { + var icon = Assets.getBitmapData("resources/icon.png"); + Lib.current.stage.window.setIcon(prepareIcon(icon).image); + } +} diff --git a/src/haxe/ru/m/puzzlez/PuzzlezApp.hx b/src/haxe/ru/m/puzzlez/PuzzlezApp.hx index 8fbbb4f..ed754ba 100644 --- a/src/haxe/ru/m/puzzlez/PuzzlezApp.hx +++ b/src/haxe/ru/m/puzzlez/PuzzlezApp.hx @@ -8,6 +8,9 @@ class PuzzlezApp extends App { public static function main() { L.push(new TraceLogger()); + #if linux + LinuxIcon.apply(); + #end var app = new PuzzlezApp(new PuzzlezTheme()); app.start(new PuzzlezAppView()); L.d("Puzzlez", "started"); diff --git a/src/haxe/ru/m/puzzlez/core/Game.hx b/src/haxe/ru/m/puzzlez/core/Game.hx index 0b93404..32b22f2 100644 --- a/src/haxe/ru/m/puzzlez/core/Game.hx +++ b/src/haxe/ru/m/puzzlez/core/Game.hx @@ -20,14 +20,24 @@ class Game implements IGame { } public function start():Void { - shuffle(); signal.emit(GameEvent.START(state)); + shuffle(); } public function shuffle():Void { for (part in state.parts) { - part.rect.x = Math.random() * (state.preset.tableRect.width - part.rect.width); - part.rect.y = Math.random() * (state.preset.tableRect.height - part.rect.height); + var bound = part.rect.width * 0.25; + part.rect.x = bound + Math.random() * (state.preset.tableRect.width - part.rect.width - bound * 2); + part.rect.y = bound + Math.random() * (state.preset.tableRect.height - part.rect.height - bound * 2); + signal.emit(GameEvent.PART_MOVED(part.id, part.rect.topLeft)); + } + } + + public function collect():Void { + for (part in state.parts) { + part.rect.x = part.position.x; + part.rect.y = part.position.y; + signal.emit(GameEvent.PART_MOVED(part.id, part.rect.topLeft)); } } diff --git a/src/haxe/ru/m/puzzlez/core/GameUtil.hx b/src/haxe/ru/m/puzzlez/core/GameUtil.hx index 910f1ef..20b030d 100644 --- a/src/haxe/ru/m/puzzlez/core/GameUtil.hx +++ b/src/haxe/ru/m/puzzlez/core/GameUtil.hx @@ -64,9 +64,10 @@ class GameUtil { public static function buildPreset(image:ImageSource):GamePreset { var size = 8; - var offset = 100; - var imageRect = new Rectangle(offset, offset, 1024, 1024); - var tableRect = new Rectangle(0, 0, imageRect.width + offset * 2, imageRect.height + offset * 2); + var offsetX = 500; + var offsetY = 200; + var imageRect = new Rectangle(offsetX, offsetY, 1024, 1024); + var tableRect = new Rectangle(0, 0, imageRect.width + offsetX * 2, imageRect.height + offsetY * 2); return { image:image, grid: {width: size, height: size}, diff --git a/src/haxe/ru/m/puzzlez/core/IGame.hx b/src/haxe/ru/m/puzzlez/core/IGame.hx index 79bba10..6815d44 100644 --- a/src/haxe/ru/m/puzzlez/core/IGame.hx +++ b/src/haxe/ru/m/puzzlez/core/IGame.hx @@ -8,5 +8,7 @@ interface IGame { public function start():Void; public function stop():Void; + public function shuffle():Void; + public function collect():Void; public function dispose():Void; } diff --git a/src/haxe/ru/m/puzzlez/render/PartView.hx b/src/haxe/ru/m/puzzlez/render/PartView.hx index c1f2424..7f6ba66 100644 --- a/src/haxe/ru/m/puzzlez/render/PartView.hx +++ b/src/haxe/ru/m/puzzlez/render/PartView.hx @@ -13,13 +13,21 @@ class PartView extends Sprite { private function set_position(value:Point):Point { position = value.clone(); - x = position.x - (bitmap.bitmapData.width - size.x) / 2; - y = position.y - (bitmap.bitmapData.height - size.y) / 2; + refresh(); return position; } + public var image(default, set):BitmapData; + + private function set_image(value:BitmapData):BitmapData { + image = value; + bitmap.bitmapData = image; + refresh(); + return image; + } + private var size:Point; - public var bitmap:Bitmap; + private var bitmap:Bitmap; public function new(id:Int, image:BitmapData, size:Point) { super(); @@ -29,4 +37,10 @@ class PartView extends Sprite { addChild(bitmap); } + private function refresh():Void { + if (position != null && image != null) { + x = position.x - (bitmap.bitmapData.width - size.x) / 2; + y = position.y - (bitmap.bitmapData.height - size.y) / 2; + } + } } diff --git a/src/haxe/ru/m/puzzlez/render/Render.hx b/src/haxe/ru/m/puzzlez/render/Render.hx index 9b2f190..a23fd2b 100644 --- a/src/haxe/ru/m/puzzlez/render/Render.hx +++ b/src/haxe/ru/m/puzzlez/render/Render.hx @@ -24,7 +24,7 @@ class Render extends SpriteView implements IRender { private function set_scale(value:Float):Float { var result = table.scaleX = table.scaleY = value; - setSize(table.width, table.height, 'table'); + //setSize(table.width, table.height, 'table'); return result; } @@ -59,19 +59,13 @@ class Render extends SpriteView implements IRender { private function onStart(state:GameState):Void { clean(); this.state = state; - RenderUtil.resolveImage(state.preset.image).then(start); - } - - private function start(image:BitmapData):Void { - this.image = RenderUtil.cropImage(image, state.preset.imageRect); for (part in state.parts) { - var partImage = RenderUtil.cropImagePart(this.image, part); - var partView = new PartView(part.id, partImage, part.rect.size); + var partView = new PartView(part.id, null, part.rect.size); partView.position = part.rect.topLeft; parts.set(part.id, partView); table.addChild(partView); } - scale = scale; + table.graphics.lineStyle(2, 0xCCCCCC); table.graphics.beginFill(0x555555); table.graphics.drawRect(state.preset.tableRect.x, state.preset.tableRect.y, state.preset.tableRect.width, state.preset.tableRect.height); @@ -83,10 +77,26 @@ class Render extends SpriteView implements IRender { table.graphics.drawRect(state.preset.imageRect.x, state.preset.imageRect.y, state.preset.imageRect.width, state.preset.imageRect.height); table.graphics.endFill(); table.graphics.lineStyle(); - + RenderUtil.resolveImage(state.preset.image).then(onImageResolved); toUpdate(); } + private function onImageResolved(image:BitmapData):Void { + this.image = RenderUtil.cropImage(image, state.preset.imageRect); + for (part in state.parts) { + var partImage = RenderUtil.cropImagePart(this.image, part); + var partView = parts[part.id]; + partView.image = partImage; + } + } + + override public function update():Void { + super.update(); + if (state != null) { + scale = Math.min(width / state.preset.tableRect.width, height / state.preset.tableRect.height); + } + } + private function onMouseDown(event:MouseEvent):Void { if (Std.is(event.target, PartView)) { activePart = event.target; @@ -117,9 +127,9 @@ class Render extends SpriteView implements IRender { private function save(part:PartView):Void { var file = new FileReference(); - var bitmapData = part.bitmap.bitmapData; + var image = part.image; var data = new ByteArray(); - bitmapData.encode(new Rectangle(0, 0, bitmapData.width, bitmapData.height), new PNGEncoderOptions(), data); + 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/view/PuzzlezAppView.hx b/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.hx index bd60de2..5e42575 100644 --- a/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.hx +++ b/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.hx @@ -1,11 +1,10 @@ package ru.m.puzzlez.view; -import haxework.view.form.ButtonView; import haxework.net.JsonLoader; import haxework.view.data.DataView; +import haxework.view.form.ButtonView; import haxework.view.group.VGroupView; import haxework.view.ImageView; -import haxework.view.list.ScrollBarView; import haxework.view.utils.DrawUtil; import openfl.utils.Assets; import openfl.utils.AssetType; @@ -24,16 +23,13 @@ typedef PixabayResponse = { @:template class PuzzlezAppView extends VGroupView { - @:view private var scale:ScrollBarView; @:view private var images:DataView; @:view private var render:IRender; private var game:IGame; public function new() { super(); - images.data = [for (name in Assets.list(AssetType.IMAGE)) ASSET(name)]; - render.scale = 0.75; - scale.position = render.scale - scale.ratio; + images.data = [for (name in Assets.list(AssetType.IMAGE).filter(function(name:String) return name.substr(0, 15) == "resources/image")) ASSET(name)]; } private function imageViewFactory(index:Int, image:ImageSource):ImageView { @@ -47,6 +43,7 @@ typedef PixabayResponse = { private function moreImages(view:ButtonView):Void { view.visible = false; + view.toUpdateParent(); new JsonLoader() .GET('https://pixabay.com/api/?key=14915210-5eae157281211e0ad28bc8def&category=nature') .then(function(result:PixabayResponse) { @@ -54,10 +51,6 @@ typedef PixabayResponse = { }); } - public function setScale(value:Float):Void { - render.scale = value + scale.ratio; - } - public function start(image:ImageSource):Void { stop(); game = new Game(GameUtil.buildPreset(image)); diff --git a/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.yaml b/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.yaml index 32592cf..bd641cf 100644 --- a/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.yaml +++ b/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.yaml @@ -12,6 +12,12 @@ views: - $type: haxework.view.form.LabelView text: Puzzle'z font.size: 42 + - $type: haxework.view.form.ButtonView + text: Shuffle + +onPress: ~game.shuffle() + - $type: haxework.view.form.ButtonView + text: Collect + +onPress: ~game.collect() - id: images $type: haxework.view.data.DataView layout: @@ -22,20 +28,17 @@ views: +onDataSelect: ~start geometry.margin: 5 overflow.y: scroll - - id: more - $type: haxework.view.form.ButtonView + - $type: haxework.view.form.ButtonView text: More +onPress: ~moreImages - - id: scale - $type: haxework.view.list.VScrollBarView - ratio: 0.5 - +onScroll: ~setScale - $type: haxework.view.group.GroupView style: frame geometry.width: 100% geometry.height: 100% - overflow.x: scroll - overflow.y: scroll + overflow.x: crop + overflow.y: crop views: - id: render $type: ru.m.puzzlez.render.Render + geometry.width: 100% + geometry.height: 100% diff --git a/src/icon.png b/src/resources/icon.png similarity index 100% rename from src/icon.png rename to src/resources/icon.png diff --git a/src/resources/avatar_2019.jpg b/src/resources/image/raccoon.jpg similarity index 100% rename from src/resources/avatar_2019.jpg rename to src/resources/image/raccoon.jpg diff --git a/src/resources/warty-final-ubuntu-blue.png b/src/resources/warty-final-ubuntu-blue.png deleted file mode 100644 index 1598959..0000000 Binary files a/src/resources/warty-final-ubuntu-blue.png and /dev/null differ diff --git a/src/resources/warty-final-ubuntu.png b/src/resources/warty-final-ubuntu.png deleted file mode 100644 index 9a72aa0..0000000 Binary files a/src/resources/warty-final-ubuntu.png and /dev/null differ diff --git a/tasks/gulp-publish.js b/tasks/gulp-publish.js new file mode 100755 index 0000000..2649207 --- /dev/null +++ b/tasks/gulp-publish.js @@ -0,0 +1,114 @@ +const gulp = require('gulp'); +const fs = require('fs'); +const fse = require('fs-extra'); +const path = require('path'); +const through = require('through2'); + +class Package { + + constructor(platform, type, version, versionInt) { + this.platform = platform; + this.type = type; + this.version = version; + this.versionInt = versionInt; + } + + get key() { + return [this.platform, this.type].join(':'); + } + + static getPlatform(filename) { + for (const [platform, r] of [ + ['android', /^.*?\.apk$/], + ['linux', /^.*?\.deb$/], + ['linux', /^.*?linux\.tar\.gz$/], + ['windows', /^.*?win\.zip$/], + ['windows', /^.*?\.exe$/], + ]) { + if (r.test(filename)) { + return platform; + } + } + return null; + } + + static getType(filename) { + for (const [type, r] of [ + ['apk', /^.*?\.apk$/], + ['deb', /^.*?\.deb$/], + ['archive', /^.*?\.tar\.gz$/], + ['archive', /^.*?\.zip$/], + ['exe', /^.*?\.exe$/], + ]) { + if (r.test(filename)) { + return type; + } + } + return null; + } + + static getVersion(filename) { + const m = /(\d+)\.(\d+)\.(\d+)/.exec(filename); + if (m) { + return [m[0], parseInt(m[1]) * 10000 + parseInt(m[2]) * 100 + parseInt(m[3])]; + } + return null; + } + + static parseFilename(filename) { + const platform = this.getPlatform(filename); + const type = this.getType(filename); + const version = this.getVersion(filename); + if (platform && type && version) { + return new Package(platform, type, version[0], version[1]); + } + return null; + } +} + +module.exports = (name, version, publishDir, publishUrl) => function publish(cb) { + const packages = {}; + fse.ensureDirSync(publishDir); + return gulp.series([ + function copy() { + return gulp.src('target/client/@(android|archive|debian|installer)/*') + .pipe(through.obj(function (file, enc, cb) { + const pack = Package.parseFilename(file.path); + if (pack) { + this.push(file); + } + cb(null); + })) + .pipe(gulp.dest(publishDir)) + }, + function generate() { + return gulp.src(`${publishDir}/*/*`) + .pipe(through.obj(function (file, enc, cb) { + const basepath = file.path.replace(file.base + path.sep, ''); + const pack = Package.parseFilename(file.path); + if (pack) { + if (!packages[pack.key] || packages[pack.key].versionInt < pack.versionInt) { + packages[pack.key] = { + platform: pack.platform, + type: pack.type, + version: pack.version, + versionInt: pack.versionInt, + path: basepath, + filename: basepath.split(path.sep).pop(), + url: `${publishUrl}/${basepath.replace(path.sep, "/")}`, + } + } + } + cb(null); + })).on('end', function () { + fs.writeFileSync(path.join(publishDir, 'packages.json'), JSON.stringify({ + name: name, + version: version, + packages: Object.values(packages), + }, null, 4)); + }) + } + ])(cb); +}; + +module.exports.Package = Package;