[common] game state refactored

This commit is contained in:
2018-02-12 22:26:25 +03:00
parent d279d52b09
commit f68e5d160f
14 changed files with 266 additions and 257 deletions

View File

@@ -51,7 +51,7 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand
render.draw(game.engine); render.draw(game.engine);
timer = new Timer(10); timer = new Timer(10);
timer.run = updateEngine; timer.run = updateEngine;
state.text = stateString(s); state.text = stateString(game);
} }
private function stop():Void { private function stop():Void {
@@ -68,20 +68,18 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand
render.reset(); render.reset();
} }
private function stateString(state:GameState):String { private function stateString(game:Game):String {
var result:Array<String> = []; var result:Array<String> = [];
result.push('Level: ${state.level}'); result.push('Level: ${game.state.level}');
for (teamId in state.teams.keys()) { for (team in game.teams) {
var ts:TeamState = state.teams[teamId]; if (game.state.loser == team.id) {
if (ts.lose) { result.push('${team.id}: LOSE');
result.push('${teamId}: LOSE'); } else if (team.life > -1) {
} else if (ts.life > -1) { result.push('${team.id}: ${team.life}');
result.push('${teamId}: ${ts.life}');
} else { } else {
for (index in ts.players.keys()) { for (player in team.players) {
var ps:PlayerState = ts.players[index]; if (player.life > -1) {
if (ps.life > -1) { result.push('${player.id.team}${player.id.index}: ${player.life}');
result.push('${teamId}${index}: ${ps.life}');
} }
} }
} }
@@ -90,14 +88,14 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand
} }
private function onGameStateChange(s:GameState):GameState { private function onGameStateChange(s:GameState):GameState {
state.text = stateString(s); state.text = stateString(game);
return s; return s;
} }
private function onGameComplete(result:Option<GameState>):Void { private function onGameComplete(result:Option<GameState>):Void {
switch (result) { switch (result) {
case Option.Some(s): case Option.Some(s):
state.text = stateString(s); state.text = stateString(game);
case Option.None: case Option.None:
} }
switch (game.next()) { switch (game.next()) {

View File

@@ -47,14 +47,8 @@ class StartFrame extends VGroupView implements ViewBuilder implements StartFrame
} }
} }
private function startGame(type:GameType, mode:GameMode):Void { private function startGame(type:GameType, presetId:PresetId):Void {
switch (type) { Provider.set(GameState, new GameState(type, presetId));
case ClassicGame.TYPE:
Provider.set(GameState, ClassicGame.buildState(0, mode));
Provider.get(IFrameSwitcher).change(LevelFrame.ID);
case DotaGame.TYPE:
Provider.set(GameState, DotaGame.buildState(0, mode));
Provider.get(IFrameSwitcher).change(LevelFrame.ID); Provider.get(IFrameSwitcher).change(LevelFrame.ID);
} }
} }
}

View File

@@ -17,18 +17,55 @@ bricks:
- {type: 4, layer: 2, armor: 2} # armor - {type: 4, layer: 2, armor: 2} # armor
- {type: 5, layer: 2, armor: 1} # brick - {type: 5, layer: 2, armor: 1} # brick
teams: player:
- id: human human: &human
control: human
life: 3
tanks: tanks:
- {type: human0, rate: 1} - {type: human0, rate: 1}
- id: bot bot: &bot
spawnInterval: 3000 control: bot
life: -1
tanks: tanks:
- {type: bot0, rate: 0.25, bonus: 0.25} - {type: bot0, rate: 0.25, bonus: 0.25}
- {type: bot1, rate: 0.25, bonus: 0.25} - {type: bot1, rate: 0.25, bonus: 0.25}
- {type: bot2, rate: 0.25, bonus: 0.25} - {type: bot2, rate: 0.25, bonus: 0.25}
- {type: bot3, rate: 0.25, bonus: 0.25} - {type: bot3, rate: 0.25, bonus: 0.25}
presets:
- id: player1
teams:
- id: human
life: -1
players:
- {<<: *human, index: 0, color: 0xFC9838, life: 3}
- id: bot
spawnInterval: 3000
life: 20
players:
- {<<: *bot, index: 0}
- {<<: *bot, index: 1}
- {<<: *bot, index: 2}
- {<<: *bot, index: 3}
- id: player2
teams:
- id: human
life: -1
players:
- {<<: *human, index: 0, color: 0xFC9838, life: 3}
- {<<: *human, index: 1, color: 0x159D49, life: 3}
- id: bot
spawnInterval: 3000
life: 20
players:
- {<<: *bot, index: 0}
- {<<: *bot, index: 1}
- {<<: *bot, index: 2}
- {<<: *bot, index: 3}
- {<<: *bot, index: 4}
- {<<: *bot, index: 5}
points: points:
- {team: human, type: eagle, index: -1, direction: right, x: 12, y: 24} - {team: human, type: eagle, index: -1, direction: right, x: 12, y: 24}
- {team: human, type: tank, index: 0, direction: top, x: 8, y: 24} - {team: human, type: tank, index: 0, direction: top, x: 8, y: 24}

