25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

194 lines
4.7 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. cpnMob.needLos = blueprint.needLos;
  41. let spells = extend([], blueprint.spells);
  42. spells.forEach(s => {
  43. if (!s.animation && mob.sheetName === 'mobs' && animations.mobs[mob.cell])
  44. s.animation = 'basic';
  45. });
  46. mob.addComponent('spellbook', {
  47. spells: spells,
  48. dmgMult: typeDefinition.dmgMult
  49. });
  50. if (!blueprint.has('attackable') || blueprint.attackable === true) {
  51. mob.addComponent('aggro', {
  52. faction: blueprint.faction
  53. });
  54. mob.aggro.calcThreatCeiling(type);
  55. }
  56. mob.addComponent('equipment');
  57. mob.addComponent('inventory', typeDefinition.drops);
  58. mob.inventory.inventorySize = -1;
  59. mob.inventory.dailyDrops = blueprint.dailyDrops;
  60. if (this.zoneConfig) {
  61. let chats = this.zoneConfig.chats;
  62. if (chats && chats[mob.name.toLowerCase()]) {
  63. mob.addComponent('chatter', {
  64. chats: chats[mob.name.toLowerCase()]
  65. });
  66. }
  67. let dialogues = this.zoneConfig.dialogues;
  68. if (dialogues && dialogues[mob.name.toLowerCase()]) {
  69. mob.addComponent('dialogue', {
  70. config: dialogues[mob.name.toLowerCase()]
  71. });
  72. }
  73. }
  74. if (blueprint.properties && blueprint.properties.cpnTrade)
  75. mob.addComponent('trade', blueprint.properties.cpnTrade);
  76. this.scale(mob, blueprint.level);
  77. mob.instance.eventEmitter.emit('onAfterBuildMob', {
  78. zoneName,
  79. mob
  80. });
  81. },
  82. scale: function (mob, level) {
  83. let drops = mob.inventory.blueprint || {};
  84. let statValues = mob.stats.values;
  85. let preferStat = ['str', 'dex', 'int'][~~(Math.random() * 3)];
  86. mob.equipment.unequipAll();
  87. mob.inventory.clear();
  88. let hp = level * 40;
  89. statValues.hpMax = hp;
  90. statValues.level = level;
  91. if ((!drops.blueprints) || (drops.alsoRandom)) {
  92. [
  93. 'head',
  94. 'chest',
  95. 'neck',
  96. 'hands',
  97. 'waist',
  98. 'legs',
  99. 'feet',
  100. 'finger',
  101. 'trinket',
  102. 'twoHanded'
  103. ].forEach(slot => {
  104. let item = itemGenerator.generate({
  105. noSpell: true,
  106. level: level,
  107. slot: slot,
  108. quality: 4,
  109. forceStats: [preferStat]
  110. });
  111. delete item.spell;
  112. mob.inventory.getItem(item);
  113. mob.equipment.autoEquip(item.id);
  114. });
  115. } else {
  116. //TODO: Don't give the mob these items: he'll drop them anyway
  117. drops.blueprints.forEach(d => {
  118. if (d.type === 'key')
  119. return;
  120. let drop = extend({}, d);
  121. drop.level = level;
  122. mob.inventory.getItem(itemGenerator.generate(drop));
  123. });
  124. }
  125. let spellCount = (mob.isRare ? 1 : 0) + (mob.isChampion ? 2 : 0);
  126. for (let i = 0; i < spellCount; i++) {
  127. let rune = itemGenerator.generate({
  128. spell: true
  129. });
  130. rune.eq = true;
  131. mob.inventory.getItem(rune);
  132. }
  133. let dmgMult = 4.5 * mob.mob.dmgMult;
  134. let hpMult = 1 * mob.mob.hpMult;
  135. 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];
  136. 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]);
  137. statValues.hpMax *= hpMult;
  138. statValues.hp = statValues.hpMax;
  139. statValues.mana = statValues.manaMax;
  140. mob.spellbook.spells.forEach(s => {
  141. s.dmgMult = s.name ? dmgMult / 3 : dmgMult;
  142. s.statType = preferStat;
  143. s.manaCost = 0;
  144. });
  145. //Hack to disallow low level mobs from having any lifeOnHit
  146. // since that makes it very difficult (and confusing) for low level players
  147. if (level <= 3)
  148. mob.stats.values.lifeOnHit = 0;
  149. ['hp', 'hpMax', 'mana', 'manaMax', 'level'].forEach(s => mob.syncer.setObject(false, 'stats', 'values', s, statValues[s]));
  150. }
  151. };