11 Commits
0.3.0 ... 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
36 changed files with 1019 additions and 458 deletions

62
WORK.md
View File

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

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

View File

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

View File

@@ -20,4 +20,9 @@
<haxeflag name="-dce" value="no"/> <haxeflag name="-dce" value="no"/>
<haxeflag name="-D" value="dom"/> <haxeflag name="-D" value="dom"/>
<!--<template path="src/client/webapp/index_template.html" rename="index.html"/>--> <!--<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> </project>

View File

@@ -7,21 +7,45 @@
"contentSize": true, "bottomMargin": 15 "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", "@type": "haxework.gui.ButtonView",
"text": "1 Player", "text": "1 Player",
"@style": "button" "@style": "button"
}, },
{ {
"id": "start_2p", "id": "classic_2p",
"@type": "haxework.gui.ButtonView", "@type": "haxework.gui.ButtonView",
"text": "2 Player", "text": "2 Player",
"@style": "button" "@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", "@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" "@style": "button"
} }
] ]

View File

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

View File

@@ -5,12 +5,8 @@ import openfl.Assets;
import ru.m.animate.OnceAnimate; import ru.m.animate.OnceAnimate;
import flash.display.DisplayObjectContainer; import flash.display.DisplayObjectContainer;
import ru.m.tankz.core.EntityType; import ru.m.tankz.core.EntityType;
import flash.display.DisplayObject;
import Type.ValueType;
import ru.m.tankz.render.RenderItem; import ru.m.tankz.render.RenderItem;
import ru.m.tankz.engine.Engine; 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.Sprite;
import flash.display.Graphics; import flash.display.Graphics;
import haxework.gui.SpriteView; import haxework.gui.SpriteView;
@@ -101,6 +97,7 @@ class Render extends SpriteView implements EngineListener {
items.set(tank.key, item); items.set(tank.key, item);
entryLayer.addChild(item.view); entryLayer.addChild(item.view);
item.update(); item.update();
playTankSpawn(tank.rect.center);
case EntityType.BULLET(bullet): case EntityType.BULLET(bullet):
var item = new BulletItem(bullet); var item = new BulletItem(bullet);
items.set(bullet.key, item); items.set(bullet.key, item);
@@ -143,6 +140,21 @@ class Render extends SpriteView implements EngineListener {
} }
} }
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 { private function playBulletBoom(point:Point):Void {
var arr = [ var arr = [
0, 1, 1, 0, 0, 1 0, 1, 1, 0, 0, 1

View File

@@ -1,5 +1,7 @@
package ru.m.tankz.render; package ru.m.tankz.render;
import ru.m.tankz.control.Control;
import flash.display.Sprite;
import ru.m.animate.Animate; import ru.m.animate.Animate;
import ru.m.tankz.core.Eagle; import ru.m.tankz.core.Eagle;
import flash.display.DisplayObject; import flash.display.DisplayObject;
@@ -76,6 +78,7 @@ class BitmapItem<T:TRectangle> extends RenderItem<T, Bitmap> {
class BrickItem extends RenderItem<Brick, Shape> { class BrickItem extends RenderItem<Brick, Shape> {
private var broken:Int; private var broken:Int;
private var type:Int;
public function new(value:Brick) { public function new(value:Brick) {
super(value); super(value);
@@ -88,22 +91,26 @@ class BrickItem extends RenderItem<Brick, Shape> {
var g = view.graphics; var g = view.graphics;
g.clear(); g.clear();
if (value.destroyed) return; if (value.destroyed) return;
g.beginBitmapFill(image); if (value.config.type > 0) {
g.drawRect(0, 0, value.rect.width, value.rect.height); g.beginBitmapFill(image);
for (c in value.cells) { g.drawRect(0, 0, value.rect.width, value.rect.height);
if (c.destroyed) { for (c in value.cells) {
g.beginFill(0x000000); if (c.destroyed) {
g.drawRect(c.rect.x - value.rect.x, c.rect.y - value.rect.y, c.rect.width, c.rect.height); 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 { override public function update():Void {
super.update(); super.update();
var b = value.broken; var b = value.broken;
if (b != this.broken) { var t = value.config.type;
this.broken = b; if (b != broken || t != type) {
broken = b;
type = t;
redraw(); redraw();
} }
} }
@@ -114,19 +121,38 @@ class BrickItem extends RenderItem<Brick, Shape> {
} }
class TankItem extends RenderItem<Tank, Animate> { class TankItem extends RenderItem<Tank, Sprite> {
private var type:String; private var type:String;
private var hits:Int; private var hits:Int;
private var tankView:Animate;
public function new(value:Tank) { public function new(value:Tank) {
super(value); super(value);
view = new Animate(); view = new Sprite();
if (value.playerId.type == Control.HUMAN) {
view.addChild(buildHumanMarkView(value));
}
tankView = new Animate();
view.addChild(tankView);
redraw(); 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 { override public function redraw():Void {
view.frames = getFrames().map(function(s) return Assets.getBitmapData(s)); tankView.frames = getFrames().map(function(s) return Assets.getBitmapData(s));
} }
private function getFrames():Array<String> { private function getFrames():Array<String> {
@@ -160,13 +186,13 @@ class TankItem extends RenderItem<Tank, Animate> {
this.hits = h; this.hits = h;
redraw(); redraw();
} }
view.playing = (value.mx !=0 || value.my != 0); tankView.playing = (value.mx !=0 || value.my != 0);
} }
override public function dispose():Void { override public function dispose():Void {
if (view != null) { if (tankView != null) {
view.dispose(); tankView.dispose();
view = null; tankView = null;
} }
} }
} }

View File

@@ -15,7 +15,7 @@ interface LevelFrameLayout {
@:template("layout/frames/level.json", "layout/styles.json") @: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 static inline var ID = "level";
public function init():Void { public function init():Void {

View File

@@ -12,9 +12,11 @@ import haxework.gui.VGroupView;
interface StartFrameLayout { interface StartFrameLayout {
var start_1p(default, null):ButtonView; var classic_1p(default, null):ButtonView;
var start_2p(default, null):ButtonView; var classic_2p(default, null):ButtonView;
var dota(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 static inline var ID = "start";
public function init() { public function init() {
start_1p.onPress = this; classic_1p.onPress = this;
start_2p.onPress = this; classic_2p.onPress = this;
dota.onPress = this; dota_1p.onPress = this;
dota_2p_coop.onPress = this;
dota_2p_vs.onPress = this;
} }
public function onPress(view:ButtonView):Void { public function onPress(view:ButtonView):Void {
switch (view.id) { switch (view.id) {
case 'start_1p': case 'classic_1p':
startGame(ClassicGame.TYPE, 1); startGame(ClassicGame.TYPE, ClassicGame.PLAYER1);
case 'start_2p': case 'classic_2p':
startGame(ClassicGame.TYPE, 2); startGame(ClassicGame.TYPE, ClassicGame.PLAYER2);
case 'dota': case 'dota_1p':
startGame(DotaGame.TYPE, 2); 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) { switch (type) {
case ClassicGame.TYPE: case ClassicGame.TYPE:
Provider.set(GameState, ClassicGame.buildState(0, humans)); Provider.set(GameState, ClassicGame.buildState(0, mode));
Provider.get(IFrameSwitcher).change(LevelFrame.ID); Provider.get(IFrameSwitcher).change(LevelFrame.ID);
case DotaGame.TYPE: case DotaGame.TYPE:
Provider.set(GameState, DotaGame.buildState(0, humans)); Provider.set(GameState, DotaGame.buildState(0, mode));
Provider.get(IFrameSwitcher).change(GameFrame.ID); Provider.get(IFrameSwitcher).change(LevelFrame.ID);
} }
} }
} }

View File

@@ -9,89 +9,34 @@ map:
gridHeight: 26 gridHeight: 26
bricks: bricks:
# border - {type: -1, layer: 2, armor: -1} # border
- type: -1 - {type: 0, layer: 0, armor: 0} # none
layer: 2 - {type: 1, layer: 0, armor: 0} # ace
armor: -1 - {type: 2, layer: 3, armor: 0} # bush
# none - {type: 3, layer: 1, armor: 0} # water
- type: 0 - {type: 4, layer: 2, armor: 2} # armor
layer: 0 - {type: 5, layer: 2, armor: 1} # brick
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
teams: teams:
- id: human - id: human
spawnInterval: 0 spawnInterval: 0
tanks: tanks:
- group: human - {group: human, type: 0, rate: 1}
type: 0
rate: 1
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
- id: bot - id: bot
spawnInterval: 3000 spawnInterval: 3000
tanks: tanks:
- group: bot - {group: bot, type: 0, rate: 0.5}
type: 0 - {group: bot, type: 1, rate: 0.5}
rate: 0.5 - {group: bot, type: 2, rate: 0.5}
- group: bot - {group: bot, type: 3, rate: 0.5}
type: 1
rate: 0.5 points:
- group: bot - {team: human, type: eagle, index: -1, direction: right, x: 12, y: 24}
type: 2 - {team: human, type: tank, index: 0, direction: top, x: 8, y: 24}
rate: 0.5 - {team: human, type: tank, index: 1, direction: top, x: 16, y: 24}
- group: bot - {team: bot, type: tank, index: -1, direction: bottom, x: 0, y: 0}
type: 3 - {team: bot, type: tank, index: -1, direction: bottom, x: 12, y: 0}
rate: 0.5 - {team: bot, type: tank, index: -1, direction: bottom, x: 24, y: 0}
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
bullet: &bullet bullet: &bullet
width: 12 width: 12

View File

@@ -1,5 +1,5 @@
game: game:
levels: 2 levels: 3
friendlyFire: true friendlyFire: true
map: map:
@@ -9,120 +9,41 @@ map:
gridHeight: 30 gridHeight: 30
bricks: bricks:
# border - {type: -1, layer: 2, armor: -1} # border
- type: -1 - {type: 0, layer: 0, armor: 0} # none
layer: 2 - {type: 1, layer: 0, armor: 0} # ace
armor: -1 - {type: 2, layer: 3, armor: 0} # bush
# none - {type: 3, layer: 1, armor: 0} # water
- type: 0 - {type: 4, layer: 2, armor: 2} # armor
layer: 0 - {type: 5, layer: 2, armor: 1} # brick
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
team_tanks: &team_tanks team_tanks: &team_tanks
tanks: tanks:
- group: any - {group: any, type: 0, rate: 0.25}
type: 0 - {group: any, type: 1, rate: 0.25}
rate: 0.25 - {group: any, type: 2, rate: 0.25}
- group: any
type: 1
rate: 0.25
- group: any
type: 2
rate: 0.25
# - group: any
# type: 3
# rate: 0.25
teams: teams:
- <<: *team_tanks - <<: *team_tanks
id: radiant id: radiant
spawnInterval: 0 spawnInterval: 0
points:
- type: eagle
index: -1
x: 0
y: 28
direction: right
- type: tank
index: 0
x: 0
y: 0
direction: right
- type: tank
index: 1
x: 6
y: 10
direction: right
- type: tank
index: 2
x: 6
y: 16
direction: right
- type: tank
index: 3
x: 6
y: 22
direction: right
- type: tank
index: 4
x: 10
y: 28
direction: right
- <<: *team_tanks - <<: *team_tanks
id: dire id: dire
spawnInterval: 0 spawnInterval: 0
points:
- type: eagle
index: -1
x: 38
y: 0
direction: right
- type: tank
index: 0
x: 38
y: 28
direction: left
- type: tank
index: 1
x: 32
y: 18
direction: left
- type: tank
index: 2
x: 32
y: 12
direction: left
- type: tank
index: 3
x: 32
y: 6
direction: left
- type: tank
index: 4
x: 28
y: 0
direction: left
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 bullet: &bullet
width: 12 width: 12

View File

@@ -1,30 +1 @@
0000000000000000000000000000000000005500 000044000000000000000000005500004400550000004400000000000000000000550000440055000000440000004400003333000055000044005555000044000000440000333300005500004400555500004422442244000000000000550000440000000000442244224400000000000055000044000000000000000000444444555544444400442200330000000000000044444455554444440044220033005555333333554422220000222244000022003300555533333355442222000022224400002200330000000000000044220000000022440000440033000000000000004422000000002244000044003300000000000000550000555500005500002200000000000000000055000055550000550000220000004444444400005500445555440055000044444444444444440000550044555544005500004444444400000022000055000055550000550000000000000000002200005500005555000055000000000000003300440000442200000000224400000000000000330044000044220000000022440000000000000033002200004422220000222244553333335555003300220000442222000022224455333333555500330022440044444455554444440000000000000033002244004444445555444444000000000000000000440000550000000000004422442244000000000044000055000000000000442244224400005555004400005500003333000044000000440000555500440000550000333300004400000044000000550044000055000000000000000000004400000055004400005500000000000000000000440000
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

View File

@@ -1,30 +1,2 @@
0000000000000000000000000000000000004400 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}]
0000000000000000000000000000000000004400 data: "005500330033003300333300330033003300550000550033003300330033330033003300330055000000000000330033000000003300330000000000000000000033003300000000330033000000000000550033000000330000000033000000330055000055003300000033000000003300000033005500000000330000003300333300330000003300000000000033000000330033330033000000330000000055000000330033003333003300330000005500005500000033003300333300330033000000550000000033003300330033330033003300330000000000003300330033003333003300330033000000005500330000003300333300330000003300550000550033000000330033330033000000330055000000000000330033003333003300330000000000000000000033003300333300330033000000000000550033003300330033330033003300330055000055003300330033003333003300330033005500000000330033003300333300330033003300000000000033003300330033330033003300330000005555553300330033003333003300330033555555555555330033003300333300330033003355555555555533003300330033330033003300335555555555553300330033003333003300330033555555440000330033000000333300000033000000004444000033003300000033330000003300000000440000443300330000003333000000330033440000000044330033000000333300000033003344000000440033003300330033330033003300330044000044003300330033003333003300330033004400"
0000000000000000000000000000000000004444
0000000000000000000000000000000000004444
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
4444000000000000000000000000000000000000
4444000000000000000000000000000000000000
0044000000000000000000000000000000000000
0044000000000000000000000000000000000000

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

@@ -1,5 +1,6 @@
package ru.m.tankz.bot; package ru.m.tankz.bot;
import ru.m.tankz.game.Game;
import ru.m.tankz.core.EntityType; import ru.m.tankz.core.EntityType;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;
import ru.m.geom.Direction; import ru.m.geom.Direction;
@@ -7,14 +8,13 @@ import haxe.Timer;
class BotControl extends Control { class BotControl extends Control {
public static var TYPE(default, never):ControlType = 'bot';
private var shotTimer:Timer; private var shotTimer:Timer;
private var turnRandomTimer:Timer;
private var turnTimer:Timer; private var turnTimer:Timer;
private var turnDelayTimer:Timer;
public function new(index:Int) { public function new(playerId:PlayerId) {
super({type:TYPE, index:index}); super(playerId);
} }
override public function onCollision(with:EntityType):Void { override public function onCollision(with:EntityType):Void {
@@ -33,9 +33,9 @@ class BotControl extends Control {
shotTimer = new Timer(1000); shotTimer = new Timer(1000);
shotTimer.run = shot; shotTimer.run = shot;
} }
if (turnTimer == null) { if (turnRandomTimer == null) {
turnTimer = new Timer(2000); turnRandomTimer = new Timer(2000);
turnTimer.run = turn; turnRandomTimer.run = turn;
} }
} }
@@ -44,9 +44,9 @@ class BotControl extends Control {
shotTimer.stop(); shotTimer.stop();
shotTimer = null; shotTimer = null;
} }
if (turnTimer != null) { if (turnRandomTimer != null) {
turnTimer.stop(); turnRandomTimer.stop();
turnTimer = null; turnRandomTimer = null;
} }
} }
@@ -55,17 +55,16 @@ class BotControl extends Control {
} }
public function turnAfter(delay:Int):Void { public function turnAfter(delay:Int):Void {
if (turnDelayTimer == null) { if (turnTimer == null) {
turnDelayTimer = new Timer(delay); turnTimer = Timer.delay(turn, delay);
turnDelayTimer.run = function() {
turnDelayTimer.stop();
turnDelayTimer = null;
turn();
}
} }
} }
public function turn():Void { public function turn():Void {
if (turnTimer != null) {
turnTimer.stop();
turnTimer = null;
}
action(TankAction.MOVE(randomDirection())); action(TankAction.MOVE(randomDirection()));
} }

View File

@@ -7,6 +7,7 @@ typedef GameConfig = {
} }
typedef SpawnPoint = { typedef SpawnPoint = {
var team:String;
var type:String; var type:String;
var index:Int; var index:Int;
var x:Int; var x:Int;
@@ -59,7 +60,12 @@ typedef TeamConfig = {
var size:Int; var size:Int;
var spawnInterval:Int; var spawnInterval:Int;
var tanks:Array<TankSpawn>; var tanks:Array<TankSpawn>;
var points:Array<SpawnPoint>; }
typedef LevelConfig = {
var data:Array<BrickConfig>;
@:optional var points:Array<SpawnPoint>;
} }
@@ -70,17 +76,27 @@ class Config {
public var bricks(default, null):Array<BrickConfig>; public var bricks(default, null):Array<BrickConfig>;
public var tanks(default, null):Array<TankConfig>; public var tanks(default, null):Array<TankConfig>;
public var teams(default, null):Array<TeamConfig>; public var teams(default, null):Array<TeamConfig>;
public var points(default, null):Array<SpawnPoint>;
private var brickMap:Map<Int, BrickConfig>; private var brickMap:Map<Int, BrickConfig>;
private var tankMap:Map<String, Map<String, TankConfig>>; private var tankMap:Map<String, Map<String, TankConfig>>;
private var teamMap:Map<String, TeamConfig>; private var teamMap:Map<String, TeamConfig>;
public function new(type:String, game:GameConfig, 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.type = type;
this.game = game; this.game = game;
this.map = map; this.map = map;
this.bricks = bricks; this.bricks = bricks;
this.teams = teams; this.teams = teams;
this.points = points;
this.tanks = tanks; this.tanks = tanks;
init(); init();
} }

View File

@@ -11,6 +11,7 @@ typedef ConfigSource = {
var map: MapConfig; var map: MapConfig;
var bricks: Array<BrickConfig>; var bricks: Array<BrickConfig>;
var teams: Array<TeamConfig>; var teams: Array<TeamConfig>;
var points: Array<SpawnPoint>;
var tanks: Dynamic<Array<TankConfig>>; var tanks: Dynamic<Array<TankConfig>>;
} }
@@ -30,6 +31,6 @@ class ConfigBundle {
tanks.push(item); tanks.push(item);
} }
} }
return new Config(type, source.game, 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; package ru.m.tankz.config;
import yaml.Renderer;
import yaml.Parser;
import yaml.Yaml;
import openfl.Assets; import openfl.Assets;
import ru.m.tankz.game.Game; import ru.m.tankz.game.Game;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
typedef LevelSource = {
var data:String;
@:optional var points:Array<SpawnPoint>;
}
class LevelBundle { class LevelBundle {
private static function formatLevel(level:Int):String { private static function formatLevel(level:Int):String {
@@ -13,16 +22,49 @@ class LevelBundle {
return result; return result;
} }
public static function get(type:GameType, config:Config, level:Int):Array<BrickConfig> { public static function loadsOld(config:Config, data:String):LevelConfig {
var bricksData:String = Assets.getText('resources/${type}/levels/level${formatLevel(level)}.txt');
var bricks:Array<BrickConfig> = []; var bricks:Array<BrickConfig> = [];
for (line in ~/\s+/g.split(bricksData)) { for (line in ~/\s+/g.split(data)) {
for (c in line.split('')) { for (c in line.split('')) {
if (c.length > 0) { 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; package ru.m.tankz.control;
import ru.m.tankz.game.Game;
import ru.m.tankz.core.Entity; import ru.m.tankz.core.Entity;
import ru.m.tankz.core.EntityType; import ru.m.tankz.core.EntityType;
import ru.m.geom.Direction; import ru.m.geom.Direction;
@@ -16,19 +17,17 @@ enum TankAction {
typedef ControlType = String; typedef ControlType = String;
typedef ControlId = {
var type:ControlType;
var index:Int;
}
class Control { 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; public var tankId(default, default):Int;
private var handler:ControlHandler; private var handler:ControlHandler;
public function new(id:ControlId) { public function new(playerId:PlayerId) {
this.id = id; this.playerId = playerId;
} }
public function bind(handler:ControlHandler):Void { public function bind(handler:ControlHandler):Void {

View File

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

View File

@@ -1,5 +1,6 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import ru.m.tankz.control.Control;
import haxe.ds.Option; import haxe.ds.Option;
import ru.m.tankz.game.Game; import ru.m.tankz.game.Game;
import ru.m.tankz.game.GameState; import ru.m.tankz.game.GameState;
@@ -12,52 +13,54 @@ class DotaGame extends Game {
public static var RADIANT(default, never):TeamId = 'radiant'; public static var RADIANT(default, never):TeamId = 'radiant';
public static var DIRE(default, never):TeamId = 'dire'; 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; private static var TEAM_SIZE(default, never):Int = 5;
public function new() { public function new() {
super(TYPE); super(TYPE);
} }
public static function buildState(level:Int, humans:Int):GameState { public static function buildState(level:Int, mode:GameMode):GameState {
var state = new GameState(); var state = new GameState();
state.type = TYPE; state.type = TYPE;
state.mode = mode;
state.level = level; state.level = level;
state.teams[RADIANT] = {life: 20, players: new Map(), lose: false}; 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) { for (i in 0...TEAM_SIZE) {
state.teams[RADIANT].players[i] = { state.teams[RADIANT].players[i] = {
index:i, id: {team:RADIANT, index:i, type:Control.BOT},
tank:{ life: -1,
group: RADIANT,
type: '1'
},
control:{
type: 'bot',
index: i
},
life:-1,
}; };
} }
for (i in 0...TEAM_SIZE) { for (i in 0...TEAM_SIZE) {
state.teams[DIRE].players[i] = { state.teams[DIRE].players[i] = {
index:i, id: {team:DIRE, index:i, type:Control.BOT},
tank:{ life: -1,
group: DIRE,
type: '1'
},
control:{
type: 'bot',
index: i
},
life:-1,
}; };
} }
for (human in mode) {
state.teams[human.team].players[human.index].id = human;
}
return state; return state;
} }
override public function next():Option<GameState> { override public function next():Option<GameState> {
state.level++; state.level++;
if (state.level >= config.game.levels) state.level = 0; if (state.level >= config.game.levels) state.level = 0;
return Option.Some(buildState(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 GameType = String;
typedef GameMode = Array<PlayerId>;
typedef TeamId = String; typedef TeamId = String;
typedef PlayerId = { typedef PlayerId = {
var team:TeamId; var team:TeamId;
var type:ControlType;
var index:Int; var index:Int;
} }
class Game implements EngineListener { class Game implements EngineListener {
private static var TAG(default, never):String = Type.getClassName(Game).split('.').pop();
public var type(default, null):GameType; public var type(default, null):GameType;
public var state(default, null):GameState; public var state(default, null):GameState;
public var teams(default, null):Map<TeamId, Team>; public var teams(default, null):Map<TeamId, Team>;
public var config(default, null):Config; public var config(default, null):Config;
public var engine(default, null):Engine; public var engine(default, null):Engine;
private var points:Array<SpawnPoint>;
private var spawners:Map<TeamId, Spawner>; private var spawners:Map<TeamId, Spawner>;
private var deferred:Deferred<GameState>; private var deferred:Deferred<GameState>;
private var stream:Stream<GameState>; private var stream:Stream<GameState>;
@@ -51,7 +57,7 @@ class Game implements EngineListener {
} }
public function getPlayer(playerId:PlayerId):Player { 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, point:SpawnPoint):Tank { private function buildTank(playerId:PlayerId, point:SpawnPoint):Tank {
@@ -71,39 +77,43 @@ class Game implements EngineListener {
public function start(state:GameState):Stream<GameState> { public function start(state:GameState):Stream<GameState> {
this.deferred = new Deferred(); this.deferred = new Deferred();
this.state = state; this.state = state;
var bricks = LevelBundle.get(type, config, state.level); var level = LevelBundle.get(type, config, state.level);
engine.map.setData(bricks); points = level.points != null ? level.points : config.points;
engine.map.setData(level.data);
teams = new Map<TeamId, Team>(); teams = new Map<TeamId, Team>();
spawners = new Map<TeamId, Spawner>(); spawners = new Map<TeamId, Spawner>();
var humanControlIndex = 0;
for (teamConfig in config.teams) { for (teamConfig in config.teams) {
var team = new Team(teamConfig); var team = new Team(teamConfig);
for (playerState in state.teams.get(team.id).players) { for (playerState in state.teams[team.id].players) {
var player = new Player({team:team.id, index:playerState.index}); var player = new Player(playerState.id);
team.players.push(player); team.players[player.id.index] = player;
teams.set(team.id, team); teams[team.id] = team;
if (playerState.control != null) { if (player.id.type != null) {
var control = switch (playerState.control.type) { var control = switch (player.id.type) {
case HumanControl.TYPE: new HumanControl(playerState.control.index); case Control.HUMAN: new HumanControl(player.id, humanControlIndex++);
case BotControl.TYPE: new BotControl(playerState.control.index); case Control.BOT: new BotControl(player.id);
case 'none': null; case Control.NONE: null;
case _: throw 'Unsupported control type: "${playerState.control.type}"'; case _: throw 'Unsupported control type: "${player.id.type}"';
} }
L.d(TAG, 'control(${player.id} - ${control})');
if (control != null) { if (control != null) {
player.control = control; player.control = control;
player.control.bind(engine); 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 (team in teams) {
for (player in team.players) { for (player in team.players) {
if (trySpawn(player.id)) { 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) { if (eaglePoint != null) {
var eagle = new Eagle(team.id); var eagle = new Eagle(team.id);
applyPoint(eagle, eaglePoint); applyPoint(eagle, eaglePoint);
@@ -115,12 +125,13 @@ class Game implements EngineListener {
} }
private function spawn(task:SpawnTask):Void { private function spawn(task:SpawnTask):Void {
L.d(TAG, 'spawn(${task}');
getPlayer(task.playerId).tankId = 0; getPlayer(task.playerId).tankId = 0;
if (trySpawn(task.playerId, true)) { if (trySpawn(task.playerId, true)) {
var tank = buildTank(task.playerId, task.point); var tank = buildTank(task.playerId, task.point);
var player:Player = getPlayer(task.playerId); var player:Player = getPlayer(task.playerId);
engine.spawn(tank);
player.tankId = tank.id; player.tankId = tank.id;
Timer.delay(function() engine.spawn(tank), 1500);
} else if (!isTeamAlive(task.playerId.team)) { } else if (!isTeamAlive(task.playerId.team)) {
state.teams[task.playerId.team].lose = true; state.teams[task.playerId.team].lose = true;
complete(); complete();
@@ -135,31 +146,18 @@ class Game implements EngineListener {
player.control.dispose(); player.control.dispose();
} }
} }
var timer = new Timer(5000); Timer.delay(function() {
timer.run = function() {
timer.stop();
deferred.resolve(state); deferred.resolve(state);
stream.end(); 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 { 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 { public function onCollision(entity:EntityType, with:EntityType):Void {
@@ -210,10 +208,11 @@ class Game implements EngineListener {
public function onDestroy(entity:EntityType):Void { public function onDestroy(entity:EntityType):Void {
switch (entity) { switch (entity) {
case EntityType.TANK(tank): 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); var respawn:Bool = trySpawn(tank.playerId);
if (respawn) { if (respawn) {
spawners.get(tank.playerId.team).push(tank.playerId); spawners[tank.playerId.team].push(tank.playerId);
} }
if (!isTeamAlive(tank.playerId.team)) { if (!isTeamAlive(tank.playerId.team)) {
state.teams[tank.playerId.team].lose = true; state.teams[tank.playerId.team].lose = true;

View File

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

View File

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

View File

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

View File

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

View File

@@ -62,4 +62,10 @@ class LevelMap {
public function getBrick(cell:GridCell):Brick { public function getBrick(cell:GridCell):Brick {
return bricksMap.get(cell.position); 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 colors = require('ansi-colors');
const log = require('fancy-log'); const log = require('fancy-log');
const vfs = require('vinyl-fs'); const vfs = require('vinyl-fs');
const rmdir = require('rmdir');
class Haxe extends Sdk { class Haxe extends Sdk {
@@ -179,13 +180,10 @@ class Haxe extends Sdk {
args.push('-debug'); args.push('-debug');
} }
//console.log('haxelib', args.join(' ')); //console.log('haxelib', args.join(' '));
const target = `${buildDir}/${params.platform}/bin`;
rmdir(target);
this.haxelib(args).then(() => { this.haxelib(args).then(() => {
const result = { vfs.src(`${target}/**/*`).pipe(through.obj((file, enc, cb) => {
'flash': `${buildDir}/flash/bin/*.swf`,
'html5': `${buildDir}/html5/bin/**/*`,
'linux': `${buildDir}/linux/bin/**/*`,
}[params.platform];
vfs.src(result).pipe(through.obj((file, enc, cb) => {
file.debug = debug; file.debug = debug;
stream.push(file); stream.push(file);
cb(); cb();