[common] add bonuses classes

This commit is contained in:
2019-08-05 23:00:59 +03:00
parent ef971e03b9
commit db80c151bb
17 changed files with 296 additions and 133 deletions

View File

@@ -1,3 +1,7 @@
0.17.0
------
* Improved `ResultFrame`
0.16.0 0.16.0
------ ------
* Added `GamepadView` for sensor control * Added `GamepadView` for sensor control

View File

@@ -1,11 +1,8 @@
* **shovel** bonus with armor bricks * **shovel** bonus with armor bricks
* bonuses in dota/death mod * bonuses in dota/death mod
* tanks and bullets speed balancing * tanks and bullets speed balancing
* result frame update (next game select, only human player info)
* network game series * network game series
* map packs (create in editor, import in game, save imported in local storage) * map packs (create in editor, import in game, save imported in local storage)
* update bots * update bots
* improve bonuses system * improve bonuses system
* screen gamepad on mobiles
* resize render on mobiles
* save human state in classic game * save human state in classic game

View File

@@ -0,0 +1,24 @@
package ru.m.tankz.bonus;
import ru.m.tankz.config.Config;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.IGame;
import ru.m.tankz.Type;
class BaseBonus implements IBonus {
public var type(get, null):BonusType;
private var config:BonusConfig;
public function new(config:BonusConfig) {
this.config = config;
}
private inline function get_type():BonusType {
return config != null ? config.type : "";
}
public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
throw "Not Implemented";
}
}

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 DestroyTeamBonus extends BaseBonus {
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
var tank:Tank = engine.getEntity(game.getPlayer(playerId).tankId);
if (tank != null) {
for (t in engine.iterTanks(tank.playerId.team.alienTank())) {
game.gameEventSignal.emit(DESTROY(TANK(t.id, {tankId: tank.id})));
}
}
}
}

View File

@@ -0,0 +1,18 @@
package ru.m.tankz.bonus;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.IGame;
import ru.m.tankz.Type;
using ru.m.tankz.game.GameUtil;
class FreezeTeamBonus extends BaseBonus {
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
for (team in game.teams) {
if (team.id != playerId.team) {
game.freezeTeam(engine, team.id, config.duration);
}
}
}
}

View File

@@ -0,0 +1,10 @@
package ru.m.tankz.bonus;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.IGame;
import ru.m.tankz.Type;
interface IBonus {
public var type(get, null):BonusType;
public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void;
}

View File

@@ -0,0 +1,14 @@
package ru.m.tankz.bonus;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.IGame;
import ru.m.tankz.Type;
using ru.m.tankz.game.GameUtil;
class LifeBonus extends BaseBonus {
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
game.changeLife(playerId, 1);
}
}

View File

@@ -0,0 +1,20 @@
package ru.m.tankz.bonus;
import ru.m.tankz.core.Eagle;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.IGame;
import ru.m.tankz.game.Team;
import ru.m.tankz.Type;
using ru.m.tankz.game.GameUtil;
class ProtectEagleBonus extends BaseBonus {
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);
game.protectEagle(eagle, config.duration);
}
}
}

View File

@@ -0,0 +1,18 @@
package ru.m.tankz.bonus;
import ru.m.tankz.core.Tank;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.IGame;
import ru.m.tankz.Type;
using ru.m.tankz.game.GameUtil;
class ProtectTankBonus extends BaseBonus {
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
var tank:Tank = engine.getEntity(game.getPlayer(playerId).tankId);
if (tank != null) {
game.protectTank(tank, config.duration);
}
}
}

View File

@@ -0,0 +1,18 @@
package ru.m.tankz.bonus;
import ru.m.tankz.core.Tank;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.IGame;
import ru.m.tankz.Type;
using ru.m.tankz.game.GameUtil;
class UpgradeTankBonus extends BaseBonus {
override public function apply(playerId:PlayerId, game:IGame, engine:IEngine):Void {
var tank:Tank = engine.getEntity(game.getPlayer(playerId).tankId);
if (tank != null) {
game.upgradeTank(tank, config.value);
}
}
}

