window.GDD = window.GDD || {}; const { useState, useEffect, useCallback, useRef } = React; function BountyDemo() { const [bounties, setBounties] = useState([]); const [killFeed, setKillFeed] = useState([]); const [placeBountyTarget, setPlaceBountyTarget] = useState(''); const [placeBountyAmount, setPlaceBountyAmount] = useState(5000); const [showPlaceBounty, setShowPlaceBounty] = useState(false); const [notifications, setNotifications] = useState([]); const [autoFeed, setAutoFeed] = useState(false); const feedRef = useRef(null); const autoRef = useRef(null); const feedNames = [ 'CMDR Picard', 'CMDR Worf', 'CMDR Data', 'CMDR Troi', 'CMDR Riker', 'MinerBob', 'PirateKing99', 'NullSecWarlord', 'TraderAlice', 'DeepMiner', 'RockHound', 'AmarrTrader', 'BulkMiner', 'GallenteForge', 'HighSecOps', ]; const shipTypes = ['Frigate', 'Destroyer', 'Cruiser', 'Battlecruiser', 'Battleship', 'Hauler', 'Mining Barge']; const systems = ['Sol', 'Amarr', 'Hek', 'Rens', 'Dodixie', 'U-IRTYR', 'PF-346', 'YZ-LQL', 'O-WAMW']; const tierConfig = [ { tier: 'Petty', threshold: 500, color: 'var(--muted)', reward: '10%', visibility: 'System-local' }, { tier: 'Standard', threshold: 5000, color: 'var(--cyan)', reward: '15%', visibility: 'Regional' }, { tier: 'Dangerous', threshold: 50000, color: 'var(--accent)', reward: '20%', visibility: 'Galaxy-wide' }, { tier: 'Most Wanted', threshold: 500000, color: 'var(--red)', reward: '25%', visibility: 'Galaxy + Leaderboard' }, ]; const getTier = (pool) => { if (pool >= 500000) return tierConfig[3]; if (pool >= 50000) return tierConfig[2]; if (pool >= 5000) return tierConfig[1]; return tierConfig[0]; }; useEffect(() => { window.GDD.api.getBounties().then(b => setBounties(b)); window.GDD.api.getKillFeed().then(k => setKillFeed(k)); }, []); const addNotif = useCallback((msg, color) => { const id = Date.now(); setNotifications(prev => [...prev, { id, msg, color }]); setTimeout(() => setNotifications(prev => prev.filter(n => n.id !== id)), 3500); }, []); const handlePlaceBounty = useCallback(async () => { if (!placeBountyTarget.trim()) return; if (placeBountyAmount < 500) { addNotif('Minimum bounty is 500 ISK.', 'var(--red)'); return; } const result = await window.GDD.api.placeBounty(placeBountyTarget, placeBountyAmount); if (result.success) { setBounties(prev => { const existing = prev.find(b => b.target === placeBountyTarget); if (existing) { return prev.map(b => b.target === placeBountyTarget ? { ...b, pool: b.pool + placeBountyAmount, tier: getTier(b.pool + placeBountyAmount).tier } : b); } return [...prev, { target: placeBountyTarget, pool: placeBountyAmount, tier: getTier(placeBountyAmount).tier, lastHostile: 'Just now', }]; }); addNotif(`Bounty of ₢${placeBountyAmount.toLocaleString()} placed on ${placeBountyTarget}.`, 'var(--green)'); setShowPlaceBounty(false); setPlaceBountyTarget(''); setPlaceBountyAmount(5000); } }, [placeBountyTarget, placeBountyAmount, addNotif]); // Auto-generate kill feed const generateKill = useCallback(() => { const victim = feedNames[Math.floor(Math.random() * feedNames.length)]; let killer; do { killer = feedNames[Math.floor(Math.random() * feedNames.length)]; } while (killer === victim); const ship = shipTypes[Math.floor(Math.random() * shipTypes.length)]; const system = systems[Math.floor(Math.random() * systems.length)]; const bounty = Math.random() > 0.6 ? Math.floor(Math.random() * 50000) : 0; const kill = { victim, killer, ship, system, bounty, time: 'Just now', }; setKillFeed(prev => [kill, ...prev.slice(0, 49)]); // If bounty, update bounty pool if (bounty > 0) { addNotif(`Bounty collected: ${killer} claimed ₢${bounty.toLocaleString()} from ${victim}'s bounty.`, 'var(--accent)'); setBounties(prev => prev.map(b => b.target === victim ? { ...b, pool: Math.max(0, b.pool - bounty) } : b ).filter(b => b.pool > 0)); } }, [addNotif]); useEffect(() => { if (autoFeed) { autoRef.current = setInterval(generateKill, 2000 + Math.random() * 3000); } else { if (autoRef.current) clearInterval(autoRef.current); } return () => { if (autoRef.current) clearInterval(autoRef.current); }; }, [autoFeed, generateKill]); // Auto-scroll kill feed useEffect(() => { if (killFeed.length > 0 && feedRef.current) { // No auto-scroll needed since newest are on top } }, [killFeed]); const totalBountyPool = bounties.reduce((sum, b) => sum + b.pool, 0); const totalKills = killFeed.length; const totalBountyCollected = killFeed.reduce((sum, k) => sum + k.bounty, 0); return (
e.currentTarget.style.color='var(--fg-bright)'} onMouseLeave={e => e.currentTarget.style.color='var(--muted)'}>← Back to Docs

