16 Commits
0.2.1 ... 0.5.0

Author SHA1 Message Date
db59377cb2 Merge remote-tracking branch 'origin/editor' 2018-02-06 23:20:20 +03:00
1f6e4fbc3d [common] update level config, spawns points 2018-02-06 17:41:49 +03:00
db5b805276 [editor] ui update 2018-02-06 10:54:48 +03:00
38538b9147 fix 2018-02-05 22:47:46 +03:00
15d830a0d4 [game] added dota maps 2018-02-05 22:24:00 +03:00
feb5bafe72 [editor] update 2018-02-05 20:46:11 +03:00
b36fd77d74 [editor] added save button 2018-02-05 17:54:16 +03:00
421f925a60 [editor] added editor module 2018-02-04 23:32:00 +03:00
ad4744cfef [common] change game team players from array to map 2018-02-04 22:33:50 +03:00
1c9ccf0fb8 [common] humans in DotaGame 2018-02-04 21:02:08 +03:00
e0ceff68f9 update worklist 2018-02-04 14:11:43 +03:00
59cab68e3e [common] tanks spawn types 2018-02-02 20:53:26 +03:00
f9cb985059 [engine] update collision detect 2018-02-02 17:30:50 +03:00
6f338584eb [render] added animates 2018-02-01 00:05:14 +03:00
f2f860fc9d Merge branch 'develop' 2018-01-31 23:04:38 +03:00
3e65a5ba5d [cap] use master branch in production 2018-01-17 09:16:17 +03:00
43 changed files with 1372 additions and 477 deletions

62
WORK.md
View File

@@ -1,46 +1,62 @@
* build
* gulp 100%
* linux 100%
* deb-package 100%
* windows 0%
* exe-package 0% (inno setup)
* deploy
* capistrano 100%
* ui
* login frame
* select person frame (autoselect)
* game mode frame (single, start server, find server)
* game frame
* auth frame 0%
* select game frame 30% (classic 1/2 player, dota singe/coop/vs)
* select level frame 10%
* game frame 50%
* engine
* config 90%
* map 80%
* tanks 30%
* bullets 30%
* boxes 0%
* map changes 50%
* config 100%
* map 100%
* tanks 100%
* bullets 100%
* boxes 100%
* map changes 100%
* bonuses 0%
* eagle 0%
* eagle 100%
* game
* classic
* state 0%
* bot 5%
* state 50%
* bot 50%
* player: 50%
* dota
* state 50%
* bot 10%
* player 0%
* state
* score 0%
* save/load 0%
* export/import 0%
* render
* map 100%
* tanks 100%
* bullet 100%
* calc redraw 20%
* calc redraw 50%
* animations
* tank spawn
* bullet boom
* tank boom
* bonuses
* html5
* tank spawn 0%
* tank move 100%
* map water 0%
* bullet boom 90%
* tank boom 90%
* bonuses 0%
* html5 50%
* proto
...
* webapp
* angular app 0%
* google analytics 0%
* editor
* open 0%
* edit 0%
* save 0%

35
build/editor.js Normal file
View File

@@ -0,0 +1,35 @@
const gulp = require('gulp');
const yargs = require('yargs');
const Haxe = require('../tasks/haxe');
const FlashPlayer = require('../tasks/flashplayer');
const version = require('./version');
const prepare = require('./prepare');
const debug = require('../tasks/debug');
const build = (platform) => function build() {
const argv = yargs.argv;
return gulp.src('.')
.pipe(new Haxe().openfl({
command: 'build',
platform: platform,
version: version,
values: {
build_editor: true
},
debug: argv.dev,
}))
.pipe(gulp.dest(`target/${platform}`));
};
const testFlash = function() {
const argv = yargs.argv;
return build('flash')()
.pipe(new FlashPlayer().run(argv.dev))
.pipe(debug());
};
exports['editor:flash'] = gulp.series(prepare(Haxe.ID), build('flash'));
exports['editor:flash:test'] = gulp.series(prepare(Haxe.ID, FlashPlayer.ID), testFlash);

View File

@@ -10,4 +10,4 @@ user = fetch(:user)
server host, ssh_options: { port: 22, user: user, forward_agent: true }
set :tmp_dir, "/home/#{user}/tmp"
set :branch, 'develop'
set :branch, 'master'

View File