View File

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

View File

@@ -9,6 +9,7 @@ import ru.m.tankz.control.NoneControlFactory;
import ru.m.tankz.control.PlayerControl; import ru.m.tankz.control.PlayerControl;
import ru.m.tankz.core.EntityType; import ru.m.tankz.core.EntityType;
import ru.m.tankz.engine.IEngine; import ru.m.tankz.engine.IEngine;
import ru.m.tankz.engine.ITicker;
import ru.m.tankz.game.GameEvent; import ru.m.tankz.game.GameEvent;
import ru.m.tankz.game.GameState; import ru.m.tankz.game.GameState;
import ru.m.tankz.game.IGame; import ru.m.tankz.game.IGame;
@@ -28,6 +29,7 @@ import ru.m.tankz.Type;
public var controlFactory(default, null):IControlFactory; public var controlFactory(default, null):IControlFactory;
public var pause(default, set):Bool; public var pause(default, set):Bool;
public var controls(default, null):Map<String, Control>; public var controls(default, null):Map<String, Control>;
public var ticker(default, null):ITicker;
@:provide var configBundle:IConfigBundle; @:provide var configBundle:IConfigBundle;

View File

@@ -2,6 +2,13 @@ package ru.m.tankz.game;
import ru.m.geom.Line; import ru.m.geom.Line;
import ru.m.geom.Point; 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.control.Control; import ru.m.tankz.control.Control;
import ru.m.tankz.core.Bonus; import ru.m.tankz.core.Bonus;
import ru.m.tankz.core.Bullet; import ru.m.tankz.core.Bullet;
@@ -16,17 +23,37 @@ import ru.m.tankz.game.Spawner;
import ru.m.tankz.Type; import ru.m.tankz.Type;
import ru.m.Timer; import ru.m.Timer;
using ru.m.tankz.game.GameUtil;
class GameRunner extends Game implements EngineListener { class GameRunner extends Game implements EngineListener {
private var timer:Timer; private var timer:Timer;
private var builder:EntityBuilder; private var builder:EntityBuilder;
private var bonuses:Map<String, IBonus>;
public function new(start:Start) { public function new(start:Start) {
super(start.state.type); super(start.state.type);
this.level = start.level; this.level = start.level;
this.state = start.state; this.state = start.state;
this.builder = new EntityBuilder(config); builder = new EntityBuilder(config);
this.engine = new Engine(config, level.size); bonuses = new Map();
this.engine.connect(this); for (bonus in buildBonuses()) {
bonuses[bonus.type] = bonus;
}
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 { override function changePause(value:Bool):Void {
@@ -92,52 +119,6 @@ class GameRunner extends Game implements EngineListener {
} }
} }
private function protectEagle(eagle:Eagle, duration:Float):Void {
eagle.protect = true;
gameEventSignal.emit(CHANGE(EAGLE_PROTECT(eagle.id, eagle.protect)));
engine.ticker.emit(function() {
eagle.protect = false;
gameEventSignal.emit(CHANGE(EAGLE_PROTECT(eagle.id, eagle.protect)));
}, Std.int(duration * 1000));
}
private function protectTank(tank:Tank, duration:Float):Void {
tank.protect = true;
gameEventSignal.emit(CHANGE(TANK_PROTECT(tank.id, tank.protect)));
engine.ticker.emit(function() {
tank.protect = false;
gameEventSignal.emit(CHANGE(TANK_PROTECT(tank.id, tank.protect)));
}, Std.int(duration * 1000));
}
private function freezeTank(tank:Tank, duration:Float):Void {
tank.freezing = true;
tank.stop();
gameEventSignal.emit(STOP(TANK(tank.id)));
gameEventSignal.emit(CHANGE(TANK_FREEZE(tank.id, tank.freezing)));
engine.ticker.emit(function() {
tank.freezing = false;
gameEventSignal.emit(CHANGE(TANK_FREEZE(tank.id, tank.freezing)));
}, Std.int(duration * 1000));
}
private function freezeTeam(teamId:TeamId, duration:Float):Void {
getTeam(teamId).freezing = true;
for (tank in engine.iterTanks(teamTank(teamId))) {
tank.freezing = true;
tank.stop();
gameEventSignal.emit(STOP(TANK(tank.id)));
gameEventSignal.emit(CHANGE(TANK_FREEZE(tank.id, tank.freezing)));
}
engine.ticker.emit(function() {
getTeam(teamId).freezing = false;
for (tank in engine.iterTanks(teamTank(teamId))) {
tank.freezing = false;
gameEventSignal.emit(CHANGE(TANK_FREEZE(tank.id, tank.freezing)));
}
}, Std.int(duration * 1000));
}
private function checkComplete():Void { private function checkComplete():Void {
var actives:Array<TeamId> = []; var actives:Array<TeamId> = [];
for (team in teams.iterator()) { for (team in teams.iterator()) {
@@ -176,14 +157,6 @@ class GameRunner extends Game implements EngineListener {
} }
} }
private inline function emitTankMove(tank:Tank):Void {
gameEventSignal.emit(MOVE(TANK(tank.id, {x:tank.rect.x, y:tank.rect.y, direction:tank.rect.direction})));
}
private inline function emitTankChange(tank:Tank):Void {
gameEventSignal.emit(CHANGE(TANK(tank.id, tank.config.type, tank.hits, tank.bonus)));
}
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):
@@ -280,78 +253,12 @@ class GameRunner extends Game implements EngineListener {
gameEventSignal.emit(EventUtil.buildBonusSpawn(bonus)); gameEventSignal.emit(EventUtil.buildBonusSpawn(bonus));
} }
private inline function teamTank(team:TeamId):Tank->Bool {
return function(tank:Tank):Bool return team == tank.playerId.team;
}
private inline function alienTank(team:TeamId):Tank->Bool {
return function(tank:Tank):Bool return team != tank.playerId.team;
}
private function applyBonus(tank:Tank, bonus:Bonus):Void { private function applyBonus(tank:Tank, bonus:Bonus):Void {
switch (bonus.config.type) { if (bonuses.exists(bonus.config.type)) {
case "life": bonuses[bonus.config.type].apply(tank.playerId, this, engine);
changeLife(tank.playerId, 1);
case "star":
upgradeTank(tank);
case "grenade":
for (t in engine.iterTanks(alienTank(tank.playerId.team))) {
gameEventSignal.emit(DESTROY(TANK(t.id, {tankId: tank.id})));
}
case "helmet":
protectTank(tank, bonus.config.duration);
case "clock":
for (team in teams) {
if (team.id != tank.playerId.team) {
freezeTeam(team.id, bonus.config.duration);
}
}
/*for (t in engine.iterTanks(alienTank(tank.playerId.team))) {
freezeTank(t, bonus.config.duration);
}*/
case "shovel":
// ToDo: protect eagle/area
var team:Team = teams[tank.playerId.team];
if (team.eagleId > 0) {
var eagle:Eagle = cast(engine.entities[team.eagleId], Eagle);
protectEagle(eagle, bonus.config.duration);
}
case "gun":
upgradeTank(tank, 5);
case _:
gameEventSignal.emit(DESTROY(TANK(tank.id, {tankId: tank.id}))); // :-D
}
}
private function upgradeTank(tank:Tank, level:Int = 1):Void {
if (tank.config.upgrade != null) {
while (level-- > 0 && tank.config.upgrade != null) {
tank.config = config.getTank(tank.config.upgrade);
}
} else { } else {
tank.hits++; gameEventSignal.emit(DESTROY(TANK(tank.id, {tankId: tank.id}))); // :-D
} }
emitTankChange(tank);
}
private function changeScore(playerId:PlayerId, score:Int):Void {
var player = getPlayer(playerId);
var team = getTeam(playerId.team);
player.state.score += score;
gameEventSignal.emit(CHANGE(PLAYER_SCORE(playerId, player.state.score)));
gameEventSignal.emit(CHANGE(TEAM_SCORE(playerId.team, state.getTeamScore(team.id))));
}
private function changeLife(playerId:PlayerId, life:Int):Void {
var player = getPlayer(playerId);
player.state.life += life;
gameEventSignal.emit(CHANGE(PLAYER_LIFE(playerId, player.state.life)));
}
private function changeTeamLife(teamId:TeamId, life:Int):Void {
var team = getTeam(teamId);
team.state.life += life;
gameEventSignal.emit(CHANGE(TEAM_LIFE(teamId, team.state.life)));
} }
override public function onGameEvent(event:GameEvent):Void { override public function onGameEvent(event:GameEvent):Void {
@@ -394,7 +301,9 @@ class GameRunner extends Game implements EngineListener {
var team = getTeam(teamId); var team = getTeam(teamId);
team.eagleId = id; team.eagleId = id;
case SPAWN(TANK(id, rect, playerId, info)): case SPAWN(TANK(id, rect, playerId, info)):
getPlayer(playerId).state.tank = info; var player = getPlayer(playerId);
player.tankId = id;
player.state.tank = info;
case SPAWN(BULLET(_, _, playerId, _)): case SPAWN(BULLET(_, _, playerId, _)):
getPlayer(playerId).bullets++; getPlayer(playerId).bullets++;
case DESTROY(EAGLE(id, shot)): case DESTROY(EAGLE(id, shot)):

View File

@@ -0,0 +1,106 @@
package ru.m.tankz.game;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.core.Eagle;
import ru.m.tankz.core.Tank;
import ru.m.tankz.game.GameEvent;
import ru.m.tankz.game.IGame;
import ru.m.tankz.Type;
using ru.m.tankz.game.GameUtil;
class GameUtil {
private static inline function teamTank(team:TeamId):Tank->Bool {
return function(tank:Tank):Bool return team == tank.playerId.team;
}
public static inline function alienTank(team:TeamId):Tank->Bool {
return function(tank:Tank):Bool return team != tank.playerId.team;
}
public static inline function emitTankMove(game:IGame, tank:Tank):Void {
game.gameEventSignal.emit(MOVE(TANK(tank.id, {x:tank.rect.x, y:tank.rect.y, direction:tank.rect.direction})));
}
public static inline function emitTankChange(game:IGame, tank:Tank):Void {
game.gameEventSignal.emit(CHANGE(TANK(tank.id, tank.config.type, tank.hits, tank.bonus)));
}
public static function protectTank(game:IGame, tank:Tank, duration:Float):Void {
tank.protect = true;
game.gameEventSignal.emit(CHANGE(TANK_PROTECT(tank.id, tank.protect)));
game.ticker.emit(function() {
tank.protect = false;
game.gameEventSignal.emit(CHANGE(TANK_PROTECT(tank.id, tank.protect)));
}, Std.int(duration * 1000));
}
public static function protectEagle(game:IGame, eagle:Eagle, duration:Float):Void {
eagle.protect = true;
game.gameEventSignal.emit(CHANGE(EAGLE_PROTECT(eagle.id, eagle.protect)));
game.ticker.emit(function() {
eagle.protect = false;
game.gameEventSignal.emit(CHANGE(EAGLE_PROTECT(eagle.id, eagle.protect)));
}, Std.int(duration * 1000));
}
public static function freezeTank(game:IGame, tank:Tank, duration:Float):Void {
tank.freezing = true;
tank.stop();
game.gameEventSignal.emit(STOP(TANK(tank.id)));
game.gameEventSignal.emit(CHANGE(TANK_FREEZE(tank.id, tank.freezing)));
game.ticker.emit(function() {
tank.freezing = false;
game.gameEventSignal.emit(CHANGE(TANK_FREEZE(tank.id, tank.freezing)));
}, Std.int(duration * 1000));
}
public static function freezeTeam(game:IGame, engine:IEngine, teamId:TeamId, duration:Float):Void {
game.getTeam(teamId).freezing = true;
for (tank in engine.iterTanks(teamId.teamTank())) {
tank.freezing = true;
tank.stop();
game.gameEventSignal.emit(STOP(TANK(tank.id)));
game.gameEventSignal.emit(CHANGE(TANK_FREEZE(tank.id, tank.freezing)));
}
game.ticker.emit(function() {
game.getTeam(teamId).freezing = false;
for (tank in engine.iterTanks(teamId.teamTank())) {
tank.freezing = false;
game.gameEventSignal.emit(CHANGE(TANK_FREEZE(tank.id, tank.freezing)));
}
}, Std.int(duration * 1000));
}
public static function changeLife(game:IGame, playerId:PlayerId, life:Int):Void {
var player = game.getPlayer(playerId);
player.state.life += life;
game.gameEventSignal.emit(CHANGE(PLAYER_LIFE(playerId, player.state.life)));
}
public static function changeTeamLife(game:IGame, teamId:TeamId, life:Int):Void {
var team = game.getTeam(teamId);
team.state.life += life;
game.gameEventSignal.emit(CHANGE(TEAM_LIFE(teamId, team.state.life)));
}
public static function changeScore(game:IGame, playerId:PlayerId, score:Int):Void {
var player = game.getPlayer(playerId);
var team = game.getTeam(playerId.team);
player.state.score += score;
game.gameEventSignal.emit(CHANGE(PLAYER_SCORE(playerId, player.state.score)));
game.gameEventSignal.emit(CHANGE(TEAM_SCORE(playerId.team, game.state.getTeamScore(team.id))));
}
public static function upgradeTank(game:IGame, tank:Tank, level:Int = 1):Void {
if (tank.config.upgrade != null) {
while (level-- > 0 && tank.config.upgrade != null) {
tank.config = game.config.getTank(tank.config.upgrade);
}
} else {
tank.hits++;
}
game.emitTankChange(tank);
}
}

View File

@@ -1,5 +1,6 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import ru.m.tankz.engine.ITicker;
import haxework.signal.Signal; import haxework.signal.Signal;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;
@@ -16,6 +17,7 @@ interface IGame extends GameListener {
public var controlFactory(default, null):IControlFactory; public var controlFactory(default, null):IControlFactory;
public var pause(default, set):Bool; public var pause(default, set):Bool;
public var controls(default, null):Map<String, Control>; public var controls(default, null):Map<String, Control>;
public var ticker(default, null):ITicker;
public var gameEventSignal(default, null):Signal<GameEvent>; public var gameEventSignal(default, null):Signal<GameEvent>;

View File

@@ -9,7 +9,6 @@ class GamePlayer extends Game {
private var record:GameRecord; private var record:GameRecord;
private var data:Array<EventItem>; private var data:Array<EventItem>;
private var ticker:Ticker;
public function new(record:GameRecord) { public function new(record:GameRecord) {
super(record.state.type); super(record.state.type);

View File

@@ -152,8 +152,8 @@ bonuses:
- {score: 500, type: helmet, duration: 20} - {score: 500, type: helmet, duration: 20}
- {score: 500, type: life} - {score: 500, type: life}
- {score: 500, type: shovel, duration: 10} - {score: 500, type: shovel, duration: 10}
- {score: 500, type: star} - {score: 500, type: star, value: 1}
- {score: 500, type: gun} - {score: 500, type: gun, value: 5}
presets: presets:
- id: 0 - id: 0