Add slots game
13
applications/external/slots/application.fam
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
App(
|
||||
appid="slotmachine",
|
||||
name="Slot Machine",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="slotmachine_app",
|
||||
cdefines=["APP_SLOTMACHINE"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
fap_icon="ddgame_icon.png",
|
||||
order=30,
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets",
|
||||
)
|
||||
BIN
applications/external/slots/assets/little_coin.png
vendored
Normal file
|
After Width: | Height: | Size: 158 B |
BIN
applications/external/slots/assets/x2.png
vendored
Normal file
|
After Width: | Height: | Size: 256 B |
BIN
applications/external/slots/assets/x2_2.png
vendored
Normal file
|
After Width: | Height: | Size: 212 B |
BIN
applications/external/slots/assets/x3.png
vendored
Normal file
|
After Width: | Height: | Size: 251 B |
BIN
applications/external/slots/assets/x4.png
vendored
Normal file
|
After Width: | Height: | Size: 224 B |
BIN
applications/external/slots/assets/x5.png
vendored
Normal file
|
After Width: | Height: | Size: 247 B |
BIN
applications/external/slots/ddgame_icon.png
vendored
Normal file
|
After Width: | Height: | Size: 177 B |
268
applications/external/slots/slotmachine.c
vendored
Normal file
@@ -0,0 +1,268 @@
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/elements.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <input/input.h>
|
||||
#include <furi_hal.h>
|
||||
#include <slotmachine_icons.h>
|
||||
|
||||
const Icon* slot_frames[] = {&I_x2, &I_x3, &I_x4, &I_x2_2, &I_x5};
|
||||
|
||||
const uint8_t slot_coef[] = {2, 3, 4, 2, 5};
|
||||
|
||||
typedef struct {
|
||||
uint8_t x, y, value, times, speed;
|
||||
bool spining;
|
||||
} SlotColumn;
|
||||
|
||||
int COLUMNS_COUNT = 4;
|
||||
int MAX_COLUMNS_COUNT = 4;
|
||||
|
||||
typedef struct {
|
||||
Gui* gui; // container gui
|
||||
ViewPort* view_port; // current viewport
|
||||
FuriMessageQueue* input_queue; // Input Events queue
|
||||
FuriMutex** model_mutex; // mutex for safe threads
|
||||
uint16_t bet;
|
||||
double money, winamount;
|
||||
SlotColumn* columns[4];
|
||||
bool winview;
|
||||
} SlotMachineApp;
|
||||
|
||||
#define START_MONEY 1500;
|
||||
#define START_BET 300;
|
||||
#define SSRAND_MAX 5;
|
||||
#define DEFAULT_SPEED 16;
|
||||
|
||||
uint8_t DEFAULT_SPINNING_TIMES = 10;
|
||||
|
||||
void game_results(SlotMachineApp* app) {
|
||||
int matches[] = {0, 0, 0, 0, 0};
|
||||
|
||||
double total = 0;
|
||||
|
||||
for(int i = 0; i < COLUMNS_COUNT; i++) {
|
||||
matches[app->columns[i]->value]++;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 5; i++) {
|
||||
if(matches[i] >= 2) {
|
||||
total += app->bet * (slot_coef[i] / (double)(MAX_COLUMNS_COUNT + 1 - matches[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if(total > 0) {
|
||||
app->money += total;
|
||||
app->winamount = total;
|
||||
app->winview = true;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_container(Canvas* canvas) {
|
||||
canvas_draw_rframe(canvas, 2, 12, 120, 34, 3);
|
||||
canvas_draw_rframe(canvas, 2, 13, 120, 34, 3);
|
||||
canvas_draw_rframe(canvas, 2, 14, 120, 34, 3);
|
||||
canvas_draw_rframe(canvas, 2, 15, 120, 34, 3);
|
||||
canvas_draw_rframe(canvas, 2, 16, 120, 34, 3);
|
||||
canvas_draw_rframe(canvas, 2, 17, 120, 34, 3);
|
||||
canvas_draw_line(canvas, 31, 16, 31, 48);
|
||||
canvas_draw_line(canvas, 61, 16, 61, 48);
|
||||
canvas_draw_line(canvas, 91, 16, 91, 48);
|
||||
}
|
||||
|
||||
bool checkIsSpinning(SlotMachineApp* slotmachine) {
|
||||
for(int i = 0; i < COLUMNS_COUNT; i++) {
|
||||
if(slotmachine->columns[i]->spining) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void drawButton(Canvas* canvas, uint8_t x, uint8_t y, char* str, bool invert) {
|
||||
const uint8_t string_width = canvas_string_width(canvas, str);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
if(invert) {
|
||||
canvas_draw_rbox(canvas, x, y, string_width + 15, 11, 3);
|
||||
canvas_invert_color(canvas);
|
||||
} else {
|
||||
canvas_draw_rframe(canvas, x, y, string_width + 15, 11, 3);
|
||||
}
|
||||
canvas_draw_circle(canvas, x + 5, y + 5, 3);
|
||||
canvas_draw_circle(canvas, x + 5, y + 5, 1);
|
||||
canvas_draw_str(canvas, x + 13, y + 9, str);
|
||||
canvas_invert_color(canvas);
|
||||
}
|
||||
|
||||
// viewport callback
|
||||
void slotmachine_draw_callback(Canvas* canvas, void* ctx) {
|
||||
SlotMachineApp* slotmachine = (SlotMachineApp*)ctx;
|
||||
furi_check(furi_mutex_acquire(slotmachine->model_mutex, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2, 10, "Slots");
|
||||
canvas_draw_icon(canvas, 30, 3, &I_little_coin);
|
||||
|
||||
char moneyStr[15];
|
||||
snprintf(moneyStr, sizeof(moneyStr), "$%.0f", slotmachine->money);
|
||||
|
||||
char betStr[7];
|
||||
snprintf(betStr, sizeof(betStr), "$%d", slotmachine->bet);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 45, 10, moneyStr);
|
||||
canvas_draw_str(canvas, 2, canvas_height(canvas) - 3, "Bet:");
|
||||
canvas_draw_str(canvas, 20, canvas_height(canvas) - 3, betStr);
|
||||
|
||||
if(slotmachine->winview) {
|
||||
char winamountStr[30];
|
||||
snprintf(winamountStr, sizeof(winamountStr), "You win: $%.2f!", slotmachine->winamount);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2, 35, winamountStr);
|
||||
drawButton(canvas, 95, 52, "Ok", false);
|
||||
|
||||
furi_mutex_release(slotmachine->model_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0; i < COLUMNS_COUNT; i++) {
|
||||
if(slotmachine->columns[i]->spining) {
|
||||
slotmachine->columns[i]->y += slotmachine->columns[i]->speed;
|
||||
|
||||
if(slotmachine->columns[i]->y > 31) {
|
||||
slotmachine->columns[i]->y = 13;
|
||||
slotmachine->columns[i]->times--;
|
||||
slotmachine->columns[i]->speed--;
|
||||
slotmachine->columns[i]->value = rand() % SSRAND_MAX;
|
||||
|
||||
if(slotmachine->columns[i]->times == 0) {
|
||||
slotmachine->columns[i]->y = 23;
|
||||
slotmachine->columns[i]->spining = false;
|
||||
|
||||
if(i == COLUMNS_COUNT - 1) {
|
||||
game_results(slotmachine);
|
||||
}
|
||||
}
|
||||
|
||||
if(i < COLUMNS_COUNT - 1 &&
|
||||
slotmachine->columns[i]->times ==
|
||||
(DEFAULT_SPINNING_TIMES - (int)(DEFAULT_SPINNING_TIMES / 3))) {
|
||||
slotmachine->columns[i + 1]->spining = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
slotmachine->columns[i]->x,
|
||||
slotmachine->columns[i]->y,
|
||||
slot_frames[slotmachine->columns[i]->value]);
|
||||
}
|
||||
draw_container(canvas);
|
||||
drawButton(canvas, 90, 52, "Spin", checkIsSpinning(slotmachine));
|
||||
|
||||
furi_mutex_release(slotmachine->model_mutex);
|
||||
}
|
||||
|
||||
// callback for viewport input events
|
||||
static void slotmachine_input_callback(InputEvent* input_event, void* ctx) {
|
||||
SlotMachineApp* slotmachine = ctx;
|
||||
furi_message_queue_put(slotmachine->input_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
// allocation memory and initialization
|
||||
SlotMachineApp* slotmachine_app_alloc() {
|
||||
SlotMachineApp* app = malloc(sizeof(SlotMachineApp));
|
||||
app->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
app->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
app->view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(
|
||||
app->view_port, slotmachine_draw_callback, app); // viewport callback register
|
||||
view_port_input_callback_set(app->view_port, slotmachine_input_callback, app);
|
||||
|
||||
app->money = START_MONEY;
|
||||
app->bet = START_BET;
|
||||
app->winview = false;
|
||||
app->winamount = 0;
|
||||
|
||||
int x = 7;
|
||||
|
||||
for(int i = 0; i < COLUMNS_COUNT; i++) {
|
||||
app->columns[i] = malloc(sizeof(SlotColumn));
|
||||
app->columns[i]->x = x;
|
||||
app->columns[i]->y = 25;
|
||||
app->columns[i]->value = 0;
|
||||
app->columns[i]->spining = false;
|
||||
x += 30;
|
||||
}
|
||||
|
||||
app->gui = furi_record_open("gui"); // start gui and adding viewport
|
||||
gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void slotmachine_app_free(SlotMachineApp* app) {
|
||||
gui_remove_view_port(app->gui, app->view_port);
|
||||
view_port_free(app->view_port);
|
||||
furi_record_close("gui"); // free memory
|
||||
furi_mutex_free(app->model_mutex);
|
||||
for(int i = 0; i < COLUMNS_COUNT; i++) {
|
||||
free(app->columns[i]);
|
||||
}
|
||||
free(app);
|
||||
}
|
||||
|
||||
// entry point
|
||||
int32_t slotmachine_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
SlotMachineApp* slotmachine = slotmachine_app_alloc();
|
||||
InputEvent input;
|
||||
|
||||
// endless input cycle
|
||||
while(furi_message_queue_get(slotmachine->input_queue, &input, FuriWaitForever) ==
|
||||
FuriStatusOk) {
|
||||
// if thread idle - take it
|
||||
furi_check(furi_mutex_acquire(slotmachine->model_mutex, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
if(!checkIsSpinning(slotmachine)) {
|
||||
if(input.key == InputKeyBack) {
|
||||
// exit on back button
|
||||
furi_mutex_release(slotmachine->model_mutex);
|
||||
break;
|
||||
} else if(input.key == InputKeyOk && input.type == InputTypeShort && slotmachine->winview) {
|
||||
slotmachine->winview = false;
|
||||
} else if(
|
||||
input.key == InputKeyOk && input.type == InputTypeShort &&
|
||||
slotmachine->bet <= slotmachine->money) {
|
||||
COLUMNS_COUNT = rand() % 3 + 2;
|
||||
slotmachine->money -= slotmachine->bet;
|
||||
slotmachine->columns[0]->spining = true;
|
||||
|
||||
for(int i = 0; i < COLUMNS_COUNT; i++) {
|
||||
slotmachine->columns[i]->times = DEFAULT_SPINNING_TIMES;
|
||||
slotmachine->columns[i]->speed = DEFAULT_SPEED;
|
||||
}
|
||||
} else if(input.key == InputKeyUp) {
|
||||
if(slotmachine->bet + 10 < slotmachine->money) {
|
||||
slotmachine->bet += 10;
|
||||
}
|
||||
} else if(input.key == InputKeyDown) {
|
||||
if(slotmachine->bet - 10 > 0) {
|
||||
slotmachine->bet -= 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// release thread
|
||||
furi_mutex_release(slotmachine->model_mutex);
|
||||
// redraw viewport
|
||||
view_port_update(slotmachine->view_port);
|
||||
}
|
||||
|
||||
slotmachine_app_free(slotmachine);
|
||||
|
||||
return 0;
|
||||
}
|
||||