You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1048 lines
23 KiB

  1. let generator = require('../items/generator');
  2. let salvager = require('../items/salvager');
  3. let enchanter = require('../items/enchanter');
  4. let classes = require('../config/spirits');
  5. let mtx = require('../mtx/mtx');
  6. let factions = require('../config/factions');
  7. let itemEffects = require('../items/itemEffects');
  8. module.exports = {
  9. type: 'inventory',
  10. inventorySize: 50,
  11. items: [],
  12. blueprint: null,
  13. init: function (blueprint, isTransfer) {
  14. let items = blueprint.items || [];
  15. let iLen = items.length;
  16. //Spells should be sorted so they're EQ'd in the right order
  17. items.sort(function (a, b) {
  18. let aId = a.has('spellId') ? ~~a.spellId : 9999;
  19. let bId = b.has('spellId') ? ~~b.spellId : 9999;
  20. return (aId - bId);
  21. });
  22. for (let i = 0; i < iLen; i++) {
  23. let item = items[i];
  24. if ((item.pos >= this.inventorySize) || (item.eq))
  25. delete item.pos;
  26. while (item.name.indexOf('\'\'') > -1)
  27. item.name = item.name.replace('\'\'', '\'');
  28. }
  29. this.hookItemEvents(items);
  30. //Hack to skip attr checks on equip
  31. let oldFn = this.canEquipItem;
  32. this.canEquipItem = () => {
  33. return true;
  34. };
  35. for (let i = 0; i < iLen; i++) {
  36. let item = items[i];
  37. let pos = item.pos;
  38. let newItem = this.getItem(item, true, true);
  39. newItem.pos = pos;
  40. }
  41. //Hack to skip attr checks on equip
  42. this.canEquipItem = oldFn.bind(this);
  43. if ((this.obj.player) && (!isTransfer) && (this.obj.stats.values.level === 1))
  44. this.getDefaultAbilities();
  45. delete blueprint.items;
  46. this.blueprint = blueprint;
  47. if (this.obj.equipment)
  48. this.obj.equipment.unequipAttrRqrGear();
  49. },
  50. transfer: function () {
  51. this.hookItemEvents();
  52. },
  53. save: function () {
  54. return {
  55. type: 'inventory',
  56. items: this.items
  57. };
  58. },
  59. simplify: function (self) {
  60. if (!self)
  61. return null;
  62. let reputation = this.obj.reputation;
  63. return {
  64. type: 'inventory',
  65. items: this.items
  66. .map(function (i) {
  67. let item = extend({}, i);
  68. if (item.effects) {
  69. item.effects = item.effects.map(e => ({
  70. factionId: e.factionId,
  71. text: e.text,
  72. properties: e.properties,
  73. mtx: e.mtx,
  74. type: e.type,
  75. rolls: e.rolls
  76. }));
  77. }
  78. if (item.factions) {
  79. item.factions = item.factions.map(function (f) {
  80. let faction = reputation.getBlueprint(f.id);
  81. let factionTier = reputation.getTier(f.id);
  82. let noEquip = null;
  83. if (factionTier < f.tier)
  84. noEquip = true;
  85. return {
  86. id: f.id,
  87. name: faction.name,
  88. tier: f.tier,
  89. tierName: ['Hated', 'Hostile', 'Unfriendly', 'Neutral', 'Friendly', 'Honored', 'Revered', 'Exalted'][f.tier],
  90. noEquip: noEquip
  91. };
  92. }, this);
  93. }
  94. return item;
  95. })
  96. };
  97. },
  98. update: function () {
  99. let items = this.items;
  100. let iLen = items.length;
  101. for (let i = 0; i < iLen; i++) {
  102. let item = items[i];
  103. if (!item.cd)
  104. continue;
  105. item.cd--;
  106. this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
  107. }
  108. },
  109. enchantItem: function (msg) {
  110. let item = this.findItem(msg.itemId);
  111. if ((!item) || (!item.slot) || (item.eq) || (item.noAugment) || ((msg.action === 'scour') && (item.power === 0))) {
  112. this.resolveCallback(msg);
  113. return;
  114. }
  115. enchanter.enchant(this.obj, item, msg);
  116. },
  117. getEnchantMaterials: function (msg) {
  118. let result = [];
  119. let item = this.findItem(msg.itemId);
  120. if ((item) && (item.slot))
  121. result = enchanter.getEnchantMaterials(item, msg.action);
  122. this.resolveCallback(msg, result);
  123. },
  124. learnAbility: function (itemId, runeSlot) {
  125. if (itemId.has('itemId')) {
  126. let msg = itemId;
  127. itemId = msg.itemId;
  128. runeSlot = msg.slot;
  129. }
  130. let item = this.findItem(itemId);
  131. let statValues = this.obj.stats.values;
  132. if (!item)
  133. return;
  134. else if (!item.spell) {
  135. item.eq = false;
  136. return;
  137. } else if (item.level > statValues.level) {
  138. item.eq = false;
  139. return;
  140. }
  141. let learnMsg = {
  142. success: true,
  143. item: item
  144. };
  145. this.obj.fireEvent('beforeLearnAbility', learnMsg);
  146. if (!learnMsg.success) {
  147. this.obj.instance.syncer.queue('onGetMessages', {
  148. id: this.obj.id,
  149. messages: [{
  150. class: 'color-redA',
  151. message: learnMsg.msg || 'you cannot learn that ability',
  152. type: 'info'
  153. }]
  154. }, [this.obj.serverId]);
  155. return;
  156. }
  157. let spellbook = this.obj.spellbook;
  158. if ((item.slot === 'twoHanded') || (item.slot === 'oneHanded'))
  159. runeSlot = 0;
  160. else if (!runeSlot) {
  161. runeSlot = 4;
  162. for (let i = 1; i <= 4; i++) {
  163. if (!this.items.some(j => (j.runeSlot === i))) {
  164. runeSlot = i;
  165. break;
  166. }
  167. }
  168. }
  169. let currentEq = this.items.find(i => (i.runeSlot === runeSlot));
  170. if (currentEq) {
  171. spellbook.removeSpellById(runeSlot);
  172. delete currentEq.eq;
  173. delete currentEq.runeSlot;
  174. this.setItemPosition(currentEq.id);
  175. this.obj.syncer.setArray(true, 'inventory', 'getItems', currentEq);
  176. }
  177. item.eq = true;
  178. item.runeSlot = runeSlot;
  179. delete item.pos;
  180. spellbook.addSpellFromRune(item.spell, runeSlot);
  181. this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
  182. },
  183. activateMtx: function (itemId) {
  184. let item = this.findItem(itemId);
  185. if (!item)
  186. return;
  187. else if (item.type !== 'mtx') {
  188. delete item.active;
  189. return;
  190. }
  191. item.active = !item.active;
  192. this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
  193. },
  194. splitStack: function (msg) {
  195. let item = this.findItem(msg.itemId);
  196. if (!item)
  197. return;
  198. else if ((!item.quantity) || (item.quantity <= msg.stackSize) || (msg.stackSize < 1))
  199. return;
  200. let newItem = extend({}, item);
  201. item.quantity -= msg.stackSize;
  202. newItem.quantity = msg.stackSize;
  203. this.getItem(newItem, true, true);
  204. this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
  205. },
  206. combineStacks: function (msg) {
  207. let fromItem = this.findItem(msg.fromId);
  208. let toItem = this.findItem(msg.toId);
  209. if ((!fromItem) || (!toItem))
  210. return;
  211. else if ((!fromItem.quantity) || (!toItem.quantity))
  212. return;
  213. toItem.quantity += fromItem.quantity;
  214. this.obj.syncer.setArray(true, 'inventory', 'getItems', toItem);
  215. this.destroyItem(fromItem.id, null, true);
  216. },
  217. useItem: function (itemId) {
  218. let item = this.findItem(itemId);
  219. if (!item)
  220. return;
  221. let obj = this.obj;
  222. if (item.cdMax) {
  223. if (item.cd) {
  224. process.send({
  225. method: 'events',
  226. data: {
  227. onGetAnnouncement: [{
  228. obj: {
  229. msg: 'That item is on cooldown'
  230. },
  231. to: [obj.serverId]
  232. }]
  233. }
  234. });
  235. return;
  236. }
  237. item.cd = item.cdMax;
  238. //Find similar items and put them on cooldown too
  239. this.items.forEach(function (i) {
  240. if ((i.name === item.name) && (i.cdMax === item.cdMax))
  241. i.cd = i.cdMax;
  242. });
  243. }
  244. let result = {};
  245. obj.instance.eventEmitter.emit('onBeforeUseItem', obj, item, result);
  246. let effects = (item.effects || []);
  247. let eLen = effects.length;
  248. for (let j = 0; j < eLen; j++) {
  249. let effect = effects[j];
  250. if (!effect.events)
  251. continue;
  252. let effectEvent = effect.events.onConsumeItem;
  253. if (!effectEvent)
  254. continue;
  255. let effectResult = {
  256. success: true,
  257. errorMessage: null
  258. };
  259. effectEvent.call(obj, effectResult, item, effect);
  260. if (!effectResult.success) {
  261. obj.instance.syncer.queue('onGetMessages', {
  262. id: obj.id,
  263. messages: [{
  264. class: 'color-redA',
  265. message: effectResult.errorMessage,
  266. type: 'info'
  267. }]
  268. }, [obj.serverId]);
  269. return;
  270. }
  271. }
  272. if (item.type === 'consumable') {
  273. if (item.uses) {
  274. item.uses--;
  275. if (item.uses) {
  276. obj.syncer.setArray(true, 'inventory', 'getItems', item);
  277. return;
  278. }
  279. }
  280. this.destroyItem(itemId, 1);
  281. if (item.has('quickSlot'))
  282. this.obj.equipment.replaceQuickSlot(item);
  283. }
  284. },
  285. unlearnAbility: function (itemId) {
  286. if (itemId.has('itemId'))
  287. itemId = itemId.itemId;
  288. let item = this.findItem(itemId);
  289. if (!item)
  290. return;
  291. else if (!item.spell) {
  292. item.eq = false;
  293. return;
  294. }
  295. let spellbook = this.obj.spellbook;
  296. spellbook.removeSpellById(item.runeSlot);
  297. delete item.eq;
  298. delete item.runeSlot;
  299. if (!item.slot)
  300. this.setItemPosition(itemId);
  301. this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
  302. },
  303. stashItem: function (id) {
  304. let item = this.findItem(id);
  305. if ((!item) || (item.quest) || (item.noStash))
  306. return;
  307. delete item.pos;
  308. let stash = this.obj.stash;
  309. if (!stash.active)
  310. return;
  311. let clonedItem = extend({}, item);
  312. this.destroyItem(id, null, true);
  313. stash.deposit(clonedItem);
  314. },
  315. salvageItem: function (id) {
  316. let item = this.findItem(id);
  317. if ((!item) || (item.material) || (item.quest) || (item.noSalvage) || (item.eq))
  318. return;
  319. let messages = [];
  320. let items = salvager.salvage(item);
  321. let iLen = items.length;
  322. if (!iLen)
  323. return;
  324. for (let i = 0; i < iLen; i++) {
  325. let material = items[i];
  326. this.getItem(material, true);
  327. messages.push({
  328. class: 'q' + material.quality,
  329. message: 'salvage (' + material.name + ' x' + material.quantity + ')'
  330. });
  331. }
  332. this.obj.instance.syncer.queue('onGetMessages', {
  333. id: this.obj.id,
  334. messages: messages
  335. }, [this.obj.serverId]);
  336. this.destroyItem(id);
  337. },
  338. destroyItem: function (id, amount, force) {
  339. let item = this.findItem(id);
  340. if ((!item) || ((item.noDestroy) && (!force)))
  341. return;
  342. amount = amount || item.quantity;
  343. if (item.eq)
  344. this.obj.equipment.unequip(id);
  345. if ((item.quantity) && (amount)) {
  346. item.quantity -= amount;
  347. if (item.quantity <= 0) {
  348. this.items.spliceWhere(i => i.id === id);
  349. this.obj.syncer.setArray(true, 'inventory', 'destroyItems', id);
  350. } else
  351. this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
  352. } else {
  353. this.items.spliceWhere(i => i.id === id);
  354. this.obj.syncer.setArray(true, 'inventory', 'destroyItems', id);
  355. }
  356. this.obj.fireEvent('afterDestroyItem', item, amount);
  357. return item;
  358. },
  359. dropItem: function (id) {
  360. let item = this.findItem(id);
  361. if ((!item) || (item.noDrop) || (item.quest))
  362. return;
  363. if (item.has('quickSlot')) {
  364. this.obj.equipment.setQuickSlot({
  365. itemId: null,
  366. slot: item.quickSlot
  367. });
  368. delete item.quickSlot;
  369. }
  370. delete item.pos;
  371. //Find close open position
  372. let x = this.obj.x;
  373. let y = this.obj.y;
  374. let dropCell = this.obj.instance.physics.getOpenCellInArea(x - 1, y - 1, x + 1, y + 1);
  375. if (!dropCell)
  376. return;
  377. if (item.eq)
  378. this.obj.equipment.unequip(id);
  379. this.items.spliceWhere(i => i.id === id);
  380. this.obj.syncer.setArray(true, 'inventory', 'destroyItems', id);
  381. this.createBag(dropCell.x, dropCell.y, [item]);
  382. },
  383. moveItem: function (msgs) {
  384. msgs.forEach(function (m) {
  385. let item = this.findItem(m.id);
  386. if (!item)
  387. return;
  388. item.pos = m.pos;
  389. }, this);
  390. },
  391. mailItem: function (msg) {
  392. let item = this.findItem(msg.itemId);
  393. if ((!item) || (item.noDrop) || (item.quest)) {
  394. this.resolveCallback(msg);
  395. return;
  396. }
  397. delete item.pos;
  398. io.get({
  399. ent: msg.recipient,
  400. field: 'character',
  401. callback: this.onCheckCharExists.bind(this, msg, item)
  402. });
  403. },
  404. onCheckCharExists: function (msg, item, res) {
  405. if (!res) {
  406. this.resolveCallback(msg, 'Recipient does not exist');
  407. return;
  408. }
  409. this.obj.instance.mail.sendMail(msg.recipient, [extend({}, item)]);
  410. this.destroyItem(item.id);
  411. this.resolveCallback(msg);
  412. },
  413. hookItemEvents: function (items) {
  414. items = items || this.items;
  415. let iLen = items.length;
  416. for (let i = 0; i < iLen; i++) {
  417. let item = items[i];
  418. if (item.effects) {
  419. item.effects.forEach(function (e) {
  420. if (e.mtx) {
  421. let mtxUrl = mtx.get(e.mtx);
  422. let mtxModule = require('../' + mtxUrl);
  423. e.events = mtxModule.events;
  424. } else if (e.factionId) {
  425. let faction = factions.getFaction(e.factionId);
  426. let statGenerator = faction.uniqueStat;
  427. statGenerator.generate(item);
  428. } else {
  429. let effectUrl = itemEffects.get(e.type);
  430. try {
  431. let effectModule = require('../' + effectUrl);
  432. e.events = effectModule.events;
  433. } catch (error) {}
  434. }
  435. });
  436. }
  437. if (!item.has('pos') && !item.eq) {
  438. let pos = i;
  439. for (let j = 0; j < iLen; j++) {
  440. if (!items.some(fj => (fj.pos === j))) {
  441. pos = j;
  442. break;
  443. }
  444. }
  445. item.pos = pos;
  446. } else if ((!item.eq) && (items.some(ii => ((ii !== item) && (ii.pos === item.pos))))) {
  447. let pos = item.pos;
  448. for (let j = 0; j < iLen; j++) {
  449. if (!items.some(fi => ((fi !== item) && (fi.pos === j)))) {
  450. pos = j;
  451. break;
  452. }
  453. }
  454. item.pos = pos;
  455. }
  456. }
  457. },
  458. setItemPosition: function (id) {
  459. let item = this.findItem(id);
  460. if (!item)
  461. return;
  462. let iSize = this.inventorySize;
  463. for (let i = 0; i < iSize; i++) {
  464. if (!this.items.some(j => (j.pos === i))) {
  465. item.pos = i;
  466. break;
  467. }
  468. }
  469. },
  470. resolveCallback: function (msg, result) {
  471. let callbackId = msg.has('callbackId') ? msg.callbackId : msg;
  472. result = result || [];
  473. if (!callbackId)
  474. return;
  475. process.send({
  476. module: 'atlas',
  477. method: 'resolveCallback',
  478. msg: {
  479. id: callbackId,
  480. result: result
  481. }
  482. });
  483. },
  484. findItem: function (id) {
  485. if (id === null)
  486. return null;
  487. return this.items.find(i => i.id === id);
  488. },
  489. getDefaultAbilities: function () {
  490. let hasWeapon = this.items.some(function (i) {
  491. return (
  492. (i.spell) &&
  493. (i.spell.rolls) &&
  494. (i.spell.rolls.has('damage')) &&
  495. ((i.slot === 'twoHanded') || (i.slot === 'oneHanded'))
  496. );
  497. });
  498. if (!hasWeapon) {
  499. let item = generator.generate({
  500. type: classes.weapons[this.obj.class],
  501. quality: 0,
  502. spellQuality: 'basic'
  503. });
  504. item.eq = true;
  505. item.noSalvage = true;
  506. this.getItem(item);
  507. }
  508. classes.spells[this.obj.class].forEach(function (spellName) {
  509. let hasSpell = this.items.some(function (i) {
  510. return (
  511. (i.spell) &&
  512. (i.spell.name.toLowerCase() === spellName)
  513. );
  514. });
  515. if (!hasSpell) {
  516. let item = generator.generate({
  517. spell: true,
  518. spellQuality: 'basic',
  519. spellName: spellName
  520. });
  521. item.eq = true;
  522. item.noSalvage = true;
  523. this.getItem(item);
  524. }
  525. }, this);
  526. },
  527. createBag: function (x, y, items, ownerName) {
  528. let bagCell = 50;
  529. let topQuality = 0;
  530. let iLen = items.length;
  531. for (let i = 0; i < iLen; i++) {
  532. let quality = items[i].quality;
  533. items[i].fromMob = !!this.obj.mob;
  534. if (quality > topQuality)
  535. topQuality = quality;
  536. }
  537. if (topQuality === 0)
  538. bagCell = 50;
  539. else if (topQuality === 1)
  540. bagCell = 51;
  541. else if (topQuality === 2)
  542. bagCell = 128;
  543. else if (topQuality === 3)
  544. bagCell = 52;
  545. else
  546. bagCell = 53;
  547. let obj = this.obj.instance.objects.buildObjects([{
  548. sheetName: 'objects',
  549. cell: bagCell,
  550. x: x,
  551. y: y,
  552. properties: {
  553. cpnChest: {
  554. ownerName: ownerName,
  555. ttl: 1710
  556. },
  557. cpnInventory: {
  558. items: extend([], items)
  559. }
  560. }
  561. }]);
  562. return obj;
  563. },
  564. hasSpace: function (item) {
  565. if (this.inventorySize !== -1) {
  566. if (item) {
  567. let exists = this.items.find(i => (i.name === item.name));
  568. if (exists && (exists.quantity || item.quantity))
  569. return true;
  570. }
  571. let nonEqItems = this.items.filter(f => !f.eq).length;
  572. return (nonEqItems < this.inventorySize);
  573. } return true;
  574. },
  575. getItem: function (item, hideMessage, noStack, hideAlert) {
  576. this.obj.instance.eventEmitter.emit('onBeforeGetItem', item, this.obj);
  577. //We need to know if a mob dropped it for quest purposes
  578. let fromMob = item.fromMob;
  579. if (!item.has('quality'))
  580. item.quality = 0;
  581. //Players can't have fromMob items in their inventory but bags can (dropped by a mob)
  582. if (this.obj.player)
  583. delete item.fromMob;
  584. //Store the quantity to send to the player
  585. let quantity = item.quantity;
  586. let exists = false;
  587. if ((item.material || item.quest || item.quantity) && !item.noStack && !item.uses && !noStack) {
  588. let existItem = this.items.find(i => i.name === item.name);
  589. if (existItem) {
  590. exists = true;
  591. existItem.quantity = (existItem.quantity || 1) + (item.quantity || 1);
  592. item = existItem;
  593. }
  594. }
  595. if (!exists)
  596. delete item.pos;
  597. //Get next id
  598. if (!exists) {
  599. let id = 0;
  600. let items = this.items;
  601. let iLen = items.length;
  602. if (!this.hasSpace()) {
  603. if (!hideMessage) {
  604. this.obj.instance.syncer.queue('onGetMessages', {
  605. id: this.obj.id,
  606. messages: [{
  607. class: 'color-redA',
  608. message: 'your bags are too full to loot any more items',
  609. type: 'info'
  610. }]
  611. }, [this.obj.serverId]);
  612. }
  613. return false;
  614. }
  615. for (let i = 0; i < iLen; i++) {
  616. let fItem = items[i];
  617. if (fItem.id >= id)
  618. id = fItem.id + 1;
  619. }
  620. item.id = id;
  621. if (item.eq)
  622. delete item.pos;
  623. if (!item.has('pos') && !item.eq) {
  624. let pos = iLen;
  625. for (let i = 0; i < iLen; i++) {
  626. if (!items.some(fi => (fi.pos === i))) {
  627. pos = i;
  628. break;
  629. }
  630. }
  631. item.pos = pos;
  632. }
  633. }
  634. if (this.obj.player) {
  635. let messages = [];
  636. let msg = item.name;
  637. if (quantity)
  638. msg += ' x' + quantity;
  639. else if ((item.stats) && (item.stats.weight))
  640. msg += ` ${item.stats.weight}lb`;
  641. messages.push({
  642. class: 'q' + item.quality,
  643. message: 'loot: {' + msg + '}',
  644. item: item,
  645. type: 'loot'
  646. });
  647. if (!hideAlert) {
  648. this.obj.instance.syncer.queue('onGetDamage', {
  649. id: this.obj.id,
  650. event: true,
  651. text: 'loot'
  652. }, -1);
  653. }
  654. if (!hideMessage) {
  655. this.obj.instance.syncer.queue('onGetMessages', {
  656. id: this.obj.id,
  657. messages: messages
  658. }, [this.obj.serverId]);
  659. }
  660. }
  661. if (item.effects) {
  662. item.effects.forEach(function (e) {
  663. if (e.mtx) {
  664. let mtxUrl = mtx.get(e.mtx);
  665. let mtxModule = require('../' + mtxUrl);
  666. e.events = mtxModule.events;
  667. } else if (e.type) {
  668. let effectUrl = itemEffects.get(e.type);
  669. try {
  670. let effectModule = require('../' + effectUrl);
  671. e.text = effectModule.events.onGetText(item, e);
  672. e.events = effectModule.events;
  673. } catch (error) {}
  674. }
  675. });
  676. }
  677. if (!exists)
  678. this.items.push(item);
  679. if (item.eq) {
  680. if (item.ability)
  681. this.learnAbility(item.id, item.runeSlot);
  682. else
  683. this.obj.equipment.equip(item.id);
  684. } else if (item.has('quickSlot')) {
  685. this.obj.equipment.setQuickSlot({
  686. itemId: item.id,
  687. slot: item.quickSlot
  688. });
  689. } else if (!item.effects)
  690. this.obj.syncer.setArray(true, 'inventory', 'getItems', item, true);
  691. else {
  692. let result = extend({}, item);
  693. result.effects = result.effects.map(e => ({
  694. factionId: e.factionId,
  695. text: e.text,
  696. properties: e.properties
  697. }));
  698. let reputation = this.obj.reputation;
  699. //Don't do this check if we don't have a reputation cpn. That means this is most likely a bag
  700. if ((reputation) && (result.factions)) {
  701. result.factions = result.factions.map(function (f) {
  702. let faction = reputation.getBlueprint(f.id);
  703. let factionTier = reputation.getTier(f.id);
  704. let noEquip = null;
  705. if (factionTier < f.tier)
  706. noEquip = true;
  707. return {
  708. name: faction.name,
  709. tier: f.tier,
  710. tierName: ['Hated', 'Hostile', 'Unfriendly', 'Neutral', 'Friendly', 'Honored', 'Revered', 'Exalted'][f.tier],
  711. noEquip: noEquip
  712. };
  713. }, this);
  714. }
  715. this.obj.syncer.setArray(true, 'inventory', 'getItems', result, true);
  716. }
  717. if (!hideMessage) {
  718. if (fromMob)
  719. this.obj.fireEvent('afterLootMobItem', item);
  720. }
  721. return item;
  722. },
  723. dropBag: function (ownerName, killSource) {
  724. if (!this.blueprint)
  725. return;
  726. //Only drop loot if this player is in the zone
  727. let playerObject = this.obj.instance.objects.find(o => o.name === ownerName);
  728. if (!playerObject)
  729. return;
  730. let items = this.items;
  731. let iLen = items.length;
  732. for (let i = 0; i < iLen; i++) {
  733. delete items[i].eq;
  734. delete items[i].pos;
  735. }
  736. let blueprint = this.blueprint;
  737. let magicFind = (blueprint.magicFind || 0);
  738. let savedItems = extend([], this.items);
  739. this.items = [];
  740. let dropEvent = {
  741. chanceMultiplier: 1,
  742. source: this.obj
  743. };
  744. playerObject.fireEvent('beforeGenerateLoot', dropEvent);
  745. if ((!blueprint.noRandom) || (blueprint.alsoRandom)) {
  746. let bonusMagicFind = killSource.stats.values.magicFind;
  747. let rolls = blueprint.rolls;
  748. let itemQuantity = killSource.stats.values.itemQuantity;
  749. rolls += ~~(itemQuantity / 100);
  750. if ((Math.random() * 100) < (itemQuantity % 100))
  751. rolls++;
  752. for (let i = 0; i < rolls; i++) {
  753. if (Math.random() * 100 >= (blueprint.chance || 35) * dropEvent.chanceMultiplier)
  754. continue;
  755. let itemBlueprint = {
  756. level: this.obj.stats.values.level,
  757. magicFind: magicFind,
  758. bonusMagicFind: bonusMagicFind
  759. };
  760. let statValues = this.obj.stats.values;
  761. let useItem = generator.generate(itemBlueprint, statValues.level);
  762. this.getItem(useItem);
  763. }
  764. }
  765. if (blueprint.noRandom) {
  766. let blueprints = blueprint.blueprints;
  767. for (let i = 0; i < blueprints.length; i++) {
  768. let drop = blueprints[i];
  769. if ((blueprint.chance) && (~~(Math.random() * 100) >= blueprint.chance * dropEvent.chanceMultiplier))
  770. continue;
  771. else if ((drop.maxLevel) && (drop.maxLevel < killSource.stats.values.level))
  772. continue;
  773. else if ((drop.chance) && (~~(Math.random() * 100) >= drop.chance * dropEvent.chanceMultiplier))
  774. continue;
  775. drop.level = drop.level || this.obj.stats.values.level;
  776. drop.magicFind = magicFind;
  777. let item = drop;
  778. if ((!item.quest) && (item.type !== 'key'))
  779. item = generator.generate(drop);
  780. if (!item.slot)
  781. delete item.level;
  782. this.getItem(item, true);
  783. }
  784. }
  785. playerObject.fireEvent('beforeTargetDeath', this.obj, this.items);
  786. this.obj.instance.eventEmitter.emit('onBeforeDropBag', this.obj, this.items, killSource);
  787. if (this.items.length > 0)
  788. this.createBag(this.obj.x, this.obj.y, this.items, ownerName);
  789. this.items = savedItems;
  790. },
  791. giveItems: function (obj, hideMessage) {
  792. let objInventory = obj.inventory;
  793. let items = this.items;
  794. let iLen = items.length;
  795. for (let i = 0; i < iLen; i++) {
  796. let item = items[i];
  797. if (objInventory.getItem(item, hideMessage)) {
  798. items.splice(i, 1);
  799. i--;
  800. iLen--;
  801. } else
  802. return false;
  803. }
  804. return true;
  805. },
  806. fireEvent: function (event, args) {
  807. let items = this.items;
  808. let iLen = items.length;
  809. for (let i = 0; i < iLen; i++) {
  810. let item = items[i];
  811. if ((!item.eq) && (!item.active))
  812. continue;
  813. let effects = item.effects;
  814. if (!effects)
  815. continue;
  816. let eLen = effects.length;
  817. for (let j = 0; j < eLen; j++) {
  818. let effect = effects[j];
  819. let effectEvent = effect.events[event];
  820. if (!effectEvent)
  821. continue;
  822. effectEvent.apply(this.obj, [item, ...args]);
  823. }
  824. }
  825. },
  826. clear: function () {
  827. delete this.items;
  828. this.items = [];
  829. },
  830. equipItemErrors: function (item) {
  831. let errors = [];
  832. if (!this.obj.player)
  833. return [];
  834. let stats = this.obj.stats.values;
  835. if (item.level > stats.level)
  836. errors.push('level');
  837. if ((item.requires) && (stats[item.requires[0].stat] < item.requires[0].value))
  838. errors.push(item.requires[0].stat);
  839. if (item.factions) {
  840. if (item.factions.some(function (f) {
  841. return f.noEquip;
  842. }))
  843. errors.push('faction');
  844. }
  845. return errors;
  846. },
  847. canEquipItem: function (item) {
  848. return (this.equipItemErrors(item).length === 0);
  849. }
  850. };