Browse Source

Fix 2160 dismissing a VIP hero

Vadim Markovtsev 9 years ago
parent
commit
9f3313524e

+ 8 - 4
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<std::string>(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; v<std::min(secSkillAreas.size(), curHero->secSkills.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;

+ 38 - 35
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<const CGHeroInstance *>(selectedObject);		
+			const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
 			if(nullptr != selectedHero)
-				detailed = selectedHero->hasVisions(town, 1);			
+				detailed = selectedHero->hasVisions(town, 1);
 		}
-		
+
 		dest.initFromTown(static_cast<const CGTownInstance *>(town), detailed);
-	}		
+	}
 	else if(town->ID == Obj::GARRISON || town->ID == Obj::GARRISON2)
 		dest.initFromArmy(static_cast<const CArmedInstance *>(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<const CGHeroInstance *>(selectedObject);		
+		const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(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<boost::multi_array<TerrainTile*, 3>> CGameInfoCallback::getAllVi
 
 
 	boost::multi_array<TerrainTile*, 3> 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<ui32>(val);
 	commitPackage(&sob);
 }
-

+ 2 - 2
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)

+ 25 - 0
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<const CGHeroInstance*>(condition.object);
+				return (hero != this);
+			}
+			else if(condition.condition == EventCondition::IS_HUMAN)
+			{
+				return true;
+			}
+			return false;
+		}))
+		{
+			return true;
+		}
+	}
+	return false;
+}

+ 3 - 0
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();