1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-29 23:07:48 +02:00

- Renamed /lib subfolders to lowercase

This commit is contained in:
beegee1
2013-04-07 10:48:07 +00:00
parent 89ad007b44
commit ee51c5beb5
87 changed files with 197 additions and 189 deletions

View File

@@ -0,0 +1,487 @@
#include "StdInc.h"
#include "CCampaignHandler.h"
#include "../filesystem/CResourceLoader.h"
#include "../filesystem/CCompressedStream.h"
#include "../VCMI_Lib.h"
#include "../vcmi_endian.h"
#include "../CGeneralTextHandler.h"
#include "../StartInfo.h"
#include "../CArtHandler.h" //for hero crossover
#include "../CObjectHandler.h" //for hero crossover
#include "../CHeroHandler.h"
#include "CMapService.h"
#include "CMap.h"
namespace fs = boost::filesystem;
/*
* CCampaignHandler.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
*
*/
CCampaignHeader CCampaignHandler::getHeader( const std::string & name)
{
std::vector<ui8> cmpgn = getFile(name, true)[0];
int it = 0;//iterator for reading
CCampaignHeader ret = readHeaderFromMemory(cmpgn.data(), it);
ret.filename = name;
return ret;
}
unique_ptr<CCampaign> CCampaignHandler::getCampaign( const std::string & name )
{
auto ret = make_unique<CCampaign>();
std::vector<std::vector<ui8>> file = getFile(name, false);
int it = 0; //iterator for reading
ret->header = readHeaderFromMemory(file[0].data(), it);
ret->header.filename = name;
int howManyScenarios = VLC->generaltexth->campaignRegionNames[ret->header.mapVersion].size();
for(int g=0; g<howManyScenarios; ++g)
{
CCampaignScenario sc = readScenarioFromMemory(file[0].data(), it, ret->header.version, ret->header.mapVersion);
ret->scenarios.push_back(sc);
}
int scenarioID = 0;
//first entry is campaign header. start loop from 1
for (int g=1; g<file.size() && scenarioID<howManyScenarios; ++g)
{
while(!ret->scenarios[scenarioID].isNotVoid()) //skip void scenarios
{
scenarioID++;
}
//set map piece appropriately, convert vector to string
ret->mapPieces[scenarioID].assign(reinterpret_cast< const char* >(file[g].data()), file[g].size());
ret->scenarios[scenarioID].scenarioName = CMapService::loadMapHeader((const ui8*)ret->mapPieces[scenarioID].c_str(), ret->mapPieces[scenarioID].size())->name;
scenarioID++;
}
return ret;
}
CCampaignHeader CCampaignHandler::readHeaderFromMemory( const ui8 *buffer, int & outIt )
{
CCampaignHeader ret;
ret.version = read_le_u32(buffer + outIt); outIt+=4;
ret.mapVersion = buffer[outIt++]; //1 byte only
ret.mapVersion -= 1; //change range of it from [1, 20] to [0, 19]
ret.name = readString(buffer, outIt);
ret.description = readString(buffer, outIt);
if (ret.version > CampaignVersion::RoE)
ret.difficultyChoosenByPlayer = readChar(buffer, outIt);
else
ret.difficultyChoosenByPlayer = 0;
ret.music = readChar(buffer, outIt);
return ret;
}
CCampaignScenario CCampaignHandler::readScenarioFromMemory( const ui8 *buffer, int & outIt, int version, int mapVersion )
{
struct HLP
{
//reads prolog/epilog info from memory
static CCampaignScenario::SScenarioPrologEpilog prologEpilogReader( const ui8 *buffer, int & outIt )
{
CCampaignScenario::SScenarioPrologEpilog ret;
ret.hasPrologEpilog = buffer[outIt++];
if(ret.hasPrologEpilog)
{
ret.prologVideo = buffer[outIt++];
ret.prologMusic = buffer[outIt++];
ret.prologText = readString(buffer, outIt);
}
return ret;
}
};
CCampaignScenario ret;
ret.conquered = false;
ret.mapName = readString(buffer, outIt);
ret.packedMapSize = read_le_u32(buffer + outIt); outIt += 4;
if(mapVersion == 18)//unholy alliance
{
ret.loadPreconditionRegions(read_le_u16(buffer + outIt)); outIt += 2;
}
else
{
ret.loadPreconditionRegions(buffer[outIt++]);
}
ret.regionColor = buffer[outIt++];
ret.difficulty = buffer[outIt++];
ret.regionText = readString(buffer, outIt);
ret.prolog = HLP::prologEpilogReader(buffer, outIt);
ret.epilog = HLP::prologEpilogReader(buffer, outIt);
ret.travelOptions = readScenarioTravelFromMemory(buffer, outIt, version);
return ret;
}
void CCampaignScenario::loadPreconditionRegions(ui32 regions)
{
for (int i=0; i<32; i++) //for each bit in region. h3c however can only hold up to 16
{
if ( (1 << i) & regions)
preconditionRegions.insert(i);
}
}
CScenarioTravel CCampaignHandler::readScenarioTravelFromMemory( const ui8 * buffer, int & outIt , int version )
{
CScenarioTravel ret;
ret.whatHeroKeeps = buffer[outIt++];
memcpy(ret.monstersKeptByHero, buffer+outIt, ARRAY_COUNT(ret.monstersKeptByHero));
outIt += ARRAY_COUNT(ret.monstersKeptByHero);
int artifBytes;
if (version < CampaignVersion::SoD)
{
artifBytes = 17;
ret.artifsKeptByHero[17] = 0;
}
else
{
artifBytes = 18;
}
memcpy(ret.artifsKeptByHero, buffer+outIt, artifBytes);
outIt += artifBytes;
ret.startOptions = buffer[outIt++];
switch(ret.startOptions)
{
case 0:
//no bonuses. Seems to be OK
break;
case 1: //reading of bonuses player can choose
{
ret.playerColor = buffer[outIt++];
ui8 numOfBonuses = buffer[outIt++];
for (int g=0; g<numOfBonuses; ++g)
{
CScenarioTravel::STravelBonus bonus;
bonus.type = static_cast<CScenarioTravel::STravelBonus::EBonusType>(buffer[outIt++]);
//hero: FFFD means 'most powerful' and FFFE means 'generated'
switch(bonus.type)
{
case CScenarioTravel::STravelBonus::SPELL:
{
bonus.info1 = read_le_u16(buffer + outIt); outIt += 2; //hero
bonus.info2 = buffer[outIt++]; //spell ID
break;
}
case CScenarioTravel::STravelBonus::MONSTER:
{
bonus.info1 = read_le_u16(buffer + outIt); outIt += 2; //hero
bonus.info2 = read_le_u16(buffer + outIt); outIt += 2; //monster type
bonus.info3 = read_le_u16(buffer + outIt); outIt += 2; //monster count
break;
}
case CScenarioTravel::STravelBonus::BUILDING:
{
bonus.info1 = buffer[outIt++]; //building ID (0 - town hall, 1 - city hall, 2 - capitol, etc)
break;
}
case CScenarioTravel::STravelBonus::ARTIFACT:
{
bonus.info1 = read_le_u16(buffer + outIt); outIt += 2; //hero
bonus.info2 = read_le_u16(buffer + outIt); outIt += 2; //artifact ID
break;
}
case CScenarioTravel::STravelBonus::SPELL_SCROLL:
{
bonus.info1 = read_le_u16(buffer + outIt); outIt += 2; //hero
bonus.info2 = buffer[outIt++]; //spell ID
break;
}
case CScenarioTravel::STravelBonus::PRIMARY_SKILL:
{
bonus.info1 = read_le_u16(buffer + outIt); outIt += 2; //hero
bonus.info2 = read_le_u32(buffer + outIt); outIt += 4; //bonuses (4 bytes for 4 skills)
break;
}
case CScenarioTravel::STravelBonus::SECONDARY_SKILL:
{
bonus.info1 = read_le_u16(buffer + outIt); outIt += 2; //hero
bonus.info2 = buffer[outIt++]; //skill ID
bonus.info3 = buffer[outIt++]; //skill level
break;
}
case CScenarioTravel::STravelBonus::RESOURCE:
{
bonus.info1 = buffer[outIt++]; //type
//FD - wood+ore
//FE - mercury+sulfur+crystal+gem
bonus.info2 = read_le_u32(buffer + outIt); outIt += 4; //count
break;
}
default:
tlog1<<"Corrupted h3c file"<<std::endl;
break;
}
ret.bonusesToChoose.push_back(bonus);
}
break;
}
case 2: //reading of players (colors / scenarios ?) player can choose
{
ui8 numOfBonuses = buffer[outIt++];
for (int g=0; g<numOfBonuses; ++g)
{
CScenarioTravel::STravelBonus bonus;
bonus.type = CScenarioTravel::STravelBonus::PLAYER_PREV_SCENARIO;
bonus.info1 = buffer[outIt++]; //player color
bonus.info2 = buffer[outIt++]; //from what scenario
ret.bonusesToChoose.push_back(bonus);
}
break;
}
case 3: //heroes player can choose between
{
ui8 numOfBonuses = buffer[outIt++];
for (int g=0; g<numOfBonuses; ++g)
{
CScenarioTravel::STravelBonus bonus;
bonus.type = CScenarioTravel::STravelBonus::HERO;
bonus.info1 = buffer[outIt++]; //player color
bonus.info2 = read_le_u16(buffer + outIt); outIt += 2; //hero, FF FF - random
ret.bonusesToChoose.push_back(bonus);
}
break;
}
default:
{
tlog1<<"Corrupted h3c file"<<std::endl;
break;
}
}
return ret;
}
std::vector< std::vector<ui8> > CCampaignHandler::getFile(const std::string & name, bool headerOnly)
{
CCompressedStream stream(std::move(CResourceHandler::get()->load(ResourceID(name, EResType::CAMPAIGN))), true);
std::vector< std::vector<ui8> > ret;
do
{
std::vector<ui8> block(stream.getSize());
stream.read(block.data(), block.size());
ret.push_back(block);
}
while (!headerOnly && stream.getNextBlock());
return ret;
}
bool CCampaign::conquerable( int whichScenario ) const
{
//check for void scenraio
if (!scenarios[whichScenario].isNotVoid())
{
return false;
}
if (scenarios[whichScenario].conquered)
{
return false;
}
//check preconditioned regions
for (int g=0; g<scenarios.size(); ++g)
{
if( vstd::contains(scenarios[whichScenario].preconditionRegions, g) && !scenarios[g].conquered)
return false; //prerequisite does not met
}
return true;
}
CCampaign::CCampaign()
{
}
bool CCampaignScenario::isNotVoid() const
{
return mapName.size() > 0;
}
void CCampaignScenario::prepareCrossoverHeroes( std::vector<CGHeroInstance *> heroes )
{
crossoverHeroes = heroes;
if (!(travelOptions.whatHeroKeeps & 1))
{
//trimming experience
BOOST_FOREACH(CGHeroInstance * cgh, crossoverHeroes)
{
cgh->initExp();
}
}
if (!(travelOptions.whatHeroKeeps & 2))
{
//trimming prim skills
BOOST_FOREACH(CGHeroInstance * cgh, crossoverHeroes)
{
for(int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
{
cgh->getBonusLocalFirst(Selector::type(Bonus::PRIMARY_SKILL) &&
Selector::subtype(g) && Selector::sourceType(Bonus::HERO_BASE_SKILL) )->val
= cgh->type->heroClass->primarySkillInitial[g];
}
}
}
if (!(travelOptions.whatHeroKeeps & 4))
{
//trimming sec skills
BOOST_FOREACH(CGHeroInstance * cgh, crossoverHeroes)
{
cgh->secSkills = cgh->type->secSkillsInit;
}
}
if (!(travelOptions.whatHeroKeeps & 8))
{
//trimming spells
BOOST_FOREACH(CGHeroInstance * cgh, crossoverHeroes)
{
cgh->spells.clear();
}
}
if (!(travelOptions.whatHeroKeeps & 16))
{
//trimming artifacts
BOOST_FOREACH(CGHeroInstance * hero, crossoverHeroes)
{
size_t totalArts = GameConstants::BACKPACK_START + hero->artifactsInBackpack.size();
for (size_t i=0; i<totalArts; i++ )
{
const ArtSlotInfo *info = hero->getSlot(ArtifactPosition(i));
if (!info)
continue;
const CArtifactInstance *art = info->artifact;
if (!art)//FIXME: check spellbook and catapult behaviour
continue;
int id = art->artType->id;
assert( 8*18 > id );//number of arts that fits into h3m format
bool takeable = travelOptions.artifsKeptByHero[id / 8] & ( 1 << (id%8) );
if (!takeable)
hero->eraseArtSlot(ArtifactPosition(i));
}
}
}
//trimming creatures
BOOST_FOREACH(CGHeroInstance * cgh, crossoverHeroes)
{
vstd::erase_if(cgh->stacks, [&](const std::pair<SlotID, CStackInstance *> & j) -> bool
{
CreatureID::ECreatureID crid = j.second->getCreatureID().toEnum();
return !(travelOptions.monstersKeptByHero[crid / 8] & (1 << (crid % 8)) );
});
}
}
const CGHeroInstance * CCampaignScenario::strongestHero( PlayerColor owner ) const
{
using boost::adaptors::filtered;
std::function<bool(CGHeroInstance*)> isOwned = [=](const CGHeroInstance *h){ return h->tempOwner == owner; };
auto ownedHeroes = crossoverHeroes | filtered(isOwned);
auto i = vstd::maxElementByFun(ownedHeroes,
[](const CGHeroInstance * h) {return h->getTotalStrength();});
return i == ownedHeroes.end() ? nullptr : *i;
}
bool CScenarioTravel::STravelBonus::isBonusForHero() const
{
return type == SPELL || type == MONSTER || type == ARTIFACT || type == SPELL_SCROLL || type == PRIMARY_SKILL
|| type == SECONDARY_SKILL;
}
// void CCampaignState::initNewCampaign( const StartInfo &si )
// {
// assert(si.mode == StartInfo::CAMPAIGN);
// campaignName = si.mapname;
// currentMap = si.campState->currentMap;
//
// camp = CCampaignHandler::getCampaign(campaignName);
// for (ui8 i = 0; i < camp->mapPieces.size(); i++)
// mapsRemaining.push_back(i);
// }
void CCampaignState::mapConquered( const std::vector<CGHeroInstance*> & heroes )
{
camp->scenarios[currentMap].prepareCrossoverHeroes(heroes);
mapsConquered.push_back(currentMap);
mapsRemaining -= currentMap;
camp->scenarios[currentMap].conquered = true;
}
boost::optional<CScenarioTravel::STravelBonus> CCampaignState::getBonusForCurrentMap() const
{
auto bonuses = getCurrentScenario().travelOptions.bonusesToChoose;
assert(chosenCampaignBonuses.count(currentMap) || bonuses.size() == 0);
if(bonuses.size())
return bonuses[currentBonusID()];
else
return boost::optional<CScenarioTravel::STravelBonus>();
}
const CCampaignScenario & CCampaignState::getCurrentScenario() const
{
return camp->scenarios[currentMap];
}
ui8 CCampaignState::currentBonusID() const
{
return chosenCampaignBonuses[currentMap];
}
CCampaignState::CCampaignState()
{}
CCampaignState::CCampaignState( unique_ptr<CCampaign> _camp ) : camp(std::move(_camp))
{
for(int i = 0; i < camp->scenarios.size(); i++)
{
if(vstd::contains(camp->mapPieces, i)) //not all maps must be present in a campaign
mapsRemaining.push_back(i);
}
}
std::string CCampaignHandler::prologVideoName(ui8 index)
{
JsonNode config(ResourceID(std::string("CONFIG/campaignMedia"), EResType::TEXT));
auto vids = config["videos"].Vector();
if(index < vids.size())
return vids[index].String();
return "";
}
std::string CCampaignHandler::prologMusicName(ui8 index)
{
return "";
}

View File

@@ -0,0 +1,188 @@
#pragma once
#include "../../Global.h"
#include "../../lib/GameConstants.h"
/*
* CCampaignHandler.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
*
*/
struct StartInfo;
class CGHeroInstance;
namespace CampaignVersion
{
enum Version
{
RoE = 4,
AB = 5,
SoD = 6,
WoG = 6
};
}
class DLL_LINKAGE CCampaignHeader
{
public:
si32 version; //4 - RoE, 5 - AB, 6 - SoD and WoG
ui8 mapVersion; //CampText.txt's format
std::string name, description;
ui8 difficultyChoosenByPlayer;
ui8 music; //CmpMusic.txt, start from 0
std::string filename;
ui8 loadFromLod; //if true, this campaign must be loaded fro, .lod file
template <typename Handler> void serialize(Handler &h, const int formatVersion)
{
h & version & mapVersion & name & description & difficultyChoosenByPlayer & music & filename & loadFromLod;
}
};
class DLL_LINKAGE CScenarioTravel
{
public:
ui8 whatHeroKeeps; //bitfield [0] - experience, [1] - prim skills, [2] - sec skills, [3] - spells, [4] - artifacts
ui8 monstersKeptByHero[19];
ui8 artifsKeptByHero[18];
ui8 startOptions; //1 - start bonus, 2 - traveling hero, 3 - hero options
ui8 playerColor; //only for startOptions == 1
struct DLL_LINKAGE STravelBonus
{
enum EBonusType {SPELL, MONSTER, BUILDING, ARTIFACT, SPELL_SCROLL, PRIMARY_SKILL, SECONDARY_SKILL, RESOURCE,
PLAYER_PREV_SCENARIO, HERO};
EBonusType type; //uses EBonusType
si32 info1, info2, info3; //purpose depends on type
bool isBonusForHero() const;
template <typename Handler> void serialize(Handler &h, const int formatVersion)
{
h & type & info1 & info2 & info3;
}
};
std::vector<STravelBonus> bonusesToChoose;
template <typename Handler> void serialize(Handler &h, const int formatVersion)
{
h & whatHeroKeeps & monstersKeptByHero & artifsKeptByHero & startOptions & playerColor & bonusesToChoose;
}
};
class DLL_LINKAGE CCampaignScenario
{
public:
std::string mapName; //*.h3m
std::string scenarioName; //from header. human-readble
ui32 packedMapSize; //generally not used
std::set<ui8> preconditionRegions; //what we need to conquer to conquer this one (stored as bitfield in h3c)
ui8 regionColor;
ui8 difficulty;
bool conquered;
std::string regionText;
struct DLL_LINKAGE SScenarioPrologEpilog
{
bool hasPrologEpilog;
ui8 prologVideo; // from CmpMovie.txt
ui8 prologMusic; // from CmpMusic.txt
std::string prologText;
template <typename Handler> void serialize(Handler &h, const int formatVersion)
{
h & hasPrologEpilog & prologVideo & prologMusic & prologText;
}
};
SScenarioPrologEpilog prolog, epilog;
CScenarioTravel travelOptions;
std::vector<CGHeroInstance *> crossoverHeroes;
const CGHeroInstance * strongestHero(PlayerColor owner) const;
void loadPreconditionRegions(ui32 regions);
void prepareCrossoverHeroes(std::vector<CGHeroInstance *> heroes);
bool isNotVoid() const;
template <typename Handler> void serialize(Handler &h, const int formatVersion)
{
h & mapName & scenarioName & packedMapSize & preconditionRegions & regionColor & difficulty & conquered & regionText &
prolog & epilog & travelOptions & crossoverHeroes;
}
};
class DLL_LINKAGE CCampaign
{
public:
CCampaignHeader header;
std::vector<CCampaignScenario> scenarios;
std::map<int, std::string > mapPieces; //binary h3ms, scenario number -> map data
template <typename Handler> void serialize(Handler &h, const int formatVersion)
{
h & header & scenarios & mapPieces;
}
bool conquerable(int whichScenario) const;
CCampaign();
};
class DLL_LINKAGE CCampaignState
{
public:
unique_ptr<CCampaign> camp;
std::string campaignName;
std::vector<ui8> mapsConquered, mapsRemaining;
ui8 currentMap;
bmap<ui8, ui8> chosenCampaignBonuses; //used only for mode CAMPAIGN
//void initNewCampaign(const StartInfo &si);
void mapConquered(const std::vector<CGHeroInstance*> & heroes);
boost::optional<CScenarioTravel::STravelBonus> getBonusForCurrentMap() const;
const CCampaignScenario &getCurrentScenario() const;
ui8 currentBonusID() const;
CCampaignState();
CCampaignState(unique_ptr<CCampaign> _camp);
~CCampaignState(){};
template <typename Handler> void serialize(Handler &h, const int version)
{
h & camp & campaignName & mapsRemaining & mapsConquered & currentMap;
h & chosenCampaignBonuses;
}
};
class DLL_LINKAGE CCampaignHandler
{
static CCampaignHeader readHeaderFromMemory( const ui8 *buffer, int & outIt );
static CCampaignScenario readScenarioFromMemory( const ui8 *buffer, int & outIt, int version, int mapVersion );
static CScenarioTravel readScenarioTravelFromMemory( const ui8 * buffer, int & outIt , int version);
/// returns h3c split in parts. 0 = h3c header, 1-end - maps (binary h3m)
/// headerOnly - only header will be decompressed, returned vector wont have any maps
static std::vector< std::vector<ui8> > getFile(const std::string & name, bool headerOnly);
public:
static std::string prologVideoName(ui8 index);
static std::string prologMusicName(ui8 index);
static CCampaignHeader getHeader( const std::string & name); //name - name of appropriate file
static unique_ptr<CCampaign> getCampaign(const std::string & name); //name - name of appropriate file
};

309
lib/mapping/CMap.cpp Normal file
View File

@@ -0,0 +1,309 @@
#include "StdInc.h"
#include "CMap.h"
#include "../CArtHandler.h"
#include "../VCMI_Lib.h"
#include "../CTownHandler.h"
#include "../CHeroHandler.h"
#include "../CDefObjInfoHandler.h"
#include "../CSpellHandler.h"
SHeroName::SHeroName() : heroId(-1)
{
}
PlayerInfo::PlayerInfo(): canHumanPlay(false), canComputerPlay(false),
aiTactic(EAiTactic::RANDOM), isFactionRandom(false), mainHeroPortrait(-1), hasMainTown(false),
generateHeroAtMainTown(false), team(255), generateHero(false), p7(0), hasHero(false), customHeroID(-1), powerPlaceholders(-1)
{
allowedFactions = VLC->townh->getDefaultAllowedFactions();
}
si8 PlayerInfo::defaultCastle() const
{
if(allowedFactions.size() == 1 || !isFactionRandom)
{
// faction can't be chosen - set to first that is marked as allowed
assert(!allowedFactions.empty());
return *allowedFactions.begin();
}
// set to random
return -1;
}
si8 PlayerInfo::defaultHero() const
{
// we will generate hero in front of main town
if((generateHeroAtMainTown && hasMainTown) || hasHero)
{
//random hero
return -1;
}
return -2;
}
LossCondition::LossCondition() : typeOfLossCon(ELossConditionType::LOSSSTANDARD),
pos(int3(-1, -1, -1)), timeLimit(-1), obj(nullptr)
{
}
VictoryCondition::VictoryCondition() : condition(EVictoryConditionType::WINSTANDARD),
allowNormalVictory(false), appliesToAI(false), pos(int3(-1, -1, -1)), objectId(0),
count(0), obj(nullptr)
{
}
DisposedHero::DisposedHero() : heroId(0), portrait(255), players(0)
{
}
CMapEvent::CMapEvent() : players(0), humanAffected(0), computerAffected(0),
firstOccurence(0), nextOccurence(0)
{
}
bool CMapEvent::earlierThan(const CMapEvent & other) const
{
return firstOccurence < other.firstOccurence;
}
bool CMapEvent::earlierThanOrEqual(const CMapEvent & other) const
{
return firstOccurence <= other.firstOccurence;
}
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),
blocked(false)
{
}
bool TerrainTile::entrableTerrain(const TerrainTile * from /*= NULL*/) const
{
return entrableTerrain(from ? from->terType != ETerrainType::WATER : true, from ? from->terType == ETerrainType::WATER : true);
}
bool TerrainTile::entrableTerrain(bool allowLand, bool allowSea) const
{
return terType != ETerrainType::ROCK
&& ((allowSea && terType == ETerrainType::WATER) || (allowLand && terType != ETerrainType::WATER));
}
bool TerrainTile::isClear(const TerrainTile *from /*= NULL*/) const
{
return entrableTerrain(from) && !blocked;
}
int TerrainTile::topVisitableId() const
{
return visitableObjects.size() ? visitableObjects.back()->ID : -1;
}
bool TerrainTile::isCoastal() const
{
return extTileFlags & 64;
}
bool TerrainTile::hasFavourableWinds() const
{
return extTileFlags & 128;
}
bool TerrainTile::isWater() const
{
return terType == ETerrainType::WATER;
}
CMapHeader::CMapHeader() : version(EMapFormat::SOD), height(72), width(72),
twoLevel(true), difficulty(1), levelLimit(0), howManyTeams(0), areAnyPlayers(false)
{
allowedHeroes = VLC->heroh->getDefaultAllowedHeroes();
players.resize(PlayerColor::PLAYER_LIMIT_I);
}
CMapHeader::~CMapHeader()
{
}
CMap::CMap() : checksum(0), terrain(nullptr), grailRadious(0)
{
allowedAbilities = VLC->heroh->getDefaultAllowedAbilities();
allowedArtifact = VLC->arth->getDefaultAllowedArtifacts();
allowedSpell = VLC->spellh->getDefaultAllowedSpells();
}
CMap::~CMap()
{
if(terrain)
{
for(int ii=0;ii<width;ii++)
{
for(int jj=0;jj<height;jj++)
delete [] terrain[ii][jj];
delete [] terrain[ii];
}
delete [] terrain;
}
}
void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
{
for(int fx=0; fx<8; ++fx)
{
for(int fy=0; fy<6; ++fy)
{
int xVal = obj->pos.x + fx - 7;
int yVal = obj->pos.y + fy - 5;
int zVal = obj->pos.z;
if(xVal>=0 && xVal<width && yVal>=0 && yVal<height)
{
TerrainTile & curt = terrain[xVal][yVal][zVal];
if(total || ((obj->defInfo->visitMap[fy] >> (7 - fx)) & 1))
{
curt.visitableObjects -= obj;
curt.visitable = curt.visitableObjects.size();
}
if(total || !((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1))
{
curt.blockingObjects -= obj;
curt.blocked = curt.blockingObjects.size();
}
}
}
}
}
void CMap::addBlockVisTiles(CGObjectInstance * obj)
{
for(int fx=0; fx<8; ++fx)
{
for(int fy=0; fy<6; ++fy)
{
int xVal = obj->pos.x + fx - 7;
int yVal = obj->pos.y + fy - 5;
int zVal = obj->pos.z;
if(xVal>=0 && xVal<width && yVal>=0 && yVal<height)
{
TerrainTile & curt = terrain[xVal][yVal][zVal];
if(((obj->defInfo->visitMap[fy] >> (7 - fx)) & 1))
{
curt.visitableObjects.push_back(obj);
curt.visitable = true;
}
if(!((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1))
{
curt.blockingObjects.push_back(obj);
curt.blocked = true;
}
}
}
}
}
CGHeroInstance * CMap::getHero(int heroID)
{
for(ui32 i=0; i<heroes.size();i++)
if(heroes[i]->subID == heroID)
return heroes[i];
return nullptr;
}
bool CMap::isInTheMap(const int3 &pos) const
{
if(pos.x < 0 || pos.y < 0 || pos.z < 0 || pos.x >= width || pos.y >= height
|| pos.z > (twoLevel ? 1 : 0))
{
return false;
}
else
{
return true;
}
}
TerrainTile & CMap::getTile( const int3 & tile )
{
return terrain[tile.x][tile.y][tile.z];
}
const TerrainTile & CMap::getTile( const int3 & tile ) const
{
return terrain[tile.x][tile.y][tile.z];
}
bool CMap::isWaterTile(const int3 &pos) const
{
return isInTheMap(pos) && getTile(pos).terType == ETerrainType::WATER;
}
const CGObjectInstance * CMap::getObjectiveObjectFrom(int3 pos, bool lookForHero)
{
const std::vector<CGObjectInstance *> & objs = getTile(pos).visitableObjects;
assert(objs.size());
if(objs.size() > 1 && lookForHero && objs.front()->ID != Obj::HERO)
{
assert(objs.back()->ID == Obj::HERO);
return objs.back();
}
else
return objs.front();
}
void CMap::checkForObjectives()
{
if(isInTheMap(victoryCondition.pos))
{
victoryCondition.obj = getObjectiveObjectFrom(victoryCondition.pos, victoryCondition.condition == EVictoryConditionType::BEATHERO);
}
if(isInTheMap(lossCondition.pos))
{
lossCondition.obj = getObjectiveObjectFrom(lossCondition.pos, lossCondition.typeOfLossCon == ELossConditionType::LOSSHERO);
}
}
void CMap::addNewArtifactInstance(CArtifactInstance * art)
{
art->id = ArtifactInstanceID(artInstances.size());
artInstances.push_back(art);
}
void CMap::eraseArtifactInstance(CArtifactInstance * art)
{
assert(artInstances[art->id.getNum()] == art);
artInstances[art->id.getNum()].dellNull();
}
void CMap::addQuest(CGObjectInstance * quest)
{
auto q = dynamic_cast<IQuestObject *>(quest);
q->quest->qid = quests.size();
quests.push_back(q->quest);
}
void CMap::initTerrain()
{
terrain = new TerrainTile**[width];
for(int i = 0; i < width; ++i)
{
terrain[i] = new TerrainTile*[height];
for(int j = 0; j < height; ++j)
{
terrain[i][j] = new TerrainTile[twoLevel ? 2 : 1];
}
}
}

897
lib/mapping/CMap.h Normal file
View File

@@ -0,0 +1,897 @@
/*
* CMap.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 "../CObjectHandler.h"
#include "../ResourceSet.h"
#include "../int3.h"
#include "../GameConstants.h"
class CArtifactInstance;
class CGDefInfo;
class CGObjectInstance;
class CGHeroInstance;
class CCommanderInstance;
class CGCreature;
class CQuest;
class CGTownInstance;
class IModableArt;
class IQuestObject;
class CInputStream;
/**
* The hero name struct consists of the hero id and the hero name.
*/
struct DLL_LINKAGE SHeroName
{
/**
* Default c-tor.
*/
SHeroName();
/** the id of the hero */
int heroId;
/** the name of the hero */
std::string heroName;
/**
* Serialize method.
*/
template <typename Handler>
void serialize(Handler & h, const int version)
{
h & heroId & heroName;
}
};
namespace EAiTactic
{
enum EAiTactic
{
NONE = -1,
RANDOM,
WARRIOR,
BUILDER,
EXPLORER
};
}
/**
* The player info constains data about which factions are allowed, AI tactical settings,
* the main hero name, where to generate the hero, whether the faction should be selected randomly,...
*/
struct DLL_LINKAGE PlayerInfo
{
/**
* Default constructor.
*/
PlayerInfo();
/**
* Gets the default faction id or -1 for a random faction.
*
* @return the default faction id or -1 for a random faction
*/
si8 defaultCastle() const;
/**
* Gets -1 for random hero.
*
* @return -1 for random hero
*/
si8 defaultHero() const;
/** True if the player can be played by a human. */
bool canHumanPlay;
/** True if th player can be played by the computer */
bool canComputerPlay;
/** Defines the tactical setting of the AI. The default value is EAiTactic::RANDOM. */
EAiTactic::EAiTactic aiTactic;
/** A list of unique IDs of allowed factions. */
std::set<TFaction> allowedFactions;
/** Unused. True if the faction should be chosen randomly. */
bool isFactionRandom;
/** Specifies the ID of the main hero with chosen portrait. The default value is -1. */
si32 mainHeroPortrait;
/** The name of the main hero. */
std::string mainHeroName;
/** The list of renamed heroes. */
std::vector<SHeroName> heroesNames;
/** True if the player has a main town. The default value is false. */
bool hasMainTown;
/** True if the main hero should be generated at the main town. The default value is false. */
bool generateHeroAtMainTown;
/** The position of the main town. */
int3 posOfMainTown;
/** The team id to which the player belongs to. The default value is 255 representing that the player belongs to no team. */
TeamID team;
/** Unused. True if a hero should be generated. */
bool generateHero;
/** Unknown and unused. */
si32 p7;
/** Player has a (custom?) hero */
bool hasHero;
/** ID of custom hero, -1 if none */
si32 customHeroID;
/**
* Unused. Count of hero placeholders containing hero type.
* WARNING: powerPlaceholders sometimes gives false 0 (eg. even if there is one placeholder),
* maybe different meaning ???
*/
ui8 powerPlaceholders;
/**
* Serialize method.
*/
template <typename Handler>
void serialize(Handler & h, const int version)
{
h & p7 & hasHero & customHeroID & canHumanPlay & canComputerPlay & aiTactic & allowedFactions & isFactionRandom &
mainHeroPortrait & mainHeroName & heroesNames & hasMainTown & generateHeroAtMainTown &
posOfMainTown & team & generateHero;
}
};
/**
* The loss condition describes the condition to lose the game. (e.g. lose all own heroes/castles)
*/
struct DLL_LINKAGE LossCondition
{
/**
* Default constructor.
*/
LossCondition();
/** specifies the condition type */
ELossConditionType::ELossConditionType typeOfLossCon;
/** the position of an object which mustn't be lost */
int3 pos;
/** time limit in days, -1 if not used */
si32 timeLimit;
/** set during map parsing: hero/town (depending on typeOfLossCon); nullptr if not used */
const CGObjectInstance * obj;
/**
* Serialize method.
*/
template <typename Handler>
void serialize(Handler & h, const int version)
{
h & typeOfLossCon & pos & timeLimit & obj;
}
};
/**
* The victory condition describes the condition to win the game. (e.g. defeat all enemy heroes/castles,
* receive a specific artifact, ...)
*/
struct DLL_LINKAGE VictoryCondition
{
/**
* Default constructor.
*/
VictoryCondition();
/** specifies the condition type */
EVictoryConditionType::EVictoryConditionType condition;
/** true if a normal victory is allowed (defeat all enemy towns, heroes) */
bool allowNormalVictory;
/** true if this victory condition also applies to the AI */
bool appliesToAI;
/** pos of city to upgrade (3); pos of town to build grail, {-1,-1,-1} if not relevant (4); hero pos (5); town pos(6); monster pos (7); destination pos(8) */
int3 pos;
/** artifact ID (0); monster ID (1); resource ID (2); needed fort level in upgraded town (3); artifact ID (8) */
si32 objectId;
/** needed count for creatures (1) / resource (2); upgraded town hall level (3); */
si32 count;
/** object of specific monster / city / hero instance (NULL if not used); set during map parsing */
const CGObjectInstance * obj;
/**
* Serialize method.
*/
template <typename Handler>
void serialize(Handler & h, const int version)
{
h & condition & allowNormalVictory & appliesToAI & pos & objectId & count & obj;
}
};
/**
* The rumor struct consists of a rumor name and text.
*/
struct DLL_LINKAGE Rumor
{
/** the name of the rumor */
std::string name;
/** the content of the rumor */
std::string text;
/**
* Serialize method.
*/
template <typename Handler>
void serialize(Handler & h, const int version)
{
h & name & text;
}
};
/**
* The disposed hero struct describes which hero can be hired from which player.
*/
struct DLL_LINKAGE DisposedHero
{
/**
* Default c-tor.
*/
DisposedHero();
/** the id of the hero */
ui32 heroId;
/** the portrait id of the hero, 0xFF is default */
ui16 portrait;
/** the name of the hero */
std::string name;
/** who can hire this hero (bitfield) */
ui8 players;
/**
* Serialize method.
*/
template <typename Handler>
void serialize(Handler & h, const int version)
{
h & heroId & portrait & name & players;
}
};
/**
* The map event is an event which e.g. gives or takes resources of a specific
* amount to/from players and can appear regularly or once a time.
*/
class DLL_LINKAGE CMapEvent
{
public:
/**
* Default c-tor.
*/
CMapEvent();
/**
* Returns true if this map event occurs earlier than the other map event for the first time.
*
* @param other the other map event to compare with
* @return true if this event occurs earlier than the other map event, false if not
*/
bool earlierThan(const CMapEvent & other) const;
/**
* Returns true if this map event occurs earlier than or at the same day than the other map event for the first time.
*
* @param other the other map event to compare with
* @return true if this event occurs earlier than or at the same day than the other map event, false if not
*/
bool earlierThanOrEqual(const CMapEvent & other) const;
/** the name of the event */
std::string name;
/** the message to display */
std::string message;
/** gained or taken resources */
TResources resources;
/** affected players */
ui8 players;
/** affected humans */
ui8 humanAffected;
/** affacted computer players */
ui8 computerAffected;
/** the day counted continously when the event happens */
ui32 firstOccurence;
/** specifies after how many days the event will occur the next time; 0 if event occurs only one time */
ui32 nextOccurence;
/**
* Serialize method.
*/
template <typename Handler>
void serialize(Handler & h, const int version)
{
h & name & message & resources
& players & humanAffected & computerAffected & firstOccurence & nextOccurence;
}
};
/**
* The castle event builds/adds buildings/creatures for a specific town.
*/
class DLL_LINKAGE CCastleEvent: public CMapEvent
{
public:
/**
* Default c-tor.
*/
CCastleEvent();
/** build specific buildings */
std::set<BuildingID> buildings;
/** additional creatures in i-th level dwelling */
std::vector<si32> creatures;
/** owner of this event */
CGTownInstance * town;
/**
* Serialize method.
*/
template <typename Handler>
void serialize(Handler & h, const int version)
{
h & static_cast<CMapEvent &>(*this);
h & buildings & creatures;
}
};
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
};
}
/**
* The terrain tile describes the terrain type and the visual representation of the terrain.
* Furthermore the struct defines whether the tile is visitable or/and blocked and which objects reside in it.
*/
struct DLL_LINKAGE TerrainTile
{
/**
* Default c-tor.
*/
TerrainTile();
/**
* Gets true if the terrain is not a rock. If from is water/land, same type is also required.
*
* @param from
* @return
*/
bool entrableTerrain(const TerrainTile * from = NULL) const;
/**
* Gets true if the terrain is not a rock. If from is water/land, same type is also required.
*
* @param allowLand
* @param allowSea
* @return
*/
bool entrableTerrain(bool allowLand, bool allowSea) const;
/**
* Checks for blocking objects and terraint type (water / land).
*
* @param from
* @return
*/
bool isClear(const TerrainTile * from = NULL) const;
/**
* Gets the ID of the top visitable object or -1 if there is none.
*
* @return the ID of the top visitable object or -1 if there is none
*/
int topVisitableId() const;
/**
* Gets true if the terrain type is water.
*
* @return true if the terrain type is water
*/
bool isWater() const;
/**
* Gets true if the terrain tile is coastal.
*
* @return true if the terrain tile is coastal
*/
bool isCoastal() const;
/**
* Gets true if the terrain tile has favourable winds.
*
* @return true if the terrain tile has favourable winds
*/
bool hasFavourableWinds() const;
/** the type of terrain */
ETerrainType terType;
/** the visual representation of the terrain */
ui8 terView;
/** the type of the river. 0 if there is no river */
ERiverType::ERiverType riverType;
/** the direction of the river */
ui8 riverDir;
/** the type of the road. 0 if there is no river */
ERoadType::ERoadType roadType;
/** the direction of the road */
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 - Favourable Winds effect
*/
ui8 extTileFlags;
/** true if it is visitable, false if not */
bool visitable;
/** true if it is blocked, false if not */
bool blocked;
/** pointers to objects which the hero can visit while being on this tile */
std::vector<CGObjectInstance *> visitableObjects;
/** pointers to objects that are blocking this tile */
std::vector<CGObjectInstance *> blockingObjects;
/**
* Serialize method.
*/
template <typename Handler>
void serialize(Handler & h, const int version)
{
h & terType & terView & riverType & riverDir & roadType &roadDir & extTileFlags & blocked;
if(!h.saving)
{
visitable = false;
//these flags (and obj vectors) will be restored in map serialization
}
}
};
namespace EMapFormat
{
enum EMapFormat
{
INVALID, WOG=0x33, AB=0x15, ROE=0x0e, SOD=0x1c
};
}
/**
* The map header holds information about loss/victory condition,
* map format, version, players, height, width,...
*/
class DLL_LINKAGE CMapHeader
{
public:
/**
* Default constructor.
*/
CMapHeader();
/**
* D-tor.
*/
virtual ~CMapHeader();
/** The version of the map. The default value is EMapFormat::SOD. */
EMapFormat::EMapFormat version;
/** The height of the map. The default value is 72. */
si32 height;
/** The width of the map. The default value is 72. */
si32 width;
/** Specifies if the map has two levels. The default value is true. */
bool twoLevel;
/** The name of the map. */
std::string name;
/** The description of the map. */
std::string description;
/**
* Specifies the difficulty of the map ranging from 0 easy to 4 impossible.
* The default value is 1 representing a normal map difficulty.
*/
ui8 difficulty;
/**
* Specifies the maximum level to reach for a hero. A value of 0 states that there is no
* maximum level for heroes.
*/
ui8 levelLimit;
/** Specifies the loss condition. The default value is lose all your towns and heroes. */
LossCondition lossCondition;
/** Specifies the victory condition. The default value is defeat all enemies. */
VictoryCondition victoryCondition;
/** A list containing information about players. The default size of the vector is PlayerColor::PLAYER_LIMIT. */
std::vector<PlayerInfo> players;
/** The number of teams. */
ui8 howManyTeams;
/**
* A list of allowed heroes. The index is the hero id and the value = hero allowed.
* The default value is a list of default allowed heroes. See CHeroHandler::getDefaultAllowedHeroes for more info.
*/
std::vector<bool> allowedHeroes;
/** A list of placeholded heroes. The index is the id of a hero type. */
std::vector<ui16> placeholdedHeroes;
/** Unused. True if there are any playable players on the map. */
bool areAnyPlayers;
/**
* Serialize method.
*/
template <typename Handler>
void serialize(Handler & h, const int Version)
{
h & version & name & description & width & height & twoLevel & difficulty & levelLimit & areAnyPlayers;
h & players & lossCondition & victoryCondition & howManyTeams & allowedHeroes;
}
};
/**
* The map contains the map header, the tiles of the terrain, objects,
* heroes, towns, rumors...
*/
class DLL_LINKAGE CMap : public CMapHeader
{
public:
/**
* Default constructor.
*/
CMap();
/**
* Destructor.
*/
~CMap();
/**
* Erases an artifact instance.
*
* @param art the artifact to erase
*/
void eraseArtifactInstance(CArtifactInstance * art);
/**
* Gets the topmost object or the lowermost object depending on the flag
* lookForHero from the specified position.
*
* @param pos the position of the tile
* @param lookForHero true if you want to get the lowermost object, false if
* you want to get the topmost object
* @return the object at the given position and level
*/
const CGObjectInstance * getObjectiveObjectFrom(int3 pos, bool lookForHero);
/**
* Sets the victory/loss condition objectives.
*/
void checkForObjectives();
/**
* Adds an visitable/blocking object to a terrain tile.
*
* @param obj the visitable/blocking object to add to a tile
*/
void addBlockVisTiles(CGObjectInstance * obj);
/**
* Removes an visitable/blocking object from a terrain tile.
*
* @param obj the visitable/blocking object to remove from a tile
* @param total
*/
void removeBlockVisTiles(CGObjectInstance * obj, bool total = false);
/**
* Gets the terrain tile of the specified position.
*
* @param tile the position of the tile
* @return the terrain tile of the specified position
*/
TerrainTile & getTile(const int3 & tile);
/**
* Gets the terrain tile as a const of the specified position.
*
* @param tile the position of the tile
* @return the terrain tile as a const of the specified position
*/
const TerrainTile & getTile(const int3 & tile) const;
/**
* Gets the hero with the given id.
* @param heroId the hero id
* @return the hero with the given id
*/
CGHeroInstance * getHero(int heroId);
/**
* Validates if the position is in the bounds of the map.
*
* @param pos the position to test
* @return true if the position is in the bounds of the map
*/
bool isInTheMap(const int3 & pos) const;
/**
* Validates if the tile at the given position is a water terrain type.
*
* @param pos the position to test
* @return true if the tile at the given position is a water terrain type
*/
bool isWaterTile(const int3 & pos) const;
/**
* Adds the specified artifact instance to the list of artifacts of this map.
*
* @param art the artifact which should be added to the list of artifacts
*/
void addNewArtifactInstance(CArtifactInstance * art);
/**
* Adds the specified quest instance to the list of quests.
*
* @param quest the quest object which should be added to the list of quests
*/
void addQuest(CGObjectInstance * quest);
/**
* Initializes the terrain of the map by allocating memory.
*/
void initTerrain();
/** the checksum of the map */
ui32 checksum;
/** a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground */
TerrainTile*** terrain;
/** list of rumors */
std::vector<Rumor> rumors;
/** list of disposed heroes */
std::vector<DisposedHero> disposedHeroes;
/** list of predefined heroes */
std::vector<ConstTransitivePtr<CGHeroInstance> > predefinedHeroes;
/** list of .def files with definitions from .h3m (may be custom) */
std::vector<ConstTransitivePtr<CGDefInfo> > customDefs;
/** list of allowed spells, index is the spell id */
std::vector<bool> allowedSpell;
/** list of allowed artifacts, index is the artifact id */
std::vector<bool> allowedArtifact;
/** list of allowed abilities, index is the ability id */
std::vector<bool> allowedAbilities;
/** list of map events */
std::list<CMapEvent> events;
/** specifies the position of the grail */
int3 grailPos;
/** specifies the radius of the grail */
int grailRadious;
/** list of objects */
std::vector< ConstTransitivePtr<CGObjectInstance> > objects;
/** list of heroes */
std::vector< ConstTransitivePtr<CGHeroInstance> > heroes;
/** list of towns */
std::vector< ConstTransitivePtr<CGTownInstance> > towns;
/** list of artifacts */
std::vector< ConstTransitivePtr<CArtifactInstance> > artInstances;
/** list of quests */
std::vector< ConstTransitivePtr<CQuest> > quests;
/** associative list to identify which hero/creature id belongs to which object id(index for objects) */
bmap<si32, ObjectInstanceID> questIdentifierToId;
/**
* Serialize method.
*/
template <typename Handler>
void serialize(Handler &h, const int formatVersion)
{
h & static_cast<CMapHeader&>(*this);
h & rumors & allowedSpell & allowedAbilities & allowedArtifact & events & grailPos;
h & artInstances & quests;
h & questIdentifierToId;
//TODO: viccondetails
if(h.saving)
{
// Save terrain
for(int i = 0; i < width ; ++i)
{
for(int j = 0; j < height ; ++j)
{
for(int k = 0; k < (twoLevel ? 2 : 1); ++k)
{
h & terrain[i][j][k];
}
}
}
}
else
{
// Load terrain
terrain = new TerrainTile**[width];
for(int ii = 0; ii < width; ++ii)
{
terrain[ii] = new TerrainTile*[height];
for(int jj = 0; jj < height; ++jj)
{
terrain[ii][jj] = new TerrainTile[twoLevel ? 2 : 1];
}
}
for(int i = 0; i < width ; ++i)
{
for(int j = 0; j < height ; ++j)
{
for(int k = 0; k < (twoLevel ? 2 : 1); ++k)
{
h & terrain[i][j][k];
}
}
}
}
h & customDefs & objects;
// static members
h & CGTeleport::objs;
h & CGTeleport::gates;
h & CGKeys::playerKeyMap;
h & CGMagi::eyelist;
h & CGObelisk::obeliskCount & CGObelisk::visited;
h & CGTownInstance::merchantArtifacts;
h & CGTownInstance::universitySkills;
if(!h.saving)
{
for(ui32 i = 0; i < objects.size(); ++i)
{
if(!objects[i]) continue;
switch (objects[i]->ID)
{
case Obj::HERO:
heroes.push_back (static_cast<CGHeroInstance*>(+objects[i]));
break;
case Obj::TOWN:
towns.push_back (static_cast<CGTownInstance*>(+objects[i]));
break;
}
// recreate blockvis map
addBlockVisTiles(objects[i]);
}
// if hero is visiting/garrisoned in town set appropriate pointers
for(ui32 i = 0; i < heroes.size(); ++i)
{
int3 vistile = heroes[i]->pos;
vistile.x++;
for(ui32 j = 0; j < towns.size(); ++j)
{
// hero stands on the town entrance
if(vistile == towns[j]->pos)
{
if(heroes[i]->inTownGarrison)
{
towns[j]->garrisonHero = heroes[i];
removeBlockVisTiles(heroes[i]);
}
else
{
towns[j]->visitingHero = heroes[i];
}
heroes[i]->visitedTown = towns[j];
break;
}
}
vistile.x -= 2; //manifest pos
const TerrainTile & t = getTile(vistile);
if(t.terType != ETerrainType::WATER) continue;
//hero stands on the water - he must be in the boat
for(ui32 j = 0; j < t.visitableObjects.size(); ++j)
{
if(t.visitableObjects[j]->ID == Obj::BOAT)
{
CGBoat * b = static_cast<CGBoat *>(t.visitableObjects[j]);
heroes[i]->boat = b;
b->hero = heroes[i];
removeBlockVisTiles(b);
break;
}
}
}
}
}
};

