diff --git a/src/server/components/spellbook.js b/src/server/components/spellbook.js index a7602b3d..e746211b 100644 --- a/src/server/components/spellbook.js +++ b/src/server/components/spellbook.js @@ -1,13 +1,14 @@ -let spellTemplate = require('../config/spells/spellTemplate'); -let animations = require('../config/animations'); -let playerSpells = require('../config/spells'); -let playerSpellsConfig = require('../config/spellsConfig'); +//Imports +const spellTemplate = require('../config/spells/spellTemplate'); +const animations = require('../config/animations'); +const playerSpells = require('../config/spells'); +const playerSpellsConfig = require('../config/spellsConfig'); //Helpers const rotationManager = require('./spellbook/rotationManager'); +const cast = require('./spellbook/cast'); //Component - module.exports = { type: 'spellbook', @@ -338,119 +339,7 @@ module.exports = { }, cast: function (action, isAuto) { - if (!action.has('spell')) { - const isCasting = this.isCasting(); - this.stopCasting(); - - const consumeTick = isCasting; - - return consumeTick; - } - - let spell = this.spells.find(s => (s.id === action.spell)); - if (!spell) - return false; - - action.target = this.getTarget(spell, action); - - //If a target has become nonSelectable, we need to stop attacks that are queued/auto - if (!action.target || action.target.nonSelectable) - return false; - - action.auto = spell.auto; - - let success = true; - if (spell.cd > 0) { - if (!isAuto) { - let type = (spell.auto) ? 'Weapon' : 'Spell'; - this.sendAnnouncement(`${type} is on cooldown`); - } - success = false; - } else if (spell.manaCost > this.obj.stats.values.mana) { - if (!isAuto) - this.sendAnnouncement('Insufficient mana to cast spell'); - success = false; - } 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; - if ((spell.useWeaponRange) && (this.obj.player)) { - let weapon = this.obj.inventory.findItem(this.obj.equipment.eq.oneHanded) || this.obj.inventory.findItem(this.obj.equipment.eq.twoHanded); - if (weapon) - range = weapon.range || 1; - } - - if (distance > range) { - if (!isAuto) - this.sendAnnouncement('Target out of range'); - success = false; - } - } - - //LoS check - //Null means we don't have LoS and as such, we should move - if (spell.needLos && success) { - if (!this.physics.hasLos(~~this.obj.x, ~~this.obj.y, ~~action.target.x, ~~action.target.y)) { - if (!isAuto) - this.sendAnnouncement('Target not in line of sight'); - action.auto = false; - success = null; - } - } - - if (!success) { - this.queueAuto(action, spell); - return success; - } else if (!this.queueAuto(action, spell)) - return false; - - let castSuccess = { - success: true - }; - this.obj.fireEvent('beforeCastSpell', castSuccess); - 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'); - return; - } 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) - spell.target = this.obj.aggro.getRandom(); - - success = spell.castBase(action); - this.stopCasting(spell, true); - - if (success) { - spell.consumeMana(); - spell.setCd(); - } - - this.obj.fireEvent('afterCastSpell', { - castSuccess: success, - spell, - action - }); - - //Null means we didn't fail but are initiating casting - return (success === null || success === true); + return cast(this, action, isAuto); }, getClosestRange: function (spellNum) { diff --git a/src/server/components/spellbook/cast.js b/src/server/components/spellbook/cast.js new file mode 100644 index 00000000..287cc9fa --- /dev/null +++ b/src/server/components/spellbook/cast.js @@ -0,0 +1,131 @@ +/* eslint-disable-next-line max-lines-per-function */ +const cast = (cpnSpellbook, action, isAuto) => { + const { obj, physics, spells } = cpnSpellbook; + + //Stop casting + if (!action.has('spell')) { + const wasCasting = cpnSpellbook.isCasting(); + cpnSpellbook.stopCasting(); + + //Consume a tick if we were casting + return wasCasting; + } + + const spell = spells.find(s => (s.id === action.spell)); + if (!spell) + return false; + + action.target = cpnSpellbook.getTarget(spell, action); + action.auto = spell.auto; + + //If a target has become nonSelectable, we need to stop attacks that are queued/auto + if (!action.target || action.target.nonSelectable) + return false; + + let success = true; + if (spell.cd > 0) { + if (!isAuto) { + const type = (spell.auto) ? 'Weapon' : 'Spell'; + cpnSpellbook.sendAnnouncement(`${type} is on cooldown`); + } + success = false; + } else if (spell.manaCost > obj.stats.values.mana) { + if (!isAuto) + cpnSpellbook.sendAnnouncement('Insufficient mana to cast spell'); + success = false; + } else if (spell.has('range')) { + const distance = Math.max(Math.abs(action.target.x - obj.x), Math.abs(action.target.y - obj.y)); + let range = spell.range; + if ((spell.useWeaponRange) && (obj.player)) { + const weapon = obj.inventory.findItem(obj.equipment.eq.oneHanded) || obj.inventory.findItem(obj.equipment.eq.twoHanded); + if (weapon) + range = weapon.range || 1; + } + + if (distance > range) { + if (!isAuto) + cpnSpellbook.sendAnnouncement('Target out of range'); + success = false; + } + } + + //LoS check + //Null means we don't have LoS and as such, we should move + if (spell.needLos && success) { + if (!physics.hasLos(~~obj.x, ~~obj.y, ~~action.target.x, ~~action.target.y)) { + if (!isAuto) + cpnSpellbook.sendAnnouncement('Target not in line of sight'); + action.auto = false; + success = null; + } + } + + if (!success) { + cpnSpellbook.queueAuto(action, spell); + return success; + } else if (!cpnSpellbook.queueAuto(action, spell)) + return false; + + const eventBeforeCastSpell = { + success: true, + action + }; + obj.fireEvent('beforeCastSpell', eventBeforeCastSpell); + if (!eventBeforeCastSpell.success) + return false; + + if (spell.manaReserve) { + const reserve = spell.manaReserve; + + if (reserve.percentage) { + const reserveEvent = { + spell: spell.name, + reservePercent: reserve.percentage + }; + obj.fireEvent('onBeforeReserveMana', reserveEvent); + + if (!spell.active) { + if (1 - obj.stats.values.manaReservePercent < reserve.percentage) { + cpnSpellbook.sendAnnouncement('Insufficient mana to cast spell'); + return; + } obj.stats.addStat('manaReservePercent', reserveEvent.reservePercent); + } else + obj.stats.addStat('manaReservePercent', -reserveEvent.reservePercent); + } + } + + if (spell.targetFurthest) + spell.target = obj.aggro.getFurthest(); + else if (spell.targetRandom) + spell.target = obj.aggro.getRandom(); + + if (!!eventBeforeCastSpell.action.target?.effects) { + const eventBeforeIsSpellTarget = { + source: obj, + spell, + target: eventBeforeCastSpell.action.target + }; + eventBeforeIsSpellTarget.target.fireEvent('beforeIsSpellTarget', eventBeforeIsSpellTarget); + eventBeforeCastSpell.action.target = eventBeforeIsSpellTarget.target; + } + + success = spell.castBase(eventBeforeCastSpell.action); + cpnSpellbook.stopCasting(spell, true); + + if (success) { + spell.consumeMana(); + spell.setCd(); + } + + obj.fireEvent('afterCastSpell', { + castSuccess: success, + spell, + action: eventBeforeCastSpell.action + }); + + //Null means we didn't fail but are initiating casting + return (success === null || success === true); +}; + +module.exports = cast; +