blackjack upd

This commit is contained in:
RogueMaster
2022-11-17 23:37:29 -05:00
parent b32612b1c9
commit 7c09807aeb
17 changed files with 1145 additions and 758 deletions

View File

@@ -1,10 +1,10 @@
App( App(
appid="BlackJack", appid="blackjack",
name="BlackJack", name="Blackjack",
apptype=FlipperAppType.EXTERNAL, apptype=FlipperAppType.EXTERNAL,
entry_point="blackjack_app", entry_point="blackjack_app",
cdefines=["APP_BLACKJACK"], cdefines=["APP_BLACKJACK"],
requires=["gui","storage"], requires=["gui","storage","canvas"],
stack_size=2 * 1024, stack_size=2 * 1024,
order=30, order=30,
fap_icon="blackjack_10px.png", fap_icon="blackjack_10px.png",

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

View File

@@ -3,6 +3,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#include <dialogs/dialogs.h> #include <dialogs/dialogs.h>
#include <gui/canvas_i.h>
#include <math.h> #include <math.h>
#include "util.h" #include "util.h"
#include "defines.h" #include "defines.h"
@@ -12,74 +14,76 @@
#include "util.h" #include "util.h"
#include "ui.h" #include "ui.h"
#include "BlackJack_icons.h" #include "blackjack_icons.h"
#define DEALER_MAX 17 #define DEALER_MAX 17
void start_round(GameState* game_state); void start_round(GameState *game_state);
void init(GameState* game_state); void init(GameState *game_state);
static void draw_ui(Canvas *const canvas, const GameState *game_state) {
static void draw_ui(Canvas* const canvas, const GameState* game_state) {
draw_money(canvas, game_state->player_score); draw_money(canvas, game_state->player_score);
draw_score(canvas, true, hand_count(game_state->player_cards, game_state->player_card_count)); draw_score(canvas, true, hand_count(game_state->player_cards, game_state->player_card_count));
if(!game_state->queue_state.running && game_state->state == GameStatePlay) { if (!game_state->queue_state.running && game_state->state == GameStatePlay) {
render_menu(game_state->menu, canvas, 2, 47); render_menu(game_state->menu,canvas, 2, 47);
} }
} }
static void render_callback(Canvas* const canvas, void* ctx) { static void render_callback(Canvas *const canvas, void *ctx) {
const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25); const GameState *game_state = acquire_mutex((ValueMutex *) ctx, 25);
if(game_state == NULL) { if (game_state == NULL) {
return; return;
} }
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, 0, 0, 128, 64); canvas_draw_frame(canvas, 0, 0, 128, 64);
if(game_state->state == GameStateStart) { if (game_state->state == GameStateStart) {
canvas_draw_icon(canvas, 0, 0, &I_blackjack); canvas_draw_icon(canvas, 0, 0, &I_blackjack);
} }
if(game_state->state == GameStateGameOver) { if (game_state->state == GameStateGameOver) {
canvas_draw_icon(canvas, 0, 0, &I_endscreen); canvas_draw_icon(canvas, 0, 0, &I_endscreen);
} }
if(game_state->state == GameStatePlay || game_state->state == GameStateDealer) { if (game_state->state == GameStatePlay || game_state->state == GameStateDealer) {
if(game_state->state == GameStatePlay) if (game_state->state == GameStatePlay)
draw_player_scene(canvas, game_state); draw_player_scene(canvas, game_state);
else else
draw_dealer_scene(canvas, game_state); draw_dealer_scene(canvas, game_state);
render_queue(&(game_state->queue_state), game_state, canvas); render_queue(&(game_state->queue_state), game_state, canvas);
draw_ui(canvas, game_state); draw_ui(canvas, game_state);
} else if(game_state->state == GameStateSettings) { } else if (game_state->state == GameStateSettings) {
settings_page(canvas, game_state); settings_page(canvas, game_state);
} }
release_mutex((ValueMutex*)ctx, game_state); release_mutex((ValueMutex *) ctx, game_state);
} }
//region card draw //region card draw
Card draw_card(GameState* game_state) { Card draw_card(GameState *game_state) {
Card c = game_state->deck.cards[game_state->deck.index]; Card c = game_state->deck.cards[game_state->deck.index];
game_state->deck.index++; game_state->deck.index++;
return c; return c;
} }
void drawPlayerCard(void* ctx) {
GameState* game_state = ctx; void drawPlayerCard(void *ctx) {
GameState *game_state = ctx;
Card c = draw_card(game_state); Card c = draw_card(game_state);
game_state->player_cards[game_state->player_card_count] = c; game_state->player_cards[game_state->player_card_count] = c;
game_state->player_card_count++; game_state->player_card_count++;
if(game_state->player_score < game_state->settings.round_price || game_state->doubled) { if(game_state->player_score < game_state->settings.round_price || game_state->doubled){
set_menu_state(game_state->menu, 0, false); set_menu_state(game_state->menu, 0, false);
} }
} }
void drawDealerCard(void* ctx) { void drawDealerCard(void *ctx) {
GameState* game_state = ctx; GameState *game_state = ctx;
Card c = draw_card(game_state); Card c = draw_card(game_state);
game_state->dealer_cards[game_state->dealer_card_count] = c; game_state->dealer_cards[game_state->dealer_card_count] = c;
game_state->dealer_card_count++; game_state->dealer_card_count++;
@@ -87,363 +91,317 @@ void drawDealerCard(void* ctx) {
//endregion //endregion
//region queue callbacks //region queue callbacks
void to_lose_state(const void* ctx, Canvas* const canvas) { void to_lose_state(const void *ctx, Canvas *const canvas) {
const GameState* game_state = ctx; const GameState *game_state = ctx;
if(game_state->settings.message_duration == 0) return; if (game_state->settings.message_duration == 0)
return;
popup_frame(canvas); popup_frame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "You lost"); elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "You lost");
} }
void to_bust_state(const void* ctx, Canvas* const canvas) { void to_bust_state(const void *ctx, Canvas *const canvas) {
const GameState* game_state = ctx; const GameState *game_state = ctx;
if(game_state->settings.message_duration == 0) return; if (game_state->settings.message_duration == 0)
return;
popup_frame(canvas); popup_frame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Busted!"); elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Busted!");
} }
void to_draw_state(const void* ctx, Canvas* const canvas) { void to_draw_state(const void *ctx, Canvas *const canvas) {
const GameState* game_state = ctx; const GameState *game_state = ctx;
if(game_state->settings.message_duration == 0) return; if (game_state->settings.message_duration == 0)
return;
popup_frame(canvas); popup_frame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Draw"); elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Draw");
} }
void to_dealer_turn(const void* ctx, Canvas* const canvas) { void to_dealer_turn(const void *ctx, Canvas *const canvas) {
const GameState* game_state = ctx; const GameState *game_state = ctx;
if(game_state->settings.message_duration == 0) return; if (game_state->settings.message_duration == 0)
return;
popup_frame(canvas); popup_frame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Dealers turn"); elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Dealers turn");
} }
void to_win_state(const void* ctx, Canvas* const canvas) { void to_win_state(const void *ctx, Canvas *const canvas) {
const GameState* game_state = ctx; const GameState *game_state = ctx;
if(game_state->settings.message_duration == 0) return; if (game_state->settings.message_duration == 0)
return;
popup_frame(canvas); popup_frame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "You win"); elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "You win");
} }
void to_start(const void* ctx, Canvas* const canvas) { void to_start(const void *ctx, Canvas *const canvas) {
const GameState* game_state = ctx; const GameState *game_state = ctx;
if(game_state->settings.message_duration == 0) return; if (game_state->settings.message_duration == 0)
return;
popup_frame(canvas); popup_frame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Round started"); elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Round started");
} }
void before_start(void* ctx) { void before_start(void *ctx) {
GameState* game_state = ctx; GameState *game_state = ctx;
game_state->dealer_card_count = 0; game_state->dealer_card_count = 0;
game_state->player_card_count = 0; game_state->player_card_count = 0;
} }
void start(void* ctx) {
GameState* game_state = ctx; void start(void *ctx) {
GameState *game_state = ctx;
start_round(game_state); start_round(game_state);
} }
void draw(void* ctx) { void draw(void *ctx) {
GameState* game_state = ctx; GameState *game_state = ctx;
game_state->player_score += game_state->bet; game_state->player_score += game_state->bet;
game_state->bet = 0; game_state->bet = 0;
enqueue( enqueue(&(game_state->queue_state), game_state, start, before_start, to_start,
&(game_state->queue_state), game_state->settings.message_duration);
game_state,
start,
before_start,
to_start,
game_state->settings.message_duration);
} }
void game_over(void* ctx) { void game_over(void *ctx) {
GameState* game_state = ctx; GameState *game_state = ctx;
game_state->state = GameStateGameOver; game_state->state = GameStateGameOver;
} }
void lose(void* ctx) { void lose(void *ctx) {
GameState* game_state = ctx; GameState *game_state = ctx;
game_state->state = GameStatePlay; game_state->state = GameStatePlay;
game_state->bet = 0; game_state->bet = 0;
if(game_state->player_score >= game_state->settings.round_price) { if (game_state->player_score >= game_state->settings.round_price) {
enqueue( enqueue(&(game_state->queue_state), game_state, start, before_start, to_start,
&(game_state->queue_state), game_state->settings.message_duration);
game_state,
start,
before_start,
to_start,
game_state->settings.message_duration);
} else { } else {
enqueue(&(game_state->queue_state), game_state, game_over, NULL, NULL, 0); enqueue(&(game_state->queue_state), game_state, game_over, NULL, NULL,
0);
} }
} }
void win(void* ctx) { void win(void *ctx) {
GameState* game_state = ctx; GameState *game_state = ctx;
game_state->state = GameStatePlay; game_state->state = GameStatePlay;
game_state->player_score += game_state->bet * 2; game_state->player_score += game_state->bet * 2;
game_state->bet = 0; game_state->bet = 0;
enqueue( enqueue(&(game_state->queue_state), game_state, start, before_start, to_start,
&(game_state->queue_state), game_state->settings.message_duration);
game_state,
start,
before_start,
to_start,
game_state->settings.message_duration);
} }
void dealerTurn(void* ctx) {
GameState* game_state = ctx; void dealerTurn(void *ctx) {
GameState *game_state = ctx;
game_state->state = GameStateDealer; game_state->state = GameStateDealer;
} }
float animationTime(const GameState* game_state) { float animationTime(const GameState *game_state){
return (float)(furi_get_tick() - game_state->queue_state.start) / return (float) (furi_get_tick() - game_state->queue_state.start) /
(float)(game_state->settings.animation_duration); (float) (game_state->settings.animation_duration);
} }
void dealer_card_animation(const void* ctx, Canvas* const canvas) { void dealer_card_animation(const void *ctx, Canvas *const canvas) {
const GameState* game_state = ctx; const GameState *game_state = ctx;
float t = animationTime(game_state); float t = animationTime(game_state);
Card animatingCard = game_state->deck.cards[game_state->deck.index]; Card animatingCard = game_state->deck.cards[game_state->deck.index];
if(game_state->dealer_card_count > 1) { if (game_state->dealer_card_count > 1) {
Vector end = card_pos_at_index(game_state->dealer_card_count); Vector end = card_pos_at_index(game_state->dealer_card_count);
draw_card_animation(animatingCard, (Vector){0, 64}, (Vector){0, 32}, end, t, true, canvas); draw_card_animation(animatingCard,
(Vector) {0, 64},
(Vector) {0, 32},
end,
t,
true,
canvas);
} else { } else {
draw_card_animation( draw_card_animation(animatingCard,
animatingCard, (Vector) {32, -CARD_HEIGHT},
(Vector){32, -CARD_HEIGHT}, (Vector) {64, 32},
(Vector){64, 32}, (Vector) {2, 2},
(Vector){2, 2}, t,
t, false,
false, canvas);
canvas);
// draw_deck(game_state->dealer_cards, game_state->dealer_card_count, canvas);
} }
} }
void dealer_back_card_animation(const void* ctx, Canvas* const canvas) { void dealer_back_card_animation(const void *ctx, Canvas *const canvas) {
const GameState* game_state = ctx; const GameState *game_state = ctx;
float t = animationTime(game_state); float t = animationTime(game_state);
Vector currentPos = Vector currentPos = quadratic_2d((Vector) {32, -CARD_HEIGHT}, (Vector) {64, 32}, (Vector) {13, 5}, t);
quadratic_2d((Vector){32, -CARD_HEIGHT}, (Vector){64, 32}, (Vector){13, 5}, t);
draw_card_back_at(currentPos.x, currentPos.y, canvas); draw_card_back_at(currentPos.x, currentPos.y, canvas);
} }
void player_card_animation(const void* ctx, Canvas* const canvas) { void player_card_animation(const void *ctx, Canvas *const canvas) {
const GameState* game_state = ctx; const GameState *game_state = ctx;
float t = animationTime(game_state); float t = animationTime(game_state);
Card animatingCard = game_state->deck.cards[game_state->deck.index]; Card animatingCard = game_state->deck.cards[game_state->deck.index];
Vector end = card_pos_at_index(game_state->player_card_count); Vector end = card_pos_at_index(game_state->player_card_count);
draw_card_animation( draw_card_animation(animatingCard,
animatingCard, (Vector){32, -CARD_HEIGHT}, (Vector){0, 32}, end, t, true, canvas); (Vector) {32, -CARD_HEIGHT},
// draw_deck(game_state->dealer_cards, game_state->player_card_count, canvas); (Vector) {0, 32},
end,
t,
true,
canvas);
} }
//endregion //endregion
void player_tick(GameState* game_state) { void player_tick(GameState *game_state) {
uint8_t score = hand_count(game_state->player_cards, game_state->player_card_count); uint8_t score = hand_count(game_state->player_cards, game_state->player_card_count);
if((game_state->doubled && score <= 21) || score == 21) { if ((game_state->doubled && score <= 21) || score == 21) {
enqueue( enqueue(&(game_state->queue_state), game_state, dealerTurn, NULL, to_dealer_turn,
&(game_state->queue_state), game_state->settings.message_duration);
game_state, } else if (score > 21) {
dealerTurn, enqueue(&(game_state->queue_state), game_state, lose, NULL, to_bust_state,
NULL, game_state->settings.message_duration);
to_dealer_turn,
game_state->settings.message_duration);
} else if(score > 21) {
enqueue(
&(game_state->queue_state),
game_state,
lose,
NULL,
to_bust_state,
game_state->settings.message_duration);
} else { } else {
if(game_state->selectDirection == DirectionUp || if(game_state->selectDirection == DirectionUp || game_state->selectDirection == DirectionDown){
game_state->selectDirection == DirectionDown) {
move_menu(game_state->menu, game_state->selectDirection == DirectionUp ? -1 : 1); move_menu(game_state->menu, game_state->selectDirection == DirectionUp ? -1 : 1);
} }
if(game_state->selectDirection == Select) { if (game_state->selectDirection == Select){
activate_menu(game_state->menu, game_state); activate_menu(game_state->menu, game_state);
} }
} }
} }
void dealer_tick(GameState* game_state) { void dealer_tick(GameState *game_state) {
uint8_t dealer_score = hand_count(game_state->dealer_cards, game_state->dealer_card_count); uint8_t dealer_score = hand_count(game_state->dealer_cards, game_state->dealer_card_count);
uint8_t player_score = hand_count(game_state->player_cards, game_state->player_card_count); uint8_t player_score = hand_count(game_state->player_cards, game_state->player_card_count);
if(dealer_score >= DEALER_MAX) { if (dealer_score >= DEALER_MAX) {
if(dealer_score > 21 || dealer_score < player_score) { if (dealer_score > 21 || dealer_score < player_score) {
enqueue( enqueue(&(game_state->queue_state), game_state, win, NULL, to_win_state,
&(game_state->queue_state), game_state->settings.message_duration);
game_state, } else if (dealer_score > player_score) {
win, enqueue(&(game_state->queue_state), game_state, lose, NULL, to_lose_state,
NULL, game_state->settings.message_duration);
to_win_state, } else if (dealer_score == player_score) {
game_state->settings.message_duration); enqueue(&(game_state->queue_state), game_state, draw, NULL, to_draw_state,
} else if(dealer_score > player_score) { game_state->settings.message_duration);
enqueue(
&(game_state->queue_state),
game_state,
lose,
NULL,
to_lose_state,
game_state->settings.message_duration);
} else if(dealer_score == player_score) {
enqueue(
&(game_state->queue_state),
game_state,
draw,
NULL,
to_draw_state,
game_state->settings.message_duration);
} }
} else { } else {
enqueue( enqueue(&(game_state->queue_state), game_state, drawDealerCard, NULL, dealer_card_animation,
&(game_state->queue_state), game_state->settings.animation_duration);
game_state,
drawDealerCard,
NULL,
dealer_card_animation,
game_state->settings.animation_duration);
} }
} }
void settings_tick(GameState* game_state) { void settings_tick(GameState *game_state) {
if(game_state->selectDirection == DirectionDown && game_state->selectedMenu < 4) { if (game_state->selectDirection == DirectionDown && game_state->selectedMenu < 4) {
game_state->selectedMenu++; game_state->selectedMenu++;
} }
if(game_state->selectDirection == DirectionUp && game_state->selectedMenu > 0) { if (game_state->selectDirection == DirectionUp && game_state->selectedMenu > 0) {
game_state->selectedMenu--; game_state->selectedMenu--;
} }
if(game_state->selectDirection == DirectionLeft || if (game_state->selectDirection == DirectionLeft || game_state->selectDirection == DirectionRight) {
game_state->selectDirection == DirectionRight) {
int nextScore = 0; int nextScore = 0;
switch(game_state->selectedMenu) { switch (game_state->selectedMenu) {
case 0: case 0:
nextScore = game_state->settings.starting_money; nextScore = game_state->settings.starting_money;
if(game_state->selectDirection == DirectionLeft) if (game_state->selectDirection == DirectionLeft)
nextScore -= 10; nextScore -= 10;
else else
nextScore += 10; nextScore += 10;
if(nextScore >= (int)game_state->settings.round_price && nextScore < 400) if (nextScore >= (int) game_state->settings.round_price && nextScore < 400)
game_state->settings.starting_money = nextScore; game_state->settings.starting_money = nextScore;
break; break;
case 1: case 1:
nextScore = game_state->settings.round_price; nextScore = game_state->settings.round_price;
if(game_state->selectDirection == DirectionLeft) if (game_state->selectDirection == DirectionLeft)
nextScore -= 10; nextScore -= 10;
else else
nextScore += 10; nextScore += 10;
if(nextScore >= 5 && nextScore <= (int)game_state->settings.starting_money) if (nextScore >= 5 && nextScore <= (int) game_state->settings.starting_money)
game_state->settings.round_price = nextScore; game_state->settings.round_price = nextScore;
break; break;
case 2: case 2:
nextScore = game_state->settings.animation_duration; nextScore = game_state->settings.animation_duration;
if(game_state->selectDirection == DirectionLeft) if (game_state->selectDirection == DirectionLeft)
nextScore -= 100; nextScore -= 100;
else else
nextScore += 100; nextScore += 100;
if(nextScore >= 0 && nextScore < 2000) if (nextScore >= 0 && nextScore < 2000)
game_state->settings.animation_duration = nextScore; game_state->settings.animation_duration = nextScore;
break; break;
case 3: case 3:
nextScore = game_state->settings.message_duration; nextScore = game_state->settings.message_duration;
if(game_state->selectDirection == DirectionLeft) if (game_state->selectDirection == DirectionLeft)
nextScore -= 100; nextScore -= 100;
else else
nextScore += 100; nextScore += 100;
if(nextScore >= 0 && nextScore < 2000) if (nextScore >= 0 && nextScore < 2000)
game_state->settings.message_duration = nextScore; game_state->settings.message_duration = nextScore;
break; break;
case 4: case 4:
game_state->settings.sound_effects = !game_state->settings.sound_effects; game_state->settings.sound_effects = !game_state->settings.sound_effects;
default: default:
break; break;
} }
} }
} }
void tick(GameState* game_state) { void tick(GameState *game_state) {
game_state->last_tick = furi_get_tick(); game_state->last_tick = furi_get_tick();
bool queue_ran = run_queue(&(game_state->queue_state), game_state); bool queue_ran = run_queue(&(game_state->queue_state), game_state);
switch(game_state->state) { switch (game_state->state) {
case GameStateGameOver: case GameStateGameOver:
case GameStateStart: case GameStateStart:
if(game_state->selectDirection == Select) if (game_state->selectDirection == Select)
init(game_state); init(game_state);
else if(game_state->selectDirection == DirectionRight) { else if (game_state->selectDirection == DirectionRight) {
game_state->selectedMenu = 0; game_state->selectedMenu = 0;
game_state->state = GameStateSettings; game_state->state = GameStateSettings;
} }
break; break;
case GameStatePlay: case GameStatePlay:
if(!game_state->started) { if (!game_state->started) {
game_state->selectedMenu = 0; game_state->selectedMenu = 0;
game_state->started = true; game_state->started = true;
enqueue( enqueue(&(game_state->queue_state), game_state, drawDealerCard, NULL, dealer_back_card_animation,
&(game_state->queue_state), game_state->settings.animation_duration);
game_state, enqueue(&(game_state->queue_state), game_state, drawPlayerCard, NULL, player_card_animation,
drawDealerCard, game_state->settings.animation_duration);
NULL, enqueue(&(game_state->queue_state), game_state, drawDealerCard, NULL, dealer_card_animation,
dealer_back_card_animation, game_state->settings.animation_duration);
game_state->settings.animation_duration); enqueue(&(game_state->queue_state), game_state, drawPlayerCard, NULL, player_card_animation,
enqueue( game_state->settings.animation_duration);
&(game_state->queue_state), }
game_state, if (!queue_ran)
drawPlayerCard, player_tick(game_state);
NULL, break;
player_card_animation, case GameStateDealer:
game_state->settings.animation_duration); if (!queue_ran)
enqueue( dealer_tick(game_state);
&(game_state->queue_state), break;
game_state, case GameStateSettings:
drawDealerCard, settings_tick(game_state);
NULL, break;
dealer_card_animation, default:
game_state->settings.animation_duration); break;
enqueue(
&(game_state->queue_state),
game_state,
drawPlayerCard,
NULL,
player_card_animation,
game_state->settings.animation_duration);
}
if(!queue_ran) player_tick(game_state);
break;
case GameStateDealer:
if(!queue_ran) dealer_tick(game_state);
break;
case GameStateSettings:
settings_tick(game_state);
break;
default:
break;
} }
game_state->selectDirection = None; game_state->selectDirection = None;
} }
void start_round(GameState* game_state) { void start_round(GameState *game_state) {
game_state->menu->current_menu = 1; game_state->menu->current_menu=1;
game_state->player_card_count = 0; game_state->player_card_count = 0;
game_state->dealer_card_count = 0; game_state->dealer_card_count = 0;
set_menu_state(game_state->menu, 0, true); set_menu_state(game_state->menu, 0, true);
game_state->menu->enabled = true; game_state->menu->enabled=true;
game_state->started = false; game_state->started = false;
game_state->doubled = false; game_state->doubled = false;
game_state->queue_state.running = true; game_state->queue_state.running = true;
shuffle_deck(&(game_state->deck)); shuffle_deck(&(game_state->deck));
game_state->doubled = false; game_state->doubled = false;
game_state->bet = game_state->settings.round_price; game_state->bet = game_state->settings.round_price;
if(game_state->player_score < game_state->settings.round_price) { if (game_state->player_score < game_state->settings.round_price) {
game_state->state = GameStateGameOver; game_state->state = GameStateGameOver;
} else { } else {
game_state->player_score -= game_state->settings.round_price; game_state->player_score -= game_state->settings.round_price;
@@ -451,10 +409,10 @@ void start_round(GameState* game_state) {
game_state->state = GameStatePlay; game_state->state = GameStatePlay;
} }
void init(GameState* game_state) { void init(GameState *game_state) {
set_menu_state(game_state->menu, 0, true); set_menu_state(game_state->menu, 0, true);
game_state->menu->enabled = true; game_state->menu->enabled=true;
game_state->menu->current_menu = 1; game_state->menu->current_menu=1;
game_state->settings = load_settings(); game_state->settings = load_settings();
game_state->last_tick = 0; game_state->last_tick = 0;
game_state->processing = true; game_state->processing = true;
@@ -464,146 +422,123 @@ void init(GameState* game_state) {
start_round(game_state); start_round(game_state);
} }
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { static void input_callback(InputEvent *input_event, FuriMessageQueue *event_queue) {
furi_assert(event_queue); furi_assert(event_queue);
AppEvent event = {.type = EventTypeKey, .input = *input_event}; AppEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever); furi_message_queue_put(event_queue, &event, FuriWaitForever);
} }
static void update_timer_callback(FuriMessageQueue* event_queue) { static void update_timer_callback(FuriMessageQueue *event_queue) {
furi_assert(event_queue); furi_assert(event_queue);
AppEvent event = {.type = EventTypeTick}; AppEvent event = {.type = EventTypeTick};
furi_message_queue_put(event_queue, &event, 0); furi_message_queue_put(event_queue, &event, 0);
} }
void doubleAction(void* state) { void doubleAction(void *state){
GameState* game_state = state; GameState *game_state = state;
if(!game_state->doubled && game_state->player_score >= game_state->settings.round_price) { if (!game_state->doubled && game_state->player_score >= game_state->settings.round_price) {
game_state->player_score -= game_state->settings.round_price; game_state->player_score -= game_state->settings.round_price;
game_state->bet += game_state->settings.round_price; game_state->bet += game_state->settings.round_price;
game_state->doubled = true; game_state->doubled = true;
enqueue( enqueue(&(game_state->queue_state), game_state, drawPlayerCard, NULL, player_card_animation,
&(game_state->queue_state), game_state->settings.animation_duration);
game_state, game_state->player_cards[game_state->player_card_count] = game_state->deck.cards[game_state->deck.index];
drawPlayerCard,
NULL,
player_card_animation,
game_state->settings.animation_duration);
game_state->player_cards[game_state->player_card_count] =
game_state->deck.cards[game_state->deck.index];
uint8_t score = hand_count(game_state->player_cards, game_state->player_card_count + 1); uint8_t score = hand_count(game_state->player_cards, game_state->player_card_count + 1);
if(score > 21) { if (score > 21) {
enqueue( enqueue(&(game_state->queue_state), game_state, lose, NULL, to_bust_state,
&(game_state->queue_state), game_state->settings.message_duration);
game_state,
lose,
NULL,
to_bust_state,
game_state->settings.message_duration);
} else { } else {
enqueue( enqueue(&(game_state->queue_state), game_state, dealerTurn, NULL, to_dealer_turn,
&(game_state->queue_state), game_state->settings.message_duration);
game_state,
dealerTurn,
NULL,
to_dealer_turn,
game_state->settings.message_duration);
} }
set_menu_state(game_state->menu, 0, false); set_menu_state(game_state->menu, 0, false);
} }
} }
void hitAction(void* state) { void hitAction(void *state){
GameState* game_state = state; GameState *game_state = state;
enqueue( enqueue(&(game_state->queue_state), game_state, drawPlayerCard, NULL, player_card_animation,
&(game_state->queue_state), game_state->settings.animation_duration);
game_state,
drawPlayerCard,
NULL,
player_card_animation,
game_state->settings.animation_duration);
} }
void stayAction(void* state) { void stayAction(void *state){
GameState* game_state = state; GameState *game_state = state;
enqueue( enqueue(&(game_state->queue_state), game_state, dealerTurn, NULL, to_dealer_turn,
&(game_state->queue_state), game_state->settings.message_duration);
game_state,
dealerTurn,
NULL,
to_dealer_turn,
game_state->settings.message_duration);
} }
int32_t blackjack_app(void* p) { int32_t blackjack_app(void *p) {
UNUSED(p); UNUSED(p);
int32_t return_code = 0; int32_t return_code = 0;
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent)); FuriMessageQueue *event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
GameState* game_state = malloc(sizeof(GameState)); GameState *game_state = malloc(sizeof(GameState));
game_state->menu = malloc(sizeof(Menu)); game_state->menu= malloc(sizeof(Menu));
game_state->menu->menu_width = 40; game_state->menu->menu_width=40;
init(game_state); init(game_state);
add_menu(game_state->menu, "Double", doubleAction); add_menu(game_state->menu, "Double", doubleAction);
add_menu(game_state->menu, "Hit", hitAction); add_menu(game_state->menu, "Hit", hitAction);
add_menu(game_state->menu, "Stay", stayAction); add_menu(game_state->menu, "Stay", stayAction);
set_card_graphics(&I_card_graphics);
game_state->state = GameStateStart; game_state->state = GameStateStart;
ValueMutex state_mutex; ValueMutex state_mutex;
if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) { if (!init_mutex(&state_mutex, game_state, sizeof(GameState))) {
FURI_LOG_E(APP_NAME, "cannot create mutex\r\n"); FURI_LOG_E(APP_NAME, "cannot create mutex\r\n");
return_code = 255; return_code = 255;
goto free_and_exit; goto free_and_exit;
} }
ViewPort* view_port = view_port_alloc(); ViewPort *view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, &state_mutex); view_port_draw_callback_set(view_port, render_callback, &state_mutex);
view_port_input_callback_set(view_port, input_callback, event_queue); view_port_input_callback_set(view_port, input_callback, event_queue);
FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue); FuriTimer *timer =
furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25); furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25);
Gui* gui = furi_record_open("gui"); Gui *gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen); gui_add_view_port(gui, view_port, GuiLayerFullscreen);
AppEvent event; AppEvent event;
for(bool processing = true; processing;) { for (bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
GameState* localstate = (GameState*)acquire_mutex_block(&state_mutex); GameState *localstate = (GameState *) acquire_mutex_block(&state_mutex);
if(event_status == FuriStatusOk) { if (event_status == FuriStatusOk) {
if(event.type == EventTypeKey) { if (event.type == EventTypeKey) {
if(event.input.type == InputTypePress) {
switch(event.input.key) { if (event.input.type == InputTypePress) {
case InputKeyUp: switch (event.input.key) {
localstate->selectDirection = DirectionUp; case InputKeyUp:
break; localstate->selectDirection = DirectionUp;
case InputKeyDown: break;
localstate->selectDirection = DirectionDown; case InputKeyDown:
break; localstate->selectDirection = DirectionDown;
case InputKeyRight: break;
localstate->selectDirection = DirectionRight; case InputKeyRight:
break; localstate->selectDirection = DirectionRight;
case InputKeyLeft: break;
localstate->selectDirection = DirectionLeft; case InputKeyLeft:
break; localstate->selectDirection = DirectionLeft;
case InputKeyBack: break;
if(localstate->state == GameStateSettings) { case InputKeyBack:
localstate->state = GameStateStart; if (localstate->state == GameStateSettings) {
save_settings(localstate->settings); localstate->state = GameStateStart;
} else save_settings(localstate->settings);
processing = false; } else
break; processing = false;
case InputKeyOk: break;
localstate->selectDirection = Select; case InputKeyOk:
break; localstate->selectDirection = Select;
default: break;
break; default:
break;
} }
} }
} else if(event.type == EventTypeTick) { } else if (event.type == EventTypeTick) {
tick(localstate); tick(localstate);
processing = localstate->processing; processing = localstate->processing;
} }
@@ -615,6 +550,7 @@ int32_t blackjack_app(void* p) {
release_mutex(&state_mutex, localstate); release_mutex(&state_mutex, localstate);
} }
furi_timer_free(timer); furi_timer_free(timer);
view_port_enabled_set(view_port, false); view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port); gui_remove_view_port(gui, view_port);
@@ -622,7 +558,8 @@ int32_t blackjack_app(void* p) {
view_port_free(view_port); view_port_free(view_port);
delete_mutex(&state_mutex); delete_mutex(&state_mutex);
free_and_exit: free_and_exit:
free(game_state->deck.cards);
free_menu(game_state->menu); free_menu(game_state->menu);
queue_clear(&(game_state->queue_state)); queue_clear(&(game_state->queue_state));
free(game_state); free(game_state);

View File

@@ -1,7 +1,7 @@
#include "card.h" #include "card.h"
#include "dml.h" #include "dml.h"
#include "ui.h"
#define CORNER_MARGIN 3
#define CARD_DRAW_X_START 108 #define CARD_DRAW_X_START 108
#define CARD_DRAW_Y_START 38 #define CARD_DRAW_Y_START 38
#define CARD_DRAW_X_SPACE 10 #define CARD_DRAW_X_SPACE 10
@@ -9,154 +9,145 @@
#define CARD_DRAW_X_OFFSET 4 #define CARD_DRAW_X_OFFSET 4
#define CARD_DRAW_FIRST_ROW_LENGTH 7 #define CARD_DRAW_FIRST_ROW_LENGTH 7
bool pips[4][49] = { uint8_t pips[4][3] = {
{//spades {21, 10, 7}, //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, {7, 10, 7}, //hearts
1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {0, 10, 7}, //diamonds
{ {14, 10, 7}, //clubs
//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, uint8_t letters[13][3] = {
1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, {0, 0, 5},
}, {5, 0, 5},
{//diamonds {10, 0, 5},
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, {15, 0, 5},
1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0}, {20, 0, 5},
{//clubs {25, 0, 5},
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, {30, 0, 5},
1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}}; {0, 5, 5},
{5, 5, 5},
{10, 5, 5},
{15, 5, 5},
{20, 5, 5},
{25, 5, 5},
};
//region Player card positions //region Player card positions
uint8_t playerCardPositions[22][4] = { uint8_t playerCardPositions[22][4] = {
//first row //first row
{108, 38}, {108, 38},
{98, 38}, {98, 38},
{88, 38}, {88, 38},
{78, 38}, {78, 38},
{68, 38}, {68, 38},
{58, 38}, {58, 38},
{48, 38}, {48, 38},
{38, 38}, {38, 38},
//second row //second row
{104, 26}, {104, 26},
{94, 26}, {94, 26},
{84, 26}, {84, 26},
{74, 26}, {74, 26},
{64, 26}, {64, 26},
{54, 26}, {54, 26},
{44, 26}, {44, 26},
//third row //third row
{99, 14}, {99, 14},
{89, 14}, {89, 14},
{79, 14}, {79, 14},
{69, 14}, {69, 14},
{59, 14}, {59, 14},
{49, 14}, {49, 14},
}; };
//endregion //endregion
Icon *card_graphics = NULL;
bool get_pip_pixel(uint8_t pip, uint8_t x, uint8_t y) { void set_card_graphics(const Icon *graphics) {
return pips[pip][x + y * 7]; card_graphics = (Icon *) graphics;
} }
void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas* const canvas) { void
canvas_set_color(canvas, ColorWhite); draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, bool inverted, Canvas *const canvas) {
canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT); DrawMode primary = inverted ? Black : White;
DrawMode secondary = inverted ? White : Black;
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, primary);
draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
canvas_set_color(canvas, ColorBlack); uint8_t *drawInfo = pips[pip];
canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT); uint8_t px = drawInfo[0], py = drawInfo[1], s = drawInfo[2];
uint8_t left = pos_x + CORNER_MARGIN; uint8_t left = pos_x + 2;
uint8_t right = (pos_x + CARD_WIDTH - CORNER_MARGIN - 7); uint8_t right = (pos_x + CARD_WIDTH - s - 2);
uint8_t top = pos_y + CORNER_MARGIN; uint8_t top = pos_y + 2;
uint8_t bottom = (pos_y + CARD_HEIGHT - CORNER_MARGIN - 7); uint8_t bottom = (pos_y + CARD_HEIGHT - s - 2);
for(uint8_t x = 0; x < 7; x++) { draw_icon_clip(canvas, card_graphics, right, top, px, py, s, s,
for(uint8_t y = 0; y < 7; y++) { secondary);
if(get_pip_pixel(pip, x, y)) { draw_icon_clip_flipped(canvas, card_graphics, left, bottom, px, py, s, s,
canvas_draw_dot(canvas, right + x + 1, top + y); secondary);
canvas_draw_dot(canvas, left + x - 1, bottom + y);
}
}
}
canvas_set_font(canvas, FontSecondary); drawInfo = letters[character];
char drawChar[3]; px = drawInfo[0], py = drawInfo[1], s = drawInfo[2];
if(character < 9) left = pos_x + 2;
snprintf(drawChar, sizeof(drawChar), "%i", character + 2); right = (pos_x + CARD_WIDTH - s - 2);
else { top = pos_y + 2;
if(character == 9) bottom = (pos_y + CARD_HEIGHT - s - 2);
snprintf(drawChar, sizeof(drawChar), "J");
else if(character == 10)
snprintf(drawChar, sizeof(drawChar), "Q");
else if(character == 11)
snprintf(drawChar, sizeof(drawChar), "K");
else if(character == 12)
snprintf(drawChar, sizeof(drawChar), "A");
}
canvas_set_font_direction(canvas, CanvasDirectionLeftToRight); draw_icon_clip(canvas, card_graphics, left, top + 1, px, py, s, s,
canvas_draw_str_aligned(canvas, left + 2, top + 3, AlignCenter, AlignCenter, drawChar); secondary);
draw_icon_clip_flipped(canvas, card_graphics, right, bottom - 1, px, py, s, s,
canvas_set_font_direction(canvas, CanvasDirectionRightToLeft); secondary);
//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);
} }
void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas) { void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas *const canvas) {
for(int i = count - 1; i >= 0; i--) { draw_card_at_colored(pos_x, pos_y, pip, character, false, canvas);
draw_card_at( }
playerCardPositions[i][0],
playerCardPositions[i][1], void draw_deck(const Card *cards, uint8_t count, Canvas *const canvas) {
cards[i].pip, for (int i = count - 1; i >= 0; i--) {
cards[i].character, draw_card_at(playerCardPositions[i][0], playerCardPositions[i][1], cards[i].pip, cards[i].character, canvas);
canvas);
} }
} }
Vector card_pos_at_index(uint8_t index) { Vector card_pos_at_index(uint8_t index) {
return (Vector){playerCardPositions[index][0], playerCardPositions[index][1]}; return (Vector) {
playerCardPositions[index][0],
playerCardPositions[index][1]
};
} }
void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas* const canvas) { void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas *const canvas) {
canvas_set_color(canvas, ColorWhite); draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, White);
canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT); draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
draw_icon_clip(canvas, card_graphics, pos_x + 1, pos_y + 1, 35, 0, 15, 21, Black);
canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT);
for(uint8_t x = 0; x < CARD_WIDTH - 2; x++) {
for(uint8_t y = 0; y < CARD_HEIGHT - 2; y++) {
if((x + y) % 2 == 1) {
canvas_draw_dot(canvas, pos_x + x + 1, pos_y + y + 1);
}
}
}
} }
void generate_deck(Deck* deck_ptr, uint8_t deck_count) { void generate_deck(Deck *deck_ptr, uint8_t deck_count) {
uint16_t counter = 0; uint16_t counter = 0;
deck_ptr->deck_count = deck_count; if (deck_ptr->cards == NULL) {
deck_ptr->cards = malloc(sizeof(Card) * 52 * deck_count); deck_ptr->deck_count = deck_count;
for(uint8_t deck = 0; deck < deck_count; deck++) { deck_ptr->card_count = deck_count * 52;
for(uint8_t pip = 0; pip < 4; pip++) { deck_ptr->cards = malloc(sizeof(Card) * deck_ptr->card_count);
for(uint8_t label = 0; label < 13; label++) { }
deck_ptr->cards[counter] = (Card){pip, label}; 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, false, false
};
counter++; counter++;
} }
} }
} }
} }
void shuffle_deck(Deck* deck_ptr) { void shuffle_deck(Deck *deck_ptr) {
srand(DWT->CYCCNT); srand(DWT->CYCCNT);
deck_ptr->index = 0; deck_ptr->index = 0;
int max = deck_ptr->deck_count * 52; int max = deck_ptr->deck_count * 52;
for(int i = 0; i < max; i++) { for (int i = 0; i < max; i++) {
int r = i + (rand() % (max - i)); int r = i + (rand() % (max - i));
Card tmp = deck_ptr->cards[i]; Card tmp = deck_ptr->cards[i];
deck_ptr->cards[i] = deck_ptr->cards[r]; deck_ptr->cards[i] = deck_ptr->cards[r];
@@ -164,53 +155,176 @@ void shuffle_deck(Deck* deck_ptr) {
} }
} }
uint8_t hand_count(const Card* cards, uint8_t count) { uint8_t hand_count(const Card *cards, uint8_t count) {
uint8_t aceCount = 0; uint8_t aceCount = 0;
uint8_t score = 0; uint8_t score = 0;
for(uint8_t i = 0; i < count; i++) { for (uint8_t i = 0; i < count; i++) {
if(cards[i].character == 12) if (cards[i].character == 12)
aceCount++; aceCount++;
else { else {
if(cards[i].character > 8) if (cards[i].character > 8)
score += 10; score += 10;
else else
score += cards[i].character + 2; score += cards[i].character + 2;
} }
} }
for(uint8_t i = 0; i < aceCount; i++) { for (uint8_t i = 0; i < aceCount; i++) {
if((score + 11) <= 21) if ((score + 11) <= 21) score += 11;
score += 11; else score++;
else
score++;
} }
return score; return score;
} }
void draw_card_animation( void draw_card_animation(Card animatingCard, Vector from, Vector control, Vector to, float t, bool extra_margin,
Card animatingCard, Canvas *const canvas) {
Vector from,
Vector control,
Vector to,
float t,
bool extra_margin,
Canvas* const canvas) {
float time = t; float time = t;
if(extra_margin) { if (extra_margin) {
time += 0.2; time += 0.2;
} }
Vector currentPos = quadratic_2d(from, control, to, time); Vector currentPos = quadratic_2d(from, control, to, time);
if(t > 1) { if (t > 1) {
draw_card_at( draw_card_at(currentPos.x, currentPos.y, animatingCard.pip,
currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas); animatingCard.character, canvas);
} else { } else {
if(t < 0.5) if (t < 0.5)
draw_card_back_at(currentPos.x, currentPos.y, canvas); draw_card_back_at(currentPos.x, currentPos.y, canvas);
else else
draw_card_at( draw_card_at(currentPos.x, currentPos.y, animatingCard.pip,
currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas); animatingCard.character, canvas);
}
}
void init_hand(Hand *hand_ptr, uint8_t count) {
hand_ptr->cards = malloc(sizeof(Card) * count);
hand_ptr->index = 0;
hand_ptr->max = count;
}
void free_hand(Hand *hand_ptr) {
FURI_LOG_D("CARD", "Freeing hand");
free(hand_ptr->cards);
}
void add_to_hand(Hand *hand_ptr, Card card) {
FURI_LOG_D("CARD", "Adding to hand");
if (hand_ptr->index < hand_ptr->max) {
hand_ptr->cards[hand_ptr->index] = card;
hand_ptr->index++;
}
}
void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas *const canvas) {
if (highlighted) {
draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
draw_rounded_box_frame(canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White);
} else {
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
draw_rounded_box_frame(canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White);
}
}
int first_non_flipped_card(Hand hand) {
for (int i = 0; i < hand.index; i++) {
if (!hand.cards[i].flipped) {
return i;
}
}
return hand.index;
}
void draw_hand_column(Hand hand, int16_t pos_x, int16_t pos_y, int8_t highlight, Canvas *const canvas) {
if (hand.index == 0) {
draw_card_space(pos_x, pos_y, highlight > 0, canvas);
if(highlight==0)
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT,
Inverse);
return;
}
int loopEnd = hand.index;
int hStart = max(loopEnd-4, 0);
int pos = 0;
int first= first_non_flipped_card(hand);
bool wastop=false;
if(first>=0 && first<=hStart && highlight!=first){
if(first>0){
draw_card_back_at(pos_x, pos_y + pos, canvas);
pos+=4;
hStart++;
wastop=true;
}
draw_card_at_colored(pos_x, pos_y + pos, hand.cards[first].pip, hand.cards[first].character,
false,
canvas);
pos+=8;
hStart++;
}
if(hStart>highlight && highlight>=0){
if(!wastop && first>0){
draw_card_back_at(pos_x, pos_y + pos, canvas);
pos+=4;
hStart++;
}
draw_card_at_colored(pos_x, pos_y + pos, hand.cards[highlight].pip, hand.cards[highlight].character,
true,
canvas);
pos+=8;
hStart++;
}
for (int i = hStart; i < loopEnd; i++, pos+=4) {
if (hand.cards[i].flipped) {
draw_card_back_at(pos_x, pos_y + pos, canvas);
if(i==highlight)
draw_rounded_box(canvas, pos_x+1, pos_y + pos+1, CARD_WIDTH - 2, CARD_HEIGHT - 2,
Inverse);
} else {
draw_card_at_colored(pos_x, pos_y + pos, hand.cards[i].pip, hand.cards[i].character,
(i == highlight),
canvas);
if(i == highlight || i==first) pos+=4;
}
}
}
Card remove_from_deck(uint16_t index, Deck *deck) {
FURI_LOG_D("CARD", "Removing from deck");
Card result = {0, 0, true, false};
if (deck->card_count > 0) {
deck->card_count--;
for (int i = 0, curr_index = 0; i <= deck->card_count; i++) {
if (i != index) {
deck->cards[curr_index] = deck->cards[i];
curr_index++;
} else {
result = deck->cards[i];
}
}
if (deck->index >= 0) {
deck->index--;
}
}
return result;
}
void extract_hand_region(Hand *hand, Hand *to, uint8_t start_index) {
FURI_LOG_D("CARD", "Extracting hand region");
if (start_index >= hand->index) return;
for (uint8_t i = start_index; i < hand->index; i++) {
add_to_hand(to, hand->cards[i]);
}
hand->index = start_index;
}
void add_hand_region(Hand *to, Hand *from) {
FURI_LOG_D("CARD", "Adding hand region");
if ((to->index + from->index) <= to->max) {
for (int i = 0; i < from->index; i++) {
add_to_hand(to, from->cards[i]);
}
} }
} }

