From d1e4add0aaa4a80f8a55a67d66721e1802e487d2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 21:38:01 +0000 Subject: [PATCH 1/8] Initial plan From d263f3adfa9a4989b2a1d0e81887a25f752be458 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 21:41:25 +0000 Subject: [PATCH 2/8] Initial planning: Five Nights at Freddy's clone for badge Co-authored-by: johnmog <12915670+johnmog@users.noreply.github.com> --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 5cce5c2..1658d8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,7 @@ "requires": true, "packages": { "": { - "name": "badger", + "name": "badger", "version": "0.0.1", "dependencies": { "@astrojs/mdx": "^4.0.0", From 7373de240d6160a78f05e3a56cca521f8ba11e3d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 21:46:35 +0000 Subject: [PATCH 3/8] Add Five Nights at Badger app documentation Co-authored-by: johnmog <12915670+johnmog@users.noreply.github.com> --- public/images/apps/fivenights_icon.svg | 27 ++ src/content/apps/fivenights.mdx | 413 +++++++++++++++++++++++++ 2 files changed, 440 insertions(+) create mode 100644 public/images/apps/fivenights_icon.svg create mode 100644 src/content/apps/fivenights.mdx diff --git a/public/images/apps/fivenights_icon.svg b/public/images/apps/fivenights_icon.svg new file mode 100644 index 0000000..9738db9 --- /dev/null +++ b/public/images/apps/fivenights_icon.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 12 AM + diff --git a/src/content/apps/fivenights.mdx b/src/content/apps/fivenights.mdx new file mode 100644 index 0000000..aa1d6e7 --- /dev/null +++ b/src/content/apps/fivenights.mdx @@ -0,0 +1,413 @@ +--- +title: "Five Nights at Badger" +description: "Survival horror game. Monitor cameras, control doors, manage power. Survive until 6AM against animatronic threats." +icon: "/images/apps/fivenights_icon.svg" +category: "game" +preloaded: false +customizable: false +fileLocation: "/apps/fivenights" +--- + +# Five Nights at Badger + +Survive the night shift as a security guard monitoring malfunctioning animatronics through a limited power supply and surveillance system. A simplified FNAF clone adapted for the Badger 2350's e-ink display and five-button interface. + +## Features + +- **Camera System**: Cycle through 4 security cameras to track animatronic movement +- **Door Controls**: Left and right door switches to block intruders +- **Light System**: Check hallways for approaching threats +- **Power Management**: Limited power supply forces strategic choices +- **Night Progression**: Survive from 12 AM to 6 AM (6 minutes real-time) +- **AI Behavior**: Two animatronics with independent pathfinding +- **Game States**: Intro screen → Night shift → Win/Lose screens + +## File Location + +Five Nights at Badger lives in `/apps/fivenights` when the badge is mounted. + +``` +/apps/fivenights/ + ├── __init__.py # Main game loop and state machine + ├── animatronic.py # AI movement and pathfinding + ├── camera.py # Camera system and views + ├── office.py # Office controls (doors, lights) + ├── power.py # Power management system + ├── icon.png # Launcher icon + └── assets/ # Camera view sprites and UI elements +``` + +## How to Play + +### Controls + +- **Button A**: Toggle left door +- **Button C**: Toggle right door +- **Button B**: Toggle camera monitor +- **D-Pad Up/Down**: Cycle through cameras (when monitor open) +- **Button D**: Toggle left light (when monitor closed) +- **Button E**: Toggle right light (when monitor closed) + +### Game States + +**1. INTRO State:** +- Shows "FIVE NIGHTS AT BADGER" title +- Night number display +- "Press A to start" prompt +- Press Button A to begin + +**2. NIGHT State:** +- Monitor animatronic positions via cameras +- Close doors when threats approach +- Use lights to check blind spots +- Watch power meter carefully +- Survive until 6 AM clock + +**3. WIN State:** +- "6 AM" clock display +- "You survived the night!" message +- Shows power remaining +- Press A for next night + +**4. GAME OVER State:** +- Jumpscare screen (static animatronic sprite) +- "Night Over" message +- Press A to retry current night + +### Core Mechanics + +**Camera System:** +- 4 camera feeds: CAM1 (Show Stage), CAM2 (Left Hall), CAM3 (Right Hall), CAM4 (Backstage) +- Opening camera monitor pauses office view +- Animatronics move between camera zones +- Camera usage: 0.5% power per second + +**Door Controls:** +- Left door blocks CAM2 approach path +- Right door blocks CAM3 approach path +- Each door: 2% power per second when closed +- Doors fail when power reaches 0% + +**Light System:** +- Reveals animatronics immediately outside doors +- Left light checks left blind spot +- Right light checks right blind spot +- Each light: 0.2% power per second while active + +**Power Management:** +- Start with 100% power each night +- Idle consumption: 0.3% per second +- All systems drain power simultaneously +- Power out = all doors open, instant loss + +**Animatronics:** +- **Freddy**: Methodical movement, prefers right path (CAM1 → CAM4 → CAM3) +- **Bonnie**: Aggressive movement, prefers left path (CAM1 → CAM2) +- Movement speed increases with night number +- Only move when not being watched on camera +- Reaching office door = jumpscare if door open + +### Scoring + +- **Survival**: Complete night = unlock next night +- **Power Efficiency**: Finish with more power = better performance +- **Night Progression**: Nights 1-5 with increasing difficulty +- Night 6+ = custom challenges (faster AI, less power) + +## Visual Elements + +**Office View (296x128 display):** +- Top bar: Clock (left), Power meter (right) +- Center: Office perspective with two doorways +- Door status indicators: `[CLOSED]` or `[OPEN]` +- Bottom: Button hints and current state + +**Camera View:** +- Header: Camera name and number +- Center: Static camera sprite showing room +- Animatronic sprites overlay when present +- Bottom: `C` to cycle cameras, `B` to close monitor + +**UI Style:** +- Monospace font for all text (terminal aesthetic) +- High-contrast black/white for e-ink optimization +- Minimal animations (position updates only) +- Clear status indicators + +## Game Mechanics + +**AI Movement System:** +```python +class Animatronic: + def __init__(self, name, aggression): + self.name = name + self.position = "CAM1" # Starting location + self.aggression = aggression + self.move_cooldown = 0 + + def update(self, being_watched, night_level): + # Only move when not on camera + if not being_watched: + self.move_cooldown -= 1 + if self.move_cooldown <= 0: + self.attempt_move(night_level) + + def attempt_move(self, night_level): + # Higher night = faster movement + chance = self.aggression * night_level * 0.2 + if random.random() < chance: + self.move_to_next_room() +``` + +**Power Calculation:** +```python +class PowerSystem: + def __init__(self): + self.power = 100.0 + self.usage_rate = 0.3 # Idle + + def update(self, delta_time): + rate = 0.3 # Base idle + if camera_open: + rate += 0.5 + if left_door_closed: + rate += 2.0 + if right_door_closed: + rate += 2.0 + if left_light_on: + rate += 0.2 + if right_light_on: + rate += 0.2 + + self.power -= rate * delta_time + return self.power > 0 +``` + +## Tips & Tricks + +- **Check Cameras Sparingly**: Every second watching drains power +- **Listen for Cues**: Door lights reveal immediate threats +- **Door Discipline**: Only close doors when absolutely necessary +- **Left vs Right**: Learn each animatronic's preferred path +- **Timing is Key**: Quick camera checks > long surveillance +- **Late Night Strategy**: Save power for last hours (5-6 AM) +- **Pattern Recognition**: Animatronics follow predictable paths +- **Don't Panic**: Closed doors buy time to check cameras + +## Difficulty Progression + +**Night 1: Training Night** +- 1 animatronic active (Bonnie only) +- Slow movement speed +- 100% starting power +- Goal: Learn basic mechanics + +**Night 2: Both Active** +- 2 animatronics active +- Moderate movement speed +- 100% starting power +- Goal: Master camera cycling + +**Night 3: Faster Movement** +- 2 animatronics, increased aggression +- Movement cooldowns reduced +- 100% starting power +- Goal: Efficient power management + +**Night 4: Power Constraint** +- High aggression AI +- 95% starting power +- Faster movement patterns +- Goal: Minimize door usage + +**Night 5: Maximum Difficulty** +- Maximum aggression AI +- 90% starting power +- Randomized movement patterns +- Goal: Perfect execution + +**Night 6+: Custom Nights** +- Player-selected AI difficulty (0-20) +- Variable starting power +- Chaos mode unlocked + +## Code Example + +Here's a simplified game loop structure: + +```python +from badger2350 import Badger2350 +import time + +badge = Badger2350() + +class FiveNightsGame: + def __init__(self): + self.state = "INTRO" + self.night = 1 + self.power = 100.0 + self.clock = 0 # 0-360 (12AM-6AM) + self.left_door = False + self.right_door = False + self.camera_open = False + self.current_cam = 1 + + def update(self): + if self.state == "INTRO": + self.draw_intro() + if badge.pressed(badge.BUTTON_A): + self.state = "NIGHT" + self.start_night() + + elif self.state == "NIGHT": + # Handle input + if badge.pressed(badge.BUTTON_A): + self.left_door = not self.left_door + if badge.pressed(badge.BUTTON_C): + self.right_door = not self.right_door + if badge.pressed(badge.BUTTON_B): + self.camera_open = not self.camera_open + + # Update game logic + self.update_power() + self.update_animatronics() + self.update_clock() + + # Check win/loss + if self.clock >= 360: # 6 AM + self.state = "WIN" + elif self.power <= 0: + self.state = "GAME_OVER" + elif self.check_jumpscare(): + self.state = "GAME_OVER" + + # Draw current state + if self.camera_open: + self.draw_camera() + else: + self.draw_office() + + elif self.state == "WIN": + self.draw_win() + if badge.pressed(badge.BUTTON_A): + self.night += 1 + self.state = "INTRO" + + elif self.state == "GAME_OVER": + self.draw_gameover() + if badge.pressed(badge.BUTTON_A): + self.state = "INTRO" + + def update_power(self): + drain = 0.3 # Idle + if self.camera_open: + drain += 0.5 + if self.left_door: + drain += 2.0 + if self.right_door: + drain += 2.0 + self.power -= drain * 0.1 # Per frame + self.power = max(0, self.power) + + def draw_office(self): + badge.set_pen(15) + badge.clear() + badge.set_pen(0) + + # Clock + hour = 12 + (self.clock // 60) + if hour > 12: + hour -= 12 + badge.text(f"{hour} AM", 10, 10, scale=2) + + # Power + badge.text(f"PWR: {int(self.power)}%", 200, 10, scale=2) + + # Doors + left_status = "CLOSED" if self.left_door else "OPEN" + right_status = "CLOSED" if self.right_door else "OPEN" + badge.text(f"L:[{left_status}]", 10, 100, scale=1) + badge.text(f"R:[{right_status}]", 200, 100, scale=1) + + badge.update() + +# Main loop +game = FiveNightsGame() +while True: + game.update() + time.sleep(0.1) +``` + +## Install + +1. Clone the game from [`badger/home`](https://github.com/badger/home/tree/main/badge/apps/fivenights) +2. Copy the entire `fivenights/` folder to `/apps/` on your badge (USB storage mode) +3. Eject the badge, reboot +4. Launch "Five Nights at Badger" from the app menu + +## Source Code + +View the complete source code: +- [Main game loop](https://github.com/badger/home/blob/main/badge/apps/fivenights/__init__.py) +- [Animatronic AI](https://github.com/badger/home/blob/main/badge/apps/fivenights/animatronic.py) +- [Camera system](https://github.com/badger/home/blob/main/badge/apps/fivenights/camera.py) +- [Office controls](https://github.com/badger/home/blob/main/badge/apps/fivenights/office.py) +- [Power management](https://github.com/badger/home/blob/main/badge/apps/fivenights/power.py) + +## Technical Implementation + +**State Machine:** +```python +class GameState: + INTRO = "intro" + NIGHT = "night" + WIN = "win" + GAME_OVER = "gameover" +``` + +**Camera Zones:** +- CAM1: Show Stage (starting point) +- CAM2: Left Hall (leads to left door) +- CAM3: Right Hall (leads to right door) +- CAM4: Backstage (alternate route) + +**Time System:** +- Real-time: 6 minutes per night +- Game time: 12 AM - 6 AM (6 hours) +- 1 game minute = 10 real seconds +- Clock increments every 100ms + +**Performance Optimization:** +- Static sprites pre-loaded at startup +- Minimal screen redraws (only changed elements) +- Camera updates: 2 FPS (e-ink optimized) +- Office updates: 5 FPS +- Input polling: 10 Hz + +## Customization + +Edit `/apps/fivenights/config.py` to customize: + +```python +# Game difficulty settings +STARTING_POWER = 100 # Starting power percentage +NIGHT_DURATION = 360 # Seconds (6 minutes default) +BASE_AI_SPEED = 5 # Movement cooldown (lower = faster) + +# Power consumption rates +IDLE_DRAIN = 0.3 # Power % per second idle +CAMERA_DRAIN = 0.5 # Additional drain while cameras open +DOOR_DRAIN = 2.0 # Additional drain per closed door +LIGHT_DRAIN = 0.2 # Additional drain per active light + +# Visual settings +CAMERA_FPS = 2 # Camera view framerate +OFFICE_FPS = 5 # Office view framerate +``` + +## Acknowledgments + +- Inspired by Scott Cawthon's Five Nights at Freddy's +- Adapted for Badger 2350 hardware constraints +- Simplified for e-ink display and button controls +- Open source implementation for educational purposes From 5a7545e33e79f5c544181d07bc491bcaacbe7ae7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 21:48:47 +0000 Subject: [PATCH 4/8] Clarify Five Nights at Badger is proposed implementation Co-authored-by: johnmog <12915670+johnmog@users.noreply.github.com> --- src/content/apps/fivenights.mdx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/content/apps/fivenights.mdx b/src/content/apps/fivenights.mdx index aa1d6e7..ae9034c 100644 --- a/src/content/apps/fivenights.mdx +++ b/src/content/apps/fivenights.mdx @@ -340,19 +340,23 @@ while True: ## Install -1. Clone the game from [`badger/home`](https://github.com/badger/home/tree/main/badge/apps/fivenights) +> **Note**: This is a proposed game design. Implementation will be available in the [`badger/home`](https://github.com/badger/home) repository once completed. + +**Planned installation steps:** + +1. Clone the game from the `badge/apps/fivenights/` folder in the badger/home repository 2. Copy the entire `fivenights/` folder to `/apps/` on your badge (USB storage mode) 3. Eject the badge, reboot 4. Launch "Five Nights at Badger" from the app menu ## Source Code -View the complete source code: -- [Main game loop](https://github.com/badger/home/blob/main/badge/apps/fivenights/__init__.py) -- [Animatronic AI](https://github.com/badger/home/blob/main/badge/apps/fivenights/animatronic.py) -- [Camera system](https://github.com/badger/home/blob/main/badge/apps/fivenights/camera.py) -- [Office controls](https://github.com/badger/home/blob/main/badge/apps/fivenights/office.py) -- [Power management](https://github.com/badger/home/blob/main/badge/apps/fivenights/power.py) +**Planned file structure:** +- `__init__.py` - Main game loop and state machine +- `animatronic.py` - AI movement and pathfinding +- `camera.py` - Camera system and views +- `office.py` - Office controls (doors, lights) +- `power.py` - Power management system ## Technical Implementation From f4ed46f518a877e1d6df7dfe553cf83ca744edfb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 22:39:45 +0000 Subject: [PATCH 5/8] Add playable Five Nights at Badger browser demo Co-authored-by: johnmog <12915670+johnmog@users.noreply.github.com> --- src/components/five-nights-game.tsx | 275 ++++++++++++++++++++++++++++ src/content/apps/fivenights.mdx | 10 + 2 files changed, 285 insertions(+) create mode 100644 src/components/five-nights-game.tsx diff --git a/src/components/five-nights-game.tsx b/src/components/five-nights-game.tsx new file mode 100644 index 0000000..edc1523 --- /dev/null +++ b/src/components/five-nights-game.tsx @@ -0,0 +1,275 @@ +import React, { useState, useEffect, useRef } from 'react' +import { Button } from '@/components/ui/button' + +// Game state type +type GameState = 'INTRO' | 'NIGHT' | 'WIN' | 'GAME_OVER' + +interface GameStats { + power: number + clock: number // 0-360 (12AM-6AM) + night: number + leftDoorClosed: boolean + rightDoorClosed: boolean +} + +export const FiveNightsGame: React.FC = () => { + const [gameState, setGameState] = useState('INTRO') + const [stats, setStats] = useState({ + power: 100, + clock: 0, + night: 1, + leftDoorClosed: false, + rightDoorClosed: false, + }) + + const gameLoopRef = useRef() + const lastUpdateRef = useRef(Date.now()) + + // Game loop + useEffect(() => { + if (gameState !== 'NIGHT') return + + const update = () => { + const now = Date.now() + const deltaTime = (now - lastUpdateRef.current) / 1000 // seconds + lastUpdateRef.current = now + + setStats((prev) => { + // Calculate power drain + let drain = 0.3 // Idle drain per second + if (prev.leftDoorClosed) drain += 2.0 + if (prev.rightDoorClosed) drain += 2.0 + + const newPower = Math.max(0, prev.power - drain * deltaTime) + const newClock = prev.clock + deltaTime // Real seconds = game minutes + + // Check win condition (6 AM = 360 seconds) + if (newClock >= 360) { + setGameState('WIN') + return prev + } + + // Check loss condition + if (newPower <= 0) { + setGameState('GAME_OVER') + return prev + } + + return { + ...prev, + power: newPower, + clock: newClock, + } + }) + + gameLoopRef.current = requestAnimationFrame(update) + } + + gameLoopRef.current = requestAnimationFrame(update) + + return () => { + if (gameLoopRef.current) { + cancelAnimationFrame(gameLoopRef.current) + } + } + }, [gameState]) + + // Start game + const startNight = () => { + setStats({ + power: 100, + clock: 0, + night: stats.night, + leftDoorClosed: false, + rightDoorClosed: false, + }) + lastUpdateRef.current = Date.now() + setGameState('NIGHT') + } + + // Restart after game over + const restart = () => { + setStats({ + power: 100, + clock: 0, + night: stats.night, + leftDoorClosed: false, + rightDoorClosed: false, + }) + setGameState('INTRO') + } + + // Next night after win + const nextNight = () => { + setStats({ + power: 100, + clock: 0, + night: stats.night + 1, + leftDoorClosed: false, + rightDoorClosed: false, + }) + setGameState('INTRO') + } + + // Toggle doors + const toggleLeftDoor = () => { + if (gameState === 'NIGHT') { + setStats((prev) => ({ ...prev, leftDoorClosed: !prev.leftDoorClosed })) + } + } + + const toggleRightDoor = () => { + if (gameState === 'NIGHT') { + setStats((prev) => ({ ...prev, rightDoorClosed: !prev.rightDoorClosed })) + } + } + + // Calculate current hour (12 AM - 6 AM) + const getCurrentHour = () => { + const hour = 12 + Math.floor(stats.clock / 60) + return hour > 12 ? hour - 12 : hour + } + + // Render intro screen + if (gameState === 'INTRO') { + return ( +
+
+

