// screens-fortune.jsx — Fortune wheel + prize reveal + lucky points const { useState: _fs_useState, useEffect: _fs_useEffect, useRef: _fs_useRef, useMemo: _fs_useMemo } = React; function FortuneScreen({ ctx }){ const { t: T, fortune, setFortune, setBalance, balance, navigate, fortuneStyle } = ctx; const prizes = window.RAVANA_DATA.fortune_prizes; const [rotation, setRotation] = _fs_useState(0); const [spinning, setSpinning] = _fs_useState(false); const [prize, setPrize] = _fs_useState(null); const [view, setView] = _fs_useState('wheel'); // wheel, exchange const [reelOffsets, setReelOffsets] = _fs_useState([0, 0, 0]); const canSpin = (fortune.spins > 0 || fortune.dailyReady) && !spinning; const spin = () => { if (!canSpin) return; setSpinning(true); const idx = Math.floor(Math.random() * prizes.length); const chosen = prizes[idx]; if (fortuneStyle === 'reel') { // 3 reels to settle on a stack const offsets = [0,0,0].map((_,i) => -(60 + i*20) * 56); setReelOffsets(offsets); } else { // Wheel: rotate so pin (top) points to sector idx (centered) const slice = 360 / prizes.length; const baseRotations = 5 * 360; const target = baseRotations + (360 - (idx * slice + slice/2)); setRotation(target); } setTimeout(() => { setPrize(chosen); setSpinning(false); // Deduct spin and add reward setFortune(f => ({ ...f, spins: fortune.dailyReady ? f.spins : Math.max(0, f.spins - 1), dailyReady: false, cooldown: '23h 59m', })); if (chosen.kind === 'photo') setBalance(b => ({...b, photo: b.photo + chosen.amount})); if (chosen.kind === 'video') setBalance(b => ({...b, video: b.video + chosen.amount})); if (chosen.kind === 'points') setFortune(f => ({...f, points: f.points + chosen.amount})); if (chosen.kind === 'spin') setFortune(f => ({...f, spins: f.spins + chosen.amount})); if (chosen.kind === 'jackpot') { setBalance(b => ({...b, photo: b.photo + 100, video: b.video + 5})); setFortune(f => ({...f, points: f.points + 500})); } }, fortuneStyle === 'reel' ? 2300 : 3700); }; if (view === 'exchange') return setView('wheel')}/>; return (
}/>
{T('spins_left')}
{fortune.spins}{fortune.dailyReady ? ' +1' : ''}
{T('lucky_points')}
{fortune.points}
{fortuneStyle === 'reel' ? ( ) : ( )}
{fortune.dailyReady ? (
{T('fortune_widget_ready')}
) : (
{T('next_daily')}: {fortune.cooldown}
)}
{/* Earn more spins */}

{T('earn_spins')}

} title={T('earn_invite')} action={()=>navigate('referrals')} accent="purple"/> } title={T('earn_purchase')} action={()=>navigate('credits')} accent="gold"/> } title={T('earn_referral_paid')} action={()=>navigate('referrals')} accent="cyan"/> } title={T('earn_streak')} accent="purple"/>
{/* Recent prizes */}

{T('recent_prizes')}

