Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 

207 rindas
5.1 KiB

  1. let animations = require('../config/animations');
  2. let itemGenerator = require('../items/generator');
  3. //const track = {};
  4. module.exports = {
  5. build: function (mob, blueprint, type, zoneName) {
  6. mob.instance.eventEmitter.emit('onBeforeBuildMob', zoneName, mob.name.toLowerCase(), blueprint);
  7. let typeDefinition = blueprint[type] || blueprint;
  8. if (blueprint.nonSelectable)
  9. mob.nonSelectable = true;
  10. mob.addComponent('effects');
  11. if (type && type !== 'regular') {
  12. mob.effects.addEffect({
  13. type: type
  14. });
  15. mob['is' + type[0].toUpperCase() + type.substr(1)] = true;
  16. mob.baseName = mob.name;
  17. mob.name = typeDefinition.name || mob.baseName;
  18. }
  19. if (typeDefinition.sheetName)
  20. mob.sheetName = typeDefinition.sheetName;
  21. if (typeDefinition.has('cell'))
  22. mob.cell = typeDefinition.cell;
  23. mob.addComponent('stats', {
  24. values: {
  25. level: blueprint.level
  26. }
  27. });
  28. let cpnMob = mob.addComponent('mob');
  29. extend(cpnMob, {
  30. walkDistance: blueprint.walkDistance,
  31. hpMult: blueprint.hpMult || typeDefinition.hpMult,
  32. dmgMult: blueprint.dmgMult || typeDefinition.dmgMult,
  33. grantRep: blueprint.grantRep,
  34. deathRep: blueprint.deathRep
  35. });
  36. if (blueprint.patrol)
  37. cpnMob.patrol = blueprint.patrol;
  38. if (cpnMob.patrol)
  39. cpnMob.walkDistance = 1;
  40. let spells = extend([], blueprint.spells);
  41. spells.forEach(s => {
  42. if (!s.animation && mob.sheetName === 'mobs' && animations.mobs[mob.cell])
  43. s.animation = 'basic';
  44. });
  45. mob.addComponent('spellbook', {
  46. spells: spells,
  47. dmgMult: typeDefinition.dmgMult
  48. });
  49. if (!blueprint.has('attackable') || blueprint.attackable === true) {
  50. mob.addComponent('aggro', {
  51. faction: blueprint.faction
  52. });
  53. mob.aggro.calcThreatCeiling(type);
  54. }
  55. mob.addComponent('equipment');
  56. mob.addComponent('inventory', typeDefinition.drops);
  57. mob.inventory.inventorySize = -1;
  58. mob.inventory.dailyDrops = blueprint.dailyDrops;
  59. if (this.zone) {
  60. let chats = this.zone.chats;
  61. if (chats && chats[mob.name.toLowerCase()]) {
  62. mob.addComponent('chatter', {
  63. chats: chats[mob.name.toLowerCase()]
  64. });
  65. }
  66. let dialogues = this.zone.dialogues;
  67. if (dialogues && dialogues[mob.name.toLowerCase()]) {
  68. mob.addComponent('dialogue', {
  69. config: dialogues[mob.name.toLowerCase()]
  70. });
  71. }
  72. }
  73. if (blueprint.properties && blueprint.properties.cpnTrade)
  74. mob.addComponent('trade', blueprint.properties.cpnTrade);
  75. this.scale(mob, blueprint.level);
  76. mob.instance.eventEmitter.emit('onAfterBuildMob', {
  77. zoneName,
  78. mob
  79. });
  80. },
  81. scale: function (mob, level) {
  82. let drops = mob.inventory.blueprint || {};
  83. let statValues = mob.stats.values;
  84. let preferStat = ['str', 'dex', 'int'][~~(Math.random() * 3)];
  85. mob.equipment.unequipAll();
  86. mob.inventory.clear();
  87. let hp = level * 40;
  88. statValues.hpMax = hp;
  89. statValues.level = level;
  90. if ((!drops.blueprints) || (drops.alsoRandom)) {
  91. [
  92. 'head',
  93. 'chest',
  94. 'neck',
  95. 'hands',
  96. 'waist',
  97. 'legs',
  98. 'feet',
  99. 'finger',
  100. 'trinket',
  101. 'twoHanded'
  102. ].forEach(slot => {
  103. let item = itemGenerator.generate({
  104. noSpell: true,
  105. level: level,
  106. slot: slot,
  107. quality: 4,
  108. forceStats: [preferStat]
  109. });
  110. delete item.spell;
  111. mob.inventory.getItem(item);
  112. mob.equipment.autoEquip(item.id);
  113. });
  114. } else {
  115. //TODO: Don't give the mob these items: he'll drop them anyway
  116. drops.blueprints.forEach(d => {
  117. if (d.type === 'key')
  118. return;
  119. let drop = extend({}, d);
  120. drop.level = level;
  121. mob.inventory.getItem(itemGenerator.generate(drop));
  122. });
  123. }
  124. let spellCount = (mob.isRare ? 1 : 0) + (mob.isChampion ? 2 : 0);
  125. for (let i = 0; i < spellCount; i++) {
  126. let rune = itemGenerator.generate({
  127. spell: true
  128. });
  129. rune.eq = true;
  130. mob.inventory.getItem(rune);
  131. }
  132. let dmgMult = 4.5 * mob.mob.dmgMult;
  133. let hpMult = 1 * mob.mob.hpMult;
  134. dmgMult *= [0.25, 0.4, 0.575, 0.8, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.1, 2.2, 2.3, 2.4, 2.5][level - 1];
  135. statValues.hpMax = ~~(statValues.hpMax * [0.1, 0.2, 0.4, 0.7, 0.78, 0.91, 1.16, 1.19, 1.65, 2.36, 3.07, 3.55, 4.1, 4.85, 5.6, 5.9, 6.5, 7.1, 7.9, 12][level - 1]);
  136. statValues.hpMax *= hpMult;
  137. statValues.hp = statValues.hpMax;
  138. statValues.mana = statValues.manaMax;
  139. mob.spellbook.spells.forEach(s => {
  140. s.dmgMult = s.name ? dmgMult / 3 : dmgMult;
  141. s.statType = preferStat;
  142. s.manaCost = 0;
  143. /*if (mob.name.toLowerCase().includes('stinktooth')) {
  144. mob.stats.values.critChance = 0;
  145. mob.stats.values.attackCritChance = 0;
  146. mob.stats.values.spellCritChance = 0;
  147. const n = mob.name + '-' + s.type;
  148. if (!track[n])
  149. track[n] = [];
  150. track[n].push(~~s.getDamage(mob, true).amount);
  151. track[n].sort((a, b) => a - b);
  152. console.log(track);
  153. console.log('');
  154. }*/
  155. });
  156. //Hack to disallow low level mobs from having any lifeOnHit
  157. // since that makes it very difficult (and confusing) for low level players
  158. if (level <= 3)
  159. mob.stats.values.lifeOnHit = 0;
  160. ['hp', 'hpMax', 'mana', 'manaMax', 'level'].forEach(s => mob.syncer.setObject(false, 'stats', 'values', s, statValues[s]));
  161. }
  162. };