mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-23 00:28:08 +02:00
Significant changes to saving system. Now both client and server store their lib part.
Desync detection upon loading. Fixed many desyncs. (more remain) Monsters won't have creature count 0 even if that is set as creature properties.
This commit is contained in:
@ -246,7 +246,7 @@ void CCallback::setSelection(const CArmedInstance * obj)
|
||||
cl->calculatePaths(static_cast<const CGHeroInstance *>(obj));
|
||||
|
||||
//nasty workaround. TODO: nice workaround
|
||||
cl->gs->getPlayer(*player)->currentSelection = obj->id;
|
||||
//cl->gs->getPlayer(*player)->currentSelection = obj->id;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -905,7 +905,7 @@ void CSelectionScreen::startScenario()
|
||||
overWrite += boost::bind(&CCallback::save, LOCPLINT->cb, saveGameName);
|
||||
overWrite += bind(&CGuiHandler::popIntTotally, &GH, this);
|
||||
|
||||
if(CResourceHandler::get()->existsResource(ResourceID(saveGameName, EResType::LIB_SAVEGAME)))
|
||||
if(CResourceHandler::get()->existsResource(ResourceID(saveGameName, EResType::CLIENT_SAVEGAME)))
|
||||
{
|
||||
std::string hlp = CGI->generaltexth->allTexts[493]; //%s exists. Overwrite?
|
||||
boost::algorithm::replace_first(hlp, "%s", sel->txt->text);
|
||||
@ -1124,13 +1124,13 @@ void SelectionTab::parseGames(const std::vector<ResourceID> &files, bool multi)
|
||||
try
|
||||
{
|
||||
CLoadFile lf(CResourceHandler::get()->getResourceName(files[i]));
|
||||
|
||||
ui8 sign[8];
|
||||
lf >> sign;
|
||||
if(std::memcmp(sign,"VCMISVG",7))
|
||||
{
|
||||
throw std::runtime_error("not a correct savefile!");
|
||||
}
|
||||
lf.checkMagicBytes(SAVEGAME_MAGIC);
|
||||
// ui8 sign[8];
|
||||
// lf >> sign;
|
||||
// if(std::memcmp(sign,"VCMISVG",7))
|
||||
// {
|
||||
// throw std::runtime_error("not a correct savefile!");
|
||||
// }
|
||||
|
||||
// Create the map info object
|
||||
CMapInfo mapInfo;
|
||||
@ -1207,7 +1207,7 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const boost::function<void(
|
||||
|
||||
case CMenuScreen::loadGame:
|
||||
case CMenuScreen::saveGame:
|
||||
parseGames(getFiles("Saves/", EResType::LIB_SAVEGAME), MultiPlayer);
|
||||
parseGames(getFiles("Saves/", EResType::CLIENT_SAVEGAME), MultiPlayer);
|
||||
if(tabType == CMenuScreen::loadGame)
|
||||
{
|
||||
positions = 18;
|
||||
@ -1343,7 +1343,7 @@ void SelectionTab::select( int position )
|
||||
if(txt)
|
||||
{
|
||||
std::string filename = CResourceHandler::get()->getResourceName(
|
||||
ResourceID(curItems[py]->fileURI, EResType::LIB_SAVEGAME));
|
||||
ResourceID(curItems[py]->fileURI, EResType::CLIENT_SAVEGAME));
|
||||
txt->setTxt(CFileInfo(filename).getBaseName());
|
||||
}
|
||||
|
||||
@ -1476,7 +1476,7 @@ void SelectionTab::printMaps(SDL_Surface *to)
|
||||
else
|
||||
{
|
||||
name = CFileInfo(CResourceHandler::get()->getResourceName(
|
||||
ResourceID(currentItem->fileURI, EResType::LIB_SAVEGAME))).getBaseName();
|
||||
ResourceID(currentItem->fileURI, EResType::CLIENT_SAVEGAME))).getBaseName();
|
||||
}
|
||||
|
||||
//print name
|
||||
|
@ -232,29 +232,27 @@ void CClient::loadGame( const std::string & fname )
|
||||
|
||||
CStopWatch tmh;
|
||||
{
|
||||
char sig[8];
|
||||
CMapHeader dum;
|
||||
const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
|
||||
StartInfo *si;
|
||||
|
||||
CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME)));
|
||||
lf >> sig >> dum >> si;
|
||||
tlog0 <<"Reading save signature: "<<tmh.getDiff()<<std::endl;
|
||||
|
||||
lf >> *VLC;
|
||||
const_cast<CGameInfo*>(CGI)->setFromLib();
|
||||
tlog0 <<"Reading handlers: "<<tmh.getDiff()<<std::endl;
|
||||
|
||||
lf >> gs;
|
||||
tlog0 <<"Reading gamestate: "<<tmh.getDiff()<<std::endl;
|
||||
auto clientSaveName = CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME));
|
||||
auto controlServerSaveName = CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME));
|
||||
|
||||
unique_ptr<CLoadFile> loader;
|
||||
{
|
||||
CLoadIntegrityValidator checkingLoader(clientSaveName, controlServerSaveName);
|
||||
loadCommonState(checkingLoader);
|
||||
loader = checkingLoader.decay();
|
||||
}
|
||||
tlog0 << "Loaded common part of save " << tmh.getDiff()<<std::endl;
|
||||
const_cast<CGameInfo*>(CGI)->state = gs;
|
||||
const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
|
||||
const_cast<CGameInfo*>(CGI)->mh->map = gs->map;
|
||||
pathInfo = new CPathsInfo(int3(gs->map->width, gs->map->height, gs->map->twoLevel ? 2 : 1));
|
||||
CGI->mh->init();
|
||||
|
||||
tlog0 <<"Initing maphandler: "<<tmh.getDiff()<<std::endl;
|
||||
|
||||
*loader >> *this;
|
||||
tlog0 << "Loaded client part of save " << tmh.getDiff()<<std::endl;
|
||||
}
|
||||
|
||||
serv = sh.connectToServer();
|
||||
serv->addStdVecItems(gs);
|
||||
|
||||
@ -280,10 +278,6 @@ void CClient::loadGame( const std::string & fname )
|
||||
serv->enableStackSendingByID();
|
||||
serv->disableSmartPointerSerialization();
|
||||
|
||||
{
|
||||
CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME)));
|
||||
lf >> *this;
|
||||
}
|
||||
}
|
||||
|
||||
void CClient::newGame( CConnection *con, StartInfo *si )
|
||||
|
@ -191,6 +191,7 @@ public:
|
||||
void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) OVERRIDE {};
|
||||
void removeArtifact(const ArtifactLocation &al) OVERRIDE {};
|
||||
bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) OVERRIDE {return false;};
|
||||
void synchronizeArtifactHandlerLists() OVERRIDE {};
|
||||
|
||||
void showCompInfo(ShowInInfobox * comp) OVERRIDE {};
|
||||
void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE {};
|
||||
|
@ -781,12 +781,32 @@ void SaveGame::applyCl(CClient *cl)
|
||||
try
|
||||
{
|
||||
CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::CLIENT_SAVEGAME)));
|
||||
cl->saveCommonState(save);
|
||||
save << *cl;
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
tlog1 << "Failed to save game:" << e.what() << std::endl;
|
||||
}
|
||||
|
||||
// try
|
||||
// {
|
||||
// auto clientPart = CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::CLIENT_SAVEGAME));
|
||||
// auto libPart = CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::LIB_SAVEGAME));
|
||||
// CLoadIntegrityValidator checker(clientPart, libPart);
|
||||
//
|
||||
// CMapHeader mh;
|
||||
// StartInfo *si;
|
||||
// LibClasses *lib;
|
||||
// CGameState *game;
|
||||
//
|
||||
// checker.checkMagicBytes(SAVEGAME_MAGIC);
|
||||
// checker >> mh >> si >> lib >> game;
|
||||
// }
|
||||
// catch(...)
|
||||
// {
|
||||
// tlog1 << "Desync!!!\n";
|
||||
// }
|
||||
}
|
||||
|
||||
void PlayerMessage::applyCl(CClient *cl)
|
||||
|
@ -646,7 +646,7 @@ void CMapHandler::terrainRect( int3 top_tile, ui8 anim, const std::vector< std::
|
||||
else //blit normal object
|
||||
{
|
||||
const std::vector<Cimage> &ourImages = graphics->getDef(obj)->ourImages;
|
||||
SDL_Surface *bitmap = ourImages[(anim+obj->animPhaseShift)%ourImages.size()].bitmap;
|
||||
SDL_Surface *bitmap = ourImages[(anim+getPhaseShift(obj))%ourImages.size()].bitmap;
|
||||
|
||||
//setting appropriate flag color
|
||||
if(color < 8 || color==255)
|
||||
@ -1101,6 +1101,19 @@ void CMapHandler::getTerrainDescr( const int3 &pos, std::string & out, bool terN
|
||||
out = CGI->generaltexth->terrainNames[t.terType];
|
||||
}
|
||||
|
||||
ui8 CMapHandler::getPhaseShift(const CGObjectInstance *object) const
|
||||
{
|
||||
auto i = animationPhase.find(object);
|
||||
if(i == animationPhase.end())
|
||||
{
|
||||
ui8 ret = rand() % 255;
|
||||
animationPhase[object] = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return i->second;
|
||||
}
|
||||
|
||||
TerrainTile2::TerrainTile2()
|
||||
:terbitmap(0)
|
||||
{}
|
||||
|
@ -99,6 +99,8 @@ public:
|
||||
|
||||
std::vector<std::vector<std::vector<ui8> > > hideBitmap; //specifies number of graphic that should be used to fully hide a tile
|
||||
|
||||
mutable std::map<const CGObjectInstance*, ui8> animationPhase;
|
||||
|
||||
static const bool MARK_BLOCKED_POSITIONS;
|
||||
static const bool MARK_VISITABLE_POSITIONS;
|
||||
|
||||
@ -106,6 +108,7 @@ public:
|
||||
~CMapHandler(); //d-tor
|
||||
|
||||
std::pair<SDL_Surface *, bool> getVisBitmap(const int3 & pos, const std::vector< std::vector< std::vector<ui8> > > & visibilityMap) const; //returns appropriate bitmap and info if alpha blitting is necessary
|
||||
ui8 getPhaseShift(const CGObjectInstance *object) const;
|
||||
|
||||
std::vector< std::string > getObjDescriptions(int3 pos); //returns desriptions of objects blocking given position
|
||||
void getTerrainDescr(const int3 &pos, std::string & out, bool terName); //if tername == false => empty string when tile is clear
|
||||
|
@ -561,40 +561,10 @@ ArtifactID CArtHandler::getRandomArt(int flags)
|
||||
}
|
||||
ArtifactID CArtHandler::getArtSync (ui32 rand, int flags, bool erasePicked)
|
||||
{
|
||||
auto erasePickedArt = [&]( ArtifactID id )
|
||||
{
|
||||
std::vector<CArtifact*>* ptr;
|
||||
CArtifact *art = artifacts[id];
|
||||
switch (art->aClass)
|
||||
{
|
||||
case CArtifact::ART_TREASURE:
|
||||
ptr = &treasures;
|
||||
break;
|
||||
case CArtifact::ART_MINOR:
|
||||
ptr = &minors;
|
||||
break;
|
||||
case CArtifact::ART_MAJOR:
|
||||
ptr = &majors;
|
||||
break;
|
||||
case CArtifact::ART_RELIC:
|
||||
ptr = &relics;
|
||||
break;
|
||||
default: //special artifacts should not be erased
|
||||
return;
|
||||
}
|
||||
ptr->erase (std::find(ptr->begin(), ptr->end(), art)); //remove the artifact from available list
|
||||
};
|
||||
|
||||
auto getAllowedArts = [&](std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, int flag)
|
||||
auto getAllowedArts = [&](std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, CArtifact::EartClass flag)
|
||||
{
|
||||
if (arts->empty()) //restock available arts
|
||||
{
|
||||
for (int i = 0; i < allowedArtifacts.size(); ++i)
|
||||
{
|
||||
if (allowedArtifacts[i]->aClass == flag)
|
||||
arts->push_back(allowedArtifacts[i]);
|
||||
}
|
||||
}
|
||||
fillList(*arts, flag);
|
||||
|
||||
for (int i = 0; i < arts->size(); ++i)
|
||||
{
|
||||
@ -625,6 +595,8 @@ ArtifactID CArtHandler::getArtSync (ui32 rand, int flags, bool erasePicked)
|
||||
out.resize (64);
|
||||
std::fill_n (out.begin(), 64, artifacts[2]); //Give Grail - this can't be banned (hopefully)
|
||||
}
|
||||
|
||||
tlog0 << "Treasure count: " << treasures.size() << std::endl;
|
||||
};
|
||||
|
||||
std::vector<ConstTransitivePtr<CArtifact> > out;
|
||||
@ -829,6 +801,55 @@ std::vector<bool> CArtHandler::getDefaultAllowedArtifacts() const
|
||||
return allowedArtifacts;
|
||||
}
|
||||
|
||||
void CArtHandler::erasePickedArt(ArtifactID id)
|
||||
{
|
||||
CArtifact *art = artifacts[id];
|
||||
|
||||
if(auto artifactList = listFromClass(art->aClass))
|
||||
{
|
||||
if(artifactList->empty())
|
||||
fillList(*artifactList, art->aClass);
|
||||
|
||||
auto itr = vstd::find(*artifactList, art);
|
||||
if(itr != artifactList->end())
|
||||
{
|
||||
artifactList->erase(itr);
|
||||
}
|
||||
else
|
||||
tlog2 << "Problem: cannot erase artifact " << art->Name() << " from list, it was not present\n";
|
||||
|
||||
}
|
||||
else
|
||||
tlog2 << "Problem: cannot find list for artifact " << art->Name() << ", strange class. (special?)\n";
|
||||
}
|
||||
|
||||
boost::optional<std::vector<CArtifact*>&> CArtHandler::listFromClass( CArtifact::EartClass artifactClass )
|
||||
{
|
||||
switch(artifactClass)
|
||||
{
|
||||
case CArtifact::ART_TREASURE:
|
||||
return treasures;
|
||||
case CArtifact::ART_MINOR:
|
||||
return minors;
|
||||
case CArtifact::ART_MAJOR:
|
||||
return majors;
|
||||
case CArtifact::ART_RELIC:
|
||||
return relics;
|
||||
default: //special artifacts should not be erased
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CArtHandler::fillList( std::vector<CArtifact*> &listToBeFilled, CArtifact::EartClass artifactClass )
|
||||
{
|
||||
assert(listToBeFilled.empty());
|
||||
for (int i = 0; i < allowedArtifacts.size(); ++i)
|
||||
{
|
||||
if (allowedArtifacts[i]->aClass == artifactClass)
|
||||
listToBeFilled.push_back(allowedArtifacts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
CArtifactInstance::CArtifactInstance()
|
||||
{
|
||||
init();
|
||||
|
@ -212,6 +212,12 @@ public:
|
||||
void addBonuses();
|
||||
void clear();
|
||||
void clearHlpLists();
|
||||
|
||||
//void restockArtifactList()''
|
||||
void fillList(std::vector<CArtifact*> &listToBeFilled, CArtifact::EartClass artifactClass); //fills given empty list with allowed artifacts of gibven class. No side effects
|
||||
|
||||
boost::optional<std::vector<CArtifact*>&> listFromClass(CArtifact::EartClass artifactClass);
|
||||
void erasePickedArt(ArtifactID id);
|
||||
ArtifactID getRandomArt (int flags);
|
||||
ArtifactID getArtSync (ui32 rand, int flags, bool erasePicked = false);
|
||||
bool legalArtifact(ArtifactID id);
|
||||
|
@ -56,6 +56,7 @@ void foofoofoo()
|
||||
registerTypes((COSer<CConnection>&)*ccc);
|
||||
registerTypes((CSaveFile&)*ccc);
|
||||
registerTypes((CLoadFile&)*ccc);
|
||||
registerTypes((CLoadIntegrityValidator&)*ccc);
|
||||
registerTypes((CTypeList&)*ccc);
|
||||
}
|
||||
|
||||
@ -815,7 +816,7 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
break;
|
||||
case CScenarioTravel::STravelBonus::ARTIFACT:
|
||||
gs->giveHeroArtifact(hero, curBonus->info2);
|
||||
gs->giveHeroArtifact(hero, static_cast<ArtifactID>(curBonus->info2));
|
||||
break;
|
||||
case CScenarioTravel::STravelBonus::SPELL_SCROLL:
|
||||
{
|
||||
@ -962,8 +963,8 @@ void CGameState::init(StartInfo * si)
|
||||
|
||||
day = 0;
|
||||
|
||||
tlog4 << "Initialization:";
|
||||
tlog4 << "\tPicking grail position";
|
||||
tlog4 << "Initialization:\n";
|
||||
tlog4 << "\tPicking grail position\n";
|
||||
//pick grail location
|
||||
if(map->grailPos.x < 0 || map->grailRadious) //grail not set or set within a radius around some place
|
||||
{
|
||||
@ -1003,7 +1004,7 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
|
||||
//picking random factions for players
|
||||
tlog4 << "\tPicking random factions for players";
|
||||
tlog4 << "\tPicking random factions for players\n";
|
||||
for(auto it = scenarioOps->playerInfos.begin();
|
||||
it != scenarioOps->playerInfos.end(); ++it)
|
||||
{
|
||||
@ -1018,7 +1019,7 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
|
||||
//randomizing objects
|
||||
tlog4 << "\tRandomizing objects";
|
||||
tlog4 << "\tRandomizing objects\n";
|
||||
BOOST_FOREACH(CGObjectInstance *obj, map->objects)
|
||||
{
|
||||
if(!obj)
|
||||
@ -1040,7 +1041,7 @@ void CGameState::init(StartInfo * si)
|
||||
//std::cout<<"\tRandomizing objects: "<<th.getDif()<<std::endl;
|
||||
|
||||
/*********creating players entries in gs****************************************/
|
||||
tlog4 << "\tCreating player entries in gs";
|
||||
tlog4 << "\tCreating player entries in gs\n";
|
||||
for(auto it = scenarioOps->playerInfos.begin();
|
||||
it != scenarioOps->playerInfos.end(); ++it)
|
||||
{
|
||||
@ -1054,7 +1055,7 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
|
||||
/*********give starting hero****************************************/
|
||||
tlog4 << "\tGiving starting hero";
|
||||
tlog4 << "\tGiving starting hero\n";
|
||||
{
|
||||
bool campaignGiveHero = false;
|
||||
if(scenarioOps->campState)
|
||||
@ -1093,7 +1094,7 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
|
||||
/*************************replace hero placeholders*****************************/
|
||||
tlog4 << "\tReplacing hero placeholders";
|
||||
tlog4 << "\tReplacing hero placeholders\n";
|
||||
std::vector<std::pair<CGHeroInstance*, ObjectInstanceID> > campHeroReplacements; //instance, id in vector
|
||||
if (scenarioOps->campState)
|
||||
{
|
||||
@ -1186,7 +1187,7 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
|
||||
/******************RESOURCES****************************************************/
|
||||
tlog4 << "\tSetting up resources";
|
||||
tlog4 << "\tSetting up resources\n";
|
||||
const JsonNode config(ResourceID("config/startres.json"));
|
||||
const JsonVector &vector = config["difficulty"].Vector();
|
||||
const JsonNode &level = vector[scenarioOps->difficulty];
|
||||
@ -1240,7 +1241,7 @@ void CGameState::init(StartInfo * si)
|
||||
|
||||
|
||||
/*************************HEROES************************************************/
|
||||
tlog4 << "\tSetting up heroes";
|
||||
tlog4 << "\tSetting up heroes\n";
|
||||
//Replace placeholders with heroes from previous missions
|
||||
BOOST_FOREACH(auto obj, campHeroReplacements)
|
||||
{
|
||||
@ -1374,7 +1375,7 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
|
||||
/*************************FOG**OF**WAR******************************************/
|
||||
tlog4 << "\tFog of war"; //FIXME: should be initialized after all bonuses are set
|
||||
tlog4 << "\tFog of war\n"; //FIXME: should be initialized after all bonuses are set
|
||||
for(auto k=teams.begin(); k!=teams.end(); ++k)
|
||||
{
|
||||
k->second.fogOfWarMap.resize(map->width);
|
||||
@ -1403,7 +1404,7 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
}
|
||||
|
||||
tlog4 << "\tStarting bonuses";
|
||||
tlog4 << "\tStarting bonuses\n";
|
||||
for(auto k=players.begin(); k!=players.end(); ++k)
|
||||
{
|
||||
//starting bonus
|
||||
@ -1445,7 +1446,7 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
}
|
||||
/****************************TOWNS************************************************/
|
||||
tlog4 << "\tTowns";
|
||||
tlog4 << "\tTowns\n";
|
||||
|
||||
//campaign bonuses for towns
|
||||
if (scenarioOps->mode == StartInfo::CAMPAIGN)
|
||||
@ -1519,16 +1520,16 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
|
||||
//town events
|
||||
BOOST_FOREACH(CCastleEvent *ev, vti->events)
|
||||
BOOST_FOREACH(CCastleEvent &ev, vti->events)
|
||||
{
|
||||
for (int i = 0; i<GameConstants::CREATURES_PER_TOWN; i++)
|
||||
if (vstd::contains(ev->buildings,(-31-i))) //if we have horde for this level
|
||||
if (vstd::contains(ev.buildings,(-31-i))) //if we have horde for this level
|
||||
{
|
||||
ev->buildings.erase(BuildingID(-31-i));
|
||||
ev.buildings.erase(BuildingID(-31-i));
|
||||
if (vti->town->hordeLvl[0] == i)
|
||||
ev->buildings.insert(BuildingID::HORDE_1);
|
||||
ev.buildings.insert(BuildingID::HORDE_1);
|
||||
if (vti->town->hordeLvl[1] == i)
|
||||
ev->buildings.insert(BuildingID::HORDE_2);
|
||||
ev.buildings.insert(BuildingID::HORDE_2);
|
||||
}
|
||||
}
|
||||
//init spells
|
||||
@ -1573,7 +1574,7 @@ void CGameState::init(StartInfo * si)
|
||||
getPlayer(vti->getOwner())->towns.push_back(vti);
|
||||
}
|
||||
|
||||
tlog4 << "\tObject initialization";
|
||||
tlog4 << "\tObject initialization\n";
|
||||
objCaller->preInit();
|
||||
BOOST_FOREACH(CGObjectInstance *obj, map->objects)
|
||||
{
|
||||
@ -1627,7 +1628,7 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
|
||||
|
||||
tlog4 << "\tChecking objectives";
|
||||
tlog4 << "\tChecking objectives\n";
|
||||
map->checkForObjectives(); //needs to be run when all objects are properly placed
|
||||
|
||||
int seedAfterInit = ran();
|
||||
@ -2643,7 +2644,7 @@ void CGameState::attachArmedObjects()
|
||||
}
|
||||
}
|
||||
|
||||
void CGameState::giveHeroArtifact(CGHeroInstance *h, int aid)
|
||||
void CGameState::giveHeroArtifact(CGHeroInstance *h, ArtifactID aid)
|
||||
{
|
||||
CArtifact * const artifact = VLC->arth->artifacts[aid]; //pointer to constant object
|
||||
CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact);
|
||||
|
@ -394,7 +394,7 @@ public:
|
||||
void randomizeObject(CGObjectInstance *cur);
|
||||
std::pair<Obj,int> pickObject(CGObjectInstance *obj); //chooses type of object to be randomized, returns <type, subtype>
|
||||
int pickHero(int owner);
|
||||
void giveHeroArtifact(CGHeroInstance *h, int aid);
|
||||
void giveHeroArtifact(CGHeroInstance *h, ArtifactID aid);
|
||||
|
||||
void apply(CPack *pack);
|
||||
BFieldType battleGetBattlefieldType(int3 tile) const;
|
||||
|
@ -257,7 +257,7 @@ int CGObjectInstance::getOwner() const
|
||||
return tempOwner; //won't have owner
|
||||
}
|
||||
|
||||
CGObjectInstance::CGObjectInstance(): animPhaseShift(rand()%0xff)
|
||||
CGObjectInstance::CGObjectInstance()
|
||||
{
|
||||
pos = int3(-1,-1,-1);
|
||||
//std::cout << "Tworze obiekt "<<this<<std::endl;
|
||||
@ -3141,6 +3141,12 @@ void CGCreature::initObj()
|
||||
amount = c.ammMax;
|
||||
else
|
||||
amount = c.ammMin + (ran() % (c.ammMax - c.ammMin));
|
||||
|
||||
if(!amount) //armies with 0 creatures are illegal
|
||||
{
|
||||
tlog2 << "Problem: stack " << nodeName() << " cannot have 0 creatures. Check properties of " << c.nodeName() << std::endl;
|
||||
amount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
temppower = stacks[SlotID(0)]->count * 1000;
|
||||
@ -3506,13 +3512,13 @@ void CGResource::initObj()
|
||||
switch(subID)
|
||||
{
|
||||
case 6:
|
||||
amount = 500 + (rand()%6)*100;
|
||||
amount = 500 + (ran()%6)*100;
|
||||
break;
|
||||
case 0: case 2:
|
||||
amount = 6 + (rand()%5);
|
||||
amount = 6 + (ran()%5);
|
||||
break;
|
||||
default:
|
||||
amount = 3 + (rand()%3);
|
||||
amount = 3 + (ran()%3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -5894,43 +5900,56 @@ void CBank::reset(ui16 var1) //prevents desync
|
||||
|
||||
void CBank::initialize() const
|
||||
{
|
||||
cb->setObjProperty (id, 14, ran()); //synchronous reset
|
||||
cb->setObjProperty (id, ObjProperty::BANK_RESET, ran()); //synchronous reset
|
||||
|
||||
for (ui8 i = 0; i <= 3; i++)
|
||||
{
|
||||
for (ui8 n = 0; n < bc->artifacts[i]; n++) //new function using proper randomization algorithm
|
||||
for (ui8 n = 0; n < bc->artifacts[i]; n++)
|
||||
{
|
||||
cb->setObjProperty (id, 18 + i, ran()); //synchronic artifacts
|
||||
CArtifact::EartClass artClass;
|
||||
switch(i)
|
||||
{
|
||||
case 0: artClass = CArtifact::ART_TREASURE; break;
|
||||
case 1: artClass = CArtifact::ART_MINOR; break;
|
||||
case 2: artClass = CArtifact::ART_MAJOR; break;
|
||||
case 3: artClass = CArtifact::ART_RELIC; break;
|
||||
default: assert(0); continue;
|
||||
}
|
||||
|
||||
int artID = cb->getArtSync(ran(), artClass, true);
|
||||
cb->setObjProperty(id, ObjProperty::BANK_ADD_ARTIFACT, artID);
|
||||
}
|
||||
}
|
||||
cb->setObjProperty (id, 17, ran()); //get army
|
||||
|
||||
cb->setObjProperty (id, ObjProperty::BANK_INIT_ARMY, ran()); //get army
|
||||
}
|
||||
void CBank::setPropertyDer (ui8 what, ui32 val)
|
||||
/// random values are passed as arguments and processed identically on all clients
|
||||
{
|
||||
switch (what)
|
||||
{
|
||||
case 11: //daycounter
|
||||
case ObjProperty::BANK_DAYCOUNTER: //daycounter
|
||||
if (val == 0)
|
||||
daycounter = 1; //yes, 1
|
||||
else
|
||||
daycounter++;
|
||||
break;
|
||||
case 12: //multiplier, in percent
|
||||
case ObjProperty::BANK_MULTIPLIER: //multiplier, in percent
|
||||
multiplier = val / 100.0;
|
||||
break;
|
||||
case 13: //bank preset
|
||||
bc = VLC->objh->banksInfo[index][val];
|
||||
break;
|
||||
case 14:
|
||||
case ObjProperty::BANK_RESET:
|
||||
reset (val%100);
|
||||
break;
|
||||
case 15:
|
||||
case ObjProperty::BANK_CLEAR_CONFIG:
|
||||
bc = NULL;
|
||||
break;
|
||||
case 16: //remove rewards from Derelict Ship
|
||||
case ObjProperty::BANK_CLEAR_ARTIFACTS: //remove rewards from Derelict Ship
|
||||
artifacts.clear();
|
||||
break;
|
||||
case 17: //set ArmedInstance army
|
||||
case ObjProperty::BANK_INIT_ARMY: //set ArmedInstance army
|
||||
{
|
||||
int upgraded = 0;
|
||||
if (val%100 < bc->upgradeChance) //once again anti-desync
|
||||
@ -5975,28 +5994,9 @@ void CBank::setPropertyDer (ui8 what, ui32 val)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 18: //add Artifact
|
||||
case ObjProperty::BANK_ADD_ARTIFACT: //add Artifact
|
||||
{
|
||||
int id = cb->getArtSync(val, CArtifact::ART_TREASURE, true);
|
||||
artifacts.push_back (id);
|
||||
break;
|
||||
}
|
||||
case 19: //add Artifact
|
||||
{
|
||||
int id = cb->getArtSync(val, CArtifact::ART_MINOR, true);
|
||||
artifacts.push_back (id);
|
||||
break;
|
||||
}
|
||||
case 20: //add Artifact
|
||||
{
|
||||
int id = cb->getArtSync(val, CArtifact::ART_MAJOR, true);
|
||||
artifacts.push_back (id);
|
||||
break;
|
||||
}
|
||||
case 21: //add Artifact
|
||||
{
|
||||
int id = cb->getArtSync(val, CArtifact::ART_RELIC, true);
|
||||
artifacts.push_back (id);
|
||||
artifacts.push_back (val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -6011,15 +6011,15 @@ void CBank::newTurn() const
|
||||
else if (daycounter >= 28 && (subID < 13 || subID > 16)) //no reset for Emissaries
|
||||
{
|
||||
initialize();
|
||||
cb->setObjProperty (id, 11, 0); //daycounter 0
|
||||
cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 0); //daycounter 0
|
||||
if (ID == Obj::DERELICT_SHIP && cb->getDate() > 1)
|
||||
{
|
||||
cb->setObjProperty (id, 12, 0);//ugly hack to make derelict ships usable only once
|
||||
cb->setObjProperty (id, 16, 0);
|
||||
cb->setObjProperty (id, ObjProperty::BANK_MULTIPLIER, 0);//ugly hack to make derelict ships usable only once
|
||||
cb->setObjProperty (id, ObjProperty::BANK_CLEAR_ARTIFACTS, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
cb->setObjProperty (id, 11, 1); //daycounter++
|
||||
cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++
|
||||
}
|
||||
}
|
||||
bool CBank::wasVisited (TPlayerColor player) const
|
||||
@ -6215,7 +6215,7 @@ void CBank::endBattle (const CGHeroInstance *h, const BattleResult *result) cons
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->giveCreatures(this, h, ourArmy, false);
|
||||
}
|
||||
cb->setObjProperty (id, 15, 0); //bc = NULL
|
||||
cb->setObjProperty (id, ObjProperty::BANK_CLEAR_CONFIG, 0); //bc = NULL
|
||||
}
|
||||
|
||||
}
|
||||
@ -6227,13 +6227,13 @@ void CGPyramid::initObj()
|
||||
if (available.size())
|
||||
{
|
||||
bc = VLC->objh->banksInfo[21].front(); //TODO: remove hardcoded value?
|
||||
spell = (available[rand()%available.size()]);
|
||||
spell = (available[ran()%available.size()]);
|
||||
}
|
||||
else
|
||||
{
|
||||
tlog1 <<"No spells available for Pyramid! Object set to empty.\n";
|
||||
}
|
||||
setPropertyDer (17,ran()); //set guards at game start
|
||||
setPropertyDer (ObjProperty::BANK_INIT_ARMY,ran()); //set guards at game start
|
||||
}
|
||||
const std::string & CGPyramid::getHoverText() const
|
||||
{
|
||||
@ -6284,7 +6284,7 @@ void CGPyramid::endBattle (const CGHeroInstance *h, const BattleResult *result)
|
||||
iw.components.push_back(Component (Component::SPELL, spell, 0, 0));
|
||||
}
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->setObjProperty (id, 15, 0);
|
||||
cb->setObjProperty (id, ObjProperty::BANK_CLEAR_CONFIG, 0);
|
||||
}
|
||||
}
|
||||
void CGKeys::setPropertyDer (ui8 what, ui32 val) //101-108 - enable key for player 1-8
|
||||
|
@ -172,7 +172,6 @@ public:
|
||||
si32 subID; //normal subID (this one from OH3 maps ;])
|
||||
ObjectInstanceID id;//number of object in map's vector
|
||||
CGDefInfo * defInfo;
|
||||
ui8 animPhaseShift;
|
||||
|
||||
TPlayerColor tempOwner;
|
||||
bool blockVisit; //if non-zero then blocks the tile but is visitable from neighbouring tile
|
||||
@ -211,7 +210,7 @@ public:
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & hoverName & pos & ID & subID & id & animPhaseShift & tempOwner & blockVisit & defInfo;
|
||||
h & hoverName & pos & ID & subID & id & tempOwner & blockVisit & defInfo;
|
||||
//definfo is handled by map serializer
|
||||
}
|
||||
protected:
|
||||
@ -591,7 +590,7 @@ public:
|
||||
std::vector<CGTownBuilding*> bonusingBuildings;
|
||||
std::vector<SpellID> possibleSpells, obligatorySpells;
|
||||
std::vector<std::vector<SpellID> > spells; //spells[level] -> vector of spells, first will be available in guild
|
||||
std::list<CCastleEvent*> events;
|
||||
std::list<CCastleEvent> events;
|
||||
std::pair<si32, si32> bonusValue;//var to store town bonuses (rampart = resources from mystic pond);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -344,6 +344,11 @@ void CSaveFile::clear()
|
||||
sfile = nullptr;
|
||||
}
|
||||
|
||||
void CSaveFile::putMagicBytes( const std::string &text )
|
||||
{
|
||||
write(text.c_str(), text.length());
|
||||
}
|
||||
|
||||
CLoadFile::CLoadFile(const std::string &fname, int minimalVersion /*= version*/)
|
||||
{
|
||||
registerTypes(*this);
|
||||
@ -424,6 +429,14 @@ void CLoadFile::clear()
|
||||
fileVersion = 0;
|
||||
}
|
||||
|
||||
void CLoadFile::checkMagicBytes( const std::string &text )
|
||||
{
|
||||
std::string loaded = text;
|
||||
read(loaded.c_str(), text.length());
|
||||
if(loaded != text)
|
||||
throw std::runtime_error("Magic bytes doesn't match!");
|
||||
}
|
||||
|
||||
CTypeList::CTypeList()
|
||||
{
|
||||
registerTypes(*this);
|
||||
@ -477,3 +490,47 @@ void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib)
|
||||
registerVectoredType(&gs->map->quests, &CQuest::qid);
|
||||
smartVectorMembersSerialization = true;
|
||||
}
|
||||
|
||||
CLoadIntegrityValidator::CLoadIntegrityValidator( const std::string &primaryFileName, const std::string &controlFileName, int minimalVersion /*= version*/ )
|
||||
: foundDesync(false)
|
||||
{
|
||||
registerTypes(*this);
|
||||
primaryFile = make_unique<CLoadFile>(primaryFileName, minimalVersion);
|
||||
controlFile = make_unique<CLoadFile>(controlFileName, minimalVersion);
|
||||
}
|
||||
|
||||
int CLoadIntegrityValidator::read( const void * data, unsigned size )
|
||||
{
|
||||
assert(primaryFile);
|
||||
assert(controlFile);
|
||||
|
||||
if(!size)
|
||||
return size;
|
||||
|
||||
std::vector<ui8> controlData(size);
|
||||
auto ret = primaryFile->read(data, size);
|
||||
controlFile->read(controlData.data(), size);
|
||||
|
||||
if(!foundDesync && std::memcmp(data, controlData.data(), size))
|
||||
{
|
||||
tlog1 << "Desync found! Position: " << primaryFile->sfile->tellg() << std::endl;
|
||||
foundDesync = true;
|
||||
//throw std::runtime_error("Savegame dsynchronized!");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
unique_ptr<CLoadFile> CLoadIntegrityValidator::decay()
|
||||
{
|
||||
return std::move(primaryFile);
|
||||
}
|
||||
|
||||
void CLoadIntegrityValidator::checkMagicBytes( const std::string &text )
|
||||
{
|
||||
assert(primaryFile);
|
||||
assert(controlFile);
|
||||
|
||||
primaryFile->checkMagicBytes(text);
|
||||
controlFile->checkMagicBytes(text);
|
||||
}
|
||||
|
@ -34,7 +34,7 @@
|
||||
#include "CObjectHandler.h" //for CArmedInstance
|
||||
#include "Mapping/CCampaignHandler.h" //for CCampaignState
|
||||
|
||||
const ui32 version = 737;
|
||||
const ui32 version = 738;
|
||||
|
||||
class CConnection;
|
||||
class CGObjectInstance;
|
||||
@ -47,6 +47,8 @@ struct CPack;
|
||||
extern DLL_LINKAGE LibClasses * VLC;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace asio
|
||||
@ -382,7 +384,7 @@ public:
|
||||
si32 idAsNumber = idToNumber(id);
|
||||
|
||||
assert(oInfo.vector);
|
||||
assert(oInfo.vector->size() > idAsNumber);
|
||||
assert(static_cast<si32>(oInfo.vector->size()) > idAsNumber);
|
||||
return const_cast<T*>((*oInfo.vector)[idAsNumber].get());
|
||||
}
|
||||
|
||||
@ -1218,6 +1220,8 @@ public:
|
||||
void openNextFile(const std::string &fname); //throws!
|
||||
void clear();
|
||||
void reportState(CLogger &out);
|
||||
|
||||
void putMagicBytes(const std::string &text);
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CLoadFile
|
||||
@ -1239,6 +1243,22 @@ public:
|
||||
void openNextFile(const std::string &fname, int minimalVersion); //throws!
|
||||
void clear();
|
||||
void reportState(CLogger &out);
|
||||
|
||||
void checkMagicBytes(const std::string &text);
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CLoadIntegrityValidator : public CISer<CLoadIntegrityValidator>
|
||||
{
|
||||
public:
|
||||
unique_ptr<CLoadFile> primaryFile, controlFile;
|
||||
bool foundDesync;
|
||||
|
||||
CLoadIntegrityValidator(const std::string &primaryFileName, const std::string &controlFileName, int minimalVersion = version); //throws!
|
||||
|
||||
int read(const void * data, unsigned size); //throws!
|
||||
void checkMagicBytes(const std::string &text);
|
||||
|
||||
unique_ptr<CLoadFile> decay(); //returns primary file. CLoadIntegrityValidator stops being usable anymore
|
||||
};
|
||||
|
||||
typedef boost::asio::basic_stream_socket < boost::asio::ip::tcp , boost::asio::stream_socket_service<boost::asio::ip::tcp> > TSocket;
|
||||
|
@ -198,7 +198,7 @@ public:
|
||||
};
|
||||
|
||||
template<typename Der>
|
||||
DLL_LINKAGE std::ostream & operator << (std::ostream & os, BaseForID<Der> id);
|
||||
std::ostream & operator << (std::ostream & os, BaseForID<Der> id);
|
||||
|
||||
template<typename Der>
|
||||
std::ostream & operator << (std::ostream & os, BaseForID<Der> id)
|
||||
|
@ -17,6 +17,9 @@
|
||||
#include "CBuildingHandler.h"
|
||||
#include "GameConstants.h"
|
||||
#include "CModHandler.h"
|
||||
#include "CDefObjInfoHandler.h"
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
/*
|
||||
* IGameCallback.cpp, part of VCMI engine
|
||||
@ -210,6 +213,48 @@ void CPrivilagedInfoCallback::getAllowedSpells(std::vector<SpellID> &out, ui16 l
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename Loader>
|
||||
void CPrivilagedInfoCallback::loadCommonState(Loader &in)
|
||||
{
|
||||
tlog0 << "Loading lib part of game...\n";
|
||||
in.checkMagicBytes(SAVEGAME_MAGIC);
|
||||
|
||||
CMapHeader dum;
|
||||
StartInfo *si;
|
||||
|
||||
tlog0 <<"\tReading header"<<std::endl;
|
||||
in >> dum;
|
||||
|
||||
tlog0 << "\tReading options"<<std::endl;
|
||||
in >> si;
|
||||
|
||||
tlog0 <<"\tReading handlers"<<std::endl;
|
||||
in >> *VLC;
|
||||
|
||||
tlog0 <<"\tReading gamestate"<<std::endl;
|
||||
in >> gs;
|
||||
}
|
||||
|
||||
template<typename Saver>
|
||||
void CPrivilagedInfoCallback::saveCommonState(Saver &out) const
|
||||
{
|
||||
tlog0 << "Saving lib part of game...";
|
||||
out.putMagicBytes(SAVEGAME_MAGIC);
|
||||
tlog0 <<"\tSaving header"<<std::endl;
|
||||
out << static_cast<CMapHeader&>(*gs->map);
|
||||
tlog0 << "\tSaving options"<<std::endl;
|
||||
out << gs->scenarioOps;
|
||||
tlog0 << "\tSaving handlers"<<std::endl;
|
||||
out << *VLC;
|
||||
tlog0 << "\tSaving gamestate"<<std::endl;
|
||||
out << gs;
|
||||
}
|
||||
|
||||
template DLL_LINKAGE void CPrivilagedInfoCallback::loadCommonState<CLoadIntegrityValidator>(CLoadIntegrityValidator&);
|
||||
template DLL_LINKAGE void CPrivilagedInfoCallback::loadCommonState<CLoadFile>(CLoadFile&);
|
||||
template DLL_LINKAGE void CPrivilagedInfoCallback::saveCommonState<CSaveFile>(CSaveFile&) const;
|
||||
|
||||
inline TerrainTile * CNonConstInfoCallback::getTile( int3 pos )
|
||||
{
|
||||
if(!gs->map->isInTheMap(pos))
|
||||
|
@ -58,7 +58,8 @@ class CStackBasicDescriptor;
|
||||
struct TeamState;
|
||||
struct QuestInfo;
|
||||
class CGCreature;
|
||||
|
||||
class CSaveFile;
|
||||
class CLoadFile;
|
||||
|
||||
class DLL_LINKAGE CGameInfoCallback : public virtual CCallbackBase
|
||||
{
|
||||
@ -176,6 +177,12 @@ public:
|
||||
ArtifactID getArtSync (ui32 rand, int flags, bool erasePicked); //synchronous
|
||||
void pickAllowedArtsSet(std::vector<const CArtifact*> &out); //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant
|
||||
void getAllowedSpells(std::vector<SpellID> &out, ui16 level);
|
||||
|
||||
template<typename Saver>
|
||||
void saveCommonState(Saver &out) const; //stores GS and VLC
|
||||
|
||||
template<typename Loader>
|
||||
void loadCommonState(Loader &in); //loads GS and VLC
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CNonConstInfoCallback : public CPrivilagedInfoCallback
|
||||
@ -235,6 +242,7 @@ public:
|
||||
virtual void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) = 0;
|
||||
virtual void removeArtifact(const ArtifactLocation &al) = 0;
|
||||
virtual bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) = 0;
|
||||
virtual void synchronizeArtifactHandlerLists() = 0;
|
||||
|
||||
virtual void showCompInfo(ShowInInfobox * comp)=0;
|
||||
virtual void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0;
|
||||
|
@ -158,11 +158,6 @@ CMap::~CMap()
|
||||
}
|
||||
delete [] terrain;
|
||||
}
|
||||
|
||||
for(std::list<ConstTransitivePtr<CMapEvent> >::iterator i = events.begin(); i != events.end(); i++)
|
||||
{
|
||||
i->dellNull();
|
||||
}
|
||||
}
|
||||
|
||||
void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
|
||||
|
@ -743,7 +743,7 @@ public:
|
||||
std::vector<bool> allowedAbilities;
|
||||
|
||||
/** list of map events */
|
||||
std::list<ConstTransitivePtr<CMapEvent> > events;
|
||||
std::list<CMapEvent> events;
|
||||
|
||||
/** specifies the position of the grail */
|
||||
int3 grailPos;
|
||||
|
@ -1959,39 +1959,39 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
|
||||
|
||||
for(int gh = 0; gh < numberOfEvent; ++gh)
|
||||
{
|
||||
CCastleEvent * nce = new CCastleEvent();
|
||||
nce->town = nt;
|
||||
nce->name = reader.readString();
|
||||
nce->message = reader.readString();
|
||||
CCastleEvent nce;
|
||||
nce.town = nt;
|
||||
nce.name = reader.readString();
|
||||
nce.message = reader.readString();
|
||||
|
||||
readResourses(nce->resources);
|
||||
readResourses(nce.resources);
|
||||
|
||||
nce->players = reader.readUInt8();
|
||||
nce.players = reader.readUInt8();
|
||||
if(map->version > EMapFormat::AB)
|
||||
{
|
||||
nce->humanAffected = reader.readUInt8();
|
||||
nce.humanAffected = reader.readUInt8();
|
||||
}
|
||||
else
|
||||
{
|
||||
nce->humanAffected = true;
|
||||
nce.humanAffected = true;
|
||||
}
|
||||
|
||||
nce->computerAffected = reader.readUInt8();
|
||||
nce->firstOccurence = reader.readUInt16();
|
||||
nce->nextOccurence = reader.readUInt8();
|
||||
nce.computerAffected = reader.readUInt8();
|
||||
nce.firstOccurence = reader.readUInt16();
|
||||
nce.nextOccurence = reader.readUInt8();
|
||||
|
||||
reader.skip(17);
|
||||
|
||||
// New buildings
|
||||
|
||||
readBitmask(nce->buildings,6,48,false);
|
||||
readBitmask(nce.buildings,6,48,false);
|
||||
|
||||
nce->buildings = convertBuildings(nce->buildings, castleID, false);
|
||||
nce.buildings = convertBuildings(nce.buildings, castleID, false);
|
||||
|
||||
nce->creatures.resize(7);
|
||||
nce.creatures.resize(7);
|
||||
for(int vv = 0; vv < 7; ++vv)
|
||||
{
|
||||
nce->creatures[vv] = reader.readUInt16();
|
||||
nce.creatures[vv] = reader.readUInt16();
|
||||
}
|
||||
reader.skip(4);
|
||||
nt->events.push_back(nce);
|
||||
@ -2072,23 +2072,23 @@ void CMapLoaderH3M::readEvents()
|
||||
int numberOfEvents = reader.readUInt32();
|
||||
for(int yyoo = 0; yyoo < numberOfEvents; ++yyoo)
|
||||
{
|
||||
CMapEvent * ne = new CMapEvent();
|
||||
ne->name = reader.readString();
|
||||
ne->message = reader.readString();
|
||||
CMapEvent ne;
|
||||
ne.name = reader.readString();
|
||||
ne.message = reader.readString();
|
||||
|
||||
readResourses(ne->resources);
|
||||
ne->players = reader.readUInt8();
|
||||
readResourses(ne.resources);
|
||||
ne.players = reader.readUInt8();
|
||||
if(map->version > EMapFormat::AB)
|
||||
{
|
||||
ne->humanAffected = reader.readUInt8();
|
||||
ne.humanAffected = reader.readUInt8();
|
||||
}
|
||||
else
|
||||
{
|
||||
ne->humanAffected = true;
|
||||
ne.humanAffected = true;
|
||||
}
|
||||
ne->computerAffected = reader.readUInt8();
|
||||
ne->firstOccurence = reader.readUInt16();
|
||||
ne->nextOccurence = reader.readUInt8();
|
||||
ne.computerAffected = reader.readUInt8();
|
||||
ne.firstOccurence = reader.readUInt16();
|
||||
ne.nextOccurence = reader.readUInt8();
|
||||
|
||||
reader.skip(17);
|
||||
|
||||
|
@ -577,6 +577,44 @@ struct PrepareForAdvancingCampaign : public CPackForClient //122
|
||||
}
|
||||
};
|
||||
|
||||
struct UpdateArtHandlerLists : public CPackForClient //123
|
||||
{
|
||||
UpdateArtHandlerLists(){type = 123;};
|
||||
std::vector<CArtifact*> treasures, minors, majors, relics;
|
||||
|
||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & treasures & minors & majors & relics;
|
||||
}
|
||||
};
|
||||
|
||||
struct UpdateMapEvents : public CPackForClient //124
|
||||
{
|
||||
UpdateMapEvents(){type = 124;}
|
||||
|
||||
std::list<CMapEvent> events;
|
||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & events;
|
||||
}
|
||||
};
|
||||
|
||||
struct UpdateCastleEvents : public CPackForClient //125
|
||||
{
|
||||
UpdateCastleEvents(){type = 125;}
|
||||
|
||||
ObjectInstanceID town;
|
||||
std::list<CCastleEvent> events;
|
||||
|
||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & town & events;
|
||||
}
|
||||
};
|
||||
|
||||
struct RemoveObject : public CPackForClient //500
|
||||
{
|
||||
RemoveObject(){type = 500;};
|
||||
@ -1133,8 +1171,11 @@ namespace ObjProperty
|
||||
|
||||
//town-specific
|
||||
STRUCTURE_ADD_VISITING_HERO, STRUCTURE_CLEAR_VISITORS, STRUCTURE_ADD_GARRISONED_HERO, //changing buildings state
|
||||
BONUS_VALUE_FIRST, BONUS_VALUE_SECOND //used in Rampart for special building that generates resources (storing resource type and quantity)
|
||||
BONUS_VALUE_FIRST, BONUS_VALUE_SECOND, //used in Rampart for special building that generates resources (storing resource type and quantity)
|
||||
|
||||
//creature-bank specific
|
||||
BANK_DAYCOUNTER, BANK_CLEAR_ARTIFACTS, BANK_ADD_ARTIFACT, BANK_MULTIPLIER, BANK_CONFIG_PRESET,
|
||||
BANK_CLEAR_CONFIG, BANK_INIT_ARMY, BANK_RESET
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -116,6 +116,26 @@ DLL_LINKAGE void AddQuest::applyGs(CGameState *gs)
|
||||
tlog2 << "Warning! Attempt to add duplicated quest\n";
|
||||
}
|
||||
|
||||
DLL_LINKAGE void UpdateArtHandlerLists::applyGs(CGameState *gs)
|
||||
{
|
||||
VLC->arth->minors = minors;
|
||||
VLC->arth->majors = majors;
|
||||
VLC->arth->treasures = treasures;
|
||||
VLC->arth->relics = relics;
|
||||
}
|
||||
|
||||
DLL_LINKAGE void UpdateMapEvents::applyGs(CGameState *gs)
|
||||
{
|
||||
gs->map->events = events;
|
||||
}
|
||||
|
||||
|
||||
DLL_LINKAGE void UpdateCastleEvents::applyGs(CGameState *gs)
|
||||
{
|
||||
auto t = gs->getTown(town);
|
||||
t->events = events;
|
||||
}
|
||||
|
||||
DLL_LINKAGE void HeroVisitCastle::applyGs( CGameState *gs )
|
||||
{
|
||||
CGHeroInstance *h = gs->getHero(hid);
|
||||
@ -794,7 +814,7 @@ DLL_LINKAGE void MoveArtifact::applyGs( CGameState *gs )
|
||||
{
|
||||
CGHeroInstance *h = *hPtr;
|
||||
if(h && !h->hasSpellbook())
|
||||
gs->giveHeroArtifact(h, 0);
|
||||
gs->giveHeroArtifact(h, ArtifactID::SPELLBOOK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +138,9 @@ void registerTypes2(Serializer &s)
|
||||
s.template registerType<RemoveBonus>();
|
||||
s.template registerType<UpdateCampaignState>();
|
||||
s.template registerType<PrepareForAdvancingCampaign>();
|
||||
s.template registerType<UpdateArtHandlerLists>();
|
||||
s.template registerType<UpdateMapEvents>();
|
||||
s.template registerType<UpdateCastleEvents>();
|
||||
s.template registerType<RemoveObject>();
|
||||
s.template registerType<TryMoveHero>();
|
||||
//s.template registerType<SetGarrisons>();
|
||||
|
@ -1111,9 +1111,9 @@ void CGameHandler::init(StartInfo *si)
|
||||
states.addPlayer(i->first);
|
||||
}
|
||||
|
||||
static bool evntCmp(const CMapEvent *a, const CMapEvent *b)
|
||||
static bool evntCmp(const CMapEvent &a, const CMapEvent &b)
|
||||
{
|
||||
return a->earlierThan(*b);
|
||||
return a.earlierThan(b);
|
||||
}
|
||||
|
||||
void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=false, bool clear = false)
|
||||
@ -1501,6 +1501,8 @@ void CGameHandler::newTurn()
|
||||
sendAndApply(&iw);
|
||||
}
|
||||
}
|
||||
|
||||
synchronizeArtifactHandlerLists(); //new day events may have changed them. TODO better of managing that
|
||||
}
|
||||
void CGameHandler::run(bool resume)
|
||||
{
|
||||
@ -2306,16 +2308,18 @@ void CGameHandler::save(const std::string & filename )
|
||||
|
||||
try
|
||||
{
|
||||
{
|
||||
tlog0 << "Serializing game info...\n";
|
||||
CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::LIB_SAVEGAME)));
|
||||
char hlp[8] = "VCMISVG";
|
||||
save << hlp << static_cast<CMapHeader&>(*gs->map) << gs->scenarioOps << *VLC << gs;
|
||||
}
|
||||
// {
|
||||
// tlog0 << "Serializing game info...\n";
|
||||
// CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::LIB_SAVEGAME)));
|
||||
// // char hlp[8] = "VCMISVG";
|
||||
// // save << hlp;
|
||||
// saveCommonState(save);
|
||||
// }
|
||||
|
||||
{
|
||||
tlog0 << "Serializing server info...\n";
|
||||
CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::SERVER_SAVEGAME)));
|
||||
saveCommonState(save);
|
||||
tlog0 << "Saving server state\n";
|
||||
save << *this;
|
||||
}
|
||||
tlog0 << "Game has been successfully saved!\n";
|
||||
@ -4688,34 +4692,34 @@ void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, c
|
||||
void CGameHandler::handleTimeEvents()
|
||||
{
|
||||
gs->map->events.sort(evntCmp);
|
||||
while(gs->map->events.size() && gs->map->events.front()->firstOccurence+1 == gs->day)
|
||||
while(gs->map->events.size() && gs->map->events.front().firstOccurence+1 == gs->day)
|
||||
{
|
||||
CMapEvent *ev = gs->map->events.front();
|
||||
CMapEvent ev = gs->map->events.front();
|
||||
for(int player = 0; player < GameConstants::PLAYER_LIMIT; player++)
|
||||
{
|
||||
PlayerState *pinfo = gs->getPlayer(player);
|
||||
|
||||
if( pinfo //player exists
|
||||
&& (ev->players & 1<<player) //event is enabled to this player
|
||||
&& ((ev->computerAffected && !pinfo->human)
|
||||
|| (ev->humanAffected && pinfo->human)
|
||||
&& (ev.players & 1<<player) //event is enabled to this player
|
||||
&& ((ev.computerAffected && !pinfo->human)
|
||||
|| (ev.humanAffected && pinfo->human)
|
||||
)
|
||||
)
|
||||
{
|
||||
//give resources
|
||||
SetResources sr;
|
||||
sr.player = player;
|
||||
sr.res = pinfo->resources + ev->resources;
|
||||
sr.res = pinfo->resources + ev.resources;
|
||||
|
||||
//prepare dialog
|
||||
InfoWindow iw;
|
||||
iw.player = player;
|
||||
iw.text << ev->message;
|
||||
iw.text << ev.message;
|
||||
|
||||
for (int i=0; i<ev->resources.size(); i++)
|
||||
for (int i=0; i<ev.resources.size(); i++)
|
||||
{
|
||||
if(ev->resources[i]) //if resource is changed, we add it to the dialog
|
||||
iw.components.push_back(Component(Component::RESOURCE,i,ev->resources[i],0));
|
||||
if(ev.resources[i]) //if resource is changed, we add it to the dialog
|
||||
iw.components.push_back(Component(Component::RESOURCE,i,ev.resources[i],0));
|
||||
}
|
||||
|
||||
if (iw.components.size())
|
||||
@ -4728,59 +4732,62 @@ void CGameHandler::handleTimeEvents()
|
||||
}
|
||||
} //PLAYERS LOOP
|
||||
|
||||
if(ev->nextOccurence)
|
||||
if(ev.nextOccurence)
|
||||
{
|
||||
gs->map->events.pop_front();
|
||||
|
||||
ev->firstOccurence += ev->nextOccurence;
|
||||
std::list<ConstTransitivePtr<CMapEvent> >::iterator it = gs->map->events.begin();
|
||||
while ( it !=gs->map->events.end() && (*it)->earlierThanOrEqual(*ev))
|
||||
ev.firstOccurence += ev.nextOccurence;
|
||||
auto it = gs->map->events.begin();
|
||||
while ( it !=gs->map->events.end() && it->earlierThanOrEqual(ev))
|
||||
it++;
|
||||
gs->map->events.insert(it, ev);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete ev;
|
||||
gs->map->events.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO send only if changed
|
||||
UpdateMapEvents ume;
|
||||
ume.events = gs->map->events;
|
||||
sendAndApply(&ume);
|
||||
}
|
||||
|
||||
void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n, std::map<ObjectInstanceID, std::map<si32, si32> > &newCreas)
|
||||
{
|
||||
//TODO event removing desync!!!
|
||||
town->events.sort(evntCmp);
|
||||
while(town->events.size() && town->events.front()->firstOccurence == gs->day)
|
||||
while(town->events.size() && town->events.front().firstOccurence == gs->day)
|
||||
{
|
||||
ui8 player = town->tempOwner;
|
||||
CCastleEvent *ev = town->events.front();
|
||||
CCastleEvent ev = town->events.front();
|
||||
PlayerState *pinfo = gs->getPlayer(player);
|
||||
|
||||
if( pinfo //player exists
|
||||
&& (ev->players & 1<<player) //event is enabled to this player
|
||||
&& ((ev->computerAffected && !pinfo->human)
|
||||
|| (ev->humanAffected && pinfo->human) ) )
|
||||
&& (ev.players & 1<<player) //event is enabled to this player
|
||||
&& ((ev.computerAffected && !pinfo->human)
|
||||
|| (ev.humanAffected && pinfo->human) ) )
|
||||
{
|
||||
|
||||
|
||||
// dialog
|
||||
InfoWindow iw;
|
||||
iw.player = player;
|
||||
iw.text << ev->message;
|
||||
iw.text << ev.message;
|
||||
|
||||
if(ev->resources.nonZero())
|
||||
if(ev.resources.nonZero())
|
||||
{
|
||||
TResources was = n.res[player];
|
||||
n.res[player] += ev->resources;
|
||||
n.res[player] += ev.resources;
|
||||
n.res[player].amax(0);
|
||||
|
||||
for (int i=0; i<ev->resources.size(); i++)
|
||||
if(ev->resources[i] && pinfo->resources[i] != n.res[player][i]) //if resource had changed, we add it to the dialog
|
||||
for (int i=0; i<ev.resources.size(); i++)
|
||||
if(ev.resources[i] && pinfo->resources[i] != n.res[player][i]) //if resource had changed, we add it to the dialog
|
||||
iw.components.push_back(Component(Component::RESOURCE,i,n.res[player][i]-was[i],0));
|
||||
|
||||
}
|
||||
|
||||
BOOST_FOREACH(auto & i, ev->buildings)
|
||||
BOOST_FOREACH(auto & i, ev.buildings)
|
||||
{
|
||||
if ( town->hasBuilt(i))
|
||||
{
|
||||
@ -4789,34 +4796,39 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n, std::map<
|
||||
}
|
||||
}
|
||||
|
||||
for(si32 i=0;i<ev->creatures.size();i++) //creature growths
|
||||
for(si32 i=0;i<ev.creatures.size();i++) //creature growths
|
||||
{
|
||||
if(town->creatureDwellingLevel(i) >= 0 && ev->creatures[i])//there is dwelling
|
||||
if(town->creatureDwellingLevel(i) >= 0 && ev.creatures[i])//there is dwelling
|
||||
{
|
||||
newCreas[town->id][i] += ev->creatures[i];
|
||||
newCreas[town->id][i] += ev.creatures[i];
|
||||
iw.components.push_back(Component(Component::CREATURE,
|
||||
town->creatures[i].second.back(), ev->creatures[i], 0));
|
||||
town->creatures[i].second.back(), ev.creatures[i], 0));
|
||||
}
|
||||
}
|
||||
sendAndApply(&iw); //show dialog
|
||||
}
|
||||
|
||||
if(ev->nextOccurence)
|
||||
if(ev.nextOccurence)
|
||||
{
|
||||
town->events.pop_front();
|
||||
|
||||
ev->firstOccurence += ev->nextOccurence;
|
||||
std::list<CCastleEvent*>::iterator it = town->events.begin();
|
||||
while ( it !=town->events.end() && (*it)->earlierThanOrEqual(*ev))
|
||||
ev.firstOccurence += ev.nextOccurence;
|
||||
auto it = town->events.begin();
|
||||
while ( it != town->events.end() && it->earlierThanOrEqual(ev))
|
||||
it++;
|
||||
town->events.insert(it, ev);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete ev;
|
||||
town->events.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO send only if changed
|
||||
UpdateCastleEvents uce;
|
||||
uce.town = town->id;
|
||||
uce.events = town->events;
|
||||
sendAndApply(&uce);
|
||||
}
|
||||
|
||||
bool CGameHandler::complain( const std::string &problem )
|
||||
@ -6182,6 +6194,16 @@ void CGameHandler::removeObstacle(const CObstacleInstance &obstacle)
|
||||
sendAndApply(&obsRem);
|
||||
}
|
||||
|
||||
void CGameHandler::synchronizeArtifactHandlerLists()
|
||||
{
|
||||
UpdateArtHandlerLists uahl;
|
||||
uahl.treasures = VLC->arth->treasures;
|
||||
uahl.minors = VLC->arth->minors;
|
||||
uahl.majors = VLC->arth->majors;
|
||||
uahl.relics = VLC->arth->relics;
|
||||
sendAndApply(&uahl);
|
||||
}
|
||||
|
||||
CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat)
|
||||
{
|
||||
heroWithDeadCommander = ObjectInstanceID();
|
||||
|
@ -159,6 +159,7 @@ public:
|
||||
void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) OVERRIDE;
|
||||
void removeArtifact(const ArtifactLocation &al) OVERRIDE;
|
||||
bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) OVERRIDE;
|
||||
void synchronizeArtifactHandlerLists() OVERRIDE;
|
||||
|
||||
void showCompInfo(ShowInInfobox * comp) OVERRIDE;
|
||||
void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) OVERRIDE;
|
||||
|
@ -448,25 +448,26 @@ void CVCMIServer::loadGame()
|
||||
|
||||
c >> clients >> fname; //how many clients should be connected - TODO: support more than one
|
||||
|
||||
{
|
||||
char sig[8];
|
||||
CMapHeader dum;
|
||||
StartInfo *si;
|
||||
|
||||
CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME)));
|
||||
lf >> sig >> dum >> si;
|
||||
tlog0 <<"Reading save signature"<<std::endl;
|
||||
|
||||
lf >> *VLC;
|
||||
tlog0 <<"Reading handlers"<<std::endl;
|
||||
|
||||
lf >> (gh.gs);
|
||||
c.addStdVecItems(gh.gs);
|
||||
tlog0 <<"Reading gamestate"<<std::endl;
|
||||
}
|
||||
// {
|
||||
// char sig[8];
|
||||
// CMapHeader dum;
|
||||
// StartInfo *si;
|
||||
//
|
||||
// CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME)));
|
||||
// lf >> sig >> dum >> si;
|
||||
// tlog0 <<"Reading save signature"<<std::endl;
|
||||
//
|
||||
// lf >> *VLC;
|
||||
// tlog0 <<"Reading handlers"<<std::endl;
|
||||
//
|
||||
// lf >> (gh.gs);
|
||||
// c.addStdVecItems(gh.gs);
|
||||
// tlog0 <<"Reading gamestate"<<std::endl;
|
||||
// }
|
||||
|
||||
{
|
||||
CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME)));
|
||||
gh.loadCommonState(lf);
|
||||
lf >> gh;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user