Files
dark-lord/app/game/page.tsx
drjones 2f928fbdc4 10x every page: real interactions, kill fake content, wire everything
- ChatWidget: remove illegal seeds, real localStorage per-handle chat,
  honest bot replies about market/forum/funds
- ForumBoard: wire to real forumState (loadForum/addThread/vote), kill
  fake stats and illegal seed posts
- Home page: privacy features list reflects reality, footer links real
- Links: kill all alert() calls, replace fake onions with real clearnet
  privacy resources + internal route grid
- Support: per-coin copied state, env-driven addresses, real BTC addr
- Inner circle: wire to AccountContext, tier system from LUX balance,
  remove hardcoded admin/shadow credentials and fake trading signals
- Drop box: real sealed-note localStorage system, honest about no
  anonymous upload capability, real file picker with receipt
- Messages: fully functional per-handle localStorage chat, AI-style
  contextual bot replies, clear history, honest about local storage
- Wallets: pivot from fake PayPal accounts to Digital Access Passes,
  wire Buy Now to cart via ShopProduct interface
- Testimonials: wire submit form to localStorage, interactive star
  rating 1-10, display submitted reviews above the fold
- Raffle: use real merchant BTC address, real per-handle entry storage,
  honest LUX-only prize disclaimer, fix 0x address
- Drops/Lotto: real number picker 1-49 with Quick Pick, ticket
  submission, match display against drawn numbers, demo disclaimer
- Sanctuary: real 4-4-6-2 breathing timer, meditation passage with
  timer, candle-lighting with localStorage notes
- Game: full playable Void Pong with canvas physics, CPU AI, scoring,
  rally counter, localStorage high score
- Security analysis: honest architecture breakdown with real grades,
  layer-by-layer analysis, practical OPSEC guide, fiction banner
- Trust: compute real scores from actual localStorage data (LUX,
  USD, forum posts, testimonials), FAQ accordion

Made-with: Cursor
2026-04-16 00:55:28 -07:00

299 lines
10 KiB
TypeScript

