[common] GameRunner extend from Game

This commit is contained in:
2019-05-27 17:59:59 +03:00
parent 94b19a1c26
commit ba13111a8e
20 changed files with 202 additions and 184 deletions

View File

@@ -12,7 +12,7 @@ class BotControl extends Control {
private var tank(get, null):Tank;
private inline function get_tank():Tank {
return handler == null ? null : handler.engine.getEntity(tankId);
return handler == null ? null : engine.getEntity(tankId);
}
override public function stop():Void {

View File

@@ -4,13 +4,13 @@ import ru.m.geom.Direction;
import ru.m.tankz.core.Eagle;
import ru.m.tankz.core.Entity;
import ru.m.tankz.core.EntityType;
import ru.m.tankz.game.IGame;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.Type;
class BotHelper {
public static function findEagle(team:TeamId, handler:IGame):Null<Eagle> {
for (entity in handler.engine.entities) {
public static function findEagle(team:TeamId, engine:IEngine):Null<Eagle> {
for (entity in engine.entities) {
switch (EntityTypeResolver.of(entity)) {
case EntityType.EAGLE(eagle):
if (eagle.team != team) {

View File

@@ -56,7 +56,7 @@ class HardBotControl extends BotControl {
}
var enemy:Tank = null;
var distance:Float = Math.POSITIVE_INFINITY;
for (entity in handler.engine.entities.iterator()) {
for (entity in engine.entities.iterator()) {
switch EntityTypeResolver.of(entity) {
case TANK(t):
if (t.playerId.team != tank.playerId.team) {
@@ -82,7 +82,7 @@ class HardBotControl extends BotControl {
}
private function calcTurn():Void {
var eagle:Eagle = BotHelper.findEagle(playerId.team, handler);
var eagle:Eagle = BotHelper.findEagle(playerId.team, engine);
if (eagle != null && Math.random() > 0.5) {
turn(BotHelper.getDirectionTo(tank, eagle));
} else {

View File

@@ -45,7 +45,7 @@ class StupidBotControl extends BotControl {
private function calcTurn():Void {
if (handler == null || tank == null) return;
var eagle:Eagle = BotHelper.findEagle(playerId.team, handler);
var eagle:Eagle = BotHelper.findEagle(playerId.team, engine);
if (eagle != null && Math.random() > 0.5) {
turn(BotHelper.getDirectionTo(tank, eagle));
} else {

View File

@@ -1,8 +1,9 @@
package ru.m.tankz.control;
import ru.m.tankz.game.GameEvent;
import ru.m.geom.Direction;
import ru.m.tankz.core.EntityType;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.GameEvent;
import ru.m.tankz.game.IGame;
import ru.m.tankz.Type;
@@ -17,13 +18,15 @@ class Control {
public var playerId(default, null):PlayerId;
public var tankId(default, default):Int;
private var handler:IGame;
private var engine:IEngine;
public function new(playerId:PlayerId) {
this.playerId = playerId;
}
public function bind(handler:IGame):Void {
public function bind(handler:IGame, engine:IEngine):Void {
this.handler = handler;
this.engine = engine;
}
public function action(action:TankAction):Void {
@@ -41,5 +44,6 @@ class Control {
public function dispose():Void {
stop();
handler = null;
engine = null;
}
}

View File

@@ -6,8 +6,6 @@ import ru.m.tankz.bundle.IConfigBundle;
import ru.m.tankz.config.Config;
import ru.m.tankz.core.Entity;
import ru.m.tankz.core.EntityType;
import ru.m.tankz.engine.Engine;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.game.GameEvent;
import ru.m.tankz.game.GameState;
import ru.m.tankz.game.IGame;
@@ -20,34 +18,18 @@ import ru.m.tankz.Type;
public var type(default, null):GameType;
public var teams(default, null):Map<TeamId, Team>;
public var config(default, null):Config;
public var engine(default, null):IEngine;
public var winner(default, null):Null<TeamId>;
public var state(default, null):GameState;
private var builder:EntityBuilder;
@:provide var configBundle:IConfigBundle;
public function new(state:GameState) {
this.type = state.type;
this.state = state;
this.teams = new Map();
this.config = configBundle.get(type);
this.engine = new Engine(config);
this.builder = new EntityBuilder(config);
connect(this);
prepare(state);
}
private function prepare(state:GameState):Void {
var level:LevelConfig = state.level;
var points:Array<SpawnPoint> = level.points != null ? level.points : config.points;
engine.map.setData(level.data);
for (teamConfig in state.preset.teams) {
var teamPoints = points.filter(function(p:SpawnPoint) return p.team == teamConfig.id);
var team:Team = new Team(teamConfig, teamPoints, state.teams[teamConfig.id]);
teams[team.id] = team;
}
}
private function applyPosition(entity:Entity, position:Position):Void {
@@ -83,8 +65,17 @@ import ru.m.tankz.Type;
}
}
public function start():Void {
var level:LevelConfig = state.level;
var points:Array<SpawnPoint> = level.points != null ? level.points : config.points;
for (teamConfig in state.preset.teams) {
var teamPoints = points.filter(function(p:SpawnPoint) return p.team == teamConfig.id);
var team:Team = new Team(teamConfig, teamPoints, state.teams[teamConfig.id]);
teams[team.id] = team;
}
}
public function dispose():Void {
gameEventSignal.dispose();
engine.dispose();
}
}

View File

@@ -1,7 +1,6 @@
package ru.m.tankz.game;
import haxe.ds.Option;
import haxework.signal.Signal;
import ru.m.geom.Line;
import ru.m.geom.Point;
import ru.m.tankz.control.Control;
@@ -12,52 +11,50 @@ import ru.m.tankz.core.Bullet;
import ru.m.tankz.core.Eagle;
import ru.m.tankz.core.EntityType;
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.IGame;
import ru.m.tankz.game.Spawner;
import ru.m.tankz.Type;
import ru.m.Timer;
class GameRunner implements EngineListener implements GameListener {
class GameRunner extends Game implements EngineListener {
@:provide var controlFactory:IControlFactory;
private var game(default, null):IGame;
private var gameEventSignal(get, null):Signal<GameEvent>;
public var engine(default, null):IEngine;
private var timer:Timer;
private var builder:EntityBuilder;
public function new(game:IGame) {
this.game = game;
this.builder = new EntityBuilder(this.game.config);
this.game.connect(this);
this.game.engine.connect(this);
}
private inline function get_gameEventSignal():Signal<GameEvent> {
return game.gameEventSignal;
public function new(state:GameState) {
super(state);
this.builder = new EntityBuilder(config);
this.engine = new Engine(config);
this.engine.connect(this);
}
private function update():Void {
game.engine.update();
engine.update();
}
public function dispose():Void {
override public function dispose():Void {
super.dispose();
if (timer != null) {
timer.stop();
timer = null;
}
game.disconnect(this);
game.engine.disconnect(this);
engine.dispose();
}
public function start(state:GameState):Void {
for (team in game.teams.iterator()) {
override public function start():Void {
super.start();
engine.map.setData(state.level.data);
for (team in teams.iterator()) {
for (player in team.players.iterator()) {
var control = controlFactory.build(player.id, AController.fromString(player.config.control));
if (control != null) {
player.control = control;
player.control.bind(game);
player.control.bind(this, engine);
}
}
team.spawner.runner = spawn;
@@ -69,30 +66,19 @@ class GameRunner implements EngineListener implements GameListener {
if (team.config.eagle != null) {
var point = team.spawner.getPoint("eagle");
var eagle = builder.buildEagle(point, team.id);
game.engine.spawn(eagle);
engine.spawn(eagle);
gameEventSignal.emit(EventUtil.buildEagleSpawn(eagle));
eagle.protect.connect(onEagleProtectChange);
}
}
gameEventSignal.emit(EventUtil.buildBricksSpawn(game.engine.map));
gameEventSignal.emit(EventUtil.buildBricksSpawn(engine.map));
gameEventSignal.emit(GameEvent.START(state));
//for (i in 0...10) spawnBonus();
}
public function next():Option<GameState> {
for (rule in game.config.game.complete) {
if (rule.team != null && rule.team != game.winner) {
return Option.None;
}
}
var level = game.state.levelId + 1;
if (level >= game.config.game.levels) level = 0;
return Option.Some(new GameState(game.type, game.state.presetId, level, game.state));
}
private function spawn(task:SpawnTask):Void {
var tank = builder.buildTank(task.point, task.playerId, task.tankType);
game.engine.spawn(tank);
engine.spawn(tank);
gameEventSignal.emit(EventUtil.buildTankSpawn(tank));
tank.protect.connect(onTankProtectChange);
tank.freezing.connect(onTankFreezingChange);
@@ -112,10 +98,10 @@ class GameRunner implements EngineListener implements GameListener {
private function checkComplete():Void {
var actives:Array<TeamId> = [];
for (team in game.teams.iterator()) {
for (team in teams.iterator()) {
if (team.isAlive) {
if (team.eagleId > 0) {
if (!cast(game.engine.entities[team.eagleId], Eagle).death) {
if (!cast(engine.entities[team.eagleId], Eagle).death) {
actives.push(team.id);
}
} else {
@@ -132,15 +118,15 @@ class GameRunner implements EngineListener implements GameListener {
}
private function complete(winner:TeamId):Void {
for (team in game.teams.iterator()) {
for (team in teams.iterator()) {
for (player in team.players) {
player.control.action(TankAction.STOP);
player.control.dispose();
}
}
Timer.delay(function() {
gameEventSignal.emit(GameEvent.COMPLETE(game.state, winner));
}, 5000);
gameEventSignal.emit(GameEvent.COMPLETE(state, winner));
}, 3000);
}
public function onSpawn(entity:EntityType):Void {
@@ -165,7 +151,7 @@ class GameRunner implements EngineListener implements GameListener {
public function onCollision(entity:EntityType, with:EntityType):Void {
switch entity {
case EntityType.TANK(tank):
var control = game.getPlayer(tank.playerId).control;
var control = getPlayer(tank.playerId).control;
if (control != null) control.onCollision(with);
case _:
}
@@ -189,7 +175,7 @@ class GameRunner implements EngineListener implements GameListener {
case [TANK(tank), BONUS(bonus)]:
gameEventSignal.emit(GameEvent.DESTROY(BONUS(bonus.id, {tankId: tank.id, score: bonus.config.score})));
case [BULLET(bullet), TANK(tank)]/* | [TANK(tank), BULLET(bullet)]*/:
if (bullet.tankId == tank.id || (!game.config.game.friendlyFire && tank.playerId.team == bullet.playerId.team)) {
if (bullet.tankId == tank.id || (!config.game.friendlyFire && tank.playerId.team == bullet.playerId.team)) {
// Nothing
} else {
if (!tank.protect.active) {
@@ -202,7 +188,7 @@ class GameRunner implements EngineListener implements GameListener {
gameEventSignal.emit(GameEvent.HIT(TANK(tank.id, buildShot(bullet))));
emitTankChange(tank);
} else if (tank.config.downgrade != null) {
tank.config = game.config.getTank(tank.config.downgrade);
tank.config = config.getTank(tank.config.downgrade);
gameEventSignal.emit(GameEvent.HIT(TANK(tank.id, buildShot(bullet))));
emitTankChange(tank);
} else {
@@ -238,14 +224,14 @@ class GameRunner implements EngineListener implements GameListener {
}
private function spawnBonus():Void {
var type = game.config.bonuses[Math.floor(Math.random() * game.config.bonuses.length)].type;
var type = config.bonuses[Math.floor(Math.random() * config.bonuses.length)].type;
var point = {
x: Math.floor(Math.random() * (game.engine.map.gridWidth - 1)),
y: Math.floor(Math.random() * (game.engine.map.gridHeight - 1)),
x: Math.floor(Math.random() * (engine.map.gridWidth - 1)),
y: Math.floor(Math.random() * (engine.map.gridHeight - 1)),
direction: "right",
}
var bonus = builder.buildBonus(point, type);
game.engine.spawn(bonus);
engine.spawn(bonus);
gameEventSignal.emit(GameEvent.SPAWN(BONUS(bonus.id, bonus.rect.clone(), bonus.config.type)));
}
@@ -260,22 +246,22 @@ class GameRunner implements EngineListener implements GameListener {
case "star":
upgradeTank(tank);
case "grenade":
for (t in game.engine.iterTanks(alienTank(tank.playerId.team))) {
for (t in engine.iterTanks(alienTank(tank.playerId.team))) {
gameEventSignal.emit(GameEvent.DESTROY(TANK(t.id, {tankId: tank.id})));
}
case "helmet":
tank.protect.on(bonus.config.duration);
case "clock":
for (t in game.engine.iterTanks(alienTank(tank.playerId.team))) {
for (t in engine.iterTanks(alienTank(tank.playerId.team))) {
t.freezing.on(bonus.config.duration);
t.stop();
gameEventSignal.emit(GameEvent.STOP(TANK(t.id)));
}
case "shovel":
// ToDo: protect eagle/area
var team:Team = game.teams[tank.playerId.team];
var team:Team = teams[tank.playerId.team];
if (team.eagleId > 0) {
var eagle:Eagle = cast(game.engine.entities[team.eagleId], Eagle);
var eagle:Eagle = cast(engine.entities[team.eagleId], Eagle);
eagle.protect.on(bonus.config.duration);
}
case "gun":
@@ -288,7 +274,7 @@ class GameRunner implements EngineListener implements GameListener {
private function upgradeTank(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);
tank.config = config.getTank(tank.config.upgrade);
}
} else {
tank.hits++;
@@ -297,26 +283,27 @@ class GameRunner implements EngineListener implements GameListener {
}
private function changeScore(playerId:PlayerId, score:Int):Void {
var player = game.getPlayer(playerId);
var team = game.getTeam(playerId.team);
var player = getPlayer(playerId);
var team = getTeam(playerId.team);
player.state.score += score;
gameEventSignal.emit(GameEvent.CHANGE(PLAYER_SCORE(playerId, player.state.score)));
gameEventSignal.emit(GameEvent.CHANGE(TEAM_SCORE(playerId.team, game.state.getTeamScore(team.id))));
gameEventSignal.emit(GameEvent.CHANGE(TEAM_SCORE(playerId.team, state.getTeamScore(team.id))));
}
private function changeLife(playerId:PlayerId, life:Int):Void {
var player = game.getPlayer(playerId);
var player = getPlayer(playerId);
player.state.life += life;
gameEventSignal.emit(GameEvent.CHANGE(PLAYER_LIFE(playerId, player.state.life)));
}
private function changeTeamLife(teamId:TeamId, life:Int):Void {
var team = game.getTeam(teamId);
var team = getTeam(teamId);
team.state.life += life;
gameEventSignal.emit(GameEvent.CHANGE(TEAM_LIFE(teamId, team.state.life)));
}
public function onGameEvent(event:GameEvent):Void {
override public function onGameEvent(event:GameEvent):Void {
super.onGameEvent(event);
switch event {
case GameEvent.START(_):
timer = new Timer(10);
@@ -327,38 +314,38 @@ class GameRunner implements EngineListener implements GameListener {
timer = null;
}
case GameEvent.ACTION(tankId, SHOT):
var tank:Tank = cast game.engine.entities.get(tankId);
var player = game.getPlayer(tank.playerId);
var tank:Tank = cast engine.entities.get(tankId);
var player = getPlayer(tank.playerId);
if (!tank.freezing.active && player.bullets < tank.config.bullets) {
var rect = tank.rect;
var point = rect.center.add(new Point(rect.width / 4 * rect.direction.x, rect.height / 4 * rect.direction.y));
var bullet = builder.buildBullet(point, rect.direction, player.id, tank.config.type);
bullet.tank = tank;
bullet.move(bullet.rect.direction);
game.engine.spawn(bullet);
engine.spawn(bullet);
gameEventSignal.emit(GameEvent.SPAWN(BULLET(bullet.id, bullet.rect.clone(), bullet.playerId, bullet.config.piercing)));
}
case GameEvent.ACTION(tankId, MOVE(direction)):
game.engine.move(tankId, direction);
engine.move(tankId, direction);
case GameEvent.ACTION(tankId, STOP):
gameEventSignal.emit(GameEvent.STOP(TANK(tankId)));
game.engine.stop(tankId);
engine.stop(tankId);
case GameEvent.SPAWN(TANK(_, _, playerId, _)):
game.getPlayer(playerId).control.start();
getPlayer(playerId).control.start();
case GameEvent.SPAWN(BULLET(_, _, playerId, _)):
game.getPlayer(playerId).bullets++;
getPlayer(playerId).bullets++;
case GameEvent.DESTROY(EAGLE(id, shot)):
var eagle:Eagle = game.engine.getEntity(id);
var eagle:Eagle = engine.getEntity(id);
eagle.death = true;
if (shot.score != 0) {
var tank:Tank = game.engine.getEntity(shot.tankId);
var tank:Tank = engine.getEntity(shot.tankId);
changeScore(tank.playerId, shot.score);
}
checkComplete();
case GameEvent.DESTROY(TANK(id, shot)):
var tank:Tank = game.engine.getEntity(id);
var team = game.getTeam(tank.playerId.team);
var player = game.getPlayer(tank.playerId);
var tank:Tank = engine.getEntity(id);
var team = getTeam(tank.playerId.team);
var player = getPlayer(tank.playerId);
player.control.stop();
player.tankId = 0; //ToDo: ?
team.onDestroy(player.id);
@@ -378,44 +365,44 @@ class GameRunner implements EngineListener implements GameListener {
spawnBonus();
}
if (shot.score != 0) {
var shooterTank:Tank = game.engine.getEntity(shot.tankId);
var shooterTank:Tank = engine.getEntity(shot.tankId);
changeScore(shooterTank.playerId, shot.score);
}
game.engine.destroy(id);
engine.destroy(id);
case GameEvent.DESTROY(BONUS(id, shot)):
var bonus:Bonus = game.engine.getEntity(id);
var tank:Tank = game.engine.getEntity(shot.tankId);
var bonus:Bonus = engine.getEntity(id);
var tank:Tank = engine.getEntity(shot.tankId);
applyBonus(tank, bonus);
if (shot.score != 0) {
changeScore(tank.playerId, shot.score);
}
game.engine.destroy(id);
engine.destroy(id);
case GameEvent.DESTROY(BULLET(id)):
var bullet:Bullet = game.engine.getEntity(id);
var player = game.getPlayer(bullet.playerId);
var bullet:Bullet = engine.getEntity(id);
var player = getPlayer(bullet.playerId);
player.bullets--;
var side:Line = bullet.rect.getSide(bullet.rect.direction.reverse()).move(
// ToDo: move
new Point(bullet.rect.direction.x * 5, bullet.rect.direction.y * 5)
);
var cells = game.engine.map.grid.getCells(side.setLength(game.engine.map.grid.cellWidth * 3));
var cells = engine.map.grid.getCells(side.setLength(engine.map.grid.cellWidth * 3));
for (cell in cells) {
if (cell.armor > 0) {
var shot = buildShot(bullet);
if (cell.armor == bullet.config.piercing) {
game.engine.destroyCell(cell.cellX, cell.cellY);
var brick = game.engine.map.getBrick(cell.position);
engine.destroyCell(cell.cellX, cell.cellY);
var brick = engine.map.getBrick(cell.position);
gameEventSignal.emit(GameEvent.DESTROY(CELL(brick.id, cell.cellX - brick.cellX * 2, cell.cellY - brick.cellY * 2, shot)));
} else if (cell.armor < bullet.config.piercing) {
var brick = game.engine.map.getBrick(cell.position);
var brick = engine.map.getBrick(cell.position);
for (cell in brick.cells) {
game.engine.destroyCell(cell.cellX, cell.cellY);
engine.destroyCell(cell.cellX, cell.cellY);
}
gameEventSignal.emit(GameEvent.DESTROY(BRICK(brick.id, shot)));
}
}
}
game.engine.destroy(id);
engine.destroy(id);
case _:
}
}

View File

@@ -2,14 +2,12 @@ package ru.m.tankz.game;
import haxework.signal.Signal;
import ru.m.tankz.config.Config;
import ru.m.tankz.engine.IEngine;
import ru.m.tankz.Type;
interface IGame extends GameListener {
public var type(default, null):GameType;
public var teams(default, null):Map<TeamId, Team>;
public var config(default, null):Config;
public var engine(default, null):IEngine;
public var winner(default, null):Null<TeamId>;
public var state(default, null):GameState;
@@ -24,6 +22,8 @@ interface IGame extends GameListener {
public function getTeam(teamId:TeamId):Team;
public function getPlayer(playerId:PlayerId):Player;
public function start():Void;
}
interface GameListener {

View File

@@ -4,21 +4,21 @@ import flash.events.Event;
import flash.Lib;
import ru.m.tankz.game.record.GameRecord;
class GamePlayer {
class GamePlayer extends Game {
private var frame:Int;
private var game:IGame;
private var record:GameRecord;
private var data:Array<EventItem>;
public function new(game:IGame, record:GameRecord) {
public function new(record:GameRecord) {
super(record.state);
frame = 0;
this.game = game;
this.record = record;
this.data = null;
}
public function start():Void {
override public function start():Void {
super.start();
frame = 0;
data = record.events.slice(0);
Lib.current.stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
@@ -34,7 +34,7 @@ class GamePlayer {
for (event in data) {
if (event.frame <= frame) {
events++;
game.gameEventSignal.emit(event.event);
gameEventSignal.emit(event.event);
switch event.event {
case GameEvent.COMPLETE(_, _):
stop();

View File

@@ -163,7 +163,7 @@ presets:
- {<<: *team_human}
- id: bot
spawnInterval: 3000
life: 10
life: 1
players:
- {<<: *bot, index: 0, control: bot-stupid}
- {<<: *bot, index: 1, control: bot-stupid}