feat(game): add persistent bush-wall breakthroughs
This commit is contained in:
@@ -20,6 +20,19 @@ static maze32_passage_t direction_passage(maze32_game_direction_t direction) {
|
||||
return PASSAGES[direction];
|
||||
}
|
||||
|
||||
static maze32_passage_t opposite_passage(maze32_passage_t passage) {
|
||||
if (passage == MAZE32_PASSAGE_NORTH) {
|
||||
return MAZE32_PASSAGE_SOUTH;
|
||||
}
|
||||
if (passage == MAZE32_PASSAGE_EAST) {
|
||||
return MAZE32_PASSAGE_WEST;
|
||||
}
|
||||
if (passage == MAZE32_PASSAGE_SOUTH) {
|
||||
return MAZE32_PASSAGE_NORTH;
|
||||
}
|
||||
return MAZE32_PASSAGE_EAST;
|
||||
}
|
||||
|
||||
static maze32_cell_t moved_cell(maze32_cell_t cell, maze32_game_direction_t direction) {
|
||||
if (direction == MAZE32_GAME_DIRECTION_NORTH) {
|
||||
cell.y--;
|
||||
@@ -45,6 +58,43 @@ static int cells_equal(maze32_cell_t first, maze32_cell_t second) {
|
||||
return first.x == second.x && first.y == second.y;
|
||||
}
|
||||
|
||||
static uint8_t game_passages(const maze32_game_t* game, maze32_cell_t cell) {
|
||||
return (uint8_t)(game->maze->passages[cell_index(game->maze, cell)] |
|
||||
game->opened_passages[cell_index(game->maze, cell)]);
|
||||
}
|
||||
|
||||
static int game_has_passage(const maze32_game_t* game, maze32_cell_t cell,
|
||||
maze32_passage_t passage) {
|
||||
return (game_passages(game, cell) & passage) != 0U;
|
||||
}
|
||||
|
||||
static void reset_perseverance(maze32_game_player_t* player) {
|
||||
player->perseverance_hits = 0U;
|
||||
player->perseverance_last_impact_ms = 0U;
|
||||
}
|
||||
|
||||
static uint8_t record_wall_impact(maze32_game_player_t* player, maze32_cell_t cell,
|
||||
maze32_game_direction_t direction, uint64_t submitted_ms) {
|
||||
if (player->perseverance_hits == 0U || !cells_equal(player->perseverance_cell, cell) ||
|
||||
player->perseverance_direction != direction ||
|
||||
submitted_ms < player->perseverance_last_impact_ms ||
|
||||
submitted_ms - player->perseverance_last_impact_ms >= MAZE32_GAME_BREAKTHROUGH_TIMEOUT_MS) {
|
||||
player->perseverance_cell = cell;
|
||||
player->perseverance_direction = direction;
|
||||
player->perseverance_hits = 1U;
|
||||
} else {
|
||||
player->perseverance_hits++;
|
||||
}
|
||||
player->perseverance_last_impact_ms = submitted_ms;
|
||||
return player->perseverance_hits;
|
||||
}
|
||||
|
||||
static void reset_all_perseverance(maze32_game_t* game) {
|
||||
for (uint8_t player = 0U; player < MAZE32_GAME_PLAYER_COUNT; ++player) {
|
||||
reset_perseverance(&game->players[player]);
|
||||
}
|
||||
}
|
||||
|
||||
static int is_visible(maze32_cell_t center, maze32_cell_t cell, uint8_t radius) {
|
||||
uint16_t dx = center.x > cell.x ? center.x - cell.x : cell.x - center.x;
|
||||
uint16_t dy = center.y > cell.y ? center.y - cell.y : cell.y - center.y;
|
||||
@@ -95,6 +145,9 @@ maze32_game_result_t maze32_game_init(maze32_game_t* game, const maze32_maze_t*
|
||||
void maze32_game_set_phase(maze32_game_t* game, maze32_game_phase_t phase) {
|
||||
if (game != NULL && phase >= MAZE32_GAME_PHASE_COUNTDOWN &&
|
||||
phase <= MAZE32_GAME_PHASE_FINISHED) {
|
||||
if (phase != MAZE32_GAME_PHASE_ACTIVE) {
|
||||
reset_all_perseverance(game);
|
||||
}
|
||||
if (phase == MAZE32_GAME_PHASE_ACTIVE &&
|
||||
(!game->players[0].connected || !game->players[1].connected)) {
|
||||
game->phase_before_pause = MAZE32_GAME_PHASE_ACTIVE;
|
||||
@@ -159,6 +212,7 @@ int maze32_game_advance_time(maze32_game_t* game, uint64_t now_ms) {
|
||||
game->paused_started_ms = 0U;
|
||||
game->command_head = 0U;
|
||||
game->command_count = 0U;
|
||||
reset_all_perseverance(game);
|
||||
game->state_sequence++;
|
||||
return 1;
|
||||
}
|
||||
@@ -176,6 +230,7 @@ int maze32_game_advance_time(maze32_game_t* game, uint64_t now_ms) {
|
||||
game->phase = MAZE32_GAME_PHASE_FINISHED;
|
||||
game->final_result = game->first_exited_player == 0U ? MAZE32_GAME_FINAL_PLAYER_ONE
|
||||
: MAZE32_GAME_FINAL_PLAYER_TWO;
|
||||
reset_all_perseverance(game);
|
||||
game->state_sequence++;
|
||||
return 1;
|
||||
}
|
||||
@@ -219,6 +274,7 @@ maze32_game_result_t maze32_game_set_player_connected(maze32_game_t* game, uint8
|
||||
}
|
||||
|
||||
player->connected = 0U;
|
||||
reset_perseverance(player);
|
||||
player->reconnect_deadline_ms = now_ms > UINT64_MAX - MAZE32_GAME_RECONNECT_TIMEOUT_MS
|
||||
? UINT64_MAX
|
||||
: now_ms + MAZE32_GAME_RECONNECT_TIMEOUT_MS;
|
||||
@@ -229,6 +285,7 @@ maze32_game_result_t maze32_game_set_player_connected(maze32_game_t* game, uint8
|
||||
game->paused_started_ms = now_ms;
|
||||
game->command_head = 0U;
|
||||
game->command_count = 0U;
|
||||
reset_all_perseverance(game);
|
||||
}
|
||||
game->state_sequence++;
|
||||
return MAZE32_GAME_RESULT_ACCEPTED;
|
||||
@@ -373,6 +430,7 @@ static void finish_outward_move(maze32_game_t* game, uint8_t player_index, uint6
|
||||
} else {
|
||||
game->phase = MAZE32_GAME_PHASE_FINISHED;
|
||||
game->final_result = MAZE32_GAME_FINAL_DRAW;
|
||||
reset_all_perseverance(game);
|
||||
processed->finished = 1U;
|
||||
}
|
||||
}
|
||||
@@ -452,6 +510,7 @@ maze32_game_command_result_t maze32_game_process_next(maze32_game_t* game) {
|
||||
return processed;
|
||||
}
|
||||
if (direction_leaves_map(game->maze, player->position, command.direction)) {
|
||||
reset_perseverance(player);
|
||||
if (!cells_equal(player->position, game->maze->exits[player_index]) ||
|
||||
!game->exits[player_index].open) {
|
||||
processed.result = MAZE32_GAME_RESULT_BLOCKED;
|
||||
@@ -460,7 +519,27 @@ maze32_game_command_result_t maze32_game_process_next(maze32_game_t* game) {
|
||||
finish_outward_move(game, player_index, command.submitted_ms, &processed);
|
||||
} else {
|
||||
maze32_passage_t passage = direction_passage(command.direction);
|
||||
if (!maze32_maze_has_passage(game->maze, player->position, passage)) {
|
||||
if (!game_has_passage(game, player->position, passage)) {
|
||||
processed.breakthrough_hits = record_wall_impact(
|
||||
player, player->position, command.direction, command.submitted_ms);
|
||||
if (processed.breakthrough_hits < MAZE32_GAME_BREAKTHROUGH_HITS) {
|
||||
processed.result = MAZE32_GAME_RESULT_BLOCKED;
|
||||
return processed;
|
||||
}
|
||||
maze32_cell_t destination = moved_cell(player->position, command.direction);
|
||||
if (!valid_cell(game->maze, destination)) {
|
||||
reset_perseverance(player);
|
||||
processed.result = MAZE32_GAME_RESULT_BLOCKED;
|
||||
return processed;
|
||||
}
|
||||
game->opened_passages[cell_index(game->maze, player->position)] |= passage;
|
||||
game->opened_passages[cell_index(game->maze, destination)] |= opposite_passage(passage);
|
||||
processed.breakthrough_opened = 1U;
|
||||
reset_perseverance(player);
|
||||
}
|
||||
if (game_has_passage(game, player->position, passage)) {
|
||||
reset_perseverance(player);
|
||||
} else {
|
||||
processed.result = MAZE32_GAME_RESULT_BLOCKED;
|
||||
return processed;
|
||||
}
|
||||
@@ -589,7 +668,8 @@ maze32_game_result_t maze32_game_project_view(const maze32_game_t* game, maze32_
|
||||
if (reveal_all || is_visible(center, cell, radius)) {
|
||||
uint16_t index = cell_index(game->maze, cell);
|
||||
view->cells[view->cell_count].cell_index = index;
|
||||
view->cells[view->cell_count].passages = game->maze->passages[index];
|
||||
view->cells[view->cell_count].passages = game_passages(game, cell);
|
||||
view->cells[view->cell_count].opened_passages = game->opened_passages[index];
|
||||
view->cell_count++;
|
||||
}
|
||||
}
|
||||
@@ -781,8 +861,9 @@ maze32_game_result_t maze32_game_serialize_view_chunk(const maze32_view_t* view,
|
||||
}
|
||||
for (size_t index = first; index < end; ++index) {
|
||||
const maze32_view_cell_t* cell = &view->cells[index];
|
||||
if (!append_text(output, output_size, &offset, "%s[%u,%u]", index == first ? "" : ",",
|
||||
cell->cell_index, cell->passages)) {
|
||||
if (!append_text(output, output_size, &offset, "%s[%u,%u,%u]",
|
||||
index == first ? "" : ",", cell->cell_index, cell->passages,
|
||||
cell->opened_passages)) {
|
||||
return MAZE32_GAME_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user