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:
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user