|
@@ -3,9 +3,11 @@
|
|
|
|
|
|
#include "data/Array.h"
|
|
|
#include "data/ArrayList.h"
|
|
|
+#include "data/HashMap.h"
|
|
|
#include "data/List.h"
|
|
|
#include "math/Vector.h"
|
|
|
#include "rendering/Shader.h"
|
|
|
+#include "rendering/Texture.h"
|
|
|
#include "rendering/VertexBuffer.h"
|
|
|
#include "rendering/Window.h"
|
|
|
#include "utils/Color.h"
|
|
@@ -13,8 +15,12 @@
|
|
|
#include "utils/Random.h"
|
|
|
|
|
|
static Shader shader;
|
|
|
+static Shader fontShader;
|
|
|
static Buffer buffer;
|
|
|
+static Buffer fontBuffer;
|
|
|
static VertexBuffer vertexBuffer;
|
|
|
+static VertexBuffer fontVertexBuffer;
|
|
|
+static Texture fontTexture;
|
|
|
|
|
|
enum class Resource { NOTHING, CLAY, ORE, WHEAT, SHEEP, WOOD };
|
|
|
static const Color4 RESOURCE_COLOR[] = {
|
|
@@ -48,10 +54,54 @@ static void addSquare(const Vector2 mid, float size, const Color4& color) {
|
|
|
addTriangle(b, d, c, color);
|
|
|
}
|
|
|
|
|
|
+static void addString(const char* s, const Vector2& pos, float size,
|
|
|
+ const Color4& color) {
|
|
|
+ int index = 0;
|
|
|
+ Vector2 topLeft = pos;
|
|
|
+ while(s[index] != '\0') {
|
|
|
+ Vector2 topRight = topLeft + Vector2(size, 0.0f);
|
|
|
+ Vector2 bottomLeft = topLeft + Vector2(0.0f, -size);
|
|
|
+ Vector2 bottomRight = topRight + Vector2(0.0f, -size);
|
|
|
+
|
|
|
+ int c = s[index];
|
|
|
+ float minX = static_cast<float>(c % 16) / 16.0f + 1.0f / 128.0f;
|
|
|
+ float maxX = minX + 6.0f / 128.0f;
|
|
|
+ float minY = static_cast<float>(c / 16) / 16.0f;
|
|
|
+ float maxY = minY + 1.0f / 16.0f;
|
|
|
+
|
|
|
+ fontBuffer.add(topLeft).add(Vector2(minX, minY)).add(color);
|
|
|
+ fontBuffer.add(bottomLeft).add(Vector2(minX, maxY)).add(color);
|
|
|
+ fontBuffer.add(topRight).add(Vector2(maxX, minY)).add(color);
|
|
|
+ fontBuffer.add(bottomLeft).add(Vector2(minX, maxY)).add(color);
|
|
|
+ fontBuffer.add(topRight).add(Vector2(maxX, minY)).add(color);
|
|
|
+ fontBuffer.add(bottomRight).add(Vector2(maxX, maxY)).add(color);
|
|
|
+
|
|
|
+ topLeft[0] += size;
|
|
|
+ index++;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+struct Player {
|
|
|
+ int id = -1;
|
|
|
+ Color4 color;
|
|
|
+ HashMap<int, int> resources;
|
|
|
+
|
|
|
+ int getResource(Resource r) const {
|
|
|
+ const int* i = resources.search(static_cast<int>(r));
|
|
|
+ return i == nullptr ? 0 : *i;
|
|
|
+ }
|
|
|
+
|
|
|
+ void placeStartSettlement();
|
|
|
+};
|
|
|
+
|
|
|
+static Array<Player, 1> players;
|
|
|
+static int currentPlayer = 0;
|
|
|
+
|
|
|
struct Hexagon {
|
|
|
Resource resource = Resource::NOTHING;
|
|
|
Vector2 mid;
|
|
|
Array<int, 6> corners;
|
|
|
+ int number = 0;
|
|
|
|
|
|
Hexagon() : corners(-1) {
|
|
|
}
|
|
@@ -68,6 +118,19 @@ struct Hexagon {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ void addStringToBuffer() const {
|
|
|
+ if(number == 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ StringBuffer<16> s(number);
|
|
|
+ constexpr float SIZE = 0.1f;
|
|
|
+ addString(s,
|
|
|
+ mid +
|
|
|
+ Vector2(-SIZE * static_cast<float>(s.getLength()), SIZE) *
|
|
|
+ 0.5f,
|
|
|
+ SIZE, Color4(0, 0, 0, 0));
|
|
|
+ }
|
|
|
+
|
|
|
Vector2 getTopCorner() const {
|
|
|
return mid + Vector2(0.0f, RADIUS);
|
|
|
}
|
|
@@ -102,8 +165,13 @@ struct Corner {
|
|
|
ArrayList<int, 3> paths;
|
|
|
|
|
|
void addToBuffer() const {
|
|
|
- addSquare(mid, 0.05f, Color4(255, 255, 255, 255));
|
|
|
+ if(player < 0 || player >= players.getLength()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ addSquare(mid, 0.05f, players[player].color);
|
|
|
}
|
|
|
+
|
|
|
+ bool validForSettlement() const;
|
|
|
};
|
|
|
|
|
|
static Array<Corner, 54> corners;
|
|
@@ -132,9 +200,65 @@ struct Path {
|
|
|
Vector2 getMid() const {
|
|
|
return (to + from) * 0.5f;
|
|
|
}
|
|
|
+
|
|
|
+ const Corner& getOtherCorner(const Corner& c) const {
|
|
|
+ if(&(corners[cornerA]) == &c) {
|
|
|
+ return corners[cornerB];
|
|
|
+ } else if(&(corners[cornerB]) == &c) {
|
|
|
+ return corners[cornerA];
|
|
|
+ }
|
|
|
+ return c;
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
-static List<Path> paths;
|
|
|
+static List<Path> gPaths;
|
|
|
+
|
|
|
+static int getQualityOfNumber(int number) {
|
|
|
+ if(number < 2 || number > 12 || number == 7) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return number < 7 ? number - 1 : 13 - number;
|
|
|
+}
|
|
|
+
|
|
|
+static int findBestCorner() {
|
|
|
+ int current = -1;
|
|
|
+ int quality = 0;
|
|
|
+ for(int i = 0; i < corners.getLength(); i++) {
|
|
|
+ if(!corners[i].validForSettlement()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ int newQuality = 0;
|
|
|
+ for(int h : corners[i].hexagons) {
|
|
|
+ newQuality += getQualityOfNumber(hexagons[h].number);
|
|
|
+ }
|
|
|
+ if(newQuality > quality) {
|
|
|
+ quality = newQuality;
|
|
|
+ current = i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return current;
|
|
|
+}
|
|
|
+
|
|
|
+void Player::placeStartSettlement() {
|
|
|
+ int c = findBestCorner();
|
|
|
+ if(c == -1) {
|
|
|
+ LOG_ERROR("Cannot place start settlement");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ corners[c].player = id;
|
|
|
+}
|
|
|
+
|
|
|
+bool Corner::validForSettlement() const {
|
|
|
+ if(player != -1) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ for(int p : paths) {
|
|
|
+ if(gPaths[p].getOtherCorner(*this).player != -1) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
|
|
|
static void initResources() {
|
|
|
for(int i = 0; i < 4; i++) {
|
|
@@ -150,7 +274,71 @@ static void initResources() {
|
|
|
|
|
|
Random r(0);
|
|
|
for(int i = 0; i < hexagons.getLength(); i++) {
|
|
|
- std::swap(hexagons[i], hexagons[r.next(i, hexagons.getLength() - 1)]);
|
|
|
+ std::swap(hexagons[i].resource,
|
|
|
+ hexagons[r.next(i, hexagons.getLength() - 1)].resource);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static bool invalidNumbers(int a, int b) {
|
|
|
+ a = a == 8 ? 6 : a;
|
|
|
+ b = b == 8 ? 6 : b;
|
|
|
+ return a == b;
|
|
|
+}
|
|
|
+
|
|
|
+static bool invalidNumbersExist() {
|
|
|
+ for(const Corner& c : corners) {
|
|
|
+ if(c.hexagons.getLength() == 2) {
|
|
|
+ int numberA = hexagons[c.hexagons[0]].number;
|
|
|
+ int numberB = hexagons[c.hexagons[1]].number;
|
|
|
+ if(invalidNumbers(numberA, numberB)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ } else if(c.hexagons.getLength() == 3) {
|
|
|
+ int numberA = hexagons[c.hexagons[0]].number;
|
|
|
+ int numberB = hexagons[c.hexagons[1]].number;
|
|
|
+ int numberC = hexagons[c.hexagons[2]].number;
|
|
|
+ if(invalidNumbers(numberA, numberB) ||
|
|
|
+ invalidNumbers(numberA, numberC) ||
|
|
|
+ invalidNumbers(numberB, numberC)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static void initNumbers() {
|
|
|
+ int index = 0;
|
|
|
+ int numbers[] = {2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 3, 4, 5, 6, 8, 9, 10, 11};
|
|
|
+ for(Hexagon& h : hexagons) {
|
|
|
+ if(h.resource != Resource::NOTHING) {
|
|
|
+ h.number = numbers[index++];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Random r(1);
|
|
|
+ for(int i = 0; i < hexagons.getLength(); i++) {
|
|
|
+ if(hexagons[i].resource == Resource::NOTHING) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ int ni = r.next(i, hexagons.getLength() - 1);
|
|
|
+ while(hexagons[ni].resource == Resource::NOTHING) {
|
|
|
+ ni = r.next(i, hexagons.getLength() - 1);
|
|
|
+ }
|
|
|
+ std::swap(hexagons[i].number, hexagons[ni].number);
|
|
|
+ }
|
|
|
+
|
|
|
+ while(invalidNumbersExist()) {
|
|
|
+ int a = r.next(0, hexagons.getLength() - 1);
|
|
|
+ while(hexagons[a].resource == Resource::NOTHING) {
|
|
|
+ a = r.next(0, hexagons.getLength() - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ int b = r.next(0, hexagons.getLength() - 1);
|
|
|
+ while(hexagons[b].resource == Resource::NOTHING) {
|
|
|
+ b = r.next(0, hexagons.getLength() - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ std::swap(hexagons[a].number, hexagons[b].number);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -231,7 +419,7 @@ static void initHexagonCorners() {
|
|
|
|
|
|
static bool doesPathExist(const Path& p) {
|
|
|
Vector2 mid = p.getMid();
|
|
|
- for(const Path& po : paths) {
|
|
|
+ for(const Path& po : gPaths) {
|
|
|
if((mid - po.getMid()).squareLength() < 0.0001f) {
|
|
|
return true;
|
|
|
}
|
|
@@ -252,39 +440,53 @@ static void initPaths() {
|
|
|
if(doesPathExist(p)) {
|
|
|
continue;
|
|
|
}
|
|
|
- paths.add(p);
|
|
|
+ gPaths.add(p);
|
|
|
}
|
|
|
}
|
|
|
LOG_INFO(
|
|
|
- StringBuffer<256>("Got ").append(paths.getLength()).append(" paths"));
|
|
|
+ StringBuffer<256>("Got ").append(gPaths.getLength()).append(" paths"));
|
|
|
}
|
|
|
|
|
|
static void initCornerPaths() {
|
|
|
for(int c = 0; c < corners.getLength(); c++) {
|
|
|
- for(int i = 0; i < paths.getLength(); i++) {
|
|
|
- Vector2 mid = paths[i].getMid();
|
|
|
+ for(int i = 0; i < gPaths.getLength(); i++) {
|
|
|
+ Vector2 mid = gPaths[i].getMid();
|
|
|
if((corners[c].mid - mid).length() >= RADIUS) {
|
|
|
continue;
|
|
|
}
|
|
|
if(corners[c].paths.add(i)) {
|
|
|
LOG_WARNING("Corner path overflow");
|
|
|
}
|
|
|
- if(paths[i].cornerA == -1) {
|
|
|
- paths[i].cornerA = c;
|
|
|
- } else if(paths[i].cornerB == -1) {
|
|
|
- paths[i].cornerB = c;
|
|
|
+ if(gPaths[i].cornerA == -1) {
|
|
|
+ gPaths[i].cornerA = c;
|
|
|
+ } else if(gPaths[i].cornerB == -1) {
|
|
|
+ gPaths[i].cornerB = c;
|
|
|
} else {
|
|
|
LOG_WARNING("Path got too much corners");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- for(const Path& p : paths) {
|
|
|
+ for(const Path& p : gPaths) {
|
|
|
if(p.cornerA == -1 || p.cornerB == -1) {
|
|
|
LOG_WARNING("Path is missing corners");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void initPlayers() {
|
|
|
+ for(int i = 0; i < players.getLength(); i++) {
|
|
|
+ players[i].id = i;
|
|
|
+ }
|
|
|
+ players[0].color = Color4(255, 0, 0, 255);
|
|
|
+
|
|
|
+ for(int i = 0; i < players.getLength(); i++) {
|
|
|
+ players[i].placeStartSettlement();
|
|
|
+ }
|
|
|
+ for(int i = players.getLength() - 1; i >= 0; i--) {
|
|
|
+ players[i].placeStartSettlement();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void init() {
|
|
|
initResources();
|
|
|
initHexagonMid();
|
|
@@ -292,6 +494,8 @@ static void init() {
|
|
|
initHexagonCorners();
|
|
|
initPaths();
|
|
|
initCornerPaths();
|
|
|
+ initNumbers();
|
|
|
+ initPlayers();
|
|
|
}
|
|
|
|
|
|
static void buildBuffer() {
|
|
@@ -299,16 +503,45 @@ static void buildBuffer() {
|
|
|
for(const Hexagon& h : hexagons) {
|
|
|
h.addToBuffer();
|
|
|
}
|
|
|
+ for(const Path& p : gPaths) {
|
|
|
+ p.addToBuffer();
|
|
|
+ }
|
|
|
for(const Corner& c : corners) {
|
|
|
c.addToBuffer();
|
|
|
}
|
|
|
- for(const Path& p : paths) {
|
|
|
- p.addToBuffer();
|
|
|
+ vertexBuffer.setData(buffer, GL::BufferUsage::DYNAMIC);
|
|
|
+}
|
|
|
+
|
|
|
+static void buildFontBuffer() {
|
|
|
+ fontBuffer.clear();
|
|
|
+ for(const Hexagon& h : hexagons) {
|
|
|
+ h.addStringToBuffer();
|
|
|
}
|
|
|
- vertexBuffer.setData(buffer, GL::BufferUsage::STATIC);
|
|
|
+ addString(StringBuffer<16>(currentPlayer), Vector2(-0.9f, 0.9f), 0.1f,
|
|
|
+ players[currentPlayer].color);
|
|
|
+ fontVertexBuffer.setData(fontBuffer, GL::BufferUsage::DYNAMIC);
|
|
|
+}
|
|
|
+
|
|
|
+static void rebuild() {
|
|
|
+ buildBuffer();
|
|
|
+ buildFontBuffer();
|
|
|
+}
|
|
|
+
|
|
|
+static int waitTicks = 0;
|
|
|
+
|
|
|
+static void doTurn() {
|
|
|
}
|
|
|
|
|
|
static void tick() {
|
|
|
+ waitTicks++;
|
|
|
+ if(waitTicks < 100) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ waitTicks = 0;
|
|
|
+
|
|
|
+ doTurn();
|
|
|
+ currentPlayer = (currentPlayer + 1) % players.getLength();
|
|
|
+ rebuild();
|
|
|
}
|
|
|
|
|
|
static void render(float lag) {
|
|
@@ -316,6 +549,13 @@ static void render(float lag) {
|
|
|
shader.use();
|
|
|
vertexBuffer.draw(vertexBuffer.getSize() /
|
|
|
static_cast<int>(sizeof(Vector2) + sizeof(Color4)));
|
|
|
+ fontShader.use();
|
|
|
+ fontTexture.bindTo(0);
|
|
|
+ GL::enableBlending();
|
|
|
+
|
|
|
+ fontVertexBuffer.draw(
|
|
|
+ vertexBuffer.getSize() /
|
|
|
+ static_cast<int>(sizeof(Vector2) * 2 + sizeof(Color4)));
|
|
|
(void)lag;
|
|
|
}
|
|
|
|
|
@@ -335,9 +575,21 @@ int main() {
|
|
|
e.message.printLine();
|
|
|
return 0;
|
|
|
}
|
|
|
+ e = fontShader.compile("resources/font.vs", "resources/font.fs");
|
|
|
+ if(e.has()) {
|
|
|
+ e.message.printLine();
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ e = fontTexture.load("resources/font8x8.png", 1);
|
|
|
+ if(e.has()) {
|
|
|
+ e.message.printLine();
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
vertexBuffer.init(VertexBuffer::Attributes().addFloat(2).addColor4());
|
|
|
+ fontVertexBuffer.init(
|
|
|
+ VertexBuffer::Attributes().addFloat(2).addFloat(2).addColor4());
|
|
|
init();
|
|
|
- buildBuffer();
|
|
|
+ rebuild();
|
|
|
Window::show();
|
|
|
Window::run<shouldRun, tick, render>(10'000'000);
|
|
|
return 0;
|