@hytopia.com/examples 1.0.11 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/frontiers-rpg-game/assets/icons/items/leather-boots.png +0 -0
  2. package/frontiers-rpg-game/assets/icons/items/leather-bracers.png +0 -0
  3. package/frontiers-rpg-game/assets/icons/items/leather-helmet.png +0 -0
  4. package/frontiers-rpg-game/assets/icons/items/leather-leggings.png +0 -0
  5. package/frontiers-rpg-game/assets/icons/items/leather-vest.png +0 -0
  6. package/frontiers-rpg-game/assets/icons/items/spiked-club.png +0 -0
  7. package/frontiers-rpg-game/assets/icons/skills/crafting.png +0 -0
  8. package/frontiers-rpg-game/assets/models/weapons/.optimized/mace/baseColor.png +0 -0
  9. package/frontiers-rpg-game/assets/models/weapons/.optimized/mace/mace-named-nodes.bin +0 -0
  10. package/frontiers-rpg-game/assets/models/weapons/.optimized/mace/mace-named-nodes.gltf +653 -0
  11. package/frontiers-rpg-game/assets/models/weapons/.optimized/mace/mace.bin +0 -0
  12. package/frontiers-rpg-game/assets/models/weapons/.optimized/mace/mace.gltf +135 -0
  13. package/frontiers-rpg-game/assets/models/weapons/.optimized/mace/mace.gltf.md5 +1 -0
  14. package/frontiers-rpg-game/assets/models/weapons/.optimized/spiked-club/baseColor.png +0 -0
  15. package/frontiers-rpg-game/assets/models/weapons/.optimized/spiked-club/spiked-club-named-nodes.bin +0 -0
  16. package/frontiers-rpg-game/assets/models/weapons/.optimized/spiked-club/spiked-club-named-nodes.gltf +840 -0
  17. package/frontiers-rpg-game/assets/models/weapons/.optimized/spiked-club/spiked-club.bin +0 -0
  18. package/frontiers-rpg-game/assets/models/weapons/.optimized/spiked-club/spiked-club.gltf +141 -0
  19. package/frontiers-rpg-game/assets/models/weapons/.optimized/spiked-club/spiked-club.gltf.md5 +1 -0
  20. package/frontiers-rpg-game/assets/models/weapons/mace.gltf +1 -0
  21. package/frontiers-rpg-game/assets/ui/build.js +2 -0
  22. package/frontiers-rpg-game/assets/ui/index.html +1328 -64
  23. package/frontiers-rpg-game/assets/ui/menus/crafting.html +976 -0
  24. package/frontiers-rpg-game/assets/ui/menus/quests.html +70 -2
  25. package/frontiers-rpg-game/assets/ui/shared/item-stats.html +224 -0
  26. package/frontiers-rpg-game/assets/ui/shared/item-tooltips.html +72 -81
  27. package/frontiers-rpg-game/dev/persistence/player-player-1.json +121 -12
  28. package/frontiers-rpg-game/src/GamePlayer.ts +54 -0
  29. package/frontiers-rpg-game/src/GamePlayerEntity.ts +9 -4
  30. package/frontiers-rpg-game/src/config.ts +7 -0
  31. package/frontiers-rpg-game/src/entities/BaseCraftingEntity.ts +115 -0
  32. package/frontiers-rpg-game/src/entities/enemies/RatkinBruteEntity.ts +2 -0
  33. package/frontiers-rpg-game/src/entities/enemies/RatkinRangerEntity.ts +2 -0
  34. package/frontiers-rpg-game/src/entities/enemies/RatkinSpellcasterEntity.ts +2 -0
  35. package/frontiers-rpg-game/src/entities/enemies/RatkinWarriorEntity.ts +2 -0
  36. package/frontiers-rpg-game/src/entities/enemies/TaintedRatkinBruteEntity.ts +2 -0
  37. package/frontiers-rpg-game/src/entities/enemies/TaintedRatkinRangerEntity.ts +2 -1
  38. package/frontiers-rpg-game/src/entities/enemies/TaintedRatkinSpellcasterEntity.ts +2 -0
  39. package/frontiers-rpg-game/src/entities/enemies/TaintedRatkinWarriorEntity.ts +2 -0
  40. package/frontiers-rpg-game/src/entities/forageables/DecayingPileEntity.ts +2 -2
  41. package/frontiers-rpg-game/src/entities/forageables/RottenLogEntity.ts +2 -2
  42. package/frontiers-rpg-game/src/items/ItemClasses.ts +14 -2
  43. package/frontiers-rpg-game/src/items/materials/RawHideItem.ts +10 -0
  44. package/frontiers-rpg-game/src/items/weapons/DullSwordItem.ts +5 -4
  45. package/frontiers-rpg-game/src/items/weapons/IronDaggerItem.ts +1 -1
  46. package/frontiers-rpg-game/src/items/weapons/IronLongSwordItem.ts +5 -4
  47. package/frontiers-rpg-game/src/items/weapons/SpikedClubItem.ts +26 -0
  48. package/frontiers-rpg-game/src/items/weapons/TrainingSwordItem.ts +5 -4
  49. package/frontiers-rpg-game/src/items/wearables/LeatherBootsItem.ts +14 -0
  50. package/frontiers-rpg-game/src/items/wearables/LeatherBracersItem.ts +14 -0
  51. package/frontiers-rpg-game/src/items/wearables/LeatherHelmetItem.ts +14 -0
  52. package/frontiers-rpg-game/src/items/wearables/LeatherLeggingsItem.ts +14 -0
  53. package/frontiers-rpg-game/src/items/wearables/LeatherVestItem.ts +14 -0
  54. package/frontiers-rpg-game/src/quests/BaseQuest.ts +1 -2
  55. package/frontiers-rpg-game/src/quests/QuestClasses.ts +2 -0
  56. package/frontiers-rpg-game/src/quests/side/FungalForagingQuest.ts +1 -23
  57. package/frontiers-rpg-game/src/quests/side/HammersAndCraftingQuest.ts +139 -0
  58. package/frontiers-rpg-game/src/regions/stalkhaven/StalkhavenRegion.ts +2 -0
  59. package/frontiers-rpg-game/src/regions/stalkhaven/npcs/BlacksmithArdenEntity.ts +114 -0
  60. package/frontiers-rpg-game/src/systems/QuestLog.ts +2 -5
  61. package/package.json +1 -1
  62. package/frontiers-rpg-game/dev/persistence/player-player-2.json +0 -31
  63. package/frontiers-rpg-game/dev/persistence/player-player-3.json +0 -25
  64. package/frontiers-rpg-game/dev/persistence/player-player-4.json +0 -31
  65. package/frontiers-rpg-game/src/items/materials/MonsterHideItem.ts +0 -10
  66. /package/frontiers-rpg-game/assets/icons/items/{monster-hide.png → raw-hide.png} +0 -0
  67. /package/frontiers-rpg-game/assets/models/weapons/{club.gltf → spiked-club.gltf} +0 -0
@@ -20,14 +20,14 @@
20
20
  }
21
21
  </style>
22
22
 
23
- <!-- Included from: ./shared/item-tooltips.html -->
24
- <!-- Shared Item Tooltip System -->
23
+ <!-- Included from: ./shared/item-stats.html -->
24
+ <!-- Shared Item Stats System -->
25
25
  <script>
26
26
  /**
27
- * Centralized Item Tooltip System
28
- * Provides consistent tooltip functionality across all UI components
27
+ * Centralized Item Stats System
28
+ * Provides consistent stat formatting across all UI components
29
29
  */