+ FIVE NIGHTS AT BADGER +

+

NIGHT {stats.night}

+

12 AM - 6 AM

+ +
+
+ ) + } + + // Render game over screen + if (gameState === 'GAME_OVER') { + return ( +
+
+

+ NIGHT OVER +

+

+ You ran out of power at {getCurrentHour()} AM +

+

Night {stats.night}

+ +
+
+ ) + } + + // Render win screen + if (gameState === 'WIN') { + return ( +
+
+

+ 6 AM +

+

YOU SURVIVED THE NIGHT!

+

+ Power Remaining: {Math.floor(stats.power)}% +

+

Night {stats.night} Complete

+ +
+
+ ) + } + + // Render office view (main gameplay) + return ( +
+
+ {/* Top bar - Clock and Power */} +
+
{getCurrentHour()} AM
+
+ PWR: + {Math.floor(stats.power)}% + +
+
+ + {/* Office view */} +
+
+

SECURITY OFFICE

+
+ {/* Left door */} +
+
+ LEFT +
+ +
+ + {/* Center */} +
+
NIGHT {stats.night}
+
+ + {/* Right door */} +
+
+ RIGHT +
+ +
+
+
+
+ + {/* Bottom bar - Controls hint */} +
+
+ CLICK DOORS TO TOGGLE • SURVIVE UNTIL 6 AM +
+
+
+
+ ) +} diff --git a/src/content/apps/fivenights.mdx b/src/content/apps/fivenights.mdx index ae9034c..5d1e804 100644 --- a/src/content/apps/fivenights.mdx +++ b/src/content/apps/fivenights.mdx @@ -8,10 +8,20 @@ customizable: false fileLocation: "/apps/fivenights" --- +import { FiveNightsGame } from '@/components/five-nights-game' + # Five Nights at Badger Survive the night shift as a security guard monitoring malfunctioning animatronics through a limited power supply and surveillance system. A simplified FNAF clone adapted for the Badger 2350's e-ink display and five-button interface. +## Play Demo + +Try the browser-based demo below! This simplified version demonstrates the core power management mechanic. Click the door buttons to close/open doors and watch your power drain. Survive until 6 AM to win! + + + +--- + ## Features - **Camera System**: Cycle through 4 security cameras to track animatronic movement From 484ba2249b44e1a6df53e6aed979c53db3d0db49 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 22:42:35 +0000 Subject: [PATCH 6/8] Fix hour display and clarify time progression comments Co-authored-by: johnmog <12915670+johnmog@users.noreply.github.com> --- src/components/five-nights-game.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/five-nights-game.tsx b/src/components/five-nights-game.tsx index edc1523..1e1aee5 100644 --- a/src/components/five-nights-game.tsx +++ b/src/components/five-nights-game.tsx @@ -41,9 +41,9 @@ export const FiveNightsGame: React.FC = () => { if (prev.rightDoorClosed) drain += 2.0 const newPower = Math.max(0, prev.power - drain * deltaTime) - const newClock = prev.clock + deltaTime // Real seconds = game minutes + const newClock = prev.clock + deltaTime // Clock in seconds (360 seconds = 6 hours) - // Check win condition (6 AM = 360 seconds) + // Check win condition (6 AM = 360 seconds = 6 real minutes) if (newClock >= 360) { setGameState('WIN') return prev @@ -127,7 +127,7 @@ export const FiveNightsGame: React.FC = () => { // Calculate current hour (12 AM - 6 AM) const getCurrentHour = () => { const hour = 12 + Math.floor(stats.clock / 60) - return hour > 12 ? hour - 12 : hour + return hour >= 13 ? hour - 12 : hour } // Render intro screen From 1a40fcd716ce7756ef636351cad6afcb95386bcb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:49:09 +0000 Subject: [PATCH 7/8] Add camera system with 4 cameras and power drain Co-authored-by: johnmog <12915670+johnmog@users.noreply.github.com> --- src/components/five-nights-game.tsx | 156 +++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 4 deletions(-) diff --git a/src/components/five-nights-game.tsx b/src/components/five-nights-game.tsx index 1e1aee5..21a8251 100644 --- a/src/components/five-nights-game.tsx +++ b/src/components/five-nights-game.tsx @@ -10,6 +10,8 @@ interface GameStats { night: number leftDoorClosed: boolean rightDoorClosed: boolean + cameraOpen: boolean + currentCamera: number // 1-4 } export const FiveNightsGame: React.FC = () => { @@ -20,6 +22,8 @@ export const FiveNightsGame: React.FC = () => { night: 1, leftDoorClosed: false, rightDoorClosed: false, + cameraOpen: false, + currentCamera: 1, }) const gameLoopRef = useRef() @@ -39,6 +43,7 @@ export const FiveNightsGame: React.FC = () => { let drain = 0.3 // Idle drain per second if (prev.leftDoorClosed) drain += 2.0 if (prev.rightDoorClosed) drain += 2.0 + if (prev.cameraOpen) drain += 0.5 const newPower = Math.max(0, prev.power - drain * deltaTime) const newClock = prev.clock + deltaTime // Clock in seconds (360 seconds = 6 hours) @@ -82,6 +87,8 @@ export const FiveNightsGame: React.FC = () => { night: stats.night, leftDoorClosed: false, rightDoorClosed: false, + cameraOpen: false, + currentCamera: 1, }) lastUpdateRef.current = Date.now() setGameState('NIGHT') @@ -95,6 +102,8 @@ export const FiveNightsGame: React.FC = () => { night: stats.night, leftDoorClosed: false, rightDoorClosed: false, + cameraOpen: false, + currentCamera: 1, }) setGameState('INTRO') } @@ -107,6 +116,8 @@ export const FiveNightsGame: React.FC = () => { night: stats.night + 1, leftDoorClosed: false, rightDoorClosed: false, + cameraOpen: false, + currentCamera: 1, }) setGameState('INTRO') } @@ -124,6 +135,37 @@ export const FiveNightsGame: React.FC = () => { } } + // Camera controls + const toggleCamera = () => { + if (gameState === 'NIGHT') { + setStats((prev) => ({ ...prev, cameraOpen: !prev.cameraOpen })) + } + } + + const cycleCamera = (direction: 'up' | 'down') => { + if (gameState === 'NIGHT' && stats.cameraOpen) { + setStats((prev) => { + let newCamera = prev.currentCamera + if (direction === 'up') { + newCamera = prev.currentCamera < 4 ? prev.currentCamera + 1 : 1 + } else { + newCamera = prev.currentCamera > 1 ? prev.currentCamera - 1 : 4 + } + return { ...prev, currentCamera: newCamera } + }) + } + } + + // Camera data + const cameras = [ + { id: 1, name: 'Show Stage', description: 'Main stage area' }, + { id: 2, name: 'Left Hall', description: 'Hallway to left door' }, + { id: 3, name: 'Right Hall', description: 'Hallway to right door' }, + { id: 4, name: 'Backstage', description: 'Storage area' }, + ] + + const getCurrentCamera = () => cameras.find((cam) => cam.id === stats.currentCamera)! + // Calculate current hour (12 AM - 6 AM) const getCurrentHour = () => { const hour = 12 + Math.floor(stats.clock / 60) @@ -198,6 +240,102 @@ export const FiveNightsGame: React.FC = () => { ) } + // Render camera view + if (gameState === 'NIGHT' && stats.cameraOpen) { + const currentCam = getCurrentCamera() + return ( +
+
+ {/* Top bar - Clock and Power */} +
+
{getCurrentHour()} AM
+
+ PWR: + {Math.floor(stats.power)}% + +
+
+ + {/* Camera view */} +
+ {/* Camera header */} +
+

