diff --git a/src/renderer/components/CaseRoulette.tsx b/src/renderer/components/CaseRoulette.tsx index 49edf36..d3450ec 100644 --- a/src/renderer/components/CaseRoulette.tsx +++ b/src/renderer/components/CaseRoulette.tsx @@ -16,13 +16,18 @@ interface CaseRouletteProps { const ITEM_WIDTH = 110; const ITEM_GAP = 8; const VISIBLE_ITEMS = 21; -const CENTER_INDEX = Math.floor(VISIBLE_ITEMS / 2); const CONTAINER_WIDTH = 800; const LINE_X = CONTAINER_WIDTH / 2; const ANIMATION_DURATION = 10; // секунды const ANIMATION_DURATION_MS = ANIMATION_DURATION * 1000; +// Удаляем майнкрафтовские цвет-коды (§a, §b, §l и т.д.) +function stripMinecraftColors(text?: string | null): string { + if (!text) return ''; + return text.replace(/§[0-9A-FK-ORa-fk-or]/g, ''); +} + function getRarityByWeight(weight?: number): Rarity { if (weight === undefined || weight === null) return 'common'; if (weight <= 5) return 'legendary'; @@ -42,7 +47,7 @@ function getRarityColor(weight?: number): string { return 'rgba(65, 105, 225, 1)'; case 'common': default: - return 'rgba(255, 255, 255, 0.25)'; + return 'rgba(255, 255, 255, 0.6)'; } } @@ -61,8 +66,9 @@ export default function CaseRoulette({ const animationTimeoutRef = useRef(); const finishTimeoutRef = useRef(); - const winningName = + const winningNameRaw = reward?.meta?.display_name || reward?.name || reward?.material || ''; + const winningName = stripMinecraftColors(winningNameRaw); // Измеряем реальные ширины элементов const measureItemWidths = useCallback((): number[] => { @@ -121,10 +127,8 @@ export default function CaseRoulette({ winPosition * (ITEM_WIDTH + ITEM_GAP) + ITEM_WIDTH / 2; const finalOffset = centerItemCenter - LINE_X; - // стартовая позиция чуть левее финальной (едем вправо к ней) const initialOffset = Math.max(finalOffset - extraDistance, 0); - // ставим ленту далеко "слева", чтобы она много проехала setOffset(initialOffset); animationTimeoutRef.current = setTimeout(() => { @@ -132,7 +136,6 @@ export default function CaseRoulette({ setOffset(finalOffset); }, 50); - // 7 секунд анимации + небольшой запас finishTimeoutRef.current = setTimeout(() => { setAnimationFinished(true); }, ANIMATION_DURATION_MS + 200); @@ -148,7 +151,6 @@ export default function CaseRoulette({ const centerItemCenter = cumulativeOffset + widths[winPosition] / 2; const finalOffset = centerItemCenter - LINE_X; - // стартовая позиция чуть левее финальной (едем вправо к ней) const initialOffset = Math.max(finalOffset - extraDistance, 0); setOffset(initialOffset); @@ -194,16 +196,31 @@ export default function CaseRoulette({ fullWidth PaperProps={{ sx: { - bgcolor: 'rgba(15, 15, 20, 0.95)', - borderRadius: '1vw', + bgcolor: 'rgba(5, 5, 10, 0.96)', + borderRadius: '24px', + border: '1px solid rgba(255,255,255,0.12)', + boxShadow: '0 25px 80px rgba(0,0,0,0.8)', + overflow: 'hidden', }, }} > - + Открытие кейса {caseName} @@ -212,15 +229,31 @@ export default function CaseRoulette({ sx={{ position: 'relative', overflow: 'hidden', - borderRadius: '1vw', - border: '2px solid rgba(255, 255, 255, 0.1)', + borderRadius: '18px', + border: '1px solid rgba(255, 255, 255, 0.15)', px: 2, py: 3, width: `${CONTAINER_WIDTH}px`, maxWidth: '100%', mx: 'auto', + background: + 'linear-gradient(135deg, rgba(10,10,20,0.9), rgba(25,25,45,0.9))', + backdropFilter: 'blur(18px)', + boxShadow: '0 0 40px rgba(0,0,0,0.7)', }} > + {/* затемнённые края */} + + {/* Линия центра */} @@ -246,6 +280,8 @@ export default function CaseRoulette({ transition: animating ? `transform ${ANIMATION_DURATION}s cubic-bezier(0.15, 0.85, 0.25, 1)` : 'none', + position: 'relative', + zIndex: 0, }} > {sequence.map((item, index) => { @@ -253,6 +289,15 @@ export default function CaseRoulette({ const isWinningItem = animationFinished && index === Math.floor(sequence.length / 2); + const rawName = + item.meta?.display_name || + item.name || + item.material + .replace(/_/g, ' ') + .toLowerCase() + .replace(/\b\w/g, (l) => l.toUpperCase()); + const displayName = stripMinecraftColors(rawName); + return ( - {item.meta?.display_name || - item.name || - item.material - .replace(/_/g, ' ') - .toLowerCase() - .replace(/\b\w/g, (l) => l.toUpperCase())} + {displayName} ); @@ -318,7 +361,7 @@ export default function CaseRoulette({ color="white" sx={{ textAlign: 'center', - mt: 2, + mt: 2.5, animation: 'fadeIn 0.5s ease', }} > @@ -340,16 +383,21 @@ export default function CaseRoulette({ variant="contained" onClick={onClose} sx={{ - borderRadius: '20px', - p: '0.5vw 2.5vw', + borderRadius: '999px', + px: '2.8vw', + py: '0.6vw', color: 'white', - backgroundColor: 'rgb(255, 77, 77)', + background: + 'linear-gradient(135deg, rgb(255, 77, 77), rgb(255, 120, 100))', '&:hover': { - backgroundColor: 'rgba(255, 77, 77, 0.5)', + background: + 'linear-gradient(135deg, rgba(255, 77, 77, 0.8), rgba(255, 120, 100, 0.8))', }, fontFamily: 'Benzin-Bold', opacity: animationFinished ? 1 : 0.5, pointerEvents: animationFinished ? 'auto' : 'none', + textTransform: 'uppercase', + letterSpacing: 1, }} > Закрыть diff --git a/src/renderer/pages/Shop.tsx b/src/renderer/pages/Shop.tsx index 0750fe9..2c892ea 100644 --- a/src/renderer/pages/Shop.tsx +++ b/src/renderer/pages/Shop.tsx @@ -343,66 +343,182 @@ export default function Shop() { {cases.map((c) => ( + {/* НОВАЯ КАРТОЧКА */} + {/* верхний “свет” */} + + {c.image_url && ( - + > + + + + + {/* маленький бейдж сверху картинки */} + + Кейс + + )} - - + + + {c.name} + {c.description && ( {c.description} )} - - Цена: {c.price} монет - + + + + Цена + + + {c.price} поп + + + {typeof c.items_count === 'number' && ( Предметов в кейсе: {c.items_count} )} +