@@ -4,7 +4,7 @@ define([ | |||
'html!ui/templates/tooltipItem/template', | |||
'html!ui/templates/tooltipItem/templateTooltip', | |||
'js/misc/statTranslations' | |||
], function( | |||
], function ( | |||
events, | |||
styles, | |||
template, | |||
@@ -31,14 +31,14 @@ define([ | |||
tooltip: null, | |||
item: null, | |||
postRender: function() { | |||
postRender: function () { | |||
this.tooltip = this.el.find('.tooltip'); | |||
this.onEvent('onShowItemTooltip', this.onShowItemTooltip.bind(this)); | |||
this.onEvent('onHideItemTooltip', this.onHideItemTooltip.bind(this)); | |||
}, | |||
onHideItemTooltip: function(item) { | |||
onHideItemTooltip: function (item) { | |||
if (this.item != item) | |||
return; | |||
@@ -46,7 +46,7 @@ define([ | |||
this.tooltip.hide(); | |||
}, | |||
onShowItemTooltip: function(item, pos, compare, bottomAlign, shiftDown) { | |||
onShowItemTooltip: function (item, pos, compare, bottomAlign, shiftDown) { | |||
this.item = item; | |||
var tempStats = $.extend(true, {}, item.stats); | |||
@@ -72,7 +72,7 @@ define([ | |||
} | |||
stats = Object.keys(tempStats) | |||
.map(function(s) { | |||
.map(function (s) { | |||
var statName = statTranslations.translate(s); | |||
var value = tempStats[s]; | |||
@@ -95,7 +95,7 @@ define([ | |||
return row; | |||
}, this) | |||
.sort(function(a, b) { | |||
.sort(function (a, b) { | |||
return (a.length - b.length); | |||
}) | |||
.join(''); | |||
@@ -178,7 +178,7 @@ define([ | |||
if ((item.effects) && (item.type != 'mtx')) { | |||
var htmlEffects = ''; | |||
item.effects.forEach(function(e, i) { | |||
item.effects.forEach(function (e, i) { | |||
htmlEffects += e.text; | |||
if (i < item.effects.length - 1) | |||
htmlEffects += '<br />'; | |||
@@ -187,13 +187,23 @@ define([ | |||
this.find('.effects') | |||
.html(htmlEffects) | |||
.show(); | |||
} else if (item.description) { | |||
this.find('.effects') | |||
.html(item.description) | |||
.show(); | |||
} else | |||
this.find('.effects').hide(); | |||
if (item.type == 'Reward Card') { | |||
this.find('.spellName') | |||
.html('Set Size: ' + item.setSize) | |||
.show(); | |||
} | |||
if (item.factions) { | |||
var htmlFactions = ''; | |||
item.factions.forEach(function(f, i) { | |||
item.factions.forEach(function (f, i) { | |||
var htmlF = f.name + ': ' + f.tierName; | |||
if (f.noEquip) | |||
htmlF = '<font class="color-red">' + htmlF + '</font>'; | |||
@@ -228,11 +238,11 @@ define([ | |||
events.emit('onBuiltItemTooltip', this.tooltip); | |||
}, | |||
showWorth: function(canAfford) { | |||
showWorth: function (canAfford) { | |||
this.tooltip.find('.worth').show(); | |||
if (!canAfford) | |||
this.tooltip.find('.worth').addClass('no-afford'); | |||
} | |||
}; | |||
}); | |||
}); |
@@ -69,7 +69,7 @@ define([ | |||
if (this.closed) { | |||
if (this.locked) { | |||
var key = obj.inventory.items.find(i => (i.keyId == this.key)); | |||
var key = obj.inventory.items.find(i => ((i.keyId == this.key) || (i.keyId == 'world'))); | |||
if (!key) { | |||
canAction = false; | |||
msg = `You don't have the key to unlock this door`; | |||
@@ -110,7 +110,7 @@ define([ | |||
var syncO = thisObj.syncer.o; | |||
if (this.locked) { | |||
var key = obj.inventory.items.find(i => (i.keyId == this.key)); | |||
var key = obj.inventory.items.find(i => ((i.keyId == this.key) || (i.keyId == 'world'))); | |||
if (!key) | |||
return; | |||
} | |||
@@ -21,7 +21,8 @@ define([ | |||
getItem: 10, | |||
getGold: 10, | |||
setLevel: 10, | |||
godMode: 10 | |||
godMode: 10, | |||
clearInventory: 10 | |||
}; | |||
var localCommands = [ | |||
@@ -161,6 +162,15 @@ define([ | |||
return character.auth.customChannels.some(c => (c == channel)); | |||
}, | |||
clearInventory: function () { | |||
var inventory = this.obj.inventory; | |||
inventory.items | |||
.filter(i => !i.eq) | |||
.map(i => i.id) | |||
.forEach(i => inventory.destroyItem(i, null, true)); | |||
}, | |||
getItem: function (config) { | |||
if (config.slot == 'set') { | |||
configSlots.slots.forEach(function (s) { | |||
@@ -5,7 +5,8 @@ define([ | |||
'objects/objects', | |||
'config/classes', | |||
'mtx/mtx', | |||
'config/factions' | |||
'config/factions', | |||
'misc/events' | |||
], function ( | |||
generator, | |||
salvager, | |||
@@ -13,7 +14,8 @@ define([ | |||
objects, | |||
classes, | |||
mtx, | |||
factions | |||
factions, | |||
events | |||
) { | |||
return { | |||
type: 'inventory', | |||
@@ -36,6 +38,8 @@ define([ | |||
for (var i = 0; i < iLen; i++) { | |||
var item = items[i]; | |||
if (item.pos >= this.inventorySize) | |||
delete item.pos; | |||
//Hacks for old items | |||
if (((item.spell) && (!item.spell.rolls)) || (!item.sprite)) { | |||
@@ -487,6 +491,8 @@ define([ | |||
}, | |||
getItem: function (item, hideMessage) { | |||
events.emit('onBeforeGetItem', item, this.obj); | |||
//We need to know if a mob dropped it for quest purposes | |||
var fromMob = item.fromMob; | |||
@@ -502,7 +508,7 @@ define([ | |||
//Material? | |||
var exists = false; | |||
if (((item.material) || (item.quest)) && (!item.noStack) || (item.quantity)) { | |||
if (((item.material) || (item.quest) || (item.quantity)) && (!item.noStack)) { | |||
var existItem = this.items.find(i => i.name == item.name); | |||
if (existItem) { | |||
exists = true; | |||
@@ -773,6 +779,7 @@ define([ | |||
} | |||
killSource.fireEvent('beforeTargetDeath', this.obj, this.items); | |||
events.emit('onBeforeDropBag', this.obj, this.items, killSource); | |||
if (this.items.length > 0) | |||
this.createBag(this.obj.x, this.obj.y, this.items, ownerId); | |||
@@ -67,7 +67,8 @@ define([ | |||
obj.addComponent('reputation', character.components.find(c => c.type == 'reputation')); | |||
var social = character.components.find(c => c.type == 'social'); | |||
delete social.party; | |||
if (social) | |||
delete social.party; | |||
obj.addComponent('social', social); | |||
obj.social.init(); | |||
obj.social.party = null; | |||
@@ -28,9 +28,9 @@ module.exports = { | |||
'disciple of flabbers': [{ | |||
msg: '*woofs in reverence*' | |||
}], | |||
'priest': [{ | |||
priest: [{ | |||
msg: 'peace be with you' | |||
}, { | |||
msg: 'walk in the light' | |||
}] | |||
}; | |||
}; |
@@ -1191,14 +1191,6 @@ | |||
"height":8, | |||
"id":775, | |||
"name":"|greencandle", | |||
"properties": | |||
{ | |||
"cpnAggro":"{ \"faction\": 1}" | |||
}, | |||
"propertytypes": | |||
{ | |||
"cpnAggro":"string" | |||
}, | |||
"rotation":0, | |||
"type":"", | |||
"visible":true, | |||
@@ -2657,7 +2649,6 @@ | |||
"spawn":"string" | |||
}, | |||
"renderorder":"right-down", | |||
"tiledversion":"1.0.3", | |||
"tileheight":8, | |||
"tilesets":[ | |||
{ | |||
@@ -2894,6 +2885,6 @@ | |||
}], | |||
"tilewidth":8, | |||
"type":"map", | |||
"version":1, | |||
"version":"2017.05.26", | |||
"width":240 | |||
} |
@@ -1,13 +1,12 @@ | |||
define([ | |||
], function( | |||
], function ( | |||
) { | |||
return [ | |||
'cave', | |||
'city', | |||
'estuary', | |||
'sewer', | |||
'tutorial' | |||
]; | |||
}); | |||
}); |
@@ -0,0 +1,14 @@ | |||
module.exports = { | |||
estrid: [{ | |||
msg: '*polishes some vials' | |||
}, { | |||
msg: 'Where did I put that flask?' | |||
}, { | |||
msg: '*coughs*' | |||
}], | |||
priest: [{ | |||
msg: 'peace be with you' | |||
}, { | |||
msg: 'walk in the light' | |||
}] | |||
}; |
@@ -106,5 +106,169 @@ module.exports = { | |||
targetName: 'hermit' | |||
}] | |||
} | |||
}, | |||
/*estrid: { | |||
'1': { | |||
msg: [{ | |||
msg: `Is there anything I can help you with today?`, | |||
options: [1.1, 1.2, 1.3, 1.4, 1.5] | |||
}], | |||
options: { | |||
'1.1': { | |||
msg: `How long have you been working here?`, | |||
goto: 2 | |||
}, | |||
'1.2': { | |||
msg: `Why do you sell equipment instead of potions?`, | |||
goto: 3 | |||
}, | |||
'1.3': { | |||
msg: `I'd like to browse your wares.`, | |||
goto: 'tradeBuy' | |||
}, | |||
'1.4': { | |||
msg: `I have some items to sell`, | |||
goto: 'tradeSell' | |||
}, | |||
'1.5': { | |||
msg: `I want to buy something back`, | |||
goto: 'tradeBuyback' | |||
} | |||
} | |||
}, | |||
'2': { | |||
msg: `I haven't been working here long, but I was born and raised here by my mother. She ran the shop before me.`, | |||
options: { | |||
'2.1': { | |||
msg: `Where is your mother now?`, | |||
goto: '2-1' | |||
}, | |||
'2.2': { | |||
msg: `I'd like to ask something else.`, | |||
goto: 1 | |||
} | |||
} | |||
}, | |||
'2-1': { | |||
msg: `A few months ago, she...took ill. She's been bedridden upstairs ever since.`, | |||
options: { | |||
'2-1.1': { | |||
msg: `I'd like to ask something else.`, | |||
goto: 1 | |||
} | |||
} | |||
}, | |||
'3': { | |||
msg: `The developer hasn't added alchemy or potions yet. Hopefully he'll do that soon.`, | |||
options: { | |||
'3.1': { | |||
msg: `I'd like to ask something else.`, | |||
goto: 1 | |||
} | |||
} | |||
}, | |||
tradeBuy: { | |||
cpn: 'trade', | |||
method: 'startBuy', | |||
args: [{ | |||
targetName: 'estrid' | |||
}] | |||
}, | |||
tradeSell: { | |||
cpn: 'trade', | |||
method: 'startSell', | |||
args: [{ | |||
targetName: 'estrid' | |||
}] | |||
}, | |||
tradeBuyback: { | |||
cpn: 'trade', | |||
method: 'startBuyback', | |||
args: [{ | |||
targetName: 'estrid' | |||
}] | |||
} | |||
},*/ | |||
vikar: { | |||
'1': { | |||
msg: [{ | |||
msg: `Is there anything I can help you with today?`, | |||
options: [1.1] | |||
}], | |||
options: { | |||
'1.1': { | |||
msg: `I want to hand some cards.`, | |||
goto: 'tradeCards' | |||
} | |||
} | |||
}, | |||
tradeCards: { | |||
msg: [{ | |||
msg: ``, | |||
options: [] | |||
}], | |||
method: function (obj) { | |||
var inventory = obj.inventory; | |||
var items = inventory.items; | |||
var sets = items.filter(function (i) { | |||
return ( | |||
(i.type == 'Reward Card') && | |||
(i.quantity >= i.setSize) | |||
); | |||
}); | |||
if (sets.length == 0) | |||
return `Sorry, you don't have any completed sets.`; | |||
sets.forEach(function (s) { | |||
obj.instance.eventEmitter.emit('onGetCardSetReward', s.name, obj); | |||
inventory.destroyItem(s.id, s.setSize); | |||
}); | |||
} | |||
} | |||
}, | |||
priest: { | |||
'1': { | |||
msg: [{ | |||
msg: `Is there anything I can help you with today?`, | |||
options: [1.1, 1.2, 1.3] | |||
}], | |||
options: { | |||
'1.1': { | |||
msg: `I'd like to browse your wares.`, | |||
goto: 'tradeBuy' | |||
}, | |||
'1.2': { | |||
msg: `I have some items to sell`, | |||
goto: 'tradeSell' | |||
}, | |||
'1.3': { | |||
msg: `I want to buy something back`, | |||
goto: 'tradeBuyback' | |||
} | |||
} | |||
}, | |||
tradeBuy: { | |||
cpn: 'trade', | |||
method: 'startBuy', | |||
args: [{ | |||
targetName: 'priest' | |||
}] | |||
}, | |||
tradeSell: { | |||
cpn: 'trade', | |||
method: 'startSell', | |||
args: [{ | |||
targetName: 'priest' | |||
}] | |||
}, | |||
tradeBuyback: { | |||
cpn: 'trade', | |||
method: 'startBuyback', | |||
args: [{ | |||
targetName: 'priest' | |||
}] | |||
} | |||
} | |||
}; | |||
}; |
@@ -20,6 +20,14 @@ module.exports = { | |||
type: 'fish', | |||
quantity: [6, 12] | |||
}, | |||
estriddoor: { | |||
properties: { | |||
cpnDoor: { | |||
locked: true, | |||
key: 'estrid' | |||
} | |||
} | |||
}, | |||
shophermit: { | |||
properties: { | |||
cpnNotice: { | |||
@@ -39,6 +47,63 @@ module.exports = { | |||
} | |||
} | |||
}, | |||
shopestrid: { | |||
properties: { | |||
cpnNotice: { | |||
actions: { | |||
enter: { | |||
cpn: 'dialogue', | |||
method: 'talk', | |||
args: [{ | |||
targetName: 'estrid' | |||
}] | |||
}, | |||
exit: { | |||
cpn: 'dialogue', | |||
method: 'stopTalk' | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
shopvikar: { | |||
properties: { | |||
cpnNotice: { | |||
actions: { | |||
enter: { | |||
cpn: 'dialogue', | |||
method: 'talk', | |||
args: [{ | |||
targetName: 'vikar' | |||
}] | |||
}, | |||
exit: { | |||
cpn: 'dialogue', | |||
method: 'stopTalk' | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
shoppriest: { | |||
properties: { | |||
cpnNotice: { | |||
actions: { | |||
enter: { | |||
cpn: 'dialogue', | |||
method: 'talk', | |||
args: [{ | |||
targetName: 'priest' | |||
}] | |||
}, | |||
exit: { | |||
cpn: 'dialogue', | |||
method: 'stopTalk' | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
'estuary entrance': { | |||
components: { | |||
cpnParticles: { | |||
@@ -90,6 +155,131 @@ module.exports = { | |||
} | |||
} | |||
} | |||
}, | |||
greencandle: { | |||
components: { | |||
cpnLight: { | |||
simplify: function () { | |||
return { | |||
type: 'light', | |||
blueprint: { | |||
color: { | |||
start: ['80f643'], | |||
end: ['4ac441', '51fc9a', 'd07840'] | |||
}, | |||
lifetime: { | |||
min: 2, | |||
max: 6 | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
alchgreenpot: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function () { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['80f643', '80f643'], | |||
end: ['4ac441', '4ac441'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 10 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 2 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 4, | |||
max: 16 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 8 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 4 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.1, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -15, | |||
y: -20, | |||
w: 30, | |||
h: 8 | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
alchredpot: { | |||
components: { | |||
cpnParticles: { | |||
simplify: function () { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['ff4252', 'ff4252'], | |||
end: ['a82841', 'a82841'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 10 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 2 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 4, | |||
max: 16 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 8 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 4 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.2, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -15, | |||
y: -28, | |||
w: 30, | |||
h: 8 | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
mobs: { | |||
@@ -237,6 +427,45 @@ module.exports = { | |||
} | |||
} | |||
}, | |||
guard: { | |||
level: 50, | |||
deathRep: -15, | |||
walkDistance: 0, | |||
rare: { | |||
count: 0 | |||
} | |||
}, | |||
estrid: { | |||
level: 25, | |||
deathRep: -7, | |||
walkDistance: 5, | |||
rare: { | |||
count: 0 | |||
}, | |||
properties: { | |||
cpnTrade: { | |||
items: { | |||
min: 5, | |||
max: 10 | |||
}, | |||
level: { | |||
min: 5, | |||
max: 15 | |||
}, | |||
markup: { | |||
buy: 0.25, | |||
sell: 2.5 | |||
} | |||
} | |||
} | |||
}, | |||
vikar: { | |||
walkDistance: 0 | |||
}, | |||
rodriguez: { | |||
attackable: false, | |||
level: 10, | |||
@@ -267,6 +496,40 @@ module.exports = { | |||
rare: { | |||
count: 0 | |||
} | |||
}, | |||
'priest': { | |||
level: 50, | |||
deathRep: -15, | |||
walkDistance: 0, | |||
rare: { | |||
count: 0 | |||
}, | |||
properties: { | |||
cpnTrade: { | |||
items: { | |||
min: 5, | |||
max: 10, | |||
extra: [{ | |||
type: 'skin', | |||
id: 'gaekatla druid', | |||
worth: 100, | |||
factions: [{ | |||
id: 'gaekatla', | |||
tier: 7 | |||
}] | |||
}] | |||
}, | |||
faction: { | |||
id: 'gaekatla', | |||
tier: 5 | |||
}, | |||
markup: { | |||
buy: 0.25, | |||
sell: 10 | |||
} | |||
} | |||
} | |||
} | |||
} | |||
}; |
@@ -0,0 +1,100 @@ | |||
define([ | |||
'items/generator' | |||
], function ( | |||
itemGenerator | |||
) { | |||
var config = { | |||
'Runecrafter\'s Toil': { | |||
chance: 0.5, | |||
reward: 'Random Rune', | |||
setSize: 3, | |||
mobLevel: [3, 100] | |||
}, | |||
'Godly Promise': { | |||
chance: 0.025, | |||
reward: 'Random Level 15 Legendary Weapon', | |||
setSize: 12, | |||
zone: 'sewer' | |||
} | |||
}; | |||
return { | |||
init: function () { | |||
}, | |||
fixCard: function (card) { | |||
var template = config[card.name]; | |||
if (!template) | |||
return; | |||
card.setSize = template.setSize; | |||
}, | |||
getCard: function (mob) { | |||
var pool = []; | |||
var mobLevel = mob.stats.values.level; | |||
Object.keys(config).forEach(function (c) { | |||
var card = config[c]; | |||
var rqrLevel = card.mobLevel; | |||
if (rqrLevel) { | |||
if ((rqrLevel.push) && ((mobLevel < rqrLevel[0]) || (mobLevel > rqrLevel[1]))) | |||
return; | |||
else if ((!rqrLevel.push) && (mobLevel != rqrLevel)) | |||
return; | |||
} | |||
var rolls = card.rolls; | |||
for (var i = 0; i < rolls; i++) { | |||
pool.push(c); | |||
} | |||
}, this); | |||
if (pool.length == 0) | |||
return; | |||
var pickName = pool[~~(Math.random() * pool.length)]; | |||
var pick = config[pickName]; | |||
var card = { | |||
name: pickName, | |||
spritesheet: `${this.folderName}/images/items.png`, | |||
type: 'Reward Card', | |||
description: 'Reward: ' + pick.reward, | |||
noSalvage: true, | |||
sprite: [0, 0], | |||
quantity: 1, | |||
quality: 1, | |||
setSize: pick.setSize | |||
}; | |||
return card; | |||
}, | |||
getReward: function (set) { | |||
var reward = config[set].reward; | |||
return this.rewards[reward](); | |||
}, | |||
rewards: { | |||
'Random Rune': function () { | |||
return itemGenerator.generate({ | |||
spell: true | |||
}); | |||
}, | |||
'Random Level 15 Legendary Weapon': function () { | |||
return itemGenerator.generate({ | |||
level: 15, | |||
quality: 4, | |||
noSpell: true, | |||
slot: 'twoHanded' | |||
}); | |||
} | |||
} | |||
}; | |||
}); |
@@ -0,0 +1,47 @@ | |||
define([ | |||
'mods/feature-cards/cards' | |||
], function ( | |||
cards | |||
) { | |||
return { | |||
name: 'Feature: Cards', | |||
extraScripts: [ | |||
'cards' | |||
], | |||
init: function () { | |||
cards.init(); | |||
this.events.on('onBeforeDropBag', this.onBeforeDropBag.bind(this)); | |||
this.events.on('onGetCardSetReward', this.onGetCardSetReward.bind(this)); | |||
this.events.on('onBeforeGetItem', this.onBeforeGetItem.bind(this)); | |||
}, | |||
onBeforeDropBag: function (dropper, items, looter) { | |||
if (!looter.player) | |||
return; | |||
var res = cards.getCard(dropper); | |||
if (!res) | |||
return; | |||
items.push(res); | |||
}, | |||
onBeforeGetItem: function (item, obj) { | |||
if ((!obj.player) && (item.type != 'Reward Card')) | |||
return; | |||
cards.fixCard(item); | |||
}, | |||
onGetCardSetReward: function (set, obj) { | |||
var reward = cards.getReward(set); | |||
console.log(reward); | |||
obj.inventory.getItem(reward); | |||
} | |||
}; | |||
}); |
@@ -12,7 +12,8 @@ define([ | |||
'events/events', | |||
'misc/scheduler', | |||
'misc/mail', | |||
'config/herbs' | |||
'config/herbs', | |||
'misc/events' | |||
], function ( | |||
map, | |||
syncer, | |||
@@ -27,7 +28,8 @@ define([ | |||
events, | |||
scheduler, | |||
mail, | |||
herbs | |||
herbs, | |||
eventEmitter | |||
) { | |||
return { | |||
instances: [], | |||
@@ -55,7 +57,8 @@ define([ | |||
zone: map.zone, | |||
mail: mail, | |||
map: map, | |||
scheduler: scheduler | |||
scheduler: scheduler, | |||
eventEmitter: eventEmitter | |||
}; | |||
this.instances.push(fakeInstance); | |||
@@ -474,7 +477,8 @@ define([ | |||
events: extend(true, {}, events), | |||
scheduler: extend(true, {}, scheduler), | |||
mail: extend(true, {}, mail), | |||
map: newMap | |||
map: newMap, | |||
eventEmitter: extend(true, {}, eventEmitter) | |||
}; | |||
['objects', 'spawners', 'syncer', 'resourceSpawner', 'questBuilder', 'events', 'scheduler', 'mail'].forEach(i => instance[i].init(instance)); | |||