[common] add GameRunner
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
package ru.m.tankz.view.common;
|
||||
|
||||
import ru.m.tankz.game.GameTracer;
|
||||
import flash.events.Event;
|
||||
import haxe.ds.Option;
|
||||
import haxe.Timer;
|
||||
import haxework.view.frame.FrameSwitcher;
|
||||
import haxework.view.GroupView;
|
||||
import ru.m.tankz.game.Game;
|
||||
import ru.m.tankz.game.GameEvent;
|
||||
import ru.m.tankz.game.GameRunner;
|
||||
import ru.m.tankz.game.GameState;
|
||||
import ru.m.tankz.game.IGame;
|
||||
import ru.m.tankz.network.NetworkManager;
|
||||
import ru.m.tankz.render.Render;
|
||||
import ru.m.tankz.sound.SoundManager;
|
||||
@@ -28,8 +30,8 @@ class GameFrame extends GroupView implements GameListener {
|
||||
@:provide var switcher:FrameSwitcher;
|
||||
@:provide var storage:GameStorage;
|
||||
|
||||
private var game:Game;
|
||||
private var timer:Timer;
|
||||
private var game:IGame;
|
||||
private var runner:GameRunner;
|
||||
|
||||
private function get_render():Render {
|
||||
throw "Not implemented";
|
||||
@@ -51,19 +53,19 @@ class GameFrame extends GroupView implements GameListener {
|
||||
if (panel != null) {
|
||||
game.connect(panel);
|
||||
}
|
||||
game.start(state);
|
||||
timer = new Timer(10);
|
||||
timer.run = updateEngine;
|
||||
//game.connect(new GameTracer());
|
||||
runner = new GameRunner(game);
|
||||
runner.start(state);
|
||||
content.addEventListener(Event.ENTER_FRAME, _redraw);
|
||||
render.draw(game.engine);
|
||||
}
|
||||
|
||||
private function stop():Void {
|
||||
if (timer != null) {
|
||||
timer.stop();
|
||||
timer = null;
|
||||
}
|
||||
content.removeEventListener(Event.ENTER_FRAME, _redraw);
|
||||
if (runner != null) {
|
||||
runner.dispose();
|
||||
runner = null;
|
||||
}
|
||||
if (game != null) {
|
||||
game.dispose();
|
||||
game = null;
|
||||
@@ -75,7 +77,7 @@ class GameFrame extends GroupView implements GameListener {
|
||||
switch event {
|
||||
case GameEvent.COMPLETE(state, winner):
|
||||
result = state;
|
||||
this.state = switch game.next() {
|
||||
this.state = switch runner.next() {
|
||||
case Some(s):
|
||||
// ToDo:
|
||||
var progress = storage.get(game.type);
|
||||
@@ -94,10 +96,6 @@ class GameFrame extends GroupView implements GameListener {
|
||||
stop();
|
||||
}
|
||||
|
||||
private function updateEngine():Void {
|
||||
game.engine.update();
|
||||
}
|
||||
|
||||
private function _redraw(_):Void {
|
||||
render.draw(game.engine);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ru.m.tankz.control;
|
||||
|
||||
import ru.m.tankz.game.GameEvent;
|
||||
import ru.m.geom.Direction;
|
||||
import ru.m.tankz.core.EntityType;
|
||||
import ru.m.tankz.game.IGame;
|
||||
@@ -27,7 +28,7 @@ class Control {
|
||||
|
||||
public function action(action:TankAction):Void {
|
||||
if (tankId != 0 && handler != null) {
|
||||
handler.action(tankId, action);
|
||||
handler.gameEventSignal.emit(GameEvent.ACTION(tankId, action));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,17 @@
|
||||
package ru.m.tankz.game;
|
||||
|
||||
import ru.m.tankz.game.GameEvent.ChangeEvent;
|
||||
import haxe.ds.Option;
|
||||
import haxe.Timer;
|
||||
import ru.m.geom.Point;
|
||||
import ru.m.tankz.bundle.IConfigBundle;
|
||||
import ru.m.tankz.config.Config;
|
||||
import ru.m.tankz.control.Control;
|
||||
import ru.m.tankz.control.Controller;
|
||||
import ru.m.tankz.control.IControlFactory;
|
||||
import ru.m.tankz.core.Bonus;
|
||||
import ru.m.tankz.core.Eagle;
|
||||
import ru.m.tankz.core.Entity;
|
||||
import ru.m.tankz.core.EntityType;
|
||||
import ru.m.tankz.core.Tank;
|
||||
import ru.m.tankz.engine.Engine;
|
||||
import ru.m.tankz.game.GameState;
|
||||
import ru.m.tankz.game.Spawner;
|
||||
import ru.m.tankz.Type;
|
||||
|
||||
interface GameListener {
|
||||
public function onGameEvent(event:GameEvent):Void;
|
||||
}
|
||||
|
||||
@:dispatcher(GameListener) class Game implements IGame implements EngineListener implements GameListener {
|
||||
@:dispatcher(GameListener) class Game implements IGame implements GameListener {
|
||||
|
||||
private static var TAG(default, never):String = "Game";
|
||||
|
||||
@@ -35,35 +23,16 @@ interface GameListener {
|
||||
|
||||
public var state(default, null):GameState;
|
||||
|
||||
private var points:Array<SpawnPoint>;
|
||||
|
||||
@:provide var configBundle:IConfigBundle;
|
||||
@:provide var controlFactory:IControlFactory;
|
||||
|
||||
public function new(type:GameType) {
|
||||
this.type = type;
|
||||
this.teams = new Map();
|
||||
this.config = configBundle.get(type);
|
||||
this.engine = new Engine(config);
|
||||
engine.connect(this);
|
||||
connect(this);
|
||||
}
|
||||
|
||||
public function action(tankId:Int, action:TankAction):Void {
|
||||
if (!engine.entities.exists(tankId)) return;
|
||||
var tank:Tank = cast engine.entities.get(tankId);
|
||||
switch (action) {
|
||||
case MOVE(direction):
|
||||
tank.move(direction);
|
||||
case STOP:
|
||||
tank.stop();
|
||||
case SHOT:
|
||||
var bullet = tank.shot();
|
||||
if (bullet != null) {
|
||||
gameEventSignal.emit(GameEvent.SPAWN(BULLET(bullet)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public inline function getTeam(teamId:TeamId):Team {
|
||||
return teams[teamId];
|
||||
}
|
||||
@@ -72,323 +41,33 @@ interface GameListener {
|
||||
return teams[playerId.team].players[playerId.index];
|
||||
}
|
||||
|
||||
private function buildTank(task:SpawnTask):Tank {
|
||||
var player = getPlayer(task.playerId);
|
||||
var tankType:TankType = if (task.tankType != null) {
|
||||
task.tankType;
|
||||
} else {
|
||||
var spawns:Array<TankSpawn> = player.config.tanks;
|
||||
var spawn:TankSpawn = spawns[Math.floor(Math.random() * spawns.length)];
|
||||
spawn.type;
|
||||
}
|
||||
var tankConfig:TankConfig = config.getTank(tankType);
|
||||
var tank = new Tank(task.playerId, tankConfig);
|
||||
tank.color = state.getPlayerColor(player.id);
|
||||
tank.bonus = Math.random() < player.config.bonus;
|
||||
applyPoint(tank, task.point);
|
||||
if (player.config.protect > 0) {
|
||||
tank.protect.on(player.config.protect);
|
||||
}
|
||||
return tank;
|
||||
}
|
||||
|
||||
private function applyPoint(entity:Entity, point:SpawnPoint):Void {
|
||||
entity.rect.center = new Point((point.x + 1) * engine.map.cellWidth, (point.y + 1) * engine.map.cellHeight);
|
||||
entity.rect.direction = point.direction;
|
||||
}
|
||||
|
||||
public function start(state:GameState):Void {
|
||||
this.state = state;
|
||||
this.winner = null;
|
||||
var level:LevelConfig = state.level;
|
||||
points = level.points != null ? level.points : config.points;
|
||||
engine.map.setData(level.data);
|
||||
teams = new Map<TeamId, Team>();
|
||||
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;
|
||||
for (player in team.players.iterator()) {
|
||||
var control = controlFactory.build(player.id, AController.fromString(player.config.control));
|
||||
L.d(TAG, 'control(${player.id} - ${control})');
|
||||
if (control != null) {
|
||||
player.control = control;
|
||||
player.control.bind(this);
|
||||
}
|
||||
}
|
||||
team.spawner.runner = spawn;
|
||||
}
|
||||
|
||||
for (team in teams.iterator()) {
|
||||
for (player in team.players.iterator()) {
|
||||
if (team.tryRespawn(player.id)) {
|
||||
team.spawner.push(player.id, player.state.tank);
|
||||
}
|
||||
}
|
||||
var eaglePoint = team.spawner.getPoint("eagle");
|
||||
if (eaglePoint != null) {
|
||||
var eagle = new Eagle(team.id, team.config.eagle);
|
||||
eagle.color = config.getColor(new PlayerId(eagle.team, -1));
|
||||
team.eagleId = eagle.id;
|
||||
applyPoint(eagle, eaglePoint);
|
||||
gameEventSignal.emit(GameEvent.SPAWN(EAGLE(eagle)));
|
||||
}
|
||||
}
|
||||
gameEventSignal.emit(GameEvent.START(state));
|
||||
}
|
||||
|
||||
private function spawn(task:SpawnTask):Void {
|
||||
L.d(TAG, 'spawn(${task})');
|
||||
var team = getTeam(task.playerId.team);
|
||||
var player = getPlayer(task.playerId);
|
||||
player.tankId = 0;
|
||||
var tank = buildTank(task);
|
||||
player.tankId = tank.id;
|
||||
player.state.tank = tank.config.type;
|
||||
gameEventSignal.emit(GameEvent.SPAWN(TANK(tank)));
|
||||
}
|
||||
|
||||
private function checkComplete():Void {
|
||||
var actives:Array<TeamId> = [];
|
||||
for (team in teams.iterator()) {
|
||||
if (team.isAlive) {
|
||||
if (team.eagleId > 0) {
|
||||
if (!cast(engine.entities[team.eagleId], Eagle).death) {
|
||||
actives.push(team.id);
|
||||
}
|
||||
} else {
|
||||
actives.push(team.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (actives.length == 1) {
|
||||
win(actives[0]);
|
||||
}
|
||||
if (actives.length == 0) {
|
||||
win(null);
|
||||
}
|
||||
}
|
||||
|
||||
private function complete():Void {
|
||||
for (team in teams.iterator()) {
|
||||
for (player in team.players) {
|
||||
player.control.action(TankAction.STOP);
|
||||
player.control.dispose();
|
||||
}
|
||||
}
|
||||
Timer.delay(function() {
|
||||
gameEventSignal.emit(GameEvent.COMPLETE(state, getTeam(winner)));
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
public function onCollision(entity:EntityType, with:EntityType):Void {
|
||||
switch entity {
|
||||
case EntityType.TANK(tank):
|
||||
var control = getPlayer(tank.playerId).control;
|
||||
if (control != null) control.onCollision(with);
|
||||
case _:
|
||||
}
|
||||
switch [entity, with] {
|
||||
case [TANK(tank), TANK(other_tank)]:
|
||||
tank.rect.lean(other_tank.rect);
|
||||
case [TANK(tank), EAGLE(eagle)]:
|
||||
tank.rect.lean(eagle.rect);
|
||||
case [BULLET(bullet), BULLET(other_bullet)]:
|
||||
gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet)));
|
||||
gameEventSignal.emit(GameEvent.DESTROY(BULLET(other_bullet)));
|
||||
case [BULLET(bullet), CELL(cell)]:
|
||||
gameEventSignal.emit(GameEvent.HIT(CELL(cell, bullet.tank, bullet)));
|
||||
gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet)));
|
||||
case [TANK(tank), BONUS(bonus)]:
|
||||
gameEventSignal.emit(GameEvent.DESTROY(BONUS(bonus, tank, bonus.config.score)));
|
||||
case [BULLET(bullet), TANK(tank)]/* | [TANK(tank), BULLET(bullet)]*/:
|
||||
if (bullet.tankId == tank.id || (!engine.config.game.friendlyFire && tank.playerId.team == bullet.playerId.team)) {
|
||||
// Nothing
|
||||
} else {
|
||||
if (!tank.protect.active) {
|
||||
if (tank.hits > 0) {
|
||||
tank.hits--;
|
||||
if (tank.bonus) {
|
||||
tank.bonus = false;
|
||||
spawnBonus();
|
||||
}
|
||||
gameEventSignal.emit(GameEvent.HIT(TANK(tank, bullet.tank, bullet)));
|
||||
} else if (tank.config.downgrade != null) {
|
||||
tank.config = engine.config.getTank(tank.config.downgrade);
|
||||
gameEventSignal.emit(GameEvent.HIT(TANK(tank, bullet.tank, bullet)));
|
||||
} else {
|
||||
var score = tank.config.score;
|
||||
if (tank.playerId.team == bullet.playerId.team) {
|
||||
score = Math.round(score * -0.5);
|
||||
}
|
||||
gameEventSignal.emit(GameEvent.DESTROY(TANK(tank, bullet.tank, bullet, score)));
|
||||
}
|
||||
}
|
||||
gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet)));
|
||||
}
|
||||
case [BULLET(bullet), EAGLE(eagle)]:
|
||||
if (!eagle.protect.active) {
|
||||
gameEventSignal.emit(GameEvent.DESTROY(EAGLE(eagle, bullet.tank, bullet, eagle.score)));
|
||||
}
|
||||
gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet)));
|
||||
case _:
|
||||
}
|
||||
}
|
||||
|
||||
private function win(teamId:TeamId):Void {
|
||||
winner = teamId;
|
||||
complete();
|
||||
}
|
||||
|
||||
public function onAction(tankId:Int, action:TankAction):Void {
|
||||
this.action(tankId, action);
|
||||
}
|
||||
|
||||
public function next():Option<GameState> {
|
||||
for (rule in config.game.complete) {
|
||||
if (rule.team != null && rule.team != winner) {
|
||||
return Option.None;
|
||||
}
|
||||
}
|
||||
var level = this.state.levelId + 1;
|
||||
if (level >= config.game.levels) level = 0;
|
||||
return Option.Some(new GameState(type, state.presetId, level, state));
|
||||
}
|
||||
|
||||
public function dispose():Void {
|
||||
engine.dispose();
|
||||
gameEventSignal.dispose();
|
||||
}
|
||||
|
||||
private function spawnBonus(?type:BonusType):Void {
|
||||
var bonusConfig:BonusConfig = type != null ? config.getBonus(type) : config.bonuses[Math.floor(Math.random() * config.bonuses.length)];
|
||||
var bonus = new Bonus(bonusConfig);
|
||||
bonus.rect.x = Math.round(Math.random() * engine.map.width / engine.map.cellWidth) * engine.map.cellWidth;
|
||||
bonus.rect.y = Math.round(Math.random() * engine.map.height/ engine.map.cellHeight) * engine.map.cellHeight;
|
||||
gameEventSignal.emit(GameEvent.SPAWN(BONUS(bonus)));
|
||||
}
|
||||
|
||||
private inline function alienTank(team:TeamId):Tank->Bool {
|
||||
return function(tank:Tank):Bool return team != tank.playerId.team;
|
||||
}
|
||||
|
||||
private function applyBonus(tank:Tank, bonus:Bonus):Void {
|
||||
switch (bonus.config.type) {
|
||||
case "life":
|
||||
changeLife(tank.playerId, 1);
|
||||
case "star":
|
||||
upgradeTank(tank);
|
||||
case "grenade":
|
||||
for (t in engine.iterTanks(alienTank(tank.playerId.team))) {
|
||||
gameEventSignal.emit(GameEvent.DESTROY(TANK(t, tank, null, 0)));
|
||||
}
|
||||
case "helmet":
|
||||
tank.protect.on(bonus.config.duration);
|
||||
case "clock":
|
||||
for (t in engine.iterTanks(alienTank(tank.playerId.team))) {
|
||||
t.freezing.on(bonus.config.duration);
|
||||
}
|
||||
case "shovel":
|
||||
// ToDo: protect eagle/area
|
||||
var team:Team = teams[tank.playerId.team];
|
||||
if (team.eagleId > 0) {
|
||||
var eagle:Eagle = cast(engine.entities[team.eagleId], Eagle);
|
||||
eagle.protect.on(bonus.config.duration);
|
||||
}
|
||||
case "gun":
|
||||
upgradeTank(tank, 5);
|
||||
case _:
|
||||
gameEventSignal.emit(GameEvent.DESTROY(TANK(tank, null, null, 0))); // :-D
|
||||
}
|
||||
}
|
||||
|
||||
private function upgradeTank(tank:Tank, level:Int = 1):Void {
|
||||
if (tank.config.upgrade != null) {
|
||||
while (level-- > 0 && tank.config.upgrade != null) {
|
||||
tank.config = config.getTank(tank.config.upgrade);
|
||||
}
|
||||
} else {
|
||||
tank.hits++;
|
||||
}
|
||||
}
|
||||
|
||||
private function changeScore(playerId:PlayerId, score:Int):Void {
|
||||
var player = getPlayer(playerId);
|
||||
var team = getTeam(playerId.team);
|
||||
player.state.score += score;
|
||||
gameEventSignal.emit(GameEvent.CHANGE(PLAYER_SCORE(player, player.state.score)));
|
||||
gameEventSignal.emit(GameEvent.CHANGE(TEAM_SCORE(team, state.getTeamScore(team.id))));
|
||||
}
|
||||
|
||||
private function changeLife(playerId:PlayerId, life:Int):Void {
|
||||
var player = getPlayer(playerId);
|
||||
player.state.life += life;
|
||||
gameEventSignal.emit(GameEvent.CHANGE(PLAYER_LIFE(player, player.state.life)));
|
||||
}
|
||||
|
||||
private function changeTeamLife(teamId:TeamId, life:Int):Void {
|
||||
var team = getTeam(teamId);
|
||||
team.state.life += life;
|
||||
gameEventSignal.emit(GameEvent.CHANGE(TEAM_LIFE(team, team.state.life)));
|
||||
}
|
||||
|
||||
public function onGameEvent(event:GameEvent):Void {
|
||||
switch event {
|
||||
case GameEvent.START(state):
|
||||
this.state = state;
|
||||
case GameEvent.COMPLETE(state, team):
|
||||
this.state = state;
|
||||
this.winner = team.id;
|
||||
case GameEvent.SPAWN(EAGLE(eagle)):
|
||||
engine.spawn(eagle);
|
||||
case GameEvent.SPAWN(TANK(tank)):
|
||||
engine.spawn(tank);
|
||||
getPlayer(tank.playerId).control.start();
|
||||
case GameEvent.SPAWN(BULLET(bullet)):
|
||||
engine.spawn(bullet);
|
||||
case GameEvent.SPAWN(BONUS(bonus)):
|
||||
engine.spawn(bonus);
|
||||
case GameEvent.DESTROY(EAGLE(eagle, who, wherewith, score)):
|
||||
eagle.death = true;
|
||||
if (score != 0) {
|
||||
changeScore(who.playerId, score);
|
||||
}
|
||||
checkComplete();
|
||||
case GameEvent.DESTROY(TANK(tank, who, wherewith, score)):
|
||||
engine.destroy(tank);
|
||||
var team = getTeam(tank.playerId.team);
|
||||
var player = getPlayer(tank.playerId);
|
||||
player.control.stop();
|
||||
player.tankId = 0; //ToDo: ?
|
||||
team.onDestroy(player.id);
|
||||
if (player.state.life > 0) {
|
||||
changeLife(player.id, -1);
|
||||
} else if (team.state.life > 0) {
|
||||
changeTeamLife(team.id, -1);
|
||||
}
|
||||
var respawn:Bool = team.tryRespawn(player.id);
|
||||
if (respawn) {
|
||||
team.spawner.push(player.id);
|
||||
}
|
||||
if (!team.isAlive) {
|
||||
checkComplete();
|
||||
}
|
||||
if (tank.bonus && wherewith != null) {
|
||||
spawnBonus();
|
||||
}
|
||||
if (score != 0) {
|
||||
changeScore(who.playerId, score);
|
||||
}
|
||||
case GameEvent.DESTROY(CELL(cell, who, wherewith)):
|
||||
|
||||
case GameEvent.DESTROY(BONUS(bonus, who, score)):
|
||||
engine.destroy(bonus);
|
||||
applyBonus(who, bonus);
|
||||
if (score != 0) {
|
||||
changeScore(who.playerId, score);
|
||||
}
|
||||
case GameEvent.DESTROY(BULLET(bullet)):
|
||||
engine.destroy(bullet);
|
||||
var tank:Tank = bullet.tank;
|
||||
if (tank != null) {
|
||||
tank.onDestroyBullet();
|
||||
}
|
||||
case _:
|
||||
}
|
||||
}
|
||||
|
||||
public function dispose():Void {
|
||||
gameEventSignal.dispose();
|
||||
engine.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ru.m.tankz.game;
|
||||
|
||||
import ru.m.tankz.control.Control.TankAction;
|
||||
import ru.m.tankz.core.Bonus;
|
||||
import ru.m.tankz.core.Eagle;
|
||||
import ru.m.tankz.core.Tank;
|
||||
@@ -40,4 +41,5 @@ enum GameEvent {
|
||||
DESTROY(event:DestroyEvent);
|
||||
CHANGE(event:ChangeEvent);
|
||||
COMPLETE(state:GameState, winner:Team);
|
||||
ACTION(tankId:Int, action:TankAction);
|
||||
}
|
||||
|
||||
363
src/common/haxe/ru/m/tankz/game/GameRunner.hx
Normal file
363
src/common/haxe/ru/m/tankz/game/GameRunner.hx
Normal file
@@ -0,0 +1,363 @@
|
||||
package ru.m.tankz.game;
|
||||
|
||||
import haxe.ds.Option;
|
||||
import haxe.Timer;
|
||||
import haxework.signal.Signal;
|
||||
import ru.m.geom.Point;
|
||||
import ru.m.tankz.config.Config;
|
||||
import ru.m.tankz.control.Control;
|
||||
import ru.m.tankz.control.Controller;
|
||||
import ru.m.tankz.control.IControlFactory;
|
||||
import ru.m.tankz.core.Bonus;
|
||||
import ru.m.tankz.core.Eagle;
|
||||
import ru.m.tankz.core.Entity;
|
||||
import ru.m.tankz.core.EntityType;
|
||||
import ru.m.tankz.core.Tank;
|
||||
import ru.m.tankz.engine.Engine;
|
||||
import ru.m.tankz.game.Game;
|
||||
import ru.m.tankz.game.Spawner;
|
||||
import ru.m.tankz.Type;
|
||||
|
||||
class GameRunner implements EngineListener implements GameListener {
|
||||
@:provide var controlFactory:IControlFactory;
|
||||
|
||||
private var game(default, null):IGame;
|
||||
private var gameEventSignal(get, null):Signal<GameEvent>;
|
||||
private var points:Array<SpawnPoint>;
|
||||
|
||||
private var timer:Timer;
|
||||
|
||||
public function new(game:IGame) {
|
||||
this.game = game;
|
||||
this.game.connect(this);
|
||||
this.game.engine.connect(this);
|
||||
}
|
||||
|
||||
private inline function get_gameEventSignal():Signal<GameEvent> {
|
||||
return game.gameEventSignal;
|
||||
}
|
||||
|
||||
public function dispose():Void {
|
||||
game.disconnect(this);
|
||||
game.engine.disconnect(this);
|
||||
}
|
||||
|
||||
private function updateEngine():Void {
|
||||
game.engine.update();
|
||||
}
|
||||
|
||||
private function applyAction(tankId:Int, action:TankAction):Void {
|
||||
if (!game.engine.entities.exists(tankId)) return;
|
||||
var tank:Tank = cast game.engine.entities.get(tankId);
|
||||
switch (action) {
|
||||
case MOVE(direction):
|
||||
tank.move(direction);
|
||||
case STOP:
|
||||
tank.stop();
|
||||
case SHOT:
|
||||
var bullet = tank.shot();
|
||||
if (bullet != null) {
|
||||
gameEventSignal.emit(GameEvent.SPAWN(BULLET(bullet)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function buildTank(task:SpawnTask):Tank {
|
||||
var player = game.getPlayer(task.playerId);
|
||||
var tankType:TankType = if (task.tankType != null) {
|
||||
task.tankType;
|
||||
} else {
|
||||
var spawns:Array<TankSpawn> = player.config.tanks;
|
||||
var spawn:TankSpawn = spawns[Math.floor(Math.random() * spawns.length)];
|
||||
spawn.type;
|
||||
}
|
||||
var tankConfig:TankConfig = game.config.getTank(tankType);
|
||||
var tank = new Tank(task.playerId, tankConfig);
|
||||
tank.color = game.state.getPlayerColor(player.id);
|
||||
tank.bonus = Math.random() < player.config.bonus;
|
||||
applyPoint(tank, task.point);
|
||||
if (player.config.protect > 0) {
|
||||
tank.protect.on(player.config.protect);
|
||||
}
|
||||
return tank;
|
||||
}
|
||||
|
||||
private function applyPoint(entity:Entity, point:SpawnPoint):Void {
|
||||
entity.rect.center = new Point((point.x + 1) * game.engine.map.cellWidth, (point.y + 1) * game.engine.map.cellHeight);
|
||||
entity.rect.direction = point.direction;
|
||||
}
|
||||
|
||||
public function start(state:GameState):Void {
|
||||
var level:LevelConfig = state.level;
|
||||
points = level.points != null ? level.points : game.config.points;
|
||||
game.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]);
|
||||
game.teams[team.id] = team;
|
||||
for (player in team.players.iterator()) {
|
||||
var control = controlFactory.build(player.id, AController.fromString(player.config.control));
|
||||
if (control != null) {
|
||||
player.control = control;
|
||||
player.control.bind(game);
|
||||
}
|
||||
}
|
||||
team.spawner.runner = spawn;
|
||||
}
|
||||
for (team in game.teams.iterator()) {
|
||||
for (player in team.players.iterator()) {
|
||||
if (team.tryRespawn(player.id)) {
|
||||
team.spawner.push(player.id, player.state.tank);
|
||||
}
|
||||
}
|
||||
var eaglePoint = team.spawner.getPoint("eagle");
|
||||
if (eaglePoint != null) {
|
||||
var eagle = new Eagle(team.id, team.config.eagle);
|
||||
eagle.color = game.config.getColor(new PlayerId(eagle.team, -1));
|
||||
team.eagleId = eagle.id;
|
||||
applyPoint(eagle, eaglePoint);
|
||||
gameEventSignal.emit(GameEvent.SPAWN(EAGLE(eagle)));
|
||||
}
|
||||
}
|
||||
gameEventSignal.emit(GameEvent.START(state));
|
||||
timer = new Timer(10);
|
||||
timer.run = updateEngine;
|
||||
}
|
||||
|
||||
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 {
|
||||
var team = game.getTeam(task.playerId.team);
|
||||
var player = game.getPlayer(task.playerId);
|
||||
player.tankId = 0;
|
||||
var tank = buildTank(task);
|
||||
player.tankId = tank.id;
|
||||
player.state.tank = tank.config.type;
|
||||
gameEventSignal.emit(GameEvent.SPAWN(TANK(tank)));
|
||||
}
|
||||
|
||||
private function checkComplete():Void {
|
||||
var actives:Array<TeamId> = [];
|
||||
for (team in game.teams.iterator()) {
|
||||
if (team.isAlive) {
|
||||
if (team.eagleId > 0) {
|
||||
if (!cast(game.engine.entities[team.eagleId], Eagle).death) {
|
||||
actives.push(team.id);
|
||||
}
|
||||
} else {
|
||||
actives.push(team.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (actives.length == 1) {
|
||||
complete(actives[0]);
|
||||
}
|
||||
if (actives.length == 0) {
|
||||
complete(null);
|
||||
}
|
||||
}
|
||||
|
||||
private function complete(winner:TeamId):Void {
|
||||
for (team in game.teams.iterator()) {
|
||||
for (player in team.players) {
|
||||
player.control.action(TankAction.STOP);
|
||||
player.control.dispose();
|
||||
}
|
||||
}
|
||||
Timer.delay(function() {
|
||||
gameEventSignal.emit(GameEvent.COMPLETE(game.state, game.getTeam(winner)));
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
public function onCollision(entity:EntityType, with:EntityType):Void {
|
||||
switch entity {
|
||||
case EntityType.TANK(tank):
|
||||
var control = game.getPlayer(tank.playerId).control;
|
||||
if (control != null) control.onCollision(with);
|
||||
case _:
|
||||
}
|
||||
switch [entity, with] {
|
||||
case [TANK(tank), TANK(other_tank)]:
|
||||
tank.rect.lean(other_tank.rect);
|
||||
case [TANK(tank), EAGLE(eagle)]:
|
||||
tank.rect.lean(eagle.rect);
|
||||
case [BULLET(bullet), BULLET(other_bullet)]:
|
||||
gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet)));
|
||||
gameEventSignal.emit(GameEvent.DESTROY(BULLET(other_bullet)));
|
||||
case [BULLET(bullet), CELL(cell)]:
|
||||
gameEventSignal.emit(GameEvent.HIT(CELL(cell, bullet.tank, bullet)));
|
||||
gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet)));
|
||||
case [TANK(tank), BONUS(bonus)]:
|
||||
gameEventSignal.emit(GameEvent.DESTROY(BONUS(bonus, tank, bonus.config.score)));
|
||||
case [BULLET(bullet), TANK(tank)]/* | [TANK(tank), BULLET(bullet)]*/:
|
||||
if (bullet.tankId == tank.id || (!game.config.game.friendlyFire && tank.playerId.team == bullet.playerId.team)) {
|
||||
// Nothing
|
||||
} else {
|
||||
if (!tank.protect.active) {
|
||||
if (tank.hits > 0) {
|
||||
tank.hits--;
|
||||
if (tank.bonus) {
|
||||
tank.bonus = false;
|
||||
spawnBonus();
|
||||
}
|
||||
gameEventSignal.emit(GameEvent.HIT(TANK(tank, bullet.tank, bullet)));
|
||||
} else if (tank.config.downgrade != null) {
|
||||
tank.config = game.config.getTank(tank.config.downgrade);
|
||||
gameEventSignal.emit(GameEvent.HIT(TANK(tank, bullet.tank, bullet)));
|
||||
} else {
|
||||
var score = tank.config.score;
|
||||
if (tank.playerId.team == bullet.playerId.team) {
|
||||
score = Math.round(score * -0.5);
|
||||
}
|
||||
gameEventSignal.emit(GameEvent.DESTROY(TANK(tank, bullet.tank, bullet, score)));
|
||||
}
|
||||
}
|
||||
gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet)));
|
||||
}
|
||||
case [BULLET(bullet), EAGLE(eagle)]:
|
||||
if (!eagle.protect.active) {
|
||||
gameEventSignal.emit(GameEvent.DESTROY(EAGLE(eagle, bullet.tank, bullet, eagle.score)));
|
||||
}
|
||||
gameEventSignal.emit(GameEvent.DESTROY(BULLET(bullet)));
|
||||
case _:
|
||||
}
|
||||
}
|
||||
|
||||
private function spawnBonus(?type:BonusType):Void {
|
||||
var bonusConfig:BonusConfig = type != null ? game.config.getBonus(type) : game.config.bonuses[Math.floor(Math.random() * game.config.bonuses.length)];
|
||||
var bonus = new Bonus(bonusConfig);
|
||||
bonus.rect.x = Math.round(Math.random() * game.engine.map.width / game.engine.map.cellWidth) * game.engine.map.cellWidth;
|
||||
bonus.rect.y = Math.round(Math.random() * game.engine.map.height/ game.engine.map.cellHeight) * game.engine.map.cellHeight;
|
||||
gameEventSignal.emit(GameEvent.SPAWN(BONUS(bonus)));
|
||||
}
|
||||
|
||||
private inline function alienTank(team:TeamId):Tank->Bool {
|
||||
return function(tank:Tank):Bool return team != tank.playerId.team;
|
||||
}
|
||||
|
||||
private function applyBonus(tank:Tank, bonus:Bonus):Void {
|
||||
switch (bonus.config.type) {
|
||||
case "life":
|
||||
changeLife(tank.playerId, 1);
|
||||
case "star":
|
||||
upgradeTank(tank);
|
||||
case "grenade":
|
||||
for (t in game.engine.iterTanks(alienTank(tank.playerId.team))) {
|
||||
gameEventSignal.emit(GameEvent.DESTROY(TANK(t, tank, null, 0)));
|
||||
}
|
||||
case "helmet":
|
||||
tank.protect.on(bonus.config.duration);
|
||||
case "clock":
|
||||
for (t in game.engine.iterTanks(alienTank(tank.playerId.team))) {
|
||||
t.freezing.on(bonus.config.duration);
|
||||
}
|
||||
case "shovel":
|
||||
// ToDo: protect eagle/area
|
||||
var team:Team = game.teams[tank.playerId.team];
|
||||
if (team.eagleId > 0) {
|
||||
var eagle:Eagle = cast(game.engine.entities[team.eagleId], Eagle);
|
||||
eagle.protect.on(bonus.config.duration);
|
||||
}
|
||||
case "gun":
|
||||
upgradeTank(tank, 5);
|
||||
case _:
|
||||
gameEventSignal.emit(GameEvent.DESTROY(TANK(tank, null, null, 0))); // :-D
|
||||
}
|
||||
}
|
||||
|
||||
private function upgradeTank(tank:Tank, level:Int = 1):Void {
|
||||
if (tank.config.upgrade != null) {
|
||||
while (level-- > 0 && tank.config.upgrade != null) {
|
||||
tank.config = game.config.getTank(tank.config.upgrade);
|
||||
}
|
||||
} else {
|
||||
tank.hits++;
|
||||
}
|
||||
}
|
||||
|
||||
private function changeScore(playerId:PlayerId, score:Int):Void {
|
||||
var player = game.getPlayer(playerId);
|
||||
var team = game.getTeam(playerId.team);
|
||||
player.state.score += score;
|
||||
gameEventSignal.emit(GameEvent.CHANGE(PLAYER_SCORE(player, player.state.score)));
|
||||
gameEventSignal.emit(GameEvent.CHANGE(TEAM_SCORE(team, game.state.getTeamScore(team.id))));
|
||||
}
|
||||
|
||||
private function changeLife(playerId:PlayerId, life:Int):Void {
|
||||
var player = game.getPlayer(playerId);
|
||||
player.state.life += life;
|
||||
gameEventSignal.emit(GameEvent.CHANGE(PLAYER_LIFE(player, player.state.life)));
|
||||
}
|
||||
|
||||
private function changeTeamLife(teamId:TeamId, life:Int):Void {
|
||||
var team = game.getTeam(teamId);
|
||||
team.state.life += life;
|
||||
gameEventSignal.emit(GameEvent.CHANGE(TEAM_LIFE(team, team.state.life)));
|
||||
}
|
||||
|
||||
public function onGameEvent(event:GameEvent):Void {
|
||||
switch event {
|
||||
case GameEvent.START(_):
|
||||
|
||||
case GameEvent.COMPLETE(_, _):
|
||||
if (timer != null) {
|
||||
timer.stop();
|
||||
timer = null;
|
||||
}
|
||||
case GameEvent.ACTION(tankId, action):
|
||||
applyAction(tankId, action);
|
||||
case GameEvent.SPAWN(TANK(tank)):
|
||||
game.getPlayer(tank.playerId).control.start();
|
||||
case GameEvent.DESTROY(EAGLE(eagle, who, wherewith, score)):
|
||||
eagle.death = true;
|
||||
if (score != 0) {
|
||||
changeScore(who.playerId, score);
|
||||
}
|
||||
checkComplete();
|
||||
case GameEvent.DESTROY(TANK(tank, who, wherewith, score)):
|
||||
var team = game.getTeam(tank.playerId.team);
|
||||
var player = game.getPlayer(tank.playerId);
|
||||
player.control.stop();
|
||||
player.tankId = 0; //ToDo: ?
|
||||
team.onDestroy(player.id);
|
||||
if (player.state.life > 0) {
|
||||
changeLife(player.id, -1);
|
||||
} else if (team.state.life > 0) {
|
||||
changeTeamLife(team.id, -1);
|
||||
}
|
||||
var respawn:Bool = team.tryRespawn(player.id);
|
||||
if (respawn) {
|
||||
team.spawner.push(player.id);
|
||||
}
|
||||
if (!team.isAlive) {
|
||||
checkComplete();
|
||||
}
|
||||
if (tank.bonus && wherewith != null) {
|
||||
spawnBonus();
|
||||
}
|
||||
if (score != 0) {
|
||||
changeScore(who.playerId, score);
|
||||
}
|
||||
case GameEvent.DESTROY(BONUS(bonus, who, score)):
|
||||
applyBonus(who, bonus);
|
||||
if (score != 0) {
|
||||
changeScore(who.playerId, score);
|
||||
}
|
||||
case GameEvent.DESTROY(BULLET(bullet)):
|
||||
var tank:Tank = bullet.tank;
|
||||
if (tank != null) {
|
||||
tank.onDestroyBullet();
|
||||
}
|
||||
case _:
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/common/haxe/ru/m/tankz/game/GameTracer.hx
Normal file
12
src/common/haxe/ru/m/tankz/game/GameTracer.hx
Normal file
@@ -0,0 +1,12 @@
|
||||
package ru.m.tankz.game;
|
||||
|
||||
import ru.m.tankz.game.Game;
|
||||
|
||||
class GameTracer implements GameListener {
|
||||
public function new() {
|
||||
}
|
||||
|
||||
public function onGameEvent(event:GameEvent):Void {
|
||||
L.d("Game", Std.string(event));
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,25 @@
|
||||
package ru.m.tankz.game;
|
||||
|
||||
import haxework.signal.Signal;
|
||||
import ru.m.tankz.control.Control;
|
||||
import ru.m.tankz.config.Config;
|
||||
import ru.m.tankz.engine.Engine;
|
||||
import ru.m.tankz.game.Game;
|
||||
import ru.m.tankz.Type;
|
||||
|
||||
interface IGame {
|
||||
public var type(default, null):GameType;
|
||||
public var teams(default, null):Map<TeamId, Team>;
|
||||
public var config(default, null):Config;
|
||||
public var engine(default, null):Engine;
|
||||
public var winner(default, null):Null<TeamId>;
|
||||
public var state(default, null):GameState;
|
||||
public var gameEventSignal(default, null):Signal<GameEvent>;
|
||||
|
||||
public function connect(listener:GameListener):Void;
|
||||
|
||||
public function disconnect(listener:GameListener):Void;
|
||||
|
||||
public function start(state:GameState):Void;
|
||||
|
||||
public function action(tankId:Int, action:TankAction):Void;
|
||||
public function dispose():Void;
|
||||
|
||||
public function getTeam(teamId:TeamId):Team;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user