No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 

1049 líneas
23 KiB

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