@@ -29,6 +29,7 @@ const merge = (value) => {
exports.update = prepare.update;
merge('./build/prepare');
merge('./build/client');
merge('./build/editor');
merge('./build/server');

View File

@@ -1,6 +1,6 @@
{
"name": "tankz",
"version": "0.2.1",
"version": "0.5.0",
"private": true,
"devDependencies": {
"ansi-colors": "^1.0.1",
@@ -23,6 +23,7 @@
"plugin-error": "^0.1.2",
"progress": "^2.0.0",
"promise-streams": "^2.1.1",
"rmdir": "^1.2.0",
"tar": "^4.2.0",
"tmp-file": "^2.0.1",
"unzip-stream": "^0.2.1",

View File

@@ -12,6 +12,7 @@
<haxelib name="protohx" version="0.4.6"/>
<haxelib name="haxework" version="git"/>
<haxelib name="yaml" version="1.3.0"/>
<window fps="30"/>
<window width="1024" height="768" unless="html5"/>
<haxeflag name="-D" value="swf-gpu"/>
<haxeflag name="-D" value="native-trace"/>
@@ -19,4 +20,9 @@
<haxeflag name="-dce" value="no"/>
<haxeflag name="-D" value="dom"/>
<!--<template path="src/client/webapp/index_template.html" rename="index.html"/>-->
<section if="build_editor">
<app main="ru.m.tankz.editor.Editor" path="target" file="editor"/>
<source path="src/editor/haxe"/>
</section>
</project>

View File

@@ -7,21 +7,45 @@
"contentSize": true, "bottomMargin": 15
},
{
"id": "start_1p",
"@type": "haxework.gui.LabelView", "@style": "label",
"fontSize": 20, "topMargin": 15,
"contentSize": true,
"text": "Classic"
},
{
"id": "classic_1p",
"@type": "haxework.gui.ButtonView",
"text": "1 Player",
"@style": "button"
},
{
"id": "start_2p",
"id": "classic_2p",
"@type": "haxework.gui.ButtonView",
"text": "2 Player",
"@style": "button"
},
{
"id": "dota",
"@type": "haxework.gui.LabelView", "@style": "label",
"fontSize": 20, "topMargin": 15,
"contentSize": true,
"text": "DotA"
},
{
"id": "dota_1p",
"@type": "haxework.gui.ButtonView",
"text": "DotA",
"text": "1 Player",
"@style": "button"
},
{
"id": "dota_2p_coop",
"@type": "haxework.gui.ButtonView",
"text": "2 COOP",
"@style": "button"
},
{
"id": "dota_2p_vs",
"@type": "haxework.gui.ButtonView",
"text": "2 VS",
"@style": "button"
}
]

View File

@@ -0,0 +1,73 @@
package ru.m.animate;
import flash.display.PixelSnapping;
import haxe.Timer;
import flash.display.Bitmap;
import flash.display.BitmapData;
class Animate extends Bitmap {
private static var timer:Timer;
private static var instances:Array<Animate> = [];
private static function init():Void {
if (timer == null) {
timer = new Timer(30);
timer.run = updateAll;
}
}
private static function updateAll():Void {
for (instance in instances) {
if (instance.playing) {
instance.update();
}
}
}
private static var a = new BitmapData(1, 1);
public var playing(default, set):Bool;
public var frames(default, set):Array<BitmapData>;
private var index:Int;
public function new(?frames:Array<BitmapData>) {
super(null, PixelSnapping.AUTO, true);
this.frames = frames == null ? [] : frames;
init();
instances.push(this);
}
public function set_frames(value:Array<BitmapData>):Array<BitmapData> {
if (value != null) {
frames = value;
bitmapData = frames[0];
index = 0;
}
return frames;
}
public function set_playing(value:Bool):Bool {
if (playing != value) {
playing = value;
}
return playing;
}
private function update():Void {
if (++index >= frames.length) {
index = 0;
}
var nextBitmapData = frames[index];
x -= (nextBitmapData.width - bitmapData.width) / 2;
y -= (nextBitmapData.height - bitmapData.height) / 2;
bitmapData = nextBitmapData;
}
public function dispose():Void {
if (instances.indexOf(this) > -1) {
instances.remove(this);
}
}
}

View File

@@ -0,0 +1,31 @@
package ru.m.animate;
import promhx.Deferred;
import flash.display.BitmapData;
import promhx.Promise;
class OnceAnimate extends Animate {
private var deferred:Deferred<Animate>;
public function new(frames:Array<BitmapData>) {
super(frames);
}
public function play():Promise<Animate> {
deferred = new Deferred();
playing = true;
return deferred.promise();
}
override private function update():Void {
super.update();
if (index == 0) {
playing = false;
if (deferred != null) {
deferred.resolve(this);
}
}
}
}

View File

@@ -1,5 +1,6 @@
package ru.m.tankz.control;
import ru.m.tankz.game.Game;
import ru.m.tankz.control.Control;
import haxe.Timer;
import ru.m.geom.Direction;
@@ -13,15 +14,14 @@ typedef KeyBinding = Map<Int, TankAction>;
class HumanControl extends Control {
public static var TYPE(default, never):ControlType = 'human';
private var keyBinding:KeyBinding;
private var moveQueue:Array<Int>;
private var shotTimer:Timer;
public function new(index:Int) {
super({type:TYPE, index:index});
this.keyBinding = resolve(index);
public function new(playerId:PlayerId, controlIndex:Int) {
super(playerId);
this.keyBinding = resolve(controlIndex);
moveQueue = new Array<Int>();
Lib.current.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
Lib.current.stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
@@ -83,8 +83,8 @@ class HumanControl extends Control {
action(TankAction.SHOT);
}
private static function resolve(index:Int):KeyBinding {
switch (index) {
private static function resolve(controlIndex:Int):KeyBinding {
switch (controlIndex) {
case 0:
return [
Keyboard.A => TankAction.MOVE(Direction.LEFT),
@@ -101,8 +101,8 @@ class HumanControl extends Control {
Keyboard.RIGHT => TankAction.MOVE(Direction.RIGHT),
Keyboard.NUMPAD_0 => TankAction.SHOT
];
case _:
throw 'Invalid control index ${index}';
case x:
throw 'Invalid control index ${x}';
}
}
}

View File

@@ -1,13 +1,12 @@
package ru.m.tankz.render;
import ru.m.geom.Point;
import openfl.Assets;
import ru.m.animate.OnceAnimate;
import flash.display.DisplayObjectContainer;
import ru.m.tankz.core.EntityType;
import flash.display.DisplayObject;
import Type.ValueType;
import ru.m.tankz.render.RenderItem;
import ru.m.tankz.engine.Engine;
import ru.m.tankz.core.Bullet;
import ru.m.tankz.core.Tank;
import flash.display.Sprite;
import flash.display.Graphics;
import haxework.gui.SpriteView;
@@ -19,20 +18,24 @@ class Render extends SpriteView implements EngineListener {
private var groundLayer:Sprite;
private var entryLayer:Sprite;
private var upLayer:Sprite;
private var upperLayer:Sprite;
private var background:Sprite;
private var items:Map<String, RenderItem<Dynamic>>;
private var items:Map<String, RenderItem<Dynamic, Dynamic>>;
public function new() {
super();
items = new Map();
backgroundLayer = new Sprite();
groundLayer = new Sprite();
entryLayer = new Sprite();
upLayer = new Sprite();
upperLayer = new Sprite();
contentAsSprite.addChild(backgroundLayer);
contentAsSprite.addChild(groundLayer);
contentAsSprite.addChild(entryLayer);
contentAsSprite.addChild(upLayer);
contentAsSprite.addChild(upperLayer);
reset();
}
@@ -74,7 +77,10 @@ class Render extends SpriteView implements EngineListener {
}
public function reset():Void {
items = new Map<String, RenderItem<Dynamic>>();
for (item in items.iterator()) {
item.dispose();
}
items = new Map();
if (background != null) {
backgroundLayer.removeChild(background);
background = null;
@@ -91,6 +97,7 @@ class Render extends SpriteView implements EngineListener {
items.set(tank.key, item);
entryLayer.addChild(item.view);
item.update();
playTankSpawn(tank.rect.center);
case EntityType.BULLET(bullet):
var item = new BulletItem(bullet);
items.set(bullet.key, item);
@@ -115,18 +122,66 @@ class Render extends SpriteView implements EngineListener {
if (items.exists(tank.key)) {
entryLayer.removeChild(items.get(tank.key).view);
items.remove(tank.key);
playTankBoom(tank.rect.center);
}
case EntityType.BULLET(bullet):
if (items.exists(bullet.key)) {
entryLayer.removeChild(items.get(bullet.key).view);
items.remove(bullet.key);
playBulletBoom(bullet.rect.center.add(new Point(bullet.rect.width * bullet.rect.direction.x, bullet.rect.height * bullet.rect.direction.y)));
}
case EntityType.EAGLE(eagle):
if (items.exists(eagle.key)) {
cast(items.get(eagle.key), EagleItem).destoyed = true;
items.get(eagle.key).redraw();
playTankBoom(eagle.rect.center);
}
case _:
}
}
private function playTankSpawn(point:Point):Void {
var arr = [
0, 1, 2, 3, 3, 4, 5, 5, 6
];
var frames = [for (i in arr) Assets.getBitmapData('resources/images/tank/appear/appear-${i}.png')];
var animate = new OnceAnimate(frames);
animate.x = point.x - animate.width / 2;
animate.y = point.y - animate.height / 2;
upperLayer.addChild(animate);
animate.play().then(function(animate) {
upperLayer.removeChild(animate);
animate.dispose();
});
}
private function playBulletBoom(point:Point):Void {
var arr = [
0, 1, 1, 0, 0, 1
];
var frames = [for (i in arr) Assets.getBitmapData('resources/images/bullet/boom/boom-${i}.png')];
var animate = new OnceAnimate(frames);
animate.x = point.x - animate.width / 2;
animate.y = point.y - animate.height / 2;
upperLayer.addChild(animate);
animate.play().then(function(animate) {
upperLayer.removeChild(animate);
animate.dispose();
});
}
private function playTankBoom(point:Point):Void {
var arr = [
0, 1, 2, 3, 4, 4, 4, 1, 4, 4, 7, 7, 8, 9, 9
];
var frames = [for (i in arr) Assets.getBitmapData('resources/images/tank/kaboom/kaboom-${i}.png')];
var animate = new OnceAnimate(frames);
animate.x = point.x - animate.width / 2;
animate.y = point.y - animate.height / 2;
upperLayer.addChild(animate);
animate.play().then(function(animate) {
upperLayer.removeChild(animate);
animate.dispose();
});
}
}

View File

@@ -1,5 +1,8 @@
package ru.m.tankz.render;
import ru.m.tankz.control.Control;
import flash.display.Sprite;
import ru.m.animate.Animate;
import ru.m.tankz.core.Eagle;
import flash.display.DisplayObject;
import flash.display.Shape;
@@ -17,23 +20,17 @@ typedef TRectangle = {
}
class RenderItem<T:TRectangle> {
class RenderItem<T:TRectangle, D:DisplayObject> {
public var value(default, null):T;
public var view(default, null):DisplayObject;
private var bitmap:Bitmap;
public var view(default, null):D;
public function new(value:T) {
this.value = value;
this.bitmap = new Bitmap();
this.view = bitmap;
redraw();
this.view = null;
}
public function redraw():Void {
bitmap.bitmapData = Assets.getBitmapData(getImage());
}
public function redraw():Void {}
public function update():Void {
var rect = value.rect;
@@ -42,10 +39,6 @@ class RenderItem<T:TRectangle> {
view.y = rect.y - rect.height * (rect.direction.x + 1) / 2 - rect.height * (rect.direction.y + 1) / 2 + 1.5 * rect.height;
}
private function getImage():String {
return 'ERROR';
}
private static function calcRotate(direction:Direction):Float {
return (if (direction == Direction.RIGHT) {
0;
@@ -59,84 +52,153 @@ class RenderItem<T:TRectangle> {
0;
});
}
public function dispose():Void {}
}
class BrickItem extends RenderItem<Brick> {
class BitmapItem<T:TRectangle> extends RenderItem<T, Bitmap> {
public function new(value:T) {
super(value);
this.view = new Bitmap();
redraw();
}
override public function redraw():Void {
view.bitmapData = Assets.getBitmapData(getImage());
}
private function getImage():String {
return 'ERROR';
}
}
class BrickItem extends RenderItem<Brick, Shape> {
private var shape:Shape;
private var broken:Int;
private var type:Int;
public function new(value:Brick) {
this.shape = new Shape();
super(value);
this.view = shape;
this.view = new Shape();
redraw();
}
override public function redraw():Void {
var image = Assets.getBitmapData(getImage());
var g = shape.graphics;
var g = view.graphics;
g.clear();
if (value.destroyed) return;
g.beginBitmapFill(image);
g.drawRect(0, 0, value.rect.width, value.rect.height);
for (c in value.cells) {
if (c.destroyed) {
g.beginFill(0x000000);
g.drawRect(c.rect.x - value.rect.x, c.rect.y - value.rect.y, c.rect.width, c.rect.height);
if (value.config.type > 0) {
g.beginBitmapFill(image);
g.drawRect(0, 0, value.rect.width, value.rect.height);
for (c in value.cells) {
if (c.destroyed) {
g.beginFill(0x000000);
g.drawRect(c.rect.x - value.rect.x, c.rect.y - value.rect.y, c.rect.width, c.rect.height);
}
}
g.endFill();
}
g.endFill();
}
override public function update():Void {
super.update();
var b = value.broken;
if (b != this.broken) {
this.broken = b;
var t = value.config.type;
if (b != broken || t != type) {
broken = b;
type = t;
redraw();
}
}
override private function getImage():String {
private function getImage():String {
return 'resources/images/map/map_${value.config.type}.png';
}
}
class TankItem extends RenderItem<Tank> {
class TankItem extends RenderItem<Tank, Sprite> {
private var type:String;
private var hits:Int;
override private function getImage():String {
private var tankView:Animate;
public function new(value:Tank) {
super(value);
view = new Sprite();
if (value.playerId.type == Control.HUMAN) {
view.addChild(buildHumanMarkView(value));
}
tankView = new Animate();
view.addChild(tankView);
redraw();
}
private static function buildHumanMarkView(tank:Tank):DisplayObject {
var view = new Shape();
view.graphics.beginFill(0x00aa00);
view.graphics.lineStyle(2, 0x00ff00);
view.graphics.drawCircle(0, 0, 23);
view.graphics.endFill();
view.x = tank.rect.width / 2;
view.y = tank.rect.height / 2;
view.alpha = 0.2;
return view;
}
override public function redraw():Void {
tankView.frames = getFrames().map(function(s) return Assets.getBitmapData(s));
}
private function getFrames():Array<String> {
var team = value.playerId.team;
var group = value.config.group;
var index = value.playerId.index;
if (group == 'human') group = 'player';
if (group == 'radiant') {
group = 'player';
if (team == 'radiant') {
index = 0;
}
if (group == 'dire') {
group = 'player';
if (team == 'dire') {
index = 1;
}
if (group == 'bot') index = 0;
return 'resources/images/tank/${group}/tank_${group.charAt(0)}${value.config.type}_${index}-0.png';
if (team == 'human' || team == 'radiant' || team == 'dire') {
group = 'player';
}
if (team == 'bot') {
index = value.hits;
}
return [
'resources/images/tank/${group}/tank_${group.charAt(0)}${value.config.type}_${index}-0.png',
'resources/images/tank/${group}/tank_${group.charAt(0)}${value.config.type}_${index}-1.png',
];
}
override public function update():Void {
super.update();
var t = value.config.type;
if (t != this.type) {
var h = value.hits;
if (t != this.type || h != this.hits) {
this.type = t;
this.hits = h;
redraw();
}
tankView.playing = (value.mx !=0 || value.my != 0);
}
override public function dispose():Void {
if (tankView != null) {
tankView.dispose();
tankView = null;
}
}
}
class BulletItem extends RenderItem<Bullet> {
class BulletItem extends BitmapItem<Bullet> {
override private function getImage():String {
return 'resources/images/bullet/bullet_${value.config.piercing > 1 ? 1 : 0}.png';
@@ -144,7 +206,7 @@ class BulletItem extends RenderItem<Bullet> {
}
class EagleItem extends RenderItem<Eagle> {
class EagleItem extends BitmapItem<Eagle> {
public var destoyed(default, default):Bool;

View File

@@ -15,7 +15,7 @@ interface LevelFrameLayout {
@:template("layout/frames/level.json", "layout/styles.json")
class LevelFrame extends VGroupView implements ViewBuilder implements LevelFrameLayout implements ListViewListener<Int> {
class LevelFrame extends VGroupView implements ViewBuilder implements LevelFrameLayout {
public static inline var ID = "level";
public function init():Void {
@@ -24,7 +24,7 @@ class LevelFrame extends VGroupView implements ViewBuilder implements LevelFrame
public function onShow():Void {
var state = Provider.get(GameState);
var c = ConfigBundle.get(state.type).levels;
var c = ConfigBundle.get(state.type).game.levels;
levels.data = [for (i in 0...c) i];
}

View File

@@ -12,9 +12,11 @@ import haxework.gui.VGroupView;
interface StartFrameLayout {
var start_1p(default, null):ButtonView;
var start_2p(default, null):ButtonView;
var dota(default, null):ButtonView;
var classic_1p(default, null):ButtonView;
var classic_2p(default, null):ButtonView;
var dota_1p(default, null):ButtonView;
var dota_2p_coop(default, null):ButtonView;
var dota_2p_vs(default, null):ButtonView;
}
@@ -24,30 +26,36 @@ class StartFrame extends VGroupView implements ViewBuilder implements StartFrame
public static inline var ID = "start";
public function init() {
start_1p.onPress = this;
start_2p.onPress = this;
dota.onPress = this;
classic_1p.onPress = this;
classic_2p.onPress = this;
dota_1p.onPress = this;
dota_2p_coop.onPress = this;
dota_2p_vs.onPress = this;
}
public function onPress(view:ButtonView):Void {
switch (view.id) {
case 'start_1p':
startGame(ClassicGame.TYPE, 1);
case 'start_2p':
startGame(ClassicGame.TYPE, 2);
case 'dota':
startGame(DotaGame.TYPE, 2);
case 'classic_1p':
startGame(ClassicGame.TYPE, ClassicGame.PLAYER1);
case 'classic_2p':
startGame(ClassicGame.TYPE, ClassicGame.PLAYER2);
case 'dota_1p':
startGame(DotaGame.TYPE, DotaGame.PLAYER1);
case 'dota_2p_coop':
startGame(DotaGame.TYPE, DotaGame.PLAYER2_COOP);
case 'dota_2p_vs':
startGame(DotaGame.TYPE, DotaGame.PLAYER2_VS);
}
}
private function startGame(type:GameType, humans:Int):Void {
private function startGame(type:GameType, mode:GameMode):Void {
switch (type) {
case ClassicGame.TYPE:
Provider.set(GameState, ClassicGame.buildState(0, humans));
Provider.set(GameState, ClassicGame.buildState(0, mode));
Provider.get(IFrameSwitcher).change(LevelFrame.ID);
case DotaGame.TYPE:
Provider.set(GameState, DotaGame.buildState(0, humans));
Provider.get(IFrameSwitcher).change(GameFrame.ID);
Provider.set(GameState, DotaGame.buildState(0, mode));
Provider.get(IFrameSwitcher).change(LevelFrame.ID);
}
}
}

View File

@@ -1,4 +1,6 @@
levels: 36
game:
levels: 36
friendlyFire: false
map:
cellWidth: 22
@@ -7,72 +9,34 @@ map:
gridHeight: 26
bricks:
# border
- type: -1
layer: 2
armor: -1
# none
- type: 0
layer: 0
armor: 0
# ace
- type: 1
layer: 0
armor: 0
# bush
- type: 2
layer: 3
armor: 0
# water
- type: 3
layer: 1
armor: 0
# armor
- type: 4
layer: 2
armor: 2
# brick
- type: 5
layer: 2
armor: 1
- {type: -1, layer: 2, armor: -1} # border
- {type: 0, layer: 0, armor: 0} # none
- {type: 1, layer: 0, armor: 0} # ace
- {type: 2, layer: 3, armor: 0} # bush
- {type: 3, layer: 1, armor: 0} # water
- {type: 4, layer: 2, armor: 2} # armor
- {type: 5, layer: 2, armor: 1} # brick
teams:
- id: human
spawnInterval: 0
points:
- type: eagle
index: -1
x: 12
y: 24
direction: right
- type: tank
index: 0
x: 8
y: 24
direction: top
- type: tank
index: 1
x: 16
y: 24
direction: top
tanks:
- {group: human, type: 0, rate: 1}
- id: bot
spawnInterval: 3000
points:
- type: tank
index: -1
x: 0
y: 0
direction: bottom
- type: tank
index: -1
x: 12
y: 0
direction: bottom
- type: tank
index: -1
x: 24
y: 0
direction: bottom
tanks:
- {group: bot, type: 0, rate: 0.5}
- {group: bot, type: 1, rate: 0.5}
- {group: bot, type: 2, rate: 0.5}
- {group: bot, type: 3, rate: 0.5}
points:
- {team: human, type: eagle, index: -1, direction: right, x: 12, y: 24}
- {team: human, type: tank, index: 0, direction: top, x: 8, y: 24}
- {team: human, type: tank, index: 1, direction: top, x: 16, y: 24}
- {team: bot, type: tank, index: -1, direction: bottom, x: 0, y: 0}
- {team: bot, type: tank, index: -1, direction: bottom, x: 12, y: 0}
- {team: bot, type: tank, index: -1, direction: bottom, x: 24, y: 0}
bullet: &bullet
width: 12
@@ -107,7 +71,7 @@ tanks:
bullet:
<<: *bullet
speed: 9.0
bullets: 3
bullets: 2
- type: 3
width: 42
@@ -159,4 +123,4 @@ tanks:
speed: 8.0
bullets: 1
score: 400
hits: 4
hits: 3

View File

@@ -1,4 +1,6 @@
levels: 36
game:
levels: 3
friendlyFire: true
map:
cellWidth: 22
@@ -7,103 +9,41 @@ map:
gridHeight: 30
bricks:
# border
- type: -1
layer: 2
armor: -1
# none
- type: 0
layer: 0
armor: 0
# ace
- type: 1
layer: 0
armor: 0
# bush
- type: 2
layer: 3
armor: 0
# water
- type: 3
layer: 1
armor: 0
# armor
- type: 4
layer: 2
armor: 2
# brick
- type: 5
layer: 2
armor: 1
- {type: -1, layer: 2, armor: -1} # border
- {type: 0, layer: 0, armor: 0} # none
- {type: 1, layer: 0, armor: 0} # ace
- {type: 2, layer: 3, armor: 0} # bush
- {type: 3, layer: 1, armor: 0} # water
- {type: 4, layer: 2, armor: 2} # armor
- {type: 5, layer: 2, armor: 1} # brick
team_tanks: &team_tanks
tanks:
- {group: any, type: 0, rate: 0.25}
- {group: any, type: 1, rate: 0.25}
- {group: any, type: 2, rate: 0.25}
teams:
- id: radiant
- <<: *team_tanks
id: radiant
spawnInterval: 0
points:
- type: eagle
index: -1
x: 0
y: 28
direction: right
- type: tank
index: 0
x: 0
y: 0
direction: top
- type: tank
index: 1
x: 6
y: 10
direction: top
- type: tank
index: 2
x: 6
y: 16
direction: top
- type: tank
index: 3
x: 6
y: 22
direction: top
- type: tank
index: 4
x: 10
y: 28
direction: top
- id: dire
- <<: *team_tanks
id: dire
spawnInterval: 0
points:
- type: eagle
index: -1
x: 38
y: 0
direction: right
- type: tank
index: 0
x: 38
y: 28
direction: bottom
- type: tank
index: 1
x: 32
y: 18
direction: bottom
- type: tank
index: 2
x: 32
y: 12
direction: bottom
- type: tank
index: 3
x: 32
y: 6
direction: bottom
- type: tank
index: 4
x: 28
y: 0
direction: bottom
points:
- {team: radiant, type: eagle, index: -1, direction: right, x: 0, y: 28}
- {team: radiant, type: tank, index: 0, direction: right, x: 0, y: 0}
- {team: radiant, type: tank, index: 1, direction: right, x: 6, y: 10}
- {team: radiant, type: tank, index: 2, direction: right, x: 6, y: 16}
- {team: radiant, type: tank, index: 3, direction: right, x: 6, y: 22}
- {team: radiant, type: tank, index: 4, direction: right, x: 10, y: 28}
- {team: dire, type: eagle, index: -1, direction: right, x: 38, y: 0}
- {team: dire, type: tank, index: 0, direction: left, x: 38, y: 28}
- {team: dire, type: tank, index: 1, direction: left, x: 32, y: 18}
- {team: dire, type: tank, index: 2, direction: left, x: 32, y: 12}
- {team: dire, type: tank, index: 3, direction: left, x: 32, y: 6}
- {team: dire, type: tank, index: 4, direction: left, x: 28, y: 0}
bullet: &bullet
width: 12
@@ -112,7 +52,7 @@ bullet: &bullet
piercing: 1
tanks:
radiant: &tanks
any:
- type: 0
width: 36
height: 36
@@ -130,5 +70,22 @@ tanks:
<<: *bullet
speed: 8.5
bullets: 1
dire:
- <<: *tanks
- type: 2
width: 40
height: 36
speed: 3.0
bullet:
<<: *bullet
speed: 9.0
bullets: 2
- type: 3
width: 42
height: 38
speed: 2.9
bullet:
<<: *bullet
speed: 9.0
piercing: 3
bullets: 2

View File

@@ -1,30 +1 @@
0000000000000000000000000000000000005500
0000000000000000000000000000000000005500
0000000000000000000000000000000000005555
0000000000000000000000000000000000005555
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
5555000000000000000000000000000000000000
5555000000000000000000000000000000000000
0055000000000000000000000000000000000000
0055000000000000000000000000000000000000
000044000000000000000000005500004400550000004400000000000000000000550000440055000000440000004400003333000055000044005555000044000000440000333300005500004400555500004422442244000000000000550000440000000000442244224400000000000055000044000000000000000000444444555544444400442200330000000000000044444455554444440044220033005555333333554422220000222244000022003300555533333355442222000022224400002200330000000000000044220000000022440000440033000000000000004422000000002244000044003300000000000000550000555500005500002200000000000000000055000055550000550000220000004444444400005500445555440055000044444444444444440000550044555544005500004444444400000022000055000055550000550000000000000000002200005500005555000055000000000000003300440000442200000000224400000000000000330044000044220000000022440000000000000033002200004422220000222244553333335555003300220000442222000022224455333333555500330022440044444455554444440000000000000033002244004444445555444444000000000000000000440000550000000000004422442244000000000044000055000000000000442244224400005555004400005500003333000044000000440000555500440000550000333300004400000044000000550044000055000000000000000000004400000055004400005500000000000000000000440000

View File

@@ -0,0 +1,2 @@
points: [{index: -1, direction: right, team: radiant, type: eagle, y: 28, x: 0}, {index: 0, direction: right, team: radiant, type: tank, y: 2, x: 2}, {index: 1, direction: right, team: radiant, type: tank, y: 6, x: 2}, {index: 2, direction: right, team: radiant, type: tank, y: 10, x: 2}, {index: 3, direction: right, team: radiant, type: tank, y: 14, x: 2}, {index: 4, direction: right, team: radiant, type: tank, y: 18, x: 2}, {index: -1, direction: right, team: dire, type: eagle, y: 28, x: 38}, {index: 0, direction: left, team: dire, type: tank, y: 2, x: 36}, {index: 1, direction: left, team: dire, type: tank, y: 6, x: 36}, {index: 2, direction: left, team: dire, type: tank, y: 10, x: 36}, {index: 3, direction: left, team: dire, type: tank, y: 14, x: 36}, {index: 4, direction: left, team: dire, type: tank, y: 18, x: 36}]
data: "005500330033003300333300330033003300550000550033003300330033330033003300330055000000000000330033000000003300330000000000000000000033003300000000330033000000000000550033000000330000000033000000330055000055003300000033000000003300000033005500000000330000003300333300330000003300000000000033000000330033330033000000330000000055000000330033003333003300330000005500005500000033003300333300330033000000550000000033003300330033330033003300330000000000003300330033003333003300330033000000005500330000003300333300330000003300550000550033000000330033330033000000330055000000000000330033003333003300330000000000000000000033003300333300330033000000000000550033003300330033330033003300330055000055003300330033003333003300330033005500000000330033003300333300330033003300000000000033003300330033330033003300330000005555553300330033003333003300330033555555555555330033003300333300330033003355555555555533003300330033330033003300335555555555553300330033003333003300330033555555440000330033000000333300000033000000004444000033003300000033330000003300000000440000443300330000003333000000330033440000000044330033000000333300000033003344000000440033003300330033330033003300330044000044003300330033003333003300330033004400"

View File

@@ -0,0 +1,2 @@
data: "000000000000000000000000000000440000000000000000000000000000000000000044000000000044003300005500440000440055000000000000004400330000550044000044005500000000000000000000440000000000000000000055004400000000000044000000000000000000005500440000003300000000330044003300440000000000000000330000000033004400330044000000000000000000440055000000000000000000005500003300000044005500000000000000000000550000330000000000000055000044005500440000004400000000000000005500004400550044000000440000555500003300000000000000000000000000555555550000330000000000000000000000000055550055440000004400330000000000003300445500005544000000440033000000000000330044550055550000440000000044004400550000000055555555000044000000004400440055000000005555000000000000000000000000000044000044000000000000000000000000000000004400004400000044000044000044005500004400000000000000004400004400004400550000440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003300000000550000330055003300005500440000330000000055000033005500330000550044000000440033000000440000000000440000000000000044003300000044000000000044000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
points: [{direction: right, team: radiant, x: 0, type: eagle, y: 14, index: -1}, {direction: right, team: radiant, x: 0, type: tank, y: 0, index: 0}, {direction: right, team: radiant, x: 6, type: tank, y: 10, index: 1}, {direction: right, team: radiant, x: 6, type: tank, y: 16, index: 2}, {direction: right, team: radiant, x: 6, type: tank, y: 22, index: 3}, {direction: right, team: radiant, x: 10, type: tank, y: 28, index: 4}, {direction: right, team: dire, x: 38, type: eagle, y: 14, index: -1}, {direction: left, team: dire, x: 38, type: tank, y: 28, index: 0}, {direction: left, team: dire, x: 32, type: tank, y: 18, index: 1}, {direction: left, team: dire, x: 32, type: tank, y: 12, index: 2}, {direction: left, team: dire, x: 32, type: tank, y: 6, index: 3}, {direction: left, team: dire, x: 28, type: tank, y: 0, index: 4}]

View File

@@ -18,6 +18,10 @@ class Line {
);
}
public function move(point:Point):Line {
return new Line(point1.add(point), point2.add(point));
}
public function setLength(value:Float):Line {
var center = this.center;
var width = point2.x - point1.x;

View File

@@ -18,6 +18,6 @@ class Point {
}
public function toString():String {
return 'Point{x=$x,y=$y}';
return 'Point{x=${Math.round(x * 100) / 100},y=${Math.round(y * 100) / 100}}';
}
}

View File

@@ -1,5 +1,6 @@
package ru.m.tankz.bot;
import ru.m.tankz.game.Game;
import ru.m.tankz.core.EntityType;
import ru.m.tankz.control.Control;
import ru.m.geom.Direction;
@@ -7,34 +8,34 @@ import haxe.Timer;
class BotControl extends Control {
public static var TYPE(default, never):ControlType = 'bot';
private var shotTimer:Timer;
private var turnRandomTimer:Timer;
private var turnTimer:Timer;
public function new(index:Int) {
super({type:TYPE, index:index});
public function new(playerId:PlayerId) {
super(playerId);
}
override public function onCollision(with:EntityType):Void {
switch (with) {
case EntityType.TANK(_): turn();
case EntityType.CELL(_): turn();
case EntityType.TANK(_): turnAfter(300);
case EntityType.CELL(_): turnAfter(300);
case _:
}
}
override public function start():Void {
//var tank = handler.entities.get(tankId);
//action(TankAction.MOVE(tank.rect.direction));
action(TankAction.MOVE(Direction.BOTTOM)); // ToDo: hardcode bot start direction
if (handler == null) return;
var tank = handler.entities.get(tankId);
action(TankAction.MOVE(tank.rect.direction));
if (shotTimer == null) {
shotTimer = new Timer(1000);
shotTimer.run = shot;
}
if (turnTimer == null) {
turnTimer = new Timer(3000);
turnTimer.run = turn;
if (turnRandomTimer == null) {
turnRandomTimer = new Timer(2000);
turnRandomTimer.run = turn;
}
}
@@ -43,9 +44,9 @@ class BotControl extends Control {
shotTimer.stop();
shotTimer = null;
}
if (turnTimer != null) {
turnTimer.stop();
turnTimer = null;
if (turnRandomTimer != null) {
turnRandomTimer.stop();
turnRandomTimer = null;
}
}
@@ -53,7 +54,17 @@ class BotControl extends Control {
action(TankAction.SHOT);
}
public function turnAfter(delay:Int):Void {
if (turnTimer == null) {
turnTimer = Timer.delay(turn, delay);
}
}
public function turn():Void {
if (turnTimer != null) {
turnTimer.stop();
turnTimer = null;
}
action(TankAction.MOVE(randomDirection()));
}

View File

@@ -1,7 +1,13 @@
package ru.m.tankz.config;
typedef GameConfig = {
var levels: Int;
var friendlyFire:Bool;
}
typedef SpawnPoint = {
var team:String;
var type:String;
var index:Int;
var x:Int;
@@ -41,6 +47,11 @@ typedef TankConfig = { > TankType,
var speed:Float;
var bullet:BulletConfig;
var bullets:Int;
var hits:Int;
}
typedef TankSpawn = { > TankType,
var rate: Float;
}
@@ -48,28 +59,44 @@ typedef TeamConfig = {
var id:String;
var size:Int;
var spawnInterval:Int;
var points:Array<SpawnPoint>;
var tanks:Array<TankSpawn>;
}
typedef LevelConfig = {
var data:Array<BrickConfig>;
@:optional var points:Array<SpawnPoint>;
}
class Config {
public var type(default, null):String;
public var levels(default, null):Int;
public var game(default, null):GameConfig;
public var map(default, null):MapConfig;
public var bricks(default, null):Array<BrickConfig>;
public var tanks(default, null):Array<TankConfig>;
public var teams(default, null):Array<TeamConfig>;
public var points(default, null):Array<SpawnPoint>;
private var brickMap:Map<Int, BrickConfig>;
private var tankMap:Map<String, Map<String, TankConfig>>;
private var teamMap:Map<String, TeamConfig>;
public function new(type:String, levels:Int, map:MapConfig, bricks:Array<BrickConfig>, teams:Array<TeamConfig>, tanks:Array<TankConfig>) {
public function new(
type:String,
game:GameConfig,
map:MapConfig,
bricks:Array<BrickConfig>,
teams:Array<TeamConfig>,
points:Array<SpawnPoint>,
tanks:Array<TankConfig>
) {
this.type = type;
this.levels = levels;
this.game = game;
this.map = map;
this.bricks = bricks;
this.teams = teams;
this.points = points;
this.tanks = tanks;
init();
}

View File

@@ -1,6 +1,5 @@
package ru.m.tankz.config;
import ru.m.tankz.game.ClassicGame;
import yaml.Parser;
import openfl.Assets;
import yaml.Yaml;
@@ -8,10 +7,11 @@ import ru.m.tankz.config.Config;
typedef ConfigSource = {
var levels:Int;
var game:GameConfig;
var map: MapConfig;
var bricks: Array<BrickConfig>;
var teams: Array<TeamConfig>;
var points: Array<SpawnPoint>;
var tanks: Dynamic<Array<TankConfig>>;
}
@@ -31,6 +31,6 @@ class ConfigBundle {
tanks.push(item);
}
}
return new Config(type, source.levels, source.map, source.bricks, source.teams, tanks);
return new Config(type, source.game, source.map, source.bricks, source.teams, source.points, tanks);
}
}

View File

@@ -1,10 +1,19 @@
package ru.m.tankz.config;
import yaml.Renderer;
import yaml.Parser;
import yaml.Yaml;
import openfl.Assets;
import ru.m.tankz.game.Game;
import ru.m.tankz.config.Config;
typedef LevelSource = {
var data:String;
@:optional var points:Array<SpawnPoint>;
}
class LevelBundle {
private static function formatLevel(level:Int):String {
@@ -13,16 +22,49 @@ class LevelBundle {
return result;
}
public static function get(type:GameType, config:Config, level:Int):Array<BrickConfig> {
var bricksData:String = Assets.getText('resources/${type}/levels/level${formatLevel(level)}.txt');
public static function loadsOld(config:Config, data:String):LevelConfig {
var bricks:Array<BrickConfig> = [];
for (line in ~/\s+/g.split(bricksData)) {
for (line in ~/\s+/g.split(data)) {
for (c in line.split('')) {
if (c.length > 0) {
bricks.push(config.bricks[Std.parseInt(c) + 1]);
bricks.push(config.getBrick(Std.parseInt(c)));
}
}
}
return bricks;
return {
data: bricks
}
}
public static function loads(config:Config, data:String):LevelConfig {
try {
var obj:LevelSource = Yaml.parse(data, Parser.options().useObjects());
return {
data: obj.data.split('').map(function(c) return config.getBrick(Std.parseInt(c))),
points: obj.points,
}
} catch (error:Dynamic) {
L.w('LevelBundle', '${error}');
return loadsOld(config, data);
}
}
public static function dumps(config:Config, level:LevelConfig):String {
var bricksStr = level.data.map(function(brick:BrickConfig) return brick.type).join('');
return Yaml.render({
data: bricksStr,
points: level.points,
}, Renderer.options().setFlowLevel(1));
}
public static function empty(config:Config):LevelConfig {
return {
data: [for (i in 0...config.map.gridWidth * config.map.gridHeight) config.bricks[1]]
}
}
public static function get(type:GameType, config:Config, level:Int):LevelConfig {
var data:String = Assets.getText('resources/${type}/levels/level${formatLevel(level)}.txt');
return loads(config, data);
}
}

View File

@@ -1,5 +1,6 @@
package ru.m.tankz.control;
import ru.m.tankz.game.Game;
import ru.m.tankz.core.Entity;
import ru.m.tankz.core.EntityType;
import ru.m.geom.Direction;
@@ -16,19 +17,17 @@ enum TankAction {
typedef ControlType = String;
typedef ControlId = {
var type:ControlType;
var index:Int;
}
class Control {
public var id:ControlId;
public static var NONE(default, never):ControlType = 'none';
public static var HUMAN(default, never):ControlType = 'human';
public static var BOT(default, never):ControlType = 'bot';
public var playerId(default, null):PlayerId;
public var tankId(default, default):Int;
private var handler:ControlHandler;
public function new(id:ControlId) {
this.id = id;
public function new(playerId:PlayerId) {
this.playerId = playerId;
}
public function bind(handler:ControlHandler):Void {

View File

@@ -11,6 +11,7 @@ import ru.m.geom.Direction;
class Tank extends MobileEntity {
public var playerId(default, null):PlayerId;
public var config(default, set):TankConfig;
public var hits(default, default):Int;
private var bulletsCounter:Int = 0;
@@ -18,6 +19,7 @@ class Tank extends MobileEntity {
super(new Rectangle(0, 0, config.width, config.height), config.speed, Direction.RIGHT);
this.playerId = playerId;
this.config = config;
this.hits = config.hits;
this.layer = 1;
}

View File

@@ -1,5 +1,6 @@
package ru.m.tankz.engine;
import ru.m.geom.Point;
import ru.m.geom.Line;
import ru.m.tankz.config.Config;
import ru.m.tankz.control.Control;
@@ -30,7 +31,16 @@ class CollisionProcessor implements EngineListener {
public function onSpawn(entity:EntityType):Void {}
private function checkTankBullet(tank:Tank, bullet:Bullet):Bool {
return tank.playerId.team != bullet.playerId.team;
if (bullet.tankId == tank.id) return false;
return engine.config.game.friendlyFire || tank.playerId.team != bullet.playerId.team;
}
private function hitTank(tank:Tank):Void {
if (tank.hits > 0) {
tank.hits--;
} else {
engine.destroy(tank);
}
}
public function onCollision(entity:EntityType, with:EntityType):Void {
@@ -41,7 +51,7 @@ class CollisionProcessor implements EngineListener {
tank1.rect.lean(tank2.rect);
case EntityType.BULLET(bullet2):
if (checkTankBullet(tank1, bullet2)) {
engine.destroy(tank1);
hitTank(tank1);
engine.destroy(bullet2);
}
case EntityType.EAGLE(eagle):
@@ -54,7 +64,7 @@ class CollisionProcessor implements EngineListener {
case EntityType.TANK(tank2):
if (checkTankBullet(tank2, bullet1)) {
engine.destroy(bullet1);
engine.destroy(tank2);
hitTank(tank2);
}
case EntityType.BULLET(bullet2):
engine.destroy(bullet1);
@@ -134,6 +144,8 @@ class Engine implements ControlHandler {
}
}
public function update():Void {
var newTime:Float = Date.now().getTime();
var d:Float = newTime - time;
@@ -153,47 +165,62 @@ class Engine implements ControlHandler {
}*/
if (entity.mx != 0 || entity.my != 0) {
entity.rect.x += entity.mx * (d / 30);
entity.rect.y += entity.my * (d / 30);
var side:Line = entity.rect.getSide(entity.rect.direction.reverse());
var cells = map.grid.getCells(side);
var step:Point = new Point(entity.rect.direction.x * map.cellWidth / 4, entity.rect.direction.y * map.cellHeight / 4);
var end:Point = side.center.add(new Point(entity.mx * (d / 30), entity.my * (d / 30)));
var c:Int = Math.floor(Math.abs(((end.x - side.center.x) / (map.cellWidth / 4) + (end.y - side.center.y) / (map.cellHeight / 4))));
var collision:Bool = false;
for (cell in cells) {
if (cell.layer >= entity.layer && cell.layer < 3) {
entity.rect.lean(cell.rect);
collision = true;
var with = EntityTypeResolver.of(cell);
for (l in listeners) l.onCollision(entityType, with);
break;
}
}
var isStop:Bool = false;
for (other in entities.iterator()) {
if (other != ent && other != null) {
if (other.rect.intersection2(side)) {
var with = EntityTypeResolver.of(other);
while (c-- >= 0) {
side = side.move(step);
var cells = map.grid.getCells(side);
var collision:Bool = false;
for (cell in cells) {
if (cell.layer >= entity.layer && cell.layer < 3) {
entity.rect.lean(cell.rect);
collision = true;
var with = EntityTypeResolver.of(cell);
for (l in listeners) l.onCollision(entityType, with);
isStop = true;
break;
}
}
}
if (Std.is(entity, Bullet)) {
var bullet:Bullet = cast ent;
if (collision) {
cells = map.grid.getCells(side.setLength(map.grid.cellWidth * 3));
for (cell in cells) {
if (cell.armor > 0) {
if (cell.armor == bullet.config.piercing) {
cell.destroyed = true;
} else if (cell.armor < bullet.config.piercing) {
var brick = map.getBrick(cell);
brick.destroyed = true;
for (other in entities.iterator()) {
if (other != ent && other != null) {
if (other.rect.intersection2(side)) {
var with = EntityTypeResolver.of(other);
for (l in listeners) l.onCollision(entityType, with);
isStop = true;
}
}
}
if (Std.is(entity, Bullet)) {
var bullet:Bullet = cast ent;
if (collision) {
cells = map.grid.getCells(side.setLength(map.grid.cellWidth * 3));
for (cell in cells) {
if (cell.armor > 0) {
if (cell.armor == bullet.config.piercing) {
cell.destroyed = true;
} else if (cell.armor < bullet.config.piercing) {
var brick = map.getBrick(cell);
brick.destroyed = true;
}
}
}
}
}
if (isStop) break;
}
if (!isStop || Std.is(entity, Bullet)) {
entity.rect.x += entity.mx * (d / 30);
entity.rect.y += entity.my * (d / 30);
}
}
}

View File

@@ -1,5 +1,6 @@
package ru.m.tankz.game;
import ru.m.tankz.control.Control;
import haxe.ds.Option;
import ru.m.tankz.game.GameState.PlayerState;
import ru.m.tankz.game.Game;
@@ -12,6 +13,9 @@ class ClassicGame extends Game {
public static var HUMAN(default, never):TeamId = 'human';
public static var BOT(default, never):TeamId = 'bot';
public static var PLAYER1(default, never):GameMode = [{team:HUMAN, type:Control.HUMAN, index:0}];
public static var PLAYER2(default, never):GameMode = [{team:HUMAN, type:Control.HUMAN, index:0}, {team:HUMAN, type:Control.HUMAN, index:1}];
private static var HUMAN_LIFE(default, never):Int = 3;
private static var BOT_LIFE(default, never):Int = 20;
@@ -19,37 +23,22 @@ class ClassicGame extends Game {
super(TYPE);
}
public static function buildState(level:Int, humans:Int):GameState {
public static function buildState(level:Int, mode:GameMode):GameState {
var state = new GameState();
state.type = TYPE;
state.mode = mode;
state.level = level;
state.teams[HUMAN] = {life: -1, players: new Map(), lose: false};
state.teams[BOT] = {life: BOT_LIFE, players: new Map(), lose: false};
for (i in 0...humans) {
state.teams[HUMAN].players[i] = {
index:i,
tank:{
group: HUMAN,
type: '1'
},
control:{
type: 'human',
index: i
},
for (human in mode) {
state.teams[HUMAN].players[human.index] = {
id:human,
life:HUMAN_LIFE,
};
}
for (i in 0...humans*2+2) {
for (i in 0...mode.length * 2 + 2) {
state.teams[BOT].players[i] = {
index:i,
tank:{
group: BOT,
type: '1'
},
control:{
type: 'bot',
index: i
},
id:{team:BOT, index:i, type:Control.BOT},
life:-1,
};
}
@@ -59,7 +48,7 @@ class ClassicGame extends Game {
override public function next():Option<GameState> {
if (!state.teams[HUMAN].lose) {
state.level++;
if (state.level >= config.levels) state.level = 0;
if (state.level >= config.game.levels) state.level = 0;
state.teams[BOT].lose = false;
state.teams[BOT].life = BOT_LIFE;
for (ps in state.teams[HUMAN].players) {

View File

@@ -1,5 +1,7 @@
package ru.m.tankz.game;
import ru.m.tankz.control.Control;
import haxe.ds.Option;
import ru.m.tankz.game.Game;
import ru.m.tankz.game.GameState;
@@ -11,46 +13,54 @@ class DotaGame extends Game {
public static var RADIANT(default, never):TeamId = 'radiant';
public static var DIRE(default, never):TeamId = 'dire';
public static var PLAYER1(default, never):GameMode = [
{team:RADIANT, type:Control.HUMAN, index:0}
];
public static var PLAYER2_COOP(default, never):GameMode = [
{team:RADIANT, type:Control.HUMAN, index:0},
{team:RADIANT, type:Control.HUMAN, index:1}
];
public static var PLAYER2_VS(default, never):GameMode = [
{team:RADIANT, type:Control.HUMAN, index:0},
{team:DIRE, type:Control.HUMAN, index:0}
];
private static var TEAM_SIZE(default, never):Int = 5;
public function new() {
super(TYPE);
}
public static function buildState(level:Int, humans:Int):GameState {
public static function buildState(level:Int, mode:GameMode):GameState {
var state = new GameState();
state.type = TYPE;
state.mode = mode;
state.level = level;
state.teams[RADIANT] = {life: 20, players: new Map(), lose: false};
state.teams[DIRE] = {life: 20, players: new Map(), lose: false};
state.teams[DIRE] = {life: 20, players: new Map(), lose: false};
for (i in 0...TEAM_SIZE) {
state.teams[RADIANT].players[i] = {
index:i,
tank:{
group: RADIANT,
type: '1'
},
control:{
type: 'bot',
index: i
},
life:-1,
id: {team:RADIANT, index:i, type:Control.BOT},
life: -1,
};
}
for (i in 0...TEAM_SIZE) {
state.teams[DIRE].players[i] = {
index:i,
tank:{
group: DIRE,
type: '1'
},
control:{
type: 'bot',
index: i
},
life:-1,
id: {team:DIRE, index:i, type:Control.BOT},
life: -1,
};
}
for (human in mode) {
state.teams[human.team].players[human.index].id = human;
}
return state;
}
override public function next():Option<GameState> {
state.level++;
if (state.level >= config.game.levels) state.level = 0;
return Option.Some(buildState(state.level, state.mode));
}
}

View File

@@ -23,22 +23,28 @@ import ru.m.tankz.game.Spawner;
typedef GameType = String;
typedef GameMode = Array<PlayerId>;
typedef TeamId = String;
typedef PlayerId = {
var team:TeamId;
var type:ControlType;
var index:Int;
}
class Game implements EngineListener {
private static var TAG(default, never):String = Type.getClassName(Game).split('.').pop();
public var type(default, null):GameType;
public var state(default, null):GameState;
public var teams(default, null):Map<TeamId, Team>;
public var config(default, null):Config;
public var engine(default, null):Engine;
private var points:Array<SpawnPoint>;
private var spawners:Map<TeamId, Spawner>;
private var deferred:Deferred<GameState>;
private var stream:Stream<GameState>;
@@ -51,11 +57,14 @@ class Game implements EngineListener {
}
public function getPlayer(playerId:PlayerId):Player {
return teams.get(playerId.team).players[playerId.index];
return teams[playerId.team].players[playerId.index];
}
private function buildTank(playerId:PlayerId, config:TankConfig, point:SpawnPoint):Tank {
var tank = new Tank(playerId, config);
private function buildTank(playerId:PlayerId, point:SpawnPoint):Tank {
var types:Array<TankSpawn> = teams[playerId.team].config.tanks;
var type:TankSpawn = types[Math.floor(Math.random() * types.length)];
var tankConfig:TankConfig = config.getTank(type.group, type.type);
var tank = new Tank(playerId, tankConfig);
applyPoint(tank, point);
return tank;
}
@@ -68,39 +77,43 @@ class Game implements EngineListener {
public function start(state:GameState):Stream<GameState> {
this.deferred = new Deferred();
this.state = state;
var bricks = LevelBundle.get(type, config, state.level);
engine.map.setData(bricks);
var level = LevelBundle.get(type, config, state.level);
points = level.points != null ? level.points : config.points;
engine.map.setData(level.data);
teams = new Map<TeamId, Team>();
spawners = new Map<TeamId, Spawner>();
var humanControlIndex = 0;
for (teamConfig in config.teams) {
var team = new Team(teamConfig);
for (playerState in state.teams.get(team.id).players) {
var player = new Player({team:team.id, index:playerState.index});
team.players.push(player);
teams.set(team.id, team);
if (playerState.control != null) {
var control = switch (playerState.control.type) {
case HumanControl.TYPE: new HumanControl(playerState.control.index);
case BotControl.TYPE: new BotControl(playerState.control.index);
case 'none': null;
case _: throw 'Unsupported control type: "${playerState.control.type}"';
for (playerState in state.teams[team.id].players) {
var player = new Player(playerState.id);
team.players[player.id.index] = player;
teams[team.id] = team;
if (player.id.type != null) {
var control = switch (player.id.type) {
case Control.HUMAN: new HumanControl(player.id, humanControlIndex++);
case Control.BOT: new BotControl(player.id);
case Control.NONE: null;
case _: throw 'Unsupported control type: "${player.id.type}"';
}
L.d(TAG, 'control(${player.id} - ${control})');
if (control != null) {
player.control = control;
player.control.bind(engine);
}
}
}
spawners.set(team.id, new Spawner(team.config, spawn));
var teamPoints = points.filter(function(p:SpawnPoint) return p.team == team.id);
spawners[team.id] = new Spawner(team.config, teamPoints, spawn);
}
for (team in teams) {
for (player in team.players) {
if (trySpawn(player.id)) {
spawners.get(team.id).push(player.id);
spawners[team.id].push(player.id);
}
}
var eaglePoint = spawners.get(team.id).getPoint('eagle');
var eaglePoint = spawners[team.id].getPoint('eagle');
if (eaglePoint != null) {
var eagle = new Eagle(team.id);
applyPoint(eagle, eaglePoint);
@@ -112,12 +125,13 @@ class Game implements EngineListener {
}
private function spawn(task:SpawnTask):Void {
L.d(TAG, 'spawn(${task}');
getPlayer(task.playerId).tankId = 0;
if (trySpawn(task.playerId, true)) {
var tank = buildTank(task.playerId, config.getTank(task.playerId.team, '0'), task.point);
var tank = buildTank(task.playerId, task.point);
var player:Player = getPlayer(task.playerId);
engine.spawn(tank);
player.tankId = tank.id;
Timer.delay(function() engine.spawn(tank), 1500);
} else if (!isTeamAlive(task.playerId.team)) {
state.teams[task.playerId.team].lose = true;
complete();
@@ -132,31 +146,18 @@ class Game implements EngineListener {
player.control.dispose();
}
}
var timer = new Timer(5000);
timer.run = function() {
timer.stop();
Timer.delay(function() {
deferred.resolve(state);
stream.end();
}
}, 5000);
}
public function setControl(playerId:PlayerId, control:Control):Void {
for (team in teams.iterator()) {
if (team.id == playerId.team) {
var player = team.players[playerId.index];
if (player.control != null) {
player.control.dispose();
}
player.control = control;
player.control.bind(engine);
break;
}
}
}
public function onSpawn(entity:EntityType):Void {
switch (entity) {
case EntityType.TANK(tank):
getPlayer(tank.playerId).control.start();
case x:
}
}
public function onCollision(entity:EntityType, with:EntityType):Void {
@@ -207,10 +208,11 @@ class Game implements EngineListener {
public function onDestroy(entity:EntityType):Void {
switch (entity) {
case EntityType.TANK(tank):
getPlayer(tank.playerId).tankId = 0;
getPlayer(tank.playerId).control.stop();
getPlayer(tank.playerId).tankId = 0; //ToDo: ?
var respawn:Bool = trySpawn(tank.playerId);
if (respawn) {
spawners.get(tank.playerId.team).push(tank.playerId);
spawners[tank.playerId.team].push(tank.playerId);
}
if (!isTeamAlive(tank.playerId.team)) {
state.teams[tank.playerId.team].lose = true;

View File

@@ -1,23 +1,14 @@
package ru.m.tankz.game;
import ru.m.tankz.game.Game;
import ru.m.tankz.config.Config;
typedef ControlType = String;
typedef ControId = {
var type:ControlType;
var index:Int;
}
typedef PlayerState = {
var index:Int;
var tank:TankType;
var id:PlayerId;
var life:Int;
var control:ControId;
}
@@ -30,6 +21,7 @@ typedef TeamState = {
class GameState {
public var type:GameType;
public var mode:GameMode;
public var level:Int;
public var teams:Map<TeamId, TeamState>;

View File

@@ -19,11 +19,6 @@ class Player {
tankId = value;
if (control != null) {
control.tankId = tankId;
if (tankId != 0) {
control.start();
} else {
control.stop();
}
}
return tankId;
}
@@ -33,9 +28,6 @@ class Player {
control = value;
if (control != null) {
control.tankId = tankId;
if (tankId != 0) {
control.start();
}
}
return control;
}

View File

@@ -17,6 +17,7 @@ class Spawner {
public var active(get, never):Bool;
private var config:TeamConfig;
private var points:Array<SpawnPoint>;
private var runner:SpawnTask -> Void;
private var queue:Array<SpawnTask>;
private var timer:Timer;
@@ -25,13 +26,14 @@ class Spawner {
private var anyPoints:Array<SpawnPoint>;
private var index:Int;
public function new(config:TeamConfig, runner:SpawnTask -> Void) {
public function new(config:TeamConfig, points:Array<SpawnPoint>, runner:SpawnTask -> Void) {
this.config = config;
this.points = points;
this.runner = runner;
queue = [];
indexedPoints = new Map();
anyPoints = [];
for (point in config.points) {
for (point in points) {
if (point.type == 'tank') {
if (point.index > -1) {
indexedPoints.set(point.index, point);
@@ -43,7 +45,7 @@ class Spawner {
}
public function getPoint(type:String, index:Int=-1):Null<SpawnPoint> {
for (point in config.points) {
for (point in points) {
if (point.type == type && point.index == index) {
return point;
}

View File

@@ -9,13 +9,13 @@ class Team {
public var id(default, null):TeamId;
public var config(default, null):TeamConfig;
public var players(default, null):Array<Player>;
public var players(default, null):Map<Int, Player>;
private static var i:Int = 0;
public function new(config:TeamConfig) {
this.id = config.id;
this.config = config;
this.players = [];
this.players = new Map();
}
}

View File

@@ -62,4 +62,10 @@ class LevelMap {
public function getBrick(cell:GridCell):Brick {
return bricksMap.get(cell.position);
}
public function getPointBrick(point:Point):Brick {
var cellX:Int = Math.floor(point.x / config.cellWidth);
var cellY:Int = Math.floor(point.y / config.cellHeight);
return bricks[cellX + cellY * config.gridWidth];
}
}

View File

@@ -0,0 +1,53 @@
package ru.m.tankz.editor;
import flash.display.Bitmap;
import flash.display.Shape;
import haxework.gui.list.ListView;
import haxework.gui.SpriteView;
import openfl.utils.Assets;
import ru.m.tankz.config.Config;
class BrickView extends SpriteView implements IListItemView<BrickConfig> {
public var item_index(default, default):Int;
public var data(default, set):BrickConfig;
public var selected(default, set):Bool;
private var imageView:Bitmap;
private var selectView:Shape;
public function new() {
super();
width = 26;
height = 26;
selectView = createSelectView(width, height);
selectView.visible = false;
contentAsSprite.addChild(selectView);
imageView = new Bitmap();
contentAsSprite.addChild(imageView);
}
private static function createSelectView(width:Float, height:Float):Shape {
var view = new Shape();
view.graphics.lineStyle(4, 0x33ff00);
view.graphics.drawRect(0, 0, width, height);
view.graphics.lineStyle();
return view;
}
private function set_data(value:BrickConfig):BrickConfig {
data = value;
var src = 'resources/images/map/map_${value.type}.png';
imageView.bitmapData = Assets.getBitmapData(src);
imageView.x = (width - imageView.width) / 2;
imageView.y = (height - imageView.height) / 2;
return data;
}
private function set_selected(value:Bool):Bool {
selected = value;
selectView.visible = value;
return selected;
}
}

View File

@@ -0,0 +1,128 @@
package ru.m.tankz.editor;
import ru.m.tankz.editor.MapEditView.Brush;
import ru.m.tankz.game.DotaGame;
import haxework.gui.list.ListView;
import haxework.gui.list.VListView;
import ru.m.tankz.editor.FileUtil;
import haxework.gui.LabelView;
import ru.m.tankz.config.Config;
import ru.m.tankz.config.LevelBundle;
import ru.m.tankz.game.ClassicGame;
import ru.m.tankz.config.ConfigBundle;
import haxework.gui.ButtonView;
import haxework.gui.Root;
import flash.text.Font;
import haxework.resources.Resources;
import haxework.resources.IResources;
import haxework.provider.Provider;
import haxework.log.TraceLogger;
import haxework.gui.ViewBuilder;
import haxework.gui.GroupView;
import haxework.log.JSLogger;
import haxework.log.SocketLogger;
interface EditorViewLayout {
var openButton(default, null):ButtonView;
var saveButton(default, null):ButtonView;
var fileNameLabel(default, null):LabelView;
var mapView(default, null):MapEditView;
var spawnPointList(default, null):VListView<SpawnPoint>;
var brickList(default, null):VListView<BrickConfig>;
}
@:template("ru/m/tankz/editor/Editor.yaml")
class EditorView extends GroupView implements ViewBuilder implements EditorViewLayout {}
class Editor {
private static inline var TAG = "Tankz.Editor";
public static function main() {
L.push(new TraceLogger());
#if flash
L.push(new JSLogger());
#end
#if debug
L.push(new SocketLogger());
#end
Const.init();
L.d(TAG, "Debug: " + Const.DEBUG);
L.i(TAG, "Version: " + Const.VERSION);
L.i(TAG, "Build: " + Const.BUILD);
new Editor();
}
private var view:EditorView;
private var config:Config;
public function new() {
Provider.setFactory(IResources, Resources);
var font:Font = Font.enumerateFonts()[0];
Provider.get(IResources).text.put("font", "Bookman Old Style");
Provider.get(IResources).text.put("version", 'v${Const.VERSION} b${Const.BUILD}');
view = new EditorView();
Root.bind(view);
view.content.stage.stageFocusRect = false;
view.openButton.onPress = this;
view.saveButton.onPress = this;
config = ConfigBundle.get(DotaGame.TYPE);
view.mapView.config = config;
view.mapView.data = LevelBundle.empty(config);
view.brickList.data = config.bricks.filter(function(brick) return brick.type > -1);
view.spawnPointList.data = config.points;
var resetSelected = function() {
for (v in view.brickList.items) {
cast(v, BrickView).selected = false;
}
for (v in view.spawnPointList.items) {
cast(v, SpawnPointView).selected = false;
}
};
view.brickList.dispatcher.addListener({
onListItemClick: function(item:IListItemView<BrickConfig>) {
view.mapView.brush = Brush.BRICK(item.data);
resetSelected();
cast(item, BrickView).selected = true; }
});
view.spawnPointList.dispatcher.addListener({
onListItemClick: function(item:IListItemView<SpawnPoint>) {
view.mapView.brush = Brush.POINT(item.data);
resetSelected();
cast(item, SpawnPointView).selected = true;
}
});
view.mapView.brush = Brush.BRICK(view.brickList.data[0]);
cast(view.brickList.items[0], BrickView).selected = true;
}
public function onPress(v:ButtonView):Void {
switch (v.id) {
case 'openButton':
L.d(TAG, 'OPEN');
FileUtil.browse().then(function(content:FileContent) {
view.fileNameLabel.text = content.name;
view.mapView.data = LevelBundle.loads(config, content.content);
});
case 'saveButton':
L.d(TAG, 'SAVE');
FileUtil.save({
name: view.fileNameLabel.text,
content: LevelBundle.dumps(config, view.mapView.data),
});
case _:
}
}
}

View File

@@ -0,0 +1,71 @@
$type: haxework.gui.GroupView
pWidth: 100
pHeight: 100
views:
- $type: haxework.gui.VGroupView
pWidth: 100
pHeight: 100
views:
- $type: haxework.gui.HGroupView
pWidth: 100
height: 20
views:
- id: openButton
$type: haxework.gui.ButtonView
text: Open
contentSize: true
skin:
$type: haxework.gui.skin.ButtonColorSkin
color: 0xaaff00
- id: saveButton
$type: haxework.gui.ButtonView
text: Save
contentSize: true
skin:
$type: haxework.gui.skin.ButtonColorSkin
color: 0xaaff00
- id: fileNameLabel
$type: haxework.gui.LabelView
contentSize: true
- $type: haxework.gui.HGroupView
contentSize: true
views:
- id: spawnPointList
$type: haxework.gui.list.VListView<SpawnPoint>
factory: '@class:ru.m.tankz.editor.SpawnPointView'
width: 56
pHeight: 100
scroll:
$type: haxework.gui.list.VScrollView
width: 0
pHeight: 100
skin:
$type: haxework.gui.list.VScrollSkin
skin:
$type: haxework.gui.skin.ColorSkin
color: 0x000000
alpha: 0.0
- id: mapView
$type: ru.m.tankz.editor.MapEditView
contentSize: true
- id: brickList
$type: haxework.gui.list.VListView<BrickConfig>
factory: '@class:ru.m.tankz.editor.BrickView'
width: 30
pHeight: 100
scroll:
$type: haxework.gui.list.VScrollView
width: 0
pHeight: 100
skin:
$type: haxework.gui.list.VScrollSkin
skin:
$type: haxework.gui.skin.ColorSkin
color: 0x000000
alpha: 0.0
- $type: haxework.gui.LabelView
inLayout: false
contentSize: true
vAlign: BOTTOM
hAlign: RIGHT
text: '@res:text:version'

View File

@@ -0,0 +1,48 @@
package ru.m.tankz.editor;
import promhx.Deferred;
import flash.events.ProgressEvent;
import flash.events.IOErrorEvent;
import flash.events.Event;
import flash.net.FileReference;
import promhx.Promise;
typedef FileContent = {
var name:String;
var content:String;
}
class FileUtil {
public static function browse():Promise<FileContent> {
var d = new Deferred<FileContent>();
var file = new FileReference();
file.addEventListener(Event.SELECT, function(event:Event) {
cast(event.target, FileReference).load();
});
file.addEventListener(IOErrorEvent.IO_ERROR, function(event:IOErrorEvent) {
d.throwError(event);
});
file.addEventListener(ProgressEvent.PROGRESS, function(event:ProgressEvent) {
trace('progress', '${event}');
});
file.addEventListener(Event.COMPLETE, function(event:Event) {
var f:FileReference = cast event.target;
var data = f.data.readUTFBytes(f.data.length);
d.resolve({
name: f.name,
content: data
});
});
file.browse();
return d.promise();
}
public static function save(content:FileContent):Void {
var file = new FileReference();
file.save(content.content, content.name);
}
}

View File

@@ -0,0 +1,227 @@
package ru.m.tankz.editor;
import ru.m.geom.Rectangle;
import ru.m.tankz.core.Entity;
import ru.m.geom.Point;
import flash.events.MouseEvent;
import ru.m.tankz.map.Brick;
import flash.display.DisplayObjectContainer;
import flash.display.Graphics;
import flash.display.Sprite;
import ru.m.tankz.render.RenderItem;
import ru.m.tankz.config.Config;
import ru.m.tankz.map.LevelMap;
import haxework.gui.SpriteView;
class SpawnPointEntity extends Entity {
public var point(default, null):SpawnPoint;
public function new(point:SpawnPoint, rect:Rectangle) {
super(rect);
this.point = point;
}
}
class SpawnPointItem extends BitmapItem<SpawnPointEntity> {
private var cellX:Int;
private var cellY:Int;
public function new(value:SpawnPoint, config:MapConfig) {
super(new SpawnPointEntity(value, new Rectangle(
value.x * config.cellWidth,
value.y * config.cellHeight,
config.cellWidth * 2,
config.cellHeight * 2
)));
}
override public function update():Void {
super.update();
if (cellX != value.point.x || cellY != value.point.y) {
cellX = value.point.x;
cellY = value.point.y;
value.rect.x = cellX * (value.rect.width / 2);
value.rect.y = cellY * (value.rect.height / 2);
redraw();
}
}
override private function getImage():String {
return switch(value.point.type) {
case 'eagle': 'resources/images/eagle/eagle-0.png';
case 'tank': 'resources/images/tank/bot/tank_b0_0-0.png';
case x: 'resources/images/eagle/eagle-1.png';
}
}
}
enum Brush {
POINT(point:SpawnPoint);
BRICK(brick:BrickConfig);
}
//ToDo: copy paste from ru.m.tankz.render.Render
class MapEditView extends SpriteView {
public var config(default, set):Config;
public var data(get, set):LevelConfig;
public var map(default, null):LevelMap;
public var brush(default, default):Brush;
private var items:Map<String, RenderItem<Dynamic, Dynamic>>;
private var backgroundLayer:Sprite;
private var upLayer:Sprite;
private var groundLayer:Sprite;
private var spawnLayer:Sprite;
public function new() {
super();
items = new Map();
map = null;
backgroundLayer = new Sprite();
upLayer = new Sprite();
groundLayer = new Sprite();
spawnLayer = new Sprite();
contentAsSprite.addChild(backgroundLayer);
contentAsSprite.addChild(groundLayer);
contentAsSprite.addChild(upLayer);
contentAsSprite.addChild(spawnLayer);
contentAsSprite.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
reset();
}
private function onMouseDown(event:MouseEvent):Void {
contentAsSprite.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
contentAsSprite.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
onMouseMove(event);
}
private function onMouseUp(event:MouseEvent):Void {
contentAsSprite.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
contentAsSprite.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
private function onMouseMove(event:MouseEvent):Void {
var b = map.getPointBrick(new Point(event.localX, event.localY));
if (b != null) {
switch (brush) {
case Brush.POINT(point):
for (p in config.points) {
if (p.team == point.team && p.type == point.type && p.index == point.index) {
p.x = b.cellX;
p.y = b.cellY;
drawMap();
break;
}
}
case Brush.BRICK(brick):
b.config = brick;
drawMap();
}
}
}
private function clearLayer(layer:DisplayObjectContainer) {
while (layer.numChildren > 0) layer.removeChildAt(0);
}
public function reset():Void {
for (item in items.iterator()) {
item.dispose();
}
items = new Map();
clearLayer(groundLayer);
clearLayer(upLayer);
clearLayer(spawnLayer);
}
private function drawBackground():Void {
var mapWidth = map.gridWidth * map.cellWidth;
var mapHeight = map.gridHeight * map.cellHeight;
var g:Graphics = backgroundLayer.graphics;
g.clear();
g.beginFill(0x000000);
g.drawRect(0, 0, mapWidth, mapHeight);
g.endFill();
if (contentSize) {
width = mapWidth;
height = mapHeight;
}
g.lineStyle(1, 0x007700);
for (x in 0...map.gridWidth) {
g.moveTo(x * map.cellWidth, 0);
g.lineTo(x * map.cellWidth, mapHeight);
}
for (y in 0...map.gridHeight) {
g.moveTo(0, y * map.cellHeight);
g.lineTo(mapWidth, y * map.cellHeight);
}
}
override public function update():Void {
if (this.map != null) {
drawBackground();
drawMap();
}
super.update();
}
private function drawMap() {
for (brick in map.bricks) if (brick.config.type > 0) {
if (!items.exists(brick.key)) {
items[brick.key] = new BrickItem(brick);
if (brick.config.layer > 2) {
upLayer.addChild(items[brick.key].view);
} else {
groundLayer.addChild(items[brick.key].view);
}
}
}
for (point in config.points) {
var key = '${point.team}:${point.type}:${point.index}';
if (!items.exists(key)) {
items[key] = new SpawnPointItem(point, config.map);
spawnLayer.addChild(items[key].view);
}
}
for (item in items) {
item.update();
}
}
private function set_config(value:Config):Config {
config = value;
map = new LevelMap(config.map);
invalidate();
return config;
}
private function get_data():LevelConfig {
return {
data: map.bricks.map(function(brick:Brick) return brick.config),
points: config.points,
}
}
private function set_data(value:LevelConfig):LevelConfig {
reset();
map.setData(value.data);
if (value.points != null) for (point in value.points) {
for (p in config.points) {
if (p.team == point.team && p.type == point.type && p.index == point.index) {
p.x = point.x;
p.y = point.y;
break;
}
}
}
invalidate();
return value;
}
}

View File

@@ -0,0 +1,57 @@
package ru.m.tankz.editor;
import flash.display.Bitmap;
import flash.display.Shape;
import haxework.gui.list.ListView;
import haxework.gui.SpriteView;
import openfl.utils.Assets;
import ru.m.tankz.config.Config;
class SpawnPointView extends SpriteView implements IListItemView<SpawnPoint> {
public var item_index(default, default):Int;
public var data(default, set):SpawnPoint;
public var selected(default, set):Bool;
private var imageView:Bitmap;
private var selectView:Shape;
public function new() {
super();
width = 52;
height = 52;
selectView = createSelectView(width, height);
selectView.visible = false;
contentAsSprite.addChild(selectView);
imageView = new Bitmap();
contentAsSprite.addChild(imageView);
}
private static function createSelectView(width:Float, height:Float):Shape {
var view = new Shape();
view.graphics.lineStyle(4, 0x33ff00);
view.graphics.drawRect(0, 0, width, height);
view.graphics.lineStyle();
return view;
}
private function set_data(value:SpawnPoint):SpawnPoint {
data = value;
var src = switch(value.type) {
case 'eagle': 'resources/images/eagle/eagle-0.png';
case 'tank': 'resources/images/tank/bot/tank_b0_0-0.png';
case x: 'resources/images/eagle/eagle-1.png';
}
imageView.bitmapData = Assets.getBitmapData(src);
imageView.x = (width - imageView.width) / 2;
imageView.y = (height - imageView.height) / 2;
return data;
}
private function set_selected(value:Bool):Bool {
selected = value;
selectView.visible = value;
return selected;
}
}

View File

@@ -11,6 +11,7 @@ const PluginError = require('plugin-error');
const colors = require('ansi-colors');
const log = require('fancy-log');
const vfs = require('vinyl-fs');
const rmdir = require('rmdir');
class Haxe extends Sdk {
@@ -179,13 +180,10 @@ class Haxe extends Sdk {
args.push('-debug');
}
//console.log('haxelib', args.join(' '));
const target = `${buildDir}/${params.platform}/bin`;
rmdir(target);
this.haxelib(args).then(() => {
const result = {
'flash': `${buildDir}/flash/bin/*.swf`,
'html5': `${buildDir}/html5/bin/**/*`,
'linux': `${buildDir}/linux/bin/**/*`,
}[params.platform];
vfs.src(result).pipe(through.obj((file, enc, cb) => {
vfs.src(`${target}/**/*`).pipe(through.obj((file, enc, cb) => {
file.debug = debug;
stream.push(file);
cb();