View File

@@ -5,24 +5,35 @@
#include <stdlib.h> #include <stdlib.h>
#include "dml.h" #include "dml.h"
#define CARD_HEIGHT 24 #define CARD_HEIGHT 23
#define CARD_HALF_HEIGHT CARD_HEIGHT / 2 #define CARD_HALF_HEIGHT 11
#define CARD_WIDTH 18 #define CARD_WIDTH 17
#define CARD_HALF_WIDTH CARD_WIDTH / 2 #define CARD_HALF_WIDTH 8
//region types //region types
typedef struct { typedef struct {
uint8_t pip; //Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs uint8_t pip; //Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs
uint8_t character; //Card letter [0-12], 0 means 2, 12 is Ace uint8_t character; //Card letter [0-12], 0 means 2, 12 is Ace
bool disabled;
bool flipped;
} Card; } Card;
typedef struct { typedef struct {
uint8_t deck_count; //Number of decks used uint8_t deck_count; //Number of decks used
Card* cards; //Cards in the deck Card *cards; //Cards in the deck
int index; //Card index (to know where we at in the deck) int card_count;
int index; //Card index (to know where we at in the deck)
} Deck; } Deck;
typedef struct {
Card *cards; //Cards in the deck
uint8_t index; //Current index
uint8_t max; //How many cards we want to store
} Hand;
//endregion //endregion
void set_card_graphics(const Icon *graphics);
/** /**
* Gets card coordinates at the index (range: 0-20). * Gets card coordinates at the index (range: 0-20).
* *
@@ -40,7 +51,20 @@ Vector card_pos_at_index(uint8_t index);
* @param character Letter [0-12] 0 is 2, 12 is A * @param character Letter [0-12] 0 is 2, 12 is A
* @param canvas Pointer to Flipper's canvas object * @param canvas Pointer to Flipper's canvas object
*/ */
void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas* const canvas); void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas *const canvas);
/**
* Draws card at a given coordinate (top-left corner)
*
* @param pos_x X position
* @param pos_y Y position
* @param pip Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs
* @param character Letter [0-12] 0 is 2, 12 is A
* @param inverted Invert colors
* @param canvas Pointer to Flipper's canvas object
*/
void
draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, bool inverted, Canvas *const canvas);
/** /**
* Draws 'count' cards at the bottom right corner * Draws 'count' cards at the bottom right corner
@@ -49,7 +73,7 @@ void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Ca
* @param count Count of cards * @param count Count of cards
* @param canvas Pointer to Flipper's canvas object * @param canvas Pointer to Flipper's canvas object
*/ */
void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas); void draw_deck(const Card *cards, uint8_t count, Canvas *const canvas);
/** /**
* Draws card back at a given coordinate (top-left corner) * Draws card back at a given coordinate (top-left corner)
@@ -58,7 +82,7 @@ void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas);
* @param pos_y Y coordinate * @param pos_y Y coordinate
* @param canvas Pointer to Flipper's canvas object * @param canvas Pointer to Flipper's canvas object
*/ */
void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas* const canvas); void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas *const canvas);
/** /**
* Generates the deck * Generates the deck
@@ -66,14 +90,14 @@ void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas* const canvas);
* @param deck_ptr Pointer to the deck * @param deck_ptr Pointer to the deck
* @param deck_count Number of decks * @param deck_count Number of decks
*/ */
void generate_deck(Deck* deck_ptr, uint8_t deck_count); void generate_deck(Deck *deck_ptr, uint8_t deck_count);
/** /**
* Shuffles the deck * Shuffles the deck
* *
* @param deck_ptr Pointer to the deck * @param deck_ptr Pointer to the deck
*/ */
void shuffle_deck(Deck* deck_ptr); void shuffle_deck(Deck *deck_ptr);
/** /**
* Calculates the hand count for blackjack * Calculates the hand count for blackjack
@@ -82,7 +106,7 @@ void shuffle_deck(Deck* deck_ptr);
* @param count Count of cards * @param count Count of cards
* @return Hand value * @return Hand value
*/ */
uint8_t hand_count(const Card* cards, uint8_t count); uint8_t hand_count(const Card *cards, uint8_t count);
/** /**
* Draws card animation * Draws card animation
@@ -95,11 +119,59 @@ uint8_t hand_count(const Card* cards, uint8_t count);
* @param extra_margin Use extra margin at the end (arrives 0.2 unit before the end so it can stay there a bit) * @param extra_margin Use extra margin at the end (arrives 0.2 unit before the end so it can stay there a bit)
* @param canvas Pointer to Flipper's canvas object * @param canvas Pointer to Flipper's canvas object
*/ */
void draw_card_animation( void draw_card_animation(Card animatingCard, Vector from, Vector control, Vector to, float t, bool extra_margin,
Card animatingCard, Canvas *const canvas);
Vector from,
Vector control, /**
Vector to, * Init hand pointer
float t, * @param hand_ptr Pointer to hand
bool extra_margin, * @param count Number of cards we want to store
Canvas* const canvas); */
void init_hand(Hand *hand_ptr, uint8_t count);
/**
* Free hand resources
* @param hand_ptr Pointer to hand
*/
void free_hand(Hand *hand_ptr);
/**
* Add card to hand
* @param hand_ptr Pointer to hand
* @param card Card to add
*/
void add_to_hand(Hand *hand_ptr, Card card);
/**
* Draw card placement position at coordinate
* @param pos_x X coordinate
* @param pos_y Y coordinate
* @param highlighted Apply highlight effect
* @param canvas Canvas object
*/
void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas *const canvas);
/**
* Draws a column of card, displaying the last [max_cards] cards on the list
* @param hand Hand object
* @param pos_x X coordinate to draw
* @param pos_y Y coordinate to draw
* @param highlight Index to highlight, negative means no highlight
* @param canvas Canvas object
*/
void
draw_hand_column(Hand hand, int16_t pos_x, int16_t pos_y, int8_t highlight, Canvas *const canvas);
/**
* Removes a card from the deck (Be aware, if you remove the first item, the deck index will be at -1 so you have to handle that)
* @param index Index to remove
* @param deck Deck reference
* @return The removed card
*/
Card remove_from_deck(uint16_t index, Deck *deck);
int first_non_flipped_card(Hand hand);
void extract_hand_region(Hand *hand, Hand *to, uint8_t start_index);
void add_hand_region(Hand *to, Hand *from);

