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

View File

@@ -1,7 +1,7 @@
#include "card.h"
#include "dml.h"
#include "ui.h"
#define CORNER_MARGIN 3
#define CARD_DRAW_X_START 108
#define CARD_DRAW_Y_START 38
#define CARD_DRAW_X_SPACE 10
@@ -9,154 +9,145 @@
#define CARD_DRAW_X_OFFSET 4
#define CARD_DRAW_FIRST_ROW_LENGTH 7
bool pips[4][49] = {
{//spades
0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0},
{
//hearts
0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
},
{//diamonds
0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0},
{//clubs
0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}};
uint8_t pips[4][3] = {
{21, 10, 7}, //spades
{7, 10, 7}, //hearts
{0, 10, 7}, //diamonds
{14, 10, 7}, //clubs
};
uint8_t letters[13][3] = {
{0, 0, 5},
{5, 0, 5},
{10, 0, 5},
{15, 0, 5},
{20, 0, 5},
{25, 0, 5},
{30, 0, 5},
{0, 5, 5},
{5, 5, 5},
{10, 5, 5},
{15, 5, 5},
{20, 5, 5},
{25, 5, 5},
};
//region Player card positions
uint8_t playerCardPositions[22][4] = {
//first row
{108, 38},
{98, 38},
{88, 38},
{78, 38},
{68, 38},
{58, 38},
{48, 38},
{38, 38},
//second row
{104, 26},
{94, 26},
{84, 26},
{74, 26},
{64, 26},
{54, 26},
{44, 26},
//third row
{99, 14},
{89, 14},
{79, 14},
{69, 14},
{59, 14},
{49, 14},
//first row
{108, 38},
{98, 38},
{88, 38},
{78, 38},
{68, 38},
{58, 38},
{48, 38},
{38, 38},
//second row
{104, 26},
{94, 26},
{84, 26},
{74, 26},
{64, 26},
{54, 26},
{44, 26},
//third row
{99, 14},
{89, 14},
{79, 14},
{69, 14},
{59, 14},
{49, 14},
};
//endregion
Icon *card_graphics = NULL;
bool get_pip_pixel(uint8_t pip, uint8_t x, uint8_t y) {
return pips[pip][x + y * 7];
void set_card_graphics(const Icon *graphics) {
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) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT);
void
draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, bool inverted, Canvas *const canvas) {
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);
canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT);
uint8_t *drawInfo = pips[pip];
uint8_t px = drawInfo[0], py = drawInfo[1], s = drawInfo[2];
uint8_t left = pos_x + CORNER_MARGIN;
uint8_t right = (pos_x + CARD_WIDTH - CORNER_MARGIN - 7);
uint8_t top = pos_y + CORNER_MARGIN;
uint8_t bottom = (pos_y + CARD_HEIGHT - CORNER_MARGIN - 7);
uint8_t left = pos_x + 2;
uint8_t right = (pos_x + CARD_WIDTH - s - 2);
uint8_t top = pos_y + 2;
uint8_t bottom = (pos_y + CARD_HEIGHT - s - 2);
for(uint8_t x = 0; x < 7; x++) {
for(uint8_t y = 0; y < 7; y++) {
if(get_pip_pixel(pip, x, y)) {
canvas_draw_dot(canvas, right + x + 1, top + y);
canvas_draw_dot(canvas, left + x - 1, bottom + y);
}
}
}
draw_icon_clip(canvas, card_graphics, right, top, px, py, s, s,
secondary);
draw_icon_clip_flipped(canvas, card_graphics, left, bottom, px, py, s, s,
secondary);
canvas_set_font(canvas, FontSecondary);
char drawChar[3];
if(character < 9)
snprintf(drawChar, sizeof(drawChar), "%i", character + 2);
else {
if(character == 9)
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");
}
drawInfo = letters[character];
px = drawInfo[0], py = drawInfo[1], s = drawInfo[2];
left = pos_x + 2;
right = (pos_x + CARD_WIDTH - s - 2);
top = pos_y + 2;
bottom = (pos_y + CARD_HEIGHT - s - 2);
canvas_set_font_direction(canvas, CanvasDirectionLeftToRight);
canvas_draw_str_aligned(canvas, left + 2, top + 3, AlignCenter, AlignCenter, drawChar);
canvas_set_font_direction(canvas, CanvasDirectionRightToLeft);
//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);
draw_icon_clip(canvas, card_graphics, left, top + 1, px, py, s, s,
secondary);
draw_icon_clip_flipped(canvas, card_graphics, right, bottom - 1, px, py, s, s,
secondary);
}
void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas) {
for(int i = count - 1; i >= 0; i--) {
draw_card_at(
playerCardPositions[i][0],
playerCardPositions[i][1],
cards[i].pip,
cards[i].character,
canvas);
void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas *const canvas) {
draw_card_at_colored(pos_x, pos_y, pip, character, false, canvas);
}
void draw_deck(const Card *cards, uint8_t count, Canvas *const canvas) {
for (int i = count - 1; i >= 0; i--) {
draw_card_at(playerCardPositions[i][0], playerCardPositions[i][1], cards[i].pip, cards[i].character, canvas);
}
}
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) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT);
void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas *const canvas) {
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, White);
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;
deck_ptr->deck_count = deck_count;
deck_ptr->cards = malloc(sizeof(Card) * 52 * deck_count);
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};
if (deck_ptr->cards == NULL) {
deck_ptr->deck_count = deck_count;
deck_ptr->card_count = deck_count * 52;
deck_ptr->cards = malloc(sizeof(Card) * deck_ptr->card_count);
}
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++;
}
}
}
}
void shuffle_deck(Deck* deck_ptr) {
void shuffle_deck(Deck *deck_ptr) {
srand(DWT->CYCCNT);
deck_ptr->index = 0;
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));
Card tmp = deck_ptr->cards[i];
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 score = 0;
for(uint8_t i = 0; i < count; i++) {
if(cards[i].character == 12)
for (uint8_t i = 0; i < count; i++) {
if (cards[i].character == 12)
aceCount++;
else {
if(cards[i].character > 8)
if (cards[i].character > 8)
score += 10;
else
score += cards[i].character + 2;
}
}
for(uint8_t i = 0; i < aceCount; i++) {
if((score + 11) <= 21)
score += 11;
else
score++;
for (uint8_t i = 0; i < aceCount; i++) {
if ((score + 11) <= 21) score += 11;
else score++;
}
return score;
}
void draw_card_animation(
Card animatingCard,
Vector from,
Vector control,
Vector to,
float t,
bool extra_margin,
Canvas* const canvas) {
void draw_card_animation(Card animatingCard, Vector from, Vector control, Vector to, float t, bool extra_margin,
Canvas *const canvas) {
float time = t;
if(extra_margin) {
if (extra_margin) {
time += 0.2;
}
Vector currentPos = quadratic_2d(from, control, to, time);
if(t > 1) {
draw_card_at(
currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas);
if (t > 1) {
draw_card_at(currentPos.x, currentPos.y, animatingCard.pip,
animatingCard.character, canvas);
} else {
if(t < 0.5)
if (t < 0.5)
draw_card_back_at(currentPos.x, currentPos.y, canvas);
else
draw_card_at(
currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas);
draw_card_at(currentPos.x, currentPos.y, animatingCard.pip,
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 "dml.h"
#define CARD_HEIGHT 24
#define CARD_HALF_HEIGHT CARD_HEIGHT / 2
#define CARD_WIDTH 18
#define CARD_HALF_WIDTH CARD_WIDTH / 2
#define CARD_HEIGHT 23
#define CARD_HALF_HEIGHT 11
#define CARD_WIDTH 17
#define CARD_HALF_WIDTH 8
//region types
typedef struct {
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 pip; //Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs
uint8_t character; //Card letter [0-12], 0 means 2, 12 is Ace
bool disabled;
bool flipped;
} Card;
typedef struct {
uint8_t deck_count; //Number of decks used
Card* cards; //Cards in the deck
int index; //Card index (to know where we at in the deck)
uint8_t deck_count; //Number of decks used
Card *cards; //Cards in the deck
int card_count;
int index; //Card index (to know where we at in the 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
void set_card_graphics(const Icon *graphics);
/**
* 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 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
@@ -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 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)
@@ -58,7 +82,7 @@ void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas);
* @param pos_y Y coordinate
* @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
@@ -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_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
*
* @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
@@ -82,7 +106,7 @@ void shuffle_deck(Deck* deck_ptr);
* @param count Count of cards
* @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
@@ -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 canvas Pointer to Flipper's canvas object
*/
void draw_card_animation(
Card animatingCard,
Vector from,
Vector control,
Vector to,
float t,
bool extra_margin,
Canvas* const canvas);
void draw_card_animation(Card animatingCard, Vector from, Vector control, Vector to, float t, bool extra_margin,
Canvas *const canvas);
/**
* Init hand pointer
* @param hand_ptr Pointer to hand
* @param count Number of cards we want to store
*/
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>
float lerp(float v0, float v1, float t) {
if(t > 1) return v1;
if (t > 1) return v1;
return (1 - t) * v0 + t * v1;
}
Vector lerp_2d(Vector start, Vector end, float t) {
return (Vector){
lerp(start.x, end.x, t),
lerp(start.y, end.y, t),
return (Vector) {
lerp(start.x, end.x, t),
lerp(start.y, end.y, 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) {
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) {
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) {
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) {
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) {
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) {

View File

@@ -9,6 +9,10 @@ typedef struct {
float y;
} 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
*

View File

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

View File

@@ -4,19 +4,18 @@
#include <gui/gui.h>
typedef struct {
const char* name; //Name of the menu
bool enabled; //Is the menu item enabled (it will not render, you cannot select it)
const char *name; //Name of the menu
bool enabled; //Is the menu item enabled (it will not render, you cannot select it)
void (*callback)(
void* state); //Callback for when the activate_menu is called while this menu is selected
void (*callback)(void *state); //Callback for when the activate_menu is called while this menu is selected
} MenuItem;
typedef struct {
MenuItem* items; //list of menu items
uint8_t menu_count; //count of menu items (do not change)
uint8_t current_menu; //currently selected menu item
uint8_t menu_width; //width of the menu
bool enabled; //is the menu enabled (it will not render and accept events when disabled)
MenuItem *items; //list of menu items
uint8_t menu_count; //count of menu items (do not change)
uint8_t current_menu; //currently selected menu item
uint8_t menu_width; //width of the menu
bool enabled; //is the menu enabled (it will not render and accept events when disabled)
} Menu;
/**
@@ -24,7 +23,7 @@ typedef struct {
*
* @param menu Pointer of the menu to clean up
*/
void free_menu(Menu* menu);
void free_menu(Menu *menu);
/**
* Add a new menu item
@@ -33,7 +32,7 @@ void free_menu(Menu* menu);
* @param name Name of the menu item
* @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
@@ -42,7 +41,7 @@ void add_menu(Menu* menu, const char* name, void (*callback)(void*));
* @param index Menu index to set
* @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
@@ -50,7 +49,7 @@ void set_menu_state(Menu* menu, uint8_t index, bool state);
* @param menu Pointer of the menu
* @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
@@ -58,7 +57,7 @@ void move_menu(Menu* menu, int8_t direction);
* @param menu Pointer of the menu
* @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).
@@ -74,4 +73,4 @@ void activate_menu(Menu* menu, void* state);
* @param pos_x X 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"
void render_queue(const QueueState* queue_state, const void* app_state, Canvas* const canvas) {
if(queue_state->current != NULL && queue_state->current->render != NULL)
((QueueItem*)queue_state->current)->render(app_state, canvas);
void render_queue(const QueueState *queue_state, const void *app_state, Canvas *const 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) {
if(queue_state->current != NULL) {
bool run_queue(QueueState *queue_state, void *app_state) {
if (queue_state->current != NULL) {
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);
return true;
@@ -16,48 +17,44 @@ bool run_queue(QueueState* queue_state, void* app_state) {
return false;
}
void dequeue(QueueState* queue_state, void* app_state) {
((QueueItem*)queue_state->current)->callback(app_state);
QueueItem* f = queue_state->current;
void dequeue(QueueState *queue_state, void *app_state) {
((QueueItem *) queue_state->current)->callback(app_state);
QueueItem *f = queue_state->current;
queue_state->current = f->next;
free(f);
if(queue_state->current != NULL) {
if(queue_state->current->start != NULL) queue_state->current->start(app_state);
if (queue_state->current != NULL) {
if (queue_state->current->start != NULL)
queue_state->current->start(app_state);
queue_state->start = furi_get_tick();
} else {
}else{
queue_state->running = false;
}
}
void queue_clear(QueueState* queue_state) {
void queue_clear(QueueState *queue_state) {
queue_state->running = false;
QueueItem* curr = queue_state->current;
while(curr != NULL) {
QueueItem* f = curr;
QueueItem *curr = queue_state->current;
while (curr != NULL) {
QueueItem *f = curr;
curr = curr->next;
free(f);
}
}
void enqueue(
QueueState* queue_state,
void* app_state,
void (*done)(void* state),
void (*start)(void* state),
void (*render)(const void* state, Canvas* const canvas),
uint32_t duration) {
QueueItem* next;
if(queue_state->current == NULL) {
void enqueue(QueueState *queue_state, void *app_state,
void(*done)(void *state), void(*start)(void *state),
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->current = malloc(sizeof(QueueItem));
next = queue_state->current;
if(next->start != NULL) next->start(app_state);
if (next->start != NULL)
next->start(app_state);
} else {
next = queue_state->current;
while(next->next != NULL) {
next = (QueueItem*)(next->next);
}
while (next->next != NULL) { next = (QueueItem *) (next->next); }
next->next = malloc(sizeof(QueueItem));
next = next->next;
}

View File

@@ -4,19 +4,17 @@
#include <furi.h>
typedef struct {
void (*callback)(void* state); //Callback for when the item is dequeued
void (*render)(
const void* state,
Canvas* const canvas); //Callback for the rendering loop while this item is running
void (*start)(void* state); //Callback when this item is started running
void* next; //Pointer to the next item
uint32_t duration; //duration of the item
void (*callback)(void *state); //Callback for when the item is dequeued
void (*render)(const void *state, Canvas *const canvas); //Callback for the rendering loop while this item is running
void (*start)(void *state); //Callback when this item is started running
void *next; //Pointer to the next item
uint32_t duration; //duration of the item
} QueueItem;
typedef struct {
unsigned int start; //current queue item start time
QueueItem* current; //current queue item
bool running; //is the queue running
unsigned int start; //current queue item start time
QueueItem *current; //current queue item
bool running; //is the queue running
} QueueState;
/**
@@ -29,19 +27,15 @@ typedef struct {
* @param render Callback to render loop if needed
* @param duration Length of the item
*/
void enqueue(
QueueState* queue_state,
void* app_state,
void (*done)(void* state),
void (*start)(void* state),
void (*render)(const void* state, Canvas* const canvas),
uint32_t duration);
void enqueue(QueueState *queue_state, void *app_state,
void(*done)(void *state), void(*start)(void *state),
void (*render)(const void *state, Canvas *const canvas), uint32_t duration);
/**
* Clears all queue items
*
* @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.
@@ -49,7 +43,7 @@ void queue_clear(QueueState* queue_state);
* @param queue_state The queue state pointer
* @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)
@@ -58,7 +52,7 @@ void dequeue(QueueState* queue_state, void* app_state);
* @param app_state Your application state
* @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)
@@ -67,4 +61,4 @@ bool run_queue(QueueState* queue_state, void* app_state);
* @param app_state Your application state
* @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,
} EventType;
typedef struct {
typedef struct{
uint32_t animation_duration;
uint32_t message_duration;
uint32_t starting_money;
@@ -43,7 +43,15 @@ typedef enum {
GameStateDealer,
} PlayState;
typedef enum { DirectionUp, DirectionRight, DirectionDown, DirectionLeft, Select, None } Direction;
typedef enum {
DirectionUp,
DirectionDown,
DirectionRight,
DirectionLeft,
Select,
Back,
None
} Direction;
typedef struct {
Card player_cards[21];
@@ -63,6 +71,7 @@ typedef struct {
Deck deck;
PlayState state;
QueueState queue_state;
Menu* menu;
Menu *menu;
unsigned int last_tick;
} GameState;

View File

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

View File

@@ -3,16 +3,16 @@
#include "defines.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 settings_page(Canvas* const canvas, const GameState* gameState);
void draw_money(Canvas *const canvas, uint32_t score);
void settings_page(Canvas *const canvas, const GameState * gameState);
void popup_frame(Canvas* const canvas);
void draw_screen(Canvas* const canvas, const bool* points);
void popup_frame(Canvas *const canvas);
void draw_screen(Canvas *const canvas, const bool* points);

View File

@@ -1,17 +1,16 @@
#include <storage/storage.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) {
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* file = flipper_format_file_alloc(storage);
Storage *storage = furi_record_open(RECORD_STORAGE);
FlipperFormat *file = flipper_format_file_alloc(storage);
FURI_LOG_D(APP_NAME, "Saving config");
if(flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) {
FURI_LOG_D(
APP_NAME, "Saving %s: %ld", CONF_ANIMATION_DURATION, settings.animation_duration);
flipper_format_update_uint32(
file, CONF_ANIMATION_DURATION, &(settings.animation_duration), 1);
if (flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) {
FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_ANIMATION_DURATION, settings.animation_duration);
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);
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);
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);
FURI_LOG_D(APP_NAME, "Config saved");
} else {
}else{
FURI_LOG_E(APP_NAME, "Save error");
}
flipper_format_file_close(file);
@@ -33,7 +32,7 @@ void save_settings(Settings settings) {
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_comment_cstr(file, "Card animation duration in ms");
flipper_format_write_uint32(file, CONF_ANIMATION_DURATION, &(settings->animation_duration), 1);
@@ -58,57 +57,58 @@ Settings load_settings() {
settings.sound_effects = true;
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");
FlipperFormat* file = flipper_format_file_alloc(storage);
FlipperFormat *file = flipper_format_file_alloc(storage);
FURI_LOG_D(APP_NAME, "Allocating string");
FuriString* string_value;
FuriString *string_value;
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);
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);
flipper_format_file_close(file);
} else {
save_settings_file(file, &settings);
}
} 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);
flipper_format_file_close(file);
} else {
}
else {
uint32_t value;
bool valueBool;
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");
} else {
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;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ANIMATION_DURATION, value);
}
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;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_MESSAGE_DURATION, value);
}
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;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_STARTING_MONEY, value);
}
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;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ROUND_PRICE, value);
}
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;
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);
@@ -116,7 +116,7 @@ Settings load_settings() {
}
furi_string_free(string_value);
// flipper_format_file_close(file);
// flipper_format_file_close(file);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return settings;