25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

332 lines
7.8 KiB

  1. define([
  2. 'js/system/events',
  3. 'js/system/client',
  4. 'html!ui/templates/workbench/template',
  5. 'css!ui/templates/workbench/styles',
  6. 'ui/shared/renderItem',
  7. 'js/misc/statTranslations'
  8. ], function (
  9. events,
  10. client,
  11. template,
  12. styles,
  13. renderItem,
  14. statTranslations
  15. ) {
  16. return {
  17. tpl: template,
  18. centered: true,
  19. modal: true,
  20. hasClose: true,
  21. workbenchId: null,
  22. recipes: null,
  23. currentRecipe: null,
  24. selectedNeedItems: null,
  25. hoverItem: null,
  26. hoverEl: null,
  27. postRender: function () {
  28. this.onEvent('onOpenWorkbench', this.onOpenWorkbench.bind(this));
  29. this.onEvent('onCloseWorkbench', this.hide.bind(this));
  30. this.onEvent('onGetItems', this.onGetItems.bind(this));
  31. this.onEvent('onKeyDown', this.onKeyDown.bind(this));
  32. this.onEvent('onKeyUp', this.onKeyUp.bind(this));
  33. this.on('.btnCraft', 'click', this.craft.bind(this));
  34. this.on('.btnCancel', 'click', this.hide.bind(this));
  35. },
  36. onOpenWorkbench: function (msg) {
  37. this.workbenchId = msg.workbenchId;
  38. this.find('.mainHeading').html(msg.name);
  39. this.find('.itemPicker').hide();
  40. this.find('.needItems').hide();
  41. this.renderRecipes(msg.recipes);
  42. this.show();
  43. },
  44. //Redraw items if they change
  45. onGetItems: function (items) {
  46. if (!this.currentRecipe)
  47. return;
  48. const { currentRecipe: { needItems } } = this;
  49. this.buildNeedItemBoxes(needItems, true);
  50. },
  51. renderRecipes: function (recipes) {
  52. this.recipes = recipes;
  53. let container = this.find('.left .list').empty();
  54. recipes.forEach(function (r) {
  55. let el = $('<div class="item">' + r + '</div>')
  56. .appendTo(container);
  57. el.on('click', this.onSelectRecipe.bind(this, el, r));
  58. }, this);
  59. },
  60. onSelectRecipe: function (el, recipeName) {
  61. el.parent().find('.selected').removeClass('selected');
  62. el.addClass('selected');
  63. client.request({
  64. cpn: 'player',
  65. method: 'performAction',
  66. data: {
  67. targetId: this.workbenchId,
  68. cpn: 'workbench',
  69. method: 'getRecipe',
  70. data: {
  71. name: recipeName
  72. }
  73. },
  74. callback: this.onGetRecipe.bind(this, false)
  75. });
  76. },
  77. onGetRecipe: function (persistNeedItems, recipe) {
  78. const { name: recipeName, description, materials, needItems } = recipe;
  79. this.currentRecipe = recipe;
  80. this.find('.info .title').html(recipeName);
  81. this.find('.description').html(description);
  82. this.find('.materialList .material').remove();
  83. let container = this.find('.materialList')
  84. .css({
  85. visibility: 'visible'
  86. });
  87. let canCraft = !!materials.length;
  88. materials.forEach(m => {
  89. const { needQuantity, nameLike, name: materialName, haveQuantity, noHaveEnough } = m;
  90. const materialText = `${needQuantity}x ${(nameLike || materialName)} (${haveQuantity})`;
  91. let el = $(`<div class="material">${materialText}</div>`)
  92. .appendTo(container);
  93. if (noHaveEnough) {
  94. canCraft = false;
  95. el.addClass('need');
  96. }
  97. });
  98. this.find('.btnCraft')
  99. .removeClass('disabled');
  100. if (!canCraft) {
  101. this.find('.btnCraft')
  102. .addClass('disabled');
  103. }
  104. //If there are no materials, the selected items aren't valid
  105. this.find('.materialList').show();
  106. if (!materials.length) {
  107. this.find('.materialList').hide();
  108. persistNeedItems = false;
  109. }
  110. this.buildNeedItemBoxes(needItems, persistNeedItems);
  111. },
  112. buildNeedItemBoxes: function (needItems = [], persistNeedItems) {
  113. if (!persistNeedItems) {
  114. this.selectedNeedItems = new Array(needItems.length);
  115. this.selectedNeedItems.fill(null);
  116. }
  117. const container = this.find('.needItems').hide();
  118. const list = container.find('.list').empty();
  119. if (!needItems.length)
  120. return;
  121. container.css({ display: 'flex' });
  122. needItems.forEach((n, i) => this.buildNeedItemBox(list, n, i));
  123. },
  124. buildNeedItemBox: function (container, needItem, needItemIndex) {
  125. const item = this.selectedNeedItems[needItemIndex];
  126. const el = renderItem(container, item);
  127. el
  128. .on('mousemove', this.toggleTooltip.bind(this, true, el, needItem, item))
  129. .on('mouseleave', this.toggleTooltip.bind(this, false, el, needItem, item))
  130. .on('click', this.toggleItemPicker.bind(this, true, needItem, needItemIndex));
  131. },
  132. toggleItemPicker: function (show, needItem, needItemIndex) {
  133. const container = this.find('.itemPicker').hide();
  134. if (!show)
  135. return;
  136. const { allowedItemIds } = needItem;
  137. container
  138. .css({ display: 'flex' })
  139. .find('.heading-text').html(needItem.info);
  140. const list = container.find('.list').empty();
  141. const items = window.player.inventory.items
  142. .filter(item => {
  143. const isValidItem = allowedItemIds.find(f => f === item.id);
  144. return isValidItem;
  145. });
  146. items.forEach(item => {
  147. const el = renderItem(list, item);
  148. el
  149. .on('click', this.onSelectItem.bind(this, item, needItemIndex))
  150. .on('mousemove', this.toggleTooltip.bind(this, true, el, null, item))
  151. .on('mouseleave', this.toggleTooltip.bind(this, false, el, null, item));
  152. });
  153. },
  154. onSelectItem: function (item, needItemIndex) {
  155. this.selectedNeedItems[needItemIndex] = item;
  156. const { currentRecipe: { needItems } } = this;
  157. this.buildNeedItemBoxes(needItems, true);
  158. const allItemsSelected = this.selectedNeedItems.every(i => !!i);
  159. if (allItemsSelected && this.currentRecipe.dynamicMaterials) {
  160. const pickedItemIds = this.selectedNeedItems.map(i => i.id);
  161. client.request({
  162. cpn: 'player',
  163. method: 'performAction',
  164. data: {
  165. targetId: this.workbenchId,
  166. cpn: 'workbench',
  167. method: 'getRecipe',
  168. data: {
  169. name: this.currentRecipe.name,
  170. pickedItemIds
  171. }
  172. },
  173. callback: this.onGetRecipe.bind(this, true)
  174. });
  175. }
  176. this.find('.itemPicker').hide();
  177. },
  178. toggleTooltip: function (show, el, needItem, item, e) {
  179. if (item) {
  180. this.hoverItem = show ? item : null;
  181. this.hoverEl = show ? el : null;
  182. }
  183. let pos = null;
  184. if (e) {
  185. const { clientX, clientY } = e;
  186. pos = {
  187. x: clientX + 25,
  188. y: clientY
  189. };
  190. }
  191. if (item) {
  192. if (show)
  193. events.emit('onShowItemTooltip', item, pos, true);
  194. else
  195. events.emit('onHideItemTooltip', item);
  196. return;
  197. }
  198. if (show)
  199. events.emit('onShowTooltip', needItem.info, el[0], pos);
  200. else
  201. events.emit('onHideTooltip', el[0]);
  202. },
  203. craft: function () {
  204. const selectedRecipe = this.find('.left .list .item.selected').html();
  205. const pickedItemIds = this.selectedNeedItems
  206. .map(item => item.id);
  207. client.request({
  208. cpn: 'player',
  209. method: 'performAction',
  210. data: {
  211. targetId: this.workbenchId,
  212. cpn: 'workbench',
  213. method: 'craft',
  214. data: {
  215. name: selectedRecipe,
  216. pickedItemIds
  217. }
  218. },
  219. callback: this.onCraft.bind(this)
  220. });
  221. },
  222. onCraft: function ({ recipe, resultMsg }) {
  223. this.onGetRecipe(true, recipe);
  224. if (resultMsg) {
  225. const { msg: baseMsg, addStatMsgs = [] } = resultMsg;
  226. let msg = baseMsg;
  227. addStatMsgs.forEach(a => {
  228. const statName = statTranslations.translate(a.stat);
  229. msg += `<br />${(a.value > 0) ? '+' : ''}${a.value} ${statName}`;
  230. });
  231. events.emit('onGetAnnouncement', {
  232. msg,
  233. top: 150
  234. });
  235. }
  236. },
  237. onAfterShow: function () {
  238. this.clear();
  239. },
  240. clear: function () {
  241. this.find('.left .list .selected').removeClass('selected');
  242. this.find('.info .title').html('');
  243. this.find('.description').html('');
  244. this.find('.materialList .material').remove();
  245. this.find('.materialList')
  246. .css({
  247. visibility: 'hidden'
  248. });
  249. this.find('.btnCraft').addClass('disabled');
  250. },
  251. onKeyDown: function (key) {
  252. if (key === 'shift' && this.hoverItem)
  253. this.toggleTooltip(true, this.hoverEl, null, this.hoverItem);
  254. },
  255. onKeyUp: function (key) {
  256. if (key === 'shift' && this.hoverItem)
  257. this.toggleTooltip(true, this.hoverEl, null, this.hoverItem);
  258. }
  259. };
  260. });