View File

@@ -2,40 +2,47 @@
#include <math.h> #include <math.h>
float lerp(float v0, float v1, float t) { float lerp(float v0, float v1, float t) {
if(t > 1) return v1; if (t > 1) return v1;
return (1 - t) * v0 + t * v1; return (1 - t) * v0 + t * v1;
} }
Vector lerp_2d(Vector start, Vector end, float t) { Vector lerp_2d(Vector start, Vector end, float t) {
return (Vector){ return (Vector) {
lerp(start.x, end.x, t), lerp(start.x, end.x, t),
lerp(start.y, end.y, t), lerp(start.y, end.y, t),
}; };
} }
Vector quadratic_2d(Vector start, Vector control, Vector end, float t) { Vector quadratic_2d(Vector start, Vector control, Vector end, float t) {
return lerp_2d(lerp_2d(start, control, t), lerp_2d(control, end, t), t); return lerp_2d(
lerp_2d(start, control, t),
lerp_2d(control, end, t),
t
);
} }
Vector vector_add(Vector a, Vector b) { Vector vector_add(Vector a, Vector b) {
return (Vector){a.x + b.x, a.y + b.y}; return (Vector) {a.x + b.x, a.y + b.y};
} }
Vector vector_sub(Vector a, Vector b) { Vector vector_sub(Vector a, Vector b) {
return (Vector){a.x - b.x, a.y - b.y}; return (Vector) {a.x - b.x, a.y - b.y};
} }
Vector vector_mul_components(Vector a, Vector b) { Vector vector_mul_components(Vector a, Vector b) {
return (Vector){a.x * b.x, a.y * b.y}; return (Vector) {a.x * b.x, a.y * b.y};
} }
Vector vector_div_components(Vector a, Vector b) { Vector vector_div_components(Vector a, Vector b) {
return (Vector){a.x / b.x, a.y / b.y}; return (Vector) {a.x / b.x, a.y / b.y};
} }
Vector vector_normalized(Vector a) { Vector vector_normalized(Vector a) {
float length = vector_magnitude(a); float length = vector_magnitude(a);
return (Vector){a.x / length, a.y / length}; return (Vector) {
a.x / length,
a.y / length
};
} }
float vector_magnitude(Vector a) { float vector_magnitude(Vector a) {

View File

@@ -9,6 +9,10 @@ typedef struct {
float y; float y;
} Vector; } Vector;
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
/** /**
* Lerp function * Lerp function
* *

View File

@@ -1,53 +1,43 @@
#include "menu.h" #include "menu.h"
void add_menu(Menu* menu, const char* name, void (*callback)(void*)) { void add_menu(Menu *menu, const char *name, void (*callback)(void *)) {
MenuItem* items = menu->items; MenuItem *items = menu->items;
menu->items = malloc(sizeof(MenuItem) * (menu->menu_count + 1)); menu->items = malloc(sizeof(MenuItem) * (menu->menu_count + 1));
for(uint8_t i = 0; i < menu->menu_count; i++) { for (uint8_t i = 0; i < menu->menu_count; i++) {
menu->items[i] = items[i]; menu->items[i] = items[i];
} }
free(items); free(items);
menu->items[menu->menu_count] = (MenuItem){name, true, callback}; menu->items[menu->menu_count] = (MenuItem) {name, true, callback};
menu->menu_count++; menu->menu_count++;
} }
void free_menu(Menu* menu) { void free_menu(Menu *menu) {
free(menu->items); free(menu->items);
free(menu); free(menu);
} }
void set_menu_state(Menu* menu, uint8_t index, bool state) { void set_menu_state(Menu *menu, uint8_t index, bool state) {
if(menu->menu_count > index) { if (menu->menu_count > index) {
menu->items[index].enabled = state; menu->items[index].enabled = state;
} }
if(!state && menu->current_menu == index) move_menu(menu, 1); if(!state && menu->current_menu==index)
move_menu(menu, 1);
} }
void move_menu(Menu* menu, int8_t direction) { void move_menu(Menu *menu, int8_t direction) {
if(!menu->enabled) return; if (!menu->enabled) return;
int max = menu->menu_count; int max = menu->menu_count;
for(int8_t i = 0; i < max; i++) { for (int8_t i = 0; i < max; i++) {
FURI_LOG_D( FURI_LOG_D("MENU", "Iteration %i, current %i, direction %i, state %i", i, menu->current_menu,direction,menu->items[menu->current_menu].enabled?1:0);
"MENU", if (direction < 0 && menu->current_menu == 0) {
"Iteration %i, current %i, direction %i, state %i",
i,
menu->current_menu,
direction,
menu->items[menu->current_menu].enabled ? 1 : 0);
if(direction < 0 && menu->current_menu == 0) {
menu->current_menu = menu->menu_count - 1; menu->current_menu = menu->menu_count - 1;
} else { } else {
menu->current_menu = (menu->current_menu + direction) % menu->menu_count; menu->current_menu = (menu->current_menu + direction) % menu->menu_count;
} }
FURI_LOG_D( FURI_LOG_D("MENU", "After process current %i, direction %i, state %i", menu->current_menu,direction,menu->items[menu->current_menu].enabled?1:0);
"MENU", if (menu->items[menu->current_menu].enabled) {
"After process current %i, direction %i, state %i",
menu->current_menu,
direction,
menu->items[menu->current_menu].enabled ? 1 : 0);
if(menu->items[menu->current_menu].enabled) {
FURI_LOG_D("MENU", "Next menu %i", menu->current_menu); FURI_LOG_D("MENU", "Next menu %i", menu->current_menu);
return; return;
} }
@@ -56,48 +46,44 @@ void move_menu(Menu* menu, int8_t direction) {
menu->enabled = false; menu->enabled = false;
} }
void activate_menu(Menu* menu, void* state) { void activate_menu(Menu *menu, void *state) {
if(!menu->enabled) return; if (!menu->enabled) return;
menu->items[menu->current_menu].callback(state); menu->items[menu->current_menu].callback(state);
} }
void render_menu(Menu* menu, Canvas* canvas, uint8_t pos_x, uint8_t pos_y) { void render_menu(Menu *menu, Canvas *canvas, uint8_t pos_x, uint8_t pos_y) {
if(!menu->enabled) return; if (!menu->enabled) return;
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2); canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2);
uint8_t w = pos_x + menu->menu_width; uint8_t w = pos_x+menu->menu_width;
uint8_t h = pos_y + 10; uint8_t h = pos_y+10;
uint8_t p1x = pos_x + 2; uint8_t p1x = pos_x+2;
uint8_t p2x = pos_x + menu->menu_width - 2; uint8_t p2x = pos_x+menu->menu_width-2;
uint8_t p1y = pos_y + 2; uint8_t p1y = pos_y+2;
uint8_t p2y = pos_y + 8; uint8_t p2y = pos_y+8;
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
canvas_draw_line(canvas, p1x, pos_y, p2x, pos_y); canvas_draw_line(canvas, p1x, pos_y, p2x, pos_y);
canvas_draw_line(canvas, p1x, h, p2x, h); canvas_draw_line(canvas, p1x, h, p2x, h);
canvas_draw_line(canvas, pos_x, p1y, pos_x, p2y); canvas_draw_line(canvas, pos_x, p1y, pos_x, p2y);
canvas_draw_line(canvas, w, p1y, w, p2y); canvas_draw_line(canvas, w, p1y, w, p2y);
canvas_draw_dot(canvas, pos_x + 1, pos_y + 1); canvas_draw_dot(canvas, pos_x+1, pos_y+1);
canvas_draw_dot(canvas, w - 1, pos_y + 1); canvas_draw_dot(canvas, w-1, pos_y+1);
canvas_draw_dot(canvas, w - 1, h - 1); canvas_draw_dot(canvas, w-1, h-1);
canvas_draw_dot(canvas, pos_x + 1, h - 1); canvas_draw_dot(canvas, pos_x+1, h-1);
// canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2); // canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2);
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned( canvas_draw_str_aligned(canvas, pos_x + menu->menu_width / 2, pos_y + 6, AlignCenter, AlignCenter,
canvas, menu->items[menu->current_menu].name);
pos_x + menu->menu_width / 2,
pos_y + 6,
AlignCenter,
AlignCenter,
menu->items[menu->current_menu].name);
//9*5 //9*5
int center = pos_x + menu->menu_width / 2; int center = pos_x + menu->menu_width / 2;
for(uint8_t i = 0; i < 4; i++) { for(uint8_t i=0;i<4;i++){
for(int8_t j = -i; j <= i; j++) { for(int8_t j = -i; j<=i;j++){
canvas_draw_dot(canvas, center + j, pos_y - 4 + i); canvas_draw_dot(canvas, center+j, pos_y-4+i);
canvas_draw_dot(canvas, center + j, pos_y + 14 - i); canvas_draw_dot(canvas, center+j, pos_y+14-i);
} }
} }
} }

View File

@@ -4,19 +4,18 @@
#include <gui/gui.h> #include <gui/gui.h>
typedef struct { typedef struct {
const char* name; //Name of the menu const char *name; //Name of the menu
bool enabled; //Is the menu item enabled (it will not render, you cannot select it) bool enabled; //Is the menu item enabled (it will not render, you cannot select it)
void (*callback)( void (*callback)(void *state); //Callback for when the activate_menu is called while this menu is selected
void* state); //Callback for when the activate_menu is called while this menu is selected
} MenuItem; } MenuItem;
typedef struct { typedef struct {
MenuItem* items; //list of menu items MenuItem *items; //list of menu items
uint8_t menu_count; //count of menu items (do not change) uint8_t menu_count; //count of menu items (do not change)
uint8_t current_menu; //currently selected menu item uint8_t current_menu; //currently selected menu item
uint8_t menu_width; //width of the menu uint8_t menu_width; //width of the menu
bool enabled; //is the menu enabled (it will not render and accept events when disabled) bool enabled; //is the menu enabled (it will not render and accept events when disabled)
} Menu; } Menu;
/** /**
@@ -24,7 +23,7 @@ typedef struct {
* *
* @param menu Pointer of the menu to clean up * @param menu Pointer of the menu to clean up
*/ */
void free_menu(Menu* menu); void free_menu(Menu *menu);
/** /**
* Add a new menu item * Add a new menu item
@@ -33,7 +32,7 @@ void free_menu(Menu* menu);
* @param name Name of the menu item * @param name Name of the menu item
* @param callback Callback called on activation * @param callback Callback called on activation
*/ */
void add_menu(Menu* menu, const char* name, void (*callback)(void*)); void add_menu(Menu *menu, const char *name, void (*callback)(void *));
/** /**
* Setting menu item to be enabled/disabled * Setting menu item to be enabled/disabled
@@ -42,7 +41,7 @@ void add_menu(Menu* menu, const char* name, void (*callback)(void*));
* @param index Menu index to set * @param index Menu index to set
* @param state Enabled (true), Disabled(false) * @param state Enabled (true), Disabled(false)
*/ */
void set_menu_state(Menu* menu, uint8_t index, bool state); void set_menu_state(Menu *menu, uint8_t index, bool state);
/** /**
* Moves selection up or down * Moves selection up or down
@@ -50,7 +49,7 @@ void set_menu_state(Menu* menu, uint8_t index, bool state);
* @param menu Pointer of the menu * @param menu Pointer of the menu
* @param direction Direction to move -1 down, 1 up * @param direction Direction to move -1 down, 1 up
*/ */
void move_menu(Menu* menu, int8_t direction); void move_menu(Menu *menu, int8_t direction);
/** /**
* Triggers the current menu callback * Triggers the current menu callback
@@ -58,7 +57,7 @@ void move_menu(Menu* menu, int8_t direction);
* @param menu Pointer of the menu * @param menu Pointer of the menu
* @param state Usually your application state * @param state Usually your application state
*/ */
void activate_menu(Menu* menu, void* state); void activate_menu(Menu *menu, void *state);
/** /**
* Renders the menu at a coordinate (call it in your render function). * Renders the menu at a coordinate (call it in your render function).
@@ -74,4 +73,4 @@ void activate_menu(Menu* menu, void* state);
* @param pos_x X position to draw * @param pos_x X position to draw
* @param pos_y Y position to draw * @param pos_y Y position to draw
*/ */
void render_menu(Menu* menu, Canvas* canvas, uint8_t pos_x, uint8_t pos_y); void render_menu(Menu *menu, Canvas *canvas, uint8_t pos_x, uint8_t pos_y);

View File

@@ -1,14 +1,15 @@
#include "queue.h" #include "queue.h"
void render_queue(const QueueState* queue_state, const void* app_state, Canvas* const canvas) {
if(queue_state->current != NULL && queue_state->current->render != NULL) void render_queue(const QueueState *queue_state, const void *app_state, Canvas *const canvas) {
((QueueItem*)queue_state->current)->render(app_state, canvas); if (queue_state->current != NULL && queue_state->current->render != NULL)
((QueueItem *) queue_state->current)->render(app_state, canvas);
} }
bool run_queue(QueueState* queue_state, void* app_state) { bool run_queue(QueueState *queue_state, void *app_state) {
if(queue_state->current != NULL) { if (queue_state->current != NULL) {
queue_state->running = true; queue_state->running = true;
if((furi_get_tick() - queue_state->start) >= queue_state->current->duration) if ((furi_get_tick() - queue_state->start) >= queue_state->current->duration)
dequeue(queue_state, app_state); dequeue(queue_state, app_state);
return true; return true;
@@ -16,48 +17,44 @@ bool run_queue(QueueState* queue_state, void* app_state) {
return false; return false;
} }
void dequeue(QueueState* queue_state, void* app_state) { void dequeue(QueueState *queue_state, void *app_state) {
((QueueItem*)queue_state->current)->callback(app_state); ((QueueItem *) queue_state->current)->callback(app_state);
QueueItem* f = queue_state->current; QueueItem *f = queue_state->current;
queue_state->current = f->next; queue_state->current = f->next;
free(f); free(f);
if(queue_state->current != NULL) { if (queue_state->current != NULL) {
if(queue_state->current->start != NULL) queue_state->current->start(app_state); if (queue_state->current->start != NULL)
queue_state->current->start(app_state);
queue_state->start = furi_get_tick(); queue_state->start = furi_get_tick();
} else { }else{
queue_state->running = false; queue_state->running = false;
} }
} }
void queue_clear(QueueState* queue_state) { void queue_clear(QueueState *queue_state) {
queue_state->running = false; queue_state->running = false;
QueueItem* curr = queue_state->current; QueueItem *curr = queue_state->current;
while(curr != NULL) { while (curr != NULL) {
QueueItem* f = curr; QueueItem *f = curr;
curr = curr->next; curr = curr->next;
free(f); free(f);
} }
} }
void enqueue( void enqueue(QueueState *queue_state, void *app_state,
QueueState* queue_state, void(*done)(void *state), void(*start)(void *state),
void* app_state, void (*render)(const void *state, Canvas *const canvas), uint32_t duration) {
void (*done)(void* state), QueueItem *next;
void (*start)(void* state), if (queue_state->current == NULL) {
void (*render)(const void* state, Canvas* const canvas),
uint32_t duration) {
QueueItem* next;
if(queue_state->current == NULL) {
queue_state->start = furi_get_tick(); queue_state->start = furi_get_tick();
queue_state->current = malloc(sizeof(QueueItem)); queue_state->current = malloc(sizeof(QueueItem));
next = queue_state->current; next = queue_state->current;
if(next->start != NULL) next->start(app_state); if (next->start != NULL)
next->start(app_state);
} else { } else {
next = queue_state->current; next = queue_state->current;
while(next->next != NULL) { while (next->next != NULL) { next = (QueueItem *) (next->next); }
next = (QueueItem*)(next->next);
}
next->next = malloc(sizeof(QueueItem)); next->next = malloc(sizeof(QueueItem));
next = next->next; next = next->next;
} }

View File

@@ -4,19 +4,17 @@
#include <furi.h> #include <furi.h>
typedef struct { typedef struct {
void (*callback)(void* state); //Callback for when the item is dequeued void (*callback)(void *state); //Callback for when the item is dequeued
void (*render)( void (*render)(const void *state, Canvas *const canvas); //Callback for the rendering loop while this item is running
const void* state, void (*start)(void *state); //Callback when this item is started running
Canvas* const canvas); //Callback for the rendering loop while this item is running void *next; //Pointer to the next item
void (*start)(void* state); //Callback when this item is started running uint32_t duration; //duration of the item
void* next; //Pointer to the next item
uint32_t duration; //duration of the item
} QueueItem; } QueueItem;
typedef struct { typedef struct {
unsigned int start; //current queue item start time unsigned int start; //current queue item start time
QueueItem* current; //current queue item QueueItem *current; //current queue item
bool running; //is the queue running bool running; //is the queue running
} QueueState; } QueueState;
/** /**
@@ -29,19 +27,15 @@ typedef struct {
* @param render Callback to render loop if needed * @param render Callback to render loop if needed
* @param duration Length of the item * @param duration Length of the item
*/ */
void enqueue( void enqueue(QueueState *queue_state, void *app_state,
QueueState* queue_state, void(*done)(void *state), void(*start)(void *state),
void* app_state, void (*render)(const void *state, Canvas *const canvas), uint32_t duration);
void (*done)(void* state),
void (*start)(void* state),
void (*render)(const void* state, Canvas* const canvas),
uint32_t duration);
/** /**
* Clears all queue items * Clears all queue items
* *
* @param queue_state The queue state pointer * @param queue_state The queue state pointer
*/ */
void queue_clear(QueueState* queue_state); void queue_clear(QueueState *queue_state);
/** /**
* Dequeues the active queue item. Usually you don't need to call it directly. * Dequeues the active queue item. Usually you don't need to call it directly.
@@ -49,7 +43,7 @@ void queue_clear(QueueState* queue_state);
* @param queue_state The queue state pointer * @param queue_state The queue state pointer
* @param app_state Your application state * @param app_state Your application state
*/ */
void dequeue(QueueState* queue_state, void* app_state); void dequeue(QueueState *queue_state, void *app_state);
/** /**
* Runs the queue logic (place it in your tick function) * Runs the queue logic (place it in your tick function)
@@ -58,7 +52,7 @@ void dequeue(QueueState* queue_state, void* app_state);
* @param app_state Your application state * @param app_state Your application state
* @return FALSE when there is nothing to run, TRUE otherwise * @return FALSE when there is nothing to run, TRUE otherwise
*/ */
bool run_queue(QueueState* queue_state, void* app_state); bool run_queue(QueueState *queue_state, void *app_state);
/** /**
* Calls the currently active queue items render callback (if there is any) * Calls the currently active queue items render callback (if there is any)
@@ -67,4 +61,4 @@ bool run_queue(QueueState* queue_state, void* app_state);
* @param app_state Your application state * @param app_state Your application state
* @param canvas Pointer to Flipper's canvas object * @param canvas Pointer to Flipper's canvas object
*/ */
void render_queue(const QueueState* queue_state, const void* app_state, Canvas* const canvas); void render_queue(const QueueState *queue_state, const void *app_state, Canvas *const canvas);

View File

@@ -0,0 +1,216 @@
#include "ui.h"
#include <gui/canvas_i.h>
#include <u8g2_glue.h>
#include <gui/icon_animation_i.h>
#include <gui/icon.h>
#include <gui/icon_i.h>
#include <furi_hal.h>
TileMap *tileMap;
uint8_t tileMapCount = 0;
void ui_cleanup() {
if (tileMap != NULL) {
for (uint8_t i = 0; i < tileMapCount; i++) {
if (tileMap[i].data != NULL)
free(tileMap[i].data);
}
free(tileMap);
}
}
void add_new_tilemap(uint8_t *data, unsigned long iconId) {
TileMap *old = tileMap;
tileMapCount++;
tileMap = malloc(sizeof(TileMap) * tileMapCount);
if (tileMapCount > 1) {
for (uint8_t i = 0; i < tileMapCount; i++)
tileMap[i] = old[i];
}
tileMap[tileMapCount - 1] = (TileMap) {data, iconId};
}
uint8_t *get_tilemap(unsigned long icon_id) {
for (uint8_t i = 0; i < tileMapCount; i++) {
if (tileMap[i].iconId == icon_id)
return tileMap[i].data;
}
return NULL;
}
uint32_t pixel_index(uint8_t x, uint8_t y) {
return y * SCREEN_WIDTH + x;
}
bool in_screen(int16_t x, int16_t y) {
return x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT;
}
unsigned flipBit(uint8_t x, uint8_t bit) {
return x ^ (1 << bit);
}
unsigned setBit(uint8_t x, uint8_t bit) {
return x | (1 << bit);
}
unsigned unsetBit(uint8_t x, uint8_t bit) {
return x & ~(1 << bit);
}
bool test_pixel(uint8_t *data, uint8_t x, uint8_t y, uint8_t w) {
uint8_t current_bit = (y % 8);
uint8_t current_row = ((y - current_bit) / 8);
uint8_t current_value = data[current_row * w + x];
return current_value & (1 << current_bit);
}
uint8_t* get_buffer(Canvas *const canvas){
return canvas->fb.tile_buf_ptr;
// return canvas_get_buffer(canvas);
}
uint8_t* make_buffer(){
return malloc(sizeof(uint8_t) * 8 * 128);
}
void clone_buffer(uint8_t* canvas, uint8_t* data){
for(int i=0;i<1024;i++){
data[i]= canvas[i];
}
}
bool read_pixel(Canvas *const canvas, int16_t x, int16_t y) {
if (in_screen(x, y)) {
return test_pixel(get_buffer(canvas), x, y, SCREEN_WIDTH);
}
return false;
}
void set_pixel(Canvas *const canvas, int16_t x, int16_t y, DrawMode draw_mode) {
if (in_screen(x, y)) {
uint8_t current_bit = (y % 8);
uint8_t current_row = ((y - current_bit) / 8);
uint32_t i = pixel_index(x, current_row);
uint8_t* buffer = get_buffer(canvas);
uint8_t current_value = buffer[i];
if (draw_mode == Inverse) {
buffer[i] = flipBit(current_value, current_bit);
} else {
if (draw_mode == White) {
buffer[i] = unsetBit(current_value, current_bit);
} else {
buffer[i] = setBit(current_value, current_bit);
}
}
}
}
void draw_line(Canvas *const canvas, int16_t x1, int16_t y1, int16_t x2, int16_t y2, DrawMode draw_mode) {
for (int16_t x = x2; x >= x1; x--) {
for (int16_t y = y2; y >= y1; y--) {
set_pixel(canvas, x, y, draw_mode);
}
}
}
void draw_rounded_box_frame(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode draw_mode) {
int16_t xMinCorner = x + 1;
int16_t xMax = x + w - 1;
int16_t xMaxCorner = x + w - 2;
int16_t yMinCorner = y + 1;
int16_t yMax = y + h - 1;
int16_t yMaxCorner = y + h - 2;
draw_line(canvas, xMinCorner, y, xMaxCorner, y, draw_mode);
draw_line(canvas, xMinCorner, yMax, xMaxCorner, yMax, draw_mode);
draw_line(canvas, x, yMinCorner, x, yMaxCorner, draw_mode);
draw_line(canvas, xMax, yMinCorner, xMax, yMaxCorner, draw_mode);
}
void draw_rounded_box(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode draw_mode) {
for (int16_t o = w - 2; o >= 1; o--) {
for (int16_t p = h - 2; p >= 1; p--) {
set_pixel(canvas, x + o, y + p, draw_mode);
}
}
draw_rounded_box_frame(canvas, x, y, w, h, draw_mode);
}
void invert_shape(Canvas *const canvas, uint8_t *data, int16_t x, int16_t y, uint8_t w, uint8_t h) {
draw_pixels(canvas, data, x, y, w, h, Inverse);
}
void draw_pixels(Canvas *const canvas, uint8_t *data, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode drawMode) {
for (int8_t o = 0; o < w; o++) {
for (int8_t p = 0; p < h; p++) {
if (in_screen(o + x, p + y) && data[p * w + o] == 1)
set_pixel(canvas, o + x, p + y, drawMode);
}
}
}
void draw_rectangle(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode drawMode) {
for (int8_t o = 0; o < w; o++) {
for (int8_t p = 0; p < h; p++) {
if (in_screen(o + x, p + y)) {
set_pixel(canvas, o + x, p + y, drawMode);
}
}
}
}
void invert_rectangle(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h) {
draw_rectangle(canvas, x, y, w, h, Inverse);
}
uint8_t *image_data(Canvas *const canvas, const Icon *icon) {
uint8_t *data = malloc(sizeof(uint8_t) * 8 * 128);
uint8_t *screen = canvas->fb.tile_buf_ptr;
canvas->fb.tile_buf_ptr = data;
canvas_draw_icon(canvas, 0, 0, icon);
canvas->fb.tile_buf_ptr = screen;
return data;
}
uint8_t *getOrAddIconData(Canvas *const canvas, const Icon *icon) {
uint8_t *icon_data = get_tilemap((unsigned long) icon);
if (icon_data == NULL) {
icon_data = image_data(canvas, icon);
add_new_tilemap(icon_data, (unsigned long) icon);
}
return icon_data;
}
void draw_icon_clip(Canvas *const canvas, const Icon *icon, int16_t x, int16_t y, uint8_t left, uint8_t top, uint8_t w,
uint8_t h, DrawMode drawMode) {
uint8_t *icon_data = getOrAddIconData(canvas, icon);
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
bool on = test_pixel(icon_data, left + i, top + j, SCREEN_WIDTH);
if (drawMode == Filled) {
set_pixel(canvas, x + i, y + j, on ? Black : White);
} else if (on)
set_pixel(canvas, x + i, y + j, drawMode);
}
}
}
void draw_icon_clip_flipped(Canvas *const canvas, const Icon *icon, int16_t x, int16_t y, uint8_t left, uint8_t top,
uint8_t w,
uint8_t h, DrawMode drawMode) {
uint8_t *icon_data = getOrAddIconData(canvas, icon);
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
bool on = test_pixel(icon_data, left + i, top + j, SCREEN_WIDTH);
if (drawMode == Filled) {
set_pixel(canvas, x + w - i - 1, y + h - j - 1, on ? Black : White);
} else if (on)
set_pixel(canvas, x + w - i - 1, y + h - j - 1, drawMode);
}
}
}

View File

@@ -0,0 +1,58 @@
#pragma once
#include <furi.h>
#include <gui/canvas.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
typedef enum {
Black,
White,
Inverse,
Filled //Currently only for Icon clip drawing
} DrawMode;
// size is the screen size
typedef struct {
uint8_t *data;
unsigned long iconId;
} TileMap;
bool test_pixel(uint8_t *data, uint8_t x, uint8_t y, uint8_t w);
uint8_t *image_data(Canvas *const canvas, const Icon *icon);
uint32_t pixel_index(uint8_t x, uint8_t y);
void draw_icon_clip(Canvas *const canvas, const Icon *icon, int16_t x, int16_t y, uint8_t left, uint8_t top, uint8_t w,
uint8_t h, DrawMode drawMode);
void draw_icon_clip_flipped(Canvas *const canvas, const Icon *icon, int16_t x, int16_t y, uint8_t left, uint8_t top, uint8_t w,
uint8_t h, DrawMode drawMode);
void draw_rounded_box(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode drawMode);
void draw_rounded_box_frame(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode drawMode);
void draw_rectangle(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode drawMode);
void invert_rectangle(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h);
void invert_shape(Canvas *const canvas, uint8_t *data, int16_t x, int16_t y, uint8_t w, uint8_t h);
void draw_pixels(Canvas *const canvas, uint8_t *data, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode drawMode);
bool read_pixel(Canvas *const canvas, int16_t x, int16_t y);
void set_pixel(Canvas *const canvas, int16_t x, int16_t y, DrawMode draw_mode);
void draw_line(Canvas *const canvas, int16_t x1, int16_t y1, int16_t x2, int16_t y2, DrawMode draw_mode);
bool in_screen(int16_t x, int16_t y);
void ui_cleanup();
uint8_t* get_buffer(Canvas *const canvas);
uint8_t* make_buffer();
void clone_buffer(uint8_t* canvas, uint8_t* data);

View File

@@ -22,7 +22,7 @@ typedef enum {
EventTypeKey, EventTypeKey,
} EventType; } EventType;
typedef struct { typedef struct{
uint32_t animation_duration; uint32_t animation_duration;
uint32_t message_duration; uint32_t message_duration;
uint32_t starting_money; uint32_t starting_money;
@@ -43,7 +43,15 @@ typedef enum {
GameStateDealer, GameStateDealer,
} PlayState; } PlayState;
typedef enum { DirectionUp, DirectionRight, DirectionDown, DirectionLeft, Select, None } Direction; typedef enum {
DirectionUp,
DirectionDown,
DirectionRight,
DirectionLeft,
Select,
Back,
None
} Direction;
typedef struct { typedef struct {
Card player_cards[21]; Card player_cards[21];
@@ -63,6 +71,7 @@ typedef struct {
Deck deck; Deck deck;
PlayState state; PlayState state;
QueueState queue_state; QueueState queue_state;
Menu* menu; Menu *menu;
unsigned int last_tick; unsigned int last_tick;
} GameState; } GameState;

View File

@@ -6,28 +6,32 @@
#define LINE_HEIGHT 16 #define LINE_HEIGHT 16
#define ITEM_PADDING 4 #define ITEM_PADDING 4
const char MoneyMul[4] = {'K', 'B', 'T', 'S'}; const char MoneyMul[4] = {
'K', 'B', 'T', 'S'
};
void draw_player_scene(Canvas* const canvas, const GameState* game_state) { void draw_player_scene(Canvas *const canvas, const GameState *game_state) {
int max_card = game_state->player_card_count; int max_card = game_state->player_card_count;
if(max_card > 0) draw_deck((game_state->player_cards), max_card, canvas); if (max_card > 0)
draw_deck((game_state->player_cards), max_card, canvas);
if(game_state->dealer_card_count > 0) draw_card_back_at(13, 5, canvas); if (game_state->dealer_card_count > 0)
draw_card_back_at(13, 5, canvas);
max_card = game_state->dealer_card_count; max_card = game_state->dealer_card_count;
if(max_card > 1) { if (max_card > 1) {
draw_card_at( draw_card_at(2, 2, game_state->dealer_cards[1].pip, game_state->dealer_cards[1].character,
2, 2, game_state->dealer_cards[1].pip, game_state->dealer_cards[1].character, canvas); canvas);
} }
} }
void draw_dealer_scene(Canvas* const canvas, const GameState* game_state) { void draw_dealer_scene(Canvas *const canvas, const GameState *game_state) {
uint8_t max_card = game_state->dealer_card_count; uint8_t max_card = game_state->dealer_card_count;
draw_deck((game_state->dealer_cards), max_card, canvas); draw_deck((game_state->dealer_cards), max_card, canvas);
} }
void popup_frame(Canvas* const canvas) { void popup_frame(Canvas *const canvas) {
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 32, 15, 66, 13); canvas_draw_box(canvas, 32, 15, 66, 13);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
@@ -35,16 +39,15 @@ void popup_frame(Canvas* const canvas) {
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
} }
void draw_play_menu(Canvas* const canvas, const GameState* game_state) {
const char* menus[3] = {"Double", "Hit", "Stay"}; void draw_play_menu(Canvas *const canvas, const GameState *game_state) {
for(uint8_t m = 0; m < 3; m++) { const char *menus[3] = {"Double", "Hit", "Stay"};
if(m == 0 && for (uint8_t m = 0; m < 3; m++) {
(game_state->doubled || game_state->player_score < game_state->settings.round_price)) if (m == 0 && (game_state->doubled || game_state->player_score < game_state->settings.round_price)) continue;
continue;
int y = m * 13 + 25; int y = m * 13 + 25;
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
if(game_state->selectedMenu == m) { if (game_state->selectedMenu == m) {
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 1, y, 31, 12); canvas_draw_box(canvas, 1, y, 31, 12);
} else { } else {
@@ -54,7 +57,7 @@ void draw_play_menu(Canvas* const canvas, const GameState* game_state) {
canvas_draw_frame(canvas, 1, y, 31, 12); canvas_draw_frame(canvas, 1, y, 31, 12);
} }
if(game_state->selectedMenu == m) if (game_state->selectedMenu == m)
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
else else
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
@@ -62,34 +65,35 @@ void draw_play_menu(Canvas* const canvas, const GameState* game_state) {
} }
} }
void draw_screen(Canvas* const canvas, const bool* points) { void draw_screen(Canvas *const canvas, const bool *points) {
for(uint8_t x = 0; x < 128; x++) { for (uint8_t x = 0; x < 128; x++) {
for(uint8_t y = 0; y < 64; y++) { for (uint8_t y = 0; y < 64; y++) {
if(points[y * 128 + x]) canvas_draw_dot(canvas, x, y); if (points[y * 128 + x])
canvas_draw_dot(canvas, x, y);
} }
} }
} }
void draw_score(Canvas* const canvas, bool top, uint8_t amount) { void draw_score(Canvas *const canvas, bool top, uint8_t amount) {
char drawChar[20]; char drawChar[20];
snprintf(drawChar, sizeof(drawChar), "Player score: %i", amount); snprintf(drawChar, sizeof(drawChar), "Player score: %i", amount);
if(top) if (top)
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, drawChar); canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, drawChar);
else else
canvas_draw_str_aligned(canvas, 64, 62, AlignCenter, AlignBottom, drawChar); canvas_draw_str_aligned(canvas, 64, 62, AlignCenter, AlignBottom, drawChar);
} }
void draw_money(Canvas* const canvas, uint32_t score) { void draw_money(Canvas *const canvas, uint32_t score) {
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
char drawChar[10]; char drawChar[10];
uint32_t currAmount = score; uint32_t currAmount = score;
if(currAmount < 1000) { if (currAmount < 1000) {
snprintf(drawChar, sizeof(drawChar), "$%lu", currAmount); snprintf(drawChar, sizeof(drawChar), "$%lu", currAmount);
} else { } else {
char c = 'K'; char c = 'K';
for(uint8_t i = 0; i < 4; i++) { for (uint8_t i = 0; i < 4; i++) {
currAmount = currAmount / 1000; currAmount = currAmount / 1000;
if(currAmount < 1000) { if (currAmount < 1000) {
c = MoneyMul[i]; c = MoneyMul[i];
break; break;
} }
@@ -100,87 +104,77 @@ void draw_money(Canvas* const canvas, uint32_t score) {
canvas_draw_str_aligned(canvas, 126, 2, AlignRight, AlignTop, drawChar); canvas_draw_str_aligned(canvas, 126, 2, AlignRight, AlignTop, drawChar);
} }
void draw_menu(
Canvas* const canvas,
const char* text,
const char* value,
int8_t y,
bool left_caret,
bool right_caret,
bool selected) {
UNUSED(selected);
if(y < 0 || y >= 64) return;
if(selected) { void draw_menu(Canvas *const canvas, const char *text, const char *value, int8_t y, bool left_caret, bool right_caret,
bool selected) {
UNUSED(selected);
if (y < 0 || y >= 64) return;
if (selected) {
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 0, y, 122, LINE_HEIGHT); canvas_draw_box(canvas, 0, y, 122, LINE_HEIGHT);
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
} }
canvas_draw_str_aligned(canvas, 4, y + ITEM_PADDING, AlignLeft, AlignTop, text); canvas_draw_str_aligned(canvas, 4, y + ITEM_PADDING, AlignLeft, AlignTop, text);
if(left_caret) canvas_draw_str_aligned(canvas, 80, y + ITEM_PADDING, AlignLeft, AlignTop, "<"); if (left_caret)
canvas_draw_str_aligned(canvas, 80, y + ITEM_PADDING, AlignLeft, AlignTop, "<");
canvas_draw_str_aligned(canvas, 100, y + ITEM_PADDING, AlignCenter, AlignTop, value); canvas_draw_str_aligned(canvas, 100, y + ITEM_PADDING, AlignCenter, AlignTop, value);
if(right_caret) if (right_caret)
canvas_draw_str_aligned(canvas, 120, y + ITEM_PADDING, AlignRight, AlignTop, ">"); canvas_draw_str_aligned(canvas, 120, y + ITEM_PADDING, AlignRight, AlignTop, ">");
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
} }
void settings_page(Canvas* const canvas, const GameState* gameState) { void settings_page(Canvas *const canvas, const GameState *gameState) {
char drawChar[10]; char drawChar[10];
int startY = 0; int startY = 0;
if(LINE_HEIGHT * (gameState->selectedMenu + 1) >= 64) { if (LINE_HEIGHT * (gameState->selectedMenu + 1) >= 64) {
startY -= (LINE_HEIGHT * (gameState->selectedMenu + 1)) - 64; startY -= (LINE_HEIGHT * (gameState->selectedMenu + 1)) - 64;
} }
int scrollHeight = round(64 / 6.0) + ITEM_PADDING * 2; int scrollHeight = round(64 / 6.0) + ITEM_PADDING * 2;
int scrollPos = 64 / (6.0 / (gameState->selectedMenu + 1)) - ITEM_PADDING * 2; int scrollPos = 64 / (6.0 / (gameState->selectedMenu + 1)) - ITEM_PADDING * 2;
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 123, scrollPos, 4, scrollHeight); canvas_draw_box(canvas, 123, scrollPos, 4, scrollHeight);
canvas_draw_box(canvas, 125, 0, 1, 64); canvas_draw_box(canvas, 125, 0, 1, 64);
snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.starting_money); snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.starting_money);
draw_menu( draw_menu(canvas, "Start money", drawChar,
canvas, 0 * LINE_HEIGHT + startY,
"Start money", gameState->settings.starting_money > gameState->settings.round_price,
drawChar, gameState->settings.starting_money < 400,
0 * LINE_HEIGHT + startY, gameState->selectedMenu == 0
gameState->settings.starting_money > gameState->settings.round_price, );
gameState->settings.starting_money < 400,
gameState->selectedMenu == 0);
snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.round_price); snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.round_price);
draw_menu( draw_menu(canvas, "Round price", drawChar,
canvas, 1 * LINE_HEIGHT + startY,
"Round price", gameState->settings.round_price > 10,
drawChar, gameState->settings.round_price < gameState->settings.starting_money,
1 * LINE_HEIGHT + startY, gameState->selectedMenu == 1
gameState->settings.round_price > 10, );
gameState->settings.round_price < gameState->settings.starting_money,
gameState->selectedMenu == 1);
snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.animation_duration); snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.animation_duration);
draw_menu( draw_menu(canvas, "Anim. length", drawChar,
canvas, 2 * LINE_HEIGHT + startY,
"Anim. length", gameState->settings.animation_duration > 0,
drawChar, gameState->settings.animation_duration < 2000,
2 * LINE_HEIGHT + startY, gameState->selectedMenu == 2
gameState->settings.animation_duration > 0, );
gameState->settings.animation_duration < 2000,
gameState->selectedMenu == 2);
snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.message_duration); snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.message_duration);
draw_menu( draw_menu(canvas, "Popup time", drawChar,
canvas, 3 * LINE_HEIGHT + startY,
"Popup time", gameState->settings.message_duration > 0,
drawChar, gameState->settings.message_duration < 2000,
3 * LINE_HEIGHT + startY, gameState->selectedMenu == 3
gameState->settings.message_duration > 0, );
gameState->settings.message_duration < 2000, // draw_menu(canvas, "Sound", gameState->settings.sound_effects ? "Yes" : "No",
gameState->selectedMenu == 3); // 5 * LINE_HEIGHT + startY,
// draw_menu(canvas, "Sound", gameState->settings.sound_effects ? "Yes" : "No", // true,
// 5 * LINE_HEIGHT + startY, // true,
// true, // gameState->selectedMenu == 5
// true, // );
// gameState->selectedMenu == 5
// );
} }

