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.
 
 
 

1021 lines
22 KiB

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