|
1 #include "Terrain.hh" |
|
2 #include "Engine.hh" |
|
3 |
|
4 #include <cmath> |
|
5 #include <cassert> |
|
6 #include <algorithm> |
|
7 #include <ClanLib/display.h> |
|
8 |
|
9 Terrain::Terrain() {} |
|
10 Terrain::Terrain(const int &seed) |
|
11 : terrain(MAP_WIDTH, std::vector<TerrainType>(MAP_HEIGHT, DIRT)){ |
|
12 this->generateTerrain(seed); |
|
13 } |
|
14 Terrain::Terrain(const Terrain &t) { |
|
15 this->terrain = t.getTerrain(); |
|
16 this->generatePixelBuffer(); |
|
17 } |
|
18 |
|
19 void Terrain::generatePixelBuffer() { |
|
20 this->pixbuf = CL_PixelBuffer(MAP_WIDTH, MAP_HEIGHT, 4*MAP_WIDTH, |
|
21 CL_PixelFormat::rgba8888); |
|
22 |
|
23 CL_Color color; |
|
24 for (uint16_t i = 0; i < MAP_WIDTH; i++) { |
|
25 for (uint16_t j = 0; j < MAP_HEIGHT; j++) { |
|
26 switch(terrain[i][j]) { |
|
27 case EMPTY: |
|
28 color = COLOR_EMPTY; |
|
29 break; |
|
30 case DIRT: |
|
31 color = COLOR_DIRT; |
|
32 break; |
|
33 case ROCK: |
|
34 color = COLOR_ROCK; |
|
35 break; |
|
36 default: // TODO: Shouldn't be here. |
|
37 break; |
|
38 } |
|
39 this->pixbuf.draw_pixel(i, j, color); |
|
40 } |
|
41 } |
|
42 } |
|
43 |
|
44 Vector Terrain::getPixelLocation(Vector point) const{ |
|
45 return Vector(scale(point.x), |
|
46 scale(point.y)); |
|
47 } |
|
48 |
|
49 uint16_t Terrain::scale(float x) const { |
|
50 return (uint16_t)(x/MAP_SCALE); |
|
51 } |
|
52 |
|
53 TerrainType Terrain::getType(int32_t x, int32_t y) const { |
|
54 if ((x < 0) || (y < 0) ||(x >= MAP_WIDTH) || (y >= MAP_HEIGHT)) { |
|
55 return ROCK; |
|
56 } |
|
57 return terrain[x][y]; |
|
58 } |
|
59 TerrainType Terrain::getType(Vector point) const { |
|
60 return getType((int32_t)point.x, (int32_t)point.y); |
|
61 } |
|
62 |
|
63 bool Terrain::collides(const Vector &point) const { |
|
64 Vector coor = getPixelLocation(point); |
|
65 return (getType(coor.x, coor.y) != EMPTY); |
|
66 } |
|
67 |
|
68 bool Terrain::collides(const Vector &begin, const Vector &end) const { |
|
69 // TODO: Maybe we should have another function prototype that also |
|
70 // returns the point where we collided. |
|
71 |
|
72 // We'll use Bresenhams line algorithm to go trough all the |
|
73 // "pixels" of the line. |
|
74 Vector b = getPixelLocation(begin); |
|
75 Vector e = getPixelLocation(end); |
|
76 |
|
77 bool steep = (abs(e.y - b.y) > abs(e.x - b.x)); // k > 1 |
|
78 if (steep) { // Line is steep -> swap x and y coordinates |
|
79 std::swap(b.x, b.y); |
|
80 std::swap(e.x, e.y); |
|
81 } |
|
82 if (b.x > e.x) { // Line goes down -> make it go up |
|
83 std::swap(b, e); |
|
84 } |
|
85 uint16_t dx = e.x - b.x; |
|
86 uint16_t dy = abs(e.y - b.y); |
|
87 int32_t err = dx/2; |
|
88 uint16_t ystep; |
|
89 uint16_t y = b.y; |
|
90 // Is the line ascending or descending |
|
91 if (b.y < e.y) ystep = 1; |
|
92 else ystep = -1; |
|
93 // Go trough the line |
|
94 for (uint16_t x = b.x; x <= e.x; x++) { |
|
95 if (steep) { // X and Y coordinates must be switched if steep |
|
96 if (getType(y,x) != EMPTY) { // Collision! |
|
97 return true; |
|
98 } |
|
99 } else { |
|
100 if (getType(x,y) != EMPTY) { // Collision! |
|
101 return true; |
|
102 } |
|
103 } |
|
104 err = err - dy; |
|
105 if (err < 0) { // Check if we want to make an ystep |
|
106 y = y + ystep; |
|
107 err = err + dx; |
|
108 } |
|
109 } |
|
110 return false; // No Collision |
|
111 } |
|
112 |
|
113 void Terrain::removeGround(const Vector &pos, const float &radius) { |
|
114 // TODO: Implement. Some circle algoritmh should be usefull here, |
|
115 // though the current impelementation doesn't seem too bad either. |
|
116 |
|
117 Vector mid = getPixelLocation(pos); |
|
118 uint16_t r = scale(radius); |
|
119 for (uint16_t i = mid.x-r; i < mid.x+r; i++) { |
|
120 for (uint16_t j = mid.y-r; j < mid.y+r; j++) { |
|
121 if (getType(i, j) != ROCK) { // getType returns ROCK if |
|
122 // out of bounds |
|
123 if ((i-mid.x)*(i-mid.x)+(j-mid.y)*(j-mid.y) < r*r) { |
|
124 terrain[i][j] = EMPTY; |
|
125 pixbuf.draw_pixel(i, j, COLOR_EMPTY); |
|
126 } |
|
127 } |
|
128 } |
|
129 } |
|
130 } |
|
131 |
|
132 /** |
|
133 * Gets the index of the given coordinate direction |
|
134 * referring to the DIRECTIONS table in Physics.hh |
|
135 */ |
|
136 int getDirectionIndex (Vector direction) { |
|
137 Vector dir = direction.roundToInt(); |
|
138 if(dir.x == 0 && dir.y == -1) { |
|
139 return 0; |
|
140 } else if(dir.x == 1 && dir.y == -1) { |
|
141 return 1; |
|
142 } else if(dir.x == 1 && dir.y == 0) { |
|
143 return 2; |
|
144 } else if(dir.x == 1 && dir.y == 1) { |
|
145 return 3; |
|
146 } else if(dir.x == 0 && dir.y == 1) { |
|
147 return 4; |
|
148 } else if(dir.x == -1 && dir.y == 1) { |
|
149 return 5; |
|
150 } else if(dir.x == -1 && dir.y == 0) { |
|
151 return 6; |
|
152 } else if(dir.x == -1 && dir.y == -1) { |
|
153 return 7; |
|
154 } |
|
155 Engine::log(DEBUG, "Terrain.getDirectionIndex ") << "invalid direction: " << direction; |
|
156 return 0; |
|
157 } |
|
158 |
|
159 /** |
|
160 * point should be ground and prevPoint air, but it's easy to assure that |
|
161 * @param point - pixel on ground to which was collided |
|
162 * @param prevPoint - pixel where we are when we collide |
|
163 */ |
|
164 Vector Terrain::getNormal(Vector point, Vector prevPoint) const { |
|
165 Vector p = getPixelLocation(point); |
|
166 |
|
167 assert(point != prevPoint); |
|
168 |
|
169 Vector normal(0,0); |
|
170 |
|
171 // These two must be rounded separately |
|
172 int dirIdx = getDirectionIndex(prevPoint.roundToInt() - point.roundToInt()); |
|
173 // dirIdx = (dirIdx+4)%8; |
|
174 |
|
175 std::cout << (prevPoint.roundToInt()) - (point.roundToInt()) << prevPoint-point << std::endl; |
|
176 |
|
177 normal += DIRECTIONS[dirIdx]; |
|
178 for(int i = 1; i <= 2; i++) { |
|
179 if(getType(point + DIRECTIONS[(dirIdx+i+8)%8]) == EMPTY) { |
|
180 normal += DIRECTIONS[(dirIdx+i+8)%8]; |
|
181 } |
|
182 } |
|
183 for(int i = 1; i <= 2; i++) { |
|
184 if(getType(point + DIRECTIONS[(dirIdx-i+8)%8]) == EMPTY) { |
|
185 normal += DIRECTIONS[(dirIdx-i+8)%8]; |
|
186 } |
|
187 } |
|
188 |
|
189 Engine::log(DEBUG, "Physics.getNormal ") << "normal: " << normal << " dirIdx: " << dirIdx; |
|
190 |
|
191 if(getType(point) == EMPTY || getType(prevPoint) != EMPTY) { |
|
192 Engine::log(DEBUG, "Physics.getNormal ") << "logic ground error"; |
|
193 } |
|
194 |
|
195 |
|
196 // for (int i = 0; i < 8; i++) { |
|
197 // if (getType(p.x+DIRECTIONS[i].x, p.y+DIRECTIONS[i].y) == EMPTY) { |
|
198 // normal += DIRECTIONS[i]; |
|
199 // } |
|
200 // } |
|
201 |
|
202 |
|
203 // Special cases |
|
204 /* Vector tmp = direction(direction(prevPoint-point) + direction(normal)); |
|
205 Engine::log(DEBUG, "Terrain.getNormal") << "tmp: " << tmp; |
|
206 if (normal.length() == 0 || (tmp.x != 0 && tmp.y != 0 && getType(tmp.x, tmp.y) != EMPTY)) |
|
207 normal = prevPoint - point; // Direct hit |
|
208 */ |
|
209 // Engine::log(DEBUG, "Terrain.getNormal") << "Normal: " << normal; |
|
210 return normal; |
|
211 return Vector(0,-1); |
|
212 } |
|
213 |
|
214 Vector direction(const Vector &v) { |
|
215 Vector tmp(v); |
|
216 if (tmp.length() > 0) tmp /= tmp.length(); |
|
217 tmp.x = (uint16_t)(tmp.x); |
|
218 tmp.y = (uint16_t)(tmp.y); |
|
219 return tmp; |
|
220 } |
|
221 |
|
222 // TODO: This could better :) |
|
223 // TODO: And this need some cleaning :) |
|
224 void Terrain::generateTerrain(int seed) { |
|
225 srand(seed); // Set random number generator seed. |
|
226 |
|
227 // Some constants to control random generation |
|
228 const int min_range = 25; |
|
229 const int max_range = 80; |
|
230 const int num = 50; |
|
231 const int rock_rarity = 4; |
|
232 |
|
233 // Generate circles (or whatever) |
|
234 for (int i = 0; i < num; i++) { |
|
235 // Random generate circle attributes |
|
236 int midx = rand()%MAP_WIDTH; |
|
237 int midy = rand()%MAP_HEIGHT; |
|
238 int range = rand()%(max_range-min_range)+min_range; |
|
239 |
|
240 // Make sure that there's a circle in the midle of the cave |
|
241 if (i == 0) { |
|
242 midx = MAP_WIDTH/2; |
|
243 midy = MAP_WIDTH/2; |
|
244 range = 150; |
|
245 } |
|
246 |
|
247 TerrainType type = EMPTY; |
|
248 if (rand()%rock_rarity == 0) { |
|
249 type = ROCK; |
|
250 } |
|
251 |
|
252 // Loops for every pixel of the cirlcle (or square as it seems |
|
253 // now) |
|
254 for (int x = std::max(0, midx-range); |
|
255 x < std::min((int32_t)MAP_WIDTH, midx+range); |
|
256 x++) { |
|
257 for (int y = std::max(0, midy-range); |
|
258 y < std::min((int32_t)MAP_HEIGHT, midy+range); |
|
259 y++) { |
|
260 |
|
261 //terrain[x][y] = type; |
|
262 |
|
263 if ((x-midx)*(x-midx)+(y-midy)*(y-midy) < range*range) { |
|
264 terrain[x][y] = type; |
|
265 } |
|
266 } |
|
267 |
|
268 } |
|
269 |
|
270 } |
|
271 |
|
272 this->generatePixelBuffer(); |
|
273 } |
|
274 |
|
275 void Terrain::draw(CL_GraphicContext *gc) { |
|
276 CL_Surface surf(this->pixbuf); |
|
277 surf.draw(0,0,gc); |
|
278 } |
|
279 |
|
280 std::vector<std::vector<TerrainType> > Terrain::getTerrain() const { |
|
281 return terrain; |
|
282 } |
|
283 |