浏览代码

Merge branch 'master' into 'release'

Master

See merge request Isleward/isleward!480
tags/v0.7.0
Big Bad Waffle 4 年前
父节点
当前提交
aa672412a1
共有 50 个文件被更改,包括 706 次插入334 次删除
  1. +1
    -1
      src/client/js/app.js
  2. +5
    -0
      src/client/js/components/keyboardMover.js
  3. +21
    -3
      src/client/js/components/sound.js
  4. +2
    -2
      src/client/js/components/stats.js
  5. +9
    -3
      src/client/js/config.js
  6. +1
    -2
      src/client/js/main.js
  7. +2
    -2
      src/client/js/misc/helpers.js
  8. +225
    -69
      src/client/js/sound/sound.js
  9. +1
    -1
      src/client/package.json
  10. +17
    -49
      src/client/ui/factory.js
  11. +3
    -2
      src/client/ui/templates/context/context.js
  12. +7
    -8
      src/client/ui/templates/equipment/equipment.js
  13. +0
    -9
      src/client/ui/templates/help/help.js
  14. +1
    -0
      src/client/ui/templates/hud/hud.js
  15. +7
    -13
      src/client/ui/templates/inventory/inventory.js
  16. +9
    -15
      src/client/ui/templates/leaderboard/leaderboard.js
  17. +22
    -5
      src/client/ui/templates/leaderboard/styles.less
  18. +7
    -7
      src/client/ui/templates/leaderboard/template.html
  19. +2
    -2
      src/client/ui/templates/login/template.html
  20. +3
    -8
      src/client/ui/templates/mail/mail.js
  21. +12
    -15
      src/client/ui/templates/mainMenu/mainMenu.js
  22. +1
    -1
      src/client/ui/templates/menu/menu.js
  23. +1
    -1
      src/client/ui/templates/messages/messages.js
  24. +4
    -4
      src/client/ui/templates/messages/template.html
  25. +2
    -8
      src/client/ui/templates/online/online.js
  26. +56
    -14
      src/client/ui/templates/options/options.js
  27. +69
    -0
      src/client/ui/templates/options/styles.less
  28. +21
    -3
      src/client/ui/templates/options/template.html
  29. +29
    -16
      src/client/ui/templates/passives/passives.js
  30. +2
    -0
      src/client/ui/templates/quests/quests.js
  31. +3
    -8
      src/client/ui/templates/reputation/reputation.js
  32. +10
    -11
      src/client/ui/templates/stash/stash.js
  33. +2
    -0
      src/client/ui/templates/trade/trade.js
  34. +1
    -0
      src/client/ui/templates/wardrobe/wardrobe.js
  35. +2
    -1
      src/client/ui/templates/workbench/workbench.js
  36. +20
    -3
      src/client/ui/uiBase.js
  37. +7
    -3
      src/server/components/social.js
  38. +34
    -7
      src/server/components/sound.js
  39. +6
    -0
      src/server/components/stash.js
  40. +42
    -1
      src/server/config/clientConfig.js
  41. +1
    -1
      src/server/config/maps/estuary/map.json
  42. +3
    -3
      src/server/config/maps/fjolarok/map.json
  43. +1
    -0
      src/server/config/quests/templates/questTemplate.js
  44. +1
    -1
      src/server/index.js
  45. +12
    -0
      src/server/mail/mailRethinkDb.js
  46. +6
    -0
      src/server/mail/mailSqlite.js
  47. +4
    -21
      src/server/misc/events.js
  48. +1
    -1
      src/server/package.json
  49. +7
    -8
      src/server/server.js
  50. +1
    -2
      src/server/world/map.js

+ 1
- 1
src/client/js/app.js 查看文件

