added custom cursor, secrets :3

This commit is contained in:
Unknown 2025-02-18 16:12:42 -07:00 committed by End
parent aa33bc3e22
commit efa1cf6737
No known key found for this signature in database
13 changed files with 259 additions and 174 deletions

View file

@ -4,7 +4,6 @@ import Navbar from "@/components/Navbar";
import AboutPage from "@/pages/AboutPage";
import ProjectsPage from "@/pages/ProjectsPage";
import APCSPPage from "@/pages/APCSPPage";
import ParallaxPage from "@/pages/ParallaxPage";
import FoxGame from "@/games/fox-adventure/components/FoxGame";
import { useState, useEffect } from "react";
@ -39,7 +38,6 @@ const App = () => {
return (
<Router>
<div className={`min-h-screen bg-background-primary ${isGameActive ? 'game-active' : ''}`}>
{/* Background Logo */}
<div className="fixed inset-0 z-behind pointer-events-none">
<div className="absolute inset-0">
<img
@ -50,7 +48,6 @@ const App = () => {
</div>
</div>
{/* Main Content */}
<div className="relative">
<Navbar />
<main className="content-wrapper section-spacing">
@ -58,7 +55,6 @@ const App = () => {
<Route path="/" element={<AboutPage />} />
<Route path="/projects" element={<ProjectsPage />} />
<Route path="/apcsp" element={<APCSPPage />} />
{/* <Route path="/parallax" element={<ParallaxPage />} /> */}
<Route path="*" element={
<div className="flex flex-col items-center justify-center min-h-[60vh] space-y-4">
<h1 className="text-4xl font-bold text-glow">404: Page Not Found</h1>
@ -69,7 +65,6 @@ const App = () => {
</main>
</div>
{/* Fox Game Overlay */}
{isGameActive && <FoxGame />}
</div>
</Router>

View file

@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
<!-- Fox head shape -->
<path d="M16 2 L24 10 L24 24 L8 24 L8 10 Z"
fill="#ff9466"
stroke="#240046"
stroke-width="1.5"/>
<!-- Ears -->
<path d="M8 10 L2 2 L8 8"
fill="#ff9466"
stroke="#240046"
stroke-width="1.5"/>
<path d="M24 10 L30 2 L24 8"
fill="#ff9466"
stroke="#240046"
stroke-width="1.5"/>
<!-- Eyes -->
<circle cx="12" cy="14" r="2" fill="#240046"/>
<circle cx="20" cy="14" r="2" fill="#240046"/>
<!-- Nose -->
<circle cx="16" cy="18" r="1.5" fill="#240046"/>
</svg>

After

Width:  |  Height:  |  Size: 658 B

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="#ffc6e5" stroke="#ff9466" stroke-width="2" d="M12,4 C14,4 15,6 15,8 C15,10 14,12 12,12 C10,12 9,10 9,8 C9,6 10,4 12,4 Z"/>
<path fill="#ffc6e5" stroke="#ff9466" stroke-width="2" d="M20,4 C22,4 23,6 23,8 C23,10 22,12 20,12 C18,12 17,10 17,8 C17,6 18,4 20,4 Z"/>
<path fill="#ffc6e5" stroke="#ff9466" stroke-width="2" d="M16,8 C20,8 23,12 23,16 C23,20 20,24 16,24 C12,24 9,20 9,16 C9,12 12,8 16,8 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 515 B

View file

@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
<!-- Tail base -->
<path d="M4 16 Q8 8 16 8 Q24 8 28 16 Q24 24 16 24 Q8 24 4 16"
fill="#ff9466"
stroke="#240046"
stroke-width="1.5">
<animateTransform
attributeName="transform"
type="rotate"
from="0 16 16"
to="360 16 16"
dur="1s"
repeatCount="indefinite"/>
</path>
<!-- Tail tip -->
<circle cx="16" cy="16" r="4"
fill="#ffc6e5"
stroke="#240046"
stroke-width="1.5">
<animate
attributeName="r"
values="4;5;4"
dur="1s"
repeatCount="indefinite"/>
</circle>
</svg>

After

Width:  |  Height:  |  Size: 683 B

View file

@ -1,8 +1,7 @@
import React, { useState } from 'react';
import { useState } from 'react';
import { Monitor, Power, Lock } from 'lucide-react';
const VNCViewer = () => {
const [isFullscreen, setIsFullscreen] = useState(false);
const [isConnected, setIsConnected] = useState(false);
const toggleFullscreen = () => {
@ -10,10 +9,8 @@ const VNCViewer = () => {
if (iframe) {
if (!document.fullscreenElement) {
iframe.requestFullscreen();
setIsFullscreen(true);
} else {
document.exitFullscreen();
setIsFullscreen(false);
}
}
};
@ -21,7 +18,6 @@ const VNCViewer = () => {
return (
<div className="min-h-screen w-full flex items-center justify-center p-4">
<div className="w-full max-w-6xl bg-background-primary/80 backdrop-blur-sm rounded-xl shadow-xl border border-accent-primary/20 overflow-hidden transition-all duration-300 hover:border-accent-neon/40 hover:shadow-accent-primary/20">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-accent-primary/20">
<div className="flex items-center gap-3">
<Monitor className="text-accent-primary" size={24} />
@ -49,7 +45,6 @@ const VNCViewer = () => {
</div>
</div>
{/* VNC Viewer */}
<div className="aspect-video w-full bg-black/50 relative">
{isConnected ? (
<iframe
@ -66,7 +61,6 @@ const VNCViewer = () => {
)}
</div>
{/* Status Bar */}
<div className="p-2 border-t border-accent-primary/20 flex justify-between items-center text-sm text-text-primary/60">
<span>Status: {isConnected ? 'Connected' : 'Disconnected'}</span>
<span>Press ESC to exit fullscreen</span>

View file

@ -1,7 +1,6 @@
// src/games/fox-adventure/components/GameOverlay.tsx
import React from 'react';
import { Play, RotateCcw } from 'lucide-react';
import useGameStore from '../state/gameStore';
import { Play, Pause, RotateCcw } from 'lucide-react';
export const GameOverlay: React.FC = () => {
const { gameStatus, score, startNewGame, resumeGame } = useGameStore();

View file

@ -32,7 +32,7 @@ export const useGameLoop = () => {
if (gameStore.gameStatus === 'PLAYING') {
// Update entities
gameStore.updateEnemies();
gameStore.updateEnemies(deltaTime);
// Spawn collectibles
spawnCollectible();
@ -75,4 +75,5 @@ export const useGameLoop = () => {
}
};
}, []);
};
};

View file

@ -1,62 +1,5 @@
import { create } from 'zustand';
interface Position {
x: number;
y: number;
}
interface PowerUp {
id: string;
type: 'SPEED' | 'SHIELD' | 'MAGNET';
duration: number;
position: Position;
}
interface Collectible {
id: string;
type: 'STAR' | 'GEM' | 'KEY';
value: number;
position: Position;
}
interface Enemy {
id: string;
type: 'WOLF' | 'OWL' | 'HUNTER';
position: Position;
direction: Position;
speed: number;
}
interface PlayerState {
position: Position;
health: number;
speed: number;
powerUps: PowerUp[];
isInvincible: boolean;
hasKey: boolean;
}
interface GameState {
player: PlayerState;
enemies: Enemy[];
collectibles: Collectible[];
powerUps: PowerUp[];
score: number;
level: number;
gameStatus: 'MENU' | 'PLAYING' | 'PAUSED' | 'GAME_OVER';
highScores: number[];
timePlayed: number;
// Actions
movePlayer: (direction: Position) => void;
updateEnemies: () => void;
collectItem: (itemId: string) => void;
takeDamage: (amount: number) => void;
activatePowerUp: (powerUpId: string) => void;
startNewGame: () => void;
pauseGame: () => void;
resumeGame: () => void;
}
import type { GameState, Position } from '@/types/game';
const useGameStore = create<GameState>((set, get) => ({
player: {
@ -76,7 +19,7 @@ const useGameStore = create<GameState>((set, get) => ({
highScores: [],
timePlayed: 0,
movePlayer: (direction) => {
movePlayer: (direction: Position) => {
const { player } = get();
set({
player: {
@ -112,8 +55,8 @@ const useGameStore = create<GameState>((set, get) => ({
set({ enemies: updatedEnemies });
},
collectItem: (itemId) => {
const { collectibles, score, player } = get();
collectItem: (itemId: string) => {
const { collectibles, score } = get();
const item = collectibles.find(c => c.id === itemId);
if (!item) return;
@ -123,7 +66,7 @@ const useGameStore = create<GameState>((set, get) => ({
});
},
takeDamage: (amount) => {
takeDamage: (amount: number) => {
const { player, gameStatus } = get();
if (player.isInvincible) return;
@ -137,7 +80,7 @@ const useGameStore = create<GameState>((set, get) => ({
});
},
activatePowerUp: (powerUpId) => {
activatePowerUp: (powerUpId: string) => {
const { player, powerUps } = get();
const powerUp = powerUps.find(p => p.id === powerUpId);
if (!powerUp) return;
@ -150,7 +93,6 @@ const useGameStore = create<GameState>((set, get) => ({
powerUps: powerUps.filter(p => p.id !== powerUpId)
});
// Reset power-up after duration
setTimeout(() => {
const currentPlayer = get().player;
set({

View file

@ -1,40 +1,104 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background-primary: #1a0b2e;
--background-secondary: #2f1c54;
--accent-primary: #9d4edd;
--accent-neon: #b249f8;
--text-glow: #e0aaff;
--text-primary: #ffffff;
--dark-accent: #240046;
--fox-pink: #ffc6e5;
--fox-pink-glow: #ffadd6;
--fox-orange: #ff9466;
--fox-white: #fff5f9;
:root {
--background-primary: #1a0b2e;
--background-secondary: #2f1c54;
--accent-primary: #9d4edd;
--accent-neon: #b249f8;
--text-glow: #e0aaff;
--text-primary: #ffffff;
--dark-accent: #240046;
--fox-pink: #ffc6e5;
--fox-pink-glow: #ffadd6;
--fox-orange: #ff9466;
--fox-white: #fff5f9;
}
body {
background-color: var(--background-primary);
color: var(--text-primary);
}
}
body {
@apply
font-family: "Inter", sans-serif
bg-background-primary text-text-primary;;
@layer utilities {
.text-glow {
text-shadow: 0 0 10px var(--text-glow);
}
.section-spacing > * + * {
margin-top: 3rem;
}
.card-spacing > * + * {
margin-top: 2rem;
}
.element-spacing > * + * {
margin-top: 1rem;
}
}
/* Global spacing */
.content-wrapper {
@apply
gap-3 px-4 py-2 my-1
items-center rounded-lg
transition-all hover:bg-accent-primary/10 hover:text-accent-neon
flex
hover:bg-accent-primary/10 hover:text-accent-neon;
}
.nav-link.active {
@apply
p-8 mb-8
rounded-xl
transition-all hover:border-accent-neon/40 hover:shadow-lg hover:shadow-accent-primary/10
relative
bg-gradient-card border border-accent-primary/20 hover:border-accent-neon/40 hover:shadow-lg hover:shadow-accent-primary/10;
}
.text-glow {
@layer components {
.content-wrapper {
max-width: 80rem;
margin-left: auto;
margin-right: auto;
padding: 2rem 1rem;
}
.nav-link {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
transition-property: all;
transition-duration: 200ms;
}
.nav-link:hover {
background-color: rgba(157, 78, 221, 0.1);
color: var(--accent-neon);
}
.nav-link.active {
background-color: rgba(157, 78, 221, 0.2);
color: var(--accent-neon);
}
.fox-card {
position: relative;
border-radius: 0.75rem;
padding: 2rem;
transition-property: all;
transition-duration: 300ms;
border: 1px solid rgba(157, 78, 221, 0.2);
background: linear-gradient(135deg, rgba(47, 28, 84, 0.3) 0%, rgba(157, 78, 221, 0.1) 100%);
}
.fox-card:hover {
border-color: rgba(178, 73, 248, 0.4);
box-shadow: 0 0 10px rgba(157, 78, 221, 0.1);
}
.content-grid {
display: grid;
gap: 2rem;
grid-template-columns: repeat(1, minmax(0, 1fr));
}
@media (min-width: 768px) {
.content-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (min-width: 1024px) {
.content-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
}

View file

@ -1,7 +1,8 @@
/* Default cursor for all elements */
* {
cursor: url('@/assets/cursors/default.svg') 16 16, auto;
cursor: url('@/assets/cursors/default.svg') 16 16, auto;
}
/* Interactive elements cursor */
a,
button,
@ -11,36 +12,36 @@ input[type="button"],
select,
.interactive,
.nav-link {
cursor: url('@/assets/cursors/paw.svg') 16 16, pointer;
cursor: url('@/assets/cursors/paw.svg') 16 16, pointer;
}
/* Loading state cursor */
.loading,
:disabled,
[aria-busy="true"] {
cursor: url('@/assets/cursors/tail-loading.svg') 16 16, progress;
cursor: url('@/assets/cursors/tail-loading.svg') 16 16, progress;
}
/* Hover effects for interactive elements */
a:hover,
button:hover,
[role="button"]:hover,
.nav-link:hover {
/* Add a subtle glow effect on hover */
filter: drop-shadow(0 0 4px var(--fox-pink-glow));
transition: filter 0.3s ease;
filter: drop-shadow(0 0 4px var(--fox-pink-glow));
transition: filter 0.3s ease;
}
/* Custom cursor regions */
.text-select {
cursor: text;
cursor: text;
}
.resize {
cursor: nw-resize;
}
/* Ensure cursors work with transform effects */
* {
cursor-position: fixed;
cursor: nw-resize;
}
/* Prevent cursor inheritance in certain cases */
iframe,
canvas {
cursor: inherit;
cursor: inherit;
}

View file

@ -1,13 +1,44 @@
@layer utilities {
.animated-bg {
@apply
overflow-hidden; } { content: ''
.animated-bg::before
relative
bg-gradient-primary .animated-bg::before;
@apply
inset-0 opacity-50; animation: gradientShift 15s ease infinite
absolute
bg-gradient-primary;
.animated-bg {
@apply overflow-hidden;
}
.animated-bg::before {
content: '';
@apply absolute inset-0 opacity-50;
@apply bg-gradient-primary;
animation: gradientShift 15s ease infinite;
}
.fox-ear {
position: absolute;
width: 30px;
height: 30px;
background: var(--fox-pink);
opacity: 0.1;
transition: opacity 0.3s ease;
}
.fox-ear-left {
top: -15px;
left: -15px;
transform: rotate(45deg);
border-radius: 0 0 0 15px;
}
.fox-ear-right {
top: -15px;
right: -15px;
transform: rotate(-45deg);
border-radius: 0 0 15px 0;
}
}
.fox-ear {
@keyframes gradientShift {
0%, 100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}

View file

@ -1,27 +1,32 @@
export interface Position {
x: number;
y: number;
}
export interface PowerUp {
id: string;
type: 'SPEED' | 'SHIELD' | 'MAGNET';
duration: number;
position: { x: number; y: number };
position: Position;
}
export interface Collectible {
id: string;
type: 'STAR' | 'GEM' | 'KEY';
value: number;
position: { x: number; y: number };
position: Position;
}
export interface Enemy {
id: string;
type: 'WOLF' | 'OWL' | 'HUNTER';
position: { x: number; y: number };
direction: { x: number; y: number };
position: Position;
direction: Position;
speed: number;
}
export interface PlayerState {
position: { x: number; y: number };
position: Position;
health: number;
speed: number;
powerUps: PowerUp[];
@ -39,4 +44,14 @@ export interface GameState {
gameStatus: 'MENU' | 'PLAYING' | 'PAUSED' | 'GAME_OVER';
highScores: number[];
timePlayed: number;
}
// Actions
movePlayer: (direction: Position) => void;
updateEnemies: (deltaTime?: number) => void;
collectItem: (itemId: string) => void;
takeDamage: (amount: number) => void;
activatePowerUp: (powerUpId: string) => void;
startNewGame: () => void;
pauseGame: () => void;
resumeGame: () => void;
}

View file

@ -19,40 +19,31 @@ export default {
'fox-orange': '#ff9466',
'fox-white': '#fff5f9',
},
animation: {
'bounce-slow': 'bounce 3s linear infinite',
'glow': 'glow 2s ease-in-out infinite',
'float': 'float 3s ease-in-out infinite',
'spin-slow': 'spin 3s linear infinite',
'wag': 'wag 1s ease-in-out infinite',
'score-popup': 'scorePopup 0.5s ease-out forwards',
'particle-fade': 'particleFade 1s ease-out forwards',
'menu-enter': 'menuEnter 0.3s ease-out forwards',
'menu-exit': 'menuExit 0.3s ease-in forwards',
'player-idle': 'playerIdle 2s ease-in-out infinite',
'player-hit': 'playerHit 0.5s ease-in-out',
},
keyframes: {
glow: {
'0%, 100%': { filter: 'drop-shadow(0 0 2px var(--accent-neon))' },
'50%': { filter: 'drop-shadow(0 0 8px var(--accent-neon))' },
},
float: {
'0%, 100%': { transform: 'translateY(0)' },
'50%': { transform: 'translateY(-10px)' },
},
wag: {
'0%, 100%': { transform: 'rotate(-10deg)' },
'50%': { transform: 'rotate(10deg)' },
},
},
backgroundImage: {
'gradient-game': 'linear-gradient(135deg, var(--background-primary) 0%, var(--background-secondary) 100%)',
'gradient-card': 'linear-gradient(135deg, rgba(47, 28, 84, 0.3) 0%, rgba(157, 78, 221, 0.1) 100%)',
},
backdropFilter: {
'game': 'blur(8px)',
transitionProperty: {
'all': 'all',
},
boxShadow: {
'accent': '0 0 10px var(--accent-primary)',
},
opacity: {
'10': '0.1',
'20': '0.2',
'40': '0.4',
}
},
},
plugins: [],
safelist: [
'bg-accent-primary/10',
'bg-accent-primary/20',
'border-accent-primary/20',
'border-accent-neon/40',
'shadow-accent-primary/10',
'hover:bg-accent-primary/10',
'hover:border-accent-neon/40',
'hover:shadow-accent-primary/10'
]
}