Master See merge request Isleward/isleward!456tags/v0.4.3
@@ -1,59 +1,49 @@ | |||
@white: rgb(242, 245, 245); | |||
@black: #2d2136; | |||
@brown: rgb(107, 79, 76); | |||
@darkGray: rgb(55, 48, 65); | |||
@gray: rgb(60, 63, 76); | |||
@lightGray: rgb(146, 147, 152); | |||
@yellow: #ffeb38; | |||
@purple: #a24eff; | |||
@orange: #ff6942; | |||
@red: #d43346; | |||
@blue: #3fa7dd; | |||
@green: #80f643; | |||
@greenA: #80f643; | |||
@greenB: #4ac441; | |||
@greenC: #386646; | |||
@blackA: #505360; | |||
@blackB: #3c3f4c; | |||
@blackC: #373041; | |||
@blackD: #312136; | |||
@brownC: #b15a30; | |||
@brownD: #763b3b; | |||
@redA: #ff4252; | |||
@redB: #d43346; | |||
@redC: #a82841; | |||
@redD: #802343; | |||
@orangeA: #ff6942; | |||
@orangeB: #db5538; | |||
@orangeC: #b34b3a; | |||
@orangeD: #953f36; | |||
@yellowB: #faac45; | |||
@yellowC: #d07840; | |||
@blueA: #48edff; | |||
@blueB: #3fa7dd; | |||
@blueC: #3a71ba; | |||
@blueD: #42548d; | |||
@tealB: #51fc9a; | |||
@tealC: #44cb95; | |||
@purpleA: #a24eff; | |||
@purpleB: #7a3ad3; | |||
@purpleC: #533399; | |||
@purpleD: #393268; | |||
@pinkA: #fc66f7; | |||
@pinkB: #de43ae; | |||
@grayB: #c0c3cf; | |||
@grayC: #929398; | |||
@grayD: #69696e; | |||
@qualityCommon: @white; | |||
@qualityMagic: @greenB; | |||
@qualityRare: @blueB; | |||
@qualityEpic: @purpleA; | |||
@qualityLegendary: @orangeA; |
@@ -38,10 +38,12 @@ body { | |||
} | |||
.mobile .right { | |||
top: 0px; | |||
right: 0px; | |||
.mobile.ui-container > .right { | |||
top: 10px; | |||
right: 158px; | |||
z-index: 2; | |||
display: flex; | |||
flex-direction: row-reverse; | |||
} | |||
* { | |||
@@ -122,7 +124,7 @@ body { | |||
.btnClose { | |||
position: absolute; | |||
background-color: @blackA; | |||
color: @orange; | |||
color: @orangeA; | |||
cursor: pointer; | |||
height: 31px; | |||
width: 31px; | |||
@@ -159,23 +161,23 @@ body { | |||
} | |||
.q0 { | |||
color: @white; | |||
color: @qualityCommon; | |||
} | |||
.q1 { | |||
color: @greenB; | |||
color: @qualityMagic; | |||
} | |||
.q2 { | |||
color: @blueB; | |||
color: @qualityRare; | |||
} | |||
.q3 { | |||
color: @purpleA; | |||
color: @qualityEpic; | |||
} | |||
.q4 { | |||
color: @orange; | |||
color: @qualityLegendary; | |||
} | |||
.color-red { | |||
@@ -1,9 +1,64 @@ | |||
define([ | |||
'js/system/browserStorage' | |||
], function ( | |||
browserStorage | |||
) { | |||
return { | |||
showNames: false | |||
const config = { | |||
showNames: true, | |||
showQuests: 'on', | |||
showEvents: true, | |||
playAudio: true, | |||
qualityIndicators: 'off', | |||
unusableIndicators: 'off', | |||
rememberChatChannel: true | |||
}; | |||
const valueChains = { | |||
showQuests: ['on', 'minimal', 'off'], | |||
qualityIndicators: ['border', 'bottom', 'background', 'off'], | |||
unusableIndicators: ['off', 'border', 'top', 'background'] | |||
}; | |||
const getNextValue = key => { | |||
const currentValue = config[key]; | |||
const chain = valueChains[key]; | |||
const currentIndex = chain.indexOf(currentValue); | |||
const nextValue = chain[(currentIndex + 1) % chain.length]; | |||
return nextValue; | |||
}; | |||
const getKeyName = key => { | |||
return `opt_${key.toLowerCase()}`; | |||
}; | |||
config.set = (key, value) => { | |||
config[key] = value; | |||
browserStorage.set(getKeyName(key), config[key]); | |||
}; | |||
config.toggle = key => { | |||
if (valueChains[key]) | |||
config[key] = getNextValue(key); | |||
else | |||
config[key] = !config[key]; | |||
browserStorage.set(getKeyName(key), config[key]); | |||
}; | |||
const loadValue = key => { | |||
const currentValue = browserStorage.get(getKeyName(key)); | |||
if (currentValue === '{unset}') | |||
return; | |||
const morphedValue = valueChains[key] ? currentValue : (currentValue === 'true'); | |||
config[key] = morphedValue; | |||
}; | |||
Object.keys(config).forEach(key => loadValue(key) ); | |||
return config; | |||
}); |
@@ -8,6 +8,8 @@ define([ | |||
'js/input', | |||
'js/system/events', | |||
'js/resources', | |||
'js/sound/sound', | |||
'js/system/globals', | |||
'ui/templates/online/online', | |||
'ui/templates/tooltips/tooltips' | |||
], function ( | |||
@@ -19,7 +21,9 @@ define([ | |||
numbers, | |||
input, | |||
events, | |||
resources | |||
resources, | |||
sound, | |||
globals | |||
) { | |||
return { | |||
hasFocus: true, | |||
@@ -34,13 +38,14 @@ define([ | |||
onClientReady: function () { | |||
client.request({ | |||
module: 'clientConfig', | |||
method: 'getResourcesList', | |||
callback: this.onGetResourceList.bind(this) | |||
method: 'getClientConfig', | |||
callback: this.onGetClientConfig.bind(this) | |||
}); | |||
}, | |||
onGetResourceList: function (list) { | |||
resources.init(list); | |||
onGetClientConfig: function (config) { | |||
globals.clientConfig = config; | |||
resources.init(config.resourceList); | |||
events.on('onResourcesLoaded', this.start.bind(this)); | |||
}, | |||
@@ -51,13 +56,15 @@ define([ | |||
$(window).on('contextmenu', this.onContextMenu.bind(this)); | |||
sound.init(); | |||
objects.init(); | |||
renderer.init(); | |||
input.init(); | |||
numbers.init(); | |||
uiFactory.init(); | |||
uiFactory.init(null, globals.clientConfig.uiList); | |||
uiFactory.build('login', 'body'); | |||
this.update(); | |||
@@ -0,0 +1,14 @@ | |||
define([ | |||
'js/system/browserStorage', | |||
'js/system/globals' | |||
], function ( | |||
browserStorage, | |||
globals | |||
) { | |||
return () => { | |||
const acceptedVersion = browserStorage.get('tos_accepted_version'); | |||
const currentVersion = globals.clientConfig.tos.version; | |||
return (acceptedVersion === currentVersion); | |||
}; | |||
}); |
@@ -12,21 +12,14 @@ define([ | |||
config | |||
) { | |||
return { | |||
showNames: true, | |||
objects: [], | |||
init: function () { | |||
events.on('onKeyDown', this.onKeyDown.bind(this)); | |||
events.on('onGetObject', this.onGetObject.bind(this)); | |||
events.on('onRezone', this.onRezone.bind(this)); | |||
events.on('onChangeHoverTile', this.getLocation.bind(this)); | |||
events.on('onTilesVisible', this.onTilesVisible.bind(this)); | |||
//Get saved value for showNames, or use the value set above | |||
let showNames = window.localStorage.getItem('iwd_opt_shownames'); | |||
this.showNames = showNames ? (showNames === 'true') : this.showNames; | |||
config.showNames = this.showNames; | |||
events.on('onToggleNameplates', this.onToggleNameplates.bind(this)); | |||
}, | |||
getLocation: function (x, y) { | |||
@@ -282,7 +275,7 @@ define([ | |||
if (template.hidden !== null) { | |||
sprite.visible = !template.hidden; | |||
if (obj.nameSprite) | |||
obj.nameSprite.visible = this.showNames; | |||
obj.nameSprite.visible = config.showNames; | |||
if ((obj.stats) && (obj.stats.hpSprite)) { | |||
obj.stats.hpSprite.visible = !template.hidden; | |||
obj.stats.hpSpriteInner.visible = !template.hidden; | |||
@@ -305,7 +298,7 @@ define([ | |||
x: (obj.x * scale) + (scale / 2), | |||
y: (obj.y * scale) + scale | |||
}); | |||
obj.nameSprite.visible = this.showNames; | |||
obj.nameSprite.visible = config.showNames; | |||
} | |||
if (obj.sprite) { | |||
@@ -346,29 +339,6 @@ define([ | |||
} | |||
}, | |||
onKeyDown: function (key) { | |||
if (key === 'v') { | |||
this.showNames = !this.showNames; | |||
//Set new value in localStorage for showNames | |||
window.localStorage.setItem('iwd_opt_shownames', this.showNames); | |||
config.showNames = this.showNames; | |||
let showNames = this.showNames; | |||
let objects = this.objects; | |||
let oLen = objects.length; | |||
for (let i = 0; i < oLen; i++) { | |||
let obj = objects[i]; | |||
let ns = obj.nameSprite; | |||
if ((!ns) || (obj.dead) || ((obj.sprite) && (!obj.sprite.visible))) | |||
continue; | |||
ns.visible = showNames; | |||
} | |||
} | |||
}, | |||
onTilesVisible: function (tiles, visible) { | |||
let objects = this.objects; | |||
let oLen = objects.length; | |||
@@ -383,6 +353,19 @@ define([ | |||
o.setVisible(visible); | |||
} | |||
}, | |||
onToggleNameplates: function (show) { | |||
let objects = this.objects; | |||
let oLen = objects.length; | |||
for (let i = 0; i < oLen; i++) { | |||
let obj = objects[i]; | |||
let ns = obj.nameSprite; | |||
if ((!ns) || (obj.dead) || ((obj.sprite) && (!obj.sprite.visible))) | |||
continue; | |||
ns.visible = show; | |||
} | |||
} | |||
}; | |||
}); |
@@ -1,15 +1,23 @@ | |||
define([ | |||
'howler', | |||
'js/misc/physics' | |||
'js/misc/physics', | |||
'js/system/events', | |||
'js/config' | |||
], function ( | |||
howler, | |||
physics | |||
physics, | |||
events, | |||
config | |||
) { | |||
return { | |||
sounds: [], | |||
init: function (zone) { | |||
muted: false, | |||
init: function () { | |||
events.on('onToggleAudio', this.onToggleAudio.bind(this)); | |||
this.onToggleAudio(config.playAudio); | |||
}, | |||
unload: function (zoneId) { | |||
@@ -24,7 +32,7 @@ define([ | |||
}, | |||
update: function (x, y) { | |||
this.sounds.forEach(function (s) { | |||
this.sounds.forEach(s => { | |||
let volume; | |||
if (!s.w) { | |||
@@ -67,9 +75,13 @@ define([ | |||
loop: true, | |||
volume: 0 | |||
}); | |||
if (this.muted) | |||
s.sound.mute(true); | |||
} | |||
s.sound.volume(volume * s.volume); | |||
if (!this.muted) | |||
s.sound.volume(volume * s.volume); | |||
}); | |||
}, | |||
@@ -96,6 +108,24 @@ define([ | |||
}; | |||
this.sounds.push(sound); | |||
}, | |||
onToggleAudio: function (isAudioOn) { | |||
this.muted = !isAudioOn; | |||
this.sounds.forEach(s => { | |||
if (!s.sound) | |||
return; | |||
s.sound.mute(this.muted); | |||
}); | |||
if (!window.player) | |||
return; | |||
const { player: { x, y } } = window; | |||
this.update(x, y); | |||
} | |||
}; | |||
}); |
@@ -0,0 +1,25 @@ | |||
define([ | |||
'js/system/browserStorage' | |||
], function ( | |||
browserStorage | |||
) { | |||
const getEntryName = key => { | |||
return `iwd_${key.toLowerCase()}`; | |||
}; | |||
return { | |||
get: key => { | |||
const keyName = getEntryName(key); | |||
const { [keyName]: value = '{unset}' } = localStorage; | |||
return value; | |||
}, | |||
set: (key, value) => { | |||
const keyName = getEntryName(key); | |||
localStorage.setItem(keyName, value); | |||
} | |||
}; | |||
}); |
@@ -1,18 +1,62 @@ | |||
define([ | |||
'ui/uiBase', | |||
'js/system/events' | |||
'js/system/events', | |||
'js/system/globals', | |||
'js/misc/tosAcceptanceValid' | |||
], function ( | |||
uiBase, | |||
events | |||
events, | |||
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', | |||
'smithing', | |||
'talk', | |||
'trade', | |||
'overlay', | |||
'death', | |||
'leaderboard', | |||
'reputation', | |||
'mail', | |||
'wardrobe', | |||
'passives', | |||
'workbench', | |||
'middleHud', | |||
'options' | |||
]; | |||
return { | |||
uis: [], | |||
root: '', | |||
init: function (root) { | |||
init: function (root, uiList = []) { | |||
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)); | |||
@@ -21,48 +65,23 @@ define([ | |||
onEnterGame: function () { | |||
events.clearQueue(); | |||
[ | |||
'inventory', | |||
'equipment', | |||
'hud', | |||
'target', | |||
'menu', | |||
'spells', | |||
'messages', | |||
'online', | |||
'options', | |||
'context', | |||
'party', | |||
'help', | |||
'dialogue', | |||
'buffs', | |||
'tooltips', | |||
'tooltipInfo', | |||
'tooltipItem', | |||
'announcements', | |||
'quests', | |||
'events', | |||
'progressBar', | |||
'stash', | |||
'smithing', | |||
'talk', | |||
'trade', | |||
'overlay', | |||
'death', | |||
'leaderboard', | |||
'reputation', | |||
'mail', | |||
'wardrobe', | |||
'passives', | |||
'workbench', | |||
'middleHud' | |||
].forEach(function (u) { | |||
this.build(u); | |||
startupUis.forEach(function (u) { | |||
if (u.path) | |||
this.buildModUi(u); | |||
else | |||
this.build(u); | |||
}, this); | |||
}, | |||
buildModUi: function (config) { | |||
const type = config.path.split('/').pop(); | |||
this.build(type, { | |||
path: config.path | |||
}); | |||
}, | |||
build: function (type, options) { | |||
//Don't make doubles? | |||
let className = 'ui' + type[0].toUpperCase() + type.substr(1); | |||
let el = $('.' + className); | |||
if (el.length > 0) | |||
@@ -72,7 +91,13 @@ define([ | |||
}, | |||
getTemplate: function (type, options) { | |||
require([this.root + 'ui/templates/' + type + '/' + type], this.onGetTemplate.bind(this, options)); | |||
let path = null; | |||
if (options && options.path) | |||
path = options.path + `\\${type}.js`; | |||
else | |||
path = this.root + 'ui/templates/' + type + '/' + type; | |||
require([path], this.onGetTemplate.bind(this, options)); | |||
}, | |||
onGetTemplate: function (options, template) { | |||
@@ -100,13 +125,14 @@ define([ | |||
onUiKeyDown: function (keyEvent) { | |||
if (keyEvent.key === 'esc') { | |||
this.uis.forEach(function (u) { | |||
this.uis.forEach(u => { | |||
if (!u.modal || !u.shown) | |||
return; | |||
keyEvent.consumed = true; | |||
u.hide(); | |||
}); | |||
$('.uiOverlay').hide(); | |||
events.emit('onHideContextMenu'); | |||
} else if (['o', 'j', 'h', 'i'].indexOf(keyEvent.key) > -1) | |||
@@ -131,7 +157,13 @@ define([ | |||
}, | |||
afterPreload: function () { | |||
this.build('characters', {}); | |||
if (!globals.clientConfig.tos.required || tosAcceptanceValid()) { | |||
this.build('characters'); | |||
return; | |||
} | |||
this.build('terms'); | |||
}, | |||
update: function () { | |||
@@ -0,0 +1,81 @@ | |||
const tplItem = ` | |||
<div class="item"> | |||
<div class="icon"></div> | |||
<div class="quantity"></div> | |||
</div> | |||
`; | |||
define([ | |||
], function ( | |||
) { | |||
return (container, item) => { | |||
let itemEl = null; | |||
if (!item) { | |||
itemEl = $(tplItem) | |||
.appendTo(container) | |||
.addClass('empty'); | |||
return itemEl; | |||
} | |||
itemEl = $(tplItem).appendTo(container); | |||
let size = 64; | |||
let offset = 0; | |||
if (item.type === 'skin') { | |||
offset = 4; | |||
size = 8; | |||
} | |||
const imgX = (-item.sprite[0] * size) + offset; | |||
const imgY = (-item.sprite[1] * size) + offset; | |||
let spritesheet = item.spritesheet || '../../../images/items.png'; | |||
if (!item.spritesheet) { | |||
if (item.material) | |||
spritesheet = '../../../images/materials.png'; | |||
else if (item.quest) | |||
spritesheet = '../../../images/questItems.png'; | |||
else if (item.type === 'consumable') | |||
spritesheet = '../../../images/consumables.png'; | |||
else if (item.type === 'skin') | |||
spritesheet = '../../../images/characters.png'; | |||
} | |||
itemEl | |||
.find('.icon') | |||
.css('background', `url(${spritesheet}) ${imgX}px ${imgY}px`); | |||
if (item.quantity > 1 || item.eq || item.active || item.has('quickSlot')) { | |||
let elQuantity = itemEl.find('.quantity'); | |||
let txtQuantity = item.quantity; | |||
if (!txtQuantity) | |||
txtQuantity = item.has('quickSlot') ? 'QS' : 'EQ'; | |||
elQuantity.html(txtQuantity); | |||
//If the item doesn't have a quantity and we reach this point | |||
//it must mean that it's active, EQd or QSd | |||
if (!item.quantity) | |||
itemEl.addClass('eq'); | |||
} else if (item.isNew) { | |||
itemEl.addClass('new'); | |||
itemEl.find('.quantity').html('NEW'); | |||
} | |||
if (item.slot) { | |||
const equipErrors = window.player.inventory.equipItemErrors(item); | |||
if (equipErrors.length) | |||
itemEl.addClass('no-equip'); | |||
} | |||
if (item.has('quality')) | |||
itemEl.addClass(`quality-${item.quality}`); | |||
return itemEl; | |||
}; | |||
}); |
@@ -28,6 +28,15 @@ define([ | |||
.on('mouseleave', this.onDeleteReset.bind(this)); | |||
this.getCharacters(); | |||
this.onEvent('onKeyDown', this.onKeyDown.bind(this)); | |||
}, | |||
onKeyDown: function (key) { | |||
if (key !== 'enter' || this.el.hasClass('disabled')) | |||
return; | |||
this.onPlayClick(); | |||
}, | |||
onPlayClick: function () { | |||
@@ -48,13 +57,13 @@ define([ | |||
}, | |||
onPlay: function () { | |||
this.el.removeClass('disabled'); | |||
this.el.remove(); | |||
this.destroy(); | |||
events.emit('onEnterGame'); | |||
}, | |||
onNewClick: function () { | |||
uiFactory.build('createCharacter', {}); | |||
this.el.remove(); | |||
this.destroy(); | |||
}, | |||
getCharacters: function () { | |||
@@ -111,7 +111,7 @@ define([ | |||
back: function () { | |||
this.clear(); | |||
this.el.remove(); | |||
this.destroy(); | |||
uiFactory.build('characters', {}); | |||
}, | |||
@@ -137,7 +137,7 @@ define([ | |||
if (!result) { | |||
this.clear(); | |||
this.el.remove(); | |||
this.destroy(); | |||
events.emit('onEnterGame'); | |||
} else | |||
this.el.find('.message').html(result); | |||
@@ -71,11 +71,11 @@ | |||
background-color: @grayD; | |||
&:first-child { | |||
margin-right: 15px; | |||
margin-right: 10px; | |||
} | |||
&:last-child { | |||
margin-left: 15px; | |||
margin-left: 10px; | |||
} | |||
&:hover { | |||
@@ -236,7 +236,7 @@ | |||
width: 45%; | |||
.skinBox { | |||
height: 70px; | |||
height: 35px; | |||
} | |||
} | |||
@@ -2,7 +2,6 @@ | |||
.uiDeath { | |||
display: none; | |||
width: 400px; | |||
background-color: @gray; | |||
border: 4px solid @lightGray; | |||
@@ -14,8 +13,9 @@ | |||
.inner { | |||
display: inline-block; | |||
color: @orange; | |||
color: @orangeA; | |||
} | |||
} | |||
.penalty { | |||
@@ -25,17 +25,18 @@ | |||
.btn { | |||
color: @white; | |||
width: 100%; | |||
height: 32px; | |||
background-color: @blue; | |||
margin: 16px 0px; | |||
padding-top: 8px; | |||
cursor: pointer; | |||
&:hover { | |||
width: 100%; | |||
height: 32px; | |||
background-color: @blue; | |||
margin: 16px 0px; | |||
padding-top: 8px; | |||
cursor: pointer; | |||
&:hover { | |||
background-color: lighten(@blue, 15%); | |||
color: @black; | |||
} | |||
} | |||
} | |||
&.permadeath { | |||
@@ -47,12 +48,16 @@ | |||
.btn-logout { | |||
display: block; | |||
} | |||
} | |||
} | |||
.buttons { | |||
.btn-logout { | |||
display: none; | |||
} | |||
} | |||
} |
@@ -279,6 +279,7 @@ | |||
background-color: @blackC; | |||
padding: 0px; | |||
z-index: 2; | |||
overflow-y: auto; | |||
.slot { | |||
float: left; | |||
@@ -3,13 +3,15 @@ define([ | |||
'js/system/events', | |||
'html!ui/templates/events/template', | |||
'html!ui/templates/events/templateEvent', | |||
'css!ui/templates/events/styles' | |||
'css!ui/templates/events/styles', | |||
'js/config' | |||
], function ( | |||
client, | |||
events, | |||
tpl, | |||
templateEvent, | |||
styles | |||
styles, | |||
config | |||
) { | |||
return { | |||
tpl: tpl, | |||
@@ -30,6 +32,9 @@ define([ | |||
this.onEvent('onRemoveEvent', this.onRemoveEvent.bind(this)); | |||
this.onEvent('onUpdateEvent', this.onUpdateEvent.bind(this)); | |||
this.onEvent('onCompleteEvent', this.onCompleteEvent.bind(this)); | |||
this.onEvent('onToggleEventsVisibility', this.onToggleEventsVisibility.bind(this)); | |||
this.onToggleEventsVisibility(config.showEvents); | |||
}, | |||
onRezone: function () { | |||
@@ -112,6 +117,15 @@ define([ | |||
toggleButtons: function (e) { | |||
this.el.toggleClass('active'); | |||
e.stopPropagation(); | |||
}, | |||
onToggleEventsVisibility: function (active) { | |||
this.shown = active; | |||
if (this.shown) | |||
this.show(); | |||
else | |||
this.hide(); | |||
} | |||
}; | |||
}); |
@@ -52,7 +52,7 @@ | |||
} | |||
.btnClose { | |||
.btnCollapse { | |||
display: none; | |||
} | |||
@@ -60,11 +60,9 @@ | |||
.mobile .uiEvents { | |||
background-color: fade(@darkGray, 90%); | |||
margin-top: 0px; | |||
&:not(.active) { | |||
position: absolute; | |||
top: 0px; | |||
right: 232px; | |||
width: @btnSize; | |||
height: @btnSize; | |||
@@ -95,11 +93,11 @@ | |||
display: block; | |||
position: absolute; | |||
right: calc(100% + 10px); | |||
top: 50%; | |||
transform: translateY(-50%); | |||
top: 0px; | |||
background-color: fade(@darkGray, 90%); | |||
width: @btnSize; | |||
height: @btnSize; | |||
padding-top: 0px; | |||
.icon { | |||
width: 100%; | |||
@@ -4,14 +4,18 @@ define([ | |||
'html!ui/templates/inventory/template', | |||
'css!ui/templates/inventory/styles', | |||
'html!ui/templates/inventory/templateItem', | |||
'js/input' | |||
'js/input', | |||
'js/config', | |||
'ui/shared/renderItem' | |||
], function ( | |||
events, | |||
client, | |||
template, | |||
styles, | |||
tplItem, | |||
input | |||
input, | |||
config, | |||
renderItem | |||
) { | |||
return { | |||
tpl: template, | |||
@@ -33,6 +37,11 @@ define([ | |||
this.onEvent('onGetItems', this.onGetItems.bind(this)); | |||
this.onEvent('onDestroyItems', this.onDestroyItems.bind(this)); | |||
this.onEvent('onShowInventory', this.toggle.bind(this)); | |||
this.onEvent('onToggleQualityIndicators', this.onToggleQualityIndicators.bind(this)); | |||
this.onToggleQualityIndicators(config.qualityIndicators); | |||
this.onEvent('onToggleUnusableIndicators', this.onToggleUnusableIndicators.bind(this)); | |||
this.onToggleUnusableIndicators(config.unusableIndicators); | |||
this.onEvent('onKeyDown', this.onKeyDown.bind(this)); | |||
this.onEvent('onKeyUp', this.onKeyUp.bind(this)); | |||
@@ -71,8 +80,7 @@ define([ | |||
let item = items.find(f => (f.pos !== null && f.pos === i)); | |||
if (!item) { | |||
itemEl = $(tplItem) | |||
.appendTo(container); | |||
itemEl = renderItem(container, null); | |||
itemEl | |||
.on('mouseup', this.onMouseDown.bind(this, null, null, false)) | |||
@@ -85,21 +93,7 @@ define([ | |||
} else | |||
rendered.push(item); | |||
let imgX = -item.sprite[0] * 64; | |||
let imgY = -item.sprite[1] * 64; | |||
itemEl = $(tplItem) | |||
.appendTo(container); | |||
let spritesheet = item.spritesheet || '../../../images/items.png'; | |||
if (!item.spritesheet) { | |||
if (item.material) | |||
spritesheet = '../../../images/materials.png'; | |||
else if (item.quest) | |||
spritesheet = '../../../images/questItems.png'; | |||
else if (item.type === 'consumable') | |||
spritesheet = '../../../images/consumables.png'; | |||
} | |||
itemEl = renderItem(container, item); | |||
let clickHandler = this.onMouseDown.bind(this, itemEl, item, true); | |||
let moveHandler = this.onHover.bind(this, itemEl, item); | |||
@@ -116,28 +110,26 @@ define([ | |||
.on('mousemove', moveHandler) | |||
.on('mouseleave', this.hideTooltip.bind(this, itemEl, item)) | |||
.find('.icon') | |||
.css('background', 'url(' + spritesheet + ') ' + imgX + 'px ' + imgY + 'px') | |||
.on('contextmenu', this.showContext.bind(this, item)); | |||
if (item.quantity > 1 || item.eq || item.active || item.has('quickSlot')) { | |||
let elQuantity = itemEl.find('.quantity'); | |||
let txtQuantity = item.quantity; | |||
if (!txtQuantity) | |||
txtQuantity = item.has('quickSlot') ? 'QS' : 'EQ'; | |||
elQuantity.html(txtQuantity); | |||
//If the item doesn't have a quantity and we reach this point | |||
//it must mean that it's active, EQd or QSd | |||
if (!item.quantity) | |||
itemEl.addClass('eq'); | |||
} else if (item.isNew) { | |||
itemEl.addClass('new'); | |||
itemEl.find('.quantity').html('NEW'); | |||
} | |||
} | |||
}, | |||
onToggleQualityIndicators: function (state) { | |||
const className = `quality-${state.toLowerCase()}`; | |||
$('.ui-container') | |||
.removeClass('quality-off quality-bottom quality-border quality-background') | |||
.addClass(className); | |||
}, | |||
onToggleUnusableIndicators: function (state) { | |||
const className = `unusable-${state.toLowerCase()}`; | |||
$('.ui-container') | |||
.removeClass('unusable-off unusable-border unusable-top unusable-background') | |||
.addClass(className); | |||
}, | |||
onClick: function (item) { | |||
let msg = { | |||
item: item, | |||
@@ -324,56 +316,56 @@ define([ | |||
if (item.active) | |||
menuItems.activate.text = 'deactivate'; | |||
let config = []; | |||
let ctxConfig = []; | |||
if (item.ability) | |||
config.push(menuItems.learn); | |||
ctxConfig.push(menuItems.learn); | |||
else if (item.type === 'mtx') | |||
config.push(menuItems.activate); | |||
ctxConfig.push(menuItems.activate); | |||
else if (item.type === 'toy' || item.type === 'consumable' || item.useText || item.type === 'recipe') { | |||
if (item.useText) | |||
menuItems.use.text = item.useText; | |||
config.push(menuItems.use); | |||
ctxConfig.push(menuItems.use); | |||
if (!item.has('quickSlot')) | |||
config.push(menuItems.quickSlot); | |||
ctxConfig.push(menuItems.quickSlot); | |||
} else if (item.slot) { | |||
config.push(menuItems.equip); | |||
ctxConfig.push(menuItems.equip); | |||
if (!item.eq) | |||
config.push(menuItems.divider); | |||
ctxConfig.push(menuItems.divider); | |||
if (!item.eq) { | |||
config.push(menuItems.augment); | |||
config.push(menuItems.divider); | |||
ctxConfig.push(menuItems.augment); | |||
ctxConfig.push(menuItems.divider); | |||
} | |||
} | |||
if ((!item.eq) && (!item.active)) { | |||
if (!item.quest) { | |||
if ((window.player.stash.active) && (!item.noStash)) | |||
config.push(menuItems.stash); | |||
ctxConfig.push(menuItems.stash); | |||
if (!item.noDrop) | |||
config.push(menuItems.drop); | |||
ctxConfig.push(menuItems.drop); | |||
if ((!item.material) && (!item.noSalvage)) | |||
config.push(menuItems.salvage); | |||
ctxConfig.push(menuItems.salvage); | |||
} | |||
if (!item.noDestroy) | |||
config.push(menuItems.destroy); | |||
ctxConfig.push(menuItems.destroy); | |||
} | |||
if (item.quantity > 1 && !item.quest) | |||
config.push(menuItems.split); | |||
ctxConfig.push(menuItems.split); | |||
//if ((!item.noDrop) && (!item.quest)) | |||
// config.push(menuItems.mail); | |||
if ((!item.noDrop) && (!item.quest)) | |||
ctxConfig.push(menuItems.mail); | |||
if (isMobile) | |||
this.hideTooltip(null, this.hoverItem); | |||
if (config.length > 0) | |||
events.emit('onContextMenu', config, e); | |||
if (ctxConfig.length > 0) | |||
events.emit('onContextMenu', ctxConfig, e); | |||
e.preventDefault(); | |||
return false; | |||
@@ -1,6 +1,7 @@ | |||
@import "../../../css/colors.less"; | |||
.uiInventory, .uiStash { | |||
.uiInventory, | |||
.uiStash { | |||
display: none; | |||
z-index: 2; | |||
width: 818px; | |||
@@ -18,8 +19,9 @@ | |||
padding-top: 8px; | |||
margin: auto; | |||
} | |||
} | |||
position: relative; | |||
.grid { | |||
@@ -52,20 +54,18 @@ | |||
background-color: transparent; | |||
.icon { | |||
filter: | |||
brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
-moz-filter: | |||
brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
filter: brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
-moz-filter: brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
} | |||
} | |||
.quantity { | |||
@@ -73,18 +73,14 @@ | |||
bottom: 3px; | |||
position: absolute; | |||
color: @white; | |||
filter: | |||
drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
-moz-filter: | |||
drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
-moz-filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
} | |||
.icon { | |||
@@ -99,30 +95,34 @@ | |||
.quantity { | |||
color: @yellow; | |||
} | |||
} | |||
&.new { | |||
.quantity { | |||
color: @green; | |||
} | |||
} | |||
&:hover { | |||
.icon { | |||
filter: brightness(160%); | |||
-moz-filter: brightness(160%); | |||
} | |||
} | |||
} | |||
} | |||
.menu-bar { | |||
background-color: @blackB; | |||
width: 100%; | |||
height: 41px; | |||
padding: 5px 0px; | |||
clear: both; | |||
width: 100%; | |||
height: 41px; | |||
padding: 5px 0px; | |||
clear: both; | |||
.btn { | |||
background-color: @blueC; | |||
@@ -136,7 +136,9 @@ | |||
&.btnSortInv { | |||
float: left; | |||
} | |||
} | |||
} | |||
.split-box { | |||
@@ -168,6 +170,7 @@ | |||
padding-top: 8px; | |||
margin: auto; | |||
} | |||
} | |||
.bottom { | |||
@@ -185,7 +188,9 @@ | |||
border: none; | |||
} | |||
input, .textbox, input:-webkit-autofill { | |||
input, | |||
.textbox, | |||
input:-webkit-autofill { | |||
color: @white; | |||
-webkit-text-fill-color: @white; | |||
background-color: @blackC; | |||
@@ -210,14 +215,148 @@ | |||
&:hover { | |||
background-color: @blackA; | |||
} | |||
} | |||
&.btnSplit { | |||
margin-top: 45px; | |||
clear: both; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.ui-container.quality-border .grid .item:not(.empty), | |||
.ui-container.quality-bottom .grid .item:not(.empty) { | |||
&:before { | |||
content: ''; | |||
position: absolute; | |||
top: 0px; | |||
left: 0px; | |||
width: 100%; | |||
height: 100%; | |||
box-sizing: border-box; | |||
border-width: 5px; | |||
border-style: solid; | |||
} | |||
&.quality-0:before { | |||
border: none; | |||
} | |||
&.quality-1:before { | |||
border-color: @greenC; | |||
} | |||
&.quality-2:before { | |||
border-color: @blueD; | |||
} | |||
&.quality-3:before { | |||
border-color: @purpleC; | |||
} | |||
&.quality-4:before { | |||
border-color: @orangeD; | |||
} | |||
} | |||
.ui-container.quality-bottom .grid .item:not(.empty) { | |||
&:before { | |||
border-width: 0px 0px 5px 0px; | |||
} | |||
} | |||
.ui-container.quality-background .grid .item:not(.empty),&.unusable-background .grid .item.no-equip { | |||
&:before { | |||
content: ''; | |||
position: absolute; | |||
top: 0px; | |||
left: 0px; | |||
width: 100%; | |||
height: 100%; | |||
} | |||
.icon { | |||
filter: brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
&:hover { | |||
filter: brightness(160%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
} | |||
} | |||
&.quality-1:before { | |||
background-color: @greenC; | |||
} | |||
&.quality-2:before { | |||
background-color: @blueD; | |||
} | |||
&.quality-3:before { | |||
background-color: @purpleC; | |||
} | |||
&.quality-4:before { | |||
background-color: @orangeD; | |||
} | |||
&.no-equip:before { | |||
background-color: @redD; | |||
} | |||
} | |||
.ui-container.unusable-border .grid .item.no-equip, | |||
.ui-container.unusable-top .grid .item.no-equip { | |||
&:after { | |||
content: ''; | |||
position: absolute; | |||
top: 0px; | |||
left: 0px; | |||
width: 100%; | |||
height: 100%; | |||
box-sizing: border-box; | |||
border-width: 5px; | |||
border-style: solid; | |||
border-color: @redD; | |||
pointer-events: none; | |||
} | |||
} | |||
.ui-container.unusable-top .grid .item.no-equip { | |||
&:after { | |||
border-width: 5px 0px 0px 0px; | |||
} | |||
} | |||
.ui-container.quality-border.unusable-border .grid .item.no-equip:not(.quality-0), | |||
.ui-container.quality-border.unusable-top .grid .item.no-equip:not(.quality-0) { | |||
&:after { | |||
top: 5px; | |||
left: 5px; | |||
width: calc(100% - 10px); | |||
height: calc(100% - 10px); | |||
} | |||
} |
@@ -73,7 +73,7 @@ define([ | |||
uiFactory.preload(); | |||
$('.uiLoginExtra').remove(); | |||
this.el.remove(); | |||
this.destroy(); | |||
} else | |||
this.el.find('.message').html(res); | |||
}, | |||
@@ -78,7 +78,7 @@ | |||
margin-top: 36px; | |||
float: left; | |||
text-align: center; | |||
color: @orange; | |||
color: @orangeA; | |||
filter: brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
@@ -11,11 +11,11 @@ | |||
</div> | |||
<div class="message"></div> | |||
</div> | |||
<div class="news" location="https://gitlab.com/Isleward/isleward/tags/v0.4.2">[ Latest Release Notes ]</div> | |||
<div class="news" location="https://gitlab.com/Isleward/isleward/tags/v0.4.3">[ 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.4.2">v0.4.2</div> | |||
<div class="version" location="https://gitlab.com/Isleward/isleward/tags/v0.4.3">v0.4.3</div> | |||
</div> |
@@ -0,0 +1,112 @@ | |||
define([ | |||
'js/system/events', | |||
'html!ui/templates/mainMenu/template', | |||
'css!ui/templates/mainMenu/styles', | |||
'js/rendering/renderer', | |||
'ui/factory', | |||
'js/objects/objects', | |||
'js/system/client', | |||
'js/sound/sound' | |||
], function ( | |||
events, | |||
template, | |||
styles, | |||
renderer, | |||
factory, | |||
objects, | |||
client, | |||
sound | |||
) { | |||
return { | |||
tpl: template, | |||
centered: true, | |||
modal: true, | |||
postRender: function () { | |||
this.onEvent('onToggleMainMenu', this.toggle.bind(this)); | |||
this.el.find('.btnOptions').on('click', this.handler.bind(this, 'onToggleOptions')); | |||
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)); | |||
this.el.find('.btnPatreon').on('click', this.patreon.bind(this)); | |||
this.el.find('.btnIssue').on('click', this.reportIssue.bind(this)); | |||
this.onEvent('onResize', this.onResize.bind(this)); | |||
}, | |||
handler: function (e) { | |||
if (isMobile) | |||
this.el.removeClass('active'); | |||
events.emit(e); | |||
return false; | |||
}, | |||
reportIssue: function () { | |||
window.open('https://gitlab.com/Isleward/isleward/issues/new', '_blank'); | |||
}, | |||
patreon: function () { | |||
window.open('https://patreon.com/bigbadwaffle', '_blank'); | |||
}, | |||
charSelect: function () { | |||
this.el.addClass('disabled'); | |||
client.request({ | |||
module: 'cons', | |||
method: 'unzone', | |||
callback: this.onCharSelect.bind(this) | |||
}); | |||
}, | |||
onCharSelect: function () { | |||
renderer.clean(); | |||
objects.onRezone(); | |||
renderer.buildTitleScreen(); | |||
sound.unload(); | |||
events.emit('onShowCharacterSelect'); | |||
$('[class^="ui"]:not(.ui-container)').toArray().forEach(el => { | |||
let ui = $(el).data('ui'); | |||
if (ui && ui.destroy) | |||
ui.destroy(); | |||
}); | |||
factory.build('characters', {}); | |||
}, | |||
onResize: function () { | |||
let isFullscreen = (window.innerHeight === screen.height); | |||
if (isFullscreen) | |||
this.el.find('.btnScreen').html('Windowed'); | |||
else | |||
this.el.find('.btnScreen').html('Fullscreen'); | |||
}, | |||
toggle: function () { | |||
this.onResize(); | |||
this.shown = !this.el.is(':visible'); | |||
if (this.shown) { | |||
this.show(); | |||
events.emit('onShowOverlay', this.el); | |||
} else { | |||
this.hide(); | |||
events.emit('onHideOverlay', this.el); | |||
} | |||
}, | |||
logOut: function () { | |||
window.location = window.location; | |||
}, | |||
onKeyDown: function (key) { | |||
if (key === 'esc') | |||
this.toggle(); | |||
} | |||
}; | |||
}); |
@@ -0,0 +1,49 @@ | |||
@import "../../../css/colors.less"; | |||
.uiMainMenu { | |||
display: none; | |||
width: 400px; | |||
background-color: @gray; | |||
border: 4px solid @lightGray; | |||
text-align: center; | |||
padding: 0px 16px; | |||
.btn { | |||
color: @white; | |||
width: 100%; | |||
height: 32px; | |||
background-color: @blue; | |||
margin: 16px 0px; | |||
padding-top: 8px; | |||
cursor: pointer; | |||
&.btnNames { | |||
display: none; | |||
} | |||
&:hover { | |||
background-color: lighten(@blue, 15%); | |||
color: @black; | |||
} | |||
} | |||
.btnIssue { | |||
background-color: @red; | |||
&:hover { | |||
background-color: lighten(@red, 15%); | |||
color: @black; | |||
} | |||
} | |||
} | |||
.mobile .uiMainMenu { | |||
.btnScreen { | |||
display: none; | |||
} | |||
.btnNames { | |||
display: block; | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
<div class="uiMainMenu"> | |||
<div class="btn btnContinue">Continue</div> | |||
<div class="btn btnOptions">Options</div> | |||
<div class="btn btnCharSelect">Character Select</div> | |||
<div class="btn btnLogOut">Log Out</div> | |||
<div class="btn btnPatreon">Pledge on Patreon</div> | |||
<div class="btn btnIssue">Report an Issue</div> | |||
</div> |
@@ -22,7 +22,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('.btnOptions').on('click', this.handler.bind(this, 'onToggleOptions')); | |||
this.find('.btnMainMenu').on('click', this.handler.bind(this, 'onToggleMainMenu')); | |||
this.find('.btnPassives').on('click', this.handler.bind(this, 'onShowPassives')); | |||
this.onEvent('onGetPassivePoints', this.onGetPassivePoints.bind(this)); | |||
@@ -95,7 +95,7 @@ | |||
background: url('../../../images/uiIcons.png') -320px 0px; | |||
} | |||
&.btnOptions .icon { | |||
&.btnMainMenu .icon { | |||
background: url('../../../images/uiIcons.png') 0px -64px; | |||
} | |||
} | |||
@@ -24,7 +24,7 @@ | |||
<div class="btnHelp"> | |||
<div class="icon"></div> | |||
</div> | |||
<div class="btnOptions"> | |||
<div class="btnMainMenu"> | |||
<div class="icon"></div> | |||
</div> | |||
<div class="btn btnCollapse"> | |||
@@ -4,14 +4,16 @@ define([ | |||
'html!ui/templates/messages/tplTab', | |||
'css!ui/templates/messages/styles', | |||
'js/input', | |||
'js/system/client' | |||
'js/system/client', | |||
'js/config' | |||
], function ( | |||
events, | |||
template, | |||
tplTab, | |||
styles, | |||
input, | |||
client | |||
client, | |||
config | |||
) { | |||
return { | |||
tpl: template, | |||
@@ -29,6 +31,8 @@ define([ | |||
blockedPlayers: [], | |||
lastChannel: null, | |||
postRender: function () { | |||
this.onEvent('onGetMessages', this.onGetMessages.bind(this)); | |||
this.onEvent('onDoWhisper', this.onDoWhisper.bind(this)); | |||
@@ -36,6 +40,7 @@ define([ | |||
this.onEvent('onLeaveChannel', this.onLeaveChannel.bind(this)); | |||
this.onEvent('onGetCustomChatChannels', this.onGetCustomChatChannels.bind(this)); | |||
this.onEvent('onGetBlockedPlayers', this.onGetBlockedPlayers.bind(this)); | |||
this.onEvent('onToggleLastChannel', this.onToggleLastChannel.bind(this)); | |||
this | |||
.find('.filter:not(.channel):not(.btn)') | |||
@@ -91,37 +96,57 @@ define([ | |||
let container = $('<div class="keyboard"></div>') | |||
.appendTo(this.el); | |||
let controls = ['|', 'caps', 'space', 'backspace', 'enter']; | |||
let keyboard = { | |||
0: '1234567890|qwertyuiop|asdfghjkl|zxcvbnm', | |||
1: '!@#$%^&*()|QWERTYUIOP|ASDFGHJKL|ZXCVBNM', | |||
2: '!@#$%^&*()| {}[]\\|`-=_+;\':"|~,./<>?' | |||
}[this.kbUpper].split('').concat(controls); | |||
keyboard | |||
.forEach(k => { | |||
if (k === '|') { | |||
$('<div class="newline"></div>') | |||
.appendTo(container); | |||
return; | |||
} | |||
0: 'qwertyuiop|asdfghjkl|zxcvbnm', | |||
1: 'QWERTYUIOP|ASDFGHJKL|ZXCVBNM', | |||
2: '1234567890|@#&*-+=()|_$"\';/' | |||
}[this.kbUpper].split(''); | |||
//Hacky: Insert control characters in correct positions | |||
//Backspace goes after 'm' | |||
if (this.kbUpper === 0) { | |||
keyboard.splice(keyboard.indexOf('z'), 0, 'caps'); | |||
keyboard.splice(keyboard.indexOf('m') + 1, 0, '<<'); | |||
} else if (this.kbUpper === 1) { | |||
keyboard.splice(keyboard.indexOf('Z'), 0, 'caps'); | |||
keyboard.splice(keyboard.indexOf('M') + 1, 0, '<<'); | |||
} else if (this.kbUpper === 2) | |||
keyboard.splice(keyboard.indexOf('/') + 1, 0, '<<'); | |||
keyboard.push(...['|', '123', ',', 'space', '.', 'send']); | |||
let row = 0; | |||
keyboard.forEach(k => { | |||
if (k === '|') { | |||
row++; | |||
const postGapCount = row === 4 ? 0 : row - 1; | |||
for (let i = 0; i < postGapCount; i++) | |||
$('<div class="gap" />').appendTo(container); | |||
$('<div class="newline" />').appendTo(container); | |||
const preGapCount = row === 3 ? 0 : row; | |||
for (let i = 0; i < preGapCount; i++) | |||
$('<div class="gap" />').appendTo(container); | |||
return; | |||
} | |||
let className = (k.match(/[a-z]/i) || k.length > 1) ? 'key' : 'key special'; | |||
if (k === ' ') { | |||
k = '.'; | |||
className = 'key hidden'; | |||
} | |||
let className = (k.length === 1) ? 'key' : 'key special'; | |||
if (k === ' ') { | |||
k = '.'; | |||
className = 'key hidden'; | |||
} | |||
className += ' ' + k; | |||
className += ' ' + k; | |||
let elKey = $(`<div class="${className}">${k}</div>`) | |||
.appendTo(container); | |||
let elKey = $(`<div class="${className}">${k}</div>`) | |||
.appendTo(container); | |||
if (!className.includes('hidden')) | |||
elKey.on('click', this.clickKey.bind(this, k)); | |||
}); | |||
if (!className.includes('hidden')) | |||
elKey.on('click', this.clickKey.bind(this, k)); | |||
}); | |||
}, | |||
clickKey: function (key) { | |||
@@ -131,7 +156,12 @@ define([ | |||
const handler = { | |||
caps: () => { | |||
this.kbUpper = (this.kbUpper + 1) % 3; | |||
this.kbUpper = (this.kbUpper + 1) % 2; | |||
this.renderKeyboard(); | |||
}, | |||
123: () => { | |||
this.kbUpper = (this.kbUpper === 2) ? 0 : 2; | |||
this.renderKeyboard(); | |||
}, | |||
@@ -139,12 +169,12 @@ define([ | |||
this.clickKey(' '); | |||
}, | |||
backspace: () => { | |||
'<<': () => { | |||
elInput.val(elInput.val().slice(0, -1)); | |||
this.find('.input').html(elInput.val()); | |||
}, | |||
enter: () => { | |||
send: () => { | |||
this.sendChat({ | |||
which: 13 | |||
}); | |||
@@ -223,9 +253,10 @@ define([ | |||
}, | |||
onKeyDown: function (key) { | |||
if (key === 'enter') | |||
if (key === 'enter') { | |||
this.toggle(true); | |||
else if (key === 'shift') | |||
this.find('input').val(this.lastChannel || ''); | |||
} else if (key === 'shift') | |||
this.showItemTooltip(); | |||
}, | |||
@@ -368,14 +399,14 @@ define([ | |||
} | |||
let textbox = this.find('input'); | |||
let config = { | |||
let msgConfig = { | |||
success: true, | |||
message: textbox.val() | |||
}; | |||
events.emit('onBeforeChat', config); | |||
events.emit('onBeforeChat', msgConfig); | |||
let val = config.message | |||
let val = msgConfig.message | |||
.split('<') | |||
.join('<') | |||
.split('>') | |||
@@ -383,12 +414,27 @@ define([ | |||
textbox.blur(); | |||
if (!config.success) | |||
if (!msgConfig.success) | |||
return; | |||
if (val.trim() === '') | |||
return; | |||
if (config.rememberChatChannel) { | |||
const firstChar = val[0]; | |||
let lastChannel = null; | |||
if ('@$'.includes(firstChar)) { | |||
const firstSpace = val.indexOf(' '); | |||
if (firstSpace === -1) | |||
lastChannel = val + ' '; | |||
else | |||
lastChannel = val.substr(0, firstSpace) + ' '; | |||
} else if (firstChar === '%') | |||
lastChannel = '%'; | |||
this.lastChannel = lastChannel; | |||
} | |||
client.request({ | |||
cpn: 'social', | |||
method: 'chat', | |||
@@ -396,6 +442,11 @@ define([ | |||
message: val | |||
} | |||
}); | |||
}, | |||
onToggleLastChannel: function (isOn) { | |||
if (!isOn) | |||
this.lastChannel = null; | |||
} | |||
}; | |||
}); |
@@ -1,5 +1,4 @@ | |||
@import "../../../css/colors.less"; | |||
@pad: 8px; | |||
@btnSize: 64px; | |||
@@ -7,10 +6,8 @@ | |||
position: absolute; | |||
left: 10px; | |||
bottom: 10px; | |||
width: 480px; | |||
padding: @pad; | |||
pointer-events: none; | |||
.input { | |||
@@ -28,6 +25,7 @@ | |||
&.time { | |||
display: none; | |||
} | |||
} | |||
.filters { | |||
@@ -42,13 +40,16 @@ | |||
.list-message { | |||
filter: none; | |||
} | |||
} | |||
} | |||
&.active { | |||
.list-message { | |||
opacity: 1 !important; | |||
} | |||
} | |||
.filters { | |||
@@ -79,8 +80,11 @@ | |||
background-color: @blackA; | |||
color: @grayB; | |||
} | |||
} | |||
} | |||
} | |||
.list { | |||
@@ -93,18 +97,14 @@ | |||
width: 100%; | |||
padding: 5px 10px; | |||
color: white; | |||
filter: | |||
drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
-moz-filter: | |||
drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
-moz-filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
word-wrap: break-word; | |||
line-height: 18px; | |||
@@ -120,16 +120,17 @@ | |||
color: @greenB; | |||
} | |||
a, &.q2 { | |||
a, | |||
&.q2 { | |||
color: @blueB; | |||
} | |||
&.q3 { | |||
color: @purple; | |||
color: @purpleA; | |||
} | |||
&.q4 { | |||
color: @orange; | |||
color: @orangeA; | |||
} | |||
&.color-green { | |||
@@ -147,12 +148,25 @@ | |||
&.color-tealC { | |||
color: @tealC; | |||
} | |||
} | |||
&.rep .rep { | |||
display: block; | |||
} | |||
&.chat .chat { | |||
display: block; | |||
} | |||
&.info .info { | |||
display: block; | |||
} | |||
&.loot .loot { | |||
display: block; | |||
} | |||
&.rep .rep { display: block; } | |||
&.chat .chat { display: block; } | |||
&.info .info { display: block; } | |||
&.loot .loot { display: block; } | |||
} | |||
.el { | |||
@@ -168,27 +182,24 @@ | |||
background-color: transparent; | |||
text-align: left; | |||
padding: 5px 10px; | |||
filter: | |||
drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
-moz-filter: | |||
drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
-moz-filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
} | |||
width: 100%; | |||
color: @white; | |||
} | |||
} | |||
.mobile .uiMessages { | |||
padding: 10px 0px; | |||
padding: 0px; | |||
pointer-events: all; | |||
.btnClose.active { | |||
@@ -204,6 +215,8 @@ | |||
.filters { | |||
margin-bottom: 0px; | |||
flex-shrink: 0; | |||
background-color: @darkGray; | |||
} | |||
.list { | |||
@@ -223,12 +236,16 @@ | |||
.input { | |||
display: block; | |||
height: 26px; | |||
flex-shrink: 0; | |||
} | |||
.el.textbox:not(.input) { | |||
&.message, &.time { | |||
&.message, | |||
&.time { | |||
display: none; | |||
} | |||
} | |||
.keyboard { | |||
@@ -238,7 +255,8 @@ | |||
background-color: @blackC; | |||
justify-content: center; | |||
align-items: center; | |||
height: 292px; | |||
height: 128px; | |||
flex-shrink: 0; | |||
.key { | |||
flex: 1; | |||
@@ -246,12 +264,9 @@ | |||
color: @white; | |||
padding: 8px 10px; | |||
text-align: center; | |||
border-top: 2px solid #373041; | |||
border-left: 1px solid #373041; | |||
border-right: 1px solid #373041; | |||
&.special { | |||
color: @orange; | |||
color: @orangeA; | |||
} | |||
&.hidden { | |||
@@ -261,12 +276,20 @@ | |||
&.space { | |||
flex: 5; | |||
} | |||
} | |||
.newline { | |||
width: 100%; | |||
} | |||
.gap { | |||
width: 5%; | |||
flex-shrink: 0; | |||
} | |||
} | |||
} | |||
&:not(.typing) { | |||
@@ -292,5 +315,7 @@ | |||
> * { | |||
display: none; | |||
} | |||
} | |||
} |
@@ -14,7 +14,7 @@ | |||
margin-left: calc(12px - 60px); | |||
text-align: center; | |||
position: absolute; | |||
left: calc(50vw - 86px); | |||
left: calc(50vw - 88px); | |||
top: calc(50vh - 113px); | |||
width: 64px; | |||
height: 64px; | |||
@@ -27,6 +27,7 @@ | |||
height: 100%; | |||
background: url(../../../images/uiIcons.png) -448px -64px; | |||
} | |||
} | |||
.casting { | |||
@@ -51,5 +52,7 @@ | |||
text-align: center; | |||
color: @black; | |||
} | |||
} | |||
} |
@@ -6,7 +6,7 @@ | |||
border: 5px solid @blackB; | |||
text-align: center; | |||
width: 450px; | |||
height: 400px; | |||
height: 396px; | |||
> .heading { | |||
color: @blueA; | |||
@@ -21,16 +21,17 @@ | |||
} | |||
.bottom { | |||
height: calc(100% - 35px); | |||
height: calc(100% - 36px); | |||
background-color: @blackC; | |||
padding: 10px; | |||
padding: 8px; | |||
.list { | |||
width: 100%; | |||
height: 100%; | |||
overflow-y: auto; | |||
background-color: @darkGray; | |||
display: flex; | |||
flex-direction: column; | |||
.heading { | |||
height: 24px; | |||
@@ -39,6 +40,7 @@ | |||
> div { | |||
float: left; | |||
padding: 4px; | |||
height: 24px; | |||
&:nth-child(1) { | |||
width: 10%; | |||
@@ -63,6 +65,7 @@ | |||
> div { | |||
float: left; | |||
padding: 4px; | |||
height: 24px; | |||
&:nth-child(1) { | |||
width: 10%; | |||
@@ -6,7 +6,8 @@ define([ | |||
'ui/factory', | |||
'js/objects/objects', | |||
'js/system/client', | |||
'js/sound/sound' | |||
'js/sound/sound', | |||
'js/config' | |||
], function ( | |||
events, | |||
template, | |||
@@ -15,95 +16,182 @@ define([ | |||
factory, | |||
objects, | |||
client, | |||
sound | |||
sound, | |||
config | |||
) { | |||
return { | |||
tpl: template, | |||
centered: true, | |||
modal: true, | |||
hasClose: true, | |||
postRender: function () { | |||
this.onEvent('onToggleOptions', this.toggle.bind(this)); | |||
//Can only toggle fullscreen directly in a listener, not deferred the way jQuery does it | |||
this.el.find('.btnScreen')[0].addEventListener('click', this.toggleScreen.bind(this)); | |||
this.el.find('.btnNames').on('click', events.emit.bind(events, 'onKeyDown', 'v')); | |||
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)); | |||
this.el.find('.btnPatreon').on('click', this.patreon.bind(this)); | |||
this.el.find('.btnIssue').on('click', this.reportIssue.bind(this)); | |||
this.find('.item.screen .name')[0].addEventListener('click', this.toggleScreen.bind(this)); | |||
this.find('.item.nameplates .name').on('click', events.emit.bind(events, 'onUiKeyDown', { key: 'v' })); | |||
this.find('.item.quests .name').on('click', this.toggleQuests.bind(this)); | |||
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.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)); | |||
}, | |||
reportIssue: function () { | |||
window.open('https://gitlab.com/Isleward/isleward/issues/new', '_blank'); | |||
toggleUnusableIndicators: function () { | |||
config.toggle('unusableIndicators'); | |||
if (config.unusableIndicators === 'background' && config.qualityIndicators === 'background') { | |||
config.toggle('qualityIndicators'); | |||
events.emit('onToggleQualityIndicators', config.qualityIndicators); | |||
} | |||
events.emit('onToggleUnusableIndicators', config.unusableIndicators); | |||
}, | |||
patreon: function () { | |||
window.open('https://patreon.com/bigbadwaffle', '_blank'); | |||
onToggleUnusableIndicators: function (state) { | |||
const newValue = state[0].toUpperCase() + state.substr(1); | |||
this.find('.item.unusable .value').html(newValue); | |||
}, | |||
charSelect: function () { | |||
this.el.addClass('disabled'); | |||
toggleQualityIndicators: function () { | |||
config.toggle('qualityIndicators'); | |||
client.request({ | |||
module: 'cons', | |||
method: 'unzone', | |||
callback: this.onCharSelect.bind(this) | |||
}); | |||
if (config.qualityIndicators === 'background' && config.unusableIndicators === 'background') { | |||
config.toggle('unusableIndicators'); | |||
events.emit('onToggleUnusableIndicators', config.unusableIndicators); | |||
} | |||
events.emit('onToggleQualityIndicators', config.qualityIndicators); | |||
}, | |||
onCharSelect: function () { | |||
renderer.clean(); | |||
objects.onRezone(); | |||
renderer.buildTitleScreen(); | |||
sound.unload(); | |||
onToggleQualityIndicators: function (state) { | |||
const newValue = state[0].toUpperCase() + state.substr(1); | |||
events.emit('onShowCharacterSelect'); | |||
$('[class^="ui"]:not(.ui-container)').toArray().forEach(el => { | |||
let ui = $(el).data('ui'); | |||
if (ui && ui.destroy) | |||
ui.destroy(); | |||
}); | |||
factory.build('characters', {}); | |||
this.find('.item.quality .value').html(newValue); | |||
}, | |||
toggleScreen: function () { | |||
this.el.find('.btnScreen').html(renderer.toggleScreen()); | |||
const state = renderer.toggleScreen(); | |||
const newValue = (state === 'Windowed') ? 'Off' : 'On'; | |||
this.find('.item.screen .value').html(newValue); | |||
}, | |||
toggleEvents: function () { | |||
config.toggle('showEvents'); | |||
events.emit('onToggleEventsVisibility', config.showEvents); | |||
}, | |||
toggleQuests: function () { | |||
config.toggle('showQuests'); | |||
events.emit('onToggleQuestsVisibility', config.showQuests); | |||
}, | |||
onToggleEventsVisibility: function (state) { | |||
const newValue = state ? 'On' : 'Off'; | |||
this.find('.item.events .value').html(newValue); | |||
}, | |||
onToggleQuestsVisibility: function (state) { | |||
const newValue = state[0].toUpperCase() + state.substr(1); | |||
this.find('.item.quests .value').html(newValue); | |||
}, | |||
onResize: function () { | |||
let isFullscreen = (window.innerHeight === screen.height); | |||
if (isFullscreen) | |||
this.el.find('.btnScreen').html('Windowed'); | |||
else | |||
this.el.find('.btnScreen').html('Fullscreen'); | |||
const newValue = isFullscreen ? 'On' : 'Off'; | |||
this.find('.item.screen .value').html(newValue); | |||
}, | |||
onToggleNameplates: function (state) { | |||
const newValue = state ? 'On' : 'Off'; | |||
this.find('.item.nameplates .value').html(newValue); | |||
}, | |||
toggleAudio: function () { | |||
config.toggle('playAudio'); | |||
events.emit('onToggleAudio', config.playAudio); | |||
}, | |||
onToggleAudio: function (isAudioOn) { | |||
const newValue = isAudioOn ? 'On' : 'Off'; | |||
this.find('.item.audio .value').html(newValue); | |||
}, | |||
toggleLastChannel: function () { | |||
config.toggle('rememberChatChannel'); | |||
events.emit('onToggleLastChannel', config.rememberChatChannel); | |||
}, | |||
onToggleLastChannel: function (state) { | |||
const newValue = state ? 'On' : 'Off'; | |||
this.find('.item.lastChannel .value').html(newValue); | |||
}, | |||
build: function () { | |||
this.onToggleNameplates(config.showNames); | |||
this.onToggleAudio(config.playAudio); | |||
this.onToggleEventsVisibility(config.showEvents); | |||
this.onToggleQuestsVisibility(config.showQuests); | |||
this.onToggleQualityIndicators(config.qualityIndicators); | |||
this.onToggleUnusableIndicators(config.unusableIndicators); | |||
this.onToggleLastChannel(config.rememberChatChannel); | |||
}, | |||
toggle: function () { | |||
this.onResize(); | |||
this.shown = !this.el.is(':visible'); | |||
if (this.shown) { | |||
if (!this.shown) { | |||
this.show(); | |||
this.shown = true; | |||
events.emit('onShowOverlay', this.el); | |||
this.build(); | |||
} else { | |||
this.hide(); | |||
events.emit('onHideOverlay', this.el); | |||
this.shown = false; | |||
} | |||
}, | |||
onKeyDown: function (keyEvent) { | |||
const { key } = keyEvent; | |||
if (key === 'v') { | |||
config.toggle('showNames'); | |||
logOut: function () { | |||
window.location = window.location; | |||
events.emit('onToggleNameplates', config.showNames); | |||
const newValue = config.showNames ? 'On' : 'Off'; | |||
this.find('.item.nameplates .value').html(newValue); | |||
} | |||
}, | |||
onKeyDown: function (key) { | |||
if (key === 'esc') | |||
this.toggle(); | |||
afterHide: function () { | |||
events.emit('onToggleMainMenu'); | |||
} | |||
}; | |||
}); |
@@ -2,48 +2,86 @@ | |||
.uiOptions { | |||
display: none; | |||
width: 400px; | |||
background-color: @gray; | |||
border: 4px solid @lightGray; | |||
text-align: center; | |||
padding: 0px 16px; | |||
.btn { | |||
color: @white; | |||
width: 100%; | |||
height: 32px; | |||
background-color: @blue; | |||
margin: 16px 0px; | |||
padding-top: 8px; | |||
cursor: pointer; | |||
&.btnNames { | |||
display: none; | |||
} | |||
&:hover { | |||
background-color: lighten(@blue, 15%); | |||
color: @black; | |||
} | |||
background-color: @blackC; | |||
border: 5px solid @blackB; | |||
> .heading { | |||
color: @blueA; | |||
width: 100%; | |||
height: 36px; | |||
background-color: @blackB; | |||
position: relative; | |||
text-align: center; | |||
.heading-text { | |||
padding-top: 8px; | |||
margin: auto; | |||
} | |||
} | |||
.btnIssue { | |||
background-color: @red; | |||
.bottom { | |||
padding: 10px; | |||
.list { | |||
display: flex; | |||
flex-direction: column; | |||
.heading { | |||
color: @grayC; | |||
border-bottom: 2px solid @grayD; | |||
margin: 0px 10px 5px 10px; | |||
padding-bottom: 5px; | |||
&:not(:first-child) { | |||
margin: 10px 10px 5px 10px; | |||
} | |||
} | |||
.item { | |||
height: 30px; | |||
display: flex; | |||
justify-content: space-between; | |||
align-items: center; | |||
.name { | |||
color: @blueB; | |||
flex: 1; | |||
height: 100%; | |||
display: flex; | |||
align-items: center; | |||
padding: 0px 10px; | |||
margin-right: 10px; | |||
cursor: pointer; | |||
&:hover { | |||
color: @blueA; | |||
background-color: @blackA; | |||
} | |||
} | |||
.value { | |||
color: @white; | |||
display: flex; | |||
align-items: center; | |||
justify-content: flex-end; | |||
padding-right: 10px; | |||
} | |||
} | |||
&:hover { | |||
background-color: lighten(@red, 15%); | |||
color: @black; | |||
} | |||
} | |||
} | |||
.mobile .uiOptions { | |||
.btnScreen { | |||
.item.screen { | |||
display: none; | |||
} | |||
.btnNames { | |||
display: block; | |||
} | |||
} |
@@ -1,9 +1,45 @@ | |||
<div class="uiOptions"> | |||
<div class="btn btnContinue">Continue</div> | |||
<div class="btn btnScreen">Windowed</div> | |||
<div class="btn btnCharSelect">Character Select</div> | |||
<div class="btn btnNames">Toggle Nameplates</div> | |||
<div class="btn btnLogOut">Log Out</div> | |||
<div class="btn btnPatreon">Pledge on Patreon</div> | |||
<div class="btn btnIssue">Report an Issue</div> | |||
<div class="heading"> | |||
<div class="heading-text">Options</div> | |||
</div> | |||
<div class="bottom"> | |||
<div class="list"> | |||
<div class="heading game">Game</div> | |||
<div class="item screen"> | |||
<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> | |||
<div class="heading hud">HUD</div> | |||
<div class="item nameplates"> | |||
<div class="name">Nameplates</div> | |||
<div class="value">On</div> | |||
</div> | |||
<div class="item quests"> | |||
<div class="name">Quests</div> | |||
<div class="value">On</div> | |||
</div> | |||
<div class="item events"> | |||
<div class="name">Events</div> | |||
<div class="value">On</div> | |||
</div> | |||
<div class="heading inventory">Inventory</div> | |||
<div class="item quality"> | |||
<div class="name">Quality Indicators</div> | |||
<div class="value">Off</div> | |||
</div> | |||
<div class="item unusable"> | |||
<div class="name">Unusable Indicators</div> | |||
<div class="value">Off</div> | |||
</div> | |||
<div class="heading inventory">Chat</div> | |||
<div class="item lastChannel"> | |||
<div class="name">Remember Last Chat Channel</div> | |||
<div class="value">On</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> |
@@ -45,7 +45,9 @@ define([ | |||
enabled: true, | |||
init: function (el) { | |||
init: function (el, zoom) { | |||
this.zoom = zoom; | |||
el | |||
.on('mousedown', this.events.mouse.mouseDown.bind(this)) | |||
.on('mouseup', this.events.mouse.mouseUp.bind(this)) | |||
@@ -197,7 +199,7 @@ define([ | |||
touch: { | |||
touchStart: function (e) { | |||
let pos = this.events.touch.convertTouchPos(e); | |||
let pos = this.events.touch.convertTouchPos.call(this, e); | |||
this.mouse.raw = { | |||
clientX: pos.x, | |||
@@ -215,7 +217,7 @@ define([ | |||
}, | |||
touchMove: function (e) { | |||
let pos = this.events.touch.convertTouchPos(e); | |||
let pos = this.events.touch.convertTouchPos.call(this, e); | |||
this.mouse.raw = { | |||
clientX: pos.x, | |||
@@ -244,8 +246,8 @@ define([ | |||
convertTouchPos: function (e) { | |||
let rect = e.target.getBoundingClientRect(); | |||
return { | |||
x: e.targetTouches[0].pageX - rect.left, | |||
y: e.targetTouches[0].pageY - rect.top | |||
x: (e.targetTouches[0].pageX - rect.left) * this.zoom, | |||
y: (e.targetTouches[0].pageY - rect.top) * this.zoom | |||
}; | |||
} | |||
} | |||
@@ -1,3 +1,7 @@ | |||
let zoom = window.devicePixelRatio; | |||
if (isMobile) | |||
zoom /= 2; | |||
define([ | |||
'js/system/events', | |||
'js/system/client', | |||
@@ -51,7 +55,7 @@ define([ | |||
handlerResize: null, | |||
postRender: function () { | |||
input.init(this.el); | |||
input.init(this.el, zoom); | |||
this.data.nodes = temp.nodes; | |||
this.data.links = temp.links.map(l => { | |||
@@ -74,8 +78,6 @@ define([ | |||
this.handlerResize = this.onResize.bind(this); | |||
window.addEventListener('resize', this.handlerResize); | |||
let zoom = window.devicePixelRatio; | |||
this.canvas = this.find('.canvas')[0]; | |||
this.size.w = this.canvas.width = this.find('.bottom').width() * zoom; | |||
this.size.h = this.canvas.height = this.find('.bottom').height() * zoom; | |||
@@ -120,8 +122,6 @@ define([ | |||
if (isMobile || !this.shown) | |||
return; | |||
let zoom = window.devicePixelRatio; | |||
this.size.w = this.canvas.width = this.find('.bottom').width() * zoom; | |||
this.size.h = this.canvas.height = this.find('.bottom').height() * zoom; | |||
@@ -357,8 +357,8 @@ define([ | |||
text = 'Starting node for ' + node.spiritStart + ' spirits'; | |||
let tooltipPos = { | |||
x: input.mouse.raw.clientX + 15, | |||
y: input.mouse.raw.clientY | |||
x: (input.mouse.raw.clientX + 15) / zoom, | |||
y: (input.mouse.raw.clientY) / zoom | |||
}; | |||
events.emit('onShowTooltip', text, this.el[0], tooltipPos); | |||
@@ -399,10 +399,9 @@ define([ | |||
this.events.onMouseMove.call(this, e); | |||
let windowZoom = window.devicePixelRatio; | |||
this.panOrigin = { | |||
x: e.raw.clientX * windowZoom, | |||
y: e.raw.clientY * windowZoom | |||
x: e.raw.clientX * zoom, | |||
y: e.raw.clientY * zoom | |||
}; | |||
}, | |||
@@ -419,12 +418,11 @@ define([ | |||
}; | |||
} | |||
let windowZoom = window.devicePixelRatio; | |||
let zoomPanMultiplier = this.currentZoom; | |||
let scrollSpeed = constants.scrollSpeed / zoomPanMultiplier; | |||
const rawX = e.raw.clientX * windowZoom; | |||
const rawY = e.raw.clientY * windowZoom; | |||
const rawX = e.raw.clientX * zoom; | |||
const rawY = e.raw.clientY * zoom; | |||
this.pos.x += (this.panOrigin.x - rawX) * scrollSpeed; | |||
this.pos.y += (this.panOrigin.y - rawY) * scrollSpeed; | |||
@@ -3,13 +3,15 @@ define([ | |||
'js/system/events', | |||
'html!ui/templates/quests/template', | |||
'html!ui/templates/quests/templateQuest', | |||
'css!ui/templates/quests/styles' | |||
'css!ui/templates/quests/styles', | |||
'js/config' | |||
], function ( | |||
client, | |||
events, | |||
tpl, | |||
templateQuest, | |||
styles | |||
styles, | |||
config | |||
) { | |||
return { | |||
tpl: tpl, | |||
@@ -28,6 +30,9 @@ define([ | |||
this.onEvent('onObtainQuest', this.onObtainQuest.bind(this)); | |||
this.onEvent('onUpdateQuest', this.onUpdateQuest.bind(this)); | |||
this.onEvent('onCompleteQuest', this.onCompleteQuest.bind(this)); | |||
this.onEvent('onToggleQuestsVisibility', this.onToggleQuestsVisibility.bind(this)); | |||
this.onToggleQuestsVisibility(config.showQuests); | |||
}, | |||
onRezone: function () { | |||
@@ -118,6 +123,19 @@ define([ | |||
toggleButtons: function (e) { | |||
this.el.toggleClass('active'); | |||
e.stopPropagation(); | |||
}, | |||
onToggleQuestsVisibility: function (state) { | |||
this.shown = state !== 'off'; | |||
if (this.shown) | |||
this.show(); | |||
else | |||
this.hide(); | |||
this.el.removeClass('minimal'); | |||
if (state === 'minimal') | |||
this.el.addClass('minimal'); | |||
} | |||
}; | |||
}); |
@@ -97,17 +97,23 @@ | |||
display: none; | |||
} | |||
&.minimal { | |||
.quest.disabled { | |||
display: none; | |||
} | |||
} | |||
} | |||
.mobile .uiQuests { | |||
background-color: fade(@darkGray, 90%); | |||
&:not(.active) { | |||
position: absolute; | |||
top: 10px; | |||
right: 158px; | |||
width: @btnSize; | |||
height: @btnSize; | |||
margin-left: 10px; | |||
position: relative; | |||
&:after { | |||
content: ''; | |||
@@ -136,11 +142,11 @@ | |||
display: block; | |||
position: absolute; | |||
right: calc(100% + 10px); | |||
top: 50%; | |||
transform: translateY(-50%); | |||
top: 0px; | |||
background-color: fade(@darkGray, 90%); | |||
width: @btnSize; | |||
height: @btnSize; | |||
padding-top: 0px; | |||
.icon { | |||
width: 100%; | |||
@@ -30,25 +30,23 @@ define([ | |||
this.find('.info .description').html(''); | |||
this.find('.bar-outer').hide(); | |||
if (list.length === 0) | |||
if (!list.length) | |||
this.find('.heading-bottom').html("you haven't discovered any factions yet"); | |||
else | |||
this.find('.heading-bottom').html('select a faction to see more info'); | |||
let elList = this.find('.list').empty(); | |||
list.forEach(function (l) { | |||
list.forEach(l => { | |||
if (l.noGainRep) | |||
return; | |||
let html = '<div class="faction">' + l.name.toLowerCase() + '</div>'; | |||
let el = $(html) | |||
.appendTo(elList); | |||
let el = $(html).appendTo(elList); | |||
el | |||
.on('click', this.onSelectFaction.bind(this, el, l)); | |||
}, this); | |||
el.on('click', this.onSelectFaction.bind(this, el, l)); | |||
}); | |||
}, | |||
onSelectFaction: function (el, faction) { | |||
@@ -17,9 +17,11 @@ | |||
padding-top: 8px; | |||
margin: auto; | |||
} | |||
} | |||
.list, .info { | |||
.list, | |||
.info { | |||
background-color: fade(@blackC, 90%); | |||
float: left; | |||
height: calc(100% - 36px); | |||
@@ -44,7 +46,9 @@ | |||
&:hover { | |||
background-color: @grayC; | |||
} | |||
} | |||
} | |||
.info { | |||
@@ -59,6 +63,7 @@ | |||
.heading-bottom { | |||
margin: auto; | |||
} | |||
} | |||
.description { | |||
@@ -96,18 +101,31 @@ | |||
position: absolute; | |||
left: 0px; | |||
top: 5px; | |||
filter: | |||
drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
-moz-filter: | |||
drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
-moz-filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
} | |||
} | |||
} | |||
.list:empty { | |||
display: none; | |||
+ .info { | |||
width: 100%; | |||
} | |||
} | |||
} | |||
.mobile .uiReputation { | |||
z-index: 2; | |||
} |
@@ -16,6 +16,7 @@ | |||
padding-top: 8px; | |||
margin: auto; | |||
} | |||
} | |||
.bottom { | |||
@@ -51,12 +52,14 @@ | |||
text-align: center; | |||
} | |||
&.item-picker, &.actionButton { | |||
&.item-picker, | |||
&.actionButton { | |||
cursor: pointer; | |||
&:hover { | |||
background-color: @blackC; | |||
} | |||
} | |||
&.item-picker { | |||
@@ -67,15 +70,18 @@ | |||
height: 64px; | |||
background: url('../../../images/uiIcons.png') -256px -64px; | |||
} | |||
} | |||
&.actionButton { | |||
padding: 8px; | |||
.icon { | |||
width: 64px; | |||
height: 64px; | |||
background: url('../../../images/uiIcons.png') -192px -64px; | |||
} | |||
} | |||
.item { | |||
@@ -91,21 +97,19 @@ | |||
bottom: 3px; | |||
position: absolute; | |||
color: @white; | |||
filter: | |||
drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
-moz-filter: | |||
drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
-moz-filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
&.red { | |||
color: @red; | |||
} | |||
} | |||
.icon { | |||
@@ -114,21 +118,20 @@ | |||
position: absolute; | |||
left: 8px; | |||
top: 8px; | |||
filter: | |||
brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
-moz-filter: | |||
brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
filter: brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
-moz-filter: brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
} | |||
} | |||
} | |||
&:first-child { | |||
@@ -142,36 +145,42 @@ | |||
.col-btn { | |||
height: calc((100% - 10px) / 2); | |||
width: 100%; | |||
color: #f2f5f5; | |||
text-align: center; | |||
padding-top: 10px; | |||
background-color: @blackB; | |||
margin-bottom: 10px; | |||
cursor: pointer; | |||
&.selected { | |||
color: @orange; | |||
} | |||
&:hover { | |||
background-color: @blackA; | |||
} | |||
&.col-half { | |||
width: calc((100% - 10px) / 2); | |||
float: left; | |||
&:nth-child(2n + 1) { | |||
width: 100%; | |||
color: #f2f5f5; | |||
text-align: center; | |||
padding-top: 10px; | |||
background-color: @blackB; | |||
margin-bottom: 10px; | |||
cursor: pointer; | |||
&.selected { | |||
color: @orangeA; | |||
} | |||
&:hover { | |||
background-color: @blackA; | |||
} | |||
&.col-half { | |||
width: calc((100% - 10px) / 2); | |||
float: left; | |||
&:nth-child(2n + 1) { | |||
margin-right: 10px; | |||
} | |||
} | |||
} | |||
&:not(.col-half) { | |||
} | |||
&:not(.col-half) { | |||
clear: both; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -81,8 +81,9 @@ | |||
.spell { | |||
position: relative; | |||
margin: 8px 0px 0px 0px; | |||
margin: 10px 0px 0px 0px; | |||
background-color: fade(#3a3b4a, 90%); | |||
border: none; | |||
.hotkey { | |||
display: none; | |||
@@ -90,9 +91,9 @@ | |||
&.active:before { | |||
content: ''; | |||
width: 4px; | |||
width: 5px; | |||
height: 100%; | |||
left: -4px; | |||
left: -5px; | |||
top: 0%; | |||
background-color: @greenB; | |||
position: absolute; | |||
@@ -4,14 +4,16 @@ define([ | |||
'html!ui/templates/stash/template', | |||
'css!ui/templates/stash/styles', | |||
'html!ui/templates/inventory/templateItem', | |||
'js/input' | |||
'js/input', | |||
'ui/shared/renderItem' | |||
], function ( | |||
events, | |||
client, | |||
template, | |||
styles, | |||
tplItem, | |||
input | |||
input, | |||
renderItem | |||
) { | |||
return { | |||
tpl: template, | |||
@@ -33,34 +35,24 @@ define([ | |||
}, | |||
build: function () { | |||
this.el.removeClass('scrolls'); | |||
if (window.player.stash.maxItems > 50) | |||
this.el.addClass('scrolls'); | |||
let container = this.el.find('.grid') | |||
.empty(); | |||
let items = this.items; | |||
let iLen = Math.max(items.length, 50); | |||
let iLen = Math.max(items.length, window.player.stash.maxItems); | |||
for (let i = 0; i < iLen; i++) { | |||
let item = items[i]; | |||
let itemEl = $(tplItem) | |||
.appendTo(container); | |||
let itemEl = renderItem(container, item); | |||
if (!item) | |||
continue; | |||
let imgX = -item.sprite[0] * 64; | |||
let imgY = -item.sprite[1] * 64; | |||
let spritesheet = item.spritesheet || '../../../images/items.png'; | |||
if (!item.spritesheet) { | |||
if (item.material) | |||
spritesheet = '../../../images/materials.png'; | |||
else if (item.quest) | |||
spritesheet = '../../../images/questItems.png'; | |||
else if (item.type === 'consumable') | |||
spritesheet = '../../../images/consumables.png'; | |||
} | |||
let moveHandler = this.onHover.bind(this, itemEl, item); | |||
let downHandler = () => {}; | |||
if (isMobile) { | |||
@@ -74,16 +66,7 @@ define([ | |||
.on('mousemove', moveHandler) | |||
.on('mouseleave', this.hideTooltip.bind(this, itemEl, item)) | |||
.find('.icon') | |||
.css('background', 'url(' + spritesheet + ') ' + imgX + 'px ' + imgY + 'px') | |||
.on('contextmenu', this.showContext.bind(this, item)); | |||
if (item.quantity) | |||
itemEl.find('.quantity').html(item.quantity); | |||
if (item.eq) | |||
itemEl.addClass('eq'); | |||
if (item.isNew) | |||
itemEl.addClass('new'); | |||
} | |||
}, | |||
@@ -1,8 +1,13 @@ | |||
@import "../../../css/colors.less"; | |||
.uiStash { | |||
.grid { | |||
overflow-y: auto; | |||
&.scrolls { | |||
width: 834px; | |||
.grid { | |||
overflow-y: auto; | |||
width: 825px; | |||
} | |||
} | |||
.footer { | |||
@@ -1,11 +1,13 @@ | |||
define([ | |||
'js/system/client', | |||
'js/system/events', | |||
'js/system/globals', | |||
'html!ui/templates/target/template', | |||
'css!ui/templates/target/styles' | |||
], function ( | |||
client, | |||
events, | |||
globals, | |||
template, | |||
styles | |||
) { | |||
@@ -47,7 +49,7 @@ define([ | |||
// target.player (only the logged-in player has a player component) | |||
if ((e.button !== 2) || (!target) || (!target.dialogue) || (target === window.player) || (target.prophecies)) { | |||
if (target.prophecies) { | |||
let inspectContext = [ | |||
const inspectContext = [ | |||
target.name, | |||
'----------', { | |||
text: 'inspect', | |||
@@ -55,13 +57,20 @@ define([ | |||
} | |||
]; | |||
globals.clientConfig.contextMenuActions.player.forEach(action => { | |||
inspectContext.push({ | |||
text: action.text, | |||
callback: this.onAction.bind(this, action, true) | |||
}); | |||
}); | |||
events.emit('onContextMenu', inspectContext, e.event); | |||
} | |||
return; | |||
} | |||
let talkContext = [ | |||
const talkContext = [ | |||
target.name, | |||
'----------', { | |||
text: 'talk', | |||
@@ -69,6 +78,13 @@ define([ | |||
} | |||
]; | |||
globals.clientConfig.contextMenuActions.npc.forEach(action => { | |||
talkContext.push({ | |||
text: action.text, | |||
callback: this.onAction.bind(this, action, false) | |||
}); | |||
}); | |||
events.emit('onContextMenu', talkContext, e.event); | |||
e.event.preventDefault(); | |||
@@ -79,6 +95,21 @@ define([ | |||
window.player.dialogue.talk(this.target); | |||
}, | |||
onAction: function (action, sendTargetServerId) { | |||
const { threadModule, cpn, method, data = {} } = action; | |||
if (method === 'performAction') | |||
data.data.playerId = this.target.id; | |||
else if (threadModule) | |||
data.targetId = sendTargetServerId ? this.target.serverId : this.target.id; | |||
client.request({ | |||
threadModule, | |||
cpn, | |||
method, | |||
data | |||
}); | |||
}, | |||
onInspect: function () { | |||
client.request({ | |||
cpn: 'player', | |||
@@ -0,0 +1,78 @@ | |||
@import "../../../css/colors.less"; | |||
.uiTerms { | |||
display: none; | |||
width: 562px; | |||
height: 700px; | |||
margin-top: -80px; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
> * { | |||
flex-shrink: 0; | |||
} | |||
.logo { | |||
width: 562px; | |||
height: 200px; | |||
margin-bottom: 24px; | |||
filter: drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
} | |||
.heading { | |||
color: @orangeA; | |||
filter: drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
margin-bottom: 24px; | |||
} | |||
.content { | |||
flex: 1; | |||
width: 100%; | |||
overflow-y: auto; | |||
background-color: @blackC; | |||
border: 5px solid @blackB; | |||
padding: 10px; | |||
color: @grayB; | |||
text-align: justify; | |||
.title { | |||
color: @white; | |||
} | |||
} | |||
.buttons { | |||
width: 100%; | |||
background-color: @blackC; | |||
display: flex; | |||
justify-content: space-between; | |||
border-style: solid; | |||
border-color: @blackB; | |||
border-width: 0px 5px 5px 5px; | |||
padding: 5px; | |||
.btn { | |||
background-color: @blueC; | |||
color: @white; | |||
height: 35px; | |||
padding-left: 10px; | |||
padding-right: 10px; | |||
&:hover { | |||
background-color: @blueB; | |||
} | |||
} | |||
} | |||
} | |||
.mobile .uiTerms { | |||
height: 80%; | |||
margin-top: 0px; | |||
} |
@@ -0,0 +1,9 @@ | |||
<div class="uiTerms"> | |||
<img class="logo" src="images/logo_0.png" alt=""> | |||
<div class="heading">Terms of Service</div> | |||
<div class="content"></div> | |||
<div class="buttons"> | |||
<div class="el btn btnDecline">Decline</div> | |||
<div class="el btn btnAccept" title="Please scroll to the bottom to accept">Accept</div> | |||
</div> | |||
</div> |
@@ -0,0 +1,43 @@ | |||
define([ | |||
'ui/factory', | |||
'html!ui/templates/terms/template', | |||
'css!ui/templates/terms/styles', | |||
'js/system/globals', | |||
'js/system/browserStorage' | |||
], function ( | |||
uiFactory, | |||
template, | |||
styles, | |||
globals, | |||
browserStorage | |||
) { | |||
return { | |||
tpl: template, | |||
centered: true, | |||
postRender: function () { | |||
const { clientConfig: { tos: { content, version } } } = globals; | |||
const morphedContent = content.split('\n').join('<br />'); | |||
const elHeading = this.find('.heading'); | |||
elHeading.html(`${elHeading.html()} (v${version})`); | |||
this.find('.content').html(morphedContent); | |||
this.find('.btnDecline').on('click', this.onDeclineClick.bind(this)); | |||
this.find('.btnAccept').on('click', this.onAcceptClick.bind(this, version)); | |||
}, | |||
onDeclineClick: function () { | |||
browserStorage.set('tos_accepted_version', null); | |||
window.location = window.location; | |||
}, | |||
onAcceptClick: function (version) { | |||
browserStorage.set('tos_accepted_version', version); | |||
this.destroy(); | |||
uiFactory.build('characters'); | |||
} | |||
}; | |||
}); |
@@ -3,13 +3,15 @@ define([ | |||
'js/system/client', | |||
'html!ui/templates/trade/template', | |||
'css!ui/templates/trade/styles', | |||
'html!ui/templates/inventory/templateItem' | |||
'html!ui/templates/inventory/templateItem', | |||
'ui/shared/renderItem' | |||
], function ( | |||
events, | |||
client, | |||
template, | |||
styles, | |||
tplItem | |||
tplItem, | |||
renderItem | |||
) { | |||
return { | |||
tpl: template, | |||
@@ -59,8 +61,7 @@ define([ | |||
} | |||
if (!item) { | |||
$(tplItem) | |||
.appendTo(container) | |||
renderItem(container, null) | |||
.on('click', uiInventory.hideTooltip.bind(uiInventory)); | |||
continue; | |||
@@ -68,35 +69,11 @@ define([ | |||
item = $.extend(true, {}, item); | |||
let size = 64; | |||
let offset = 0; | |||
let itemEl = $(tplItem) | |||
.appendTo(container); | |||
let spritesheet = item.spritesheet || '../../../images/items.png'; | |||
if (!item.spritesheet) { | |||
if (item.material) | |||
spritesheet = '../../../images/materials.png'; | |||
else if (item.quest) | |||
spritesheet = '../../../images/questItems.png'; | |||
else if (item.type === 'consumable') | |||
spritesheet = '../../../images/consumables.png'; | |||
else if (item.type === 'skin') | |||
spritesheet = '../../../images/characters.png'; | |||
} | |||
if (item.type === 'skin') { | |||
offset = 4; | |||
size = 8; | |||
} | |||
let imgX = (-item.sprite[0] * size) + offset; | |||
let imgY = (-item.sprite[1] * size) + offset; | |||
let itemEl = renderItem(container, item); | |||
itemEl | |||
.data('item', item) | |||
.find('.icon') | |||
.css('background', 'url(' + spritesheet + ') ' + imgX + 'px ' + imgY + 'px') | |||
.addClass(item.type); | |||
if (isMobile) | |||
@@ -108,11 +85,6 @@ define([ | |||
.on('mouseleave', uiInventory.hideTooltip.bind(uiInventory, itemEl, item)); | |||
} | |||
if (item.quantity) | |||
itemEl.find('.quantity').html(item.quantity); | |||
else if (item.eq) | |||
itemEl.find('.quantity').html('EQ'); | |||
if (action === 'buy') { | |||
let noAfford = false; | |||
if (item.worth.currency) { | |||
@@ -132,13 +104,6 @@ define([ | |||
item.worthText = item.worth.amount + 'x ' + item.worth.currency; | |||
else | |||
item.worthText = ~~(itemList.markup * item.worth); | |||
if (item.eq) | |||
itemEl.addClass('eq'); | |||
else if (item.isNew) { | |||
itemEl.addClass('new'); | |||
itemEl.find('.quantity').html('NEW'); | |||
} | |||
} | |||
this.center(); | |||
@@ -18,6 +18,7 @@ | |||
padding-top: 8px; | |||
margin: auto; | |||
} | |||
} | |||
.bottom { | |||
@@ -29,8 +30,9 @@ | |||
color: @blueB; | |||
margin-bottom: 10px; | |||
} | |||
.left, .right { | |||
.left, | |||
.right { | |||
float: left; | |||
height: 100%; | |||
padding: 10px; | |||
@@ -55,8 +57,11 @@ | |||
&:hover { | |||
background-color: @blackB; | |||
} | |||
} | |||
} | |||
} | |||
.right { | |||
@@ -70,9 +75,10 @@ | |||
height: calc(100% - 100px - 35px); | |||
.title { | |||
color: @orange; | |||
color: @orangeA; | |||
padding-bottom: 10px; | |||
} | |||
} | |||
.materialList { | |||
@@ -80,12 +86,12 @@ | |||
visibility: hidden; | |||
.material { | |||
&.need { | |||
color: @redB; | |||
} | |||
} | |||
} | |||
.buttons { | |||
@@ -93,23 +99,32 @@ | |||
> .btn { | |||
height: 35px; | |||
width: 100px; | |||
color: @white; | |||
text-align: center; | |||
padding-top: 10px; | |||
background-color: @blackB; | |||
cursor: pointer; | |||
float: left; | |||
&:last-child { | |||
width: 100px; | |||
color: @white; | |||
text-align: center; | |||
padding-top: 10px; | |||
background-color: @blackB; | |||
cursor: pointer; | |||
float: left; | |||
&:last-child { | |||
float: right; | |||
} | |||
} | |||
&:hover { | |||
background-color: @blackA; | |||
} | |||
&:hover { | |||
background-color: @blackA; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.mobile .uiWorkbench { | |||
z-index: 2; | |||
} |
@@ -96,11 +96,20 @@ define([ | |||
}, | |||
show: function () { | |||
if (this.modal) | |||
$('.modal').hide(); | |||
if (this.modal) { | |||
//Close any other open modal | |||
$('.modal').toArray().forEach(el => { | |||
const ui = $(el).data('ui'); | |||
if (ui.shown) | |||
ui.hide(); | |||
}); | |||
} | |||
this.shown = true; | |||
this.el.show(); | |||
if (this.isFlex) | |||
this.el.css('display', 'flex'); | |||
else | |||
this.el.show(); | |||
if (this.onAfterShow) | |||
this.onAfterShow(); | |||
@@ -115,6 +124,9 @@ define([ | |||
this.shown = false; | |||
this.el.hide(); | |||
if (this.afterHide) | |||
this.afterHide(); | |||
}, | |||
destroy: function () { | |||
@@ -6,6 +6,8 @@ let factions = require('../../config/factions'); | |||
let connections = require('../../security/connections'); | |||
const ban = require('../social/ban'); | |||
const rezone = require('../social/rezone'); | |||
const canChat = require('../social/canChat'); | |||
let commandRoles = { | |||
//Regular players | |||
@@ -15,6 +17,7 @@ let commandRoles = { | |||
roll: 0, | |||
block: 0, | |||
unblock: 0, | |||
help: 0, | |||
//Mods | |||
ban: 5, | |||
@@ -39,7 +42,8 @@ let commandRoles = { | |||
getXp: 10, | |||
setPassword: 10, | |||
giveSkin: 10, | |||
getMaterials: 10 | |||
getMaterials: 10, | |||
rezone: 10 | |||
}; | |||
//Commands that should be run on the main thread (not the zone thread) | |||
@@ -56,7 +60,8 @@ const localCommands = [ | |||
'unblock', | |||
'broadcast', | |||
'saveAll', | |||
'ban' | |||
'ban', | |||
'help' | |||
]; | |||
//Actions that should appear when a player is right clicked | |||
@@ -281,18 +286,34 @@ module.exports = { | |||
}); | |||
}, | |||
help: function () { | |||
const msg = [ | |||
'You can use the following commands:', | |||
...Object.keys(commandRoles) | |||
.filter(c => this.roleLevel >= commandRoles[c]) | |||
.map(c => `/${c}`) | |||
].join('<br />'); | |||
this.sendMessage(msg, 'color-yellowB'); | |||
}, | |||
isInChannel: function (character, channel) { | |||
return character.auth.customChannels.some(c => (c === channel)); | |||
}, | |||
roll: function () { | |||
let roll = 1 + ~~(Math.random() * 100); | |||
if (!canChat(this.obj)) { | |||
this.sendMessage('Your character needs to be played for at least 3 minutes or be at least level 3 to be able to send messages in chat.', 'color-redA'); | |||
return; | |||
} | |||
const roll = 1 + ~~(Math.random() * 100); | |||
cons.emit('event', { | |||
event: 'onGetMessages', | |||
data: { | |||
messages: [{ | |||
class: 'color-grayB', | |||
message: this.obj.name + ' rolled ' + roll, | |||
message: `${this.obj.name} rolled ${roll}`, | |||
type: 'chat' | |||
}] | |||
} | |||
@@ -674,5 +695,9 @@ module.exports = { | |||
ban: function (msg) { | |||
ban(this, msg); | |||
}, | |||
rezone: function (msg) { | |||
rezone(this, msg); | |||
} | |||
}; |
@@ -5,6 +5,7 @@ let classes = require('../config/spirits'); | |||
let mtx = require('../mtx/mtx'); | |||
let factions = require('../config/factions'); | |||
let itemEffects = require('../items/itemEffects'); | |||
const transactions = require('../security/transactions'); | |||
const { applyItemStats } = require('./equipment/helpers'); | |||
@@ -449,8 +450,9 @@ module.exports = { | |||
}, | |||
mailItem: async function (msg) { | |||
/*let item = this.findItem(msg.itemId); | |||
if ((!item) || (item.noDrop) || (item.quest)) { | |||
let item = this.findItem(msg.itemId); | |||
if (!item || item.noDrop || item.quest) { | |||
this.resolveCallback(msg); | |||
return; | |||
} | |||
@@ -468,6 +470,8 @@ module.exports = { | |||
} else if (!this.findItem(msg.itemId)) | |||
return; | |||
const resolveTrans = transactions.register(); | |||
let blocked = false; | |||
if (res.components) { | |||
let social = res.components.find(f => f.type === 'social'); | |||
@@ -475,13 +479,15 @@ module.exports = { | |||
blocked = true; | |||
} | |||
if (!blocked) { | |||
const mappedItem = this.simplifyItem(item); | |||
this.obj.instance.mail.sendMail(msg.recipient, [mappedItem]); | |||
} | |||
const mappedItem = this.simplifyItem(item); | |||
this.destroyItem(item.id); | |||
this.resolveCallback(msg);*/ | |||
if (!blocked) | |||
await this.obj.instance.mail.sendMail(msg.recipient, [mappedItem]); | |||
this.resolveCallback(msg); | |||
resolveTrans(); | |||
}, | |||
hookItemEvents: function (items) { | |||
@@ -554,9 +560,12 @@ module.exports = { | |||
this.items | |||
.filter(i => !i.eq) | |||
.map(i => { | |||
//If we don't do this, [waist] goes before [undefined] | |||
const useSlot = i.slot ? i.slot : 'z'; | |||
return { | |||
item: i, | |||
sortId: `${i.slot}${i.material}${i.quest}${i.spell}${i.quality}${i.level}${i.sprite}${i.id}` | |||
sortId: `${useSlot}${i.material}${i.quest}${i.spell}${i.quality}${i.level}${i.sprite}${i.id}` | |||
}; | |||
}) | |||
.sort((a, b) => { | |||
@@ -109,6 +109,7 @@ module.exports = { | |||
this.selected.push(passiveTree.nodes.find(n => (n.spiritStart === this.obj.class)).id); | |||
this.obj.spellbook.calcDps(); | |||
this.obj.equipment.unequipAttrRqrGear(); | |||
}, | |||
simplify: function (self) { | |||
@@ -75,6 +75,7 @@ module.exports = { | |||
obj.addComponent('stash', { | |||
items: character.stash | |||
}); | |||
obj.stash.calculateMaxItems(roles.getExtraStashSlots(character.account)); | |||
let blueprintEffects = character.components.find(c => c.type === 'effects') || {}; | |||
if (blueprintEffects.effects) { | |||
@@ -30,7 +30,9 @@ module.exports = { | |||
if (!quest.init(hideMessage)) { | |||
this.quests.spliceWhere(q => (q === quest)); | |||
return false; | |||
} return true; | |||
} | |||
return true; | |||
}, | |||
complete: function (id) { | |||
@@ -0,0 +1,13 @@ | |||
module.exports = (obj, time) => { | |||
if (!time) | |||
time = +new Date(); | |||
const playerLevel = obj.level; | |||
const playedTime = obj.stats.stats.played * 1000; | |||
const sessionStart = obj.player.sessionStart; | |||
const sessionDelta = time - sessionStart; | |||
const canChat = (playerLevel >= 3 || playedTime + sessionDelta >= 180000); | |||
return canChat; | |||
}; |
@@ -1,6 +1,7 @@ | |||
let roles = require('../../config/roles'); | |||
let events = require('../../misc/events'); | |||
const profanities = require('../../misc/profanities'); | |||
const canChat = require('./canChat'); | |||
module.exports = (cpnSocial, msg) => { | |||
if (!msg.data.message) | |||
@@ -54,12 +55,7 @@ module.exports = (cpnSocial, msg) => { | |||
return; | |||
} | |||
let playerLevel = obj.level; | |||
let playedTime = obj.stats.stats.played * 1000; | |||
let sessionStart = obj.player.sessionStart; | |||
let sessionDelta = time - sessionStart; | |||
if (playerLevel < 3 && playedTime + sessionDelta < 180000) { | |||
if (!canChat(obj, time)) { | |||
sendMessage('Your character needs to be played for at least 3 minutes or be at least level 3 to be able to send messages in chat.', 'color-redA'); | |||
return; | |||
} | |||
@@ -0,0 +1,18 @@ | |||
module.exports = async (cpnSocial, targetZone) => { | |||
const { obj } = cpnSocial; | |||
obj.fireEvent('beforeRezone'); | |||
obj.destroyed = true; | |||
const simpleObj = obj.getSimple(true, false, true); | |||
process.send({ | |||
method: 'rezone', | |||
id: obj.serverId, | |||
args: { | |||
obj: simpleObj, | |||
newZone: targetZone | |||
} | |||
}); | |||
}; |
@@ -351,25 +351,6 @@ module.exports = { | |||
if (!isAuto) | |||
this.sendAnnouncement('Insufficient mana to cast spell'); | |||
success = false; | |||
} else if (spell.manaReserve) { | |||
let reserve = spell.manaReserve; | |||
if (reserve.percentage) { | |||
let reserveEvent = { | |||
spell: spell.name, | |||
reservePercent: reserve.percentage | |||
}; | |||
this.obj.fireEvent('onBeforeReserveMana', reserveEvent); | |||
if (!spell.active) { | |||
if (1 - this.obj.stats.values.manaReservePercent < reserve.percentage) { | |||
this.sendAnnouncement('Insufficient mana to cast spell'); | |||
success = false; | |||
} else | |||
this.obj.stats.addStat('manaReservePercent', reserveEvent.reservePercent); | |||
} else | |||
this.obj.stats.addStat('manaReservePercent', -reserveEvent.reservePercent); | |||
} | |||
} else if (spell.has('range')) { | |||
let distance = Math.max(Math.abs(action.target.x - this.obj.x), Math.abs(action.target.y - this.obj.y)); | |||
let range = spell.range; | |||
@@ -410,6 +391,27 @@ module.exports = { | |||
if (!castSuccess.success) | |||
return false; | |||
if (spell.manaReserve) { | |||
let reserve = spell.manaReserve; | |||
if (reserve.percentage) { | |||
let reserveEvent = { | |||
spell: spell.name, | |||
reservePercent: reserve.percentage | |||
}; | |||
this.obj.fireEvent('onBeforeReserveMana', reserveEvent); | |||
if (!spell.active) { | |||
if (1 - this.obj.stats.values.manaReservePercent < reserve.percentage) { | |||
this.sendAnnouncement('Insufficient mana to cast spell'); | |||
success = false; | |||
} else | |||
this.obj.stats.addStat('manaReservePercent', reserveEvent.reservePercent); | |||
} else | |||
this.obj.stats.addStat('manaReservePercent', -reserveEvent.reservePercent); | |||
} | |||
} | |||
if (spell.targetFurthest) | |||
spell.target = this.obj.aggro.getFurthest(); | |||
else if (spell.targetRandom) | |||
@@ -1,5 +1,7 @@ | |||
const cpnInventory = require('./inventory'); | |||
const maxItems = 50; | |||
module.exports = { | |||
type: 'stash', | |||
@@ -7,6 +9,8 @@ module.exports = { | |||
items: [], | |||
changed: false, | |||
maxItems, | |||
init: function (blueprint) { | |||
let items = blueprint.items || []; | |||
let iLen = items.length; | |||
@@ -18,6 +22,10 @@ module.exports = { | |||
this.blueprint = blueprint; | |||
}, | |||
calculateMaxItems: function (extraSlots) { | |||
this.maxItems = maxItems + extraSlots; | |||
}, | |||
getItem: function (item) { | |||
//Material? | |||
let exists = false; | |||
@@ -57,7 +65,7 @@ module.exports = { | |||
deposit: function (item) { | |||
if (!this.active) | |||
return; | |||
else if (this.items.length >= 50) { | |||
else if (this.items.length >= this.maxItems) { | |||
let isMaterial = this.items.some(stashedItem => item.name === stashedItem.name && (item.quantity || item.material)); | |||
if (!isMaterial) { | |||
this.obj.instance.syncer.queue('onGetMessages', { | |||
@@ -137,12 +145,12 @@ module.exports = { | |||
} | |||
}); | |||
if (this.active && this.items.length > 50) { | |||
if (this.active && this.items.length > this.maxItems) { | |||
obj.instance.syncer.queue('onGetMessages', { | |||
id: this.obj.id, | |||
messages: [{ | |||
class: 'color-redA', | |||
message: 'You have more than 50 items in your stash. In the next version (v0.3.1) you will lose all items that put you over the limit', | |||
message: `You have more than ${this.maxItems} items in your stash. In the next version (v0.3.1) you will lose all items that put you over the limit`, | |||
type: 'info' | |||
}] | |||
}, [obj.serverId]); | |||
@@ -161,7 +169,8 @@ module.exports = { | |||
return { | |||
type: 'stash', | |||
active: this.active, | |||
items: this.items | |||
items: this.items, | |||
maxItems: this.maxItems | |||
}; | |||
}, | |||
@@ -1,13 +1,25 @@ | |||
let events = require('../misc/events'); | |||
const events = require('../misc/events'); | |||
const tos = require('./tos'); | |||
module.exports = { | |||
const config = { | |||
resourceList: [], | |||
uiList: [], | |||
contextMenuActions: { | |||
player: [], | |||
npc: [] | |||
}, | |||
tos | |||
}; | |||
module.exports = { | |||
init: function () { | |||
events.emit('onBeforeGetResourceList', this.resourceList); | |||
events.emit('onBeforeGetResourceList', config.resourceList); | |||
events.emit('onBeforeGetUiList', config.uiList); | |||
events.emit('onBeforeGetContextMenuActions', config.contextMenuActions); | |||
events.emit('onBeforeGetTermsOfService', config.tos); | |||
}, | |||
getResourcesList: function (msg) { | |||
msg.callback(this.resourceList); | |||
getClientConfig: function (msg) { | |||
msg.callback(config); | |||
} | |||
}; |
@@ -1,6 +1,10 @@ | |||
module.exports = { | |||
type: 'stunned', | |||
init: function () { | |||
this.obj.spellbook.stopCasting(); | |||
}, | |||
events: { | |||
beforeMove: function (targetPos) { | |||
targetPos.success = false; | |||
@@ -675,7 +675,19 @@ module.exports = { | |||
} | |||
}, | |||
rare: { | |||
name: 'Fleshripper' | |||
name: 'Fleshripper', | |||
drops: { | |||
rolls: 1, | |||
noRandom: true, | |||
alsoRandom: true, | |||
blueprints: [{ | |||
chance: 80, | |||
name: 'Eagle Feather', | |||
material: true, | |||
sprite: [0, 0], | |||
spritesheet: 'images/questItems.png' | |||
}] | |||
} | |||
} | |||
}, | |||
hermit: { | |||
@@ -278,7 +278,7 @@ module.exports = { | |||
}, { | |||
type: 'setDescription', | |||
desc: descriptionStrings.success, | |||
ttl: 50 | |||
ttl: 100 | |||
}, { | |||
type: 'giveRewards' | |||
}, { | |||
@@ -286,6 +286,6 @@ module.exports = { | |||
}, { | |||
type: 'setDescription', | |||
desc: descriptionStrings.failure, | |||
ttl: 50 | |||
ttl: 100 | |||
}] | |||
}; |
@@ -5,6 +5,9 @@ const balance = { | |||
giantRat: { | |||
clawChance: 5 | |||
}, | |||
enragedRat: { | |||
clawChance: 80 | |||
}, | |||
stinktooth: { | |||
runestoneChance: 10, | |||
recipeChance: 3, | |||
@@ -63,7 +66,20 @@ module.exports = { | |||
rare: { | |||
name: 'Enraged Rat', | |||
cell: 24 | |||
cell: 24, | |||
drops: { | |||
rolls: 1, | |||
noRandom: true, | |||
alsoRandom: true, | |||
blueprints: [{ | |||
chance: balance.enragedRat.clawChance, | |||
name: 'Rat Claw', | |||
material: true, | |||
sprite: [3, 0], | |||
spritesheet: 'images/materials.png' | |||
}] | |||
} | |||
} | |||
}, | |||
@@ -8,6 +8,7 @@ module.exports = { | |||
init: function (instance) { | |||
this.instance = instance; | |||
}, | |||
obtain: function (obj, template) { | |||
let zoneName = template ? template.zoneName : obj.zoneName; | |||
let zonePath = mapList.mapList.find(m => m.name === zoneName).path; | |||
@@ -31,6 +32,10 @@ module.exports = { | |||
if (config.infini.length === 0) | |||
return; | |||
const minPlayerLevel = ~~(obj.instance.map.zone.level[0] * 0.75); | |||
if (obj.stats.values.level < minPlayerLevel) | |||
return; | |||
let pickQuest = null; | |||
if ((template) && (template.type)) | |||
pickQuest = config.infini.find(c => c.type === template.type); | |||
@@ -2,7 +2,7 @@ module.exports = [{ | |||
id: 'runeWhirlwind', | |||
name: 'Rune of Whirlwind', | |||
default: false, | |||
description: 'Wiggle-wiggly woo-woo.', | |||
description: 'You furiously spin in a circle, striking all foes around you.', | |||
item: { | |||
name: 'Rune of Whirlwind', | |||
generate: true, | |||
@@ -20,7 +20,7 @@ module.exports = [{ | |||
id: 'runeAmbush', | |||
name: 'Rune of Ambush', | |||
default: false, | |||
description: 'Wiggle-wiggly woo-woo.', | |||
description: 'Step into the shadows and reappear behind your target before delivering a concussing blow.', | |||
item: { | |||
name: 'Rune of Ambush', | |||
generate: true, | |||
@@ -73,6 +73,14 @@ module.exports = { | |||
return skins; | |||
}, | |||
getExtraStashSlots: function (accountName) { | |||
const account = this.getAccount(accountName); | |||
if (!account) | |||
return 0; | |||
return account.extraStashSlots || 0; | |||
}, | |||
sendMessage: function (player, msg) { | |||
msg = 'Only certain roles can ' + msg + ' at the moment'; | |||
@@ -1,5 +1,5 @@ | |||
module.exports = { | |||
version: '0.4.2', | |||
version: '0.4.3', | |||
port: 4000, | |||
startupMessage: 'Server: ready', | |||
defaultZone: 'fjolarok', | |||
@@ -1,4 +1,4 @@ | |||
const getTargetPos = (physics, obj, m) => { | |||
const getTargetPos = (physics, obj, m, pushback) => { | |||
let targetPos = { | |||
x: m.x, | |||
y: m.y | |||
@@ -14,7 +14,7 @@ const getTargetPos = (physics, obj, m) => { | |||
dx = ~~(dx / Math.abs(dx)); | |||
dy = ~~(dy / Math.abs(dy)); | |||
for (let l = 0; l < this.pushback; l++) { | |||
for (let l = 0; l < pushback; l++) { | |||
if (physics.isTileBlocking(targetPos.x + dx, targetPos.y + dy)) { | |||
if (physics.isTileBlocking(targetPos.x + dx, targetPos.y)) { | |||
if (physics.isTileBlocking(targetPos.x, targetPos.y + dy)) | |||
@@ -89,7 +89,7 @@ module.exports = { | |||
else if (!obj.aggro.canAttack(m)) | |||
continue; | |||
const targetPos = getTargetPos(physics, obj, m); | |||
const targetPos = getTargetPos(physics, obj, m, this.pushback); | |||
let distance = Math.max(Math.abs(m.x - targetPos.x), Math.abs(m.y - targetPos.y)); | |||
let ttl = distance * 125; | |||
@@ -0,0 +1,18 @@ | |||
const version = '0.0.1'; | |||
const content = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus venenatis risus enim, a bibendum est suscipit vel. Donec consectetur justo nec sodales varius. Ut dignissim condimentum lacus, at euismod arcu scelerisque in. Vestibulum et orci quis nulla laoreet placerat. Fusce tempus, tortor eu commodo suscipit, massa urna rhoncus nisi, eget malesuada lorem mauris nec lorem. Vestibulum eu turpis rutrum, auctor nulla eget, placerat mi. Praesent porta, tortor pulvinar euismod ornare, quam sem porta nulla, rhoncus egestas odio nulla sit amet massa. Phasellus non lacus eu massa commodo feugiat. Proin cursus augue nibh, at egestas nibh facilisis at. Nam tempus dictum mollis. In ante ex, commodo ut tortor in, fermentum posuere felis. Donec vel ipsum maximus nisi lacinia finibus non a enim. Maecenas elementum, diam id placerat gravida, mi ex tristique eros, id rhoncus ex diam eget nibh. | |||
Vestibulum non viverra elit. Cras nec turpis nibh. Ut eu diam nisl. Pellentesque scelerisque augue imperdiet, bibendum ante non, imperdiet est. Praesent eu lacus porttitor, maximus sem vel, fringilla arcu. Nunc aliquet dictum justo, eu blandit dolor finibus eget. Vestibulum massa risus, convallis sit amet venenatis ac, viverra interdum diam. Aenean at condimentum enim, ut blandit odio. Proin congue nulla nisi, at consequat ipsum imperdiet ut. Morbi nec varius risus. Mauris dui erat, semper ut massa quis, ornare congue nisl. Duis sed dolor iaculis, egestas ligula nec, pretium libero. Nulla leo elit, lacinia imperdiet nisl et, viverra aliquet ex. Sed quis fringilla felis. | |||
Donec eget ligula tristique, consequat nunc in, porttitor elit. Nulla faucibus eleifend elit eget vestibulum. Nam imperdiet dui nec felis egestas, at dapibus turpis volutpat. Sed faucibus ultrices porta. Aliquam tempus est libero, sed pulvinar erat luctus et. Integer convallis maximus justo, non pulvinar nisl viverra vitae. In hac habitasse platea dictumst. Etiam ut nibh vel odio accumsan fermentum vitae consectetur metus. | |||
Mauris eget erat ipsum. Suspendisse efficitur dictum metus, ac vulputate risus euismod id. Quisque suscipit quis sem ac luctus. Integer tincidunt sit amet massa feugiat auctor. Ut faucibus cursus augue eu ornare. Morbi arcu elit, tincidunt eu elit non, rhoncus egestas justo. Fusce viverra fermentum purus sed finibus. Nunc consectetur, massa vehicula laoreet mattis, sapien neque facilisis quam, vel ultrices mi lacus nec odio. Sed eros sapien, cursus vel euismod a, ornare eu magna. Vestibulum pharetra in sapien vehicula commodo. Aliquam erat volutpat. Duis eget risus augue. Maecenas at lobortis urna. Morbi ultrices porttitor lorem consequat volutpat. Phasellus ac tristique purus, eu vestibulum quam. | |||
In vehicula mauris neque, nec hendrerit enim accumsan quis. Nunc sed sapien ac odio venenatis blandit. Vestibulum et ipsum arcu. Ut lectus dolor, venenatis et massa quis, auctor mollis tellus. Nulla mauris velit, lacinia id ligula id, molestie ullamcorper risus. Sed tincidunt feugiat metus, a mattis eros aliquet non. Sed gravida risus at vestibulum aliquet. Maecenas pellentesque libero eu elit cursus, a blandit massa ultricies. Donec urna eros, facilisis ac lacus eu, vulputate volutpat nibh. Nam feugiat molestie ligula, vitae lacinia nibh interdum in. In pellentesque est et lorem euismod, eu vehicula justo placerat. Mauris quis ligula nec enim consectetur congue. Vestibulum et maximus ex. | |||
`; | |||
module.exports = { | |||
version, | |||
required: true, | |||
content | |||
}; |
@@ -15,6 +15,7 @@ let mapList = require('./config/maps/mapList'); | |||
let sheets = require('./security/sheets'); | |||
let fixes = require('./fixes/fixes'); | |||
let profanities = require('./misc/profanities'); | |||
const routerConfig = require('./security/routerConfig'); | |||
let startup = { | |||
init: function () { | |||
@@ -32,6 +33,7 @@ let startup = { | |||
}, | |||
onModsLoaded: function () { | |||
routerConfig.init(); | |||
classes.init(); | |||
spellsConfig.init(); | |||
spells.init(); | |||
@@ -103,6 +103,7 @@ const reslot = (item, msg) => { | |||
delete item.spell; | |||
delete item.implicitStats; | |||
delete item.power; | |||
delete item.range; | |||
extend(item, newItem); | |||
}; | |||
@@ -138,20 +138,14 @@ module.exports = { | |||
noParse: true | |||
}); | |||
this.doSendMail(playerName, items, callback, result); | |||
}, | |||
doSendMail: async function (playerName, items, callback, result) { | |||
if (result === 'null') | |||
result = null; | |||
result = JSON.parse(result || '[]'); | |||
items.forEach(function (i) { | |||
result.push(i); | |||
}); | |||
result.push(...items); | |||
let itemString = JSON.stringify(result).split('\'').join('`'); | |||
const itemString = JSON.stringify(result).split('\'').join('`'); | |||
await io.setAsync({ | |||
key: playerName, | |||
@@ -5,6 +5,8 @@ module.exports = { | |||
actionQueue: [], | |||
eventListeners: [], | |||
addComponent: function (type, blueprint, isTransfer) { | |||
let cpn = this[type]; | |||
if (!cpn) { | |||
@@ -355,6 +357,21 @@ module.exports = { | |||
} | |||
}, | |||
onEvent: function (eventName, callback) { | |||
const entry = { | |||
eventName, | |||
callback | |||
}; | |||
this.eventListeners.push(entry); | |||
return this.offEvent.bind(this, entry); | |||
}, | |||
offEvent: function (entry) { | |||
this.eventListeners.spliceWhere(e => e === entry); | |||
}, | |||
fireEvent: function (event) { | |||
let args = [].slice.call(arguments, 1); | |||
@@ -373,6 +390,14 @@ module.exports = { | |||
callback.apply(cpn, args); | |||
} | |||
this.eventListeners.forEach(l => { | |||
const { eventName, callback } = l; | |||
if (eventName !== event) | |||
return; | |||
callback.apply(null, args); | |||
}); | |||
if (this.effects) | |||
this.effects.fireEvent(event, args); | |||
if (this.quests) | |||
@@ -14,6 +14,7 @@ module.exports = { | |||
this.players.push(p); | |||
}, | |||
onDisconnect: function (socket) { | |||
let player = this.players.find(p => p.socket.id === socket.id); | |||
@@ -52,6 +53,7 @@ module.exports = { | |||
this.players.spliceWhere(p => p.socket.id === socket.id); | |||
}, | |||
route: function (socket, msg) { | |||
let player = null; | |||
@@ -79,6 +81,17 @@ module.exports = { | |||
) | |||
return; | |||
if (msg.threadModule) { | |||
const source = this.players.find(p => p.socket.id === socket.id); | |||
if (!source) | |||
return; | |||
msg.data.sourceId = source.id; | |||
atlas.send(player.zone, msg); | |||
return; | |||
} | |||
let cpn = player[msg.cpn]; | |||
if (!cpn) | |||
return; | |||
@@ -87,6 +100,7 @@ module.exports = { | |||
if (cpn[method]) | |||
cpn[method](msg); | |||
}, | |||
unzone: function (msg) { | |||
let socket = msg.socket; | |||
let player = this.players.find(p => p.socket.id === socket.id); | |||
@@ -1,5 +1,4 @@ | |||
const serverConfig = require('../config/serverConfig'); | |||
const connections = require('../security/connections'); | |||
const version = serverConfig.version; | |||
const bcrypt = require('bcrypt-nodejs'); | |||
const roles = require('../config/roles'); | |||
@@ -37,7 +36,7 @@ module.exports = { | |||
bcrypt.compare(config.pwd, storedPassword, this.doSaveAll.bind(this, res, config)); | |||
}, | |||
doSaveAll: function (res, config, err, compareResult) { | |||
doSaveAll: async function (res, config, err, compareResult) { | |||
if (!compareResult) | |||
return; | |||
@@ -47,6 +46,8 @@ module.exports = { | |||
if (roleLevel < 9) | |||
return; | |||
await atlas.returnWhenZonesIdle(); | |||
cons.emit('event', { | |||
event: 'onGetMessages', | |||
data: { | |||
@@ -58,7 +59,7 @@ module.exports = { | |||
} | |||
}); | |||
connections.forceSaveAll(); | |||
cons.forceSaveAll(); | |||
res.jsonp({ | |||
success: true | |||
@@ -1,43 +1,28 @@ | |||
const { routerConfig: { allowed, secondaryAllowed, globalAllowed } } = require('./routerConfig'); | |||
module.exports = { | |||
allowedCpn: function (msg) { | |||
let allowed = { | |||
player: ['performAction', 'queueAction', 'move'], | |||
auth: ['login', 'register', 'play', 'getCharacterList', 'getCharacter', 'deleteCharacter', 'getSkinList', 'createCharacter', 'getCustomChannels'], | |||
social: ['chat', 'getInvite', 'acceptInvite', 'declineInvite', 'removeFromParty', 'leaveParty'] | |||
}; | |||
let valid = ((allowed[msg.cpn]) && (allowed[msg.cpn].indexOf(msg.method) > -1)); | |||
let valid = allowed[msg.cpn] && allowed[msg.cpn].includes(msg.method); | |||
if (!valid) | |||
return false; | |||
if (!msg.data.cpn) | |||
return true; | |||
let secondaryAllowed = { | |||
dialogue: ['talk'], | |||
gatherer: ['gather'], | |||
quests: ['complete'], | |||
inventory: ['combineStacks', 'splitStack', 'activateMtx', 'useItem', 'moveItem', 'enchantItem', 'getEnchantMaterials', 'learnAbility', 'unlearnAbility', 'dropItem', 'destroyItem', 'salvageItem', 'stashItem', 'mailItem', 'sortInventory'], | |||
equipment: ['equip', 'unequip', 'setQuickSlot', 'useQuickSlot', 'inspect'], | |||
stash: ['withdraw', 'open'], | |||
trade: ['buySell'], | |||
door: ['lock', 'unlock'], | |||
wardrobe: ['open', 'apply'], | |||
stats: ['respawn'], | |||
passives: ['tickNode', 'untickNode'], | |||
workbench: ['open', 'craft', 'getRecipe'] | |||
}; | |||
return ((secondaryAllowed[msg.data.cpn]) && (secondaryAllowed[msg.data.cpn].indexOf(msg.data.method) > -1)); | |||
const result = secondaryAllowed[msg.data.cpn] && secondaryAllowed[msg.data.cpn].includes(msg.data.method); | |||
return result; | |||
}, | |||
allowedGlobal: function (msg) { | |||
let allowed = { | |||
clientConfig: ['getResourcesList'], | |||
leaderboard: ['requestList'], | |||
cons: ['unzone'] | |||
}; | |||
const result = globalAllowed[msg.module] && globalAllowed[msg.module].includes(msg.method); | |||
return result; | |||
}, | |||
allowedGlobalCall: function (threadModule, method) { | |||
const result = globalAllowed[threadModule] && globalAllowed[threadModule].includes(method); | |||
return ((allowed[msg.module]) && (allowed[msg.module].indexOf(msg.method) > -1)); | |||
return result; | |||
} | |||
}; |
@@ -0,0 +1,36 @@ | |||
let events = require('../misc/events'); | |||
const routerConfig = { | |||
allowed: { | |||
player: ['performAction', 'queueAction', 'move'], | |||
auth: ['login', 'register', 'play', 'getCharacterList', 'getCharacter', 'deleteCharacter', 'getSkinList', 'createCharacter', 'getCustomChannels'], | |||
social: ['chat', 'getInvite', 'acceptInvite', 'declineInvite', 'removeFromParty', 'leaveParty'] | |||
}, | |||
secondaryAllowed: { | |||
dialogue: ['talk'], | |||
gatherer: ['gather'], | |||
quests: ['complete'], | |||
inventory: ['combineStacks', 'splitStack', 'activateMtx', 'useItem', 'moveItem', 'enchantItem', 'getEnchantMaterials', 'learnAbility', 'unlearnAbility', 'dropItem', 'destroyItem', 'salvageItem', 'stashItem', 'mailItem', 'sortInventory'], | |||
equipment: ['equip', 'unequip', 'setQuickSlot', 'useQuickSlot', 'inspect'], | |||
stash: ['withdraw', 'open'], | |||
trade: ['buySell'], | |||
door: ['lock', 'unlock'], | |||
wardrobe: ['open', 'apply'], | |||
stats: ['respawn'], | |||
passives: ['tickNode', 'untickNode'], | |||
workbench: ['open', 'craft', 'getRecipe'] | |||
}, | |||
globalAllowed: { | |||
clientConfig: ['getClientConfig'], | |||
leaderboard: ['requestList'], | |||
cons: ['unzone'] | |||
} | |||
}; | |||
module.exports = { | |||
routerConfig, | |||
init: function () { | |||
events.emit('onBeforeGetRouterConfig', routerConfig); | |||
} | |||
}; |
@@ -55,6 +55,8 @@ module.exports = { | |||
o.messagePrefix = o.messageprefix; | |||
delete o.messageprefix; | |||
o.extraStashSlots = ~~o.extrastashslots; | |||
if (typeof (o.items) === 'string') | |||
o.items = JSON.parse(o.items || '[]'); | |||
if (typeof (o.skins) === 'string') | |||
@@ -12,6 +12,7 @@ module.exports = { | |||
name: 'Key to the world', | |||
sprite: [12, 0], | |||
keyId: 'world' | |||
}] | |||
}], | |||
extrastashslots: 10 | |||
}] | |||
}; |
@@ -0,0 +1,37 @@ | |||
let lastId = 0; | |||
const list = []; | |||
const complete = id => { | |||
list.spliceWhere(l => l === id); | |||
}; | |||
const register = () => { | |||
const nextId = ++lastId; | |||
list.push(nextId); | |||
return complete.bind(null, nextId); | |||
}; | |||
const returnWhenDone = async () => { | |||
if (!list.length) | |||
return; | |||
return new Promise(res => { | |||
const checker = () => { | |||
if (!list.length) { | |||
res(); | |||
return; | |||
} | |||
setTimeout(checker, 100); | |||
}; | |||
checker(); | |||
}); | |||
}; | |||
module.exports = { | |||
register, | |||
returnWhenDone | |||
}; |
@@ -72,6 +72,11 @@ module.exports = { | |||
if (!router.allowedCpn(msg)) | |||
return; | |||
cons.route(socket, msg); | |||
} else if (msg.threadModule) { | |||
if (!router.allowedGlobalCall(msg.threadModule, msg.method)) | |||
return; | |||
cons.route(socket, msg); | |||
} else { | |||
if (!router.allowedGlobal(msg)) | |||
@@ -92,7 +97,18 @@ module.exports = { | |||
file = file.replace('/' + root + '/', ''); | |||
if (root === 'server' && (file.indexOf('mods') === -1 || file.indexOf('png') === -1)) | |||
const validRequest = ( | |||
root !== 'server' || | |||
( | |||
file.includes('mods/') && | |||
( | |||
file.includes('.png') || | |||
file.includes('/ui/') | |||
) | |||
) | |||
); | |||
if (!validRequest) | |||
return null; | |||
res.sendFile(file, { | |||
@@ -5,6 +5,8 @@ let connections = require('../security/connections'); | |||
let serverConfig = require('../config/serverConfig'); | |||
let events = require('../misc/events'); | |||
const listenersOnZoneIdle = []; | |||
module.exports = { | |||
nextId: 0, | |||
lastCallbackId: 0, | |||
@@ -220,6 +222,34 @@ module.exports = { | |||
serverObj.player.broadcastSelf(); | |||
this.addObject(obj, true, true); | |||
}, | |||
onZoneIdle: function (thread) { | |||
listenersOnZoneIdle.forEach(l => l(thread)); | |||
} | |||
}, | |||
returnWhenZonesIdle: async function () { | |||
return new Promise(res => { | |||
const waiting = [...this.threads]; | |||
const onZoneIdle = thread => { | |||
waiting.spliceWhere(w => w === thread); | |||
if (waiting.length) | |||
return; | |||
listenersOnZoneIdle.spliceWhere(l => l === onZoneIdle); | |||
res(); | |||
}; | |||
listenersOnZoneIdle.push(onZoneIdle); | |||
this.threads.forEach(t => { | |||
t.worker.send({ | |||
method: 'notifyOnceIdle' | |||
}); | |||
}); | |||
}); | |||
} | |||
}; |
@@ -12,6 +12,7 @@ let scheduler = require('../misc/scheduler'); | |||
let mail = require('../mail/mail'); | |||
let herbs = require('../config/herbs'); | |||
let eventEmitter = require('../misc/events'); | |||
const transactions = require('../security/transactions'); | |||
module.exports = { | |||
instances: [], | |||
@@ -27,7 +28,7 @@ module.exports = { | |||
herbs.init(); | |||
map.init(args); | |||
let fakeInstance = { | |||
const fakeInstance = { | |||
objects: objects, | |||
syncer: syncer, | |||
physics: physics, | |||
@@ -211,5 +212,13 @@ module.exports = { | |||
} | |||
}); | |||
} | |||
}, | |||
notifyOnceIdle: async function () { | |||
await transactions.returnWhenDone(); | |||
process.send({ | |||
method: 'onZoneIdle' | |||
}); | |||
} | |||
}; |
@@ -5,13 +5,13 @@ const canPath = (pos, positions, maxDistance = 0) => { | |||
return positions.some(p => { | |||
const path = physics.getPath(pos, p); | |||
//Are we on the position? | |||
if (!path.length) | |||
return true; | |||
if (!path.length) | |||
return (p.x === pos.x && p.y === pos.y); | |||
const { x, y } = path[path.length - 1]; | |||
//Can we get close enough to the position? | |||
const isCloseEnough = Math.max(Math.abs(p.x - x), Math.abs(p.y - y)) <= maxDistance; | |||
if (isCloseEnough) | |||
if (isCloseEnough) | |||
return true; | |||
return false; | |||
@@ -91,6 +91,8 @@ process.on('message', m => { | |||
if (found) | |||
break; | |||
} | |||
} else if (m.method) | |||
} else if (m.threadModule) | |||
global[m.threadModule][m.method](m.data); | |||
else if (m.method) | |||
instancer[m.method](m.args); | |||
}); |