From 9f3313524e11dcd486b2b53e22fc22817af9f8e5 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Wed, 27 Jan 2016 13:47:42 +0300 Subject: [PATCH] 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();