View File

@@ -0,0 +1,434 @@
#include "StdInc.h"
#include "CMapEditManager.h"
#include "../JsonNode.h"
#include "../filesystem/CResourceLoader.h"
#include "../CDefObjInfoHandler.h"
const std::string TerrainViewPattern::FLIP_MODE_SAME_IMAGE = "sameImage";
const std::string TerrainViewPattern::FLIP_MODE_DIFF_IMAGES = "diffImages";
const std::string TerrainViewPattern::RULE_DIRT = "D";
const std::string TerrainViewPattern::RULE_SAND = "S";
const std::string TerrainViewPattern::RULE_TRANSITION = "T";
const std::string TerrainViewPattern::RULE_NATIVE = "N";
const std::string TerrainViewPattern::RULE_ANY = "?";
TerrainViewPattern::TerrainViewPattern() : minPoints(0), flipMode(FLIP_MODE_SAME_IMAGE),
terGroup(ETerrainGroup::NORMAL)
{
}
TerrainViewPattern::WeightedRule::WeightedRule() : points(0)
{
}
bool TerrainViewPattern::WeightedRule::isStandardRule() const
{
return TerrainViewPattern::RULE_ANY == name || TerrainViewPattern::RULE_DIRT == name
|| TerrainViewPattern::RULE_NATIVE == name || TerrainViewPattern::RULE_SAND == name
|| TerrainViewPattern::RULE_TRANSITION == name;
}
CTerrainViewPatternConfig::CTerrainViewPatternConfig()
{
const JsonNode config(ResourceID("config/terrainViewPatterns.json"));
const std::map<std::string, ETerrainGroup::ETerrainGroup> terGroups
= boost::assign::map_list_of("normal", ETerrainGroup::NORMAL)("dirt", ETerrainGroup::DIRT)
("sand", ETerrainGroup::SAND)("water", ETerrainGroup::WATER)("rock", ETerrainGroup::ROCK);
BOOST_FOREACH(auto terMapping, terGroups)
{
BOOST_FOREACH(const JsonNode & ptrnNode, config[terMapping.first].Vector())
{
TerrainViewPattern pattern;
// Read pattern data
const JsonVector & data = ptrnNode["data"].Vector();
if(data.size() != 9)
{
throw std::runtime_error("Size of pattern's data vector has to be 9.");
}
for(int i = 0; i < data.size(); ++i)
{
std::string cell = data[i].String();
boost::algorithm::erase_all(cell, " ");
std::vector<std::string> rules;
boost::split(rules, cell, boost::is_any_of(","));
BOOST_FOREACH(std::string ruleStr, rules)
{
std::vector<std::string> ruleParts;
boost::split(ruleParts, ruleStr, boost::is_any_of("-"));
TerrainViewPattern::WeightedRule rule;
rule.name = ruleParts[0];
if(ruleParts.size() > 1)
{
rule.points = boost::lexical_cast<int>(ruleParts[1]);
}
pattern.data[i].push_back(rule);
}
}
// Read mapping
std::string mappingStr = ptrnNode["mapping"].String();
boost::algorithm::erase_all(mappingStr, " ");
std::vector<std::string> mappings;
boost::split(mappings, mappingStr, boost::is_any_of(","));
BOOST_FOREACH(std::string mapping, mappings)
{
std::vector<std::string> range;
boost::split(range, mapping, boost::is_any_of("-"));
pattern.mapping.push_back(std::make_pair(boost::lexical_cast<int>(range[0]),
boost::lexical_cast<int>(range.size() > 1 ? range[1] : range[0])));
}
// Read optional attributes
pattern.id = ptrnNode["id"].String();
pattern.minPoints = static_cast<int>(ptrnNode["minPoints"].Float());
pattern.flipMode = ptrnNode["flipMode"].String();
if(pattern.flipMode.empty())
{
pattern.flipMode = TerrainViewPattern::FLIP_MODE_SAME_IMAGE;
}
pattern.terGroup = terMapping.second;
patterns[terMapping.second].push_back(pattern);
}
}
}
const std::vector<TerrainViewPattern> & CTerrainViewPatternConfig::getPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const
{
return patterns.find(terGroup)->second;
}
const TerrainViewPattern & CTerrainViewPatternConfig::getPatternById(ETerrainGroup::ETerrainGroup terGroup, const std::string & id) const
{
const std::vector<TerrainViewPattern> & groupPatterns = getPatternsForGroup(terGroup);
BOOST_FOREACH(const TerrainViewPattern & pattern, groupPatterns)
{
if(id == pattern.id)
{
return pattern;
}
}
throw std::runtime_error("Pattern with ID not found: " + id);
}
CMapEditManager::CMapEditManager(const CTerrainViewPatternConfig * terViewPatternConfig, CMap * map, int randomSeed /*= std::time(nullptr)*/)
: map(map), terViewPatternConfig(terViewPatternConfig)
{
gen.seed(randomSeed);
}
void CMapEditManager::clearTerrain()
{
for(int i = 0; i < map->width; ++i)
{
for(int j = 0; j < map->height; ++j)
{
map->terrain[i][j][0].terType = ETerrainType::WATER;
map->terrain[i][j][0].terView = gen.getInteger(20, 32);
if(map->twoLevel)
{
map->terrain[i][j][1].terType = ETerrainType::ROCK;
map->terrain[i][j][1].terView = 0;
}
}
}
}
void CMapEditManager::drawTerrain(ETerrainType terType, int posx, int posy, int width, int height, bool underground)
{
bool mapLevel = underground ? 1 : 0;
for(int i = posx; i < posx + width; ++i)
{
for(int j = posy; j < posy + height; ++j)
{
map->terrain[i][j][mapLevel].terType = terType;
}
}
//TODO there are situations where more tiles are affected implicitely
//TODO add coastal bit to extTileFlags appropriately
updateTerrainViews(posx - 1, posy - 1, width + 2, height + 2, mapLevel);
}
void CMapEditManager::updateTerrainViews(int posx, int posy, int width, int height, int mapLevel)
{
for(int i = posx; i < posx + width; ++i)
{
for(int j = posy; j < posy + height; ++j)
{
const std::vector<TerrainViewPattern> & patterns =
terViewPatternConfig->getPatternsForGroup(getTerrainGroup(map->terrain[i][j][mapLevel].terType));
// Detect a pattern which fits best
int bestPattern = -1, bestFlip = -1;
std::string transitionReplacement;
for(int k = 0; k < patterns.size(); ++k)
{
const TerrainViewPattern & pattern = patterns[k];
for(int flip = 0; flip < 4; ++flip)
{
ValidationResult valRslt = validateTerrainView(i, j, mapLevel, flip > 0 ? getFlippedPattern(pattern, flip) : pattern);
if(valRslt.result)
{
tlog5 << "Pattern detected at pos " << i << "x" << j << "x" << mapLevel << ": P-Nr. " << k
<< ", Flip " << flip << ", Repl. " << valRslt.transitionReplacement << std::endl;
bestPattern = k;
bestFlip = flip;
transitionReplacement = valRslt.transitionReplacement;
break;
}
}
}
if(bestPattern == -1)
{
// This shouldn't be the case
tlog2 << "No pattern detected at pos " << i << "x" << j << "x" << mapLevel << std::endl;
continue;
}
// Get mapping
const TerrainViewPattern & pattern = patterns[bestPattern];
std::pair<int, int> mapping;
if(transitionReplacement.empty())
{
mapping = pattern.mapping[0];
}
else
{
mapping = transitionReplacement == TerrainViewPattern::RULE_DIRT ? pattern.mapping[0] : pattern.mapping[1];
}
// Set terrain view
if(pattern.flipMode == TerrainViewPattern::FLIP_MODE_SAME_IMAGE)
{
map->terrain[i][j][mapLevel].terView = gen.getInteger(mapping.first, mapping.second);
map->terrain[i][j][mapLevel].extTileFlags = bestFlip;
}
else
{
int range = (mapping.second - mapping.first) / 4;
map->terrain[i][j][mapLevel].terView = gen.getInteger(mapping.first + bestFlip * range,
mapping.first + (bestFlip + 1) * range - 1);
map->terrain[i][j][mapLevel].extTileFlags = 0;
}
}
}
}
ETerrainGroup::ETerrainGroup CMapEditManager::getTerrainGroup(ETerrainType terType) const
{
switch(terType)
{
case ETerrainType::DIRT:
return ETerrainGroup::DIRT;
case ETerrainType::SAND:
return ETerrainGroup::SAND;
case ETerrainType::WATER:
return ETerrainGroup::WATER;
case ETerrainType::ROCK:
return ETerrainGroup::ROCK;
default:
return ETerrainGroup::NORMAL;
}
}
CMapEditManager::ValidationResult CMapEditManager::validateTerrainView(int posx, int posy, int mapLevel, const TerrainViewPattern & pattern, int recDepth /*= 0*/) const
{
ETerrainType centerTerType = map->terrain[posx][posy][mapLevel].terType;
int totalPoints = 0;
std::string transitionReplacement;
for(int i = 0; i < 9; ++i)
{
// The center, middle cell can be skipped
if(i == 4)
{
continue;
}
// Get terrain group of the current cell
int cx = posx + (i % 3) - 1;
int cy = posy + (i / 3) - 1;
bool isAlien = false;
ETerrainType terType;
if(cx < 0 || cx >= map->width || cy < 0 || cy >= map->height)
{
terType = centerTerType;
}
else
{
terType = map->terrain[cx][cy][mapLevel].terType;
if(terType != centerTerType)
{
isAlien = true;
}
}
// Validate all rules per cell
int topPoints = -1;
for(int j = 0; j < pattern.data[i].size(); ++j)
{
TerrainViewPattern::WeightedRule rule = pattern.data[i][j];
if(!rule.isStandardRule())
{
if(recDepth == 0)
{
const TerrainViewPattern & patternForRule = terViewPatternConfig->getPatternById(pattern.terGroup, rule.name);
ValidationResult rslt = validateTerrainView(cx, cy, mapLevel, patternForRule, 1);
if(!rslt.result)
{
return ValidationResult(false);
}
else
{
topPoints = std::max(topPoints, rule.points);
continue;
}
}
else
{
rule.name = TerrainViewPattern::RULE_NATIVE;
}
}
bool nativeTestOk = (rule.name == TerrainViewPattern::RULE_NATIVE || rule.name == TerrainViewPattern::RULE_ANY) && !isAlien;
auto applyValidationRslt = [&](bool rslt)
{
if(rslt)
{
topPoints = std::max(topPoints, rule.points);
}
};
// Validate cell with the ruleset of the pattern
if(pattern.terGroup == ETerrainGroup::NORMAL)
{
bool dirtTestOk = (rule.name == TerrainViewPattern::RULE_DIRT
|| rule.name == TerrainViewPattern::RULE_TRANSITION || rule.name == TerrainViewPattern::RULE_ANY)
&& isAlien && !isSandType(terType);
bool sandTestOk = (rule.name == TerrainViewPattern::RULE_SAND || rule.name == TerrainViewPattern::RULE_TRANSITION
|| rule.name == TerrainViewPattern::RULE_ANY)
&& isSandType(terType);
if(transitionReplacement.empty() && (rule.name == TerrainViewPattern::RULE_TRANSITION
|| rule.name == TerrainViewPattern::RULE_ANY) && (dirtTestOk || sandTestOk))
{
transitionReplacement = dirtTestOk ? TerrainViewPattern::RULE_DIRT : TerrainViewPattern::RULE_SAND;
}
applyValidationRslt((dirtTestOk && transitionReplacement != TerrainViewPattern::RULE_SAND)
|| (sandTestOk && transitionReplacement != TerrainViewPattern::RULE_DIRT)
|| nativeTestOk);
}
else if(pattern.terGroup == ETerrainGroup::DIRT)
{
bool sandTestOk = rule.name == TerrainViewPattern::RULE_SAND && isSandType(terType);
bool dirtTestOk = rule.name == TerrainViewPattern::RULE_DIRT && !isSandType(terType) && !nativeTestOk;
applyValidationRslt(rule.name == TerrainViewPattern::RULE_ANY || sandTestOk || dirtTestOk || nativeTestOk);
}
else if(pattern.terGroup == ETerrainGroup::SAND)
{
bool sandTestOk = rule.name == TerrainViewPattern::RULE_SAND && isAlien;
applyValidationRslt(rule.name == TerrainViewPattern::RULE_ANY || sandTestOk || nativeTestOk);
}
else if(pattern.terGroup == ETerrainGroup::WATER)
{
bool sandTestOk = rule.name == TerrainViewPattern::RULE_SAND && terType != ETerrainType::DIRT
&& terType != ETerrainType::WATER;
applyValidationRslt(rule.name == TerrainViewPattern::RULE_ANY || sandTestOk || nativeTestOk);
}
else if(pattern.terGroup == ETerrainGroup::ROCK)
{
bool sandTestOk = rule.name == TerrainViewPattern::RULE_SAND && terType != ETerrainType::DIRT
&& terType != ETerrainType::ROCK;
applyValidationRslt(rule.name == TerrainViewPattern::RULE_ANY || sandTestOk || nativeTestOk);
}
}
if(topPoints == -1)
{
return ValidationResult(false);
}
else
{
totalPoints += topPoints;
}
}
if(pattern.minPoints > totalPoints)
{
return ValidationResult(false);
}
return ValidationResult(true, transitionReplacement);
}
bool CMapEditManager::isSandType(ETerrainType terType) const
{
switch(terType)
{
case ETerrainType::WATER:
case ETerrainType::SAND:
case ETerrainType::ROCK:
return true;
default:
return false;
}
}
TerrainViewPattern CMapEditManager::getFlippedPattern(const TerrainViewPattern & pattern, int flip) const
{
if(flip == 0)
{
return pattern;
}
TerrainViewPattern ret = pattern;
if(flip == FLIP_PATTERN_HORIZONTAL || flip == FLIP_PATTERN_BOTH)
{
for(int i = 0; i < 3; ++i)
{
int y = i * 3;
std::swap(ret.data[y], ret.data[y + 2]);
}
}
if(flip == FLIP_PATTERN_VERTICAL || flip == FLIP_PATTERN_BOTH)
{
for(int i = 0; i < 3; ++i)
{
std::swap(ret.data[i], ret.data[6 + i]);
}
}
return ret;
}
void CMapEditManager::insertObject(CGObjectInstance * obj, int posx, int posy, bool underground)
{
obj->pos = int3(posx, posy, underground ? 1 : 0);
obj->id = ObjectInstanceID(map->objects.size());
map->objects.push_back(obj);
if(obj->ID == Obj::TOWN)
{
map->towns.push_back(static_cast<CGTownInstance *>(obj));
}
if(obj->ID == Obj::HERO)
{
map->heroes.push_back(static_cast<CGHeroInstance*>(obj));
}
map->addBlockVisTiles(obj);
}
CMapEditManager::ValidationResult::ValidationResult(bool result, const std::string & transitionReplacement /*= ""*/)
: result(result), transitionReplacement(transitionReplacement)
{
}