Bounty Board & Kill Feed

Live bounty board with tier escalation and a galaxy-wide kill feed. Place bounties on pirates, track kill events, and watch bounty pools climb. Toggle the auto-feed to simulate live combat activity.

{/* HUD-style bounty strip */}
BOUNTY BOARD
ACTIVE {bounties.length}
POOL ₢{totalBountyPool.toLocaleString()}
COLLECTED ₢{totalBountyCollected.toLocaleString()} {autoFeed && ● LIVE FEED}
{/* Notifications */}
{notifications.map(n => (
{n.msg}
))}
{/* Stats */}
{bounties.length}
Active Bounties
₢{totalBountyPool.toLocaleString()}
Total Bounty Pool
{totalKills}
Kill Events
₢{totalBountyCollected.toLocaleString()}
Bounty Collected
{/* Place bounty modal */} {showPlaceBounty && (
setShowPlaceBounty(false)}>
e.stopPropagation()}>

Place a Bounty

Target Player
setPlaceBountyTarget(e.target.value)} placeholder="Enter player name..." style={{ width: '100%', padding: 'var(--sp-2) var(--sp-3)', background: 'var(--surface-raised)', border: '1px solid var(--border)', borderRadius: 'var(--radius-md)', color: 'var(--fg)', fontFamily: 'var(--font-mono)', fontSize: '0.85rem', }} />
Amount (min 500 ISK)
setPlaceBountyAmount(parseInt(e.target.value) || 0)} style={{ width: '100%', padding: 'var(--sp-2) var(--sp-3)', background: 'var(--surface-raised)', border: '1px solid var(--border)', borderRadius: 'var(--radius-md)', color: 'var(--fg)', fontFamily: 'var(--font-mono)', fontSize: '0.85rem', }} />
{/* Tier preview */}
Resulting Tier
{(() => { const t = getTier(placeBountyAmount); return (
{t.tier} Hunter reward: {t.reward} · {t.visibility}
); })()}
)}
{/* Active Bounties */}

Active Bounties

{bounties.length === 0 && (
No active bounties. Place one to get started.
)} {bounties.sort((a, b) => b.pool - a.pool).map((bounty, i) => { const tier = getTier(bounty.pool); return (

{bounty.target}

{tier.tier.toUpperCase()}
₢{bounty.pool.toLocaleString()}
Hunter reward: {tier.reward}
Visibility: {tier.visibility} Last hostile: {bounty.lastHostile}
{/* Pool bar */}
₢{tier.threshold.toLocaleString()} Next tier
); })} {/* Tier legend */}

Bounty Tiers

{tierConfig.map((t, i) => (
{t.tier} ≥ ₢{t.threshold.toLocaleString()}
{t.reward} · {t.visibility}
))}
{/* Kill Feed */}

Kill Feed {autoFeed && ( ● LIVE )}

{killFeed.length === 0 && (
No kill events yet. Start the live feed or generate events manually.
)} {killFeed.map((kill, i) => (
0 ? 'var(--accent-border)' : 'var(--border)'}`, borderRadius: 'var(--radius-md)', transition: 'background 0.3s', }}>
{kill.victim} destroyed by {kill.killer}
{kill.time}
Ship: {kill.ship} System: {kill.system} {kill.bounty > 0 && ( Bounty: ₢{kill.bounty.toLocaleString()} )}
))}
{/* Anti-abuse rules */}
Anti-abuse rules (implemented in backend): You cannot claim your own bounty (alt check). Payout never exceeds ship loss value. Minimum placement is 500 ISK. Target must have negative security status or committed a hostile act within 24h. Bounties decay 10%/week if target stays clean for 30 days.
); } window.GDD.BountyDemo = BountyDemo;