[client] update frames
This commit is contained in:
1
WORK.md
1
WORK.md
@@ -8,3 +8,4 @@
|
|||||||
* improve bonuses system
|
* improve bonuses system
|
||||||
* screen gamepad on mobiles
|
* screen gamepad on mobiles
|
||||||
* resize render on mobiles
|
* resize render on mobiles
|
||||||
|
* save state in classic game
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ class LevelBundle implements ILevelBundle {
|
|||||||
var bytes = Assets.getBytes('levels/${id}.zip');
|
var bytes = Assets.getBytes('levels/${id}.zip');
|
||||||
return {
|
return {
|
||||||
id: id,
|
id: id,
|
||||||
data: LevelUtil.unpack(bytes),
|
data: LevelUtil.unpack(bytes).map(function(level) {
|
||||||
|
level.packId = id;
|
||||||
|
return level;
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,38 @@ package ru.m.tankz.local;
|
|||||||
import ru.m.tankz.game.GameEvent;
|
import ru.m.tankz.game.GameEvent;
|
||||||
import ru.m.tankz.game.GameRunner;
|
import ru.m.tankz.game.GameRunner;
|
||||||
import ru.m.tankz.local.LocalControlFactory;
|
import ru.m.tankz.local.LocalControlFactory;
|
||||||
|
import ru.m.tankz.storage.GameStorage;
|
||||||
|
import ru.m.tankz.Type;
|
||||||
|
|
||||||
class LocalGame extends GameRunner {
|
class LocalGame extends GameRunner {
|
||||||
|
|
||||||
|
@:provide static var gameStorage:GameStorage;
|
||||||
|
|
||||||
public function new(start:Start) {
|
public function new(start:Start) {
|
||||||
super(start);
|
super(start);
|
||||||
controlFactory = new LocalControlFactory();
|
controlFactory = new LocalControlFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public function onGameEvent(event:GameEvent):Void {
|
||||||
|
super.onGameEvent(event);
|
||||||
|
switch event {
|
||||||
|
case COMPLETE(result):
|
||||||
|
updateProgress(result);
|
||||||
|
case _:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function updateProgress(result:Result):Void {
|
||||||
|
var complete = true;
|
||||||
|
for (rule in result.state.config.game.complete) {
|
||||||
|
if (rule.team != null && rule.team != result.winner) {
|
||||||
|
complete = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (complete) {
|
||||||
|
var progress = gameStorage.get(new PackId(result.state.type));
|
||||||
|
progress.completeLevel(result.level.id, result.state.presetId);
|
||||||
|
gameStorage.set(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,9 +207,7 @@ class Render extends SpriteView implements IRender {
|
|||||||
var item = items[id];
|
var item = items[id];
|
||||||
entryLayer.removeChild(item.view);
|
entryLayer.removeChild(item.view);
|
||||||
playAnimate(item.rect.center, AnimateBundle.tankBoom());
|
playAnimate(item.rect.center, AnimateBundle.tankBoom());
|
||||||
if (shot.score != null) {
|
|
||||||
showScore(item.rect.center, shot.score);
|
showScore(item.rect.center, shot.score);
|
||||||
}
|
|
||||||
items.remove(id);
|
items.remove(id);
|
||||||
}
|
}
|
||||||
case DESTROY(BULLET(id)):
|
case DESTROY(BULLET(id)):
|
||||||
@@ -226,18 +224,14 @@ class Render extends SpriteView implements IRender {
|
|||||||
if (items.exists(id)) {
|
if (items.exists(id)) {
|
||||||
var item = items[id];
|
var item = items[id];
|
||||||
playAnimate(item.rect.center, AnimateBundle.tankBoom());
|
playAnimate(item.rect.center, AnimateBundle.tankBoom());
|
||||||
if (shot.score != null) {
|
|
||||||
showScore(item.rect.center, shot.score);
|
showScore(item.rect.center, shot.score);
|
||||||
}
|
|
||||||
cast(item, EagleRenderItem).death = true;
|
cast(item, EagleRenderItem).death = true;
|
||||||
}
|
}
|
||||||
case DESTROY(BONUS(id, shot)):
|
case DESTROY(BONUS(id, shot)):
|
||||||
if (items.exists(id)) {
|
if (items.exists(id)) {
|
||||||
var item = items[id];
|
var item = items[id];
|
||||||
upperLayer.removeChild(item.view);
|
upperLayer.removeChild(item.view);
|
||||||
if (shot.score != null) {
|
|
||||||
showScore(item.rect.center, shot.score);
|
showScore(item.rect.center, shot.score);
|
||||||
}
|
|
||||||
items.remove(id);
|
items.remove(id);
|
||||||
}
|
}
|
||||||
case DESTROY(CELL(id, x, y, shot)):
|
case DESTROY(CELL(id, x, y, shot)):
|
||||||
@@ -266,7 +260,8 @@ class Render extends SpriteView implements IRender {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private function showScore(point:Point, score:Int):Void {
|
private function showScore(point:Point, score:Null<Int>):Void {
|
||||||
|
if (score != null && score != 0) {
|
||||||
var view:LabelView = new LabelView();
|
var view:LabelView = new LabelView();
|
||||||
view.skinId = "text";
|
view.skinId = "text";
|
||||||
view.text = Std.string(score);
|
view.text = Std.string(score);
|
||||||
@@ -278,3 +273,4 @@ class Render extends SpriteView implements IRender {
|
|||||||
Timer.delay(function() upperLayer.removeChildSafety(view.content), 1000);
|
Timer.delay(function() upperLayer.removeChildSafety(view.content), 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import ru.m.tankz.network.NetworkGame;
|
|||||||
import ru.m.tankz.sound.SoundManager;
|
import ru.m.tankz.sound.SoundManager;
|
||||||
import ru.m.tankz.storage.GameStorage;
|
import ru.m.tankz.storage.GameStorage;
|
||||||
import ru.m.tankz.storage.SettingsStorage;
|
import ru.m.tankz.storage.SettingsStorage;
|
||||||
import ru.m.tankz.Type;
|
|
||||||
import ru.m.tankz.view.game.GameView;
|
import ru.m.tankz.view.game.GameView;
|
||||||
import ru.m.tankz.view.GamepadView;
|
import ru.m.tankz.view.GamepadView;
|
||||||
|
|
||||||
@@ -31,7 +30,6 @@ import ru.m.tankz.view.GamepadView;
|
|||||||
@:provide static var gameStorage:GameStorage;
|
@:provide static var gameStorage:GameStorage;
|
||||||
@:provide static var settings:SettingsStorage;
|
@:provide static var settings:SettingsStorage;
|
||||||
|
|
||||||
//@:provide var result:Result;
|
|
||||||
@:provide static var bus:IControlBus;
|
@:provide static var bus:IControlBus;
|
||||||
|
|
||||||
private var game:IGame;
|
private var game:IGame;
|
||||||
@@ -87,29 +85,12 @@ import ru.m.tankz.view.GamepadView;
|
|||||||
public function onGameEvent(event:GameEvent):Void {
|
public function onGameEvent(event:GameEvent):Void {
|
||||||
switch event {
|
switch event {
|
||||||
case COMPLETE(result):
|
case COMPLETE(result):
|
||||||
//this.result = result;
|
|
||||||
updateProgress(game, result.winner);
|
|
||||||
stop();
|
stop();
|
||||||
switcher.change(ResultFrame.ID, result);
|
switcher.change(ResultFrame.ID, result);
|
||||||
case _:
|
case _:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToDo:
|
|
||||||
private static function updateProgress(game:IGame, winner:TeamId):Void {
|
|
||||||
var complete = true;
|
|
||||||
for (rule in game.config.game.complete) {
|
|
||||||
if (rule.team != null && rule.team != winner) {
|
|
||||||
complete = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (complete) {
|
|
||||||
var progress = gameStorage.get(new PackId(game.state.type));
|
|
||||||
progress.completeLevel(game.level.id, game.state.presetId);
|
|
||||||
gameStorage.set(progress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override public function onHide():Void {
|
override public function onHide():Void {
|
||||||
stop();
|
stop();
|
||||||
soundManager.stopAll();
|
soundManager.stopAll();
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
---
|
---
|
||||||
geometry.size.stretch: true
|
|
||||||
layout:
|
|
||||||
$type: haxework.view.layout.VerticalLayout
|
|
||||||
views:
|
views:
|
||||||
- $type: haxework.view.VGroupView
|
- $type: haxework.view.VGroupView
|
||||||
skinId: container
|
skinId: container
|
||||||
|
geometry.padding: 20
|
||||||
views:
|
views:
|
||||||
- id: header
|
- id: header
|
||||||
$type: haxework.view.LabelView
|
$type: haxework.view.LabelView
|
||||||
skinId: text.header
|
skinId: text.header
|
||||||
- id: levels
|
- $type: haxework.view.ScrollView
|
||||||
|
geometry.size.stretch: true
|
||||||
|
scroll.skinId: scroll.vertical
|
||||||
|
view:
|
||||||
|
id: levels
|
||||||
$type: haxework.view.DataView
|
$type: haxework.view.DataView
|
||||||
geometry.size.width: 100%
|
geometry.size.width: 100%
|
||||||
layout:
|
layout:
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
---
|
---
|
||||||
geometry.size.stretch: true
|
|
||||||
layout:
|
|
||||||
$type: haxework.view.layout.VerticalLayout
|
|
||||||
views:
|
views:
|
||||||
- $type: haxework.view.VGroupView
|
- $type: haxework.view.VGroupView
|
||||||
skinId: container
|
skinId: container
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
package ru.m.tankz.view;
|
package ru.m.tankz.view;
|
||||||
|
|
||||||
|
import haxework.view.ButtonView;
|
||||||
import haxework.view.DataView;
|
import haxework.view.DataView;
|
||||||
import haxework.view.frame.FrameSwitcher;
|
import haxework.view.frame.FrameSwitcher;
|
||||||
import haxework.view.frame.FrameView;
|
import haxework.view.frame.FrameView;
|
||||||
import haxework.view.LabelView;
|
import haxework.view.LabelView;
|
||||||
|
import ru.m.tankz.bundle.ILevelBundle;
|
||||||
import ru.m.tankz.game.GameEvent;
|
import ru.m.tankz.game.GameEvent;
|
||||||
|
import ru.m.tankz.game.GameInit;
|
||||||
import ru.m.tankz.game.GameState;
|
import ru.m.tankz.game.GameState;
|
||||||
|
import ru.m.tankz.storage.GameStorage;
|
||||||
import ru.m.tankz.view.common.LifeView;
|
import ru.m.tankz.view.common.LifeView;
|
||||||
|
|
||||||
@:template class ResultFrame extends FrameView<Result> {
|
@:template class ResultFrame extends FrameView<Result> {
|
||||||
@@ -13,10 +17,13 @@ import ru.m.tankz.view.common.LifeView;
|
|||||||
|
|
||||||
@:view("result") var resultView:DataView<PlayerState, LifeView>;
|
@:view("result") var resultView:DataView<PlayerState, LifeView>;
|
||||||
@:view("level") var levelLabel:LabelView;
|
@:view("level") var levelLabel:LabelView;
|
||||||
|
@:view("next") var nextButton:ButtonView;
|
||||||
|
|
||||||
@:provide var frames:FrameSwitcher;
|
@:provide var frames:FrameSwitcher;
|
||||||
|
@:provide static var levelBundle:ILevelBundle;
|
||||||
|
@:provide static var gameStorage:GameStorage;
|
||||||
|
|
||||||
private var state:GameState;
|
private var result:Result;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
super(ID);
|
super(ID);
|
||||||
@@ -24,28 +31,45 @@ import ru.m.tankz.view.common.LifeView;
|
|||||||
|
|
||||||
private function playerViewFactory(index:Int, player:PlayerState):LifeView {
|
private function playerViewFactory(index:Int, player:PlayerState):LifeView {
|
||||||
var view = new LifeView();
|
var view = new LifeView();
|
||||||
var playerConfig = state.config.getPlayer(player.id);
|
var playerConfig = result.state.config.getPlayer(player.id);
|
||||||
var tankType = playerConfig.tanks[0].type;
|
var tankType = playerConfig.tanks[0].type;
|
||||||
var tankConfig = state.config.getTank(tankType);
|
var tankConfig = result.state.config.getTank(tankType);
|
||||||
view.tank = tankConfig == null ? 'ba' : tankConfig.skin;
|
view.tank = tankConfig == null ? 'ba' : tankConfig.skin;
|
||||||
view.color = state.getPlayerColor(player.id);
|
view.color = result.state.getPlayerColor(player.id);
|
||||||
view.life = player.frags;
|
view.life = player.frags;
|
||||||
view.score = player.score;
|
view.score = player.score;
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
override public function onShow(data:Result):Void {
|
override public function onShow(data:Result):Void {
|
||||||
state = data.state;
|
result = data;
|
||||||
resultView.data = Lambda.array(data.state.players);
|
resultView.data = Lambda.array(result.state.players);
|
||||||
var label = '${data.state.type} Level ${data.level.id}';
|
var label = '${result.state.type} Level ${result.level.id}';
|
||||||
if (data.level.name != null) {
|
if (result.level.name != null) {
|
||||||
label += '\n${data.level.name}';
|
label += '\n${result.level.name}';
|
||||||
}
|
}
|
||||||
levelLabel.text = label;
|
levelLabel.text = label;
|
||||||
|
nextButton.visible = gameStorage.get(result.level.packId).isPresetAvailable(result.level.id + 1, result.state.presetId);
|
||||||
|
nextButton.text = 'Next (${result.level.id + 1})';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function levels():Void {
|
||||||
|
frames.change(LevelFrame.ID, levelBundle.get(result.level.packId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function restart():Void {
|
||||||
|
frames.change(GameFrame.ID, LOCAL({state: result.state.clean(), level: result.level}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function next():Void {
|
private function next():Void {
|
||||||
//ToDo: next level?
|
var pack = levelBundle.get(result.level.packId);
|
||||||
|
if (pack.data.length > result.level.id + 1) {
|
||||||
|
var level = pack.data[result.level.id + 1];
|
||||||
|
var progress = gameStorage.get(pack.id);
|
||||||
|
if (progress.isPresetAvailable(level.id, result.state.presetId)) {
|
||||||
|
frames.change(GameFrame.ID, LOCAL({state: result.state.clean(), level: level}));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function close():Void {
|
private function close():Void {
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
---
|
---
|
||||||
geometry.size.stretch: true
|
|
||||||
layout:
|
|
||||||
$type: haxework.view.layout.VerticalLayout
|
|
||||||
views:
|
views:
|
||||||
- $type: haxework.view.VGroupView
|
- $type: haxework.view.VGroupView
|
||||||
skinId: container
|
skinId: container
|
||||||
@@ -18,3 +15,22 @@ views:
|
|||||||
$type: haxework.view.layout.VerticalLayout
|
$type: haxework.view.layout.VerticalLayout
|
||||||
hAlign: right
|
hAlign: right
|
||||||
margin: 10
|
margin: 10
|
||||||
|
- $type: haxework.view.HGroupView
|
||||||
|
layout.margin: 10
|
||||||
|
geometry.padding: 10
|
||||||
|
views:
|
||||||
|
- id: levels
|
||||||
|
$type: haxework.view.ButtonView
|
||||||
|
skinId: button.simple
|
||||||
|
text: Levels
|
||||||
|
+onPress: $code:levels()
|
||||||
|
- id: restart
|
||||||
|
$type: haxework.view.ButtonView
|
||||||
|
skinId: button.simple
|
||||||
|
text: Restart
|
||||||
|
+onPress: $code:restart()
|
||||||
|
- id: next
|
||||||
|
$type: haxework.view.ButtonView
|
||||||
|
skinId: button.simple
|
||||||
|
text: Next
|
||||||
|
+onPress: $code:next()
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
---
|
---
|
||||||
geometry.size.stretch: true
|
|
||||||
layout:
|
|
||||||
$type: haxework.view.layout.VerticalLayout
|
|
||||||
views:
|
views:
|
||||||
- $type: haxework.view.VGroupView
|
- $type: haxework.view.VGroupView
|
||||||
skinId: container
|
skinId: container
|
||||||
@@ -12,9 +9,7 @@ views:
|
|||||||
text: Settings
|
text: Settings
|
||||||
- $type: haxework.view.ScrollView
|
- $type: haxework.view.ScrollView
|
||||||
geometry.size.stretch: true
|
geometry.size.stretch: true
|
||||||
scroll:
|
scroll.skinId: scroll.vertical
|
||||||
$type: haxework.view.list.VScrollBarView
|
|
||||||
skinId: scroll.vertical
|
|
||||||
view:
|
view:
|
||||||
$type: haxework.view.GroupView
|
$type: haxework.view.GroupView
|
||||||
geometry.size.stretch: true
|
geometry.size.stretch: true
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
---
|
---
|
||||||
geometry.size.stretch: true
|
|
||||||
layout:
|
|
||||||
$type: haxework.view.layout.VerticalLayout
|
|
||||||
views:
|
views:
|
||||||
- $type: haxework.view.VGroupView
|
- $type: haxework.view.VGroupView
|
||||||
skinId: container
|
skinId: container
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package ru.m.tankz.view.common;
|
package ru.m.tankz.view.common;
|
||||||
|
|
||||||
|
import haxework.color.Color;
|
||||||
import haxework.view.HGroupView;
|
import haxework.view.HGroupView;
|
||||||
import haxework.view.ImageView;
|
import haxework.view.ImageView;
|
||||||
import haxework.view.LabelView;
|
import haxework.view.LabelView;
|
||||||
@@ -16,7 +17,7 @@ import ru.m.tankz.Type;
|
|||||||
public var playerId(default, default):PlayerId;
|
public var playerId(default, default):PlayerId;
|
||||||
public var state(null, set):PlayerState;
|
public var state(null, set):PlayerState;
|
||||||
public var tank(null, set):String;
|
public var tank(null, set):String;
|
||||||
public var color(null, set):Int;
|
public var color(null, set):Color;
|
||||||
public var life(null, set):Int;
|
public var life(null, set):Int;
|
||||||
public var score(null, set):Int;
|
public var score(null, set):Int;
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ import ru.m.tankz.Type;
|
|||||||
return tank;
|
return tank;
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline function set_color(value:Int):Int {
|
private inline function set_color(value:Color):Color {
|
||||||
tankImage.color = value;
|
tankImage.color = value;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,9 +51,7 @@ view:
|
|||||||
text: Level
|
text: Level
|
||||||
- $type: haxework.view.ScrollView
|
- $type: haxework.view.ScrollView
|
||||||
geometry.size.stretch: true
|
geometry.size.stretch: true
|
||||||
scroll:
|
scroll.skinId: scroll.vertical
|
||||||
$type: haxework.view.list.VScrollBarView
|
|
||||||
skinId: scroll.vertical
|
|
||||||
view:
|
view:
|
||||||
id: level
|
id: level
|
||||||
$type: haxework.view.DataView
|
$type: haxework.view.DataView
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ typedef GamePreset = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef LevelConfig = {
|
typedef LevelConfig = {
|
||||||
|
@:optional var packId:PackId;
|
||||||
@:optional var id:LevelId;
|
@:optional var id:LevelId;
|
||||||
var data:Array<BrickIndex>;
|
var data:Array<BrickIndex>;
|
||||||
@:optional var name:String;
|
@:optional var name:String;
|
||||||
|
|||||||
@@ -238,10 +238,12 @@ class GameRunner extends Game implements EngineListener {
|
|||||||
gameEventSignal.emit(DESTROY(BULLET(bullet.id)));
|
gameEventSignal.emit(DESTROY(BULLET(bullet.id)));
|
||||||
}
|
}
|
||||||
case [BULLET(bullet), EAGLE(eagle)]:
|
case [BULLET(bullet), EAGLE(eagle)]:
|
||||||
|
if (!eagle.death) {
|
||||||
if (!eagle.protect) {
|
if (!eagle.protect) {
|
||||||
gameEventSignal.emit(DESTROY(EAGLE(eagle.id, buildShot(bullet, eagle.score))));
|
gameEventSignal.emit(DESTROY(EAGLE(eagle.id, buildShot(bullet, eagle.score))));
|
||||||
}
|
}
|
||||||
gameEventSignal.emit(DESTROY(BULLET(bullet.id)));
|
gameEventSignal.emit(DESTROY(BULLET(bullet.id)));
|
||||||
|
}
|
||||||
case _:
|
case _:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,4 +144,8 @@ class GameState {
|
|||||||
var playerState = players[id];
|
var playerState = players[id];
|
||||||
return (playerState == null || playerState.color.zero) ? config.getColor(id) : playerState.color;
|
return (playerState == null || playerState.color.zero) ? config.getColor(id) : playerState.color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clean():GameState {
|
||||||
|
return new GameState(type, presetId, null, controls);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class PackProgress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function isPresetAvailable(levelId:LevelId, presetId:PresetId):Bool {
|
public function isPresetAvailable(levelId:LevelId, presetId:PresetId):Bool {
|
||||||
return presetId == 0 || completed.exists(levelId) && completed.get(levelId).presets.get(presetId - 1) != null;
|
return isLevelAvailable(levelId) && (presetId == 0 || (completed.exists(levelId) && completed.get(levelId).presets.get(presetId - 1) != null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isPresetCompleted(levelId:LevelId, presetId:PresetId):Bool {
|
public function isPresetCompleted(levelId:LevelId, presetId:PresetId):Bool {
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ views:
|
|||||||
$type: haxework.view.ButtonGroup<String>
|
$type: haxework.view.ButtonGroup<String>
|
||||||
geometry.margin.bottom: -2
|
geometry.margin.bottom: -2
|
||||||
buttonSkinId: button.tab
|
buttonSkinId: button.tab
|
||||||
layout:
|
|
||||||
$type: haxework.view.layout.HorizontalLayout
|
|
||||||
+onDataSelect: $code:function(id) switcher.change(id)
|
+onDataSelect: $code:function(id) switcher.change(id)
|
||||||
data:
|
data:
|
||||||
- "map"
|
- "map"
|
||||||
|
|||||||
Reference in New Issue
Block a user