style: format code
This commit is contained in:
@@ -4,13 +4,10 @@ root = true
|
|||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 2
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
[*.yaml]
|
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
max_line_length = off
|
max_line_length = off
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|||||||
32
.vscode/launch.json
vendored
32
.vscode/launch.json
vendored
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"args": [
|
|
||||||
"app:flash:test"
|
|
||||||
],
|
|
||||||
"name": "app:flash:test",
|
|
||||||
"program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js",
|
|
||||||
"request": "launch",
|
|
||||||
"skipFiles": [
|
|
||||||
"<node_internals>/**"
|
|
||||||
],
|
|
||||||
"type": "node"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"args": [
|
|
||||||
"server:cpp:test"
|
|
||||||
],
|
|
||||||
"name": "server:cpp:test",
|
|
||||||
"program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js",
|
|
||||||
"request": "launch",
|
|
||||||
"skipFiles": [
|
|
||||||
"<node_internals>/**"
|
|
||||||
],
|
|
||||||
"type": "node"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
16
.vscode/settings.json
vendored
16
.vscode/settings.json
vendored
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"haxe.executable": {
|
|
||||||
"path": "/home/shmyga/sdk/haxe/4.2.5/haxe",
|
|
||||||
"env": {
|
|
||||||
"HAXE_STD_PATH": "/home/shmyga/sdk/haxe/4.2.5/std",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"haxe.configurations": [
|
|
||||||
["build/app/flash/haxe/debug.hxml"],
|
|
||||||
],
|
|
||||||
"haxe.displayServer": {
|
|
||||||
"arguments": [
|
|
||||||
//"-v"
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
10
README.md
Normal file
10
README.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
`.bashrc`
|
||||||
|
```bash
|
||||||
|
export NEKO_VERSION="2.2.0"
|
||||||
|
export HAXE_VERSION="4.2.5"
|
||||||
|
|
||||||
|
export NEKO_SDK="$HOME/sdk/neko/$NEKO_VERSION"
|
||||||
|
export HAXE_SDK="$HOME/sdk/haxe/$HAXE_VERSION"
|
||||||
|
|
||||||
|
alias haxe-activate=". $NEKO_SDK/activate && . $HAXE_SDK/activate"
|
||||||
|
```
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"SdkDir": "C:\\sdk",
|
"SdkDir": "C:\\sdk",
|
||||||
"BuildDir": null,
|
"BuildDir": null,
|
||||||
"PublishDir": "",
|
"PublishDir": "",
|
||||||
"PublishUrl": "https://shmyga.ru/repo/puzzlez",
|
"PublishUrl": "https://shmyga.ru/repo/puzzlez",
|
||||||
"Develop": false,
|
"Develop": false,
|
||||||
"Key": {
|
"Key": {
|
||||||
"store": "keystore.jks",
|
"store": "keystore.jks",
|
||||||
"pass": null
|
"pass": null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
hxformat.json
Normal file
5
hxformat.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"indentation": {
|
||||||
|
"character": " "
|
||||||
|
}
|
||||||
|
}
|
||||||
45
package.json
45
package.json
@@ -1,24 +1,25 @@
|
|||||||
{
|
{
|
||||||
"name": "puzzlez",
|
"name": "puzzlez",
|
||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"dateformat": "^3.0.3",
|
"dateformat": "^3.0.3",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"gulp": "^4.0.0",
|
"gulp": "^4.0.0",
|
||||||
"gulp-add": "0.0.2",
|
"gulp-add": "0.0.2",
|
||||||
"gulp-clean": "^0.4.0",
|
"gulp-clean": "^0.4.0",
|
||||||
"gulp-cli": "^2.2.0",
|
"gulp-cli": "^2.2.0",
|
||||||
"gulp-haxetool": "^0.1.9",
|
"gulp-haxetool": "^0.1.9",
|
||||||
"yargs": "^13.2.4"
|
"yargs": "^13.2.4"
|
||||||
},
|
},
|
||||||
"haxeDependencies": {
|
"haxeDependencies": {
|
||||||
"haxework": "2.1.0",
|
"haxework": "2.1.0",
|
||||||
"lime": "8.0.0",
|
"lime": "8.0.0",
|
||||||
"openfl": "9.2.0",
|
"openfl": "9.2.0",
|
||||||
"hxcpp": "4.2.1",
|
"hxcpp": "4.2.1",
|
||||||
"svg": "1.1.3",
|
"svg": "1.1.3",
|
||||||
"protohx": "0.4.6"
|
"protohx": "0.4.6",
|
||||||
},
|
"formatter": "1.16.0"
|
||||||
"haxe": "4.2.5"
|
},
|
||||||
|
"haxe": "4.2.5"
|
||||||
}
|
}
|
||||||
|
|||||||
46
puzzlez.code-workspace
Normal file
46
puzzlez.code-workspace
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"haxe.executable": {
|
||||||
|
"path": "/home/shmyga/sdk/haxe/4.2.5/haxe",
|
||||||
|
"env": {
|
||||||
|
"HAXE_STD_PATH": "/home/shmyga/sdk/haxe/4.2.5/std"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"haxe.configurations": [["build/app/flash/haxe/debug.hxml"]],
|
||||||
|
"haxe.displayServer": {
|
||||||
|
"arguments": [
|
||||||
|
//"-v"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extensions": {
|
||||||
|
"recommendations": ["nadako.vshaxe"]
|
||||||
|
},
|
||||||
|
"launch": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"args": ["app:flash:test"],
|
||||||
|
"name": "app:flash:test",
|
||||||
|
"program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js",
|
||||||
|
"request": "launch",
|
||||||
|
"skipFiles": ["<node_internals>/**"],
|
||||||
|
"type": "node"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": ["server:cpp:test"],
|
||||||
|
"name": "server:cpp:test",
|
||||||
|
"program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js",
|
||||||
|
"request": "launch",
|
||||||
|
"skipFiles": ["<node_internals>/**"],
|
||||||
|
"type": "node"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"compounds": []
|
||||||
|
}
|
||||||
|
}
|
||||||
8
scripts/format
Executable file
8
scripts/format
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
cd "$(dirname $(dirname "$0"))" || exit
|
||||||
|
|
||||||
|
source $NEKO_SDK/activate
|
||||||
|
source $HAXE_SDK/activate
|
||||||
|
haxelib install formatter
|
||||||
|
haxelib run formatter -s ./src/common/haxe -s ./src/app/haxe -s ./src/server/haxe
|
||||||
8
scripts/lint
Executable file
8
scripts/lint
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
cd "$(dirname $(dirname "$0"))" || exit
|
||||||
|
|
||||||
|
source $NEKO_SDK/activate
|
||||||
|
source $HAXE_SDK/activate
|
||||||
|
haxelib install formatter
|
||||||
|
haxelib run formatter --check -s ./src/common/haxe -s ./src/app/haxe -s ./src/server/haxe
|
||||||
5
scripts/setup
Executable file
5
scripts/setup
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
cd "$(dirname $(dirname "$0"))" || exit
|
||||||
|
|
||||||
|
npm ci
|
||||||
@@ -3,36 +3,37 @@ package ru.m;
|
|||||||
#if macro
|
#if macro
|
||||||
import haxe.macro.Context;
|
import haxe.macro.Context;
|
||||||
import haxe.macro.Expr;
|
import haxe.macro.Expr;
|
||||||
|
|
||||||
using haxe.macro.Tools;
|
using haxe.macro.Tools;
|
||||||
#end
|
#end
|
||||||
|
|
||||||
class AbstractEnumTools {
|
class AbstractEnumTools {
|
||||||
public static macro function getValues(typePath:Expr):Expr {
|
public static macro function getValues(typePath:Expr):Expr {
|
||||||
// Get the type from a given expression converted to string.
|
// Get the type from a given expression converted to string.
|
||||||
// This will work for identifiers and field access which is what we need,
|
// This will work for identifiers and field access which is what we need,
|
||||||
// it will also consider local imports. If expression is not a valid type path or type is not found,
|
// it will also consider local imports. If expression is not a valid type path or type is not found,
|
||||||
// compiler will give a error here.
|
// compiler will give a error here.
|
||||||
var type = Context.getType(typePath.toString());
|
var type = Context.getType(typePath.toString());
|
||||||
|
|
||||||
// Switch on the type and check if it's an abstract with @:enum metadata
|
// Switch on the type and check if it's an abstract with @:enum metadata
|
||||||
switch (type.follow()) {
|
switch (type.follow()) {
|
||||||
case TAbstract(_.get() => ab, _) if (ab.meta.has(":enum")):
|
case TAbstract(_.get() => ab, _) if (ab.meta.has(":enum")):
|
||||||
// @:enum abstract values are actually static fields of the abstract implementation class,
|
// @:enum abstract values are actually static fields of the abstract implementation class,
|
||||||
// marked with @:enum and @:impl metadata. We generate an array of expressions that access those fields.
|
// marked with @:enum and @:impl metadata. We generate an array of expressions that access those fields.
|
||||||
// Note that this is a bit of implementation detail, so it can change in future Haxe versions, but it's been
|
// Note that this is a bit of implementation detail, so it can change in future Haxe versions, but it's been
|
||||||
// stable so far.
|
// stable so far.
|
||||||
var valueExprs = [];
|
var valueExprs = [];
|
||||||
for (field in ab.impl.get().statics.get()) {
|
for (field in ab.impl.get().statics.get()) {
|
||||||
if (field.meta.has(":enum") && field.meta.has(":impl")) {
|
if (field.meta.has(":enum") && field.meta.has(":impl")) {
|
||||||
var fieldName = field.name;
|
var fieldName = field.name;
|
||||||
valueExprs.push(macro $typePath.$fieldName);
|
valueExprs.push(macro $typePath.$fieldName);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Return collected expressions as an array declaration.
|
|
||||||
return macro $a{valueExprs};
|
|
||||||
default:
|
|
||||||
// The given type is not an abstract, or doesn't have @:enum metadata, show a nice error message.
|
|
||||||
throw new Error(type.toString() + " should be @:enum abstract", typePath.pos);
|
|
||||||
}
|
}
|
||||||
|
// Return collected expressions as an array declaration.
|
||||||
|
return macro $a{valueExprs};
|
||||||
|
default:
|
||||||
|
// The given type is not an abstract, or doesn't have @:enum metadata, show a nice error message.
|
||||||
|
throw new Error(type.toString() + " should be @:enum abstract", typePath.pos);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,72 +6,68 @@ import flash.Lib;
|
|||||||
import hw.signal.Signal;
|
import hw.signal.Signal;
|
||||||
|
|
||||||
enum abstract Platform(String) from String to String {
|
enum abstract Platform(String) from String to String {
|
||||||
var ANDROID = "android";
|
var ANDROID = "android";
|
||||||
var LINUX = "linux";
|
var LINUX = "linux";
|
||||||
var WINDOWS = "windows";
|
var WINDOWS = "windows";
|
||||||
var FLASH = "flash";
|
var FLASH = "flash";
|
||||||
var HTML5 = "html5";
|
var HTML5 = "html5";
|
||||||
var UNKNOWN = "unknown";
|
var UNKNOWN = "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
class Device {
|
class Device {
|
||||||
|
public static var platform(get, null):Platform;
|
||||||
|
|
||||||
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;
|
||||||
|
#elseif flash
|
||||||
|
return FLASH;
|
||||||
|
#elseif html5
|
||||||
|
return HTML5;
|
||||||
|
#else
|
||||||
|
return UNKNOWN;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
private static function get_platform():Platform {
|
private static var MOBILES(default, never):Array<String> = ["Android", "webOS", "iPhone", "iPad", "iPod", "BlackBerry", "Windows Phone",];
|
||||||
#if android
|
|
||||||
return ANDROID;
|
|
||||||
#elseif linux
|
|
||||||
return LINUX;
|
|
||||||
#elseif windows
|
|
||||||
return WINDOWS;
|
|
||||||
#elseif flash
|
|
||||||
return FLASH;
|
|
||||||
#elseif html5
|
|
||||||
return HTML5;
|
|
||||||
#else
|
|
||||||
return UNKNOWN;
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
|
|
||||||
private static var MOBILES(default, never):Array<String> = [
|
public static function isMobile():Bool {
|
||||||
"Android", "webOS", "iPhone", "iPad", "iPod", "BlackBerry", "Windows Phone",
|
#if android
|
||||||
];
|
return true;
|
||||||
|
#elseif js
|
||||||
public static function isMobile():Bool {
|
var userAgent = js.Browser.navigator.userAgent;
|
||||||
#if android
|
for (mobile in MOBILES) {
|
||||||
|
if (userAgent.indexOf(mobile) > -1) {
|
||||||
return true;
|
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
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
public static var fullScreenSignal(get, null):Signal<Bool>;
|
public static var fullScreenSignal(get, null):Signal<Bool>;
|
||||||
|
|
||||||
private static function get_fullScreenSignal():Signal<Bool> {
|
private static function get_fullScreenSignal():Signal<Bool> {
|
||||||
if (fullScreenSignal == null) {
|
if (fullScreenSignal == null) {
|
||||||
fullScreenSignal = new Signal();
|
fullScreenSignal = new Signal();
|
||||||
Lib.current.stage.addEventListener(FullScreenEvent.FULL_SCREEN, (event:FullScreenEvent) -> fullScreenSignal.emit(event.fullScreen));
|
Lib.current.stage.addEventListener(FullScreenEvent.FULL_SCREEN, (event:FullScreenEvent) -> fullScreenSignal.emit(event.fullScreen));
|
||||||
}
|
|
||||||
return fullScreenSignal;
|
|
||||||
}
|
}
|
||||||
|
return fullScreenSignal;
|
||||||
|
}
|
||||||
|
|
||||||
public static var fullScreenSupport(get, never):Bool;
|
public static var fullScreenSupport(get, never):Bool;
|
||||||
|
|
||||||
public static function get_fullScreenSupport():Bool {
|
public static function get_fullScreenSupport():Bool {
|
||||||
return Lib.current.stage.allowsFullScreen;
|
return Lib.current.stage.allowsFullScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function toggleFullScreen():Void {
|
public static function toggleFullScreen():Void {
|
||||||
Lib.current.stage.displayState = Lib.current.stage.displayState == StageDisplayState.NORMAL ?
|
Lib.current.stage.displayState = Lib.current.stage.displayState == StageDisplayState.NORMAL ? StageDisplayState.FULL_SCREEN : StageDisplayState.NORMAL;
|
||||||
StageDisplayState.FULL_SCREEN : StageDisplayState.NORMAL;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,48 +5,39 @@ import openfl.net.FileReference;
|
|||||||
import openfl.utils.ByteArray;
|
import openfl.utils.ByteArray;
|
||||||
|
|
||||||
class Callback<T> {
|
class Callback<T> {
|
||||||
private var success:T -> Void;
|
private var success:T->Void;
|
||||||
private var fail:Dynamic -> Void;
|
private var fail:Dynamic->Void;
|
||||||
|
|
||||||
public function new(success: T -> Void, fail: Dynamic -> Void) {
|
public function new(success:T->Void, fail:Dynamic->Void) {
|
||||||
this.success = success;
|
this.success = success;
|
||||||
this.fail = fail;
|
this.fail = fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onSuccess(result:T):Void {
|
public function onSuccess(result:T):Void {
|
||||||
this.success(result);
|
this.success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onFail(error:Dynamic):Void {
|
public function onFail(error:Dynamic):Void {
|
||||||
this.fail(error);
|
this.fail(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ModernFileReference extends FileReference {
|
class ModernFileReference extends FileReference {
|
||||||
#if android
|
#if android
|
||||||
private static var fileUtilBrowse = lime.system.JNI.createStaticMethod(
|
private static var fileUtilBrowse = lime.system.JNI.createStaticMethod("ru.m.android.FileUtil", "browse", "(Lorg/haxe/lime/HaxeObject;)V");
|
||||||
"ru.m.android.FileUtil",
|
#end
|
||||||
"browse",
|
|
||||||
"(Lorg/haxe/lime/HaxeObject;)V"
|
|
||||||
);
|
|
||||||
#end
|
|
||||||
|
|
||||||
override public function browse(?typeFilter:Array<FileFilter>):Bool {
|
override public function browse(?typeFilter:Array<FileFilter>):Bool {
|
||||||
#if android
|
#if android
|
||||||
fileUtilBrowse(new Callback<haxe.io.BytesData>(
|
fileUtilBrowse(new Callback<haxe.io.BytesData>(function(result:haxe.io.BytesData):Void {
|
||||||
function(result:haxe.io.BytesData):Void {
|
data = ByteArray.fromBytesData(result);
|
||||||
data = ByteArray.fromBytesData(result);
|
dispatchEvent(new flash.events.Event(flash.events.Event.COMPLETE));
|
||||||
dispatchEvent(new flash.events.Event(flash.events.Event.COMPLETE));
|
}, function(error:Dynamic):Void {
|
||||||
},
|
dispatchEvent(new flash.events.IOErrorEvent(flash.events.IOErrorEvent.IO_ERROR, false, false, Std.string(error)));
|
||||||
function(error:Dynamic):Void {
|
}));
|
||||||
dispatchEvent(new flash.events.IOErrorEvent(
|
return true;
|
||||||
flash.events.IOErrorEvent.IO_ERROR, false, false, Std.string(error)
|
#else
|
||||||
));
|
return super.browse(typeFilter);
|
||||||
}
|
#end
|
||||||
));
|
}
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
return super.browse(typeFilter);
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
76
src/app/haxe/ru/m/api/PixabayApi.hx
Normal file
76
src/app/haxe/ru/m/api/PixabayApi.hx
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package ru.m.api;
|
||||||
|
|
||||||
|
import hw.net.JsonLoader;
|
||||||
|
import promhx.Promise;
|
||||||
|
|
||||||
|
typedef PixabayImage = {
|
||||||
|
var id:Int;
|
||||||
|
var largeImageURL:String;
|
||||||
|
var webformatURL:String;
|
||||||
|
var previewURL:String;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef PixabayResponse = {
|
||||||
|
var total:Int;
|
||||||
|
var totalHits:Int;
|
||||||
|
var hits:Array<PixabayImage>;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum abstract PixabayCategory(String) from String to String {
|
||||||
|
var FASHION = "fashion";
|
||||||
|
var NATURE = "nature";
|
||||||
|
var BACKGROUNDS = "backgrounds";
|
||||||
|
var SCIENCE = "science";
|
||||||
|
var EDUCATION = "education";
|
||||||
|
var PEOPLE = "people";
|
||||||
|
var FEELINGS = "feelings";
|
||||||
|
var RELIGION = "religion";
|
||||||
|
var HEALTH = "health";
|
||||||
|
var PLACES = "places";
|
||||||
|
var ANIMALS = "animals";
|
||||||
|
var INDUSTRY = "industry";
|
||||||
|
var FOOD = "food";
|
||||||
|
var COMPUTER = "computer";
|
||||||
|
var SPORTS = "sports";
|
||||||
|
var TRANSPORTATION = "transportation";
|
||||||
|
var TRAVEL = "travel";
|
||||||
|
var BUILDINGS = "buildings";
|
||||||
|
var BUSINESS = "business";
|
||||||
|
var MUSIC = "music";
|
||||||
|
}
|
||||||
|
|
||||||
|
enum abstract PixabayImageType(String) from String to String {
|
||||||
|
var ALL = "all";
|
||||||
|
var PHOTO = "photo";
|
||||||
|
var ILLUSTRATION = "illustration";
|
||||||
|
var VECTOR = "vector";
|
||||||
|
}
|
||||||
|
|
||||||
|
class PixabayApi {
|
||||||
|
private var baseUrl:String = "https://pixabay.com/api/";
|
||||||
|
private var key:String;
|
||||||
|
|
||||||
|
public function new(key:String) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildRequest(queryMap:Map<String, Dynamic>):String {
|
||||||
|
queryMap.set("key", key);
|
||||||
|
var query = [for (k in queryMap.keys()) '${k}=${queryMap.get(k)}'].join("&");
|
||||||
|
return '${baseUrl}?${query}';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPage(page:Int, perPage:Int, category:PixabayCategory = PixabayCategory.NATURE):Promise<PixabayResponse> {
|
||||||
|
return new JsonLoader<PixabayResponse>().GET(buildRequest([
|
||||||
|
"category" => category,
|
||||||
|
"image_type" => PixabayImageType.PHOTO,
|
||||||
|
"editors_choice" => true,
|
||||||
|
"per_page" => perPage,
|
||||||
|
"page" => page,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(id:Int):Promise<PixabayImage> {
|
||||||
|
return new JsonLoader<PixabayResponse>().GET(buildRequest(["id" => id])).then((response:PixabayResponse) -> response.hits[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,59 +4,55 @@ import hw.net.JsonLoader;
|
|||||||
import promhx.Promise;
|
import promhx.Promise;
|
||||||
|
|
||||||
typedef UnsplashImage = {
|
typedef UnsplashImage = {
|
||||||
var id:String;
|
var id:String;
|
||||||
var width:Int;
|
var width:Int;
|
||||||
var height:Int;
|
var height:Int;
|
||||||
var urls:{
|
var urls:{
|
||||||
raw:String,
|
raw:String,
|
||||||
full:String,
|
full:String,
|
||||||
regular:String,
|
regular:String,
|
||||||
small:String,
|
small:String,
|
||||||
thumb:String,
|
thumb:String,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef UnsplashResponse = {
|
typedef UnsplashResponse = {
|
||||||
var total: Int;
|
var total:Int;
|
||||||
var total_pages: Int;
|
var total_pages:Int;
|
||||||
var results: Array<UnsplashImage>;
|
var results:Array<UnsplashImage>;
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnsplashApi {
|
class UnsplashApi {
|
||||||
private var baseUrl:String = "https://api.unsplash.com";
|
private var baseUrl:String = "https://api.unsplash.com";
|
||||||
private var key:String;
|
private var key:String;
|
||||||
|
|
||||||
public function new(key:String) {
|
public function new(key:String) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildQuery(queryMap:Map<String, Dynamic>):String {
|
private function buildQuery(queryMap:Map<String, Dynamic>):String {
|
||||||
return [for (k in queryMap.keys()) '${k}=${queryMap.get(k)}'].join("&");
|
return [for (k in queryMap.keys()) '${k}=${queryMap.get(k)}'].join("&");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildRequest(queryMap:Map<String, Dynamic>):String {
|
private function buildRequest(queryMap:Map<String, Dynamic>):String {
|
||||||
queryMap.set("client_id", key);
|
queryMap.set("client_id", key);
|
||||||
var query = buildQuery(queryMap);
|
var query = buildQuery(queryMap);
|
||||||
return '${baseUrl}/search/photos?${query}';
|
return '${baseUrl}/search/photos?${query}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPage(page:Int, perPage:Int, query:String):Promise<UnsplashResponse> {
|
public function getPage(page:Int, perPage:Int, query:String):Promise<UnsplashResponse> {
|
||||||
return new JsonLoader<UnsplashResponse>()
|
return new JsonLoader<UnsplashResponse>().GET(buildRequest([
|
||||||
.GET(buildRequest([
|
"per_page" => perPage,
|
||||||
"per_page" => perPage,
|
"page" => page,
|
||||||
"page" => page,
|
"order_by" => "relevant",
|
||||||
"order_by" => "relevant",
|
"orientation" => "landscape",
|
||||||
"orientation" => "landscape",
|
"query" => query,
|
||||||
"query" => query,
|
]));
|
||||||
]));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function get(id:String):Promise<UnsplashImage> {
|
public function get(id:String):Promise<UnsplashImage> {
|
||||||
var queryMap = [
|
var queryMap = ["client_id" => key,];
|
||||||
"client_id" => key,
|
var query = buildQuery(queryMap);
|
||||||
];
|
return new JsonLoader<UnsplashImage>().GET('${baseUrl}/photos/${id}?${query}');
|
||||||
var query = buildQuery(queryMap);
|
}
|
||||||
return new JsonLoader<UnsplashImage>()
|
|
||||||
.GET('${baseUrl}/photos/${id}?${query}');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
52
src/app/haxe/ru/m/cache/StorageCache.hx
vendored
52
src/app/haxe/ru/m/cache/StorageCache.hx
vendored
@@ -5,34 +5,34 @@ import haxe.crypto.Md5;
|
|||||||
import haxe.io.Bytes;
|
import haxe.io.Bytes;
|
||||||
|
|
||||||
class StorageCache {
|
class StorageCache {
|
||||||
private static var DATA_KEY:String = "data";
|
private static var DATA_KEY:String = "data";
|
||||||
|
|
||||||
private var name:String;
|
private var name:String;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
name = "cache";
|
name = "cache";
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveDataObject(key:String):SharedObject {
|
||||||
|
return SharedObject.getLocal('${name}/${Md5.encode(key)}');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exists(key:String):Bool {
|
||||||
|
var dataObject = resolveDataObject(key);
|
||||||
|
return Reflect.hasField(dataObject.data, DATA_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(key:String):Null<Bytes> {
|
||||||
|
var dataObject = resolveDataObject(key);
|
||||||
|
if (Reflect.hasField(dataObject.data, DATA_KEY)) {
|
||||||
|
return Bytes.ofHex(Reflect.field(dataObject.data, DATA_KEY));
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private function resolveDataObject(key:String):SharedObject {
|
public function set(key:String, data:Bytes):Void {
|
||||||
return SharedObject.getLocal('${name}/${Md5.encode(key)}');
|
var dataObject = resolveDataObject(key);
|
||||||
}
|
dataObject.setProperty(DATA_KEY, data.toHex());
|
||||||
|
dataObject.flush();
|
||||||
public function exists(key:String):Bool {
|
}
|
||||||
var dataObject = resolveDataObject(key);
|
|
||||||
return Reflect.hasField(dataObject.data, DATA_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(key:String):Null<Bytes> {
|
|
||||||
var dataObject = resolveDataObject(key);
|
|
||||||
if (Reflect.hasField(dataObject.data, DATA_KEY)) {
|
|
||||||
return Bytes.ofHex(Reflect.field(dataObject.data, DATA_KEY));
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function set(key:String, data:Bytes):Void {
|
|
||||||
var dataObject = resolveDataObject(key);
|
|
||||||
dataObject.setProperty(DATA_KEY, data.toHex());
|
|
||||||
dataObject.flush();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,31 +3,32 @@ package ru.m.data;
|
|||||||
import promhx.Promise;
|
import promhx.Promise;
|
||||||
|
|
||||||
typedef Filter = Map<String, Dynamic>;
|
typedef Filter = Map<String, Dynamic>;
|
||||||
|
|
||||||
typedef Order = Array<{
|
typedef Order = Array<{
|
||||||
var field:String;
|
var field:String;
|
||||||
@:optional var reverse:Bool;
|
@:optional var reverse:Bool;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
typedef Page = {
|
typedef Page = {
|
||||||
var index:Int;
|
var index:Int;
|
||||||
var count:Int;
|
var count:Int;
|
||||||
@:optional var filter:Filter;
|
@:optional var filter:Filter;
|
||||||
@:optional var order:Order;
|
@:optional var order:Order;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef DataPage<D> = {
|
typedef DataPage<D> = {
|
||||||
var page:Page;
|
var page:Page;
|
||||||
var data:Array<D>;
|
var data:Array<D>;
|
||||||
var total:Int;
|
var total:Int;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DataSource<D> {
|
interface DataSource<D> {
|
||||||
public function getPage(page:Page):Promise<DataPage<D>>;
|
public function getPage(page:Page):Promise<DataPage<D>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DataStorage<D, I> extends DataSource<D> {
|
interface DataStorage<D, I> extends DataSource<D> {
|
||||||
public function getIndexPage(page:Page):Promise<DataPage<I>>;
|
public function getIndexPage(page:Page):Promise<DataPage<I>>;
|
||||||
public function get(id:I):Promise<D>;
|
public function get(id:I):Promise<D>;
|
||||||
public function save(item:D):Promise<D>;
|
public function save(item:D):Promise<D>;
|
||||||
public function delete(id:I):Promise<Bool>;
|
public function delete(id:I):Promise<Bool>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,49 +5,49 @@ import flash.display.GraphicsPathCommand;
|
|||||||
import flash.Vector;
|
import flash.Vector;
|
||||||
|
|
||||||
enum DrawCommand {
|
enum DrawCommand {
|
||||||
MOVE_TO(x:Float, y:Float);
|
MOVE_TO(x:Float, y:Float);
|
||||||
LINE_TO(x:Float, y:Float);
|
LINE_TO(x:Float, y:Float);
|
||||||
CURVE_TO(cx:Float, cy:Float, ax:Float, ay:Float);
|
CURVE_TO(cx:Float, cy:Float, ax:Float, ay:Float);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DrawPath {
|
class DrawPath {
|
||||||
public var commands(default, null):Array<DrawCommand>;
|
public var commands(default, null):Array<DrawCommand>;
|
||||||
|
|
||||||
public function new(commands:Array<DrawCommand> = null) {
|
public function new(commands:Array<DrawCommand> = null) {
|
||||||
this.commands = commands != null ? commands : [];
|
this.commands = commands != null ? commands : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function draw(graphics:Graphics):Void {
|
public function draw(graphics:Graphics):Void {
|
||||||
var commands = new Vector<GraphicsPathCommand>();
|
var commands = new Vector<GraphicsPathCommand>();
|
||||||
var data = new Vector<Float>();
|
var data = new Vector<Float>();
|
||||||
for (command in this.commands) {
|
for (command in this.commands) {
|
||||||
switch command {
|
switch command {
|
||||||
case MOVE_TO(x, y):
|
case MOVE_TO(x, y):
|
||||||
commands.push(GraphicsPathCommand.MOVE_TO);
|
commands.push(GraphicsPathCommand.MOVE_TO);
|
||||||
data.push(x);
|
data.push(x);
|
||||||
data.push(y);
|
data.push(y);
|
||||||
case LINE_TO(x, y):
|
case LINE_TO(x, y):
|
||||||
commands.push(GraphicsPathCommand.LINE_TO);
|
commands.push(GraphicsPathCommand.LINE_TO);
|
||||||
data.push(x);
|
data.push(x);
|
||||||
data.push(y);
|
data.push(y);
|
||||||
case CURVE_TO(cx, cy, ax, ay):
|
case CURVE_TO(cx, cy, ax, ay):
|
||||||
commands.push(GraphicsPathCommand.CURVE_TO);
|
commands.push(GraphicsPathCommand.CURVE_TO);
|
||||||
data.push(cx);
|
data.push(cx);
|
||||||
data.push(cy);
|
data.push(cy);
|
||||||
data.push(ax);
|
data.push(ax);
|
||||||
data.push(ay);
|
data.push(ay);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
graphics.drawPath(commands, data);
|
|
||||||
}
|
}
|
||||||
|
graphics.drawPath(commands, data);
|
||||||
|
}
|
||||||
|
|
||||||
public function move(mx:Float, my:Float):DrawPath {
|
public function move(mx:Float, my:Float):DrawPath {
|
||||||
var result = new DrawPath();
|
var result = new DrawPath();
|
||||||
result.commands = commands.map(command -> switch command {
|
result.commands = commands.map(command -> switch command {
|
||||||
case MOVE_TO(x, y): MOVE_TO(x + mx, y + my);
|
case MOVE_TO(x, y): MOVE_TO(x + mx, y + my);
|
||||||
case LINE_TO(x, y): LINE_TO(x + mx, y + my);
|
case LINE_TO(x, y): LINE_TO(x + mx, y + my);
|
||||||
case CURVE_TO(cx, cy, ax, ay): CURVE_TO(cx + mx, cy + my, ax + mx, ay + my);
|
case CURVE_TO(cx, cy, ax, ay): CURVE_TO(cx + mx, cy + my, ax + mx, ay + my);
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,4 @@ package ru.m.event;
|
|||||||
|
|
||||||
import flash.events.Event;
|
import flash.events.Event;
|
||||||
|
|
||||||
class GestureEvent extends Event {
|
class GestureEvent extends Event {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,69 +5,68 @@ import flash.display.DisplayObject;
|
|||||||
import flash.events.TouchEvent;
|
import flash.events.TouchEvent;
|
||||||
|
|
||||||
typedef Touch = {
|
typedef Touch = {
|
||||||
var id:Int;
|
var id:Int;
|
||||||
var point:Point;
|
var point:Point;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GestureManager {
|
class GestureManager {
|
||||||
|
private var target:DisplayObject;
|
||||||
|
private var touchesMap:Map<Int, Touch>;
|
||||||
|
private var touches:Array<Touch>;
|
||||||
|
private var distance:Float;
|
||||||
|
|
||||||
private var target:DisplayObject;
|
public function new(target:DisplayObject) {
|
||||||
private var touchesMap:Map<Int, Touch>;
|
this.target = target;
|
||||||
private var touches:Array<Touch>;
|
touchesMap = new Map();
|
||||||
private var distance:Float;
|
touches = new Array();
|
||||||
|
target.addEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin);
|
||||||
|
target.addEventListener(TouchEvent.TOUCH_MOVE, onTouchMove);
|
||||||
|
target.addEventListener(TouchEvent.TOUCH_END, onTouchEnd);
|
||||||
|
}
|
||||||
|
|
||||||
public function new(target:DisplayObject) {
|
private function addTouch(id:Int, point:Point):Void {
|
||||||
this.target = target;
|
var touch:Touch = {id: id, point: point};
|
||||||
touchesMap = new Map();
|
touchesMap.set(touch.id, touch);
|
||||||
touches = new Array();
|
touches.push(touch);
|
||||||
target.addEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin);
|
if (touches.length == 2) {
|
||||||
target.addEventListener(TouchEvent.TOUCH_MOVE, onTouchMove);
|
distance = Point.distance(touches[0].point, touches[1].point);
|
||||||
target.addEventListener(TouchEvent.TOUCH_END, onTouchEnd);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function addTouch(id:Int, point:Point):Void {
|
private function updateTouch(id:Int, point:Point):Void {
|
||||||
var touch:Touch = {id: id, point: point};
|
touchesMap.get(id).point = point;
|
||||||
touchesMap.set(touch.id, touch);
|
if (touches.length == 2) {
|
||||||
touches.push(touch);
|
var newDistance = Point.distance(touches[0].point, touches[1].point);
|
||||||
if (touches.length == 2) {
|
var event = new ZoomGestureEvent(ZoomGestureEvent.GESTURE_ZOOM);
|
||||||
distance = Point.distance(touches[0].point, touches[1].point);
|
event.zoom = (newDistance - distance) * 0.001;
|
||||||
}
|
distance = newDistance;
|
||||||
|
target.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function updateTouch(id:Int, point:Point):Void {
|
private function removeTouch(id:Int):Void {
|
||||||
touchesMap.get(id).point = point;
|
touches.remove(touchesMap.get(id));
|
||||||
if (touches.length == 2) {
|
touchesMap.remove(id);
|
||||||
var newDistance = Point.distance(touches[0].point, touches[1].point);
|
}
|
||||||
var event = new ZoomGestureEvent(ZoomGestureEvent.GESTURE_ZOOM);
|
|
||||||
event.zoom = (newDistance - distance) * 0.001;
|
|
||||||
distance = newDistance;
|
|
||||||
target.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function removeTouch(id:Int):Void {
|
private function onTouchBegin(event:TouchEvent):Void {
|
||||||
touches.remove(touchesMap.get(id));
|
addTouch(event.touchPointID, new Point(event.stageX, event.stageY));
|
||||||
touchesMap.remove(id);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private function onTouchBegin(event:TouchEvent):Void {
|
private function onTouchMove(event:TouchEvent):Void {
|
||||||
addTouch(event.touchPointID, new Point(event.stageX, event.stageY));
|
updateTouch(event.touchPointID, new Point(event.stageX, event.stageY));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function onTouchMove(event:TouchEvent):Void {
|
private function onTouchEnd(event:TouchEvent):Void {
|
||||||
updateTouch(event.touchPointID, new Point(event.stageX, event.stageY));
|
removeTouch(event.touchPointID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function onTouchEnd(event:TouchEvent):Void {
|
public function dispose():Void {
|
||||||
removeTouch(event.touchPointID);
|
if (target != null) {
|
||||||
}
|
target.removeEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin);
|
||||||
|
target.removeEventListener(TouchEvent.TOUCH_MOVE, onTouchMove);
|
||||||
public function dispose():Void {
|
target.removeEventListener(TouchEvent.TOUCH_END, onTouchEnd);
|
||||||
if (target != null) {
|
target = null;
|
||||||
target.removeEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin);
|
|
||||||
target.removeEventListener(TouchEvent.TOUCH_MOVE, onTouchMove);
|
|
||||||
target.removeEventListener(TouchEvent.TOUCH_END, onTouchEnd);
|
|
||||||
target = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package ru.m.event;
|
package ru.m.event;
|
||||||
|
|
||||||
class ZoomGestureEvent extends GestureEvent {
|
class ZoomGestureEvent extends GestureEvent {
|
||||||
public static var GESTURE_ZOOM(default, never):String = "gesture_zoom";
|
public static var GESTURE_ZOOM(default, never):String = "gesture_zoom";
|
||||||
|
|
||||||
public var zoom(default, default):Float;
|
public var zoom(default, default):Float;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
package ru.m.pixabay;
|
|
||||||
|
|
||||||
import hw.net.JsonLoader;
|
|
||||||
import promhx.Promise;
|
|
||||||
|
|
||||||
typedef PixabayImage = {
|
|
||||||
var id:Int;
|
|
||||||
var largeImageURL:String;
|
|
||||||
var webformatURL:String;
|
|
||||||
var previewURL:String;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef PixabayResponse = {
|
|
||||||
var total:Int;
|
|
||||||
var totalHits:Int;
|
|
||||||
var hits:Array<PixabayImage>;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum abstract PixabayCategory(String) from String to String {
|
|
||||||
var FASHION = "fashion";
|
|
||||||
var NATURE = "nature";
|
|
||||||
var BACKGROUNDS = "backgrounds";
|
|
||||||
var SCIENCE = "science";
|
|
||||||
var EDUCATION = "education";
|
|
||||||
var PEOPLE = "people";
|
|
||||||
var FEELINGS = "feelings";
|
|
||||||
var RELIGION = "religion";
|
|
||||||
var HEALTH = "health";
|
|
||||||
var PLACES = "places";
|
|
||||||
var ANIMALS = "animals";
|
|
||||||
var INDUSTRY = "industry";
|
|
||||||
var FOOD = "food";
|
|
||||||
var COMPUTER = "computer";
|
|
||||||
var SPORTS = "sports";
|
|
||||||
var TRANSPORTATION = "transportation";
|
|
||||||
var TRAVEL = "travel";
|
|
||||||
var BUILDINGS = "buildings";
|
|
||||||
var BUSINESS = "business";
|
|
||||||
var MUSIC = "music";
|
|
||||||
}
|
|
||||||
|
|
||||||
enum abstract PixabayImageType(String) from String to String {
|
|
||||||
var ALL = "all";
|
|
||||||
var PHOTO = "photo";
|
|
||||||
var ILLUSTRATION = "illustration";
|
|
||||||
var VECTOR = "vector";
|
|
||||||
}
|
|
||||||
|
|
||||||
class PixabayApi {
|
|
||||||
private var baseUrl:String = "https://pixabay.com/api/";
|
|
||||||
private var key:String;
|
|
||||||
|
|
||||||
public function new(key:String) {
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function buildRequest(queryMap:Map<String, Dynamic>):String {
|
|
||||||
queryMap.set("key", key);
|
|
||||||
var query = [for (k in queryMap.keys()) '${k}=${queryMap.get(k)}'].join("&");
|
|
||||||
return '${baseUrl}?${query}';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPage(page:Int, perPage:Int, category:PixabayCategory = PixabayCategory.NATURE):Promise<PixabayResponse> {
|
|
||||||
return new JsonLoader<PixabayResponse>()
|
|
||||||
.GET(buildRequest([
|
|
||||||
"category" => category,
|
|
||||||
"image_type" => PixabayImageType.PHOTO,
|
|
||||||
"editors_choice" => true,
|
|
||||||
"per_page" => perPage,
|
|
||||||
"page" => page,
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(id:Int):Promise<PixabayImage> {
|
|
||||||
return new JsonLoader<PixabayResponse>()
|
|
||||||
.GET(buildRequest(["id" => id]))
|
|
||||||
.then((response:PixabayResponse) -> response.hits[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,37 +8,36 @@ import promhx.Deferred;
|
|||||||
import promhx.Promise;
|
import promhx.Promise;
|
||||||
|
|
||||||
typedef FileContent = {
|
typedef FileContent = {
|
||||||
var name:String;
|
var name:String;
|
||||||
var content:Bytes;
|
var content:Bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
class FileUtil {
|
class FileUtil {
|
||||||
|
public static function browse():Promise<FileContent> {
|
||||||
|
var d = new Deferred<FileContent>();
|
||||||
|
var file = new ModernFileReference();
|
||||||
|
file.addEventListener(Event.SELECT, (event:Event) -> {
|
||||||
|
cast(event.target, ModernFileReference).load();
|
||||||
|
});
|
||||||
|
file.addEventListener(IOErrorEvent.IO_ERROR, (event:IOErrorEvent) -> {
|
||||||
|
d.throwError(event);
|
||||||
|
});
|
||||||
|
file.addEventListener(ProgressEvent.PROGRESS, (event:ProgressEvent) -> {
|
||||||
|
// trace('progress', '${event}');
|
||||||
|
});
|
||||||
|
file.addEventListener(Event.COMPLETE, (event:Event) -> {
|
||||||
|
var f:ModernFileReference = cast event.target;
|
||||||
|
d.resolve({
|
||||||
|
name: f.name,
|
||||||
|
content: Bytes.ofData(f.data),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
file.browse();
|
||||||
|
return d.promise();
|
||||||
|
}
|
||||||
|
|
||||||
public static function browse():Promise<FileContent> {
|
public static function save(content:FileContent):Void {
|
||||||
var d = new Deferred<FileContent>();
|
var file = new ModernFileReference();
|
||||||
var file = new ModernFileReference();
|
file.save(content.content.getData(), content.name);
|
||||||
file.addEventListener(Event.SELECT, (event:Event) -> {
|
}
|
||||||
cast(event.target, ModernFileReference).load();
|
|
||||||
});
|
|
||||||
file.addEventListener(IOErrorEvent.IO_ERROR, (event:IOErrorEvent) -> {
|
|
||||||
d.throwError(event);
|
|
||||||
});
|
|
||||||
file.addEventListener(ProgressEvent.PROGRESS, (event:ProgressEvent) -> {
|
|
||||||
//trace('progress', '${event}');
|
|
||||||
});
|
|
||||||
file.addEventListener(Event.COMPLETE, (event:Event) -> {
|
|
||||||
var f:ModernFileReference = cast event.target;
|
|
||||||
d.resolve({
|
|
||||||
name: f.name,
|
|
||||||
content: Bytes.ofData(f.data),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
file.browse();
|
|
||||||
return d.promise();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function save(content:FileContent):Void {
|
|
||||||
var file = new ModernFileReference();
|
|
||||||
file.save(content.content.getData(), content.name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,17 +11,16 @@ import promhx.Deferred;
|
|||||||
import promhx.Promise;
|
import promhx.Promise;
|
||||||
|
|
||||||
class ImageUtil {
|
class ImageUtil {
|
||||||
|
public static function bytesToImage(bytes:Bytes):Promise<BitmapData> {
|
||||||
public static function bytesToImage(bytes:Bytes):Promise<BitmapData> {
|
var def = new Deferred();
|
||||||
var def = new Deferred();
|
var loader = new Loader();
|
||||||
var loader = new Loader();
|
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, (event:Event) -> {
|
||||||
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, (event:Event) -> {
|
def.resolve(cast(cast(event.target, LoaderInfo).content, Bitmap).bitmapData);
|
||||||
def.resolve(cast(cast(event.target, LoaderInfo).content, Bitmap).bitmapData);
|
});
|
||||||
});
|
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, (event:IOErrorEvent) -> {
|
||||||
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, (event:IOErrorEvent) -> {
|
def.throwError(event);
|
||||||
def.throwError(event);
|
});
|
||||||
});
|
loader.loadBytes(bytes);
|
||||||
loader.loadBytes(bytes);
|
return def.promise();
|
||||||
return def.promise();
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,25 +15,24 @@ import ru.m.puzzlez.view.PuzzlezAppView;
|
|||||||
import ru.m.update.Updater;
|
import ru.m.update.Updater;
|
||||||
|
|
||||||
class PuzzlezApp {
|
class PuzzlezApp {
|
||||||
|
@:provide static var updater:Updater;
|
||||||
|
@:provide static var sourceBundle:ImageSourceBundle;
|
||||||
|
|
||||||
@:provide static var updater:Updater;
|
public static function main() {
|
||||||
@:provide static var sourceBundle:ImageSourceBundle;
|
// ToDo: fix @:provide macro
|
||||||
|
Settings;
|
||||||
public static function main() {
|
IPartBuilder;
|
||||||
// ToDo: fix @:provide macro
|
GameStorage;
|
||||||
Settings;
|
sourceBundle.register(new AssetImageSource());
|
||||||
IPartBuilder;
|
sourceBundle.register(new FileImageSource());
|
||||||
GameStorage;
|
sourceBundle.register(new PixabayImageSource());
|
||||||
sourceBundle.register(new AssetImageSource());
|
sourceBundle.register(new UnsplashImageSource());
|
||||||
sourceBundle.register(new FileImageSource());
|
L.push(new TraceLogger());
|
||||||
sourceBundle.register(new PixabayImageSource());
|
updater = new Updater(Const.instance.VERSION, "https://shmyga.ru/repo/puzzlez/packages.json");
|
||||||
sourceBundle.register(new UnsplashImageSource());
|
var app = new App();
|
||||||
L.push(new TraceLogger());
|
app.theme = new PuzzlezTheme();
|
||||||
updater = new Updater(Const.instance.VERSION, "https://shmyga.ru/repo/puzzlez/packages.json");
|
app.icon = openfl.Assets.getBitmapData("resources/icon.png");
|
||||||
var app = new App();
|
app.view = new PuzzlezAppView();
|
||||||
app.theme = new PuzzlezTheme();
|
L.d("Puzzlez", "started");
|
||||||
app.icon = openfl.Assets.getBitmapData("resources/icon.png");
|
}
|
||||||
app.view = new PuzzlezAppView();
|
|
||||||
L.d("Puzzlez", "started");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,75 +10,60 @@ import openfl.Assets;
|
|||||||
import ru.m.skin.ButtonSVGSkin;
|
import ru.m.skin.ButtonSVGSkin;
|
||||||
|
|
||||||
class PuzzlezTheme extends Theme {
|
class PuzzlezTheme extends Theme {
|
||||||
|
private static var ICONS:Map<String, String> = [
|
||||||
|
"close" => "times-circle-solid.svg",
|
||||||
|
"setting" => "cog-solid.svg",
|
||||||
|
"image" => "image-polaroid.svg",
|
||||||
|
"lock" => "lock-alt-solid.svg",
|
||||||
|
"restore" => "window-restore-solid.svg",
|
||||||
|
"compress" => "compress-solid.svg",
|
||||||
|
"expand" => "expand-solid.svg",
|
||||||
|
];
|
||||||
|
|
||||||
private static var ICONS:Map<String, String> = [
|
public function new() {
|
||||||
"close" => "times-circle-solid.svg",
|
super({embed: true}, {light: "gray"}, {base: "4h"});
|
||||||
"setting" => "cog-solid.svg",
|
register(new Style("frame", ["geometry.padding" => Box.fromFloat(8),]));
|
||||||
"image" => "image-polaroid.svg",
|
register(new Style("view", [
|
||||||
"lock" => "lock-alt-solid.svg",
|
"skin.background.color" => colors.light,
|
||||||
"restore" => "window-restore-solid.svg",
|
"skin.border.color" => colors.border,
|
||||||
"compress" => "compress-solid.svg",
|
"geometry.padding" => Box.fromFloat(3),
|
||||||
"expand" => "expand-solid.svg",
|
"geometry.width" => SizeValue.fromString("50h"),
|
||||||
];
|
"geometry.height" => SizeValue.fromString("40h"),
|
||||||
|
]));
|
||||||
public function new() {
|
register(new Style("text.error", ["font.color" => Color.fromString("red"),], "text"));
|
||||||
super({embed: true}, {light: "gray"}, {base: "4h"});
|
register(new Style("icon", [
|
||||||
register(new Style("frame", [
|
"geometry.width" => SizeValue.fromString("8h"),
|
||||||
"geometry.padding" => Box.fromFloat(8),
|
"geometry.height" => SizeValue.fromString("8h"),
|
||||||
]));
|
"skin" => function() return new ButtonSVGSkin(),
|
||||||
register(new Style("view", [
|
"skin.color" => colors.light,
|
||||||
"skin.background.color" => colors.light,
|
]));
|
||||||
"skin.border.color" => colors.border,
|
for (key in ICONS.keys()) {
|
||||||
"geometry.padding" => Box.fromFloat(3),
|
register(new Style('icon.${key}', ["skin.svg" => Assets.getText('resources/icon/${ICONS.get(key)}'),]));
|
||||||
"geometry.width" => SizeValue.fromString("50h"),
|
|
||||||
"geometry.height" => SizeValue.fromString("40h"),
|
|
||||||
]));
|
|
||||||
register(new Style("text.error", [
|
|
||||||
"font.color" => Color.fromString("red"),
|
|
||||||
], "text"));
|
|
||||||
register(new Style("icon", [
|
|
||||||
"geometry.width" => SizeValue.fromString("8h"),
|
|
||||||
"geometry.height" => SizeValue.fromString("8h"),
|
|
||||||
"skin" => function() return new ButtonSVGSkin(),
|
|
||||||
"skin.color" => colors.light,
|
|
||||||
]));
|
|
||||||
for (key in ICONS.keys()) {
|
|
||||||
register(new Style('icon.${key}', [
|
|
||||||
"skin.svg" => Assets.getText('resources/icon/${ICONS.get(key)}'),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
register(new Style("icon.small", [
|
|
||||||
"geometry.width" => SizeValue.fromString("6h"),
|
|
||||||
"geometry.height" => SizeValue.fromString("6h"),
|
|
||||||
]));
|
|
||||||
register(new Style("icon.red", [
|
|
||||||
"skin.color" => 0xcc0000,
|
|
||||||
]));
|
|
||||||
register(new Style("icon.orange", [
|
|
||||||
"skin.color" => 0xcc5500,
|
|
||||||
]));
|
|
||||||
register(new Style("icon.green", [
|
|
||||||
"skin.color" => 0x00ff00,
|
|
||||||
]));
|
|
||||||
register(new Style("icon.control", [
|
|
||||||
"geometry.hAlign" => HAlign.RIGHT,
|
|
||||||
"geometry.vAlign" => VAlign.TOP,
|
|
||||||
"geometry.margin" => Box.fromFloat(3),
|
|
||||||
]));
|
|
||||||
|
|
||||||
register(new Style("button.red", [
|
|
||||||
"skin.color" => 0xcc0000,
|
|
||||||
], "button"));
|
|
||||||
|
|
||||||
register(new Style("label.header", [
|
|
||||||
"font.size" => SizeValue.fromString("5h"),
|
|
||||||
"geometry.hAlign" => HAlign.CENTER,
|
|
||||||
"geometry.margin.top" => 10,
|
|
||||||
"geometry.margin.bottom" => 10,
|
|
||||||
], "label"));
|
|
||||||
register(new Style("button.background", [
|
|
||||||
"geometry.width" => SizeValue.fromString("10h"),
|
|
||||||
"geometry.height" => SizeValue.fromString("10h"),
|
|
||||||
]));
|
|
||||||
}
|
}
|
||||||
|
register(new Style("icon.small", [
|
||||||
|
"geometry.width" => SizeValue.fromString("6h"),
|
||||||
|
"geometry.height" => SizeValue.fromString("6h"),
|
||||||
|
]));
|
||||||
|
register(new Style("icon.red", ["skin.color" => 0xcc0000,]));
|
||||||
|
register(new Style("icon.orange", ["skin.color" => 0xcc5500,]));
|
||||||
|
register(new Style("icon.green", ["skin.color" => 0x00ff00,]));
|
||||||
|
register(new Style("icon.control", [
|
||||||
|
"geometry.hAlign" => HAlign.RIGHT,
|
||||||
|
"geometry.vAlign" => VAlign.TOP,
|
||||||
|
"geometry.margin" => Box.fromFloat(3),
|
||||||
|
]));
|
||||||
|
|
||||||
|
register(new Style("button.red", ["skin.color" => 0xcc0000,], "button"));
|
||||||
|
|
||||||
|
register(new Style("label.header", [
|
||||||
|
"font.size" => SizeValue.fromString("5h"),
|
||||||
|
"geometry.hAlign" => HAlign.CENTER,
|
||||||
|
"geometry.margin.top" => 10,
|
||||||
|
"geometry.margin.bottom" => 10,
|
||||||
|
], "label"));
|
||||||
|
register(new Style("button.background", [
|
||||||
|
"geometry.width" => SizeValue.fromString("10h"),
|
||||||
|
"geometry.height" => SizeValue.fromString("10h"),
|
||||||
|
]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import ru.m.puzzlez.proto.game.ImageId;
|
|||||||
import ru.m.puzzlez.proto.game.GameState;
|
import ru.m.puzzlez.proto.game.GameState;
|
||||||
|
|
||||||
enum GameAction {
|
enum GameAction {
|
||||||
START(state:GameState);
|
START(state:GameState);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Action {
|
enum Action {
|
||||||
GAME(state:GameState, ?delete:Bool);
|
GAME(state:GameState, ?delete:Bool);
|
||||||
IMAGE(image:ImageId);
|
IMAGE(image:ImageId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ import ru.m.data.DataSource;
|
|||||||
import ru.m.puzzlez.proto.game.ImageId;
|
import ru.m.puzzlez.proto.game.ImageId;
|
||||||
|
|
||||||
interface ImageSource extends DataSource<ImageId> {
|
interface ImageSource extends DataSource<ImageId> {
|
||||||
public var id(default, never):String;
|
public var id(default, never):String;
|
||||||
public function load(id:String, thumb:Bool = false):Promise<ImageValue>;
|
public function load(id:String, thumb:Bool = false):Promise<ImageValue>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import haxe.io.Bytes;
|
|||||||
import flash.display.BitmapData;
|
import flash.display.BitmapData;
|
||||||
|
|
||||||
enum ImageValue {
|
enum ImageValue {
|
||||||
BITMAP(value:BitmapData);
|
BITMAP(value:BitmapData);
|
||||||
BYTES(value:Bytes);
|
BYTES(value:Bytes);
|
||||||
URL(value:String);
|
URL(value:String);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,50 +9,49 @@ import ru.m.puzzlez.core.ImageValue;
|
|||||||
import ru.m.puzzlez.proto.game.ImageId;
|
import ru.m.puzzlez.proto.game.ImageId;
|
||||||
|
|
||||||
abstract ImageData(ImageId) from ImageId {
|
abstract ImageData(ImageId) from ImageId {
|
||||||
|
private static var cache:Map<String, Promise<BitmapData>> = new Map();
|
||||||
|
private static var storageCache:StorageCache = new StorageCache();
|
||||||
|
|
||||||
private static var cache:Map<String, Promise<BitmapData>> = new Map();
|
@:provide private var sourceBundle:ImageSourceBundle;
|
||||||
private static var storageCache:StorageCache = new StorageCache();
|
|
||||||
|
|
||||||
@:provide private var sourceBundle:ImageSourceBundle;
|
public function new(image:ImageId) {
|
||||||
|
this = image;
|
||||||
|
}
|
||||||
|
|
||||||
public function new(image: ImageId) {
|
@:from public static function fromImageId(value:ImageId):ImageData {
|
||||||
this = image;
|
return new ImageData(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@:from public static function fromImageId(value:ImageId):ImageData {
|
public function withThumb():ImageData {
|
||||||
return new ImageData(value);
|
return new ImageData(new ImageId().setSource(this.source).setId(this.id).setThumb(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function withThumb():ImageData {
|
private function extractImage(key:String, value:ImageValue):Promise<BitmapData> {
|
||||||
return new ImageData(new ImageId().setSource(this.source).setId(this.id).setThumb(true));
|
return switch value {
|
||||||
}
|
case ImageValue.BITMAP(value):
|
||||||
|
Promise.promise(value);
|
||||||
private function extractImage(key:String, value:ImageValue):Promise<BitmapData> {
|
case ImageValue.BYTES(value):
|
||||||
return switch value {
|
if (!storageCache.exists(key)) {
|
||||||
case ImageValue.BITMAP(value):
|
storageCache.set(key, value);
|
||||||
Promise.promise(value);
|
|
||||||
case ImageValue.BYTES(value):
|
|
||||||
if (!storageCache.exists(key)) {
|
|
||||||
storageCache.set(key, value);
|
|
||||||
}
|
|
||||||
ImageUtil.bytesToImage(value);
|
|
||||||
case ImageValue.URL(value):
|
|
||||||
new BytesLoader().GET(value).pipe(bytes -> extractImage(key, ImageValue.BYTES(Bytes.ofData(bytes))));
|
|
||||||
}
|
}
|
||||||
|
ImageUtil.bytesToImage(value);
|
||||||
|
case ImageValue.URL(value):
|
||||||
|
new BytesLoader().GET(value).pipe(bytes -> extractImage(key, ImageValue.BYTES(Bytes.ofData(bytes))));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function resolve():Promise<BitmapData> {
|
public function resolve():Promise<BitmapData> {
|
||||||
var key = '${this.source}:${this.id}';
|
var key = '${this.source}:${this.id}';
|
||||||
if (this.thumb) {
|
if (this.thumb) {
|
||||||
key = '${key}:thumb';
|
key = '${key}:thumb';
|
||||||
}
|
|
||||||
if (!cache.exists(key)) {
|
|
||||||
if (storageCache.exists(key)) {
|
|
||||||
cache.set(key, extractImage(key, ImageValue.BYTES(storageCache.get(key))));
|
|
||||||
} else {
|
|
||||||
cache.set(key, sourceBundle.get(this.source).load(this.id, this.thumb).pipe(value -> extractImage(key, value)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cache.get(key);
|
|
||||||
}
|
}
|
||||||
|
if (!cache.exists(key)) {
|
||||||
|
if (storageCache.exists(key)) {
|
||||||
|
cache.set(key, extractImage(key, ImageValue.BYTES(storageCache.get(key))));
|
||||||
|
} else {
|
||||||
|
cache.set(key, sourceBundle.get(this.source).load(this.id, this.thumb).pipe(value -> extractImage(key, value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cache.get(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,20 +3,20 @@ package ru.m.puzzlez.image;
|
|||||||
import ru.m.puzzlez.core.ImageSource;
|
import ru.m.puzzlez.core.ImageSource;
|
||||||
|
|
||||||
@:provide class ImageSourceBundle {
|
@:provide class ImageSourceBundle {
|
||||||
private var sources:Map<String, ImageSource>;
|
private var sources:Map<String, ImageSource>;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
sources = new Map();
|
sources = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(source:ImageSource):Void {
|
public function register(source:ImageSource):Void {
|
||||||
sources.set(source.id, source);
|
sources.set(source.id, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get(sourceId:String):ImageSource {
|
public function get(sourceId:String):ImageSource {
|
||||||
if (!sources.exists(sourceId)) {
|
if (!sources.exists(sourceId)) {
|
||||||
throw 'ImageSource "$sourceId" not registered';
|
throw 'ImageSource "$sourceId" not registered';
|
||||||
}
|
|
||||||
return sources.get(sourceId);
|
|
||||||
}
|
}
|
||||||
|
return sources.get(sourceId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,99 +22,99 @@ import ru.m.puzzlez.proto.pack.Request;
|
|||||||
import ru.m.puzzlez.proto.pack.Response;
|
import ru.m.puzzlez.proto.pack.Response;
|
||||||
|
|
||||||
@:provide class Network implements IDataSource<String, GameState> {
|
@:provide class Network implements IDataSource<String, GameState> {
|
||||||
public var userSignal:Signal<User> = new Signal();
|
public var userSignal:Signal<User> = new Signal();
|
||||||
public var notificationSignal:Signal<NotificationResponse> = new Signal();
|
public var notificationSignal:Signal<NotificationResponse> = new Signal();
|
||||||
public var listSignal:Signal<GameListResponse> = new Signal();
|
public var listSignal:Signal<GameListResponse> = new Signal();
|
||||||
public var joinSignal:Signal<GameState> = new Signal();
|
public var joinSignal:Signal<GameState> = new Signal();
|
||||||
public var gameEventSignal:Signal<GameEvent> = new Signal();
|
public var gameEventSignal:Signal<GameEvent> = new Signal();
|
||||||
|
|
||||||
private var connection:IConnection<Request, Response>;
|
private var connection:IConnection<Request, Response>;
|
||||||
private var storage:SharedObjectStorage;
|
private var storage:SharedObjectStorage;
|
||||||
|
|
||||||
private static var USER_KEY = "user";
|
private static var USER_KEY = "user";
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
storage = new SharedObjectStorage("network/2");
|
storage = new SharedObjectStorage("network/2");
|
||||||
connection = ConnectionFactory.buildClientConnection("127.0.0.1", 5000, Response);
|
connection = ConnectionFactory.buildClientConnection("127.0.0.1", 5000, Response);
|
||||||
connection.handler.connect(onConnectionChange);
|
connection.handler.connect(onConnectionChange);
|
||||||
connection.receiveHandler.connect(onReceivePacket);
|
connection.receiveHandler.connect(onReceivePacket);
|
||||||
connection.connect().catchError(_ -> {});
|
connection.connect().catchError(_ -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function restoreUser():User {
|
||||||
|
if (storage.exists(USER_KEY)) {
|
||||||
|
return storage.read(USER_KEY);
|
||||||
|
} else {
|
||||||
|
return new User().setName('Anonimus #${IdUtil.generate()}');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function restoreUser():User {
|
public function auth():Promise<User> {
|
||||||
if (storage.exists(USER_KEY)) {
|
connection.send(new Request().setAuth(new AuthRequest().setUser(restoreUser())));
|
||||||
return storage.read(USER_KEY);
|
return userSignal.next();
|
||||||
} else {
|
}
|
||||||
return new User().setName('Anonimus #${IdUtil.generate()}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function auth():Promise<User> {
|
public function createGame(preset:GamePreset):Promise<GameState> {
|
||||||
connection.send(new Request().setAuth(new AuthRequest().setUser(restoreUser())));
|
connection.send(new Request().setJoin(new GameJoinRequest().setPreset(preset)));
|
||||||
return userSignal.next();
|
return joinSignal.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createGame(preset:GamePreset):Promise<GameState> {
|
public function joinGame(gameId:String):Promise<GameState> {
|
||||||
connection.send(new Request().setJoin(new GameJoinRequest().setPreset(preset)));
|
connection.send(new Request().setJoin(new GameJoinRequest().setGameId(gameId)));
|
||||||
return joinSignal.next();
|
return joinSignal.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function joinGame(gameId:String):Promise<GameState> {
|
public function leaveGame():Void {
|
||||||
connection.send(new Request().setJoin(new GameJoinRequest().setGameId(gameId)));
|
connection.send(new Request().setLeave(new GameLeaveRequest()));
|
||||||
return joinSignal.next();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function leaveGame():Void {
|
public function sendGameAction(action:GameAction):Void {
|
||||||
connection.send(new Request().setLeave(new GameLeaveRequest()));
|
connection.send(new Request().setAction(new GameActionRequest().setActions([action])));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendGameAction(action:GameAction):Void {
|
private function onConnectionChange(event:ConnectionEvent):Void {
|
||||||
connection.send(new Request().setAction(new GameActionRequest().setActions([action])));
|
L.i("network", '${event}');
|
||||||
|
switch event {
|
||||||
|
case CONNECTED:
|
||||||
|
auth().then(user -> storage.write(USER_KEY, user));
|
||||||
|
case DISCONNECTED:
|
||||||
|
//
|
||||||
|
case ERROR(error):
|
||||||
|
// ToDo: reconnect
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function onConnectionChange(event:ConnectionEvent):Void {
|
private function onReceivePacket(packet:Response):Void {
|
||||||
L.i("network", '${event}');
|
if (packet.hasAuth()) {
|
||||||
switch event {
|
userSignal.emit(packet.auth.user);
|
||||||
case CONNECTED:
|
} else if (packet.hasNotification()) {
|
||||||
auth().then(user -> storage.write(USER_KEY, user));
|
notificationSignal.emit(packet.notification);
|
||||||
case DISCONNECTED:
|
} else if (packet.hasList()) {
|
||||||
//
|
listSignal.emit(packet.list);
|
||||||
case ERROR(error):
|
} else if (packet.hasJoin()) {
|
||||||
// ToDo: reconnect
|
joinSignal.emit(packet.join.game);
|
||||||
}
|
} else if (packet.hasEvent()) {
|
||||||
|
for (event in packet.event.events) {
|
||||||
|
gameEventSignal.emit(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function onReceivePacket(packet:Response):Void {
|
public function getPage(page:Page):Promise<DataPage<GameState>> {
|
||||||
if (packet.hasAuth()) {
|
connection.send(new Request().setList(new GameListRequest().setCount(page.count).setPage(page.index)));
|
||||||
userSignal.emit(packet.auth.user);
|
return listSignal.next().then((list:GameListResponse) -> ({
|
||||||
} else if (packet.hasNotification()) {
|
page: {
|
||||||
notificationSignal.emit(packet.notification);
|
index: list.page,
|
||||||
} else if (packet.hasList()) {
|
count: list.count,
|
||||||
listSignal.emit(packet.list);
|
filter: null,
|
||||||
} else if (packet.hasJoin()) {
|
order: null,
|
||||||
joinSignal.emit(packet.join.game);
|
},
|
||||||
} else if (packet.hasEvent()) {
|
total: list.total,
|
||||||
for (event in packet.event.events) {
|
data: list.games,
|
||||||
gameEventSignal.emit(event);
|
}));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPage(page:Page):Promise<DataPage<GameState>> {
|
public function get(id:String):GameState {
|
||||||
connection.send(new Request().setList(new GameListRequest().setCount(page.count).setPage(page.index)));
|
return null;
|
||||||
return listSignal.next().then((list:GameListResponse) -> ({
|
}
|
||||||
page: {
|
|
||||||
index: list.page,
|
|
||||||
count: list.count,
|
|
||||||
filter: null,
|
|
||||||
order: null,
|
|
||||||
},
|
|
||||||
total: list.total,
|
|
||||||
data: list.games,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(id:String):GameState {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,36 +8,36 @@ import ru.m.puzzlez.proto.event.GameEvent;
|
|||||||
import ru.m.puzzlez.proto.game.GameState;
|
import ru.m.puzzlez.proto.game.GameState;
|
||||||
|
|
||||||
class NetworkGame implements IGame {
|
class NetworkGame implements IGame {
|
||||||
public var state(default, null):GameState;
|
public var state(default, null):GameState;
|
||||||
public var events(default, null):Signal<GameEvent>;
|
public var events(default, null):Signal<GameEvent>;
|
||||||
|
|
||||||
@:provide private var network:Network;
|
@:provide private var network:Network;
|
||||||
|
|
||||||
public function new(state:GameState) {
|
public function new(state:GameState) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
events = new Signal();
|
events = new Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function action(action:GameAction):Void {
|
public function action(action:GameAction):Void {
|
||||||
network.sendGameAction(action);
|
network.sendGameAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function start():Void {
|
public function start():Void {
|
||||||
events.emit(new GameEvent().setStart(new GameStart().setState(state)));
|
events.emit(new GameEvent().setStart(new GameStart().setState(state)));
|
||||||
network.gameEventSignal.connect(onEvent);
|
network.gameEventSignal.connect(onEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stop():Void {
|
public function stop():Void {
|
||||||
network.gameEventSignal.disconnect(onEvent);
|
network.gameEventSignal.disconnect(onEvent);
|
||||||
network.leaveGame();
|
network.leaveGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dispose():Void {
|
public function dispose():Void {
|
||||||
stop();
|
stop();
|
||||||
events.dispose();
|
events.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function onEvent(event:GameEvent):Void {
|
private function onEvent(event:GameEvent):Void {
|
||||||
events.emit(event);
|
events.emit(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import hw.color.Color;
|
|||||||
import ru.m.puzzlez.proto.game.ImageId;
|
import ru.m.puzzlez.proto.game.ImageId;
|
||||||
|
|
||||||
enum Background {
|
enum Background {
|
||||||
NONE;
|
NONE;
|
||||||
COLOR(color:Color);
|
COLOR(color:Color);
|
||||||
IMAGE(id:ImageId);
|
IMAGE(id:ImageId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,66 +7,65 @@ import flash.geom.Matrix;
|
|||||||
import ru.m.puzzlez.render.part.IPartBuilder;
|
import ru.m.puzzlez.render.part.IPartBuilder;
|
||||||
|
|
||||||
class CompleteView extends Shape {
|
class CompleteView extends Shape {
|
||||||
|
public var parts:Array<PartView>;
|
||||||
|
public var preset:GamePreset;
|
||||||
|
|
||||||
public var parts:Array<PartView>;
|
@:provide static var builder:IPartBuilder;
|
||||||
public var preset:GamePreset;
|
|
||||||
|
|
||||||
@:provide static var builder:IPartBuilder;
|
public function new() {
|
||||||
|
super();
|
||||||
|
parts = [];
|
||||||
|
}
|
||||||
|
|
||||||
public function new() {
|
public function addChild(part:PartView):Void {
|
||||||
super();
|
if (part.parent != null) {
|
||||||
parts = [];
|
part.parent.removeChild(part);
|
||||||
|
}
|
||||||
|
parts.push(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function redraw():Void {
|
||||||
|
if (preset == null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addChild(part:PartView):Void {
|
var partWidth = preset.imageRect.width / preset.grid.x;
|
||||||
if (part.parent != null) {
|
var partHeight = preset.imageRect.height / preset.grid.y;
|
||||||
part.parent.removeChild(part);
|
|
||||||
}
|
|
||||||
parts.push(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function redraw():Void {
|
graphics.clear();
|
||||||
if (preset == null) {
|
graphics.lineStyle(2, 0xCCCCCC);
|
||||||
return;
|
graphics.beginFill(0x555555, 0.4);
|
||||||
}
|
graphics.drawRect(0, 0, preset.imageRect.width, preset.imageRect.height);
|
||||||
|
graphics.endFill();
|
||||||
|
graphics.lineStyle();
|
||||||
|
|
||||||
var partWidth = preset.imageRect.width / preset.grid.x;
|
for (partView in parts) {
|
||||||
var partHeight = preset.imageRect.height / preset.grid.y;
|
var part = partView.part;
|
||||||
|
|
||||||
graphics.clear();
|
if (partView.currentImage != null) {
|
||||||
graphics.lineStyle(2, 0xCCCCCC);
|
var image = partView.currentImage;
|
||||||
graphics.beginFill(0x555555, 0.4);
|
var m = new Matrix();
|
||||||
graphics.drawRect(0, 0, preset.imageRect.width, preset.imageRect.height);
|
m.translate(partView.x, partView.y);
|
||||||
|
graphics.beginBitmapFill(image, m, false, true);
|
||||||
|
graphics.drawRect(m.tx, m.ty, image.width, image.height);
|
||||||
graphics.endFill();
|
graphics.endFill();
|
||||||
|
}
|
||||||
|
|
||||||
|
var rect = cast(part.rect, RectangleExt).clone();
|
||||||
|
rect.x = part.point.x * part.rect.width;
|
||||||
|
rect.y = part.point.y * part.rect.height;
|
||||||
|
var path = 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();
|
graphics.lineStyle();
|
||||||
|
}
|
||||||
for (partView in parts) {
|
|
||||||
var part = partView.part;
|
|
||||||
|
|
||||||
if (partView.currentImage != null) {
|
|
||||||
var image = partView.currentImage;
|
|
||||||
var m = new Matrix();
|
|
||||||
m.translate(partView.x, partView.y);
|
|
||||||
graphics.beginBitmapFill(image, m, false, true);
|
|
||||||
graphics.drawRect(m.tx, m.ty, image.width, image.height);
|
|
||||||
graphics.endFill();
|
|
||||||
}
|
|
||||||
|
|
||||||
var rect = cast(part.rect, RectangleExt).clone();
|
|
||||||
rect.x = part.point.x * part.rect.width;
|
|
||||||
rect.y = part.point.y * part.rect.height;
|
|
||||||
var path = 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function clean():Void {
|
public function clean():Void {
|
||||||
preset = null;
|
preset = null;
|
||||||
parts = [];
|
parts = [];
|
||||||
graphics.clear();
|
graphics.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import ru.m.puzzlez.proto.event.GameAction;
|
|||||||
import ru.m.puzzlez.proto.event.GameEvent;
|
import ru.m.puzzlez.proto.event.GameEvent;
|
||||||
|
|
||||||
interface IRender extends IView<Dynamic> {
|
interface IRender extends IView<Dynamic> {
|
||||||
public var actions(default, null):Signal<GameAction>;
|
public var actions(default, null):Signal<GameAction>;
|
||||||
public var scale(get, set):Float;
|
public var scale(get, set):Float;
|
||||||
public var manager(default, null):RenderManager;
|
public var manager(default, null):RenderManager;
|
||||||
|
|
||||||
public function onGameEvent(event:GameEvent):Void;
|
public function onGameEvent(event:GameEvent):Void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,33 +8,32 @@ import ru.m.puzzlez.proto.game.Part;
|
|||||||
import ru.m.puzzlez.render.RenderUtil;
|
import ru.m.puzzlez.render.RenderUtil;
|
||||||
|
|
||||||
typedef Result = {
|
typedef Result = {
|
||||||
var part:Part;
|
var part:Part;
|
||||||
var image:PartImage;
|
var image:PartImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImagePartBuilder {
|
class ImagePartBuilder {
|
||||||
|
private var image:BitmapData;
|
||||||
|
|
||||||
private var image:BitmapData;
|
public function new(image:BitmapData) {
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
|
||||||
public function new(image:BitmapData) {
|
private function buildPart(index:Int, count:Int, parts:Array<Part>, stream:PublicStream<Result>):Void {
|
||||||
this.image = image;
|
for (i in index...index + count) {
|
||||||
|
if (i >= parts.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var part = parts[i];
|
||||||
|
var image = RenderUtil.cropImagePart(image, part);
|
||||||
|
stream.update({part: part, image: image});
|
||||||
}
|
}
|
||||||
|
Timer.delay(() -> buildPart(index + count, count, parts, stream), 0);
|
||||||
|
}
|
||||||
|
|
||||||
private function buildPart(index:Int, count:Int, parts:Array<Part>, stream:PublicStream<Result>):Void {
|
public function build(parts:Array<Part>):Stream<Result> {
|
||||||
for (i in index...index + count) {
|
var stream = new PublicStream<Result>();
|
||||||
if (i >= parts.length) {
|
Timer.delay(() -> buildPart(0, 5, parts, stream), 0);
|
||||||
return;
|
return stream;
|
||||||
}
|
}
|
||||||
var part = parts[i];
|
|
||||||
var image = RenderUtil.cropImagePart(image, part);
|
|
||||||
stream.update({part: part, image: image});
|
|
||||||
}
|
|
||||||
Timer.delay(() -> buildPart(index + count, count, parts, stream), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function build(parts:Array<Part>):Stream<Result> {
|
|
||||||
var stream = new PublicStream<Result>();
|
|
||||||
Timer.delay(() -> buildPart(0, 5, parts, stream), 0);
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,116 +12,114 @@ import ru.m.puzzlez.wrap.PointExt;
|
|||||||
import ru.m.puzzlez.wrap.RectangleExt;
|
import ru.m.puzzlez.wrap.RectangleExt;
|
||||||
|
|
||||||
class PartView extends Sprite {
|
class PartView extends Sprite {
|
||||||
|
public var id(default, null):Int;
|
||||||
|
public var part(default, null):Part;
|
||||||
|
public var position(default, set):Point;
|
||||||
|
public var completed(default, default):Bool;
|
||||||
|
public var target(default, null):Point;
|
||||||
|
|
||||||
public var id(default, null):Int;
|
private function set_position(value:Point):Point {
|
||||||
public var part(default, null):Part;
|
position = cast(value, PointExt).clone();
|
||||||
public var position(default, set):Point;
|
refresh();
|
||||||
public var completed(default, default):Bool;
|
return position;
|
||||||
public var target(default, null):Point;
|
}
|
||||||
|
|
||||||
private function set_position(value:Point):Point {
|
public var image(default, set):PartImage;
|
||||||
position = cast(value, PointExt).clone();
|
|
||||||
refresh();
|
private function set_image(value:PartImage):PartImage {
|
||||||
return position;
|
image = value;
|
||||||
|
redraw();
|
||||||
|
refresh();
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public var currentImage(get, never):BitmapData;
|
||||||
|
|
||||||
|
private function get_currentImage():BitmapData {
|
||||||
|
if (image == null) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
return completed ? image.borderedImage : image.shadedImage;
|
||||||
|
}
|
||||||
|
|
||||||
public var image(default, set):PartImage;
|
public var size(default, null):Point;
|
||||||
|
|
||||||
private function set_image(value:PartImage):PartImage {
|
public var playerId(default, set):Null<String>;
|
||||||
image = value;
|
|
||||||
redraw();
|
private function set_playerId(value:Null<String>):Null<String> {
|
||||||
refresh();
|
if (playerId != value) {
|
||||||
return image;
|
playerId = value;
|
||||||
|
redraw();
|
||||||
}
|
}
|
||||||
|
return playerId;
|
||||||
|
}
|
||||||
|
|
||||||
public var currentImage(get, never):BitmapData;
|
public function new(part:Part) {
|
||||||
|
super();
|
||||||
|
this.id = part.id;
|
||||||
|
this.part = part;
|
||||||
|
this.size = cast(part.rect, RectangleExt).size;
|
||||||
|
this.target = new Point().setX(part.point.x * size.x).setY(part.point.y * size.y);
|
||||||
|
}
|
||||||
|
|
||||||
private function get_currentImage():BitmapData {
|
private function redraw():Void {
|
||||||
if (image == null) {
|
throw "Unimplemented";
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
return completed ? image.borderedImage : image.shadedImage;
|
private function refresh():Void {
|
||||||
|
if (position != null && image != null) {
|
||||||
|
x = position.x - (image.borderedImage.width - size.x) / 2;
|
||||||
|
y = position.y - (image.borderedImage.height - size.y) / 2;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var size(default, null):Point;
|
public function complete():Void {
|
||||||
|
position = target;
|
||||||
|
playerId = null;
|
||||||
|
completed = true;
|
||||||
|
refresh();
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
public var playerId(default, set):Null<String>;
|
public static function factory(part:Part):PartView {
|
||||||
|
return new SpritePartView(part);
|
||||||
private function set_playerId(value:Null<String>):Null<String> {
|
}
|
||||||
if (playerId != value) {
|
|
||||||
playerId = value;
|
|
||||||
redraw();
|
|
||||||
}
|
|
||||||
return playerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function new(part:Part) {
|
|
||||||
super();
|
|
||||||
this.id = part.id;
|
|
||||||
this.part = part;
|
|
||||||
this.size = cast(part.rect, RectangleExt).size;
|
|
||||||
this.target = new Point().setX(part.point.x * size.x).setY(part.point.y * size.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function redraw():Void {
|
|
||||||
throw "Unimplemented";
|
|
||||||
}
|
|
||||||
|
|
||||||
private function refresh():Void {
|
|
||||||
if (position != null && image != null) {
|
|
||||||
x = position.x - (image.borderedImage.width - size.x) / 2;
|
|
||||||
y = position.y - (image.borderedImage.height - size.y) / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function complete():Void {
|
|
||||||
position = target;
|
|
||||||
playerId = null;
|
|
||||||
completed = true;
|
|
||||||
refresh();
|
|
||||||
redraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function factory(part:Part):PartView {
|
|
||||||
return new SpritePartView(part);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpritePartView extends PartView {
|
class SpritePartView extends PartView {
|
||||||
|
@:provide static var builder:IPartBuilder;
|
||||||
|
|
||||||
@:provide static var builder:IPartBuilder;
|
override private function redraw():Void {
|
||||||
|
if (image == null) {
|
||||||
override private function redraw():Void {
|
return;
|
||||||
if (image == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var image = currentImage;
|
|
||||||
graphics.clear();
|
|
||||||
graphics.beginBitmapFill(image, null, false, true);
|
|
||||||
graphics.drawRect(0, 0, image.width, image.height);
|
|
||||||
graphics.endFill();
|
|
||||||
|
|
||||||
if (playerId != null) {
|
|
||||||
var rect = cast(part.rect, RectangleExt).clone();
|
|
||||||
rect.x += (image.width - size.x) / 2;
|
|
||||||
rect.y += (image.height - size.y) / 2;
|
|
||||||
var path = builder.build(rect, part.bounds);
|
|
||||||
graphics.lineStyle(4, 0xffff00, 0.3);
|
|
||||||
path.draw(graphics);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
var image = currentImage;
|
||||||
|
graphics.clear();
|
||||||
|
graphics.beginBitmapFill(image, null, false, true);
|
||||||
|
graphics.drawRect(0, 0, image.width, image.height);
|
||||||
|
graphics.endFill();
|
||||||
|
|
||||||
|
if (playerId != null) {
|
||||||
|
var rect = cast(part.rect, RectangleExt).clone();
|
||||||
|
rect.x += (image.width - size.x) / 2;
|
||||||
|
rect.y += (image.height - size.y) / 2;
|
||||||
|
var path = builder.build(rect, part.bounds);
|
||||||
|
graphics.lineStyle(4, 0xffff00, 0.3);
|
||||||
|
path.draw(graphics);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BitmapPartView extends PartView {
|
class BitmapPartView extends PartView {
|
||||||
private var bitmap:Bitmap;
|
private var bitmap:Bitmap;
|
||||||
|
|
||||||
public function new(part) {
|
public function new(part) {
|
||||||
super(part);
|
super(part);
|
||||||
bitmap = new Bitmap(null, PixelSnapping.AUTO, true);
|
bitmap = new Bitmap(null, PixelSnapping.AUTO, true);
|
||||||
addChild(bitmap);
|
addChild(bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
override private function redraw():Void {
|
override private function redraw():Void {
|
||||||
bitmap.bitmapData = currentImage;
|
bitmap.bitmapData = currentImage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,18 @@ package ru.m.puzzlez.render;
|
|||||||
import hw.view.form.LabelView;
|
import hw.view.form.LabelView;
|
||||||
|
|
||||||
class ProgressView extends LabelView {
|
class ProgressView extends LabelView {
|
||||||
|
public function new() {
|
||||||
|
super();
|
||||||
|
text = 'Loading...';
|
||||||
|
}
|
||||||
|
|
||||||
public function new() {
|
public function setProgress(current:Int, total:Int):Void {
|
||||||
super();
|
text = 'Loading ${current}/${total}';
|
||||||
text = 'Loading...';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function setProgress(current:Int, total:Int):Void {
|
override private function set_text(value:String):String {
|
||||||
text = 'Loading ${current}/${total}';
|
var result = super.set_text(value);
|
||||||
}
|
update();
|
||||||
|
return result;
|
||||||
override private function set_text(value:String):String {
|
}
|
||||||
var result = super.set_text(value);
|
|
||||||
update();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,232 +22,217 @@ import ru.m.puzzlez.settings.Settings;
|
|||||||
import ru.m.puzzlez.wrap.PointExt;
|
import ru.m.puzzlez.wrap.PointExt;
|
||||||
|
|
||||||
class Render extends SpriteView implements IRender {
|
class Render extends SpriteView implements IRender {
|
||||||
|
public var actions(default, null):Signal<GameAction>;
|
||||||
|
public var scale(get, set):Float;
|
||||||
|
public var manager(default, null):RenderManager;
|
||||||
|
|
||||||
public var actions(default, null):Signal<GameAction>;
|
@:provide static var settings:Settings;
|
||||||
public var scale(get, set):Float;
|
|
||||||
public var manager(default, null):RenderManager;
|
|
||||||
|
|
||||||
@:provide static var settings:Settings;
|
private var playerId(get, never):String;
|
||||||
|
|
||||||
private var playerId(get, never):String;
|
private function get_playerId():String {
|
||||||
|
// ToDo: network user
|
||||||
|
return "local";
|
||||||
|
}
|
||||||
|
|
||||||
private function get_playerId():String {
|
private function get_scale():Float {
|
||||||
// ToDo: network user
|
return tableView.scaleX;
|
||||||
return "local";
|
}
|
||||||
|
|
||||||
|
private function set_scale(value:Float):Float {
|
||||||
|
var result = tableView.scaleX = tableView.scaleY = value;
|
||||||
|
tableView.x = (width - state.preset.tableRect.width * value) / 2;
|
||||||
|
tableView.y = (height - state.preset.tableRect.height * value) / 2;
|
||||||
|
// setSize(table.width, table.height, 'table');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private var state:GameState;
|
||||||
|
private var image:BitmapData;
|
||||||
|
|
||||||
|
private var progress:ProgressView;
|
||||||
|
private var container:Sprite;
|
||||||
|
private var tableView:Sprite;
|
||||||
|
private var imageView:CompleteView;
|
||||||
|
private var parts:Map<Int, PartView>;
|
||||||
|
private var activePart:PartView;
|
||||||
|
private var activePoint:PointExt;
|
||||||
|
|
||||||
|
private var movePoint:FlashPoint;
|
||||||
|
|
||||||
|
public function new() {
|
||||||
|
super();
|
||||||
|
container = new Sprite();
|
||||||
|
content.addChild(container);
|
||||||
|
manager = new RenderManager(content, container);
|
||||||
|
manager.locked = settings.locked;
|
||||||
|
progress = new ProgressView();
|
||||||
|
actions = new Signal();
|
||||||
|
tableView = new Sprite();
|
||||||
|
tableView.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||||
|
imageView = new CompleteView();
|
||||||
|
tableView.addChild(imageView);
|
||||||
|
container.addChild(tableView);
|
||||||
|
parts = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onGameEvent(event:GameEvent):Void {
|
||||||
|
if (event.hasStart()) {
|
||||||
|
onStart(event.start.state);
|
||||||
|
} else if (event.hasChange()) {
|
||||||
|
var part:PartView = parts[event.change.partId];
|
||||||
|
part.playerId = event.change.playerId;
|
||||||
|
part.position = event.change.position;
|
||||||
|
switch event.change.location {
|
||||||
|
case PartLocation.IMAGE:
|
||||||
|
part.complete();
|
||||||
|
imageView.addChild(part);
|
||||||
|
imageView.redraw();
|
||||||
|
case _:
|
||||||
|
}
|
||||||
|
} else if (event.hasComplete()) {
|
||||||
|
AlertView.alert("Complete!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onStart(state:GameState):Void {
|
||||||
|
clean();
|
||||||
|
this.state = state;
|
||||||
|
for (part in state.parts) {
|
||||||
|
var partView = PartView.factory(part);
|
||||||
|
parts.set(part.id, partView);
|
||||||
|
switch part.location {
|
||||||
|
case PartLocation.TABLE:
|
||||||
|
partView.position = part.position;
|
||||||
|
tableView.addChild(partView);
|
||||||
|
case PartLocation.IMAGE:
|
||||||
|
partView.complete();
|
||||||
|
imageView.addChild(partView);
|
||||||
|
case _:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_scale():Float {
|
imageView.x = state.preset.imageRect.x;
|
||||||
return tableView.scaleX;
|
imageView.y = state.preset.imageRect.y;
|
||||||
}
|
imageView.preset = state.preset;
|
||||||
|
imageView.redraw();
|
||||||
|
progress.text = "Loading image";
|
||||||
|
content.addChild(progress.content);
|
||||||
|
ImageData.fromImageId(state.preset.image).resolve().then(onImageResolved);
|
||||||
|
toUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
private function set_scale(value:Float):Float {
|
private function onImageResolved(image:BitmapData):Void {
|
||||||
var result = tableView.scaleX = tableView.scaleY = value;
|
this.image = RenderUtil.cropImage(image, state.preset.imageRect);
|
||||||
tableView.x = (width - state.preset.tableRect.width * value) / 2;
|
var builder = new ImagePartBuilder(this.image);
|
||||||
tableView.y = (height - state.preset.tableRect.height * value) / 2;
|
var i = 0;
|
||||||
//setSize(table.width, table.height, 'table');
|
builder.build(state.parts).then((result:Result) -> {
|
||||||
return result;
|
parts[result.part.id].image = result.image;
|
||||||
}
|
progress.setProgress(++i, state.parts.length);
|
||||||
|
if (i >= state.parts.length - 1) {
|
||||||
private var state:GameState;
|
if (progress.content.parent != null) {
|
||||||
private var image:BitmapData;
|
progress.content.parent.removeChild(progress.content);
|
||||||
|
|
||||||
private var progress:ProgressView;
|
|
||||||
private var container:Sprite;
|
|
||||||
private var tableView:Sprite;
|
|
||||||
private var imageView:CompleteView;
|
|
||||||
private var parts:Map<Int, PartView>;
|
|
||||||
private var activePart:PartView;
|
|
||||||
private var activePoint:PointExt;
|
|
||||||
|
|
||||||
private var movePoint:FlashPoint;
|
|
||||||
|
|
||||||
public function new() {
|
|
||||||
super();
|
|
||||||
container = new Sprite();
|
|
||||||
content.addChild(container);
|
|
||||||
manager = new RenderManager(content, container);
|
|
||||||
manager.locked = settings.locked;
|
|
||||||
progress = new ProgressView();
|
|
||||||
actions = new Signal();
|
|
||||||
tableView = new Sprite();
|
|
||||||
tableView.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
|
||||||
imageView = new CompleteView();
|
|
||||||
tableView.addChild(imageView);
|
|
||||||
container.addChild(tableView);
|
|
||||||
parts = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onGameEvent(event:GameEvent):Void {
|
|
||||||
if (event.hasStart()) {
|
|
||||||
onStart(event.start.state);
|
|
||||||
} else if (event.hasChange()) {
|
|
||||||
var part:PartView = parts[event.change.partId];
|
|
||||||
part.playerId = event.change.playerId;
|
|
||||||
part.position = event.change.position;
|
|
||||||
switch event.change.location {
|
|
||||||
case PartLocation.IMAGE:
|
|
||||||
part.complete();
|
|
||||||
imageView.addChild(part);
|
|
||||||
imageView.redraw();
|
|
||||||
case _:
|
|
||||||
}
|
|
||||||
} else if (event.hasComplete()) {
|
|
||||||
AlertView.alert("Complete!");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private function onStart(state:GameState):Void {
|
|
||||||
clean();
|
|
||||||
this.state = state;
|
|
||||||
for (part in state.parts) {
|
|
||||||
var partView = PartView.factory(part);
|
|
||||||
parts.set(part.id, partView);
|
|
||||||
switch part.location {
|
|
||||||
case PartLocation.TABLE:
|
|
||||||
partView.position = part.position;
|
|
||||||
tableView.addChild(partView);
|
|
||||||
case PartLocation.IMAGE:
|
|
||||||
partView.complete();
|
|
||||||
imageView.addChild(partView);
|
|
||||||
case _:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
imageView.x = state.preset.imageRect.x;
|
|
||||||
imageView.y = state.preset.imageRect.y;
|
|
||||||
imageView.preset = state.preset;
|
|
||||||
imageView.redraw();
|
imageView.redraw();
|
||||||
progress.text = "Loading image";
|
}
|
||||||
content.addChild(progress.content);
|
});
|
||||||
ImageData.fromImageId(state.preset.image).resolve().then(onImageResolved);
|
}
|
||||||
toUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function onImageResolved(image:BitmapData):Void {
|
override public function update():Void {
|
||||||
this.image = RenderUtil.cropImage(image, state.preset.imageRect);
|
super.update();
|
||||||
var builder = new ImagePartBuilder(this.image);
|
progress.x = (width - progress.width) / 2;
|
||||||
var i = 0;
|
progress.y = (height - progress.height) / 2;
|
||||||
builder.build(state.parts).then((result:Result) -> {
|
progress.update();
|
||||||
parts[result.part.id].image = result.image;
|
if (state != null) {
|
||||||
progress.setProgress(++i, state.parts.length);
|
scale = Math.min(width / state.preset.tableRect.width, height / state.preset.tableRect.height);
|
||||||
if (i >= state.parts.length - 1) {
|
}
|
||||||
if (progress.content.parent != null) {
|
}
|
||||||
progress.content.parent.removeChild(progress.content);
|
|
||||||
}
|
override public function redraw():Void {
|
||||||
imageView.redraw();
|
switch settings.background {
|
||||||
}
|
case Background.NONE:
|
||||||
|
super.redraw();
|
||||||
|
case Background.COLOR(color):
|
||||||
|
content.graphics.clear();
|
||||||
|
content.graphics.beginFill(color);
|
||||||
|
content.graphics.drawRect(0, 0, width, height);
|
||||||
|
content.graphics.endFill();
|
||||||
|
case Background.IMAGE(id):
|
||||||
|
ImageData.fromImageId(id).resolve().then(result -> {
|
||||||
|
content.graphics.clear();
|
||||||
|
content.graphics.beginBitmapFill(result);
|
||||||
|
content.graphics.drawRect(0, 0, width, height);
|
||||||
|
content.graphics.endFill();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override public function update():Void {
|
private function onMouseDown(event:MouseEvent):Void {
|
||||||
super.update();
|
var point:FlashPoint = new FlashPoint(event.stageX, event.stageY);
|
||||||
progress.x = (width - progress.width) / 2;
|
var objects = tableView.getObjectsUnderPoint(point);
|
||||||
progress.y = (height - progress.height) / 2;
|
objects.reverse();
|
||||||
progress.update();
|
var pointPart:PartView = null;
|
||||||
if (state != null) {
|
for (object in objects) {
|
||||||
scale = Math.min(width / state.preset.tableRect.width, height / state.preset.tableRect.height);
|
if (Std.is(object, PartView)) {
|
||||||
|
var part:PartView = cast object;
|
||||||
|
var partPoint = part.globalToLocal(point);
|
||||||
|
var color = part.image.shadedImage.getPixel(Std.int(partPoint.x), Std.int(partPoint.y));
|
||||||
|
if (color > 0) {
|
||||||
|
pointPart = part;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (pointPart != null) {
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
if (event.ctrlKey) {
|
||||||
|
save(pointPart);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pointPart.completed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activePart = pointPart;
|
||||||
|
tableView.setChildIndex(activePart, tableView.numChildren - 1);
|
||||||
|
activePoint = tableView.globalToLocal(point);
|
||||||
|
tableView.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
||||||
|
tableView.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||||
|
actions.emit(new GameAction().setAction(Action.TAKE).setPlayerId(playerId).setPartId(activePart.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override public function redraw():Void {
|
private function onMouseMove(event:MouseEvent):Void {
|
||||||
switch settings.background {
|
var newPoint:PointExt = tableView.globalToLocal(new FlashPoint(event.stageX, event.stageY));
|
||||||
case Background.NONE:
|
var partPosition = cast(activePart.position, PointExt).add(newPoint).subtract(activePoint);
|
||||||
super.redraw();
|
actions.emit(new GameAction().setAction(Action.MOVE).setPlayerId(playerId).setPartId(activePart.id).setPosition(partPosition));
|
||||||
case Background.COLOR(color):
|
activePoint = newPoint;
|
||||||
content.graphics.clear();
|
}
|
||||||
content.graphics.beginFill(color);
|
|
||||||
content.graphics.drawRect(0, 0, width, height);
|
|
||||||
content.graphics.endFill();
|
|
||||||
case Background.IMAGE(id):
|
|
||||||
ImageData.fromImageId(id).resolve().then(result -> {
|
|
||||||
content.graphics.clear();
|
|
||||||
content.graphics.beginBitmapFill(result);
|
|
||||||
content.graphics.drawRect(0, 0, width, height);
|
|
||||||
content.graphics.endFill();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function onMouseDown(event:MouseEvent):Void {
|
private function onMouseUp(event:MouseEvent):Void {
|
||||||
var point:FlashPoint = new FlashPoint(event.stageX, event.stageY);
|
var newPoint:PointExt = tableView.globalToLocal(new FlashPoint(event.stageX, event.stageY));
|
||||||
var objects = tableView.getObjectsUnderPoint(point);
|
var partPosition = cast(activePart.position, PointExt).add(newPoint).subtract(activePoint);
|
||||||
objects.reverse();
|
actions.emit(new GameAction().setAction(Action.PUT).setPlayerId(playerId).setPartId(activePart.id).setPosition(partPosition));
|
||||||
var pointPart:PartView = null;
|
tableView.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
||||||
for (object in objects) {
|
tableView.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||||
if (Std.is(object, PartView)) {
|
activePart = null;
|
||||||
var part:PartView = cast object;
|
activePoint = null;
|
||||||
var partPoint = part.globalToLocal(point);
|
}
|
||||||
var color = part.image.shadedImage.getPixel(Std.int(partPoint.x), Std.int(partPoint.y));
|
|
||||||
if (color > 0) {
|
|
||||||
pointPart = part;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pointPart != null) {
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
if (event.ctrlKey) {
|
|
||||||
save(pointPart);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (pointPart.completed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
activePart = pointPart;
|
|
||||||
tableView.setChildIndex(activePart, tableView.numChildren - 1);
|
|
||||||
activePoint = tableView.globalToLocal(point);
|
|
||||||
tableView.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
|
||||||
tableView.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
|
||||||
actions.emit(new GameAction()
|
|
||||||
.setAction(Action.TAKE)
|
|
||||||
.setPlayerId(playerId)
|
|
||||||
.setPartId(activePart.id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function onMouseMove(event:MouseEvent):Void {
|
private function save(part:PartView):Void {
|
||||||
var newPoint:PointExt = tableView.globalToLocal(new FlashPoint(event.stageX, event.stageY));
|
var file = new FileReference();
|
||||||
var partPosition = cast(activePart.position, PointExt).add(newPoint).subtract(activePoint);
|
var image = part.image.shadedImage;
|
||||||
actions.emit(new GameAction()
|
var data = new ByteArray();
|
||||||
.setAction(Action.MOVE)
|
image.encode(new Rectangle(0, 0, image.width, image.height), new PNGEncoderOptions(), data);
|
||||||
.setPlayerId(playerId)
|
file.save(data, "icon.png");
|
||||||
.setPartId(activePart.id)
|
}
|
||||||
.setPosition(partPosition)
|
|
||||||
);
|
|
||||||
activePoint = newPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function onMouseUp(event:MouseEvent):Void {
|
public function clean() {
|
||||||
var newPoint:PointExt = tableView.globalToLocal(new FlashPoint(event.stageX, event.stageY));
|
for (partView in parts) {
|
||||||
var partPosition = cast(activePart.position, PointExt).add(newPoint).subtract(activePoint);
|
if (partView.parent != null) {
|
||||||
actions.emit(new GameAction()
|
partView.parent.removeChild(partView);
|
||||||
.setAction(Action.PUT)
|
}
|
||||||
.setPlayerId(playerId)
|
|
||||||
.setPartId(activePart.id)
|
|
||||||
.setPosition(partPosition)
|
|
||||||
);
|
|
||||||
tableView.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
|
||||||
tableView.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
|
||||||
activePart = null;
|
|
||||||
activePoint = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function save(part:PartView):Void {
|
|
||||||
var file = new FileReference();
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clean() {
|
|
||||||
for (partView in parts) {
|
|
||||||
if (partView.parent != null) {
|
|
||||||
partView.parent.removeChild(partView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parts = new Map();
|
|
||||||
imageView.clean();
|
|
||||||
}
|
}
|
||||||
|
parts = new Map();
|
||||||
|
imageView.clean();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,62 +7,61 @@ import flash.events.MouseEvent;
|
|||||||
import flash.geom.Point;
|
import flash.geom.Point;
|
||||||
|
|
||||||
class RenderManager {
|
class RenderManager {
|
||||||
|
public var locked(default, default):Bool;
|
||||||
|
|
||||||
public var locked(default, default):Bool;
|
private var content:DisplayObject;
|
||||||
|
private var container:DisplayObject;
|
||||||
|
private var movePoint:Point;
|
||||||
|
private var gesture:GestureManager;
|
||||||
|
|
||||||
private var content:DisplayObject;
|
public function new(content:DisplayObject, container:DisplayObject) {
|
||||||
private var container:DisplayObject;
|
this.content = content;
|
||||||
private var movePoint:Point;
|
this.container = container;
|
||||||
private var gesture:GestureManager;
|
content.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||||
|
content.addEventListener(ZoomGestureEvent.GESTURE_ZOOM, onGestureZoom);
|
||||||
|
gesture = new GestureManager(content);
|
||||||
|
}
|
||||||
|
|
||||||
public function new(content:DisplayObject, container:DisplayObject) {
|
private function onGestureZoom(event:ZoomGestureEvent):Void {
|
||||||
this.content = content;
|
if (locked) {
|
||||||
this.container = container;
|
return;
|
||||||
content.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
|
||||||
content.addEventListener(ZoomGestureEvent.GESTURE_ZOOM, onGestureZoom);
|
|
||||||
gesture = new GestureManager(content);
|
|
||||||
}
|
}
|
||||||
|
container.scaleX = container.scaleY += event.zoom;
|
||||||
|
}
|
||||||
|
|
||||||
private function onGestureZoom(event:ZoomGestureEvent):Void {
|
private function onMouseDown(event:MouseEvent):Void {
|
||||||
if (locked) {
|
if (locked) {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
container.scaleX = container.scaleY += event.zoom;
|
|
||||||
}
|
}
|
||||||
|
movePoint = new Point(event.stageX, event.stageY);
|
||||||
|
content.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
||||||
|
content.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
private function onMouseDown(event:MouseEvent):Void {
|
private function onMouseMove(event:MouseEvent):Void {
|
||||||
if (locked) {
|
var newPoint = new Point(event.stageX, event.stageY);
|
||||||
return;
|
var diff = newPoint.subtract(movePoint);
|
||||||
}
|
container.x += diff.x;
|
||||||
movePoint = new Point(event.stageX, event.stageY);
|
container.y += diff.y;
|
||||||
content.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
movePoint = newPoint;
|
||||||
content.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private function onMouseMove(event:MouseEvent):Void {
|
private function onMouseUp(event:MouseEvent):Void {
|
||||||
var newPoint = new Point(event.stageX, event.stageY);
|
content.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
||||||
var diff = newPoint.subtract(movePoint);
|
content.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||||
container.x += diff.x;
|
movePoint = null;
|
||||||
container.y += diff.y;
|
}
|
||||||
movePoint = newPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function onMouseUp(event:MouseEvent):Void {
|
public function reset():Void {
|
||||||
content.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
container.x = 0;
|
||||||
content.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
container.y = 0;
|
||||||
movePoint = null;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function reset():Void {
|
public function dispose():Void {
|
||||||
container.x = 0;
|
if (gesture != null) {
|
||||||
container.y = 0;
|
gesture.dispose();
|
||||||
}
|
gesture = null;
|
||||||
|
|
||||||
public function dispose():Void {
|
|
||||||
if (gesture != null) {
|
|
||||||
gesture.dispose();
|
|
||||||
gesture = null;
|
|
||||||
}
|
|
||||||
content.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
|
||||||
}
|
}
|
||||||
|
content.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,115 +13,113 @@ import ru.m.puzzlez.render.part.PartMask;
|
|||||||
import ru.m.puzzlez.wrap.RectangleExt;
|
import ru.m.puzzlez.wrap.RectangleExt;
|
||||||
|
|
||||||
typedef DrawSetting = {
|
typedef DrawSetting = {
|
||||||
var offset:Point;
|
var offset:Point;
|
||||||
var color:Color;
|
var color:Color;
|
||||||
var opacity:Float;
|
var opacity:Float;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef PartImage = {
|
typedef PartImage = {
|
||||||
var borderedImage:BitmapData;
|
var borderedImage:BitmapData;
|
||||||
var shadedImage:BitmapData;
|
var shadedImage:BitmapData;
|
||||||
}
|
}
|
||||||
|
|
||||||
class RenderUtil {
|
class RenderUtil {
|
||||||
public static var shadowSettings(default, null):Array<DrawSetting> = [
|
public static var shadowSettings(default, null):Array<DrawSetting> = [
|
||||||
{offset: new Point().setX(-2).setY(-2), color: 0x000000, opacity: 0.4},
|
{offset: new Point().setX(-2).setY(-2), color: 0x000000, opacity: 0.4},
|
||||||
{offset: new Point().setX(2).setY(2), color: 0xffffff, opacity: 0.4},
|
{offset: new Point().setX(2).setY(2), color: 0xffffff, opacity: 0.4},
|
||||||
];
|
];
|
||||||
|
|
||||||
public static var borderSettings(default, null):Array<DrawSetting> = [
|
public static var borderSettings(default, null):Array<DrawSetting> = [
|
||||||
{offset: new Point().setX(-1).setY(-1), color: 0x555555, opacity: 0.4},
|
{offset: new Point().setX(-1).setY(-1), color: 0x555555, opacity: 0.4},
|
||||||
{offset: new Point().setX(1).setY(1), color: 0xcccccc, opacity: 0.4},
|
{offset: new Point().setX(1).setY(1), color: 0xcccccc, opacity: 0.4},
|
||||||
];
|
];
|
||||||
|
|
||||||
@:provide static var builder:IPartBuilder;
|
@:provide static var builder:IPartBuilder;
|
||||||
|
|
||||||
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));
|
||||||
var matrix = new Matrix();
|
var matrix = new Matrix();
|
||||||
var scale = Math.max(rect.width / source.width, rect.height / source.height);
|
var scale = Math.max(rect.width / source.width, rect.height / source.height);
|
||||||
matrix.scale(scale, scale);
|
matrix.scale(scale, scale);
|
||||||
matrix.translate((rect.width - source.width * scale) / 2, (rect.height - source.height * scale) / 2);
|
matrix.translate((rect.width - source.width * scale) / 2, (rect.height - source.height * scale) / 2);
|
||||||
image.draw(source, matrix);
|
image.draw(source, matrix);
|
||||||
return image;
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function buildPartGeometry(part:Part):{rect:Rectangle, drawRect:Rectangle} {
|
||||||
|
var source = new Rectangle().setX(part.rect.width * part.point.x)
|
||||||
|
.setY(part.rect.height * part.point.y)
|
||||||
|
.setWidth(part.rect.width)
|
||||||
|
.setHeight(part.rect.height);
|
||||||
|
var rect = cast(source, RectangleExt).clone();
|
||||||
|
var offset = rect.width / 4 + rect.width * 0.05;
|
||||||
|
rect.x -= offset;
|
||||||
|
rect.y -= offset;
|
||||||
|
rect.width += offset * 2;
|
||||||
|
rect.height += offset * 2;
|
||||||
|
var drawRect = cast(source, RectangleExt).clone();
|
||||||
|
drawRect.x = offset;
|
||||||
|
drawRect.y = offset;
|
||||||
|
return {rect: rect, drawRect: drawRect};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function drawShadow(source:BitmapData, path:DrawPath, values:Array<DrawSetting>):BitmapData {
|
||||||
|
var canvas = new Shape();
|
||||||
|
canvas.cacheAsBitmap = true;
|
||||||
|
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();
|
||||||
|
var image = new BitmapData(source.width, source.height, true, 0x00000000);
|
||||||
|
image.draw(canvas, null, null, null, null, true);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
private static function buildPartGeometry(part:Part):{rect:Rectangle, drawRect:Rectangle} {
|
private static function drawBorder(source:BitmapData, path:DrawPath, values:Array<DrawSetting>):BitmapData {
|
||||||
var source = new Rectangle()
|
var canvas = new Shape();
|
||||||
.setX(part.rect.width * part.point.x)
|
canvas.cacheAsBitmap = true;
|
||||||
.setY(part.rect.height * part.point.y)
|
for (value in values) {
|
||||||
.setWidth(part.rect.width)
|
canvas.graphics.lineStyle(2, value.color, value.opacity);
|
||||||
.setHeight(part.rect.height);
|
path.move(value.offset.x, value.offset.y).draw(canvas.graphics);
|
||||||
var rect = cast(source, RectangleExt).clone();
|
canvas.graphics.lineStyle();
|
||||||
var offset = rect.width / 4 + rect.width * 0.05;
|
|
||||||
rect.x -= offset;
|
|
||||||
rect.y -= offset;
|
|
||||||
rect.width += offset * 2;
|
|
||||||
rect.height += offset * 2;
|
|
||||||
var drawRect = cast(source, RectangleExt).clone();
|
|
||||||
drawRect.x = offset;
|
|
||||||
drawRect.y = offset ;
|
|
||||||
return {rect:rect, drawRect:drawRect};
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
private static function drawShadow(source:BitmapData, path:DrawPath, values:Array<DrawSetting>):BitmapData {
|
public static function cropImagePart(source:BitmapData, part:Part, completed:Bool = false):PartImage {
|
||||||
var canvas = new Shape();
|
var geometry = buildPartGeometry(part);
|
||||||
canvas.cacheAsBitmap = true;
|
var path = builder.build(geometry.drawRect, part.bounds);
|
||||||
for (value in values) {
|
var canvas:Shape = new Shape();
|
||||||
canvas.graphics.beginFill(value.color, value.opacity);
|
canvas.cacheAsBitmap = true;
|
||||||
path.move(value.offset.x, value.offset.y).draw(canvas.graphics);
|
canvas.mask = new PartMask(path);
|
||||||
canvas.graphics.endFill();
|
var matrix = new Matrix();
|
||||||
}
|
matrix.translate(-geometry.rect.x, -geometry.rect.y);
|
||||||
canvas.graphics.beginBitmapFill(source, null, false, true);
|
canvas.graphics.beginBitmapFill(source, matrix, false, true);
|
||||||
canvas.graphics.drawRect(0, 0, source.width, source.height);
|
canvas.graphics.drawRect(0, 0, geometry.rect.width, geometry.rect.height);
|
||||||
canvas.graphics.endFill();
|
canvas.graphics.endFill();
|
||||||
var image = new BitmapData(source.width, source.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 image;
|
var shadedImage = drawShadow(image, path, shadowSettings);
|
||||||
}
|
// var borderedImage = drawBorder(image, path, borderSettings);
|
||||||
|
return {borderedImage: image, shadedImage: shadedImage};
|
||||||
|
}
|
||||||
|
|
||||||
private static function drawBorder(source:BitmapData, path:DrawPath, values:Array<DrawSetting>):BitmapData {
|
public static function containRectangle(source:Rectangle, target:Rectangle):Rectangle {
|
||||||
var canvas = new Shape();
|
var s = Math.min(1, Math.min(target.width / source.width, target.height / source.height));
|
||||||
canvas.cacheAsBitmap = true;
|
var width = source.width * s;
|
||||||
for (value in values) {
|
var height = source.height * s;
|
||||||
canvas.graphics.lineStyle(2, value.color, value.opacity);
|
return new Rectangle().setX((target.width - width) / 2)
|
||||||
path.move(value.offset.x, value.offset.y).draw(canvas.graphics);
|
.setY((target.height - height) / 2)
|
||||||
canvas.graphics.lineStyle();
|
.setWidth(width)
|
||||||
}
|
.setHeight(height);
|
||||||
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();
|
|
||||||
canvas.cacheAsBitmap = true;
|
|
||||||
canvas.mask = new PartMask(path);
|
|
||||||
var matrix = new Matrix();
|
|
||||||
matrix.translate(-geometry.rect.x, -geometry.rect.y);
|
|
||||||
canvas.graphics.beginBitmapFill(source, matrix, false, true);
|
|
||||||
canvas.graphics.drawRect(0, 0, geometry.rect.width, geometry.rect.height);
|
|
||||||
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);
|
|
||||||
var shadedImage = drawShadow(image, path, shadowSettings);
|
|
||||||
//var borderedImage = drawBorder(image, path, borderSettings);
|
|
||||||
return {borderedImage: image, shadedImage: shadedImage};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function containRectangle(source:Rectangle, target:Rectangle):Rectangle {
|
|
||||||
var s = Math.min(1, Math.min(target.width / source.width, target.height / source.height));
|
|
||||||
var width = source.width * s;
|
|
||||||
var height = source.height * s;
|
|
||||||
return new Rectangle()
|
|
||||||
.setX((target.width - width) / 2)
|
|
||||||
.setY((target.height - height) / 2)
|
|
||||||
.setWidth(width)
|
|
||||||
.setHeight(height);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,28 +8,25 @@ import ru.m.puzzlez.proto.game.PartBounds;
|
|||||||
import ru.m.puzzlez.render.part.IPartBuilder;
|
import ru.m.puzzlez.render.part.IPartBuilder;
|
||||||
|
|
||||||
class BasePartBuilder implements IPartBuilder {
|
class BasePartBuilder implements IPartBuilder {
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
public function new() {
|
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 createAngle(path:DrawPath, x:Float, y:Float, first: Bool = false):Void {
|
private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, bound:PartBound):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 {
|
public function build(rect:RectangleExt, bounds:PartBounds):DrawPath {
|
||||||
}
|
var path = new DrawPath();
|
||||||
|
createAngle(path, rect.left, rect.top, true);
|
||||||
public function build(rect:RectangleExt, bounds:PartBounds):DrawPath {
|
createBound(path, rect.left, rect.top, rect.right, rect.top, bounds.top);
|
||||||
var path = new DrawPath();
|
createAngle(path, rect.right, rect.top);
|
||||||
createAngle(path, rect.left, rect.top, true);
|
createBound(path, rect.right, rect.top, rect.right, rect.bottom, bounds.right);
|
||||||
createBound(path, rect.left, rect.top, rect.right, rect.top, bounds.top);
|
createAngle(path, rect.right, rect.bottom);
|
||||||
createAngle(path, rect.right, rect.top);
|
createBound(path, rect.right, rect.bottom, rect.left, rect.bottom, bounds.bottom);
|
||||||
createBound(path, rect.right, rect.top, rect.right, rect.bottom, bounds.right);
|
createAngle(path, rect.left, rect.bottom);
|
||||||
createAngle(path, rect.right, rect.bottom);
|
createBound(path, rect.left, rect.bottom, rect.left, rect.top, bounds.left);
|
||||||
createBound(path, rect.right, rect.bottom, rect.left, rect.bottom, bounds.bottom);
|
createAngle(path, rect.left, rect.top);
|
||||||
createAngle(path, rect.left, rect.bottom);
|
return path;
|
||||||
createBound(path, rect.left, rect.bottom, rect.left, rect.top, bounds.left);
|
}
|
||||||
createAngle(path, rect.left, rect.top);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,42 +5,55 @@ import ru.m.puzzlez.proto.game.BoundType;
|
|||||||
import ru.m.puzzlez.proto.game.PartBound;
|
import ru.m.puzzlez.proto.game.PartBound;
|
||||||
|
|
||||||
class ClassicPartBuilder extends BasePartBuilder {
|
class ClassicPartBuilder extends BasePartBuilder {
|
||||||
|
override private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, bound:PartBound):Void {
|
||||||
override private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, bound:PartBound):Void {
|
var dx = toX == fromX ? 0 : (toX - fromX) / Math.abs(toX - fromX);
|
||||||
var dx = toX == fromX ? 0 : (toX - fromX) / Math.abs(toX - fromX);
|
var dy = toY == fromY ? 0 : (toY - fromY) / Math.abs(toY - fromY);
|
||||||
var dy = toY == fromY ? 0 : (toY - fromY) / Math.abs(toY - fromY);
|
var boundLength = Math.max(Math.abs(toX - fromX), Math.abs(toY - fromY));
|
||||||
var boundLength = Math.max(Math.abs(toX - fromX), Math.abs(toY - fromY));
|
var spikeWidth = boundLength * 0.2;
|
||||||
var spikeWidth = boundLength * 0.2;
|
var spikeOffset = (boundLength - spikeWidth) / 2;
|
||||||
var spikeOffset = (boundLength - spikeWidth) / 2;
|
var spikeDepth = switch bound.spike {
|
||||||
var spikeDepth = switch bound.spike {
|
case BoundType.IN: spikeWidth;
|
||||||
case BoundType.IN: spikeWidth;
|
case BoundType.OUT: -spikeWidth;
|
||||||
case BoundType.OUT: -spikeWidth;
|
case _: 0;
|
||||||
case _: 0;
|
};
|
||||||
};
|
var spikeSideOffset = switch bound.side {
|
||||||
var spikeSideOffset = switch bound.side {
|
case BoundType.IN: boundLength * -0.04;
|
||||||
case BoundType.IN: boundLength * -0.04;
|
case BoundType.OUT: boundLength * 0.04;
|
||||||
case BoundType.OUT: boundLength * 0.04;
|
case _: 0;
|
||||||
case _: 0;
|
|
||||||
}
|
|
||||||
switch bound.spike {
|
|
||||||
case BoundType.IN | BoundType.OUT:
|
|
||||||
path.commands.push(LINE_TO(
|
|
||||||
fromX + spikeOffset * dx + spikeSideOffset * dy,
|
|
||||||
fromY + spikeOffset * dy + spikeSideOffset * dx
|
|
||||||
));
|
|
||||||
path.commands.push(CURVE_TO(
|
|
||||||
fromX + (spikeOffset * 0.7) * dx - spikeDepth * dy + spikeSideOffset * dy,
|
|
||||||
fromY + spikeDepth * dx + (spikeOffset * 0.7) * dy + spikeSideOffset * dx,
|
|
||||||
fromX + (spikeOffset + spikeWidth / 2) * dx - (spikeDepth * 1.1) * dy + spikeSideOffset * dy,
|
|
||||||
fromY + (spikeDepth * 1.1) * dx + (spikeOffset + spikeWidth / 2) * dy + spikeSideOffset * dx
|
|
||||||
));
|
|
||||||
path.commands.push(CURVE_TO(
|
|
||||||
fromX + (spikeOffset + spikeWidth + spikeOffset * 0.3) * dx - spikeDepth * dy + spikeSideOffset * dy,
|
|
||||||
fromY + spikeDepth * dx + (spikeOffset + spikeWidth + spikeOffset * 0.3) * dy + spikeSideOffset * dx,
|
|
||||||
fromX + (spikeOffset + spikeWidth) * dx + spikeSideOffset * dy,
|
|
||||||
fromY + (spikeOffset + spikeWidth) * dy + spikeSideOffset * dx
|
|
||||||
));
|
|
||||||
case _:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
switch bound.spike {
|
||||||
|
case BoundType.IN | BoundType.OUT:
|
||||||
|
path.commands.push(LINE_TO(fromX + spikeOffset * dx + spikeSideOffset * dy, fromY + spikeOffset * dy + spikeSideOffset * dx));
|
||||||
|
path.commands.push(CURVE_TO(fromX
|
||||||
|
+ (spikeOffset * 0.7) * dx
|
||||||
|
- spikeDepth * dy
|
||||||
|
+ spikeSideOffset * dy,
|
||||||
|
fromY
|
||||||
|
+ spikeDepth * dx
|
||||||
|
+ (spikeOffset * 0.7) * dy
|
||||||
|
+ spikeSideOffset * dx,
|
||||||
|
fromX
|
||||||
|
+ (spikeOffset + spikeWidth / 2) * dx
|
||||||
|
- (spikeDepth * 1.1) * dy
|
||||||
|
+ spikeSideOffset * dy,
|
||||||
|
fromY
|
||||||
|
+ (spikeDepth * 1.1) * dx
|
||||||
|
+ (spikeOffset + spikeWidth / 2) * dy
|
||||||
|
+ spikeSideOffset * dx));
|
||||||
|
path.commands.push(CURVE_TO(fromX
|
||||||
|
+ (spikeOffset + spikeWidth + spikeOffset * 0.3) * dx
|
||||||
|
- spikeDepth * dy
|
||||||
|
+ spikeSideOffset * dy,
|
||||||
|
fromY
|
||||||
|
+ spikeDepth * dx
|
||||||
|
+ (spikeOffset + spikeWidth + spikeOffset * 0.3) * dy
|
||||||
|
+ spikeSideOffset * dx,
|
||||||
|
fromX
|
||||||
|
+ (spikeOffset + spikeWidth) * dx
|
||||||
|
+ spikeSideOffset * dy, fromY
|
||||||
|
+ (spikeOffset + spikeWidth) * dy
|
||||||
|
+ spikeSideOffset * dx));
|
||||||
|
case _:
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ import ru.m.puzzlez.proto.game.PartBounds;
|
|||||||
import ru.m.puzzlez.wrap.RectangleExt;
|
import ru.m.puzzlez.wrap.RectangleExt;
|
||||||
|
|
||||||
@:provide(ClassicPartBuilder) interface IPartBuilder {
|
@:provide(ClassicPartBuilder) interface IPartBuilder {
|
||||||
public function build(rect:RectangleExt, bound:PartBounds):DrawPath;
|
public function build(rect:RectangleExt, bound:PartBounds):DrawPath;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ import flash.display.Shape;
|
|||||||
import ru.m.draw.DrawPath;
|
import ru.m.draw.DrawPath;
|
||||||
|
|
||||||
class PartMask extends Shape {
|
class PartMask extends Shape {
|
||||||
|
public function new(path:DrawPath) {
|
||||||
public function new(path:DrawPath) {
|
super();
|
||||||
super();
|
graphics.beginFill(0xff00ff, 1);
|
||||||
graphics.beginFill(0xff00ff, 1);
|
path.draw(graphics);
|
||||||
path.draw(graphics);
|
graphics.endFill();
|
||||||
graphics.endFill();
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,37 +5,24 @@ import ru.m.puzzlez.proto.game.BoundType;
|
|||||||
import ru.m.puzzlez.proto.game.PartBound;
|
import ru.m.puzzlez.proto.game.PartBound;
|
||||||
|
|
||||||
class SquarePartBuilder extends BasePartBuilder {
|
class SquarePartBuilder extends BasePartBuilder {
|
||||||
|
override private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, bound:PartBound):Void {
|
||||||
override private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, bound:PartBound):Void {
|
var dx = toX == fromX ? 0 : (toX - fromX) / Math.abs(toX - fromX);
|
||||||
var dx = toX == fromX ? 0 : (toX - fromX) / Math.abs(toX - fromX);
|
var dy = toY == fromY ? 0 : (toY - fromY) / Math.abs(toY - fromY);
|
||||||
var dy = toY == fromY ? 0 : (toY - fromY) / Math.abs(toY - fromY);
|
var boundLength = Math.max(Math.abs(toX - fromX), Math.abs(toY - fromY));
|
||||||
var boundLength = Math.max(Math.abs(toX - fromX), Math.abs(toY - fromY));
|
var spikeWidth = boundLength * 0.2;
|
||||||
var spikeWidth = boundLength * 0.2;
|
var spikeOffset = (boundLength - spikeWidth) / 2;
|
||||||
var spikeOffset = (boundLength - spikeWidth) / 2;
|
var spikeDepth = switch bound.spike {
|
||||||
var spikeDepth = switch bound.spike {
|
case BoundType.NONE: 0;
|
||||||
case BoundType.NONE: 0;
|
case BoundType.IN: spikeWidth;
|
||||||
case BoundType.IN: spikeWidth;
|
case BoundType.OUT: -spikeWidth;
|
||||||
case BoundType.OUT: -spikeWidth;
|
|
||||||
}
|
|
||||||
switch bound.spike {
|
|
||||||
case BoundType.NONE:
|
|
||||||
case BoundType.IN | BoundType.OUT:
|
|
||||||
path.commands.push(LINE_TO(
|
|
||||||
fromX + spikeOffset * dx,
|
|
||||||
fromY + spikeOffset * dy
|
|
||||||
))
|
|
||||||
path.commands.push(LINE_TO(
|
|
||||||
fromX + spikeOffset * dx - spikeDepth * dy,
|
|
||||||
fromY + spikeDepth * dx + spikeOffset * dy
|
|
||||||
));
|
|
||||||
path.commands.push(LINE_TO(
|
|
||||||
fromX + (spikeOffset + spikeWidth) * dx - spikeDepth * dy,
|
|
||||||
fromY + spikeDepth * dx + (spikeOffset + spikeWidth) * dy
|
|
||||||
));
|
|
||||||
path.commands.push(LINE_TO(
|
|
||||||
fromX + (spikeOffset + spikeWidth) * dx,
|
|
||||||
fromY + (spikeOffset + spikeWidth) * dy
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
switch bound.spike {
|
||||||
|
case BoundType.NONE:
|
||||||
|
case BoundType.IN | BoundType.OUT:
|
||||||
|
path.commands.push(LINE_TO(fromX + spikeOffset * dx,
|
||||||
|
fromY + spikeOffset * dy)) path.commands.push(LINE_TO(fromX + spikeOffset * dx - spikeDepth * dy, fromY + spikeDepth * dx + spikeOffset * dy));
|
||||||
|
path.commands.push(LINE_TO(fromX + (spikeOffset + spikeWidth) * dx - spikeDepth * dy, fromY + spikeDepth * dx + (spikeOffset + spikeWidth) * dy));
|
||||||
|
path.commands.push(LINE_TO(fromX + (spikeOffset + spikeWidth) * dx, fromY + (spikeOffset + spikeWidth) * dy));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,33 +4,33 @@ import hw.storage.SharedObjectStorage;
|
|||||||
import ru.m.puzzlez.render.Background;
|
import ru.m.puzzlez.render.Background;
|
||||||
|
|
||||||
@:provide class Settings extends SharedObjectStorage {
|
@:provide class Settings extends SharedObjectStorage {
|
||||||
private inline static var VERSION = 2;
|
private inline static var VERSION = 2;
|
||||||
private inline static var BACKGROUND_KEY = "background";
|
private inline static var BACKGROUND_KEY = "background";
|
||||||
private inline static var LOCKED_KEY = "locked";
|
private inline static var LOCKED_KEY = "locked";
|
||||||
|
|
||||||
public var background(get, set):Background;
|
public var background(get, set):Background;
|
||||||
|
|
||||||
private inline function get_background():Background {
|
private inline function get_background():Background {
|
||||||
return exists(BACKGROUND_KEY) ? read(BACKGROUND_KEY) : NONE;
|
return exists(BACKGROUND_KEY) ? read(BACKGROUND_KEY) : NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline function set_background(value:Background):Background {
|
private inline function set_background(value:Background):Background {
|
||||||
write(BACKGROUND_KEY, value);
|
write(BACKGROUND_KEY, value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var locked(get, set):Bool;
|
public var locked(get, set):Bool;
|
||||||
|
|
||||||
private inline function get_locked():Bool {
|
private inline function get_locked():Bool {
|
||||||
return exists(LOCKED_KEY) ? read(LOCKED_KEY) : false;
|
return exists(LOCKED_KEY) ? read(LOCKED_KEY) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline function set_locked(value:Bool):Bool {
|
private inline function set_locked(value:Bool):Bool {
|
||||||
write(LOCKED_KEY, value);
|
write(LOCKED_KEY, value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
super('setting/${VERSION}');
|
super('setting/${VERSION}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,34 +9,36 @@ import ru.m.puzzlez.core.ImageValue;
|
|||||||
import ru.m.puzzlez.proto.game.ImageId;
|
import ru.m.puzzlez.proto.game.ImageId;
|
||||||
|
|
||||||
class AssetImageSource implements ImageSource {
|
class AssetImageSource implements ImageSource {
|
||||||
public var id(default, never):String = "asset";
|
public var id(default, never):String = "asset";
|
||||||
|
|
||||||
private var _data:Array<ImageId>;
|
private var _data:Array<ImageId>;
|
||||||
private var data(get, null):Array<ImageId>;
|
private var data(get, null):Array<ImageId>;
|
||||||
|
|
||||||
private function get_data():Array<ImageId> {
|
private function get_data():Array<ImageId> {
|
||||||
if (_data == null) {
|
if (_data == null) {
|
||||||
_data = resolveData();
|
_data = resolveData();
|
||||||
}
|
|
||||||
return _data;
|
|
||||||
}
|
}
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
public function new() {
|
public function new() {}
|
||||||
}
|
|
||||||
|
|
||||||
private function resolveData():Array<ImageId> {
|
private function resolveData():Array<ImageId> {
|
||||||
return [for (name in Assets.list(AssetType.IMAGE).filter((name:String) -> name.substr(0, 15) == "resources/image")) new ImageId().setSource(id).setId(name)];
|
return [
|
||||||
}
|
for (name in Assets.list(AssetType.IMAGE).filter((name:String) -> name.substr(0, 15) == "resources/image"))
|
||||||
|
new ImageId().setSource(id).setId(name)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function getPage(page:Page):Promise<DataPage<ImageId>> {
|
public function getPage(page:Page):Promise<DataPage<ImageId>> {
|
||||||
return Promise.promise({
|
return Promise.promise({
|
||||||
page: page,
|
page: page,
|
||||||
data: data.slice(page.index * page.count, page.count),
|
data: data.slice(page.index * page.count, page.count),
|
||||||
total: data.length,
|
total: data.length,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
|
public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
|
||||||
return Promise.promise(ImageValue.BITMAP(Assets.getBitmapData(id)));
|
return Promise.promise(ImageValue.BITMAP(Assets.getBitmapData(id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,24 +8,23 @@ import ru.m.puzzlez.proto.game.ImageId;
|
|||||||
import ru.m.puzzlez.storage.FileStorage;
|
import ru.m.puzzlez.storage.FileStorage;
|
||||||
|
|
||||||
class FileImageSource implements ImageSource {
|
class FileImageSource implements ImageSource {
|
||||||
public var id(default, never):String = "file";
|
public var id(default, never):String = "file";
|
||||||
|
|
||||||
@:provide private var storage:FileStorage;
|
@:provide private var storage:FileStorage;
|
||||||
|
|
||||||
public function new() {
|
public function new() {}
|
||||||
}
|
|
||||||
|
|
||||||
public function getPage(page:Page):Promise<DataPage<ImageId>> {
|
public function getPage(page:Page):Promise<DataPage<ImageId>> {
|
||||||
return storage.getIndexPage(page).then((response:DataPage<String>) -> {
|
return storage.getIndexPage(page).then((response:DataPage<String>) -> {
|
||||||
return {
|
return {
|
||||||
page: response.page,
|
page: response.page,
|
||||||
data: response.data.map(key -> new ImageId().setSource(id).setId(key)),
|
data: response.data.map(key -> new ImageId().setSource(id).setId(key)),
|
||||||
total: response.total,
|
total: response.total,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
|
public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
|
||||||
return storage.get(id).then(bytes -> ImageValue.BYTES(bytes));
|
return storage.get(id).then(bytes -> ImageValue.BYTES(bytes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +1,53 @@
|
|||||||
package ru.m.puzzlez.source;
|
package ru.m.puzzlez.source;
|
||||||
|
|
||||||
import promhx.Promise;
|
import promhx.Promise;
|
||||||
|
import ru.m.api.PixabayApi;
|
||||||
import ru.m.data.DataSource;
|
import ru.m.data.DataSource;
|
||||||
import ru.m.pixabay.PixabayApi;
|
|
||||||
import ru.m.puzzlez.core.ImageSource;
|
import ru.m.puzzlez.core.ImageSource;
|
||||||
import ru.m.puzzlez.core.ImageValue;
|
import ru.m.puzzlez.core.ImageValue;
|
||||||
import ru.m.puzzlez.proto.game.ImageId;
|
import ru.m.puzzlez.proto.game.ImageId;
|
||||||
|
|
||||||
class PixabayImageSource implements ImageSource {
|
class PixabayImageSource implements ImageSource {
|
||||||
public var id(default, never):String = "pixabay";
|
public var id(default, never):String = "pixabay";
|
||||||
|
|
||||||
private var api:PixabayApi;
|
private var api:PixabayApi;
|
||||||
private static var imageUrlsCache:Map<String, String> = new Map();
|
|
||||||
|
|
||||||
public function new() {
|
private static var imageUrlsCache:Map<String, String> = new Map();
|
||||||
var key:String = CompilationOption.get("PIXABAY_KEY");
|
|
||||||
api = new PixabayApi(key);
|
public function new() {
|
||||||
|
var key:String = CompilationOption.get("PIXABAY_KEY");
|
||||||
|
api = new PixabayApi(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPage(page:Page):Promise<DataPage<ImageId>> {
|
||||||
|
return this.api.getPage(page.index + 1, page.count, page.filter.get("category")).then((response:PixabayResponse) -> {
|
||||||
|
var data:Array<ImageId> = [];
|
||||||
|
for (hit in response.hits) {
|
||||||
|
imageUrlsCache.set('${hit.id}', hit.largeImageURL);
|
||||||
|
data.push(new ImageId().setSource(id).setId(Std.string(hit.id)));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
page: page,
|
||||||
|
data: data,
|
||||||
|
total: response.totalHits,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
|
||||||
|
var imageId = Std.parseInt(id);
|
||||||
|
var key = id;
|
||||||
|
if (thumb) {
|
||||||
|
key = '${key}:thumb';
|
||||||
}
|
}
|
||||||
|
if (imageUrlsCache.exists(key)) {
|
||||||
public function getPage(page:Page):Promise<DataPage<ImageId>> {
|
return Promise.promise(ImageValue.URL(imageUrlsCache.get(key)));
|
||||||
return this.api.getPage(page.index + 1, page.count, page.filter.get("category"))
|
} else {
|
||||||
.then((response:PixabayResponse) -> {
|
return api.get(imageId).then((data:PixabayImage) -> {
|
||||||
var data:Array<ImageId> = [];
|
var url = thumb ? data.previewURL : data.largeImageURL;
|
||||||
for (hit in response.hits) {
|
imageUrlsCache.set(key, url);
|
||||||
imageUrlsCache.set('${hit.id}', hit.largeImageURL);
|
return ImageValue.URL(url);
|
||||||
data.push(new ImageId().setSource(id).setId(Std.string(hit.id)));
|
});
|
||||||
}
|
|
||||||
return {
|
|
||||||
page: page,
|
|
||||||
data: data,
|
|
||||||
total: response.totalHits,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
|
|
||||||
var imageId = Std.parseInt(id);
|
|
||||||
var key = id;
|
|
||||||
if (thumb) {
|
|
||||||
key = '${key}:thumb';
|
|
||||||
}
|
|
||||||
if (imageUrlsCache.exists(key)) {
|
|
||||||
return Promise.promise(ImageValue.URL(imageUrlsCache.get(key)));
|
|
||||||
} else {
|
|
||||||
return api.get(imageId).then((data:PixabayImage) -> {
|
|
||||||
var url = thumb ? data.previewURL : data.largeImageURL;
|
|
||||||
imageUrlsCache.set(key, url);
|
|
||||||
return ImageValue.URL(url);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,76 +7,75 @@ import ru.m.puzzlez.core.ImageSource;
|
|||||||
import ru.m.puzzlez.core.ImageValue;
|
import ru.m.puzzlez.core.ImageValue;
|
||||||
import ru.m.puzzlez.proto.game.ImageId;
|
import ru.m.puzzlez.proto.game.ImageId;
|
||||||
|
|
||||||
|
typedef Resolver = (id:String, thumb:Bool) -> Promise<String>;
|
||||||
typedef Resolver = (id:String, thumb: Bool) -> Promise<String>;
|
|
||||||
|
|
||||||
|
|
||||||
class ImageResolver {
|
class ImageResolver {
|
||||||
|
private var cache:Map<String, String>;
|
||||||
|
private var resolver:Resolver;
|
||||||
|
|
||||||
private var cache: Map<String, String>;
|
public function new(resolver:Resolver) {
|
||||||
private var resolver:Resolver;
|
this.cache = new Map();
|
||||||
|
this.resolver = resolver;
|
||||||
|
}
|
||||||
|
|
||||||
public function new(resolver:Resolver) {
|
private function buildKey(id:String, thumb:Bool = false):String {
|
||||||
this.cache = new Map();
|
var key = id;
|
||||||
this.resolver = resolver;
|
if (thumb) {
|
||||||
|
key = '${key}:thumb';
|
||||||
}
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
private function buildKey(id:String, thumb:Bool = false):String {
|
public function setValue(id:String, url:String, thumb:Bool = false):Void {
|
||||||
var key = id;
|
var key = buildKey(id, thumb);
|
||||||
if (thumb) {
|
cache.set(key, url);
|
||||||
key = '${key}:thumb';
|
}
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setValue(id:String, url:String):Void {
|
public function resolve(id:String, thumb:Bool = false) {
|
||||||
cache.set(id, url);
|
var key = buildKey(id, thumb);
|
||||||
}
|
if (cache.exists(key)) {
|
||||||
|
return Promise.promise(ImageValue.URL(cache.get(key)));
|
||||||
public function resolve(id:String, thumb:Bool = false) {
|
} else {
|
||||||
var key = buildKey(id, thumb);
|
return resolver(id, thumb).then(url -> {
|
||||||
if (cache.exists(key)) {
|
cache.set(key, url);
|
||||||
return Promise.promise(ImageValue.URL(cache.get(key)));
|
return ImageValue.URL(url);
|
||||||
} else {
|
});
|
||||||
return resolver(id, thumb).then(url -> {
|
|
||||||
cache.set(key, url);
|
|
||||||
return ImageValue.URL(url);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnsplashImageSource implements ImageSource {
|
class UnsplashImageSource implements ImageSource {
|
||||||
public var id(default, never):String = "unsplash";
|
public var id(default, never):String = "unsplash";
|
||||||
|
|
||||||
private var api:UnsplashApi;
|
private var api:UnsplashApi;
|
||||||
private static var resolver:ImageResolver;
|
|
||||||
|
|
||||||
public function new() {
|
private static var resolver:ImageResolver;
|
||||||
var key:String = CompilationOption.get("UNSPLASH_KEY");
|
|
||||||
api = new UnsplashApi(key);
|
|
||||||
resolver = new ImageResolver((imageId, thumb) -> {
|
|
||||||
return api.get(imageId).then(data -> thumb ? data.urls.thumb : data.urls.regular);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPage(page:Page):Promise<DataPage<ImageId>> {
|
public function new() {
|
||||||
return this.api.getPage(page.index + 1, page.count, page.filter.get("category"))
|
var key:String = CompilationOption.get("UNSPLASH_KEY");
|
||||||
.then((response:UnsplashResponse) -> {
|
api = new UnsplashApi(key);
|
||||||
var data:Array<ImageId> = [];
|
resolver = new ImageResolver((imageId, thumb) -> {
|
||||||
for (image in response.results) {
|
return api.get(imageId).then(data -> thumb ? data.urls.small : data.urls.regular);
|
||||||
resolver.setValue(image.id, image.urls.regular);
|
});
|
||||||
data.push(new ImageId().setSource(id).setId(image.id));
|
}
|
||||||
}
|
|
||||||
return {
|
|
||||||
page: page,
|
|
||||||
data: data,
|
|
||||||
total: response.total,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
|
public function getPage(page:Page):Promise<DataPage<ImageId>> {
|
||||||
return resolver.resolve(id, thumb);
|
return this.api.getPage(page.index + 1, page.count, page.filter.get("category")).then((response:UnsplashResponse) -> {
|
||||||
}
|
var data:Array<ImageId> = [];
|
||||||
|
for (image in response.results) {
|
||||||
|
resolver.setValue(image.id, image.urls.small, true);
|
||||||
|
resolver.setValue(image.id, image.urls.regular);
|
||||||
|
data.push(new ImageId().setSource(id).setId(image.id));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
page: page,
|
||||||
|
data: data,
|
||||||
|
total: response.total,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function load(id:String, thumb:Bool = false):Promise<ImageValue> {
|
||||||
|
return resolver.resolve(id, thumb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import haxe.io.Bytes;
|
|||||||
import ru.m.storage.SharedObjectDataStorage;
|
import ru.m.storage.SharedObjectDataStorage;
|
||||||
|
|
||||||
@:provide class FileStorage extends SharedObjectDataStorage<Bytes, String> {
|
@:provide class FileStorage extends SharedObjectDataStorage<Bytes, String> {
|
||||||
|
public function new() {
|
||||||
public function new() {
|
super("file", new DefaultHelper());
|
||||||
super("file", new DefaultHelper());
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,42 +7,36 @@ import ru.m.puzzlez.proto.game.ImageId;
|
|||||||
import ru.m.storage.SharedObjectDataStorage;
|
import ru.m.storage.SharedObjectDataStorage;
|
||||||
|
|
||||||
class GameStorageHelper implements DataHelper<GameState, ImageId> {
|
class GameStorageHelper implements DataHelper<GameState, ImageId> {
|
||||||
public function new() {
|
public function new() {}
|
||||||
|
|
||||||
}
|
public function idKey(id:ImageId):String {
|
||||||
|
return '${id.source}:${id.id}';
|
||||||
|
}
|
||||||
|
|
||||||
public function idKey(id:ImageId):String {
|
public function keyId(key:String):ImageId {
|
||||||
return '${id.source}:${id.id}';
|
var keyArray = key.split(":");
|
||||||
}
|
return new ImageId().setSource(keyArray[0]).setId(keyArray[1]);
|
||||||
|
}
|
||||||
|
|
||||||
public function keyId(key:String):ImageId {
|
public function dataId(data:GameState):ImageId {
|
||||||
var keyArray = key.split(":");
|
return data.preset.image;
|
||||||
return new ImageId().setSource(keyArray[0]).setId(keyArray[1]);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function dataId(data:GameState):ImageId {
|
public function dataBytes(data:GameState):Bytes {
|
||||||
return data.preset.image;
|
return PacketUtil.toBytes(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataBytes(data:GameState):Bytes {
|
public function bytesData(bytes:Bytes):GameState {
|
||||||
return PacketUtil.toBytes(data);
|
return PacketUtil.fromBytes(bytes, GameState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function bytesData(bytes:Bytes):GameState {
|
public function dataMeta(data:GameState):DataMeta {
|
||||||
return PacketUtil.fromBytes(bytes, GameState);
|
return ["status" => data.status, "date" => Date.now(),];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataMeta(data:GameState):DataMeta {
|
|
||||||
return [
|
|
||||||
"status" => data.status,
|
|
||||||
"date" => Date.now(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@:provide class GameStorage extends SharedObjectDataStorage<GameState, ImageId> {
|
@:provide class GameStorage extends SharedObjectDataStorage<GameState, ImageId> {
|
||||||
|
public function new() {
|
||||||
public function new() {
|
super("game", new GameStorageHelper());
|
||||||
super("game", new GameStorageHelper());
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,86 +17,85 @@ import ru.m.puzzlez.view.popup.BackgroundPopup;
|
|||||||
import ru.m.puzzlez.view.popup.PreviewPopup;
|
import ru.m.puzzlez.view.popup.PreviewPopup;
|
||||||
|
|
||||||
@:template class GameFrame extends FrameView<GameState> {
|
@:template class GameFrame extends FrameView<GameState> {
|
||||||
public static var ID = "game";
|
public static var ID = "game";
|
||||||
|
|
||||||
@:view private var render:IRender;
|
@:view private var render:IRender;
|
||||||
private var game:IGame;
|
private var game:IGame;
|
||||||
@:provide var switcher:FrameSwitcher;
|
@:provide var switcher:FrameSwitcher;
|
||||||
@:provide var storage:GameStorage;
|
@:provide var storage:GameStorage;
|
||||||
@:provide var settings:Settings;
|
@:provide var settings:Settings;
|
||||||
|
|
||||||
private var saveTimer:Timer;
|
private var saveTimer:Timer;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
super(ID);
|
super(ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function onShow(state:GameState):Void {
|
||||||
|
onHide();
|
||||||
|
if (state.online) {
|
||||||
|
// game = new NetworkGame(state);
|
||||||
|
} else {
|
||||||
|
game = new Game(state);
|
||||||
}
|
}
|
||||||
|
game.events.connect(render.onGameEvent);
|
||||||
|
game.events.connect(onGameEvent);
|
||||||
|
render.actions.connect(game.action);
|
||||||
|
game.start();
|
||||||
|
}
|
||||||
|
|
||||||
override public function onShow(state:GameState):Void {
|
override public function onHide():Void {
|
||||||
onHide();
|
if (saveTimer != null) {
|
||||||
if (state.online) {
|
save();
|
||||||
//game = new NetworkGame(state);
|
|
||||||
} else {
|
|
||||||
game = new Game(state);
|
|
||||||
}
|
|
||||||
game.events.connect(render.onGameEvent);
|
|
||||||
game.events.connect(onGameEvent);
|
|
||||||
render.actions.connect(game.action);
|
|
||||||
game.start();
|
|
||||||
}
|
}
|
||||||
|
if (game != null) {
|
||||||
|
render.actions.disconnect(game.action);
|
||||||
|
game.stop();
|
||||||
|
game.dispose();
|
||||||
|
game = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override public function onHide():Void {
|
private function toSave():Void {
|
||||||
if (saveTimer != null) {
|
if (saveTimer == null) {
|
||||||
save();
|
saveTimer = Timer.delay(save, 5000);
|
||||||
}
|
|
||||||
if (game != null) {
|
|
||||||
render.actions.disconnect(game.action);
|
|
||||||
game.stop();
|
|
||||||
game.dispose();
|
|
||||||
game = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function toSave():Void {
|
private function save():Void {
|
||||||
if (saveTimer == null) {
|
if (saveTimer != null) {
|
||||||
saveTimer = Timer.delay(save, 5000);
|
saveTimer.stop();
|
||||||
}
|
saveTimer = null;
|
||||||
}
|
}
|
||||||
|
if (game != null) {
|
||||||
|
storage.save(game.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function save():Void {
|
private function onGameEvent(event:GameEvent):Void {
|
||||||
if (saveTimer != null) {
|
if (event.hasStart() || event.hasAction()) {
|
||||||
saveTimer.stop();
|
toSave();
|
||||||
saveTimer = null;
|
|
||||||
}
|
|
||||||
if (game != null) {
|
|
||||||
storage.save(game.state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function onGameEvent(event:GameEvent):Void {
|
private function showPreview():Void {
|
||||||
if (event.hasStart() || event.hasAction()) {
|
PreviewPopup.instance.showPreview(game.state);
|
||||||
toSave();
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function showPreview():Void {
|
private function choiseBackground():Void {
|
||||||
PreviewPopup.instance.showPreview(game.state);
|
BackgroundPopup.instance.choise(settings.background).then(background -> {
|
||||||
}
|
if (background != null) {
|
||||||
|
settings.background = background;
|
||||||
|
render.toRedraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private function choiseBackground():Void {
|
private function back():Void {
|
||||||
BackgroundPopup.instance.choise(settings.background).then(background -> {
|
(game.state.status == GameStatus.COMPLETE ? Promise.promise(true) : ConfirmView.confirm("Exit?")).then(result -> {
|
||||||
if (background != null) {
|
if (result) {
|
||||||
settings.background = background;
|
switcher.change(StartFrame.ID);
|
||||||
render.toRedraw();
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private function back():Void {
|
|
||||||
(game.state.status == GameStatus.COMPLETE ? Promise.promise(true) : ConfirmView.confirm("Exit?"))
|
|
||||||
.then(result -> {
|
|
||||||
if (result) {
|
|
||||||
switcher.change(StartFrame.ID);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,37 +10,37 @@ import ru.m.data.DataSource.Filter;
|
|||||||
import hw.view.frame.FrameView;
|
import hw.view.frame.FrameView;
|
||||||
|
|
||||||
typedef GameListConfig = {
|
typedef GameListConfig = {
|
||||||
var title:String;
|
var title:String;
|
||||||
var source:DataSource<GameState>;
|
var source:DataSource<GameState>;
|
||||||
@:optional var filter:Filter;
|
@:optional var filter:Filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@:template class GameListFrame extends FrameView<GameListConfig> {
|
@:template class GameListFrame extends FrameView<GameListConfig> {
|
||||||
public static var ID(default, never) = "game_list";
|
public static var ID(default, never) = "game_list";
|
||||||
|
|
||||||
@:view var header:LabelView;
|
@:view var header:LabelView;
|
||||||
@:view var games:DataList<GameState, GameStateView>;
|
@:view var games:DataList<GameState, GameStateView>;
|
||||||
@:provide var switcher:FrameSwitcher;
|
@:provide var switcher:FrameSwitcher;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
super(ID);
|
super(ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function onShow(data:GameListConfig):Void {
|
||||||
|
games.reset();
|
||||||
|
if (data != null) {
|
||||||
|
header.text = data.title;
|
||||||
|
games.page.filter = data.filter;
|
||||||
|
games.source = data.source;
|
||||||
|
games.refresh();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override public function onShow(data:GameListConfig):Void {
|
public function start(state:GameState):Void {
|
||||||
games.reset();
|
switcher.change(GameFrame.ID, state);
|
||||||
if (data != null) {
|
}
|
||||||
header.text = data.title;
|
|
||||||
games.page.filter = data.filter;
|
|
||||||
games.source = data.source;
|
|
||||||
games.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function start(state:GameState):Void {
|
private function back():Void {
|
||||||
switcher.change(GameFrame.ID, state);
|
switcher.change(StartFrame.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function back():Void {
|
|
||||||
switcher.change(StartFrame.ID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,38 +10,38 @@ import ru.m.puzzlez.image.ImageSourceBundle;
|
|||||||
import ru.m.puzzlez.proto.game.ImageId;
|
import ru.m.puzzlez.proto.game.ImageId;
|
||||||
|
|
||||||
typedef ImageListConfig = {
|
typedef ImageListConfig = {
|
||||||
var title:String;
|
var title:String;
|
||||||
var sourceId:String;
|
var sourceId:String;
|
||||||
@:optional var filter:Filter;
|
@:optional var filter:Filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@:template class ImageListFrame extends FrameView<ImageListConfig> {
|
@:template class ImageListFrame extends FrameView<ImageListConfig> {
|
||||||
public static var ID = "image_list";
|
public static var ID = "image_list";
|
||||||
|
|
||||||
@:view var header:LabelView;
|
@:view var header:LabelView;
|
||||||
@:view var images:DataList<ImageId, ImageIdView>;
|
@:view var images:DataList<ImageId, ImageIdView>;
|
||||||
@:provide var switcher:FrameSwitcher;
|
@:provide var switcher:FrameSwitcher;
|
||||||
@:provide var sourceBundle:ImageSourceBundle;
|
@:provide var sourceBundle:ImageSourceBundle;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
super(ID);
|
super(ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function onShow(data:ImageListConfig):Void {
|
||||||
|
images.reset();
|
||||||
|
if (data != null) {
|
||||||
|
header.text = data.title;
|
||||||
|
images.page.filter = data.filter;
|
||||||
|
images.source = sourceBundle.get(data.sourceId);
|
||||||
|
images.refresh();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override public function onShow(data:ImageListConfig):Void {
|
private function start(imageId:ImageId):Void {
|
||||||
images.reset();
|
switcher.change(PresetFrame.ID, imageId);
|
||||||
if (data != null) {
|
}
|
||||||
header.text = data.title;
|
|
||||||
images.page.filter = data.filter;
|
|
||||||
images.source = sourceBundle.get(data.sourceId);
|
|
||||||
images.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function start(imageId:ImageId):Void {
|
private function back():Void {
|
||||||
switcher.change(PresetFrame.ID, imageId);
|
switcher.change(StartFrame.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function back():Void {
|
|
||||||
switcher.change(StartFrame.ID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,57 +10,57 @@ import ru.m.puzzlez.proto.game.ImageId;
|
|||||||
import ru.m.puzzlez.view.common.PresetView;
|
import ru.m.puzzlez.view.common.PresetView;
|
||||||
|
|
||||||
@:template class PresetFrame extends FrameView<ImageId> {
|
@:template class PresetFrame extends FrameView<ImageId> {
|
||||||
public static var ID = "preset";
|
public static var ID = "preset";
|
||||||
|
|
||||||
@:view("image") var imageView:PresetView;
|
@:view("image") var imageView:PresetView;
|
||||||
@:view("sizes") var sizesView:DataView<IntPoint, ToggleButtonView>;
|
@:view("sizes") var sizesView:DataView<IntPoint, ToggleButtonView>;
|
||||||
|
|
||||||
@:provide var switcher:FrameSwitcher;
|
@:provide var switcher:FrameSwitcher;
|
||||||
//@:provide var network:Network;
|
|
||||||
|
|
||||||
private var imageId:ImageId;
|
// @:provide var network:Network;
|
||||||
|
private var imageId:ImageId;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
super(ID);
|
super(ID);
|
||||||
sizesView.data = [
|
sizesView.data = [
|
||||||
new IntPoint(3, 2),
|
new IntPoint(3, 2),
|
||||||
new IntPoint(4, 3),
|
new IntPoint(4, 3),
|
||||||
new IntPoint(5, 4),
|
new IntPoint(5, 4),
|
||||||
new IntPoint(6, 5),
|
new IntPoint(6, 5),
|
||||||
new IntPoint(7, 6),
|
new IntPoint(7, 6),
|
||||||
new IntPoint(8, 7),
|
new IntPoint(8, 7),
|
||||||
new IntPoint(10, 8),
|
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];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function factory(index:Int, size:IntPoint):ToggleButtonView {
|
override public function onShow(data:ImageId):Void {
|
||||||
var result = new ToggleButtonView();
|
super.onShow(data);
|
||||||
result.text = '${size.x}x${size.y}';
|
imageId = data;
|
||||||
return result;
|
selectSize(sizesView.data[sizesView.data.length - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function selectSize(size:IntPoint):Void {
|
private function start(online:Bool = false):Void {
|
||||||
imageView.state = GameUtil.buildState(GameUtil.buildPreset(imageId, size.x, size.y));
|
if (online) {
|
||||||
for (i in 0...sizesView.dataViews.length) {
|
// network.createGame(imageView.state.preset).then(state -> switcher.change(GameFrame.ID, state));
|
||||||
sizesView.dataViews[i].on = size == sizesView.data[i];
|
} else {
|
||||||
}
|
switcher.change(GameFrame.ID, imageView.state);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override public function onShow(data:ImageId):Void {
|
private function back():Void {
|
||||||
super.onShow(data);
|
switcher.change(ImageListFrame.ID);
|
||||||
imageId = data;
|
}
|
||||||
selectSize(sizesView.data[sizesView.data.length - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function start(online:Bool = false):Void {
|
|
||||||
if (online) {
|
|
||||||
//network.createGame(imageView.state.preset).then(state -> switcher.change(GameFrame.ID, state));
|
|
||||||
} else {
|
|
||||||
switcher.change(GameFrame.ID, imageView.state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function back():Void {
|
|
||||||
switcher.change(ImageListFrame.ID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,28 +9,27 @@ import hw.view.frame.FrameSwitcher;
|
|||||||
import hw.view.group.VGroupView;
|
import hw.view.group.VGroupView;
|
||||||
|
|
||||||
@:template class PuzzlezAppView extends VGroupView {
|
@:template class PuzzlezAppView extends VGroupView {
|
||||||
|
@:view("switcher") var switcherView:FrameSwitcher;
|
||||||
|
@:view("fullscreen") var fullscreenButton:ButtonView;
|
||||||
|
@:view("user") var userLabel:LabelView;
|
||||||
|
|
||||||
@:view("switcher") var switcherView:FrameSwitcher;
|
@:provide static var switcher:FrameSwitcher;
|
||||||
@:view("fullscreen") var fullscreenButton:ButtonView;
|
@:provide static var app:App;
|
||||||
@:view("user") var userLabel:LabelView;
|
|
||||||
|
|
||||||
@:provide static var switcher:FrameSwitcher;
|
public function new() {
|
||||||
@:provide static var app:App;
|
super();
|
||||||
|
fullscreenButton.visible = app.fullScreenSupport;
|
||||||
public function new() {
|
app.fullScreenSignal.connect(fs -> fullscreenButton.style = 'icon.${fs ? "compress" : "expand"}');
|
||||||
super();
|
switcher = switcherView;
|
||||||
fullscreenButton.visible = app.fullScreenSupport;
|
switcher.change(StartFrame.ID);
|
||||||
app.fullScreenSignal.connect(fs -> fullscreenButton.style = 'icon.${fs ? "compress" : "expand"}');
|
stage.addEventListener(KeyboardEvent.KEY_DOWN, (event:KeyboardEvent) -> {
|
||||||
switcher = switcherView;
|
switch event.keyCode {
|
||||||
switcher.change(StartFrame.ID);
|
case Keyboard.ESCAPE:
|
||||||
stage.addEventListener(KeyboardEvent.KEY_DOWN, (event:KeyboardEvent) -> {
|
// switcher.change(StartFrame.ID);
|
||||||
switch event.keyCode {
|
case Keyboard.F:
|
||||||
case Keyboard.ESCAPE:
|
app.fullScreen = !app.fullScreen;
|
||||||
//switcher.change(StartFrame.ID);
|
case _:
|
||||||
case Keyboard.F:
|
}
|
||||||
app.fullScreen = !app.fullScreen;
|
});
|
||||||
case _:
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,79 +4,79 @@ import hw.view.data.DataView;
|
|||||||
import hw.view.form.ButtonView;
|
import hw.view.form.ButtonView;
|
||||||
import hw.view.frame.FrameSwitcher;
|
import hw.view.frame.FrameSwitcher;
|
||||||
import hw.view.frame.FrameView;
|
import hw.view.frame.FrameView;
|
||||||
import ru.m.pixabay.PixabayApi;
|
import ru.m.api.PixabayApi;
|
||||||
import ru.m.puzzlez.FileUtil;
|
import ru.m.puzzlez.FileUtil;
|
||||||
import ru.m.puzzlez.proto.game.GameStatus;
|
import ru.m.puzzlez.proto.game.GameStatus;
|
||||||
import ru.m.puzzlez.storage.FileStorage;
|
import ru.m.puzzlez.storage.FileStorage;
|
||||||
import ru.m.puzzlez.storage.GameStorage;
|
import ru.m.puzzlez.storage.GameStorage;
|
||||||
import ru.m.puzzlez.view.ImageListFrame;
|
|
||||||
import ru.m.puzzlez.view.GameListFrame;
|
import ru.m.puzzlez.view.GameListFrame;
|
||||||
|
import ru.m.puzzlez.view.ImageListFrame;
|
||||||
import ru.m.update.Updater;
|
import ru.m.update.Updater;
|
||||||
|
|
||||||
@:template class StartFrame extends FrameView<Dynamic> {
|
@:template class StartFrame extends FrameView<Dynamic> {
|
||||||
public static var ID = "start";
|
public static var ID = "start";
|
||||||
|
|
||||||
@:view var sources:DataView<ImageListConfig, ButtonView>;
|
@:view var sources:DataView<ImageListConfig, ButtonView>;
|
||||||
@:view var startedButton:ButtonView;
|
@:view var startedButton:ButtonView;
|
||||||
@:view var completedButton:ButtonView;
|
@:view var completedButton:ButtonView;
|
||||||
@:view var updateButton:ButtonView;
|
@:view var updateButton:ButtonView;
|
||||||
|
|
||||||
@:provide var switcher:FrameSwitcher;
|
@:provide var switcher:FrameSwitcher;
|
||||||
@:provide static var appUpdater:Updater;
|
|
||||||
@:provide var fileStorage:FileStorage;
|
|
||||||
@:provide var gameStorage:GameStorage;
|
|
||||||
|
|
||||||
private var fileSource:ImageListConfig = {title: "Files", sourceId: "file"};
|
@:provide static var appUpdater:Updater;
|
||||||
private var startedGames:GameListConfig = {title: "Started", source: gameStorage, filter: ["status" => GameStatus.STARTED]};
|
|
||||||
private var completedGames:GameListConfig = {title: "Completed", source: gameStorage, filter: ["status" => GameStatus.COMPLETE]};
|
|
||||||
|
|
||||||
public function new() {
|
@:provide var fileStorage:FileStorage;
|
||||||
super(ID);
|
@:provide var gameStorage:GameStorage;
|
||||||
var data:Array<ImageListConfig> = [];
|
|
||||||
// data.push({title: "Assets", sourceId: "asset"});
|
private var fileSource:ImageListConfig = {title: "Files", sourceId: "file"};
|
||||||
data.push(fileSource);
|
private var startedGames:GameListConfig = {title: "Started", source: gameStorage, filter: ["status" => GameStatus.STARTED]};
|
||||||
for (type in AbstractEnumTools.getValues(PixabayCategory)) {
|
private var completedGames:GameListConfig = {title: "Completed", source: gameStorage, filter: ["status" => GameStatus.COMPLETE]};
|
||||||
data.push({title: type, sourceId: "unsplash", filter: ["category" => type]});
|
|
||||||
}
|
public function new() {
|
||||||
sources.data = data;
|
super(ID);
|
||||||
|
var data:Array<ImageListConfig> = [];
|
||||||
|
// data.push({title: "Assets", sourceId: "asset"});
|
||||||
|
data.push(fileSource);
|
||||||
|
for (type in AbstractEnumTools.getValues(PixabayCategory)) {
|
||||||
|
data.push({title: type, sourceId: "unsplash", filter: ["category" => type]});
|
||||||
}
|
}
|
||||||
|
sources.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
private function sourceViewFactory(index:Int, source:ImageListConfig):ButtonView {
|
private function sourceViewFactory(index:Int, source:ImageListConfig):ButtonView {
|
||||||
var result = new ButtonView();
|
var result = new ButtonView();
|
||||||
result.text = source.title;
|
result.text = source.title;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function upload():Void {
|
public function upload():Void {
|
||||||
FileUtil.browse()
|
FileUtil.browse().pipe((data:FileContent) -> fileStorage.save(data.content)).then(_ -> showSource(fileSource));
|
||||||
.pipe((data:FileContent) -> fileStorage.save(data.content))
|
}
|
||||||
.then(_ -> showSource(fileSource));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function showSource(config:ImageListConfig):Void {
|
public function showSource(config:ImageListConfig):Void {
|
||||||
switcher.change(ImageListFrame.ID, config);
|
switcher.change(ImageListFrame.ID, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showGames(config:GameListConfig):Void {
|
public function showGames(config:GameListConfig):Void {
|
||||||
switcher.change(GameListFrame.ID, config);
|
switcher.change(GameListFrame.ID, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
override public function onShow(data:Dynamic):Void {
|
override public function onShow(data:Dynamic):Void {
|
||||||
appUpdater.check().then((info:Null<PackageInfo>) -> {
|
appUpdater.check().then((info:Null<PackageInfo>) -> {
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
updateButton.visible = true;
|
updateButton.visible = true;
|
||||||
updateButton.text = 'Update ${info.version}';
|
updateButton.text = 'Update ${info.version}';
|
||||||
}
|
}
|
||||||
}).catchError(error -> L.w('Update', 'failed: ${error}'));
|
}).catchError(error -> L.w('Update', 'failed: ${error}'));
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function refresh():Void {
|
private function refresh():Void {
|
||||||
gameStorage.getIndexPage({index:0, count:0, filter:startedGames.filter}).then(response -> {
|
gameStorage.getIndexPage({index: 0, count: 0, filter: startedGames.filter}).then(response -> {
|
||||||
startedButton.text = response.total > 0 ? 'Started (${response.total})' : 'Started';
|
startedButton.text = response.total > 0 ? 'Started (${response.total})' : 'Started';
|
||||||
});
|
});
|
||||||
gameStorage.getIndexPage({index:0, count:0, filter:completedGames.filter}).then(response -> {
|
gameStorage.getIndexPage({index: 0, count: 0, filter: completedGames.filter}).then(response -> {
|
||||||
completedButton.text = response.total > 0 ? 'Completed (${response.total})' : 'Completed';
|
completedButton.text = response.total > 0 ? 'Completed (${response.total})' : 'Completed';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,28 +7,28 @@ import ru.m.puzzlez.image.GameUtil;
|
|||||||
import ru.m.puzzlez.proto.game.GameState;
|
import ru.m.puzzlez.proto.game.GameState;
|
||||||
|
|
||||||
@:template class GameStateView extends GroupView {
|
@:template class GameStateView extends GroupView {
|
||||||
public var state(default, set):GameState;
|
public var state(default, set):GameState;
|
||||||
|
|
||||||
private function set_state(value:GameState):GameState {
|
private function set_state(value:GameState):GameState {
|
||||||
state = value;
|
state = value;
|
||||||
image.imageId = state.preset.image;
|
image.imageId = state.preset.image;
|
||||||
var progress = GameUtil.calcProgress(state);
|
var progress = GameUtil.calcProgress(state);
|
||||||
label.text = '${progress.complete}/${progress.total}';
|
label.text = '${progress.complete}/${progress.total}';
|
||||||
return state;
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:view var image:ImageIdView;
|
||||||
|
@:view var label:LabelView;
|
||||||
|
|
||||||
|
public function new(?state:GameState, ?style:StyleId) {
|
||||||
|
super();
|
||||||
|
this.style = style;
|
||||||
|
if (state != null) {
|
||||||
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@:view var image:ImageIdView;
|
public static function factory(index:Int, value:GameState):GameStateView {
|
||||||
@:view var label:LabelView;
|
return new GameStateView(value, "view");
|
||||||
|
}
|
||||||
public function new(?state:GameState, ?style: StyleId) {
|
|
||||||
super();
|
|
||||||
this.style = style;
|
|
||||||
if (state != null) {
|
|
||||||
this.state = state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function factory(index:Int, value:GameState):GameStateView {
|
|
||||||
return new GameStateView(value, "view");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,41 +8,38 @@ import ru.m.puzzlez.proto.game.ImageId;
|
|||||||
import ru.m.view.LoadingWrapper;
|
import ru.m.view.LoadingWrapper;
|
||||||
|
|
||||||
@:template class ImageIdView extends GroupView {
|
@:template class ImageIdView extends GroupView {
|
||||||
|
public var thumb:Bool = false;
|
||||||
|
|
||||||
public var thumb:Bool = false;
|
public var imageId(default, set):ImageId;
|
||||||
|
|
||||||
public var imageId(default, set):ImageId;
|
private function set_imageId(value:ImageId):ImageId {
|
||||||
private function set_imageId(value:ImageId):ImageId {
|
imageId = value;
|
||||||
imageId = value;
|
var imageData = ImageData.fromImageId(imageId);
|
||||||
var imageData = ImageData
|
if (thumb) {
|
||||||
.fromImageId(imageId);
|
imageData = imageData.withThumb();
|
||||||
if (thumb) {
|
|
||||||
imageData = imageData.withThumb();
|
|
||||||
}
|
|
||||||
loading.promise = imageData
|
|
||||||
.resolve()
|
|
||||||
.then(data -> this.imageView.image = data);
|
|
||||||
return imageId;
|
|
||||||
}
|
}
|
||||||
|
loading.promise = imageData.resolve().then(data -> this.imageView.image = data);
|
||||||
|
return imageId;
|
||||||
|
}
|
||||||
|
|
||||||
@:view("image") var imageView:ImageView;
|
@:view("image") var imageView:ImageView;
|
||||||
private var loading:LoadingWrapper;
|
private var loading:LoadingWrapper;
|
||||||
|
|
||||||
public function new(?imageId:ImageId, ?style: StyleId, ?thumb: Bool = false) {
|
public function new(?imageId:ImageId, ?style:StyleId, ?thumb:Bool = false) {
|
||||||
super();
|
super();
|
||||||
this.style = style;
|
this.style = style;
|
||||||
this.thumb = thumb;
|
this.thumb = thumb;
|
||||||
loading = new LoadingWrapper(this);
|
loading = new LoadingWrapper(this);
|
||||||
if (imageId != null) {
|
if (imageId != null) {
|
||||||
this.imageId = imageId;
|
this.imageId = imageId;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function factory(index:Int, value:ImageId):ImageIdView {
|
public static function factory(index:Int, value:ImageId):ImageIdView {
|
||||||
return new ImageIdView(value, "view");
|
return new ImageIdView(value, "view");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function factoryThumb(index:Int, value:ImageId):ImageIdView {
|
public static function factoryThumb(index:Int, value:ImageId):ImageIdView {
|
||||||
return new ImageIdView(value, "view", true);
|
return new ImageIdView(value, "view", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,77 +12,77 @@ import ru.m.puzzlez.render.RenderUtil;
|
|||||||
import ru.m.puzzlez.wrap.RectangleExt;
|
import ru.m.puzzlez.wrap.RectangleExt;
|
||||||
|
|
||||||
class PresetView extends GroupView {
|
class PresetView extends GroupView {
|
||||||
@:provide static var builder:IPartBuilder;
|
@:provide static var builder:IPartBuilder;
|
||||||
|
|
||||||
public var scale(get, set):Float;
|
public var scale(get, set):Float;
|
||||||
|
|
||||||
private function get_scale():Float {
|
private function get_scale():Float {
|
||||||
return table.scaleX;
|
return table.scaleX;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function set_scale(value:Float):Float {
|
||||||
|
var result = table.scaleX = table.scaleY = value;
|
||||||
|
table.x = (width - state.preset.imageRect.width * value) / 2;
|
||||||
|
table.y = (height - state.preset.imageRect.height * value) / 2;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public var state(default, set):GameState;
|
||||||
|
|
||||||
|
private function set_state(value:GameState):GameState {
|
||||||
|
state = value;
|
||||||
|
this.image = null;
|
||||||
|
table.graphics.clear();
|
||||||
|
loading.promise = ImageData.fromImageId(state.preset.image).resolve().then(image -> {
|
||||||
|
this.image = RenderUtil.cropImage(image, state.preset.imageRect);
|
||||||
|
toRedraw();
|
||||||
|
toUpdate();
|
||||||
|
});
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private var loading:LoadingWrapper;
|
||||||
|
|
||||||
|
public function new() {
|
||||||
|
super();
|
||||||
|
table = new Shape();
|
||||||
|
content.addChild(table);
|
||||||
|
loading = new LoadingWrapper(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private var table:Shape;
|
||||||
|
private var image:BitmapData;
|
||||||
|
|
||||||
|
override public function redraw():Void {
|
||||||
|
if (state == null || image == null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
var preset = state.preset;
|
||||||
|
var partWidth = preset.imageRect.width / preset.grid.x;
|
||||||
|
var partHeight = preset.imageRect.height / preset.grid.y;
|
||||||
|
var graphics:Graphics = table.graphics;
|
||||||
|
graphics.clear();
|
||||||
|
graphics.beginBitmapFill(image, null, false, true);
|
||||||
|
graphics.drawRect(0, 0, preset.imageRect.width, preset.imageRect.height);
|
||||||
|
graphics.endFill();
|
||||||
|
|
||||||
private function set_scale(value:Float):Float {
|
for (part in state.parts) {
|
||||||
var result = table.scaleX = table.scaleY = value;
|
var rect = cast(part.rect, RectangleExt).clone();
|
||||||
table.x = (width - state.preset.imageRect.width * value) / 2;
|
rect.x = part.point.x * part.rect.width;
|
||||||
table.y = (height - state.preset.imageRect.height * value) / 2;
|
rect.y = part.point.y * part.rect.height;
|
||||||
return result;
|
var path = 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var state(default, set):GameState;
|
override public function update():Void {
|
||||||
|
super.update();
|
||||||
private function set_state(value:GameState):GameState {
|
if (state != null) {
|
||||||
state = value;
|
scale = Math.min(width / state.preset.imageRect.width, height / state.preset.imageRect.height);
|
||||||
this.image = null;
|
|
||||||
table.graphics.clear();
|
|
||||||
loading.promise = ImageData.fromImageId(state.preset.image).resolve().then(image -> {
|
|
||||||
this.image = RenderUtil.cropImage(image, state.preset.imageRect);
|
|
||||||
toRedraw();
|
|
||||||
toUpdate();
|
|
||||||
});
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
private var loading:LoadingWrapper;
|
|
||||||
|
|
||||||
public function new() {
|
|
||||||
super();
|
|
||||||
table = new Shape();
|
|
||||||
content.addChild(table);
|
|
||||||
loading = new LoadingWrapper(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private var table:Shape;
|
|
||||||
private var image:BitmapData;
|
|
||||||
|
|
||||||
override public function redraw():Void {
|
|
||||||
if (state == null || image == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var preset = state.preset;
|
|
||||||
var partWidth = preset.imageRect.width / preset.grid.x;
|
|
||||||
var partHeight = preset.imageRect.height / preset.grid.y;
|
|
||||||
var graphics:Graphics = table.graphics;
|
|
||||||
graphics.clear();
|
|
||||||
graphics.beginBitmapFill(image, null, false, true);
|
|
||||||
graphics.drawRect(0, 0, preset.imageRect.width, preset.imageRect.height);
|
|
||||||
graphics.endFill();
|
|
||||||
|
|
||||||
for (part in state.parts) {
|
|
||||||
var rect = cast(part.rect, RectangleExt).clone();
|
|
||||||
rect.x = part.point.x * part.rect.width;
|
|
||||||
rect.y = part.point.y * part.rect.height;
|
|
||||||
var path = 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override public function update():Void {
|
|
||||||
super.update();
|
|
||||||
if (state != null) {
|
|
||||||
scale = Math.min(width / state.preset.imageRect.width, height / state.preset.imageRect.height);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,82 +15,66 @@ import ru.m.puzzlez.proto.game.ImageId;
|
|||||||
import ru.m.puzzlez.render.Background;
|
import ru.m.puzzlez.render.Background;
|
||||||
|
|
||||||
@:singleton @:template class BackgroundPopup extends PopupView<Background> {
|
@:singleton @:template class BackgroundPopup extends PopupView<Background> {
|
||||||
|
private static var colorsList:Array<Color> = [
|
||||||
|
'#FFFFFF', '#001f3f', '#0074D9', '#7FDBFF', '#39CCCC', '#3D9970', '#2ECC40', '#01FF70', '#FFDC00', '#FF851B', '#FF4136', '#85144b', '#F012BE', '#B10DC9',
|
||||||
|
'#111111', '#AAAAAA', '#DDDDDD',
|
||||||
|
];
|
||||||
|
|
||||||
private static var colorsList:Array<Color> = [
|
@:view("selected") var selectedView:SpriteView;
|
||||||
'#FFFFFF',
|
@:view("colors") var colorsView:DataView<Color, ButtonView>;
|
||||||
'#001f3f',
|
@:view("textures") var texturesView:DataView<ImageId, ButtonView>;
|
||||||
'#0074D9',
|
|
||||||
'#7FDBFF',
|
|
||||||
'#39CCCC',
|
|
||||||
'#3D9970',
|
|
||||||
'#2ECC40',
|
|
||||||
'#01FF70',
|
|
||||||
'#FFDC00',
|
|
||||||
'#FF851B',
|
|
||||||
'#FF4136',
|
|
||||||
'#85144b',
|
|
||||||
'#F012BE',
|
|
||||||
'#B10DC9',
|
|
||||||
'#111111',
|
|
||||||
'#AAAAAA',
|
|
||||||
'#DDDDDD',
|
|
||||||
];
|
|
||||||
|
|
||||||
@:view("selected") var selectedView:SpriteView;
|
public var selected(default, set):Background;
|
||||||
@:view("colors") var colorsView:DataView<Color, ButtonView>;
|
|
||||||
@:view("textures") var texturesView:DataView<ImageId, ButtonView>;
|
|
||||||
|
|
||||||
public var selected(default, set):Background;
|
// @:provide static var imageStorage:ImageStorage;
|
||||||
|
|
||||||
// @:provide static var imageStorage:ImageStorage;
|
public function new() {
|
||||||
|
super();
|
||||||
public function new() {
|
colorsView.data = colorsList;
|
||||||
super();
|
var textures = [];
|
||||||
colorsView.data = colorsList;
|
for (name in Assets.list(AssetType.IMAGE)) {
|
||||||
var textures = [];
|
if (StringTools.startsWith(name, 'resources/texture')) {
|
||||||
for (name in Assets.list(AssetType.IMAGE)) {
|
textures.push(new ImageId().setSource('asset').setId(name));
|
||||||
if (StringTools.startsWith(name, 'resources/texture')) {
|
}
|
||||||
textures.push(new ImageId().setSource('asset').setId(name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
texturesView.data = textures;
|
|
||||||
}
|
}
|
||||||
|
texturesView.data = textures;
|
||||||
|
}
|
||||||
|
|
||||||
private function set_selected(value:Background):Background {
|
private function set_selected(value:Background):Background {
|
||||||
selected = value;
|
selected = value;
|
||||||
switch selected {
|
switch selected {
|
||||||
case NONE:
|
case NONE:
|
||||||
selectedView.skin = null;
|
selectedView.skin = null;
|
||||||
case COLOR(color):
|
case COLOR(color):
|
||||||
selectedView.skin = Skin.color(color);
|
selectedView.skin = Skin.color(color);
|
||||||
case IMAGE(id):
|
case IMAGE(id):
|
||||||
ImageData.fromImageId(id).resolve().then(result -> {
|
ImageData.fromImageId(id).resolve().then(result -> {
|
||||||
selectedView.skin = Skin.bitmap(result, REPEAT);
|
selectedView.skin = Skin.bitmap(result, REPEAT);
|
||||||
selectedView.toRedraw();
|
selectedView.toRedraw();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
selectedView.toRedraw();
|
|
||||||
return selected;
|
|
||||||
}
|
}
|
||||||
|
selectedView.toRedraw();
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
private function colorButtonFactory(index:Int, color:Color):ButtonView {
|
private function colorButtonFactory(index:Int, color:Color):ButtonView {
|
||||||
var result = new ButtonView();
|
var result = new ButtonView();
|
||||||
result.style = "button.background";
|
result.style = "button.background";
|
||||||
result.text = " ";
|
result.text = " ";
|
||||||
result.skin = Skin.buttonColor(color);
|
result.skin = Skin.buttonColor(color);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function textureButtonFactory(index:Int, imageId:ImageId):ButtonView {
|
private function textureButtonFactory(index:Int, imageId:ImageId):ButtonView {
|
||||||
var result = new ButtonView();
|
var result = new ButtonView();
|
||||||
result.style = "button.background";
|
result.style = "button.background";
|
||||||
result.text = " ";
|
result.text = " ";
|
||||||
result.skin = Skin.buttonBitmap(Assets.getBitmapData(imageId.id), REPEAT);
|
result.skin = Skin.buttonBitmap(Assets.getBitmapData(imageId.id), REPEAT);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function choise(current:Background):Promise<Background> {
|
public function choise(current:Background):Promise<Background> {
|
||||||
selected = current;
|
selected = current;
|
||||||
return show();
|
return show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,15 @@ import promhx.Promise;
|
|||||||
import ru.m.puzzlez.view.common.PresetView;
|
import ru.m.puzzlez.view.common.PresetView;
|
||||||
|
|
||||||
@:singleton @:template class PreviewPopup extends PopupView<Dynamic> {
|
@:singleton @:template class PreviewPopup extends PopupView<Dynamic> {
|
||||||
|
@:view("preview") var previewView:PresetView;
|
||||||
|
|
||||||
@:view("preview") var previewView:PresetView;
|
public function new() {
|
||||||
|
super();
|
||||||
|
content.addEventListener(MouseEvent.CLICK, _ -> close(null));
|
||||||
|
}
|
||||||
|
|
||||||
public function new() {
|
public function showPreview(state:GameState):Promise<Dynamic> {
|
||||||
super();
|
previewView.state = state;
|
||||||
content.addEventListener(MouseEvent.CLICK, _ -> close(null));
|
return show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showPreview(state:GameState):Promise<Dynamic> {
|
|
||||||
previewView.state = state;
|
|
||||||
return show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,59 +10,58 @@ using StringTools;
|
|||||||
using hw.color.ColorUtil;
|
using hw.color.ColorUtil;
|
||||||
|
|
||||||
@:style class ButtonSVGSkin implements ISkin<ButtonView> {
|
@:style class ButtonSVGSkin implements ISkin<ButtonView> {
|
||||||
|
@:style(null) public var svg:String;
|
||||||
|
@:style(0) public var color:Null<Color>;
|
||||||
|
@:style(false) public var solid:Null<Bool>;
|
||||||
|
|
||||||
@:style(null) public var svg:String;
|
private var svgs:Map<ButtonState, SVG>;
|
||||||
@:style(0) public var color:Null<Color>;
|
private var needUpdate:Bool;
|
||||||
@:style(false) public var solid:Null<Bool>;
|
|
||||||
|
|
||||||
private var svgs:Map<ButtonState, SVG>;
|
public function new(?svg:String, ?color:Color, ?solid:Bool) {
|
||||||
private var needUpdate:Bool;
|
this.svg = svg;
|
||||||
|
this.color = color;
|
||||||
|
this.solid = solid;
|
||||||
|
this.needUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
public function new(?svg:String, ?color:Color, ?solid:Bool) {
|
private inline function buildSVG(color:Color):SVG {
|
||||||
this.svg = svg;
|
return new SVG(svg.replace("currentColor", '#${color}'));
|
||||||
this.color = color;
|
}
|
||||||
this.solid = solid;
|
|
||||||
this.needUpdate = true;
|
private function update():Void {
|
||||||
|
if (needUpdate && svg != null) {
|
||||||
|
var color = color;
|
||||||
|
if (solid) {
|
||||||
|
color = color.multiply(1.5);
|
||||||
|
}
|
||||||
|
svgs = new Map();
|
||||||
|
svgs.set(UP, buildSVG(color));
|
||||||
|
svgs.set(DOWN, buildSVG(color.diff(-24)));
|
||||||
|
svgs.set(OVER, buildSVG(color.diff(24)));
|
||||||
|
svgs.set(DISABLED, buildSVG(color.grey()));
|
||||||
|
needUpdate = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private inline function buildSVG(color:Color):SVG {
|
public function draw(view:ButtonView):Void {
|
||||||
return new SVG(svg.replace("currentColor", '#${color}'));
|
update();
|
||||||
|
var svg = svgs.get(view.state);
|
||||||
|
var graphics = view.content.graphics;
|
||||||
|
var color = this.color;
|
||||||
|
if (Std.is(view, ToggleButtonView)) {
|
||||||
|
if (!cast(view, ToggleButtonView).on) {
|
||||||
|
color = color.multiply(0.5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
graphics.beginFill(0, 0);
|
||||||
private function update():Void {
|
graphics.drawRect(0, 0, view.width, view.height);
|
||||||
if (needUpdate && svg != null) {
|
graphics.beginFill(color);
|
||||||
var color = color;
|
if (!solid) {
|
||||||
if (solid) {
|
graphics.lineStyle(2, color.multiply(1.5));
|
||||||
color = color.multiply(1.5);
|
|
||||||
}
|
|
||||||
svgs = new Map();
|
|
||||||
svgs.set(UP, buildSVG(color));
|
|
||||||
svgs.set(DOWN, buildSVG(color.diff(-24)));
|
|
||||||
svgs.set(OVER, buildSVG(color.diff(24)));
|
|
||||||
svgs.set(DISABLED, buildSVG(color.grey()));
|
|
||||||
needUpdate = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function draw(view:ButtonView):Void {
|
|
||||||
update();
|
|
||||||
var svg = svgs.get(view.state);
|
|
||||||
var graphics = view.content.graphics;
|
|
||||||
var color = this.color;
|
|
||||||
if (Std.is(view, ToggleButtonView)) {
|
|
||||||
if (!cast(view, ToggleButtonView).on) {
|
|
||||||
color = color.multiply(0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
graphics.beginFill(0, 0);
|
|
||||||
graphics.drawRect(0, 0, view.width, view.height);
|
|
||||||
graphics.beginFill(color);
|
|
||||||
if (!solid) {
|
|
||||||
graphics.lineStyle(2, color.multiply(1.5));
|
|
||||||
}
|
|
||||||
// ToDo: padding
|
|
||||||
svg.render(graphics, 0, 0, Std.int(view.width * 0.8), Std.int(view.height * 0.8));
|
|
||||||
graphics.lineStyle();
|
|
||||||
graphics.endFill();
|
|
||||||
}
|
}
|
||||||
|
// ToDo: padding
|
||||||
|
svg.render(graphics, 0, 0, Std.int(view.width * 0.8), Std.int(view.height * 0.8));
|
||||||
|
graphics.lineStyle();
|
||||||
|
graphics.endFill();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,167 +11,165 @@ import ru.m.data.DataSource;
|
|||||||
typedef DataMeta = Map<String, Dynamic>;
|
typedef DataMeta = Map<String, Dynamic>;
|
||||||
|
|
||||||
typedef IdMeta<I> = {
|
typedef IdMeta<I> = {
|
||||||
var id:I;
|
var id:I;
|
||||||
var meta:DataMeta;
|
var meta:DataMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DataHelper<D, I> {
|
interface DataHelper<D, I> {
|
||||||
public function idKey(id:I):String;
|
public function idKey(id:I):String;
|
||||||
|
|
||||||
public function keyId(key:String):I;
|
public function keyId(key:String):I;
|
||||||
|
|
||||||
public function dataId(data:D):I;
|
public function dataId(data:D):I;
|
||||||
|
|
||||||
public function dataBytes(data:D):Bytes;
|
public function dataBytes(data:D):Bytes;
|
||||||
|
|
||||||
public function bytesData(bytes:Bytes):D;
|
public function bytesData(bytes:Bytes):D;
|
||||||
|
|
||||||
public function dataMeta(data:D):DataMeta;
|
public function dataMeta(data:D):DataMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultHelper implements DataHelper<Bytes, String> {
|
class DefaultHelper implements DataHelper<Bytes, String> {
|
||||||
public function new() {
|
public function new() {}
|
||||||
|
|
||||||
}
|
public function idKey(id:String):String {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
public function idKey(id:String):String {
|
public function keyId(key:String):String {
|
||||||
return id;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function keyId(key:String):String {
|
public function dataId(data:Bytes):String {
|
||||||
return key;
|
return Md5.make(data).toHex();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataId(data:Bytes):String {
|
public function dataBytes(data:Bytes):Bytes {
|
||||||
return Md5.make(data).toHex();
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataBytes(data:Bytes):Bytes {
|
public function bytesData(bytes:Bytes):Bytes {
|
||||||
return data;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function bytesData(bytes:Bytes):Bytes {
|
public function dataMeta(data:Bytes):DataMeta {
|
||||||
return bytes;
|
return ["date" => Date.now()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataMeta(data:Bytes):DataMeta {
|
|
||||||
return ["date" => Date.now()];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SharedObjectDataStorage<D, I> implements DataStorage<D, I> {
|
class SharedObjectDataStorage<D, I> implements DataStorage<D, I> {
|
||||||
private static var DATA_KEY:String = "data";
|
private static var DATA_KEY:String = "data";
|
||||||
|
|
||||||
public var name(default, null):String;
|
public var name(default, null):String;
|
||||||
public var version(default, null):Int;
|
public var version(default, null):Int;
|
||||||
|
|
||||||
private var helper:DataHelper<D, I>;
|
private var helper:DataHelper<D, I>;
|
||||||
|
|
||||||
private var _indexObject:SharedObject;
|
private var _indexObject:SharedObject;
|
||||||
private var indexObject(get, null):SharedObject;
|
private var indexObject(get, null):SharedObject;
|
||||||
|
|
||||||
private function get_indexObject():SharedObject {
|
private function get_indexObject():SharedObject {
|
||||||
if (_indexObject == null) {
|
if (_indexObject == null) {
|
||||||
_indexObject = SharedObject.getLocal('${name}/${version}/index');
|
_indexObject = SharedObject.getLocal('${name}/${version}/index');
|
||||||
|
}
|
||||||
|
return _indexObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function new(name:String, helper:DataHelper<D, I>, version:Int = 1) {
|
||||||
|
this.name = name;
|
||||||
|
this.version = version;
|
||||||
|
this.helper = helper;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveDataObject(id:I):SharedObject {
|
||||||
|
var idKey = helper.idKey(id);
|
||||||
|
return SharedObject.getLocal('${name}/${version}/${Md5.encode(idKey)}');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyFilter(filter:Null<Filter>, meta:DataMeta):Bool {
|
||||||
|
if (filter != null) {
|
||||||
|
for (k in filter.keys()) {
|
||||||
|
if (meta.exists(k) && meta.get(k) != filter.get(k)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return _indexObject;
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function new(name:String, helper:DataHelper<D, I>, version:Int = 1) {
|
private function applyOrder(order:Order, values:Array<IdMeta<I>>):Void {
|
||||||
this.name = name;
|
if (order != null) {
|
||||||
this.version = version;
|
values.sort((a:IdMeta<I>, b:IdMeta<I>) -> {
|
||||||
this.helper = helper;
|
for (item in order) {
|
||||||
}
|
var av = a.meta.get(item.field);
|
||||||
|
var bv = b.meta.get(item.field);
|
||||||
private function resolveDataObject(id:I):SharedObject {
|
if (av > bv) {
|
||||||
var idKey = helper.idKey(id);
|
return item.reverse ? -1 : 1;
|
||||||
return SharedObject.getLocal('${name}/${version}/${Md5.encode(idKey)}');
|
} else if (av < bv) {
|
||||||
}
|
return item.reverse ? 1 : -1;
|
||||||
|
}
|
||||||
private function applyFilter(filter:Null<Filter>, meta:DataMeta):Bool {
|
|
||||||
if (filter != null) {
|
|
||||||
for (k in filter.keys()) {
|
|
||||||
if (meta.exists(k) && meta.get(k) != filter.get(k)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return 0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function applyOrder(order:Order, values:Array<IdMeta<I>>):Void {
|
public function getIndexPage(page:Page):Promise<DataPage<I>> {
|
||||||
if (order != null) {
|
var data = indexObject.data;
|
||||||
values.sort((a:IdMeta<I>, b:IdMeta<I>) -> {
|
var values:Array<IdMeta<I>> = [];
|
||||||
for (item in order) {
|
for (k in Reflect.fields(data)) {
|
||||||
var av = a.meta.get(item.field);
|
var meta = Unserializer.run(Reflect.field(data, k));
|
||||||
var bv = b.meta.get(item.field);
|
if (applyFilter(page.filter, meta)) {
|
||||||
if (av > bv) {
|
values.push({id: helper.keyId(k), meta: meta});
|
||||||
return item.reverse ? -1 : 1;
|
}
|
||||||
} else if (av < bv) {
|
}
|
||||||
return item.reverse ? 1 : -1;
|
applyOrder(page.order, values);
|
||||||
}
|
var result:Array<I> = values.slice(page.index * page.count, page.index * page.count + page.count).map(value -> value.id);
|
||||||
}
|
return Promise.promise({
|
||||||
return 0;
|
page: page,
|
||||||
});
|
total: values.length,
|
||||||
|
data: result,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPage(page:Page):Promise<DataPage<D>> {
|
||||||
|
return getIndexPage(page).pipe((indexPage:DataPage<I>) -> {
|
||||||
|
return Promise.whenAll([for (id in indexPage.data) get(id)]).then((data:Array<D>) -> {
|
||||||
|
return {
|
||||||
|
page: indexPage.page,
|
||||||
|
total: indexPage.total,
|
||||||
|
data: data,
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function getIndexPage(page:Page):Promise<DataPage<I>> {
|
public function get(id:I):Promise<D> {
|
||||||
var data = indexObject.data;
|
var dataObject = resolveDataObject(id);
|
||||||
var values:Array<IdMeta<I>> = [];
|
if (Reflect.hasField(dataObject.data, DATA_KEY)) {
|
||||||
for (k in Reflect.fields(data)) {
|
return Promise.promise(helper.bytesData(Bytes.ofHex(Reflect.field(dataObject.data, DATA_KEY))));
|
||||||
var meta = Unserializer.run(Reflect.field(data, k));
|
|
||||||
if (applyFilter(page.filter, meta)) {
|
|
||||||
values.push({id: helper.keyId(k), meta: meta});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
applyOrder(page.order, values);
|
|
||||||
var result:Array<I> = values.slice(page.index * page.count, page.index * page.count + page.count).map(value -> value.id);
|
|
||||||
return Promise.promise({
|
|
||||||
page: page,
|
|
||||||
total: values.length,
|
|
||||||
data: result,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
return Promise.promise(null);
|
||||||
|
}
|
||||||
|
|
||||||
public function getPage(page:Page):Promise<DataPage<D>> {
|
public function save(item:D):Promise<D> {
|
||||||
return getIndexPage(page).pipe((indexPage:DataPage<I>) -> {
|
var id = helper.dataId(item);
|
||||||
return Promise.whenAll([for (id in indexPage.data) get(id)]).then((data:Array<D>) -> {
|
var dataObject = resolveDataObject(id);
|
||||||
return {
|
dataObject.setProperty(DATA_KEY, helper.dataBytes(item).toHex());
|
||||||
page: indexPage.page,
|
dataObject.flush();
|
||||||
total: indexPage.total,
|
indexObject.setProperty(helper.idKey(id), Serializer.run(helper.dataMeta(item)));
|
||||||
data: data,
|
indexObject.flush();
|
||||||
}
|
return Promise.promise(item);
|
||||||
});
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(id:I):Promise<D> {
|
public function delete(id:I):Promise<Bool> {
|
||||||
var dataObject = resolveDataObject(id);
|
var dataObject = resolveDataObject(id);
|
||||||
if (Reflect.hasField(dataObject.data, DATA_KEY)) {
|
if (Reflect.hasField(dataObject.data, DATA_KEY)) {
|
||||||
return Promise.promise(helper.bytesData(Bytes.ofHex(Reflect.field(dataObject.data, DATA_KEY))));
|
dataObject.clear();
|
||||||
}
|
Reflect.deleteField(indexObject.data, helper.idKey(id));
|
||||||
return Promise.promise(null);
|
indexObject.flush();
|
||||||
}
|
return Promise.promise(true);
|
||||||
|
|
||||||
public function save(item:D):Promise<D> {
|
|
||||||
var id = helper.dataId(item);
|
|
||||||
var dataObject = resolveDataObject(id);
|
|
||||||
dataObject.setProperty(DATA_KEY, helper.dataBytes(item).toHex());
|
|
||||||
dataObject.flush();
|
|
||||||
indexObject.setProperty(helper.idKey(id), Serializer.run(helper.dataMeta(item)));
|
|
||||||
indexObject.flush();
|
|
||||||
return Promise.promise(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete(id:I):Promise<Bool> {
|
|
||||||
var dataObject = resolveDataObject(id);
|
|
||||||
if (Reflect.hasField(dataObject.data, DATA_KEY)) {
|
|
||||||
dataObject.clear();
|
|
||||||
Reflect.deleteField(indexObject.data, helper.idKey(id));
|
|
||||||
indexObject.flush();
|
|
||||||
return Promise.promise(true);
|
|
||||||
}
|
|
||||||
return Promise.promise(false);
|
|
||||||
}
|
}
|
||||||
|
return Promise.promise(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,76 +8,72 @@ import ru.m.Device;
|
|||||||
import ru.m.update.Version;
|
import ru.m.update.Version;
|
||||||
|
|
||||||
@:enum abstract PackageType(String) from String to String {
|
@:enum abstract PackageType(String) from String to String {
|
||||||
var APK = "apk";
|
var APK = "apk";
|
||||||
var DEB = "deb";
|
var DEB = "deb";
|
||||||
var EXE = "exe";
|
var EXE = "exe";
|
||||||
var ARCHIVE = "archive";
|
var ARCHIVE = "archive";
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef PackageInfo = {
|
typedef PackageInfo = {
|
||||||
var platform:Platform;
|
var platform:Platform;
|
||||||
var type:PackageType;
|
var type:PackageType;
|
||||||
var path:String;
|
var path:String;
|
||||||
var filename:String;
|
var filename:String;
|
||||||
var url:String;
|
var url:String;
|
||||||
var version:String;
|
var version:String;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef PackagesBundle = {
|
typedef PackagesBundle = {
|
||||||
var name:String;
|
var name:String;
|
||||||
var version:String;
|
var version:String;
|
||||||
var packages:Array<PackageInfo>;
|
var packages:Array<PackageInfo>;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Updater {
|
class Updater {
|
||||||
|
private static inline var TAG = "Update";
|
||||||
|
|
||||||
private static inline var TAG = "Update";
|
public var type(get, null):PackageType;
|
||||||
|
public var bundle(get, null):Promise<PackagesBundle>;
|
||||||
|
|
||||||
public var type(get, null):PackageType;
|
private function get_bundle():Promise<PackagesBundle> {
|
||||||
public var bundle(get, null):Promise<PackagesBundle>;
|
if (bundle == null) {
|
||||||
|
bundle = new JsonLoader().GET(url);
|
||||||
|
}
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
private function get_bundle():Promise<PackagesBundle> {
|
private var url:String;
|
||||||
if (bundle == null) {
|
private var version:Version;
|
||||||
bundle = new JsonLoader().GET(url);
|
|
||||||
|
public function new(version:Version, 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 bundle;
|
}
|
||||||
}
|
return false;
|
||||||
|
});
|
||||||
private var url:String;
|
}
|
||||||
private var version:Version;
|
|
||||||
|
|
||||||
public function new(version:Version, 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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +1,45 @@
|
|||||||
package ru.m.update;
|
package ru.m.update;
|
||||||
|
|
||||||
abstract Version(Array<Int>) {
|
abstract Version(Array<Int>) {
|
||||||
|
public function new(mayor:Int, minor:Int, patch:Int, snapshot:Bool = false) {
|
||||||
|
this = [mayor, minor, patch, snapshot ? -1 : 0,];
|
||||||
|
}
|
||||||
|
|
||||||
public function new(mayor:Int, minor:Int, patch:Int, snapshot:Bool = false) {
|
@:arrayAccess public inline function get(key:Int)
|
||||||
this = [
|
return this[key];
|
||||||
mayor,
|
|
||||||
minor,
|
public function compare(other:Version):Int {
|
||||||
patch,
|
if (other == null) {
|
||||||
snapshot ? -1 : 0,
|
return 1;
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
for (i in 0...4) {
|
||||||
@:arrayAccess public inline function get(key:Int) return this[key];
|
var d = this[i] - other[i];
|
||||||
|
if (d != 0) {
|
||||||
public function compare(other:Version):Int {
|
return d;
|
||||||
if (other == null) {
|
}
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
for (i in 0...4) {
|
|
||||||
var d = this[i] - other[i];
|
|
||||||
if (d != 0) {
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@:op(A > B) static function gt(a:Version, b:Version):Bool return a.compare(b) > 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 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;
|
@:op(A < B) static function lt(a:Version, b:Version):Bool
|
||||||
|
return a.compare(b) < 0;
|
||||||
|
|
||||||
@:from public static function fromString(value:String):Version {
|
@:from public static function fromString(value:String):Version {
|
||||||
var r = ~/(\d+)\.(\d+)\.(\d+)(-SNAPSHOT)?/;
|
var r = ~/(\d+)\.(\d+)\.(\d+)(-SNAPSHOT)?/;
|
||||||
return if (r.match(value)) {
|
return if (r.match(value)) {
|
||||||
new Version(
|
new Version(Std.parseInt(r.matched(1)), Std.parseInt(r.matched(2)), Std.parseInt(r.matched(3)), r.matched(4) != null);
|
||||||
Std.parseInt(r.matched(1)),
|
} else {
|
||||||
Std.parseInt(r.matched(2)),
|
new Version(0, 0, 0);
|
||||||
Std.parseInt(r.matched(3)),
|
|
||||||
r.matched(4) != null
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
new Version(0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@:to public function toString():String {
|
@:to public function toString():String {
|
||||||
return '${this[0]}.${this[1]}.${this[2]}${this[3] < 0 ? "-SNAPSHOT" : ""}';
|
return '${this[0]}.${this[1]}.${this[2]}${this[3] < 0 ? "-SNAPSHOT" : ""}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,39 +7,39 @@ import ru.m.data.DataSource;
|
|||||||
import ru.m.view.LoadingWrapper;
|
import ru.m.view.LoadingWrapper;
|
||||||
|
|
||||||
@:template class DataList<T, V:View<Dynamic>> extends VGroupView {
|
@:template class DataList<T, V:View<Dynamic>> extends VGroupView {
|
||||||
|
public var source:DataSource<T>;
|
||||||
|
public var page:Page;
|
||||||
|
|
||||||
public var source:DataSource<T>;
|
@:view public var list(default, null):DataView<T, V>;
|
||||||
public var page:Page;
|
|
||||||
|
|
||||||
@:view public var list(default, null):DataView<T, V>;
|
@:view private var paginator:PaginatorView;
|
||||||
@:view private var paginator:PaginatorView;
|
private var loading:LoadingWrapper;
|
||||||
private var loading:LoadingWrapper;
|
|
||||||
|
|
||||||
public var data(default, set):DataPage<T>;
|
public var data(default, set):DataPage<T>;
|
||||||
|
|
||||||
private function set_data(value:DataPage<T>):DataPage<T> {
|
private function set_data(value:DataPage<T>):DataPage<T> {
|
||||||
data = value;
|
data = value;
|
||||||
list.data = data.data;
|
list.data = data.data;
|
||||||
paginator.page = data;
|
paginator.page = data;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
super();
|
super();
|
||||||
loading = new LoadingWrapper(list);
|
loading = new LoadingWrapper(list);
|
||||||
page = {index: 0, count: 6, order: [{field: "date", reverse: true}]};
|
page = {index: 0, count: 6, order: [{field: "date", reverse: true}]};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function refresh():Void {
|
public function refresh():Void {
|
||||||
loading.promise = source.getPage(page).then(data -> this.data = data);
|
loading.promise = source.getPage(page).then(data -> this.data = data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reset():Void {
|
public function reset():Void {
|
||||||
page.index = 0;
|
page.index = 0;
|
||||||
data = {
|
data = {
|
||||||
page: page,
|
page: page,
|
||||||
total: 0,
|
total: 0,
|
||||||
data: [],
|
data: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,98 +10,96 @@ import hw.view.text.TextView;
|
|||||||
import promhx.Promise;
|
import promhx.Promise;
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
NONE;
|
NONE;
|
||||||
LOADING;
|
LOADING;
|
||||||
ERROR(error:Dynamic);
|
ERROR(error:Dynamic);
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoadingWrapper {
|
class LoadingWrapper {
|
||||||
public var promise(null, set):Promise<Dynamic>;
|
public var promise(null, set):Promise<Dynamic>;
|
||||||
|
|
||||||
private function set_promise(value:Promise<Dynamic>):Promise<Dynamic> {
|
private function set_promise(value:Promise<Dynamic>):Promise<Dynamic> {
|
||||||
state = LOADING;
|
state = LOADING;
|
||||||
value
|
value.then(_ -> state = NONE).catchError(error -> state = ERROR(error));
|
||||||
.then(_ -> state = NONE)
|
return value;
|
||||||
.catchError(error -> state = ERROR(error));
|
}
|
||||||
return value;
|
|
||||||
|
public var state(default, set):State;
|
||||||
|
|
||||||
|
private function set_state(value:State):State {
|
||||||
|
if (state != value) {
|
||||||
|
state = value;
|
||||||
|
switch state {
|
||||||
|
case NONE:
|
||||||
|
overlay = null;
|
||||||
|
case LOADING:
|
||||||
|
overlay = loadingView;
|
||||||
|
case ERROR(error):
|
||||||
|
L.e("wrapper", "", error);
|
||||||
|
cast(errorView, TextView).text = Std.string(error);
|
||||||
|
overlay = errorView;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
public var state(default, set):State;
|
private var view:IGroupView;
|
||||||
|
|
||||||
private function set_state(value:State):State {
|
private var overlay(default, set):IView<Dynamic>;
|
||||||
if (state != value) {
|
|
||||||
state = value;
|
private function set_overlay(value:IView<Dynamic>):IView<Dynamic> {
|
||||||
switch state {
|
if (overlay != null && view.containsView(overlay)) {
|
||||||
case NONE:
|
view.removeView(overlay);
|
||||||
overlay = null;
|
|
||||||
case LOADING:
|
|
||||||
overlay = loadingView;
|
|
||||||
case ERROR(error):
|
|
||||||
L.e("wrapper", "", error);
|
|
||||||
cast(errorView, TextView).text = Std.string(error);
|
|
||||||
overlay = errorView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
}
|
||||||
|
overlay = value;
|
||||||
private var view:IGroupView;
|
if (overlay != null) {
|
||||||
|
view.addView(overlay);
|
||||||
private var overlay(default, set):IView<Dynamic>;
|
|
||||||
|
|
||||||
private function set_overlay(value:IView<Dynamic>):IView<Dynamic> {
|
|
||||||
if (overlay != null && view.containsView(overlay)) {
|
|
||||||
view.removeView(overlay);
|
|
||||||
}
|
|
||||||
overlay = value;
|
|
||||||
if (overlay != null) {
|
|
||||||
view.addView(overlay);
|
|
||||||
}
|
|
||||||
return overlay;
|
|
||||||
}
|
}
|
||||||
|
return overlay;
|
||||||
|
}
|
||||||
|
|
||||||
private var loadingView(get, null):IView<Dynamic>;
|
private var loadingView(get, null):IView<Dynamic>;
|
||||||
|
|
||||||
private function get_loadingView():IView<Dynamic> {
|
private function get_loadingView():IView<Dynamic> {
|
||||||
if (loadingView == null) {
|
if (loadingView == null) {
|
||||||
loadingView = buildLoadingView();
|
loadingView = buildLoadingView();
|
||||||
}
|
|
||||||
return loadingView;
|
|
||||||
}
|
}
|
||||||
|
return loadingView;
|
||||||
|
}
|
||||||
|
|
||||||
private var errorView(get, null):IView<Dynamic>;
|
private var errorView(get, null):IView<Dynamic>;
|
||||||
|
|
||||||
private function get_errorView():IView<Dynamic> {
|
private function get_errorView():IView<Dynamic> {
|
||||||
if (errorView == null) {
|
if (errorView == null) {
|
||||||
errorView = buildErrorView();
|
errorView = buildErrorView();
|
||||||
}
|
|
||||||
return errorView;
|
|
||||||
}
|
}
|
||||||
|
return errorView;
|
||||||
|
}
|
||||||
|
|
||||||
public function new(view:IGroupView) {
|
public function new(view:IGroupView) {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildLoadingView():IView<Dynamic> {
|
private function buildLoadingView():IView<Dynamic> {
|
||||||
var view = new LabelView();
|
var view = new LabelView();
|
||||||
view.geometry.position = ABSOLUTE;
|
view.geometry.position = ABSOLUTE;
|
||||||
view.geometry.hAlign = CENTER;
|
view.geometry.hAlign = CENTER;
|
||||||
view.geometry.vAlign = MIDDLE;
|
view.geometry.vAlign = MIDDLE;
|
||||||
view.text = "Loading...";
|
view.text = "Loading...";
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildErrorView():IView<Dynamic> {
|
private function buildErrorView():IView<Dynamic> {
|
||||||
var view = new TextView();
|
var view = new TextView();
|
||||||
view.geometry.position = ABSOLUTE;
|
view.geometry.position = ABSOLUTE;
|
||||||
view.geometry.hAlign = CENTER;
|
view.geometry.hAlign = CENTER;
|
||||||
view.geometry.vAlign = MIDDLE;
|
view.geometry.vAlign = MIDDLE;
|
||||||
view.geometry.stretch = true;
|
view.geometry.stretch = true;
|
||||||
view.style = "text.error";
|
view.style = "text.error";
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reset():Void {
|
public function reset():Void {
|
||||||
overlay = null;
|
overlay = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,76 +7,75 @@ import hw.view.form.ToggleButtonView;
|
|||||||
import hw.view.layout.TailLayout;
|
import hw.view.layout.TailLayout;
|
||||||
|
|
||||||
enum PaginatorAction {
|
enum PaginatorAction {
|
||||||
START;
|
START;
|
||||||
JUMP(distance:Int);
|
JUMP(distance:Int);
|
||||||
INDEX(index:Int);
|
INDEX(index:Int);
|
||||||
END;
|
END;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PaginatorButtonView extends ToggleButtonView {
|
class PaginatorButtonView extends ToggleButtonView {
|
||||||
public var action(default, set):PaginatorAction;
|
public var action(default, set):PaginatorAction;
|
||||||
|
|
||||||
private function set_action(value:PaginatorAction):PaginatorAction {
|
private function set_action(value:PaginatorAction):PaginatorAction {
|
||||||
action = value;
|
action = value;
|
||||||
text = switch action {
|
text = switch action {
|
||||||
case INDEX(index): Std.string(index);
|
case INDEX(index): Std.string(index);
|
||||||
case x: Std.string(x);
|
case x: Std.string(x);
|
||||||
}
|
|
||||||
return action;
|
|
||||||
}
|
}
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
public static function factory(index:Int, action:PaginatorAction):PaginatorButtonView {
|
public static function factory(index:Int, action:PaginatorAction):PaginatorButtonView {
|
||||||
var result = new PaginatorButtonView();
|
var result = new PaginatorButtonView();
|
||||||
result.action = action;
|
result.action = action;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PaginatorView extends DataView<PaginatorAction, PaginatorButtonView> {
|
class PaginatorView extends DataView<PaginatorAction, PaginatorButtonView> {
|
||||||
|
public var page(default, set):DataPage<Dynamic>;
|
||||||
|
public var onPageSelect(default, null):Signal<Int>;
|
||||||
|
|
||||||
public var page(default, set):DataPage<Dynamic>;
|
private function set_page(value:DataPage<Dynamic>):DataPage<Dynamic> {
|
||||||
public var onPageSelect(default, null):Signal<Int>;
|
page = value;
|
||||||
|
refresh();
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
private function set_page(value:DataPage<Dynamic>):DataPage<Dynamic> {
|
public function new() {
|
||||||
page = value;
|
super(new TailLayout());
|
||||||
refresh();
|
layout.margin = 3;
|
||||||
return page;
|
onPageSelect = new Signal();
|
||||||
|
factory = PaginatorButtonView.factory;
|
||||||
|
onDataSelect.connect(onAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onAction(action:PaginatorAction):Void {
|
||||||
|
onPageSelect.emit(switch action {
|
||||||
|
case START: 0;
|
||||||
|
case INDEX(index): index;
|
||||||
|
case JUMP(distance): page.page.index + distance;
|
||||||
|
case END: Std.int((page.total - 1) / page.page.count);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function refresh():Void {
|
||||||
|
var total:Int = Std.int((page.total - 1) / page.page.count);
|
||||||
|
var start:Int = Std.int(Math.max(page.page.index - 2, 0));
|
||||||
|
var end:Int = Std.int(Math.min(page.page.index + 2, total));
|
||||||
|
var data = [for (index in start...end + 1) INDEX(index)];
|
||||||
|
if (start > 0) {
|
||||||
|
data.unshift(START);
|
||||||
}
|
}
|
||||||
|
if (end < total) {
|
||||||
public function new() {
|
data.push(END);
|
||||||
super(new TailLayout());
|
|
||||||
layout.margin = 3;
|
|
||||||
onPageSelect = new Signal();
|
|
||||||
factory = PaginatorButtonView.factory;
|
|
||||||
onDataSelect.connect(onAction);
|
|
||||||
}
|
}
|
||||||
|
this.data = data;
|
||||||
private function onAction(action:PaginatorAction):Void {
|
for (button in dataViews) {
|
||||||
onPageSelect.emit(switch action {
|
button.on = switch button.action {
|
||||||
case START: 0;
|
case INDEX(index): index == page.page.index;
|
||||||
case INDEX(index): index;
|
case x: false;
|
||||||
case JUMP(distance): page.page.index + distance;
|
}
|
||||||
case END: Std.int((page.total - 1) / page.page.count);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private function refresh():Void {
|
|
||||||
var total:Int = Std.int((page.total - 1) / page.page.count);
|
|
||||||
var start:Int = Std.int(Math.max(page.page.index - 2, 0));
|
|
||||||
var end:Int = Std.int(Math.min(page.page.index + 2, total));
|
|
||||||
var data = [for (index in start...end + 1) INDEX(index)];
|
|
||||||
if (start > 0) {
|
|
||||||
data.unshift(START);
|
|
||||||
}
|
|
||||||
if (end < total) {
|
|
||||||
data.push(END);
|
|
||||||
}
|
|
||||||
this.data = data;
|
|
||||||
for (button in dataViews) {
|
|
||||||
button.on = switch button.action {
|
|
||||||
case INDEX(index): index == page.page.index;
|
|
||||||
case x: false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import com.hurlant.crypto.prng.Random;
|
|||||||
import com.hurlant.crypto.extra.UUID;
|
import com.hurlant.crypto.extra.UUID;
|
||||||
|
|
||||||
class IdUtil {
|
class IdUtil {
|
||||||
public static function generate():String {
|
public static function generate():String {
|
||||||
return UUID.generateRandom(new Random()).toString().split("-").shift();
|
return UUID.generateRandom(new Random()).toString().split("-").shift();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,28 +9,19 @@ import ru.m.puzzlez.proto.event.GameEvent;
|
|||||||
import ru.m.puzzlez.proto.game.GameState;
|
import ru.m.puzzlez.proto.game.GameState;
|
||||||
|
|
||||||
class EventUtil {
|
class EventUtil {
|
||||||
|
public static function start(state:GameState, resume:Bool):GameEvent {
|
||||||
|
return new GameEvent().setStart(new GameStart().setState(state).setResume(resume));
|
||||||
|
}
|
||||||
|
|
||||||
public static function start(state:GameState, resume: Bool):GameEvent {
|
public static function complete():GameEvent {
|
||||||
return new GameEvent().setStart(new GameStart()
|
return new GameEvent().setComplete(new GameComplete());
|
||||||
.setState(state)
|
}
|
||||||
.setResume(resume)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function complete():GameEvent {
|
public static function action(action:GameAction):GameEvent {
|
||||||
return new GameEvent().setComplete(new GameComplete());
|
return new GameEvent().setAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function action(action:GameAction):GameEvent {
|
public static function change(part:Part):GameEvent {
|
||||||
return new GameEvent().setAction(action);
|
return new GameEvent().setChange(new GameChange().setPartId(part.id).setLocation(part.location).setPosition(part.position).setPlayerId(part.playerId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function change(part:Part):GameEvent {
|
|
||||||
return new GameEvent().setChange(new GameChange()
|
|
||||||
.setPartId(part.id)
|
|
||||||
.setLocation(part.location)
|
|
||||||
.setPosition(part.position)
|
|
||||||
.setPlayerId(part.playerId)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,109 +12,108 @@ import ru.m.puzzlez.proto.game.PartLocation;
|
|||||||
import ru.m.puzzlez.wrap.PointExt;
|
import ru.m.puzzlez.wrap.PointExt;
|
||||||
|
|
||||||
class Game implements IGame {
|
class Game implements IGame {
|
||||||
public var state(default, null):GameState;
|
public var state(default, null):GameState;
|
||||||
public var events(default, null):Signal<GameEvent>;
|
public var events(default, null):Signal<GameEvent>;
|
||||||
|
|
||||||
private var partsById:Map<Int, Part>;
|
private var partsById:Map<Int, Part>;
|
||||||
|
|
||||||
public function new(state:GameState) {
|
public function new(state:GameState) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
partsById = new Map();
|
partsById = new Map();
|
||||||
for (part in state.parts) {
|
for (part in state.parts) {
|
||||||
partsById[part.id] = part;
|
partsById[part.id] = part;
|
||||||
}
|
|
||||||
events = new Signal();
|
|
||||||
events.connect(onGameEvent);
|
|
||||||
}
|
}
|
||||||
|
events = new Signal();
|
||||||
|
events.connect(onGameEvent);
|
||||||
|
}
|
||||||
|
|
||||||
public function action(action:GameAction):Void {
|
public function action(action:GameAction):Void {
|
||||||
events.emit(EventUtil.action(action));
|
events.emit(EventUtil.action(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function start():Void {
|
||||||
|
switch state.status {
|
||||||
|
case GameStatus.READY:
|
||||||
|
shuffle();
|
||||||
|
state.status = GameStatus.STARTED;
|
||||||
|
events.emit(EventUtil.start(state, false));
|
||||||
|
case _:
|
||||||
|
events.emit(EventUtil.start(state, true));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function start():Void {
|
private function shuffle():Void {
|
||||||
switch state.status {
|
for (part in state.parts) {
|
||||||
case GameStatus.READY:
|
switch part.location {
|
||||||
shuffle();
|
case PartLocation.TABLE:
|
||||||
state.status = GameStatus.STARTED;
|
var bound = part.rect.width * 0.25;
|
||||||
events.emit(EventUtil.start(state, false));
|
var x = bound + Math.random() * (state.preset.tableRect.width - part.rect.width - bound * 2);
|
||||||
|
var y = bound + Math.random() * (state.preset.tableRect.height - part.rect.height - bound * 2);
|
||||||
|
part.position = new PointExt(x, y);
|
||||||
|
case _:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function distance(a:PointExt, b:PointExt):Float {
|
||||||
|
var diff:Point = a.subtract(b);
|
||||||
|
return Math.abs(diff.x) + Math.abs(diff.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkIsComplete():Bool {
|
||||||
|
for (part in partsById) {
|
||||||
|
switch part.location {
|
||||||
|
case PartLocation.IMAGE:
|
||||||
|
case _:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onGameEvent(event:GameEvent):Void {
|
||||||
|
if (event.hasAction()) {
|
||||||
|
var part:Part = partsById[event.action.partId];
|
||||||
|
switch event.action.action {
|
||||||
|
case Action.TAKE:
|
||||||
|
switch part.location {
|
||||||
|
case PartLocation.TABLE:
|
||||||
|
part.location = PartLocation.HAND;
|
||||||
|
part.playerId = event.action.playerId;
|
||||||
|
events.emit(EventUtil.change(part));
|
||||||
case _:
|
case _:
|
||||||
events.emit(EventUtil.start(state, true));
|
}
|
||||||
}
|
case Action.MOVE:
|
||||||
}
|
switch part.location {
|
||||||
|
case PartLocation.HAND if (event.action.playerId == part.playerId):
|
||||||
private function shuffle():Void {
|
part.position = event.action.position;
|
||||||
for (part in state.parts) {
|
events.emit(EventUtil.change(part));
|
||||||
switch part.location {
|
case _:
|
||||||
case PartLocation.TABLE:
|
}
|
||||||
var bound = part.rect.width * 0.25;
|
case Action.PUT:
|
||||||
var x = bound + Math.random() * (state.preset.tableRect.width - part.rect.width - bound * 2);
|
part.playerId = null;
|
||||||
var y = bound + Math.random() * (state.preset.tableRect.height - part.rect.height - bound * 2);
|
var target = new PointExt(state.preset.imageRect.x,
|
||||||
part.position = new PointExt(x, y);
|
state.preset.imageRect.y).add(new PointExt(part.point.x * part.rect.width, part.point.y * part.rect.height));
|
||||||
case _:
|
var d = distance(target, event.action.position);
|
||||||
|
if (d < 70) {
|
||||||
|
part.location = PartLocation.IMAGE;
|
||||||
|
events.emit(EventUtil.change(part));
|
||||||
|
if (checkIsComplete()) {
|
||||||
|
state.status = GameStatus.COMPLETE;
|
||||||
|
events.emit(EventUtil.complete());
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
part.location = PartLocation.TABLE;
|
||||||
|
part.position = event.action.position;
|
||||||
|
events.emit(EventUtil.change(part));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static function distance(a:PointExt, b:PointExt):Float {
|
public function stop():Void {}
|
||||||
var diff:Point = a.subtract(b);
|
|
||||||
return Math.abs(diff.x) + Math.abs(diff.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function checkIsComplete():Bool {
|
public function dispose():Void {
|
||||||
for (part in partsById) {
|
events.dispose();
|
||||||
switch part.location {
|
}
|
||||||
case PartLocation.IMAGE:
|
|
||||||
case _: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function onGameEvent(event:GameEvent):Void {
|
|
||||||
if (event.hasAction()) {
|
|
||||||
var part:Part = partsById[event.action.partId];
|
|
||||||
switch event.action.action {
|
|
||||||
case Action.TAKE:
|
|
||||||
switch part.location {
|
|
||||||
case PartLocation.TABLE:
|
|
||||||
part.location = PartLocation.HAND;
|
|
||||||
part.playerId = event.action.playerId;
|
|
||||||
events.emit(EventUtil.change(part));
|
|
||||||
case _:
|
|
||||||
}
|
|
||||||
case Action.MOVE:
|
|
||||||
switch part.location {
|
|
||||||
case PartLocation.HAND if (event.action.playerId == part.playerId):
|
|
||||||
part.position = event.action.position;
|
|
||||||
events.emit(EventUtil.change(part));
|
|
||||||
case _:
|
|
||||||
}
|
|
||||||
case Action.PUT:
|
|
||||||
part.playerId = null;
|
|
||||||
var target = new PointExt(state.preset.imageRect.x, state.preset.imageRect.y)
|
|
||||||
.add(new PointExt(part.point.x * part.rect.width, part.point.y * part.rect.height));
|
|
||||||
var d = distance(target, event.action.position);
|
|
||||||
if (d < 70) {
|
|
||||||
part.location = PartLocation.IMAGE;
|
|
||||||
events.emit(EventUtil.change(part));
|
|
||||||
if (checkIsComplete()) {
|
|
||||||
state.status = GameStatus.COMPLETE;
|
|
||||||
events.emit(EventUtil.complete());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
part.location = PartLocation.TABLE;
|
|
||||||
part.position = event.action.position;
|
|
||||||
events.emit(EventUtil.change(part));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function stop():Void {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function dispose():Void {
|
|
||||||
events.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,139 +15,125 @@ import ru.m.puzzlez.proto.game.PartBounds;
|
|||||||
import ru.m.puzzlez.proto.game.PartLocation;
|
import ru.m.puzzlez.proto.game.PartLocation;
|
||||||
|
|
||||||
class BoundsMap {
|
class BoundsMap {
|
||||||
|
private var grid:IntPoint;
|
||||||
|
private var bounds:Array<PartBound>;
|
||||||
|
|
||||||
private var grid:IntPoint;
|
public function new(grid:IntPoint) {
|
||||||
private var bounds:Array<PartBound>;
|
this.grid = grid;
|
||||||
|
bounds = [];
|
||||||
public function new(grid:IntPoint) {
|
for (x in 0...grid.x + 1) {
|
||||||
this.grid = grid;
|
for (y in 0...grid.y + 1) {
|
||||||
bounds = [];
|
bounds.push(buildPartBound());
|
||||||
for (x in 0...grid.x + 1) {
|
bounds.push(buildPartBound());
|
||||||
for (y in 0...grid.y + 1) {
|
}
|
||||||
bounds.push(buildPartBound());
|
|
||||||
bounds.push(buildPartBound());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function buildBound():Int {
|
public function buildBound():Int {
|
||||||
return Math.random() > 0.5 ? BoundType.OUT : BoundType.IN;
|
return Math.random() > 0.5 ? BoundType.OUT : BoundType.IN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildPartBound():PartBound {
|
public function buildPartBound():PartBound {
|
||||||
return new PartBound().setSpike(buildBound()).setSide(buildBound());
|
return new PartBound().setSpike(buildBound()).setSide(buildBound());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildNonePartBound():PartBound {
|
public function buildNonePartBound():PartBound {
|
||||||
return new PartBound().setSpike(BoundType.NONE).setSide(BoundType.NONE);
|
return new PartBound().setSpike(BoundType.NONE).setSide(BoundType.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function revert(type:Int):Int {
|
public function revert(type:Int):Int {
|
||||||
return switch type {
|
return switch type {
|
||||||
case BoundType.IN: BoundType.OUT;
|
case BoundType.IN: BoundType.OUT;
|
||||||
case BoundType.OUT: BoundType.IN;
|
case BoundType.OUT: BoundType.IN;
|
||||||
case _: BoundType.NONE;
|
case _: BoundType.NONE;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getBound(x:Int, y:Int, side:Side):PartBound {
|
public function getBound(x:Int, y:Int, side:Side):PartBound {
|
||||||
var index = switch side {
|
var index = switch side {
|
||||||
case TOP: x + (grid.x + 1) * (y * 2);
|
case TOP: x + (grid.x + 1) * (y * 2);
|
||||||
case LEFT: x + (grid.x + 1) * (y + 1);
|
case LEFT: x + (grid.x + 1) * (y + 1);
|
||||||
case RIGHT: x + (grid.x + 1) * (y + 1) + 1;
|
case RIGHT: x + (grid.x + 1) * (y + 1) + 1;
|
||||||
case BOTTOM: x + (grid.x + 1) * (y * 2) + (grid.x + 1) * 2;
|
case BOTTOM: x + (grid.x + 1) * (y * 2) + (grid.x + 1) * 2;
|
||||||
}
|
|
||||||
return switch side {
|
|
||||||
case TOP | LEFT: new PartBound().setSpike(revert(bounds[index].spike)).setSide(revert(bounds[index].side));
|
|
||||||
case _: bounds[index];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return switch side {
|
||||||
|
case TOP | LEFT: new PartBound().setSpike(revert(bounds[index].spike)).setSide(revert(bounds[index].side));
|
||||||
|
case _: bounds[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GameUtil {
|
class GameUtil {
|
||||||
|
private static inline var MAX_SIZE = 20;
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
private static function normilizeSize(size:Int, maxValue:Int = MAX_SIZE):Int {
|
public static function buildPreset(image:ImageId, width:Int = 8, height:Int = MAX_SIZE):GamePreset {
|
||||||
return Math.isNaN(size) ? maxValue : size < 1 ? 1 : size > maxValue ? maxValue : size;
|
width = normilizeSize(width);
|
||||||
}
|
height = normilizeSize(height);
|
||||||
|
var offsetX = 500;
|
||||||
|
var offsetY = 200;
|
||||||
|
var imageSize = 1024;
|
||||||
|
var s = width / height;
|
||||||
|
var imageRect = new Rectangle().setX(offsetX).setY(offsetY).setWidth(imageSize).setHeight(imageSize / s);
|
||||||
|
var tableRect = new Rectangle().setX(0)
|
||||||
|
.setY(0)
|
||||||
|
.setWidth(imageRect.width + offsetX * 2)
|
||||||
|
.setHeight(imageRect.height + offsetY * 2);
|
||||||
|
return new GamePreset().setImage(image)
|
||||||
|
.setGrid(new IntPoint().setX(width).setY(height))
|
||||||
|
.setTableRect(tableRect)
|
||||||
|
.setImageRect(imageRect);
|
||||||
|
}
|
||||||
|
|
||||||
public static function buildPreset(image: ImageId, width:Int = 8, height:Int = MAX_SIZE):GamePreset {
|
public static function buildState(preset:GamePreset):GameState {
|
||||||
width = normilizeSize(width);
|
var parts:Array<Part> = [];
|
||||||
height = normilizeSize(height);
|
var partWidth = preset.imageRect.width / preset.grid.x;
|
||||||
var offsetX = 500;
|
var partHeight = preset.imageRect.height / preset.grid.y;
|
||||||
var offsetY = 200;
|
var position = new Point().setX(preset.imageRect.x).setY(preset.imageRect.y);
|
||||||
var imageSize = 1024;
|
var boundsMap = new BoundsMap(preset.grid);
|
||||||
var s = width / height;
|
for (y in 0...preset.grid.y) {
|
||||||
var imageRect = new Rectangle()
|
for (x in 0...preset.grid.x) {
|
||||||
.setX(offsetX)
|
var bounds:PartBounds = new PartBounds().setLeft(boundsMap.getBound(x, y, LEFT))
|
||||||
.setY(offsetY)
|
.setRight(boundsMap.getBound(x, y, RIGHT))
|
||||||
.setWidth(imageSize)
|
.setTop(boundsMap.getBound(x, y, TOP))
|
||||||
.setHeight(imageSize / s);
|
.setBottom(boundsMap.getBound(x, y, BOTTOM));
|
||||||
var tableRect = new Rectangle()
|
if (x == 0) {
|
||||||
.setX(0)
|
bounds.left = boundsMap.buildNonePartBound();
|
||||||
.setY(0)
|
|
||||||
.setWidth(imageRect.width + offsetX * 2)
|
|
||||||
.setHeight(imageRect.height + offsetY * 2);
|
|
||||||
return new GamePreset()
|
|
||||||
.setImage(image)
|
|
||||||
.setGrid(new IntPoint().setX(width).setY(height))
|
|
||||||
.setTableRect(tableRect)
|
|
||||||
.setImageRect(imageRect);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function buildState(preset:GamePreset):GameState {
|
|
||||||
var parts:Array<Part> = [];
|
|
||||||
var partWidth = preset.imageRect.width / preset.grid.x;
|
|
||||||
var partHeight = preset.imageRect.height / preset.grid.y;
|
|
||||||
var position = new Point().setX(preset.imageRect.x).setY(preset.imageRect.y);
|
|
||||||
var boundsMap = new BoundsMap(preset.grid);
|
|
||||||
for (y in 0...preset.grid.y) {
|
|
||||||
for (x in 0...preset.grid.x) {
|
|
||||||
var bounds:PartBounds = new PartBounds()
|
|
||||||
.setLeft(boundsMap.getBound(x, y, LEFT))
|
|
||||||
.setRight(boundsMap.getBound(x, y, RIGHT))
|
|
||||||
.setTop(boundsMap.getBound(x, y, TOP))
|
|
||||||
.setBottom(boundsMap.getBound(x, y, BOTTOM));
|
|
||||||
if (x == 0) {
|
|
||||||
bounds.left = boundsMap.buildNonePartBound();
|
|
||||||
}
|
|
||||||
if (y == 0) {
|
|
||||||
bounds.top = boundsMap.buildNonePartBound();
|
|
||||||
}
|
|
||||||
if (x == preset.grid.x - 1) {
|
|
||||||
bounds.right = boundsMap.buildNonePartBound();
|
|
||||||
}
|
|
||||||
if (y == preset.grid.y - 1) {
|
|
||||||
bounds.bottom = boundsMap.buildNonePartBound();
|
|
||||||
}
|
|
||||||
var id = (x << 16) + y;
|
|
||||||
var position = new Point().setX(position.x + x * partWidth).setY(position.y + y * partHeight);
|
|
||||||
parts.push(new Part()
|
|
||||||
.setId(id)
|
|
||||||
.setPoint(new IntPoint().setX(x).setY(y))
|
|
||||||
.setLocation(PartLocation.TABLE)
|
|
||||||
.setPosition(position)
|
|
||||||
.setBounds(bounds)
|
|
||||||
.setRect(new Rectangle().setX(0).setY(0).setWidth(partWidth).setHeight(partHeight))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return new GameState()
|
if (y == 0) {
|
||||||
.setId(IdUtil.generate())
|
bounds.top = boundsMap.buildNonePartBound();
|
||||||
.setStatus(GameStatus.READY)
|
|
||||||
.setPreset(preset)
|
|
||||||
.setParts(parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function calcProgress(state:GameState):{complete:Int, total:Int} {
|
|
||||||
var complete = 0;
|
|
||||||
for (part in state.parts) {
|
|
||||||
switch part.location {
|
|
||||||
case PartLocation.IMAGE: complete++;
|
|
||||||
case _:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {complete:complete, total:state.parts.length};
|
if (x == preset.grid.x - 1) {
|
||||||
|
bounds.right = boundsMap.buildNonePartBound();
|
||||||
|
}
|
||||||
|
if (y == preset.grid.y - 1) {
|
||||||
|
bounds.bottom = boundsMap.buildNonePartBound();
|
||||||
|
}
|
||||||
|
var id = (x << 16) + y;
|
||||||
|
var position = new Point().setX(position.x + x * partWidth).setY(position.y + y * partHeight);
|
||||||
|
parts.push(new Part().setId(id)
|
||||||
|
.setPoint(new IntPoint().setX(x).setY(y))
|
||||||
|
.setLocation(PartLocation.TABLE)
|
||||||
|
.setPosition(position)
|
||||||
|
.setBounds(bounds)
|
||||||
|
.setRect(new Rectangle().setX(0).setY(0).setWidth(partWidth).setHeight(partHeight)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return new GameState().setId(IdUtil.generate()).setStatus(GameStatus.READY).setPreset(preset).setParts(parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function calcProgress(state:GameState):{complete:Int, total:Int} {
|
||||||
|
var complete = 0;
|
||||||
|
for (part in state.parts) {
|
||||||
|
switch part.location {
|
||||||
|
case PartLocation.IMAGE:
|
||||||
|
complete++;
|
||||||
|
case _:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {complete: complete, total: state.parts.length};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import ru.m.puzzlez.proto.event.GameEvent;
|
|||||||
import ru.m.puzzlez.proto.game.GameState;
|
import ru.m.puzzlez.proto.game.GameState;
|
||||||
|
|
||||||
interface IGame {
|
interface IGame {
|
||||||
public var state(default, null):GameState;
|
public var state(default, null):GameState;
|
||||||
public var events(default, null):Signal<GameEvent>;
|
public var events(default, null):Signal<GameEvent>;
|
||||||
|
|
||||||
public function action(action:GameAction):Void;
|
public function action(action:GameAction):Void;
|
||||||
|
|
||||||
public function start():Void;
|
public function start():Void;
|
||||||
|
|
||||||
public function stop():Void;
|
public function stop():Void;
|
||||||
|
|
||||||
public function dispose():Void;
|
public function dispose():Void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package ru.m.puzzlez.image;
|
package ru.m.puzzlez.image;
|
||||||
|
|
||||||
enum Side {
|
enum Side {
|
||||||
TOP;
|
TOP;
|
||||||
LEFT;
|
LEFT;
|
||||||
RIGHT;
|
RIGHT;
|
||||||
BOTTOM;
|
BOTTOM;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,36 +6,25 @@ import flash.geom.Point as FlashPoint;
|
|||||||
#end
|
#end
|
||||||
|
|
||||||
abstract PointExt(Point) from Point to Point {
|
abstract PointExt(Point) from Point to Point {
|
||||||
|
public function new(x:Float = 0, y:Float = 0) {
|
||||||
|
this = new Point().setX(x).setY(y);
|
||||||
|
}
|
||||||
|
|
||||||
public function new(x: Float = 0, y: Float = 0) {
|
public function clone():PointExt {
|
||||||
this = new Point()
|
return new Point().setX(this.x).setY(this.y);
|
||||||
.setX(x)
|
}
|
||||||
.setY(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clone():PointExt {
|
public function add(point:Point):PointExt {
|
||||||
return new Point()
|
return new Point().setX(this.x + point.x).setY(this.y + point.y);
|
||||||
.setX(this.x)
|
}
|
||||||
.setY(this.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function add(point:Point):PointExt {
|
public function subtract(point:Point):PointExt {
|
||||||
return new Point()
|
return new Point().setX(this.x - point.x).setY(this.y - point.y);
|
||||||
.setX(this.x + point.x)
|
}
|
||||||
.setY(this.y + point.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function subtract(point:Point):PointExt {
|
#if app
|
||||||
return new Point()
|
@:from public static function fromFlashPoint(point:FlashPoint):PointExt {
|
||||||
.setX(this.x - point.x)
|
return new Point().setX(point.x).setY(point.y);
|
||||||
.setY(this.y - point.y);
|
}
|
||||||
}
|
#end
|
||||||
|
|
||||||
#if app
|
|
||||||
@:from public static function fromFlashPoint(point:FlashPoint):PointExt {
|
|
||||||
return new Point()
|
|
||||||
.setX(point.x)
|
|
||||||
.setY(point.y);
|
|
||||||
}
|
|
||||||
#end
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,47 +4,37 @@ import ru.m.puzzlez.proto.core.Point;
|
|||||||
import ru.m.puzzlez.proto.core.Rectangle;
|
import ru.m.puzzlez.proto.core.Rectangle;
|
||||||
|
|
||||||
abstract RectangleExt(Rectangle) from Rectangle to Rectangle {
|
abstract RectangleExt(Rectangle) from Rectangle to Rectangle {
|
||||||
public var left(get, never):Float;
|
public var left(get, never):Float;
|
||||||
public var right(get, never):Float;
|
public var right(get, never):Float;
|
||||||
public var top(get, never):Float;
|
public var top(get, never):Float;
|
||||||
public var bottom(get, never):Float;
|
public var bottom(get, never):Float;
|
||||||
public var size(get, never):Point;
|
public var size(get, never):Point;
|
||||||
|
|
||||||
public function new(x:Float = 0, y:Float = 0, width:Float = 0, height:Float = 0) {
|
public function new(x:Float = 0, y:Float = 0, width:Float = 0, height:Float = 0) {
|
||||||
this = new Rectangle()
|
this = new Rectangle().setX(x).setY(y).setWidth(width).setHeight(height);
|
||||||
.setX(x)
|
}
|
||||||
.setY(y)
|
|
||||||
.setWidth(width)
|
|
||||||
.setHeight(height);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function get_left():Float {
|
private function get_left():Float {
|
||||||
return this.x;
|
return this.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_right():Float {
|
private function get_right():Float {
|
||||||
return this.x + this.width;
|
return this.x + this.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_top():Float {
|
private function get_top():Float {
|
||||||
return this.y;
|
return this.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_bottom():Float {
|
private function get_bottom():Float {
|
||||||
return this.y + this.height;
|
return this.y + this.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_size():Point {
|
private function get_size():Point {
|
||||||
return new Point()
|
return new Point().setX(this.width).setY(this.height);
|
||||||
.setX(this.width)
|
}
|
||||||
.setY(this.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clone():Rectangle {
|
public function clone():Rectangle {
|
||||||
return new Rectangle()
|
return new Rectangle().setX(this.x).setY(this.y).setWidth(this.width).setHeight(this.height);
|
||||||
.setX(this.x)
|
}
|
||||||
.setY(this.y)
|
|
||||||
.setWidth(this.width)
|
|
||||||
.setHeight(this.height);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,122 +24,120 @@ import ru.m.puzzlez.proto.pack.Response;
|
|||||||
import sys.net.Socket;
|
import sys.net.Socket;
|
||||||
|
|
||||||
class GameSession extends ProtoSession<Response, Request> {
|
class GameSession extends ProtoSession<Response, Request> {
|
||||||
private static inline var TAG = "Session";
|
private static inline var TAG = "Session";
|
||||||
|
|
||||||
public static var gamesById(default, null):Map<String, Game> = new Map();
|
public static var gamesById(default, null):Map<String, Game> = new Map();
|
||||||
public static var sessionsById(default, null):Map<Int, GameSession> = new Map();
|
public static var sessionsById(default, null):Map<Int, GameSession> = new Map();
|
||||||
|
|
||||||
public var user(default, null):User;
|
public var user(default, null):User;
|
||||||
public var game(default, null):Game;
|
public var game(default, null):Game;
|
||||||
|
|
||||||
private var subscribed:Bool;
|
private var subscribed:Bool;
|
||||||
private var tag(get, never):String;
|
private var tag(get, never):String;
|
||||||
|
|
||||||
private function get_tag():String {
|
private function get_tag():String {
|
||||||
return '[${id}|${user == null ? '-' : user.uuid}|${game == null ? '-' : game.state.id}]';
|
return '[${id}|${user == null ? '-' : user.uuid}|${game == null ? '-' : game.state.id}]';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function new(socket:Socket) {
|
||||||
|
super(socket, Request);
|
||||||
|
sessionsById.set(id, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendError(code:Int, message:String):Void {
|
||||||
|
send(new Response().setError(new ErrorResponse().setCode(code).setMessage(message)));
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function send(packet:Response):Void {
|
||||||
|
#if proto_debug L.d(TAG, '$tag send: ${packet}'); #end
|
||||||
|
try {
|
||||||
|
super.send(packet);
|
||||||
|
} catch (error:Dynamic) {
|
||||||
|
L.e(TAG, '$tag send ', error);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function new(socket:Socket) {
|
private function auth(auth:AuthRequest):Void {
|
||||||
super(socket, Request);
|
user = auth.hasUser() ? auth.user : new User().setUuid(IdUtil.generate()).setName('Anonim #${IdUtil.generate()}');
|
||||||
sessionsById.set(id, this);
|
send(new Response().setAuth(new AuthResponse().setUser(user)));
|
||||||
|
send(new Response().setNotification(new NotificationResponse().setGames(Lambda.count(gamesById))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function list(list:GameListRequest):Void {
|
||||||
|
var games = Lambda.array(gamesById);
|
||||||
|
var pageGames = games.slice(list.page * list.count, list.count);
|
||||||
|
send(new Response().setList(new GameListResponse().setPage(list.page)
|
||||||
|
.setCount(list.count)
|
||||||
|
.setTotal(games.length)
|
||||||
|
.setGames(pageGames.map(game -> game.state))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function create(create:GameCreateRequest):Void {
|
||||||
|
var game = new Game(GameUtil.buildState(create.preset).setOnline(true));
|
||||||
|
game.start();
|
||||||
|
gamesById.set(game.state.id, game);
|
||||||
|
join(game.state.id);
|
||||||
|
for (session in sessionsById) {
|
||||||
|
session.send(new Response().setNotification(new NotificationResponse().setGames(Lambda.count(gamesById))));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function sendError(code:Int, message:String):Void {
|
private function join(gameId:String):Void {
|
||||||
send(new Response().setError(new ErrorResponse().setCode(code).setMessage(message)));
|
game = gamesById.get(gameId);
|
||||||
}
|
game.events.connect(onGameEvent);
|
||||||
|
game.events.emit(new GameEvent().setPlayer(new GamePlayer().setPlayer(user).setAction(GamePlayerAction.JOIN)));
|
||||||
|
send(new Response().setJoin(new GameJoinResponse().setGame(game.state)));
|
||||||
|
}
|
||||||
|
|
||||||
override public function send(packet:Response):Void {
|
private function leave():Void {
|
||||||
#if proto_debug L.d(TAG, '$tag send: ${packet}'); #end
|
game.events.disconnect(onGameEvent);
|
||||||
try {
|
game.events.emit(new GameEvent().setPlayer(new GamePlayer().setPlayer(user).setAction(GamePlayerAction.LEAVE)));
|
||||||
super.send(packet);
|
send(new Response().setLeave(new GameLeaveResponse()));
|
||||||
} catch (error:Dynamic) {
|
}
|
||||||
L.e(TAG, '$tag send ', error);
|
|
||||||
|
private function action(action:GameAction):Void {
|
||||||
|
game.events.emit(new GameEvent().setAction(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
override private function onRequest(request:Request):Void {
|
||||||
|
#if proto_debug L.d(TAG, '$tag onRequest: ${request}'); #end
|
||||||
|
try {
|
||||||
|
if (!request.hasAuth() && user == null) {
|
||||||
|
throw "Not Authorized";
|
||||||
|
}
|
||||||
|
if (request.hasAuth()) {
|
||||||
|
auth(request.auth);
|
||||||
|
} else if (request.hasList()) {
|
||||||
|
list(request.list);
|
||||||
|
} else if (request.hasCreate()) {
|
||||||
|
create(request.create);
|
||||||
|
} else if (request.hasJoin()) {
|
||||||
|
join(request.join.gameId);
|
||||||
|
} else if (request.hasLeave()) {
|
||||||
|
leave();
|
||||||
|
} else if (request.hasAction()) {
|
||||||
|
for (action in request.action.actions) {
|
||||||
|
this.action(action);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} catch (error:Dynamic) {
|
||||||
|
L.e(TAG, '$tag onRequest ', error);
|
||||||
|
sendError(500, LoggerUtil.printError(error));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function auth(auth:AuthRequest):Void {
|
private function onGameEvent(event:GameEvent):Void {
|
||||||
user = auth.hasUser() ? auth.user : new User().setUuid(IdUtil.generate()).setName('Anonim #${IdUtil.generate()}');
|
send(new Response().setEvent(new GameEventResponse().setEvents([event])));
|
||||||
send(new Response().setAuth(new AuthResponse().setUser(user)));
|
}
|
||||||
send(new Response().setNotification(new NotificationResponse().setGames(Lambda.count(gamesById))));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function list(list:GameListRequest):Void {
|
override public function disconnect():Void {
|
||||||
var games = Lambda.array(gamesById);
|
L.d(TAG, '$tag disconnect');
|
||||||
var pageGames = games.slice(list.page * list.count, list.count);
|
if (game != null) {
|
||||||
send(new Response().setList(new GameListResponse()
|
leave();
|
||||||
.setPage(list.page)
|
game = null;
|
||||||
.setCount(list.count)
|
|
||||||
.setTotal(games.length)
|
|
||||||
.setGames(pageGames.map(game -> game.state))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function create(create:GameCreateRequest):Void {
|
|
||||||
var game = new Game(GameUtil.buildState(create.preset).setOnline(true));
|
|
||||||
game.start();
|
|
||||||
gamesById.set(game.state.id, game);
|
|
||||||
join(game.state.id);
|
|
||||||
for (session in sessionsById) {
|
|
||||||
session.send(new Response().setNotification(new NotificationResponse().setGames(Lambda.count(gamesById))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function join(gameId:String):Void {
|
|
||||||
game = gamesById.get(gameId);
|
|
||||||
game.events.connect(onGameEvent);
|
|
||||||
game.events.emit(new GameEvent().setPlayer(new GamePlayer().setPlayer(user).setAction(GamePlayerAction.JOIN)));
|
|
||||||
send(new Response().setJoin(new GameJoinResponse().setGame(game.state)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function leave():Void {
|
|
||||||
game.events.disconnect(onGameEvent);
|
|
||||||
game.events.emit(new GameEvent().setPlayer(new GamePlayer().setPlayer(user).setAction(GamePlayerAction.LEAVE)));
|
|
||||||
send(new Response().setLeave(new GameLeaveResponse()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function action(action:GameAction):Void {
|
|
||||||
game.events.emit(new GameEvent().setAction(action));
|
|
||||||
}
|
|
||||||
|
|
||||||
override private function onRequest(request:Request):Void {
|
|
||||||
#if proto_debug L.d(TAG, '$tag onRequest: ${request}'); #end
|
|
||||||
try {
|
|
||||||
if (!request.hasAuth() && user == null) {
|
|
||||||
throw "Not Authorized";
|
|
||||||
}
|
|
||||||
if (request.hasAuth()) {
|
|
||||||
auth(request.auth);
|
|
||||||
} else if (request.hasList()) {
|
|
||||||
list(request.list);
|
|
||||||
} else if (request.hasCreate()) {
|
|
||||||
create(request.create);
|
|
||||||
} else if (request.hasJoin()) {
|
|
||||||
join(request.join.gameId);
|
|
||||||
} else if (request.hasLeave()) {
|
|
||||||
leave();
|
|
||||||
} else if (request.hasAction()) {
|
|
||||||
for (action in request.action.actions) {
|
|
||||||
this.action(action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error:Dynamic) {
|
|
||||||
L.e(TAG, '$tag onRequest ', error);
|
|
||||||
sendError(500, LoggerUtil.printError(error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function onGameEvent(event:GameEvent):Void {
|
|
||||||
send(new Response().setEvent(new GameEventResponse().setEvents([event])));
|
|
||||||
}
|
|
||||||
|
|
||||||
override public function disconnect():Void {
|
|
||||||
L.d(TAG, '$tag disconnect');
|
|
||||||
if (game != null) {
|
|
||||||
leave();
|
|
||||||
game = null;
|
|
||||||
}
|
|
||||||
user = null;
|
|
||||||
sessionsById.remove(id);
|
|
||||||
super.disconnect();
|
|
||||||
}
|
}
|
||||||
|
user = null;
|
||||||
|
sessionsById.remove(id);
|
||||||
|
super.disconnect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,41 +8,40 @@ import haxe.io.Bytes;
|
|||||||
typedef Message = Bytes;
|
typedef Message = Bytes;
|
||||||
|
|
||||||
typedef ClientMessage<M> = {
|
typedef ClientMessage<M> = {
|
||||||
var msg:M;
|
var msg:M;
|
||||||
var bytes:Int;
|
var bytes:Int;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PuzzlezServer extends ThreadServer<GameSession, Message> {
|
class PuzzlezServer extends ThreadServer<GameSession, Message> {
|
||||||
|
private static inline var TAG = 'Server';
|
||||||
|
|
||||||
private static inline var TAG = 'Server';
|
override public function clientConnected(socket:Socket):GameSession {
|
||||||
|
var session = new GameSession(socket);
|
||||||
|
L.d(TAG, 'Client connected');
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
override public function clientConnected(socket:Socket):GameSession {
|
override public function clientDisconnected(session:GameSession) {
|
||||||
var session = new GameSession(socket);
|
L.d(TAG, 'Client disconnected');
|
||||||
L.d(TAG, 'Client connected');
|
session.disconnect();
|
||||||
return session;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override public function clientDisconnected(session:GameSession) {
|
override public function readClientMessage(session:GameSession, buf:Bytes, pos:Int, len:Int):ClientMessage<Message> {
|
||||||
L.d(TAG, 'Client disconnected');
|
// L.d(TAG, 'Client message: ${buf}');
|
||||||
session.disconnect();
|
return {msg: buf.sub(pos, len), bytes: len};
|
||||||
}
|
}
|
||||||
|
|
||||||
override public function readClientMessage(session:GameSession, buf:Bytes, pos:Int, len:Int): ClientMessage<Message> {
|
override public function clientMessage(session:GameSession, message:Message) {
|
||||||
//L.d(TAG, 'Client message: ${buf}');
|
session.pushData(message);
|
||||||
return {msg: buf.sub(pos, len), bytes: len};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override public function clientMessage(session:GameSession, message:Message) {
|
public static function main():Void {
|
||||||
session.pushData(message);
|
L.push(new TraceLogger());
|
||||||
}
|
L.i(TAG, 'Build: ${CompilationOption.get("build")}');
|
||||||
|
var host:String = Sys.args().length > 0 ? Sys.args()[0] : "0.0.0.0";
|
||||||
public static function main():Void {
|
var port:Int = Sys.args().length > 1 ? Std.parseInt(Sys.args()[1]) : 5000;
|
||||||
L.push(new TraceLogger());
|
var wserver = new PuzzlezServer();
|
||||||
L.i(TAG, 'Build: ${CompilationOption.get("build")}');
|
L.i(TAG, 'Start on ${host}:${port}');
|
||||||
var host:String = Sys.args().length > 0 ? Sys.args()[0] : "0.0.0.0";
|
wserver.run(host, port);
|
||||||
var port:Int = Sys.args().length > 1 ? Std.parseInt(Sys.args()[1]) : 5000;
|
}
|
||||||
var wserver = new PuzzlezServer();
|
|
||||||
L.i(TAG, 'Start on ${host}:${port}');
|
|
||||||
wserver.run(host, port);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,114 +1,134 @@
|
|||||||
const gulp = require('gulp');
|
const gulp = require("gulp");
|
||||||
const fs = require('fs');
|
const fs = require("fs");
|
||||||
const fse = require('fs-extra');
|
const fse = require("fs-extra");
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
const through = require('through2');
|
const through = require("through2");
|
||||||
|
|
||||||
class Package {
|
class Package {
|
||||||
|
constructor(platform, type, version, versionInt) {
|
||||||
|
this.platform = platform;
|
||||||
|
this.type = type;
|
||||||
|
this.version = version;
|
||||||
|
this.versionInt = versionInt;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(platform, type, version, versionInt) {
|
get key() {
|
||||||
this.platform = platform;
|
return [this.platform, this.type].join(":");
|
||||||
this.type = type;
|
}
|
||||||
this.version = version;
|
|
||||||
this.versionInt = versionInt;
|
|
||||||
}
|
|
||||||
|
|
||||||
get key() {
|
static getPlatform(filename) {
|
||||||
return [this.platform, this.type].join(':');
|
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 getPlatform(filename) {
|
static getType(filename) {
|
||||||
for (const [platform, r] of [
|
for (const [type, r] of [
|
||||||
['android', /^.*?\.apk$/],
|
["apk", /^.*?\.apk$/],
|
||||||
['linux', /^.*?\.deb$/],
|
["deb", /^.*?\.deb$/],
|
||||||
['linux', /^.*?linux\.tar\.gz$/],
|
["archive", /^.*?\.tar\.gz$/],
|
||||||
['windows', /^.*?win\.zip$/],
|
["archive", /^.*?\.zip$/],
|
||||||
['windows', /^.*?\.exe$/],
|
["exe", /^.*?\.exe$/],
|
||||||
]) {
|
]) {
|
||||||
if (r.test(filename)) {
|
if (r.test(filename)) {
|
||||||
return platform;
|
return type;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
static getType(filename) {
|
static getVersion(filename) {
|
||||||
for (const [type, r] of [
|
const m = /(\d+)\.(\d+)\.(\d+)/.exec(filename);
|
||||||
['apk', /^.*?\.apk$/],
|
if (m) {
|
||||||
['deb', /^.*?\.deb$/],
|
return [
|
||||||
['archive', /^.*?\.tar\.gz$/],
|
m[0],
|
||||||
['archive', /^.*?\.zip$/],
|
parseInt(m[1]) * 10000 + parseInt(m[2]) * 100 + parseInt(m[3]),
|
||||||
['exe', /^.*?\.exe$/],
|
];
|
||||||
]) {
|
|
||||||
if (r.test(filename)) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
static getVersion(filename) {
|
static parseFilename(filename) {
|
||||||
const m = /(\d+)\.(\d+)\.(\d+)/.exec(filename);
|
const platform = this.getPlatform(filename);
|
||||||
if (m) {
|
const type = this.getType(filename);
|
||||||
return [m[0], parseInt(m[1]) * 10000 + parseInt(m[2]) * 100 + parseInt(m[3])];
|
const version = this.getVersion(filename);
|
||||||
}
|
if (platform && type && version) {
|
||||||
return null;
|
return new Package(platform, type, version[0], version[1]);
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = (name, version, publishDir, publishUrl) => function publish(cb) {
|
module.exports = (name, version, publishDir, publishUrl) =>
|
||||||
|
function publish(cb) {
|
||||||
const packages = {};
|
const packages = {};
|
||||||
fse.ensureDirSync(publishDir);
|
fse.ensureDirSync(publishDir);
|
||||||
return gulp.series([
|
return gulp.series([
|
||||||
function copy() {
|
function copy() {
|
||||||
return gulp.src('target/app/@(android|archive|debian|installer)/*')
|
return gulp
|
||||||
.pipe(through.obj(function (file, enc, cb) {
|
.src("target/app/@(android|archive|debian|installer)/*")
|
||||||
const pack = Package.parseFilename(file.path);
|
.pipe(
|
||||||
if (pack) {
|
through.obj(function (file, enc, cb) {
|
||||||
this.push(file);
|
const pack = Package.parseFilename(file.path);
|
||||||
}
|
if (pack) {
|
||||||
cb(null);
|
this.push(file);
|
||||||
}))
|
}
|
||||||
.pipe(gulp.dest(publishDir))
|
cb(null);
|
||||||
},
|
})
|
||||||
function generate() {
|
)
|
||||||
return gulp.src(`${publishDir}/*/*`)
|
.pipe(gulp.dest(publishDir));
|
||||||
.pipe(through.obj(function (file, enc, cb) {
|
},
|
||||||
const basepath = file.path.replace(file.base + path.sep, '');
|
function generate() {
|
||||||
const pack = Package.parseFilename(file.path);
|
return gulp
|
||||||
if (pack) {
|
.src(`${publishDir}/*/*`)
|
||||||
if (!packages[pack.key] || packages[pack.key].versionInt < pack.versionInt) {
|
.pipe(
|
||||||
packages[pack.key] = {
|
through.obj(function (file, enc, cb) {
|
||||||
platform: pack.platform,
|
const basepath = file.path.replace(file.base + path.sep, "");
|
||||||
type: pack.type,
|
const pack = Package.parseFilename(file.path);
|
||||||
version: pack.version,
|
if (pack) {
|
||||||
versionInt: pack.versionInt,
|
if (
|
||||||
path: basepath,
|
!packages[pack.key] ||
|
||||||
filename: basepath.split(path.sep).pop(),
|
packages[pack.key].versionInt < pack.versionInt
|
||||||
url: `${publishUrl}/${basepath.replace(path.sep, "/")}`,
|
) {
|
||||||
}
|
packages[pack.key] = {
|
||||||
}
|
platform: pack.platform,
|
||||||
}
|
type: pack.type,
|
||||||
cb(null);
|
version: pack.version,
|
||||||
})).on('end', function () {
|
versionInt: pack.versionInt,
|
||||||
fs.writeFileSync(path.join(publishDir, 'packages.json'), JSON.stringify({
|
path: basepath,
|
||||||
name: name,
|
filename: basepath.split(path.sep).pop(),
|
||||||
version: version,
|
url: `${publishUrl}/${basepath.replace(path.sep, "/")}`,
|
||||||
packages: Object.values(packages),
|
};
|
||||||
}, null, 4));
|
}
|
||||||
})
|
}
|
||||||
}
|
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);
|
])(cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.Package = Package;
|
module.exports.Package = Package;
|
||||||
|
|||||||
Reference in New Issue
Block a user