Skip to content

Commit

Permalink
Merge pull request #24 from ashleyhuxley/fen/level-select
Browse files Browse the repository at this point in the history
feat: level select and tx level data to player 2
  • Loading branch information
ashleyhuxley authored Aug 27, 2024
2 parents 1ab9062 + 756bde7 commit 081aa25
Show file tree
Hide file tree
Showing 10 changed files with 275 additions and 38 deletions.
32 changes: 3 additions & 29 deletions bomber.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,37 +181,11 @@ int32_t bomber_main(void* p)
return 1;
}

state->selectedLevel = 0;
state->rxMode = RxMode_Command;

bomber_app_set_mode(state, BomberAppMode_PlayerSelect);

// TODO: This should be moved to after the Player Select menu
state->level = level1;
uint8_t wall_count = count_walls(state->level);
uint8_t powerup_bomb_count = (uint8_t)round((POWERUP_EXTRABOMB_RATIO * wall_count));
uint8_t powerup_power_count = (uint8_t)round((POWERUP_BOMBPOWER_RATIO * wall_count));
FURI_LOG_D(TAG, "Walls: %d, Extra Bombs: %d, Bomb Power: %d", wall_count, powerup_bomb_count, powerup_power_count);

uint8_t* bomb_powerups = malloc(sizeof(uint8_t) * powerup_bomb_count);
uint8_t* power_powerups = malloc(sizeof(uint8_t) * powerup_power_count);

get_random_powerup_locations(state->level, powerup_bomb_count, bomb_powerups);
get_random_powerup_locations(state->level, powerup_power_count, power_powerups);

for (uint8_t i = 0; i < powerup_bomb_count; i++) {
state->level[bomb_powerups[i]] = BlockType_PuExtraBomb_Hidden;
}
for (uint8_t i = 0; i < powerup_power_count; i++) {
state->level[power_powerups[i]] = BlockType_PuBombStrength_Hidden;
}

free(bomb_powerups);
free(power_powerups);

// Figure out player starting positions from level data
state->fox = bomber_app_get_block(state->level, BlockType_Fox);
state->level[ix(state->fox.x, state->fox.y)] = (uint8_t)BlockType_Empty;
state->wolf = bomber_app_get_block(state->level, BlockType_Wolf);
state->level[ix(state->wolf.x, state->wolf.y)] = (uint8_t)BlockType_Empty;

bomber_main_loop(state);

FURI_LOG_I(TAG, "Destroying app");
Expand Down
103 changes: 101 additions & 2 deletions bomber_loop.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "bomber_loop.h"
#include "helpers.h"
#include "subghz.h"
#include "levels.h"

#define BOMB_HOT_TIME furi_ms_to_ticks(2000)
#define BOMB_PLANTED_TIME furi_ms_to_ticks(2100)
Expand Down Expand Up @@ -28,12 +29,59 @@ static void bomber_app_error(BomberAppState* state) {
bomber_app_set_mode(state, BomberAppMode_Error);
}

static void bomber_app_wait(BomberAppState* state) {
FURI_LOG_E(TAG, "Waiting for P1 to select level");
state->rxMode = RxMode_LevelData;
bomber_app_set_mode(state, BomberAppMode_Waiting);
}

// Start playing
static void bomber_app_start(BomberAppState* state) {
FURI_LOG_I(TAG, "Start playing");

// Figure out player starting positions from level data
state->fox = bomber_app_get_block(state->level, BlockType_Fox);
state->level[ix(state->fox.x, state->fox.y)] = (uint8_t)BlockType_Empty;
state->wolf = bomber_app_get_block(state->level, BlockType_Wolf);
state->level[ix(state->wolf.x, state->wolf.y)] = (uint8_t)BlockType_Empty;

bomber_app_set_mode(state, BomberAppMode_Playing);
}

