From 8c5f93dd8ff989013bc74d6cbc1872023e7bc320 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Wed, 13 Jan 2016 19:17:12 +0100 Subject: [PATCH 01/45] Little refactoring for object placement. --- lib/rmg/CMapGenerator.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 0c7f6c963..0c3c3eea9 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -20,6 +20,8 @@ void CMapGenerator::foreach_neighbour(const int3 &pos, std::functionisInTheMap(n)) foo(n); } From ac730a591c45df0fec1afea73e0e0c0cc36302d7 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Wed, 13 Jan 2016 20:03:25 +0100 Subject: [PATCH 02/45] Fixed large treasure piles sometimes being uncovered. --- lib/rmg/CRmgTemplateZone.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/rmg/CRmgTemplateZone.cpp b/lib/rmg/CRmgTemplateZone.cpp index 9245c5753..715d27a6e 100644 --- a/lib/rmg/CRmgTemplateZone.cpp +++ b/lib/rmg/CRmgTemplateZone.cpp @@ -1177,25 +1177,23 @@ bool CRmgTemplateZone::createTreasurePile(CMapGenerator* gen, int3 &pos, float m int3 closestTile = int3(-1,-1,-1); float minDistance = 1e10; + for (auto visitablePos : info.visitableFromBottomPositions) //objects that are not visitable from top must be accessible from bottom or side { int3 closestFreeTile = findClosestTile(freePaths, visitablePos); if (closestFreeTile.dist2d(visitablePos) < minDistance) { - closestTile = visitablePos + int3 (0, 1, 0); //start below object (y+1), possibly even outside the map (?) + closestTile = visitablePos + int3 (0, 1, 0); //start below object (y+1), possibly even outside the map, to not make path up through it minDistance = closestFreeTile.dist2d(visitablePos); } } - if (!closestTile.valid()) + for (auto visitablePos : info.visitableFromTopPositions) //all objects are accessible from any direction { - for (auto visitablePos : info.visitableFromTopPositions) //all objects are accessible from any direction + int3 closestFreeTile = findClosestTile(freePaths, visitablePos); + if (closestFreeTile.dist2d(visitablePos) < minDistance) { - int3 closestFreeTile = findClosestTile(freePaths, visitablePos); - if (closestFreeTile.dist2d(visitablePos) < minDistance) - { - closestTile = visitablePos; - minDistance = closestFreeTile.dist2d(visitablePos); - } + closestTile = visitablePos; + minDistance = closestFreeTile.dist2d(visitablePos); } } assert (closestTile.valid()); From b84d7bd288552d7cde99a830397062a19263637a Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Fri, 15 Jan 2016 04:29:46 +0300 Subject: [PATCH 03/45] CGCreature: add reward granting. Fix issue 2372 --- lib/mapObjects/MiscObjects.cpp | 31 +++++++++++++++++++++++++++++++ lib/mapObjects/MiscObjects.h | 1 + 2 files changed, 32 insertions(+) diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 814e64af3..dcdad9a27 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -392,6 +392,7 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co if(cost) cb->giveResource(h->tempOwner,Res::GOLD,-cost); + giveReward(h); cb->tryJoiningArmy(this, h, true, true); } } @@ -455,6 +456,7 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult & if(result.winner==0) { + giveReward(hero); cb->removeObject(this); } else @@ -557,6 +559,35 @@ int CGCreature::getNumberOfStacks(const CGHeroInstance *hero) const return split; } +void CGCreature::giveReward(const CGHeroInstance * h) const +{ + InfoWindow iw; + iw.player = h->tempOwner; + + if(resources.size()) + { + cb->giveResources(h->tempOwner, resources); + for(int i = 0; i < resources.size(); i++) + { + if(resources[i] > 0) + iw.components.push_back(Component(Component::RESOURCE, i, resources[i], 0)); + } + } + + if(gainedArtifact != ArtifactID::NONE) + { + cb->giveHeroNewArtifact(h, VLC->arth->artifacts[gainedArtifact], ArtifactPosition::FIRST_AVAILABLE); + iw.components.push_back(Component(Component::ARTIFACT, gainedArtifact, 0, 0)); + } + + if(iw.components.size()) + { + iw.text.addTxt(MetaString::ADVOB_TXT, 183); // % has found treasure + iw.text.addReplacement(h->name); + cb->showInfoDialog(&iw); + } +} + void CGMine::onHeroVisit( const CGHeroInstance * h ) const { int relations = cb->gameState()->getPlayerRelations(h->tempOwner, tempOwner); diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 4d84766f9..aa3afe4c6 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -90,6 +90,7 @@ private: void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const; int takenAction(const CGHeroInstance *h, bool allowJoin=true) const; //action on confrontation: -2 - fight, -1 - flee, >=0 - will join for given value of gold (may be 0) + void giveReward(const CGHeroInstance * h) const; }; From 530fe04c751fe48636c65b25ebb5ada7ec70f609 Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Fri, 15 Jan 2016 19:30:43 +0300 Subject: [PATCH 04/45] CAdvMapInt::updateMoveHero: proper indeterminate check. Fix issue 2074 --- client/windows/CAdvmapInterface.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index 34f547e1b..923a4299b 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -758,14 +758,15 @@ void CAdvMapInt::updateSleepWake(const CGHeroInstance *h) void CAdvMapInt::updateMoveHero(const CGHeroInstance *h, tribool hasPath) { - //default value is for everywhere but CPlayerInterface::moveHero, because paths are not updated from there immediately - if (hasPath == boost::indeterminate) - hasPath = LOCPLINT->paths[h].nodes.size() ? true : false; - if (!h) + if(!h) { moveHero->block(true); return; } + //default value is for everywhere but CPlayerInterface::moveHero, because paths are not updated from there immediately + if(boost::logic::indeterminate(hasPath)) + hasPath = LOCPLINT->paths[h].nodes.size() ? true : false; + moveHero->block(!hasPath || (h->movement == 0)); } From 725cce368fb008c4911de826e32bc9e4431a8c93 Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Fri, 15 Jan 2016 20:36:16 +0300 Subject: [PATCH 05/45] CGameState::initStartingBonus: fix amounts for wood and ore bonus In original game when wood and ore bonus is choosen you always get same amount of both resources. --- lib/CGameState.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 782b8409c..862e5db42 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -1665,8 +1665,9 @@ void CGameState::initStartingBonus() int res = VLC->townh->factions[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes; if(res == Res::WOOD_AND_ORE) { - elem.second.resources[Res::WOOD] += rand.nextInt(5, 10); - elem.second.resources[Res::ORE] += rand.nextInt(5, 10); + int amount = rand.nextInt(5, 10); + elem.second.resources[Res::WOOD] += amount; + elem.second.resources[Res::ORE] += amount; } else { From 18b2f866c99e2e07388370a823ed930eb194d028 Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Sat, 16 Jan 2016 09:45:30 +0300 Subject: [PATCH 06/45] CGPickable: add missing break for flotsam. Fix issue 1956 --- lib/mapObjects/CRewardableObject.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index bb454007c..59740789f 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -496,6 +496,7 @@ void CGPickable::initObj() info.resize(1); info[0].message.addTxt(MetaString::ADVOB_TXT, 51); info[0].reward.removeObject = true; + break; case 1: { info.resize(1); From cacc811ed8bd66c2d3c891f1579bc337cc71f175 Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Sun, 17 Jan 2016 07:48:21 +0300 Subject: [PATCH 07/45] CAdvMapInt: keep old path if non-accessible tile clicked. Fix issue 2380 --- client/windows/CAdvmapInterface.cpp | 30 ++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index 923a4299b..b92bf4272 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -1421,13 +1421,13 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos) bool canSelect = topBlocking && topBlocking->ID == Obj::HERO && topBlocking->tempOwner == LOCPLINT->playerID; canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner); - if (selection->ID != Obj::HERO) //hero is not selected (presumably town) + if(selection->ID != Obj::HERO) //hero is not selected (presumably town) { assert(!terrain.currentPath); //path can be active only when hero is selected if(selection == topBlocking) //selected town clicked LOCPLINT->openTownWindow(static_cast(topBlocking)); - else if ( canSelect ) - select(static_cast(topBlocking), false); + else if(canSelect) + select(static_cast(topBlocking), false); return; } else if(const CGHeroInstance * currentHero = curHero()) //hero is selected @@ -1445,22 +1445,26 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos) } else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise { - if (terrain.currentPath && terrain.currentPath->endPos() == mapPos)//we'll be moving + if(terrain.currentPath && terrain.currentPath->endPos() == mapPos)//we'll be moving { - if (CGI->mh->canStartHeroMovement()) - LOCPLINT->moveHero(currentHero,*terrain.currentPath); + if(CGI->mh->canStartHeroMovement()) + LOCPLINT->moveHero(currentHero, *terrain.currentPath); return; } - else/* if(mp.z == currentHero->pos.z)*/ //remove old path and find a new one if we clicked on the map level on which hero is present + else //remove old path and find a new one if we clicked on accessible tile { CGPath &path = LOCPLINT->paths[currentHero]; - terrain.currentPath = &path; - bool gotPath = LOCPLINT->cb->getPathsInfo(currentHero)->getPath(path, mapPos); //try getting path, erase if failed - updateMoveHero(currentHero); - if (!gotPath) - LOCPLINT->eraseCurrentPathOf(currentHero); + CGPath newpath; + bool gotPath = LOCPLINT->cb->getPathsInfo(currentHero)->getPath(newpath, mapPos); //try getting path, erase if failed + if(gotPath && newpath.nodes.size()) + path = newpath; + + if(path.nodes.size()) + terrain.currentPath = &path; else - return; + LOCPLINT->eraseCurrentPathOf(currentHero); + + updateMoveHero(currentHero); } } } //end of hero is selected "case" From 575a68d78b116f7a531ac91307184be8030f393a Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Sun, 17 Jan 2016 15:16:35 +0300 Subject: [PATCH 08/45] CRewardableObject: fix onVisited message duplication for Idol of Fortune --- lib/mapObjects/CRewardableObject.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 59740789f..99e77266d 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -683,7 +683,6 @@ void CGBonusingObject::initObj() info[i].message.addTxt(MetaString::ADVOB_TXT, 62); soundID = soundBase::experience; } - onVisited.addTxt(MetaString::ADVOB_TXT, 63); info.back().limiter.dayOfWeek = 7; configureBonus(info.back(), Bonus::MORALE, 1, 68); // on last day of week configureBonus(info.back(), Bonus::LUCK, 1, 68); From 4a05402c2f5a41f954616bca81e64172072cd19c Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Mon, 18 Jan 2016 10:44:46 +0300 Subject: [PATCH 09/45] Revert 5cd4e852d49a988157d04349db6350322de59342 "Use portable cast in CTeleportDialogQuery also" Actually, this leads to crash on MacOSX, I specially left that intact. --- server/CQuery.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/CQuery.cpp b/server/CQuery.cpp index 834db4e38..eca836f64 100644 --- a/server/CQuery.cpp +++ b/server/CQuery.cpp @@ -321,7 +321,8 @@ CBlockingDialogQuery::CBlockingDialogQuery(const BlockingDialog &bd) void CTeleportDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const { - auto obj = dynamic_ptr_cast(objectVisit.visitedObject); + // do not change to dynamic_ptr_cast - SIGSEGV! + auto obj = dynamic_cast(objectVisit.visitedObject); obj->teleportDialogAnswered(objectVisit.visitingHero, *answer, td.exits); } From 7c7bb39904df7b8c3d00d30ca4aa468378d30721 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Mon, 18 Jan 2016 16:05:43 +0300 Subject: [PATCH 10/45] Enable right mouse button click emulation on MacOSX --- client/CMT.cpp | 131 +++++++++++++++++++++++++------------------------ 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/client/CMT.cpp b/client/CMT.cpp index 6a937ac3a..0409b6e74 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -79,7 +79,7 @@ extern boost::thread_specific_ptr inGuiThread; SDL_Surface *screen = nullptr, //main screen surface *screen2 = nullptr, //and hlp surface (used to store not-active interfaces layer) *screenBuf = screen; //points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed - + std::queue events; boost::mutex eventsM; @@ -187,7 +187,7 @@ static void SDLLogCallback(void* userdata, { //todo: convert SDL log priority to vcmi log priority //todo: make separate log domain for SDL - + logGlobal->debugStream() << "SDL(category " << category << "; priority " <init(); //(!)init here AFTER SDL_Init() while using SDL for FPS management atexit(SDL_Quit); - + SDL_LogSetOutputFunction(&SDLLogCallback, nullptr); - + int driversCount = SDL_GetNumRenderDrivers(); std::string preferredDriverName = video["driver"].String(); - + logGlobal->infoStream() << "Found " << driversCount << " render drivers"; - + for(int it = 0; it < driversCount; it++) { SDL_RendererInfo info; SDL_GetRenderDriverInfo(it,&info); - + std::string driverName(info.name); - + if(!preferredDriverName.empty() && driverName == preferredDriverName) { preferredDriverIndex = it; @@ -385,8 +385,8 @@ int main(int argc, char** argv) } else logGlobal->infoStream() << "\t" << driverName; - } - + } + config::CConfigHandler::GuiOptionsMap::key_type resPair(res["width"].Float(), res["height"].Float()); if (conf.guiOptions.count(resPair) == 0) { @@ -440,10 +440,15 @@ int main(int argc, char** argv) CCS->musich->setVolume(settings["general"]["music"].Float()); logGlobal->infoStream()<<"Initializing screen and sound handling: "<16) bpp = 32; - + int suggestedBpp = bpp; if(!checkVideoMode(0,w,h,suggestedBpp,fullscreen)) { logGlobal->errorStream() << "Error: SDL says that " << w << "x" << h << " resolution is not available!"; return false; - } - + } + bool bufOnScreen = (screenBuf == screen); screenBuf = nullptr; //it`s a link - just nullify @@ -873,34 +878,34 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen) SDL_FreeSurface(screen2); screen2 = nullptr; } - - + + if(nullptr != screen) { SDL_FreeSurface(screen); screen = nullptr; - } - - + } + + if(nullptr != screenTexture) { SDL_DestroyTexture(screenTexture); screenTexture = nullptr; } - - if(nullptr != mainRenderer) + + if(nullptr != mainRenderer) { SDL_DestroyRenderer(mainRenderer); mainRenderer = nullptr; } - + if(nullptr != mainWindow) { SDL_DestroyWindow(mainWindow); mainWindow = nullptr; - } - - + } + + if(fullscreen) { //in full-screen mode always use desktop resolution @@ -911,33 +916,33 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen) { mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED, w, h, 0); } - - - + + + if(nullptr == mainWindow) { throw std::runtime_error("Unable to create window\n"); } - - + + //create first available renderer if preferred not set. Use no flags, so HW accelerated will be preferred but SW renderer also will possible mainRenderer = SDL_CreateRenderer(mainWindow,preferredDriverIndex,0); if(nullptr == mainRenderer) { throw std::runtime_error("Unable to create renderer\n"); - } - + } + SDL_RendererInfo info; SDL_GetRendererInfo(mainRenderer,&info); - logGlobal->infoStream() << "Created renderer " << info.name; - + logGlobal->infoStream() << "Created renderer " << info.name; + SDL_RenderSetLogicalSize(mainRenderer, w, h); - + SDL_RenderSetViewport(mainRenderer, nullptr); - + #if (SDL_BYTEORDER == SDL_BIG_ENDIAN) int bmask = 0xff000000; int gmask = 0x00ff0000; @@ -955,13 +960,13 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen) { logGlobal->errorStream() << "Unable to create surface"; logGlobal->errorStream() << w << " "<< h << " "<< bpp; - + logGlobal->errorStream() << SDL_GetError(); throw std::runtime_error("Unable to create surface"); - } + } //No blending for screen itself. Required for proper cursor rendering. SDL_SetSurfaceBlendMode(screen, SDL_BLENDMODE_NONE); - + screenTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, @@ -972,23 +977,23 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen) logGlobal->errorStream() << "Unable to create screen texture"; logGlobal->errorStream() << SDL_GetError(); throw std::runtime_error("Unable to create screen texture"); - } - + } + screen2 = CSDL_Ext::copySurface(screen); if(nullptr == screen2) { throw std::runtime_error("Unable to copy surface\n"); - } - + } + screenBuf = bufOnScreen ? screen : screen2; SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 0); SDL_RenderClear(mainRenderer); SDL_RenderPresent(mainRenderer); - - return true; + + return true; } //used only once during initialization @@ -997,7 +1002,7 @@ static void setScreenRes(int w, int h, int bpp, bool fullscreen, bool resetVideo if(!recreateWindow(w,h,bpp,fullscreen)) { throw std::runtime_error("Requested screen resolution is not available\n"); - } + } } static void fullScreenChanged() @@ -1008,16 +1013,16 @@ static void fullScreenChanged() const bool toFullscreen = full->Bool(); auto bitsPerPixel = screen->format->BitsPerPixel; - + auto w = screen->w; auto h = screen->h; - + if(!recreateWindow(w,h,bitsPerPixel,toFullscreen)) { //will return false and report error if video mode is not supported - return; - } - + return; + } + GH.totalRedraw(); } @@ -1025,7 +1030,7 @@ static void handleEvent(SDL_Event & ev) { if((ev.type==SDL_QUIT) ||(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT))) { - handleQuit(); + handleQuit(); return; } else if(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4) @@ -1040,8 +1045,8 @@ static void handleEvent(SDL_Event & ev) { case FORCE_QUIT: { - handleQuit(false); - return; + handleQuit(false); + return; } break; case RETURN_TO_MAIN_MENU: @@ -1080,8 +1085,8 @@ static void handleEvent(SDL_Event & ev) fullScreenChanged(); break; default: - logGlobal->errorStream() << "Unknown user event. Code " << ev.user.code; - break; + logGlobal->errorStream() << "Unknown user event. Code " << ev.user.code; + break; } return; @@ -1098,8 +1103,8 @@ static void handleEvent(SDL_Event & ev) { boost::unique_lock lock(eventsM); events.push(ev); - } - + } + } @@ -1114,12 +1119,12 @@ static void mainLoop() while(1) //main SDL events loop { SDL_Event ev; - + while(1 == SDL_PollEvent(&ev)) { handleEvent(ev); } - + GH.renderFrame(); } From 92e011429fac88d264364c4dd1578b4b805e4b3f Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Mon, 18 Jan 2016 16:24:35 +0300 Subject: [PATCH 11/45] Fix crash on unknown map objects If getHandlerFor() fails to find the handler, it returns a nullptr shared pointer, which is later dereferenced unconditionally. How to reproduce: Download map "Happy time Dragons!!!" from http://heroesportal.net/maps.php?type=H3AB-XL&sort=r => Failed to find object of type 5:144 SIGSEGV --- lib/mapObjects/CObjectHandler.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index 8c785b374..8b28f36a8 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -169,7 +169,7 @@ std::set CGObjectInstance::getBlockedPos() const { for(int h=0; hgameState()->map->removeBlockVisTiles(this, true); auto handler = VLC->objtypeh->getHandlerFor(ID, subID); - if (!handler->getTemplates(tile.terType).empty()) + if(!handler) + { + logGlobal->errorStream() << boost::format( + "Unknown object type %d:%d at %s") % ID % subID % visitablePos(); + return; + } + if(!handler->getTemplates(tile.terType).empty()) appearance = handler->getTemplates(tile.terType)[0]; else appearance = handler->getTemplates()[0]; // get at least some appearance since alternative is crash @@ -328,9 +334,9 @@ int3 IBoatGenerator::bestLocation() const for (auto & offset : offsets) { - if (const TerrainTile *tile = IObjectInterface::cb->getTile(o->pos + offset, false)) //tile is in the map + if(const TerrainTile *tile = IObjectInterface::cb->getTile(o->pos + offset, false)) //tile is in the map { - if (tile->terType == ETerrainType::WATER && (!tile->blocked || tile->blockingObjects.front()->ID == Obj::BOAT)) //and is water and is not blocked or is blocked by boat + if(tile->terType == ETerrainType::WATER && (!tile->blocked || tile->blockingObjects.front()->ID == Obj::BOAT)) //and is water and is not blocked or is blocked by boat return o->pos + offset; } } From 74111b76897f1d3c9627dbdfef441fce35be284f Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Tue, 19 Jan 2016 10:30:55 +0300 Subject: [PATCH 12/45] Return missing building name instead of crashing in CComponent::getSubtitleInternal --- client/widgets/CComponent.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index e1f0924e8..41f331539 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -186,19 +186,31 @@ std::string CComponent::getSubtitleInternal() case artifact: return CGI->arth->artifacts[subtype]->Name(); case experience: { - if (subtype == 1) //+1 level - tree of knowledge + if(subtype == 1) //+1 level - tree of knowledge { std::string level = CGI->generaltexth->allTexts[442]; boost::replace_first(level, "1", boost::lexical_cast(val)); return level; } else + { return boost::lexical_cast(val); //amount of experience OR level required for seer hut; + } } case spell: return CGI->spellh->objects[subtype]->name; case morale: return ""; case luck: return ""; - case building: return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Name(); + case building: + { + auto building = CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]; + if(!building) + { + logGlobal->errorStream() << boost::format("Town of faction %s has no building #%d") + % CGI->townh->factions[subtype]->town->faction->name % val; + return (boost::format("Missing building #%d") % val).str(); + } + return building->Name(); + } case hero: return ""; case flag: return CGI->generaltexth->capColors[subtype]; } From 2bfc8ec8cb074a98372597b1e1d3f91b79cb4df1 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Mon, 18 Jan 2016 22:57:19 +0300 Subject: [PATCH 13/45] Fix AI hero infinite move on the same tile --- AI/VCAI/VCAI.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 4510529ef..666c9c8f7 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -1987,7 +1987,6 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) if(teleportChannelProbingList.size()) doChannelProbing(); } - ret = !i; } if (h) { @@ -2002,6 +2001,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) completeGoal (sptr(Goals::VisitTile(dst).sethero(h))); //we stepped on some tile, anyway completeGoal (sptr(Goals::ClearWayTo(dst).sethero(h))); + ret = (dst == h->visitablePos()); + if(!ret) //reserve object we are heading towards { auto obj = vstd::frontOrNull(cb->getVisitableObjs(dst)); @@ -2020,7 +2021,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) } void VCAI::tryRealize(Goals::Explore & g) { - throw cannotFulfillGoalException("EXPLORE is not a elementar goal!"); + throw cannotFulfillGoalException("EXPLORE is not an elementar goal!"); } void VCAI::tryRealize(Goals::RecruitHero & g) @@ -2853,7 +2854,7 @@ BattleState AIStatus::getBattle() } void AIStatus::addQuery(QueryID ID, std::string description) -{ +{ if(ID == QueryID(-1)) { logAi->debugStream() << boost::format("The \"query\" has an id %d, it'll be ignored as non-query. Description: %s") % ID % description; @@ -2878,7 +2879,7 @@ void AIStatus::removeQuery(QueryID ID) std::string description = remainingQueries[ID]; remainingQueries.erase(ID); - + cv.notify_all(); logAi->debugStream() << boost::format("Removing query %d - %s. Total queries count: %d") % ID % description % remainingQueries.size(); } From 6eefce23fe06fbcc98d1d6c90d33b4831ce966df Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Tue, 19 Jan 2016 20:25:15 +0300 Subject: [PATCH 14/45] Check against nullptr in VisitHero::fulfillsMe() --- AI/VCAI/Goals.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/AI/VCAI/Goals.cpp b/AI/VCAI/Goals.cpp index 6c4f8fdce..4931e4e19 100644 --- a/AI/VCAI/Goals.cpp +++ b/AI/VCAI/Goals.cpp @@ -154,8 +154,8 @@ bool Goals::AbstractGoal::operator== (AbstractGoal &g) //TODO: find out why the following are not generated automatically on MVS? -namespace Goals -{ +namespace Goals +{ template <> void CGoal::accept (VCAI * ai) { @@ -367,7 +367,7 @@ TSubgoal FindObj::whatToDoToAchieve() std::string GetObj::completeMessage() const { - return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast(objid); + return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast(objid); } TSubgoal GetObj::whatToDoToAchieve() @@ -409,7 +409,7 @@ bool GetObj::fulfillsMe (TSubgoal goal) std::string VisitHero::completeMessage() const { - return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast(objid); + return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast(objid); } TSubgoal VisitHero::whatToDoToAchieve() @@ -435,10 +435,18 @@ TSubgoal VisitHero::whatToDoToAchieve() bool VisitHero::fulfillsMe (TSubgoal goal) { - if (goal->goalType == Goals::VISIT_TILE && cb->getObj(ObjectInstanceID(objid))->visitablePos() == goal->tile) - return true; - else + if (goal->goalType != Goals::VISIT_TILE) + { return false; + } + auto obj = cb->getObj(ObjectInstanceID(objid)); + if (!obj) + { + logAi->errorStream() << boost::format("Hero %s: VisitHero::fulfillsMe at %s: object %d not found") + % hero.name % goal->tile % objid; + return false; + } + return obj->visitablePos() == goal->tile; } TSubgoal GetArtOfType::whatToDoToAchieve() @@ -458,7 +466,7 @@ TSubgoal ClearWayTo::whatToDoToAchieve() return sptr (Goals::Explore()); } - return (fh->chooseSolution(getAllPossibleSubgoals())); + return (fh->chooseSolution(getAllPossibleSubgoals())); } TGoalVec ClearWayTo::getAllPossibleSubgoals() @@ -862,7 +870,7 @@ TSubgoal GatherTroops::whatToDoToAchieve() { auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1); if(!creatures) - continue; + continue; int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber); if(upgradeNumber < 0) @@ -957,7 +965,7 @@ TGoalVec Conquer::getAllPossibleSubgoals() std::vector objs; for (auto obj : ai->visitableObjs) { - if (conquerable(obj)) + if (conquerable(obj)) objs.push_back (obj); } @@ -1035,7 +1043,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals() { //get all possible towns, heroes and dwellings we may use TGoalVec ret; - + //TODO: include evaluation of monsters gather in calculation for (auto t : cb->getTownsInfo()) { From 10f888a483e22e5a0707fbd9d53bfdbfefadf811 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Wed, 20 Jan 2016 10:44:13 +0300 Subject: [PATCH 15/45] Fix obelisks puzzle revealing Teams and players were messed up in lib; hardcoded constants were refactored. --- AI/VCAI/Goals.cpp | 2 +- client/CPlayerInterface.cpp | 22 +++++----- lib/CGameInfoCallback.cpp | 76 +++++++++++++++++----------------- lib/CGameInfoCallback.h | 5 +-- lib/mapObjects/CQuest.cpp | 6 +-- lib/mapObjects/CQuest.h | 2 + lib/mapObjects/MiscObjects.cpp | 62 +++++++++++++++------------ lib/mapObjects/MiscObjects.h | 3 ++ 8 files changed, 95 insertions(+), 83 deletions(-) diff --git a/AI/VCAI/Goals.cpp b/AI/VCAI/Goals.cpp index 4931e4e19..540e56e6c 100644 --- a/AI/VCAI/Goals.cpp +++ b/AI/VCAI/Goals.cpp @@ -275,7 +275,7 @@ TSubgoal Win::whatToDoToAchieve() // 0.85 -> radius now 2 tiles // 0.95 -> 1 tile radius, position is fully known // AFAIK H3 AI does something like this - int3 grailPos = cb->getGrailPos(ratio); + int3 grailPos = cb->getGrailPos(&ratio); if(ratio > 0.99) { return sptr (Goals::DigAtTile(grailPos)); diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 33040aef8..97ebe9fa6 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -119,7 +119,7 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player) isAutoFightOn = false; duringMovement = false; - ignoreEvents = false; + ignoreEvents = false; } CPlayerInterface::~CPlayerInterface() @@ -824,16 +824,16 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i //tidy up BattleAction ret = *(b->givenCommand->data); vstd::clear_pointer(b->givenCommand->data); - + if(ret.actionType == Battle::CANCEL) { if(stackId != ret.stackNumber) logGlobal->error("Not current active stack action canceled"); - logGlobal->traceStream() << "Canceled command for " << stackName; + logGlobal->traceStream() << "Canceled command for " << stackName; } else logGlobal->traceStream() << "Giving command for " << stackName; - + return ret; } @@ -1610,11 +1610,11 @@ void CPlayerInterface::update() { // Make sure that gamestate won't change when GUI objects may obtain its parts on event processing or drawing request boost::shared_lock gsLock(cb->getGsMutex()); - - // While mutexes were locked away we may be have stopped being the active interface + + // While mutexes were locked away we may be have stopped being the active interface if(LOCPLINT != this) return; - + //if there are any waiting dialogs, show them if((howManyPeople <= 1 || makingTurn) && !dialogs.empty() && !showingDialog->get()) { @@ -2170,7 +2170,7 @@ void CPlayerInterface::showPuzzleMap() //TODO: interface should not know the real position of Grail... double ratio = 0; - int3 grailPos = cb->getGrailPos(ratio); + int3 grailPos = cb->getGrailPos(&ratio); GH.pushInt(new CPuzzleWindow(grailPos, ratio)); } @@ -2195,7 +2195,7 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI int level = caster->getSpellSchoolLevel(spell); adventureInt->worldViewOptions.showAllTerrain = (level>2); } - + auto castSoundPath = spell->getCastSound(); if (!castSoundPath.empty()) CCS->soundh->playSound(castSoundPath); @@ -2783,8 +2783,8 @@ void CPlayerInterface::showWorldViewEx(const std::vector& objectP { EVENT_HANDLER_CALLED_BY_CLIENT; //TODO: showWorldViewEx - + std::copy(objectPositions.begin(), objectPositions.end(), std::back_inserter(adventureInt->worldViewOptions.iconPositions)); - + viewWorldMap(); } diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 9abf44444..f0c68c3d8 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -229,13 +229,13 @@ bool CGameInfoCallback::getTownInfo(const CGObjectInstance * town, InfoAboutTown { if(!detailed && nullptr != selectedObject) { - const CGHeroInstance * selectedHero = dynamic_cast(selectedObject); + const CGHeroInstance * selectedHero = dynamic_cast(selectedObject); if(nullptr != selectedHero) - detailed = selectedHero->hasVisions(town, 1); + detailed = selectedHero->hasVisions(town, 1); } - + dest.initFromTown(static_cast(town), detailed); - } + } else if(town->ID == Obj::GARRISON || town->ID == Obj::GARRISON2) dest.initFromArmy(static_cast(town), detailed); else @@ -268,28 +268,28 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero ERROR_RET_VAL_IF(!isVisible(h->getPosition(false)), "That hero is not visible!", false); bool accessFlag = hasAccess(h->tempOwner); - + if(!accessFlag && nullptr != selectedObject) { - const CGHeroInstance * selectedHero = dynamic_cast(selectedObject); + const CGHeroInstance * selectedHero = dynamic_cast(selectedObject); if(nullptr != selectedHero) - accessFlag = selectedHero->hasVisions(hero, 1); + accessFlag = selectedHero->hasVisions(hero, 1); } - + dest.initFromHero(h, accessFlag); - + //DISGUISED bonus implementation - + if(getPlayerRelations(getLocalPlayer(), hero->tempOwner) == PlayerRelations::ENEMIES) { - //todo: bonus cashing + //todo: bonus cashing int disguiseLevel = h->valOfBonuses(Selector::typeSubtype(Bonus::DISGUISED, 0)); - - auto doBasicDisguise = [disguiseLevel](InfoAboutHero & info) + + auto doBasicDisguise = [disguiseLevel](InfoAboutHero & info) { int maxAIValue = 0; const CCreature * mostStrong = nullptr; - + for(auto & elem : info.army) { if(elem.second.type->AIValue > maxAIValue) @@ -298,7 +298,7 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero mostStrong = elem.second.type; } } - + if(nullptr == mostStrong)//just in case logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Unable to select most strong stack" << disguiseLevel; else @@ -307,25 +307,25 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero elem.second.type = mostStrong; } }; - - auto doAdvancedDisguise = [accessFlag, &doBasicDisguise](InfoAboutHero & info) + + auto doAdvancedDisguise = [accessFlag, &doBasicDisguise](InfoAboutHero & info) { doBasicDisguise(info); - + for(auto & elem : info.army) elem.second.count = 0; }; - - auto doExpertDisguise = [this,h](InfoAboutHero & info) + + auto doExpertDisguise = [this,h](InfoAboutHero & info) { for(auto & elem : info.army) elem.second.count = 0; - + const auto factionIndex = getStartInfo(false)->playerInfos.at(h->tempOwner).castle; - + int maxAIValue = 0; const CCreature * mostStrong = nullptr; - + for(auto creature : VLC->creh->creatures) { if(creature->faction == factionIndex && creature->AIValue > maxAIValue) @@ -334,35 +334,35 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero mostStrong = creature; } } - + if(nullptr != mostStrong) //possible, faction may have no creatures at all for(auto & elem : info.army) elem.second.type = mostStrong; - }; - - + }; + + switch (disguiseLevel) { case 0: //no bonus at all - do nothing - break; + break; case 1: doBasicDisguise(dest); - break; + break; case 2: doAdvancedDisguise(dest); - break; + break; case 3: doExpertDisguise(dest); - break; + break; default: //invalid value logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Invalid DISGUISED bonus value " << disguiseLevel; break; } - + } - + return true; } @@ -486,7 +486,7 @@ std::shared_ptr> CGameInfoCallback::getAllVi boost::multi_array tileArray(boost::extents[width][height][levels]); - + for (size_t x = 0; x < width; x++) for (size_t y = 0; y < height; y++) for (size_t z = 0; z < levels; z++) @@ -723,15 +723,16 @@ int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool return -1; } -int3 CPlayerSpecificInfoCallback::getGrailPos( double &outKnownRatio ) +int3 CPlayerSpecificInfoCallback::getGrailPos( double *outKnownRatio ) { if (!player || CGObelisk::obeliskCount == 0) { - outKnownRatio = 0.0; + *outKnownRatio = 0.0; } else { - outKnownRatio = static_cast(CGObelisk::visited[gs->getPlayerTeam(*player)->id]) / CGObelisk::obeliskCount; + *outKnownRatio = static_cast(CGObelisk::visited[gs->getPlayerTeam(*player)->id]) + / CGObelisk::obeliskCount; } return gs->map->grailPos; } @@ -964,4 +965,3 @@ void IGameEventRealizer::setObjProperty(ObjectInstanceID objid, int prop, si64 v sob.val = static_cast(val); commitPackage(&sob); } - diff --git a/lib/CGameInfoCallback.h b/lib/CGameInfoCallback.h index 81c691e71..4d2903657 100644 --- a/lib/CGameInfoCallback.h +++ b/lib/CGameInfoCallback.h @@ -125,7 +125,7 @@ class DLL_LINKAGE CPlayerSpecificInfoCallback : public CGameInfoCallback public: int howManyTowns() const; int howManyHeroes(bool includeGarrisoned = true) const; - int3 getGrailPos(double &outKnownRatio); + int3 getGrailPos(double *outKnownRatio); boost::optional getMyColor() const; std::vector getTownsInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible @@ -139,7 +139,7 @@ public: int getResourceAmount(Res::ERes type) const; TResources getResourceAmount() const; - const std::vector< std::vector< std::vector > > & getVisibilityMap()const; //returns visibility map + const std::vector< std::vector< std::vector > > & getVisibilityMap()const; //returns visibility map const PlayerSettings * getPlayerSettings(PlayerColor color) const; }; @@ -154,4 +154,3 @@ public: virtual void showInfoDialog(const std::string &msg, PlayerColor player); }; - diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index b8964a342..c097ef511 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -533,7 +533,7 @@ void CGSeerHut::newTurn() const { if (quest->lastDay >= 0 && quest->lastDay < cb->getDate()-1) //time is up { - cb->setObjProperty (id, 10, CQuest::COMPLETE); + cb->setObjProperty (id, CGSeerHut::OBJPROP_VISITED, CQuest::COMPLETE); } } @@ -550,7 +550,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const if (firstVisit) { isCustom = quest->isCustomFirst; - cb->setObjProperty (id, 10, CQuest::IN_PROGRESS); + cb->setObjProperty (id, CGSeerHut::OBJPROP_VISITED, CQuest::IN_PROGRESS); AddQuest aq; aq.quest = QuestInfo (quest, this, visitablePos()); @@ -645,7 +645,7 @@ void CGSeerHut::finishQuest(const CGHeroInstance * h, ui32 accept) const default: break; } - cb->setObjProperty (id, 10, CQuest::COMPLETE); //mission complete + cb->setObjProperty (id, CGSeerHut::OBJPROP_VISITED, CQuest::COMPLETE); //mission complete completeQuest(h); //make sure to remove QuestGuard at the very end } } diff --git a/lib/mapObjects/CQuest.h b/lib/mapObjects/CQuest.h index cc9fd9063..18d35df98 100644 --- a/lib/mapObjects/CQuest.h +++ b/lib/mapObjects/CQuest.h @@ -118,6 +118,8 @@ public: h & rewardType & rID & rVal & seerName; } protected: + static constexpr int OBJPROP_VISITED = 10; + void setPropertyDer(ui8 what, ui32 val) override; }; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index dcdad9a27..587bb02b4 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -60,7 +60,7 @@ static std::string & visitedTxt(const bool visited) void CPlayersVisited::setPropertyDer( ui8 what, ui32 val ) { - if(what == 10) + if(what == CPlayersVisited::OBJPROP_VISITED) players.insert(PlayerColor(val)); } @@ -103,16 +103,16 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const { std::string hoverName; if(hero->hasVisions(this, 0)) - { + { MetaString ms; ms << stacks.begin()->second->count; ms << " " ; ms.addTxt(MetaString::CRE_PL_NAMES,subID); - + ms << "\n"; - + int decision = takenAction(hero, true); - + switch (decision) { case FIGHT: @@ -123,19 +123,19 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const break; case JOIN_FOR_FREE: ms.addTxt(MetaString::GENERAL_TXT,243); - break; + break; default: //decision = cost in gold VLC->generaltexth->allTexts[244]; - ms << boost::to_string(boost::format(VLC->generaltexth->allTexts[244]) % decision); + ms << boost::to_string(boost::format(VLC->generaltexth->allTexts[244]) % decision); break; - } + } - ms.toString(hoverName); + ms.toString(hoverName); } else { - hoverName = getHoverText(hero->tempOwner); - } + hoverName = getHoverText(hero->tempOwner); + } const JsonNode & texts = VLC->generaltexth->localizedTexts["adventureMap"]["monsterThreat"]; @@ -1310,7 +1310,7 @@ void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const iw.soundID = soundBase::gazebo; iw.player = h->getOwner(); if(!wasVisited(h->tempOwner)) - cb->setObjProperty(id, 10, h->tempOwner.getNum()); + cb->setObjProperty(id, CGWitchHut::OBJPROP_VISITED, h->tempOwner.getNum()); ui32 txt_id; if(h->getSecSkillLevel(SecondarySkill(ability))) //you already know this skill { @@ -1420,7 +1420,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const } if(!wasVisited(h->tempOwner)) - cb->setObjProperty(id, 10, h->tempOwner.getNum()); + cb->setObjProperty(id, CGShrine::OBJPROP_VISITED, h->tempOwner.getNum()); InfoWindow iw; iw.soundID = soundBase::temple; @@ -1745,7 +1745,7 @@ void CGShipyard::getOutOffsets( std::vector &offsets ) const int3(-3,0,0), int3(1,0,0), //AB int3(-3,1,0), int3(1,1,0), int3(-2,1,0), int3(0,1,0), int3(-1,1,0), //CDEFG int3(-3,-1,0), int3(1,-1,0), int3(-2,-1,0), int3(0,-1,0), int3(-1,-1,0) //HIJKL - }; + }; } void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const @@ -1821,7 +1821,7 @@ void CCartographer::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answ //water = 0; land = 1; underground = 2; cb->getAllTiles (fw.tiles, hero->tempOwner, subID - 1, !subID + 1); //reveal appropriate tiles cb->sendAndApply (&fw); - cb->setObjProperty (id, 10, hero->tempOwner.getNum()); + cb->setObjProperty (id, CCartographer::OBJPROP_VISITED, hero->tempOwner.getNum()); } } @@ -1843,11 +1843,13 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const iw.text.addTxt(MetaString::ADVOB_TXT, 96); cb->sendAndApply(&iw); - cb->setObjProperty(id, 20, h->tempOwner.getNum()); //increment general visited obelisks counter + // increment general visited obelisks counter + cb->setObjProperty(id, CGObelisk::OBJPROP_INC, team.getNum()); openWindow(OpenWindow::PUZZLE_MAP, h->tempOwner.getNum()); - cb->setObjProperty(id, 10, h->tempOwner.getNum()); //mark that particular obelisk as visited + // mark that particular obelisk as visited + cb->setObjProperty(id, CGObelisk::OBJPROP_VISITED, h->tempOwner.getNum()); } else { @@ -1869,20 +1871,26 @@ std::string CGObelisk::getHoverText(PlayerColor player) const void CGObelisk::setPropertyDer( ui8 what, ui32 val ) { - CPlayersVisited::setPropertyDer(what, val); switch(what) { - case 20: - assert(val < PlayerColor::PLAYER_LIMIT_I); - visited[TeamID(val)]++; + case CGObelisk::OBJPROP_INC: + { + assert(val < PlayerColor::PLAYER_LIMIT_I); + auto progress = ++visited[TeamID(val)]; + logGlobal->debugStream() << boost::format("Player %d: obelisk progress %d / %d") + % val % static_cast(progress) % static_cast(obeliskCount); - if(visited[TeamID(val)] > obeliskCount) - { - logGlobal->errorStream() << "Error: Visited " << visited[TeamID(val)] << "\t\t" << obeliskCount; - assert(0); - } + if(progress > obeliskCount) + { + logGlobal->errorStream() << "Error: Visited " << progress << "\t\t" << obeliskCount; + assert(0); + } - break; + break; + } + default: + CPlayersVisited::setPropertyDer(what, val); + break; } } diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index aa3afe4c6..de358318d 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -28,6 +28,8 @@ public: h & static_cast(*this); h & players; } + + static constexpr int OBJPROP_VISITED = 10; }; class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map @@ -451,6 +453,7 @@ class DLL_LINKAGE CGDenOfthieves : public CGObjectInstance class DLL_LINKAGE CGObelisk : public CPlayersVisited { public: + static constexpr int OBJPROP_INC = 20; static ui8 obeliskCount; //how many obelisks are on map static std::map visited; //map: team_id => how many obelisks has been visited From bb3a31f3ff339f1d8d5b4e6a7f96ad87ab56e046 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Wed, 20 Jan 2016 11:14:03 +0300 Subject: [PATCH 16/45] Mark obelisk visited for all players in the team --- lib/mapObjects/MiscObjects.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 587bb02b4..6fa3640c7 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -1848,8 +1848,11 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const openWindow(OpenWindow::PUZZLE_MAP, h->tempOwner.getNum()); - // mark that particular obelisk as visited - cb->setObjProperty(id, CGObelisk::OBJPROP_VISITED, h->tempOwner.getNum()); + // mark that particular obelisk as visited for all players in the team + for (auto & color : ts->players) + { + cb->setObjProperty(id, CGObelisk::OBJPROP_VISITED, color.getNum()); + } } else { From 1a2cd36df80b44a66d542b64fdcc47e1b0082c8f Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Wed, 20 Jan 2016 12:43:07 +0300 Subject: [PATCH 17/45] Initialize obelisks count to 0 Otherwise every time player starts with a random number. --- lib/mapObjects/MiscObjects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 6fa3640c7..1889dacd8 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -24,7 +24,7 @@ #include "../CPlayerState.h" std::map > CGMagi::eyelist; -ui8 CGObelisk::obeliskCount; //how many obelisks are on map +ui8 CGObelisk::obeliskCount = 0; //how many obelisks are on map std::map CGObelisk::visited; //map: team_id => how many obelisks has been visited ///helpers From e8db456bebcca238a1e83093064f9a8bd8bcdba0 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Wed, 20 Jan 2016 13:02:52 +0300 Subject: [PATCH 18/45] Reset obelisks upon game end Otherwise, the static variables are preserved and lead to wrong values in next new game. --- client/Client.cpp | 64 ++++++++++++++++++---------------- lib/mapObjects/MiscObjects.cpp | 6 ++++ lib/mapObjects/MiscObjects.h | 1 + 3 files changed, 40 insertions(+), 31 deletions(-) diff --git a/client/Client.cpp b/client/Client.cpp index 8f8ef5be0..1faea77fa 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -59,8 +59,8 @@ template class CApplyOnCL; class CBaseForCLApply { public: - virtual void applyOnClAfter(CClient *cl, void *pack) const =0; - virtual void applyOnClBefore(CClient *cl, void *pack) const =0; + virtual void applyOnClAfter(CClient *cl, void *pack) const =0; + virtual void applyOnClBefore(CClient *cl, void *pack) const =0; virtual ~CBaseForCLApply(){} template static CBaseForCLApply *getApplier(const U * t=nullptr) @@ -144,7 +144,7 @@ void CClient::waitForMoveAndSend(PlayerColor color) { logNetwork->traceStream() << "Send battle action to server: " << ba; MakeAction temp_action(ba); - sendRequest(&temp_action, color); + sendRequest(&temp_action, color); } return; } @@ -169,8 +169,8 @@ void CClient::run() while(!terminate) { CPack *pack = serv->retreivePack(); //get the package from the server - - if (terminate) + + if (terminate) { vstd::clear_pointer(pack); break; @@ -178,10 +178,10 @@ void CClient::run() handlePack(pack); } - } + } //catch only asio exceptions catch (const boost::system::system_error& e) - { + { logNetwork->errorStream() << "Lost connection to server, ending listening thread!"; logNetwork->errorStream() << e.what(); if(!terminate) //rethrow (-> boom!) only if closing connection was unexpected @@ -219,18 +219,20 @@ void CClient::endGame( bool closeConnection /*= true*/ ) GH.curInt = nullptr; { boost::unique_lock un(*LOCPLINT->pim); - logNetwork->infoStream() << "Ending current game!"; + logNetwork->infoStream() << "Ending current game!"; if(GH.topInt()) + { GH.topInt()->deactivate(); + } GH.listInt.clear(); GH.objsToBlit.clear(); GH.statusbar = nullptr; - logNetwork->infoStream() << "Removed GUI."; + logNetwork->infoStream() << "Removed GUI."; vstd::clear_pointer(const_cast(CGI)->mh); vstd::clear_pointer(gs); - logNetwork->infoStream() << "Deleted mapHandler and gameState."; + logNetwork->infoStream() << "Deleted mapHandler and gameState."; LOCPLINT = nullptr; } @@ -238,9 +240,9 @@ void CClient::endGame( bool closeConnection /*= true*/ ) battleints.clear(); callbacks.clear(); battleCallbacks.clear(); - logNetwork->infoStream() << "Deleted playerInts."; - - logNetwork->infoStream() << "Client stopped."; + CGObelisk::reset(); + logNetwork->infoStream() << "Deleted playerInts."; + logNetwork->infoStream() << "Client stopped."; } #if 1 @@ -320,7 +322,7 @@ void CClient::loadGame(const std::string & fname, const bool server, const std:: *serv << ui8(3) << ui8(loadNumPlayers); //load game; one client if single-player *serv << fname; *serv >> pom8; - if(pom8) + if(pom8) throw std::runtime_error("Server cannot open the savegame!"); else logNetwork->infoStream() << "Server opened savegame properly."; @@ -376,7 +378,7 @@ void CClient::newGame( CConnection *con, StartInfo *si ) { enum {SINGLE, HOST, GUEST} networkMode = SINGLE; - if (con == nullptr) + if (con == nullptr) { CServerHandler sh; serv = sh.connectToServer(); @@ -459,7 +461,7 @@ void CClient::newGame( CConnection *con, StartInfo *si ) logNetwork->infoStream() << boost::format("Player %s will be lead by %s") % color % AiToGive; installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), color); } - else + else { installNewPlayerInterface(std::make_shared(color), color); humanPlayers++; @@ -502,7 +504,7 @@ void CClient::newGame( CConnection *con, StartInfo *si ) // nm->giveActionCB(this); // nm->giveInfoCB(this); // nm->init(); -// +// // erm = nm; //something tells me that there'll at most one module and it'll be ERM // } } @@ -510,7 +512,7 @@ void CClient::newGame( CConnection *con, StartInfo *si ) void CClient::serialize(COSer & h, const int version) { assert(h.saving); - h & hotSeat; + h & hotSeat; { ui8 players = playerint.size(); h & players; @@ -520,7 +522,7 @@ void CClient::serialize(COSer & h, const int version) LOG_TRACE_PARAMS(logGlobal, "Saving player %s interface", i->first); assert(i->first == i->second->playerID); h & i->first & i->second->dllName & i->second->human; - i->second->saveGame(h, version); + i->second->saveGame(h, version); } } } @@ -536,7 +538,7 @@ void CClient::serialize(CISer & h, const int version) for(int i=0; i < players; i++) { std::string dllname; - PlayerColor pid; + PlayerColor pid; bool isHuman = false; h & pid & dllname & isHuman; @@ -548,7 +550,7 @@ void CClient::serialize(CISer & h, const int version) if(pid == PlayerColor::NEUTRAL) { installNewBattleInterface(CDynLibHandler::getNewBattleAI(dllname), pid); - //TODO? consider serialization + //TODO? consider serialization continue; } else @@ -589,7 +591,7 @@ void CClient::serialize(COSer & h, const int version, const std::setfirst); assert(i->first == i->second->playerID); h & i->first & i->second->dllName & i->second->human; - i->second->saveGame(h, version); + i->second->saveGame(h, version); } } } @@ -605,7 +607,7 @@ void CClient::serialize(CISer & h, const int version, const std::setloadGame(h, version); + nInt->loadGame(h, version); } if(playerIDs.count(PlayerColor::NEUTRAL)) @@ -714,7 +716,7 @@ void CClient::battleStarted(const BattleInfo * info) { for(auto &battleCb : battleCallbacks) { - if(vstd::contains_if(info->sides, [&](const SideInBattle& side) {return side.color == battleCb.first; }) + if(vstd::contains_if(info->sides, [&](const SideInBattle& side) {return side.color == battleCb.first; }) || battleCb.first >= PlayerColor::PLAYER_LIMIT) { battleCb.second->setBattle(info); @@ -742,7 +744,7 @@ void CClient::battleStarted(const BattleInfo * info) { boost::unique_lock un(*LOCPLINT->pim); auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero, - Rect((screen->w - 800)/2, + Rect((screen->w - 800)/2, (screen->h - 600)/2, 800, 600), att, def); GH.pushInt(bi); @@ -805,7 +807,7 @@ void CClient::commenceTacticPhaseForInt(std::shared_ptr ba catch(...) { handleException(); - } + } } void CClient::invalidatePaths() @@ -889,7 +891,7 @@ void CClient::installNewBattleInterface(std::shared_ptr ba boost::unique_lock un(*LOCPLINT->pim); PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE); - if(!color) + if(!color) privilagedBattleEventReceivers.push_back(battleInterface); battleints[colorUsed] = battleInterface; @@ -961,7 +963,7 @@ CConnection * CServerHandler::connectToServer() #endif th.update(); //put breakpoint here to attach to server before it does something stupid - + CConnection *ret = justConnectToServer(settings["server"]["server"].String(), port); if(verbose) @@ -1033,7 +1035,7 @@ CConnection * CServerHandler::justConnectToServer(const std::string &host, const try { logNetwork->infoStream() << "Establishing connection..."; - ret = new CConnection( host.size() ? host : settings["server"]["server"].String(), + ret = new CConnection( host.size() ? host : settings["server"]["server"].String(), realPort, NAME); } diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 1889dacd8..28bfdb09f 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -1867,6 +1867,12 @@ void CGObelisk::initObj() obeliskCount++; } +void CGObelisk::reset() +{ + obeliskCount = 0; + visited.clear(); +} + std::string CGObelisk::getHoverText(PlayerColor player) const { return getObjectName() + " " + visitedTxt(wasVisited(player)); diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index de358318d..5cc9f379f 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -460,6 +460,7 @@ public: void onHeroVisit(const CGHeroInstance * h) const override; void initObj() override; std::string getHoverText(PlayerColor player) const override; + static void reset(); template void serialize(Handler &h, const int version) { From 419bde899773c2bb17dd7eefcad8e728efe1c450 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Thu, 21 Jan 2016 09:57:52 +0300 Subject: [PATCH 19/45] Fix 2358 swapped garrison <-> visiting status text --- client/widgets/CGarrisonInt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/widgets/CGarrisonInt.cpp b/client/widgets/CGarrisonInt.cpp index f3efd8559..7d582042c 100644 --- a/client/widgets/CGarrisonInt.cpp +++ b/client/widgets/CGarrisonInt.cpp @@ -73,11 +73,11 @@ void CGarrisonSlot::hover (bool on) { if(upg == EGarrisonType::UP) { - temp = CGI->generaltexth->tcommands[32]; //Select %s (visiting) + temp = CGI->generaltexth->tcommands[12]; //Select %s (in garrison) } else if(owner->armedObjs[0] && (owner->armedObjs[0]->ID == Obj::TOWN || owner->armedObjs[0]->ID == Obj::HERO)) { - temp = CGI->generaltexth->tcommands[12]; //Select %s (in garrison) + temp = CGI->generaltexth->tcommands[32]; //Select %s (visiting) } else { From d25372a397a9306eb799afe1c52fdaa486d20731 Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Thu, 21 Jan 2016 10:49:09 +0300 Subject: [PATCH 20/45] Added vcmiglaurung cheat that add 5000 crystal dragons into each slot --- server/CGameHandler.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 1be4bd9e1..10ee4951d 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4026,6 +4026,16 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message if(!hero->hasStackAtSlot(SlotID(i))) insertNewStack(StackLocation(hero, SlotID(i)), blackKnight, 10); } + else if(message == "vcmiglaurung") //gives 5000 crystal dragons into each slot + { + CGHeroInstance *hero = gs->getHero(currObj); + const CCreature *crystalDragon = VLC->creh->creatures.at(133); + if(!hero) return; + + for(int i = 0; i < GameConstants::ARMY_SIZE; i++) + if(!hero->hasStackAtSlot(SlotID(i))) + insertNewStack(StackLocation(hero, SlotID(i)), crystalDragon, 5000); + } else if(message == "vcminoldor") //all war machines { CGHeroInstance *hero = gs->getHero(currObj); From b1f3480c685f2f180714e71391466668302314f4 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Thu, 21 Jan 2016 10:49:46 +0300 Subject: [PATCH 21/45] Fix 1821 artifact assembly statusbar text --- client/widgets/CArtifactHolder.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index bf6e84ad4..4e01243e6 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -303,10 +303,10 @@ void CArtPlace::deselect () for(int i = 0; i < GameConstants::BACKPACK_START; i++) { auto place = ourOwner->getArtPlace(i); - + if(nullptr != place)//getArtPlace may return null place->pickSlot(false); - } + } } CCS->curh->dragAndDropCursor(nullptr); @@ -454,12 +454,12 @@ void CArtifactsOfHero::setHero(const CGHeroInstance * hero) backpackPos = 0; // Fill the slots for worn artifacts and backpack. - + for(auto p : artWorn) { setSlotData(p.second, p.first); } - + scrollBackpack(0); } @@ -587,8 +587,8 @@ void CArtifactsOfHero::setSlotData(CArtPlace* artPlace, ArtifactPosition slotID) if(const ArtSlotInfo *asi = curHero->getSlot(slotID)) { - artPlace->setArtifact(asi->artifact); artPlace->lockSlot(asi->locked); + artPlace->setArtifact(asi->artifact); } else artPlace->setArtifact(nullptr); @@ -853,7 +853,7 @@ CArtPlace * CArtifactsOfHero::getArtPlace(int slot) for(CArtPlace *ap : backpack) if(ap->slotID == slot) return ap; - return nullptr; + return nullptr; } } From 6ce645b28f4a200be62e0244aa8f59614757a30b Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Thu, 21 Jan 2016 10:58:43 +0300 Subject: [PATCH 22/45] Added new cheat into changelog --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index d496b2a2a..f7830abef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,8 @@ GENERAL: - Angel Wings - Boots of Levitation * Implemented rumors in tavern window +* New cheat code: +- vcmiglaurung - gives 5000 crystal dragons into each slot ADVETURE AI: * Fixed AI trying to go through underground rock From 11bce2908ddde0346b8a74d14062749f3809310d Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Fri, 22 Jan 2016 12:53:01 +0300 Subject: [PATCH 23/45] Fix 1723 quest crash on combined arts --- lib/CArtHandler.cpp | 45 +++++++++++++++++++++++++++++++++------ lib/CArtHandler.h | 25 +++++++++++++++------- lib/NetPacksLib.cpp | 34 +++++++++++++++++++++++++---- lib/mapObjects/CQuest.cpp | 14 +++++++++++- server/CGameHandler.cpp | 1 - 5 files changed, 99 insertions(+), 20 deletions(-) diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 9d89aae60..4e48b5ff3 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -280,16 +280,16 @@ ArtifactPosition CArtHandler::stringToSlot(std::string slotName) void CArtHandler::addSlot(CArtifact * art, const std::string & slotID) { - static const std::vector miscSlots = + static const std::vector miscSlots = { ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5 }; - + static const std::vector ringSlots = { ArtifactPosition::LEFT_RING, ArtifactPosition::RIGHT_RING }; - + if (slotID == "MISC") { vstd::concatenate(art->possibleSlots[ArtBearer::HERO], miscSlots); @@ -323,7 +323,7 @@ void CArtHandler::loadSlots(CArtifact * art, const JsonNode & node) CArtifact::EartClass CArtHandler::stringToClass(std::string className) { static const std::map artifactClassMap = - { + { {"TREASURE", CArtifact::ART_TREASURE}, {"MINOR", CArtifact::ART_MINOR}, {"MAJOR", CArtifact::ART_MAJOR}, @@ -1152,9 +1152,42 @@ const CArtifactInstance * CArtifactSet::getArtByInstanceId( ArtifactInstanceID a return nullptr; } -bool CArtifactSet::hasArt(ui32 aid, bool onlyWorn /*= false*/) const +bool CArtifactSet::hasArt(ui32 aid, bool onlyWorn /*= false*/, + bool searchBackpackAssemblies /*= false*/) const { - return getArtPos(aid, onlyWorn) != ArtifactPosition::PRE_FIRST; + return getArtPos(aid, onlyWorn) != ArtifactPosition::PRE_FIRST || + (searchBackpackAssemblies && getHiddenArt(aid)); +} + +std::pair +CArtifactSet::searchForConstituent(int aid) const +{ + for(auto & slot : artifactsInBackpack) + { + auto art = slot.artifact; + if(art->canBeDisassembled()) + { + auto ass = static_cast(art.get()); + for(auto& ci : ass->constituentsInfo) + { + if(ci.art->artType->id == aid) + { + return {ass, ci.art}; + } + } + } + } + return {nullptr, nullptr}; +} + +const CArtifactInstance *CArtifactSet::getHiddenArt(int aid) const +{ + return searchForConstituent(aid).second; +} + +const CCombinedArtifactInstance *CArtifactSet::getAssemblyByConstituent(int aid) const +{ + return searchForConstituent(aid).first; } const ArtSlotInfo * CArtifactSet::getSlot(ArtifactPosition pos) const diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index 6a71ac2bf..d9ffdc46e 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -128,7 +128,9 @@ public: virtual bool canBeDisassembled() const; virtual void putAt(ArtifactLocation al); virtual void removeFrom(ArtifactLocation al); - virtual bool isPart(const CArtifactInstance *supposedPart) const; //checks if this a part of this artifact: artifact instance is a part of itself, additionally truth is returned for consituents of combined arts + /// Checks if this a part of this artifact: artifact instance is a part + /// of itself, additionally truth is returned for constituents of combined arts + virtual bool isPart(const CArtifactInstance *supposedPart) const; std::vector assemblyPossibilities(const CArtifactSet *h) const; void move(ArtifactLocation src, ArtifactLocation dst); @@ -172,7 +174,7 @@ public: void createConstituents(); void addAsConstituent(CArtifactInstance *art, ArtifactPosition slot); - CArtifactInstance *figureMainConstituent(const ArtifactLocation al); //main constituent is replcaed with us (combined art), not lock + CArtifactInstance *figureMainConstituent(const ArtifactLocation al); //main constituent is replaced with us (combined art), not lock CCombinedArtifactInstance(); @@ -265,10 +267,8 @@ struct DLL_LINKAGE ArtSlotInfo ConstTransitivePtr artifact; ui8 locked; //if locked, then artifact points to the combined artifact - ArtSlotInfo() - { - locked = false; - } + ArtSlotInfo() : locked(false) {} + template void serialize(Handler &h, const int version) { h & artifact & locked; @@ -288,10 +288,16 @@ public: const ArtSlotInfo *getSlot(ArtifactPosition pos) const; const CArtifactInstance* getArt(ArtifactPosition pos, bool excludeLocked = true) const; //nullptr - no artifact CArtifactInstance* getArt(ArtifactPosition pos, bool excludeLocked = true); //nullptr - no artifact - ArtifactPosition getArtPos(int aid, bool onlyWorn = true) const; //looks for equipped artifact with given ID and returns its slot ID or -1 if none(if more than one such artifact lower ID is returned) + /// Looks for equipped artifact with given ID and returns its slot ID or -1 if none + /// (if more than one such artifact lower ID is returned) + ArtifactPosition getArtPos(int aid, bool onlyWorn = true) const; ArtifactPosition getArtPos(const CArtifactInstance *art) const; const CArtifactInstance *getArtByInstanceId(ArtifactInstanceID artInstId) const; - bool hasArt(ui32 aid, bool onlyWorn = false) const; //checks if hero possess artifact of given id (either in backack or worn) + /// Search for constituents of assemblies in backpack which do not have an ArtifactPosition + const CArtifactInstance *getHiddenArt(int aid) const; + const CCombinedArtifactInstance *getAssemblyByConstituent(int aid) const; + /// Checks if hero possess artifact of given id (either in backack or worn) + bool hasArt(ui32 aid, bool onlyWorn = false, bool searchBackpackAssemblies = false) const; bool isPositionFree(ArtifactPosition pos, bool onlyLockCheck = false) const; si32 getArtTypeId(ArtifactPosition pos) const; @@ -304,4 +310,7 @@ public: } void artDeserializationFix(CBonusSystemNode *node); + +protected: + std::pair searchForConstituent(int aid) const; }; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 7de59201b..7a5fe5c04 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -340,7 +340,7 @@ DLL_LINKAGE void RemoveBonus::applyGs( CGameState *gs ) if(b->source == source && b->sid == id) { bonus = *b; //backup bonus (to show to interfaces later) - node->removeBonus(b); + node->removeBonus(b); break; } } @@ -754,7 +754,7 @@ DLL_LINKAGE const CArtifactInstance *ArtifactLocation::getArt() const return s->artifact; else { - logNetwork->warnStream() << "ArtifactLocation::getArt: That location is locked!"; + logNetwork->warnStream() << "ArtifactLocation::getArt: This location is locked!"; return nullptr; } } @@ -914,6 +914,32 @@ DLL_LINKAGE void PutArtifact::applyGs( CGameState *gs ) DLL_LINKAGE void EraseArtifact::applyGs( CGameState *gs ) { + auto slot = al.getSlot(); + if(slot->locked) + { + logGlobal->debugStream() << "Erasing locked artifact: " << slot->artifact->artType->Name(); + DisassembledArtifact dis; + dis.al.artHolder = al.artHolder; + auto aset = al.getHolderArtSet(); + bool found = false; + for(auto& p : aset->artifactsWorn) + { + auto art = p.second.artifact; + if(art->canBeDisassembled() && art->isPart(slot->artifact)) + { + dis.al.slot = aset->getArtPos(art); + found = true; + break; + } + } + assert(found && "Failed to determine the assembly this locked artifact belongs to"); + logGlobal->debugStream() << "Found the corresponding assembly: " << dis.al.getSlot()->artifact->artType->Name(); + dis.applyGs(gs); + } + else + { + logGlobal->debugStream() << "Erasing artifact " << slot->artifact->artType->Name(); + } al.removeArtifact(); } @@ -1262,7 +1288,7 @@ DLL_LINKAGE void BattleStackAttacked::applyGs( CGameState *gs ) { //"hide" killed creatures instead so we keep info about it at->state.insert(EBattleStackState::DEAD_CLONE); - + for(CStack * s : gs->curB->stacks) { if(s->cloneID == at->ID) @@ -1375,7 +1401,7 @@ void actualizeEffect(CStack * s, const Bonus & ef) stackBonus->turnsRemain = std::max(stackBonus->turnsRemain, ef.turnsRemain); } } - CBonusSystemNode::treeHasChanged(); + CBonusSystemNode::treeHasChanged(); } void actualizeEffect(CStack * s, const std::vector & ef) diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index b8964a342..0db3258bd 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -70,7 +70,7 @@ bool CQuest::checkQuest (const CGHeroInstance * h) const case MISSION_ART: for (auto & elem : m5arts) { - if (h->hasArt(elem)) + if (h->hasArt(elem, false, true)) continue; return false; //if the artifact was not found } @@ -630,6 +630,18 @@ void CGSeerHut::finishQuest(const CGHeroInstance * h, ui32 accept) const case CQuest::MISSION_ART: for (auto & elem : quest->m5arts) { + if(!h->hasArt(elem)) + { + // first we need to disassemble this backpack artifact + auto assembly = h->getAssemblyByConstituent(elem); + assert(assembly); + for(auto & ci : assembly->constituentsInfo) + { + cb->giveHeroNewArtifact(h, ci.art->artType, ArtifactPosition::PRE_FIRST); + } + // remove the assembly + cb->removeArtifact(ArtifactLocation(h, h->getArtPos(assembly))); + } cb->removeArtifact(ArtifactLocation(h, h->getArtPos(elem, false))); } break; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 10ee4951d..5748ee1c6 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2113,7 +2113,6 @@ void CGameHandler::stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroI void CGameHandler::removeArtifact(const ArtifactLocation &al) { - assert(al.getArt()); EraseArtifact ea; ea.al = al; sendAndApply(&ea); From 9dfef2186d2161ba507c41f4132815c9ae9fb9c0 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Fri, 22 Jan 2016 22:29:53 +0300 Subject: [PATCH 24/45] Fix 1569 winning hero with no troops shall retreat --- server/CGameHandler.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 10ee4951d..19627cb5a 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -712,7 +712,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result ) setBattle(nullptr); - if(visitObjectAfterVictory && result.winner==0) + if(visitObjectAfterVictory && result.winner==0 && !finishingBattle->winnerHero->stacks.empty()) { logGlobal->traceStream() << "post-victory visit"; visitObjectOnTile(*getTile(finishingBattle->winnerHero->getPosition()), finishingBattle->winnerHero); @@ -741,6 +741,24 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result ) sendAndApply(&sah); } + if(finishingBattle->winnerHero->stacks.empty()) + { + RemoveObject ro(finishingBattle->winnerHero->id); + sendAndApply(&ro); + + SetAvailableHeroes sah; + sah.player = finishingBattle->victor; + sah.hid[0] = finishingBattle->winnerHero->subID; + sah.army[0].clear(); + sah.army[0].setCreature(SlotID(0), finishingBattle->winnerHero->type->initialArmy.at(0).creature, 1); + + if(const CGHeroInstance *another = getPlayer(finishingBattle->victor)->availableHeroes.at(1)) + sah.hid[1] = another->subID; + else + sah.hid[1] = -1; + + sendAndApply(&sah); + } } void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance, int targetHex) From 99992599dbea19221ced02de76de3e4a2de26933 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Fri, 22 Jan 2016 22:37:46 +0300 Subject: [PATCH 25/45] Partially fix 1416 tavern hero rotation --- server/CGameHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 19627cb5a..03a703240 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -734,7 +734,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result ) sah.army[0].setCreature(SlotID(0), finishingBattle->loserHero->type->initialArmy.at(0).creature, 1); } - if(const CGHeroInstance *another = getPlayer(finishingBattle->loser)->availableHeroes.at(1)) + if(const CGHeroInstance *another = getPlayer(finishingBattle->loser)->availableHeroes.at(0)) sah.hid[1] = another->subID; else sah.hid[1] = -1; @@ -752,7 +752,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result ) sah.army[0].clear(); sah.army[0].setCreature(SlotID(0), finishingBattle->winnerHero->type->initialArmy.at(0).creature, 1); - if(const CGHeroInstance *another = getPlayer(finishingBattle->victor)->availableHeroes.at(1)) + if(const CGHeroInstance *another = getPlayer(finishingBattle->victor)->availableHeroes.at(0)) sah.hid[1] = another->subID; else sah.hid[1] = -1; From f2376485e3740df4fddf63af1390929bc4291dd7 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Fri, 22 Jan 2016 23:30:24 +0300 Subject: [PATCH 26/45] Fix 1799 not updated hero info box on artifact pickup --- client/CPlayerInterface.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 33040aef8..016841820 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -119,7 +119,7 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player) isAutoFightOn = false; duringMovement = false; - ignoreEvents = false; + ignoreEvents = false; } CPlayerInterface::~CPlayerInterface() @@ -824,16 +824,16 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i //tidy up BattleAction ret = *(b->givenCommand->data); vstd::clear_pointer(b->givenCommand->data); - + if(ret.actionType == Battle::CANCEL) { if(stackId != ret.stackNumber) logGlobal->error("Not current active stack action canceled"); - logGlobal->traceStream() << "Canceled command for " << stackName; + logGlobal->traceStream() << "Canceled command for " << stackName; } else logGlobal->traceStream() << "Giving command for " << stackName; - + return ret; } @@ -1610,11 +1610,11 @@ void CPlayerInterface::update() { // Make sure that gamestate won't change when GUI objects may obtain its parts on event processing or drawing request boost::shared_lock gsLock(cb->getGsMutex()); - - // While mutexes were locked away we may be have stopped being the active interface + + // While mutexes were locked away we may be have stopped being the active interface if(LOCPLINT != this) return; - + //if there are any waiting dialogs, show them if((howManyPeople <= 1 || makingTurn) && !dialogs.empty() && !showingDialog->get()) { @@ -2195,7 +2195,7 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI int level = caster->getSpellSchoolLevel(spell); adventureInt->worldViewOptions.showAllTerrain = (level>2); } - + auto castSoundPath = spell->getCastSound(); if (!castSoundPath.empty()) CCS->soundh->playSound(castSoundPath); @@ -2513,6 +2513,7 @@ void CPlayerInterface::stacksRebalanced(const StackLocation &src, const StackLoc void CPlayerInterface::artifactPut(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; + adventureInt->infoBar.showSelection(); } void CPlayerInterface::artifactRemoved(const ArtifactLocation &al) @@ -2783,8 +2784,8 @@ void CPlayerInterface::showWorldViewEx(const std::vector& objectP { EVENT_HANDLER_CALLED_BY_CLIENT; //TODO: showWorldViewEx - + std::copy(objectPositions.begin(), objectPositions.end(), std::back_inserter(adventureInt->worldViewOptions.iconPositions)); - + viewWorldMap(); } From a266154b265c3bd1d8a40762240d72d6bfeff310 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Sat, 23 Jan 2016 00:35:41 +0300 Subject: [PATCH 27/45] Fix 2014 cursor must disappear during movement and Magi's Eye --- client/CPlayerInterface.cpp | 41 ++++++++++++++++++++++------------ client/CPlayerInterface.h | 3 ++- lib/mapObjects/MiscObjects.cpp | 23 ++++++++++--------- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 33040aef8..d17162da9 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -119,7 +119,7 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player) isAutoFightOn = false; duringMovement = false; - ignoreEvents = false; + ignoreEvents = false; } CPlayerInterface::~CPlayerInterface() @@ -824,16 +824,16 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i //tidy up BattleAction ret = *(b->givenCommand->data); vstd::clear_pointer(b->givenCommand->data); - + if(ret.actionType == Battle::CANCEL) { if(stackId != ret.stackNumber) logGlobal->error("Not current active stack action canceled"); - logGlobal->traceStream() << "Canceled command for " << stackName; + logGlobal->traceStream() << "Canceled command for " << stackName; } else logGlobal->traceStream() << "Giving command for " << stackName; - + return ret; } @@ -1335,7 +1335,7 @@ void CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path ) if(showingDialog->get() || !dialogs.empty()) return; - duringMovement = true; + setMovementStatus(true); if (adventureInt && adventureInt->isHeroSleeping(h)) { @@ -1347,8 +1347,6 @@ void CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path ) } boost::thread moveHeroTask(std::bind(&CPlayerInterface::doMoveHero,this,h,path)); - - } bool CPlayerInterface::shiftPressed() const @@ -1559,6 +1557,7 @@ void CPlayerInterface::centerView (int3 pos, int focusTime) { EVENT_HANDLER_CALLED_BY_CLIENT; waitWhileDialog(); + CCS->curh->hide(); adventureInt->centerOn (pos); if(focusTime) { @@ -1569,6 +1568,7 @@ void CPlayerInterface::centerView (int3 pos, int focusTime) SDL_Delay(focusTime); } } + CCS->curh->show(); } void CPlayerInterface::objectRemoved( const CGObjectInstance *obj ) @@ -1610,11 +1610,11 @@ void CPlayerInterface::update() { // Make sure that gamestate won't change when GUI objects may obtain its parts on event processing or drawing request boost::shared_lock gsLock(cb->getGsMutex()); - - // While mutexes were locked away we may be have stopped being the active interface + + // While mutexes were locked away we may be have stopped being the active interface if(LOCPLINT != this) return; - + //if there are any waiting dialogs, show them if((howManyPeople <= 1 || makingTurn) && !dialogs.empty() && !showingDialog->get()) { @@ -2195,7 +2195,7 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI int level = caster->getSpellSchoolLevel(spell); adventureInt->worldViewOptions.showAllTerrain = (level>2); } - + auto castSoundPath = spell->getCastSound(); if (!castSoundPath.empty()) CCS->soundh->playSound(castSoundPath); @@ -2632,6 +2632,19 @@ bool CPlayerInterface::capturedAllEvents() return false; } +void CPlayerInterface::setMovementStatus(bool value) +{ + duringMovement = value; + if(value) + { + CCS->curh->hide(); + } + else + { + CCS->curh->show(); + } +} + void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) { int i = 1; @@ -2776,15 +2789,15 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) adventureInt->updateNextHero(h); } - duringMovement = false; + setMovementStatus(false); } void CPlayerInterface::showWorldViewEx(const std::vector& objectPositions) { EVENT_HANDLER_CALLED_BY_CLIENT; //TODO: showWorldViewEx - + std::copy(objectPositions.begin(), objectPositions.end(), std::back_inserter(adventureInt->worldViewOptions.iconPositions)); - + viewWorldMap(); } diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 0d6c86e51..4f4ce274c 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -197,7 +197,7 @@ public: void showComp(const Component &comp, std::string message) override; //display component in the advmapint infobox void saveGame(COSer & h, const int version) override; //saving void loadGame(CISer & h, const int version) override; //loading - void showWorldViewEx(const std::vector & objectPositions) override; + void showWorldViewEx(const std::vector & objectPositions) override; //for battles void actionFinished(const BattleAction& action) override;//occurs AFTER action taken by active stack or by the hero @@ -295,6 +295,7 @@ private: bool ignoreEvents; void doMoveHero(const CGHeroInstance *h, CGPath path); + void setMovementStatus(bool value); }; extern CPlayerInterface * LOCPLINT; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index dcdad9a27..51a293ed3 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -103,16 +103,16 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const { std::string hoverName; if(hero->hasVisions(this, 0)) - { + { MetaString ms; ms << stacks.begin()->second->count; ms << " " ; ms.addTxt(MetaString::CRE_PL_NAMES,subID); - + ms << "\n"; - + int decision = takenAction(hero, true); - + switch (decision) { case FIGHT: @@ -123,19 +123,19 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const break; case JOIN_FOR_FREE: ms.addTxt(MetaString::GENERAL_TXT,243); - break; + break; default: //decision = cost in gold VLC->generaltexth->allTexts[244]; - ms << boost::to_string(boost::format(VLC->generaltexth->allTexts[244]) % decision); + ms << boost::to_string(boost::format(VLC->generaltexth->allTexts[244]) % decision); break; - } + } - ms.toString(hoverName); + ms.toString(hoverName); } else { - hoverName = getHoverText(hero->tempOwner); - } + hoverName = getHoverText(hero->tempOwner); + } const JsonNode & texts = VLC->generaltexth->localizedTexts["adventureMap"]["monsterThreat"]; @@ -1667,6 +1667,7 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const cb->sendAndApply(&cv); } cv.pos = h->getPosition(false); + cv.focusTime = 0; cb->sendAndApply(&cv); } } @@ -1745,7 +1746,7 @@ void CGShipyard::getOutOffsets( std::vector &offsets ) const int3(-3,0,0), int3(1,0,0), //AB int3(-3,1,0), int3(1,1,0), int3(-2,1,0), int3(0,1,0), int3(-1,1,0), //CDEFG int3(-3,-1,0), int3(1,-1,0), int3(-2,-1,0), int3(0,-1,0), int3(-1,-1,0) //HIJKL - }; + }; } void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const From 9ffd0155e6cae9057d1f64830751073f2dcc3fbd Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Sat, 23 Jan 2016 15:20:51 +0300 Subject: [PATCH 28/45] Fix 1810 suggest artifact assembly --- client/CPlayerInterface.cpp | 11 ++++++++ client/CPlayerInterface.h | 1 + client/widgets/CArtifactHolder.cpp | 40 ++++++++++++++++++++---------- client/widgets/CArtifactHolder.h | 6 +++-- server/CGameHandler.cpp | 11 +++++--- 5 files changed, 50 insertions(+), 19 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index fe3522979..c8701f8dd 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -2510,10 +2510,20 @@ void CPlayerInterface::stacksRebalanced(const StackLocation &src, const StackLoc garrisonsChanged(objects); } +void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al) +{ + auto hero = dynamic_cast(al.relatedObj()); + if(hero) + { + CArtPlace::askToAssemble(hero->getArt(al.slot), al.slot, hero); + } +} + void CPlayerInterface::artifactPut(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; adventureInt->infoBar.showSelection(); + askToAssembleArtifact(al); } void CPlayerInterface::artifactRemoved(const ArtifactLocation &al) @@ -2538,6 +2548,7 @@ void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const Artifact if(artWin) artWin->artifactMoved(src, dst); } + askToAssembleArtifact(dst); } void CPlayerInterface::artifactAssembled(const ArtifactLocation &al) diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 4f4ce274c..bc54b9899 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -296,6 +296,7 @@ private: void doMoveHero(const CGHeroInstance *h, CGPath path); void setMovementStatus(bool value); + void askToAssembleArtifact(const ArtifactLocation &al); }; extern CPlayerInterface * LOCPLINT; diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index 4e01243e6..f885ad961 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -214,6 +214,32 @@ void CArtPlace::clickLeft(tribool down, bool previousState) } } +bool CArtPlace::askToAssemble(const CArtifactInstance *art, ArtifactPosition slot, + const CGHeroInstance *hero) +{ + std::vector assemblyPossibilities = art->assemblyPossibilities(hero); + + // If the artifact can be assembled, display dialog. + for(const CArtifact *combination : assemblyPossibilities) + { + LOCPLINT->showArtifactAssemblyDialog( + art->artType->id, + combination->id, + true, + std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combination->id), + 0); + + if(assemblyPossibilities.size() > 2) + { + logGlobal->warnStream() << boost::format( + "More than one possibility of assembling on %s... taking only first") + % art->artType->Name(); + } + return true; + } + return false; +} + void CArtPlace::clickRight(tribool down, bool previousState) { if(down && ourArt && !locked && text.size() && !picked) //if there is no description or it's a lock, do nothing ;] @@ -225,20 +251,8 @@ void CArtPlace::clickRight(tribool down, bool previousState) std::vector assemblyPossibilities = ourArt->assemblyPossibilities(ourOwner->curHero); // If the artifact can be assembled, display dialog. - for(const CArtifact *combination : assemblyPossibilities) + if (askToAssemble(ourArt, slotID, ourOwner->curHero)) { - LOCPLINT->showArtifactAssemblyDialog( - ourArt->artType->id, - combination->id, - true, - std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), ourOwner->curHero, slotID, true, combination->id), - 0); - - if(assemblyPossibilities.size() > 2) - { - logGlobal->warnStream() << "More than one possibility of assembling... taking only first"; - break; - } return; } diff --git a/client/widgets/CArtifactHolder.h b/client/widgets/CArtifactHolder.h index 7dae86b4e..40b308c47 100644 --- a/client/widgets/CArtifactHolder.h +++ b/client/widgets/CArtifactHolder.h @@ -74,15 +74,17 @@ public: void setMeAsDest(bool backpackAsVoid = true); void setArtifact(const CArtifactInstance *art); + static bool askToAssemble(const CArtifactInstance *art, ArtifactPosition slot, + const CGHeroInstance *hero); }; /// Contains artifacts of hero. Distincts which artifacts are worn or backpacked class CArtifactsOfHero : public CIntObject { const CGHeroInstance * curHero; - + std::map artWorn; - + std::vector backpack; //hero's visible backpack (only 5 elements!) int backpackPos; //number of first art visible in backpack (in hero's vector) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 10ee4951d..ed96081ab 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -926,13 +926,16 @@ void CGameHandler::handleConnection(std::set players, CConnection & { const bool result = apply->applyOnGH(this,&c,pack, player); if(!result) - complain("Got false in applying... that request must have been fishy!"); - logGlobal->traceStream() << "Message successfully applied (result=" << result << ")!"; + { + complain((boost::format("Got false in applying %s... that request must have been fishy!") + % typeid(*pack).name()).str()); + } + logGlobal->traceStream() << "Message successfully applied (result=" << result << ")!"; sendPackageResponse(true); } else { - logGlobal->errorStream() << "Message cannot be applied, cannot find applier (unregistered type)!"; + logGlobal->errorStream() << "Message cannot be applied, cannot find applier (unregistered type)!"; sendPackageResponse(false); } @@ -3038,7 +3041,7 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition sendAndApply(&da); } - return false; + return true; } bool CGameHandler::buyArtifact( ObjectInstanceID hid, ArtifactID aid ) From 2a6a8cd43356aeaec7a9355a8859a4d3a389e39c Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Sat, 23 Jan 2016 19:42:56 +0300 Subject: [PATCH 29/45] Fix segfault when non-hero forces win --- server/CGameHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 03a703240..9912d53f8 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -741,7 +741,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result ) sendAndApply(&sah); } - if(finishingBattle->winnerHero->stacks.empty()) + if(finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty()) { RemoveObject ro(finishingBattle->winnerHero->id); sendAndApply(&ro); From 7185890723031c033b87468a9f4edf4ae34c9b2c Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Sun, 24 Jan 2016 01:31:39 +0300 Subject: [PATCH 30/45] Fix double free in battleAfterLevelUp() in case of a draw --- server/CGameHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 9912d53f8..75802cb9c 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -741,7 +741,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result ) sendAndApply(&sah); } - if(finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty()) + if(result.winner != 2 && finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty()) { RemoveObject ro(finishingBattle->winnerHero->id); sendAndApply(&ro); From afa95312baaf5318301edb3ffd669443e64bb8f0 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Thu, 21 Jan 2016 20:23:45 +0300 Subject: [PATCH 31/45] Fix 2139 captured spell scroll descriptions --- client/widgets/CArtifactHolder.cpp | 98 ++++++++++-------------------- client/widgets/CComponent.cpp | 19 +++++- client/windows/CTradeWindow.cpp | 10 +-- lib/CArtHandler.cpp | 51 +++++++++++++++- lib/CArtHandler.h | 2 + server/CGameHandler.cpp | 66 +++++++++++--------- 6 files changed, 140 insertions(+), 106 deletions(-) diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index f885ad961..0af6f901a 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -32,7 +32,7 @@ */ CArtPlace::CArtPlace(Point position, const CArtifactInstance * Art): - locked(false), picked(false), marked(false), ourArt(Art) + locked(false), picked(false), marked(false), ourArt(Art) { pos += position; pos.w = pos.h = 44; @@ -180,7 +180,7 @@ void CArtPlace::clickLeft(tribool down, bool previousState) if(srcInBackpack && srcInSameHero) { if(!ourArt //cannot move from backpack to AFTER backpack -> combined with vstd::amin above it will guarantee that dest is at most the last artifact - || ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted + || ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted vstd::advance(ourOwner->commonInfo->dst.slotID, -1); } if(srcInSameHero && ourOwner->commonInfo->dst.slotID == ourOwner->commonInfo->src.slotID) //we came to src == dst @@ -386,70 +386,36 @@ void CArtPlace::setArtifact(const CArtifactInstance *art) image->disable(); text = std::string(); hoverText = CGI->generaltexth->allTexts[507]; + return; + } + + image->enable(); + image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex); + + text = art->getEffectiveDescription(ourOwner->curHero); + + if(art->artType->id == ArtifactID::SPELL_SCROLL) + { + int spellID = art->getGivenSpellID(); + if(spellID >= 0) + { + //add spell component info (used to provide a pic in r-click popup) + baseType = CComponent::spell; + type = spellID; + bonusValue = 0; + } } else { - image->enable(); - image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex); - - std::string artDesc = ourArt->artType->Description(); - if (vstd::contains (artDesc, '{')) - text = artDesc; - else - text = '{' + ourArt->artType->Name() + "}\n\n" + artDesc; //workaround for new artifacts with single name, turns it to H3-style - - if(art->artType->id == ArtifactID::SPELL_SCROLL) - { - // we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll. - // so we want to replace text in [...] with a spell name - // however other language versions don't have name placeholder at all, so we have to be careful - int spellID = art->getGivenSpellID(); - size_t nameStart = text.find_first_of('['); - size_t nameEnd = text.find_first_of(']', nameStart); - if(spellID >= 0) - { - if(nameStart != std::string::npos && nameEnd != std::string::npos) - text = text.replace(nameStart, nameEnd - nameStart + 1, CGI->spellh->objects[spellID]->name); - - //add spell component info (used to provide a pic in r-click popup) - baseType = CComponent::spell; - type = spellID; - bonusValue = 0; - } - } - else - { - baseType = CComponent::artifact; - type = art->artType->id; - bonusValue = 0; - } - if (art->artType->constituents) //display info about components of combined artifact - { - //TODO - } - else if (art->artType->constituentOf.size()) //display info about set - { - std::string artList; - auto combinedArt = art->artType->constituentOf[0]; - text += "\n\n"; - text += "{" + combinedArt->Name() + "}"; - int wornArtifacts = 0; - for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set? - { - artList += "\n" + a->Name(); - if (ourOwner->curHero->hasArt(a->id, true)) - wornArtifacts++; - } - text += " (" + boost::str(boost::format("%d") % wornArtifacts) + " / " + - boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList; - //TODO: fancy colors and fonts for this text - } - - if (locked) // Locks should appear as empty. - hoverText = CGI->generaltexth->allTexts[507]; - else - hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name()); + baseType = CComponent::artifact; + type = art->artType->id; + bonusValue = 0; } + + if (locked) // Locks should appear as empty. + hoverText = CGI->generaltexth->allTexts[507]; + else + hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name()); } void CArtifactsOfHero::SCommonPart::reset() @@ -811,7 +777,7 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact } else if(src.slot >= GameConstants::BACKPACK_START && src.slot < commonInfo->src.slotID && - src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one + src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one { //int fixedSlot = src.hero->getArtPos(commonInfo->src.art); vstd::advance(commonInfo->src.slotID, -1); @@ -825,14 +791,14 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact } updateParentWindow(); - int shift = 0; + int shift = 0; // if(dst.slot >= Arts::BACKPACK_START && dst.slot - Arts::BACKPACK_START < backpackPos) // shift++; // - if(src.slot < GameConstants::BACKPACK_START && dst.slot - GameConstants::BACKPACK_START < backpackPos) + if(src.slot < GameConstants::BACKPACK_START && dst.slot - GameConstants::BACKPACK_START < backpackPos) shift++; if(dst.slot < GameConstants::BACKPACK_START && src.slot - GameConstants::BACKPACK_START < backpackPos) - shift--; + shift--; if( (isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START) || (isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START) ) diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index 41f331539..e841f830c 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -7,6 +7,7 @@ #include "../CMessage.h" #include "../CGameInfo.h" #include "../widgets/Images.h" +#include "../widgets/CArtifactHolder.h" #include "../windows/CAdvmapInterface.h" #include "../../lib/CArtHandler.h" @@ -144,14 +145,26 @@ size_t CComponent::getIndex() std::string CComponent::getDescription() { - switch (compType) + switch(compType) { case primskill: return (subtype < 4)? CGI->generaltexth->arraytxt[2+subtype] //Primary skill : CGI->generaltexth->allTexts[149]; //mana case secskill: return CGI->generaltexth->skillInfoTexts[subtype][val-1]; case resource: return CGI->generaltexth->allTexts[242]; case creature: return ""; - case artifact: return CGI->arth->artifacts[subtype]->Description(); + case artifact: + { + std::unique_ptr art; + if (subtype != ArtifactID::SPELL_SCROLL) + { + art.reset(CArtifactInstance::createNewArtifactInstance(subtype)); + } + else + { + art.reset(CArtifactInstance::createScroll(static_cast(val))); + } + return art->getEffectiveDescription(); + } case experience: return CGI->generaltexth->allTexts[241]; case spell: return CGI->spellh->objects[subtype]->getLevelInfo(val).description; case morale: return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)]; @@ -166,7 +179,7 @@ std::string CComponent::getDescription() std::string CComponent::getSubtitle() { - if (!perDay) + if(!perDay) return getSubtitleInternal(); std::string ret = CGI->generaltexth->allTexts[3]; diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index a601d7e33..dda828c3a 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -269,7 +269,7 @@ void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState) case ARTIFACT_TYPE: case ARTIFACT_PLACEHOLDER: if(id >= 0) - adventureInt->handleRightClick(CGI->arth->artifacts[id]->Description(), down); + adventureInt->handleRightClick(hlp->getEffectiveDescription(), down); break; } } @@ -500,14 +500,14 @@ void CTradeWindow::getPositionsFor(std::vector &poss, bool Left, EType typ int h, w, x, y, dx, dy; int leftToRightOffset; getBaseForPositions(type, dx, dy, x, y, h, w, !Left, leftToRightOffset); - - const std::vector tmp = + + const std::vector tmp = { genRect(h, w, x, y), genRect(h, w, x + dx, y), genRect(h, w, x + 2*dx, y), genRect(h, w, x, y + dy), genRect(h, w, x + dx, y + dy), genRect(h, w, x + 2*dx, y + dy), - genRect(h, w, x + dx, y + 2*dy) + genRect(h, w, x + dx, y + 2*dy) }; - + vstd::concatenate(poss, tmp); if(!Left) diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 4e48b5ff3..80957e0eb 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -738,9 +738,14 @@ std::string CArtifactInstance::nodeName() const } CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s) +{ + return createScroll(s->id); +} + +CArtifactInstance *CArtifactInstance::createScroll(SpellID sid) { auto ret = new CArtifactInstance(VLC->arth->artifacts[ArtifactID::SPELL_SCROLL]); - auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, s->id); + auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, sid); ret->addNewBonus(b); return ret; } @@ -752,6 +757,48 @@ void CArtifactInstance::init() setNodeType(ARTIFACT_INSTANCE); } +std::string CArtifactInstance::getEffectiveDescription( + const CGHeroInstance *hero) const +{ + std::string text = this->artType->Description(); + if (!vstd::contains(text, '{')) + text = '{' + this->artType->Name() + "}\n\n" + text; //workaround for new artifacts with single name, turns it to H3-style + + if(this->artType->id == ArtifactID::SPELL_SCROLL) + { + // we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll. + // so we want to replace text in [...] with a spell name + // however other language versions don't have name placeholder at all, so we have to be careful + int spellID = this->getGivenSpellID(); + size_t nameStart = text.find_first_of('['); + size_t nameEnd = text.find_first_of(']', nameStart); + if(spellID >= 0) + { + if(nameStart != std::string::npos && nameEnd != std::string::npos) + text = text.replace(nameStart, nameEnd - nameStart + 1, VLC->spellh->objects[spellID]->name); + } + } + else if (hero && this->artType->constituentOf.size()) //display info about set + { + std::string artList; + auto combinedArt = this->artType->constituentOf[0]; + text += "\n\n"; + text += "{" + combinedArt->Name() + "}"; + int wornArtifacts = 0; + for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set? + { + artList += "\n" + a->Name(); + if (hero->hasArt(a->id, true)) + wornArtifacts++; + } + text += " (" + boost::str(boost::format("%d") % wornArtifacts) + " / " + + boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList; + //TODO: fancy colors and fonts for this text + } + + return text; +} + ArtifactPosition CArtifactInstance::firstAvailableSlot(const CArtifactSet *h) const { for(auto slot : artType->possibleSlots.at(h->bearerType())) @@ -900,7 +947,7 @@ SpellID CArtifactInstance::getGivenSpellID() const const Bonus * b = getBonusLocalFirst(Selector::type(Bonus::SPELL)); if(!b) { - logGlobal->warnStream() << "Warning: " << nodeName() << " doesn't bear any spell!"; + logGlobal->warnStream() << "Warning: " << nodeName() << " doesn't bear any spell!"; return SpellID::NONE; } return SpellID(b->subtype); diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index d9ffdc46e..a19539c12 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -119,6 +119,7 @@ public: void deserializationFix(); void setType(CArtifact *Art); + std::string getEffectiveDescription(const CGHeroInstance *hero = nullptr) const; ArtifactPosition firstAvailableSlot(const CArtifactSet *h) const; ArtifactPosition firstBackpackSlot(const CArtifactSet *h) const; SpellID getGivenSpellID() const; //to be used with scrolls (and similar arts), -1 if none @@ -143,6 +144,7 @@ public: } static CArtifactInstance *createScroll(const CSpell *s); + static CArtifactInstance *createScroll(SpellID sid); static CArtifactInstance *createNewArtifactInstance(CArtifact *Art); static CArtifactInstance *createNewArtifactInstance(int aid); }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 968c4b571..df0fe9c86 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -471,7 +471,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer const CArmedInstance *bEndArmy2 = gs->curB->sides.at(1).armyObject; const BattleResult::EResult result = battleResult.get()->result; - auto findBattleQuery = [this] () -> std::shared_ptr + auto findBattleQuery = [this]() -> std::shared_ptr { for(auto &q : queries.allQueries()) { @@ -526,66 +526,70 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer } - std::vector arts; //display them in window + std::vector arts; //display them in window - if (result == BattleResult::NORMAL && finishingBattle->winnerHero) + if(result == BattleResult::NORMAL && finishingBattle->winnerHero) { - if (finishingBattle->loserHero) + auto sendMoveArtifact = [&](const CArtifactInstance *art, MoveArtifact *ma) { - auto artifactsWorn = finishingBattle->loserHero->artifactsWorn; //TODO: wrap it into a function, somehow (boost::variant -_-) + arts.push_back(art); + ma->dst = ArtifactLocation(finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); + sendAndApply(ma); + }; + if(finishingBattle->loserHero) + { + //TODO: wrap it into a function, somehow (boost::variant -_-) + auto artifactsWorn = finishingBattle->loserHero->artifactsWorn; for (auto artSlot : artifactsWorn) { MoveArtifact ma; - ma.src = ArtifactLocation (finishingBattle->loserHero, artSlot.first); + ma.src = ArtifactLocation(finishingBattle->loserHero, artSlot.first); const CArtifactInstance * art = ma.src.getArt(); - if (art && !art->artType->isBig() && art->artType->id != ArtifactID::SPELLBOOK) // don't move war machines or locked arts (spellbook) + if(art && !art->artType->isBig() && + art->artType->id != ArtifactID::SPELLBOOK) + // don't move war machines or locked arts (spellbook) { - arts.push_back (art->artType->id); - ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); - sendAndApply(&ma); + sendMoveArtifact(art, &ma); } } - while (!finishingBattle->loserHero->artifactsInBackpack.empty()) + while(!finishingBattle->loserHero->artifactsInBackpack.empty()) { //we assume that no big artifacts can be found MoveArtifact ma; - ma.src = ArtifactLocation (finishingBattle->loserHero, + ma.src = ArtifactLocation(finishingBattle->loserHero, ArtifactPosition(GameConstants::BACKPACK_START)); //backpack automatically shifts arts to beginning const CArtifactInstance * art = ma.src.getArt(); - arts.push_back (art->artType->id); - ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); - sendAndApply(&ma); + if(art->artType->id != ArtifactID::GRAIL) //grail may not be won + { + sendMoveArtifact(art, &ma); + } } - if (finishingBattle->loserHero->commander) //TODO: what if commanders belong to no hero? + if(finishingBattle->loserHero->commander) //TODO: what if commanders belong to no hero? { artifactsWorn = finishingBattle->loserHero->commander->artifactsWorn; - for (auto artSlot : artifactsWorn) + for(auto artSlot : artifactsWorn) { MoveArtifact ma; - ma.src = ArtifactLocation (finishingBattle->loserHero->commander.get(), artSlot.first); + ma.src = ArtifactLocation(finishingBattle->loserHero->commander.get(), artSlot.first); const CArtifactInstance * art = ma.src.getArt(); if (art && !art->artType->isBig()) { - arts.push_back (art->artType->id); - ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); - sendAndApply(&ma); + sendMoveArtifact(art, &ma); } } } } - for (auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks) + for(auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks) { auto artifactsWorn = armySlot.second->artifactsWorn; for (auto artSlot : artifactsWorn) { MoveArtifact ma; - ma.src = ArtifactLocation (armySlot.second, artSlot.first); + ma.src = ArtifactLocation(armySlot.second, artSlot.first); const CArtifactInstance * art = ma.src.getArt(); if (art && !art->artType->isBig()) { - arts.push_back (art->artType->id); - ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); - sendAndApply(&ma); + sendMoveArtifact(art, &ma); } } } @@ -593,23 +597,25 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer sendAndApply(battleResult.data); //after this point casualties objects are destroyed - if (arts.size()) //display loot + if(arts.size()) //display loot { InfoWindow iw; iw.player = finishingBattle->winnerHero->tempOwner; iw.text.addTxt (MetaString::GENERAL_TXT, 30); //You have captured enemy artifact - for (auto id : arts) //TODO; separate function to display loot for various ojects? + for(auto art : arts) //TODO; separate function to display loot for various ojects? { - iw.components.push_back (Component (Component::ARTIFACT, id, 0, 0)); + iw.components.push_back(Component( + Component::ARTIFACT, art->artType->id, + art->artType->id == ArtifactID::SPELL_SCROLL? art->getGivenSpellID() : 0, 0)); if(iw.components.size() >= 14) { sendAndApply(&iw); iw.components.clear(); } } - if (iw.components.size()) + if(iw.components.size()) { sendAndApply(&iw); } From a74fbff34ffe761f4c48a8909c447dba20a77a17 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Wed, 20 Jan 2016 14:22:12 +0300 Subject: [PATCH 32/45] Fix 2076 Grail removal --- lib/NetPacksLib.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 7a5fe5c04..fd39d78d9 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -362,6 +362,10 @@ DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs ) p->heroes -= h; h->detachFrom(h->whereShouldBeAttached(gs)); h->tempOwner = PlayerColor::NEUTRAL; //no one owns beaten hero + vstd::erase_if(h->artifactsInBackpack, [](const ArtSlotInfo& asi) + { + return asi.artifact->artType->id == ArtifactID::GRAIL; + }); if(h->visitedTown) { From 22eb71de0325d6ff672f0ac9c918befd6a7f9dfa Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Mon, 25 Jan 2016 14:14:32 +0300 Subject: [PATCH 33/45] Remove this-> occurrences in new code --- lib/CArtHandler.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 80957e0eb..464f8d923 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -760,16 +760,16 @@ void CArtifactInstance::init() std::string CArtifactInstance::getEffectiveDescription( const CGHeroInstance *hero) const { - std::string text = this->artType->Description(); + std::string text = artType->Description(); if (!vstd::contains(text, '{')) - text = '{' + this->artType->Name() + "}\n\n" + text; //workaround for new artifacts with single name, turns it to H3-style + text = '{' + artType->Name() + "}\n\n" + text; //workaround for new artifacts with single name, turns it to H3-style - if(this->artType->id == ArtifactID::SPELL_SCROLL) + if(artType->id == ArtifactID::SPELL_SCROLL) { // we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll. // so we want to replace text in [...] with a spell name // however other language versions don't have name placeholder at all, so we have to be careful - int spellID = this->getGivenSpellID(); + int spellID = getGivenSpellID(); size_t nameStart = text.find_first_of('['); size_t nameEnd = text.find_first_of(']', nameStart); if(spellID >= 0) @@ -778,10 +778,10 @@ std::string CArtifactInstance::getEffectiveDescription( text = text.replace(nameStart, nameEnd - nameStart + 1, VLC->spellh->objects[spellID]->name); } } - else if (hero && this->artType->constituentOf.size()) //display info about set + else if (hero && artType->constituentOf.size()) //display info about set { std::string artList; - auto combinedArt = this->artType->constituentOf[0]; + auto combinedArt = artType->constituentOf[0]; text += "\n\n"; text += "{" + combinedArt->Name() + "}"; int wornArtifacts = 0; From 6849ff846c4afe3c2c46941148fea4819dd46126 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Mon, 25 Jan 2016 15:36:34 +0300 Subject: [PATCH 34/45] Fix memory corruption with a draw; race in setBattleResult() --- server/CGameHandler.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 75802cb9c..e9fe4ac3e 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -124,6 +124,11 @@ static inline double distance(int3 a, int3 b) } static void giveExp(BattleResult &r) { + if(r.winner > 1) + { + // draw + return; + } r.exp[0] = 0; r.exp[1] = 0; for(auto i = r.casualties[!r.winner].begin(); i!=r.casualties[!r.winner].end(); i++) @@ -5679,16 +5684,18 @@ void CGameHandler::giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact void CGameHandler::setBattleResult(BattleResult::EResult resultType, int victoriusSide) { - if(battleResult.get()) + boost::unique_lock guard(battleResult.mx); + if(battleResult.data) { - complain("There is already set result?"); + complain((boost::format("The battle result has been already set (to %d, asked to %d)") + % battleResult.data->result % resultType).str()); return; } auto br = new BattleResult; br->result = resultType; br->winner = victoriusSide; //surrendering side loses gs->curB->calculateCasualties(br->casualties); - battleResult.set(br); + battleResult.data = br; } void CGameHandler::commitPackage( CPackForClient *pack ) From 97a8874ed73cb13529b6222dbd9ecc14f8a42130 Mon Sep 17 00:00:00 2001 From: Arseniy Shestakov Date: Tue, 26 Jan 2016 08:41:09 +0300 Subject: [PATCH 35/45] CGCreature: fix crash on draw Also according to H3 behaviour if there artifact monster guarded it's will be lost on draw. --- lib/mapObjects/CGPandoraBox.cpp | 8 ++++---- lib/mapObjects/MiscObjects.cpp | 20 ++++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index f512716d6..b21fc3ced 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -295,10 +295,10 @@ void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int val, int nega void CGPandoraBox::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const { - if(result.winner) - return; - - giveContentsUpToExp(hero); + if(result.winner == 0) + { + giveContentsUpToExp(hero); + } } void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 51a293ed3..5f1014670 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -453,22 +453,26 @@ void CGCreature::flee( const CGHeroInstance * h ) const void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const { - - if(result.winner==0) + if(result.winner == 0) { giveReward(hero); cb->removeObject(this); } + else if(result.winner > 1) // draw + { + // guarded reward is lost forever on draw + cb->removeObject(this); + } else { //merge stacks into one TSlots::const_iterator i; CCreature * cre = VLC->creh->creatures[formation.basicType]; - for (i = stacks.begin(); i != stacks.end(); i++) + for(i = stacks.begin(); i != stacks.end(); i++) { - if (cre->isMyUpgrade(i->second->type)) + if(cre->isMyUpgrade(i->second->type)) { - cb->changeStackType (StackLocation(this, i->first), cre); //un-upgrade creatures + cb->changeStackType(StackLocation(this, i->first), cre); //un-upgrade creatures } } @@ -476,16 +480,16 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult & if(!hasStackAtSlot(SlotID(0))) cb->moveStack(StackLocation(this, stacks.begin()->first), StackLocation(this, SlotID(0)), stacks.begin()->second->count); - while (stacks.size() > 1) //hopefully that's enough + while(stacks.size() > 1) //hopefully that's enough { // TODO it's either overcomplicated (if we assume there'll be only one stack) or buggy (if we allow multiple stacks... but that'll also cause troubles elsewhere) i = stacks.end(); i--; SlotID slot = getSlotFor(i->second->type); - if (slot == i->first) //no reason to move stack to its own slot + if(slot == i->first) //no reason to move stack to its own slot break; else - cb->moveStack (StackLocation(this, i->first), StackLocation(this, slot), i->second->count); + cb->moveStack(StackLocation(this, i->first), StackLocation(this, slot), i->second->count); } cb->setObjProperty(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties From 40cb48d65edf7197c0542883cd3e96b278958530 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Tue, 26 Jan 2016 09:37:55 +0300 Subject: [PATCH 36/45] Replace std::remove_if with vstd::erase_if --- client/battle/CBattleInterface.cpp | 30 +++++++++++++++--------------- lib/CGameState.cpp | 4 ++-- lib/mapObjects/JsonRandom.cpp | 4 ++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 4c2725d9f..83bf8fde7 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -1017,7 +1017,7 @@ void CBattleInterface::stackRemoved(int stackID) setActiveStack(nullptr); } } - + delete creAnims[stackID]; creAnims.erase(stackID); creDir.erase(stackID); @@ -1201,18 +1201,18 @@ void CBattleInterface::stackIsCatapulting(const CatapultAttack & ca) for(auto attackInfo : ca.attackedParts) { addNewAnim(new CShootingAnimation(this, stack, attackInfo.destinationTile, nullptr, true, attackInfo.damageDealt)); - } + } } else { - //no attacker stack, assume spell-related (earthquake) - only hit animation + //no attacker stack, assume spell-related (earthquake) - only hit animation for(auto attackInfo : ca.attackedParts) { Point destPos = CClickableHex::getXYUnitAnim(attackInfo.destinationTile, nullptr, this) + Point(99, 120); - + addNewAnim(new CSpellEffectAnimation(this, "SGEXPL.DEF", destPos.x, destPos.y)); } - } + } waitForAnims(); @@ -1323,9 +1323,9 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc ) //displaying message in console std::vector logLines; - + spell.prepareBattleLog(curInt->cb.get(), sc, logLines); - + for(auto line : logLines) console->addText(line); @@ -1427,15 +1427,15 @@ void CBattleInterface::displayEffect(ui32 effect, int destTile, bool areaEffect) } void CBattleInterface::displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile, bool areaEffect) -{ +{ if(animation.pause > 0) { - addNewAnim(new CDummyAnimation(this, animation.pause)); + addNewAnim(new CDummyAnimation(this, animation.pause)); } else { - addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM)); - } + addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM)); + } } void CBattleInterface::displaySpellCast(SpellID spellID, BattleHex destinationTile, bool areaEffect) @@ -1444,11 +1444,11 @@ void CBattleInterface::displaySpellCast(SpellID spellID, BattleHex destinationTi if(spell == nullptr) return; - + for(const CSpell::TAnimation & animation : spell->animationInfo.cast) { displaySpellAnimation(animation, destinationTile, areaEffect); - } + } } void CBattleInterface::displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect) @@ -1933,7 +1933,7 @@ void CBattleInterface::bTacticNextStack(const CStack *current /*= nullptr*/) waitForAnims(); TStacks stacksOfMine = tacticianInterface->cb->battleGetStacks(CBattleCallback::ONLY_MINE); - stacksOfMine.erase(std::remove_if(stacksOfMine.begin(), stacksOfMine.end(), &immobile), stacksOfMine.end()); + vstd::erase_if(stacksOfMine, &immobile); auto it = vstd::find(stacksOfMine, current); if(it != stacksOfMine.end() && ++it != stacksOfMine.end()) stackActivated(*it); @@ -2072,7 +2072,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) case ANY_CREATURE: if (shere && shere->alive() && isCastingPossibleHere (sactive, shere, myNumber)) legalAction = true; - break; + break; case HOSTILE_CREATURE_SPELL: if (shere && shere->alive() && !ourStack && isCastingPossibleHere (sactive, shere, myNumber)) legalAction = true; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 862e5db42..50b1c8afe 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -1219,8 +1219,8 @@ 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) { - crossoverHeroes.heroesFromAnyPreviousScenarios.erase(range::remove_if(crossoverHeroes.heroesFromAnyPreviousScenarios, - CGObjectInstanceBySubIdFinder(hero)), crossoverHeroes.heroesFromAnyPreviousScenarios.end()); + vstd::erase_if(crossoverHeroes.heroesFromAnyPreviousScenarios, + CGObjectInstanceBySubIdFinder(hero)); } // now add heroes which completed the scenario diff --git a/lib/mapObjects/JsonRandom.cpp b/lib/mapObjects/JsonRandom.cpp index a64271c1e..36fdf59a3 100644 --- a/lib/mapObjects/JsonRandom.cpp +++ b/lib/mapObjects/JsonRandom.cpp @@ -133,10 +133,10 @@ namespace JsonRandom if (value["type"].getType() == JsonNode::DATA_STRING) return SpellID(VLC->modh->identifiers.getIdentifier("spell", value["type"]).get()); - spells.erase(std::remove_if(spells.begin(), spells.end(), [=](SpellID spell) + vstd::erase_if(spells, [=](SpellID spell) { return VLC->spellh->objects[spell]->level != si32(value["level"].Float()); - }), spells.end()); + }); return SpellID(*RandomGeneratorUtil::nextItem(spells, rng)); } From 7772b6de741c4f00a9248355fd2508fd3c719e44 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Tue, 26 Jan 2016 22:24:38 +0300 Subject: [PATCH 37/45] Fix 981 reset hero on hiring after retreat/surrender --- lib/NetPacksLib.cpp | 11 +++++- lib/mapObjects/CGHeroInstance.cpp | 61 ++++++++++++++++--------------- lib/mapObjects/CGHeroInstance.h | 22 +++++++---- server/CGameHandler.cpp | 23 ++++++++---- 4 files changed, 70 insertions(+), 47 deletions(-) diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index fd39d78d9..46d515a78 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -594,7 +594,11 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs ) h->setOwner(player); h->pos = tile; - h->movement = h->maxMovePoints(true); + bool fresh = !h->isInitialized(); + if(fresh) + { // this is a fresh hero who hasn't appeared yet + h->movement = h->maxMovePoints(true); + } gs->hpool.heroesPool.erase(hid); if(h->id == ObjectInstanceID()) @@ -608,7 +612,10 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs ) gs->map->heroesOnMap.push_back(h); p->heroes.push_back(h); h->attachTo(p); - h->initObj(); + if(fresh) + { + h->initObj(); + } gs->map->addBlockVisTiles(h); if(t) diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 2299c42a0..13a25f9db 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -220,7 +220,9 @@ CGHeroInstance::CGHeroInstance() setNodeType(HERO); ID = Obj::HERO; tacticFormationEnabled = inTownGarrison = false; - mana = movement = portrait = -1; + mana = UNINITIALIZED_MANA; + movement = UNINITIALIZED_MOVEMENT; + portrait = UNINITIALIZED_PORTRAIT; isStanding = true; moveDir = 4; level = 1; @@ -868,18 +870,18 @@ TExpType CGHeroInstance::calculateXp(TExpType exp) const ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool) const { si16 skill = -1; //skill level - + spell->forEachSchool([&, this](const SpellSchoolInfo & cnf, bool & stop) { int thisSchool = std::max(getSecSkillLevel(cnf.skill), valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << ((ui8)cnf.id))); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies) - if(thisSchool > skill) - { - skill = thisSchool; - if(outSelectedSchool) - *outSelectedSchool = (ui8)cnf.id; - } + if(thisSchool > skill) + { + skill = thisSchool; + if(outSelectedSchool) + *outSelectedSchool = (ui8)cnf.id; + } }); - + vstd::amax(skill, valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 0)); //any school bonus vstd::amax(skill, valOfBonuses(Bonus::SPELL, spell->id.toEnum())); //given by artifact or other effect @@ -904,7 +906,7 @@ ui32 CGHeroInstance::getSpellBonus(const CSpell * spell, ui32 base, const CStack if (affectedStack && affectedStack->getCreature()->level) //Hero specials like Solmyr, Deemer base *= (100. + ((valOfBonuses(Bonus::SPECIAL_SPELL_LEV, spell->id.toEnum()) * level) / affectedStack->getCreature()->level)) / 100.0; - return base; + return base; } int CGHeroInstance::getEffectLevel(const CSpell * spell) const @@ -912,7 +914,7 @@ int CGHeroInstance::getEffectLevel(const CSpell * spell) const if(hasBonusOfType(Bonus::MAXED_SPELL, spell->id)) return 3;//todo: recheck specialty from where this bonus is. possible bug else - return getSpellSchoolLevel(spell); + return getSpellSchoolLevel(spell); } int CGHeroInstance::getEffectPower(const CSpell * spell) const @@ -922,7 +924,7 @@ int CGHeroInstance::getEffectPower(const CSpell * spell) const int CGHeroInstance::getEnchantPower(const CSpell * spell) const { - return getPrimSkillLevel(PrimarySkill::SPELL_POWER) + valOfBonuses(Bonus::SPELL_DURATION); + return getPrimSkillLevel(PrimarySkill::SPELL_POWER) + valOfBonuses(Bonus::SPELL_DURATION); } int CGHeroInstance::getEffectValue(const CSpell * spell) const @@ -1107,8 +1109,8 @@ void CGHeroInstance::getOutOffsets(std::vector &offsets) const { // FIXME: Offsets need to be fixed once we get rid of convertPosition // Check issue 515 for details - offsets = - { + offsets = + { int3(-1,1,0), int3(-1,-1,0), int3(-2,0,0), int3(0,0,0), int3(0,1,0), int3(-2,1,0), int3(0,-1,0), int3(-2,-1,0) }; } @@ -1436,20 +1438,19 @@ void CGHeroInstance::levelUpAutomatically() bool CGHeroInstance::hasVisions(const CGObjectInstance * target, const int subtype) const { //VISIONS spell support - - const std::string cached = boost::to_string((boost::format("type_%d__subtype_%d") % Bonus::VISIONS % subtype)); - - const int visionsMultiplier = valOfBonuses(Selector::typeSubtype(Bonus::VISIONS,subtype), cached); - - int visionsRange = visionsMultiplier * getPrimSkillLevel(PrimarySkill::SPELL_POWER); - - if (visionsMultiplier > 0) - vstd::amax(visionsRange, 3); //minimum range is 3 tiles, but only if VISIONS bonus present - - const int distance = target->pos.dist2d(getPosition(false)); - - //logGlobal->debug(boost::to_string(boost::format("Visions: dist %d, mult %d, range %d") % distance % visionsMultiplier % visionsRange)); - - return (distance < visionsRange) && (target->pos.z == pos.z); -} + const std::string cached = boost::to_string((boost::format("type_%d__subtype_%d") % Bonus::VISIONS % subtype)); + + const int visionsMultiplier = valOfBonuses(Selector::typeSubtype(Bonus::VISIONS,subtype), cached); + + int visionsRange = visionsMultiplier * getPrimSkillLevel(PrimarySkill::SPELL_POWER); + + if (visionsMultiplier > 0) + vstd::amax(visionsRange, 3); //minimum range is 3 tiles, but only if VISIONS bonus present + + const int distance = target->pos.dist2d(getPosition(false)); + + //logGlobal->debug(boost::to_string(boost::format("Visions: dist %d, mult %d, range %d") % distance % visionsMultiplier % visionsRange)); + + return (distance < visionsRange) && (target->pos.z == pos.z); +} diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index a6cc59cb1..0e902035c 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -64,6 +64,9 @@ public: ConstTransitivePtr commander; const CGBoat *boat; //set to CGBoat when sailing + static constexpr ui32 UNINITIALIZED_PORTRAIT = -1; + static constexpr ui32 UNINITIALIZED_MANA = -1; + static constexpr ui32 UNINITIALIZED_MOVEMENT = -1; //std::vector artifacts; //hero's artifacts from bag //std::map artifWorn; //map; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5 @@ -124,6 +127,11 @@ public: } } skillsInfo; + inline bool isInitialized() const + { // has this hero been on the map at least once? + return movement == UNINITIALIZED_MOVEMENT && mana == UNINITIALIZED_MANA; + } + //int3 getSightCenter() const; //"center" tile from which the sight distance is calculated int getSightRadious() const override; //sight distance (should be used if player-owned structure) ////////////////////////////////////////////////////////////////////////// @@ -177,7 +185,7 @@ public: double getHeroStrength() const; // includes fighting and magic strength ui64 getTotalStrength() const; // includes fighting strength and army strength TExpType calculateXp(TExpType exp) const; //apply learning skill - + bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const; void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const; @@ -201,23 +209,23 @@ public: void Updatespecialty(); void recreateSecondarySkillsBonuses(); void updateSkill(SecondarySkill which, int val); - + bool hasVisions(const CGObjectInstance * target, const int subtype) const; CGHeroInstance(); virtual ~CGHeroInstance(); - + ///ArtBearer ArtBearer::ArtBearer bearerType() const override; ///IBonusBearer CBonusSystemNode *whereShouldBeAttached(CGameState *gs) override; std::string nodeName() const override; - + ///ISpellCaster ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const override; ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override; - + ///default spell school level for effect calculation int getEffectLevel(const CSpell * spell) const override; @@ -229,9 +237,9 @@ public: ///damage/heal override(ignores spell configuration, effect level and effect power) int getEffectValue(const CSpell * spell) const override; - + const PlayerColor getOwner() const override; - + void deserializationFix(); void initObj() override; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index df0fe9c86..ba585c7dd 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3328,23 +3328,27 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl // || (getHeroCount(player, false) >= GameConstants::MAX_HEROES_PER_PLAYER && complain("Cannot hire hero, only 8 wandering heroes are allowed!"))) if((p->resources.at(Res::GOLD) < GameConstants::HERO_GOLD_COST && complain("Not enough gold for buying hero!")) || ((!t) && (getHeroCount(player, false) >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER && complain("Cannot hire hero, too many wandering heroes already!"))) - || ((t) && (getHeroCount(player, true) >= VLC->modh->settings.MAX_HEROES_AVAILABLE_PER_PLAYER && complain("Cannot hire hero, too many heroes garrizoned and wandering already!"))) ) - - return false; + || ((t) && (getHeroCount(player, true) >= VLC->modh->settings.MAX_HEROES_AVAILABLE_PER_PLAYER && complain("Cannot hire hero, too many heroes garrizoned and wandering already!"))) ) + { + return false; + } if(t) //tavern in town { - if( (!t->hasBuilt(BuildingID::TAVERN) && complain("No tavern!")) - || (t->visitingHero && complain("There is visiting hero - no place!"))) + if((!t->hasBuilt(BuildingID::TAVERN) && complain("No tavern!")) + || (t->visitingHero && complain("There is visiting hero - no place!"))) + { return false; + } } else if(obj->ID == Obj::TAVERN) { - if(getTile(obj->visitablePos())->visitableObjects.back() != obj && complain("Tavern entry must be unoccupied!")) + if(getTile(obj->visitablePos())->visitableObjects.back() != obj && complain("Tavern entry must be unoccupied!")) + { return false; + } } - const CGHeroInstance *nh = p->availableHeroes.at(hid); if (!nh) { @@ -3359,13 +3363,14 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl hr.tile = obj->visitablePos() + nh->getVisitableOffset(); sendAndApply(&hr); - std::map > pool = gs->unusedHeroesFromPool(); const CGHeroInstance *theOtherHero = p->availableHeroes.at(!hid); const CGHeroInstance *newHero = nullptr; if (theOtherHero) //on XXL maps all heroes can be imprisoned :( + { newHero = gs->hpool.pickHeroFor(false, player, getNativeTown(player), pool, gs->getRandomGenerator(), theOtherHero->type->heroClass); + } SetAvailableHeroes sah; sah.player = player; @@ -3377,7 +3382,9 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl sah.army[hid].setCreature(SlotID(0), newHero->type->initialArmy[0].creature, 1); } else + { sah.hid[hid] = -1; + } sah.hid[!hid] = theOtherHero ? theOtherHero->subID : -1; sendAndApply(&sah); From 92dd194c97b51c323c38b0fa79c31036153a7d1b Mon Sep 17 00:00:00 2001 From: Zyx-2000 Date: Tue, 26 Jan 2016 22:42:15 +0100 Subject: [PATCH 38/45] logger now supports unicode file paths --- lib/logging/CLogger.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/logging/CLogger.h b/lib/logging/CLogger.h index e4ceaf866..2318cf4f3 100644 --- a/lib/logging/CLogger.h +++ b/lib/logging/CLogger.h @@ -12,6 +12,7 @@ #pragma once #include "../CConsoleHandler.h" +#include "../filesystem/FileStream.h" class CLogger; struct LogRecord; @@ -147,7 +148,7 @@ private: /// Macros for tracing the control flow of the application conveniently. If the LOG_TRACE macro is used it should be /// the first statement in the function. Logging traces via this macro have almost no impact when the trace is disabled. -/// +/// #define RAII_TRACE(logger, onEntry, onLeave) \ std::unique_ptr ctl00; \ if(logger->isTraceEnabled()) \ @@ -217,7 +218,7 @@ public: CLogFormatter(CLogFormatter && move); CLogFormatter(const std::string & pattern); - + CLogFormatter & operator=(const CLogFormatter & copy); CLogFormatter & operator=(CLogFormatter && move); @@ -302,7 +303,7 @@ public: void write(const LogRecord & record) override; private: - boost::filesystem::ofstream file; + FileStream file; CLogFormatter formatter; mutable boost::mutex mx; }; From f6679bba509dda46100636569b1fa225417c98a0 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Wed, 27 Jan 2016 09:44:04 +0300 Subject: [PATCH 39/45] Fix CGHeroInstance::isInitialized implementation --- lib/mapObjects/CGHeroInstance.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 0e902035c..41ccbcdda 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -129,7 +129,7 @@ public: inline bool isInitialized() const { // has this hero been on the map at least once? - return movement == UNINITIALIZED_MOVEMENT && mana == UNINITIALIZED_MANA; + return movement != UNINITIALIZED_MOVEMENT && mana != UNINITIALIZED_MANA; } //int3 getSightCenter() const; //"center" tile from which the sight distance is calculated From 94ecc3f52035196177c0ec46a4b5f43231bdf283 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Wed, 27 Jan 2016 10:13:33 +0100 Subject: [PATCH 40/45] Compilation fix. MVS 2013 doesn't support constexpr and it's not needed either. --- lib/mapObjects/CGHeroInstance.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 41ccbcdda..196c06af3 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -64,9 +64,9 @@ public: ConstTransitivePtr commander; const CGBoat *boat; //set to CGBoat when sailing - static constexpr ui32 UNINITIALIZED_PORTRAIT = -1; - static constexpr ui32 UNINITIALIZED_MANA = -1; - static constexpr ui32 UNINITIALIZED_MOVEMENT = -1; + static const ui32 UNINITIALIZED_PORTRAIT = -1; + static const ui32 UNINITIALIZED_MANA = -1; + static const ui32 UNINITIALIZED_MOVEMENT = -1; //std::vector artifacts; //hero's artifacts from bag //std::map artifWorn; //map; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5 From bffdc2813db254a3d21b9523d84002ab96f67105 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Wed, 27 Jan 2016 15:21:29 +0300 Subject: [PATCH 41/45] revert some project changes --- AI/VCAI/VCAI.cbp | 1 - client/VCMI_client.cbp | 1 - lib/VCMI_lib.cbp | 1 - server/VCMI_server.cbp | 1 - vcmi.workspace | 1 - 5 files changed, 5 deletions(-) diff --git a/AI/VCAI/VCAI.cbp b/AI/VCAI/VCAI.cbp index d5b20f7b4..c30e9356c 100644 --- a/AI/VCAI/VCAI.cbp +++ b/AI/VCAI/VCAI.cbp @@ -86,7 +86,6 @@ - diff --git a/client/VCMI_client.cbp b/client/VCMI_client.cbp index 3a2d36535..dff43308e 100644 --- a/client/VCMI_client.cbp +++ b/client/VCMI_client.cbp @@ -127,7 +127,6 @@ - diff --git a/lib/VCMI_lib.cbp b/lib/VCMI_lib.cbp index da84a74df..093f30e7c 100644 --- a/lib/VCMI_lib.cbp +++ b/lib/VCMI_lib.cbp @@ -201,7 +201,6 @@ - diff --git a/server/VCMI_server.cbp b/server/VCMI_server.cbp index c403a5a91..c13d7031f 100644 --- a/server/VCMI_server.cbp +++ b/server/VCMI_server.cbp @@ -98,7 +98,6 @@ - diff --git a/vcmi.workspace b/vcmi.workspace index 539655b1d..674989792 100644 --- a/vcmi.workspace +++ b/vcmi.workspace @@ -38,6 +38,5 @@ - From 36eaa399e7fc5c7a0a8c69e35ec6882138f8faf5 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Wed, 27 Jan 2016 11:38:35 +0300 Subject: [PATCH 42/45] Add hardcodedFeature to switch winner's retreating with no troops --- config/defaultMods.json | 3 ++- lib/CModHandler.cpp | 33 +++++++++++++++++++++----- lib/CModHandler.h | 16 ++++++++++--- lib/Connection.h | 2 +- lib/JsonNode.cpp | 13 ++++++++-- lib/JsonNode.h | 19 ++++++++------- lib/filesystem/AdapterLoaders.cpp | 14 +++++++++++ lib/filesystem/AdapterLoaders.h | 14 ++++------- lib/filesystem/ISimpleResourceLoader.h | 16 +++++++++++++ server/CGameHandler.cpp | 23 ++++++++++-------- 10 files changed, 111 insertions(+), 42 deletions(-) diff --git a/config/defaultMods.json b/config/defaultMods.json index 6413b79af..e84c877f8 100644 --- a/config/defaultMods.json +++ b/config/defaultMods.json @@ -23,7 +23,8 @@ "ALL_CREATURES_GET_DOUBLE_MONTHS" : false, "NEGATIVE_LUCK" : false, "MAX_HEROES_AVAILABLE_PER_PLAYER" : 16, - "MAX_HEROES_ON_MAP_PER_PLAYER" : 8 + "MAX_HEROES_ON_MAP_PER_PLAYER" : 8, + "WINNING_HERO_WITH_NO_TROOPS_RETREATS": true }, "modules": diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index 571d5c10b..5d5095c5f 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -101,9 +101,9 @@ void CIdentifierStorage::requestIdentifier(std::string scope, std::string type, void CIdentifierStorage::requestIdentifier(std::string scope, std::string fullName, const std::function& callback) { - auto scopeAndFullName = splitString(fullName, ':'); - auto typeAndName = splitString(scopeAndFullName.second, '.'); - + auto scopeAndFullName = splitString(fullName, ':'); + auto typeAndName = splitString(scopeAndFullName.second, '.'); + requestIdentifier(ObjectCallback(scope, scopeAndFullName.first, typeAndName.first, typeAndName.second, callback, false)); } @@ -331,11 +331,11 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali { ModInfo & modInfo = modData[modName]; bool result = true; - + auto performValidate = [&,this](JsonNode & data, const std::string & name){ handler->beforeValidate(data); if (validate) - result &= JsonUtils::validate(data, "vcmi:" + objectName, name); + result &= JsonUtils::validate(data, "vcmi:" + objectName, name); }; // apply patches @@ -355,7 +355,7 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali if (originalData.size() > index) { JsonUtils::merge(originalData[index], data); - + performValidate(originalData[index],name); handler->loadObject(modName, name, originalData[index], index); @@ -550,21 +550,42 @@ CModHandler::CModHandler() void CModHandler::loadConfigFromFile (std::string name) { + std::string paths; + for(auto& p : CResourceHandler::get()->getResourceNames(ResourceID("config/" + name))) + { + paths += p + ", "; + } + paths = paths.substr(0, paths.size() - 2); + logGlobal->debugStream() << "Loading hardcoded features settings from [" << paths << "], result:"; settings.data = JsonUtils::assembleFromFiles("config/" + name); const JsonNode & hardcodedFeatures = settings.data["hardcodedFeatures"]; settings.MAX_HEROES_AVAILABLE_PER_PLAYER = hardcodedFeatures["MAX_HEROES_AVAILABLE_PER_PLAYER"].Float(); + logGlobal->debugStream() << "\tMAX_HEROES_AVAILABLE_PER_PLAYER\t" << settings.MAX_HEROES_AVAILABLE_PER_PLAYER; settings.MAX_HEROES_ON_MAP_PER_PLAYER = hardcodedFeatures["MAX_HEROES_ON_MAP_PER_PLAYER"].Float(); + logGlobal->debugStream() << "\tMAX_HEROES_ON_MAP_PER_PLAYER\t" << settings.MAX_HEROES_ON_MAP_PER_PLAYER; settings.CREEP_SIZE = hardcodedFeatures["CREEP_SIZE"].Float(); + logGlobal->debugStream() << "\tCREEP_SIZE\t" << settings.CREEP_SIZE; settings.WEEKLY_GROWTH = hardcodedFeatures["WEEKLY_GROWTH_PERCENT"].Float(); + logGlobal->debugStream() << "\tWEEKLY_GROWTH\t" << settings.WEEKLY_GROWTH; settings.NEUTRAL_STACK_EXP = hardcodedFeatures["NEUTRAL_STACK_EXP_DAILY"].Float(); + logGlobal->debugStream() << "\tNEUTRAL_STACK_EXP\t" << settings.NEUTRAL_STACK_EXP; settings.MAX_BUILDING_PER_TURN = hardcodedFeatures["MAX_BUILDING_PER_TURN"].Float(); + logGlobal->debugStream() << "\tMAX_BUILDING_PER_TURN\t" << settings.MAX_BUILDING_PER_TURN; settings.DWELLINGS_ACCUMULATE_CREATURES = hardcodedFeatures["DWELLINGS_ACCUMULATE_CREATURES"].Bool(); + logGlobal->debugStream() << "\tDWELLINGS_ACCUMULATE_CREATURES\t" << settings.DWELLINGS_ACCUMULATE_CREATURES; settings.ALL_CREATURES_GET_DOUBLE_MONTHS = hardcodedFeatures["ALL_CREATURES_GET_DOUBLE_MONTHS"].Bool(); + logGlobal->debugStream() << "\tALL_CREATURES_GET_DOUBLE_MONTHS\t" << settings.ALL_CREATURES_GET_DOUBLE_MONTHS; + settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS = hardcodedFeatures["WINNING_HERO_WITH_NO_TROOPS_RETREATS"].Bool(); + logGlobal->debugStream() << "\tWINNING_HERO_WITH_NO_TROOPS_RETREATS\t" << settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS; const JsonNode & gameModules = settings.data["modules"]; modules.STACK_EXP = gameModules["STACK_EXPERIENCE"].Bool(); + logGlobal->debugStream() << "\tSTACK_EXP\t" << modules.STACK_EXP; modules.STACK_ARTIFACT = gameModules["STACK_ARTIFACTS"].Bool(); + logGlobal->debugStream() << "\tSTACK_ARTIFACT\t" << modules.STACK_ARTIFACT; modules.COMMANDERS = gameModules["COMMANDERS"].Bool(); + logGlobal->debugStream() << "\tCOMMANDERS\t" << modules.COMMANDERS; modules.MITHRIL = gameModules["MITHRIL"].Bool(); + logGlobal->debugStream() << "\tMITHRIL\t" << modules.MITHRIL; } // currentList is passed by value to get current list of depending mods diff --git a/lib/CModHandler.h b/lib/CModHandler.h index 7d4f00fe5..b74918ca7 100644 --- a/lib/CModHandler.h +++ b/lib/CModHandler.h @@ -73,7 +73,7 @@ public: /// Function callback will be called during ID resolution phase of loading void requestIdentifier(std::string scope, std::string type, std::string name, const std::function & callback); ///fullName = [remoteScope:]type.name - void requestIdentifier(std::string scope, std::string fullName, const std::function & callback); + void requestIdentifier(std::string scope, std::string fullName, const std::function & callback); void requestIdentifier(std::string type, const JsonNode & name, const std::function & callback); void requestIdentifier(const JsonNode & name, const std::function & callback); @@ -253,17 +253,27 @@ public: int CREEP_SIZE; // neutral stacks won't grow beyond this number int WEEKLY_GROWTH; //percent - int NEUTRAL_STACK_EXP; + int NEUTRAL_STACK_EXP; int MAX_BUILDING_PER_TURN; bool DWELLINGS_ACCUMULATE_CREATURES; bool ALL_CREATURES_GET_DOUBLE_MONTHS; int MAX_HEROES_AVAILABLE_PER_PLAYER; int MAX_HEROES_ON_MAP_PER_PLAYER; + bool WINNING_HERO_WITH_NO_TROOPS_RETREATS; template void serialize(Handler &h, const int version) { h & data & CREEP_SIZE & WEEKLY_GROWTH & NEUTRAL_STACK_EXP & MAX_BUILDING_PER_TURN; - h & DWELLINGS_ACCUMULATE_CREATURES & ALL_CREATURES_GET_DOUBLE_MONTHS & MAX_HEROES_AVAILABLE_PER_PLAYER & MAX_HEROES_ON_MAP_PER_PLAYER; + h & DWELLINGS_ACCUMULATE_CREATURES & ALL_CREATURES_GET_DOUBLE_MONTHS & + MAX_HEROES_AVAILABLE_PER_PLAYER & MAX_HEROES_ON_MAP_PER_PLAYER; + if(version >= 756) + { + h & WINNING_HERO_WITH_NO_TROOPS_RETREATS; + } + else if(!h.saving) + { + WINNING_HERO_WITH_NO_TROOPS_RETREATS = true; + } } } settings; diff --git a/lib/Connection.h b/lib/Connection.h index 259b9f23f..00a16afac 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -27,7 +27,7 @@ #include "mapping/CCampaignHandler.h" //for CCampaignState #include "rmg/CMapGenerator.h" // for CMapGenOptions -const ui32 version = 755; +const ui32 version = 756; const ui32 minSupportedVersion = 753; class CISer; diff --git a/lib/JsonNode.cpp b/lib/JsonNode.cpp index c9ea181b2..8128efcbc 100644 --- a/lib/JsonNode.cpp +++ b/lib/JsonNode.cpp @@ -56,6 +56,15 @@ JsonNode::JsonNode(ResourceID && fileURI): *this = parser.parse(fileURI.getName()); } +JsonNode::JsonNode(const ResourceID & fileURI): + type(DATA_NULL) +{ + auto file = CResourceHandler::get()->load(fileURI)->readAll(); + + JsonParser parser(reinterpret_cast(file.first.get()), file.second); + *this = parser.parse(fileURI.getName()); +} + JsonNode::JsonNode(ResourceID && fileURI, bool &isValidSyntax): type(DATA_NULL) { @@ -328,7 +337,7 @@ void JsonUtils::parseTypedBonusShort(const JsonVector& source, Bonus *dest) resolveIdentifier(source[2],dest->subtype); dest->additionalInfo = source[3].Float(); dest->duration = Bonus::PERMANENT; //TODO: handle flags (as integer) - dest->turnsRemain = 0; + dest->turnsRemain = 0; } @@ -343,7 +352,7 @@ Bonus * JsonUtils::parseBonus (const JsonVector &ability_vec) //TODO: merge with return b; } b->type = it->second; - + parseTypedBonusShort(ability_vec, b); return b; } diff --git a/lib/JsonNode.h b/lib/JsonNode.h index dfe9aba0b..b926a4d6d 100644 --- a/lib/JsonNode.h +++ b/lib/JsonNode.h @@ -7,7 +7,7 @@ * Full text of license available in license.txt file, in main folder * */ - + #pragma once class JsonNode; @@ -55,6 +55,7 @@ public: explicit JsonNode(const char * data, size_t datasize); //Create tree from JSON file explicit JsonNode(ResourceID && fileURI); + explicit JsonNode(const ResourceID & fileURI); explicit JsonNode(ResourceID && fileURI, bool & isValidSyntax); //Copy c-tor JsonNode(const JsonNode ©); @@ -125,9 +126,9 @@ namespace JsonUtils /** * @brief parse short bonus format, excluding type * @note sets duration to Permament - */ + */ DLL_LINKAGE void parseTypedBonusShort(const JsonVector &source, Bonus *dest); - + /// DLL_LINKAGE Bonus * parseBonus (const JsonVector &ability_vec); DLL_LINKAGE Bonus * parseBonus (const JsonNode &bonus); @@ -144,7 +145,7 @@ namespace JsonUtils * @note this function will destroy data in source */ DLL_LINKAGE void merge(JsonNode & dest, JsonNode & source); - + /** * @brief recursively merges source into dest, replacing identical fields * struct : recursively calls this function @@ -152,12 +153,12 @@ namespace JsonUtils * values : value in source will replace value in dest * null : if value in source is present but set to null it will delete entry in dest * @note this function will preserve data stored in source by creating copy - */ + */ DLL_LINKAGE void mergeCopy(JsonNode & dest, JsonNode source); - + /** @brief recursively merges descendant into copy of base node * Result emulates inheritance semantic - * + * * */ DLL_LINKAGE void inherit(JsonNode & descendant, const JsonNode & base); @@ -200,7 +201,7 @@ namespace JsonDetail { // conversion helpers for JsonNode::convertTo (partial template function instantiation is illegal in c++) - template + template struct JsonConvImpl; template @@ -229,7 +230,7 @@ namespace JsonDetail ///this should be triggered only for numeric types and enums static_assert(boost::mpl::or_, std::is_enum, boost::is_class >::value, "Unsupported type for JsonNode::convertTo()!"); return JsonConvImpl, boost::is_class >::value >::convertImpl(node); - + } }; diff --git a/lib/filesystem/AdapterLoaders.cpp b/lib/filesystem/AdapterLoaders.cpp index cb2ceaf23..8c20743e5 100644 --- a/lib/filesystem/AdapterLoaders.cpp +++ b/lib/filesystem/AdapterLoaders.cpp @@ -87,6 +87,20 @@ boost::optional CFilesystemList::getResourceName(const ResourceID & return boost::optional(); } +std::set CFilesystemList::getResourceNames(const ResourceID & resourceName) const +{ + std::set paths; + for(auto& loader : getResourcesWithName(resourceName)) + { + auto rn = loader->getResourceName(resourceName); + if(rn) + { + paths.insert(*rn); + } + } + return std::move(paths); +} + std::unordered_set CFilesystemList::getFilteredFiles(std::function filter) const { std::unordered_set ret; diff --git a/lib/filesystem/AdapterLoaders.h b/lib/filesystem/AdapterLoaders.h index cabe5760e..2abe5fcb9 100644 --- a/lib/filesystem/AdapterLoaders.h +++ b/lib/filesystem/AdapterLoaders.h @@ -59,16 +59,9 @@ class DLL_LINKAGE CFilesystemList : public ISimpleResourceLoader std::set writeableLoaders; //FIXME: this is only compile fix, should be removed in the end - CFilesystemList(CFilesystemList &) - { - //class is not copyable - } - CFilesystemList &operator=(CFilesystemList &) - { - //class is not copyable - return *this; - } - + CFilesystemList(CFilesystemList &) = delete; + CFilesystemList &operator=(CFilesystemList &) = delete; + public: CFilesystemList(); ~CFilesystemList(); @@ -78,6 +71,7 @@ public: bool existsResource(const ResourceID & resourceName) const override; std::string getMountPoint() const override; boost::optional getResourceName(const ResourceID & resourceName) const override; + std::set getResourceNames(const ResourceID & resourceName) const override; std::unordered_set getFilteredFiles(std::function filter) const override; bool createResource(std::string filename, bool update = false) override; std::vector getResourcesWithName(const ResourceID & resourceName) const override; diff --git a/lib/filesystem/ISimpleResourceLoader.h b/lib/filesystem/ISimpleResourceLoader.h index f04762210..f3d0be633 100644 --- a/lib/filesystem/ISimpleResourceLoader.h +++ b/lib/filesystem/ISimpleResourceLoader.h @@ -53,6 +53,22 @@ public: return boost::optional(); } + /** + * Gets all full names of matching resources, e.g. names of files in filesystem. + * + * @return std::set with names. + */ + virtual std::set getResourceNames(const ResourceID & resourceName) const + { + std::set result; + auto rn = getResourceName(resourceName); + if(rn) + { + result.insert(*rn); + } + return result; + } + /** * Get list of files that matches filter function * diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index e2e56e113..368201a81 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -757,18 +757,21 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result ) RemoveObject ro(finishingBattle->winnerHero->id); sendAndApply(&ro); - SetAvailableHeroes sah; - sah.player = finishingBattle->victor; - sah.hid[0] = finishingBattle->winnerHero->subID; - sah.army[0].clear(); - sah.army[0].setCreature(SlotID(0), finishingBattle->winnerHero->type->initialArmy.at(0).creature, 1); + if (VLC->modh->settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS) + { + SetAvailableHeroes sah; + sah.player = finishingBattle->victor; + sah.hid[0] = finishingBattle->winnerHero->subID; + sah.army[0].clear(); + sah.army[0].setCreature(SlotID(0), finishingBattle->winnerHero->type->initialArmy.at(0).creature, 1); - if(const CGHeroInstance *another = getPlayer(finishingBattle->victor)->availableHeroes.at(0)) - sah.hid[1] = another->subID; - else - sah.hid[1] = -1; + if(const CGHeroInstance *another = getPlayer(finishingBattle->victor)->availableHeroes.at(0)) + sah.hid[1] = another->subID; + else + sah.hid[1] = -1; - sendAndApply(&sah); + sendAndApply(&sah); + } } } From 9f3313524e11dcd486b2b53e22fc22817af9f8e5 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Wed, 27 Jan 2016 13:47:42 +0300 Subject: [PATCH 43/45] Fix 2160 dismissing a VIP hero --- client/windows/CHeroWindow.cpp | 12 +++-- lib/CGameInfoCallback.cpp | 73 ++++++++++++++++--------------- lib/CGameState.cpp | 4 +- lib/mapObjects/CGHeroInstance.cpp | 25 +++++++++++ lib/mapObjects/CGHeroInstance.h | 3 ++ 5 files changed, 76 insertions(+), 41 deletions(-) diff --git a/client/windows/CHeroWindow.cpp b/client/windows/CHeroWindow.cpp index 06ca90d6e..7246e1ff8 100644 --- a/client/windows/CHeroWindow.cpp +++ b/client/windows/CHeroWindow.cpp @@ -28,6 +28,7 @@ #include "../lib/CHeroHandler.h" #include "../lib/mapObjects/CGHeroInstance.h" #include "../lib/NetPacksBase.h" +#include "../mapHandler.h" /* * CHeroWindow.cpp, part of VCMI engine @@ -275,6 +276,9 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals if(!LOCPLINT->cb->howManyTowns() && LOCPLINT->cb->howManyHeroes() == 1) noDismiss = true; + if(curHero->isMissionCritical()) + noDismiss = true; + dismissButton->block(!!curHero->visitedTown || noDismiss); if(curHero->getSecSkillLevel(SecondarySkill::TACTICS) == 0) @@ -343,10 +347,10 @@ void CHeroWindow::commanderWindow() void CHeroWindow::showAll(SDL_Surface * to) { CIntObject::showAll(to); - + //printing hero's name printAtMiddleLoc(curHero->name, 190, 38, FONT_BIG, Colors::YELLOW, to); - + //printing hero's level std::string secondLine= CGI->generaltexth->allTexts[342]; boost::algorithm::replace_first(secondLine,"%d",boost::lexical_cast(curHero->level)); @@ -360,14 +364,14 @@ void CHeroWindow::showAll(SDL_Surface * to) primarySkill << primSkillAreas[m]->bonusValue; printAtMiddleLoc(primarySkill.str(), 53 + 70 * m, 166, FONT_SMALL, Colors::WHITE, to); } - + //secondary skills for(size_t v=0; vsecSkills.size()); ++v) { printAtLoc(CGI->generaltexth->levels[curHero->secSkills[v].second-1], (v%2) ? 212 : 68, 280 + 48 * (v/2), FONT_SMALL, Colors::WHITE, to); printAtLoc(CGI->generaltexth->skillName[curHero->secSkills[v].first], (v%2) ? 212 : 68, 300 + 48 * (v/2), FONT_SMALL, Colors::WHITE, to); } - + //printing special ability printAtLoc(curHero->type->specName, 69, 205, FONT_SMALL, Colors::WHITE, to); std::ostringstream expstr; diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 9abf44444..08af6db42 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -66,6 +66,10 @@ const PlayerState * CGameInfoCallback::getPlayer(PlayerColor color, bool verbose { //funtion written from scratch since it's accessed A LOT by AI + if(!color.isValidPlayer()) + { + return nullptr; + } auto player = gs->players.find(color); if (player != gs->players.end()) { @@ -229,13 +233,13 @@ bool CGameInfoCallback::getTownInfo(const CGObjectInstance * town, InfoAboutTown { if(!detailed && nullptr != selectedObject) { - const CGHeroInstance * selectedHero = dynamic_cast(selectedObject); + const CGHeroInstance * selectedHero = dynamic_cast(selectedObject); if(nullptr != selectedHero) - detailed = selectedHero->hasVisions(town, 1); + detailed = selectedHero->hasVisions(town, 1); } - + dest.initFromTown(static_cast(town), detailed); - } + } else if(town->ID == Obj::GARRISON || town->ID == Obj::GARRISON2) dest.initFromArmy(static_cast(town), detailed); else @@ -268,28 +272,28 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero ERROR_RET_VAL_IF(!isVisible(h->getPosition(false)), "That hero is not visible!", false); bool accessFlag = hasAccess(h->tempOwner); - + if(!accessFlag && nullptr != selectedObject) { - const CGHeroInstance * selectedHero = dynamic_cast(selectedObject); + const CGHeroInstance * selectedHero = dynamic_cast(selectedObject); if(nullptr != selectedHero) - accessFlag = selectedHero->hasVisions(hero, 1); + accessFlag = selectedHero->hasVisions(hero, 1); } - + dest.initFromHero(h, accessFlag); - + //DISGUISED bonus implementation - + if(getPlayerRelations(getLocalPlayer(), hero->tempOwner) == PlayerRelations::ENEMIES) { - //todo: bonus cashing + //todo: bonus cashing int disguiseLevel = h->valOfBonuses(Selector::typeSubtype(Bonus::DISGUISED, 0)); - - auto doBasicDisguise = [disguiseLevel](InfoAboutHero & info) + + auto doBasicDisguise = [disguiseLevel](InfoAboutHero & info) { int maxAIValue = 0; const CCreature * mostStrong = nullptr; - + for(auto & elem : info.army) { if(elem.second.type->AIValue > maxAIValue) @@ -298,7 +302,7 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero mostStrong = elem.second.type; } } - + if(nullptr == mostStrong)//just in case logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Unable to select most strong stack" << disguiseLevel; else @@ -307,25 +311,25 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero elem.second.type = mostStrong; } }; - - auto doAdvancedDisguise = [accessFlag, &doBasicDisguise](InfoAboutHero & info) + + auto doAdvancedDisguise = [accessFlag, &doBasicDisguise](InfoAboutHero & info) { doBasicDisguise(info); - + for(auto & elem : info.army) elem.second.count = 0; }; - - auto doExpertDisguise = [this,h](InfoAboutHero & info) + + auto doExpertDisguise = [this,h](InfoAboutHero & info) { for(auto & elem : info.army) elem.second.count = 0; - + const auto factionIndex = getStartInfo(false)->playerInfos.at(h->tempOwner).castle; - + int maxAIValue = 0; const CCreature * mostStrong = nullptr; - + for(auto creature : VLC->creh->creatures) { if(creature->faction == factionIndex && creature->AIValue > maxAIValue) @@ -334,35 +338,35 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero mostStrong = creature; } } - + if(nullptr != mostStrong) //possible, faction may have no creatures at all for(auto & elem : info.army) elem.second.type = mostStrong; - }; - - + }; + + switch (disguiseLevel) { case 0: //no bonus at all - do nothing - break; + break; case 1: doBasicDisguise(dest); - break; + break; case 2: doAdvancedDisguise(dest); - break; + break; case 3: doExpertDisguise(dest); - break; + break; default: //invalid value logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Invalid DISGUISED bonus value " << disguiseLevel; break; } - + } - + return true; } @@ -486,7 +490,7 @@ std::shared_ptr> CGameInfoCallback::getAllVi boost::multi_array tileArray(boost::extents[width][height][levels]); - + for (size_t x = 0; x < width; x++) for (size_t y = 0; y < height; y++) for (size_t z = 0; z < levels; z++) @@ -964,4 +968,3 @@ void IGameEventRealizer::setObjProperty(ObjectInstanceID objid, int prop, si64 v sob.val = static_cast(val); commitPackage(&sob); } - diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 50b1c8afe..cc3ded16b 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2268,7 +2268,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c for (const TriggeredEvent & event : map->triggeredEvents) { - if ((event.trigger.test(evaluateEvent))) + if (event.trigger.test(evaluateEvent)) { if (event.effect.type == EventEffect::VICTORY) return EVictoryLossCheckResult::victory(event.onFulfill, event.effect.toOtherMessage); @@ -2285,7 +2285,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c return EVictoryLossCheckResult(); } -bool CGameState::checkForVictory( PlayerColor player, const EventCondition & condition ) const +bool CGameState::checkForVictory(PlayerColor player, const EventCondition & condition) const { const PlayerState *p = CGameInfoCallback::getPlayer(player); switch (condition.condition) diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 13a25f9db..c03e3419d 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -23,6 +23,7 @@ #include "../CCreatureHandler.h" #include "../BattleState.h" #include "../CTownHandler.h" +#include "../mapping/CMap.h" #include "CGTownInstance.h" ///helpers @@ -1454,3 +1455,27 @@ bool CGHeroInstance::hasVisions(const CGObjectInstance * target, const int subty return (distance < visionsRange) && (target->pos.z == pos.z); } + +bool CGHeroInstance::isMissionCritical() const +{ + for(const TriggeredEvent & event : IObjectInterface::cb->getMapHeader()->triggeredEvents) + { + if(event.trigger.test([&](const EventCondition & condition) + { + if (condition.condition == EventCondition::CONTROL && condition.object) + { + auto hero = dynamic_cast(condition.object); + return (hero != this); + } + else if(condition.condition == EventCondition::IS_HUMAN) + { + return true; + } + return false; + })) + { + return true; + } + } + return false; +} diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 41ccbcdda..90177cfe3 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -20,6 +20,7 @@ class CHero; class CGBoat; class CGTownInstance; +class CMap; struct TerrainTile; struct TurnInfo; @@ -211,6 +212,8 @@ public: void updateSkill(SecondarySkill which, int val); bool hasVisions(const CGObjectInstance * target, const int subtype) const; + /// If this hero perishes, the scenario is failed + bool isMissionCritical() const; CGHeroInstance(); virtual ~CGHeroInstance(); From 048e22dbe64852908ea5f1ae7a9c353a02d389f1 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Wed, 27 Jan 2016 19:32:59 +0300 Subject: [PATCH 44/45] Fix build. --- lib/CModHandler.cpp | 2 +- lib/filesystem/AdapterLoaders.cpp | 4 +- lib/filesystem/AdapterLoaders.h | 172 ++++++++++++------------- lib/filesystem/ISimpleResourceLoader.h | 4 +- 4 files changed, 91 insertions(+), 91 deletions(-) diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index b2a523168..85fcdb924 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -554,7 +554,7 @@ void CModHandler::loadConfigFromFile (std::string name) std::string paths; for(auto& p : CResourceHandler::get()->getResourceNames(ResourceID("config/" + name))) { - paths += p + ", "; + paths += p.string() + ", "; } paths = paths.substr(0, paths.size() - 2); logGlobal->debugStream() << "Loading hardcoded features settings from [" << paths << "], result:"; diff --git a/lib/filesystem/AdapterLoaders.cpp b/lib/filesystem/AdapterLoaders.cpp index 9fa8a7101..26193b228 100644 --- a/lib/filesystem/AdapterLoaders.cpp +++ b/lib/filesystem/AdapterLoaders.cpp @@ -87,9 +87,9 @@ boost::optional CFilesystemList::getResourceName(const return boost::optional(); } -std::set CFilesystemList::getResourceNames(const ResourceID & resourceName) const +std::set CFilesystemList::getResourceNames(const ResourceID & resourceName) const { - std::set paths; + std::set paths; for(auto& loader : getResourcesWithName(resourceName)) { auto rn = loader->getResourceName(resourceName); diff --git a/lib/filesystem/AdapterLoaders.h b/lib/filesystem/AdapterLoaders.h index 51f9ce789..6b2db0592 100644 --- a/lib/filesystem/AdapterLoaders.h +++ b/lib/filesystem/AdapterLoaders.h @@ -1,87 +1,87 @@ -#pragma once - -/* - * AdapterLoaders.h, part of VCMI engine - * - * Authors: listed in file AUTHORS in main folder - * - * License: GNU General Public License v2.0 or later - * Full text of license available in license.txt file, in main folder - * - */ - -#include "ISimpleResourceLoader.h" -#include "ResourceID.h" - -class CFileInfo; -class CInputStream; -class JsonNode; - -/** - * Class that implements file mapping (aka *nix symbolic links) - * Uses json file as input, content is map: - * "fileA.txt" : "fileB.txt" - * Note that extension is necessary, but used only to determine type - * - * fileA - file which will be replaced - * fileB - file which will be used as replacement - */ -class DLL_LINKAGE CMappedFileLoader : public ISimpleResourceLoader -{ -public: - /** - * Ctor. - * - * @param config Specifies filesystem configuration - */ - explicit CMappedFileLoader(const std::string &mountPoint, const JsonNode & config); - - /// Interface implementation - /// @see ISimpleResourceLoader - std::unique_ptr load(const ResourceID & resourceName) const override; - bool existsResource(const ResourceID & resourceName) const override; - std::string getMountPoint() const override; - boost::optional getResourceName(const ResourceID & resourceName) const override; - std::unordered_set getFilteredFiles(std::function filter) const override; - -private: - /** A list of files in this map - * key = ResourceID for resource loader - * value = ResourceID to which file this request will be redirected - */ - std::unordered_map fileList; -}; - -class DLL_LINKAGE CFilesystemList : public ISimpleResourceLoader -{ - std::vector > loaders; - - std::set writeableLoaders; - - //FIXME: this is only compile fix, should be removed in the end - CFilesystemList(CFilesystemList &) = delete; - CFilesystemList &operator=(CFilesystemList &) = delete; +#pragma once -public: - CFilesystemList(); - ~CFilesystemList(); - /// Interface implementation - /// @see ISimpleResourceLoader - std::unique_ptr load(const ResourceID & resourceName) const override; - bool existsResource(const ResourceID & resourceName) const override; - std::string getMountPoint() const override; - boost::optional getResourceName(const ResourceID & resourceName) const override; - std::set getResourceNames(const ResourceID & resourceName) const override; - std::unordered_set getFilteredFiles(std::function filter) const override; - bool createResource(std::string filename, bool update = false) override; - std::vector getResourcesWithName(const ResourceID & resourceName) const override; - - /** - * Adds a resource loader to the loaders list - * Passes loader ownership to this object - * - * @param loader The simple resource loader object to add - * @param writeable - resource shall be treated as writeable - */ - void addLoader(ISimpleResourceLoader * loader, bool writeable); -}; +/* + * AdapterLoaders.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "ISimpleResourceLoader.h" +#include "ResourceID.h" + +class CFileInfo; +class CInputStream; +class JsonNode; + +/** + * Class that implements file mapping (aka *nix symbolic links) + * Uses json file as input, content is map: + * "fileA.txt" : "fileB.txt" + * Note that extension is necessary, but used only to determine type + * + * fileA - file which will be replaced + * fileB - file which will be used as replacement + */ +class DLL_LINKAGE CMappedFileLoader : public ISimpleResourceLoader +{ +public: + /** + * Ctor. + * + * @param config Specifies filesystem configuration + */ + explicit CMappedFileLoader(const std::string &mountPoint, const JsonNode & config); + + /// Interface implementation + /// @see ISimpleResourceLoader + std::unique_ptr load(const ResourceID & resourceName) const override; + bool existsResource(const ResourceID & resourceName) const override; + std::string getMountPoint() const override; + boost::optional getResourceName(const ResourceID & resourceName) const override; + std::unordered_set getFilteredFiles(std::function filter) const override; + +private: + /** A list of files in this map + * key = ResourceID for resource loader + * value = ResourceID to which file this request will be redirected + */ + std::unordered_map fileList; +}; + +class DLL_LINKAGE CFilesystemList : public ISimpleResourceLoader +{ + std::vector > loaders; + + std::set writeableLoaders; + + //FIXME: this is only compile fix, should be removed in the end + CFilesystemList(CFilesystemList &) = delete; + CFilesystemList &operator=(CFilesystemList &) = delete; + +public: + CFilesystemList(); + ~CFilesystemList(); + /// Interface implementation + /// @see ISimpleResourceLoader + std::unique_ptr load(const ResourceID & resourceName) const override; + bool existsResource(const ResourceID & resourceName) const override; + std::string getMountPoint() const override; + boost::optional getResourceName(const ResourceID & resourceName) const override; + std::set getResourceNames(const ResourceID & resourceName) const override; + std::unordered_set getFilteredFiles(std::function filter) const override; + bool createResource(std::string filename, bool update = false) override; + std::vector getResourcesWithName(const ResourceID & resourceName) const override; + + /** + * Adds a resource loader to the loaders list + * Passes loader ownership to this object + * + * @param loader The simple resource loader object to add + * @param writeable - resource shall be treated as writeable + */ + void addLoader(ISimpleResourceLoader * loader, bool writeable); +}; diff --git a/lib/filesystem/ISimpleResourceLoader.h b/lib/filesystem/ISimpleResourceLoader.h index f7b663edc..5ee281942 100644 --- a/lib/filesystem/ISimpleResourceLoader.h +++ b/lib/filesystem/ISimpleResourceLoader.h @@ -58,9 +58,9 @@ public: * * @return std::set with names. */ - virtual std::set getResourceNames(const ResourceID & resourceName) const + virtual std::set getResourceNames(const ResourceID & resourceName) const { - std::set result; + std::set result; auto rn = getResourceName(resourceName); if(rn) { From 0ac833cca42af257381d862f36e4d55068d4741c Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Wed, 20 Jan 2016 22:40:21 +0300 Subject: [PATCH 45/45] Fix 2377 "Seer Empty Text" --- lib/Connection.h | 2 +- lib/mapObjects/CQuest.cpp | 5 +++-- lib/mapObjects/CQuest.h | 16 ++++++++++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/Connection.h b/lib/Connection.h index 5e3ee07a9..69e3725b4 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -27,7 +27,7 @@ #include "mapping/CCampaignHandler.h" //for CCampaignState #include "rmg/CMapGenerator.h" // for CMapGenOptions -const ui32 version = 756; +const ui32 version = 757; const ui32 minSupportedVersion = 753; class CISer; diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index 0db3258bd..8432dfc50 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -416,6 +416,7 @@ void CGSeerHut::init() { seerName = *RandomGeneratorUtil::nextItem(VLC->generaltexth->seerNames, cb->gameState()->getRandomGenerator()); quest->textOption = cb->gameState()->getRandomGenerator().nextInt(2); + quest->completedOption = cb->gameState()->getRandomGenerator().nextInt(1, 5); } void CGSeerHut::initObj() @@ -435,7 +436,7 @@ void CGSeerHut::initObj() else { quest->progress = CQuest::COMPLETE; - quest->firstVisitText = VLC->generaltexth->seerEmpty[quest->textOption]; + quest->firstVisitText = VLC->generaltexth->seerEmpty[quest->completedOption]; } } @@ -582,7 +583,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const } else { - iw.text << VLC->generaltexth->seerEmpty[quest->textOption]; + iw.text << VLC->generaltexth->seerEmpty[quest->completedOption]; if (ID == Obj::SEER_HUT) iw.text.addReplacement(seerName); cb->showInfoDialog(&iw); diff --git a/lib/mapObjects/CQuest.h b/lib/mapObjects/CQuest.h index cc9fd9063..fe02c57de 100644 --- a/lib/mapObjects/CQuest.h +++ b/lib/mapObjects/CQuest.h @@ -37,8 +37,11 @@ public: std::vector m6creatures; //pair[cre id, cre count], CreatureSet info irrelevant std::vector m7resources; //TODO: use resourceset? - //following field are used only for kill creature/hero missions, the original objects became inaccessible after their removal, so we need to store info needed for messages / hover text + // following fields are used only for kill creature/hero missions, the original + // objects became inaccessible after their removal, so we need to store info + // needed for messages / hover text ui8 textOption; + ui8 completedOption; CStackBasicDescriptor stackToKill; ui8 stackDirection; std::string heroName; //backup of hero name @@ -66,7 +69,16 @@ public: { h & qid & missionType & progress & lastDay & m13489val & m2stats & m5arts & m6creatures & m7resources & textOption & stackToKill & stackDirection & heroName & heroPortrait - & firstVisitText & nextVisitText & completedText & isCustomFirst & isCustomNext & isCustomComplete; + & firstVisitText & nextVisitText & completedText & isCustomFirst + & isCustomNext & isCustomComplete; + if(version >= 757) + { + h & completedOption; + } + else if(!h.saving) + { + completedOption = 1; + } } };