[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

@@ -0,0 +1,25 @@
package ru.m.tankz.network;
import ru.m.tankz.proto.core.GameProto;
import ru.m.tankz.game.GameState;
import ru.m.tankz.game.Game;
class NetworkGame extends Game {
private var network:NetworkManager;
public function new(network:NetworkManager) {
super(new GameState(network.game.type, 0, network.game.level));
this.network = network;
network.gameSignal.connect(onGameChange);
}
private function onGameChange(game:GameProto):Void {
}
override public function dispose():Void {
super.dispose();
network.gameSignal.disconnect(onGameChange);
}
}

View File

@@ -1,10 +1,10 @@
package ru.m.tankz.network; package ru.m.tankz.network;
import ru.m.tankz.proto.core.UserProto;
import haxework.signal.Signal; import haxework.signal.Signal;
import ru.m.connect.IConnection; import ru.m.connect.IConnection;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;
import ru.m.tankz.proto.core.GameProto; import ru.m.tankz.proto.core.GameProto;
import ru.m.tankz.proto.core.UserProto;
import ru.m.tankz.proto.game.GameChangeProto; import ru.m.tankz.proto.game.GameChangeProto;
import ru.m.tankz.proto.pack.CreateGameRequest; import ru.m.tankz.proto.pack.CreateGameRequest;
import ru.m.tankz.proto.pack.JoinGameRequest; import ru.m.tankz.proto.pack.JoinGameRequest;

View File

@@ -4,7 +4,6 @@ import flash.events.KeyboardEvent;
import flash.ui.Keyboard; import flash.ui.Keyboard;
import haxework.resources.IResources; import haxework.resources.IResources;
import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameSwitcher;
import haxework.view.IView;
import ru.m.tankz.sound.SoundManager; import ru.m.tankz.sound.SoundManager;
@:template class ClientView extends FrameSwitcher { @:template class ClientView extends FrameSwitcher {
@@ -16,7 +15,6 @@ import ru.m.tankz.sound.SoundManager;
public function init():Void { public function init():Void {
resources.text.put('version', '${Const.VERSION}'); resources.text.put('version', '${Const.VERSION}');
switcher = this; switcher = this;
onSwitch.connect(onFrameSwitch);
} }
public function launch():Void { public function launch():Void {
@@ -31,10 +29,4 @@ import ru.m.tankz.sound.SoundManager;
}); });
change(StartFrame.ID); change(StartFrame.ID);
} }
private function onFrameSwitch(frame:IView<Dynamic>):Void {
if (frame.id == StartFrame.ID) {
soundManager.stopAll();
}
}
} }

View File

