diff --git a/src/client/haxe/ru/m/tankz/bundle/LevelBundle.hx b/src/client/haxe/ru/m/tankz/bundle/LevelBundle.hx index 3549efa..3e6dd18 100644 --- a/src/client/haxe/ru/m/tankz/bundle/LevelBundle.hx +++ b/src/client/haxe/ru/m/tankz/bundle/LevelBundle.hx @@ -7,9 +7,11 @@ import ru.m.tankz.util.LevelUtil; class LevelBundle implements ILevelBundle { + private var _cache:Map = new Map(); + public function new() {} - public function get(id:PackId):LevelPack { + private function resolve(id:PackId):LevelPack { var bytes = Assets.getBytes('levels/${id}.zip'); return { id: id, @@ -19,4 +21,11 @@ class LevelBundle implements ILevelBundle { }), }; } + + public function get(id:PackId):LevelPack { + if (!_cache.exists(id)) { + _cache.set(id, resolve(id)); + } + return _cache.get(id); + } } diff --git a/src/client/haxe/ru/m/tankz/network/NetworkGame.hx b/src/client/haxe/ru/m/tankz/network/NetworkGame.hx index 82d93d5..3a3bbd9 100644 --- a/src/client/haxe/ru/m/tankz/network/NetworkGame.hx +++ b/src/client/haxe/ru/m/tankz/network/NetworkGame.hx @@ -1,6 +1,7 @@ package ru.m.tankz.network; import haxe.Unserializer; +import ru.m.tankz.control.Controller; import ru.m.tankz.game.Game; import ru.m.tankz.game.GameEvent; import ru.m.tankz.network.NetworkControlFactory; @@ -40,7 +41,7 @@ class NetworkGame extends Game { override private function onStart(start:Start):Void { var slot:RoomSlotProto = Lambda.find(network.room.slots, function(slot:RoomSlotProto) return slot.hasUser() && slot.user.uuid == network.user.uuid); if (slot != null) { - start.state.controls.push({playerId: [slot.slot.team, slot.slot.index], control: "human-0"}); + start.state.controls.push({playerId: [slot.slot.team, slot.slot.index], controller: HUMAN(0)}); } super.onStart(start); } diff --git a/src/client/haxe/ru/m/tankz/view/LevelFrame.hx b/src/client/haxe/ru/m/tankz/view/LevelFrame.hx index 781721f..0518ad3 100644 --- a/src/client/haxe/ru/m/tankz/view/LevelFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/LevelFrame.hx @@ -7,9 +7,13 @@ import haxework.view.frame.FrameView; import haxework.view.LabelView; 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.GameInit; import ru.m.tankz.game.GameState; import ru.m.tankz.storage.GameStorage; +import ru.m.tankz.Type.PlayerId; +import ru.m.tankz.Type; import ru.m.tankz.view.popup.LevelPopup; @:template class LevelFrame extends FrameView { @@ -40,9 +44,9 @@ import ru.m.tankz.view.popup.LevelPopup; levels.data = pack.data; } - private function start(level:LevelConfig, preset:GamePreset, control:ControlPreset):Void { + private function start(level:LevelConfig, presetId:PresetId, controls:Array):Void { switcher.change(GameFrame.ID, LOCAL({ - state: new GameState(pack.id.type, preset.id, control.values), + state: new GameState(pack.id.type, presetId, controls), level: level, })); } @@ -64,12 +68,31 @@ import ru.m.tankz.view.popup.LevelPopup; if (levelPopup == null) { levelPopup = new LevelPopup(); } + var controls:Array = []; + var preset = config.getPreset(0); + for (team in preset.teams) { + if (team.id != "bot") { // ToDo: + for (player in team.players) { + var playerId = new PlayerId(team.id, player.index); + controls.push({ + playerId: playerId, + color: config.getColor(playerId), + controller: NONE, + }); + } + } + } + controls.sort(function(a, b) return a.playerId.compare(b.playerId)); + if (controls.length > 0) { + controls[0].controller = HUMAN(0); + controls[0].name = ControllerParser.defaultName(controls[0].controller); + } levelPopup.setData( level, config.presets, - config.controls, + controls, storage.get(pack.id) ); - levelPopup.show().then(function(result) result != null ? start(level, result.preset, result.control) : {}); + levelPopup.show().then(function(result) result != null ? start(level, result.presetId, result.controls) : {}); } } diff --git a/src/client/haxe/ru/m/tankz/view/ResultFrame.hx b/src/client/haxe/ru/m/tankz/view/ResultFrame.hx index e694c3b..572e5e2 100644 --- a/src/client/haxe/ru/m/tankz/view/ResultFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/ResultFrame.hx @@ -43,7 +43,9 @@ import ru.m.tankz.view.common.LifeView; override public function onShow(data:Result):Void { result = data; - resultView.data = Lambda.array(result.state.players); + var players = Lambda.array(result.state.players); + players.sort(function(a, b) return a.id.compare(b.id)); + resultView.data = players; var label = '${result.state.type} Level ${result.level.id}'; if (result.level.name != null) { label += '\n${result.level.name}'; diff --git a/src/client/haxe/ru/m/tankz/view/common/PlayerView.yaml b/src/client/haxe/ru/m/tankz/view/common/PlayerView.yaml deleted file mode 100644 index e4bb79c..0000000 --- a/src/client/haxe/ru/m/tankz/view/common/PlayerView.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -geometry.margin: 5 -layout.margin: 10 -layout.vAlign: middle -views: - - id: label - $type: haxework.view.LabelView - skinId: text - - id: teams - $type: haxework.view.DataView - factory: $this:teamViewFactory - layout: - $type: haxework.view.layout.TailLayout - rowSize: 5 - margin: 3 - +onDataSelect: $this:onTeamSelect diff --git a/src/client/haxe/ru/m/tankz/view/common/SlotView.hx b/src/client/haxe/ru/m/tankz/view/common/SlotView.hx new file mode 100644 index 0000000..850cbd8 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/common/SlotView.hx @@ -0,0 +1,29 @@ +package ru.m.tankz.view.common; + +import haxework.view.ButtonView; +import haxework.view.HGroupView; +import haxework.view.LabelView; +import ru.m.tankz.control.Controller; +import ru.m.tankz.control.PlayerControl; + +@:template class SlotView extends HGroupView { + + @:view("slot") var slotLabel:LabelView; + @:view("player") var playerButton:ButtonView; + + public var control(default, set):PlayerControl; + + private function set_control(value:PlayerControl):PlayerControl { + control = value; + slotLabel.text = '${control.playerId.team}(${control.playerId.index})'; + playerButton.skinId = switch control.controller { + case HUMAN(_): "button.simple.active"; + case _: "button.simple"; + } + var name = ControllerParser.defaultName(control.controller); + playerButton.text = name != null ? name : "None"; + slotLabel.skin = Style.textBox(value.color != null ? value.color : Style.textColor); + return control; + } + +} diff --git a/src/client/haxe/ru/m/tankz/view/common/SlotView.yaml b/src/client/haxe/ru/m/tankz/view/common/SlotView.yaml new file mode 100644 index 0000000..0698aee --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/common/SlotView.yaml @@ -0,0 +1,16 @@ +--- +geometry.size.height: 48 +geometry.padding: 2 +layout.margin: 10 +views: + - id: slot + $type: haxework.view.LabelView + skinId: text.box + geometry.size.height: 100% + geometry.size.width: 150 + - id: player + $type: haxework.view.ButtonView + skinId: button.simple + geometry.size.height: 100% + geometry.size.width: 120 + text: "-" diff --git a/src/client/haxe/ru/m/tankz/view/popup/LevelPopup.hx b/src/client/haxe/ru/m/tankz/view/popup/LevelPopup.hx index 7f1f53c..d02c873 100644 --- a/src/client/haxe/ru/m/tankz/view/popup/LevelPopup.hx +++ b/src/client/haxe/ru/m/tankz/view/popup/LevelPopup.hx @@ -1,16 +1,20 @@ package ru.m.tankz.view.popup; +using haxe.EnumTools.EnumValueTools; import haxework.view.ButtonView; import haxework.view.DataView; import haxework.view.LabelView; import haxework.view.popup.PopupView; -import haxework.view.ToggleButtonView; import ru.m.tankz.config.Config; +import ru.m.tankz.control.Controller; +import ru.m.tankz.control.PlayerControl; import ru.m.tankz.game.PackProgress; +import ru.m.tankz.Type; +import ru.m.tankz.view.common.SlotView; private typedef Result = { - var control:ControlPreset; - var preset:GamePreset; + var presetId:PresetId; + var controls:Array; } @:template class LevelPopup extends PopupView { @@ -20,16 +24,46 @@ private typedef Result = { @:view var name:LabelView; @:view("presets") var presetsView:DataView; - @:view("controls") var controlsView:DataView; - private var control:ControlPreset; + @:view("slots") var slotsView:DataView; - public function setData(level:LevelConfig, presets:Array, controls:Array, progress:PackProgress):Void { + private var humanIndex:Int = 0; + private var humanTotal:Int = 2; + + public function setData(level:LevelConfig, presets:Array, controls:Array, progress:PackProgress):Void { this.level = level; this.progress = progress; name.text = '${level.id}. ${level.name != null ? level.name : "#"}'; presetsView.data = presets; - control = controls[0]; - controlsView.data = controls; + slotsView.data = controls; + } + + private function slotViewFactory(index:Int, value:PlayerControl):SlotView { + var result = new SlotView(); + result.control = value; + return result; + } + + private function onControlSelect(index:Int, value:PlayerControl, view:SlotView):Void { + if (!value.controller.match(NONE)) { + value.controller = NONE; + value.name = null; + } else { + humanIndex++; + if (humanIndex == humanTotal) { + humanIndex = 0; + } + var controller = HUMAN(humanIndex); + for (v in slotsView.dataViews) { + if (v.control.controller.equals(controller)) { + v.control.controller = NONE; + v.control.name = null; + v.control = v.control; + } + } + value.controller = controller; + value.name = ControllerParser.defaultName(controller); + } + view.control = value; } private function presetViewFactory(index:Int, value:GamePreset):ButtonView { @@ -42,22 +76,7 @@ private typedef Result = { private function onPresetSelect(value:GamePreset):Void { if (progress.isPresetAvailable(level.id, value.id)) { - close({control: control, preset: value}); - } - } - - private function controlViewFactory(index:Int, value:ControlPreset):ToggleButtonView { - var result = new ToggleButtonView(); - result.skinId = 'button.simple.active'; - result.on = control == value; - result.text = value.name; - return result; - } - - private function onControlSelect(index:Int, value:ControlPreset, view:ToggleButtonView):Void { - control = value; - for (v in controlsView.dataViews) { - v.on = v == view; + close({presetId: value.id, controls: slotsView.data}); } } } diff --git a/src/client/haxe/ru/m/tankz/view/popup/LevelPopup.yaml b/src/client/haxe/ru/m/tankz/view/popup/LevelPopup.yaml index 65fc273..6a09fe6 100644 --- a/src/client/haxe/ru/m/tankz/view/popup/LevelPopup.yaml +++ b/src/client/haxe/ru/m/tankz/view/popup/LevelPopup.yaml @@ -2,43 +2,47 @@ layout.hAlign: center layout.vAlign: middle view: - $type: haxework.view.VGroupView - layout.hAlign: center - geometry.size.width: 400 - geometry.size.height: 400 - skinId: window - views: - - $type: haxework.view.HGroupView + $type: haxework.view.VGroupView + layout.hAlign: center + geometry.size.width: 400 + geometry.size.height: 400 + geometry.margin: 10 + skinId: window + views: + - $type: haxework.view.HGroupView + geometry.size.width: 100% + geometry.padding: 10 + layout.vAlign: middle + views: + - id: name + $type: haxework.view.LabelView geometry.size.width: 100% - geometry.padding: 10 - layout.vAlign: middle - views: - - id: name - $type: haxework.view.LabelView - geometry.size.width: 100% - geometry.margin.left: 10 - layout.hAlign: left - skinId: text - - $type: haxework.view.ButtonView - skinId: window.close - +onPress: $code:reject('close') - - $type: haxework.view.SpriteView - geometry.size.height: 100% - - id: controls - $type: haxework.view.DataView - factory: $this:controlViewFactory - +onItemSelect: $this:onControlSelect - layout: - $type: haxework.view.layout.HorizontalLayout - hAlign: center - margin: 5 - skinId: panel - - id: presets - $type: haxework.view.DataView - factory: $this:presetViewFactory - +onDataSelect: $this:onPresetSelect - layout: - $type: haxework.view.layout.HorizontalLayout - hAlign: center - margin: 5 - skinId: panel + geometry.margin.left: 10 + layout.hAlign: left + skinId: text + - $type: haxework.view.ButtonView + skinId: window.close + +onPress: $code:reject('close') + - $type: haxework.view.ScrollView + geometry.size.stretch: true + layout.hAlign: center + scroll.skinId: scroll.vertical + view: + id: slots + $type: haxework.view.DataView + geometry.padding: 10 + factory: $this:slotViewFactory + layout: + $type: haxework.view.layout.VerticalLayout + # $type: haxework.view.layout.TailLayout + margin: 5 + +onItemSelect: $this:onControlSelect + - id: presets + $type: haxework.view.DataView + factory: $this:presetViewFactory + +onDataSelect: $this:onPresetSelect + layout: + $type: haxework.view.layout.HorizontalLayout + hAlign: center + margin: 5 + skinId: panel diff --git a/src/common/haxe/ru/m/tankz/Type.hx b/src/common/haxe/ru/m/tankz/Type.hx index c9fe7f2..adcea9f 100644 --- a/src/common/haxe/ru/m/tankz/Type.hx +++ b/src/common/haxe/ru/m/tankz/Type.hx @@ -26,6 +26,16 @@ abstract PlayerId(Array) { private inline function get_index():Int return this[1]; + public function compare(other:PlayerId):Int { + if (other == null) { + return 1; + } + if (other.team != team) { + return team > other.team ? 1 : -1; + } + return index - other.index; + } + @:from static public inline function fromArray(value:Array):PlayerId { return new PlayerId(value[0], value[1]); } diff --git a/src/common/haxe/ru/m/tankz/config/Config.hx b/src/common/haxe/ru/m/tankz/config/Config.hx index 0eecbba..9bceb4e 100644 --- a/src/common/haxe/ru/m/tankz/config/Config.hx +++ b/src/common/haxe/ru/m/tankz/config/Config.hx @@ -120,25 +120,11 @@ typedef LevelPack = { var data:Array; } -typedef PlayerControl = { - var playerId:PlayerId; - var control:String; - @:optional var color:Null; - @:optional var name:String; -} - -typedef ControlPreset = { - var id:Int; - var name:String; - var values:Array; -}; - typedef ConfigSource = { var game:GameConfig; var map: MapConfig; var bricks: Array; var presets: Array; - var controls: Array; var points: Array; var tanks: Array; var bonuses: Array; @@ -151,7 +137,6 @@ class Config { public var bricks(default, null):Array; public var tanks(default, null):Array; public var presets(default, null):Array; - public var controls(default, null):Array; public var points(default, null):Array; public var bonuses(default, null):Array; @@ -164,7 +149,7 @@ class Config { private var playersMap:Map; public static function fromSource(type:GameType, source:ConfigSource):Config { - return new Config(type, source.game, source.map, source.bricks, source.presets, source.controls, source.points, source.tanks, source.bonuses); + return new Config(type, source.game, source.map, source.bricks, source.presets, source.points, source.tanks, source.bonuses); } public function new( @@ -173,7 +158,6 @@ class Config { map:MapConfig, bricks:Array, presets:Array, - controls:Array, points:Array, tanks:Array, bonuses:Array @@ -183,7 +167,6 @@ class Config { this.map = map; this.bricks = bricks; this.presets = presets; - this.controls = controls; this.points = points; this.tanks = tanks; this.bonuses = bonuses; @@ -271,10 +254,6 @@ class Config { } public function isHuman(playerId:PlayerId) { - var controller:Controller = AController.fromString(getPlayer(playerId).control); - return switch controller { - case HUMAN(_): true; - case _: false; - } + return ControllerParser.fromString(getPlayer(playerId).control).match(HUMAN(_)); } } diff --git a/src/common/haxe/ru/m/tankz/control/Controller.hx b/src/common/haxe/ru/m/tankz/control/Controller.hx index cce3cec..251c55f 100644 --- a/src/common/haxe/ru/m/tankz/control/Controller.hx +++ b/src/common/haxe/ru/m/tankz/control/Controller.hx @@ -6,21 +6,23 @@ enum Controller { BOT(type:String); } -abstract AController(Controller) { +class ControllerParser { - public inline function new(value:Controller) { - this = value; + public static function fromString(value:String):Controller { + return switch value { + case null: NONE; + case x: switch x.split("-") { + case ["human", index]: HUMAN(Std.parseInt(index)); + case ["bot", type]: BOT(type); + case _: NONE; + } + } } - @:from public static function fromString(value:String):AController { - return new AController(switch value.split("-") { - case ["human", index]: HUMAN(Std.parseInt(index)); - case ["bot", type]: BOT(type); - case _: NONE; - }); - } - - @:to public inline function toController():Controller { - return this; + public static function defaultName(controller:Controller):String { + return switch controller { + case HUMAN(index): 'Player $index'; + case BOT(_) | NONE: null; + } } } diff --git a/src/common/haxe/ru/m/tankz/control/PlayerControl.hx b/src/common/haxe/ru/m/tankz/control/PlayerControl.hx new file mode 100644 index 0000000..5d699dd --- /dev/null +++ b/src/common/haxe/ru/m/tankz/control/PlayerControl.hx @@ -0,0 +1,11 @@ +package ru.m.tankz.control; + +import haxework.color.Color; +import ru.m.tankz.Type; + +typedef PlayerControl = { + var playerId:PlayerId; + var controller:Controller; + @:optional var color:Color; + @:optional var name:String; +} diff --git a/src/common/haxe/ru/m/tankz/game/Game.hx b/src/common/haxe/ru/m/tankz/game/Game.hx index 254855a..ea76049 100644 --- a/src/common/haxe/ru/m/tankz/game/Game.hx +++ b/src/common/haxe/ru/m/tankz/game/Game.hx @@ -1,14 +1,12 @@ package ru.m.tankz.game; -import ru.m.geom.Point; -import ru.m.geom.Position; 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.control.NoneControlFactory; -import ru.m.tankz.core.Entity; +import ru.m.tankz.control.PlayerControl; import ru.m.tankz.core.EntityType; import ru.m.tankz.engine.IEngine; import ru.m.tankz.game.GameEvent; @@ -102,6 +100,7 @@ import ru.m.tankz.Type; } for (team in teams.iterator()) { for (player in team.players.iterator()) { + var controller = ControllerParser.fromString(player.config.control); var playerControl = controlsById.get(player.id); if (playerControl != null) { if (playerControl.color != null) { @@ -110,9 +109,11 @@ import ru.m.tankz.Type; if (playerControl.name != null) { player.state.name = playerControl.name; } + if (playerControl.controller != null && !playerControl.controller.match(NONE)) { + controller = playerControl.controller; + } } - var controlType:Controller = AController.fromString(playerControl != null ? playerControl.control : player.config.control); - var control = controlFactory.build(player.id, controlType); + var control = controlFactory.build(player.id, controller); if (control != null) { controls[player.id] = control; control.bind(this, engine); diff --git a/src/common/haxe/ru/m/tankz/game/GameState.hx b/src/common/haxe/ru/m/tankz/game/GameState.hx index 2fdda37..48e5ea0 100644 --- a/src/common/haxe/ru/m/tankz/game/GameState.hx +++ b/src/common/haxe/ru/m/tankz/game/GameState.hx @@ -3,6 +3,7 @@ package ru.m.tankz.game; import haxework.color.Color; import ru.m.tankz.bundle.IConfigBundle; import ru.m.tankz.config.Config; +import ru.m.tankz.control.PlayerControl; import ru.m.tankz.Type; class State { diff --git a/src/common/resources/config/classic.yaml b/src/common/resources/config/classic.yaml index d3ba1e6..e86d877 100644 --- a/src/common/resources/config/classic.yaml +++ b/src/common/resources/config/classic.yaml @@ -196,20 +196,3 @@ presets: - {<<: *bot, index: 3, control: bot-hard} - {<<: *bot, index: 4, control: bot-hard} - {<<: *bot, index: 5, control: bot-hard} - -controls: - - id: 0 - name: 1 Player - values: - - playerId: [human, 0] - control: human-0 - name: Player 1 - - id: 1 - name: 2 Player - values: - - playerId: [human, 0] - control: human-0 - name: Player 1 - - playerId: [human, 1] - control: human-1 - name: Player 2 diff --git a/src/common/resources/config/death.yaml b/src/common/resources/config/death.yaml index 9f0696f..d16b239 100644 --- a/src/common/resources/config/death.yaml +++ b/src/common/resources/config/death.yaml @@ -91,20 +91,3 @@ tanks: skin: pc bonuses: [] - -controls: - - id: 0 - name: 1 Player - values: - - playerId: [alpha, 0] - control: human-0 - name: Player 1 - - id: 1 - name: 2 Player - values: - - playerId: [alpha, 0] - control: human-0 - name: Player 1 - - playerId: [beta, 0] - control: human-1 - name: Player 2 diff --git a/src/common/resources/config/dota.yaml b/src/common/resources/config/dota.yaml index 459e150..2a6e2b6 100644 --- a/src/common/resources/config/dota.yaml +++ b/src/common/resources/config/dota.yaml @@ -115,34 +115,3 @@ bonuses: - {type: life} - {type: shovel, duration: 10} - {type: star} - -controls: - - id: 0 - name: 1 Player - values: - - playerId: [radiant, 0] - control: human-0 - color: 0xff8866 - name: Player 1 - - id: 1 - name: 2 Player Coop - values: - - playerId: [radiant, 0] - control: human-0 - color: 0xff8866 - name: Player 1 - - playerId: [radiant, 1] - control: human-1 - color: 0xff8866 - name: Player 2 - - id: 2 - name: 2 Player VS - values: - - playerId: [radiant, 0] - control: human-0 - color: 0xff8866 - name: Player 1 - - playerId: [dire, 0] - control: human-1 - color: 0x4294ff - name: Player 2 diff --git a/src/server/haxe/ru/m/tankz/server/game/ServerGame.hx b/src/server/haxe/ru/m/tankz/server/game/ServerGame.hx index a426b55..25a4b9e 100644 --- a/src/server/haxe/ru/m/tankz/server/game/ServerGame.hx +++ b/src/server/haxe/ru/m/tankz/server/game/ServerGame.hx @@ -1,7 +1,8 @@ package ru.m.tankz.server.game; +import ru.m.tankz.control.Controller; import ru.m.tankz.bundle.ILevelBundle; -import ru.m.tankz.config.Config; +import ru.m.tankz.control.PlayerControl; import ru.m.tankz.core.EntityType; import ru.m.tankz.game.EventUtil; import ru.m.tankz.game.GameEvent; @@ -81,7 +82,7 @@ class ServerGame extends GameRunner { .filter(function(slot:RoomSlotProto) return slot.hasUser()) .map(function(slot:RoomSlotProto):PlayerControl return { playerId: [slot.slot.team, slot.slot.index], - control: "human-0", + controller: HUMAN(0), name: slot.user.name, }); super.start();