[common] add GameDispatcher

This commit is contained in:
2019-03-26 11:23:34 +03:00
parent a948dfbaad
commit e609dafedc
16 changed files with 259 additions and 89 deletions

View File

@@ -24,6 +24,10 @@ abstract PlayerId(Array<Dynamic>) {
private inline function get_index():Int return this[1];
@:from static public inline function fromArray(value:Array<Dynamic>):PlayerId {
return new PlayerId(value[0], value[1]);
}
@:to public inline function toString():String {
return '${team}:${index}';
}

View File

@@ -1,11 +1,10 @@
package ru.m.tankz.game;
import haxework.color.Color;
import haxe.ds.Option;
import haxe.Timer;
import haxework.color.Color;
import haxework.provider.Provider;
import promhx.Deferred;
import promhx.Stream;
import haxework.signal.Signal;
import ru.m.geom.Point;
import ru.m.tankz.bundle.IConfigBundle;
import ru.m.tankz.bundle.ILevelBundle;
@@ -22,7 +21,35 @@ import ru.m.tankz.game.GameState;
import ru.m.tankz.game.Spawner;
import ru.m.tankz.Type;
class Game {
typedef GameListener = {
public function onGameStart(state:GameState):Void;
public function onGameChange(state:GameState):Void;
public function onGameComplete(state:GameState):Void;
}
class GameDispatcher {
public var onGameStart(default, null):Signal<GameState> = new Signal();
public var onGameChange(default, null):Signal<GameState> = new Signal();
public var onGameComplete(default, null):Signal<GameState> = new Signal();
public function new() {
}
public function connect(listener:GameListener) {
onGameStart.connect(listener.onGameStart);
onGameChange.connect(listener.onGameChange);
onGameComplete.connect(listener.onGameComplete);
}
public function disconnect(listener:GameListener) {
onGameStart.disconnect(listener.onGameStart);
onGameChange.disconnect(listener.onGameChange);
onGameComplete.disconnect(listener.onGameComplete);
}
}
class Game extends GameDispatcher {
private static var TAG(default, never):String = 'Game';
@@ -35,13 +62,12 @@ class Game {
public var state(default, null):GameState;
private var points:Array<SpawnPoint>;
private var deferred:Deferred<GameState>;
private var stream:Stream<GameState>;
@:provide var configBundle:IConfigBundle;
@:provide var levelBundle:ILevelBundle;
public function new(type:GameType) {
super();
this.type = type;
this.config = configBundle.get(type);
this.engine = new Engine(config);
@@ -94,10 +120,9 @@ class Game {
entity.rect.direction = point.direction;
}
public function start(state:GameState):Stream<GameState> {
public function start(state:GameState):Void {
this.state = state;
this.loser = null;
this.deferred = new Deferred();
var level:LevelConfig = levelBundle.get(type, config, state.level);
points = level.points != null ? level.points : config.points;
engine.map.setData(level.data);
@@ -129,6 +154,8 @@ class Game {
}
team.spawner.runner = spawn;
}
// ToDo:
state.teams = [for (team in teams.iterator()) team.state];
for (team in teams.iterator()) {
for (player in team.players.iterator()) {
@@ -145,8 +172,8 @@ class Game {
engine.spawn(eagle);
}
}
return stream = deferred.stream();
onGameStart.emit(state);
onGameChange.emit(state);
}
private function spawn(task:SpawnTask):Void {
@@ -158,7 +185,7 @@ class Game {
player.tankId = tank.id;
player.state.tank = tank.config.type;
engine.spawn(tank);
deferred.resolve(state);
onGameChange.emit(state);
}
private function complete():Void {
@@ -169,8 +196,8 @@ class Game {
}
}
Timer.delay(function() {
deferred.resolve(state);
stream.end();
//onGameChange.emit(state);
onGameComplete.emit(state);
}, 5000);
}
@@ -190,7 +217,7 @@ class Game {
if (eagle.death) {
getPlayer(playerId).state.score += eagle.score * (eagle.team == playerId.team ? 0 : 1);
lose(eagle.team);
deferred.resolve(state);
onGameChange.emit(state);
}
case [EntityType.TANK(tank), EntityChange.HIT]:
if (tank.bonus) {
@@ -241,12 +268,12 @@ class Game {
getPlayer(playerId).state.frags++;
getPlayer(playerId).state.score += tank.config.score * (tank.playerId.team == playerId.team ? 0 : 1);
}
deferred.resolve(state);
onGameChange.emit(state);
case EntityType.BONUS(bonus):
if (bonus.config.score > 0 && playerId != null) {
getPlayer(playerId).state.score += bonus.config.score;
}
deferred.resolve(state);
onGameChange.emit(state);
case _:
}
}
@@ -268,11 +295,14 @@ class Game {
}
var level = this.state.level + 1;
if (level >= config.game.levels) level = 0;
return Option.Some(new GameState(type, state.presetId, level, state.players));
return Option.Some(new GameState(type, state.presetId, level, state));
}
public function dispose():Void {
engine.dispose();
onGameStart.dispose();
onGameChange.dispose();
onGameComplete.dispose();
}
private function spawnBonus(?type:BonusType):Void {

View File

@@ -29,22 +29,34 @@ class PlayerState {
}
}
class TeamState {
public var id:TeamId;
public var life:Int;
public function new(id:TeamId, life:Int = 0) {
this.id = id;
this.life = life;
}
}
class GameState {
public var type:GameType;
public var presetId:PresetId;
public var level:Int;
public var players:Array<PlayerState>;
public var teams:Array<TeamState>;
public var preset(get, null):GamePreset;
public var config(get, null):Config;
@:provide private var configBundle:IConfigBundle;
public function new(type:GameType, presetId:PresetId, level:Int = 1, players:Array<PlayerState> = null) {
public function new(type:GameType, presetId:PresetId, level:Int = 1, state:GameState = null) {
this.type = type;
this.presetId = presetId;
this.level = level;
this.players = players == null ? [] : players;
this.players = state == null ? [] : state.players;
this.teams = state == null ? [] : state.teams;
}
private function get_preset():GamePreset {
@@ -56,4 +68,47 @@ class GameState {
private function get_config():Config {
return configBundle.get(type);
}
public function getTeamLife(id:TeamId):Int {
var result = 0;
for (team in teams) {
if (team.id == id) {
result += team.life;
}
}
for (player in players) {
if (player.id.team == id) {
result += player.life;
}
}
return result;
}
public function getTeamScore(id:TeamId):Int {
var result = 0;
for (player in players) {
if (player.id.team == id) {
result += player.score;
}
}
return result;
}
public function getPlayerLife(id:PlayerId):Int {
for (player in players) {
if (player.id == id) {
return player.life;
}
}
return 0;
}
public function getPlayerScore(id:PlayerId):Int {
for (player in players) {
if (player.id == id) {
return player.score;
}
}
return 0;
}
}

View File

@@ -1,5 +1,6 @@
package ru.m.tankz.game;
import ru.m.tankz.game.GameState.TeamState;
import ru.m.tankz.Type;
import ru.m.tankz.config.Config;
@@ -9,14 +10,13 @@ class Team {
public var config(default, null):TeamConfig;
public var spawner(default, null):Spawner;
public var players(default, null):Map<Int, Player>;
public var life(default, default):Int;
public var isAlive(get, null):Bool;
public var score(get, null):Int;
public var eagleId(default, default):Int;
private var active(default, default):Int;
public var state(default, default):TeamState;
public function new(config:TeamConfig, points:Array<SpawnPoint>) {
public function new(config:TeamConfig, points:Array<SpawnPoint>, state:TeamState = null) {
this.id = config.id;
this.config = config;
this.players = new Map();
@@ -24,13 +24,14 @@ class Team {
var player:Player = new Player(id, playerConfig);
this.players[playerConfig.index] = player;
}
this.life = config.life;
this.spawner = new Spawner(config, points);
this.state = state == null ? new TeamState(id) : state;
this.state.life = config.life;
}
public function tryRespawn(playerId:PlayerId):Bool {
var player:Player = players[playerId.index];
var result = player.state.life > 0 || life > active;
var result = player.state.life > 0 || state.life > active;
active++;
return result;
}
@@ -41,13 +42,13 @@ class Team {
if (player.state.life > 0) {
player.state.life--;
} else {
life--;
state.life--;
}
}
// ToDo: eagle state?
private function get_isAlive():Bool {
if (life > 0) {
if (state.life > 0) {
return true;
}
if (spawner.active) {

View File

@@ -20,13 +20,13 @@ bricks:
player:
default: &player
protect: 3
life: 10
protect: 2
tanks:
- {type: default, rate: 1}
team:
base: &team
life: 10
players:
- {<<: *player, index: 0}