antirez 3 лет назад
Сommit
596f77c042
7 измененных файлов с 241 добавлено и 0 удалено
  1. 24 0
      LICENSE
  2. 1 0
      README.md
  3. 190 0
      app.c
  4. BIN
      appicon.png
  5. 12 0
      application.fam
  6. 10 0
      binaries/README.md
  7. 4 0
      binaries/update.sh

+ 24 - 0
LICENSE

@@ -0,0 +1,24 @@
+Copyright (c) 2022-2023 Salvatore Sanfilippo <antirez at gmail dot com>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 1 - 0
README.md

@@ -0,0 +1 @@
+Asteroid game for the [Flipper Zero](https://flipperzero.one/).

+ 190 - 0
app.c

@@ -0,0 +1,190 @@
+/* Copyright (C) 2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license. */
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <input/input.h>
+#include <gui/gui.h>
+#include <stdlib.h>
+#include <gui/gui.h>
+#include <gui/view_dispatcher.h>
+#include <gui/scene_manager.h>
+#include <math.h>
+
+#define TAG "Asteroids" // Used for logging
+#define DEBUG_MSG 1
+#define SCREEN_XRES 128
+#define SCREEN_YRES 64
+
+#define MAXBUL 10
+typedef struct AsteroidsApp {
+    /* GUI */
+    Gui *gui;
+    ViewPort *view_port;     /* We just use a raw viewport and we render
+                                everything into the low level canvas. */
+    FuriMessageQueue *event_queue;  /* Keypress events go here. */
+
+    /* Game state. */
+    int running;            /* Once false exists the app. */
+    uint32_t ticks;         /* Game ticks. Increments at each refresh. */
+    float shipx;            /* Ship x position. */
+    float shipy;            /* Ship y position. */
+    float shipa;            /* Ship current angle, 2*PI is a full rotation. */
+    float shipvx;           /* x velocity. */
+    float shipvy;           /* y velocity. */
+    float bulletsx[MAXBUL]; /* Bullets x position. */
+    float bulletsy[MAXBUL]; /* Bullets y position. */
+    int bullets;            /* Active bullets. */
+    uint32_t last_bullet_tick; /* Tick the last bullet was fired. */
+    float asteroidsx[MAXBUL];  /* Asteroids x position. */
+    float asteroidsy[MAXBUL];  /* Asteroids y position. */
+    int asteroids;             /* Active asteroids. */
+} AsteroidsApp;
+
+/* Rotate the point X,Y by an angle 'a', with center 0,0. */
+void rot2D(float x, float y, float *rx, float *ry, float a) {
+    *rx = x*(float)cos(a)-y*(float)sin(a),
+    *ry = y*(float)cos(a)+x*(float)sin(a);
+}
+
+/* Render the ship at the current position, and rotated by the current
+ * angle. */
+void render_ship(Canvas *const canvas, float x, float y, float a) {
+    struct { float x; float y; } shape[3] = {
+        {-3,3}, {0,-6}, {3,3}
+    };
+    for (int j =0; j < 3; j++) {
+        float nx, ny;
+        rot2D(shape[j].x, shape[j].y, &nx, &ny, a);
+        shape[j].x = nx;
+        shape[j].y = ny;
+    }
+
+    canvas_set_color(canvas, ColorBlack);
+    for (int j =0; j < 4; j++) {
+        int a = j%3;
+        int b = (j+1)%3;
+        canvas_draw_line(canvas,x+shape[a].x,y+shape[a].y,
+                                x+shape[b].x,y+shape[b].y);
+    }
+}
+
+/* Render the current game screen. */
+static void render_callback(Canvas *const canvas, void *ctx) {
+    AsteroidsApp *app = ctx;
+
+    /* Clear screen. */
+    canvas_set_color(canvas, ColorWhite);
+    canvas_draw_box(canvas, 0, 0, 127, 63);
+    render_ship(canvas,app->shipx,app->shipy,app->shipa);
+}
+
+/* Here all we do is putting the events into the queue that will be handled
+ * in the while() loop of the app entry point function. */
+static void input_callback(InputEvent* input_event, void* ctx)
+{
+    AsteroidsApp *app = ctx;
+    furi_message_queue_put(app->event_queue,input_event,FuriWaitForever);
+}
+
+/* Allocate the application state and initialize a number of stuff.
+ * This is called in the entry point to create the application state. */
+AsteroidsApp* asteroids_app_alloc() {
+    AsteroidsApp *app = malloc(sizeof(AsteroidsApp));
+
+    app->gui = furi_record_open(RECORD_GUI);
+    app->view_port = view_port_alloc();
+    view_port_draw_callback_set(app->view_port, render_callback, app);
+    view_port_input_callback_set(app->view_port, input_callback, app);
+    gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
+    app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
+
+    app->running = 1;
+    app->ticks = 0;
+    app->shipx = SCREEN_XRES / 2;
+    app->shipy = SCREEN_YRES / 2;
+    app->shipa = 0;
+    app->shipvx = 0;
+    app->shipvy = 0;
+    app->bullets = 0;
+    app->last_bullet_tick = 0;
+    app->asteroids = 0;
+    return app;
+}
+
+/* Free what the application allocated. It is not clear to me if the
+ * Flipper OS, once the application exits, will be able to reclaim space
+ * even if we forget to free something here. */
+void asteroids_app_free(AsteroidsApp *app) {
+    furi_assert(app);
+
+    // View related.
+    view_port_enabled_set(app->view_port, false);
+    gui_remove_view_port(app->gui, app->view_port);
+    view_port_free(app->view_port);
+    furi_record_close(RECORD_GUI);
+    furi_message_queue_free(app->event_queue);
+    app->gui = NULL;
+
+    free(app);
+}
+
+/* Called periodically. Do signal processing here. Data we process here
+ * will be later displayed by the render callback. The side effect of this
+ * function is to scan for signals and set DetectedSamples. */
+static void timer_callback(void *ctx) {
+    AsteroidsApp *app = ctx;
+
+    UNUSED(app);
+}
+
+/* Handle keys interaction. */
+void asteroids_process_keypress(AsteroidsApp *app, InputEvent input) {
+    UNUSED(app);
+    UNUSED(input);
+}
+
+int32_t asteroids_app_entry(void* p) {
+    UNUSED(p);
+    AsteroidsApp *app = asteroids_app_alloc();
+
+    /* Create a timer. We do data analysis in the callback. */
+    FuriTimer *timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app);
+    furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4);
+
+    /* This is the main event loop: here we get the events that are pushed
+     * in the queue by input_callback(), and process them one after the
+     * other. The timeout is 100 milliseconds, so if not input is received
+     * before such time, we exit the queue_get() function and call
+     * view_port_update() in order to refresh our screen content. */
+    InputEvent input;
+    while(app->running) {
+        FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100);
+        if (qstat == FuriStatusOk) {
+            if (DEBUG_MSG) FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u",
+                    input.type, input.key);
+
+            /* Handle navigation here. Then handle view-specific inputs
+             * in the view specific handling function. */
+            if (input.type == InputTypeShort &&
+                input.key == InputKeyBack)
+            {
+                app->running = 0;
+            } else {
+                asteroids_process_keypress(app,input);
+            }
+        } else {
+            /* Useful to understand if the app is still alive when it
+             * does not respond because of bugs. */
+            if (DEBUG_MSG) {
+                static int c = 0; c++;
+                if (!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
+            }
+        }
+        view_port_update(app->view_port);
+    }
+
+    furi_timer_free(timer);
+    asteroids_app_free(app);
+    return 0;
+}


+ 12 - 0
application.fam

@@ -0,0 +1,12 @@
+App(
+    appid="protoview",
+    name="Asteroids",
+    apptype=FlipperAppType.EXTERNAL,
+    entry_point="asteroids_app_entry",
+    cdefines=["APP_PROTOVIEW"],
+    requires=["gui"],
+    stack_size=8*1024,
+    order=50,
+    fap_icon="appicon.png",
+    fap_category="Games",
+)

+ 10 - 0
binaries/README.md

@@ -0,0 +1,10 @@
+This is the binary distribution of the application. If you don't want
+to build it from source, just take `asteroids.fap` file and drop it into the
+following Flipper Zero location:
+
+    /ext/apps/Games
+
+The `ext` part means that we are in the SD card. So if you don't want
+to use the Android (or other) application to upload the file,
+you can just take out the SD card, insert it in your computer,
+copy the fine into `apps/Games`, and that's it.

+ 4 - 0
binaries/update.sh

@@ -0,0 +1,4 @@
+#!/bin/sh
+BINPATH="/Users/antirez/hack/flipper/official/build/f7-firmware-D/.extapps/asteroids.fap"
+cp $BINPATH .
+git commit -a -m 'Binary file updated.'