BJ updated

This commit is contained in:
RogueMaster
2022-10-16 14:50:41 -04:00
parent 6f9c82650d
commit b2c9f0cc66
11 changed files with 625 additions and 172 deletions

View File

@@ -4,9 +4,10 @@ App(
apptype=FlipperAppType.EXTERNAL, apptype=FlipperAppType.EXTERNAL,
entry_point="blackjack_app", entry_point="blackjack_app",
cdefines=["APP_BLACKJACK"], cdefines=["APP_BLACKJACK"],
requires=["gui"], requires=["gui","storage"],
stack_size=1 * 1024, stack_size=2 * 1024,
order=30, order=30,
fap_icon="blackjack_10px.png", fap_icon="blackjack_10px.png",
fap_category="Games", fap_category="Games",
fap_icon_assets="assets"
) )

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -2,20 +2,22 @@
#include <gui/gui.h> #include <gui/gui.h>
#include <stdlib.h> #include <stdlib.h>
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#include <dialogs/dialogs.h>
#include <math.h> #include <math.h>
#include "util.h"
#include "defines.h" #include "defines.h"
#include "card.h" #include "card.h"
#include "util.h" #include "util.h"
#include "ui.h" #include "ui.h"
#define APP_NAME "Blackjack" #include "blackjack_icons.h"
#define STARTING_MONEY 200
#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);
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);
@@ -27,31 +29,32 @@ static void draw_ui(Canvas *const canvas, const GameState *game_state) {
} }
} }
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);
switch (game_state->state) { if (game_state->state == GameStateStart) {
case GameStateStart: canvas_draw_icon(canvas, 0, 0, &I_blackjack);
case GameStateGameOver:
draw_message_scene(canvas, game_state);
break;
case GameStatePlay:
draw_player_scene(canvas, game_state);
break;
case GameStateDealer:
draw_dealer_scene(canvas, game_state);
break;
} }
if (game_state->state != GameStateStart && game_state->state != GameStateGameOver) { 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)
draw_player_scene(canvas, game_state);
else
draw_dealer_scene(canvas, game_state);
animateQueue(game_state, canvas); animateQueue(game_state, canvas);
draw_ui(canvas, game_state); draw_ui(canvas, game_state);
} else if (game_state->state == GameStateSettings) {
settings_page(canvas, game_state);
} }
release_mutex((ValueMutex *) ctx, game_state); release_mutex((ValueMutex *) ctx, game_state);
@@ -64,56 +67,78 @@ Card draw_card(GameState *game_state) {
return c; return c;
} }
char *letters[4] = {"spade", "hearth", "diamond", "club"};
void drawPlayerCard(GameState *game_state) { void drawPlayerCard(GameState *game_state) {
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->selectedMenu == 0 &&
(game_state->player_score < game_state->settings.round_price || game_state->doubled))
game_state->selectedMenu = 1;
} }
void drawDealerCard(GameState *game_state) { void drawDealerCard(GameState *game_state) {
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++;
FURI_LOG_D(APP_NAME, "drawing dealer %s %i", letters[c.pip], c.character + 2);
} }
//endregion //endregion
//region queue callbacks //region queue callbacks
void to_lose_state(const GameState *game_state, Canvas *const canvas) { void to_lose_state(const GameState *game_state, Canvas *const canvas, uint32_t duration, uint32_t margin) {
UNUSED(game_state); UNUSED(game_state);
popupFrame(canvas); UNUSED(margin);
if (duration == 0)
return;
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 GameState *game_state, Canvas *const canvas) { void to_bust_state(const GameState *game_state, Canvas *const canvas, uint32_t duration, uint32_t margin) {
UNUSED(game_state); UNUSED(game_state);
popupFrame(canvas); UNUSED(margin);
if (duration == 0)
return;
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 GameState *game_state, Canvas *const canvas) { void to_draw_state(const GameState *game_state, Canvas *const canvas, uint32_t duration, uint32_t margin) {
UNUSED(game_state); UNUSED(game_state);
popupFrame(canvas); UNUSED(margin);
if (duration == 0)
return;
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 GameState *game_state, Canvas *const canvas) { void to_dealer_turn(const GameState *game_state, Canvas *const canvas, uint32_t duration, uint32_t margin) {
UNUSED(game_state); UNUSED(game_state);
popupFrame(canvas); UNUSED(margin);
if (duration == 0)
return;
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 GameState *game_state, Canvas *const canvas) { void to_win_state(const GameState *game_state, Canvas *const canvas, uint32_t duration, uint32_t margin) {
UNUSED(game_state); UNUSED(game_state);
popupFrame(canvas); UNUSED(margin);
if (duration == 0)
return;
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 GameState *game_state, Canvas *const canvas) { void to_start(const GameState *game_state, Canvas *const canvas, uint32_t duration, uint32_t margin) {
UNUSED(game_state); UNUSED(game_state);
popupFrame(canvas); UNUSED(margin);
if (duration == 0)
return;
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");
} }
@@ -130,7 +155,7 @@ void start(GameState *game_state) {
void draw(GameState *game_state) { void draw(GameState *game_state) {
game_state->player_score += game_state->bet; game_state->player_score += game_state->bet;
game_state->bet = 0; game_state->bet = 0;
queue(game_state, start, before_start, to_start); queue(game_state, start, before_start, to_start, game_state->settings.message_duration, 0);
} }
void game_over(GameState *game_state) { void game_over(GameState *game_state) {
@@ -140,33 +165,83 @@ void game_over(GameState *game_state) {
void lose(GameState *game_state) { void lose(GameState *game_state) {
game_state->state = GameStatePlay; game_state->state = GameStatePlay;
game_state->bet = 0; game_state->bet = 0;
if (game_state->player_score >= ROUND_PRICE) if (game_state->player_score >= game_state->settings.round_price)
queue(game_state, start, before_start, to_start); queue(game_state, start, before_start, to_start, game_state->settings.message_duration, 0);
else else
queue(game_state, game_over, NULL, NULL); queue(game_state, game_over, NULL, NULL, 0, 0);
} }
void win(GameState *game_state) { void win(GameState *game_state) {
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;
queue(game_state, start, before_start, to_start); queue(game_state, start, before_start, to_start, game_state->settings.message_duration, 0);
} }
void dealerTurn(GameState *game_state) { void dealerTurn(GameState *game_state) {
game_state->state = GameStateDealer; 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);
Card animatingCard = game_state->deck.cards[game_state->deck.index];
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);
} 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);
}
}
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 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);
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);
}
//endregion //endregion
void player_tick(GameState *game_state) { void player_tick(GameState *game_state) {
uint8_t score = handCount(game_state->player_cards, game_state->player_card_count); uint8_t score = handCount(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) {
queue(game_state, dealerTurn, NULL, NULL); queue(game_state, dealerTurn, NULL, NULL, game_state->settings.message_duration, 0);
} else if (score > 21) { } else if (score > 21) {
queue(game_state, lose, NULL, to_bust_state); queue(game_state, lose, NULL, to_bust_state, game_state->settings.message_duration, 0);
} else { } else {
if (game_state->selectDirection == DirectionUp && game_state->selectedMenu > 0) { if (game_state->selectDirection == DirectionUp && game_state->selectedMenu > 0 &&
game_state->player_score >= game_state->settings.round_price && !game_state->doubled) {
game_state->selectedMenu--; game_state->selectedMenu--;
} }
if (game_state->selectDirection == DirectionDown && game_state->selectedMenu < 2) { if (game_state->selectDirection == DirectionDown && game_state->selectedMenu < 2) {
@@ -175,65 +250,154 @@ void player_tick(GameState *game_state) {
if (game_state->selectDirection == Select) { if (game_state->selectDirection == Select) {
//double //double
if (!game_state->doubled && game_state->selectedMenu == 0 && if (!game_state->doubled && game_state->selectedMenu == 0 &&
game_state->player_score >= ROUND_PRICE) { game_state->player_score >= game_state->settings.round_price) {
game_state->player_score -= ROUND_PRICE; game_state->player_score -= game_state->settings.round_price;
game_state->bet += ROUND_PRICE; game_state->bet += game_state->settings.round_price;
game_state->doubled = true; game_state->doubled = true;
game_state->selectedMenu = 1; game_state->selectedMenu = 1;
queue(game_state, drawPlayerCard, NULL, draw_card_animation); 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]; 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); score = handCount(game_state->player_cards, game_state->player_card_count + 1);
if (score > 21) if (score > 21)
queue(game_state, lose, NULL, to_bust_state); queue(game_state, lose, NULL, to_bust_state, game_state->settings.message_duration, 0);
else else
queue(game_state, dealerTurn, NULL, to_dealer_turn); queue(game_state, dealerTurn, NULL, to_dealer_turn, game_state->settings.message_duration, 0);
} //hit } //hit
else if (game_state->selectedMenu == 1) { else if (game_state->selectedMenu == 1) {
queue(game_state, drawPlayerCard, NULL, draw_card_animation); queue(game_state, drawPlayerCard, NULL, player_card_animation, game_state->settings.animation_duration,
game_state->settings.animation_margin);
} //stay } //stay
else if (game_state->selectedMenu == 2) { else if (game_state->selectedMenu == 2) {
queue(game_state, dealerTurn, NULL, to_dealer_turn); queue(game_state, dealerTurn, NULL, to_dealer_turn, game_state->settings.message_duration, 0);
} }
} }
} }
} }
void dealer_tick(GameState *game_state) { void dealer_tick(GameState *game_state) {
uint8_t dealer_score = handCount(game_state->dealer_cards, game_state->dealer_card_count); 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); uint8_t player_score = handCount(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)
queue(game_state, win, NULL, to_win_state); queue(game_state, win, NULL, to_win_state, game_state->settings.message_duration, 0);
else if (dealer_score > player_score) else if (dealer_score > player_score)
queue(game_state, lose, NULL, to_lose_state); queue(game_state, lose, NULL, to_lose_state, game_state->settings.message_duration, 0);
else if (dealer_score == player_score) else if (dealer_score == player_score)
queue(game_state, draw, NULL, to_draw_state); queue(game_state, draw, NULL, to_draw_state, game_state->settings.message_duration, 0);
} else { } else {
queue(game_state, drawDealerCard, NULL, draw_card_animation); queue(game_state, drawDealerCard, NULL, dealer_card_animation, game_state->settings.animation_duration,
game_state->settings.animation_margin);
} }
} }
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) {
game_state->selectedMenu--;
}
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;
}
}
}
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);
if (!game_state->started && game_state->state == GameStatePlay) { switch (game_state->state) {
game_state->started = true; case GameStateGameOver:
drawDealerCard(game_state); case GameStateStart:
queue(game_state, drawPlayerCard, NULL, draw_card_animation); if (game_state->selectDirection == Select)
queue(game_state, drawDealerCard, NULL, draw_card_animation); init(game_state);
queue(game_state, drawPlayerCard, NULL, draw_card_animation); else if (game_state->selectDirection == DirectionRight) {
} game_state->selectedMenu = 0;
game_state->state = GameStateSettings;
if (!run_queue(game_state)) { }
if (game_state->state == GameStatePlay) { break;
player_tick(game_state); case GameStatePlay:
} else if (game_state->state == GameStateDealer) { if (!game_state->started) {
dealer_tick(game_state); 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;
} }
game_state->selectDirection = None; game_state->selectDirection = None;
@@ -250,18 +414,20 @@ void start_round(GameState *game_state) {
game_state->animationStart = 0; game_state->animationStart = 0;
shuffleDeck(&(game_state->deck)); shuffleDeck(&(game_state->deck));
game_state->doubled = false; game_state->doubled = false;
game_state->bet = ROUND_PRICE; game_state->bet = game_state->settings.round_price;
if (game_state->player_score < 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 -= ROUND_PRICE; game_state->player_score -= game_state->settings.round_price;
} }
game_state->state = GameStatePlay; game_state->state = GameStatePlay;
} }
void init(GameState *game_state) { void init(GameState *game_state) {
game_state->settings = load_settings();
game_state->last_tick = 0; game_state->last_tick = 0;
game_state->player_score = STARTING_MONEY; game_state->processing = true;
game_state->player_score = game_state->settings.starting_money;
generateDeck(&(game_state->deck)); generateDeck(&(game_state->deck));
start_round(game_state); start_round(game_state);
} }
@@ -312,13 +478,11 @@ int32_t blackjack_app(void *p) {
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 *game_state = (GameState *) acquire_mutex_block(&state_mutex); GameState *game_state = (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:
game_state->selectDirection = DirectionUp; game_state->selectDirection = DirectionUp;
break; break;
@@ -332,20 +496,20 @@ int32_t blackjack_app(void *p) {
game_state->selectDirection = DirectionLeft; game_state->selectDirection = DirectionLeft;
break; break;
case InputKeyBack: case InputKeyBack:
processing = false; if (game_state->state == GameStateSettings) {
game_state->state = GameStateStart;
save_settings(game_state->settings);
} else
processing = false;
break; break;
case InputKeyOk: case InputKeyOk:
if (game_state->state == GameStateGameOver || game_state->state == GameStateStart) { game_state->selectDirection = Select;
init(game_state);
} else {
game_state->selectDirection = Select;
}
break; break;
} }
} }
} else if (event.type == EventTypeTick) { } else if (event.type == EventTypeTick) {
tick(game_state); tick(game_state);
processing = game_state->processing;
} }
} else { } else {
FURI_LOG_D(APP_NAME, "osMessageQueue: event timeout"); FURI_LOG_D(APP_NAME, "osMessageQueue: event timeout");

View File

@@ -1,5 +1,6 @@
#include "card.h"
#include <math.h> #include <math.h>
#include "card.h"
#include "util.h"
//region CardDesign //region CardDesign
bool pips[4][49] = bool pips[4][49] =
@@ -57,7 +58,9 @@ uint8_t characters[13] =
2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'A' 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 //region Player card positions
uint8_t playerCardPositions[22][4] = { uint8_t playerCardPositions[22][4] = {
//first row //first row
@@ -102,38 +105,52 @@ void drawPlayerDeck(const Card cards[21], uint8_t count, Canvas *const canvas) {
} }
} }
void drawCardAt(uint8_t pos_x, uint8_t pos_y, uint8_t pip, uint8_t character, CardState state, Canvas *const canvas) { 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) { if (state == Normal) {
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDHT, CARD_HEIGHT); canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDHT, CARD_HEIGHT); canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT);
} else { } else {
if (state == BottomCut || state == BottomAndRightCut) if (state == BottomCut || state == BottomAndRightCut)
canvas_draw_line(canvas, pos_x, pos_y, pos_x, pos_y + CARD_HALF_HEIGHT - 1); //half height line canvas_draw_line(canvas, pos_x, pos_y, pos_x, pos_y + CARD_HALF_HEIGHT - 1); //half height line
if (state == BottomCut) { if (state == BottomCut) {
canvas_draw_line(canvas, pos_x, pos_y, pos_x + CARD_WIDHT - 1, pos_y); //full width line canvas_draw_line(canvas, pos_x, pos_y, pos_x + CARD_WIDTH - 1, pos_y); //full width line
canvas_draw_line(canvas, pos_x + CARD_WIDHT - 1, pos_y, pos_x + CARD_WIDHT - 1, 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 pos_y + CARD_HALF_HEIGHT - 1); //half height line
} }
if (state == BottomAndRightCut) { if (state == BottomAndRightCut) {
canvas_draw_line(canvas, pos_x, pos_y, pos_x + CARD_HALF_WIDHT - 1, pos_y); //half width canvas_draw_line(canvas, pos_x, pos_y, pos_x + CARD_HALF_WIDTH - 1, pos_y); //half width
} }
if (state == RightCut) { if (state == RightCut) {
canvas_draw_line(canvas, pos_x, pos_y, pos_x + CARD_HALF_WIDHT - 1, pos_y); //half width canvas_draw_line(canvas, pos_x, pos_y, pos_x + 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, pos_x, pos_y + CARD_HEIGHT - 1); //full height line
canvas_draw_line(canvas, pos_x, pos_y + CARD_HEIGHT - 1, pos_x + CARD_HALF_WIDHT - 1, 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 pos_y + CARD_HEIGHT - 1); //full height line
} }
} }
uint8_t left = pos_x + CORNER_MARGIN; uint8_t left = pos_x + CORNER_MARGIN;
uint8_t right = (pos_x + CARD_WIDHT - CORNER_MARGIN - 7); uint8_t right = (pos_x + CARD_WIDTH - CORNER_MARGIN - 7);
uint8_t top = pos_y + CORNER_MARGIN; uint8_t top = pos_y + CORNER_MARGIN;
uint8_t bottom = (pos_y + CARD_HEIGHT - CORNER_MARGIN - 7); uint8_t bottom = (pos_y + CARD_HEIGHT - CORNER_MARGIN - 7);
@@ -171,13 +188,13 @@ void drawCardAt(uint8_t pos_x, uint8_t pos_y, uint8_t pip, uint8_t character, Ca
//canvas_draw_str(canvas, left, top, drawChar ); //canvas_draw_str(canvas, left, top, drawChar );
} }
void drawCardBackAt(uint8_t pos_x, uint8_t pos_y, Canvas *const canvas) { void drawCardBackAt(int8_t pos_x, int8_t pos_y, Canvas *const canvas) {
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDHT, CARD_HEIGHT); canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDHT, CARD_HEIGHT); canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT);
for (uint8_t x = 0; x < CARD_WIDHT - 2; x++) { for (uint8_t x = 0; x < CARD_WIDTH - 2; x++) {
for (uint8_t y = 0; y < CARD_HEIGHT - 2; y++) { for (uint8_t y = 0; y < CARD_HEIGHT - 2; y++) {
uint8_t _x = x; uint8_t _x = x;
uint8_t _y = y * 2; uint8_t _y = y * 2;
@@ -237,3 +254,23 @@ uint8_t handCount(const Card cards[21], uint8_t count) {
return 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);
}
}

View File

@@ -6,10 +6,11 @@
#define DECK_COUNT 6 #define DECK_COUNT 6
#define CARD_HEIGHT 24 #define CARD_HEIGHT 24
#define CARD_HALF_HEIGHT CARD_HEIGHT/2 #define CARD_HALF_HEIGHT CARD_HEIGHT/2
#define CARD_WIDHT 18 #define CARD_WIDTH 18
#define CARD_HALF_WIDHT CARD_WIDHT/2 #define CARD_HALF_WIDTH CARD_WIDTH/2
#define CORNER_MARGIN 3 #define CORNER_MARGIN 3
#define LEGEND_SIZE 10 #define LEGEND_SIZE 10
typedef struct Vector Vector;
typedef enum { typedef enum {
Normal, BottomCut, RightCut, BottomAndRightCut, TopCut, LeftCut, TopAndLeftCut Normal, BottomCut, RightCut, BottomAndRightCut, TopCut, LeftCut, TopAndLeftCut
@@ -27,14 +28,18 @@ typedef struct {
void drawPlayerDeck(const Card cards[21], uint8_t count, Canvas *const canvas); void drawPlayerDeck(const Card cards[21], uint8_t count, Canvas *const canvas);
void drawCardAt(uint8_t pos_x, uint8_t pos_y, uint8_t pip, uint8_t character, CardState state, Canvas *const canvas); void drawCardAt(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, CardState state, Canvas *const canvas);
void drawCardBackAt(uint8_t pos_x, uint8_t pos_y, Canvas *const canvas); void drawCardBackAt(int8_t pos_x, int8_t pos_y, Canvas *const canvas);
void generateDeck(Deck *deck_ptr); void generateDeck(Deck *deck_ptr);
void shuffleDeck(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); uint8_t handCount(const Card cards[21], uint8_t count);
#endif #endif

View File

@@ -3,17 +3,35 @@
#include <furi.h> #include <furi.h>
#include <input/input.h> #include <input/input.h>
#include <gui/elements.h> #include <gui/elements.h>
#include <flipper_format/flipper_format.h>
#include <flipper_format/flipper_format_i.h>
#include "card.h" #include "card.h"
#define ANIMATION_TIME furi_ms_to_ticks(1500) #define APP_NAME "Blackjack"
#define ANIMATION_END_MARGIN furi_ms_to_ticks(200) //#define ANIMATION_TIME furi_ms_to_ticks(1500)
#define ROUND_PRICE 10 //#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"
#define CONF_SOUND_EFFECTS "SoundEffects"
typedef enum { typedef enum {
EventTypeTick, EventTypeTick,
EventTypeKey, EventTypeKey,
} EventType; } EventType;
typedef struct{
uint32_t animation_margin;
uint32_t animation_duration;
uint32_t message_duration;
uint32_t starting_money;
uint32_t round_price;
bool sound_effects;
} Settings;
typedef struct { typedef struct {
EventType type; EventType type;
InputEvent input; InputEvent input;
@@ -23,6 +41,7 @@ typedef enum {
GameStateGameOver, GameStateGameOver,
GameStateStart, GameStateStart,
GameStatePlay, GameStatePlay,
GameStateSettings,
GameStateDealer, GameStateDealer,
} PlayState; } PlayState;
@@ -42,20 +61,18 @@ typedef struct {
uint8_t dealer_card_count; uint8_t dealer_card_count;
Direction selectDirection; Direction selectDirection;
Settings settings;
uint32_t player_score; uint32_t player_score;
uint8_t multiplier;
uint32_t bet; uint32_t bet;
uint8_t player_time;
bool doubled; bool doubled;
bool animating; bool animating;
bool started; bool started;
uint8_t selectedMenu; uint8_t selectedMenu;
bool processing;
Deck deck; Deck deck;
PlayState state; PlayState state;
unsigned int last_tick; unsigned int last_tick;
unsigned int animationStart; unsigned int animationStart;
bool dealer_animating;
unsigned int delay_tick;
} GameState; } GameState;

View File

@@ -1,8 +1,13 @@
#include <math.h>
#include <notification/notification_messages.h>
#include "ui.h" #include "ui.h"
#include "card.h" #include "card.h"
#include <math.h>
#include "util.h" #include "util.h"
#define LINE_HEIGHT 16
#define ITEM_PADDING 4
const char MoneyMul[4] = { const char MoneyMul[4] = {
'K', 'B', 'T', 'S' 'K', 'B', 'T', 'S'
}; };
@@ -13,7 +18,8 @@ void draw_player_scene(Canvas *const canvas, const GameState *game_state) {
if (max_card > 0) if (max_card > 0)
drawPlayerDeck((game_state->player_cards), max_card, canvas); drawPlayerDeck((game_state->player_cards), max_card, canvas);
drawCardBackAt(13, 5, canvas); if (game_state->dealer_card_count > 0)
drawCardBackAt(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) {
@@ -27,23 +33,7 @@ void draw_dealer_scene(Canvas *const canvas, const GameState *game_state) {
drawPlayerDeck((game_state->dealer_cards), max_card, canvas); drawPlayerDeck((game_state->dealer_cards), max_card, canvas);
} }
void draw_card_animation(const GameState *game_state, Canvas *const canvas) { void popup_frame(Canvas *const canvas) {
float t = (float) (furi_get_tick() - game_state->animationStart) / (ANIMATION_TIME - ANIMATION_END_MARGIN);
t *= 2;
Card animatingCard = game_state->deck.cards[game_state->deck.index];
if (t > 1) {
int cardY = round(lerp(-CARD_HEIGHT, 10, 1));
drawCardAt(64 - CARD_HALF_WIDHT, cardY, animatingCard.pip,
animatingCard.character, Normal, canvas);
} else {
int cardY = round(lerp(-CARD_HEIGHT, 10, t));
drawCardAt(64 - CARD_HALF_WIDHT, cardY, animatingCard.pip,
animatingCard.character, Normal, canvas);
// drawCardBackAt(64 - CARD_HALF_WIDHT, cardY, canvas);
}
}
void popupFrame(Canvas *const canvas) {
canvas_set_color(canvas, ColorWhite); canvas_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);
@@ -51,30 +41,11 @@ void popupFrame(Canvas *const canvas) {
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
} }
void draw_message_scene(Canvas *const canvas, const GameState *game_state) {
switch (game_state->state) {
case GameStateStart:
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 64, 5, AlignCenter, AlignTop, "Blackjack");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 64, 24, AlignCenter, AlignTop, "Made by Doofy");
elements_multiline_text_aligned(canvas, 64, 38, AlignCenter, AlignTop, "Press center button\nto start");
break;
case GameStateGameOver:
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 64, 5, AlignCenter, AlignTop, "Game Over");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 64, 24, AlignCenter, AlignTop, "Press center button\nto start");
break;
default:
break;
}
}
void draw_play_menu(Canvas *const canvas, const GameState *game_state) { 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 && (game_state->doubled || game_state->player_score < ROUND_PRICE)) continue; if (m == 0 && (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);
@@ -96,6 +67,15 @@ 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_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);
@@ -125,3 +105,84 @@ void draw_money(Canvas *const canvas, uint32_t score) {
} }
canvas_draw_str_aligned(canvas, 126, 2, AlignRight, AlignTop, drawChar); canvas_draw_str_aligned(canvas, 126, 2, AlignRight, AlignTop, drawChar);
} }
void draw_menu(Canvas *const canvas, const char *text, const char *value, int8_t y, bool left_caret, bool right_caret,
bool selected) {
UNUSED(selected);
if (y < 0 || y >= 64) return;
if (selected) {
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, "<");
canvas_draw_str_aligned(canvas, 100, y + ITEM_PADDING, AlignCenter, AlignTop, value);
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) {
char drawChar[10];
int startY = 0;
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;
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
);
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
);
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
);
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
// );
}