static void bomber_app_setup_level(BomberAppState* state) {
state->level = all_levels[state->selectedLevel];
uint8_t wall_count = count_walls(state->level);
uint8_t powerup_bomb_count = (uint8_t)round((POWERUP_EXTRABOMB_RATIO * wall_count));
uint8_t powerup_power_count = (uint8_t)round((POWERUP_BOMBPOWER_RATIO * wall_count));
FURI_LOG_D(TAG, "Walls: %d, Extra Bombs: %d, Bomb Power: %d", wall_count, powerup_bomb_count, powerup_power_count);

uint8_t* bomb_powerups = malloc(sizeof(uint8_t) * powerup_bomb_count);
uint8_t* power_powerups = malloc(sizeof(uint8_t) * powerup_power_count);

get_random_powerup_locations(state->level, powerup_bomb_count, bomb_powerups);
get_random_powerup_locations(state->level, powerup_power_count, power_powerups);

for (uint8_t i = 0; i < powerup_bomb_count; i++) {
state->level[bomb_powerups[i]] = BlockType_PuExtraBomb_Hidden;
}
for (uint8_t i = 0; i < powerup_power_count; i++) {
state->level[power_powerups[i]] = BlockType_PuBombStrength_Hidden;
}

free(bomb_powerups);
free(power_powerups);

// Tx level data
subghz_tx_level_data(state, state->level);

bomber_app_start(state);
}

static void bomber_app_select_level(BomberAppState* state) {
FURI_LOG_I(TAG, "Select Level");
bomber_app_set_mode(state, BomberAppMode_LevelSelect);
}

// 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++) {
Expand Down Expand Up @@ -120,7 +168,11 @@ static bool handle_player_select_input(BomberAppState* state, InputEvent input)
state->isPlayerTwo = !state->isPlayerTwo;
return true;
case InputKeyOk:
bomber_app_start(state);
if (!state->isPlayerTwo) {
bomber_app_select_level(state);
} else {
bomber_app_wait(state);
}
return true;
default:
return false;
Expand All @@ -129,6 +181,37 @@ static bool handle_player_select_input(BomberAppState* state, InputEvent input)
return false;
}

static bool handle_levelselect_input(BomberAppState* state, InputEvent input) {
if(input.type == InputTypeShort) {
switch(input.key) {
case InputKeyOk:
bomber_app_setup_level(state);
return true;
case InputKeyUp:
state->selectedLevel -= 2;
break;
case InputKeyDown:
state->selectedLevel += 2;
break;
case InputKeyLeft:
state->selectedLevel -= 1;
break;
case InputKeyRight:
state->selectedLevel += 1;
break;
default:
return false;
}
}

uint8_t levelCount = sizeof(all_levels) / sizeof(int);
if (state->selectedLevel >= levelCount) {
state->selectedLevel = levelCount - 1;
}

return true;
}

// Handle input while playing the game
// state: Pointer to the application state
// input: Represents the input event
Expand Down Expand Up @@ -191,6 +274,8 @@ static bool bomber_app_handle_input(BomberAppState* state, InputEvent input) {
return handle_game_input(state, input);
case BomberAppMode_PlayerSelect:
return handle_player_select_input(state, input);
case BomberAppMode_LevelSelect:
return handle_levelselect_input(state, input);
default:
break;
}
Expand All @@ -209,7 +294,14 @@ void bomber_main_loop(BomberAppState* state) {
state->running = true;

while(state->running) {
subghz_check_incoming(state);
switch (state->rxMode) {
case RxMode_Command:
subghz_check_incoming(state);
break;
case RxMode_LevelData:
subghz_check_incoming_leveldata(state);
break;
}

switch(furi_message_queue_get(state->queue, &event, LOOP_MESSAGE_TIMEOUT_ms)) {
case FuriStatusOk:
Expand All @@ -228,6 +320,13 @@ void bomber_main_loop(BomberAppState* state) {
bomber_game_post_rx(state, event.subGhzIncomingSize);
updated = true;
break;
case BomberEventType_HaveLevelData:
FURI_LOG_I(TAG, "Level data event from queue");
state->level = state->levelData;
state->rxMode = RxMode_Command;
bomber_app_start(state);
updated = true;
break;
default:
FURI_LOG_E(TAG, "Unknown event received from queue.");
break;
Expand Down
57 changes: 54 additions & 3 deletions bomber_ui.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "bomber_ui.h"
#include "types.h"
#include "helpers.h"
#include "levels.h"

// Draws a single bomb based on its state
static void draw_bomb(Canvas* canvas, Bomb bomb) {
Expand Down Expand Up @@ -47,14 +48,14 @@ static void draw_block(Canvas* canvas, int x, int y, BlockType block) {
// Renders the game to the viewport - called while playing
static void render_game(Canvas* canvas, BomberAppState* state) {
// Draw bombs
for(int i = 0; i < MAX_BOMBS; i++) {
for(uint8_t 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++) {
for(uint8_t x = 0; x < 16; x++) {
for(uint8_t y = 0; y < 8; y++) {
if(x == state->fox.x && y == state->fox.y) {
draw_player(canvas, x, y, fox_glyph);
}
Expand Down Expand Up @@ -82,7 +83,46 @@ static void render_player_select(Canvas* canvas, BomberAppState* state) {
canvas_draw_xbm(canvas, ax, 52, 4, 7, select_glyph);
}

static void render_level_preview(Canvas* canvas, uint8_t offset_x, uint8_t offset_y, uint8_t* level) {
for(uint8_t x = 0; x < 16; x++) {
for(uint8_t y = 0; y < 8; y++) {
BlockType block = (BlockType)level[ix(x, y)];
if (is_solid_block(block)) {
canvas_draw_frame(canvas, offset_x + (x * 2), offset_y + (y * 2), 2, 2);
}
}
}
}

// Render the Level Select menu
static void render_level_select(Canvas* canvas, BomberAppState* state) {
furi_assert(state);

canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 31, 12, "Select Level");

uint8_t page_number = state->selectedLevel / 4;
uint8_t start = page_number * 4;
uint8_t levelCount = sizeof(all_levels) / sizeof(int);

for (uint8_t ix = 0; ix < 4; ix++) {
uint8_t displayLevel = start + ix;

if (displayLevel < levelCount) {
uint8_t x = ix % 2 == 0 ? 18 : 75;
uint8_t y = ix > 1 ? 39 : 15;

canvas_draw_frame(canvas, x, y, 36, 20);
render_level_preview(canvas, x + 2, y + 2, all_levels[displayLevel]);
if (displayLevel == state->selectedLevel) {
canvas_draw_xbm(canvas, x - 6, y + 5, 4, 7, select_glyph);
}
}
}
}

static void render_game_over(Canvas* canvas, BomberAppState* state) {
furi_assert(state);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 15, 14, "Game Over!");
if(state->dead == WhoDied_Fox) {
Expand All @@ -92,6 +132,12 @@ static void render_game_over(Canvas* canvas, BomberAppState* state) {
}
}

static void render_wait_screen(Canvas* canvas, BomberAppState* state) {
furi_assert(state);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 15, 14, "Waiting for fox...");
}

// Main callback that starts off rendering
void bomber_ui_render_callback(Canvas* canvas, void* context) {
furi_assert(context);
Expand All @@ -111,9 +157,14 @@ void bomber_ui_render_callback(Canvas* canvas, void* context) {
case BomberAppMode_PlayerSelect:
render_player_select(canvas, state);
break;
case BomberAppMode_LevelSelect:
render_level_select(canvas, state);
break;
case BomberAppMode_GameOver:
render_game_over(canvas, state);
break;
case BomberAppMode_Waiting:
render_wait_screen(canvas, state);
default:
break;
}
Expand Down
6 changes: 6 additions & 0 deletions helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,10 @@ void bomber_app_set_mode(BomberAppState* state, BomberAppMode mode)
furi_mutex_acquire(state->data_mutex, FuriWaitForever);
state->mode = mode;
furi_mutex_release(state->data_mutex);
}

bool is_solid_block(BlockType type) {
return type == BlockType_Brick ||
type == BlockType_PuBombStrength_Hidden ||
type == BlockType_PuExtraBomb_Hidden;
}
1 change: 1 addition & 0 deletions helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ Player* get_player(BomberAppState* state);
void bomber_app_set_mode(BomberAppState* state, BomberAppMode mode);
uint8_t count_walls(uint8_t level[]);
void get_random_powerup_locations(uint8_t level[], int n, uint8_t output[]);
bool is_solid_block(BlockType type);

#endif
49 changes: 48 additions & 1 deletion levels.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,51 @@ uint8_t level2[128] = {
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
};

uint8_t level3[128] = {
3, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 4,
};

uint8_t level4[128] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1,
0, 1, 3, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1,
0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1,
0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 4, 1,
0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

uint8_t level5[128] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1,
1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};

uint8_t level6[128] = {
3, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
};


uint8_t* all_levels[6] = { level1, level2, level3, level4, level5, level6 };
6 changes: 6 additions & 0 deletions levels.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@

#include <stdint.h>

extern uint8_t* all_levels[6];

extern uint8_t level1[128];
extern uint8_t level2[128];
extern uint8_t level3[128];
extern uint8_t level4[128];
extern uint8_t level5[128];


#endif
Loading

0 comments on commit 081aa25

Please sign in to comment.