123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- #include "World.h"
-
- #include <algorithm>
- #include <tuple>
-
- #include "log.h"
- #include "Game.h"
- #include "Clock.h"
- #include "assets.h"
-
- namespace Swan {
-
- static void chunkLine(int l, WorldPlane &plane, ChunkPos &abspos, const Vec2i &dir) {
- for (int i = 0; i < l; ++i) {
- plane.slowGetChunk(abspos).keepActive();
- abspos += dir;
- }
- }
-
- std::vector<ModWrapper> World::loadMods(std::vector<std::string> paths) {
- std::vector<ModWrapper> mods;
- mods.reserve(paths.size());
-
- for (auto &path: paths) {
- OS::Dynlib dl(path + "/mod");
- auto create = dl.get<Mod *(*)(World &)>("mod_create");
- if (create == NULL) {
- warn << path << ": No 'mod_create' function!";
- continue;
- }
-
- std::unique_ptr<Mod> mod(create(*this));
- mods.push_back(ModWrapper(std::move(mod), std::move(path), std::move(dl)));
- }
-
- return mods;
- }
-
- Cygnet::ResourceManager World::buildResources() {
- Cygnet::ResourceBuilder builder(game_->renderer_);
-
- auto fillTileImage = [&](unsigned char *data, int r, int g, int b, int a) {
- for (size_t i = 0; i < TILE_SIZE * TILE_SIZE; ++i) {
- data[i * 4 + 0] = r;
- data[i * 4 + 1] = g;
- data[i * 4 + 2] = b;
- data[i * 4 + 2] = a;
- }
- };
-
- struct ImageAsset fallbackImage = {
- .width = 32,
- .frameHeight = 32,
- .frameCount = 1,
- .data = std::make_unique<unsigned char[]>(TILE_SIZE * TILE_SIZE * 4),
- };
- fillTileImage(fallbackImage.data.get(),
- PLACEHOLDER_RED, PLACEHOLDER_GREEN, PLACEHOLDER_BLUE, 255);
-
- auto airImage = std::make_unique<unsigned char[]>(TILE_SIZE * TILE_SIZE * 4);
- fillTileImage(airImage.get(),
- PLACEHOLDER_RED, PLACEHOLDER_GREEN, PLACEHOLDER_BLUE, 255);
-
- // Let tile ID 0 be the invalid tile
- builder.addTile(INVALID_TILE_ID, fallbackImage.data.get());
- tilesMap_[INVALID_TILE_NAME] = INVALID_TILE_ID;
- tiles_.push_back(Tile(INVALID_TILE_ID, INVALID_TILE_NAME, {
- .name = "", .image = "", // Not used in this case
- .isSolid = false,
- }));
- items_.emplace(INVALID_TILE_NAME, Item(INVALID_TILE_ID, INVALID_TILE_NAME, {
- .name = "", .image = "", // Not used in this case
- }));
-
- // ...And tile ID 1 be the air tile
- builder.addTile(AIR_TILE_ID, std::move(airImage));
- tilesMap_[AIR_TILE_NAME] = AIR_TILE_ID;
- tiles_.push_back(Tile(AIR_TILE_ID, AIR_TILE_NAME, {
- .name = "", .image = "", // Not used in this case
- .isSolid = false,
- }));
- items_.emplace(AIR_TILE_NAME, Item(AIR_TILE_ID, AIR_TILE_NAME, {
- .name = "", .image = "", // Not used in this case
- }));
-
- // Assets are namespaced on the mod, so if something references, say,
- // "core::stone", we need to know which directory the "core" mod is in
- std::unordered_map<std::string, std::string> modPaths;
- for (auto &mod: mods_) {
- modPaths[mod.mod_->name_] = mod.path_;
- }
-
- auto loadTileImage = [&](std::string path) -> Result<ImageAsset> {
- // Don't force all tiles/items to have an associated image.
- // It could be that some tiles/items exist for a purpose which implies
- // it should never actually be visible.
- if (path == INVALID_TILE_NAME) {
- ImageAsset asset{
- .width = 32,
- .frameHeight = 32,
- .frameCount = 1,
- .data = std::make_unique<unsigned char[]>(TILE_SIZE * TILE_SIZE * 4),
- };
- memcpy(asset.data.get(), fallbackImage.data.get(), TILE_SIZE * TILE_SIZE * 4);
- return {Ok, std::move(asset)};
- }
-
- auto image = loadImageAsset(modPaths, path);
- if (!image) {
- warn << '\'' << path << "': " << image.err();
- return {Err, '\'' + path + "': " + image.err()};
- } else if (image->width != TILE_SIZE) {
- warn << '\'' << path << "': Width must be " << TILE_SIZE << " pixels";
- return {Err, '\'' + path + "': Width must be " + std::to_string(TILE_SIZE) + " pixels"};
- } else {
- return image;
- }
- };
-
- // Need to fill in every tile before we do items,
- // because all items will end up after all tiles in the tile atlas.
- // In the rendering system, there's no real difference between a tile
- // and an item.
- for (auto &mod: mods_) {
- for (auto &tileBuilder: mod.mod_->tiles_) {
- auto image = loadTileImage(tileBuilder.image);
-
- std::string tileName = mod.mod_->name_ + "::" + tileBuilder.name;
- Tile::ID tileId = tiles_.size();
-
- if (image) {
- builder.addTile(tileId, std::move(image->data));
- } else {
- warn << image.err();
- builder.addTile(tileId, fallbackImage.data.get());
- }
-
- tilesMap_[tileName] = tileId;
- tiles_.push_back(Tile(tileId, tileName, tileBuilder));
-
- // All tiles should have an item.
- // Some items will be overwritten later my mod_->items,
- // but if not, this is their default item.
- items_.emplace(tileName, Item(tileId, tileName, {
- .name = "", .image = "", // Not used in this case
- }));
- }
- }
-
- // Put all items after all the tiles
- Tile::ID nextItemId = tiles_.size();
-
- // Load all items which aren't just tiles in disguise.
- for (auto &mod: mods_) {
- for (auto &itemBuilder: mod.mod_->items_) {
- auto image = loadTileImage(itemBuilder.image);
-
- std::string itemName = mod.mod_->name_ + "::" + itemBuilder.name;
- Tile::ID itemId = nextItemId++;
-
- if (image) {
- builder.addTile(itemId, std::move(image->data));
- } else {
- warn << image.err();
- builder.addTile(itemId, fallbackImage.data.get());
- }
-
- items_.emplace(itemName, Item(itemId, itemName, itemBuilder));
- }
- }
-
- // Load sprites
- for (auto &mod: mods_) {
- for (auto spritePath: mod.mod_->sprites_) {
- std::string path = mod.mod_->name_ + "::" + spritePath;
- auto image = loadImageAsset(modPaths, path);
-
- if (image) {
- builder.addSprite(
- path, image->data.get(), image->width,
- image->frameHeight * image->frameCount,
- image->frameHeight);
- } else {
- warn << '\'' << path << "': " << image.err();
- builder.addSprite(
- path, fallbackImage.data.get(), fallbackImage.width,
- fallbackImage.frameHeight * fallbackImage.frameCount,
- fallbackImage.frameHeight);
- }
- }
- }
-
- // Load world gens and entities
- for (auto &mod: mods_) {
- for (auto &worldGenFactory: mod.mod_->worldGens_) {
- std::string name = mod.mod_->name_ + "::" + worldGenFactory.name;
- worldGenFactories_.emplace(name, worldGenFactory);
- }
-
- for (auto &entCollFactory: mod.mod_->entities_) {
- std::string name = mod.mod_->name_ + "::" + entCollFactory.name;
- entCollFactories_.emplace(name, entCollFactory);
- }
- }
-
- return Cygnet::ResourceManager(std::move(builder));
- }
-
- World::World(Game *game, unsigned long randSeed, std::vector<std::string> modPaths):
- game_(game), random_(randSeed), mods_(loadMods(std::move(modPaths))),
- resources_(buildResources()) {}
-
- void World::ChunkRenderer::tick(WorldPlane &plane, ChunkPos abspos) {
- ZoneScopedN("World::ChunkRenderer tick");
- int l = 0;
-
- RTClock clock;
- for (int i = 0; i < 4; ++i) {
- chunkLine(l, plane, abspos, Vec2i(0, -1));
- chunkLine(l, plane, abspos, Vec2i(1, 0));
- l += 1;
- chunkLine(l, plane, abspos, Vec2i(0, 1));
- chunkLine(l, plane, abspos, Vec2i(-1, 0));
- l += 1;
- }
- }
-
- void World::setWorldGen(std::string gen) {
- defaultWorldGen_ = std::move(gen);
- }
-
- void World::spawnPlayer() {
- player_ = &((dynamic_cast<BodyTrait *>(
- planes_[currentPlane_]->spawnPlayer().get()))->get(BodyTrait::Tag{}));
- }
-
- void World::setCurrentPlane(WorldPlane &plane) {
- currentPlane_ = plane.id_;
- }
-
- WorldPlane &World::addPlane(const std::string &gen) {
- WorldPlane::ID id = planes_.size();
- auto it = worldGenFactories_.find(gen);
- if (it == worldGenFactories_.end()) {
- panic << "Tried to add plane with non-existant world gen " << gen << "!";
- abort();
- }
-
- std::vector<std::unique_ptr<EntityCollection>> colls;
- colls.reserve(entCollFactories_.size());
- for (auto &fact: entCollFactories_) {
- colls.emplace_back(fact.second.create(fact.second.name));
- }
-
- WorldGen::Factory &factory = it->second;
- std::unique_ptr<WorldGen> g = factory.create(*this);
- planes_.push_back(std::make_unique<WorldPlane>(
- id, this, std::move(g), std::move(colls)));
- return *planes_[id];
- }
-
- Tile::ID World::getTileID(const std::string &name) {
- auto iter = tilesMap_.find(name);
- if (iter == tilesMap_.end()) {
- warn << "Tried to get non-existant item " << name << "!";
- return INVALID_TILE_ID;
- }
-
- return iter->second;
- }
-
- Tile &World::getTile(const std::string &name) {
- Tile::ID id = getTileID(name);
- return getTileByID(id);
- }
-
- Item &World::getItem(const std::string &name) {
- auto iter = items_.find(name);
- if (iter == items_.end()) {
- warn << "Tried to get non-existent item " << name << "!";
- return items_.at(INVALID_TILE_NAME);
- }
-
- return iter->second;
- }
-
- Cygnet::RenderSprite &World::getSprite(const std::string &name) {
- auto iter = resources_.sprites_.find(name);
- if (iter == resources_.sprites_.end()) {
- warn << "Tried to get non-existent sprite " << name << "!";
- return resources_.sprites_.at(INVALID_TILE_NAME);
- }
-
- return iter->second;
- }
-
- SDL_Color World::backgroundColor() {
- return planes_[currentPlane_]->backgroundColor();
- }
-
- void World::draw(Cygnet::Renderer &rnd) {
- ZoneScopedN("World draw");
- game_->cam_.pos = player_->pos; // - (win.getSize() / 2) + (player_->size / 2); TODO
- planes_[currentPlane_]->draw(rnd);
- }
-
- void World::update(float dt) {
- ZoneScopedN("World update");
- for (auto &plane: planes_)
- plane->update(dt);
- }
-
- void World::tick(float dt) {
- ZoneScopedN("World tick");
- for (auto &plane: planes_)
- plane->tick(dt);
-
- chunkRenderer_.tick(
- *planes_[currentPlane_],
- ChunkPos((int)player_->pos.x / CHUNK_WIDTH, (int)player_->pos.y / CHUNK_HEIGHT));
- }
-
- }
|