View File

@@ -17,18 +17,72 @@ bricks:
- {type: 4, layer: 2, armor: 2} # armor - {type: 4, layer: 2, armor: 2} # armor
- {type: 5, layer: 2, armor: 1} # brick - {type: 5, layer: 2, armor: 1} # brick
team_tanks: &team_tanks team:
radiant: &radiant
id: radiant
color: 0xff5555
life: 20
dire: &dire
id: dire
color: 0x5555ff
life: 20
player: &player
life: -1
control: bot
tanks: tanks:
- {type: slow, rate: 0.5} - {type: slow, rate: 0.5}
- {type: fast, rate: 0.5} - {type: fast, rate: 0.5}
presets:
- id: player1
teams: teams:
- <<: *team_tanks - <<: *radiant
id: radiant players:
color: 0xff5555 - {<<: *player, index: 0, control: human, color: 0xff7777}
- <<: *team_tanks - {<<: *player, index: 1}
id: dire - {<<: *player, index: 2}
color: 0x5555ff - {<<: *player, index: 3}
- {<<: *player, index: 4}
- <<: *dire
players:
- {<<: *player, index: 0}
- {<<: *player, index: 1}
- {<<: *player, index: 2}
- {<<: *player, index: 3}
- {<<: *player, index: 4}
- id: player2_coop
teams:
- <<: *radiant
players:
- {<<: *player, index: 0, control: human, color: 0xff7777}
- {<<: *player, index: 1, control: human, color: 0xff7777}
- {<<: *player, index: 2}
- {<<: *player, index: 3}
- {<<: *player, index: 4}
- <<: *dire
players:
- {<<: *player, index: 0}
- {<<: *player, index: 1}
- {<<: *player, index: 2}
- {<<: *player, index: 3}
- {<<: *player, index: 4}
- id: player2_vs
teams:
- <<: *radiant
players:
- {<<: *player, index: 0, control: human, color: 0xff7777}
- {<<: *player, index: 1}
- {<<: *player, index: 2}
- {<<: *player, index: 3}
- {<<: *player, index: 4}
- <<: *dire
players:
- {<<: *player, index: 0, control: human, color: 0x7777ff}
- {<<: *player, index: 1}
- {<<: *player, index: 2}
- {<<: *player, index: 3}
- {<<: *player, index: 4}
points: points:
- {team: radiant, type: eagle, index: -1, direction: right, x: 0, y: 28} - {team: radiant, type: eagle, index: -1, direction: right, x: 0, y: 28}

View File

@@ -1,6 +1,5 @@
package ru.m.tankz; package ru.m.tankz;
import ru.m.draw.Color;
typedef Type = Dynamic; typedef Type = Dynamic;
@@ -18,9 +17,7 @@ typedef BonusType = String;
typedef PlayerId = { typedef PlayerId = {
var team:TeamId; var team:TeamId;
var type:ControlType;
var index:Int; var index:Int;
@:optional var color:Color;
} }
typedef GameMode = Array<PlayerId>; typedef PresetId = String;

View File

@@ -60,15 +60,26 @@ typedef TankSpawn = {
@:optional var bonus:Float; @:optional var bonus:Float;
} }
typedef PlayerConfig = {
var index:Int;
var control:ControlType;
var tanks:Array<TankSpawn>;
@:optional var life:Int;
@:optional var color:Color;
}
typedef TeamConfig = { typedef TeamConfig = {
var id:TeamId; var id:TeamId;
var size:Int; var players:Array<PlayerConfig>;
var tanks:Array<TankSpawn>; @:optional var life:Int;
@:optional var spawnInterval:Int; @:optional var spawnInterval:Int;
@:optional var color:Color; @:optional var color:Color;
} }
typedef GamePreset = {
var id:PresetId;
var teams:Array<TeamConfig>;
}
typedef LevelConfig = { typedef LevelConfig = {
var data:Array<BrickConfig>; var data:Array<BrickConfig>;
@@ -82,13 +93,13 @@ class Config {
public var map(default, null):MapConfig; public var map(default, null):MapConfig;
public var bricks(default, null):Array<BrickConfig>; public var bricks(default, null):Array<BrickConfig>;
public var tanks(default, null):Array<TankConfig>; public var tanks(default, null):Array<TankConfig>;
public var teams(default, null):Array<TeamConfig>; public var presets(default, null):Array<GamePreset>;
public var points(default, null):Array<SpawnPoint>; public var points(default, null):Array<SpawnPoint>;
public var bonuses(default, null):Array<BonusConfig>; public var bonuses(default, null):Array<BonusConfig>;
private var brickMap:Map<Int, BrickConfig>; private var brickMap:Map<Int, BrickConfig>;
private var tankMap:Map<TankType, TankConfig>; private var tankMap:Map<TankType, TankConfig>;
private var teamMap:Map<TeamId, TeamConfig>; private var presetsMap:Map<PresetId, GamePreset>;
private var bonusMap:Map<BonusType, BonusConfig>; private var bonusMap:Map<BonusType, BonusConfig>;
public function new( public function new(
@@ -96,7 +107,7 @@ class Config {
game:GameConfig, game:GameConfig,
map:MapConfig, map:MapConfig,
bricks:Array<BrickConfig>, bricks:Array<BrickConfig>,
teams:Array<TeamConfig>, presets:Array<GamePreset>,
points:Array<SpawnPoint>, points:Array<SpawnPoint>,
tanks:Array<TankConfig>, tanks:Array<TankConfig>,
bonuses:Array<BonusConfig> bonuses:Array<BonusConfig>
@@ -105,7 +116,7 @@ class Config {
this.game = game; this.game = game;
this.map = map; this.map = map;
this.bricks = bricks; this.bricks = bricks;
this.teams = teams; this.presets = presets;
this.points = points; this.points = points;
this.tanks = tanks; this.tanks = tanks;
this.bonuses = bonuses; this.bonuses = bonuses;
@@ -117,9 +128,9 @@ class Config {
for (item in bricks) { for (item in bricks) {
brickMap.set(item.type, item); brickMap.set(item.type, item);
} }
teamMap = new Map(); presetsMap = new Map();
for (team in teams) { for (preset in presets) {
teamMap.set(team.id, team); presetsMap.set(preset.id, preset);
} }
tankMap = new Map(); tankMap = new Map();
for (item in tanks) { for (item in tanks) {
@@ -135,8 +146,8 @@ class Config {
return brickMap.get(type); return brickMap.get(type);
} }
public function getTeam(id:String):TeamConfig { public function getPreset(id:PresetId):GamePreset {
return teamMap.get(id); return presetsMap.get(id);
} }
public function getTank(type:TankType):TankConfig { public function getTank(type:TankType):TankConfig {

View File

@@ -10,7 +10,7 @@ typedef ConfigSource = {
var game:GameConfig; var game:GameConfig;
var map: MapConfig; var map: MapConfig;
var bricks: Array<BrickConfig>; var bricks: Array<BrickConfig>;
var teams: Array<TeamConfig>; var presets: Array<GamePreset>;
var points: Array<SpawnPoint>; var points: Array<SpawnPoint>;
var tanks: Array<TankConfig>; var tanks: Array<TankConfig>;
var bonuses: Array<BonusConfig>; var bonuses: Array<BonusConfig>;
@@ -24,6 +24,6 @@ class ConfigBundle {
public static function get(type:String):Config { public static function get(type:String):Config {
var source = convert(Yaml.parse(Assets.getText('resources/${type}/config.yaml'), Parser.options().useObjects())); var source = convert(Yaml.parse(Assets.getText('resources/${type}/config.yaml'), Parser.options().useObjects()));
return new Config(type, source.game, source.map, source.bricks, source.teams, source.points, source.tanks, source.bonuses); return new Config(type, source.game, source.map, source.bricks, source.presets, source.points, source.tanks, source.bonuses);
} }
} }

View File

@@ -1,10 +1,8 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import haxe.ds.Option; import haxe.ds.Option;
import ru.m.draw.Color;
import ru.m.tankz.control.Control;
import ru.m.tankz.game.Game; import ru.m.tankz.game.Game;
import ru.m.tankz.game.GameState.PlayerState; import ru.m.tankz.game.GameState;
import ru.m.tankz.Type; import ru.m.tankz.Type;
@@ -15,57 +13,17 @@ class ClassicGame extends Game {
public static var HUMAN(default, never):TeamId = 'human'; public static var HUMAN(default, never):TeamId = 'human';
public static var BOT(default, never):TeamId = 'bot'; public static var BOT(default, never):TeamId = 'bot';
private static var PLAYER1_COLOR:Color = 0xFC9838; public static var PLAYER1(default, never):PresetId = 'player1';
private static var PLAYER2_COLOR:Color = 0x159D49; public static var PLAYER2(default, never):PresetId = 'player2';
public static var PLAYER1(default, never):GameMode = [
{team:HUMAN, type:Control.HUMAN, color: PLAYER1_COLOR, index:0}
];
public static var PLAYER2(default, never):GameMode = [
{team:HUMAN, type:Control.HUMAN, color: PLAYER1_COLOR, index:0},
{team:HUMAN, type:Control.HUMAN, color: PLAYER2_COLOR, index:1}
];
private static var HUMAN_LIFE(default, never):Int = 3;
private static var BOT_LIFE(default, never):Int = 20;
public function new() { public function new() {
super(TYPE); super(TYPE);
} }
public static function buildState(level:Int, mode:GameMode):GameState {
var state = new GameState();
state.type = TYPE;
state.mode = mode;
state.level = level;
state.teams[HUMAN] = {life: -1, players: new Map(), lose: false};
state.teams[BOT] = {life: BOT_LIFE, players: new Map(), lose: false};
for (human in mode) {
state.teams[HUMAN].players[human.index] = {
id:human,
life:HUMAN_LIFE,
};
}
for (i in 0...mode.length * 2 + 2) {
state.teams[BOT].players[i] = {
id:{team:BOT, index:i, type:Control.BOT},
life:-1,
};
}
return state;
}
override public function next():Option<GameState> { override public function next():Option<GameState> {
if (!state.teams[HUMAN].lose) { if (state.loser == HUMAN) {
state.level++;
if (state.level >= config.game.levels) state.level = 0;
state.teams[BOT].lose = false;
state.teams[BOT].life = BOT_LIFE;
for (ps in state.teams[HUMAN].players) {
if (ps.life > 0) ps.life++;
}
return Option.Some(state);
}
return Option.None; return Option.None;
} }
return super.next();
}
} }

View File

@@ -1,10 +1,6 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import haxe.ds.Option;
import ru.m.draw.Color;
import ru.m.tankz.control.Control;
import ru.m.tankz.game.Game; import ru.m.tankz.game.Game;
import ru.m.tankz.game.GameState;
import ru.m.tankz.Type; import ru.m.tankz.Type;
@@ -15,57 +11,11 @@ class DotaGame extends Game {
public static var RADIANT(default, never):TeamId = 'radiant'; public static var RADIANT(default, never):TeamId = 'radiant';
public static var DIRE(default, never):TeamId = 'dire'; public static var DIRE(default, never):TeamId = 'dire';
public static var PLAYER1(default, never):GameMode = [ public static var PLAYER1(default, never):PresetId = 'player1';
{team:RADIANT, type:Control.HUMAN, index:0} public static var PLAYER2_COOP(default, never):PresetId = 'player2_coop';
]; public static var PLAYER2_VS(default, never):PresetId = 'player2_vs';
private static var PLAYER1_COLOR:Color = 0xff7777;
private static var PLAYER2_COLOR:Color = 0x7777ff;
public static var PLAYER2_COOP(default, never):GameMode = [
{team:RADIANT, type:Control.HUMAN, color: PLAYER1_COLOR, index:0},
{team:RADIANT, type:Control.HUMAN, color: PLAYER1_COLOR, index:1}
];
public static var PLAYER2_VS(default, never):GameMode = [
{team:RADIANT, type:Control.HUMAN, color: PLAYER1_COLOR, index:0},
{team:DIRE, type:Control.HUMAN, color: PLAYER2_COLOR, index:0}
];
private static var TEAM_SIZE(default, never):Int = 5;
public function new() { public function new() {
super(TYPE); super(TYPE);
} }
public static function buildState(level:Int, mode:GameMode):GameState {
var state = new GameState();
state.type = TYPE;
state.mode = mode;
state.level = level;
state.teams[RADIANT] = {life: 20, players: new Map(), lose: false};
state.teams[DIRE] = {life: 20, players: new Map(), lose: false};
for (i in 0...TEAM_SIZE) {
state.teams[RADIANT].players[i] = {
id: {team:RADIANT, index:i, type:Control.BOT},
life: -1,
};
}
for (i in 0...TEAM_SIZE) {
state.teams[DIRE].players[i] = {
id: {team:DIRE, index:i, type:Control.BOT},
life: -1,
};
}
for (human in mode) {
state.teams[human.team].players[human.index].id = human;
}
return state;
}
override public function next():Option<GameState> {
state.level++;
if (state.level >= config.game.levels) state.level = 0;
return Option.Some(buildState(state.level, state.mode));
}
} }

View File

@@ -1,6 +1,5 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import ru.m.tankz.core.Bonus;
import haxe.ds.Option; import haxe.ds.Option;
import haxe.Timer; import haxe.Timer;
import promhx.Deferred; import promhx.Deferred;
@@ -13,6 +12,7 @@ import ru.m.tankz.config.ConfigBundle;
import ru.m.tankz.config.LevelBundle; import ru.m.tankz.config.LevelBundle;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;
import ru.m.tankz.control.HumanControl; import ru.m.tankz.control.HumanControl;
import ru.m.tankz.core.Bonus;
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;
@@ -29,12 +29,12 @@ class Game implements EngineListener {
public var type(default, null):GameType; public var type(default, null):GameType;
public var state(default, null):GameState; public var state(default, null):GameState;
public var preset(default, null):GamePreset;
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):Engine; public var engine(default, null):Engine;
private var points:Array<SpawnPoint>; private var points:Array<SpawnPoint>;
private var spawners:Map<TeamId, Spawner>;
private var deferred:Deferred<GameState>; private var deferred:Deferred<GameState>;
private var stream:Stream<GameState>; private var stream:Stream<GameState>;
@@ -45,16 +45,21 @@ class Game implements EngineListener {
engine.listeners.push(this); engine.listeners.push(this);
} }
public function getTeam(teamId:TeamId):Team {
return teams[teamId];
}
public function getPlayer(playerId:PlayerId):Player { public function getPlayer(playerId:PlayerId):Player {
return teams[playerId.team].players[playerId.index]; return teams[playerId.team].players[playerId.index];
} }
private function buildTank(playerId:PlayerId, point:SpawnPoint):Tank { private function buildTank(playerId:PlayerId, point:SpawnPoint):Tank {
var spawns:Array<TankSpawn> = teams[playerId.team].config.tanks; var player = getPlayer(playerId);
var spawns:Array<TankSpawn> = player.config.tanks;
var spawn:TankSpawn = spawns[Math.floor(Math.random() * spawns.length)]; var spawn:TankSpawn = spawns[Math.floor(Math.random() * spawns.length)];
var tankConfig:TankConfig = config.getTank(spawn.type); var tankConfig:TankConfig = config.getTank(spawn.type);
var tank = new Tank(playerId, tankConfig); var tank = new Tank(playerId, tankConfig);
tank.color = playerId.color.zero ? teams[playerId.team].config.color : playerId.color; tank.color = player.config.color.zero ? teams[playerId.team].config.color : player.config.color;
tank.bonus = Math.random() < spawn.bonus; tank.bonus = Math.random() < spawn.bonus;
applyPoint(tank, point); applyPoint(tank, point);
return tank; return tank;
@@ -66,26 +71,25 @@ class Game implements EngineListener {
} }
public function start(state:GameState):Stream<GameState> { public function start(state:GameState):Stream<GameState> {
this.deferred = new Deferred();
this.state = state; this.state = state;
this.preset = config.getPreset(state.presetId);
this.deferred = new Deferred();
var level = LevelBundle.get(type, config, state.level); var level = LevelBundle.get(type, config, state.level);
points = level.points != null ? level.points : config.points; points = level.points != null ? level.points : config.points;
engine.map.setData(level.data); engine.map.setData(level.data);
teams = new Map<TeamId, Team>(); teams = new Map<TeamId, Team>();
spawners = new Map<TeamId, Spawner>();
var humanControlIndex = 0; var humanControlIndex = 0;
for (teamConfig in config.teams) { for (teamConfig in preset.teams) {
var team = new Team(teamConfig); var teamPoints = points.filter(function(p:SpawnPoint) return p.team == teamConfig.id);
for (playerState in state.teams[team.id].players) { var team:Team = new Team(teamConfig, teamPoints);
var player = new Player(playerState.id);
team.players[player.id.index] = player;
teams[team.id] = team; teams[team.id] = team;
if (player.id.type != null) { for (player in team.players.iterator()) {
var control = switch (player.id.type) { if (player.config.control != null) {
var control = switch (player.config.control) {
case Control.HUMAN: new HumanControl(player.id, humanControlIndex++); case Control.HUMAN: new HumanControl(player.id, humanControlIndex++);
case Control.BOT: new BotControl(player.id); case Control.BOT: new BotControl(player.id);
case Control.NONE: null; case Control.NONE: null;
case _: throw 'Unsupported control type: "${player.id.type}"'; case _: throw 'Unsupported control type: "${player.config.control}"';
} }
L.d(TAG, 'control(${player.id} - ${control})'); L.d(TAG, 'control(${player.id} - ${control})');
if (control != null) { if (control != null) {
@@ -94,17 +98,16 @@ class Game implements EngineListener {
} }
} }
} }
var teamPoints = points.filter(function(p:SpawnPoint) return p.team == team.id); team.spawner.runner = spawn;
spawners[team.id] = new Spawner(team.config, teamPoints, spawn);
} }
for (team in teams) { for (team in teams.iterator()) {
for (player in team.players) { for (player in team.players.iterator()) {
if (trySpawn(player.id)) { if (team.trySpawn(player.id)) {
spawners[team.id].push(player.id); team.spawner.push(player.id);
} }
} }
var eaglePoint = spawners[team.id].getPoint('eagle'); var eaglePoint = team.spawner.getPoint('eagle');
if (eaglePoint != null) { if (eaglePoint != null) {
var eagle = new Eagle(team.id); var eagle = new Eagle(team.id);
applyPoint(eagle, eaglePoint); applyPoint(eagle, eaglePoint);
@@ -117,15 +120,15 @@ class Game implements EngineListener {
private function spawn(task:SpawnTask):Void { private function spawn(task:SpawnTask):Void {
L.d(TAG, 'spawn(${task}'); L.d(TAG, 'spawn(${task}');
getPlayer(task.playerId).tankId = 0; var team = getTeam(task.playerId.team);
if (trySpawn(task.playerId, true)) { var player = getPlayer(task.playerId);
player.tankId = 0;
if (getTeam(task.playerId.team).trySpawn(task.playerId, true)) {
var tank = buildTank(task.playerId, task.point); var tank = buildTank(task.playerId, task.point);
var player:Player = getPlayer(task.playerId);
player.tankId = tank.id; player.tankId = tank.id;
Timer.delay(function() engine.spawn(tank), 1500); Timer.delay(function() engine.spawn(tank), 1500);
} else if (!isTeamAlive(task.playerId.team)) { } else if (!team.isAlive) {
state.teams[task.playerId.team].lose = true; lose(team.id);
complete();
} }
deferred.resolve(state); deferred.resolve(state);
} }
@@ -165,71 +168,43 @@ class Game implements EngineListener {
} }
} }
private function isTeamAlive(team:TeamId):Bool {
var ts:TeamState = state.teams[team];
var life:Int = Lambda.fold(ts.players, function(ps, t) return t + ps.life, ts.life);
if (life > 0) {
return true;
} else {
for (player in teams[team].players) {
if (player.tankId > 0) {
return true;
}
}
if (spawners[team].active) {
return true;
}
}
return false;
}
private function trySpawn(player:PlayerId, spawn:Bool = false):Bool {
var ts:TeamState = state.teams[player.team];
var ps:PlayerState = ts.players[player.index];
var result = false;
if (ps.life > -1) {
if (ps.life > 0) {
if (spawn) ps.life--;
result = true;
}
} else if (ts.life > -1) {
if (ts.life > 0) {
if (spawn) ts.life--;
result = true;
}
}
return result;
}
public function onDestroy(entity:EntityType):Void { public function onDestroy(entity:EntityType):Void {
switch (entity) { switch (entity) {
case EntityType.TANK(tank): case EntityType.TANK(tank):
getPlayer(tank.playerId).control.stop(); var team = getTeam(tank.playerId.team);
getPlayer(tank.playerId).tankId = 0; //ToDo: ? var player = getPlayer(tank.playerId);
var respawn:Bool = trySpawn(tank.playerId); player.control.stop();
player.tankId = 0; //ToDo: ?
var respawn:Bool = team.trySpawn(player.id);
if (respawn) { if (respawn) {
spawners[tank.playerId.team].push(tank.playerId); team.spawner.push(player.id);
} }
if (!isTeamAlive(tank.playerId.team)) { if (!team.isAlive) {
state.teams[tank.playerId.team].lose = true; lose(team.id);
complete();
} }
if (tank.bonus) spawnBonus(); if (tank.bonus) spawnBonus();
deferred.resolve(state); deferred.resolve(state);
case EntityType.EAGLE(eagle): case EntityType.EAGLE(eagle):
state.teams[eagle.team].lose = true; lose(eagle.team);
complete();
deferred.resolve(state); deferred.resolve(state);
case x: case x:
} }
} }
private function lose(teamId:TeamId):Void {
state.loser = teamId;
complete();
}
public function onAction(tankId:Int, action:TankAction):Void { public function onAction(tankId:Int, action:TankAction):Void {
engine.action(tankId, action); engine.action(tankId, action);
} }
public function next():Option<GameState> { public function next():Option<GameState> {
return Option.None; var level = state.level + 1;
state.level++;
if (level >= config.game.levels) level = 0;
return Option.Some(new GameState(state.type, preset.id, level));
} }
public function dispose():Void { public function dispose():Void {
@@ -249,7 +224,7 @@ class Game implements EngineListener {
private function applyBonus(tank:Tank, bonus:Bonus):Void { private function applyBonus(tank:Tank, bonus:Bonus):Void {
switch (bonus.bonusType) { switch (bonus.bonusType) {
case 'life': case 'life':
state.teams[tank.playerId.team].players[tank.playerId.index].life++; getPlayer(tank.playerId).life++;
case 'star': case 'star':
if (tank.config.upgrade != null) { if (tank.config.upgrade != null) {
tank.config = config.getTank(tank.config.upgrade); tank.config = config.getTank(tank.config.upgrade);

View File

@@ -3,26 +3,16 @@ package ru.m.tankz.game;
import ru.m.tankz.Type; import ru.m.tankz.Type;
typedef PlayerState = {
var id:PlayerId;
var life:Int;
}
typedef TeamState = {
var players:Map<Int, PlayerState>;
var life:Int;
var lose:Bool;
}
class GameState { class GameState {
public var type:GameType; public var type:GameType;
public var mode:GameMode;
public var level:Int; public var level:Int;
public var teams:Map<TeamId, TeamState>; public var presetId:PresetId;
public var loser:TeamId;
public function new() { public function new(type:GameType, presetId:PresetId, level:Int = 0) {
type = null; this.type = type;
level = -1; this.presetId = presetId;
teams = new Map(); this.level = level;
this.loser = null;
} }
} }

View File

@@ -1,18 +1,23 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import ru.m.tankz.config.Config;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;
import ru.m.tankz.Type; import ru.m.tankz.Type;
class Player { class Player {
public var config(default, null):PlayerConfig;
public var id(default, null):PlayerId; public var id(default, null):PlayerId;
public var tankId(default, set):Int; public var tankId(default, set):Int;
public var control(default, set):Control; public var control(default, set):Control;
public var life(default, default):Int;
public function new(id:PlayerId, control:Control=null) { public function new(teamId:TeamId, config:PlayerConfig) {
this.id = id; this.config = config;
this.control = control; this.id = {team:teamId, index:config.index};
this.control = null;
this.life = config.life;
} }
public function set_tankId(value:Int):Int { public function set_tankId(value:Int):Int {

View File

@@ -13,10 +13,10 @@ typedef SpawnTask = {
class Spawner { class Spawner {
public var active(get, never):Bool; public var active(get, never):Bool;
public var runner(default, default):SpawnTask -> Void;
private var config:TeamConfig; private var config:TeamConfig;
private var points:Array<SpawnPoint>; private var points:Array<SpawnPoint>;
private var runner:SpawnTask -> Void;
private var queue:Array<SpawnTask>; private var queue:Array<SpawnTask>;
private var timer:Timer; private var timer:Timer;
@@ -24,10 +24,10 @@ class Spawner {
private var anyPoints:Array<SpawnPoint>; private var anyPoints:Array<SpawnPoint>;
private var index:Int; private var index:Int;
public function new(config:TeamConfig, points:Array<SpawnPoint>, runner:SpawnTask -> Void) { public function new(config:TeamConfig, points:Array<SpawnPoint>) {
this.config = config; this.config = config;
this.points = points; this.points = points;
this.runner = runner; this.runner = null;
queue = []; queue = [];
indexedPoints = new Map(); indexedPoints = new Map();
anyPoints = []; anyPoints = [];

View File

@@ -1,21 +1,61 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import ru.m.tankz.config.Config;
import ru.m.tankz.game.Player;
import ru.m.tankz.Type; import ru.m.tankz.Type;
import ru.m.tankz.config.Config;
class Team { class Team {
public var id(default, null):TeamId; public var id(default, null):TeamId;
public var config(default, null):TeamConfig; public var config(default, null):TeamConfig;
public var spawner(default, null):Spawner;
public var players(default, null):Map<Int, Player>; public var players(default, null):Map<Int, Player>;
public var life(default, default):Int;
public var isAlive(get, null):Bool;
private static var i:Int = 0; public function new(config:TeamConfig, points:Array<SpawnPoint>) {
public function new(config:TeamConfig) {
this.id = config.id; this.id = config.id;
this.config = config; this.config = config;
this.players = new Map(); this.players = new Map();
for (playerConfig in config.players) {
this.players[playerConfig.index] = new Player(id, playerConfig);
}
this.life = config.life;
this.spawner = new Spawner(config, points);
}
public function trySpawn(player:PlayerId, spawn:Bool = false):Bool {
var player:Player = players[player.index];
var result = false;
if (player.life > -1) {
if (player.life > 0) {
if (spawn) player.life--;
result = true;
}
} else if (life > -1) {
if (life > 0) {
if (spawn) life--;
result = true;
}
}
return result;
}
// ToDo: eagle state?
private function get_isAlive():Bool {
var life:Int = Lambda.fold(players, function(p:Player, t:Int) return t + p.life, life);
if (life > 0) {
return true;
} else {
for (player in players) {
if (player.tankId > 0) {
return true;
}
}
if (spawner.active) {
return true;
}
}
return false;
} }
} }