From 44763a368d8a6ae0ff7f91b4aee96626434de5a7 Mon Sep 17 00:00:00 2001 From: BeanGirlThing Date: Sun, 7 Jul 2024 21:11:58 +0100 Subject: [PATCH] Add bomb damage Add logic to kill player when they are within the explosion radius of a bomb when it detonates Add transmission of death signal and handling for reception of death signal Add snarky comment about argument order --- bomber_loop.c | 323 +++++++++++++++++++++++--------------------------- bomber_ui.c | 116 +++++++++--------- subghz.c | 80 ++++++++----- subghz.h | 1 + types.h | 88 ++++++++------ 5 files changed, 307 insertions(+), 301 deletions(-) diff --git a/bomber_loop.c b/bomber_loop.c index 922d452d0af..242bb0e3fdc 100644 --- a/bomber_loop.c +++ b/bomber_loop.c @@ -2,50 +2,43 @@ #include "helpers.h" #include "subghz.h" -#define BOMB_HOT_TIME furi_ms_to_ticks(2000) -#define BOMB_PLANTED_TIME furi_ms_to_ticks(2100) -#define BOMB_EXPLODE_TIME furi_ms_to_ticks(2500) -#define BOMB_RESET_TIME furi_ms_to_ticks(2600) -#define MAX_X 16 -#define MAX_Y 8 +#define BOMB_HOT_TIME furi_ms_to_ticks(2000) +#define BOMB_PLANTED_TIME furi_ms_to_ticks(2100) +#define BOMB_EXPLODE_TIME furi_ms_to_ticks(2500) +#define BOMB_RESET_TIME furi_ms_to_ticks(2600) +#define MAX_X 16 +#define MAX_Y 8 // End the game but don't quit the app // TODO: Figure out whether this actually needs to be here. What should happen when the player presses Back during gameplay? -static void bomber_app_stop_playing(BomberAppState* state) -{ +static void bomber_app_stop_playing(BomberAppState* state) { FURI_LOG_I(TAG, "Stop playing"); bomber_app_set_mode(state, BomberAppMode_Finished); } // Quit the app -static void bomber_app_quit(BomberAppState* state) -{ +static void bomber_app_quit(BomberAppState* state) { FURI_LOG_I(TAG, "Quitting"); bomber_app_set_mode(state, BomberAppMode_Quit); } // Put the game in error state -static void bomber_app_error(BomberAppState* state) -{ +static void bomber_app_error(BomberAppState* state) { FURI_LOG_E(TAG, "Error occurred"); bomber_app_set_mode(state, BomberAppMode_Error); } // Start playing -static void bomber_app_start(BomberAppState* state) -{ +static void bomber_app_start(BomberAppState* state) { FURI_LOG_I(TAG, "Start playing"); bomber_app_set_mode(state, BomberAppMode_Playing); } // Check if a particular coordingate is occupied by a players active bomb -static bool is_occupied_by_bomb(Player* player, uint8_t x, uint8_t y) -{ - for(int i = 0; i < MAX_BOMBS; i++) - { +static bool is_occupied_by_bomb(Player* player, uint8_t x, uint8_t y) { + for(int i = 0; i < MAX_BOMBS; i++) { Bomb bomb = player->bombs[i]; - if (bomb.state != BombState_None && bomb.x == x && bomb.y == y) - { + if(bomb.state != BombState_None && bomb.x == x && bomb.y == y) { return true; } } @@ -57,45 +50,40 @@ static bool is_occupied_by_bomb(Player* player, uint8_t x, uint8_t y) // state: Pointer to the application state // input: Represents the input event // returns: true if the viewport should be updated, else false -static bool handle_game_direction(BomberAppState* state, InputEvent input) -{ +static bool handle_game_direction(BomberAppState* state, InputEvent input) { Player* player = get_player(state); - Point newPoint = { player->x, player->y }; - - switch(input.key) - { - case InputKeyUp: - if (player->y == 0) return false; - newPoint.y -= 1; - break; - case InputKeyDown: - if (player->y >= 7) return false; - newPoint.y += 1; - break; - case InputKeyLeft: - if (player->x == 0) return false; - newPoint.x -= 1; - break; - case InputKeyRight: - if (player->x >= 15) return false; - newPoint.x += 1; - break; - default: - return false; + Point newPoint = {player->x, player->y}; + + switch(input.key) { + case InputKeyUp: + if(player->y == 0) return false; + newPoint.y -= 1; + break; + case InputKeyDown: + if(player->y >= 7) return false; + newPoint.y += 1; + break; + case InputKeyLeft: + if(player->x == 0) return false; + newPoint.x -= 1; + break; + case InputKeyRight: + if(player->x >= 15) return false; + newPoint.x += 1; + break; + default: + return false; } // Only allow move to new position if the block at that position is not occupied BlockType block = (BlockType)(state->level)[ix(newPoint.x, newPoint.y)]; - if (block != BlockType_Empty) - { + if(block != BlockType_Empty) { return false; } - if ( - is_occupied_by_bomb(&state->fox, newPoint.x, newPoint.y) || - is_occupied_by_bomb(&state->wolf, newPoint.x, newPoint.y)) - { + if(is_occupied_by_bomb(&state->fox, newPoint.x, newPoint.y) || + is_occupied_by_bomb(&state->wolf, newPoint.x, newPoint.y)) { return false; } @@ -111,23 +99,20 @@ static bool handle_game_direction(BomberAppState* state, InputEvent input) // state: Pointer to the application state // input: Represents the input event // returns: true if the viewport should be updated, else false -static bool handle_menu_input(BomberAppState* state, InputEvent input) -{ - if (input.type == InputTypeShort) - { - switch(input.key) - { - case InputKeyUp: - case InputKeyDown: - case InputKeyLeft: - case InputKeyRight: - state->isPlayerTwo = !state->isPlayerTwo; - return true; - case InputKeyOk: - bomber_app_start(state); - return true; - default: - return false; +static bool handle_menu_input(BomberAppState* state, InputEvent input) { + if(input.type == InputTypeShort) { + switch(input.key) { + case InputKeyUp: + case InputKeyDown: + case InputKeyLeft: + case InputKeyRight: + state->isPlayerTwo = !state->isPlayerTwo; + return true; + case InputKeyOk: + bomber_app_start(state); + return true; + default: + return false; } } return false; @@ -137,43 +122,39 @@ static bool handle_menu_input(BomberAppState* state, InputEvent input) // state: Pointer to the application state // input: Represents the input event // returns: true if the viewport should be updated, else false -static bool handle_game_input(BomberAppState* state, InputEvent input) -{ +static bool handle_game_input(BomberAppState* state, InputEvent input) { Player* player = get_player(state); - if(input.type == InputTypeShort) - { - switch(input.key) - { - case InputKeyOk: - FURI_LOG_I(TAG, "Drop Bomb"); - - Bomb bomb; - bomb.x = player->x; - bomb.y = player->y; - bomb.state = BombState_Planted; - bomb.planted = furi_get_tick(); - player->bombs[player->bomb_ix] = bomb; + if(input.type == InputTypeShort) { + switch(input.key) { + case InputKeyOk: + FURI_LOG_I(TAG, "Drop Bomb"); - player->bomb_ix = (player->bomb_ix + 1) % 10; + Bomb bomb; + bomb.x = player->x; + bomb.y = player->y; + bomb.state = BombState_Planted; + bomb.planted = furi_get_tick(); + player->bombs[player->bomb_ix] = bomb; - tx_bomb_placement(state, bomb.x, bomb.y); + player->bomb_ix = (player->bomb_ix + 1) % 10; + tx_bomb_placement(state, bomb.x, bomb.y); + + return true; + case InputKeyUp: + case InputKeyDown: + case InputKeyLeft: + case InputKeyRight: + return handle_game_direction(state, input); + case InputKeyBack: + if(state->mode == BomberAppMode_Playing) { + bomber_app_stop_playing(state); return true; - case InputKeyUp: - case InputKeyDown: - case InputKeyLeft: - case InputKeyRight: - return handle_game_direction(state, input); - case InputKeyBack: - if(state->mode == BomberAppMode_Playing) - { - bomber_app_stop_playing(state); - return true; - } - break; - default: - break; + } + break; + default: + break; } } @@ -184,22 +165,19 @@ static bool handle_game_input(BomberAppState* state, InputEvent input) // state: Pointer to the application state // input: Represents the input event // returns: true if the viewport should be updated, else false -static bool bomber_app_handle_input(BomberAppState* state, InputEvent input) -{ - if(input.type == InputTypeLong && input.key == InputKeyBack) - { +static bool bomber_app_handle_input(BomberAppState* state, InputEvent input) { + if(input.type == InputTypeLong && input.key == InputKeyBack) { bomber_app_quit(state); return false; // don't try to update the UI while quitting } - switch (state->mode) - { - case BomberAppMode_Playing: - return handle_game_input(state, input); - case BomberAppMode_Menu: - return handle_menu_input(state, input); - default: - break; + switch(state->mode) { + case BomberAppMode_Playing: + return handle_game_input(state, input); + case BomberAppMode_Menu: + return handle_menu_input(state, input); + default: + break; } return false; @@ -207,8 +185,7 @@ static bool bomber_app_handle_input(BomberAppState* state, InputEvent input) // Application main loop // state: Pointer to the application state -void bomber_main_loop(BomberAppState* state) -{ +void bomber_main_loop(BomberAppState* state) { FURI_LOG_I(TAG, "Begin application main loop"); view_port_update(state->view_port); @@ -216,105 +193,100 @@ void bomber_main_loop(BomberAppState* state) state->running = true; - while(state->running) - { + while(state->running) { subghz_check_incoming(state); - switch(furi_message_queue_get(state->queue, &event, LOOP_MESSAGE_TIMEOUT_ms)) - { - case FuriStatusOk: - FURI_LOG_I(TAG, "Event from queue: %d", event.type); - bool updated = false; - switch(event.type) - { - case BomberEventType_Input: - updated = bomber_app_handle_input(state, event.input); - break; - default: - FURI_LOG_E(TAG, "Unknown event received from queue."); - break; - } - - if(updated) - { - view_port_update(state->view_port); - } - break; - - // error cases - case FuriStatusErrorTimeout: - FURI_LOG_D(TAG, "furi_message_queue_get timed out"); + switch(furi_message_queue_get(state->queue, &event, LOOP_MESSAGE_TIMEOUT_ms)) { + case FuriStatusOk: + FURI_LOG_I(TAG, "Event from queue: %d", event.type); + bool updated = false; + switch(event.type) { + case BomberEventType_Input: + updated = bomber_app_handle_input(state, event.input); break; default: - FURI_LOG_E(TAG, "furi_message_queue_get was neither ok nor timeout"); - bomber_app_error(state); + FURI_LOG_E(TAG, "Unknown event received from queue."); + break; + } + + if(updated) { + view_port_update(state->view_port); + } + break; + + // error cases + case FuriStatusErrorTimeout: + FURI_LOG_D(TAG, "furi_message_queue_get timed out"); + break; + default: + FURI_LOG_E(TAG, "furi_message_queue_get was neither ok nor timeout"); + bomber_app_error(state); } - if(state->mode == BomberAppMode_Quit || state->mode == BomberAppMode_Error) - { + if(state->mode == BomberAppMode_Quit || state->mode == BomberAppMode_Error) { state->running = false; } } } -static bool destroy_block(BomberAppState* state, uint8_t x, uint8_t y) -{ +static bool handle_explosion(BomberAppState* state, uint8_t x, uint8_t y) { // Out of bounds. // No need to check negatives as uint8_t is unsigned and will underflow, resulting in a value way over MAX_X and MAX_Y. - if (x >= MAX_X || y >= MAX_Y) - { + if(x >= MAX_X || y >= MAX_Y) { return false; } - // TODO: Check for player hit + Player* player = get_player(state); + if(player->x == x && player->y == y) { + tx_death(state); + bomber_app_set_mode(state, BomberAppMode_GameOver); + if(state->isPlayerTwo) { + state->dead = WhoDied_Wolf; + } else { + state->dead = WhoDied_Fox; + } + } - switch(state->level[ix(x, y)]) - { - case BlockType_Brick: - state->level[ix(x, y)] = BlockType_Empty; - return true; - default: - return false; + switch(state->level[ix(x, y)]) { + case BlockType_Brick: + state->level[ix(x, y)] = BlockType_Empty; + return true; + default: + return false; } } -static bool update_bombs(Player* player, BomberAppState* state) -{ +static bool update_bombs(Player* player, BomberAppState* state) { bool changed = false; - for (uint8_t i = 0; i < MAX_BOMBS; i++) - { + for(uint8_t i = 0; i < MAX_BOMBS; i++) { Bomb* bomb = &player->bombs[i]; - if (bomb->state != BombState_None) - { + if(bomb->state != BombState_None) { uint32_t time = furi_get_tick() - bomb->planted; - if (time > BOMB_RESET_TIME) - { + if(time > BOMB_RESET_TIME) { bomb->planted = 0; bomb->state = BombState_None; continue; } - if (time > BOMB_EXPLODE_TIME) - { + if(time > BOMB_EXPLODE_TIME) { bomb->state = BombState_Explode; - for (uint8_t j = 0; j < player->bomb_power + 1; j++) - { - changed &= destroy_block(state, bomb->x - j, bomb->y); - changed &= destroy_block(state, bomb->x + j, bomb->y); - changed &= destroy_block(state, bomb->x, bomb->y + j); - changed &= destroy_block(state, bomb->x, bomb->y - j); + for(uint8_t j = 0; j < player->bomb_power + 1; j++) { + changed &= handle_explosion(state, bomb->x - j, bomb->y); + changed &= handle_explosion(state, bomb->x + j, bomb->y); + changed &= handle_explosion(state, bomb->x, bomb->y + j); + changed &= handle_explosion(state, bomb->x, bomb->y - j); } continue; } - if (time > BOMB_PLANTED_TIME) { + if(time > BOMB_PLANTED_TIME) { bomb->state = BombState_Planted; - } else if (time > BOMB_HOT_TIME) { + } else if(time > BOMB_HOT_TIME) { bomb->state = BombState_Hot; } } @@ -323,15 +295,12 @@ static bool update_bombs(Player* player, BomberAppState* state) return changed; } -void bomber_game_tick(BomberAppState* state) -{ +void bomber_game_tick(BomberAppState* state) { bool changed = false; changed &= update_bombs(&state->fox, state); changed &= update_bombs(&state->wolf, state); - if (changed) - { + if(changed) { view_port_update(state->view_port); } } - diff --git a/bomber_ui.c b/bomber_ui.c index 593c9797e4f..4504826d80e 100644 --- a/bomber_ui.c +++ b/bomber_ui.c @@ -3,65 +3,54 @@ #include "helpers.h" // Draws a single bomb based on its state -static void draw_bomb(Canvas* canvas, Bomb bomb) -{ - switch(bomb.state) - { - case BombState_Planted: - canvas_draw_xbm(canvas, bomb.x * 8, bomb.y * 8, 8, 8, bomb_glyph); - break; - case BombState_Hot: - canvas_draw_xbm(canvas, bomb.x * 8, bomb.y * 8, 8, 8, bomb_flash); - break; - case BombState_Explode: - canvas_draw_xbm(canvas, bomb.x * 8, bomb.y * 8, 8, 8, bomb_explode); - break; - default: - break; +static void draw_bomb(Canvas* canvas, Bomb bomb) { + switch(bomb.state) { + case BombState_Planted: + canvas_draw_xbm(canvas, bomb.x * 8, bomb.y * 8, 8, 8, bomb_glyph); + break; + case BombState_Hot: + canvas_draw_xbm(canvas, bomb.x * 8, bomb.y * 8, 8, 8, bomb_flash); + break; + case BombState_Explode: + canvas_draw_xbm(canvas, bomb.x * 8, bomb.y * 8, 8, 8, bomb_explode); + break; + default: + break; } } // Draws a single player -static void draw_player(Canvas* canvas, int x, int y, const uint8_t* glyph) -{ +static void draw_player(Canvas* canvas, int x, int y, const uint8_t* glyph) { canvas_draw_xbm(canvas, x * 8, y * 8, 8, 8, glyph); } // Draws a single block -static void draw_block(Canvas* canvas, int x, int y, BlockType block) -{ - switch(block) - { - case BlockType_Brick: - canvas_draw_xbm(canvas, x * 8, y * 8, 8, 8, brick_glyph); - break; - case BlockType_Empty: - default: - break; +static void draw_block(Canvas* canvas, int x, int y, BlockType block) { + switch(block) { + case BlockType_Brick: + canvas_draw_xbm(canvas, x * 8, y * 8, 8, 8, brick_glyph); + break; + case BlockType_Empty: + default: + break; } } // Renders the game to the viewport - called while playing -static void render_game(Canvas* canvas, BomberAppState* state) -{ +static void render_game(Canvas* canvas, BomberAppState* state) { // Draw bombs - for(int i = 0; i < MAX_BOMBS; i++) - { + for(int i = 0; i < MAX_BOMBS; i++) { draw_bomb(canvas, state->fox.bombs[i]); draw_bomb(canvas, state->wolf.bombs[i]); } // Draw players and blocks - for(int x = 0; x < 16; x++) - { - for(int y = 0; y < 8; y++) - { - if(x == state->fox.x && y == state->fox.y) - { + for(int x = 0; x < 16; x++) { + for(int y = 0; y < 8; y++) { + if(x == state->fox.x && y == state->fox.y) { draw_player(canvas, x, y, fox_glyph); } - if(x == state->wolf.x && y == state->wolf.y) - { + if(x == state->wolf.x && y == state->wolf.y) { draw_player(canvas, x, y, wolf_glyph); } @@ -72,8 +61,7 @@ static void render_game(Canvas* canvas, BomberAppState* state) } // Renders the menu to the viewport - called while in the menu -static void render_menu(Canvas* canvas, BomberAppState* state) -{ +static void render_menu(Canvas* canvas, BomberAppState* state) { uint8_t ax = state->isPlayerTwo ? 70 : 20; canvas_set_font(canvas, FontSecondary); @@ -86,46 +74,54 @@ static void render_menu(Canvas* canvas, BomberAppState* state) canvas_draw_xbm(canvas, ax, 52, 4, 7, select_glyph); } +static void render_game_over(Canvas* canvas, BomberAppState* state) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 15, 14, "Game Over!"); + if(state->dead == WhoDied_Fox) { + canvas_draw_str(canvas, 15, 30, "Wolf Wins!"); + } else { + canvas_draw_str(canvas, 15, 30, "Fox Wins!"); + } +} + // Main callback that starts off rendering -void bomber_ui_render_callback(Canvas* canvas, void* context) -{ +void bomber_ui_render_callback(Canvas* canvas, void* context) { furi_assert(context); BomberAppState* state = context; - if(furi_mutex_acquire(state->data_mutex, 200) != FuriStatusOk) - { + if(furi_mutex_acquire(state->data_mutex, 200) != FuriStatusOk) { FURI_LOG_W(TAG, "Failed to acquire mutex in render callback"); return; } canvas_set_bitmap_mode(canvas, true); - switch(state->mode) - { - case BomberAppMode_Playing: - render_game(canvas, state); - break; - case BomberAppMode_Menu: - render_menu(canvas, state); - break; - default: - break; + switch(state->mode) { + case BomberAppMode_Playing: + render_game(canvas, state); + break; + case BomberAppMode_Menu: + render_menu(canvas, state); + break; + case BomberAppMode_GameOver: + render_game_over(canvas, state); + break; + default: + break; } - + furi_mutex_release(state->data_mutex); } // Main callback that handles input and puts it on the queue -void bomber_ui_input_callback(InputEvent* input_event, void* context_q) -{ +void bomber_ui_input_callback(InputEvent* input_event, void* context_q) { FURI_LOG_T(TAG, "bomber_ui_input_callback"); furi_assert(context_q); FuriMessageQueue* queue = context_q; BomberEvent event = {.type = BomberEventType_Input, .input = *input_event}; - if(furi_message_queue_put(queue, &event, FuriWaitForever) != FuriStatusOk) - { + if(furi_message_queue_put(queue, &event, FuriWaitForever) != FuriStatusOk) { FURI_LOG_W(TAG, "Failed to put input event in message queue"); } } diff --git a/subghz.c b/subghz.c index a5399e0d966..5baaaa33d3d 100644 --- a/subghz.c +++ b/subghz.c @@ -1,8 +1,10 @@ #include "subghz.h" #include "types.h" +#include "helpers.h" #define ACTION_MOVE 0x01 #define ACTION_BOMB 0x02 +#define ACTION_DEATH 0x03 #define PLAYER_TWO 0x10 @@ -10,14 +12,14 @@ // player: Pointer to the current player structure // state: Pointer to the game state void tx_new_position(Player* player, BomberAppState* state) +// Hot Take: App state should always be first { furi_assert(state); furi_assert(player); // First hex digit of 1st byte is character (0 = Fox, 1 = Wolf) uint8_t action = ACTION_MOVE; - if(state->isPlayerTwo) - { + if(state->isPlayerTwo) { action = action | PLAYER_TWO; } @@ -26,15 +28,14 @@ void tx_new_position(Player* player, BomberAppState* state) state->tx_buffer[2] = player->y; // Transmit the buffer - FURI_LOG_I(TAG, "Transmitting new position: action=0x%02X, x=%d, y=%d", action, player->x, player->y); + FURI_LOG_I( + TAG, "Transmitting new position: action=0x%02X, x=%d, y=%d", action, player->x, player->y); subghz_tx_rx_worker_write(state->subghz_worker, state->tx_buffer, RX_TX_BUFFER_SIZE); } -void tx_bomb_placement(BomberAppState* state, uint8_t x, uint8_t y) -{ +void tx_bomb_placement(BomberAppState* state, uint8_t x, uint8_t y) { uint8_t action = ACTION_BOMB; - if(state->isPlayerTwo) - { + if(state->isPlayerTwo) { action = action | PLAYER_TWO; } @@ -47,16 +48,28 @@ void tx_bomb_placement(BomberAppState* state, uint8_t x, uint8_t y) subghz_tx_rx_worker_write(state->subghz_worker, state->tx_buffer, RX_TX_BUFFER_SIZE); } +void tx_death(BomberAppState* state) { + uint8_t action = ACTION_DEATH; + if(state->isPlayerTwo) { + action = action | PLAYER_TWO; + } + + state->tx_buffer[0] = action; + state->tx_buffer[1] = 0x00; + state->tx_buffer[2] = 0x00; + + FURI_LOG_I(TAG, "Transmitting death: action=0x%02X", action); + subghz_tx_rx_worker_write(state->subghz_worker, state->tx_buffer, RX_TX_BUFFER_SIZE); +} + // Handle the buffer once data receive has completed // state: Pointer to the application state // rx_size: Number of bytes of data received -static void post_rx(BomberAppState* state, size_t rx_size) -{ +static void post_rx(BomberAppState* state, size_t rx_size) { furi_assert(state); furi_assert(rx_size); - if(rx_size == 0) - { + if(rx_size == 0) { FURI_LOG_W(TAG, "Received data size is 0, ignoring"); return; } @@ -64,30 +77,42 @@ static void post_rx(BomberAppState* state, size_t rx_size) // Ensure received size is within buffer limits furi_check(rx_size <= RX_TX_BUFFER_SIZE); FURI_LOG_T(TAG, "Received data size: %zu", rx_size); - FURI_LOG_D(TAG, "Received data: 0x%02X 0x%12X 0x%22X", state->rx_buffer[0], state->rx_buffer[1], state->rx_buffer[2]); + FURI_LOG_D( + TAG, + "Received data: 0x%02X 0x%12X 0x%22X", + state->rx_buffer[0], + state->rx_buffer[1], + state->rx_buffer[2]); Player* player = &state->fox; - if (state->rx_buffer[0] >= PLAYER_TWO) - { + if(state->rx_buffer[0] >= PLAYER_TWO) { player = &state->wolf; state->rx_buffer[0] -= PLAYER_TWO; } - if (state->rx_buffer[0] == ACTION_MOVE) - { + switch(state->rx_buffer[0]) { + case ACTION_MOVE: player->x = state->rx_buffer[1]; player->y = state->rx_buffer[2]; - } - else if (state->rx_buffer[0] == ACTION_BOMB) - { + break; + case ACTION_BOMB: FURI_LOG_T(TAG, "Hostile bomb at index %zu", player->bomb_ix); player->bombs[player->bomb_ix].x = state->rx_buffer[1]; player->bombs[player->bomb_ix].y = state->rx_buffer[2]; - + player->bombs[player->bomb_ix].planted = furi_get_tick(); player->bombs[player->bomb_ix].state = BombState_Planted; player->bomb_ix = (player->bomb_ix + 1) % 10; + break; + case ACTION_DEATH: + bomber_app_set_mode(state, BomberAppMode_GameOver); + if(state->isPlayerTwo) { + state->dead = WhoDied_Fox; + } else { + state->dead = WhoDied_Wolf; + } + break; } view_port_update(state->view_port); @@ -95,30 +120,27 @@ static void post_rx(BomberAppState* state, size_t rx_size) // Handle incoming subghz data // state: Pointer to the application state -void subghz_check_incoming(BomberAppState* state) -{ +void subghz_check_incoming(BomberAppState* state) { FURI_LOG_T(TAG, "subghz_check_incoming"); size_t avail = 0; - while((avail = subghz_tx_rx_worker_available(state->subghz_worker)) > 0) - { + while((avail = subghz_tx_rx_worker_available(state->subghz_worker)) > 0) { FURI_LOG_D(TAG, "Received data size: %zu", avail); uint32_t since_last_rx = furi_get_tick() - state->last_time_rx_data; - if(avail < RX_TX_BUFFER_SIZE && since_last_rx < MESSAGE_COMPLETION_TIMEOUT) - { + if(avail < RX_TX_BUFFER_SIZE && since_last_rx < MESSAGE_COMPLETION_TIMEOUT) { break; } - size_t rx_size = subghz_tx_rx_worker_read(state->subghz_worker, state->rx_buffer, RX_TX_BUFFER_SIZE); + size_t rx_size = + subghz_tx_rx_worker_read(state->subghz_worker, state->rx_buffer, RX_TX_BUFFER_SIZE); post_rx(state, rx_size); } } /* Callback for RX events from the Sub-GHz worker. Records the current ticks as * the time of the last reception. */ -void have_read_cb(void* context) -{ +void have_read_cb(void* context) { furi_assert(context); BomberAppState* state = context; diff --git a/subghz.h b/subghz.h index 5e943944e9c..044877c1632 100644 --- a/subghz.h +++ b/subghz.h @@ -7,6 +7,7 @@ void tx_new_position(Player* player, BomberAppState* state); void tx_bomb_placement(BomberAppState* state, uint8_t x, uint8_t y); +void tx_death(BomberAppState* state); void subghz_check_incoming(BomberAppState* state); void have_read_cb(void* context); diff --git a/types.h b/types.h index 346fed5e270..fc03b79e69c 100644 --- a/types.h +++ b/types.h @@ -10,22 +10,35 @@ #define TAG "bomb" #define LOOP_MESSAGE_TIMEOUT_ms 500 -#define DEFAULT_FREQ 433920000 -#define RX_TX_BUFFER_SIZE 3 -#define MAX_BOMBS 10 +#define DEFAULT_FREQ 433920000 +#define RX_TX_BUFFER_SIZE 3 +#define MAX_BOMBS 10 // Graphics -static const uint8_t brick_glyph[] = { 0xff, 0x11, 0xff, 0x88, 0xff, 0x11, 0xff, 0x88 }; -static const uint8_t fox_glyph[] = { 0x81, 0xc3, 0xbd, 0x81, 0x99, 0x42, 0x24, 0x18 }; -static const uint8_t wolf_glyph[] = { 0x81, 0xc3, 0xff, 0xff, 0xe7, 0x7e, 0x3c, 0x18 }; -static const uint8_t bomb_glyph[] = { 0x20, 0x10, 0x08, 0x1e, 0x3f, 0x27, 0x37, 0x1e }; -static const uint8_t bomb_flash[] = { 0x20, 0x10, 0x08, 0x1e, 0x21, 0x21, 0x21, 0x1e }; -static const uint8_t bomb_explode[] = { 0x30, 0x4b, 0x8d, 0x61, 0x22, 0x91, 0xaa, 0xcc }; -static const uint8_t select_glyph[] = { 0x01, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01 }; - -static const uint8_t fox[] = {0x08,0x00,0x01,0x00,0x14,0x80,0x02,0x00,0x24,0x4f,0x02,0x00,0xc4,0x30,0x02,0x00,0x04,0x00,0x02,0x00,0x14,0x80,0x02,0x00,0x0c,0x00,0x03,0x00,0xc4,0x30,0x02,0x00,0x42,0x20,0x04,0x00,0xc2,0x30,0x04,0x00,0x01,0x06,0x08,0x00,0x06,0x00,0xe6,0x01,0x18,0x80,0x11,0x01,0xe0,0x70,0x10,0x01,0x20,0x4f,0x08,0x01,0x20,0x40,0x08,0x01,0x10,0x80,0x84,0x00,0x10,0x80,0x84,0x00,0x50,0xa0,0x42,0x00,0x50,0xa0,0x22,0x00,0x48,0x29,0x11,0x00,0x48,0x29,0x09,0x00,0x48,0x29,0x07,0x00,0x48,0x26,0x01,0x00,0xf0,0xf9,0x00,0x00}; -static const uint8_t wolf[] = {0x08,0x00,0x01,0x00,0x14,0x80,0x02,0x00,0x24,0x4f,0x02,0x00,0xc2,0x30,0x04,0x00,0x02,0x00,0x04,0x00,0x12,0x80,0x04,0x00,0x0a,0x00,0x05,0x00,0x44,0x20,0x02,0x00,0x82,0x10,0x04,0x00,0xc2,0x30,0x04,0x00,0x01,0x00,0x08,0x00,0x06,0x06,0xe6,0x01,0x18,0x80,0x11,0x01,0xe0,0x70,0x10,0x01,0x20,0x4f,0x08,0x01,0x20,0x40,0x08,0x01,0x10,0x80,0x84,0x00,0x10,0x80,0x84,0x00,0x50,0xa0,0x42,0x00,0x50,0xa0,0x22,0x00,0x48,0x29,0x11,0x00,0x48,0x29,0x09,0x00,0x48,0x29,0x07,0x00,0x48,0x26,0x01,0x00,0xf0,0xf9,0x00,0x00}; - +static const uint8_t brick_glyph[] = {0xff, 0x11, 0xff, 0x88, 0xff, 0x11, 0xff, 0x88}; +static const uint8_t fox_glyph[] = {0x81, 0xc3, 0xbd, 0x81, 0x99, 0x42, 0x24, 0x18}; +static const uint8_t wolf_glyph[] = {0x81, 0xc3, 0xff, 0xff, 0xe7, 0x7e, 0x3c, 0x18}; +static const uint8_t bomb_glyph[] = {0x20, 0x10, 0x08, 0x1e, 0x3f, 0x27, 0x37, 0x1e}; +static const uint8_t bomb_flash[] = {0x20, 0x10, 0x08, 0x1e, 0x21, 0x21, 0x21, 0x1e}; +static const uint8_t bomb_explode[] = {0x30, 0x4b, 0x8d, 0x61, 0x22, 0x91, 0xaa, 0xcc}; +static const uint8_t select_glyph[] = {0x01, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01}; + +static const uint8_t fox[] = { + 0x08, 0x00, 0x01, 0x00, 0x14, 0x80, 0x02, 0x00, 0x24, 0x4f, 0x02, 0x00, 0xc4, 0x30, 0x02, + 0x00, 0x04, 0x00, 0x02, 0x00, 0x14, 0x80, 0x02, 0x00, 0x0c, 0x00, 0x03, 0x00, 0xc4, 0x30, + 0x02, 0x00, 0x42, 0x20, 0x04, 0x00, 0xc2, 0x30, 0x04, 0x00, 0x01, 0x06, 0x08, 0x00, 0x06, + 0x00, 0xe6, 0x01, 0x18, 0x80, 0x11, 0x01, 0xe0, 0x70, 0x10, 0x01, 0x20, 0x4f, 0x08, 0x01, + 0x20, 0x40, 0x08, 0x01, 0x10, 0x80, 0x84, 0x00, 0x10, 0x80, 0x84, 0x00, 0x50, 0xa0, 0x42, + 0x00, 0x50, 0xa0, 0x22, 0x00, 0x48, 0x29, 0x11, 0x00, 0x48, 0x29, 0x09, 0x00, 0x48, 0x29, + 0x07, 0x00, 0x48, 0x26, 0x01, 0x00, 0xf0, 0xf9, 0x00, 0x00}; +static const uint8_t wolf[] = { + 0x08, 0x00, 0x01, 0x00, 0x14, 0x80, 0x02, 0x00, 0x24, 0x4f, 0x02, 0x00, 0xc2, 0x30, 0x04, + 0x00, 0x02, 0x00, 0x04, 0x00, 0x12, 0x80, 0x04, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x44, 0x20, + 0x02, 0x00, 0x82, 0x10, 0x04, 0x00, 0xc2, 0x30, 0x04, 0x00, 0x01, 0x00, 0x08, 0x00, 0x06, + 0x06, 0xe6, 0x01, 0x18, 0x80, 0x11, 0x01, 0xe0, 0x70, 0x10, 0x01, 0x20, 0x4f, 0x08, 0x01, + 0x20, 0x40, 0x08, 0x01, 0x10, 0x80, 0x84, 0x00, 0x10, 0x80, 0x84, 0x00, 0x50, 0xa0, 0x42, + 0x00, 0x50, 0xa0, 0x22, 0x00, 0x48, 0x29, 0x11, 0x00, 0x48, 0x29, 0x09, 0x00, 0x48, 0x29, + 0x07, 0x00, 0x48, 0x26, 0x01, 0x00, 0xf0, 0xf9, 0x00, 0x00}; // A level is basically a 2D array of blocks of one of the following types typedef enum BlockType { @@ -40,10 +53,10 @@ typedef enum BlockType { // Possible states a bomb can be in typedef enum BombState { - BombState_None = 0, // Bomb not planted yet - BombState_Planted = 1, // Bomb has been planted - BombState_Hot = 2, // Bomb is about to be detonated - BombState_Explode = 3 // Bomb has exploded + BombState_None = 0, // Bomb not planted yet + BombState_Planted = 1, // Bomb has been planted + BombState_Hot = 2, // Bomb is about to be detonated + BombState_Explode = 3 // Bomb has exploded } BombState; typedef struct { @@ -62,10 +75,10 @@ typedef struct { typedef struct { uint8_t x; uint8_t y; - Bomb bombs[MAX_BOMBS]; // Array of bombs - uint8_t bomb_power; // Number of blocks a bomb will destroy - uint8_t bomb_ix; // Index of currently held bomb - uint8_t bomb_count; // How many bombs the player can use + Bomb bombs[MAX_BOMBS]; // Array of bombs + uint8_t bomb_power; // Number of blocks a bomb will destroy + uint8_t bomb_ix; // Index of currently held bomb + uint8_t bomb_count; // How many bombs the player can use } Player; // Application mode. Rendering and input handlers rely on this to know what to render, or how to handle input. @@ -75,6 +88,7 @@ typedef enum { BomberAppMode_Ready, BomberAppMode_Playing, BomberAppMode_Finished, + BomberAppMode_GameOver, BomberAppMode_Error, BomberAppMode_Quit } BomberAppMode; @@ -92,27 +106,31 @@ typedef struct { InputEvent input; } BomberEvent; +typedef enum { WhoDied_None, WhoDied_Fox, WhoDied_Wolf } WhoDied; + typedef struct { - FuriMessageQueue* queue; // Message queue - FuriMutex* data_mutex; // Mutex - ViewPort* view_port; // Viewport - Gui* gui; // GUI - NotificationApp* notification; + FuriMessageQueue* queue; // Message queue + FuriMutex* data_mutex; // Mutex + ViewPort* view_port; // Viewport + Gui* gui; // GUI + NotificationApp* notification; FuriTimer* timer; BomberAppMode mode; - uint8_t* level; // Pointer to the current level array - bool running; - Player fox; // Position of the fox - Player wolf; // Position of the wolf + uint8_t* level; // Pointer to the current level array + bool running; + Player fox; // Position of the fox + Player wolf; // Position of the wolf bool isPlayerTwo; + WhoDied dead; + // for Sub-GHz - uint32_t frequency; - SubGhzTxRxWorker *subghz_worker; - const SubGhzDevice *subghz_device; + uint32_t frequency; + SubGhzTxRxWorker* subghz_worker; + const SubGhzDevice* subghz_device; volatile uint32_t last_time_rx_data; uint8_t rx_buffer[RX_TX_BUFFER_SIZE]; - uint8_t tx_buffer[RX_TX_BUFFER_SIZE]; + uint8_t tx_buffer[RX_TX_BUFFER_SIZE]; } BomberAppState; #endif \ No newline at end of file