#ifndef PI
#define PI 3.14159265358979323846
#endif
#ifndef HEXMAP_HPP
#define HEXMAP_HPP
#include <stdint.h>
#include <stdlib.h>
#include <ctime>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <Godot.hpp>
#include <Node.hpp>
#include <SpatialMaterial.hpp>
#include <OpenSimplexNoise.hpp>
#include "human.hpp"
#include "hexTile.hpp"
namespace godot
{
class HexMap : public Node
{ GODOT_CLASS(HexMap, Node)
private:
HexTile*** _hexTile;
std::unordered_map<int, Human*> _humans;
int _numHumans = 0;
double _hexRadius = 1.0;
double _hexHeight = _hexRadius * 2;
double _hexWidth = (sqrt(3) / 2) * _hexHeight;
int _numRow = 75;
int _numCol = 145;
double _globeRadius = 25;
double _angleStep;
double _mapHeight = (_hexHeight * 0.75) * _numRow;
double _mapWidth = _hexWidth * _numCol;
OpenSimplexNoise* _noiseElevation = OpenSimplexNoise::_new();
int64_t _elevationOctaves = 4;
real_t _elevationPeriod = 5;
real_t _elevationLacunarity = 1.0;
real_t _elevationPersistance = 1.0;
OpenSimplexNoise* _noiseMoisture = OpenSimplexNoise::_new();
int64_t _moistureOctaves = 2;
real_t _moisturePeriod = 2.5;
real_t _moistureLacunarity = 1.5;
real_t _moisturePersistance = 1.0;
public:
static void _register_methods();
void _init();
void spawn_human(HexTile* pHex);
void remove_human(Human* pHuman);
void generate_map();
std::vector<HexTile*> get_area(int pX, int pY, int pRadius);
void elevate_area(int pX, int pY, int pRadius, double pElevation);
HexTile* get_hexTile(int pX, int pY);
double get_distance(HexTile* pHexA, HexTile* pHexB);
double get_height();
double get_width();
void set_numRow(int pRow);
int get_numRow() const;
void set_numCol(int pCol);
int get_numCol() const;
};
}
#endif /* HEXMAP_HPP */
#include "hexMap.hpp"
using namespace godot;
void HexMap::_register_methods()
{
}
void HexMap::_init()
{
generate_map();
}
void HexMap::spawn_human(HexTile* pHex)
{
Human* newHuman = Human::_new();
newHuman->set_id(_numHumans);
newHuman->set_parentHex(pHex);
pHex->add_child(newHuman);
_humans[_numHumans] = newHuman;
_numHumans++;
}
void HexMap::remove_human(Human* pHuman)
{ // Don't decrement _numHumans, the number is required as a unique ID for each human created.
pHuman->get_parent()->remove_child(pHuman);
_humans.erase(pHuman->get_id());
}
void HexMap::generate_map()
{
// Dynamic Array Allocation
_hexTile = new HexTile**[_numRow];
for (int i = 0; i < _numRow; i++) {
_hexTile[i] = new HexTile*[_numCol];
}
// Set Map Parameters
_angleStep = ((360.0 / _numCol) * (PI / 180.0));
_noiseElevation->set_octaves(_elevationOctaves);
_noiseElevation->set_period(_elevationPeriod);
_noiseElevation->set_lacunarity(_elevationLacunarity);
_noiseElevation->set_persistence(_elevationPersistance);
_noiseMoisture->set_octaves(_moistureOctaves);
_noiseMoisture->set_period(_moisturePeriod);
_noiseMoisture->set_lacunarity(_moistureLacunarity);
_noiseMoisture->set_persistence(_moisturePersistance);
srand((int) time(0));
_noiseElevation->set_seed(rand());
_noiseMoisture->set_seed(rand());
// Generate Empty Map
double currentAngle;
for (int x = 0; x < _numRow; x++) {
currentAngle = 0.0;
for (int y = 0; y < _numCol; y++) {
_hexTile[x][y] = HexTile::_new();
_hexTile[x][y]->initialize(
x,
y,
-0.25,
0
);
add_child(_hexTile[x][y]);
currentAngle += _angleStep;
}
}
// Generate Continents
int numSplats = (rand() % 10) + 10;
for (int i = 0; i < numSplats; i++) {
int size = (rand() % 10) + 10;
double intensity = ((rand() % 70) + 40) / 100.0;
int x = (rand() % _numRow);
int y = (rand() % _numCol);
elevate_area(x, y, size, intensity);
}
// Generate Lakes
int numLakes = (rand() % 5) + 25;
for (int i = 0; i < numLakes; i++) {
int size = (rand() % 5) + 5;
double intensity = ((rand() % 25) + 85) / 100.0;
int x = (rand() % _numRow);
int y = (rand() % _numCol);
elevate_area(x, y, size, -intensity);
}
// Add Noise
for (int x = 0; x < _numRow; x++) {
currentAngle = 0.0;
for (int y = 0; y < _numCol; y++) {
_hexTile[x][y]->_elevation += _noiseElevation->get_noise_3d(x, _globeRadius * sin(currentAngle), _globeRadius * cos(currentAngle)) * 0.75;
_hexTile[x][y]->_moisture += _noiseMoisture->get_noise_3d(x, _globeRadius * sin(currentAngle), _globeRadius * cos(currentAngle));
currentAngle += _angleStep;
}
}
// Update Hex Visuals
for (int x = 0; x < _numRow; x++) {
currentAngle = 0.0;
for (int y = 0; y < _numCol; y++) {
_hexTile[x][y]->update_visuals();
}
}
}
std::vector<HexTile*> HexMap::get_area(int pX, int pY, int pRadius)
{
std::vector<HexTile*> area;
for (int dX = -pRadius; dX < pRadius - 1; dX++) {
for (int dY = std::max(-pRadius + 1, -dX - pRadius); dY < std::min(pRadius, -dX + pRadius - 1); dY++) {
int x = pX + dX;;
int y = pY + dY;
if (x >= _numRow) {
x = x - _numRow;
} else if (x < 0) {
x = x + _numRow;
}
if (y >= _numCol) {
y = y - _numCol;
} else if (y < 0) {
y = y + _numCol;
}
area.push_back(_hexTile[x][y]);
}
}
return area;
}
void HexMap::elevate_area(int pX, int pY, int pRadius, double pElevation)
{
std::vector<HexTile*> area = get_area(pX, pY, pRadius);
for (int i = 0; i < area.size(); i++) {
area[i]->_elevation += (pElevation * (1 - (get_distance(_hexTile[pX][pY], area[i]) / pRadius)));
}
}
double HexMap::get_distance(HexTile* pHexA, HexTile* pHexB)
{
int dX;
int dY;
int dZ;
if (std::abs(pHexA->_x - pHexB->_x) > _numRow / 2) {
dX = _numRow - std::abs(pHexA->_x - pHexB->_x);
} else {
dX = std::abs(pHexA->_x - pHexB->_x);
}
if (std::abs(pHexA->_y - pHexB->_y) > _numCol / 2) {
dY = _numCol - std::abs(pHexA->_y - pHexB->_y);
} else {
dY = std::abs(pHexA->_y - pHexB->_y);
}
if (std::abs(pHexA->_z - pHexB->_z) > _numRow / 2) {
dZ = _numRow - std::abs(pHexA->_z - pHexB->_z);
} else {
dZ = std::abs(pHexA->_z - pHexB->_z);
}
return std::max(std::max(dX, dY), dZ);
}
HexTile* HexMap::get_hexTile(int pX, int pY)
{
return _hexTile[pX][pY];
}
double HexMap::get_height()
{
return _mapHeight;
}
double HexMap::get_width()
{
return _mapWidth;
}
void HexMap::set_numRow(int pRow)
{
_numRow = pRow;
}
int HexMap::get_numRow() const
{
return _numRow;
}
void HexMap::set_numCol(int pCol)
{
_numCol = pCol;
}
int HexMap::get_numCol() const
{
return _numCol;
}