[common] add ArmorEagleBonus

This commit is contained in:
2019-08-06 16:19:38 +03:00
parent db80c151bb
commit de64e2813e
28 changed files with 230 additions and 84 deletions

View File

@@ -0,0 +1,26 @@
package ru.m.tankz.render;
import flash.display.BitmapData;
import openfl.Assets;
import ru.m.tankz.Type;
class RenderUtil {
public static function eagleImage(death:Bool = false):BitmapData {
var suffix = death ? '-death' : '';
return Assets.getBitmapData('resources/image/eagle/eagle${suffix}.png');
}
public static function tankImage(skin:String, frame:Int = 0):BitmapData {
return Assets.getBitmapData('resources/image/tank/${skin}-${frame}.png');
}
public static function bonusImage(type:BonusType):BitmapData {
return Assets.getBitmapData('resources/image/bonus/${type}.png');
}
public static function bulletImage(piercing:Int = 0):BitmapData {
var type = piercing > 0 ? 'piercing' : 'normal';
return Assets.getBitmapData('resources/image/bullet/${type}.png');
}
}

View File

@@ -4,13 +4,11 @@ import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.PixelSnapping;
import openfl.Assets;
import ru.m.geom.Rectangle;
class BitmapRenderItem extends RenderItem {
public var image(default, set):String;
public var image(default, set):BitmapData;
private var bitmapData:BitmapData;
private var bitmap:Bitmap;
public function new(rect:Rectangle) {
@@ -22,11 +20,10 @@ class BitmapRenderItem extends RenderItem {
return bitmap;
}
private function set_image(value:String):String {
private function set_image(value:BitmapData):BitmapData {
if (image != value) {
image = value;
bitmapData = Assets.getBitmapData(image);
bitmap.bitmapData = bitmapData;
bitmap.bitmapData = image;
}
return image;
}

View File

@@ -18,7 +18,7 @@ class BonusRenderItem extends BitmapRenderItem {
private function set_type(value:BonusType):BonusType {
if (type != value) {
type = value;
image = 'resources/image/bonus/${type}.png';
image = RenderUtil.bonusImage(type);
}
return type;
}

View File

@@ -23,15 +23,19 @@ class BrickRenderItem extends RenderItem {
private var shape:Shape;
private var cells:Array<Point>;
public function new(rect:Rectangle, type:BrickType) {
super(rect);
this.shape = new Shape();
cells = [
private static function buildCells():Array<Point> {
return [
new Point(0, 0),
new Point(0, 1),
new Point(1, 0),
new Point(1, 1),
];
}
public function new(rect:Rectangle, type:BrickType) {
super(rect);
this.shape = new Shape();
cells = buildCells();
this.type = type;
move(rect.position);
}
@@ -51,6 +55,7 @@ class BrickRenderItem extends RenderItem {
private function set_type(value:BrickType):BrickType {
if (type != value) {
type = value;
cells = buildCells();
image = Assets.getBitmapData('resources/image/map/${type}.png');
redraw();
}

View File

@@ -14,8 +14,7 @@ class BulletRenderItem extends BitmapRenderItem {
private function set_piercing(value:Int):Int {
if (piercing != value) {
piercing = value;
var type = piercing > 0 ? 'piercing' : 'normal';
image = 'resources/image/bullet/${type}.png';
image = RenderUtil.bulletImage(piercing);
}
return piercing;
}

View File

@@ -35,8 +35,8 @@ class EagleRenderItem extends BitmapRenderItem {
if (protect != value) {
protect = value;
if (protect) {
protectView.x = (bitmapData.width - protectView.frames[0].image.width) / 2;
protectView.y = (bitmapData.height - protectView.frames[0].image.height) / 2;
protectView.x = (image.width - protectView.frames[0].image.width) / 2;
protectView.y = (image.height - protectView.frames[0].image.height) / 2;
}
protectView.visible = protect;
protectView.playing = protect;
@@ -47,10 +47,9 @@ class EagleRenderItem extends BitmapRenderItem {
private function set_death(value:Bool):Bool {
if (death != value) {
death = value;
var suffix = death ? '-death' : '';
image = 'resources/image/eagle/eagle${suffix}.png';
image = RenderUtil.eagleImage(death);
if (!color.zero) {
bitmap.bitmapData = bitmapData = BitmapUtil.colorize(bitmapData, color);
bitmap.bitmapData = image = BitmapUtil.colorize(image, color);
}
}
return death;

View File

@@ -1,6 +1,5 @@
package ru.m.tankz.render.item;
import haxework.view.theme.ITheme;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Sprite;
@@ -9,8 +8,8 @@ import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import haxework.color.Color;
import haxework.text.BitmapTextField;
import haxework.view.theme.ITheme;
import haxework.view.utils.BitmapUtil;
import openfl.Assets;
import ru.m.animate.Animate;
import ru.m.geom.Rectangle;
@@ -62,8 +61,8 @@ class TankRenderItem extends BitmapRenderItem {
}
private function redraw():Void {
var image1 = Assets.getBitmapData('resources/image/tank/${skin}-0.png');
var image2 = Assets.getBitmapData('resources/image/tank/${skin}-1.png');
var image1 = RenderUtil.tankImage(skin, 0);
var image2 = RenderUtil.tankImage(skin, 1);
var color1:Color = switch hits {
case 1: 0x869C43;
case 2: 0xDEAF80;

View File

@@ -1,8 +1,8 @@
package ru.m.tankz.view.common;
import haxework.view.ImageView;
import openfl.Assets;
import ru.m.tankz.game.GameEvent;
import ru.m.tankz.render.RenderUtil;
class TankView extends ImageView {
@@ -16,7 +16,7 @@ class TankView extends ImageView {
private function set_tank(value:TankInfo):TankInfo {
if (value != null) {
color = value.color;
image = Assets.getBitmapData('resources/image/tank/${value.skin}-0.png');
image = RenderUtil.tankImage(value.skin);
}
return value;
}

View File

@@ -1,9 +1,9 @@
package ru.m.tankz.view.result;
import openfl.utils.Assets;
import haxework.view.form.LabelView;
import haxework.view.group.HGroupView;
import ru.m.tankz.game.GameState;
import ru.m.tankz.render.RenderUtil;
import ru.m.tankz.view.common.TankView;
@:template class FragView extends HGroupView {
@@ -16,15 +16,18 @@ import ru.m.tankz.view.common.TankView;
private function set_data(value:Frag):Frag {
data = value;
titleView.text = data.playerId.format();
scoreView.text = data.score.format();
switch data.target {
case TANK(tank):
tankView.tank = tank;
case EAGLE(color):
tankView.image = Assets.getBitmapData('resources/image/eagle/eagle.png');
tankView.image = RenderUtil.eagleImage();
tankView.color = color;
case BONUS(type):
tankView.image = RenderUtil.bonusImage(type);
titleView.text = type;
}
titleView.text = data.playerId.format();
scoreView.text = data.score.format();
return data;
}

View File

@@ -0,0 +1,43 @@
package ru.m.tankz.bonus;
import ru.m.geom.Point;
import ru.m.tankz.core.Eagle;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.GameEvent;
import ru.m.tankz.game.IGame;
import ru.m.tankz.game.Team;
import ru.m.tankz.Type;
using ru.m.tankz.game.GameUtil;
class ArmorEagleBonus extends BaseBonus {
public static inline var CLASS = "armor.eagle";
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
var team:Team = game.getTeam(playerId.team);
if (team.eagleId > 0) {
var eagle:Eagle = cast(engine.entities[team.eagleId], Eagle);
var center = eagle.rect.center;
var cx:Int = Math.round(center.x / engine.map.cellWidth);
var cy:Int = Math.round(center.y / engine.map.cellHeight);
var bricks:Array<Int> = [];
for (x in cx - 2...cx + 2) {
for (y in cy - 2...cy + 2) {
var brick = engine.map.getBrick(new Point(x, y));
if (brick != null && brick.config.type != "none") {
bricks.push(brick.id);
}
}
}
for (brickId in bricks) {
game.gameEventSignal.emit(CHANGE(BRICK(brickId, "armor")));
}
game.ticker.emit(function() {
for (brickId in bricks) {
game.gameEventSignal.emit(CHANGE(BRICK(brickId, "brick")));
}
}, Std.int(config.duration * 1000), '$CLASS.${eagle.id}');
}
}
}

View File

@@ -0,0 +1,26 @@
package ru.m.tankz.bonus;
import ru.m.tankz.config.Config.BonusConfig;
class BonusFactory {
private var classes:Map<String, Class<IBonus>>;
public function new() {
classes = [
DestroyTeamBonus.CLASS => DestroyTeamBonus,
FreezeTeamBonus.CLASS => FreezeTeamBonus,
LifeBonus.CLASS => LifeBonus,
ArmorEagleBonus.CLASS => ArmorEagleBonus,
ProtectEagleBonus.CLASS => ProtectEagleBonus,
ProtectTankBonus.CLASS => ProtectTankBonus,
UpgradeTankBonus.CLASS => UpgradeTankBonus,
SuicideTankBonus.CLASS => SuicideTankBonus,
];
}
public function build(config:BonusConfig):IBonus {
var bonusClass = classes.exists(config.factory) ? classes[config.factory] : classes[SuicideTankBonus.CLASS];
return Type.createInstance(bonusClass, [config]);
}
}

View File

@@ -10,6 +10,8 @@ using ru.m.tankz.game.GameUtil;
class DestroyTeamBonus extends BaseBonus {
public static inline var CLASS = "destroy.team";
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
var tank:Tank = engine.getEntity(game.getPlayer(playerId).tankId);
if (tank != null) {

View File

@@ -8,6 +8,8 @@ using ru.m.tankz.game.GameUtil;
class FreezeTeamBonus extends BaseBonus {
public static inline var CLASS = "freeze.team";
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
for (team in game.teams) {
if (team.id != playerId.team) {

View File

@@ -8,6 +8,8 @@ using ru.m.tankz.game.GameUtil;
class LifeBonus extends BaseBonus {
public static inline var CLASS = "life";
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
game.changeLife(playerId, 1);
}

View File

@@ -10,6 +10,8 @@ using ru.m.tankz.game.GameUtil;
class ProtectEagleBonus extends BaseBonus {
public static inline var CLASS = "protect.eagle";
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
var team:Team = game.getTeam(playerId.team);
if (team.eagleId > 0) {

View File

@@ -9,6 +9,8 @@ using ru.m.tankz.game.GameUtil;
class ProtectTankBonus extends BaseBonus {
public static inline var CLASS = "protect.tank";
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
var tank:Tank = engine.getEntity(game.getPlayer(playerId).tankId);
if (tank != null) {

View File

@@ -0,0 +1,21 @@
package ru.m.tankz.bonus;
import ru.m.tankz.core.Tank;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.GameEvent;
import ru.m.tankz.game.IGame;
import ru.m.tankz.Type;
using ru.m.tankz.game.GameUtil;
class SuicideTankBonus extends BaseBonus {
public static inline var CLASS = "suicide.tank";
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
var tank:Tank = engine.getEntity(game.getPlayer(playerId).tankId);
if (tank != null) {
game.gameEventSignal.emit(DESTROY(TANK(tank.id, {tankId: tank.id})));
}
}
}

View File

@@ -9,6 +9,8 @@ using ru.m.tankz.game.GameUtil;
class UpgradeTankBonus extends BaseBonus {
public static inline var CLASS = "upgrade.tank";
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
var tank:Tank = engine.getEntity(game.getPlayer(playerId).tankId);
if (tank != null) {

View File

@@ -63,6 +63,7 @@ typedef TankConfig = {
typedef BonusConfig = {
var type:BonusType;
var factory:String;
@:optional var duration:Null<Int>;
@:optional var value:Null<Int>;
@:optinal var score:Null<Int>;

View File

@@ -7,5 +7,6 @@ interface ITicker {
public function start():Void;
public function stop():Void;
public function tick():Int;
public function emit(f:Void->Void, delay:Int):Void;
public function emit(f:Void->Void, delay:Int, ?key:String):Void;
public function cancel(key:String):Void;
}

View File

@@ -13,6 +13,7 @@ class Ticker implements ITicker {
private var passed:Int;
private var last_tick:Int;
private var actions:Array<Action>;
private var actionsByKey:Map<String, Action>;
private var actionId = 0;
private static var TIME = Timer.stamp();
@@ -26,6 +27,7 @@ class Ticker implements ITicker {
last_tick = 0;
running = false;
actions = [];
actionsByKey = new Map();
}
private function get_time():Int {
@@ -73,10 +75,21 @@ class Ticker implements ITicker {
}
}
public function emit(fun:Void->Void, delay:Int):Void {
public function emit(fun:Void->Void, delay:Int, ?key:String):Void {
var action:Action = {action:fun, time:time+delay, id:++actionId};
//L.d("Ticker", 'emit: ${action.id} ${action.time}');
if (key != null) {
cancel(key);
actionsByKey[key] = action;
}
actions.push(action);
actions.sort(function(a, b) return a.time - b.time);
}
public function cancel(key:String):Void {
if (actionsByKey.exists(key)) {
actions.remove(actionsByKey[key]);
actionsByKey.remove(key);
}
}
}

View File

@@ -2,13 +2,7 @@ package ru.m.tankz.game;
import ru.m.geom.Line;
import ru.m.geom.Point;
import ru.m.tankz.bonus.DestroyTeamBonus;
import ru.m.tankz.bonus.FreezeTeamBonus;
import ru.m.tankz.bonus.IBonus;
import ru.m.tankz.bonus.LifeBonus;
import ru.m.tankz.bonus.ProtectEagleBonus;
import ru.m.tankz.bonus.ProtectTankBonus;
import ru.m.tankz.bonus.UpgradeTankBonus;
import ru.m.tankz.bonus.BonusFactory;
import ru.m.tankz.control.Control;
import ru.m.tankz.core.Bonus;
import ru.m.tankz.core.Bullet;
@@ -18,7 +12,7 @@ import ru.m.tankz.core.Tank;
import ru.m.tankz.engine.Engine;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.GameEvent;
import ru.m.tankz.game.GameState.FragTarget;
import ru.m.tankz.game.GameState;
import ru.m.tankz.game.Spawner;
import ru.m.tankz.Type;
import ru.m.Timer;
@@ -28,34 +22,19 @@ using ru.m.tankz.game.GameUtil;
class GameRunner extends Game implements EngineListener {
private var timer:Timer;
private var builder:EntityBuilder;
private var bonuses:Map<String, IBonus>;
private var bonuses:BonusFactory;
public function new(start:Start) {
super(start.state.type);
this.level = start.level;
this.state = start.state;
builder = new EntityBuilder(config);
bonuses = new Map();
for (bonus in buildBonuses()) {
bonuses[bonus.type] = bonus;
}
bonuses = new BonusFactory();
engine = new Engine(config, level.size);
ticker = engine.ticker;
engine.connect(this);
}
private function buildBonuses():Array<IBonus> {
return [
new ProtectEagleBonus(config.getBonus("shovel")),
new ProtectTankBonus(config.getBonus("helmet")),
new LifeBonus(config.getBonus("life")),
new UpgradeTankBonus(config.getBonus("star")),
new UpgradeTankBonus(config.getBonus("gun")),
new DestroyTeamBonus(config.getBonus("grenade")),
new FreezeTeamBonus(config.getBonus("clock")),
];
}
override function changePause(value:Bool):Void {
if (engine != null) {
if (value) {
@@ -254,11 +233,8 @@ class GameRunner extends Game implements EngineListener {
}
private function applyBonus(tank:Tank, bonus:Bonus):Void {
if (bonuses.exists(bonus.config.type)) {
bonuses[bonus.config.type].apply(tank.playerId, this, engine);
} else {
gameEventSignal.emit(DESTROY(TANK(tank.id, {tankId: tank.id}))); // :-D
}
var bonus = bonuses.build(bonus.config);
bonus.apply(tank.playerId, this, engine);
}
override public function onGameEvent(event:GameEvent):Void {
@@ -306,6 +282,8 @@ class GameRunner extends Game implements EngineListener {
player.state.tank = info;
case SPAWN(BULLET(_, _, playerId, _)):
getPlayer(playerId).bullets++;
case CHANGE(BRICK(id, type)):
engine.map.bricksById[id].config = config.getBrick(type);
case DESTROY(EAGLE(id, shot)):
var eagle:Eagle = engine.getEntity(id);
eagle.death = true;
@@ -355,6 +333,12 @@ class GameRunner extends Game implements EngineListener {
var bonus:Bonus = engine.getEntity(id);
var tank:Tank = engine.getEntity(shot.tankId);
applyBonus(tank, bonus);
var shooter = getPlayer(tank.playerId);
shooter.state.frags.push({
playerId: shooter.id,
target: BONUS(bonus.config.type),
score: shot.score,
});
if (shot.score != null) {
changeScore(tank.playerId, shot.score);
}
@@ -373,10 +357,10 @@ class GameRunner extends Game implements EngineListener {
var shot = buildShot(bullet);
if (cell.armor == bullet.config.piercing) {
engine.destroyCell(cell.cellX, cell.cellY);
var brick = engine.map.getBrick(cell.position);
var brick = engine.map.getCellBrick(cell.position);
gameEventSignal.emit(DESTROY(CELL(brick.id, cell.cellX - brick.cellX * 2, cell.cellY - brick.cellY * 2, shot)));
} else if (cell.armor < bullet.config.piercing) {
var brick = engine.map.getBrick(cell.position);
var brick = engine.map.getCellBrick(cell.position);
for (cell in brick.cells) {
engine.destroyCell(cell.cellX, cell.cellY);
}

View File

@@ -11,6 +11,7 @@ import ru.m.tankz.Type;
enum FragTarget {
TANK(tank:TankInfo);
EAGLE(color:Color);
BONUS(type:BonusType);
}
typedef Frag = {

View File

@@ -33,7 +33,7 @@ class GameUtil {
game.ticker.emit(function() {
tank.protect = false;
game.gameEventSignal.emit(CHANGE(TANK_PROTECT(tank.id, tank.protect)));
}, Std.int(duration * 1000));
}, Std.int(duration * 1000), 'protect.tank.${tank.id}');
}
public static function protectEagle(game:IGame, eagle:Eagle, duration:Float):Void {
@@ -42,7 +42,7 @@ class GameUtil {
game.ticker.emit(function() {
eagle.protect = false;
game.gameEventSignal.emit(CHANGE(EAGLE_PROTECT(eagle.id, eagle.protect)));
}, Std.int(duration * 1000));
}, Std.int(duration * 1000), 'protect.eagle.${eagle.id}');
}
public static function freezeTank(game:IGame, tank:Tank, duration:Float):Void {
@@ -53,7 +53,7 @@ class GameUtil {
game.ticker.emit(function() {
tank.freezing = false;
game.gameEventSignal.emit(CHANGE(TANK_FREEZE(tank.id, tank.freezing)));
}, Std.int(duration * 1000));
}, Std.int(duration * 1000), 'freeze.tank.${tank.id}');
}
public static function freezeTeam(game:IGame, engine:IEngine, teamId:TeamId, duration:Float):Void {
@@ -70,7 +70,7 @@ class GameUtil {
tank.freezing = false;
game.gameEventSignal.emit(CHANGE(TANK_FREEZE(tank.id, tank.freezing)));
}
}, Std.int(duration * 1000));
}, Std.int(duration * 1000), 'freeze.team.${teamId}');
}
public static function changeLife(game:IGame, playerId:PlayerId, life:Int):Void {

View File

@@ -13,7 +13,7 @@ class Brick {
public var cellY(default, null):Int;
public var mapConfig(default, null):MapConfig;
public var config(default, default):BrickConfig;
public var config(default, set):BrickConfig;
public var rect(default, null):Rectangle;
public var cells(default, null):HashMap<Point, GridCell>;
@@ -34,7 +34,17 @@ class Brick {
);
}
public function get_broken():Int {
private function set_config(value:BrickConfig):BrickConfig {
config = value;
for (cell in cells) {
cell.layer = config.layer;
cell.armor = config.armor;
cell.destroyed = false;
}
return config;
}
private function get_broken():Int {
var i:Int = 0;
for (c in cells.iterator()) {
if (c.destroyed) {
@@ -44,7 +54,7 @@ class Brick {
return i;
}
public function get_destroyed():Bool {
private function get_destroyed():Bool {
var i = 0;
var result:Bool = false;
for (c in cells.iterator()) {
@@ -57,7 +67,7 @@ class Brick {
return result;
}
public function set_destroyed(value:Bool):Bool {
private function set_destroyed(value:Bool):Bool {
if (value) {
for (c in cells.iterator()) {
c.destroyed = true;
@@ -66,7 +76,7 @@ class Brick {
return value;
}
public function get_id():Int {
private function get_id():Int {
return -((cellX * 1000) + cellY);
}

View File

@@ -18,6 +18,7 @@ class LevelMap {
public var height(get, null):Float;
public var bricks(default, null):Array<Brick>;
public var bricksById(default, null):Map<Int, Brick>;
public var grid(default, null):Grid;
@@ -31,6 +32,7 @@ class LevelMap {
gridHeight = size != null ? size.height : config.grid.height;
bricksMap = new HashMap();
bricks = [];
bricksById = new Map();
grid = new Grid(
Std.int(cellWidth / 2),
Std.int(cellHeight / 2),
@@ -57,11 +59,16 @@ class LevelMap {
for (cell in brick.cells.iterator()) {
bricksMap.set(cell.position, brick);
}
bricksById.set(brick.id, brick);
return brick;
}));
}
public function getBrick(position:Point):Brick {
return bricks[Std.int(position.x + position.y * gridWidth)];
}
public function getCellBrick(position:Point):Brick {
return bricksMap.get(position);
}

View File

@@ -147,13 +147,13 @@ tanks:
skin: bd
bonuses:
- {score: 500, type: clock, duration: 10}
- {score: 500, type: grenade}
- {score: 500, type: helmet, duration: 20}
- {score: 500, type: life}
- {score: 500, type: shovel, duration: 10}
- {score: 500, type: star, value: 1}
- {score: 500, type: gun, value: 5}
- {score: 500, factory: freeze.team, type: clock, duration: 10}
- {score: 500, factory: destroy.team, type: grenade}
- {score: 500, factory: protect.tank, type: helmet, duration: 15}
- {score: 500, factory: life, type: life}
- {score: 500, factory: armor.eagle, type: shovel, duration: 10}
- {score: 500, factory: upgrade.tank, type: star, value: 1}
- {score: 500, factory: upgrade.tank, type: gun, value: 5}
presets:
- id: 0

View File

@@ -107,9 +107,8 @@ tanks:
skin: bb
bonuses:
- {type: clock, duration: 10}
- {type: grenade}
- {type: helmet, duration: 20}
- {type: life}
- {type: shovel, duration: 10}
- {type: star}
- {score: 100, factory: freeze.team, type: clock, duration: 10}
- {score: 100, factory: destroy.team, type: grenade}
- {score: 100, factory: protect.tank, type: helmet, duration: 15}
- {score: 100, factory: life, type: life}
- {score: 100, factory: protect.eagle, type: shovel, duration: 10}