@@ -11,7 +11,7 @@ require.config({
helpers: 'js/misc/helpers',
particles: 'plugins/pixi.particles.min',
pixi: 'https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.1.3/pixi.min',
howler: 'https://cdnjs.cloudflare.com/ajax/libs/howler/2.1.2/howler.core.min'
howler: 'https://cdnjs.cloudflare.com/ajax/libs/howler/2.1.3/howler.core.min'
},
shim: {
howler: {


+ 5
- 0
src/client/js/components/keyboardMover.js 查看文件

@@ -57,6 +57,11 @@ define([
if (this.obj.pather.path.length > 0)
return;

if (this.obj.bumpAnimation)
return;

events.emit('onObjCollideBump', this.obj);

this.obj.addComponent('bumpAnimation', {
deltaX: dx,
deltaY: dy


+ 21
- 3
src/client/js/components/sound.js 查看文件

@@ -1,7 +1,7 @@
define([
'js/sound/sound'
], function (
sound
soundManager
) {
return {
type: 'sound',
@@ -10,8 +10,26 @@ define([
volume: 0,

init: function () {
let obj = this.obj;
sound.addSound(obj.zoneId, this.sound, this.volume, obj.x, obj.y, obj.width, obj.height, obj.area);
const {
sound, volume, music, defaultMusic,
obj: { zoneId, x, y, width, height, area }
} = this;

const config = {
scope: zoneId,
file: sound,
volume,
x,
y,
w: width,
h: height,
area,
music,
defaultMusic,
loop: true
};

soundManager.addSound(config);
}
};
});

+ 2
- 2
src/client/js/components/stats.js 查看文件

@@ -77,7 +77,7 @@ define([
this.hpSpriteInner.visible = this.hpSprite.visible;
},

extend: function (blueprint) {
extend: function (blueprint) {
let bValues = blueprint.values || {};

let values = this.values;
@@ -86,7 +86,7 @@ define([
values[b] = bValues[b];

if (this.obj.self)
events.emit('onGetStats', this.values);
events.emit('onGetStats', this.values, blueprint);

if (this.obj.has('serverId'))
events.emit('onGetPartyStats', this.obj.serverId, this.values);


+ 9
- 3
src/client/js/config.js 查看文件

@@ -10,7 +10,9 @@ define([
playAudio: true,
qualityIndicators: 'off',
unusableIndicators: 'off',
rememberChatChannel: true
rememberChatChannel: true,
soundVolume: 100,
musicVolume: 100
};

const valueChains = {
@@ -54,8 +56,12 @@ define([
if (currentValue === '{unset}')
return;

const morphedValue = valueChains[key] ? currentValue : (currentValue === 'true');
config[key] = morphedValue;
if (['true', 'false'].includes(currentValue))
config[key] = currentValue === 'true';
else if (~~currentValue === parseInt(currentValue))
config[key] = ~~currentValue;
else
config[key] = currentValue;
};

Object.keys(config).forEach(key => loadValue(key) );


+ 1
- 2
src/client/js/main.js 查看文件

@@ -74,8 +74,7 @@ define([

numbers.init();

uiFactory.init(null, globals.clientConfig.uiList);
uiFactory.build('login', 'body');
uiFactory.init(null);

fnQueueTick = getQueueTick(this.update.bind(this));
fnQueueTick();


+ 2
- 2
src/client/js/misc/helpers.js 查看文件

@@ -1,6 +1,6 @@
window.isMobile = /Mobi|Android/i.test(navigator.userAgent);
window.scale = isMobile ? 32 : 40;
window.scaleMult = isMobile ? 4 : 5;
window.scale = isMobile ? 32 : 40 * window.devicePixelRatio;
window.scaleMult = isMobile ? 4 : 5 * window.devicePixelRatio;

//eslint-disable-next-line no-extend-native
Array.prototype.spliceWhere = function (callback, thisArg) {


+ 225
- 69
src/client/js/sound/sound.js 查看文件

@@ -2,91 +2,210 @@ define([
'howler',
'js/misc/physics',
'js/system/events',
'js/config'
'js/config',
'js/system/globals'
], function (
howler,
physics,
events,
config
config,
globals
) {
const globalVolume = 0.3;
let soundVolume = config.soundVolume;
let musicVolume = config.musicVolume;

const globalScopes = ['ui'];
const minDistance = 10;
const fadeDuration = 1800;

window.Howler.volume(globalVolume);

return {
sounds: [],

muted: false,

currentMusic: null,

init: function () {
events.on('onToggleAudio', this.onToggleAudio.bind(this));
events.on('onPlaySound', this.playSound.bind(this));
events.on('onManipulateVolume', this.onManipulateVolume.bind(this));

const { clientConfig: { sounds: loadSounds } } = globals;

Object.entries(loadSounds).forEach(([ scope, soundList ]) => {
soundList.forEach(({ name: soundName, file }) => {
this.addSound({
name: soundName,
file,
scope: 'ui',
autoLoad: true
});
});
});

this.onToggleAudio(config.playAudio);
},

unload: function (zoneId) {
this.sounds.forEach(function (s) {
if ((s.sound) && (s.zoneId !== zoneId))
s.sound.unload();
});
//Fired when a character rezones
// 'scope' is the new zone name
unload: function (newScope) {
const { sounds } = this;

this.sounds.spliceWhere(function (s) {
return (s.zoneId !== zoneId);
});
for (let i = 0; i < sounds.length; i++) {
const { scope, sound } = sounds[i];

if (!globalScopes.includes(scope) && scope !== newScope) {
if (sound)
sound.unload();
sounds.splice(i, 1);
i--;
}
}
},

playSound: function (soundName) {
const soundEntry = this.sounds.find(s => s.name === soundName);
if (!soundEntry)
return;

const { sound } = soundEntry;

sound.volume(soundVolume / 100);
sound.play();
},

playSoundHelper: function (soundEntry, volume) {
const { sound } = soundEntry;

if (!sound) {
const { file, loop } = soundEntry;

soundEntry.sound = this.loadSound(file, loop, true, volume);

return;
}

soundEntry.volume = volume;

volume *= (soundVolume / 100);

if (sound.playing()) {
if (sound.volume() === volume)
return;

sound.volume(volume);
} else {
sound.volume(volume);
sound.play();
}
},

playMusicHelper: function (soundEntry) {
const { sound } = soundEntry;

if (!sound) {
const { file, loop } = soundEntry;

soundEntry.volume = musicVolume;
soundEntry.sound = this.loadSound(file, loop, true, musicVolume / 100);

return;
}

if (!sound.playing()) {
soundEntry.volume = 0;
sound.volume(0);
sound.play();
}

if (this.currentMusic === soundEntry && sound.volume() === musicVolume / 100)
return;

soundEntry.volume = 1;

this.currentMusic = soundEntry;

sound.fade(sound.volume(), (musicVolume / 100), fadeDuration);
},

stopSoundHelper: function (soundEntry) {
const { sound, music } = soundEntry;

if (!sound || !sound.playing())
return;

if (music)
sound.fade(sound.volume(), 0, fadeDuration);
else {
sound.stop();
sound.volume(0);
}
},

update: function (x, y) {
updateSounds: function (playerX, playerY) {
this.sounds.forEach(s => {
let volume;

if (!s.w) {
let dx = Math.abs(s.x - x);
if (dx > 10) {
if (s.sound)
s.sound.volume(0);
return;
}
let dy = Math.abs(s.y - y);
if (dy > 10) {
if (s.sound)
s.sound.volume(0);
return;
}

let dist = 10 - Math.max(dx, dy);
dist = (dist * dist) / 100;
volume = 0.3 * dist;
} else if (physics.isInPolygon(x, y, s.area))
volume = 0.3;
else {
let distance = physics.distanceToPolygon([x, y], s.area);
if (distance > 10) {
if (s.sound)
s.sound.volume(0);
return;
}

let dist = 10 - distance;
dist = (dist * dist) / 100;
volume = 0.3 * dist;
}
const { x, y, area, music, scope } = s;

if (!s.sound) {
//eslint-disable-next-line no-undef
s.sound = new Howl({
src: ['audio/' + s.file],
autoplay: true,
loop: true,
volume: 0
});
if (music || scope === 'ui')
return;

let distance = 0;

if (this.muted)
s.sound.mute(true);
if (!area) {
let dx = Math.abs(x - playerX);
let dy = Math.abs(y - playerY);
distance = Math.max(dx, dy);
} else if (!physics.isInPolygon(playerX, playerY, area))
distance = physics.distanceToPolygon([playerX, playerY], area);
if (distance > minDistance) {
this.stopSoundHelper(s);

return;
}

if (!this.muted)
s.sound.volume(volume * s.volume);
//Exponential fall-off
const volume = s.volume * (1 - (Math.pow(distance, 2) / Math.pow(minDistance, 2)));
this.playSoundHelper(s, volume);
});
},

addSound: function (zoneId, file, volume, x, y, w, h, area) {
if ((!area) && (w)) {
updateMusic: function (playerX, playerY) {
const sounds = this.sounds;

const areaMusic = sounds.filter(s => s.music && s.area);
const currentMusic = areaMusic.find(s => physics.isInPolygon(playerX, playerY, s.area));
const defaultMusic = sounds.find(s => s.music && s.defaultMusic);

if (!currentMusic) {
if (defaultMusic)
this.playMusicHelper(defaultMusic);

const activeMusic = sounds.filter(s => s.music && s !== defaultMusic);
activeMusic.forEach(s => this.stopSoundHelper(s));
} else {
if (defaultMusic)
this.stopSoundHelper(defaultMusic);

if (currentMusic)
this.playMusicHelper(currentMusic);
}
},

update: function (playerX, playerY) {
this.updateSounds(playerX, playerY);
this.updateMusic(playerX, playerY);
},

addSound: function (
{ name: soundName, scope, file, volume = 1, x, y, w, h, area, music, defaultMusic, autoLoad, loop }
) {
if (!area && w) {
area = [
[x, y],
[x + w, y],
@@ -95,19 +214,40 @@ define([
];
}

let sound = {
file: file,
x: x,
y: y,
w: w,
h: h,
volume: volume,
area: area,
sound: null,
zoneId: zoneId
let sound = null;
if (autoLoad)
sound = this.loadSound(file, loop);

if (music)
volume = 0;

const soundEntry = {
name: soundName,
sound,
scope,
file,
loop,
x,
y,
volume,
area,
music,
defaultMusic
};

this.sounds.push(sound);
this.sounds.push(soundEntry);
},

loadSound: function (file, loop = false, autoplay = false, volume = 1) {
//eslint-disable-next-line no-undef
const sound = new Howl({
src: [file],
volume,
loop,
autoplay
});

return sound;
},

onToggleAudio: function (isAudioOn) {
@@ -124,7 +264,23 @@ define([
return;
const { player: { x, y } } = window;
this.update(x, y);
},

onManipulateVolume: function ({ soundType, delta }) {
if (soundType === 'sound')
soundVolume = Math.max(0, Math.min(100, soundVolume + delta));
else if (soundType === 'music')
musicVolume = Math.max(0, Math.min(100, musicVolume + delta));

const volume = soundType === 'sound' ? soundVolume : musicVolume;

events.emit('onVolumeChange', {
soundType,
volume
});

const { player: { x, y } } = window;
this.update(x, y);
}
};


+ 1
- 1
src/client/package.json 查看文件

@@ -1,6 +1,6 @@
{
"name": "isleward_client",
"version": "0.6.1",
"version": "0.7.0",
"description": "isleward",
"dependencies": {},
"devDependencies": {


+ 17
- 49
src/client/ui/factory.js 查看文件

@@ -9,67 +9,35 @@ define([
globals,
tosAcceptanceValid
) {
const startupUis = [
'inventory',
'equipment',
'hud',
'target',
'menu',
'spells',
'messages',
'online',
'mainMenu',
'context',
'party',
'help',
'dialogue',
'buffs',
'tooltips',
'tooltipInfo',
'tooltipItem',
'announcements',
'quests',
'events',
'progressBar',
'stash',
'talk',
'trade',
'overlay',
'death',
'leaderboard',
'reputation',
'mail',
'wardrobe',
'passives',
'workbench',
'middleHud',
'options'
];

return {
uis: [],
root: '',

init: function (root, uiList = []) {
init: function (root) {
if (root)
this.root = root + '/';

startupUis.push(...uiList);

events.on('onEnterGame', this.onEnterGame.bind(this));
events.on('onUiKeyDown', this.onUiKeyDown.bind(this));
events.on('onResize', this.onResize.bind(this));

globals.clientConfig.uiLoginList.forEach(u => {
if (u.path)
this.buildModUi(u);
else
this.build(u);
});
},

onEnterGame: function () {
events.clearQueue();

startupUis.forEach(function (u) {
globals.clientConfig.uiList.forEach(u => {
if (u.path)
this.buildModUi(u);
else
this.build(u);
}, this);
});
},

buildModUi: function (config) {
@@ -95,14 +63,14 @@ define([
path = options.path + `\\${type}.js`;
else
path = this.root + 'ui/templates/' + type + '/' + type;
require([path], this.onGetTemplate.bind(this, options));
require([path], this.onGetTemplate.bind(this, options, type));
},
onGetTemplate: function (options, template) {
let ui = $.extend(true, {}, uiBase, template);
onGetTemplate: function (options, type, template) {
let ui = $.extend(true, { type }, uiBase, template);
ui.setOptions(options);
requestAnimationFrame(this.renderUi.bind(this, ui));
},

@@ -129,7 +97,7 @@ define([
return;

keyEvent.consumed = true;
u.hide();
u.toggle();
});
$('.uiOverlay').hide();


+ 3
- 2
src/client/ui/templates/context/context.js 查看文件

@@ -43,9 +43,10 @@ define([
if (hotkey)
row.find('.hotkey').html(`(${hotkey})`);

if (c.callback)
if (c.callback) {
row.on('click', this.onClick.bind(this, i, c.callback));
else
row.on('click', events.emit.bind(events, 'onClickContextItem'));
} else
row.addClass('no-hover');
});



+ 7
- 8
src/client/ui/templates/equipment/equipment.js 查看文件

@@ -47,21 +47,20 @@ define([
beforeHide: function () {
this.isInspecting = false;
delete this.result;

this.find('.itemList').hide();

this.onHoverItem(null, null, null);
},

toggle: function (show) {
this.shown = !this.el.is(':visible');
onAfterShow: function () {
this.isInspecting = false;
delete this.result;

this.find('.itemList').hide();

if (this.shown) {
this.show();
this.onGetStats();
this.onGetItems();
} else
this.hide();
this.onGetStats();
this.onGetItems();

this.onHoverItem(null, null, null);
},


+ 0
- 9
src/client/ui/templates/help/help.js 查看文件

@@ -22,15 +22,6 @@ define([
onKeyDown: function (key) {
if (key === 'h')
this.toggle();
},

toggle: function () {
this.shown = !this.el.is(':visible');

if (this.shown)
this.show();
else
this.hide();
}
};
});

+ 1
- 0
src/client/ui/templates/hud/hud.js 查看文件

@@ -55,6 +55,7 @@ define([
return;

events.emit('onHideItemTooltip', quickItem);
events.emit('onUseQuickItem', quickItem);

client.request({
cpn: 'player',


+ 7
- 13
src/client/ui/templates/inventory/inventory.js 查看文件

@@ -495,19 +495,9 @@ define([
this.build();
},

toggle: function (show) {
this.shown = !this.el.is(':visible');

if (this.shown) {
this.find('.split-box').hide();
this.show();
this.build();
} else {
this.hide();
events.emit('onHideInventory');
events.emit('onHideContextMenu');
}

onAfterShow: function () {
this.find('.split-box').hide();
this.build();
this.hideTooltip();
},

@@ -521,6 +511,10 @@ define([
$('.uiSpells').css('z-index', this.oldSpellsZIndex);
this.oldSpellsZIndex = null;
}

events.emit('onHideInventory');
events.emit('onHideContextMenu');
this.hideTooltip();
},

performItemAction: function (item, action) {


+ 9
- 15
src/client/ui/templates/leaderboard/leaderboard.js 查看文件

@@ -167,23 +167,17 @@ define([
this.find('.btn-next, .btn-last').addClass('disabled');
},

toggle: function () {
let shown = !this.el.is(':visible');

if (shown) {
this.find('.prophecy[prophecy]').removeClass('selected');
let prophecies = window.player.prophecies;
prophecies = prophecies ? prophecies.list : [];
prophecies.forEach(function (p) {
this.find('.prophecy[prophecy="' + p + '"]').addClass('selected');
}, this);
onAfterShow: function () {
this.find('.prophecy[prophecy]').removeClass('selected');
let prophecies = window.player.prophecies;
prophecies = prophecies ? prophecies.list : [];
prophecies.forEach(function (p) {
this.find('.prophecy[prophecy="' + p + '"]').addClass('selected');
}, this);

this.prophecyFilter = null;
this.prophecyFilter = null;

this.getList();
this.show();
} else
this.hide();
this.getList();
}
};
});

+ 22
- 5
src/client/ui/templates/leaderboard/styles.less 查看文件

@@ -17,6 +17,7 @@
padding-top: 8px;
margin: auto;
}

}

.result {
@@ -37,6 +38,7 @@
float: left;
color: @white;
}

}

.list {
@@ -50,12 +52,14 @@
.col {
color: @green;
}

}

&.online {
.col {
color: @greenB;
}

}

.col {
@@ -69,7 +73,9 @@
white-space: nowrap;
text-overflow: ellipsis;
}

}

}

.buttons {
@@ -93,8 +99,11 @@
&:last-child {
margin-right: 0px;
}

}

}

}

.prophecies {
@@ -113,18 +122,20 @@
.heading-text {
margin: auto;
}

}

.prophecy, .btn {
.prophecy,
.btn {
cursor: pointer;
}
.prophecy {
width: 100%;
padding: 5px 10px;
background-color: @grayD;
color: @white;
margin-bottom: 15px;
margin-bottom: 12px;

&.selected {
color: @green;
@@ -134,16 +145,19 @@
background-color: @grayC;
}

&.prophecy-mine, &.prophecy-none {
&.prophecy-mine,
&.prophecy-none {
background-color: @grayC;

&:hover {
background-color: @grayB;
}

}

}

.btn {
.btn-refresh {
width: calc(100% - 10px);
background-color: @blueC;
padding: 5px 10px;
@@ -154,8 +168,11 @@
&:hover {
background-color: @blueB;
}

}

}

}

.mobile .uiLeaderboard {


+ 7
- 7
src/client/ui/templates/leaderboard/template.html 查看文件

@@ -21,13 +21,13 @@
<div class="heading">
<div class="heading-text">prophecies</div>
</div>
<div class="prophecy prophecy-none">none</div>
<div class="prophecy prophecy-mine">mine</div>
<div class="prophecy" prophecy="austere">austere</div>
<div class="prophecy" prophecy="butcher">butcher</div>
<div class="prophecy" prophecy="crushable">crushable</div>
<div class="prophecy" prophecy="hardcore">hardcore</div>
<div class="prophecy" prophecy="titangrip">titangrip</div>
<div class="btn prophecy prophecy-none">none</div>
<div class="btn prophecy prophecy-mine">mine</div>
<div class="btn prophecy" prophecy="austere">austere</div>
<div class="btn prophecy" prophecy="butcher">butcher</div>
<div class="btn prophecy" prophecy="crushable">crushable</div>
<div class="btn prophecy" prophecy="hardcore">hardcore</div>
<div class="btn prophecy" prophecy="titangrip">titangrip</div>
<div class="btn btn-refresh">refresh</div>
</div>
</div>

+ 2
- 2
src/client/ui/templates/login/template.html 查看文件

@@ -11,11 +11,11 @@
</div>
<div class="message"></div>
</div>
<div class="news" location="https://gitlab.com/Isleward/isleward/tags/v0.6.1">[ Latest Release Notes ]</div>
<div class="news" location="https://gitlab.com/Isleward/isleward/tags/v0.7.0">[ Latest Release Notes ]</div>
<div class="extra">
<div class="el btn btnPatreon" location="https://patreon.com/bigbadwaffle">Pledge on Patreon</div>
<div class="el btn btnPaypal" location="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BR2CC82WUAVEA">Donate on Paypal</div>
<div class="el btn btnWiki" location="http://wiki.isleward.com/Main_Page">Access the Wiki</div>
</div>
<div class="version" location="https://gitlab.com/Isleward/isleward/tags/v0.6.1">v0.6.1</div>
<div class="version" location="https://gitlab.com/Isleward/isleward/tags/v0.7.0">v0.7.0</div>
</div>

+ 3
- 8
src/client/ui/templates/mail/mail.js 查看文件

@@ -64,6 +64,7 @@ define([
return;
}

events.emit('onSendMail');
this.hide();
},

@@ -92,14 +93,8 @@ define([
.on('mouseleave', this.hideTooltip.bind(this, itemEl, item));
},

toggle: function () {
this.shown = !this.el.is(':visible');

if (this.shown) {
this.show();
this.find('input').focus();
} else
this.hide();
onAfterShow: function () {
this.find('input').focus();
},

hideTooltip: function () {


+ 12
- 15
src/client/ui/templates/mainMenu/mainMenu.js 查看文件

@@ -24,9 +24,10 @@ define([
modal: true,

postRender: function () {
this.onEvent('onToggleMainMenu', this.toggle.bind(this));
this.onEvent('onCloseOptions', this.show.bind(this));
this.onEvent('onShowMainMenu', this.show.bind(this));

this.el.find('.btnOptions').on('click', this.handler.bind(this, 'onToggleOptions'));
this.el.find('.btnOptions').on('click', this.openOptions.bind(this));
this.el.find('.btnCharSelect').on('click', this.charSelect.bind(this));
this.el.find('.btnLogOut').on('click', this.logOut.bind(this));
this.el.find('.btnContinue').on('click', this.toggle.bind(this));
@@ -35,13 +36,11 @@ define([
this.onEvent('onResize', this.onResize.bind(this));
},

handler: function (e) {
openOptions: function () {
if (isMobile)
this.el.removeClass('active');

events.emit(e);

return false;
events.emit('onOpenOptions');
},
patreon: function () {
@@ -81,18 +80,16 @@ define([
this.el.find('.btnScreen').html('Fullscreen');
},

toggle: function () {
onAfterShow: function () {
this.onResize();

this.shown = !this.el.is(':visible');
events.emit('onShowOverlay', this.el);
},

beforeHide: function () {
this.onResize();

if (this.shown) {
this.show();
events.emit('onShowOverlay', this.el);
} else {
this.hide();
events.emit('onHideOverlay', this.el);
}
events.emit('onHideOverlay', this.el);
},

logOut: function () {


+ 1
- 1
src/client/ui/templates/menu/menu.js 查看文件

@@ -21,7 +21,7 @@ define([
this.find('.btnOnline').on('click', this.handler.bind(this, 'onShowOnline'));
this.find('.btnLeaderboard').on('click', this.handler.bind(this, 'onShowLeaderboard'));
this.find('.btnReputation').on('click', this.handler.bind(this, 'onShowReputation'));
this.find('.btnMainMenu').on('click', this.handler.bind(this, 'onToggleMainMenu'));
this.find('.btnMainMenu').on('click', this.handler.bind(this, 'onShowMainMenu'));
this.find('.btnPassives').on('click', this.handler.bind(this, 'onShowPassives'));

this.onEvent('onGetPassivePoints', this.onGetPassivePoints.bind(this));


+ 1
- 1
src/client/ui/templates/messages/messages.js 查看文件

@@ -40,7 +40,7 @@ define([
this.onEvent('onToggleLastChannel', this.onToggleLastChannel.bind(this));

this
.find('.filter:not(.channel):not(.btn)')
.find('.filter:not(.channel)')
.on('mouseover', this.onFilterHover.bind(this, true))
.on('mouseleave', this.onFilterHover.bind(this, false))
.on('click', this.onClickFilter.bind(this));


+ 4
- 4
src/client/ui/templates/messages/template.html 查看文件

@@ -1,9 +1,9 @@
<div class="uiMessages active">
<div class="filters">
<div class="filter active" filter="info">info</div>
<div class="filter active" filter="rep">reputation</div>
<div class="filter active" filter="chat">players</div>
<div class="filter active" filter="loot">loot</div>
<div class="btn filter active" filter="info">info</div>
<div class="btn filter active" filter="rep">reputation</div>
<div class="btn filter active" filter="chat">players</div>
<div class="btn filter active" filter="loot">loot</div>
</div>
<div class="list rep chat info loot">


+ 2
- 8
src/client/ui/templates/online/online.js 查看文件

@@ -45,14 +45,8 @@ define([
this.toggle();
},

toggle: function () {
this.shown = !this.el.is(':visible');

if (this.shown) {
this.show();
this.build();
} else
this.hide();
onAfterShow: function () {
this.build();
},

onGetConnectedPlayer: function (list) {


+ 56
- 14
src/client/ui/templates/options/options.js 查看文件

@@ -27,7 +27,7 @@ define([
hasClose: true,

postRender: function () {
this.onEvent('onToggleOptions', this.toggle.bind(this));
this.onEvent('onOpenOptions', this.show.bind(this));

//Can only toggle fullscreen directly in a listener, not deferred the way jQuery does it
this.find('.item.screen .name')[0].addEventListener('click', this.toggleScreen.bind(this));
@@ -36,18 +36,35 @@ define([
this.find('.item.events .name').on('click', this.toggleEvents.bind(this));
this.find('.item.quality .name').on('click', this.toggleQualityIndicators.bind(this));
this.find('.item.unusable .name').on('click', this.toggleUnusableIndicators.bind(this));
this.find('.item.audio .name').on('click', this.toggleAudio.bind(this));
this.find('.item.lastChannel .name').on('click', this.toggleLastChannel.bind(this));

this.find('.item.volume .btn').on('click', this.modifyVolume.bind(this));

this.onEvent('onResize', this.onResize.bind(this));
this.onEvent('onUiKeyDown', this.onKeyDown.bind(this));
this.onEvent('onToggleAudio', this.onToggleAudio.bind(this));
this.onEvent('onToggleNameplates', this.onToggleNameplates.bind(this));
this.onEvent('onToggleQualityIndicators', this.onToggleQualityIndicators.bind(this));
this.onEvent('onToggleUnusableIndicators', this.onToggleUnusableIndicators.bind(this));
this.onEvent('onToggleEventsVisibility', this.onToggleEventsVisibility.bind(this));
this.onEvent('onToggleQuestsVisibility', this.onToggleQuestsVisibility.bind(this));
this.onEvent('onToggleLastChannel', this.onToggleLastChannel.bind(this));
this.onEvent('onVolumeChange', this.onVolumeChange.bind(this));

this.find('.item').on('click', events.emit.bind(events, 'onClickOptionsItem'));
},

modifyVolume: function (e) {
const el = $(e.target);

const isIncrease = el.hasClass('increase');
const delta = isIncrease ? 10 : -10;
const soundType = el.parent().parent().hasClass('sound') ? 'sound' : 'music';

events.emit('onManipulateVolume', {
soundType,
delta
});
},

toggleUnusableIndicators: function () {
@@ -152,6 +169,26 @@ define([
this.find('.item.lastChannel .value').html(newValue);
},

onVolumeChange: function ({ soundType, volume }) {
const item = this.find(`.item.volume.${soundType}`);
item.find('.value').html(volume);

const tickLeftPosition = `${volume}%`;
item.find('.tick').css({ left: tickLeftPosition });
const btnDecrease = item.find('.btn.decrease').removeClass('disabled');
const btnIncrease = item.find('.btn.increase').removeClass('disabled');

if (volume === 0)
btnDecrease.addClass('disabled');
else if (volume === 100)
btnIncrease.addClass('disabled');

const configKey = `${soundType}Volume`;
config.set(configKey, volume);
},

build: function () {
this.onToggleNameplates(config.showNames);
this.onToggleAudio(config.playAudio);
@@ -160,21 +197,24 @@ define([
this.onToggleQualityIndicators(config.qualityIndicators);
this.onToggleUnusableIndicators(config.unusableIndicators);
this.onToggleLastChannel(config.rememberChatChannel);
this.onVolumeChange({
soundType: 'sound',
volume: config.soundVolume
});

this.onVolumeChange({
soundType: 'music',
volume: config.musicVolume
});
},

toggle: function () {
onAfterShow: function () {
this.onResize();

if (!this.shown) {
this.show();
this.shown = true;
events.emit('onShowOverlay', this.el);
events.emit('onShowOverlay', this.el);

this.build();
} else {
this.hide();
this.shown = false;
}
this.build();
},
onKeyDown: function (keyEvent) {
@@ -191,7 +231,9 @@ define([
},

afterHide: function () {
events.emit('onToggleMainMenu');
this.onResize();

events.emit('onCloseOptions');
}
};
});

+ 69
- 0
src/client/ui/templates/options/styles.less 查看文件

@@ -71,6 +71,75 @@
padding-right: 10px;
}

&.volume {
flex-wrap: wrap;
height: 60px;

.name {
pointer-events: none;
}

.name,
.value {
width: 50%;
height: 30px;
}

.slider {
padding: 0px 10px;
width: 100%;
height: 30px;
display: flex;
position: relative;

.btn {
width: 30px;
margin-top: 5px;
height: calc(100% - 10px);
display: flex;
justify-content: center;
align-items: center;
color: @white;
background-color: @blackA;
padding-top: unset;

&:first-child {
margin-right: 10px;
}

&:last-child {
margin-left: 10px;
}

&:hover {
background-color: @grayD;
}

}

.bar {
width: 100%;
background-color: @blueD;
border-top: 10px solid @blackC;
border-bottom: 10px solid @blackC;
height: 100%;
position: relative;

.tick {
position: absolute;
width: 5px;
height: 20px;
top: -5px;
left: 100%;
background-color: @blueA;
}

}

}

}

}

}


+ 21
- 3
src/client/ui/templates/options/template.html 查看文件

@@ -9,9 +9,27 @@
<div class="name">Fullscreen</div>
<div class="value">Off</div>
</div>
<div class="item audio">
<div class="name">Audio</div>
<div class="value">On</div>
<div class="item volume sound">
<div class="name">Sound Volume</div>
<div class="value">100</div>
<div class="slider">
<div class="btn decrease">-</div>
<div class="bar">
<div class="tick"></div>
</div>
<div class="btn increase">+</div>
</div>
</div>
<div class="item volume music">
<div class="name">Music Volume</div>
<div class="value">100</div>
<div class="slider">
<div class="btn decrease">-</div>
<div class="bar">
<div class="tick"></div>
</div>
<div class="btn increase">+</div>
</div>
</div>
<div class="heading hud">HUD</div>
<div class="item nameplates">


+ 29
- 16
src/client/ui/templates/passives/passives.js 查看文件

@@ -150,31 +150,24 @@ define([
nodes.forEach(n => this.renderers.node.call(this, n, n.pos.x, n.pos.y));
},

toggle: function (show) {
this.shown = !this.el.is(':visible');
onAfterShow: function () {
//Calculate midpoint
let start = this.data.nodes.find(n => n.spiritStart === window.player.class);

if (this.shown) {
//Calculate midpoint
let start = this.data.nodes.find(n => n.spiritStart === window.player.class);
this.pos.x = start.pos.x * constants.gridSize;
this.pos.y = start.pos.y * constants.gridSize;

this.pos.x = start.pos.x * constants.gridSize;
this.pos.y = start.pos.y * constants.gridSize;
this.pos.x -= ~~(this.canvas.width / 2);
this.pos.y -= ~~(this.canvas.height / 2);

this.pos.x -= ~~(this.canvas.width / 2);
this.pos.y -= ~~(this.canvas.height / 2);

this.show();
this.onResize();
this.renderNodes();
} else
this.hide();
this.onResize();
this.renderNodes();

events.emit('onHideTooltip', this.el[0]);
this.tooltipId = null;
},

beforeHide: function () {
events.emit('onHideTooltip', this.el[0]);
events.emit('onHideTooltip', this.el[0]);
this.tooltipId = null;
},
@@ -445,6 +438,26 @@ define([
else if (isMobile && this.tooltipId !== node.id)
return;

const canReachNode = this.data.links.some(l => {
return (
(
l.to.id === node.id ||
l.from.id === node.id
) &&
this.data.nodes.some(n => {
return (
(n.id === l.from.id && n.selected) ||
(n.id === l.to.id && n.selected)
);
})
);
});

if (!canReachNode)
return;

events.emit('onTryTickPassiveNode', { tick: !node.selected });

client.request({
cpn: 'player',
method: 'performAction',


+ 2
- 0
src/client/ui/templates/quests/quests.js 查看文件

@@ -107,6 +107,8 @@ define([
msg: 'Quest ready for turn-in'
});
}

events.emit('onQuestReady', quest);
}
},



+ 3
- 8
src/client/ui/templates/reputation/reputation.js 查看文件

@@ -46,6 +46,7 @@ define([
let el = $(html).appendTo(elList);

el.on('click', this.onSelectFaction.bind(this, el, l));
el.on('click', events.emit.bind(events, 'onClickButton'));
});
},

@@ -93,14 +94,8 @@ define([
this.onSelectFaction(selElement, list[selElement.index() + 1]);
},

toggle: function () {
let shown = !this.el.is(':visible');

if (shown) {
this.build();
this.show();
} else
this.hide();
onAfterShow: function () {
this.build();
}
};
});

+ 10
- 11
src/client/ui/templates/stash/stash.js 查看文件

@@ -139,21 +139,20 @@ define([
this.build();
},

toggle: function () {
onAfterShow: function () {
if ((!this.shown) && (!window.player.stash.active))
return;

this.shown = !this.el.is(':visible');
events.emit('onShowOverlay', this.el);
this.build();
},

if (this.shown) {
this.show();
events.emit('onShowOverlay', this.el);
this.build();
} else {
this.hide();
events.emit('onHideOverlay', this.el);
events.emit('onHideContextMenu');
}
beforeHide: function () {
if ((!this.shown) && (!window.player.stash.active))
return;

events.emit('onHideOverlay', this.el);
events.emit('onHideContextMenu');
},

onOpenStash: function () {


+ 2
- 0
src/client/ui/templates/trade/trade.js 查看文件

@@ -128,6 +128,8 @@ define([
callback: this.onServerRespond.bind(this, el)
});

events.emit('onBuySellItem', this.el);

let uiInventory = $('.uiInventory').data('ui');
uiInventory.hideTooltip(el, item, e);
},


+ 1
- 0
src/client/ui/templates/wardrobe/wardrobe.js 查看文件

@@ -41,6 +41,7 @@ define([
.appendTo(container);

el.on('click', this.setPreview.bind(this, l, el));
el.on('click', events.emit.bind(events, 'onClickListItem'));

if (l.id === window.player.skinId) {
el.addClass('current');


+ 2
- 1
src/client/ui/templates/workbench/workbench.js 查看文件

@@ -74,6 +74,7 @@ define([
.appendTo(container);

el.on('click', this.onSelectRecipe.bind(this, el, r));
el.on('click', events.emit.bind(events, 'onClickListItem'));
}, this);
},

@@ -185,7 +186,7 @@ define([

const items = window.player.inventory.items
.filter(item => {
const isValidItem = allowedItemIds.find(f => f === item.id);
const isValidItem = allowedItemIds.some(f => f === item.id);

return isValidItem;
});


+ 20
- 3
src/client/ui/uiBase.js 查看文件

@@ -45,9 +45,16 @@ define([
if ((this.centeredX) || (this.centeredY))
this.center(this.centeredX, this.centeredY);

this.registerUiEvents();

this.shown = this.el.is(':visible');
},

registerUiEvents: function () {
this.find('.btn').on('click', events.emit.bind(events, 'onClickButton'));
this.find('.tab').on('click', events.emit.bind(events, 'onClickTab'));
},

onMouseEnter: function (enter) {
events.emit('onUiHover', enter);
},
@@ -99,6 +106,9 @@ define([
},

show: function () {
if (this.shown)
return;

if (this.modal) {
//Close any other open modal
$('.modal').toArray().forEach(el => {
@@ -119,9 +129,14 @@ define([

if ((this.centeredX) || (this.centeredY))
this.center(this.centeredX, this.centeredY);

events.emit('onShowUi', this);
},

hide: function () {
if (!this.shown)
return;

if (this.beforeHide)
this.beforeHide();

@@ -130,6 +145,8 @@ define([

if (this.afterHide)
this.afterHide();

events.emit('onHideUi', this);
},

destroy: function () {
@@ -178,12 +195,12 @@ define([
},

toggle: function () {
this.shown = !this.el.is(':visible');

if (this.shown)
if (!this.shown)
this.show();
else
this.hide();

events.emit('onToggleUi', this);
},

buildClose: function () {


+ 7
- 3
src/server/components/social.js 查看文件

@@ -172,6 +172,7 @@ module.exports = {
class: 'color-yellowB',
message: '(you to ' + playerName + '): ' + messageString,
type: 'chat',
subType: 'privateOut',
source: this.obj.name
}]
}
@@ -184,6 +185,7 @@ module.exports = {
class: 'color-yellowB',
message: '(' + this.obj.name + ' to you): ' + messageString,
type: 'chat',
subType: 'privateIn',
source: this.obj.name
}]
}
@@ -399,7 +401,7 @@ module.exports = {

//Sends a notification to yourself
// arg1 = { message, className, type }
notifySelf: function ({ message, className = 'color-redA', type = 'info' }) {
notifySelf: function ({ message, className = 'color-redA', type = 'info', subType }) {
const { obj: { id, serverId, instance: { syncer } } } = this;

syncer.queue('onGetMessages', {
@@ -407,7 +409,8 @@ module.exports = {
messages: [{
class: className,
message,
type
type,
subType
}]
}, [serverId]);
},
@@ -418,9 +421,10 @@ module.exports = {
const { obj: { id, serverId, instance: { syncer } } } = this;

messages.forEach(m => {
const { className = 'color-redA', type = 'info ' } = m;
const { className = 'color-redA', type = 'info', subType } = m;
m.className = className;
m.type = type;
m.subType = subType;
});

syncer.queue('onGetMessages', {


+ 34
- 7
src/server/components/sound.js 查看文件

@@ -1,14 +1,41 @@
const serializeProps = [
'sound',
'defaultMusic',
'volume',
'music'
];

module.exports = {
type: 'sound',

sound: null,
volume: 0,
simplified: null,

buildSimplified: function () {
const s = Object.fromEntries(
serializeProps
.map(p => {
if (!this.has(p))
return null;

return [p, this[p]];
})
.filter(p => !!p)
);

s.type = 'sound';

let file = s.sound;
if (!file.includes('server'))
file = 'audio/' + file;
s.sound = file;

this.simplified = s;
},

simplify: function () {
return {
type: 'sound',
sound: this.sound,
volume: this.volume
};
if (!this.simplified)
this.buildSimplified();

return this.simplified;
}
};

+ 6
- 0
src/server/components/stash.js 查看文件

@@ -134,6 +134,12 @@ module.exports = {
}
});

let msg = 'Press U to access your Shared Stash';
this.obj.instance.syncer.queue('onGetAnnouncement', {
src: this.obj.id,
msg: msg
}, [obj.serverId]);

if (this.active && this.items.length > this.maxItems) {
const message = `You have more than ${this.maxItems} items in your stash. In the future, these items will be lost.`;
obj.social.notifySelf({ message });


+ 42
- 1
src/server/config/clientConfig.js 查看文件

@@ -25,11 +25,52 @@ const config = {
'images/skins/0010.png',
'images/skins/0012.png'
],
uiList: [],
uiLoginList: [
'login'
],
uiList: [
'inventory',
'equipment',
'hud',
'target',
'menu',
'spells',
'messages',
'online',
'mainMenu',
'context',
'party',
'help',
'dialogue',
'buffs',
'tooltips',
'tooltipInfo',
'tooltipItem',
'announcements',
'quests',
'events',
'progressBar',
'stash',
'talk',
'trade',
'overlay',
'death',
'leaderboard',
'reputation',
'mail',
'wardrobe',
'passives',
'workbench',
'middleHud',
'options'
],
contextMenuActions: {
player: [],
npc: []
},
sounds: {
ui: []
},
tos
};



+ 1
- 1
src/server/config/maps/estuary/map.json 查看文件

@@ -1296,7 +1296,7 @@
"objects":[],
"opacity":1,
"type":"objectgroup",
"visible":false,
"visible":true,
"x":0,
"y":0
}],


+ 3
- 3
src/server/config/maps/fjolarok/map.json 查看文件

@@ -1042,7 +1042,7 @@
{
"name":"cpnNotice",
"type":"string",
"value":"{\n\t\"msg\": \"Your Stash (U to Access)\",\n\t\"actions\": {\n\t\t\"enter\": {\n\t\t\t\"cpn\": \"stash\",\n\t\t\t\"method\": \"setActive\",\n\t\t\t\"args\": [true]\n\t\t},\n\t\t\"exit\": {\n\t\t\t\"cpn\": \"stash\",\n\t\t\t\"method\": \"setActive\",\n\t\t\t\"args\": [false]\n\t\t}\n\t}\n}"
"value":"{\"actions\":{\"enter\":{\"cpn\":\"stash\",\"method\":\"setActive\",\"args\":[true]},\"exit\":{\"cpn\":\"stash\",\"method\":\"setActive\",\"args\":[false]}}}"
}],
"rotation":0,
"type":"",
@@ -1302,7 +1302,7 @@
{
"name":"cpnNotice",
"type":"string",
"value":"{\n\t\"msg\": \"Your Stash (U to Access)\",\n\t\"actions\": {\n\t\t\"enter\": {\n\t\t\t\"cpn\": \"stash\",\n\t\t\t\"method\": \"setActive\",\n\t\t\t\"args\": [true]\n\t\t},\n\t\t\"exit\": {\n\t\t\t\"cpn\": \"stash\",\n\t\t\t\"method\": \"setActive\",\n\t\t\t\"args\": [false]\n\t\t}\n\t}\n}"
"value":"{\"actions\":{\"enter\":{\"cpn\":\"stash\",\"method\":\"setActive\",\"args\":[true]},\"exit\":{\"cpn\":\"stash\",\"method\":\"setActive\",\"args\":[false]}}}"
}],
"rotation":0,
"type":"",
@@ -4218,7 +4218,7 @@
"y":0
}],
"nextlayerid":31,
"nextobjectid":991,
"nextobjectid":995,
"orientation":"orthogonal",
"properties":[
{


+ 1
- 0
src/server/config/quests/templates/questTemplate.js 查看文件

@@ -38,6 +38,7 @@ module.exports = {
});

this.obj.syncer.setArray(true, 'quests', 'updateQuests', this.simplify(true));
this.obj.fireEvent('onQuestReady', this);
},

complete: function () {


+ 1
- 1
src/server/index.js 查看文件

@@ -28,11 +28,11 @@ let startup = {
process.on('unhandledRejection', this.onError.bind(this));
process.on('uncaughtException', this.onError.bind(this));

animations.init();
mods.init(this.onModsLoaded.bind(this));
},

onModsLoaded: function () {
animations.init();
routerConfig.init();
classes.init();
spellsConfig.init();


+ 12
- 0
src/server/mail/mailRethinkDb.js 查看文件

@@ -61,6 +61,12 @@ module.exports = {

sentMessages.push(r.msg);
delete r.msg;
} else {
player.social.notifySelf({
message: 'You have received a mail',
className: 'color-greenB',
subType: 'mail'
});
}

delete r.pos;
@@ -130,6 +136,12 @@ module.exports = {

sentMessages.push(r.msg);
delete r.msg;
} else {
player.social.notifySelf({
message: 'You have received a mail',
className: 'color-greenB',
subType: 'mail'
});
}

delete r.pos;


+ 6
- 0
src/server/mail/mailSqlite.js 查看文件

@@ -84,6 +84,12 @@ module.exports = {

sentMessages.push(r.msg);
delete r.msg;
} else {
player.social.notifySelf({
message: 'You have received a mail',
className: 'color-greenB',
subType: 'mail'
});
}
inventory.getItem(r, false, false, false, true);
}


+ 4
- 21
src/server/misc/events.js 查看文件

@@ -1,25 +1,13 @@
module.exports = {
events: {},
queue: [],
on: function (event, callback) {
let list = this.events[event] || (this.events[event] = []);
list.push(callback);

for (let i = 0; i < this.queue.length; i++) {
let q = this.queue[i];
if (q.event !== event)
continue;

this.queue.splice(i, 1);
i--;

q.args.splice(0, 0, event);

this.emit.apply(this, q.args);
}

return callback;
},

off: function (event, callback) {
let list = this.events[event] || [];
let lLen = list.length;
@@ -34,18 +22,13 @@ module.exports = {
if (lLen === 0)
delete this.events[event];
},

emit: function (event) {
let args = [].slice.call(arguments, 1);

let list = this.events[event];
if (!list) {
this.queue.push({
event: event,
args: args
});

if (!list)
return;
}

let len = list.length;
for (let i = 0; i < len; i++) {


+ 1
- 1
src/server/package.json 查看文件

@@ -1,6 +1,6 @@
{
"name": "isleward_server",
"version": "0.6.1",
"version": "0.7.0",
"description": "isleward",
"dependencies": {
"axios": "^0.19.2",


+ 7
- 8
src/server/server.js 查看文件

@@ -94,23 +94,22 @@ module.exports = {
default: function (req, res) {
let root = req.url.split('/')[1];
let file = req.params[0];
file = file.replace('/' + root + '/', '');

const validModPatterns = ['.png', '/ui/', '/clientComponents/', '/audio/'];
const validRequest = (
root !== 'server' ||
(
file.includes('mods/') &&
(
file.includes('.png') ||
file.includes('/ui/')
)
validModPatterns.some(v => file.includes(v))
)
);
if (!validRequest)
return null;
res.sendFile(file, {
root: '../' + root
});


+ 1
- 2
src/server/world/map.js 查看文件

@@ -256,8 +256,7 @@ module.exports = {
continue;

let data = layer.data || layer.objects;
let firstItem = data[0];
if (firstItem && firstItem.has('width')) {
if (layer.objects) {
let info = {
map: this.name,
layer: layerName,


正在加载...
取消
保存