@@ -146,6 +146,14 @@ | |||
"func-style": [1,"expression"], | |||
"indent": [2, "tab"], | |||
"key-spacing": [2,{"afterColon":true}], | |||
"max-lines-per-function": [ | |||
2, | |||
{ | |||
"max": 120, | |||
"skipBlankLines": false, | |||
"skipComments": false | |||
} | |||
], | |||
"new-parens": 2, | |||
"no-inline-comments": 2, | |||
"no-lonely-if": 2, | |||
@@ -249,15 +249,7 @@ module.exports = { | |||
const hasSpace = this.hasSpace(item, true); | |||
if (!hasSpace) { | |||
this.obj.instance.syncer.queue('onGetMessages', { | |||
id: this.obj.id, | |||
messages: [{ | |||
class: 'color-redA', | |||
message: 'Your bags are too full to split that stack', | |||
type: 'info' | |||
}] | |||
}, [this.obj.serverId]); | |||
this.notifyNoBagSpace(); | |||
return; | |||
} | |||
@@ -548,6 +540,8 @@ module.exports = { | |||
hookItemEvents: function (items) { | |||
items = items || this.items; | |||
if (!items.push) | |||
items = [ items ]; | |||
let iLen = items.length; | |||
for (let i = 0; i < iLen; i++) { | |||
let item = items[i]; | |||
@@ -568,6 +562,7 @@ module.exports = { | |||
try { | |||
let effectModule = require('../' + effectUrl); | |||
e.events = effectModule.events; | |||
e.text = effectModule.events.onGetText(item, e); | |||
} catch (error) {} | |||
} | |||
}); | |||
@@ -793,16 +788,8 @@ module.exports = { | |||
let iLen = items.length; | |||
if (!this.hasSpace(item)) { | |||
if (!hideMessage) { | |||
this.obj.instance.syncer.queue('onGetMessages', { | |||
id: this.obj.id, | |||
messages: [{ | |||
class: 'color-redA', | |||
message: 'Your bags are too full to loot any more items', | |||
type: 'info' | |||
}] | |||
}, [this.obj.serverId]); | |||
} | |||
if (!hideMessage) | |||
this.notifyNoBagSpace(); | |||
return false; | |||
} | |||
@@ -830,19 +817,18 @@ module.exports = { | |||
} | |||
if (this.obj.player) { | |||
let messages = []; | |||
let msg = item.name; | |||
if (quantity) | |||
msg += ' x' + quantity; | |||
else if ((item.stats) && (item.stats.weight)) | |||
msg += ` ${item.stats.weight}lb`; | |||
messages.push({ | |||
const messages = [{ | |||
class: 'q' + item.quality, | |||
message: 'loot: {' + msg + '}', | |||
item: item, | |||
type: 'loot' | |||
}); | |||
}]; | |||
if (!hideAlert) { | |||
this.obj.instance.syncer.queue('onGetDamage', { | |||
@@ -860,23 +846,8 @@ module.exports = { | |||
} | |||
} | |||
if (item.effects) { | |||
item.effects.forEach(function (e) { | |||
if (e.mtx) { | |||
let mtxUrl = mtx.get(e.mtx); | |||
let mtxModule = require('../' + mtxUrl); | |||
e.events = mtxModule.events; | |||
} else if (e.type) { | |||
let effectUrl = itemEffects.get(e.type); | |||
try { | |||
let effectModule = require('../' + effectUrl); | |||
e.text = effectModule.events.onGetText(item, e); | |||
e.events = effectModule.events; | |||
} catch (error) {} | |||
} | |||
}); | |||
} | |||
if (item.effects) | |||
this.hookItemEvents([item]); | |||
if (!exists) | |||
this.items.push(item); | |||
@@ -896,10 +867,8 @@ module.exports = { | |||
this.obj.syncer.setArray(true, 'inventory', 'getItems', this.simplifyItem(item), true); | |||
} | |||
if (!hideMessage) { | |||
if (fromMob) | |||
this.obj.fireEvent('afterLootMobItem', item); | |||
} | |||
if (!hideMessage && fromMob) | |||
this.obj.fireEvent('afterLootMobItem', item); | |||
return item; | |||
}, | |||
@@ -1068,5 +1037,16 @@ module.exports = { | |||
canEquipItem: function (item) { | |||
return (this.equipItemErrors(item).length === 0); | |||
}, | |||
notifyNoBagSpace: function () { | |||
this.obj.instance.syncer.queue('onGetMessages', { | |||
id: this.obj.id, | |||
messages: [{ | |||
class: 'color-redA', | |||
message: 'Your bags are too full to loot any more items', | |||
type: 'info' | |||
}] | |||
}, [this.obj.serverId]); | |||
} | |||
}; |
@@ -384,6 +384,70 @@ 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); | |||
obj.fireEvent('afterDeath', deathEvent); | |||
if (obj.player) { | |||
obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp); | |||
if (deathEvent.permadeath) { | |||
obj.auth.permadie(); | |||
obj.instance.syncer.queue('onGetMessages', { | |||
messages: { | |||
class: 'color-redA', | |||
message: `(level ${this.values.level}) ${obj.name} has forever left the shores of the living.` | |||
} | |||
}, -1); | |||
this.syncer.queue('onPermadeath', { | |||
source: killSource.name | |||
}, [obj.serverId]); | |||
} else | |||
this.values.hp = 0; | |||
obj.player.die(killSource, deathEvent.permadeath); | |||
} else { | |||
obj.effects.die(); | |||
if (this.obj.spellbook) | |||
this.obj.spellbook.die(); | |||
obj.destroyed = true; | |||
let deathAnimation = _.getDeepProperty(animations, ['mobs', obj.sheetName, obj.cell, 'death']); | |||
if (deathAnimation) { | |||
obj.instance.syncer.queue('onGetObject', { | |||
x: obj.x, | |||
y: obj.y, | |||
components: [deathAnimation] | |||
}, -1); | |||
} | |||
if (obj.inventory) { | |||
let aggroList = obj.aggro.list; | |||
let aLen = aggroList.length; | |||
for (let i = 0; i < aLen; i++) { | |||
let a = aggroList[i]; | |||
if (a.damage <= 0 || !a.obj.has('serverId')) | |||
continue; | |||
obj.inventory.dropBag(a.obj.name, killSource); | |||
} | |||
} | |||
} | |||
}, | |||
die: function (source) { | |||
let obj = this.obj; | |||
let values = this.values; | |||
@@ -485,18 +549,10 @@ module.exports = { | |||
source.fireEvent('beforeDealDamage', damage, obj); | |||
obj.fireEvent('beforeTakeDamage', damage, source); | |||
//Maybe the attacker was stunned? | |||
if (damage.failed) | |||
return; | |||
//Maybe something else killed this mob already? | |||
if (obj.destroyed) | |||
if (damage.failed || obj.destroyed) | |||
return; | |||
let amount = damage.amount; | |||
if (amount > this.values.hp) | |||
amount = this.values.hp; | |||
let amount = Math.min(this.values.hp, damage.amount); | |||
damage.dealt = amount; | |||
@@ -548,67 +604,8 @@ module.exports = { | |||
obj.instance.eventEmitter.emitNoSticky('onBeforeActorDies', death, obj, source); | |||
obj.fireEvent('beforeDeath', death); | |||
if (death.success) { | |||
let deathEvent = {}; | |||
let killSource = source; | |||
if (source.follower) | |||
killSource = source.follower.master; | |||
if (killSource.player) | |||
killSource.stats.kill(obj); | |||
obj.fireEvent('afterDeath', deathEvent); | |||
if (obj.player) { | |||
obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp); | |||
if (deathEvent.permadeath) { | |||
obj.auth.permadie(); | |||
obj.instance.syncer.queue('onGetMessages', { | |||
messages: { | |||
class: 'color-redA', | |||
message: `(level ${this.values.level}) ${obj.name} has forever left the shores of the living.` | |||
} | |||
}, -1); | |||
this.syncer.queue('onPermadeath', { | |||
source: killSource.name | |||
}, [obj.serverId]); | |||
} else | |||
this.values.hp = 0; | |||
obj.player.die(killSource, deathEvent.permadeath); | |||
} else { | |||
obj.effects.die(); | |||
if (this.obj.spellbook) | |||
this.obj.spellbook.die(); | |||
obj.destroyed = true; | |||
let deathAnimation = _.getDeepProperty(animations, ['mobs', obj.sheetName, obj.cell, 'death']); | |||
if (deathAnimation) { | |||
obj.instance.syncer.queue('onGetObject', { | |||
x: obj.x, | |||
y: obj.y, | |||
components: [deathAnimation] | |||
}, -1); | |||
} | |||
if (obj.inventory) { | |||
let aggroList = obj.aggro.list; | |||
let aLen = aggroList.length; | |||
for (let i = 0; i < aLen; i++) { | |||
let a = aggroList[i]; | |||
if (a.damage <= 0 || !a.obj.has('serverId')) | |||
continue; | |||
obj.inventory.dropBag(a.obj.name, killSource); | |||
} | |||
} | |||
} | |||
} | |||
if (death.success) | |||
this.preDeath(source); | |||
} else { | |||
source.aggro.tryEngage(obj, 0); | |||
obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp); | |||
@@ -1,5 +1,5 @@ | |||
module.exports = { | |||
version: '0.3.2', | |||
version: '0.4.0', | |||
port: 4000, | |||
startupMessage: 'Server: ready', | |||
defaultZone: 'fjolarok', | |||
@@ -7,6 +7,144 @@ let configCurrencies = require('./config/currencies'); | |||
let configSlots = require('./config/slots'); | |||
let generator = require('./generator'); | |||
const reroll = (item, msg) => { | |||
let enchantedStats = item.enchantedStats; | |||
delete item.enchantedStats; | |||
delete item.implicitStats; | |||
delete msg.addStatMsgs; | |||
if ((item.stats) && (item.stats.lvlRequire)) { | |||
item.level += item.stats.lvlRequire; | |||
delete item.originalLevel; | |||
} | |||
item.stats = {}; | |||
let bpt = { | |||
slot: item.slot, | |||
type: item.type, | |||
sprite: item.sprite, | |||
spritesheet: item.spritesheet | |||
}; | |||
generatorSlots.generate(item, bpt); | |||
generatorTypes.generate(item, bpt); | |||
generatorStats.generate(item, bpt); | |||
for (let p in enchantedStats) { | |||
if (!item.stats[p]) | |||
item.stats[p] = 0; | |||
item.stats[p] += enchantedStats[p]; | |||
if (p === 'lvlRequire') { | |||
if (!item.originalLevel) | |||
item.originalLevel = item.level; | |||
item.level -= enchantedStats[p]; | |||
if (item.level < 1) | |||
item.level = 1; | |||
} | |||
} | |||
item.enchantedStats = enchantedStats || null; | |||
}; | |||
const relevel = item => { | |||
if (item.slot === 'tool') | |||
return; | |||
let offset = 1 + ~~(Math.random() * 2); | |||
const maxLevel = consts.maxLevel; | |||
if (!item.originalLevel) | |||
item.level = Math.min(maxLevel, item.level + offset); | |||
else { | |||
offset = Math.min(maxLevel - item.originalLevel, offset); | |||
item.originalLevel = Math.min(maxLevel, item.originalLevel + offset); | |||
item.level = Math.min(maxLevel, item.level + offset); | |||
} | |||
}; | |||
const reslot = (item, msg) => { | |||
if (item.effects || item.slot === 'tool') | |||
return; | |||
if (item.originalLevel) | |||
item.level = item.originalLevel; | |||
delete item.enchantedStats; | |||
delete msg.addStatMsgs; | |||
let possibleStats = Object.keys(item.stats || {}); | |||
let newItem = generator.generate({ | |||
slot: configSlots.getRandomSlot(item.slot), | |||
level: item.level, | |||
quality: item.quality, | |||
stats: possibleStats, | |||
limitSlotStats: true | |||
}); | |||
delete item.spritesheet; | |||
delete item.stats; | |||
delete item.spell; | |||
delete item.implicitStats; | |||
delete item.power; | |||
extend(item, newItem); | |||
}; | |||
const reforge = item => { | |||
if (!item.spell) | |||
return; | |||
let spellName = item.spell.name.toLowerCase(); | |||
let oldSpell = item.spell; | |||
delete item.spell; | |||
generatorSpells.generate(item, { | |||
spellName: spellName | |||
}); | |||
item.spell = extend(oldSpell, item.spell); | |||
}; | |||
const scour = (item, result) => { | |||
if (!item.power) | |||
return; | |||
for (let p in item.enchantedStats) { | |||
let value = item.enchantedStats[p]; | |||
if (item.stats[p]) { | |||
result.addStatMsgs.push({ | |||
stat: p, | |||
value: -value | |||
}); | |||
item.stats[p] -= value; | |||
if (item.stats[p] <= 0) | |||
delete item.stats[p]; | |||
if (p === 'lvlRequire') { | |||
item.level = Math.min(consts.maxLevel, item.level + value); | |||
delete item.originalLevel; | |||
} | |||
} | |||
} | |||
delete item.enchantedStats; | |||
delete item.power; | |||
}; | |||
const augment = (item, inventory, result, msg) => { | |||
let newPower = (item.power || 0) + 1; | |||
if (newPower > 3) { | |||
inventory.resolveCallback(msg); | |||
return; | |||
} | |||
item.power = newPower; | |||
this.addStat(item, result); | |||
}; | |||
module.exports = { | |||
enchant: function (obj, item, msg) { | |||
let inventory = obj.inventory; | |||
@@ -36,133 +174,18 @@ module.exports = { | |||
inventory.destroyItem(invMaterial.id, m.quantity); | |||
}); | |||
if (msg.action === 'reroll') { | |||
let enchantedStats = item.enchantedStats; | |||
delete item.enchantedStats; | |||
delete item.implicitStats; | |||
delete msg.addStatMsgs; | |||
if ((item.stats) && (item.stats.lvlRequire)) { | |||
item.level += item.stats.lvlRequire; | |||
delete item.originalLevel; | |||
} | |||
item.stats = {}; | |||
let bpt = { | |||
slot: item.slot, | |||
type: item.type, | |||
sprite: item.sprite, | |||
spritesheet: item.spritesheet | |||
}; | |||
generatorSlots.generate(item, bpt); | |||
generatorTypes.generate(item, bpt); | |||
generatorStats.generate(item, bpt); | |||
for (let p in enchantedStats) { | |||
if (!item.stats[p]) | |||
item.stats[p] = 0; | |||
item.stats[p] += enchantedStats[p]; | |||
if (p === 'lvlRequire') { | |||
if (!item.originalLevel) | |||
item.originalLevel = item.level; | |||
item.level -= enchantedStats[p]; | |||
if (item.level < 1) | |||
item.level = 1; | |||
} | |||
} | |||
item.enchantedStats = enchantedStats || null; | |||
} else if (msg.action === 'relevel') { | |||
if (item.slot === 'tool') | |||
return; | |||
let offset = 1 + ~~(Math.random() * 2); | |||
const maxLevel = consts.maxLevel; | |||
if (!item.originalLevel) | |||
item.level = Math.min(maxLevel, item.level + offset); | |||
else { | |||
offset = Math.min(maxLevel - item.originalLevel, offset); | |||
item.originalLevel = Math.min(maxLevel, item.originalLevel + offset); | |||
item.level = Math.min(maxLevel, item.level + offset); | |||
} | |||
} else if (msg.action === 'reslot') { | |||
if (item.effects || item.slot === 'tool') | |||
return; | |||
if (item.originalLevel) | |||
item.level = item.originalLevel; | |||
delete item.enchantedStats; | |||
delete msg.addStatMsgs; | |||
let possibleStats = Object.keys(item.stats || {}); | |||
let newItem = generator.generate({ | |||
slot: configSlots.getRandomSlot(item.slot), | |||
level: item.level, | |||
quality: item.quality, | |||
stats: possibleStats, | |||
limitSlotStats: true | |||
}); | |||
delete item.spritesheet; | |||
delete item.stats; | |||
delete item.spell; | |||
delete item.implicitStats; | |||
delete item.power; | |||
extend(item, newItem); | |||
} else if (msg.action === 'reforge') { | |||
if (!item.spell) | |||
return; | |||
let spellName = item.spell.name.toLowerCase(); | |||
let oldSpell = item.spell; | |||
delete item.spell; | |||
generatorSpells.generate(item, { | |||
spellName: spellName | |||
}); | |||
item.spell = extend(oldSpell, item.spell); | |||
} else if (msg.action === 'scour') { | |||
if (!item.power) | |||
return; | |||
for (let p in item.enchantedStats) { | |||
let value = item.enchantedStats[p]; | |||
if (item.stats[p]) { | |||
result.addStatMsgs.push({ | |||
stat: p, | |||
value: -value | |||
}); | |||
item.stats[p] -= value; | |||
if (item.stats[p] <= 0) | |||
delete item.stats[p]; | |||
if (p === 'lvlRequire') { | |||
item.level = Math.min(consts.maxLevel, item.level + value); | |||
delete item.originalLevel; | |||
} | |||
} | |||
} | |||
delete item.enchantedStats; | |||
delete item.power; | |||
} else { | |||
let newPower = (item.power || 0) + 1; | |||
if (newPower > 3) { | |||
inventory.resolveCallback(msg); | |||
return; | |||
} | |||
item.power = newPower; | |||
this.addStat(item, result); | |||
} | |||
if (msg.action === 'reroll') | |||
reroll(item, msg); | |||
else if (msg.action === 'relevel') | |||
relevel(item); | |||
else if (msg.action === 'reslot') | |||
reslot(item, msg); | |||
else if (msg.action === 'reforge') | |||
reforge(item); | |||
else if (msg.action === 'scour') | |||
scour(item, result); | |||
else | |||
augment(item, inventory, result, msg); | |||
obj.syncer.setArray(true, 'inventory', 'getItems', inventory.simplifyItem(item)); | |||