Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Shadow Tag Tactics - Pyro-Amplified Edition</title>
- <style>
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- }
- body {
- background: radial-gradient(ellipse at center, #0a0a15 0%, #000000 100%);
- color: #fff;
- font-family: 'Segoe UI', system-ui, sans-serif;
- overflow: hidden;
- cursor: crosshair;
- display: flex;
- flex-direction: column;
- align-items: center;
- min-height: 100vh;
- position: relative;
- }
- body::before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: linear-gradient(45deg,
- transparent 30%,
- rgba(138, 43, 226, 0.05) 50%,
- transparent 70%);
- animation: shimmerFlow 3s linear infinite;
- pointer-events: none;
- }
- @keyframes shimmerFlow {
- 0% { transform: translateX(-100%); }
- 100% { transform: translateX(100%); }
- }
- header {
- text-align: center;
- padding: 20px;
- width: 100%;
- background: linear-gradient(180deg, rgba(138, 43, 226, 0.2) 0%, transparent 100%);
- position: relative;
- z-index: 10;
- }
- h1 {
- font-size: 3rem;
- margin-bottom: 10px;
- text-shadow: 0 0 20px rgba(255, 255, 255, 0.5);
- letter-spacing: 3px;
- background: linear-gradient(90deg, #ffd700, #ff69b4, #8a2be2);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- animation: glow 2s ease-in-out infinite alternate, pyroWave 4s ease-in-out infinite;
- }
- @keyframes glow {
- from { filter: drop-shadow(0 0 20px rgba(255, 215, 0, 0.5)); }
- to { filter: drop-shadow(0 0 30px rgba(138, 43, 226, 0.8)); }
- }
- @keyframes pyroWave {
- 0%, 100% { transform: scale(1) rotate(0deg); }
- 25% { transform: scale(1.02) rotate(-1deg); }
- 75% { transform: scale(1.02) rotate(1deg); }
- }
- .game-wrapper {
- display: flex;
- gap: 30px;
- align-items: center;
- justify-content: center;
- flex: 1;
- padding: 20px;
- position: relative;
- }
- #gameCanvas {
- width: 800px;
- height: 600px;
- background: radial-gradient(circle at center, #0f0f1a 0%, #050508 100%);
- border: 3px solid #2a2a3e;
- border-radius: 20px;
- box-shadow:
- 0 0 50px rgba(138, 43, 226, 0.3),
- inset 0 0 100px rgba(0, 0, 0, 0.8),
- 0 0 100px rgba(255, 105, 180, 0.1);
- position: relative;
- }
- .controls-panel {
- background: rgba(0, 0, 0, 0.8);
- padding: 25px;
- border-radius: 15px;
- border: 2px solid #8a2be2;
- backdrop-filter: blur(10px);
- min-width: 250px;
- box-shadow: 0 0 30px rgba(138, 43, 226, 0.3);
- position: relative;
- overflow: hidden;
- }
- .controls-panel::before {
- content: '';
- position: absolute;
- top: -50%;
- left: -50%;
- width: 200%;
- height: 200%;
- background: radial-gradient(circle, rgba(255, 105, 180, 0.1) 0%, transparent 70%);
- animation: rotateGlow 10s linear infinite;
- pointer-events: none;
- }
- @keyframes rotateGlow {
- from { transform: rotate(0deg); }
- to { transform: rotate(360deg); }
- }
- .stats {
- margin-bottom: 25px;
- position: relative;
- z-index: 1;
- }
- .stat-item {
- margin-bottom: 15px;
- padding: 10px;
- background: linear-gradient(90deg, rgba(138, 43, 226, 0.2) 0%, rgba(255, 105, 180, 0.2) 100%);
- border-radius: 10px;
- position: relative;
- overflow: hidden;
- }
- .stat-item::after {
- content: '';
- position: absolute;
- top: 0;
- left: -100%;
- width: 100%;
- height: 100%;
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
- animation: statShine 3s ease-in-out infinite;
- }
- @keyframes statShine {
- 0% { left: -100%; }
- 50%, 100% { left: 100%; }
- }
- .stat-label {
- font-size: 14px;
- color: #ff69b4;
- margin-bottom: 5px;
- text-transform: uppercase;
- letter-spacing: 1px;
- }
- .stat-value {
- font-size: 24px;
- font-weight: bold;
- color: #ffd700;
- text-shadow: 0 0 10px rgba(255, 215, 0, 0.5);
- display: inline-block;
- }
- #score {
- font-size: 32px;
- animation: pulse 2s infinite, scoreFlash 0.5s ease-in-out;
- }
- @keyframes pulse {
- 0%, 100% { transform: scale(1); }
- 50% { transform: scale(1.05); }
- }
- @keyframes scoreFlash {
- 0% { color: #ffd700; }
- 50% { color: #fff; }
- 100% { color: #ffd700; }
- }
- .lamp-controls {
- margin-top: 20px;
- padding-top: 20px;
- border-top: 2px solid rgba(138, 43, 226, 0.5);
- position: relative;
- z-index: 1;
- }
- .lamp-controls h3 {
- margin-bottom: 15px;
- color: #ffd700;
- text-shadow: 0 0 10px rgba(255, 215, 0, 0.5);
- animation: lampTitleGlow 2s ease-in-out infinite alternate;
- }
- @keyframes lampTitleGlow {
- from { text-shadow: 0 0 10px rgba(255, 215, 0, 0.5); }
- to { text-shadow: 0 0 20px rgba(255, 215, 0, 0.8), 0 0 30px rgba(255, 105, 180, 0.5); }
- }
- .lamp-buttons {
- display: flex;
- gap: 10px;
- justify-content: center;
- }
- .lamp-btn {
- width: 60px;
- height: 60px;
- border: 2px solid #ffd700;
- background: radial-gradient(circle, rgba(255, 215, 0, 0.3) 0%, rgba(138, 43, 226, 0.2) 100%);
- color: #ffd700;
- font-size: 28px;
- font-weight: bold;
- border-radius: 50%;
- cursor: pointer;
- transition: all 0.3s ease;
- box-shadow: 0 0 20px rgba(255, 215, 0, 0.3);
- position: relative;
- overflow: hidden;
- }
- .lamp-btn::before {
- content: '';
- position: absolute;
- top: 50%;
- left: 50%;
- width: 0;
- height: 0;
- background: radial-gradient(circle, rgba(255, 255, 255, 0.5), transparent);
- transform: translate(-50%, -50%);
- transition: width 0.3s, height 0.3s;
- }
- .lamp-btn:hover {
- transform: scale(1.1) rotate(5deg);
- box-shadow: 0 0 30px rgba(255, 215, 0, 0.6), 0 0 50px rgba(255, 105, 180, 0.3);
- background: radial-gradient(circle, rgba(255, 215, 0, 0.5) 0%, rgba(138, 43, 226, 0.3) 100%);
- }
- .lamp-btn:hover::before {
- width: 100%;
- height: 100%;
- }
- .lamp-btn:active {
- transform: scale(0.95);
- }
- .lamp-count {
- text-align: center;
- margin: 15px 0;
- font-size: 18px;
- color: #ff69b4;
- text-shadow: 0 0 5px rgba(255, 105, 180, 0.5);
- }
- .game-status {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background: rgba(0, 0, 0, 0.95);
- padding: 30px 50px;
- border-radius: 20px;
- text-align: center;
- z-index: 1000;
- box-shadow: 0 0 50px rgba(255, 0, 0, 0.5), 0 0 100px rgba(138, 43, 226, 0.3);
- border: 3px solid #ff0000;
- display: none;
- }
- .game-status.show {
- display: block;
- animation: zoomIn 0.5s ease, statusPulse 2s ease-in-out infinite;
- }
- @keyframes zoomIn {
- from {
- transform: translate(-50%, -50%) scale(0) rotate(-180deg);
- opacity: 0;
- }
- to {
- transform: translate(-50%, -50%) scale(1) rotate(0deg);
- opacity: 1;
- }
- }
- @keyframes statusPulse {
- 0%, 100% { box-shadow: 0 0 50px rgba(255, 0, 0, 0.5), 0 0 100px rgba(138, 43, 226, 0.3); }
- 50% { box-shadow: 0 0 70px rgba(255, 0, 0, 0.8), 0 0 140px rgba(138, 43, 226, 0.5); }
- }
- .game-status h2 {
- font-size: 36px;
- color: #ff0000;
- margin-bottom: 20px;
- text-shadow: 0 0 20px rgba(255, 0, 0, 0.8);
- animation: statusTextPulse 1s ease-in-out infinite;
- }
- @keyframes statusTextPulse {
- 0%, 100% { transform: scale(1); }
- 50% { transform: scale(1.05); }
- }
- .restart-btn {
- background: linear-gradient(90deg, #8a2be2, #ff69b4, #ffd700);
- color: white;
- border: none;
- padding: 15px 40px;
- border-radius: 10px;
- font-size: 18px;
- font-weight: bold;
- cursor: pointer;
- transition: all 0.3s ease;
- margin-top: 20px;
- text-shadow: 0 2px 4px rgba(0,0,0,0.5);
- position: relative;
- overflow: hidden;
- }
- .restart-btn::before {
- content: '';
- position: absolute;
- top: 0;
- left: -100%;
- width: 100%;
- height: 100%;
- background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
- transition: left 0.5s;
- }
- .restart-btn:hover {
- transform: scale(1.05) rotate(-2deg);
- box-shadow: 0 0 30px rgba(138, 43, 226, 0.6), 0 5px 15px rgba(0,0,0,0.3);
- }
- .restart-btn:hover::before {
- left: 100%;
- }
- .warning-phase {
- position: absolute;
- top: 150px;
- left: 50%;
- transform: translateX(-50%);
- background: linear-gradient(45deg, rgba(255, 0, 0, 0.9), rgba(255, 105, 180, 0.9));
- padding: 15px 30px;
- border-radius: 10px;
- font-size: 20px;
- font-weight: bold;
- display: none;
- animation: warningPulse 1s infinite, warningShake 0.5s infinite;
- z-index: 500;
- box-shadow: 0 0 50px rgba(255, 0, 0, 0.8);
- }
- @keyframes warningPulse {
- 0%, 100% {
- transform: translateX(-50%) scale(1);
- box-shadow: 0 0 20px rgba(255, 0, 0, 0.8);
- }
- 50% {
- transform: translateX(-50%) scale(1.05);
- box-shadow: 0 0 40px rgba(255, 0, 0, 1);
- }
- }
- @keyframes warningShake {
- 0%, 100% { transform: translateX(-50%) rotate(0deg); }
- 25% { transform: translateX(-52%) rotate(-1deg); }
- 75% { transform: translateX(-48%) rotate(1deg); }
- }
- .warning-phase.show {
- display: block;
- }
- .ability-indicator {
- position: absolute;
- bottom: 10px;
- left: 50%;
- transform: translateX(-50%);
- display: flex;
- gap: 10px;
- z-index: 100;
- }
- .ability-icon {
- width: 40px;
- height: 40px;
- background: rgba(138, 43, 226, 0.3);
- border: 2px solid #8a2be2;
- border-radius: 8px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 20px;
- color: #ff69b4;
- box-shadow: 0 0 15px rgba(138, 43, 226, 0.5);
- animation: abilityFloat 3s ease-in-out infinite;
- }
- @keyframes abilityFloat {
- 0%, 100% { transform: translateY(0); }
- 50% { transform: translateY(-5px); }
- }
- .ability-icon.active {
- background: rgba(255, 105, 180, 0.5);
- border-color: #ff69b4;
- animation: abilityActive 0.5s ease-in-out infinite alternate;
- }
- @keyframes abilityActive {
- from { box-shadow: 0 0 20px rgba(255, 105, 180, 0.8); }
- to { box-shadow: 0 0 40px rgba(255, 105, 180, 1); }
- }
- .pyro-particle {
- position: absolute;
- width: 4px;
- height: 4px;
- background: radial-gradient(circle, #fff, transparent);
- border-radius: 50%;
- pointer-events: none;
- animation: pyroFloat 2s linear forwards;
- }
- @keyframes pyroFloat {
- 0% {
- transform: translateY(0) scale(1) rotate(0deg);
- opacity: 1;
- }
- 100% {
- transform: translateY(-100px) scale(0) rotate(720deg);
- opacity: 0;
- }
- }
- .shadow-trail {
- position: absolute;
- width: 10px;
- height: 10px;
- background: radial-gradient(circle, rgba(75, 0, 130, 0.5), transparent);
- border-radius: 50%;
- pointer-events: none;
- animation: trailFade 1s linear forwards;
- }
- @keyframes trailFade {
- 0% { opacity: 0.5; transform: scale(1); }
- 100% { opacity: 0; transform: scale(0.5); }
- }
- .light-spark {
- position: absolute;
- width: 2px;
- height: 2px;
- background: #ffd700;
- border-radius: 50%;
- pointer-events: none;
- animation: sparkLife 0.5s linear forwards;
- }
- @keyframes sparkLife {
- 0% {
- transform: scale(1) rotate(0deg);
- opacity: 1;
- }
- 100% {
- transform: scale(0) rotate(180deg) translateY(-20px);
- opacity: 0;
- }
- }
- </style>
- </head>
- <body>
- <header>
- <h1>Shadow Tag Tactics</h1>
- <p style="color: #ff69b4; font-size: 18px; animation: textGlow 2s ease-in-out infinite alternate;">
- Outwit your rogue shadows - They stretch-metch and spiral away!
- </p>
- </header>
- <div class="game-wrapper">
- <canvas id="gameCanvas"></canvas>
- <div class="controls-panel">
- <div class="stats">
- <div class="stat-item">
- <div class="stat-label">SCORE</div>
- <div class="stat-value" id="score">0</div>
- </div>
- <div class="stat-item">
- <div class="stat-label">TIME</div>
- <div class="stat-value" id="time">0s</div>
- </div>
- <div class="stat-item">
- <div class="stat-label">SHADOWS DESTROYED</div>
- <div class="stat-value" id="shadowsDestroyed">0</div>
- </div>
- <div class="stat-item">
- <div class="stat-label">SHADOWS ESCAPED</div>
- <div class="stat-value" id="shadowsEscaped">0</div>
- </div>
- </div>
- <div class="lamp-controls">
- <h3>🔦 Light Control</h3>
- <div class="lamp-buttons">
- <button class="lamp-btn" id="addLamp">+</button>
- <button class="lamp-btn" id="removeLamp">−</button>
- </div>
- <div class="lamp-count" id="lampCount">Lamps: 3</div>
- </div>
- </div>
- <div class="ability-indicator">
- <div class="ability-icon" title="Stretch">🌊</div>
- <div class="ability-icon" title="Spiral">🌀</div>
- <div class="ability-icon" title="Dampen">❄️</div>
- </div>
- </div>
- <div class="warning-phase" id="warningPhase">
- ⚠️ SHADOWS NOW TRYING TO ESCAPE! ⚠️
- </div>
- <div class="game-status" id="gameStatus">
- <h2 id="statusTitle">GAME OVER</h2>
- <p id="statusMessage" style="font-size: 20px; margin: 20px 0;">The shadows have overwhelmed you!</p>
- <p id="finalStats" style="color: #ffd700; font-size: 18px;"></p>
- <button class="restart-btn" id="restartBtn">PLAY AGAIN</button>
- </div>
- <script>
- class PyroAmplifiedShadowTag {
- constructor() {
- this.canvas = document.getElementById('gameCanvas');
- this.ctx = this.canvas.getContext('2d');
- this.canvas.width = 800;
- this.canvas.height = 600;
- this.score = 0;
- this.shadowsDestroyed = 0;
- this.shadowsEscaped = 0;
- this.gameTime = 0;
- this.startTime = Date.now();
- this.escapePhase = false;
- this.escapePhaseTime = 25000 + Math.random() * 10000;
- this.gameRunning = true;
- this.lamps = [];
- this.shadows = [];
- this.particles = [];
- this.trails = [];
- this.sparks = [];
- this.draggedLamp = null;
- this.mouseX = 0;
- this.mouseY = 0;
- this.abilityTimers = {
- stretch: 0,
- spiral: 0,
- dampen: 0
- };
- this.pallameter = 1.0;
- this.fluffychup = 0;
- this.tremourLevel = 0;
- this.init();
- }
- init() {
- this.createLamp(200, 150);
- this.createLamp(600, 150);
- this.createLamp(400, 450);
- for (let i = 0; i < 5; i++) {
- this.createShadow();
- }
- this.setupEventListeners();
- this.gameLoop();
- this.prosticate();
- }
- prosticate() {
- this.pallameter = Math.sin(Date.now() * 0.001) * 0.5 + 1.5;
- this.fluffychup = (this.fluffychup + 1) % 200;
- this.tremourLevel = Math.cos(Date.now() * 0.002) * 0.3;
- }
- createLamp(x, y) {
- this.lamps.push({
- x: x || Math.random() * (this.canvas.width - 100) + 50,
- y: y || Math.random() * (this.canvas.height - 100) + 50,
- radius: 120,
- intensity: 1,
- pulsePhase: Math.random() * Math.PI * 2,
- eyeOffsetX: 0,
- eyeOffsetY: 0,
- mouthOpen: 0,
- expression: 'happy',
- blinkTimer: Math.random() * 100
- });
- this.updateLampCount();
- }
- removeLamp() {
- if (this.lamps.length > 1) {
- const removed = this.lamps.pop();
- this.createExplosionParticles(removed.x, removed.y, '#ffd700');
- this.updateLampCount();
- }
- }
- createShadow() {
- const shadow = {
- x: 400 + (Math.random() - 0.5) * 200,
- y: 300 + (Math.random() - 0.5) * 200,
- vx: (Math.random() - 0.5) * 2,
- vy: (Math.random() - 0.5) * 2,
- size: 20 + Math.random() * 20,
- originalSize: 20 + Math.random() * 20,
- health: 100,
- maxHealth: 100,
- attracted: false,
- escaping: false,
- opacity: 1,
- pulsePhase: Math.random() * Math.PI * 2,
- // Abilities
- stretchActive: false,
- stretchFactor: 1,
- stretchTimer: 0,
- spiralActive: false,
- spiralAngle: 0,
- spiralSpeed: 0.1,
- dampenActive: false,
- dampenTimer: 0,
- // Character features
- eyeOffsetX: (Math.random() - 0.5) * 5,
- eyeOffsetY: (Math.random() - 0.5) * 5,
- mouthCurve: Math.random() * 0.5,
- blinkTimer: Math.random() * 200,
- personality: ['mischievous', 'scared', 'angry', 'sneaky'][Math.floor(Math.random() * 4)]
- };
- this.shadows.push(shadow);
- }
- setupEventListeners() {
- this.canvas.addEventListener('mousemove', (e) => {
- const rect = this.canvas.getBoundingClientRect();
- this.mouseX = e.clientX - rect.left;
- this.mouseY = e.clientY - rect.top;
- if (this.draggedLamp) {
- this.draggedLamp.x = Math.max(50, Math.min(this.mouseX, this.canvas.width - 50));
- this.draggedLamp.y = Math.max(50, Math.min(this.mouseY, this.canvas.height - 50));
- this.createSparkTrail(this.draggedLamp.x, this.draggedLamp.y);
- }
- });
- this.canvas.addEventListener('mousedown', (e) => {
- const rect = this.canvas.getBoundingClientRect();
- const x = e.clientX - rect.left;
- const y = e.clientY - rect.top;
- for (let lamp of this.lamps) {
- const dist = Math.sqrt((x - lamp.x) ** 2 + (y - lamp.y) ** 2);
- if (dist < 30) {
- this.draggedLamp = lamp;
- lamp.expression = 'surprised';
- break;
- }
- }
- });
- this.canvas.addEventListener('mouseup', () => {
- if (this.draggedLamp) {
- this.draggedLamp.expression = 'happy';
- this.draggedLamp = null;
- }
- });
- document.getElementById('addLamp').addEventListener('click', () => {
- if (this.lamps.length < 8) {
- this.createLamp();
- this.createExplosionParticles(400, 300, '#ffd700');
- }
- });
- document.getElementById('removeLamp').addEventListener('click', () => {
- this.removeLamp();
- });
- document.getElementById('restartBtn').addEventListener('click', () => {
- this.restart();
- });
- }
- updateLampCount() {
- document.getElementById('lampCount').textContent = `Lamps: ${this.lamps.length}`;
- }
- activateShadowAbility(shadow) {
- const rand = Math.random();
- if (rand < 0.02 && !shadow.stretchActive) {
- // Stretch ability - metch letch ka-tetch
- shadow.stretchActive = true;
- shadow.stretchTimer = 100;
- shadow.stretchFactor = 1 + Math.random() * 2;
- this.updateAbilityIndicator(0, true);
- } else if (rand < 0.04 && !shadow.spiralActive) {
- // Spiral ability
- shadow.spiralActive = true;
- shadow.spiralAngle = 0;
- shadow.spiralSpeed = 0.15 + Math.random() * 0.1;
- this.updateAbilityIndicator(1, true);
- } else if (rand < 0.06 && !shadow.dampenActive) {
- // Dampen ability - tremourly slow
- shadow.dampenActive = true;
- shadow.dampenTimer = 150;
- this.tremourLevel += 0.5;
- // Apply to nearby allies
- for (let ally of this.shadows) {
- if (ally !== shadow) {
- const dist = Math.sqrt((shadow.x - ally.x) ** 2 + (shadow.y - ally.y) ** 2);
- if (dist < 100) {
- ally.dampenActive = true;
- ally.dampenTimer = 75;
- }
- }
- }
- this.updateAbilityIndicator(2, true);
- }
- }
- updateAbilityIndicator(index, active) {
- const icons = document.querySelectorAll('.ability-icon');
- if (icons[index]) {
- if (active) {
- icons[index].classList.add('active');
- setTimeout(() => icons[index].classList.remove('active'), 1000);
- }
- }
- }
- updateShadows() {
- this.shadows = this.shadows.filter(shadow => {
- // Update abilities
- this.activateShadowAbility(shadow);
- // Update stretch
- if (shadow.stretchActive) {
- shadow.stretchTimer--;
- if (shadow.stretchTimer <= 0) {
- shadow.stretchActive = false;
- shadow.stretchFactor = 1;
- }
- }
- // Update spiral
- if (shadow.spiralActive) {
- shadow.spiralAngle += shadow.spiralSpeed;
- if (shadow.spiralAngle > Math.PI * 4) {
- shadow.spiralActive = false;
- shadow.spiralAngle = 0;
- }
- }
- // Update dampen
- if (shadow.dampenActive) {
- shadow.dampenTimer--;
- if (shadow.dampenTimer <= 0) {
- shadow.dampenActive = false;
- this.tremourLevel = Math.max(0, this.tremourLevel - 0.5);
- }
- }
- // Update personality-based behavior
- shadow.blinkTimer--;
- if (shadow.blinkTimer <= 0) {
- shadow.blinkTimer = 100 + Math.random() * 100;
- }
- // Check proximity to lamps
- let nearestLamp = null;
- let nearestDist = Infinity;
- for (let lamp of this.lamps) {
- const dist = Math.sqrt((shadow.x - lamp.x) ** 2 + (shadow.y - lamp.y) ** 2);
- if (dist < nearestDist) {
- nearestDist = dist;
- nearestLamp = lamp;
- }
- }
- // Moths to flame behavior with abilities
- if (nearestDist < nearestLamp.radius * this.pallameter) {
- shadow.attracted = true;
- nearestLamp.expression = 'determined';
- if (!shadow.dampenActive) {
- // Normal attraction
- const dx = nearestLamp.x - shadow.x;
- const dy = nearestLamp.y - shadow.y;
- const angle = Math.atan2(dy, dx);
- if (shadow.spiralActive) {
- // Spiral movement towards lamp
- const spiralX = Math.cos(angle + shadow.spiralAngle) * 3;
- const spiralY = Math.sin(angle + shadow.spiralAngle) * 3;
- shadow.vx = spiralX;
- shadow.vy = spiralY;
- } else {
- shadow.vx = Math.cos(angle) * 3;
- shadow.vy = Math.sin(angle) * 3;
- }
- } else {
- // Dampened movement - tremourly slow, spiraly away
- const dx = shadow.x - nearestLamp.x;
- const dy = shadow.y - nearestLamp.y;
- const angle = Math.atan2(dy, dx);
- shadow.vx = Math.cos(angle + this.tremourLevel) * 0.5;
- shadow.vy = Math.sin(angle + this.tremourLevel) * 0.5;
- }
- // Take damage from light
- shadow.health -= shadow.dampenActive ? 0.5 : 2;
- // Create trail effect
- if (Math.random() < 0.3) {
- this.createShadowTrail(shadow.x, shadow.y);
- }
- if (shadow.health <= 0) {
- // Shadow destroyed
- nearestLamp.expression = 'victorious';
- setTimeout(() => nearestLamp.expression = 'happy', 1000);
- this.createExplosionParticles(shadow.x, shadow.y, shadow.personality === 'angry' ? '#ff0000' : '#8a2be2');
- this.score += 10;
- this.shadowsDestroyed++;
- this.updateStats();
- // Spawn new shadow
- setTimeout(() => {
- if (this.gameRunning) this.createShadow();
- }, 1000);
- return false;
- }
- } else if (this.escapePhase && !shadow.attracted) {
- shadow.escaping = true;
- nearestLamp.expression = 'worried';
- // Calculate escape vector
- const edgeDists = [
- shadow.x,
- this.canvas.width - shadow.x,
- shadow.y,
- this.canvas.height - shadow.y
- ];
- const minEdge = Math.min(...edgeDists);
- if (shadow.stretchActive) {
- // Stretch towards exit
- shadow.size = shadow.originalSize * shadow.stretchFactor;
- }
- if (minEdge === edgeDists[0]) shadow.vx = -2 - this.fluffychup * 0.01;
- else if (minEdge === edgeDists[1]) shadow.vx = 2 + this.fluffychup * 0.01;
- else if (minEdge === edgeDists[2]) shadow.vy = -2 - this.fluffychup * 0.01;
- else if (minEdge === edgeDists[3]) shadow.vy = 2 + this.fluffychup * 0.01;
- } else {
- // Random wandering with personality
- switch(shadow.personality) {
- case 'mischievous':
- shadow.vx += (Math.random() - 0.5) * 0.8;
- shadow.vy += (Math.random() - 0.5) * 0.8;
- break;
- case 'scared':
- shadow.vx += (Math.random() - 0.5) * 0.3;
- shadow.vy += (Math.random() - 0.5) * 0.3;
- break;
- case 'angry':
- shadow.vx += (Math.random() - 0.5) * 1;
- shadow.vy += (Math.random() - 0.5) * 1;
- break;
- case 'sneaky':
- shadow.vx += Math.sin(Date.now() * 0.001) * 0.5;
- shadow.vy += Math.cos(Date.now() * 0.001) * 0.5;
- break;
- }
- shadow.vx *= 0.95;
- shadow.vy *= 0.95;
- }
- // Apply physics with stretch modification
- const velocityMod = shadow.stretchActive ? 1.5 : 1;
- shadow.x += shadow.vx * velocityMod;
- shadow.y += shadow.vy * velocityMod;
- // Check escape
- if (shadow.escaping && (
- shadow.x < -shadow.size ||
- shadow.x > this.canvas.width + shadow.size ||
- shadow.y < -shadow.size ||
- shadow.y > this.canvas.height + shadow.size
- )) {
- this.score -= 5;
- this.shadowsEscaped++;
- this.updateStats();
- // Lamps react to escape
- for (let lamp of this.lamps) {
- lamp.expression = 'sad';
- setTimeout(() => lamp.expression = 'happy', 2000);
- }
- if (this.score <= -1) {
- this.gameOver();
- }
- setTimeout(() => {
- if (this.gameRunning) this.createShadow();
- }, 500);
- return false;
- }
- // Boundary behavior
- if (!this.escapePhase) {
- if (shadow.x < shadow.size) {
- shadow.x = shadow.size;
- shadow.vx *= -0.8;
- }
- if (shadow.x > this.canvas.width - shadow.size) {
- shadow.x = this.canvas.width - shadow.size;
- shadow.vx *= -0.8;
- }
- if (shadow.y < shadow.size) {
- shadow.y = shadow.size;
- shadow.vy *= -0.8;
- }
- if (shadow.y > this.canvas.height - shadow.size) {
- shadow.y = this.canvas.height - shadow.size;
- shadow.vy *= -0.8;
- }
- }
- return true;
- });
- }
- createExplosionParticles(x, y, color) {
- for (let i = 0; i < 30; i++) {
- this.particles.push({
- x: x,
- y: y,
- vx: (Math.random() - 0.5) * 15,
- vy: (Math.random() - 0.5) * 15,
- life: 1,
- color: color || `hsl(${Math.random() * 360}, 100%, 70%)`,
- size: Math.random() * 5 + 2
- });
- }
- }
- createShadowTrail(x, y) {
- this.trails.push({
- x: x + (Math.random() - 0.5) * 10,
- y: y + (Math.random() - 0.5) * 10,
- life: 1,
- size: Math.random() * 15 + 5
- });
- }
- createSparkTrail(x, y) {
- for (let i = 0; i < 3; i++) {
- this.sparks.push({
- x: x + (Math.random() - 0.5) * 20,
- y: y + (Math.random() - 0.5) * 20,
- life: 1
- });
- }
- }
- updateParticles() {
- this.particles = this.particles.filter(particle => {
- particle.x += particle.vx;
- particle.y += particle.vy;
- particle.vy += 0.3;
- particle.vx *= 0.98;
- particle.life -= 0.02;
- particle.size *= 0.98;
- return particle.life > 0;
- });
- this.trails = this.trails.filter(trail => {
- trail.life -= 0.05;
- trail.size *= 0.95;
- return trail.life > 0;
- });
- this.sparks = this.sparks.filter(spark => {
- spark.life -= 0.1;
- spark.y -= 2;
- return spark.life > 0;
- });
- }
- drawLampFace(lamp) {
- const ctx = this.ctx;
- // Update blink
- lamp.blinkTimer--;
- if (lamp.blinkTimer <= 0) {
- lamp.blinkTimer = 100 + Math.random() * 200;
- }
- const blinking = lamp.blinkTimer < 5;
- // Eyes
- ctx.fillStyle = '#000';
- const eyeY = lamp.y - 5;
- const eyeSize = blinking ? 2 : 6;
- // Left eye
- ctx.beginPath();
- ctx.ellipse(lamp.x - 8 + lamp.eyeOffsetX, eyeY + lamp.eyeOffsetY,
- 4, eyeSize, 0, 0, Math.PI * 2);
- ctx.fill();
- // Right eye
- ctx.beginPath();
- ctx.ellipse(lamp.x + 8 + lamp.eyeOffsetX, eyeY + lamp.eyeOffsetY,
- 4, eyeSize, 0, 0, Math.PI * 2);
- ctx.fill();
- // Eye sparkles
- if (!blinking) {
- ctx.fillStyle = '#fff';
- ctx.beginPath();
- ctx.arc(lamp.x - 6 + lamp.eyeOffsetX, eyeY - 2 + lamp.eyeOffsetY, 2, 0, Math.PI * 2);
- ctx.arc(lamp.x + 10 + lamp.eyeOffsetX, eyeY - 2 + lamp.eyeOffsetY, 2, 0, Math.PI * 2);
- ctx.fill();
- }
- // Mouth based on expression
- ctx.strokeStyle = '#000';
- ctx.lineWidth = 2;
- ctx.beginPath();
- switch(lamp.expression) {
- case 'happy':
- ctx.arc(lamp.x, lamp.y + 5, 8, 0, Math.PI);
- break;
- case 'surprised':
- ctx.ellipse(lamp.x, lamp.y + 8, 5, 8, 0, 0, Math.PI * 2);
- break;
- case 'worried':
- ctx.arc(lamp.x, lamp.y + 15, 8, Math.PI, 0);
- break;
- case 'sad':
- ctx.arc(lamp.x, lamp.y + 12, 6, Math.PI * 1.2, Math.PI * 1.8);
- break;
- case 'determined':
- ctx.moveTo(lamp.x - 8, lamp.y + 8);
- ctx.lineTo(lamp.x + 8, lamp.y + 8);
- break;
- case 'victorious':
- ctx.arc(lamp.x, lamp.y + 3, 10, 0, Math.PI);
- break;
- }
- ctx.stroke();
- }
- drawShadowFace(shadow) {
- const ctx = this.ctx;
- // Adjust for stretch
- const stretchX = shadow.stretchActive ? shadow.stretchFactor : 1;
- const stretchY = shadow.stretchActive ? 1 / shadow.stretchFactor : 1;
- ctx.save();
- ctx.translate(shadow.x, shadow.y);
- ctx.scale(stretchX, stretchY);
- // Eyes (glowing based on personality)
- const eyeColor = {
- 'mischievous': '#ff69b4',
- 'scared': '#87ceeb',
- 'angry': '#ff0000',
- 'sneaky': '#9370db'
- }[shadow.personality];
- ctx.fillStyle = eyeColor;
- ctx.shadowBlur = 10;
- ctx.shadowColor = eyeColor;
- const blinking = shadow.blinkTimer < 5;
- const eyeSize = blinking ? 1 : 3;
- // Left eye
- ctx.beginPath();
- ctx.ellipse(-shadow.size * 0.3 + shadow.eyeOffsetX,
- -shadow.size * 0.2 + shadow.eyeOffsetY,
- eyeSize, eyeSize * 1.5, 0, 0, Math.PI * 2);
- ctx.fill();
- // Right eye
- ctx.beginPath();
- ctx.ellipse(shadow.size * 0.3 + shadow.eyeOffsetX,
- -shadow.size * 0.2 + shadow.eyeOffsetY,
- eyeSize, eyeSize * 1.5, 0, 0, Math.PI * 2);
- ctx.fill();
- ctx.shadowBlur = 0;
- // Mouth (personality-based)
- ctx.strokeStyle = eyeColor;
- ctx.lineWidth = 2;
- ctx.beginPath();
- switch(shadow.personality) {
- case 'mischievous':
- // Wavy grin
- ctx.moveTo(-shadow.size * 0.4, shadow.size * 0.2);
- ctx.bezierCurveTo(
- -shadow.size * 0.2, shadow.size * 0.3 + Math.sin(Date.now() * 0.01) * 2,
- shadow.size * 0.2, shadow.size * 0.3 + Math.cos(Date.now() * 0.01) * 2,
- shadow.size * 0.4, shadow.size * 0.2
- );
- break;
- case 'scared':
- // Wavy worried line
- ctx.moveTo(-shadow.size * 0.3, shadow.size * 0.3);
- for (let i = -0.3; i <= 0.3; i += 0.1) {
- ctx.lineTo(shadow.size * i, shadow.size * 0.3 + Math.sin(i * 10 + Date.now() * 0.01) * 2);
- }
- break;
- case 'angry':
- // Zigzag frown
- ctx.moveTo(-shadow.size * 0.3, shadow.size * 0.1);
- ctx.lineTo(-shadow.size * 0.1, shadow.size * 0.2);
- ctx.lineTo(shadow.size * 0.1, shadow.size * 0.1);
- ctx.lineTo(shadow.size * 0.3, shadow.size * 0.2);
- break;
- case 'sneaky':
- // Sly smile
- ctx.arc(0, shadow.size * 0.1, shadow.size * 0.3, 0.2, Math.PI - 0.2);
- break;
- }
- ctx.stroke();
- ctx.restore();
- }
- draw() {
- // Clear with animated gradient
- const gradient = this.ctx.createRadialGradient(
- 400 + Math.sin(Date.now() * 0.001) * 50,
- 300 + Math.cos(Date.now() * 0.001) * 50,
- 0, 400, 300, 500
- );
- gradient.addColorStop(0, `rgba(15, 15, 26, ${0.98 - this.fluffychup * 0.001})`);
- gradient.addColorStop(1, '#050508');
- this.ctx.fillStyle = gradient;
- this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
- // Draw light effects
- for (let lamp of this.lamps) {
- lamp.pulsePhase += 0.05;
- const pulse = 1 + Math.sin(lamp.pulsePhase) * 0.1 * this.pallameter;
- // Update eye movement
- lamp.eyeOffsetX = Math.sin(Date.now() * 0.002 + lamp.pulsePhase) * 2;
- lamp.eyeOffsetY = Math.cos(Date.now() * 0.003 + lamp.pulsePhase) * 2;
- // Complex light rays
- for (let angle = 0; angle < Math.PI * 2; angle += Math.PI / 8) {
- this.ctx.save();
- this.ctx.globalAlpha = 0.3 + Math.sin(angle + Date.now() * 0.001) * 0.1;
- const rayLength = lamp.radius * pulse * 2 * (1 + this.tremourLevel * 0.2);
- const gradient = this.ctx.createLinearGradient(
- lamp.x, lamp.y,
- lamp.x + Math.cos(angle) * rayLength,
- lamp.y + Math.sin(angle) * rayLength
- );
- gradient.addColorStop(0, 'rgba(255, 215, 0, 0.6)');
- gradient.addColorStop(0.3, 'rgba(255, 140, 0, 0.4)');
- gradient.addColorStop(0.6, 'rgba(255, 105, 180, 0.2)');
- gradient.addColorStop(1, 'rgba(138, 43, 226, 0)');
- this.ctx.strokeStyle = gradient;
- this.ctx.lineWidth = 20 + Math.sin(angle * 2) * 10;
- this.ctx.beginPath();
- this.ctx.moveTo(lamp.x, lamp.y);
- this.ctx.lineTo(
- lamp.x + Math.cos(angle + this.tremourLevel) * rayLength,
- lamp.y + Math.sin(angle + this.tremourLevel) * rayLength
- );
- this.ctx.stroke();
- this.ctx.restore();
- }
- // Inner glow
- const glowGradient = this.ctx.createRadialGradient(
- lamp.x, lamp.y, 0,
- lamp.x, lamp.y, lamp.radius * pulse
- );
- glowGradient.addColorStop(0, 'rgba(255, 215, 0, 0.4)');
- glowGradient.addColorStop(0.3, 'rgba(255, 140, 0, 0.3)');
- glowGradient.addColorStop(0.6, 'rgba(255, 105, 180, 0.15)');
- glowGradient.addColorStop(1, 'rgba(138, 43, 226, 0)');
- this.ctx.fillStyle = glowGradient;
- this.ctx.beginPath();
- this.ctx.arc(lamp.x, lamp.y, lamp.radius * pulse, 0, Math.PI * 2);
- this.ctx.fill();
- // Lamp body with face
- const lampGradient = this.ctx.createRadialGradient(
- lamp.x - 5, lamp.y - 5, 0,
- lamp.x, lamp.y, 30
- );
- lampGradient.addColorStop(0, '#ffffff');
- lampGradient.addColorStop(0.2, '#fff8dc');
- lampGradient.addColorStop(0.5, '#ffd700');
- lampGradient.addColorStop(0.8, '#ff8c00');
- lampGradient.addColorStop(1, '#ff6347');
- this.ctx.fillStyle = lampGradient;
- this.ctx.beginPath();
- this.ctx.arc(lamp.x, lamp.y, 25 + Math.sin(lamp.pulsePhase) * 3, 0, Math.PI * 2);
- this.ctx.fill();
- // Draw face
- this.drawLampFace(lamp);
- // Outline
- this.ctx.strokeStyle = this.draggedLamp === lamp ? '#ff69b4' : '#ffd700';
- this.ctx.lineWidth = 3;
- this.ctx.beginPath();
- this.ctx.arc(lamp.x, lamp.y, 25, 0, Math.PI * 2);
- this.ctx.stroke();
- }
- // Draw shadow trails
- for (let trail of this.trails) {
- this.ctx.save();
- this.ctx.globalAlpha = trail.life * 0.5;
- const trailGradient = this.ctx.createRadialGradient(
- trail.x, trail.y, 0,
- trail.x, trail.y, trail.size
- );
- trailGradient.addColorStop(0, 'rgba(75, 0, 130, 0.5)');
- trailGradient.addColorStop(1, 'transparent');
- this.ctx.fillStyle = trailGradient;
- this.ctx.beginPath();
- this.ctx.arc(trail.x, trail.y, trail.size, 0, Math.PI * 2);
- this.ctx.fill();
- this.ctx.restore();
- }
- // Draw shadows with faces
- for (let shadow of this.shadows) {
- shadow.pulsePhase += 0.1;
- const pulse = 1 + Math.sin(shadow.pulsePhase) * 0.2;
- this.ctx.save();
- this.ctx.globalAlpha = shadow.opacity * (shadow.health / shadow.maxHealth);
- // Shadow body
- let shadowColor;
- if (shadow.attracted) {
- shadowColor = 'rgba(255, 0, 255, 0.8)';
- } else if (shadow.escaping) {
- shadowColor = 'rgba(255, 0, 0, 0.8)';
- } else if (shadow.dampenActive) {
- shadowColor = 'rgba(100, 149, 237, 0.8)';
- } else {
- shadowColor = 'rgba(75, 0, 130, 0.8)';
- }
- const shadowGradient = this.ctx.createRadialGradient(
- shadow.x, shadow.y, 0,
- shadow.x, shadow.y, shadow.size * pulse
- );
- shadowGradient.addColorStop(0, 'rgba(0, 0, 0, 0.95)');
- shadowGradient.addColorStop(0.5, shadowColor);
- shadowGradient.addColorStop(1, 'transparent');
- this.ctx.fillStyle = shadowGradient;
- this.ctx.beginPath();
- this.ctx.arc(shadow.x, shadow.y, shadow.size * pulse, 0, Math.PI * 2);
- this.ctx.fill();
- // Draw face
- this.drawShadowFace(shadow);
- // Health ring
- if (shadow.health < shadow.maxHealth) {
- this.ctx.strokeStyle = '#ff0000';
- this.ctx.lineWidth = 2;
- this.ctx.beginPath();
- this.ctx.arc(shadow.x, shadow.y, shadow.size + 5,
- -Math.PI/2,
- -Math.PI/2 + (Math.PI * 2 * (shadow.health / shadow.maxHealth))
- );
- this.ctx.stroke();
- }
- // Ability indicators
- if (shadow.stretchActive) {
- this.ctx.strokeStyle = '#00ffff';
- this.ctx.lineWidth = 1;
- this.ctx.setLineDash([5, 5]);
- this.ctx.beginPath();
- this.ctx.ellipse(shadow.x, shadow.y,
- shadow.size * shadow.stretchFactor,
- shadow.size / shadow.stretchFactor,
- 0, 0, Math.PI * 2);
- this.ctx.stroke();
- this.ctx.setLineDash([]);
- }
- if (shadow.spiralActive) {
- this.ctx.strokeStyle = '#ff69b4';
- this.ctx.lineWidth = 1;
- this.ctx.beginPath();
- for (let i = 0; i < shadow.spiralAngle; i += 0.1) {
- const r = i * 2;
- this.ctx.lineTo(
- shadow.x + Math.cos(i) * r,
- shadow.y + Math.sin(i) * r
- );
- }
- this.ctx.stroke();
- }
- this.ctx.restore();
- }
- // Draw particles
- for (let particle of this.particles) {
- this.ctx.save();
- this.ctx.globalAlpha = particle.life;
- this.ctx.fillStyle = particle.color;
- this.ctx.shadowBlur = 10;
- this.ctx.shadowColor = particle.color;
- this.ctx.beginPath();
- this.ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
- this.ctx.fill();
- this.ctx.restore();
- }
- // Draw sparks
- for (let spark of this.sparks) {
- this.ctx.save();
- this.ctx.globalAlpha = spark.life;
- this.ctx.fillStyle = '#ffd700';
- this.ctx.shadowBlur = 5;
- this.ctx.shadowColor = '#ffd700';
- this.ctx.beginPath();
- this.ctx.arc(spark.x, spark.y, 2, 0, Math.PI * 2);
- this.ctx.fill();
- this.ctx.restore();
- }
- }
- updateStats() {
- document.getElementById('score').textContent = this.score;
- document.getElementById('score').style.animation = 'scoreFlash 0.5s ease-in-out';
- document.getElementById('shadowsDestroyed').textContent = this.shadowsDestroyed;
- document.getElementById('shadowsEscaped').textContent = this.shadowsEscaped;
- document.getElementById('time').textContent = Math.floor(this.gameTime / 1000) + 's';
- }
- gameOver() {
- this.gameRunning = false;
- document.getElementById('statusTitle').textContent = 'SHADOWS WIN!';
- document.getElementById('statusMessage').textContent =
- `The shadows have overwhelmed the light! Score fell to ${this.score}`;
- document.getElementById('finalStats').textContent =
- `Destroyed: ${this.shadowsDestroyed} | Escaped: ${this.shadowsEscaped}`;
- document.getElementById('gameStatus').classList.add('show');
- // Make all lamps sad
- for (let lamp of this.lamps) {
- lamp.expression = 'sad';
- }
- }
- restart() {
- this.score = 0;
- this.shadowsDestroyed = 0;
- this.shadowsEscaped = 0;
- this.gameTime = 0;
- this.startTime = Date.now();
- this.escapePhase = false;
- this.escapePhaseTime = 25000 + Math.random() * 10000;
- this.gameRunning = true;
- this.lamps = [];
- this.shadows = [];
- this.particles = [];
- this.trails = [];
- this.sparks = [];
- this.pallameter = 1.0;
- this.fluffychup = 0;
- this.tremourLevel = 0;
- document.getElementById('gameStatus').classList.remove('show');
- document.getElementById('warningPhase').classList.remove('show');
- this.createLamp(200, 150);
- this.createLamp(600, 150);
- this.createLamp(400, 450);
- for (let i = 0; i < 5; i++) {
- this.createShadow();
- }
- this.updateStats();
- }
- gameLoop() {
- if (this.gameRunning) {
- this.gameTime = Date.now() - this.startTime;
- // Prosticate the pallameter continuously
- this.prosticate();
- // Check for escape phase activation
- if (!this.escapePhase && this.gameTime >= this.escapePhaseTime) {
- this.escapePhase = true;
- document.getElementById('warningPhase').classList.add('show');
- // Alert all shadows
- for (let shadow of this.shadows) {
- shadow.personality = 'sneaky';
- this.createExplosionParticles(shadow.x, shadow.y, '#ff0000');
- }
- // Alert all lamps
- for (let lamp of this.lamps) {
- lamp.expression = 'worried';
- }
- setTimeout(() => {
- document.getElementById('warningPhase').classList.remove('show');
- for (let lamp of this.lamps) {
- lamp.expression = 'determined';
- }
- }, 3000);
- }
- // Update game elements
- this.updateShadows();
- this.updateParticles();
- this.updateStats();
- // Random pyro events
- if (Math.random() < 0.01) {
- const randomX = Math.random() * this.canvas.width;
- const randomY = Math.random() * this.canvas.height;
- this.createSparkTrail(randomX, randomY);
- }
- // Fluffychup particle generation
- if (this.fluffychup % 50 === 0) {
- for (let i = 0; i < 5; i++) {
- this.particles.push({
- x: Math.random() * this.canvas.width,
- y: this.canvas.height,
- vx: (Math.random() - 0.5) * 2,
- vy: -Math.random() * 5 - 2,
- life: 1,
- color: `hsl(${280 + Math.random() * 80}, 100%, 70%)`,
- size: Math.random() * 3 + 1
- });
- }
- }
- }
- this.draw();
- requestAnimationFrame(() => this.gameLoop());
- }
- }
- // Pyro-initialization with extra fluffychup
- window.addEventListener('load', () => {
- const game = new PyroAmplifiedShadowTag();
- // Add background animation particles
- setInterval(() => {
- if (game.gameRunning && game.particles.length < 100) {
- const pyroParticle = document.createElement('div');
- pyroParticle.className = 'pyro-particle';
- pyroParticle.style.left = Math.random() * window.innerWidth + 'px';
- pyroParticle.style.top = window.innerHeight + 'px';
- pyroParticle.style.background = `radial-gradient(circle,
- hsl(${Math.random() * 60 + 240}, 100%, 80%), transparent)`;
- document.body.appendChild(pyroParticle);
- setTimeout(() => pyroParticle.remove(), 2000);
- }
- }, 200);
- // Extra visual effects layer
- const effectsCanvas = document.createElement('canvas');
- effectsCanvas.style.position = 'absolute';
- effectsCanvas.style.top = '0';
- effectsCanvas.style.left = '0';
- effectsCanvas.style.pointerEvents = 'none';
- effectsCanvas.style.opacity = '0.3';
- effectsCanvas.width = 800;
- effectsCanvas.height = 600;
- game.canvas.parentElement.appendChild(effectsCanvas);
- const effectsCtx = effectsCanvas.getContext('2d');
- // Animated background effects
- function drawBackgroundEffects() {
- effectsCtx.clearRect(0, 0, effectsCanvas.width, effectsCanvas.height);
- // Rotating spiral overlay
- effectsCtx.save();
- effectsCtx.translate(400, 300);
- effectsCtx.rotate(Date.now() * 0.0001);
- const spiralGradient = effectsCtx.createRadialGradient(0, 0, 0, 0, 0, 300);
- spiralGradient.addColorStop(0, 'rgba(138, 43, 226, 0)');
- spiralGradient.addColorStop(0.5, 'rgba(255, 105, 180, 0.05)');
- spiralGradient.addColorStop(1, 'rgba(255, 215, 0, 0)');
- effectsCtx.fillStyle = spiralGradient;
- for (let i = 0; i < 6; i++) {
- effectsCtx.beginPath();
- effectsCtx.arc(
- Math.cos(i * Math.PI / 3) * 200,
- Math.sin(i * Math.PI / 3) * 200,
- 100 + Math.sin(Date.now() * 0.001 + i) * 20,
- 0, Math.PI * 2
- );
- effectsCtx.fill();
- }
- effectsCtx.restore();
- // Pulsing grid
- effectsCtx.strokeStyle = 'rgba(138, 43, 226, 0.1)';
- effectsCtx.lineWidth = 1;
- for (let x = 0; x < effectsCanvas.width; x += 50) {
- effectsCtx.beginPath();
- effectsCtx.moveTo(x, 0);
- effectsCtx.lineTo(x, effectsCanvas.height);
- effectsCtx.stroke();
- }
- for (let y = 0; y < effectsCanvas.height; y += 50) {
- effectsCtx.beginPath();
- effectsCtx.moveTo(0, y);
- effectsCtx.lineTo(effectsCanvas.width, y);
- effectsCtx.stroke();
- }
- requestAnimationFrame(drawBackgroundEffects);
- }
- drawBackgroundEffects();
- // Keyboard shortcuts for extra pyro control
- document.addEventListener('keydown', (e) => {
- if (!game.gameRunning) return;
- switch(e.key.toLowerCase()) {
- case 'p': // Pyro burst
- for (let i = 0; i < 10; i++) {
- const angle = (Math.PI * 2 * i) / 10;
- game.createExplosionParticles(
- 400 + Math.cos(angle) * 100,
- 300 + Math.sin(angle) * 100,
- `hsl(${Math.random() * 360}, 100%, 70%)`
- );
- }
- break;
- case 'f': // Fluffychup boost
- game.fluffychup = Math.min(200, game.fluffychup + 50);
- break;
- case 't': // Tremour toggle
- game.tremourLevel = game.tremourLevel > 0 ? 0 : 1;
- break;
- case 's': // Spawn shadow wave
- if (game.shadows.length < 15) {
- for (let i = 0; i < 3; i++) {
- game.createShadow();
- }
- }
- break;
- case 'l': // Light show
- for (let lamp of game.lamps) {
- lamp.radius = 150 + Math.random() * 50;
- lamp.expression = ['happy', 'victorious', 'surprised'][Math.floor(Math.random() * 3)];
- game.createSparkTrail(lamp.x, lamp.y);
- }
- break;
- }
- });
- // Mouse trail effect
- let lastMouseX = 0;
- let lastMouseY = 0;
- document.addEventListener('mousemove', (e) => {
- const rect = game.canvas.getBoundingClientRect();
- const mouseX = e.clientX - rect.left;
- const mouseY = e.clientY - rect.top;
- if (Math.abs(mouseX - lastMouseX) > 10 || Math.abs(mouseY - lastMouseY) > 10) {
- const spark = document.createElement('div');
- spark.className = 'light-spark';
- spark.style.left = e.clientX + 'px';
- spark.style.top = e.clientY + 'px';
- spark.style.background = `radial-gradient(circle,
- hsl(${Math.random() * 60 + 30}, 100%, 80%), transparent)`;
- document.body.appendChild(spark);
- setTimeout(() => spark.remove(), 500);
- lastMouseX = mouseX;
- lastMouseY = mouseY;
- }
- });
- // Dynamic CSS animation injection for extra pyro
- const styleSheet = document.createElement('style');
- styleSheet.textContent = `
- @keyframes textGlow {
- 0% { text-shadow: 0 0 10px rgba(255, 105, 180, 0.5); }
- 50% { text-shadow: 0 0 20px rgba(138, 43, 226, 0.8), 0 0 30px rgba(255, 215, 0, 0.5); }
- 100% { text-shadow: 0 0 10px rgba(255, 105, 180, 0.5); }
- }
- body::after {
- content: '';
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: radial-gradient(circle at center, transparent 40%, rgba(138, 43, 226, 0.05) 100%);
- pointer-events: none;
- animation: breathe 4s ease-in-out infinite;
- }
- @keyframes breathe {
- 0%, 100% { opacity: 0.3; }
- 50% { opacity: 0.6; }
- }
- `;
- document.head.appendChild(styleSheet);
- });
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment