6 Commits
0.6.2 ... 0.7.0

Author SHA1 Message Date
5b04242aab [version] up to 0.7.0 2018-02-20 00:02:44 +03:00
a127777e49 [common] added GameSave 2018-02-20 00:01:35 +03:00
11aea59cf8 [client] resource images rename 2018-02-18 21:22:46 +03:00
5456a01551 [common] bonus config duration 2018-02-16 22:19:14 +03:00
a93a67161d Merge branch 'develop' of bitbucket.org:infernalgames/tankz into develop 2018-02-16 17:59:22 +03:00
52a4e44e47 [client] added AniamteBundle 2018-02-16 17:57:43 +03:00
58 changed files with 436 additions and 175 deletions

View File

@@ -21,7 +21,7 @@ const generate = () => function generate() {
]); ]);
}; };
const build = (platform) => function build() { const build = (platform, values) => function build() {
const argv = yargs.argv; const argv = yargs.argv;
return gulp.src('.') return gulp.src('.')
.pipe(new Haxe().openfl({ .pipe(new Haxe().openfl({
@@ -29,6 +29,7 @@ const build = (platform) => function build() {
platform: platform, platform: platform,
version: version, version: version,
debug: argv.dev, debug: argv.dev,
values: values
})) }))
.pipe(gulp.dest(`target/${platform}`)); .pipe(gulp.dest(`target/${platform}`));
}; };
@@ -87,6 +88,7 @@ const buildDeb = gulp.series(
exports['client:flash:html'] = gulp.parallel(flashIndex, flashJs); exports['client:flash:html'] = gulp.parallel(flashIndex, flashJs);
exports['client:flash'] = gulp.series(prepare(Haxe.ID), generate(), build('flash'), exports['client:flash:html']); exports['client:flash'] = gulp.series(prepare(Haxe.ID), generate(), build('flash'), exports['client:flash:html']);
exports['client:html5'] = gulp.series(prepare(Haxe.ID), generate(), build('html5')); exports['client:html5'] = gulp.series(prepare(Haxe.ID), generate(), build('html5'));
exports['client:html5-dom'] = gulp.series(prepare(Haxe.ID), generate(), build('html5', {dom:true}));
exports['client:linux'] = gulp.series(prepare(Haxe.ID), generate(), build('linux')); exports['client:linux'] = gulp.series(prepare(Haxe.ID), generate(), build('linux'));
exports['client:webapp'] = webapp; exports['client:webapp'] = webapp;
exports['client:deb'] = buildDeb; exports['client:deb'] = buildDeb;
@@ -108,9 +110,9 @@ const testFlash = function() {
.pipe(debug()); .pipe(debug());
}; };
const testHtml5 = function() { const testHtml5 = (dom) => function() {
return gulp.series( return gulp.series(
build('html5'), build('html5', {dom:dom}),
() => gulp.src('target/html5').pipe(webserver({ () => gulp.src('target/html5').pipe(webserver({
host: 'localhost', port: 3000, host: 'localhost', port: 3000,
open: true, open: true,
@@ -130,7 +132,8 @@ const testLinux = gulp.series(
exports['client:flash:test'] = gulp.series(prepare(Haxe.ID, FlashPlayer.ID), testFlash); exports['client:flash:test'] = gulp.series(prepare(Haxe.ID, FlashPlayer.ID), testFlash);
exports['client:html5:test'] = gulp.series(prepare(Haxe.ID), testHtml5); exports['client:html5:test'] = gulp.series(prepare(Haxe.ID), testHtml5());
exports['client:html5-dom:test'] = gulp.series(prepare(Haxe.ID), testHtml5(true));
exports['client:linux:test'] = gulp.series(prepare(Haxe.ID), testLinux); exports['client:linux:test'] = gulp.series(prepare(Haxe.ID), testLinux);
exports['client:test'] = exports['client:flash:test']; exports['client:test'] = exports['client:flash:test'];

View File

@@ -1,6 +1,6 @@
{ {
"name": "tankz", "name": "tankz",
"version": "0.6.2", "version": "0.7.0",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"ansi-colors": "^1.0.1", "ansi-colors": "^1.0.1",

View File

@@ -19,7 +19,8 @@
<haxeflag name="-D" value="native-trace"/> <haxeflag name="-D" value="native-trace"/>
<!--<haxeflag name="-D" value="proto_debug"/>--> <!--<haxeflag name="-D" value="proto_debug"/>-->
<haxeflag name="-dce" value="no"/> <haxeflag name="-dce" value="no"/>
<haxeflag name="-D" value="dom"/> <!--<haxeflag name="-D" value="dom"/>-->
<haxeflag name="--macro" value="yield.parser.Parser.auto()"/>
<!--<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"> <section if="build_editor">

View File

@@ -6,6 +6,11 @@ import flash.display.Bitmap;
import flash.display.BitmapData; import flash.display.BitmapData;
typedef Frame = {
var image:BitmapData;
var length:Int;
}
class Animate extends Bitmap { class Animate extends Bitmap {
private static var timer:Timer; private static var timer:Timer;
@@ -27,21 +32,29 @@ class Animate extends Bitmap {
} }
public var playing(default, set):Bool; public var playing(default, set):Bool;
public var frames(default, set):Array<BitmapData>; public var frames(default, set):Array<Frame>;
private var sequence:Array<BitmapData>;
private var index:Int; private var index:Int;
public function new(?frames:Array<BitmapData>) { public function new(?frames:Array<Frame>) {
super(null, PixelSnapping.AUTO, true); super(null, PixelSnapping.AUTO, true);
this.frames = frames == null ? [] : frames; this.frames = frames == null ? [] : frames;
init(); init();
instances.push(this); instances.push(this);
} }
public function set_frames(value:Array<BitmapData>):Array<BitmapData> { public function set_frames(value:Array<Frame>):Array<Frame> {
sequence = [];
index = 0;
if (value != null) { if (value != null) {
frames = value; frames = value;
bitmapData = frames[0]; for (frame in frames) {
index = 0; sequence = sequence.concat([for (i in 0...frame.length) frame.image]);
}
bitmapData = sequence[0];
} else {
bitmapData = null;
} }
return frames; return frames;
} }
@@ -54,10 +67,10 @@ class Animate extends Bitmap {
} }
private function update():Void { private function update():Void {
if (++index >= frames.length) { if (++index >= sequence.length) {
index = 0; index = 0;
} }
var nextBitmapData = frames[index]; var nextBitmapData = sequence[index];
x -= (nextBitmapData.width - bitmapData.width) / 2; x -= (nextBitmapData.width - bitmapData.width) / 2;
y -= (nextBitmapData.height - bitmapData.height) / 2; y -= (nextBitmapData.height - bitmapData.height) / 2;
bitmapData = nextBitmapData; bitmapData = nextBitmapData;

View File

@@ -1,7 +1,6 @@
package ru.m.animate; package ru.m.animate;
import promhx.Deferred; import promhx.Deferred;
import flash.display.BitmapData;
import promhx.Promise; import promhx.Promise;
@@ -9,10 +8,6 @@ class OnceAnimate extends Animate {
private var deferred:Deferred<Animate>; private var deferred:Deferred<Animate>;
public function new(frames:Array<BitmapData>) {
super(frames);
}
public function play():Promise<Animate> { public function play():Promise<Animate> {
deferred = new Deferred(); deferred = new Deferred();
playing = true; playing = true;

View File

@@ -13,11 +13,16 @@ import haxework.provider.Provider;
import haxework.resources.IResources; import haxework.resources.IResources;
import haxework.resources.Resources; import haxework.resources.Resources;
import ru.m.connect.IConnection; import ru.m.connect.IConnection;
import ru.m.tankz.bundle.ConfigBundle;
import ru.m.tankz.bundle.IConfigBundle;
import ru.m.tankz.bundle.ILevelBundle;
import ru.m.tankz.bundle.LevelBundle;
import ru.m.tankz.frame.StartFrame; import ru.m.tankz.frame.StartFrame;
import ru.m.tankz.game.ClassicGame; import ru.m.tankz.game.ClassicGame;
import ru.m.tankz.game.DotaGame; import ru.m.tankz.game.DotaGame;
import ru.m.tankz.game.Game; import ru.m.tankz.game.Game;
import ru.m.tankz.PacketBuilder; import ru.m.tankz.PacketBuilder;
import ru.m.tankz.storage.SaveStorage;
#if flash import haxework.log.JSLogger; #end #if flash import haxework.log.JSLogger; #end
#if debug import haxework.log.SocketLogger; #end #if debug import haxework.log.SocketLogger; #end
@@ -71,7 +76,6 @@ class Client implements IConnectionHandler {
Root.bind(view); Root.bind(view);
view.content.stage.stageFocusRect = false; view.content.stage.stageFocusRect = false;
//view.logout.onPress = this; //view.logout.onPress = this;
view.switcher.change(StartFrame.ID);
view.content.stage.addEventListener(KeyboardEvent.KEY_UP, function(event:KeyboardEvent):Void { view.content.stage.addEventListener(KeyboardEvent.KEY_UP, function(event:KeyboardEvent):Void {
if (event.keyCode == Keyboard.ESCAPE) { if (event.keyCode == Keyboard.ESCAPE) {
@@ -79,8 +83,13 @@ class Client implements IConnectionHandler {
} }
}); });
Provider.setFactory(IConfigBundle, ConfigBundle);
Provider.setFactory(ILevelBundle, LevelBundle);
Provider.setFactory(SaveStorage, SaveStorage);
Provider.setFactory(Game, ClassicGame, ClassicGame.TYPE); Provider.setFactory(Game, ClassicGame, ClassicGame.TYPE);
Provider.setFactory(Game, DotaGame, DotaGame.TYPE); Provider.setFactory(Game, DotaGame, DotaGame.TYPE);
view.switcher.change(StartFrame.ID);
} }
public function onPress(view:ButtonView):Void { public function onPress(view:ButtonView):Void {

View File

@@ -9,7 +9,7 @@ views:
pHeight: 100 pHeight: 100
skin: skin:
$type: haxework.gui.skin.BitmapSkin $type: haxework.gui.skin.BitmapSkin
image: "@asset:image:resources/images/background.png" image: "@asset:image:resources/image/ui/background.png"
fillType: REPEAT fillType: REPEAT
views: views:
- id: start - id: start

View File

@@ -4,9 +4,9 @@ button:
height: 60 height: 60
skin: skin:
$type: haxework.gui.skin.ButtonBitmapSkin $type: haxework.gui.skin.ButtonBitmapSkin
upImage: "@asset:image:resources/images/control/button_normal.png" upImage: "@asset:image:resources/image/ui/button/normal.png"
downImage: "@asset:image:resources/images/control/button_down.png" downImage: "@asset:image:resources/image/ui/button/down.png"
overImage: "@asset:image:resources/images/control/button_over.png" overImage: "@asset:image:resources/image/ui/button/over.png"
fillType: NINEPATH fillType: NINEPATH
fontFamily: "@res:text:font" fontFamily: "@res:text:font"
fontEmbed: true fontEmbed: true

View File

@@ -1,9 +1,10 @@
package ru.m.tankz.config; package ru.m.tankz.bundle;
import yaml.Parser;
import openfl.Assets; import openfl.Assets;
import yaml.Yaml;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
import ru.m.tankz.Type;
import yaml.Parser;
import yaml.Yaml;
typedef ConfigSource = { typedef ConfigSource = {
@@ -16,13 +17,13 @@ typedef ConfigSource = {
var bonuses: Array<BonusConfig>; var bonuses: Array<BonusConfig>;
} }
class ConfigBundle { class ConfigBundle implements IConfigBundle {
private static function convert(raw:Dynamic):ConfigSource { private static function convert(raw:Dynamic):ConfigSource {
return raw; return raw;
} }
public static function get(type:String):Config { public function get(type:GameType):Config {
var source = convert(Yaml.parse(Assets.getText('resources/${type}/config.yaml'), Parser.options().useObjects())); var source = convert(Yaml.parse(Assets.getText('resources/${type}/config.yaml'), Parser.options().useObjects()));
return new Config(type, source.game, source.map, source.bricks, source.presets, source.points, source.tanks, source.bonuses); return new Config(type, source.game, source.map, source.bricks, source.presets, source.points, source.tanks, source.bonuses);
} }

View File

@@ -0,0 +1,15 @@
package ru.m.tankz.bundle;
import openfl.Assets;
import ru.m.tankz.config.Config;
import ru.m.tankz.Type;
import ru.m.tankz.util.LevelUtil;
class LevelBundle implements ILevelBundle {
public function get(type:GameType, config:Config, level:Int):LevelConfig {
var data:String = Assets.getText('resources/${type}/levels/level${LevelUtil.formatLevel(level)}.txt');
return LevelUtil.loads(config, data);
}
}

View File

@@ -11,9 +11,11 @@ import haxework.provider.Provider;
import protohx.Message; import protohx.Message;
import ru.m.connect.IConnection; import ru.m.connect.IConnection;
import ru.m.tankz.game.Game; import ru.m.tankz.game.Game;
import ru.m.tankz.game.GameSave;
import ru.m.tankz.game.GameState; import ru.m.tankz.game.GameState;
import ru.m.tankz.proto.pack.GameUpdateResponse; import ru.m.tankz.proto.pack.GameUpdateResponse;
import ru.m.tankz.render.Render; import ru.m.tankz.render.Render;
import ru.m.tankz.storage.SaveStorage;
interface GameFrameLayout { interface GameFrameLayout {
@@ -34,16 +36,16 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand
public function init():Void {} public function init():Void {}
public function onShow():Void { public function onShow():Void {
start(Provider.get(GameState)); start(Provider.get(GameSave));
} }
private function start(s:GameState):Void { private function start(save:GameSave):Void {
game = Provider.build(Game, s.type); game = Provider.build(Game, save.state.type);
if (game == null) { if (game == null) {
throw 'Unsupported game type "${s.type}"'; throw 'Unsupported game type "${save.state.type}"';
} }
game.engine.listeners.push(render); game.engine.listeners.push(render);
game.start(s).then(onGameStateChange).endThen(onGameComplete); game.start(save).then(onGameStateChange).endThen(onGameComplete);
content.addEventListener(Event.ENTER_FRAME, redraw); content.addEventListener(Event.ENTER_FRAME, redraw);
//Provider.get(IConnection).packetHandler.addListener(this); //Provider.get(IConnection).packetHandler.addListener(this);
render.draw(game.engine); render.draw(game.engine);
@@ -67,6 +69,9 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand
} }
private function stateString(game:Game):String { private function stateString(game:Game):String {
if (game.state == null) {
return '';
}
var result:Array<String> = []; var result:Array<String> = [];
result.push('Level: ${game.state.level}'); result.push('Level: ${game.state.level}');
for (team in game.teams) { for (team in game.teams) {
@@ -98,8 +103,10 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand
} }
switch (game.next()) { switch (game.next()) {
case Option.Some(s): case Option.Some(s):
var save = game.save();
Provider.get(SaveStorage).write(save);
stop(); stop();
start(s); start(save);
case Option.None: case Option.None:
Provider.get(IFrameSwitcher).change(StartFrame.ID); Provider.get(IFrameSwitcher).change(StartFrame.ID);
} }

View File

@@ -1,12 +1,13 @@
package ru.m.tankz.frame; package ru.m.tankz.frame;
import ru.m.tankz.config.ConfigBundle; import ru.m.tankz.game.GameSave;
import haxework.gui.frame.IFrameSwitcher; import haxework.gui.frame.IFrameSwitcher;
import ru.m.tankz.game.GameState;
import haxework.provider.Provider;
import haxework.gui.list.ListView; import haxework.gui.list.ListView;
import haxework.gui.ViewBuilder;
import haxework.gui.VGroupView; import haxework.gui.VGroupView;
import haxework.gui.ViewBuilder;
import haxework.provider.Provider;
import ru.m.tankz.bundle.IConfigBundle;
import ru.m.tankz.game.GameState;
interface LevelFrameLayout { interface LevelFrameLayout {
@@ -23,13 +24,13 @@ class LevelFrame extends VGroupView implements ViewBuilder implements LevelFrame
} }
public function onShow():Void { public function onShow():Void {
var state = Provider.get(GameState); var state:GameState = Provider.get(GameSave).state;
var c = ConfigBundle.get(state.type).game.levels; var c = Provider.get(IConfigBundle).get(state.type).game.levels;
levels.data = [for (i in 0...c) i]; levels.data = [for (i in 0...c) i];
} }
public function onListItemClick(item:IListItemView<Int>):Void { public function onListItemClick(item:IListItemView<Int>):Void {
Provider.get(GameState).level = item.data; Provider.get(GameSave).state.level = item.data;
Provider.get(IFrameSwitcher).change(GameFrame.ID); Provider.get(IFrameSwitcher).change(GameFrame.ID);
} }
} }

View File

@@ -1,5 +1,7 @@
package ru.m.tankz.frame; package ru.m.tankz.frame;
import ru.m.tankz.storage.SaveStorage;
import ru.m.tankz.game.GameSave;
import haxework.gui.ButtonView; import haxework.gui.ButtonView;
import haxework.gui.frame.IFrameSwitcher; import haxework.gui.frame.IFrameSwitcher;
import haxework.gui.VGroupView; import haxework.gui.VGroupView;
@@ -14,6 +16,7 @@ import ru.m.tankz.Type;
interface StartFrameLayout { interface StartFrameLayout {
var classic_1p(default, null):ButtonView; var classic_1p(default, null):ButtonView;
var classic_2p(default, null):ButtonView; var classic_2p(default, null):ButtonView;
var classic_load(default, null):ButtonView;
var dota_1p(default, null):ButtonView; var dota_1p(default, null):ButtonView;
var dota_2p_coop(default, null):ButtonView; var dota_2p_coop(default, null):ButtonView;
var dota_2p_vs(default, null):ButtonView; var dota_2p_vs(default, null):ButtonView;
@@ -24,20 +27,27 @@ 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():Void {
classic_1p.onPress = this; classic_1p.onPress = this;
classic_2p.onPress = this; classic_2p.onPress = this;
classic_load.onPress = this;
dota_1p.onPress = this; dota_1p.onPress = this;
dota_2p_coop.onPress = this; dota_2p_coop.onPress = this;
dota_2p_vs.onPress = this; dota_2p_vs.onPress = this;
} }
public function onShow():Void {
classic_load.visible = Provider.get(SaveStorage).read(ClassicGame.TYPE) != null;
}
public function onPress(view:ButtonView):Void { public function onPress(view:ButtonView):Void {
switch (view.id) { switch (view.id) {
case 'classic_1p': case 'classic_1p':
startGame(ClassicGame.TYPE, ClassicGame.PLAYER1); startGame(ClassicGame.TYPE, ClassicGame.PLAYER1);
case 'classic_2p': case 'classic_2p':
startGame(ClassicGame.TYPE, ClassicGame.PLAYER2); startGame(ClassicGame.TYPE, ClassicGame.PLAYER2);
case 'classic_load':
loadGame(ClassicGame.TYPE);
case 'dota_1p': case 'dota_1p':
startGame(DotaGame.TYPE, DotaGame.PLAYER1); startGame(DotaGame.TYPE, DotaGame.PLAYER1);
case 'dota_2p_coop': case 'dota_2p_coop':
@@ -48,7 +58,13 @@ class StartFrame extends VGroupView implements ViewBuilder implements StartFrame
} }
private function startGame(type:GameType, presetId:PresetId):Void { private function startGame(type:GameType, presetId:PresetId):Void {
Provider.set(GameState, new GameState(type, presetId)); Provider.set(GameSave, new GameSave({type: type, presetId: presetId}));
Provider.get(IFrameSwitcher).change(LevelFrame.ID); Provider.get(IFrameSwitcher).change(LevelFrame.ID);
} }
private function loadGame(type:GameType):Void {
var save:GameSave = Provider.get(SaveStorage).read(type);
Provider.set(GameSave, save);
Provider.get(IFrameSwitcher).change(GameFrame.ID);
}
} }

View File

@@ -3,7 +3,7 @@ pWidth: 100
pHeight: 100 pHeight: 100
views: views:
- $type: haxework.gui.ImageView - $type: haxework.gui.ImageView
image: "@asset:image:resources/images/logo/logo.png" image: "@asset:image:resources/image/ui/logo.png"
contentSize: true contentSize: true
bottomMargin: 15 bottomMargin: 15
- $type: haxework.gui.LabelView - $type: haxework.gui.LabelView
@@ -20,6 +20,10 @@ views:
$type: haxework.gui.ButtonView $type: haxework.gui.ButtonView
text: 2 Player text: 2 Player
$style: button $style: button
- id: classic_load
$type: haxework.gui.ButtonView
text: Load
$style: button
- $type: haxework.gui.LabelView - $type: haxework.gui.LabelView
$style: label $style: label
fontSize: 20 fontSize: 20

View File

@@ -0,0 +1,71 @@
package ru.m.tankz.render;
import ru.m.draw.BitmapUtil;
import ru.m.draw.Color;
import flash.display.BitmapData;
import openfl.Assets;
import ru.m.animate.Animate;
import ru.m.animate.OnceAnimate;
import ru.m.tankz.core.Tank;
import ru.m.tankz.Type;
class AnimateBundle {
private static function buildAnimate(name:String, sequence:Array<Int>):OnceAnimate {
var template:String = 'resources/image/animate/${name}/%index%.png';
var frames = [for (i in sequence) ({
image: Assets.getBitmapData(StringTools.replace(template, '%index%', Std.string(i))),
length: 1
})];
return new OnceAnimate(frames);
}
public static function tankSpawn():OnceAnimate {
return buildAnimate('appear', [
0, 1, 2, 3, 3, 4, 5, 5, 6
]);
}
public static function tankBoom():OnceAnimate {
return buildAnimate('boom', [
0, 1, 2, 3, 4, 4, 4, 1, 4, 4, 7, 7, 8, 9, 9
]);
}
public static function bulletBoom():OnceAnimate {
return buildAnimate('boom-sm', [
0, 1, 1, 0, 0, 1
]);
}
public static function tankProtect():Animate {
return new Animate([for (i in 0...2) ({
image: Assets.getBitmapData('resources/image/animate/protect/${i}.png'),
length: 5
})]);
}
public static function tankFrames(tank:Tank):Array<Frame> {
var color:Color = switch (tank.hits) {
case 1: 0x869C43;
case 2: 0xDEAF80;
case 3: 0x5EA67A;
case _: tank.color;
}
var colors:Array<Color> = [color, color];
if (tank.bonus) {
colors[1] = 0xff00aa;
}
return [for (i in 0...2) ({
image: BitmapUtil.colorize(Assets.getBitmapData('resources/image/tank/${tank.config.skin}-${i}.png'), colors[i]),
length: 3
})];
}
public static function bonusFrames(type:BonusType):Array<Frame> {
var image = Assets.getBitmapData('resources/image/bonus/${type}.png');
var empty = new BitmapData(image.width, image.height, true, 0x00000000);
return [{image: image, length: 15}, {image: empty, length: 15}];
}
}

View File

@@ -1,15 +1,15 @@
package ru.m.tankz.render; package ru.m.tankz.render;
import ru.m.geom.Point;
import openfl.Assets;
import ru.m.animate.OnceAnimate;
import flash.display.DisplayObjectContainer; import flash.display.DisplayObjectContainer;
import ru.m.tankz.core.EntityType;
import ru.m.tankz.render.RenderItem;
import ru.m.tankz.engine.Engine;
import flash.display.Sprite;
import flash.display.Graphics; import flash.display.Graphics;
import flash.display.Sprite;
import haxework.gui.SpriteView; import haxework.gui.SpriteView;
import ru.m.animate.Animate;
import ru.m.animate.OnceAnimate;
import ru.m.geom.Point;
import ru.m.tankz.core.EntityType;
import ru.m.tankz.engine.Engine;
import ru.m.tankz.render.RenderItem;
class Render extends SpriteView implements EngineListener { class Render extends SpriteView implements EngineListener {
@@ -105,7 +105,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); playAnimate(tank.rect.center, AnimateBundle.tankSpawn());
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);
@@ -119,16 +119,20 @@ class Render extends SpriteView implements EngineListener {
case EntityType.BONUS(bonus): case EntityType.BONUS(bonus):
var item = new BonusItem(bonus); var item = new BonusItem(bonus);
items.set(bonus.key, item); items.set(bonus.key, item);
entryLayer.addChild(item.view); upperLayer.addChild(item.view);
item.update(); item.update();
case _: case _:
} }
} }
public function onChange(entity:EntityType, ?change:EntityChange):Void {}
public function onCollision(entity:EntityType, with:EntityType):Void { public function onCollision(entity:EntityType, with:EntityType):Void {
switch [entity, with] { switch [entity, with] {
case [EntityType.BULLET(_), EntityType.EAGLE(eagle)]: case [EntityType.BULLET(_), EntityType.EAGLE(eagle)]:
playTankBoom(eagle.rect.center); if (eagle.death) {
playAnimate(eagle.rect.center, AnimateBundle.tankBoom());
}
case _: case _:
} }
} }
@@ -139,63 +143,29 @@ class Render extends SpriteView implements EngineListener {
if (items.exists(tank.key)) { if (items.exists(tank.key)) {
entryLayer.removeChild(items.get(tank.key).view); entryLayer.removeChild(items.get(tank.key).view);
items.remove(tank.key); items.remove(tank.key);
playTankBoom(tank.rect.center); playAnimate(tank.rect.center, AnimateBundle.tankBoom());
} }
case EntityType.BULLET(bullet): case EntityType.BULLET(bullet):
if (items.exists(bullet.key)) { if (items.exists(bullet.key)) {
entryLayer.removeChild(items.get(bullet.key).view); entryLayer.removeChild(items.get(bullet.key).view);
items.remove(bullet.key); 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))); var point = bullet.rect.center.add(new Point(bullet.rect.width * bullet.rect.direction.x, bullet.rect.height * bullet.rect.direction.y));
playAnimate(point, AnimateBundle.bulletBoom());
} }
case EntityType.BONUS(bonus): case EntityType.BONUS(bonus):
if (items.exists(bonus.key)) { if (items.exists(bonus.key)) {
entryLayer.removeChild(items.get(bonus.key).view); upperLayer.removeChild(items.get(bonus.key).view);
items.remove(bonus.key); items.remove(bonus.key);
} }
case _: case _:
} }
} }
private function playTankSpawn(point:Point):Void { private function playAnimate(point:Point, animate:OnceAnimate):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.x = point.x - animate.width / 2;
animate.y = point.y - animate.height / 2; animate.y = point.y - animate.height / 2;
upperLayer.addChild(animate); upperLayer.addChild(animate);
animate.play().then(function(animate) { animate.play().then(function(animate:Animate):Void {
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); upperLayer.removeChild(animate);
animate.dispose(); animate.dispose();
}); });

View File

@@ -1,22 +1,19 @@
package ru.m.tankz.render; package ru.m.tankz.render;
import ru.m.tankz.Type.BrickType;
import openfl.display.BitmapData;
import ru.m.draw.Color;
import ru.m.tankz.core.Bonus;
import flash.display.Bitmap; import flash.display.Bitmap;
import flash.display.DisplayObject; import flash.display.DisplayObject;
import flash.display.Shape; import flash.display.Shape;
import flash.display.Sprite; import flash.display.Sprite;
import openfl.Assets; import openfl.Assets;
import ru.m.animate.Animate; import ru.m.animate.Animate;
import ru.m.draw.BitmapUtil;
import ru.m.geom.Direction; import ru.m.geom.Direction;
import ru.m.geom.Rectangle; import ru.m.geom.Rectangle;
import ru.m.tankz.core.Bonus;
import ru.m.tankz.core.Bullet; import ru.m.tankz.core.Bullet;
import ru.m.tankz.core.Eagle; import ru.m.tankz.core.Eagle;
import ru.m.tankz.core.Tank; import ru.m.tankz.core.Tank;
import ru.m.tankz.map.Brick; import ru.m.tankz.map.Brick;
import ru.m.tankz.Type.BrickType;
typedef TRectangle = { typedef TRectangle = {
@@ -96,8 +93,6 @@ class BrickItem extends BitmapItem<Brick> {
} }
} }
override private function getImage():String { override private function getImage():String {
return 'resources/image/map/${value.config.type}.png'; return 'resources/image/map/${value.config.type}.png';
} }
@@ -112,9 +107,10 @@ class BrickAnimateItem extends AnimateItem<Brick> {
} }
override public function redraw():Void { override public function redraw():Void {
var frame0 = Assets.getBitmapData('resources/image/map/${value.config.type}-0.png'); view.frames = [for (i in 0...2) ({
var frame1 = Assets.getBitmapData('resources/image/map/${value.config.type}-1.png'); image: Assets.getBitmapData('resources/image/map/${value.config.type}-${i}.png'),
view.frames = [for (i in 0...15) frame0].concat([for (i in 0...15) frame1]); length: 15
})];
view.playing = true; view.playing = true;
} }
} }
@@ -193,25 +189,17 @@ class TankItem extends RenderItem<Tank, Sprite> {
view = new Sprite(); view = new Sprite();
tankView = new Animate(); tankView = new Animate();
view.addChild(tankView); view.addChild(tankView);
protectView = new Animate( protectView = AnimateBundle.tankProtect();
[for (i in 0...5) Assets.getBitmapData('resources/images/tank/protect/protect-0.png')].concat(
[for (i in 0...5) Assets.getBitmapData('resources/images/tank/protect/protect-1.png')])
);
protectView.visible = false; protectView.visible = false;
view.addChild(protectView); view.addChild(protectView);
redraw(); redraw();
} }
override public function redraw():Void { override public function redraw():Void {
var colors:Array<Color> = [value.color, value.bonus ? 0xff00aa : value.color]; tankView.frames = AnimateBundle.tankFrames(value);
var frames = [for (frame in getFrames()) BitmapUtil.colorize(Assets.getBitmapData(frame), colors.shift())];
tankView.frames = [
frames[0], frames[0], frames[0],
frames[1], frames[1], frames[1],
];
if (value.protect.active) { if (value.protect.active) {
protectView.x = (tankView.frames[0].width - protectView.frames[0].width) / 2; protectView.x = (tankView.frames[0].image.width - protectView.frames[0].image.width) / 2;
protectView.y = (tankView.frames[0].height - protectView.frames[0].height) / 2; protectView.y = (tankView.frames[0].image.height - protectView.frames[0].image.height) / 2;
protectView.playing = true; protectView.playing = true;
protectView.visible = true; protectView.visible = true;
} else { } else {
@@ -254,7 +242,9 @@ class TankItem extends RenderItem<Tank, Sprite> {
class BulletItem extends BitmapItem<Bullet> { class BulletItem extends BitmapItem<Bullet> {
override private function getImage():String { override private function getImage():String {
return 'resources/images/bullet/bullet_${value.config.piercing > 1 ? 1 : 0}.png'; var type:String = 'normal';
if (value.config.piercing > 0) type = 'piercing';
return 'resources/image/bullet/${type}.png';
} }
} }
@@ -262,18 +252,22 @@ class BulletItem extends BitmapItem<Bullet> {
class EagleItem extends BitmapItem<Eagle> { class EagleItem extends BitmapItem<Eagle> {
private var death:Bool; private var death:Bool;
private var protected:Bool;
override public function update():Void { override public function update():Void {
super.update(); super.update();
var d = value.death; var d = value.death;
if (d != death) { var p = value.protect.active;
if (d != death || p != protected) {
death = d; death = d;
protected = p;
redraw(); redraw();
} }
} }
override private function getImage():String { override private function getImage():String {
return 'resources/image/eagle/eagle${value.death ? '-death' : ''}.png'; var suffix = value.death ? '-death' : value.protect.active ? '-protected' : '';
return 'resources/image/eagle/eagle${suffix}.png';
} }
} }
@@ -286,9 +280,8 @@ class BonusItem extends AnimateItem<Bonus> {
} }
override public function redraw():Void { override public function redraw():Void {
var image = Assets.getBitmapData('resources/image/bonus/${value.bonusType}.png'); var image = Assets.getBitmapData('resources/image/bonus/${value.config.type}.png');
var empty = new BitmapData(image.width, image.height, true, 0x00000000); view.frames = AnimateBundle.bonusFrames(value.config.type);
view.frames = [for (i in 0...15) image].concat([for (i in 0...15) empty]);
view.playing = true; view.playing = true;
} }
} }

View File

@@ -0,0 +1,33 @@
package ru.m.tankz.storage;
import flash.net.SharedObject;
import ru.m.tankz.Type;
import ru.m.tankz.game.GameSave;
class SaveStorage {
private static var TAG(default, never):String = 'SaveStorage';
private var so:SharedObject;
public function new() {
so = SharedObject.getLocal('tankz');
}
public function read(type:GameType):Null<GameSave> {
var data:String = Reflect.getProperty(so.data, type);
L.d(TAG, 'read: ${data}');
if (data != null) {
return GameSave.fromYaml(data);
}
return null;
}
public function write(save:GameSave):Void {
var data:String = save.toYaml();
L.d(TAG, 'write: ${data}');
so.setProperty(save.state.type, data);
so.flush();
}
}

View File

@@ -25,6 +25,7 @@ player:
- {type: human0, rate: 1, protect: 5} - {type: human0, rate: 1, protect: 5}
bot: &bot bot: &bot
control: bot control: bot
color: 0xFFFFFF
life: -1 life: -1
tanks: tanks:
- {type: bot0, rate: 0.25, bonus: 0.25} - {type: bot0, rate: 0.25, bonus: 0.25}
@@ -173,9 +174,9 @@ tanks:
skin: bd skin: bd
bonuses: bonuses:
- {type: clock} - {type: clock, duration: 10}
- {type: grenade} - {type: grenade}
- {type: helmet} - {type: helmet, duration: 20}
- {type: life} - {type: life}
- {type: shovel} - {type: shovel, duration: 10}
- {type: star} - {type: star}

View File

@@ -136,9 +136,9 @@ tanks:
skin: bb skin: bb
bonuses: bonuses:
- {type: clock} - {type: clock, duration: 10}
- {type: grenade} - {type: grenade}
- {type: helmet} - {type: helmet, duration: 20}
- {type: life} - {type: life}
- {type: shovel} - {type: shovel, duration: 10}
- {type: star} - {type: star}

View File

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 110 B

View File

Before

Width:  |  Height:  |  Size: 141 B

After

Width:  |  Height:  |  Size: 141 B

View File

Before

Width:  |  Height:  |  Size: 144 B

After

Width:  |  Height:  |  Size: 144 B

View File

Before

Width:  |  Height:  |  Size: 186 B

After

Width:  |  Height:  |  Size: 186 B

View File

Before

Width:  |  Height:  |  Size: 144 B

After

Width:  |  Height:  |  Size: 144 B

View File

Before

Width:  |  Height:  |  Size: 186 B

After

Width:  |  Height:  |  Size: 186 B

View File

Before

Width:  |  Height:  |  Size: 144 B

After

Width:  |  Height:  |  Size: 144 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 292 B

After

Width:  |  Height:  |  Size: 292 B

View File

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 386 B

View File

Before

Width:  |  Height:  |  Size: 389 B

After

Width:  |  Height:  |  Size: 389 B

View File

Before

Width:  |  Height:  |  Size: 375 B

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,9 @@
package ru.m.tankz.bundle;
import ru.m.tankz.config.Config;
import ru.m.tankz.Type;
interface IConfigBundle {
public function get(type:GameType):Config;
}

View File

@@ -0,0 +1,9 @@
package ru.m.tankz.bundle;
import ru.m.tankz.config.Config;
import ru.m.tankz.Type;
interface ILevelBundle {
public function get(type:GameType, config:Config, level:Int):LevelConfig;
}

View File

@@ -53,6 +53,7 @@ typedef TankConfig = {
typedef BonusConfig = { typedef BonusConfig = {
var type:BonusType; var type:BonusType;
@:optional var duration:Int;
} }
typedef TankSpawn = { typedef TankSpawn = {

View File

@@ -1,15 +1,15 @@
package ru.m.tankz.core; package ru.m.tankz.core;
import ru.m.tankz.config.Config;
import ru.m.geom.Rectangle; import ru.m.geom.Rectangle;
import ru.m.tankz.Type;
class Bonus extends Entity { class Bonus extends Entity {
public var bonusType(default, null):BonusType; public var config(default, null):BonusConfig;
public function new(bonusType:BonusType) { public function new(config:BonusConfig) {
super(new Rectangle(0, 0, 44, 44)); super(new Rectangle(0, 0, 44, 44));
this.bonusType = bonusType; this.config = config;
} }
} }

View File

@@ -8,10 +8,12 @@ class Eagle extends Entity {
public var team(default, null):TeamId; public var team(default, null):TeamId;
public var death(default, default):Bool; public var death(default, default):Bool;
public var protect(default, null):Modificator;
public function new(team:TeamId) { public function new(team:TeamId) {
super(new Rectangle(0, 0, 44, 44)); super(new Rectangle(0, 0, 44, 44)); // ToDo: hardcode size
this.team = team; this.team = team;
this.death = false; this.death = false;
this.protect = new Modificator();
} }
} }

View File

@@ -1,7 +1,7 @@
package ru.m.tankz.engine; package ru.m.tankz.engine;
import ru.m.geom.Point;
import ru.m.geom.Line; import ru.m.geom.Line;
import ru.m.geom.Point;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;
import ru.m.tankz.core.Bullet; import ru.m.tankz.core.Bullet;
@@ -12,8 +12,15 @@ import ru.m.tankz.core.Tank;
import ru.m.tankz.map.LevelMap; import ru.m.tankz.map.LevelMap;
enum EntityChange {
DEATH;
HIT;
}
interface EngineListener { interface EngineListener {
public function onSpawn(entity:EntityType):Void; public function onSpawn(entity:EntityType):Void;
public function onChange(entity:EntityType, ?change:EntityChange):Void;
public function onCollision(entity:EntityType, with:EntityType):Void; public function onCollision(entity:EntityType, with:EntityType):Void;
public function onDestroy(entity:EntityType):Void; public function onDestroy(entity:EntityType):Void;
} }
@@ -28,6 +35,8 @@ class CollisionProcessor implements EngineListener {
public function onSpawn(entity:EntityType):Void {} public function onSpawn(entity:EntityType):Void {}
public function onChange(entity:EntityType, ?change:EntityChange):Void {}
public function onCollision(entity:EntityType, with:EntityType):Void { public function onCollision(entity:EntityType, with:EntityType):Void {
switch [entity, with] { switch [entity, with] {
case [EntityType.TANK(tank), EntityType.TANK(other_tank)]: case [EntityType.TANK(tank), EntityType.TANK(other_tank)]:
@@ -44,6 +53,7 @@ class CollisionProcessor implements EngineListener {
if (!tank.protect.active) { if (!tank.protect.active) {
if (tank.hits > 0) { if (tank.hits > 0) {
tank.hits--; tank.hits--;
engine.change(tank, EntityChange.HIT);
} else { } else {
engine.destroy(tank); engine.destroy(tank);
} }
@@ -57,7 +67,10 @@ class CollisionProcessor implements EngineListener {
engine.destroy(bullet); engine.destroy(bullet);
case [EntityType.BULLET(bullet), EntityType.EAGLE(eagle)]: case [EntityType.BULLET(bullet), EntityType.EAGLE(eagle)]:
engine.destroy(bullet); engine.destroy(bullet);
if (!eagle.protect.active) {
eagle.death = true; eagle.death = true;
engine.change(eagle, EntityChange.DEATH);
}
case _: case _:
} }
} }
@@ -65,8 +78,7 @@ class CollisionProcessor implements EngineListener {
public function onDestroy(entity:EntityType):Void { } public function onDestroy(entity:EntityType):Void { }
} }
@:build(yield.parser.Parser.run()) @:yield
//@:yield //ToDo: not working
class Engine implements ControlHandler { class Engine implements ControlHandler {
public var config(default, default):Config; public var config(default, default):Config;
@@ -95,6 +107,11 @@ class Engine implements ControlHandler {
for (l in listeners) l.onSpawn(type); for (l in listeners) l.onSpawn(type);
} }
public function change(entity:Entity, ?change:EntityChange):Void {
var type = EntityTypeResolver.of(entity);
for (l in listeners) l.onChange(type, change);
}
public function destroy(entity:Entity):Void { public function destroy(entity:Entity):Void {
if (entities.exists(entity.id)) { if (entities.exists(entity.id)) {
var type = EntityTypeResolver.of(entity); var type = EntityTypeResolver.of(entity);

View File

@@ -1,15 +1,17 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import ru.m.tankz.game.GameSave.PlayerSave;
import haxe.ds.Option; import haxe.ds.Option;
import haxe.Timer; import haxe.Timer;
import haxework.provider.Provider;
import promhx.Deferred; import promhx.Deferred;
import promhx.Stream; import promhx.Stream;
import ru.m.geom.Direction; import ru.m.geom.Direction;
import ru.m.geom.Point; import ru.m.geom.Point;
import ru.m.tankz.bot.BotControl; import ru.m.tankz.bot.BotControl;
import ru.m.tankz.bundle.IConfigBundle;
import ru.m.tankz.bundle.ILevelBundle;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
import ru.m.tankz.config.ConfigBundle;
import ru.m.tankz.config.LevelBundle;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;
import ru.m.tankz.control.HumanControl; import ru.m.tankz.control.HumanControl;
import ru.m.tankz.core.Bonus; import ru.m.tankz.core.Bonus;
@@ -40,7 +42,7 @@ class Game implements EngineListener {
public function new(type:GameType) { public function new(type:GameType) {
this.type = type; this.type = type;
this.config = ConfigBundle.get(type); this.config = Provider.get(IConfigBundle).get(type);
this.engine = new Engine(config); this.engine = new Engine(config);
engine.listeners.push(this); engine.listeners.push(this);
} }
@@ -73,11 +75,11 @@ class Game implements EngineListener {
entity.rect.direction = Direction.fromString(point.direction); entity.rect.direction = Direction.fromString(point.direction);
} }
public function start(state:GameState):Stream<GameState> { public function start(save:GameSave):Stream<GameState> {
this.state = state; this.state = save.state;
this.preset = config.getPreset(state.presetId); this.preset = config.getPreset(state.presetId);
this.deferred = new Deferred(); this.deferred = new Deferred();
var level = LevelBundle.get(type, config, state.level); var level:LevelConfig = Provider.get(ILevelBundle).get(type, config, state.level);
points = level.points != null ? level.points : config.points; points = level.points != null ? level.points : config.points;
engine.map.setData(level.data); engine.map.setData(level.data);
teams = new Map<TeamId, Team>(); teams = new Map<TeamId, Team>();
@@ -87,6 +89,16 @@ class Game implements EngineListener {
var team:Team = new Team(teamConfig, teamPoints); var team:Team = new Team(teamConfig, teamPoints);
teams[team.id] = team; teams[team.id] = team;
for (player in team.players.iterator()) { for (player in team.players.iterator()) {
var playerSave:PlayerSave = save.getPlayer(player.id);
if (playerSave != null) {
player.life = playerSave.life;
if (playerSave.tank != null) {
player.config.tanks = [{
type: playerSave.tank,
rate: 1,
}];
}
}
if (player.config.control != null) { if (player.config.control != null) {
var control = switch (player.config.control) { var control = switch (player.config.control) {
case Control.HUMAN: new HumanControl(player.id, humanControlIndex++); case Control.HUMAN: new HumanControl(player.id, humanControlIndex++);
@@ -113,6 +125,7 @@ class Game implements EngineListener {
var eaglePoint = team.spawner.getPoint('eagle'); var eaglePoint = team.spawner.getPoint('eagle');
if (eaglePoint != null) { if (eaglePoint != null) {
var eagle = new Eagle(team.id); var eagle = new Eagle(team.id);
team.eagleId = eagle.id;
applyPoint(eagle, eaglePoint); applyPoint(eagle, eaglePoint);
engine.spawn(eagle); engine.spawn(eagle);
} }
@@ -157,6 +170,22 @@ class Game implements EngineListener {
} }
} }
public function onChange(entity:EntityType, ?change:EntityChange):Void {
switch [entity, change] {
case [EntityType.EAGLE(eagle), EntityChange.DEATH]:
if (eagle.death) {
lose(eagle.team);
deferred.resolve(state);
}
case [EntityType.TANK(tank), EntityChange.HIT]:
if (tank.bonus) {
tank.bonus = false;
spawnBonus();
}
case _:
}
}
public function onCollision(entity:EntityType, with:EntityType):Void { public function onCollision(entity:EntityType, with:EntityType):Void {
switch entity { switch entity {
case EntityType.TANK(tank): case EntityType.TANK(tank):
@@ -167,9 +196,6 @@ class Game implements EngineListener {
switch [entity, with] { switch [entity, with] {
case [EntityType.TANK(tank), EntityType.BONUS(bonus)]: case [EntityType.TANK(tank), EntityType.BONUS(bonus)]:
applyBonus(tank, bonus); applyBonus(tank, bonus);
case [EntityType.BULLET(_), EntityType.EAGLE(eagle)]:
lose(eagle.team);
deferred.resolve(state);
case _: case _:
} }
} }
@@ -188,7 +214,9 @@ class Game implements EngineListener {
if (!team.isAlive) { if (!team.isAlive) {
lose(team.id); lose(team.id);
} }
if (tank.bonus) spawnBonus(); if (tank.bonus) {
spawnBonus();
}
deferred.resolve(state); deferred.resolve(state);
case _: case _:
} }
@@ -207,7 +235,7 @@ class Game implements EngineListener {
var level = state.level + 1; var level = state.level + 1;
state.level++; state.level++;
if (level >= config.game.levels) level = 0; if (level >= config.game.levels) level = 0;
return Option.Some(new GameState(state.type, preset.id, level)); return Option.Some({type: state.type, presetId: preset.id, level: level});
} }
public function dispose():Void { public function dispose():Void {
@@ -216,7 +244,7 @@ class Game implements EngineListener {
private function spawnBonus(?type:BonusType):Void { private function spawnBonus(?type:BonusType):Void {
var bonusConfig:BonusConfig = type != null ? config.getBonus(type) : config.bonuses[Math.floor(Math.random() * config.bonuses.length)]; var bonusConfig:BonusConfig = type != null ? config.getBonus(type) : config.bonuses[Math.floor(Math.random() * config.bonuses.length)];
var bonus = new Bonus(bonusConfig.type); var bonus = new Bonus(bonusConfig);
bonus.rect.x = Math.random() * engine.map.width; bonus.rect.x = Math.random() * engine.map.width;
bonus.rect.y = Math.random() * engine.map.height; bonus.rect.y = Math.random() * engine.map.height;
engine.spawn(bonus); engine.spawn(bonus);
@@ -227,8 +255,7 @@ class Game implements EngineListener {
} }
private function applyBonus(tank:Tank, bonus:Bonus):Void { private function applyBonus(tank:Tank, bonus:Bonus):Void {
L.e('XXX', 'applyBonus: ${bonus.bonusType}'); switch (bonus.config.type) {
switch (bonus.bonusType) {
case 'life': case 'life':
getPlayer(tank.playerId).life++; getPlayer(tank.playerId).life++;
case 'star': case 'star':
@@ -237,20 +264,50 @@ class Game implements EngineListener {
} else { } else {
tank.hits++; tank.hits++;
} }
engine.change(tank);
case 'grenade': case 'grenade':
for (t in engine.iterTanks(alienTank(tank.playerId.team))) { for (t in engine.iterTanks(alienTank(tank.playerId.team))) {
engine.destroy(t); engine.destroy(t);
} }
case 'helmet': case 'helmet':
tank.protect.on(20); tank.protect.on(bonus.config.duration);
engine.change(tank);
case 'clock': case 'clock':
for (t in engine.iterTanks(alienTank(tank.playerId.team))) { for (t in engine.iterTanks(alienTank(tank.playerId.team))) {
t.freezing.on(10); t.freezing.on(bonus.config.duration);
engine.change(t);
} }
case 'shovel': case 'shovel':
// ToDo: protect eagle/area // ToDo: protect eagle/area
var team:Team = teams[tank.playerId.team];
if (team.eagleId > 0) {
var eagle:Eagle = cast(engine.entities[team.eagleId], Eagle);
eagle.protect.on(bonus.config.duration);
engine.change(eagle);
}
case _: case _:
engine.destroy(tank); // :-D engine.destroy(tank); // :-D
} }
} }
public function save():GameSave {
var players:Array<PlayerSave> = [];
for (team in teams) {
for (player in team.players) {
if (player.config.control == Control.HUMAN) {
var tank:Tank = EntityTypeResolver.as(engine.entities[player.tankId], Tank);
players.push({
id: player.id,
life: player.life + (player.tankId > 0 ? 1 : 0),
tank: tank != null ? tank.config.type : null
});
}
}
}
return new GameSave({
type: state.type,
presetId: state.presetId,
level: state.level
}, players);
}
} }

View File

@@ -0,0 +1,46 @@
package ru.m.tankz.game;
import yaml.Parser;
import yaml.Renderer;
import yaml.Yaml;
import ru.m.tankz.Type;
typedef PlayerSave = {
var id:PlayerId;
var tank:TankType;
var life:Int;
}
class GameSave {
public var state:GameState;
public var players:Array<PlayerSave>;
public function new(state:GameState, ?players:Array<PlayerSave>) {
this.state = state;
this.players = players != null ? players : [];
}
public function getPlayer(id:PlayerId):PlayerSave {
for (player in players) {
if (player.id.team == id.team && player.id.index == id.index) {
return player;
}
}
return null;
}
public function toYaml():String {
return Yaml.render({
state: state,
players: players,
}, Renderer.options().setFlowLevel(0));
}
public static function fromYaml(value:String):GameSave {
var data:Dynamic = Yaml.parse(value, Parser.options().useObjects());
return new GameSave(data.state, data.players);
}
}

View File

@@ -3,16 +3,9 @@ package ru.m.tankz.game;
import ru.m.tankz.Type; import ru.m.tankz.Type;
class GameState { typedef GameState = {
public var type:GameType; var type:GameType;
public var level:Int; var presetId:PresetId;
public var presetId:PresetId; @:optional var level:Int;
public var loser:TeamId; @:optional var loser:TeamId;
public function new(type:GameType, presetId:PresetId, level:Int = 0) {
this.type = type;
this.presetId = presetId;
this.level = level;
this.loser = null;
}
} }

View File

@@ -12,6 +12,7 @@ class Team {
public var players(default, null):Map<Int, Player>; public var players(default, null):Map<Int, Player>;
public var life(default, default):Int; public var life(default, default):Int;
public var isAlive(get, null):Bool; public var isAlive(get, null):Bool;
public var eagleId(default, default):Int;
public function new(config:TeamConfig, points:Array<SpawnPoint>) { public function new(config:TeamConfig, points:Array<SpawnPoint>) {
this.id = config.id; this.id = config.id;

View File

@@ -1,8 +1,6 @@
package ru.m.tankz.config; package ru.m.tankz.util;
import openfl.Assets;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
import ru.m.tankz.Type;
import yaml.Parser; import yaml.Parser;
import yaml.Renderer; import yaml.Renderer;
import yaml.Yaml; import yaml.Yaml;
@@ -13,9 +11,9 @@ typedef LevelSource = {
@:optional var points:Array<SpawnPoint>; @:optional var points:Array<SpawnPoint>;
} }
class LevelBundle { class LevelUtil {
private static function formatLevel(level:Int):String { public static function formatLevel(level:Int):String {
var result = Std.string(level); var result = Std.string(level);
while (result.length < 3) result = '0${result}'; while (result.length < 3) result = '0${result}';
return result; return result;
@@ -60,9 +58,4 @@ class LevelBundle {
data: [for (i in 0...config.map.gridWidth * config.map.gridHeight) config.bricks[1]] 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);
}
} }