diff --git a/README.md b/README.md
index c67df8b..4d39d2e 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ Because LumenLab is restricted to a single dimension composed of an array of LED
| **Title** | **Description** |
| --- | --- |
| **Recall** |*Recall* is an adaptation of Hasbro's fifty-year-old game [*Simon*](https://en.wikipedia.org/wiki/Simon_(game)), chosen for its familiarity and simplicity. Players easily recognize the format, making it a natural fit for LumenLab’s constraints. Similar to the original, the LED strip flashes a color, and the player must press the matching button on the PS3 controller. Each round adds another color to the sequence, which must be recalled in full and in order.|
-| **Phase Evasion** | *Phase Evasion* is a game inspired by Google Chrome's [*Dinosaur Game*](https://en.wikipedia.org/wiki/Dinosaur_Game), replacing jumping and ducking obstacles with color-based evasion. You are a solid-colored specter pursued by incoming flares. To survive, you must phase shift to match each flare’s color and blend in to evade capture. As the game progresses, the flares accelerate, testing reflexes and timing.
*In development.* |
+| **Phase Evasion** | *Phase Evasion* is a fast, colorful test of reflexes and hand-eye coordination. You are a color-shifting specter that is being pursued by colorful flares. You must match their color at the right moment to phase through them to evade. Gems are scattered throughout periodically. Quickly move over and grab them for bonus points if you dare. As the speed climbs and the screen fills with color, quick reactions and bold moves are the only way to stay in the game. |
| **Light Strike** | *Light Strike* combines the spirit of Atari's [*Space Invaders*](https://en.wikipedia.org/wiki/Space_Invaders) with elements of [*Asteroids*](https://en.wikipedia.org/wiki/Asteroids_(video_game)). As a lone agent in deep space, you enounter waves of predators approaching you from directions sides at varying speeds. Moving freely across the x-axis, and your survival requires defending yourself with your laser pistol. Conserve your ammunition however, your weapon can only fire so many rounds a minute!
*In development.* |
| **Reflex** | *Reflex* is a fast-moving inspired by the modern arcade game [*Pop the Lock*](https://www.baytekent.com/pop-the-lock/), testing precision and timing under pressure. A moving marker sweeps across the field toward a shifting target. Fire at the perfect moment to score a hit. Each success quickens the pace, reverses the marker's direction, and relocates the target to a new random location. Survive fifty flawless shots to win.
*In development.* |
| **Spectrum** | *Spectrum* tests your perception of color and precision of control. A random RGB color appears on one end of the display, and you must use the analog stick to recreate it as closely as possible. The closer your match, the higher your score.
*In development* |
diff --git a/include/core/context-manager.h b/include/core/context-manager.h
index acf2aec..4d51781 100644
--- a/include/core/context-manager.h
+++ b/include/core/context-manager.h
@@ -2,7 +2,6 @@
#include "engine/layer.h"
#include "engine/state-manager.h"
-#include "core/configuration.h"
#include "player/controller.h"
#include "lights/led-strip.h"
#include "display/display.h"
@@ -19,7 +18,6 @@ namespace SystemCore
Engine::Layer *application = nullptr;
Engine::StateManager stateManager;
- SystemCore::Configuration config;
Player::Controller controller;
Lights::LedStrip leds;
Display::OledDisplay display;
diff --git a/include/engine/engine.h b/include/engine/engine.h
index 634bf66..4c9b742 100644
--- a/include/engine/engine.h
+++ b/include/engine/engine.h
@@ -8,9 +8,6 @@ namespace Engine
{
public:
GameEngine();
- virtual ~GameEngine() = default;
- GameEngine(const GameEngine &) = delete;
- GameEngine &operator=(const GameEngine &) = delete;
void runApplication();
void standbyControllerConnection();
@@ -18,12 +15,13 @@ namespace Engine
private:
SystemCore::ContextManager contextManager;
uint32_t lastRender = 0;
+ float disconnectedLedPhaseShift = 0;
+
void initializeEngine();
void renderLedStrip();
void displayMainMenuSelection();
void displayGameSelection();
void displaySceneSelection();
static void displayTask(void *param);
- float disconnectedLedPhaseShift = 0;
};
}
\ No newline at end of file
diff --git a/include/engine/layer.h b/include/engine/layer.h
index 3e286f5..4907316 100644
--- a/include/engine/layer.h
+++ b/include/engine/layer.h
@@ -5,7 +5,6 @@ namespace Engine
class Layer
{
public:
- Layer() {};
virtual void nextEvent() = 0;
};
}
\ No newline at end of file
diff --git a/include/engine/state-manager.h b/include/engine/state-manager.h
index fa116d6..5f3e67c 100644
--- a/include/engine/state-manager.h
+++ b/include/engine/state-manager.h
@@ -54,10 +54,12 @@ namespace Engine
StateManager() : systemState{SystemState::Initialize},
userMainMenuChoice{MainMenuSelection::Games},
userGameChoice{GameSelection::Recall} {}
- bool isRunning() { return systemState != SystemState::Error; }
+
bool displayShouldUpdate = true;
bool displayIsVisible = true;
+ bool isRunning() const { return systemState != SystemState::Error; }
+
const SystemState current() const { return systemState; }
void setNext(SystemState state);
@@ -83,11 +85,11 @@ namespace Engine
Scenes::Canvas::SceneState &getCanvasSceneState() { return canvasSceneState; }
private:
- SystemState systemState = SystemState::MenuHome;
+ SystemState systemState;
- MainMenuSelection userMainMenuChoice = MainMenuSelection::Games;
- GameSelection userGameChoice = GameSelection::Demo;
- SceneSelection userSceneChoice = SceneSelection::Canvas;
+ MainMenuSelection userMainMenuChoice;
+ GameSelection userGameChoice;
+ SceneSelection userSceneChoice;
Games::Recall::GameState recallGameState;
Games::PhaseEvasion::GameState phaseEvasionGameState;
diff --git a/include/engine/timer.h b/include/engine/timer.h
index 20a9f17..ae4125d 100644
--- a/include/engine/timer.h
+++ b/include/engine/timer.h
@@ -7,12 +7,9 @@ namespace Engine
class Timer
{
public:
- Timer() = default;
- virtual ~Timer() = default;
-
void wait(uint32_t futureTime);
const bool isReady() const;
- uint32_t nextOccurrence() { return next; }
+ uint32_t nextOccurrence() const { return next; }
private:
uint32_t next{millis()};
diff --git a/include/games/demo/controller.h b/include/games/demo/driver.h
similarity index 75%
rename from include/games/demo/controller.h
rename to include/games/demo/driver.h
index c750e7e..b71f0a3 100644
--- a/include/games/demo/controller.h
+++ b/include/games/demo/driver.h
@@ -6,10 +6,10 @@
namespace Games::Demo
{
- class Controller : public Engine::Layer
+ class Driver : public Engine::Layer
{
public:
- Controller(SystemCore::ContextManager *ctx) : Engine::Layer{}, contextManager{ctx}, player1{Player{ctx}}, player2{Player{ctx}}
+ Driver(SystemCore::ContextManager *ctx) : Engine::Layer{}, contextManager{ctx}, player1{Player{ctx, width}}, player2{Player{ctx, width}}
{
contextManager->stateManager.getDemoGameState().reset();
}
@@ -25,5 +25,6 @@ namespace Games::Demo
Player player1;
Player player2;
static constexpr float speed = 4.0f;
+ static constexpr uint16_t width = 7;
};
}
\ No newline at end of file
diff --git a/include/games/demo/player.h b/include/games/demo/player.h
index a4f4252..cd1b79f 100644
--- a/include/games/demo/player.h
+++ b/include/games/demo/player.h
@@ -5,15 +5,11 @@
namespace Games::Demo
{
- class Player : public ::Player::Player
+ class Player : public ::Player::BasePlayer
{
public:
- Player(SystemCore::ContextManager *ctx) : contextManager{ctx}, ::Player::Player{ctx} {};
+ Player(SystemCore::ContextManager *ctx, const uint16_t width) : ::Player::BasePlayer{ctx, width} {};
void updatePlayer1LedBuffer();
void updatePlayer2LedBuffer();
-
- private:
- SystemCore::ContextManager *contextManager;
- static constexpr uint16_t width = 7;
};
}
\ No newline at end of file
diff --git a/include/games/phase-evasion/controller.h b/include/games/phase-evasion/controller.h
deleted file mode 100644
index 083a9e6..0000000
--- a/include/games/phase-evasion/controller.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include "engine/layer.h"
-#include "engine/timer.h"
-#include "core/context-manager.h"
-#include "games/phase-evasion/player.h"
-#include "games/phase-evasion/flare.h"
-#include "games/phase-evasion/flare-manager.h"
-
-namespace Games::PhaseEvasion
-{
- class Controller : public Engine::Layer, private Engine::Timer
- {
- public:
- Controller(SystemCore::ContextManager *ctx);
- void nextEvent() override;
- static constexpr uint16_t playerClearance = 20;
-
- private:
- SystemCore::ContextManager *contextManager;
- GameState &state = contextManager->stateManager.getPhaseEvasionGameState();
- Player player;
- FlareManager flareManager;
-
- void getUpdates();
- void renderUserColor();
- void renderFlare();
- void checkCollision();
- void checkGrowth();
- };
-}
\ No newline at end of file
diff --git a/include/games/phase-evasion/driver.h b/include/games/phase-evasion/driver.h
new file mode 100644
index 0000000..0ecb6ba
--- /dev/null
+++ b/include/games/phase-evasion/driver.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include "engine/layer.h"
+#include "engine/timer.h"
+#include "core/context-manager.h"
+#include "games/phase-evasion/player.h"
+#include "games/phase-evasion/flare.h"
+#include "games/phase-evasion/flare-manager.h"
+#include "games/phase-evasion/gem.h"
+
+namespace Games::PhaseEvasion
+{
+ class Driver : public Engine::Layer, private Engine::Timer
+ {
+ public:
+ Driver(SystemCore::ContextManager *ctx);
+ void nextEvent() override;
+ static constexpr uint16_t playerOffset = 25;
+ static constexpr uint16_t playerWidth = 5;
+ static constexpr uint32_t windDownLength = 20'000;
+
+ private:
+ SystemCore::ContextManager *contextManager;
+ GameState &state = contextManager->stateManager.getPhaseEvasionGameState();
+
+ Player player;
+ FlareManager flareManager;
+ Gem gem;
+
+ Engine::Timer windDownTimer;
+ Engine::Timer gemTimeoutTimer;
+
+ float interval;
+ float gap;
+ float speed;
+
+ uint32_t gemRespawnDelay = 5000;
+ uint32_t gemCaptureDelay = 15000;
+
+ float gameOverPhaseShift = 0.0f;
+ float gameOverPhaseOffset = 0.0f;
+
+ void getUpdates();
+ void renderPlayer();
+ void renderFlare();
+ void renderGem();
+ void checkCollision();
+ void checkGemCapture();
+ void assessDifficulty();
+ void muzzleFlash();
+ void gameOver();
+ void reset();
+ };
+}
\ No newline at end of file
diff --git a/include/games/phase-evasion/flare-manager.h b/include/games/phase-evasion/flare-manager.h
index e2db21f..db986c7 100644
--- a/include/games/phase-evasion/flare-manager.h
+++ b/include/games/phase-evasion/flare-manager.h
@@ -1,5 +1,6 @@
#pragma once
+#include "core/context-manager.h"
#include "games/phase-evasion/flare.h"
namespace Games::PhaseEvasion
@@ -7,7 +8,7 @@ namespace Games::PhaseEvasion
class FlareManager : public Engine::Timer
{
public:
- FlareManager() = default;
+ FlareManager(SystemCore::ContextManager *ctx) : contextManager{ctx} {}
FlareManager(const FlareManager &) = delete;
FlareManager &operator=(const FlareManager &) = delete;
@@ -22,9 +23,11 @@ namespace Games::PhaseEvasion
const size_t size() const;
void updatePositions();
- void dispatch();
+ void dispatch(float speed);
+ void reset();
private:
+ SystemCore::ContextManager *contextManager;
std::array flarePool;
};
}
\ No newline at end of file
diff --git a/include/games/phase-evasion/flare.h b/include/games/phase-evasion/flare.h
index a5ddcfe..453c7e1 100644
--- a/include/games/phase-evasion/flare.h
+++ b/include/games/phase-evasion/flare.h
@@ -13,10 +13,7 @@ namespace Games::PhaseEvasion
class Flare : public Engine::Timer
{
public:
- Flare() : active{false},
- color{Lights::Color::WhiteSmoke},
- speed{0.0f},
- positionFloat{SystemCore::Configuration::numLeds + width} {};
+ Flare() { reset(); };
static constexpr uint16_t width = 10;
void updatePosition();
@@ -26,6 +23,9 @@ namespace Games::PhaseEvasion
bool isActive() const { return active; }
void activate(Lights::Color color, float speed);
void deactivate();
+ bool completedCycle = false;
+ bool impacted = false;
+ void reset();
private:
bool active;
diff --git a/include/games/phase-evasion/gem.h b/include/games/phase-evasion/gem.h
new file mode 100644
index 0000000..0d60774
--- /dev/null
+++ b/include/games/phase-evasion/gem.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include
+
+#include "core/configuration.h"
+
+namespace Games::PhaseEvasion
+{
+
+ class Gem : public Engine::Timer
+ {
+ public:
+ Gem() : active{false} { spawn(0); }
+
+ static constexpr uint16_t width = 3;
+ uint16_t getPosition() { return position; }
+ bool isActive() { return active; }
+
+ Lights::Color getColor() { return Lights::Color::NavajoWhite; }
+
+ void capture() { active = false; }
+ void spawn(const uint16_t pos)
+ {
+ bool isOutsideLeftRegion = pos < (width - 1);
+ bool isOutsideRightRegion = pos > SystemCore::Configuration::numLeds + (width - 1);
+ if (isOutsideLeftRegion || isOutsideRightRegion)
+ return;
+
+ position = pos;
+ active = true;
+ }
+
+ private:
+ uint16_t position;
+ bool active;
+ };
+}
\ No newline at end of file
diff --git a/include/games/phase-evasion/player.h b/include/games/phase-evasion/player.h
index 830fe91..5551044 100644
--- a/include/games/phase-evasion/player.h
+++ b/include/games/phase-evasion/player.h
@@ -5,21 +5,14 @@
namespace Games::PhaseEvasion
{
- class Player : public ::Player::Player
+ class Player : public ::Player::BasePlayer
{
public:
- Player(SystemCore::ContextManager *ctx) : contextManager{ctx}, ::Player::Player{ctx} {};
+ Player(SystemCore::ContextManager *ctx, uint16_t width) : ::Player::BasePlayer{ctx, width} {};
void checkColorChangeRequest();
Lights::Color getColor() { return currentColor; }
- static constexpr uint16_t width = 10;
private:
- SystemCore::ContextManager *contextManager;
Lights::Color currentColor;
- static constexpr ::Player::ControllerButton availableGameplayButtons[] = {
- ::Player::ControllerButton::Cross,
- ::Player::ControllerButton::Square,
- ::Player::ControllerButton::Triangle,
- ::Player::ControllerButton::Circle};
};
}
\ No newline at end of file
diff --git a/include/games/phase-evasion/state.h b/include/games/phase-evasion/state.h
index 4c9778c..7e31976 100644
--- a/include/games/phase-evasion/state.h
+++ b/include/games/phase-evasion/state.h
@@ -8,16 +8,19 @@ namespace Games::PhaseEvasion
{
Startup,
ActiveGame,
+ WindDown,
+ MuzzleFlash,
GameOver
};
class GameState
{
public:
- GameState() : highScore{0}, spectersDodged{0} {}
+ GameState() : highScore{0}, flaresEvaded{0}, gemsCaptured{0} {}
+ uint16_t flaresEvaded;
+ uint16_t gemsCaptured;
uint16_t highScore;
- uint16_t spectersDodged;
Actions current = Actions::Startup;
- void reset() { highScore = spectersDodged = 0; }
+ void reset() { highScore = flaresEvaded = gemsCaptured = 0; }
};
}
\ No newline at end of file
diff --git a/include/games/recall/controller.h b/include/games/recall/driver.h
similarity index 75%
rename from include/games/recall/controller.h
rename to include/games/recall/driver.h
index 81c4777..9f2a75e 100644
--- a/include/games/recall/controller.h
+++ b/include/games/recall/driver.h
@@ -5,13 +5,14 @@
#include "engine/timer.h"
#include "lights/color.h"
#include "lights/color-code.h"
+#include "player/player.h"
namespace Games::Recall
{
- class Controller : public Engine::Layer, private Engine::Timer
+ class Driver : public Engine::Layer, private Engine::Timer
{
public:
- Controller(SystemCore::ContextManager *ctx);
+ Driver(SystemCore::ContextManager *ctx);
void nextEvent() override;
private:
@@ -21,11 +22,7 @@ namespace Games::Recall
uint16_t gameplaySpeedIlluminated = 500;
uint16_t gameplaySpeedPaused = gameplaySpeedIlluminated / 6;
static constexpr uint16_t maxRound = 1000;
- static constexpr Player::ControllerButton availableGameplayButtons[] = {
- Player::ControllerButton::Cross,
- Player::ControllerButton::Square,
- Player::ControllerButton::Triangle,
- Player::ControllerButton::Circle};
+ static constexpr auto &availableGameplayButtons = ::Player::BasePlayer::availableGameplayButtons;
uint16_t sequenceIndex = 0;
Player::ControllerButton gameplayColors[maxRound];
float gameOverLedPhaseShift = 0.0f;
diff --git a/include/lights/led-buffer.h b/include/lights/led-buffer.h
index 8c3abda..67396ef 100644
--- a/include/lights/led-buffer.h
+++ b/include/lights/led-buffer.h
@@ -2,40 +2,19 @@
#include
+#include "core/configuration.h"
+
namespace Lights
{
class LedBuffer
{
public:
- LedBuffer(uint16_t numLeds) : _size{numLeds}, leds{new Color[numLeds]} {}
+ LedBuffer() : leds{new Color[SystemCore::Configuration::numLeds]} {}
~LedBuffer() { delete leds; }
LedBuffer(LedBuffer &&other) = delete;
LedBuffer &operator=(LedBuffer &&other) = delete;
-
- LedBuffer(const LedBuffer &other) : _size(other._size), leds(new Color[other._size])
- {
- for (uint16_t i = 0; i < _size; ++i)
- {
- leds[i] = other.leds[i];
- }
- }
-
- LedBuffer &operator=(const LedBuffer &other)
- {
- if (this != &other)
- {
- Color *newLeds = new Color[other._size];
- for (uint16_t i = 0; i < other._size; ++i)
- {
- newLeds[i] = other.leds[i];
- }
-
- delete[] leds;
- leds = newLeds;
- _size = other._size;
- }
- return *this;
- }
+ LedBuffer(const LedBuffer &other) = delete;
+ LedBuffer &operator=(const LedBuffer &other) = delete;
Color &operator[](uint16_t index) { return leds[index]; }
const Color &operator[](uint16_t index) const { return leds[index]; }
@@ -43,10 +22,9 @@ namespace Lights
explicit operator Color *() { return leds; }
explicit operator const Color *() const { return leds; }
- uint16_t size() { return _size; }
+ uint16_t size() { return SystemCore::Configuration::numLeds; }
private:
- uint16_t _size;
Color *leds;
};
}
\ No newline at end of file
diff --git a/include/lights/led-luminance.h b/include/lights/led-luminance.h
index d10a4b1..d5637b6 100644
--- a/include/lights/led-luminance.h
+++ b/include/lights/led-luminance.h
@@ -7,7 +7,7 @@ namespace Lights
class LedLuminance
{
public:
- LedLuminance(SystemCore::Configuration &configuration) : config{configuration} {}
+ LedLuminance() {}
static constexpr float MAX_LED_BRIGHTNESS = 255.0f;
static constexpr int MAX_ADC_READING = 4095;
@@ -17,7 +17,6 @@ namespace Lights
static uint8_t applyGamma(uint8_t value);
private:
- const SystemCore::Configuration &config;
int currentLuminance = MAX_ADC_READING;
};
}
\ No newline at end of file
diff --git a/include/lights/led-strip.h b/include/lights/led-strip.h
index c7dadcd..db2ffe6 100644
--- a/include/lights/led-strip.h
+++ b/include/lights/led-strip.h
@@ -10,8 +10,8 @@ namespace Lights
class LedStrip
{
public:
+ LedStrip();
LedBuffer buffer;
- LedStrip(SystemCore::Configuration &configuration);
Color *getRawColors();
static constexpr uint16_t size() { return SystemCore::Configuration::numLeds; }
@@ -19,7 +19,6 @@ namespace Lights
void adjustLuminance();
private:
- const SystemCore::Configuration &config;
LedLuminance luminance;
};
}
\ No newline at end of file
diff --git a/include/player/controller.h b/include/player/controller.h
index 3ca1285..42d7943 100644
--- a/include/player/controller.h
+++ b/include/player/controller.h
@@ -66,9 +66,6 @@ namespace Player
void poll();
bool isDown(const ControllerButton button) const;
void reset();
- float analogToSpeed(int value, float maxOutput) const;
- void setResponseExponent(float exp) { responseExponent = exp; }
- void setResponseBlend(float blend) { responseBlend = blend; }
private:
Ps3Controller controller;
@@ -77,8 +74,7 @@ namespace Player
bool buttonLastState[17] = {0};
bool buttonPressedEvent[17] = {0};
bool buttonReleasedEvent[17] = {0};
- float responseExponent = 2.0f;
- float responseBlend = 0.35f;
+
static Controller *instance;
bool connection = false;
diff --git a/include/player/player.h b/include/player/player.h
index 6ded16e..35bf3b6 100644
--- a/include/player/player.h
+++ b/include/player/player.h
@@ -4,16 +4,33 @@
namespace Player
{
- class Player
+ class BasePlayer
{
public:
- Player(SystemCore::ContextManager *ctx) : contextManager{ctx} {};
- void move(const int distance, const float speed);
- uint16_t getPosition() { return position; }
+ BasePlayer(SystemCore::ContextManager *ctx, const uint16_t w) : contextManager{ctx}, width{w} {};
+
+ const uint16_t width;
+
+ void move(const int distance, const float speed, const bool shouldWrap = true);
+
+ uint16_t getPosition() { return static_cast(positionPrecise); }
+ void setPosition(float position) { positionPrecise = position; }
+ void setPosition(uint16_t position) { positionPrecise = static_cast(position); }
+
+ float analogToSpeed(int value, float maxOutput) const;
+
+ static constexpr ControllerButton availableGameplayButtons[] = {
+ ControllerButton::Cross,
+ ControllerButton::Square,
+ ControllerButton::Triangle,
+ ControllerButton::Circle};
protected:
SystemCore::ContextManager *contextManager;
- uint16_t position = 0;
float positionPrecise = 0.0f;
+ float responseExponent = 2.0f;
+ float responseBlend = 0.35f;
};
+
+ inline constexpr auto &availableGameplayButtons = BasePlayer::BasePlayer::availableGameplayButtons;
}
\ No newline at end of file
diff --git a/include/scenes/canvas/controller.h b/include/scenes/canvas/driver.h
similarity index 84%
rename from include/scenes/canvas/controller.h
rename to include/scenes/canvas/driver.h
index d62998f..5e20db0 100644
--- a/include/scenes/canvas/controller.h
+++ b/include/scenes/canvas/driver.h
@@ -6,10 +6,10 @@
namespace Scenes::Canvas
{
- class Controller : public Engine::Layer, private Engine::Timer
+ class Driver : public Engine::Layer, private Engine::Timer
{
public:
- Controller(SystemCore::ContextManager *ctx);
+ Driver(SystemCore::ContextManager *ctx);
void nextEvent() override;
private:
diff --git a/src/core/context-manager.cpp b/src/core/context-manager.cpp
index 422fdc5..927bc6b 100644
--- a/src/core/context-manager.cpp
+++ b/src/core/context-manager.cpp
@@ -1,13 +1,13 @@
#include "core/context-manager.h"
-#include "games/demo/controller.h"
-#include "games/recall/controller.h"
-#include "games/phase-evasion/controller.h"
-#include "scenes/canvas/controller.h"
+#include "games/demo/driver.h"
+#include "games/recall/driver.h"
+#include "games/phase-evasion/driver.h"
+#include "scenes/canvas/driver.h"
#include "logger.h"
namespace SystemCore
{
- ContextManager::ContextManager() : leds{config}, display{this} {}
+ ContextManager::ContextManager() : display{this} {}
ContextManager::~ContextManager()
{
@@ -150,19 +150,19 @@ namespace SystemCore
switch (stateManager.current())
{
case Engine::SystemState::GameRecall:
- application = new Games::Recall::Controller{this};
+ application = new Games::Recall::Driver{this};
logf("Transitioning to Recall (Game)");
break;
case Engine::SystemState::GamePhaseEvasion:
- application = new Games::PhaseEvasion::Controller{this};
+ application = new Games::PhaseEvasion::Driver{this};
logf("Transitioning to Phase Evasion (Game)");
break;
case Engine::SystemState::GameDemo:
- application = new Games::Demo::Controller{this};
+ application = new Games::Demo::Driver{this};
logf("Transitioning to Demo (Game)");
break;
case Engine::SystemState::SceneCanvas:
- application = new Scenes::Canvas::Controller{this};
+ application = new Scenes::Canvas::Driver{this};
logf("Transitioning to Canvas (Scene)");
break;
}
diff --git a/src/display/display.cpp b/src/display/display.cpp
index 59a53b3..3fcb149 100644
--- a/src/display/display.cpp
+++ b/src/display/display.cpp
@@ -185,14 +185,21 @@ namespace Display
void OledDisplay::drawPhaseEvasionGameHud()
{
+ const auto flaresEvaded = contextManager->stateManager.getPhaseEvasionGameState().flaresEvaded;
+ const auto gemsCaptured = contextManager->stateManager.getPhaseEvasionGameState().gemsCaptured;
+
display.clearDisplay();
drawHeader("Phase Evasion");
display.setCursor(0, 16);
- display.print("Specters Dodged: ");
- display.print(contextManager->stateManager.getPhaseEvasionGameState().spectersDodged);
+ display.printf("Flares: %u", flaresEvaded);
+ display.setCursor(DISPLAY_WIDTH / 2 + 4, 16);
+ display.printf("Total: %u", (flaresEvaded + (2 * gemsCaptured)));
+
display.setCursor(0, 24);
- display.print("High Score: -");
+ display.printf(" Gems: %u", gemsCaptured);
+ display.setCursor(DISPLAY_WIDTH / 2 + 4, 24);
+ display.printf(" High: %u", (flaresEvaded + (2 * gemsCaptured)));
display.display();
}
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
index 7044a89..d0700e5 100644
--- a/src/engine/engine.cpp
+++ b/src/engine/engine.cpp
@@ -69,11 +69,11 @@ namespace Engine
void GameEngine::initializeEngine()
{
- contextManager.controller.begin(contextManager.config.macAddress);
+ contextManager.controller.begin(SystemCore::Configuration::macAddress);
// If debugging, ensure serial connection is stable before setting up components
#if defined(VIRTUALIZATION) || defined(DEBUG)
- Serial.begin(contextManager.config.serialBaud);
+ Serial.begin(SystemCore::Configuration::serialBaud);
contextManager.leds.reset();
renderLedStrip();
log("Connecting to computer using a serial connection for debugging.");
@@ -127,14 +127,14 @@ namespace Engine
return;
}
- for (uint16_t i = 0; i <= contextManager.leds.size(); ++i)
+ for (uint16_t i = 0; i <= SystemCore::Configuration::numLeds; ++i)
{
- float phase = std::cos((2 * M_PI * i / contextManager.leds.size()) + (2 * M_PI * disconnectedLedPhaseShift / contextManager.leds.size())) * 127 + 128;
+ float phase = std::cos((2 * M_PI * i / SystemCore::Configuration::numLeds) + (2 * M_PI * disconnectedLedPhaseShift / SystemCore::Configuration::numLeds)) * 127 + 128;
contextManager.leds.buffer[i] = {static_cast(std::floor(phase)), 0, 0};
}
disconnectedLedPhaseShift += 0.5;
- if (disconnectedLedPhaseShift > contextManager.leds.size())
+ if (disconnectedLedPhaseShift > SystemCore::Configuration::numLeds)
disconnectedLedPhaseShift = 0;
}
@@ -142,7 +142,7 @@ namespace Engine
{
constexpr uint8_t numOfSupportedModes = static_cast(MainMenuSelection::COUNT);
uint8_t option = static_cast(contextManager.stateManager.getUserMenuChoice());
- uint16_t boundaryWidth = contextManager.leds.size() / numOfSupportedModes;
+ uint16_t boundaryWidth = SystemCore::Configuration::numLeds / numOfSupportedModes;
uint16_t boundaryStart = boundaryWidth * option;
uint16_t boundaryEnd = boundaryWidth * (option + 1);
double mu = (boundaryStart + boundaryEnd) / 2.0;
@@ -160,7 +160,7 @@ namespace Engine
{
constexpr uint8_t numOfSupportedModes = static_cast(GameSelection::COUNT);
uint8_t option = static_cast(contextManager.stateManager.getUserGameChoice());
- uint16_t boundaryWidth = contextManager.leds.size() / numOfSupportedModes;
+ uint16_t boundaryWidth = SystemCore::Configuration::numLeds / numOfSupportedModes;
uint16_t boundaryStart = boundaryWidth * option;
uint16_t boundaryEnd = boundaryWidth * (option + 1);
double mu = (boundaryStart + boundaryEnd) / 2.0;
@@ -178,7 +178,7 @@ namespace Engine
{
constexpr uint8_t numOfSupportedModes = static_cast(SceneSelection::COUNT);
uint8_t option = static_cast(contextManager.stateManager.getUserSceneChoice());
- uint16_t boundaryWidth = contextManager.leds.size() / numOfSupportedModes;
+ uint16_t boundaryWidth = SystemCore::Configuration::numLeds / numOfSupportedModes;
uint16_t boundaryStart = boundaryWidth * option;
uint16_t boundaryEnd = boundaryWidth * (option + 1);
double mu = (boundaryStart + boundaryEnd) / 2.0;
@@ -201,7 +201,7 @@ namespace Engine
#ifdef VIRTUALIZATION
Serial.write(0xAA); // sync bytes
Serial.write(0x55);
- Serial.write(reinterpret_cast(contextManager.leds.getRawColors()), contextManager.leds.size() * sizeof(Lights::Color));
+ Serial.write(reinterpret_cast(contextManager.leds.getRawColors()), SystemCore::Configuration::numLeds * sizeof(Lights::Color));
#endif
}
diff --git a/src/games/demo/core.cpp b/src/games/demo/driver.cpp
similarity index 85%
rename from src/games/demo/core.cpp
rename to src/games/demo/driver.cpp
index b5ffe14..ec8f0d4 100644
--- a/src/games/demo/core.cpp
+++ b/src/games/demo/driver.cpp
@@ -1,10 +1,10 @@
-#include "games/demo/controller.h"
+#include "games/demo/driver.h"
#include "player/controller.h"
#include "logger.h"
namespace Games::Demo
{
- void Controller::nextEvent()
+ void Driver::nextEvent()
{
if (contextManager->controller.wasPressed(::Player::ControllerButton::Cross))
{
@@ -20,13 +20,13 @@ namespace Games::Demo
player2.updatePlayer2LedBuffer();
}
- void Controller::incrementCurrentScore()
+ void Driver::incrementCurrentScore()
{
++contextManager->stateManager.getDemoGameState().currentScore;
contextManager->stateManager.displayShouldUpdate = true;
}
- void Controller::incrementHighScore()
+ void Driver::incrementHighScore()
{
++contextManager->stateManager.getDemoGameState().highScore;
contextManager->stateManager.displayShouldUpdate = true;
diff --git a/src/games/demo/player.cpp b/src/games/demo/player.cpp
index 645df10..bf9c82b 100644
--- a/src/games/demo/player.cpp
+++ b/src/games/demo/player.cpp
@@ -8,9 +8,9 @@ namespace Games::Demo
void Player::updatePlayer1LedBuffer()
{
float center = (width + 1) / 2.0f;
- for (uint16_t i = 1; i <= width; ++i)
+ for (uint16_t i = 0; i <= width; ++i)
{
- uint16_t index = (position + i) % contextManager->leds.size();
+ uint16_t index = (getPosition() + i) % SystemCore::Configuration::numLeds;
float intensity = 1.0f - std::abs(i - center) / center;
if (intensity < 0)
intensity = 0;
@@ -25,9 +25,9 @@ namespace Games::Demo
{
float center = (width + 1) / 2.0f;
- for (uint16_t i = 1; i <= width; ++i)
+ for (uint16_t i = 0; i <= width; ++i)
{
- uint16_t index = (position + i) % contextManager->leds.size();
+ uint16_t index = (getPosition() + i) % SystemCore::Configuration::numLeds;
float intensity = 1.0f - std::abs(i - center) / center;
if (intensity < 0)
intensity = 0;
diff --git a/src/games/phase-evasion/controller.cpp b/src/games/phase-evasion/controller.cpp
deleted file mode 100644
index 66ddbfe..0000000
--- a/src/games/phase-evasion/controller.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-#include "games/phase-evasion/controller.h"
-#include "logger.h"
-
-namespace Games::PhaseEvasion
-{
- Controller::Controller(SystemCore::ContextManager *ctx) : contextManager{ctx},
- player{ctx}
- {
- state = contextManager->stateManager.getPhaseEvasionGameState();
- state.reset();
- state.current = Actions::Startup;
- wait(500);
- }
-
- void Controller::nextEvent()
- {
- switch (state.current)
- {
- case Actions::Startup:
- if (isReady())
- {
- state.current = Actions::ActiveGame;
- flareManager.dispatch();
- log("Starting new game.");
- wait(1000);
- }
- break;
- case Actions::ActiveGame:
- getUpdates();
- checkGrowth();
- checkCollision();
- renderFlare();
- renderUserColor();
- break;
- case Actions::GameOver:
- for (uint16_t i = 0; i < contextManager->config.numLeds; ++i)
- {
- contextManager->leds.buffer[i] = Lights::ColorCode::GameRed;
- }
- break;
- }
- }
-
- void Controller::getUpdates()
- {
- player.checkColorChangeRequest();
- flareManager.updatePositions();
- }
-
- void Controller::renderUserColor()
- {
- for (uint16_t i = playerClearance; i < playerClearance + player.width; ++i)
- {
- contextManager->leds.buffer[i] = player.getColor();
- }
- }
-
- void Controller::renderFlare()
- {
- for (const auto &flare : flareManager)
- {
- if (!flare.isActive())
- continue;
-
- uint16_t start = std::max(flare.getPosition() - flare.width, 0);
- uint16_t end = std::min(flare.getPosition(), contextManager->config.numLeds);
-
- for (uint16_t i = start; i < end; ++i)
- {
- contextManager->leds.buffer[i] = flare.getColor();
- }
- }
- }
-
- void Controller::checkCollision()
- {
- for (const auto &flare : flareManager)
- {
- if (!flare.isActive())
- continue;
-
- uint16_t start = std::max(flare.getPosition() - flare.width, 0);
- uint16_t end = std::min(flare.getPosition(), contextManager->config.numLeds);
-
- if (start <= playerClearance + player.width && end >= playerClearance && player.getColor() != flare.getColor())
- {
- state.current = Actions::GameOver;
- }
- }
- }
-
- void Controller::checkGrowth()
- {
- if (isReady())
- {
- flareManager.dispatch();
- wait(1000);
- }
- }
-}
\ No newline at end of file
diff --git a/src/games/phase-evasion/driver.cpp b/src/games/phase-evasion/driver.cpp
new file mode 100644
index 0000000..98e2e4d
--- /dev/null
+++ b/src/games/phase-evasion/driver.cpp
@@ -0,0 +1,249 @@
+#include "games/phase-evasion/driver.h"
+#include "player/controller.h"
+#include "logger.h"
+
+namespace Games::PhaseEvasion
+{
+ Driver::Driver(SystemCore::ContextManager *ctx) : contextManager{ctx},
+ player{ctx, playerWidth},
+ flareManager{ctx}
+ {
+ state = contextManager->stateManager.getPhaseEvasionGameState();
+ state.reset();
+ state.current = Actions::Startup;
+ reset();
+ wait(500);
+ gem.wait(gemRespawnDelay);
+ windDownTimer.wait(windDownLength);
+ }
+
+ void Driver::nextEvent()
+ {
+ switch (state.current)
+ {
+ case Actions::Startup:
+ if (isReady())
+ {
+ state.current = Actions::ActiveGame;
+ log("Starting new game.");
+ }
+ break;
+ case Actions::ActiveGame:
+ case Actions::WindDown:
+ getUpdates();
+ assessDifficulty();
+ checkCollision();
+ checkGemCapture();
+ renderFlare();
+ renderGem();
+ renderPlayer();
+ break;
+ case Actions::MuzzleFlash:
+ muzzleFlash();
+ break;
+ case Actions::GameOver:
+ gameOver();
+ renderFlare();
+ renderGem();
+ renderPlayer();
+ break;
+ }
+ }
+
+ void Driver::getUpdates()
+ {
+ player.checkColorChangeRequest();
+ flareManager.updatePositions();
+ auto leftAnalog = contextManager->controller.leftAnalog();
+ player.move(leftAnalog.x, 1.25, false);
+ }
+
+ void Driver::renderPlayer()
+ {
+ for (uint16_t i = 0; i < player.width; ++i)
+ {
+ uint16_t index = (player.getPosition() + i) % SystemCore::Configuration::numLeds;
+ contextManager->leds.buffer[index] = player.getColor();
+ }
+ }
+
+ void Driver::renderFlare()
+ {
+ for (const auto &flare : flareManager)
+ {
+ if (!flare.isActive())
+ continue;
+
+ if (state.current == Actions::GameOver)
+ {
+ if (!flare.impacted)
+ continue;
+ }
+
+ uint16_t flareHead = std::max(flare.getPosition() - flare.width, 0);
+ uint16_t flareTail = std::min(flare.getPosition(), SystemCore::Configuration::numLeds);
+
+ for (uint16_t i = flareHead; i < flareTail; ++i)
+ {
+ uint16_t distance = i - flareHead;
+ double attenuation = std::clamp(1.0 - 0.08 * static_cast(distance), 0.0, 1.0);
+
+ contextManager->leds.buffer[i] = flare.getColor() * attenuation;
+ }
+ }
+ }
+
+ void Driver::renderGem()
+ {
+ if (!gem.isActive() || !gem.isReady())
+ return;
+
+ uint16_t gemLeft = std::max(gem.getPosition() - gem.width, 0);
+ uint16_t gemRight = std::min(gem.getPosition(), SystemCore::Configuration::numLeds);
+
+ for (uint16_t i = gemLeft; i < gemRight; ++i)
+ {
+ contextManager->leds.buffer[i] = gem.getColor();
+ }
+ }
+
+ void Driver::checkCollision()
+ {
+ for (auto &flare : flareManager)
+ {
+ if (!flare.isActive())
+ continue;
+
+ uint16_t flareStart = std::max(flare.getPosition() - flare.width, 0);
+ uint16_t flareEnd = std::min(flare.getPosition(), SystemCore::Configuration::numLeds);
+
+ bool isUnmatchingColor = player.getColor() != flare.getColor();
+ bool hasEnteredRegion = flareStart <= player.getPosition() + player.width;
+ bool hasNotExitedRegion = flareEnd >= player.getPosition();
+
+ if (isUnmatchingColor && hasEnteredRegion && hasNotExitedRegion)
+ {
+ flare.impacted = true;
+ state.current = Actions::MuzzleFlash;
+ gameOverPhaseShift = static_cast(((SystemCore::Configuration::numLeds / 2) + player.getPosition()) % SystemCore::Configuration::numLeds);
+ wait(20);
+ }
+ }
+ }
+
+ void Driver::checkGemCapture()
+ {
+ if (!gem.isActive() && gem.isReady())
+ {
+ const auto randGemPosition = static_cast((esp_random() % static_cast(SystemCore::Configuration::numLeds)) + gem.width);
+ gem.spawn(randGemPosition);
+ gemTimeoutTimer.wait(gemCaptureDelay);
+ }
+
+ if (gem.isActive() && gem.isReady())
+ {
+ if (!gemTimeoutTimer.isReady())
+ {
+ uint16_t gemStart = std::max(gem.getPosition() - gem.width, 0);
+ uint16_t gemEnd = std::min(gem.getPosition(), SystemCore::Configuration::numLeds);
+
+ bool hasEnteredRegion = gemStart <= player.getPosition() + player.width;
+ bool hasNotExitedRegion = gemEnd >= player.getPosition();
+
+ if (hasEnteredRegion && hasNotExitedRegion) // player captured the gem
+ {
+ gem.capture();
+ contextManager->stateManager.getPhaseEvasionGameState().gemsCaptured++;
+ contextManager->stateManager.displayShouldUpdate = true;
+ gem.wait(gemRespawnDelay);
+ }
+ }
+ else // gem timed out, player took too long
+ {
+ gem.capture();
+ gem.wait(gemRespawnDelay);
+ }
+ }
+ }
+
+ void Driver::assessDifficulty()
+ {
+ if (isReady())
+ {
+ if (windDownTimer.isReady())
+ {
+ state.current = Actions::WindDown;
+ }
+
+ if (state.current == Actions::ActiveGame)
+ {
+ flareManager.dispatch(speed);
+ uint32_t timeDelay = static_cast((esp_random() % static_cast(interval)) + gap);
+ wait(timeDelay);
+ }
+
+ bool shouldStartNextRound = state.current == Actions::WindDown && flareManager.size() == 2;
+ if (shouldStartNextRound)
+ {
+ windDownTimer.wait(windDownLength);
+ state.current = Actions::ActiveGame;
+ speed *= 1.07;
+ interval *= 0.8;
+ gap *= 0.82;
+ }
+ }
+ }
+
+ void Driver::muzzleFlash()
+ {
+ for (uint16_t i = 0; i < SystemCore::Configuration::numLeds; ++i)
+ {
+ contextManager->leds.buffer[i] = Lights::Color::White;
+ }
+
+ if (isReady())
+ {
+ state.current = Actions::GameOver;
+ }
+ }
+
+ void Driver::gameOver()
+ {
+
+ for (uint16_t i = 0; i <= SystemCore::Configuration::numLeds; ++i)
+ {
+ float offset = std::cos((2.0f * M_PI * i / SystemCore::Configuration::numLeds) - (2.0f * M_PI * static_cast(gameOverPhaseShift) / SystemCore::Configuration::numLeds));
+ float phase = offset * 127.5 + 127.5;
+ contextManager->leds.buffer[i] = {static_cast(std::floor(phase) * 0.95),
+ static_cast(std::floor(phase) * 0.15),
+ static_cast(std::floor(phase) * 0.25)};
+ }
+
+ gameOverPhaseShift += std::cos(gameOverPhaseOffset) / 16.0f;
+ gameOverPhaseOffset += 1.0 / 32.0f;
+
+ if (gameOverPhaseShift > SystemCore::Configuration::numLeds)
+ gameOverPhaseShift = 0.0;
+
+ if (contextManager->controller.wasPressed(::Player::ControllerButton::Start))
+ {
+ state.current = Actions::Startup;
+ state.reset();
+ contextManager->stateManager.displayShouldUpdate = true;
+ reset();
+ windDownTimer.wait(windDownLength);
+ }
+ }
+
+ void Driver::reset()
+ {
+ interval = 2500;
+ gap = 1600;
+ speed = 0.4f;
+ gem.capture();
+ gem.wait(gemRespawnDelay);
+ flareManager.reset();
+ player.setPosition(static_cast(0));
+ wait(500);
+ }
+}
\ No newline at end of file
diff --git a/src/games/phase-evasion/flare-manager.cpp b/src/games/phase-evasion/flare-manager.cpp
index b30a3a4..5b04f2a 100644
--- a/src/games/phase-evasion/flare-manager.cpp
+++ b/src/games/phase-evasion/flare-manager.cpp
@@ -4,7 +4,7 @@
namespace Games::PhaseEvasion
{
- void FlareManager::dispatch()
+ void FlareManager::dispatch(float speed)
{
auto flare = flarePool.begin();
while (flare != flarePool.end())
@@ -12,7 +12,7 @@ namespace Games::PhaseEvasion
if (!flare->isActive())
{
uint16_t colorIndex = static_cast(esp_random()) % arraySize(Lights::colorPalette);
- flare->activate(Lights::colorPalette[colorIndex], 0.75);
+ flare->activate(Lights::colorPalette[colorIndex], speed);
return;
}
++flare;
@@ -28,6 +28,13 @@ namespace Games::PhaseEvasion
continue;
flare.updatePosition();
+
+ if (flare.completedCycle)
+ {
+ contextManager->stateManager.getPhaseEvasionGameState().flaresEvaded++;
+ contextManager->stateManager.displayShouldUpdate = true;
+ flare.completedCycle = false;
+ }
}
}
@@ -36,4 +43,12 @@ namespace Games::PhaseEvasion
return std::count_if(flarePool.begin(), flarePool.end(), [](const Flare &flare)
{ return flare.isActive(); });
}
+
+ void FlareManager::reset()
+ {
+ for (Flare &flare : flarePool)
+ {
+ flare.reset();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/games/phase-evasion/flare.cpp b/src/games/phase-evasion/flare.cpp
index 8dae6a2..0927093 100644
--- a/src/games/phase-evasion/flare.cpp
+++ b/src/games/phase-evasion/flare.cpp
@@ -4,20 +4,19 @@ namespace Games::PhaseEvasion
{
void Flare::updatePosition()
{
+ positionFloat -= speed;
if (positionFloat <= 0.0f)
{
active = false;
color = Lights::Color::WhiteSmoke;
- }
- else
- {
- positionFloat -= speed;
+ completedCycle = true;
}
}
void Flare::activate(Lights::Color _color, float _speed)
{
active = true;
+ completedCycle = false;
color = _color;
speed = _speed;
positionFloat = static_cast(SystemCore::Configuration::numLeds + width);
@@ -27,4 +26,13 @@ namespace Games::PhaseEvasion
{
active = false;
}
+
+ void Flare::reset()
+ {
+ active = false;
+ color = Lights::Color::WhiteSmoke;
+ speed = 0.0f;
+ impacted = false;
+ positionFloat = static_cast(SystemCore::Configuration::numLeds + width);
+ }
}
\ No newline at end of file
diff --git a/src/games/phase-evasion/player.cpp b/src/games/phase-evasion/player.cpp
index 6ee4599..735f543 100644
--- a/src/games/phase-evasion/player.cpp
+++ b/src/games/phase-evasion/player.cpp
@@ -5,7 +5,7 @@ namespace Games::PhaseEvasion
{
void Player::checkColorChangeRequest()
{
- for (auto button : availableGameplayButtons)
+ for (auto button : Player::availableGameplayButtons)
{
if (contextManager->controller.isDown(button))
{
diff --git a/src/games/recall/controller.cpp b/src/games/recall/driver.cpp
similarity index 81%
rename from src/games/recall/controller.cpp
rename to src/games/recall/driver.cpp
index 55b16ed..b6ac0ab 100644
--- a/src/games/recall/controller.cpp
+++ b/src/games/recall/driver.cpp
@@ -1,12 +1,12 @@
#include "esp_system.h"
-#include "games/recall/controller.h"
+#include "games/recall/driver.h"
#include "common.h"
#include "logger.h"
namespace Games::Recall
{
- Controller::Controller(SystemCore::ContextManager *ctx) : contextManager{ctx}
+ Driver::Driver(SystemCore::ContextManager *ctx) : contextManager{ctx}
{
setupGameColors();
state = contextManager->stateManager.getRecallGameState();
@@ -15,14 +15,14 @@ namespace Games::Recall
wait(gameplaySpeedIlluminated);
}
- void Controller::setupGameColors()
+ void Driver::setupGameColors()
{
for (uint16_t i = 0; i < maxRound; ++i)
{
// for testing
- // gameplayColors[i] = static_cast(i % arraySize(availableGameplayButtons));
+ // gameplayColors[i] = static_cast(i % arraySize(Driver::availableGameplayButtons));
- uint16_t colorIndex = static_cast(esp_random()) % arraySize(availableGameplayButtons);
+ uint16_t colorIndex = static_cast(esp_random()) % arraySize(Driver::availableGameplayButtons);
gameplayColors[i] = static_cast(colorIndex);
}
auto button = gameplayColors[0];
@@ -31,7 +31,7 @@ namespace Games::Recall
logf(" Color=%u (%u - %u - %u)", button, color.r, color.g, color.b);
}
- void Controller::nextEvent()
+ void Driver::nextEvent()
{
handleUserSpeedChange();
switch (state.current)
@@ -61,7 +61,7 @@ namespace Games::Recall
}
}
- void Controller::handleUserSpeedChange()
+ void Driver::handleUserSpeedChange()
{
if (contextManager->controller.wasPressed(Player::ControllerButton::Up) || contextManager->controller.leftAnalog().y < -64)
{
@@ -77,7 +77,7 @@ namespace Games::Recall
}
}
- void Controller::displayComputerPlayback()
+ void Driver::displayComputerPlayback()
{
if (isReady())
{
@@ -88,11 +88,12 @@ namespace Games::Recall
auto boundaries = directionBoundaries(gameplayColors[sequenceIndex]);
double mu = (boundaries.first + boundaries.second) / 2.0;
+ const auto &recallBoundary = SystemCore::Configuration::recallBoundaries;
double delta = ((gameplayColors[sequenceIndex] ==
Player::ControllerButton::Circle ||
gameplayColors[sequenceIndex] == Player::ControllerButton::Square)
- ? contextManager->config.recallBoundaries[2] - contextManager->config.recallBoundaries[1]
- : contextManager->config.recallBoundaries[1] - contextManager->config.recallBoundaries[0]) /
+ ? recallBoundary[2] - recallBoundary[1]
+ : recallBoundary[1] - recallBoundary[0]) /
5;
for (uint16_t i = boundaries.first; i <= boundaries.second; ++i)
@@ -104,7 +105,7 @@ namespace Games::Recall
}
}
- void Controller::pauseComputerPlayback()
+ void Driver::pauseComputerPlayback()
{
if (sequenceIndex >= state.round)
{
@@ -124,7 +125,7 @@ namespace Games::Recall
}
}
- void Controller::evaluateUserRecall()
+ void Driver::evaluateUserRecall()
{
if (sequenceIndex > state.round && isReady())
{
@@ -140,9 +141,9 @@ namespace Games::Recall
evaluateUserButton(gameplayColors[sequenceIndex]);
}
- void Controller::evaluateUserButton(Player::ControllerButton expectedButton)
+ void Driver::evaluateUserButton(Player::ControllerButton expectedButton)
{
- for (auto button : availableGameplayButtons)
+ for (auto button : Driver::availableGameplayButtons)
{
if (contextManager->controller.wasPressedAndReleased(button))
{
@@ -161,12 +162,12 @@ namespace Games::Recall
}
}
- void Controller::illuminateOnSelection()
+ void Driver::illuminateOnSelection()
{
static uint32_t lastLightTime = 0;
static int pressedButtonIndex = -1;
bool buttonPressed = false;
- for (uint16_t i = 0; i < arraySize(availableGameplayButtons); ++i)
+ for (uint16_t i = 0; i < arraySize(Driver::availableGameplayButtons); ++i)
{
auto btn = static_cast(i);
if (contextManager->controller.isDown(btn))
@@ -181,7 +182,7 @@ namespace Games::Recall
bool keepLit = ((millis() - lastLightTime) < gameplaySpeedPaused);
if (buttonPressed || keepLit)
{
- if (pressedButtonIndex >= 0 && pressedButtonIndex < static_cast(arraySize(availableGameplayButtons)))
+ if (pressedButtonIndex >= 0 && pressedButtonIndex < static_cast(arraySize(Driver::availableGameplayButtons)))
{
auto boundaries = directionBoundaries(static_cast(pressedButtonIndex));
for (uint16_t i = boundaries.first; i <= boundaries.second; ++i)
@@ -190,9 +191,9 @@ namespace Games::Recall
}
}
- std::pair Controller::directionBoundaries(Player::ControllerButton button)
+ std::pair Driver::directionBoundaries(Player::ControllerButton button)
{
- const auto &boundary = contextManager->config.recallBoundaries;
+ const auto &boundary = SystemCore::Configuration::recallBoundaries;
switch (button)
{
@@ -203,13 +204,13 @@ namespace Games::Recall
case Player::ControllerButton::Cross:
return {boundary[2], static_cast(boundary[3] - 1)};
case Player::ControllerButton::Square:
- return {boundary[3], static_cast(contextManager->leds.size() - 1)};
+ return {boundary[3], static_cast(SystemCore::Configuration::numLeds - 1)};
default:
return {0, 0};
}
}
- void Controller::prepareComputerPlayback()
+ void Driver::prepareComputerPlayback()
{
if (isReady())
{
@@ -235,7 +236,7 @@ namespace Games::Recall
successFadeawayAnimation = std::clamp(successFadeawayAnimation - 0.08, 0.0, 1.0);
}
- void Controller::gameOver()
+ void Driver::gameOver()
{
if (contextManager->controller.wasPressed(Player::ControllerButton::Start))
{
@@ -247,14 +248,14 @@ namespace Games::Recall
contextManager->stateManager.displayShouldUpdate = true;
}
- for (uint16_t i = 0; i <= contextManager->leds.size(); ++i)
+ for (uint16_t i = 0; i <= SystemCore::Configuration::numLeds; ++i)
{
- float phase = std::cos((2.0f * M_PI * i / contextManager->leds.size()) + (2.0f * M_PI * gameOverLedPhaseShift / contextManager->leds.size())) * 127 + 128;
+ float phase = std::cos((2.0f * M_PI * i / SystemCore::Configuration::numLeds) + (2.0f * M_PI * gameOverLedPhaseShift / SystemCore::Configuration::numLeds)) * 127 + 128;
contextManager->leds.buffer[i] = {static_cast(std::floor(phase)), 0, 0};
}
gameOverLedPhaseShift += 0.5f;
- if (gameOverLedPhaseShift > contextManager->leds.size())
- gameOverLedPhaseShift = 0;
+ if (gameOverLedPhaseShift > SystemCore::Configuration::numLeds)
+ gameOverLedPhaseShift = 0.0f;
}
}
\ No newline at end of file
diff --git a/src/lights/color-hsl.cpp b/src/lights/color-hsl.cpp
index 28bb52a..c0d44a2 100644
--- a/src/lights/color-hsl.cpp
+++ b/src/lights/color-hsl.cpp
@@ -1,5 +1,4 @@
#include
-#include
#include "lights/color-hsl.h"
namespace Lights
diff --git a/src/lights/led-luminance.cpp b/src/lights/led-luminance.cpp
index b2cc65f..f1854b0 100644
--- a/src/lights/led-luminance.cpp
+++ b/src/lights/led-luminance.cpp
@@ -6,7 +6,7 @@ namespace Lights
{
void LedLuminance::adjustLuminance()
{
- int dialReading = analogRead(config.ledDimmerGpio);
+ int dialReading = analogRead(SystemCore::Configuration::ledDimmerGpio);
dialReading = map(dialReading, 0, LedLuminance::MAX_ADC_READING, 0, LedLuminance::MAX_LED_BRIGHTNESS);
dialReading = constrain(dialReading, 0, LedLuminance::MAX_LED_BRIGHTNESS);
diff --git a/src/lights/led-strip.cpp b/src/lights/led-strip.cpp
index f07fd62..3a45cea 100644
--- a/src/lights/led-strip.cpp
+++ b/src/lights/led-strip.cpp
@@ -2,9 +2,7 @@
namespace Lights
{
- LedStrip::LedStrip(SystemCore::Configuration &configuration) : config{configuration},
- buffer{configuration.numLeds},
- luminance{configuration}
+ LedStrip::LedStrip()
{
#ifdef RELEASE
FastLED.addLeds(static_cast(buffer), size());
diff --git a/src/player/controller.cpp b/src/player/controller.cpp
index b83f709..70e767e 100644
--- a/src/player/controller.cpp
+++ b/src/player/controller.cpp
@@ -102,21 +102,6 @@ namespace Player
return instance->buttonLastState[idx];
}
- float Controller::analogToSpeed(int value, float maxOutput) const
- {
- if (value == 0)
- return 0.0f;
-
- float normalizedAnalogValue = static_cast(value) / 127.0f;
- float sign = (normalizedAnalogValue > 0.0f) ? 1.0f : -1.0f;
- float absoluteValueNormalized = std::abs(normalizedAnalogValue);
-
- float nonlinear = std::pow(absoluteValueNormalized, instance->responseExponent);
- float mixed = (1.0f - instance->responseBlend) * nonlinear + instance->responseBlend * absoluteValueNormalized;
- float scaled = mixed * maxOutput;
- return sign * scaled;
- }
-
void Controller::reset()
{
for (uint32_t i = 0; i < arraySize(buttonPressedEvent); ++i)
diff --git a/src/player/player.cpp b/src/player/player.cpp
index eab4260..00ccc01 100644
--- a/src/player/player.cpp
+++ b/src/player/player.cpp
@@ -2,20 +2,49 @@
namespace Player
{
- void Player::move(const int distance, const float speed)
+ void BasePlayer::move(const int distance, const float speed, const bool shouldWrap)
{
if (distance == 0)
return;
- float step = contextManager->controller.analogToSpeed(distance, speed);
+ float step = analogToSpeed(distance, speed);
positionPrecise += step;
- const float ledCountF = static_cast(contextManager->leds.size());
- positionPrecise = std::fmod(positionPrecise, ledCountF);
+ const float adjustedLedCount = static_cast(SystemCore::Configuration::numLeds - width);
+ if (shouldWrap)
+ {
+ positionPrecise = std::fmod(positionPrecise, adjustedLedCount);
- if (positionPrecise < 0.0f)
- positionPrecise += ledCountF;
+ if (positionPrecise < 0.0f)
+ {
+ positionPrecise += adjustedLedCount;
+ }
+ }
+ else
+ {
+ if (positionPrecise >= adjustedLedCount)
+ {
+ positionPrecise = adjustedLedCount;
+ }
+ else if (positionPrecise < 0.0f)
+ {
+ positionPrecise = 0.0f;
+ }
+ }
+ }
+
+ float BasePlayer::analogToSpeed(int value, float maxOutput) const
+ {
+ if (value == 0)
+ return 0.0f;
+
+ float normalizedAnalogValue = static_cast(value) / 127.0f;
+ float sign = (normalizedAnalogValue > 0.0f) ? 1.0f : -1.0f;
+ float absoluteValueNormalized = std::abs(normalizedAnalogValue);
- position = static_cast(std::floor(positionPrecise)) % contextManager->leds.size();
+ float nonlinear = std::pow(absoluteValueNormalized, responseExponent);
+ float mixed = (1.0f - responseBlend) * nonlinear + responseBlend * absoluteValueNormalized;
+ float scaled = mixed * maxOutput;
+ return sign * scaled;
}
}
\ No newline at end of file
diff --git a/src/scenes/canvas/controller.cpp b/src/scenes/canvas/driver.cpp
similarity index 88%
rename from src/scenes/canvas/controller.cpp
rename to src/scenes/canvas/driver.cpp
index cdce2a7..b96d55d 100644
--- a/src/scenes/canvas/controller.cpp
+++ b/src/scenes/canvas/driver.cpp
@@ -1,4 +1,4 @@
-#include "scenes/canvas/controller.h"
+#include "scenes/canvas/driver.h"
#include "esp_system.h"
#include "logger.h"
#include
@@ -6,12 +6,12 @@
namespace Scenes::Canvas
{
- Controller::Controller(SystemCore::ContextManager *ctx) : contextManager{ctx}
+ Driver::Driver(SystemCore::ContextManager *ctx) : contextManager{ctx}
{
reset();
}
- void Controller::nextEvent()
+ void Driver::nextEvent()
{
checkAnalogColorChange();
checkBalanceColorRequest();
@@ -19,13 +19,13 @@ namespace Scenes::Canvas
checkChangeOccured();
checkStableControllerForDisplay();
- for (uint16_t i; i < contextManager->leds.size(); ++i)
+ for (uint16_t i; i < SystemCore::Configuration::numLeds; ++i)
{
contextManager->leds.buffer[i] = contextManager->stateManager.getCanvasSceneState().currentColor;
}
}
- void Controller::checkAnalogColorChange()
+ void Driver::checkAnalogColorChange()
{
if (contextManager->controller.leftAnalog().x > 64)
{
@@ -59,7 +59,7 @@ namespace Scenes::Canvas
}
}
- void Controller::checkNewColorRequest()
+ void Driver::checkNewColorRequest()
{
if (contextManager->controller.wasPressed(Player::ControllerButton::Triangle))
{
@@ -68,7 +68,7 @@ namespace Scenes::Canvas
}
}
- void Controller::checkBalanceColorRequest()
+ void Driver::checkBalanceColorRequest()
{
if (contextManager->controller.wasPressed(Player::ControllerButton::L1))
{
@@ -91,7 +91,7 @@ namespace Scenes::Canvas
}
}
- void Controller::checkChangeOccured()
+ void Driver::checkChangeOccured()
{
if (hasChange)
{
@@ -102,7 +102,7 @@ namespace Scenes::Canvas
}
}
- void Controller::checkStableControllerForDisplay()
+ void Driver::checkStableControllerForDisplay()
{
bool isMoving =
abs(contextManager->controller.leftAnalog().x) > 64 ||
@@ -122,7 +122,7 @@ namespace Scenes::Canvas
}
}
- void Controller::reset()
+ void Driver::reset()
{
colorHsl.hue = static_cast(esp_random() % std::numeric_limits::max());
colorHsl.saturation = static_cast((esp_random() % 64u) + 191u);