[client] update render

This commit is contained in:
2019-05-16 17:13:06 +03:00
parent cd7dd1984b
commit c1ff14111d
24 changed files with 479 additions and 114 deletions

View File

@@ -1,10 +1,10 @@
package ru.m.tankz.render; package ru.m.tankz.render;
import haxework.view.IView; import haxework.view.IView;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.IGame; import ru.m.tankz.game.IGame;
interface IRender extends IView<Dynamic> extends GameListener extends EngineListener { interface IRender extends IView<Dynamic> {
public function draw(game:IEngine):Void; public var game(default, set):IGame;
public function draw():Void;
public function reset():Void; public function reset():Void;
} }

View File

@@ -1,9 +1,9 @@
package ru.m.tankz.render; package ru.m.tankz.render;
import ru.m.geom.Rectangle;
import flash.display.DisplayObjectContainer; import flash.display.DisplayObjectContainer;
import flash.display.Graphics; import flash.display.Graphics;
import flash.display.Sprite; import flash.display.Sprite;
import flash.events.Event;
import haxe.Timer; import haxe.Timer;
import haxework.view.LabelView; import haxework.view.LabelView;
import haxework.view.SpriteView; import haxework.view.SpriteView;
@@ -14,9 +14,17 @@ import ru.m.geom.Point;
import ru.m.tankz.core.EntityType; import ru.m.tankz.core.EntityType;
import ru.m.tankz.engine.IEngine; import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.GameEvent; import ru.m.tankz.game.GameEvent;
import ru.m.tankz.render.RenderItem; import ru.m.tankz.game.IGame;
import ru.m.tankz.render.item.BonusRenderItem;
import ru.m.tankz.render.item.BrickRenderItem;
import ru.m.tankz.render.item.BulletRenderItem;
import ru.m.tankz.render.item.EagleRenderItem;
import ru.m.tankz.render.item.IRenderItem;
import ru.m.tankz.render.item.TankRenderItem;
class Render extends SpriteView implements IRender { class Render extends SpriteView implements IRender implements GameListener {
public var game(default, set):IGame;
private var backgroundLayer:Sprite; private var backgroundLayer:Sprite;
private var groundLayer:Sprite; private var groundLayer:Sprite;
@@ -25,7 +33,7 @@ class Render extends SpriteView implements IRender {
private var upperLayer:Sprite; private var upperLayer:Sprite;
private var background:Sprite; private var background:Sprite;
private var items:Map<Int, RenderItem<Dynamic, Dynamic>>; private var items:Map<Int, IRenderItem>;
public function new() { public function new() {
super(); super();
@@ -43,6 +51,13 @@ class Render extends SpriteView implements IRender {
reset(); reset();
} }
private function set_game(value:IGame):IGame {
game = value;
game.connect(this);
game.engine.spawnSignal.connect(onSpawn);
return game;
}
private function drawBackground(engine:IEngine):Void { private function drawBackground(engine:IEngine):Void {
var g:Graphics = backgroundLayer.graphics; var g:Graphics = backgroundLayer.graphics;
g.clear(); g.clear();
@@ -52,38 +67,25 @@ class Render extends SpriteView implements IRender {
setContentSize(engine.map.width, engine.map.height); setContentSize(engine.map.width, engine.map.height);
} }
public function draw(game:IEngine):Void { public function draw():Void {
for (brick in game.map.bricks) if (brick.config.index > 0) {
if (!items.exists(brick.id)) {
var item:RenderItem<Dynamic, Dynamic> = switch(brick.config.type) {
case 'ace' | 'bush': new BrickItem(brick);
case 'water': new BrickAnimateItem(brick);
case 'armor' | 'brick': new BrickBreakingItem(brick);
case x: null;
};
if (item != null) {
items[brick.id] = item;
if (brick.config.layer > 2) {
upLayer.addChild(item.view);
} else {
groundLayer.addChild(item.view);
}
}
}
}
for (item in items) { for (item in items) {
item.update(); item.update();
} }
if (background == null) { if (background == null) {
drawBackground(game); drawBackground(game.engine);
} }
} }
private function onEnterFrame(event:Event):Void {
draw();
}
private function clearLayer(layer:DisplayObjectContainer) { private function clearLayer(layer:DisplayObjectContainer) {
while (layer.numChildren > 0) layer.removeChildAt(0); while (layer.numChildren > 0) layer.removeChildAt(0);
} }
public function reset():Void { public function reset():Void {
content.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
for (item in items.iterator()) { for (item in items.iterator()) {
item.dispose(); item.dispose();
} }
@@ -101,23 +103,23 @@ class Render extends SpriteView implements IRender {
public function onSpawn(entity:EntityType):Void { public function onSpawn(entity:EntityType):Void {
switch entity { switch entity {
case EAGLE(eagle): case EAGLE(eagle):
var item = new EagleItem(eagle); var item = new EagleRenderItem(eagle);
items.set(eagle.id, item); items.set(eagle.id, item);
entryLayer.addChild(item.view); entryLayer.addChild(item.view);
item.update(); item.update();
case TANK(tank): case TANK(tank):
var item = new TankItem(tank); var item = new TankRenderItem(tank);
items.set(tank.id, item); items.set(tank.id, item);
entryLayer.addChild(item.view); entryLayer.addChild(item.view);
item.update(); item.update();
playAnimate(tank.rect.center, AnimateBundle.tankSpawn()); playAnimate(item.rect.center, AnimateBundle.tankSpawn());
case BULLET(bullet): case BULLET(bullet):
var item = new BulletItem(bullet); var item = new BulletRenderItem(bullet);
items.set(bullet.id, item); items.set(bullet.id, item);
entryLayer.addChild(item.view); entryLayer.addChild(item.view);
item.update(); item.update();
case BONUS(bonus): case BONUS(bonus):
var item = new BonusItem(bonus); var item = new BonusRenderItem(bonus);
items.set(bonus.id, item); items.set(bonus.id, item);
upperLayer.addChild(item.view); upperLayer.addChild(item.view);
item.update(); item.update();
@@ -125,25 +127,51 @@ class Render extends SpriteView implements IRender {
} }
} }
public function onCollision(entity:EntityType, with:EntityType):Void {
}
public function onMove(entity:EntityType):Void {
}
public function onDestroy(entity:EntityType):Void {
}
public function onGameEvent(event:GameEvent):Void { public function onGameEvent(event:GameEvent):Void {
switch event { switch event {
case START(_):
for (brick in game.engine.map.bricks) {
var item:IRenderItem = new BrickRenderItem(brick);
items[brick.id] = item;
if (brick.config.layer > 2) {
upLayer.addChild(item.view);
} else {
groundLayer.addChild(item.view);
}
}
content.addEventListener(Event.ENTER_FRAME, onEnterFrame);
case COMPLETE(_, _):
content.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
case MOVE(BULLET(id, position)):
if (items.exists(id)) {
var item = items[id];
item.move(position);
}
case MOVE(TANK(id, position)):
if (items.exists(id)) {
var item = items[id];
item.move(position);
cast(item, TankRenderItem).moves = true;
}
case STOP(TANK(id)):
if (items.exists(id)) {
var item = items[id];
cast(item, TankRenderItem).moves = false;
}
case CHANGE(TANK(id, type, hits, bonus)):
if (items.exists(id)) {
var item:TankRenderItem = cast items[id];
item.skin = game.config.getTank(type).skin;
item.hits = hits;
item.bonus = bonus;
}
case DESTROY(TANK(id, shot)): case DESTROY(TANK(id, shot)):
if (items.exists(id)) { if (items.exists(id)) {
var item = items[id]; var item = items[id];
entryLayer.removeChild(item.view); entryLayer.removeChild(item.view);
var rect:Rectangle = item.value.rect; playAnimate(item.rect.center, AnimateBundle.tankBoom());
playAnimate(rect.center, AnimateBundle.tankBoom());
if (shot.score != 0) { if (shot.score != 0) {
showScore(rect.center, shot.score); showScore(item.rect.center, shot.score);
} }
items.remove(id); items.remove(id);
} }
@@ -151,38 +179,35 @@ class Render extends SpriteView implements IRender {
if (items.exists(id)) { if (items.exists(id)) {
var item = items[id]; var item = items[id];
entryLayer.removeChild(item.view); entryLayer.removeChild(item.view);
var rect:Rectangle = item.value.rect; var rect = item.rect;
// move boom
var point = rect.center.add(new Point(rect.width * rect.direction.x, rect.height * rect.direction.y)); var point = rect.center.add(new Point(rect.width * rect.direction.x, rect.height * rect.direction.y));
playAnimate(point, AnimateBundle.bulletBoom()); playAnimate(point, AnimateBundle.bulletBoom());
items.remove(id); items.remove(id);
} }
case DESTROY(BONUS(id, shot)):
if (items.exists(id)) {
var item = items[id];
upperLayer.removeChild(item.view);
var rect:Rectangle = item.value.rect;
if (shot.score != 0) {
showScore(rect.center, shot.score);
}
items.remove(id);
}
case DESTROY(EAGLE(id, shot)): case DESTROY(EAGLE(id, shot)):
if (items.exists(id)) { if (items.exists(id)) {
var item = items[id]; var item = items[id];
var rect:Rectangle = item.value.rect; playAnimate(item.rect.center, AnimateBundle.tankBoom());
playAnimate(rect.center, AnimateBundle.tankBoom());
if (shot.score != 0) { if (shot.score != 0) {
showScore(rect.center, shot.score); showScore(item.rect.center, shot.score);
} }
cast(item, EagleRenderItem).death = true;
} }
case MOVE(TANK(id, position)) | MOVE(BULLET(id, position)): case DESTROY(BONUS(id, shot)):
if (items.exists(id)) { if (items.exists(id)) {
var item = items[id]; var item = items[id];
item.value.rect.x = position.x; upperLayer.removeChild(item.view);
item.value.rect.y = position.y; if (shot.score != 0) {
item.value.rect.direction = position.direction; showScore(item.rect.center, shot.score);
item.update();
} }
items.remove(id);
}
case DESTROY(CELL(cellX, cellY, shot)):
// ToDo: redraw only cell?
var brick = game.engine.map.getBrick(new Point(cellX, cellY));
var item:BrickRenderItem = cast items[brick.id];
item.redraw(brick);
case _: case _:
} }
} }

View File

@@ -0,0 +1,34 @@
package ru.m.tankz.render.item;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.PixelSnapping;
import openfl.Assets;
import ru.m.geom.Rectangle;
class BitmapRenderItem extends RenderItem {
public var image(default, set):String;
private var bitmapData:BitmapData;
private var bitmap:Bitmap;
public function new(rect:Rectangle) {
super(rect);
this.bitmap = new Bitmap(null, PixelSnapping.AUTO, true);
move(rect.position);
}
override private function get_view():DisplayObject {
return bitmap;
}
private function set_image(value:String):String {
if (image != value) {
image = value;
bitmapData = Assets.getBitmapData(image);
bitmap.bitmapData = bitmapData;
}
return image;
}
}

View File

@@ -0,0 +1,22 @@
package ru.m.tankz.render.item;
import ru.m.tankz.core.Bonus;
import ru.m.tankz.Type.BonusType;
class BonusRenderItem extends BitmapRenderItem {
public var type(default, set):BonusType;
public function new(bonus:Bonus) {
super(bonus.rect);
type = bonus.config.type;
}
private function set_type(value:BonusType):BonusType {
if (type != value) {
type = value;
image = 'resources/image/bonus/${type}.png';
}
return type;
}
}

View File

@@ -0,0 +1,54 @@
package ru.m.tankz.render.item;
import flash.display.BitmapData;
import flash.display.Shape;
import openfl.Assets;
import openfl.display.DisplayObject;
import ru.m.tankz.map.Brick;
import ru.m.tankz.Type.BrickType;
class BrickRenderItem extends RenderItem {
public var type(default, set):BrickType;
private var image:BitmapData;
private var shape:Shape;
public function new(brick:Brick) {
super(brick.rect);
this.shape = new Shape();
this.type = brick.config.type;
redraw(brick);
move(rect.position);
}
override private function get_view():DisplayObject {
return shape;
}
private function set_type(value:BrickType):BrickType {
if (type != value) {
type = value;
image = Assets.getBitmapData('resources/image/map/${type}.png');
}
return type;
}
public function redraw(brick:Brick):Void {
shape.graphics.clear();
if (brick.destroyed) return;
if (brick.config.index > 0) {
shape.graphics.beginBitmapFill(image);
if (brick.broken == 0) {
shape.graphics.drawRect(0, 0, brick.rect.width, brick.rect.height);
} else {
for (c in brick.cells) {
if (!c.destroyed) {
shape.graphics.drawRect(c.rect.x - brick.rect.x, c.rect.y - brick.rect.y, c.rect.width, c.rect.height);
}
}
}
shape.graphics.endFill();
}
}
}

View File

@@ -0,0 +1,21 @@
package ru.m.tankz.render.item;
import ru.m.tankz.core.Bullet;
class BulletRenderItem extends BitmapRenderItem {
public var piercing(default, set):Int = -1;
public function new(bullet:Bullet) {
super(bullet.rect);
piercing = bullet.config.piercing;
}
private function set_piercing(value:Int):Int {
if (piercing != value) {
piercing = value;
var type = piercing > 0 ? 'piercing' : 'normal';
image = 'resources/image/bullet/${type}.png';
}
return piercing;
}
}

View File

@@ -0,0 +1,21 @@
package ru.m.tankz.render.item;
import ru.m.tankz.core.Eagle;
class EagleRenderItem extends BitmapRenderItem {
public var death(default, set):Bool = true;
public function new(eagle:Eagle) {
super(eagle.rect);
death = false;
}
private function set_death(value:Bool):Bool {
if (death != value) {
death = value;
var suffix = death ? '-death' : '';
image = 'resources/image/eagle/eagle${suffix}.png';
}
return death;
}
}

View File

@@ -0,0 +1,13 @@
package ru.m.tankz.render.item;
import flash.display.DisplayObject;
import ru.m.geom.Position;
import ru.m.geom.Rectangle;
interface IRenderItem {
public var view(get, null):DisplayObject;
public var rect(default, null):Rectangle;
public function move(position:Position):Void;
public function update():Void;
public function dispose():Void;
}

View File

@@ -0,0 +1,35 @@
package ru.m.tankz.render.item;
import flash.display.DisplayObject;
import ru.m.geom.Position;
import ru.m.geom.Rectangle;
class RenderItem implements IRenderItem {
public var view(get, null):DisplayObject;
public var rect(default, null):Rectangle;
public function new(rect:Rectangle) {
this.rect = rect.clone();
}
private function get_view():DisplayObject {
throw "Not Implemented";
}
public function move(position:Position):Void {
view.x = rect.x = position.x;
view.y = rect.y = position.y;
if (position.direction != null) {
rect.direction = position.direction;
view.rotation = rect.direction.angle;
view.x = rect.x - rect.width * (rect.direction.x + 1) / 2 + rect.width * (rect.direction.y + 1) / 2 + 0.5 * rect.width;
view.y = rect.y - rect.height * (rect.direction.x + 1) / 2 - rect.height * (rect.direction.y + 1) / 2 + 1.5 * rect.height;
}
}
public function update():Void {}
public function dispose():Void {}
}

View File

@@ -0,0 +1,97 @@
package ru.m.tankz.render.item;
import flash.display.BitmapData;
import haxework.color.Color;
import haxework.view.utils.BitmapUtil;
import openfl.Assets;
import ru.m.tankz.core.Tank;
class TankRenderItem extends BitmapRenderItem {
public var color(default, default):Color;
public var skin(default, set):String;
public var hits(default, set):Int;
public var bonus(default, set):Bool;
public var moves(default, set):Bool;
private var images:Array<BitmapData>;
private var frame:Int;
public function new(tank:Tank) {
super(tank.rect);
color = tank.color;
skin = tank.config.skin;
hits = tank.hits;
bonus = tank.bonus;
move(rect.position);
}
private function redraw():Void {
var image1 = Assets.getBitmapData('resources/image/tank/${skin}-0.png');
var image2 = Assets.getBitmapData('resources/image/tank/${skin}-1.png');
var color1:Color = switch hits {
case 1: 0x869C43;
case 2: 0xDEAF80;
case 3: 0x5EA67A;
case _: color;
}
var color2:Color = color1;
if (bonus) {
color1 = 0xff00aa;
}
if (!color1.zero) {
image1 = BitmapUtil.colorize(image1, color1);
}
if (!color1.zero) {
image2 = BitmapUtil.colorize(image2, color2);
}
images = [
image1, image1, image1,
image2, image2, image2,
];
frame = 0;
bitmap.bitmapData = images[frame];
}
private function set_skin(value:String):String {
if (skin != value) {
skin = value;
redraw();
}
return skin;
}
private function set_hits(value:Int):Int {
if (hits != value) {
hits = value;
redraw();
}
return hits;
}
private function set_bonus(value:Bool):Bool {
if (bonus != value) {
bonus = value;
redraw();
}
return bonus;
}
private function set_moves(value:Bool):Bool {
if (moves != value) {
moves = value;
bitmap.bitmapData = images[0];
}
return moves;
}
override public function update():Void {
super.update();
if (moves) {
frame++;
if (frame > images.length - 1) {
frame = 0;
}
bitmap.bitmapData = images[frame];
}
}
}

View File

@@ -14,14 +14,22 @@ import ru.m.tankz.game.record.GameRecord;
public function save(record:GameRecord):Void { public function save(record:GameRecord):Void {
L.d("RecordStorage", 'save $record'); L.d("RecordStorage", 'save $record');
write(record.id, record); write('${record.info.id}.info', record.info);
write(record.info.id, record);
} }
public function iterator():Iterator<GameRecord> { override public function delete(id:String):Void {
super.delete(id);
super.delete('${id}.info');
}
public function iterator():Iterator<GameRecordInfo> {
var data:DynamicAccess<String> = so.data; var data:DynamicAccess<String> = so.data;
for (id in data.keys()) { for (id in data.keys()) {
try { try {
if (StringTools.endsWith(id, '.info')) {
@yield return read(id); @yield return read(id);
}
} catch (error:Dynamic) { } catch (error:Dynamic) {
L.w("RecordStorage", 'read ', error); L.w("RecordStorage", 'read ', error);
delete(id); delete(id);

View File

@@ -1,6 +1,5 @@
package ru.m.tankz.view; package ru.m.tankz.view;
import flash.events.Event;
import haxe.ds.Option; import haxe.ds.Option;
import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameSwitcher;
import haxework.view.VGroupView; import haxework.view.VGroupView;
@@ -51,8 +50,7 @@ import ru.m.tankz.view.game.GameView;
private function start(state:GameState):Void { private function start(state:GameState):Void {
gameView.type = state.type; gameView.type = state.type;
game = new Game(state); game = new Game(state);
game.connect(gameView.render); gameView.render.game = game;
game.engine.connect(gameView.render);
game.connect(soundManager); game.connect(soundManager);
game.connect(this); game.connect(this);
if (gameView.panel != null) { if (gameView.panel != null) {
@@ -63,15 +61,13 @@ import ru.m.tankz.view.game.GameView;
game.connect(recorder); game.connect(recorder);
runner = new GameRunner(game); runner = new GameRunner(game);
runner.start(state); runner.start(state);
content.addEventListener(Event.ENTER_FRAME, _redraw); gameView.render.draw();
gameView.render.draw(game.engine);
} }
private function play(record:GameRecord):Void { private function play(record:GameRecord):Void {
gameView.type = record.type; gameView.type = record.info.type;
game = new Game(record.state); game = new Game(record.state);
game.connect(gameView.render); gameView.render.game = game;
game.engine.connect(gameView.render);
game.connect(soundManager); game.connect(soundManager);
//game.connect(this); //game.connect(this);
if (gameView.panel != null) { if (gameView.panel != null) {
@@ -79,12 +75,10 @@ import ru.m.tankz.view.game.GameView;
} }
player = new GamePlayer(game, record); player = new GamePlayer(game, record);
player.start(); player.start();
content.addEventListener(Event.ENTER_FRAME, _redraw); gameView.render.draw();
gameView.render.draw(game.engine);
} }
private function stop():Void { private function stop():Void {
content.removeEventListener(Event.ENTER_FRAME, _redraw);
if (runner != null) { if (runner != null) {
runner.dispose(); runner.dispose();
runner = null; runner = null;
@@ -122,10 +116,6 @@ import ru.m.tankz.view.game.GameView;
stop(); stop();
} }
private function _redraw(_):Void {
gameView.render.draw(game.engine);
}
public function close():Void { public function close():Void {
switcher.change(LevelFrame.ID); switcher.change(LevelFrame.ID);
} }

View File

@@ -10,7 +10,7 @@ import ru.m.tankz.storage.RecordStorage;
@:template class RecordFrame extends VGroupView { @:template class RecordFrame extends VGroupView {
public static var ID(default, never):String = "record"; public static var ID(default, never):String = "record";
@:view var data:VListView<GameRecord>; @:view var data:VListView<GameRecordInfo>;
@:provide var recordStorage:RecordStorage; @:provide var recordStorage:RecordStorage;
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
@@ -18,13 +18,12 @@ import ru.m.tankz.storage.RecordStorage;
public function onShow():Void { public function onShow():Void {
var data = Lambda.array(recordStorage); var data = Lambda.array(recordStorage);
data.sort(function(a:GameRecord, b:GameRecord) return Std.int(b.date.getTime() - a.date.getTime())); data.sort(function(a:GameRecordInfo, b:GameRecordInfo) return Std.int(b.date.getTime() - a.date.getTime()));
this.data.data = data; this.data.data = data;
} }
private function onRecordSelect(item:IListItemView<GameRecord>):Void { private function onRecordSelect(item:IListItemView<GameRecordInfo>):Void {
//record = item.data;
//switcher.change(GameFrame.ID);
} }
private function close() { private function close() {

View File

@@ -7,20 +7,20 @@ import haxework.view.list.ListView;
import ru.m.tankz.game.record.GameRecord; import ru.m.tankz.game.record.GameRecord;
import ru.m.tankz.storage.RecordStorage; import ru.m.tankz.storage.RecordStorage;
@:template class RecordView extends HGroupView implements IListItemView<GameRecord> { @:template class RecordView extends HGroupView implements IListItemView<GameRecordInfo> {
@:view var date:LabelView; @:view var date:LabelView;
@:view var type:LabelView; @:view var type:LabelView;
@:view var level:LabelView; @:view var level:LabelView;
@:view var preset:LabelView; @:view var preset:LabelView;
public var item_index(default, default):Int; public var item_index(default, default):Int;
public var data(default, set):GameRecord; public var data(default, set):GameRecordInfo;
@:provide var recordStorage:RecordStorage; @:provide var recordStorage:RecordStorage;
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
@:provide var record:GameRecord; @:provide var record:GameRecord;
private function set_data(value:GameRecord):GameRecord { private function set_data(value:GameRecordInfo):GameRecordInfo {
if (data != value) { if (data != value) {
data = value; data = value;
date.text = data.date.toString(); date.text = data.date.toString();
@@ -32,7 +32,7 @@ import ru.m.tankz.storage.RecordStorage;
} }
private function play():Void { private function play():Void {
record = data; record = recordStorage.read(data.id);
switcher.change(GameFrame.ID); switcher.change(GameFrame.ID);
} }

View File

@@ -0,0 +1,7 @@
package ru.m.geom;
typedef Position = {
var x:Float;
var y:Float;
@:optional var direction:Direction;
}

View File

@@ -11,6 +11,7 @@ class Rectangle {
public var right(get, null):Float; public var right(get, null):Float;
public var top(get, null):Float; public var top(get, null):Float;
public var bottom(get, null):Float; public var bottom(get, null):Float;
public var position(get, null):Position;
public function new(x:Float, y:Float, width:Float, height:Float) { public function new(x:Float, y:Float, width:Float, height:Float) {
this.x = x; this.x = x;
@@ -58,7 +59,7 @@ class Rectangle {
} }
direction = value; direction = value;
} }
return value; return direction;
} }
public function contain(point:Point):Bool { public function contain(point:Point):Bool {
@@ -85,6 +86,12 @@ class Rectangle {
} }
} }
public function clone():Rectangle {
var rect = new Rectangle(x, y, width, height);
rect.direction = direction;
return rect;
}
public function toString():String { public function toString():String {
return 'Rectangle{x=$x,y=$y,width=$width,height=$height}'; return 'Rectangle{x=$x,y=$y,width=$width,height=$height}';
} }
@@ -104,4 +111,8 @@ class Rectangle {
function get_bottom():Float { function get_bottom():Float {
return y + height; return y + height;
} }
private function get_position():Position {
return {x:x, y:y, direction:direction};
}
} }

View File

@@ -100,7 +100,6 @@ import ru.m.tankz.map.LevelMap;
var collision:Bool = false; var collision:Bool = false;
for (cell in cells) { for (cell in cells) {
if (cell.getCollision(entity.layer)) { if (cell.getCollision(entity.layer)) {
entity.rect.lean(cell.rect);
collision = true; collision = true;
var with = EntityTypeResolver.of(cell); var with = EntityTypeResolver.of(cell);
collisionSignal.emit(entityType, with); collisionSignal.emit(entityType, with);

View File

@@ -1,6 +1,7 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import ru.m.geom.Point; import ru.m.geom.Point;
import ru.m.geom.Position;
import ru.m.tankz.bundle.IConfigBundle; import ru.m.tankz.bundle.IConfigBundle;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;

View File

@@ -1,15 +1,9 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import ru.m.geom.Direction; import ru.m.geom.Position;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;
import ru.m.tankz.Type; import ru.m.tankz.Type;
typedef Position = {
var x:Float;
var y:Float;
@:optional var direction:Direction;
}
enum SpawnEvent { enum SpawnEvent {
EAGLE(id:Int, position:Position, teamId:TeamId); EAGLE(id:Int, position:Position, teamId:TeamId);
TANK(id:Int, position:Position, playerId:PlayerId, type:TankType); TANK(id:Int, position:Position, playerId:PlayerId, type:TankType);
@@ -41,7 +35,12 @@ enum MoveEvent {
BULLET(id:Int, position:Position); BULLET(id:Int, position:Position);
} }
enum StopEvent {
TANK(id:Int);
}
enum ChangeEvent { enum ChangeEvent {
TANK(id:Int, type:TankType, hits:Int, bonus:Bool);
PLAYER_SCORE(playerId:PlayerId, value:Int); PLAYER_SCORE(playerId:PlayerId, value:Int);
PLAYER_LIFE(playerId:PlayerId, value:Int); PLAYER_LIFE(playerId:PlayerId, value:Int);
TEAM_SCORE(teamId:TeamId, value:Int); TEAM_SCORE(teamId:TeamId, value:Int);
@@ -52,6 +51,7 @@ enum GameEvent {
START(state:GameState); START(state:GameState);
SPAWN(event:SpawnEvent); SPAWN(event:SpawnEvent);
MOVE(event:MoveEvent); MOVE(event:MoveEvent);
STOP(event:StopEvent);
HIT(event:HitEvent); HIT(event:HitEvent);
DESTROY(event:DestroyEvent); DESTROY(event:DestroyEvent);
CHANGE(event:ChangeEvent); CHANGE(event:ChangeEvent);

View File

@@ -1,10 +1,12 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import ru.m.geom.Direction;
import haxe.ds.Option; import haxe.ds.Option;
import haxe.Timer; import haxe.Timer;
import haxework.signal.Signal; import haxework.signal.Signal;
import ru.m.geom.Line; import ru.m.geom.Line;
import ru.m.geom.Point; import ru.m.geom.Point;
import ru.m.geom.Position;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;
import ru.m.tankz.control.Controller; import ru.m.tankz.control.Controller;
import ru.m.tankz.control.IControlFactory; import ru.m.tankz.control.IControlFactory;
@@ -55,7 +57,7 @@ class GameRunner implements EngineListener implements GameListener {
return { return {
x: (point.x + 1) * game.config.map.cellWidth, x: (point.x + 1) * game.config.map.cellWidth,
y: (point.y + 1) * game.config.map.cellHeight, y: (point.y + 1) * game.config.map.cellHeight,
direction: point.direction, direction: Direction.fromString(point.direction),
} }
} }
@@ -141,6 +143,14 @@ class GameRunner implements EngineListener implements GameListener {
} }
} }
private inline function emitTankMove(tank:Tank):Void {
gameEventSignal.emit(GameEvent.MOVE(TANK(tank.id, {x:tank.rect.x, y:tank.rect.y, direction:tank.rect.direction})));
}
private inline function emitTankChange(tank:Tank):Void {
gameEventSignal.emit(GameEvent.CHANGE(TANK(tank.id, tank.config.type, tank.hits, tank.bonus)));
}
public function onCollision(entity:EntityType, with:EntityType):Void { public function onCollision(entity:EntityType, with:EntityType):Void {
switch entity { switch entity {
case EntityType.TANK(tank): case EntityType.TANK(tank):
@@ -151,8 +161,13 @@ class GameRunner implements EngineListener implements GameListener {
switch [entity, with] { switch [entity, with] {
case [TANK(tank), TANK(other_tank)]: case [TANK(tank), TANK(other_tank)]:
tank.rect.lean(other_tank.rect); tank.rect.lean(other_tank.rect);
emitTankMove(tank);
case [TANK(tank), EAGLE(eagle)]: case [TANK(tank), EAGLE(eagle)]:
tank.rect.lean(eagle.rect); tank.rect.lean(eagle.rect);
emitTankMove(tank);
case [TANK(tank), CELL(cell)]:
tank.rect.lean(cell.rect);
emitTankMove(tank);
case [BULLET(bullet), BULLET(other_bullet)]: case [BULLET(bullet), BULLET(other_bullet)]:
gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet.id))); gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet.id)));
gameEventSignal.emit(GameEvent.DESTROY(BULLET(other_bullet.id))); gameEventSignal.emit(GameEvent.DESTROY(BULLET(other_bullet.id)));
@@ -173,9 +188,11 @@ class GameRunner implements EngineListener implements GameListener {
spawnBonus(); spawnBonus();
} }
gameEventSignal.emit(GameEvent.HIT(TANK(tank.id, buildShot(bullet)))); gameEventSignal.emit(GameEvent.HIT(TANK(tank.id, buildShot(bullet))));
emitTankChange(tank);
} else if (tank.config.downgrade != null) { } else if (tank.config.downgrade != null) {
tank.config = game.config.getTank(tank.config.downgrade); tank.config = game.config.getTank(tank.config.downgrade);
gameEventSignal.emit(GameEvent.HIT(TANK(tank.id, buildShot(bullet)))); gameEventSignal.emit(GameEvent.HIT(TANK(tank.id, buildShot(bullet))));
emitTankChange(tank);
} else { } else {
var score = tank.config.score; var score = tank.config.score;
if (tank.playerId.team == bullet.playerId.team) { if (tank.playerId.team == bullet.playerId.team) {
@@ -198,7 +215,7 @@ class GameRunner implements EngineListener implements GameListener {
public function onMove(entity:EntityType):Void { public function onMove(entity:EntityType):Void {
switch entity { switch entity {
case TANK(tank): case TANK(tank):
gameEventSignal.emit(GameEvent.MOVE(TANK(tank.id, {x:tank.rect.x, y:tank.rect.y, direction:tank.rect.direction}))); emitTankMove(tank);
case BULLET(bullet): case BULLET(bullet):
gameEventSignal.emit(GameEvent.MOVE(BULLET(bullet.id, {x:bullet.rect.x, y:bullet.rect.y, direction:bullet.rect.direction}))); gameEventSignal.emit(GameEvent.MOVE(BULLET(bullet.id, {x:bullet.rect.x, y:bullet.rect.y, direction:bullet.rect.direction})));
case _: case _:
@@ -237,6 +254,8 @@ class GameRunner implements EngineListener implements GameListener {
case "clock": case "clock":
for (t in game.engine.iterTanks(alienTank(tank.playerId.team))) { for (t in game.engine.iterTanks(alienTank(tank.playerId.team))) {
t.freezing.on(bonus.config.duration); t.freezing.on(bonus.config.duration);
t.stop();
gameEventSignal.emit(GameEvent.STOP(TANK(t.id)));
} }
case "shovel": case "shovel":
// ToDo: protect eagle/area // ToDo: protect eagle/area
@@ -260,6 +279,7 @@ class GameRunner implements EngineListener implements GameListener {
} else { } else {
tank.hits++; tank.hits++;
} }
emitTankChange(tank);
} }
private function changeScore(playerId:PlayerId, score:Int):Void { private function changeScore(playerId:PlayerId, score:Int):Void {
@@ -305,6 +325,8 @@ class GameRunner implements EngineListener implements GameListener {
} }
gameEventSignal.emit(GameEvent.SPAWN(BULLET(++entityId, position, tank.playerId))); gameEventSignal.emit(GameEvent.SPAWN(BULLET(++entityId, position, tank.playerId)));
} }
case GameEvent.ACTION(tankId, STOP):
gameEventSignal.emit(GameEvent.STOP(TANK(tankId)));
case GameEvent.SPAWN(TANK(_, _, playerId, _)): case GameEvent.SPAWN(TANK(_, _, playerId, _)):
game.getPlayer(playerId).control.start(); game.getPlayer(playerId).control.start();
case GameEvent.SPAWN(BULLET(_, _, playerId)): case GameEvent.SPAWN(BULLET(_, _, playerId)):
@@ -361,7 +383,7 @@ class GameRunner implements EngineListener implements GameListener {
if (cell.armor == bullet.config.piercing) { if (cell.armor == bullet.config.piercing) {
gameEventSignal.emit(GameEvent.DESTROY(CELL(cell.cellX, cell.cellY, buildShot(bullet)))); gameEventSignal.emit(GameEvent.DESTROY(CELL(cell.cellX, cell.cellY, buildShot(bullet))));
} else if (cell.armor < bullet.config.piercing) { } else if (cell.armor < bullet.config.piercing) {
var brick = game.engine.map.getBrick(cell); var brick = game.engine.map.getBrick(cell.position);
for (cell in brick.cells) { for (cell in brick.cells) {
gameEventSignal.emit(GameEvent.DESTROY(CELL(cell.cellX, cell.cellY, buildShot(bullet)))); gameEventSignal.emit(GameEvent.DESTROY(CELL(cell.cellX, cell.cellY, buildShot(bullet))));
} }

View File

@@ -9,27 +9,33 @@ typedef EventItem = {
event:GameEvent event:GameEvent
} }
class GameRecord { class GameRecordInfo {
public var id(default, default):String; public var id(default, default):String;
public var date(default, default):Date; public var date(default, default):Date;
public var type(default, default):GameType; public var type(default, default):GameType;
public var presetId(default, default):PresetId; public var presetId(default, default):PresetId;
public var levelId(default, default):LevelId; public var levelId(default, default):LevelId;
public function new() {}
}
class GameRecord {
public var info(default, null):GameRecordInfo;
public var events(default, default):Array<EventItem>; public var events(default, default):Array<EventItem>;
public var state(get, null):GameState; public var state(get, null):GameState;
public function new() { public function new() {
this.id = UUID.generateRandom(new Random()).toString(); this.info = new GameRecordInfo();
this.date = null; this.info.id = UUID.generateRandom(new Random()).toString();
this.events = []; this.events = [];
} }
private inline function get_state():GameState { private inline function get_state():GameState {
return new GameState(type, presetId, levelId); return new GameState(info.type, info.presetId, info.levelId);
} }
public function toString():String { public function toString():String {
return 'GameRecord{id=$id,date=$date,type=$type,preset=$presetId,level=$levelId,events=${events.length}}'; return 'GameRecord{id=${info.id},date=${info.date},type=${info.type},preset=${info.presetId},level=${info.levelId},events=${events.length}}';
} }
} }

View File

@@ -17,9 +17,9 @@ class GameRecorder implements GameListener {
public function onGameEvent(event:GameEvent):Void { public function onGameEvent(event:GameEvent):Void {
switch event { switch event {
case GameEvent.START(state): case GameEvent.START(state):
record.type = state.type; record.info.type = state.type;
record.presetId = state.presetId; record.info.presetId = state.presetId;
record.levelId = state.levelId; record.info.levelId = state.levelId;
start(); start();
case GameEvent.COMPLETE(_, _): case GameEvent.COMPLETE(_, _):
stop(); stop();
@@ -30,7 +30,7 @@ class GameRecorder implements GameListener {
public function start():Void { public function start():Void {
frame = 0; frame = 0;
record.date = Date.now(); record.info.date = Date.now();
Lib.current.stage.addEventListener(Event.ENTER_FRAME, onEnterFrame); Lib.current.stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
} }

View File

@@ -62,8 +62,8 @@ class LevelMap {
})); }));
} }
public function getBrick(cell:GridCell):Brick { public function getBrick(position:Point):Brick {
return bricksMap.get(cell.position); return bricksMap.get(position);
} }
public function getPointBrick(point:Point):Brick { public function getPointBrick(point:Point):Brick {

View File

@@ -163,7 +163,7 @@ presets:
- {<<: *team_human} - {<<: *team_human}
- id: bot - id: bot
spawnInterval: 3000 spawnInterval: 3000
life: 1 life: 10
players: players:
- {<<: *bot, index: 0, control: bot-stupid} - {<<: *bot, index: 0, control: bot-stupid}
- {<<: *bot, index: 1, control: bot-stupid} - {<<: *bot, index: 1, control: bot-stupid}