View File

@@ -0,0 +1,293 @@
/*
* CMapEditManager.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 "../CRandomGenerator.h"
#include "CMap.h"
class CGObjectInstance;
namespace ETerrainGroup
{
/**
* This enumeration lists terrain groups which differ in the terrain view frames alignment.
*/
enum ETerrainGroup
{
NORMAL,
DIRT,
SAND,
WATER,
ROCK
};
}
/**
* The terrain view pattern describes a specific composition of terrain tiles
* in a 3x3 matrix and notes which terrain view frame numbers can be used.
*/
struct TerrainViewPattern
{
/**
* A weighted rule struct is a combination of the rule name and optionally points.
*/
struct WeightedRule
{
/** The name of the rule. Can be any value of the RULE_* constants or a ID of a another pattern. */
std::string name;
/** Optional. A rule can have points. Patterns may have a minimum count of points to reach to be successful. */
int points;
/**
* Constructor.
*/
WeightedRule();
/**
* Gets true if this rule is a standard rule which means that it has a value of one of the RULE_* constants.
*
* @return true for a standard rule
*/
bool isStandardRule() const;
};
/** Constant for the flip mode same image. Pattern will be flipped and the same image will be used(which is given in the mapping). */
static const std::string FLIP_MODE_SAME_IMAGE;
/** Constant for the flip mode different images. Pattern will be flipped and different images will be used(mapping area is divided into 4 parts) */
static const std::string FLIP_MODE_DIFF_IMAGES;
/** Constant for the rule dirt, meaning a dirty border is required. */
static const std::string RULE_DIRT;
/** Constant for the rule sand, meaning a sandy border is required. */
static const std::string RULE_SAND;
/** Constant for the rule transition, meaning a dirty OR sandy border is required. */
static const std::string RULE_TRANSITION;
/** Constant for the rule native, meaning a native type is required. */
static const std::string RULE_NATIVE;
/** Constant for the rule any, meaning a native type, dirty OR sandy border is required. */
static const std::string RULE_ANY;
/**
* Default constructor.
*/
TerrainViewPattern();
/**
* The pattern data.
*
* It can be visualized as a 3x3 matrix:
* [ ][ ][ ]
* [ ][ ][ ]
* [ ][ ][ ]
*
* The box in the center belongs always to the native terrain type and
* is the point of origin. Depending on the terrain type different rules
* can be used. Their meaning differs also from type to type.
*
* std::vector -> several rules can be used in one cell
*/
std::array<std::vector<WeightedRule>, 9> data;
/** The identifier of the pattern, if it's referenced from a another pattern. */
std::string id;
/**
* This describes the mapping between this pattern and the corresponding range of frames
* which should be used for the ter view.
*
* std::vector -> size=1: typical, size=2: if this pattern should map to two different types of borders
* std::pair -> 1st value: lower range, 2nd value: upper range
*/
std::vector<std::pair<int, int> > mapping;
/** The minimum points to reach to to validate the pattern successfully. */
int minPoints;
/** Describes if flipping is required and which mapping should be used. */
std::string flipMode;
/** The terrain group to which the pattern belongs to. */
ETerrainGroup::ETerrainGroup terGroup;
};
/**
* The terrain view pattern config loads pattern data from the filesystem.
*/
class CTerrainViewPatternConfig
{
public:
/**
* Constructor. Initializes the patterns data.
*/
CTerrainViewPatternConfig();
/**
* Gets the patterns for a specific group of terrain.
*
* @param terGroup the terrain group e.g. normal for grass, lava,... OR dirt OR sand,...
* @return a vector containing patterns
*/
const std::vector<TerrainViewPattern> & getPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const;
/**
* Gets a pattern by ID. Throws if pattern isn't available(config error).
*
* @param terGroup the terrain group e.g. normal for grass, lava,... OR dirt OR sand,...
* @param id the id of the pattern
* @return the pattern which matches the ID
*/
const TerrainViewPattern & getPatternById(ETerrainGroup::ETerrainGroup terGroup, const std::string & id) const;
private:
/** The patterns data. */
std::map<ETerrainGroup::ETerrainGroup, std::vector<TerrainViewPattern> > patterns;
};
/**
* The map edit manager provides functionality for drawing terrain and placing
* objects on the map.
*
* TODO add undo / selection functionality for the map editor
*/
class CMapEditManager
{
public:
/**
* Constructor. The map object / terrain data has to be initialized.
*
* @param terViewPatternConfig the terrain view pattern config
* @param map the map object which should be edited
* @param randomSeed optional. the seed which is used for generating randomly terrain views
*/
CMapEditManager(const CTerrainViewPatternConfig * terViewPatternConfig, CMap * map, int randomSeed = std::time(nullptr));
/**
* Clears the terrain. The free level is filled with water and the
* underground level with rock.
*/
void clearTerrain();
/**
* Draws terrain.
*
* @param terType the type of the terrain to draw
* @param posx the x coordinate
* @param posy the y coordinate
* @param width the height of the terrain to draw
* @param height the width of the terrain to draw
* @param underground true if you want to draw at the underground, false if open
*/
void drawTerrain(ETerrainType terType, int posx, int posy, int width, int height, bool underground);
/**
* Inserts an object.
*
* @param obj the object to insert
* @param posx the x coordinate
* @param posy the y coordinate
* @param underground true if you want to draw at the underground, false if open
*/
void insertObject(CGObjectInstance * obj, int posx, int posy, bool underground);
private:
/**
* The validation result struct represents the result of a pattern validation.
*/
struct ValidationResult
{
/**
* Constructor.
*
* @param result the result of the validation either true or false
* @param transitionReplacement optional. the replacement of a T rule, either D or S
*/
ValidationResult(bool result, const std::string & transitionReplacement = "");
/** The result of the validation. */
bool result;
/** The replacement of a T rule, either D or S. */
std::string transitionReplacement;
};
/**
* Updates the terrain view ids in the specified area.
*
* @param posx the x coordinate
* @param posy the y coordinate
* @param width the height of the terrain to update
* @param height the width of the terrain to update
* @param mapLevel the map level, 0 for open and 1 for underground
*/
void updateTerrainViews(int posx, int posy, int width, int height, int mapLevel);
/**
* Gets the terrain group by the terrain type number.
*
* @param terType the terrain type
* @return the terrain group
*/
ETerrainGroup::ETerrainGroup getTerrainGroup(ETerrainType terType) const;
/**
* Validates the terrain view of the given position and with the given pattern.
*
* @param posx the x position
* @param posy the y position
* @param mapLevel the map level, 0 for open and 1 for underground
* @param pattern the pattern to validate the terrain view with
* @param recDepth the depth of the recursion, 0 for no recursion - 1 for recursion
* @return a validation result struct
*/
ValidationResult validateTerrainView(int posx, int posy, int mapLevel, const TerrainViewPattern & pattern, int recDepth = 0) const;
/**
* Tests whether the given terrain type is a sand type. Sand types are: Water, Sand and Rock
*
* @param terType the terrain type to test
* @return true if the terrain type is a sand type, otherwise false
*/
bool isSandType(ETerrainType terType) const;
/**
* Gets a flipped pattern.
*
* @param pattern the original pattern to flip
* @param flip the flip mode value, see FLIP_PATTERN_* constants for details
* @return the flipped pattern
*/
TerrainViewPattern getFlippedPattern(const TerrainViewPattern & pattern, int flip) const;
/** Constant for flipping a pattern horizontally. */
static const int FLIP_PATTERN_HORIZONTAL = 1;
/** Constant for flipping a pattern vertically. */
static const int FLIP_PATTERN_VERTICAL = 2;
/** Constant for flipping a pattern horizontally and vertically. */
static const int FLIP_PATTERN_BOTH = 3;
/** The map object to edit. */
CMap * map;
/** The random number generator. */
CRandomGenerator gen;
/** The terrain view pattern config. */
const CTerrainViewPatternConfig * terViewPatternConfig;
};

