123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935 |
- #include <cmath>
- #include <cstdio>
- #include "GLFW/glfw3.h"
- #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"
- #include "utils/Logger.h"
- #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;
- static Random rng(0);
- static int restartButton = 0;
- static int restartMapButton = 0;
- static int turns = 0;
- enum class Resource { NOTHING, CLAY, ORE, WHEAT, SHEEP, WOOD };
- static const char* RESOURCE_NAME[] = {"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<float>(M_PI) / 180.0f));
- static const float LINE_LENGTH =
- RADIUS * sinf(30.0f * static_cast<float>(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);
- }
- 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++;
- }
- }
- static int getQualityOfNumber(int number) {
- if(number < 2 || number > 12 || number == 7) {
- return 0;
- }
- return number < 7 ? number - 1 : 13 - number;
- }
- struct Player {
- int id = -1;
- Color4 color;
- HashMap<int, int> resources;
- int settlements = 5;
- int paths = 15;
- int cities = 4;
- Player() {
- giveResource(Resource::CLAY, 0);
- giveResource(Resource::ORE, 0);
- giveResource(Resource::SHEEP, 0);
- giveResource(Resource::WHEAT, 0);
- giveResource(Resource::WOOD, 0);
- }
- int getResource(Resource r) const {
- const int* i = resources.search(static_cast<int>(r));
- return i == nullptr ? 0 : *i;
- }
- void placeStartSettlement();
- void giveResource(Resource r, int amount) {
- LOG_INFO(StringBuffer<256>("Player ")
- .append(id)
- .append(" gets ")
- .append(amount)
- .append(" ")
- .append(RESOURCE_NAME[static_cast<int>(r)]));
- int* i = resources.search(static_cast<int>(r));
- if(i == nullptr) {
- resources.tryEmplace(static_cast<int>(r), amount);
- } else {
- *i += amount;
- }
- LOG_INFO(StringBuffer<256>(resources));
- }
- void removeResource(Resource r, int amount) {
- LOG_INFO(StringBuffer<256>("Player ")
- .append(id)
- .append(" pays ")
- .append(amount)
- .append(" ")
- .append(RESOURCE_NAME[static_cast<int>(r)]));
- int* i = resources.search(static_cast<int>(r));
- if(i == nullptr) {
- return;
- }
- *i -= amount;
- LOG_INFO(StringBuffer<256>(resources));
- }
- void takeTurn();
- bool swap() {
- int max = 0;
- int maxResource = 0;
- for(const auto& entry : resources.entries()) {
- if(entry.value > max) {
- max = entry.value;
- maxResource = entry.getKey();
- }
- }
- int min = max;
- int minResource = 0;
- for(const auto& entry : resources.entries()) {
- if(entry.value < min) {
- min = entry.value;
- minResource = entry.getKey();
- }
- }
- if(max >= 4) {
- removeResource(static_cast<Resource>(maxResource), 4);
- giveResource(static_cast<Resource>(minResource), 1);
- return true;
- }
- return false;
- }
- int getPoints() const;
- void reset() {
- removeResource(Resource::CLAY, getResource(Resource::CLAY));
- removeResource(Resource::ORE, getResource(Resource::ORE));
- removeResource(Resource::SHEEP, getResource(Resource::SHEEP));
- removeResource(Resource::WHEAT, getResource(Resource::WHEAT));
- removeResource(Resource::WOOD, getResource(Resource::WOOD));
- settlements = 5;
- paths = 15;
- cities = 4;
- }
- };
- static Array<Player, 4> players;
- static int currentPlayer = 0;
- struct Hexagon {
- Resource resource = Resource::NOTHING;
- Vector2 mid;
- Array<int, 6> corners;
- int number = 0;
- Hexagon() : corners(-1) {
- }
- void addToBuffer() const {
- Color4 color = RESOURCE_COLOR[static_cast<int>(resource)];
- float angle = 2.0f * static_cast<float>(M_PI) / 6.0f;
- for(int i = 0; i < 6; i++) {
- Vector2 a(sinf(angle * static_cast<float>(i)) * RADIUS,
- cosf(angle * static_cast<float>(i)) * RADIUS);
- Vector2 b(sinf(angle * static_cast<float>(i + 1)) * RADIUS,
- cosf(angle * static_cast<float>(i + 1)) * RADIUS);
- addTriangle(a + mid, b + mid, mid, color);
- }
- }
- 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);
- }
- 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<Hexagon, 19> gHexagons;
- struct Corner {
- int player = -1;
- bool city = false;
- Vector2 mid;
- ArrayList<int, 3> hexagons;
- ArrayList<int, 3> paths;
- void addToBuffer() const {
- if(player < 0 || player >= players.getLength()) {
- return;
- }
- addSquare(mid, 0.075f * (static_cast<float>(city) + 1.0f),
- players[player].color);
- }
- bool validForSettlement() const;
- bool doesAnyPathBelongToPlayer(int player) const;
- bool isFree() const {
- return player < 0 || player >= players.getLength();
- }
- void reset() {
- player = -1;
- city = false;
- }
- int getQuality() {
- int q = 0;
- for(int h : hexagons) {
- q += getQualityOfNumber(gHexagons[h].number);
- }
- return q;
- }
- };
- static Array<Corner, 54> gCorners;
- struct Path {
- int player = -1;
- Vector2 from;
- Vector2 to;
- int cornerA = -1;
- int cornerB = -1;
- void addToBuffer() const {
- if(player < 0 || player >= players.getLength()) {
- return;
- }
- 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, players[player].color);
- addTriangle(c, d, b, players[player].color);
- }
- Vector2 getMid() const {
- return (to + from) * 0.5f;
- }
- const Corner& getOtherCorner(const Corner& c) const {
- if(&(gCorners[cornerA]) == &c) {
- return gCorners[cornerB];
- } else if(&(gCorners[cornerB]) == &c) {
- return gCorners[cornerA];
- }
- return c;
- }
- bool isFree() const {
- return player < 0 || player >= players.getLength();
- }
- void reset() {
- player = -1;
- }
- };
- static List<Path> gPaths;
- static int findBestCorner() {
- int current = -1;
- int quality = 0;
- for(int i = 0; i < gCorners.getLength(); i++) {
- if(!gCorners[i].validForSettlement()) {
- continue;
- }
- int newQuality = 0;
- for(int h : gCorners[i].hexagons) {
- newQuality += getQualityOfNumber(gHexagons[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;
- }
- gCorners[c].player = id;
- settlements--;
- while(true) {
- int p =
- gCorners[c].paths[rng.next(0, gCorners[c].paths.getLength() - 1)];
- if(gPaths[p].player == -1) {
- gPaths[p].player = id;
- paths--;
- return;
- }
- }
- }
- static List<int> getPossiblePaths(int player) {
- List<int> paths;
- for(int i = 0; i < gPaths.getLength(); i++) {
- if(!gPaths[i].isFree()) {
- continue;
- }
- const Corner& cornerA = gCorners[gPaths[i].cornerA];
- const Corner& cornerB = gCorners[gPaths[i].cornerB];
- if(cornerA.player == player || cornerB.player == player ||
- cornerA.doesAnyPathBelongToPlayer(player) ||
- cornerB.doesAnyPathBelongToPlayer(player)) {
- paths.add(i);
- }
- }
- return paths;
- }
- static List<int> getPossibleCorners(int player) {
- List<int> corners;
- for(int i = 0; i < gCorners.getLength(); i++) {
- if(!gCorners[i].isFree()) {
- continue;
- }
- if(gCorners[i].doesAnyPathBelongToPlayer(player) &&
- gCorners[i].validForSettlement()) {
- corners.add(i);
- }
- }
- return corners;
- }
- static List<int> getPossibleCityCorners(int player) {
- List<int> corners;
- for(int i = 0; i < gCorners.getLength(); i++) {
- if(gCorners[i].player == player && !gCorners[i].city) {
- corners.add(i);
- }
- }
- return corners;
- }
- void Player::takeTurn() {
- while(true) {
- int clay = getResource(Resource::CLAY);
- int ore = getResource(Resource::ORE);
- int sheep = getResource(Resource::SHEEP);
- int wheat = getResource(Resource::WHEAT);
- int wood = getResource(Resource::WOOD);
- if(wood >= 1 && clay >= 1 && wheat >= 1 && sheep >= 1 &&
- settlements > 0) {
- LOG_INFO("I can build a settlement.");
- List<int> possibleCorners = getPossibleCorners(id);
- if(possibleCorners.getLength() >= 1) {
- int index = rng.next(0, possibleCorners.getLength() - 1);
- gCorners[possibleCorners[index]].player = id;
- removeResource(Resource::CLAY, 1);
- removeResource(Resource::WOOD, 1);
- removeResource(Resource::SHEEP, 1);
- removeResource(Resource::WHEAT, 1);
- LOG_INFO("I build a settlement.");
- settlements--;
- continue;
- }
- }
- if(wood >= 1 && clay >= 1 && paths > 0) {
- LOG_INFO("I can build a path.");
- List<int> possiblePaths = getPossiblePaths(id);
- if(possiblePaths.getLength() >= 1 &&
- getPossibleCorners(id).getLength() == 0) {
- int index = rng.next(0, possiblePaths.getLength() - 1);
- gPaths[possiblePaths[index]].player = id;
- removeResource(Resource::CLAY, 1);
- removeResource(Resource::WOOD, 1);
- LOG_INFO("I build a path.");
- paths--;
- continue;
- }
- }
- if(ore >= 3 && wheat >= 2 && cities > 0) {
- LOG_INFO("I can build a city.");
- List<int> possibleCityCorners = getPossibleCityCorners(id);
- if(possibleCityCorners.getLength() >= 1) {
- int bestIndex = -1;
- int quality = -1;
- for(int index : possibleCityCorners) {
- int newQuality = gCorners[index].getQuality();
- if(newQuality > quality) {
- bestIndex = index;
- quality = newQuality;
- }
- }
- gCorners[bestIndex].city = true;
- removeResource(Resource::ORE, 3);
- removeResource(Resource::WHEAT, 2);
- LOG_INFO("I build a city.");
- cities--;
- settlements++;
- continue;
- }
- }
- if(swap()) {
- continue;
- }
- break;
- }
- }
- bool Corner::doesAnyPathBelongToPlayer(int playerId) const {
- for(int p : paths) {
- if(gPaths[p].player == playerId) {
- return true;
- }
- }
- return false;
- }
- bool Corner::validForSettlement() const {
- if(!isFree()) {
- return false;
- }
- for(int p : paths) {
- if(!gPaths[p].getOtherCorner(*this).isFree()) {
- return false;
- }
- }
- return true;
- }
- int Player::getPoints() const {
- return (4 - cities) * 2 + 5 - settlements;
- }
- static void initResources() {
- for(int i = 0; i < 4; i++) {
- gHexagons[i].resource = Resource::WHEAT;
- gHexagons[i + 4].resource = Resource::WOOD;
- gHexagons[i + 8].resource = Resource::SHEEP;
- }
- for(int i = 0; i < 3; i++) {
- gHexagons[i + 12].resource = Resource::ORE;
- gHexagons[i + 15].resource = Resource::CLAY;
- }
- gHexagons[18].resource = Resource::NOTHING;
- for(int i = 0; i < gHexagons.getLength(); i++) {
- std::swap(gHexagons[i].resource,
- gHexagons[rng.next(i, gHexagons.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 : gCorners) {
- if(c.hexagons.getLength() == 2) {
- int numberA = gHexagons[c.hexagons[0]].number;
- int numberB = gHexagons[c.hexagons[1]].number;
- if(invalidNumbers(numberA, numberB)) {
- return true;
- }
- } else if(c.hexagons.getLength() == 3) {
- int numberA = gHexagons[c.hexagons[0]].number;
- int numberB = gHexagons[c.hexagons[1]].number;
- int numberC = gHexagons[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 : gHexagons) {
- if(h.resource != Resource::NOTHING) {
- h.number = numbers[index++];
- } else {
- h.number = 0;
- }
- }
- for(int i = 0; i < gHexagons.getLength(); i++) {
- if(gHexagons[i].resource == Resource::NOTHING) {
- continue;
- }
- int ni = rng.next(i, gHexagons.getLength() - 1);
- while(gHexagons[ni].resource == Resource::NOTHING) {
- ni = rng.next(i, gHexagons.getLength() - 1);
- }
- std::swap(gHexagons[i].number, gHexagons[ni].number);
- }
- while(invalidNumbersExist()) {
- int a = rng.next(0, gHexagons.getLength() - 1);
- while(gHexagons[a].resource == Resource::NOTHING) {
- a = rng.next(0, gHexagons.getLength() - 1);
- }
- int b = rng.next(0, gHexagons.getLength() - 1);
- while(gHexagons[b].resource == Resource::NOTHING) {
- b = rng.next(0, gHexagons.getLength() - 1);
- }
- std::swap(gHexagons[a].number, gHexagons[b].number);
- }
- }
- static void initHexagonMid() {
- Vector2 mid = MIN_BORDER + AREA * 0.5f;
- for(int i = 0; i < 3; i++) {
- gHexagons[i].mid = mid - Vector2(WIDTH * static_cast<float>(i - 1),
- -RADIUS * 2.0f - LINE_LENGTH);
- gHexagons[i + 16].mid = mid - Vector2(WIDTH * static_cast<float>(i - 1),
- RADIUS * 2.0f + LINE_LENGTH);
- }
- for(int i = 3; i < 7; i++) {
- gHexagons[i].mid = mid - Vector2(WIDTH * (static_cast<float>(i) - 4.5f),
- -RADIUS - LINE_LENGTH * 0.5f);
- gHexagons[i + 9].mid =
- mid - Vector2(WIDTH * (static_cast<float>(i) - 4.5f),
- RADIUS + LINE_LENGTH * 0.5f);
- }
- for(int i = 7; i < 12; i++) {
- gHexagons[i].mid =
- mid - Vector2(WIDTH * static_cast<float>(i - 9), 0.0f);
- }
- }
- static void initCornerMid() {
- for(int i = 0; i < gHexagons.getLength(); i++) {
- gCorners[i].mid = gHexagons[i].getLeftTopCorner();
- gCorners[i + 19].mid = gHexagons[i].getLeftBottomCorner();
- }
- gCorners[38].mid = gHexagons[0].getTopCorner();
- gCorners[39].mid = gHexagons[1].getTopCorner();
- gCorners[40].mid = gHexagons[2].getTopCorner();
- gCorners[41].mid = gHexagons[16].getBottomCorner();
- gCorners[42].mid = gHexagons[17].getBottomCorner();
- gCorners[43].mid = gHexagons[18].getBottomCorner();
- gCorners[44].mid = gHexagons[0].getRightTopCorner();
- gCorners[45].mid = gHexagons[0].getRightBottomCorner();
- gCorners[46].mid = gHexagons[3].getRightTopCorner();
- gCorners[47].mid = gHexagons[3].getRightBottomCorner();
- gCorners[48].mid = gHexagons[7].getRightTopCorner();
- gCorners[49].mid = gHexagons[7].getRightBottomCorner();
- gCorners[50].mid = gHexagons[12].getRightTopCorner();
- gCorners[51].mid = gHexagons[12].getRightBottomCorner();
- gCorners[52].mid = gHexagons[16].getRightTopCorner();
- gCorners[53].mid = gHexagons[16].getRightBottomCorner();
- }
- static int findCorner(const Vector2& mid) {
- for(int i = 0; i < gCorners.getLength(); i++) {
- if((mid - gCorners[i].mid).squareLength() < 0.00001f) {
- return i;
- }
- }
- return -1;
- }
- static void initHexagongCorners() {
- for(Hexagon& h : gHexagons) {
- 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 < gHexagons.getLength(); i++) {
- for(int c = 0; c < gHexagons[c].corners.getLength(); c++) {
- if(gHexagons[i].corners[c] == -1) {
- LOG_WARNING("Could not find a hexagon corner");
- } else {
- if(gCorners[gHexagons[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 : gPaths) {
- if((mid - po.getMid()).squareLength() < 0.0001f) {
- return true;
- }
- }
- return false;
- }
- static void initPaths() {
- for(int i = 0; i < gCorners.getLength(); i++) {
- for(int k = 0; k < gCorners.getLength(); k++) {
- if(i == k || (gCorners[i].mid - gCorners[k].mid).length() >=
- LINE_LENGTH * 1.01f) {
- continue;
- }
- Path p;
- p.from = gCorners[i].mid;
- p.to = gCorners[k].mid;
- if(doesPathExist(p)) {
- continue;
- }
- gPaths.add(p);
- }
- }
- LOG_INFO(
- StringBuffer<256>("Got ").append(gPaths.getLength()).append(" paths"));
- }
- static void initCornerPaths() {
- for(int c = 0; c < gCorners.getLength(); c++) {
- for(int i = 0; i < gPaths.getLength(); i++) {
- Vector2 mid = gPaths[i].getMid();
- if((gCorners[c].mid - mid).length() >= RADIUS) {
- continue;
- }
- if(gCorners[c].paths.add(i)) {
- LOG_WARNING("Corner path overflow");
- }
- 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 gCorners");
- }
- }
- }
- for(const Path& p : gPaths) {
- if(p.cornerA == -1 || p.cornerB == -1) {
- LOG_WARNING("Path is missing gCorners");
- }
- }
- }
- static void initPlayers() {
- static const Color4 PLAYER_COLOR[] = {
- Color4(255, 0, 0, 255), Color4(0, 0, 255, 255),
- Color4(255, 128, 0, 255), Color4(255, 255, 255, 255)};
- for(int i = 0; i < players.getLength(); i++) {
- players[i].id = i;
- players[i].color = PLAYER_COLOR[i];
- }
- }
- static void placeStartSettlement() {
- 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();
- initCornerMid();
- initHexagongCorners();
- initPaths();
- initCornerPaths();
- initNumbers();
- initPlayers();
- placeStartSettlement();
- }
- static void buildBuffer() {
- buffer.clear();
- for(const Hexagon& h : gHexagons) {
- h.addToBuffer();
- }
- for(const Path& p : gPaths) {
- p.addToBuffer();
- }
- for(const Corner& c : gCorners) {
- c.addToBuffer();
- }
- vertexBuffer.setData(buffer, GL::BufferUsage::DYNAMIC);
- }
- static void buildFontBuffer() {
- fontBuffer.clear();
- for(const Hexagon& h : gHexagons) {
- h.addStringToBuffer();
- }
- for(int i = 0; i < players.getLength(); i++) {
- StringBuffer<16> s(players[i].getPoints());
- if(i == currentPlayer) {
- s.append(" <");
- }
- addString(s,
- Vector2(-0.9f, 0.9f) -
- Vector2(0.0f, 0.1f * static_cast<float>(i)),
- 0.1f, players[i].color);
- }
- addString(StringBuffer<16>(turns), Vector2(0.6f, 0.9f), 0.1f,
- Color4(255, 255, 255, 255));
- fontVertexBuffer.setData(fontBuffer, GL::BufferUsage::DYNAMIC);
- }
- static void rebuild() {
- buildBuffer();
- buildFontBuffer();
- }
- static void giveResources(int dice) {
- for(const Hexagon& h : gHexagons) {
- if(h.number != dice) {
- continue;
- }
- for(int c : h.corners) {
- const Corner& corner = gCorners[c];
- if(corner.player < 0 || corner.player >= players.getLength()) {
- continue;
- }
- players[corner.player].giveResource(h.resource, 1 + corner.city);
- }
- }
- }
- static void doTurn() {
- int dice = rng.next(1, 6) + rng.next(1, 6);
- LOG_INFO(StringBuffer<256>("Dice: ").append(dice));
- giveResources(dice);
- players[currentPlayer].takeTurn();
- }
- static int waitTicks = 0;
- static bool checkForWinner() {
- bool won = false;
- for(const Player& p : players) {
- if(p.getPoints() >= 10) {
- return true;
- }
- }
- return won;
- }
- static void restart() {
- for(Corner& c : gCorners) {
- c.reset();
- }
- for(Path& p : gPaths) {
- p.reset();
- }
- for(Player& p : players) {
- p.reset();
- }
- currentPlayer = 0;
- turns = 0;
- placeStartSettlement();
- }
- static void restartMap() {
- restart();
- initResources();
- initNumbers();
- }
- static void tick() {
- if(Window::Controls::wasReleased(restartButton)) {
- restart();
- }
- if(Window::Controls::wasReleased(restartMapButton)) {
- restartMap();
- }
- waitTicks++;
- if(waitTicks < 1) {
- return;
- }
- waitTicks = 0;
- if(checkForWinner()) {
- return;
- }
- turns++;
- doTurn();
- rebuild();
- currentPlayer = (currentPlayer + 1) % players.getLength();
- }
- static void render(float lag) {
- GL::setViewport(Window::getSize()[0], Window::getSize()[1]);
- GL::clear();
- 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;
- }
- 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;
- }
- 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());
- restartButton = Window::Controls::add("Restart");
- Window::Controls::bindKey(restartButton, GLFW_KEY_R);
- restartMapButton = Window::Controls::add("Restart Map");
- Window::Controls::bindKey(restartMapButton, GLFW_KEY_M);
- init();
- rebuild();
- Window::show();
- Window::run<shouldRun, tick, render>(10'000'000);
- return 0;
- }
|