From fc18e2e0b72fdaa13bd99e9ee2cbb6dd8df51edf Mon Sep 17 00:00:00 2001 From: shmyga Date: Tue, 30 Apr 2019 12:17:28 +0300 Subject: [PATCH] [common] add GameRunner --- .../haxe/ru/m/tankz/view/common/GameFrame.hx | 28 +- src/common/haxe/ru/m/tankz/control/Control.hx | 3 +- src/common/haxe/ru/m/tankz/game/Game.hx | 345 +---------------- src/common/haxe/ru/m/tankz/game/GameEvent.hx | 2 + src/common/haxe/ru/m/tankz/game/GameRunner.hx | 363 ++++++++++++++++++ src/common/haxe/ru/m/tankz/game/GameTracer.hx | 12 + src/common/haxe/ru/m/tankz/game/IGame.hx | 11 +- 7 files changed, 411 insertions(+), 353 deletions(-) create mode 100644 src/common/haxe/ru/m/tankz/game/GameRunner.hx create mode 100644 src/common/haxe/ru/m/tankz/game/GameTracer.hx diff --git a/src/client/haxe/ru/m/tankz/view/common/GameFrame.hx b/src/client/haxe/ru/m/tankz/view/common/GameFrame.hx index 4df7343..05b3d30 100644 --- a/src/client/haxe/ru/m/tankz/view/common/GameFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/common/GameFrame.hx @@ -1,13 +1,15 @@ package ru.m.tankz.view.common; +import ru.m.tankz.game.GameTracer; import flash.events.Event; import haxe.ds.Option; -import haxe.Timer; import haxework.view.frame.FrameSwitcher; import haxework.view.GroupView; import ru.m.tankz.game.Game; import ru.m.tankz.game.GameEvent; +import ru.m.tankz.game.GameRunner; import ru.m.tankz.game.GameState; +import ru.m.tankz.game.IGame; import ru.m.tankz.network.NetworkManager; import ru.m.tankz.render.Render; import ru.m.tankz.sound.SoundManager; @@ -28,8 +30,8 @@ class GameFrame extends GroupView implements GameListener { @:provide var switcher:FrameSwitcher; @:provide var storage:GameStorage; - private var game:Game; - private var timer:Timer; + private var game:IGame; + private var runner:GameRunner; private function get_render():Render { throw "Not implemented"; @@ -51,19 +53,19 @@ class GameFrame extends GroupView implements GameListener { if (panel != null) { game.connect(panel); } - game.start(state); - timer = new Timer(10); - timer.run = updateEngine; + //game.connect(new GameTracer()); + runner = new GameRunner(game); + runner.start(state); content.addEventListener(Event.ENTER_FRAME, _redraw); render.draw(game.engine); } private function stop():Void { - if (timer != null) { - timer.stop(); - timer = null; - } content.removeEventListener(Event.ENTER_FRAME, _redraw); + if (runner != null) { + runner.dispose(); + runner = null; + } if (game != null) { game.dispose(); game = null; @@ -75,7 +77,7 @@ class GameFrame extends GroupView implements GameListener { switch event { case GameEvent.COMPLETE(state, winner): result = state; - this.state = switch game.next() { + this.state = switch runner.next() { case Some(s): // ToDo: var progress = storage.get(game.type); @@ -94,10 +96,6 @@ class GameFrame extends GroupView implements GameListener { stop(); } - private function updateEngine():Void { - game.engine.update(); - } - private function _redraw(_):Void { render.draw(game.engine); } diff --git a/src/common/haxe/ru/m/tankz/control/Control.hx b/src/common/haxe/ru/m/tankz/control/Control.hx index 3034fd9..ca882fe 100644 --- a/src/common/haxe/ru/m/tankz/control/Control.hx +++ b/src/common/haxe/ru/m/tankz/control/Control.hx @@ -1,5 +1,6 @@ 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.game.IGame; @@ -27,7 +28,7 @@ class Control { public function action(action:TankAction):Void { if (tankId != 0 && handler != null) { - handler.action(tankId, action); + handler.gameEventSignal.emit(GameEvent.ACTION(tankId, action)); } } diff --git a/src/common/haxe/ru/m/tankz/game/Game.hx b/src/common/haxe/ru/m/tankz/game/Game.hx index 53a1b93..4dc560d 100644 --- a/src/common/haxe/ru/m/tankz/game/Game.hx +++ b/src/common/haxe/ru/m/tankz/game/Game.hx @@ -1,29 +1,17 @@ package ru.m.tankz.game; -import ru.m.tankz.game.GameEvent.ChangeEvent; -import haxe.ds.Option; -import haxe.Timer; -import ru.m.geom.Point; import ru.m.tankz.bundle.IConfigBundle; import ru.m.tankz.config.Config; -import ru.m.tankz.control.Control; -import ru.m.tankz.control.Controller; -import ru.m.tankz.control.IControlFactory; -import ru.m.tankz.core.Bonus; -import ru.m.tankz.core.Eagle; -import ru.m.tankz.core.Entity; import ru.m.tankz.core.EntityType; -import ru.m.tankz.core.Tank; import ru.m.tankz.engine.Engine; import ru.m.tankz.game.GameState; -import ru.m.tankz.game.Spawner; import ru.m.tankz.Type; interface GameListener { public function onGameEvent(event:GameEvent):Void; } -@:dispatcher(GameListener) class Game implements IGame implements EngineListener implements GameListener { +@:dispatcher(GameListener) class Game implements IGame implements GameListener { private static var TAG(default, never):String = "Game"; @@ -35,35 +23,16 @@ interface GameListener { public var state(default, null):GameState; - private var points:Array; - @:provide var configBundle:IConfigBundle; - @:provide var controlFactory:IControlFactory; public function new(type:GameType) { this.type = type; + this.teams = new Map(); this.config = configBundle.get(type); this.engine = new Engine(config); - engine.connect(this); connect(this); } - public function action(tankId:Int, action:TankAction):Void { - if (!engine.entities.exists(tankId)) return; - var tank:Tank = cast engine.entities.get(tankId); - switch (action) { - case MOVE(direction): - tank.move(direction); - case STOP: - tank.stop(); - case SHOT: - var bullet = tank.shot(); - if (bullet != null) { - gameEventSignal.emit(GameEvent.SPAWN(BULLET(bullet))); - } - } - } - public inline function getTeam(teamId:TeamId):Team { return teams[teamId]; } @@ -72,323 +41,33 @@ interface GameListener { return teams[playerId.team].players[playerId.index]; } - private function buildTank(task:SpawnTask):Tank { - var player = getPlayer(task.playerId); - var tankType:TankType = if (task.tankType != null) { - task.tankType; - } else { - var spawns:Array = player.config.tanks; - var spawn:TankSpawn = spawns[Math.floor(Math.random() * spawns.length)]; - spawn.type; - } - var tankConfig:TankConfig = config.getTank(tankType); - var tank = new Tank(task.playerId, tankConfig); - tank.color = state.getPlayerColor(player.id); - tank.bonus = Math.random() < player.config.bonus; - applyPoint(tank, task.point); - if (player.config.protect > 0) { - tank.protect.on(player.config.protect); - } - return tank; - } - - private function applyPoint(entity:Entity, point:SpawnPoint):Void { - entity.rect.center = new Point((point.x + 1) * engine.map.cellWidth, (point.y + 1) * engine.map.cellHeight); - entity.rect.direction = point.direction; - } - - public function start(state:GameState):Void { - this.state = state; - this.winner = null; - var level:LevelConfig = state.level; - points = level.points != null ? level.points : config.points; - engine.map.setData(level.data); - teams = new Map(); - 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; - for (player in team.players.iterator()) { - var control = controlFactory.build(player.id, AController.fromString(player.config.control)); - L.d(TAG, 'control(${player.id} - ${control})'); - if (control != null) { - player.control = control; - player.control.bind(this); - } - } - team.spawner.runner = spawn; - } - - for (team in teams.iterator()) { - for (player in team.players.iterator()) { - if (team.tryRespawn(player.id)) { - team.spawner.push(player.id, player.state.tank); - } - } - var eaglePoint = team.spawner.getPoint("eagle"); - if (eaglePoint != null) { - var eagle = new Eagle(team.id, team.config.eagle); - eagle.color = config.getColor(new PlayerId(eagle.team, -1)); - team.eagleId = eagle.id; - applyPoint(eagle, eaglePoint); - gameEventSignal.emit(GameEvent.SPAWN(EAGLE(eagle))); - } - } - gameEventSignal.emit(GameEvent.START(state)); - } - - private function spawn(task:SpawnTask):Void { - L.d(TAG, 'spawn(${task})'); - var team = getTeam(task.playerId.team); - var player = getPlayer(task.playerId); - player.tankId = 0; - var tank = buildTank(task); - player.tankId = tank.id; - player.state.tank = tank.config.type; - gameEventSignal.emit(GameEvent.SPAWN(TANK(tank))); - } - - private function checkComplete():Void { - var actives:Array = []; - for (team in teams.iterator()) { - if (team.isAlive) { - if (team.eagleId > 0) { - if (!cast(engine.entities[team.eagleId], Eagle).death) { - actives.push(team.id); - } - } else { - actives.push(team.id); - } - } - } - if (actives.length == 1) { - win(actives[0]); - } - if (actives.length == 0) { - win(null); - } - } - - private function complete():Void { - 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(state, getTeam(winner))); - }, 5000); - } - - public function onCollision(entity:EntityType, with:EntityType):Void { - switch entity { - case EntityType.TANK(tank): - var control = getPlayer(tank.playerId).control; - if (control != null) control.onCollision(with); - case _: - } - switch [entity, with] { - case [TANK(tank), TANK(other_tank)]: - tank.rect.lean(other_tank.rect); - case [TANK(tank), EAGLE(eagle)]: - tank.rect.lean(eagle.rect); - case [BULLET(bullet), BULLET(other_bullet)]: - gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet))); - gameEventSignal.emit(GameEvent.DESTROY(BULLET(other_bullet))); - case [BULLET(bullet), CELL(cell)]: - gameEventSignal.emit(GameEvent.HIT(CELL(cell, bullet.tank, bullet))); - gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet))); - case [TANK(tank), BONUS(bonus)]: - gameEventSignal.emit(GameEvent.DESTROY(BONUS(bonus, tank, bonus.config.score))); - case [BULLET(bullet), TANK(tank)]/* | [TANK(tank), BULLET(bullet)]*/: - 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--; - if (tank.bonus) { - tank.bonus = false; - spawnBonus(); - } - gameEventSignal.emit(GameEvent.HIT(TANK(tank, bullet.tank, bullet))); - } else if (tank.config.downgrade != null) { - tank.config = engine.config.getTank(tank.config.downgrade); - gameEventSignal.emit(GameEvent.HIT(TANK(tank, bullet.tank, bullet))); - } else { - var score = tank.config.score; - if (tank.playerId.team == bullet.playerId.team) { - score = Math.round(score * -0.5); - } - gameEventSignal.emit(GameEvent.DESTROY(TANK(tank, bullet.tank, bullet, score))); - } - } - gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet))); - } - case [BULLET(bullet), EAGLE(eagle)]: - if (!eagle.protect.active) { - gameEventSignal.emit(GameEvent.DESTROY(EAGLE(eagle, bullet.tank, bullet, eagle.score))); - } - gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet))); - case _: - } - } - - private function win(teamId:TeamId):Void { - winner = teamId; - complete(); - } - - public function onAction(tankId:Int, action:TankAction):Void { - this.action(tankId, action); - } - - public function next():Option { - for (rule in config.game.complete) { - if (rule.team != null && rule.team != winner) { - return Option.None; - } - } - var level = this.state.levelId + 1; - if (level >= config.game.levels) level = 0; - return Option.Some(new GameState(type, state.presetId, level, state)); - } - - public function dispose():Void { - engine.dispose(); - gameEventSignal.dispose(); - } - - 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); - bonus.rect.x = Math.round(Math.random() * engine.map.width / engine.map.cellWidth) * engine.map.cellWidth; - bonus.rect.y = Math.round(Math.random() * engine.map.height/ engine.map.cellHeight) * engine.map.cellHeight; - gameEventSignal.emit(GameEvent.SPAWN(BONUS(bonus))); - } - - 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 { - switch (bonus.config.type) { - case "life": - changeLife(tank.playerId, 1); - case "star": - upgradeTank(tank); - case "grenade": - for (t in engine.iterTanks(alienTank(tank.playerId.team))) { - gameEventSignal.emit(GameEvent.DESTROY(TANK(t, tank, null, 0))); - } - case "helmet": - tank.protect.on(bonus.config.duration); - case "clock": - for (t in engine.iterTanks(alienTank(tank.playerId.team))) { - t.freezing.on(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); - eagle.protect.on(bonus.config.duration); - } - case "gun": - upgradeTank(tank, 5); - case _: - gameEventSignal.emit(GameEvent.DESTROY(TANK(tank, null, null, 0))); // :-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 { - tank.hits++; - } - } - - private function changeScore(playerId:PlayerId, score:Int):Void { - var player = getPlayer(playerId); - var team = getTeam(playerId.team); - player.state.score += score; - gameEventSignal.emit(GameEvent.CHANGE(PLAYER_SCORE(player, player.state.score))); - gameEventSignal.emit(GameEvent.CHANGE(TEAM_SCORE(team, state.getTeamScore(team.id)))); - } - - private function changeLife(playerId:PlayerId, life:Int):Void { - var player = getPlayer(playerId); - player.state.life += life; - gameEventSignal.emit(GameEvent.CHANGE(PLAYER_LIFE(player, player.state.life))); - } - - private function changeTeamLife(teamId:TeamId, life:Int):Void { - var team = getTeam(teamId); - team.state.life += life; - gameEventSignal.emit(GameEvent.CHANGE(TEAM_LIFE(team, team.state.life))); - } - public function onGameEvent(event:GameEvent):Void { switch event { + case GameEvent.START(state): + this.state = state; + case GameEvent.COMPLETE(state, team): + this.state = state; + this.winner = team.id; case GameEvent.SPAWN(EAGLE(eagle)): engine.spawn(eagle); case GameEvent.SPAWN(TANK(tank)): engine.spawn(tank); - getPlayer(tank.playerId).control.start(); case GameEvent.SPAWN(BULLET(bullet)): engine.spawn(bullet); case GameEvent.SPAWN(BONUS(bonus)): engine.spawn(bonus); - case GameEvent.DESTROY(EAGLE(eagle, who, wherewith, score)): - eagle.death = true; - if (score != 0) { - changeScore(who.playerId, score); - } - checkComplete(); case GameEvent.DESTROY(TANK(tank, who, wherewith, score)): engine.destroy(tank); - var team = getTeam(tank.playerId.team); - var player = getPlayer(tank.playerId); - player.control.stop(); - player.tankId = 0; //ToDo: ? - team.onDestroy(player.id); - if (player.state.life > 0) { - changeLife(player.id, -1); - } else if (team.state.life > 0) { - changeTeamLife(team.id, -1); - } - var respawn:Bool = team.tryRespawn(player.id); - if (respawn) { - team.spawner.push(player.id); - } - if (!team.isAlive) { - checkComplete(); - } - if (tank.bonus && wherewith != null) { - spawnBonus(); - } - if (score != 0) { - changeScore(who.playerId, score); - } - case GameEvent.DESTROY(CELL(cell, who, wherewith)): - case GameEvent.DESTROY(BONUS(bonus, who, score)): engine.destroy(bonus); - applyBonus(who, bonus); - if (score != 0) { - changeScore(who.playerId, score); - } case GameEvent.DESTROY(BULLET(bullet)): engine.destroy(bullet); - var tank:Tank = bullet.tank; - if (tank != null) { - tank.onDestroyBullet(); - } case _: } } + + public function dispose():Void { + gameEventSignal.dispose(); + engine.dispose(); + } } diff --git a/src/common/haxe/ru/m/tankz/game/GameEvent.hx b/src/common/haxe/ru/m/tankz/game/GameEvent.hx index b2752e4..63b8b4e 100644 --- a/src/common/haxe/ru/m/tankz/game/GameEvent.hx +++ b/src/common/haxe/ru/m/tankz/game/GameEvent.hx @@ -1,5 +1,6 @@ package ru.m.tankz.game; +import ru.m.tankz.control.Control.TankAction; import ru.m.tankz.core.Bonus; import ru.m.tankz.core.Eagle; import ru.m.tankz.core.Tank; @@ -40,4 +41,5 @@ enum GameEvent { DESTROY(event:DestroyEvent); CHANGE(event:ChangeEvent); COMPLETE(state:GameState, winner:Team); + ACTION(tankId:Int, action:TankAction); } diff --git a/src/common/haxe/ru/m/tankz/game/GameRunner.hx b/src/common/haxe/ru/m/tankz/game/GameRunner.hx new file mode 100644 index 0000000..559fab1 --- /dev/null +++ b/src/common/haxe/ru/m/tankz/game/GameRunner.hx @@ -0,0 +1,363 @@ +package ru.m.tankz.game; + +import haxe.ds.Option; +import haxe.Timer; +import haxework.signal.Signal; +import ru.m.geom.Point; +import ru.m.tankz.config.Config; +import ru.m.tankz.control.Control; +import ru.m.tankz.control.Controller; +import ru.m.tankz.control.IControlFactory; +import ru.m.tankz.core.Bonus; +import ru.m.tankz.core.Eagle; +import ru.m.tankz.core.Entity; +import ru.m.tankz.core.EntityType; +import ru.m.tankz.core.Tank; +import ru.m.tankz.engine.Engine; +import ru.m.tankz.game.Game; +import ru.m.tankz.game.Spawner; +import ru.m.tankz.Type; + +class GameRunner implements EngineListener implements GameListener { + @:provide var controlFactory:IControlFactory; + + private var game(default, null):IGame; + private var gameEventSignal(get, null):Signal; + private var points:Array; + + private var timer:Timer; + + public function new(game:IGame) { + this.game = game; + this.game.connect(this); + this.game.engine.connect(this); + } + + private inline function get_gameEventSignal():Signal { + return game.gameEventSignal; + } + + public function dispose():Void { + game.disconnect(this); + game.engine.disconnect(this); + } + + private function updateEngine():Void { + game.engine.update(); + } + + private function applyAction(tankId:Int, action:TankAction):Void { + if (!game.engine.entities.exists(tankId)) return; + var tank:Tank = cast game.engine.entities.get(tankId); + switch (action) { + case MOVE(direction): + tank.move(direction); + case STOP: + tank.stop(); + case SHOT: + var bullet = tank.shot(); + if (bullet != null) { + gameEventSignal.emit(GameEvent.SPAWN(BULLET(bullet))); + } + } + } + + private function buildTank(task:SpawnTask):Tank { + var player = game.getPlayer(task.playerId); + var tankType:TankType = if (task.tankType != null) { + task.tankType; + } else { + var spawns:Array = player.config.tanks; + var spawn:TankSpawn = spawns[Math.floor(Math.random() * spawns.length)]; + spawn.type; + } + var tankConfig:TankConfig = game.config.getTank(tankType); + var tank = new Tank(task.playerId, tankConfig); + tank.color = game.state.getPlayerColor(player.id); + tank.bonus = Math.random() < player.config.bonus; + applyPoint(tank, task.point); + if (player.config.protect > 0) { + tank.protect.on(player.config.protect); + } + return tank; + } + + private function applyPoint(entity:Entity, point:SpawnPoint):Void { + entity.rect.center = new Point((point.x + 1) * game.engine.map.cellWidth, (point.y + 1) * game.engine.map.cellHeight); + entity.rect.direction = point.direction; + } + + public function start(state:GameState):Void { + var level:LevelConfig = state.level; + points = level.points != null ? level.points : game.config.points; + game.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]); + game.teams[team.id] = team; + 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); + } + } + team.spawner.runner = spawn; + } + for (team in game.teams.iterator()) { + for (player in team.players.iterator()) { + if (team.tryRespawn(player.id)) { + team.spawner.push(player.id, player.state.tank); + } + } + var eaglePoint = team.spawner.getPoint("eagle"); + if (eaglePoint != null) { + var eagle = new Eagle(team.id, team.config.eagle); + eagle.color = game.config.getColor(new PlayerId(eagle.team, -1)); + team.eagleId = eagle.id; + applyPoint(eagle, eaglePoint); + gameEventSignal.emit(GameEvent.SPAWN(EAGLE(eagle))); + } + } + gameEventSignal.emit(GameEvent.START(state)); + timer = new Timer(10); + timer.run = updateEngine; + } + + public function next():Option { + 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 team = game.getTeam(task.playerId.team); + var player = game.getPlayer(task.playerId); + player.tankId = 0; + var tank = buildTank(task); + player.tankId = tank.id; + player.state.tank = tank.config.type; + gameEventSignal.emit(GameEvent.SPAWN(TANK(tank))); + } + + private function checkComplete():Void { + var actives:Array = []; + for (team in game.teams.iterator()) { + if (team.isAlive) { + if (team.eagleId > 0) { + if (!cast(game.engine.entities[team.eagleId], Eagle).death) { + actives.push(team.id); + } + } else { + actives.push(team.id); + } + } + } + if (actives.length == 1) { + complete(actives[0]); + } + if (actives.length == 0) { + complete(null); + } + } + + private function complete(winner:TeamId):Void { + for (team in game.teams.iterator()) { + for (player in team.players) { + player.control.action(TankAction.STOP); + player.control.dispose(); + } + } + Timer.delay(function() { + gameEventSignal.emit(GameEvent.COMPLETE(game.state, game.getTeam(winner))); + }, 5000); + } + + public function onCollision(entity:EntityType, with:EntityType):Void { + switch entity { + case EntityType.TANK(tank): + var control = game.getPlayer(tank.playerId).control; + if (control != null) control.onCollision(with); + case _: + } + switch [entity, with] { + case [TANK(tank), TANK(other_tank)]: + tank.rect.lean(other_tank.rect); + case [TANK(tank), EAGLE(eagle)]: + tank.rect.lean(eagle.rect); + case [BULLET(bullet), BULLET(other_bullet)]: + gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet))); + gameEventSignal.emit(GameEvent.DESTROY(BULLET(other_bullet))); + case [BULLET(bullet), CELL(cell)]: + gameEventSignal.emit(GameEvent.HIT(CELL(cell, bullet.tank, bullet))); + gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet))); + case [TANK(tank), BONUS(bonus)]: + gameEventSignal.emit(GameEvent.DESTROY(BONUS(bonus, tank, 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)) { + // Nothing + } else { + if (!tank.protect.active) { + if (tank.hits > 0) { + tank.hits--; + if (tank.bonus) { + tank.bonus = false; + spawnBonus(); + } + gameEventSignal.emit(GameEvent.HIT(TANK(tank, bullet.tank, bullet))); + } else if (tank.config.downgrade != null) { + tank.config = game.config.getTank(tank.config.downgrade); + gameEventSignal.emit(GameEvent.HIT(TANK(tank, bullet.tank, bullet))); + } else { + var score = tank.config.score; + if (tank.playerId.team == bullet.playerId.team) { + score = Math.round(score * -0.5); + } + gameEventSignal.emit(GameEvent.DESTROY(TANK(tank, bullet.tank, bullet, score))); + } + } + gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet))); + } + case [BULLET(bullet), EAGLE(eagle)]: + if (!eagle.protect.active) { + gameEventSignal.emit(GameEvent.DESTROY(EAGLE(eagle, bullet.tank, bullet, eagle.score))); + } + gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet))); + case _: + } + } + + private function spawnBonus(?type:BonusType):Void { + var bonusConfig:BonusConfig = type != null ? game.config.getBonus(type) : game.config.bonuses[Math.floor(Math.random() * game.config.bonuses.length)]; + var bonus = new Bonus(bonusConfig); + bonus.rect.x = Math.round(Math.random() * game.engine.map.width / game.engine.map.cellWidth) * game.engine.map.cellWidth; + bonus.rect.y = Math.round(Math.random() * game.engine.map.height/ game.engine.map.cellHeight) * game.engine.map.cellHeight; + gameEventSignal.emit(GameEvent.SPAWN(BONUS(bonus))); + } + + 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 { + switch (bonus.config.type) { + case "life": + changeLife(tank.playerId, 1); + case "star": + upgradeTank(tank); + case "grenade": + for (t in game.engine.iterTanks(alienTank(tank.playerId.team))) { + gameEventSignal.emit(GameEvent.DESTROY(TANK(t, tank, null, 0))); + } + case "helmet": + tank.protect.on(bonus.config.duration); + case "clock": + for (t in game.engine.iterTanks(alienTank(tank.playerId.team))) { + t.freezing.on(bonus.config.duration); + } + case "shovel": + // ToDo: protect eagle/area + var team:Team = game.teams[tank.playerId.team]; + if (team.eagleId > 0) { + var eagle:Eagle = cast(game.engine.entities[team.eagleId], Eagle); + eagle.protect.on(bonus.config.duration); + } + case "gun": + upgradeTank(tank, 5); + case _: + gameEventSignal.emit(GameEvent.DESTROY(TANK(tank, null, null, 0))); // :-D + } + } + + 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); + } + } else { + tank.hits++; + } + } + + private function changeScore(playerId:PlayerId, score:Int):Void { + var player = game.getPlayer(playerId); + var team = game.getTeam(playerId.team); + player.state.score += score; + gameEventSignal.emit(GameEvent.CHANGE(PLAYER_SCORE(player, player.state.score))); + gameEventSignal.emit(GameEvent.CHANGE(TEAM_SCORE(team, game.state.getTeamScore(team.id)))); + } + + private function changeLife(playerId:PlayerId, life:Int):Void { + var player = game.getPlayer(playerId); + player.state.life += life; + gameEventSignal.emit(GameEvent.CHANGE(PLAYER_LIFE(player, player.state.life))); + } + + private function changeTeamLife(teamId:TeamId, life:Int):Void { + var team = game.getTeam(teamId); + team.state.life += life; + gameEventSignal.emit(GameEvent.CHANGE(TEAM_LIFE(team, team.state.life))); + } + + public function onGameEvent(event:GameEvent):Void { + switch event { + case GameEvent.START(_): + + case GameEvent.COMPLETE(_, _): + if (timer != null) { + timer.stop(); + timer = null; + } + case GameEvent.ACTION(tankId, action): + applyAction(tankId, action); + case GameEvent.SPAWN(TANK(tank)): + game.getPlayer(tank.playerId).control.start(); + case GameEvent.DESTROY(EAGLE(eagle, who, wherewith, score)): + eagle.death = true; + if (score != 0) { + changeScore(who.playerId, score); + } + checkComplete(); + case GameEvent.DESTROY(TANK(tank, who, wherewith, score)): + var team = game.getTeam(tank.playerId.team); + var player = game.getPlayer(tank.playerId); + player.control.stop(); + player.tankId = 0; //ToDo: ? + team.onDestroy(player.id); + if (player.state.life > 0) { + changeLife(player.id, -1); + } else if (team.state.life > 0) { + changeTeamLife(team.id, -1); + } + var respawn:Bool = team.tryRespawn(player.id); + if (respawn) { + team.spawner.push(player.id); + } + if (!team.isAlive) { + checkComplete(); + } + if (tank.bonus && wherewith != null) { + spawnBonus(); + } + if (score != 0) { + changeScore(who.playerId, score); + } + case GameEvent.DESTROY(BONUS(bonus, who, score)): + applyBonus(who, bonus); + if (score != 0) { + changeScore(who.playerId, score); + } + case GameEvent.DESTROY(BULLET(bullet)): + var tank:Tank = bullet.tank; + if (tank != null) { + tank.onDestroyBullet(); + } + case _: + } + } +} diff --git a/src/common/haxe/ru/m/tankz/game/GameTracer.hx b/src/common/haxe/ru/m/tankz/game/GameTracer.hx new file mode 100644 index 0000000..bbad7fc --- /dev/null +++ b/src/common/haxe/ru/m/tankz/game/GameTracer.hx @@ -0,0 +1,12 @@ +package ru.m.tankz.game; + +import ru.m.tankz.game.Game; + +class GameTracer implements GameListener { + public function new() { + } + + public function onGameEvent(event:GameEvent):Void { + L.d("Game", Std.string(event)); + } +} diff --git a/src/common/haxe/ru/m/tankz/game/IGame.hx b/src/common/haxe/ru/m/tankz/game/IGame.hx index 0fb2934..c92f9ca 100644 --- a/src/common/haxe/ru/m/tankz/game/IGame.hx +++ b/src/common/haxe/ru/m/tankz/game/IGame.hx @@ -1,22 +1,25 @@ package ru.m.tankz.game; import haxework.signal.Signal; -import ru.m.tankz.control.Control; +import ru.m.tankz.config.Config; import ru.m.tankz.engine.Engine; import ru.m.tankz.game.Game; import ru.m.tankz.Type; interface IGame { + public var type(default, null):GameType; + public var teams(default, null):Map; + public var config(default, null):Config; public var engine(default, null):Engine; + public var winner(default, null):Null; + public var state(default, null):GameState; public var gameEventSignal(default, null):Signal; public function connect(listener:GameListener):Void; public function disconnect(listener:GameListener):Void; - public function start(state:GameState):Void; - - public function action(tankId:Int, action:TankAction):Void; + public function dispose():Void; public function getTeam(teamId:TeamId):Team;