#ifndef TERRAIN_HH
#define TERRAIN_HH
#include "Vector.hh"
#include "Types.hh"
#include "Config.hh"
#include "Configuration.hh"
#include <vector>
#if GRAPHICS_ENABLED
#include "Graphics/Drawable.hh"
#endif
/**
* Different types of terrain available
*/
enum TerrainType {
/** Empty space, air */
TERRAIN_EMPTY = 0x00,
/** Dirt, which can be destroyed/dug through */
TERRAIN_DIRT = 0x01,
/** Indestructible rock */
TERRAIN_ROCK = 0x02
};
/**
* Terrain class. Represents game terrain and contains member
* functions to manipulate terrain and get info about it.
*
* Terrain resolution is a constant that is defined in
* configuration. Terrain has a scale (i.e. the width and height in
* "real" units. Scaling is needed because physics simulation uses
* "real" units. The idea is that this class is used with "real" units
* and it uses pixelcoordinates internally.
*/
class Terrain {
protected:
/** The terrain data is stored as a linear array in row-major order, with width * height elements */
TerrainPixel *terrain_buf;
/** Terrain dimensions */
PixelDimension width, height;
#if GRAPHICS_ENABLED
/** We pre-render the textured terrain data for display */
CL_PixelBuffer pixbuf;
/** Generated texture pattern */
std::vector<std::vector<int> > texture;
#endif
public:
/**
* Construct a new terrain based on the given configuration
*
* @param config a TerrainConfig describing how to build the terrain
*/
explicit Terrain (const TerrainConfig &config);
/**
* Construct the terrain using the provided terrain data. The given \a terrain_buf must be a linear array in the
* same format as Terrain::terrain_buf, meaning a row-major order array with width * height elements. The buffer
* must be allocated on the heap using 'new []', and ownership will be transferred (i.e. the buffer is not copied,
* and will eventually be delete []'d).
*
* @param width terrain width
* @param height terrain height
* @param terrain_buf dynamically allocated width * height array of terrain data
*/
Terrain (PixelDimension width, PixelDimension height, TerrainPixel *terrain_buf);
/**
* Destructor, frees our terrain buffer
*/
~Terrain (void);
private:
/* No copying */
Terrain (const Terrain ©);
Terrain &operator= (const Terrain ©);
protected:
/**
* Set induvidual pixel value, updates both terrain_buf and pixbuf
*/
inline void setType (PixelDimension x, PixelDimension y, TerrainType t) {
terrain_buf[y * width + x] = (TerrainPixel) t;
#if GRAPHICS_ENABLED
// XXX: locking?
pixbuf.draw_pixel(x, y, getTexturePixel(x, y));
#endif
}
/**
* Generate random terrain using given seed
*
* @param seed seed for the random number generator
*/
void generateTerrain (int seed);
/**
* Regenerates our pixbuf from the current terrain. This is a slow process, but using the pixbuffer directly
* greatly speeds up drawing.
*/
void generatePixelBuffer (void);
#if GRAPHICS_ENABLED
/**
* Generates an area of random texture using a fractal-based algorithm, this can then be applied to the terrain
* pixels using noisifyPixel
*/
void generateTexture();
/**
* Applies noise generated using generateTexture to generate a slightly varying color for the terrain pixel
* at \a pc.
*/
Color getTexturePixel (PixelDimension x, PixelDimension y);
#endif
/**
* Scale parameter to "pixels"
*
* @param x Scaled value
* @return Corresponding value in pixels
*/
inline PixelDimension scale (float x) const {
return (int) x;
}
public:
/**
* Get pixel location of a point that is in "real" units.
*
* @param point Point in "real" units
* @return Int vector
*/
inline PixelCoordinate getPixelCoordinate (Vector point) const {
return PixelCoordinate(scale(point.x), scale(point.y));
}
/**
* Return the terrain dimensions à la a PixelCoordinate
*/
PixelCoordinate getDimensions (void) const {
return PixelCoordinate(width, height);
}
/**
* Return dimensions in component form
*/
PixelDimension getWidth (void) const {
return width;
}
PixelDimension getHeight (void) const {
return height;
}
/**
* Return the type of terrain at given position. Returns TERRAIN_ROCK if given point is not inside terrain area.
*
* @param x terrain x coordinate
* @param y terrain x coordinate
* @return terrain pixel type
*/
TerrainType getType (PixelDimension x, PixelDimension y) const {
// XXX: optimize access by removing error checking?
if (x < 0 || y < 0 || x >= width || y >= height)
return TERRAIN_ROCK;
return (TerrainType) terrain_buf[y * width + x];
}
inline TerrainType getType (PixelCoordinate pc) const {
return getType(pc.x, pc.y);
}
inline TerrainType getType (Vector point) const {
return getType(getPixelCoordinate(point));
}
/**
* Get raw read-only terrain data buffer
*
* @see terrain_buf
*/
const TerrainPixel* getTerrainBuffer (void) const;
/**
* Load new terrain data from given buffer. Must be the right size
*
* @see terrain_buf
*/
void loadFromBuffer (const TerrainPixel *buf);
/**
* Check if the given point is terrain
*/
bool collides (const Vector &point) const;
/**
* Check if the given line collides with terrain
*
* @param begin line starting point
* @param end line ending point
*/
bool collides (const Vector &begin, const Vector &end) const;
/**
* Remove a circular area of terrain
*
* @param pos circle center
* @param r circle radius
*/
void removeGround (const Vector &pos, float r);
/**
* Returns direction normal vector for the given \a point.
*
* \a point should be ground, and \a prevPoint air.
*
* @param point the ground that we collided into
* @param prevPoint position in air where we were before the collision
* @return direction ormal vector, or (0,0) if there's no terrain
*/
Vector getNormal (Vector point, Vector prevPoint) const;
#if GRAPHICS_ENABLED
/**
* Draw the terrain onto the given graphics context
*
* @param gc Graphics to draw on
* @param camera view position
*/
virtual void draw (graphics::Display &display, PixelCoordinate camera = PixelCoordinate(0, 0));
#endif
};
#endif