Add Blackjack

This commit is contained in:
Haseo
2022-10-13 21:17:26 +02:00
parent 431a5b1a12
commit 607e46e757
10 changed files with 954 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
App(
appid="blackjack",
name="Blackjack",
apptype=FlipperAppType.EXTERNAL,
entry_point="blackjack_app",
cdefines=["APP_BLACKJACK"],
requires=["gui"],
stack_size=1 * 1024,
order=30,
fap_icon="blackjack_10px.png",
fap_category="Games",
)

View File

@@ -0,0 +1,372 @@
#include <gui/gui.h>
#include <stdlib.h>
#include <dolphin/dolphin.h>
#include <math.h>
#include "defines.h"
#include "card.h"
#include "util.h"
#include "ui.h"
#define APP_NAME "Blackjack"
#define STARTING_MONEY 200
#define DEALER_MAX 17
void start_round(GameState *game_state);
static void draw_ui(Canvas *const canvas, const GameState *game_state) {
draw_money(canvas, game_state->player_score);
draw_score(canvas, true, handCount(game_state->player_cards, game_state->player_card_count));
if (!game_state->animating && game_state->state == GameStatePlay) {
draw_play_menu(canvas, game_state);
}
}
static void render_callback(Canvas *const canvas, void *ctx) {
const GameState *game_state = acquire_mutex((ValueMutex *) ctx, 25);
if (game_state == NULL) {
return;
}
canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, 0, 0, 128, 64);
switch (game_state->state) {
case GameStateStart:
case GameStateGameOver:
draw_message_scene(canvas, game_state);
break;
case GameStatePlay:
draw_player_scene(canvas, game_state);
break;
case GameStateDealer:
draw_dealer_scene(canvas, game_state);
break;
}
if (game_state->state != GameStateStart && game_state->state != GameStateGameOver) {
animateQueue(game_state, canvas);
draw_ui(canvas, game_state);
}
release_mutex((ValueMutex *) ctx, game_state);
}
//region card draw
Card draw_card(GameState *game_state) {
Card c = game_state->deck.cards[game_state->deck.index];
game_state->deck.index++;
return c;
}
char *letters[4] = {"spade", "hearth", "diamond", "club"};
void drawPlayerCard(GameState *game_state) {
Card c = draw_card(game_state);
game_state->player_cards[game_state->player_card_count] = c;
game_state->player_card_count++;
}
void drawDealerCard(GameState *game_state) {
Card c = draw_card(game_state);
game_state->dealer_cards[game_state->dealer_card_count] = c;
game_state->dealer_card_count++;
FURI_LOG_D(APP_NAME, "drawing dealer %s %i", letters[c.pip], c.character + 2);
}
//endregion
//region queue callbacks
void to_lose_state(const GameState *game_state, Canvas *const canvas) {
UNUSED(game_state);
popupFrame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "You lost");
}
void to_bust_state(const GameState *game_state, Canvas *const canvas) {
UNUSED(game_state);
popupFrame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Busted!");
}
void to_draw_state(const GameState *game_state, Canvas *const canvas) {
UNUSED(game_state);
popupFrame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Draw");
}
void to_dealer_turn(const GameState *game_state, Canvas *const canvas) {
UNUSED(game_state);
popupFrame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Dealers turn");
}
void to_win_state(const GameState *game_state, Canvas *const canvas) {
UNUSED(game_state);
popupFrame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "You win");
}
void to_start(const GameState *game_state, Canvas *const canvas) {
UNUSED(game_state);
popupFrame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Round started");
}
void before_start(GameState *gameState) {
gameState->dealer_card_count = 0;
gameState->player_card_count = 0;
}
void start(GameState *game_state) {
start_round(game_state);
}
void draw(GameState *game_state) {
game_state->player_score += game_state->bet;
game_state->bet = 0;
queue(game_state, start, before_start, to_start);
}
void game_over(GameState *game_state) {
game_state->state = GameStateGameOver;
}
void lose(GameState *game_state) {
game_state->state = GameStatePlay;
game_state->bet = 0;
if (game_state->player_score >= ROUND_PRICE)
queue(game_state, start, before_start, to_start);
else
queue(game_state, game_over, NULL, NULL);
}
void win(GameState *game_state) {
game_state->state = GameStatePlay;
game_state->player_score += game_state->bet * 2;
game_state->bet = 0;
queue(game_state, start, before_start, to_start);
}
void dealerTurn(GameState *game_state) {
game_state->state = GameStateDealer;
}
//endregion
void player_tick(GameState *game_state) {
uint8_t score = handCount(game_state->player_cards, game_state->player_card_count);
if ((game_state->doubled && score <= 21) || score == 21) {
queue(game_state, dealerTurn, NULL, NULL);
} else if (score > 21) {
queue(game_state, lose, NULL, to_bust_state);
} else {
if (game_state->selectDirection == DirectionUp && game_state->selectedMenu > 0) {
game_state->selectedMenu--;
}
if (game_state->selectDirection == DirectionDown && game_state->selectedMenu < 2) {
game_state->selectedMenu++;
}
if (game_state->selectDirection == Select) {
//double
if (!game_state->doubled && game_state->selectedMenu == 0 &&
game_state->player_score >= ROUND_PRICE) {
game_state->player_score -= ROUND_PRICE;
game_state->bet += ROUND_PRICE;
game_state->doubled = true;
game_state->selectedMenu = 1;
queue(game_state, drawPlayerCard, NULL, draw_card_animation);
game_state->player_cards[game_state->player_card_count] = game_state->deck.cards[game_state->deck.index];
score = handCount(game_state->player_cards, game_state->player_card_count + 1);
if (score > 21)
queue(game_state, lose, NULL, to_bust_state);
else
queue(game_state, dealerTurn, NULL, to_dealer_turn);
} //hit
else if (game_state->selectedMenu == 1) {
queue(game_state, drawPlayerCard, NULL, draw_card_animation);
} //stay
else if (game_state->selectedMenu == 2) {
queue(game_state, dealerTurn, NULL, to_dealer_turn);
}
}
}
}
void dealer_tick(GameState *game_state) {
uint8_t dealer_score = handCount(game_state->dealer_cards, game_state->dealer_card_count);
uint8_t player_score = handCount(game_state->player_cards, game_state->player_card_count);
if (dealer_score >= DEALER_MAX) {
if (dealer_score > 21 || dealer_score < player_score)
queue(game_state, win, NULL, to_win_state);
else if (dealer_score > player_score)
queue(game_state, lose, NULL, to_lose_state);
else if (dealer_score == player_score)
queue(game_state, draw, NULL, to_draw_state);
} else {
queue(game_state, drawDealerCard, NULL, draw_card_animation);
}
}
void tick(GameState *game_state) {
game_state->last_tick = furi_get_tick();
if (!game_state->started && game_state->state == GameStatePlay) {
game_state->started = true;
drawDealerCard(game_state);
queue(game_state, drawPlayerCard, NULL, draw_card_animation);
queue(game_state, drawDealerCard, NULL, draw_card_animation);
queue(game_state, drawPlayerCard, NULL, draw_card_animation);
}
if (!run_queue(game_state)) {
if (game_state->state == GameStatePlay) {
player_tick(game_state);
} else if (game_state->state == GameStateDealer) {
dealer_tick(game_state);
}
}
game_state->selectDirection = None;
}
void start_round(GameState *game_state) {
game_state->player_card_count = 0;
game_state->dealer_card_count = 0;
game_state->selectedMenu = 0;
game_state->started = false;
game_state->doubled = false;
game_state->animating = true;
game_state->animationStart = 0;
shuffleDeck(&(game_state->deck));
game_state->doubled = false;
game_state->bet = ROUND_PRICE;
if (game_state->player_score < ROUND_PRICE) {
game_state->state = GameStateGameOver;
} else {
game_state->player_score -= ROUND_PRICE;
}
game_state->state = GameStatePlay;
}
void init(GameState *game_state) {
game_state->last_tick = 0;
game_state->player_score = STARTING_MONEY;
generateDeck(&(game_state->deck));
start_round(game_state);
}
static void input_callback(InputEvent *input_event, FuriMessageQueue *event_queue) {
furi_assert(event_queue);
AppEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void update_timer_callback(FuriMessageQueue *event_queue) {
furi_assert(event_queue);
AppEvent event = {.type = EventTypeTick};
furi_message_queue_put(event_queue, &event, 0);
}
int32_t blackjack_app(void *p) {
UNUSED(p);
int32_t return_code = 0;
FuriMessageQueue *event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
GameState *game_state = malloc(sizeof(GameState));
init(game_state);
game_state->state = GameStateStart;
ValueMutex state_mutex;
if (!init_mutex(&state_mutex, game_state, sizeof(GameState))) {
FURI_LOG_E(APP_NAME, "cannot create mutex\r\n");
return_code = 255;
goto free_and_exit;
}
ViewPort *view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, &state_mutex);
view_port_input_callback_set(view_port, input_callback, event_queue);
FuriTimer *timer =
furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25);
Gui *gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
AppEvent event;
for (bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
GameState *game_state = (GameState *) acquire_mutex_block(&state_mutex);
if (event_status == FuriStatusOk) {
if (event.type == EventTypeKey) {
if (event.input.type == InputTypePress) {
switch (event.input.key) {
case InputKeyUp:
game_state->selectDirection = DirectionUp;
break;
case InputKeyDown:
game_state->selectDirection = DirectionDown;
break;
case InputKeyRight:
game_state->selectDirection = DirectionRight;
break;
case InputKeyLeft:
game_state->selectDirection = DirectionLeft;
break;
case InputKeyBack:
processing = false;
break;
case InputKeyOk:
if (game_state->state == GameStateGameOver || game_state->state == GameStateStart) {
init(game_state);
} else {
game_state->selectDirection = Select;
}
break;
}
}
} else if (event.type == EventTypeTick) {
tick(game_state);
}
} else {
FURI_LOG_D(APP_NAME, "osMessageQueue: event timeout");
// event timeout
}
view_port_update(view_port);
release_mutex(&state_mutex, game_state);
}
furi_timer_free(timer);
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
view_port_free(view_port);
delete_mutex(&state_mutex);
free_and_exit:
queue_clear();
free(game_state);
furi_message_queue_free(event_queue);
return return_code;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

