From 39927248a973152cdf570fd7c8a17d3456687e02 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Tue, 15 Oct 2024 14:19:32 +0200 Subject: [PATCH] refine drawing game --- src/app/Application.h | 6 +- src/app/DrawingGame.h | 210 ++++++++++++++++++++---------------------- src/app/Game.h | 3 + src/app/Timer.h | 38 ++++++++ src/ws/Connection.cpp | 4 +- 5 files changed, 148 insertions(+), 113 deletions(-) create mode 100644 src/app/Timer.h diff --git a/src/app/Application.h b/src/app/Application.h index 24b68c5..f17ee82 100644 --- a/src/app/Application.h +++ b/src/app/Application.h @@ -130,9 +130,9 @@ private: } void HandleTwitch(const twitch::IRCMessage &msg) { - std::string text = msg.GetText(); - if (text.length() < 2 || text[0] != '$') return; - drawing_game.QueueCommands(text); + if (state.HasGame()) { + state.GetGame().Handle(msg); + } } private: diff --git a/src/app/DrawingGame.h b/src/app/DrawingGame.h index f42499a..080ee61 100644 --- a/src/app/DrawingGame.h +++ b/src/app/DrawingGame.h @@ -4,7 +4,9 @@ #include #include +#include "Clock.h" #include "Game.h" +#include "Timer.h" #include "../gfx/ColorRGB.h" #include "../pango/Layout.h" @@ -18,17 +20,28 @@ public: , w(w), h(h), x(0), y(0) , step_size(1) , color_intensity(9) + , brush{ 0, 0, 0 } , pos{ 760, 100 } , font_color{ 1, 1, 1 } , cursor_font("DejaVu Sans 16px") , cursor_layout(ctx.CreateLayout()) - , text_dirty(true) { + , text_dirty(true) + , draw_timer(150) { + cursor_layout.SetFont(cursor_font); cells.resize(w * h, base_color); } public: + virtual void Handle(const twitch::IRCMessage &msg) { + std::string text = msg.GetText(); + if (!text.empty()) { + QueueCommands(text); + } + } + virtual void Update(cairo::Context &ctx, const Clock &now) { - if (!command_queue.empty()) { + int amount = draw_timer.ActivationsBetween(last_update, now); + while (amount > 0 && !command_queue.empty()) { const char c = command_queue.back(); command_queue.pop_back(); if (!command_queue.empty()) { @@ -36,10 +49,12 @@ public: } else { InterpretCharacter(c); } + --amount; } if (text_dirty) { UpdateText(ctx); } + last_update = now; } virtual void Render(cairo::Context &ctx) const { @@ -48,6 +63,16 @@ public: ctx.DrawLayout(cursor_layout); gfx::Rectangle r{ 0, 0, 10, 10 }; + r.x = pos.x + (w - 1) * r.w; + r.y = pos.y + 30 - 2 * r.h; + ctx.SetSourceColor(brush); + ctx.Rectangle(r); + ctx.Fill(); + ctx.SetSourceColor({ 0.5, 0.5, 0.5 }); + ctx.SetLineWidth(1); + ctx.Rectangle(r); + ctx.Stroke(); + for (int cy = 0; cy < h; ++cy) { for (int cx = 0; cx < w; ++cx) { r.x = pos.x + cx * r.w; @@ -89,95 +114,90 @@ public: case '5': case '6': case '7': case '8': case '9': if (IsMovementCharacter(next)) { SetStepSize(c - '0'); - } else { + } else if (IsColorCharacter(next)) { SetColorIntensity(c - '0'); } break; - case 'u': case 'U': - case 'n': case 'N': + case 'w': case 'W': + case 'i': case 'I': MoveUp(); break; - case 'd': case 'D': case 's': case 'S': + case 'k': case 'K': MoveDown(); break; - case 'l': case 'L': - case 'w': case 'W': + case 'a': case 'A': + case 'j': case 'J': MoveLeft(); break; - case 'r': case 'R': - case 'e': case 'E': + case 'd': case 'D': + case 'l': case 'L': MoveRight(); break; - case 'a': case 'A': + case 'r': case 'R': SetRed(); break; - case 'b': case 'B': + case 'g': case 'G': SetGreen(); break; - case 'c': case 'C': + case 'b': case 'B': SetBlue(); break; - case 'f': case 'F': - ClearRed(); - break; - case 'g': case 'G': - ClearGreen(); - break; - case 'h': case 'H': - ClearBlue(); - break; - case 'i': case 'I': - SetYellow(); - break; - case 'j': case 'J': + case 'c': case 'C': SetCyan(); break; - case 'k': case 'K': - SetMagenta(); - break; case 'm': case 'M': - ClearYellow(); - break; - case 'o': case 'O': - ClearCyan(); + SetMagenta(); break; - case 'p': case 'P': - ClearMagenta(); + case 'y': case 'Y': + SetYellow(); break; case 'q': case 'Q': - InvertRed(); + Pick(); break; + case 'e': case 'E': + case 'f': case 'F': + case 'h': case 'H': + case 'n': case 'N': + case 'o': case 'O': + case 'p': case 'P': case 't': case 'T': - InvertGreen(); - break; + case 'u': case 'U': case 'v': case 'V': - InvertBlue(); + Paste(); break; case 'x': case 'X': - InvertColor(); - break; - case 'y': case 'Y': - SetColor({ 1, 1, 1 }); - break; case 'z': case 'Z': - SetColor({ 0, 0, 0 }); + InvertColor(); break; default: break; } } + bool IsColorCharacter(char c) const { + switch (c) { + case 'r': case 'R': + case 'g': case 'G': + case 'b': case 'B': + case 'c': case 'C': + case 'm': case 'M': + case 'y': case 'Y': + return true; + } + return false; + } + bool IsMovementCharacter(char c) const { switch (c) { - case 'u': case 'U': - case 'n': case 'N': - case 'd': case 'D': + case 'w': case 'W': + case 'a': case 'A': case 's': case 'S': + case 'd': case 'D': + case 'i': case 'I': + case 'j': case 'J': + case 'k': case 'K': case 'l': case 'L': - case 'w': case 'W': - case 'r': case 'R': - case 'e': case 'E': return true; } return false; @@ -217,96 +237,66 @@ public: } void SetRed() { - cells[y * w + x].r = double(color_intensity) / 9.0; + brush.r = double(color_intensity) / 9.0; + SetColorIntensity(9); } void SetGreen() { - cells[y * w + x].g = double(color_intensity) / 9.0; + brush.g = double(color_intensity) / 9.0; + SetColorIntensity(9); } void SetBlue() { - cells[y * w + x].b = double(color_intensity) / 9.0; - } - - void ClearRed() { - cells[y * w + x].r = 0; - } - - void ClearGreen() { - cells[y * w + x].g = 0; - } - - void ClearBlue() { - cells[y * w + x].b = 0; - } - - void SetYellow() { - cells[y * w + x].r = double(color_intensity) / 9.0; - cells[y * w + x].g = double(color_intensity) / 9.0; + brush.b = double(color_intensity) / 9.0; + SetColorIntensity(9); } void SetCyan() { - cells[y * w + x].g = double(color_intensity) / 9.0; - cells[y * w + x].b = double(color_intensity) / 9.0; + brush.r = 1.0 - double(color_intensity) / 9.0; + SetColorIntensity(9); } void SetMagenta() { - cells[y * w + x].r = double(color_intensity) / 9.0; - cells[y * w + x].b = double(color_intensity) / 9.0; - } - - void ClearYellow() { - cells[y * w + x].r = 0; - cells[y * w + x].g = 0; - } - - void ClearCyan() { - cells[y * w + x].g = 0; - cells[y * w + x].b = 0; + brush.g = 1.0 - double(color_intensity) / 9.0; + SetColorIntensity(9); } - void ClearMagenta() { - cells[y * w + x].r = 0; - cells[y * w + x].b = 0; - } - - void InvertRed() { - cells[y * w + x].r = 1.0 - cells[y * w + x].r; + void SetYellow() { + brush.b = 1.0 - double(color_intensity) / 9.0; + SetColorIntensity(9); } - void InvertGreen() { - cells[y * w + x].g = 1.0 - cells[y * w + x].g; + void Pick() { + brush = CurrentCell(); } - void InvertBlue() { - cells[y * w + x].b = 1.0 - cells[y * w + x].b; + void Paste() { + CurrentCell() = brush; } void InvertColor() { - cells[y * w + x].r = 1.0 - cells[y * w + x].r; - cells[y * w + x].g = 1.0 - cells[y * w + x].g; - cells[y * w + x].b = 1.0 - cells[y * w + x].b; + brush.r = 1.0 - brush.r; + brush.g = 1.0 - brush.g; + brush.b = 1.0 - brush.b; } void SetColorIntensity(int m) { - int new_intensity = std::max(0, std::min(9, m)); - text_dirty = new_intensity != color_intensity; - color_intensity = new_intensity; + color_intensity = std::max(0, std::min(9, m)); } void SetStepSize(int m) { - int new_size = std::max(0, std::min(9, m)); - text_dirty = new_size != step_size; - step_size = new_size; + step_size = std::max(0, std::min(9, m)); + } + + gfx::ColorRGB &CurrentCell() { + return cells[y * w + x]; } private: void UpdateText(cairo::Context &ctx) { if (!text_dirty) return; std::stringstream out; - out << "Cursor: " << x << ", " << y; - out << " Schrittgröße: " << step_size; - out << " Deckkraft: " << color_intensity; + out << "Cursor: " << (x + 1) << ", " << (y + 1); cursor_layout.SetText(out.str()); ctx.UpdateLayout(cursor_layout); text_dirty = false; @@ -317,6 +307,7 @@ private: int w, h, x, y; int step_size; int color_intensity; + gfx::ColorRGB brush; std::string command_queue; @@ -327,6 +318,9 @@ private: pango::Layout cursor_layout; bool text_dirty; + Clock last_update; + Timer draw_timer; + }; } diff --git a/src/app/Game.h b/src/app/Game.h index eabc3ce..c8512e1 100644 --- a/src/app/Game.h +++ b/src/app/Game.h @@ -3,6 +3,7 @@ #include "Clock.h" #include "../cairo/Context.h" +#include "../twitch/IRCMessage.h" namespace app { @@ -13,6 +14,8 @@ public: } public: + virtual void Handle(const twitch::IRCMessage &) = 0; + virtual void Update(cairo::Context &, const Clock &) = 0; virtual void Render(cairo::Context &) const = 0; diff --git a/src/app/Timer.h b/src/app/Timer.h new file mode 100644 index 0000000..0944358 --- /dev/null +++ b/src/app/Timer.h @@ -0,0 +1,38 @@ +#ifndef TEST_APP_TIMER_H_ +#define TEST_APP_TIMER_H_ + +#include "Clock.h" + +namespace app { + +class Timer { + +public: + explicit Timer(int64_t interval) + : start(), interval(interval) { + } + Timer(const Clock &start, int64_t interval) + : start(start), interval(interval) { + } + ~Timer() { + } + +public: + int ActivationsAt(const Clock &now) const { + const Clock diff(now.Difference(start)); + return diff.GetMS() / interval; + } + + int ActivationsBetween(const Clock &from, const Clock &till) const { + return ActivationsAt(till) - ActivationsAt(from); + } + +private: + Clock start; + int64_t interval; + +}; + +} + +#endif diff --git a/src/ws/Connection.cpp b/src/ws/Connection.cpp index 2109aff..82e9c02 100644 --- a/src/ws/Connection.cpp +++ b/src/ws/Connection.cpp @@ -91,7 +91,7 @@ int HttpsConnection::ProtoCallback(lws_callback_reasons reason, void *in, size_t break; case LWS_CALLBACK_WSI_CREATE: case LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION: - case LWS_CALLBACK_HTTP_DROP_PROTOCOL: + case LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL: case LWS_CALLBACK_CLOSED_CLIENT_HTTP: break; default: @@ -217,7 +217,7 @@ int TwitchConnection::ProtoCallback(lws_callback_reasons reason, void *in, size_ break; case LWS_CALLBACK_CLIENT_CLOSED: connected = false; - std::cout << "twitch connection closed :/" << std::endl; + std::cout << "twitch connection closed" << std::endl; break; case LWS_CALLBACK_CLIENT_RECEIVE: if (lws_is_first_fragment(wsi)) { -- 2.39.2