Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

132 linhas
3.3 KiB

  1. const getDefaultRotationSpell = rotationSpells => {
  2. const spells = rotationSpells.filter(s => !s.atRotationTicks);
  3. if (!spells.length)
  4. return;
  5. if (spells.length === 1)
  6. return spells[0];
  7. const randomSpell = spells[~~(Math.random() * spells.length)];
  8. return randomSpell;
  9. };
  10. //Mobs that define rotations (normally bosses) use this method to determine their spell choices
  11. const getRotationSpell = (source, target) => {
  12. const { spells, rotation: { currentTick, spells: rotationSpells } } = source;
  13. //Find spell matching current tick
  14. let rotationEntry = rotationSpells.find(s => s.atRotationTicks?.includes(currentTick));
  15. if (!rotationEntry)
  16. rotationEntry = getDefaultRotationSpell(rotationSpells);
  17. if (!rotationEntry)
  18. return;
  19. //Don't cast anything
  20. if (rotationEntry.spellIndex === -1)
  21. return;
  22. const useSpell = spells[rotationEntry.spellIndex];
  23. //Todo: We should set cdMax and manaCost to 0 of rotation spells (unless there's a mana drain mechanic)
  24. // later and we want to allow that on bosses
  25. useSpell.cd = 0;
  26. useSpell.manaCost = 0;
  27. if (!useSpell.selfCast && !useSpell.canCast(target))
  28. return getDefaultRotationSpell(rotationSpells);
  29. return useSpell;
  30. };
  31. //Mobs without rune rotations (normally the case) simple select any random spell that is valid
  32. const getRandomSpell = (source, target) => {
  33. const valid = source.spells.filter(s => {
  34. return (!s.selfCast && !s.procCast && !s.castOnDeath && s.canCast(target));
  35. });
  36. if (!valid.length)
  37. return null;
  38. return valid[~~(Math.random() * valid.length)];
  39. };
  40. const getSpellToCast = (source, target) => {
  41. if (source.rotation)
  42. return getRotationSpell(source, target);
  43. const { obj: { follower } } = source;
  44. //Mobs don't cast all the time but player followers do
  45. if (!follower?.master?.player && Math.random() >= 0.65)
  46. return;
  47. return getRandomSpell(source, target);
  48. };
  49. const tick = source => {
  50. if (!source.obj.aggro.isInCombat())
  51. return;
  52. const { rotation } = source;
  53. rotation.currentTick++;
  54. if (rotation.currentTick === rotation.duration)
  55. rotation.currentTick = 1;
  56. };
  57. //Gets the range we need to be at to cast a specific rotation spell
  58. const getFurthestRangeRotation = (source, target, checkCanCast) => {
  59. const spell = getRotationSpell(source, target);
  60. if (!spell)
  61. return 0;
  62. return spell.range;
  63. };
  64. /*
  65. This is used by mobs when in combat mode,
  66. * When checkCanCast is true, we want to see if we can cast right now
  67. * When checkCanCast is false, we want to see if there is a spell we could cast in the near future
  68. -> This could be a spell that is currently on cooldown, or that the mob has insufficient mana for
  69. ---> Even though mobs don't need mana for spells at the moment
  70. -> Ultimately, this means the mob should not move, just wait
  71. */
  72. const getFurthestRange = (source, target, checkCanCast) => {
  73. const { spells, rotation } = source;
  74. if (rotation)
  75. return getFurthestRangeRotation(source, target, checkCanCast);
  76. let sLen = spells.length;
  77. let furthest = 0;
  78. for (let i = 0; i < sLen; i++) {
  79. let spell = spells[i];
  80. if (spell.procCast || spell.castOnDeath)
  81. continue;
  82. if (spell.range > furthest && (!checkCanCast || spell.canCast()))
  83. furthest = spell.range;
  84. }
  85. return furthest;
  86. };
  87. const resetRotation = source => {
  88. if (!source.rotation)
  89. return;
  90. source.rotation.currentTick = 0;
  91. };
  92. module.exports = {
  93. tick,
  94. resetRotation,
  95. getSpellToCast,
  96. getFurthestRange
  97. };