+ CAM {currentCam.id} +

+

+ {currentCam.name.toUpperCase()} +

+

+ {currentCam.description} +

+
+ + {/* Camera feed area */} +
+ {/* Static effect overlay */} +
+ + {/* Camera content */} +
+

+ [SECURITY CAMERA] +

+
+ +
+

+ NO ACTIVITY +

+
+ + {/* Scan lines effect */} +
+ {[...Array(8)].map((_, i) => ( +
+ ))} +
+
+ + {/* Camera controls */} +
+ + + {stats.currentCamera} / 4 + + +
+
+ + {/* Bottom bar - Controls hint */} +
+
+ PRESS B TO CLOSE CAMERA • ARROWS TO CYCLE +
+
+
+
+ ) + } + // Render office view (main gameplay) return (
@@ -236,9 +374,19 @@ export const FiveNightsGame: React.FC = () => {
- {/* Center */} -
-
NIGHT {stats.night}
+ {/* Center - Camera button */} +
+
+
NIGHT {stats.night}
+
+
{/* Right door */} @@ -266,7 +414,7 @@ export const FiveNightsGame: React.FC = () => { {/* Bottom bar - Controls hint */}
- CLICK DOORS TO TOGGLE • SURVIVE UNTIL 6 AM + DOORS: A/C • CAMERAS: B • SURVIVE UNTIL 6 AM
From 0ee8d8c167aec9322d48e720959028b448fd4e7b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:54:58 +0000 Subject: [PATCH 8/8] Optimize camera system - move constants outside component Co-authored-by: johnmog <12915670+johnmog@users.noreply.github.com> --- src/components/five-nights-game.tsx | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/components/five-nights-game.tsx b/src/components/five-nights-game.tsx index 21a8251..b135783 100644 --- a/src/components/five-nights-game.tsx +++ b/src/components/five-nights-game.tsx @@ -14,6 +14,17 @@ interface GameStats { currentCamera: number // 1-4 } +// Camera data (constant outside component to avoid re-creation) +const CAMERAS = [ + { id: 1, name: 'Show Stage', description: 'Main stage area' }, + { id: 2, name: 'Left Hall', description: 'Hallway to left door' }, + { id: 3, name: 'Right Hall', description: 'Hallway to right door' }, + { id: 4, name: 'Backstage', description: 'Storage area' }, +] as const + +// Scan lines for camera effect (constant to avoid re-creation) +const SCAN_LINES = Array.from({ length: 8 }, (_, i) => i) + export const FiveNightsGame: React.FC = () => { const [gameState, setGameState] = useState('INTRO') const [stats, setStats] = useState({ @@ -156,15 +167,11 @@ export const FiveNightsGame: React.FC = () => { } } - // Camera data - const cameras = [ - { id: 1, name: 'Show Stage', description: 'Main stage area' }, - { id: 2, name: 'Left Hall', description: 'Hallway to left door' }, - { id: 3, name: 'Right Hall', description: 'Hallway to right door' }, - { id: 4, name: 'Backstage', description: 'Storage area' }, - ] - - const getCurrentCamera = () => cameras.find((cam) => cam.id === stats.currentCamera)! + // Get current camera with fallback + const getCurrentCamera = () => { + const camera = CAMERAS.find((cam) => cam.id === stats.currentCamera) + return camera ?? CAMERAS[0] // Fallback to first camera if invalid ID + } // Calculate current hour (12 AM - 6 AM) const getCurrentHour = () => { @@ -291,7 +298,7 @@ export const FiveNightsGame: React.FC = () => { {/* Scan lines effect */}
- {[...Array(8)].map((_, i) => ( + {SCAN_LINES.map((i) => (