From 1ae84cf5a83d50ee97c48a096d7d0472f4a48e57 Mon Sep 17 00:00:00 2001 From: shmyga Date: Thu, 15 Feb 2018 17:11:41 +0300 Subject: [PATCH] [common] bonuses implemented --- WORK.md | 29 +++++++---- package.json | 3 +- project.xml | 1 + .../haxe/ru/m/tankz/render/RenderItem.hx | 26 +++++++++- src/client/resources/classic/config.yaml | 2 +- src/client/resources/dota/config.yaml | 24 ++++++--- src/common/haxe/ru/m/tankz/config/Config.hx | 1 + src/common/haxe/ru/m/tankz/core/EntityType.hx | 15 ++++++ .../haxe/ru/m/tankz/core/Modificator.hx | 27 ++++++++++ src/common/haxe/ru/m/tankz/core/Tank.hx | 11 ++++ src/common/haxe/ru/m/tankz/engine/Engine.hx | 52 +++++++++++-------- src/common/haxe/ru/m/tankz/game/Game.hx | 40 ++++++++------ src/common/haxe/ru/m/tankz/game/Player.hx | 9 +++- src/common/haxe/ru/m/tankz/game/Team.hx | 14 +++-- 14 files changed, 187 insertions(+), 67 deletions(-) create mode 100644 src/common/haxe/ru/m/tankz/core/Modificator.hx diff --git a/WORK.md b/WORK.md index baa9473..fca9f3e 100644 --- a/WORK.md +++ b/WORK.md @@ -10,7 +10,7 @@ * ui * auth frame 0% - * select game frame 30% (classic 1/2 player, dota singe/coop/vs) + * select game frame 100% (classic 1/2 player, dota singe/coop/vs) * select level frame 10% * game frame 50% @@ -21,21 +21,29 @@ * bullets 100% * boxes 100% * map changes 100% - * bonuses 0% + * bonuses 100% * eagle 100% * game * classic * state 50% * bot 50% - * player: 50% + * human 100% * dota * state 50% * bot 10% - * player 0% + * human 100% + +* bonus + * star 50% + * grenade 0% + * helm 0% + * clock 0% + * shovel 0% * state * score 0% + * human tank 0% * save/load 0% * export/import 0% @@ -47,16 +55,19 @@ * animations * tank spawn 0% * tank move 100% - * map water 0% + * map water 100% * bullet boom 90% * tank boom 90% - * bonuses 0% + * bonuses 100% * html5 50% * proto ... * editor - * open 0% - * edit 0% - * save 0% \ No newline at end of file + * level + * open 100% + * edit 50% + * save 100% + * tank + * color 10% \ No newline at end of file diff --git a/package.json b/package.json index 8ed5999..7a5dc50 100755 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "protohx": "0.4.6", "yaml": "1.3.0", "haxework": "git@bitbucket.org:shmyga/haxework.git", - "orm": "2.1.0" + "orm": "2.1.0", + "yield": "1.1.2" } } diff --git a/project.xml b/project.xml index fcc9e10..a684dfc 100755 --- a/project.xml +++ b/project.xml @@ -12,6 +12,7 @@ + diff --git a/src/client/haxe/ru/m/tankz/render/RenderItem.hx b/src/client/haxe/ru/m/tankz/render/RenderItem.hx index 460e931..6cb305a 100644 --- a/src/client/haxe/ru/m/tankz/render/RenderItem.hx +++ b/src/client/haxe/ru/m/tankz/render/RenderItem.hx @@ -182,14 +182,23 @@ class TankItem extends RenderItem { private var type:String; private var hits:Int; + private var protected:Bool; + private var frozen:Bool; private var tankView:Animate; + private var protectView:Animate; public function new(value:Tank) { super(value); view = new Sprite(); tankView = new Animate(); view.addChild(tankView); + protectView = new Animate( + [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; + view.addChild(protectView); redraw(); } @@ -200,6 +209,15 @@ class TankItem extends RenderItem { frames[0], frames[0], frames[0], frames[1], frames[1], frames[1], ]; + if (value.protect.active) { + protectView.x = (tankView.frames[0].width - protectView.frames[0].width) / 2; + protectView.y = (tankView.frames[0].height - protectView.frames[0].height) / 2; + protectView.playing = true; + protectView.visible = true; + } else { + protectView.playing = false; + protectView.visible = false; + } } private function getFrames():Array { @@ -212,12 +230,16 @@ class TankItem extends RenderItem { super.update(); var t = value.config.type; var h = value.hits; - if (t != this.type || h != this.hits) { + var p = value.protect.active; + var f = value.freezing.active; + if (t != type || h != hits || p != protected || f != frozen) { this.type = t; this.hits = h; + this.protected = p; + this.frozen = f; redraw(); } - tankView.playing = (value.mx !=0 || value.my != 0); + tankView.playing = !value.freezing.active && (value.mx !=0 || value.my != 0); } override public function dispose():Void { diff --git a/src/client/resources/classic/config.yaml b/src/client/resources/classic/config.yaml index bbc6ca8..2912337 100644 --- a/src/client/resources/classic/config.yaml +++ b/src/client/resources/classic/config.yaml @@ -22,7 +22,7 @@ player: control: human life: 3 tanks: - - {type: human0, rate: 1} + - {type: human0, rate: 1, protect: 5} bot: &bot control: bot life: -1 diff --git a/src/client/resources/dota/config.yaml b/src/client/resources/dota/config.yaml index 13b41a4..9b7c1cc 100644 --- a/src/client/resources/dota/config.yaml +++ b/src/client/resources/dota/config.yaml @@ -31,15 +31,25 @@ player: &player life: -1 control: bot tanks: - - {type: slow, rate: 0.5} - - {type: fast, rate: 0.5} + - {type: slow, rate: 0.5, protect: 3, bonus: 0} + - {type: fast, rate: 0.5, protect: 3, bonus: 0} + +human1: &human1 + <<: *player + control: human + color: 0xf055a0 + +human2: &human2 + <<: *player + control: human + color: 0xa055f0 presets: - id: player1 teams: - <<: *radiant players: - - {<<: *player, index: 0, control: human, color: 0xf055a0} + - {<<: *human1, index: 0} - {<<: *player, index: 1} - {<<: *player, index: 2} - {<<: *player, index: 3} @@ -55,8 +65,8 @@ presets: teams: - <<: *radiant players: - - {<<: *player, index: 0, control: human, color: 0xf055a0} - - {<<: *player, index: 1, control: human, color: 0xf055a0} + - {<<: *human1, index: 0} + - {<<: *human1, index: 1} - {<<: *player, index: 2} - {<<: *player, index: 3} - {<<: *player, index: 4} @@ -71,14 +81,14 @@ presets: teams: - <<: *radiant players: - - {<<: *player, index: 0, control: human, color: 0xf055a0} + - {<<: *human1, index: 0} - {<<: *player, index: 1} - {<<: *player, index: 2} - {<<: *player, index: 3} - {<<: *player, index: 4} - <<: *dire players: - - {<<: *player, index: 0, control: human, color: 0xa055f0} + - {<<: *human2, index: 0} - {<<: *player, index: 1} - {<<: *player, index: 2} - {<<: *player, index: 3} diff --git a/src/common/haxe/ru/m/tankz/config/Config.hx b/src/common/haxe/ru/m/tankz/config/Config.hx index d247251..bf4e3b0 100644 --- a/src/common/haxe/ru/m/tankz/config/Config.hx +++ b/src/common/haxe/ru/m/tankz/config/Config.hx @@ -59,6 +59,7 @@ typedef TankSpawn = { var type:TankType; var rate:Float; @:optional var bonus:Float; + @:optional var protect:Float; } typedef PlayerConfig = { diff --git a/src/common/haxe/ru/m/tankz/core/EntityType.hx b/src/common/haxe/ru/m/tankz/core/EntityType.hx index ad301a0..b76f752 100644 --- a/src/common/haxe/ru/m/tankz/core/EntityType.hx +++ b/src/common/haxe/ru/m/tankz/core/EntityType.hx @@ -25,4 +25,19 @@ class EntityTypeResolver { case x: null; } } + + public static function as(entity:Entity, type:Class):Null { + if (Std.is(entity, type)) { + return cast entity; + } + return null; + } + + public static function asCall(entity:Entity, type:Class, fun:T->R, ?defaultResult:R):R { + var e:T = as(entity, type); + if (e != null) { + return fun(e); + } + return defaultResult; + } } \ No newline at end of file diff --git a/src/common/haxe/ru/m/tankz/core/Modificator.hx b/src/common/haxe/ru/m/tankz/core/Modificator.hx new file mode 100644 index 0000000..01f05f5 --- /dev/null +++ b/src/common/haxe/ru/m/tankz/core/Modificator.hx @@ -0,0 +1,27 @@ +package ru.m.tankz.core; + +import haxe.Timer; + +class Modificator { + + public var active(default, default):Bool; + private var timer:Timer; + + public function new() { + active = false; + } + + public function on(seconds:Float):Void { + off(); + active = true; + timer = Timer.delay(off, Std.int(seconds * 1000)); + } + + public function off():Void { + if (timer != null) { + timer.stop(); + timer = null; + } + active = false; + } +} diff --git a/src/common/haxe/ru/m/tankz/core/Tank.hx b/src/common/haxe/ru/m/tankz/core/Tank.hx index 7f0fd60..1a0c6a3 100755 --- a/src/common/haxe/ru/m/tankz/core/Tank.hx +++ b/src/common/haxe/ru/m/tankz/core/Tank.hx @@ -15,11 +15,15 @@ class Tank extends MobileEntity { public var color(default, default):Color; public var hits(default, default):Int; public var bonus(default, default):Bool; + public var protect(default, null):Modificator; + public var freezing(default, null):Modificator; private var bulletsCounter:Int = 0; public function new(playerId:PlayerId, config:TankConfig) { super(new Rectangle(0, 0, config.width, config.height), config.speed, Direction.RIGHT); + this.protect = new Modificator(); + this.freezing = new Modificator(); this.playerId = playerId; this.config = config; this.hits = config.hits; @@ -35,7 +39,14 @@ class Tank extends MobileEntity { return value; } + override public function move(direction:Direction):Void { + if (!freezing.active) { + super.move(direction); + } + } + public function shot():Null { + if (freezing.active) return null; if (bulletsCounter >= config.bullets) return null; var bullet = new Bullet(this); bullet.rect.center = rect.center.add(new Point(rect.width / 4 * rect.direction.x, rect.height / 4 * rect.direction.y)); diff --git a/src/common/haxe/ru/m/tankz/engine/Engine.hx b/src/common/haxe/ru/m/tankz/engine/Engine.hx index 1c6355f..daa1ad7 100755 --- a/src/common/haxe/ru/m/tankz/engine/Engine.hx +++ b/src/common/haxe/ru/m/tankz/engine/Engine.hx @@ -28,19 +28,6 @@ class CollisionProcessor implements EngineListener { public function onSpawn(entity:EntityType):Void {} - private function checkTankBullet(tank:Tank, bullet:Bullet):Bool { - if (bullet.tankId == tank.id) return false; - return engine.config.game.friendlyFire || tank.playerId.team != bullet.playerId.team; - } - - private function hitTank(tank:Tank):Void { - if (tank.hits > 0) { - tank.hits--; - } else { - engine.destroy(tank); - } - } - public function onCollision(entity:EntityType, with:EntityType):Void { switch [entity, with] { case [EntityType.TANK(tank), EntityType.TANK(other_tank)]: @@ -51,8 +38,16 @@ class CollisionProcessor implements EngineListener { engine.destroy(bonus); case [EntityType.TANK(tank), EntityType.BULLET(bullet)] | [EntityType.BULLET(bullet), EntityType.TANK(tank)]: - if (checkTankBullet(tank, bullet)) { - hitTank(tank); + if (bullet.tankId == tank.id || (!engine.config.game.friendlyFire && tank.playerId.team == bullet.playerId.team)) { + // Nothing + } else { + if (!tank.protect.active) { + if (tank.hits > 0) { + tank.hits--; + } else { + engine.destroy(tank); + } + } engine.destroy(bullet); } case [EntityType.BULLET(bullet), EntityType.BULLET(other_bullet)]: @@ -70,6 +65,7 @@ class CollisionProcessor implements EngineListener { public function onDestroy(entity:EntityType):Void { } } +@:yield class Engine implements ControlHandler { public var config(default, default):Config; @@ -132,8 +128,6 @@ class Engine implements ControlHandler { } } - - public function update():Void { var newTime:Float = Date.now().getTime(); var d:Float = newTime - time; @@ -153,6 +147,15 @@ class Engine implements ControlHandler { }*/ if (entity.mx != 0 || entity.my != 0) { + var asTank:Tank = EntityTypeResolver.as(entity, Tank); + var asBullet:Bullet = EntityTypeResolver.as(entity, Bullet); + + if (asTank != null) { + if (asTank.freezing.active) { + continue; + } + } + var side:Line = entity.rect.getSide(entity.rect.direction.reverse()); var step:Point = new Point(entity.rect.direction.x * map.cellWidth / 4, entity.rect.direction.y * map.cellHeight / 4); var end:Point = side.center.add(new Point(entity.mx * (d / 30), entity.my * (d / 30))); @@ -186,15 +189,14 @@ class Engine implements ControlHandler { } } - if (Std.is(entity, Bullet)) { - var bullet:Bullet = cast ent; + if (asBullet != null) { if (collision) { cells = map.grid.getCells(side.setLength(map.grid.cellWidth * 3)); for (cell in cells) { if (cell.armor > 0) { - if (cell.armor == bullet.config.piercing) { + if (cell.armor == asBullet.config.piercing) { cell.destroyed = true; - } else if (cell.armor < bullet.config.piercing) { + } else if (cell.armor < asBullet.config.piercing) { var brick = map.getBrick(cell); brick.destroyed = true; } @@ -218,4 +220,12 @@ class Engine implements ControlHandler { listeners = []; entities = new Map(); } + + public function iterTanks(filter:Tank->Bool):Iterator { + for (entity in entities) { + if (Std.is(entity, Tank) && filter(entity)) { + @yield return entity; + } + } + } } diff --git a/src/common/haxe/ru/m/tankz/game/Game.hx b/src/common/haxe/ru/m/tankz/game/Game.hx index 82e4694..04e99de 100644 --- a/src/common/haxe/ru/m/tankz/game/Game.hx +++ b/src/common/haxe/ru/m/tankz/game/Game.hx @@ -62,6 +62,9 @@ class Game implements EngineListener { tank.color = player.config.color.zero ? teams[playerId.team].config.color : player.config.color; tank.bonus = Math.random() < spawn.bonus; applyPoint(tank, point); + if (spawn.protect > 0) { + tank.protect.on(spawn.protect); + } return tank; } @@ -126,7 +129,7 @@ class Game implements EngineListener { if (getTeam(task.playerId.team).trySpawn(task.playerId, true)) { var tank = buildTank(task.playerId, task.point); player.tankId = tank.id; - Timer.delay(function() engine.spawn(tank), 1500); + engine.spawn(tank); } else if (!team.isAlive) { lose(team.id); } @@ -155,10 +158,13 @@ class Game implements EngineListener { } public function onCollision(entity:EntityType, with:EntityType):Void { - switch [entity, with] { - case [EntityType.TANK(tank), _]: + switch entity { + case EntityType.TANK(tank): var control = getPlayer(tank.playerId).control; if (control != null) control.onCollision(with); + case _: + } + switch [entity, with] { case [EntityType.TANK(tank), EntityType.BONUS(bonus)]: applyBonus(tank, bonus); case [EntityType.BULLET(_), EntityType.EAGLE(eagle)]: @@ -208,17 +214,20 @@ class Game implements EngineListener { engine.dispose(); } - - private function spawnBonus():Void { - var bonusConfig = config.bonuses[Math.floor(Math.random() * config.bonuses.length)]; - L.d(TAG, 'Spawn Bonus(${bonusConfig}'); + private function spawnBonus(?type:BonusType):Void { + var bonusConfig:BonusConfig = type != null ? config.getBonus(type) : config.bonuses[Math.floor(Math.random() * config.bonuses.length)]; var bonus = new Bonus(bonusConfig.type); bonus.rect.x = Math.random() * engine.map.width; bonus.rect.y = Math.random() * engine.map.height; engine.spawn(bonus); } + inline private function alienTank(team:TeamId):Tank->Bool { + return function(tank:Tank):Bool return team != tank.playerId.team; + } + private function applyBonus(tank:Tank, bonus:Bonus):Void { + L.e('XXX', 'applyBonus: ${bonus.bonusType}'); switch (bonus.bonusType) { case 'life': getPlayer(tank.playerId).life++; @@ -229,19 +238,18 @@ class Game implements EngineListener { tank.hits++; } case 'grenade': - for (entity in engine.entities.iterator()) { - switch (EntityTypeResolver.of(entity)) { - case EntityType.TANK(t): - if (t.playerId.team != tank.playerId.team) { - engine.destroy(t); - } - case x: - } + for (t in engine.iterTanks(alienTank(tank.playerId.team))) { + engine.destroy(t); } case 'helmet': + tank.protect.on(20); case 'clock': + for (t in engine.iterTanks(alienTank(tank.playerId.team))) { + t.freezing.on(10); + } case 'shovel': - case x: + // ToDo: protect eagle/area + case _: engine.destroy(tank); // :-D } } diff --git a/src/common/haxe/ru/m/tankz/game/Player.hx b/src/common/haxe/ru/m/tankz/game/Player.hx index a1d8a8c..3270a21 100644 --- a/src/common/haxe/ru/m/tankz/game/Player.hx +++ b/src/common/haxe/ru/m/tankz/game/Player.hx @@ -12,6 +12,7 @@ class Player { public var tankId(default, set):Int; public var control(default, set):Control; public var life(default, default):Int; + public var isAlive(get, null):Bool; public function new(teamId:TeamId, config:PlayerConfig) { this.config = config; @@ -20,7 +21,7 @@ class Player { this.life = config.life; } - public function set_tankId(value:Int):Int { + private function set_tankId(value:Int):Int { tankId = value; if (control != null) { control.tankId = tankId; @@ -28,7 +29,7 @@ class Player { return tankId; } - public function set_control(value:Control):Control { + private function set_control(value:Control):Control { if (control != null) control.dispose(); control = value; if (control != null) { @@ -36,4 +37,8 @@ class Player { } return control; } + + private function get_isAlive():Bool { + return tankId > 0 || life > 0; + } } diff --git a/src/common/haxe/ru/m/tankz/game/Team.hx b/src/common/haxe/ru/m/tankz/game/Team.hx index 70be402..b9e00dc 100644 --- a/src/common/haxe/ru/m/tankz/game/Team.hx +++ b/src/common/haxe/ru/m/tankz/game/Team.hx @@ -43,16 +43,14 @@ class Team { // ToDo: eagle state? private function get_isAlive():Bool { - var life:Int = Lambda.fold(players, function(p:Player, t:Int) return t + p.life, life); if (life > 0) { return true; - } else { - for (player in players) { - if (player.tankId > 0) { - return true; - } - } - if (spawner.active) { + } + if (spawner.active) { + return true; + } + for (player in players) { + if (player.isAlive) { return true; } }