Initial implementation of HeisserDraht buzz wire game
ESP32-C3 based hot wire game with WS2812B LED effects, SSD1306 OLED display, debounced touch detection, and full game state machine (IDLE → COUNTDOWN → PLAYING → GAME_OVER). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
132
led_effects.cpp
Normal file
132
led_effects.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
#include "led_effects.h"
|
||||
#include "config.h"
|
||||
#include "game_logic.h"
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
|
||||
static Adafruit_NeoPixel strip(NUM_LEDS, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
|
||||
|
||||
// ── Helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
static uint32_t colorWheel(uint8_t pos) {
|
||||
pos = 255 - pos;
|
||||
if (pos < 85) return strip.Color(255 - pos * 3, 0, pos * 3);
|
||||
if (pos < 170) { pos -= 85; return strip.Color(0, pos * 3, 255 - pos * 3); }
|
||||
pos -= 170;
|
||||
return strip.Color(pos * 3, 255 - pos * 3, 0);
|
||||
}
|
||||
|
||||
// ── Animations ──────────────────────────────────────────────────────
|
||||
|
||||
static void animIdle() {
|
||||
// Rainbow chase
|
||||
static uint16_t hue = 0;
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
strip.setPixelColor(i, colorWheel((i * 256 / NUM_LEDS + hue) & 0xFF));
|
||||
}
|
||||
hue += 2;
|
||||
strip.show();
|
||||
}
|
||||
|
||||
static void animCountdown() {
|
||||
GameData& gd = gameGetData();
|
||||
uint32_t color;
|
||||
switch (gd.countdownValue) {
|
||||
case 3: // fall through
|
||||
case 2: color = strip.Color(255, 0, 0); break; // Red
|
||||
case 1: color = strip.Color(255, 180, 0); break; // Yellow
|
||||
default: color = strip.Color(0, 255, 0); break; // Green (GO!)
|
||||
}
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
strip.setPixelColor(i, color);
|
||||
}
|
||||
strip.show();
|
||||
}
|
||||
|
||||
static void animPlaying() {
|
||||
GameData& gd = gameGetData();
|
||||
unsigned long totalMs = (unsigned long)GAME_DURATION_S * 1000UL;
|
||||
float progress = 1.0f - (float)gd.elapsedMs / (float)totalMs;
|
||||
if (progress < 0.0f) progress = 0.0f;
|
||||
|
||||
int litLeds = (int)(progress * NUM_LEDS + 0.5f);
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
if (i < litLeds) {
|
||||
// Color transitions: green → yellow → red as time depletes
|
||||
if (progress > 0.5f) strip.setPixelColor(i, strip.Color(0, 255, 0));
|
||||
else if (progress > 0.2f) strip.setPixelColor(i, strip.Color(255, 180, 0));
|
||||
else strip.setPixelColor(i, strip.Color(255, 0, 0));
|
||||
} else {
|
||||
strip.setPixelColor(i, 0);
|
||||
}
|
||||
}
|
||||
strip.show();
|
||||
}
|
||||
|
||||
static void animGameOverWin() {
|
||||
// Green/gold sparkle
|
||||
static unsigned long lastSparkle = 0;
|
||||
unsigned long now = millis();
|
||||
if (now - lastSparkle > 80) {
|
||||
lastSparkle = now;
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
if (random(3) == 0)
|
||||
strip.setPixelColor(i, strip.Color(255, 215, 0)); // Gold
|
||||
else
|
||||
strip.setPixelColor(i, strip.Color(0, 180, 0)); // Green
|
||||
}
|
||||
strip.show();
|
||||
}
|
||||
}
|
||||
|
||||
static void animGameOverLose() {
|
||||
// Pulsing red breathing
|
||||
static uint8_t breathVal = 0;
|
||||
static int8_t breathDir = 2;
|
||||
breathVal += breathDir;
|
||||
if (breathVal >= 250 || breathVal <= 5) breathDir = -breathDir;
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
strip.setPixelColor(i, strip.Color(breathVal, 0, 0));
|
||||
}
|
||||
strip.show();
|
||||
}
|
||||
|
||||
// ── Public API ──────────────────────────────────────────────────────
|
||||
|
||||
void ledsInit() {
|
||||
strip.begin();
|
||||
strip.setBrightness(LED_BRIGHTNESS);
|
||||
strip.show();
|
||||
}
|
||||
|
||||
void ledsSetBrightness(uint8_t b) {
|
||||
strip.setBrightness(b);
|
||||
}
|
||||
|
||||
void ledsTouchFlash() {
|
||||
// Blocking 3x red blink (~300ms total)
|
||||
for (int flash = 0; flash < 3; flash++) {
|
||||
for (int i = 0; i < NUM_LEDS; i++)
|
||||
strip.setPixelColor(i, strip.Color(255, 0, 0));
|
||||
strip.show();
|
||||
delay(50);
|
||||
for (int i = 0; i < NUM_LEDS; i++)
|
||||
strip.setPixelColor(i, 0);
|
||||
strip.show();
|
||||
delay(50);
|
||||
}
|
||||
}
|
||||
|
||||
void ledsUpdate() {
|
||||
GameData& gd = gameGetData();
|
||||
switch (gd.state) {
|
||||
case STATE_IDLE: animIdle(); break;
|
||||
case STATE_COUNTDOWN: animCountdown(); break;
|
||||
case STATE_PLAYING: animPlaying(); break;
|
||||
case STATE_GAME_OVER:
|
||||
if (gd.won) animGameOverWin();
|
||||
else animGameOverLose();
|
||||
break;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user