View File

@@ -3,16 +3,16 @@
#include "defines.h" #include "defines.h"
#include <gui/gui.h> #include <gui/gui.h>
void draw_player_scene(Canvas* const canvas, const GameState* game_state); void draw_player_scene(Canvas *const canvas, const GameState *game_state);
void draw_dealer_scene(Canvas* const canvas, const GameState* game_state); void draw_dealer_scene(Canvas *const canvas, const GameState *game_state);
void draw_play_menu(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_score(Canvas *const canvas, bool top, uint8_t amount);
void draw_money(Canvas* const canvas, uint32_t score); void draw_money(Canvas *const canvas, uint32_t score);
void settings_page(Canvas* const canvas, const GameState* gameState); void settings_page(Canvas *const canvas, const GameState * gameState);
void popup_frame(Canvas* const canvas); void popup_frame(Canvas *const canvas);
void draw_screen(Canvas* const canvas, const bool* points); void draw_screen(Canvas *const canvas, const bool* points);

View File

@@ -1,17 +1,16 @@
#include <storage/storage.h> #include <storage/storage.h>
#include "util.h" #include "util.h"
const char* CONFIG_FILE_PATH = EXT_PATH(".blackjack.settings"); const char *CONFIG_FILE_PATH = EXT_PATH(".blackjack.settings");
void save_settings(Settings settings) { void save_settings(Settings settings) {
Storage* storage = furi_record_open(RECORD_STORAGE); Storage *storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* file = flipper_format_file_alloc(storage); FlipperFormat *file = flipper_format_file_alloc(storage);
FURI_LOG_D(APP_NAME, "Saving config"); FURI_LOG_D(APP_NAME, "Saving config");
if(flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { if (flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) {
FURI_LOG_D( FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_ANIMATION_DURATION, settings.animation_duration);
APP_NAME, "Saving %s: %ld", CONF_ANIMATION_DURATION, settings.animation_duration); flipper_format_update_uint32(file, CONF_ANIMATION_DURATION, &(settings.animation_duration), 1);
flipper_format_update_uint32(
file, CONF_ANIMATION_DURATION, &(settings.animation_duration), 1);
FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_MESSAGE_DURATION, settings.message_duration); FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_MESSAGE_DURATION, settings.message_duration);
flipper_format_update_uint32(file, CONF_MESSAGE_DURATION, &(settings.message_duration), 1); flipper_format_update_uint32(file, CONF_MESSAGE_DURATION, &(settings.message_duration), 1);
@@ -22,10 +21,10 @@ void save_settings(Settings settings) {
FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_ROUND_PRICE, settings.round_price); FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_ROUND_PRICE, settings.round_price);
flipper_format_update_uint32(file, CONF_ROUND_PRICE, &(settings.round_price), 1); flipper_format_update_uint32(file, CONF_ROUND_PRICE, &(settings.round_price), 1);
FURI_LOG_D(APP_NAME, "Saving %s: %i", CONF_SOUND_EFFECTS, settings.sound_effects ? 1 : 0); FURI_LOG_D(APP_NAME, "Saving %s: %i", CONF_SOUND_EFFECTS, settings.sound_effects?1:0);
flipper_format_update_bool(file, CONF_SOUND_EFFECTS, &(settings.sound_effects), 1); flipper_format_update_bool(file, CONF_SOUND_EFFECTS, &(settings.sound_effects), 1);
FURI_LOG_D(APP_NAME, "Config saved"); FURI_LOG_D(APP_NAME, "Config saved");
} else { }else{
FURI_LOG_E(APP_NAME, "Save error"); FURI_LOG_E(APP_NAME, "Save error");
} }
flipper_format_file_close(file); flipper_format_file_close(file);
@@ -33,7 +32,7 @@ void save_settings(Settings settings) {
furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_STORAGE);
} }
void save_settings_file(FlipperFormat* file, Settings* settings) { void save_settings_file(FlipperFormat *file, Settings *settings) {
flipper_format_write_header_cstr(file, CONFIG_FILE_HEADER, CONFIG_FILE_VERSION); flipper_format_write_header_cstr(file, CONFIG_FILE_HEADER, CONFIG_FILE_VERSION);
flipper_format_write_comment_cstr(file, "Card animation duration in ms"); flipper_format_write_comment_cstr(file, "Card animation duration in ms");
flipper_format_write_uint32(file, CONF_ANIMATION_DURATION, &(settings->animation_duration), 1); flipper_format_write_uint32(file, CONF_ANIMATION_DURATION, &(settings->animation_duration), 1);
@@ -58,57 +57,58 @@ Settings load_settings() {
settings.sound_effects = true; settings.sound_effects = true;
FURI_LOG_D(APP_NAME, "Opening storage"); FURI_LOG_D(APP_NAME, "Opening storage");
Storage* storage = furi_record_open(RECORD_STORAGE); Storage *storage = furi_record_open(RECORD_STORAGE);
FURI_LOG_D(APP_NAME, "Allocating file"); FURI_LOG_D(APP_NAME, "Allocating file");
FlipperFormat* file = flipper_format_file_alloc(storage); FlipperFormat *file = flipper_format_file_alloc(storage);
FURI_LOG_D(APP_NAME, "Allocating string"); FURI_LOG_D(APP_NAME, "Allocating string");
FuriString* string_value; FuriString *string_value;
string_value = furi_string_alloc(); string_value = furi_string_alloc();
if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) != FSE_OK) { if (storage_common_stat(storage, CONFIG_FILE_PATH, NULL) != FSE_OK) {
FURI_LOG_D(APP_NAME, "Config file %s not found, creating new one...", CONFIG_FILE_PATH); FURI_LOG_D(APP_NAME, "Config file %s not found, creating new one...", CONFIG_FILE_PATH);
if(!flipper_format_file_open_new(file, CONFIG_FILE_PATH)) { if (!flipper_format_file_open_new(file, CONFIG_FILE_PATH)) {
FURI_LOG_E(APP_NAME, "Error creating new file %s", CONFIG_FILE_PATH); FURI_LOG_E(APP_NAME, "Error creating new file %s", CONFIG_FILE_PATH);
flipper_format_file_close(file); flipper_format_file_close(file);
} else { } else {
save_settings_file(file, &settings); save_settings_file(file, &settings);
} }
} else { } else {
if(!flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { if (!flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) {
FURI_LOG_E(APP_NAME, "Error opening existing file %s", CONFIG_FILE_PATH); FURI_LOG_E(APP_NAME, "Error opening existing file %s", CONFIG_FILE_PATH);
flipper_format_file_close(file); flipper_format_file_close(file);
} else { }
else {
uint32_t value; uint32_t value;
bool valueBool; bool valueBool;
FURI_LOG_D(APP_NAME, "Checking version"); FURI_LOG_D(APP_NAME, "Checking version");
if(!flipper_format_read_header(file, string_value, &value)) { if (!flipper_format_read_header(file, string_value, &value)) {
FURI_LOG_E(APP_NAME, "Config file mismatch"); FURI_LOG_E(APP_NAME, "Config file mismatch");
} else { } else {
FURI_LOG_D(APP_NAME, "Loading %s", CONF_ANIMATION_DURATION); FURI_LOG_D(APP_NAME, "Loading %s", CONF_ANIMATION_DURATION);
if(flipper_format_read_uint32(file, CONF_ANIMATION_DURATION, &value, 1)) { if (flipper_format_read_uint32(file, CONF_ANIMATION_DURATION, &value, 1)) {
settings.animation_duration = value; settings.animation_duration = value;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ANIMATION_DURATION, value); FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ANIMATION_DURATION, value);
} }
FURI_LOG_D(APP_NAME, "Loading %s", CONF_MESSAGE_DURATION); FURI_LOG_D(APP_NAME, "Loading %s", CONF_MESSAGE_DURATION);
if(flipper_format_read_uint32(file, CONF_MESSAGE_DURATION, &value, 1)) { if (flipper_format_read_uint32(file, CONF_MESSAGE_DURATION, &value, 1)) {
settings.message_duration = value; settings.message_duration = value;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_MESSAGE_DURATION, value); FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_MESSAGE_DURATION, value);
} }
FURI_LOG_D(APP_NAME, "Loading %s", CONF_STARTING_MONEY); FURI_LOG_D(APP_NAME, "Loading %s", CONF_STARTING_MONEY);
if(flipper_format_read_uint32(file, CONF_STARTING_MONEY, &value, 1)) { if (flipper_format_read_uint32(file, CONF_STARTING_MONEY, &value, 1)) {
settings.starting_money = value; settings.starting_money = value;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_STARTING_MONEY, value); FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_STARTING_MONEY, value);
} }
FURI_LOG_D(APP_NAME, "Loading %s", CONF_ROUND_PRICE); FURI_LOG_D(APP_NAME, "Loading %s", CONF_ROUND_PRICE);
if(flipper_format_read_uint32(file, CONF_ROUND_PRICE, &value, 1)) { if (flipper_format_read_uint32(file, CONF_ROUND_PRICE, &value, 1)) {
settings.round_price = value; settings.round_price = value;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ROUND_PRICE, value); FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ROUND_PRICE, value);
} }
FURI_LOG_D(APP_NAME, "Loading %s", CONF_SOUND_EFFECTS); FURI_LOG_D(APP_NAME, "Loading %s", CONF_SOUND_EFFECTS);
if(flipper_format_read_bool(file, CONF_SOUND_EFFECTS, &valueBool, 1)) { if (flipper_format_read_bool(file, CONF_SOUND_EFFECTS, &valueBool, 1)) {
settings.sound_effects = valueBool; settings.sound_effects = valueBool;
FURI_LOG_D(APP_NAME, "Loaded %s: %i", CONF_ROUND_PRICE, valueBool ? 1 : 0); FURI_LOG_D(APP_NAME, "Loaded %s: %i", CONF_ROUND_PRICE, valueBool?1:0);
} }
} }
flipper_format_file_close(file); flipper_format_file_close(file);
@@ -116,7 +116,7 @@ Settings load_settings() {
} }
furi_string_free(string_value); furi_string_free(string_value);
// flipper_format_file_close(file); // flipper_format_file_close(file);
flipper_format_free(file); flipper_format_free(file);
furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_STORAGE);
return settings; return settings;