diff --git a/src/client/haxe/ru/m/tankz/AppTheme.hx b/src/client/haxe/ru/m/tankz/AppTheme.hx index be33df8..ab3e6ae 100644 --- a/src/client/haxe/ru/m/tankz/AppTheme.hx +++ b/src/client/haxe/ru/m/tankz/AppTheme.hx @@ -96,7 +96,8 @@ class AppTheme extends Theme { ], ["dark", "border"])); register(new Style("line", [ - "_" => null, + "geometry.width" => SizeValue.fromString("100%"), + "geometry.height" => SizeValue.fromInt(2), ], ["border"])); register(new Style("window.close", [ diff --git a/src/client/haxe/ru/m/tankz/view/ResultFrame.hx b/src/client/haxe/ru/m/tankz/view/ResultFrame.hx index 8d43332..14d4bba 100644 --- a/src/client/haxe/ru/m/tankz/view/ResultFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/ResultFrame.hx @@ -37,7 +37,7 @@ using ru.m.tankz.view.ViewUtil; players.sort(function(a, b) return a.id.compare(b.id)); resultView.data = players; for (view in resultView.dataViews) { - view.tank = result.state.config.getPlayerTank(view.data.id).skin; + view.winner = view.data.id.team == data.winner; } levelLabel.text = data.level.toLevelLabel(); nextButton.disabled = !gameStorage.get(result.level.packId).isPresetAvailable(result.level.id + 1, result.state.presetId); diff --git a/src/client/haxe/ru/m/tankz/view/StartFrame.hx b/src/client/haxe/ru/m/tankz/view/StartFrame.hx index e08e4c0..d202a54 100644 --- a/src/client/haxe/ru/m/tankz/view/StartFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/StartFrame.hx @@ -89,7 +89,7 @@ using ru.m.tankz.view.ViewUtil; var result = new SlotView(); result.select.onSelect.connect(function(controller:Controller) setController(value, controller)); result.control = value; - result.tank = start.state.config.getTank(start.state.config.getPlayer(value.playerId).tanks[0].type).skin; + result.tank = start.state.config.getPlayerTank(value.playerId).skin; return result; } diff --git a/src/client/haxe/ru/m/tankz/view/common/TankView.hx b/src/client/haxe/ru/m/tankz/view/common/TankView.hx new file mode 100644 index 0000000..0b6075a --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/common/TankView.hx @@ -0,0 +1,23 @@ +package ru.m.tankz.view.common; + +import haxework.view.ImageView; +import openfl.Assets; +import ru.m.tankz.game.GameEvent; + +class TankView extends ImageView { + + public var tank(null, set):TankInfo; + + public function new() { + super(); + style = "icon.tank"; + } + + private function set_tank(value:TankInfo):TankInfo { + if (value != null) { + color = value.color; + image = Assets.getBitmapData('resources/image/tank/${value.skin}-0.png'); + } + return value; + } +} diff --git a/src/client/haxe/ru/m/tankz/view/game/GamePanelHelper.hx b/src/client/haxe/ru/m/tankz/view/game/GamePanelHelper.hx index 9f12d40..37f1ef9 100644 --- a/src/client/haxe/ru/m/tankz/view/game/GamePanelHelper.hx +++ b/src/client/haxe/ru/m/tankz/view/game/GamePanelHelper.hx @@ -44,10 +44,17 @@ class GamePanelHelper { case START(start): panel.level.text = start.level.toLevelLabel(onelinelevel); panel.teams.data = [for (team in start.state.teams) team]; + // ToDo: PlayerState default tank? for (teamView in panel.teams.dataViews) { for (playerView in teamView.dataViews) { - playerView.tank = config.getPlayerTank(playerView.playerId).skin; - playerView.color = config.getColor(playerView.playerId); + var tank = config.getPlayerTank(playerView.playerId); + playerView.tank = { + type: tank.type, + skin: tank.skin, + hits: 0, + bonus: false, + color: config.getColor(playerView.playerId), + }; } } case CHANGE(TEAM_LIFE(teamId, life)): diff --git a/src/client/haxe/ru/m/tankz/view/game/PlayerView.hx b/src/client/haxe/ru/m/tankz/view/game/PlayerView.hx index 17ceb31..bd0ee61 100644 --- a/src/client/haxe/ru/m/tankz/view/game/PlayerView.hx +++ b/src/client/haxe/ru/m/tankz/view/game/PlayerView.hx @@ -1,61 +1,51 @@ package ru.m.tankz.view.game; -import haxework.color.Color; import haxework.view.form.LabelView; import haxework.view.group.VGroupView; -import haxework.view.ImageView; -import openfl.Assets; +import ru.m.tankz.game.GameEvent.TankInfo; import ru.m.tankz.game.GameState; import ru.m.tankz.Type; +import ru.m.tankz.view.common.TankView; @:template class PlayerView extends VGroupView { @:view("title") public var titleView:LabelView; - @:view("tank") public var tankImage:ImageView; + @:view("tank") public var tankView:TankView; @:view("life") public var lifeLabel:LabelView; @:view("score") public var scoreLabel:LabelView; public var playerId(default, default):PlayerId; public var state(null, set):PlayerState; - public var tank(null, set):String; - public var color(null, set):Color; + public var tank(null, set):TankInfo; public var life(null, set):Int; public var score(null, set):Int; - private inline function set_state(value:PlayerState):PlayerState { + private function set_state(value:PlayerState):PlayerState { playerId = value.id; - titleView.text = value.name != null ? value.name : playerId.index > -1 ? '${playerId.team} #${playerId.index}' : playerId.team; - tank = value.tank; - color = value.color; + titleView.text = value.name != null ? value.name : playerId.format(); life = value.life; score = value.score; + tank = value.tank; return state; } - private inline function set_tank(value:String):String { - if (value != null && value != tank) { - tank = value; - tankImage.image = Assets.getBitmapData('resources/image/tank/${tank}-0.png'); - } + private function set_tank(value:TankInfo):TankInfo { + tank = value; + tankView.tank = tank; return tank; } - private inline function set_color(value:Color):Color { - tankImage.color = value; - return value; - } - - private inline function set_life(value:Int):Int { + private function set_life(value:Int):Int { lifeLabel.text = '${value}'; return value; } - private inline function set_score(value:Int):Int { + private function set_score(value:Int):Int { scoreLabel.text = '${value}$'; return value; } - public static inline function factory(index:Int, data:PlayerState):PlayerView { + public static function factory(index:Int, data:PlayerState):PlayerView { var result = new PlayerView(); result.state = data; return result; diff --git a/src/client/haxe/ru/m/tankz/view/game/PlayerView.yaml b/src/client/haxe/ru/m/tankz/view/game/PlayerView.yaml index 7947773..1b00f62 100644 --- a/src/client/haxe/ru/m/tankz/view/game/PlayerView.yaml +++ b/src/client/haxe/ru/m/tankz/view/game/PlayerView.yaml @@ -8,8 +8,7 @@ views: layout.vAlign: middle views: - id: tank - $type: haxework.view.ImageView - style: icon.tank + $type: ru.m.tankz.view.common.TankView - id: life $type: haxework.view.form.LabelView style: text.box diff --git a/src/client/haxe/ru/m/tankz/view/result/FragView.hx b/src/client/haxe/ru/m/tankz/view/result/FragView.hx new file mode 100644 index 0000000..8e36307 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/result/FragView.hx @@ -0,0 +1,36 @@ +package ru.m.tankz.view.result; + +import openfl.utils.Assets; +import haxework.view.form.LabelView; +import haxework.view.group.HGroupView; +import ru.m.tankz.game.GameState; +import ru.m.tankz.view.common.TankView; + +@:template class FragView extends HGroupView { + + @:view("tank") var tankView:TankView; + @:view("title") var titleView:LabelView; + @:view("score") var scoreView:LabelView; + + public var data(default, set):Frag; + + private function set_data(value:Frag):Frag { + data = value; + switch data.target { + case TANK(tank): + tankView.tank = tank; + case EAGLE(color): + tankView.image = Assets.getBitmapData('resources/image/eagle/eagle.png'); + tankView.color = color; + } + titleView.text = data.playerId.format(); + scoreView.text = data.score.format(); + return data; + } + + public static function factory(index:Int, data:Frag):FragView { + var result = new FragView(); + result.data = data; + return result; + } +} diff --git a/src/client/haxe/ru/m/tankz/view/result/FragView.yaml b/src/client/haxe/ru/m/tankz/view/result/FragView.yaml new file mode 100644 index 0000000..58556d8 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/result/FragView.yaml @@ -0,0 +1,14 @@ +--- +geometry.width: 100% +layout.vAlign: middle +views: + - id: tank + $type: ru.m.tankz.view.common.TankView + - id: title + $type: haxework.view.form.LabelView + - $type: haxework.view.SpriteView + geometry.width: 100% + - id: score + $type: haxework.view.form.LabelView + style: text.box + geometry.width: 100 diff --git a/src/client/haxe/ru/m/tankz/view/result/ResultPlayerView.hx b/src/client/haxe/ru/m/tankz/view/result/ResultPlayerView.hx index c0b15cf..f01b9e4 100644 --- a/src/client/haxe/ru/m/tankz/view/result/ResultPlayerView.hx +++ b/src/client/haxe/ru/m/tankz/view/result/ResultPlayerView.hx @@ -1,32 +1,36 @@ package ru.m.tankz.view.result; +import haxework.view.data.DataView; import haxework.view.form.LabelView; import haxework.view.group.VGroupView; -import haxework.view.ImageView; -import openfl.Assets; +import haxework.view.skin.SpriteSkin; import ru.m.tankz.game.GameState; +import ru.m.tankz.view.common.TankView; @:template class ResultPlayerView extends VGroupView { - @:view("tank") var tankView:ImageView; + @:view("tank") var tankView:TankView; @:view("title") var titleView:LabelView; @:view("score") var scoreView:LabelView; + @:view("frags") var fragsView:DataView; public var data(default, set):PlayerState; - public var tank(default, set):String; + public var winner(default, set):Bool; private function set_data(value:PlayerState):PlayerState { data = value; titleView.text = data.name; scoreView.text = '${data.score}$'; tankView.color = value.color; + fragsView.data = data.frags; + tankView.tank = value.tank; return data; } - public function set_tank(value:String):String { - tank = value; - tankView.image = Assets.getBitmapData('resources/image/tank/${tank}-0.png'); - return tank; + private function set_winner(value:Bool):Bool { + winner = value; + cast(skin, SpriteSkin).border.color = value ? 0x00ff00 : 0xff0000; + return winner; } public static function factory(index:Int, data:PlayerState):ResultPlayerView { diff --git a/src/client/haxe/ru/m/tankz/view/result/ResultPlayerView.yaml b/src/client/haxe/ru/m/tankz/view/result/ResultPlayerView.yaml index 57c6f2c..f65739a 100644 --- a/src/client/haxe/ru/m/tankz/view/result/ResultPlayerView.yaml +++ b/src/client/haxe/ru/m/tankz/view/result/ResultPlayerView.yaml @@ -1,20 +1,29 @@ --- style: light -geometry.margin: 10 +geometry.margin: 15 geometry.padding: 10 geometry.width: 50% geometry.height: 100% layout.hAlign: center views: - $type: haxework.view.group.HGroupView + geometry.width: 100% + layout.vAlign: middle views: - id: tank - $type: haxework.view.ImageView - geometry.width: 42 - geometry.height: 42 + $type: ru.m.tankz.view.common.TankView - id: title $type: haxework.view.form.LabelView - - id: score - $type: haxework.view.form.LabelView - style: text.box - geometry.width: 100 + - $type: haxework.view.SpriteView + geometry.width: 100% + - id: score + $type: haxework.view.form.LabelView + style: text.box + geometry.width: 100 + - $type: haxework.view.SpriteView + style: line + - id: frags + $type: haxework.view.data.DataView + factory: ~ru.m.tankz.view.result.FragView.factory + geometry.stretch: true + overflow.y: scroll diff --git a/src/common/haxe/ru/m/tankz/Type.hx b/src/common/haxe/ru/m/tankz/Type.hx index adcea9f..bba01c0 100644 --- a/src/common/haxe/ru/m/tankz/Type.hx +++ b/src/common/haxe/ru/m/tankz/Type.hx @@ -1,7 +1,5 @@ package ru.m.tankz; -typedef Type = Dynamic; - typedef GameType = String; typedef TeamId = String; @@ -36,6 +34,10 @@ abstract PlayerId(Array) { return index - other.index; } + public function format():String { + return index > -1 ? '${team} #${index}' : team; + } + @:from static public inline function fromArray(value:Array):PlayerId { return new PlayerId(value[0], value[1]); } @@ -79,3 +81,10 @@ abstract PackId(Array) { typedef LevelId = Int; typedef PresetId = Int; + +abstract Score(Int) from Int to Int { + + public function new(value:Int) this = value; + + public inline function format():String return '${this}$'; +} diff --git a/src/common/haxe/ru/m/tankz/config/Config.hx b/src/common/haxe/ru/m/tankz/config/Config.hx index 256f3f8..c1efd87 100644 --- a/src/common/haxe/ru/m/tankz/config/Config.hx +++ b/src/common/haxe/ru/m/tankz/config/Config.hx @@ -58,7 +58,7 @@ typedef TankConfig = { @:optinal var hits:Int; @:optinal var upgrade:TankType; @:optinal var downgrade:TankType; - @:optinal var score:Null; + @:optinal var score:Null; } typedef BonusConfig = { @@ -137,7 +137,7 @@ class Config { private var brickMap:Map; private var brickMapByIndex:Map; - private var tankMap:Map; + private var tankMap:Map; private var presetsMap:Map; private var bonusMap:Map; private var teamsMap:Map; diff --git a/src/common/haxe/ru/m/tankz/core/Tank.hx b/src/common/haxe/ru/m/tankz/core/Tank.hx index ad2efbd..dca0c1a 100755 --- a/src/common/haxe/ru/m/tankz/core/Tank.hx +++ b/src/common/haxe/ru/m/tankz/core/Tank.hx @@ -4,6 +4,7 @@ import haxework.color.Color; import ru.m.geom.Direction; import ru.m.geom.Rectangle; import ru.m.tankz.config.Config; +import ru.m.tankz.game.GameEvent; import ru.m.tankz.Type; class Tank extends MobileEntity { @@ -16,6 +17,8 @@ class Tank extends MobileEntity { public var protect(default, default):Bool; public var freezing(default, default):Bool; + public var info(get, null):TankInfo; + public function new(id:Int, rect:Rectangle, playerId:PlayerId, config:TankConfig) { super(id, rect, config.speed, Direction.RIGHT); this.protect = false; @@ -35,6 +38,17 @@ class Tank extends MobileEntity { return value; } + private function get_info():TankInfo { + return { + type: config.type, + skin: config.skin, + hits: hits, + bonus: bonus, + color: color, + name: name, + } + } + override public function move(direction:Direction):Void { if (!freezing) { super.move(direction); diff --git a/src/common/haxe/ru/m/tankz/game/EventUtil.hx b/src/common/haxe/ru/m/tankz/game/EventUtil.hx index cae4b83..89907f8 100644 --- a/src/common/haxe/ru/m/tankz/game/EventUtil.hx +++ b/src/common/haxe/ru/m/tankz/game/EventUtil.hx @@ -42,13 +42,7 @@ class EventUtil { } public static function buildTankSpawn(tank:Tank):GameEvent { - return SPAWN(TANK(tank.id, tank.rect.clone(), tank.playerId, { - type:tank.config.type, - hits:tank.hits, - bonus:tank.bonus, - color:tank.color, - name:tank.name, - })); + return SPAWN(TANK(tank.id, tank.rect.clone(), tank.playerId, tank.info)); } public static function buildBonusSpawn(bonus:Bonus):GameEvent { diff --git a/src/common/haxe/ru/m/tankz/game/GameEvent.hx b/src/common/haxe/ru/m/tankz/game/GameEvent.hx index 316cfda..1eb2527 100644 --- a/src/common/haxe/ru/m/tankz/game/GameEvent.hx +++ b/src/common/haxe/ru/m/tankz/game/GameEvent.hx @@ -20,6 +20,7 @@ typedef Result = { typedef TankInfo = { var type:TankType; + var skin:String; var hits:Int; var bonus:Bool; var color:Color; diff --git a/src/common/haxe/ru/m/tankz/game/GameRunner.hx b/src/common/haxe/ru/m/tankz/game/GameRunner.hx index 6341b29..a7a0233 100644 --- a/src/common/haxe/ru/m/tankz/game/GameRunner.hx +++ b/src/common/haxe/ru/m/tankz/game/GameRunner.hx @@ -11,6 +11,7 @@ import ru.m.tankz.core.Tank; import ru.m.tankz.engine.Engine; import ru.m.tankz.engine.IEngine; import ru.m.tankz.game.GameEvent; +import ru.m.tankz.game.GameState.FragTarget; import ru.m.tankz.game.Spawner; import ru.m.tankz.Type; import ru.m.Timer; @@ -240,7 +241,11 @@ class GameRunner extends Game implements EngineListener { case [BULLET(bullet), EAGLE(eagle)]: if (!eagle.death) { if (!eagle.protect) { - gameEventSignal.emit(DESTROY(EAGLE(eagle.id, buildShot(bullet, eagle.score)))); + var score = eagle.config.score; + if (score != null && eagle.team == bullet.playerId.team) { + score = Math.round(score * -0.5); + } + gameEventSignal.emit(DESTROY(EAGLE(eagle.id, buildShot(bullet, score)))); } gameEventSignal.emit(DESTROY(BULLET(bullet.id))); } @@ -388,11 +393,19 @@ class GameRunner extends Game implements EngineListener { case SPAWN(EAGLE(id, rect, teamId)): var team = getTeam(teamId); team.eagleId = id; + case SPAWN(TANK(id, rect, playerId, info)): + getPlayer(playerId).state.tank = info; case SPAWN(BULLET(_, _, playerId, _)): getPlayer(playerId).bullets++; case DESTROY(EAGLE(id, shot)): var eagle:Eagle = engine.getEntity(id); eagle.death = true; + var shooter = getPlayer(cast(engine.getEntity(shot.tankId), Tank).playerId); + shooter.state.frags.push({ + playerId: new PlayerId(eagle.team, -1), + target: EAGLE(eagle.color), + score: shot.score, + }); if (shot.score != null) { var tank:Tank = engine.getEntity(shot.tankId); changeScore(tank.playerId, shot.score); @@ -419,9 +432,14 @@ class GameRunner extends Game implements EngineListener { if (tank.bonus && shot.bulletId != null) { spawnBonus(); } + var shooter = getPlayer(cast(engine.getEntity(shot.tankId), Tank).playerId); + shooter.state.frags.push({ + playerId: player.id, + target: TANK(tank.info), + score: shot.score, + }); if (shot.score != null) { - var shooterTank:Tank = engine.getEntity(shot.tankId); - changeScore(shooterTank.playerId, shot.score); + changeScore(shooter.id, shot.score); } engine.destroy(id); case DESTROY(BONUS(id, shot)): diff --git a/src/common/haxe/ru/m/tankz/game/GameState.hx b/src/common/haxe/ru/m/tankz/game/GameState.hx index 3555b29..e2c6731 100644 --- a/src/common/haxe/ru/m/tankz/game/GameState.hx +++ b/src/common/haxe/ru/m/tankz/game/GameState.hx @@ -5,39 +5,43 @@ import ru.m.tankz.bundle.IConfigBundle; import ru.m.tankz.config.Config; import ru.m.tankz.control.Controller; import ru.m.tankz.control.PlayerControl; +import ru.m.tankz.game.GameEvent; import ru.m.tankz.Type; +enum FragTarget { + TANK(tank:TankInfo); + EAGLE(color:Color); +} + +typedef Frag = { + var playerId:PlayerId; + var target:FragTarget; + var score:Score; +} + class State { public var score:Int; - public var frags:Int; - public var shots:Int; - public var hits:Int; + public var frags:Array; public function new() { score = 0; - frags = 0; - shots = 0; - hits = 0; + frags = []; } public function add(state:State) { score += state.score; - frags += state.frags; - shots += state.shots; - hits += state.hits; + frags = frags.concat(state.frags); } public function reset() { score = 0; - frags = 0; - shots = 0; - hits = 0; + frags = []; } } class PlayerState extends State { public var id:PlayerId; - public var tank:TankType; + public var tank:TankInfo; public var color:Color; public var name:String; public var life:Int;