1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-03 00:46:55 +02:00

Full rework of pre-game interface and networking

New features for players:
* Loading for multiplayer. Any save could be used for multiplayer.
* Restart for multiplayer. All clients will restart together.
* Loading from single save.
* Hotseat mixed with network game. Multiple players per client.
* Now connection to server could be cancelled.
* Return to menu on disconnections instead of crashes.
* Restoring of last selected map, save or campaign on next run.

TLDR on important changes in engine code:
* UI: work with server separated from UI
* UI: all explitic blitting replaced with IntObject's
* UI: all new code use smart pointers instead of DISPOSE
* Gameplay always start through lobby controlled by server.
* Threads receiving netpacks now shared for lobby and gameplay.
* Campaigns: heroes for crossover now serialized as JsonNode.
This commit is contained in:
Arseniy Shestakov
2018-01-05 20:21:07 +03:00
parent 14f03e22da
commit ac66fc7f42
85 changed files with 8808 additions and 7938 deletions

View File

@ -64,8 +64,6 @@ public:
}
};
static CApplier<CBaseForGSApply> *applierGs = nullptr;
void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst) const
{
int type = txt.first, ser = txt.second;
@ -673,9 +671,9 @@ int CGameState::getDate(Date::EDateType mode) const
CGameState::CGameState()
{
gs = this;
applierGs = new CApplier<CBaseForGSApply>();
registerTypesClientPacks1(*applierGs);
registerTypesClientPacks2(*applierGs);
applier = std::make_shared<CApplier<CBaseForGSApply>>();
registerTypesClientPacks1(*applier);
registerTypesClientPacks2(*applier);
//objCaller = new CObjectCallersHandler();
globalEffects.setDescription("Global effects");
globalEffects.setNodeType(CBonusSystemNode::GLOBAL_EFFECTS);
@ -686,10 +684,6 @@ CGameState::~CGameState()
{
map.dellNull();
curB.dellNull();
//delete scenarioOps; //TODO: fix for loading ind delete
//delete initialOpts;
delete applierGs;
//delete objCaller;
for(auto ptr : hpool.heroesPool) // clean hero pool
ptr.second.dellNull();
@ -709,7 +703,7 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, bool allow
initNewGame(mapService, allowSavingRandomMap);
break;
case StartInfo::CAMPAIGN:
initCampaign(mapService);
initCampaign();
break;
default:
logGlobal->error("Wrong mode: %d", static_cast<int>(scenarioOps->mode));
@ -765,6 +759,13 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, bool allow
}
}
void CGameState::updateOnLoad(StartInfo * si)
{
scenarioOps->playerInfos = si->playerInfos;
for(auto & i : si->playerInfos)
gs->players[i.first].human = i.second.isControlledByHuman();
}
void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRandomMap)
{
if(scenarioOps->createRandomMap())
@ -815,7 +816,7 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
playerSettings.compOnly = !playerInfo.canHumanPlay;
playerSettings.team = playerInfo.team;
playerSettings.castle = playerInfo.defaultCastle();
if(playerSettings.playerID == PlayerSettings::PLAYER_AI && playerSettings.name.empty())
if(playerSettings.isControlledByAI() && playerSettings.name.empty())
{
playerSettings.name = VLC->generaltexth->allTexts[468];
}
@ -837,19 +838,10 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
}
}
void CGameState::initCampaign(const IMapService * mapService)
void CGameState::initCampaign()
{
logGlobal->info("Open campaign map file: %d", scenarioOps->campState->currentMap.get());
auto campaign = scenarioOps->campState;
assert(vstd::contains(campaign->camp->mapPieces, *scenarioOps->campState->currentMap));
std::string scenarioName = scenarioOps->mapname.substr(0, scenarioOps->mapname.find('.'));
boost::to_lower(scenarioName);
scenarioName += ':' + boost::lexical_cast<std::string>(*campaign->currentMap);
std::string & mapContent = campaign->camp->mapPieces[*campaign->currentMap];
auto buffer = reinterpret_cast<const ui8 *>(mapContent.data());
map = mapService->loadMap(buffer, mapContent.size(), scenarioName).release();
map = scenarioOps->campState->getMap();
}
void CGameState::checkMapChecksum()
@ -962,13 +954,11 @@ void CGameState::initPlayerStates()
for(auto & elem : scenarioOps->playerInfos)
{
PlayerState & p = players[elem.first];
//std::pair<PlayerColor, PlayerState> ins(elem.first,PlayerState());
p.color=elem.first;
p.human = elem.second.playerID;
p.human = elem.second.isControlledByHuman();
p.team = map->players[elem.first.getNum()].team;
teams[p.team].id = p.team;//init team
teams[p.team].players.insert(elem.first);//add player to team
//players.insert(ins);
}
}
@ -1091,13 +1081,26 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
auto bonus = campaignState->getBonusForCurrentMap();
if (bonus && bonus->type == CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO)
{
crossoverHeroes.heroesFromAnyPreviousScenarios = crossoverHeroes.heroesFromPreviousScenario = campaignState->camp->scenarios[bonus->info2].crossoverHeroes;
std::vector<CGHeroInstance *> heroes;
for(auto & node : campaignState->camp->scenarios[bonus->info2].crossoverHeroes)
{
auto h = CCampaignState::crossoverDeserialize(node);
heroes.push_back(h);
}
crossoverHeroes.heroesFromAnyPreviousScenarios = crossoverHeroes.heroesFromPreviousScenario = heroes;
}
else
{
if(!campaignState->mapsConquered.empty())
{
crossoverHeroes.heroesFromPreviousScenario = campaignState->camp->scenarios[campaignState->mapsConquered.back()].crossoverHeroes;
std::vector<CGHeroInstance *> heroes;
for(auto & node : campaignState->camp->scenarios[campaignState->mapsConquered.back()].crossoverHeroes)
{
auto h = CCampaignState::crossoverDeserialize(node);
heroes.push_back(h);
}
crossoverHeroes.heroesFromAnyPreviousScenarios = crossoverHeroes.heroesFromPreviousScenario = heroes;
crossoverHeroes.heroesFromPreviousScenario = heroes;
for(auto mapNr : campaignState->mapsConquered)
{
@ -1108,15 +1111,22 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
// remove heroes which didn't reached the end of the scenario, but were available at the start
for(auto hero : lostCrossoverHeroes)
{
vstd::erase_if(crossoverHeroes.heroesFromAnyPreviousScenarios,
CGObjectInstanceBySubIdFinder(hero));
// auto hero = CCampaignState::crossoverDeserialize(node);
vstd::erase_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h)
{
return hero->subID == h->subID;
});
}
// now add heroes which completed the scenario
for(auto hero : scenario.crossoverHeroes)
for(auto node : scenario.crossoverHeroes)
{
auto hero = CCampaignState::crossoverDeserialize(node);
// add new heroes and replace old heroes with newer ones
auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, CGObjectInstanceBySubIdFinder(hero));
auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h)
{
return hero->subID == h->subID;
});
if (it != crossoverHeroes.heroesFromAnyPreviousScenarios.end())
{
// replace old hero with newer one
@ -1306,7 +1316,7 @@ void CGameState::initStartingResources()
for(auto it = scenarioOps->playerInfos.cbegin();
it != scenarioOps->playerInfos.cend(); ++it)
{
if(it->second.playerID != PlayerSettings::PLAYER_AI)
if(it->second.isControlledByHuman())
ret.push_back(&it->second);
}
@ -1955,7 +1965,7 @@ PlayerRelations::PlayerRelations CGameState::getPlayerRelations( PlayerColor col
void CGameState::apply(CPack *pack)
{
ui16 typ = typeList.getTypeID(pack);
applierGs->getApplier(typ)->applyOnGS(this,pack);
applier->getApplier(typ)->applyOnGS(this, pack);
}
void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out)
@ -2806,7 +2816,7 @@ void CGameState::replaceHeroesPlaceholders(const std::vector<CGameState::Campaig
map->objects[heroToPlace->id.getNum()] = heroToPlace;
map->addBlockVisTiles(heroToPlace);
scenarioOps->campState->getCurrentScenario().placedCrossoverHeroes.push_back(heroToPlace);
scenarioOps->campState->getCurrentScenario().placedCrossoverHeroes.push_back(CCampaignState::crossoverSerialize(heroToPlace));
}
}