Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

786 lignes
17 KiB

  1. define([
  2. 'items/generator',
  3. 'items/salvager',
  4. 'items/enchanter',
  5. 'objects/objects',
  6. 'config/classes'
  7. ], function(
  8. generator,
  9. salvager,
  10. enchanter,
  11. objects,
  12. classes
  13. ) {
  14. return {
  15. type: 'inventory',
  16. inventorySize: 50,
  17. items: [],
  18. blueprint: null,
  19. init: function(blueprint, isTransfer) {
  20. var items = blueprint.items || [];
  21. var iLen = items.length;
  22. //Spells should be sorted so they're EQ'd in the right order
  23. items.sort(function(a, b) {
  24. var aId = (a.spellId != null) ? ~~a.spellId : 9999;
  25. var bId = (b.spellId != null) ? ~~b.spellId : 9999;
  26. return (aId - bId);
  27. });
  28. for (var i = 0; i < iLen; i++) {
  29. var item = items[i];
  30. //Hacks for old items
  31. if ((item.spell) && (!item.spell.rolls))
  32. continue;
  33. else if ((item.spell) && (item.type == 'Spear')) {
  34. item.spell.properties = item.spell.properties || {};
  35. item.spell.properties.range = item.range;
  36. }
  37. this.getItem(items[i], true);
  38. }
  39. this.hookItemEvents();
  40. if ((this.obj.player) && (!isTransfer)) {
  41. this.getDefaultAbilities();
  42. /*this.getItem(generator.generate({
  43. spell: true,
  44. spellName: 'arcane barrier'
  45. }));*/
  46. /*for (var i = 0; i < 1; i++) {
  47. var item = generator.generate({
  48. slot: 'twoHanded',
  49. type: 'Spear',
  50. quality: 4,
  51. level: 1
  52. });
  53. this.getItem(item);
  54. }*/
  55. }
  56. delete blueprint.items;
  57. this.blueprint = blueprint;
  58. },
  59. transfer: function() {
  60. this.hookItemEvents();
  61. },
  62. hookItemEvents: function() {
  63. var items = this.items;
  64. var iLen = items.length;
  65. for (var i = 0; i < iLen; i++) {
  66. var item = items[i];
  67. if (item.effects) {
  68. item.effects.forEach(function(e) {
  69. var faction = require('config/factions/' + e.factionId);
  70. var statGenerator = faction.uniqueStat;
  71. statGenerator.generate(item);
  72. });
  73. }
  74. }
  75. },
  76. //Client Actions
  77. enchantItem: function(msg) {
  78. var item = this.findItem(msg.itemId);
  79. if ((!item) || (!item.slot) || (item.eq) || ((msg.action == 'scour') && (item.power == 0))) {
  80. this.resolveCallback(msg);
  81. return;
  82. }
  83. enchanter.enchant(this.obj, item, msg);
  84. },
  85. getEnchantMaterials: function(msg) {
  86. var result = [];
  87. var item = this.findItem(msg.itemId);
  88. if ((item) && (item.slot))
  89. result = enchanter.getEnchantMaterials(item, msg.action);
  90. this.resolveCallback(msg, result);
  91. },
  92. learnAbility: function(id, forceLearn) {
  93. var replaceId = null;
  94. var newSpellId = id.spellId;
  95. if (id.id != null) {
  96. replaceId = id.replaceId;
  97. id = id.id;
  98. }
  99. var item = this.findItem(id);
  100. if ((!item) || (!item.spell) || ((item.spellId == null) && (item.eq) && (!forceLearn))) {
  101. if (item)
  102. item.eq = false;
  103. return;
  104. }
  105. var spellbook = this.obj.spellbook;
  106. if ((item.eq) && (!forceLearn)) {
  107. delete item.eq;
  108. spellbook.removeSpellById(item.spellId);
  109. delete item.spellId;
  110. this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
  111. return;
  112. }
  113. if (replaceId != null) {
  114. var replaceItem = this.findItem(replaceId);
  115. if (replaceItem) {
  116. delete replaceItem.eq;
  117. spellbook.removeSpellById(replaceItem.spellId);
  118. newSpellId = replaceItem.spellId;
  119. delete replaceItem.spellId;
  120. this.obj.syncer.setArray(true, 'inventory', 'getItems', replaceItem);
  121. }
  122. }
  123. if (spellbook.spells.length >= 3) {
  124. if (item.slot)
  125. item.spellId = -1;
  126. this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
  127. return;
  128. }
  129. item.spellId = spellbook.addSpellFromRune(item.spell, newSpellId);
  130. if (item.spellId != -1)
  131. item.eq = true;
  132. else
  133. delete item.spell;
  134. this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
  135. },
  136. stashItem: function(id) {
  137. var item = this.findItem(id);
  138. if ((!item) || (item.quest) || (item.noSalvage))
  139. return;
  140. delete item.pos;
  141. var stash = this.obj.stash;
  142. if (!stash.active)
  143. return;
  144. var clonedItem = extend(true, {}, item);
  145. this.destroyItem(id);
  146. stash.deposit(clonedItem);
  147. },
  148. salvageItem: function(id) {
  149. var item = this.findItem(id);
  150. if ((!item) || (item.material) || (item.quest) || (item.noSalvage) || (item.eq))
  151. return;
  152. var messages = [];
  153. var items = salvager.salvage(item);
  154. var iLen = items.length;
  155. if (!iLen)
  156. return;
  157. for (var i = 0; i < iLen; i++) {
  158. var material = items[i];
  159. this.getItem(material, true);
  160. messages.push({
  161. class: 'q' + material.quality,
  162. message: 'salvage (' + material.name + ' x' + material.quantity + ')'
  163. });
  164. }
  165. this.obj.instance.syncer.queue('onGetMessages', {
  166. id: this.obj.id,
  167. messages: messages
  168. }, [this.obj.serverId]);
  169. this.destroyItem(id);
  170. },
  171. destroyItem: function(id, amount) {
  172. var item = this.findItem(id);
  173. if (!item)
  174. return;
  175. amount = amount || item.quantity;
  176. if (item.eq)
  177. this.obj.equipment.unequip(id);
  178. if ((item.quantity) && (amount)) {
  179. item.quantity -= amount;
  180. if (item.quantity <= 0) {
  181. this.items.spliceWhere(i => i.id == id);
  182. this.obj.syncer.setArray(true, 'inventory', 'destroyItems', id);
  183. } else
  184. this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
  185. } else {
  186. this.items.spliceWhere(i => i.id == id);
  187. this.obj.syncer.setArray(true, 'inventory', 'destroyItems', id);
  188. }
  189. if (this.obj.player)
  190. this.getDefaultAbilities();
  191. this.obj.fireEvent('afterDestroyItem', item, amount);
  192. return item;
  193. },
  194. dropItem: function(id) {
  195. var item = this.findItem(id);
  196. if (!item)
  197. return;
  198. delete item.pos;
  199. //Find close open position
  200. var x = this.obj.x;
  201. var y = this.obj.y;
  202. var dropCell = this.obj.instance.physics.getOpenCellInArea(x - 1, y - 1, x + 1, y + 1);
  203. if (!dropCell)
  204. return;
  205. if (item.eq)
  206. this.obj.equipment.unequip(id);
  207. this.items.spliceWhere(i => i.id == id);
  208. this.obj.syncer.setArray(true, 'inventory', 'destroyItems', id);
  209. this.createBag(dropCell.x, dropCell.y, [item]);
  210. },
  211. moveItem: function(msgs) {
  212. msgs.forEach(function(m) {
  213. var item = this.findItem(m.id);
  214. if (!item)
  215. return;
  216. item.pos = m.pos;
  217. }, this);
  218. },
  219. //Helpers
  220. resolveCallback: function(msg, result) {
  221. var callbackId = (msg.callbackId != null) ? msg.callbackId : msg;
  222. result = result || [];
  223. if (callbackId == null)
  224. return;
  225. this.obj.instance.syncer.queue('serverModule', {
  226. module: 'atlas',
  227. method: 'resolveCallback',
  228. msg: {
  229. id: callbackId,
  230. result: result
  231. }
  232. });
  233. },
  234. findItem: function(id) {
  235. if (id == null)
  236. return null;
  237. return this.items.find(i => i.id == id);
  238. },
  239. getDefaultAbilities: function() {
  240. var hasWeapon = this.items.some(function(i) {
  241. return (
  242. (i.spell) &&
  243. (i.spell.rolls) &&
  244. (i.spell.rolls.damage != null) &&
  245. (i.slot == 'twoHanded')
  246. );
  247. });
  248. if (!hasWeapon) {
  249. var item = generator.generate({
  250. slot: 'twoHanded',
  251. type: classes.weapons[this.obj.class],
  252. quality: 0,
  253. spellQuality: 'mid'
  254. });
  255. item.eq = true;
  256. item.noSalvage = true;
  257. this.getItem(item);
  258. }
  259. var hasSpell = this.items.some(function(i) {
  260. return (
  261. (i.spell) &&
  262. (i.spell.rolls) &&
  263. ((i.spell.rolls.damage != null) || (i.spell.rolls.healing != null)) &&
  264. (i.slot != 'twoHanded')
  265. );
  266. });
  267. if (!hasSpell) {
  268. var item = generator.generate({
  269. spell: true,
  270. spellQuality: 'basic',
  271. spellName: classes.spells[this.obj.class][0]
  272. });
  273. item.eq = true;
  274. item.noSalvage = true;
  275. this.getItem(item);
  276. item = generator.generate({
  277. spell: true,
  278. spellQuality: 'basic',
  279. spellName: classes.spells[this.obj.class][1]
  280. });
  281. item.eq = true;
  282. item.noSalvage = true;
  283. this.getItem(item);
  284. }
  285. },
  286. createBag: function(x, y, items, ownerId) {
  287. if (ownerId == null)
  288. ownerId = -1;
  289. var bagCell = 50;
  290. var topQuality = 0;
  291. var iLen = items.length;
  292. for (var i = 0; i < iLen; i++) {
  293. var quality = items[i].quality;
  294. items[i].fromMob = !!this.obj.mob;
  295. if (quality > topQuality)
  296. topQuality = quality;
  297. }
  298. if (topQuality == 0)
  299. bagCell = 50;
  300. else if (topQuality == 1)
  301. bagCell = 51;
  302. else if (topQuality == 2)
  303. bagCell = 128;
  304. else if (topQuality == 3)
  305. bagCell = 52;
  306. else
  307. bagCell = 53;
  308. var obj = this.obj.instance.objects.buildObjects([{
  309. sheetName: 'objects',
  310. cell: bagCell,
  311. x: x,
  312. y: y,
  313. properties: {
  314. cpnChest: {
  315. ownerId: ownerId
  316. },
  317. cpnInventory: {
  318. items: extend(true, [], items)
  319. }
  320. }
  321. }]);
  322. return obj;
  323. },
  324. getItem: function(item, hideMessage) {
  325. //We need to know if a mob dropped it for quest purposes
  326. var fromMob = item.fromMob;
  327. if (item.quality == null)
  328. item.quality = 0;
  329. //Players can't have fromMob items in their inventory but bags can (dropped by a mob)
  330. if (this.obj.player)
  331. delete item.fromMob;
  332. //Store the quantity to send to the player
  333. var quantity = item.quantity;
  334. //Material?
  335. var exists = false;
  336. if ((item.material) || (item.quest)) {
  337. var existItem = this.items.find(i => i.name == item.name);
  338. if (existItem) {
  339. exists = true;
  340. if (!existItem.quantity)
  341. existItem.quantity = 1;
  342. existItem.quantity += item.quantity;
  343. item = existItem;
  344. }
  345. }
  346. //Get next id
  347. if (!exists) {
  348. var id = 0;
  349. var items = this.items;
  350. var iLen = items.length;
  351. var nonEqItems = items.filter(f => !f.eq).length;
  352. if ((nonEqItems >= this.inventorySize) && (!hideMessage)) {
  353. this.obj.instance.syncer.queue('onGetMessages', {
  354. id: this.obj.id,
  355. messages: [{
  356. class: 'q0',
  357. message: 'you bags are too full to loot any more items',
  358. type: 'info'
  359. }]
  360. }, [this.obj.serverId]);
  361. return false;
  362. }
  363. for (var i = 0; i < iLen; i++) {
  364. var fItem = items[i];
  365. if (fItem.id >= id) {
  366. id = fItem.id + 1;
  367. }
  368. }
  369. item.id = id;
  370. if (item.eq)
  371. delete item.pos;
  372. if ((item.pos == null) && (!item.eq)) {
  373. var pos = iLen;
  374. for (var i = 0; i < iLen; i++) {
  375. if (!items.some(fi => (fi.pos == i))) {
  376. pos = i;
  377. break;
  378. }
  379. }
  380. item.pos = pos;
  381. }
  382. }
  383. if ((this.obj.player) && (!hideMessage)) {
  384. var messages = [];
  385. var msg = item.name;
  386. if (quantity)
  387. msg += ' x' + quantity;
  388. messages.push({
  389. class: 'q' + item.quality,
  390. message: 'loot (' + msg + ')',
  391. type: 'loot'
  392. });
  393. this.obj.instance.syncer.queue('onGetDamage', {
  394. id: this.obj.id,
  395. event: true,
  396. text: 'loot'
  397. });
  398. this.obj.instance.syncer.queue('onGetMessages', {
  399. id: this.obj.id,
  400. messages: messages
  401. }, [this.obj.serverId]);
  402. }
  403. //TODO: Remove later, just for test
  404. if (item.stats) {
  405. var stats = Object.keys(item.stats);
  406. var sLen = stats.length
  407. for (var i = 0; i < sLen; i++) {
  408. var s = stats[i];
  409. var val = item.stats[s];
  410. if (s == 'maxHp') {
  411. delete item.stats[s];
  412. item.stats.hpMax = val;
  413. } else if (s == 'maxMana') {
  414. delete item.stats[s];
  415. item.stats.manaMax = val;
  416. }
  417. }
  418. }
  419. if (!exists)
  420. this.items.push(item);
  421. if (item.eq) {
  422. if (item.ability)
  423. this.learnAbility(item.id, true);
  424. else
  425. this.obj.equipment.equip(item.id);
  426. } else {
  427. if (!item.effects)
  428. this.obj.syncer.setArray(true, 'inventory', 'getItems', item);
  429. else {
  430. var result = extend(true, {}, item);
  431. result.effects = result.effects.map(e => ({
  432. factionId: e.factionId,
  433. text: e.text,
  434. properties: e.properties
  435. }));
  436. var reputation = this.obj.reputation;
  437. //Don't do this check if we don't have a reputation cpn. That means this is most likely a bag
  438. if ((reputation) && (result.factions)) {
  439. result.factions = result.factions.map(function(f) {
  440. var faction = reputation.getBlueprint(f.id);
  441. var factionTier = reputation.getTier(f.id);
  442. var noEquip = null;
  443. if (factionTier < f.tier)
  444. noEquip = true;
  445. return {
  446. name: faction.name,
  447. tier: f.tier,
  448. tierName: ['Hated', 'Hostile', 'Unfriendly', 'Neutral', 'Friendly', 'Honored', 'Revered', 'Exalted'][f.tier],
  449. noEquip: noEquip
  450. };
  451. }, this);
  452. }
  453. this.obj.syncer.setArray(true, 'inventory', 'getItems', result);
  454. }
  455. }
  456. if (!hideMessage) {
  457. if (fromMob)
  458. this.obj.fireEvent('afterLootMobItem', item);
  459. }
  460. return item;
  461. },
  462. dropBag: function(ownerId, killSource) {
  463. if (!this.blueprint)
  464. return;
  465. //Only drop loot if this player is in the zone
  466. var playerObject = this.obj.instance.objects.find(o => o.serverId == ownerId);
  467. if (!playerObject)
  468. return;
  469. //Get player's spells' statTypes
  470. var stats = [];
  471. playerObject.spellbook.spells.forEach(function(s) {
  472. var spellStatType = s.statType;
  473. if (!(spellStatType instanceof Array))
  474. spellStatType = [spellStatType];
  475. spellStatType.forEach(function(ss) {
  476. if (stats.indexOf(ss) == -1)
  477. stats.push(ss);
  478. });
  479. });
  480. var items = this.items;
  481. var iLen = items.length;
  482. for (var i = 0; i < iLen; i++) {
  483. delete items[i].eq;
  484. delete items[i].pos;
  485. }
  486. var blueprint = this.blueprint;
  487. if (blueprint.noRandom) {
  488. this.items = [];
  489. var blueprints = blueprint.blueprints;
  490. for (var i = 0; i < blueprints.length; i++) {
  491. var drop = blueprints[i];
  492. if ((drop.maxLevel) && (drop.maxLevel < killSource.stats.values.level))
  493. continue;
  494. drop.level = drop.level || level;
  495. drop.magicFind = magicFind;
  496. this.getItem(generator.generate(drop), true);
  497. }
  498. killSource.fireEvent('beforeTargetDeath', this.obj, this.items);
  499. if (this.items.length > 0)
  500. this.createBag(this.obj.x, this.obj.y, this.items, ownerId);
  501. } else {
  502. var instancedItems = extend(true, [], this.items);
  503. var useItems = [];
  504. var magicFind = (blueprint.magicFind || 0) + killSource.stats.values.magicFind;
  505. for (var i = 0; i < blueprint.rolls; i++) {
  506. if (Math.random() * 100 >= (blueprint.chance || 35))
  507. continue;
  508. var useItem = null;
  509. if (Math.random() < generator.spellChance) {
  510. useItem = instancedItems
  511. .filter(item => item.ability);
  512. if (useItem.length > 0)
  513. useItem = useItem[~~(Math.random() * useItem.length)];
  514. }
  515. if (!useItem) {
  516. var slot = generator.pickRandomSlot();
  517. var useItem = instancedItems.find(item => item.slot == slot);
  518. }
  519. if (!useItem)
  520. useItem = instancedItems[~~(Math.random() * iLen)];
  521. iLen--;
  522. instancedItems.spliceWhere(item => item == useItem);
  523. //Spells don't have stats
  524. if (useItem.stats)
  525. delete useItem.stats.armor;
  526. var itemBlueprint = {
  527. level: useItem.level,
  528. slot: useItem.slot,
  529. type: useItem.type,
  530. spell: !!useItem.ability,
  531. stats: useItem.stats ? Object.keys(useItem.stats) : null,
  532. magicFind: magicFind
  533. };
  534. useItem = generator.generate(itemBlueprint);
  535. useItems.push(useItem);
  536. }
  537. killSource.fireEvent('beforeTargetDeath', this.obj, useItems);
  538. if (useItems.length > 0)
  539. this.createBag(this.obj.x, this.obj.y, useItems, ownerId);
  540. }
  541. },
  542. giveItems: function(obj, hideMessage) {
  543. var objInventory = obj.inventory;
  544. var messages = [];
  545. var items = this.items;
  546. var iLen = items.length;
  547. for (var i = 0; i < iLen; i++) {
  548. var item = items[i];
  549. if (objInventory.getItem(item, hideMessage)) {
  550. items.splice(i, 1);
  551. i--;
  552. iLen--;
  553. } else
  554. return false;
  555. }
  556. return true;
  557. },
  558. rollItems: function(party) {
  559. var items = this.items;
  560. var iLen = items.length;
  561. for (var i = 0; i < iLen; i++) {
  562. var item = items[i];
  563. this.obj.instance.syncer.queue('serverModule', {
  564. module: 'lootRoller',
  565. method: 'enqueue',
  566. msg: {
  567. party: party,
  568. item: item
  569. }
  570. });
  571. }
  572. this.items = [];
  573. },
  574. fireEvent: function(event, args) {
  575. var items = this.items;
  576. var iLen = items.length;
  577. for (var i = 0; i < iLen; i++) {
  578. var item = items[i];
  579. if (!item.eq)
  580. continue;
  581. var effects = item.effects;
  582. if (!effects)
  583. continue;
  584. var eLen = effects.length;
  585. for (var j = 0; j < eLen; j++) {
  586. var effect = effects[j];
  587. var effectEvent = effect.events[event];
  588. if (!effectEvent)
  589. continue;
  590. effectEvent.call(this.obj, item, args[0]);
  591. }
  592. }
  593. },
  594. clear: function() {
  595. delete this.items;
  596. this.items = [];
  597. },
  598. save: function() {
  599. return {
  600. type: 'inventory',
  601. items: this.items
  602. };
  603. },
  604. simplify: function(self) {
  605. if (!self)
  606. return null;
  607. var reputation = this.obj.reputation;
  608. return {
  609. type: 'inventory',
  610. items: this.items
  611. .map(function(i) {
  612. if (!i.effects)
  613. return i;
  614. else {
  615. var item = extend(true, {}, i);
  616. item.effects = item.effects.map(e => ({
  617. factionId: e.factionId,
  618. text: e.text,
  619. properties: e.properties
  620. }));
  621. if (item.factions) {
  622. item.factions = item.factions.map(function(f) {
  623. var faction = reputation.getBlueprint(f.id);
  624. var factionTier = reputation.getTier(f.id);
  625. var noEquip = null;
  626. if (factionTier < f.tier)
  627. noEquip = true;
  628. return {
  629. id: f.id,
  630. name: faction.name,
  631. tier: f.tier,
  632. tierName: ['Hated', 'Hostile', 'Unfriendly', 'Neutral', 'Friendly', 'Honored', 'Revered', 'Exalted'][f.tier],
  633. noEquip: noEquip
  634. };
  635. }, this);
  636. }
  637. return item;
  638. }
  639. })
  640. };
  641. }
  642. };
  643. });