| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- #include <furi.h>
- #include <gui/gui.h>
- #include <input/input.h>
- #include <stdlib.h>
- #include <dolphin/dolphin.h>
- //ORIGINAL REPO: https://github.com/Dooskington/flipperzero-zombiez
- //AUTHORS: https://github.com/Dooskington | https://github.com/DevMilanIan
- #include "zombiez.h"
- #define ZOMBIES_MAX 3
- #define ZOMBIES_WIDTH 5
- #define ZOMBIES_HEIGHT 8
- #define PROJECTILES_MAX 10
- #define MIN_Y 5
- #define MAX_Y 58
- #define WALL_X 16
- #define PLAYER_START_X 8
- #define PLAYER_START_Y (MAX_Y - MIN_Y) / 2
- typedef enum {
- EventTypeTick,
- EventTypeKey,
- } EventType;
- typedef struct {
- EventType type;
- InputEvent input;
- } PluginEvent;
- typedef enum {
- GameStatePlaying,
- GameStateGameOver
- } GameState;
- typedef struct {
- int x;
- int y;
- } Point;
- typedef struct {
- Point position;
- int hp;
- } Player;
- typedef struct {
- Point position;
- int hp;
- } Zombie;
- typedef struct {
- Point position;
- } Projectile;
- typedef struct {
- FuriMutex* mutex;
- GameState game_state;
- Player player;
- size_t zombies_count;
- Zombie* zombies[ZOMBIES_MAX];
- size_t projectiles_count;
- Projectile* projectiles[PROJECTILES_MAX];
- uint16_t score;
- bool input_shoot;
- } PluginState;
- static void render_callback(Canvas* const canvas, void* ctx) {
- furi_assert(ctx);
- const PluginState* plugin_state = ctx;
- furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
- canvas_draw_frame(canvas, 0, 0, 128, 64);
- canvas_set_font(canvas, FontPrimary);
- canvas_draw_str_aligned(
- canvas,
- plugin_state->player.position.x,
- plugin_state->player.position.y,
- AlignCenter,
- AlignCenter,
- "@");
- canvas_draw_line(canvas, WALL_X, 0, WALL_X, 64);
- canvas_draw_line(canvas, WALL_X + 2, 4, WALL_X + 2, 59);
- for(int i = 0; i < PROJECTILES_MAX; ++i) {
- Projectile* p = plugin_state->projectiles[i];
- if(p != NULL) {
- canvas_draw_disc(canvas, p->position.x, p->position.y, 3);
- }
- }
- for(int i = 0; i < ZOMBIES_MAX; ++i) {
- Zombie* z = plugin_state->zombies[i];
- if(z != NULL) {
- for(int h = 0; h < ZOMBIES_HEIGHT; h++) {
- for(int w = 0; w < ZOMBIES_WIDTH; w++) {
- // Switch animation
- int zIdx = 0;
- if(z->position.x % 2 == 0) {
- zIdx = 1;
- }
- // Draw zombie pixels
- if(zombie_array[zIdx][h][w] == 1) {
- int x = z->position.x + w;
- int y = z->position.y + h;
- canvas_draw_dot(canvas, x, y);
- }
- }
- }
- }
- }
- int heart;
- if((plugin_state->player.hp - 10) > 5) { // 16, 17, 18, 19, 20
- heart = 0;
- } else if((plugin_state->player.hp - 5) > 5) { // 11, 12, 13, 14, 15
- heart = 1;
- } else if((plugin_state->player.hp - 3) > 2) { // 6, 7, 8, 9, 10
- heart = 2;
- } else if(plugin_state->player.hp > 0) { // 1, 2, 3, 4, 5
- heart = 3;
- } else { // 0
- heart = 4;
- }
- // visual representation of health
- for(int h = 0; h < 5; h++) {
- for(int w = 0; w < 5; w++) {
- if(heart_array[heart][h][w] == 1) {
- int x = 124 - w;
- int y = 56 + h;
- canvas_draw_dot(canvas, x, y);
- }
- }
- }
- // buffer hp + score
- char hpBuffer[8];
- char scoreBuffer[14];
- if(plugin_state->game_state == GameStatePlaying) {
- // display ammo / reload
- if(plugin_state->projectiles_count >= PROJECTILES_MAX) {
- canvas_draw_str_aligned(canvas, 24, 10, AlignLeft, AlignCenter, "RELOAD");
- } else {
- for(uint8_t i = 0; i < (PROJECTILES_MAX - plugin_state->projectiles_count); i++) {
- canvas_draw_box(canvas, 24 + (4 * i), 6, 2, 4);
- }
- }
- // display hp + score
- snprintf(hpBuffer, sizeof(hpBuffer), "%u", plugin_state->player.hp);
- canvas_draw_str_aligned(canvas, 118, 62, AlignRight, AlignBottom, hpBuffer);
- snprintf(scoreBuffer, sizeof(scoreBuffer), "%u", plugin_state->score);
- canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, scoreBuffer);
- }
- // Game Over banner
- if(plugin_state->game_state == GameStateGameOver) {
- // Screen is 128x64 px
- canvas_set_color(canvas, ColorWhite);
- canvas_draw_box(canvas, 34, 20, 62, 24);
- canvas_set_color(canvas, ColorBlack);
- canvas_draw_frame(canvas, 34, 20, 62, 24);
- canvas_set_font(canvas, FontPrimary);
- canvas_draw_str(canvas, 37, 31, "Game Over");
- canvas_set_font(canvas, FontSecondary);
- snprintf(scoreBuffer, sizeof(scoreBuffer), "Score: %u", plugin_state->score);
- canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, scoreBuffer);
- }
- //char* info = (char*)malloc(16 * sizeof(char));
- //asprintf(&info, "%d, %d", plugin_state->x, plugin_state->y);
- //canvas_draw_str_aligned(canvas, 32, 16, AlignLeft, AlignBottom, info);
- //free(info);
- furi_mutex_release(plugin_state->mutex);
- }
- static void input_callback(InputEvent* input_event, void* ctx) {
- furi_assert(ctx);
- FuriMessageQueue* event_queue = ctx;
- PluginEvent event = {.type = EventTypeKey, .input = *input_event};
- furi_message_queue_put(event_queue, &event, FuriWaitForever);
- }
- static void tick(PluginState* const plugin_state) {
- if(plugin_state->input_shoot && (plugin_state->projectiles_count < PROJECTILES_MAX)) {
- Projectile* p = (Projectile*)malloc(sizeof(Projectile));
- p->position.x = plugin_state->player.position.x;
- p->position.y = plugin_state->player.position.y;
- size_t idx = plugin_state->projectiles_count;
- plugin_state->projectiles[idx] = p;
- plugin_state->projectiles_count += 1;
- }
- for(int i = 0; i < ZOMBIES_MAX; ++i) {
- if(!plugin_state->zombies[i]) {
- Zombie* z = (Zombie*)malloc(sizeof(Zombie));
- //z->hp = 20;
- z->position.x = 126;
- z->position.y = MIN_Y + (rand() % (MAX_Y - MIN_Y));
- plugin_state->zombies[i] = z;
- plugin_state->zombies_count += 1;
- }
- }
- for(int i = 0; i < PROJECTILES_MAX; ++i) {
- Projectile* p = plugin_state->projectiles[i];
- if(p != NULL) {
- p->position.x += 2;
- for(int i = 0; i < ZOMBIES_MAX; ++i) {
- Zombie* z = plugin_state->zombies[i];
- if(z != NULL) {
- if( // projectile close enough to zombie
- (((z->position.x - p->position.x) <= 2) &&
- ((z->position.y - p->position.y) <= 4)) &&
- (((p->position.x - z->position.x) <= 2) &&
- ((p->position.y - z->position.y) <= 6))) {
- //z->hp -= 5;
- //if(z->hp <= 0) {
- plugin_state->zombies_count -= 1;
- free(z);
- plugin_state->zombies[i] = NULL;
- plugin_state->score++;
- //if(plugin_state->score % 15 == 0) dolphin_deed(getRandomDeed());
- //}
- } else if(z->position.x <= WALL_X && z->position.x > 0) { // zombie got to the wall
- plugin_state->zombies_count -= 1;
- free(z);
- plugin_state->zombies[i] = NULL;
- if(plugin_state->player.hp > 0) {
- plugin_state->player.hp--;
- } else {
- plugin_state->game_state = GameStateGameOver;
- }
- } else {
- if(furi_get_tick() % 2 == 0) z->position.x--;
- }
- }
- }
- if(p->position.x >= 128) {
- free(p);
- plugin_state->projectiles[i] = NULL;
- }
- }
- }
- plugin_state->input_shoot = false;
- }
- static void timer_callback(void* ctx) {
- furi_assert(ctx);
- FuriMessageQueue* event_queue = ctx;
- PluginEvent event = {.type = EventTypeTick};
- furi_message_queue_put(event_queue, &event, 0);
- }
- static void zombiez_state_init(PluginState* const plugin_state) {
- plugin_state->player.position.x = PLAYER_START_X;
- plugin_state->player.position.y = PLAYER_START_Y;
- plugin_state->player.hp = 20;
- plugin_state->projectiles_count = 0;
- plugin_state->zombies_count = 0;
- plugin_state->score = 0;
- for(int i = 0; i < PROJECTILES_MAX; i++) {
- plugin_state->projectiles[i] = NULL;
- }
- for(int i = 0; i < ZOMBIES_MAX; i++) {
- plugin_state->zombies[i] = NULL;
- }
- plugin_state->game_state = GameStatePlaying;
- plugin_state->input_shoot = false;
- }
- int32_t zombiez_game_app(void* p) {
- UNUSED(p);
- uint32_t return_code = 0;
- FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
- PluginState* plugin_state = malloc(sizeof(PluginState));
- zombiez_state_init(plugin_state);
- plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
- if(!plugin_state->mutex) {
- FURI_LOG_E("Zombiez", "cannot create mutex\r\n");
- return_code = 255;
- goto free_and_exit;
- }
- // Set system callbacks
- ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, plugin_state);
- view_port_input_callback_set(view_port, input_callback, event_queue);
- FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue);
- furi_timer_start(timer, furi_kernel_get_tick_frequency() / 22);
- // Open GUI and register view_port
- Gui* gui = furi_record_open(RECORD_GUI);
- gui_add_view_port(gui, view_port, GuiLayerFullscreen);
- // Call dolphin deed on game start
- dolphin_deed(DolphinDeedPluginGameStart);
- PluginEvent event;
- bool isRunning = true;
- while(isRunning) {
- FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
- if(event_status == FuriStatusOk) {
- if(event.type == EventTypeKey) {
- if(event.input.type == InputTypePress) {
- switch(event.input.key) {
- case InputKeyUp:
- if(plugin_state->player.position.y > MIN_Y &&
- plugin_state->game_state == GameStatePlaying) {
- plugin_state->player.position.y--;
- }
- break;
- case InputKeyDown:
- if(plugin_state->player.position.y < MAX_Y &&
- plugin_state->game_state == GameStatePlaying) {
- plugin_state->player.position.y++;
- }
- break;
- case InputKeyOk:
- if(plugin_state->projectiles_count < PROJECTILES_MAX &&
- plugin_state->game_state == GameStatePlaying) {
- plugin_state->input_shoot = true;
- }
- break;
- case InputKeyBack:
- break;
- default:
- break;
- }
- } else if(
- event.input.type == InputTypeRepeat &&
- plugin_state->game_state == GameStatePlaying) {
- switch(event.input.key) {
- case InputKeyUp:
- if(plugin_state->player.position.y > (MIN_Y + 1)) {
- plugin_state->player.position.y -= 4;
- }
- break;
- case InputKeyDown:
- if(plugin_state->player.position.y < (MAX_Y - 1)) {
- plugin_state->player.position.y += 4;
- }
- break;
- default:
- break;
- }
- } else if(event.input.type == InputTypeLong) {
- if(event.input.key == InputKeyOk) {
- if(plugin_state->game_state == GameStateGameOver) {
- zombiez_state_init(plugin_state);
- } else if(plugin_state->projectiles_count >= PROJECTILES_MAX) {
- plugin_state->projectiles_count = 0;
- plugin_state->player.hp++;
- }
- } else if(event.input.key == InputKeyBack) {
- isRunning = false;
- }
- }
- } else if(event.type == EventTypeTick) {
- tick(plugin_state);
- }
- }
- furi_mutex_release(plugin_state->mutex);
- view_port_update(view_port);
- }
- furi_timer_free(timer);
- view_port_enabled_set(view_port, false);
- gui_remove_view_port(gui, view_port);
- furi_record_close(RECORD_GUI);
- view_port_free(view_port);
- furi_mutex_free(plugin_state->mutex);
- free_and_exit:
- free(plugin_state);
- furi_message_queue_free(event_queue);
- return return_code;
- }
|