diff --git a/src/ui/FaceRenderer.cpp b/src/ui/FaceRenderer.cpp index 9ab50fc..27ef97f 100644 --- a/src/ui/FaceRenderer.cpp +++ b/src/ui/FaceRenderer.cpp @@ -36,12 +36,18 @@ void FaceRenderer::loop(const MoistureSensor& moisture, const BatterySensor& bat updateMood(moisture.percent()); updateDeathMode(now); + updateBatteryLowMode(battery.percent()); if (_deadMode) { renderDead(now, battery); return; } + if (_batteryLowMode) { + renderBatteryLow(now, battery); + return; + } + if (_mood == HAPPY) updateHappy(now); else if (_mood == DRY) updateDry(now); else updateTooWet(now); @@ -76,6 +82,11 @@ void FaceRenderer::updateDeathMode(unsigned long now) { } } +void FaceRenderer::updateBatteryLowMode(int batteryPercent) { + // Enter nervous mode when battery is critically low (< 10%) + _batteryLowMode = (batteryPercent < 10); +} + void FaceRenderer::updateHappy(unsigned long now) { if ((long)(now - _nextGazeMs) >= 0) { _targetX = randRangeI8(-3, 3); @@ -151,6 +162,48 @@ void FaceRenderer::renderDead(unsigned long now, const BatterySensor& battery) { d.display(); } +void FaceRenderer::renderBatteryLow(unsigned long now, const BatterySensor& battery) { + auto &d = _display->oled(); + d.clearDisplay(); + + // Nervous animation: eyes glance up at battery icon repeatedly + // Battery icon is at (110, 2) in top-right corner + // Eyes are centered at (40, 24) for left, (88, 24) for right + + // Create a glancing pattern: look up at battery, then center, repeat + int glancePhase = (now / 800) % 3; // 3 phases: up, center, up + + int pupilX = 0, pupilY = 0; + + if (glancePhase == 0 || glancePhase == 2) { + // Look up toward battery icon (upper right) + pupilX = 3; // Look right + pupilY = -4; // Look up + } else { + // Brief center position between glances + pupilX = 0; + pupilY = -2; // Still slightly upward (worried/anxious) + } + + // Draw eyes with pupils positioned + if (_blinking) { + drawEyesClosed(); + } else { + drawEyesOpen(pupilX, pupilY); + } + + // Worried eyebrows + drawBrowsWorried(); + + // Nervous mouth (wavy/uncertain) + drawMouthNervous(); + + // Draw battery icon in top-right corner (blinking) + _display->drawBatteryIcon(110, 2, battery.percent(), true); + + d.display(); +} + void FaceRenderer::renderNormal(unsigned long now, const BatterySensor& battery) { auto &d = _display->oled(); d.clearDisplay(); @@ -212,6 +265,15 @@ void FaceRenderer::drawBrowsVerySad() { d.drawLine(80, 18, 102, 12, 1); } +void FaceRenderer::drawBrowsWorried() { + auto &d = _display->oled(); + // Raised/arched worried eyebrows + d.drawLine(26, 14, 36, 10, 1); + d.drawLine(36, 10, 48, 12, 1); + d.drawLine(80, 12, 92, 10, 1); + d.drawLine(92, 10, 102, 14, 1); +} + void FaceRenderer::drawMouthHappy() { auto &d = _display->oled(); for (int i = 0; i < 5; i++) @@ -229,6 +291,15 @@ void FaceRenderer::drawMouthFlat() { d.drawLine(52, 48, 76, 48, 1); } +void FaceRenderer::drawMouthNervous() { + auto &d = _display->oled(); + // Wavy/uncertain nervous mouth + d.drawLine(52, 48, 58, 47, 1); + d.drawLine(58, 47, 64, 48, 1); + d.drawLine(64, 48, 70, 47, 1); + d.drawLine(70, 47, 76, 48, 1); +} + void FaceRenderer::drawBubbles(int off) { auto &d = _display->oled(); d.drawCircle(10 + off, 12, 4, 1); diff --git a/src/ui/FaceRenderer.h b/src/ui/FaceRenderer.h index ae5aede..4a33304 100644 --- a/src/ui/FaceRenderer.h +++ b/src/ui/FaceRenderer.h @@ -22,6 +22,7 @@ private: Mood _mood = HAPPY; bool _deadMode = false; + bool _batteryLowMode = false; unsigned long _dryStartMs = 0; static constexpr unsigned long DRY_DEADLINE_MS = 72UL * 60UL * 60UL * 1000UL; unsigned long _nextDeadToggleMs = 0; @@ -52,20 +53,24 @@ private: void updateMood(int moisturePct); void updateDeathMode(unsigned long now); + void updateBatteryLowMode(int batteryPercent); void updateHappy(unsigned long now); void updateDry(unsigned long now); void updateTooWet(unsigned long now); void renderDead(unsigned long now, const BatterySensor& battery); + void renderBatteryLow(unsigned long now, const BatterySensor& battery); void renderNormal(unsigned long now, const BatterySensor& battery); void drawEyesOpen(int pupilDx, int pupilDy); void drawEyesClosed(); void drawEyesSmallPupils(int pupilDy, int sx, int sy); void drawBrowsVerySad(); + void drawBrowsWorried(); void drawMouthHappy(); void drawMouthFrown(); void drawMouthFlat(); + void drawMouthNervous(); void drawDroplet(int x, int y, bool pulse); void drawBubbles(int off); void drawSparkles(unsigned long now);