Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

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