From aaa07e4d2ecafb1add7187c2dab99b2b72d572ee Mon Sep 17 00:00:00 2001 From: Nordsoft91 Date: Mon, 20 Jun 2022 17:39:50 +0300 Subject: [PATCH] New terrain support - part 1 (#755) Initial support of new terrains --- AI/Nullkiller/Pathfinding/AINodeStorage.cpp | 18 +- AI/Nullkiller/Pathfinding/Actors.cpp | 2 +- AI/VCAI/Pathfinding/AINodeStorage.cpp | 16 +- AI/VCAI/VCAI.h | 5 - client/CMusicHandler.cpp | 60 +++--- client/CMusicHandler.h | 17 +- client/CPlayerInterface.cpp | 14 +- client/Client.cpp | 12 +- client/battle/CBattleInterface.cpp | 2 +- client/mapHandler.cpp | 106 +++++----- client/mapHandler.h | 4 +- client/widgets/AdventureMapClasses.cpp | 36 ++-- client/widgets/AdventureMapClasses.h | 5 +- client/windows/CAdvmapInterface.cpp | 2 +- config/gameConfig.json | 4 + config/obstacles.json | 158 +++++++-------- config/randomMap.json | 4 +- config/terrains.json | 43 ++++- lib/CArtHandler.h | 15 +- lib/CCreatureHandler.cpp | 16 +- lib/CCreatureHandler.h | 20 +- lib/CGameInfoCallback.cpp | 2 +- lib/CGameState.cpp | 36 ++-- lib/CGameState.h | 9 +- lib/CGeneralTextHandler.cpp | 15 +- lib/CGeneralTextHandler.h | 2 +- lib/CHeroHandler.cpp | 19 +- lib/CHeroHandler.h | 38 +--- lib/CMakeLists.txt | 2 + lib/CModHandler.h | 29 +-- lib/CPathfinder.cpp | 28 ++- lib/CPathfinder.h | 3 +- lib/CPlayerState.h | 8 - lib/CSkillHandler.h | 14 +- lib/CStack.cpp | 8 +- lib/CStack.h | 5 +- lib/CTownHandler.cpp | 142 +------------- lib/CTownHandler.h | 61 ++---- lib/GameConstants.cpp | 32 --- lib/GameConstants.h | 48 +---- lib/HeroBonus.cpp | 15 +- lib/HeroBonus.h | 39 +--- lib/IGameCallback.cpp | 6 +- lib/JsonDetail.h | 2 +- lib/JsonNode.cpp | 12 +- lib/JsonNode.h | 11 +- lib/NetPacksLib.cpp | 6 +- lib/PathfinderUtil.h | 4 +- lib/StartInfo.h | 14 +- lib/StringConstants.h | 4 - lib/Terrain.cpp | 204 ++++++++++++++++++++ lib/Terrain.h | 93 +++++++++ lib/VCMI_Lib.cpp | 12 -- lib/VCMI_Lib.h | 22 +-- lib/battle/BattleInfo.cpp | 7 +- lib/battle/BattleInfo.h | 7 +- lib/battle/BattleProxy.cpp | 3 +- lib/battle/BattleProxy.h | 2 +- lib/battle/CBattleInfoEssentials.cpp | 4 +- lib/battle/CBattleInfoEssentials.h | 2 +- lib/battle/IBattleInfoCallback.h | 4 +- lib/battle/IBattleState.h | 2 +- lib/mapObjects/CGHeroInstance.cpp | 47 ++--- lib/mapObjects/CGHeroInstance.h | 25 +-- lib/mapObjects/CGTownInstance.cpp | 106 ---------- lib/mapObjects/CGTownInstance.h | 18 +- lib/mapObjects/CObjectClassesHandler.cpp | 10 +- lib/mapObjects/CObjectClassesHandler.h | 38 +--- lib/mapObjects/CObjectHandler.cpp | 2 +- lib/mapObjects/CObjectHandler.h | 10 +- lib/mapObjects/CQuest.h | 9 +- lib/mapObjects/CRewardableObject.h | 5 - lib/mapObjects/ObjectTemplate.cpp | 44 +++-- lib/mapObjects/ObjectTemplate.h | 10 +- lib/mapping/CCampaignHandler.cpp | 11 -- lib/mapping/CCampaignHandler.h | 18 +- lib/mapping/CDrawRoadsOperation.cpp | 6 +- lib/mapping/CDrawRoadsOperation.h | 4 +- lib/mapping/CMap.cpp | 14 +- lib/mapping/CMap.h | 24 +-- lib/mapping/CMapDefines.h | 6 +- lib/mapping/CMapEditManager.cpp | 46 ++--- lib/mapping/CMapEditManager.h | 13 +- lib/mapping/MapFormatH3M.cpp | 8 +- lib/mapping/MapFormatJson.cpp | 47 ++--- lib/rmg/CMapGenerator.cpp | 51 ++--- lib/rmg/CMapGenerator.h | 10 +- lib/rmg/CRmgTemplate.cpp | 56 ++++-- lib/rmg/CRmgTemplate.h | 9 +- lib/rmg/CRmgTemplateZone.cpp | 77 ++++++-- lib/rmg/CRmgTemplateZone.h | 18 +- lib/rmg/CZonePlacer.cpp | 38 ++-- lib/serializer/CSerializer.h | 4 +- lib/spells/CSpellHandler.cpp | 51 ----- lib/spells/CSpellHandler.h | 80 +------- scripting/lua/api/BattleCb.cpp | 4 +- server/CGameHandler.cpp | 14 +- server/CGameHandler.h | 26 +-- test/game/CGameStateTest.cpp | 2 +- test/googletest | 2 +- test/map/CMapEditManagerTest.cpp | 36 ++-- test/mock/mock_IBattleInfoCallback.h | 2 +- test/mock/mock_battle_IBattleState.h | 2 +- 103 files changed, 1066 insertions(+), 1472 deletions(-) create mode 100644 lib/Terrain.cpp create mode 100644 lib/Terrain.h diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index 1d2782d44..a52457ea3 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -88,24 +88,22 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta for(pos.z = 0; pos.z < sizes.z; ++pos.z) { const TerrainTile * tile = &gs->map->getTile(pos); - switch(tile->terType) + if(!tile->terType.isPassable()) + continue; + + if(tile->terType.isWater()) { - case ETerrainType::ROCK: - break; - - case ETerrainType::WATER: resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); if(useFlying) resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); if(useWaterWalking) resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); - break; - - default: + } + else + { resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); if(useFlying) resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); - break; } } } @@ -1428,4 +1426,4 @@ std::string AIPath::toString() const str << node.targetHero->name << "[" << std::hex << node.chainMask << std::dec << "]" << "->" << node.coord.toString() << "; "; return str.str(); -} \ No newline at end of file +} diff --git a/AI/Nullkiller/Pathfinding/Actors.cpp b/AI/Nullkiller/Pathfinding/Actors.cpp index e10a7e6d5..68d10fecc 100644 --- a/AI/Nullkiller/Pathfinding/Actors.cpp +++ b/AI/Nullkiller/Pathfinding/Actors.cpp @@ -70,7 +70,7 @@ int ChainActor::maxMovePoints(CGPathNode::ELayer layer) { #if AI_TRACE_LEVEL > 0 if(!hero) - throw std::exception("Asking movement points for static actor"); + throw std::logic_error("Asking movement points for static actor"); #endif return hero->maxMovePointsCached(layer, tiCache.get()); diff --git a/AI/VCAI/Pathfinding/AINodeStorage.cpp b/AI/VCAI/Pathfinding/AINodeStorage.cpp index 30bfc93a9..7c2fe609b 100644 --- a/AI/VCAI/Pathfinding/AINodeStorage.cpp +++ b/AI/VCAI/Pathfinding/AINodeStorage.cpp @@ -46,24 +46,22 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta for(pos.z=0; pos.z < sizes.z; ++pos.z) { const TerrainTile * tile = &gs->map->getTile(pos); - switch(tile->terType) + if(!tile->terType.isPassable()) + continue; + + if(tile->terType.isWater()) { - case ETerrainType::ROCK: - break; - - case ETerrainType::WATER: resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); if(useFlying) resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); if(useWaterWalking) resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); - break; - - default: + } + else + { resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); if(useFlying) resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); - break; } } } diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 9e1d69738..f19fed2ba 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -347,11 +347,6 @@ public: h & visitableObjs; h & alreadyVisited; h & reservedObjs; - if (version < 788 && !h.saving) - { - TResources saving; - h & saving; //mind the ambiguity - } h & status; h & battlename; h & heroesUnableToExplore; diff --git a/client/CMusicHandler.cpp b/client/CMusicHandler.cpp index 2f550780c..894b76124 100644 --- a/client/CMusicHandler.cpp +++ b/client/CMusicHandler.cpp @@ -19,6 +19,7 @@ #include "../lib/StringConstants.h" #include "../lib/CRandomGenerator.h" #include "../lib/VCMIDirs.h" +#include "../lib/Terrain.h" #define VCMI_SOUND_NAME(x) #define VCMI_SOUND_FILE(y) #y, @@ -92,20 +93,34 @@ CSoundHandler::CSoundHandler(): soundBase::pickup04, soundBase::pickup05, soundBase::pickup06, soundBase::pickup07 }; - horseSounds = // must be the same order as terrains (see ETerrainType); - { - soundBase::horseDirt, soundBase::horseSand, soundBase::horseGrass, - soundBase::horseSnow, soundBase::horseSwamp, soundBase::horseRough, - soundBase::horseSubterranean, soundBase::horseLava, - soundBase::horseWater, soundBase::horseRock - }; - battleIntroSounds = { soundBase::battle00, soundBase::battle01, soundBase::battle02, soundBase::battle03, soundBase::battle04, soundBase::battle05, soundBase::battle06, soundBase::battle07 }; + + //predefine terrain set + //TODO: need refactoring - support custom sounds for new terrains and load from json + int h3mTerrId = 0; + for(auto snd : + { + soundBase::horseDirt, soundBase::horseSand, soundBase::horseGrass, + soundBase::horseSnow, soundBase::horseSwamp, soundBase::horseRough, + soundBase::horseSubterranean, soundBase::horseLava, + soundBase::horseWater, soundBase::horseRock + }) + { + horseSounds[Terrain::createTerrainTypeH3M(h3mTerrId++)] = snd; + } + for(auto & terrain : Terrain::Manager::terrains()) + { + //since all sounds are hardcoded, let's keep it + if(vstd::contains(horseSounds, terrain)) + continue; + + horseSounds[terrain] = horseSounds.at(Terrain::createTerrainTypeH3M(Terrain::Manager::getInfo(terrain).horseSoundId)); + } }; void CSoundHandler::init() @@ -341,26 +356,22 @@ CMusicHandler::CMusicHandler(): return true; }); - int battleMusicID = 0; - int AIThemeID = 0; - for(const ResourceID & file : mp3files) { if(boost::algorithm::istarts_with(file.getName(), "MUSIC/Combat")) - addEntryToSet("battle", battleMusicID++, file.getName()); + addEntryToSet("battle", file.getName(), file.getName()); else if(boost::algorithm::istarts_with(file.getName(), "MUSIC/AITheme")) - addEntryToSet("enemy-turn", AIThemeID++, file.getName()); + addEntryToSet("enemy-turn", file.getName(), file.getName()); } - JsonNode terrains(ResourceID("config/terrains.json")); - for (auto entry : terrains.Struct()) + for(auto & terrain : Terrain::Manager::terrains()) { - int terrIndex = vstd::find_pos(GameConstants::TERRAIN_NAMES, entry.first); - addEntryToSet("terrain", terrIndex, "Music/" + entry.second["music"].String()); + auto & entry = Terrain::Manager::getInfo(terrain); + addEntryToSet("terrain", terrain, "Music/" + entry.musicFilename); } } -void CMusicHandler::addEntryToSet(std::string set, int musicID, std::string musicURI) +void CMusicHandler::addEntryToSet(const std::string & set, const std::string & musicID, const std::string & musicURI) { musicsSet[set][musicID] = musicURI; } @@ -388,7 +399,7 @@ void CMusicHandler::release() CAudioBase::release(); } -void CMusicHandler::playMusic(std::string musicURI, bool loop) +void CMusicHandler::playMusic(const std::string & musicURI, bool loop) { if (current && current->isTrack(musicURI)) return; @@ -396,7 +407,7 @@ void CMusicHandler::playMusic(std::string musicURI, bool loop) queueNext(this, "", musicURI, loop); } -void CMusicHandler::playMusicFromSet(std::string whichSet, bool loop) +void CMusicHandler::playMusicFromSet(const std::string & whichSet, bool loop) { auto selectedSet = musicsSet.find(whichSet); if (selectedSet == musicsSet.end()) @@ -412,8 +423,7 @@ void CMusicHandler::playMusicFromSet(std::string whichSet, bool loop) queueNext(this, whichSet, "", loop); } - -void CMusicHandler::playMusicFromSet(std::string whichSet, int entryID, bool loop) +void CMusicHandler::playMusicFromSet(const std::string & whichSet, const std::string & entryID, bool loop) { auto selectedSet = musicsSet.find(whichSet); if (selectedSet == musicsSet.end()) @@ -425,7 +435,7 @@ void CMusicHandler::playMusicFromSet(std::string whichSet, int entryID, bool loo auto selectedEntry = selectedSet->second.find(entryID); if (selectedEntry == selectedSet->second.end()) { - logGlobal->error("Error: playing non-existing entry %d from set: %s", entryID, whichSet); + logGlobal->error("Error: playing non-existing entry %s from set: %s", entryID, whichSet); return; } @@ -452,7 +462,7 @@ void CMusicHandler::queueNext(std::unique_ptr queued) } } -void CMusicHandler::queueNext(CMusicHandler *owner, std::string setName, std::string musicURI, bool looped) +void CMusicHandler::queueNext(CMusicHandler *owner, const std::string & setName, const std::string & musicURI, bool looped) { try { @@ -552,7 +562,7 @@ bool MusicEntry::play() if (!setName.empty()) { - auto set = owner->musicsSet[setName]; + const auto & set = owner->musicsSet[setName]; load(RandomGeneratorUtil::nextItem(set, CRandomGenerator::getDefault())->second); } diff --git a/client/CMusicHandler.h b/client/CMusicHandler.h index 36fe6f2ec..b2f3c9a81 100644 --- a/client/CMusicHandler.h +++ b/client/CMusicHandler.h @@ -16,6 +16,7 @@ struct _Mix_Music; struct SDL_RWops; typedef struct _Mix_Music Mix_Music; struct Mix_Chunk; +class Terrain; class CAudioBase { protected: @@ -81,8 +82,8 @@ public: // Sets std::vector pickupSounds; - std::vector horseSounds; std::vector battleIntroSounds; + std::map horseSounds; }; // Helper //now it looks somewhat useless @@ -118,6 +119,7 @@ public: class CMusicHandler: public CAudioBase { private: + //update volume on configuration change SettingsListener listener; void onVolumeChange(const JsonNode &volumeNode); @@ -125,26 +127,27 @@ private: std::unique_ptr current; std::unique_ptr next; - void queueNext(CMusicHandler *owner, std::string setName, std::string musicURI, bool looped); + void queueNext(CMusicHandler *owner, const std::string & setName, const std::string & musicURI, bool looped); void queueNext(std::unique_ptr queued); - std::map > musicsSet; + std::map> musicsSet; public: + CMusicHandler(); /// add entry with URI musicURI in set. Track will have ID musicID - void addEntryToSet(std::string set, int musicID, std::string musicURI); + void addEntryToSet(const std::string & set, const std::string & entryID, const std::string & musicURI); void init() override; void release() override; void setVolume(ui32 percent) override; /// play track by URI, if loop = true music will be looped - void playMusic(std::string musicURI, bool loop); + void playMusic(const std::string & musicURI, bool loop); /// play random track from this set - void playMusicFromSet(std::string musicSet, bool loop); + void playMusicFromSet(const std::string & musicSet, bool loop); /// play specific track from set - void playMusicFromSet(std::string musicSet, int entryID, bool loop); + void playMusicFromSet(const std::string & musicSet, const std::string & entryID, bool loop); void stopMusic(int fade_ms=1000); void musicFinishedCallback(); diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 57d97f0e1..779f973b9 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1351,12 +1351,6 @@ void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus template void CPlayerInterface::serializeTempl( Handler &h, const int version ) { - if(version < 774 && !h.saving) - { - bool observerInDuelMode = false; - h & observerInDuelMode; - } - h & wanderingHeroes; h & towns; h & sleepingHeroes; @@ -2742,8 +2736,8 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) { path.convert(0); - ETerrainType currentTerrain = ETerrainType::BORDER; // not init yet - ETerrainType newTerrain; + Terrain currentTerrain = Terrain("BORDER"); // not init yet + Terrain newTerrain; int sh = -1; auto canStop = [&](CGPathNode * node) -> bool @@ -2779,7 +2773,9 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) destinationTeleportPos = int3(-1); } if(i != path.nodes.size() - 1) + { sh = CCS->soundh->playSound(CCS->soundh->horseSounds[currentTerrain], -1); + } continue; } @@ -2797,7 +2793,7 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) #endif { newTerrain = cb->getTile(CGHeroInstance::convertPosition(currentCoord, false))->terType; - if (newTerrain != currentTerrain) + if(newTerrain != currentTerrain) { CCS->soundh->stopSound(sh); sh = CCS->soundh->playSound(CCS->soundh->horseSounds[newTerrain], -1); diff --git a/client/Client.cpp b/client/Client.cpp index 40ec2f7e6..a200c2044 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -274,12 +274,6 @@ void CClient::serialize(BinarySerializer & h, const int version) void CClient::serialize(BinaryDeserializer & h, const int version) { assert(!h.saving); - if(version < 787) - { - bool hotSeat = false; - h & hotSeat; - } - ui8 players = 0; h & players; @@ -337,11 +331,7 @@ void CClient::serialize(BinaryDeserializer & h, const int version) { JsonNode scriptsState; - if(version >= 800) - { - h & scriptsState; - } - + h & scriptsState; clientScripts->serializeState(h.saving, scriptsState); } diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index a47c209e8..e04f36b7a 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -454,7 +454,7 @@ CBattleInterface::~CBattleInterface() if (adventureInt && adventureInt->selection) { - int terrain = LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType; + auto & terrain = LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType; CCS->musich->playMusicFromSet("terrain", terrain, true); } animsAreDisplayed.setn(false); diff --git a/client/mapHandler.cpp b/client/mapHandler.cpp index 54ae44a0f..f33a8dad5 100644 --- a/client/mapHandler.cpp +++ b/client/mapHandler.cpp @@ -20,6 +20,7 @@ #include "../lib/CGameState.h" #include "../lib/CHeroHandler.h" #include "../lib/CTownHandler.h" +#include "../lib/CModHandler.h" #include "Graphics.h" #include "../lib/mapping/CMap.h" #include "../lib/CConfigHandler.h" @@ -29,6 +30,9 @@ #include "CMT.h" #include "CMusicHandler.h" #include "../lib/CRandomGenerator.h" +#include "../lib/Terrain.h" +#include "../lib/filesystem/ResourceID.h" +#include "../lib/JsonDetail.h" #define ADVOPT (conf.go()->ac) @@ -142,79 +146,68 @@ EMapAnimRedrawStatus CMapHandler::drawTerrainRectNew(SDL_Surface * targetSurface void CMapHandler::initTerrainGraphics() { - static const std::vector TERRAIN_FILES = + static const std::map ROAD_FILES = { - "DIRTTL", - "SANDTL", - "GRASTL", - "SNOWTL", - "SWMPTL", - - "ROUGTL", - "SUBBTL", - "LAVATL", - "WATRTL", - "ROCKTL" + {ROAD_NAMES[1], "dirtrd"}, + {ROAD_NAMES[2], "gravrd"}, + {ROAD_NAMES[3], "cobbrd"} }; - static const std::vector ROAD_FILES = + static const std::map RIVER_FILES = { - "dirtrd", - "gravrd", - "cobbrd" + {RIVER_NAMES[1], "clrrvr"}, + {RIVER_NAMES[2], "icyrvr"}, + {RIVER_NAMES[3], "mudrvr"}, + {RIVER_NAMES[4], "lavrvr"} }; + - static const std::vector RIVER_FILES = + auto loadFlipped = [](TFlippedAnimations & animation, TFlippedCache & cache, const std::map & files) { - "clrrvr", - "icyrvr", - "mudrvr", - "lavrvr" - }; - - auto loadFlipped = [](int types, TFlippedAnimations & animation, TFlippedCache & cache, const std::vector & files) - { - animation.resize(types); - cache.resize(types); - //no rotation and basic setup - for(int i = 0; i < types; i++) + for(auto & type : files) { - animation[i][0] = make_unique(files[i]); - animation[i][0]->preload(); - const size_t views = animation[i][0]->size(0); - cache[i].resize(views); + animation[type.first][0] = make_unique(type.second); + animation[type.first][0]->preload(); + const size_t views = animation[type.first][0]->size(0); + cache[type.first].resize(views); for(int j = 0; j < views; j++) - cache[i][j][0] = animation[i][0]->getImage(j); + cache[type.first][j][0] = animation[type.first][0]->getImage(j); } for(int rotation = 1; rotation < 4; rotation++) { - for(int i = 0; i < types; i++) + for(auto & type : files) { - animation[i][rotation] = make_unique(files[i]); - animation[i][rotation]->preload(); - const size_t views = animation[i][rotation]->size(0); + animation[type.first][rotation] = make_unique(type.second); + animation[type.first][rotation]->preload(); + const size_t views = animation[type.first][rotation]->size(0); for(int j = 0; j < views; j++) { - auto image = animation[i][rotation]->getImage(j); + auto image = animation[type.first][rotation]->getImage(j); if(rotation == 2 || rotation == 3) image->horizontalFlip(); if(rotation == 1 || rotation == 3) image->verticalFlip(); - cache[i][j][rotation] = image; + cache[type.first][j][rotation] = image; } } } }; - - loadFlipped(GameConstants::TERRAIN_TYPES, terrainAnimations, terrainImages, TERRAIN_FILES); - loadFlipped(3, roadAnimations, roadImages, ROAD_FILES); - loadFlipped(4, riverAnimations, riverImages, RIVER_FILES); + + std::map terrainFiles; + for(auto & terrain : Terrain::Manager::terrains()) + { + terrainFiles[terrain] = Terrain::Manager::getInfo(terrain).tilesFilename; + } + + loadFlipped(terrainAnimations, terrainImages, terrainFiles); + loadFlipped(roadAnimations, roadImages, ROAD_FILES); + loadFlipped(riverAnimations, riverImages, RIVER_FILES); // Create enough room for the whole map and its frame @@ -626,6 +619,9 @@ void CMapHandler::CMapBlitter::drawTileTerrain(SDL_Surface * targetSurf, const T Rect destRect(realTileRect); ui8 rotation = tinfo.extTileFlags % 4; + + if(parent->terrainImages[tinfo.terType].size()<=tinfo.terView) + return; drawElement(EMapCacheType::TERRAIN, parent->terrainImages[tinfo.terType][tinfo.terView][rotation], nullptr, targetSurf, &destRect); } @@ -802,21 +798,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 != ERoadType::NO_ROAD) + if (tinfoUpper && tinfoUpper->roadType != ROAD_NAMES[0]) { 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 - 1][tinfoUpper->roadDir][rotation], + drawElement(EMapCacheType::ROADS, parent->roadImages[tinfoUpper->roadType][tinfoUpper->roadDir][rotation], &source, targetSurf, &dest); } - if(tinfo.roadType != ERoadType::NO_ROAD) //print road from this tile + if(tinfo.roadType != ROAD_NAMES[0]) //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 - 1][tinfo.roadDir][rotation], + drawElement(EMapCacheType::ROADS, parent->roadImages[tinfo.roadType][tinfo.roadDir][rotation], &source, targetSurf, &dest); } } @@ -825,7 +821,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-1][tinfo.riverDir][rotation], nullptr, targetSurf, &destRect); + drawElement(EMapCacheType::RIVERS, parent->riverImages[tinfo.riverType][tinfo.riverDir][rotation], nullptr, targetSurf, &destRect); } void CMapHandler::CMapBlitter::drawFow(SDL_Surface * targetSurf) const @@ -876,7 +872,7 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn if(isVisible || info->showAllTerrain) { drawTileTerrain(targetSurf, tinfo, tile); - if (tinfo.riverType) + if(tinfo.riverType != RIVER_NAMES[0]) drawRiver(targetSurf, tinfo); drawRoad(targetSurf, tinfo, tinfoUpper); } @@ -1326,13 +1322,13 @@ bool CMapHandler::canStartHeroMovement() void CMapHandler::updateWater() //shift colors in palettes of water tiles { - for(auto & elem : terrainImages[7]) + for(auto & elem : terrainImages["lava"]) { for(auto img : elem) img->shiftPalette(246, 9); } - for(auto & elem : terrainImages[8]) + for(auto & elem : terrainImages["water"]) { for(auto img : elem) { @@ -1341,7 +1337,7 @@ void CMapHandler::updateWater() //shift colors in palettes of water tiles } } - for(auto & elem : riverImages[0]) + for(auto & elem : riverImages["clrrvr"]) { for(auto img : elem) { @@ -1350,7 +1346,7 @@ void CMapHandler::updateWater() //shift colors in palettes of water tiles } } - for(auto & elem : riverImages[2]) + for(auto & elem : riverImages["mudrvr"]) { for(auto img : elem) { @@ -1360,7 +1356,7 @@ void CMapHandler::updateWater() //shift colors in palettes of water tiles } } - for(auto & elem : riverImages[3]) + for(auto & elem : riverImages["lavrvr"]) { for(auto img : elem) img->shiftPalette(240, 9); diff --git a/client/mapHandler.h b/client/mapHandler.h index 03610ead2..5206e0613 100644 --- a/client/mapHandler.h +++ b/client/mapHandler.h @@ -354,8 +354,8 @@ public: //terrain graphics //FIXME: unique_ptr should be enough, but fails to compile in MSVS 2013 - typedef std::vector, 4>> TFlippedAnimations; //[type, rotation] - typedef std::vector, 4>>> TFlippedCache;//[type, view type, rotation] + typedef std::map, 4>> TFlippedAnimations; //[type, rotation] + typedef std::map, 4>>> TFlippedCache;//[type, view type, rotation] TFlippedAnimations terrainAnimations;//[terrain type, rotation] TFlippedCache terrainImages;//[terrain type, view type, rotation] diff --git a/client/widgets/AdventureMapClasses.cpp b/client/widgets/AdventureMapClasses.cpp index 5113df340..eac733371 100644 --- a/client/widgets/AdventureMapClasses.cpp +++ b/client/widgets/AdventureMapClasses.cpp @@ -42,6 +42,7 @@ #include "../../lib/CHeroHandler.h" #include "../../lib/CModHandler.h" #include "../../lib/CTownHandler.h" +#include "../../lib/Terrain.h" #include "../../lib/filesystem/Filesystem.h" #include "../../lib/JsonNode.h" #include "../../lib/mapObjects/CGHeroInstance.h" @@ -494,41 +495,30 @@ void CMinimapInstance::showAll(SDL_Surface * to) } } -std::map > CMinimap::loadColors(std::string from) +std::map > CMinimap::loadColors() { - std::map > ret; + std::map > ret; - const JsonNode config(ResourceID(from, EResType::TEXT)); - - for(auto &m : config.Struct()) + for(auto & terrain : Terrain::Manager::terrains()) { - auto index = boost::find(GameConstants::TERRAIN_NAMES, m.first); - if (index == std::end(GameConstants::TERRAIN_NAMES)) - { - logGlobal->error("Error: unknown terrain in terrains.json: %s", m.first); - continue; - } - int terrainID = static_cast(index - std::begin(GameConstants::TERRAIN_NAMES)); - - const JsonVector &unblockedVec = m.second["minimapUnblocked"].Vector(); + auto & m = Terrain::Manager::getInfo(terrain); SDL_Color normal = { - ui8(unblockedVec[0].Float()), - ui8(unblockedVec[1].Float()), - ui8(unblockedVec[2].Float()), + ui8(m.minimapUnblocked[0]), + ui8(m.minimapUnblocked[1]), + ui8(m.minimapUnblocked[2]), ui8(255) }; - const JsonVector &blockedVec = m.second["minimapBlocked"].Vector(); SDL_Color blocked = { - ui8(blockedVec[0].Float()), - ui8(blockedVec[1].Float()), - ui8(blockedVec[2].Float()), + ui8(m.minimapBlocked[0]), + ui8(m.minimapBlocked[1]), + ui8(m.minimapBlocked[2]), ui8(255) }; - ret.insert(std::make_pair(terrainID, std::make_pair(normal, blocked))); + ret[terrain] = std::make_pair(normal, blocked); } return ret; } @@ -536,7 +526,7 @@ std::map > CMinimap::loadColors(std::string CMinimap::CMinimap(const Rect & position) : CIntObject(LCLICK | RCLICK | HOVER | MOVE, position.topLeft()), level(0), - colors(loadColors("config/terrains.json")) + colors(loadColors()) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); pos.w = position.w; diff --git a/client/widgets/AdventureMapClasses.h b/client/widgets/AdventureMapClasses.h index d87c58a81..97cb7da74 100644 --- a/client/widgets/AdventureMapClasses.h +++ b/client/widgets/AdventureMapClasses.h @@ -30,6 +30,7 @@ struct InfoAboutTown; class CHeroTooltip; class CTownTooltip; class CTextBox; +class Terrain; /// Base UI Element for hero\town lists class CList : public CIntObject @@ -216,7 +217,7 @@ protected: int level; //to initialize colors - std::map > loadColors(std::string from); + std::map > loadColors(); void clickLeft(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override; @@ -227,7 +228,7 @@ protected: public: // terrainID -> (normal color, blocked color) - const std::map > colors; + const std::map > colors; CMinimap(const Rect & position); diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index 9959529c2..fbf5e9c76 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -265,7 +265,7 @@ void CTerrainRect::showPath(const SDL_Rect * extRect, SDL_Surface * to) {-1, 1, 2, 23, -1, 3, 22, 21, 12} }; //table of magic values TODO meaning, change variable name - for (int i=0; i < (int)currentPath->nodes.size()-1; ++i) + for (int i = 0; i < -1 + (int)currentPath->nodes.size(); ++i) { const int3 &curPos = currentPath->nodes[i].coord, &nextPos = currentPath->nodes[i+1].coord; if(curPos.z != adventureInt->position.z) diff --git a/config/gameConfig.json b/config/gameConfig.json index ec5ee608f..57872e9a4 100644 --- a/config/gameConfig.json +++ b/config/gameConfig.json @@ -80,5 +80,9 @@ "skills" : [ "config/skills.json" + ], + "terrains": + [ + "config/terrains.json" ] } diff --git a/config/obstacles.json b/config/obstacles.json index 10c8360e5..bee4258e4 100644 --- a/config/obstacles.json +++ b/config/obstacles.json @@ -14,7 +14,7 @@ "obstacles" : [ { "id" : 0, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 2, "height" : 1, @@ -24,7 +24,7 @@ }, { "id" : 1, - "allowedTerrain" : [0, 1, 5, 6], + "allowedTerrain" : ["dirt", "sand", "rough", "subterra"], "specialBattlefields" : [0], "width" : 3, "height" : 2, @@ -34,7 +34,7 @@ }, { "id" : 2, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 4, "height" : 2, @@ -44,7 +44,7 @@ }, { "id" : 3, - "allowedTerrain" : [0, 5], + "allowedTerrain" : ["dirt", "rough"], "specialBattlefields" : [1], "width" : 2, "height" : 1, @@ -54,7 +54,7 @@ }, { "id" : 4, - "allowedTerrain" : [0, 5, 6], + "allowedTerrain" : ["dirt", "rough", "subterra"], "specialBattlefields" : [0, 1], "width" : 2, "height" : 1, @@ -64,7 +64,7 @@ }, { "id" : 5, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 4, "height" : 2, @@ -74,7 +74,7 @@ }, { "id" : 6, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 3, "height" : 2, @@ -84,7 +84,7 @@ }, { "id" : 7, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 2, "height" : 2, @@ -94,7 +94,7 @@ }, { "id" : 8, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 2, "height" : 2, @@ -104,7 +104,7 @@ }, { "id" : 9, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 2, "height" : 2, @@ -114,7 +114,7 @@ }, { "id" : 10, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 2, "height" : 2, @@ -124,7 +124,7 @@ }, { "id" : 11, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 2, "height" : 1, @@ -134,7 +134,7 @@ }, { "id" : 12, - "allowedTerrain" : [0, 5], + "allowedTerrain" : ["dirt", "rough"], "specialBattlefields" : [1], "width" : 3, "height" : 3, @@ -144,7 +144,7 @@ }, { "id" : 13, - "allowedTerrain" : [0, 5], + "allowedTerrain" : ["dirt", "rough"], "specialBattlefields" : [1], "width" : 3, "height" : 2, @@ -154,7 +154,7 @@ }, { "id" : 14, - "allowedTerrain" : [0, 5], + "allowedTerrain" : ["dirt", "rough"], "specialBattlefields" : [1], "width" : 3, "height" : 2, @@ -164,7 +164,7 @@ }, { "id" : 15, - "allowedTerrain" : [0, 5], + "allowedTerrain" : ["dirt", "rough"], "specialBattlefields" : [1], "width" : 3, "height" : 3, @@ -174,7 +174,7 @@ }, { "id" : 16, - "allowedTerrain" : [1], + "allowedTerrain" : ["sand"], "specialBattlefields" : [], "width" : 4, "height" : 4, @@ -184,7 +184,7 @@ }, { "id" : 17, - "allowedTerrain" : [1], + "allowedTerrain" : ["sand"], "specialBattlefields" : [], "width" : 3, "height" : 2, @@ -194,7 +194,7 @@ }, { "id" : 18, - "allowedTerrain" : [1], + "allowedTerrain" : ["sand"], "specialBattlefields" : [], "width" : 4, "height" : 2, @@ -204,7 +204,7 @@ }, { "id" : 19, - "allowedTerrain" : [2, 4], + "allowedTerrain" : ["grass", "swamp"], "specialBattlefields" : [], "width" : 2, "height" : 1, @@ -214,7 +214,7 @@ }, { "id" : 20, - "allowedTerrain" : [2, 4], + "allowedTerrain" : ["grass", "swamp"], "specialBattlefields" : [2], "width" : 2, "height" : 2, @@ -224,7 +224,7 @@ }, { "id" : 21, - "allowedTerrain" : [2, 4], + "allowedTerrain" : ["grass", "swamp"], "specialBattlefields" : [], "width" : 1, "height" : 1, @@ -234,7 +234,7 @@ }, { "id" : 22, - "allowedTerrain" : [2], + "allowedTerrain" : ["grass"], "specialBattlefields" : [2], "width" : 6, "height" : 2, @@ -244,7 +244,7 @@ }, { "id" : 23, - "allowedTerrain" : [2], + "allowedTerrain" : ["grass"], "specialBattlefields" : [], "width" : 7, "height" : 1, @@ -254,7 +254,7 @@ }, { "id" : 24, - "allowedTerrain" : [3], + "allowedTerrain" : ["snow"], "specialBattlefields" : [], "width" : 3, "height" : 1, @@ -264,7 +264,7 @@ }, { "id" : 25, - "allowedTerrain" : [3], + "allowedTerrain" : ["snow"], "specialBattlefields" : [], "width" : 5, "height" : 1, @@ -274,7 +274,7 @@ }, { "id" : 26, - "allowedTerrain" : [3], + "allowedTerrain" : ["snow"], "specialBattlefields" : [], "width" : 3, "height" : 3, @@ -284,7 +284,7 @@ }, { "id" : 27, - "allowedTerrain" : [3], + "allowedTerrain" : ["snow"], "specialBattlefields" : [], "width" : 3, "height" : 1, @@ -294,7 +294,7 @@ }, { "id" : 28, - "allowedTerrain" : [3], + "allowedTerrain" : ["snow"], "specialBattlefields" : [], "width" : 3, "height" : 1, @@ -304,7 +304,7 @@ }, { "id" : 29, - "allowedTerrain" : [3], + "allowedTerrain" : ["snow"], "specialBattlefields" : [], "width" : 3, "height" : 2, @@ -314,7 +314,7 @@ }, { "id" : 30, - "allowedTerrain" : [3], + "allowedTerrain" : ["snow"], "specialBattlefields" : [], "width" : 2, "height" : 1, @@ -324,7 +324,7 @@ }, { "id" : 31, - "allowedTerrain" : [3], + "allowedTerrain" : ["snow"], "specialBattlefields" : [], "width" : 3, "height" : 2, @@ -334,7 +334,7 @@ }, { "id" : 32, - "allowedTerrain" : [3], + "allowedTerrain" : ["snow"], "specialBattlefields" : [], "width" : 7, "height" : 2, @@ -344,7 +344,7 @@ }, { "id" : 33, - "allowedTerrain" : [3], + "allowedTerrain" : ["snow"], "specialBattlefields" : [], "width" : 5, "height" : 5, @@ -354,7 +354,7 @@ }, { "id" : 34, - "allowedTerrain" : [4], + "allowedTerrain" : ["swamp"], "specialBattlefields" : [], "width" : 2, "height" : 2, @@ -364,7 +364,7 @@ }, { "id" : 35, - "allowedTerrain" : [4], + "allowedTerrain" : ["swamp"], "specialBattlefields" : [], "width" : 8, "height" : 3, @@ -374,7 +374,7 @@ }, { "id" : 36, - "allowedTerrain" : [4], + "allowedTerrain" : ["swamp"], "specialBattlefields" : [], "width" : 2, "height" : 1, @@ -384,7 +384,7 @@ }, { "id" : 37, - "allowedTerrain" : [4], + "allowedTerrain" : ["swamp"], "specialBattlefields" : [], "width" : 3, "height" : 1, @@ -394,7 +394,7 @@ }, { "id" : 38, - "allowedTerrain" : [4], + "allowedTerrain" : ["swamp"], "specialBattlefields" : [], "width" : 5, "height" : 4, @@ -404,7 +404,7 @@ }, { "id" : 39, - "allowedTerrain" : [4], + "allowedTerrain" : ["swamp"], "specialBattlefields" : [], "width" : 4, "height" : 3, @@ -414,7 +414,7 @@ }, { "id" : 40, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 2, "height" : 2, @@ -424,7 +424,7 @@ }, { "id" : 41, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 4, "height" : 3, @@ -434,7 +434,7 @@ }, { "id" : 42, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 3, "height" : 2, @@ -444,7 +444,7 @@ }, { "id" : 43, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 3, "height" : 3, @@ -454,7 +454,7 @@ }, { "id" : 44, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 3, "height" : 3, @@ -464,7 +464,7 @@ }, { "id" : 45, - "allowedTerrain" : [6], + "allowedTerrain" : ["subterra"], "specialBattlefields" : [], "width" : 3, "height" : 3, @@ -474,7 +474,7 @@ }, { "id" : 46, - "allowedTerrain" : [6], + "allowedTerrain" : ["subterra"], "specialBattlefields" : [], "width" : 3, "height" : 2, @@ -484,7 +484,7 @@ }, { "id" : 47, - "allowedTerrain" : [6], + "allowedTerrain" : ["subterra"], "specialBattlefields" : [], "width" : 4, "height" : 3, @@ -494,7 +494,7 @@ }, { "id" : 48, - "allowedTerrain" : [7], + "allowedTerrain" : ["lava"], "specialBattlefields" : [], "width" : 4, "height" : 3, @@ -504,7 +504,7 @@ }, { "id" : 49, - "allowedTerrain" : [7], + "allowedTerrain" : ["lava"], "specialBattlefields" : [], "width" : 4, "height" : 2, @@ -514,7 +514,7 @@ }, { "id" : 50, - "allowedTerrain" : [7], + "allowedTerrain" : ["lava"], "specialBattlefields" : [], "width" : 5, "height" : 3, @@ -524,7 +524,7 @@ }, { "id" : 51, - "allowedTerrain" : [7], + "allowedTerrain" : ["lava"], "specialBattlefields" : [], "width" : 3, "height" : 2, @@ -534,7 +534,7 @@ }, { "id" : 52, - "allowedTerrain" : [7], + "allowedTerrain" : ["lava"], "specialBattlefields" : [], "width" : 4, "height" : 4, @@ -544,7 +544,7 @@ }, { "id" : 53, - "allowedTerrain" : [7], + "allowedTerrain" : ["lava"], "specialBattlefields" : [], "width" : 5, "height" : 3, @@ -554,7 +554,7 @@ }, { "id" : 54, - "allowedTerrain" : [7], + "allowedTerrain" : ["lava"], "specialBattlefields" : [], "width" : 5, "height" : 3, @@ -564,7 +564,7 @@ }, { "id" : 55, - "allowedTerrain" : [8], + "allowedTerrain" : ["water"], "specialBattlefields" : [], "width" : 3, "height" : 3, @@ -926,7 +926,7 @@ "absoluteObstacles" : [ { "id" : 0, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 124, "height" : 254, @@ -935,7 +935,7 @@ }, { "id" : 1, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 256, "height" : 254, @@ -944,7 +944,7 @@ }, { "id" : 2, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 168, "height" : 212, @@ -953,7 +953,7 @@ }, { "id" : 3, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 124, "height" : 254, @@ -962,7 +962,7 @@ }, { "id" : 4, - "allowedTerrain" : [0], + "allowedTerrain" : ["dirt"], "specialBattlefields" : [], "width" : 146, "height" : 254, @@ -971,7 +971,7 @@ }, { "id" : 5, - "allowedTerrain" : [2], + "allowedTerrain" : ["grass"], "specialBattlefields" : [], "width" : 173, "height" : 221, @@ -980,7 +980,7 @@ }, { "id" : 6, - "allowedTerrain" : [2], + "allowedTerrain" : ["grass"], "specialBattlefields" : [], "width" : 180, "height" : 264, @@ -989,7 +989,7 @@ }, { "id" : 7, - "allowedTerrain" : [3], + "allowedTerrain" : ["snow"], "specialBattlefields" : [], "width" : 166, "height" : 255, @@ -998,7 +998,7 @@ }, { "id" : 8, - "allowedTerrain" : [3], + "allowedTerrain" : ["snow"], "specialBattlefields" : [], "width" : 302, "height" : 172, @@ -1007,7 +1007,7 @@ }, { "id" : 9, - "allowedTerrain" : [4], + "allowedTerrain" : ["swamp"], "specialBattlefields" : [], "width" : 300, "height" : 170, @@ -1016,7 +1016,7 @@ }, { "id" : 10, - "allowedTerrain" : [4], + "allowedTerrain" : ["swamp"], "specialBattlefields" : [], "width" : 278, "height" : 171, @@ -1025,7 +1025,7 @@ }, { "id" : 11, - "allowedTerrain" : [4], + "allowedTerrain" : ["swamp"], "specialBattlefields" : [], "width" : 256, "height" : 254, @@ -1034,7 +1034,7 @@ }, { "id" : 12, - "allowedTerrain" : [7], + "allowedTerrain" : ["lava"], "specialBattlefields" : [], "width" : 124, "height" : 254, @@ -1043,7 +1043,7 @@ }, { "id" : 13, - "allowedTerrain" : [7], + "allowedTerrain" : ["lava"], "specialBattlefields" : [], "width" : 256, "height" : 128, @@ -1052,7 +1052,7 @@ }, { "id" : 14, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 186, "height" : 212, @@ -1061,7 +1061,7 @@ }, { "id" : 15, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 347, "height" : 174, @@ -1070,7 +1070,7 @@ }, { "id" : 16, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 294, "height" : 169, @@ -1079,7 +1079,7 @@ }, { "id" : 17, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 165, "height" : 257, @@ -1088,7 +1088,7 @@ }, { "id" : 18, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 208, "height" : 268, @@ -1097,7 +1097,7 @@ }, { "id" : 19, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 252, "height" : 254, @@ -1106,7 +1106,7 @@ }, { "id" : 20, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 278, "height" : 128, @@ -1115,7 +1115,7 @@ }, { "id" : 21, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 208, "height" : 268, @@ -1124,7 +1124,7 @@ }, { "id" : 22, - "allowedTerrain" : [5], + "allowedTerrain" : ["rough"], "specialBattlefields" : [1], "width" : 168, "height" : 212, diff --git a/config/randomMap.json b/config/randomMap.json index 72de50821..260acf3e0 100644 --- a/config/randomMap.json +++ b/config/randomMap.json @@ -2,7 +2,7 @@ "terrain" : { "undergroundAllow" : ["lava"], //others to be replaced by subterranena - "groundProhibit" : ["subterranean"] //to be replaced by dirt + "groundProhibit" : ["subterra"] //to be replaced by dirt }, "waterZone" : { @@ -31,7 +31,7 @@ "extraResourcesLimit" : 3 }, "minGuardStrength" : 2000, - "defaultRoadType" : "cobblestone_road", + "defaultRoadType" : "pc", //pd - dirt, pg - gravel, pc - cobblestone "treasureValueLimit" : 20000, //generate pandora with gold for treasure above this limit "prisons" : { diff --git a/config/terrains.json b/config/terrains.json index 0e56d0986..b6c1b0bf6 100644 --- a/config/terrains.json +++ b/config/terrains.json @@ -4,69 +4,92 @@ "moveCost" : 100, "minimapUnblocked" : [ 82, 56, 8 ], "minimapBlocked" : [ 57, 40, 8 ], - "music" : "Dirt.mp3" + "music" : "Dirt.mp3", + "tiles" : "DIRTTL", + "code" : "dt" }, "sand" : { "moveCost" : 150, "minimapUnblocked" : [ 222, 207, 140 ], "minimapBlocked" : [ 165, 158, 107 ], - "music" : "Sand.mp3" + "music" : "Sand.mp3", + "tiles" : "SANDTL", + "code" : "sa" }, "grass" : { "moveCost" : 100, "minimapUnblocked" : [ 0, 65, 0 ], "minimapBlocked" : [ 0, 48, 0 ], - "music" : "Grass.mp3" + "music" : "Grass.mp3", + "tiles" : "GRASTL", + "code" : "gr" }, "snow" : { "moveCost" : 150, "minimapUnblocked" : [ 181, 199, 198 ], "minimapBlocked" : [ 140, 158, 156 ], - "music" : "Snow.mp3" + "music" : "Snow.mp3", + "tiles" : "SNOWTL", + "code" : "sn" }, "swamp" : { "moveCost" : 175, "minimapUnblocked" : [ 74, 134, 107 ], "minimapBlocked" : [ 33, 89, 66 ], - "music" : "Swamp.mp3" + "music" : "Swamp.mp3", + "tiles" : "SWMPTL", + "code" : "sw" }, "rough" : { "moveCost" : 125, "minimapUnblocked" : [ 132, 113, 49 ], "minimapBlocked" : [ 99, 81, 33 ], - "music" : "Rough.mp3" + "music" : "Rough.mp3", + "tiles" : "ROUGTL", + "code" : "rg" }, "subterra" : { "moveCost" : 100, "minimapUnblocked" : [ 132, 48, 0 ], "minimapBlocked" : [ 90, 8, 0 ], - "music" : "Underground.mp3" + "music" : "Underground.mp3", + "tiles" : "SUBBTL", + "type" : "SUB", + "code" : "sb" }, "lava" : { "moveCost" : 100, "minimapUnblocked" : [ 74, 73, 74 ], "minimapBlocked" : [ 41, 40, 41 ], - "music" : "Lava.mp3" + "music" : "Lava.mp3", + "tiles" : "LAVATL", + "code" : "lv" }, "water" : { "moveCost" : 100, "minimapUnblocked" : [ 8, 81, 148 ], "minimapBlocked" : [ 8, 81, 148 ], - "music" : "Water.mp3" + "music" : "Water.mp3", + "tiles" : "WATRTL", + "type" : "WATER", + "code" : "wt" }, "rock" : { "moveCost" : -1, "minimapUnblocked" : [ 0, 0, 0 ], "minimapBlocked" : [ 0, 0, 0 ], - "music" : "Underground.mp3" // Impossible in H3 + "music" : "Underground.mp3", // Impossible in H3 + "tiles" : "ROCKTL", + "type" : "ROCK", + "code" : "rc" } } diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index 0dc2bb6b0..2acdbeefc 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -102,19 +102,8 @@ public: h & constituentOf; h & aClass; h & id; - if(version >= 759) - { - h & identifier; - } - - if(version >= 771) - { - h & warMachine; - } - else if(!h.saving) - { - fillWarMachine(); - } + h & identifier; + h & warMachine; } CArtifact(); diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 97306a3dd..8fa13ef39 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -16,6 +16,7 @@ #include "CGameState.h" #include "CTownHandler.h" #include "CModHandler.h" +#include "Terrain.h" #include "StringConstants.h" #include "serializer/JsonDeserializer.h" #include "serializer/JsonUpdater.h" @@ -282,13 +283,13 @@ std::string CCreature::nodeName() const return "\"" + namePl + "\""; } -bool CCreature::isItNativeTerrain(ETerrainType::EETerrainType terrain) const +bool CCreature::isItNativeTerrain(const Terrain & terrain) const { auto native = getNativeTerrain(); - return native == terrain || native == ETerrainType::ANY_TERRAIN; + return native == terrain || native == Terrain::ANY; } -ETerrainType::EETerrainType CCreature::getNativeTerrain() const +Terrain CCreature::getNativeTerrain() const { const std::string cachingStringBlocksRetaliation = "type_NO_TERRAIN_PENALTY"; static const auto selectorBlocksRetaliation = Selector::type()(Bonus::NO_TERRAIN_PENALTY); @@ -296,8 +297,8 @@ ETerrainType::EETerrainType 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(selectorBlocksRetaliation, selectorBlocksRetaliation) - ? ETerrainType::ANY_TERRAIN - : (ETerrainType::EETerrainType)(*VLC->townh)[faction]->nativeTerrain; + ? Terrain::ANY + : (Terrain)(*VLC->townh)[faction]->nativeTerrain; } void CCreature::updateFrom(const JsonNode & data) @@ -1340,11 +1341,6 @@ void CCreatureHandler::removeBonusesFromAllCreatures() allCreatures.removeBonuses(Selector::all); } -void CCreatureHandler::restoreAllCreaturesNodeType794() -{ - allCreatures.setNodeType(CBonusSystemNode::ENodeTypes::ALL_CREATURES); -} - void CCreatureHandler::buildBonusTreeForTiers() { for(CCreature * c : objects) diff --git a/lib/CCreatureHandler.h b/lib/CCreatureHandler.h index dfdcd3e01..46939ea20 100644 --- a/lib/CCreatureHandler.h +++ b/lib/CCreatureHandler.h @@ -24,6 +24,7 @@ class CLegacyConfigParser; class CCreatureHandler; class CCreature; class JsonSerializeFormat; +class Terrain; class DLL_LINKAGE CCreature : public Creature, public CBonusSystemNode { @@ -118,14 +119,14 @@ public: ArtifactID warMachine; - bool isItNativeTerrain(ETerrainType::EETerrainType terrain) const; + bool isItNativeTerrain(const Terrain & terrain) const; /** Returns creature native terrain considering some terrain bonuses. @param considerBonus is used to avoid Dead Lock when this method is called inside getAllBonuses considerBonus = true is called from Pathfinder and fills actual nativeTerrain considering bonus(es). considerBonus = false is called on Battle init and returns already prepared nativeTerrain without Bonus system calling. */ - ETerrainType::EETerrainType getNativeTerrain() const; + Terrain getNativeTerrain() const; int32_t getIndex() const override; int32_t getIconIndex() const override; const std::string & getName() const override; @@ -211,18 +212,8 @@ public: h & doubleWide; h & special; - if(version>=759) - { - h & identifier; - } - if(version >= 771) - { - h & warMachine; - } - else if(!h.saving) - { - fillWarMachine(); - } + h & identifier; + h & warMachine; } CCreature(); @@ -281,7 +272,6 @@ public: void addBonusForTier(int tier, const std::shared_ptr & b); //tier must be <1-7> void addBonusForAllCreatures(const std::shared_ptr & b); //due to CBonusSystem::addNewBonus(const std::shared_ptr& b); void removeBonusesFromAllCreatures(); - void restoreAllCreaturesNodeType794(); //restore ALL_CREATURES node type for old saves CCreatureHandler(); ~CCreatureHandler(); diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 38737d81e..b089ac138 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -574,7 +574,7 @@ EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTow { const TerrainTile *tile = getTile(t->bestLocation(), false); - if(!tile || tile->terType != ETerrainType::WATER) + if(!tile || tile->terType.isLand()) return EBuildingState::NO_WATER; //lack of water } diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index a0fcdad80..a70f7a998 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -963,8 +963,8 @@ void CGameState::initGrailPosition() const TerrainTile &t = map->getTile(int3(i, j, k)); if(!t.blocked && !t.visitable - && t.terType != ETerrainType::WATER - && t.terType != ETerrainType::ROCK + && t.terType.isLand() + && t.terType.isPassable() && (int)map->grailPos.dist2dSQ(int3(i, j, k)) <= (map->grailRadius * map->grailRadius)) allowedPos.push_back(int3(i,j,k)); } @@ -1940,31 +1940,31 @@ BFieldType CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & ra if(map->isCoastalTile(tile)) //coastal tile is always ground return BFieldType::SAND_SHORE; - switch(t.terType) - { - case ETerrainType::DIRT: + if(t.terType == Terrain("dirt")) return BFieldType(rand.nextInt(3, 5)); - case ETerrainType::SAND: + if(t.terType == Terrain("sand")) return BFieldType::SAND_MESAS; //TODO: coast support - case ETerrainType::GRASS: + if(t.terType == Terrain("grass")) return BFieldType(rand.nextInt(6, 7)); - case ETerrainType::SNOW: + if(t.terType == Terrain("snow")) return BFieldType(rand.nextInt(10, 11)); - case ETerrainType::SWAMP: + if(t.terType == Terrain("swamp")) return BFieldType::SWAMP_TREES; - case ETerrainType::ROUGH: + if(t.terType == Terrain("rough")) return BFieldType::ROUGH; - case ETerrainType::SUBTERRANEAN: + if(t.terType.isUnderground()) return BFieldType::SUBTERRANEAN; - case ETerrainType::LAVA: + if(t.terType == Terrain("lava")) return BFieldType::LAVA; - case ETerrainType::WATER: + if(t.terType.isWater()) return BFieldType::SHIP; - case ETerrainType::ROCK: + if(!t.terType.isPassable()) return BFieldType::ROCKLANDS; - default: - return BFieldType::NONE; - } + + //TODO: STUB, support new battlegrounds + return BFieldType::DIRT_HILLS; + + return BFieldType::NONE; } UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack) @@ -2145,7 +2145,7 @@ void CGameState::updateRumor() rumorId = *RandomGeneratorUtil::nextItem(sRumorTypes, rand); if(rumorId == RumorState::RUMOR_GRAIL) { - rumorExtra = getTile(map->grailPos)->terType; + rumorExtra = getTile(map->grailPos)->terType.id(); break; } diff --git a/lib/CGameState.h b/lib/CGameState.h index 381b29242..d4fba20c9 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -227,14 +227,7 @@ public: h & hpool; h & globalEffects; h & rand; - if(version >= 755) //save format backward compatibility - { - h & rumor; - } - else if(!h.saving) - { - rumor = RumorState(); - } + h & rumor; BONUS_TREE_DESERIALIZATION_FIX } diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index 18f9dcf1e..aa19fdfb3 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -17,6 +17,7 @@ #include "CModHandler.h" #include "GameConstants.h" #include "VCMI_Lib.h" +#include "Terrain.h" size_t Unicode::getCharacterSize(char firstByte) { @@ -309,6 +310,7 @@ void CGeneralTextHandler::readToVector(std::string sourceName, std::vector h3mTerrainNames; readToVector("DATA/VCDESC.TXT", victoryConditions); readToVector("DATA/LCDESC.TXT", lossCondtions); readToVector("DATA/TCOMMAND.TXT", tcommands); @@ -317,7 +319,7 @@ CGeneralTextHandler::CGeneralTextHandler() readToVector("DATA/ADVEVENT.TXT", advobtxt); readToVector("DATA/XTRAINFO.TXT", xtrainfo); readToVector("DATA/RESTYPES.TXT", restypes); - readToVector("DATA/TERRNAME.TXT", terrainNames); + readToVector("DATA/TERRNAME.TXT", h3mTerrainNames); readToVector("DATA/RANDSIGN.TXT", randsign); readToVector("DATA/CRGEN1.TXT", creGens); readToVector("DATA/CRGEN4.TXT", creGens4); @@ -331,6 +333,17 @@ CGeneralTextHandler::CGeneralTextHandler() readToVector("DATA/HEROSCRN.TXT", heroscrn); readToVector("DATA/TENTCOLR.TXT", tentColors); readToVector("DATA/SKILLLEV.TXT", levels); + + for(int i = 0; i < h3mTerrainNames.size(); ++i) + { + terrainNames[Terrain::createTerrainTypeH3M(i)] = h3mTerrainNames[i]; + } + for(auto & terrain : Terrain::Manager::terrains()) + { + if(!Terrain::Manager::getInfo(terrain).terrainText.empty()) + terrainNames[terrain] = Terrain::Manager::getInfo(terrain).terrainText; + } + static const char * QE_MOD_COMMANDS = "DATA/QECOMMANDS.TXT"; if (CResourceHandler::get()->existsResource(ResourceID(QE_MOD_COMMANDS, EResType::TEXT))) diff --git a/lib/CGeneralTextHandler.h b/lib/CGeneralTextHandler.h index 02c2b6385..58feed9ce 100644 --- a/lib/CGeneralTextHandler.h +++ b/lib/CGeneralTextHandler.h @@ -122,7 +122,7 @@ public: std::vector advobtxt; std::vector xtrainfo; std::vector restypes; //names of resources - std::vector terrainNames; + std::map terrainNames; std::vector randsign; std::vector> mines; //first - name; second - event description std::vector seerEmpty; diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index 268929ea3..107fe81cd 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -19,6 +19,7 @@ #include "CCreatureHandler.h" #include "CModHandler.h" #include "CTownHandler.h" +#include "Terrain.h" #include "mapObjects/CObjectHandler.h" //for hero specialty #include "CSkillHandler.h" #include @@ -176,7 +177,7 @@ std::vector CObstacleInfo::getBlocked(BattleHex hex) const return ret; } -bool CObstacleInfo::isAppropriate(ETerrainType terrainType, int specialBattlefield) const +bool CObstacleInfo::isAppropriate(Terrain terrainType, int specialBattlefield) const { if(specialBattlefield != -1) return vstd::contains(allowedSpecialBfields, specialBattlefield); @@ -376,9 +377,9 @@ CHeroHandler::CHeroHandler() { loadObstacles(); loadTerrains(); - for (int i = 0; i < GameConstants::TERRAIN_TYPES; ++i) + for(int i = 0; i < Terrain::Manager::terrains().size(); ++i) { - VLC->modh->identifiers.registerObject("core", "terrain", GameConstants::TERRAIN_NAMES[i], i); + VLC->modh->identifiers.registerObject("core", "terrain", Terrain::Manager::terrains()[i], i); } loadBallistics(); loadExperience(); @@ -826,7 +827,8 @@ void CHeroHandler::loadObstacles() obi.defName = obs["defname"].String(); obi.width = static_cast(obs["width"].Float()); obi.height = static_cast(obs["height"].Float()); - obi.allowedTerrains = obs["allowedTerrain"].convertTo >(); + for(auto & t : obs["allowedTerrain"].Vector()) + obi.allowedTerrains.emplace_back(t.String()); obi.allowedSpecialBfields = obs["specialBattlefields"].convertTo >(); obi.blockedTiles = obs["blockedTiles"].convertTo >(); obi.isAbsoluteObstacle = absolute; @@ -1029,11 +1031,10 @@ ui64 CHeroHandler::reqExp (ui32 level) const void CHeroHandler::loadTerrains() { - const JsonNode config(ResourceID("config/terrains.json")); - - terrCosts.reserve(GameConstants::TERRAIN_TYPES); - for(const std::string & name : GameConstants::TERRAIN_NAMES) - terrCosts.push_back((int)config[name]["moveCost"].Float()); + for(auto & terrain : Terrain::Manager::terrains()) + { + terrCosts[terrain] = Terrain::Manager::getInfo(terrain).moveCost; + } } std::vector CHeroHandler::getDefaultAllowed() const diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index f0fd83031..14d2b1b8a 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -26,6 +26,7 @@ struct BattleHex; class JsonNode; class CRandomGenerator; class JsonSerializeFormat; +class Terrain; struct SSpecialtyInfo { si32 type; @@ -119,15 +120,7 @@ public: h & initialArmy; h & heroClass; h & secSkillsInit; - if(version >= 781) - { - h & specialty; - } - else - { - h & specDeprecated; - h & specialtyDeprecated; - } + h & specialty; h & spells; h & haveSpellBook; h & sex; @@ -141,14 +134,8 @@ public: h & iconSpecLarge; h & portraitSmall; h & portraitLarge; - if(version >= 759) - { - h & identifier; - } - if(version >= 790) - { - h & battleImage; - } + h & identifier; + h & battleImage; } }; @@ -211,16 +198,7 @@ public: h & identifier; h & name; h & faction; - if(version >= 800) - { - h & id; - } - else - { - ui8 old_id = 0; - h & old_id; - id = HeroClassID(old_id); - } + h & id; h & defaultTavernChance; h & primarySkillInitial; h & primarySkillLowLevel; @@ -248,7 +226,7 @@ struct DLL_LINKAGE CObstacleInfo { si32 ID; std::string defName; - std::vector allowedTerrains; + std::vector allowedTerrains; std::vector allowedSpecialBfields; ui8 isAbsoluteObstacle; //there may only one such obstacle in battle and its position is always the same @@ -257,7 +235,7 @@ struct DLL_LINKAGE CObstacleInfo std::vector getBlocked(BattleHex hex) const; //returns vector of hexes blocked by obstacle when it's placed on hex 'hex' - bool isAppropriate(ETerrainType terrainType, int specialBattlefield = -1) const; + bool isAppropriate(Terrain terrainType, int specialBattlefield = -1) const; template void serialize(Handler &h, const int version) { @@ -315,7 +293,7 @@ public: CHeroClassHandler classes; //default costs of going through terrains. -1 means terrain is impassable - std::vector terrCosts; + std::map terrCosts; struct SBallisticsLevelInfo { diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 2675d68df..81e5846c2 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -163,6 +163,7 @@ set(lib_SRCS StartInfo.cpp ResourceSet.cpp ScriptHandler.cpp + Terrain.cpp VCMIDirs.cpp VCMI_Lib.cpp @@ -385,6 +386,7 @@ set(lib_HEADERS ScopeGuard.h StartInfo.h StringConstants.h + Terrain.h UnlockGuard.h VCMIDirs.h vcmi_endian.h diff --git a/lib/CModHandler.h b/lib/CModHandler.h index 9cb0474df..5df3dfc38 100644 --- a/lib/CModHandler.h +++ b/lib/CModHandler.h @@ -303,32 +303,9 @@ public: h & ALL_CREATURES_GET_DOUBLE_MONTHS; h & MAX_HEROES_AVAILABLE_PER_PLAYER; h & MAX_HEROES_ON_MAP_PER_PLAYER; - if(version >= 756) - { - h & WINNING_HERO_WITH_NO_TROOPS_RETREATS; - } - else if(!h.saving) - { - WINNING_HERO_WITH_NO_TROOPS_RETREATS = true; - } - - if(version >= 776) - { - h & BLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE; - } - else if(!h.saving) - { - BLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE = true; - } - - if(version >= 791) - { - h & NO_RANDOM_SPECIAL_WEEKS_AND_MONTHS; - } - else if(!h.saving) - { - NO_RANDOM_SPECIAL_WEEKS_AND_MONTHS = false; - } + h & WINNING_HERO_WITH_NO_TROOPS_RETREATS; + h & BLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE; + h & NO_RANDOM_SPECIAL_WEEKS_AND_MONTHS; } } settings; diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 11a86beaf..f1c10ff77 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -46,24 +46,19 @@ void NodeStorage::initialize(const PathfinderOptions & options, const CGameState for(pos.z=0; pos.z < sizes.z; ++pos.z) { const TerrainTile * tile = &gs->map->getTile(pos); - switch(tile->terType) + if(tile->terType.isWater()) { - case ETerrainType::ROCK: - break; - - case ETerrainType::WATER: resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); if(useFlying) resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); if(useWaterWalking) resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); - break; - - default: + } + if(tile->terType.isLand()) + { resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); if(useFlying) resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility(pos, tile, fow, player, gs)); - break; } } } @@ -1012,8 +1007,7 @@ bool CPathfinderHelper::passOneTurnLimitCheck(const PathNodeInfo & source) const TurnInfo::BonusCache::BonusCache(TConstBonusListPtr bl) { - noTerrainPenalty.reserve(ETerrainType::ROCK); - for(int i = 0; i < ETerrainType::ROCK; i++) + for(int i = 0; i < Terrain::Manager::terrains().size(); ++i) { noTerrainPenalty.push_back(static_cast( bl->getFirst(Selector::type()(Bonus::NO_TERRAIN_PENALTY).And(Selector::subtype()(i))))); @@ -1179,7 +1173,7 @@ void CPathfinderHelper::getNeighbours( continue; const TerrainTile & hlpt = map->getTile(hlp); - if(hlpt.terType == ETerrainType::ROCK) + if(!hlpt.terType.isPassable()) continue; // //we cannot visit things from blocked tiles @@ -1189,18 +1183,18 @@ void CPathfinderHelper::getNeighbours( // } /// Following condition let us avoid diagonal movement over coast when sailing - if(srct.terType == ETerrainType::WATER && limitCoastSailing && hlpt.terType == ETerrainType::WATER && dir.x && dir.y) //diagonal move through water + if(srct.terType.isWater() && limitCoastSailing && hlpt.terType.isWater() && dir.x && dir.y) //diagonal move through water { int3 hlp1 = tile, hlp2 = tile; hlp1.x += dir.x; hlp2.y += dir.y; - if(map->getTile(hlp1).terType != ETerrainType::WATER || map->getTile(hlp2).terType != ETerrainType::WATER) + if(map->getTile(hlp1).terType.isLand() || map->getTile(hlp2).terType.isLand()) continue; } - if(indeterminate(onLand) || onLand == (hlpt.terType != ETerrainType::WATER)) + if(indeterminate(onLand) || onLand == hlpt.terType.isLand()) { vec.push_back(hlp); } @@ -1238,7 +1232,7 @@ int CPathfinderHelper::getMovementCost( { ret = static_cast(ret * (100.0 + ti->valOfBonuses(Bonus::FLYING_MOVEMENT)) / 100.0); } - else if(dt->terType == ETerrainType::WATER) + else if(dt->terType.isWater()) { if(hero->boat && ct->hasFavorableWinds() && dt->hasFavorableWinds()) ret = static_cast(ret * 0.666); @@ -1266,7 +1260,7 @@ int CPathfinderHelper::getMovementCost( { std::vector vec; vec.reserve(8); //optimization - getNeighbours(*dt, dst, vec, ct->terType != ETerrainType::WATER, true); + getNeighbours(*dt, dst, vec, ct->terType.isLand(), true); for(auto & elem : vec) { int fcost = getMovementCost(dst, elem, nullptr, nullptr, left, false); diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 84984842a..7c5388514 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -13,6 +13,7 @@ #include "IGameCallback.h" #include "HeroBonus.h" #include "int3.h" +#include "Terrain.h" #include @@ -521,7 +522,7 @@ struct DLL_LINKAGE TurnInfo TConstBonusListPtr bonuses; mutable int maxMovePointsLand; mutable int maxMovePointsWater; - ETerrainType::EETerrainType nativeTerrain; + Terrain nativeTerrain; TurnInfo(const CGHeroInstance * Hero, const int Turn = 0); bool isLayerAvailable(const EPathfindingLayer layer) const; diff --git a/lib/CPlayerState.h b/lib/CPlayerState.h index dd2b431ca..5a2921ff0 100644 --- a/lib/CPlayerState.h +++ b/lib/CPlayerState.h @@ -67,14 +67,6 @@ public: h & dwellings; h & quests; h & visitedObjects; - - if(version < 760) - { - //was: h & getBonusList(); - BonusList junk; - h & junk; - } - h & status; h & daysWithoutCastle; h & enteredLosingCheatCode; diff --git a/lib/CSkillHandler.h b/lib/CSkillHandler.h index 425eafc38..1f93b4678 100644 --- a/lib/CSkillHandler.h +++ b/lib/CSkillHandler.h @@ -35,12 +35,9 @@ public: template void serialize(Handler & h, const int version) { h & description; - if(version >= 785) - { - h & iconSmall; - h & iconMedium; - h & iconLarge; - } + h & iconSmall; + h & iconMedium; + h & iconLarge; h & effects; } }; @@ -78,10 +75,7 @@ public: h & id; h & identifier; h & name; - if(version >= 785) - { - h & gainChance; - } + h & gainChance; h & levels; } diff --git a/lib/CStack.cpp b/lib/CStack.cpp index f1d9d3c27..64c5e98f8 100644 --- a/lib/CStack.cpp +++ b/lib/CStack.cpp @@ -32,7 +32,7 @@ CStack::CStack(const CStackInstance * Base, PlayerColor O, int I, ui8 Side, Slot slot(S), side(Side), initialPosition(), - nativeTerrain(ETerrainType::WRONG) + nativeTerrain() { health.init(); //??? } @@ -40,7 +40,7 @@ CStack::CStack(const CStackInstance * Base, PlayerColor O, int I, ui8 Side, Slot CStack::CStack() : CBonusSystemNode(STACK_BATTLE), CUnitState(), - nativeTerrain(ETerrainType::WRONG) + nativeTerrain() { base = nullptr; type = nullptr; @@ -328,11 +328,11 @@ bool CStack::canBeHealed() const bool CStack::isOnNativeTerrain() const { //this code is called from CreatureTerrainLimiter::limit on battle start - auto res = nativeTerrain == ETerrainType::ANY_TERRAIN || nativeTerrain == battle->getTerrainType(); + auto res = nativeTerrain == Terrain::ANY || nativeTerrain == battle->getTerrainType(); return res; } -bool CStack::isOnTerrain(int terrain) const +bool CStack::isOnTerrain(const Terrain & terrain) const { return battle->getTerrainType() == terrain; } diff --git a/lib/CStack.h b/lib/CStack.h index 2171a33b2..f969efab3 100644 --- a/lib/CStack.h +++ b/lib/CStack.h @@ -14,6 +14,7 @@ #include "CCreatureHandler.h" //todo: remove #include "battle/BattleHex.h" #include "mapObjects/CGHeroInstance.h" // for commander serialization +#include "Terrain.h" #include "battle/CUnitState.h" @@ -28,7 +29,7 @@ public: ui32 ID; //unique ID of stack const CCreature * type; - ETerrainType::EETerrainType nativeTerrain; //tmp variable to save native terrain value on battle init + Terrain nativeTerrain; //tmp variable to save native terrain value on battle init ui32 baseAmount; PlayerColor owner; //owner - player color (255 for neutrals) @@ -50,7 +51,7 @@ public: bool canBeHealed() const; //for first aid tent - only harmed stacks that are not war machines bool isOnNativeTerrain() const; - bool isOnTerrain(int terrain) const; + bool isOnTerrain(const Terrain & terrain) const; ui32 level() const; si32 magicResistance() const override; //include aura of resistance diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index f057997cb..bb5085f2b 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -26,6 +26,10 @@ const int NAMES_PER_TOWN=16; // number of town names per faction in H3 files. Json can define any number +const Terrain CTownHandler::defaultGoodTerrain{"grass"}; +const Terrain CTownHandler::defaultEvilTerrain{"lava"}; +const Terrain CTownHandler::defaultNeutralTerrain{"rough"}; + const std::map CBuilding::MODES = { { "normal", CBuilding::BUILD_NORMAL }, @@ -77,135 +81,11 @@ si32 CBuilding::getDistance(BuildingID buildID) const return -1; } -void CBuilding::deserializeFix() -{ - //default value for mode was broken, have to fix it here for old saves (v777 and older) - switch(mode) - { - case BUILD_NORMAL: - case BUILD_AUTO: - case BUILD_SPECIAL: - case BUILD_GRAIL: - break; - - default: - mode = BUILD_NORMAL; - break; - } -} - void CBuilding::addNewBonus(std::shared_ptr b, BonusList & bonusList) { bonusList.push_back(b); } -const JsonNode & CBuilding::getCurrentFactionForUpdateRoutine() const -{ - const auto & faction = town->faction->identifier; - const auto & factionsContent = (*VLC->modh->content)["factions"]; - const auto & coreData = factionsContent.modData.at("core"); - const auto & coreFactions = coreData.modData; - const auto & currentFaction = coreFactions[faction]; - - if(currentFaction.isNull()) - { - const auto index = faction.find(':'); - const std::string factionDir = index == std::string::npos ? faction : faction.substr(0, index); - const auto it = factionsContent.modData.find(factionDir); - - if(it == factionsContent.modData.end()) - { - logMod->warn("Warning: Update old save failed: Faction: '%s' is not found.", factionDir); - return currentFaction; - } - const std::string modFaction = index == std::string::npos ? faction : faction.substr(index + 1); - return it->second.modData[modFaction]; - } - return currentFaction; -} - -void CBuilding::update792() -{ - subId = BuildingSubID::NONE; - height = ETowerHeight::HEIGHT_NO_TOWER; - - if(!bid.IsSpecialOrGrail() || town == nullptr || town->faction == nullptr || town->faction->identifier.empty()) - return; - - const auto buildingName = CTownHandler::getMappedValue(bid, std::string(), MappedKeys::BUILDING_TYPES_TO_NAMES); - - if(buildingName.empty()) - return; - - auto & currentFaction = getCurrentFactionForUpdateRoutine(); - - if(!currentFaction.isNull() && currentFaction.getType() == JsonNode::JsonType::DATA_STRUCT) - { - const auto & buildings = currentFaction["town"]["buildings"]; - const auto & currentBuilding = buildings[buildingName]; - - subId = CTownHandler::getMappedValue(currentBuilding["type"], BuildingSubID::NONE, MappedKeys::SPECIAL_BUILDINGS); - height = subId == BuildingSubID::LOOKOUT_TOWER || bid == BuildingID::GRAIL - ? CTownHandler::getMappedValue(currentBuilding["height"], CBuilding::HEIGHT_NO_TOWER, CBuilding::TOWER_TYPES) - : height = CBuilding::HEIGHT_NO_TOWER; - } -} - -void CBuilding::update794() -{ - if(bid == BuildingID::TAVERN || subId == BuildingSubID::BROTHERHOOD_OF_SWORD) - { - VLC->townh->addBonusesForVanilaBuilding(this); - return; - } - if(!bid.IsSpecialOrGrail()) - return; - - VLC->townh->addBonusesForVanilaBuilding(this); - - if(!buildingBonuses.empty() //addBonusesForVanilaBuilding has done all work - || town->faction == nullptr //or faction data is not valid - || town->faction->identifier.empty()) - return; - - const auto buildingName = CTownHandler::getMappedValue(bid, std::string(), MappedKeys::BUILDING_TYPES_TO_NAMES, false); - - if(buildingName.empty()) - return; - - auto & currentFaction = getCurrentFactionForUpdateRoutine(); - - if(currentFaction.isNull() || currentFaction.getType() != JsonNode::JsonType::DATA_STRUCT) - return; - - const auto & buildings = currentFaction["town"]["buildings"]; - const auto & currentBuilding = buildings[buildingName]; - - CTownHandler::loadSpecialBuildingBonuses(currentBuilding["bonuses"], buildingBonuses, this); - CTownHandler::loadSpecialBuildingBonuses(currentBuilding["onVisitBonuses"], onVisitBonuses, this); - - if(!onVisitBonuses.empty()) - { - if(subId == BuildingSubID::NONE) - subId = BuildingSubID::CUSTOM_VISITING_BONUS; - - for(auto & bonus : onVisitBonuses) - bonus->sid = Bonus::getSid32(town->faction->index, bid); - } - const auto & overriddenBids = currentBuilding["overrides"]; - - if(overriddenBids.isNull()) - return; - - auto scope = town->getBuildingScope(); - - for(auto b : overriddenBids.Vector()) - { - auto bid = BuildingID(VLC->modh->identifiers.getIdentifier(scope, b).get()); - overrideBids.insert(bid); - } -} - CFaction::CFaction() { town = nullptr; @@ -1062,9 +942,9 @@ void CTownHandler::loadPuzzle(CFaction &faction, const JsonNode &source) assert(faction.puzzleMap.size() == GameConstants::PUZZLE_MAP_PIECES); } -ETerrainType::EETerrainType CTownHandler::getDefaultTerrainForAlignment(EAlignment::EAlignment alignment) const +Terrain CTownHandler::getDefaultTerrainForAlignment(EAlignment::EAlignment alignment) const { - ETerrainType::EETerrainType terrain = defaultGoodTerrain; + Terrain terrain = defaultGoodTerrain; switch(alignment) { @@ -1095,19 +975,15 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode faction->alignment = EAlignment::NEUTRAL; else faction->alignment = static_cast(alignment); - - auto nativeTerrain = source["nativeTerrain"]; - int terrainNum = nativeTerrain.isNull() - ? -1 - : vstd::find_pos(GameConstants::TERRAIN_NAMES, nativeTerrain.String()); auto preferUndergound = source["preferUndergroundPlacement"]; faction->preferUndergroundPlacement = preferUndergound.isNull() ? false : preferUndergound.Bool(); //Contructor is not called here, but operator= - faction->nativeTerrain = terrainNum < 0 + auto nativeTerrain = source["nativeTerrain"]; + faction->nativeTerrain = nativeTerrain.isNull() ? getDefaultTerrainForAlignment(faction->alignment) - : static_cast(terrainNum); + : Terrain(nativeTerrain.String()); if (!source["town"].isNull()) { diff --git a/lib/CTownHandler.h b/lib/CTownHandler.h index 7b38870af..7728d2992 100644 --- a/lib/CTownHandler.h +++ b/lib/CTownHandler.h @@ -20,6 +20,7 @@ #include "LogicalExpression.h" #include "battle/BattleHex.h" #include "HeroBonus.h" +#include "Terrain.h" class CLegacyConfigParser; class JsonNode; @@ -111,8 +112,6 @@ public: } void addNewBonus(std::shared_ptr b, BonusList & bonusList); - void update792(); - void update794(); template void serialize(Handler &h, const int version) { @@ -126,33 +125,14 @@ public: h & requirements; h & upgrade; h & mode; - - if(version >= 792) - { - h & subId; - h & height; - } - if(!h.saving && version < 793) - update792(); //adjust height, subId - - if(version >= 794) - { - h & overrideBids; - h & buildingBonuses; - h & onVisitBonuses; - } - else if(!h.saving) - update794(); //populate overrideBids, buildingBonuses, onVisitBonuses - - if(!h.saving) - deserializeFix(); + h & subId; + h & height; + h & overrideBids; + h & buildingBonuses; + h & onVisitBonuses; } friend class CTownHandler; - -private: - void deserializeFix(); - const JsonNode & getCurrentFactionForUpdateRoutine() const; }; /// This is structure used only by client @@ -205,7 +185,7 @@ public: TFaction index; - ETerrainType nativeTerrain; + Terrain nativeTerrain; EAlignment::EAlignment alignment; bool preferUndergroundPlacement; @@ -356,14 +336,7 @@ public: h & warMachine; h & clientInfo; h & moatDamage; - if(version >= 758) - { - h & moatHexes; - } - else if(!h.saving) - { - moatHexes = defaultMoatHexes(); - } + h & moatHexes; h & defaultTavernChance; } @@ -385,9 +358,9 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase requirementsToLoad; std::vector overriddenBidsToLoad; //list of buildings, which bonuses should be overridden. - const static ETerrainType::EETerrainType defaultGoodTerrain = ETerrainType::EETerrainType::GRASS; - const static ETerrainType::EETerrainType defaultEvilTerrain = ETerrainType::EETerrainType::LAVA; - const static ETerrainType::EETerrainType defaultNeutralTerrain = ETerrainType::EETerrainType::ROUGH; + const static Terrain defaultGoodTerrain; + const static Terrain defaultEvilTerrain; + const static Terrain defaultNeutralTerrain; static TPropagatorPtr & emptyPropagator(); @@ -418,7 +391,7 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase void serialize(Handler &h, const int version) { h & objects; - - if(version >= 770) - { - h & randomTown; - } - else if(!h.saving) - { - loadRandomFaction(); - } + h & randomTown; } protected: diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index ff39e5d82..ed73ef621 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -236,38 +236,6 @@ std::ostream & operator<<(std::ostream & os, const EActionType actionType) else return os << it->second; } -std::ostream & operator<<(std::ostream & os, const ETerrainType terrainType) -{ - static const std::map terrainTypeToString = - { - #define DEFINE_ELEMENT(element) {ETerrainType::element, #element} - DEFINE_ELEMENT(WRONG), - DEFINE_ELEMENT(BORDER), - DEFINE_ELEMENT(DIRT), - DEFINE_ELEMENT(SAND), - DEFINE_ELEMENT(GRASS), - DEFINE_ELEMENT(SNOW), - DEFINE_ELEMENT(SWAMP), - DEFINE_ELEMENT(ROUGH), - DEFINE_ELEMENT(SUBTERRANEAN), - DEFINE_ELEMENT(LAVA), - DEFINE_ELEMENT(WATER), - DEFINE_ELEMENT(ROCK) - #undef DEFINE_ELEMENT - }; - - auto it = terrainTypeToString.find(terrainType.num); - if (it == terrainTypeToString.end()) return os << ""; - else return os << it->second; -} - -std::string ETerrainType::toString() const -{ - std::stringstream ss; - ss << *this; - return ss.str(); -} - std::ostream & operator<<(std::ostream & os, const EPathfindingLayer pathfindingLayer) { static const std::map pathfinderLayerToString diff --git a/lib/GameConstants.h b/lib/GameConstants.h index ce0236bea..34777ad55 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -61,7 +61,6 @@ namespace GameConstants const int SKILL_QUANTITY=28; const int PRIMARY_SKILLS=4; - const int TERRAIN_TYPES=10; const int RESOURCE_QUANTITY=8; const int HEROES_PER_TYPE=8; //amount of heroes of each type @@ -678,21 +677,8 @@ enum class ETeleportChannelType }; -namespace ERiverType -{ - enum ERiverType - { - NO_RIVER, CLEAR_RIVER, ICY_RIVER, MUDDY_RIVER, LAVA_RIVER - }; -} - -namespace ERoadType -{ - enum ERoadType - { - NO_ROAD, DIRT_ROAD, GRAVEL_ROAD, COBBLESTONE_ROAD - }; -} +static std::vector RIVER_NAMES {"", "rw", "ri", "rm", "rl"}; +static std::vector ROAD_NAMES {"", "pd", "pg", "pc"}; class Obj { @@ -912,36 +898,6 @@ enum class EActionType : int32_t DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EActionType actionType); -class DLL_LINKAGE ETerrainType -{ -public: - enum EETerrainType - { - ANY_TERRAIN = -3, - WRONG = -2, BORDER = -1, DIRT, SAND, GRASS, SNOW, SWAMP, - ROUGH, SUBTERRANEAN, LAVA, WATER, ROCK // ROCK is also intended to be max value. - }; - - ETerrainType(EETerrainType _num = WRONG) : num(_num) - {} - - ETerrainType& operator=(EETerrainType _num) - { - num = _num; - return *this; - } - - ID_LIKE_CLASS_COMMON(ETerrainType, EETerrainType) - - EETerrainType num; - - std::string toString() const; -}; - -DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const ETerrainType terrainType); - -ID_LIKE_OPERATORS(ETerrainType, ETerrainType::EETerrainType) - class DLL_LINKAGE EDiggingStatus { public: diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 5a59862cc..50eaf021f 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -2078,13 +2078,8 @@ bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest) return nodeType == dest->getNodeType(); } -CreatureTerrainLimiter::CreatureTerrainLimiter(int TerrainType) - : terrainType(TerrainType) -{ -} - CreatureTerrainLimiter::CreatureTerrainLimiter() - : terrainType(-1) + : terrainType() { } @@ -2094,7 +2089,7 @@ int CreatureTerrainLimiter::limit(const BonusLimitationContext &context) const const CStack *stack = retrieveStackBattle(&context.node); if(stack) { - if(terrainType == -1)//terrainType not specified = native + if(terrainType.isNative())//terrainType not specified = native return !stack->isOnNativeTerrain(); return !stack->isOnTerrain(terrainType); } @@ -2105,7 +2100,7 @@ int CreatureTerrainLimiter::limit(const BonusLimitationContext &context) const std::string CreatureTerrainLimiter::toString() const { boost::format fmt("CreatureTerrainLimiter(terrainType=%s)"); - fmt % (terrainType >= 0 ? GameConstants::TERRAIN_NAMES[terrainType] : "native"); + fmt % (terrainType.isNative() ? "native" : static_cast(terrainType)); return fmt.str(); } @@ -2114,8 +2109,8 @@ JsonNode CreatureTerrainLimiter::toJsonNode() const JsonNode root(JsonNode::JsonType::DATA_STRUCT); root["type"].String() = "CREATURE_TERRAIN_LIMITER"; - if(terrainType >= 0) - root["parameters"].Vector().push_back(JsonUtils::stringNode(GameConstants::TERRAIN_NAMES[terrainType])); + if(!terrainType.isNative()) + root["parameters"].Vector().push_back(JsonUtils::stringNode(terrainType)); return root; } diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 1bb218e28..28f00d05f 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -11,6 +11,7 @@ #include "GameConstants.h" #include "JsonNode.h" +#include "Terrain.h" class CCreature; struct Bonus; @@ -438,36 +439,15 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this h & val; h & sid; h & description; - if(version >= 783) - { - h & additionalInfo; - } - else - { - additionalInfo.resize(1, -1); - h & additionalInfo[0]; - } + h & additionalInfo; h & turnsRemain; h & valType; - if(version >= 784) - { - h & stacking; - } + h & stacking; h & effectRange; h & limiter; h & propagator; - if(version >= 781) - { - h & updater; - } - if(version >= 801) - { - h & propagationUpdater; - } - if(version < 801 && !h.saving) //Opposite Side bonuses are introduced - { - updateOppositeBonuses(); - } + h & updater; + h & propagationUpdater; } template @@ -999,10 +979,7 @@ public: template void serialize(Handler & h, const int version) { h & static_cast(*this); - if(version >= 786) - { - h & limiters; - } + h & limiters; } }; @@ -1081,9 +1058,9 @@ public: class DLL_LINKAGE CreatureTerrainLimiter : public ILimiter //applies only to creatures that are on specified terrain, default native terrain { public: - int terrainType; + Terrain terrainType; CreatureTerrainLimiter(); - CreatureTerrainLimiter(int TerrainType); + CreatureTerrainLimiter(const Terrain& terrain); int limit(const BonusLimitationContext &context) const override; virtual std::string toString() const override; diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 2cc8482c2..cc75b45d0 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -49,7 +49,7 @@ void CPrivilegedInfoCallback::getFreeTiles(std::vector & tiles) const for (int yd = 0; yd < gs->map->height; yd++) { tinfo = getTile(int3 (xd,yd,zd)); - if (tinfo->terType != ETerrainType::WATER && tinfo->terType != ETerrainType::ROCK && !tinfo->blocked) //land and free + if (tinfo->terType.isLand() && tinfo->terType.isPassable() && !tinfo->blocked) //land and free tiles.push_back (int3 (xd,yd,zd)); } } @@ -116,8 +116,8 @@ void CPrivilegedInfoCallback::getAllTiles(std::unordered_set & { for (int yd = 0; yd < gs->map->height; yd++) { - if ((getTile (int3 (xd,yd,zd))->terType == ETerrainType::WATER && water) - || (getTile (int3 (xd,yd,zd))->terType != ETerrainType::WATER && land)) + if ((getTile (int3 (xd,yd,zd))->terType.isWater() && water) + || (getTile (int3 (xd,yd,zd))->terType.isLand() && land)) tiles.insert(int3(xd,yd,zd)); } } diff --git a/lib/JsonDetail.h b/lib/JsonDetail.h index 5095eabd9..89d19d5a6 100644 --- a/lib/JsonDetail.h +++ b/lib/JsonDetail.h @@ -57,7 +57,7 @@ public: }; //Internal class for string -> JsonNode conversion -class JsonParser +class DLL_LINKAGE JsonParser { std::string errors; // Contains description of all encountered errors constString input; // Input data diff --git a/lib/JsonNode.cpp b/lib/JsonNode.cpp index d8a8dbd9a..f80e8cfba 100644 --- a/lib/JsonNode.cpp +++ b/lib/JsonNode.cpp @@ -59,6 +59,15 @@ JsonNode::JsonNode(const ResourceID & fileURI): *this = parser.parse(fileURI.getName()); } +JsonNode::JsonNode(const std::string & idx, const ResourceID & fileURI): +type(JsonType::DATA_NULL) +{ + auto file = CResourceHandler::get(idx)->load(fileURI)->readAll(); + + JsonParser parser(reinterpret_cast(file.first.get()), file.second); + *this = parser.parse(fileURI.getName()); +} + JsonNode::JsonNode(ResourceID && fileURI, bool &isValidSyntax): type(JsonType::DATA_NULL) { @@ -711,7 +720,8 @@ std::shared_ptr JsonUtils::parseLimiter(const JsonNode & limiter) { VLC->modh->identifiers.requestIdentifier("terrain", parameters[0], [=](si32 terrain) { - terrainLimiter->terrainType = terrain; + //TODO: support limiters + //terrainLimiter->terrainType = terrain; }); } return terrainLimiter; diff --git a/lib/JsonNode.h b/lib/JsonNode.h index 6db74d158..e29865f49 100644 --- a/lib/JsonNode.h +++ b/lib/JsonNode.h @@ -60,6 +60,7 @@ public: //Create tree from JSON file explicit JsonNode(ResourceID && fileURI); explicit JsonNode(const ResourceID & fileURI); + explicit JsonNode(const std::string& idx, const ResourceID & fileURI); explicit JsonNode(ResourceID && fileURI, bool & isValidSyntax); //Copy c-tor JsonNode(const JsonNode ©); @@ -127,10 +128,7 @@ public: template void serialize(Handler &h, const int version) { h & meta; - if(version >= 782) - { - h & flags; - } + h & flags; h & type; switch(type) { @@ -152,10 +150,7 @@ public: h & data.Struct; break; case JsonType::DATA_INTEGER: - if(version >= 770) - { - h & data.Integer; - } + h & data.Integer; break; } } diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 249afd680..7efc87096 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -699,13 +699,13 @@ DLL_LINKAGE void GiveHero::applyGs(CGameState *gs) DLL_LINKAGE void NewObject::applyGs(CGameState *gs) { - ETerrainType terrainType; + Terrain terrainType; 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(ETerrainType::WATER).front(); + testObject.appearance = VLC->objtypeh->getHandlerFor(ID, subID)->getTemplates(Terrain("water")).front(); const int3 previousXAxisTile = int3(pos.x - 1, pos.y, pos.z); assert(gs->isInTheMap(previousXAxisTile) && (testObject.visitablePos() == previousXAxisTile)); @@ -722,7 +722,7 @@ DLL_LINKAGE void NewObject::applyGs(CGameState *gs) { case Obj::BOAT: o = new CGBoat(); - terrainType = ETerrainType::WATER; //TODO: either boat should only spawn on water, or all water objects should be handled this way + terrainType = Terrain("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/PathfinderUtil.h b/lib/PathfinderUtil.h index dc4f9a1d5..7070d82c5 100644 --- a/lib/PathfinderUtil.h +++ b/lib/PathfinderUtil.h @@ -59,13 +59,13 @@ namespace PathfinderUtil break; case ELayer::WATER: - if(tinfo->blocked || tinfo->terType != ETerrainType::WATER) + if(tinfo->blocked || tinfo->terType.isLand()) return CGPathNode::BLOCKED; break; case ELayer::AIR: - if(tinfo->blocked || tinfo->terType == ETerrainType::WATER) + if(tinfo->blocked || tinfo->terType.isLand()) return CGPathNode::FLYABLE; break; diff --git a/lib/StartInfo.h b/lib/StartInfo.h index e42b88555..2f0fb3b71 100644 --- a/lib/StartInfo.h +++ b/lib/StartInfo.h @@ -56,19 +56,7 @@ struct DLL_LINKAGE PlayerSettings h & color; h & handicap; h & name; - if(version < 787) - { - ui8 oldConnectedId = 0; - h & oldConnectedId; - if(oldConnectedId) - { - connectedPlayerIDs.insert(oldConnectedId); - } - } - else - { - h & connectedPlayerIDs; - } + h & connectedPlayerIDs; h & team; h & compOnly; } diff --git a/lib/StringConstants.h b/lib/StringConstants.h index e1dcf9e25..721a070ae 100644 --- a/lib/StringConstants.h +++ b/lib/StringConstants.h @@ -16,10 +16,6 @@ /// namespace GameConstants { - const std::string TERRAIN_NAMES [TERRAIN_TYPES] = { - "dirt", "sand", "grass", "snow", "swamp", "rough", "subterra", "lava", "water", "rock" - }; - const std::string RESOURCE_NAMES [RESOURCE_QUANTITY] = { "wood", "mercury", "ore", "sulfur", "crystal", "gems", "gold", "mithril" }; diff --git a/lib/Terrain.cpp b/lib/Terrain.cpp new file mode 100644 index 000000000..17f8de861 --- /dev/null +++ b/lib/Terrain.cpp @@ -0,0 +1,204 @@ +/* + * 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 "Terrain.h" +#include "VCMI_Lib.h" +#include "CModHandler.h" + +//regular expression to change id for string at config +//("allowedTerrain"\s*:\s*\[.*)9(.*\],\n) +//\1"rock"\2 + +const Terrain Terrain::ANY("ANY"); + +Terrain Terrain::createTerrainTypeH3M(int tId) +{ + static std::array terrainsH3M + { + "dirt", "sand", "grass", "snow", "swamp", "rough", "subterra", "lava", "water", "rock" + }; + return Terrain(terrainsH3M.at(tId)); +} + +Terrain Terrain::createTerrainByCode(const std::string & typeCode) +{ + for(const auto & terrain : Manager::terrains()) + { + if(Manager::getInfo(terrain).typeCode == typeCode) + return terrain; + } + return Terrain::ANY; +} + +Terrain::Manager::Manager() +{ + auto allConfigs = VLC->modh->getActiveMods(); + allConfigs.insert(allConfigs.begin(), "core"); + 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()) + { + Terrain::Info info; + info.moveCost = 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.type = Terrain::Info::Type::Land; + } + else + { + auto s = terr.second["type"].String(); + if(s == "LAND") info.type = Terrain::Info::Type::Land; + if(s == "WATER") info.type = Terrain::Info::Type::Water; + if(s == "SUB") info.type = Terrain::Info::Type::Subterranean; + if(s == "ROCK") info.type = Terrain::Info::Type::Rock; + } + + if(terr.second["horseSoundId"].isNull()) + { + info.horseSoundId = 9; //rock sound as default + } + else + { + info.horseSoundId = terr.second["horseSoundId"].Integer(); + } + + 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); + } + + + terrainInfo[Terrain(terr.first)] = info; + } + } +} + +Terrain::Manager & Terrain::Manager::get() +{ + static Terrain::Manager manager; + return manager; +} + +std::vector Terrain::Manager::terrains() +{ + std::vector _terrains; + for(const auto & info : Terrain::Manager::get().terrainInfo) + _terrains.push_back(info.first); + return _terrains; +} + +const Terrain::Info & Terrain::Manager::getInfo(const Terrain & terrain) +{ + return Terrain::Manager::get().terrainInfo.at(terrain); +} + +std::ostream & operator<<(std::ostream & os, const Terrain terrainType) +{ + return os << static_cast(terrainType); +} + +Terrain::operator std::string() const +{ + return name; +} + +Terrain::Terrain(const std::string & _name) : name(_name) +{} + +Terrain& Terrain::operator=(const Terrain & _name) +{ + name = _name.name; + return *this; +} + +Terrain& Terrain::operator=(const std::string & _name) +{ + name = _name; + return *this; +} + +bool operator==(const Terrain & l, const Terrain & r) +{ + return l.name == r.name; +} + +bool operator!=(const Terrain & l, const Terrain & r) +{ + return l.name != r.name; +} + +bool operator<(const Terrain & l, const Terrain & r) +{ + return l.name < r.name; +} + +int Terrain::id() const +{ + if(name == "ANY") return -3; + if(name == "WRONG") return -2; + if(name == "BORDER") return -1; + + auto _terrains = Terrain::Manager::terrains(); + auto iter = std::find(_terrains.begin(), _terrains.end(), *this); + return iter - _terrains.begin(); +} + +bool Terrain::isLand() const +{ + return !isWater(); +} +bool Terrain::isWater() const +{ + return Terrain::Manager::getInfo(*this).type == Terrain::Info::Type::Water; +} +bool Terrain::isPassable() const +{ + return Terrain::Manager::getInfo(*this).type != Terrain::Info::Type::Rock; +} +bool Terrain::isUnderground() const +{ + return Terrain::Manager::getInfo(*this).type == Terrain::Info::Type::Subterranean; +} +bool Terrain::isNative() const +{ + return name.empty(); +} diff --git a/lib/Terrain.h b/lib/Terrain.h new file mode 100644 index 000000000..d173f2ab7 --- /dev/null +++ b/lib/Terrain.h @@ -0,0 +1,93 @@ +/* + * 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 "JsonNode.h" + +class DLL_LINKAGE Terrain +{ +public: + + friend class Manager; + + struct Info + { + enum class Type + { + Land, Water, Subterranean, Rock + }; + + int moveCost; + std::array minimapBlocked; + std::array minimapUnblocked; + std::string musicFilename; + std::string tilesFilename; + std::string terrainText; + std::string typeCode; + int horseSoundId; + Type type; + }; + + class DLL_LINKAGE Manager + { + public: + static std::vector terrains(); + static const Info & getInfo(const Terrain &); + + private: + static Manager & get(); + Manager(); + + std::map terrainInfo; + }; + + /*enum EETerrainType + { + ANY_TERRAIN = -3, + WRONG = -2, BORDER = -1, DIRT, SAND, GRASS, SNOW, SWAMP, + ROUGH, SUBTERRANEAN, LAVA, WATER, ROCK // ROCK is also intended to be max value. + };*/ + + Terrain(const std::string & _type = ""); + static Terrain createTerrainTypeH3M(int tId); + static Terrain createTerrainByCode(const std::string & typeCode); + + int id() const; //TODO: has to be completely removed + + Terrain& operator=(const Terrain & _type); + Terrain& operator=(const std::string & _type); + + DLL_LINKAGE friend bool operator==(const Terrain & l, const Terrain & r); + DLL_LINKAGE friend bool operator!=(const Terrain & l, const Terrain & r); + DLL_LINKAGE friend bool operator<(const Terrain & l, const Terrain & r); + + static const Terrain ANY; + + bool isLand() const; + bool isWater() const; + bool isPassable() const; //ROCK + bool isUnderground() const; + bool isNative() const; + + operator std::string() const; + + template void serialize(Handler &h, const int version) + { + h & name; + } + +protected: + + std::string name; +}; + +DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const Terrain terrainType); diff --git a/lib/VCMI_Lib.cpp b/lib/VCMI_Lib.cpp index a558dcf0c..30ba67e41 100644 --- a/lib/VCMI_Lib.cpp +++ b/lib/VCMI_Lib.cpp @@ -288,15 +288,3 @@ void LibClasses::setContent(std::shared_ptr content) { modh->content = content; } - -void LibClasses::restoreAllCreaturesNodeType794() -{ - creh->restoreAllCreaturesNodeType794(); -} - -void LibClasses::update800() -{ - vstd::clear_pointer(scriptHandler); - scriptHandler = new scripting::ScriptHandler(); -} - diff --git a/lib/VCMI_Lib.h b/lib/VCMI_Lib.h index 38b4a6bf5..e982e3cfe 100644 --- a/lib/VCMI_Lib.h +++ b/lib/VCMI_Lib.h @@ -44,7 +44,6 @@ class DLL_LINKAGE LibClasses : public Services void makeNull(); //sets all handler pointers to null std::shared_ptr getContent() const; void setContent(std::shared_ptr content); - void restoreAllCreaturesNodeType794(); public: bool IS_AI_ENABLED; //unused? @@ -91,33 +90,20 @@ public: template void serialize(Handler &h, const int version) { - if(version >= 800) + h & scriptHandler;//must be first (or second after modh), it can modify factories other handlers depends on + if(!h.saving) { - h & scriptHandler;//must be first (or second after modh), it can modify factories other handlers depends on - if(!h.saving) - { - scriptsLoaded(); - } - } - else if(!h.saving) - { - update800(); + scriptsLoaded(); } h & heroh; h & arth; h & creh; - if(!h.saving && version < 794) - restoreAllCreaturesNodeType794(); - h & townh; h & objh; h & objtypeh; h & spellh; - if(version >= 777) - { - h & skillh; - } + h & skillh; if(!h.saving) { //modh will be changed and modh->content will be empty after deserialization diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index 12e347cf7..bc119bde7 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -15,6 +15,7 @@ #include "../filesystem/Filesystem.h" #include "../mapObjects/CGTownInstance.h" #include "../CGeneralTextHandler.h" +#include "../Terrain.h" //TODO: remove #include "../IGameCallback.h" @@ -186,7 +187,7 @@ struct RangeGenerator std::function myRand; }; -BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType battlefieldType, const CArmedInstance * armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance * town) +BattleInfo * BattleInfo::setupBattle(int3 tile, Terrain terrain, BFieldType battlefieldType, const CArmedInstance * armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance * town) { CMP_stack cmpst; auto curB = new BattleInfo(); @@ -610,7 +611,7 @@ CStack * BattleInfo::getStack(int stackID, bool onlyAlive) BattleInfo::BattleInfo() : round(-1), activeStack(-1), town(nullptr), tile(-1,-1,-1), - battlefieldType(BFieldType::NONE), terrainType(ETerrainType::WRONG), + battlefieldType(BFieldType::NONE), terrainType(), tacticsSide(0), tacticDistance(0) { setBattle(this); @@ -644,7 +645,7 @@ BFieldType BattleInfo::getBattlefieldType() const return battlefieldType; } -ETerrainType BattleInfo::getTerrainType() const +Terrain BattleInfo::getTerrainType() const { return terrainType; } diff --git a/lib/battle/BattleInfo.h b/lib/battle/BattleInfo.h index e1e85a7da..0fc5a03b9 100644 --- a/lib/battle/BattleInfo.h +++ b/lib/battle/BattleInfo.h @@ -18,6 +18,7 @@ class CStack; class CStackInstance; class CStackBasicDescriptor; +class Terrain; class DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallback, public IBattleState { @@ -36,7 +37,7 @@ public: SiegeInfo si; BFieldType battlefieldType; //like !!BA:B - ETerrainType terrainType; //used for some stack nativity checks (not the bonus limiters though that have their own copy) + Terrain terrainType; //used for some stack nativity checks (not the bonus limiters though that have their own copy) ui8 tacticsSide; //which side is requested to play tactics phase ui8 tacticDistance; //how many hexes we can go forward (1 = only hexes adjacent to margin line) @@ -72,7 +73,7 @@ public: battle::Units getUnitsIf(battle::UnitFilter predicate) const override; BFieldType getBattlefieldType() const override; - ETerrainType getTerrainType() const override; + Terrain getTerrainType() const override; ObstacleCList getAllObstacles() const override; @@ -137,7 +138,7 @@ public: const CGHeroInstance * getHero(PlayerColor player) const; //returns fighting hero that belongs to given player void localInit(); - static BattleInfo * setupBattle(int3 tile, ETerrainType terrain, BFieldType battlefieldType, const CArmedInstance * armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance * town); + static BattleInfo * setupBattle(int3 tile, Terrain terrain, BFieldType battlefieldType, const CArmedInstance * armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance * town); ui8 whatSide(PlayerColor player) const; diff --git a/lib/battle/BattleProxy.cpp b/lib/battle/BattleProxy.cpp index 8ca8a418c..b1d533501 100644 --- a/lib/battle/BattleProxy.cpp +++ b/lib/battle/BattleProxy.cpp @@ -10,6 +10,7 @@ #include "StdInc.h" #include "BattleProxy.h" #include "Unit.h" +#include "Terrain.h" ///BattleProxy @@ -46,7 +47,7 @@ BFieldType BattleProxy::getBattlefieldType() const return subject->battleGetBattlefieldType(); } -ETerrainType BattleProxy::getTerrainType() const +Terrain BattleProxy::getTerrainType() const { return subject->battleTerrainType(); } diff --git a/lib/battle/BattleProxy.h b/lib/battle/BattleProxy.h index d55d469e9..0489d6974 100644 --- a/lib/battle/BattleProxy.h +++ b/lib/battle/BattleProxy.h @@ -30,7 +30,7 @@ public: battle::Units getUnitsIf(battle::UnitFilter predicate) const override; BFieldType getBattlefieldType() const override; - ETerrainType getTerrainType() const override; + Terrain getTerrainType() const override; ObstacleCList getAllObstacles() const override; diff --git a/lib/battle/CBattleInfoEssentials.cpp b/lib/battle/CBattleInfoEssentials.cpp index 589b93ed6..f1b1a55f0 100644 --- a/lib/battle/CBattleInfoEssentials.cpp +++ b/lib/battle/CBattleInfoEssentials.cpp @@ -14,9 +14,9 @@ #include "../NetPacks.h" #include "../mapObjects/CGTownInstance.h" -ETerrainType CBattleInfoEssentials::battleTerrainType() const +Terrain CBattleInfoEssentials::battleTerrainType() const { - RETURN_IF_NOT_BATTLE(ETerrainType::WRONG); + RETURN_IF_NOT_BATTLE(Terrain()); return getBattle()->getTerrainType(); } diff --git a/lib/battle/CBattleInfoEssentials.h b/lib/battle/CBattleInfoEssentials.h index ab8fc1d4b..8d489b998 100644 --- a/lib/battle/CBattleInfoEssentials.h +++ b/lib/battle/CBattleInfoEssentials.h @@ -46,7 +46,7 @@ public: BattlePerspective::BattlePerspective battleGetMySide() const; const IBonusBearer * getBattleNode() const; - ETerrainType battleTerrainType() const override; + Terrain battleTerrainType() const override; BFieldType battleGetBattlefieldType() const override; int32_t battleGetEnchanterCounter(ui8 side) const; diff --git a/lib/battle/IBattleInfoCallback.h b/lib/battle/IBattleInfoCallback.h index 92f50ebe1..999dbc536 100644 --- a/lib/battle/IBattleInfoCallback.h +++ b/lib/battle/IBattleInfoCallback.h @@ -14,7 +14,7 @@ struct CObstacleInstance; class BFieldType; -class ETerrainType; +class Terrain; namespace battle { @@ -34,7 +34,7 @@ class DLL_LINKAGE IBattleInfoCallback public: virtual scripting::Pool * getContextPool() const = 0; - virtual ETerrainType battleTerrainType() const = 0; + virtual Terrain battleTerrainType() const = 0; virtual BFieldType battleGetBattlefieldType() const = 0; ///return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw diff --git a/lib/battle/IBattleState.h b/lib/battle/IBattleState.h index 8aa80e4d2..2d96be75f 100644 --- a/lib/battle/IBattleState.h +++ b/lib/battle/IBattleState.h @@ -41,7 +41,7 @@ public: virtual battle::Units getUnitsIf(battle::UnitFilter predicate) const = 0; virtual BFieldType getBattlefieldType() const = 0; - virtual ETerrainType getTerrainType() const = 0; + virtual Terrain getTerrainType() const = 0; virtual ObstacleCList getAllObstacles() const = 0; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 59ebdd789..5fe402118 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -79,28 +79,28 @@ 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 != ERoadType::NO_ROAD && from.roadType != ERoadType::NO_ROAD) + if(dest.roadType != ROAD_NAMES[0] && from.roadType != ROAD_NAMES[0]) { - int road = std::min(dest.roadType,from.roadType); //used road ID - switch(road) + int roadPos = std::min(vstd::find_pos(ROAD_NAMES, dest.roadType), vstd::find_pos(ROAD_NAMES, from.roadType)); //used road ID + switch(roadPos) { - case ERoadType::DIRT_ROAD: + case 1: ret = 75; break; - case ERoadType::GRAVEL_ROAD: + case 2: ret = 65; break; - case ERoadType::COBBLESTONE_ROAD: + case 3: ret = 50; break; default: - logGlobal->error("Unknown road type: %d", road); + logGlobal->error("Unknown road type: %d", roadPos); break; } } else if(ti->nativeTerrain != from.terType //the terrain is not native - && ti->nativeTerrain != ETerrainType::ANY_TERRAIN //no special creature bonus - && !ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType) //no special movement bonus + && ti->nativeTerrain != Terrain::ANY //no special creature bonus + && !ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType.id()) //no special movement bonus ) { static const CSelector selectorPATHFINDING = Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::PATHFINDING); @@ -114,7 +114,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile & dest, const TerrainTile & f return (ui32)ret; } -ETerrainType::EETerrainType CGHeroInstance::getNativeTerrain() const +Terrain CGHeroInstance::getNativeTerrain() const { // NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army. // This is clearly bug in H3 however intended behaviour is not clear. @@ -122,18 +122,18 @@ ETerrainType::EETerrainType CGHeroInstance::getNativeTerrain() const // will always have best penalty without any influence from player-defined stacks order // TODO: What should we do if all hero stacks are neutral creatures? - ETerrainType::EETerrainType nativeTerrain = ETerrainType::BORDER; + Terrain nativeTerrain("BORDER"); for(auto stack : stacks) { - ETerrainType::EETerrainType stackNativeTerrain = stack.second->type->getNativeTerrain(); //consider terrain bonuses e.g. Lodestar. + Terrain stackNativeTerrain = stack.second->type->getNativeTerrain(); //consider terrain bonuses e.g. Lodestar. - if(stackNativeTerrain == ETerrainType::BORDER) + if(stackNativeTerrain == Terrain("BORDER")) continue; - if(nativeTerrain == ETerrainType::BORDER) + if(nativeTerrain == Terrain("BORDER")) nativeTerrain = stackNativeTerrain; else if(nativeTerrain != stackNativeTerrain) - return ETerrainType::BORDER; + return Terrain("BORDER"); } return nativeTerrain; } @@ -560,23 +560,6 @@ void CGHeroInstance::recreateSecondarySkillsBonuses() updateSkillBonus(SecondarySkill(skill_info.first), skill_info.second); } -void CGHeroInstance::recreateSpecialtyBonuses(std::vector & specialtyDeprecated) -{ - auto HeroSpecialToSpecialtyBonus = [](HeroSpecial & hs) -> SSpecialtyBonus - { - SSpecialtyBonus sb; - sb.growsWithLevel = hs.growsWithLevel; - sb.bonuses = hs.getBonusList(); - return sb; - }; - - for(HeroSpecial * hs : specialtyDeprecated) - { - for(std::shared_ptr b : SpecialtyBonusToBonuses(HeroSpecialToSpecialtyBonus(*hs), type->ID.getNum())) - addNewBonus(b); - } -} - void CGHeroInstance::updateSkillBonus(SecondarySkill which, int val) { removeBonuses(Selector::source(Bonus::SECONDARY_SKILL, which)); diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 2071d3f5e..02b738c68 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -1,4 +1,4 @@ -/* +/* * CGHeroInstance.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder @@ -89,15 +89,7 @@ public: template void serialize(Handler &h, const int version) { h & patrolling; - if(version >= 755) //save format backward compatibility - { - h & initialPos; - } - else if(!h.saving) - { - patrolling = false; - initialPos = int3(); - } + h & initialPos; h & patrolRadius; } } patrol; @@ -163,7 +155,7 @@ public: bool needsLastStack()const override; ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from, const TurnInfo * ti) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling - ETerrainType::EETerrainType getNativeTerrain() const; + Terrain getNativeTerrain() const; ui32 getLowestCreatureSpeed() const; int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation' si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day @@ -287,7 +279,6 @@ protected: private: void levelUpAutomatically(CRandomGenerator & rand); - void recreateSpecialtyBonuses(std::vector & specialtyDeprecated); public: std::string getHeroTypeName() const; @@ -316,18 +307,8 @@ public: h & visitedTown; h & boat; h & type; - if(version < 781) - { - std::vector specialtyDeprecated; - h & specialtyDeprecated; - if(!h.saving) - recreateSpecialtyBonuses(specialtyDeprecated); - } h & commander; h & visitedObjects; BONUS_TREE_DESERIALIZATION_FIX - //visitied town pointer will be restored by map serialization method - if(version < 777 && !h.saving) - recreateSecondarySkillsBonuses(); } }; diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 290be028e..843f21326 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -796,28 +796,6 @@ void CGTownInstance::addTownBonuses() } } -void CGTownInstance::fixBonusingDuplicates() //For versions 794-800 -{ - std::map bids; - - for(auto i = 0; i != bonusingBuildings.size(); i++) - { - auto bid = bonusingBuildings[i]->getBuildingType(); - if(!bids.count(bid)) - bids.insert({ bid, 0 }); - else - bids[bid]++; - } - for(auto & pair : bids) - { - if(!pair.second) - continue; - - for(auto i = 0; i < pair.second; i++) - deleteTownBonus(pair.first); - } -} - void CGTownInstance::deleteTownBonus(BuildingID::EBuildingID bid) { size_t i = 0; @@ -875,90 +853,6 @@ void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structu updateAppearance(); } -void CGTownInstance::updateBonusingBuildings() //update to version 792 -{ - if(this->town->faction != nullptr) - { - //firstly, update subtype for the Bonusing objects, which are already stored in the bonusing list - for(auto building : bonusingBuildings) //no garrison bonuses here, only week and visiting bonuses - { - switch(this->town->faction->index) - { - case ETownType::CASTLE: - building->setBuildingSubtype(BuildingSubID::STABLES); - break; - - case ETownType::DUNGEON: - if(building->getBuildingType() == BuildingID::SPECIAL_2) - building->setBuildingSubtype(BuildingSubID::MANA_VORTEX); - else if(building->getBuildingType() == BuildingID::SPECIAL_4) - building->setBuildingSubtype(BuildingSubID::EXPERIENCE_VISITING_BONUS); - break; - - case ETownType::TOWER: - building->setBuildingSubtype(BuildingSubID::KNOWLEDGE_VISITING_BONUS); - break; - - case ETownType::STRONGHOLD: - building->setBuildingSubtype(BuildingSubID::ATTACK_VISITING_BONUS); - break; - - case ETownType::INFERNO: - building->setBuildingSubtype(BuildingSubID::SPELL_POWER_VISITING_BONUS); - break; - - case ETownType::FORTRESS: - building->setBuildingSubtype(BuildingSubID::DEFENSE_VISITING_BONUS); - break; - } - } - } - //secondly, supplement bonusing buildings list and active bonuses; subtypes for these objects are already set in update792 - for(auto & kvp : town->buildings) - { - auto & building = kvp.second; - - if(building->subId == BuildingSubID::PORTAL_OF_SUMMONING) - { - if(!hasBuiltInOldWay(ETownType::DUNGEON, BuildingID::PORTAL_OF_SUMMON)) - creatures.resize(GameConstants::CREATURES_PER_TOWN + 1); - continue; - } - if(!building->IsVisitingBonus() && !building->IsWeekBonus()) //it's not bonusing => nothing to handle - continue; - - if(getBonusingBuilding(building->subId) != nullptr) //it's already added => already handled - continue; - - ///'hasBuilt' checking for bonuses is in the onHeroVisit handler - if(building->IsWeekBonus()) - tryAddOnePerWeekBonus(building->subId); - - if(building->IsVisitingBonus()) - tryAddVisitingBonus(building->subId); - } - recreateBuildingsBonuses(); ///Clear all bonuses and recreate -} - -void CGTownInstance::updateTown794() -{ - for(auto builtBuilding : builtBuildings) - { - auto building = town->buildings.at(builtBuilding); - - for(auto overriddenBid : building->overrideBids) - overriddenBuildings.insert(overriddenBid); - } - for(auto & kvp : town->buildings) - { - auto & building = kvp.second; - //The building acts as a visiting bonus and it has not been overridden. - if(building->IsVisitingBonus() && overriddenBuildings.find(kvp.first) == overriddenBuildings.end()) - tryAddVisitingBonus(building->subId); - } - recreateBuildingsBonuses(); -} - bool CGTownInstance::hasBuiltInOldWay(ETownType::ETownType type, BuildingID bid) const { return (this->town->faction != nullptr && this->town->faction->index == type && hasBuilt(bid)); diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index 66c1a48c5..5a0153f9c 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -130,9 +130,7 @@ public: { h & bID; h & indexOnTV; - - if(version >= 792) - h & bType; + h & bType; } protected: @@ -267,16 +265,7 @@ public: return false; }); - if(!h.saving && version < 793) - updateBonusingBuildings(); - - if(version >= 794) - h & overriddenBuildings; - else if(!h.saving) - updateTown794(); - - if(!h.saving && (version >= 794 && version < 801)) - fixBonusingDuplicates(); + h & overriddenBuildings; if(!h.saving) this->setNodeType(CBonusSystemNode::TOWN); @@ -367,7 +356,6 @@ private: void setOwner(const PlayerColor owner) const; void onTownCaptured(const PlayerColor winner) const; int getDwellingBonus(const std::vector& creatureIds, const std::vector >& dwellings) const; - void updateBonusingBuildings(); bool hasBuiltInOldWay(ETownType::ETownType type, BuildingID bid) const; bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const; bool isBonusingBuildingAdded(BuildingID::EBuildingID bid) const; @@ -375,6 +363,4 @@ private: void tryAddVisitingBonus(BuildingSubID::EBuildingSubID subID); void initOverriddenBids(); void addTownBonuses(); - void updateTown794(); //populate overriddenBuildings and vanila bonuses for old saves - void fixBonusingDuplicates(); //For versions 794-800. }; diff --git a/lib/mapObjects/CObjectClassesHandler.cpp b/lib/mapObjects/CObjectClassesHandler.cpp index a2cd7d555..c7944b940 100644 --- a/lib/mapObjects/CObjectClassesHandler.cpp +++ b/lib/mapObjects/CObjectClassesHandler.cpp @@ -1,4 +1,4 @@ -/* +/* * CObjectClassesHandler.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder @@ -195,7 +195,7 @@ void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, cons else handler->init(entry); - if (handler->getTemplates().empty()) + //if (handler->getTemplates().empty()) { auto range = legacyTemplates.equal_range(std::make_pair(obj->id, id)); for (auto & templ : boost::make_iterator_range(range.first, range.second)) @@ -569,14 +569,14 @@ std::vector AObjectTypeHandler::getTemplates() const return templates; } -std::vector AObjectTypeHandler::getTemplates(si32 terrainType) const// FIXME: replace with ETerrainType +std::vector AObjectTypeHandler::getTemplates(const Terrain & terrainType) const { std::vector templates = getTemplates(); std::vector filtered; std::copy_if(templates.begin(), templates.end(), std::back_inserter(filtered), [&](const ObjectTemplate & obj) { - return obj.canBePlacedAt(ETerrainType(terrainType)); + return obj.canBePlacedAt(terrainType); }); // H3 defines allowed terrains in a weird way - artifacts, monsters and resources have faulty masks here // Perhaps we should re-define faulty templates and remove this workaround (already done for resources) @@ -586,7 +586,7 @@ std::vector AObjectTypeHandler::getTemplates(si32 terrainType) c return filtered; } -boost::optional AObjectTypeHandler::getOverride(si32 terrainType, const CGObjectInstance * object) const +boost::optional AObjectTypeHandler::getOverride(const Terrain & terrainType, const CGObjectInstance * object) const { std::vector ret = getTemplates(terrainType); for (auto & tmpl : ret) diff --git a/lib/mapObjects/CObjectClassesHandler.h b/lib/mapObjects/CObjectClassesHandler.h index 89647230a..cc737fc99 100644 --- a/lib/mapObjects/CObjectClassesHandler.h +++ b/lib/mapObjects/CObjectClassesHandler.h @@ -178,11 +178,11 @@ public: /// returns all templates matching parameters std::vector getTemplates() const; - std::vector getTemplates(si32 terrainType) const; + std::vector getTemplates(const Terrain & terrainType) const; /// returns preferred template for this object, if present (e.g. one of 3 possible templates for town - village, fort and castle) /// note that appearance will not be changed - this must be done separately (either by assignment or via pack from server) - boost::optional getOverride(si32 terrainType, const CGObjectInstance * object) const; + boost::optional getOverride(const Terrain & terrainType, const CGObjectInstance * object) const; const RandomMapInfo & getRMGInfo(); @@ -210,19 +210,10 @@ public: h & templates; h & rmgInfo; h & objectName; - if(version >= 759) - { - h & typeName; - h & subTypeName; - } - if(version >= 778) - { - h & sounds; - } - if(version >= 789) - { - h & aiValue; - } + h & typeName; + h & subTypeName; + h & sounds; + h & aiValue; } }; @@ -253,19 +244,10 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase h & handlerName; h & base; h & subObjects; - if(version >= 759) - { - h & identifier; - h & subIds; - } - if(version >= 778) - { - h & sounds; - } - if(version >= 789) - { - h & groupDefaultAiValue; - } + h & identifier; + h & subIds; + h & sounds; + h & groupDefaultAiValue; } }; diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index 411296ea9..7a0f84d64 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -419,7 +419,7 @@ int3 IBoatGenerator::bestLocation() const { if(const TerrainTile *tile = IObjectInterface::cb->getTile(o->pos + offset, false)) //tile is in the map { - if(tile->terType == ETerrainType::WATER && (!tile->blocked || tile->blockingObjects.front()->ID == Obj::BOAT)) //and is water and is not blocked or is blocked by boat + if(tile->terType.isWater() && (!tile->blocked || tile->blockingObjects.front()->ID == Obj::BOAT)) //and is water and is not blocked or is blocked by boat return o->pos + offset; } } diff --git a/lib/mapObjects/CObjectHandler.h b/lib/mapObjects/CObjectHandler.h index 42f46bfe9..2897606cc 100644 --- a/lib/mapObjects/CObjectHandler.h +++ b/lib/mapObjects/CObjectHandler.h @@ -213,13 +213,9 @@ public: ///Entry point of binary (de-)serialization template void serialize(Handler &h, const int version) { - if(version >= 759) - { - h & instanceName; - h & typeName; - h & subTypeName; - } - + h & instanceName; + h & typeName; + h & subTypeName; h & pos; h & ID; h & subID; diff --git a/lib/mapObjects/CQuest.h b/lib/mapObjects/CQuest.h index 0e46d45c1..3fff59254 100644 --- a/lib/mapObjects/CQuest.h +++ b/lib/mapObjects/CQuest.h @@ -87,14 +87,7 @@ public: h & isCustomFirst; h & isCustomNext; h & isCustomComplete; - if(version >= 757) - { - h & completedOption; - } - else if(!h.saving) - { - completedOption = 1; - } + h & completedOption; } void serializeJson(JsonSerializeFormat & handler, const std::string & fieldName); diff --git a/lib/mapObjects/CRewardableObject.h b/lib/mapObjects/CRewardableObject.h index 7dc2b4556..223f6a03f 100644 --- a/lib/mapObjects/CRewardableObject.h +++ b/lib/mapObjects/CRewardableObject.h @@ -269,11 +269,6 @@ public: h & onVisited; h & onEmpty; h & visitMode; - if(version < 778) - { - ui16 soundID = 0; - h & soundID; - } h & selectMode; h & selectedReward; } diff --git a/lib/mapObjects/ObjectTemplate.cpp b/lib/mapObjects/ObjectTemplate.cpp index 5fd9d24a0..deaa1f63a 100644 --- a/lib/mapObjects/ObjectTemplate.cpp +++ b/lib/mapObjects/ObjectTemplate.cpp @@ -19,6 +19,7 @@ #include "CObjectHandler.h" #include "../CModHandler.h" #include "../JsonNode.h" +#include "../Terrain.h" #include "CRewardableConstructor.h" @@ -143,7 +144,17 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser) for (size_t i=0; i<9; i++) { if (terrStr[8-i] == '1') - allowedTerrains.insert(ETerrainType((si32)i)); + allowedTerrains.insert(Terrain::createTerrainTypeH3M(i)); + } + + //assuming that object can be placed on other land terrains + if(allowedTerrains.size() >= 8 && !allowedTerrains.count(Terrain("water"))) + { + for(auto & terrain : Terrain::Manager::terrains()) + { + if(terrain.isLand() && terrain.isPassable()) + allowedTerrains.insert(terrain); + } } id = Obj(boost::lexical_cast(strings[5])); @@ -205,7 +216,17 @@ void ObjectTemplate::readMap(CBinaryReader & reader) for (size_t i=0; i<9; i++) { if (((terrMask >> i) & 1 ) != 0) - allowedTerrains.insert(ETerrainType((si32)i)); + allowedTerrains.insert(Terrain::createTerrainTypeH3M(i)); + } + + //assuming that object can be placed on other land terrains + if(allowedTerrains.size() >= 8 && !allowedTerrains.count(Terrain("water"))) + { + for(auto & terrain : Terrain::Manager::terrains()) + { + if(terrain.isLand() && terrain.isPassable()) + allowedTerrains.insert(terrain); + } } id = Obj(reader.readUInt32()); @@ -247,15 +268,16 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain) if(withTerrain && !node["allowedTerrains"].isNull()) { for (auto & entry : node["allowedTerrains"].Vector()) - allowedTerrains.insert(ETerrainType(vstd::find_pos(GameConstants::TERRAIN_NAMES, entry.String()))); + allowedTerrains.insert(entry.String()); } else { - for (size_t i=0; i< GameConstants::TERRAIN_TYPES; i++) - allowedTerrains.insert(ETerrainType((si32)i)); - - allowedTerrains.erase(ETerrainType::ROCK); - allowedTerrains.erase(ETerrainType::WATER); + for(auto & i : Terrain::Manager::terrains()) + { + if(!i.isPassable() || i.isWater()) + continue; + allowedTerrains.insert(i); + } } if(withTerrain && allowedTerrains.empty()) @@ -329,14 +351,14 @@ void ObjectTemplate::writeJson(JsonNode & node, const bool withTerrain) const if(withTerrain) { //assumed that ROCK and WATER terrains are not included - if(allowedTerrains.size() < (GameConstants::TERRAIN_TYPES - 2)) + if(allowedTerrains.size() < (Terrain::Manager::terrains().size() - 2)) { JsonVector & data = node["allowedTerrains"].Vector(); for(auto type : allowedTerrains) { JsonNode value(JsonNode::JsonType::DATA_STRING); - value.String() = GameConstants::TERRAIN_NAMES[type.num]; + value.String() = type; data.push_back(value); } } @@ -511,7 +533,7 @@ bool ObjectTemplate::isVisitableFromTop() const //return isVisitableFrom (0, 1); } -bool ObjectTemplate::canBePlacedAt(ETerrainType terrain) const +bool ObjectTemplate::canBePlacedAt(Terrain terrain) const { return allowedTerrains.count(terrain) != 0; } diff --git a/lib/mapObjects/ObjectTemplate.h b/lib/mapObjects/ObjectTemplate.h index 723511979..080561f2d 100644 --- a/lib/mapObjects/ObjectTemplate.h +++ b/lib/mapObjects/ObjectTemplate.h @@ -15,6 +15,7 @@ class CBinaryReader; class CLegacyConfigParser; class JsonNode; class int3; +class Terrain; class DLL_LINKAGE ObjectTemplate { @@ -30,7 +31,7 @@ class DLL_LINKAGE ObjectTemplate /// directions from which object can be entered, format same as for moveDir in CGHeroInstance(but 0 - 7) ui8 visitDir; /// list of terrains on which this object can be placed - std::set allowedTerrains; + std::set allowedTerrains; void afterLoadFixup(); @@ -70,7 +71,7 @@ public: bool isVisitableFromTop() const; // Checks if object can be placed on specific terrain - bool canBePlacedAt(ETerrainType terrain) const; + bool canBePlacedAt(Terrain terrain) const; ObjectTemplate(); //custom copy constructor is required @@ -96,10 +97,7 @@ public: h & subid; h & printPriority; h & visitDir; - if(version >= 770) - { - h & editorAnimationFile; - } + h & editorAnimationFile; } }; diff --git a/lib/mapping/CCampaignHandler.cpp b/lib/mapping/CCampaignHandler.cpp index b86e5052d..71c130383 100644 --- a/lib/mapping/CCampaignHandler.cpp +++ b/lib/mapping/CCampaignHandler.cpp @@ -422,17 +422,6 @@ std::vector CCampaignScenario::getLostCrossoverHeroes() return lostCrossoverHeroes; } -std::vector CCampaignScenario::update787(std::vector & heroes) -{ - static_assert(MINIMAL_SERIALIZATION_VERSION < 787, "No longer needed CCampaignScenario::update787"); - std::vector heroesNew; - for(auto hero : heroes) - { - heroesNew.push_back(CCampaignState::crossoverSerialize(hero)); - } - return heroesNew; -} - void CCampaignState::setCurrentMapAsConquered(const std::vector & heroes) { camp->scenarios[*currentMap].crossoverHeroes.clear(); diff --git a/lib/mapping/CCampaignHandler.h b/lib/mapping/CCampaignHandler.h index d977881d9..ea1dd6f7f 100644 --- a/lib/mapping/CCampaignHandler.h +++ b/lib/mapping/CCampaignHandler.h @@ -146,8 +146,7 @@ public: // FIXME: due to usage of JsonNode I can't make these methods const const CGHeroInstance * strongestHero(PlayerColor owner); std::vector getLostCrossoverHeroes(); /// returns a list of crossover heroes which started the scenario, but didn't complete it - std::vector update787(std::vector & heroes); - + CCampaignScenario(); template void serialize(Handler &h, const int formatVersion) @@ -163,19 +162,8 @@ public: h & prolog; h & epilog; h & travelOptions; - if(formatVersion < 787) - { - std::vector crossoverHeroesOld, placedCrossoverHeroesOld; - h & crossoverHeroesOld; - h & placedCrossoverHeroesOld; - crossoverHeroes = update787(crossoverHeroesOld); - placedCrossoverHeroes = update787(placedCrossoverHeroesOld); - } - else - { - h & crossoverHeroes; - h & placedCrossoverHeroes; - } + h & crossoverHeroes; + h & placedCrossoverHeroes; h & keepHeroes; } }; diff --git a/lib/mapping/CDrawRoadsOperation.cpp b/lib/mapping/CDrawRoadsOperation.cpp index b39f8418c..ea7398a4f 100644 --- a/lib/mapping/CDrawRoadsOperation.cpp +++ b/lib/mapping/CDrawRoadsOperation.cpp @@ -148,7 +148,7 @@ static bool ruleIsAny(const std::string & rule) #endif ///CDrawRoadsOperation -CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, ERoadType::ERoadType roadType, CRandomGenerator * gen): +CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, const std::string & roadType, CRandomGenerator * gen): CMapOperation(map),terrainSel(terrainSel), roadType(roadType), gen(gen) { @@ -225,7 +225,7 @@ void CDrawRoadsOperation::flipPattern(RoadPattern& pattern, int flip) const bool CDrawRoadsOperation::needUpdateTile(const TerrainTile & tile) const { - return tile.roadType != ERoadType::NO_ROAD; //TODO: this method should be virtual for river support + return tile.roadType != ROAD_NAMES[0]; //TODO: this method should be virtual for river support } void CDrawRoadsOperation::updateTiles(std::set & invalidated) @@ -263,7 +263,7 @@ bool CDrawRoadsOperation::tileHasSomething(const int3& pos) const { //TODO: this method should be virtual for river support - return map->getTile(pos).roadType != ERoadType::NO_ROAD; + return map->getTile(pos).roadType != ROAD_NAMES[0]; } diff --git a/lib/mapping/CDrawRoadsOperation.h b/lib/mapping/CDrawRoadsOperation.h index de74a0c23..3b7c98ed8 100644 --- a/lib/mapping/CDrawRoadsOperation.h +++ b/lib/mapping/CDrawRoadsOperation.h @@ -18,7 +18,7 @@ struct TerrainTile; class CDrawRoadsOperation : public CMapOperation { public: - CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, ERoadType::ERoadType roadType, CRandomGenerator * gen); + CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, const std::string & roadType, CRandomGenerator * gen); void execute() override; void undo() override; void redo() override; @@ -53,6 +53,6 @@ private: bool tileHasSomething(const int3 & pos) const; CTerrainSelection terrainSel; - ERoadType::ERoadType roadType; + std::string roadType; CRandomGenerator * gen; }; diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index 014745e3c..749a2e5b0 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -123,8 +123,8 @@ CCastleEvent::CCastleEvent() : town(nullptr) } -TerrainTile::TerrainTile() : terType(ETerrainType::BORDER), terView(0), riverType(ERiverType::NO_RIVER), - riverDir(0), roadType(ERoadType::NO_ROAD), roadDir(0), extTileFlags(0), visitable(false), +TerrainTile::TerrainTile() : terType("BORDER"), terView(0), riverType(RIVER_NAMES[0]), + riverDir(0), roadType(ROAD_NAMES[0]), roadDir(0), extTileFlags(0), visitable(false), blocked(false) { @@ -132,13 +132,13 @@ TerrainTile::TerrainTile() : terType(ETerrainType::BORDER), terView(0), riverTyp bool TerrainTile::entrableTerrain(const TerrainTile * from) const { - return entrableTerrain(from ? from->terType != ETerrainType::WATER : true, from ? from->terType == ETerrainType::WATER : true); + return entrableTerrain(from ? from->terType.isLand() : true, from ? from->terType.isWater() : true); } bool TerrainTile::entrableTerrain(bool allowLand, bool allowSea) const { - return terType != ETerrainType::ROCK - && ((allowSea && terType == ETerrainType::WATER) || (allowLand && terType != ETerrainType::WATER)); + return terType.isPassable() + && ((allowSea && terType.isWater()) || (allowLand && terType.isLand())); } bool TerrainTile::isClear(const TerrainTile * from) const @@ -164,7 +164,7 @@ CGObjectInstance * TerrainTile::topVisitableObj(bool excludeTop) const EDiggingStatus TerrainTile::getDiggingStatus(const bool excludeTop) const { - if(terType == ETerrainType::WATER || terType == ETerrainType::ROCK) + if(terType.isWater() || !terType.isPassable()) return EDiggingStatus::WRONG_TERRAIN; int allowedBlocked = excludeTop ? 1 : 0; @@ -181,7 +181,7 @@ bool TerrainTile::hasFavorableWinds() const bool TerrainTile::isWater() const { - return terType == ETerrainType::WATER; + return terType.isWater(); } void CMapHeader::setupEvents() diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 7ff0fc5de..c13bb4e93 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -110,11 +110,7 @@ struct DLL_LINKAGE PlayerInfo h & posOfMainTown; h & team; h & generateHero; - - if(version >= 770) - { - h & mainHeroInstance; - } + h & mainHeroInstance; } }; @@ -164,16 +160,9 @@ struct DLL_LINKAGE EventCondition h & objectType; h & position; h & condition; - //(!!!) should be `version >= 759` here, but do not try to "fix" it - if(version > 759) - { - h & objectSubtype; - h & objectInstanceName; - } - if(version >= 770) - { - h & metaType; - } + h & objectSubtype; + h & objectInstanceName; + h & metaType; } }; @@ -495,9 +484,6 @@ public: h & CGTownInstance::merchantArtifacts; h & CGTownInstance::universitySkills; - if(formatVersion >= 759) - { - h & instanceNames; - } + h & instanceNames; } }; diff --git a/lib/mapping/CMapDefines.h b/lib/mapping/CMapDefines.h index 9514dcbb6..3016f968f 100644 --- a/lib/mapping/CMapDefines.h +++ b/lib/mapping/CMapDefines.h @@ -80,11 +80,11 @@ struct DLL_LINKAGE TerrainTile EDiggingStatus getDiggingStatus(const bool excludeTop = true) const; bool hasFavorableWinds() const; - ETerrainType terType; + Terrain terType; ui8 terView; - ERiverType::ERiverType riverType; + std::string riverType; ui8 riverDir; - ERoadType::ERoadType roadType; + std::string 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.cpp b/lib/mapping/CMapEditManager.cpp index fd632fb87..d23b1100d 100644 --- a/lib/mapping/CMapEditManager.cpp +++ b/lib/mapping/CMapEditManager.cpp @@ -243,13 +243,13 @@ void CMapEditManager::clearTerrain(CRandomGenerator * gen) execute(make_unique(map, gen ? gen : &(this->gen))); } -void CMapEditManager::drawTerrain(ETerrainType terType, CRandomGenerator * gen) +void CMapEditManager::drawTerrain(Terrain terType, CRandomGenerator * gen) { execute(make_unique(map, terrainSel, terType, gen ? gen : &(this->gen))); terrainSel.clearSelection(); } -void CMapEditManager::drawRoad(ERoadType::ERoadType roadType, CRandomGenerator* gen) +void CMapEditManager::drawRoad(const std::string & roadType, CRandomGenerator* gen) { execute(make_unique(map, terrainSel, roadType, gen ? gen : &(this->gen))); terrainSel.clearSelection(); @@ -534,7 +534,7 @@ void CTerrainViewPatternConfig::flipPattern(TerrainViewPattern & pattern, int fl } -CDrawTerrainOperation::CDrawTerrainOperation(CMap * map, const CTerrainSelection & terrainSel, ETerrainType terType, CRandomGenerator * gen) +CDrawTerrainOperation::CDrawTerrainOperation(CMap * map, const CTerrainSelection & terrainSel, Terrain terType, CRandomGenerator * gen) : CMapOperation(map), terrainSel(terrainSel), terType(terType), gen(gen) { @@ -760,21 +760,17 @@ void CDrawTerrainOperation::updateTerrainViews() } } -ETerrainGroup::ETerrainGroup CDrawTerrainOperation::getTerrainGroup(ETerrainType terType) const +ETerrainGroup::ETerrainGroup CDrawTerrainOperation::getTerrainGroup(Terrain terType) const { - switch(terType) - { - case ETerrainType::DIRT: + if(terType == Terrain("dirt")) return ETerrainGroup::DIRT; - case ETerrainType::SAND: + if(terType == Terrain("sand")) return ETerrainGroup::SAND; - case ETerrainType::WATER: + if(terType.isWater()) return ETerrainGroup::WATER; - case ETerrainType::ROCK: + if(!terType.isPassable()) return ETerrainGroup::ROCK; - default: - return ETerrainGroup::NORMAL; - } + return ETerrainGroup::NORMAL; } CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainView(const int3 & pos, const std::vector * pattern, int recDepth) const @@ -811,7 +807,7 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi int cy = pos.y + (i / 3) - 1; int3 currentPos(cx, cy, pos.z); bool isAlien = false; - ETerrainType terType; + Terrain terType; if(!map->isInTheMap(currentPos)) { // position is not in the map, so take the ter type from the neighbor tile @@ -949,17 +945,11 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi } } -bool CDrawTerrainOperation::isSandType(ETerrainType terType) const +bool CDrawTerrainOperation::isSandType(Terrain terType) const { - switch(terType) - { - case ETerrainType::WATER: - case ETerrainType::SAND: - case ETerrainType::ROCK: + if(terType.isWater() || terType == Terrain("sand") || !terType.isPassable()) return true; - default: - return false; - } + return false; } void CDrawTerrainOperation::invalidateTerrainViews(const int3 & centerPos) @@ -986,7 +976,7 @@ CDrawTerrainOperation::InvalidTiles CDrawTerrainOperation::getInvalidTiles(const auto valid = validateTerrainView(pos, ptrConfig->getTerrainTypePatternById("n1")).result; // Special validity check for rock & water - if(valid && (terType == ETerrainType::WATER || terType == ETerrainType::ROCK)) + if(valid && (terType.isWater() || !terType.isPassable())) { static const std::string patternIds[] = { "s1", "s2" }; for(auto & patternId : patternIds) @@ -996,7 +986,7 @@ CDrawTerrainOperation::InvalidTiles CDrawTerrainOperation::getInvalidTiles(const } } // Additional validity check for non rock OR water - else if(!valid && (terType != ETerrainType::WATER && terType != ETerrainType::ROCK)) + else if(!valid && (terType.isLand() && terType.isPassable())) { static const std::string patternIds[] = { "n2", "n3" }; for(auto & patternId : patternIds) @@ -1040,7 +1030,7 @@ void CTerrainViewPatternUtils::printDebuggingInfoAboutTile(const CMap * map, int { auto debugTile = map->getTile(debugPos); - std::string terType = debugTile.terType.toString().substr(0, 6); + std::string terType = static_cast(debugTile.terType).substr(0, 6); line += terType; line.insert(line.end(), PADDED_LENGTH - terType.size(), ' '); } @@ -1059,12 +1049,12 @@ CClearTerrainOperation::CClearTerrainOperation(CMap * map, CRandomGenerator * ge { CTerrainSelection terrainSel(map); terrainSel.selectRange(MapRect(int3(0, 0, 0), map->width, map->height)); - addOperation(make_unique(map, terrainSel, ETerrainType::WATER, gen)); + addOperation(make_unique(map, terrainSel, Terrain("water"), gen)); if(map->twoLevel) { terrainSel.clearSelection(); terrainSel.selectRange(MapRect(int3(0, 0, 1), map->width, map->height)); - addOperation(make_unique(map, terrainSel, ETerrainType::ROCK, gen)); + addOperation(make_unique(map, terrainSel, Terrain("rock"), gen)); } } diff --git a/lib/mapping/CMapEditManager.h b/lib/mapping/CMapEditManager.h index 96cad1557..5e18cf969 100644 --- a/lib/mapping/CMapEditManager.h +++ b/lib/mapping/CMapEditManager.h @@ -13,6 +13,7 @@ #include "../CRandomGenerator.h" #include "../int3.h" #include "../GameConstants.h" +#include "Terrain.h" class CGObjectInstance; class CTerrainViewPatternConfig; @@ -168,10 +169,10 @@ public: void clearTerrain(CRandomGenerator * gen = nullptr); /// Draws terrain at the current terrain selection. The selection will be cleared automatically. - void drawTerrain(ETerrainType terType, CRandomGenerator * gen = nullptr); + void drawTerrain(Terrain terType, CRandomGenerator * gen = nullptr); /// Draws roads at the current terrain selection. The selection will be cleared automatically. - void drawRoad(ERoadType::ERoadType roadType, CRandomGenerator * gen = nullptr); + void drawRoad(const std::string & roadType, CRandomGenerator * gen = nullptr); void insertObject(CGObjectInstance * obj); @@ -353,7 +354,7 @@ private: class CDrawTerrainOperation : public CMapOperation { public: - CDrawTerrainOperation(CMap * map, const CTerrainSelection & terrainSel, ETerrainType terType, CRandomGenerator * gen); + CDrawTerrainOperation(CMap * map, const CTerrainSelection & terrainSel, Terrain terType, CRandomGenerator * gen); void execute() override; void undo() override; @@ -384,16 +385,16 @@ private: InvalidTiles getInvalidTiles(const int3 & centerPos) const; void updateTerrainViews(); - ETerrainGroup::ETerrainGroup getTerrainGroup(ETerrainType terType) const; + ETerrainGroup::ETerrainGroup getTerrainGroup(Terrain terType) const; /// Validates the terrain view of the given position and with the given pattern. The first method wraps the /// second method to validate the terrain view with the given pattern in all four flip directions(horizontal, vertical). ValidationResult validateTerrainView(const int3 & pos, const std::vector * pattern, int recDepth = 0) const; ValidationResult validateTerrainViewInner(const int3 & pos, const TerrainViewPattern & pattern, int recDepth = 0) const; /// Tests whether the given terrain type is a sand type. Sand types are: Water, Sand and Rock - bool isSandType(ETerrainType terType) const; + bool isSandType(Terrain terType) const; CTerrainSelection terrainSel; - ETerrainType terType; + Terrain terType; CRandomGenerator * gen; std::set invalidatedTerViews; }; diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 09cdb5ef7..9030e9737 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -935,14 +935,14 @@ void CMapLoaderH3M::readTerrain() for(int z = 0; z < map->height; z++) { auto & tile = map->getTile(int3(z, c, a)); - tile.terType = ETerrainType(reader.readUInt8()); + tile.terType = Terrain::createTerrainTypeH3M(reader.readUInt8()); tile.terView = reader.readUInt8(); - tile.riverType = static_cast(reader.readUInt8()); + tile.riverType = RIVER_NAMES[reader.readUInt8()]; tile.riverDir = reader.readUInt8(); - tile.roadType = static_cast(reader.readUInt8()); + tile.roadType = ROAD_NAMES[reader.readUInt8()]; tile.roadDir = reader.readUInt8(); tile.extTileFlags = reader.readUInt8(); - tile.blocked = ((tile.terType == ETerrainType::ROCK || tile.terType == ETerrainType::BORDER ) ? true : false); //underground tiles are always blocked + tile.blocked = ((!tile.terType.isPassable() || tile.terType == Terrain("BORDER") ) ? true : false); //underground tiles are always blocked tile.visitable = 0; } } diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index 6d956f42b..ff08ba2c5 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -323,20 +323,6 @@ namespace TriggeredEventsDetail namespace TerrainDetail { - static const std::array terrainCodes = - { - "dt", "sa", "gr", "sn", "sw", "rg", "sb", "lv", "wt", "rc" - }; - static const std::array roadCodes = - { - "", "pd", "pg", "pc" - }; - - static const std::array riverCodes = - { - "", "rw", "ri", "rm", "rl" - }; - static const std::array flipCodes = { '_', '-', '|', '+' @@ -959,13 +945,7 @@ void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile using namespace TerrainDetail; {//terrain type const std::string typeCode = src.substr(0, 2); - - int rawType = vstd::find_pos(terrainCodes, typeCode); - - if(rawType < 0) - throw std::runtime_error("Invalid terrain type code in "+src); - - tile.terType = ETerrainType(rawType); + tile.terType = Terrain::createTerrainByCode(typeCode); } int startPos = 2; //0+typeCode fixed length {//terrain view @@ -992,20 +972,18 @@ void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile {//road type const std::string typeCode = src.substr(startPos, 2); startPos+=2; - int rawType = vstd::find_pos(roadCodes, typeCode); - if(rawType < 0) + if(vstd::find_pos(ROAD_NAMES, typeCode) < 0) { - rawType = vstd::find_pos(riverCodes, typeCode); - if(rawType < 0) + if(vstd::find_pos(RIVER_NAMES, typeCode) < 0) throw std::runtime_error("Invalid river type in "+src); else { - tile.riverType = ERiverType::ERiverType(rawType); + tile.riverType = typeCode; hasRoad = false; } } else - tile.roadType = ERoadType::ERoadType(rawType); + tile.roadType = typeCode; } if(hasRoad) {//road dir @@ -1033,10 +1011,9 @@ void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile {//river type const std::string typeCode = src.substr(startPos, 2); startPos+=2; - int rawType = vstd::find_pos(riverCodes, typeCode); - if(rawType < 0) + if(vstd::find_pos(RIVER_NAMES, typeCode) < 0) throw std::runtime_error("Invalid river type in "+src); - tile.riverType = ERiverType::ERiverType(rawType); + tile.riverType = typeCode; } {//river dir int pos = startPos; @@ -1298,13 +1275,13 @@ std::string CMapSaverJson::writeTerrainTile(const TerrainTile & tile) out.setf(std::ios::dec, std::ios::basefield); out.unsetf(std::ios::showbase); - out << terrainCodes.at(int(tile.terType)) << (int)tile.terView << flipCodes[tile.extTileFlags % 4]; + out << Terrain::Manager::getInfo(tile.terType).typeCode << (int)tile.terView << flipCodes[tile.extTileFlags % 4]; - if(tile.roadType != ERoadType::NO_ROAD) - out << roadCodes.at(int(tile.roadType)) << (int)tile.roadDir << flipCodes[(tile.extTileFlags >> 4) % 4]; + if(tile.roadType != ROAD_NAMES[0]) + out << tile.roadType << (int)tile.roadDir << flipCodes[(tile.extTileFlags >> 4) % 4]; - if(tile.riverType != ERiverType::NO_RIVER) - out << riverCodes.at(int(tile.riverType)) << (int)tile.riverDir << flipCodes[(tile.extTileFlags >> 2) % 4]; + if(tile.riverType != RIVER_NAMES[0]) + out << tile.riverType << (int)tile.riverDir << flipCodes[(tile.extTileFlags >> 2) % 4]; return out.str(); } diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index f8440c651..5736f8855 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -69,17 +69,6 @@ CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed) : void CMapGenerator::loadConfig() { - static std::map terrainMap - { - {"dirt", ETerrainType::DIRT}, - {"sand", ETerrainType::SAND}, - {"grass", ETerrainType::GRASS}, - {"snow", ETerrainType::SNOW}, - {"swamp", ETerrainType::SWAMP}, - {"subterranean", ETerrainType::SUBTERRANEAN}, - {"lava", ETerrainType::LAVA}, - {"rough", ETerrainType::ROUGH} - }; static const std::map resMap { {"wood", Res::ERes::WOOD}, @@ -90,23 +79,17 @@ void CMapGenerator::loadConfig() {"sulfur", Res::ERes::SULFUR}, {"gold", Res::ERes::GOLD}, }; - static std::map roadTypeMap - { - {"dirt_road", ERoadType::DIRT_ROAD}, - {"gravel_road", ERoadType::GRAVEL_ROAD}, - {"cobblestone_road", ERoadType::COBBLESTONE_ROAD} - }; static const ResourceID path("config/randomMap.json"); JsonNode randomMapJson(path); for(auto& s : randomMapJson["terrain"]["undergroundAllow"].Vector()) { if(!s.isNull()) - config.terrainUndergroundAllowed.push_back(terrainMap[s.String()]); + config.terrainUndergroundAllowed.emplace_back(s.String()); } for(auto& s : randomMapJson["terrain"]["groundProhibit"].Vector()) { if(!s.isNull()) - config.terrainGroundProhibit.push_back(terrainMap[s.String()]); + config.terrainGroundProhibit.emplace_back(s.String()); } config.shipyardGuard = randomMapJson["waterZone"]["shipyard"]["value"].Integer(); for(auto & treasure : randomMapJson["waterZone"]["treasure"].Vector()) @@ -119,7 +102,7 @@ void CMapGenerator::loadConfig() } config.mineExtraResources = randomMapJson["mines"]["extraResourcesLimit"].Integer(); config.minGuardStrength = randomMapJson["minGuardStrength"].Integer(); - config.defaultRoadType = roadTypeMap[randomMapJson["defaultRoadType"].String()]; + config.defaultRoadType = randomMapJson["defaultRoadType"].String(); config.treasureValueLimit = randomMapJson["treasureValueLimit"].Integer(); for(auto & i : randomMapJson["prisons"]["experience"].Vector()) config.prisonExperience.push_back(i.Integer()); @@ -357,7 +340,7 @@ void CMapGenerator::genZones() { getEditManager()->clearTerrain(&rand); getEditManager()->getTerrainSelection().selectRange(MapRect(int3(0, 0, 0), mapGenOptions.getWidth(), mapGenOptions.getHeight())); - getEditManager()->drawTerrain(ETerrainType::GRASS, &rand); + getEditManager()->drawTerrain(Terrain("grass"), &rand); auto tmpl = mapGenOptions.getMapTemplate(); zones.clear(); @@ -530,14 +513,14 @@ void CMapGenerator::fillZones() void CMapGenerator::createObstaclesCommon1() { - if (map->twoLevel) //underground + if(map->twoLevel) //underground { //negative approach - create rock tiles first, then make sure all accessible tiles have no rock std::vector rockTiles; - for (int x = 0; x < map->width; x++) + for(int x = 0; x < map->width; x++) { - for (int y = 0; y < map->height; y++) + for(int y = 0; y < map->height; y++) { int3 tile(x, y, 1); if (shouldBeBlocked(tile)) @@ -547,21 +530,29 @@ void CMapGenerator::createObstaclesCommon1() } } getEditManager()->getTerrainSelection().setSelection(rockTiles); - getEditManager()->drawTerrain(ETerrainType::ROCK, &rand); + + //collect all rock terrain types + std::vector rockTerrains; + for(auto & terrain : Terrain::Manager::terrains()) + if(!terrain.isPassable()) + rockTerrains.push_back(terrain); + auto rockTerrain = *RandomGeneratorUtil::nextItem(rockTerrains, rand); + + getEditManager()->drawTerrain(rockTerrain, &rand); } } void CMapGenerator::createObstaclesCommon2() { - if (map->twoLevel) + if(map->twoLevel) { //finally mark rock tiles as occupied, spawn no obstacles there - for (int x = 0; x < map->width; x++) + for(int x = 0; x < map->width; x++) { - for (int y = 0; y < map->height; y++) + for(int y = 0; y < map->height; y++) { int3 tile(x, y, 1); - if (map->getTile(tile).terType == ETerrainType::ROCK) + if(!map->getTile(tile).terType.isPassable()) { setOccupied(tile, ETileType::USED); } @@ -904,7 +895,7 @@ void CMapGenerator::setOccupied(const int3 &tile, ETileType::ETileType state) tiles[tile.x][tile.y][tile.z].setOccupied(state); } -void CMapGenerator::setRoad(const int3& tile, ERoadType::ERoadType roadType) +void CMapGenerator::setRoad(const int3& tile, const std::string & roadType) { checkIsOnMap(tile); diff --git a/lib/rmg/CMapGenerator.h b/lib/rmg/CMapGenerator.h index 3469af69f..f0e67c81b 100644 --- a/lib/rmg/CMapGenerator.h +++ b/lib/rmg/CMapGenerator.h @@ -26,8 +26,6 @@ class JsonNode; class CMapGenerator; class CTileInfo; -//#define _BETA - typedef std::vector JsonVector; class rmgException : public std::exception @@ -54,14 +52,14 @@ class DLL_LINKAGE CMapGenerator public: struct Config { - std::vector terrainUndergroundAllowed; - std::vector terrainGroundProhibit; + std::vector terrainUndergroundAllowed; + std::vector terrainGroundProhibit; std::vector waterTreasure; int shipyardGuard; int mineExtraResources; std::map mineValues; int minGuardStrength; - ERoadType::ERoadType defaultRoadType; + std::string defaultRoadType; int treasureValueLimit; std::vector prisonExperience, prisonValues; std::vector scrollValues; @@ -101,7 +99,7 @@ public: bool isRoad(const int3 &tile) const; void setOccupied(const int3 &tile, ETileType::ETileType state); - void setRoad(const int3 &tile, ERoadType::ERoadType roadType); + void setRoad(const int3 &tile, const std::string & roadType); CTileInfo getTile(const int3 & tile) const; bool isAllowedSpell(SpellID sid) const; diff --git a/lib/rmg/CRmgTemplate.cpp b/lib/rmg/CRmgTemplate.cpp index 35ce71dc7..5dcf46732 100644 --- a/lib/rmg/CRmgTemplate.cpp +++ b/lib/rmg/CRmgTemplate.cpp @@ -16,6 +16,7 @@ #include "../mapping/CMap.h" #include "../VCMI_Lib.h" #include "../CTownHandler.h" +#include "../Terrain.h" #include "../serializer/JsonSerializeFormat.h" #include "../StringConstants.h" @@ -66,12 +67,12 @@ class TerrainEncoder public: static si32 decode(const std::string & identifier) { - return vstd::find_pos(GameConstants::TERRAIN_NAMES, identifier); + return vstd::find_pos(Terrain::Manager::terrains(), identifier); } static std::string encode(const si32 index) { - return (index >=0 && index < GameConstants::TERRAIN_TYPES) ? GameConstants::TERRAIN_NAMES[index] : ""; + return (index >=0 && index < Terrain::Manager::terrains().size()) ? static_cast(Terrain::Manager::terrains()[index]) : ""; } }; @@ -89,18 +90,6 @@ public: } }; -const std::set ZoneOptions::DEFAULT_TERRAIN_TYPES = -{ - ETerrainType::DIRT, - ETerrainType::SAND, - ETerrainType::GRASS, - ETerrainType::SNOW, - ETerrainType::SWAMP, - ETerrainType::ROUGH, - ETerrainType::SUBTERRANEAN, - ETerrainType::LAVA -}; - const TRmgTemplateZoneId ZoneOptions::NO_ZONE = -1; ZoneOptions::CTownInfo::CTownInfo() @@ -149,7 +138,6 @@ ZoneOptions::ZoneOptions() playerTowns(), neutralTowns(), matchTerrainToTown(true), - terrainTypes(DEFAULT_TERRAIN_TYPES), townsAreSameType(false), townTypes(), monsterTypes(), @@ -161,7 +149,9 @@ ZoneOptions::ZoneOptions() terrainTypeLikeZone(NO_ZONE), treasureLikeZone(NO_ZONE) { - + for(auto & terr : Terrain::Manager::terrains()) + if(terr.isLand() && terr.isPassable()) + terrainTypes.insert(terr); } ZoneOptions & ZoneOptions::operator=(const ZoneOptions & other) @@ -224,15 +214,15 @@ boost::optional ZoneOptions::getOwner() const return owner; } -const std::set & ZoneOptions::getTerrainTypes() const +const std::set & ZoneOptions::getTerrainTypes() const { return terrainTypes; } -void ZoneOptions::setTerrainTypes(const std::set & value) +void ZoneOptions::setTerrainTypes(const std::set & value) { - assert(value.find(ETerrainType::WRONG) == value.end() && value.find(ETerrainType::BORDER) == value.end() && - value.find(ETerrainType::WATER) == value.end() && value.find(ETerrainType::ROCK) == value.end()); + //assert(value.find(ETerrainType::WRONG) == value.end() && value.find(ETerrainType::BORDER) == value.end() && + // value.find(ETerrainType::WATER) == value.end() && value.find(ETerrainType::ROCK) == value.end()); terrainTypes = value; } @@ -339,7 +329,31 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler) #undef SERIALIZE_ZONE_LINK if(terrainTypeLikeZone == NO_ZONE) - handler.serializeIdArray("terrainTypes", terrainTypes, DEFAULT_TERRAIN_TYPES); + { + JsonNode node; + if(handler.saving) + { + node.setType(JsonNode::JsonType::DATA_VECTOR); + for(auto & ttype : terrainTypes) + { + JsonNode n; + n.String() = ttype; + node.Vector().push_back(n); + } + } + handler.serializeRaw("terrainTypes", node, boost::none); + if(!handler.saving) + { + if(!node.Vector().empty()) + { + terrainTypes.clear(); + for(auto ttype : node.Vector()) + { + terrainTypes.emplace(ttype.String()); + } + } + } + } handler.serializeBool("townsAreSameType", townsAreSameType, false); handler.serializeIdArray("allowedMonsters", monsterTypes, VLC->townh->getAllowedFactions(false)); diff --git a/lib/rmg/CRmgTemplate.h b/lib/rmg/CRmgTemplate.h index 0053e57c8..24bfe6ecb 100644 --- a/lib/rmg/CRmgTemplate.h +++ b/lib/rmg/CRmgTemplate.h @@ -13,9 +13,11 @@ #include "../int3.h" #include "../GameConstants.h" #include "../ResourceSet.h" +#include "../Terrain.h" #include "CMapGenOptions.h" class JsonSerializeFormat; +class Terrain; namespace ETemplateZoneType { @@ -65,7 +67,6 @@ private: class DLL_LINKAGE ZoneOptions { public: - static const std::set DEFAULT_TERRAIN_TYPES; static const TRmgTemplateZoneId NO_ZONE; class DLL_LINKAGE CTownInfo @@ -101,8 +102,8 @@ public: void setSize(int value); boost::optional getOwner() const; - const std::set & getTerrainTypes() const; - void setTerrainTypes(const std::set & value); + const std::set & getTerrainTypes() const; + void setTerrainTypes(const std::set & value); std::set getDefaultTownTypes() const; const std::set & getTownTypes() const; @@ -134,7 +135,7 @@ protected: CTownInfo playerTowns; CTownInfo neutralTowns; bool matchTerrainToTown; - std::set terrainTypes; + std::set terrainTypes; bool townsAreSameType; std::set townTypes; diff --git a/lib/rmg/CRmgTemplateZone.cpp b/lib/rmg/CRmgTemplateZone.cpp index f359ed28a..2600fdc55 100644 --- a/lib/rmg/CRmgTemplateZone.cpp +++ b/lib/rmg/CRmgTemplateZone.cpp @@ -36,7 +36,7 @@ void CRmgTemplateZone::addRoadNode(const int3& node) roadNodes.insert(node); } -CTileInfo::CTileInfo():nearestObjectDistance(float(INT_MAX)), terrain(ETerrainType::WRONG),roadType(ERoadType::NO_ROAD) +CTileInfo::CTileInfo():nearestObjectDistance(float(INT_MAX)), terrain() { occupied = ETileType::POSSIBLE; //all tiles are initially possible to place objects or passages } @@ -69,7 +69,7 @@ bool CTileInfo::isFree() const bool CTileInfo::isRoad() const { - return roadType != ERoadType::NO_ROAD; + return roadType != ROAD_NAMES[0]; } bool CTileInfo::isUsed() const @@ -86,17 +86,17 @@ ETileType::ETileType CTileInfo::getTileType() const return occupied; } -ETerrainType CTileInfo::getTerrainType() const +Terrain CTileInfo::getTerrainType() const { return terrain; } -void CTileInfo::setTerrainType(ETerrainType value) +void CTileInfo::setTerrainType(Terrain value) { terrain = value; } -void CTileInfo::setRoadType(ERoadType::ERoadType value) +void CTileInfo::setRoadType(const std::string & value) { roadType = value; // setOccupied(ETileType::FREE); @@ -106,7 +106,7 @@ void CTileInfo::setRoadType(ERoadType::ERoadType value) CRmgTemplateZone::CRmgTemplateZone(CMapGenerator * Gen) : ZoneOptions(), townType(ETownType::NEUTRAL), - terrainType (ETerrainType::GRASS), + terrainType (Terrain("grass")), minGuardedValue(0), questArtZone(), gen(Gen) @@ -989,7 +989,7 @@ bool CRmgTemplateZone::createRoad(const int3& src, const int3& dst) std::map cameFrom; // The map of navigated nodes. std::map distances; - gen->setRoad (src, ERoadType::NO_ROAD); //just in case zone guard already has road under it. Road under nodes will be added at very end + gen->setRoad (src, ROAD_NAMES[0]); //just in case zone guard already has road under it. Road under nodes will be added at very end cameFrom[src] = int3(-1, -1, -1); //first node points to finish condition pq.push(std::make_pair(src, 0.f)); @@ -1012,8 +1012,8 @@ bool CRmgTemplateZone::createRoad(const int3& src, const int3& dst) while (cameFrom[backTracking].valid()) { // add node to path - roads.insert (backTracking); - gen->setRoad (backTracking, ERoadType::COBBLESTONE_ROAD); + roads.insert(backTracking); + gen->setRoad(backTracking, gen->getConfig().defaultRoadType); //logGlobal->trace("Setting road at tile %s", backTracking); // do the same for the predecessor backTracking = cameFrom[backTracking]; @@ -1250,6 +1250,17 @@ void CRmgTemplateZone::addToConnectLater(const int3& src) tilesToConnectLater.insert(src); } +int CRmgTemplateZone::chooseRandomAppearance(si32 ObjID) const +{ + auto factories = VLC->objtypeh->knownSubObjects(ObjID); + vstd::erase_if(factories, [this, ObjID](si32 f) + { + return VLC->objtypeh->getHandlerFor(ObjID, f)->getTemplates(terrainType).empty(); + }); + + return *RandomGeneratorUtil::nextItem(factories, gen->rand); +} + bool CRmgTemplateZone::addMonster(int3 &pos, si32 strength, bool clearSurroundingTiles, bool zoneGuard) { //precalculate actual (randomized) monster strength based on this post @@ -1731,33 +1742,51 @@ void CRmgTemplateZone::initTerrainType () { if (type==ETemplateZoneType::WATER) { - terrainType = ETerrainType::WATER; + //collect all water terrain types + std::vector waterTerrains; + for(auto & terrain : Terrain::Manager::terrains()) + if(terrain.isWater()) + waterTerrains.push_back(terrain); + + terrainType = *RandomGeneratorUtil::nextItem(waterTerrains, gen->rand); } else { if (matchTerrainToTown && townType != ETownType::NEUTRAL) + { terrainType = (*VLC->townh)[townType]->nativeTerrain; + } else + { terrainType = *RandomGeneratorUtil::nextItem(terrainTypes, gen->rand); + } //TODO: allow new types of terrain? { if(isUnderground()) { if(!vstd::contains(gen->getConfig().terrainUndergroundAllowed, terrainType)) - terrainType = ETerrainType::SUBTERRANEAN; + { + //collect all underground terrain types + std::vector undegroundTerrains; + for(auto & terrain : Terrain::Manager::terrains()) + if(terrain.isUnderground()) + undegroundTerrains.push_back(terrain); + + terrainType = *RandomGeneratorUtil::nextItem(undegroundTerrains, gen->rand); + } } else { - if(vstd::contains(gen->getConfig().terrainGroundProhibit, terrainType)) - terrainType = ETerrainType::DIRT; + if(vstd::contains(gen->getConfig().terrainGroundProhibit, terrainType) || terrainType.isUnderground()) + terrainType = Terrain("dirt"); } } } paintZoneTerrain (terrainType); } -void CRmgTemplateZone::paintZoneTerrain (ETerrainType terrainType) +void CRmgTemplateZone::paintZoneTerrain (Terrain terrainType) { std::vector tiles(tileinfo.begin(), tileinfo.end()); gen->getEditManager()->getTerrainSelection().setSelection(tiles); @@ -1837,6 +1866,9 @@ bool CRmgTemplateZone::createRequiredObjects() for(const auto &object : requiredObjects) { auto obj = object.first; + if (!obj->appearance.canBePlacedAt(terrainType)) + continue; + int3 pos; while (true) { @@ -1845,6 +1877,7 @@ bool CRmgTemplateZone::createRequiredObjects() logGlobal->error("Failed to fill zone %d due to lack of space", id); return false; } + if (tryToPlaceObjectAndConnectToPath(obj, pos) == EObjectPlacingResult::SUCCESS) { //paths to required objects constitute main paths of zone. otherwise they just may lead to middle and create dead zones @@ -1858,6 +1891,9 @@ bool CRmgTemplateZone::createRequiredObjects() for (const auto &obj : closeObjects) { setTemplateForObject(obj.first); + if (!obj.first->appearance.canBePlacedAt(terrainType)) + continue; + auto tilesBlockedByObject = obj.first->getBlockedOffsets(); bool finished = false; @@ -2069,8 +2105,8 @@ int3 CRmgTemplateZone::createShipyard(const std::set & lake, si32 guardStr bool CRmgTemplateZone::createShipyard(const int3 & position, si32 guardStrength) { - auto subObjects = VLC->objtypeh->knownSubObjects(Obj::SHIPYARD); - auto shipyard = (CGShipyard*) VLC->objtypeh->getHandlerFor(Obj::SHIPYARD, *RandomGeneratorUtil::nextItem(subObjects, gen->rand))->create(ObjectTemplate()); + int subtype = chooseRandomAppearance(Obj::SHIPYARD); + auto shipyard = (CGShipyard*) VLC->objtypeh->getHandlerFor(Obj::SHIPYARD, subtype)->create(ObjectTemplate()); shipyard->tempOwner = PlayerColor::NEUTRAL; setTemplateForObject(shipyard); @@ -2569,7 +2605,8 @@ void CRmgTemplateZone::checkAndPlaceObject(CGObjectInstance* object, const int3 if (object->appearance.id == Obj::NO_OBJ) { auto terrainType = gen->map->getTile(pos).terType; - auto templates = VLC->objtypeh->getHandlerFor(object->ID, object->subID)->getTemplates(terrainType); + auto h = VLC->objtypeh->getHandlerFor(object->ID, object->subID); + auto templates = h->getTemplates(terrainType); if (templates.empty()) throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") % object->ID % object->subID % pos.toString() % terrainType)); @@ -3239,7 +3276,7 @@ void CRmgTemplateZone::addAllPossibleObjects() if (!creaturesAmount) continue; - int randomAppearance = *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::SEER_HUT), gen->rand); + int randomAppearance = chooseRandomAppearance(Obj::SEER_HUT); oi.generateObject = [creature, creaturesAmount, randomAppearance, this, generateArtInfo]() -> CGObjectInstance * { @@ -3270,7 +3307,7 @@ void CRmgTemplateZone::addAllPossibleObjects() static int seerLevels = std::min(gen->getConfig().questValues.size(), gen->getConfig().questRewardValues.size()); for(int i = 0; i < seerLevels; i++) //seems that code for exp and gold reward is similiar { - int randomAppearance = *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::SEER_HUT), gen->rand); + int randomAppearance = chooseRandomAppearance(Obj::SEER_HUT); oi.setTemplate(Obj::SEER_HUT, randomAppearance, terrainType); oi.value = gen->getConfig().questValues[i]; @@ -3332,7 +3369,7 @@ ObjectInfo::ObjectInfo() } -void ObjectInfo::setTemplate (si32 type, si32 subtype, ETerrainType terrainType) +void ObjectInfo::setTemplate (si32 type, si32 subtype, Terrain terrainType) { auto templHandler = VLC->objtypeh->getHandlerFor(type, subtype); if(!templHandler) diff --git a/lib/rmg/CRmgTemplateZone.h b/lib/rmg/CRmgTemplateZone.h index 83a7778c2..24f006c9a 100644 --- a/lib/rmg/CRmgTemplateZone.h +++ b/lib/rmg/CRmgTemplateZone.h @@ -17,6 +17,7 @@ #include "CRmgTemplate.h" #include "../mapObjects/ObjectTemplate.h" #include //A* +#include "Terrain.h" class CMapGenerator; class CTileInfo; @@ -48,16 +49,16 @@ public: bool isUsed() const; bool isRoad() const; void setOccupied(ETileType::ETileType value); - ETerrainType getTerrainType() const; + Terrain getTerrainType() const; ETileType::ETileType getTileType() const; - void setTerrainType(ETerrainType value); + void setTerrainType(Terrain value); - void setRoadType(ERoadType::ERoadType value); + void setRoadType(const std::string & value); private: float nearestObjectDistance; ETileType::ETileType occupied; - ETerrainType terrain; - ERoadType::ERoadType roadType; + Terrain terrain; + std::string roadType; }; struct DLL_LINKAGE ObjectInfo @@ -69,7 +70,7 @@ struct DLL_LINKAGE ObjectInfo //ui32 maxPerMap; //unused std::function generateObject; - void setTemplate (si32 type, si32 subtype, ETerrainType terrain); + void setTemplate (si32 type, si32 subtype, Terrain terrain); ObjectInfo(); @@ -120,7 +121,7 @@ public: bool fill (); bool placeMines (); void initTownType (); - void paintZoneTerrain (ETerrainType terrainType); + void paintZoneTerrain (Terrain terrainType); void randomizeTownType(bool matchUndergroundType = false); //helper function void initTerrainType (); void createBorder(); @@ -192,7 +193,7 @@ private: //template info si32 townType; - ETerrainType terrainType; + Terrain terrainType; std::weak_ptr questArtZone; //artifacts required for Seer Huts will be placed here - or not if null std::vector possibleObjects; @@ -230,6 +231,7 @@ private: bool canObstacleBePlacedHere(ObjectTemplate &temp, int3 &pos); void setTemplateForObject(CGObjectInstance* obj); void checkAndPlaceObject(CGObjectInstance* object, const int3 &pos); + int chooseRandomAppearance(si32 ObjID) const; bool isGuardNeededForTreasure(int value); }; diff --git a/lib/rmg/CZonePlacer.cpp b/lib/rmg/CZonePlacer.cpp index 567d7723c..bae33194f 100644 --- a/lib/rmg/CZonePlacer.cpp +++ b/lib/rmg/CZonePlacer.cpp @@ -184,28 +184,26 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const zonesToPlace.push_back(zone); else { - switch ((*VLC->townh)[faction]->nativeTerrain) + auto & tt = (*VLC->townh)[faction]->nativeTerrain; + if(tt == Terrain("dirt")) { - case ETerrainType::GRASS: - case ETerrainType::SWAMP: - case ETerrainType::SNOW: - case ETerrainType::SAND: - case ETerrainType::ROUGH: - //surface - zonesOnLevel[0]++; - levels[zone.first] = 0; - break; - case ETerrainType::LAVA: - case ETerrainType::SUBTERRANEAN: - //underground - zonesOnLevel[1]++; - levels[zone.first] = 1; - break; - case ETerrainType::DIRT: - default: //any / random zonesToPlace.push_back(zone); - break; + } + else + { + if(tt.isUnderground()) + { + //underground + zonesOnLevel[1]++; + levels[zone.first] = 1; + } + else + { + //surface + zonesOnLevel[0]++; + levels[zone.first] = 0; + } } } } @@ -564,7 +562,7 @@ void CZonePlacer::assignZones() //make sure that terrain inside zone is not a rock //FIXME: reorder actions? - zone.second->paintZoneTerrain (ETerrainType::SUBTERRANEAN); + zone.second->paintZoneTerrain (Terrain("subterra")); } } logGlobal->info("Finished zone colouring"); diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index 33579ad84..15c84f5d8 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -12,8 +12,8 @@ #include "../ConstTransitivePtr.h" #include "../GameConstants.h" -const ui32 SERIALIZATION_VERSION = 801; -const ui32 MINIMAL_SERIALIZATION_VERSION = 753; +const ui32 SERIALIZATION_VERSION = 802; +const ui32 MINIMAL_SERIALIZATION_VERSION = 802; const std::string SAVEGAME_MAGIC = "VCMISVG"; class CHero; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 82c1c7714..358a91bef 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -1018,54 +1018,3 @@ std::vector CSpellHandler::getDefaultAllowed() const return allowedSpells; } - -void CSpellHandler::update780() -{ - static_assert(MINIMAL_SERIALIZATION_VERSION < 780, "No longer needed CSpellHandler::update780"); - - auto spellsContent = (*VLC->modh->content)["spells"]; - - const ContentTypeHandler::ModInfo & coreData = spellsContent.modData.at("core"); - - const JsonNode & coreSpells = coreData.modData; - - const int levelsCount = GameConstants::SPELL_SCHOOL_LEVELS; - - for(CSpell * spell : objects) - { - auto identifier = spell->identifier; - size_t colonPos = identifier.find(':'); - if(colonPos != std::string::npos) - continue; - - const JsonNode & actualConfig = coreSpells[spell->identifier]; - - if(actualConfig.getType() != JsonNode::JsonType::DATA_STRUCT) - { - logGlobal->error("Spell not found %s", spell->identifier); - continue; - } - - if(actualConfig["targetCondition"].getType() == JsonNode::JsonType::DATA_STRUCT && !actualConfig["targetCondition"].Struct().empty()) - { - spell->targetCondition = actualConfig["targetCondition"]; - } - - for(int levelIndex = 0; levelIndex < levelsCount; levelIndex++) - { - const JsonNode & levelNode = actualConfig["levels"][SpellConfig::LEVEL_NAMES[levelIndex]]; - - logGlobal->debug(levelNode.toJson()); - - CSpell::LevelInfo & levelObject = spell->levels[levelIndex]; - - if(levelNode["battleEffects"].getType() == JsonNode::JsonType::DATA_STRUCT && !levelNode["battleEffects"].Struct().empty()) - { - levelObject.battleEffects = levelNode["battleEffects"]; - - logGlobal->trace("Updated special effects for level %d of spell %s", levelIndex, spell->identifier); - } - } - - } -} diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index a25d4de2c..f821abc46 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -83,14 +83,7 @@ public: { h & resourceName; h & verticalPosition; - if(version >= 754) - { - h & pause; - } - else if(!h.saving) - { - pause = 0; - } + h & pause; } }; @@ -120,10 +113,7 @@ public: h & projectile; h & hit; h & cast; - if(version >= 762) - { - h & affect; - } + h & affect; } std::string selectProjectile(const double angle) const; @@ -158,32 +148,11 @@ public: h & AIValue; h & smartTarget; h & range; - - if(version >= 773) - { - h & effects; - h & cumulativeEffects; - } - else - { - //all old effects treated as not cumulative, special cases handled by CSpell::serialize - std::vector old; - h & old; - - if(!h.saving) - { - effects.clear(); - cumulativeEffects.clear(); - for(const Bonus & oldBonus : old) - effects.push_back(std::make_shared(oldBonus)); - } - } - + h & effects; + h & cumulativeEffects; h & clearTarget; h & clearAffected; - - if(version >= 780) - h & battleEffects; + h & battleEffects; } }; @@ -316,27 +285,7 @@ public: h & damage; h & offensive; h & targetType; - - if(version >= 780) - { - h & targetCondition; - } - else - { - BTVector immunities; - BTVector absoluteImmunities; - BTVector limiters; - BTVector absoluteLimiters; - - h & immunities; - h & limiters; - h & absoluteImmunities; - h & absoluteLimiters; - - if(!h.saving) - targetCondition = convertTargetCondition(immunities, absoluteImmunities, limiters, absoluteLimiters); - } - + h & targetCondition; h & iconImmune; h & defaultProbability; h & special; @@ -348,16 +297,6 @@ public: h & levels; h & school; h & animationInfo; - - //backward compatibility - //can not be added to level structure as level structure does not know spell id - if(!h.saving && version < 773) - { - if(id == SpellID::DISRUPTING_RAY || id == SpellID::ACID_BREATH_DEFENSE) - for(auto & level : levels) - std::swap(level.effects, level.cumulativeEffects); - } - } friend class CSpellHandler; friend class Graphics; @@ -442,11 +381,6 @@ public: template void serialize(Handler & h, const int version) { h & objects; - if(!h.saving && version < 780) - { - update780(); - } - if(!h.saving) { afterLoadFinalization(); @@ -456,6 +390,4 @@ public: protected: const std::vector & getTypeNames() const override; CSpell * loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) override; -private: - void update780(); }; diff --git a/scripting/lua/api/BattleCb.cpp b/scripting/lua/api/BattleCb.cpp index 38d242e80..779289014 100644 --- a/scripting/lua/api/BattleCb.cpp +++ b/scripting/lua/api/BattleCb.cpp @@ -84,9 +84,7 @@ int BattleCbProxy::getTerrainType(lua_State * L) if(!S.tryGet(1, object)) return S.retVoid(); - auto ret = object->battleTerrainType(); - - return LuaStack::quickRetInt(L, static_cast(ret.num)); + return LuaStack::quickRetStr(L, object->battleTerrainType()); } int BattleCbProxy::getUnitByPos(lua_State * L) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index dcb46375a..62e992919 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1911,7 +1911,7 @@ void CGameHandler::newTurn() hth.id = h->id; auto ti = make_unique(h, 1); // TODO: this code executed when bonuses of previous day not yet updated (this happen in NewTurn::applyGs). See issue 2356 - hth.move = h->maxMovePointsCached(gs->map->getTile(h->getPosition(false)).terType != ETerrainType::WATER, ti.get()); + hth.move = h->maxMovePointsCached(gs->map->getTile(h->getPosition(false)).terType.isLand(), ti.get()); hth.mana = h->getManaNewTurn(); n.heroes.insert(hth); @@ -2215,9 +2215,9 @@ void CGameHandler::setupBattle(int3 tile, const CArmedInstance *armies[2], const battleResult.set(nullptr); const auto t = getTile(tile); - ETerrainType terrain = t->terType; + Terrain terrain = t->terType; if (gs->map->isCoastalTile(tile)) //coastal tile is always ground - terrain = ETerrainType::SAND; + terrain = Terrain("sand"); BFieldType terType = gs->battleGetBattlefieldType(tile, getRandomGenerator()); if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat) @@ -2314,7 +2314,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo const int3 guardPos = gs->guardingCreaturePosition(hmpos); const bool embarking = !h->boat && !t.visitableObjects.empty() && t.visitableObjects.back()->ID == Obj::BOAT; - const bool disembarking = h->boat && t.terType != ETerrainType::WATER && !t.blocked; + const bool disembarking = h->boat && t.terType.isLand() && !t.blocked; //result structure for start - movement failed, no move points used TryMoveHero tmh; @@ -2336,11 +2336,11 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo //it's a rock or blocked and not visitable tile //OR hero is on land and dest is water and (there is not present only one object - boat) - if (((t.terType == ETerrainType::ROCK || (t.blocked && !t.visitable && !canFly)) + if (((!t.terType.isPassable() || (t.blocked && !t.visitable && !canFly)) && complain("Cannot move hero, destination tile is blocked!")) - || ((!h->boat && !canWalkOnSea && !canFly && t.terType == ETerrainType::WATER && (t.visitableObjects.size() < 1 || (t.visitableObjects.back()->ID != Obj::BOAT && t.visitableObjects.back()->ID != Obj::HERO))) //hero is not on boat/water walking and dst water tile doesn't contain boat/hero (objs visitable from land) -> we test back cause boat may be on top of another object (#276) + || ((!h->boat && !canWalkOnSea && !canFly && t.terType.isWater() && (t.visitableObjects.size() < 1 || (t.visitableObjects.back()->ID != Obj::BOAT && t.visitableObjects.back()->ID != Obj::HERO))) //hero is not on boat/water walking and dst water tile doesn't contain boat/hero (objs visitable from land) -> we test back cause boat may be on top of another object (#276) && complain("Cannot move hero, destination tile is on water!")) - || ((h->boat && t.terType != ETerrainType::WATER && t.blocked) + || ((h->boat && t.terType.isLand() && t.blocked) && complain("Cannot disembark hero, tile is blocked!")) || ((distance(h->pos, dst) >= 1.5 && !teleporting) && complain("Tiles are not neighboring!")) diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 98454cac5..2c90edf6e 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -272,21 +272,14 @@ public: h & QID; h & states; h & finishingBattle; + h & getRandomGenerator(); - if(version >= 761) - { - h & getRandomGenerator(); - } - - if(version >= 800) - { - JsonNode scriptsState; - if(h.saving) - serverScripts->serializeState(h.saving, scriptsState); - h & scriptsState; - if(!h.saving) - serverScripts->serializeState(h.saving, scriptsState); - } + JsonNode scriptsState; + if(h.saving) + serverScripts->serializeState(h.saving, scriptsState); + h & scriptsState; + if(!h.saving) + serverScripts->serializeState(h.saving, scriptsState); } void sendMessageToAll(const std::string &message); @@ -314,11 +307,6 @@ public: h & loserHero; h & victor; h & loser; - if(version < 774 && !h.saving) - { - bool duel; - h & duel; - } h & remainingBattleQueriesCount; } }; diff --git a/test/game/CGameStateTest.cpp b/test/game/CGameStateTest.cpp index 9b15f9971..a31d8eb25 100644 --- a/test/game/CGameStateTest.cpp +++ b/test/game/CGameStateTest.cpp @@ -193,7 +193,7 @@ public: const auto t = gameCallback->getTile(tile); - ETerrainType terrain = t->terType; + Terrain terrain = t->terType; BFieldType terType = BFieldType::GRASS_HILLS; //send info about battles diff --git a/test/googletest b/test/googletest index e2239ee60..4bab34d20 160000 --- a/test/googletest +++ b/test/googletest @@ -1 +1 @@ -Subproject commit e2239ee6043f73722e7aa812a459f54a28552929 +Subproject commit 4bab34d2084259cba67f3bfb51217c10d606e175 diff --git a/test/map/CMapEditManagerTest.cpp b/test/map/CMapEditManagerTest.cpp index 572b7e0cd..8283bccde 100644 --- a/test/map/CMapEditManagerTest.cpp +++ b/test/map/CMapEditManagerTest.cpp @@ -33,30 +33,30 @@ TEST(MapManager, DrawTerrain_Type) // 1x1 Blow up editManager->getTerrainSelection().select(int3(5, 5, 0)); - editManager->drawTerrain(ETerrainType::GRASS); + editManager->drawTerrain(Terrain("grass")); static const int3 squareCheck[] = { int3(5,5,0), int3(5,4,0), int3(4,4,0), int3(4,5,0) }; for(int i = 0; i < ARRAY_COUNT(squareCheck); ++i) { - EXPECT_EQ(map->getTile(squareCheck[i]).terType, ETerrainType::GRASS); + EXPECT_EQ(map->getTile(squareCheck[i]).terType, Terrain("grass")); } // Concat to square editManager->getTerrainSelection().select(int3(6, 5, 0)); - editManager->drawTerrain(ETerrainType::GRASS); - EXPECT_EQ(map->getTile(int3(6, 4, 0)).terType, ETerrainType::GRASS); + editManager->drawTerrain(Terrain("grass")); + EXPECT_EQ(map->getTile(int3(6, 4, 0)).terType, Terrain("grass")); editManager->getTerrainSelection().select(int3(6, 5, 0)); - editManager->drawTerrain(ETerrainType::LAVA); - EXPECT_EQ(map->getTile(int3(4, 4, 0)).terType, ETerrainType::GRASS); - EXPECT_EQ(map->getTile(int3(7, 4, 0)).terType, ETerrainType::LAVA); + editManager->drawTerrain(Terrain("lava")); + EXPECT_EQ(map->getTile(int3(4, 4, 0)).terType, Terrain("grass")); + EXPECT_EQ(map->getTile(int3(7, 4, 0)).terType, Terrain("lava")); // Special case water,rock editManager->getTerrainSelection().selectRange(MapRect(int3(10, 10, 0), 10, 5)); - editManager->drawTerrain(ETerrainType::GRASS); + editManager->drawTerrain(Terrain("grass")); editManager->getTerrainSelection().selectRange(MapRect(int3(15, 17, 0), 10, 5)); - editManager->drawTerrain(ETerrainType::GRASS); + editManager->drawTerrain(Terrain("grass")); editManager->getTerrainSelection().select(int3(21, 16, 0)); - editManager->drawTerrain(ETerrainType::GRASS); - EXPECT_EQ(map->getTile(int3(20, 15, 0)).terType, ETerrainType::GRASS); + editManager->drawTerrain(Terrain("grass")); + EXPECT_EQ(map->getTile(int3(20, 15, 0)).terType, Terrain("grass")); // Special case non water,rock static const int3 diagonalCheck[] = { int3(31,42,0), int3(32,42,0), int3(32,43,0), int3(33,43,0), int3(33,44,0), @@ -66,17 +66,17 @@ TEST(MapManager, DrawTerrain_Type) { editManager->getTerrainSelection().select(diagonalCheck[i]); } - editManager->drawTerrain(ETerrainType::GRASS); - EXPECT_EQ(map->getTile(int3(35, 44, 0)).terType, ETerrainType::WATER); + editManager->drawTerrain(Terrain("grass")); + EXPECT_EQ(map->getTile(int3(35, 44, 0)).terType, Terrain("water")); // Rock case editManager->getTerrainSelection().selectRange(MapRect(int3(1, 1, 1), 15, 15)); - editManager->drawTerrain(ETerrainType::SUBTERRANEAN); + editManager->drawTerrain(Terrain("subterra")); std::vector vec({ int3(6, 6, 1), int3(7, 6, 1), int3(8, 6, 1), int3(5, 7, 1), int3(6, 7, 1), int3(7, 7, 1), int3(8, 7, 1), int3(4, 8, 1), int3(5, 8, 1), int3(6, 8, 1)}); editManager->getTerrainSelection().setSelection(vec); - editManager->drawTerrain(ETerrainType::ROCK); - EXPECT_TRUE(map->getTile(int3(5, 6, 1)).terType == ETerrainType::ROCK || map->getTile(int3(7, 8, 1)).terType == ETerrainType::ROCK); + editManager->drawTerrain(Terrain("rock")); + EXPECT_TRUE(!map->getTile(int3(5, 6, 1)).terType.isPassable() || !map->getTile(int3(7, 8, 1)).terType.isPassable()); //todo: add checks here and enable, also use smaller size #if 0 @@ -89,13 +89,13 @@ TEST(MapManager, DrawTerrain_Type) auto editManager2 = map2->getEditManager(); editManager2->getTerrainSelection().selectRange(MapRect(int3(0, 0, 1), 128, 128)); - editManager2->drawTerrain(ETerrainType::SUBTERRANEAN); + editManager2->drawTerrain(CTerrainType("subterra")); std::vector selection({ int3(95, 43, 1), int3(95, 44, 1), int3(94, 45, 1), int3(95, 45, 1), int3(96, 45, 1), int3(93, 46, 1), int3(94, 46, 1), int3(95, 46, 1), int3(96, 46, 1), int3(97, 46, 1), int3(98, 46, 1), int3(99, 46, 1)}); editManager2->getTerrainSelection().setSelection(selection); - editManager2->drawTerrain(ETerrainType::ROCK); + editManager2->drawTerrain(CTerrainType("rock")); #endif // 0 } diff --git a/test/mock/mock_IBattleInfoCallback.h b/test/mock/mock_IBattleInfoCallback.h index 4f4765f25..ff7904b3c 100644 --- a/test/mock/mock_IBattleInfoCallback.h +++ b/test/mock/mock_IBattleInfoCallback.h @@ -17,7 +17,7 @@ class IBattleInfoCallbackMock : public IBattleInfoCallback { public: MOCK_CONST_METHOD0(getContextPool, scripting::Pool *()); - MOCK_CONST_METHOD0(battleTerrainType, ETerrainType()); + MOCK_CONST_METHOD0(battleTerrainType, Terrain()); MOCK_CONST_METHOD0(battleGetBattlefieldType, BFieldType()); MOCK_CONST_METHOD0(battleIsFinished, boost::optional()); diff --git a/test/mock/mock_battle_IBattleState.h b/test/mock/mock_battle_IBattleState.h index 36dba2262..3493b703d 100644 --- a/test/mock/mock_battle_IBattleState.h +++ b/test/mock/mock_battle_IBattleState.h @@ -19,7 +19,7 @@ public: MOCK_CONST_METHOD1(getStacksIf, TStacks(TStackFilter)); MOCK_CONST_METHOD1(getUnitsIf, battle::Units(battle::UnitFilter)); MOCK_CONST_METHOD0(getBattlefieldType, BFieldType()); - MOCK_CONST_METHOD0(getTerrainType, ETerrainType()); + MOCK_CONST_METHOD0(getTerrainType, Terrain()); MOCK_CONST_METHOD0(getAllObstacles, IBattleInfo::ObstacleCList()); MOCK_CONST_METHOD0(getDefendedTown, const CGTownInstance *()); MOCK_CONST_METHOD1(getWallState, si8(int));