#include #include #include "data/Array.h" #include "data/ArrayList.h" #include "data/List.h" #include "math/Vector.h" #include "rendering/Shader.h" #include "rendering/VertexBuffer.h" #include "rendering/Window.h" #include "utils/Color.h" #include "utils/Logger.h" #include "utils/Random.h" static Shader shader; static Buffer buffer; static VertexBuffer vertexBuffer; enum class Resource { NOTHING, CLAY, ORE, WHEAT, SHEEP, WOOD }; static const Color4 RESOURCE_COLOR[] = { Color4(160, 120, 0, 255), Color4(220, 96, 0, 255), Color4(128, 128, 128, 255), Color4(255, 200, 0, 255), Color4(40, 200, 40, 255), Color4(0, 120, 0, 255)}; static const Vector2 MIN_BORDER(-0.9f, -0.9f); static const Vector2 MAX_BORDER(0.9f, 0.9f); static const Vector2 AREA = MAX_BORDER - MIN_BORDER; static const float RADIUS = AREA[0] / (5.0f * 2.0f * cosf(30.0f * static_cast(M_PI) / 180.0f)); static const float LINE_LENGTH = RADIUS * sinf(30.0f * static_cast(M_PI) / 180.0f) * 2.0f; static const float WIDTH = AREA[0] / 5.0f; static void addTriangle(const Vector2 a, const Vector2 b, const Vector2 c, const Color4& color) { buffer.add(a).add(color); buffer.add(b).add(color); buffer.add(c).add(color); } static void addSquare(const Vector2 mid, float size, const Color4& color) { size *= 0.5f; Vector2 a = mid + Vector2(-size, size); Vector2 b = mid + Vector2(size, size); Vector2 c = mid + Vector2(-size, -size); Vector2 d = mid + Vector2(size, -size); addTriangle(a, b, c, color); addTriangle(b, d, c, color); } struct Hexagon { Resource resource = Resource::NOTHING; Vector2 mid; Array corners; Hexagon() : corners(-1) { } void addToBuffer() const { Color4 color = RESOURCE_COLOR[static_cast(resource)]; float angle = 2.0f * static_cast(M_PI) / 6.0f; for(int i = 0; i < 6; i++) { Vector2 a(sinf(angle * static_cast(i)) * RADIUS, cosf(angle * static_cast(i)) * RADIUS); Vector2 b(sinf(angle * static_cast(i + 1)) * RADIUS, cosf(angle * static_cast(i + 1)) * RADIUS); addTriangle(a + mid, b + mid, mid, color); } } Vector2 getTopCorner() const { return mid + Vector2(0.0f, RADIUS); } Vector2 getBottomCorner() const { return mid - Vector2(0.0f, RADIUS); } Vector2 getLeftTopCorner() const { return mid - Vector2(WIDTH, -LINE_LENGTH) * 0.5f; } Vector2 getLeftBottomCorner() const { return mid - Vector2(WIDTH, LINE_LENGTH) * 0.5f; } Vector2 getRightTopCorner() const { return mid + Vector2(WIDTH, LINE_LENGTH) * 0.5f; } Vector2 getRightBottomCorner() const { return mid + Vector2(WIDTH, -LINE_LENGTH) * 0.5f; } }; static Array hexagons; struct Corner { int player = -1; Vector2 mid; ArrayList hexagons; ArrayList paths; void addToBuffer() const { addSquare(mid, 0.05f, Color4(255, 255, 255, 255)); } }; static Array corners; struct Path { int player = -1; Vector2 from; Vector2 to; int cornerA = -1; int cornerB = -1; void addToBuffer() const { Vector2 diff = to - from; Vector2 normal(diff[1], -diff[0]); normal.normalize(); normal *= 0.01f; Vector2 a = from + normal; Vector2 b = from - normal; Vector2 c = to + normal; Vector2 d = to - normal; addTriangle(a, c, b, Color4(255, 255, 255, 255)); addTriangle(c, d, b, Color4(255, 255, 255, 255)); } Vector2 getMid() const { return (to + from) * 0.5f; } }; static List paths; static void initResources() { for(int i = 0; i < 4; i++) { hexagons[i].resource = Resource::WHEAT; hexagons[i + 4].resource = Resource::WOOD; hexagons[i + 8].resource = Resource::SHEEP; } for(int i = 0; i < 3; i++) { hexagons[i + 12].resource = Resource::ORE; hexagons[i + 15].resource = Resource::CLAY; } hexagons[18].resource = Resource::NOTHING; Random r(0); for(int i = 0; i < hexagons.getLength(); i++) { std::swap(hexagons[i], hexagons[r.next(i, hexagons.getLength() - 1)]); } } static void initHexagonMid() { Vector2 mid = MIN_BORDER + AREA * 0.5f; for(int i = 0; i < 3; i++) { hexagons[i].mid = mid - Vector2(WIDTH * static_cast(i - 1), -RADIUS * 2.0f - LINE_LENGTH); hexagons[i + 16].mid = mid - Vector2(WIDTH * static_cast(i - 1), RADIUS * 2.0f + LINE_LENGTH); } for(int i = 3; i < 7; i++) { hexagons[i].mid = mid - Vector2(WIDTH * (static_cast(i) - 4.5f), -RADIUS - LINE_LENGTH * 0.5f); hexagons[i + 9].mid = mid - Vector2(WIDTH * (static_cast(i) - 4.5f), RADIUS + LINE_LENGTH * 0.5f); } for(int i = 7; i < 12; i++) { hexagons[i].mid = mid - Vector2(WIDTH * static_cast(i - 9), 0.0f); } } static void initCornerMid() { for(int i = 0; i < hexagons.getLength(); i++) { corners[i].mid = hexagons[i].getLeftTopCorner(); corners[i + 19].mid = hexagons[i].getLeftBottomCorner(); } corners[38].mid = hexagons[0].getTopCorner(); corners[39].mid = hexagons[1].getTopCorner(); corners[40].mid = hexagons[2].getTopCorner(); corners[41].mid = hexagons[16].getBottomCorner(); corners[42].mid = hexagons[17].getBottomCorner(); corners[43].mid = hexagons[18].getBottomCorner(); corners[44].mid = hexagons[0].getRightTopCorner(); corners[45].mid = hexagons[0].getRightBottomCorner(); corners[46].mid = hexagons[3].getRightTopCorner(); corners[47].mid = hexagons[3].getRightBottomCorner(); corners[48].mid = hexagons[7].getRightTopCorner(); corners[49].mid = hexagons[7].getRightBottomCorner(); corners[50].mid = hexagons[12].getRightTopCorner(); corners[51].mid = hexagons[12].getRightBottomCorner(); corners[52].mid = hexagons[16].getRightTopCorner(); corners[53].mid = hexagons[16].getRightBottomCorner(); } static int findCorner(const Vector2& mid) { for(int i = 0; i < corners.getLength(); i++) { if((mid - corners[i].mid).squareLength() < 0.00001f) { return i; } } return -1; } static void initHexagonCorners() { for(Hexagon& h : hexagons) { h.corners[0] = findCorner(h.getTopCorner()); h.corners[1] = findCorner(h.getBottomCorner()); h.corners[2] = findCorner(h.getLeftTopCorner()); h.corners[3] = findCorner(h.getLeftBottomCorner()); h.corners[4] = findCorner(h.getRightTopCorner()); h.corners[5] = findCorner(h.getRightBottomCorner()); } for(int i = 0; i < hexagons.getLength(); i++) { for(int c = 0; c < hexagons[c].corners.getLength(); c++) { if(hexagons[i].corners[c] == -1) { LOG_WARNING("Could not find a hexagon corner"); } else { if(corners[hexagons[i].corners[c]].hexagons.add(i)) { LOG_WARNING("Corner hexagon overflow"); } } } } } static bool doesPathExist(const Path& p) { Vector2 mid = p.getMid(); for(const Path& po : paths) { if((mid - po.getMid()).squareLength() < 0.0001f) { return true; } } return false; } static void initPaths() { for(int i = 0; i < corners.getLength(); i++) { for(int k = 0; k < corners.getLength(); k++) { if(i == k || (corners[i].mid - corners[k].mid).length() >= LINE_LENGTH * 1.01f) { continue; } Path p; p.from = corners[i].mid; p.to = corners[k].mid; if(doesPathExist(p)) { continue; } paths.add(p); } } LOG_INFO( StringBuffer<256>("Got ").append(paths.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(); 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; } else { LOG_WARNING("Path got too much corners"); } } } for(const Path& p : paths) { if(p.cornerA == -1 || p.cornerB == -1) { LOG_WARNING("Path is missing corners"); } } } static void init() { initResources(); initHexagonMid(); initCornerMid(); initHexagonCorners(); initPaths(); initCornerPaths(); } static void buildBuffer() { buffer.clear(); for(const Hexagon& h : hexagons) { h.addToBuffer(); } for(const Corner& c : corners) { c.addToBuffer(); } for(const Path& p : paths) { p.addToBuffer(); } vertexBuffer.setData(buffer, GL::BufferUsage::STATIC); } static void tick() { } static void render(float lag) { GL::setViewport(Window::getSize()[0], Window::getSize()[1]); shader.use(); vertexBuffer.draw(vertexBuffer.getSize() / static_cast(sizeof(Vector2) + sizeof(Color4))); (void)lag; } static bool shouldRun() { return !Window::shouldClose(); } int main() { Error e = Window::open( Window::Options(4, 6, IntVector2(400, 300), false, "Catan Simulator")); if(e.has()) { e.message.printLine(); return 0; } e = shader.compile("resources/vertex.vs", "resources/fragment.fs"); if(e.has()) { e.message.printLine(); return 0; } vertexBuffer.init(VertexBuffer::Attributes().addFloat(2).addColor4()); init(); buildBuffer(); Window::show(); Window::run(10'000'000); return 0; }