49
lib/mapping/CMapInfo.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "StdInc.h"
#include "CMapInfo.h"
#include "../StartInfo.h"
#include "CMap.h"
#include "CCampaignHandler.h"
#include "../GameConstants.h"
#include "CMapService.h"
void CMapInfo::countPlayers()
{
actualHumanPlayers = playerAmnt = humanPlayers = 0;
for(int i=0; i<PlayerColor::PLAYER_LIMIT_I; i++)
{
if(mapHeader->players[i].canHumanPlay)
{
playerAmnt++;
humanPlayers++;
}
else if(mapHeader->players[i].canComputerPlay)
{
playerAmnt++;
}
}
if(scenarioOpts)
for (auto i = scenarioOpts->playerInfos.cbegin(); i != scenarioOpts->playerInfos.cend(); i++)
if(i->second.playerID != PlayerSettings::PLAYER_AI)
actualHumanPlayers++;
}
CMapInfo::CMapInfo() : scenarioOpts(nullptr), playerAmnt(0), humanPlayers(0),
actualHumanPlayers(0), isRandomMap(false)
{
}
void CMapInfo::mapInit(const std::string & fname)
{
fileURI = fname;
mapHeader = CMapService::loadMapHeader(fname);
countPlayers();
}
void CMapInfo::campaignInit()
{
campaignHeader = std::unique_ptr<CCampaignHeader>(new CCampaignHeader(CCampaignHandler::getHeader(fileURI)));
}

45
lib/mapping/CMapInfo.h Normal file
View File

@@ -0,0 +1,45 @@
#pragma once
// Forward class declarations aren't enough here. The compiler
// generated CMapInfo d-tor, generates the unique_ptr d-tor as well here
// as a inline method. The unique_ptr d-tor requires a complete type. Defining
// the CMapInfo d-tor to let the compiler add the d-tor stuff in the .cpp file
// would work with one exception. It prevents the generation of the move
// constructor which is needed. (Writing such a c-tor is nasty.) With the
// new c++11 keyword "default" for constructors this problem could be solved. But it isn't
// available for Visual Studio for now. (Empty d-tor in .cpp would be required anyway)
#include "CMap.h"
#include "CCampaignHandler.h"
struct StartInfo;
/**
* A class which stores the count of human players and all players, the filename,
* scenario options, the map header information,...
*/
class DLL_LINKAGE CMapInfo
{
public:
//FIXME: unique_ptr won't work here with gcc-4.5. (can't move into vector at CPregame.cpp, SelectionTab::parseMaps() method)
//Needs some workaround or wait till there is no need to support 4.5
shared_ptr<CMapHeader> mapHeader; //may be nullptr if campaign
shared_ptr<CCampaignHeader> campaignHeader; //may be nullptr if scenario
StartInfo * scenarioOpts; //options with which scenario has been started (used only with saved games)
std::string fileURI;
std::string date;
int playerAmnt; //players in map
int humanPlayers; //players ALLOWED to be controlled by human
int actualHumanPlayers; // >1 if multiplayer game
bool isRandomMap; // true if the map will be created randomly, false if not
CMapInfo();
void mapInit(const std::string & fname);
void campaignInit();
void countPlayers();
template <typename Handler> void serialize(Handler &h, const int Version)
{
h & mapHeader & campaignHeader & scenarioOpts & fileURI & date & playerAmnt & humanPlayers;
h & actualHumanPlayers & isRandomMap;
}
};

View File

@@ -0,0 +1,70 @@
#include "StdInc.h"
#include "CMapService.h"
#include "../filesystem/CResourceLoader.h"
#include "../filesystem/CBinaryReader.h"
#include "../filesystem/CCompressedStream.h"
#include "../filesystem/CMemoryStream.h"
#include "CMap.h"
#include "MapFormatH3M.h"
std::unique_ptr<CMap> CMapService::loadMap(const std::string & name)
{
auto stream = getStreamFromFS(name);
return getMapLoader(stream)->loadMap();
}
std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const std::string & name)
{
auto stream = getStreamFromFS(name);
return getMapLoader(stream)->loadMapHeader();
}
std::unique_ptr<CMap> CMapService::loadMap(const ui8 * buffer, int size)
{
auto stream = getStreamFromMem(buffer, size);
return getMapLoader(stream)->loadMap();
}
std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const ui8 * buffer, int size)
{
auto stream = getStreamFromMem(buffer, size);
return getMapLoader(stream)->loadMapHeader();
}
std::unique_ptr<CInputStream> CMapService::getStreamFromFS(const std::string & name)
{
return CResourceHandler::get()->load(ResourceID(name, EResType::MAP));
}
std::unique_ptr<CInputStream> CMapService::getStreamFromMem(const ui8 * buffer, int size)
{
return std::unique_ptr<CInputStream>(new CMemoryStream(buffer, size));
}
std::unique_ptr<IMapLoader> CMapService::getMapLoader(std::unique_ptr<CInputStream> & stream)
{
// Read map header
CBinaryReader reader(stream.get());
ui32 header = reader.readUInt32();
reader.getStream()->seek(0);
// Check which map format is used
// gzip header is 3 bytes only in size
switch(header & 0xffffff)
{
// gzip header magic number, reversed for LE
case 0x00088B1F:
stream = std::unique_ptr<CInputStream>(new CCompressedStream(std::move(stream), true));
return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(stream.get()));
case EMapFormat::WOG :
case EMapFormat::AB :
case EMapFormat::ROE :
case EMapFormat::SOD :
return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(stream.get()));
default :
throw std::runtime_error("Unknown map format");
}
}

118
lib/mapping/CMapService.h Normal file
View File

@@ -0,0 +1,118 @@
/*
* CMapService.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
class CMap;
class CMapHeader;
class CInputStream;
class IMapLoader;
/**
* The map service provides loading of VCMI/H3 map files. It can
* be extended to save maps later as well.
*/
class DLL_LINKAGE CMapService
{
public:
/**
* Loads the VCMI/H3 map file specified by the name.
*
* @param name the name of the map
* @return a unique ptr to the loaded map class
*/
static std::unique_ptr<CMap> loadMap(const std::string & name);
/**
* Loads the VCMI/H3 map header specified by the name.
*
* @param name the name of the map
* @return a unique ptr to the loaded map header class
*/
static std::unique_ptr<CMapHeader> loadMapHeader(const std::string & name);
/**
* Loads the VCMI/H3 map file from a buffer. This method is temporarily
* in use to ease the transition to use the new map service.
*
* TODO Replace method params with a CampaignMapInfo struct which contains
* a campaign loading object + name of map.
*
* @param buffer a pointer to a buffer containing the map data
* @param size the size of the buffer
* @return a unique ptr to the loaded map class
*/
static std::unique_ptr<CMap> loadMap(const ui8 * buffer, int size);
/**
* Loads the VCMI/H3 map header from a buffer. This method is temporarily
* in use to ease the transition to use the new map service.
*
* TODO Replace method params with a CampaignMapInfo struct which contains
* a campaign loading object + name of map.
*
* @param buffer a pointer to a buffer containing the map header data
* @param size the size of the buffer
* @return a unique ptr to the loaded map class
*/
static std::unique_ptr<CMapHeader> loadMapHeader(const ui8 * buffer, int size);
private:
/**
* Gets a map input stream object specified by a map name.
*
* @param name the name of the map
* @return a unique ptr to the input stream class
*/
static std::unique_ptr<CInputStream> getStreamFromFS(const std::string & name);
/**
* Gets a map input stream from a buffer.
*
* @param buffer a pointer to a buffer containing the map data
* @param size the size of the buffer
* @return a unique ptr to the input stream class
*/
static std::unique_ptr<CInputStream> getStreamFromMem(const ui8 * buffer, int size);
/**
* Gets a map loader from the given stream. It performs checks to test
* in which map format the map is.
*
* @param stream the input map stream
* @return the constructed map loader
*/
static std::unique_ptr<IMapLoader> getMapLoader(std::unique_ptr<CInputStream> & stream);
};
/**
* Interface for loading a map.
*/
class DLL_LINKAGE IMapLoader
{
public:
/**
* Loads the VCMI/H3 map file.
*
* @return a unique ptr of the loaded map class
*/
virtual std::unique_ptr<CMap> loadMap() = 0;
/**
* Loads the VCMI/H3 map header.
*
* @return a unique ptr of the loaded map header class
*/
virtual std::unique_ptr<CMapHeader> loadMapHeader() = 0;
};

