mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-12-24 19:53:08 +00:00
28eb4d1060
Not full refactoring, only small issues is fixed and moved all plugins to furi mutex instead of valuemutex Many small issues was found and fixed due mutex upgrade OFW removed 60 lines of code and it was painful
402 lines
No EOL
13 KiB
C
402 lines
No EOL
13 KiB
C
#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, FuriMessageQueue* event_queue) {
|
|
furi_assert(event_queue);
|
|
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);
|
|
}
|
|
}
|
|
|
|
view_port_update(view_port);
|
|
furi_mutex_release(plugin_state->mutex);
|
|
}
|
|
|
|
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;
|
|
} |