@@ -4,7 +4,7 @@ | |||
Built with NodeJS, JS, HTML and CSS. | |||
[Play Now](http://play.isleward.com/) | [Discord](https://discord.gg/gnsn7ZP) | [Subreddit](https://www.reddit.com/r/isleward) | [Blog](http://blog.isleward.com/) | [Wiki](http://wiki.isleward.com/Main_Page) | [Twitter](https://twitter.com/bigbadwofl) | [Patreon](http://patreon.com/bigbadwaffle) | |||
[Play Now](http://play.isleward.com/) | [Discord](https://discord.gg/gnsn7ZP) | [Subreddit](https://www.reddit.com/r/isleward) | [Blog](http://blog.isleward.com/) | [Wiki](http://wiki.isleward.com/Main_Page) | [Twitter](https://twitter.com/playisleward) | [Facebook](https://www.facebook.com/isleward/) | [Patreon](http://patreon.com/bigbadwaffle) | |||
[How to contribute](CONTRIBUTING.md) | |||
@@ -14,4 +14,4 @@ Built with NodeJS, JS, HTML and CSS. | |||
* [Linux](https://gitlab.com/Isleward/isleward/wikis/installation-and-usage-(linux)) | |||
* [MacOS](https://gitlab.com/Isleward/isleward/wikis/installation-and-usage-(macos)) | |||
![Ingame Screenshots](http://i.imgur.com/p4ktJ5O.png) | |||
![Ingame Screenshots](http://i.imgur.com/p4ktJ5O.png) |
@@ -1,8 +1,4 @@ | |||
var extend = require('extend'); | |||
var requirejs = require('requirejs'); | |||
global.extend = require('../../../src/server/misc/clone'); | |||
global.extend = extend; | |||
requirejs(['sim'], function (sim) { | |||
sim.init(); | |||
}); | |||
const sim = require('./sim'); | |||
sim(); |
@@ -1,182 +1,181 @@ | |||
define([ | |||
'../../src/server/config/spellsConfig', | |||
'../../src/server/combat/combat' | |||
], function ( | |||
config, | |||
combat | |||
) { | |||
var spells = config.spells; | |||
var max = false; | |||
var maxTarget = false; | |||
spells['harvest life'] = { | |||
statType: ['str', 'int'], | |||
statMult: 0.76, | |||
auto: true, | |||
cdMax: 6, | |||
manaCost: 0, | |||
range: 1, | |||
random: { | |||
damage: [1.5, 5.7], | |||
healPercent: [5, 15] | |||
} | |||
}; | |||
var bloodBarrierMult = 1.25; | |||
spells['skeleton melee'] = { | |||
statType: ['str', 'int'], | |||
statMult: 0.27 * bloodBarrierMult, | |||
auto: true, | |||
cdMax: 5, | |||
manaCost: 0, | |||
range: 1, | |||
random: { | |||
damage: [1, 3.8] | |||
} | |||
}; | |||
var level = 20; | |||
var hp = [ | |||
32.70, | |||
65.40, | |||
98.10, | |||
130.80, | |||
163.50, | |||
196.20, | |||
228.90, | |||
261.60, | |||
294.30, | |||
327.00, | |||
359.70, | |||
392.40, | |||
425.10, | |||
457.80, | |||
490.50, | |||
523.20, | |||
555.90, | |||
588.60, | |||
621.30, | |||
654.00 | |||
]; | |||
var hpMax = [ | |||
160.48, | |||
324.53, | |||
489.90, | |||
660.79, | |||
841.44, | |||
1036.21, | |||
1249.50, | |||
1485.85, | |||
1749.87, | |||
2046.32, | |||
2380.05, | |||
2756.08, | |||
3179.54, | |||
3655.72, | |||
4190.09, | |||
4788.27, | |||
5456.08, | |||
6199.50, | |||
7024.73, | |||
7938.17 | |||
]; | |||
return { | |||
init: function () { | |||
var res = []; | |||
for (var s in spells) { | |||
var c = spells[s]; | |||
var d = c.random.damage || c.random.healing; | |||
if (!d) | |||
continue; | |||
var damage = d[0]; | |||
if (max) | |||
damage = d[1]; | |||
var config = { | |||
statType: c.statType, | |||
statMult: c.statMult, | |||
element: c.element, | |||
cd: c.cdMax, | |||
damage: damage, | |||
noCrit: true, | |||
noMitigate: !!c.random.healing, | |||
source: { | |||
stats: { | |||
values: { | |||
level: level, | |||
elementArcanePercent: 0, | |||
elementFrostPercent: 0, | |||
elementPoisonPercent: 0, | |||
elementHolyPercent: 0, | |||
elementFirePercent: 0 | |||
} | |||
}, | |||
}, | |||
target: { | |||
stats: { | |||
values: { | |||
armor: maxTarget ? (level * 50) : (level * 20), | |||
elementAllResist: maxTarget ? 100 : 0, | |||
elementArcaneResist: 0, | |||
elementFrostResist: 0, | |||
elementPoisonResist: 0, | |||
elementHolyResist: 0, | |||
elementFireResist: 0 | |||
} | |||
} | |||
const spellsConfig = require('../../../src/server/config/spellsConfig'); | |||
const combat = require('../../../src/server/combat/combat'); | |||
let spells = spellsConfig.spells; | |||
let max = true; | |||
let maxTarget = false; | |||
spells['harvest life'] = { | |||
statType: ['str', 'int'], | |||
statMult: 1, | |||
cdMax: 10, | |||
castTimeMax: 3, | |||
manaCost: 5, | |||
isAttack: true, | |||
range: 1, | |||
random: { | |||
damage: [4, 14], | |||
healPercent: [10, 30] | |||
} | |||
}; | |||
/*let bloodBarrierMult = 1.25; | |||
spells['skeleton melee'] = { | |||
statType: ['str', 'int'], | |||
statMult: 1 * bloodBarrierMult, | |||
auto: true, | |||
cdMax: 5, | |||
manaCost: 0, | |||
range: 1, | |||
random: { | |||
damage: [1, 3.8] | |||
} | |||
};*/ | |||
let level = 20; | |||
let hp = [ | |||
32.70, | |||
65.40, | |||
98.10, | |||
130.80, | |||
163.50, | |||
196.20, | |||
228.90, | |||
261.60, | |||
294.30, | |||
327.00, | |||
359.70, | |||
392.40, | |||
425.10, | |||
457.80, | |||
490.50, | |||
523.20, | |||
555.90, | |||
588.60, | |||
621.30, | |||
654.00 | |||
]; | |||
let hpMax = [ | |||
160.48, | |||
324.53, | |||
489.90, | |||
660.79, | |||
841.44, | |||
1036.21, | |||
1249.50, | |||
1485.85, | |||
1749.87, | |||
2046.32, | |||
2380.05, | |||
2756.08, | |||
3179.54, | |||
3655.72, | |||
4190.09, | |||
4788.27, | |||
5456.08, | |||
6199.50, | |||
7024.73, | |||
7938.17 | |||
]; | |||
module.exports = function () { | |||
let res = []; | |||
for (let s in spells) { | |||
let c = spells[s]; | |||
c.statType = c.statType || 'int'; | |||
let d = c.random.damage || c.random.healing; | |||
if (!d) | |||
continue; | |||
let damage = d[0]; | |||
if (max) | |||
damage = d[1]; | |||
var config = { | |||
statType: c.statType, | |||
statMult: c.statMult, | |||
element: c.element, | |||
cd: c.cdMax, | |||
damage: damage, | |||
noCrit: true, | |||
noMitigate: true, | |||
source: { | |||
stats: { | |||
values: { | |||
level: level, | |||
elementArcanePercent: 0, | |||
elementFrostPercent: 0, | |||
elementPoisonPercent: 0, | |||
elementHolyPercent: 0, | |||
elementFirePercent: 0 | |||
} | |||
}; | |||
var stat = c.statType; | |||
if (!stat.push) | |||
stat = [stat]; | |||
var minStat = level; | |||
var maxStat = level * 5; | |||
var mult = (stat.length == 1) ? 1 : 1; | |||
stat.forEach(s => config.source.stats.values[s] = (max ? maxStat : minStat) * mult); | |||
var amount = combat.getDamage(config).amount; | |||
var duration = c.random.i_duration; | |||
if (duration) | |||
amount *= max ? duration[1] : duration[0]; | |||
amount /= c.cdMax; | |||
var critChance = max ? 0.5 : 0.05; | |||
var critMult = max ? 3 : 1.5; | |||
amount = (amount * (1 - critChance)) + (amount * critChance * critMult); | |||
res.push({ | |||
name: s, | |||
dpt: (~~(amount * 10) / 10), | |||
cd: c.cdMax, | |||
mana: c.manaCost || '', | |||
tpk: ~~((maxTarget ? hpMax : hp)[level - 1] / amount), | |||
amount: amount | |||
}); | |||
} | |||
}, | |||
target: { | |||
stats: { | |||
values: { | |||
armor: maxTarget ? (level * 50) : (level * 20), | |||
elementAllResist: maxTarget ? 100 : 0, | |||
elementArcaneResist: 0, | |||
elementFrostResist: 0, | |||
elementPoisonResist: 0, | |||
elementHolyResist: 0, | |||
elementFireResist: 0 | |||
} | |||
} | |||
} | |||
res = res.sort((a, b) => (b.dpt - a.dpt)); | |||
console.log(); | |||
console.log('ability dpt'); | |||
console.log(); | |||
res.forEach(function (r) { | |||
var gap = new Array(20 - r.name.length); | |||
console.log(r.name + ': ' + gap.join(' ') + r.dpt + ' ' + r.tpk + ' ticks ' + (~~((r.tpk / 2.85) * 10) / 10) + ' seconds'); | |||
}); | |||
console.log(); | |||
} | |||
}; | |||
}); | |||
}; | |||
let stat = c.statType; | |||
if (!stat.push) | |||
stat = [stat]; | |||
const minStat = level; | |||
const maxStat = level * 10; | |||
stat.forEach(ss => { | |||
config.source.stats.values[ss] = (max ? maxStat : minStat); | |||
}); | |||
let amount = combat.getDamage(config).amount; | |||
let critChance = max ? 0.5 : 0.05; | |||
let critMult = max ? 3 : 1.5; | |||
let castTimeMax = c.castTimeMax; | |||
amount = (((amount / 100) * (100 - critChance)) + (((amount / 100) * critChance) * (critMult / 100))); | |||
let duration = c.random.i_duration; | |||
if (duration) | |||
amount *= max ? duration[1] : duration[0]; | |||
const div = (c.cdMax + castTimeMax) || 1; | |||
amount /= div; | |||
res.push({ | |||
name: s, | |||
dpt: ~~(~~(amount * 10) / 10), | |||
cd: c.cdMax, | |||
mana: c.manaCost || '', | |||
tpk: ~~((maxTarget ? hpMax : hp)[level - 1] / amount), | |||
amount: amount | |||
}); | |||
} | |||
res = res.sort((a, b) => (b.dpt - a.dpt)); | |||
console.log(); | |||
console.log('ability dpt'); | |||
console.log(); | |||
res.forEach(function (r) { | |||
let gap = new Array(20 - r.name.length); | |||
console.log(r.name + ': ' + gap.join(' ') + r.dpt + ' ' + r.tpk + ' ticks ' + (~~((r.tpk / 2.85) * 10) / 10) + ' seconds'); | |||
}); | |||
console.log(); | |||
}; | |||
@@ -52,6 +52,26 @@ r.db('live').table('character') | |||
}); | |||
}); | |||
r.db('live').table('character') | |||
.concatMap(row => { | |||
return row('value')('components') | |||
.filter(cpn => { | |||
return cpn('type').eq('inventory'); | |||
}) | |||
.concatMap(c => { | |||
return [{ | |||
name: row('value')('name'), | |||
account: row('value')('account'), | |||
cpn: c('items').filter(item => { | |||
return item('quantity').ge(30000); | |||
}) | |||
}]; | |||
}) | |||
.filter(c => { | |||
return c('cpn').count().ge(1); | |||
}); | |||
}); | |||
//Play time per account from low to high | |||
r.db('live').table('character') | |||
.concatMap(row => { | |||
@@ -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; |
@@ -26,7 +26,7 @@ body { | |||
> .right { | |||
position: absolute; | |||
right: 10px; | |||
top: 92px; | |||
top: 94px; | |||
} | |||
&.mobile { | |||
@@ -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 { | |||
@@ -3,7 +3,62 @@ define([ | |||
], function ( | |||
) { | |||
return { | |||
showNames: false | |||
const config = { | |||
showNames: true, | |||
showQuests: 'on', | |||
showEvents: true, | |||
playAudio: true, | |||
qualityIndicators: 'off', | |||
unusableIndicators: 'off' | |||
}; | |||
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 `iwd_opt_${key.toLowerCase()}`; | |||
}; | |||
config.set = (key, value) => { | |||
config[key] = value; | |||
window.localStorage.setItem(getKeyName(key), config[key]); | |||
}; | |||
config.toggle = key => { | |||
if (valueChains[key]) | |||
config[key] = getNextValue(key); | |||
else | |||
config[key] = !config[key]; | |||
window.localStorage.setItem(getKeyName(key), config[key]); | |||
}; | |||
const loadValue = key => { | |||
const keyName = getKeyName(key); | |||
const { [keyName]: currentValue = '{unset}' } = localStorage; | |||
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,7 @@ define([ | |||
'js/input', | |||
'js/system/events', | |||
'js/resources', | |||
'js/sound/sound', | |||
'ui/templates/online/online', | |||
'ui/templates/tooltips/tooltips' | |||
], function ( | |||
@@ -19,7 +20,8 @@ define([ | |||
numbers, | |||
input, | |||
events, | |||
resources | |||
resources, | |||
sound | |||
) { | |||
return { | |||
hasFocus: true, | |||
@@ -51,6 +53,8 @@ define([ | |||
$(window).on('contextmenu', this.onContextMenu.bind(this)); | |||
sound.init(); | |||
objects.init(); | |||
renderer.init(); | |||
input.init(); | |||
@@ -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; | |||
} | |||
} | |||
}; | |||
}); |
@@ -194,28 +194,11 @@ define([ | |||
alpha = Math.min(Math.max(0.15, alpha), 0.65); | |||
//Hack for xmas | |||
tile = 3; | |||
let min = Math.min( | |||
(i + j), | |||
(w - i + j), | |||
(i + h - j), | |||
(w - i + h - j) | |||
); | |||
let tree = false; | |||
let val = min + (Math.random() * 10); | |||
if (val < 23) { | |||
if (val < 18) | |||
tree = true; | |||
tile = 184; | |||
} | |||
if (mRandom() < 0.35) { | |||
tile = { | |||
5: 6, | |||
3: 0, | |||
4: 1, | |||
184: 185, | |||
53: 54 | |||
}[tile]; | |||
} | |||
@@ -234,25 +217,6 @@ define([ | |||
} | |||
container.addChild(sprite); | |||
if (tree) { | |||
let s = [216, 216, 217, 217, 217, 217, 217, 218, 219, 219, 219][~~(Math.random() * 11)]; | |||
s += 224; | |||
tile = new pixi.Sprite(this.getTexture('sprites', s)); | |||
tile.alpha = 0.7 + (Math.random() * 0.3); | |||
tile.position.x = i * scale; | |||
tile.position.y = j * scale; | |||
tile.width = scale; | |||
tile.height = scale; | |||
if (Math.random() < 0.5) { | |||
tile.position.x += scale; | |||
tile.scale.x = -scaleMult; | |||
} | |||
container.addChild(tile); | |||
} | |||
} | |||
} | |||
}, | |||
@@ -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); | |||
} | |||
}; | |||
}); |
@@ -30,7 +30,7 @@ define([ | |||
'spells', | |||
'messages', | |||
'online', | |||
'options', | |||
'mainMenu', | |||
'context', | |||
'party', | |||
'help', | |||
@@ -55,7 +55,8 @@ define([ | |||
'wardrobe', | |||
'passives', | |||
'workbench', | |||
'middleHud' | |||
'middleHud', | |||
'options' | |||
].forEach(function (u) { | |||
this.build(u); | |||
}, this); | |||
@@ -100,15 +101,25 @@ define([ | |||
onUiKeyDown: function (keyEvent) { | |||
if (keyEvent.key === 'esc') { | |||
this.uis.forEach(function (u) { | |||
const closedUis = []; | |||
this.uis.forEach(u => { | |||
if (!u.modal || !u.shown) | |||
return; | |||
keyEvent.consumed = true; | |||
u.hide(); | |||
closedUis.push(u); | |||
}); | |||
$('.uiOverlay').hide(); | |||
events.emit('onHideContextMenu'); | |||
closedUis.forEach(c => { | |||
if (c.afterHide) | |||
c.afterHide(); | |||
}); | |||
} else if (['o', 'j', 'h', 'i'].indexOf(keyEvent.key) > -1) | |||
$('.uiOverlay').hide(); | |||
}, | |||
@@ -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 () { | |||
@@ -1,16 +1,11 @@ | |||
@import "../../../css/colors.less"; | |||
@leftWidth: 400px; | |||
@rightWidth: 400px; | |||
@boxPadding: 35px; | |||
@logoWidth: 559px; | |||
@logoHeight: 200px; | |||
@boxHeight: 300px; | |||
@messageHeight: @boxPadding; | |||
@totalWidth: (@leftWidth + @rightWidth + @boxPadding); | |||
@totalHeight: (@logoHeight + @boxHeight + (@boxPadding * 3) + @messageHeight); | |||
@@ -25,7 +20,8 @@ | |||
margin-bottom: (@boxPadding * 2); | |||
} | |||
.left, .right { | |||
.left, | |||
.right { | |||
height: @boxHeight; | |||
float: left; | |||
background-color: #3a3b4a; | |||
@@ -50,7 +46,9 @@ | |||
&.selected { | |||
background-color: fade(@white, 25%); | |||
} | |||
} | |||
} | |||
.right { | |||
@@ -74,6 +72,7 @@ | |||
image-rendering: crisp-edges; | |||
display: none; | |||
} | |||
} | |||
.info { | |||
@@ -100,10 +99,11 @@ | |||
text-align: right; | |||
color: darken(@white, 25%); | |||
} | |||
} | |||
.btn { | |||
background-color: @redC; | |||
background-color: @blueC; | |||
width: calc((100% - (16px * 2)) / 3); | |||
float: left; | |||
margin-right: 16px; | |||
@@ -114,18 +114,20 @@ | |||
} | |||
&:hover { | |||
background-color: @redB; | |||
background-color: @blueB; | |||
} | |||
&.deleting { | |||
background-color: @yellow; | |||
color: @black; | |||
background-color: @red; | |||
color: @white; | |||
} | |||
} | |||
.spacer-h { | |||
height: 91px; | |||
} | |||
} | |||
.message { | |||
@@ -135,12 +137,12 @@ | |||
float: left; | |||
text-align: center; | |||
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); | |||
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); | |||
} | |||
} | |||
.mobile .uiCharacters { | |||
@@ -149,4 +151,5 @@ | |||
.logo { | |||
display: none; | |||
} | |||
} |
@@ -1,5 +1,5 @@ | |||
<div class="uiCharacters"> | |||
<img class="logo" src="server/mods/iwd-merrywinter/images/logo.png" alt=""> | |||
<img class="logo" src="images/logo_0.png" alt=""> | |||
<div class="left"> | |||
<div class="character">loading characters...</div> | |||
</div> | |||
@@ -13,32 +13,41 @@ define([ | |||
tpl: template, | |||
modal: true, | |||
config: null, | |||
postRender: function () { | |||
this.onEvent('onContextMenu', this.onContextMenu.bind(this)); | |||
this.onEvent('onHideContextMenu', this.onMouseDown.bind(this)); | |||
this.onEvent('mouseDown', this.onMouseDown.bind(this)); | |||
this.onEvent('onUiKeyDown', this.onUiKeyDown.bind(this)); | |||
$('.ui-container').on('mouseup', this.onMouseDown.bind(this)); | |||
}, | |||
onContextMenu: function (config, e) { | |||
this.config = config; | |||
let container = this.el.find('.list') | |||
.empty(); | |||
config.forEach(function (c, i) { | |||
let text = c.text ? c.text : c; | |||
config.forEach((c, i) => { | |||
const text = (c.text || c); | |||
const hotkey = c.hotkey; | |||
let html = templateItem | |||
const html = templateItem | |||
.replace('$TEXT$', text); | |||
let row = $(html) | |||
const row = $(html) | |||
.appendTo(container); | |||
if (hotkey) | |||
row.find('.hotkey').html(`(${hotkey})`); | |||
if (c.callback) | |||
row.on('click', this.onClick.bind(this, i, c.callback)); | |||
else | |||
row.addClass('no-hover'); | |||
}, this); | |||
}); | |||
this.el | |||
.css({ | |||
@@ -57,6 +66,20 @@ define([ | |||
if (!this.el.is(':visible') || (e && (e.cancel || e.button === 2))) | |||
return; | |||
this.config = null; | |||
this.el.hide(); | |||
}, | |||
onUiKeyDown: function (keyEvent) { | |||
if (!this.config || !this.el.is(':visible')) | |||
return; | |||
const configEntry = this.config.find(({ hotkey }) => hotkey === keyEvent.key); | |||
if (!configEntry) | |||
return; | |||
configEntry.callback(); | |||
keyEvent.consumed = true; | |||
this.el.hide(); | |||
} | |||
}; | |||
@@ -22,6 +22,8 @@ | |||
padding: 4px; | |||
color: @white; | |||
cursor: pointer; | |||
display: flex; | |||
justify-content: center; | |||
&:hover:not(.no-hover) { | |||
background-color: @lightGray; | |||
@@ -31,6 +33,14 @@ | |||
line-height: 2px; | |||
color: @blueB; | |||
} | |||
.hotkey { | |||
color: @blueA; | |||
&:not(:empty) { | |||
margin-left: 8px; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -1 +1 @@ | |||
<div class="option">$TEXT$</div> | |||
<div class="option">$TEXT$<div class='hotkey'></div></div> |
@@ -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); | |||
@@ -1,13 +1,9 @@ | |||
@import "../../../css/colors.less"; | |||
@boxHeight: 276px; | |||
@boxPadding: 27px; | |||
@logoWidth: 559px; | |||
@logoHeight: 200px; | |||
@messageHeight: @boxPadding; | |||
@totalWidth: (@logoWidth + (@boxPadding * 2) + 427px); | |||
@totalHeight: (@logoHeight + @boxHeight + (@boxPadding * 3) + @messageHeight); | |||
@@ -28,7 +24,8 @@ | |||
width: 613px; | |||
background-color: #3a3b4a; | |||
.left, .right { | |||
.left, | |||
.right { | |||
float: left; | |||
height: 100%; | |||
} | |||
@@ -37,17 +34,18 @@ | |||
width: calc(100% - (@boxPadding * 2) - 160px); | |||
padding: @boxPadding 0px @boxPadding @boxPadding; | |||
.txtClass, .txtCostume { | |||
.txtClass, | |||
.txtCostume { | |||
cursor: pointer; | |||
-webkit-user-select: none; | |||
&:active { | |||
background-color: darken(@gray, 20%); | |||
} | |||
} | |||
.txtClass { | |||
} | |||
.label { | |||
@@ -73,16 +71,17 @@ | |||
background-color: @grayD; | |||
&:first-child { | |||
margin-right: 15px; | |||
margin-right: 10px; | |||
} | |||
&:last-child { | |||
margin-left: 15px; | |||
margin-left: 10px; | |||
} | |||
&:hover { | |||
background-color: @grayC; | |||
} | |||
} | |||
.txtCostume { | |||
@@ -92,7 +91,9 @@ | |||
align-items: center; | |||
padding: 0px; | |||
} | |||
} | |||
} | |||
} | |||
.right { | |||
@@ -117,6 +118,7 @@ | |||
image-rendering: crisp-edges; | |||
background: url('../../../images/charas.png') -64px 0px; | |||
} | |||
} | |||
.textbox { | |||
@@ -128,7 +130,7 @@ | |||
} | |||
.btn { | |||
background-color: @redC; | |||
background-color: @blueC; | |||
width: calc((100% - @boxPadding) / 2); | |||
float: left; | |||
margin-right: @boxPadding; | |||
@@ -139,8 +141,9 @@ | |||
} | |||
&:hover { | |||
background-color: @redB; | |||
background-color: @blueB; | |||
} | |||
} | |||
.message { | |||
@@ -151,6 +154,7 @@ | |||
text-align: center; | |||
color: @redA; | |||
} | |||
} | |||
.box-right { | |||
@@ -195,6 +199,7 @@ | |||
&:after { | |||
content: ' ✔'; | |||
} | |||
} | |||
&.disabled { | |||
@@ -204,10 +209,15 @@ | |||
&:hover { | |||
color: @white; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.mobile .uiCreateCharacter { | |||
@@ -226,11 +236,13 @@ | |||
width: 45%; | |||
.skinBox { | |||
height: 70px; | |||
height: 35px; | |||
} | |||
} | |||
.box-right { | |||
width: 45%; | |||
} | |||
} |
@@ -1,5 +1,5 @@ | |||
<div class="uiCreateCharacter"> | |||
<img class="logo" src="server/mods/iwd-merrywinter/images/logo.png" alt=""> | |||
<img class="logo" src="images/logo_0.png" alt=""> | |||
<div class="box-left"> | |||
<div class="left"> | |||
<div class="label">name</div> | |||
@@ -27,13 +27,9 @@ | |||
<div class="heading">prophecies</div> | |||
<div class="list"> | |||
<div prophecy="austere" tooltip="you live a simple life and will only use common and magic items" class="prophecy">austere</div> | |||
<div prophecy="butcher" tooltip="you can engage other butchers in combat" class="prophecy">butcher</div> | |||
<div prophecy="crushable" tooltip="take quadruple damage from all sources" class="prophecy">crushable</div> | |||
<div prophecy="hardcore" tooltip="death is permanent" class="prophecy">hardcore</div> | |||
<div prophecy="titangrip" tooltip="gain double stats from weapons" class="prophecy">titangrip</div> | |||
<div class="prophecy disabled">waterproof</div> | |||
<div class="prophecy disabled">wolfborn</div> | |||
<div class="prophecy disabled">demonic</div> | |||
<div prophecy="butcher" tooltip="you will engage other butchers in combat" class="prophecy">butcher</div> | |||
<div prophecy="crushable" tooltip="you will take quadruple damage from all sources" class="prophecy">crushable</div> | |||
<div prophecy="hardcore" tooltip="your death will be permanent" class="prophecy">hardcore</div> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -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(); | |||
} | |||
}; | |||
}); |
@@ -1,11 +1,10 @@ | |||
@import "../../../css/colors.less"; | |||
@btnSize: 64px; | |||
.uiEvents { | |||
margin-top: 10px; | |||
width: 320px; | |||
width: 326px; | |||
.heading { | |||
color: @yellow; | |||
padding: 8px; | |||
@@ -35,6 +34,7 @@ | |||
.name { | |||
color: @blue; | |||
} | |||
} | |||
&.ready { | |||
@@ -45,22 +45,24 @@ | |||
.description { | |||
display: none; | |||
} | |||
} | |||
} | |||
} | |||
.btnClose { | |||
.btnCollapse { | |||
display: none; | |||
} | |||
} | |||
.mobile .uiEvents { | |||
background-color: fade(@darkGray, 90%); | |||
margin-top: 0px; | |||
&:not(.active) { | |||
position: absolute; | |||
top: 0px; | |||
right: 232px; | |||
width: @btnSize; | |||
height: @btnSize; | |||
@@ -78,6 +80,7 @@ | |||
> * { | |||
display: none; | |||
} | |||
} | |||
&.active { | |||
@@ -90,17 +93,20 @@ | |||
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%; | |||
height: 100%; | |||
background: url('../../../images/uiIcons.png') -448px 0px; | |||
} | |||
} | |||
} | |||
} |
@@ -15,12 +15,18 @@ define([ | |||
stats: null, | |||
items: null, | |||
quickItem: null, | |||
postRender: function () { | |||
this.onEvent('onGetStats', this.events.onGetStats.bind(this)); | |||
this.onEvent('onGetPortrait', this.events.onGetPortrait.bind(this)); | |||
this.onEvent('onGetItems', this.events.onGetItems.bind(this)); | |||
this.onEvent('onDestroyItems', this.events.onDestroyItems.bind(this)); | |||
this.onEvent('onKeyDown', this.events.onKeyDown.bind(this)); | |||
this.find('.quickItem') | |||
.on('mousemove', this.showQuickItemTooltip.bind(this, true)) | |||
.on('mouseleave', this.showQuickItemTooltip.bind(this, false)); | |||
}, | |||
build: function () { | |||
@@ -61,7 +67,9 @@ define([ | |||
}); | |||
}, | |||
showQuickItemTooltip: function (show, item, e) { | |||
showQuickItemTooltip: function (show, e) { | |||
const item = this.quickItem; | |||
if (show) { | |||
let ttPos = null; | |||
if (e) { | |||
@@ -112,6 +120,7 @@ define([ | |||
this.items = items; | |||
const quickItem = items.find(f => f.has('quickSlot')); | |||
this.quickItem = quickItem; | |||
if (!quickItem) { | |||
const oldQuickItem = this.find('.quickItem').data('item'); | |||
if (oldQuickItem) | |||
@@ -132,13 +141,14 @@ define([ | |||
let imgX = -quickItem.sprite[0] * 64; | |||
let imgY = -quickItem.sprite[1] * 64; | |||
let el = this.find('.quickItem').show(); | |||
const el = this.find('.quickItem').show(); | |||
if (el.data('item') && el.data('item').id === quickItem.id) | |||
return; | |||
el | |||
.data('item', quickItem) | |||
.find('.icon') | |||
.css('background', 'url("' + spritesheet + '") ' + imgX + 'px ' + imgY + 'px') | |||
.on('mousemove', this.showQuickItemTooltip.bind(this, true, quickItem)) | |||
.on('mouseleave', this.showQuickItemTooltip.bind(this, false, quickItem)); | |||
.css('background', 'url("' + spritesheet + '") ' + imgX + 'px ' + imgY + 'px'); | |||
}, | |||
onKeyDown: function (key) { | |||
@@ -4,14 +4,16 @@ define([ | |||
'html!ui/templates/inventory/template', | |||
'css!ui/templates/inventory/styles', | |||
'html!ui/templates/inventory/templateItem', | |||
'js/input' | |||
'js/input', | |||
'js/config' | |||
], function ( | |||
events, | |||
client, | |||
template, | |||
styles, | |||
tplItem, | |||
input | |||
input, | |||
config | |||
) { | |||
return { | |||
tpl: template, | |||
@@ -33,6 +35,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)); | |||
@@ -78,6 +85,7 @@ define([ | |||
.on('mouseup', this.onMouseDown.bind(this, null, null, false)) | |||
.on('mousemove', this.onHover.bind(this, itemEl, item)) | |||
.on('mouseleave', this.hideTooltip.bind(this, itemEl, item)) | |||
.addClass('empty') | |||
.children() | |||
.remove(); | |||
@@ -135,9 +143,32 @@ define([ | |||
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}`); | |||
} | |||
}, | |||
onToggleQualityIndicators: function (state) { | |||
this.el.removeClass('quality-off quality-bottom quality-border quality-background'); | |||
const className = `quality-${state.toLowerCase()}`; | |||
this.el.addClass(className); | |||
}, | |||
onToggleUnusableIndicators: function (state) { | |||
this.el.removeClass('unusable-off unusable-border unusable-top unusable-background'); | |||
const className = `unusable-${state.toLowerCase()}`; | |||
this.el.addClass(className); | |||
}, | |||
onClick: function (item) { | |||
let msg = { | |||
item: item, | |||
@@ -274,7 +305,8 @@ define([ | |||
}, | |||
salvage: { | |||
text: 'salvage', | |||
callback: this.performItemAction.bind(this, item, 'salvageItem') | |||
callback: this.performItemAction.bind(this, item, 'salvageItem'), | |||
hotkey: 'f' | |||
}, | |||
stash: { | |||
text: 'stash', | |||
@@ -323,56 +355,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) | |||
config.push(menuItems.split); | |||
if (item.quantity > 1 && !item.quest) | |||
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,162 @@ | |||
.quantity { | |||
color: @yellow; | |||
} | |||
} | |||
&.new { | |||
.quantity { | |||
color: @green; | |||
} | |||
} | |||
&:hover { | |||
.icon { | |||
filter: brightness(160%); | |||
-moz-filter: brightness(160%); | |||
} | |||
} | |||
} | |||
} | |||
&.quality-border .grid .item:not(.empty), | |||
&.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; | |||
} | |||
} | |||
&.quality-bottom .grid .item:not(.empty) { | |||
&:before { | |||
border-width: 0px 0px 5px 0px; | |||
} | |||
} | |||
&.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; | |||
} | |||
} | |||
&.unusable-border .grid .item.no-equip, | |||
&.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; | |||
} | |||
} | |||
&.unusable-top .grid .item.no-equip { | |||
&:after { | |||
border-width: 5px 0px 0px 0px; | |||
} | |||
} | |||
&.quality-border.unusable-border .grid .item.no-equip:not(.quality-0), | |||
&.quality-border.unusable-top .grid .item.no-equip:not(.quality-0) { | |||
&:after { | |||
top: 5px; | |||
left: 5px; | |||
width: calc(100% - 10px); | |||
height: calc(100% - 10px); | |||
} | |||
} | |||
.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 +264,9 @@ | |||
&.btnSortInv { | |||
float: left; | |||
} | |||
} | |||
} | |||
.split-box { | |||
@@ -168,6 +298,7 @@ | |||
padding-top: 8px; | |||
margin: auto; | |||
} | |||
} | |||
.bottom { | |||
@@ -185,7 +316,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 +343,20 @@ | |||
&:hover { | |||
background-color: @blackA; | |||
} | |||
} | |||
&.btnSplit { | |||
margin-top: 45px; | |||
clear: both; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -73,7 +73,7 @@ define([ | |||
uiFactory.preload(); | |||
$('.uiLoginExtra').remove(); | |||
this.el.remove(); | |||
this.destroy(); | |||
} else | |||
this.el.find('.message').html(res); | |||
}, | |||
@@ -1,40 +1,45 @@ | |||
@import "../../../css/colors.less"; | |||
@leftWidth: 400px; | |||
@rightWidth: 400px; | |||
@boxPadding: 16px; | |||
@logoWidth: 559px; | |||
@logoHeight: 200px; | |||
@boxHeight: 169px; | |||
@messageHeight: @boxPadding; | |||
@totalWidth: @rightWidth; | |||
@totalHeight: (@logoHeight + @boxHeight + (@boxPadding * 3) + @messageHeight); | |||
.uiLogin { | |||
display: none; | |||
width: @totalWidth; | |||
height: @totalHeight; | |||
width: 562px; | |||
height: 475px; | |||
margin-top: -80px; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
> * { | |||
flex-shrink: 0; | |||
} | |||
.logo { | |||
width: 562px; | |||
height: @logoHeight; | |||
margin-left: ((@totalWidth / 2) - (@logoWidth / 2)); | |||
margin-bottom: (@boxPadding * 3); | |||
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); | |||
} | |||
.right { | |||
height: @boxHeight; | |||
background-color: #3a3b4a; | |||
width: @rightWidth; | |||
padding: @boxPadding; | |||
.label, input { | |||
.label, | |||
input { | |||
float: left; | |||
} | |||
@@ -48,16 +53,19 @@ | |||
width: 70%; | |||
} | |||
input, .textbox, input:-webkit-autofill { | |||
box-shadow: 0 0 0px 1000px darken(@gray, 15%) inset; | |||
input, | |||
.textbox, | |||
input:-webkit-autofill { | |||
box-shadow: 0 0 0px 1000px darken(@gray, 15%) inset; | |||
color: @white; | |||
-webkit-text-fill-color: @white; | |||
margin-bottom: @boxPadding; | |||
} | |||
/* We duplicate this for firefox which doesn't like the webkit selector */ | |||
input, .textbox { | |||
box-shadow: 0 0 0px 1000px darken(@gray, 15%) inset; | |||
input, | |||
.textbox { | |||
box-shadow: 0 0 0px 1000px darken(@gray, 15%) inset; | |||
color: @white; | |||
-webkit-text-fill-color: @white; | |||
margin-bottom: @boxPadding; | |||
@@ -70,21 +78,17 @@ | |||
margin-top: 36px; | |||
float: left; | |||
text-align: center; | |||
color: @orange; | |||
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); | |||
color: @orangeA; | |||
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); | |||
} | |||
.top-buttons { | |||
@@ -92,7 +96,7 @@ | |||
height: 35px; | |||
.btn { | |||
background-color: @redC; | |||
background-color: @blueC; | |||
width: calc((100% - @boxPadding) / 2); | |||
float: left; | |||
margin-right: @boxPadding; | |||
@@ -103,54 +107,58 @@ | |||
} | |||
&:hover { | |||
background-color: @redB; | |||
background-color: @blueB; | |||
} | |||
} | |||
} | |||
} | |||
.news { | |||
margin-left: -100px; | |||
margin-top: 40px; | |||
color: @white; | |||
width: 600px; | |||
text-align: center; | |||
cursor: pointer; | |||
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); | |||
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); | |||
} | |||
.spacer-h { | |||
height: 61px; | |||
} | |||
} | |||
.uiLoginExtra { | |||
.extra { | |||
position: absolute; | |||
left: 10px; | |||
left: 50%; | |||
bottom: 10px; | |||
transform: translateX(-50%); | |||
display: flex; | |||
.btn { | |||
width: 180px; | |||
padding-left: 10px; | |||
padding-right: 10px; | |||
width: 100%; | |||
margin-bottom: 10px; | |||
background-color: @redD; | |||
margin-left: 10px; | |||
background-color: @blueD; | |||
color: @white; | |||
&:last-child { | |||
margin-bottom: 0px; | |||
&:first-child { | |||
margin-left: 0px; | |||
} | |||
&:hover { | |||
background-color: @redC; | |||
background-color: @blueC; | |||
} | |||
} | |||
} | |||
.version { | |||
@@ -159,23 +167,32 @@ | |||
bottom: 10px; | |||
color: @yellow; | |||
cursor: pointer; | |||
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); | |||
} | |||
.mobile { | |||
.uiLogin { | |||
margin-top: 0px; | |||
.logo { | |||
margin-bottom: 30px; | |||
} | |||
.news { | |||
margin-top: 20px; | |||
} | |||
-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); | |||
} | |||
} | |||
.mobile .uiLogin { | |||
margin-top: 0px; | |||
} |
@@ -1,5 +1,5 @@ | |||
<div class="uiLogin"> | |||
<img class="logo" src="server/mods/iwd-merrywinter/images/logo.png" alt=""> | |||
<img class="logo" src="images/logo_0.png" alt=""> | |||
<div class="right"> | |||
<div class="label">username</div> | |||
<input type="text" class="el textbox txtUsername" placeholder="username"> | |||
@@ -11,11 +11,11 @@ | |||
</div> | |||
<div class="message"></div> | |||
</div> | |||
<div class="news" location="https://gitlab.com/Isleward/isleward/tags/v0.4.0">[ 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.0">v0.4.1</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"> | |||
@@ -93,37 +93,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) { | |||
@@ -133,7 +153,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(); | |||
}, | |||
@@ -141,12 +166,12 @@ define([ | |||
this.clickKey(' '); | |||
}, | |||
backspace: () => { | |||
'<<': () => { | |||
elInput.val(elInput.val().slice(0, -1)); | |||
this.find('.input').html(elInput.val()); | |||
}, | |||
enter: () => { | |||
send: () => { | |||
this.sendChat({ | |||
which: 13 | |||
}); | |||
@@ -372,13 +397,23 @@ define([ | |||
} | |||
let textbox = this.find('input'); | |||
let val = textbox.val() | |||
let config = { | |||
success: true, | |||
message: textbox.val() | |||
}; | |||
events.emit('onBeforeChat', config); | |||
let val = config.message | |||
.split('<') | |||
.join('<') | |||
.split('>') | |||
.join('>'); | |||
textbox.blur(); | |||
if (!config.success) | |||
return; | |||
if (val.trim() === '') | |||
return; | |||
@@ -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,72 +16,135 @@ 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.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)); | |||
}, | |||
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); | |||
}, | |||
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); | |||
}, | |||
toggle: function () { | |||
@@ -91,19 +155,29 @@ define([ | |||
if (this.shown) { | |||
this.show(); | |||
events.emit('onShowOverlay', this.el); | |||
this.build(); | |||
} else { | |||
this.hide(); | |||
events.emit('onHideOverlay', this.el); | |||
events.emit('onToggleMainMenu'); | |||
} | |||
}, | |||
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,73 @@ | |||
.uiOptions { | |||
display: none; | |||
width: 400px; | |||
background-color: @gray; | |||
border: 4px solid @lightGray; | |||
background-color: @blackC; | |||
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; | |||
} | |||
border: 5px solid @blackB; | |||
> .heading { | |||
color: @blueA; | |||
width: 100%; | |||
height: 36px; | |||
background-color: @blackB; | |||
position: relative; | |||
.heading-text { | |||
padding-top: 8px; | |||
margin: auto; | |||
} | |||
} | |||
.btnIssue { | |||
background-color: @red; | |||
.bottom { | |||
padding: 10px; | |||
.list { | |||
display: flex; | |||
flex-direction: column; | |||
.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; | |||
} | |||
} | |||
&:hover { | |||
background-color: lighten(@red, 15%); | |||
color: @black; | |||
} | |||
} | |||
} | |||
.mobile .uiOptions { | |||
.btnScreen { | |||
.item.screen { | |||
display: none; | |||
} | |||
.btnNames { | |||
display: block; | |||
} | |||
} |
@@ -1,9 +1,37 @@ | |||
<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="item screen"> | |||
<div class="name">Fullscreen</div> | |||
<div class="value">Off</div> | |||
</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="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="item audio"> | |||
<div class="name">Audio</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'); | |||
} | |||
}; | |||
}); |
@@ -1,9 +1,8 @@ | |||
@import "../../../css/colors.less"; | |||
@btnSize: 64px; | |||
.uiQuests { | |||
width: 320px; | |||
width: 326px; | |||
.heading { | |||
color: @yellow; | |||
@@ -40,6 +39,7 @@ | |||
.description { | |||
display: none; | |||
} | |||
} | |||
.reward { | |||
@@ -55,6 +55,7 @@ | |||
> .reward { | |||
display: block; | |||
} | |||
} | |||
.ready-text { | |||
@@ -65,6 +66,7 @@ | |||
.name { | |||
color: @blue; | |||
} | |||
} | |||
&.ready { | |||
@@ -84,24 +86,34 @@ | |||
display: block; | |||
color: @white; | |||
} | |||
} | |||
} | |||
} | |||
.btn { | |||
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: ''; | |||
@@ -117,6 +129,7 @@ | |||
> * { | |||
display: none; | |||
} | |||
} | |||
&.active { | |||
@@ -129,17 +142,20 @@ | |||
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%; | |||
height: 100%; | |||
background: url('../../../images/uiIcons.png') -448px 0px; | |||
} | |||
} | |||
} | |||
} |
@@ -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,4 +16,4 @@ | |||
<div class="tier"></div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> |
@@ -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; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -1,5 +1,4 @@ | |||
@import "../../../css/colors.less"; | |||
@pad: 8px; | |||
@btnSize: 64px; | |||
@@ -7,7 +6,6 @@ | |||
position: absolute; | |||
right: 10px; | |||
top: 10px; | |||
height: (@btnSize + @pad); | |||
.spell { | |||
@@ -29,7 +27,6 @@ | |||
&:hover { | |||
filter: brightness(160%); | |||
-moz-filter: brightness(160%); | |||
} | |||
@@ -61,17 +58,16 @@ | |||
color: @white; | |||
font-size: 18px; | |||
z-index: 2; | |||
text-shadow: 2px 2px 0 #2d2136, | |||
-2px -2px 0 #2d2136, | |||
2px -2px 0 #2d2136, | |||
-2px 2px 0 #2d2136, | |||
2px 2px 0 #2d2136; | |||
text-shadow: 2px 2px 0 #2d2136, -2px -2px 0 #2d2136, 2px -2px 0 #2d2136, -2px 2px 0 #2d2136, 2px 2px 0 #2d2136; | |||
&.no-mana { | |||
color: @red; | |||
} | |||
} | |||
} | |||
} | |||
.mobile .uiSpells { | |||
@@ -83,11 +79,11 @@ | |||
display: flex; | |||
flex-direction: column-reverse; | |||
.spell { | |||
position: relative; | |||
margin: 8px 0px 0px 0px; | |||
margin: 10px 0px 0px 0px; | |||
background-color: fade(#3a3b4a, 90%); | |||
border: none; | |||
.hotkey { | |||
display: none; | |||
@@ -95,12 +91,14 @@ | |||
&.active:before { | |||
content: ''; | |||
width: 4px; | |||
height: 100%; | |||
left: -4px; | |||
top: 0%; | |||
background-color: @greenB; | |||
position: absolute; | |||
width: 5px; | |||
height: 100%; | |||
left: -5px; | |||
top: 0%; | |||
background-color: @greenB; | |||
position: absolute; | |||
} | |||
} | |||
} |
@@ -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; | |||
} |
@@ -1,5 +1,5 @@ | |||
const scheduler = require('../../misc/scheduler'); | |||
const loginRewards = require('../../config/loginRewards'); | |||
const rewardGenerator = require('../../misc/rewardGenerator'); | |||
const mail = require('../../mail/mail'); | |||
const calculateDaysSkipped = (oldTime, newTime) => { | |||
@@ -37,7 +37,8 @@ module.exports = async (cpnAuth, data, character, cbDone) => { | |||
const accountInfo = cpnAuth.accountInfo; | |||
const time = scheduler.getTime(); | |||
const lastLogin = accountInfo.lastLogin; | |||
let { lastLogin, loginStreak } = accountInfo; | |||
accountInfo.lastLogin = time; | |||
if ( | |||
@@ -53,13 +54,22 @@ module.exports = async (cpnAuth, data, character, cbDone) => { | |||
} | |||
const daysSkipped = calculateDaysSkipped(lastLogin, time); | |||
if (daysSkipped === 1) | |||
accountInfo.loginStreak++; | |||
else | |||
accountInfo.loginStreak -= (daysSkipped - 1); | |||
if (daysSkipped === 1) | |||
loginStreak++; | |||
else | |||
loginStreak = 1; | |||
loginStreak = Math.max(1, Math.min(21, loginStreak)); | |||
accountInfo.loginStreak = loginStreak; | |||
const itemCount = 1 + ~~(loginStreak / 2); | |||
const rewards = rewardGenerator(itemCount); | |||
if (!rewards) { | |||
cbDone(); | |||
return; | |||
} | |||
accountInfo.loginStreak = Math.min(1, Math.max(21, accountInfo.loginStreak)); | |||
rewards[0].msg = `Daily login reward for ${loginStreak} day${(loginStreak > 1) ? 's' : ''}:`; | |||
const rewards = loginRewards.generate(accountInfo.loginStreak); | |||
mail.sendMail(character.name, rewards, cbDone); | |||
}; |
@@ -28,6 +28,10 @@ module.exports = { | |||
}; | |||
}, | |||
isSlotEmpty: function (slot) { | |||
return !this.eq.has(slot); | |||
}, | |||
autoEquip: function (itemId) { | |||
if (!this.doAutoEq) | |||
return; | |||
@@ -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 | |||
@@ -39,7 +41,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) | |||
@@ -286,13 +289,18 @@ module.exports = { | |||
}, | |||
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 +682,9 @@ module.exports = { | |||
ban: function (msg) { | |||
ban(this, msg); | |||
}, | |||
rezone: function (msg) { | |||
rezone(this, msg); | |||
} | |||
}; |
@@ -23,6 +23,8 @@ module.exports = { | |||
if (nodes.length === 0) | |||
return; | |||
const { obj: { equipment, stats } } = this; | |||
let firstNode = nodes[0]; | |||
this.gathering = firstNode; | |||
@@ -30,17 +32,14 @@ module.exports = { | |||
let ttlMax = firstNode.resourceNode.ttl || this.defaultTtlMax; | |||
if (firstNode.resourceNode.nodeType === 'fish') { | |||
let rod = this.obj.equipment.eq.tool; | |||
if (!rod) { | |||
if (equipment.isSlotEmpty('tool')) { | |||
this.sendAnnouncement('You need a fishing rod to fish'); | |||
this.gathering = null; | |||
return; | |||
} | |||
rod = this.obj.inventory.findItem(rod); | |||
let statCatchSpeed = Math.min(150, this.obj.stats.values.catchSpeed); | |||
let statCatchSpeed = Math.min(150, stats.values.catchSpeed); | |||
ttlMax *= (1 - (statCatchSpeed / 200)); | |||
} | |||
@@ -270,7 +270,7 @@ module.exports = { | |||
splitStack: function (msg) { | |||
let item = this.findItem(msg.itemId); | |||
if (!item || !item.quantity || item.quantity <= msg.stackSize || msg.stackSize < 1) | |||
if (!item || !item.quantity || item.quantity <= msg.stackSize || msg.stackSize < 1 || item.quest) | |||
return; | |||
const hasSpace = this.hasSpace(item, true); | |||
@@ -449,9 +449,9 @@ module.exports = { | |||
}, | |||
mailItem: async function (msg) { | |||
return; | |||
let item = this.findItem(msg.itemId); | |||
if ((!item) || (item.noDrop) || (item.quest)) { | |||
if (!item || item.noDrop || item.quest) { | |||
this.resolveCallback(msg); | |||
return; | |||
} | |||
@@ -476,12 +476,12 @@ 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); | |||
if (!blocked) | |||
this.obj.instance.mail.sendMail(msg.recipient, [mappedItem]); | |||
this.resolveCallback(msg); | |||
}, | |||
@@ -555,9 +555,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) { | |||
@@ -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,11 +1,15 @@ | |||
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) | |||
return; | |||
const { obj } = cpnSocial; | |||
const sendMessage = cpnSocial.sendMessage.bind(cpnSocial); | |||
msg.data.message = msg.data.message | |||
.split('<') | |||
.join('<') | |||
@@ -19,7 +23,7 @@ module.exports = (cpnSocial, msg) => { | |||
return; | |||
if (cpnSocial.muted) { | |||
cpnSocial.sendMessage('You have been muted from talking', 'color-redA'); | |||
sendMessage('You have been muted from talking', 'color-redA'); | |||
return; | |||
} | |||
@@ -34,10 +38,10 @@ module.exports = (cpnSocial, msg) => { | |||
if (history.length > 0) { | |||
if (history[history.length - 1].msg === messageString) { | |||
cpnSocial.sendMessage('You have already sent that message', 'color-redA'); | |||
sendMessage('You have already sent that message', 'color-redA'); | |||
return; | |||
} else if (history.length >= 3) { | |||
cpnSocial.sendMessage('You are sending too many messages', 'color-redA'); | |||
sendMessage('You are sending too many messages', 'color-redA'); | |||
return; | |||
} | |||
} | |||
@@ -47,17 +51,12 @@ module.exports = (cpnSocial, msg) => { | |||
return; | |||
if (!msg.data.item && !profanities.isClean(messageString)) { | |||
cpnSocial.sendMessage('Profanities detected in message. Blocked.', 'color-redA'); | |||
sendMessage('Profanities detected in message. Blocked.', 'color-redA'); | |||
return; | |||
} | |||
let playerLevel = cpnSocial.obj.level; | |||
let playedTime = cpnSocial.obj.stats.stats.played * 1000; | |||
let sessionStart = cpnSocial.obj.player.sessionStart; | |||
let sessionDelta = time - sessionStart; | |||
if (playerLevel < 3 || playedTime + sessionDelta < 180000) { | |||
cpnSocial.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'); | |||
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; | |||
} | |||
@@ -66,9 +65,9 @@ module.exports = (cpnSocial, msg) => { | |||
time: time | |||
}); | |||
let charname = cpnSocial.obj.auth.charname; | |||
let charname = obj.auth.charname; | |||
let msgStyle = roles.getRoleMessageStyle(cpnSocial.obj) || ('color-grayB'); | |||
let msgStyle = roles.getRoleMessageStyle(obj) || ('color-grayB'); | |||
let msgEvent = { | |||
source: charname, | |||
@@ -83,7 +82,7 @@ module.exports = (cpnSocial, msg) => { | |||
else if (messageString[0] === '%') | |||
cpnSocial.sendPartyMessage(msg); | |||
else { | |||
let prefix = roles.getRoleMessagePrefix(cpnSocial.obj) || ''; | |||
let prefix = roles.getRoleMessagePrefix(obj) || ''; | |||
cons.emit('event', { | |||
event: 'onGetMessages', | |||
@@ -93,7 +92,7 @@ module.exports = (cpnSocial, msg) => { | |||
message: prefix + charname + ': ' + msg.data.message, | |||
item: msg.data.item, | |||
type: 'chat', | |||
source: cpnSocial.obj.name | |||
source: obj.name | |||
}] | |||
} | |||
}); | |||
@@ -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) | |||
@@ -391,16 +391,17 @@ module.exports = { | |||
preDeath: function (source) { | |||
const obj = this.obj; | |||
let deathEvent = {}; | |||
let killSource = source; | |||
if (source.follower) | |||
killSource = source.follower.master; | |||
if (killSource.player) | |||
killSource.stats.kill(obj); | |||
const deathEvent = { | |||
source: killSource | |||
}; | |||
obj.fireEvent('afterDeath', deathEvent); | |||
if (obj.player) { | |||
@@ -265,10 +265,16 @@ module.exports = { | |||
let targetTrade = target.trade; | |||
let item = this.obj.inventory.destroyItem(msg.itemId, 1); | |||
const item = this.obj.inventory.findItem(msg.itemId); | |||
if (!item) | |||
return; | |||
const oldQuantity = item.quantity; | |||
this.obj.inventory.destroyItem(msg.itemId); | |||
if (oldQuantity) | |||
item.quantity = oldQuantity; | |||
let worth = ~~(item.worth * targetTrade.markup.buy); | |||
this.gold += worth; | |||
@@ -1,6 +1,10 @@ | |||
module.exports = { | |||
type: 'stunned', | |||
init: function () { | |||
this.obj.spellbook.stopCasting(); | |||
}, | |||
events: { | |||
beforeMove: function (targetPos) { | |||
targetPos.success = false; | |||
@@ -0,0 +1,28 @@ | |||
module.exports = { | |||
id: null, | |||
init: function () { | |||
const { instance, id } = this; | |||
const mob = instance.objects.find(o => o.id === id); | |||
if (!mob) { | |||
this.end = true; | |||
return; | |||
} | |||
this.instance.syncer.queue('onGetObject', { | |||
x: mob.x, | |||
y: mob.y, | |||
components: [{ | |||
type: 'attackAnimation', | |||
row: 0, | |||
col: 4 | |||
}] | |||
}, -1); | |||
mob.destroyed = true; | |||
this.end = true; | |||
} | |||
}; |
@@ -0,0 +1,9 @@ | |||
module.exports = { | |||
init: function () { | |||
const event = this.event; | |||
event.nextPhase = event.phases.length; | |||
this.end = true; | |||
} | |||
}; |
@@ -0,0 +1,26 @@ | |||
module.exports = { | |||
init: function (event) { | |||
const { config, rewards, eventManager } = event; | |||
event.participators.forEach(p => { | |||
const rList = [{ | |||
nameLike: 'Ancient Carp', | |||
removeAll: true | |||
}]; | |||
const pRewards = rewards[p.name]; | |||
if (pRewards && pRewards.length) | |||
rList.push(...pRewards); | |||
if (rList.length > 1) | |||
rList[1].msg = `${config.name} reward:`; | |||
eventManager.instance.mail.sendMail(p.name, rList); | |||
}); | |||
if (config.events && config.events.afterGiveRewards) | |||
config.events.afterGiveRewards(event); | |||
this.end = true; | |||
} | |||
}; |
@@ -0,0 +1,27 @@ | |||
module.exports = { | |||
gotoPhaseIndex: null, | |||
repeats: 0, | |||
init: function () { | |||
if (!this.repeats) { | |||
this.end = true; | |||
return; | |||
} | |||
const event = this.event; | |||
const currentPhaseIndex = event.phases.findIndex(p => p === this); | |||
for (let i = this.gotoPhaseIndex; i < currentPhaseIndex; i++) { | |||
const phase = event.phases[i]; | |||
delete phase.end; | |||
delete phase.destroyed; | |||
} | |||
event.nextPhase = this.gotoPhaseIndex; | |||
this.repeats--; | |||
this.end = true; | |||
} | |||
}; |
@@ -0,0 +1,15 @@ | |||
module.exports = { | |||
update: function () { | |||
const anyMobsAlive = this.event.objects.some(o => { | |||
if (!o.mob) | |||
return false; | |||
else if (!o.destroyed) | |||
return true; | |||
return false; | |||
}); | |||
if (!anyMobsAlive) | |||
this.end = true; | |||
} | |||
}; |
@@ -0,0 +1,28 @@ | |||
module.exports = { | |||
id: null, | |||
mob: null, | |||
pos: { | |||
x: 0, | |||
y: 0 | |||
}, | |||
init: function () { | |||
const { instance, id, pos: { x, y } } = this; | |||
this.mob = instance.objects.find(o => o.id === id); | |||
this.mob.mob.originX = x; | |||
this.mob.mob.originY = y; | |||
this.mob.mob.goHome = true; | |||
}, | |||
update: function () { | |||
const { pos: { x: targetX, y: targetY } } = this; | |||
const { mob: { x, y } } = this; | |||
const distance = Math.max(Math.abs(x - targetX), Math.abs(y - targetY)); | |||
if (distance > 0) | |||
return; | |||
this.end = true; | |||
} | |||
}; |
@@ -0,0 +1,7 @@ | |||
module.exports = { | |||
desc: null, | |||
init: function (event) { | |||
event.eventManager.setEventDescription(event.config.name, this.desc); | |||
} | |||
}; |
@@ -1,12 +1,97 @@ | |||
let mobBuilder = require('../../world/mobBuilder'); | |||
const buildMob = (objects, mobConfig, x, y, mobIndex) => { | |||
const { id, sheetName, cell, name, properties, originX, originY, maxChaseDistance, dialogue, trade, chats, events } = mobConfig; | |||
let mob = objects.buildObjects([{ | |||
x: x, | |||
y: y, | |||
sheetName: sheetName || 'mobs', | |||
cell: cell, | |||
name: name, | |||
properties: properties | |||
}]); | |||
mobBuilder.build(mob, mobConfig); | |||
if (id) | |||
mob.id = id.split('$').join(mobIndex); | |||
if (originX) { | |||
mob.mob.originX = originX; | |||
mob.mob.originY = originY; | |||
mob.mob.goHome = true; | |||
mob.mob.maxChaseDistance = maxChaseDistance; | |||
//This is a hack to make mobs that run somewhere able to take damage | |||
delete mob.mob.events.beforeTakeDamage; | |||
} | |||
if (dialogue) { | |||
mob.addComponent('dialogue', { | |||
config: dialogue.config | |||
}); | |||
if (dialogue.auto) { | |||
mob.dialogue.trigger = objects.buildObjects([{ | |||
properties: { | |||
x: mob.x - 1, | |||
y: mob.y - 1, | |||
width: 3, | |||
height: 3, | |||
cpnNotice: { | |||
actions: { | |||
enter: { | |||
cpn: 'dialogue', | |||
method: 'talk', | |||
args: [{ | |||
targetName: 'angler nayla' | |||
}] | |||
}, | |||
exit: { | |||
cpn: 'dialogue', | |||
method: 'stopTalk' | |||
} | |||
} | |||
} | |||
} | |||
}]); | |||
} | |||
} | |||
if (trade) | |||
mob.addComponent('trade', trade); | |||
if (chats) | |||
mob.addComponent('chatter', chats); | |||
if (events) { | |||
mob.addBuiltComponent({ | |||
type: 'eventComponent', | |||
events: events | |||
}); | |||
} | |||
return mob; | |||
}; | |||
const spawnAnimation = (syncer, { x, y }) => { | |||
syncer.queue('onGetObject', { | |||
x: x, | |||
y: y, | |||
components: [{ | |||
type: 'attackAnimation', | |||
row: 0, | |||
col: 4 | |||
}] | |||
}, -1); | |||
}; | |||
module.exports = { | |||
spawnRect: null, | |||
mobs: null, | |||
init: function () { | |||
let objects = this.instance.objects; | |||
let spawnRect = this.spawnRect; | |||
const { spawnRect, instance: { objects, syncer } } = this; | |||
if (!this.mobs.push) | |||
this.mobs = [this.mobs]; | |||
@@ -60,79 +145,15 @@ module.exports = { | |||
this.spawnAnimation(mob); | |||
this.event.objects.push(mob); | |||
} else { | |||
let mob = objects.buildObjects([{ | |||
x: x, | |||
y: y, | |||
sheetName: l.sheetName || 'mobs', | |||
cell: l.cell, | |||
name: l.name, | |||
properties: l.properties | |||
}]); | |||
mobBuilder.build(mob, l); | |||
this.spawnAnimation(mob); | |||
if (l.id) { | |||
let id = l.id.split('$').join(i); | |||
mob.id = id; | |||
} | |||
const mob = buildMob(objects, l, x, y, i); | |||
this.event.objects.push(mob); | |||
if (l.dialogue) { | |||
mob.addComponent('dialogue', { | |||
config: l.dialogue.config | |||
}); | |||
if (l.dialogue.auto) { | |||
mob.dialogue.trigger = objects.buildObjects([{ | |||
properties: { | |||
x: mob.x - 1, | |||
y: mob.y - 1, | |||
width: 3, | |||
height: 3, | |||
cpnNotice: { | |||
actions: { | |||
enter: { | |||
cpn: 'dialogue', | |||
method: 'talk', | |||
args: [{ | |||
targetName: 'angler nayla' | |||
}] | |||
}, | |||
exit: { | |||
cpn: 'dialogue', | |||
method: 'stopTalk' | |||
} | |||
} | |||
} | |||
} | |||
}]); | |||
} | |||
} | |||
if (l.trade) | |||
mob.addComponent('trade', l.trade); | |||
if (l.chats) | |||
mob.addComponent('chatter', l.chats); | |||
mob.event = this.event; | |||
spawnAnimation(syncer, mob); | |||
} | |||
} | |||
}, this); | |||
if (!this.endMark) | |||
this.end = true; | |||
}, | |||
spawnAnimation: function (mob) { | |||
this.instance.syncer.queue('onGetObject', { | |||
x: mob.x, | |||
y: mob.y, | |||
components: [{ | |||
type: 'attackAnimation', | |||
row: 0, | |||
col: 4 | |||
}] | |||
}, -1); | |||
} | |||
}; |
@@ -1,3 +1,10 @@ | |||
module.exports = { | |||
oldTtl: null, | |||
init: function () { | |||
if (!this.oldTtl) | |||
this.oldTtl = this.ttl; | |||
this.ttl = this.oldTtl; | |||
} | |||
}; |
@@ -1,72 +0,0 @@ | |||
const config = [{ | |||
name: 'Iron Bar', | |||
sprite: [0, 0], | |||
quality: 0, | |||
chance: 15 | |||
}, { | |||
name: 'Cloth Scrap', | |||
sprite: [0, 1], | |||
quality: 0, | |||
chance: 15 | |||
}, { | |||
name: 'Leather Scrap', | |||
sprite: [0, 7], | |||
quality: 0, | |||
chance: 15 | |||
}, { | |||
name: 'Skyblossom', | |||
sprite: [1, 2], | |||
quality: 0, | |||
chance: 8 | |||
}, { | |||
name: 'Common Essence', | |||
sprite: [0, 2], | |||
quality: 0, | |||
chance: 5 | |||
}, { | |||
name: 'Magic Essence', | |||
sprite: [0, 3], | |||
quality: 1, | |||
chance: 2 | |||
}, { | |||
name: 'Rare Essence', | |||
sprite: [0, 4], | |||
quality: 2, | |||
chance: 1 | |||
}]; | |||
let pool = []; | |||
config.forEach(function (c) { | |||
for (let i = 0; i < c.chance; i++) | |||
pool.push(c.name); | |||
}); | |||
module.exports = { | |||
generate: function (streak) { | |||
let items = []; | |||
const itemCount = 1 + ~~(streak / 2); | |||
for (let i = 0; i < itemCount; i++) { | |||
let pickName = pool[~~(Math.random() * pool.length)]; | |||
const pick = config.find(f => f.name === pickName); | |||
let item = items.find(f => f.name === pickName); | |||
if (!item) { | |||
items.push({ | |||
name: pick.name, | |||
material: true, | |||
quality: pick.quality, | |||
sprite: pick.sprite, | |||
quantity: 1 | |||
}); | |||
} else | |||
item.quantity++; | |||
} | |||
if (items.length > 0) | |||
items[0].msg = `Daily login reward for ${streak} day${(streak > 1) ? 's' : ''}:`; | |||
return items; | |||
} | |||
}; |
@@ -0,0 +1,291 @@ | |||
const { mobs: { rat: { level, faction, grantRep, regular: { drops } } } } = require('../zone'); | |||
const rewardGenerator = require('../../../../misc/rewardGenerator'); | |||
const rewardConfig = { | |||
regularKill: [{ | |||
name: 'Iron Bar', | |||
sprite: [0, 0], | |||
quality: 0, | |||
quantity: 5, | |||
chance: 10 | |||
}, { | |||
name: 'Cloth Scrap', | |||
sprite: [0, 1], | |||
quality: 0, | |||
quantity: 5, | |||
chance: 10 | |||
}, { | |||
name: 'Leather Scrap', | |||
sprite: [0, 7], | |||
quality: 0, | |||
quantity: 5, | |||
chance: 10 | |||
}, { | |||
name: 'Muddy Runestone', | |||
material: true, | |||
quality: 0, | |||
sprite: [6, 0], | |||
spritesheet: 'images/materials.png', | |||
chance: 1 | |||
}, { | |||
name: 'Epic Essence', | |||
material: true, | |||
sprite: [0, 5], | |||
quality: 3, | |||
chance: 1 | |||
}, { | |||
name: 'Rat Claw', | |||
material: true, | |||
sprite: [3, 0], | |||
spritesheet: 'images/materials.png', | |||
chance: 3 | |||
}] | |||
}; | |||
const descriptionStrings = { | |||
leadup: 'A bandit alchemist has been spotted in the sewer tunnels.', | |||
active: 'Rats are swarming towards the city.', | |||
success: 'Success: The rat invasion has been averted.', | |||
failure: 'Failure: The rats have made it to the city.', | |||
escapeCounter: 'Escapees: $ratEscapees$' | |||
}; | |||
const config = { | |||
cron: '0/3 * * * *', | |||
//cron: '* * * * *', | |||
idFirstSpawnPhase: 6, | |||
idFailPhase: 19, | |||
maxEscapees: 5, | |||
repeats: [5, 5, 3], | |||
//repeats: [1, 1, 1], | |||
rewardItemsPerKill: 3 | |||
}; | |||
const ratTargetPos = { | |||
x: 97, | |||
y: 87 | |||
}; | |||
const rat = { | |||
name: 'Bloodthirsty Rat', | |||
cell: 24, | |||
level, | |||
faction, | |||
grantRep, | |||
drops, | |||
hpMult: 3, | |||
pos: { | |||
x: 61, | |||
y: 62 | |||
}, | |||
originX: ratTargetPos.x, | |||
originY: ratTargetPos.y, | |||
maxChaseDistance: 1000, | |||
spells: [{ | |||
type: 'smokeBomb', | |||
radius: 1, | |||
duration: 20, | |||
range: 2, | |||
selfCast: 1, | |||
statMult: 1, | |||
damage: 0.125, | |||
element: 'poison', | |||
cdMax: 5, | |||
particles: { | |||
scale: { | |||
start: { | |||
min: 10, | |||
max: 25 | |||
}, | |||
end: { | |||
min: 10, | |||
max: 0 | |||
} | |||
}, | |||
opacity: { | |||
start: 0.3, | |||
end: 0 | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 2 | |||
}, | |||
speed: { | |||
start: 3, | |||
end: 0 | |||
}, | |||
color: { | |||
start: ['db5538', '4ac441'], | |||
end: ['953f36', '386646'] | |||
}, | |||
chance: 0.125, | |||
randomColor: true, | |||
randomScale: true, | |||
blendMode: 'screen', | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -10, | |||
y: -10, | |||
w: 20, | |||
h: 20 | |||
} | |||
} | |||
}], | |||
events: { | |||
afterMove: function () { | |||
const { obj: { x, y } } = this; | |||
if (x !== ratTargetPos.x || y !== ratTargetPos.y) | |||
return; | |||
const eventManager = this.obj.instance.events; | |||
const eventName = this.obj.event.config.name; | |||
eventManager.incrementEventVariable(eventName, 'ratEscapees', 1); | |||
const { active, escapeCounter } = descriptionStrings; | |||
const newDesc = `${active}<br /><br />${escapeCounter}`; | |||
eventManager.setEventDescription(eventName, newDesc); | |||
this.obj.destroyed = true; | |||
const totalEscapees = this.obj.event.variables.ratEscapees; | |||
if (totalEscapees >= config.maxEscapees) { | |||
const event = this.obj.event; | |||
event.currentPhase.end = true; | |||
event.nextPhase = config.idFailPhase; | |||
} | |||
}, | |||
afterDeath: function ({ source: { name } }) { | |||
const eventManager = this.obj.instance.events; | |||
const eventName = this.obj.event.config.name; | |||
const itemCount = config.rewardItemsPerKill; | |||
const rewards = rewardGenerator(itemCount, rewardConfig.regularKill); | |||
eventManager.addParticipantRewards(eventName, name, rewards); | |||
} | |||
} | |||
}; | |||
module.exports = { | |||
name: 'Plague of Rats', | |||
description: descriptionStrings.leadup, | |||
distance: -1, | |||
cron: config.cron, | |||
phases: [{ | |||
type: 'spawnMob', | |||
auto: true, | |||
mobs: [{ | |||
id: 'banditAlchemist', | |||
name: 'Bandit Alchemist', | |||
attackable: false, | |||
hpMult: 1, | |||
cell: 79, | |||
level: 15, | |||
pos: { | |||
x: 117, | |||
y: 62 | |||
} | |||
}] | |||
}, { | |||
type: 'moveMob', | |||
id: 'banditAlchemist', | |||
pos: { | |||
x: 64, | |||
y: 63 | |||
} | |||
}, { | |||
type: 'eventChain', | |||
config: [{ | |||
type: 'mobTalk', | |||
id: 'banditAlchemist', | |||
text: 'I think I cracked it! The serum should finally be complete.', | |||
delay: 15 | |||
}, { | |||
type: 'mobTalk', | |||
id: 'banditAlchemist', | |||
text: 'One taste of this and the rats\' hunger for blood will be instatiable.', | |||
delay: 15 | |||
}, { | |||
type: 'mobTalk', | |||
id: 'banditAlchemist', | |||
text: '*pours a bubbling liquid into a rat nest*', | |||
delay: 15 | |||
}, { | |||
type: 'mobTalk', | |||
id: 'banditAlchemist', | |||
text: 'Let\'s see how Fjolgard handles a rat infestation of the bloodthirsty variety.', | |||
delay: 15 | |||
}, { | |||
type: 'mobTalk', | |||
id: 'banditAlchemist', | |||
text: '*laughs*', | |||
delay: 15 | |||
}] | |||
}, { | |||
type: 'moveMob', | |||
id: 'banditAlchemist', | |||
pos: { | |||
x: 117, | |||
y: 63 | |||
}, | |||
auto: true | |||
}, { | |||
type: 'despawnMob', | |||
id: 'banditAlchemist' | |||
}, { | |||
type: 'setDescription', | |||
desc: descriptionStrings.active, | |||
auto: true | |||
}, { | |||
type: 'spawnMob', | |||
mobs: [rat], | |||
auto: true | |||
}, { | |||
type: 'wait', | |||
ttl: 15 | |||
}, { | |||
type: 'goto', | |||
gotoPhaseIndex: config.idFirstSpawnPhase, | |||
repeats: config.repeats[0] | |||
}, { | |||
type: 'spawnMob', | |||
mobs: [rat], | |||
auto: true | |||
}, { | |||
type: 'wait', | |||
ttl: 7 | |||
}, { | |||
type: 'goto', | |||
gotoPhaseIndex: config.idFirstSpawnPhase + 3, | |||
repeats: config.repeats[1] | |||
}, { | |||
type: 'spawnMob', | |||
mobs: [rat], | |||
auto: true | |||
}, { | |||
type: 'wait', | |||
ttl: 3 | |||
}, { | |||
type: 'goto', | |||
gotoPhaseIndex: config.idFirstSpawnPhase + 6, | |||
repeats: config.repeats[2] | |||
}, { | |||
type: 'killAllMobs' | |||
}, { | |||
type: 'setDescription', | |||
desc: descriptionStrings.success, | |||
ttl: 50 | |||
}, { | |||
type: 'giveRewards' | |||
}, { | |||
type: 'end' | |||
}, { | |||
type: 'setDescription', | |||
desc: descriptionStrings.failure, | |||
ttl: 50 | |||
}] | |||
}; |
@@ -111,6 +111,7 @@ module.exports = { | |||
drops: { | |||
rolls: 3, | |||
chance: 100, | |||
noRandom: true, | |||
alsoRandom: true, | |||
magicFind: [2000, 125], | |||
@@ -231,6 +232,7 @@ module.exports = { | |||
level: 13, | |||
walkDistance: 0, | |||
faction: 'hostile', | |||
noQuest: true, | |||
grantRep: { | |||
fjolgard: 22 | |||
}, | |||
@@ -1,36 +0,0 @@ | |||
module.exports = { | |||
type: 'titangrip', | |||
init: function () { | |||
}, | |||
simplify: function () { | |||
return this.type; | |||
}, | |||
events: { | |||
afterEquipItem: function (item) { | |||
if (['oneHanded', 'twoHanded'].indexOf(item.slot) === -1) | |||
return; | |||
let stats = item.stats; | |||
for (let s in stats) { | |||
let val = stats[s]; | |||
this.obj.stats.addStat(s, val); | |||
} | |||
}, | |||
afterUnequipItem: function (item) { | |||
if (['oneHanded', 'twoHanded'].indexOf(item.slot) === -1) | |||
return; | |||
let stats = item.stats; | |||
for (let s in stats) { | |||
let val = stats[s]; | |||
this.obj.stats.addStat(s, -val); | |||
} | |||
} | |||
} | |||
}; |
@@ -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); | |||
@@ -14,7 +14,7 @@ module.exports = { | |||
if (this.mobName) { | |||
let mobType = mobTypes[this.mobName.toLowerCase()]; | |||
//Maybe the zoneFile changed in the meantime. If so, regenerate | |||
if ((!mobType) || (mobType.attackable === false)) | |||
if (!mobType || mobType.attackable === false || mobType.noQuest) | |||
this.mobName = null; | |||
} | |||
@@ -24,13 +24,14 @@ module.exports = { | |||
let mobBlueprint = mobTypes[m]; | |||
return ( | |||
(m !== 'default') && | |||
m !== 'default' && | |||
!mobBlueprint.noQuest && | |||
( | |||
(mobBlueprint.attackable) || | |||
(!mobBlueprint.has('attackable')) | |||
mobBlueprint.attackable || | |||
!mobBlueprint.has('attackable') | |||
) && | |||
(mobBlueprint.level <= ~~(this.obj.stats.values.level * 1.35)) && | |||
(mobCounts[m] > 1) | |||
mobBlueprint.level <= ~~(this.obj.stats.values.level * 1.35) && | |||
mobCounts[m] > 1 | |||
); | |||
}, this); | |||
@@ -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, | |||
@@ -1,5 +1,5 @@ | |||
module.exports = { | |||
version: '0.4.0', | |||
version: '0.4.3', | |||
port: 4000, | |||
startupMessage: 'Server: ready', | |||
defaultZone: 'fjolarok', | |||
@@ -8,7 +8,7 @@ module.exports = { | |||
// sqlite | |||
// rethink | |||
//eslint-disable-next-line no-process-env | |||
db: process.env.IWD_DB || 'rethink', | |||
db: process.env.IWD_DB || 'sqlite', | |||
//eslint-disable-next-line no-process-env | |||
dbHost: process.env.IWD_DB_HOST || 'localhost', | |||
//eslint-disable-next-line no-process-env | |||
@@ -75,27 +75,32 @@ module.exports = { | |||
// the delta by -1 | |||
let offsetX = 0; | |||
if (dx !== 0) | |||
offsetX = (dx / Math.abs(dx)) * -1; | |||
offsetX = dx / Math.abs(dx); | |||
let offsetY = 0; | |||
if (dy !== 0) | |||
offsetY = (dy / Math.abs(dy)) * -1; | |||
offsetY = dy / Math.abs(dy); | |||
let targetPos = { | |||
x: target.x, | |||
y: target.y | |||
}; | |||
let physics = obj.instance.physics; | |||
const physics = obj.instance.physics; | |||
const fnTileValid = this.isTileValid.bind(this, physics, x, y); | |||
//Check where we should land | |||
if (!this.isTileValid(physics, x, y, targetPos.x - offsetX, targetPos.y - offsetY)) { | |||
if (!this.isTileValid(physics, x, y, targetPos.x - offsetX, targetPos.y)) | |||
targetPos.y -= offsetY; | |||
else | |||
targetPos.x -= offsetX; | |||
if (!fnTileValid(targetPos.x + offsetX, targetPos.y + offsetY)) { | |||
if (!fnTileValid(targetPos.x + offsetX, targetPos.y)) { | |||
if (!fnTileValid(targetPos.x, targetPos.y + offsetY)) { | |||
targetPos.x -= offsetX; | |||
targetPos.y -= offsetY; | |||
} else | |||
targetPos.y += offsetY; | |||
} else | |||
targetPos.x += offsetX; | |||
} else { | |||
targetPos.x -= offsetX; | |||
targetPos.y -= offsetY; | |||
targetPos.x += offsetX; | |||
targetPos.y += offsetY; | |||
} | |||
let targetEffect = target.effects.addEffect({ | |||
@@ -95,7 +95,8 @@ module.exports = { | |||
if (!members.find(m => ~~m === ~~serverId)) { | |||
delete effects[serverId]; | |||
const obj = objects.find(f => ~~f.serverId === ~~serverId); | |||
obj.effects.removeEffect(effect); | |||
if (obj) | |||
obj.effects.removeEffect(effect); | |||
} | |||
}); | |||
}, | |||
@@ -1,3 +1,41 @@ | |||
const getTargetPos = (physics, obj, m) => { | |||
let targetPos = { | |||
x: m.x, | |||
y: m.y | |||
}; | |||
let dx = m.x - obj.x; | |||
let dy = m.y - obj.y; | |||
while ((dx === 0) && (dy === 0)) { | |||
dx = ~~(Math.random() * 2) - 1; | |||
dy = ~~(Math.random() * 2) - 1; | |||
} | |||
dx = ~~(dx / Math.abs(dx)); | |||
dy = ~~(dy / Math.abs(dy)); | |||
for (let l = 0; l < this.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)) | |||
break; | |||
else { | |||
dx = 0; | |||
targetPos.y += dy; | |||
} | |||
} else { | |||
dy = 0; | |||
targetPos.x += dx; | |||
} | |||
} else { | |||
targetPos.x += dx; | |||
targetPos.y += dy; | |||
} | |||
} | |||
return targetPos; | |||
}; | |||
module.exports = { | |||
type: 'fireblast', | |||
@@ -17,7 +55,7 @@ module.exports = { | |||
const particleConfig = extend({}, this.particles); | |||
this.obj.fireEvent('beforeSpawnParticles', this, particleConfig); | |||
obj.fireEvent('beforeSpawnParticles', this, particleConfig); | |||
for (let i = x - radius; i <= x + radius; i++) { | |||
for (let j = y - radius; j <= y + radius; j++) { | |||
@@ -46,60 +84,33 @@ module.exports = { | |||
if (!m) { | |||
mLen--; | |||
continue; | |||
} | |||
if ((!m.aggro) || (!m.effects)) | |||
} else if (!m.aggro || !m.effects) | |||
continue; | |||
if (!this.obj.aggro.canAttack(m)) | |||
else if (!obj.aggro.canAttack(m)) | |||
continue; | |||
let targetEffect = m.effects.addEffect({ | |||
type: 'stunned', | |||
noMsg: true, | |||
new: true | |||
}); | |||
let targetPos = { | |||
x: m.x, | |||
y: m.y | |||
}; | |||
//Find out where the mob should end up | |||
let dx = m.x - obj.x; | |||
let dy = m.y - obj.y; | |||
while ((dx === 0) && (dy === 0)) { | |||
dx = ~~(Math.random() * 2) - 1; | |||
dy = ~~(Math.random() * 2) - 1; | |||
} | |||
dx = ~~(dx / Math.abs(dx)); | |||
dy = ~~(dy / Math.abs(dy)); | |||
for (let l = 0; l < this.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)) | |||
break; | |||
else { | |||
dx = 0; | |||
targetPos.y += dy; | |||
} | |||
} else { | |||
dy = 0; | |||
targetPos.x += dx; | |||
} | |||
} else { | |||
targetPos.x += dx; | |||
targetPos.y += dy; | |||
} | |||
} | |||
const targetPos = getTargetPos(physics, obj, m); | |||
let distance = Math.max(Math.abs(m.x - targetPos.x), Math.abs(m.y - targetPos.y)); | |||
let ttl = distance * 125; | |||
m.clearQueue(); | |||
let damage = this.getDamage(m); | |||
m.stats.takeDamage(damage, 1, obj); | |||
if (m.destroyed) | |||
continue; | |||
const eventMsg = { | |||
success: true, | |||
targetPos | |||
}; | |||
m.fireEvent('beforePositionChange', eventMsg); | |||
if (!eventMsg.success) | |||
continue; | |||
this.sendAnimation({ | |||
id: m.id, | |||
components: [{ | |||
@@ -110,13 +121,14 @@ module.exports = { | |||
}] | |||
}); | |||
let damage = this.getDamage(m); | |||
m.stats.takeDamage(damage, 1, obj); | |||
let targetEffect = m.effects.addEffect({ | |||
type: 'stunned', | |||
noMsg: true, | |||
new: true | |||
}); | |||
if (!m.destroyed) { | |||
physics.removeObject(m, m.x, m.y); | |||
this.queueCallback(this.endEffect.bind(this, m, targetPos, targetEffect), ttl, null, m); | |||
} | |||
physics.removeObject(m, m.x, m.y); | |||
this.queueCallback(this.endEffect.bind(this, m, targetPos, targetEffect), ttl, null, m); | |||
} | |||
} | |||
} | |||
@@ -140,5 +152,6 @@ module.exports = { | |||
syncer.o.y = targetPos.y; | |||
target.instance.physics.addObject(target, target.x, target.y); | |||
target.fireEvent('afterPositionChange', targetPos); | |||
} | |||
}; |
@@ -17,7 +17,7 @@ let spells = { | |||
manaCost: 0, | |||
range: 9, | |||
random: { | |||
damage: [2, 7.6] | |||
damage: [2, 7.2] | |||
} | |||
}, | |||
@@ -25,12 +25,12 @@ let spells = { | |||
statType: 'int', | |||
statMult: 1, | |||
element: 'arcane', | |||
cdMax: 6, | |||
castTimeMax: 7, | |||
manaCost: 4, | |||
cdMax: 7, | |||
castTimeMax: 6, | |||
manaCost: 5, | |||
range: 9, | |||
random: { | |||
damage: [4, 15] | |||
damage: [4, 32] | |||
} | |||
}, | |||
'ice spear': { | |||
@@ -42,7 +42,7 @@ let spells = { | |||
manaCost: 4, | |||
range: 9, | |||
random: { | |||
damage: [2, 8], | |||
damage: [2, 15], | |||
i_freezeDuration: [6, 10] | |||
} | |||
}, | |||
@@ -54,7 +54,7 @@ let spells = { | |||
castTimeMax: 2, | |||
manaCost: 5, | |||
random: { | |||
damage: [2, 5], | |||
damage: [2, 10], | |||
i_radius: [1, 2.2], | |||
i_pushback: [2, 5] | |||
} | |||
@@ -63,8 +63,8 @@ let spells = { | |||
statType: 'int', | |||
statMult: 1, | |||
element: 'holy', | |||
cdMax: 4, | |||
castTimeMax: 6, | |||
cdMax: 6, | |||
castTimeMax: 3, | |||
range: 9, | |||
manaCost: 7, | |||
random: { | |||
@@ -77,7 +77,7 @@ let spells = { | |||
statMult: 1, | |||
element: 'holy', | |||
cdMax: 5, | |||
castTimeMax: 5, | |||
castTimeMax: 4, | |||
manaCost: 8, | |||
range: 9, | |||
radius: 3, | |||
@@ -108,7 +108,7 @@ let spells = { | |||
range: 10, | |||
manaCost: 3, | |||
random: { | |||
damage: [2, 8], | |||
damage: [2, 11], | |||
i_stunDuration: [6, 10] | |||
} | |||
}, | |||
@@ -117,10 +117,10 @@ let spells = { | |||
statMult: 1, | |||
cdMax: 20, | |||
castTimeMax: 0, | |||
manaCost: 12, | |||
manaCost: 10, | |||
random: { | |||
i_duration: [10, 20], | |||
i_chance: [20, 50] | |||
i_chance: [30, 60] | |||
} | |||
}, | |||
whirlwind: { | |||
@@ -133,18 +133,18 @@ let spells = { | |||
noDrop: true, | |||
random: { | |||
i_range: [1, 2.5], | |||
damage: [4, 15] | |||
damage: [4, 18] | |||
} | |||
}, | |||
smokebomb: { | |||
statType: 'dex', | |||
statMult: 1, | |||
element: 'poison', | |||
cdMax: 5, | |||
cdMax: 7, | |||
castTimeMax: 0, | |||
manaCost: 6, | |||
random: { | |||
damage: [0.25, 1.45], | |||
damage: [0.25, 1.2], | |||
i_radius: [1, 3], | |||
i_duration: [7, 13] | |||
} | |||
@@ -152,26 +152,26 @@ let spells = { | |||
ambush: { | |||
statType: 'dex', | |||
statMult: 1, | |||
cdMax: 16, | |||
castTimeMax: 7, | |||
cdMax: 15, | |||
castTimeMax: 3, | |||
range: 10, | |||
manaCost: 7, | |||
noDrop: true, | |||
random: { | |||
damage: [8, 28], | |||
i_stunDuration: [4, 6] | |||
damage: [8, 35], | |||
i_stunDuration: [4, 7] | |||
} | |||
}, | |||
'crystal spikes': { | |||
statType: ['dex', 'int'], | |||
statMult: 1, | |||
manaCost: 22, | |||
manaCost: 14, | |||
needLos: true, | |||
cdMax: 20, | |||
castTimeMax: 0, | |||
range: 9, | |||
random: { | |||
damage: [3, 14], | |||
damage: [3, 16], | |||
i_delay: [1, 4] | |||
}, | |||
negativeStats: [ | |||
@@ -2,6 +2,18 @@ let phaseTemplate = require('../config/eventPhases/phaseTemplate'); | |||
let fs = require('fs'); | |||
let mapList = require('../config/maps/mapList'); | |||
const applyVariablesToDescription = (desc, variables) => { | |||
if (!variables) | |||
return desc; | |||
Object.entries(variables).forEach(e => { | |||
const [key, value] = e; | |||
desc = desc.split(`$${key}$`).join(value); | |||
}); | |||
return desc; | |||
}; | |||
module.exports = { | |||
configs: [], | |||
nextId: 0, | |||
@@ -30,6 +42,7 @@ module.exports = { | |||
getEvent: function (name) { | |||
return this.configs.find(c => (c.name === name)).event.config; | |||
}, | |||
setEventDescription: function (name, desc) { | |||
let config = this.getEvent(name); | |||
let event = config.event; | |||
@@ -42,11 +55,15 @@ module.exports = { | |||
if ((config.events) && (config.events.beforeSetDescription)) | |||
config.events.beforeSetDescription(this); | |||
if (desc) | |||
if (desc) { | |||
desc = applyVariablesToDescription(desc, event.variables); | |||
config.description = desc; | |||
} | |||
event.participators.forEach(p => p.events.syncList()); | |||
}, | |||
setEventRewards: function (name, rewards) { | |||
let config = this.getEvent(name); | |||
let event = config.event; | |||
@@ -56,6 +73,36 @@ module.exports = { | |||
event.rewards = rewards; | |||
event.age = event.config.duration - 2; | |||
}, | |||
setParticipantRewards: function (eventName, participantName, newRewards) { | |||
const { event: { rewards } } = this.getEvent(eventName); | |||
rewards[participantName] = newRewards; | |||
}, | |||
addParticipantRewards: function (eventName, participantName, addRewards) { | |||
const { event: { rewards } } = this.getEvent(eventName); | |||
let pRewards = rewards[participantName]; | |||
if (!pRewards) { | |||
pRewards = []; | |||
rewards[participantName] = pRewards; | |||
} | |||
if (!addRewards.push) | |||
addRewards = [ addRewards ]; | |||
addRewards.forEach(r => { | |||
const { name, quantity = 1 } = r; | |||
const exists = pRewards.find(f => f.name === name); | |||
if (exists) | |||
exists.quantity = (exists.quantity || 1) + quantity; | |||
else | |||
pRewards.push(r); | |||
}); | |||
}, | |||
setWinText: function (name, text) { | |||
let config = this.getEvent(name); | |||
let event = config.event; | |||
@@ -65,6 +112,25 @@ module.exports = { | |||
event.winText = text; | |||
}, | |||
setEventVariable: function (eventName, variableName, value) { | |||
let config = this.getEvent(eventName); | |||
let event = config.event; | |||
if (!event) | |||
return; | |||
event.variables[variableName] = value; | |||
}, | |||
incrementEventVariable: function (eventName, variableName, delta) { | |||
let config = this.getEvent(eventName); | |||
let event = config.event; | |||
if (!event) | |||
return; | |||
const currentValue = event.variables[variableName] || 0; | |||
event.variables[variableName] = currentValue + delta; | |||
}, | |||
update: function () { | |||
let configs = this.configs; | |||
if (!configs) | |||
@@ -103,6 +169,9 @@ module.exports = { | |||
let event = { | |||
id: this.nextId++, | |||
config: extend({}, config), | |||
eventManager: this, | |||
variables: {}, | |||
rewards: {}, | |||
phases: [], | |||
participators: [], | |||
objects: [], | |||
@@ -127,7 +196,7 @@ module.exports = { | |||
if ((rewards) && (rewards[p.name])) { | |||
rewards[p.name].forEach(r => rList.push(r)); | |||
if (rList.length > 1) | |||
rList[1].msg = 'Fishing tournament reward:'; | |||
rList[1].msg = `${event.config.name} reward:`; | |||
} | |||
this.instance.mail.sendMail(p.name, rList); | |||
@@ -243,10 +312,18 @@ module.exports = { | |||
phase.destroyed = true; | |||
continue; | |||
} else { | |||
if (!phase.auto) | |||
if (phase.has('ttl')) { | |||
if (phase.ttl === 0) { | |||
phase.end = true; | |||
continue; | |||
} | |||
phase.ttl--; | |||
stillBusy = true; | |||
} else if (!phase.auto) | |||
stillBusy = true; | |||
phase.update(); | |||
phase.update(event); | |||
} | |||
} | |||
} | |||
@@ -275,18 +352,21 @@ module.exports = { | |||
for (let i = event.nextPhase; i < pLen; i++) { | |||
let p = phases[i]; | |||
let phaseFile = 'phase' + p.type[0].toUpperCase() + p.type.substr(1); | |||
let typeTemplate = require('../config/eventPhases/' + phaseFile); | |||
let phase = extend({ | |||
instance: this.instance, | |||
event: event | |||
}, phaseTemplate, typeTemplate, p); | |||
event.phases.push(phase); | |||
phase.init(); | |||
let phase = event.phases[i]; | |||
if (!phase) { | |||
let phaseFile = 'phase' + p.type[0].toUpperCase() + p.type.substr(1); | |||
let typeTemplate = require('../config/eventPhases/' + phaseFile); | |||
phase = extend({ | |||
instance: this.instance, | |||
event: event | |||
}, phaseTemplate, typeTemplate, p); | |||
event.phases.push(phase); | |||
event.currentPhase = phase; | |||
} | |||
event.nextPhase = i + 1; | |||
phase.init(event); | |||
if (!p.auto) { | |||
stillBusy = true; | |||
@@ -103,6 +103,7 @@ const reslot = (item, msg) => { | |||
delete item.spell; | |||
delete item.implicitStats; | |||
delete item.power; | |||
delete item.range; | |||
extend(item, newItem); | |||
}; | |||
@@ -660,9 +660,10 @@ module.exports = { | |||
stat: i.stat | |||
}; | |||
if (i.value) | |||
stat.value = i.value[0] + ~~(Math.random() * (i.value[1] - i.value[0] + 1)); | |||
else if (i.valueMult) { | |||
if (i.value) { | |||
const [min, max] = i.value; | |||
stat.value = Math.ceil(random.expNorm(min, max)); | |||
} else if (i.valueMult) { | |||
let statBlueprint = this.stats[i.stat]; | |||
if (statBlueprint.generator) { | |||
@@ -27,14 +27,9 @@ extend(random, { | |||
}, | |||
expNorm: function(low, high) { | |||
var roll = random.norm(0, 100); | |||
if (roll > 50) | |||
roll = 100 - roll; | |||
roll = (50 - roll) / 50; | |||
roll = low + ((high - low) * roll); | |||
var roll = Math.abs(random.norm(0, 100) - 50) / 50; | |||
const result = low + ((high - low) * roll); | |||
return roll; | |||
return result; | |||
} | |||
}); |
@@ -0,0 +1,76 @@ | |||
const defaultConfig = [{ | |||
name: 'Iron Bar', | |||
sprite: [0, 0], | |||
quality: 0, | |||
chance: 15 | |||
}, { | |||
name: 'Cloth Scrap', | |||
sprite: [0, 1], | |||
quality: 0, | |||
chance: 15 | |||
}, { | |||
name: 'Leather Scrap', | |||
sprite: [0, 7], | |||
quality: 0, | |||
chance: 15 | |||
}, { | |||
name: 'Skyblossom', | |||
sprite: [1, 2], | |||
quality: 0, | |||
chance: 8 | |||
}, { | |||
name: 'Common Essence', | |||
sprite: [0, 2], | |||
quality: 0, | |||
chance: 5 | |||
}, { | |||
name: 'Magic Essence', | |||
sprite: [0, 3], | |||
quality: 1, | |||
chance: 2 | |||
}, { | |||
name: 'Rare Essence', | |||
sprite: [0, 4], | |||
quality: 2, | |||
chance: 1 | |||
}]; | |||
const buildPool = config => { | |||
const pool = []; | |||
config.forEach(c => { | |||
for (let i = 0; i < c.chance; i++) | |||
pool.push(c.name); | |||
}); | |||
return pool; | |||
}; | |||
const defaultPool = buildPool(defaultConfig); | |||
module.exports = (itemCount, useConfig) => { | |||
const config = useConfig || defaultConfig; | |||
const pool = useConfig ? buildPool(useConfig) : defaultPool; | |||
const items = []; | |||
for (let i = 0; i < itemCount; i++) { | |||
let pickName = pool[~~(Math.random() * pool.length)]; | |||
const pick = config.find(f => f.name === pickName); | |||
let item = items.find(f => f.name === pickName); | |||
if (!item) { | |||
items.push({ | |||
name: pick.name, | |||
material: true, | |||
quality: pick.quality, | |||
sprite: pick.sprite, | |||
quantity: pick.quantity || 1 | |||
}); | |||
} else | |||
item.quantity += (pick.quantity || 1); | |||
} | |||
return items; | |||
}; | |||
@@ -32,6 +32,14 @@ module.exports = { | |||
return cpn; | |||
}, | |||
addBuiltComponent: function (cpn) { | |||
this[cpn.type] = cpn; | |||
cpn.obj = this; | |||
this.components.push(cpn); | |||
return cpn; | |||
}, | |||
removeComponent: function (type) { | |||
let cpn = this[type]; | |||
if (!cpn) | |||
@@ -19,7 +19,7 @@ module.exports = { | |||
let pars = req.originalUrl.split('?').pop().split('&'); | |||
pars.forEach(p => { | |||
let [par, val] = p.split('='); | |||
let [par, val = ''] = p.split('='); | |||
config[par] = val | |||
.split('%20') | |||
.join(' '); | |||
@@ -28,7 +28,6 @@ module.exports = { | |||
let lessMiddleware = require('less-middleware'); | |||
app.use(lessMiddleware('../', { | |||
force: true, | |||
render: { | |||
strictMath: true | |||
} | |||
@@ -93,7 +93,7 @@ module.exports = { | |||
if (spawnEvent.changed) | |||
msg.keepPos = false; | |||
if ((msg.keepPos) && (!physics.isValid(obj.x, obj.y))) | |||
if (msg.keepPos && (!physics.isValid(obj.x, obj.y) || !map.canPathFromPos(obj))) | |||
msg.keepPos = false; | |||
if (!msg.keepPos || !obj.has('x') || (map.mapFile.properties.isRandom && obj.instanceId !== map.seed)) { | |||