[common] use proto types
This commit is contained in:
@@ -2,8 +2,10 @@ package ru.m.data;
|
||||
|
||||
import flash.net.SharedObject;
|
||||
import haxe.DynamicAccess;
|
||||
import haxe.io.Bytes;
|
||||
import haxe.Serializer;
|
||||
import haxe.Unserializer;
|
||||
import hw.connect.PacketUtil;
|
||||
import promhx.Promise;
|
||||
import ru.m.data.IDataSource;
|
||||
|
||||
@@ -34,6 +36,15 @@ class DefaultConverter<D> extends Converter<D, String> {
|
||||
}
|
||||
}
|
||||
|
||||
class ProtoConverter<D:protohx.Message> extends Converter<D, String> {
|
||||
public function new(messageClass:Class<D>) {
|
||||
super(
|
||||
item -> PacketUtil.toBytes(item).toHex(),
|
||||
data -> PacketUtil.fromBytes(Bytes.ofHex(data), messageClass)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyConverter<T> extends Converter<T, T> {
|
||||
public function new() {
|
||||
super(item -> item, data -> data);
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package ru.m.puzzlez.net;
|
||||
|
||||
import haxe.Unserializer;
|
||||
import hw.connect.ConnectionFactory;
|
||||
import hw.connect.IConnection;
|
||||
import hw.signal.Signal;
|
||||
import hw.storage.SharedObjectStorage;
|
||||
import promhx.Promise;
|
||||
import ru.m.data.IDataSource;
|
||||
import ru.m.puzzlez.core.GameEvent;
|
||||
import ru.m.puzzlez.core.GameState;
|
||||
import ru.m.puzzlez.core.Id;
|
||||
import ru.m.puzzlez.proto.game.GameProto;
|
||||
import ru.m.puzzlez.proto.core.UserProto;
|
||||
import ru.m.puzzlez.proto.core.User;
|
||||
import ru.m.puzzlez.proto.event.GameAction;
|
||||
import ru.m.puzzlez.proto.event.GameEvent;
|
||||
import ru.m.puzzlez.proto.game.GameItem;
|
||||
import ru.m.puzzlez.proto.game.GameState;
|
||||
import ru.m.puzzlez.proto.pack.GameActionRequest;
|
||||
import ru.m.puzzlez.proto.pack.GameCreateRequest;
|
||||
import ru.m.puzzlez.proto.pack.GameJoinRequest;
|
||||
import ru.m.puzzlez.proto.pack.LoginRequest;
|
||||
@@ -19,14 +20,14 @@ import ru.m.puzzlez.proto.pack.Request;
|
||||
import ru.m.puzzlez.proto.pack.Response;
|
||||
|
||||
@:provide class Network implements IDataIndex<ImageId> {
|
||||
public var user(default, null):UserProto;
|
||||
public var userSignal(default, null):Signal<UserProto> = new Signal();
|
||||
public var user(default, null):User;
|
||||
public var userSignal(default, null):Signal<User> = new Signal();
|
||||
|
||||
public var gameList(default, null):Array<GameProto>;
|
||||
public var gameListSignal(default, null):Signal<Array<GameProto>> = new Signal();
|
||||
public var gameList(default, null):Array<GameItem>;
|
||||
public var gameListSignal(default, null):Signal<Array<GameItem>> = new Signal();
|
||||
|
||||
public var game(default, null):GameProto;
|
||||
public var gameSignal(default, null):Signal<GameProto> = new Signal();
|
||||
public var game(default, null):GameItem;
|
||||
public var gameSignal(default, null):Signal<GameItem> = new Signal();
|
||||
|
||||
public var gameEventSignal(default, null):Signal<GameEvent> = new Signal();
|
||||
|
||||
@@ -37,11 +38,11 @@ import ru.m.puzzlez.proto.pack.Response;
|
||||
|
||||
public function new() {
|
||||
gameList = [];
|
||||
storage = new SharedObjectStorage("network");
|
||||
storage = new SharedObjectStorage("network/2");
|
||||
if (storage.exists(USER_KEY)) {
|
||||
user = storage.read(USER_KEY);
|
||||
} else {
|
||||
user = new UserProto().setName('Anonimus #${IdUtil.generate()}');
|
||||
user = new User().setName('Anonimus #${IdUtil.generate()}');
|
||||
storage.write(USER_KEY, user);
|
||||
}
|
||||
connection = ConnectionFactory.buildClientConnection("127.0.0.1", 5000, Response);
|
||||
@@ -63,8 +64,7 @@ import ru.m.puzzlez.proto.pack.Response;
|
||||
}
|
||||
|
||||
public function sendGameAction(action:GameAction):Void {
|
||||
// ToDo: send action
|
||||
//connection.send(new Request().setGameEvent());
|
||||
connection.send(new Request().setGameAction(new GameActionRequest().setActions([action])));
|
||||
}
|
||||
|
||||
private function onConnectionChange(event:ConnectionEvent):Void {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package ru.m.puzzlez.net;
|
||||
|
||||
import hw.signal.Signal;
|
||||
import ru.m.puzzlez.core.GameState;
|
||||
import ru.m.puzzlez.core.GameEvent;
|
||||
import ru.m.puzzlez.core.IGame;
|
||||
import ru.m.puzzlez.proto.event.GameAction;
|
||||
import ru.m.puzzlez.proto.event.GameEvent;
|
||||
import ru.m.puzzlez.proto.game.GameState;
|
||||
import ru.m.puzzlez.proto.game.GameStatus;
|
||||
|
||||
class NetworkGame implements IGame {
|
||||
public var state(default, null):GameState;
|
||||
@@ -23,7 +25,7 @@ class NetworkGame implements IGame {
|
||||
public function start():Void {
|
||||
network.gameEventSignal.connect(onEvent);
|
||||
switch state.status {
|
||||
case READY:
|
||||
case GameStatus.READY:
|
||||
network.startGame(state);
|
||||
case _:
|
||||
network.joinGame(state);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package ru.m.puzzlez.render;
|
||||
|
||||
import ru.m.puzzlez.wrap.RectangleExt;
|
||||
import ru.m.puzzlez.proto.game.GamePreset;
|
||||
import flash.display.Shape;
|
||||
import flash.geom.Matrix;
|
||||
import ru.m.puzzlez.core.GamePreset;
|
||||
import ru.m.puzzlez.render.part.IPartBuilder;
|
||||
|
||||
class CompleteView extends Shape {
|
||||
@@ -29,8 +30,8 @@ class CompleteView extends Shape {
|
||||
return;
|
||||
}
|
||||
|
||||
var partWidth = preset.imageRect.width / preset.grid.width;
|
||||
var partHeight = preset.imageRect.height / preset.grid.height;
|
||||
var partWidth = preset.imageRect.width / preset.grid.x;
|
||||
var partHeight = preset.imageRect.height / preset.grid.y;
|
||||
|
||||
graphics.clear();
|
||||
graphics.lineStyle(2, 0xCCCCCC);
|
||||
@@ -51,9 +52,9 @@ class CompleteView extends Shape {
|
||||
graphics.endFill();
|
||||
}
|
||||
|
||||
var rect = part.rect.clone();
|
||||
rect.x = part.gridX * part.rect.width;
|
||||
rect.y = part.gridY * part.rect.height;
|
||||
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);
|
||||
|
||||
@@ -2,7 +2,8 @@ package ru.m.puzzlez.render;
|
||||
|
||||
import hw.signal.Signal;
|
||||
import hw.view.IView;
|
||||
import ru.m.puzzlez.core.GameEvent;
|
||||
import ru.m.puzzlez.proto.event.GameAction;
|
||||
import ru.m.puzzlez.proto.event.GameEvent;
|
||||
|
||||
interface IRender extends IView<Dynamic> {
|
||||
public var actions(default, null):Signal<GameAction>;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package ru.m.puzzlez.render;
|
||||
|
||||
import ru.m.puzzlez.render.RenderUtil;
|
||||
import flash.display.BitmapData;
|
||||
import haxe.Timer;
|
||||
import promhx.PublicStream;
|
||||
import promhx.Stream;
|
||||
import ru.m.puzzlez.core.Part;
|
||||
import ru.m.puzzlez.proto.game.Part;
|
||||
import ru.m.puzzlez.render.RenderUtil;
|
||||
|
||||
typedef Result = {
|
||||
var part:Part;
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package ru.m.puzzlez.render;
|
||||
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.Bitmap;
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.PixelSnapping;
|
||||
import flash.display.Sprite;
|
||||
import hw.geom.Point;
|
||||
import ru.m.puzzlez.core.Part;
|
||||
import ru.m.puzzlez.core.Id;
|
||||
import ru.m.puzzlez.render.RenderUtil;
|
||||
import ru.m.puzzlez.proto.core.Point;
|
||||
import ru.m.puzzlez.proto.game.Part;
|
||||
import ru.m.puzzlez.render.part.IPartBuilder;
|
||||
import ru.m.puzzlez.render.RenderUtil;
|
||||
import ru.m.puzzlez.wrap.PointExt;
|
||||
import ru.m.puzzlez.wrap.RectangleExt;
|
||||
|
||||
class PartView extends Sprite {
|
||||
|
||||
@@ -19,7 +21,7 @@ class PartView extends Sprite {
|
||||
public var target(default, null):Point;
|
||||
|
||||
private function set_position(value:Point):Point {
|
||||
position = value.clone();
|
||||
position = cast(value, PointExt).clone();
|
||||
refresh();
|
||||
return position;
|
||||
}
|
||||
@@ -58,8 +60,8 @@ class PartView extends Sprite {
|
||||
super();
|
||||
this.id = part.id;
|
||||
this.part = part;
|
||||
this.size = part.rect.size.clone();
|
||||
this.target = new Point(part.gridX * size.x, part.gridY * size.y);
|
||||
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 {
|
||||
@@ -101,7 +103,7 @@ class SpritePartView extends PartView {
|
||||
graphics.endFill();
|
||||
|
||||
if (playerId != null) {
|
||||
var rect = part.rect.clone();
|
||||
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);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package ru.m.puzzlez.render;
|
||||
|
||||
import ru.m.puzzlez.core.Id;
|
||||
import ru.m.puzzlez.net.Network;
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.PNGEncoderOptions;
|
||||
import flash.display.Sprite;
|
||||
@@ -10,16 +8,20 @@ import flash.geom.Point as FlashPoint;
|
||||
import flash.geom.Rectangle;
|
||||
import flash.net.FileReference;
|
||||
import flash.utils.ByteArray;
|
||||
import hw.geom.Point;
|
||||
import hw.signal.Signal;
|
||||
import hw.view.popup.AlertView;
|
||||
import hw.view.SpriteView;
|
||||
import ru.m.puzzlez.core.GameEvent;
|
||||
import ru.m.puzzlez.core.GameState;
|
||||
import ru.m.puzzlez.core.PartLocation;
|
||||
import ru.m.puzzlez.core.Id;
|
||||
import ru.m.puzzlez.net.Network;
|
||||
import ru.m.puzzlez.proto.event.gameaction.Action;
|
||||
import ru.m.puzzlez.proto.event.GameAction;
|
||||
import ru.m.puzzlez.proto.event.GameEvent;
|
||||
import ru.m.puzzlez.proto.game.GameState;
|
||||
import ru.m.puzzlez.proto.game.PartLocation;
|
||||
import ru.m.puzzlez.render.ImagePartBuilder;
|
||||
import ru.m.puzzlez.storage.ImageStorage;
|
||||
import ru.m.puzzlez.storage.SettingsStorage;
|
||||
import ru.m.puzzlez.wrap.PointExt;
|
||||
|
||||
class Render extends SpriteView implements IRender {
|
||||
|
||||
@@ -33,7 +35,7 @@ class Render extends SpriteView implements IRender {
|
||||
private var playerId(get, never):PlayerId;
|
||||
|
||||
private function get_playerId():PlayerId {
|
||||
return network.user.uuid;
|
||||
return network.user.hasUuid() ? network.user.uuid : "local";
|
||||
}
|
||||
|
||||
private function get_scale():Float {
|
||||
@@ -57,7 +59,7 @@ class Render extends SpriteView implements IRender {
|
||||
private var imageView:CompleteView;
|
||||
private var parts:Map<Int, PartView>;
|
||||
private var activePart:PartView;
|
||||
private var activePoint:Point;
|
||||
private var activePoint:PointExt;
|
||||
|
||||
private var movePoint:FlashPoint;
|
||||
|
||||
@@ -79,26 +81,22 @@ class Render extends SpriteView implements IRender {
|
||||
}
|
||||
|
||||
public function onGameEvent(event:GameEvent):Void {
|
||||
switch event {
|
||||
case START(state, resume):
|
||||
onStart(state);
|
||||
case CHANGE(PART_UPDATE(id, TABLE(point))):
|
||||
var part:PartView = parts[id];
|
||||
part.position = point;
|
||||
part.playerId = null;
|
||||
case CHANGE(PART_UPDATE(id, HAND(playerId, point))):
|
||||
var part:PartView = parts[id];
|
||||
part.position = point;
|
||||
part.playerId = playerId;
|
||||
case CHANGE(PART_UPDATE(id, IMAGE)):
|
||||
var part:PartView = parts[id];
|
||||
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 COMPLETE:
|
||||
AlertView.alert("Complete!");
|
||||
case _:
|
||||
}
|
||||
} else if (event.hasComplete()) {
|
||||
AlertView.alert("Complete!");
|
||||
}
|
||||
}
|
||||
|
||||
private function onStart(state:GameState):Void {
|
||||
@@ -108,10 +106,10 @@ class Render extends SpriteView implements IRender {
|
||||
var partView = PartView.factory(part);
|
||||
parts.set(part.id, partView);
|
||||
switch part.location {
|
||||
case TABLE(point):
|
||||
partView.position = point;
|
||||
case PartLocation.TABLE:
|
||||
partView.position = part.position;
|
||||
tableView.addChild(partView);
|
||||
case IMAGE:
|
||||
case PartLocation.IMAGE:
|
||||
partView.complete();
|
||||
imageView.addChild(partView);
|
||||
case _:
|
||||
@@ -200,24 +198,38 @@ class Render extends SpriteView implements IRender {
|
||||
}
|
||||
activePart = pointPart;
|
||||
tableView.setChildIndex(activePart, tableView.numChildren - 1);
|
||||
activePoint = RenderUtil.convertPoint(tableView.globalToLocal(point));
|
||||
activePoint = tableView.globalToLocal(point);
|
||||
tableView.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
|
||||
tableView.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
|
||||
actions.emit(PART_TAKE(playerId, activePart.id));
|
||||
actions.emit(new GameAction()
|
||||
.setAction(Action.TAKE)
|
||||
.setPlayerId(playerId)
|
||||
.setPartId(activePart.id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function onMouseMove(event:MouseEvent):Void {
|
||||
var newPoint:Point = RenderUtil.convertPoint(tableView.globalToLocal(new FlashPoint(event.stageX, event.stageY)));
|
||||
var partPosition = activePart.position.add(newPoint).subtract(activePoint);
|
||||
actions.emit(PART_MOVE(playerId, activePart.id, partPosition.clone()));
|
||||
var newPoint:PointExt = tableView.globalToLocal(new FlashPoint(event.stageX, event.stageY));
|
||||
var partPosition = cast(activePart.position, PointExt).add(newPoint).subtract(activePoint);
|
||||
actions.emit(new GameAction()
|
||||
.setAction(Action.MOVE)
|
||||
.setPlayerId(playerId)
|
||||
.setPartId(activePart.id)
|
||||
.setPosition(partPosition)
|
||||
);
|
||||
activePoint = newPoint;
|
||||
}
|
||||
|
||||
private function onMouseUp(event:MouseEvent):Void {
|
||||
var newPoint:Point = RenderUtil.convertPoint(tableView.globalToLocal(new FlashPoint(event.stageX, event.stageY)));
|
||||
var partPosition = activePart.position.add(newPoint).subtract(activePoint);
|
||||
actions.emit(PART_PUT(playerId, activePart.id, partPosition.clone()));
|
||||
var newPoint:PointExt = tableView.globalToLocal(new FlashPoint(event.stageX, event.stageY));
|
||||
var partPosition = cast(activePart.position, PointExt).add(newPoint).subtract(activePoint);
|
||||
actions.emit(new GameAction()
|
||||
.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;
|
||||
|
||||
@@ -4,12 +4,13 @@ import flash.display.BitmapData;
|
||||
import flash.display.Shape;
|
||||
import flash.geom.Matrix;
|
||||
import hw.color.Color;
|
||||
import hw.geom.Point;
|
||||
import hw.geom.Rectangle;
|
||||
import ru.m.draw.DrawPath;
|
||||
import ru.m.puzzlez.core.Part;
|
||||
import ru.m.puzzlez.proto.core.Point;
|
||||
import ru.m.puzzlez.proto.core.Rectangle;
|
||||
import ru.m.puzzlez.proto.game.Part;
|
||||
import ru.m.puzzlez.render.part.IPartBuilder;
|
||||
import ru.m.puzzlez.render.part.PartMask;
|
||||
import ru.m.puzzlez.wrap.RectangleExt;
|
||||
|
||||
typedef DrawSetting = {
|
||||
var offset:Point;
|
||||
@@ -24,13 +25,13 @@ typedef PartImage = {
|
||||
|
||||
class RenderUtil {
|
||||
public static var shadowSettings(default, null):Array<DrawSetting> = [
|
||||
{offset: new Point(-2, -2), color: 0x000000, opacity: 0.4},
|
||||
{offset: new Point(2, 2), color: 0xffffff, opacity: 0.4},
|
||||
{offset: new Point().setX(-2).setY(-2), color: 0x000000, opacity: 0.4},
|
||||
{offset: new Point().setX(2).setY(2), color: 0xffffff, opacity: 0.4},
|
||||
];
|
||||
|
||||
public static var borderSettings(default, null):Array<DrawSetting> = [
|
||||
{offset: new Point(-1, -1), color: 0x555555, opacity: 0.4},
|
||||
{offset: new Point(1, 1), color: 0xcccccc, opacity: 0.4},
|
||||
{offset: new Point().setX(-1).setY(-1), color: 0x555555, opacity: 0.4},
|
||||
{offset: new Point().setX(1).setY(1), color: 0xcccccc, opacity: 0.4},
|
||||
];
|
||||
|
||||
@:provide static var builder:IPartBuilder;
|
||||
@@ -46,14 +47,18 @@ class RenderUtil {
|
||||
}
|
||||
|
||||
private static function buildPartGeometry(part:Part):{rect:Rectangle, drawRect:Rectangle} {
|
||||
var source = new Rectangle(part.rect.width * part.gridX, part.rect.height * part.gridY, part.rect.width, part.rect.height);
|
||||
var rect = source.clone();
|
||||
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 = source.clone();
|
||||
var drawRect = cast(source, RectangleExt).clone();
|
||||
drawRect.x = offset;
|
||||
drawRect.y = offset ;
|
||||
return {rect:rect, drawRect:drawRect};
|
||||
@@ -113,10 +118,10 @@ class RenderUtil {
|
||||
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((target.width - width) / 2, (target.height - height) / 2, width, height);
|
||||
}
|
||||
|
||||
public static function convertPoint(point:flash.geom.Point):Point {
|
||||
return new Point(point.x, point.y);
|
||||
return new Rectangle()
|
||||
.setX((target.width - width) / 2)
|
||||
.setY((target.height - height) / 2)
|
||||
.setWidth(width)
|
||||
.setHeight(height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package ru.m.puzzlez.render.part;
|
||||
|
||||
import hw.geom.Rectangle;
|
||||
import ru.m.puzzlez.wrap.RectangleExt;
|
||||
import ru.m.puzzlez.proto.core.Rectangle;
|
||||
import ru.m.draw.DrawPath;
|
||||
import ru.m.puzzlez.core.Part;
|
||||
import ru.m.puzzlez.proto.game.PartBound;
|
||||
import ru.m.puzzlez.proto.game.PartBounds;
|
||||
import ru.m.puzzlez.render.part.IPartBuilder;
|
||||
|
||||
class BasePartBuilder implements IPartBuilder {
|
||||
@@ -17,7 +19,7 @@ class BasePartBuilder implements IPartBuilder {
|
||||
private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, bound:PartBound):Void {
|
||||
}
|
||||
|
||||
public function build(rect:Rectangle, bounds:PartBounds):DrawPath {
|
||||
public function build(rect:RectangleExt, bounds:PartBounds):DrawPath {
|
||||
var path = new DrawPath();
|
||||
createAngle(path, rect.left, rect.top, true);
|
||||
createBound(path, rect.left, rect.top, rect.right, rect.top, bounds.top);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package ru.m.puzzlez.render.part;
|
||||
|
||||
import ru.m.draw.DrawPath;
|
||||
import ru.m.puzzlez.core.BoundType;
|
||||
import ru.m.puzzlez.core.Part;
|
||||
import ru.m.puzzlez.proto.game.BoundType;
|
||||
import ru.m.puzzlez.proto.game.PartBound;
|
||||
|
||||
class ClassicPartBuilder extends BasePartBuilder {
|
||||
|
||||
@@ -13,18 +13,17 @@ class ClassicPartBuilder extends BasePartBuilder {
|
||||
var spikeWidth = boundLength * 0.2;
|
||||
var spikeOffset = (boundLength - spikeWidth) / 2;
|
||||
var spikeDepth = switch bound.spike {
|
||||
case NONE: 0;
|
||||
case IN: spikeWidth;
|
||||
case OUT: -spikeWidth;
|
||||
case BoundType.IN: spikeWidth;
|
||||
case BoundType.OUT: -spikeWidth;
|
||||
case _: 0;
|
||||
};
|
||||
var spikeSideOffset = switch bound.side {
|
||||
case NONE: 0;
|
||||
case IN: boundLength * -0.04;
|
||||
case OUT: boundLength * 0.04;
|
||||
case BoundType.IN: boundLength * -0.04;
|
||||
case BoundType.OUT: boundLength * 0.04;
|
||||
case _: 0;
|
||||
}
|
||||
switch bound.spike {
|
||||
case NONE:
|
||||
case IN | OUT:
|
||||
case BoundType.IN | BoundType.OUT:
|
||||
path.commands.push(LINE_TO(
|
||||
fromX + spikeOffset * dx + spikeSideOffset * dy,
|
||||
fromY + spikeOffset * dy + spikeSideOffset * dx
|
||||
@@ -41,6 +40,7 @@ class ClassicPartBuilder extends BasePartBuilder {
|
||||
fromX + (spikeOffset + spikeWidth) * dx + spikeSideOffset * dy,
|
||||
fromY + (spikeOffset + spikeWidth) * dy + spikeSideOffset * dx
|
||||
));
|
||||
case _:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package ru.m.puzzlez.render.part;
|
||||
|
||||
import hw.geom.Rectangle;
|
||||
import ru.m.draw.DrawPath;
|
||||
import ru.m.puzzlez.core.Part;
|
||||
import ru.m.puzzlez.proto.game.PartBounds;
|
||||
import ru.m.puzzlez.wrap.RectangleExt;
|
||||
|
||||
@:provide(ClassicPartBuilder) interface IPartBuilder {
|
||||
public function build(rect:Rectangle, bound:PartBounds):DrawPath;
|
||||
public function build(rect:RectangleExt, bound:PartBounds):DrawPath;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package ru.m.puzzlez.render.part;
|
||||
|
||||
import ru.m.draw.DrawPath;
|
||||
import ru.m.puzzlez.core.BoundType;
|
||||
import ru.m.puzzlez.core.Part;
|
||||
import ru.m.puzzlez.proto.game.BoundType;
|
||||
import ru.m.puzzlez.proto.game.PartBound;
|
||||
|
||||
class SquarePartBuilder extends BasePartBuilder {
|
||||
|
||||
@@ -13,13 +13,13 @@ class SquarePartBuilder extends BasePartBuilder {
|
||||
var spikeWidth = boundLength * 0.2;
|
||||
var spikeOffset = (boundLength - spikeWidth) / 2;
|
||||
var spikeDepth = switch bound.spike {
|
||||
case NONE: 0;
|
||||
case IN: spikeWidth;
|
||||
case OUT: -spikeWidth;
|
||||
case BoundType.NONE: 0;
|
||||
case BoundType.IN: spikeWidth;
|
||||
case BoundType.OUT: -spikeWidth;
|
||||
}
|
||||
switch bound.spike {
|
||||
case NONE:
|
||||
case IN | OUT:
|
||||
case BoundType.NONE:
|
||||
case BoundType.IN | BoundType.OUT:
|
||||
path.commands.push(LINE_TO(
|
||||
fromX + spikeOffset * dx,
|
||||
fromY + spikeOffset * dy
|
||||
|
||||
@@ -1,27 +1,32 @@
|
||||
package ru.m.puzzlez.storage;
|
||||
|
||||
import ru.m.data.DataStorage;
|
||||
import ru.m.puzzlez.core.GameState;
|
||||
import ru.m.puzzlez.core.Id;
|
||||
import ru.m.puzzlez.core.ImageListSource;
|
||||
import ru.m.puzzlez.proto.game.GameState;
|
||||
|
||||
@:provide class GameStorage extends DataStorage<ImageId, GameState> {
|
||||
inline private static var NAME = "game";
|
||||
inline private static var VERSION = 4;
|
||||
inline private static var VERSION = 6;
|
||||
|
||||
public function new() {
|
||||
super(
|
||||
'${NAME}/${VERSION}',
|
||||
new MetaBuilder<GameState>(item -> ["id" => item.preset.imageId, "status" => item.status, "date" => Date.now().toString()]),
|
||||
new Converter<ImageId, String>(id -> id.toString(), data -> ImageId.fromString(data))
|
||||
new MetaBuilder<GameState>(item -> [
|
||||
"id" => item.preset.imageId,
|
||||
"status" => Std.string(item.status),
|
||||
"date" => Date.now().toString()
|
||||
]),
|
||||
new Converter<ImageId, String>(id -> id.toString(), data -> ImageId.fromString(data)),
|
||||
new ProtoConverter(GameState)
|
||||
);
|
||||
}
|
||||
|
||||
public function statusSource(status:GameStatus):ImageListSource {
|
||||
public function statusSource(status:Int):ImageListSource {
|
||||
return {
|
||||
title: status,
|
||||
title: Std.string(status), // # ToDo:
|
||||
source: this,
|
||||
filter: ["status" => status],
|
||||
filter: ["status" => Std.string(status)],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package ru.m.puzzlez.view;
|
||||
|
||||
import ru.m.puzzlez.proto.game.GameStatus;
|
||||
import haxe.Timer;
|
||||
import hw.view.frame.FrameSwitcher;
|
||||
import hw.view.frame.FrameView;
|
||||
import hw.view.popup.ConfirmView;
|
||||
import promhx.Promise;
|
||||
import ru.m.puzzlez.core.Game;
|
||||
import ru.m.puzzlez.core.GameEvent;
|
||||
import ru.m.puzzlez.core.GameState;
|
||||
import ru.m.puzzlez.core.IGame;
|
||||
import ru.m.puzzlez.net.NetworkGame;
|
||||
import ru.m.puzzlez.proto.event.GameEvent;
|
||||
import ru.m.puzzlez.proto.game.GameState;
|
||||
import ru.m.puzzlez.render.IRender;
|
||||
import ru.m.puzzlez.storage.GameStorage;
|
||||
import ru.m.puzzlez.storage.SettingsStorage;
|
||||
@@ -73,10 +74,8 @@ import ru.m.puzzlez.view.popup.PreviewPopup;
|
||||
}
|
||||
|
||||
private function onGameEvent(event:GameEvent):Void {
|
||||
switch event {
|
||||
case START(_) | ACTION(PART_PUT(_, _)):
|
||||
if (event.hasStart() || event.hasAction()) {
|
||||
toSave();
|
||||
case _:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +93,7 @@ import ru.m.puzzlez.view.popup.PreviewPopup;
|
||||
}
|
||||
|
||||
private function back():Void {
|
||||
(game.state.status == COMPLETE ? Promise.promise(true) : ConfirmView.confirm("Exit?"))
|
||||
(game.state.status == GameStatus.COMPLETE ? Promise.promise(true) : ConfirmView.confirm("Exit?"))
|
||||
.then(result -> {
|
||||
if (result) {
|
||||
switcher.change(ImageListFrame.ID, storage.statusSource(game.state.status));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ru.m.puzzlez.view;
|
||||
|
||||
import ru.m.puzzlez.proto.game.GameStatus;
|
||||
import ru.m.puzzlez.net.Network;
|
||||
import hw.view.data.DataView;
|
||||
import hw.view.form.ButtonView;
|
||||
@@ -7,7 +8,6 @@ import hw.view.frame.FrameSwitcher;
|
||||
import hw.view.frame.FrameView;
|
||||
import ru.m.data.IDataSource;
|
||||
import ru.m.pixabay.PixabayApi;
|
||||
import ru.m.puzzlez.core.GameState.GameStatus;
|
||||
import ru.m.puzzlez.core.ImageListSource;
|
||||
import ru.m.puzzlez.source.AssetSource;
|
||||
import ru.m.puzzlez.source.FileSource;
|
||||
@@ -43,13 +43,13 @@ import ru.m.update.Updater;
|
||||
}
|
||||
|
||||
private function refresh():Void {
|
||||
var startedRequest:Page = {index: 0, count: 0, filter: ["status" => STARTED]};
|
||||
var startedRequest:Page = {index: 0, count: 0, filter: ["status" => Std.string(GameStatus.STARTED)]};
|
||||
gameStorage.getIndexPage(startedRequest).then(page -> {
|
||||
var total = page.total;
|
||||
loadButton.text = 'Resume (${total})';
|
||||
loadButton.disabled = total == 0;
|
||||
});
|
||||
var completeRequest:Page = {index: 0, count: 0, filter: ["status" => COMPLETE]};
|
||||
var completeRequest:Page = {index: 0, count: 0, filter: ["status" => Std.string(GameStatus.COMPLETE)]};
|
||||
gameStorage.getIndexPage(completeRequest).then(page -> {
|
||||
var total = page.total;
|
||||
completeButton.text = 'Complete (${total})';
|
||||
@@ -77,7 +77,7 @@ import ru.m.update.Updater;
|
||||
switcher.change(ImageListFrame.ID, source);
|
||||
}
|
||||
|
||||
private function openGames(status:GameStatus):Void {
|
||||
private function openGames(status:Int):Void {
|
||||
switcher.change(ImageListFrame.ID, gameStorage.statusSource(status));
|
||||
}
|
||||
|
||||
|
||||
@@ -35,11 +35,11 @@ views:
|
||||
- id: load
|
||||
$type: hw.view.form.ButtonView
|
||||
text: Load
|
||||
+onPress: ~openGames('started')
|
||||
+onPress: ~openGames(1)
|
||||
- id: complete
|
||||
$type: hw.view.form.ButtonView
|
||||
text: Complete
|
||||
+onPress: ~openGames('complete')
|
||||
+onPress: ~openGames(2)
|
||||
- id: network
|
||||
$type: hw.view.form.ButtonView
|
||||
text: Network
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package ru.m.puzzlez.view.common;
|
||||
|
||||
import ru.m.puzzlez.wrap.RectangleExt;
|
||||
import ru.m.puzzlez.proto.game.GameState;
|
||||
import flash.display.Graphics;
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.Shape;
|
||||
import hw.view.group.GroupView;
|
||||
import ru.m.puzzlez.core.GameState;
|
||||
import ru.m.puzzlez.render.part.IPartBuilder;
|
||||
import ru.m.puzzlez.render.RenderUtil;
|
||||
import ru.m.puzzlez.storage.ImageStorage;
|
||||
@@ -57,8 +58,8 @@ class PresetView extends GroupView {
|
||||
return;
|
||||
}
|
||||
var preset = state.preset;
|
||||
var partWidth = preset.imageRect.width / preset.grid.width;
|
||||
var partHeight = preset.imageRect.height / preset.grid.height;
|
||||
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);
|
||||
@@ -66,9 +67,9 @@ class PresetView extends GroupView {
|
||||
graphics.endFill();
|
||||
|
||||
for (part in state.parts) {
|
||||
var rect = part.rect.clone();
|
||||
rect.x = part.gridX * part.rect.width;
|
||||
rect.y = part.gridY * part.rect.height;
|
||||
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);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package ru.m.puzzlez.view.popup;
|
||||
|
||||
import ru.m.puzzlez.proto.game.GameState;
|
||||
import flash.events.MouseEvent;
|
||||
import hw.view.popup.PopupView;
|
||||
import promhx.Promise;
|
||||
import ru.m.puzzlez.core.GameState;
|
||||
import ru.m.puzzlez.view.common.PresetView;
|
||||
|
||||
@:singleton @:template class PreviewPopup extends PopupView<Dynamic> {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package ru.m.puzzlez.core;
|
||||
|
||||
enum BoundType {
|
||||
NONE;
|
||||
OUT;
|
||||
IN;
|
||||
}
|
||||
36
src/common/haxe/ru/m/puzzlez/core/EventUtil.hx
Normal file
36
src/common/haxe/ru/m/puzzlez/core/EventUtil.hx
Normal file
@@ -0,0 +1,36 @@
|
||||
package ru.m.puzzlez.core;
|
||||
|
||||
import ru.m.puzzlez.proto.event.GameComplete;
|
||||
import ru.m.puzzlez.proto.event.GameChange;
|
||||
import ru.m.puzzlez.proto.game.Part;
|
||||
import ru.m.puzzlez.proto.event.GameAction;
|
||||
import ru.m.puzzlez.proto.event.GameStart;
|
||||
import ru.m.puzzlez.proto.event.GameEvent;
|
||||
import ru.m.puzzlez.proto.game.GameState;
|
||||
|
||||
class EventUtil {
|
||||
|
||||
public static function start(state:GameState, resume: Bool):GameEvent {
|
||||
return new GameEvent().setStart(new GameStart()
|
||||
.setState(state)
|
||||
.setResume(resume)
|
||||
);
|
||||
}
|
||||
|
||||
public static function complete():GameEvent {
|
||||
return new GameEvent().setComplete(new GameComplete());
|
||||
}
|
||||
|
||||
public static function action(action:GameAction):GameEvent {
|
||||
return new GameEvent().setAction(action);
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
package ru.m.puzzlez.core;
|
||||
|
||||
import hw.geom.Point;
|
||||
import ru.m.puzzlez.proto.core.Point;
|
||||
import hw.signal.Signal;
|
||||
import ru.m.puzzlez.core.GameEvent;
|
||||
import ru.m.puzzlez.core.GameState;
|
||||
import ru.m.puzzlez.core.PartLocation;
|
||||
import ru.m.puzzlez.proto.event.gameaction.Action;
|
||||
import ru.m.puzzlez.proto.event.GameAction;
|
||||
import ru.m.puzzlez.proto.event.GameEvent;
|
||||
import ru.m.puzzlez.proto.game.GameState;
|
||||
import ru.m.puzzlez.proto.game.GameStatus;
|
||||
import ru.m.puzzlez.proto.game.Part;
|
||||
import ru.m.puzzlez.proto.game.PartLocation;
|
||||
import ru.m.puzzlez.wrap.PointExt;
|
||||
|
||||
class Game implements IGame {
|
||||
public var state(default, null):GameState;
|
||||
@@ -23,42 +28,42 @@ class Game implements IGame {
|
||||
}
|
||||
|
||||
public function action(action:GameAction):Void {
|
||||
events.emit(ACTION(action));
|
||||
events.emit(EventUtil.action(action));
|
||||
}
|
||||
|
||||
public function start():Void {
|
||||
switch state.status {
|
||||
case READY:
|
||||
case GameStatus.READY:
|
||||
shuffle();
|
||||
state.status = STARTED;
|
||||
events.emit(START(state, false));
|
||||
state.status = GameStatus.STARTED;
|
||||
events.emit(EventUtil.start(state, false));
|
||||
case _:
|
||||
events.emit(START(state, true));
|
||||
events.emit(EventUtil.start(state, true));
|
||||
}
|
||||
}
|
||||
|
||||
private function shuffle():Void {
|
||||
for (part in state.parts) {
|
||||
switch part.location {
|
||||
case TABLE(_):
|
||||
case PartLocation.TABLE:
|
||||
var bound = part.rect.width * 0.25;
|
||||
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.location = TABLE(new Point(x, y));
|
||||
part.position = new PointExt(x, y);
|
||||
case _:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function distance(a:Point, b:Point):Float {
|
||||
var diff = a.subtract(b);
|
||||
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 IMAGE:
|
||||
case PartLocation.IMAGE:
|
||||
case _: return false;
|
||||
}
|
||||
}
|
||||
@@ -66,40 +71,42 @@ class Game implements IGame {
|
||||
}
|
||||
|
||||
private function onGameEvent(event:GameEvent):Void {
|
||||
switch event {
|
||||
case ACTION(PART_TAKE(playerId, partId)):
|
||||
var part = partsById[partId];
|
||||
if (event.hasAction()) {
|
||||
var part:Part = partsById[event.action.partId];
|
||||
switch event.action.action {
|
||||
case Action.TAKE:
|
||||
switch part.location {
|
||||
case TABLE(point):
|
||||
part.location = HAND(playerId, point);
|
||||
events.emit(CHANGE(PART_UPDATE(partId, part.location)));
|
||||
case PartLocation.TABLE:
|
||||
part.location = PartLocation.HAND;
|
||||
part.playerId = event.action.playerId;
|
||||
events.emit(EventUtil.change(part));
|
||||
case _:
|
||||
}
|
||||
case ACTION(PART_MOVE(playerId, partId, point)):
|
||||
var part = partsById[partId];
|
||||
case Action.MOVE:
|
||||
switch part.location {
|
||||
case HAND(currentPlayerId, _) if (currentPlayerId == playerId):
|
||||
part.location = HAND(playerId, point);
|
||||
events.emit(CHANGE(PART_UPDATE(partId, part.location)));
|
||||
case PartLocation.HAND if (event.action.playerId == part.playerId):
|
||||
part.position = event.action.position;
|
||||
events.emit(EventUtil.change(part));
|
||||
case _:
|
||||
}
|
||||
case ACTION(PART_PUT(playerId, partId, point)):
|
||||
var part:Part = partsById[partId];
|
||||
var target:Point = state.preset.imageRect.position.clone();
|
||||
target = target.add(new Point(part.gridX * part.rect.width, part.gridY * part.rect.height));
|
||||
var d = distance(target, point);
|
||||
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 = IMAGE;
|
||||
events.emit(CHANGE(PART_UPDATE(partId, part.location)));
|
||||
part.location = PartLocation.IMAGE;
|
||||
events.emit(EventUtil.change(part));
|
||||
if (checkIsComplete()) {
|
||||
state.status = COMPLETE;
|
||||
events.emit(COMPLETE);
|
||||
state.status = GameStatus.COMPLETE;
|
||||
events.emit(EventUtil.complete());
|
||||
}
|
||||
} else {
|
||||
part.location = TABLE(point);
|
||||
events.emit(CHANGE(PART_UPDATE(partId, part.location)));
|
||||
part.location = PartLocation.TABLE;
|
||||
part.position = event.action.position;
|
||||
events.emit(EventUtil.change(part));
|
||||
}
|
||||
}
|
||||
case _:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package ru.m.puzzlez.core;
|
||||
|
||||
import ru.m.puzzlez.core.Id;
|
||||
import hw.geom.Point;
|
||||
|
||||
enum GameAction {
|
||||
PART_TAKE(playerId:PlayerId, partId:PartId);
|
||||
PART_MOVE(playerId:PlayerId, partId:PartId, point:Point);
|
||||
PART_PUT(playerId:PlayerId, partId:PartId, point:Point);
|
||||
}
|
||||
|
||||
enum GameChange {
|
||||
PART_UPDATE(id:Int, location:PartLocation);
|
||||
}
|
||||
|
||||
enum GameEvent {
|
||||
START(state:GameState, resume:Bool);
|
||||
ACTION(action:GameAction);
|
||||
CHANGE(change:GameChange);
|
||||
COMPLETE;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package ru.m.puzzlez.core;
|
||||
|
||||
import hw.geom.Rectangle;
|
||||
import ru.m.puzzlez.core.Id;
|
||||
|
||||
typedef GamePreset = {
|
||||
var imageId:ImageId;
|
||||
var grid:Grid;
|
||||
var tableRect:Rectangle;
|
||||
var imageRect:Rectangle;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package ru.m.puzzlez.core;
|
||||
|
||||
enum abstract GameStatus(String) from String to String {
|
||||
var READY = "ready";
|
||||
var STARTED = "started";
|
||||
var COMPLETE = "complete";
|
||||
}
|
||||
|
||||
typedef GameState = {
|
||||
var id:String;
|
||||
var status:GameStatus;
|
||||
var preset:GamePreset;
|
||||
var parts:Array<Part>;
|
||||
@:optional var online:Bool;
|
||||
}
|
||||
@@ -1,65 +1,64 @@
|
||||
package ru.m.puzzlez.core;
|
||||
|
||||
import hw.geom.Point;
|
||||
import hw.geom.Rectangle;
|
||||
import ru.m.puzzlez.core.BoundType;
|
||||
import ru.m.puzzlez.core.GameState;
|
||||
import ru.m.puzzlez.core.Id;
|
||||
import ru.m.puzzlez.core.Part;
|
||||
import ru.m.puzzlez.core.PartLocation;
|
||||
import ru.m.puzzlez.core.Side;
|
||||
import ru.m.puzzlez.proto.core.IntPoint;
|
||||
import ru.m.puzzlez.proto.core.Point;
|
||||
import ru.m.puzzlez.proto.core.Rectangle;
|
||||
import ru.m.puzzlez.proto.game.BoundType;
|
||||
import ru.m.puzzlez.proto.game.GamePreset;
|
||||
import ru.m.puzzlez.proto.game.GameState;
|
||||
import ru.m.puzzlez.proto.game.GameStatus;
|
||||
import ru.m.puzzlez.proto.game.Part;
|
||||
import ru.m.puzzlez.proto.game.PartBound;
|
||||
import ru.m.puzzlez.proto.game.PartBounds;
|
||||
import ru.m.puzzlez.proto.game.PartLocation;
|
||||
|
||||
class BoundsMap {
|
||||
|
||||
private var grid:Grid;
|
||||
private var grid:IntPoint;
|
||||
private var bounds:Array<PartBound>;
|
||||
|
||||
public function new(grid:Grid) {
|
||||
public function new(grid:IntPoint) {
|
||||
this.grid = grid;
|
||||
bounds = [];
|
||||
for (x in 0...grid.width + 1) {
|
||||
for (y in 0...grid.height + 1) {
|
||||
for (x in 0...grid.x + 1) {
|
||||
for (y in 0...grid.y + 1) {
|
||||
bounds.push(buildPartBound());
|
||||
bounds.push(buildPartBound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function buildBound():BoundType {
|
||||
public function buildBound():Int {
|
||||
return Math.random() > 0.5 ? BoundType.OUT : BoundType.IN;
|
||||
}
|
||||
|
||||
public function buildPartBound():PartBound {
|
||||
return {
|
||||
spike: buildBound(),
|
||||
side: buildBound(),
|
||||
}
|
||||
return new PartBound().setSpike(buildBound()).setSide(buildBound());
|
||||
}
|
||||
|
||||
public function buildNonePartBound():PartBound {
|
||||
return {
|
||||
spike: NONE,
|
||||
side: NONE,
|
||||
}
|
||||
return new PartBound().setSpike(BoundType.NONE).setSide(BoundType.NONE);
|
||||
}
|
||||
|
||||
public function revert(type:BoundType):BoundType {
|
||||
public function revert(type:Int):Int {
|
||||
return switch type {
|
||||
case IN: OUT;
|
||||
case OUT: IN;
|
||||
case NONE: NONE;
|
||||
case BoundType.IN: BoundType.OUT;
|
||||
case BoundType.OUT: BoundType.IN;
|
||||
case _: BoundType.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBound(x:Int, y:Int, side:Side):PartBound {
|
||||
var index = switch side {
|
||||
case TOP: x + (grid.width + 1) * (y * 2);
|
||||
case LEFT: x + (grid.width + 1) * (y + 1);
|
||||
case RIGHT: x + (grid.width + 1) * (y + 1) + 1;
|
||||
case BOTTOM: x + (grid.width + 1) * (y * 2) + (grid.width + 1) * 2;
|
||||
case TOP: x + (grid.x + 1) * (y * 2);
|
||||
case LEFT: x + (grid.x + 1) * (y + 1);
|
||||
case RIGHT: x + (grid.x + 1) * (y + 1) + 1;
|
||||
case BOTTOM: x + (grid.x + 1) * (y * 2) + (grid.x + 1) * 2;
|
||||
}
|
||||
return switch side {
|
||||
case TOP | LEFT: {spike: revert(bounds[index].spike), side: revert(bounds[index].side)};
|
||||
case TOP | LEFT: new PartBound().setSpike(revert(bounds[index].spike)).setSide(revert(bounds[index].side));
|
||||
case _: bounds[index];
|
||||
}
|
||||
}
|
||||
@@ -80,67 +79,72 @@ class GameUtil {
|
||||
var offsetY = 200;
|
||||
var imageSize = 1024;
|
||||
var s = width / height;
|
||||
var imageRect = new Rectangle(offsetX, offsetY, imageSize, imageSize / s);
|
||||
var tableRect = new Rectangle(0, 0, imageRect.width + offsetX * 2, imageRect.height + offsetY * 2);
|
||||
return {
|
||||
imageId: imageId,
|
||||
grid: {width: width, height: height},
|
||||
tableRect: tableRect,
|
||||
imageRect: imageRect,
|
||||
}
|
||||
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()
|
||||
.setImageId(imageId)
|
||||
.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.width;
|
||||
var partHeight = preset.imageRect.height / preset.grid.height;
|
||||
var position = preset.imageRect.position;
|
||||
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.height) {
|
||||
for (x in 0...preset.grid.width) {
|
||||
var bounds:PartBounds = {
|
||||
left: boundsMap.getBound(x, y, LEFT),
|
||||
right: boundsMap.getBound(x, y, RIGHT),
|
||||
top: boundsMap.getBound(x, y, TOP),
|
||||
bottom: boundsMap.getBound(x, y, BOTTOM),
|
||||
}
|
||||
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.width - 1) {
|
||||
if (x == preset.grid.x - 1) {
|
||||
bounds.right = boundsMap.buildNonePartBound();
|
||||
}
|
||||
if (y == preset.grid.height - 1) {
|
||||
if (y == preset.grid.y - 1) {
|
||||
bounds.bottom = boundsMap.buildNonePartBound();
|
||||
}
|
||||
var id = (x << 16) + y;
|
||||
var position = new Point(position.x + x * partWidth, position.y + y * partHeight);
|
||||
parts.push({
|
||||
id: id,
|
||||
gridX: x,
|
||||
gridY: y,
|
||||
location: TABLE(position),
|
||||
bounds: bounds,
|
||||
rect: new Rectangle(0, 0, partWidth, partHeight),
|
||||
});
|
||||
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 {
|
||||
id: IdUtil.generate(),
|
||||
status: READY,
|
||||
preset: preset,
|
||||
parts: parts,
|
||||
}
|
||||
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 IMAGE: complete++;
|
||||
case PartLocation.IMAGE: complete++;
|
||||
case _:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package ru.m.puzzlez.core;
|
||||
|
||||
typedef Grid = {
|
||||
var width:Int;
|
||||
var height:Int;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package ru.m.puzzlez.core;
|
||||
|
||||
import ru.m.puzzlez.core.GameEvent.GameAction;
|
||||
import hw.signal.Signal;
|
||||
import ru.m.puzzlez.proto.event.GameAction;
|
||||
import ru.m.puzzlez.proto.event.GameEvent;
|
||||
import ru.m.puzzlez.proto.game.GameState;
|
||||
|
||||
interface IGame {
|
||||
public var state(default, null):GameState;
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package ru.m.puzzlez.core;
|
||||
|
||||
import hw.geom.Rectangle;
|
||||
|
||||
typedef PartBound = {
|
||||
var spike:BoundType;
|
||||
var side:BoundType;
|
||||
}
|
||||
|
||||
typedef PartBounds = {
|
||||
var left:PartBound;
|
||||
var right:PartBound;
|
||||
var top:PartBound;
|
||||
var bottom:PartBound;
|
||||
}
|
||||
|
||||
typedef Part = {
|
||||
var id:Int;
|
||||
var gridX:Int;
|
||||
var gridY:Int;
|
||||
var location:PartLocation;
|
||||
var bounds:PartBounds;
|
||||
var rect:Rectangle;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package ru.m.puzzlez.core;
|
||||
|
||||
import hw.geom.Point;
|
||||
import ru.m.puzzlez.core.Id;
|
||||
|
||||
enum PartLocation {
|
||||
HAND(playerId:PlayerId, point:Point);
|
||||
TABLE(point:Point);
|
||||
IMAGE;
|
||||
}
|
||||
41
src/common/haxe/ru/m/puzzlez/wrap/PointExt.hx
Normal file
41
src/common/haxe/ru/m/puzzlez/wrap/PointExt.hx
Normal file
@@ -0,0 +1,41 @@
|
||||
package ru.m.puzzlez.wrap;
|
||||
|
||||
import ru.m.puzzlez.proto.core.Point;
|
||||
#if openfl
|
||||
import flash.geom.Point as FlashPoint;
|
||||
#end
|
||||
|
||||
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 clone():PointExt {
|
||||
return new Point()
|
||||
.setX(this.x)
|
||||
.setY(this.y);
|
||||
}
|
||||
|
||||
public function add(point:Point):PointExt {
|
||||
return new Point()
|
||||
.setX(this.x + point.x)
|
||||
.setY(this.y + point.y);
|
||||
}
|
||||
|
||||
public function subtract(point:Point):PointExt {
|
||||
return new Point()
|
||||
.setX(this.x - point.x)
|
||||
.setY(this.y - point.y);
|
||||
}
|
||||
|
||||
#if openfl
|
||||
@:from public static function fromFlashPoint(point:FlashPoint):PointExt {
|
||||
return new Point()
|
||||
.setX(point.x)
|
||||
.setY(point.y);
|
||||
}
|
||||
#end
|
||||
}
|
||||
50
src/common/haxe/ru/m/puzzlez/wrap/RectangleExt.hx
Normal file
50
src/common/haxe/ru/m/puzzlez/wrap/RectangleExt.hx
Normal file
@@ -0,0 +1,50 @@
|
||||
package ru.m.puzzlez.wrap;
|
||||
|
||||
import ru.m.puzzlez.proto.core.Point;
|
||||
import ru.m.puzzlez.proto.core.Rectangle;
|
||||
|
||||
abstract RectangleExt(Rectangle) from Rectangle to Rectangle {
|
||||
public var left(get, never):Float;
|
||||
public var right(get, never):Float;
|
||||
public var top(get, never):Float;
|
||||
public var bottom(get, never):Float;
|
||||
public var size(get, never):Point;
|
||||
|
||||
public function new(x:Float = 0, y:Float = 0, width:Float = 0, height:Float = 0) {
|
||||
this = new Rectangle()
|
||||
.setX(x)
|
||||
.setY(y)
|
||||
.setWidth(width)
|
||||
.setHeight(height);
|
||||
}
|
||||
|
||||
private function get_left():Float {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
private function get_right():Float {
|
||||
return this.x + this.width;
|
||||
}
|
||||
|
||||
private function get_top():Float {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
private function get_bottom():Float {
|
||||
return this.y + this.height;
|
||||
}
|
||||
|
||||
private function get_size():Point {
|
||||
return new Point()
|
||||
.setX(this.width)
|
||||
.setY(this.height);
|
||||
}
|
||||
|
||||
public function clone():Rectangle {
|
||||
return new Rectangle()
|
||||
.setX(this.x)
|
||||
.setY(this.y)
|
||||
.setWidth(this.width)
|
||||
.setHeight(this.height);
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,24 @@ syntax = "proto3";
|
||||
|
||||
package ru.m.puzzlez.proto.core;
|
||||
|
||||
message PointProto {
|
||||
message Point {
|
||||
float x = 1;
|
||||
float y = 2;
|
||||
}
|
||||
|
||||
message UserProto {
|
||||
message IntPoint {
|
||||
int32 x = 1;
|
||||
int32 y = 2;
|
||||
}
|
||||
|
||||
message Rectangle {
|
||||
float x = 1;
|
||||
float y = 2;
|
||||
float width = 3;
|
||||
float height = 4;
|
||||
}
|
||||
|
||||
message User {
|
||||
string uuid = 1;
|
||||
string name = 2;
|
||||
}
|
||||
|
||||
@@ -5,16 +5,16 @@ import "game.proto";
|
||||
|
||||
package ru.m.puzzlez.proto.event;
|
||||
|
||||
message GameStartProto {
|
||||
ru.m.puzzlez.proto.game.GameStateProto state = 1;
|
||||
message GameStart {
|
||||
ru.m.puzzlez.proto.game.GameState state = 1;
|
||||
bool resume = 2;
|
||||
}
|
||||
|
||||
message GameCompleteProto {
|
||||
message GameComplete {
|
||||
|
||||
}
|
||||
|
||||
message GameActionProto {
|
||||
message GameAction {
|
||||
string playerId = 1;
|
||||
int32 partId = 2;
|
||||
enum Action {
|
||||
@@ -23,19 +23,22 @@ message GameActionProto {
|
||||
PUT = 2;
|
||||
}
|
||||
Action action = 3;
|
||||
ru.m.puzzlez.proto.core.PointProto point = 4;
|
||||
ru.m.puzzlez.proto.core.Point position = 4;
|
||||
}
|
||||
|
||||
message GameChangeProto {
|
||||
message GameChange {
|
||||
int32 partId = 1;
|
||||
ru.m.puzzlez.proto.core.Point position = 2;
|
||||
ru.m.puzzlez.proto.game.PartLocation location = 3;
|
||||
string playerId = 4;
|
||||
}
|
||||
|
||||
message GameEventProto {
|
||||
message GameEvent {
|
||||
int32 time = 1;
|
||||
oneof event {
|
||||
GameStartProto start = 10;
|
||||
GameCompleteProto complete = 11;
|
||||
GameActionProto action = 12;
|
||||
GameChangeProto change = 13;
|
||||
GameStart start = 10;
|
||||
GameComplete complete = 11;
|
||||
GameAction action = 12;
|
||||
GameChange change = 13;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,27 +4,62 @@ import "core.proto";
|
||||
|
||||
package ru.m.puzzlez.proto.game;
|
||||
|
||||
message PartProto {
|
||||
|
||||
enum BoundType {
|
||||
NONE = 0;
|
||||
OUT = 1;
|
||||
IN = 2;
|
||||
}
|
||||
|
||||
message GridProto {
|
||||
int32 width = 1;
|
||||
int32 height = 2;
|
||||
message PartBound {
|
||||
BoundType spike = 1;
|
||||
BoundType side = 2;
|
||||
}
|
||||
|
||||
message GamePresetProto {
|
||||
message PartBounds {
|
||||
PartBound left = 1;
|
||||
PartBound right = 2;
|
||||
PartBound top = 3;
|
||||
PartBound bottom = 4;
|
||||
}
|
||||
|
||||
enum PartLocation {
|
||||
TABLE = 0;
|
||||
HAND = 1;
|
||||
IMAGE = 2;
|
||||
}
|
||||
|
||||
message Part {
|
||||
int32 id = 1;
|
||||
ru.m.puzzlez.proto.core.IntPoint point = 2;
|
||||
PartBounds bounds = 3;
|
||||
ru.m.puzzlez.proto.core.Point position = 4;
|
||||
PartLocation location = 5;
|
||||
ru.m.puzzlez.proto.core.Rectangle rect = 6;
|
||||
string playerId = 7;
|
||||
}
|
||||
|
||||
message GamePreset {
|
||||
string imageId = 1;
|
||||
GridProto grid = 2;
|
||||
ru.m.puzzlez.proto.core.IntPoint grid = 2;
|
||||
ru.m.puzzlez.proto.core.Rectangle tableRect = 3;
|
||||
ru.m.puzzlez.proto.core.Rectangle imageRect = 4;
|
||||
}
|
||||
|
||||
message GameStateProto {
|
||||
enum GameStatus {
|
||||
READY = 0;
|
||||
STARTED = 1;
|
||||
COMPLETE = 2;
|
||||
}
|
||||
|
||||
message GameState {
|
||||
string id = 1;
|
||||
GamePresetProto preset = 2;
|
||||
repeated PartProto parts = 3;
|
||||
GameStatus status = 2;
|
||||
GamePreset preset = 3;
|
||||
repeated Part parts = 4;
|
||||
bool online = 5;
|
||||
}
|
||||
|
||||
message GameProto {
|
||||
GameStateProto state = 2;
|
||||
repeated ru.m.puzzlez.proto.core.UserProto users = 3;
|
||||
message GameItem {
|
||||
GameState state = 2;
|
||||
repeated ru.m.puzzlez.proto.core.User users = 3;
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ message ErrorResponse {
|
||||
}
|
||||
|
||||
message LoginRequest {
|
||||
ru.m.puzzlez.proto.core.UserProto user = 1;
|
||||
ru.m.puzzlez.proto.core.User user = 1;
|
||||
}
|
||||
|
||||
message LoginResponse {
|
||||
ru.m.puzzlez.proto.core.UserProto user = 1;
|
||||
ru.m.puzzlez.proto.core.User user = 1;
|
||||
}
|
||||
|
||||
message LogoutRequest {}
|
||||
@@ -34,7 +34,7 @@ message GameJoinRequest {
|
||||
message GameLeaveRequest {}
|
||||
|
||||
message GameResponse {
|
||||
ru.m.puzzlez.proto.game.GameProto game = 1;
|
||||
ru.m.puzzlez.proto.game.GameItem game = 1;
|
||||
}
|
||||
|
||||
message GameListRequest {
|
||||
@@ -42,15 +42,15 @@ message GameListRequest {
|
||||
}
|
||||
|
||||
message GameListResponse {
|
||||
repeated ru.m.puzzlez.proto.game.GameProto games = 1;
|
||||
repeated ru.m.puzzlez.proto.game.GameItem games = 1;
|
||||
}
|
||||
|
||||
message GameActionRequest {
|
||||
repeated ru.m.puzzlez.proto.event.GameActionProto actions = 1;
|
||||
repeated ru.m.puzzlez.proto.event.GameAction actions = 1;
|
||||
}
|
||||
|
||||
message GameEventResponse {
|
||||
repeated ru.m.puzzlez.proto.event.GameEventProto events = 1;
|
||||
repeated ru.m.puzzlez.proto.event.GameEvent events = 1;
|
||||
}
|
||||
|
||||
message Request {
|
||||
|
||||
Reference in New Issue
Block a user