mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +02:00
Merge pull request #176 from vmarkovtsev/issue/2160
Fix 2160 dismissing a VIP hero.
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
#include "../lib/CHeroHandler.h"
|
#include "../lib/CHeroHandler.h"
|
||||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||||
#include "../lib/NetPacksBase.h"
|
#include "../lib/NetPacksBase.h"
|
||||||
|
#include "../mapHandler.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CHeroWindow.cpp, part of VCMI engine
|
* 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)
|
if(!LOCPLINT->cb->howManyTowns() && LOCPLINT->cb->howManyHeroes() == 1)
|
||||||
noDismiss = true;
|
noDismiss = true;
|
||||||
|
|
||||||
|
if(curHero->isMissionCritical())
|
||||||
|
noDismiss = true;
|
||||||
|
|
||||||
dismissButton->block(!!curHero->visitedTown || noDismiss);
|
dismissButton->block(!!curHero->visitedTown || noDismiss);
|
||||||
|
|
||||||
if(curHero->getSecSkillLevel(SecondarySkill::TACTICS) == 0)
|
if(curHero->getSecSkillLevel(SecondarySkill::TACTICS) == 0)
|
||||||
@@ -343,10 +347,10 @@ void CHeroWindow::commanderWindow()
|
|||||||
void CHeroWindow::showAll(SDL_Surface * to)
|
void CHeroWindow::showAll(SDL_Surface * to)
|
||||||
{
|
{
|
||||||
CIntObject::showAll(to);
|
CIntObject::showAll(to);
|
||||||
|
|
||||||
//printing hero's name
|
//printing hero's name
|
||||||
printAtMiddleLoc(curHero->name, 190, 38, FONT_BIG, Colors::YELLOW, to);
|
printAtMiddleLoc(curHero->name, 190, 38, FONT_BIG, Colors::YELLOW, to);
|
||||||
|
|
||||||
//printing hero's level
|
//printing hero's level
|
||||||
std::string secondLine= CGI->generaltexth->allTexts[342];
|
std::string secondLine= CGI->generaltexth->allTexts[342];
|
||||||
boost::algorithm::replace_first(secondLine,"%d",boost::lexical_cast<std::string>(curHero->level));
|
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;
|
primarySkill << primSkillAreas[m]->bonusValue;
|
||||||
printAtMiddleLoc(primarySkill.str(), 53 + 70 * m, 166, FONT_SMALL, Colors::WHITE, to);
|
printAtMiddleLoc(primarySkill.str(), 53 + 70 * m, 166, FONT_SMALL, Colors::WHITE, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
//secondary skills
|
//secondary skills
|
||||||
for(size_t v=0; v<std::min(secSkillAreas.size(), curHero->secSkills.size()); ++v)
|
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->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);
|
printAtLoc(CGI->generaltexth->skillName[curHero->secSkills[v].first], (v%2) ? 212 : 68, 300 + 48 * (v/2), FONT_SMALL, Colors::WHITE, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
//printing special ability
|
//printing special ability
|
||||||
printAtLoc(curHero->type->specName, 69, 205, FONT_SMALL, Colors::WHITE, to);
|
printAtLoc(curHero->type->specName, 69, 205, FONT_SMALL, Colors::WHITE, to);
|
||||||
std::ostringstream expstr;
|
std::ostringstream expstr;
|
||||||
|
@@ -66,6 +66,10 @@ const PlayerState * CGameInfoCallback::getPlayer(PlayerColor color, bool verbose
|
|||||||
{
|
{
|
||||||
//funtion written from scratch since it's accessed A LOT by AI
|
//funtion written from scratch since it's accessed A LOT by AI
|
||||||
|
|
||||||
|
if(!color.isValidPlayer())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
auto player = gs->players.find(color);
|
auto player = gs->players.find(color);
|
||||||
if (player != gs->players.end())
|
if (player != gs->players.end())
|
||||||
{
|
{
|
||||||
@@ -229,13 +233,13 @@ bool CGameInfoCallback::getTownInfo(const CGObjectInstance * town, InfoAboutTown
|
|||||||
{
|
{
|
||||||
if(!detailed && nullptr != selectedObject)
|
if(!detailed && nullptr != selectedObject)
|
||||||
{
|
{
|
||||||
const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
|
const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
|
||||||
if(nullptr != selectedHero)
|
if(nullptr != selectedHero)
|
||||||
detailed = selectedHero->hasVisions(town, 1);
|
detailed = selectedHero->hasVisions(town, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
dest.initFromTown(static_cast<const CGTownInstance *>(town), detailed);
|
dest.initFromTown(static_cast<const CGTownInstance *>(town), detailed);
|
||||||
}
|
}
|
||||||
else if(town->ID == Obj::GARRISON || town->ID == Obj::GARRISON2)
|
else if(town->ID == Obj::GARRISON || town->ID == Obj::GARRISON2)
|
||||||
dest.initFromArmy(static_cast<const CArmedInstance *>(town), detailed);
|
dest.initFromArmy(static_cast<const CArmedInstance *>(town), detailed);
|
||||||
else
|
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);
|
ERROR_RET_VAL_IF(!isVisible(h->getPosition(false)), "That hero is not visible!", false);
|
||||||
|
|
||||||
bool accessFlag = hasAccess(h->tempOwner);
|
bool accessFlag = hasAccess(h->tempOwner);
|
||||||
|
|
||||||
if(!accessFlag && nullptr != selectedObject)
|
if(!accessFlag && nullptr != selectedObject)
|
||||||
{
|
{
|
||||||
const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
|
const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
|
||||||
if(nullptr != selectedHero)
|
if(nullptr != selectedHero)
|
||||||
accessFlag = selectedHero->hasVisions(hero, 1);
|
accessFlag = selectedHero->hasVisions(hero, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
dest.initFromHero(h, accessFlag);
|
dest.initFromHero(h, accessFlag);
|
||||||
|
|
||||||
//DISGUISED bonus implementation
|
//DISGUISED bonus implementation
|
||||||
|
|
||||||
if(getPlayerRelations(getLocalPlayer(), hero->tempOwner) == PlayerRelations::ENEMIES)
|
if(getPlayerRelations(getLocalPlayer(), hero->tempOwner) == PlayerRelations::ENEMIES)
|
||||||
{
|
{
|
||||||
//todo: bonus cashing
|
//todo: bonus cashing
|
||||||
int disguiseLevel = h->valOfBonuses(Selector::typeSubtype(Bonus::DISGUISED, 0));
|
int disguiseLevel = h->valOfBonuses(Selector::typeSubtype(Bonus::DISGUISED, 0));
|
||||||
|
|
||||||
auto doBasicDisguise = [disguiseLevel](InfoAboutHero & info)
|
auto doBasicDisguise = [disguiseLevel](InfoAboutHero & info)
|
||||||
{
|
{
|
||||||
int maxAIValue = 0;
|
int maxAIValue = 0;
|
||||||
const CCreature * mostStrong = nullptr;
|
const CCreature * mostStrong = nullptr;
|
||||||
|
|
||||||
for(auto & elem : info.army)
|
for(auto & elem : info.army)
|
||||||
{
|
{
|
||||||
if(elem.second.type->AIValue > maxAIValue)
|
if(elem.second.type->AIValue > maxAIValue)
|
||||||
@@ -298,7 +302,7 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
|
|||||||
mostStrong = elem.second.type;
|
mostStrong = elem.second.type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nullptr == mostStrong)//just in case
|
if(nullptr == mostStrong)//just in case
|
||||||
logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Unable to select most strong stack" << disguiseLevel;
|
logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Unable to select most strong stack" << disguiseLevel;
|
||||||
else
|
else
|
||||||
@@ -307,25 +311,25 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
|
|||||||
elem.second.type = mostStrong;
|
elem.second.type = mostStrong;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto doAdvancedDisguise = [accessFlag, &doBasicDisguise](InfoAboutHero & info)
|
auto doAdvancedDisguise = [accessFlag, &doBasicDisguise](InfoAboutHero & info)
|
||||||
{
|
{
|
||||||
doBasicDisguise(info);
|
doBasicDisguise(info);
|
||||||
|
|
||||||
for(auto & elem : info.army)
|
for(auto & elem : info.army)
|
||||||
elem.second.count = 0;
|
elem.second.count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto doExpertDisguise = [this,h](InfoAboutHero & info)
|
auto doExpertDisguise = [this,h](InfoAboutHero & info)
|
||||||
{
|
{
|
||||||
for(auto & elem : info.army)
|
for(auto & elem : info.army)
|
||||||
elem.second.count = 0;
|
elem.second.count = 0;
|
||||||
|
|
||||||
const auto factionIndex = getStartInfo(false)->playerInfos.at(h->tempOwner).castle;
|
const auto factionIndex = getStartInfo(false)->playerInfos.at(h->tempOwner).castle;
|
||||||
|
|
||||||
int maxAIValue = 0;
|
int maxAIValue = 0;
|
||||||
const CCreature * mostStrong = nullptr;
|
const CCreature * mostStrong = nullptr;
|
||||||
|
|
||||||
for(auto creature : VLC->creh->creatures)
|
for(auto creature : VLC->creh->creatures)
|
||||||
{
|
{
|
||||||
if(creature->faction == factionIndex && creature->AIValue > maxAIValue)
|
if(creature->faction == factionIndex && creature->AIValue > maxAIValue)
|
||||||
@@ -334,35 +338,35 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
|
|||||||
mostStrong = creature;
|
mostStrong = creature;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nullptr != mostStrong) //possible, faction may have no creatures at all
|
if(nullptr != mostStrong) //possible, faction may have no creatures at all
|
||||||
for(auto & elem : info.army)
|
for(auto & elem : info.army)
|
||||||
elem.second.type = mostStrong;
|
elem.second.type = mostStrong;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
switch (disguiseLevel)
|
switch (disguiseLevel)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
//no bonus at all - do nothing
|
//no bonus at all - do nothing
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
doBasicDisguise(dest);
|
doBasicDisguise(dest);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
doAdvancedDisguise(dest);
|
doAdvancedDisguise(dest);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
doExpertDisguise(dest);
|
doExpertDisguise(dest);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
//invalid value
|
//invalid value
|
||||||
logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Invalid DISGUISED bonus value " << disguiseLevel;
|
logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Invalid DISGUISED bonus value " << disguiseLevel;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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]);
|
boost::multi_array<TerrainTile*, 3> tileArray(boost::extents[width][height][levels]);
|
||||||
|
|
||||||
for (size_t x = 0; x < width; x++)
|
for (size_t x = 0; x < width; x++)
|
||||||
for (size_t y = 0; y < height; y++)
|
for (size_t y = 0; y < height; y++)
|
||||||
for (size_t z = 0; z < levels; z++)
|
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);
|
sob.val = static_cast<ui32>(val);
|
||||||
commitPackage(&sob);
|
commitPackage(&sob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2268,7 +2268,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c
|
|||||||
|
|
||||||
for (const TriggeredEvent & event : map->triggeredEvents)
|
for (const TriggeredEvent & event : map->triggeredEvents)
|
||||||
{
|
{
|
||||||
if ((event.trigger.test(evaluateEvent)))
|
if (event.trigger.test(evaluateEvent))
|
||||||
{
|
{
|
||||||
if (event.effect.type == EventEffect::VICTORY)
|
if (event.effect.type == EventEffect::VICTORY)
|
||||||
return EVictoryLossCheckResult::victory(event.onFulfill, event.effect.toOtherMessage);
|
return EVictoryLossCheckResult::victory(event.onFulfill, event.effect.toOtherMessage);
|
||||||
@@ -2285,7 +2285,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c
|
|||||||
return EVictoryLossCheckResult();
|
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);
|
const PlayerState *p = CGameInfoCallback::getPlayer(player);
|
||||||
switch (condition.condition)
|
switch (condition.condition)
|
||||||
|
@@ -23,6 +23,7 @@
|
|||||||
#include "../CCreatureHandler.h"
|
#include "../CCreatureHandler.h"
|
||||||
#include "../BattleState.h"
|
#include "../BattleState.h"
|
||||||
#include "../CTownHandler.h"
|
#include "../CTownHandler.h"
|
||||||
|
#include "../mapping/CMap.h"
|
||||||
#include "CGTownInstance.h"
|
#include "CGTownInstance.h"
|
||||||
|
|
||||||
///helpers
|
///helpers
|
||||||
@@ -1454,3 +1455,27 @@ bool CGHeroInstance::hasVisions(const CGObjectInstance * target, const int subty
|
|||||||
|
|
||||||
return (distance < visionsRange) && (target->pos.z == pos.z);
|
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;
|
||||||
|
}
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
class CHero;
|
class CHero;
|
||||||
class CGBoat;
|
class CGBoat;
|
||||||
class CGTownInstance;
|
class CGTownInstance;
|
||||||
|
class CMap;
|
||||||
struct TerrainTile;
|
struct TerrainTile;
|
||||||
struct TurnInfo;
|
struct TurnInfo;
|
||||||
|
|
||||||
@@ -211,6 +212,8 @@ public:
|
|||||||
void updateSkill(SecondarySkill which, int val);
|
void updateSkill(SecondarySkill which, int val);
|
||||||
|
|
||||||
bool hasVisions(const CGObjectInstance * target, const int subtype) const;
|
bool hasVisions(const CGObjectInstance * target, const int subtype) const;
|
||||||
|
/// If this hero perishes, the scenario is failed
|
||||||
|
bool isMissionCritical() const;
|
||||||
|
|
||||||
CGHeroInstance();
|
CGHeroInstance();
|
||||||
virtual ~CGHeroInstance();
|
virtual ~CGHeroInstance();
|
||||||
|
Reference in New Issue
Block a user