app updates

This commit is contained in:
RogueMaster
2022-10-24 16:34:57 -04:00
parent f0a8da63e4
commit 4787656e6b
9 changed files with 655 additions and 1141 deletions
+337 -370
View File
@@ -6,76 +6,82 @@
#include <math.h>
#include "util.h"
#include "defines.h"
#include "card.h"
#include "common/card.h"
#include "common/dml.h"
#include "common/queue.h"
#include "util.h"
#include "ui.h"
#include "BlackJack_icons.h"
#include "blackjack_icons.h"
#define DEALER_MAX 17
void start_round(GameState* game_state);
void start_round(GameState *game_state);
void init(GameState* game_state);
void init(GameState *game_state);
static void draw_ui(Canvas *const canvas, const GameState *game_state) {
static void draw_ui(Canvas* const canvas, const GameState* game_state) {
draw_money(canvas, game_state->player_score);
draw_score(canvas, true, handCount(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->animating && game_state->state == GameStatePlay) {
draw_play_menu(canvas, game_state);
if (!game_state->queue_state.running && game_state->state == GameStatePlay) {
render_menu(game_state->menu,canvas, 2, 47);
}
}
static void render_callback(Canvas* const canvas, void* ctx) {
const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25);
static void render_callback(Canvas *const canvas, void *ctx) {
const GameState *game_state = acquire_mutex((ValueMutex *) ctx, 25);
if(game_state == NULL) {
if (game_state == NULL) {
return;
}
canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, 0, 0, 128, 64);
if(game_state->state == GameStateStart) {
if (game_state->state == GameStateStart) {
canvas_draw_icon(canvas, 0, 0, &I_blackjack);
}
if(game_state->state == GameStateGameOver) {
if (game_state->state == GameStateGameOver) {
canvas_draw_icon(canvas, 0, 0, &I_endscreen);
}
if(game_state->state == GameStatePlay || game_state->state == GameStateDealer) {
if(game_state->state == GameStatePlay)
if (game_state->state == GameStatePlay || game_state->state == GameStateDealer) {
if (game_state->state == GameStatePlay)
draw_player_scene(canvas, game_state);
else
draw_dealer_scene(canvas, game_state);
animateQueue(game_state, canvas);
render_queue(&(game_state->queue_state), game_state, canvas);
draw_ui(canvas, game_state);
} else if(game_state->state == GameStateSettings) {
} else if (game_state->state == GameStateSettings) {
settings_page(canvas, game_state);
}
release_mutex((ValueMutex*)ctx, game_state);
release_mutex((ValueMutex *) ctx, game_state);
}
//region card draw
Card draw_card(GameState* game_state) {
Card draw_card(GameState *game_state) {
Card c = game_state->deck.cards[game_state->deck.index];
game_state->deck.index++;
return c;
}
void drawPlayerCard(GameState* game_state) {
void drawPlayerCard(void *ctx) {
GameState *game_state = ctx;
Card c = draw_card(game_state);
game_state->player_cards[game_state->player_card_count] = c;
game_state->player_card_count++;
if(game_state->selectedMenu == 0 &&
(game_state->player_score < game_state->settings.round_price || game_state->doubled))
game_state->selectedMenu = 1;
if(game_state->player_score < game_state->settings.round_price || game_state->doubled){
set_menu_state(game_state->menu, 0, false);
}
}
void drawDealerCard(GameState* game_state) {
void drawDealerCard(void *ctx) {
GameState *game_state = ctx;
Card c = draw_card(game_state);
game_state->dealer_cards[game_state->dealer_card_count] = c;
game_state->dealer_card_count++;
@@ -83,404 +89,319 @@ void drawDealerCard(GameState* game_state) {
//endregion
//region queue callbacks
void to_lose_state(
const GameState* game_state,
Canvas* const canvas,
uint32_t duration,
uint32_t margin) {
UNUSED(game_state);
UNUSED(margin);
if(duration == 0) return;
void to_lose_state(const void *ctx, Canvas *const canvas) {
const GameState *game_state = ctx;
if (game_state->settings.message_duration == 0)
return;
popup_frame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "You lost");
}
void to_bust_state(
const GameState* game_state,
Canvas* const canvas,
uint32_t duration,
uint32_t margin) {
UNUSED(game_state);
UNUSED(margin);
if(duration == 0) return;
void to_bust_state(const void *ctx, Canvas *const canvas) {
const GameState *game_state = ctx;
if (game_state->settings.message_duration == 0)
return;
popup_frame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Busted!");
}
void to_draw_state(
const GameState* game_state,
Canvas* const canvas,
uint32_t duration,
uint32_t margin) {
UNUSED(game_state);
UNUSED(margin);
if(duration == 0) return;
void to_draw_state(const void *ctx, Canvas *const canvas) {
const GameState *game_state = ctx;
if (game_state->settings.message_duration == 0)
return;
popup_frame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Draw");
}
void to_dealer_turn(
const GameState* game_state,
Canvas* const canvas,
uint32_t duration,
uint32_t margin) {
UNUSED(game_state);
UNUSED(margin);
if(duration == 0) return;
void to_dealer_turn(const void *ctx, Canvas *const canvas) {
const GameState *game_state = ctx;
if (game_state->settings.message_duration == 0)
return;
popup_frame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Dealers turn");
}
void to_win_state(
const GameState* game_state,
Canvas* const canvas,
uint32_t duration,
uint32_t margin) {
UNUSED(game_state);
UNUSED(margin);
if(duration == 0) return;
void to_win_state(const void *ctx, Canvas *const canvas) {
const GameState *game_state = ctx;
if (game_state->settings.message_duration == 0)
return;
popup_frame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "You win");
}
void to_start(
const GameState* game_state,
Canvas* const canvas,
uint32_t duration,
uint32_t margin) {
UNUSED(game_state);
UNUSED(margin);
if(duration == 0) return;
void to_start(const void *ctx, Canvas *const canvas) {
const GameState *game_state = ctx;
if (game_state->settings.message_duration == 0)
return;
popup_frame(canvas);
elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Round started");
}
void before_start(GameState* gameState) {
gameState->dealer_card_count = 0;
gameState->player_card_count = 0;
void before_start(void *ctx) {
GameState *game_state = ctx;
game_state->dealer_card_count = 0;
game_state->player_card_count = 0;
}
void start(GameState* game_state) {
void start(void *ctx) {
GameState *game_state = ctx;
start_round(game_state);
}
void draw(GameState* game_state) {
void draw(void *ctx) {
GameState *game_state = ctx;
game_state->player_score += game_state->bet;
game_state->bet = 0;
queue(game_state, start, before_start, to_start, game_state->settings.message_duration, 0);
enqueue(&(game_state->queue_state), game_state, start, before_start, to_start,
game_state->settings.message_duration);
}
void game_over(GameState* game_state) {
void game_over(void *ctx) {
GameState *game_state = ctx;
game_state->state = GameStateGameOver;
}
void lose(GameState* game_state) {
void lose(void *ctx) {
GameState *game_state = ctx;
game_state->state = GameStatePlay;
game_state->bet = 0;
if(game_state->player_score >= game_state->settings.round_price)
queue(game_state, start, before_start, to_start, game_state->settings.message_duration, 0);
else
queue(game_state, game_over, NULL, NULL, 0, 0);
if (game_state->player_score >= game_state->settings.round_price) {
enqueue(&(game_state->queue_state), game_state, start, before_start, to_start,
game_state->settings.message_duration);
} else {
enqueue(&(game_state->queue_state), game_state, game_over, NULL, NULL,
0);
}
}
void win(GameState* game_state) {
void win(void *ctx) {
GameState *game_state = ctx;
game_state->state = GameStatePlay;
game_state->player_score += game_state->bet * 2;
game_state->bet = 0;
queue(game_state, start, before_start, to_start, game_state->settings.message_duration, 0);
enqueue(&(game_state->queue_state), game_state, start, before_start, to_start,
game_state->settings.message_duration);
}
void dealerTurn(GameState* game_state) {
void dealerTurn(void *ctx) {
GameState *game_state = ctx;
game_state->state = GameStateDealer;
}
void dealer_card_animation(
const GameState* game_state,
Canvas* const canvas,
uint32_t duration,
uint32_t margin) {
float t = (float)(furi_get_tick() - game_state->animationStart) / (duration - margin);
float animationTime(const GameState *game_state){
return (float) (furi_get_tick() - game_state->queue_state.start) /
(float) (game_state->settings.animation_duration);
}
void dealer_card_animation(const void *ctx, Canvas *const canvas) {
const GameState *game_state = ctx;
float t = animationTime(game_state);
Card animatingCard = game_state->deck.cards[game_state->deck.index];
if(game_state->dealer_card_count > 1) {
if (game_state->dealer_card_count > 1) {
Vector end = card_pos_at_index(game_state->dealer_card_count);
if(!is_at_edge(game_state->dealer_card_count)) end.x -= CARD_HALF_WIDTH;
draw_card_animation(animatingCard, (Vector){0, 64}, (Vector){0, 32}, end, t, true, canvas);
draw_card_animation(animatingCard,
(Vector) {0, 64},
(Vector) {0, 32},
end,
t,
true,
canvas);
} else {
draw_card_animation(
animatingCard,
(Vector){32, -CARD_HEIGHT},
(Vector){64, 32},
(Vector){2, 2},
t,
false,
canvas);
// drawPlayerDeck(game_state->dealer_cards, game_state->dealer_card_count, canvas);
draw_card_animation(animatingCard,
(Vector) {32, -CARD_HEIGHT},
(Vector) {64, 32},
(Vector) {2, 2},
t,
false,
canvas);
// draw_deck(game_state->dealer_cards, game_state->dealer_card_count, canvas);
}
}
void dealer_back_card_animation(
const GameState* game_state,
Canvas* const canvas,
uint32_t duration,
uint32_t margin) {
float t = (float)(furi_get_tick() - game_state->animationStart) / (duration - margin);
Vector currentPos =
quadratic_2d((Vector){32, -CARD_HEIGHT}, (Vector){64, 32}, (Vector){13, 5}, t);
drawCardBackAt(currentPos.x, currentPos.y, canvas);
void dealer_back_card_animation(const void *ctx, Canvas *const canvas) {
const GameState *game_state = ctx;
float t = animationTime(game_state);
Vector currentPos = quadratic_2d((Vector) {32, -CARD_HEIGHT}, (Vector) {64, 32}, (Vector) {13, 5}, t);
draw_card_back_at(currentPos.x, currentPos.y, canvas);
}
void player_card_animation(
const GameState* game_state,
Canvas* const canvas,
uint32_t duration,
uint32_t margin) {
float t = (float)(furi_get_tick() - game_state->animationStart) / (duration - margin);
void player_card_animation(const void *ctx, Canvas *const canvas) {
const GameState *game_state = ctx;
float t = animationTime(game_state);
Card animatingCard = game_state->deck.cards[game_state->deck.index];
Vector end = card_pos_at_index(game_state->player_card_count);
if(!is_at_edge(game_state->player_card_count)) end.x -= CARD_HALF_WIDTH;
draw_card_animation(
animatingCard, (Vector){32, -CARD_HEIGHT}, (Vector){0, 32}, end, t, true, canvas);
// drawPlayerDeck(game_state->dealer_cards, game_state->player_card_count, canvas);
draw_card_animation(animatingCard,
(Vector) {32, -CARD_HEIGHT},
(Vector) {0, 32},
end,
t,
true,
canvas);
// draw_deck(game_state->dealer_cards, game_state->player_card_count, canvas);
}
//endregion
void player_tick(GameState* game_state) {
uint8_t score = handCount(game_state->player_cards, game_state->player_card_count);
if((game_state->doubled && score <= 21) || score == 21) {
queue(game_state, dealerTurn, NULL, NULL, game_state->settings.message_duration, 0);
} else if(score > 21) {
queue(game_state, lose, NULL, to_bust_state, game_state->settings.message_duration, 0);
void player_tick(GameState *game_state) {
uint8_t score = hand_count(game_state->player_cards, game_state->player_card_count);
if ((game_state->doubled && score <= 21) || score == 21) {
enqueue(&(game_state->queue_state), game_state, dealerTurn, NULL, to_dealer_turn,
game_state->settings.message_duration);
} else if (score > 21) {
enqueue(&(game_state->queue_state), game_state, lose, NULL, to_bust_state,
game_state->settings.message_duration);
} else {
if(game_state->selectDirection == DirectionUp && game_state->selectedMenu > 0 &&
game_state->player_score >= game_state->settings.round_price && !game_state->doubled) {
game_state->selectedMenu--;
if(game_state->selectDirection == DirectionUp || game_state->selectDirection == DirectionDown){
move_menu(game_state->menu, game_state->selectDirection == DirectionUp ? -1 : 1);
}
if(game_state->selectDirection == DirectionDown && game_state->selectedMenu < 2) {
game_state->selectedMenu++;
}
if(game_state->selectDirection == Select) {
//double
if(!game_state->doubled && game_state->selectedMenu == 0 &&
game_state->player_score >= game_state->settings.round_price) {
game_state->player_score -= game_state->settings.round_price;
game_state->bet += game_state->settings.round_price;
game_state->doubled = true;
game_state->selectedMenu = 1;
queue(
game_state,
drawPlayerCard,
NULL,
player_card_animation,
game_state->settings.animation_duration,
game_state->settings.animation_margin);
game_state->player_cards[game_state->player_card_count] =
game_state->deck.cards[game_state->deck.index];
score = handCount(game_state->player_cards, game_state->player_card_count + 1);
if(score > 21)
queue(
game_state,
lose,
NULL,
to_bust_state,
game_state->settings.message_duration,
0);
else
queue(
game_state,
dealerTurn,
NULL,
to_dealer_turn,
game_state->settings.message_duration,
0);
} //hit
else if(game_state->selectedMenu == 1) {
queue(
game_state,
drawPlayerCard,
NULL,
player_card_animation,
game_state->settings.animation_duration,
game_state->settings.animation_margin);
} //stay
else if(game_state->selectedMenu == 2) {
queue(
game_state,
dealerTurn,
NULL,
to_dealer_turn,
game_state->settings.message_duration,
0);
}
if (game_state->selectDirection == Select){
activate_menu(game_state->menu, game_state);
}
}
}
void dealer_tick(GameState* game_state) {
uint8_t dealer_score = handCount(game_state->dealer_cards, game_state->dealer_card_count);
uint8_t player_score = handCount(game_state->player_cards, game_state->player_card_count);
void dealer_tick(GameState *game_state) {
uint8_t dealer_score = hand_count(game_state->dealer_cards, game_state->dealer_card_count);
uint8_t player_score = hand_count(game_state->player_cards, game_state->player_card_count);
if(dealer_score >= DEALER_MAX) {
if(dealer_score > 21 || dealer_score < player_score)
queue(game_state, win, NULL, to_win_state, game_state->settings.message_duration, 0);
else if(dealer_score > player_score)
queue(game_state, lose, NULL, to_lose_state, game_state->settings.message_duration, 0);
else if(dealer_score == player_score)
queue(game_state, draw, NULL, to_draw_state, game_state->settings.message_duration, 0);
if (dealer_score >= DEALER_MAX) {
if (dealer_score > 21 || dealer_score < player_score) {
enqueue(&(game_state->queue_state), game_state, win, NULL, to_win_state,
game_state->settings.message_duration);
} else if (dealer_score > player_score) {
enqueue(&(game_state->queue_state), game_state, lose, NULL, to_lose_state,
game_state->settings.message_duration);
} else if (dealer_score == player_score) {
enqueue(&(game_state->queue_state), game_state, draw, NULL, to_draw_state,
game_state->settings.message_duration);
}
} else {
queue(
game_state,
drawDealerCard,
NULL,
dealer_card_animation,
game_state->settings.animation_duration,
game_state->settings.animation_margin);
enqueue(&(game_state->queue_state), game_state, drawDealerCard, NULL, dealer_card_animation,
game_state->settings.animation_duration);
}
}
void settings_tick(GameState* game_state) {
if(game_state->selectDirection == DirectionDown && game_state->selectedMenu < 4) {
void settings_tick(GameState *game_state) {
if (game_state->selectDirection == DirectionDown && game_state->selectedMenu < 4) {
game_state->selectedMenu++;
}
if(game_state->selectDirection == DirectionUp && game_state->selectedMenu > 0) {
if (game_state->selectDirection == DirectionUp && game_state->selectedMenu > 0) {
game_state->selectedMenu--;
}
if(game_state->selectDirection == DirectionLeft ||
game_state->selectDirection == DirectionRight) {
if (game_state->selectDirection == DirectionLeft || game_state->selectDirection == DirectionRight) {
int nextScore = 0;
switch(game_state->selectedMenu) {
case 0:
nextScore = game_state->settings.starting_money;
if(game_state->selectDirection == DirectionLeft)
nextScore -= 10;
else
nextScore += 10;
if(nextScore >= (int)game_state->settings.round_price && nextScore < 400)
game_state->settings.starting_money = nextScore;
break;
case 1:
nextScore = game_state->settings.round_price;
if(game_state->selectDirection == DirectionLeft)
nextScore -= 10;
else
nextScore += 10;
if(nextScore >= 5 && nextScore <= (int)game_state->settings.starting_money)
game_state->settings.round_price = nextScore;
break;
case 2:
nextScore = game_state->settings.animation_margin;
if(game_state->selectDirection == DirectionLeft)
nextScore -= 100;
else
nextScore += 100;
if(nextScore >= 0 && nextScore <= (int)game_state->settings.animation_duration)
game_state->settings.animation_margin = nextScore;
break;
case 3:
nextScore = game_state->settings.animation_duration;
if(game_state->selectDirection == DirectionLeft)
nextScore -= 100;
else
nextScore += 100;
if(nextScore >= (int)game_state->settings.animation_margin && nextScore < 2000)
game_state->settings.animation_duration = nextScore;
break;
case 4:
nextScore = game_state->settings.message_duration;
if(game_state->selectDirection == DirectionLeft)
nextScore -= 100;
else
nextScore += 100;
if(nextScore >= 0 && nextScore < 2000)
game_state->settings.message_duration = nextScore;
break;
case 5:
game_state->settings.sound_effects = !game_state->settings.sound_effects;
default:
break;
switch (game_state->selectedMenu) {
case 0:
nextScore = game_state->settings.starting_money;
if (game_state->selectDirection == DirectionLeft)
nextScore -= 10;
else
nextScore += 10;
if (nextScore >= (int) game_state->settings.round_price && nextScore < 400)
game_state->settings.starting_money = nextScore;
break;
case 1:
nextScore = game_state->settings.round_price;
if (game_state->selectDirection == DirectionLeft)
nextScore -= 10;
else
nextScore += 10;
if (nextScore >= 5 && nextScore <= (int) game_state->settings.starting_money)
game_state->settings.round_price = nextScore;
break;
case 2:
nextScore = game_state->settings.animation_duration;
if (game_state->selectDirection == DirectionLeft)
nextScore -= 100;
else
nextScore += 100;
if (nextScore >= 0 && nextScore < 2000)
game_state->settings.animation_duration = nextScore;
break;
case 3:
nextScore = game_state->settings.message_duration;
if (game_state->selectDirection == DirectionLeft)
nextScore -= 100;
else
nextScore += 100;
if (nextScore >= 0 && nextScore < 2000)
game_state->settings.message_duration = nextScore;
break;
case 4:
game_state->settings.sound_effects = !game_state->settings.sound_effects;
default:
break;
}
}
}
void tick(GameState* game_state) {
void tick(GameState *game_state) {
game_state->last_tick = furi_get_tick();
bool queue_ran = run_queue(game_state);
bool queue_ran = run_queue(&(game_state->queue_state), game_state);
switch(game_state->state) {
case GameStateGameOver:
case GameStateStart:
if(game_state->selectDirection == Select)
init(game_state);
else if(game_state->selectDirection == DirectionRight) {
game_state->selectedMenu = 0;
game_state->state = GameStateSettings;
}
break;
case GameStatePlay:
if(!game_state->started) {
game_state->selectedMenu = 0;
game_state->started = true;
queue(
game_state,
drawDealerCard,
NULL,
dealer_back_card_animation,
game_state->settings.animation_duration,
game_state->settings.animation_margin);
queue(
game_state,
drawPlayerCard,
NULL,
player_card_animation,
game_state->settings.animation_duration,
game_state->settings.animation_margin);
queue(
game_state,
drawDealerCard,
NULL,
dealer_card_animation,
game_state->settings.animation_duration,
game_state->settings.animation_margin);
queue(
game_state,
drawPlayerCard,
NULL,
player_card_animation,
game_state->settings.animation_duration,
game_state->settings.animation_margin);
}
if(!queue_ran) player_tick(game_state);
break;
case GameStateDealer:
if(!queue_ran) dealer_tick(game_state);
break;
case GameStateSettings:
settings_tick(game_state);
break;
default:
break;
switch (game_state->state) {
case GameStateGameOver:
case GameStateStart:
if (game_state->selectDirection == Select)
init(game_state);
else if (game_state->selectDirection == DirectionRight) {
game_state->selectedMenu = 0;
game_state->state = GameStateSettings;
}
break;
case GameStatePlay:
if (!game_state->started) {
game_state->selectedMenu = 0;
game_state->started = true;
enqueue(&(game_state->queue_state), game_state, drawDealerCard, NULL, dealer_back_card_animation,
game_state->settings.animation_duration);
enqueue(&(game_state->queue_state), game_state, drawPlayerCard, NULL, player_card_animation,
game_state->settings.animation_duration);
enqueue(&(game_state->queue_state), game_state, drawDealerCard, NULL, dealer_card_animation,
game_state->settings.animation_duration);
enqueue(&(game_state->queue_state), game_state, drawPlayerCard, NULL, player_card_animation,
game_state->settings.animation_duration);
}
if (!queue_ran)
player_tick(game_state);
break;
case GameStateDealer:
if (!queue_ran)
dealer_tick(game_state);
break;
case GameStateSettings:
settings_tick(game_state);
break;
default:
break;
}
game_state->selectDirection = None;
}
void start_round(GameState* game_state) {
void start_round(GameState *game_state) {
game_state->menu->current_menu=1;
game_state->player_card_count = 0;
game_state->dealer_card_count = 0;
game_state->selectedMenu = 0;
set_menu_state(game_state->menu, 0, true);
game_state->menu->enabled=true;
game_state->started = false;
game_state->doubled = false;
game_state->animating = true;
game_state->animationStart = 0;
shuffleDeck(&(game_state->deck));
game_state->queue_state.running = true;
shuffle_deck(&(game_state->deck));
game_state->doubled = false;
game_state->bet = game_state->settings.round_price;
if(game_state->player_score < game_state->settings.round_price) {
if (game_state->player_score < game_state->settings.round_price) {
game_state->state = GameStateGameOver;
} else {
game_state->player_score -= game_state->settings.round_price;
@@ -488,100 +409,145 @@ void start_round(GameState* game_state) {
game_state->state = GameStatePlay;
}
void init(GameState* game_state) {
void init(GameState *game_state) {
set_menu_state(game_state->menu, 0, true);
game_state->menu->enabled=true;
game_state->menu->current_menu=1;
game_state->settings = load_settings();
game_state->last_tick = 0;
game_state->processing = true;
game_state->selectedMenu = 0;
game_state->player_score = game_state->settings.starting_money;
generateDeck(&(game_state->deck));
generate_deck(&(game_state->deck), 6);
start_round(game_state);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
static void input_callback(InputEvent *input_event, FuriMessageQueue *event_queue) {
furi_assert(event_queue);
AppEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void update_timer_callback(FuriMessageQueue* event_queue) {
static void update_timer_callback(FuriMessageQueue *event_queue) {
furi_assert(event_queue);
AppEvent event = {.type = EventTypeTick};
furi_message_queue_put(event_queue, &event, 0);
}
int32_t blackjack_app(void* p) {
void doubleAction(void *state){
GameState *game_state = state;
if (!game_state->doubled && game_state->player_score >= game_state->settings.round_price) {
game_state->player_score -= game_state->settings.round_price;
game_state->bet += game_state->settings.round_price;
game_state->doubled = true;
enqueue(&(game_state->queue_state), game_state, drawPlayerCard, NULL, player_card_animation,
game_state->settings.animation_duration);
game_state->player_cards[game_state->player_card_count] = game_state->deck.cards[game_state->deck.index];
uint8_t score = hand_count(game_state->player_cards, game_state->player_card_count + 1);
if (score > 21) {
enqueue(&(game_state->queue_state), game_state, lose, NULL, to_bust_state,
game_state->settings.message_duration);
} else {
enqueue(&(game_state->queue_state), game_state, dealerTurn, NULL, to_dealer_turn,
game_state->settings.message_duration);
}
set_menu_state(game_state->menu, 0, false);
}
}
void hitAction(void *state){
GameState *game_state = state;
enqueue(&(game_state->queue_state), game_state, drawPlayerCard, NULL, player_card_animation,
game_state->settings.animation_duration);
}
void stayAction(void *state){
GameState *game_state = state;
enqueue(&(game_state->queue_state), game_state, dealerTurn, NULL, to_dealer_turn,
game_state->settings.message_duration);
}
int32_t blackjack_app(void *p) {
UNUSED(p);
int32_t return_code = 0;
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
FuriMessageQueue *event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
GameState* game_state = malloc(sizeof(GameState));
GameState *game_state = malloc(sizeof(GameState));
game_state->menu= malloc(sizeof(Menu));
game_state->menu->menu_width=40;
init(game_state);
add_menu(game_state->menu, "Double", doubleAction);
add_menu(game_state->menu, "Hit", hitAction);
add_menu(game_state->menu, "Stay", stayAction);
game_state->state = GameStateStart;
ValueMutex state_mutex;
if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) {
if (!init_mutex(&state_mutex, game_state, sizeof(GameState))) {
FURI_LOG_E(APP_NAME, "cannot create mutex\r\n");
return_code = 255;
goto free_and_exit;
}
ViewPort* view_port = view_port_alloc();
ViewPort *view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, &state_mutex);
view_port_input_callback_set(view_port, input_callback, event_queue);
FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue);
FuriTimer *timer =
furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25);
Gui* gui = furi_record_open("gui");
Gui *gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
AppEvent event;
for(bool processing = true; processing;) {
for (bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
GameState* game_state = (GameState*)acquire_mutex_block(&state_mutex);
if(event_status == FuriStatusOk) {
if(event.type == EventTypeKey) {
if(event.input.type == InputTypePress) {
switch(event.input.key) {
case InputKeyUp:
game_state->selectDirection = DirectionUp;
break;
case InputKeyDown:
game_state->selectDirection = DirectionDown;
break;
case InputKeyRight:
game_state->selectDirection = DirectionRight;
break;
case InputKeyLeft:
game_state->selectDirection = DirectionLeft;
break;
case InputKeyBack:
if(game_state->state == GameStateSettings) {
game_state->state = GameStateStart;
save_settings(game_state->settings);
} else
processing = false;
break;
case InputKeyOk:
game_state->selectDirection = Select;
break;
GameState *localstate = (GameState *) acquire_mutex_block(&state_mutex);
if (event_status == FuriStatusOk) {
if (event.type == EventTypeKey) {
if (event.input.type == InputTypePress) {
switch (event.input.key) {
case InputKeyUp:
localstate->selectDirection = DirectionUp;
break;
case InputKeyDown:
localstate->selectDirection = DirectionDown;
break;
case InputKeyRight:
localstate->selectDirection = DirectionRight;
break;
case InputKeyLeft:
localstate->selectDirection = DirectionLeft;
break;
case InputKeyBack:
if (localstate->state == GameStateSettings) {
localstate->state = GameStateStart;
save_settings(localstate->settings);
} else
processing = false;
break;
case InputKeyOk:
localstate->selectDirection = Select;
break;
}
}
} else if(event.type == EventTypeTick) {
tick(game_state);
processing = game_state->processing;
} else if (event.type == EventTypeTick) {
tick(localstate);
processing = localstate->processing;
}
} else {
FURI_LOG_D(APP_NAME, "osMessageQueue: event timeout");
// event timeout
}
view_port_update(view_port);
release_mutex(&state_mutex, game_state);
release_mutex(&state_mutex, localstate);
}
furi_timer_free(timer);
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
@@ -589,8 +555,9 @@ int32_t blackjack_app(void* p) {
view_port_free(view_port);
delete_mutex(&state_mutex);
free_and_exit:
queue_clear();
free_and_exit:
free_menu(game_state->menu);
queue_clear(&(game_state->queue_state));
free(game_state);
furi_message_queue_free(event_queue);
-270
View File
@@ -1,270 +0,0 @@
#include <math.h>
#include "card.h"
#include "util.h"
//region CardDesign
bool pips[4][49] = {
{//spades
0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0},
{
//hearts
0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
},
{//diamonds
0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0},
{//clubs
0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}};
bool backDesign[4] = {0, 1, 1, 0};
//endregion
uint8_t characters[13] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'A'};
uint8_t edge_cards[3] = {0, 8, 15};
//region Player card positions
uint8_t playerCardPositions[22][4] = {
//first row
{108, 38, 0, 0},
{98, 38, 0, 1},
{88, 38, 0, 1},
{78, 38, 0, 1},
{68, 38, 0, 1},
{58, 38, 0, 1},
{48, 38, 0, 1},
{38, 38, 0, 1},
//second row
{104, 26, 1, 0},
{94, 26, 1, 1},
{84, 26, 1, 1},
{74, 26, 1, 1},
{64, 26, 1, 1},
{54, 26, 1, 1},
{44, 26, 1, 1},
//third row
{99, 14, 1, 0},
{89, 14, 1, 1},
{79, 14, 1, 1},
{69, 14, 1, 1},
{59, 14, 1, 1},
{49, 14, 1, 1},
};
//endregion
void drawPlayerDeck(const Card cards[21], uint8_t count, Canvas* const canvas) {
for(uint8_t i = 0; i < count; i++) {
CardState state = Normal;
if(playerCardPositions[i][2] == 1 && playerCardPositions[i][3] == 1)
state = BottomAndRightCut;
else if(playerCardPositions[i][3] == 1)
state = RightCut;
else if(playerCardPositions[i][2] == 1)
state = BottomCut;
drawCardAt(
playerCardPositions[i][0],
playerCardPositions[i][1],
cards[i].pip,
cards[i].character,
state,
canvas);
}
}
bool is_at_edge(uint8_t index) {
for(uint8_t i = 0; i < 3; i++)
if(edge_cards[i] == index) return true;
return false;
}
Vector card_pos_at_index(uint8_t index) {
return (Vector){playerCardPositions[index][0], playerCardPositions[index][1]};
}
void drawCardAt(
int8_t pos_x,
int8_t pos_y,
uint8_t pip,
uint8_t character,
CardState state,
Canvas* const canvas) {
if(state == Normal) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT);
canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT);
} else {
if(state == BottomCut || state == BottomAndRightCut)
canvas_draw_line(
canvas, pos_x, pos_y, pos_x, pos_y + CARD_HALF_HEIGHT - 1); //half height line
if(state == BottomCut) {
canvas_draw_line(
canvas, pos_x, pos_y, pos_x + CARD_WIDTH - 1, pos_y); //full width line
canvas_draw_line(
canvas,
pos_x + CARD_WIDTH - 1,
pos_y,
pos_x + CARD_WIDTH - 1,
pos_y + CARD_HALF_HEIGHT - 1); //half height line
}
if(state == BottomAndRightCut) {
canvas_draw_line(
canvas, pos_x, pos_y, pos_x + CARD_HALF_WIDTH - 1, pos_y); //half width
}
if(state == RightCut) {
canvas_draw_line(
canvas, pos_x, pos_y, pos_x + CARD_HALF_WIDTH - 1, pos_y); //half width
canvas_draw_line(
canvas, pos_x, pos_y, pos_x, pos_y + CARD_HEIGHT - 1); //full height line
canvas_draw_line(
canvas,
pos_x,
pos_y + CARD_HEIGHT - 1,
pos_x + CARD_HALF_WIDTH - 1,
pos_y + CARD_HEIGHT - 1); //full height line
}
}
uint8_t left = pos_x + CORNER_MARGIN;
uint8_t right = (pos_x + CARD_WIDTH - CORNER_MARGIN - 7);
uint8_t top = pos_y + CORNER_MARGIN;
uint8_t bottom = (pos_y + CARD_HEIGHT - CORNER_MARGIN - 7);
for(uint8_t x = 0; x < 7; x++) {
for(uint8_t y = 0; y < 7; y++) {
if(pips[pip][x + y * 7]) {
if(state == Normal || state == BottomCut)
canvas_draw_dot(canvas, right + x + 1, top + y);
if(state == Normal || state == RightCut)
canvas_draw_dot(canvas, left + x - 1, bottom + y);
}
}
}
canvas_set_font(canvas, FontSecondary);
char drawChar[3];
if(character < 9)
snprintf(drawChar, sizeof(drawChar), "%i", character + 2);
else {
snprintf(drawChar, sizeof(drawChar), "%c", characters[character]);
}
canvas_set_font_direction(canvas, CanvasDirectionLeftToRight);
canvas_draw_str_aligned(canvas, left + 2, top + 3, AlignCenter, AlignCenter, drawChar);
canvas_set_font_direction(canvas, CanvasDirectionRightToLeft);
if(state == Normal) { //flipper crashes on non center aligned text when upside down
uint8_t margin = 9;
if(character == 8) //10 needs bigger margin
margin = 12;
canvas_draw_str_aligned(
canvas, right + margin, bottom - 3, AlignCenter, AlignCenter, drawChar);
}
canvas_set_font_direction(canvas, CanvasDirectionLeftToRight);
//canvas_draw_str(canvas, left, top, drawChar );
}
void drawCardBackAt(int8_t pos_x, int8_t pos_y, Canvas* const canvas) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT);
canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT);
for(uint8_t x = 0; x < CARD_WIDTH - 2; x++) {
for(uint8_t y = 0; y < CARD_HEIGHT - 2; y++) {
uint8_t _x = x;
uint8_t _y = y * 2;
if(backDesign[(_x + _y) % 4]) {
canvas_draw_dot(canvas, pos_x + x + 1, pos_y + y + 1);
}
}
}
}
void generateDeck(Deck* deck_ptr) {
uint16_t counter = 0;
for(uint8_t deck = 0; deck < DECK_COUNT; deck++) {
for(uint8_t pip = 0; pip < 4; pip++) {
for(uint8_t label = 0; label < 13; label++) {
deck_ptr->cards[counter] = (Card){pip, label};
counter++;
}
}
}
}
void shuffleDeck(Deck* deck_ptr) {
srand(DWT->CYCCNT);
deck_ptr->index = 0;
int max = DECK_COUNT * 52;
for(int i = 0; i < max; i++) {
int r = i + (rand() % (max - i));
Card tmp = deck_ptr->cards[i];
deck_ptr->cards[i] = deck_ptr->cards[r];
deck_ptr->cards[r] = tmp;
}
}
uint8_t handCount(const Card cards[21], uint8_t count) {
uint8_t aceCount = 0;
uint8_t score = 0;
for(uint8_t i = 0; i < count; i++) {
if(cards[i].character == 12)
aceCount++;
else {
if(cards[i].character > 8)
score += 10;
else
score += cards[i].character + 2;
}
}
for(uint8_t i = 0; i < aceCount; i++) {
if((score + 11) <= 21)
score += 11;
else
score++;
}
return score;
}
void draw_card_animation(
Card animatingCard,
Vector from,
Vector control,
Vector to,
float t,
bool extra_margin,
Canvas* const canvas) {
float time = t;
if(extra_margin) {
time += 0.2;
}
Vector currentPos = quadratic_2d(from, control, to, time);
if(t > 1) {
drawCardAt(
currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, Normal, canvas);
} else {
if(t < 0.5)
drawCardBackAt(currentPos.x, currentPos.y, canvas);
else
drawCardAt(
currentPos.x,
currentPos.y,
animatingCard.pip,
animatingCard.character,
Normal,
canvas);
}
}
-64
View File
@@ -1,64 +0,0 @@
#ifndef _card_h
#define _card_h
#include <gui/gui.h>
#define DECK_COUNT 6
#define CARD_HEIGHT 24
#define CARD_HALF_HEIGHT CARD_HEIGHT / 2
#define CARD_WIDTH 18
#define CARD_HALF_WIDTH CARD_WIDTH / 2
#define CORNER_MARGIN 3
#define LEGEND_SIZE 10
typedef struct Vector Vector;
typedef enum {
Normal,
BottomCut,
RightCut,
BottomAndRightCut,
TopCut,
LeftCut,
TopAndLeftCut
} CardState;
typedef struct {
uint8_t pip;
uint8_t character;
} Card;
typedef struct {
Card cards[52 * DECK_COUNT];
int index;
} Deck;
void drawPlayerDeck(const Card cards[21], uint8_t count, Canvas* const canvas);
void drawCardAt(
int8_t pos_x,
int8_t pos_y,
uint8_t pip,
uint8_t character,
CardState state,
Canvas* const canvas);
void drawCardBackAt(int8_t pos_x, int8_t pos_y, Canvas* const canvas);
void generateDeck(Deck* deck_ptr);
void shuffleDeck(Deck* deck_ptr);
void draw_card_animation(
Card animatingCard,
Vector from,
Vector control,
Vector to,
float t,
bool extra_margin,
Canvas* const canvas);
Vector card_pos_at_index(uint8_t index);
bool is_at_edge(uint8_t index);
uint8_t handCount(const Card cards[21], uint8_t count);
#endif
+17 -11
View File
@@ -5,14 +5,13 @@
#include <gui/elements.h>
#include <flipper_format/flipper_format.h>
#include <flipper_format/flipper_format_i.h>
#include "card.h"
#include "common/card.h"
#include "common/queue.h"
#include "common/menu.h"
#define APP_NAME "Blackjack"
//#define ANIMATION_TIME furi_ms_to_ticks(1500)
//#define ANIMATION_END_MARGIN furi_ms_to_ticks(200)
#define CONF_ANIMATION_DURATION "AnimationDuration"
#define CONF_ANIMATION_MARGIN "AnimationMargin"
#define CONF_MESSAGE_DURATION "MessageDuration"
#define CONF_STARTING_MONEY "StartingMoney"
#define CONF_ROUND_PRICE "RoundPrice"
@@ -23,8 +22,7 @@ typedef enum {
EventTypeKey,
} EventType;
typedef struct {
uint32_t animation_margin;
typedef struct{
uint32_t animation_duration;
uint32_t message_duration;
uint32_t starting_money;
@@ -45,7 +43,14 @@ typedef enum {
GameStateDealer,
} PlayState;
typedef enum { DirectionUp, DirectionRight, DirectionDown, DirectionLeft, Select, None } Direction;
typedef enum {
DirectionUp,
DirectionRight,
DirectionDown,
DirectionLeft,
Select,
None
} Direction;
typedef struct {
Card player_cards[21];
@@ -58,13 +63,14 @@ typedef struct {
uint32_t player_score;
uint32_t bet;
bool doubled;
bool animating;
bool started;
uint8_t selectedMenu;
bool doubled;
bool started;
bool processing;
Deck deck;
PlayState state;
QueueState queue_state;
Menu *menu;
unsigned int last_tick;
unsigned int animationStart;
} GameState;
+78 -99
View File
@@ -2,39 +2,36 @@
#include <notification/notification_messages.h>
#include "ui.h"
#include "card.h"
#include "util.h"
#define LINE_HEIGHT 16
#define ITEM_PADDING 4
const char MoneyMul[4] = {'K', 'B', 'T', 'S'};
const char MoneyMul[4] = {
'K', 'B', 'T', 'S'
};
void draw_player_scene(Canvas* const canvas, const GameState* game_state) {
void draw_player_scene(Canvas *const canvas, const GameState *game_state) {
int max_card = game_state->player_card_count;
if(max_card > 0) drawPlayerDeck((game_state->player_cards), max_card, canvas);
if (max_card > 0)
draw_deck((game_state->player_cards), max_card, canvas);
if(game_state->dealer_card_count > 0) drawCardBackAt(13, 5, canvas);
if (game_state->dealer_card_count > 0)
draw_card_back_at(13, 5, canvas);
max_card = game_state->dealer_card_count;
if(max_card > 1) {
drawCardAt(
2,
2,
game_state->dealer_cards[1].pip,
game_state->dealer_cards[1].character,
Normal,
canvas);
if (max_card > 1) {
draw_card_at(2, 2, game_state->dealer_cards[1].pip, game_state->dealer_cards[1].character,
canvas);
}
}
void draw_dealer_scene(Canvas* const canvas, const GameState* game_state) {
void draw_dealer_scene(Canvas *const canvas, const GameState *game_state) {
uint8_t max_card = game_state->dealer_card_count;
drawPlayerDeck((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_draw_box(canvas, 32, 15, 66, 13);
canvas_set_color(canvas, ColorBlack);
@@ -42,16 +39,15 @@ void popup_frame(Canvas* const canvas) {
canvas_set_font(canvas, FontSecondary);
}
void draw_play_menu(Canvas* const canvas, const GameState* game_state) {
const char* menus[3] = {"Double", "Hit", "Stay"};
for(uint8_t m = 0; m < 3; m++) {
if(m == 0 &&
(game_state->doubled || game_state->player_score < game_state->settings.round_price))
continue;
void draw_play_menu(Canvas *const canvas, const GameState *game_state) {
const char *menus[3] = {"Double", "Hit", "Stay"};
for (uint8_t m = 0; m < 3; m++) {
if (m == 0 && (game_state->doubled || game_state->player_score < game_state->settings.round_price)) continue;
int y = m * 13 + 25;
canvas_set_color(canvas, ColorBlack);
if(game_state->selectedMenu == m) {
if (game_state->selectedMenu == m) {
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 1, y, 31, 12);
} else {
@@ -61,7 +57,7 @@ void draw_play_menu(Canvas* const canvas, const GameState* game_state) {
canvas_draw_frame(canvas, 1, y, 31, 12);
}
if(game_state->selectedMenu == m)
if (game_state->selectedMenu == m)
canvas_set_color(canvas, ColorWhite);
else
canvas_set_color(canvas, ColorBlack);
@@ -69,34 +65,35 @@ void draw_play_menu(Canvas* const canvas, const GameState* game_state) {
}
}
void draw_screen(Canvas* const canvas, const bool* points) {
for(uint8_t x = 0; x < 128; x++) {
for(uint8_t y = 0; y < 64; y++) {
if(points[y * 128 + x]) canvas_draw_dot(canvas, x, y);
void draw_screen(Canvas *const canvas, const bool *points) {
for (uint8_t x = 0; x < 128; x++) {
for (uint8_t y = 0; y < 64; y++) {
if (points[y * 128 + x])
canvas_draw_dot(canvas, x, y);
}
}
}
void draw_score(Canvas* const canvas, bool top, uint8_t amount) {
void draw_score(Canvas *const canvas, bool top, uint8_t amount) {
char drawChar[20];
snprintf(drawChar, sizeof(drawChar), "Player score: %i", amount);
if(top)
if (top)
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, drawChar);
else
canvas_draw_str_aligned(canvas, 64, 62, AlignCenter, AlignBottom, drawChar);
}
void draw_money(Canvas* const canvas, uint32_t score) {
void draw_money(Canvas *const canvas, uint32_t score) {
canvas_set_font(canvas, FontSecondary);
char drawChar[10];
uint32_t currAmount = score;
if(currAmount < 1000) {
if (currAmount < 1000) {
snprintf(drawChar, sizeof(drawChar), "$%lu", currAmount);
} else {
char c = 'K';
for(uint8_t i = 0; i < 4; i++) {
for (uint8_t i = 0; i < 4; i++) {
currAmount = currAmount / 1000;
if(currAmount < 1000) {
if (currAmount < 1000) {
c = MoneyMul[i];
break;
}
@@ -107,95 +104,77 @@ void draw_money(Canvas* const canvas, uint32_t score) {
canvas_draw_str_aligned(canvas, 126, 2, AlignRight, AlignTop, drawChar);
}
void draw_menu(
Canvas* const canvas,
const char* text,
const char* value,
int8_t y,
bool left_caret,
bool right_caret,
bool selected) {
UNUSED(selected);
if(y < 0 || y >= 64) return;
if(selected) {
void draw_menu(Canvas *const canvas, const char *text, const char *value, int8_t y, bool left_caret, bool right_caret,
bool selected) {
UNUSED(selected);
if (y < 0 || y >= 64) return;
if (selected) {
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 0, y, 122, LINE_HEIGHT);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_str_aligned(canvas, 4, y + ITEM_PADDING, AlignLeft, AlignTop, text);
if(left_caret) canvas_draw_str_aligned(canvas, 80, y + ITEM_PADDING, AlignLeft, AlignTop, "<");
if (left_caret)
canvas_draw_str_aligned(canvas, 80, y + ITEM_PADDING, AlignLeft, AlignTop, "<");
canvas_draw_str_aligned(canvas, 100, y + ITEM_PADDING, AlignCenter, AlignTop, value);
if(right_caret)
if (right_caret)
canvas_draw_str_aligned(canvas, 120, y + ITEM_PADDING, AlignRight, AlignTop, ">");
canvas_set_color(canvas, ColorBlack);
}
void settings_page(Canvas* const canvas, const GameState* gameState) {
void settings_page(Canvas *const canvas, const GameState *gameState) {
char drawChar[10];
int startY = 0;
if(LINE_HEIGHT * (gameState->selectedMenu + 1) >= 64) {
if (LINE_HEIGHT * (gameState->selectedMenu + 1) >= 64) {
startY -= (LINE_HEIGHT * (gameState->selectedMenu + 1)) - 64;
}
int scrollHeight = round(64 / 7.0) + ITEM_PADDING * 2;
int scrollPos = 64 / (7.0 / (gameState->selectedMenu + 1)) - ITEM_PADDING * 2;
int scrollHeight = round(64 / 6.0) + ITEM_PADDING * 2;
int scrollPos = 64 / (6.0 / (gameState->selectedMenu + 1)) - ITEM_PADDING * 2;
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 123, scrollPos, 4, scrollHeight);
canvas_draw_box(canvas, 125, 0, 1, 64);
snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.starting_money);
draw_menu(
canvas,
"Start money",
drawChar,
0 * LINE_HEIGHT + startY,
gameState->settings.starting_money > gameState->settings.round_price,
gameState->settings.starting_money < 400,
gameState->selectedMenu == 0);
draw_menu(canvas, "Start money", drawChar,
0 * LINE_HEIGHT + startY,
gameState->settings.starting_money > gameState->settings.round_price,
gameState->settings.starting_money < 400,
gameState->selectedMenu == 0
);
snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.round_price);
draw_menu(
canvas,
"Round price",
drawChar,
1 * LINE_HEIGHT + startY,
gameState->settings.round_price > 10,
gameState->settings.round_price < gameState->settings.starting_money,
gameState->selectedMenu == 1);
snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.animation_margin);
draw_menu(
canvas,
"Anim. margin",
drawChar,
2 * LINE_HEIGHT + startY,
gameState->settings.animation_margin > 0,
gameState->settings.animation_margin < gameState->settings.animation_duration,
gameState->selectedMenu == 2);
draw_menu(canvas, "Round price", drawChar,
1 * LINE_HEIGHT + startY,
gameState->settings.round_price > 10,
gameState->settings.round_price < gameState->settings.starting_money,
gameState->selectedMenu == 1
);
snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.animation_duration);
draw_menu(
canvas,
"Anim. length",
drawChar,
3 * LINE_HEIGHT + startY,
gameState->settings.animation_duration > gameState->settings.animation_margin,
gameState->settings.animation_duration < 2000,
gameState->selectedMenu == 3);
draw_menu(canvas, "Anim. length", drawChar,
2 * LINE_HEIGHT + startY,
gameState->settings.animation_duration > 0,
gameState->settings.animation_duration < 2000,
gameState->selectedMenu == 2
);
snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.message_duration);
draw_menu(
canvas,
"Popup time",
drawChar,
4 * LINE_HEIGHT + startY,
gameState->settings.message_duration > 0,
gameState->settings.message_duration < 2000,
gameState->selectedMenu == 4);
// draw_menu(canvas, "Sound", gameState->settings.sound_effects ? "Yes" : "No",
// 5 * LINE_HEIGHT + startY,
// true,
// true,
// gameState->selectedMenu == 5
// );
draw_menu(canvas, "Popup time", drawChar,
3 * LINE_HEIGHT + startY,
gameState->settings.message_duration > 0,
gameState->settings.message_duration < 2000,
gameState->selectedMenu == 3
);
// draw_menu(canvas, "Sound", gameState->settings.sound_effects ? "Yes" : "No",
// 5 * LINE_HEIGHT + startY,
// true,
// true,
// gameState->selectedMenu == 5
// );
}
+8 -9
View File
@@ -1,19 +1,18 @@
#pragma once
#include "defines.h"
#include "util.h"
#include <gui/gui.h>
void draw_player_scene(Canvas* const canvas, const GameState* game_state);
void draw_player_scene(Canvas *const canvas, const GameState *game_state);
void draw_dealer_scene(Canvas* const canvas, const GameState* game_state);
void draw_dealer_scene(Canvas *const canvas, const GameState *game_state);
void draw_play_menu(Canvas* const canvas, const GameState* game_state);
void draw_play_menu(Canvas *const canvas, const GameState *game_state);
void draw_score(Canvas* const canvas, bool top, uint8_t amount);
void draw_score(Canvas *const canvas, bool top, uint8_t amount);
void draw_money(Canvas* const canvas, uint32_t score);
void settings_page(Canvas* const canvas, const GameState* gameState);
void draw_money(Canvas *const canvas, uint32_t score);
void settings_page(Canvas *const canvas, const GameState * gameState);
void popup_frame(Canvas* const canvas);
void draw_screen(Canvas* const canvas, const bool* points);
void popup_frame(Canvas *const canvas);
void draw_screen(Canvas *const canvas, const bool* points);
+27 -127
View File
@@ -1,108 +1,16 @@
#include <storage/storage.h>
#include "util.h"
const char* CONFIG_FILE_PATH = EXT_PATH(".blackjack.settings");
static QueueItem* afterDelay;
float lerp(float v0, float v1, float t) {
if(t > 1) return v1;
return (1 - t) * v0 + t * v1;
}
Vector lerp_2d(Vector start, Vector end, float t) {
return (Vector){
lerp(start.x, end.x, t),
lerp(start.y, end.y, t),
};
}
Vector quadratic_2d(Vector start, Vector control, Vector end, float t) {
return lerp_2d(lerp_2d(start, control, t), lerp_2d(control, end, t), t);
}
void queue(
GameState* game_state,
void (*callback)(GameState* game_state),
void (*start)(GameState* game_state),
void (*processing)(
const GameState* gameState,
Canvas* const canvas,
uint32_t duration,
uint32_t margin),
uint32_t duration,
uint32_t margin) {
if(afterDelay == NULL) {
game_state->animationStart = game_state->last_tick;
afterDelay = malloc(sizeof(QueueItem));
afterDelay->callback = callback;
afterDelay->processing = processing;
afterDelay->start = start;
afterDelay->duration = duration;
afterDelay->margin = margin;
afterDelay->next = NULL;
} else {
QueueItem* next = afterDelay;
while(next->next != NULL) {
next = (QueueItem*)(next->next);
}
next->next = malloc(sizeof(QueueItem));
((QueueItem*)next->next)->callback = callback;
((QueueItem*)next->next)->processing = processing;
((QueueItem*)next->next)->start = start;
((QueueItem*)next->next)->duration = duration;
((QueueItem*)next->next)->margin = margin;
((QueueItem*)next->next)->next = NULL;
}
}
void queue_clear() {
while(afterDelay != NULL) {
QueueItem* f = afterDelay;
free(f);
afterDelay = f->next;
}
}
void dequeue(GameState* game_state) {
((QueueItem*)afterDelay)->callback(game_state);
QueueItem* f = afterDelay;
afterDelay = (QueueItem*)afterDelay->next;
free(f);
if(afterDelay != NULL && afterDelay->start != NULL) afterDelay->start(game_state);
game_state->animationStart = game_state->last_tick;
}
void animateQueue(const GameState* game_state, Canvas* const canvas) {
if(afterDelay != NULL && ((QueueItem*)afterDelay)->processing != NULL) {
((QueueItem*)afterDelay)
->processing(game_state, canvas, afterDelay->duration, afterDelay->margin);
}
}
bool run_queue(GameState* game_state) {
if(afterDelay != NULL) {
game_state->animating = true;
if((game_state->last_tick - game_state->animationStart) > afterDelay->duration) {
dequeue(game_state);
}
return true;
}
game_state->animating = false;
return false;
}
const char *CONFIG_FILE_PATH = EXT_PATH(".blackjack.settings");
void save_settings(Settings settings) {
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* file = flipper_format_file_alloc(storage);
Storage *storage = furi_record_open(RECORD_STORAGE);
FlipperFormat *file = flipper_format_file_alloc(storage);
FURI_LOG_D(APP_NAME, "Saving config");
if(flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) {
FURI_LOG_D(
APP_NAME, "Saving %s: %ld", CONF_ANIMATION_DURATION, settings.animation_duration);
flipper_format_update_uint32(
file, CONF_ANIMATION_DURATION, &(settings.animation_duration), 1);
if (flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) {
FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_ANIMATION_DURATION, settings.animation_duration);
flipper_format_update_uint32(file, CONF_ANIMATION_DURATION, &(settings.animation_duration), 1);
FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_ANIMATION_MARGIN, settings.animation_margin);
flipper_format_update_uint32(file, CONF_ANIMATION_MARGIN, &(settings.animation_margin), 1);
FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_MESSAGE_DURATION, settings.message_duration);
flipper_format_update_uint32(file, CONF_MESSAGE_DURATION, &(settings.message_duration), 1);
@@ -113,10 +21,10 @@ void save_settings(Settings settings) {
FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_ROUND_PRICE, settings.round_price);
flipper_format_update_uint32(file, CONF_ROUND_PRICE, &(settings.round_price), 1);
FURI_LOG_D(APP_NAME, "Saving %s: %i", CONF_SOUND_EFFECTS, settings.sound_effects ? 1 : 0);
FURI_LOG_D(APP_NAME, "Saving %s: %i", CONF_SOUND_EFFECTS, settings.sound_effects?1:0);
flipper_format_update_bool(file, CONF_SOUND_EFFECTS, &(settings.sound_effects), 1);
FURI_LOG_D(APP_NAME, "Config saved");
} else {
}else{
FURI_LOG_E(APP_NAME, "Save error");
}
flipper_format_file_close(file);
@@ -124,13 +32,10 @@ void save_settings(Settings settings) {
furi_record_close(RECORD_STORAGE);
}
void save_settings_file(FlipperFormat* file, Settings* settings) {
void save_settings_file(FlipperFormat *file, Settings *settings) {
flipper_format_write_header_cstr(file, CONFIG_FILE_HEADER, CONFIG_FILE_VERSION);
flipper_format_write_comment_cstr(file, "Card animation duration in ms");
flipper_format_write_uint32(file, CONF_ANIMATION_DURATION, &(settings->animation_duration), 1);
flipper_format_write_comment_cstr(
file, "Card animation margin in ms (how long the card will stay besides the played hand)");
flipper_format_write_uint32(file, CONF_ANIMATION_MARGIN, &(settings->animation_margin), 1);
flipper_format_write_comment_cstr(file, "Popup message duration in ms");
flipper_format_write_uint32(file, CONF_MESSAGE_DURATION, &(settings->message_duration), 1);
flipper_format_write_comment_cstr(file, "Player's starting money");
@@ -145,70 +50,65 @@ Settings load_settings() {
Settings settings;
FURI_LOG_D(APP_NAME, "Loading default settings");
settings.animation_margin = 200;
settings.animation_duration = 1500;
settings.animation_duration = 800;
settings.message_duration = 1500;
settings.starting_money = 200;
settings.round_price = 10;
settings.sound_effects = true;
FURI_LOG_D(APP_NAME, "Opening storage");
Storage* storage = furi_record_open(RECORD_STORAGE);
Storage *storage = furi_record_open(RECORD_STORAGE);
FURI_LOG_D(APP_NAME, "Allocating file");
FlipperFormat* file = flipper_format_file_alloc(storage);
FlipperFormat *file = flipper_format_file_alloc(storage);
FURI_LOG_D(APP_NAME, "Allocating string");
FuriString* string_value;
FuriString *string_value;
string_value = furi_string_alloc();
if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) != FSE_OK) {
if (storage_common_stat(storage, CONFIG_FILE_PATH, NULL) != FSE_OK) {
FURI_LOG_D(APP_NAME, "Config file %s not found, creating new one...", CONFIG_FILE_PATH);
if(!flipper_format_file_open_new(file, CONFIG_FILE_PATH)) {
if (!flipper_format_file_open_new(file, CONFIG_FILE_PATH)) {
FURI_LOG_E(APP_NAME, "Error creating new file %s", CONFIG_FILE_PATH);
flipper_format_file_close(file);
} else {
save_settings_file(file, &settings);
}
} else {
if(!flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) {
if (!flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) {
FURI_LOG_E(APP_NAME, "Error opening existing file %s", CONFIG_FILE_PATH);
flipper_format_file_close(file);
} else {
}
else {
uint32_t value;
bool valueBool;
FURI_LOG_D(APP_NAME, "Checking version");
if(!flipper_format_read_header(file, string_value, &value)) {
if (!flipper_format_read_header(file, string_value, &value)) {
FURI_LOG_E(APP_NAME, "Config file mismatch");
} else {
FURI_LOG_D(APP_NAME, "Loading %s", CONF_ANIMATION_DURATION);
if(flipper_format_read_uint32(file, CONF_ANIMATION_DURATION, &value, 1)) {
if (flipper_format_read_uint32(file, CONF_ANIMATION_DURATION, &value, 1)) {
settings.animation_duration = value;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ANIMATION_DURATION, value);
}
FURI_LOG_D(APP_NAME, "Loading %s", CONF_ANIMATION_MARGIN);
if(flipper_format_read_uint32(file, CONF_ANIMATION_MARGIN, &value, 1)) {
settings.animation_margin = value;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ANIMATION_MARGIN, value);
}
FURI_LOG_D(APP_NAME, "Loading %s", CONF_MESSAGE_DURATION);
if(flipper_format_read_uint32(file, CONF_MESSAGE_DURATION, &value, 1)) {
if (flipper_format_read_uint32(file, CONF_MESSAGE_DURATION, &value, 1)) {
settings.message_duration = value;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_MESSAGE_DURATION, value);
}
FURI_LOG_D(APP_NAME, "Loading %s", CONF_STARTING_MONEY);
if(flipper_format_read_uint32(file, CONF_STARTING_MONEY, &value, 1)) {
if (flipper_format_read_uint32(file, CONF_STARTING_MONEY, &value, 1)) {
settings.starting_money = value;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_STARTING_MONEY, value);
}
FURI_LOG_D(APP_NAME, "Loading %s", CONF_ROUND_PRICE);
if(flipper_format_read_uint32(file, CONF_ROUND_PRICE, &value, 1)) {
if (flipper_format_read_uint32(file, CONF_ROUND_PRICE, &value, 1)) {
settings.round_price = value;
FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ROUND_PRICE, value);
}
FURI_LOG_D(APP_NAME, "Loading %s", CONF_SOUND_EFFECTS);
if(flipper_format_read_bool(file, CONF_SOUND_EFFECTS, &valueBool, 1)) {
if (flipper_format_read_bool(file, CONF_SOUND_EFFECTS, &valueBool, 1)) {
settings.sound_effects = valueBool;
FURI_LOG_D(APP_NAME, "Loaded %s: %i", CONF_ROUND_PRICE, valueBool ? 1 : 0);
FURI_LOG_D(APP_NAME, "Loaded %s: %i", CONF_ROUND_PRICE, valueBool?1:0);
}
}
flipper_format_file_close(file);
@@ -216,7 +116,7 @@ Settings load_settings() {
}
furi_string_free(string_value);
// flipper_format_file_close(file);
// flipper_format_file_close(file);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return settings;
-36
View File
@@ -3,41 +3,5 @@
#define CONFIG_FILE_HEADER "Blackjack config file"
#define CONFIG_FILE_VERSION 1
typedef struct {
void (*callback)(GameState* game_state);
void (*processing)(
const GameState* game_state,
Canvas* const canvas,
uint32_t duration,
uint32_t margin);
void (*start)(GameState* game_state);
void* next;
uint32_t duration;
uint32_t margin;
} QueueItem;
struct Vector {
float x;
float y;
};
float lerp(float v0, float v1, float t);
void queue(
GameState* game_state,
void (*callback)(GameState* game_state),
void (*start)(GameState* game_state),
void (*processing)(
const GameState* gameState,
Canvas* const canvas,
uint32_t duration,
uint32_t margin),
uint32_t duration,
uint32_t margin);
bool run_queue(GameState* gameState);
void animateQueue(const GameState* gameState, Canvas* const canvas);
void queue_clear();
Vector lerp_2d(Vector start, Vector end, float t);
Vector quadratic_2d(Vector start, Vector control, Vector end, float t);
void save_settings(Settings settings);
Settings load_settings();
@@ -3,6 +3,7 @@
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_i2c.h>
#include <math.h>
#include <gui/gui.h>
#include <input/input.h>
@@ -11,12 +12,12 @@
#include <string.h>
#define TS_DEFAULT_VALUE 0xFFFF
#define TS_DEFAULT_VALUE 0xFFFF
#define HTU21D_ADDRESS (0x40 << 1)
#define HTU21D_ADDRESS (0x40 << 1)
#define HTU21D_CMD_TEMPERATURE 0xE3
#define HTU21D_CMD_HUMIDITY 0xE5
#define HTU21D_CMD_TEMPERATURE 0xE3
#define HTU21D_CMD_HUMIDITY 0xE5
#define DATA_BUFFER_SIZE 8
@@ -24,19 +25,19 @@
#define I2C_BUS &furi_hal_i2c_handle_external
typedef enum {
TSSInitializing,
TSSNoSensor,
TSSPendingUpdate,
TSSInitializing,
TSSNoSensor,
TSSPendingUpdate,
} TSStatus;
typedef enum {
TSEventTypeTick,
TSEventTypeInput,
TSEventTypeTick,
TSEventTypeInput,
} TSEventType;
typedef struct {
TSEventType type;
InputEvent input;
TSEventType type;
InputEvent input;
} TSEvent;
extern const NotificationSequence sequence_blink_red_100;
@@ -47,7 +48,8 @@ static TSStatus temperature_sensor_current_status = TSSInitializing;
// Temperature and Humidity data buffers, ready to print
char ts_data_buffer_temperature_c[DATA_BUFFER_SIZE];
char ts_data_buffer_temperature_f[DATA_BUFFER_SIZE];
char ts_data_buffer_humidity[DATA_BUFFER_SIZE];
char ts_data_buffer_relative_humidity[DATA_BUFFER_SIZE];
char ts_data_buffer_absolute_humidity[DATA_BUFFER_SIZE];
// <sumary>
// Executes an I2C cmd (trx)
@@ -56,32 +58,36 @@ char ts_data_buffer_humidity[DATA_BUFFER_SIZE];
// true if fetch was successful, false otherwise
// </returns>
static bool temperature_sensor_cmd(uint8_t cmd, uint8_t* buffer, uint8_t size) {
uint32_t timeout = furi_ms_to_ticks(100);
bool ret = false;
// Aquire I2C and check if device is ready, then release
furi_hal_i2c_acquire(I2C_BUS);
if(furi_hal_i2c_is_device_ready(I2C_BUS, HTU21D_ADDRESS, timeout)) {
furi_hal_i2c_release(I2C_BUS);
uint32_t timeout = furi_ms_to_ticks(100);
bool ret = false;
furi_hal_i2c_acquire(I2C_BUS);
// Transmit given command
ret = furi_hal_i2c_tx(I2C_BUS, HTU21D_ADDRESS, &cmd, 1, timeout);
furi_hal_i2c_release(I2C_BUS);
// Aquire I2C and check if device is ready, then release
furi_hal_i2c_acquire(I2C_BUS);
if (furi_hal_i2c_is_device_ready(I2C_BUS, HTU21D_ADDRESS, timeout))
{
furi_hal_i2c_release(I2C_BUS);
if(ret) {
uint32_t wait_ticks = furi_ms_to_ticks(50);
furi_delay_tick(wait_ticks);
furi_hal_i2c_acquire(I2C_BUS);
// Transmit given command
ret = furi_hal_i2c_tx(I2C_BUS, HTU21D_ADDRESS, &cmd, 1, timeout);
furi_hal_i2c_release(I2C_BUS);
furi_hal_i2c_acquire(I2C_BUS);
// Receive data
ret = furi_hal_i2c_rx(I2C_BUS, HTU21D_ADDRESS, buffer, size, timeout);
furi_hal_i2c_release(I2C_BUS);
}
} else
furi_hal_i2c_release(I2C_BUS);
if (ret)
{
uint32_t wait_ticks = furi_ms_to_ticks(50);
furi_delay_tick(wait_ticks);
return ret;
furi_hal_i2c_acquire(I2C_BUS);
// Receive data
ret = furi_hal_i2c_rx(I2C_BUS, HTU21D_ADDRESS, buffer, size, timeout);
furi_hal_i2c_release(I2C_BUS);
}
}
else
furi_hal_i2c_release(I2C_BUS);
return ret;
}
// <sumary>
@@ -95,185 +101,212 @@ static bool temperature_sensor_cmd(uint8_t cmd, uint8_t* buffer, uint8_t size) {
// true if fetch was successful, false otherwise
// </returns>
static bool temperature_sensor_fetch_data(double* temperature, double* humidity) {
bool ret = false;
bool ret = false;
uint16_t adc_raw;
uint16_t adc_raw;
uint8_t buffer[2] = {0x00};
uint8_t buffer[2] = { 0x00 };
// Fetch temperature
ret = temperature_sensor_cmd((uint8_t)HTU21D_CMD_TEMPERATURE, buffer, 2);
// Fetch temperature
ret = temperature_sensor_cmd((uint8_t)HTU21D_CMD_TEMPERATURE, buffer, 2);
if(ret) {
// Calculate temperature
adc_raw = ((uint16_t)(buffer[0] << 8) | (buffer[1]));
*temperature = (float)(adc_raw * 175.72 / 65536.00) - 46.85;
if (ret)
{
// Calculate temperature
adc_raw = ((uint16_t)(buffer[0] << 8) | (buffer[1]));
*temperature = (float)(adc_raw * 175.72 / 65536.00) - 46.85;
// Fetch humidity
ret = temperature_sensor_cmd((uint8_t)HTU21D_CMD_TEMPERATURE, buffer, 2);
// Fetch humidity
ret = temperature_sensor_cmd((uint8_t)HTU21D_CMD_TEMPERATURE, buffer, 2);
if(ret) {
// Calculate humidity
adc_raw = ((uint16_t)(buffer[0] << 8) | (buffer[1]));
*humidity = (float)(adc_raw * 125.0 / 65536.00) - 6.0;
}
}
if (ret)
{
// Calculate humidity
adc_raw = ((uint16_t)(buffer[0] << 8) | (buffer[1]));
*humidity = (float)(adc_raw * 125.0 / 65536.00) - 6.0;
}
}
return ret;
return ret;
}
// <sumary>
// Draw callback
// </sumary>
static void temperature_sensor_draw_callback(Canvas* canvas, void* ctx) {
UNUSED(ctx);
canvas_clear(canvas);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 10, "HTU21D Sensor");
UNUSED(ctx);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 62, "Press back to exit.");
canvas_clear(canvas);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 10, "HTU21D/Si7021 Sensor");
switch(temperature_sensor_current_status) {
case TSSInitializing:
canvas_draw_str(canvas, 2, 30, "Initializing..");
break;
case TSSNoSensor:
canvas_draw_str(canvas, 2, 30, "No sensor found!");
break;
case TSSPendingUpdate: {
canvas_draw_str(canvas, 6, 24, "Temperature");
canvas_draw_str(canvas, 80, 24, "Humidity");
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 62, "Press back to exit.");
// Draw vertical lines
canvas_draw_line(canvas, 68, 16, 68, 50);
canvas_draw_line(canvas, 69, 16, 69, 50);
switch (temperature_sensor_current_status) {
case TSSInitializing:
canvas_draw_str(canvas, 2, 30, "Initializing..");
break;
case TSSNoSensor:
canvas_draw_str(canvas, 2, 30, "No sensor found!");
break;
case TSSPendingUpdate: {
// Draw horizontal line
canvas_draw_line(canvas, 3, 27, 144, 27);
canvas_draw_str(canvas, 3, 24, "Temperature");
canvas_draw_str(canvas, 68, 24, "Humidity");
// Draw temperature and humidity values
canvas_draw_str(canvas, 14, 38, ts_data_buffer_temperature_c);
canvas_draw_str(canvas, 48, 38, "C");
canvas_draw_str(canvas, 14, 48, ts_data_buffer_temperature_f);
canvas_draw_str(canvas, 48, 48, "F");
canvas_draw_str(canvas, 78, 42, ts_data_buffer_humidity);
canvas_draw_str(canvas, 112, 42, "%");
// Draw vertical lines
canvas_draw_line(canvas, 61, 16, 61, 50);
canvas_draw_line(canvas, 62, 16, 62, 50);
// Draw horizontal line
canvas_draw_line(canvas, 2, 27, 122, 27);
// Draw temperature and humidity values
canvas_draw_str(canvas, 8, 38, ts_data_buffer_temperature_c);
canvas_draw_str(canvas, 42, 38, "C");
canvas_draw_str(canvas, 8, 48, ts_data_buffer_temperature_f);
canvas_draw_str(canvas, 42, 48, "F");
canvas_draw_str(canvas, 68, 38, ts_data_buffer_relative_humidity);
canvas_draw_str(canvas, 100, 38, "%");
canvas_draw_str(canvas, 68, 48, ts_data_buffer_absolute_humidity);
canvas_draw_str(canvas, 100, 48, "g/m3");
} break;
default:
break;
}
} break;
default:
break;
}
}
// <sumary>
// Input callback
// </sumary>
static void temperature_sensor_input_callback(InputEvent* input_event, void* ctx) {
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
TSEvent event = {.type = TSEventTypeInput, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
TSEvent event = { .type = TSEventTypeInput, .input = *input_event };
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
// <sumary>
// Timer callback
// </sumary>
static void temperature_sensor_timer_callback(FuriMessageQueue* event_queue) {
furi_assert(event_queue);
TSEvent event = {.type = TSEventTypeTick};
furi_message_queue_put(event_queue, &event, 0);
furi_assert(event_queue);
TSEvent event = { .type = TSEventTypeTick };
furi_message_queue_put(event_queue, &event, 0);
}
// <sumary>
// App entry point
// </sumary>
int32_t temperature_sensor_app(void* p) {
UNUSED(p);
furi_hal_power_suppress_charge_enter();
// Declare our variables
TSEvent tsEvent;
bool sensorFound = false;
double celsius, fahrenheit, humidity;
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(TSEvent));
// Declare our variables and assign variables a default value
TSEvent tsEvent;
bool sensorFound = false;
double celsius, fahrenheit, rel_humidity, abs_humidity = TS_DEFAULT_VALUE;
// Register callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, temperature_sensor_draw_callback, NULL);
view_port_input_callback_set(view_port, temperature_sensor_input_callback, event_queue);
// Used for absolute humidity calculation
double vapour_pressure = 0;
// Create timer and register its callback
FuriTimer* timer =
furi_timer_alloc(temperature_sensor_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency());
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(TSEvent));
// Register viewport
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
// Register callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, temperature_sensor_draw_callback, NULL);
view_port_input_callback_set(view_port, temperature_sensor_input_callback, event_queue);
// Used to notify the user by blinking red (error) or blue (fetch successful)
NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
// Create timer and register its callback
FuriTimer* timer = furi_timer_alloc(temperature_sensor_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency());
// Assign variables a default value
celsius = fahrenheit = humidity = TS_DEFAULT_VALUE;
// Register viewport
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
while(1) {
furi_check(furi_message_queue_get(event_queue, &tsEvent, FuriWaitForever) == FuriStatusOk);
// Used to notify the user by blinking red (error) or blue (fetch successful)
NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
// Handle events
if(tsEvent.type == TSEventTypeInput) {
// Exit on back key
if(tsEvent.input.key ==
InputKeyBack) // We dont check for type here, we can check the type of keypress like: (event.input.type == InputTypeShort)
break;
while (1) {
} else if(tsEvent.type == TSEventTypeTick) {
// Update sensor data
// Fetch data and set the sensor current status accordingly
sensorFound = temperature_sensor_fetch_data(&celsius, &humidity);
temperature_sensor_current_status = (sensorFound ? TSSPendingUpdate : TSSNoSensor);
furi_check(furi_message_queue_get(event_queue, &tsEvent, FuriWaitForever) == FuriStatusOk);
if(sensorFound) {
// Blink blue
notification_message(notifications, &sequence_blink_blue_100);
// Handle events
if (tsEvent.type == TSEventTypeInput) {
if(celsius != TS_DEFAULT_VALUE && humidity != TS_DEFAULT_VALUE) {
// Convert celsius to fahrenheit
fahrenheit = (celsius * 9 / 5) + 32;
// Exit on back key
if (tsEvent.input.key == InputKeyBack) // We dont check for type here, we can check the type of keypress like: (event.input.type == InputTypeShort)
break;
// Fill our buffers here, not on the canvas draw callback
snprintf(ts_data_buffer_temperature_c, DATA_BUFFER_SIZE, "%.2f", celsius);
snprintf(ts_data_buffer_temperature_f, DATA_BUFFER_SIZE, "%.2f", fahrenheit);
snprintf(ts_data_buffer_humidity, DATA_BUFFER_SIZE, "%.2f", humidity);
}
}
else if (tsEvent.type == TSEventTypeTick) {
} else {
// Reset our variables to their default values
celsius = fahrenheit = humidity = TS_DEFAULT_VALUE;
// Update sensor data
// Fetch data and set the sensor current status accordingly
sensorFound = temperature_sensor_fetch_data(&celsius, &rel_humidity);
temperature_sensor_current_status = (sensorFound ? TSSPendingUpdate : TSSNoSensor);
// Blink red
notification_message(notifications, &sequence_blink_red_100);
}
}
if (sensorFound) {
uint32_t wait_ticks = furi_ms_to_ticks(!sensorFound ? 100 : 500);
furi_delay_tick(wait_ticks);
}
// Blink blue
notification_message(notifications, &sequence_blink_blue_100);
furi_hal_power_suppress_charge_exit();
// Dobby is freee (free our variables, Flipper will crash if we don't do this!)
furi_timer_free(timer);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
if (celsius != TS_DEFAULT_VALUE && rel_humidity != TS_DEFAULT_VALUE) {
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_GUI);
// Convert celsius to fahrenheit
fahrenheit = (celsius * 9 / 5) + 32;
return 0;
// Calculate absolute humidity - For more info refer to https://github.com/Mywk/FlipperTemperatureSensor/issues/1
// Calculate saturation vapour pressure first
vapour_pressure = (double)6.11 * pow(10, (double)(((double)7.5 * celsius) / ((double)237.3 + celsius)));
// Then the vapour pressure in Pa
vapour_pressure = vapour_pressure * rel_humidity;
// Calculate absolute humidity
abs_humidity = (double)2.16679 * (double)(vapour_pressure / ((double)273.15 + celsius));
// Fill our buffers here, not on the canvas draw callback
snprintf(ts_data_buffer_temperature_c, DATA_BUFFER_SIZE, "%.2f", celsius);
snprintf(ts_data_buffer_temperature_f, DATA_BUFFER_SIZE, "%.2f", fahrenheit);
snprintf(ts_data_buffer_relative_humidity, DATA_BUFFER_SIZE, "%.2f", rel_humidity);
snprintf(ts_data_buffer_absolute_humidity, DATA_BUFFER_SIZE, "%.2f", abs_humidity);
}
}
else {
// Reset our variables to their default values
celsius = fahrenheit = rel_humidity = abs_humidity = TS_DEFAULT_VALUE;
// Blink red
notification_message(notifications, &sequence_blink_red_100);
}
}
uint32_t wait_ticks = furi_ms_to_ticks(!sensorFound ? 100 : 500);
furi_delay_tick(wait_ticks);
}
// Dobby is freee (free our variables, Flipper will crash if we don't do this!)
furi_timer_free(timer);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_GUI);
return 0;
}