1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-20 20:23:03 +02:00
vcmi/lib/mapping/CCampaignHandler.cpp
Arseniy Shestakov 9fd1cff090 Refactoring: always use std prefix for shared_ptr, unique_ptr and make_shared
Long time ago it's was used without prefix to make future switch from boost to std version easier.
I discusses this with Ivan and decide to drop these using from Global.h now.

This change wouldn't break anything because there was already code with prefix for each of three cases.
2015-12-29 05:43:33 +03:00

453 lines
13 KiB
C++

#include "StdInc.h"
#include "CCampaignHandler.h"
#include "../filesystem/Filesystem.h"
#include "../filesystem/CCompressedStream.h"
#include "../filesystem/CMemoryStream.h"
#include "../filesystem/CBinaryReader.h"
#include "../VCMI_Lib.h"
#include "../vcmi_endian.h"
#include "../CGeneralTextHandler.h"
#include "../StartInfo.h"
#include "../CArtHandler.h" //for hero crossover
#include "../mapObjects/CGHeroInstance.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];
CMemoryStream stream(cmpgn.data(), cmpgn.size());
CBinaryReader reader(&stream);
CCampaignHeader ret = readHeaderFromMemory(reader);
ret.filename = name;
return ret;
}
std::unique_ptr<CCampaign> CCampaignHandler::getCampaign( const std::string & name )
{
auto ret = make_unique<CCampaign>();
std::vector<std::vector<ui8>> file = getFile(name, false);
CMemoryStream stream(file[0].data(), file[0].size());
CBinaryReader reader(&stream);
ret->header = readHeaderFromMemory(reader);
ret->header.filename = name;
int howManyScenarios = VLC->generaltexth->campaignRegionNames[ret->header.mapVersion].size();
for(int g=0; g<howManyScenarios; ++g)
{
CCampaignScenario sc = readScenarioFromMemory(reader, 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++;
}
std::string scenarioName = name.substr(0, name.find('.'));
boost::to_lower(scenarioName);
scenarioName += ':' + boost::lexical_cast<std::string>(g-1);
//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(), scenarioName)->name;
scenarioID++;
}
// handle campaign specific discrepancies
if(name == "DATA/AB.H3C")
{
ret->scenarios[6].keepHeroes.push_back(HeroTypeID(155)); // keep hero Xeron for map 7 To Kill A Hero
}
else if(name == "DATA/FINAL.H3C")
{
// keep following heroes for map 8 Final H
ret->scenarios[7].keepHeroes.push_back(HeroTypeID(148)); // Gelu
ret->scenarios[7].keepHeroes.push_back(HeroTypeID(27)); // Gem
ret->scenarios[7].keepHeroes.push_back(HeroTypeID(102)); // Crag Hack
ret->scenarios[7].keepHeroes.push_back(HeroTypeID(96)); // Yog
}
return ret;
}
CCampaignHeader CCampaignHandler::readHeaderFromMemory( CBinaryReader & reader )
{
CCampaignHeader ret;
ret.version = reader.readUInt32();
ret.mapVersion = reader.readUInt8() - 1;//change range of it from [1, 20] to [0, 19]
ret.name = reader.readString();
ret.description = reader.readString();
if (ret.version > CampaignVersion::RoE)
ret.difficultyChoosenByPlayer = reader.readInt8();
else
ret.difficultyChoosenByPlayer = 0;
ret.music = reader.readInt8();
return ret;
}
CCampaignScenario CCampaignHandler::readScenarioFromMemory( CBinaryReader & reader, int version, int mapVersion )
{
auto prologEpilogReader = [&]() -> CCampaignScenario::SScenarioPrologEpilog
{
CCampaignScenario::SScenarioPrologEpilog ret;
ret.hasPrologEpilog = reader.readUInt8();
if(ret.hasPrologEpilog)
{
ret.prologVideo = reader.readUInt8();
ret.prologMusic = reader.readUInt8();
ret.prologText = reader.readString();
}
return ret;
};
CCampaignScenario ret;
ret.conquered = false;
ret.mapName = reader.readString();
ret.packedMapSize = reader.readUInt32();
if(mapVersion == 18)//unholy alliance
{
ret.loadPreconditionRegions(reader.readUInt16());
}
else
{
ret.loadPreconditionRegions(reader.readUInt8());
}
ret.regionColor = reader.readUInt8();
ret.difficulty = reader.readUInt8();
ret.regionText = reader.readString();
ret.prolog = prologEpilogReader();
ret.epilog = prologEpilogReader();
ret.travelOptions = readScenarioTravelFromMemory(reader, 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(CBinaryReader & reader, int version )
{
CScenarioTravel ret;
ret.whatHeroKeeps = reader.readUInt8();
reader.getStream()->read(ret.monstersKeptByHero.data(), ret.monstersKeptByHero.size());
if (version < CampaignVersion::SoD)
{
ret.artifsKeptByHero.fill(0);
reader.getStream()->read(ret.artifsKeptByHero.data(), ret.artifsKeptByHero.size() - 1);
}
else
{
reader.getStream()->read(ret.artifsKeptByHero.data(), ret.artifsKeptByHero.size());
}
ret.startOptions = reader.readUInt8();
switch(ret.startOptions)
{
case 0:
//no bonuses. Seems to be OK
break;
case 1: //reading of bonuses player can choose
{
ret.playerColor = reader.readUInt8();
ui8 numOfBonuses = reader.readUInt8();
for (int g=0; g<numOfBonuses; ++g)
{
CScenarioTravel::STravelBonus bonus;
bonus.type = static_cast<CScenarioTravel::STravelBonus::EBonusType>(reader.readUInt8());
//hero: FFFD means 'most powerful' and FFFE means 'generated'
switch(bonus.type)
{
case CScenarioTravel::STravelBonus::SPELL:
{
bonus.info1 = reader.readUInt16(); //hero
bonus.info2 = reader.readUInt8(); //spell ID
break;
}
case CScenarioTravel::STravelBonus::MONSTER:
{
bonus.info1 = reader.readUInt16(); //hero
bonus.info2 = reader.readUInt16(); //monster type
bonus.info3 = reader.readUInt16(); //monster count
break;
}
case CScenarioTravel::STravelBonus::BUILDING:
{
bonus.info1 = reader.readUInt8(); //building ID (0 - town hall, 1 - city hall, 2 - capitol, etc)
break;
}
case CScenarioTravel::STravelBonus::ARTIFACT:
{
bonus.info1 = reader.readUInt16(); //hero
bonus.info2 = reader.readUInt16(); //artifact ID
break;
}
case CScenarioTravel::STravelBonus::SPELL_SCROLL:
{
bonus.info1 = reader.readUInt16(); //hero
bonus.info2 = reader.readUInt8(); //spell ID
break;
}
case CScenarioTravel::STravelBonus::PRIMARY_SKILL:
{
bonus.info1 = reader.readUInt16(); //hero
bonus.info2 = reader.readUInt32(); //bonuses (4 bytes for 4 skills)
break;
}
case CScenarioTravel::STravelBonus::SECONDARY_SKILL:
{
bonus.info1 = reader.readUInt16(); //hero
bonus.info2 = reader.readUInt8(); //skill ID
bonus.info3 = reader.readUInt8(); //skill level
break;
}
case CScenarioTravel::STravelBonus::RESOURCE:
{
bonus.info1 = reader.readUInt8(); //type
//FD - wood+ore
//FE - mercury+sulfur+crystal+gem
bonus.info2 = reader.readUInt32(); //count
break;
}
default:
logGlobal->warnStream() << "Corrupted h3c file";
break;
}
ret.bonusesToChoose.push_back(bonus);
}
break;
}
case 2: //reading of players (colors / scenarios ?) player can choose
{
ui8 numOfBonuses = reader.readUInt8();
for (int g=0; g<numOfBonuses; ++g)
{
CScenarioTravel::STravelBonus bonus;
bonus.type = CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO;
bonus.info1 = reader.readUInt8(); //player color
bonus.info2 = reader.readUInt8(); //from what scenario
ret.bonusesToChoose.push_back(bonus);
}
break;
}
case 3: //heroes player can choose between
{
ui8 numOfBonuses = reader.readUInt8();
for (int g=0; g<numOfBonuses; ++g)
{
CScenarioTravel::STravelBonus bonus;
bonus.type = CScenarioTravel::STravelBonus::HERO;
bonus.info1 = reader.readUInt8(); //player color
bonus.info2 = reader.readUInt16(); //hero, FF FF - random
ret.bonusesToChoose.push_back(bonus);
}
break;
}
default:
{
logGlobal->warnStream() << "Corrupted h3c file";
break;
}
}
return ret;
}
std::vector< std::vector<ui8> > CCampaignHandler::getFile(const std::string & name, bool headerOnly)
{
CCompressedStream stream(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;
}
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->getHeroStrength();});
return i == ownedHeroes.end() ? nullptr : *i;
}
std::vector<CGHeroInstance *> CCampaignScenario::getLostCrossoverHeroes() const
{
std::vector<CGHeroInstance *> lostCrossoverHeroes;
if(conquered)
{
for(auto hero : placedCrossoverHeroes)
{
auto it = range::find_if(crossoverHeroes, CGObjectInstanceBySubIdFinder(hero));
if(it == crossoverHeroes.end())
{
lostCrossoverHeroes.push_back(hero);
}
}
}
return lostCrossoverHeroes;
}
bool CScenarioTravel::STravelBonus::isBonusForHero() const
{
return type == SPELL || type == MONSTER || type == ARTIFACT || type == SPELL_SCROLL || type == PRIMARY_SKILL
|| type == SECONDARY_SKILL;
}
void CCampaignState::setCurrentMapAsConquered( const std::vector<CGHeroInstance*> & heroes )
{
camp->scenarios[*currentMap].crossoverHeroes = 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.empty())
return boost::optional<CScenarioTravel::STravelBonus>();
else
return bonuses[currentBonusID()];
}
const CCampaignScenario & CCampaignState::getCurrentScenario() const
{
return camp->scenarios[*currentMap];
}
CCampaignScenario & CCampaignState::getCurrentScenario()
{
return camp->scenarios[*currentMap];
}
ui8 CCampaignState::currentBonusID() const
{
return chosenCampaignBonuses.at(*currentMap);
}
CCampaignState::CCampaignState()
{
}
CCampaignState::CCampaignState( std::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)
{
std::vector<std::string> music;
VLC->generaltexth->readToVector("Data/CmpMusic.txt", music);
if(index < music.size())
return music[index];
return "";
}
std::string CCampaignHandler::prologVoiceName(ui8 index)
{
JsonNode config(ResourceID(std::string("CONFIG/campaignMedia"), EResType::TEXT));
auto audio = config["voice"].Vector();
if(index < audio.size())
return audio[index].String();
return "";
}