diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..81a8981f739 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +dist/* +.vscode +.clang-format +.editorconfig +.env +.ufbt diff --git a/README.md b/README.md index 04b906fe7a5..218097c178c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # Flipper-Zero-Laser-Tag - Laser Tag game for Flipper Zero + + Laser Tag game for Flipper Zero. ---> Not working yet, so please help! + + ![rocketgod_logo](https://github.com/RocketGod-git/shodanbot/assets/57732082/7929b554-0fba-4c2b-b22d-6772d23c4a18) diff --git a/application.fam b/application.fam new file mode 100644 index 00000000000..1ca784a2492 --- /dev/null +++ b/application.fam @@ -0,0 +1,20 @@ +App( + appid="laser_tag", + name="Laser Tag", + apptype=FlipperAppType.EXTERNAL, + entry_point="laser_tag_app", + cdefines=["APP_LASER_TAG"], + fap_category="Games", + fap_author="@RocketGod-git", + fap_version="0.1", + fap_description="Laser Tag game for Flipper Zero", + fap_icon="icons/laser_tag_10px.png", + fap_libs=["assets"], + fap_weburl="https://github.com/RocketGod-Git/Flipper-Zero-Laser-Tag", + requires=[ + "gui", + "infrared", + ], + stack_size=2 * 1024, + order=10, +) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md new file mode 100644 index 00000000000..9be3b2d20a2 --- /dev/null +++ b/docs/CHANGELOG.md @@ -0,0 +1,3 @@ +## v1.0 + +- Initial release. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000000..2fb6d451b8d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# Flipper Zero - Laser Tag + +Not working, yet. diff --git a/game_state.c b/game_state.c new file mode 100644 index 00000000000..c492f94ce39 --- /dev/null +++ b/game_state.c @@ -0,0 +1,99 @@ +#include "game_state.h" +#include + +struct GameState { + LaserTagTeam team; + uint8_t health; + uint16_t score; + uint16_t ammo; + uint32_t game_time; + bool game_over; +}; + +GameState* game_state_alloc() { + GameState* state = malloc(sizeof(GameState)); + state->team = TeamRed; + state->health = 100; + state->score = 0; + state->ammo = 100; + state->game_time = 0; + state->game_over = false; + return state; +} + +void game_state_free(GameState* state) { + free(state); +} + +void game_state_reset(GameState* state) { + state->health = 100; + state->score = 0; + state->ammo = 100; + state->game_time = 0; + state->game_over = false; +} + +void game_state_set_team(GameState* state, LaserTagTeam team) { + state->team = team; +} + +LaserTagTeam game_state_get_team(GameState* state) { + return state->team; +} + +void game_state_decrease_health(GameState* state, uint8_t amount) { + if(state->health > amount) { + state->health -= amount; + } else { + state->health = 0; + state->game_over = true; + } +} + +void game_state_increase_health(GameState* state, uint8_t amount) { + state->health = (state->health + amount > 100) ? 100 : state->health + amount; +} + +uint8_t game_state_get_health(GameState* state) { + return state->health; +} + +void game_state_increase_score(GameState* state, uint16_t points) { + state->score += points; +} + +uint16_t game_state_get_score(GameState* state) { + return state->score; +} + +void game_state_decrease_ammo(GameState* state, uint16_t amount) { + if(state->ammo > amount) { + state->ammo -= amount; + } else { + state->ammo = 0; + } +} + +void game_state_increase_ammo(GameState* state, uint16_t amount) { + state->ammo += amount; +} + +uint16_t game_state_get_ammo(GameState* state) { + return state->ammo; +} + +void game_state_update_time(GameState* state, uint32_t delta_time) { + state->game_time += delta_time; +} + +uint32_t game_state_get_time(GameState* state) { + return state->game_time; +} + +bool game_state_is_game_over(GameState* state) { + return state->game_over; +} + +void game_state_set_game_over(GameState* state, bool game_over) { + state->game_over = game_over; +} \ No newline at end of file diff --git a/game_state.h b/game_state.h new file mode 100644 index 00000000000..c1c5f52e319 --- /dev/null +++ b/game_state.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +typedef enum { + TeamRed, + TeamBlue +} LaserTagTeam; + +typedef enum { + LaserTagStateTeamSelect, + LaserTagStateGame, +} LaserTagState; + +typedef struct GameState GameState; + +GameState* game_state_alloc(); +void game_state_free(GameState* state); +void game_state_reset(GameState* state); + +void game_state_set_team(GameState* state, LaserTagTeam team); +LaserTagTeam game_state_get_team(GameState* state); + +void game_state_decrease_health(GameState* state, uint8_t amount); +void game_state_increase_health(GameState* state, uint8_t amount); +uint8_t game_state_get_health(GameState* state); + +void game_state_increase_score(GameState* state, uint16_t points); +uint16_t game_state_get_score(GameState* state); + +void game_state_decrease_ammo(GameState* state, uint16_t amount); +void game_state_increase_ammo(GameState* state, uint16_t amount); +uint16_t game_state_get_ammo(GameState* state); + +void game_state_update_time(GameState* state, uint32_t delta_time); +uint32_t game_state_get_time(GameState* state); + +bool game_state_is_game_over(GameState* state); +void game_state_set_game_over(GameState* state, bool game_over); + +#define INITIAL_HEALTH 100 +#define INITIAL_AMMO 100 +#define MAX_HEALTH 100 \ No newline at end of file diff --git a/icons/laser_tag_10px.png b/icons/laser_tag_10px.png new file mode 100644 index 00000000000..9d043a84049 Binary files /dev/null and b/icons/laser_tag_10px.png differ diff --git a/icons/toolkit.png b/icons/toolkit.png new file mode 100644 index 00000000000..2f747cb5f66 Binary files /dev/null and b/icons/toolkit.png differ diff --git a/infrared_controller.c b/infrared_controller.c new file mode 100644 index 00000000000..6c32dd38119 --- /dev/null +++ b/infrared_controller.c @@ -0,0 +1,112 @@ +#include "infrared_controller.h" +#include +#include +#include +#include +#include + +#define TAG "LaserTagInfrared" + +#define IR_COMMAND_RED_TEAM 0xA1 +#define IR_COMMAND_BLUE_TEAM 0xB2 + +struct InfraredController { + LaserTagTeam team; + InfraredWorker* worker; + FuriThread* rx_thread; + volatile bool rx_running; + volatile bool hit_received; +}; + +static void infrared_rx_callback(void* context, InfraredWorkerSignal* received_signal) { + InfraredController* controller = (InfraredController*)context; + + const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal); + if (message != NULL) { + uint32_t received_command = message->address; + if((controller->team == TeamRed && received_command == IR_COMMAND_BLUE_TEAM) || + (controller->team == TeamBlue && received_command == IR_COMMAND_RED_TEAM)) { + controller->hit_received = true; + } + } +} + +static int32_t infrared_rx_thread(void* context) { + InfraredController* controller = (InfraredController*)context; + + while(controller->rx_running) { + infrared_worker_rx_start(controller->worker); + furi_thread_flags_wait(0, FuriFlagWaitAny, 10); + } + + return 0; +} + +InfraredController* infrared_controller_alloc() { + InfraredController* controller = malloc(sizeof(InfraredController)); + controller->team = TeamRed; + controller->worker = infrared_worker_alloc(); + controller->rx_running = true; + controller->hit_received = false; + + infrared_worker_rx_set_received_signal_callback(controller->worker, infrared_rx_callback, controller); + + controller->rx_thread = furi_thread_alloc(); + furi_thread_set_name(controller->rx_thread, "IR_Rx_Thread"); + furi_thread_set_stack_size(controller->rx_thread, 1024); + furi_thread_set_context(controller->rx_thread, controller); + furi_thread_set_callback(controller->rx_thread, infrared_rx_thread); + furi_thread_start(controller->rx_thread); + + infrared_worker_rx_start(controller->worker); + + return controller; +} + +void infrared_controller_free(InfraredController* controller) { + furi_assert(controller); + + controller->rx_running = false; + furi_thread_join(controller->rx_thread); + furi_thread_free(controller->rx_thread); + + infrared_worker_rx_stop(controller->worker); + infrared_worker_free(controller->worker); + free(controller); +} + +void infrared_controller_set_team(InfraredController* controller, LaserTagTeam team) { + furi_assert(controller); + controller->team = team; +} + +void infrared_controller_send(InfraredController* controller) { + furi_assert(controller); + uint32_t command = (controller->team == TeamRed) ? IR_COMMAND_RED_TEAM : IR_COMMAND_BLUE_TEAM; + InfraredMessage message = { + .protocol = InfraredProtocolNEC, + .address = 0x00, + .command = command, + .repeat = false + }; + + infrared_worker_set_decoded_signal(controller->worker, &message); + + infrared_worker_tx_set_get_signal_callback( + controller->worker, + infrared_worker_tx_get_signal_steady_callback, + NULL); + + infrared_worker_tx_start(controller->worker); + + furi_delay_ms(250); // Delay to ensure the signal is sent + + infrared_worker_tx_stop(controller->worker); +} + +bool infrared_controller_receive(InfraredController* controller) { + furi_assert(controller); + bool hit = controller->hit_received; + controller->hit_received = false; + return hit; +} \ No newline at end of file diff --git a/infrared_controller.h b/infrared_controller.h new file mode 100644 index 00000000000..391e26ee12a --- /dev/null +++ b/infrared_controller.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include "game_state.h" // For LaserTagTeam enum + +typedef struct InfraredController InfraredController; + +/** + * Allocate and initialize an InfraredController. + * @return Pointer to the newly allocated InfraredController. + */ +InfraredController* infrared_controller_alloc(); + +/** + * Free an InfraredController and its resources. + * @param controller Pointer to the InfraredController to free. + */ +void infrared_controller_free(InfraredController* controller); + +/** + * Set the team for the InfraredController. + * @param controller Pointer to the InfraredController. + * @param team The team to set (TeamRed or TeamBlue). + */ +void infrared_controller_set_team(InfraredController* controller, LaserTagTeam team); + +/** + * Send an infrared signal corresponding to the controller's team. + * @param controller Pointer to the InfraredController. + */ +void infrared_controller_send(InfraredController* controller); + +/** + * Check if a hit has been received from the opposite team. + * @param controller Pointer to the InfraredController. + * @return true if a hit was received, false otherwise. + */ +bool infrared_controller_receive(InfraredController* controller); + +// IR command definitions +#define IR_COMMAND_RED_TEAM 0xA1 +#define IR_COMMAND_BLUE_TEAM 0xB2 \ No newline at end of file diff --git a/laser_tag_app.c b/laser_tag_app.c new file mode 100644 index 00000000000..099f1a55093 --- /dev/null +++ b/laser_tag_app.c @@ -0,0 +1,174 @@ +#include "laser_tag_app.h" +#include "laser_tag_view.h" +#include "infrared_controller.h" +#include "game_state.h" +#include +#include +#include +#include +#include +#include + +#define TAG "LaserTag" + +struct LaserTagApp { + Gui* gui; + ViewPort* view_port; + LaserTagView* view; + FuriMessageQueue* event_queue; + FuriTimer* timer; + NotificationApp* notifications; + InfraredController* ir_controller; + GameState* game_state; + LaserTagState state; +}; + +const NotificationSequence sequence_vibro_1 = { + &message_vibro_on, + &message_vibro_off, + NULL +}; + +static void laser_tag_app_timer_callback(void* context) { + furi_assert(context); + LaserTagApp* app = context; + game_state_update_time(app->game_state, 1); + laser_tag_view_update(app->view, app->game_state); +} + +static void laser_tag_app_input_callback(InputEvent* input_event, void* context) { + furi_assert(context); + LaserTagApp* app = context; + furi_message_queue_put(app->event_queue, input_event, FuriWaitForever); +} + +static void laser_tag_app_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + LaserTagApp* app = context; + if(app->state == LaserTagStateTeamSelect) { + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 32, 32, "Select Team:"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 32, 48, "LEFT: Red RIGHT: Blue"); + } else { + laser_tag_view_draw(laser_tag_view_get_view(app->view), canvas); + } +} + +LaserTagApp* laser_tag_app_alloc() { + LaserTagApp* app = malloc(sizeof(LaserTagApp)); + + app->gui = furi_record_open(RECORD_GUI); + app->view_port = view_port_alloc(); + app->view = laser_tag_view_alloc(); + app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + app->notifications = furi_record_open(RECORD_NOTIFICATION); + app->ir_controller = infrared_controller_alloc(); + app->game_state = game_state_alloc(); + app->state = LaserTagStateTeamSelect; + + view_port_draw_callback_set(app->view_port, laser_tag_app_draw_callback, app); + view_port_input_callback_set(app->view_port, laser_tag_app_input_callback, app); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + + app->timer = furi_timer_alloc(laser_tag_app_timer_callback, FuriTimerTypePeriodic, app); + furi_timer_start(app->timer, furi_kernel_get_tick_frequency()); + + return app; +} + +void laser_tag_app_free(LaserTagApp* app) { + furi_assert(app); + + furi_timer_free(app->timer); + view_port_enabled_set(app->view_port, false); + gui_remove_view_port(app->gui, app->view_port); + view_port_free(app->view_port); + laser_tag_view_free(app->view); + furi_message_queue_free(app->event_queue); + infrared_controller_free(app->ir_controller); + game_state_free(app->game_state); + + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + + free(app); +} + +void laser_tag_app_fire(LaserTagApp* app) { + infrared_controller_send(app->ir_controller); + game_state_decrease_ammo(app->game_state, 1); + notification_message(app->notifications, &sequence_blink_blue_100); +} + +void laser_tag_app_handle_hit(LaserTagApp* app) { + game_state_decrease_health(app->game_state, 10); + notification_message(app->notifications, &sequence_vibro_1); +} + +void laser_tag_app_enter_game_state(LaserTagApp* app) { + app->state = LaserTagStateGame; + game_state_reset(app->game_state); + laser_tag_view_update(app->view, app->game_state); +} + +int32_t laser_tag_app(void* p) { + UNUSED(p); + LaserTagApp* app = laser_tag_app_alloc(); + + InputEvent event; + bool running = true; + while(running) { + FuriStatus status = furi_message_queue_get(app->event_queue, &event, 100); + if(status == FuriStatusOk) { + if(event.type == InputTypePress) { + if(app->state == LaserTagStateTeamSelect) { + switch(event.key) { + case InputKeyLeft: + infrared_controller_set_team(app->ir_controller, TeamRed); + game_state_set_team(app->game_state, TeamRed); + laser_tag_app_enter_game_state(app); + break; + case InputKeyRight: + infrared_controller_set_team(app->ir_controller, TeamBlue); + game_state_set_team(app->game_state, TeamBlue); + laser_tag_app_enter_game_state(app); + break; + default: + break; + } + } else { + switch(event.key) { + case InputKeyBack: + running = false; + break; + case InputKeyOk: + laser_tag_app_fire(app); + break; + default: + break; + } + } + } + } + + if(app->state == LaserTagStateGame) { + if(infrared_controller_receive(app->ir_controller)) { + laser_tag_app_handle_hit(app); + } + + if(game_state_is_game_over(app->game_state)) { + notification_message(app->notifications, &sequence_error); + running = false; + } + + laser_tag_view_update(app->view, app->game_state); + } + + view_port_update(app->view_port); + } + + laser_tag_app_free(app); + return 0; +} diff --git a/laser_tag_app.h b/laser_tag_app.h new file mode 100644 index 00000000000..c932c4ae499 --- /dev/null +++ b/laser_tag_app.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FRAME_WIDTH 128 +#define FRAME_HEIGHT 64 + +typedef struct LaserTagApp LaserTagApp; + +LaserTagApp* laser_tag_app_alloc(); + +void laser_tag_app_free(LaserTagApp* app); + +int32_t laser_tag_app(void* p); + +void laser_tag_app_set_view_port(LaserTagApp* app, View* view); + +void laser_tag_app_switch_to_next_scene(LaserTagApp* app); + +void laser_tag_app_fire(LaserTagApp* app); + +void laser_tag_app_handle_hit(LaserTagApp* app); \ No newline at end of file diff --git a/laser_tag_icons.c b/laser_tag_icons.c new file mode 100644 index 00000000000..c6912c71730 --- /dev/null +++ b/laser_tag_icons.c @@ -0,0 +1,123 @@ +#include "laser_tag_icons.h" +#include + +const uint8_t laser_gun_icon_data[] = { + 0b00000000, 0b00000000, + 0b00000001, 0b10000000, + 0b00000011, 0b11000000, + 0b00000111, 0b11100000, + 0b00001111, 0b11110000, + 0b00011111, 0b11111000, + 0b11111111, 0b11111110, + 0b11111111, 0b11111111, +}; + +const uint8_t health_icon_data[] = { + 0b00001100, 0b00110000, + 0b00011110, 0b01111000, + 0b00111111, 0b11111100, + 0b01111111, 0b11111110, + 0b01111111, 0b11111110, + 0b00111111, 0b11111100, + 0b00011111, 0b11111000, + 0b00000111, 0b11100000, +}; + +const uint8_t ammo_icon_data[] = { + 0b00011000, 0b00011000, + 0b00111100, 0b00111100, + 0b01111110, 0b01111110, + 0b11111111, 0b11111111, + 0b11111111, 0b11111111, + 0b01111110, 0b01111110, + 0b00111100, 0b00111100, + 0b00011000, 0b00011000, +}; + +const uint8_t team_red_icon_data[] = { + 0b00011000, 0b00011000, + 0b00111100, 0b00111100, + 0b01111110, 0b01111110, + 0b11111111, 0b11111111, + 0b11111111, 0b11111111, + 0b01111110, 0b01111110, + 0b00111100, 0b00111100, + 0b00011000, 0b00011000, +}; + +const uint8_t team_blue_icon_data[] = { + 0b11100111, 0b11100111, + 0b11000011, 0b11000011, + 0b10000001, 0b10000001, + 0b00000000, 0b00000000, + 0b00000000, 0b00000000, + 0b10000001, 0b10000001, + 0b11000011, 0b11000011, + 0b11100111, 0b11100111, +}; + +const uint8_t game_over_icon_data[] = { + 0b11111111, 0b11111111, + 0b10000000, 0b00000001, + 0b10111101, 0b10111101, + 0b10100001, 0b10100001, + 0b10100001, 0b10100001, + 0b10111101, 0b10111101, + 0b10000000, 0b00000001, + 0b11111111, 0b11111111, +}; + +const uint8_t* const laser_gun_icon_frames[] = {laser_gun_icon_data}; +const uint8_t* const health_icon_frames[] = {health_icon_data}; +const uint8_t* const ammo_icon_frames[] = {ammo_icon_data}; +const uint8_t* const team_red_icon_frames[] = {team_red_icon_data}; +const uint8_t* const team_blue_icon_frames[] = {team_blue_icon_data}; +const uint8_t* const game_over_icon_frames[] = {game_over_icon_data}; + +const Icon I_laser_gun_icon = { + .width = 16, + .height = 8, + .frame_count = 1, + .frame_rate = 0, + .frames = laser_gun_icon_frames, +}; + +const Icon I_health_icon = { + .width = 16, + .height = 8, + .frame_count = 1, + .frame_rate = 0, + .frames = health_icon_frames, +}; + +const Icon I_ammo_icon = { + .width = 16, + .height = 8, + .frame_count = 1, + .frame_rate = 0, + .frames = ammo_icon_frames, +}; + +const Icon I_team_red_icon = { + .width = 16, + .height = 8, + .frame_count = 1, + .frame_rate = 0, + .frames = team_red_icon_frames, +}; + +const Icon I_team_blue_icon = { + .width = 16, + .height = 8, + .frame_count = 1, + .frame_rate = 0, + .frames = team_blue_icon_frames, +}; + +const Icon I_game_over_icon = { + .width = 16, + .height = 8, + .frame_count = 1, + .frame_rate = 0, + .frames = game_over_icon_frames, +}; \ No newline at end of file diff --git a/laser_tag_icons.h b/laser_tag_icons.h new file mode 100644 index 00000000000..d1026367399 --- /dev/null +++ b/laser_tag_icons.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +extern const Icon I_laser_gun_icon; +extern const Icon I_health_icon; +extern const Icon I_ammo_icon; +extern const Icon I_team_red_icon; +extern const Icon I_team_blue_icon; +extern const Icon I_game_over_icon; + +#define LASER_GUN_ICON (&I_laser_gun_icon) +#define HEALTH_ICON (&I_health_icon) +#define AMMO_ICON (&I_ammo_icon) +#define TEAM_RED_ICON (&I_team_red_icon) +#define TEAM_BLUE_ICON (&I_team_blue_icon) +#define GAME_OVER_ICON (&I_game_over_icon) \ No newline at end of file diff --git a/laser_tag_view.c b/laser_tag_view.c new file mode 100644 index 00000000000..7c1ec65e53e --- /dev/null +++ b/laser_tag_view.c @@ -0,0 +1,91 @@ +#include "laser_tag_view.h" +#include "laser_tag_icons.h" +#include +#include + +struct LaserTagView { + View* view; +}; + +typedef struct { + LaserTagTeam team; + uint8_t health; + uint16_t ammo; + uint16_t score; + uint32_t game_time; + bool game_over; +} LaserTagViewModel; + +static void laser_tag_view_draw_callback(Canvas* canvas, void* model) { + LaserTagViewModel* m = model; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 0, 0, m->team == TeamRed ? TEAM_RED_ICON : TEAM_BLUE_ICON); + canvas_draw_icon(canvas, 0, 10, HEALTH_ICON); + canvas_draw_str_aligned(canvas, 20, 14, AlignLeft, AlignBottom, furi_string_get_cstr(furi_string_alloc_printf("%d", m->health))); + canvas_draw_icon(canvas, 0, 20, AMMO_ICON); + canvas_draw_str_aligned(canvas, 20, 24, AlignLeft, AlignBottom, furi_string_get_cstr(furi_string_alloc_printf("%d", m->ammo))); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignBottom, "Score:"); + canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignBottom, furi_string_get_cstr(furi_string_alloc_printf("%d", m->score))); + uint32_t minutes = m->game_time / 60; + uint32_t seconds = m->game_time % 60; + canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignBottom, furi_string_get_cstr(furi_string_alloc_printf("%02ld:%02ld", minutes, seconds))); + canvas_draw_icon(canvas, 112, 0, LASER_GUN_ICON); + + if(m->game_over) { + canvas_draw_icon(canvas, 56, 28, GAME_OVER_ICON); + } +} + +static bool laser_tag_view_input_callback(InputEvent* event, void* context) { + UNUSED(event); + UNUSED(context); + return false; +} + +LaserTagView* laser_tag_view_alloc() { + LaserTagView* laser_tag_view = malloc(sizeof(LaserTagView)); + laser_tag_view->view = view_alloc(); + view_set_context(laser_tag_view->view, laser_tag_view); + view_allocate_model(laser_tag_view->view, ViewModelTypeLocking, sizeof(LaserTagViewModel)); + view_set_draw_callback(laser_tag_view->view, laser_tag_view_draw_callback); + view_set_input_callback(laser_tag_view->view, laser_tag_view_input_callback); + return laser_tag_view; +} + +void laser_tag_view_draw(View* view, Canvas* canvas) { + LaserTagViewModel* model = view_get_model(view); + laser_tag_view_draw_callback(canvas, model); + view_commit_model(view, false); +} + +void laser_tag_view_free(LaserTagView* laser_tag_view) { + furi_assert(laser_tag_view); + view_free(laser_tag_view->view); + free(laser_tag_view); +} + +View* laser_tag_view_get_view(LaserTagView* laser_tag_view) { + furi_assert(laser_tag_view); + return laser_tag_view->view; +} + +void laser_tag_view_update(LaserTagView* laser_tag_view, GameState* game_state) { + furi_assert(laser_tag_view); + furi_assert(game_state); + + with_view_model( + laser_tag_view->view, + LaserTagViewModel * model, + { + model->team = game_state_get_team(game_state); + model->health = game_state_get_health(game_state); + model->ammo = game_state_get_ammo(game_state); + model->score = game_state_get_score(game_state); + model->game_time = game_state_get_time(game_state); + model->game_over = game_state_is_game_over(game_state); + }, + true); +} diff --git a/laser_tag_view.h b/laser_tag_view.h new file mode 100644 index 00000000000..f7079f49ad3 --- /dev/null +++ b/laser_tag_view.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include "game_state.h" + +typedef struct LaserTagView LaserTagView; + +LaserTagView* laser_tag_view_alloc(); + +void laser_tag_view_draw(View* view, Canvas* canvas); + +void laser_tag_view_free(LaserTagView* laser_tag_view); + +View* laser_tag_view_get_view(LaserTagView* laser_tag_view); + +void laser_tag_view_update(LaserTagView* laser_tag_view, GameState* game_state); \ No newline at end of file diff --git a/manifest.yml b/manifest.yml new file mode 100644 index 00000000000..f77eab31ded --- /dev/null +++ b/manifest.yml @@ -0,0 +1,19 @@ +# Manifest required for the Flipper Application Catalog +# https://github.com/flipperdevices/flipper-application-catalog/blob/main/documentation/Manifest.md +author: "@CodyTolene" +category: "Sub-GHz" +changelog: "@./docs/CHANGELOG.md" +description: "@./docs/README.md" +icon: "icons/toolkit.png" +id: "toolkit" +name: "Development Toolkit" +screenshots: + - "screenshots/todo.png" +short_description: "A Flipper Zero Development Toolkit for all to use!" +sourcecode: + location: + commit_sha: ... # Update this with the latest commit sha + origin: https://github.com/CodyTolene/Flipper-Zero-Development-Toolkit.git + subdir: src-fap + type: git +version: 1.0 diff --git a/screenshots/todo.png b/screenshots/todo.png new file mode 100644 index 00000000000..092b044d5e8 Binary files /dev/null and b/screenshots/todo.png differ