Removed one memory leak.
#include "Terrain.hh"
#include "Engine.hh"
#include <cmath>
#include <cassert>
#include <algorithm>
#include <ClanLib/display.h>
Terrain::Terrain() {}
Terrain::Terrain(const int &seed)
: terrain(MAP_WIDTH, std::vector<TerrainType>(MAP_HEIGHT, DIRT)){
this->generateTerrain(seed);
}
Terrain::Terrain(const Terrain &t) {
this->terrain = t.getTerrain();
this->generatePixelBuffer();
}
void Terrain::generatePixelBuffer() {
this->pixbuf = CL_PixelBuffer(MAP_WIDTH, MAP_HEIGHT, 4*MAP_WIDTH,
CL_PixelFormat::rgba8888);
CL_Color color;
for (uint16_t i = 0; i < MAP_WIDTH; i++) {
for (uint16_t j = 0; j < MAP_HEIGHT; j++) {
switch(terrain[i][j]) {
case EMPTY:
color = COLOR_EMPTY;
break;
case DIRT:
color = COLOR_DIRT;
break;
case ROCK:
color = COLOR_ROCK;
break;
default: // TODO: Shouldn't be here.
break;
}
this->pixbuf.draw_pixel(i, j, color);
}
}
}
Vector Terrain::getPixelLocation(Vector point) const{
return Vector(scale(point.x),
scale(point.y));
}
uint16_t Terrain::scale(float x) const {
return (uint16_t)(x/MAP_SCALE);
}
TerrainType Terrain::getType(int32_t x, int32_t y) const {
if ((x < 0) || (y < 0) ||(x >= MAP_WIDTH) || (y >= MAP_HEIGHT)) {
return ROCK;
}
return terrain[x][y];
}
TerrainType Terrain::getType(Vector point) const {
return getType((int32_t)point.x, (int32_t)point.y);
}
bool Terrain::collides(const Vector &point) const {
return (getType(point) != EMPTY);
}
bool Terrain::collides(const Vector &begin, const Vector &end) const {
// TODO: Maybe we should have another function prototype that also
// returns the point where we collided.
// We'll use Bresenhams line algorithm to go trough all the
// "pixels" of the line.
Vector b = getPixelLocation(begin);
Vector e = getPixelLocation(end);
bool steep = (abs(e.y - b.y) > abs(e.x - b.x)); // k > 1
if (steep) { // Line is steep -> swap x and y coordinates
std::swap(b.x, b.y);
std::swap(e.x, e.y);
}
if (b.x > e.x) { // Line goes down -> make it go up
std::swap(b, e);
}
uint16_t dx = e.x - b.x;
uint16_t dy = abs(e.y - b.y);
int32_t err = dx/2;
uint16_t ystep;
uint16_t y = b.y;
// Is the line ascending or descending
if (b.y < e.y) ystep = 1;
else ystep = -1;
// Go trough the line
for (uint16_t x = b.x; x <= e.x; x++) {
if (steep) { // X and Y coordinates must be switched if steep
if (getType(y,x) != EMPTY) { // Collision!
return true;
}
} else {
if (getType(x,y) != EMPTY) { // Collision!
return true;
}
}
err = err - dy;
if (err < 0) { // Check if we want to make an ystep
y = y + ystep;
err = err + dx;
}
}
return false; // No Collision
}
void Terrain::removeGround(const Vector &pos, const float &radius) {
// TODO: Implement. Some circle algoritmh should be usefull here,
// though the current impelementation doesn't seem too bad either.
Vector mid = getPixelLocation(pos);
uint16_t r = scale(radius);
for (uint16_t i = mid.x-r; i < mid.x+r; i++) {
for (uint16_t j = mid.y-r; j < mid.y+r; j++) {
if (getType(i, j) != ROCK) { // getType returns ROCK if
// out of bounds
if ((i-mid.x)*(i-mid.x)+(j-mid.y)*(j-mid.y) < r*r) {
terrain[i][j] = EMPTY;
pixbuf.draw_pixel(i, j, COLOR_EMPTY);
}
}
}
}
}
/**
* Gets the index of the given coordinate direction
* referring to the DIRECTIONS table in Physics.hh
*/
int getDirectionIndex (Vector direction) {
Vector dir = direction.roundToInt();
if(dir.x == 0 && dir.y == -1) {
return 0;
} else if(dir.x == 1 && dir.y == -1) {
return 1;
} else if(dir.x == 1 && dir.y == 0) {
return 2;
} else if(dir.x == 1 && dir.y == 1) {
return 3;
} else if(dir.x == 0 && dir.y == 1) {
return 4;
} else if(dir.x == -1 && dir.y == 1) {
return 5;
} else if(dir.x == -1 && dir.y == 0) {
return 6;
} else if(dir.x == -1 && dir.y == -1) {
return 7;
}
Engine::log(DEBUG, "Terrain.getDirectionIndex ") << "invalid direction: " << direction;
return 0;
}
/**
* point should be ground and prevPoint air, but it's easy to assure that
* @param point - pixel on ground to which was collided
* @param prevPoint - pixel where we are when we collide
*/
Vector Terrain::getNormal(Vector point, Vector prevPoint) const {
Vector p = getPixelLocation(point);
assert(point != prevPoint);
Vector normal(0,0);
// These two must be rounded separately
int dirIdx = getDirectionIndex(prevPoint.roundToInt() - point.roundToInt());
// dirIdx = (dirIdx+4)%8;
std::cout << (prevPoint.roundToInt()) - (point.roundToInt()) << prevPoint-point << std::endl;
normal += DIRECTIONS[dirIdx];
for(int i = 1; i <= 2; i++) {
if(getType(point + DIRECTIONS[(dirIdx+i+8)%8]) == EMPTY) {
normal += DIRECTIONS[(dirIdx+i+8)%8];
}
}
for(int i = 1; i <= 2; i++) {
if(getType(point + DIRECTIONS[(dirIdx-i+8)%8]) == EMPTY) {
normal += DIRECTIONS[(dirIdx-i+8)%8];
}
}
Engine::log(DEBUG, "Physics.getNormal ") << "normal: " << normal << " dirIdx: " << dirIdx;
if(getType(point) == EMPTY || getType(prevPoint) != EMPTY) {
Engine::log(DEBUG, "Physics.getNormal ") << "logic ground error";
}
// for (int i = 0; i < 8; i++) {
// if (getType(p.x+DIRECTIONS[i].x, p.y+DIRECTIONS[i].y) == EMPTY) {
// normal += DIRECTIONS[i];
// }
// }
// Special cases
/* Vector tmp = direction(direction(prevPoint-point) + direction(normal));
Engine::log(DEBUG, "Terrain.getNormal") << "tmp: " << tmp;
if (normal.length() == 0 || (tmp.x != 0 && tmp.y != 0 && getType(tmp.x, tmp.y) != EMPTY))
normal = prevPoint - point; // Direct hit
*/
// Engine::log(DEBUG, "Terrain.getNormal") << "Normal: " << normal;
return normal;
return Vector(0,-1);
}
Vector direction(const Vector &v) {
Vector tmp(v);
if (tmp.length() > 0) tmp /= tmp.length();
tmp.x = (uint16_t)(tmp.x);
tmp.y = (uint16_t)(tmp.y);
return tmp;
}
// TODO: This could better :)
// TODO: And this need some cleaning :)
void Terrain::generateTerrain(int seed) {
srand(seed); // Set random number generator seed.
// Some constants to control random generation
const int min_range = 25;
const int max_range = 80;
const int num = 50;
const int rock_rarity = 4;
// Generate circles (or whatever)
for (int i = 0; i < num; i++) {
// Random generate circle attributes
int midx = rand()%MAP_WIDTH;
int midy = rand()%MAP_HEIGHT;
int range = rand()%(max_range-min_range)+min_range;
// Make sure that there's a circle in the midle of the cave
if (i == 0) {
midx = MAP_WIDTH/2;
midy = MAP_WIDTH/2;
range = 150;
}
TerrainType type = EMPTY;
if (rand()%rock_rarity == 0) {
type = ROCK;
}
// Loops for every pixel of the cirlcle (or square as it seems
// now)
for (int x = std::max(0, midx-range);
x < std::min((int32_t)MAP_WIDTH, midx+range);
x++) {
for (int y = std::max(0, midy-range);
y < std::min((int32_t)MAP_HEIGHT, midy+range);
y++) {
//terrain[x][y] = type;
if ((x-midx)*(x-midx)+(y-midy)*(y-midy) < range*range) {
terrain[x][y] = type;
}
}
}
}
this->generatePixelBuffer();
}
void Terrain::draw(CL_GraphicContext *gc) {
CL_Surface surf(this->pixbuf);
surf.draw(0,0,gc);
}
std::vector<std::vector<TerrainType> > Terrain::getTerrain() const {
return terrain;
}