Browse Source

modding #1976: refactored onGetHp and its events

tags/v0.12.0
Shaun 1 year ago
parent
commit
d699bc52a7
14 changed files with 251 additions and 87 deletions
  1. +38
    -1
      src/server/components/inventory.js
  2. +43
    -40
      src/server/components/stats.js
  3. +5
    -1
      src/server/config/effects/effectHolyVengeance.js
  4. +86
    -28
      src/server/config/itemEffects/castSpellOnHit.js
  5. +7
    -3
      src/server/config/itemEffects/gainStat.js
  6. +6
    -2
      src/server/config/itemEffects/healOnCrit.js
  7. +5
    -1
      src/server/config/spells/spellArcaneBarrier.js
  8. +5
    -1
      src/server/config/spells/spellHealingCircle.js
  9. +7
    -3
      src/server/config/spells/spellSingleTargetHeal.js
  10. +7
    -3
      src/server/config/spells/spellSummonConsumableFollower.js
  11. +30
    -0
      src/server/fixes/fixes.js
  12. +1
    -1
      src/server/items/generators/effects.js
  13. +5
    -1
      src/server/mods/class-necromancer/spells/spellBloodBarrier.js
  14. +6
    -2
      src/server/mods/class-necromancer/spells/spellHarvestLife.js

+ 38
- 1
src/server/components/inventory.js View File

@@ -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}`);


+ 43
- 40
src/server/components/stats.js View File

@@ -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
});
}
}
};

+ 5
- 1
src/server/config/effects/effectHolyVengeance.js View File

@@ -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
});
}
}
};

+ 86
- 28
src/server/config/itemEffects/castSpellOnHit.js View File

@@ -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');
}
}
};

+ 7
- 3
src/server/config/itemEffects/gainStat.js View File

@@ -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);
},


+ 6
- 2
src/server/config/itemEffects/healOnCrit.js View File

@@ -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
});
}
}
};

+ 5
- 1
src/server/config/spells/spellArcaneBarrier.js View File

@@ -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
});
}
}
};


+ 5
- 1
src/server/config/spells/spellHealingCircle.js View File

@@ -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) {


+ 7
- 3
src/server/config/spells/spellSingleTargetHeal.js View File

@@ -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,


+ 7
- 3
src/server/config/spells/spellSummonConsumableFollower.js View File

@@ -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,


+ 30
- 0
src/server/fixes/fixes.js View File

@@ -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) {


+ 1
- 1
src/server/items/generators/effects.js View File

@@ -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;


+ 5
- 1
src/server/mods/class-necromancer/spells/spellBloodBarrier.js View File

@@ -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];


+ 6
- 2
src/server/mods/class-necromancer/spells/spellHarvestLife.js View File

@@ -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
});
}
};

Loading…
Cancel
Save