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