View File

@@ -0,0 +1,239 @@
#include "card.h"
#include <math.h>
//region CardDesign
bool pips[4][49] =
{
{
//spades
0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1,
1, 1, 0, 1, 0, 1, 1,
0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0
},
{
//hearts
0, 1, 0, 0, 0, 1, 0,
1, 1, 1, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 0,
0, 0, 1, 1, 1, 0, 0,
0, 0, 0, 1, 0, 0, 0,
},
{
//diamonds
0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 0,
0, 0, 1, 1, 1, 0, 0,
0, 0, 0, 1, 0, 0, 0
},
{
//clubs
0, 0, 1, 1, 1, 0, 0,
0, 0, 1, 1, 1, 0, 0,
1, 1, 0, 1, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1,
1, 1, 0, 1, 0, 1, 1,
0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0
}
};
bool backDesign[4] = {
0, 1,
1, 0
};
//endregion
uint8_t characters[13] =
{
2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'A'
};
//region Player card positions
uint8_t playerCardPositions[22][4] = {
//first row
{108, 38, 0, 0},
{98, 38, 0, 1},
{88, 38, 0, 1},
{78, 38, 0, 1},
{68, 38, 0, 1},
{58, 38, 0, 1},
{48, 38, 0, 1},
{38, 38, 0, 1},
//second row
{104, 26, 1, 0},
{94, 26, 1, 1},
{84, 26, 1, 1},
{74, 26, 1, 1},
{64, 26, 1, 1},
{54, 26, 1, 1},
{44, 26, 1, 1},
//third row
{99, 14, 1, 0},
{89, 14, 1, 1},
{79, 14, 1, 1},
{69, 14, 1, 1},
{59, 14, 1, 1},
{49, 14, 1, 1},
};
//endregion
void drawPlayerDeck(const Card cards[21], uint8_t count, Canvas *const canvas) {
for (uint8_t i = 0; i < count; i++) {
CardState state = Normal;
if (playerCardPositions[i][2] == 1 && playerCardPositions[i][3] == 1)
state = BottomAndRightCut;
else if (playerCardPositions[i][3] == 1)
state = RightCut;
else if (playerCardPositions[i][2] == 1)
state = BottomCut;
drawCardAt(playerCardPositions[i][0], playerCardPositions[i][1], cards[i].pip, cards[i].character, state,
canvas);
}
}
void drawCardAt(uint8_t pos_x, uint8_t pos_y, uint8_t pip, uint8_t character, CardState state, Canvas *const canvas) {
if (state == Normal) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDHT, CARD_HEIGHT);
canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDHT, CARD_HEIGHT);
} else {
if (state == BottomCut || state == BottomAndRightCut)
canvas_draw_line(canvas, pos_x, pos_y, pos_x, pos_y + CARD_HALF_HEIGHT - 1); //half height line
if (state == BottomCut) {
canvas_draw_line(canvas, pos_x, pos_y, pos_x + CARD_WIDHT - 1, pos_y); //full width line
canvas_draw_line(canvas, pos_x + CARD_WIDHT - 1, pos_y, pos_x + CARD_WIDHT - 1,
pos_y + CARD_HALF_HEIGHT - 1); //half height line
}
if (state == BottomAndRightCut) {
canvas_draw_line(canvas, pos_x, pos_y, pos_x + CARD_HALF_WIDHT - 1, pos_y); //half width
}
if (state == RightCut) {
canvas_draw_line(canvas, pos_x, pos_y, pos_x + CARD_HALF_WIDHT - 1, pos_y); //half width
canvas_draw_line(canvas, pos_x, pos_y, pos_x, pos_y + CARD_HEIGHT - 1); //full height line
canvas_draw_line(canvas, pos_x, pos_y + CARD_HEIGHT - 1, pos_x + CARD_HALF_WIDHT - 1,
pos_y + CARD_HEIGHT - 1); //full height line
}
}
uint8_t left = pos_x + CORNER_MARGIN;
uint8_t right = (pos_x + CARD_WIDHT - CORNER_MARGIN - 7);
uint8_t top = pos_y + CORNER_MARGIN;
uint8_t bottom = (pos_y + CARD_HEIGHT - CORNER_MARGIN - 7);
for (uint8_t x = 0; x < 7; x++) {
for (uint8_t y = 0; y < 7; y++) {
if (pips[pip][x + y * 7]) {
if (state == Normal || state == BottomCut)
canvas_draw_dot(canvas, right + x + 1, top + y);
if (state == Normal || state == RightCut)
canvas_draw_dot(canvas, left + x - 1, bottom + y);
}
}
}
canvas_set_font(canvas, FontSecondary);
char drawChar[3];
if (character < 9)
snprintf(drawChar, sizeof(drawChar), "%i", character + 2);
else {
snprintf(drawChar, sizeof(drawChar), "%c", characters[character]);
}
canvas_set_font_direction(canvas, CanvasDirectionLeftToRight);
canvas_draw_str_aligned(canvas, left + 2, top + 3, AlignCenter, AlignCenter, drawChar);
canvas_set_font_direction(canvas, CanvasDirectionRightToLeft);
if (state == Normal) { //flipper crashes on non center aligned text when upside down
uint8_t margin = 9;
if (character == 8) //10 needs bigger margin
margin = 12;
canvas_draw_str_aligned(canvas, right + margin, bottom - 3, AlignCenter, AlignCenter, drawChar);
}
canvas_set_font_direction(canvas, CanvasDirectionLeftToRight);
//canvas_draw_str(canvas, left, top, drawChar );
}
void drawCardBackAt(uint8_t pos_x, uint8_t pos_y, Canvas *const canvas) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDHT, CARD_HEIGHT);
canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDHT, CARD_HEIGHT);
for (uint8_t x = 0; x < CARD_WIDHT - 2; x++) {
for (uint8_t y = 0; y < CARD_HEIGHT - 2; y++) {
uint8_t _x = x;
uint8_t _y = y * 2;
if (backDesign[(_x + _y) % 4]) {
canvas_draw_dot(canvas, pos_x + x + 1, pos_y + y + 1);
}
}
}
}
void generateDeck(Deck *deck_ptr) {
uint16_t counter = 0;
for (uint8_t deck = 0; deck < DECK_COUNT; deck++) {
for (uint8_t pip = 0; pip < 4; pip++) {
for (uint8_t label = 0; label < 13; label++) {
deck_ptr->cards[counter] = (Card)
{
pip, label
};
counter++;
}
}
}
}
void shuffleDeck(Deck *deck_ptr) {
srand(DWT->CYCCNT);
deck_ptr->index = 0;
int max = DECK_COUNT * 52;
for (int i = 0; i < max; i++) {
int r = i + (rand() % (max - i));
Card tmp = deck_ptr->cards[i];
deck_ptr->cards[i] = deck_ptr->cards[r];
deck_ptr->cards[r] = tmp;
}
}
uint8_t handCount(const Card cards[21], uint8_t count) {
uint8_t aceCount = 0;
uint8_t score = 0;
for (uint8_t i = 0; i < count; i++) {
if (cards[i].character == 12)
aceCount++;
else {
if (cards[i].character > 8)
score += 10;
else
score += cards[i].character + 2;
}
}
for (uint8_t i = 0; i < aceCount; i++) {
if ((score + 11) <= 21) score += 11;
else score++;
}
return score;
}

