Browse Source

Font rendering, number placement, find best start spot, player basics

Kajetan Johannes Hammerle 1 year ago
parent
commit
c07885bd9b
4 changed files with 295 additions and 17 deletions
  1. 269 17
      Main.cpp
  2. 12 0
      resources/font.fs
  3. 14 0
      resources/font.vs
  4. BIN
      resources/font8x8.png

+ 269 - 17
Main.cpp

@@ -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;

+ 12 - 0
resources/font.fs

@@ -0,0 +1,12 @@
+#version 460
+
+uniform sampler2D samp;
+
+in vec2 varTex;
+in vec4 varColor;
+
+out vec4 color;
+
+void main(void) {
+    color = vec4(varColor.xyz, texture(samp, varTex).r);
+}

+ 14 - 0
resources/font.vs

@@ -0,0 +1,14 @@
+#version 460
+
+layout (location = 0) in vec2 pos;
+layout (location = 1) in vec2 tex;
+layout (location = 2) in vec4 color;
+
+out vec2 varTex;
+out vec4 varColor;
+
+void main(void) {
+    gl_Position = vec4(pos, 0.0, 1.0);
+    varTex = tex;
+    varColor = color;
+}

BIN
resources/font8x8.png