-
Notifications
You must be signed in to change notification settings - Fork 0
/
terrain_state.cpp
143 lines (120 loc) · 4.34 KB
/
terrain_state.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include "include/state_management.h"
#include "include/terrain_patterns.h"
#include "include/globals.h"
#include <stack>
int count_non_empty_rows(GameState& g) {
int non_empty_rows = 0;
for (const auto& row : g.terrain) {
if (std::any_of(row.begin(), row.end(), [](const std::unique_ptr<Block>& block) { return block != nullptr; })) {
++non_empty_rows;
}
}
return non_empty_rows;
}
void shift_rows_down(GameState& g, int num_rows_to_shift) {
if (num_rows_to_shift <= 0) return;
// Shift rows down
for (int y = g.terrain.size() - 1; y >= num_rows_to_shift; --y) {
g.terrain[y] = std::move(g.terrain[y - num_rows_to_shift]);
for (auto &block : g.terrain[y]) {
if (block) {
block->target_pos.y += BLOCK_HEIGHT * num_rows_to_shift;
block->grid_pos.y += num_rows_to_shift;
}
}
}
// Clear the top rows
for (int y = 0; y < num_rows_to_shift; ++y) {
for (auto &block : g.terrain[y]) {
block.reset(); // Set each element to nullptr
}
}
}
void add_new_chunk(GameState& g, int num_rows, std::function<Grid(int, int)> pattern_func) {
int num_cols = rng.randomInt(20, NUM_COLS);
Grid new_chunk = pattern_func(num_rows, num_cols);
// Add the new chunk at the top
for (int y = 0; y < num_rows; ++y) {
g.terrain[y] = std::move(new_chunk[y]);
for (auto &block : g.terrain[y]) {
if (block) {
block->target_pos.y = y * BLOCK_HEIGHT;
}
}
}
}
void update_terrain(GameState& g) {
// Check if the bottom row is completely empty
bool bottom_row_empty = true;
for (auto& block : g.terrain.back()) {
if (block) {
bottom_row_empty = false;
break;
}
}
// Shift rows down and add a new chunk at the top if the bottom row is empty
if (bottom_row_empty) {
int non_empty_rows = count_non_empty_rows(g);
int num_rows_to_shift = NUM_ROWS - non_empty_rows;
if (num_rows_to_shift > 0) {
PatternFunc pattern_func = rng.choose({sine_landscape, grid_pattern, sine_pattern, circle_lattice_pattern}); //
shift_rows_down(g, num_rows_to_shift);
add_new_chunk(g, num_rows_to_shift, pattern_func);
}
}
// Deactivate disconnected clusters
deactivate_disconnected_clusters(g);
// Update each block
for (auto& row : g.terrain) {
for (auto& block : row) {
if (block) {
block_update(*block, g);
if (!block->active) {
block.reset(); // Automatically deletes the block and sets the pointer to nullptr
}
}
}
}
}
void dfs_mark_reachable(GameState& g, int row, int col, std::vector<std::vector<bool>>& visited) {
std::stack<std::pair<int, int>> stack;
stack.push({row, col});
while (!stack.empty()) {
auto pair = stack.top();
int r = pair.first;
int c = pair.second;
stack.pop();
if (r < 0 || r >= g.terrain.size() || c < 0 || c >= g.terrain[r].size() || !g.terrain[r][c] || visited[r][c]) {
continue;
}
visited[r][c] = true;
// Check adjacent blocks
stack.push({r - 1, c}); // above
stack.push({r + 1, c}); // below
stack.push({r, c - 1}); // left
stack.push({r, c + 1}); // right
}
}
/**
* @brief Uses DFS to check if blocks are not connected to top row (have been shaved off main body of terrain)
* and deactivates them.
* @visited array is used to mark blocks that are reachable from the top row (is a boolean map of terrain grid).
* @param g The game state.
*/
void deactivate_disconnected_clusters(GameState& g) {
std::vector<std::vector<bool>> visited(NUM_ROWS, std::vector<bool>(NUM_COLS, false));
// Mark all reachable blocks starting from the top row
for (int col = 0; col < NUM_COLS; ++col) {
if (g.terrain[0][col]) {
dfs_mark_reachable(g, 0, col, visited);
}
}
// Deactivate all unvisited (disconnected) blocks
for (int row = 0; row < NUM_ROWS; ++row) {
for (int col = 0; col < NUM_COLS; ++col) {
if (g.terrain[row][col] && !visited[row][col]) {
g.terrain[row][col]->active = false;
}
}
}
}