"use client";
import { useState, useEffect, useRef, useCallback } from "react";
import Link from "next/link";
const FIELD_W = 600;
const FIELD_H = 400;
const BALL_R = 12;
const PADDLE_W = 12;
const PADDLE_H = 70;
const CPU_SPEED = 3.2;
const INITIAL_SPEED = 4.5;
type Vec = { x: number; y: number };
type GameState = "idle" | "playing" | "paused" | "over";
function clamp(v: number, lo: number, hi: number) {
return Math.max(lo, Math.min(hi, v));
}
const HIGH_SCORE_KEY = "cyberlux-pong-hs";
function getHighScore() {
if (typeof window === "undefined") return 0;
return parseInt(localStorage.getItem(HIGH_SCORE_KEY) ?? "0", 10);
}
function setHighScore(n: number) {
if (typeof window !== "undefined") localStorage.setItem(HIGH_SCORE_KEY, String(n));
}
export default function PongPage() {
const canvasRef = useRef<HTMLCanvasElement>(null);
const stateRef = useRef<GameState>("idle");
const ballRef = useRef<Vec>({ x: FIELD_W / 2, y: FIELD_H / 2 });
const velRef = useRef<Vec>({ x: INITIAL_SPEED, y: INITIAL_SPEED });
const playerYRef = useRef((FIELD_H - PADDLE_H) / 2);
const cpuYRef = useRef((FIELD_H - PADDLE_H) / 2);
const scoreRef = useRef({ player: 0, cpu: 0 });
const rafRef = useRef<number>(0);
const mouseYRef = useRef(FIELD_H / 2);
const [displayScore, setDisplayScore] = useState({ player: 0, cpu: 0 });
const [gameState, setGameState] = useState<GameState>("idle");
const [highScore, setHighScoreState] = useState(0);
const [rally, setRally] = useState(0);
const rallyRef = useRef(0);
useEffect(() => {
setHighScoreState(getHighScore());
}, []);
const reset = useCallback(() => {
ballRef.current = { x: FIELD_W / 2, y: FIELD_H / 2 };
const angle = (Math.random() * Math.PI) / 3 - Math.PI / 6;
const dir = Math.random() > 0.5 ? 1 : -1;
velRef.current = {
x: INITIAL_SPEED * dir * Math.cos(angle),
y: INITIAL_SPEED * Math.sin(angle),
};
rallyRef.current = 0;
setRally(0);
}, []);
const draw = useCallback(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
// Background
ctx.fillStyle = "#050510";
ctx.fillRect(0, 0, FIELD_W, FIELD_H);
// Center line
ctx.setLineDash([8, 8]);
ctx.strokeStyle = "rgba(0,255,200,0.1)";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(FIELD_W / 2, 0);
ctx.lineTo(FIELD_W / 2, FIELD_H);
ctx.stroke();
ctx.setLineDash([]);
// Paddles
const drawPaddle = (x: number, y: number, color: string) => {
ctx.shadowColor = color;
ctx.shadowBlur = 12;
ctx.fillStyle = color;
ctx.fillRect(x, y, PADDLE_W, PADDLE_H);
ctx.shadowBlur = 0;
};
drawPaddle(8, playerYRef.current, "#00ffcc");
drawPaddle(FIELD_W - 8 - PADDLE_W, cpuYRef.current, "#ff00ff");
// Ball
ctx.shadowColor = "#00ffcc";
ctx.shadowBlur = 20;
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.arc(ballRef.current.x, ballRef.current.y, BALL_R, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
// Score overlay
ctx.fillStyle = "rgba(0,255,200,0.15)";
ctx.font = "bold 36px monospace";
ctx.textAlign = "center";
ctx.fillText(String(scoreRef.current.player), FIELD_W / 4, 50);
ctx.fillStyle = "rgba(255,0,255,0.15)";
ctx.fillText(String(scoreRef.current.cpu), (FIELD_W * 3) / 4, 50);
}, []);
const tick = useCallback(() => {
if (stateRef.current !== "playing") return;
// Move ball
const b = ballRef.current;
const v = velRef.current;
b.x += v.x;
b.y += v.y;
// Wall bounce top/bottom
if (b.y - BALL_R <= 0) { b.y = BALL_R; v.y = Math.abs(v.y); }
if (b.y + BALL_R >= FIELD_H) { b.y = FIELD_H - BALL_R; v.y = -Math.abs(v.y); }
// CPU paddle AI
const cpuCenter = cpuYRef.current + PADDLE_H / 2;
if (cpuCenter < b.y - 4) cpuYRef.current = clamp(cpuYRef.current + CPU_SPEED, 0, FIELD_H - PADDLE_H);
if (cpuCenter > b.y + 4) cpuYRef.current = clamp(cpuYRef.current - CPU_SPEED, 0, FIELD_H - PADDLE_H);
// Player paddle follow mouse
playerYRef.current = clamp(mouseYRef.current - PADDLE_H / 2, 0, FIELD_H - PADDLE_H);
// Player paddle hit
if (b.x - BALL_R <= 8 + PADDLE_W && b.y >= playerYRef.current && b.y <= playerYRef.current + PADDLE_H && v.x < 0) {
b.x = 8 + PADDLE_W + BALL_R;
const hitPos = (b.y - playerYRef.current) / PADDLE_H - 0.5;
const speed = Math.min(Math.sqrt(v.x ** 2 + v.y ** 2) + 0.15, 10);
v.x = Math.abs(speed * Math.cos(hitPos * Math.PI * 0.6));
v.y = speed * Math.sin(hitPos * Math.PI * 0.6);
rallyRef.current++;
setRally(rallyRef.current);
}
// CPU paddle hit
if (b.x + BALL_R >= FIELD_W - 8 - PADDLE_W && b.y >= cpuYRef.current && b.y <= cpuYRef.current + PADDLE_H && v.x > 0) {
b.x = FIELD_W - 8 - PADDLE_W - BALL_R;
const hitPos = (b.y - cpuYRef.current) / PADDLE_H - 0.5;
const speed = Math.sqrt(v.x ** 2 + v.y ** 2);
v.x = -Math.abs(speed * Math.cos(hitPos * Math.PI * 0.6));
v.y = speed * Math.sin(hitPos * Math.PI * 0.6);
rallyRef.current++;
setRally(rallyRef.current);
}
// Scoring
if (b.x < 0) {
scoreRef.current.cpu++;
setDisplayScore({ ...scoreRef.current });
if (scoreRef.current.cpu >= 7) {
stateRef.current = "over";
setGameState("over");
const hs = getHighScore();
if (rallyRef.current > hs) { setHighScore(rallyRef.current); setHighScoreState(rallyRef.current); }
} else reset();
}
if (b.x > FIELD_W) {
scoreRef.current.player++;
setDisplayScore({ ...scoreRef.current });
if (scoreRef.current.player >= 7) {
stateRef.current = "over";
setGameState("over");
const hs = getHighScore();
if (rallyRef.current > hs) { setHighScore(rallyRef.current); setHighScoreState(rallyRef.current); }
} else reset();
}
draw();
rafRef.current = requestAnimationFrame(tick);
}, [draw, reset]);
const startGame = useCallback(() => {
scoreRef.current = { player: 0, cpu: 0 };
setDisplayScore({ player: 0, cpu: 0 });
stateRef.current = "playing";
setGameState("playing");
reset();
if (rafRef.current) cancelAnimationFrame(rafRef.current);
rafRef.current = requestAnimationFrame(tick);
}, [tick, reset]);
useEffect(() => {
draw();
return () => { if (rafRef.current) cancelAnimationFrame(rafRef.current); };
}, [draw]);
const handleMouseMove = useCallback((e: React.MouseEvent<HTMLCanvasElement>) => {
const rect = canvasRef.current?.getBoundingClientRect();
if (!rect) return;
const scaleY = FIELD_H / rect.height;
mouseYRef.current = (e.clientY - rect.top) * scaleY;
}, []);
const handleTouchMove = useCallback((e: React.TouchEvent<HTMLCanvasElement>) => {
e.preventDefault();
const rect = canvasRef.current?.getBoundingClientRect();
if (!rect || !e.touches[0]) return;
const scaleY = FIELD_H / rect.height;
mouseYRef.current = (e.touches[0].clientY - rect.top) * scaleY;
}, []);
return (
<div className="flex min-h-screen flex-col items-center justify-center bg-[#050510] p-6 font-mono text-white">
<div className="w-full max-w-[620px]">
<div className="mb-4 flex items-center justify-between">
<div>
<h1 className="text-xl font-black uppercase tracking-widest text-neon-cyan">VOID PONG</h1>
<p className="text-[10px] text-white/30">Move mouse over field to control paddle. First to 7.</p>
</div>
<div className="text-right text-xs text-white/40">
<div>Rally record: <span className="text-neon-cyan">{highScore}</span></div>
{rally > 0 && gameState === "playing" && (
<div>Current rally: <span className="text-neon-purple">{rally}</span></div>
)}
</div>
</div>
{gameState !== "idle" && (
<div className="mb-3 flex items-center justify-between text-sm">
<span className="text-neon-cyan font-bold">YOU {displayScore.player}</span>
<span className="text-xs text-white/30">vs</span>
<span className="text-neon-purple font-bold">{displayScore.cpu} CPU</span>
</div>
)}
<canvas
ref={canvasRef}
width={FIELD_W}
height={FIELD_H}
onMouseMove={handleMouseMove}
onTouchMove={handleTouchMove}
className="w-full rounded-lg border border-neon-cyan/20 cursor-none"
style={{ touchAction: "none" }}
/>
{gameState === "idle" && (
<div className="mt-6 text-center">
<button
type="button"
onClick={startGame}
className="rounded-full border-2 border-neon-cyan px-8 py-3 font-black uppercase text-neon-cyan hover:bg-neon-cyan/10 transition-all"
>
Start Game
</button>
<p className="mt-3 text-xs text-white/30">Move your mouse over the field to control the left paddle.</p>
</div>
)}
{gameState === "playing" && (
<div className="mt-4 text-center text-xs text-white/20">
Rally: {rally} hits
</div>
)}
{gameState === "over" && (
<div className="mt-6 text-center">
<div className="mb-3 text-2xl font-black uppercase">
{displayScore.player >= 7 ? (
<span className="text-neon-cyan">You Win</span>
) : (
<span className="text-neon-purple">CPU Wins</span>
)}
</div>
<div className="mb-4 text-sm text-white/50">
Score: {displayScore.player} {displayScore.cpu} · Rally: {rallyRef.current}
{rallyRef.current > 0 && rallyRef.current >= highScore && (
<span className="ml-2 text-neon-cyan">New record!</span>
)}
</div>
<button
type="button"
onClick={startGame}
className="rounded-full border-2 border-neon-cyan px-8 py-3 font-black uppercase text-neon-cyan hover:bg-neon-cyan/10 transition-all"
>
Play Again
</button>
</div>
)}
<div className="mt-6 flex justify-center gap-6 text-[10px] text-white/20">
<Link href="/" className="hover:text-white/50">Hub</Link>
<Link href="/arcade" className="hover:text-white/50">Arcade</Link>
<Link href="/drops" className="hover:text-white/50">Shadow Lotto</Link>
</div>
</div>
</div>
);
}