Jelajahi Sumber

Add slots from https://github.com/xMasterX/all-the-plugins

git-subtree-dir: slots
git-subtree-mainline: 0957221ed1b7714acdfa9d760705640bbe7e6a2d
git-subtree-split: d6805243993cb3561b19c147949d739e1018057e
Willy-JL 2 tahun lalu
induk
melakukan
6c7518627f
15 mengubah file dengan 361 tambahan dan 0 penghapusan
  1. 1 0
      slots/.gitsubtree
  2. 6 0
      slots/README.md
  3. 17 0
      slots/application.fam
  4. TEMPAT SAMPAH
      slots/assets/little_coin.png
  5. TEMPAT SAMPAH
      slots/assets/x2.png
  6. TEMPAT SAMPAH
      slots/assets/x2_2.png
  7. TEMPAT SAMPAH
      slots/assets/x3.png
  8. TEMPAT SAMPAH
      slots/assets/x4.png
  9. TEMPAT SAMPAH
      slots/assets/x5.png
  10. TEMPAT SAMPAH
      slots/ddgame_icon.png
  11. TEMPAT SAMPAH
      slots/img/1.png
  12. TEMPAT SAMPAH
      slots/img/2.png
  13. TEMPAT SAMPAH
      slots/screen1.png
  14. TEMPAT SAMPAH
      slots/screen2.png
  15. 337 0
      slots/slotmachine.c

+ 1 - 0
slots/.gitsubtree

@@ -0,0 +1 @@
+https://github.com/xMasterX/all-the-plugins dev apps_source_code/flipperzero-slots

+ 6 - 0
slots/README.md

@@ -0,0 +1,6 @@
+
+# Slots for Flipper Zero :)
+![Screen1](screen1.png "Title")
+![Screen2](screen2.png "Title")
+
+I am not a C programmer, so any suggestions are welcome!

+ 17 - 0
slots/application.fam

@@ -0,0 +1,17 @@
+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",
+    fap_author="@Daniel-dev-s",
+    fap_weburl="https://github.com/Daniel-dev-s/flipperzero-slots",
+    fap_version="1.2",
+    fap_description="Simple Slots simulator game",
+)

TEMPAT SAMPAH
slots/assets/little_coin.png


TEMPAT SAMPAH
slots/assets/x2.png


TEMPAT SAMPAH
slots/assets/x2_2.png


TEMPAT SAMPAH
slots/assets/x3.png


TEMPAT SAMPAH
slots/assets/x4.png


TEMPAT SAMPAH
slots/assets/x5.png


TEMPAT SAMPAH
slots/ddgame_icon.png


TEMPAT SAMPAH
slots/img/1.png


TEMPAT SAMPAH
slots/img/2.png


TEMPAT SAMPAH
slots/screen1.png


TEMPAT SAMPAH
slots/screen2.png


+ 337 - 0
slots/slotmachine.c

@@ -0,0 +1,337 @@
+#include <furi.h>
+#include <gui/gui.h>
+#include <gui/elements.h>
+#include <stdlib.h>
+#include <storage/storage.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, loseview;
+} SlotMachineApp;
+
+typedef struct {
+    int highscore;
+} SlotsHighscore;
+
+#define START_MONEY 1500;
+#define START_BET 300;
+#define SLOTS_RAND_MAX 5;
+#define DEFAULT_SPEED 16;
+#define HIGHSCORES_FILENAME APP_DATA_PATH("slotmachine.save")
+
+uint8_t DEFAULT_SPINNING_TIMES = 10;
+static SlotsHighscore highscore;
+
+static bool highscores_load() {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    File* file = storage_file_alloc(storage);
+
+    uint16_t bytes_readed = 0;
+
+    if(storage_file_open(file, HIGHSCORES_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) {
+        bytes_readed = storage_file_read(file, &highscore, sizeof(SlotsHighscore));
+    }
+    storage_file_close(file);
+    storage_file_free(file);
+    furi_record_close(RECORD_STORAGE);
+
+    return bytes_readed == sizeof(SlotsHighscore);
+}
+
+static void highscores_save() {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+
+    File* file = storage_file_alloc(storage);
+
+    if(storage_file_open(file, HIGHSCORES_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
+        storage_file_write(file, &highscore, sizeof(SlotsHighscore));
+    }
+    storage_file_close(file);
+    storage_file_free(file);
+    furi_record_close(RECORD_STORAGE);
+}
+
+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;
+
+        if(total > highscore.highscore) {
+            highscore.highscore = total;
+            highscores_save();
+        }
+    }
+
+    if(app->money < 10) {
+        app->loseview = 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");
+    const Icon* litl_icon = &I_little_coin;
+    canvas_draw_icon(canvas, 30, 3, litl_icon);
+
+    char moneyStr[15];
+    snprintf(moneyStr, sizeof(moneyStr), "$%.0f", slotmachine->money);
+
+    char highscoresStr[25];
+    snprintf(highscoresStr, sizeof(highscoresStr), "HS:$%d", highscore.highscore);
+
+    char betStr[7];
+    snprintf(betStr, sizeof(betStr), "$%d", slotmachine->bet);
+
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str(canvas, 40, 10, moneyStr);
+    canvas_draw_str(canvas, 2, canvas_height(canvas) - 3, "Bet:");
+    canvas_draw_str(canvas, 20, canvas_height(canvas) - 3, betStr);
+    canvas_draw_str(canvas, 75, 10, highscoresStr);
+
+    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;
+    } else if(slotmachine->loseview) {
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str(canvas, 2, 35, "You lose ;(");
+        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() % SLOTS_RAND_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->loseview = 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;
+
+    if(!highscores_load()) {
+        memset(&highscore, 0, sizeof(highscore));
+    }
+    // endless input cycle
+    while(1) {
+        if(furi_message_queue_get(slotmachine->input_queue, &input, 100) == FuriStatusOk) {
+            // if thread idle - take it
+            furi_check(
+                furi_mutex_acquire(slotmachine->model_mutex, FuriWaitForever) == FuriStatusOk);
+
+            if(!checkIsSpinning(slotmachine)) {
+                if(input.key == InputKeyBack) {
+                    highscores_save();
+                    // exit on back button
+                    furi_mutex_release(slotmachine->model_mutex);
+                    break;
+                } else if(
+                    input.key == InputKeyOk && input.type == InputTypeShort &&
+                    (slotmachine->winview || slotmachine->loseview)) {
+                    slotmachine->winview = false;
+                    slotmachine->loseview = 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;
+                    }
+                } else if(input.key == InputKeyLeft && input.type == InputTypeLong) {
+                    memset(&highscore, 0, sizeof(highscore));
+                    highscores_save();
+                }
+            }
+
+            // release thread
+            furi_mutex_release(slotmachine->model_mutex);
+        }
+        // redraw viewport
+        view_port_update(slotmachine->view_port);
+    }
+
+    slotmachine_app_free(slotmachine);
+
+    return 0;
+}