diff --git a/pwa/src/components/game/InventoryModal.tsx b/pwa/src/components/game/InventoryModal.tsx index cc6fe62..b901e80 100644 --- a/pwa/src/components/game/InventoryModal.tsx +++ b/pwa/src/components/game/InventoryModal.tsx @@ -641,10 +641,11 @@ function InventoryModal({ { + onClick={(e) => { + e.stopPropagation(); playSfx('/audio/sfx/drop.wav'); onDropItem(item.item_id, item.id, 1); - setActiveDropdown(null); + // Keep open }} style={{ width: '100%', justifyContent: 'flex-start' }} > @@ -655,10 +656,11 @@ function InventoryModal({ { + onClick={(e) => { + e.stopPropagation(); playSfx('/audio/sfx/drop.wav'); onDropItem(item.item_id, item.id, 5); - setActiveDropdown(null); + // Keep open }} style={{ width: '100%', justifyContent: 'flex-start' }} > @@ -666,14 +668,31 @@ function InventoryModal({ )} + {item.quantity >= 10 && ( + { + e.stopPropagation(); + playSfx('/audio/sfx/drop.wav'); + onDropItem(item.item_id, item.id, 10); + // Keep open + }} + style={{ width: '100%', justifyContent: 'flex-start' }} + > + {t('game.drop')} (x10) + + )} + {item.quantity > 1 && ( { + onClick={(e) => { + e.stopPropagation(); playSfx('/audio/sfx/drop.wav'); onDropItem(item.item_id, item.id, item.quantity); - setActiveDropdown(null); + setActiveDropdown(null); // Close on drop all }} style={{ width: '100%', justifyContent: 'flex-start' }} > diff --git a/pwa/src/components/game/LocationView.css b/pwa/src/components/game/LocationView.css index 8d62511..c16274f 100644 --- a/pwa/src/components/game/LocationView.css +++ b/pwa/src/components/game/LocationView.css @@ -8,28 +8,56 @@ /* Ensure padding inside container */ } -/* Ensure expanded corpse container spans full width */ +/* Padded Image for enemies */ +.padded-image { + padding: 10px; + box-sizing: border-box; +} + +/* Remove old expanded grid styles */ .corpse-container { display: contents; /* Allows the card to be a direct grid child when not expanded */ } -.corpse-container.expanded { +/* Loot Overlay Modal */ +.corpse-loot-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(0, 0, 0, 0.7); + z-index: 2000; display: flex; - flex-direction: column; - grid-column: 1 / -1; - background: rgba(0, 0, 0, 0.4); - border: 1px solid var(--game-border-color); - padding: 1rem; - gap: 1rem; + justify-content: center; + align-items: center; + backdrop-filter: blur(2px); } -/* When expanded, the card inside should probably behave differently or just be the header */ -.corpse-container.expanded .entity-card.grid-card { - width: 150px; - /* Fixed size for the header icon when expanded */ - margin: 0 auto; - aspect-ratio: 1; +.corpse-loot-modal { + background: var(--game-bg-app); + border: 1px solid var(--game-border-color); + width: 90%; + max-width: 500px; + max-height: 80vh; + display: flex; + flex-direction: column; + padding: 1rem; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.8); + animation: fadeIn 0.2s ease-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + + to { + opacity: 1; + transform: translateY(0); + } } .entity-card.grid-card { @@ -145,15 +173,6 @@ border-color: rgba(76, 175, 80, 1); } -/* Styles for the expanded corpse details (loot list) */ -.corpse-details { - width: 100%; - background: var(--game-bg-app); - border: 1px solid var(--game-border-color); - padding: 1rem; - margin-top: 1rem; -} - .corpse-details-header { display: flex; justify-content: space-between; @@ -166,6 +185,7 @@ .corpse-details-header h4 { margin: 0; color: var(--game-text-highlight); + font-size: 1.2rem; } .corpse-items-list { @@ -173,4 +193,112 @@ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.5rem; margin-bottom: 1rem; + overflow-y: auto; + flex-grow: 1; + padding-right: 5px; +} + +/* Scrollbar for loot list */ +.corpse-items-list::-webkit-scrollbar { + width: 6px; +} + +.corpse-items-list::-webkit-scrollbar-thumb { + background: var(--game-border-color); + border-radius: 3px; +} + +.corpse-item { + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--game-border-color); + padding: 0.5rem; + display: flex; + justify-content: space-between; + align-items: center; + transition: all 0.2s; +} + +.corpse-item:hover { + background: rgba(255, 255, 255, 0.1); + border-color: var(--game-text-highlight); +} + +.corpse-item.locked { + opacity: 0.6; + border-style: dashed; +} + +.corpse-item-info { + font-size: 0.9rem; +} + +.corpse-item-name { + font-weight: bold; + color: var(--game-text-primary); +} + +.corpse-item-qty { + font-size: 0.8rem; + color: #a0aec0; +} + +.corpse-item-tool { + font-size: 0.8rem; + color: #e53e3e; + display: flex; + align-items: center; + gap: 4px; +} + +.corpse-item-tool.has-tool { + color: #48bb78; +} + +.corpse-item-loot-btn { + background: var(--game-btn-bg); + border: 1px solid var(--game-btn-border); + color: var(--game-btn-text); + padding: 0.25rem 0.5rem; + cursor: pointer; + font-size: 0.8rem; + transition: all 0.2s; +} + +.corpse-item-loot-btn:hover:not(:disabled) { + background: var(--game-btn-bg-hover); +} + +.corpse-item-loot-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.loot-all-btn { + background: #2d3748; + color: white; + border: 1px solid #4a5568; + padding: 0.75rem; + width: 100%; + text-align: center; + font-weight: bold; + cursor: pointer; + margin-top: auto; + transition: background 0.2s; +} + +.loot-all-btn:hover { + background: #4a5568; +} + +.close-btn { + background: none; + border: none; + color: #a0aec0; + font-size: 1.5rem; + cursor: pointer; + line-height: 1; +} + +.close-btn:hover { + color: #fff; } \ No newline at end of file diff --git a/pwa/src/components/game/LocationView.tsx b/pwa/src/components/game/LocationView.tsx index cfbf84d..34ebb46 100644 --- a/pwa/src/components/game/LocationView.tsx +++ b/pwa/src/components/game/LocationView.tsx @@ -210,7 +210,6 @@ function LocationView({ )}
- {/* Enemies */} {/* Enemies */} {location.npcs.filter((npc: any) => npc.type === 'enemy').length > 0 && (
@@ -222,7 +221,7 @@ function LocationView({
handleDropdownClick(e, id)}> {enemy.id && ( -
+
{getTranslatedText(enemy.name)}{t('location.corpses')}
{location.corpses.map((corpse: any) => ( -
+
handleDropdownClick(e, `corpse-${corpse.id}`)} > @@ -317,56 +316,6 @@ function LocationView({ )}
- - {expandedCorpse === String(corpse.id) && corpseDetails && corpseDetails.loot_items && ( -
-
-

{t('location.lootableItems')}

- -
-
- {corpseDetails.loot_items.map((item: any) => ( -
-
-
- {item.emoji} {getTranslatedText(item.item_name)} -
-
- {t('common.qty')}: {item.quantity_min}{item.quantity_min !== item.quantity_max ? `-${item.quantity_max}` : ''} -
- {item.required_tool && ( -
- 🔧 {getTranslatedText(item.required_tool_name)} {item.has_tool ? '✓' : '✗'} -
- )} -
- - - -
- ))} -
- -
- )}
))}
@@ -417,9 +366,33 @@ function LocationView({

{t('location.itemsOnGround')}

{location.items.map((item: any, i: number) => { - // Use loose equality to handle potential string/number mismatches const isShaking = failedActionItemId == item.id; const itemId = `item-${item.id}-${i}`; + + // Pickup Options Helper + const renderPickupOptions = () => { + const options = []; + options.push({ label: 'x1', qty: 1 }); + if (item.quantity >= 5) options.push({ label: 'x5', qty: 5 }); + if (item.quantity >= 10) options.push({ label: 'x10', qty: 10 }); + if (item.quantity > 1) options.push({ label: t('common.all'), qty: item.quantity }); + + return options.map(opt => ( + { + e.stopPropagation(); // Prevent closing + onPickup(item.id, opt.qty); + }} + style={{ width: '100%', justifyContent: 'flex-start', marginBottom: '2px' }} + > + 🤚 {t('common.pickUp')} ({opt.label}) + + )); + }; + return (
handleDropdownClick(e, itemId)} @@ -437,25 +410,6 @@ function LocationView({ 📦 {t('stats.volume')}: {item.volume}L {item.quantity > 1 && `(Total: ${(item.volume * item.quantity).toFixed(2)}L)`}
)} - {item.hp_restore && item.hp_restore > 0 && ( -
❤️ {t('stats.hpRestore')}: +{item.hp_restore}
- )} - {item.stamina_restore && item.stamina_restore > 0 && ( -
⚡ {t('stats.staminaRestore')}: +{item.stamina_restore}
- )} - {item.damage_min !== undefined && item.damage_max !== undefined && (item.damage_min > 0 || item.damage_max > 0) && ( -
- ⚔️ {t('stats.damage')}: {item.damage_min}-{item.damage_max} -
- )} - {item.durability !== undefined && item.durability !== null && item.max_durability !== undefined && item.max_durability !== null && ( -
- 🔧 {t('stats.durability')}: {item.durability}/{item.max_durability} -
- )} - {item.tier !== undefined && item.tier !== null && item.tier > 0 && ( -
⭐ {t('stats.tier')}: {item.tier}
- )}
}>
@@ -488,24 +442,9 @@ function LocationView({ width="160px" >
{getTranslatedText(item.name)}
- { onPickup(item.id, 1); setActiveDropdown(null); }} - style={{ width: '100%', justifyContent: 'flex-start' }} - > - 🤚 {t('common.pickUp')} (x1) - - {item.quantity > 1 && ( - { onPickup(item.id, item.quantity); setActiveDropdown(null); }} - style={{ width: '100%', justifyContent: 'flex-start' }} - > - 🤚 {t('common.pickUp')} ({t('common.all')}) - - )} +
+ {renderPickupOptions()} +
)}
@@ -555,6 +494,60 @@ function LocationView({ )}
+ {/* Corpse Loot Overlay Modal */} + {expandedCorpse && corpseDetails && corpseDetails.loot_items && ( +
+
+
+

{t('location.lootableItems')}

+ +
+
+ {corpseDetails.loot_items.map((item: any) => ( +
+
+
+ {item.emoji} {getTranslatedText(item.item_name)} +
+
+ {t('common.qty')}: {item.quantity_min}{item.quantity_min !== item.quantity_max ? `-${item.quantity_max}` : ''} +
+ {item.required_tool && ( +
+ 🔧 {getTranslatedText(item.required_tool_name)} {item.has_tool ? '✓' : '✗'} +
+ )} +
+ + + +
+ ))} +
+ +
+
+ )} + + {(showCraftingMenu || showRepairMenu) && (