Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

191 řádky
4.4 KiB

  1. //Balance
  2. const { hpMults, dmgMults } = require('../config/consts');
  3. //Imports
  4. const animations = require('../config/animations');
  5. const itemGenerator = require('../items/generator');
  6. //Mobs will be given random items to equip for these slots
  7. const generateSlots = [
  8. 'head',
  9. 'chest',
  10. 'neck',
  11. 'hands',
  12. 'waist',
  13. 'legs',
  14. 'feet',
  15. 'finger',
  16. 'trinket',
  17. 'twoHanded'
  18. ];
  19. //Mobs will pick one of these stats to be force rolles onto their items
  20. const statSelector = ['str', 'dex', 'int'];
  21. //These stat values are synced to players
  22. const syncStats = ['hp', 'hpMax', 'mana', 'manaMax', 'level'];
  23. //Component generators
  24. const buildCpnMob = (mob, blueprint, typeDefinition) => {
  25. const { walkDistance, grantRep, deathRep, patrol, needLos } = blueprint;
  26. const cpnMob = mob.addComponent('mob');
  27. extend(cpnMob, {
  28. walkDistance,
  29. grantRep,
  30. deathRep,
  31. needLos
  32. });
  33. if (patrol !== undefined)
  34. cpnMob.patrol = blueprint.patrol;
  35. if (cpnMob.patrol)
  36. cpnMob.walkDistance = 1;
  37. };
  38. const buildCpnStats = (mob, blueprint, typeDefinition) => {
  39. const {
  40. level,
  41. hpMult: baseHpMult = typeDefinition.hpMult
  42. } = blueprint;
  43. const hpMax = ~~(level * 40 * hpMults[level - 1] * baseHpMult);
  44. const cpnStats = mob.addComponent('stats', {
  45. values: {
  46. level,
  47. hpMax
  48. }
  49. });
  50. //Hack to disallow low level mobs from having any lifeOnHit
  51. // since that makes it very difficult (and confusing) for low level players
  52. if (level <= 3)
  53. cpnStats.values.lifeOnHit = 0;
  54. };
  55. const buildCpnInventory = (mob, blueprint, { drops }, preferStat) => {
  56. const { level } = blueprint;
  57. const cpnInventory = mob.addComponent('inventory', drops);
  58. cpnInventory.inventorySize = -1;
  59. cpnInventory.dailyDrops = blueprint.dailyDrops;
  60. generateSlots.forEach(slot => {
  61. const item = itemGenerator.generate({
  62. noSpell: true,
  63. level,
  64. slot,
  65. quality: 4,
  66. forceStats: [preferStat]
  67. });
  68. delete item.spell;
  69. item.eq = true;
  70. cpnInventory.getItem(item);
  71. });
  72. };
  73. const buildCpnSpells = (mob, blueprint, typeDefinition, preferStat) => {
  74. const dmgMult = 4.5 * typeDefinition.dmgMult * dmgMults[blueprint.level - 1];
  75. const spells = extend([], blueprint.spells);
  76. spells.forEach(s => {
  77. if (!s.animation && mob.sheetName === 'mobs' && animations.mobs[mob.cell])
  78. s.animation = 'basic';
  79. });
  80. mob.addComponent('spellbook', { spells });
  81. let spellCount = 0;
  82. if (mob.isRare)
  83. spellCount = 1;
  84. for (let i = 0; i < spellCount; i++) {
  85. const rune = itemGenerator.generate({ spell: true });
  86. rune.eq = true;
  87. mob.inventory.getItem(rune);
  88. }
  89. mob.spellbook.spells.forEach(s => {
  90. s.dmgMult = s.name ? dmgMult / 3 : dmgMult;
  91. s.statType = preferStat;
  92. s.manaCost = 0;
  93. });
  94. };
  95. const fnComponentGenerators = [
  96. buildCpnMob, buildCpnStats, buildCpnInventory, buildCpnSpells
  97. ];
  98. //Main Generator
  99. /*
  100. mob = the mob object
  101. blueprint = mob blueprint (normally from the zoneFile)
  102. type = regular,rare
  103. zoneName = the name of the zone
  104. */
  105. const build = (mob, blueprint, type, zoneName) => {
  106. mob.instance.eventEmitter.emit('onBeforeBuildMob', zoneName, mob.name.toLowerCase(), blueprint);
  107. const typeDefinition = blueprint[type] || blueprint;
  108. if (blueprint.nonSelectable)
  109. mob.nonSelectable = true;
  110. mob.addComponent('effects');
  111. if (type === 'rare') {
  112. mob.effects.addEffect({ type: 'rare' });
  113. mob.isRare = true;
  114. mob.baseName = mob.name;
  115. mob.name = typeDefinition.name ?? mob.name;
  116. }
  117. if (typeDefinition.sheetName)
  118. mob.sheetName = typeDefinition.sheetName;
  119. if (typeDefinition.has('cell'))
  120. mob.cell = typeDefinition.cell;
  121. mob.addComponent('equipment');
  122. const preferStat = statSelector[~~(Math.random() * 3)];
  123. fnComponentGenerators.forEach(fn => fn(mob, blueprint, typeDefinition, preferStat));
  124. if (blueprint.attackable !== false) {
  125. mob.addComponent('aggro', { faction: blueprint.faction });
  126. mob.aggro.calcThreatCeiling(type);
  127. }
  128. const zoneConfig = instancer.instances[0].map.zoneConfig;
  129. const chats = zoneConfig?.chats?.[mob.name.toLowerCase()];
  130. if (chats)
  131. mob.addComponent('chatter', { chats });
  132. const dialogues = zoneConfig?.dialogues?.[mob.name.toLowerCase()];
  133. if (dialogues)
  134. mob.addComponent('dialogue', { config: dialogues });
  135. if (blueprint?.properties?.cpnTrade)
  136. mob.addComponent('trade', blueprint.properties.cpnTrade);
  137. mob.instance.eventEmitter.emit('onAfterBuildMob', {
  138. zoneName,
  139. mob
  140. });
  141. const statValues = mob.stats.values;
  142. statValues.hp = statValues.hpMax;
  143. syncStats.forEach(s => mob.syncer.setObject(false, 'stats', 'values', s, statValues[s]));
  144. };
  145. module.exports = { build };