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.
 
 
 

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