[client] add SettingsFrame

This commit is contained in:
2018-07-19 17:47:25 +03:00
parent 17289fde92
commit d0c796aab2
22 changed files with 549 additions and 34 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "tankz", "name": "tankz",
"version": "0.7.3", "version": "0.7.4",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"dateformat": "^3.0.3", "dateformat": "^3.0.3",

View File

@@ -20,10 +20,14 @@ views:
$type: ru.m.tankz.frame.GameFrame $type: ru.m.tankz.frame.GameFrame
- id: network - id: network
$type: ru.m.tankz.frame.NetworkFrame $type: ru.m.tankz.frame.NetworkFrame
- id: settings
$type: ru.m.tankz.frame.SettingsFrame
- $type: haxework.gui.LabelView - $type: haxework.gui.LabelView
$style: label $style: label
inLayout: false inLayout: false
contentSize: true contentSize: true
vAlign: BOTTOM vAlign: BOTTOM
hAlign: RIGHT hAlign: RIGHT
rightMargin: 10
bottomMargin: 10
text: "@res:text:version" text: "@res:text:version"

View File

@@ -1,5 +1,6 @@
package ru.m.tankz; package ru.m.tankz;
import ru.m.tankz.storage.SettingsStorage;
import haxework.provider.Provider; import haxework.provider.Provider;
import haxework.resources.IResources; import haxework.resources.IResources;
import haxework.resources.Resources; import haxework.resources.Resources;
@@ -47,6 +48,7 @@ class Init {
Provider.setFactory(IConfigBundle, ConfigBundle); Provider.setFactory(IConfigBundle, ConfigBundle);
Provider.setFactory(SaveStorage, SaveStorage); Provider.setFactory(SaveStorage, SaveStorage);
Provider.setFactory(UserStorage, UserStorage); Provider.setFactory(UserStorage, UserStorage);
Provider.setFactory(SettingsStorage, SettingsStorage);
Provider.setFactory(SoundManager, SoundManager); Provider.setFactory(SoundManager, SoundManager);
Provider.setFactory(NetworkManager, NetworkManager); Provider.setFactory(NetworkManager, NetworkManager);
Provider.setFactory(IControlFactory, ClientControlFactory); Provider.setFactory(IControlFactory, ClientControlFactory);

View File

@@ -19,3 +19,14 @@ label:
fontFamily: Courirer New fontFamily: Courirer New
fontSize: 16 fontSize: 16
shadowColor: 0x000000 shadowColor: 0x000000
close:
inLayout: false
hAlign: LEFT
vAlign: BOTTOM
leftMargin: 10
bottomMargin: 10
contentSize: true
skin:
$type: haxework.gui.skin.ButtonBitmapSkin
image: "@asset:image:resources/image/ui/close.png"

View File

@@ -0,0 +1,86 @@
package ru.m.tankz.control;
import ru.m.geom.Direction;
import ru.m.tankz.control.Control.TankAction;
import yaml.Parser;
import yaml.Renderer;
import yaml.Yaml;
typedef ActionItem = {
public var action: TankAction;
public var key: Int;
}
typedef KeyBinding = Map<Int, TankAction>;
typedef ActionItemRaw = {
public var action: String;
public var key: Int;
}
class ActionConfig {
public var data(default, null): Array<ActionItem>;
public function new(data: Array<ActionItem>) {
this.data = data;
}
public function asKeyBinding(): KeyBinding {
var result = new Map<Int, TankAction>();
for (item in data) {
result[item.key] = item.action;
}
return result;
}
public static function action2string(action: TankAction): String {
return switch (action) {
case TankAction.SHOT: "SHOT";
case TankAction.MOVE(Direction.TOP): "MOVE_TOP";
case TankAction.MOVE(Direction.LEFT): "MOVE_LEFT";
case TankAction.MOVE(Direction.BOTTOM): "MOVE_BOTTOM";
case TankAction.MOVE(Direction.RIGHT): "MOVE_RIGHT";
case _: throw 'Unsupported action "${action}"';
}
}
public static function string2action(value: String): TankAction {
return switch (value) {
case "SHOT": TankAction.SHOT;
case "MOVE_TOP": TankAction.MOVE(Direction.TOP);
case "MOVE_LEFT": TankAction.MOVE(Direction.LEFT);
case "MOVE_BOTTOM": TankAction.MOVE(Direction.BOTTOM);
case "MOVE_RIGHT": TankAction.MOVE(Direction.RIGHT);
case _: throw 'Unsupported value "${value}"';
}
}
public function clone(): ActionConfig {
return loads(dumps());
}
public function dumps(): String {
var raw: Array<ActionItemRaw> = [];
for (item in this.data) {
raw.push({
action: action2string(item.action),
key: item.key,
});
}
return Yaml.render(raw, Renderer.options().setFlowLevel(0));
}
public static function loads(value: String): ActionConfig {
var raw: Array<ActionItemRaw> = Yaml.parse(value, Parser.options().useObjects());
var data: Array<ActionItem> = [];
for (item in raw) {
data.push({
action: string2action(item.action),
key: item.key,
});
}
return new ActionConfig(data);
}
}

View File

@@ -5,22 +5,23 @@ import flash.events.KeyboardEvent;
import flash.Lib; import flash.Lib;
import flash.ui.Keyboard; import flash.ui.Keyboard;
import haxe.Timer; import haxe.Timer;
import ru.m.geom.Direction; import ru.m.tankz.control.ActionConfig;
import ru.m.tankz.control.Control; import ru.m.tankz.control.Control;
import ru.m.tankz.storage.SettingsStorage;
import ru.m.tankz.Type; import ru.m.tankz.Type;
typedef KeyBinding = Map<Int, TankAction>;
class HumanControl extends Control { class HumanControl extends Control {
@:provide var settings: SettingsStorage;
private var keyBinding:KeyBinding; private var keyBinding:KeyBinding;
private var moveQueue:Array<Int>; private var moveQueue:Array<Int>;
private var shotTimer:Timer; private var shotTimer:Timer;
public function new(playerId:PlayerId, controlIndex:Int) { public function new(playerId:PlayerId, controlIndex:Int) {
super(playerId); super(playerId);
this.keyBinding = resolve(controlIndex); this.keyBinding = settings.read(controlIndex).asKeyBinding();
moveQueue = new Array<Int>(); moveQueue = new Array<Int>();
Lib.current.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); Lib.current.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
Lib.current.stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp); Lib.current.stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
@@ -81,27 +82,4 @@ class HumanControl extends Control {
private function shot():Void { private function shot():Void {
action(TankAction.SHOT); action(TankAction.SHOT);
} }
private static function resolve(controlIndex:Int):KeyBinding {
switch (controlIndex) {
case 0:
return [
Keyboard.A => TankAction.MOVE(Direction.LEFT),
Keyboard.S => TankAction.MOVE(Direction.BOTTOM),
Keyboard.W => TankAction.MOVE(Direction.TOP),
Keyboard.D => TankAction.MOVE(Direction.RIGHT),
Keyboard.SPACE => TankAction.SHOT
];
case 1:
return [
Keyboard.LEFT => TankAction.MOVE(Direction.LEFT),
Keyboard.DOWN => TankAction.MOVE(Direction.BOTTOM),
Keyboard.UP => TankAction.MOVE(Direction.TOP),
Keyboard.RIGHT => TankAction.MOVE(Direction.RIGHT),
Keyboard.NUMPAD_0 => TankAction.SHOT
];
case x:
throw 'Invalid control index ${x}';
}
}
} }

View File

@@ -37,19 +37,24 @@ class GameFrame extends VGroupView {
start(Provider.get(GameSave)); start(Provider.get(GameSave));
} }
private function connectGame(game: Game) {
game.engine.connect(render);
game.engine.connect(Provider.get(SoundManager));
}
private function start(save:GameSave):Void { private function start(save:GameSave):Void {
switch (save.server) { switch (save.server) {
case GameServer.LOCAL: case GameServer.LOCAL:
game = new Game(save.state.type); game = new Game(save.state.type);
connectGame(game);
game.start(save).then(onGameStateChange).endThen(onGameComplete); game.start(save).then(onGameStateChange).endThen(onGameComplete);
timer = new Timer(10); timer = new Timer(10);
timer.run = updateEngine; timer.run = updateEngine;
case GameServer.NETWORK: case GameServer.NETWORK:
game = new NetworkGame(save.state.type); game = new NetworkGame(save.state.type);
connectGame(game);
network.game = cast game; network.game = cast game;
} }
game.engine.connect(render);
game.engine.connect(Provider.get(SoundManager));
content.addEventListener(Event.ENTER_FRAME, redraw); content.addEventListener(Event.ENTER_FRAME, redraw);
render.draw(game.engine); render.draw(game.engine);
state.text = stateString(game); state.text = stateString(game);

View File

@@ -0,0 +1,24 @@
package ru.m.tankz.frame;
import haxework.gui.frame.IFrameSwitcher;
import haxework.gui.ButtonView;
import haxework.gui.VGroupView;
@:template("ru/m/tankz/frame/SettingsFrame.yaml", "ru/m/tankz/Style.yaml")
class SettingsFrame extends VGroupView {
public static var ID(default, never):String = "settings";
@:provide var frameSwitcher:IFrameSwitcher;
@:view var close:ButtonView;
private function init():Void {
close.onPress = this;
}
public function onPress(_):Void {
frameSwitcher.change(StartFrame.ID);
}
}

View File

@@ -0,0 +1,24 @@
---
pWidth: 100
pHeight: 100
views:
- $type: haxework.gui.LabelView
$style: label
pWidth: 100
height: 20
text: Settings
- $type: haxework.gui.HGroupView
pWidth: 100
pHeight: 100
views:
- $type: ru.m.tankz.frame.settings.SettingsEditor
pWidth: 50
pHeight: 100
controlIndex: 0
- $type: ru.m.tankz.frame.settings.SettingsEditor
pWidth: 50
pHeight: 100
controlIndex: 1
- id: close
$type: haxework.gui.ButtonView
$style: close

View File

@@ -23,6 +23,7 @@ class StartFrame extends VGroupView {
@:view var dota_2p_coop(default, null):ButtonView; @:view var dota_2p_coop(default, null):ButtonView;
@:view var dota_2p_vs(default, null):ButtonView; @:view var dota_2p_vs(default, null):ButtonView;
@:view var network(default, null):ButtonView; @:view var network(default, null):ButtonView;
@:view var settings(default, null):ButtonView;
@:provide var frameSwitcher:IFrameSwitcher; @:provide var frameSwitcher:IFrameSwitcher;
@:provide var storage:SaveStorage; @:provide var storage:SaveStorage;
@@ -35,6 +36,7 @@ class StartFrame extends VGroupView {
dota_2p_coop.onPress = this; dota_2p_coop.onPress = this;
dota_2p_vs.onPress = this; dota_2p_vs.onPress = this;
network.onPress = this; network.onPress = this;
settings.onPress = this;
} }
public function onShow():Void { public function onShow():Void {
@@ -63,6 +65,8 @@ class StartFrame extends VGroupView {
startGame(DotaGame.TYPE, DotaGame.PLAYER2_VS); startGame(DotaGame.TYPE, DotaGame.PLAYER2_VS);
case 'network': case 'network':
frameSwitcher.change(NetworkFrame.ID); frameSwitcher.change(NetworkFrame.ID);
case 'settings':
frameSwitcher.change(SettingsFrame.ID);
} }
} }

View File

@@ -70,3 +70,15 @@ views:
$type: haxework.gui.ButtonView $type: haxework.gui.ButtonView
text: Network text: Network
$style: button $style: button
# settings
- id: settings
$type: haxework.gui.ButtonView
inLayout: false
hAlign: LEFT
vAlign: BOTTOM
leftMargin: 10
bottomMargin: 10
contentSize: true
skin:
$type: haxework.gui.skin.ButtonBitmapSkin
image: "@asset:image:resources/image/ui/settings.png"

View File

@@ -0,0 +1,88 @@
package ru.m.tankz.frame.settings;
import haxework.gui.ButtonView;
import haxework.gui.HGroupView;
import haxework.gui.LabelView;
import haxework.gui.list.ListView.IListItemView;
import haxework.gui.skin.ColorSkin;
import openfl.Assets;
import openfl.events.KeyboardEvent;
import promhx.Deferred;
import promhx.Promise;
import ru.m.tankz.control.ActionConfig;
import ru.m.tankz.control.Control;
class KeyboardMap {
private var data:Map<Int, String>;
public function new() {
this.data = new Map();
var data = Assets.getText("resources/keyboard.txt");
for (line in data.split("\n")) {
var arr = line.split("\t");
if (arr.length == 2) {
this.data.set(Std.parseInt(arr[1]), arr[0]);
}
}
}
private static var instance: KeyboardMap;
public static function getName(key: Int): String {
if (instance == null) instance = new KeyboardMap();
return key == -1 ? "<NONE>" : instance.data.exists(key) ? instance.data.get(key) : Std.string(key);
}
}
@:template("ru/m/tankz/frame/settings/ActionView.yaml", "ru/m/tankz/Style.yaml")
class ActionView extends HGroupView implements IListItemView<ActionItem> {
public var item_index(default, default):Int;
public var data(default, set):ActionItem;
@:view var action(default, null):LabelView;
@:view var key(default, null):LabelView;
private var editDeferred: Deferred<Int>;
private function init():Void {
}
private static function actionLabel(action: TankAction): String {
return ActionConfig.action2string(action);
}
private static function keyLabel(key: Int): String {
return KeyboardMap.getName(key);
}
private function set_data(value:ActionItem):ActionItem {
data = value;
action.text = actionLabel(data.action);
key.text = keyLabel(data.key);
return data;
}
public function edit():Promise<Int> {
cast(this.skin, ColorSkin).color = 0x00ff00;
invalidate();
editDeferred = new Deferred();
content.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
return editDeferred.promise();
}
private function onKeyDown(event: KeyboardEvent):Void {
content.stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
cast(this.skin, ColorSkin).color = 0x000000;
invalidate();
data.key = event.keyCode;
key.text = keyLabel(data.key);
editDeferred.resolve(data.key);
editDeferred = null;
}
}

View File

@@ -0,0 +1,21 @@
---
width: 440
height: 44
margins: 5
views:
- id: action
$type: haxework.gui.LabelView
$style: label
pWidth: 50
pHeight: 100
text: ""
- id: key
$type: haxework.gui.LabelView
$style: label
pWidth: 50
pHeight: 100
text: ""
skin:
$type: haxework.gui.skin.ColorSkin
color: "#000000"
alpha: 0.2

View File

@@ -0,0 +1,75 @@
package ru.m.tankz.frame.settings;
import haxework.gui.LabelView;
import ru.m.tankz.control.ActionConfig;
import promhx.Promise;
import ru.m.tankz.storage.SettingsStorage;
import haxework.gui.ButtonView;
import ru.m.tankz.control.ActionConfig.ActionItem;
import haxework.gui.list.ListView;
import haxework.gui.VGroupView;
@:template("ru/m/tankz/frame/settings/SettingsEditor.yaml", "ru/m/tankz/Style.yaml")
class SettingsEditor extends VGroupView {
public var controlIndex(default, set): Int;
@:view var label:LabelView;
@:view var list:ListView<ActionItem>;
@:view var change:ButtonView;
@:view var clear:ButtonView;
@:view var reset:ButtonView;
@:provide var storage: SettingsStorage;
private function init():Void {
change.onPress = this;
clear.onPress = this;
reset.onPress = this;
}
private function set_controlIndex(value: Int): Int {
this.controlIndex = value;
label.text = 'Player ${controlIndex+1}';
list.data = storage.read(controlIndex).data;
return this.controlIndex;
}
public function onPress(view:ButtonView):Void {
switch (view.id) {
case "change": _change();
case "clear": _clear();
case "reset": _reset();
case _:
}
}
private function _change():Void {
var p: Promise<Int> = Promise.promise(0);
for (view in list.items) {
var v: ActionView = cast view;
if (v.data == null) break;
p = p.pipe(function(_):Promise<Int> return v.edit());
}
p.then(function(_) _save());
}
private function _clear():Void {
for (item in list.data) {
item.key = -1;
}
list.invalidate();
_save();
}
private function _reset():Void {
list.data = SettingsStorage.getDefault(controlIndex).data;
list.invalidate();
_save();
}
private function _save():Void {
storage.write(controlIndex, new ActionConfig(list.data));
}
}

View File

@@ -0,0 +1,27 @@
$type: haxework.gui.VGroupView
layoutMargin: 10
views:
- id: label
$type: haxework.gui.LabelView
$style: label
- id: change
$type: haxework.gui.ButtonView
$style: button
text: Change
- id: clear
$type: haxework.gui.ButtonView
$style: button
text: Clear
- id: reset
$type: haxework.gui.ButtonView
$style: button
text: Reset
- id: list
$type: haxework.gui.list.VListView<ru.m.tankz.control.ActionItem>
factory: "@class:ru.m.tankz.frame.settings.ActionView"
pWidth: 100
pHeight: 100
scroll:
$type: haxework.gui.list.VScrollView
width: 1
pHeight: 100

View File

@@ -0,0 +1,55 @@
package ru.m.tankz.storage;
import flash.net.SharedObject;
import flash.ui.Keyboard;
import ru.m.geom.Direction;
import ru.m.tankz.control.ActionConfig;
import ru.m.tankz.control.Control.TankAction;
class SettingsStorage {
private static var TAG(default, never):String = 'SettingsStorage';
private var so:SharedObject;
public function new() {
so = SharedObject.getLocal('settings');
}
public function read(index: Int):Null<ActionConfig> {
var data:String = Reflect.getProperty(so.data, Std.string(index));
L.d(TAG, 'read: ${data}');
if (data != null) {
return ActionConfig.loads(data);
}
return getDefault(index);
}
public function write(index: Int, data: ActionConfig):Void {
L.d(TAG, 'write: ${data}');
so.setProperty(Std.string(index), data.dumps());
so.flush();
}
public static function getDefault(index: Int): ActionConfig {
return defaults.get(index).clone();
}
private static var defaults: Map<Int, ActionConfig> = [
0 => new ActionConfig([
{action:TankAction.MOVE(Direction.TOP), key:Keyboard.W},
{action:TankAction.MOVE(Direction.LEFT), key:Keyboard.A},
{action:TankAction.MOVE(Direction.BOTTOM), key:Keyboard.S},
{action:TankAction.MOVE(Direction.RIGHT), key:Keyboard.D},
{action:TankAction.SHOT, key:Keyboard.SPACE},
]),
1 => new ActionConfig([
{action:TankAction.MOVE(Direction.TOP), key:Keyboard.UP},
{action:TankAction.MOVE(Direction.LEFT), key:Keyboard.LEFT},
{action:TankAction.MOVE(Direction.BOTTOM), key:Keyboard.RIGHT},
{action:TankAction.MOVE(Direction.RIGHT), key:Keyboard.DOWN},
{action:TankAction.SHOT, key:Keyboard.NUMPAD_0},
]),
];
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -0,0 +1,99 @@
BACKSPACE 8
TAB 9
ENTER 13
SHIFT 16
CTRL 17
ALT 18
PAUSE/BREAK 19
CAPS LOCK 20
ESCAPE 27
SPACE 32
PAGE UP 33
PAGE DOWN 34
END 35
HOME 36
LEFT ARROW 37
UP ARROW 38
RIGHT ARROW 39
DOWN ARROW 40
INSERT 45
DELETE 46
0 48
1 49
2 50
3 51
4 52
5 53
6 54
7 55
8 56
9 57
A 65
B 66
C 67
D 68
E 69
F 70
G 71
H 72
I 73
J 74
K 75
L 76
M 77
N 78
O 79
P 80
Q 81
R 82
S 83
T 84
U 85
V 86
W 87
X 88
Y 89
Z 90
LEFT WINDOW KEY 91
RIGHT WINDOW KEY 92
SELECT KEY 93
NUMPAD 0 96
NUMPAD 1 97
NUMPAD 2 98
NUMPAD 3 99
NUMPAD 4 100
NUMPAD 5 101
NUMPAD 6 102
NUMPAD 7 103
NUMPAD 8 104
NUMPAD 9 105
MULTIPLY 106
ADD 107
SUBTRACT 109
DECIMAL POINT 110
DIVIDE 111
F1 112
F2 113
F3 114
F4 115
F5 116
F6 117
F7 118
F8 119
F9 120
F10 121
F11 122
F12 123
NUM LOCK 144
SCROLL LOCK 145
SEMI-COLON 186
EQUAL SIGN 187
COMMA 188
DASH 189
PERIOD 190
FORWARD SLASH 191
GRAVE ACCENT 192
OPEN BRACKET 219
BACK SLASH 220
CLOSE BRAKET 221
SINGLE QUOTE 222

View File

@@ -5,10 +5,10 @@ import haxe.ds.IntMap;
class Direction { class Direction {
private static var directions:IntMap<Direction> = new IntMap<Direction>(); private static var directions:IntMap<Direction> = new IntMap<Direction>();
public static var LEFT(default, null) = new Direction(-1, 0); public static var LEFT(default, never) = new Direction(-1, 0);
public static var TOP(default, null) = new Direction(0, -1); public static var TOP(default, never) = new Direction(0, -1);
public static var RIGHT(default, null) = new Direction(1, 0); public static var RIGHT(default, never) = new Direction(1, 0);
public static var BOTTOM(default, null) = new Direction(0, 1); public static var BOTTOM(default, never) = new Direction(0, 1);
public var x(default, null):Int; public var x(default, null):Int;
public var y(default, null):Int; public var y(default, null):Int;