Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bouncing dvd app that will show a bouncing "HITCON" text #159

Merged
merged 8 commits into from
Aug 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions fw/Core/Hitcon/App/BouncingDVDApp.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include "BouncingDVDApp.h"

namespace hitcon {

namespace app {

namespace bouncing_dvd {

void BouncingDVD::update(int now) {
if (now - last_update_time < move_period) {
return;
}

last_update_time = now;

int new_x = x + dx;
int new_y = y + dy;
constexpr bool stop_on_border = false;
bool cross_border = false;
if (!(0 <= new_x && (new_x + FONT_WIDTH - 1) < DISPLAY_WIDTH)) {
dx = -dx;
new_x = x + dx;
new_y = y + dy;
cross_border = true;
}
if (!(0 <= new_y && (new_y + FONT_HEIGHT - 1) < DISPLAY_HEIGHT)) {
dy = -dy;
new_x = x + dx;
new_y = y + dy;
cross_border = true;
}
if (cross_border) {
current_char = (current_char + 1) % TEXT_LENGTH;
}

if (!cross_border || !stop_on_border) {
x = new_x;
y = new_y;
}

// If the character is at the corner, drift it by 1 pixel randomly so
// the audience won't be satisfied (:woozy_face:)
if (x == 0 && y == 0) {
if (__rand() % 2 == 0)
x += __rand() % 5 == 0 ? 1 : 0;
else
y += __rand() % 5 == 0 ? 1 : 0;
} else if (x == 0 && y == DISPLAY_HEIGHT - FONT_HEIGHT) {
if (__rand() % 2 == 0)
x += __rand() % 5 == 0 ? 1 : 0;
else
y -= __rand() % 5 == 0 ? 1 : 0;
} else if (x == DISPLAY_WIDTH - FONT_WIDTH && y == 0) {
if (__rand() % 2 == 0)
x -= __rand() % 5 == 0 ? 1 : 0;
else
y += __rand() % 5 == 0 ? 1 : 0;
} else if (x == DISPLAY_WIDTH - FONT_WIDTH &&
y == DISPLAY_HEIGHT - FONT_HEIGHT) {
if (__rand() % 2 == 0)
x -= __rand() % 5 == 0 ? 1 : 0;
else
y -= __rand() % 5 == 0 ? 1 : 0;
}
}

void BouncingDVD::draw(display_buf_t *buf) {
memset(buf, 0, sizeof(display_buf_t) * DISPLAY_WIDTH);

for (int i = 0; i < FONT_HEIGHT; i++) {
for (int j = 0; j < FONT_WIDTH; j++) {
display_buf_assign(buf[x + j], y + i, FONT[current_char][i][j]);
}
}
}

#ifndef HITCON_TEST_MODE

BouncingDVDApp bouncing_dvd_app;

using hitcon::service::sched::task_callback_t;

unsigned bouncing_dvd_rand() { return g_fast_random_pool.GetRandom(); }

BouncingDVDApp::BouncingDVDApp()
: periodic_task(UPDATE_PRIORITY,
(task_callback_t)&BouncingDVDApp::periodic_task_callback,
this, UPDATE_INTERVAL),
bouncing_dvd(bouncing_dvd_rand) {
hitcon::service::sched::scheduler.Queue(&periodic_task, nullptr);
}

void BouncingDVDApp::OnEntry() {
display_set_orientation(0);
hitcon::service::sched::scheduler.EnablePeriodic(&periodic_task);
}

void BouncingDVDApp::OnExit() {
display_set_orientation(1);
hitcon::service::sched::scheduler.DisablePeriodic(&periodic_task);
}

void BouncingDVDApp::OnButton(button_t button) {
switch (button) {
case BUTTON_UP:
bouncing_dvd.inc_move_speed();
break;

case BUTTON_DOWN:
bouncing_dvd.dec_move_speed();
break;

case BUTTON_BACK:
case BUTTON_LONG_BACK:
badge_controller.BackToMenu(this);
break;

default:
break;
}
}

void BouncingDVDApp::periodic_task_callback(void *) {
using hitcon::service::sched::SysTimer;

int now = static_cast<int>(SysTimer::GetTime());
bouncing_dvd.update(now);

display_buf_t display_buf[DISPLAY_WIDTH];
bouncing_dvd.draw(display_buf);
display_set_mode_fixed_packed(display_buf);
}

#endif

} // namespace bouncing_dvd

} // namespace app

} // namespace hitcon
163 changes: 163 additions & 0 deletions fw/Core/Hitcon/App/BouncingDVDApp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#ifndef BOUNCING_DVD_APP_H
#define BOUNCING_DVD_APP_H

