diff --git a/games/xmas/src/screens/play/dev/dev-config-panel.tsx b/games/xmas/src/screens/play/dev/dev-config-panel.tsx
new file mode 100644
index 00000000..40126bea
--- /dev/null
+++ b/games/xmas/src/screens/play/dev/dev-config-panel.tsx
@@ -0,0 +1,156 @@
+import 'react';
+import styled from 'styled-components';
+import { useDevConfig, useDevMode } from './dev-config';
+
+export function DevConfigPanel() {
+ const isDevMode = useDevMode();
+ const [config, updateConfig] = useDevConfig();
+
+ if (!isDevMode) {
+ return null;
+ }
+
+ const handleToggle = (key: keyof typeof config) => {
+ updateConfig({ [key]: !config[key] });
+ };
+
+ return (
+
+ Dev Configuration
+
+
+ Game Features
+
+ handleToggle('enableAISantas')}
+ />
+ AI Santas
+
+
+
+
+ Rendering
+
+ handleToggle('renderSnow')} />
+ Snow Effects
+
+
+ handleToggle('renderFire')} />
+ Fire Effects
+
+
+ handleToggle('renderTrees')} />
+ Trees
+
+
+ handleToggle('renderMountains')}
+ />
+ Mountains
+
+
+ handleToggle('renderSnowGround')}
+ />
+ Snow Ground
+
+
+
+ );
+}
+
+const Panel = styled.div`
+ position: fixed;
+ top: 10px;
+ right: 10px;
+ background: rgba(0, 0, 0, 0.8);
+ color: white;
+ padding: 15px;
+ border-radius: 8px;
+ min-width: 200px;
+ z-index: 1000;
+ font-family: 'Arial', sans-serif;
+ backdrop-filter: blur(5px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+`;
+
+const Title = styled.h3`
+ margin: 0 0 15px 0;
+ font-size: 14px;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ color: #fff;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+ padding-bottom: 8px;
+`;
+
+const Section = styled.div`
+ margin-bottom: 15px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+`;
+
+const SectionTitle = styled.h4`
+ margin: 0 0 8px 0;
+ font-size: 12px;
+ color: rgba(255, 255, 255, 0.7);
+ font-weight: normal;
+`;
+
+const ToggleContainer = styled.label`
+ display: flex;
+ align-items: center;
+ margin-bottom: 8px;
+ cursor: pointer;
+ font-size: 12px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ &:hover {
+ color: #4a9eff;
+ }
+`;
+
+const ToggleInput = styled.input`
+ margin-right: 8px;
+ cursor: pointer;
+
+ /* Custom checkbox styling */
+ appearance: none;
+ width: 16px;
+ height: 16px;
+ border: 2px solid rgba(255, 255, 255, 0.3);
+ border-radius: 3px;
+ background: transparent;
+ position: relative;
+
+ &:checked {
+ background: #4a9eff;
+ border-color: #4a9eff;
+ }
+
+ &:checked::after {
+ content: '✓';
+ position: absolute;
+ color: white;
+ font-size: 12px;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+
+ &:hover {
+ border-color: #4a9eff;
+ }
+`;
diff --git a/games/xmas/src/screens/play/dev/dev-config.ts b/games/xmas/src/screens/play/dev/dev-config.ts
new file mode 100644
index 00000000..5fba36bf
--- /dev/null
+++ b/games/xmas/src/screens/play/dev/dev-config.ts
@@ -0,0 +1,153 @@
+import { useEffect, useState } from 'react';
+
+/**
+ * Interface defining the development configuration options
+ */
+export interface DevConfig {
+ // AI Features
+ enableAISantas: boolean;
+
+ // Rendering Features
+ renderSnow: boolean;
+ renderFire: boolean;
+ renderTrees: boolean;
+ renderMountains: boolean;
+ renderSnowGround: boolean;
+}
+
+/**
+ * Default configuration values
+ */
+const DEFAULT_CONFIG: DevConfig = {
+ enableAISantas: true,
+ renderSnow: true,
+ renderFire: true,
+ renderTrees: true,
+ renderMountains: true,
+ renderSnowGround: true,
+};
+
+/**
+ * Custom event for config updates
+ */
+const CONFIG_UPDATE_EVENT = 'devConfigUpdate';
+
+/**
+ * Event interface for config updates
+ */
+interface DevConfigUpdateEvent extends CustomEvent {
+ detail: Partial;
+}
+
+/**
+ * Class managing the development configuration
+ */
+class DevConfigManager {
+ private config: DevConfig;
+ private listeners: Set<(config: DevConfig) => void>;
+
+ constructor() {
+ this.config = { ...DEFAULT_CONFIG };
+ this.listeners = new Set();
+ this.initHashListener();
+ }
+
+ /**
+ * Initialize hash change listener for dev mode
+ */
+ private initHashListener() {
+ window.addEventListener('hashchange', this.checkDevMode.bind(this));
+ this.checkDevMode(); // Initial check
+ }
+
+ /**
+ * Check if dev mode is enabled via URL hash
+ */
+ private checkDevMode() {
+ const isDevMode = window.location.hash.includes('#dev');
+ if (!isDevMode) {
+ // Reset to default config when dev mode is disabled
+ this.updateConfig(DEFAULT_CONFIG);
+ }
+ }
+
+ /**
+ * Get current configuration state
+ */
+ getConfig(): Readonly {
+ return { ...this.config };
+ }
+
+ /**
+ * Update configuration
+ */
+ updateConfig(updates: Partial) {
+ this.config = {
+ ...this.config,
+ ...updates,
+ };
+
+ // Notify all listeners
+ this.listeners.forEach((listener) => listener(this.getConfig()));
+
+ // Dispatch custom event
+ const event = new CustomEvent(CONFIG_UPDATE_EVENT, {
+ detail: updates,
+ }) as DevConfigUpdateEvent;
+ window.dispatchEvent(event);
+ }
+
+ /**
+ * Subscribe to configuration changes
+ */
+ subscribe(listener: (config: DevConfig) => void): () => void {
+ this.listeners.add(listener);
+ return () => this.listeners.delete(listener);
+ }
+
+ /**
+ * Check if dev mode is currently active
+ */
+ isDevMode(): boolean {
+ return window.location.hash.includes('#dev');
+ }
+}
+
+// Create singleton instance
+export const devConfig = new DevConfigManager();
+
+/**
+ * React hook for accessing dev configuration
+ */
+export function useDevConfig(): [DevConfig, (updates: Partial) => void] {
+ const [config, setConfig] = useState(devConfig.getConfig());
+
+ useEffect(() => {
+ // Subscribe to config updates
+ const unsubscribe = devConfig.subscribe((newConfig) => {
+ setConfig(newConfig);
+ });
+
+ return unsubscribe;
+ }, []);
+
+ return [config, (updates) => devConfig.updateConfig(updates)];
+}
+
+/**
+ * React hook for checking dev mode status
+ */
+export function useDevMode(): boolean {
+ const [isDevMode, setIsDevMode] = useState(devConfig.isDevMode());
+
+ useEffect(() => {
+ const handleHashChange = () => {
+ setIsDevMode(devConfig.isDevMode());
+ };
+
+ window.addEventListener('hashchange', handleHashChange);
+ return () => window.removeEventListener('hashchange', handleHashChange);
+ }, []);
+
+ return isDevMode;
+}
\ No newline at end of file
diff --git a/games/xmas/src/screens/play/game-ai/ai-santa-spawner.ts b/games/xmas/src/screens/play/game-ai/ai-santa-spawner.ts
index cd06bf2a..2483e97c 100644
--- a/games/xmas/src/screens/play/game-ai/ai-santa-spawner.ts
+++ b/games/xmas/src/screens/play/game-ai/ai-santa-spawner.ts
@@ -3,6 +3,7 @@ import { createSanta } from '../game-world/game-world-manipulate';
import { AI_CONFIG, AI_DIFFICULTY, AISanta, WaveState } from './ai-santa-types';
import { initializeAIState } from './ai-santa-decision';
import { GAME_WORLD_HEIGHT, GAME_WORLD_WIDTH } from '../game-world/game-world-consts';
+import { devConfig } from '../dev/dev-config';
/**
* Calculate a safe spawn position for AI Santa
@@ -93,6 +94,12 @@ function spawnWaveSantas(gameState: GameWorldState, waveState: WaveState): void
* Main update function for AI Santa spawning system
*/
export function updateAISpawner(gameState: GameWorldState, waveState: WaveState): void {
+ if (!devConfig.getConfig().enableAISantas) {
+ // Reset wave state when AI is disabled
+ waveState.currentWave = AI_DIFFICULTY.WAVE_1;
+ return;
+ }
+
// Check if current wave is completed
if (areAllSantasEliminated(gameState)) {
if (waveState.nextSpawnTime === null) {
diff --git a/games/xmas/src/screens/play/game-render/game-renderer.ts b/games/xmas/src/screens/play/game-render/game-renderer.ts
index 6bb8fdda..bf0e84b7 100644
--- a/games/xmas/src/screens/play/game-render/game-renderer.ts
+++ b/games/xmas/src/screens/play/game-render/game-renderer.ts
@@ -6,6 +6,7 @@ import { renderSanta } from './santa-renderer';
import { renderFireballs } from './fireball-renderer';
import { renderSky } from './landscape/sky/sky-renderer';
import { renderGifts } from './gift-renderer';
+import { devConfig } from '../dev/dev-config';
export const renderGame = (ctx: CanvasRenderingContext2D, world: GameWorldState, renderState: RenderState) => {
const { viewport } = renderState;
@@ -19,20 +20,33 @@ export const renderGame = (ctx: CanvasRenderingContext2D, world: GameWorldState,
// Apply viewport translation
ctx.translate(viewport.x, viewport.y);
- // Render all game elements with the applied translation
- renderLandscape(ctx, renderState.landscape, renderState.viewport);
- renderSnow(ctx, renderState);
+ // Get current dev configuration
+ const config = devConfig.getConfig();
+
+ // Conditionally render landscape elements based on dev config
+ if (config.renderMountains || config.renderTrees || config.renderSnowGround) {
+ // Pass dev config to control individual landscape elements
+ renderLandscape(ctx, renderState.landscape, renderState.viewport);
+ }
+
+ // Conditionally render snow effects
+ if (config.renderSnow) {
+ renderSnow(ctx, renderState);
+ }
// Render gifts before Santas for proper depth
renderGifts(ctx, world.gifts, world.time);
- // Render all santas
+ // Always render Santas as they're essential game elements
world.santas.forEach((santa) => {
renderSanta(ctx, santa, world.time);
});
- renderFireballs(ctx, renderState);
+ // Conditionally render fire effects
+ if (config.renderFire) {
+ renderFireballs(ctx, renderState);
+ }
// Restore the context state
ctx.restore();
-};
\ No newline at end of file
+};
diff --git a/games/xmas/src/screens/play/game-render/landscape/landscape-renderer.ts b/games/xmas/src/screens/play/game-render/landscape/landscape-renderer.ts
index d5197a7c..c52e2b1b 100644
--- a/games/xmas/src/screens/play/game-render/landscape/landscape-renderer.ts
+++ b/games/xmas/src/screens/play/game-render/landscape/landscape-renderer.ts
@@ -4,8 +4,11 @@ import { renderMountains } from './mountain/mountain-renderer';
import { renderTrees } from './tree/tree-renderer';
import { renderSnowGrounds } from './snow-ground/snow-ground-renderer';
import { ViewportState } from '../render-state';
+import { devConfig } from '../../dev/dev-config';
export function renderLandscape(ctx: CanvasRenderingContext2D, state: LandscapeState, viewport: ViewportState): void {
+ const config = devConfig.getConfig();
+
// Save the current context state
ctx.save();
@@ -16,14 +19,20 @@ export function renderLandscape(ctx: CanvasRenderingContext2D, state: LandscapeS
renderStars(ctx, state.stars.stars);
// Render mountains (back to front)
- renderMountains(ctx, state.mountains.mountains, viewport);
+ if (config.renderMountains) {
+ renderMountains(ctx, state.mountains.mountains, viewport);
+ }
// Render snow ground (between mountains and trees)
- renderSnowGrounds(ctx, state.snowGround.grounds, viewport);
+ if (config.renderSnowGround) {
+ renderSnowGrounds(ctx, state.snowGround.grounds, viewport);
+ }
// Render trees (back to front)
- renderTrees(ctx, state.trees.trees, viewport);
+ if (config.renderTrees) {
+ renderTrees(ctx, state.trees.trees, viewport);
+ }
// Restore the context state
ctx.restore();
-}
\ No newline at end of file
+}
diff --git a/games/xmas/src/screens/play/game-world/game-world-update.ts b/games/xmas/src/screens/play/game-world/game-world-update.ts
index ce77f68e..6012b036 100644
--- a/games/xmas/src/screens/play/game-world/game-world-update.ts
+++ b/games/xmas/src/screens/play/game-world/game-world-update.ts
@@ -12,6 +12,7 @@ import {
} from './game-world-manipulate';
import { dropGift } from './game-world-gift-manipulate';
import { updateGifts } from './game-world-update-gifts';
+import { devConfig } from '../dev/dev-config';
/**
* Check and handle collisions between fireballs and all Santas
@@ -34,21 +35,25 @@ function processFireballSantaCollisions(state: GameWorldState) {
}
});
- // Check collisions with AI Santas
- state.santas.forEach((santa) => {
- fireballs.forEach((fireball) => {
- if (checkFireballSantaCollision(fireball, santa)) {
- handleFireballSantaCollision(state, fireball, santa);
- // Drop gift if Santa was carrying one
- if (santa.carriedGift) {
- const gift = state.gifts.find((g) => g.id === santa.carriedGift);
- if (gift) {
- dropGift(state, santa.id);
+ // Check collisions with AI Santas only if AI is enabled
+ if (devConfig.getConfig().enableAISantas) {
+ state.santas.forEach((santa) => {
+ if (santa === state.playerSanta) return; // Skip player Santa
+
+ fireballs.forEach((fireball) => {
+ if (checkFireballSantaCollision(fireball, santa)) {
+ handleFireballSantaCollision(state, fireball, santa);
+ // Drop gift if Santa was carrying one
+ if (santa.carriedGift) {
+ const gift = state.gifts.find((g) => g.id === santa.carriedGift);
+ if (gift) {
+ dropGift(state, santa.id);
+ }
}
}
- }
+ });
});
- });
+ }
}
/**
@@ -100,26 +105,36 @@ export function updateGameWorld(state: GameWorldState, deltaTime: number) {
// Process fireball-Santa collisions
processFireballSantaCollisions(state);
- // Update AI-controlled Santas
- state.santas.forEach((santa) => {
- if ('ai' in santa) {
- // Handle AI Santa updates including fireball creation
- handleAISantaFireball(state, santa as AISanta);
- }
+ // Get current dev configuration
+ const config = devConfig.getConfig();
+
+ // Update AI-controlled Santas only if enabled
+ if (config.enableAISantas) {
+ state.santas.forEach((santa) => {
+ if (santa === state.playerSanta) return; // Skip player Santa
+
+ if ('ai' in santa) {
+ // Handle AI Santa updates including fireball creation
+ handleAISantaFireball(state, santa as AISanta);
+ }
- // Update AI Santa physics
- updateSantaPhysics(santa, deltaTime);
- updateSantaEnergy(santa, deltaTime);
-
- // Check for automatic gift collection for AI Santas
- tryCollectNearbyGifts(state, santa);
- });
+ // Update AI Santa physics
+ updateSantaPhysics(santa, deltaTime);
+ updateSantaEnergy(santa, deltaTime);
+
+ // Check for automatic gift collection for AI Santas
+ tryCollectNearbyGifts(state, santa);
+ });
+
+ // Update AI spawning system only if AI is enabled
+ updateAISpawner(state, state.waveState);
+ } else {
+ // If AI is disabled, ensure only player Santa remains
+ state.santas = state.santas.filter(santa => santa === state.playerSanta);
+ }
// Update gifts system
updateGifts(state);
- // Update AI spawning system
- updateAISpawner(state, state.waveState);
-
return state;
-}
+}
\ No newline at end of file
diff --git a/games/xmas/src/screens/play/play-screen.tsx b/games/xmas/src/screens/play/play-screen.tsx
index 3e247325..5f5b88b1 100644
--- a/games/xmas/src/screens/play/play-screen.tsx
+++ b/games/xmas/src/screens/play/play-screen.tsx
@@ -7,6 +7,7 @@ import { createRenderState, RenderState, updateRenderState } from './game-render
import { createSanta } from './game-world/game-world-manipulate';
import { GAME_WORLD_HEIGHT, GAME_WORLD_WIDTH } from './game-world/game-world-consts';
import { initializeWaveState } from './game-ai/ai-santa-spawner';
+import { DevConfigPanel } from './dev/dev-config-panel';
// Game configuration
const GAME_CONFIG = {
@@ -85,5 +86,10 @@ export function PlayScreen() {
}
});
- return ;
+ return (
+ <>
+
+
+ >
+ );
}