[common] add GameState

This commit is contained in:
2019-03-14 12:58:00 +03:00
parent 1f87a71833
commit 8ea862a118
12 changed files with 132 additions and 125 deletions

View File

@@ -1,5 +1,6 @@
package ru.m.tankz.frame.common; package ru.m.tankz.frame.common;
import ru.m.tankz.game.GameState;
import flash.events.Event; import flash.events.Event;
import haxe.ds.Option; import haxe.ds.Option;
import haxe.Timer; import haxe.Timer;
@@ -7,7 +8,6 @@ import haxework.gui.frame.FrameSwitcher;
import haxework.gui.GroupView; import haxework.gui.GroupView;
import ru.m.tankz.frame.common.IGamePanel; import ru.m.tankz.frame.common.IGamePanel;
import ru.m.tankz.game.Game; import ru.m.tankz.game.Game;
import ru.m.tankz.game.GameStart;
import ru.m.tankz.network.NetworkManager; import ru.m.tankz.network.NetworkManager;
import ru.m.tankz.render.Render; import ru.m.tankz.render.Render;
import ru.m.tankz.sound.SoundManager; import ru.m.tankz.sound.SoundManager;
@@ -21,7 +21,7 @@ class GameFrame extends GroupView {
@:provide var network:NetworkManager; @:provide var network:NetworkManager;
@:provide var sound:SoundManager; @:provide var sound:SoundManager;
@:provide var gameStart:GameStart; @:provide var state:GameState;
@:provide var switcher:FrameSwitcher; @:provide var switcher:FrameSwitcher;
private var game:Game; private var game:Game;
@@ -36,14 +36,14 @@ class GameFrame extends GroupView {
} }
public function onShow():Void { public function onShow():Void {
start(gameStart); start(state);
} }
private function start(start:GameStart):Void { private function start(state:GameState):Void {
game = new Game(start.type); game = new Game(state.type);
game.engine.connect(render); game.engine.connect(render);
game.engine.connect(sound); game.engine.connect(sound);
game.start(start).then(onGameStateChange).endThen(onGameComplete); game.start(state).then(onGameStateChange).endThen(onGameComplete);
timer = new Timer(10); timer = new Timer(10);
timer.run = updateEngine; timer.run = updateEngine;
panel.game = game; panel.game = game;

View File

@@ -5,8 +5,7 @@ import haxework.gui.GroupView;
import haxework.resources.IResources; import haxework.resources.IResources;
import ru.m.tankz.bundle.IConfigBundle; import ru.m.tankz.bundle.IConfigBundle;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
import ru.m.tankz.control.Control; import ru.m.tankz.game.GameState;
import ru.m.tankz.game.GameStart;
import ru.m.tankz.Type; import ru.m.tankz.Type;
class LevelFrame extends GroupView { class LevelFrame extends GroupView {
@@ -17,7 +16,7 @@ class LevelFrame extends GroupView {
private var config(default, null):Config; private var config(default, null):Config;
@:provide var configBundle:IConfigBundle; @:provide var configBundle:IConfigBundle;
@:provide var start:GameStart; @:provide var state:GameState;
@:provide var resources:IResources; @:provide var resources:IResources;
private function set_gameType(value:GameType):GameType { private function set_gameType(value:GameType):GameType {
@@ -32,11 +31,11 @@ class LevelFrame extends GroupView {
private function set_preset(value:GamePreset):GamePreset { private function set_preset(value:GamePreset):GamePreset {
if (preset != value || true) { // ToDo: if (preset != value || true) { // ToDo:
preset = value; preset = value;
start = new GameStart(gameType, preset.id); state = new GameState(gameType, preset.id);
for (team in value.teams) { for (team in value.teams) {
for (player in team.players) { for (player in team.players) {
var playerId = new PlayerId(team.id, player.index); var playerId = new PlayerId(team.id, player.index);
start.players.push({playerId: playerId, control: player.control != null ? player.control : Control.BOT}); state.players.push(new PlayerState(playerId, player.control, player.life));
} }
} }
} }
@@ -44,8 +43,8 @@ class LevelFrame extends GroupView {
} }
private function set_level(value:Int):Int { private function set_level(value:Int):Int {
start.level = value; state.level = value;
return start.level; return state.level;
} }
private function levelViewFactory(index:Int, level:Int):ButtonView { private function levelViewFactory(index:Int, level:Int):ButtonView {

View File

@@ -5,7 +5,7 @@ import haxework.gui.HGroupView;
import haxework.gui.LabelView; import haxework.gui.LabelView;
import haxework.gui.skin.ISkin; import haxework.gui.skin.ISkin;
import haxework.gui.ToggleButtonView; import haxework.gui.ToggleButtonView;
import ru.m.tankz.game.GameStart; import ru.m.tankz.game.GameState;
import ru.m.tankz.Type.PlayerId; import ru.m.tankz.Type.PlayerId;
import ru.m.tankz.Type.TeamId; import ru.m.tankz.Type.TeamId;
@@ -44,12 +44,12 @@ class TeamSkin implements ISkin<TeamButton> {
private static inline var NONE:TeamId = "none"; private static inline var NONE:TeamId = "none";
public var item_index(default, set):Int; public var item_index(default, set):Int;
public var data(default, set):PlayerType; public var data(default, set):PlayerState;
@:view var label(default, null):LabelView; @:view var label(default, null):LabelView;
@:view var teams(default, null):DataView<TeamId, ToggleButtonView>; @:view var teams(default, null):DataView<TeamId, ToggleButtonView>;
@:provide var start:GameStart; @:provide var state:GameState;
private function teamViewFactory(index:Int, team:TeamId) { private function teamViewFactory(index:Int, team:TeamId) {
var view = new TeamButton(); var view = new TeamButton();
@@ -60,15 +60,15 @@ class TeamSkin implements ISkin<TeamButton> {
return view; return view;
} }
private function set_data(value:PlayerType):PlayerType { private function set_data(value:PlayerState):PlayerState {
data = value; data = value;
teams.data = [NONE].concat([for (team in start.preset.teams) team.id]); teams.data = [NONE].concat([for (team in state.preset.teams) team.id]);
return data; return data;
} }
private function getTeamColor(teamId:TeamId):Int { private function getTeamColor(teamId:TeamId):Int {
var color = 0xcccccc; var color = 0xcccccc;
for (team in start.preset.teams) { for (team in state.preset.teams) {
if (team.id == teamId) { if (team.id == teamId) {
if (!team.color.zero) color = team.color; if (!team.color.zero) color = team.color;
break; break;
@@ -84,7 +84,7 @@ class TeamSkin implements ISkin<TeamButton> {
} }
private function onTeamSelect(team:TeamId) { private function onTeamSelect(team:TeamId) {
data.playerId = new PlayerId(team, item_index); data.id = new PlayerId(team, item_index);
for (view in teams.views) { for (view in teams.views) {
var button = cast(view, TeamButton); var button = cast(view, TeamButton);
button.on = team == button.team; button.on = team == button.team;

View File

@@ -15,9 +15,9 @@ import ru.m.tankz.preset.DotaGame;
private function updateViews():Void { private function updateViews():Void {
radiant.live.text = '${game.teams[DotaGame.RADIANT].life}'; radiant.live.text = '${game.teams[DotaGame.RADIANT].life}';
radiant.score.text = '0'; // ToDO radiant.score.text = '${game.teams[DotaGame.RADIANT].score}';
dire.live.text = '${game.teams[DotaGame.DIRE].life}'; dire.live.text = '${game.teams[DotaGame.DIRE].life}';
dire.score.text = '0'; // ToDO dire.score.text = '${game.teams[DotaGame.DIRE].score}';
} }
override public function update():Void { override public function update():Void {

View File

@@ -6,14 +6,14 @@ import haxework.gui.frame.FrameSwitcher;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;
import ru.m.tankz.frame.common.LevelFrame; import ru.m.tankz.frame.common.LevelFrame;
import ru.m.tankz.frame.common.PlayerView; import ru.m.tankz.frame.common.PlayerView;
import ru.m.tankz.game.GameStart.PlayerType; import ru.m.tankz.game.GameState;
import ru.m.tankz.preset.DotaGame; import ru.m.tankz.preset.DotaGame;
@:template class DotaLevelFrame extends LevelFrame { @:template class DotaLevelFrame extends LevelFrame {
public static inline var ID = "dota.level"; public static inline var ID = "dota.level";
@:view var levels(default, null):DataView<Int, ButtonView>; @:view var levels(default, null):DataView<Int, ButtonView>;
@:view var players(default, null):DataView<PlayerType, PlayerView>; @:view var players(default, null):DataView<PlayerState, PlayerView>;
@:provide var frames:FrameSwitcher; @:provide var frames:FrameSwitcher;
@@ -22,16 +22,13 @@ import ru.m.tankz.preset.DotaGame;
levels.data = [for (i in 0...config.game.levels) i]; levels.data = [for (i in 0...config.game.levels) i];
var data = []; var data = [];
for (i in 0...2) { for (i in 0...2) {
data.push({ data.push(new PlayerState(null, Control.HUMAN));
playerId: null,
control: Control.HUMAN,
});
} }
start.players = data; state.players = data;
players.data = data; players.data = data;
} }
private function playerViewFactory(index:Int, player:PlayerType):PlayerView { private function playerViewFactory(index:Int, player:PlayerState):PlayerView {
var view = new PlayerView(); var view = new PlayerView();
view.item_index = index; view.item_index = index;
view.data = player; view.data = player;

View File

@@ -17,7 +17,7 @@ import ru.m.tankz.core.Entity;
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.Engine;
import ru.m.tankz.game.GameStart; import ru.m.tankz.game.GameState;
import ru.m.tankz.game.Spawner; import ru.m.tankz.game.Spawner;
import ru.m.tankz.Type; import ru.m.tankz.Type;
@@ -26,17 +26,16 @@ class Game {
private static var TAG(default, never):String = 'Game'; private static var TAG(default, never):String = 'Game';
public var type(default, null):GameType; public var type(default, null):GameType;
public var level(default, null):Int;
public var players(default, null):Array<PlayerType>;
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;
public var loser(default, null):Null<TeamId>; public var loser(default, null):Null<TeamId>;
public var state(default, null):GameState;
private var points:Array<SpawnPoint>; private var points:Array<SpawnPoint>;
private var deferred:Deferred<Dynamic>; private var deferred:Deferred<GameState>;
private var stream:Stream<Dynamic>; private var stream:Stream<GameState>;
@:provide var configBundle:IConfigBundle; @:provide var configBundle:IConfigBundle;
@:provide var levelBundle:ILevelBundle; @:provide var levelBundle:ILevelBundle;
@@ -81,26 +80,25 @@ class Game {
entity.rect.direction = point.direction; entity.rect.direction = point.direction;
} }
public function start(start:GameStart):Stream<Dynamic> { public function start(state:GameState):Stream<GameState> {
this.players = start.players; this.state = state;
var players = players.slice(0);
this.loser = null; this.loser = null;
this.preset = config.getPreset(start.presetId);
this.level = start.level;
this.deferred = new Deferred(); this.deferred = new Deferred();
var level:LevelConfig = levelBundle.get(type, config, start.level); var level:LevelConfig = 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>();
var controlFactory:IControlFactory = Provider.build(IControlFactory); var controlFactory:IControlFactory = Provider.build(IControlFactory);
for (teamConfig in preset.teams) { var players = state.players.slice(0);
for (teamConfig in state.preset.teams) {
var teamPoints = points.filter(function(p:SpawnPoint) return p.team == teamConfig.id); var teamPoints = points.filter(function(p:SpawnPoint) return p.team == teamConfig.id);
var team:Team = new Team(teamConfig, teamPoints); var team:Team = new Team(teamConfig, teamPoints);
teams[team.id] = team; teams[team.id] = team;
for (player in team.players.iterator()) { for (player in team.players.iterator()) {
var controlType:ControlType = Control.BOT; var controlType:ControlType = Control.BOT;
var nextPlayer = Lambda.find(players, function(p) return p.playerId != null && p.playerId.team == team.id); var nextPlayer:PlayerState = Lambda.find(players, function(p) return p.id != null && p.id.team == team.id);
if (nextPlayer != null) { if (nextPlayer != null) {
player.state = nextPlayer;
players.remove(nextPlayer); players.remove(nextPlayer);
controlType = nextPlayer.control; controlType = nextPlayer.control;
} }
@@ -116,7 +114,7 @@ class Game {
for (team in teams.iterator()) { for (team in teams.iterator()) {
for (player in team.players.iterator()) { for (player in team.players.iterator()) {
if (team.trySpawn(player.id)) { if (team.tryRespawn(player.id)) {
team.spawner.push(player.id, player.state.tank); team.spawner.push(player.id, player.state.tank);
} }
} }
@@ -137,15 +135,11 @@ class Game {
var team = getTeam(task.playerId.team); var team = getTeam(task.playerId.team);
var player = getPlayer(task.playerId); var player = getPlayer(task.playerId);
player.tankId = 0; player.tankId = 0;
if (getTeam(task.playerId.team).trySpawn(task.playerId, true)) { var tank = buildTank(task);
var tank = buildTank(task); player.tankId = tank.id;
player.tankId = tank.id; player.state.tank = tank.config.type;
player.state.tank = tank.config.type; engine.spawn(tank);
engine.spawn(tank); deferred.resolve(state);
} else if (!team.isAlive) {
lose(team.id);
}
deferred.resolve(null);
} }
private function complete():Void { private function complete():Void {
@@ -156,7 +150,7 @@ class Game {
} }
} }
Timer.delay(function() { Timer.delay(function() {
deferred.resolve(null); deferred.resolve(state);
stream.end(); stream.end();
}, 5000); }, 5000);
} }
@@ -176,7 +170,7 @@ class Game {
case [EntityType.EAGLE(eagle), EntityChange.DEATH]: case [EntityType.EAGLE(eagle), EntityChange.DEATH]:
if (eagle.death) { if (eagle.death) {
lose(eagle.team); lose(eagle.team);
deferred.resolve(null); deferred.resolve(state);
} }
case [EntityType.TANK(tank), EntityChange.HIT]: case [EntityType.TANK(tank), EntityChange.HIT]:
if (tank.bonus) { if (tank.bonus) {
@@ -212,7 +206,8 @@ class Game {
var player = getPlayer(tank.playerId); var player = getPlayer(tank.playerId);
player.control.stop(); player.control.stop();
player.tankId = 0; //ToDo: ? player.tankId = 0; //ToDo: ?
var respawn:Bool = team.trySpawn(player.id); team.onDestroy(player.id);
var respawn:Bool = team.tryRespawn(player.id);
if (respawn) { if (respawn) {
team.spawner.push(player.id); team.spawner.push(player.id);
} }
@@ -226,12 +221,12 @@ class Game {
getPlayer(playerId).state.frags++; getPlayer(playerId).state.frags++;
getPlayer(playerId).state.score += tank.config.score; getPlayer(playerId).state.score += tank.config.score;
} }
deferred.resolve(null); deferred.resolve(state);
case EntityType.BONUS(bonus): case EntityType.BONUS(bonus):
if (bonus.config.score > 0 && playerId != null) { if (bonus.config.score > 0 && playerId != null) {
getPlayer(playerId).state.score += bonus.config.score; getPlayer(playerId).state.score += bonus.config.score;
} }
deferred.resolve(null); deferred.resolve(state);
case _: case _:
} }
} }
@@ -245,15 +240,15 @@ class Game {
engine.action(tankId, action); engine.action(tankId, action);
} }
public function next():Option<GameStart> { public function next():Option<GameState> {
for (rule in config.game.complete) { for (rule in config.game.complete) {
if (rule.team != null && rule.team == loser) { if (rule.team != null && rule.team == loser) {
return Option.None; return Option.None;
} }
} }
var level = this.level + 1; var level = this.state.level + 1;
if (level >= config.game.levels) level = 0; if (level >= config.game.levels) level = 0;
return Option.Some(new GameStart(type, preset.id, level, players)); return Option.Some(new GameState(type, state.presetId, level, state.players));
} }
public function dispose():Void { public function dispose():Void {

View File

@@ -1,34 +0,0 @@
package ru.m.tankz.game;
import ru.m.tankz.config.Config;
import ru.m.tankz.bundle.IConfigBundle;
import ru.m.tankz.Type;
typedef PlayerType = {
var playerId: PlayerId;
var control:ControlType;
}
class GameStart {
@:provide private var configBundle:IConfigBundle;
public var type(default, default):GameType;
public var presetId(default, default):PresetId;
public var level(default, default):Int;
public var players(default, default):Array<PlayerType>;
public var preset(get, null):GamePreset;
public function new(type:GameType, presetId:PresetId, level:Int = 0, players:Array<PlayerType> = null) {
this.type = type;
this.presetId = presetId;
this.level = level;
this.players = players == null ? [] : players;
}
private function get_preset():GamePreset {
var config = configBundle.get(type);
var preset = config.getPreset(presetId);
return preset;
}
}

View File

@@ -0,0 +1,52 @@
package ru.m.tankz.game;
import ru.m.tankz.bundle.IConfigBundle;
import ru.m.tankz.config.Config;
import ru.m.tankz.control.Control;
import ru.m.tankz.Type;
class PlayerState {
public var id:PlayerId;
public var tank:TankType;
public var control:ControlType;
public var life:Int;
public var score:Int;
public var frags:Int;
public var shots:Int;
public var hits:Int;
public function new(id:PlayerId, control:ControlType = null, life:Int = 0) {
this.id = id;
this.tank = null;
this.control = control == null ? Control.BOT : control;
this.life = life;
this.score = 0;
this.frags = 0;
this.shots = 0;
this.hits = 0;
}
}
class GameState {
public var type:GameType;
public var presetId:PresetId;
public var level:Int;
public var players:Array<PlayerState>;
public var preset(get, null):GamePreset;
@:provide private var configBundle:IConfigBundle;
public function new(type:GameType, presetId:PresetId, level:Int = 1, players:Array<PlayerState> = null) {
this.type = type;
this.presetId = presetId;
this.level = level;
this.players = players == null ? [] : players;
}
private function get_preset():GamePreset {
var config = configBundle.get(type);
var preset = config.getPreset(presetId);
return preset;
}
}

View File

@@ -1,18 +1,10 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import ru.m.tankz.game.GameState;
import ru.m.tankz.config.Config; 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;
typedef PlayerState = {
var tank:TankType;
var life:Int;
var score:Int;
var shots:Int;
var hits:Int;
var frags:Int;
}
class Player { class Player {
public var config(default, null):PlayerConfig; public var config(default, null):PlayerConfig;
public var id(default, null):PlayerId; public var id(default, null):PlayerId;
@@ -21,18 +13,11 @@ class Player {
public var isAlive(get, null):Bool; public var isAlive(get, null):Bool;
public var state(default, default):PlayerState; public var state(default, default):PlayerState;
public function new(teamId:TeamId, config:PlayerConfig) { public function new(teamId:TeamId, config:PlayerConfig, state:PlayerState = null) {
this.config = config; this.config = config;
this.id = new PlayerId(teamId, config.index); this.id = new PlayerId(teamId, config.index);
this.control = null; this.control = null;
this.state = { this.state = state == null ? new PlayerState(id) : state;
life: config.life,
tank: null,
score: 0,
shots: 0,
hits: 0,
frags: 0,
}
} }
private function set_tankId(value:Int):Int { private function set_tankId(value:Int):Int {

View File

@@ -69,7 +69,7 @@ class Spawner {
private function run():Void { private function run():Void {
if (timer == null) { if (timer == null) {
timer = new Timer(config.spawnInterval == null ? 1 : config.spawnInterval); timer = new Timer(config.spawnInterval == null ? 500 : config.spawnInterval);
timer.run = spawn; timer.run = spawn;
} }
} }
@@ -88,4 +88,4 @@ class Spawner {
private function get_active():Bool { private function get_active():Bool {
return queue.length > 0; return queue.length > 0;
} }
} }

View File

@@ -11,8 +11,11 @@ class Team {
public var players(default, null):Map<Int, Player>; public var players(default, null):Map<Int, Player>;
public var life(default, default):Int; public var life(default, default):Int;
public var isAlive(get, null):Bool; public var isAlive(get, null):Bool;
public var score(get, null):Int;
public var eagleId(default, default):Int; public var eagleId(default, default):Int;
private var active(default, default):Int;
public function new(config:TeamConfig, points:Array<SpawnPoint>) { public function new(config:TeamConfig, points:Array<SpawnPoint>) {
this.id = config.id; this.id = config.id;
this.config = config; this.config = config;
@@ -25,19 +28,23 @@ class Team {
this.spawner = new Spawner(config, points); this.spawner = new Spawner(config, points);
} }
public function trySpawn(playerId:PlayerId, spawn:Bool = false):Bool { public function tryRespawn(playerId:PlayerId):Bool {
var player:Player = players[playerId.index]; var player:Player = players[playerId.index];
var result = false; var result = player.state.life > 0 || life > active;
if (player.state.life > 0) { active++;
if (spawn) player.state.life--;
result = true;
} else if (life > 0) {
if (spawn) life--;
result = true;
}
return result; return result;
} }
public function onDestroy(playerId:PlayerId) {
active--;
var player:Player = players[playerId.index];
if (player.state.life > 0) {
player.state.life--;
} else {
life--;
}
}
// ToDo: eagle state? // ToDo: eagle state?
private function get_isAlive():Bool { private function get_isAlive():Bool {
if (life > 0) { if (life > 0) {
@@ -53,4 +60,8 @@ class Team {
} }
return false; return false;
} }
private function get_score():Int {
return Lambda.fold(players, function(p:Player, s:Int) return s + p.state.score, 0);
}
} }

View File

@@ -83,6 +83,7 @@ tanks:
<<: *bullet <<: *bullet
speed: 12.0 speed: 12.0
bullets: 1 bullets: 1
score: 100
skin: bc skin: bc
- type: fast - type: fast
@@ -93,6 +94,7 @@ tanks:
<<: *bullet <<: *bullet
speed: 8.0 speed: 8.0
bullets: 1 bullets: 1
score: 100
skin: bb skin: bb
bonuses: bonuses: