|
@@ -726,6 +726,7 @@ int CGameState::getDate(Date::EDateType mode) const
|
|
}
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+
|
|
CGameState::CGameState()
|
|
CGameState::CGameState()
|
|
{
|
|
{
|
|
gs = this;
|
|
gs = this;
|
|
@@ -735,6 +736,7 @@ CGameState::CGameState()
|
|
objCaller = new CObjectCallersHandler;
|
|
objCaller = new CObjectCallersHandler;
|
|
globalEffects.setDescription("Global effects");
|
|
globalEffects.setDescription("Global effects");
|
|
}
|
|
}
|
|
|
|
+
|
|
CGameState::~CGameState()
|
|
CGameState::~CGameState()
|
|
{
|
|
{
|
|
//delete mx;//TODO: crash on Linux (mutex must be unlocked before destruction)
|
|
//delete mx;//TODO: crash on Linux (mutex must be unlocked before destruction)
|
|
@@ -762,171 +764,249 @@ BattleInfo * CGameState::setupBattle(int3 tile, const CArmedInstance *armies[2],
|
|
|
|
|
|
void CGameState::init(StartInfo * si)
|
|
void CGameState::init(StartInfo * si)
|
|
{
|
|
{
|
|
- auto giveCampaignBonusToHero = [&](CGHeroInstance * hero)
|
|
|
|
|
|
+ logGlobal->infoStream() << "\tUsing random seed: "<< si->seedToBeUsed;
|
|
|
|
+ ran.seed((boost::int32_t)si->seedToBeUsed);
|
|
|
|
+ scenarioOps = new StartInfo(*si);
|
|
|
|
+ initialOpts = new StartInfo(*si);
|
|
|
|
+ si = nullptr;
|
|
|
|
+
|
|
|
|
+ switch(scenarioOps->mode)
|
|
{
|
|
{
|
|
- const boost::optional<CScenarioTravel::STravelBonus> & curBonus = scenarioOps->campState->getBonusForCurrentMap();
|
|
|
|
- if(!curBonus)
|
|
|
|
- return;
|
|
|
|
|
|
+ case StartInfo::NEW_GAME:
|
|
|
|
+ initNewGame();
|
|
|
|
+ break;
|
|
|
|
+ case StartInfo::CAMPAIGN:
|
|
|
|
+ initCampaign();
|
|
|
|
+ break;
|
|
|
|
+ case StartInfo::DUEL:
|
|
|
|
+ initDuel();
|
|
|
|
+ return;
|
|
|
|
+ default:
|
|
|
|
+ logGlobal->errorStream() << "Wrong mode: " << (int)scenarioOps->mode;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ VLC->arth->initAllowedArtifactsList(map->allowedArtifact);
|
|
|
|
+ logGlobal->infoStream() << "Map loaded!";
|
|
|
|
+
|
|
|
|
+ checkMapChecksum();
|
|
|
|
+
|
|
|
|
+ day = 0;
|
|
|
|
+
|
|
|
|
+ logGlobal->debugStream() << "Initialization:";
|
|
|
|
|
|
- if(curBonus->isBonusForHero())
|
|
|
|
|
|
+ initGrailPosition();
|
|
|
|
+ initRandomFactionsForPlayers();
|
|
|
|
+ randomizeMapObjects();
|
|
|
|
+ initPlayerStates();
|
|
|
|
+ initHeroPlaceholders();
|
|
|
|
+ placeStartingHeroes();
|
|
|
|
+ initStartingResources();
|
|
|
|
+ initHeroes();
|
|
|
|
+ initFogOfWar();
|
|
|
|
+ initStartingBonus();
|
|
|
|
+ initTowns();
|
|
|
|
+ initMapObjects();
|
|
|
|
+ buildBonusSystemTree();
|
|
|
|
+ initVisitingAndGarrisonedHeroes();
|
|
|
|
+
|
|
|
|
+ logGlobal->debugStream() << "\tChecking objectives";
|
|
|
|
+ map->checkForObjectives(); //needs to be run when all objects are properly placed
|
|
|
|
+
|
|
|
|
+ int seedAfterInit = ran();
|
|
|
|
+ logGlobal->infoStream() << "Seed after init is " << seedAfterInit << " (before was " << scenarioOps->seedToBeUsed << ")";
|
|
|
|
+ if(scenarioOps->seedPostInit > 0)
|
|
|
|
+ {
|
|
|
|
+ //RNG must be in the same state on all machines when initialization is done (otherwise we have desync)
|
|
|
|
+ assert(scenarioOps->seedPostInit == seedAfterInit);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ scenarioOps->seedPostInit = seedAfterInit; //store the post init "seed"
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void CGameState::initNewGame()
|
|
|
|
+{
|
|
|
|
+ if(scenarioOps->createRandomMap())
|
|
|
|
+ {
|
|
|
|
+ logGlobal->infoStream() << "Create random map.";
|
|
|
|
+ CStopWatch sw;
|
|
|
|
+
|
|
|
|
+ // Gen map
|
|
|
|
+ CMapGenerator mapGenerator;
|
|
|
|
+ map = mapGenerator.generate(scenarioOps->mapGenOptions.get(), scenarioOps->seedToBeUsed).release();
|
|
|
|
+
|
|
|
|
+ // Update starting options
|
|
|
|
+ for(int i = 0; i < map->players.size(); ++i)
|
|
{
|
|
{
|
|
- //apply bonus
|
|
|
|
- switch (curBonus->type)
|
|
|
|
|
|
+ const auto & playerInfo = map->players[i];
|
|
|
|
+ if(playerInfo.canAnyonePlay())
|
|
{
|
|
{
|
|
- case CScenarioTravel::STravelBonus::SPELL:
|
|
|
|
- hero->spells.insert(SpellID(curBonus->info2));
|
|
|
|
- break;
|
|
|
|
- case CScenarioTravel::STravelBonus::MONSTER:
|
|
|
|
- {
|
|
|
|
- for(int i=0; i<GameConstants::ARMY_SIZE; i++)
|
|
|
|
- {
|
|
|
|
- if(hero->slotEmpty(SlotID(i)))
|
|
|
|
- {
|
|
|
|
- hero->addToSlot(SlotID(i), CreatureID(curBonus->info2), curBonus->info3);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- case CScenarioTravel::STravelBonus::ARTIFACT:
|
|
|
|
- gs->giveHeroArtifact(hero, static_cast<ArtifactID>(curBonus->info2));
|
|
|
|
- break;
|
|
|
|
- case CScenarioTravel::STravelBonus::SPELL_SCROLL:
|
|
|
|
- {
|
|
|
|
- CArtifactInstance * scroll = CArtifactInstance::createScroll(SpellID(curBonus->info2).toSpell());
|
|
|
|
- scroll->putAt(ArtifactLocation(hero, scroll->firstAvailableSlot(hero)));
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- case CScenarioTravel::STravelBonus::PRIMARY_SKILL:
|
|
|
|
|
|
+ PlayerSettings & playerSettings = scenarioOps->playerInfos[PlayerColor(i)];
|
|
|
|
+ playerSettings.compOnly = !playerInfo.canHumanPlay;
|
|
|
|
+ playerSettings.team = playerInfo.team;
|
|
|
|
+ playerSettings.castle = playerInfo.defaultCastle();
|
|
|
|
+ if(playerSettings.playerID == PlayerSettings::PLAYER_AI && playerSettings.name.empty())
|
|
{
|
|
{
|
|
- const ui8* ptr = reinterpret_cast<const ui8*>(&curBonus->info2);
|
|
|
|
- for (int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
|
|
|
|
- {
|
|
|
|
- int val = ptr[g];
|
|
|
|
- if (val == 0)
|
|
|
|
- {
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- auto bb = new Bonus(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::CAMPAIGN_BONUS, val, *scenarioOps->campState->currentMap, g);
|
|
|
|
- hero->addNewBonus(bb);
|
|
|
|
- }
|
|
|
|
|
|
+ playerSettings.name = VLC->generaltexth->allTexts[468];
|
|
}
|
|
}
|
|
- break;
|
|
|
|
- case CScenarioTravel::STravelBonus::SECONDARY_SKILL:
|
|
|
|
- hero->setSecSkillLevel(SecondarySkill(curBonus->info2), curBonus->info3, true);
|
|
|
|
- break;
|
|
|
|
|
|
+ playerSettings.color = PlayerColor(i);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ scenarioOps->playerInfos.erase(PlayerColor(i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- };
|
|
|
|
|
|
|
|
- auto getHumanPlayerInfo = [&]() -> std::vector<const PlayerSettings *>
|
|
|
|
|
|
+ logGlobal->infoStream() << boost::format("Generated random map in %i ms.") % sw.getDiff();
|
|
|
|
+ }
|
|
|
|
+ else
|
|
{
|
|
{
|
|
- std::vector<const PlayerSettings *> ret;
|
|
|
|
- for(auto it = scenarioOps->playerInfos.cbegin();
|
|
|
|
- it != scenarioOps->playerInfos.cend(); ++it)
|
|
|
|
- {
|
|
|
|
- if(it->second.playerID != PlayerSettings::PLAYER_AI)
|
|
|
|
- ret.push_back(&it->second);
|
|
|
|
- }
|
|
|
|
|
|
+ logGlobal->infoStream() << "Open map file: " << scenarioOps->mapname;
|
|
|
|
+ map = CMapService::loadMap(scenarioOps->mapname).release();
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
- return ret;
|
|
|
|
- };
|
|
|
|
|
|
+void CGameState::initCampaign()
|
|
|
|
+{
|
|
|
|
+ logGlobal->infoStream() << "Open campaign map file: " << scenarioOps->campState->currentMap;
|
|
|
|
+ auto campaign = scenarioOps->campState;
|
|
|
|
+ assert(vstd::contains(campaign->camp->mapPieces, *scenarioOps->campState->currentMap));
|
|
|
|
|
|
- logGlobal->infoStream() << "\tUsing random seed: "<< si->seedToBeUsed;
|
|
|
|
- ran.seed((boost::int32_t)si->seedToBeUsed);
|
|
|
|
- scenarioOps = new StartInfo(*si);
|
|
|
|
- initialOpts = new StartInfo(*si);
|
|
|
|
- si = nullptr;
|
|
|
|
|
|
+ std::string & mapContent = campaign->camp->mapPieces[*scenarioOps->campState->currentMap];
|
|
|
|
+ auto buffer = reinterpret_cast<const ui8 *>(mapContent.data());
|
|
|
|
+ map = CMapService::loadMap(buffer, mapContent.size()).release();
|
|
|
|
+}
|
|
|
|
|
|
- switch(scenarioOps->mode)
|
|
|
|
|
|
+void CGameState::initDuel()
|
|
|
|
+{
|
|
|
|
+ DuelParameters dp;
|
|
|
|
+ try //CLoadFile likes throwing
|
|
{
|
|
{
|
|
- case StartInfo::NEW_GAME:
|
|
|
|
|
|
+ if(boost::algorithm::ends_with(scenarioOps->mapname, ".json"))
|
|
{
|
|
{
|
|
- if(scenarioOps->createRandomMap())
|
|
|
|
- {
|
|
|
|
- logGlobal->infoStream() << "Create random map.";
|
|
|
|
- CStopWatch sw;
|
|
|
|
|
|
+ logGlobal->infoStream() << "Loading duel settings from JSON file: " << scenarioOps->mapname;
|
|
|
|
+ dp = DuelParameters::fromJSON(scenarioOps->mapname);
|
|
|
|
+ logGlobal->infoStream() << "JSON file has been successfully read!";
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ CLoadFile lf(scenarioOps->mapname);
|
|
|
|
+ lf >> dp;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ catch(...)
|
|
|
|
+ {
|
|
|
|
+ logGlobal->errorStream() << "Cannot load duel settings from " << scenarioOps->mapname;
|
|
|
|
+ throw;
|
|
|
|
+ }
|
|
|
|
|
|
- // Gen map
|
|
|
|
- CMapGenerator mapGenerator;
|
|
|
|
- map = mapGenerator.generate(scenarioOps->mapGenOptions.get(), scenarioOps->seedToBeUsed).release();
|
|
|
|
|
|
+ const CArmedInstance *armies[2] = {nullptr};
|
|
|
|
+ const CGHeroInstance *heroes[2] = {nullptr};
|
|
|
|
+ CGTownInstance *town = nullptr;
|
|
|
|
|
|
- // Update starting options
|
|
|
|
- for(int i = 0; i < map->players.size(); ++i)
|
|
|
|
- {
|
|
|
|
- const auto & playerInfo = map->players[i];
|
|
|
|
- if(playerInfo.canAnyonePlay())
|
|
|
|
- {
|
|
|
|
- PlayerSettings & playerSettings = scenarioOps->playerInfos[PlayerColor(i)];
|
|
|
|
- playerSettings.compOnly = !playerInfo.canHumanPlay;
|
|
|
|
- playerSettings.team = playerInfo.team;
|
|
|
|
- playerSettings.castle = playerInfo.defaultCastle();
|
|
|
|
- if(playerSettings.playerID == PlayerSettings::PLAYER_AI && playerSettings.name.empty())
|
|
|
|
- {
|
|
|
|
- playerSettings.name = VLC->generaltexth->allTexts[468];
|
|
|
|
- }
|
|
|
|
- playerSettings.color = PlayerColor(i);
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- scenarioOps->playerInfos.erase(PlayerColor(i));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ for(int i = 0; i < 2; i++)
|
|
|
|
+ {
|
|
|
|
+ CArmedInstance *obj = nullptr;
|
|
|
|
+ if(dp.sides[i].heroId >= 0)
|
|
|
|
+ {
|
|
|
|
+ const DuelParameters::SideSettings &ss = dp.sides[i];
|
|
|
|
+ auto h = new CGHeroInstance();
|
|
|
|
+ armies[i] = heroes[i] = h;
|
|
|
|
+ obj = h;
|
|
|
|
+ h->subID = ss.heroId;
|
|
|
|
+ for(int i = 0; i < ss.heroPrimSkills.size(); i++)
|
|
|
|
+ h->pushPrimSkill(static_cast<PrimarySkill::PrimarySkill>(i), ss.heroPrimSkills[i]);
|
|
|
|
|
|
- logGlobal->infoStream() << boost::format("Generated random map in %i ms.") % sw.getDiff();
|
|
|
|
|
|
+ if(!ss.spells.empty())
|
|
|
|
+ {
|
|
|
|
+ h->putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0));
|
|
|
|
+ boost::copy(ss.spells, std::inserter(h->spells, h->spells.begin()));
|
|
}
|
|
}
|
|
- else
|
|
|
|
|
|
+
|
|
|
|
+ for(auto &parka : ss.artifacts)
|
|
{
|
|
{
|
|
- logGlobal->infoStream() << "Open map file: " << scenarioOps->mapname;
|
|
|
|
- map = CMapService::loadMap(scenarioOps->mapname).release();
|
|
|
|
|
|
+ h->putArtifact(ArtifactPosition(parka.first), parka.second);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ typedef const std::pair<si32, si8> &TSecSKill;
|
|
|
|
+ for(TSecSKill secSkill : ss.heroSecSkills)
|
|
|
|
+ h->setSecSkillLevel(SecondarySkill(secSkill.first), secSkill.second, 1);
|
|
|
|
+
|
|
|
|
+ h->initHero(HeroTypeID(h->subID));
|
|
|
|
+ obj->initObj();
|
|
}
|
|
}
|
|
- break;
|
|
|
|
- case StartInfo::CAMPAIGN:
|
|
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ auto c = new CGCreature();
|
|
|
|
+ armies[i] = obj = c;
|
|
|
|
+ //c->subID = 34;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ obj->setOwner(PlayerColor(i));
|
|
|
|
+
|
|
|
|
+ for(int j = 0; j < ARRAY_COUNT(dp.sides[i].stacks); j++)
|
|
{
|
|
{
|
|
- logGlobal->infoStream() << "Open campaign map file: " << scenarioOps->campState->currentMap;
|
|
|
|
- auto campaign = scenarioOps->campState;
|
|
|
|
- assert(vstd::contains(campaign->camp->mapPieces, *scenarioOps->campState->currentMap));
|
|
|
|
|
|
+ CreatureID cre = dp.sides[i].stacks[j].type;
|
|
|
|
+ TQuantity count = dp.sides[i].stacks[j].count;
|
|
|
|
+ if(count || obj->hasStackAtSlot(SlotID(j)))
|
|
|
|
+ obj->setCreature(SlotID(j), cre, count);
|
|
|
|
+ }
|
|
|
|
|
|
- std::string & mapContent = campaign->camp->mapPieces[*scenarioOps->campState->currentMap];
|
|
|
|
- auto buffer = reinterpret_cast<const ui8 *>(mapContent.data());
|
|
|
|
- map = CMapService::loadMap(buffer, mapContent.size()).release();
|
|
|
|
|
|
+ for(const DuelParameters::CusomCreature &cc : dp.creatures)
|
|
|
|
+ {
|
|
|
|
+ CCreature *c = VLC->creh->creatures[cc.id];
|
|
|
|
+ if(cc.attack >= 0)
|
|
|
|
+ c->getBonusLocalFirst(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK))->val = cc.attack;
|
|
|
|
+ if(cc.defense >= 0)
|
|
|
|
+ c->getBonusLocalFirst(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE))->val = cc.defense;
|
|
|
|
+ if(cc.speed >= 0)
|
|
|
|
+ c->getBonusLocalFirst(Selector::type(Bonus::STACKS_SPEED))->val = cc.speed;
|
|
|
|
+ if(cc.HP >= 0)
|
|
|
|
+ c->getBonusLocalFirst(Selector::type(Bonus::STACK_HEALTH))->val = cc.HP;
|
|
|
|
+ if(cc.dmg >= 0)
|
|
|
|
+ {
|
|
|
|
+ c->getBonusLocalFirst(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1))->val = cc.dmg;
|
|
|
|
+ c->getBonusLocalFirst(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2))->val = cc.dmg;
|
|
|
|
+ }
|
|
|
|
+ if(cc.shoots >= 0)
|
|
|
|
+ c->getBonusLocalFirst(Selector::type(Bonus::SHOTS))->val = cc.shoots;
|
|
}
|
|
}
|
|
- break;
|
|
|
|
- case StartInfo::DUEL:
|
|
|
|
- initDuel();
|
|
|
|
- return;
|
|
|
|
- default:
|
|
|
|
- logGlobal->errorStream() << "Wrong mode: " << (int)scenarioOps->mode;
|
|
|
|
- return;
|
|
|
|
}
|
|
}
|
|
- VLC->arth->initAllowedArtifactsList(map->allowedArtifact);
|
|
|
|
- logGlobal->infoStream() << "Map loaded!";
|
|
|
|
|
|
|
|
- logGlobal->infoStream() << "\tOur checksum for the map: "<< map->checksum;
|
|
|
|
|
|
+ curB = BattleInfo::setupBattle(int3(), dp.terType, dp.bfieldType, armies, heroes, false, town);
|
|
|
|
+ curB->obstacles = dp.obstacles;
|
|
|
|
+ curB->localInit();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void CGameState::checkMapChecksum()
|
|
|
|
+{
|
|
|
|
+ logGlobal->infoStream() << "\tOur checksum for the map: "<< map->checksum;
|
|
if(scenarioOps->mapfileChecksum)
|
|
if(scenarioOps->mapfileChecksum)
|
|
{
|
|
{
|
|
- logGlobal->infoStream() << "\tServer checksum for " << scenarioOps->mapname <<": "<< scenarioOps->mapfileChecksum;
|
|
|
|
|
|
+ logGlobal->infoStream() << "\tServer checksum for " << scenarioOps->mapname <<": "<< scenarioOps->mapfileChecksum;
|
|
if(map->checksum != scenarioOps->mapfileChecksum)
|
|
if(map->checksum != scenarioOps->mapfileChecksum)
|
|
{
|
|
{
|
|
- logGlobal->errorStream() << "Wrong map checksum!!!";
|
|
|
|
|
|
+ logGlobal->errorStream() << "Wrong map checksum!!!";
|
|
throw std::runtime_error("Wrong checksum");
|
|
throw std::runtime_error("Wrong checksum");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
else
|
|
|
|
+ {
|
|
scenarioOps->mapfileChecksum = map->checksum;
|
|
scenarioOps->mapfileChecksum = map->checksum;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
- day = 0;
|
|
|
|
-
|
|
|
|
- logGlobal->debugStream() << "Initialization:";
|
|
|
|
- logGlobal->debugStream() << "\tPicking grail position";
|
|
|
|
- //pick grail location
|
|
|
|
- if(map->grailPos.x < 0 || map->grailRadious) //grail not set or set within a radius around some place
|
|
|
|
- {
|
|
|
|
|
|
+void CGameState::initGrailPosition()
|
|
|
|
+{
|
|
|
|
+ logGlobal->debugStream() << "\tPicking grail position";
|
|
|
|
+ //pick grail location
|
|
|
|
+ if(map->grailPos.x < 0 || map->grailRadious) //grail not set or set within a radius around some place
|
|
|
|
+ {
|
|
if(!map->grailRadious) //radius not given -> anywhere on map
|
|
if(!map->grailRadious) //radius not given -> anywhere on map
|
|
map->grailRadious = map->width * 2;
|
|
map->grailRadious = map->width * 2;
|
|
|
|
|
|
- std::vector<int3> allowedPos;
|
|
|
|
|
|
+ std::vector<int3> allowedPos;
|
|
static const int BORDER_WIDTH = 9; // grail must be at least 9 tiles away from border
|
|
static const int BORDER_WIDTH = 9; // grail must be at least 9 tiles away from border
|
|
|
|
|
|
// add all not blocked tiles in range
|
|
// add all not blocked tiles in range
|
|
@@ -939,13 +1019,13 @@ void CGameState::init(StartInfo * si)
|
|
const TerrainTile &t = map->getTile(int3(i, j, k));
|
|
const TerrainTile &t = map->getTile(int3(i, j, k));
|
|
if(!t.blocked
|
|
if(!t.blocked
|
|
&& !t.visitable
|
|
&& !t.visitable
|
|
- && t.terType != ETerrainType::WATER
|
|
|
|
- && t.terType != ETerrainType::ROCK
|
|
|
|
|
|
+ && t.terType != ETerrainType::WATER
|
|
|
|
+ && t.terType != ETerrainType::ROCK
|
|
&& map->grailPos.dist2d(int3(i,j,k)) <= map->grailRadious)
|
|
&& map->grailPos.dist2d(int3(i,j,k)) <= map->grailRadious)
|
|
- allowedPos.push_back(int3(i,j,k));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ allowedPos.push_back(int3(i,j,k));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
//remove tiles with holes
|
|
//remove tiles with holes
|
|
for(auto & elem : map->objects)
|
|
for(auto & elem : map->objects)
|
|
@@ -955,11 +1035,13 @@ void CGameState::init(StartInfo * si)
|
|
if(allowedPos.size())
|
|
if(allowedPos.size())
|
|
map->grailPos = allowedPos[ran() % allowedPos.size()];
|
|
map->grailPos = allowedPos[ran() % allowedPos.size()];
|
|
else
|
|
else
|
|
- logGlobal->warnStream() << "Warning: Grail cannot be placed, no appropriate tile found!";
|
|
|
|
|
|
+ logGlobal->warnStream() << "Warning: Grail cannot be placed, no appropriate tile found!";
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
- //picking random factions for players
|
|
|
|
- logGlobal->debugStream() << "\tPicking random factions for players";
|
|
|
|
|
|
+void CGameState::initRandomFactionsForPlayers()
|
|
|
|
+{
|
|
|
|
+ logGlobal->debugStream() << "\tPicking random factions for players";
|
|
for(auto & elem : scenarioOps->playerInfos)
|
|
for(auto & elem : scenarioOps->playerInfos)
|
|
{
|
|
{
|
|
if(elem.second.castle==-1)
|
|
if(elem.second.castle==-1)
|
|
@@ -971,30 +1053,36 @@ void CGameState::init(StartInfo * si)
|
|
elem.second.castle = *iter;
|
|
elem.second.castle = *iter;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
- //randomizing objects
|
|
|
|
- logGlobal->debugStream() << "\tRandomizing objects";
|
|
|
|
|
|
+void CGameState::randomizeMapObjects()
|
|
|
|
+{
|
|
|
|
+ logGlobal->debugStream() << "\tRandomizing objects";
|
|
for(CGObjectInstance *obj : map->objects)
|
|
for(CGObjectInstance *obj : map->objects)
|
|
{
|
|
{
|
|
- if(!obj)
|
|
|
|
- continue;
|
|
|
|
|
|
+ if(!obj) continue;
|
|
|
|
|
|
randomizeObject(obj);
|
|
randomizeObject(obj);
|
|
obj->hoverName = VLC->generaltexth->names[obj->ID];
|
|
obj->hoverName = VLC->generaltexth->names[obj->ID];
|
|
|
|
|
|
//handle Favouring Winds - mark tiles under it
|
|
//handle Favouring Winds - mark tiles under it
|
|
if(obj->ID == Obj::FAVORABLE_WINDS)
|
|
if(obj->ID == Obj::FAVORABLE_WINDS)
|
|
|
|
+ {
|
|
for (int i = 0; i < obj->getWidth() ; i++)
|
|
for (int i = 0; i < obj->getWidth() ; i++)
|
|
|
|
+ {
|
|
for (int j = 0; j < obj->getHeight() ; j++)
|
|
for (int j = 0; j < obj->getHeight() ; j++)
|
|
{
|
|
{
|
|
int3 pos = obj->pos - int3(i,j,0);
|
|
int3 pos = obj->pos - int3(i,j,0);
|
|
- if(map->isInTheMap(pos))
|
|
|
|
- map->getTile(pos).extTileFlags |= 128;
|
|
|
|
|
|
+ if(map->isInTheMap(pos)) map->getTile(pos).extTileFlags |= 128;
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
- /*********creating players entries in gs****************************************/
|
|
|
|
- logGlobal->debugStream() << "\tCreating player entries in gs";
|
|
|
|
|
|
+void CGameState::initPlayerStates()
|
|
|
|
+{
|
|
|
|
+ logGlobal->debugStream() << "\tCreating player entries in gs";
|
|
for(auto & elem : scenarioOps->playerInfos)
|
|
for(auto & elem : scenarioOps->playerInfos)
|
|
{
|
|
{
|
|
std::pair<PlayerColor, PlayerState> ins(elem.first,PlayerState());
|
|
std::pair<PlayerColor, PlayerState> ins(elem.first,PlayerState());
|
|
@@ -1005,8 +1093,10 @@ void CGameState::init(StartInfo * si)
|
|
teams[ins.second.team].players.insert(ins.first);//add player to team
|
|
teams[ins.second.team].players.insert(ins.first);//add player to team
|
|
players.insert(ins);
|
|
players.insert(ins);
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
- /*************************replace hero placeholders*****************************/
|
|
|
|
|
|
+void CGameState::initHeroPlaceholders()
|
|
|
|
+{
|
|
if (scenarioOps->campState)
|
|
if (scenarioOps->campState)
|
|
{
|
|
{
|
|
logGlobal->debugStream() << "\tReplacing hero placeholders";
|
|
logGlobal->debugStream() << "\tReplacing hero placeholders";
|
|
@@ -1015,47 +1105,48 @@ void CGameState::init(StartInfo * si)
|
|
logGlobal->debugStream() << "\tSetting up heroes";
|
|
logGlobal->debugStream() << "\tSetting up heroes";
|
|
placeCampaignHeroes(campHeroReplacements);
|
|
placeCampaignHeroes(campHeroReplacements);
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
-
|
|
|
|
- /*********give starting hero****************************************/
|
|
|
|
- logGlobal->debugStream() << "\tGiving starting hero";
|
|
|
|
|
|
+void CGameState::placeStartingHeroes()
|
|
|
|
+{
|
|
|
|
+ logGlobal->debugStream() << "\tGiving starting hero";
|
|
|
|
+ bool campaignGiveHero = false;
|
|
|
|
+ if(scenarioOps->campState)
|
|
{
|
|
{
|
|
- bool campaignGiveHero = false;
|
|
|
|
- if(scenarioOps->campState)
|
|
|
|
|
|
+ if(auto bonus = scenarioOps->campState->getBonusForCurrentMap())
|
|
{
|
|
{
|
|
- if(auto bonus = scenarioOps->campState->getBonusForCurrentMap())
|
|
|
|
- {
|
|
|
|
- campaignGiveHero = scenarioOps->mode == StartInfo::CAMPAIGN &&
|
|
|
|
- bonus.get().type == CScenarioTravel::STravelBonus::HERO;
|
|
|
|
- }
|
|
|
|
|
|
+ campaignGiveHero = scenarioOps->mode == StartInfo::CAMPAIGN &&
|
|
|
|
+ bonus.get().type == CScenarioTravel::STravelBonus::HERO;
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- for(auto it = scenarioOps->playerInfos.begin(); it != scenarioOps->playerInfos.end(); ++it)
|
|
|
|
|
|
+ for(auto it = scenarioOps->playerInfos.begin(); it != scenarioOps->playerInfos.end(); ++it)
|
|
|
|
+ {
|
|
|
|
+ const PlayerInfo &p = map->players[it->first.getNum()];
|
|
|
|
+ bool generateHero = (p.generateHeroAtMainTown ||
|
|
|
|
+ (it->second.playerID != PlayerSettings::PLAYER_AI && campaignGiveHero)) && p.hasMainTown;
|
|
|
|
+ if(generateHero && vstd::contains(scenarioOps->playerInfos, it->first))
|
|
{
|
|
{
|
|
- const PlayerInfo &p = map->players[it->first.getNum()];
|
|
|
|
- bool generateHero = (p.generateHeroAtMainTown ||
|
|
|
|
- (it->second.playerID != PlayerSettings::PLAYER_AI && campaignGiveHero)) && p.hasMainTown;
|
|
|
|
- if(generateHero && vstd::contains(scenarioOps->playerInfos, it->first))
|
|
|
|
- {
|
|
|
|
- int3 hpos = p.posOfMainTown;
|
|
|
|
- hpos.x+=1;
|
|
|
|
|
|
+ int3 hpos = p.posOfMainTown;
|
|
|
|
+ hpos.x+=1;
|
|
|
|
|
|
- int h = pickHero(it->first);
|
|
|
|
- if(it->second.hero == -1)
|
|
|
|
- it->second.hero = h;
|
|
|
|
|
|
+ int h = pickHero(it->first);
|
|
|
|
+ if(it->second.hero == -1)
|
|
|
|
+ it->second.hero = h;
|
|
|
|
|
|
- CGHeroInstance * nnn = static_cast<CGHeroInstance*>(createObject(Obj::HERO,h,hpos,it->first));
|
|
|
|
- nnn->id = ObjectInstanceID(map->objects.size());
|
|
|
|
- nnn->initHero();
|
|
|
|
- map->heroesOnMap.push_back(nnn);
|
|
|
|
- map->objects.push_back(nnn);
|
|
|
|
- map->addBlockVisTiles(nnn);
|
|
|
|
- }
|
|
|
|
|
|
+ CGHeroInstance * nnn = static_cast<CGHeroInstance*>(createObject(Obj::HERO,h,hpos,it->first));
|
|
|
|
+ nnn->id = ObjectInstanceID(map->objects.size());
|
|
|
|
+ nnn->initHero();
|
|
|
|
+ map->heroesOnMap.push_back(nnn);
|
|
|
|
+ map->objects.push_back(nnn);
|
|
|
|
+ map->addBlockVisTiles(nnn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
- /******************RESOURCES****************************************************/
|
|
|
|
- logGlobal->debugStream() << "\tSetting up resources";
|
|
|
|
|
|
+void CGameState::initStartingResources()
|
|
|
|
+{
|
|
|
|
+ logGlobal->debugStream() << "\tSetting up resources";
|
|
const JsonNode config(ResourceID("config/startres.json"));
|
|
const JsonNode config(ResourceID("config/startres.json"));
|
|
const JsonVector &vector = config["difficulty"].Vector();
|
|
const JsonVector &vector = config["difficulty"].Vector();
|
|
const JsonNode &level = vector[scenarioOps->difficulty];
|
|
const JsonNode &level = vector[scenarioOps->difficulty];
|
|
@@ -1073,6 +1164,19 @@ void CGameState::init(StartInfo * si)
|
|
p.resources = startresAI;
|
|
p.resources = startresAI;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ auto getHumanPlayerInfo = [&]() -> std::vector<const PlayerSettings *>
|
|
|
|
+ {
|
|
|
|
+ std::vector<const PlayerSettings *> ret;
|
|
|
|
+ for(auto it = scenarioOps->playerInfos.cbegin();
|
|
|
|
+ it != scenarioOps->playerInfos.cend(); ++it)
|
|
|
|
+ {
|
|
|
|
+ if(it->second.playerID != PlayerSettings::PLAYER_AI)
|
|
|
|
+ ret.push_back(&it->second);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+ };
|
|
|
|
+
|
|
//give start resource bonus in case of campaign
|
|
//give start resource bonus in case of campaign
|
|
if (scenarioOps->mode == StartInfo::CAMPAIGN)
|
|
if (scenarioOps->mode == StartInfo::CAMPAIGN)
|
|
{
|
|
{
|
|
@@ -1106,14 +1210,15 @@ void CGameState::init(StartInfo * si)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
-
|
|
|
|
- /*************************HEROES INIT / POOL************************************************/
|
|
|
|
|
|
+void CGameState::initHeroes()
|
|
|
|
+{
|
|
for(auto hero : map->heroesOnMap) //heroes instances initialization
|
|
for(auto hero : map->heroesOnMap) //heroes instances initialization
|
|
{
|
|
{
|
|
if (hero->getOwner() == PlayerColor::UNFLAGGABLE)
|
|
if (hero->getOwner() == PlayerColor::UNFLAGGABLE)
|
|
{
|
|
{
|
|
- logGlobal->warnStream() << "Warning - hero with uninitialized owner!";
|
|
|
|
|
|
+ logGlobal->warnStream() << "Warning - hero with uninitialized owner!";
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1154,7 +1259,7 @@ void CGameState::init(StartInfo * si)
|
|
|
|
|
|
for(auto & elem : map->disposedHeroes)
|
|
for(auto & elem : map->disposedHeroes)
|
|
{
|
|
{
|
|
- hpool.pavailable[elem.heroId] = elem.players;
|
|
|
|
|
|
+ hpool.pavailable[elem.heroId] = elem.players;
|
|
}
|
|
}
|
|
|
|
|
|
if (scenarioOps->mode == StartInfo::CAMPAIGN) //give campaign bonuses for specific / best hero
|
|
if (scenarioOps->mode == StartInfo::CAMPAIGN) //give campaign bonuses for specific / best hero
|
|
@@ -1187,7 +1292,7 @@ void CGameState::init(StartInfo * si)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(maxB < 0)
|
|
if(maxB < 0)
|
|
- logGlobal->warnStream() << "Warning - cannot give bonus to hero cause there are no heroes!";
|
|
|
|
|
|
+ logGlobal->warnStream() << "Warning - cannot give bonus to hero cause there are no heroes!";
|
|
else
|
|
else
|
|
giveCampaignBonusToHero(heroes[maxB]);
|
|
giveCampaignBonusToHero(heroes[maxB]);
|
|
}
|
|
}
|
|
@@ -1204,9 +1309,68 @@ void CGameState::init(StartInfo * si)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void CGameState::giveCampaignBonusToHero(CGHeroInstance * hero)
|
|
|
|
+{
|
|
|
|
+ const boost::optional<CScenarioTravel::STravelBonus> & curBonus = scenarioOps->campState->getBonusForCurrentMap();
|
|
|
|
+ if(!curBonus)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if(curBonus->isBonusForHero())
|
|
|
|
+ {
|
|
|
|
+ //apply bonus
|
|
|
|
+ switch (curBonus->type)
|
|
|
|
+ {
|
|
|
|
+ case CScenarioTravel::STravelBonus::SPELL:
|
|
|
|
+ hero->spells.insert(SpellID(curBonus->info2));
|
|
|
|
+ break;
|
|
|
|
+ case CScenarioTravel::STravelBonus::MONSTER:
|
|
|
|
+ {
|
|
|
|
+ for(int i=0; i<GameConstants::ARMY_SIZE; i++)
|
|
|
|
+ {
|
|
|
|
+ if(hero->slotEmpty(SlotID(i)))
|
|
|
|
+ {
|
|
|
|
+ hero->addToSlot(SlotID(i), CreatureID(curBonus->info2), curBonus->info3);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case CScenarioTravel::STravelBonus::ARTIFACT:
|
|
|
|
+ gs->giveHeroArtifact(hero, static_cast<ArtifactID>(curBonus->info2));
|
|
|
|
+ break;
|
|
|
|
+ case CScenarioTravel::STravelBonus::SPELL_SCROLL:
|
|
|
|
+ {
|
|
|
|
+ CArtifactInstance * scroll = CArtifactInstance::createScroll(SpellID(curBonus->info2).toSpell());
|
|
|
|
+ scroll->putAt(ArtifactLocation(hero, scroll->firstAvailableSlot(hero)));
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case CScenarioTravel::STravelBonus::PRIMARY_SKILL:
|
|
|
|
+ {
|
|
|
|
+ const ui8* ptr = reinterpret_cast<const ui8*>(&curBonus->info2);
|
|
|
|
+ for (int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
|
|
|
|
+ {
|
|
|
|
+ int val = ptr[g];
|
|
|
|
+ if (val == 0)
|
|
|
|
+ {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ auto bb = new Bonus(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::CAMPAIGN_BONUS, val, *scenarioOps->campState->currentMap, g);
|
|
|
|
+ hero->addNewBonus(bb);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case CScenarioTravel::STravelBonus::SECONDARY_SKILL:
|
|
|
|
+ hero->setSecSkillLevel(SecondarySkill(curBonus->info2), curBonus->info3, true);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
- /*************************FOG**OF**WAR******************************************/
|
|
|
|
- logGlobal->debugStream() << "\tFog of war"; //FIXME: should be initialized after all bonuses are set
|
|
|
|
|
|
+void CGameState::initFogOfWar()
|
|
|
|
+{
|
|
|
|
+ logGlobal->debugStream() << "\tFog of war"; //FIXME: should be initialized after all bonuses are set
|
|
for(auto & elem : teams)
|
|
for(auto & elem : teams)
|
|
{
|
|
{
|
|
elem.second.fogOfWarMap.resize(map->width);
|
|
elem.second.fogOfWarMap.resize(map->width);
|
|
@@ -1234,8 +1398,11 @@ void CGameState::init(StartInfo * si)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
- logGlobal->debugStream() << "\tStarting bonuses";
|
|
|
|
|
|
+void CGameState::initStartingBonus()
|
|
|
|
+{
|
|
|
|
+ logGlobal->debugStream() << "\tStarting bonuses";
|
|
for(auto & elem : players)
|
|
for(auto & elem : players)
|
|
{
|
|
{
|
|
//starting bonus
|
|
//starting bonus
|
|
@@ -1262,22 +1429,25 @@ void CGameState::init(StartInfo * si)
|
|
}
|
|
}
|
|
case PlayerSettings::ARTIFACT:
|
|
case PlayerSettings::ARTIFACT:
|
|
{
|
|
{
|
|
- if(!elem.second.heroes.size())
|
|
|
|
|
|
+ if(!elem.second.heroes.size())
|
|
{
|
|
{
|
|
- logGlobal->debugStream() << "Cannot give starting artifact - no heroes!";
|
|
|
|
|
|
+ logGlobal->debugStream() << "Cannot give starting artifact - no heroes!";
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
- CArtifact *toGive;
|
|
|
|
- toGive = VLC->arth->artifacts[VLC->arth->getRandomArt (CArtifact::ART_TREASURE)];
|
|
|
|
|
|
+ CArtifact *toGive;
|
|
|
|
+ toGive = VLC->arth->artifacts[VLC->arth->getRandomArt (CArtifact::ART_TREASURE)];
|
|
|
|
|
|
CGHeroInstance *hero = elem.second.heroes[0];
|
|
CGHeroInstance *hero = elem.second.heroes[0];
|
|
- giveHeroArtifact(hero, toGive->id);
|
|
|
|
|
|
+ giveHeroArtifact(hero, toGive->id);
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- /****************************TOWNS************************************************/
|
|
|
|
- logGlobal->debugStream() << "\tTowns";
|
|
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void CGameState::initTowns()
|
|
|
|
+{
|
|
|
|
+ logGlobal->debugStream() << "\tTowns";
|
|
|
|
|
|
//campaign bonuses for towns
|
|
//campaign bonuses for towns
|
|
if (scenarioOps->mode == StartInfo::CAMPAIGN)
|
|
if (scenarioOps->mode == StartInfo::CAMPAIGN)
|
|
@@ -1329,7 +1499,7 @@ void CGameState::init(StartInfo * si)
|
|
}
|
|
}
|
|
|
|
|
|
//#1444 - remove entries that don't have buildings defined (like some unused extra town hall buildings)
|
|
//#1444 - remove entries that don't have buildings defined (like some unused extra town hall buildings)
|
|
- vstd::erase_if(vti->builtBuildings, [vti](BuildingID bid){
|
|
|
|
|
|
+ vstd::erase_if(vti->builtBuildings, [vti](BuildingID bid){
|
|
return !vti->town->buildings.count(bid) || !vti->town->buildings.at(bid); });
|
|
return !vti->town->buildings.count(bid) || !vti->town->buildings.at(bid); });
|
|
|
|
|
|
if (vstd::contains(vti->builtBuildings, BuildingID::SHIPYARD) && vti->shipyardStatus()==IBoatGenerator::TILE_BLOCKED)
|
|
if (vstd::contains(vti->builtBuildings, BuildingID::SHIPYARD) && vti->shipyardStatus()==IBoatGenerator::TILE_BLOCKED)
|
|
@@ -1415,8 +1585,11 @@ void CGameState::init(StartInfo * si)
|
|
if(vti->getOwner() != PlayerColor::NEUTRAL)
|
|
if(vti->getOwner() != PlayerColor::NEUTRAL)
|
|
getPlayer(vti->getOwner())->towns.push_back(vti);
|
|
getPlayer(vti->getOwner())->towns.push_back(vti);
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
- logGlobal->debugStream() << "\tObject initialization";
|
|
|
|
|
|
+void CGameState::initMapObjects()
|
|
|
|
+{
|
|
|
|
+ logGlobal->debugStream() << "\tObject initialization";
|
|
objCaller->preInit();
|
|
objCaller->preInit();
|
|
for(CGObjectInstance *obj : map->objects)
|
|
for(CGObjectInstance *obj : map->objects)
|
|
{
|
|
{
|
|
@@ -1440,9 +1613,10 @@ void CGameState::init(StartInfo * si)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CGTeleport::postInit(); //pairing subterranean gates
|
|
CGTeleport::postInit(); //pairing subterranean gates
|
|
|
|
+}
|
|
|
|
|
|
- buildBonusSystemTree();
|
|
|
|
-
|
|
|
|
|
|
+void CGameState::initVisitingAndGarrisonedHeroes()
|
|
|
|
+{
|
|
for(auto k=players.begin(); k!=players.end(); ++k)
|
|
for(auto k=players.begin(); k!=players.end(); ++k)
|
|
{
|
|
{
|
|
if(k->first==PlayerColor::NEUTRAL)
|
|
if(k->first==PlayerColor::NEUTRAL)
|
|
@@ -1468,124 +1642,6 @@ void CGameState::init(StartInfo * si)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
-
|
|
|
|
- logGlobal->debugStream() << "\tChecking objectives";
|
|
|
|
- map->checkForObjectives(); //needs to be run when all objects are properly placed
|
|
|
|
-
|
|
|
|
- int seedAfterInit = ran();
|
|
|
|
- logGlobal->infoStream() << "Seed after init is " << seedAfterInit << " (before was " << scenarioOps->seedToBeUsed << ")";
|
|
|
|
- if(scenarioOps->seedPostInit > 0)
|
|
|
|
- {
|
|
|
|
- //RNG must be in the same state on all machines when initialization is done (otherwise we have desync)
|
|
|
|
- assert(scenarioOps->seedPostInit == seedAfterInit);
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- scenarioOps->seedPostInit = seedAfterInit; //store the post init "seed"
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void CGameState::initDuel()
|
|
|
|
-{
|
|
|
|
- DuelParameters dp;
|
|
|
|
- try //CLoadFile likes throwing
|
|
|
|
- {
|
|
|
|
- if(boost::algorithm::ends_with(scenarioOps->mapname, ".json"))
|
|
|
|
- {
|
|
|
|
- logGlobal->infoStream() << "Loading duel settings from JSON file: " << scenarioOps->mapname;
|
|
|
|
- dp = DuelParameters::fromJSON(scenarioOps->mapname);
|
|
|
|
- logGlobal->infoStream() << "JSON file has been successfully read!";
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- CLoadFile lf(scenarioOps->mapname);
|
|
|
|
- lf >> dp;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- catch(...)
|
|
|
|
- {
|
|
|
|
- logGlobal->errorStream() << "Cannot load duel settings from " << scenarioOps->mapname;
|
|
|
|
- throw;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const CArmedInstance *armies[2] = {nullptr};
|
|
|
|
- const CGHeroInstance *heroes[2] = {nullptr};
|
|
|
|
- CGTownInstance *town = nullptr;
|
|
|
|
-
|
|
|
|
- for(int i = 0; i < 2; i++)
|
|
|
|
- {
|
|
|
|
- CArmedInstance *obj = nullptr;
|
|
|
|
- if(dp.sides[i].heroId >= 0)
|
|
|
|
- {
|
|
|
|
- const DuelParameters::SideSettings &ss = dp.sides[i];
|
|
|
|
- auto h = new CGHeroInstance();
|
|
|
|
- armies[i] = heroes[i] = h;
|
|
|
|
- obj = h;
|
|
|
|
- h->subID = ss.heroId;
|
|
|
|
- for(int i = 0; i < ss.heroPrimSkills.size(); i++)
|
|
|
|
- h->pushPrimSkill(static_cast<PrimarySkill::PrimarySkill>(i), ss.heroPrimSkills[i]);
|
|
|
|
-
|
|
|
|
- if(!ss.spells.empty())
|
|
|
|
- {
|
|
|
|
- h->putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0));
|
|
|
|
- boost::copy(ss.spells, std::inserter(h->spells, h->spells.begin()));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for(auto &parka : ss.artifacts)
|
|
|
|
- {
|
|
|
|
- h->putArtifact(ArtifactPosition(parka.first), parka.second);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- typedef const std::pair<si32, si8> &TSecSKill;
|
|
|
|
- for(TSecSKill secSkill : ss.heroSecSkills)
|
|
|
|
- h->setSecSkillLevel(SecondarySkill(secSkill.first), secSkill.second, 1);
|
|
|
|
-
|
|
|
|
- h->initHero(HeroTypeID(h->subID));
|
|
|
|
- obj->initObj();
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- auto c = new CGCreature();
|
|
|
|
- armies[i] = obj = c;
|
|
|
|
- //c->subID = 34;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- obj->setOwner(PlayerColor(i));
|
|
|
|
-
|
|
|
|
- for(int j = 0; j < ARRAY_COUNT(dp.sides[i].stacks); j++)
|
|
|
|
- {
|
|
|
|
- CreatureID cre = dp.sides[i].stacks[j].type;
|
|
|
|
- TQuantity count = dp.sides[i].stacks[j].count;
|
|
|
|
- if(count || obj->hasStackAtSlot(SlotID(j)))
|
|
|
|
- obj->setCreature(SlotID(j), cre, count);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for(const DuelParameters::CusomCreature &cc : dp.creatures)
|
|
|
|
- {
|
|
|
|
- CCreature *c = VLC->creh->creatures[cc.id];
|
|
|
|
- if(cc.attack >= 0)
|
|
|
|
- c->getBonusLocalFirst(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK))->val = cc.attack;
|
|
|
|
- if(cc.defense >= 0)
|
|
|
|
- c->getBonusLocalFirst(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE))->val = cc.defense;
|
|
|
|
- if(cc.speed >= 0)
|
|
|
|
- c->getBonusLocalFirst(Selector::type(Bonus::STACKS_SPEED))->val = cc.speed;
|
|
|
|
- if(cc.HP >= 0)
|
|
|
|
- c->getBonusLocalFirst(Selector::type(Bonus::STACK_HEALTH))->val = cc.HP;
|
|
|
|
- if(cc.dmg >= 0)
|
|
|
|
- {
|
|
|
|
- c->getBonusLocalFirst(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1))->val = cc.dmg;
|
|
|
|
- c->getBonusLocalFirst(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2))->val = cc.dmg;
|
|
|
|
- }
|
|
|
|
- if(cc.shoots >= 0)
|
|
|
|
- c->getBonusLocalFirst(Selector::type(Bonus::SHOTS))->val = cc.shoots;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- curB = BattleInfo::setupBattle(int3(), dp.terType, dp.bfieldType, armies, heroes, false, town);
|
|
|
|
- curB->obstacles = dp.obstacles;
|
|
|
|
- curB->localInit();
|
|
|
|
- return;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
BFieldType CGameState::battleGetBattlefieldType(int3 tile) const
|
|
BFieldType CGameState::battleGetBattlefieldType(int3 tile) const
|
|
@@ -2555,32 +2611,31 @@ std::vector<std::pair<CGHeroInstance*, ObjectInstanceID> > CGameState::campaignH
|
|
//selecting heroes by type
|
|
//selecting heroes by type
|
|
for(int g=0; g<map->objects.size(); ++g)
|
|
for(int g=0; g<map->objects.size(); ++g)
|
|
{
|
|
{
|
|
- const ObjectInstanceID gid = ObjectInstanceID(g);
|
|
|
|
CGObjectInstance * obj = map->objects[g];
|
|
CGObjectInstance * obj = map->objects[g];
|
|
- if (obj->ID != Obj::HERO_PLACEHOLDER)
|
|
|
|
|
|
+ if (obj->ID == Obj::HERO_PLACEHOLDER)
|
|
{
|
|
{
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- CGHeroPlaceholder * hp = static_cast<CGHeroPlaceholder*>(obj);
|
|
|
|
|
|
+ CGHeroPlaceholder * hp = static_cast<CGHeroPlaceholder*>(obj);
|
|
|
|
|
|
- if(hp->subID != 0xFF) //select by type
|
|
|
|
- {
|
|
|
|
- bool found = false;
|
|
|
|
- for(auto ghi : Xheroes)
|
|
|
|
|
|
+ const ObjectInstanceID gid = ObjectInstanceID(g);
|
|
|
|
+ if(hp->subID != 0xFF) //select by type
|
|
{
|
|
{
|
|
- if (ghi->subID == hp->subID)
|
|
|
|
|
|
+ bool found = false;
|
|
|
|
+ for(auto ghi : Xheroes)
|
|
{
|
|
{
|
|
- found = true;
|
|
|
|
- replaceHero(gid, ghi);
|
|
|
|
- Xheroes -= ghi;
|
|
|
|
- break;
|
|
|
|
|
|
+ if (ghi->subID == hp->subID)
|
|
|
|
+ {
|
|
|
|
+ found = true;
|
|
|
|
+ replaceHero(gid, ghi);
|
|
|
|
+ Xheroes -= ghi;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (!found)
|
|
|
|
+ {
|
|
|
|
+ auto nh = new CGHeroInstance();
|
|
|
|
+ nh->initHero(HeroTypeID(hp->subID));
|
|
|
|
+ replaceHero(gid, nh);
|
|
}
|
|
}
|
|
- }
|
|
|
|
- if (!found)
|
|
|
|
- {
|
|
|
|
- auto nh = new CGHeroInstance();
|
|
|
|
- nh->initHero(HeroTypeID(hp->subID));
|
|
|
|
- replaceHero(gid, nh);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -2594,26 +2649,25 @@ std::vector<std::pair<CGHeroInstance*, ObjectInstanceID> > CGameState::campaignH
|
|
|
|
|
|
for(int g=0; g<map->objects.size(); ++g)
|
|
for(int g=0; g<map->objects.size(); ++g)
|
|
{
|
|
{
|
|
- const ObjectInstanceID gid = ObjectInstanceID(g);
|
|
|
|
CGObjectInstance * obj = map->objects[g];
|
|
CGObjectInstance * obj = map->objects[g];
|
|
- if (obj->ID != Obj::HERO_PLACEHOLDER)
|
|
|
|
|
|
+ if (obj->ID == Obj::HERO_PLACEHOLDER)
|
|
{
|
|
{
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- CGHeroPlaceholder * hp = static_cast<CGHeroPlaceholder*>(obj);
|
|
|
|
|
|
+ CGHeroPlaceholder * hp = static_cast<CGHeroPlaceholder*>(obj);
|
|
|
|
|
|
- if (hp->subID == 0xFF) //select by power
|
|
|
|
- {
|
|
|
|
- if(Xheroes.size() > hp->power - 1)
|
|
|
|
- replaceHero(gid, Xheroes[hp->power - 1]);
|
|
|
|
- else
|
|
|
|
|
|
+ const ObjectInstanceID gid = ObjectInstanceID(g);
|
|
|
|
+ if (hp->subID == 0xFF) //select by power
|
|
{
|
|
{
|
|
- logGlobal->warnStream() << "Warning, no hero to replace!";
|
|
|
|
- map->removeBlockVisTiles(hp, true);
|
|
|
|
- delete hp;
|
|
|
|
- map->objects[g] = nullptr;
|
|
|
|
|
|
+ if(Xheroes.size() > hp->power - 1)
|
|
|
|
+ replaceHero(gid, Xheroes[hp->power - 1]);
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ logGlobal->warnStream() << "Warning, no hero to replace!";
|
|
|
|
+ map->removeBlockVisTiles(hp, true);
|
|
|
|
+ delete hp;
|
|
|
|
+ map->objects[g] = nullptr;
|
|
|
|
+ }
|
|
|
|
+ //we don't have to remove hero from Xheroes because it would destroy the order and duplicates shouldn't happen
|
|
}
|
|
}
|
|
- //we don't have to remove hero from Xheroes because it would destroy the order and duplicates shouldn't happen
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|