View File

@@ -1,20 +1,19 @@
#pragma once #pragma once
#include "defines.h" #include "defines.h"
#include "util.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_message_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 draw_card_animation(const GameState *game_state, Canvas *const canvas); void popup_frame(Canvas *const canvas);
void draw_screen(Canvas *const canvas, const bool* points);
void popupFrame(Canvas *const canvas);

View File

@@ -1,61 +1,85 @@
#include "util.h" #include "util.h"
static List *afterDelay; const char *CONFIG_FILE_PATH = EXT_PATH(".blackjack.settings");
static QueueItem *afterDelay;
float lerp(float v0, float v1, float t) { float lerp(float v0, float v1, float t) {
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) {
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 queue(GameState *game_state,
void (*callback)(GameState *game_state), void (*callback)(GameState *game_state),
void (*start)(GameState *game_state), void (*start)(GameState *game_state),
void (*processing)(const GameState *gameState, Canvas *const canvas) void (*processing)(const GameState *gameState, Canvas *const canvas,uint32_t duration,uint32_t margin),
uint32_t duration,uint32_t margin
) { ) {
if (afterDelay == NULL) { if (afterDelay == NULL) {
game_state->animationStart = game_state->last_tick; game_state->animationStart = game_state->last_tick;
afterDelay = malloc(sizeof(List)); afterDelay = malloc(sizeof(QueueItem));
afterDelay->callback = callback; afterDelay->callback = callback;
afterDelay->processing = processing; afterDelay->processing = processing;
afterDelay->start = start; afterDelay->start = start;
afterDelay->duration = duration;
afterDelay->margin = margin;
afterDelay->next = NULL; afterDelay->next = NULL;
} else { } else {
List *next = afterDelay; QueueItem *next = afterDelay;
while (next->next != NULL) { next = (List *) (next->next); } while (next->next != NULL) { next = (QueueItem *) (next->next); }
next->next = malloc(sizeof(List)); next->next = malloc(sizeof(QueueItem));
((List *) next->next)->callback = callback; ((QueueItem *) next->next)->callback = callback;
((List *) next->next)->processing = processing; ((QueueItem *) next->next)->processing = processing;
((List *) next->next)->start = start; ((QueueItem *) next->next)->start = start;
((List *) next->next)->next = NULL; ((QueueItem *) next->next)->duration = duration;
((QueueItem *) next->next)->margin = margin;
((QueueItem *) next->next)->next = NULL;
} }
} }
void queue_clear() { void queue_clear() {
while (afterDelay != NULL) { while (afterDelay != NULL) {
List *f = afterDelay; QueueItem *f = afterDelay;
free(f); free(f);
afterDelay = f->next; afterDelay = f->next;
} }
} }
void dequeue(GameState *game_state) { void dequeue(GameState *game_state) {
((List *) afterDelay)->callback(game_state); ((QueueItem *) afterDelay)->callback(game_state);
List *f = afterDelay; QueueItem *f = afterDelay;
afterDelay = (List *) afterDelay->next; afterDelay = (QueueItem *) afterDelay->next;
free(f); free(f);
if (afterDelay != NULL && afterDelay->start != NULL)afterDelay->start(game_state); if (afterDelay != NULL && afterDelay->start != NULL)afterDelay->start(game_state);
game_state->animationStart = game_state->last_tick; game_state->animationStart = game_state->last_tick;
} }
void animateQueue(const GameState *game_state, Canvas *const canvas) { void animateQueue(const GameState *game_state, Canvas *const canvas) {
if (afterDelay != NULL && ((List *) afterDelay)->processing != NULL) { if (afterDelay != NULL && ((QueueItem *) afterDelay)->processing != NULL) {
((List *) afterDelay)->processing(game_state, canvas); ((QueueItem *) afterDelay)->processing(game_state, canvas, afterDelay->duration, afterDelay->margin);
} }
} }
bool run_queue(GameState *game_state) { bool run_queue(GameState *game_state) {
if (afterDelay != NULL) { if (afterDelay != NULL) {
game_state->animating = true; game_state->animating = true;
if ((game_state->last_tick - game_state->animationStart) > ANIMATION_TIME) { if ((game_state->last_tick - game_state->animationStart) > afterDelay->duration) {
dequeue(game_state); dequeue(game_state);
} }
return true; return true;
@@ -63,3 +87,132 @@ bool run_queue(GameState *game_state) {
game_state->animating = false; game_state->animating = false;
return false; return false;
} }
void save_settings(Settings settings) {
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);
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);
FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_STARTING_MONEY, settings.starting_money);
flipper_format_update_uint32(file, CONF_STARTING_MONEY, &(settings.starting_money), 1);
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);
flipper_format_update_bool(file, CONF_SOUND_EFFECTS, &(settings.sound_effects), 1);
FURI_LOG_D(APP_NAME, "Config saved");
}else{
FURI_LOG_E(APP_NAME, "Save error");
}
flipper_format_file_close(file);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
}
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");
flipper_format_write_uint32(file, CONF_STARTING_MONEY, &(settings->starting_money), 1);
flipper_format_write_comment_cstr(file, "Round price");
flipper_format_write_uint32(file, CONF_ROUND_PRICE, &(settings->round_price), 1);
flipper_format_write_comment_cstr(file, "Enable sound effects");
flipper_format_write_bool(file, CONF_SOUND_EFFECTS, &(settings->sound_effects), 1);
}
Settings load_settings() {
Settings settings;
FURI_LOG_D(APP_NAME, "Loading default settings");
settings.animation_margin = 200;
settings.animation_duration = 1500;
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);
FURI_LOG_D(APP_NAME, "Allocating file");
FlipperFormat *file = flipper_format_file_alloc(storage);
FURI_LOG_D(APP_NAME, "Allocating string");
FuriString *string_value;
string_value = furi_string_alloc();
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)) {
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)) {
FURI_LOG_E(APP_NAME, "Error opening existing file %s", CONFIG_FILE_PATH);
flipper_format_file_close(file);
} else {
uint32_t value;
bool valueBool;
FURI_LOG_D(APP_NAME, "Checking version");
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)) {
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)) {
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)) {
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)) {
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)) {
settings.sound_effects = valueBool;
FURI_LOG_D(APP_NAME, "Loaded %s: %i", CONF_ROUND_PRICE, valueBool?1:0);
}
}
flipper_format_file_close(file);
}
}
furi_string_free(string_value);
// flipper_format_file_close(file);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return settings;
}