2181
lib/mapping/MapFormatH3M.cpp Normal file

File diff suppressed because it is too large Load Diff

265
lib/mapping/MapFormatH3M.h Normal file
View File

@@ -0,0 +1,265 @@
/*
* MapFormatH3M.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 "CMapService.h"
#include "../GameConstants.h"
#include "../ResourceSet.h"
#include "../int3.h"
#include "../filesystem/CBinaryReader.h"
class CGHeroInstance;
class CArtifactInstance;
class CGObjectInstance;
class CGSeerHut;
class IQuestObject;
class CGTownInstance;
class CCreatureSet;
class CInputStream;
class DLL_LINKAGE CMapLoaderH3M : public IMapLoader
{
public:
/**
* Default constructor.
*
* @param stream a stream containing the map data
*/
CMapLoaderH3M(CInputStream * stream);
/**
* Destructor.
*/
~CMapLoaderH3M();
/**
* Loads the VCMI/H3 map file.
*
* @return a unique ptr of the loaded map class
*/
std::unique_ptr<CMap> loadMap();
/**
* Loads the VCMI/H3 map header.
*
* @return a unique ptr of the loaded map header class
*/
std::unique_ptr<CMapHeader> loadMapHeader();
/** true if you want to enable the map loader profiler to see how long a specific part took; default=false */
static const bool IS_PROFILING_ENABLED;
private:
/**
* Initializes the map object from parsing the input buffer.
*/
void init();
/**
* Reads the map header.
*/
void readHeader();
/**
* Reads player information.
*/
void readPlayerInfo();
/**
* Reads victory/loss conditions.
*/
void readVictoryLossConditions();
/**
* Reads team information.
*/
void readTeamInfo();
/**
* Reads the list of allowed heroes.
*/
void readAllowedHeroes();
/**
* Reads the list of disposed heroes.
*/
void readDisposedHeroes();
/**
* Reads the list of allowed artifacts.
*/
void readAllowedArtifacts();
/**
* Reads the list of allowed spells and abilities.
*/
void readAllowedSpellsAbilities();
/**
* Loads artifacts of a hero.
*
* @param hero the hero which should hold those artifacts
*/
void loadArtifactsOfHero(CGHeroInstance * hero);
/**
* Loads an artifact to the given slot of the specified hero.
*
* @param hero the hero which should hold that artifact
* @param slot the artifact slot where to place that artifact
* @return true if it loaded an artifact
*/
bool loadArtifactToSlot(CGHeroInstance * hero, int slot);
/**
* Creates an artifact instance.
*
* @param aid the id of the artifact
* @param spellID optional. the id of a spell if a spell scroll object should be created
* @return the created artifact instance
*/
CArtifactInstance * createArtifact(int aid, int spellID = -1);
/**
* Read rumors.
*/
void readRumors();
/**
* Reads predefined heroes.
*/
void readPredefinedHeroes();
/**
* Reads terrain data.
*/
void readTerrain();
/**
* Reads custom(map) def information.
*/
void readDefInfo();
/**
* Reads objects(towns, mines,...).
*/
void readObjects();
/**
* Reads a creature set.
*
* @param out the loaded creature set
* @param number the count of creatures to read
*/
void readCreatureSet(CCreatureSet * out, int number);
/**
* Reads a hero.
*
* @param idToBeGiven the object id which should be set for the hero
* @return a object instance
*/
CGObjectInstance * readHero(ObjectInstanceID idToBeGiven);
/**
* Reads a seer hut.
*
* @return the initialized seer hut object
*/
CGSeerHut * readSeerHut();
/**
* Reads a quest for the given quest guard.
*
* @param guard the quest guard where that quest should be applied to
*/
void readQuest(IQuestObject * guard);
/**
* Reads a town.
*
* @param castleID the id of the castle type
* @return the loaded town object
*/
CGTownInstance * readTown(int castleID);
/**
* Converts buildings to the specified castle id.
*
* @param h3m the ids of the buildings
* @param castleID the castle id
* @param addAuxiliary true if the village hall should be added
* @return the converted buildings
*/
std::set<BuildingID> convertBuildings(const std::set<BuildingID> h3m, int castleID, bool addAuxiliary = true);
/**
* Reads events.
*/
void readEvents();
/**
* read optional message and optional guards
*/
void readMessageAndGuards(std::string& message, CCreatureSet * guards);
void readSpells(std::set<SpellID> & dest);
void readResourses(TResources& resources);
template <class Indenifier>
void readBitmask(std::set<Indenifier> &dest, const int byteCount, const int limit, bool negate = true);
/** Reads bitmask to boolean vector
* @param dest destination vector, shall be filed with "true" values
* @param byteCount size in bytes of bimask
* @param limit max count of vector elements to alter
* @param negate if true then set bit in mask means clear flag in vertor
*/
void readBitmask(std::vector<bool> & dest, const int byteCount, const int limit, bool negate = true);
/**
* Reverses the input argument.
*
* @param arg the input argument
* @return the reversed 8-bit integer
*/
ui8 reverse(ui8 arg);
/**
* Helper to read map position
*/
inline int3 readInt3()
{
int3 p;
p.x = reader.readUInt8();
p.y = reader.readUInt8();
p.z = reader.readUInt8();
return p;
}
/** ptr to the map object which gets filled by data from the buffer */
CMap * map;
/**
* ptr to the map header object which gets filled by data from the buffer.
* (when loading a map then the mapHeader ptr points to the same object)
*/
std::unique_ptr<CMapHeader> mapHeader;
CBinaryReader reader;
CInputStream * inputStream;
};