This commit is contained in:
RogueMaster
2022-11-17 23:48:40 -05:00
parent d686d7f650
commit 567f9f4585
39 changed files with 2007 additions and 1630 deletions

View File

@@ -18,72 +18,70 @@
#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) {
void drawPlayerCard(void *ctx) { GameState* game_state = 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++;
@@ -91,317 +89,361 @@ 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) if(game_state->settings.message_duration == 0) return;
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) if(game_state->settings.message_duration == 0) return;
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) if(game_state->settings.message_duration == 0) return;
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) if(game_state->settings.message_duration == 0) return;
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) if(game_state->settings.message_duration == 0) return;
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) if(game_state->settings.message_duration == 0) return;
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) {
void start(void *ctx) { GameState* game_state = 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(&(game_state->queue_state), game_state, start, before_start, to_start, enqueue(
game_state->settings.message_duration); &(game_state->queue_state),
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(&(game_state->queue_state), game_state, start, before_start, to_start, enqueue(
game_state->settings.message_duration); &(game_state->queue_state),
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, enqueue(&(game_state->queue_state), game_state, game_over, NULL, NULL, 0);
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(&(game_state->queue_state), game_state, start, before_start, to_start, enqueue(
game_state->settings.message_duration); &(game_state->queue_state),
game_state,
start,
before_start,
to_start,
game_state->settings.message_duration);
} }
void dealerTurn(void* ctx) {
void dealerTurn(void *ctx) { GameState* game_state = 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, draw_card_animation(animatingCard, (Vector){0, 64}, (Vector){0, 32}, end, t, true, canvas);
(Vector) {0, 64},
(Vector) {0, 32},
end,
t,
true,
canvas);
} else { } else {
draw_card_animation(animatingCard, draw_card_animation(
(Vector) {32, -CARD_HEIGHT}, animatingCard,
(Vector) {64, 32}, (Vector){32, -CARD_HEIGHT},
(Vector) {2, 2}, (Vector){64, 32},
t, (Vector){2, 2},
false, t,
canvas); false,
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 = 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); 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(animatingCard, draw_card_animation(
(Vector) {32, -CARD_HEIGHT}, animatingCard, (Vector){32, -CARD_HEIGHT}, (Vector){0, 32}, end, t, true, 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(&(game_state->queue_state), game_state, dealerTurn, NULL, to_dealer_turn, enqueue(
game_state->settings.message_duration); &(game_state->queue_state),
} else if (score > 21) { game_state,
enqueue(&(game_state->queue_state), game_state, lose, NULL, to_bust_state, dealerTurn,
game_state->settings.message_duration); 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 { } 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); 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(&(game_state->queue_state), game_state, win, NULL, to_win_state, enqueue(
game_state->settings.message_duration); &(game_state->queue_state),
} else if (dealer_score > player_score) { game_state,
enqueue(&(game_state->queue_state), game_state, lose, NULL, to_lose_state, win,
game_state->settings.message_duration); NULL,
} else if (dealer_score == player_score) { to_win_state,
enqueue(&(game_state->queue_state), game_state, draw, NULL, to_draw_state, game_state->settings.message_duration);
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 { } else {
enqueue(&(game_state->queue_state), game_state, drawDealerCard, NULL, dealer_card_animation, enqueue(
game_state->settings.animation_duration); &(game_state->queue_state),
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 || game_state->selectDirection == DirectionRight) { if(game_state->selectDirection == DirectionLeft ||
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(&(game_state->queue_state), game_state, drawDealerCard, NULL, dealer_back_card_animation, enqueue(
game_state->settings.animation_duration); &(game_state->queue_state),
enqueue(&(game_state->queue_state), game_state, drawPlayerCard, NULL, player_card_animation, game_state,
game_state->settings.animation_duration); drawDealerCard,
enqueue(&(game_state->queue_state), game_state, drawDealerCard, NULL, dealer_card_animation, NULL,
game_state->settings.animation_duration); dealer_back_card_animation,
enqueue(&(game_state->queue_state), game_state, drawPlayerCard, NULL, player_card_animation, game_state->settings.animation_duration);
game_state->settings.animation_duration); enqueue(
} &(game_state->queue_state),
if (!queue_ran) game_state,
player_tick(game_state); drawPlayerCard,
break; NULL,
case GameStateDealer: player_card_animation,
if (!queue_ran) game_state->settings.animation_duration);
dealer_tick(game_state); enqueue(
break; &(game_state->queue_state),
case GameStateSettings: game_state,
settings_tick(game_state); drawDealerCard,
break; NULL,
default: dealer_card_animation,
break; 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; 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;
@@ -409,10 +451,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;
@@ -422,60 +464,86 @@ 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(&(game_state->queue_state), game_state, drawPlayerCard, NULL, player_card_animation, enqueue(
game_state->settings.animation_duration); &(game_state->queue_state),
game_state->player_cards[game_state->player_card_count] = game_state->deck.cards[game_state->deck.index]; 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); uint8_t score = hand_count(game_state->player_cards, game_state->player_card_count + 1);
if (score > 21) { if(score > 21) {
enqueue(&(game_state->queue_state), game_state, lose, NULL, to_bust_state, enqueue(
game_state->settings.message_duration); &(game_state->queue_state),
game_state,
lose,
NULL,
to_bust_state,
game_state->settings.message_duration);
} else { } else {
enqueue(&(game_state->queue_state), game_state, dealerTurn, NULL, to_dealer_turn, enqueue(
game_state->settings.message_duration); &(game_state->queue_state),
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(&(game_state->queue_state), game_state, drawPlayerCard, NULL, player_card_animation, enqueue(
game_state->settings.animation_duration); &(game_state->queue_state),
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(&(game_state->queue_state), game_state, dealerTurn, NULL, to_dealer_turn, enqueue(
game_state->settings.message_duration); &(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); 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);
@@ -485,60 +553,58 @@ int32_t blackjack_app(void *p) {
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 = FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue);
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) {
if (event.input.type == InputTypePress) { switch(event.input.key) {
switch (event.input.key) { case InputKeyUp:
case InputKeyUp: localstate->selectDirection = DirectionUp;
localstate->selectDirection = DirectionUp; break;
break; case InputKeyDown:
case InputKeyDown: localstate->selectDirection = DirectionDown;
localstate->selectDirection = DirectionDown; break;
break; case InputKeyRight:
case InputKeyRight: localstate->selectDirection = DirectionRight;
localstate->selectDirection = DirectionRight; break;
break; case InputKeyLeft:
case InputKeyLeft: localstate->selectDirection = DirectionLeft;
localstate->selectDirection = DirectionLeft; break;
break; case InputKeyBack:
case InputKeyBack: if(localstate->state == GameStateSettings) {
if (localstate->state == GameStateSettings) { localstate->state = GameStateStart;
localstate->state = GameStateStart; save_settings(localstate->settings);
save_settings(localstate->settings); } else
} else processing = false;
processing = false; break;
break; case InputKeyOk:
case InputKeyOk: localstate->selectDirection = Select;
localstate->selectDirection = Select; break;
break; default:
default: break;
break;
} }
} }
} else if (event.type == EventTypeTick) { } else if(event.type == EventTypeTick) {
tick(localstate); tick(localstate);
processing = localstate->processing; processing = localstate->processing;
} }
@@ -550,7 +616,6 @@ 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);
@@ -558,7 +623,7 @@ 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(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));

View File

@@ -10,69 +10,74 @@
#define CARD_DRAW_FIRST_ROW_LENGTH 7 #define CARD_DRAW_FIRST_ROW_LENGTH 7
uint8_t pips[4][3] = { uint8_t pips[4][3] = {
{21, 10, 7}, //spades {21, 10, 7}, //spades
{7, 10, 7}, //hearts {7, 10, 7}, //hearts
{0, 10, 7}, //diamonds {0, 10, 7}, //diamonds
{14, 10, 7}, //clubs {14, 10, 7}, //clubs
}; };
uint8_t letters[13][3] = { uint8_t letters[13][3] = {
{0, 0, 5}, {0, 0, 5},
{5, 0, 5}, {5, 0, 5},
{10, 0, 5}, {10, 0, 5},
{15, 0, 5}, {15, 0, 5},
{20, 0, 5}, {20, 0, 5},
{25, 0, 5}, {25, 0, 5},
{30, 0, 5}, {30, 0, 5},
{0, 5, 5}, {0, 5, 5},
{5, 5, 5}, {5, 5, 5},
{10, 5, 5}, {10, 5, 5},
{15, 5, 5}, {15, 5, 5},
{20, 5, 5}, {20, 5, 5},
{25, 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; Icon* card_graphics = NULL;
void set_card_graphics(const Icon *graphics) { void set_card_graphics(const Icon* graphics) {
card_graphics = (Icon *) graphics; card_graphics = (Icon*)graphics;
} }
void void draw_card_at_colored(
draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, bool inverted, Canvas *const canvas) { 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 primary = inverted ? Black : White;
DrawMode secondary = inverted ? White : Black; DrawMode secondary = inverted ? White : Black;
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, primary); 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); draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
uint8_t *drawInfo = pips[pip]; uint8_t* drawInfo = pips[pip];
uint8_t px = drawInfo[0], py = drawInfo[1], s = drawInfo[2]; uint8_t px = drawInfo[0], py = drawInfo[1], s = drawInfo[2];
uint8_t left = pos_x + 2; uint8_t left = pos_x + 2;
@@ -80,10 +85,8 @@ draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character,
uint8_t top = pos_y + 2; uint8_t top = pos_y + 2;
uint8_t bottom = (pos_y + CARD_HEIGHT - s - 2); uint8_t bottom = (pos_y + CARD_HEIGHT - s - 2);
draw_icon_clip(canvas, card_graphics, right, top, px, py, s, s, draw_icon_clip(canvas, card_graphics, right, top, px, py, s, s, secondary);
secondary); draw_icon_clip_flipped(canvas, card_graphics, left, bottom, px, py, s, s, secondary);
draw_icon_clip_flipped(canvas, card_graphics, left, bottom, px, py, s, s,
secondary);
drawInfo = letters[character]; drawInfo = letters[character];
px = drawInfo[0], py = drawInfo[1], s = drawInfo[2]; px = drawInfo[0], py = drawInfo[1], s = drawInfo[2];
@@ -92,62 +95,58 @@ draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character,
top = pos_y + 2; top = pos_y + 2;
bottom = (pos_y + CARD_HEIGHT - s - 2); bottom = (pos_y + CARD_HEIGHT - s - 2);
draw_icon_clip(canvas, card_graphics, left, top + 1, px, py, s, s, draw_icon_clip(canvas, card_graphics, left, top + 1, px, py, s, s, secondary);
secondary); draw_icon_clip_flipped(canvas, card_graphics, right, bottom - 1, px, py, s, s, secondary);
draw_icon_clip_flipped(canvas, card_graphics, right, bottom - 1, px, py, s, s,
secondary);
} }
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) {
draw_card_at_colored(pos_x, pos_y, pip, character, false, 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) { void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas) {
for (int i = count - 1; i >= 0; i--) { for(int i = count - 1; i >= 0; i--) {
draw_card_at(playerCardPositions[i][0], playerCardPositions[i][1], cards[i].pip, cards[i].character, canvas); draw_card_at(
playerCardPositions[i][0],
playerCardPositions[i][1],
cards[i].pip,
cards[i].character,
canvas);
} }
} }
Vector card_pos_at_index(uint8_t index) { Vector card_pos_at_index(uint8_t index) {
return (Vector) { return (Vector){playerCardPositions[index][0], playerCardPositions[index][1]};
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) {
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, White); 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_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); draw_icon_clip(canvas, card_graphics, pos_x + 1, pos_y + 1, 35, 0, 15, 21, Black);
} }
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;
if (deck_ptr->cards == NULL) { if(deck_ptr->cards == NULL) {
deck_ptr->deck_count = deck_count; deck_ptr->deck_count = deck_count;
deck_ptr->card_count = deck_count * 52; deck_ptr->card_count = deck_count * 52;
deck_ptr->cards = malloc(sizeof(Card) * deck_ptr->card_count); deck_ptr->cards = malloc(sizeof(Card) * deck_ptr->card_count);
} }
for (uint8_t deck = 0; deck < deck_count; deck++) { for(uint8_t deck = 0; deck < deck_count; deck++) {
for (uint8_t pip = 0; pip < 4; pip++) { for(uint8_t pip = 0; pip < 4; pip++) {
for (uint8_t label = 0; label < 13; label++) { for(uint8_t label = 0; label < 13; label++) {
deck_ptr->cards[counter] = (Card) deck_ptr->cards[counter] = (Card){pip, label, false, false};
{
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];
@@ -155,175 +154,196 @@ 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) score += 11; if((score + 11) <= 21)
else score++; score += 11;
else
score++;
} }
return score; return score;
} }
void draw_card_animation(Card animatingCard, Vector from, Vector control, Vector to, float t, bool extra_margin, void draw_card_animation(
Canvas *const canvas) { Card animatingCard,
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(currentPos.x, currentPos.y, animatingCard.pip, draw_card_at(
animatingCard.character, canvas); currentPos.x, currentPos.y, animatingCard.pip, 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(currentPos.x, currentPos.y, animatingCard.pip, draw_card_at(
animatingCard.character, canvas); currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas);
} }
} }
void init_hand(Hand *hand_ptr, uint8_t count) { void init_hand(Hand* hand_ptr, uint8_t count) {
hand_ptr->cards = malloc(sizeof(Card) * count); hand_ptr->cards = malloc(sizeof(Card) * count);
hand_ptr->index = 0; hand_ptr->index = 0;
hand_ptr->max = count; hand_ptr->max = count;
} }
void free_hand(Hand *hand_ptr) { void free_hand(Hand* hand_ptr) {
FURI_LOG_D("CARD", "Freeing hand"); FURI_LOG_D("CARD", "Freeing hand");
free(hand_ptr->cards); free(hand_ptr->cards);
} }
void add_to_hand(Hand *hand_ptr, Card card) { void add_to_hand(Hand* hand_ptr, Card card) {
FURI_LOG_D("CARD", "Adding to hand"); FURI_LOG_D("CARD", "Adding to hand");
if (hand_ptr->index < hand_ptr->max) { if(hand_ptr->index < hand_ptr->max) {
hand_ptr->cards[hand_ptr->index] = card; hand_ptr->cards[hand_ptr->index] = card;
hand_ptr->index++; hand_ptr->index++;
} }
} }
void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas *const canvas) { void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas* const canvas) {
if (highlighted) { if(highlighted) {
draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); 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); draw_rounded_box_frame(
canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White);
} else { } else {
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); 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); 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) { int first_non_flipped_card(Hand hand) {
for (int i = 0; i < hand.index; i++) { for(int i = 0; i < hand.index; i++) {
if (!hand.cards[i].flipped) { if(!hand.cards[i].flipped) {
return i; return i;
} }
} }
return hand.index; return hand.index;
} }
void draw_hand_column(Hand hand, int16_t pos_x, int16_t pos_y, int8_t highlight, Canvas *const canvas) { void draw_hand_column(
if (hand.index == 0) { 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); draw_card_space(pos_x, pos_y, highlight > 0, canvas);
if(highlight==0) if(highlight == 0)
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Inverse);
Inverse);
return; return;
} }
int loopEnd = hand.index; int loopEnd = hand.index;
int hStart = max(loopEnd-4, 0); int hStart = max(loopEnd - 4, 0);
int pos = 0; int pos = 0;
int first= first_non_flipped_card(hand); int first = first_non_flipped_card(hand);
bool wastop=false; bool wastop = false;
if(first>=0 && first<=hStart && highlight!=first){ if(first >= 0 && first <= hStart && highlight != first) {
if(first>0){ if(first > 0) {
draw_card_back_at(pos_x, pos_y + pos, canvas); draw_card_back_at(pos_x, pos_y + pos, canvas);
pos+=4; pos += 4;
hStart++; hStart++;
wastop=true; wastop = true;
} }
draw_card_at_colored(pos_x, pos_y + pos, hand.cards[first].pip, hand.cards[first].character, draw_card_at_colored(
false, pos_x, pos_y + pos, hand.cards[first].pip, hand.cards[first].character, false, canvas);
canvas); pos += 8;
pos+=8;
hStart++; hStart++;
} }
if(hStart>highlight && highlight>=0){ if(hStart > highlight && highlight >= 0) {
if(!wastop && first>0){ if(!wastop && first > 0) {
draw_card_back_at(pos_x, pos_y + pos, canvas); draw_card_back_at(pos_x, pos_y + pos, canvas);
pos+=4; pos += 4;
hStart++; hStart++;
} }
draw_card_at_colored(pos_x, pos_y + pos, hand.cards[highlight].pip, hand.cards[highlight].character, draw_card_at_colored(
true, pos_x,
canvas); pos_y + pos,
pos+=8; hand.cards[highlight].pip,
hand.cards[highlight].character,
true,
canvas);
pos += 8;
hStart++; hStart++;
} }
for (int i = hStart; i < loopEnd; i++, pos+=4) { for(int i = hStart; i < loopEnd; i++, pos += 4) {
if (hand.cards[i].flipped) { if(hand.cards[i].flipped) {
draw_card_back_at(pos_x, pos_y + pos, canvas); draw_card_back_at(pos_x, pos_y + pos, canvas);
if(i==highlight) if(i == highlight)
draw_rounded_box(canvas, pos_x+1, pos_y + pos+1, CARD_WIDTH - 2, CARD_HEIGHT - 2, draw_rounded_box(
Inverse); canvas, pos_x + 1, pos_y + pos + 1, CARD_WIDTH - 2, CARD_HEIGHT - 2, Inverse);
} else { } else {
draw_card_at_colored(pos_x, pos_y + pos, hand.cards[i].pip, hand.cards[i].character, draw_card_at_colored(
(i == highlight), pos_x,
canvas); pos_y + pos,
if(i == highlight || i==first) pos+=4; 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) { Card remove_from_deck(uint16_t index, Deck* deck) {
FURI_LOG_D("CARD", "Removing from deck"); FURI_LOG_D("CARD", "Removing from deck");
Card result = {0, 0, true, false}; Card result = {0, 0, true, false};
if (deck->card_count > 0) { if(deck->card_count > 0) {
deck->card_count--; deck->card_count--;
for (int i = 0, curr_index = 0; i <= deck->card_count; i++) { for(int i = 0, curr_index = 0; i <= deck->card_count; i++) {
if (i != index) { if(i != index) {
deck->cards[curr_index] = deck->cards[i]; deck->cards[curr_index] = deck->cards[i];
curr_index++; curr_index++;
} else { } else {
result = deck->cards[i]; result = deck->cards[i];
} }
} }
if (deck->index >= 0) { if(deck->index >= 0) {
deck->index--; deck->index--;
} }
} }
return result; return result;
} }
void extract_hand_region(Hand *hand, Hand *to, uint8_t start_index) { void extract_hand_region(Hand* hand, Hand* to, uint8_t start_index) {
FURI_LOG_D("CARD", "Extracting hand region"); FURI_LOG_D("CARD", "Extracting hand region");
if (start_index >= hand->index) return; if(start_index >= hand->index) return;
for (uint8_t i = start_index; i < hand->index; i++) { for(uint8_t i = start_index; i < hand->index; i++) {
add_to_hand(to, hand->cards[i]); add_to_hand(to, hand->cards[i]);
} }
hand->index = start_index; hand->index = start_index;
} }
void add_hand_region(Hand *to, Hand *from) { void add_hand_region(Hand* to, Hand* from) {
FURI_LOG_D("CARD", "Adding hand region"); FURI_LOG_D("CARD", "Adding hand region");
if ((to->index + from->index) <= to->max) { if((to->index + from->index) <= to->max) {
for (int i = 0; i < from->index; i++) { for(int i = 0; i < from->index; i++) {
add_to_hand(to, from->cards[i]); add_to_hand(to, from->cards[i]);
} }
} }

View File

@@ -12,27 +12,27 @@
//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 disabled;
bool flipped; 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 card_count; int card_count;
int index; //Card index (to know where we at in the deck) int index; //Card index (to know where we at in the deck)
} Deck; } Deck;
typedef struct { typedef struct {
Card *cards; //Cards in the deck Card* cards; //Cards in the deck
uint8_t index; //Current index uint8_t index; //Current index
uint8_t max; //How many cards we want to store uint8_t max; //How many cards we want to store
} Hand; } Hand;
//endregion //endregion
void set_card_graphics(const Icon *graphics); 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).
@@ -51,7 +51,7 @@ 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) * Draws card at a given coordinate (top-left corner)
@@ -63,8 +63,13 @@ void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Ca
* @param inverted Invert colors * @param inverted Invert colors
* @param canvas Pointer to Flipper's canvas object * @param canvas Pointer to Flipper's canvas object
*/ */
void void draw_card_at_colored(
draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, bool inverted, Canvas *const canvas); 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
@@ -73,7 +78,7 @@ draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character,
* @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)
@@ -82,7 +87,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
@@ -90,14 +95,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
@@ -106,7 +111,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
@@ -119,28 +124,34 @@ 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(Card animatingCard, Vector from, Vector control, Vector to, float t, bool extra_margin, void draw_card_animation(
Canvas *const canvas); Card animatingCard,
Vector from,
Vector control,
Vector to,
float t,
bool extra_margin,
Canvas* const canvas);
/** /**
* Init hand pointer * Init hand pointer
* @param hand_ptr Pointer to hand * @param hand_ptr Pointer to hand
* @param count Number of cards we want to store * @param count Number of cards we want to store
*/ */
void init_hand(Hand *hand_ptr, uint8_t count); void init_hand(Hand* hand_ptr, uint8_t count);
/** /**
* Free hand resources * Free hand resources
* @param hand_ptr Pointer to hand * @param hand_ptr Pointer to hand
*/ */
void free_hand(Hand *hand_ptr); void free_hand(Hand* hand_ptr);
/** /**
* Add card to hand * Add card to hand
* @param hand_ptr Pointer to hand * @param hand_ptr Pointer to hand
* @param card Card to add * @param card Card to add
*/ */
void add_to_hand(Hand *hand_ptr, Card card); void add_to_hand(Hand* hand_ptr, Card card);
/** /**
* Draw card placement position at coordinate * Draw card placement position at coordinate
@@ -149,7 +160,7 @@ void add_to_hand(Hand *hand_ptr, Card card);
* @param highlighted Apply highlight effect * @param highlighted Apply highlight effect
* @param canvas Canvas object * @param canvas Canvas object
*/ */
void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas *const canvas); 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 * Draws a column of card, displaying the last [max_cards] cards on the list
@@ -159,8 +170,12 @@ void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas *con
* @param highlight Index to highlight, negative means no highlight * @param highlight Index to highlight, negative means no highlight
* @param canvas Canvas object * @param canvas Canvas object
*/ */
void void draw_hand_column(
draw_hand_column(Hand hand, int16_t pos_x, int16_t pos_y, int8_t highlight, Canvas *const canvas); 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) * 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)
@@ -168,10 +183,10 @@ draw_hand_column(Hand hand, int16_t pos_x, int16_t pos_y, int8_t highlight, Canv
* @param deck Deck reference * @param deck Deck reference
* @return The removed card * @return The removed card
*/ */
Card remove_from_deck(uint16_t index, Deck *deck); Card remove_from_deck(uint16_t index, Deck* deck);
int first_non_flipped_card(Hand hand); int first_non_flipped_card(Hand hand);
void extract_hand_region(Hand *hand, Hand *to, uint8_t start_index); void extract_hand_region(Hand* hand, Hand* to, uint8_t start_index);
void add_hand_region(Hand *to, Hand *from); void add_hand_region(Hand* to, Hand* from);

View File

@@ -2,47 +2,40 @@
#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( return lerp_2d(lerp_2d(start, control, t), lerp_2d(control, end, t), t);
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) { return (Vector){a.x / length, a.y / length};
a.x / length,
a.y / length
};
} }
float vector_magnitude(Vector a) { float vector_magnitude(Vector a) {

View File

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

View File

@@ -1,43 +1,53 @@
#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) if(!state && menu->current_menu == index) move_menu(menu, 1);
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("MENU", "Iteration %i, current %i, direction %i, state %i", i, menu->current_menu,direction,menu->items[menu->current_menu].enabled?1:0); FURI_LOG_D(
if (direction < 0 && menu->current_menu == 0) { "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; 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("MENU", "After process current %i, direction %i, state %i", menu->current_menu,direction,menu->items[menu->current_menu].enabled?1:0); FURI_LOG_D(
if (menu->items[menu->current_menu].enabled) { "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); FURI_LOG_D("MENU", "Next menu %i", menu->current_menu);
return; return;
} }
@@ -46,44 +56,48 @@ 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, pos_x + menu->menu_width / 2, pos_y + 6, AlignCenter, AlignCenter, canvas_draw_str_aligned(
menu->items[menu->current_menu].name); canvas,
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,18 +4,19 @@
#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 *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; } 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;
/** /**
@@ -23,7 +24,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
@@ -32,7 +33,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
@@ -41,7 +42,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
@@ -49,7 +50,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
@@ -57,7 +58,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).
@@ -73,4 +74,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,15 +1,14 @@
#include "queue.h" #include "queue.h"
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) { if(queue_state->current != NULL && queue_state->current->render != NULL)
if (queue_state->current != NULL && queue_state->current->render != NULL) ((QueueItem*)queue_state->current)->render(app_state, canvas);
((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;
@@ -17,44 +16,48 @@ 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) if(queue_state->current->start != NULL) queue_state->current->start(app_state);
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(QueueState *queue_state, void *app_state, void enqueue(
void(*done)(void *state), void(*start)(void *state), QueueState* queue_state,
void (*render)(const void *state, Canvas *const canvas), uint32_t duration) { void* app_state,
QueueItem *next; void (*done)(void* state),
if (queue_state->current == NULL) { 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->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) if(next->start != NULL) next->start(app_state);
next->start(app_state);
} else { } else {
next = queue_state->current; 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 = malloc(sizeof(QueueItem));
next = next->next; next = next->next;
} }

View File

@@ -4,17 +4,19 @@
#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)(const void *state, Canvas *const canvas); //Callback for the rendering loop while this item is running void (*render)(
void (*start)(void *state); //Callback when this item is started running const void* state,
void *next; //Pointer to the next item Canvas* const canvas); //Callback for the rendering loop while this item is running
uint32_t duration; //duration of the item 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; } 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;
/** /**
@@ -27,15 +29,19 @@ 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(QueueState *queue_state, void *app_state, void enqueue(
void(*done)(void *state), void(*start)(void *state), QueueState* queue_state,
void (*render)(const void *state, Canvas *const canvas), uint32_t duration); 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 * 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.
@@ -43,7 +49,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)
@@ -52,7 +58,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)
@@ -61,4 +67,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

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

View File

@@ -10,49 +10,96 @@ typedef enum {
Black, Black,
White, White,
Inverse, Inverse,
Filled //Currently only for Icon clip drawing Filled //Currently only for Icon clip drawing
} DrawMode; } DrawMode;
// size is the screen size // size is the screen size
typedef struct { typedef struct {
uint8_t *data; uint8_t* data;
unsigned long iconId; unsigned long iconId;
} TileMap; } TileMap;
bool test_pixel(uint8_t *data, uint8_t x, uint8_t y, uint8_t w); 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); uint8_t* image_data(Canvas* const canvas, const Icon* icon);
uint32_t pixel_index(uint8_t x, uint8_t y); 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, void draw_icon_clip(
uint8_t h, DrawMode drawMode); 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, void draw_icon_clip_flipped(
uint8_t h, DrawMode drawMode); 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(
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_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 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_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 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); 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); 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 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); 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); bool in_screen(int16_t x, int16_t y);
void ui_cleanup(); void ui_cleanup();
uint8_t* get_buffer(Canvas *const canvas); uint8_t* get_buffer(Canvas* const canvas);
uint8_t* make_buffer(); uint8_t* make_buffer();
void clone_buffer(uint8_t* canvas, uint8_t* data); 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;
@@ -71,7 +71,6 @@ 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,32 +6,28 @@
#define LINE_HEIGHT 16 #define LINE_HEIGHT 16
#define ITEM_PADDING 4 #define ITEM_PADDING 4
const char MoneyMul[4] = { const char MoneyMul[4] = {'K', 'B', 'T', 'S'};
'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) if(max_card > 0) draw_deck((game_state->player_cards), max_card, canvas);
draw_deck((game_state->player_cards), max_card, canvas);
if (game_state->dealer_card_count > 0) if(game_state->dealer_card_count > 0) draw_card_back_at(13, 5, canvas);
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(2, 2, game_state->dealer_cards[1].pip, game_state->dealer_cards[1].character, draw_card_at(
canvas); 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; 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);
@@ -39,15 +35,16 @@ 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) {
void draw_play_menu(Canvas *const canvas, const GameState *game_state) { const char* menus[3] = {"Double", "Hit", "Stay"};
const char *menus[3] = {"Double", "Hit", "Stay"}; for(uint8_t m = 0; m < 3; m++) {
for (uint8_t m = 0; m < 3; m++) { if(m == 0 &&
if (m == 0 && (game_state->doubled || game_state->player_score < game_state->settings.round_price)) continue; (game_state->doubled || game_state->player_score < game_state->settings.round_price))
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 {
@@ -57,7 +54,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);
@@ -65,35 +62,34 @@ 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]) if(points[y * 128 + x]) canvas_draw_dot(canvas, x, y);
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;
} }
@@ -104,77 +100,87 @@ 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(
void draw_menu(Canvas *const canvas, const char *text, const char *value, int8_t y, bool left_caret, bool right_caret, Canvas* const canvas,
bool selected) { const char* text,
const char* value,
int8_t y,
bool left_caret,
bool right_caret,
bool selected) {
UNUSED(selected); UNUSED(selected);
if (y < 0 || y >= 64) return; if(y < 0 || y >= 64) return;
if (selected) { 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) if(left_caret) canvas_draw_str_aligned(canvas, 80, y + ITEM_PADDING, AlignLeft, AlignTop, "<");
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(canvas, "Start money", drawChar, draw_menu(
0 * LINE_HEIGHT + startY, canvas,
gameState->settings.starting_money > gameState->settings.round_price, "Start money",
gameState->settings.starting_money < 400, drawChar,
gameState->selectedMenu == 0 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); snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.round_price);
draw_menu(canvas, "Round price", drawChar, draw_menu(
1 * LINE_HEIGHT + startY, canvas,
gameState->settings.round_price > 10, "Round price",
gameState->settings.round_price < gameState->settings.starting_money, drawChar,
gameState->selectedMenu == 1 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); snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.animation_duration);
draw_menu(canvas, "Anim. length", drawChar, draw_menu(
2 * LINE_HEIGHT + startY, canvas,
gameState->settings.animation_duration > 0, "Anim. length",
gameState->settings.animation_duration < 2000, drawChar,
gameState->selectedMenu == 2 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); snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.message_duration);
draw_menu(canvas, "Popup time", drawChar, draw_menu(
3 * LINE_HEIGHT + startY, canvas,
gameState->settings.message_duration > 0, "Popup time",
gameState->settings.message_duration < 2000, drawChar,
gameState->selectedMenu == 3 3 * LINE_HEIGHT + startY,
); gameState->settings.message_duration > 0,
// draw_menu(canvas, "Sound", gameState->settings.sound_effects ? "Yes" : "No", gameState->settings.message_duration < 2000,
// 5 * LINE_HEIGHT + startY, gameState->selectedMenu == 3);
// true, // draw_menu(canvas, "Sound", gameState->settings.sound_effects ? "Yes" : "No",
// true, // 5 * LINE_HEIGHT + startY,
// gameState->selectedMenu == 5 // true,
// ); // 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,16 +1,17 @@
#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(APP_NAME, "Saving %s: %ld", CONF_ANIMATION_DURATION, settings.animation_duration); FURI_LOG_D(
flipper_format_update_uint32(file, CONF_ANIMATION_DURATION, &(settings.animation_duration), 1); 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); 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);
@@ -21,10 +22,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);
@@ -32,7 +33,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);
@@ -57,58 +58,57 @@ 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;

View File

@@ -78,7 +78,7 @@ static void app_free(DTMFDolphinApp* app) {
free(app); free(app);
} }
int32_t dtmf_dolphin_app(void *p) { int32_t dtmf_dolphin_app(void* p) {
UNUSED(p); UNUSED(p);
DTMFDolphinApp* app = app_alloc(); DTMFDolphinApp* app = app_alloc();

View File

@@ -1,11 +1,11 @@
#include "dtmf_dolphin_audio.h" #include "dtmf_dolphin_audio.h"
DTMFDolphinAudio *current_player; DTMFDolphinAudio* current_player;
static void dtmf_dolphin_audio_dma_isr(void* ctx) { static void dtmf_dolphin_audio_dma_isr(void* ctx) {
FuriMessageQueue *event_queue = ctx; FuriMessageQueue* event_queue = ctx;
if (LL_DMA_IsActiveFlag_HT1(DMA1)) { if(LL_DMA_IsActiveFlag_HT1(DMA1)) {
LL_DMA_ClearFlag_HT1(DMA1); LL_DMA_ClearFlag_HT1(DMA1);
DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAHalfTransfer}; DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAHalfTransfer};
@@ -21,13 +21,13 @@ static void dtmf_dolphin_audio_dma_isr(void* ctx) {
} }
void dtmf_dolphin_audio_clear_samples(DTMFDolphinAudio* player) { void dtmf_dolphin_audio_clear_samples(DTMFDolphinAudio* player) {
for (size_t i = 0; i < player->buffer_length; i++) { for(size_t i = 0; i < player->buffer_length; i++) {
player->sample_buffer[i] = 0; player->sample_buffer[i] = 0;
} }
} }
DTMFDolphinOsc* dtmf_dolphin_osc_alloc() { DTMFDolphinOsc* dtmf_dolphin_osc_alloc() {
DTMFDolphinOsc *osc = malloc(sizeof(DTMFDolphinOsc)); DTMFDolphinOsc* osc = malloc(sizeof(DTMFDolphinOsc));
osc->cached_freq = 0; osc->cached_freq = 0;
osc->offset = 0; osc->offset = 0;
osc->period = 0; osc->period = 0;
@@ -36,7 +36,7 @@ DTMFDolphinOsc* dtmf_dolphin_osc_alloc() {
} }
DTMFDolphinPulseFilter* dtmf_dolphin_pulse_filter_alloc() { DTMFDolphinPulseFilter* dtmf_dolphin_pulse_filter_alloc() {
DTMFDolphinPulseFilter *pf = malloc(sizeof(DTMFDolphinPulseFilter)); DTMFDolphinPulseFilter* pf = malloc(sizeof(DTMFDolphinPulseFilter));
pf->duration = 0; pf->duration = 0;
pf->period = 0; pf->period = 0;
pf->offset = 0; pf->offset = 0;
@@ -45,7 +45,7 @@ DTMFDolphinPulseFilter* dtmf_dolphin_pulse_filter_alloc() {
} }
DTMFDolphinAudio* dtmf_dolphin_audio_alloc() { DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
DTMFDolphinAudio *player = malloc(sizeof(DTMFDolphinAudio)); DTMFDolphinAudio* player = malloc(sizeof(DTMFDolphinAudio));
player->buffer_length = SAMPLE_BUFFER_LENGTH; player->buffer_length = SAMPLE_BUFFER_LENGTH;
player->half_buffer_length = SAMPLE_BUFFER_LENGTH / 2; player->half_buffer_length = SAMPLE_BUFFER_LENGTH / 2;
player->sample_buffer = malloc(sizeof(uint16_t) * player->buffer_length); player->sample_buffer = malloc(sizeof(uint16_t) * player->buffer_length);
@@ -61,56 +61,58 @@ DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
} }
size_t calc_waveform_period(float freq) { size_t calc_waveform_period(float freq) {
if (!freq) { if(!freq) {
return 0; return 0;
} }
// DMA Rate calculation, thanks to Dr_Zlo // DMA Rate calculation, thanks to Dr_Zlo
float dma_rate = CPU_CLOCK_FREQ \ float dma_rate = CPU_CLOCK_FREQ / 2 / DTMF_DOLPHIN_HAL_DMA_PRESCALER /
/ 2 \ (DTMF_DOLPHIN_HAL_DMA_AUTORELOAD + 1);
/ DTMF_DOLPHIN_HAL_DMA_PRESCALER \
/ (DTMF_DOLPHIN_HAL_DMA_AUTORELOAD + 1);
// Using a constant scaling modifier, which likely represents // Using a constant scaling modifier, which likely represents
// the combined system overhead and isr latency. // the combined system overhead and isr latency.
return (uint16_t) dma_rate * 2 / freq * 0.801923; return (uint16_t)dma_rate * 2 / freq * 0.801923;
} }
void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) { void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) {
if (osc->lookup_table != NULL) { if(osc->lookup_table != NULL) {
free(osc->lookup_table); free(osc->lookup_table);
} }
osc->offset = 0; osc->offset = 0;
osc->cached_freq = freq; osc->cached_freq = freq;
osc->period = calc_waveform_period(freq); osc->period = calc_waveform_period(freq);
if (!osc->period) { if(!osc->period) {
osc->lookup_table = NULL; osc->lookup_table = NULL;
return; return;
} }
osc->lookup_table = malloc(sizeof(float) * osc->period); osc->lookup_table = malloc(sizeof(float) * osc->period);
for (size_t i = 0; i < osc->period; i++) { for(size_t i = 0; i < osc->period; i++) {
osc->lookup_table[i] = sin(i * PERIOD_2_PI / osc->period) + 1; osc->lookup_table[i] = sin(i * PERIOD_2_PI / osc->period) + 1;
} }
} }
void filter_generate_lookup_table(DTMFDolphinPulseFilter* pf, uint16_t pulses, uint16_t pulse_ms, uint16_t gap_ms) { void filter_generate_lookup_table(
if (pf->lookup_table != NULL) { DTMFDolphinPulseFilter* pf,
uint16_t pulses,
uint16_t pulse_ms,
uint16_t gap_ms) {
if(pf->lookup_table != NULL) {
free(pf->lookup_table); free(pf->lookup_table);
} }
pf->offset = 0; pf->offset = 0;
uint16_t gap_period = calc_waveform_period(1000 / (float) gap_ms); uint16_t gap_period = calc_waveform_period(1000 / (float)gap_ms);
uint16_t pulse_period = calc_waveform_period(1000 / (float) pulse_ms); uint16_t pulse_period = calc_waveform_period(1000 / (float)pulse_ms);
pf->period = pulse_period + gap_period; pf->period = pulse_period + gap_period;
if (!pf->period) { if(!pf->period) {
pf->lookup_table = NULL; pf->lookup_table = NULL;
return; return;
} }
pf->duration = pf->period * pulses; pf->duration = pf->period * pulses;
pf->lookup_table = malloc(sizeof(bool) * pf->duration); pf->lookup_table = malloc(sizeof(bool) * pf->duration);
for (size_t i = 0; i < pf->duration; i++) { for(size_t i = 0; i < pf->duration; i++) {
pf->lookup_table[i] = i % pf->period < pulse_period; pf->lookup_table[i] = i % pf->period < pulse_period;
} }
} }
@@ -118,7 +120,7 @@ void filter_generate_lookup_table(DTMFDolphinPulseFilter* pf, uint16_t pulses, u
float sample_frame(DTMFDolphinOsc* osc) { float sample_frame(DTMFDolphinOsc* osc) {
float frame = 0.0; float frame = 0.0;
if (osc->period) { if(osc->period) {
frame = osc->lookup_table[osc->offset]; frame = osc->lookup_table[osc->offset];
osc->offset = (osc->offset + 1) % osc->period; osc->offset = (osc->offset + 1) % osc->period;
} }
@@ -129,8 +131,8 @@ float sample_frame(DTMFDolphinOsc* osc) {
bool sample_filter(DTMFDolphinPulseFilter* pf) { bool sample_filter(DTMFDolphinPulseFilter* pf) {
bool frame = true; bool frame = true;
if (pf->duration) { if(pf->duration) {
if (pf->offset < pf->duration) { if(pf->offset < pf->duration) {
frame = pf->lookup_table[pf->offset]; frame = pf->lookup_table[pf->offset];
pf->offset = pf->offset + 1; pf->offset = pf->offset + 1;
} else { } else {
@@ -142,14 +144,14 @@ bool sample_filter(DTMFDolphinPulseFilter* pf) {
} }
void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) { void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) {
if (osc->lookup_table != NULL) { if(osc->lookup_table != NULL) {
free(osc->lookup_table); free(osc->lookup_table);
} }
free(osc); free(osc);
} }
void dtmf_dolphin_filter_free(DTMFDolphinPulseFilter* pf) { void dtmf_dolphin_filter_free(DTMFDolphinPulseFilter* pf) {
if (pf->lookup_table != NULL) { if(pf->lookup_table != NULL) {
free(pf->lookup_table); free(pf->lookup_table);
} }
free(pf); free(pf);
@@ -165,22 +167,19 @@ void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) {
current_player = NULL; current_player = NULL;
} }
bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) { bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) {
uint16_t* sample_buffer_start = &player->sample_buffer[buffer_index]; uint16_t* sample_buffer_start = &player->sample_buffer[buffer_index];
for (size_t i = 0; i < player->half_buffer_length; i++) { for(size_t i = 0; i < player->half_buffer_length; i++) {
float data = 0; float data = 0;
if (player->osc2->period) { if(player->osc2->period) {
data = \ data = (sample_frame(player->osc1) / 2) + (sample_frame(player->osc2) / 2);
(sample_frame(player->osc1) / 2) + \
(sample_frame(player->osc2) / 2);
} else { } else {
data = (sample_frame(player->osc1)); data = (sample_frame(player->osc1));
} }
data *= sample_filter(player->filter) ? player->volume : 0.0; data *= sample_filter(player->filter) ? player->volume : 0.0;
data *= UINT8_MAX / 2; // scale -128..127 data *= UINT8_MAX / 2; // scale -128..127
data += UINT8_MAX / 2; // to unsigned data += UINT8_MAX / 2; // to unsigned
if(data < 0) { if(data < 0) {
data = 0; data = 0;
@@ -196,8 +195,13 @@ bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) {
return true; return true;
} }
bool dtmf_dolphin_audio_play_tones(float freq1, float freq2, uint16_t pulses, uint16_t pulse_ms, uint16_t gap_ms) { bool dtmf_dolphin_audio_play_tones(
if (current_player != NULL && current_player->playing) { float freq1,
float freq2,
uint16_t pulses,
uint16_t pulse_ms,
uint16_t gap_ms) {
if(current_player != NULL && current_player->playing) {
// Cannot start playing while still playing something else // Cannot start playing while still playing something else
return false; return false;
} }
@@ -213,7 +217,8 @@ bool dtmf_dolphin_audio_play_tones(float freq1, float freq2, uint16_t pulses, ui
dtmf_dolphin_speaker_init(); dtmf_dolphin_speaker_init();
dtmf_dolphin_dma_init((uint32_t)current_player->sample_buffer, current_player->buffer_length); dtmf_dolphin_dma_init((uint32_t)current_player->sample_buffer, current_player->buffer_length);
furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, dtmf_dolphin_audio_dma_isr, current_player->queue); furi_hal_interrupt_set_isr(
FuriHalInterruptIdDma1Ch1, dtmf_dolphin_audio_dma_isr, current_player->queue);
dtmf_dolphin_dma_start(); dtmf_dolphin_dma_start();
dtmf_dolphin_speaker_start(); dtmf_dolphin_speaker_start();
@@ -222,11 +227,12 @@ bool dtmf_dolphin_audio_play_tones(float freq1, float freq2, uint16_t pulses, ui
} }
bool dtmf_dolphin_audio_stop_tones() { bool dtmf_dolphin_audio_stop_tones() {
if (current_player != NULL && !current_player->playing) { if(current_player != NULL && !current_player->playing) {
// Can't stop a player that isn't playing. // Can't stop a player that isn't playing.
return false; return false;
} }
while(current_player->filter->offset > 0 && current_player->filter->offset < current_player->filter->duration) { while(current_player->filter->offset > 0 &&
current_player->filter->offset < current_player->filter->duration) {
// run remaining ticks if needed to complete filter sequence // run remaining ticks if needed to complete filter sequence
dtmf_dolphin_audio_handle_tick(); dtmf_dolphin_audio_handle_tick();
} }
@@ -243,13 +249,13 @@ bool dtmf_dolphin_audio_stop_tones() {
bool dtmf_dolphin_audio_handle_tick() { bool dtmf_dolphin_audio_handle_tick() {
bool handled = false; bool handled = false;
if (current_player) { if(current_player) {
DTMFDolphinCustomEvent event; DTMFDolphinCustomEvent event;
if(furi_message_queue_get(current_player->queue, &event, 250) == FuriStatusOk) { if(furi_message_queue_get(current_player->queue, &event, 250) == FuriStatusOk) {
if(event.type == DTMFDolphinEventDMAHalfTransfer) { if(event.type == DTMFDolphinEventDMAHalfTransfer) {
generate_waveform(current_player, 0); generate_waveform(current_player, 0);
handled = true; handled = true;
} else if (event.type == DTMFDolphinEventDMAFullTransfer) { } else if(event.type == DTMFDolphinEventDMAFullTransfer) {
generate_waveform(current_player, current_player->half_buffer_length); generate_waveform(current_player, current_player->half_buffer_length);
handled = true; handled = true;
} }

View File

@@ -24,13 +24,13 @@ typedef struct {
typedef struct { typedef struct {
size_t buffer_length; size_t buffer_length;
size_t half_buffer_length; size_t half_buffer_length;
uint8_t *buffer_buffer; uint8_t* buffer_buffer;
uint16_t *sample_buffer; uint16_t* sample_buffer;
float volume; float volume;
FuriMessageQueue *queue; FuriMessageQueue* queue;
DTMFDolphinOsc *osc1; DTMFDolphinOsc* osc1;
DTMFDolphinOsc *osc2; DTMFDolphinOsc* osc2;
DTMFDolphinPulseFilter *filter; DTMFDolphinPulseFilter* filter;
bool playing; bool playing;
} DTMFDolphinAudio; } DTMFDolphinAudio;
@@ -42,7 +42,12 @@ void dtmf_dolphin_audio_free(DTMFDolphinAudio* player);
void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc); void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc);
bool dtmf_dolphin_audio_play_tones(float freq1, float freq2, uint16_t pulses, uint16_t pulse_ms, uint16_t gap_ms); bool dtmf_dolphin_audio_play_tones(
float freq1,
float freq2,
uint16_t pulses,
uint16_t pulse_ms,
uint16_t gap_ms);
bool dtmf_dolphin_audio_stop_tones(); bool dtmf_dolphin_audio_stop_tones();

View File

@@ -7,13 +7,13 @@ typedef struct {
} DTMFDolphinTonePos; } DTMFDolphinTonePos;
typedef struct { typedef struct {
const char *name; const char* name;
const float frequency_1; const float frequency_1;
const float frequency_2; const float frequency_2;
const DTMFDolphinTonePos pos; const DTMFDolphinTonePos pos;
const uint16_t pulses; // for Redbox const uint16_t pulses; // for Redbox
const uint16_t pulse_ms; // for Redbox const uint16_t pulse_ms; // for Redbox
const uint16_t gap_duration; // for Redbox const uint16_t gap_duration; // for Redbox
} DTMFDolphinTones; } DTMFDolphinTones;
typedef struct { typedef struct {
@@ -44,52 +44,48 @@ DTMFDolphinSceneData DTMFDolphinSceneDataDialer = {
{"0", 941.0, 1336.0, {3, 1, 1}, 0, 0, 0}, {"0", 941.0, 1336.0, {3, 1, 1}, 0, 0, 0},
{"#", 941.0, 1477.0, {3, 2, 1}, 0, 0, 0}, {"#", 941.0, 1477.0, {3, 2, 1}, 0, 0, 0},
{"D", 941.0, 1633.0, {3, 3, 1}, 0, 0, 0}, {"D", 941.0, 1633.0, {3, 3, 1}, 0, 0, 0},
} }};
};
DTMFDolphinSceneData DTMFDolphinSceneDataBluebox = { DTMFDolphinSceneData DTMFDolphinSceneDataBluebox = {
.name = "Bluebox", .name = "Bluebox",
.block = DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX, .block = DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX,
.tone_count = 13, .tone_count = 13,
.tones = { .tones = {
{"1", 700.0, 900.0, {0, 0, 1}, 0, 0, 0}, {"1", 700.0, 900.0, {0, 0, 1}, 0, 0, 0},
{"2", 700.0, 1100.0, {0, 1, 1}, 0, 0, 0}, {"2", 700.0, 1100.0, {0, 1, 1}, 0, 0, 0},
{"3", 900.0, 1100.0, {0, 2, 1}, 0, 0, 0}, {"3", 900.0, 1100.0, {0, 2, 1}, 0, 0, 0},
{"4", 700.0, 1300.0, {1, 0, 1}, 0, 0, 0}, {"4", 700.0, 1300.0, {1, 0, 1}, 0, 0, 0},
{"5", 900.0, 1300.0, {1, 1, 1}, 0, 0, 0}, {"5", 900.0, 1300.0, {1, 1, 1}, 0, 0, 0},
{"6", 1100.0, 1300.0, {1, 2, 1}, 0, 0, 0}, {"6", 1100.0, 1300.0, {1, 2, 1}, 0, 0, 0},
{"7", 700.0, 1500.0, {2, 0, 1}, 0, 0, 0}, {"7", 700.0, 1500.0, {2, 0, 1}, 0, 0, 0},
{"8", 900.0, 1500.0, {2, 1, 1}, 0, 0, 0}, {"8", 900.0, 1500.0, {2, 1, 1}, 0, 0, 0},
{"9", 1100.0, 1500.0, {2, 2, 1}, 0, 0, 0}, {"9", 1100.0, 1500.0, {2, 2, 1}, 0, 0, 0},
{"0", 1300.0, 1500.0, {3, 1, 1}, 0, 0, 0}, {"0", 1300.0, 1500.0, {3, 1, 1}, 0, 0, 0},
{"KP", 1100.0, 1700.0, {0, 3, 2}, 0, 0, 0}, {"KP", 1100.0, 1700.0, {0, 3, 2}, 0, 0, 0},
{"ST", 1500.0, 1700.0, {1, 3, 2}, 0, 0, 0}, {"ST", 1500.0, 1700.0, {1, 3, 2}, 0, 0, 0},
{"2600", 2600.0, 0.0, {3, 2, 3}, 0, 0, 0}, {"2600", 2600.0, 0.0, {3, 2, 3}, 0, 0, 0},
} }};
};
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUS = { DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUS = {
.name = "Redbox (US)", .name = "Redbox (US)",
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US, .block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US,
.tone_count = 4, .tone_count = 4,
.tones = { .tones = {
{"Nickel", 1700.0, 2200.0, {0, 0, 5}, 1, 66, 0}, {"Nickel", 1700.0, 2200.0, {0, 0, 5}, 1, 66, 0},
{"Dime", 1700.0, 2200.0, {1, 0, 5}, 2, 66, 66}, {"Dime", 1700.0, 2200.0, {1, 0, 5}, 2, 66, 66},
{"Quarter", 1700.0, 2200.0, {2, 0, 5}, 5, 33, 33}, {"Quarter", 1700.0, 2200.0, {2, 0, 5}, 5, 33, 33},
{"Dollar", 1700.0, 2200.0, {3, 0, 5}, 1, 650, 0}, {"Dollar", 1700.0, 2200.0, {3, 0, 5}, 1, 650, 0},
} }};
};
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxCA = { DTMFDolphinSceneData DTMFDolphinSceneDataRedboxCA = {
.name = "Redbox (CA)", .name = "Redbox (CA)",
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA, .block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA,
.tone_count = 3, .tone_count = 3,
.tones = { .tones = {
{"Nickel", 2200.0, 0.0, {0, 0, 5}, 1, 66, 0}, {"Nickel", 2200.0, 0.0, {0, 0, 5}, 1, 66, 0},
{"Dime", 2200.0, 0.0, {1, 0, 5}, 2, 66, 66}, {"Dime", 2200.0, 0.0, {1, 0, 5}, 2, 66, 66},
{"Quarter", 2200.0, 0.0, {2, 0, 5}, 5, 33, 33}, {"Quarter", 2200.0, 0.0, {2, 0, 5}, 5, 33, 33},
} }};
};
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = { DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = {
.name = "Redbox (UK)", .name = "Redbox (UK)",
@@ -98,28 +94,25 @@ DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = {
.tones = { .tones = {
{"10p", 1000.0, 0.0, {0, 0, 5}, 1, 200, 0}, {"10p", 1000.0, 0.0, {0, 0, 5}, 1, 200, 0},
{"50p", 1000.0, 0.0, {1, 0, 5}, 1, 350, 0}, {"50p", 1000.0, 0.0, {1, 0, 5}, 1, 350, 0},
} }};
};
DTMFDolphinSceneData DTMFDolphinSceneDataMisc = { DTMFDolphinSceneData DTMFDolphinSceneDataMisc = {
.name = "Misc", .name = "Misc",
.block = DTMF_DOLPHIN_TONE_BLOCK_MISC, .block = DTMF_DOLPHIN_TONE_BLOCK_MISC,
.tone_count = 3, .tone_count = 3,
.tones = { .tones = {
{"CCITT 11", 700.0, 1700.0, {0, 0, 5}, 0, 0, 0}, {"CCITT 11", 700.0, 1700.0, {0, 0, 5}, 0, 0, 0},
{"CCITT 12", 900.0, 1700.0, {1, 0, 5}, 0, 0, 0}, {"CCITT 12", 900.0, 1700.0, {1, 0, 5}, 0, 0, 0},
{"CCITT KP2", 1300.0, 1700.0, {2, 0, 5}, 0, 0, 0}, {"CCITT KP2", 1300.0, 1700.0, {2, 0, 5}, 0, 0, 0},
} }};
};
DTMFDolphinToneSection current_section; DTMFDolphinToneSection current_section;
DTMFDolphinSceneData *current_scene_data; DTMFDolphinSceneData* current_scene_data;
void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section) { void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section) {
current_section = section; current_section = section;
switch (current_section) switch(current_section) {
{
case DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX: case DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX:
current_scene_data = &DTMFDolphinSceneDataBluebox; current_scene_data = &DTMFDolphinSceneDataBluebox;
break; break;
@@ -128,7 +121,7 @@ void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section) {
break; break;
case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA: case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA:
current_scene_data = &DTMFDolphinSceneDataRedboxCA; current_scene_data = &DTMFDolphinSceneDataRedboxCA;
break; break;
case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK: case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK:
current_scene_data = &DTMFDolphinSceneDataRedboxUK; current_scene_data = &DTMFDolphinSceneDataRedboxUK;
break; break;
@@ -145,14 +138,14 @@ DTMFDolphinToneSection dtmf_dolphin_data_get_current_section() {
return current_section; return current_section;
} }
DTMFDolphinSceneData *dtmf_dolphin_data_get_current_scene_data() { DTMFDolphinSceneData* dtmf_dolphin_data_get_current_scene_data() {
return current_scene_data; return current_scene_data;
} }
bool dtmf_dolphin_data_get_tone_frequencies(float *freq1, float *freq2, uint8_t row, uint8_t col) { bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t row, uint8_t col) {
for (size_t i = 0; i < current_scene_data->tone_count; i++) { for(size_t i = 0; i < current_scene_data->tone_count; i++) {
DTMFDolphinTones tones = current_scene_data->tones[i]; DTMFDolphinTones tones = current_scene_data->tones[i];
if (tones.pos.row == row && tones.pos.col == col) { if(tones.pos.row == row && tones.pos.col == col) {
freq1[0] = tones.frequency_1; freq1[0] = tones.frequency_1;
freq2[0] = tones.frequency_2; freq2[0] = tones.frequency_2;
return true; return true;
@@ -161,10 +154,15 @@ bool dtmf_dolphin_data_get_tone_frequencies(float *freq1, float *freq2, uint8_t
return false; return false;
} }
bool dtmf_dolphin_data_get_filter_data(uint16_t *pulses, uint16_t *pulse_ms, uint16_t *gap_ms, uint8_t row, uint8_t col) { bool dtmf_dolphin_data_get_filter_data(
for (size_t i = 0; i < current_scene_data->tone_count; i++) { uint16_t* pulses,
uint16_t* pulse_ms,
uint16_t* gap_ms,
uint8_t row,
uint8_t col) {
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
DTMFDolphinTones tones = current_scene_data->tones[i]; DTMFDolphinTones tones = current_scene_data->tones[i];
if (tones.pos.row == row && tones.pos.col == col) { if(tones.pos.row == row && tones.pos.col == col) {
pulses[0] = tones.pulses; pulses[0] = tones.pulses;
pulse_ms[0] = tones.pulse_ms; pulse_ms[0] = tones.pulse_ms;
gap_ms[0] = tones.gap_duration; gap_ms[0] = tones.gap_duration;
@@ -175,9 +173,9 @@ bool dtmf_dolphin_data_get_filter_data(uint16_t *pulses, uint16_t *pulse_ms, uin
} }
const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col) { const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col) {
for (size_t i = 0; i < current_scene_data->tone_count; i++) { for(size_t i = 0; i < current_scene_data->tone_count; i++) {
DTMFDolphinTones tones = current_scene_data->tones[i]; DTMFDolphinTones tones = current_scene_data->tones[i];
if (tones.pos.row == row && tones.pos.col == col) { if(tones.pos.row == row && tones.pos.col == col) {
return tones.name; return tones.name;
} }
} }
@@ -185,7 +183,7 @@ const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col) {
} }
const char* dtmf_dolphin_data_get_current_section_name() { const char* dtmf_dolphin_data_get_current_section_name() {
if (current_scene_data) { if(current_scene_data) {
return current_scene_data->name; return current_scene_data->name;
} }
return NULL; return NULL;
@@ -195,27 +193,26 @@ void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t
max_rows[0] = 0; max_rows[0] = 0;
max_cols[0] = 0; max_cols[0] = 0;
max_span[0] = 0; max_span[0] = 0;
uint8_t tmp_rowspan[5] = { 0, 0, 0, 0, 0 }; uint8_t tmp_rowspan[5] = {0, 0, 0, 0, 0};
for (size_t i = 0; i < current_scene_data->tone_count; i++) { for(size_t i = 0; i < current_scene_data->tone_count; i++) {
DTMFDolphinTones tones = current_scene_data->tones[i]; DTMFDolphinTones tones = current_scene_data->tones[i];
if (tones.pos.row > max_rows[0]) { if(tones.pos.row > max_rows[0]) {
max_rows[0] = tones.pos.row; max_rows[0] = tones.pos.row;
} }
if (tones.pos.col > max_cols[0]) { if(tones.pos.col > max_cols[0]) {
max_cols[0] = tones.pos.col; max_cols[0] = tones.pos.col;
} }
tmp_rowspan[tones.pos.row] += tones.pos.span; tmp_rowspan[tones.pos.row] += tones.pos.span;
if (tmp_rowspan[tones.pos.row] > max_span[0]) if(tmp_rowspan[tones.pos.row] > max_span[0]) max_span[0] = tmp_rowspan[tones.pos.row];
max_span[0] = tmp_rowspan[tones.pos.row];
} }
max_rows[0]++; max_rows[0]++;
max_cols[0]++; max_cols[0]++;
} }
uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col) { uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col) {
for (size_t i = 0; i < current_scene_data->tone_count; i++) { for(size_t i = 0; i < current_scene_data->tone_count; i++) {
DTMFDolphinTones tones = current_scene_data->tones[i]; DTMFDolphinTones tones = current_scene_data->tones[i];
if (tones.pos.row == row && tones.pos.col == col) { if(tones.pos.row == row && tones.pos.col == col) {
return tones.pos.span; return tones.pos.span;
} }
} }

View File

@@ -17,9 +17,14 @@ void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section);
DTMFDolphinToneSection dtmf_dolphin_data_get_current_section(); DTMFDolphinToneSection dtmf_dolphin_data_get_current_section();
bool dtmf_dolphin_data_get_tone_frequencies(float *freq1, float *freq2, uint8_t row, uint8_t col); bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t row, uint8_t col);
bool dtmf_dolphin_data_get_filter_data(uint16_t *pulses, uint16_t *pulse_ms, uint16_t *gap_ms, uint8_t row, uint8_t col); bool dtmf_dolphin_data_get_filter_data(
uint16_t* pulses,
uint16_t* pulse_ms,
uint16_t* gap_ms,
uint8_t row,
uint8_t col);
const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col); const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col);

View File

@@ -17,7 +17,6 @@
#define TAG "DTMFDolphin" #define TAG "DTMFDolphin"
enum DTMFDolphinSceneState { enum DTMFDolphinSceneState {
DTMFDolphinSceneStateDialer, DTMFDolphinSceneStateDialer,
DTMFDolphinSceneStateBluebox, DTMFDolphinSceneStateBluebox,
@@ -39,7 +38,4 @@ typedef struct {
NotificationApp* notification; NotificationApp* notification;
} DTMFDolphinApp; } DTMFDolphinApp;
typedef enum { typedef enum { DTMFDolphinViewMainMenu, DTMFDolphinViewDialer } DTMFDolphinView;
DTMFDolphinViewMainMenu,
DTMFDolphinViewDialer
} DTMFDolphinView;

View File

@@ -2,14 +2,12 @@
// #include "../dtmf_dolphin_data.h" // #include "../dtmf_dolphin_data.h"
// #include "../dtmf_dolphin_audio.h" // #include "../dtmf_dolphin_audio.h"
void dtmf_dolphin_scene_dialer_on_enter(void* context) {
void dtmf_dolphin_scene_dialer_on_enter(void *context) {
DTMFDolphinApp* app = context; DTMFDolphinApp* app = context;
DTMFDolphinScene scene_id = DTMFDolphinSceneDialer; DTMFDolphinScene scene_id = DTMFDolphinSceneDialer;
enum DTMFDolphinSceneState state = scene_manager_get_scene_state(app->scene_manager, scene_id); enum DTMFDolphinSceneState state = scene_manager_get_scene_state(app->scene_manager, scene_id);
switch (state) switch(state) {
{
case DTMFDolphinSceneStateBluebox: case DTMFDolphinSceneStateBluebox:
dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX); dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX);
break; break;

View File

@@ -3,8 +3,7 @@
static void dtmf_dolphin_scene_start_main_menu_enter_callback(void* context, uint32_t index) { static void dtmf_dolphin_scene_start_main_menu_enter_callback(void* context, uint32_t index) {
DTMFDolphinApp* app = context; DTMFDolphinApp* app = context;
uint8_t cust_event = 255; uint8_t cust_event = 255;
switch (index) switch(index) {
{
case 0: case 0:
cust_event = DTMFDolphinEventStartDialer; cust_event = DTMFDolphinEventStartDialer;
break; break;
@@ -24,10 +23,7 @@ static void dtmf_dolphin_scene_start_main_menu_enter_callback(void* context, uin
return; return;
} }
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(app->view_dispatcher, cust_event);
app->view_dispatcher,
cust_event
);
} }
void dtmf_dolphin_scene_start_on_enter(void* context) { void dtmf_dolphin_scene_start_on_enter(void* context) {
@@ -36,9 +32,7 @@ void dtmf_dolphin_scene_start_on_enter(void* context) {
// VariableItem* item; // VariableItem* item;
variable_item_list_set_enter_callback( variable_item_list_set_enter_callback(
var_item_list, var_item_list, dtmf_dolphin_scene_start_main_menu_enter_callback, app);
dtmf_dolphin_scene_start_main_menu_enter_callback,
app);
variable_item_list_add(var_item_list, "Dialer", 0, NULL, context); variable_item_list_add(var_item_list, "Dialer", 0, NULL, context);
variable_item_list_add(var_item_list, "Bluebox", 0, NULL, context); variable_item_list_add(var_item_list, "Bluebox", 0, NULL, context);
@@ -47,12 +41,9 @@ void dtmf_dolphin_scene_start_on_enter(void* context) {
variable_item_list_add(var_item_list, "Misc", 0, NULL, context); variable_item_list_add(var_item_list, "Misc", 0, NULL, context);
variable_item_list_set_selected_item( variable_item_list_set_selected_item(
var_item_list, var_item_list, scene_manager_get_scene_state(app->scene_manager, DTMFDolphinSceneStart));
scene_manager_get_scene_state(app->scene_manager, DTMFDolphinSceneStart));
view_dispatcher_switch_to_view( view_dispatcher_switch_to_view(app->view_dispatcher, DTMFDolphinViewMainMenu);
app->view_dispatcher,
DTMFDolphinViewMainMenu);
} }
bool dtmf_dolphin_scene_start_on_event(void* context, SceneManagerEvent event) { bool dtmf_dolphin_scene_start_on_event(void* context, SceneManagerEvent event) {
@@ -63,8 +54,7 @@ bool dtmf_dolphin_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
uint8_t sc_state; uint8_t sc_state;
switch (event.event) switch(event.event) {
{
case DTMFDolphinEventStartDialer: case DTMFDolphinEventStartDialer:
sc_state = DTMFDolphinSceneStateDialer; sc_state = DTMFDolphinSceneStateDialer;
break; break;

View File

@@ -7,4 +7,4 @@
#define DTMF_DOLPHIN_NUMPAD_Y 14 #define DTMF_DOLPHIN_NUMPAD_Y 14
#define DTMF_DOLPHIN_BUTTON_WIDTH 13 #define DTMF_DOLPHIN_BUTTON_WIDTH 13
#define DTMF_DOLPHIN_BUTTON_HEIGHT 13 #define DTMF_DOLPHIN_BUTTON_HEIGHT 13
#define DTMF_DOLPHIN_BUTTON_PADDING 1 // all sides #define DTMF_DOLPHIN_BUTTON_PADDING 1 // all sides

View File

@@ -24,53 +24,55 @@ static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_diale
static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dialer); static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dialer);
static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dialer); static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dialer);
static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_dialer); static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_dialer);
static bool dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event); static bool
dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event);
void draw_button(Canvas* canvas, uint8_t row, uint8_t col, bool invert) { void draw_button(Canvas* canvas, uint8_t row, uint8_t col, bool invert) {
uint8_t left = DTMF_DOLPHIN_NUMPAD_X + // ((col + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
uint8_t left = DTMF_DOLPHIN_NUMPAD_X + \ (col * DTMF_DOLPHIN_BUTTON_WIDTH);
// ((col + 1) * DTMF_DOLPHIN_BUTTON_PADDING) + // (col * DTMF_DOLPHIN_BUTTON_PADDING);
(col * DTMF_DOLPHIN_BUTTON_WIDTH); uint8_t top = DTMF_DOLPHIN_NUMPAD_Y + // ((row + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
// (col * DTMF_DOLPHIN_BUTTON_PADDING); (row * DTMF_DOLPHIN_BUTTON_HEIGHT);
uint8_t top = DTMF_DOLPHIN_NUMPAD_Y + \ // (row * DTMF_DOLPHIN_BUTTON_PADDING);
// ((row + 1) * DTMF_DOLPHIN_BUTTON_PADDING) +
(row * DTMF_DOLPHIN_BUTTON_HEIGHT);
// (row * DTMF_DOLPHIN_BUTTON_PADDING);
uint8_t span = dtmf_dolphin_get_tone_span(row, col); uint8_t span = dtmf_dolphin_get_tone_span(row, col);
if (span == 0) { if(span == 0) {
return; return;
} }
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
if (invert) if(invert)
canvas_draw_rbox(canvas, left, top, canvas_draw_rbox(
canvas,
left,
top,
(DTMF_DOLPHIN_BUTTON_WIDTH * span) - (DTMF_DOLPHIN_BUTTON_PADDING * 2), (DTMF_DOLPHIN_BUTTON_WIDTH * span) - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
DTMF_DOLPHIN_BUTTON_HEIGHT - (DTMF_DOLPHIN_BUTTON_PADDING * 2), DTMF_DOLPHIN_BUTTON_HEIGHT - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
2); 2);
else else
canvas_draw_rframe(canvas, left, top, canvas_draw_rframe(
canvas,
left,
top,
(DTMF_DOLPHIN_BUTTON_WIDTH * span) - (DTMF_DOLPHIN_BUTTON_PADDING * 2), (DTMF_DOLPHIN_BUTTON_WIDTH * span) - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
DTMF_DOLPHIN_BUTTON_HEIGHT- (DTMF_DOLPHIN_BUTTON_PADDING * 2), DTMF_DOLPHIN_BUTTON_HEIGHT - (DTMF_DOLPHIN_BUTTON_PADDING * 2),
2); 2);
if (invert) if(invert) canvas_invert_color(canvas);
canvas_invert_color(canvas);
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
// canvas_set_color(canvas, invert ? ColorWhite : ColorBlack); // canvas_set_color(canvas, invert ? ColorWhite : ColorBlack);
canvas_draw_str_aligned(canvas, canvas_draw_str_aligned(
left - 1 + (int) ((DTMF_DOLPHIN_BUTTON_WIDTH * span) / 2), canvas,
top + (int) (DTMF_DOLPHIN_BUTTON_HEIGHT / 2), left - 1 + (int)((DTMF_DOLPHIN_BUTTON_WIDTH * span) / 2),
top + (int)(DTMF_DOLPHIN_BUTTON_HEIGHT / 2),
AlignCenter, AlignCenter,
AlignCenter, AlignCenter,
dtmf_dolphin_data_get_tone_name(row, col)); dtmf_dolphin_data_get_tone_name(row, col));
if (invert) if(invert) canvas_invert_color(canvas);
canvas_invert_color(canvas);
} }
void draw_dialer(Canvas* canvas, void* _model) { void draw_dialer(Canvas* canvas, void* _model) {
@@ -82,9 +84,9 @@ void draw_dialer(Canvas* canvas, void* _model) {
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
for (int r = 0; r < max_rows; r++) { for(int r = 0; r < max_rows; r++) {
for (int c = 0; c < max_cols; c++) { for(int c = 0; c < max_cols; c++) {
if (model->row == r && model->col == c) if(model->row == r && model->col == c)
draw_button(canvas, r, c, true); draw_button(canvas, r, c, true);
else else
draw_button(canvas, r, c, false); draw_button(canvas, r, c, false);
@@ -92,14 +94,15 @@ void draw_dialer(Canvas* canvas, void* _model) {
} }
} }
void update_frequencies(DTMFDolphinDialerModel *model) { void update_frequencies(DTMFDolphinDialerModel* model) {
dtmf_dolphin_data_get_tone_frequencies(&model->freq1, &model->freq2, model->row, model->col); dtmf_dolphin_data_get_tone_frequencies(&model->freq1, &model->freq2, model->row, model->col);
dtmf_dolphin_data_get_filter_data(&model->pulses, &model->pulse_ms, &model->gap_ms, model->row, model->col); dtmf_dolphin_data_get_filter_data(
&model->pulses, &model->pulse_ms, &model->gap_ms, model->row, model->col);
} }
static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) { static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) {
DTMFDolphinDialerModel* model = _model; DTMFDolphinDialerModel* model = _model;
if (model->playing) { if(model->playing) {
// Leverage the prioritized draw callback to handle // Leverage the prioritized draw callback to handle
// the DMA so that it doesn't skip. // the DMA so that it doesn't skip.
dtmf_dolphin_audio_handle_tick(); dtmf_dolphin_audio_handle_tick();
@@ -122,46 +125,41 @@ static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) {
canvas_set_font(canvas, FontPrimary); canvas_set_font(canvas, FontPrimary);
elements_multiline_text(canvas, 2, 10, dtmf_dolphin_data_get_current_section_name()); elements_multiline_text(canvas, 2, 10, dtmf_dolphin_data_get_current_section_name());
canvas_draw_line(canvas, canvas_draw_line(
(max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1, 0, canvas,
(max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1, canvas_height(canvas)); (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1,
0,
(max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 1,
canvas_height(canvas));
elements_multiline_text(canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 10, "Detail"); elements_multiline_text(canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 10, "Detail");
canvas_draw_line(canvas, 0, DTMF_DOLPHIN_NUMPAD_Y - 3, canvas_width(canvas), DTMF_DOLPHIN_NUMPAD_Y - 3); canvas_draw_line(
canvas, 0, DTMF_DOLPHIN_NUMPAD_Y - 3, canvas_width(canvas), DTMF_DOLPHIN_NUMPAD_Y - 3);
// elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Dialer Mode"); // elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Dialer Mode");
draw_dialer(canvas, model); draw_dialer(canvas, model);
FuriString *output = furi_string_alloc(); FuriString* output = furi_string_alloc();
if (model->freq1 && model->freq2) { if(model->freq1 && model->freq2) {
furi_string_cat_printf( furi_string_cat_printf(
output, output,
"Dual Tone\nF1: %u Hz\nF2: %u Hz\n", "Dual Tone\nF1: %u Hz\nF2: %u Hz\n",
(unsigned int) model->freq1, (unsigned int)model->freq1,
(unsigned int) model->freq2); (unsigned int)model->freq2);
} else if (model->freq1) { } else if(model->freq1) {
furi_string_cat_printf( furi_string_cat_printf(output, "Single Tone\nF: %u Hz\n", (unsigned int)model->freq1);
output,
"Single Tone\nF: %u Hz\n",
(unsigned int) model->freq1);
} }
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
if (model->pulse_ms) { if(model->pulse_ms) {
furi_string_cat_printf( furi_string_cat_printf(output, "P: %u * %u ms\n", model->pulses, model->pulse_ms);
output,
"P: %u * %u ms\n",
model->pulses,
model->pulse_ms);
} }
if (model->gap_ms) { if(model->gap_ms) {
furi_string_cat_printf( furi_string_cat_printf(output, "Gaps: %u ms\n", model->gap_ms);
output,
"Gaps: %u ms\n",
model->gap_ms);
} }
elements_multiline_text(canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 21, furi_string_get_cstr(output)); elements_multiline_text(
canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 21, furi_string_get_cstr(output));
furi_string_free(output); furi_string_free(output);
} }
@@ -196,11 +194,11 @@ static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_diale
{ {
uint8_t span = 0; uint8_t span = 0;
uint8_t cursor = model->row; uint8_t cursor = model->row;
while (span == 0 && cursor > 0) { while(span == 0 && cursor > 0) {
cursor--; cursor--;
span = dtmf_dolphin_get_tone_span(cursor, model->col); span = dtmf_dolphin_get_tone_span(cursor, model->col);
} }
if (span != 0) { if(span != 0) {
model->row = cursor; model->row = cursor;
} }
}, },
@@ -224,7 +222,7 @@ static bool dtmf_dolphin_dialer_process_down(DTMFDolphinDialer* dtmf_dolphin_dia
cursor++; cursor++;
span = dtmf_dolphin_get_tone_span(cursor, model->col); span = dtmf_dolphin_get_tone_span(cursor, model->col);
} }
if (span != 0) { if(span != 0) {
model->row = cursor; model->row = cursor;
} }
}, },
@@ -239,11 +237,11 @@ static bool dtmf_dolphin_dialer_process_left(DTMFDolphinDialer* dtmf_dolphin_dia
{ {
uint8_t span = 0; uint8_t span = 0;
uint8_t cursor = model->col; uint8_t cursor = model->col;
while (span == 0 && cursor > 0) { while(span == 0 && cursor > 0) {
cursor--; cursor--;
span = dtmf_dolphin_get_tone_span(model->row, cursor); span = dtmf_dolphin_get_tone_span(model->row, cursor);
} }
if (span != 0) { if(span != 0) {
model->col = cursor; model->col = cursor;
} }
}, },
@@ -267,7 +265,7 @@ static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_di
cursor++; cursor++;
span = dtmf_dolphin_get_tone_span(model->row, cursor); span = dtmf_dolphin_get_tone_span(model->row, cursor);
} }
if (span != 0) { if(span != 0) {
model->col = cursor; model->col = cursor;
} }
}, },
@@ -275,16 +273,18 @@ static bool dtmf_dolphin_dialer_process_right(DTMFDolphinDialer* dtmf_dolphin_di
return true; return true;
} }
static bool dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event) { static bool
dtmf_dolphin_dialer_process_ok(DTMFDolphinDialer* dtmf_dolphin_dialer, InputEvent* event) {
bool consumed = false; bool consumed = false;
with_view_model( with_view_model(
dtmf_dolphin_dialer->view, dtmf_dolphin_dialer->view,
DTMFDolphinDialerModel * model, DTMFDolphinDialerModel * model,
{ {
if (event->type == InputTypePress) { if(event->type == InputTypePress) {
model->playing = dtmf_dolphin_audio_play_tones(model->freq1, model->freq2, model->pulses, model->pulse_ms, model->gap_ms); model->playing = dtmf_dolphin_audio_play_tones(
} else if (event->type == InputTypeRelease) { model->freq1, model->freq2, model->pulses, model->pulse_ms, model->gap_ms);
} else if(event->type == InputTypeRelease) {
model->playing = !dtmf_dolphin_audio_stop_tones(); model->playing = !dtmf_dolphin_audio_stop_tones();
} }
}, },
@@ -308,15 +308,15 @@ static void dtmf_dolphin_dialer_enter_callback(void* context) {
model->freq2 = 0.0; model->freq2 = 0.0;
model->playing = false; model->playing = false;
}, },
true true);
);
} }
DTMFDolphinDialer* dtmf_dolphin_dialer_alloc() { DTMFDolphinDialer* dtmf_dolphin_dialer_alloc() {
DTMFDolphinDialer* dtmf_dolphin_dialer = malloc(sizeof(DTMFDolphinDialer)); DTMFDolphinDialer* dtmf_dolphin_dialer = malloc(sizeof(DTMFDolphinDialer));
dtmf_dolphin_dialer->view = view_alloc(); dtmf_dolphin_dialer->view = view_alloc();
view_allocate_model(dtmf_dolphin_dialer->view, ViewModelTypeLocking, sizeof(DTMFDolphinDialerModel)); view_allocate_model(
dtmf_dolphin_dialer->view, ViewModelTypeLocking, sizeof(DTMFDolphinDialerModel));
with_view_model( with_view_model(
dtmf_dolphin_dialer->view, dtmf_dolphin_dialer->view,
@@ -329,8 +329,7 @@ DTMFDolphinDialer* dtmf_dolphin_dialer_alloc() {
model->freq2 = 0.0; model->freq2 = 0.0;
model->playing = false; model->playing = false;
}, },
true true);
);
view_set_context(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer); view_set_context(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer);
view_set_draw_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_draw_callback); view_set_draw_callback(dtmf_dolphin_dialer->view, dtmf_dolphin_dialer_draw_callback);
@@ -349,4 +348,3 @@ View* dtmf_dolphin_dialer_get_view(DTMFDolphinDialer* dtmf_dolphin_dialer) {
furi_assert(dtmf_dolphin_dialer); furi_assert(dtmf_dolphin_dialer);
return dtmf_dolphin_dialer->view; return dtmf_dolphin_dialer->view;
} }

View File

@@ -12,4 +12,7 @@ void dtmf_dolphin_dialer_free(DTMFDolphinDialer* dtmf_dolphin_dialer);
View* dtmf_dolphin_dialer_get_view(DTMFDolphinDialer* dtmf_dolphin_dialer); View* dtmf_dolphin_dialer_get_view(DTMFDolphinDialer* dtmf_dolphin_dialer);
void dtmf_dolphin_dialer_set_ok_callback(DTMFDolphinDialer* dtmf_dolphin_dialer, DTMFDolphinDialerOkCallback callback, void* context); void dtmf_dolphin_dialer_set_ok_callback(
DTMFDolphinDialer* dtmf_dolphin_dialer,
DTMFDolphinDialerOkCallback callback,
void* context);

View File

@@ -10,69 +10,74 @@
#define CARD_DRAW_FIRST_ROW_LENGTH 7 #define CARD_DRAW_FIRST_ROW_LENGTH 7
uint8_t pips[4][3] = { uint8_t pips[4][3] = {
{21, 10, 7}, //spades {21, 10, 7}, //spades
{7, 10, 7}, //hearts {7, 10, 7}, //hearts
{0, 10, 7}, //diamonds {0, 10, 7}, //diamonds
{14, 10, 7}, //clubs {14, 10, 7}, //clubs
}; };
uint8_t letters[13][3] = { uint8_t letters[13][3] = {
{0, 0, 5}, {0, 0, 5},
{5, 0, 5}, {5, 0, 5},
{10, 0, 5}, {10, 0, 5},
{15, 0, 5}, {15, 0, 5},
{20, 0, 5}, {20, 0, 5},
{25, 0, 5}, {25, 0, 5},
{30, 0, 5}, {30, 0, 5},
{0, 5, 5}, {0, 5, 5},
{5, 5, 5}, {5, 5, 5},
{10, 5, 5}, {10, 5, 5},
{15, 5, 5}, {15, 5, 5},
{20, 5, 5}, {20, 5, 5},
{25, 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; Icon* card_graphics = NULL;
void set_card_graphics(const Icon *graphics) { void set_card_graphics(const Icon* graphics) {
card_graphics = (Icon *) graphics; card_graphics = (Icon*)graphics;
} }
void void draw_card_at_colored(
draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, bool inverted, Canvas *const canvas) { 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 primary = inverted ? Black : White;
DrawMode secondary = inverted ? White : Black; DrawMode secondary = inverted ? White : Black;
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, primary); 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); draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
uint8_t *drawInfo = pips[pip]; uint8_t* drawInfo = pips[pip];
uint8_t px = drawInfo[0], py = drawInfo[1], s = drawInfo[2]; uint8_t px = drawInfo[0], py = drawInfo[1], s = drawInfo[2];
uint8_t left = pos_x + 2; uint8_t left = pos_x + 2;
@@ -80,10 +85,8 @@ draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character,
uint8_t top = pos_y + 2; uint8_t top = pos_y + 2;
uint8_t bottom = (pos_y + CARD_HEIGHT - s - 2); uint8_t bottom = (pos_y + CARD_HEIGHT - s - 2);
draw_icon_clip(canvas, card_graphics, right, top, px, py, s, s, draw_icon_clip(canvas, card_graphics, right, top, px, py, s, s, secondary);
secondary); draw_icon_clip_flipped(canvas, card_graphics, left, bottom, px, py, s, s, secondary);
draw_icon_clip_flipped(canvas, card_graphics, left, bottom, px, py, s, s,
secondary);
drawInfo = letters[character]; drawInfo = letters[character];
px = drawInfo[0], py = drawInfo[1], s = drawInfo[2]; px = drawInfo[0], py = drawInfo[1], s = drawInfo[2];
@@ -92,62 +95,58 @@ draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character,
top = pos_y + 2; top = pos_y + 2;
bottom = (pos_y + CARD_HEIGHT - s - 2); bottom = (pos_y + CARD_HEIGHT - s - 2);
draw_icon_clip(canvas, card_graphics, left, top + 1, px, py, s, s, draw_icon_clip(canvas, card_graphics, left, top + 1, px, py, s, s, secondary);
secondary); draw_icon_clip_flipped(canvas, card_graphics, right, bottom - 1, px, py, s, s, secondary);
draw_icon_clip_flipped(canvas, card_graphics, right, bottom - 1, px, py, s, s,
secondary);
} }
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) {
draw_card_at_colored(pos_x, pos_y, pip, character, false, 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) { void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas) {
for (int i = count - 1; i >= 0; i--) { for(int i = count - 1; i >= 0; i--) {
draw_card_at(playerCardPositions[i][0], playerCardPositions[i][1], cards[i].pip, cards[i].character, canvas); draw_card_at(
playerCardPositions[i][0],
playerCardPositions[i][1],
cards[i].pip,
cards[i].character,
canvas);
} }
} }
Vector card_pos_at_index(uint8_t index) { Vector card_pos_at_index(uint8_t index) {
return (Vector) { return (Vector){playerCardPositions[index][0], playerCardPositions[index][1]};
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) {
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, White); 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_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); draw_icon_clip(canvas, card_graphics, pos_x + 1, pos_y + 1, 35, 0, 15, 21, Black);
} }
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;
if (deck_ptr->cards == NULL) { if(deck_ptr->cards == NULL) {
deck_ptr->deck_count = deck_count; deck_ptr->deck_count = deck_count;
deck_ptr->card_count = deck_count * 52; deck_ptr->card_count = deck_count * 52;
deck_ptr->cards = malloc(sizeof(Card) * deck_ptr->card_count); deck_ptr->cards = malloc(sizeof(Card) * deck_ptr->card_count);
} }
for (uint8_t deck = 0; deck < deck_count; deck++) { for(uint8_t deck = 0; deck < deck_count; deck++) {
for (uint8_t pip = 0; pip < 4; pip++) { for(uint8_t pip = 0; pip < 4; pip++) {
for (uint8_t label = 0; label < 13; label++) { for(uint8_t label = 0; label < 13; label++) {
deck_ptr->cards[counter] = (Card) deck_ptr->cards[counter] = (Card){pip, label, false, false};
{
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];
@@ -155,175 +154,196 @@ 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) score += 11; if((score + 11) <= 21)
else score++; score += 11;
else
score++;
} }
return score; return score;
} }
void draw_card_animation(Card animatingCard, Vector from, Vector control, Vector to, float t, bool extra_margin, void draw_card_animation(
Canvas *const canvas) { Card animatingCard,
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(currentPos.x, currentPos.y, animatingCard.pip, draw_card_at(
animatingCard.character, canvas); currentPos.x, currentPos.y, animatingCard.pip, 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(currentPos.x, currentPos.y, animatingCard.pip, draw_card_at(
animatingCard.character, canvas); currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas);
} }
} }
void init_hand(Hand *hand_ptr, uint8_t count) { void init_hand(Hand* hand_ptr, uint8_t count) {
hand_ptr->cards = malloc(sizeof(Card) * count); hand_ptr->cards = malloc(sizeof(Card) * count);
hand_ptr->index = 0; hand_ptr->index = 0;
hand_ptr->max = count; hand_ptr->max = count;
} }
void free_hand(Hand *hand_ptr) { void free_hand(Hand* hand_ptr) {
FURI_LOG_D("CARD", "Freeing hand"); FURI_LOG_D("CARD", "Freeing hand");
free(hand_ptr->cards); free(hand_ptr->cards);
} }
void add_to_hand(Hand *hand_ptr, Card card) { void add_to_hand(Hand* hand_ptr, Card card) {
FURI_LOG_D("CARD", "Adding to hand"); FURI_LOG_D("CARD", "Adding to hand");
if (hand_ptr->index < hand_ptr->max) { if(hand_ptr->index < hand_ptr->max) {
hand_ptr->cards[hand_ptr->index] = card; hand_ptr->cards[hand_ptr->index] = card;
hand_ptr->index++; hand_ptr->index++;
} }
} }
void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas *const canvas) { void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas* const canvas) {
if (highlighted) { if(highlighted) {
draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); 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); draw_rounded_box_frame(
canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White);
} else { } else {
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); 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); 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) { int first_non_flipped_card(Hand hand) {
for (int i = 0; i < hand.index; i++) { for(int i = 0; i < hand.index; i++) {
if (!hand.cards[i].flipped) { if(!hand.cards[i].flipped) {
return i; return i;
} }
} }
return hand.index; return hand.index;
} }
void draw_hand_column(Hand hand, int16_t pos_x, int16_t pos_y, int8_t highlight, Canvas *const canvas) { void draw_hand_column(
if (hand.index == 0) { 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); draw_card_space(pos_x, pos_y, highlight > 0, canvas);
if(highlight==0) if(highlight == 0)
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Inverse);
Inverse);
return; return;
} }
int loopEnd = hand.index; int loopEnd = hand.index;
int hStart = max(loopEnd-4, 0); int hStart = max(loopEnd - 4, 0);
int pos = 0; int pos = 0;
int first= first_non_flipped_card(hand); int first = first_non_flipped_card(hand);
bool wastop=false; bool wastop = false;
if(first>=0 && first<=hStart && highlight!=first){ if(first >= 0 && first <= hStart && highlight != first) {
if(first>0){ if(first > 0) {
draw_card_back_at(pos_x, pos_y + pos, canvas); draw_card_back_at(pos_x, pos_y + pos, canvas);
pos+=4; pos += 4;
hStart++; hStart++;
wastop=true; wastop = true;
} }
draw_card_at_colored(pos_x, pos_y + pos, hand.cards[first].pip, hand.cards[first].character, draw_card_at_colored(
false, pos_x, pos_y + pos, hand.cards[first].pip, hand.cards[first].character, false, canvas);
canvas); pos += 8;
pos+=8;
hStart++; hStart++;
} }
if(hStart>highlight && highlight>=0){ if(hStart > highlight && highlight >= 0) {
if(!wastop && first>0){ if(!wastop && first > 0) {
draw_card_back_at(pos_x, pos_y + pos, canvas); draw_card_back_at(pos_x, pos_y + pos, canvas);
pos+=4; pos += 4;
hStart++; hStart++;
} }
draw_card_at_colored(pos_x, pos_y + pos, hand.cards[highlight].pip, hand.cards[highlight].character, draw_card_at_colored(
true, pos_x,
canvas); pos_y + pos,
pos+=8; hand.cards[highlight].pip,
hand.cards[highlight].character,
true,
canvas);
pos += 8;
hStart++; hStart++;
} }
for (int i = hStart; i < loopEnd; i++, pos+=4) { for(int i = hStart; i < loopEnd; i++, pos += 4) {
if (hand.cards[i].flipped) { if(hand.cards[i].flipped) {
draw_card_back_at(pos_x, pos_y + pos, canvas); draw_card_back_at(pos_x, pos_y + pos, canvas);
if(i==highlight) if(i == highlight)
draw_rounded_box(canvas, pos_x+1, pos_y + pos+1, CARD_WIDTH - 2, CARD_HEIGHT - 2, draw_rounded_box(
Inverse); canvas, pos_x + 1, pos_y + pos + 1, CARD_WIDTH - 2, CARD_HEIGHT - 2, Inverse);
} else { } else {
draw_card_at_colored(pos_x, pos_y + pos, hand.cards[i].pip, hand.cards[i].character, draw_card_at_colored(
(i == highlight), pos_x,
canvas); pos_y + pos,
if(i == highlight || i==first) pos+=4; 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) { Card remove_from_deck(uint16_t index, Deck* deck) {
FURI_LOG_D("CARD", "Removing from deck"); FURI_LOG_D("CARD", "Removing from deck");
Card result = {0, 0, true, false}; Card result = {0, 0, true, false};
if (deck->card_count > 0) { if(deck->card_count > 0) {
deck->card_count--; deck->card_count--;
for (int i = 0, curr_index = 0; i <= deck->card_count; i++) { for(int i = 0, curr_index = 0; i <= deck->card_count; i++) {
if (i != index) { if(i != index) {
deck->cards[curr_index] = deck->cards[i]; deck->cards[curr_index] = deck->cards[i];
curr_index++; curr_index++;
} else { } else {
result = deck->cards[i]; result = deck->cards[i];
} }
} }
if (deck->index >= 0) { if(deck->index >= 0) {
deck->index--; deck->index--;
} }
} }
return result; return result;
} }
void extract_hand_region(Hand *hand, Hand *to, uint8_t start_index) { void extract_hand_region(Hand* hand, Hand* to, uint8_t start_index) {
FURI_LOG_D("CARD", "Extracting hand region"); FURI_LOG_D("CARD", "Extracting hand region");
if (start_index >= hand->index) return; if(start_index >= hand->index) return;
for (uint8_t i = start_index; i < hand->index; i++) { for(uint8_t i = start_index; i < hand->index; i++) {
add_to_hand(to, hand->cards[i]); add_to_hand(to, hand->cards[i]);
} }
hand->index = start_index; hand->index = start_index;
} }
void add_hand_region(Hand *to, Hand *from) { void add_hand_region(Hand* to, Hand* from) {
FURI_LOG_D("CARD", "Adding hand region"); FURI_LOG_D("CARD", "Adding hand region");
if ((to->index + from->index) <= to->max) { if((to->index + from->index) <= to->max) {
for (int i = 0; i < from->index; i++) { for(int i = 0; i < from->index; i++) {
add_to_hand(to, from->cards[i]); add_to_hand(to, from->cards[i]);
} }
} }

View File

@@ -12,27 +12,27 @@
//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 disabled;
bool flipped; 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 card_count; int card_count;
int index; //Card index (to know where we at in the deck) int index; //Card index (to know where we at in the deck)
} Deck; } Deck;
typedef struct { typedef struct {
Card *cards; //Cards in the deck Card* cards; //Cards in the deck
uint8_t index; //Current index uint8_t index; //Current index
uint8_t max; //How many cards we want to store uint8_t max; //How many cards we want to store
} Hand; } Hand;
//endregion //endregion
void set_card_graphics(const Icon *graphics); 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).
@@ -51,7 +51,7 @@ 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) * Draws card at a given coordinate (top-left corner)
@@ -63,8 +63,13 @@ void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Ca
* @param inverted Invert colors * @param inverted Invert colors
* @param canvas Pointer to Flipper's canvas object * @param canvas Pointer to Flipper's canvas object
*/ */
void void draw_card_at_colored(
draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, bool inverted, Canvas *const canvas); 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
@@ -73,7 +78,7 @@ draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character,
* @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)
@@ -82,7 +87,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
@@ -90,14 +95,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
@@ -106,7 +111,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
@@ -119,28 +124,34 @@ 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(Card animatingCard, Vector from, Vector control, Vector to, float t, bool extra_margin, void draw_card_animation(
Canvas *const canvas); Card animatingCard,
Vector from,
Vector control,
Vector to,
float t,
bool extra_margin,
Canvas* const canvas);
/** /**
* Init hand pointer * Init hand pointer
* @param hand_ptr Pointer to hand * @param hand_ptr Pointer to hand
* @param count Number of cards we want to store * @param count Number of cards we want to store
*/ */
void init_hand(Hand *hand_ptr, uint8_t count); void init_hand(Hand* hand_ptr, uint8_t count);
/** /**
* Free hand resources * Free hand resources
* @param hand_ptr Pointer to hand * @param hand_ptr Pointer to hand
*/ */
void free_hand(Hand *hand_ptr); void free_hand(Hand* hand_ptr);
/** /**
* Add card to hand * Add card to hand
* @param hand_ptr Pointer to hand * @param hand_ptr Pointer to hand
* @param card Card to add * @param card Card to add
*/ */
void add_to_hand(Hand *hand_ptr, Card card); void add_to_hand(Hand* hand_ptr, Card card);
/** /**
* Draw card placement position at coordinate * Draw card placement position at coordinate
@@ -149,7 +160,7 @@ void add_to_hand(Hand *hand_ptr, Card card);
* @param highlighted Apply highlight effect * @param highlighted Apply highlight effect
* @param canvas Canvas object * @param canvas Canvas object
*/ */
void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas *const canvas); 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 * Draws a column of card, displaying the last [max_cards] cards on the list
@@ -159,8 +170,12 @@ void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas *con
* @param highlight Index to highlight, negative means no highlight * @param highlight Index to highlight, negative means no highlight
* @param canvas Canvas object * @param canvas Canvas object
*/ */
void void draw_hand_column(
draw_hand_column(Hand hand, int16_t pos_x, int16_t pos_y, int8_t highlight, Canvas *const canvas); 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) * 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)
@@ -168,10 +183,10 @@ draw_hand_column(Hand hand, int16_t pos_x, int16_t pos_y, int8_t highlight, Canv
* @param deck Deck reference * @param deck Deck reference
* @return The removed card * @return The removed card
*/ */
Card remove_from_deck(uint16_t index, Deck *deck); Card remove_from_deck(uint16_t index, Deck* deck);
int first_non_flipped_card(Hand hand); int first_non_flipped_card(Hand hand);
void extract_hand_region(Hand *hand, Hand *to, uint8_t start_index); void extract_hand_region(Hand* hand, Hand* to, uint8_t start_index);
void add_hand_region(Hand *to, Hand *from); void add_hand_region(Hand* to, Hand* from);

View File

@@ -2,47 +2,40 @@
#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( return lerp_2d(lerp_2d(start, control, t), lerp_2d(control, end, t), t);
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) { return (Vector){a.x / length, a.y / length};
a.x / length,
a.y / length
};
} }
float vector_magnitude(Vector a) { float vector_magnitude(Vector a) {

View File

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

View File

@@ -1,43 +1,53 @@
#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) if(!state && menu->current_menu == index) move_menu(menu, 1);
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("MENU", "Iteration %i, current %i, direction %i, state %i", i, menu->current_menu,direction,menu->items[menu->current_menu].enabled?1:0); FURI_LOG_D(
if (direction < 0 && menu->current_menu == 0) { "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; 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("MENU", "After process current %i, direction %i, state %i", menu->current_menu,direction,menu->items[menu->current_menu].enabled?1:0); FURI_LOG_D(
if (menu->items[menu->current_menu].enabled) { "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); FURI_LOG_D("MENU", "Next menu %i", menu->current_menu);
return; return;
} }
@@ -46,44 +56,48 @@ 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, pos_x + menu->menu_width / 2, pos_y + 6, AlignCenter, AlignCenter, canvas_draw_str_aligned(
menu->items[menu->current_menu].name); canvas,
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,18 +4,19 @@
#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 *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; } 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;
/** /**
@@ -23,7 +24,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
@@ -32,7 +33,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
@@ -41,7 +42,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
@@ -49,7 +50,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
@@ -57,7 +58,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).
@@ -73,4 +74,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,15 +1,14 @@
#include "queue.h" #include "queue.h"
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) { if(queue_state->current != NULL && queue_state->current->render != NULL)
if (queue_state->current != NULL && queue_state->current->render != NULL) ((QueueItem*)queue_state->current)->render(app_state, canvas);
((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;
@@ -17,44 +16,48 @@ 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) if(queue_state->current->start != NULL) queue_state->current->start(app_state);
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(QueueState *queue_state, void *app_state, void enqueue(
void(*done)(void *state), void(*start)(void *state), QueueState* queue_state,
void (*render)(const void *state, Canvas *const canvas), uint32_t duration) { void* app_state,
QueueItem *next; void (*done)(void* state),
if (queue_state->current == NULL) { 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->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) if(next->start != NULL) next->start(app_state);
next->start(app_state);
} else { } else {
next = queue_state->current; 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 = malloc(sizeof(QueueItem));
next = next->next; next = next->next;
} }

View File

@@ -4,17 +4,19 @@
#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)(const void *state, Canvas *const canvas); //Callback for the rendering loop while this item is running void (*render)(
void (*start)(void *state); //Callback when this item is started running const void* state,
void *next; //Pointer to the next item Canvas* const canvas); //Callback for the rendering loop while this item is running
uint32_t duration; //duration of the item 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; } 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;
/** /**
@@ -27,15 +29,19 @@ 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(QueueState *queue_state, void *app_state, void enqueue(
void(*done)(void *state), void(*start)(void *state), QueueState* queue_state,
void (*render)(const void *state, Canvas *const canvas), uint32_t duration); 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 * 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.
@@ -43,7 +49,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)
@@ -52,7 +58,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)
@@ -61,4 +67,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

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

View File

@@ -10,49 +10,96 @@ typedef enum {
Black, Black,
White, White,
Inverse, Inverse,
Filled //Currently only for Icon clip drawing Filled //Currently only for Icon clip drawing
} DrawMode; } DrawMode;
// size is the screen size // size is the screen size
typedef struct { typedef struct {
uint8_t *data; uint8_t* data;
unsigned long iconId; unsigned long iconId;
} TileMap; } TileMap;
bool test_pixel(uint8_t *data, uint8_t x, uint8_t y, uint8_t w); 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); uint8_t* image_data(Canvas* const canvas, const Icon* icon);
uint32_t pixel_index(uint8_t x, uint8_t y); 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, void draw_icon_clip(
uint8_t h, DrawMode drawMode); 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, void draw_icon_clip_flipped(
uint8_t h, DrawMode drawMode); 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(
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_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 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_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 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); 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); 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 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); 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); bool in_screen(int16_t x, int16_t y);
void ui_cleanup(); void ui_cleanup();
uint8_t* get_buffer(Canvas *const canvas); uint8_t* get_buffer(Canvas* const canvas);
uint8_t* make_buffer(); uint8_t* make_buffer();
void clone_buffer(uint8_t* canvas, uint8_t* data); void clone_buffer(uint8_t* canvas, uint8_t* data);

View File

@@ -19,15 +19,10 @@ typedef struct {
InputEvent input; InputEvent input;
} AppEvent; } AppEvent;
typedef enum { typedef enum { GameStateGameOver, GameStateStart, GameStatePlay, GameStateAnimate } PlayState;
GameStateGameOver,
GameStateStart,
GameStatePlay,
GameStateAnimate
} PlayState;
typedef struct { typedef struct {
uint8_t *buffer; uint8_t* buffer;
Card card; Card card;
int8_t deck; int8_t deck;
int indexes[4]; int indexes[4];
@@ -57,5 +52,5 @@ typedef struct {
uint8_t selectColumn; uint8_t selectColumn;
int8_t selected_card; int8_t selected_card;
CardAnimation animation; CardAnimation animation;
uint8_t *buffer; uint8_t* buffer;
} GameState; } GameState;

View File

@@ -9,223 +9,253 @@
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
const NotificationSequence sequence_fail = { const NotificationSequence sequence_fail = {
&message_vibro_on, &message_vibro_on,
&message_note_c4, &message_note_c4,
&message_delay_10, &message_delay_10,
&message_vibro_off, &message_vibro_off,
&message_sound_off, &message_sound_off,
&message_delay_10, &message_delay_10,
&message_vibro_on, &message_vibro_on,
&message_note_a3, &message_note_a3,
&message_delay_10, &message_delay_10,
&message_vibro_off, &message_vibro_off,
&message_sound_off, &message_sound_off,
NULL, NULL,
}; };
int8_t columns[7][3] = { int8_t columns[7][3] = {
{1, 1, 25}, {1, 1, 25},
{19, 1, 25}, {19, 1, 25},
{37, 1, 25}, {37, 1, 25},
{55, 1, 25}, {55, 1, 25},
{73, 1, 25}, {73, 1, 25},
{91, 1, 25}, {91, 1, 25},
{109, 1, 25}, {109, 1, 25},
}; };
bool can_place_card(Card where, Card what) { bool can_place_card(Card where, Card what) {
FURI_LOG_D(APP_NAME, "TESTING pip %i, letter %i with pip %i, letter %i", where.pip, where.character, what.pip, FURI_LOG_D(
what.character); APP_NAME,
"TESTING pip %i, letter %i with pip %i, letter %i",
where.pip,
where.character,
what.pip,
what.character);
bool a_black = where.pip == 0 || where.pip == 3; bool a_black = where.pip == 0 || where.pip == 3;
bool b_black = what.pip == 0 || what.pip == 3; bool b_black = what.pip == 0 || what.pip == 3;
if (a_black == b_black) return false; if(a_black == b_black) return false;
int8_t a_letter = (int8_t) where.character; int8_t a_letter = (int8_t)where.character;
int8_t b_letter = (int8_t) what.character; int8_t b_letter = (int8_t)what.character;
if (a_letter == 12) a_letter = -1; if(a_letter == 12) a_letter = -1;
if (b_letter == 12) b_letter = -1; if(b_letter == 12) b_letter = -1;
return (a_letter - 1) == b_letter; return (a_letter - 1) == b_letter;
} }
static void draw_scene(Canvas *const canvas, const GameState *game_state) { static void draw_scene(Canvas* const canvas, const GameState* game_state) {
int deckIndex = game_state->deck.index; int deckIndex = game_state->deck.index;
if (game_state->dragging_deck) if(game_state->dragging_deck) deckIndex--;
deckIndex--;
if ((game_state->deck.index < (game_state->deck.card_count - 1) || game_state->deck.index == -1) && game_state->deck.card_count>0) { if((game_state->deck.index < (game_state->deck.card_count - 1) ||
game_state->deck.index == -1) &&
game_state->deck.card_count > 0) {
draw_card_back_at(columns[0][0], columns[0][1], canvas); draw_card_back_at(columns[0][0], columns[0][1], canvas);
if (game_state->selectRow == 0 && game_state->selectColumn == 0) { if(game_state->selectRow == 0 && game_state->selectColumn == 0) {
draw_rounded_box(canvas, columns[0][0] + 1, columns[0][1] + 1, CARD_WIDTH - 2, CARD_HEIGHT - 2, draw_rounded_box(
Inverse); canvas,
columns[0][0] + 1,
columns[0][1] + 1,
CARD_WIDTH - 2,
CARD_HEIGHT - 2,
Inverse);
} }
} else } else
draw_card_space(columns[0][0], columns[0][1], draw_card_space(
game_state->selectRow == 0 && game_state->selectColumn == 0, columns[0][0],
canvas); columns[0][1],
game_state->selectRow == 0 && game_state->selectColumn == 0,
canvas);
//deck side //deck side
if (deckIndex >= 0) { if(deckIndex >= 0) {
Card c = game_state->deck.cards[deckIndex]; Card c = game_state->deck.cards[deckIndex];
draw_card_at_colored(columns[1][0], columns[1][1], c.pip, c.character, draw_card_at_colored(
game_state->selectRow == 0 && game_state->selectColumn == 1, canvas); columns[1][0],
columns[1][1],
c.pip,
c.character,
game_state->selectRow == 0 && game_state->selectColumn == 1,
canvas);
} else } else
draw_card_space(columns[1][0], columns[1][1], draw_card_space(
game_state->selectRow == 0 && game_state->selectColumn == 1, columns[1][0],
canvas); columns[1][1],
game_state->selectRow == 0 && game_state->selectColumn == 1,
canvas);
for (uint8_t i = 0; i < 4; i++) { for(uint8_t i = 0; i < 4; i++) {
Card current = game_state->top_cards[i]; Card current = game_state->top_cards[i];
bool selected = game_state->selectRow == 0 && game_state->selectColumn == (i + 3); bool selected = game_state->selectRow == 0 && game_state->selectColumn == (i + 3);
if (current.disabled) { if(current.disabled) {
draw_card_space(columns[i + 3][0], columns[i + 3][1], selected, canvas); draw_card_space(columns[i + 3][0], columns[i + 3][1], selected, canvas);
} else { } else {
draw_card_at(columns[i + 3][0], columns[i + 3][1], current.pip, current.character, canvas); draw_card_at(
if (selected) { columns[i + 3][0], columns[i + 3][1], current.pip, current.character, canvas);
draw_rounded_box(canvas, columns[i + 3][0], columns[i + 3][1], CARD_WIDTH, CARD_HEIGHT, if(selected) {
Inverse); draw_rounded_box(
canvas, columns[i + 3][0], columns[i + 3][1], CARD_WIDTH, CARD_HEIGHT, Inverse);
} }
} }
} }
for (uint8_t i = 0; i < 7; i++) { for(uint8_t i = 0; i < 7; i++) {
bool selected = game_state->selectRow == 1 && game_state->selectColumn == i; bool selected = game_state->selectRow == 1 && game_state->selectColumn == i;
int8_t index= (game_state->bottom_columns[i].index - 1 - game_state->selected_card); int8_t index = (game_state->bottom_columns[i].index - 1 - game_state->selected_card);
if(index<0)index=0; if(index < 0) index = 0;
draw_hand_column(game_state->bottom_columns[i], columns[i][0], columns[i][2], draw_hand_column(
selected ? index : -1, canvas); game_state->bottom_columns[i],
columns[i][0],
columns[i][2],
selected ? index : -1,
canvas);
} }
int8_t pos[2] = {columns[game_state->selectColumn][0], int8_t pos[2] = {
columns[game_state->selectColumn][game_state->selectRow + 1]}; columns[game_state->selectColumn][0],
columns[game_state->selectColumn][game_state->selectRow + 1]};
/* draw_icon_clip(canvas, &I_card_graphics, pos[0] + CARD_HALF_WIDTH, pos[1] + CARD_HALF_HEIGHT, 30, 5, 5, 5, /* draw_icon_clip(canvas, &I_card_graphics, pos[0] + CARD_HALF_WIDTH, pos[1] + CARD_HALF_HEIGHT, 30, 5, 5, 5,
Filled);*/ Filled);*/
if (game_state->dragging_hand.index > 0) { if(game_state->dragging_hand.index > 0) {
draw_hand_column(game_state->dragging_hand, draw_hand_column(
pos[0] + CARD_HALF_WIDTH + 3, pos[1] + CARD_HALF_HEIGHT + 3, game_state->dragging_hand,
-1, canvas); pos[0] + CARD_HALF_WIDTH + 3,
pos[1] + CARD_HALF_HEIGHT + 3,
-1,
canvas);
} }
} }
static void draw_animation(Canvas *const canvas, const GameState *game_state) { static void draw_animation(Canvas* const canvas, const GameState* game_state) {
if (!game_state->animation.started) { if(!game_state->animation.started) {
draw_scene(canvas, game_state); draw_scene(canvas, game_state);
} else { } else {
clone_buffer(game_state->animation.buffer, get_buffer(canvas)); clone_buffer(game_state->animation.buffer, get_buffer(canvas));
draw_card_at((int8_t) game_state->animation.x, (int8_t) game_state->animation.y, game_state->animation.card.pip, draw_card_at(
game_state->animation.card.character, canvas); (int8_t)game_state->animation.x,
(int8_t)game_state->animation.y,
game_state->animation.card.pip,
game_state->animation.card.character,
canvas);
} }
clone_buffer(get_buffer(canvas), game_state->animation.buffer); clone_buffer(get_buffer(canvas), game_state->animation.buffer);
} }
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;
} }
switch (game_state->state) { switch(game_state->state) {
case GameStateAnimate: case GameStateAnimate:
draw_animation(canvas, game_state); draw_animation(canvas, game_state);
break; break;
case GameStateStart: case GameStateStart:
canvas_draw_icon(canvas, 0, 0, &I_solitaire_main); canvas_draw_icon(canvas, 0, 0, &I_solitaire_main);
break; break;
case GameStatePlay: case GameStatePlay:
draw_scene(canvas, game_state); draw_scene(canvas, game_state);
break; break;
default: default:
break; break;
} }
release_mutex((ValueMutex *) ctx, game_state); release_mutex((ValueMutex*)ctx, game_state);
} }
void remove_drag(GameState *game_state) { void remove_drag(GameState* game_state) {
if (game_state->dragging_deck) { if(game_state->dragging_deck) {
remove_from_deck(game_state->deck.index, &(game_state->deck)); remove_from_deck(game_state->deck.index, &(game_state->deck));
game_state->dragging_deck = false; game_state->dragging_deck = false;
} else if (game_state->dragging_column < 7) { } else if(game_state->dragging_column < 7) {
game_state->dragging_column = 8; game_state->dragging_column = 8;
} }
game_state->dragging_hand.index = 0; game_state->dragging_hand.index = 0;
} }
bool handleInput(GameState *game_state) { bool handleInput(GameState* game_state) {
Hand currentHand = game_state->bottom_columns[game_state->selectColumn]; Hand currentHand = game_state->bottom_columns[game_state->selectColumn];
switch (game_state->input) { switch(game_state->input) {
case InputKeyUp: case InputKeyUp:
if (game_state->selectRow > 0) { if(game_state->selectRow > 0) {
int first = first_non_flipped_card(currentHand); int first = first_non_flipped_card(currentHand);
first = currentHand.index - first; first = currentHand.index - first;
if ((first - 1) > game_state->selected_card && game_state->dragging_hand.index == 0 && if((first - 1) > game_state->selected_card && game_state->dragging_hand.index == 0 &&
!game_state->longPress) { !game_state->longPress) {
game_state->selected_card++; game_state->selected_card++;
} else {
game_state->selectRow--;
game_state->selected_card = 0;
}
}
break;
case InputKeyDown:
if (game_state->selectRow < 1) {
game_state->selectRow++;
game_state->selected_card = 0;
} else { } else {
if (game_state->selected_card > 0) { game_state->selectRow--;
if (game_state->longPress)
game_state->selected_card = 0;
else
game_state->selected_card--;
}
}
break;
case InputKeyRight:
if (game_state->selectColumn < 6) {
game_state->selectColumn++;
game_state->selected_card = 0; game_state->selected_card = 0;
} }
break; }
case InputKeyLeft: break;
if (game_state->selectColumn > 0) { case InputKeyDown:
game_state->selectColumn--; if(game_state->selectRow < 1) {
game_state->selected_card = 0; game_state->selectRow++;
game_state->selected_card = 0;
} else {
if(game_state->selected_card > 0) {
if(game_state->longPress)
game_state->selected_card = 0;
else
game_state->selected_card--;
} }
break; }
case InputKeyOk: break;
return true; case InputKeyRight:
break; if(game_state->selectColumn < 6) {
default: game_state->selectColumn++;
break; game_state->selected_card = 0;
}
break;
case InputKeyLeft:
if(game_state->selectColumn > 0) {
game_state->selectColumn--;
game_state->selected_card = 0;
}
break;
case InputKeyOk:
return true;
break;
default:
break;
} }
if (game_state->selectRow == 0 && game_state->selectColumn == 2) { if(game_state->selectRow == 0 && game_state->selectColumn == 2) {
if (game_state->input == InputKeyRight) if(game_state->input == InputKeyRight)
game_state->selectColumn++; game_state->selectColumn++;
else else
game_state->selectColumn--; game_state->selectColumn--;
} }
if (game_state->dragging_hand.index > 0) if(game_state->dragging_hand.index > 0) game_state->selected_card = 0;
game_state->selected_card = 0;
return false; return false;
} }
bool place_on_top(Card *where, Card what) { bool place_on_top(Card* where, Card what) {
if (where->disabled && what.character == 12) { if(where->disabled && what.character == 12) {
where->disabled = what.disabled; where->disabled = what.disabled;
where->pip = what.pip; where->pip = what.pip;
where->character = what.character; where->character = what.character;
return true; return true;
} else if (where->pip == what.pip) { } else if(where->pip == what.pip) {
int8_t a_letter = (int8_t) where->character; int8_t a_letter = (int8_t)where->character;
int8_t b_letter = (int8_t) what.character; int8_t b_letter = (int8_t)what.character;
if (a_letter == 12) a_letter = -1; if(a_letter == 12) a_letter = -1;
if (b_letter == 12) b_letter = -1; if(b_letter == 12) b_letter = -1;
if ((a_letter + 1) == b_letter) { if((a_letter + 1) == b_letter) {
where->disabled = what.disabled; where->disabled = what.disabled;
where->pip = what.pip; where->pip = what.pip;
where->character = what.character; where->character = what.character;
@@ -235,87 +265,92 @@ bool place_on_top(Card *where, Card what) {
return false; return false;
} }
void tick(GameState *game_state, NotificationApp *notification) { void tick(GameState* game_state, NotificationApp* notification) {
game_state->last_tick = furi_get_tick(); game_state->last_tick = furi_get_tick();
uint8_t row = game_state->selectRow; uint8_t row = game_state->selectRow;
uint8_t column = game_state->selectColumn; uint8_t column = game_state->selectColumn;
if (game_state->state != GameStatePlay && game_state->state != GameStateAnimate) return; if(game_state->state != GameStatePlay && game_state->state != GameStateAnimate) return;
bool wasAction = false; bool wasAction = false;
if (handleInput(game_state)) { if(handleInput(game_state)) {
if (game_state->state == GameStatePlay) { if(game_state->state == GameStatePlay) {
if(game_state->top_cards[0].character == 11 &&
if(game_state->top_cards[0].character==11 && game_state->top_cards[1].character==11 && game_state->top_cards[2].character==11 && game_state->top_cards[3].character==11){ game_state->top_cards[1].character == 11 &&
game_state->state=GameStateAnimate; game_state->top_cards[2].character == 11 &&
game_state->top_cards[3].character == 11) {
game_state->state = GameStateAnimate;
return; return;
} }
if (game_state->longPress && game_state->dragging_hand.index == 1) { if(game_state->longPress && game_state->dragging_hand.index == 1) {
for (uint8_t i = 0; i < 4; i++) { for(uint8_t i = 0; i < 4; i++) {
if (place_on_top(&(game_state->top_cards[i]), game_state->dragging_hand.cards[0])) { if(place_on_top(
&(game_state->top_cards[i]), game_state->dragging_hand.cards[0])) {
remove_drag(game_state); remove_drag(game_state);
wasAction = true; wasAction = true;
break; break;
} }
} }
} else { } else {
if (row == 0 && column == 0 && game_state->dragging_hand.index == 0) { if(row == 0 && column == 0 && game_state->dragging_hand.index == 0) {
FURI_LOG_D(APP_NAME, "Drawing card"); FURI_LOG_D(APP_NAME, "Drawing card");
game_state->deck.index++; game_state->deck.index++;
wasAction = true; wasAction = true;
if (game_state->deck.index >= (game_state->deck.card_count)) if(game_state->deck.index >= (game_state->deck.card_count))
game_state->deck.index = -1; game_state->deck.index = -1;
} }
//pick/place from deck //pick/place from deck
else if (row == 0 && column == 1) { else if(row == 0 && column == 1) {
//place //place
if (game_state->dragging_deck) { if(game_state->dragging_deck) {
wasAction = true; wasAction = true;
game_state->dragging_deck = false; game_state->dragging_deck = false;
game_state->dragging_hand.index = 0; game_state->dragging_hand.index = 0;
} }
//pick //pick
else { else {
if (game_state->dragging_hand.index == 0 && game_state->deck.index >= 0) { if(game_state->dragging_hand.index == 0 && game_state->deck.index >= 0) {
wasAction = true; wasAction = true;
game_state->dragging_deck = true; game_state->dragging_deck = true;
add_to_hand(&(game_state->dragging_hand), game_state->deck.cards[game_state->deck.index]); add_to_hand(
&(game_state->dragging_hand),
game_state->deck.cards[game_state->deck.index]);
} }
} }
} }
//place on top row //place on top row
else if (row == 0 && game_state->dragging_hand.index == 1) { else if(row == 0 && game_state->dragging_hand.index == 1) {
column -= 3; column -= 3;
Card currCard = game_state->dragging_hand.cards[0]; Card currCard = game_state->dragging_hand.cards[0];
wasAction = place_on_top(&(game_state->top_cards[column]), currCard); wasAction = place_on_top(&(game_state->top_cards[column]), currCard);
if (wasAction) if(wasAction) remove_drag(game_state);
remove_drag(game_state);
} }
//pick/place from bottom //pick/place from bottom
else if (row == 1) { else if(row == 1) {
Hand *curr_hand = &(game_state->bottom_columns[column]); Hand* curr_hand = &(game_state->bottom_columns[column]);
//pick up //pick up
if (game_state->dragging_hand.index == 0) { if(game_state->dragging_hand.index == 0) {
Card curr_card = curr_hand->cards[curr_hand->index - 1]; Card curr_card = curr_hand->cards[curr_hand->index - 1];
if (curr_card.flipped) { if(curr_card.flipped) {
curr_hand->cards[curr_hand->index - 1].flipped = false; curr_hand->cards[curr_hand->index - 1].flipped = false;
wasAction = true; wasAction = true;
} else { } else {
if (curr_hand->index > 0) { if(curr_hand->index > 0) {
extract_hand_region(curr_hand, &(game_state->dragging_hand), extract_hand_region(
curr_hand->index - game_state->selected_card - 1); curr_hand,
&(game_state->dragging_hand),
curr_hand->index - game_state->selected_card - 1);
game_state->selected_card = 0; game_state->selected_card = 0;
game_state->dragging_column = column; game_state->dragging_column = column;
wasAction = true; wasAction = true;
} }
} }
} }
//place //place
else { else {
Card first = game_state->dragging_hand.cards[0]; Card first = game_state->dragging_hand.cards[0];
if (game_state->dragging_column == column || if(game_state->dragging_column == column ||
(curr_hand->index == 0 && first.character == 11) || (curr_hand->index == 0 && first.character == 11) ||
can_place_card(curr_hand->cards[curr_hand->index - 1], first) can_place_card(curr_hand->cards[curr_hand->index - 1], first)) {
) {
add_hand_region(curr_hand, &(game_state->dragging_hand)); add_hand_region(curr_hand, &(game_state->dragging_hand));
remove_drag(game_state); remove_drag(game_state);
wasAction = true; wasAction = true;
@@ -324,38 +359,29 @@ void tick(GameState *game_state, NotificationApp *notification) {
} }
} }
if(!wasAction) {
if (!wasAction) {
notification_message(notification, &sequence_fail); notification_message(notification, &sequence_fail);
} }
} }
} }
if (game_state->state == GameStateAnimate) { if(game_state->state == GameStateAnimate) {
if (game_state->animation.started) if(game_state->animation.started) game_state->state = GameStateStart;
game_state->state = GameStateStart;
game_state->animation.started = true; game_state->animation.started = true;
if (game_state->animation.x < -20 || game_state->animation.x > 128) { if(game_state->animation.x < -20 || game_state->animation.x > 128) {
game_state->animation.deck++; game_state->animation.deck++;
if (game_state->animation.deck > 3) if(game_state->animation.deck > 3) game_state->animation.deck = 0;
game_state->animation.deck = 0;
int8_t cardIndex = 11 - game_state->animation.indexes[game_state->animation.deck]; int8_t cardIndex = 11 - game_state->animation.indexes[game_state->animation.deck];
if (game_state->animation.indexes[0] == 13 && if(game_state->animation.indexes[0] == 13 && game_state->animation.indexes[1] == 13 &&
game_state->animation.indexes[1] == 13 && game_state->animation.indexes[2] == 13 && game_state->animation.indexes[3] == 13) {
game_state->animation.indexes[2] == 13 &&
game_state->animation.indexes[3] == 13) {
game_state->state = GameStateStart; game_state->state = GameStateStart;
return; return;
} }
if (cardIndex == -1) if(cardIndex == -1) cardIndex = 12;
cardIndex = 12; game_state->animation.card = (Card){
game_state->animation.card = (Card) { game_state->top_cards[game_state->animation.deck].pip, cardIndex, false, false};
game_state->top_cards[game_state->animation.deck].pip,
cardIndex,
false, false
};
game_state->animation.indexes[game_state->animation.deck]++; game_state->animation.indexes[game_state->animation.deck]++;
game_state->animation.vx = -(rand() % 3 + 1) * (rand() % 2 == 1 ? 1 : -1); game_state->animation.vx = -(rand() % 3 + 1) * (rand() % 2 == 1 ? 1 : -1);
game_state->animation.vy = (rand() % 3 + 1); game_state->animation.vy = (rand() % 3 + 1);
@@ -365,16 +391,16 @@ void tick(GameState *game_state, NotificationApp *notification) {
game_state->animation.x += game_state->animation.vx; game_state->animation.x += game_state->animation.vx;
game_state->animation.y -= game_state->animation.vy; game_state->animation.y -= game_state->animation.vy;
game_state->animation.vy -= 1; game_state->animation.vy -= 1;
if (game_state->animation.vy < -10)game_state->animation.vy = -10; if(game_state->animation.vy < -10) game_state->animation.vy = -10;
if (game_state->animation.y > 41) { if(game_state->animation.y > 41) {
game_state->animation.y = 41; game_state->animation.y = 41;
game_state->animation.vy = -(game_state->animation.vy * 0.7f); game_state->animation.vy = -(game_state->animation.vy * 0.7f);
} }
} }
} }
void init(GameState *game_state) { void init(GameState* game_state) {
game_state->selectColumn = 0; game_state->selectColumn = 0;
game_state->selected_card = 0; game_state->selected_card = 0;
game_state->selectRow = 0; game_state->selectRow = 0;
@@ -387,52 +413,49 @@ void init(GameState *game_state) {
game_state->state = GameStatePlay; game_state->state = GameStatePlay;
game_state->dragging_column = 8; game_state->dragging_column = 8;
for (uint8_t i = 0; i < 7; i++) { for(uint8_t i = 0; i < 7; i++) {
free_hand(&(game_state->bottom_columns[i])); free_hand(&(game_state->bottom_columns[i]));
init_hand(&(game_state->bottom_columns[i]), 21); init_hand(&(game_state->bottom_columns[i]), 21);
game_state->bottom_columns[i].index = 0; game_state->bottom_columns[i].index = 0;
for (uint8_t j = 0; j <= i; j++) { for(uint8_t j = 0; j <= i; j++) {
Card cur = remove_from_deck(0, &(game_state->deck)); Card cur = remove_from_deck(0, &(game_state->deck));
cur.flipped = i != j; cur.flipped = i != j;
add_to_hand(&(game_state->bottom_columns[i]), cur); add_to_hand(&(game_state->bottom_columns[i]), cur);
} }
} }
for (uint8_t i = 0; i < 4; i++) { for(uint8_t i = 0; i < 4; i++) {
game_state->animation.indexes[i] = 0; game_state->animation.indexes[i] = 0;
game_state->top_cards[i] = (Card) {0, 0, true, false}; game_state->top_cards[i] = (Card){0, 0, true, false};
} }
game_state->deck.index = -1; game_state->deck.index = -1;
} }
void init_start(GameState *game_state) { void init_start(GameState* game_state) {
game_state->input = InputKeyMAX; game_state->input = InputKeyMAX;
for (uint8_t i = 0; i < 7; i++) for(uint8_t i = 0; i < 7; i++) init_hand(&(game_state->bottom_columns[i]), 21);
init_hand(&(game_state->bottom_columns[i]), 21);
init_hand(&(game_state->dragging_hand), 13); init_hand(&(game_state->dragging_hand), 13);
game_state->animation.buffer = make_buffer(); game_state->animation.buffer = make_buffer();
} }
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);
} }
int32_t solitaire_app(void *p) { int32_t solitaire_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));
init_start(game_state); init_start(game_state);
set_card_graphics(&I_card_graphics); set_card_graphics(&I_card_graphics);
@@ -440,72 +463,70 @@ int32_t solitaire_app(void *p) {
game_state->processing = true; game_state->processing = true;
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;
} }
NotificationApp *notification = furi_record_open(RECORD_NOTIFICATION); NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
notification_message_block(notification, &sequence_display_backlight_enforce_on); notification_message_block(notification, &sequence_display_backlight_enforce_on);
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 = FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 30); furi_timer_start(timer, furi_kernel_get_tick_frequency() / 30);
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, 150); FuriStatus event_status = furi_message_queue_get(event_queue, &event, 150);
GameState *localstate = (GameState *) acquire_mutex_block(&state_mutex); GameState* localstate = (GameState*)acquire_mutex_block(&state_mutex);
bool hadChange = false; bool hadChange = false;
if (event_status == FuriStatusOk) { if(event_status == FuriStatusOk) {
if (event.type == EventTypeKey) { if(event.type == EventTypeKey) {
if (event.input.type == InputTypeLong) { if(event.input.type == InputTypeLong) {
game_state->longPress = true; game_state->longPress = true;
switch (event.input.key) { switch(event.input.key) {
case InputKeyUp: case InputKeyUp:
case InputKeyDown: case InputKeyDown:
case InputKeyRight: case InputKeyRight:
case InputKeyLeft: case InputKeyLeft:
case InputKeyOk: case InputKeyOk:
localstate->input = event.input.key; localstate->input = event.input.key;
break; break;
default: default:
break; break;
} }
} else if (event.input.type == InputTypePress) { } else if(event.input.type == InputTypePress) {
game_state->longPress = false; game_state->longPress = false;
switch (event.input.key) { switch(event.input.key) {
case InputKeyUp: case InputKeyUp:
case InputKeyDown: case InputKeyDown:
case InputKeyRight: case InputKeyRight:
case InputKeyLeft: case InputKeyLeft:
case InputKeyOk: case InputKeyOk:
if (event.input.key == InputKeyOk && localstate->state == GameStateStart) { if(event.input.key == InputKeyOk && localstate->state == GameStateStart) {
localstate->state = GameStatePlay; localstate->state = GameStatePlay;
init(game_state); init(game_state);
} } else {
else { hadChange = true;
hadChange = true; localstate->input = event.input.key;
localstate->input = event.input.key; }
} break;
break; case InputKeyBack:
case InputKeyBack: processing = false;
processing = false; return_code = 1;
return_code = 1; break;
break; default:
default: break;
break;
} }
} }
} else if (event.type == EventTypeTick) { } else if(event.type == EventTypeTick) {
tick(localstate, notification); tick(localstate, notification);
processing = localstate->processing; processing = localstate->processing;
localstate->input = InputKeyMAX; localstate->input = InputKeyMAX;
@@ -514,12 +535,10 @@ int32_t solitaire_app(void *p) {
FURI_LOG_W(APP_NAME, "osMessageQueue: event timeout"); FURI_LOG_W(APP_NAME, "osMessageQueue: event timeout");
// event timeout // event timeout
} }
if (hadChange || game_state->state == GameStateAnimate) if(hadChange || game_state->state == GameStateAnimate) view_port_update(view_port);
view_port_update(view_port);
release_mutex(&state_mutex, localstate); release_mutex(&state_mutex, localstate);
} }
notification_message_block(notification, &sequence_display_backlight_enforce_auto); notification_message_block(notification, &sequence_display_backlight_enforce_auto);
furi_timer_free(timer); furi_timer_free(timer);
view_port_enabled_set(view_port, false); view_port_enabled_set(view_port, false);
@@ -529,11 +548,10 @@ int32_t solitaire_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->animation.buffer); free(game_state->animation.buffer);
ui_cleanup(); ui_cleanup();
for (uint8_t i = 0; i < 7; i++) for(uint8_t i = 0; i < 7; i++) free_hand(&(game_state->bottom_columns[i]));
free_hand(&(game_state->bottom_columns[i]));
free(game_state->deck.cards); free(game_state->deck.cards);
free(game_state); free(game_state);

View File

@@ -24,7 +24,7 @@
static void render_callback(Canvas* const canvas, void* ctx) { static void render_callback(Canvas* const canvas, void* ctx) {
PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
if (plugin_state != NULL && !plugin_state->changing_scene) { if(plugin_state != NULL && !plugin_state->changing_scene) {
totp_scene_director_render(canvas, plugin_state); totp_scene_director_render(canvas, plugin_state);
} }
@@ -49,29 +49,43 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
totp_scene_director_init_scenes(plugin_state); totp_scene_director_init_scenes(plugin_state);
if (plugin_state->crypto_verify_data == NULL) { if(plugin_state->crypto_verify_data == NULL) {
DialogMessage* message = dialog_message_alloc(); DialogMessage* message = dialog_message_alloc();
dialog_message_set_buttons(message, "No", NULL, "Yes"); dialog_message_set_buttons(message, "No", NULL, "Yes");
dialog_message_set_text(message, "Would you like to setup PIN?", SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER, AlignCenter, AlignCenter); dialog_message_set_text(
message,
"Would you like to setup PIN?",
SCREEN_WIDTH_CENTER,
SCREEN_HEIGHT_CENTER,
AlignCenter,
AlignCenter);
DialogMessageButton dialog_result = dialog_message_show(plugin_state->dialogs, message); DialogMessageButton dialog_result = dialog_message_show(plugin_state->dialogs, message);
dialog_message_free(message); dialog_message_free(message);
if (dialog_result == DialogMessageButtonRight) { if(dialog_result == DialogMessageButtonRight) {
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL);
} else { } else {
totp_crypto_seed_iv(plugin_state, NULL, 0); totp_crypto_seed_iv(plugin_state, NULL, 0);
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
} }
} else if (plugin_state->pin_set) { } else if(plugin_state->pin_set) {
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL);
} else { } else {
totp_crypto_seed_iv(plugin_state, NULL, 0); totp_crypto_seed_iv(plugin_state, NULL, 0);
if (totp_crypto_verify_key(plugin_state)) { if(totp_crypto_verify_key(plugin_state)) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
} else { } else {
FURI_LOG_E(LOGGING_TAG, "Digital signature verification failed. Looks like conf file was created on another flipper and can't be used on any other"); FURI_LOG_E(
LOGGING_TAG,
"Digital signature verification failed. Looks like conf file was created on another flipper and can't be used on any other");
DialogMessage* message = dialog_message_alloc(); DialogMessage* message = dialog_message_alloc();
dialog_message_set_buttons(message, "Exit", NULL, NULL); dialog_message_set_buttons(message, "Exit", NULL, NULL);
dialog_message_set_text(message, "Digital signature verification failed", SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER, AlignCenter, AlignCenter); dialog_message_set_text(
message,
"Digital signature verification failed",
SCREEN_WIDTH_CENTER,
SCREEN_HEIGHT_CENTER,
AlignCenter,
AlignCenter);
dialog_message_show(plugin_state->dialogs, message); dialog_message_show(plugin_state->dialogs, message);
dialog_message_free(message); dialog_message_free(message);
return false; return false;
@@ -94,7 +108,7 @@ static void totp_plugin_state_free(PluginState* plugin_state) {
ListNode* node = plugin_state->tokens_list; ListNode* node = plugin_state->tokens_list;
ListNode* tmp; ListNode* tmp;
while (node != NULL) { while(node != NULL) {
tmp = node->next; tmp = node->next;
TokenInfo* tokenInfo = node->data; TokenInfo* tokenInfo = node->data;
token_info_free(tokenInfo); token_info_free(tokenInfo);
@@ -102,7 +116,7 @@ static void totp_plugin_state_free(PluginState* plugin_state) {
node = tmp; node = tmp;
} }
if (plugin_state->crypto_verify_data != NULL) { if(plugin_state->crypto_verify_data != NULL) {
free(plugin_state->crypto_verify_data); free(plugin_state->crypto_verify_data);
} }
free(plugin_state); free(plugin_state);
@@ -113,7 +127,7 @@ int32_t totp_app() {
PluginState* plugin_state = malloc(sizeof(PluginState)); PluginState* plugin_state = malloc(sizeof(PluginState));
furi_check(plugin_state != NULL); furi_check(plugin_state != NULL);
if (!totp_plugin_state_init(plugin_state)) { if(!totp_plugin_state_init(plugin_state)) {
FURI_LOG_E(LOGGING_TAG, "App state initialization failed\r\n"); FURI_LOG_E(LOGGING_TAG, "App state initialization failed\r\n");
totp_plugin_state_free(plugin_state); totp_plugin_state_free(plugin_state);
return 254; return 254;
@@ -138,18 +152,20 @@ int32_t totp_app() {
bool processing = true; bool processing = true;
uint32_t last_user_interaction_time = furi_get_tick(); uint32_t last_user_interaction_time = furi_get_tick();
while(processing) { while(processing) {
if (plugin_state->changing_scene) continue; if(plugin_state->changing_scene) continue;
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
PluginState* plugin_state_m = acquire_mutex_block(&state_mutex); PluginState* plugin_state_m = acquire_mutex_block(&state_mutex);
if(event_status == FuriStatusOk) { if(event_status == FuriStatusOk) {
if (event.type == EventTypeKey) { if(event.type == EventTypeKey) {
last_user_interaction_time = furi_get_tick(); last_user_interaction_time = furi_get_tick();
} }
processing = totp_scene_director_handle_event(&event, plugin_state_m); processing = totp_scene_director_handle_event(&event, plugin_state_m);
} else if (plugin_state_m->pin_set && plugin_state_m->current_scene != TotpSceneAuthentication && furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) { } else if(
plugin_state_m->pin_set && plugin_state_m->current_scene != TotpSceneAuthentication &&
furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) {
totp_scene_director_activate_scene(plugin_state_m, TotpSceneAuthentication, NULL); totp_scene_director_activate_scene(plugin_state_m, TotpSceneAuthentication, NULL);
} }