{window.RAVANA_DATA.recent_prizes.map((p, i) => (
{prizeText(p.kind, p.amount)}
{p.time}
))}
setPrize(null)} canSpin={fortune.spins > 0 || fortune.dailyReady} onSpinAgain={()=>{ setPrize(null); setTimeout(spin, 200); }} navigate={navigate} ctx={ctx}/> ); } // ===== SVG Wheel ===== function WheelStage({ rotation, prizes, spinning }){ const N = prizes.length; const slice = 360 / N; const cx = 150, cy = 150, R = 138, RI = 28; const TAU = Math.PI / 180; // Sector path const sectorPath = (i) => { const a1 = (i * slice - 90 - slice/2) * TAU; const a2 = ((i+1) * slice - 90 - slice/2) * TAU; const x1 = cx + R * Math.cos(a1), y1 = cy + R * Math.sin(a1); const x2 = cx + R * Math.cos(a2), y2 = cy + R * Math.sin(a2); return `M ${cx} ${cy} L ${x1} ${y1} A ${R} ${R} 0 0 1 ${x2} ${y2} Z`; }; // Icon at sector midpoint const sectorIconPos = (i) => { const ang = (i * slice - 90) * TAU; const r = R * 0.62; return { x: cx + r * Math.cos(ang), y: cy + r * Math.sin(ang), deg: i * slice }; }; // Studs around the rim const STUDS = 24; const studs = Array.from({length: STUDS}, (_, i) => { const a = (i / STUDS) * 360 * TAU; return { x: cx + (R + 12) * Math.cos(a), y: cy + (R + 12) * Math.sin(a), i }; }); return (
{/* Pointer at top — fixed */} {/* The disc */} {prizes.map((p, i) => ( ))} {/* Outer rim — gold */} {/* Studs */} {studs.map(s => ( ))} {/* Sectors */} {prizes.map((p, i) => ( ))} {/* Sector labels — icon + text */} {prizes.map((p, i) => { const { x, y, deg } = sectorIconPos(i); const darkFg = p.color === '#F6C85F' || p.color === '#DFA64A'; const fg = darkFg ? '#2a1700' : '#fff'; return ( ); })} {/* Spokes (subtle highlight) */} {prizes.map((_, i) => { const a = (i * slice - 90 - slice/2) * TAU; const xo = cx + (R - 2) * Math.cos(a); const yo = cy + (R - 2) * Math.sin(a); return ; })} {/* Inner hub frame */} {/* Hub label (counter-rotated so it stays upright) */}
); } // renders the icon + short text within a single sector group (transform already applied) function SectorContent({ prize, fg }){ // 16px icon centered at -10y, text below at +14y let icon = null; if (prize.kind === 'photo') icon = ; else if (prize.kind === 'video') icon = ; else if (prize.kind === 'points') icon = ; else if (prize.kind === 'spin') icon = ; else if (prize.kind === 'jackpot') icon = ; return ( {icon} {prize.short} ); } // Filled SVG glyphs that go inside sectors (no stroke, currentColor=fg) const PathPhoto = () => ; const PathVideo = () => ; const PathSparkle = () => ; const PathRefresh = () => ; const PathCrown = () => ; function lighten(hex, amt){ // Simple: blend hex toward white const c = hex.replace('#',''); const r = parseInt(c.substr(0,2),16), g = parseInt(c.substr(2,2),16), b = parseInt(c.substr(4,2),16); const m = v => Math.round(v + (255 - v) * amt); const h = v => v.toString(16).padStart(2,'0'); return `#${h(m(r))}${h(m(g))}${h(m(b))}`; } // ===== Slot reel ===== function ReelStage({ offsets, spinning, prizes }){ // Render symbols as visual chips (icon + color) const Cells = ({ col }) => ( <> {Array.from({length: 80}, (_, i) => { const p = prizes[i % prizes.length]; return (
{p.kind === 'photo' ? : p.kind === 'video' ? : p.kind === 'points' ? : p.kind === 'spin' ? : }
{p.short}
); })} ); return (
{/* Top crown lights */}
{Array.from({length: 9}).map((_, i) => )}
{[0,1,2].map(col => (
))} {/* Win line + arrows */}
{Array.from({length: 9}).map((_, i) => )}
); } function PrizeIcon({ kind }){ const map = { photo: { i: , c: 'rgba(155,92,255,0.18)', col: 'var(--purple-3)' }, video: { i: , c: 'rgba(34,211,238,0.16)', col: 'var(--cyan)' }, points: { i: , c: 'rgba(155,92,255,0.18)', col: 'var(--purple-3)' }, spin: { i: , c: 'rgba(56,189,248,0.16)', col: 'var(--eblue)' }, jackpot: { i: , c: 'rgba(246,200,95,0.18)', col: 'var(--gold)' }, }; const m = map[kind] || map.photo; return
{m.i}
; } function prizeText(kind, amount){ if (kind === 'photo') return `+${amount} photo credits`; if (kind === 'video') return `+${amount} video min`; if (kind === 'points') return `+${amount} lucky points`; if (kind === 'spin') return `+${amount} extra spin`; if (kind === 'jackpot') return `Jackpot — +100 photos, +5 videos, +500 pts`; return kind; } function EarnRow({ icon, title, action, accent }){ return ( ); } function PrizeRevealModal({ prize, onClose, onSpinAgain, canSpin, navigate, ctx }){ if (!prize) return null; const { t: T } = ctx; const isJackpot = prize.kind === 'jackpot'; const darkFg = prize.color === '#F6C85F' || prize.color === '#DFA64A'; const particles = _fs_useMemo(() => Array.from({length: 22}, (_, i) => ({ i, x: 50 + (Math.random() - 0.5) * 90, y: 50 + (Math.random() - 0.5) * 90, delay: Math.random() * 0.4, size: 4 + Math.random() * 6, color: [prize.color, '#F6C85F', '#9B5CFF', '#22D3EE'][Math.floor(Math.random()*4)], rotate: Math.random() * 360, })), [prize]); return (
{Array.from({length: 12}).map((_, i) => (
))}
{particles.map(p => ( ))}
{prize.kind === 'photo' ? : prize.kind === 'video' ? : prize.kind === 'points' ? : prize.kind === 'spin' ? : }
{prize.short}
{isJackpot ? 'JACKPOT!' : 'You won'}
{prizeText(prize.kind, prize.amount)}
{T('prize_added')}
{canSpin && ( )}
); } function lightenStr(hex, amt){ const c = hex.replace('#',''); const r = parseInt(c.substr(0,2),16), g = parseInt(c.substr(2,2),16), b = parseInt(c.substr(4,2),16); const m = v => Math.round(v + (255 - v) * amt); const h = v => v.toString(16).padStart(2,'0'); return `#${h(m(r))}${h(m(g))}${h(m(b))}`; } function LuckyPointsExchange({ ctx, onBack }){ const { t: T, fortune, setFortune, setBalance } = ctx; const [confirmation, setConfirmation] = _fs_useState(null); const exchanges = [ { id: 'photo', cost: 100, label: T('points_to_photo'), reward: () => ({photo: 20}) }, { id: 'video', cost: 500, label: T('points_to_video'), reward: () => ({video: 1}) }, { id: 'spin', cost: 300, label: T('points_to_spin'), reward: 'spin' }, ]; const doExchange = (ex) => { if (fortune.points < ex.cost) return; setFortune(f => ({...f, points: f.points - ex.cost, spins: ex.reward === 'spin' ? f.spins + 1 : f.spins})); if (ex.reward !== 'spin') { const r = ex.reward(); setBalance(b => ({photo: b.photo + (r.photo||0), video: b.video + (r.video||0)})); } setConfirmation(ex.id); setTimeout(()=>setConfirmation(null), 1800); }; return (
{T('lucky_points')}
{fortune.points}
{exchanges.map(ex => { const can = fortune.points >= ex.cost; return (
{ex.label}
cost: {ex.cost} pts
); })}
); } window.FortuneScreen = FortuneScreen;