[common] added game progress

This commit is contained in:
2018-01-29 22:06:07 +03:00
parent 69d2e266cd
commit 46836a8bcb
8 changed files with 139 additions and 29 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "tankz", "name": "tankz",
"version": "0.1.0", "version": "0.2.0",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"ansi-colors": "^1.0.1", "ansi-colors": "^1.0.1",

View File

@@ -1,6 +1,10 @@
{ {
"pWidth": 100, "pHeight": 100, "pWidth": 100, "pHeight": 100,
"views": [ "views": [
{
"id": "state", "@type": "haxework.gui.LabelView", "@style": "label",
"pWidth": 100, "height": 20
},
{ {
"id": "render", "@type": "ru.m.tankz.render.Render", "id": "render", "@type": "ru.m.tankz.render.Render",
"contentSize": true "contentSize": true

View File

@@ -1,19 +1,30 @@
package ru.m.tankz.view.frames; package ru.m.tankz.view.frames;
import ru.m.tankz.game.GameState; import haxe.ds.Option;
import haxe.ds.Option;
import flash.events.Event; import flash.events.Event;
import haxe.Timer; import haxe.Timer;
import haxework.gui.frame.IFrameSwitcher;
import haxework.gui.LabelView;
import haxework.gui.VGroupView; import haxework.gui.VGroupView;
import haxework.gui.ViewBuilder; import haxework.gui.ViewBuilder;
import haxework.provider.Provider; import haxework.provider.Provider;
import protohx.Message; import protohx.Message;
import ru.m.connect.IConnection; import ru.m.connect.IConnection;
import ru.m.tankz.game.Game; import ru.m.tankz.game.Game;
import ru.m.tankz.game.GameState;
import ru.m.tankz.proto.pack.GameUpdateResponse; import ru.m.tankz.proto.pack.GameUpdateResponse;
import ru.m.tankz.render.Render;
interface GameFrameLayout {
var state(default, null):LabelView;
var render(default, null):Render;
}
@:template("layout/frames/game.json", "layout/styles.json") @:template("layout/frames/game.json", "layout/styles.json")
class GameFrame extends VGroupView implements ViewBuilder implements IPacketHandler { class GameFrame extends VGroupView implements ViewBuilder implements IPacketHandler implements GameFrameLayout {
private static inline var TAG = "GameFrame"; private static inline var TAG = "GameFrame";
@@ -26,18 +37,53 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand
} }
public function onShow():Void { public function onShow():Void {
var state:GameState = Provider.get(GameState); var s:GameState = Provider.get(GameState);
game = Provider.build(Game, state.type); game = Provider.build(Game, s.type);
if (game == null) { if (game == null) {
throw 'Unsupported game type "${state.type}"'; throw 'Unsupported game type "${s.type}"';
} }
game.engine.listeners.push(render); game.engine.listeners.push(render);
game.start(state); game.start(s).then(onGameStateChange).endThen(onGameComplete);
content.addEventListener(Event.ENTER_FRAME, redraw); content.addEventListener(Event.ENTER_FRAME, redraw);
//Provider.get(IConnection).packetHandler.addListener(this); //Provider.get(IConnection).packetHandler.addListener(this);
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);
}
private function stateString(state:GameState):String {
var result:Array<String> = [];
for (teamId in state.teams.keys()) {
var ts:TeamState = state.teams[teamId];
if (ts.lose) {
result.push('${teamId}: LOSE');
} else if (ts.life > -1) {
result.push('${teamId}: ${ts.life}');
} else {
for (index in ts.players.keys()) {
var ps:PlayerState = ts.players[index];
if (ps.life > -1) {
result.push('${teamId}${index}: ${ps.life}');
}
}
}
}
return result.join(' ');
}
private function onGameStateChange(s:GameState):GameState {
state.text = stateString(s);
return s;
}
private function onGameComplete(result:Option<GameState>):Void {
switch (result) {
case Option.Some(s):
state.text = stateString(s);
case Option.None:
}
Provider.get(IFrameSwitcher).change(StartFrame.ID);
} }
public function onHide():Void { public function onHide():Void {

View File

@@ -1,11 +1,15 @@
package ru.m.tankz.core; package ru.m.tankz.core;
import ru.m.tankz.game.Game;
import ru.m.geom.Rectangle; import ru.m.geom.Rectangle;
class Eagle extends Entity { class Eagle extends Entity {
public function new() { public var team(default, null):TeamId;
public function new(team:TeamId) {
super(new Rectangle(0, 0, 44, 44)); super(new Rectangle(0, 0, 44, 44));
this.team = team;
} }
} }

View File

@@ -19,10 +19,10 @@ class ClassicGame extends Game {
var state = new GameState(); var state = new GameState();
state.type = TYPE; state.type = TYPE;
state.level = level; state.level = level;
state.players[HUMAN] = new Map(); state.teams[HUMAN] = {life: -1, players: new Map(), lose: false};
state.players[BOT] = new Map(); state.teams[BOT] = {life: 20, players: new Map(), lose: false};
for (i in 0...humans) { for (i in 0...humans) {
state.players[HUMAN][i] = { state.teams[HUMAN].players[i] = {
index:i, index:i,
tank:{ tank:{
group: HUMAN, group: HUMAN,
@@ -36,7 +36,7 @@ class ClassicGame extends Game {
}; };
} }
for (i in 0...humans*2+2) { for (i in 0...humans*2+2) {
state.players[BOT][i] = { state.teams[BOT].players[i] = {
index:i, index:i,
tank:{ tank:{
group: BOT, group: BOT,

View File

@@ -21,10 +21,10 @@ class DotaGame extends Game {
var state = new GameState(); var state = new GameState();
state.type = TYPE; state.type = TYPE;
state.level = level; state.level = level;
state.players[RADIANT] = new Map(); state.teams[RADIANT] = {life: 20, players: new Map(), lose: false};
state.players[DIRE] = new Map(); state.teams[DIRE] = {life: 20, players: new Map(), lose: false};
for (i in 0...TEAM_SIZE) { for (i in 0...TEAM_SIZE) {
state.players[RADIANT][i] = { state.teams[RADIANT].players[i] = {
index:i, index:i,
tank:{ tank:{
group: RADIANT, group: RADIANT,
@@ -38,7 +38,7 @@ class DotaGame extends Game {
}; };
} }
for (i in 0...TEAM_SIZE) { for (i in 0...TEAM_SIZE) {
state.players[DIRE][i] = { state.teams[DIRE].players[i] = {
index:i, index:i,
tank:{ tank:{
group: DIRE, group: DIRE,

View File

@@ -1,19 +1,23 @@
package ru.m.tankz.game; package ru.m.tankz.game;
import ru.m.tankz.bot.BotControl; import haxe.Timer;
import ru.m.tankz.control.HumanControl; import promhx.Deferred;
import ru.m.tankz.config.ConfigBundle; import promhx.Stream;
import ru.m.tankz.config.LevelBundle;
import ru.m.tankz.game.Spawner;
import ru.m.tankz.core.Entity;
import ru.m.tankz.core.Eagle;
import ru.m.geom.Direction; import ru.m.geom.Direction;
import ru.m.geom.Point; import ru.m.geom.Point;
import ru.m.tankz.bot.BotControl;
import ru.m.tankz.config.Config; import ru.m.tankz.config.Config;
import ru.m.tankz.config.ConfigBundle;
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.core.Eagle;
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.GameState;
import ru.m.tankz.game.Spawner;
typedef GameType = String; typedef GameType = String;
@@ -35,6 +39,8 @@ class Game implements EngineListener {
public var engine(default, null):Engine; public var engine(default, null):Engine;
private var spawners:Map<TeamId, Spawner>; private var spawners:Map<TeamId, Spawner>;
private var deferred:Deferred<GameState>;
private var stream:Stream<GameState>;
public function new(type:GameType) { public function new(type:GameType) {
this.type = type; this.type = type;
@@ -58,7 +64,8 @@ class Game implements EngineListener {
entity.rect.direction = Direction.fromString(point.direction); entity.rect.direction = Direction.fromString(point.direction);
} }
public function start(state:GameState):Void { public function start(state:GameState):Stream<GameState> {
this.deferred = new Deferred();
this.state = state; this.state = state;
var bricks = LevelBundle.get(type, config, state.level); var bricks = LevelBundle.get(type, config, state.level);
engine.map.setData(bricks); engine.map.setData(bricks);
@@ -66,7 +73,7 @@ class Game implements EngineListener {
spawners = new Map<TeamId, Spawner>(); spawners = new Map<TeamId, Spawner>();
for (teamConfig in config.teams) { for (teamConfig in config.teams) {
var team = new Team(teamConfig); var team = new Team(teamConfig);
for (playerState in state.players.get(team.id)) { for (playerState in state.teams.get(team.id).players) {
var player = new Player({team:team.id, index:playerState.index}); var player = new Player({team:team.id, index:playerState.index});
team.players.push(player); team.players.push(player);
teams.set(team.id, team); teams.set(team.id, team);
@@ -92,11 +99,13 @@ class Game implements EngineListener {
} }
var eaglePoint = spawners.get(team.id).getPoint('eagle'); var eaglePoint = spawners.get(team.id).getPoint('eagle');
if (eaglePoint != null) { if (eaglePoint != null) {
var eagle = new Eagle(); var eagle = new Eagle(team.id);
applyPoint(eagle, eaglePoint); applyPoint(eagle, eaglePoint);
engine.spawn(eagle); engine.spawn(eagle);
} }
} }
return stream = deferred.stream();
} }
private function spawn(task:SpawnTask):Void { private function spawn(task:SpawnTask):Void {
@@ -106,6 +115,20 @@ class Game implements EngineListener {
player.tankId = tank.id; player.tankId = tank.id;
} }
private function complete():Void {
for (team in teams.iterator()) {
for (player in team.players) {
player.control.dispose();
}
}
var timer = new Timer(5000);
timer.run = function() {
timer.stop();
deferred.resolve(state);
stream.end();
}
}
public function setControl(playerId:PlayerId, control:Control):Void { public function setControl(playerId:PlayerId, control:Control):Void {
for (team in teams.iterator()) { for (team in teams.iterator()) {
if (team.id == playerId.team) { if (team.id == playerId.team) {
@@ -134,10 +157,36 @@ class Game implements EngineListener {
} }
} }
private function calcTeamLife(ts:TeamState):Int {
return Lambda.fold(ts.players, function(ps, t) return t + ps.life, ts.life);
}
public function onDestroy(entity:EntityType):Void { public function onDestroy(entity:EntityType):Void {
switch (entity) { switch (entity) {
case EntityType.TANK(tank): case EntityType.TANK(tank):
var respawn:Bool = false;
var teamState:TeamState = state.teams[tank.playerId.team];
if (teamState.life > 0) {
teamState.life--;
respawn = true;
} else {
var playerState:PlayerState = teamState.players[tank.playerId.index];
if (playerState.life > 0) {
playerState.life--;
respawn = true;
}
}
if (respawn) {
spawners.get(tank.playerId.team).push(tank.playerId); spawners.get(tank.playerId.team).push(tank.playerId);
} else if (calcTeamLife(teamState) < 1) {
state.teams[tank.playerId.team].lose = true;
complete();
}
deferred.resolve(state);
case EntityType.EAGLE(eagle):
state.teams[eagle.team].lose = true;
complete();
deferred.resolve(state);
case x: case x:
} }
} }

View File

@@ -21,14 +21,21 @@ typedef PlayerState = {
} }
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 level:Int; public var level:Int;
public var players:Map<TeamId, Map<Int, PlayerState>>; public var teams:Map<TeamId, TeamState>;
public function new() { public function new() {
type = null; type = null;
level = -1; level = -1;
players = new Map(); teams = new Map();
} }
} }