From da813fd5384d490507c0e5749488b74b5679d124 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 31 May 2023 18:39:08 +0100 Subject: [PATCH] Add slots game --- applications/external/slots/application.fam | 13 + .../external/slots/assets/little_coin.png | Bin 0 -> 158 bytes applications/external/slots/assets/x2.png | Bin 0 -> 256 bytes applications/external/slots/assets/x2_2.png | Bin 0 -> 212 bytes applications/external/slots/assets/x3.png | Bin 0 -> 251 bytes applications/external/slots/assets/x4.png | Bin 0 -> 224 bytes applications/external/slots/assets/x5.png | Bin 0 -> 247 bytes applications/external/slots/ddgame_icon.png | Bin 0 -> 177 bytes applications/external/slots/slotmachine.c | 268 ++++++++++++++++++ 9 files changed, 281 insertions(+) create mode 100644 applications/external/slots/application.fam create mode 100644 applications/external/slots/assets/little_coin.png create mode 100644 applications/external/slots/assets/x2.png create mode 100644 applications/external/slots/assets/x2_2.png create mode 100644 applications/external/slots/assets/x3.png create mode 100644 applications/external/slots/assets/x4.png create mode 100644 applications/external/slots/assets/x5.png create mode 100644 applications/external/slots/ddgame_icon.png create mode 100644 applications/external/slots/slotmachine.c diff --git a/applications/external/slots/application.fam b/applications/external/slots/application.fam new file mode 100644 index 000000000..27337ca53 --- /dev/null +++ b/applications/external/slots/application.fam @@ -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", +) diff --git a/applications/external/slots/assets/little_coin.png b/applications/external/slots/assets/little_coin.png new file mode 100644 index 0000000000000000000000000000000000000000..34ef4f8e7d7be7455d630e78a88cfc2207d5f78b GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j+1SGu;4zvI%#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSL74G){)!Z!ps}ZmV~9j}@{j-j?U}V0x(u8&IOLfmBpsVH y773UMw=lDuDB=iQ;bw3kQE!%jz@PvBT(avwr#xid&w6D@s)qSr}Lk$H4}3v{l>vx={lFC zMUiuA#!ZRYHjKiL7q-79b3hZo~!!q8F1ho(18q|u6{1-oD!M$ literal 0 HcmV?d00001 diff --git a/applications/external/slots/assets/x2_2.png b/applications/external/slots/assets/x2_2.png new file mode 100644 index 0000000000000000000000000000000000000000..6808d7212097730116823a0dc6836e3ceae19896 GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1SD^+kpz+qjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc!AwsV#}Etut&;-z4k&OqCw|{={79iE=S9Pj ze;FJ2GetT%@)Q^4m>GRm^=?bFHOrnLu6)}2P}zUyTEqB@Iqaf6Z@m*&ZLm;fH+8#` z9=Gg}?XllUVb#li=G{GimE*t(nN2QTk(*ULw=(j~{2OzC$za z(-gK$i;T*Cs=gs)eT2-)t2*CY`O}+D8=Sp-A-*(Y=e%V?pIJBeUa$Da)pgcQ^W46Z zY_XH%JP%JiQ5yS8wM9zImX}?uI%eAbCm*M2&eEN5OQGOq_TvN3xZQmBJ(-v%Z5*63 t*F5)e&wc~lnO2M0efT|+7G!ztWc=fwCUYYAPZ`i{44$rjF6*2UngE_pTYvxn literal 0 HcmV?d00001 diff --git a/applications/external/slots/assets/x4.png b/applications/external/slots/assets/x4.png new file mode 100644 index 0000000000000000000000000000000000000000..48b3cb4077c156c9ffdabe7f4490650b72a35ed3 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1SD^+kpz+qjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc!BS5b#}EtusS~657!)~}5B|P?b8Ta&iruR} z89wd-Tdrx@ugaQTA@Q;?iTi~+&pJcDGbXy0yZMxNp7>g&d-jV|sD%f{?d(yCMua(j##>X=D!UsNw RSOXov;OXk;vd$@?2>=+DPr3jA literal 0 HcmV?d00001 diff --git a/applications/external/slots/assets/x5.png b/applications/external/slots/assets/x5.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaf5401f2006ac2af81144b83645bf657404bba GIT binary patch literal 247 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1SD^+kpz+qjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc!5&W+#}Etuy%Riz8Wea~8vp;7TcO9jqj+xDg zFDcDr{zopr00|RXO8@`> literal 0 HcmV?d00001 diff --git a/applications/external/slots/ddgame_icon.png b/applications/external/slots/ddgame_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..aa2aa5b353e020189fe88e67a9902b502ceb144f GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>VM%xNb!1@J z*w6hZkrl}2EbxddW?X?_wfUrhf^MEJjv*SsYbRXfVlZHFz4`zD=AGH)hPN*J zd0GTrkqH&`zjW}XV7!W;D|>qKb#9Ynmq{JI$9#o2&%1Wr@LbR_m!)q1@f0S;C$@i1 T_BFWzO=R$N^>bP0l+XkKmeMu% literal 0 HcmV?d00001 diff --git a/applications/external/slots/slotmachine.c b/applications/external/slots/slotmachine.c new file mode 100644 index 000000000..c8eb270d6 --- /dev/null +++ b/applications/external/slots/slotmachine.c @@ -0,0 +1,268 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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; +}