30
- window.ItemTooltips = (function() {
30
+ window.ItemStats = (function() {
31
31
  'use strict';
32
32
 
33
33
  // Parse color formatting in text: [HEXCODE]text[/] -> <span style="color: #HEXCODE">text</span>
@@ -46,50 +46,24 @@ window.ItemTooltips = (function() {
46
46
  });
47
47
  }
48
48
 
49
- function hasTooltipData(itemData) {
50
- return itemData.name ||
51
- itemData.description ||
52
- itemData.sellPrice !== undefined ||
53
- itemData.buyPrice !== undefined ||
54
- itemData.damageBonus !== undefined ||
55
- itemData.damageBonusPercent !== undefined ||
56
- itemData.damageReduction !== undefined ||
57
- itemData.damageReductionPercent !== undefined ||
58
- itemData.damage !== undefined ||
59
- itemData.damageVariance !== undefined ||
60
- (itemData.statTexts && itemData.statTexts.length > 0) ||
61
- itemData.statsHeader;
62
- }
63
-
64
- function createTooltip(slot, itemData, options = {}) {
65
- if (!hasTooltipData(itemData)) return;
66
-
67
- // Default options
49
+ function generateItemStats(itemData, options = {}) {
68
50
  const config = {
69
- tooltipClass: 'item-tooltip',
70
- contentClass: 'item-tooltip-content',
51
+ includeDescription: true,
52
+ includePricing: false,
71
53
  showBuyPrice: false,
72
- showSellPrice: true,
54
+ showSellPrice: false,
73
55
  buyPriceLabel: 'Price: ',
74
56
  sellPriceLabel: 'Sells For: ',
75
57
  notBuyableLabel: 'Not Buyable',
76
58
  notSellableLabel: 'Not Sellable',
59
+ statsClass: 'item-stats',
60
+ descriptionClass: 'item-description',
61
+ priceClass: 'item-price',
77
62
  ...options
78
63
  };
79
64
 
80
- const tooltip = document.createElement('div');
81
- tooltip.className = config.tooltipClass;
82
-
83
- const content = document.createElement('div');
84
- content.className = config.contentClass;
85
-
86
65
  const sections = [];
87
66
 
88
- // Item name
89
- if (itemData.name) {
90
- sections.push(`<div class="tooltip-name">${itemData.name}</div>`);
91
- }
92
-
93
67
  // Item stats (weapons and wearables)
94
68
  const stats = [];
95
69
 
@@ -139,20 +113,234 @@ window.ItemTooltips = (function() {
139
113
  if (allStatLines.length > 0) {
140
114
  if (itemData.statsHeader) {
141
115
  const headerText = parseText(itemData.statsHeader);
142
- stats.push(`<div class="tooltip-stats-header">${headerText}</div>${allStatLines.join('<br>')}`);
116
+ stats.push(`<div class="stats-header">${headerText}</div>${allStatLines.join('<br>')}`);
143
117
  } else {
144
118
  stats.push(allStatLines.join('<br>'));
145
119
  }
146
120
  }
147
121
 
148
122
  if (stats.length > 0) {
149
- sections.push(`<div class="tooltip-stats">${stats.join('<br>')}</div>`);
123
+ sections.push(`<div class="${config.statsClass}">${stats.join('<br>')}</div>`);
150
124
  }
151
125
 
152
126
  // Item description
153
- if (itemData.description) {
127
+ if (config.includeDescription && itemData.description) {
154
128
  const parsedDescription = parseText(itemData.description);
155
- sections.push(`<div class="tooltip-description">${parsedDescription}</div>`);
129
+ sections.push(`<div class="${config.descriptionClass}">${parsedDescription}</div>`);
130
+ }
131
+
132
+ // Price information
133
+ if (config.includePricing) {
134
+ if (config.showBuyPrice && itemData.buyPrice !== undefined) {
135
+ const price = itemData.buyPrice || 0;
136
+ if (price > 0) {
137
+ sections.push(`<div class="${config.priceClass}">${config.buyPriceLabel}${price.toLocaleString()} gold</div>`);
138
+ } else {
139
+ sections.push(`<div class="${config.priceClass}">${config.notBuyableLabel}</div>`);
140
+ }
141
+ }
142
+
143
+ if (config.showSellPrice && itemData.sellPrice !== undefined) {
144
+ const price = itemData.sellPrice || 0;
145
+ if (price > 0) {
146
+ sections.push(`<div class="${config.priceClass}">${config.sellPriceLabel}${price.toLocaleString()} gold</div>`);
147
+ } else {
148
+ sections.push(`<div class="${config.priceClass}">${config.notSellableLabel}</div>`);
149
+ }
150
+ }
151
+ }
152
+
153
+ return sections.join('');
154
+ }
155
+
156
+ function hasItemStats(itemData) {
157
+ return itemData.description ||
158
+ itemData.sellPrice !== undefined ||
159
+ itemData.buyPrice !== undefined ||
160
+ itemData.damageBonus !== undefined ||
161
+ itemData.damageBonusPercent !== undefined ||
162
+ itemData.damageReduction !== undefined ||
163
+ itemData.damageReductionPercent !== undefined ||
164
+ itemData.damage !== undefined ||
165
+ itemData.damageVariance !== undefined ||
166
+ (itemData.statTexts && itemData.statTexts.length > 0) ||
167
+ itemData.statsHeader;
168
+ }
169
+
170
+ // Public API
171
+ return {
172
+ parseText: parseText,
173
+ generateItemStats: generateItemStats,
174
+ hasItemStats: hasItemStats
175
+ };
176
+ })();
177
+ </script>
178
+
179
+ <style>
180
+ /* Shared Item Stats Styles */
181
+ .item-stats {
182
+ margin-top: 6px;
183
+ font-size: 11px;
184
+ line-height: 1.4;
185
+ color: #7dd87d;
186
+ font-weight: 500;
187
+ }
188
+
189
+ .stats-header {
190
+ color: #999999;
191
+ font-weight: 500;
192
+ margin-bottom: 3px;
193
+ font-size: 11px;
194
+ }
195
+
196
+ .item-description {
197
+ color: #cccccc;
198
+ margin-top: 6px;
199
+ font-size: 11px;
200
+ line-height: 1.4;
201
+ }
202
+
203
+ .item-price {
204
+ color: #ffd700;
205
+ margin-top: 6px;
206
+ font-weight: 500;
207
+ font-size: 11px;
208
+ }
209
+
210
+ /* Crafting-specific stat styles */
211
+ .crafting-item-stats {
212
+ margin-top: 4px;
213
+ font-size: 9px;
214
+ line-height: 1.3;
215
+ color: #7dd87d;
216
+ font-weight: 500;
217
+ }
218
+
219
+ .crafting-item-stats .stats-header {
220
+ color: #999999;
221
+ font-weight: 500;
222
+ margin-bottom: 2px;
223
+ font-size: 9px;
224
+ }
225
+
226
+ .crafting-item-description {
227
+ margin-top: 4px;
228
+ font-size: 9px;
229
+ line-height: 1.3;
230
+ color: var(--crafting-text-muted, #cccccc);
231
+ }
232
+
233
+ /* Mobile styles */
234
+ body.mobile .crafting-item-stats {
235
+ font-size: 8px;
236
+ margin-top: 3px;
237
+ }
238
+
239
+ body.mobile .crafting-item-stats .stats-header {
240
+ font-size: 8px;
241
+ }
242
+
243
+ body.mobile .crafting-item-description {
244
+ font-size: 8px;
245
+ margin-top: 3px;
246
+ }
247
+ </style>
248
+
249
+ <!-- Included from: ./shared/item-tooltips.html -->
250
+ <!-- Shared Item Tooltip System -->
251
+ <script>
252
+ /**
253
+ * Centralized Item Tooltip System
254
+ * Provides consistent tooltip functionality across all UI components
255
+ */
256
+ window.ItemTooltips = (function() {
257
+ 'use strict';
258
+
259
+ // Parse color formatting in text: [HEXCODE]text[/] -> <span style="color: #HEXCODE">text</span>
260
+ // Parse line breaks: [b] -> <br>
261
+ function parseText(text) {
262
+ if (!text) return text;
263
+
264
+ // First, replace [b] with <br> for line breaks
265
+ text = text.replace(/\[b\]/g, '<br>');
266
+
267
+ // Then, handle color formatting [HEXCODE]text[/] pattern
268
+ const colorPattern = /\[([A-Fa-f0-9]{6})\](.*?)\[\/\]/g;
269
+
270
+ return text.replace(colorPattern, (match, hexCode, content) => {
271
+ return `<span style="color: #${hexCode}">${content}</span>`;
272
+ });
273
+ }
274
+
275
+ function hasTooltipData(itemData) {
276
+ // Use shared ItemStats system if available, otherwise use basic check
277
+ if (window.ItemStats) {
278
+ return itemData.name || window.ItemStats.hasItemStats(itemData);
279
+ }
280
+
281
+ // Fallback check if ItemStats not available
282
+ return itemData.name ||
283
+ itemData.description ||
284
+ itemData.sellPrice !== undefined ||
285
+ itemData.buyPrice !== undefined ||
286
+ itemData.damageBonus !== undefined ||
287
+ itemData.damageBonusPercent !== undefined ||
288
+ itemData.damageReduction !== undefined ||
289
+ itemData.damageReductionPercent !== undefined ||
290
+ itemData.damage !== undefined ||
291
+ itemData.damageVariance !== undefined ||
292
+ (itemData.statTexts && itemData.statTexts.length > 0) ||
293
+ itemData.statsHeader;
294
+ }
295
+
296
+ function createTooltip(slot, itemData, options = {}) {
297
+ if (!hasTooltipData(itemData)) return;
298
+
299
+ // Default options
300
+ const config = {
301
+ tooltipClass: 'item-tooltip',
302
+ contentClass: 'item-tooltip-content',
303
+ showBuyPrice: false,
304
+ showSellPrice: true,
305
+ buyPriceLabel: 'Price: ',
306
+ sellPriceLabel: 'Sells For: ',
307
+ notBuyableLabel: 'Not Buyable',
308
+ notSellableLabel: 'Not Sellable',
309
+ ...options
310
+ };
311
+
312
+ const tooltip = document.createElement('div');
313
+ tooltip.className = config.tooltipClass;
314
+
315
+ const content = document.createElement('div');
316
+ content.className = config.contentClass;
317
+
318
+ const sections = [];
319
+
320
+ // Item name
321
+ if (itemData.name) {
322
+ sections.push(`<div class="tooltip-name">${itemData.name}</div>`);
323
+ }
324
+
325
+ // Use shared ItemStats system for consistent stat generation
326
+ if (window.ItemStats) {
327
+ const itemStatsHTML = window.ItemStats.generateItemStats(itemData, {
328
+ includeDescription: true,
329
+ includePricing: false,
330
+ statsClass: 'tooltip-stats',
331
+ descriptionClass: 'tooltip-description',
332
+ priceClass: 'tooltip-value'
333
+ });
334
+
335
+ if (itemStatsHTML) {
336
+ sections.push(itemStatsHTML);
337
+ }
338
+ } else {
339
+ // Fallback: Item description only if ItemStats not available
340
+ if (itemData.description) {
341
+ const parsedDescription = parseText(itemData.description);
342
+ sections.push(`<div class="tooltip-description">${parsedDescription}</div>`);
343
+ }
156
344
  }
157
345
 
158
346
  // Price information
@@ -212,22 +400,31 @@ window.ItemTooltips = (function() {
212
400
  .backpack-slot:hover .item-tooltip,
213
401
  .backpack-hotbar-slot:hover .item-tooltip,
214
402
  .backpack-wearable-slot:hover .item-tooltip,
215
- .merchant-slot:hover .item-tooltip,
216
- .merchant-hotbar-slot:hover .item-tooltip,
217
- /* Tooltip visibility on hover - Specific classes */
218
- .backpack-slot:hover .backpack-item-tooltip,
219
- .backpack-hotbar-slot:hover .backpack-item-tooltip,
220
- .backpack-wearable-slot:hover .backpack-item-tooltip,
221
- .merchant-slot:hover .merchant-item-tooltip,
222
- .merchant-hotbar-slot:hover .merchant-item-tooltip {
403
+ .merchant-slot:hover .item-tooltip,
404
+ .merchant-hotbar-slot:hover .item-tooltip,
405
+ .crafting-slot:hover .item-tooltip,
406
+ .crafting-requirement-item:hover .item-tooltip,
407
+ .quests-reward-item:hover .item-tooltip,
408
+ /* Tooltip visibility on hover - Specific classes */
409
+ .backpack-slot:hover .backpack-item-tooltip,
410
+ .backpack-hotbar-slot:hover .backpack-item-tooltip,
411
+ .backpack-wearable-slot:hover .backpack-item-tooltip,
412
+ .merchant-slot:hover .merchant-item-tooltip,
413
+ .merchant-hotbar-slot:hover .merchant-item-tooltip,
414
+ .crafting-slot:hover .crafting-item-tooltip,
415
+ .crafting-requirement-item:hover .crafting-requirement-tooltip,
416
+ .quests-reward-item:hover .quests-reward-item-tooltip {
223
417
  opacity: 1;
224
418
  visibility: visible;
225
419
  transform: translateY(-8px);
226
420
  }
227
421
 
228
- .item-tooltip-content,
229
- .backpack-item-tooltip-content,
230
- .merchant-item-tooltip-content {
422
+ .item-tooltip-content,
423
+ .backpack-item-tooltip-content,
424
+ .merchant-item-tooltip-content,
425
+ .crafting-item-tooltip-content,
426
+ .crafting-requirement-tooltip-content,
427
+ .quests-reward-item-tooltip-content {
231
428
  background: linear-gradient(145deg, #2a2a2a, #1e1e1e);
232
429
  border: 2px solid #444;
233
430
  border-radius: 8px;
@@ -248,9 +445,12 @@ window.ItemTooltips = (function() {
248
445
  }
249
446
 
250
447
  /* Tooltip Caret */
251
- .item-tooltip-content::after,
252
- .backpack-item-tooltip-content::after,
253
- .merchant-item-tooltip-content::after {
448
+ .item-tooltip-content::after,
449
+ .backpack-item-tooltip-content::after,
450
+ .merchant-item-tooltip-content::after,
451
+ .crafting-item-tooltip-content::after,
452
+ .crafting-requirement-tooltip-content::after,
453
+ .quests-reward-item-tooltip-content::after {
254
454
  content: '';
255
455
  position: absolute;
256
456
  top: 100%;
@@ -260,9 +460,12 @@ window.ItemTooltips = (function() {
260
460
  border-top-color: #444;
261
461
  }
262
462
 
263
- .item-tooltip-content::before,
264
- .backpack-item-tooltip-content::before,
265
- .merchant-item-tooltip-content::before {
463
+ .item-tooltip-content::before,
464
+ .backpack-item-tooltip-content::before,
465
+ .merchant-item-tooltip-content::before,
466
+ .crafting-item-tooltip-content::before,
467
+ .crafting-requirement-tooltip-content::before,
468
+ .quests-reward-item-tooltip-content::before {
266
469
  content: '';
267
470
  position: absolute;
268
471
  top: calc(100% - 2px);
@@ -270,8 +473,17 @@ window.ItemTooltips = (function() {
270
473
  transform: translateX(-50%);
271
474
  border: 6px solid transparent;
272
475
  border-top-color: #2a2a2a;
273
- z-index: 1;
274
- }
476
+ z-index: 1;
477
+ }
478
+
479
+ /* Tooltip Content Sections */
480
+ .tooltip-name,
481
+ .backpack-tooltip-name,
482
+ .merchant-tooltip-name {
483
+ font-weight: 600;
484
+ color: #ffffff;
485
+ font-size: 12px;
486
+ }
275
487
 
276
488
  /* Tooltip Content Sections */
277
489
  .tooltip-name,
@@ -318,7 +530,12 @@ window.ItemTooltips = (function() {
318
530
  }
319
531
 
320
532
  /* Mobile - Hide tooltips on mobile devices */
321
- body.mobile .item-tooltip {
533
+ body.mobile .item-tooltip,
534
+ body.mobile .backpack-item-tooltip,
535
+ body.mobile .merchant-item-tooltip,
536
+ body.mobile .crafting-item-tooltip,
537
+ body.mobile .crafting-requirement-tooltip,
538
+ body.mobile .quests-reward-item-tooltip {
322
539
  display: none;
323
540
  }
324
541
 
@@ -2665,6 +2882,40 @@ window.ItemTooltips = (function() {
2665
2882
  </div>
2666
2883
  </div>
2667
2884
  `;
2885
+
2886
+ // Add tooltips to reward items after rendering
2887
+ addRewardTooltips(quest.reward);
2888
+ }
2889
+
2890
+ function addRewardTooltips(reward) {
2891
+ // Clear any existing tooltips first
2892
+ const existingTooltipItems = document.querySelectorAll('.quests-reward-item-tooltip-enabled');
2893
+ existingTooltipItems.forEach(item => removeRewardTooltip(item));
2894
+
2895
+ // Add tooltips to item rewards
2896
+ if (reward.items) {
2897
+ reward.items.forEach((itemReward, index) => {
2898
+ const rewardItem = document.querySelector(`[data-reward-item-index="${index}"]`);
2899
+ if (rewardItem && window.ItemTooltips) {
2900
+ createRewardTooltip(rewardItem, itemReward);
2901
+ }
2902
+ });
2903
+ }
2904
+ }
2905
+
2906
+ function createRewardTooltip(item, itemData) {
2907
+ const options = {
2908
+ tooltipClass: 'quests-reward-item-tooltip',
2909
+ contentClass: 'quests-reward-item-tooltip-content',
2910
+ showBuyPrice: false,
2911
+ showSellPrice: false
2912
+ };
2913
+
2914
+ ItemTooltips.createTooltip(item, itemData, options);
2915
+ }
2916
+
2917
+ function removeRewardTooltip(item) {
2918
+ ItemTooltips.removeTooltip(item, 'quests-reward-item-tooltip');
2668
2919
  }
2669
2920
 
2670
2921
  function generateObjectivesHTML(quest) {
@@ -2701,11 +2952,11 @@ window.ItemTooltips = (function() {
2701
2952
  });
2702
2953
 
2703
2954
  // Handle item rewards
2704
- reward.items?.forEach(itemReward => {
2955
+ reward.items?.forEach((itemReward, index) => {
2705
2956
  const quantity = itemReward.quantity > 1 ? `x${itemReward.quantity.toLocaleString()}` : 'x1';
2706
2957
  const iconSrc = itemReward.iconImageUri ? `{{CDN_ASSETS_URL}}/${itemReward.iconImageUri}` : '{{CDN_ASSETS_URL}}/icons/items/gold.png';
2707
2958
  rewards.push(`
2708
- <div class="quests-reward-item">
2959
+ <div class="quests-reward-item quests-reward-item-tooltip-enabled" data-reward-item-index="${index}">
2709
2960
  <img src="${iconSrc}" alt="Item" class="quests-reward-icon">
2710
2961
  <span class="quests-reward-text">${sanitizeText(itemReward.name)} (${quantity})</span>
2711
2962
  </div>
@@ -3125,6 +3376,40 @@ window.ItemTooltips = (function() {
3125
3376
  line-height: 1.3;
3126
3377
  }
3127
3378
 
3379
+ /* Quest reward tooltip positioning - left-aligned for full-width items */
3380
+ .quests-reward-item-tooltip {
3381
+ position: absolute;
3382
+ bottom: 100%;
3383
+ left: 0;
3384
+ margin-bottom: 8px;
3385
+ opacity: 0;
3386
+ visibility: hidden;
3387
+ transition: all 0.3s ease;
3388
+ z-index: 2000;
3389
+ pointer-events: none;
3390
+ }
3391
+
3392
+ .quests-reward-item-tooltip-enabled {
3393
+ position: relative;
3394
+ cursor: pointer;
3395
+ }
3396
+
3397
+ /* Override center positioning for quest reward tooltips */
3398
+ .quests-reward-item-tooltip-content {
3399
+ transform: translateX(0) !important;
3400
+ }
3401
+
3402
+ /* Position caret over the reward icon */
3403
+ .quests-reward-item-tooltip-content::after {
3404
+ left: 20px !important;
3405
+ transform: translateX(-50%) !important;
3406
+ }
3407
+
3408
+ .quests-reward-item-tooltip-content::before {
3409
+ left: 20px !important;
3410
+ transform: translateX(-50%) !important;
3411
+ }
3412
+
3128
3413
  /* Empty State */
3129
3414
  .quests-empty-state {
3130
3415
  flex: 1;
@@ -4392,6 +4677,985 @@ window.ItemTooltips = (function() {
4392
4677
  }
4393
4678
  </style>
4394
4679
 
4680
+ <!-- Included from: ./menus/crafting.html -->
4681
+ <!-- Crafting UI -->
4682
+ <div class="crafting-overlay">
4683
+ <div class="crafting-container">
4684
+ <div class="crafting-header">
4685
+ <div class="crafting-info">
4686
+ <img src="" alt="Crafter" class="crafting-avatar">
4687
+ <div class="crafting-details">
4688
+ <div class="crafting-name"></div>
4689
+ <div class="crafting-title"></div>
4690
+ </div>
4691
+ </div>
4692
+ <button class="crafting-close">×</button>
4693
+ </div>
4694
+
4695
+ <div class="crafting-content">
4696
+ <!-- Left Panel - Craftable Items Grid -->
4697
+ <div class="crafting-grid-container">
4698
+ <div class="crafting-grid" id="crafting-grid">
4699
+ <!-- Grid slots will be generated by JavaScript -->
4700
+ </div>
4701
+ </div>
4702
+
4703
+ <!-- Right Panel - Crafting Details & Requirements -->
4704
+ <div class="crafting-right-panel">
4705
+ <div class="crafting-panel-content">
4706
+ <div class="crafting-placeholder">
4707
+ Select an item to view its requirements and craft it.
4708
+ </div>
4709
+ </div>
4710
+ </div>
4711
+ </div>
4712
+ </div>
4713
+ </div>
4714
+
4715
+ <script>
4716
+ // Configuration
4717
+ const CRAFTING_CONFIG = {
4718
+ slots: 12,
4719
+ rowWidth: 4,
4720
+ minRows: 3
4721
+ };
4722
+
4723
+ // State management
4724
+ const craftingState = {
4725
+ craftingRecipes: [],
4726
+ selectedRecipe: null
4727
+ };
4728
+
4729
+ // Setup hytopia data handlers
4730
+ hytopia.onData(data => {
4731
+ if (data.type === 'toggleCrafting') {
4732
+ toggleCrafting(data);
4733
+ }
4734
+ });
4735
+
4736
+ // Initialize crafting system
4737
+ function initializeCrafting() {
4738
+ createCraftingSlots();
4739
+
4740
+ // Add close button handler
4741
+ const closeButton = document.querySelector('.crafting-close');
4742
+ if (closeButton) {
4743
+ closeButton.addEventListener('click', () => toggleCrafting(null));
4744
+ }
4745
+ }
4746
+
4747
+ function createCraftingSlots() {
4748
+ const grid = document.getElementById('crafting-grid');
4749
+ grid.innerHTML = '';
4750
+
4751
+ for (let i = 0; i < CRAFTING_CONFIG.slots; i++) {
4752
+ const slot = document.createElement('div');
4753
+ slot.className = 'crafting-slot';
4754
+ slot.dataset.slotIndex = i;
4755
+ slot.innerHTML = '<div class="crafting-slot-content"></div>';
4756
+ grid.appendChild(slot);
4757
+ }
4758
+ }
4759
+
4760
+ // Use shared tooltip system
4761
+ function createTooltip(slot, itemData) {
4762
+ const options = {
4763
+ tooltipClass: 'crafting-item-tooltip',
4764
+ contentClass: 'crafting-item-tooltip-content',
4765
+ showBuyPrice: false,
4766
+ showSellPrice: false,
4767
+ showCraftable: true
4768
+ };
4769
+
4770
+ ItemTooltips.createTooltip(slot, itemData, options);
4771
+ }
4772
+
4773
+ function removeTooltip(slot) {
4774
+ ItemTooltips.removeTooltip(slot, 'crafting-item-tooltip');
4775
+ }
4776
+
4777
+ // Create tooltip for requirement items
4778
+ function createRequirementTooltip(item, requirementData) {
4779
+ const options = {
4780
+ tooltipClass: 'crafting-requirement-tooltip',
4781
+ contentClass: 'crafting-requirement-tooltip-content',
4782
+ showBuyPrice: false,
4783
+ showSellPrice: false
4784
+ };
4785
+
4786
+ ItemTooltips.createTooltip(item, requirementData, options);
4787
+ }
4788
+
4789
+ function removeRequirementTooltip(item) {
4790
+ ItemTooltips.removeTooltip(item, 'crafting-requirement-tooltip');
4791
+ }
4792
+
4793
+ function createItemContent(content, itemData) {
4794
+ // Create item icon
4795
+ const icon = document.createElement('img');
4796
+ icon.src = `{{CDN_ASSETS_URL}}/${itemData.iconImageUri}`;
4797
+ icon.alt = 'Item';
4798
+ icon.className = 'crafting-item-icon';
4799
+ content.appendChild(icon);
4800
+
4801
+ // Add quantity if greater than 1
4802
+ if (itemData.quantity && itemData.quantity > 1) {
4803
+ const quantity = document.createElement('div');
4804
+ quantity.className = 'crafting-slot-quantity';
4805
+ quantity.textContent = itemData.quantity.toLocaleString();
4806
+ content.appendChild(quantity);
4807
+ }
4808
+ }
4809
+
4810
+ // Slot update functions
4811
+ function updateCraftingSlot(position, recipeData) {
4812
+ if (position < 0 || position >= CRAFTING_CONFIG.slots) return;
4813
+
4814
+ const slots = document.querySelectorAll('.crafting-slot');
4815
+ const slot = slots[position];
4816
+ if (!slot) return;
4817
+
4818
+ const content = slot.querySelector('.crafting-slot-content');
4819
+ if (!content) return;
4820
+
4821
+ // Clear existing content and tooltip
4822
+ content.innerHTML = '';
4823
+ removeTooltip(slot);
4824
+
4825
+ // Remove previous click handler
4826
+ slot.onclick = null;
4827
+ slot.classList.remove('crafting-slot-selected');
4828
+
4829
+ if (!recipeData || recipeData.removed) {
4830
+ // Empty slot
4831
+ return;
4832
+ }
4833
+
4834
+ const craftedItem = recipeData.craftedItem;
4835
+ createItemContent(content, craftedItem);
4836
+ createTooltip(slot, craftedItem);
4837
+
4838
+ // Add click handler for recipe selection
4839
+ slot.onclick = () => selectCraftingRecipe(recipeData, position);
4840
+ }
4841
+
4842
+ // Helper functions for crafting management
4843
+ function updateCrafterInfo(crafterName, crafterTitle, crafterAvatarUri) {
4844
+ const avatar = document.querySelector('.crafting-avatar');
4845
+ const name = document.querySelector('.crafting-name');
4846
+ const title = document.querySelector('.crafting-title');
4847
+
4848
+ if (avatar && crafterAvatarUri) {
4849
+ avatar.src = `{{CDN_ASSETS_URL}}/${crafterAvatarUri}`;
4850
+ }
4851
+ if (name) {
4852
+ name.textContent = crafterName || 'Crafter';
4853
+ }
4854
+ if (title) {
4855
+ title.textContent = crafterTitle || '';
4856
+ }
4857
+ }
4858
+
4859
+ function setCraftingVisibility(visible) {
4860
+ const overlay = document.querySelector('.crafting-overlay');
4861
+ if (overlay) {
4862
+ overlay.style.display = visible ? 'flex' : 'none';
4863
+ }
4864
+ }
4865
+
4866
+ function updateCraftingGrid(rows) {
4867
+ CRAFTING_CONFIG.slots = rows * CRAFTING_CONFIG.rowWidth;
4868
+ createCraftingSlots();
4869
+
4870
+ const craftingGrid = document.getElementById('crafting-grid');
4871
+ if (craftingGrid) {
4872
+ craftingGrid.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
4873
+ }
4874
+ }
4875
+
4876
+ function populateSlots(items, maxSlots, updateFunction) {
4877
+ for (let i = 0; i < maxSlots; i++) {
4878
+ const item = items[i];
4879
+ updateFunction(i, item || { removed: true });
4880
+ }
4881
+ }
4882
+
4883
+ function calculateRequiredRows(craftingRecipes) {
4884
+ if (!craftingRecipes || craftingRecipes.length === 0) return CRAFTING_CONFIG.minRows;
4885
+
4886
+ const positions = craftingRecipes
4887
+ .map(recipe => recipe.position)
4888
+ .filter(pos => typeof pos === 'number');
4889
+
4890
+ if (positions.length === 0) return CRAFTING_CONFIG.minRows;
4891
+
4892
+ const maxPosition = Math.max(11, Math.max(...positions)); // At least 12 slots (0-11)
4893
+ return Math.ceil((maxPosition + 1) / CRAFTING_CONFIG.rowWidth);
4894
+ }
4895
+
4896
+ function populateCraftingRecipes(craftingRecipes) {
4897
+ if (!craftingRecipes || craftingRecipes.length === 0) return;
4898
+
4899
+ craftingRecipes.forEach(recipe => {
4900
+ if (typeof recipe.position === 'number' &&
4901
+ recipe.position >= 0 &&
4902
+ recipe.position < CRAFTING_CONFIG.slots) {
4903
+ updateCraftingSlot(recipe.position, recipe);
4904
+ }
4905
+ });
4906
+ }
4907
+
4908
+ function updateRightPanelContent() {
4909
+ const placeholder = document.querySelector('.crafting-panel-content');
4910
+ if (!placeholder) return;
4911
+
4912
+ if (craftingState.selectedRecipe) {
4913
+ renderCraftingDetails();
4914
+ } else {
4915
+ placeholder.innerHTML = `
4916
+ <div class="crafting-placeholder">
4917
+ Select a craftable item to view its requirements and craft it.
4918
+ </div>
4919
+ `;
4920
+ }
4921
+ }
4922
+
4923
+ function selectCraftingRecipe(recipeData, position) {
4924
+ // Clear previous selection highlighting
4925
+ document.querySelectorAll('.crafting-slot-selected').forEach(slot => {
4926
+ slot.classList.remove('crafting-slot-selected');
4927
+ });
4928
+
4929
+ craftingState.selectedRecipe = {
4930
+ ...recipeData,
4931
+ position
4932
+ };
4933
+
4934
+ // Highlight the selected slot
4935
+ const slots = document.querySelectorAll('.crafting-slot');
4936
+ const selectedSlot = slots[position];
4937
+ if (selectedSlot) {
4938
+ selectedSlot.classList.add('crafting-slot-selected');
4939
+ }
4940
+
4941
+ updateRightPanelContent();
4942
+ }
4943
+
4944
+ function clearSelection() {
4945
+ // Clear previous selection highlighting
4946
+ document.querySelectorAll('.crafting-slot-selected').forEach(slot => {
4947
+ slot.classList.remove('crafting-slot-selected');
4948
+ });
4949
+
4950
+ craftingState.selectedRecipe = null;
4951
+ updateRightPanelContent();
4952
+ }
4953
+
4954
+ function renderCraftingDetails() {
4955
+ const placeholder = document.querySelector('.crafting-panel-content');
4956
+ if (!placeholder || !craftingState.selectedRecipe) return;
4957
+
4958
+ // Clear any existing requirement tooltips
4959
+ const existingRequirements = placeholder.querySelectorAll('.crafting-requirement-item');
4960
+ existingRequirements.forEach(item => removeRequirementTooltip(item));
4961
+
4962
+ const recipe = craftingState.selectedRecipe;
4963
+ const craftedItem = recipe.craftedItem;
4964
+ const requirements = recipe.requirements || [];
4965
+
4966
+ // Generate item stats using the shared system
4967
+ const itemStatsHTML = window.ItemStats ?
4968
+ window.ItemStats.generateItemStats(craftedItem, {
4969
+ includeDescription: true,
4970
+ includePricing: false,
4971
+ statsClass: 'crafting-item-stats',
4972
+ descriptionClass: 'crafting-item-description'
4973
+ }) : '';
4974
+
4975
+ placeholder.innerHTML = `
4976
+ <div class="crafting-item-details">
4977
+ <div class="crafting-item-header">
4978
+ <img src="{{CDN_ASSETS_URL}}/${craftedItem.iconImageUri}" alt="${craftedItem.name}" class="crafting-item-icon">
4979
+ <div class="crafting-item-info">
4980
+ <div class="crafting-item-name">${craftedItem.name}</div>
4981
+ ${itemStatsHTML}
4982
+ </div>
4983
+ </div>
4984
+
4985
+ <div class="crafting-requirements">
4986
+ <div class="crafting-requirements-title">Requirements:</div>
4987
+ <div class="crafting-requirements-list">
4988
+ ${requirements.length > 0 ?
4989
+ requirements.map((req, index) => `
4990
+ <div class="crafting-requirement-item" data-requirement-index="${index}">
4991
+ <img src="{{CDN_ASSETS_URL}}/${req.iconImageUri}" alt="${req.name}" class="crafting-requirement-icon">
4992
+ <div class="crafting-requirement-info">
4993
+ <div class="crafting-requirement-name">${req.name}</div>
4994
+ <div class="crafting-requirement-quantity">x${req.quantity || 1}</div>
4995
+ </div>
4996
+ </div>
4997
+ `).join('') :
4998
+ '<div class="crafting-no-requirements">No materials required</div>'
4999
+ }
5000
+ </div>
5001
+ </div>
5002
+
5003
+ <div class="crafting-item-actions">
5004
+ <button class="crafting-action-btn crafting-confirm-action">Craft</button>
5005
+ </div>
5006
+ </div>
5007
+ `;
5008
+
5009
+ // Add event listener
5010
+ const confirmBtn = placeholder.querySelector('.crafting-confirm-action');
5011
+
5012
+ if (confirmBtn) {
5013
+ confirmBtn.addEventListener('click', confirmCrafting);
5014
+ }
5015
+
5016
+ // Add tooltips to requirement items
5017
+ const requirementItems = placeholder.querySelectorAll('.crafting-requirement-item');
5018
+ requirementItems.forEach((item, index) => {
5019
+ if (requirements[index]) {
5020
+ createRequirementTooltip(item, requirements[index]);
5021
+ }
5022
+ });
5023
+ }
5024
+
5025
+ function confirmCrafting() {
5026
+ if (!craftingState.selectedRecipe) return;
5027
+
5028
+ const actionData = {
5029
+ type: 'craftItem',
5030
+ recipeIndex: craftingState.selectedRecipe.position,
5031
+ quantity: 1
5032
+ };
5033
+
5034
+ hytopia.sendData(actionData);
5035
+ clearSelection();
5036
+ }
5037
+
5038
+ function toggleCrafting(data) {
5039
+ if (!data) {
5040
+ setCraftingVisibility(false);
5041
+ clearSelection();
5042
+ hytopia.lockPointer(true);
5043
+ return;
5044
+ }
5045
+
5046
+ const { craftingRecipes, crafterName, crafterTitle, crafterAvatarUri } = data;
5047
+ craftingState.craftingRecipes = craftingRecipes || [];
5048
+ clearSelection(); // Clear selection when opening
5049
+
5050
+ // Update crafter info
5051
+ updateCrafterInfo(crafterName, crafterTitle, crafterAvatarUri);
5052
+
5053
+ setCraftingVisibility(true);
5054
+
5055
+ const requiredRows = calculateRequiredRows(craftingRecipes);
5056
+ updateCraftingGrid(requiredRows);
5057
+ populateCraftingRecipes(craftingRecipes);
5058
+
5059
+ hytopia.lockPointer(false, true);
5060
+ }
5061
+
5062
+ // Initialize on load
5063
+ initializeCrafting();
5064
+ </script>
5065
+
5066
+ <style>
5067
+ :root {
5068
+ --crafting-slot-size: 56px;
5069
+ --crafting-icon-size: 32px;
5070
+ --crafting-gap: 6px;
5071
+ --crafting-padding: 12px;
5072
+ --crafting-font: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
5073
+
5074
+ /* Color palette - matching merchant */
5075
+ --crafting-bg-primary: linear-gradient(145deg, #2a2a2a, #1e1e1e);
5076
+ --crafting-bg-secondary: linear-gradient(135deg, #3a3a3a, #2d2d2d);
5077
+ --crafting-border: #444;
5078
+ --crafting-border-light: #555;
5079
+ --crafting-text: #ffffff;
5080
+ --crafting-text-muted: #cccccc;
5081
+ --crafting-text-dim: #888;
5082
+ --crafting-overlay: rgba(0, 0, 0, 0.3);
5083
+ --crafting-shadow: 0 8px 32px rgba(0, 0, 0, 0.9);
5084
+ --crafting-shadow-inset: inset 0 2px 8px rgba(0, 0, 0, 0.5);
5085
+ --crafting-accent: #4CAF50;
5086
+ --crafting-accent-hover: #66BB6A;
5087
+ }
5088
+
5089
+ .crafting-overlay {
5090
+ position: fixed;
5091
+ inset: 0;
5092
+ background: rgba(0, 0, 0, 0.8);
5093
+ display: none;
5094
+ flex-direction: column;
5095
+ align-items: center;
5096
+ justify-content: center;
5097
+ z-index: 1000;
5098
+ font-family: var(--crafting-font);
5099
+ user-select: none;
5100
+ }
5101
+
5102
+ /* Container styles */
5103
+ .crafting-container {
5104
+ background: var(--crafting-bg-primary);
5105
+ border: 2px solid var(--crafting-border);
5106
+ border-radius: 12px;
5107
+ box-shadow: var(--crafting-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.1);
5108
+ width: 680px;
5109
+ }
5110
+
5111
+ /* Header styles */
5112
+ .crafting-header {
5113
+ display: flex;
5114
+ justify-content: space-between;
5115
+ align-items: center;
5116
+ padding: 16px 20px;
5117
+ background: var(--crafting-bg-secondary);
5118
+ border-bottom: 1px solid var(--crafting-border);
5119
+ }
5120
+
5121
+ .crafting-info {
5122
+ display: flex;
5123
+ align-items: center;
5124
+ gap: 12px;
5125
+ flex: 1;
5126
+ min-width: 0;
5127
+ }
5128
+
5129
+ .crafting-avatar {
5130
+ width: 48px;
5131
+ height: 48px;
5132
+ border: 2px solid var(--crafting-border);
5133
+ border-radius: 6px;
5134
+ object-fit: cover;
5135
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
5136
+ flex-shrink: 0;
5137
+ }
5138
+
5139
+ .crafting-details {
5140
+ display: flex;
5141
+ flex-direction: column;
5142
+ min-width: 0;
5143
+ flex: 1;
5144
+ }
5145
+
5146
+ .crafting-name {
5147
+ font-size: 16px;
5148
+ font-weight: 600;
5149
+ color: var(--crafting-text);
5150
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
5151
+ line-height: 1;
5152
+ white-space: nowrap;
5153
+ overflow: hidden;
5154
+ text-overflow: ellipsis;
5155
+ }
5156
+
5157
+ .crafting-title {
5158
+ font-size: 14px;
5159
+ font-weight: 500;
5160
+ color: var(--crafting-text-muted);
5161
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
5162
+ margin-top: 2px;
5163
+ white-space: nowrap;
5164
+ overflow: hidden;
5165
+ text-overflow: ellipsis;
5166
+ letter-spacing: 0.3px;
5167
+ }
5168
+
5169
+ .crafting-close {
5170
+ background: rgba(255, 0, 0, 0.2);
5171
+ border: 1px solid rgba(255, 0, 0, 0.3);
5172
+ border-radius: 6px;
5173
+ color: #ff6b6b;
5174
+ width: 24px;
5175
+ height: 24px;
5176
+ display: flex;
5177
+ align-items: center;
5178
+ justify-content: center;
5179
+ font-size: 16px;
5180
+ font-weight: bold;
5181
+ cursor: pointer;
5182
+ transition: all 0.2s ease;
5183
+ }
5184
+
5185
+ .crafting-close:hover {
5186
+ background: rgba(255, 0, 0, 0.3);
5187
+ transform: scale(1.05);
5188
+ }
5189
+
5190
+ /* Layout - 50/50 split */
5191
+ .crafting-content {
5192
+ padding: 20px;
5193
+ display: flex;
5194
+ gap: 20px;
5195
+ align-items: flex-start;
5196
+ }
5197
+
5198
+ .crafting-grid-container {
5199
+ flex: 1;
5200
+ }
5201
+
5202
+ .crafting-right-panel {
5203
+ flex: 1;
5204
+ background: var(--crafting-overlay);
5205
+ border: 1px solid var(--crafting-border);
5206
+ border-radius: 8px;
5207
+ padding: var(--crafting-padding);
5208
+ min-height: 192px;
5209
+ display: flex;
5210
+ align-items: center;
5211
+ justify-content: center;
5212
+ }
5213
+
5214
+ .crafting-panel-content {
5215
+ width: 100%;
5216
+ }
5217
+
5218
+ .crafting-placeholder {
5219
+ color: #666;
5220
+ font-size: 14px;
5221
+ text-align: center;
5222
+ line-height: 1.5;
5223
+ }
5224
+
5225
+ /* Grid styles */
5226
+ .crafting-grid {
5227
+ display: grid;
5228
+ grid-template: repeat(3, 1fr) / repeat(4, 1fr);
5229
+ gap: var(--crafting-gap);
5230
+ background: var(--crafting-overlay);
5231
+ padding: var(--crafting-padding);
5232
+ border: 1px solid var(--crafting-border);
5233
+ border-radius: 8px;
5234
+ box-shadow: var(--crafting-shadow-inset);
5235
+ }
5236
+
5237
+ /* Slot styles */
5238
+ .crafting-slot {
5239
+ background: rgba(0, 0, 0, 0.6);
5240
+ border: 2px solid #333;
5241
+ border-radius: 6px;
5242
+ display: flex;
5243
+ align-items: center;
5244
+ justify-content: center;
5245
+ position: relative;
5246
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.7);
5247
+ transition: all 0.2s ease;
5248
+ cursor: pointer;
5249
+ width: var(--crafting-slot-size);
5250
+ height: var(--crafting-slot-size);
5251
+ }
5252
+
5253
+ .crafting-slot:hover {
5254
+ border-color: var(--crafting-border-light);
5255
+ background: rgba(0, 0, 0, 0.8);
5256
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.7), 0 2px 8px rgba(0, 0, 0, 0.5);
5257
+ transform: translateY(-1px);
5258
+ }
5259
+
5260
+ .crafting-slot-selected {
5261
+ border-color: #4CAF50 !important;
5262
+ background: rgba(76, 175, 80, 0.1) !important;
5263
+ box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.3) !important;
5264
+ }
5265
+
5266
+ .crafting-slot-content {
5267
+ width: 100%;
5268
+ height: 100%;
5269
+ display: flex;
5270
+ align-items: center;
5271
+ justify-content: center;
5272
+ }
5273
+
5274
+ .crafting-item-icon {
5275
+ width: 48px;
5276
+ height: 48px;
5277
+ object-fit: contain;
5278
+ image-rendering: pixelated;
5279
+ filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.8));
5280
+ }
5281
+
5282
+ /* Quantity badges */
5283
+ .crafting-slot-quantity {
5284
+ position: absolute;
5285
+ bottom: 2px;
5286
+ right: 2px;
5287
+ background: rgba(0, 0, 0, 0.8);
5288
+ color: var(--crafting-text);
5289
+ font-weight: 700;
5290
+ padding: 1px 3px;
5291
+ border-radius: 3px;
5292
+ line-height: 1;
5293
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.9);
5294
+ border: 1px solid rgba(255, 255, 255, 0.2);
5295
+ min-width: 10px;
5296
+ text-align: center;
5297
+ font-size: 9px;
5298
+ }
5299
+
5300
+ /* Item Details Panel */
5301
+ .crafting-item-details {
5302
+ width: 100%;
5303
+ display: flex;
5304
+ flex-direction: column;
5305
+ gap: 10px;
5306
+ }
5307
+
5308
+ .crafting-item-header {
5309
+ display: flex;
5310
+ flex-direction: row;
5311
+ align-items: flex-start;
5312
+ gap: 8px;
5313
+ padding: 8px;
5314
+ background: rgba(0, 0, 0, 0.2);
5315
+ border: 1px solid var(--crafting-border);
5316
+ border-radius: 6px;
5317
+ margin-bottom: 8px;
5318
+ }
5319
+
5320
+ .crafting-item-header .crafting-item-icon {
5321
+ width: 32px;
5322
+ height: 32px;
5323
+ border-radius: 4px;
5324
+ background: rgba(0, 0, 0, 0.4);
5325
+ padding: 2px;
5326
+ flex-shrink: 0;
5327
+ }
5328
+
5329
+ .crafting-item-info {
5330
+ display: flex;
5331
+ flex-direction: column;
5332
+ align-items: flex-start;
5333
+ gap: 2px;
5334
+ flex: 1;
5335
+ min-width: 0;
5336
+ }
5337
+
5338
+ .crafting-item-name {
5339
+ font-size: 12px;
5340
+ font-weight: 500;
5341
+ color: var(--crafting-text);
5342
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
5343
+ text-align: left;
5344
+ width: 100%;
5345
+ line-height: 1.2;
5346
+ word-wrap: break-word;
5347
+ word-break: break-word;
5348
+ hyphens: auto;
5349
+ }
5350
+
5351
+ /* Requirements section */
5352
+ .crafting-requirements {
5353
+ display: flex;
5354
+ flex-direction: column;
5355
+ gap: 8px;
5356
+ }
5357
+
5358
+ .crafting-requirements-title {
5359
+ font-size: 12px;
5360
+ font-weight: 600;
5361
+ color: var(--crafting-text);
5362
+ margin-bottom: 4px;
5363
+ }
5364
+
5365
+ .crafting-requirements-list {
5366
+ display: grid;
5367
+ grid-template-columns: repeat(3, 1fr);
5368
+ gap: 6px;
5369
+ }
5370
+
5371
+ .crafting-requirement-item {
5372
+ display: flex;
5373
+ flex-direction: row;
5374
+ align-items: center;
5375
+ gap: 6px;
5376
+ padding: 4px 6px;
5377
+ background: rgba(0, 0, 0, 0.3);
5378
+ border: 1px solid var(--crafting-border);
5379
+ border-radius: 6px;
5380
+ min-height: 32px;
5381
+ position: relative;
5382
+ cursor: pointer;
5383
+ transition: all 0.2s ease;
5384
+ }
5385
+
5386
+ .crafting-requirement-item:hover {
5387
+ background: rgba(0, 0, 0, 0.5);
5388
+ border-color: var(--crafting-border-light);
5389
+ transform: translateY(-1px);
5390
+ }
5391
+
5392
+ .crafting-requirement-icon {
5393
+ width: 20px;
5394
+ height: 20px;
5395
+ object-fit: contain;
5396
+ image-rendering: pixelated;
5397
+ border-radius: 3px;
5398
+ background: rgba(0, 0, 0, 0.4);
5399
+ padding: 1px;
5400
+ flex-shrink: 0;
5401
+ }
5402
+
5403
+ .crafting-requirement-info {
5404
+ display: flex;
5405
+ flex-direction: column;
5406
+ align-items: flex-start;
5407
+ gap: 1px;
5408
+ min-width: 0;
5409
+ flex: 1;
5410
+ }
5411
+
5412
+ .crafting-requirement-name {
5413
+ font-size: 10px;
5414
+ color: var(--crafting-text);
5415
+ font-weight: 500;
5416
+ line-height: 1.2;
5417
+ text-align: left;
5418
+ width: 100%;
5419
+ overflow: hidden;
5420
+ text-overflow: ellipsis;
5421
+ white-space: nowrap;
5422
+ }
5423
+
5424
+ .crafting-requirement-quantity {
5425
+ font-size: 9px;
5426
+ color: var(--crafting-text-muted);
5427
+ font-weight: 600;
5428
+ padding: 1px 3px;
5429
+ border-radius: 2px;
5430
+ line-height: 1;
5431
+ }
5432
+
5433
+ .crafting-no-requirements {
5434
+ color: var(--crafting-text-dim);
5435
+ font-size: 12px;
5436
+ text-align: center;
5437
+ padding: 16px;
5438
+ font-style: italic;
5439
+ grid-column: 1 / -1;
5440
+ background: rgba(0, 0, 0, 0.2);
5441
+ border: 1px dashed var(--crafting-border);
5442
+ border-radius: 6px;
5443
+ }
5444
+
5445
+ /* Actions section */
5446
+ .crafting-item-actions {
5447
+ display: flex;
5448
+ align-items: center;
5449
+ justify-content: center;
5450
+ padding-top: 8px;
5451
+ }
5452
+
5453
+ .crafting-action-btn {
5454
+ padding: 8px 16px;
5455
+ border: 1px solid #4CAF50;
5456
+ border-radius: 4px;
5457
+ background: rgba(76, 175, 80, 0.2);
5458
+ color: #4CAF50;
5459
+ font-size: 12px;
5460
+ font-weight: 600;
5461
+ cursor: pointer;
5462
+ transition: all 0.2s ease;
5463
+ text-transform: uppercase;
5464
+ letter-spacing: 0.3px;
5465
+ }
5466
+
5467
+ .crafting-action-btn:hover {
5468
+ background: rgba(76, 175, 80, 0.3);
5469
+ color: #66BB6A;
5470
+ }
5471
+
5472
+ /* Disabled button styles */
5473
+ .crafting-action-btn:disabled {
5474
+ opacity: 0.5;
5475
+ cursor: not-allowed;
5476
+ pointer-events: none;
5477
+ }
5478
+
5479
+ /* Item rarity colors */
5480
+ .crafting-slot.crafting-common { border-color: #9CA3AF; }
5481
+ .crafting-slot.crafting-uncommon { border-color: #22C55E; }
5482
+ .crafting-slot.crafting-rare { border-color: #3B82F6; }
5483
+ .crafting-slot.crafting-epic { border-color: #A855F7; }
5484
+ .crafting-slot.crafting-legendary { border-color: #F59E0B; }
5485
+
5486
+ /* Crafting-specific tooltip positioning (content handled by shared system) */
5487
+ .crafting-item-tooltip {
5488
+ position: absolute;
5489
+ bottom: 100%;
5490
+ left: 50%;
5491
+ margin-bottom: 8px;
5492
+ opacity: 0;
5493
+ visibility: hidden;
5494
+ transition: all 0.3s ease;
5495
+ z-index: 2000;
5496
+ pointer-events: none;
5497
+ }
5498
+
5499
+ .crafting-requirement-tooltip {
5500
+ position: absolute;
5501
+ bottom: 100%;
5502
+ left: 50%;
5503
+ margin-bottom: 8px;
5504
+ opacity: 0;
5505
+ visibility: hidden;
5506
+ transition: all 0.3s ease;
5507
+ z-index: 2000;
5508
+ pointer-events: none;
5509
+ }
5510
+
5511
+
5512
+
5513
+ /* Mobile responsive */
5514
+ @media (max-width: 768px) {
5515
+ body.mobile {
5516
+ --crafting-slot-size: 40px;
5517
+ --crafting-icon-size: 20px;
5518
+ --crafting-gap: 3px;
5519
+ --crafting-padding: 6px;
5520
+ }
5521
+
5522
+ body.mobile .crafting-container {
5523
+ width: auto;
5524
+ max-width: 500px;
5525
+ min-width: 320px;
5526
+ }
5527
+
5528
+ body.mobile .crafting-content {
5529
+ padding: 6px;
5530
+ flex-direction: column;
5531
+ gap: 8px;
5532
+ }
5533
+
5534
+ body.mobile .crafting-grid {
5535
+ justify-items: center;
5536
+ width: fit-content;
5537
+ margin: 0 auto;
5538
+ }
5539
+
5540
+ body.mobile .crafting-right-panel {
5541
+ width: auto;
5542
+ flex: 1;
5543
+ min-height: 80px;
5544
+ padding: var(--crafting-padding);
5545
+ }
5546
+
5547
+ body.mobile .crafting-panel-content {
5548
+ padding: 8px;
5549
+ font-size: 12px;
5550
+ }
5551
+
5552
+ body.mobile .crafting-placeholder {
5553
+ font-size: 12px;
5554
+ padding: 16px;
5555
+ }
5556
+
5557
+
5558
+
5559
+ body.mobile .crafting-header {
5560
+ padding: 4px 8px;
5561
+ }
5562
+
5563
+ body.mobile .crafting-info {
5564
+ gap: 8px;
5565
+ }
5566
+
5567
+ body.mobile .crafting-avatar {
5568
+ width: 32px;
5569
+ height: 32px;
5570
+ }
5571
+
5572
+ body.mobile .crafting-name {
5573
+ font-size: 12px;
5574
+ }
5575
+
5576
+ body.mobile .crafting-title {
5577
+ font-size: 10px;
5578
+ }
5579
+
5580
+ body.mobile .crafting-item-details {
5581
+ gap: 6px;
5582
+ }
5583
+
5584
+ body.mobile .crafting-item-header {
5585
+ gap: 6px;
5586
+ padding: 6px;
5587
+ margin-bottom: 6px;
5588
+ }
5589
+
5590
+ body.mobile .crafting-item-header .crafting-item-icon {
5591
+ width: 24px;
5592
+ height: 24px;
5593
+ }
5594
+
5595
+ body.mobile .crafting-item-name {
5596
+ font-size: 10px;
5597
+ }
5598
+
5599
+ body.mobile .crafting-requirements-title {
5600
+ font-size: 10px;
5601
+ }
5602
+
5603
+ body.mobile .crafting-requirements-list {
5604
+ grid-template-columns: repeat(2, 1fr);
5605
+ gap: 3px;
5606
+ }
5607
+
5608
+ body.mobile .crafting-requirement-item {
5609
+ padding: 3px 4px;
5610
+ min-height: 28px;
5611
+ gap: 4px;
5612
+ }
5613
+
5614
+ body.mobile .crafting-requirement-icon {
5615
+ width: 16px;
5616
+ height: 16px;
5617
+ }
5618
+
5619
+ body.mobile .crafting-requirement-name {
5620
+ font-size: 9px;
5621
+ }
5622
+
5623
+ body.mobile .crafting-requirement-quantity {
5624
+ font-size: 8px;
5625
+ padding: 1px 2px;
5626
+ }
5627
+
5628
+ body.mobile .crafting-no-requirements {
5629
+ font-size: 10px;
5630
+ padding: 12px;
5631
+ }
5632
+
5633
+ body.mobile .crafting-item-actions {
5634
+ padding-top: 6px;
5635
+ }
5636
+
5637
+ body.mobile .crafting-action-btn {
5638
+ padding: 6px 12px;
5639
+ font-size: 10px;
5640
+ }
5641
+
5642
+ body.mobile .crafting-item-stats {
5643
+ font-size: 8px;
5644
+ margin-top: 3px;
5645
+ }
5646
+
5647
+ body.mobile .crafting-item-stats .stats-header {
5648
+ font-size: 8px;
5649
+ }
5650
+
5651
+ body.mobile .crafting-item-description {
5652
+ font-size: 8px;
5653
+ margin-top: 3px;
5654
+ }
5655
+ }
5656
+ </style>
5657
+
5658
+
4395
5659
  <!-- Included from: ./menus/help.html -->
4396
5660
  <!-- Help UI -->
4397
5661
  <div class="help-overlay">