Solitaire

This commit is contained in:
RogueMaster
2022-11-17 23:39:43 -05:00
parent 7c09807aeb
commit 4b81d2f24d
16 changed files with 1868 additions and 0 deletions
@@ -0,0 +1,13 @@
App(
appid="solitaire",
name="Solitaire",
apptype=FlipperAppType.EXTERNAL,
entry_point="solitaire_app",
cdefines=["APP_SOLITAIRE"],
requires=["gui","storage","canvas"],
stack_size=2 * 1024,
order=30,
fap_icon="solitaire_10px.png",
fap_category="Games",
fap_icon_assets="assets"
)
Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,330 @@
#include "card.h"
#include "dml.h"
#include "ui.h"
#define CARD_DRAW_X_START 108
#define CARD_DRAW_Y_START 38
#define CARD_DRAW_X_SPACE 10
#define CARD_DRAW_Y_SPACE 8
#define CARD_DRAW_X_OFFSET 4
#define CARD_DRAW_FIRST_ROW_LENGTH 7
uint8_t pips[4][3] = {
{21, 10, 7}, //spades
{7, 10, 7}, //hearts
{0, 10, 7}, //diamonds
{14, 10, 7}, //clubs
};
uint8_t letters[13][3] = {
{0, 0, 5},
{5, 0, 5},
{10, 0, 5},
{15, 0, 5},
{20, 0, 5},
{25, 0, 5},
{30, 0, 5},
{0, 5, 5},
{5, 5, 5},
{10, 5, 5},
{15, 5, 5},
{20, 5, 5},
{25, 5, 5},
};
//region Player card positions
uint8_t playerCardPositions[22][4] = {
//first row
{108, 38},
{98, 38},
{88, 38},
{78, 38},
{68, 38},
{58, 38},
{48, 38},
{38, 38},
//second row
{104, 26},
{94, 26},
{84, 26},
{74, 26},
{64, 26},
{54, 26},
{44, 26},
//third row
{99, 14},
{89, 14},
{79, 14},
{69, 14},
{59, 14},
{49, 14},
};
//endregion
Icon *card_graphics = NULL;
void set_card_graphics(const Icon *graphics) {
card_graphics = (Icon *) graphics;
}
void
draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, bool inverted, Canvas *const canvas) {
DrawMode primary = inverted ? Black : White;
DrawMode secondary = inverted ? White : Black;
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, primary);
draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
uint8_t *drawInfo = pips[pip];
uint8_t px = drawInfo[0], py = drawInfo[1], s = drawInfo[2];
uint8_t left = pos_x + 2;
uint8_t right = (pos_x + CARD_WIDTH - s - 2);
uint8_t top = pos_y + 2;
uint8_t bottom = (pos_y + CARD_HEIGHT - s - 2);
draw_icon_clip(canvas, card_graphics, right, top, px, py, s, s,
secondary);
draw_icon_clip_flipped(canvas, card_graphics, left, bottom, px, py, s, s,
secondary);
drawInfo = letters[character];
px = drawInfo[0], py = drawInfo[1], s = drawInfo[2];
left = pos_x + 2;
right = (pos_x + CARD_WIDTH - s - 2);
top = pos_y + 2;
bottom = (pos_y + CARD_HEIGHT - s - 2);
draw_icon_clip(canvas, card_graphics, left, top + 1, px, py, s, s,
secondary);
draw_icon_clip_flipped(canvas, card_graphics, right, bottom - 1, px, py, s, s,
secondary);
}
void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas *const canvas) {
draw_card_at_colored(pos_x, pos_y, pip, character, false, canvas);
}
void draw_deck(const Card *cards, uint8_t count, Canvas *const canvas) {
for (int i = count - 1; i >= 0; i--) {
draw_card_at(playerCardPositions[i][0], playerCardPositions[i][1], cards[i].pip, cards[i].character, canvas);
}
}
Vector card_pos_at_index(uint8_t index) {
return (Vector) {
playerCardPositions[index][0],
playerCardPositions[index][1]
};
}
void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas *const canvas) {
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, White);
draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
draw_icon_clip(canvas, card_graphics, pos_x + 1, pos_y + 1, 35, 0, 15, 21, Black);
}
void generate_deck(Deck *deck_ptr, uint8_t deck_count) {
uint16_t counter = 0;
if (deck_ptr->cards == NULL) {
deck_ptr->deck_count = deck_count;
deck_ptr->card_count = deck_count * 52;
deck_ptr->cards = malloc(sizeof(Card) * deck_ptr->card_count);
}
for (uint8_t deck = 0; deck < deck_count; deck++) {
for (uint8_t pip = 0; pip < 4; pip++) {
for (uint8_t label = 0; label < 13; label++) {
deck_ptr->cards[counter] = (Card)
{
pip, label, false, false
};
counter++;
}
}
}
}
void shuffle_deck(Deck *deck_ptr) {
srand(DWT->CYCCNT);
deck_ptr->index = 0;
int max = deck_ptr->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 hand_count(const Card *cards, uint8_t count) {
uint8_t aceCount = 0;
uint8_t score = 0;
for (uint8_t i = 0; i < count; i++) {
if (cards[i].character == 12)
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) {
draw_card_at(currentPos.x, currentPos.y, animatingCard.pip,
animatingCard.character, canvas);
} else {
if (t < 0.5)
draw_card_back_at(currentPos.x, currentPos.y, canvas);
else
draw_card_at(currentPos.x, currentPos.y, animatingCard.pip,
animatingCard.character, canvas);
}
}
void init_hand(Hand *hand_ptr, uint8_t count) {
hand_ptr->cards = malloc(sizeof(Card) * count);
hand_ptr->index = 0;
hand_ptr->max = count;
}
void free_hand(Hand *hand_ptr) {
FURI_LOG_D("CARD", "Freeing hand");
free(hand_ptr->cards);
}
void add_to_hand(Hand *hand_ptr, Card card) {
FURI_LOG_D("CARD", "Adding to hand");
if (hand_ptr->index < hand_ptr->max) {
hand_ptr->cards[hand_ptr->index] = card;
hand_ptr->index++;
}
}
void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas *const canvas) {
if (highlighted) {
draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
draw_rounded_box_frame(canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White);
} else {
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black);
draw_rounded_box_frame(canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White);
}
}
int first_non_flipped_card(Hand hand) {
for (int i = 0; i < hand.index; i++) {
if (!hand.cards[i].flipped) {
return i;
}
}
return hand.index;
}
void draw_hand_column(Hand hand, int16_t pos_x, int16_t pos_y, int8_t highlight, Canvas *const canvas) {
if (hand.index == 0) {
draw_card_space(pos_x, pos_y, highlight > 0, canvas);
if(highlight==0)
draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT,
Inverse);
return;
}
int loopEnd = hand.index;
int hStart = max(loopEnd-4, 0);
int pos = 0;
int first= first_non_flipped_card(hand);
bool wastop=false;
if(first>=0 && first<=hStart && highlight!=first){
if(first>0){
draw_card_back_at(pos_x, pos_y + pos, canvas);
pos+=4;
hStart++;
wastop=true;
}
draw_card_at_colored(pos_x, pos_y + pos, hand.cards[first].pip, hand.cards[first].character,
false,
canvas);
pos+=8;
hStart++;
}
if(hStart>highlight && highlight>=0){
if(!wastop && first>0){
draw_card_back_at(pos_x, pos_y + pos, canvas);
pos+=4;
hStart++;
}
draw_card_at_colored(pos_x, pos_y + pos, hand.cards[highlight].pip, hand.cards[highlight].character,
true,
canvas);
pos+=8;
hStart++;
}
for (int i = hStart; i < loopEnd; i++, pos+=4) {
if (hand.cards[i].flipped) {
draw_card_back_at(pos_x, pos_y + pos, canvas);
if(i==highlight)
draw_rounded_box(canvas, pos_x+1, pos_y + pos+1, CARD_WIDTH - 2, CARD_HEIGHT - 2,
Inverse);
} else {
draw_card_at_colored(pos_x, pos_y + pos, hand.cards[i].pip, hand.cards[i].character,
(i == highlight),
canvas);
if(i == highlight || i==first) pos+=4;
}
}
}
Card remove_from_deck(uint16_t index, Deck *deck) {
FURI_LOG_D("CARD", "Removing from deck");
Card result = {0, 0, true, false};
if (deck->card_count > 0) {
deck->card_count--;
for (int i = 0, curr_index = 0; i <= deck->card_count; i++) {
if (i != index) {
deck->cards[curr_index] = deck->cards[i];
curr_index++;
} else {
result = deck->cards[i];
}
}
if (deck->index >= 0) {
deck->index--;
}
}
return result;
}
void extract_hand_region(Hand *hand, Hand *to, uint8_t start_index) {
FURI_LOG_D("CARD", "Extracting hand region");
if (start_index >= hand->index) return;
for (uint8_t i = start_index; i < hand->index; i++) {
add_to_hand(to, hand->cards[i]);
}
hand->index = start_index;
}
void add_hand_region(Hand *to, Hand *from) {
FURI_LOG_D("CARD", "Adding hand region");
if ((to->index + from->index) <= to->max) {
for (int i = 0; i < from->index; i++) {
add_to_hand(to, from->cards[i]);
}
}
}
@@ -0,0 +1,177 @@
#pragma once
#include <gui/gui.h>
#include <math.h>
#include <stdlib.h>
#include "dml.h"
#define CARD_HEIGHT 23
#define CARD_HALF_HEIGHT 11
#define CARD_WIDTH 17
#define CARD_HALF_WIDTH 8
//region types
typedef struct {
uint8_t pip; //Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs
uint8_t character; //Card letter [0-12], 0 means 2, 12 is Ace
bool disabled;
bool flipped;
} Card;
typedef struct {
uint8_t deck_count; //Number of decks used
Card *cards; //Cards in the deck
int card_count;
int index; //Card index (to know where we at in the deck)
} Deck;
typedef struct {
Card *cards; //Cards in the deck
uint8_t index; //Current index
uint8_t max; //How many cards we want to store
} Hand;
//endregion
void set_card_graphics(const Icon *graphics);
/**
* Gets card coordinates at the index (range: 0-20).
*
* @param index Index to check 0-20
* @return Position of the card
*/
Vector card_pos_at_index(uint8_t index);
/**
* Draws card at a given coordinate (top-left corner)
*
* @param pos_x X position
* @param pos_y Y position
* @param pip Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs
* @param character Letter [0-12] 0 is 2, 12 is A
* @param canvas Pointer to Flipper's canvas object
*/
void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas *const canvas);
/**
* Draws card at a given coordinate (top-left corner)
*
* @param pos_x X position
* @param pos_y Y position
* @param pip Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs
* @param character Letter [0-12] 0 is 2, 12 is A
* @param inverted Invert colors
* @param canvas Pointer to Flipper's canvas object
*/
void
draw_card_at_colored(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, bool inverted, Canvas *const canvas);
/**
* Draws 'count' cards at the bottom right corner
*
* @param cards List of cards
* @param count Count of cards
* @param canvas Pointer to Flipper's canvas object
*/
void draw_deck(const Card *cards, uint8_t count, Canvas *const canvas);
/**
* Draws card back at a given coordinate (top-left corner)
*
* @param pos_x X coordinate
* @param pos_y Y coordinate
* @param canvas Pointer to Flipper's canvas object
*/
void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas *const canvas);
/**
* Generates the deck
*
* @param deck_ptr Pointer to the deck
* @param deck_count Number of decks
*/
void generate_deck(Deck *deck_ptr, uint8_t deck_count);
/**
* Shuffles the deck
*
* @param deck_ptr Pointer to the deck
*/
void shuffle_deck(Deck *deck_ptr);
/**
* Calculates the hand count for blackjack
*
* @param cards List of cards
* @param count Count of cards
* @return Hand value
*/
uint8_t hand_count(const Card *cards, uint8_t count);
/**
* Draws card animation
*
* @param animatingCard Card to animate
* @param from Starting position
* @param control Quadratic lerp control point
* @param to End point
* @param t Current time (0-1)
* @param extra_margin Use extra margin at the end (arrives 0.2 unit before the end so it can stay there a bit)
* @param canvas Pointer to Flipper's canvas object
*/
void draw_card_animation(Card animatingCard, Vector from, Vector control, Vector to, float t, bool extra_margin,
Canvas *const canvas);
/**
* Init hand pointer
* @param hand_ptr Pointer to hand
* @param count Number of cards we want to store
*/
void init_hand(Hand *hand_ptr, uint8_t count);
/**
* Free hand resources
* @param hand_ptr Pointer to hand
*/
void free_hand(Hand *hand_ptr);
/**
* Add card to hand
* @param hand_ptr Pointer to hand
* @param card Card to add
*/
void add_to_hand(Hand *hand_ptr, Card card);
/**
* Draw card placement position at coordinate
* @param pos_x X coordinate
* @param pos_y Y coordinate
* @param highlighted Apply highlight effect
* @param canvas Canvas object
*/
void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas *const canvas);
/**
* Draws a column of card, displaying the last [max_cards] cards on the list
* @param hand Hand object
* @param pos_x X coordinate to draw
* @param pos_y Y coordinate to draw
* @param highlight Index to highlight, negative means no highlight
* @param canvas Canvas object
*/
void
draw_hand_column(Hand hand, int16_t pos_x, int16_t pos_y, int8_t highlight, Canvas *const canvas);
/**
* Removes a card from the deck (Be aware, if you remove the first item, the deck index will be at -1 so you have to handle that)
* @param index Index to remove
* @param deck Deck reference
* @return The removed card
*/
Card remove_from_deck(uint16_t index, Deck *deck);
int first_non_flipped_card(Hand hand);
void extract_hand_region(Hand *hand, Hand *to, uint8_t start_index);
void add_hand_region(Hand *to, Hand *from);
@@ -0,0 +1,60 @@
#include "dml.h"
#include <math.h>
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
);
}
Vector vector_add(Vector a, Vector b) {
return (Vector) {a.x + b.x, a.y + b.y};
}
Vector vector_sub(Vector a, Vector b) {
return (Vector) {a.x - b.x, a.y - b.y};
}
Vector vector_mul_components(Vector a, Vector b) {
return (Vector) {a.x * b.x, a.y * b.y};
}
Vector vector_div_components(Vector a, Vector b) {
return (Vector) {a.x / b.x, a.y / b.y};
}
Vector vector_normalized(Vector a) {
float length = vector_magnitude(a);
return (Vector) {
a.x / length,
a.y / length
};
}
float vector_magnitude(Vector a) {
return sqrt(a.x * a.x + a.y * a.y);
}
float vector_distance(Vector a, Vector b) {
return vector_magnitude(vector_sub(a, b));
}
float vector_dot(Vector a, Vector b) {
Vector _a = vector_normalized(a);
Vector _b = vector_normalized(b);
return _a.x * _b.x + _a.y * _b.y;
}
+116
View File
@@ -0,0 +1,116 @@
//
// Doofy's Math library
//
#pragma once
typedef struct {
float x;
float y;
} Vector;
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
/**
* Lerp function
*
* @param v0 Start value
* @param v1 End value
* @param t Time (0-1 range)
* @return Point between v0-v1 at a given time
*/
float lerp(float v0, float v1, float t);
/**
* 2D lerp function
*
* @param start Start vector
* @param end End vector
* @param t Time (0-1 range)
* @return 2d Vector between start and end at time
*/
Vector lerp_2d(Vector start, Vector end, float t);
/**
* Quadratic lerp function
*
* @param start Start vector
* @param control Control point
* @param end End vector
* @param t Time (0-1 range)
* @return 2d Vector at time
*/
Vector quadratic_2d(Vector start, Vector control, Vector end, float t);
/**
* Add vector components together
*
* @param a First vector
* @param b Second vector
* @return Resulting vector
*/
Vector vector_add(Vector a, Vector b);
/**
* Subtract vector components together
*
* @param a First vector
* @param b Second vector
* @return Resulting vector
*/
Vector vector_sub(Vector a, Vector b);
/**
* Multiplying vector components together
*
* @param a First vector
* @param b Second vector
* @return Resulting vector
*/
Vector vector_mul_components(Vector a, Vector b);
/**
* Dividing vector components
*
* @param a First vector
* @param b Second vector
* @return Resulting vector
*/
Vector vector_div_components(Vector a, Vector b);
/**
* Calculating Vector length
*
* @param a Direction vector
* @return Length of the vector
*/
float vector_magnitude(Vector a);
/**
* Get a normalized vector (length of 1)
*
* @param a Direction vector
* @return Normalized vector
*/
Vector vector_normalized(Vector a);
/**
* Calculate two vector's distance
*
* @param a First vector
* @param b Second vector
* @return Distance between vectors
*/
float vector_distance(Vector a, Vector b);
/**
* Calculate the dot product of the vectors.
* No need to normalize, it will do it
*
* @param a First vector
* @param b Second vector
* @return value from -1 to 1
*/
float vector_dot(Vector a, Vector b);
@@ -0,0 +1,89 @@
#include "menu.h"
void add_menu(Menu *menu, const char *name, void (*callback)(void *)) {
MenuItem *items = menu->items;
menu->items = malloc(sizeof(MenuItem) * (menu->menu_count + 1));
for (uint8_t i = 0; i < menu->menu_count; i++) {
menu->items[i] = items[i];
}
free(items);
menu->items[menu->menu_count] = (MenuItem) {name, true, callback};
menu->menu_count++;
}
void free_menu(Menu *menu) {
free(menu->items);
free(menu);
}
void set_menu_state(Menu *menu, uint8_t index, bool state) {
if (menu->menu_count > index) {
menu->items[index].enabled = state;
}
if(!state && menu->current_menu==index)
move_menu(menu, 1);
}
void move_menu(Menu *menu, int8_t direction) {
if (!menu->enabled) return;
int max = menu->menu_count;
for (int8_t i = 0; i < max; i++) {
FURI_LOG_D("MENU", "Iteration %i, current %i, direction %i, state %i", i, menu->current_menu,direction,menu->items[menu->current_menu].enabled?1:0);
if (direction < 0 && menu->current_menu == 0) {
menu->current_menu = menu->menu_count - 1;
} else {
menu->current_menu = (menu->current_menu + direction) % menu->menu_count;
}
FURI_LOG_D("MENU", "After process current %i, direction %i, state %i", menu->current_menu,direction,menu->items[menu->current_menu].enabled?1:0);
if (menu->items[menu->current_menu].enabled) {
FURI_LOG_D("MENU", "Next menu %i", menu->current_menu);
return;
}
}
FURI_LOG_D("MENU", "Not found, setting false");
menu->enabled = false;
}
void activate_menu(Menu *menu, void *state) {
if (!menu->enabled) return;
menu->items[menu->current_menu].callback(state);
}
void render_menu(Menu *menu, Canvas *canvas, uint8_t pos_x, uint8_t pos_y) {
if (!menu->enabled) return;
canvas_set_color(canvas, ColorWhite);
canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2);
uint8_t w = pos_x+menu->menu_width;
uint8_t h = pos_y+10;
uint8_t p1x = pos_x+2;
uint8_t p2x = pos_x+menu->menu_width-2;
uint8_t p1y = pos_y+2;
uint8_t p2y = pos_y+8;
canvas_set_color(canvas, ColorBlack);
canvas_draw_line(canvas, p1x, pos_y, p2x, pos_y);
canvas_draw_line(canvas, p1x, h, p2x, h);
canvas_draw_line(canvas, pos_x, p1y, pos_x, p2y);
canvas_draw_line(canvas, w, p1y, w, p2y);
canvas_draw_dot(canvas, pos_x+1, pos_y+1);
canvas_draw_dot(canvas, w-1, pos_y+1);
canvas_draw_dot(canvas, w-1, h-1);
canvas_draw_dot(canvas, pos_x+1, h-1);
// canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, pos_x + menu->menu_width / 2, pos_y + 6, AlignCenter, AlignCenter,
menu->items[menu->current_menu].name);
//9*5
int center = pos_x + menu->menu_width / 2;
for(uint8_t i=0;i<4;i++){
for(int8_t j = -i; j<=i;j++){
canvas_draw_dot(canvas, center+j, pos_y-4+i);
canvas_draw_dot(canvas, center+j, pos_y+14-i);
}
}
}
@@ -0,0 +1,76 @@
#pragma once
#include <furi.h>
#include <gui/gui.h>
typedef struct {
const char *name; //Name of the menu
bool enabled; //Is the menu item enabled (it will not render, you cannot select it)
void (*callback)(void *state); //Callback for when the activate_menu is called while this menu is selected
} MenuItem;
typedef struct {
MenuItem *items; //list of menu items
uint8_t menu_count; //count of menu items (do not change)
uint8_t current_menu; //currently selected menu item
uint8_t menu_width; //width of the menu
bool enabled; //is the menu enabled (it will not render and accept events when disabled)
} Menu;
/**
* Cleans up the pointers used by the menu
*
* @param menu Pointer of the menu to clean up
*/
void free_menu(Menu *menu);
/**
* Add a new menu item
*
* @param menu Pointer of the menu
* @param name Name of the menu item
* @param callback Callback called on activation
*/
void add_menu(Menu *menu, const char *name, void (*callback)(void *));
/**
* Setting menu item to be enabled/disabled
*
* @param menu Pointer of the menu
* @param index Menu index to set
* @param state Enabled (true), Disabled(false)
*/
void set_menu_state(Menu *menu, uint8_t index, bool state);
/**
* Moves selection up or down
*
* @param menu Pointer of the menu
* @param direction Direction to move -1 down, 1 up
*/
void move_menu(Menu *menu, int8_t direction);
/**
* Triggers the current menu callback
*
* @param menu Pointer of the menu
* @param state Usually your application state
*/
void activate_menu(Menu *menu, void *state);
/**
* Renders the menu at a coordinate (call it in your render function).
*
* Keep in mind that Flipper has a 128x64 pixel screen resolution and the coordinate
* you give is the menu's rectangle top-left corner (arrows not included).
* The rectangle height is 10 px, the arrows have a 4 pixel height. Space needed is 18px.
* The width of the menu can be configured in the menu object.
*
*
* @param menu Pointer of the menu
* @param canvas Flippers Canvas pointer
* @param pos_x X position to draw
* @param pos_y Y position to draw
*/
void render_menu(Menu *menu, Canvas *canvas, uint8_t pos_x, uint8_t pos_y);
@@ -0,0 +1,66 @@
#include "queue.h"
void render_queue(const QueueState *queue_state, const void *app_state, Canvas *const canvas) {
if (queue_state->current != NULL && queue_state->current->render != NULL)
((QueueItem *) queue_state->current)->render(app_state, canvas);
}
bool run_queue(QueueState *queue_state, void *app_state) {
if (queue_state->current != NULL) {
queue_state->running = true;
if ((furi_get_tick() - queue_state->start) >= queue_state->current->duration)
dequeue(queue_state, app_state);
return true;
}
return false;
}
void dequeue(QueueState *queue_state, void *app_state) {
((QueueItem *) queue_state->current)->callback(app_state);
QueueItem *f = queue_state->current;
queue_state->current = f->next;
free(f);
if (queue_state->current != NULL) {
if (queue_state->current->start != NULL)
queue_state->current->start(app_state);
queue_state->start = furi_get_tick();
}else{
queue_state->running = false;
}
}
void queue_clear(QueueState *queue_state) {
queue_state->running = false;
QueueItem *curr = queue_state->current;
while (curr != NULL) {
QueueItem *f = curr;
curr = curr->next;
free(f);
}
}
void enqueue(QueueState *queue_state, void *app_state,
void(*done)(void *state), void(*start)(void *state),
void (*render)(const void *state, Canvas *const canvas), uint32_t duration) {
QueueItem *next;
if (queue_state->current == NULL) {
queue_state->start = furi_get_tick();
queue_state->current = malloc(sizeof(QueueItem));
next = queue_state->current;
if (next->start != NULL)
next->start(app_state);
} else {
next = queue_state->current;
while (next->next != NULL) { next = (QueueItem *) (next->next); }
next->next = malloc(sizeof(QueueItem));
next = next->next;
}
next->callback = done;
next->render = render;
next->start = start;
next->duration = duration;
next->next = NULL;
}
@@ -0,0 +1,64 @@
#pragma once
#include <gui/gui.h>
#include <furi.h>
typedef struct {
void (*callback)(void *state); //Callback for when the item is dequeued
void (*render)(const void *state, Canvas *const canvas); //Callback for the rendering loop while this item is running
void (*start)(void *state); //Callback when this item is started running
void *next; //Pointer to the next item
uint32_t duration; //duration of the item
} QueueItem;
typedef struct {
unsigned int start; //current queue item start time
QueueItem *current; //current queue item
bool running; //is the queue running
} QueueState;
/**
* Enqueue a new item.
*
* @param queue_state The queue state pointer
* @param app_state Your app state
* @param done Callback for dequeue event
* @param start Callback for when the item is activated
* @param render Callback to render loop if needed
* @param duration Length of the item
*/
void enqueue(QueueState *queue_state, void *app_state,
void(*done)(void *state), void(*start)(void *state),
void (*render)(const void *state, Canvas *const canvas), uint32_t duration);
/**
* Clears all queue items
*
* @param queue_state The queue state pointer
*/
void queue_clear(QueueState *queue_state);
/**
* Dequeues the active queue item. Usually you don't need to call it directly.
*
* @param queue_state The queue state pointer
* @param app_state Your application state
*/
void dequeue(QueueState *queue_state, void *app_state);
/**
* Runs the queue logic (place it in your tick function)
*
* @param queue_state The queue state pointer
* @param app_state Your application state
* @return FALSE when there is nothing to run, TRUE otherwise
*/
bool run_queue(QueueState *queue_state, void *app_state);
/**
* Calls the currently active queue items render callback (if there is any)
*
* @param queue_state The queue state pointer
* @param app_state Your application state
* @param canvas Pointer to Flipper's canvas object
*/
void render_queue(const QueueState *queue_state, const void *app_state, Canvas *const canvas);
+216
View File
@@ -0,0 +1,216 @@
#include "ui.h"
#include <gui/canvas_i.h>
#include <u8g2_glue.h>
#include <gui/icon_animation_i.h>
#include <gui/icon.h>
#include <gui/icon_i.h>
#include <furi_hal.h>
TileMap *tileMap;
uint8_t tileMapCount = 0;
void ui_cleanup() {
if (tileMap != NULL) {
for (uint8_t i = 0; i < tileMapCount; i++) {
if (tileMap[i].data != NULL)
free(tileMap[i].data);
}
free(tileMap);
}
}
void add_new_tilemap(uint8_t *data, unsigned long iconId) {
TileMap *old = tileMap;
tileMapCount++;
tileMap = malloc(sizeof(TileMap) * tileMapCount);
if (tileMapCount > 1) {
for (uint8_t i = 0; i < tileMapCount; i++)
tileMap[i] = old[i];
}
tileMap[tileMapCount - 1] = (TileMap) {data, iconId};
}
uint8_t *get_tilemap(unsigned long icon_id) {
for (uint8_t i = 0; i < tileMapCount; i++) {
if (tileMap[i].iconId == icon_id)
return tileMap[i].data;
}
return NULL;
}
uint32_t pixel_index(uint8_t x, uint8_t y) {
return y * SCREEN_WIDTH + x;
}
bool in_screen(int16_t x, int16_t y) {
return x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT;
}
unsigned flipBit(uint8_t x, uint8_t bit) {
return x ^ (1 << bit);
}
unsigned setBit(uint8_t x, uint8_t bit) {
return x | (1 << bit);
}
unsigned unsetBit(uint8_t x, uint8_t bit) {
return x & ~(1 << bit);
}
bool test_pixel(uint8_t *data, uint8_t x, uint8_t y, uint8_t w) {
uint8_t current_bit = (y % 8);
uint8_t current_row = ((y - current_bit) / 8);
uint8_t current_value = data[current_row * w + x];
return current_value & (1 << current_bit);
}
uint8_t* get_buffer(Canvas *const canvas){
return canvas->fb.tile_buf_ptr;
// return canvas_get_buffer(canvas);
}
uint8_t* make_buffer(){
return malloc(sizeof(uint8_t) * 8 * 128);
}
void clone_buffer(uint8_t* canvas, uint8_t* data){
for(int i=0;i<1024;i++){
data[i]= canvas[i];
}
}
bool read_pixel(Canvas *const canvas, int16_t x, int16_t y) {
if (in_screen(x, y)) {
return test_pixel(get_buffer(canvas), x, y, SCREEN_WIDTH);
}
return false;
}
void set_pixel(Canvas *const canvas, int16_t x, int16_t y, DrawMode draw_mode) {
if (in_screen(x, y)) {
uint8_t current_bit = (y % 8);
uint8_t current_row = ((y - current_bit) / 8);
uint32_t i = pixel_index(x, current_row);
uint8_t* buffer = get_buffer(canvas);
uint8_t current_value = buffer[i];
if (draw_mode == Inverse) {
buffer[i] = flipBit(current_value, current_bit);
} else {
if (draw_mode == White) {
buffer[i] = unsetBit(current_value, current_bit);
} else {
buffer[i] = setBit(current_value, current_bit);
}
}
}
}
void draw_line(Canvas *const canvas, int16_t x1, int16_t y1, int16_t x2, int16_t y2, DrawMode draw_mode) {
for (int16_t x = x2; x >= x1; x--) {
for (int16_t y = y2; y >= y1; y--) {
set_pixel(canvas, x, y, draw_mode);
}
}
}
void draw_rounded_box_frame(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode draw_mode) {
int16_t xMinCorner = x + 1;
int16_t xMax = x + w - 1;
int16_t xMaxCorner = x + w - 2;
int16_t yMinCorner = y + 1;
int16_t yMax = y + h - 1;
int16_t yMaxCorner = y + h - 2;
draw_line(canvas, xMinCorner, y, xMaxCorner, y, draw_mode);
draw_line(canvas, xMinCorner, yMax, xMaxCorner, yMax, draw_mode);
draw_line(canvas, x, yMinCorner, x, yMaxCorner, draw_mode);
draw_line(canvas, xMax, yMinCorner, xMax, yMaxCorner, draw_mode);
}
void draw_rounded_box(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode draw_mode) {
for (int16_t o = w - 2; o >= 1; o--) {
for (int16_t p = h - 2; p >= 1; p--) {
set_pixel(canvas, x + o, y + p, draw_mode);
}
}
draw_rounded_box_frame(canvas, x, y, w, h, draw_mode);
}
void invert_shape(Canvas *const canvas, uint8_t *data, int16_t x, int16_t y, uint8_t w, uint8_t h) {
draw_pixels(canvas, data, x, y, w, h, Inverse);
}
void draw_pixels(Canvas *const canvas, uint8_t *data, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode drawMode) {
for (int8_t o = 0; o < w; o++) {
for (int8_t p = 0; p < h; p++) {
if (in_screen(o + x, p + y) && data[p * w + o] == 1)
set_pixel(canvas, o + x, p + y, drawMode);
}
}
}
void draw_rectangle(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode drawMode) {
for (int8_t o = 0; o < w; o++) {
for (int8_t p = 0; p < h; p++) {
if (in_screen(o + x, p + y)) {
set_pixel(canvas, o + x, p + y, drawMode);
}
}
}
}
void invert_rectangle(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h) {
draw_rectangle(canvas, x, y, w, h, Inverse);
}
uint8_t *image_data(Canvas *const canvas, const Icon *icon) {
uint8_t *data = malloc(sizeof(uint8_t) * 8 * 128);
uint8_t *screen = canvas->fb.tile_buf_ptr;
canvas->fb.tile_buf_ptr = data;
canvas_draw_icon(canvas, 0, 0, icon);
canvas->fb.tile_buf_ptr = screen;
return data;
}
uint8_t *getOrAddIconData(Canvas *const canvas, const Icon *icon) {
uint8_t *icon_data = get_tilemap((unsigned long) icon);
if (icon_data == NULL) {
icon_data = image_data(canvas, icon);
add_new_tilemap(icon_data, (unsigned long) icon);
}
return icon_data;
}
void draw_icon_clip(Canvas *const canvas, const Icon *icon, int16_t x, int16_t y, uint8_t left, uint8_t top, uint8_t w,
uint8_t h, DrawMode drawMode) {
uint8_t *icon_data = getOrAddIconData(canvas, icon);
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
bool on = test_pixel(icon_data, left + i, top + j, SCREEN_WIDTH);
if (drawMode == Filled) {
set_pixel(canvas, x + i, y + j, on ? Black : White);
} else if (on)
set_pixel(canvas, x + i, y + j, drawMode);
}
}
}
void draw_icon_clip_flipped(Canvas *const canvas, const Icon *icon, int16_t x, int16_t y, uint8_t left, uint8_t top,
uint8_t w,
uint8_t h, DrawMode drawMode) {
uint8_t *icon_data = getOrAddIconData(canvas, icon);
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
bool on = test_pixel(icon_data, left + i, top + j, SCREEN_WIDTH);
if (drawMode == Filled) {
set_pixel(canvas, x + w - i - 1, y + h - j - 1, on ? Black : White);
} else if (on)
set_pixel(canvas, x + w - i - 1, y + h - j - 1, drawMode);
}
}
}
@@ -0,0 +1,58 @@
#pragma once
#include <furi.h>
#include <gui/canvas.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
typedef enum {
Black,
White,
Inverse,
Filled //Currently only for Icon clip drawing
} DrawMode;
// size is the screen size
typedef struct {
uint8_t *data;
unsigned long iconId;
} TileMap;
bool test_pixel(uint8_t *data, uint8_t x, uint8_t y, uint8_t w);
uint8_t *image_data(Canvas *const canvas, const Icon *icon);
uint32_t pixel_index(uint8_t x, uint8_t y);
void draw_icon_clip(Canvas *const canvas, const Icon *icon, int16_t x, int16_t y, uint8_t left, uint8_t top, uint8_t w,
uint8_t h, DrawMode drawMode);
void draw_icon_clip_flipped(Canvas *const canvas, const Icon *icon, int16_t x, int16_t y, uint8_t left, uint8_t top, uint8_t w,
uint8_t h, DrawMode drawMode);
void draw_rounded_box(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode drawMode);
void draw_rounded_box_frame(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode drawMode);
void draw_rectangle(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode drawMode);
void invert_rectangle(Canvas *const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h);
void invert_shape(Canvas *const canvas, uint8_t *data, int16_t x, int16_t y, uint8_t w, uint8_t h);
void draw_pixels(Canvas *const canvas, uint8_t *data, int16_t x, int16_t y, uint8_t w, uint8_t h, DrawMode drawMode);
bool read_pixel(Canvas *const canvas, int16_t x, int16_t y);
void set_pixel(Canvas *const canvas, int16_t x, int16_t y, DrawMode draw_mode);
void draw_line(Canvas *const canvas, int16_t x1, int16_t y1, int16_t x2, int16_t y2, DrawMode draw_mode);
bool in_screen(int16_t x, int16_t y);
void ui_cleanup();
uint8_t* get_buffer(Canvas *const canvas);
uint8_t* make_buffer();
void clone_buffer(uint8_t* canvas, uint8_t* data);
+61
View File
@@ -0,0 +1,61 @@
#pragma once
#include <furi.h>
#include <input/input.h>
#include <gui/elements.h>
#include <flipper_format/flipper_format.h>
#include <flipper_format/flipper_format_i.h>
#include "common/card.h"
#include "common/queue.h"
#define APP_NAME "Solitaire"
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} AppEvent;
typedef enum {
GameStateGameOver,
GameStateStart,
GameStatePlay,
GameStateAnimate
} PlayState;
typedef struct {
uint8_t *buffer;
Card card;
int8_t deck;
int indexes[4];
float x;
float y;
float vx;
float vy;
bool started;
} CardAnimation;
typedef struct {
Deck deck;
Hand bottom_columns[7];
Card top_cards[4];
bool dragging_deck;
uint8_t dragging_column;
Hand dragging_hand;
InputKey input;
bool started;
bool processing;
bool longPress;
PlayState state;
unsigned int last_tick;
uint8_t selectRow;
uint8_t selectColumn;
int8_t selected_card;
CardAnimation animation;
uint8_t *buffer;
} GameState;
+542
View File
@@ -0,0 +1,542 @@
#include <stdlib.h>
#include <dolphin/dolphin.h>
#include <furi.h>
#include <gui/canvas_i.h>
#include "defines.h"
#include "common/ui.h"
#include "solitaire_icons.h"
#include <notification/notification.h>
#include <notification/notification_messages.h>
const NotificationSequence sequence_fail = {
&message_vibro_on,
&message_note_c4,
&message_delay_10,
&message_vibro_off,
&message_sound_off,
&message_delay_10,
&message_vibro_on,
&message_note_a3,
&message_delay_10,
&message_vibro_off,
&message_sound_off,
NULL,
};
int8_t columns[7][3] = {
{1, 1, 25},
{19, 1, 25},
{37, 1, 25},
{55, 1, 25},
{73, 1, 25},
{91, 1, 25},
{109, 1, 25},
};
bool can_place_card(Card where, Card what) {
FURI_LOG_D(APP_NAME, "TESTING pip %i, letter %i with pip %i, letter %i", where.pip, where.character, what.pip,
what.character);
bool a_black = where.pip == 0 || where.pip == 3;
bool b_black = what.pip == 0 || what.pip == 3;
if (a_black == b_black) return false;
int8_t a_letter = (int8_t) where.character;
int8_t b_letter = (int8_t) what.character;
if (a_letter == 12) a_letter = -1;
if (b_letter == 12) b_letter = -1;
return (a_letter - 1) == b_letter;
}
static void draw_scene(Canvas *const canvas, const GameState *game_state) {
int deckIndex = game_state->deck.index;
if (game_state->dragging_deck)
deckIndex--;
if ((game_state->deck.index < (game_state->deck.card_count - 1) || game_state->deck.index == -1) && game_state->deck.card_count>0) {
draw_card_back_at(columns[0][0], columns[0][1], canvas);
if (game_state->selectRow == 0 && game_state->selectColumn == 0) {
draw_rounded_box(canvas, columns[0][0] + 1, columns[0][1] + 1, CARD_WIDTH - 2, CARD_HEIGHT - 2,
Inverse);
}
} else
draw_card_space(columns[0][0], columns[0][1],
game_state->selectRow == 0 && game_state->selectColumn == 0,
canvas);
//deck side
if (deckIndex >= 0) {
Card c = game_state->deck.cards[deckIndex];
draw_card_at_colored(columns[1][0], columns[1][1], c.pip, c.character,
game_state->selectRow == 0 && game_state->selectColumn == 1, canvas);
} else
draw_card_space(columns[1][0], columns[1][1],
game_state->selectRow == 0 && game_state->selectColumn == 1,
canvas);
for (uint8_t i = 0; i < 4; i++) {
Card current = game_state->top_cards[i];
bool selected = game_state->selectRow == 0 && game_state->selectColumn == (i + 3);
if (current.disabled) {
draw_card_space(columns[i + 3][0], columns[i + 3][1], selected, canvas);
} else {
draw_card_at(columns[i + 3][0], columns[i + 3][1], current.pip, current.character, canvas);
if (selected) {
draw_rounded_box(canvas, columns[i + 3][0], columns[i + 3][1], CARD_WIDTH, CARD_HEIGHT,
Inverse);
}
}
}
for (uint8_t i = 0; i < 7; i++) {
bool selected = game_state->selectRow == 1 && game_state->selectColumn == i;
int8_t index= (game_state->bottom_columns[i].index - 1 - game_state->selected_card);
if(index<0)index=0;
draw_hand_column(game_state->bottom_columns[i], columns[i][0], columns[i][2],
selected ? index : -1, canvas);
}
int8_t pos[2] = {columns[game_state->selectColumn][0],
columns[game_state->selectColumn][game_state->selectRow + 1]};
/* draw_icon_clip(canvas, &I_card_graphics, pos[0] + CARD_HALF_WIDTH, pos[1] + CARD_HALF_HEIGHT, 30, 5, 5, 5,
Filled);*/
if (game_state->dragging_hand.index > 0) {
draw_hand_column(game_state->dragging_hand,
pos[0] + CARD_HALF_WIDTH + 3, pos[1] + CARD_HALF_HEIGHT + 3,
-1, canvas);
}
}
static void draw_animation(Canvas *const canvas, const GameState *game_state) {
if (!game_state->animation.started) {
draw_scene(canvas, game_state);
} else {
clone_buffer(game_state->animation.buffer, get_buffer(canvas));
draw_card_at((int8_t) game_state->animation.x, (int8_t) game_state->animation.y, game_state->animation.card.pip,
game_state->animation.card.character, canvas);
}
clone_buffer(get_buffer(canvas), game_state->animation.buffer);
}
static void render_callback(Canvas *const canvas, void *ctx) {
const GameState *game_state = acquire_mutex((ValueMutex *) ctx, 25);
if (game_state == NULL) {
return;
}
switch (game_state->state) {
case GameStateAnimate:
draw_animation(canvas, game_state);
break;
case GameStateStart:
canvas_draw_icon(canvas, 0, 0, &I_solitaire_main);
break;
case GameStatePlay:
draw_scene(canvas, game_state);
break;
default:
break;
}
release_mutex((ValueMutex *) ctx, game_state);
}
void remove_drag(GameState *game_state) {
if (game_state->dragging_deck) {
remove_from_deck(game_state->deck.index, &(game_state->deck));
game_state->dragging_deck = false;
} else if (game_state->dragging_column < 7) {
game_state->dragging_column = 8;
}
game_state->dragging_hand.index = 0;
}
bool handleInput(GameState *game_state) {
Hand currentHand = game_state->bottom_columns[game_state->selectColumn];
switch (game_state->input) {
case InputKeyUp:
if (game_state->selectRow > 0) {
int first = first_non_flipped_card(currentHand);
first = currentHand.index - first;
if ((first - 1) > game_state->selected_card && game_state->dragging_hand.index == 0 &&
!game_state->longPress) {
game_state->selected_card++;
} else {
game_state->selectRow--;
game_state->selected_card = 0;
}
}
break;
case InputKeyDown:
if (game_state->selectRow < 1) {
game_state->selectRow++;
game_state->selected_card = 0;
} else {
if (game_state->selected_card > 0) {
if (game_state->longPress)
game_state->selected_card = 0;
else
game_state->selected_card--;
}
}
break;
case InputKeyRight:
if (game_state->selectColumn < 6) {
game_state->selectColumn++;
game_state->selected_card = 0;
}
break;
case InputKeyLeft:
if (game_state->selectColumn > 0) {
game_state->selectColumn--;
game_state->selected_card = 0;
}
break;
case InputKeyOk:
return true;
break;
default:
break;
}
if (game_state->selectRow == 0 && game_state->selectColumn == 2) {
if (game_state->input == InputKeyRight)
game_state->selectColumn++;
else
game_state->selectColumn--;
}
if (game_state->dragging_hand.index > 0)
game_state->selected_card = 0;
return false;
}
bool place_on_top(Card *where, Card what) {
if (where->disabled && what.character == 12) {
where->disabled = what.disabled;
where->pip = what.pip;
where->character = what.character;
return true;
} else if (where->pip == what.pip) {
int8_t a_letter = (int8_t) where->character;
int8_t b_letter = (int8_t) what.character;
if (a_letter == 12) a_letter = -1;
if (b_letter == 12) b_letter = -1;
if ((a_letter + 1) == b_letter) {
where->disabled = what.disabled;
where->pip = what.pip;
where->character = what.character;
return true;
}
}
return false;
}
void tick(GameState *game_state, NotificationApp *notification) {
game_state->last_tick = furi_get_tick();
uint8_t row = game_state->selectRow;
uint8_t column = game_state->selectColumn;
if (game_state->state != GameStatePlay && game_state->state != GameStateAnimate) return;
bool wasAction = false;
if (handleInput(game_state)) {
if (game_state->state == GameStatePlay) {
if(game_state->top_cards[0].character==11 && game_state->top_cards[1].character==11 && game_state->top_cards[2].character==11 && game_state->top_cards[3].character==11){
game_state->state=GameStateAnimate;
return;
}
if (game_state->longPress && game_state->dragging_hand.index == 1) {
for (uint8_t i = 0; i < 4; i++) {
if (place_on_top(&(game_state->top_cards[i]), game_state->dragging_hand.cards[0])) {
remove_drag(game_state);
wasAction = true;
break;
}
}
} else {
if (row == 0 && column == 0 && game_state->dragging_hand.index == 0) {
FURI_LOG_D(APP_NAME, "Drawing card");
game_state->deck.index++;
wasAction = true;
if (game_state->deck.index >= (game_state->deck.card_count))
game_state->deck.index = -1;
}
//pick/place from deck
else if (row == 0 && column == 1) {
//place
if (game_state->dragging_deck) {
wasAction = true;
game_state->dragging_deck = false;
game_state->dragging_hand.index = 0;
}
//pick
else {
if (game_state->dragging_hand.index == 0 && game_state->deck.index >= 0) {
wasAction = true;
game_state->dragging_deck = true;
add_to_hand(&(game_state->dragging_hand), game_state->deck.cards[game_state->deck.index]);
}
}
}
//place on top row
else if (row == 0 && game_state->dragging_hand.index == 1) {
column -= 3;
Card currCard = game_state->dragging_hand.cards[0];
wasAction = place_on_top(&(game_state->top_cards[column]), currCard);
if (wasAction)
remove_drag(game_state);
}
//pick/place from bottom
else if (row == 1) {
Hand *curr_hand = &(game_state->bottom_columns[column]);
//pick up
if (game_state->dragging_hand.index == 0) {
Card curr_card = curr_hand->cards[curr_hand->index - 1];
if (curr_card.flipped) {
curr_hand->cards[curr_hand->index - 1].flipped = false;
wasAction = true;
} else {
if (curr_hand->index > 0) {
extract_hand_region(curr_hand, &(game_state->dragging_hand),
curr_hand->index - game_state->selected_card - 1);
game_state->selected_card = 0;
game_state->dragging_column = column;
wasAction = true;
}
}
}
//place
else {
Card first = game_state->dragging_hand.cards[0];
if (game_state->dragging_column == column ||
(curr_hand->index == 0 && first.character == 11) ||
can_place_card(curr_hand->cards[curr_hand->index - 1], first)
) {
add_hand_region(curr_hand, &(game_state->dragging_hand));
remove_drag(game_state);
wasAction = true;
}
}
}
}
if (!wasAction) {
notification_message(notification, &sequence_fail);
}
}
}
if (game_state->state == GameStateAnimate) {
if (game_state->animation.started)
game_state->state = GameStateStart;
game_state->animation.started = true;
if (game_state->animation.x < -20 || game_state->animation.x > 128) {
game_state->animation.deck++;
if (game_state->animation.deck > 3)
game_state->animation.deck = 0;
int8_t cardIndex = 11 - game_state->animation.indexes[game_state->animation.deck];
if (game_state->animation.indexes[0] == 13 &&
game_state->animation.indexes[1] == 13 &&
game_state->animation.indexes[2] == 13 &&
game_state->animation.indexes[3] == 13) {
game_state->state = GameStateStart;
return;
}
if (cardIndex == -1)
cardIndex = 12;
game_state->animation.card = (Card) {
game_state->top_cards[game_state->animation.deck].pip,
cardIndex,
false, false
};
game_state->animation.indexes[game_state->animation.deck]++;
game_state->animation.vx = -(rand() % 3 + 1) * (rand() % 2 == 1 ? 1 : -1);
game_state->animation.vy = (rand() % 3 + 1);
game_state->animation.x = columns[game_state->animation.deck + 3][0];
game_state->animation.y = columns[game_state->animation.deck + 3][1];
}
game_state->animation.x += game_state->animation.vx;
game_state->animation.y -= game_state->animation.vy;
game_state->animation.vy -= 1;
if (game_state->animation.vy < -10)game_state->animation.vy = -10;
if (game_state->animation.y > 41) {
game_state->animation.y = 41;
game_state->animation.vy = -(game_state->animation.vy * 0.7f);
}
}
}
void init(GameState *game_state) {
game_state->selectColumn = 0;
game_state->selected_card = 0;
game_state->selectRow = 0;
generate_deck(&(game_state->deck), 1);
shuffle_deck(&(game_state->deck));
game_state->dragging_deck = false;
game_state->animation.started = false;
game_state->animation.deck = -1;
game_state->animation.x = -21;
game_state->state = GameStatePlay;
game_state->dragging_column = 8;
for (uint8_t i = 0; i < 7; i++) {
free_hand(&(game_state->bottom_columns[i]));
init_hand(&(game_state->bottom_columns[i]), 21);
game_state->bottom_columns[i].index = 0;
for (uint8_t j = 0; j <= i; j++) {
Card cur = remove_from_deck(0, &(game_state->deck));
cur.flipped = i != j;
add_to_hand(&(game_state->bottom_columns[i]), cur);
}
}
for (uint8_t i = 0; i < 4; i++) {
game_state->animation.indexes[i] = 0;
game_state->top_cards[i] = (Card) {0, 0, true, false};
}
game_state->deck.index = -1;
}
void init_start(GameState *game_state) {
game_state->input = InputKeyMAX;
for (uint8_t i = 0; i < 7; i++)
init_hand(&(game_state->bottom_columns[i]), 21);
init_hand(&(game_state->dragging_hand), 13);
game_state->animation.buffer = make_buffer();
}
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) {
furi_assert(event_queue);
AppEvent event = {.type = EventTypeTick};
furi_message_queue_put(event_queue, &event, 0);
}
int32_t solitaire_app(void *p) {
UNUSED(p);
int32_t return_code = 0;
FuriMessageQueue *event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
GameState *game_state = malloc(sizeof(GameState));
init_start(game_state);
set_card_graphics(&I_card_graphics);
game_state->state = GameStateStart;
game_state->processing = true;
ValueMutex state_mutex;
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;
}
NotificationApp *notification = furi_record_open(RECORD_NOTIFICATION);
notification_message_block(notification, &sequence_display_backlight_enforce_on);
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);
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 30);
Gui *gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
AppEvent event;
for (bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 150);
GameState *localstate = (GameState *) acquire_mutex_block(&state_mutex);
bool hadChange = false;
if (event_status == FuriStatusOk) {
if (event.type == EventTypeKey) {
if (event.input.type == InputTypeLong) {
game_state->longPress = true;
switch (event.input.key) {
case InputKeyUp:
case InputKeyDown:
case InputKeyRight:
case InputKeyLeft:
case InputKeyOk:
localstate->input = event.input.key;
break;
default:
break;
}
} else if (event.input.type == InputTypePress) {
game_state->longPress = false;
switch (event.input.key) {
case InputKeyUp:
case InputKeyDown:
case InputKeyRight:
case InputKeyLeft:
case InputKeyOk:
if (event.input.key == InputKeyOk && localstate->state == GameStateStart) {
localstate->state = GameStatePlay;
init(game_state);
}
else {
hadChange = true;
localstate->input = event.input.key;
}
break;
case InputKeyBack:
processing = false;
return_code = 1;
break;
default:
break;
}
}
} else if (event.type == EventTypeTick) {
tick(localstate, notification);
processing = localstate->processing;
localstate->input = InputKeyMAX;
}
} else {
FURI_LOG_W(APP_NAME, "osMessageQueue: event timeout");
// event timeout
}
if (hadChange || game_state->state == GameStateAnimate)
view_port_update(view_port);
release_mutex(&state_mutex, localstate);
}
notification_message_block(notification, &sequence_display_backlight_enforce_auto);
furi_timer_free(timer);
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
view_port_free(view_port);
delete_mutex(&state_mutex);
free_and_exit:
free(game_state->animation.buffer);
ui_cleanup();
for (uint8_t i = 0; i < 7; i++)
free_hand(&(game_state->bottom_columns[i]));
free(game_state->deck.cards);
free(game_state);
furi_message_queue_free(event_queue);
return return_code;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B