1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

New terrain support - part 1 (#755)

Initial support of new terrains
This commit is contained in:
Nordsoft91 2022-06-20 17:39:50 +03:00 committed by Andrii Danylchenko
parent 205bb09880
commit aaa07e4d2e
103 changed files with 1066 additions and 1472 deletions

View File

@ -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<ELayer::SAIL>(pos, tile, fow, player, gs));
if(useFlying)
resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
if(useWaterWalking)
resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs));
break;
default:
}
else
{
resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs));
if(useFlying)
resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(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();
}
}

View File

@ -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());

View File

@ -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<ELayer::SAIL>(pos, tile, fow, player, gs));
if(useFlying)
resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
if(useWaterWalking)
resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs));
break;
default:
}
else
{
resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs));
if(useFlying)
resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
break;
}
}
}

View File

@ -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;

View File

@ -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<MusicEntry> 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);
}

View File

@ -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<soundBase::soundID> pickupSounds;
std::vector<soundBase::soundID> horseSounds;
std::vector<soundBase::soundID> battleIntroSounds;
std::map<Terrain, soundBase::soundID> 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<MusicEntry> current;
std::unique_ptr<MusicEntry> 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<MusicEntry> queued);
std::map<std::string, std::map<int, std::string> > musicsSet;
std::map<std::string, std::map<std::string, std::string>> 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();

View File

@ -1351,12 +1351,6 @@ void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus
template <typename Handler> 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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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<std::string> TERRAIN_FILES =
static const std::map<std::string, std::string> 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<std::string> ROAD_FILES =
static const std::map<std::string, std::string> 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<std::string> RIVER_FILES =
auto loadFlipped = [](TFlippedAnimations & animation, TFlippedCache & cache, const std::map<std::string, std::string> & files)
{
"clrrvr",
"icyrvr",
"mudrvr",
"lavrvr"
};
auto loadFlipped = [](int types, TFlippedAnimations & animation, TFlippedCache & cache, const std::vector<std::string> & 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<CAnimation>(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<CAnimation>(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<CAnimation>(files[i]);
animation[i][rotation]->preload();
const size_t views = animation[i][rotation]->size(0);
animation[type.first][rotation] = make_unique<CAnimation>(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<std::string, std::string> 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);

View File

@ -354,8 +354,8 @@ public:
//terrain graphics
//FIXME: unique_ptr should be enough, but fails to compile in MSVS 2013
typedef std::vector<std::array<std::shared_ptr<CAnimation>, 4>> TFlippedAnimations; //[type, rotation]
typedef std::vector<std::vector<std::array<std::shared_ptr<IImage>, 4>>> TFlippedCache;//[type, view type, rotation]
typedef std::map<std::string, std::array<std::shared_ptr<CAnimation>, 4>> TFlippedAnimations; //[type, rotation]
typedef std::map<std::string, std::vector<std::array<std::shared_ptr<IImage>, 4>>> TFlippedCache;//[type, view type, rotation]
TFlippedAnimations terrainAnimations;//[terrain type, rotation]
TFlippedCache terrainImages;//[terrain type, view type, rotation]

View File

@ -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<int, std::pair<SDL_Color, SDL_Color> > CMinimap::loadColors(std::string from)
std::map<Terrain, std::pair<SDL_Color, SDL_Color> > CMinimap::loadColors()
{
std::map<int, std::pair<SDL_Color, SDL_Color> > ret;
std::map<Terrain, std::pair<SDL_Color, SDL_Color> > 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<int>(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<int, std::pair<SDL_Color, SDL_Color> > 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;

View File

@ -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<int, std::pair<SDL_Color, SDL_Color> > loadColors(std::string from);
std::map<Terrain, std::pair<SDL_Color, SDL_Color> > 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<int, std::pair<SDL_Color, SDL_Color> > colors;
const std::map<Terrain, std::pair<SDL_Color, SDL_Color> > colors;
CMinimap(const Rect & position);

View File

@ -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)

View File

@ -80,5 +80,9 @@
"skills" :
[
"config/skills.json"
],
"terrains":
[
"config/terrains.json"
]
}

View File

@ -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,

View File

@ -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" :
{

View File

@ -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"
}
}

View File

@ -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();

View File

@ -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)

View File

@ -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<Bonus> & b); //tier must be <1-7>
void addBonusForAllCreatures(const std::shared_ptr<Bonus> & b); //due to CBonusSystem::addNewBonus(const std::shared_ptr<Bonus>& b);
void removeBonusesFromAllCreatures();
void restoreAllCreaturesNodeType794(); //restore ALL_CREATURES node type for old saves
CCreatureHandler();
~CCreatureHandler();

View File

@ -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
}

View File

@ -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;
}

View File

@ -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
}

View File

@ -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<std::
CGeneralTextHandler::CGeneralTextHandler()
{
std::vector<std::string> 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)))

View File

@ -122,7 +122,7 @@ public:
std::vector<std::string> advobtxt;
std::vector<std::string> xtrainfo;
std::vector<std::string> restypes; //names of resources
std::vector<std::string> terrainNames;
std::map<std::string, std::string> terrainNames;
std::vector<std::string> randsign;
std::vector<std::pair<std::string,std::string>> mines; //first - name; second - event description
std::vector<std::string> seerEmpty;

View File

@ -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 <math.h>
@ -176,7 +177,7 @@ std::vector<BattleHex> 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<si32>(obs["width"].Float());
obi.height = static_cast<si32>(obs["height"].Float());
obi.allowedTerrains = obs["allowedTerrain"].convertTo<std::vector<ETerrainType> >();
for(auto & t : obs["allowedTerrain"].Vector())
obi.allowedTerrains.emplace_back(t.String());
obi.allowedSpecialBfields = obs["specialBattlefields"].convertTo<std::vector<BFieldType> >();
obi.blockedTiles = obs["blockedTiles"].convertTo<std::vector<si16> >();
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<bool> CHeroHandler::getDefaultAllowed() const

View File