View File

@@ -1,18 +1,34 @@
#pragma once #pragma once
#include "defines.h" #include "defines.h"
#define CONFIG_FILE_HEADER "Blackjack config file"
#define CONFIG_FILE_VERSION 1
typedef struct{ typedef struct{
void (*callback)(GameState *game_state); void (*callback)(GameState *game_state);
void (*processing)(const GameState *game_state, Canvas *const canvas); void (*processing)(const GameState *game_state, Canvas *const canvas,uint32_t duration,uint32_t margin);
void (*start)(GameState *game_state); void (*start)(GameState *game_state);
void *next; void *next;
} List; uint32_t duration;
uint32_t margin;
} QueueItem;
struct Vector{
float x;
float y;
};
float lerp(float v0, float v1, float t); float lerp(float v0, float v1, float t);
void queue(GameState *game_state, void queue(GameState *game_state,
void (*callback)(GameState *game_state), void (*callback)(GameState *game_state),
void (*start)(GameState *game_state), void (*start)(GameState *game_state),
void (*processing)(const GameState *gameState, Canvas *const canvas)); 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); bool run_queue(GameState *gameState);
void animateQueue(const GameState *gameState, Canvas *const canvas); void animateQueue(const GameState *gameState, Canvas *const canvas);
void queue_clear(); 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();