#include "libtcod.hpp" #include "Map.h" #include "Actor.h" #include "Engine.h" #include "Destructible.h" #include "Attacker.h" #include "Ai.h" #include "Healer.h" #include "Lightningbolt.h" #include "Fireball.h" #include "Confuser.h" static const int ROOM_MAX_SIZE = 12; static const int ROOM_MIN_SIZE = 6; static const int MAX_ROOM_MONSTERS = 3; static const int MAX_ROOM_ITEMS = 2; class BspListener : public ITCODBspCallback { private: Map& map; // a map to dig int roomNum; // room number int lastx = 0, lasty = 0; // center of the last room public: BspListener(Map& map) : map(map), roomNum(0) {} bool visitNode(TCODBsp* node, void* userData) { if (node->isLeaf()) { int x, y, w, h; bool withActors = (bool)userData; // dig a room w = map.rng->getInt(ROOM_MIN_SIZE, node->w - 2); h = map.rng->getInt(ROOM_MIN_SIZE, node->h - 2); x = map.rng->getInt(node->x + 1, node->x + node->w - w - 1); y = map.rng->getInt(node->y + 1, node->y + node->h - h - 1); map.createRoom(roomNum == 0, x, y, x + w - 1, y + h - 1, withActors); if (roomNum != 0) { // dig a corridor from last room map.dig(lastx, lasty, x + w / 2, lasty); map.dig(x + w / 2, lasty, x + w / 2, y + h / 2); } lastx = x + w / 2; lasty = y + h / 2; roomNum++; } return true; } }; Map::Map(int width, int height) : width(width), height(height) { seed = TCODRandom::getInstance()->getInt(0, 0x7FFFFFFF); } void Map::init(bool withActors) { rng = new TCODRandom(seed, TCOD_RNG_CMWC); tiles = new Tile[width * height]; map = new TCODMap(width, height); TCODBsp bsp(0, 0, width, height); bsp.splitRecursive(rng, 8, ROOM_MAX_SIZE, ROOM_MAX_SIZE, 1.5f, 1.5f); BspListener listener(*this); bsp.traverseInvertedLevelOrder(&listener, (void*)withActors); } Map::~Map() { delete[] tiles; delete map; } bool Map::canWalk(int x, int y) const { if (isWall(x, y)) { // this is a wall return false; } for (Actor** iterator = engine->actors.begin(); iterator != engine->actors.end();iterator++) { Actor* actor = *iterator; if (actor->blocks && actor->x == x && actor->y == y) { // there is an actor there. cannot walk return false; } } return true; } bool Map::isInFov(int x, int y) const { if (x < 0 || x >= width || y < 0 || y >= height) { return false; } if (map->isInFov(x, y)) { tiles[x + y * width].explored = true; return true; } return false; } void Map::computeFov() { map->computeFov(engine->player->x, engine->player->y, engine->fovRadius); } bool Map::isWall(int x, int y) const { return !map->isWalkable(x, y); } bool Map::isExplored(int x, int y) const { return tiles[x + y * width].explored; } void Map::setWall(int x, int y) { map->setProperties(x, y, false, false); } void Map::render(TCOD_Console& cons) const { static const TCOD_ColorRGB darkWall(0, 0, 100); static const TCOD_ColorRGB darkGround(50, 50, 150); static const TCOD_ColorRGB lightWall(130, 110, 50); static const TCOD_ColorRGB lightGround(200, 180, 50); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { if (isInFov(x, y)) { tcod::print(cons, { x, y }, " ", std::nullopt, isWall(x, y) ? lightWall : lightGround); } else if (isExplored(x, y)) { tcod::print(cons, { x, y }, " ", std::nullopt, isWall(x, y) ? darkWall : darkGround); } } } } void Map::dig(int x1, int y1, int x2, int y2) { if (x2 < x1) { int tmp = x2; x2 = x1; x1 = tmp; } if (y2 < y1) { int tmp = y2; y2 = y1; y1 = tmp; } for (int tilex = x1; tilex <= x2; tilex++) { for (int tiley = y1; tiley <= y2; tiley++) { map->setProperties(tilex, tiley, true, true); } } } void Map::createRoom(bool first, int x1, int y1, int x2, int y2, bool withActors) { dig(x1, y1, x2, y2); if (!withActors) { return; } if (first) { // put the player in the first room engine->player->x = (x1 + x2) / 2; engine->player->y = (y1 + y2) / 2; } else { TCODRandom* rng = TCODRandom::getInstance(); int nbMonsters = rng->getInt(0, MAX_ROOM_MONSTERS); while (nbMonsters > 0) { int x = rng->getInt(x1, x2); int y = rng->getInt(y1, y2); if (canWalk(x, y)) { addMonster(x, y); } nbMonsters--; } int nbItems = rng->getInt(0, MAX_ROOM_ITEMS); while (nbItems > 0) { int x = rng->getInt(x1, x2); int y = rng->getInt(y1, y2); if (canWalk(x, y)) { addItem(x, y); } nbItems--; } } } void Map::addMonster(int x, int y) { TCODRandom* rng = TCODRandom::getInstance(); if (rng->getInt(0, 100) < 80) { // create an orc Actor* orc = new Actor(x, y, "o", "orc", TCOD_ColorRGB(0, 100, 0)); orc->destructible = new MonsterDestructible(10, 0, "dead orc"); orc->attacker = new Attacker(3); orc->ai = new MonsterAi(); engine->actors.push(orc); } else { // create a troll Actor* troll = new Actor(x, y, "T", "troll", TCOD_ColorRGB(0, 255, 0)); troll->destructible = new MonsterDestructible(16, 1, "troll carcass"); troll->attacker = new Attacker(4); troll->ai = new MonsterAi(); engine->actors.push(troll); } } void Map::addItem(int x, int y) { TCODRandom* rng = TCODRandom::getInstance(); int dice = rng->getInt(0, 100); if (dice < 70) { // create a health potion Actor* healthPotion = new Actor(x, y, "!", "health potion", TCOD_ColorRGB(128, 0, 128)); healthPotion->blocks = false; healthPotion->pickable = new Healer(4); engine->actors.push(healthPotion); } else if (dice < 70 + 10) { // create a scroll of lightning bolt Actor* scrollOfLightningBolt = new Actor(x, y, "#", "scroll of lightning bolt", TCOD_ColorRGB(255, 100, 100)); scrollOfLightningBolt->blocks = false; scrollOfLightningBolt->pickable = new LightningBolt(5, 20); engine->actors.push(scrollOfLightningBolt); } else if (dice < 70 + 10 + 10) { // create a scroll of fireball Actor* scrollOfFireball = new Actor(x, y, "#", "scroll of fireball", TCOD_ColorRGB(255, 100, 100)); scrollOfFireball->blocks = false; scrollOfFireball->pickable = new Fireball(3, 12); engine->actors.push(scrollOfFireball); } else { // create a scroll of confusion Actor* scrollOfConfusion = new Actor(x, y, "#", "scroll of confusion", TCOD_ColorRGB(255, 100, 100)); scrollOfConfusion->blocks = false; scrollOfConfusion->pickable = new Confuser(10, 8); engine->actors.push(scrollOfConfusion); } } void Map::save(TCODZip& zip) { zip.putInt(seed); for (int i = 0; i < width * height; i++) { zip.putInt(tiles[i].explored); } } void Map::load(TCODZip& zip) { seed = zip.getInt(); init(false); for (int i = 0; i < width * height; i++) { tiles[i].explored = zip.getInt(); } }