@ -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<ETerrainType> allowedTerrains;
std::vector<Terrain> allowedTerrains;
std::vector<BFieldType> 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<BattleHex> 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 <typename Handler> 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<int> terrCosts;
std::map<Terrain, int> terrCosts;
struct SBallisticsLevelInfo
{

View File

@ -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

View File

@ -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;

View File

@ -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<ELayer::SAIL>(pos, tile, fow, player, gs));
if(useFlying)
resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
if(useWaterWalking)
resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs));
break;
default:
}
if(tile->terType.isLand())
{
resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs));
if(useFlying)
resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(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<bool>(
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<int>(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<int>(ret * 0.666);
@ -1266,7 +1260,7 @@ int CPathfinderHelper::getMovementCost(
{
std::vector<int3> 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);

View File

@ -13,6 +13,7 @@
#include "IGameCallback.h"
#include "HeroBonus.h"
#include "int3.h"
#include "Terrain.h"
#include <boost/heap/fibonacci_heap.hpp>
@ -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;

View File

@ -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;

View File

@ -35,12 +35,9 @@ public:
template <typename Handler> 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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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<std::string, CBuilding::EBuildMode> 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<Bonus> 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<std::string, BuildingID>(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<BuildingSubID::EBuildingSubID>(currentBuilding["type"], BuildingSubID::NONE, MappedKeys::SPECIAL_BUILDINGS);
height = subId == BuildingSubID::LOOKOUT_TOWER || bid == BuildingID::GRAIL
? CTownHandler::getMappedValue<CBuilding::ETowerHeight>(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<std::string, BuildingID>(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<EAlignment::EAlignment>(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<ETerrainType::EETerrainType>(terrainNum);
: Terrain(nativeTerrain.String());
if (!source["town"].isNull())
{

View File

@ -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<Bonus> b, BonusList & bonusList);
void update792();
void update794();
template <typename Handler> 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<FactionID, Faction, CFactio
std::vector<BuildingRequirementsHelper> requirementsToLoad;
std::vector<BuildingRequirementsHelper> 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<FactionID, Faction, CFactio
void loadPuzzle(CFaction & faction, const JsonNode & source);
ETerrainType::EETerrainType getDefaultTerrainForAlignment(EAlignment::EAlignment aligment) const;
Terrain getDefaultTerrainForAlignment(EAlignment::EAlignment aligment) const;
void loadRandomFaction();
@ -450,15 +423,7 @@ public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & objects;
if(version >= 770)
{
h & randomTown;
}
else if(!h.saving)
{
loadRandomFaction();
}
h & randomTown;
}
protected:

View File

@ -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<ETerrainType::EETerrainType, std::string> 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 << "<Unknown type>";
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<EPathfindingLayer::EEPathfindingLayer, std::string> pathfinderLayerToString

View File

@ -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<std::string> RIVER_NAMES {"", "rw", "ri", "rm", "rl"};
static std::vector<std::string> 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:

View File

@ -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<std::string>(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;
}

View File

@ -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<Bonus>
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 <typename Ptr>
@ -999,10 +979,7 @@ public:
template <typename Handler> void serialize(Handler & h, const int version)
{
h & static_cast<ILimiter&>(*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;

View File

@ -49,7 +49,7 @@ void CPrivilegedInfoCallback::getFreeTiles(std::vector<int3> & 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<int3, ShashInt3> &
{
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));
}
}

View File

@ -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

View File

@ -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<char*>(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<ILimiter> 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;

View File

@ -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 &copy);
@ -127,10 +128,7 @@ public:
template <typename Handler> 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;
}
}

View File

@ -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();

View File

@ -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;

View File

@ -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;
}

View File

@ -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"
};

204
lib/Terrain.cpp Normal file
View File

@ -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<std::string, 10> 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> Terrain::Manager::terrains()
{
std::vector<Terrain> _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<const std::string &>(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();
}

93
lib/Terrain.h Normal file
View File

@ -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<int, 3> minimapBlocked;
std::array<int, 3> 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<Terrain> terrains();
static const Info & getInfo(const Terrain &);
private:
static Manager & get();
Manager();
std::map<Terrain, Info> 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 <typename Handler> void serialize(Handler &h, const int version)
{
h & name;
}
protected:
std::string name;
};
DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const Terrain terrainType);

View File

@ -288,15 +288,3 @@ void LibClasses::setContent(std::shared_ptr<CContentHandler> content)
{
modh->content = content;
}
void LibClasses::restoreAllCreaturesNodeType794()
{
creh->restoreAllCreaturesNodeType794();
}
void LibClasses::update800()
{
vstd::clear_pointer(scriptHandler);
scriptHandler = new scripting::ScriptHandler();
}

View File

@ -44,7 +44,6 @@ class DLL_LINKAGE LibClasses : public Services
void makeNull(); //sets all handler pointers to null
std::shared_ptr<CContentHandler> getContent() const;
void setContent(std::shared_ptr<CContentHandler> content);
void restoreAllCreaturesNodeType794();
public:
bool IS_AI_ENABLED; //unused?
@ -91,33 +90,20 @@ public:
template <typename Handler> 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

View File

@ -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<int()> 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;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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<HeroSpecial *> & 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<Bonus> b : SpecialtyBonusToBonuses(HeroSpecialToSpecialtyBonus(*hs), type->ID.getNum()))
addNewBonus(b);
}
}
void CGHeroInstance::updateSkillBonus(SecondarySkill which, int val)
{
removeBonuses(Selector::source(Bonus::SECONDARY_SKILL, which));

View File

@ -1,4 +1,4 @@
/*
/*
* CGHeroInstance.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
@ -89,15 +89,7 @@ public:
template <typename Handler> 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<HeroSpecial*> & specialtyDeprecated);
public:
std::string getHeroTypeName() const;
@ -316,18 +307,8 @@ public:
h & visitedTown;
h & boat;
h & type;
if(version < 781)
{
std::vector<HeroSpecial*> 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();
}
};

View File

@ -796,28 +796,6 @@ void CGTownInstance::addTownBonuses()
}
}
void CGTownInstance::fixBonusingDuplicates() //For versions 794-800
{
std::map<BuildingID::EBuildingID, int> 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));

View File

@ -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<CreatureID>& creatureIds, const std::vector<ConstTransitivePtr<CGDwelling> >& 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.
};

View File

@ -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<ObjectTemplate> AObjectTypeHandler::getTemplates() const
return templates;
}
std::vector<ObjectTemplate> AObjectTypeHandler::getTemplates(si32 terrainType) const// FIXME: replace with ETerrainType
std::vector<ObjectTemplate> AObjectTypeHandler::getTemplates(const Terrain & terrainType) const
{
std::vector<ObjectTemplate> templates = getTemplates();
std::vector<ObjectTemplate> 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<ObjectTemplate> AObjectTypeHandler::getTemplates(si32 terrainType) c
return filtered;
}
boost::optional<ObjectTemplate> AObjectTypeHandler::getOverride(si32 terrainType, const CGObjectInstance * object) const
boost::optional<ObjectTemplate> AObjectTypeHandler::getOverride(const Terrain & terrainType, const CGObjectInstance * object) const
{
std::vector<ObjectTemplate> ret = getTemplates(terrainType);
for (auto & tmpl : ret)

View File

@ -178,11 +178,11 @@ public:
/// returns all templates matching parameters
std::vector<ObjectTemplate> getTemplates() const;
std::vector<ObjectTemplate> getTemplates(si32 terrainType) const;
std::vector<ObjectTemplate> 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<ObjectTemplate> getOverride(si32 terrainType, const CGObjectInstance * object) const;
boost::optional<ObjectTemplate> 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;
}
};

View File

@ -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;
}
}

View File

@ -213,13 +213,9 @@ public:
///Entry point of binary (de-)serialization
template <typename Handler> 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;

View File

@ -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);

View File

@ -269,11 +269,6 @@ public:
h & onVisited;
h & onEmpty;
h & visitMode;
if(version < 778)
{
ui16 soundID = 0;
h & soundID;
}
h & selectMode;
h & selectedReward;
}

View File

@ -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<int>(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;
}

View File

@ -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<ETerrainType> allowedTerrains;
std::set<Terrain> 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;
}
};

View File

@ -422,17 +422,6 @@ std::vector<CGHeroInstance *> CCampaignScenario::getLostCrossoverHeroes()
return lostCrossoverHeroes;
}
std::vector<JsonNode> CCampaignScenario::update787(std::vector<CGHeroInstance *> & heroes)
{
static_assert(MINIMAL_SERIALIZATION_VERSION < 787, "No longer needed CCampaignScenario::update787");
std::vector<JsonNode> heroesNew;
for(auto hero : heroes)
{
heroesNew.push_back(CCampaignState::crossoverSerialize(hero));
}
return heroesNew;
}
void CCampaignState::setCurrentMapAsConquered(const std::vector<CGHeroInstance *> & heroes)
{
camp->scenarios[*currentMap].crossoverHeroes.clear();

View File

@ -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<CGHeroInstance *> getLostCrossoverHeroes(); /// returns a list of crossover heroes which started the scenario, but didn't complete it
std::vector<JsonNode> update787(std::vector<CGHeroInstance *> & heroes);
CCampaignScenario();
template <typename Handler> void serialize(Handler &h, const int formatVersion)
@ -163,19 +162,8 @@ public:
h & prolog;
h & epilog;
h & travelOptions;
if(formatVersion < 787)
{
std::vector<CGHeroInstance *> crossoverHeroesOld, placedCrossoverHeroesOld;
h & crossoverHeroesOld;
h & placedCrossoverHeroesOld;
crossoverHeroes = update787(crossoverHeroesOld);
placedCrossoverHeroes = update787(placedCrossoverHeroesOld);
}
else
{
h & crossoverHeroes;
h & placedCrossoverHeroes;
}
h & crossoverHeroes;
h & placedCrossoverHeroes;
h & keepHeroes;
}
};

View File

@ -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<int3> & 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];
}

View File

@ -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;
};

View File

@ -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()

View File

@ -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;
}
};

View File

@ -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

View File

@ -243,13 +243,13 @@ void CMapEditManager::clearTerrain(CRandomGenerator * gen)
execute(make_unique<CClearTerrainOperation>(map, gen ? gen : &(this->gen)));
}
void CMapEditManager::drawTerrain(ETerrainType terType, CRandomGenerator * gen)
void CMapEditManager::drawTerrain(Terrain terType, CRandomGenerator * gen)
{
execute(make_unique<CDrawTerrainOperation>(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<CDrawRoadsOperation>(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<TerrainViewPattern> * 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<std::string>(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<CDrawTerrainOperation>(map, terrainSel, ETerrainType::WATER, gen));
addOperation(make_unique<CDrawTerrainOperation>(map, terrainSel, Terrain("water"), gen));
if(map->twoLevel)
{
terrainSel.clearSelection();
terrainSel.selectRange(MapRect(int3(0, 0, 1), map->width, map->height));
addOperation(make_unique<CDrawTerrainOperation>(map, terrainSel, ETerrainType::ROCK, gen));
addOperation(make_unique<CDrawTerrainOperation>(map, terrainSel, Terrain("rock"), gen));
}
}

View File

@ -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<TerrainViewPattern> * 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<int3> invalidatedTerViews;
};

View File

@ -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<ERiverType::ERiverType>(reader.readUInt8());
tile.riverType = RIVER_NAMES[reader.readUInt8()];
tile.riverDir = reader.readUInt8();
tile.roadType = static_cast<ERoadType::ERoadType>(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;
}
}

View File

@ -323,20 +323,6 @@ namespace TriggeredEventsDetail
namespace TerrainDetail
{
static const std::array<std::string, 10> terrainCodes =
{
"dt", "sa", "gr", "sn", "sw", "rg", "sb", "lv", "wt", "rc"
};
static const std::array<std::string, 4> roadCodes =
{
"", "pd", "pg", "pc"
};
static const std::array<std::string, 5> riverCodes =
{
"", "rw", "ri", "rm", "rl"
};
static const std::array<char, 4> 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();
}

View File

@ -69,17 +69,6 @@ CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed) :
void CMapGenerator::loadConfig()
{
static std::map<std::string, ETerrainType> 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<std::string, Res::ERes> resMap
{
{"wood", Res::ERes::WOOD},
@ -90,23 +79,17 @@ void CMapGenerator::loadConfig()
{"sulfur", Res::ERes::SULFUR},
{"gold", Res::ERes::GOLD},
};
static std::map<std::string, ERoadType::ERoadType> 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<int3> 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<Terrain> 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);

View File

@ -26,8 +26,6 @@ class JsonNode;
class CMapGenerator;
class CTileInfo;
//#define _BETA
typedef std::vector<JsonNode> JsonVector;
class rmgException : public std::exception
@ -54,14 +52,14 @@ class DLL_LINKAGE CMapGenerator
public:
struct Config
{
std::vector<ETerrainType> terrainUndergroundAllowed;
std::vector<ETerrainType> terrainGroundProhibit;
std::vector<Terrain> terrainUndergroundAllowed;
std::vector<Terrain> terrainGroundProhibit;
std::vector<CTreasureInfo> waterTreasure;
int shipyardGuard;
int mineExtraResources;
std::map<Res::ERes, int> mineValues;
int minGuardStrength;
ERoadType::ERoadType defaultRoadType;
std::string defaultRoadType;
int treasureValueLimit;
std::vector<int> prisonExperience, prisonValues;
std::vector<int> 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;

View File

@ -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] : "<INVALID TERRAIN>";
return (index >=0 && index < Terrain::Manager::terrains().size()) ? static_cast<std::string>(Terrain::Manager::terrains()[index]) : "<INVALID TERRAIN>";
}
};
@ -89,18 +90,6 @@ public:
}
};
const std::set<ETerrainType> 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<int> ZoneOptions::getOwner() const
return owner;
}
const std::set<ETerrainType> & ZoneOptions::getTerrainTypes() const
const std::set<Terrain> & ZoneOptions::getTerrainTypes() const
{
return terrainTypes;
}
void ZoneOptions::setTerrainTypes(const std::set<ETerrainType> & value)
void ZoneOptions::setTerrainTypes(const std::set<Terrain> & 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<ETerrainType, TerrainEncoder>("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<TFaction, FactionID>("allowedMonsters", monsterTypes, VLC->townh->getAllowedFactions(false));

View File

@ -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<ETerrainType> DEFAULT_TERRAIN_TYPES;
static const TRmgTemplateZoneId NO_ZONE;
class DLL_LINKAGE CTownInfo
@ -101,8 +102,8 @@ public:
void setSize(int value);
boost::optional<int> getOwner() const;
const std::set<ETerrainType> & getTerrainTypes() const;
void setTerrainTypes(const std::set<ETerrainType> & value);
const std::set<Terrain> & getTerrainTypes() const;
void setTerrainTypes(const std::set<Terrain> & value);
std::set<TFaction> getDefaultTownTypes() const;
const std::set<TFaction> & getTownTypes() const;
@ -134,7 +135,7 @@ protected:
CTownInfo playerTowns;
CTownInfo neutralTowns;
bool matchTerrainToTown;
std::set<ETerrainType> terrainTypes;
std::set<Terrain> terrainTypes;
bool townsAreSameType;
std::set<TFaction> townTypes;

View File

@ -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<int3, int3> cameFrom; // The map of navigated nodes.
std::map<int3, float> 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<Terrain> 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<Terrain> 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<int3> 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<int3> & 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)

View File

@ -17,6 +17,7 @@
#include "CRmgTemplate.h"
#include "../mapObjects/ObjectTemplate.h"
#include <boost/heap/priority_queue.hpp> //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<CGObjectInstance *()> 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<CRmgTemplateZone> questArtZone; //artifacts required for Seer Huts will be placed here - or not if null
std::vector<ObjectInfo> 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);
};

View File

@ -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");

View File

@ -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;

View File

@ -1018,54 +1018,3 @@ std::vector<bool> 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);
}
}
}
}

View File

@ -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<Bonus> old;
h & old;
if(!h.saving)
{
effects.clear();
cumulativeEffects.clear();
for(const Bonus & oldBonus : old)
effects.push_back(std::make_shared<Bonus>(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 <typename Handler> 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<std::string> & getTypeNames() const override;
CSpell * loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) override;
private:
void update780();
};

View File

@ -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<si32>(ret.num));
return LuaStack::quickRetStr(L, object->battleTerrainType());
}
int BattleCbProxy::getUnitByPos(lua_State * L)

View File

@ -1911,7 +1911,7 @@ void CGameHandler::newTurn()
hth.id = h->id;
auto ti = make_unique<TurnInfo>(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!"))

View File

@ -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;
}
};

View File

@ -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

@ -1 +1 @@
Subproject commit e2239ee6043f73722e7aa812a459f54a28552929
Subproject commit 4bab34d2084259cba67f3bfb51217c10d606e175

Some files were not shown because too many files have changed in this diff Show More