View File

@@ -0,0 +1,40 @@
#ifndef _card_h
#define _card_h
#include <gui/gui.h>
#define DECK_COUNT 6
#define CARD_HEIGHT 24
#define CARD_HALF_HEIGHT CARD_HEIGHT/2
#define CARD_WIDHT 18
#define CARD_HALF_WIDHT CARD_WIDHT/2
#define CORNER_MARGIN 3
#define LEGEND_SIZE 10
typedef enum {
Normal, BottomCut, RightCut, BottomAndRightCut, TopCut, LeftCut, TopAndLeftCut
} CardState;
typedef struct {
uint8_t pip;
uint8_t character;
} Card;
typedef struct {
Card cards[52 * DECK_COUNT];
int index;
} Deck;
void drawPlayerDeck(const Card cards[21], uint8_t count, Canvas *const canvas);
void drawCardAt(uint8_t pos_x, uint8_t pos_y, uint8_t pip, uint8_t character, CardState state, Canvas *const canvas);
void drawCardBackAt(uint8_t pos_x, uint8_t pos_y, Canvas *const canvas);
void generateDeck(Deck *deck_ptr);
void shuffleDeck(Deck *deck_ptr);
uint8_t handCount(const Card cards[21], uint8_t count);
#endif

View File

@@ -0,0 +1,61 @@
#pragma once
#include <furi.h>
#include <input/input.h>
#include <gui/elements.h>
#include "card.h"
#define ANIMATION_TIME furi_ms_to_ticks(1500)
#define ANIMATION_END_MARGIN furi_ms_to_ticks(200)
#define ROUND_PRICE 10
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} AppEvent;
typedef enum {
GameStateGameOver,
GameStateStart,
GameStatePlay,
GameStateDealer,
} PlayState;
typedef enum {
DirectionUp,
DirectionRight,
DirectionDown,
DirectionLeft,
Select,
None
} Direction;
typedef struct {
Card player_cards[21];
Card dealer_cards[21];
uint8_t player_card_count;
uint8_t dealer_card_count;
Direction selectDirection;
uint32_t player_score;
uint8_t multiplier;
uint32_t bet;
uint8_t player_time;
bool doubled;
bool animating;
bool started;
uint8_t selectedMenu;
Deck deck;
PlayState state;
unsigned int last_tick;
unsigned int animationStart;
bool dealer_animating;
unsigned int delay_tick;
} GameState;

View File

@@ -0,0 +1,127 @@
#include "ui.h"
#include "card.h"
#include <math.h>
#include "util.h"
const char MoneyMul[4] = {
'K', 'B', 'T', 'S'
};
void draw_player_scene(Canvas *const canvas, const GameState *game_state) {
int max_card = game_state->player_card_count;
if (max_card > 0)
drawPlayerDeck((game_state->player_cards), max_card, canvas);
drawCardBackAt(13, 5, canvas);
max_card = game_state->dealer_card_count;
if (max_card > 1) {
drawCardAt(2, 2, game_state->dealer_cards[1].pip, game_state->dealer_cards[1].character, Normal,
canvas);
}
}
void draw_dealer_scene(Canvas *const canvas, const GameState *game_state) {
uint8_t max_card = game_state->dealer_card_count;
drawPlayerDeck((game_state->dealer_cards), max_card, canvas);
}
void draw_card_animation(const GameState *game_state, Canvas *const canvas) {
float t = (float) (furi_get_tick() - game_state->animationStart) / (ANIMATION_TIME - ANIMATION_END_MARGIN);
t *= 2;
Card animatingCard = game_state->deck.cards[game_state->deck.index];
if (t > 1) {
int cardY = round(lerp(-CARD_HEIGHT, 10, 1));
drawCardAt(64 - CARD_HALF_WIDHT, cardY, animatingCard.pip,
animatingCard.character, Normal, canvas);
} else {
int cardY = round(lerp(-CARD_HEIGHT, 10, t));
drawCardAt(64 - CARD_HALF_WIDHT, cardY, animatingCard.pip,
animatingCard.character, Normal, canvas);
// drawCardBackAt(64 - CARD_HALF_WIDHT, cardY, canvas);
}
}
void popupFrame(Canvas *const canvas) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 32, 15, 66, 13);
canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, 32, 15, 66, 13);
canvas_set_font(canvas, FontSecondary);
}
void draw_message_scene(Canvas *const canvas, const GameState *game_state) {
switch (game_state->state) {
case GameStateStart:
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 64, 5, AlignCenter, AlignTop, "Blackjack");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 64, 24, AlignCenter, AlignTop, "Made by Doofy");
elements_multiline_text_aligned(canvas, 64, 38, AlignCenter, AlignTop, "Press center button\nto start");
break;
case GameStateGameOver:
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 64, 5, AlignCenter, AlignTop, "Game Over");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 64, 24, AlignCenter, AlignTop, "Press center button\nto start");
break;
default:
break;
}
}
void draw_play_menu(Canvas *const canvas, const GameState *game_state) {
const char *menus[3] = {"Double", "Hit", "Stay"};
for (uint8_t m = 0; m < 3; m++) {
if (m == 0 && (game_state->doubled || game_state->player_score < ROUND_PRICE)) continue;
int y = m * 13 + 25;
canvas_set_color(canvas, ColorBlack);
if (game_state->selectedMenu == m) {
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 1, y, 31, 12);
} else {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 1, y, 31, 12);
canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, 1, y, 31, 12);
}
if (game_state->selectedMenu == m)
canvas_set_color(canvas, ColorWhite);
else
canvas_set_color(canvas, ColorBlack);
canvas_draw_str_aligned(canvas, 16, y + 6, AlignCenter, AlignCenter, menus[m]);
}
}
void draw_score(Canvas *const canvas, bool top, uint8_t amount) {
char drawChar[20];
snprintf(drawChar, sizeof(drawChar), "Player score: %i", amount);
if (top)
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, drawChar);
else
canvas_draw_str_aligned(canvas, 64, 62, AlignCenter, AlignBottom, drawChar);
}
void draw_money(Canvas *const canvas, uint32_t score) {
canvas_set_font(canvas, FontSecondary);
char drawChar[10];
uint32_t currAmount = score;
if (currAmount < 1000) {
snprintf(drawChar, sizeof(drawChar), "$%lu", currAmount);
} else {
char c = 'K';
for (uint8_t i = 0; i < 4; i++) {
currAmount = currAmount / 1000;
if (currAmount < 1000) {
c = MoneyMul[i];
break;
}
}
snprintf(drawChar, sizeof(drawChar), "$%lu %c", currAmount, c);
}
canvas_draw_str_aligned(canvas, 126, 2, AlignRight, AlignTop, drawChar);
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "defines.h"
#include <gui/gui.h>
void draw_player_scene(Canvas *const canvas, const GameState *game_state);
void draw_dealer_scene(Canvas *const canvas, const GameState *game_state);
void draw_message_scene(Canvas *const canvas, const GameState *game_state);
void draw_play_menu(Canvas *const canvas, const GameState *game_state);
void draw_score(Canvas *const canvas, bool top, uint8_t amount);
void draw_money(Canvas *const canvas, uint32_t score);
void draw_card_animation(const GameState *game_state, Canvas *const canvas);
void popupFrame(Canvas *const canvas);

View File

@@ -0,0 +1,65 @@
#include "util.h"
static List *afterDelay;
float lerp(float v0, float v1, float t) {
return (1 - t) * v0 + t * v1;
}
void queue(GameState *game_state,
void (*callback)(GameState *game_state),
void (*start)(GameState *game_state),
void (*processing)(const GameState *gameState, Canvas *const canvas)
) {
if (afterDelay == NULL) {
game_state->animationStart = game_state->last_tick;
afterDelay = malloc(sizeof(List));
afterDelay->callback = callback;
afterDelay->processing = processing;
afterDelay->start = start;
afterDelay->next = NULL;
} else {
List *next = afterDelay;
while (next->next != NULL) { next = (List *) (next->next); }
next->next = malloc(sizeof(List));
((List *) next->next)->callback = callback;
((List *) next->next)->processing = processing;
((List *) next->next)->start = start;
((List *) next->next)->next = NULL;
}
}
void queue_clear() {
while (afterDelay != NULL) {
List *f = afterDelay;
free(f);
afterDelay = f->next;
}
}
void dequeue(GameState *game_state) {
((List *) afterDelay)->callback(game_state);
List *f = afterDelay;
afterDelay = (List *) afterDelay->next;
free(f);
if (afterDelay != NULL && afterDelay->start != NULL)afterDelay->start(game_state);
game_state->animationStart = game_state->last_tick;
}
void animateQueue(const GameState *game_state, Canvas *const canvas) {
if (afterDelay != NULL && ((List *) afterDelay)->processing != NULL) {
((List *) afterDelay)->processing(game_state, canvas);
}
}
bool run_queue(GameState *game_state) {
if (afterDelay != NULL) {
game_state->animating = true;
if ((game_state->last_tick - game_state->animationStart) > ANIMATION_TIME) {
dequeue(game_state);
}
return true;
}
game_state->animating = false;
return false;
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include "defines.h"
typedef struct{
void (*callback)(GameState *game_state);
void (*processing)(const GameState *game_state, Canvas *const canvas);
void (*start)(GameState *game_state);
void *next;
} List;
float lerp(float v0, float v1, float t);
void queue(GameState *game_state,
void (*callback)(GameState *game_state),
void (*start)(GameState *game_state),
void (*processing)(const GameState *gameState, Canvas *const canvas));
bool run_queue(GameState *gameState);
void animateQueue(const GameState *gameState, Canvas *const canvas);
void queue_clear();