#include <Logic/Display/display.h>

#ifndef HITCON_TEST_MODE
#include <Logic/BadgeController.h>
#include <Logic/RandomPool.h>
#include <Service/Sched/Scheduler.h>
#include <Service/Sched/SysTimer.h>
#include <Service/Sched/Task.h>

#include "app.h"
#endif

namespace hitcon {

namespace app {

namespace bouncing_dvd {

#define HITCON_BOUNCING_SMALL

#ifdef HITCON_BOUNCING_SMALL
/**
* X.X XXX XXX XXX XXX XXX
* H XXX I .X. T .X. C X.. O X.X N X.X
* X.X XXX .X. XXX XXX X.X
*/

constexpr int FONT_WIDTH = 3;
constexpr int FONT_HEIGHT = 3;
constexpr int FONT[][FONT_HEIGHT][FONT_WIDTH] = {
{{1, 0, 1}, {1, 1, 1}, {1, 0, 1}}, // H
{{1, 1, 1}, {0, 1, 0}, {1, 1, 1}}, // I
{{1, 1, 1}, {0, 1, 0}, {0, 1, 0}}, // T
{{1, 1, 1}, {1, 0, 0}, {1, 1, 1}}, // C
{{1, 1, 1}, {1, 0, 1}, {1, 1, 1}}, // O
{{1, 1, 1}, {1, 0, 1}, {1, 0, 1}}, // N
};

#else // HITCON_BOUNCING_BIG

/**
* X...X XXXXX XXXXX XXXXX XXXXX X...X
* X...X ..X.. ..X.. X.... X...X XX..X
* H XXXXX I ..X.. T ..X.. C X.... O X...X N X.X.X
* X...X ..X.. ..X.. X.... X...X X..XX
* X...X XXXXX ..X.. XXXXX XXXXX X...X
*/
constexpr int FONT_WIDTH = 5;
constexpr int FONT_HEIGHT = 5;
constexpr int FONT[][FONT_HEIGHT][FONT_WIDTH] = {
{{1, 0, 0, 0, 1},
{1, 0, 0, 0, 1},
{1, 1, 1, 1, 1},
{1, 0, 0, 0, 1},
{1, 0, 0, 0, 1}}, // H
{{1, 1, 1, 1, 1},
{0, 0, 1, 0, 0},
{0, 0, 1, 0, 0},
{0, 0, 1, 0, 0},
{1, 1, 1, 1, 1}}, // I
{{1, 1, 1, 1, 1},
{0, 0, 1, 0, 0},
{0, 0, 1, 0, 0},
{0, 0, 1, 0, 0},
{0, 0, 1, 0, 0}}, // T
{{1, 1, 1, 1, 1},
{1, 0, 0, 0, 0},
{1, 0, 0, 0, 0},
{1, 0, 0, 0, 0},
{1, 1, 1, 1, 1}}, // C
{{1, 1, 1, 1, 1},
{1, 0, 0, 0, 1},
{1, 0, 0, 0, 1},
{1, 0, 0, 0, 1},
{1, 1, 1, 1, 1}}, // O
{{1, 0, 0, 0, 1},
{1, 1, 0, 0, 1},
{1, 0, 1, 0, 1},
{1, 0, 0, 1, 1},
{1, 0, 0, 0, 1}}, // N
};

#endif // HITCON_BOUNCING_SMALL

constexpr int TEXT_LENGTH =
sizeof(FONT) / (FONT_WIDTH * FONT_HEIGHT) / sizeof(int);
constexpr int UPDATE_PRIORITY = 960;
constexpr int UPDATE_INTERVAL = 50; // ms
constexpr int DEFAULT_MOVE_PERIOD = 500; // ms
inline void inc_move_speed(int &move_speed) {
move_speed = move_speed * 9 / 10;
if (move_speed < UPDATE_INTERVAL) {
move_speed = UPDATE_INTERVAL;
}
}
inline void dec_move_speed(int &move_speed) {
move_speed = move_speed * 10 / 9;
if (move_speed > 2000) {
move_speed = 2000;
}
}

class BouncingDVD {
private:
int x = DISPLAY_WIDTH / 2 - FONT_WIDTH / 2;
int y = DISPLAY_HEIGHT / 2 - FONT_HEIGHT / 2;
int dx = 1;
int dy = 1;
int move_period = DEFAULT_MOVE_PERIOD;
int last_update_time = 0;
int current_char = 0;

unsigned (*__rand)(void) = nullptr;

public:
BouncingDVD() = default;
BouncingDVD(unsigned (*rand)(void)) : __rand(rand) {}

void update(int now);
void draw(display_buf_t *buf);
inline void inc_move_speed() {
::hitcon::app::bouncing_dvd::inc_move_speed(move_period);
}
inline void dec_move_speed() {
::hitcon::app::bouncing_dvd::dec_move_speed(move_period);
}
};

#ifndef HITCON_TEST_MODE

/**
* The app that displays a bouncing "HITCON" text.
*/
class BouncingDVDApp : public App {
private:
BouncingDVD bouncing_dvd;
hitcon::service::sched::PeriodicTask periodic_task;

public:
BouncingDVDApp();
virtual ~BouncingDVDApp() = default;

void OnEntry() override;
void OnExit() override;
void OnButton(button_t button) override;

void periodic_task_callback(void *);
};

extern BouncingDVDApp bouncing_dvd_app;

#endif // HITCON_TEST_MODE

} // namespace bouncing_dvd

} // namespace app

} // namespace hitcon