@@ -3,9 +3,7 @@ package ru.m.tankz.view;
import haxe.ds.Option; import haxe.ds.Option;
import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameSwitcher;
import haxework.view.VGroupView; import haxework.view.VGroupView;
import ru.m.tankz.game.Game;
import ru.m.tankz.game.GameEvent; import ru.m.tankz.game.GameEvent;
import ru.m.tankz.game.GameRunner;
import ru.m.tankz.game.GameState; import ru.m.tankz.game.GameState;
import ru.m.tankz.game.IGame; import ru.m.tankz.game.IGame;
import ru.m.tankz.game.record.GamePlayer; import ru.m.tankz.game.record.GamePlayer;
@@ -15,6 +13,7 @@ import ru.m.tankz.network.NetworkManager;
import ru.m.tankz.sound.SoundManager; import ru.m.tankz.sound.SoundManager;
import ru.m.tankz.storage.GameStorage; import ru.m.tankz.storage.GameStorage;
import ru.m.tankz.storage.RecordStorage; import ru.m.tankz.storage.RecordStorage;
import ru.m.tankz.Type;
import ru.m.tankz.view.game.GameView; import ru.m.tankz.view.game.GameView;
@:template class GameFrame extends VGroupView implements GameListener { @:template class GameFrame extends VGroupView implements GameListener {
@@ -28,28 +27,16 @@ import ru.m.tankz.view.game.GameView;
@:provide var soundManager:SoundManager; @:provide var soundManager:SoundManager;
@:provide var state:GameState; @:provide var state:GameState;
@:provide var record:GameRecord; @:provide var record:GameRecord;
@:provide("result") var result:GameState; @:provide("next") var nextState:GameState;
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
@:provide var gameStorage:GameStorage; @:provide var gameStorage:GameStorage;
@:provide var recordStorage:RecordStorage; @:provide var recordStorage:RecordStorage;
private var game:IGame; @:provide var game:IGame;
private var runner:GameRunner;
private var recorder:GameRecorder; private var recorder:GameRecorder;
private var player:GamePlayer;
public function onShow():Void { public function onShow():Void {
if (record != null) { gameView.type = game.type;
play(record);
record = null;
} else {
start(state);
}
}
private function buildGame(state:GameState):Void {
gameView.type = state.type;
game = new Game(state);
soundManager.config = game.config; soundManager.config = game.config;
gameView.render.config = game.config; gameView.render.config = game.config;
game.connect(gameView.render); game.connect(gameView.render);
@@ -57,28 +44,16 @@ import ru.m.tankz.view.game.GameView;
if (gameView.panel != null) { if (gameView.panel != null) {
game.connect(gameView.panel); game.connect(gameView.panel);
} }
} // ToDo:
if (!Std.is(game, GamePlayer)) {
private function start(state:GameState):Void {
buildGame(state);
game.connect(this);
recorder = new GameRecorder(); recorder = new GameRecorder();
game.connect(recorder); game.connect(recorder);
runner = new GameRunner(game);
runner.start(state);
} }
game.connect(this);
private function play(record:GameRecord):Void { game.start();
buildGame(record.state);
player = new GamePlayer(game, record);
player.start();
} }
private function stop():Void { private function stop():Void {
if (runner != null) {
runner.dispose();
runner = null;
}
if (game != null) { if (game != null) {
game.dispose(); game.dispose();
game = null; game = null;
@@ -88,19 +63,21 @@ import ru.m.tankz.view.game.GameView;
public function onGameEvent(event:GameEvent):Void { public function onGameEvent(event:GameEvent):Void {
switch event { switch event {
case GameEvent.COMPLETE(state, _): case GameEvent.COMPLETE(state, winner):
// ToDo: // ToDo:
if (recorder != null) {
recordStorage.save(recorder.record); recordStorage.save(recorder.record);
result = state; }
this.state = switch runner.next() { this.state = state;
nextState = switch next(winner) {
case Some(s): case Some(s):
// ToDo: // ToDo:
var progress = gameStorage.get(game.type); var progress = gameStorage.get(game.type);
progress.completeLevel(result.levelId, result.presetId); progress.completeLevel(state.levelId, state.presetId);
gameStorage.set(progress); gameStorage.set(progress);
s; s;
case None: case None:
new GameState(state.type, state.presetId, 0); null;
} }
stop(); stop();
switcher.change(ResultFrame.ID); switcher.change(ResultFrame.ID);
@@ -108,8 +85,22 @@ import ru.m.tankz.view.game.GameView;
} }
} }
// ToDo:
private function next(winner:TeamId):Option<GameState> {
for (rule in game.config.game.complete) {
if (rule.team != null && rule.team != winner) {
return Option.None;
}
}
var level = state.levelId + 1;
if (level >= game.config.game.levels) level = 0;
return Option.Some(new GameState(game.type, state.presetId, level, state));
}
public function onHide():Void { public function onHide():Void {
stop(); stop();
soundManager.stopAll();
recorder = null;
} }
public function close():Void { public function close():Void {

View File

@@ -7,7 +7,9 @@ import haxework.view.LabelView;
import haxework.view.VGroupView; import haxework.view.VGroupView;
import ru.m.tankz.bundle.ILevelBundle; import ru.m.tankz.bundle.ILevelBundle;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
import ru.m.tankz.game.GameRunner;
import ru.m.tankz.game.GameState; import ru.m.tankz.game.GameState;
import ru.m.tankz.game.IGame;
import ru.m.tankz.storage.GameStorage; import ru.m.tankz.storage.GameStorage;
import ru.m.tankz.Type; import ru.m.tankz.Type;
import ru.m.tankz.view.popup.LevelPopup; import ru.m.tankz.view.popup.LevelPopup;
@@ -19,6 +21,7 @@ import ru.m.tankz.view.popup.LevelPopup;
@:view var levels:DataView<LevelId, ButtonView>; @:view var levels:DataView<LevelId, ButtonView>;
@:provide var state:GameState; @:provide var state:GameState;
@:provide var game:IGame;
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
@:provide var levelBundle:ILevelBundle; @:provide var levelBundle:ILevelBundle;
@:provide var storage:GameStorage; @:provide var storage:GameStorage;
@@ -33,6 +36,7 @@ import ru.m.tankz.view.popup.LevelPopup;
private function start(level:LevelConfig, preset:GamePreset):Void { private function start(level:LevelConfig, preset:GamePreset):Void {
state.levelId = level.id; state.levelId = level.id;
state.presetId = preset.id; state.presetId = preset.id;
game = new GameRunner(state);
switcher.change(GameFrame.ID); switcher.change(GameFrame.ID);
} }

View File

@@ -5,7 +5,9 @@ import haxework.view.DataView;
import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameSwitcher;
import haxework.view.LabelView; import haxework.view.LabelView;
import haxework.view.VGroupView; import haxework.view.VGroupView;
import ru.m.tankz.game.GameRunner;
import ru.m.tankz.game.GameState; import ru.m.tankz.game.GameState;
import ru.m.tankz.game.IGame;
import ru.m.tankz.view.common.LifeView; import ru.m.tankz.view.common.LifeView;
@:template class ResultFrame extends VGroupView { @:template class ResultFrame extends VGroupView {
@@ -17,29 +19,33 @@ import ru.m.tankz.view.common.LifeView;
@:provide var frames:FrameSwitcher; @:provide var frames:FrameSwitcher;
@:provide var state:GameState; @:provide var state:GameState;
@:provide("result") var resultState:GameState; @:provide("next") var nextState:GameState;
@:provide var game:IGame;
private function playerViewFactory(index:Int, player:PlayerState) { private function playerViewFactory(index:Int, player:PlayerState) {
var view = new LifeView(); var view = new LifeView();
var playerConfig = resultState.config.getPlayer(player.id); var playerConfig = state.config.getPlayer(player.id);
var tankType = playerConfig.tanks[0].type; var tankType = playerConfig.tanks[0].type;
var tankConfig = resultState.config.getTank(tankType); var tankConfig = state.config.getTank(tankType);
view.tank = tankConfig == null ? 'ba' : tankConfig.skin; view.tank = tankConfig == null ? 'ba' : tankConfig.skin;
view.color = resultState.getPlayerColor(player.id); view.color = state.getPlayerColor(player.id);
view.life = player.frags; view.life = player.frags;
view.score = player.score; view.score = player.score;
return view; return view;
} }
public function onShow() { public function onShow() {
resultView.data = Lambda.array(resultState.players); resultView.data = Lambda.array(state.players);
levelLabel.text = 'Level ${resultState.levelId}'; levelLabel.text = 'Level ${state.levelId}';
nextButton.visible = state != null; nextButton.visible = nextState != null;
} }
private function next() { private function next() {
if (nextState != null) {
game = new GameRunner(nextState);
frames.change(GameFrame.ID); frames.change(GameFrame.ID);
} }
}
private function close() { private function close() {
frames.change(LevelFrame.ID); frames.change(LevelFrame.ID);

View File

@@ -1,5 +1,7 @@
package ru.m.tankz.view.common; package ru.m.tankz.view.common;
import ru.m.tankz.game.record.GamePlayer;
import ru.m.tankz.game.IGame;
import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameSwitcher;
import haxework.view.HGroupView; import haxework.view.HGroupView;
import haxework.view.LabelView; import haxework.view.LabelView;
@@ -18,7 +20,7 @@ import ru.m.tankz.storage.RecordStorage;
@:provide var recordStorage:RecordStorage; @:provide var recordStorage:RecordStorage;
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
@:provide var record:GameRecord; @:provide var game:IGame;
private function set_data(value:GameRecordInfo):GameRecordInfo { private function set_data(value:GameRecordInfo):GameRecordInfo {
if (data != value) { if (data != value) {
@@ -32,7 +34,8 @@ import ru.m.tankz.storage.RecordStorage;
} }
private function play():Void { private function play():Void {
record = recordStorage.read(data.id); var record = recordStorage.read(data.id);
game = new GamePlayer(record);
switcher.change(GameFrame.ID); switcher.change(GameFrame.ID);
} }

View File

@@ -1,27 +1,38 @@
package ru.m.tankz.view.network; package ru.m.tankz.view.network;
import haxework.view.ButtonView;
import haxework.view.frame.FrameSwitcher; import haxework.view.frame.FrameSwitcher;
import haxework.view.list.VListView; import haxework.view.list.VListView;
import haxework.view.TextView; import haxework.view.TextView;
import haxework.view.VGroupView; import haxework.view.VGroupView;
import ru.m.tankz.game.IGame;
import ru.m.tankz.network.NetworkGame;
import ru.m.tankz.network.NetworkManager; import ru.m.tankz.network.NetworkManager;
import ru.m.tankz.proto.core.GameProto; import ru.m.tankz.proto.core.GameProto;
import ru.m.tankz.proto.core.GameStateProto;
import ru.m.tankz.proto.core.UserProto; import ru.m.tankz.proto.core.UserProto;
@:template class GameRoomFrame extends VGroupView { @:template class GameRoomFrame extends VGroupView {
public static inline var ID = "game_room"; public static inline var ID = "game_room";
@:view var start:ButtonView;
@:view var info:TextView; @:view var info:TextView;
@:view var players:VListView<UserProto>; @:view var players:VListView<UserProto>;
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
@:provide var network:NetworkManager; @:provide var network:NetworkManager;
@:provide var game:IGame;
private function refresh(game:GameProto):Void { private function refresh(game:GameProto):Void {
if (game != null) { if (game != null) {
start.visible = game.creator.uuid == network.user.uuid;
info.text = '[${game.creator.name}] ${game.type} ${game.level} (${game.players.length})'; info.text = '[${game.creator.name}] ${game.type} ${game.level} (${game.players.length})';
players.data = game.players; players.data = game.players;
if (game.state == GameStateProto.STARTED) {
this.game = new NetworkGame(network);
switcher.change(GameFrame.ID);
}
} else { } else {
Timer.delay(function() switcher.change(GameListFrame.ID), 1); Timer.delay(function() switcher.change(GameListFrame.ID), 1);
} }

View File

@@ -9,6 +9,12 @@ views:
geometry.margin.bottom: 20 geometry.margin.bottom: 20
skinId: text.header skinId: text.header
text: Game Room text: Game Room
- id: start
$type: haxework.view.ButtonView
skinId: button.simple
text: Start
+onPress: $code:network.startGame()
visible: false
- id: info - id: info
$type: haxework.view.LabelView $type: haxework.view.LabelView
geometry.size.width: 100% geometry.size.width: 100%

View File

@@ -12,7 +12,7 @@ class BotControl extends Control {
private var tank(get, null):Tank; private var tank(get, null):Tank;
private inline function get_tank():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 { 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.Eagle;
import ru.m.tankz.core.Entity; import ru.m.tankz.core.Entity;
import ru.m.tankz.core.EntityType; import ru.m.tankz.core.EntityType;
import ru.m.tankz.game.IGame; import ru.m.tankz.engine.IEngine;
import ru.m.tankz.Type; import ru.m.tankz.Type;
class BotHelper { class BotHelper {
public static function findEagle(team:TeamId, handler:IGame):Null<Eagle> { public static function findEagle(team:TeamId, engine:IEngine):Null<Eagle> {
for (entity in handler.engine.entities) { for (entity in engine.entities) {
switch (EntityTypeResolver.of(entity)) { switch (EntityTypeResolver.of(entity)) {
case EntityType.EAGLE(eagle): case EntityType.EAGLE(eagle):
if (eagle.team != team) { if (eagle.team != team) {

View File

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

View File

@@ -45,7 +45,7 @@ class StupidBotControl extends BotControl {
private function calcTurn():Void { private function calcTurn():Void {
if (handler == null || tank == null) return; 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) { if (eagle != null && Math.random() > 0.5) {
turn(BotHelper.getDirectionTo(tank, eagle)); turn(BotHelper.getDirectionTo(tank, eagle));
} else { } else {

View File

@@ -1,8 +1,9 @@
package ru.m.tankz.control; package ru.m.tankz.control;
import ru.m.tankz.game.GameEvent;
import ru.m.geom.Direction; import ru.m.geom.Direction;
import ru.m.tankz.core.EntityType; 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.game.IGame;
import ru.m.tankz.Type; import ru.m.tankz.Type;
@@ -17,13 +18,15 @@ class Control {
public var playerId(default, null):PlayerId; public var playerId(default, null):PlayerId;
public var tankId(default, default):Int; public var tankId(default, default):Int;
private var handler:IGame; private var handler:IGame;
private var engine:IEngine;
public function new(playerId:PlayerId) { public function new(playerId:PlayerId) {
this.playerId = playerId; this.playerId = playerId;
} }
public function bind(handler:IGame):Void { public function bind(handler:IGame, engine:IEngine):Void {
this.handler = handler; this.handler = handler;
this.engine = engine;
} }
public function action(action:TankAction):Void { public function action(action:TankAction):Void {
@@ -41,5 +44,6 @@ class Control {
public function dispose():Void { public function dispose():Void {
stop(); stop();
handler = null; 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.config.Config;
import ru.m.tankz.core.Entity; import ru.m.tankz.core.Entity;
import ru.m.tankz.core.EntityType; 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.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;
@@ -20,34 +18,18 @@ import ru.m.tankz.Type;
public var type(default, null):GameType; public var type(default, null):GameType;
public var teams(default, null):Map<TeamId, Team>; public var teams(default, null):Map<TeamId, Team>;
public var config(default, null):Config; public var config(default, null):Config;
public var engine(default, null):IEngine;
public var winner(default, null):Null<TeamId>; public var winner(default, null):Null<TeamId>;
public var state(default, null):GameState; public var state(default, null):GameState;
private var builder:EntityBuilder;
@:provide var configBundle:IConfigBundle; @:provide var configBundle:IConfigBundle;
public function new(state:GameState) { public function new(state:GameState) {
this.type = state.type; this.type = state.type;
this.state = state;
this.teams = new Map(); this.teams = new Map();
this.config = configBundle.get(type); this.config = configBundle.get(type);
this.engine = new Engine(config);
this.builder = new EntityBuilder(config);
connect(this); 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 { 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 { public function dispose():Void {
gameEventSignal.dispose(); gameEventSignal.dispose();
engine.dispose();
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,10 @@
package ru.m.tankz.server.game; package ru.m.tankz.server.game;
import ru.m.tankz.game.Game;
import ru.m.tankz.game.GameRunner; import ru.m.tankz.game.GameRunner;
import ru.m.tankz.game.GameState; import ru.m.tankz.game.GameState;
import ru.m.tankz.proto.core.GameProto; import ru.m.tankz.proto.core.GameProto;
class ServerGame extends Game { class ServerGame extends GameRunner {
public var runner(default, null):GameRunner; public var runner(default, null):GameRunner;
public var proto(default, null):GameProto; public var proto(default, null):GameProto;
@@ -13,6 +12,5 @@ class ServerGame extends Game {
public function new(proto:GameProto) { public function new(proto:GameProto) {
super(new GameState(proto.type, 0, proto.level)); super(new GameState(proto.type, 0, proto.level));
this.proto = proto; this.proto = proto;
runner = new GameRunner(this);
} }
} }