[common] added game package

This commit is contained in:
2018-01-21 21:09:55 +03:00
parent f1bb6514ad
commit a4558aa2f2
13 changed files with 272 additions and 234 deletions

View File

@@ -14,8 +14,8 @@ class PlayerControl extends Control {
private var moveQueue:Array<Int>;
private var shotTimer:Timer;
public function new(tankId:Int, keyBinding:Map<Int, TankAction>) {
super(tankId);
public function new(keyBinding:Map<Int, TankAction>) {
super();
this.keyBinding = keyBinding;
moveQueue = new Array<Int>();
Lib.current.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
@@ -78,10 +78,10 @@ class PlayerControl extends Control {
action(TankAction.SHOT);
}
public static function forPlayer(index:Int, tankId:Int):PlayerControl {
public static function forPlayer(index:Int):PlayerControl {
switch (index) {
case 0:
return new PlayerControl(tankId, [
return new PlayerControl([
Keyboard.A => TankAction.MOVE(Direction.LEFT),
Keyboard.S => TankAction.MOVE(Direction.BOTTOM),
Keyboard.W => TankAction.MOVE(Direction.TOP),
@@ -89,7 +89,7 @@ class PlayerControl extends Control {
Keyboard.SPACE => TankAction.SHOT
]);
case 1:
return new PlayerControl(tankId, [
return new PlayerControl([
Keyboard.LEFT => TankAction.MOVE(Direction.LEFT),
Keyboard.DOWN => TankAction.MOVE(Direction.BOTTOM),
Keyboard.UP => TankAction.MOVE(Direction.TOP),

View File

@@ -69,13 +69,13 @@ class Render extends SpriteView {
entryLayer.addChild(items[entity.key].view);
}
}
for (key in game.removedEntities) {
/*for (key in game.removedEntities) {
if (items.exists(key)) {
var view:DisplayObject = items[key].view;
view.parent.removeChild(view);
items.remove(key);
}
}
}*/
for (item in items) {
item.update();
}

View File

@@ -1,18 +1,19 @@
package ru.m.tankz.view.frames;
import flash.events.Event;
import haxe.Timer;
import haxework.gui.VGroupView;
import haxework.gui.ViewBuilder;
import haxework.provider.Provider;
import protohx.Message;
import ru.m.connect.IConnection;
import ru.m.tankz.config.ConfigBundle;
import ru.m.tankz.proto.core.GameType;
import ru.m.tankz.proto.core.Game;
import ru.m.tankz.control.PlayerControl;
import ru.m.tankz.engine.Engine;
import protohx.Message;
import ru.m.tankz.game.ClassicGame;
import ru.m.tankz.game.Game;
import ru.m.tankz.player.Player;
import ru.m.tankz.proto.pack.GameUpdateResponse;
import ru.m.connect.IConnection;
import haxework.gui.ViewBuilder;
import flash.events.Event;
import haxework.provider.Provider;
import haxework.gui.VGroupView;
@:template("layout/frames/game.json", "layout/styles.json")
@@ -22,25 +23,20 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand
public static inline var ID = "game";
private var engine:Engine;
private var game:Game;
private var timer:Timer;
public function init():Void {
engine = new Engine();
}
public function onShow():Void {
var game:Game = Provider.get(Game);
engine.init(ConfigBundle.get(GameType.CLASSIC, 0));
engine.initTanks(game.players);
game = new ClassicGame(ConfigBundle.get(ClassicGame.TYPE, 0));
game.start([
new Player(1)
]);
content.addEventListener(Event.ENTER_FRAME, redraw);
for (index in 0...game.players.length) {
var playerId:Int = game.players[index].id;
var tankId:Int = engine.playerTanks.get(playerId).id;
engine.bindControl(PlayerControl.forPlayer(index, tankId));
}
Provider.get(IConnection).packetHandler.addListener(this);
render.draw(engine);
render.draw(game.engine);
timer = new Timer(10);
timer.run = updateEngine;
}
@@ -52,21 +48,21 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand
timer = null;
}
content.removeEventListener(Event.ENTER_FRAME, redraw);
engine.clear();
game.dispose();
render.reset();
}
private function updateEngine():Void {
engine.update();
game.engine.update();
}
private function redraw(_):Void {
render.draw(engine);
render.draw(game.engine);
}
public function onGameUpdateResponse(packet:GameUpdateResponse):Void {
//engine.updateFromChanges(packet.changes);
render.draw(engine);
render.draw(game.engine);
}
public function onPacket(packet:Message):Void {}

View File

@@ -1,14 +1,8 @@
package ru.m.tankz.config;
@:enum abstract EntityType(String) from String to String {
var PLAYER = 'player';
var BOT = 'bot';
var EAGLE = 'eagle';
}
typedef SpawnPoint = {
var type:EntityType;
var type:String;
var index:Int;
var x:Int;
var y:Int;
@@ -55,7 +49,7 @@ class Config {
public var bricks(default, null):Array<BrickConfig>;
public var tanks(default, null):Array<TankConfig>;
private var pointMap:Map<EntityType, Map<Int, SpawnPoint>>;
private var pointMap:Map<String, Map<Int, SpawnPoint>>;
private var brickMap:Map<Int, BrickConfig>;
private var tankMap:Map<String, Map<Int, TankConfig>>;
@@ -83,7 +77,7 @@ class Config {
}
}
public function getSpawnPoint(type:EntityType, index:Int):SpawnPoint {
public function getSpawnPoint(type:String, index:Int):SpawnPoint {
return pointMap.get(type).get(index);
}

View File

@@ -1,9 +1,9 @@
package ru.m.tankz.config;
import ru.m.tankz.game.ClassicGame;
import yaml.Parser;
import openfl.Assets;
import yaml.Yaml;
import ru.m.tankz.proto.core.GameType;
import ru.m.tankz.config.Config;
@@ -19,9 +19,9 @@ class ConfigBundle {
return raw;
}
public static function get(type:Int, level:Int):Config {
public static function get(type:String, level:Int):Config {
switch (type) {
case GameType.CLASSIC:
case ClassicGame.TYPE:
var source:ConfigSource = convert(Yaml.parse(Assets.getText('resources/config/config.yaml'), Parser.options().useObjects()));
var bricksData:String = Assets.getText('resources/levels/level00${level}.txt');
var bricks:Array<BrickConfig> = [];

View File

@@ -12,19 +12,17 @@ enum TankAction {
class Control {
public var tankId(default, null):Int;
public var tankId(default, default):Int;
private var listener:ControlListener;
public function new(tankId:Int) {
this.tankId = tankId;
}
public function new() {}
public function bind(listener:ControlListener):Void {
this.listener = listener;
}
public function action(action:TankAction):Void {
if (listener != null) {
if (tankId != 0 && listener != null) {
listener.onAction(tankId, action);
}
}

View File

@@ -0,0 +1,11 @@
package ru.m.tankz.core;
import ru.m.geom.Rectangle;
class Eagle extends Entity {
public function new() {
super(new Rectangle(0, 0, 10, 10));
}
}

View File

@@ -0,0 +1,26 @@
package ru.m.tankz.core;
import Type.ValueType;
import ru.m.tankz.map.Grid.GridCell;
enum EntityType {
EAGLE(eagle:Eagle);
TANK(tank:Tank);
BULLET(bullet:Bullet);
CELL(cell:GridCell);
}
class EntityTypeResolver {
public static function of(entity:Dynamic):EntityType {
return switch (Type.typeof(entity)) {
case ValueType.TClass(Eagle): EntityType.EAGLE(cast entity);
case ValueType.TClass(Tank): EntityType.TANK(cast entity);
case ValueType.TClass(Bullet): EntityType.BULLET(cast entity);
case ValueType.TClass(GridCell): EntityType.CELL(cast entity);
case x: null;
}
}
}

View File

@@ -1,209 +1,139 @@
package ru.m.tankz.engine;
import ru.m.tankz.control.Control;
import ru.m.tankz.bot.Bot;
import haxe.Constraints.Function;
import ru.m.geom.Line;
import ru.m.tankz.core.MobileEntity;
import ru.m.tankz.core.Entity;
import ru.m.geom.Direction;
import ru.m.tankz.config.Config.TankConfig;
import ru.m.geom.Point;
import ru.m.tankz.core.Bullet;
import ru.m.tankz.proto.game.GameObjectType;
import ru.m.tankz.proto.game.GameChangeType;
import ru.m.tankz.proto.game.GameChange;
import ru.m.tankz.proto.core.Player;
import ru.m.tankz.config.Config;
import ru.m.tankz.control.Control;
import ru.m.tankz.core.Bullet;
import ru.m.tankz.core.Entity;
import ru.m.tankz.core.EntityType;
import ru.m.tankz.core.MobileEntity;
import ru.m.tankz.core.Tank;
import ru.m.tankz.map.LevelMap;
class Engine implements ControlListener {
interface EngineListener {
public function onSpawn(entity:EntityType):Void;
public function onCollision(entity:EntityType, with:EntityType):Void;
public function onDestroy(entity:EntityType):Void;
}
class CollisionProcessor implements EngineListener {
private var engine:Engine;
public function new(engine:Engine) {
this.engine = engine;
}
public function onSpawn(entity:EntityType):Void {}
private function checkTankBullet(tank:Tank, bullet:Bullet):Bool {
return tank.config.group != bullet.tankConfig.group;
}
public function onCollision(entity:EntityType, with:EntityType):Void {
switch (entity) {
case EntityType.TANK(tank1):
switch (with) {
case EntityType.TANK(tank2):
tank1.rect.lean(tank2.rect);
case EntityType.BULLET(bullet2):
if (checkTankBullet(tank1, bullet2)) {
engine.destroy(tank1);
engine.destroy(bullet2);
}
case EntityType.EAGLE(eagle):
tank1.rect.lean(eagle.rect);
case EntityType.CELL(cell):
tank1.rect.lean(cell.rect);
}
case EntityType.BULLET(bullet1):
switch (with) {
case EntityType.TANK(tank2):
if (checkTankBullet(tank2, bullet1)) {
engine.destroy(bullet1);
engine.destroy(tank2);
}
case EntityType.BULLET(bullet2):
engine.destroy(bullet1);
engine.destroy(bullet2);
case EntityType.EAGLE(eagle):
engine.destroy(bullet1);
engine.destroy(eagle);
case EntityType.CELL(cell):
engine.destroy(bullet1);
}
case _:
}
}
public function onDestroy(entity:EntityType):Void { }
}
class Engine {
public var config(default, default):Config;
public var map(default, null):LevelMap;
public var entities(default, null):Map<Int, Entity>;
public var removedEntities(default, null):Array<String>;
public var listeners(default, null):Array<EngineListener>;
private var collision:CollisionProcessor;
public var playerTanks(default, null):Map<Int, Tank>;
private var time:Float;
private var controls:Map<Int, Control>;
public function new() {}
public function clear():Void {
playerTanks = new Map<Int, Tank>();
controls = new Map<Int, Control>();
}
private function buildTank(index:Int, config:TankConfig, point:SpawnPoint):Tank {
var tank = new Tank(index, config);
tank.rect.center = new Point((point.x + 1) * map.cellWidth, (point.y + 1) * map.cellHeight);
tank.rect.direction = Direction.fromString(point.direction);
return tank;
}
public function init(config:Config):Void {
public function new(config:Config) {
this.config = config;
listeners = [];
map = new LevelMap(config.map);
playerTanks = new Map<Int, Tank>();
controls = new Map<Int, Control>();
entities = new Map<Int, Entity>();
removedEntities = new Array<String>();
time = Date.now().getTime();
collision = new CollisionProcessor(this);
listeners.push(collision);
}
public function initTanks(players:Array<Player>):Array<GameChange> {
var changes = new Array<GameChange>();
for (index in 0...players.length) {
var player:Player = players[index];
var point:SpawnPoint = config.getSpawnPoint(EntityType.PLAYER, index);
var tank = buildTank(index, config.getTank('player', 0), point);
playerTanks.set(player.id, tank);
entities.set(tank.id, tank);
changes.push(new GameChange()
.setType(GameChangeType.APPEND)
.setObjectType(GameObjectType.TANK)
.setObjectId(tank.id)
.setX(tank.rect.x)
.setY(tank.rect.y)
.setDirectionX(tank.rect.direction.x)
.setDirectionY(tank.rect.direction.y)
);
public function spawn(entity:Entity):Void {
entities.set(entity.id, entity);
var type = EntityTypeResolver.of(entity);
for (l in listeners) l.onSpawn(type);
}
public function destroy(entity:Entity):Void {
if (entities.exists(entity.id)) {
var type = EntityTypeResolver.of(entity);
for (l in listeners) l.onDestroy(type);
entities.remove(entity.id);
}
for (index in 1...4) {
var point:SpawnPoint = config.getSpawnPoint(EntityType.BOT, index);
var tank = buildTank(0, config.getTank('bot', 0), point);
entities.set(tank.id, tank);
var bot = new Bot(tank.id);
bindControl(bot);
bot.start();
}
return changes;
}
public function bindControl(control:Control):Void {
control.bind(this);
controls.set(control.tankId, control);
}
public function onAction(tankId:Int, action:TankAction):Void {
public function action(tankId:Int, action:TankAction):Void {
if (!entities.exists(tankId)) return;
var tank:Tank = cast entities.get(tankId);
switch (action) {
case TankAction.MOVE(direction):
tank.move(direction);
/*Provider.get(IConnection).send(
Map GameActionRequest()
.setType(GameActionType.MOVE)
.setDirectionX(direction.x)
.setDirectionY(direction.y)
);*/
case TankAction.LEVEL_UP(level):
tank.config = config.getTank('player', Std.int(Math.min(tank.config.type + level, 3)));
case TankAction.STOP:
tank.stop();
/*Provider.get(IConnection).send(
Map GameActionRequest()
.setType(GameActionType.STOP)
);*/
case TankAction.SHOT:
var bullet = tank.shot();
if (bullet != null) {
entities.set(bullet.id, bullet);
}
/*Provider.get(IConnection).send(
Map GameActionRequest()
.setType(GameActionType.SHOT)
);*/
}
}
/*public function updateFromChanges(changes:Array<GameChange>):Void {
for (change in changes) {
switch (change.type) {
case GameChangeType.APPEND:
switch (change.objectType) {
case GameObjectType.TANK:
var tank:Tank = buildTank(change.personId, change.objectId, change.x, change.y, Direction.from(change.directionX, change.directionY));
mobileEntities.set(tank.id, tank);
tanks.set(tank.personId, tank);
case GameObjectType.BULLET:
var bullet:Bullet = Map Bullet(change.personId, change.objectId, change.x, change.y, 0, Direction.from(change.directionX, change.directionY));
mobileEntities.set(bullet.id, bullet);
}
case GameChangeType.DESTROED:
mobileEntities.remove(change.objectId);
case GameChangeType.DIRECTION:
var target = mobileEntities.get(change.objectId);
target.direction = Direction.from(change.directionX, change.directionY);
case GameChangeType.MOVED:
var target = mobileEntities.get(change.objectId);
target.x = change.x;
target.y = change.y;
case GameChangeType.MODIFIED:
}
}
}*/
private function collisionTankTank(a:Tank, b:Tank):Bool {
a.rect.lean(b.rect);
return true;
}
private function collisionBulletTank(a:Bullet, b:Tank):Bool {
if (a.tankConfig.group != b.config.group) {
removeEntity(a);
removeEntity(b);
return true;
}
return false;
}
private function collisionTankBullet(a:Tank, b:Bullet):Bool {
if (a.config.group != b.tankConfig.group) {
removeEntity(a);
removeEntity(b);
return true;
}
return false;
}
private function collisionBulletBullet(a:Bullet, b:Bullet):Bool {
removeEntity(a);
removeEntity(b);
return true;
}
private function removeEntity(entity:Entity):Void {
if (entities.exists(entity.id)) {
entities.remove(entity.id);
removedEntities.push(entity.key);
if (Std.is(entity, Bullet)) {
var tank:Tank = cast entities.get(cast(entity, Bullet).tankId);
if (tank != null) tank.onDestroyBullet();
}
}
if (controls.exists(entity.id)) {
var control:Control = controls.get(entity.id);
control.dispose();
controls.remove(entity.id);
}
}
public function update():Array<GameChange> {
public function update():Void {
var newTime:Float = Date.now().getTime();
var d:Float = newTime - time;
time = newTime;
var changes = new Array<GameChange>();
for (ent in entities) if (Std.is(ent, MobileEntity)) {
var entityType:EntityType = EntityTypeResolver.of(ent);
var entity:MobileEntity = cast ent;
/*if (Std.is(entity, Tank)) {
@@ -227,9 +157,8 @@ class Engine implements ControlListener {
if (cell.layer >= entity.layer && cell.layer < 3) {
entity.rect.lean(cell.rect);
collision = true;
if (controls.exists(entity.id)) {
controls.get(entity.id).onCollision(cell);
}
var with = EntityTypeResolver.of(cell);
for (l in listeners) l.onCollision(entityType, with);
break;
}
}
@@ -237,18 +166,8 @@ class Engine implements ControlListener {
for (other in entities.iterator()) {
if (other != ent && other != null) {
if (other.rect.intersection2(side)) {
//if (ent.rect.intersection(other.rect)) {
var funName = 'collision${ent.type}${other.type}';
var fun:Function = Reflect.field(this, funName);
if (fun != null) {
var c = Reflect.callMethod(this, fun, [ent, other]);
if (c) {
if (controls.exists(entity.id)) {
controls.get(entity.id).onCollision(other);
}
}
collision = c || collision;
}
var with = EntityTypeResolver.of(other);
for (l in listeners) l.onCollision(entityType, with);
}
}
}
@@ -267,19 +186,10 @@ class Engine implements ControlListener {
}
}
}
removeEntity(entity);
}
}
/*changes.push(new GameChange()
.setType(GameChangeType.MOVED)
.setObjectType(objectType)
.setObjectId(entity.id)
.setX(entity.rect.x)
.setY(entity.rect.y)
);*/
}
}
return changes;
}
}

View File

@@ -0,0 +1,13 @@
package ru.m.tankz.game;
import ru.m.tankz.config.Config;
class ClassicGame extends Game {
public static var TYPE(default, never):String = Type.getClassName(ClassicGame);
public function new(config:Config) {
super(TYPE, config);
}
}

View File

@@ -0,0 +1,69 @@
package ru.m.tankz.game;
import ru.m.tankz.core.EntityType;
import ru.m.geom.Direction;
import ru.m.geom.Point;
import ru.m.tankz.config.Config;
import ru.m.tankz.control.Control;
import ru.m.tankz.core.Tank;
import ru.m.tankz.engine.Engine;
import ru.m.tankz.player.Player;
class Game implements EngineListener implements ControlListener {
public var type(default, null):String;
public var config(default, null):Config;
public var engine(default, null):Engine;
public function new(type:String, config:Config) {
this.type = type;
this.config = config;
this.engine = new Engine(config);
}
private function buildTank(index:Int, config:TankConfig, point:SpawnPoint):Tank {
var tank = new Tank(index, config);
tank.rect.center = new Point((point.x + 1) * engine.map.cellWidth, (point.y + 1) * engine.map.cellHeight);
tank.rect.direction = Direction.fromString(point.direction);
return tank;
}
public function start(players:Array<Player>):Void {
for (index in 0...players.length) {
var player:Player = players[index];
var point:SpawnPoint = config.getSpawnPoint('player', index);
var tank = buildTank(index, config.getTank('player', 0), point);
engine.spawn(tank);
}
for (index in 1...4) {
var point:SpawnPoint = config.getSpawnPoint('bot', index);
var tank = buildTank(0, config.getTank('bot', 0), point);
engine.spawn(tank);
/*var bot = new Bot(tank.id);
bindControl(bot);
bot.start();*/
}
}
public function onSpawn(entity:EntityType):Void {
}
public function onCollision(entity:EntityType, with:EntityType):Void {
}
public function onDestroy(entity:EntityType):Void {
}
public function onAction(tankId:Int, action:TankAction):Void {
engine.action(tankId, action);
}
public function dispose():Void {
}
}

View File

@@ -0,0 +1,14 @@
package ru.m.tankz.player;
import ru.m.tankz.control.Control;
class Player extends Control {
public var index(default, null):Int;
public function new(index:Int) {
super();
this.index = index;
}
}