#endif // BOUNCING_DVD_APP_H
3 changes: 3 additions & 0 deletions fw/Core/Hitcon/App/MainMenuApp.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <App/BadUsbApp.h>
#include <App/BouncingDVDApp.h>
#include <App/DinoApp.h>
#include <App/ScoreHistApp.h>
#include <App/ShowNameApp.h>
Expand All @@ -10,6 +11,7 @@

namespace hitcon {

using hitcon::app::bouncing_dvd::bouncing_dvd_app;
using hitcon::app::dino::dino_app;
using hitcon::app::snake::snake_app;
using hitcon::app::tetris::tetris_app;
Expand All @@ -21,6 +23,7 @@ constexpr menu_entry_t main_menu_entries[] = {
{"Dino", &dino_app, nullptr},
{"Tetris", &tetris_app, &hitcon::app::tetris::SetSingleplayer},
{"Show Scores", &score_hist::g_score_hist, nullptr},
{"Bouncing DVD", &bouncing_dvd_app, nullptr},
};

constexpr int main_menu_entries_len =
Expand Down
6 changes: 5 additions & 1 deletion fw/Core/Hitcon/App/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
/tmp/test-tetris: *.cc *.h
g++ -Wall -Wextra -pedantic -g -O0 -DHITCON_TEST_MODE -o /tmp/test-tetris -I.. test-tetris.cc TetrisGame.cc

/tmp/test-bouncing: *.cc *.h
g++ -Wall -Wextra -pedantic -g -O0 -DHITCON_TEST_MODE -o /tmp/test-bouncing -I.. test-bouncing.cc BouncingDVDApp.cc

format:
clang-format -i *.cc *.h

test: /tmp/test-tetris
test: /tmp/test-tetris /tmp/test-bouncing
/tmp/test-tetris
/tmp/test-bouncing
1 change: 1 addition & 0 deletions fw/Core/Hitcon/App/ShowNameApp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ void ShowNameApp::check_update() {
SysTimer::GetTime() - last_disp_update > SURPRISE_TIME) {
mode = NameScore;
update_display();
badge_controller.RestoreApp();
} else if (mode != Surprise &&
(SysTimer::GetTime() - last_disp_update > kMinUpdateInterval ||
starting_up)) {
Expand Down
Loading
Loading