@@ -403,7 +403,44 @@ module.exports = { | |||
try { | |||
let effectModule = require('../' + effectUrl); | |||
e.events = effectModule.events; | |||
if (effectModule.events.onGetText) | |||
const { rolls } = e; | |||
if (rolls.textTemplate) { | |||
let text = rolls.textTemplate; | |||
while (text.includes('((')) { | |||
Object.entries(rolls).forEach(([k, v]) => { | |||
text = text.replaceAll(`((${k}))`, v); | |||
}); | |||
if (rolls.applyEffect) { | |||
Object.entries(rolls.applyEffect).forEach(([k, v]) => { | |||
text = text.replaceAll(`((applyEffect.${k}))`, v); | |||
}); | |||
} | |||
if (rolls.castSpell) { | |||
Object.entries(rolls.castSpell).forEach(([k, v]) => { | |||
text = text.replaceAll(`((castSpell.${k}))`, v); | |||
}); | |||
} | |||
if (rolls.applyEffect?.scaleDamage) { | |||
Object.entries(rolls.applyEffect.scaleDamage).forEach(([k, v]) => { | |||
text = text.replaceAll(`((applyEffect.scaleDamage.${k}))`, v); | |||
}); | |||
} | |||
if (rolls.castSpell?.scaleDamage) { | |||
Object.entries(rolls.castSpell.scaleDamage).forEach(([k, v]) => { | |||
text = text.replaceAll(`((castSpell.scaleDamage.${k}))`, v); | |||
}); | |||
} | |||
} | |||
e.text = text; | |||
} else if (effectModule.events.onGetText) | |||
e.text = effectModule.events.onGetText(item, e); | |||
} catch (error) { | |||
_.log(`Effect not found: ${e.type}`); | |||
@@ -536,7 +536,9 @@ module.exports = { | |||
target: Target object (heal.target) | |||
spell: Optional spell object that caused this event | |||
*/ | |||
getHp: function (heal, source, event) { | |||
getHp: function (event) { | |||
const { heal, source } = event; | |||
let amount = heal.amount; | |||
if (amount === 0) | |||
return; | |||
@@ -548,48 +550,45 @@ module.exports = { | |||
let values = this.values; | |||
let hpMax = values.hpMax; | |||
if (values.hp >= hpMax) | |||
return; | |||
if (hpMax - values.hp < amount) | |||
amount = hpMax - values.hp; | |||
values.hp += amount; | |||
if (values.hp > hpMax) | |||
values.hp = hpMax; | |||
let recipients = []; | |||
if (this.obj.serverId) | |||
recipients.push(this.obj.serverId); | |||
if (source.serverId) | |||
recipients.push(source.serverId); | |||
if (recipients.length > 0) { | |||
this.syncer.queue('onGetDamage', { | |||
id: this.obj.id, | |||
source: source.id, | |||
heal: true, | |||
amount: amount, | |||
crit: heal.crit, | |||
element: heal.element | |||
}, recipients); | |||
} | |||
if (values.hp < hpMax) { | |||
if (hpMax - values.hp < amount) | |||
amount = hpMax - values.hp; | |||
values.hp += amount; | |||
if (values.hp > hpMax) | |||
values.hp = hpMax; | |||
let recipients = []; | |||
if (this.obj.serverId) | |||
recipients.push(this.obj.serverId); | |||
if (source.serverId) | |||
recipients.push(source.serverId); | |||
if (recipients.length > 0) { | |||
this.syncer.queue('onGetDamage', { | |||
id: this.obj.id, | |||
source: source.id, | |||
heal: true, | |||
amount: amount, | |||
crit: heal.crit, | |||
element: heal.element | |||
}, recipients); | |||
} | |||
//Add aggro to all our attackers | |||
let threat = amount * 0.4 * threatMult; | |||
if (threat !== 0) { | |||
let aggroList = this.obj.aggro.list; | |||
let aLen = aggroList.length; | |||
for (let i = 0; i < aLen; i++) { | |||
let a = aggroList[i].obj; | |||
a.aggro.tryEngage(source, threat); | |||
//Add aggro to all our attackers | |||
let threat = amount * 0.4 * threatMult; | |||
if (threat !== 0) { | |||
let aggroList = this.obj.aggro.list; | |||
let aLen = aggroList.length; | |||
for (let i = 0; i < aLen; i++) { | |||
let a = aggroList[i].obj; | |||
a.aggro.tryEngage(source, threat); | |||
} | |||
} | |||
} | |||
this.obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp); | |||
this.obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp); | |||
} | |||
//We want to eventually replace the first two args with the event object | |||
// For now, only fire the event when the event object is specified. | |||
if (!heal.noEvents && event) | |||
if (!heal.noEvents) | |||
source.fireEvent('afterGiveHp', event); | |||
}, | |||
@@ -730,7 +729,11 @@ module.exports = { | |||
if (target === obj || !lifeOnHit) | |||
return; | |||
this.getHp({ amount: lifeOnHit }, obj); | |||
this.getHp({ | |||
event: { amount: lifeOnHit }, | |||
source: obj, | |||
target: obj | |||
}); | |||
} | |||
} | |||
}; |
@@ -6,7 +6,11 @@ module.exports = { | |||
events: { | |||
afterDealDamage: function ({ damage, target }) { | |||
damage.dealt *= 0.5; | |||
this.obj.stats.getHp(damage, this.obj); | |||
this.obj.stats.getHp({ | |||
event: damage, | |||
source: this.obj, | |||
target: this.obj | |||
}); | |||
} | |||
} | |||
}; |
@@ -1,37 +1,95 @@ | |||
//Imports | |||
const spellBaseTemplate = require('../spells/spellTemplate'); | |||
module.exports = { | |||
events: { | |||
onGetText: function (item) { | |||
const { rolls: { chance, spell, damage = 1 } } = item.effects.find(e => (e.type === 'castSpellOnHit')); | |||
//Helpers | |||
const getItemEffect = item => { | |||
return item.effects.find(e => (e.type === 'castSpellOnHit')); | |||
}; | |||
return `${chance}% chance to cast a ${damage} damage ${spell} on hit`; | |||
}, | |||
const doesEventMatch = (firedEvent, eventCondition) => { | |||
if ( | |||
!firedEvent || | |||
( | |||
!eventCondition.targetNotSelf && | |||
firedEvent.target === firedEvent.source | |||
) | |||
) | |||
return false; | |||
const foundNonMatch = Object.entries(eventCondition).some(([k, v]) => { | |||
if (v !== null && typeof(v) === 'object') { | |||
if (!doesEventMatch(firedEvent[k], v)) | |||
return true; | |||
return false; | |||
} | |||
return firedEvent[k] !== v; | |||
}); | |||
return !foundNonMatch; | |||
}; | |||
const shouldApplyEffect = (itemEffect, firedEvent, firedEventName) => { | |||
const { rolls: { chance, combatEvent: { [firedEventName]: eventCondition } } } = itemEffect; | |||
if (!eventCondition || !doesEventMatch(firedEvent, eventCondition)) | |||
return false; | |||
if (chance !== undefined && Math.random() * 100 >= chance) | |||
return false; | |||
return true; | |||
}; | |||
const handler = (obj, item, event, firedEventName) => { | |||
const itemEffect = getItemEffect(item); | |||
afterDealDamage: function (item, { damage, target }) { | |||
//Should only proc for attacks...this is kind of a hack | |||
const { element } = damage; | |||
if (element) | |||
return; | |||
const { rolls: { chance, spell, statType = 'dex', damage: spellDamage = 1 } } = item.effects.find(e => (e.type === 'castSpellOnHit')); | |||
const chanceRoll = Math.random() * 100; | |||
if (chanceRoll >= chance) | |||
return; | |||
const spellName = 'spell' + spell.replace(/./, spell.toUpperCase()[0]); | |||
const spellTemplate = require(`../spells/${spellName}`); | |||
const builtSpell = extend({ obj: this }, spellBaseTemplate, spellTemplate, { | |||
name: spellName, | |||
noEvents: true, | |||
statType, | |||
damage: spellDamage, | |||
duration: 5, | |||
radius: 1 | |||
if (!shouldApplyEffect(itemEffect, event, firedEventName)) | |||
return; | |||
const { rolls: { castSpell, castTarget } } = itemEffect; | |||
const spellConfig = extend({}, castSpell); | |||
spellConfig.noEvents = true; | |||
const scaleDamage = spellConfig.scaleDamage; | |||
delete spellConfig.scaleDamage; | |||
if (scaleDamage) { | |||
if (scaleDamage.s_useOriginal) { | |||
scaleDamage.s_useOriginal.forEach(s => { | |||
spellConfig[s] = event.spell[s]; | |||
}); | |||
} | |||
if (scaleDamage.percentage) | |||
spellConfig.damage *= (scaleDamage.percentage / 100); | |||
} | |||
const spellName = 'spell' + spellConfig.type.replace(/./, spellConfig.type.toUpperCase()[0]); | |||
const spellTemplate = require(`../spells/${spellName}`); | |||
const builtSpell = extend({ obj }, spellBaseTemplate, spellTemplate, spellConfig); | |||
let target = event.target; | |||
if (castTarget === 'self') | |||
target = obj; | |||
else if (castTarget === 'none') | |||
target = undefined; | |||
builtSpell.cast({ target }); | |||
}; | |||
//Effect | |||
module.exports = { | |||
events: { | |||
afterGiveHp: function (item, event) { | |||
handler(this, item, event, 'afterGiveHp'); | |||
}, | |||
builtSpell.cast(); | |||
afterDealDamage: function (item, event) { | |||
handler(this, item, event, 'afterDealDamage'); | |||
} | |||
} | |||
}; |
@@ -18,9 +18,13 @@ module.exports = { | |||
amount = (cpnStats.values.hpMax / 100) * ~~amount.replace('%', ''); | |||
cpnStats.getHp({ | |||
amount: amount, | |||
threatMult: 0 | |||
}, item); | |||
event: { | |||
amount: amount, | |||
threatMult: 0 | |||
}, | |||
source: cpnStats.obj, | |||
target: cpnStats.obj | |||
}); | |||
} else | |||
cpnStats.addStat(stat, amount); | |||
}, | |||
@@ -34,8 +34,12 @@ module.exports = { | |||
let amount = rolls.amount || ((damage.dealt / 100) * rolls.percentage); | |||
this.stats.getHp({ | |||
amount: amount | |||
}, this); | |||
event: { | |||
amount: amount | |||
}, | |||
source: this, | |||
target: this | |||
}); | |||
} | |||
} | |||
}; |
@@ -39,7 +39,11 @@ let cpnArcanePatch = { | |||
let c = contents[i]; | |||
let amount = this.spell.getDamage(c, true); | |||
c.stats.getHp(amount, this.caster); | |||
c.stats.getHp({ | |||
event: amount, | |||
source: this.caster, | |||
target: c | |||
}); | |||
} | |||
} | |||
}; | |||
@@ -9,7 +9,11 @@ let cpnHealPatch = { | |||
}, | |||
applyHeal: function (o, amount) { | |||
o.stats.getHp(amount, this.caster); | |||
o.stats.getHp({ | |||
event: amount, | |||
source: this.caster, | |||
target: o | |||
}); | |||
}, | |||
collisionEnter: function (o) { | |||
@@ -17,14 +17,18 @@ module.exports = { | |||
const target = action.target; | |||
const { x, y } = target; | |||
const amount = this.getDamage(target, true); | |||
const heal = this.getDamage(target, true); | |||
heal.noEvents = this.noEvents; | |||
const event = { | |||
heal: amount, | |||
heal, | |||
source: this.obj, | |||
target, | |||
spellName: 'singleTargetHeal', | |||
spell: this | |||
}; | |||
target.stats.getHp(amount, this.obj, event); | |||
target.stats.getHp(event); | |||
const effect = { | |||
x, | |||
@@ -96,9 +96,13 @@ module.exports = { | |||
mLen--; | |||
} else if ((Math.abs(x - m.x) <= 1) && (Math.abs(y - m.y) <= 1)) { | |||
m.destroyed = true; | |||
this.obj.stats.getHp({ | |||
amount: obj.stats.values.hpMax / 10 | |||
}, obj); | |||
obj.stats.getHp({ | |||
event: { | |||
amount: obj.stats.values.hpMax / 10 | |||
}, | |||
source: obj, | |||
target: obj | |||
}); | |||
obj.instance.syncer.queue('onGetObject', { | |||
x: m.x, | |||
@@ -1,3 +1,5 @@ | |||
/* eslint-disable max-lines-per-function */ | |||
module.exports = { | |||
fixDb: async function () { | |||
await io.deleteAsync({ | |||
@@ -116,6 +118,34 @@ module.exports = { | |||
effect.properties.element = 'poison'; | |||
}); | |||
items | |||
.filter(i => i.name === 'Gourdhowl') | |||
.forEach(i => { | |||
const effect = i.effects[0]; | |||
if (!effect.rolls.castSpell) { | |||
effect.rolls = { | |||
castSpell: { | |||
type: 'whirlwind', | |||
damage: effect.rolls.damage, | |||
range: 1, | |||
statType: 'str', | |||
statMult: 1, | |||
isAttack: true | |||
}, | |||
castTarget: 'none', | |||
chance: effect.rolls.chance, | |||
textTemplate: 'Grants you a ((chance))% chance to cast a ((castSpell.damage)) damage whirlwind on hit', | |||
combatEvent: { | |||
name: 'afterDealDamage', | |||
afterDealDamage: { | |||
spellName: 'melee' | |||
} | |||
} | |||
}; | |||
} | |||
}); | |||
items | |||
.filter(f => f.effects?.[0]?.factionId === 'akarei' && !f.effects[0].properties) | |||
.forEach(function (i) { | |||
@@ -16,7 +16,7 @@ const rollValues = (rollsDefinition, result) => { | |||
const isInt = (p.indexOf('i_') === 0); | |||
const fieldName = p.replace('i_', ''); | |||
if (!entry.push) { | |||
if (!Array.isArray(entry) || p.indexOf('s_') === 0) { | |||
result[fieldName] = range; | |||
continue; | |||
@@ -45,7 +45,11 @@ module.exports = { | |||
amount = amount * this.shieldMultiplier; | |||
const heal = { amount }; | |||
target.stats.getHp(heal, obj); | |||
target.stats.getHp({ | |||
event: heal, | |||
source: obj, | |||
target | |||
}); | |||
//Only reset the first spell's cooldown if it's an auto attack and not a spell | |||
const firstSpell = target.spellbook.spells[0]; | |||
@@ -54,7 +54,11 @@ module.exports = { | |||
let healAmount = damage.amount * (this.healPercent / 100); | |||
obj.stats.getHp({ | |||
amount: healAmount | |||
}, obj); | |||
event: { | |||
amount: healAmount | |||
}, | |||
source: obj, | |||
target: obj | |||
}); | |||
} | |||
}; |