diff --git a/client/CMusicHandler.cpp b/client/CMusicHandler.cpp index a4dd5fa62..7569218a3 100644 --- a/client/CMusicHandler.cpp +++ b/client/CMusicHandler.cpp @@ -20,7 +20,7 @@ #include "../lib/StringConstants.h" #include "../lib/CRandomGenerator.h" #include "../lib/VCMIDirs.h" -#include "../lib/Terrain.h" +#include "../lib/TerrainHandler.h" #define VCMI_SOUND_NAME(x) #define VCMI_SOUND_FILE(y) #y, @@ -89,36 +89,6 @@ CSoundHandler::CSoundHandler(): soundBase::battle02, soundBase::battle03, soundBase::battle04, soundBase::battle05, soundBase::battle06, soundBase::battle07 }; - - //predefine terrain set - //TODO: support custom sounds for new terrains and load from json - horseSounds = - { - {Terrain::DIRT, soundBase::horseDirt}, - {Terrain::SAND, soundBase::horseSand}, - {Terrain::GRASS, soundBase::horseGrass}, - {Terrain::SNOW, soundBase::horseSnow}, - {Terrain::SWAMP, soundBase::horseSwamp}, - {Terrain::ROUGH, soundBase::horseRough}, - {Terrain::SUBTERRANEAN, soundBase::horseSubterranean}, - {Terrain::LAVA, soundBase::horseLava}, - {Terrain::WATER, soundBase::horseWater}, - {Terrain::ROCK, soundBase::horseRock} - }; -} - -void CSoundHandler::loadHorseSounds() -{ - const auto & terrains = CGI->terrainTypeHandler->terrains(); - for(const auto & terrain : terrains) - { - //since all sounds are hardcoded, let's keep it - if(vstd::contains(horseSounds, terrain.id)) - continue; - - //Use already existing horse sound - horseSounds[terrain.id] = horseSounds.at(terrains[terrain.id].horseSoundId); - } } void CSoundHandler::init() @@ -368,9 +338,9 @@ CMusicHandler::CMusicHandler(): void CMusicHandler::loadTerrainMusicThemes() { - for (const auto & terrain : CGI->terrainTypeHandler->terrains()) + for (const auto & terrain : CGI->terrainTypeHandler->objects) { - addEntryToSet("terrain_" + terrain.name, "Music/" + terrain.musicFilename); + addEntryToSet("terrain_" + terrain->getJsonKey(), "Music/" + terrain->musicFilename); } } diff --git a/client/CMusicHandler.h b/client/CMusicHandler.h index f465a471d..33fcc6b3b 100644 --- a/client/CMusicHandler.h +++ b/client/CMusicHandler.h @@ -11,7 +11,6 @@ #include "../lib/CConfigHandler.h" #include "../lib/CSoundBase.h" -#include "../lib/Terrain.h" struct _Mix_Music; struct SDL_RWops; @@ -61,7 +60,6 @@ public: CSoundHandler(); void init() override; - void loadHorseSounds(); void release() override; void setVolume(ui32 percent) override; @@ -84,7 +82,6 @@ public: // Sets std::vector pickupSounds; std::vector battleIntroSounds; - std::map horseSounds; }; // Helper //now it looks somewhat useless diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 789e69231..f0b364e4f 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -61,6 +61,8 @@ #include "windows/InfoWindows.h" #include "../lib/UnlockGuard.h" #include "../lib/CPathfinder.h" +#include "../lib/RoadHandler.h" +#include "../lib/TerrainHandler.h" #include #include "CServerHandler.h" // FIXME: only needed for CGameState::mutex @@ -156,7 +158,6 @@ void CPlayerInterface::initGameInterface(std::shared_ptr ENV, std:: cb = CB; env = ENV; - CCS->soundh->loadHorseSounds(); CCS->musich->loadTerrainMusicThemes(); initializeHeroTownList(); @@ -260,7 +261,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose) { updateAmbientSounds(); //We may need to change music - select new track, music handler will change it if needed - CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(hero->visitablePos())->terType->name, true, false); + CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(hero->visitablePos())->terType->getJsonKey(), true, false); if(details.result == TryMoveHero::TELEPORTATION) { @@ -436,7 +437,7 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero) adventureInt->select(newSelection, true); else if (adventureInt->selection == hero) adventureInt->selection = nullptr; - + if (vstd::contains(paths, hero)) paths.erase(hero); } @@ -2372,8 +2373,9 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) for (auto & elem : path.nodes) elem.coord = h->convertFromVisitablePos(elem.coord); - TerrainId currentTerrain = Terrain::BORDER; // not init yet + TerrainId currentTerrain = ETerrainId::NONE; TerrainId newTerrain; + bool wasOnRoad = true; int sh = -1; auto canStop = [&](CGPathNode * node) -> bool @@ -2389,13 +2391,18 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) for (i=(int)path.nodes.size()-1; i>0 && (stillMoveHero.data == CONTINUE_MOVE || !canStop(&path.nodes[i])); i--) { - int3 currentCoord = path.nodes[i].coord; + int3 prevCoord = path.nodes[i].coord; int3 nextCoord = path.nodes[i-1].coord; - auto currentObject = getObj(currentCoord, currentCoord == h->pos); + auto prevRoad = cb->getTile(h->convertToVisitablePos(prevCoord))->roadType; + auto nextRoad = cb->getTile(h->convertToVisitablePos(nextCoord))->roadType; + + bool movingOnRoad = prevRoad->getId() != Road::NO_ROAD && nextRoad->getId() != Road::NO_ROAD; + + auto prevObject = getObj(prevCoord, prevCoord == h->pos); auto nextObjectTop = getObj(nextCoord, false); auto nextObject = getObj(nextCoord, true); - auto destTeleportObj = getDestTeleportObj(currentObject, nextObjectTop, nextObject); + auto destTeleportObj = getDestTeleportObj(prevObject, nextObjectTop, nextObject); if (isTeleportAction(path.nodes[i-1].action) && destTeleportObj != nullptr) { CCS->soundh->stopSound(sh); @@ -2410,7 +2417,10 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) } if(i != path.nodes.size() - 1) { - sh = CCS->soundh->playSound(CCS->soundh->horseSounds[currentTerrain], -1); + if (movingOnRoad) + sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(currentTerrain)->horseSound, -1); + else + sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(currentTerrain)->horseSoundPenalty, -1); } continue; } @@ -2428,12 +2438,16 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) sh = CCS->soundh->playSound(soundBase::horseFlying, -1); #endif { - newTerrain = cb->getTile(h->convertToVisitablePos(currentCoord))->terType->id; - if(newTerrain != currentTerrain) + newTerrain = cb->getTile(h->convertToVisitablePos(prevCoord))->terType->getId(); + if(newTerrain != currentTerrain || wasOnRoad != movingOnRoad) { CCS->soundh->stopSound(sh); - sh = CCS->soundh->playSound(CCS->soundh->horseSounds[newTerrain], -1); + if (movingOnRoad) + sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(newTerrain)->horseSound, -1); + else + sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(newTerrain)->horseSoundPenalty, -1); currentTerrain = newTerrain; + wasOnRoad = movingOnRoad; } } diff --git a/client/battle/BattleInterface.cpp b/client/battle/BattleInterface.cpp index 6d3a0c4c5..06e84033a 100644 --- a/client/battle/BattleInterface.cpp +++ b/client/battle/BattleInterface.cpp @@ -41,6 +41,7 @@ #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/NetPacks.h" #include "../../lib/UnlockGuard.h" +#include "../../lib/TerrainHandler.h" CondSh BattleInterface::givenCommand(nullptr); @@ -136,8 +137,8 @@ BattleInterface::~BattleInterface() if (adventureInt && adventureInt->selection) { //FIXME: this should be moved to adventureInt which should restore correct track based on selection/active player - const auto & terrain = *(LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType); - CCS->musich->playMusicFromSet("terrain", terrain.name, true, false); + const auto * terrain = LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType; + CCS->musich->playMusicFromSet("terrain", terrain->getJsonKey(), true, false); } // may happen if user decided to close game while in battle diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index b268b764b..bcefe0c23 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -29,6 +29,7 @@ #include "../../lib/rmg/CMapGenOptions.h" #include "../../lib/CModHandler.h" #include "../../lib/rmg/CRmgTemplateStorage.h" +#include "../../lib/RoadHandler.h" RandomMapTab::RandomMapTab(): InterfaceObjectConfigurable() @@ -108,12 +109,12 @@ RandomMapTab::RandomMapTab(): GH.pushIntT(*this); }); - for(auto road : VLC->terrainTypeHandler->roads()) + for(auto road : VLC->roadTypeHandler->objects) { - std::string cbRoadType = "selectRoad_" + road.name; + std::string cbRoadType = "selectRoad_" + road->getJsonKey(); addCallback(cbRoadType, [&, road](bool on) { - mapGenOptions->setRoadEnabled(road.name, on); + mapGenOptions->setRoadEnabled(road->getJsonKey(), on); updateMapInfoByHost(); }); } @@ -283,11 +284,11 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr opts) else w->addTextOverlay(readText(variables["defaultTemplate"]), EFonts::FONT_SMALL); } - for(auto r : VLC->terrainTypeHandler->roads()) + for(auto r : VLC->roadTypeHandler->objects) { - if(auto w = widget(r.name)) + if(auto w = widget(r->getJsonKey())) { - w->setSelected(opts->isRoadEnabled(r.name)); + w->setSelected(opts->isRoadEnabled(r->getJsonKey())); } } } diff --git a/client/mapHandler.cpp b/client/mapHandler.cpp index 05fb0d797..215781521 100644 --- a/client/mapHandler.cpp +++ b/client/mapHandler.cpp @@ -30,7 +30,9 @@ #include "CMT.h" #include "CMusicHandler.h" #include "../lib/CRandomGenerator.h" -#include "../lib/Terrain.h" +#include "../lib/RoadHandler.h" +#include "../lib/RiverHandler.h" +#include "../lib/TerrainHandler.h" #include "../lib/filesystem/ResourceID.h" #include "../lib/JsonDetail.h" @@ -175,17 +177,17 @@ void CMapHandler::initTerrainGraphics() std::map terrainFiles; std::map riverFiles; std::map roadFiles; - for(const auto & terrain : VLC->terrainTypeHandler->terrains()) + for(const auto & terrain : VLC->terrainTypeHandler->objects) { - terrainFiles[terrain.name] = terrain.tilesFilename; + terrainFiles[terrain->getJsonKey()] = terrain->tilesFilename; } - for(const auto & river : VLC->terrainTypeHandler->rivers()) + for(const auto & river : VLC->riverTypeHandler->objects) { - riverFiles[river.fileName] = river.fileName; + riverFiles[river->getJsonKey()] = river->tilesFilename; } - for(const auto & road : VLC->terrainTypeHandler->roads()) + for(const auto & road : VLC->roadTypeHandler->objects) { - roadFiles[road.fileName] = road.fileName; + roadFiles[road->getJsonKey()] = road->tilesFilename; } loadFlipped(terrainAnimations, terrainImages, terrainFiles); @@ -606,7 +608,7 @@ void CMapHandler::CMapBlitter::drawTileTerrain(SDL_Surface * targetSurf, const T ui8 rotation = tinfo.extTileFlags % 4; //TODO: use ui8 instead of string key - auto terrainName = tinfo.terType->name; + auto terrainName = tinfo.terType->getJsonKey(); if(parent->terrainImages[terrainName].size()<=tinfo.terView) return; @@ -786,21 +788,21 @@ void CMapHandler::CMapBlitter::drawObjects(SDL_Surface * targetSurf, const Terra void CMapHandler::CMapBlitter::drawRoad(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile * tinfoUpper) const { - if (tinfoUpper && tinfoUpper->roadType->id != Road::NO_ROAD) + if (tinfoUpper && tinfoUpper->roadType->getId() != Road::NO_ROAD) { ui8 rotation = (tinfoUpper->extTileFlags >> 4) % 4; Rect source(0, tileSize / 2, tileSize, tileSize / 2); Rect dest(realPos.x, realPos.y, tileSize, tileSize / 2); - drawElement(EMapCacheType::ROADS, parent->roadImages[tinfoUpper->roadType->fileName][tinfoUpper->roadDir][rotation], + drawElement(EMapCacheType::ROADS, parent->roadImages[tinfoUpper->roadType->getJsonKey()][tinfoUpper->roadDir][rotation], &source, targetSurf, &dest); } - if(tinfo.roadType->id != Road::NO_ROAD) //print road from this tile + if(tinfo.roadType->getId() != Road::NO_ROAD) //print road from this tile { ui8 rotation = (tinfo.extTileFlags >> 4) % 4; Rect source(0, 0, tileSize, halfTileSizeCeil); Rect dest(realPos.x, realPos.y + tileSize / 2, tileSize, tileSize / 2); - drawElement(EMapCacheType::ROADS, parent->roadImages[tinfo.roadType->fileName][tinfo.roadDir][rotation], + drawElement(EMapCacheType::ROADS, parent->roadImages[tinfo.roadType->getJsonKey()][tinfo.roadDir][rotation], &source, targetSurf, &dest); } } @@ -809,7 +811,7 @@ void CMapHandler::CMapBlitter::drawRiver(SDL_Surface * targetSurf, const Terrain { Rect destRect(realTileRect); ui8 rotation = (tinfo.extTileFlags >> 2) % 4; - drawElement(EMapCacheType::RIVERS, parent->riverImages[tinfo.riverType->fileName][tinfo.riverDir][rotation], nullptr, targetSurf, &destRect); + drawElement(EMapCacheType::RIVERS, parent->riverImages[tinfo.riverType->getJsonKey()][tinfo.riverDir][rotation], nullptr, targetSurf, &destRect); } void CMapHandler::CMapBlitter::drawFow(SDL_Surface * targetSurf) const @@ -860,7 +862,7 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn if(isVisible || info->showAllTerrain) { drawTileTerrain(targetSurf, tinfo, tile); - if(tinfo.riverType->id != River::NO_RIVER) + if(tinfo.riverType->getId() != River::NO_RIVER) drawRiver(targetSurf, tinfo); drawRoad(targetSurf, tinfo, tinfoUpper); } @@ -1388,8 +1390,9 @@ void CMapHandler::getTerrainDescr(const int3 & pos, std::string & out, bool isRM break; } } + if(!isTile2Terrain || out.empty()) - out = CGI->generaltexth->terrainNames[t.terType->id]; + out = t.terType->getNameTranslated(); if(t.getDiggingStatus(false) == EDiggingStatus::CAN_DIG) { @@ -1485,4 +1488,3 @@ TerrainTileObject::TerrainTileObject(const CGObjectInstance * obj_, SDL_Rect rec TerrainTileObject::~TerrainTileObject() { } - diff --git a/client/widgets/AdventureMapClasses.cpp b/client/widgets/AdventureMapClasses.cpp index e148f68dd..9a6bd3443 100644 --- a/client/widgets/AdventureMapClasses.cpp +++ b/client/widgets/AdventureMapClasses.cpp @@ -41,7 +41,7 @@ #include "../../lib/CHeroHandler.h" #include "../../lib/CModHandler.h" #include "../../lib/CTownHandler.h" -#include "../../lib/Terrain.h" +#include "../../lib/TerrainHandler.h" #include "../../lib/filesystem/Filesystem.h" #include "../../lib/JsonNode.h" #include "../../lib/mapObjects/CGHeroInstance.h" @@ -391,7 +391,7 @@ const SDL_Color & CMinimapInstance::getTileColor(const int3 & pos) } // else - use terrain color (blocked version or normal) - const auto & colorPair = parent->colors.find(tile->terType->id)->second; + const auto & colorPair = parent->colors.find(tile->terType->getId())->second; if (tile->blocked && (!tile->visitable)) return colorPair.second; else @@ -500,25 +500,25 @@ std::map > CMinimap::loadColors() { std::map > ret; - for(const auto & terrain : CGI->terrainTypeHandler->terrains()) + for(const auto & terrain : CGI->terrainTypeHandler->objects) { SDL_Color normal = { - ui8(terrain.minimapUnblocked[0]), - ui8(terrain.minimapUnblocked[1]), - ui8(terrain.minimapUnblocked[2]), + ui8(terrain->minimapUnblocked[0]), + ui8(terrain->minimapUnblocked[1]), + ui8(terrain->minimapUnblocked[2]), ui8(255) }; SDL_Color blocked = { - ui8(terrain.minimapBlocked[0]), - ui8(terrain.minimapBlocked[1]), - ui8(terrain.minimapBlocked[2]), + ui8(terrain->minimapBlocked[0]), + ui8(terrain->minimapBlocked[1]), + ui8(terrain->minimapBlocked[2]), ui8(255) }; - ret[terrain.id] = std::make_pair(normal, blocked); + ret[terrain->getId()] = std::make_pair(normal, blocked); } return ret; } diff --git a/client/widgets/AdventureMapClasses.h b/client/widgets/AdventureMapClasses.h index e86d26e72..921f5ca1e 100644 --- a/client/widgets/AdventureMapClasses.h +++ b/client/widgets/AdventureMapClasses.h @@ -11,7 +11,6 @@ #include "ObjectLists.h" #include "../../lib/FunctionList.h" -#include "Terrain.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index 704d8391d..2936eeef0 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -53,6 +53,7 @@ #include "../../lib/VCMI_Lib.h" #include "../../lib/StartInfo.h" #include "../../lib/mapping/CMapInfo.h" +#include "../../lib/TerrainHandler.h" #define ADVOPT (conf.go()->ac) using namespace CSDL_Ext; @@ -1414,7 +1415,7 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView) auto pos = sel->visitablePos(); auto tile = LOCPLINT->cb->getTile(pos); if(tile) - CCS->musich->playMusicFromSet("terrain", tile->terType->name, true, false); + CCS->musich->playMusicFromSet("terrain", tile->terType->getJsonKey(), true, false); } if(centerView) centerOn(sel); diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index 0c3266f48..4399502d0 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -191,8 +191,10 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/ObstacleHandler.cpp ${MAIN_LIB_DIR}/StartInfo.cpp ${MAIN_LIB_DIR}/ResourceSet.cpp + ${MAIN_LIB_DIR}/RiverHandler.cpp + ${MAIN_LIB_DIR}/RoadHandler.cpp ${MAIN_LIB_DIR}/ScriptHandler.cpp - ${MAIN_LIB_DIR}/Terrain.cpp + ${MAIN_LIB_DIR}/TerrainHandler.cpp ${MAIN_LIB_DIR}/VCMIDirs.cpp ${MAIN_LIB_DIR}/VCMI_Lib.cpp @@ -439,11 +441,13 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/ObstacleHandler.h ${MAIN_LIB_DIR}/PathfinderUtil.h ${MAIN_LIB_DIR}/ResourceSet.h + ${MAIN_LIB_DIR}/RiverHandler.h + ${MAIN_LIB_DIR}/RoadHandler.h ${MAIN_LIB_DIR}/ScriptHandler.h ${MAIN_LIB_DIR}/ScopeGuard.h ${MAIN_LIB_DIR}/StartInfo.h ${MAIN_LIB_DIR}/StringConstants.h - ${MAIN_LIB_DIR}/Terrain.h + ${MAIN_LIB_DIR}/TerrainHandler.h ${MAIN_LIB_DIR}/UnlockGuard.h ${MAIN_LIB_DIR}/VCMIDirs.h ${MAIN_LIB_DIR}/vcmi_endian.h diff --git a/config/defaultMods.json b/config/defaultMods.json index 7b2e37197..bfced4e84 100644 --- a/config/defaultMods.json +++ b/config/defaultMods.json @@ -10,6 +10,9 @@ "hero" : 156, "spell" : 81, "object" : 256, + "terrain" : 10, + "river" : 5, + "road" : 4, "mapVersion" : 28 // max supported version, SoD }, diff --git a/config/factions/neutral.json b/config/factions/neutral.json index 6a6abac52..580a14c21 100644 --- a/config/factions/neutral.json +++ b/config/factions/neutral.json @@ -3,6 +3,7 @@ { "name" : "Neutral", "index" : 9, + "nativeTerrain" : "none", "alignment" : "neutral", "creatureBackground" : { diff --git a/config/gameConfig.json b/config/gameConfig.json index a8e5bc71b..edd8b7231 100644 --- a/config/gameConfig.json +++ b/config/gameConfig.json @@ -85,6 +85,14 @@ [ "config/terrains.json" ], + "roads": + [ + "config/roads.json" + ], + "rivers": + [ + "config/rivers.json" + ], "battlefields": [ "config/battlefields.json" diff --git a/config/randomMap.json b/config/randomMap.json index fa54f121f..5aa0d981a 100644 --- a/config/randomMap.json +++ b/config/randomMap.json @@ -42,4 +42,4 @@ "value" : [2000, 5333, 8666, 12000], "rewardValue" : [5000, 10000, 15000, 20000] } -} \ No newline at end of file +} diff --git a/config/rivers.json b/config/rivers.json index 1ff5e32c2..b9cc138dd 100644 --- a/config/rivers.json +++ b/config/rivers.json @@ -1,30 +1,34 @@ { "waterRiver": { - "originalRiverId": 1, - "code": "rw", //must be 2 characters - "animation": "clrrvr", + "index": 1, + "text" : "Water river", + "shortIdentifier": "rw", //must be 2 characters + "tilesFilename": "clrrvr", "delta": "clrdelt" }, "iceRiver": { - "originalRiverId": 2, - "code": "ri", - "animation": "icyrvr", + "index": 2, + "text" : "Ice river", + "shortIdentifier": "ri", + "tilesFilename": "icyrvr", "delta": "icedelt" }, "mudRiver": { - "originalRiverId": 3, - "code": "rm", - "animation": "mudrvr", + "index": 3, + "text" : "Mud river", + "shortIdentifier": "rm", + "tilesFilename": "mudrvr", "delta": "muddelt" }, "lavaRiver": { - "originalRiverId": 4, - "code": "rl", - "animation": "lavrvr", + "index": 4, + "text" : "Lava river", + "shortIdentifier": "rl", + "tilesFilename": "lavrvr", "delta": "lavdelt" } -} \ No newline at end of file +} diff --git a/config/roads.json b/config/roads.json index 48832d2e0..b0fc2d79d 100644 --- a/config/roads.json +++ b/config/roads.json @@ -1,23 +1,26 @@ { "dirtRoad": { - "originalRoadId": 1, - "code": "pd", //must be 2 characters - "animation": "dirtrd", + "index": 1, + "text" : "Dirt road", + "shortIdentifier": "pd", //must be 2 characters + "tilesFilename": "dirtrd", "moveCost": 75 }, "gravelRoad": { - "originalRoadId": 2, - "code": "pg", - "animation": "gravrd", + "index": 2, + "text" : "Gravel road", + "shortIdentifier": "pg", + "tilesFilename": "gravrd", "moveCost": 65 }, "cobblestoneRoad": { - "originalRoadId": 3, - "code": "pc", - "animation": "cobbrd", + "index": 3, + "text" : "Cobblestone road", + "shortIdentifier": "pc", + "tilesFilename": "cobbrd", "moveCost": 50 } -} \ No newline at end of file +} diff --git a/config/schemas/faction.json b/config/schemas/faction.json index b6e25b4d0..f67ba6386 100644 --- a/config/schemas/faction.json +++ b/config/schemas/faction.json @@ -31,7 +31,7 @@ "$schema": "http://json-schema.org/draft-04/schema", "title" : "VCMI faction format", "description": "Json format for defining new faction (aka towns) in VCMI", - "required" : [ "name", "alignment", "creatureBackground" ], + "required" : [ "name", "alignment", "creatureBackground", "nativeTerrain" ], "dependencies" : { "town" : [ "puzzleMap" ] }, diff --git a/config/schemas/river.json b/config/schemas/river.json new file mode 100644 index 000000000..51eb1031f --- /dev/null +++ b/config/schemas/river.json @@ -0,0 +1,37 @@ +{ + "type":"object", + "$schema": "http://json-schema.org/draft-04/schema", + "title" : "VCMI river format", + "description" : "Format used to define new rivers in VCMI", + "required" : [ "text", "shortIdentifier", "tilesFilename", "delta" ], + + "additionalProperties" : false, + "properties":{ + "index" : + { + "type": "number", + "description": "Internal, do not use" + }, + "text": + { + "type": "string", + "description": "Human-readable name of the river" + }, + "shortIdentifier": + { + "type": "string", + "description": "Two-letters unique indentifier for this road. Used in map format" + }, + "tilesFilename": + { + "type": "string", + "description": "Name of file with river graphics", + "format": "defFile" + }, + "delta": + { + "type": "string", + "description": "Name of file with river delta graphics" + } + } +} diff --git a/config/schemas/road.json b/config/schemas/road.json new file mode 100644 index 000000000..8e684c251 --- /dev/null +++ b/config/schemas/road.json @@ -0,0 +1,37 @@ +{ + "type":"object", + "$schema": "http://json-schema.org/draft-04/schema", + "title" : "VCMI road format", + "description" : "Format used to define new roads in VCMI", + "required" : [ "text", "shortIdentifier", "tilesFilename", "moveCost" ], + + "additionalProperties" : false, + "properties":{ + "index" : + { + "type": "number", + "description": "Internal, do not use" + }, + "text": + { + "type": "string", + "description": "Human-readable name of the road" + }, + "shortIdentifier": + { + "type": "string", + "description": "Two-letters unique indentifier for this road. Used in map format" + }, + "tilesFilename": + { + "type": "string", + "description": "Name of file with road graphics", + "format": "defFile" + }, + "moveCost": + { + "type": "number", + "description": "How many movement points needed to move hero" + } + } +} diff --git a/config/schemas/terrain.json b/config/schemas/terrain.json index bcbabeaa8..3a1c3140d 100644 --- a/config/schemas/terrain.json +++ b/config/schemas/terrain.json @@ -3,10 +3,20 @@ "$schema": "http://json-schema.org/draft-04/schema", "title" : "VCMI terrain format", "description" : "Format used to define new terrains in VCMI", - "required" : [ "tiles", "code", "moveCost" ], + "required" : [ "text", "moveCost", "minimapUnblocked", "minimapBlocked", "music", "tiles", "type", "horseSound", "horseSoundPenalty", "shortIdentifier", "battleFields" ], "additionalProperties" : false, "properties":{ + "index" : + { + "type": "number", + "description": "Internal, do not use" + }, + "text": + { + "type": "string", + "description": "Human-readable name of this terrain" + }, "moveCost": { "type": "number", @@ -47,9 +57,13 @@ }, "type": { - "type": "string", + "type": "array", "description": "Type of this terrain. Can be land, water, subterranean or rock", - "enum": ["LAND", "WATER", "SUB", "ROCK"] + "items": + { + "enum": ["LAND", "WATER", "SUB", "ROCK", "SURFACE"], + "type": "string" + } }, "rockTerrain": { @@ -59,23 +73,22 @@ "river": { "type": "string", - "description": "River type which should be used for that terrain", - "enum": ["", "rw", "ri", "rm", "rl"] + "description": "River type which should be used for that terrain" }, - "horseSoundId": - { - "type": "number", - "description": "Id of horse sound to be played when hero is moving across terrain" - }, - "text": + "horseSound": { "type": "string", - "description": "Text to be shown when mouse if over terrain" + "description": "Hero movement sound for this terrain, version for moving on tiles with road" }, - "code": + "horseSoundPenalty": { "type": "string", - "description": "Two-letters unique indentifier for this terrain. Used for terrain serializaion" + "description": "Hero movement sound for this terrain, version for moving on tiles without road" + }, + "shortIdentifier": + { + "type": "string", + "description": "Two-letters unique indentifier for this terrain. Used for map format" }, "battleFields": { @@ -86,6 +99,24 @@ "type": "string" } }, + "sounds": + { + "type": "object", + "description": "list of sounds for this terrain", + "additionalProperties" : false, + "properties": + { + "ambient" : + { + "type": "array", + "description": "list of ambient sounds for this terrain", + "items": + { + "type": "string" + } + } + } + }, "prohibitTransitions": { "type": "array", diff --git a/config/terrains.json b/config/terrains.json index 149ca98c3..b381072e1 100644 --- a/config/terrains.json +++ b/config/terrains.json @@ -1,146 +1,162 @@ { "dirt" : { - "originalTerrainId": 0, + "index": 0, "moveCost" : 100, "minimapUnblocked" : [ 82, 56, 8 ], "minimapBlocked" : [ 57, 40, 8 ], "music" : "Dirt.mp3", "tiles" : "DIRTTL", - "code" : "dt", - "river" : "rm", + "type" : ["SURFACE"], + "shortIdentifier" : "dt", + "river" : "mudRiver", "battleFields" : ["dirt_birches", "dirt_hills", "dirt_pines"], "terrainViewPatterns" : "dirt", - "horseSoundId" : 0 + "horseSound" : "horse00", + "horseSoundPenalty" : "horse20" }, "sand" : { - "originalTerrainId": 1, + "index": 1, "moveCost" : 150, "minimapUnblocked" : [ 222, 207, 140 ], "minimapBlocked" : [ 165, 158, 107 ], "music" : "Sand.mp3", "tiles" : "SANDTL", - "code" : "sa", - "river" : "rm", + "type" : ["SURFACE"], + "shortIdentifier" : "sa", + "river" : "mudRiver", "battleFields" : ["sand_mesas"], "transitionRequired" : true, "terrainViewPatterns" : "sand", - "horseSoundId" : 1 + "horseSound" : "horse01", + "horseSoundPenalty" : "horse21" }, "grass" : { - "originalTerrainId": 2, + "index": 2, "moveCost" : 100, "minimapUnblocked" : [ 0, 65, 0 ], "minimapBlocked" : [ 0, 48, 0 ], "music" : "Grass.mp3", "tiles" : "GRASTL", - "code" : "gr", - "river" : "rw", + "type" : ["SURFACE"], + "shortIdentifier" : "gr", + "river" : "waterRiver", "battleFields" : ["grass_hills", "grass_pines"], - "horseSoundId" : 2 + "horseSound" : "horse02", + "horseSoundPenalty" : "horse22" }, "snow" : { - "originalTerrainId": 3, + "index": 3, "moveCost" : 150, "minimapUnblocked" : [ 181, 199, 198 ], "minimapBlocked" : [ 140, 158, 156 ], "music" : "Snow.mp3", "tiles" : "SNOWTL", - "code" : "sn", - "river" : "ri", + "type" : ["SURFACE"], + "shortIdentifier" : "sn", + "river" : "iceRiver", "battleFields" : ["snow_mountains", "snow_trees"], - "horseSoundId" : 3 + "horseSound" : "horse03", + "horseSoundPenalty" : "horse23" }, "swamp" : { - "originalTerrainId": 4, + "index": 4, "moveCost" : 175, "minimapUnblocked" : [ 74, 134, 107 ], "minimapBlocked" : [ 33, 89, 66 ], "music" : "Swamp.mp3", "tiles" : "SWMPTL", - "code" : "sw", - "river" : "rw", + "type" : ["SURFACE"], + "shortIdentifier" : "sw", + "river" : "waterRiver", "battleFields" : ["swamp_trees"], - "horseSoundId" : 4 + "horseSound" : "horse04", + "horseSoundPenalty" : "horse24" }, "rough" : { - "originalTerrainId": 5, + "index": 5, "moveCost" : 125, "minimapUnblocked" : [ 132, 113, 49 ], "minimapBlocked" : [ 99, 81, 33 ], "music" : "Rough.mp3", "tiles" : "ROUGTL", - "code" : "rg", - "river" : "rm", + "type" : ["SURFACE"], + "shortIdentifier" : "rg", + "river" : "mudRiver", "battleFields" : ["rough"], - "horseSoundId" : 5 + "horseSound" : "horse05", + "horseSoundPenalty" : "horse25" }, "subterra" : { - "originalTerrainId": 6, + "index": 6, "moveCost" : 100, "minimapUnblocked" : [ 132, 48, 0 ], "minimapBlocked" : [ 90, 8, 0 ], "music" : "Underground.mp3", "tiles" : "SUBBTL", - "type" : "SUB", - "code" : "sb", - "river" : "rw", + "type" : [ "SUB" ], + "shortIdentifier" : "sb", + "river" : "waterRiver", "battleFields" : ["subterranean"], "rockTerrain" : "rock", - "horseSoundId" : 6 + "horseSound" : "horse06", + "horseSoundPenalty" : "horse26" }, "lava" : { - "originalTerrainId": 7, + "index": 7, "moveCost" : 100, "minimapUnblocked" : [ 74, 73, 74 ], "minimapBlocked" : [ 41, 40, 41 ], "music" : "Lava.mp3", "tiles" : "LAVATL", "type" : ["SUB", "SURFACE"], - "code" : "lv", - "river" : "rl", + "shortIdentifier" : "lv", + "river" : "lavaRiver", "battleFields" : ["lava"], "rockTerrain" : "rock", - "horseSoundId" : 7 + "horseSound" : "horse07", + "horseSoundPenalty" : "horse27" }, "water" : { - "originalTerrainId": 8, + "index": 8, "moveCost" : 100, "minimapUnblocked" : [ 8, 81, 148 ], "minimapBlocked" : [ 8, 81, 148 ], "music" : "Water.mp3", "tiles" : "WATRTL", - "type" : "WATER", - "code" : "wt", + "type" : [ "WATER" ], + "shortIdentifier" : "wt", "battleFields" : ["ship"], "transitionRequired" : true, "terrainViewPatterns" : "water", - "horseSoundId" : 8, + "horseSound" : "horse08", + "horseSoundPenalty" : "horse28", "sounds": { "ambient": ["LOOPOCEA"] } }, "rock" : { - "originalTerrainId": 9, + "index": 9, "moveCost" : -1, "minimapUnblocked" : [ 0, 0, 0 ], "minimapBlocked" : [ 0, 0, 0 ], "music" : "Underground.mp3", // Impossible in H3 "tiles" : "ROCKTL", - "type" : "ROCK", - "code" : "rc", + "type" : [ "ROCK" ], + "shortIdentifier" : "rc", "battleFields" : ["rocklands"], "transitionRequired" : true, "terrainViewPatterns" : "rock", - "horseSoundId" : 9 + "horseSound" : "horse09", + "horseSoundPenalty" : "horse29" } } diff --git a/lib/BattleFieldHandler.h b/lib/BattleFieldHandler.h index 51822e0cc..3bfd2440e 100644 --- a/lib/BattleFieldHandler.h +++ b/lib/BattleFieldHandler.h @@ -14,7 +14,6 @@ #include "HeroBonus.h" #include "GameConstants.h" #include "IHandlerBase.h" -#include "Terrain.h" #include "battle/BattleHex.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index ac50e96c5..f4b5f47bd 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -16,7 +16,6 @@ #include "CGameState.h" #include "CTownHandler.h" #include "CModHandler.h" -#include "Terrain.h" #include "StringConstants.h" #include "serializer/JsonDeserializer.h" #include "serializer/JsonUpdater.h" @@ -288,7 +287,7 @@ std::string CCreature::nodeName() const bool CCreature::isItNativeTerrain(TerrainId terrain) const { auto native = getNativeTerrain(); - return native == terrain || native == Terrain::ANY_TERRAIN; + return native == terrain || native == ETerrainId::ANY_TERRAIN; } TerrainId CCreature::getNativeTerrain() const @@ -299,7 +298,7 @@ TerrainId CCreature::getNativeTerrain() const //this code is used in the CreatureTerrainLimiter::limit to setup battle bonuses //and in the CGHeroInstance::getNativeTerrain() to setup mevement bonuses or/and penalties. return hasBonus(selectorNoTerrainPenalty, selectorNoTerrainPenalty) - ? TerrainId(Terrain::ANY_TERRAIN) + ? TerrainId(ETerrainId::ANY_TERRAIN) : (*VLC->townh)[faction]->nativeTerrain; } diff --git a/lib/CCreatureHandler.h b/lib/CCreatureHandler.h index c621d617e..0912dd124 100644 --- a/lib/CCreatureHandler.h +++ b/lib/CCreatureHandler.h @@ -19,7 +19,6 @@ #include "JsonNode.h" #include "IHandlerBase.h" #include "CRandomGenerator.h" -#include "Terrain.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 21de886dd..c284852a8 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -17,6 +17,7 @@ #include "battle/BattleInfo.h" // for BattleInfo #include "NetPacks.h" // for InfoWindow #include "CModHandler.h" +#include "TerrainHandler.h" #include "spells/CSpellHandler.h" #include "mapping/CMap.h" #include "CPlayerState.h" diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 8bf972536..e8a268954 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -20,6 +20,7 @@ #include "CHeroHandler.h" #include "mapObjects/CObjectHandler.h" #include "CModHandler.h" +#include "TerrainHandler.h" #include "CSkillHandler.h" #include "mapping/CMap.h" #include "mapping/CMapService.h" @@ -1944,8 +1945,7 @@ BattleField CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & r if(map->isCoastalTile(tile)) //coastal tile is always ground return BattleField::fromString("sand_shore"); - return BattleField::fromString( - *RandomGeneratorUtil::nextItem(t.terType->battleFields, rand)); + return BattleField(*RandomGeneratorUtil::nextItem(t.terType->battleFields, rand)); } @@ -2135,7 +2135,7 @@ void CGameState::updateRumor() rumorId = *RandomGeneratorUtil::nextItem(sRumorTypes, rand); if(rumorId == RumorState::RUMOR_GRAIL) { - rumorExtra = getTile(map->grailPos)->terType->id; + rumorExtra = getTile(map->grailPos)->terType->getIndex(); break; } diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index 908ef3281..d7ed33199 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -18,7 +18,6 @@ #include "GameConstants.h" #include "mapObjects/CQuest.h" #include "VCMI_Lib.h" -#include "Terrain.h" VCMI_LIB_NAMESPACE_BEGIN @@ -420,7 +419,6 @@ CGeneralTextHandler::CGeneralTextHandler(): advobtxt (*this, "core.advevent" ), xtrainfo (*this, "core.xtrainfo" ), restypes (*this, "core.restypes" ), - terrainNames (*this, "core.terrname" ), randsign (*this, "core.randsign" ), overview (*this, "core.overview" ), arraytxt (*this, "core.arraytxt" ), @@ -452,7 +450,6 @@ CGeneralTextHandler::CGeneralTextHandler(): readToVector("core.advevent", "DATA/ADVEVENT.TXT" ); readToVector("core.xtrainfo", "DATA/XTRAINFO.TXT" ); readToVector("core.restypes", "DATA/RESTYPES.TXT" ); - readToVector("core.terrname", "DATA/TERRNAME.TXT" ); readToVector("core.randsign", "DATA/RANDSIGN.TXT" ); readToVector("core.crgen1", "DATA/CRGEN1.TXT" ); readToVector("core.crgen4", "DATA/CRGEN4.TXT" ); diff --git a/lib/CGeneralTextHandler.h b/lib/CGeneralTextHandler.h index bad392794..0e25fd38e 100644 --- a/lib/CGeneralTextHandler.h +++ b/lib/CGeneralTextHandler.h @@ -207,7 +207,6 @@ public: LegacyTextContainer advobtxt; LegacyTextContainer xtrainfo; LegacyTextContainer restypes; //names of resources - LegacyTextContainer terrainNames; LegacyTextContainer randsign; LegacyTextContainer seerEmpty; LegacyTextContainer seerNames; diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index 72bc7ce37..469acafa9 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -19,7 +19,6 @@ #include "CCreatureHandler.h" #include "CModHandler.h" #include "CTownHandler.h" -#include "Terrain.h" #include "mapObjects/CObjectHandler.h" //for hero specialty #include "CSkillHandler.h" #include @@ -345,11 +344,6 @@ CHeroHandler::~CHeroHandler() = default; CHeroHandler::CHeroHandler() { - loadTerrains(); - for(const auto & terrain : VLC->terrainTypeHandler->terrains()) - { - VLC->modh->identifiers.registerObject(CModHandler::scopeBuiltin(), "terrain", terrain.name, terrain.id); - } loadBallistics(); loadExperience(); } @@ -972,14 +966,6 @@ ui64 CHeroHandler::reqExp (ui32 level) const } } -void CHeroHandler::loadTerrains() -{ - for(const auto & terrain : VLC->terrainTypeHandler->terrains()) - { - terrCosts[terrain.id] = terrain.moveCost; - } -} - std::vector CHeroHandler::getDefaultAllowed() const { // Look Data/HOTRAITS.txt for reference diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index 023015705..93e4f0f87 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -18,7 +18,6 @@ #include "GameConstants.h" #include "HeroBonus.h" #include "IHandlerBase.h" -#include "Terrain.h" VCMI_LIB_NAMESPACE_BEGIN @@ -260,7 +259,6 @@ class DLL_LINKAGE CHeroHandler : public CHandlerBasescriptHandler, "script"))); #endif handlers.insert(std::make_pair("battlefields", ContentTypeHandler(VLC->battlefieldsHandler, "battlefield"))); + handlers.insert(std::make_pair("terrains", ContentTypeHandler(VLC->terrainTypeHandler, "terrain"))); + handlers.insert(std::make_pair("rivers", ContentTypeHandler(VLC->riverTypeHandler, "river"))); + handlers.insert(std::make_pair("roads", ContentTypeHandler(VLC->roadTypeHandler, "road"))); handlers.insert(std::make_pair("obstacles", ContentTypeHandler(VLC->obstacleHandler, "obstacle"))); //TODO: any other types of moddables? } diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index ba3813139..13d5500ae 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -1000,10 +1000,10 @@ bool CPathfinderHelper::passOneTurnLimitCheck(const PathNodeInfo & source) const TurnInfo::BonusCache::BonusCache(TConstBonusListPtr bl) { - for(const auto & terrain : VLC->terrainTypeHandler->terrains()) + for(const auto & terrain : VLC->terrainTypeHandler->objects) { noTerrainPenalty.push_back(static_cast( - bl->getFirst(Selector::type()(Bonus::NO_TERRAIN_PENALTY).And(Selector::subtype()(terrain.id))))); + bl->getFirst(Selector::type()(Bonus::NO_TERRAIN_PENALTY).And(Selector::subtype()(terrain->getIndex()))))); } freeShipBoarding = static_cast(bl->getFirst(Selector::type()(Bonus::FREE_SHIP_BOARDING))); diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 01cc372b4..c2d032988 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -13,7 +13,6 @@ #include "IGameCallback.h" #include "HeroBonus.h" #include "int3.h" -#include "Terrain.h" #include diff --git a/lib/CStack.cpp b/lib/CStack.cpp index 63046dfaa..da227fa3f 100644 --- a/lib/CStack.cpp +++ b/lib/CStack.cpp @@ -333,7 +333,7 @@ bool CStack::canBeHealed() const bool CStack::isOnNativeTerrain() const { //this code is called from CreatureTerrainLimiter::limit on battle start - auto res = nativeTerrain == Terrain::ANY_TERRAIN || nativeTerrain == battle->getTerrainType(); + auto res = nativeTerrain == ETerrainId::ANY_TERRAIN || nativeTerrain == battle->getTerrainType(); return res; } diff --git a/lib/CStack.h b/lib/CStack.h index fa31a3d03..a3805543e 100644 --- a/lib/CStack.h +++ b/lib/CStack.h @@ -14,7 +14,6 @@ #include "CCreatureHandler.h" //todo: remove #include "battle/BattleHex.h" #include "mapObjects/CGHeroInstance.h" // for commander serialization -#include "Terrain.h" #include "battle/CUnitState.h" diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index 6cda5f1e9..65d777dd5 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -28,10 +28,6 @@ VCMI_LIB_NAMESPACE_BEGIN const int NAMES_PER_TOWN=16; // number of town names per faction in H3 files. Json can define any number -const TerrainId CTownHandler::defaultGoodTerrain(Terrain::GRASS); -const TerrainId CTownHandler::defaultEvilTerrain(Terrain::LAVA); -const TerrainId CTownHandler::defaultNeutralTerrain(Terrain::ROUGH); - const std::map CBuilding::MODES = { { "normal", CBuilding::BUILD_NORMAL }, @@ -951,22 +947,6 @@ void CTownHandler::loadPuzzle(CFaction &faction, const JsonNode &source) assert(faction.puzzleMap.size() == GameConstants::PUZZLE_MAP_PIECES); } -TerrainId CTownHandler::getDefaultTerrainForAlignment(EAlignment::EAlignment alignment) const -{ - TerrainId terrain = defaultGoodTerrain; - - switch(alignment) - { - case EAlignment::EAlignment::EVIL: - terrain = defaultEvilTerrain; - break; - case EAlignment::EAlignment::NEUTRAL: - terrain = defaultNeutralTerrain; - break; - } - return terrain; -} - CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode & source, const std::string & identifier, size_t index) { auto faction = new CFaction(); @@ -987,11 +967,17 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode auto preferUndergound = source["preferUndergroundPlacement"]; faction->preferUndergroundPlacement = preferUndergound.isNull() ? false : preferUndergound.Bool(); - //Contructor is not called here, but operator= - auto nativeTerrain = source["nativeTerrain"]; - faction->nativeTerrain = nativeTerrain.isNull() - ? getDefaultTerrainForAlignment(faction->alignment) - : VLC->terrainTypeHandler->getInfoByName(nativeTerrain.String())->id; + // NOTE: semi-workaround - normally, towns are supposed to have native terrains. + // Towns without one are exceptions. So, vcmi requires nativeTerrain to be defined + // But allows it to be defined with explicit value of "none" if town should not have native terrain + // This is better than allowing such terrain-less towns silently, leading to issues with RMG + faction->nativeTerrain = ETerrainId::NONE; + if ( !source["nativeTerrain"].isNull() && source["nativeTerrain"].String() != "none") + { + VLC->modh->identifiers.requestIdentifier("terrain", source["nativeTerrain"], [=](int32_t index){ + faction->nativeTerrain = TerrainId(index); + }); + } if (!source["town"].isNull()) { diff --git a/lib/CTownHandler.h b/lib/CTownHandler.h index d85f1465b..b6472a38f 100644 --- a/lib/CTownHandler.h +++ b/lib/CTownHandler.h @@ -20,7 +20,6 @@ #include "LogicalExpression.h" #include "battle/BattleHex.h" #include "HeroBonus.h" -#include "Terrain.h" VCMI_LIB_NAMESPACE_BEGIN @@ -364,10 +363,6 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase requirementsToLoad; std::vector overriddenBidsToLoad; //list of buildings, which bonuses should be overridden. - const static TerrainId defaultGoodTerrain; - const static TerrainId defaultEvilTerrain; - const static TerrainId defaultNeutralTerrain; - static TPropagatorPtr & emptyPropagator(); void initializeRequirements(); @@ -397,7 +392,6 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase=(const A & a, const B & b) \ ID_LIKE_OPERATORS_INTERNAL(ENUM_NAME, CLASS_NAME, a, b.num) -#define OP_DECL_INT(CLASS_NAME, OP) \ -bool operator OP (const CLASS_NAME & b) const \ -{ \ - return num OP b.num; \ -} - #define INSTID_LIKE_CLASS_COMMON(CLASS_NAME, NUMERIC_NAME) \ public: \ CLASS_NAME() : BaseForID(-1) {} \ @@ -204,15 +198,79 @@ public: num += change; } - typedef BaseForID __SelfType; - OP_DECL_INT(__SelfType, ==) - OP_DECL_INT(__SelfType, !=) - OP_DECL_INT(__SelfType, <) - OP_DECL_INT(__SelfType, >) - OP_DECL_INT(__SelfType, <=) - OP_DECL_INT(__SelfType, >=) + bool operator == (const BaseForID & b) const { return num == b.num; } + bool operator <= (const BaseForID & b) const { return num <= b.num; } + bool operator >= (const BaseForID & b) const { return num >= b.num; } + bool operator != (const BaseForID & b) const { return num != b.num; } + bool operator < (const BaseForID & b) const { return num < b.num; } + bool operator > (const BaseForID & b) const { return num > b.num; } + + BaseForID & operator++() { ++num; return *this; } }; +template < typename T> +class Identifier : public IdTag +{ +public: + using EnumType = T; + using NumericType = typename std::underlying_type::type; + +private: + NumericType num; + +public: + NumericType getNum() const + { + return num; + } + + EnumType toEnum() const + { + return static_cast(num); + } + + template void serialize(Handler &h, const int version) + { + h & num; + } + + explicit Identifier(NumericType _num = -1) + { + num = _num; + } + + /* implicit */ Identifier(EnumType _num) + { + num = static_cast(_num); + } + + void advance(int change) + { + num += change; + } + + bool operator == (const Identifier & b) const { return num == b.num; } + bool operator <= (const Identifier & b) const { return num >= b.num; } + bool operator >= (const Identifier & b) const { return num <= b.num; } + bool operator != (const Identifier & b) const { return num != b.num; } + bool operator < (const Identifier & b) const { return num < b.num; } + bool operator > (const Identifier & b) const { return num > b.num; } + + Identifier & operator++() + { + ++num; + return *this; + } + + Identifier operator++(int) + { + Identifier ret(*this); + ++num; + return ret; + } +}; + + template std::ostream & operator << (std::ostream & os, BaseForID id); @@ -224,6 +282,14 @@ std::ostream & operator << (std::ostream & os, BaseForID id) return os << static_cast(id.getNum()); } +template +std::ostream & operator << (std::ostream & os, Identifier id) +{ + //We use common type with short to force char and unsigned char to be promoted and formatted as numbers. + typedef typename std::common_type::NumericType>::type Number; + return os << static_cast(id.getNum()); +} + class ArtifactInstanceID : public BaseForID { INSTID_LIKE_CLASS_COMMON(ArtifactInstanceID, si32) @@ -830,55 +896,26 @@ public: ID_LIKE_OPERATORS(Obj, Obj::EObj) -namespace Terrain +enum class Road : int8_t { - enum ETerrain : si8 - { - NATIVE_TERRAIN = -4, - ANY_TERRAIN = -3, - WRONG = -2, - BORDER = -1, - FIRST_REGULAR_TERRAIN = 0, - DIRT = 0, - SAND, - GRASS, - SNOW, - SWAMP, - ROUGH, - SUBTERRANEAN, - LAVA, - WATER, - ROCK, - ORIGINAL_TERRAIN_COUNT - }; -} + NO_ROAD = 0, + FIRST_REGULAR_ROAD = 1, + DIRT_ROAD = 1, + GRAVEL_ROAD = 2, + COBBLESTONE_ROAD = 3, + ORIGINAL_ROAD_COUNT //+1 +}; -namespace Road +enum class River : int8_t { - enum ERoad : ui8 - { - NO_ROAD = 0, - FIRST_REGULAR_ROAD = 1, - DIRT_ROAD = 1, - GRAVEL_ROAD = 2, - COBBLESTONE_ROAD = 3, - ORIGINAL_ROAD_COUNT //+1 - }; -} - -namespace River -{ - enum ERiver : ui8 - { - NO_RIVER = 0, - FIRST_REGULAR_RIVER = 1, - WATER_RIVER = 1, - ICY_RIVER = 2, - MUD_RIVER = 3, - LAVA_RIVER = 4, - ORIGINAL_RIVER_COUNT //+1 - }; -} + NO_RIVER = 0, + FIRST_REGULAR_RIVER = 1, + WATER_RIVER = 1, + ICY_RIVER = 2, + MUD_RIVER = 3, + LAVA_RIVER = 4, + ORIGINAL_RIVER_COUNT //+1 +}; namespace SecSkillLevel { @@ -1182,7 +1219,29 @@ class BattleField : public BaseForID DLL_LINKAGE static BattleField fromString(std::string identifier); }; - + +enum class ETerrainId { + NATIVE_TERRAIN = -4, + ANY_TERRAIN = -3, + NONE = -1, + FIRST_REGULAR_TERRAIN = 0, + DIRT = 0, + SAND, + GRASS, + SNOW, + SWAMP, + ROUGH, + SUBTERRANEAN, + LAVA, + WATER, + ROCK, + ORIGINAL_REGULAR_TERRAIN_COUNT = ROCK +}; + +using TerrainId = Identifier; +using RoadId = Identifier; +using RiverId = Identifier; + class ObstacleInfo; class Obstacle : public BaseForID { @@ -1240,9 +1299,6 @@ typedef si64 TExpType; typedef std::pair TDmgRange; typedef si32 TBonusSubtype; typedef si32 TQuantity; -typedef si8 TerrainId; -typedef si8 RoadId; -typedef si8 RiverId; typedef int TRmgTemplateZoneId; @@ -1250,6 +1306,5 @@ typedef int TRmgTemplateZoneId; #undef ID_LIKE_OPERATORS #undef ID_LIKE_OPERATORS_INTERNAL #undef INSTID_LIKE_CLASS_COMMON -#undef OP_DECL_INT VCMI_LIB_NAMESPACE_END diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 575e7c35a..a69628092 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -22,6 +22,7 @@ #include "CStack.h" #include "CArtHandler.h" #include "CModHandler.h" +#include "TerrainHandler.h" #include "StringConstants.h" #include "battle/BattleInfo.h" @@ -2106,7 +2107,7 @@ bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest) } CreatureTerrainLimiter::CreatureTerrainLimiter() - : terrainType(Terrain::NATIVE_TERRAIN) + : terrainType(ETerrainId::NATIVE_TERRAIN) { } @@ -2120,7 +2121,7 @@ int CreatureTerrainLimiter::limit(const BonusLimitationContext &context) const const CStack *stack = retrieveStackBattle(&context.node); if(stack) { - if (terrainType == Terrain::NATIVE_TERRAIN)//terrainType not specified = native + if (terrainType == ETerrainId::NATIVE_TERRAIN)//terrainType not specified = native { return !stack->isOnNativeTerrain(); } @@ -2136,8 +2137,8 @@ int CreatureTerrainLimiter::limit(const BonusLimitationContext &context) const std::string CreatureTerrainLimiter::toString() const { boost::format fmt("CreatureTerrainLimiter(terrainType=%s)"); - auto terrainName = VLC->terrainTypeHandler->terrains()[terrainType].name; - fmt % (terrainType == Terrain::NATIVE_TERRAIN ? "native" : terrainName); + auto terrainName = VLC->terrainTypeHandler->getById(terrainType)->getJsonKey(); + fmt % (terrainType == ETerrainId::NATIVE_TERRAIN ? "native" : terrainName); return fmt.str(); } @@ -2146,7 +2147,7 @@ JsonNode CreatureTerrainLimiter::toJsonNode() const JsonNode root(JsonNode::JsonType::DATA_STRUCT); root["type"].String() = "CREATURE_TERRAIN_LIMITER"; - auto terrainName = VLC->terrainTypeHandler->terrains()[terrainType].name; + auto terrainName = VLC->terrainTypeHandler->getById(terrainType)->getJsonKey(); root["parameters"].Vector().push_back(JsonUtils::stringNode(terrainName)); return root; diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 11af6f8b1..6422b20e5 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -11,7 +11,6 @@ #include "GameConstants.h" #include "JsonNode.h" -#include "Terrain.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 8e4210d15..03b5edfe6 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -32,6 +32,9 @@ #include "CPlayerState.h" #include "CSkillHandler.h" #include "ScriptHandler.h" +#include "RoadHandler.h" +#include "RiverHandler.h" +#include "TerrainHandler.h" #include "serializer/Connection.h" diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 5e4e869ea..b1ed9949c 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -27,6 +27,7 @@ #include "mapping/CMapInfo.h" #include "StartInfo.h" #include "CPlayerState.h" +#include "TerrainHandler.h" VCMI_LIB_NAMESPACE_BEGIN @@ -720,13 +721,13 @@ DLL_LINKAGE void GiveHero::applyGs(CGameState *gs) DLL_LINKAGE void NewObject::applyGs(CGameState *gs) { - TerrainId terrainType = Terrain::BORDER; + TerrainId terrainType = ETerrainId::NONE; if(ID == Obj::BOAT && !gs->isInTheMap(pos)) //special handling for bug #3060 - pos outside map but visitablePos is not { CGObjectInstance testObject = CGObjectInstance(); testObject.pos = pos; - testObject.appearance = VLC->objtypeh->getHandlerFor(ID, subID)->getTemplates(Terrain::WATER).front(); + testObject.appearance = VLC->objtypeh->getHandlerFor(ID, subID)->getTemplates(ETerrainId::WATER).front(); const int3 previousXAxisTile = int3(pos.x - 1, pos.y, pos.z); assert(gs->isInTheMap(previousXAxisTile) && (testObject.visitablePos() == previousXAxisTile)); @@ -735,7 +736,7 @@ DLL_LINKAGE void NewObject::applyGs(CGameState *gs) else { const TerrainTile & t = gs->map->getTile(pos); - terrainType = t.terType->id; + terrainType = t.terType->getId(); } CGObjectInstance *o = nullptr; @@ -743,7 +744,7 @@ DLL_LINKAGE void NewObject::applyGs(CGameState *gs) { case Obj::BOAT: o = new CGBoat(); - terrainType = Terrain::WATER; //TODO: either boat should only spawn on water, or all water objects should be handled this way + terrainType = ETerrainId::WATER; //TODO: either boat should only spawn on water, or all water objects should be handled this way break; case Obj::MONSTER: //probably more options will be needed o = new CGCreature(); diff --git a/lib/ObstacleHandler.cpp b/lib/ObstacleHandler.cpp index 36b316881..c427d4640 100644 --- a/lib/ObstacleHandler.cpp +++ b/lib/ObstacleHandler.cpp @@ -10,6 +10,7 @@ #include "StdInc.h" #include "ObstacleHandler.h" #include "BattleFieldHandler.h" +#include "CModHandler.h" VCMI_LIB_NAMESPACE_BEGIN @@ -86,8 +87,13 @@ ObstacleInfo * ObstacleHandler::loadFromJson(const std::string & scope, const Js info->width = json["width"].Integer(); info->height = json["height"].Integer(); for(auto & t : json["allowedTerrains"].Vector()) - info->allowedTerrains.emplace_back(VLC->terrainTypeHandler->getInfoByName(t.String())->id); + { + VLC->modh->identifiers.requestIdentifier("terrain", t, [info](int32_t identifier){ + info->allowedTerrains.emplace_back(identifier); + }); + } for(auto & t : json["specialBattlefields"].Vector()) + info->allowedSpecialBfields.emplace_back(t.String()); info->blockedTiles = json["blockedTiles"].convertTo>(); info->isAbsoluteObstacle = json["absolute"].Bool(); diff --git a/lib/ObstacleHandler.h b/lib/ObstacleHandler.h index 77ed0a078..40fef6366 100644 --- a/lib/ObstacleHandler.h +++ b/lib/ObstacleHandler.h @@ -13,7 +13,6 @@ #include #include "GameConstants.h" #include "IHandlerBase.h" -#include "Terrain.h" #include "battle/BattleHex.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/PathfinderUtil.h b/lib/PathfinderUtil.h index 83e9025c2..d62a80c1b 100644 --- a/lib/PathfinderUtil.h +++ b/lib/PathfinderUtil.h @@ -9,6 +9,8 @@ */ #pragma once +#include "TerrainHandler.h" +#include "mapObjects/CObjectHandler.h" #include "mapping/CMapDefines.h" #include "CGameState.h" diff --git a/lib/RiverHandler.cpp b/lib/RiverHandler.cpp new file mode 100644 index 000000000..4236d6ce4 --- /dev/null +++ b/lib/RiverHandler.cpp @@ -0,0 +1,78 @@ +/* + * Terrain.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "RiverHandler.h" +#include "CModHandler.h" +#include "CGeneralTextHandler.h" + +VCMI_LIB_NAMESPACE_BEGIN + +RiverTypeHandler::RiverTypeHandler() +{ + objects.push_back(new RiverType); +} + +RiverType * RiverTypeHandler::loadFromJson( + const std::string & scope, + const JsonNode & json, + const std::string & identifier, + size_t index) +{ + RiverType * info = new RiverType; + + info->id = RiverId(index); + if (identifier.find(':') == std::string::npos) + info->identifier = scope + ":" + identifier; + else + info->identifier = identifier; + + info->tilesFilename = json["tilesFilename"].String(); + info->shortIdentifier = json["shortIdentifier"].String(); + info->deltaName = json["delta"].String(); + + VLC->generaltexth->registerString(info->getNameTextID(), json["text"].String()); + + return info; +} + +const std::vector & RiverTypeHandler::getTypeNames() const +{ + static const std::vector typeNames = { "river" }; + return typeNames; +} + +std::vector RiverTypeHandler::loadLegacyData(size_t dataSize) +{ + objects.resize(dataSize); + return {}; +} + +std::vector RiverTypeHandler::getDefaultAllowed() const +{ + return {}; +} + +std::string RiverType::getNameTextID() const +{ + return TextIdentifier( "river", identifier, "name" ).get(); +} + +std::string RiverType::getNameTranslated() const +{ + return VLC->generaltexth->translate(getNameTextID()); +} + +RiverType::RiverType(): + id(River::NO_RIVER), + identifier("core:empty") +{} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/RiverHandler.h b/lib/RiverHandler.h new file mode 100644 index 000000000..0dc57225a --- /dev/null +++ b/lib/RiverHandler.h @@ -0,0 +1,79 @@ +/* + * TerrainHandler.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include +#include +#include "GameConstants.h" +#include "IHandlerBase.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class DLL_LINKAGE RiverType : public EntityT +{ + friend class RiverTypeHandler; + std::string identifier; + RiverId id; + + const std::string & getName() const override { return identifier;} +public: + int32_t getIndex() const override { return id.getNum(); } + int32_t getIconIndex() const override { return 0; } + const std::string & getJsonKey() const override { return identifier;} + void registerIcons(const IconRegistar & cb) const override {} + RiverId getId() const override { return id;} + void updateFrom(const JsonNode & data) {}; + + std::string getNameTextID() const; + std::string getNameTranslated() const; + + std::string tilesFilename; + std::string shortIdentifier; + std::string deltaName; + + RiverType(); + + template void serialize(Handler& h, const int version) + { + h & tilesFilename; + h & identifier; + h & deltaName; + h & id; + } +}; + +class DLL_LINKAGE RiverTypeService : public EntityServiceT +{ +public: +}; + +class DLL_LINKAGE RiverTypeHandler : public CHandlerBase +{ +public: + virtual RiverType * loadFromJson( + const std::string & scope, + const JsonNode & json, + const std::string & identifier, + size_t index) override; + + RiverTypeHandler(); + + virtual const std::vector & getTypeNames() const override; + virtual std::vector loadLegacyData(size_t dataSize) override; + virtual std::vector getDefaultAllowed() const override; + + template void serialize(Handler & h, const int version) + { + h & objects; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/RoadHandler.cpp b/lib/RoadHandler.cpp new file mode 100644 index 000000000..d7f94eecd --- /dev/null +++ b/lib/RoadHandler.cpp @@ -0,0 +1,78 @@ +/* + * Terrain.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "RoadHandler.h" +#include "CModHandler.h" +#include "CGeneralTextHandler.h" + +VCMI_LIB_NAMESPACE_BEGIN + +RoadTypeHandler::RoadTypeHandler() +{ + objects.push_back(new RoadType); +} + +RoadType * RoadTypeHandler::loadFromJson( + const std::string & scope, + const JsonNode & json, + const std::string & identifier, + size_t index) +{ + RoadType * info = new RoadType; + + info->id = RoadId(index); + if (identifier.find(':') == std::string::npos) + info->identifier = scope + ":" + identifier; + else + info->identifier = identifier; + + info->tilesFilename = json["tilesFilename"].String(); + info->shortIdentifier = json["shortIdentifier"].String(); + info->movementCost = json["moveCost"].Integer(); + + VLC->generaltexth->registerString(info->getNameTextID(), json["text"].String()); + + return info; +} + +const std::vector & RoadTypeHandler::getTypeNames() const +{ + static const std::vector typeNames = { "road" }; + return typeNames; +} + +std::vector RoadTypeHandler::loadLegacyData(size_t dataSize) +{ + objects.resize(dataSize); + return {}; +} + +std::vector RoadTypeHandler::getDefaultAllowed() const +{ + return {}; +} + +std::string RoadType::getNameTextID() const +{ + return TextIdentifier( "road", identifier, "name" ).get(); +} + +std::string RoadType::getNameTranslated() const +{ + return VLC->generaltexth->translate(getNameTextID()); +} + +RoadType::RoadType(): + id(Road::NO_ROAD), + identifier("core:empty"), + movementCost(GameConstants::BASE_MOVEMENT_COST) +{} +VCMI_LIB_NAMESPACE_END diff --git a/lib/RoadHandler.h b/lib/RoadHandler.h new file mode 100644 index 000000000..2b74cdec2 --- /dev/null +++ b/lib/RoadHandler.h @@ -0,0 +1,79 @@ +/* + * TerrainHandler.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include +#include +#include "GameConstants.h" +#include "IHandlerBase.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class DLL_LINKAGE RoadType : public EntityT +{ + friend class RoadTypeHandler; + std::string identifier; + RoadId id; + + const std::string & getName() const override { return identifier;} +public: + int32_t getIndex() const override { return id.getNum(); } + int32_t getIconIndex() const override { return 0; } + const std::string & getJsonKey() const override { return identifier;} + void registerIcons(const IconRegistar & cb) const override {} + RoadId getId() const override { return id;} + void updateFrom(const JsonNode & data) {}; + + std::string getNameTextID() const; + std::string getNameTranslated() const; + + std::string tilesFilename; + std::string shortIdentifier; + ui8 movementCost; + + RoadType(); + + template void serialize(Handler& h, const int version) + { + h & tilesFilename; + h & identifier; + h & id; + h & movementCost; + } +}; + +class DLL_LINKAGE RoadTypeService : public EntityServiceT +{ +public: +}; + +class DLL_LINKAGE RoadTypeHandler : public CHandlerBase +{ +public: + virtual RoadType * loadFromJson( + const std::string & scope, + const JsonNode & json, + const std::string & identifier, + size_t index) override; + + RoadTypeHandler(); + + virtual const std::vector & getTypeNames() const override; + virtual std::vector loadLegacyData(size_t dataSize) override; + virtual std::vector getDefaultAllowed() const override; + + template void serialize(Handler & h, const int version) + { + h & objects; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/Terrain.cpp b/lib/Terrain.cpp deleted file mode 100644 index 5237a5c35..000000000 --- a/lib/Terrain.cpp +++ /dev/null @@ -1,473 +0,0 @@ -/* - * Terrain.cpp, part of VCMI engine - * - * Authors: listed in file AUTHORS in main folder - * - * License: GNU General Public License v2.0 or later - * Full text of license available in license.txt file, in main folder - * - */ - -#include "StdInc.h" -#include "Terrain.h" -#include "VCMI_Lib.h" -#include "CModHandler.h" - -VCMI_LIB_NAMESPACE_BEGIN - -//regular expression to change id for string at config -//("allowedTerrain"\s*:\s*\[.*)9(.*\],\n) -//\1"rock"\2 - -TerrainTypeHandler::TerrainTypeHandler() -{ - auto allConfigs = VLC->modh->getActiveMods(); - allConfigs.insert(allConfigs.begin(), CModHandler::scopeBuiltin()); - - initRivers(allConfigs); - recreateRiverMaps(); - initRoads(allConfigs); - recreateRoadMaps(); - initTerrains(allConfigs); //maps will be populated inside -} - -void TerrainTypeHandler::initTerrains(const std::vector & allConfigs) -{ - std::vector> resolveLater; - - objects.resize(Terrain::ORIGINAL_TERRAIN_COUNT); //make space for original terrains - - for(auto & mod : allConfigs) - { - if(!CResourceHandler::get(mod)->existsResource(ResourceID("config/terrains.json"))) - continue; - - JsonNode terrs(mod, ResourceID("config/terrains.json")); - for(auto & terr : terrs.Struct()) - { - TerrainType info(terr.first); //set name - - info.moveCost = static_cast(terr.second["moveCost"].Integer()); - const JsonVector &unblockedVec = terr.second["minimapUnblocked"].Vector(); - info.minimapUnblocked = - { - ui8(unblockedVec[0].Float()), - ui8(unblockedVec[1].Float()), - ui8(unblockedVec[2].Float()) - }; - - const JsonVector &blockedVec = terr.second["minimapBlocked"].Vector(); - info.minimapBlocked = - { - ui8(blockedVec[0].Float()), - ui8(blockedVec[1].Float()), - ui8(blockedVec[2].Float()) - }; - info.musicFilename = terr.second["music"].String(); - info.tilesFilename = terr.second["tiles"].String(); - - if(terr.second["type"].isNull()) - { - info.passabilityType = TerrainType::PassabilityType::LAND | TerrainType::PassabilityType::SURFACE; - } - else if (terr.second["type"].getType() == JsonNode::JsonType::DATA_VECTOR) - { - for(const auto& node : terr.second["type"].Vector()) - { - //Set bits - auto s = node.String(); - if (s == "LAND") info.passabilityType |= TerrainType::PassabilityType::LAND; - if (s == "WATER") info.passabilityType |= TerrainType::PassabilityType::WATER; - if (s == "ROCK") info.passabilityType |= TerrainType::PassabilityType::ROCK; - if (s == "SURFACE") info.passabilityType |= TerrainType::PassabilityType::SURFACE; - if (s == "SUB") info.passabilityType |= TerrainType::PassabilityType::SUBTERRANEAN; - } - } - else //should be string - one option only - { - auto s = terr.second["type"].String(); - if (s == "LAND") info.passabilityType = TerrainType::PassabilityType::LAND; - if (s == "WATER") info.passabilityType = TerrainType::PassabilityType::WATER; - if (s == "ROCK") info.passabilityType = TerrainType::PassabilityType::ROCK; - if (s == "SURFACE") info.passabilityType = TerrainType::PassabilityType::SURFACE; - if (s == "SUB") info.passabilityType = TerrainType::PassabilityType::SUBTERRANEAN; - } - - if(terr.second["river"].isNull()) - { - info.river = River::NO_RIVER; - } - else - { - info.river = getRiverByCode(terr.second["river"].String())->id; - } - - if(terr.second["horseSoundId"].isNull()) - { - info.horseSoundId = Terrain::ROCK; //rock sound as default - } - else - { - info.horseSoundId = static_cast(terr.second["horseSoundId"].Float()); - } - - if(!terr.second["text"].isNull()) - { - info.terrainText = terr.second["text"].String(); - } - - if(terr.second["code"].isNull()) - { - info.typeCode = terr.first.substr(0, 2); - } - else - { - info.typeCode = terr.second["code"].String(); - assert(info.typeCode.length() == 2); - } - - if(!terr.second["battleFields"].isNull()) - { - for(auto & t : terr.second["battleFields"].Vector()) - { - info.battleFields.emplace_back(t.String()); - } - } - - info.transitionRequired = false; - if(!terr.second["transitionRequired"].isNull()) - { - info.transitionRequired = terr.second["transitionRequired"].Bool(); - } - - info.terrainViewPatterns = "normal"; - if(!terr.second["terrainViewPatterns"].isNull()) - { - info.terrainViewPatterns = terr.second["terrainViewPatterns"].String(); - } - - if(!terr.second["originalTerrainId"].isNull()) - { - //place in reserved slot - info.id = (TerrainId)(terr.second["originalTerrainId"].Float()); - objects[info.id] = info; - } - else - { - //append at the end - info.id = static_cast(objects.size()); - objects.push_back(info); - } - TerrainId id = info.id; - - //Update terrain with this id in the future, after all terrain types are populated - - if(!terr.second["prohibitTransitions"].isNull()) - { - for(auto & t : terr.second["prohibitTransitions"].Vector()) - { - std::string prohibitedTerrainName = t.String(); - resolveLater.push_back([this, prohibitedTerrainName, id]() - { - //FIXME: is that reference to the element in vector? - objects[id].prohibitTransitions.emplace_back(getInfoByName(prohibitedTerrainName)->id); - }); - } - } - - if(terr.second["rockTerrain"].isNull()) - { - objects[id].rockTerrain = Terrain::ROCK; - } - else - { - auto rockTerrainName = terr.second["rockTerrain"].String(); - resolveLater.push_back([this, rockTerrainName, id]() - { - //FIXME: is that reference to the element in vector? - objects[id].rockTerrain = getInfoByName(rockTerrainName)->id; - }); - } - } - } - - for(size_t i = Terrain::FIRST_REGULAR_TERRAIN; i < Terrain::ORIGINAL_TERRAIN_COUNT; i++) - { - //Make sure that original terrains are loaded - assert(objects[i].id != Terrain::WRONG); - } - - recreateTerrainMaps(); - - for(auto& functor : resolveLater) - { - functor(); - } -} - -void TerrainTypeHandler::initRivers(const std::vector & allConfigs) -{ - riverTypes.resize(River::ORIGINAL_RIVER_COUNT); //make space for original rivers - //First object will be default NO_RIVER - - for(auto & mod : allConfigs) - { - if (!CResourceHandler::get(mod)->existsResource(ResourceID("config/rivers.json"))) - continue; - - JsonNode rivs(mod, ResourceID("config/rivers.json")); - for(auto & river : rivs.Struct()) - { - RiverType info; - - info.name = river.first; - info.fileName = river.second["animation"].String(); - info.code = river.second["code"].String(); - info.deltaName = river.second["delta"].String(); - - if (!river.second["originalRiverId"].isNull()) - { - info.id = static_cast(river.second["originalRiverId"].Float()); - riverTypes[info.id] = info; - } - else - { - info.id = static_cast(riverTypes.size()); - riverTypes.push_back(info); - } - } - } - - recreateRiverMaps(); -} - -void TerrainTypeHandler::initRoads(const std::vector & allConfigs) -{ - roadTypes.resize(Road::ORIGINAL_ROAD_COUNT); //make space for original rivers - //first object will be default NO_ROAD - - for(auto & mod : allConfigs) - { - if (!CResourceHandler::get(mod)->existsResource(ResourceID("config/roads.json"))) - continue; - - JsonNode rds(mod, ResourceID("config/roads.json")); - for(auto & road : rds.Struct()) - { - RoadType info; - - info.name = road.first; - info.fileName = road.second["animation"].String(); - info.code = road.second["code"].String(); - info.movementCost = static_cast(road.second["moveCost"].Float()); - - if (!road.second["originalRoadId"].isNull()) - { - info.id = static_cast(road.second["originalRoadId"].Float()); - roadTypes[info.id] = info; - } - else - { - info.id = static_cast(roadTypes.size()); - roadTypes.push_back(info); - } - } - } - - recreateRoadMaps(); -} - -void TerrainTypeHandler::recreateTerrainMaps() -{ - //This assumes the vector will never be updated or reallocated in the future - - for(size_t i = 0; i < objects.size(); i++) - { - const auto * terrainInfo = &objects[i]; - - terrainInfoByName[terrainInfo->name] = terrainInfo; - terrainInfoByCode[terrainInfo->typeCode] = terrainInfo; - terrainInfoById[terrainInfo->id] = terrainInfo; - } -} - -void TerrainTypeHandler::recreateRiverMaps() -{ - for(size_t i = River::FIRST_REGULAR_RIVER ; i < riverTypes.size(); i++) - { - const auto * riverInfo = &riverTypes[i]; - - riverInfoByName[riverInfo->name] = riverInfo; - riverInfoByCode[riverInfo->code] = riverInfo; - riverInfoById[riverInfo->id] = riverInfo; - } -} - -void TerrainTypeHandler::recreateRoadMaps() -{ - for(size_t i = Road::FIRST_REGULAR_ROAD ; i < roadTypes.size(); i++) - { - const auto * roadInfo = &roadTypes[i]; - - roadInfoByName[roadInfo->name] = roadInfo; - roadInfoByCode[roadInfo->code] = roadInfo; - roadInfoById[roadInfo->id] = roadInfo; - } -} - -const std::vector & TerrainTypeHandler::terrains() const -{ - //FIXME: somehow make it non-copyable? Pointers must point to original data and not its copy - return objects; -} - -const std::vector& TerrainTypeHandler::rivers() const -{ - return riverTypes; -} - -const std::vector& TerrainTypeHandler::roads() const -{ - return roadTypes; -} - -const TerrainType* TerrainTypeHandler::getInfoByName(const std::string& terrainName) const -{ - return terrainInfoByName.at(terrainName); -} - -const TerrainType* TerrainTypeHandler::getInfoByCode(const std::string& terrainCode) const -{ - return terrainInfoByCode.at(terrainCode); -} - -const TerrainType* TerrainTypeHandler::getInfoById(TerrainId id) const -{ - return terrainInfoById.at(id); -} - -const RiverType* TerrainTypeHandler::getRiverByName(const std::string& riverName) const -{ - return riverInfoByName.at(riverName); -} - -const RiverType* TerrainTypeHandler::getRiverByCode(const std::string& riverCode) const -{ - return riverInfoByCode.at(riverCode); -} - -const RiverType* TerrainTypeHandler::getRiverById(RiverId id) const -{ - return riverInfoById.at(id); -} - -const RoadType* TerrainTypeHandler::getRoadByName(const std::string& roadName) const -{ - return roadInfoByName.at(roadName); -} - -const RoadType* TerrainTypeHandler::getRoadByCode(const std::string& roadCode) const -{ - return roadInfoByCode.at(roadCode); -} - -const RoadType* TerrainTypeHandler::getRoadById(RoadId id) const -{ - return roadInfoById.at(id); -} - -std::ostream & operator<<(std::ostream & os, const TerrainType & terrainType) -{ - return os << static_cast(terrainType); -} - -TerrainType::operator std::string() const -{ - return name; -} - -TerrainType::TerrainType(const std::string& _name): - minimapBlocked({0,0,0}), //black - minimapUnblocked({ 128,128,128 }), //grey - name(_name), - river(River::NO_RIVER), - id(Terrain::WRONG), - rockTerrain(Terrain::ROCK), - moveCost(GameConstants::BASE_MOVEMENT_COST), - horseSoundId(0), - passabilityType(0), - transitionRequired(false) -{ -} - -bool TerrainType::operator==(const TerrainType& other) -{ - return id == other.id; -} - -bool TerrainType::operator!=(const TerrainType& other) -{ - return id != other.id; -} - -bool TerrainType::operator<(const TerrainType& other) -{ - return id < other.id; -} - -bool TerrainType::isLand() const -{ - return !isWater(); -} - -bool TerrainType::isWater() const -{ - return passabilityType & PassabilityType::WATER; -} - -bool TerrainType::isPassable() const -{ - return !(passabilityType & PassabilityType::ROCK); -} - -bool TerrainType::isSurface() const -{ - return passabilityType & PassabilityType::SURFACE; -} - -bool TerrainType::isUnderground() const -{ - return passabilityType & PassabilityType::SUBTERRANEAN; -} - -bool TerrainType::isSurfaceCartographerCompatible() const -{ - return isSurface(); -} - -bool TerrainType::isUndergroundCartographerCompatible() const -{ - return isLand() && isPassable() && !isSurface(); -} - -bool TerrainType::isTransitionRequired() const -{ - return transitionRequired; -} - -RiverType::RiverType(const std::string & fileName, const std::string & code, RiverId id): - fileName(fileName), - code(code), - id(id) -{ -} - -RoadType::RoadType(const std::string& fileName, const std::string& code, RoadId id): - fileName(fileName), - code(code), - id(id), - movementCost(GameConstants::BASE_MOVEMENT_COST) -{ -} - -VCMI_LIB_NAMESPACE_END diff --git a/lib/Terrain.h b/lib/Terrain.h deleted file mode 100644 index 818d70e9e..000000000 --- a/lib/Terrain.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Terrain.h, part of VCMI engine - * - * Authors: listed in file AUTHORS in main folder - * - * License: GNU General Public License v2.0 or later - * Full text of license available in license.txt file, in main folder - * - */ - -#pragma once - -#include "ConstTransitivePtr.h" -#include "GameConstants.h" -#include "JsonNode.h" - -VCMI_LIB_NAMESPACE_BEGIN - -class DLL_LINKAGE TerrainType -{ -public: - - enum PassabilityType : ui8 - { - LAND = 1, - WATER = 2, - SURFACE = 4, - SUBTERRANEAN = 8, - ROCK = 16 - }; - - std::vector battleFields; - std::vector prohibitTransitions; - std::array minimapBlocked; - std::array minimapUnblocked; - std::string name; - std::string musicFilename; - std::string tilesFilename; - std::string terrainText; - std::string typeCode; - std::string terrainViewPatterns; - RiverId river; - - TerrainId id; - TerrainId rockTerrain; - int moveCost; - int horseSoundId; - ui8 passabilityType; - bool transitionRequired; - - TerrainType(const std::string & name = ""); - - bool operator==(const TerrainType & other); - bool operator!=(const TerrainType & other); - bool operator<(const TerrainType & other); - - bool isLand() const; - bool isWater() const; - bool isPassable() const; - bool isSurface() const; - bool isUnderground() const; - bool isTransitionRequired() const; - bool isSurfaceCartographerCompatible() const; - bool isUndergroundCartographerCompatible() const; - - operator std::string() const; - - template void serialize(Handler &h, const int version) - { - h & battleFields; - h & prohibitTransitions; - h & minimapBlocked; - h & minimapUnblocked; - h & name; - h & musicFilename; - h & tilesFilename; - h & terrainText; - h & typeCode; - h & terrainViewPatterns; - h & rockTerrain; - h & river; - - h & id; - h & moveCost; - h & horseSoundId; - h & passabilityType; - h & transitionRequired; - } -}; - -class DLL_LINKAGE RiverType -{ -public: - std::string name; - std::string fileName; - std::string code; - std::string deltaName; - RiverId id; - - RiverType(const std::string & fileName = "", const std::string & code = "", RiverId id = River::NO_RIVER); - - template void serialize(Handler& h, const int version) - { - if(version >= 806) - { - h & name; - } - h & fileName; - h & code; - h & deltaName; - h & id; - } -}; - -class DLL_LINKAGE RoadType -{ -public: - std::string name; - std::string fileName; - std::string code; - RoadId id; - ui8 movementCost; - - RoadType(const std::string & fileName = "", const std::string& code = "", RoadId id = Road::NO_ROAD); - - template void serialize(Handler& h, const int version) - { - if(version >= 806) - { - h & name; - } - h & fileName; - h & code; - h & id; - h & movementCost; - } -}; - -DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const TerrainType & terrainType); - -class DLL_LINKAGE TerrainTypeHandler //TODO: public IHandlerBase ? -{ -public: - - TerrainTypeHandler(); - ~TerrainTypeHandler() {}; - - const std::vector & terrains() const; - const TerrainType * getInfoByName(const std::string & terrainName) const; - const TerrainType * getInfoByCode(const std::string & terrainCode) const; - const TerrainType * getInfoById(TerrainId id) const; - - const std::vector & rivers() const; - const RiverType * getRiverByName(const std::string & riverName) const; - const RiverType * getRiverByCode(const std::string & riverCode) const; - const RiverType * getRiverById(RiverId id) const; - - const std::vector & roads() const; - const RoadType * getRoadByName(const std::string & roadName) const; - const RoadType * getRoadByCode(const std::string & roadCode) const; - const RoadType * getRoadById(RoadId id) const; - - template void serialize(Handler &h, const int version) - { - h & objects; - h & riverTypes; - h & roadTypes; - - if (!h.saving) - { - recreateTerrainMaps(); - recreateRiverMaps(); - recreateRoadMaps(); - } - } - -private: - - std::vector objects; - std::vector riverTypes; - std::vector roadTypes; - - std::unordered_map terrainInfoByName; - std::unordered_map terrainInfoByCode; - std::unordered_map terrainInfoById; - - std::unordered_map riverInfoByName; - std::unordered_map riverInfoByCode; - std::unordered_map riverInfoById; - - std::unordered_map roadInfoByName; - std::unordered_map roadInfoByCode; - std::unordered_map roadInfoById; - - void initTerrains(const std::vector & allConfigs); - void initRivers(const std::vector & allConfigs); - void initRoads(const std::vector & allConfigs); - void recreateTerrainMaps(); - void recreateRiverMaps(); - void recreateRoadMaps(); - -}; - -VCMI_LIB_NAMESPACE_END diff --git a/lib/TerrainHandler.cpp b/lib/TerrainHandler.cpp new file mode 100644 index 000000000..c9e3fac67 --- /dev/null +++ b/lib/TerrainHandler.cpp @@ -0,0 +1,191 @@ +/* + * Terrain.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "TerrainHandler.h" +#include "CModHandler.h" +#include "CGeneralTextHandler.h" + +VCMI_LIB_NAMESPACE_BEGIN + +TerrainType * TerrainTypeHandler::loadFromJson( const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) +{ + TerrainType * info = new TerrainType; + + info->id = TerrainId(index); + + if (identifier.find(':') == std::string::npos) + info->identifier = scope + ":" + identifier; + else + info->identifier = identifier; + + info->moveCost = static_cast(json["moveCost"].Integer()); + info->musicFilename = json["music"].String(); + info->tilesFilename = json["tiles"].String(); + info->horseSound = json["horseSound"].String(); + info->horseSoundPenalty = json["horseSoundPenalty"].String(); + info->transitionRequired = json["transitionRequired"].Bool(); + info->terrainViewPatterns = json["terrainViewPatterns"].String(); + + VLC->generaltexth->registerString(info->getNameTextID(), json["text"].String()); + + const JsonVector & unblockedVec = json["minimapUnblocked"].Vector(); + info->minimapUnblocked = + { + ui8(unblockedVec[0].Float()), + ui8(unblockedVec[1].Float()), + ui8(unblockedVec[2].Float()) + }; + + const JsonVector &blockedVec = json["minimapBlocked"].Vector(); + info->minimapBlocked = + { + ui8(blockedVec[0].Float()), + ui8(blockedVec[1].Float()), + ui8(blockedVec[2].Float()) + }; + + info->passabilityType = 0; + + for(const auto& node : json["type"].Vector()) + { + //Set bits + auto s = node.String(); + if (s == "LAND") info->passabilityType |= TerrainType::PassabilityType::LAND; + if (s == "WATER") info->passabilityType |= TerrainType::PassabilityType::WATER; + if (s == "ROCK") info->passabilityType |= TerrainType::PassabilityType::ROCK; + if (s == "SURFACE") info->passabilityType |= TerrainType::PassabilityType::SURFACE; + if (s == "SUB") info->passabilityType |= TerrainType::PassabilityType::SUBTERRANEAN; + } + + info->river = River::NO_RIVER; + if(!json["river"].isNull()) + { + VLC->modh->identifiers.requestIdentifier("river", json["river"], [info](int32_t identifier) + { + info->river = RiverId(identifier); + }); + } + + info->shortIdentifier = json["shortIdentifier"].String(); + assert(info->shortIdentifier.length() == 2); + + for(auto & t : json["battleFields"].Vector()) + { + VLC->modh->identifiers.requestIdentifier("battlefield", t, [info](int32_t identifier) + { + info->battleFields.emplace_back(identifier); + }); + } + + for(auto & t : json["prohibitTransitions"].Vector()) + { + VLC->modh->identifiers.requestIdentifier("terrain", t, [info](int32_t identifier) + { + info->prohibitTransitions.emplace_back(identifier); + }); + } + + info->rockTerrain = ETerrainId::ROCK; + + if(!json["rockTerrain"].isNull()) + { + VLC->modh->identifiers.requestIdentifier("terrain", json["rockTerrain"], [info](int32_t identifier) + { + info->rockTerrain = TerrainId(identifier); + }); + } + + return info; +} + +const std::vector & TerrainTypeHandler::getTypeNames() const +{ + static const std::vector typeNames = { "terrain" }; + return typeNames; +} + +std::vector TerrainTypeHandler::loadLegacyData(size_t dataSize) +{ + objects.resize(dataSize); + + CLegacyConfigParser terrainParser("DATA/TERRNAME.TXT"); + + std::vector result; + do + { + JsonNode terrain; + terrain["text"].String() = terrainParser.readString(); + result.push_back(terrain); + } + while (terrainParser.endLine()); + + return result; +} + +std::vector TerrainTypeHandler::getDefaultAllowed() const +{ + return {}; +} + +bool TerrainType::isLand() const +{ + return !isWater(); +} + +bool TerrainType::isWater() const +{ + return passabilityType & PassabilityType::WATER; +} + +bool TerrainType::isPassable() const +{ + return !(passabilityType & PassabilityType::ROCK); +} + +bool TerrainType::isSurface() const +{ + return passabilityType & PassabilityType::SURFACE; +} + +bool TerrainType::isUnderground() const +{ + return passabilityType & PassabilityType::SUBTERRANEAN; +} + +bool TerrainType::isSurfaceCartographerCompatible() const +{ + return isSurface(); +} + +bool TerrainType::isUndergroundCartographerCompatible() const +{ + return isLand() && isPassable() && !isSurface(); +} + +bool TerrainType::isTransitionRequired() const +{ + return transitionRequired; +} + +std::string TerrainType::getNameTextID() const +{ + return TextIdentifier( "terrain", identifier, "name" ).get(); +} + +std::string TerrainType::getNameTranslated() const +{ + return VLC->generaltexth->translate(getNameTextID()); +} + +TerrainType::TerrainType() +{} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/TerrainHandler.h b/lib/TerrainHandler.h new file mode 100644 index 000000000..983e06cc3 --- /dev/null +++ b/lib/TerrainHandler.h @@ -0,0 +1,122 @@ +/* + * TerrainHandler.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include +#include +#include "GameConstants.h" +#include "IHandlerBase.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class DLL_LINKAGE TerrainType : public EntityT +{ + friend class TerrainTypeHandler; + std::string identifier; + TerrainId id; + ui8 passabilityType; + + const std::string & getName() const override { return identifier;} +public: + int32_t getIndex() const override { return id.getNum(); } + int32_t getIconIndex() const override { return 0; } + const std::string & getJsonKey() const override { return identifier;} + void registerIcons(const IconRegistar & cb) const override {} + TerrainId getId() const override { return id;} + void updateFrom(const JsonNode & data) {}; + + std::string getNameTextID() const; + std::string getNameTranslated() const; + + enum PassabilityType : ui8 + { + LAND = 1, + WATER = 2, + SURFACE = 4, + SUBTERRANEAN = 8, + ROCK = 16 + }; + + std::vector battleFields; + std::vector prohibitTransitions; + std::array minimapBlocked; + std::array minimapUnblocked; + std::string shortIdentifier; + std::string musicFilename; + std::string tilesFilename; + std::string terrainViewPatterns; + std::string horseSound; + std::string horseSoundPenalty; + + TerrainId rockTerrain; + RiverId river; + int moveCost; + bool transitionRequired; + + TerrainType(); + + bool isLand() const; + bool isWater() const; + bool isPassable() const; + bool isSurface() const; + bool isUnderground() const; + bool isTransitionRequired() const; + bool isSurfaceCartographerCompatible() const; + bool isUndergroundCartographerCompatible() const; + + template void serialize(Handler &h, const int version) + { + h & battleFields; + h & prohibitTransitions; + h & minimapBlocked; + h & minimapUnblocked; + h & identifier; + h & musicFilename; + h & tilesFilename; + h & shortIdentifier; + h & terrainViewPatterns; + h & rockTerrain; + h & river; + + h & id; + h & moveCost; + h & horseSound; + h & horseSoundPenalty; + h & passabilityType; + h & transitionRequired; + } +}; + +class DLL_LINKAGE TerrainTypeService : public EntityServiceT +{ +public: +}; + +class DLL_LINKAGE TerrainTypeHandler : public CHandlerBase +{ +public: + virtual TerrainType * loadFromJson( + const std::string & scope, + const JsonNode & json, + const std::string & identifier, + size_t index) override; + + virtual const std::vector & getTypeNames() const override; + virtual std::vector loadLegacyData(size_t dataSize) override; + virtual std::vector getDefaultAllowed() const override; + + template void serialize(Handler & h, const int version) + { + h & objects; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/VCMI_Lib.cpp b/lib/VCMI_Lib.cpp index 4b74bf86f..e8245d88a 100644 --- a/lib/VCMI_Lib.cpp +++ b/lib/VCMI_Lib.cpp @@ -18,6 +18,9 @@ #include "CHeroHandler.h" #include "mapObjects/CObjectHandler.h" #include "CTownHandler.h" +#include "RoadHandler.h" +#include "RiverHandler.h" +#include "TerrainHandler.h" #include "CBuildingHandler.h" #include "spells/CSpellHandler.h" #include "spells/effects/Registry.h" @@ -197,6 +200,8 @@ void LibClasses::init(bool onlyEssential) createHandler(bth, "Bonus type", pomtime); + createHandler(roadTypeHandler, "Road", pomtime); + createHandler(riverTypeHandler, "River", pomtime); createHandler(terrainTypeHandler, "Terrain", pomtime); createHandler(generaltexth, "General text", pomtime); diff --git a/lib/VCMI_Lib.h b/lib/VCMI_Lib.h index 738743aba..55e58712b 100644 --- a/lib/VCMI_Lib.h +++ b/lib/VCMI_Lib.h @@ -30,6 +30,8 @@ class BattleFieldHandler; class IBonusTypeHandler; class CBonusTypeHandler; class TerrainTypeHandler; +class RoadTypeHandler; +class RiverTypeHandler; class ObstacleHandler; class CTerrainViewPatternConfig; class CRmgTemplateStorage; @@ -86,7 +88,11 @@ public: CTownHandler * townh; CGeneralTextHandler * generaltexth; CModHandler * modh; + TerrainTypeHandler * terrainTypeHandler; + RoadTypeHandler * roadTypeHandler; + RiverTypeHandler * riverTypeHandler; + CTerrainViewPatternConfig * terviewh; CRmgTemplateStorage * tplh; BattleFieldHandler * battlefieldsHandler; @@ -127,6 +133,8 @@ public: h & skillh; h & battlefieldsHandler; h & obstacleHandler; + h & roadTypeHandler; + h & riverTypeHandler; h & terrainTypeHandler; if(!h.saving) diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index f1687ccef..581d82ddd 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -15,7 +15,6 @@ #include "../filesystem/Filesystem.h" #include "../mapObjects/CGTownInstance.h" #include "../CGeneralTextHandler.h" -#include "../Terrain.h" #include "../BattleFieldHandler.h" #include "../ObstacleHandler.h" diff --git a/lib/battle/BattleProxy.cpp b/lib/battle/BattleProxy.cpp index 0ebb475ba..ab0402c33 100644 --- a/lib/battle/BattleProxy.cpp +++ b/lib/battle/BattleProxy.cpp @@ -10,7 +10,6 @@ #include "StdInc.h" #include "BattleProxy.h" #include "Unit.h" -#include "Terrain.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 6b5301732..9b8bcdf3d 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -17,6 +17,8 @@ #include "../NetPacks.h" #include "../CGeneralTextHandler.h" #include "../CHeroHandler.h" +#include "../TerrainHandler.h" +#include "../RoadHandler.h" #include "../CModHandler.h" #include "../CSoundBase.h" #include "../spells/CSpellHandler.h" @@ -81,16 +83,16 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile & dest, const TerrainTile & f int64_t ret = GameConstants::BASE_MOVEMENT_COST; //if there is road both on dest and src tiles - use road movement cost - if(dest.roadType->id && from.roadType->id) + if(dest.roadType->getId() != Road::NO_ROAD && from.roadType->getId() != Road::NO_ROAD) { ret = std::max(dest.roadType->movementCost, from.roadType->movementCost); } - else if(ti->nativeTerrain != from.terType->id &&//the terrain is not native - ti->nativeTerrain != Terrain::ANY_TERRAIN && //no special creature bonus - !ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType->id)) //no special movement bonus + else if(ti->nativeTerrain != from.terType->getId() &&//the terrain is not native + ti->nativeTerrain != ETerrainId::ANY_TERRAIN && //no special creature bonus + !ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType->getIndex())) //no special movement bonus { - ret = VLC->heroh->terrCosts[from.terType->id]; + ret = VLC->heroh->terrCosts[from.terType->getId()]; ret -= ti->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::PATHFINDING); if(ret < GameConstants::BASE_MOVEMENT_COST) ret = GameConstants::BASE_MOVEMENT_COST; @@ -104,20 +106,21 @@ TerrainId CGHeroInstance::getNativeTerrain() const // This is clearly bug in H3 however intended behaviour is not clear. // Current VCMI behaviour will ignore neutrals in calculations so army in VCMI // will always have best penalty without any influence from player-defined stacks order + // and army that consist solely from neutral will always be considered to be on native terrain - // TODO: What should we do if all hero stacks are neutral creatures? - TerrainId nativeTerrain = Terrain::BORDER; + TerrainId nativeTerrain = ETerrainId::ANY_TERRAIN; for(auto stack : stacks) { TerrainId stackNativeTerrain = stack.second->type->getNativeTerrain(); //consider terrain bonuses e.g. Lodestar. - if(stackNativeTerrain == Terrain::BORDER) //where does this value come from? + if(stackNativeTerrain == ETerrainId::NONE) continue; - if(nativeTerrain == Terrain::BORDER) + + if(nativeTerrain == ETerrainId::ANY_TERRAIN) nativeTerrain = stackNativeTerrain; else if(nativeTerrain != stackNativeTerrain) - return Terrain::BORDER; + return ETerrainId::NONE; } return nativeTerrain; } @@ -519,7 +522,7 @@ void CGHeroInstance::initObj(CRandomGenerator & rand) if (ID != Obj::PRISON) { - auto terrain = cb->gameState()->getTile(visitablePos())->terType->id; + auto terrain = cb->gameState()->getTile(visitablePos())->terType->getId(); auto customApp = VLC->objtypeh->getHandlerFor(ID, type->heroClass->getIndex())->getOverride(terrain, this); if (customApp) appearance = customApp; diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 55b5ac447..65e8f85c6 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -20,6 +20,7 @@ #include "../CGameState.h" #include "../mapping/CMap.h" #include "../CPlayerState.h" +#include "../TerrainHandler.h" #include "../serializer/JsonSerializeFormat.h" #include "../HeroBonus.h" @@ -1132,7 +1133,7 @@ void CGTownInstance::setType(si32 ID, si32 subID) void CGTownInstance::updateAppearance() { - auto terrain = cb->gameState()->getTile(visitablePos())->terType->id; + auto terrain = cb->gameState()->getTile(visitablePos())->terType->getId(); //FIXME: not the best way to do this auto app = VLC->objtypeh->getHandlerFor(ID, subID)->getOverride(terrain, this); if (app) diff --git a/lib/mapObjects/CObjectClassesHandler.h b/lib/mapObjects/CObjectClassesHandler.h index 9324b9696..a4a2eb910 100644 --- a/lib/mapObjects/CObjectClassesHandler.h +++ b/lib/mapObjects/CObjectClassesHandler.h @@ -15,7 +15,6 @@ #include "../ConstTransitivePtr.h" #include "../IHandlerBase.h" #include "../JsonNode.h" -#include "Terrain.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index d6f27bbff..1ac9b2675 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -20,6 +20,7 @@ #include "../CGameState.h" #include "../StringConstants.h" #include "../mapping/CMap.h" +#include "../TerrainHandler.h" #include "CObjectClassesHandler.h" #include "CGTownInstance.h" @@ -207,13 +208,13 @@ void CGObjectInstance::setType(si32 ID, si32 subID) logGlobal->error("Unknown object type %d:%d at %s", ID, subID, visitablePos().toString()); return; } - if(!handler->getTemplates(tile.terType->id).empty()) + if(!handler->getTemplates(tile.terType->getId()).empty()) { - appearance = handler->getTemplates(tile.terType->id)[0]; + appearance = handler->getTemplates(tile.terType->getId())[0]; } else { - logGlobal->warn("Object %d:%d at %s has no templates suitable for terrain %s", ID, subID, visitablePos().toString(), tile.terType->name); + logGlobal->warn("Object %d:%d at %s has no templates suitable for terrain %s", ID, subID, visitablePos().toString(), tile.terType->getNameTranslated()); appearance = handler->getTemplates()[0]; // get at least some appearance since alternative is crash } diff --git a/lib/mapObjects/CommonConstructors.cpp b/lib/mapObjects/CommonConstructors.cpp index f66b4a82e..20f0ed114 100644 --- a/lib/mapObjects/CommonConstructors.cpp +++ b/lib/mapObjects/CommonConstructors.cpp @@ -13,6 +13,7 @@ #include "CGTownInstance.h" #include "CGHeroInstance.h" #include "CBank.h" +#include "../TerrainHandler.h" #include "../mapping/CMap.h" #include "../CHeroHandler.h" #include "../CCreatureHandler.h" @@ -84,7 +85,7 @@ CGObjectInstance * CTownInstanceConstructor::create(std::shared_ptrcb->getTile(object->pos)->terType->id, object); + auto templ = getOverride(object->cb->getTile(object->pos)->terType->getId(), object); if(templ) object->appearance = templ; } diff --git a/lib/mapObjects/ObjectTemplate.cpp b/lib/mapObjects/ObjectTemplate.cpp index d91536542..59727bbec 100644 --- a/lib/mapObjects/ObjectTemplate.cpp +++ b/lib/mapObjects/ObjectTemplate.cpp @@ -19,7 +19,7 @@ #include "CObjectHandler.h" #include "../CModHandler.h" #include "../JsonNode.h" -#include "../Terrain.h" +#include "../TerrainHandler.h" #include "CRewardableConstructor.h" @@ -157,22 +157,15 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser) // so these two fields can be interpreted as "strong affinity" and "weak affinity" towards terrains std::string & terrStr = strings[4]; // allowed terrains, 1 = object can be placed on this terrain - assert(terrStr.size() == Terrain::ROCK); // all terrains but rock - counting from 0 - for(TerrainId i = Terrain::FIRST_REGULAR_TERRAIN; i < Terrain::ROCK; i++) + assert(terrStr.size() == TerrainId(ETerrainId::ROCK).getNum()); // all terrains but rock - counting from 0 + for(TerrainId i = TerrainId(0); i < ETerrainId::ORIGINAL_REGULAR_TERRAIN_COUNT; ++i) { - if (terrStr[8-i] == '1') + if (terrStr[8-i.getNum()] == '1') allowedTerrains.insert(i); } //assuming that object can be placed on other land terrains - if(allowedTerrains.size() >= 8 && !allowedTerrains.count(Terrain::WATER)) - { - for(const auto & terrain : VLC->terrainTypeHandler->terrains()) - { - if(terrain.isLand() && terrain.isPassable()) - allowedTerrains.insert(terrain.id); - } - } + anyTerrain = allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER); id = Obj(boost::lexical_cast(strings[5])); subid = boost::lexical_cast(strings[6]); @@ -231,21 +224,14 @@ void ObjectTemplate::readMap(CBinaryReader & reader) reader.readUInt16(); ui16 terrMask = reader.readUInt16(); - for(size_t i = Terrain::FIRST_REGULAR_TERRAIN; i < Terrain::ROCK; i++) + for(TerrainId i = ETerrainId::FIRST_REGULAR_TERRAIN; i < ETerrainId::ORIGINAL_REGULAR_TERRAIN_COUNT; ++i) { - if (((terrMask >> i) & 1 ) != 0) + if (((terrMask >> i.getNum()) & 1 ) != 0) allowedTerrains.insert(i); } //assuming that object can be placed on other land terrains - if(allowedTerrains.size() >= 8 && !allowedTerrains.count(Terrain::WATER)) - { - for(const auto & terrain : VLC->terrainTypeHandler->terrains()) - { - if(terrain.isLand() && terrain.isPassable()) - allowedTerrains.insert(terrain.id); - } - } + anyTerrain = allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER); id = Obj(reader.readUInt32()); subid = reader.readUInt32(); @@ -288,28 +274,17 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain) { for(auto& entry : node["allowedTerrains"].Vector()) { - try { - allowedTerrains.insert(VLC->terrainTypeHandler->getInfoByName(entry.String())->id); - } - catch (const std::out_of_range & ) - { - logGlobal->warn("Failed to find terrain '%s' for object '%s'", entry.String(), animationFile); - } + VLC->modh->identifiers.requestIdentifier("terrain", entry, [this](int32_t identifier){ + allowedTerrains.insert(TerrainId(identifier)); + }); } + anyTerrain = false; } else { - for(const auto & terrain : VLC->terrainTypeHandler->terrains()) - { - if(!terrain.isPassable() || terrain.isWater()) - continue; - allowedTerrains.insert(terrain.id); - } + anyTerrain = true; } - if(withTerrain && allowedTerrains.empty()) - logGlobal->warn("Loaded template %s without allowed terrains!", animationFile); - auto charToTile = [&](const char & ch) -> ui8 { switch (ch) @@ -378,14 +353,14 @@ void ObjectTemplate::writeJson(JsonNode & node, const bool withTerrain) const if(withTerrain) { //assumed that ROCK and WATER terrains are not included - if(allowedTerrains.size() < (VLC->terrainTypeHandler->terrains().size() - 2)) + if(allowedTerrains.size() < (VLC->terrainTypeHandler->objects.size() - 2)) { JsonVector & data = node["allowedTerrains"].Vector(); for(auto type : allowedTerrains) { JsonNode value(JsonNode::JsonType::DATA_STRING); - value.String() = type; + value.String() = VLC->terrainTypeHandler->getById(type)->getJsonKey(); data.push_back(value); } } @@ -565,9 +540,14 @@ void ObjectTemplate::calculateVisitableOffset() visitableOffset = int3(0, 0, 0); } -bool ObjectTemplate::canBePlacedAt(TerrainId terrain) const +bool ObjectTemplate::canBePlacedAt(TerrainId terrainID) const { - return vstd::contains(allowedTerrains, terrain); + if (anyTerrain) + { + auto const & terrain = VLC->terrainTypeHandler->getById(terrainID); + return terrain->isLand() && terrain->isPassable(); + } + return vstd::contains(allowedTerrains, terrainID); } void ObjectTemplate::recalculate() diff --git a/lib/mapObjects/ObjectTemplate.h b/lib/mapObjects/ObjectTemplate.h index b492d1770..4f4affa67 100644 --- a/lib/mapObjects/ObjectTemplate.h +++ b/lib/mapObjects/ObjectTemplate.h @@ -35,6 +35,9 @@ class DLL_LINKAGE ObjectTemplate /// list of terrains on which this object can be placed std::set allowedTerrains; + /// or, allow placing object on any terrain + bool anyTerrain; + void afterLoadFixup(); public: diff --git a/lib/mapping/CDrawRoadsOperation.cpp b/lib/mapping/CDrawRoadsOperation.cpp index 4c529b17f..a2e252bfe 100644 --- a/lib/mapping/CDrawRoadsOperation.cpp +++ b/lib/mapping/CDrawRoadsOperation.cpp @@ -12,6 +12,9 @@ #include "CDrawRoadsOperation.h" #include "CMap.h" +#include "../RoadHandler.h" +#include "../RiverHandler.h" + VCMI_LIB_NAMESPACE_BEGIN const std::vector CDrawLinesOperation::patterns = @@ -338,12 +341,12 @@ std::string CDrawRiversOperation::getLabel() const void CDrawRoadsOperation::executeTile(TerrainTile & tile) { - tile.roadType = const_cast(&VLC->terrainTypeHandler->roads()[roadType]); + tile.roadType = const_cast(VLC->roadTypeHandler->getByIndex(roadType.getNum())); } void CDrawRiversOperation::executeTile(TerrainTile & tile) { - tile.riverType = const_cast(&VLC->terrainTypeHandler->rivers()[riverType]); + tile.riverType = const_cast(VLC->riverTypeHandler->getByIndex(riverType.getNum())); } bool CDrawRoadsOperation::canApplyPattern(const LinePattern & pattern) const @@ -358,22 +361,22 @@ bool CDrawRiversOperation::canApplyPattern(const LinePattern & pattern) const bool CDrawRoadsOperation::needUpdateTile(const TerrainTile & tile) const { - return tile.roadType->id != Road::NO_ROAD; + return tile.roadType->getId() != Road::NO_ROAD; } bool CDrawRiversOperation::needUpdateTile(const TerrainTile & tile) const { - return tile.riverType->id != River::NO_RIVER; + return tile.riverType->getId() != River::NO_RIVER; } bool CDrawRoadsOperation::tileHasSomething(const int3& pos) const { - return map->getTile(pos).roadType->id != Road::NO_ROAD; + return map->getTile(pos).roadType->getId() != Road::NO_ROAD; } bool CDrawRiversOperation::tileHasSomething(const int3& pos) const { - return map->getTile(pos).riverType->id != River::NO_RIVER; + return map->getTile(pos).riverType->getId() != River::NO_RIVER; } void CDrawRoadsOperation::updateTile(TerrainTile & tile, const LinePattern & pattern, const int flip) diff --git a/lib/mapping/CDrawRoadsOperation.h b/lib/mapping/CDrawRoadsOperation.h index bb31868d0..8ba2e7eee 100644 --- a/lib/mapping/CDrawRoadsOperation.h +++ b/lib/mapping/CDrawRoadsOperation.h @@ -80,7 +80,7 @@ private: class CDrawRiversOperation : public CDrawLinesOperation { public: - CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, RoadId roadType, CRandomGenerator * gen); + CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, RiverId roadType, CRandomGenerator * gen); std::string getLabel() const override; protected: diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index 490653924..edf9dec98 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -15,6 +15,9 @@ #include "../CCreatureHandler.h" #include "../CTownHandler.h" #include "../CHeroHandler.h" +#include "../RiverHandler.h" +#include "../RoadHandler.h" +#include "../TerrainHandler.h" #include "../mapObjects/CObjectClassesHandler.h" #include "../mapObjects/CGHeroInstance.h" #include "../CGeneralTextHandler.h" @@ -128,9 +131,9 @@ CCastleEvent::CCastleEvent() : town(nullptr) TerrainTile::TerrainTile(): terType(nullptr), terView(0), - riverType(const_cast(&VLC->terrainTypeHandler->rivers()[River::NO_RIVER])), + riverType(VLC->riverTypeHandler->getById(River::NO_RIVER)), riverDir(0), - roadType(const_cast(&VLC->terrainTypeHandler->roads()[Road::NO_ROAD])), + roadType(VLC->roadTypeHandler->getById(Road::NO_ROAD)), roadDir(0), extTileFlags(0), visitable(false), diff --git a/lib/mapping/CMapDefines.h b/lib/mapping/CMapDefines.h index f39e8eb27..52d6885d5 100644 --- a/lib/mapping/CMapDefines.h +++ b/lib/mapping/CMapDefines.h @@ -12,6 +12,10 @@ VCMI_LIB_NAMESPACE_BEGIN +class TerrainType; +class RiverType; +class RoadType; + /// The map event is an event which e.g. gives or takes resources of a specific /// amount to/from players and can appear regularly or once a time. class DLL_LINKAGE CMapEvent @@ -82,11 +86,11 @@ struct DLL_LINKAGE TerrainTile EDiggingStatus getDiggingStatus(const bool excludeTop = true) const; bool hasFavorableWinds() const; - TerrainType * terType; + const TerrainType * terType; ui8 terView; - RiverType * riverType; + const RiverType * riverType; ui8 riverDir; - RoadType * roadType; + const RoadType * roadType; ui8 roadDir; /// first two bits - how to rotate terrain graphic (next two - river graphic, next two - road); /// 7th bit - whether tile is coastal (allows disembarking if land or block movement if water); 8th bit - Favorable Winds effect diff --git a/lib/mapping/CMapEditManager.h b/lib/mapping/CMapEditManager.h index c8dcbabbc..04a52b39a 100644 --- a/lib/mapping/CMapEditManager.h +++ b/lib/mapping/CMapEditManager.h @@ -12,7 +12,6 @@ #include "../GameConstants.h" #include "CMapOperation.h" -#include "Terrain.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapping/CMapOperation.cpp b/lib/mapping/CMapOperation.cpp index 6f7798741..88d25768a 100644 --- a/lib/mapping/CMapOperation.cpp +++ b/lib/mapping/CMapOperation.cpp @@ -12,6 +12,7 @@ #include "CMapOperation.h" #include "../VCMI_Lib.h" +#include "../TerrainHandler.h" #include "CMap.h" #include "MapEditUtils.h" @@ -97,7 +98,7 @@ void CDrawTerrainOperation::execute() for(const auto & pos : terrainSel.getSelectedItems()) { auto & tile = map->getTile(pos); - tile.terType = const_cast(&VLC->terrainTypeHandler->terrains()[terType]); + tile.terType = const_cast(VLC->terrainTypeHandler->getById(terType)); invalidateTerrainViews(pos); } @@ -154,7 +155,7 @@ void CDrawTerrainOperation::updateTerrainTypes() rect.forEach([&](const int3& posToTest) { auto & terrainTile = map->getTile(posToTest); - if(centerTile.terType->id != terrainTile.terType->id) + if(centerTile.terType->getId() != terrainTile.terType->getId()) { auto formerTerType = terrainTile.terType; terrainTile.terType = centerTile.terType; @@ -257,7 +258,7 @@ void CDrawTerrainOperation::updateTerrainViews() { for(const auto & pos : invalidatedTerViews) { - const auto & patterns = VLC->terviewh->getTerrainViewPatterns(map->getTile(pos).terType->id); + const auto & patterns = VLC->terviewh->getTerrainViewPatterns(map->getTile(pos).terType->getId()); // Detect a pattern which fits best int bestPattern = -1; @@ -345,7 +346,7 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi int cy = pos.y + (i / 3) - 1; int3 currentPos(cx, cy, pos.z); bool isAlien = false; - TerrainType * terType = nullptr; + const TerrainType * terType = nullptr; if(!map->isInTheMap(currentPos)) { // position is not in the map, so take the ter type from the neighbor tile @@ -393,9 +394,9 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi { if(recDepth == 0 && map->isInTheMap(currentPos)) { - if(terType->id == centerTerType->id) + if(terType->getId() == centerTerType->getId()) { - const auto & patternForRule = VLC->terviewh->getTerrainViewPatternsById(centerTerType->id, rule.name); + const auto & patternForRule = VLC->terviewh->getTerrainViewPatternsById(centerTerType->getId(), rule.name); if(auto p = patternForRule) { auto rslt = validateTerrainView(currentPos, &(*p), 1); @@ -422,14 +423,14 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi bool nativeTestOk, nativeTestStrongOk; nativeTestOk = nativeTestStrongOk = (rule.isNativeStrong() || rule.isNativeRule()) && !isAlien; - if(centerTerType->id == Terrain::DIRT) + if(centerTerType->getId() == ETerrainId::DIRT) { nativeTestOk = rule.isNativeRule() && !terType->isTransitionRequired(); bool sandTestOk = (rule.isSandRule() || rule.isTransition()) && terType->isTransitionRequired(); applyValidationRslt(rule.isAnyRule() || sandTestOk || nativeTestOk || nativeTestStrongOk); } - else if(centerTerType->id == Terrain::SAND) + else if(centerTerType->getId() == ETerrainId::SAND) { applyValidationRslt(true); } @@ -551,12 +552,12 @@ CClearTerrainOperation::CClearTerrainOperation(CMap* map, CRandomGenerator* gen) { CTerrainSelection terrainSel(map); terrainSel.selectRange(MapRect(int3(0, 0, 0), map->width, map->height)); - addOperation(std::make_unique(map, terrainSel, Terrain::WATER, gen)); + addOperation(std::make_unique(map, terrainSel, ETerrainId::WATER, gen)); if(map->twoLevel) { terrainSel.clearSelection(); terrainSel.selectRange(MapRect(int3(0, 0, 1), map->width, map->height)); - addOperation(std::make_unique(map, terrainSel, Terrain::ROCK, gen)); + addOperation(std::make_unique(map, terrainSel, ETerrainId::ROCK, gen)); } } diff --git a/lib/mapping/MapEditUtils.cpp b/lib/mapping/MapEditUtils.cpp index b0e9dc822..a58e30ce7 100644 --- a/lib/mapping/MapEditUtils.cpp +++ b/lib/mapping/MapEditUtils.cpp @@ -13,6 +13,7 @@ #include "../filesystem/Filesystem.h" #include "../JsonNode.h" +#include "../TerrainHandler.h" #include "CMap.h" #include "CMapOperation.h" @@ -272,7 +273,7 @@ CTerrainViewPatternConfig::~CTerrainViewPatternConfig() const std::vector & CTerrainViewPatternConfig::getTerrainViewPatterns(TerrainId terrain) const { - auto iter = terrainViewPatterns.find(VLC->terrainTypeHandler->terrains()[terrain].terrainViewPatterns); + auto iter = terrainViewPatterns.find(VLC->terrainTypeHandler->getById(terrain)->terrainViewPatterns); if (iter == terrainViewPatterns.end()) return terrainViewPatterns.at("normal"); return iter->second; @@ -357,7 +358,7 @@ void CTerrainViewPatternUtils::printDebuggingInfoAboutTile(const CMap * map, int { auto debugTile = map->getTile(debugPos); - std::string terType = debugTile.terType->name.substr(0, 6); + std::string terType = debugTile.terType->shortIdentifier; line += terType; line.insert(line.end(), PADDED_LENGTH - terType.size(), ' '); } diff --git a/lib/mapping/MapEditUtils.h b/lib/mapping/MapEditUtils.h index 288cc7b5a..10ebe8e65 100644 --- a/lib/mapping/MapEditUtils.h +++ b/lib/mapping/MapEditUtils.h @@ -12,7 +12,7 @@ #include "../int3.h" #include "../CRandomGenerator.h" -#include "Terrain.h" +#include "../GameConstants.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 719a8eb1e..794e30fcc 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -24,6 +24,9 @@ #include "../mapObjects/CObjectClassesHandler.h" #include "../mapObjects/MapObjects.h" #include "../VCMI_Lib.h" +#include "../TerrainHandler.h" +#include "../RoadHandler.h" +#include "../RiverHandler.h" #include "../NetPacksBase.h" VCMI_LIB_NAMESPACE_BEGIN @@ -923,9 +926,6 @@ bool CMapLoaderH3M::loadArtifactToSlot(CGHeroInstance * hero, int slot) void CMapLoaderH3M::readTerrain() { map->initTerrain(); - const auto & terrains = VLC->terrainTypeHandler->terrains(); - const auto & rivers = VLC->terrainTypeHandler->rivers(); - const auto & roads = VLC->terrainTypeHandler->roads(); // Read terrain int3 pos; @@ -937,15 +937,17 @@ void CMapLoaderH3M::readTerrain() for(pos.x = 0; pos.x < map->width; pos.x++) { auto & tile = map->getTile(pos); - tile.terType = const_cast(&terrains[reader.readUInt8()]); + tile.terType = const_cast(VLC->terrainTypeHandler->getByIndex(reader.readUInt8())); tile.terView = reader.readUInt8(); - tile.riverType = const_cast(&rivers[reader.readUInt8()]); + tile.riverType = const_cast(VLC->riverTypeHandler->getByIndex(reader.readUInt8())); tile.riverDir = reader.readUInt8(); - tile.roadType = const_cast(&roads[reader.readUInt8()]); + tile.roadType = const_cast(VLC->roadTypeHandler->getByIndex(reader.readUInt8())); tile.roadDir = reader.readUInt8(); tile.extTileFlags = reader.readUInt8(); - tile.blocked = ((!tile.terType->isPassable() || tile.terType->id == Terrain::BORDER ) ? true : false); //underground tiles are always blocked + tile.blocked = !tile.terType->isPassable(); tile.visitable = 0; + + assert(tile.terType->getId() != ETerrainId::NONE); } } } diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index 158e94d10..fec83720b 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -19,6 +19,9 @@ #include "../CHeroHandler.h" #include "../CTownHandler.h" #include "../VCMI_Lib.h" +#include "../RiverHandler.h" +#include "../RoadHandler.h" +#include "../TerrainHandler.h" #include "../mapObjects/ObjectTemplate.h" #include "../mapObjects/CObjectHandler.h" #include "../mapObjects/CObjectClassesHandler.h" @@ -346,6 +349,37 @@ CMapFormatJson::CMapFormatJson(): } +TerrainType * CMapFormatJson::getTerrainByCode( std::string code) +{ + for ( auto const & object : VLC->terrainTypeHandler->objects) + { + if (object->shortIdentifier == code) + return const_cast(object.get()); + } + return nullptr; +} + +RiverType * CMapFormatJson::getRiverByCode( std::string code) +{ + for ( auto const & object : VLC->riverTypeHandler->objects) + { + if (object->shortIdentifier == code) + return const_cast(object.get()); + } + return nullptr; +} + +RoadType * CMapFormatJson::getRoadByCode( std::string code) +{ + for ( auto const & object : VLC->roadTypeHandler->objects) + { + if (object->shortIdentifier == code) + return const_cast(object.get()); + } + return nullptr; +} + + void CMapFormatJson::serializeAllowedFactions(JsonSerializeFormat & handler, std::set & value) { //TODO: unify allowed factions with others - make them std::vector @@ -949,7 +983,7 @@ void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile using namespace TerrainDetail; {//terrain type const std::string typeCode = src.substr(0, 2); - tile.terType = const_cast(VLC->terrainTypeHandler->getInfoByCode(typeCode)); + tile.terType = getTerrainByCode(typeCode); } int startPos = 2; //0+typeCode fixed length {//terrain view @@ -979,13 +1013,13 @@ void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile startPos += 2; try { - tile.roadType = const_cast(VLC->terrainTypeHandler->getRoadByCode(typeCode)); + tile.roadType = getRoadByCode(typeCode); } catch (const std::exception&) //it's not a road, it's a river { try { - tile.riverType = const_cast(VLC->terrainTypeHandler->getRiverByCode(typeCode)); + tile.riverType = getRiverByCode(typeCode); hasRoad = false; } catch (const std::exception&) @@ -1021,7 +1055,7 @@ void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile {//river type const std::string typeCode = src.substr(startPos, 2); startPos += 2; - tile.riverType = const_cast(VLC->terrainTypeHandler->getRiverByCode(typeCode)); + tile.riverType = getRiverByCode(typeCode); } {//river dir int pos = startPos; @@ -1289,12 +1323,12 @@ std::string CMapSaverJson::writeTerrainTile(const TerrainTile & tile) out.setf(std::ios::dec, std::ios::basefield); out.unsetf(std::ios::showbase); - out << tile.terType->typeCode << (int)tile.terView << flipCodes[tile.extTileFlags % 4]; + out << tile.terType->shortIdentifier << (int)tile.terView << flipCodes[tile.extTileFlags % 4]; - if(tile.roadType->id != Road::NO_ROAD) + if(tile.roadType->getId() != Road::NO_ROAD) out << tile.roadType << (int)tile.roadDir << flipCodes[(tile.extTileFlags >> 4) % 4]; - if(tile.riverType->id != River::NO_RIVER) + if(tile.riverType->getId() != River::NO_RIVER) out << tile.riverType << (int)tile.riverDir << flipCodes[(tile.extTileFlags >> 2) % 4]; return out.str(); diff --git a/lib/mapping/MapFormatJson.h b/lib/mapping/MapFormatJson.h index 149984e63..6b0ebe00f 100644 --- a/lib/mapping/MapFormatJson.h +++ b/lib/mapping/MapFormatJson.h @@ -26,6 +26,9 @@ struct TerrainTile; struct PlayerInfo; class CGObjectInstance; class AObjectTypeHandler; +class TerrainType; +class RoadType; +class RiverType; class JsonSerializeFormat; class JsonDeserializer; @@ -57,6 +60,10 @@ protected: CMapFormatJson(); + static TerrainType * getTerrainByCode( std::string code); + static RiverType * getRiverByCode( std::string code); + static RoadType * getRoadByCode( std::string code); + void serializeAllowedFactions(JsonSerializeFormat & handler, std::set & value); ///common part of header saving/loading diff --git a/lib/registerTypes/TypesLobbyPacks.cpp b/lib/registerTypes/TypesLobbyPacks.cpp index 3a65eb231..106529f37 100644 --- a/lib/registerTypes/TypesLobbyPacks.cpp +++ b/lib/registerTypes/TypesLobbyPacks.cpp @@ -22,6 +22,9 @@ #include "../CHeroHandler.h" #include "../spells/CSpellHandler.h" #include "../CTownHandler.h" +#include "../RoadHandler.h" +#include "../RiverHandler.h" +#include "../TerrainHandler.h" #include "../mapping/CCampaignHandler.h" #include "../NetPacks.h" #include "../mapObjects/CObjectClassesHandler.h" diff --git a/lib/rmg/CRmgTemplate.cpp b/lib/rmg/CRmgTemplate.cpp index 4e54e413a..b675cbf2a 100644 --- a/lib/rmg/CRmgTemplate.cpp +++ b/lib/rmg/CRmgTemplate.cpp @@ -16,7 +16,8 @@ #include "../mapping/CMap.h" #include "../VCMI_Lib.h" #include "../CTownHandler.h" -#include "../Terrain.h" +#include "../CModHandler.h" +#include "../TerrainHandler.h" #include "../serializer/JsonSerializeFormat.h" #include "../StringConstants.h" @@ -69,13 +70,12 @@ class TerrainEncoder public: static si32 decode(const std::string & identifier) { - return VLC->terrainTypeHandler->getInfoByCode(identifier)->id; + return *VLC->modh->identifiers.getIdentifier(VLC->modh->scopeGame(), "terrain", identifier); } static std::string encode(const si32 index) { - const auto& terrains = VLC->terrainTypeHandler->terrains(); - return (index >=0 && index < terrains.size()) ? terrains[index].name : ""; + return VLC->terrainTypeHandler->getByIndex(index)->getJsonKey(); } }; @@ -152,9 +152,9 @@ ZoneOptions::ZoneOptions() terrainTypeLikeZone(NO_ZONE), treasureLikeZone(NO_ZONE) { - for(const auto & terr : VLC->terrainTypeHandler->terrains()) - if(terr.isLand() && terr.isPassable()) - terrainTypes.insert(terr.id); + for(const auto & terr : VLC->terrainTypeHandler->objects) + if(terr->isLand() && terr->isPassable()) + terrainTypes.insert(terr->getId()); } ZoneOptions & ZoneOptions::operator=(const ZoneOptions & other) @@ -224,7 +224,7 @@ const std::set & ZoneOptions::getTerrainTypes() const void ZoneOptions::setTerrainTypes(const std::set & value) { - //assert(value.find(ETerrainType::WRONG) == value.end() && value.find(ETerrainType::BORDER) == value.end() && + //assert(value.find(ETerrainType::NONE) == value.end() && // value.find(ETerrainType::WATER) == value.end() && value.find(ETerrainType::ROCK) == value.end()); terrainTypes = value; } @@ -365,7 +365,7 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler) for(auto & ttype : terrainTypes) { JsonNode n; - n.String() = ttype; + n.String() = VLC->terrainTypeHandler->getById(ttype)->getJsonKey(); node.Vector().push_back(n); } } @@ -377,7 +377,10 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler) terrainTypes.clear(); for(auto ttype : node.Vector()) { - terrainTypes.emplace(VLC->terrainTypeHandler->getInfoByName(ttype.String())->id); + VLC->modh->identifiers.requestIdentifier("terrain", ttype, [this](int32_t identifier) + { + terrainTypes.emplace(identifier); + }); } } } diff --git a/lib/rmg/CRmgTemplate.h b/lib/rmg/CRmgTemplate.h index 50ce3227f..b683b8978 100644 --- a/lib/rmg/CRmgTemplate.h +++ b/lib/rmg/CRmgTemplate.h @@ -13,7 +13,6 @@ #include "../int3.h" #include "../GameConstants.h" #include "../ResourceSet.h" -#include "../Terrain.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/rmg/CZonePlacer.cpp b/lib/rmg/CZonePlacer.cpp index 03616a48f..543c9c9e2 100644 --- a/lib/rmg/CZonePlacer.cpp +++ b/lib/rmg/CZonePlacer.cpp @@ -11,6 +11,7 @@ #include "StdInc.h" #include "../CRandomGenerator.h" #include "CZonePlacer.h" +#include "../TerrainHandler.h" #include "../mapping/CMap.h" #include "../mapping/CMapEditManager.h" #include "CMapGenOptions.h" @@ -194,15 +195,15 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const else { auto & tt = (*VLC->townh)[faction]->nativeTerrain; - if(tt == Terrain::DIRT) + if(tt == ETerrainId::NONE) { //any / random zonesToPlace.push_back(zone); } else { - const auto & terrainType = VLC->terrainTypeHandler->terrains()[tt]; - if(terrainType.isUnderground() && !terrainType.isSurface()) + const auto & terrainType = VLC->terrainTypeHandler->getById(tt); + if(terrainType->isUnderground() && !terrainType->isSurface()) { //underground only zonesOnLevel[1]++; @@ -580,7 +581,7 @@ void CZonePlacer::assignZones(CRandomGenerator * rand) //make sure that terrain inside zone is not a rock //FIXME: reorder actions? - paintZoneTerrain(*zone.second, *rand, map, Terrain::SUBTERRANEAN); + paintZoneTerrain(*zone.second, *rand, map, ETerrainId::SUBTERRANEAN); } } logGlobal->info("Finished zone colouring"); diff --git a/lib/rmg/ConnectionsPlacer.cpp b/lib/rmg/ConnectionsPlacer.cpp index ddcd5ebfe..c63377a16 100644 --- a/lib/rmg/ConnectionsPlacer.cpp +++ b/lib/rmg/ConnectionsPlacer.cpp @@ -12,6 +12,7 @@ #include "ConnectionsPlacer.h" #include "CMapGenerator.h" #include "RmgMap.h" +#include "../TerrainHandler.h" #include "../mapping/CMap.h" #include "../mapping/CMapEditManager.h" #include "../mapObjects/CObjectClassesHandler.h" @@ -84,9 +85,11 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con //1. Try to make direct connection //Do if it's not prohibited by terrain settings - const auto& terrains = VLC->terrainTypeHandler->terrains(); - bool directProhibited = vstd::contains(terrains[zone.getTerrainType()].prohibitTransitions, otherZone->getTerrainType()) - || vstd::contains(terrains[otherZone->getTerrainType()].prohibitTransitions, zone.getTerrainType()); + const auto * ourTerrain = VLC->terrainTypeHandler->getById(zone.getTerrainType()); + const auto * otherTerrain = VLC->terrainTypeHandler->getById(otherZone->getTerrainType()); + + bool directProhibited = vstd::contains(ourTerrain->prohibitTransitions, otherZone->getTerrainType()) + || vstd::contains(otherTerrain->prohibitTransitions, zone.getTerrainType()); auto directConnectionIterator = dNeighbourZones.find(otherZoneId); if(!directProhibited && directConnectionIterator != dNeighbourZones.end()) { diff --git a/lib/rmg/Functions.cpp b/lib/rmg/Functions.cpp index 04846ebe4..226c2e10e 100644 --- a/lib/rmg/Functions.cpp +++ b/lib/rmg/Functions.cpp @@ -21,6 +21,7 @@ #include "RmgMap.h" #include "TileInfo.h" #include "RmgPath.h" +#include "../TerrainHandler.h" #include "../CTownHandler.h" #include "../mapping/CMapEditManager.h" #include "../mapping/CMap.h" @@ -119,9 +120,9 @@ void initTerrainType(Zone & zone, CMapGenerator & gen) { //collect all water terrain types std::vector waterTerrains; - for(const auto & terrain : VLC->terrainTypeHandler->terrains()) - if(terrain.isWater()) - waterTerrains.push_back(terrain.id); + for(const auto & terrain : VLC->terrainTypeHandler->objects) + if(terrain->isWater()) + waterTerrains.push_back(terrain->getId()); zone.setTerrainType(*RandomGeneratorUtil::nextItem(waterTerrains, gen.rand)); } @@ -137,20 +138,20 @@ void initTerrainType(Zone & zone, CMapGenerator & gen) } //Now, replace disallowed terrains on surface and in the underground - const auto & terrainType = VLC->terrainTypeHandler->terrains()[zone.getTerrainType()]; + const auto & terrainType = VLC->terrainTypeHandler->getById(zone.getTerrainType()); if(zone.isUnderground()) { - if(!terrainType.isUnderground()) + if(!terrainType->isUnderground()) { - zone.setTerrainType(Terrain::SUBTERRANEAN); + zone.setTerrainType(ETerrainId::SUBTERRANEAN); } } else { - if (!terrainType.isSurface()) + if (!terrainType->isSurface()) { - zone.setTerrainType(Terrain::DIRT); + zone.setTerrainType(ETerrainId::DIRT); } } } diff --git a/lib/rmg/RiverPlacer.cpp b/lib/rmg/RiverPlacer.cpp index 64ba549e5..de4dd0ace 100644 --- a/lib/rmg/RiverPlacer.cpp +++ b/lib/rmg/RiverPlacer.cpp @@ -13,6 +13,8 @@ #include "Functions.h" #include "CMapGenerator.h" #include "RmgMap.h" +#include "../RiverHandler.h" +#include "../TerrainHandler.h" #include "../mapping/CMap.h" #include "../mapping/CMapEditManager.h" #include "../mapObjects/CObjectClassesHandler.h" @@ -86,7 +88,7 @@ void RiverPlacer::init() void RiverPlacer::drawRivers() { map.getEditManager()->getTerrainSelection().setSelection(rivers.getTilesVector()); - map.getEditManager()->drawRiver(VLC->terrainTypeHandler->terrains()[zone.getTerrainType()].river, &generator.rand); + map.getEditManager()->drawRiver(VLC->terrainTypeHandler->getById(zone.getTerrainType())->river, &generator.rand); } char RiverPlacer::dump(const int3 & t) @@ -195,7 +197,7 @@ void RiverPlacer::preprocess() //calculate delta positions if(connectedToWaterZoneId > -1) { - auto river = VLC->terrainTypeHandler->terrains()[zone.getTerrainType()].river; + auto river = VLC->terrainTypeHandler->getById(zone.getTerrainType())->river; auto & a = neighbourZonesTiles[connectedToWaterZoneId]; auto availableArea = zone.areaPossible() + zone.freePaths(); for(auto & tileToProcess : availableArea.getTilesVector()) @@ -321,9 +323,9 @@ void RiverPlacer::preprocess() void RiverPlacer::connectRiver(const int3 & tile) { - auto riverType = VLC->terrainTypeHandler->terrains()[zone.getTerrainType()].river; - const auto & river = VLC->terrainTypeHandler->rivers()[riverType]; - if(river.id == River::NO_RIVER) + auto riverType = VLC->terrainTypeHandler->getById(zone.getTerrainType())->river; + const auto * river = VLC->riverTypeHandler->getById(riverType); + if(river->getId() == River::NO_RIVER) return; rmg::Area roads; @@ -381,9 +383,9 @@ void RiverPlacer::connectRiver(const int3 & tile) { if(tmplates.size() % 4 != 0) throw rmgException(boost::to_string(boost::format("River templates for (%d,%d) at terrain %s, river %s are incorrect") % - RIVER_DELTA_ID % RIVER_DELTA_SUBTYPE % zone.getTerrainType() % river.code)); + RIVER_DELTA_ID % RIVER_DELTA_SUBTYPE % zone.getTerrainType() % river->shortIdentifier)); - std::string targetTemplateName = river.deltaName + std::to_string(deltaOrientations[pos]) + ".def"; + std::string targetTemplateName = river->deltaName + std::to_string(deltaOrientations[pos]) + ".def"; for(auto & templ : tmplates) { if(templ->animationFile == targetTemplateName) diff --git a/lib/rmg/RmgMap.cpp b/lib/rmg/RmgMap.cpp index c1aac566e..f1a2a8ea8 100644 --- a/lib/rmg/RmgMap.cpp +++ b/lib/rmg/RmgMap.cpp @@ -84,7 +84,7 @@ void RmgMap::initTiles(CMapGenerator & generator) getEditManager()->clearTerrain(&generator.rand); getEditManager()->getTerrainSelection().selectRange(MapRect(int3(0, 0, 0), mapGenOptions.getWidth(), mapGenOptions.getHeight())); - getEditManager()->drawTerrain(Terrain::GRASS, &generator.rand); + getEditManager()->drawTerrain(ETerrainId::GRASS, &generator.rand); auto tmpl = mapGenOptions.getMapTemplate(); zones.clear(); diff --git a/lib/rmg/RmgObject.cpp b/lib/rmg/RmgObject.cpp index 6c1e27574..5edb324ae 100644 --- a/lib/rmg/RmgObject.cpp +++ b/lib/rmg/RmgObject.cpp @@ -18,6 +18,7 @@ #include "../mapObjects/CommonConstructors.h" #include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h #include "Functions.h" +#include "../TerrainHandler.h" VCMI_LIB_NAMESPACE_BEGIN @@ -121,7 +122,7 @@ void Object::Instance::setTemplate(TerrainId terrain) auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrain); if (templates.empty()) { - auto terrainName = VLC->terrainTypeHandler->terrains()[terrain].name; + auto terrainName = VLC->terrainTypeHandler->getById(terrain)->getNameTranslated(); throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s") % dObject.ID % dObject.subID % terrainName)); } dObject.appearance = templates.front(); @@ -293,14 +294,14 @@ void Object::Instance::finalize(RmgMap & map) if (!dObject.appearance) { auto terrainType = map.map().getTile(getPosition(true)).terType; - auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType->id); + auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType->getId()); if (templates.empty()) { throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") % dObject.ID % dObject.subID % getPosition(true).toString() % terrainType)); } else { - setTemplate(terrainType->id); + setTemplate(terrainType->getId()); } } diff --git a/lib/rmg/RoadPlacer.cpp b/lib/rmg/RoadPlacer.cpp index b8a037135..e39719f25 100644 --- a/lib/rmg/RoadPlacer.cpp +++ b/lib/rmg/RoadPlacer.cpp @@ -16,6 +16,7 @@ #include "RmgMap.h" #include "../mapping/CMap.h" #include "../mapping/CMapEditManager.h" +#include "../CModHandler.h" #include "RmgPath.h" VCMI_LIB_NAMESPACE_BEGIN @@ -78,8 +79,9 @@ void RoadPlacer::drawRoads(bool secondary) zone.areaPossible().subtract(roads); zone.freePaths().unite(roads); map.getEditManager()->getTerrainSelection().setSelection(roads.getTilesVector()); + std::string roadName = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType); - RoadId roadType = VLC->terrainTypeHandler->getRoadByName(roadName)->id; + RoadId roadType(*VLC->modh->identifiers.getIdentifier(VLC->modh->scopeGame(), "road", roadName)); map.getEditManager()->drawRoad(roadType, &generator.rand); } diff --git a/lib/rmg/RockPlacer.cpp b/lib/rmg/RockPlacer.cpp index 3cf9fbc25..119b00c75 100644 --- a/lib/rmg/RockPlacer.cpp +++ b/lib/rmg/RockPlacer.cpp @@ -17,6 +17,7 @@ #include "RmgMap.h" #include "CMapGenerator.h" #include "Functions.h" +#include "../TerrainHandler.h" #include "../CRandomGenerator.h" #include "../mapping/CMapEditManager.h" @@ -24,8 +25,8 @@ VCMI_LIB_NAMESPACE_BEGIN void RockPlacer::process() { - rockTerrain = VLC->terrainTypeHandler->terrains()[zone.getTerrainType()].rockTerrain; - assert(!VLC->terrainTypeHandler->terrains()[rockTerrain].isPassable()); + rockTerrain = VLC->terrainTypeHandler->getById(zone.getTerrainType())->rockTerrain; + assert(!VLC->terrainTypeHandler->getById(rockTerrain)->isPassable()); accessibleArea = zone.freePaths() + zone.areaUsed(); if(auto * m = zone.getModificator()) diff --git a/lib/rmg/TileInfo.h b/lib/rmg/TileInfo.h index 951334b6d..61e874a74 100644 --- a/lib/rmg/TileInfo.h +++ b/lib/rmg/TileInfo.h @@ -11,7 +11,6 @@ #pragma once #include "../GameConstants.h" -#include "../Terrain.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/rmg/WaterProxy.cpp b/lib/rmg/WaterProxy.cpp index d61913a63..4a203187b 100644 --- a/lib/rmg/WaterProxy.cpp +++ b/lib/rmg/WaterProxy.cpp @@ -12,6 +12,7 @@ #include "WaterProxy.h" #include "CMapGenerator.h" #include "RmgMap.h" +#include "../TerrainHandler.h" #include "../mapping/CMap.h" #include "../mapping/CMapEditManager.h" #include "../mapObjects/CObjectClassesHandler.h" @@ -44,7 +45,7 @@ void WaterProxy::process() { MAYBE_UNUSED(t); assert(map.isOnMap(t)); - assert(map.map().getTile(t).terType->id == zone.getTerrainType()); + assert(map.map().getTile(t).terType->getId() == zone.getTerrainType()); } for(auto z : map.getZones()) @@ -54,7 +55,7 @@ void WaterProxy::process() for(auto & t : z.second->area().getTilesVector()) { - if(map.map().getTile(t).terType->id == zone.getTerrainType()) + if(map.map().getTile(t).terType->getId() == zone.getTerrainType()) { z.second->areaPossible().erase(t); z.second->area().erase(t); diff --git a/lib/rmg/Zone.cpp b/lib/rmg/Zone.cpp index fef80250f..244483d5b 100644 --- a/lib/rmg/Zone.cpp +++ b/lib/rmg/Zone.cpp @@ -28,7 +28,7 @@ std::function AREA_NO_FILTER = [](const int3 & t) Zone::Zone(RmgMap & map, CMapGenerator & generator) : ZoneOptions(), townType(ETownType::NEUTRAL), - terrainType(Terrain::GRASS), + terrainType(ETerrainId::GRASS), map(map), generator(generator) { diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index 9ae6f1714..6889ae373 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -14,8 +14,8 @@ VCMI_LIB_NAMESPACE_BEGIN -const ui32 SERIALIZATION_VERSION = 807; -const ui32 MINIMAL_SERIALIZATION_VERSION = 805; +const ui32 SERIALIZATION_VERSION = 810; +const ui32 MINIMAL_SERIALIZATION_VERSION = 810; const std::string SAVEGAME_MAGIC = "VCMISVG"; class CHero; diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index 0acaf6244..06bb1fe2d 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -21,12 +21,15 @@ #include "../lib/VCMI_Lib.h" #include "../lib/logging/CBasicLogConfigurator.h" #include "../lib/CConfigHandler.h" +#include "../lib/CModHandler.h" #include "../lib/filesystem/Filesystem.h" #include "../lib/GameConstants.h" #include "../lib/mapping/CMapService.h" #include "../lib/mapping/CMap.h" #include "../lib/mapping/CMapEditManager.h" -#include "../lib/Terrain.h" +#include "../lib/RoadHandler.h" +#include "../lib/RiverHandler.h" +#include "../lib/TerrainHandler.h" #include "../lib/mapObjects/CObjectClassesHandler.h" #include "../lib/filesystem/CFilesystemLoader.h" @@ -542,32 +545,32 @@ void MainWindow::loadObjectsTree() { ui->terrainFilterCombo->addItem(""); //adding terrains - for(auto & terrain : VLC->terrainTypeHandler->terrains()) + for(auto & terrain : VLC->terrainTypeHandler->objects) { - QPushButton *b = new QPushButton(QString::fromStdString(terrain.name)); + QPushButton *b = new QPushButton(QString::fromStdString(terrain->getNameTranslated())); ui->terrainLayout->addWidget(b); - connect(b, &QPushButton::clicked, this, [this, terrain]{ terrainButtonClicked(terrain.id); }); + connect(b, &QPushButton::clicked, this, [this, terrain]{ terrainButtonClicked(terrain->getId()); }); //filter - ui->terrainFilterCombo->addItem(QString::fromStdString(terrain)); + ui->terrainFilterCombo->addItem(QString::fromStdString(terrain->getNameTranslated())); } //add spacer to keep terrain button on the top ui->terrainLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); //adding roads - for(auto & road : VLC->terrainTypeHandler->roads()) + for(auto & road : VLC->roadTypeHandler->objects) { - QPushButton *b = new QPushButton(QString::fromStdString(road.fileName)); + QPushButton *b = new QPushButton(QString::fromStdString(road->getNameTranslated())); ui->roadLayout->addWidget(b); - connect(b, &QPushButton::clicked, this, [this, road]{ roadOrRiverButtonClicked(road.id, true); }); + connect(b, &QPushButton::clicked, this, [this, road]{ roadOrRiverButtonClicked(road->getIndex(), true); }); } //add spacer to keep terrain button on the top ui->roadLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); //adding rivers - for(auto & river : VLC->terrainTypeHandler->rivers()) + for(auto & river : VLC->riverTypeHandler->objects) { - QPushButton *b = new QPushButton(QString::fromStdString(river.fileName)); + QPushButton *b = new QPushButton(QString::fromStdString(river->getNameTranslated())); ui->riverLayout->addWidget(b); - connect(b, &QPushButton::clicked, this, [this, river]{ roadOrRiverButtonClicked(river.id, false); }); + connect(b, &QPushButton::clicked, this, [this, river]{ roadOrRiverButtonClicked(river->getIndex(), false); }); } //add spacer to keep terrain button on the top ui->riverLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); @@ -930,7 +933,13 @@ void MainWindow::on_terrainFilterCombo_currentTextChanged(const QString &arg1) if(!objectBrowser) return; - objectBrowser->terrain = arg1.isEmpty() ? TerrainId(Terrain::ANY_TERRAIN) : VLC->terrainTypeHandler->getInfoByName(arg1.toStdString())->id; + objectBrowser->terrain = TerrainId(ETerrainId::ANY_TERRAIN); + if (!arg1.isEmpty()) + { + for (auto const & terrain : VLC->terrainTypeHandler->objects) + if (terrain->getJsonKey() == arg1.toStdString()) + objectBrowser->terrain = terrain->getId(); + } objectBrowser->invalidate(); objectBrowser->sort(0); } @@ -1122,7 +1131,7 @@ void MainWindow::on_actionUpdate_appearance_triggered() if(handler->isStaticObject()) { staticObjects.insert(obj); - if(obj->appearance->canBePlacedAt(terrain->id)) + if(obj->appearance->canBePlacedAt(terrain->getId())) { controller.scene(mapLevel)->selectionObjectsView.deselectObject(obj); continue; @@ -1133,13 +1142,13 @@ void MainWindow::on_actionUpdate_appearance_triggered() } else { - auto app = handler->getOverride(terrain->id, obj); + auto app = handler->getOverride(terrain->getId(), obj); if(!app) { - if(obj->appearance->canBePlacedAt(terrain->id)) + if(obj->appearance->canBePlacedAt(terrain->getId())) continue; - auto templates = handler->getTemplates(terrain->id); + auto templates = handler->getTemplates(terrain->getId()); if(templates.empty()) { ++errors; diff --git a/mapeditor/mainwindow.h b/mapeditor/mainwindow.h index 45f98184a..2408a4dd5 100644 --- a/mapeditor/mainwindow.h +++ b/mapeditor/mainwindow.h @@ -4,7 +4,6 @@ #include #include #include "mapcontroller.h" -#include "../lib/Terrain.h" #include "resourceExtractor/ResourceConverter.h" class ObjectBrowser; diff --git a/mapeditor/mapcontroller.cpp b/mapeditor/mapcontroller.cpp index b94634334..2a5d3fdd8 100644 --- a/mapeditor/mapcontroller.cpp +++ b/mapeditor/mapcontroller.cpp @@ -14,7 +14,7 @@ #include "../lib/mapping/CMapService.h" #include "../lib/mapping/CMap.h" #include "../lib/mapping/CMapEditManager.h" -#include "../lib/Terrain.h" +#include "../lib/TerrainHandler.h" #include "../lib/mapObjects/CObjectClassesHandler.h" #include "../lib/rmg/ObstaclePlacer.h" #include "../lib/CSkillHandler.h" @@ -408,7 +408,7 @@ void MapController::commitObstacleFill(int level) if(tl.blocked || tl.visitable) continue; - terrainSelected[tl.terType->id].blockedArea.add(t); + terrainSelected[tl.terType->getId()].blockedArea.add(t); } for(auto & sel : terrainSelected) diff --git a/mapeditor/mapcontroller.h b/mapeditor/mapcontroller.h index 1446225f6..338798201 100644 --- a/mapeditor/mapcontroller.h +++ b/mapeditor/mapcontroller.h @@ -13,7 +13,6 @@ #include "maphandler.h" #include "mapview.h" #include "../lib/mapping/CMap.h" -#include "../lib/Terrain.h" class MainWindow; class MapController diff --git a/mapeditor/maphandler.cpp b/mapeditor/maphandler.cpp index 1a49915ba..b9799eb0e 100644 --- a/mapeditor/maphandler.cpp +++ b/mapeditor/maphandler.cpp @@ -12,6 +12,9 @@ #include "StdInc.h" #include "maphandler.h" #include "graphics.h" +#include "../lib/RoadHandler.h" +#include "../lib/RiverHandler.h" +#include "../lib/TerrainHandler.h" #include "../lib/mapping/CMap.h" #include "../lib/mapObjects/CGHeroInstance.h" #include "../lib/mapObjects/CObjectClassesHandler.h" @@ -78,17 +81,17 @@ void MapHandler::initTerrainGraphics() std::map terrainFiles; std::map roadFiles; std::map riverFiles; - for(const auto & terrain : VLC->terrainTypeHandler->terrains()) + for(const auto & terrain : VLC->terrainTypeHandler->objects) { - terrainFiles[terrain.name] = terrain.tilesFilename; + terrainFiles[terrain->getJsonKey()] = terrain->tilesFilename; } - for(const auto & river : VLC->terrainTypeHandler->rivers()) + for(const auto & river : VLC->riverTypeHandler->objects) { - riverFiles[river.fileName] = river.fileName; + riverFiles[river->getJsonKey()] = river->tilesFilename; } - for(const auto & road : VLC->terrainTypeHandler->roads()) + for(const auto & road : VLC->roadTypeHandler->objects) { - roadFiles[road.fileName] = road.fileName; + roadFiles[road->getJsonKey()] = road->tilesFilename; } loadFlipped(terrainAnimations, terrainImages, terrainFiles); @@ -101,8 +104,7 @@ void MapHandler::drawTerrainTile(QPainter & painter, int x, int y, int z) auto & tinfo = map->getTile(int3(x, y, z)); ui8 rotation = tinfo.extTileFlags % 4; - //TODO: use ui8 instead of string key - auto terrainName = tinfo.terType->name; + auto terrainName = tinfo.terType->getJsonKey(); if(terrainImages.at(terrainName).size() <= tinfo.terView) return; @@ -116,9 +118,9 @@ void MapHandler::drawRoad(QPainter & painter, int x, int y, int z) auto & tinfo = map->getTile(int3(x, y, z)); auto * tinfoUpper = map->isInTheMap(int3(x, y - 1, z)) ? &map->getTile(int3(x, y - 1, z)) : nullptr; - if(tinfoUpper && tinfoUpper->roadType->id != Road::NO_ROAD) + if(tinfoUpper && tinfoUpper->roadType->getId() != Road::NO_ROAD) { - auto roadName = tinfoUpper->roadType->fileName; + auto roadName = tinfoUpper->roadType->getJsonKey(); QRect source(0, tileSize / 2, tileSize, tileSize / 2); ui8 rotation = (tinfoUpper->extTileFlags >> 4) % 4; bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3); @@ -128,9 +130,9 @@ void MapHandler::drawRoad(QPainter & painter, int x, int y, int z) } } - if(tinfo.roadType->id != Road::NO_ROAD) //print road from this tile + if(tinfo.roadType->getId() != Road::NO_ROAD) //print road from this tile { - auto roadName = tinfo.roadType->fileName; + auto roadName = tinfo.roadType->getJsonKey();; QRect source(0, 0, tileSize, tileSize / 2); ui8 rotation = (tinfo.extTileFlags >> 4) % 4; bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3); @@ -145,11 +147,11 @@ void MapHandler::drawRiver(QPainter & painter, int x, int y, int z) { auto & tinfo = map->getTile(int3(x, y, z)); - if(tinfo.riverType->id == River::NO_RIVER) + if(tinfo.riverType->getId() == River::NO_RIVER) return; //TODO: use ui8 instead of string key - auto riverName = tinfo.riverType->fileName; + auto riverName = tinfo.riverType->getJsonKey(); if(riverImages.at(riverName).size() <= tinfo.riverDir) return; diff --git a/mapeditor/objectbrowser.cpp b/mapeditor/objectbrowser.cpp index e7bbdc8c7..68d4addf1 100644 --- a/mapeditor/objectbrowser.cpp +++ b/mapeditor/objectbrowser.cpp @@ -13,7 +13,7 @@ #include "../lib/mapObjects/CObjectClassesHandler.h" ObjectBrowserProxyModel::ObjectBrowserProxyModel(QObject *parent) - : QSortFilterProxyModel{parent}, terrain(Terrain::ANY_TERRAIN) + : QSortFilterProxyModel{parent}, terrain(ETerrainId::ANY_TERRAIN) { } @@ -33,7 +33,7 @@ bool ObjectBrowserProxyModel::filterAcceptsRow(int source_row, const QModelIndex if(!filterAcceptsRowText(source_row, source_parent)) return false; - if(terrain == Terrain::ANY_TERRAIN) + if(terrain == ETerrainId::ANY_TERRAIN) return result; auto data = item->data().toJsonObject(); diff --git a/mapeditor/objectbrowser.h b/mapeditor/objectbrowser.h index 2c5433ad2..82ab6196b 100644 --- a/mapeditor/objectbrowser.h +++ b/mapeditor/objectbrowser.h @@ -11,7 +11,7 @@ #pragma once #include -#include "../lib/Terrain.h" +#include "../lib/GameConstants.h" class ObjectBrowserProxyModel : public QSortFilterProxyModel { diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 6bc846f1a..5e421fdb3 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -38,6 +38,7 @@ #include "../lib/VCMIDirs.h" #include "../lib/ScopeGuard.h" #include "../lib/CSoundBase.h" +#include "../lib/TerrainHandler.h" #include "CGameHandler.h" #include "CVCMIServer.h" #include "../lib/CCreatureSet.h" @@ -2242,9 +2243,9 @@ void CGameHandler::setupBattle(int3 tile, const CArmedInstance *armies[2], const battleResult.set(nullptr); const auto & t = *getTile(tile); - TerrainId terrain = t.terType->id; + TerrainId terrain = t.terType->getId(); if (gs->map->isCoastalTile(tile)) //coastal tile is always ground - terrain = Terrain::SAND; + terrain = ETerrainId::SAND; BattleField terType = gs->battleGetBattlefieldType(tile, getRandomGenerator()); if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat)