1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-06 09:09:40 +02:00

Merge branch 'develop' of https://github.com/vcmi/vcmi into develop

This commit is contained in:
DjWarmonger
2016-02-02 12:03:16 +01:00
40 changed files with 408 additions and 170 deletions

View File

@@ -1,2 +1,2 @@
// Creates the precompiled header
#include "StdInc.h"
#include "StdInc.h"

View File

@@ -1,2 +1,2 @@
// Creates the precompiled header
#include "StdInc.h"
#include "StdInc.h"

View File

@@ -16,4 +16,4 @@ extern "C" DLL_EXPORT void GetAiName(char* name)
extern "C" DLL_EXPORT void GetNewAI(std::shared_ptr<CGlobalAI> &out)
{
out = std::make_shared<CEmptyAI>();
}
}

View File

@@ -1,2 +1,2 @@
// Creates the precompiled header
#include "StdInc.h"
#include "StdInc.h"

View File

@@ -360,7 +360,7 @@ bool canBeEmbarkmentPoint(const TerrainTile *t, bool fromWater)
int3 whereToExplore(HeroPtr h)
{
TimeCheck tc ("where to explore");
int radius = h->getSightRadious();
int radius = h->getSightRadius();
int3 hpos = h->visitablePos();
auto sm = ai->getCachedSectorMap(h);

View File

@@ -2554,7 +2554,7 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h)
int3 VCAI::explorationNewPoint(HeroPtr h)
{
int radius = h->getSightRadious();
int radius = h->getSightRadius();
CCallback * cbp = cb.get();
const CGHeroInstance * hero = h.get();
@@ -2603,7 +2603,7 @@ int3 VCAI::explorationNewPoint(HeroPtr h)
int3 VCAI::explorationDesperate(HeroPtr h)
{
auto sm = getCachedSectorMap(h);
int radius = h->getSightRadious();
int radius = h->getSightRadius();
std::vector<std::vector<int3> > tiles; //tiles[distance_to_fow]
tiles.resize(radius);

View File

@@ -2163,7 +2163,15 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
break;
case RISE_DEMONS:
if (shere && ourStack && !shere->alive())
legalAction = true;
{
if(!(shere->hasBonusOfType(Bonus::UNDEAD)
|| shere->hasBonusOfType(Bonus::NON_LIVING)
|| vstd::contains(shere->state, EBattleStackState::SUMMONED)
|| vstd::contains(shere->state, EBattleStackState::CLONED)
|| shere->hasBonusOfType(Bonus::SIEGE_WEAPON)
))
legalAction = true;
}
break;
}
if (legalAction)
@@ -2320,7 +2328,10 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
break;
case RISE_DEMONS:
cursorType = ECursor::SPELLBOOK;
realizeAction = [=]{ giveCommand(Battle::DAEMON_SUMMONING, myNumber, activeStack->ID); };
realizeAction = [=]
{
giveCommand(Battle::DAEMON_SUMMONING, myNumber, activeStack->ID);
};
break;
case CATAPULT:
cursorFrame = ECursor::COMBAT_SHOOT_CATAPULT;

View File

@@ -112,7 +112,11 @@
"index": 149,
"level": 0,
"faction": "neutral",
"abilities": { "shooter" : { "type" : "SHOOTER" } },
"abilities":
{
"shooter" : { "type" : "SHOOTER" },
"ignoreDefence" : { "type" : "ENEMY_DEFENCE_REDUCTION", "val" : 100 }
},
"graphics" :
{
"animation": "CLCBOW.DEF" // needed to pass validation, never used

View File

@@ -147,8 +147,8 @@
"warMachine" : "ballista",
"moatDamage" : 70,
"moatHexes" : [ 11, 28, 44, 61, 77, 111, 129, 146, 164, 181 ],
// primaryResource not specified so town get both Wood and Ore for resource bonus
"primaryResource": "ore",
"buildings" :
{
"mageGuild1": { "id" : 0 },

View File

@@ -144,10 +144,10 @@
],
"horde" : [ 0, -1 ],
"mageGuild" : 3,
"primaryResource":"ore",
"warMachine" : "firstAidTent",
"moatDamage" : 90,
"moatHexes" : [ 10, 11, 27, 28, 43, 44, 60, 61, 76, 77, 94, 110, 111, 128, 129, 145, 146, 163, 164, 180, 181 ],
// primaryResource not specified so town get both Wood and Ore for resource bonus
"buildings" :
{

View File

@@ -148,10 +148,10 @@
],
"horde" : [ 0, -1 ],
"mageGuild" : 5,
"primaryResource": "ore",
"warMachine" : "firstAidTent",
"moatDamage" : 70,
"moatHexes" : [ 11, 28, 44, 61, 77, 111, 129, 146, 164, 181 ],
// primaryResource not specified so town get both Wood and Ore for resource bonus
"buildings" :
{

View File

@@ -118,7 +118,6 @@
"defaultTavern" : 5,
"tavernVideo" : "TAVERN.BIK",
"guildBackground" : "TPMAGE.bmp",
"primaryResource": "ore",
"townBackground": "TBSTBACK.bmp",
"guildWindow": "TPMAGEST.bmp",
"buildingsIcons": "HALLSTRN.DEF",
@@ -146,6 +145,7 @@
"warMachine" : "ammoCart",
"moatDamage" : 70,
"moatHexes" : [ 11, 28, 44, 61, 77, 111, 129, 146, 164, 181 ],
// primaryResource not specified so town get both Wood and Ore for resource bonus
"buildings" :
{

View File

@@ -469,7 +469,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp
auto handleWarMachine= [&](int side, ArtifactPosition artslot, CreatureID cretype, BattleHex hex)
{
if(heroes[side] && heroes[side]->getArt(artslot))
stacks.push_back(curB->generateNewStack(CStackBasicDescriptor(cretype, 1), !side, SlotID(255), hex));
stacks.push_back(curB->generateNewStack(CStackBasicDescriptor(cretype, 1), !side, SlotID::WAR_MACHINES_SLOT, hex));
};
handleWarMachine(0, ArtifactPosition::MACH1, CreatureID::BALLISTA, 52);
@@ -526,15 +526,15 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp
if (curB->town && curB->town->fortLevel() >= CGTownInstance::CITADEL)
{
// keep tower
CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), false, SlotID(255), -2);
CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), false, SlotID::ARROW_TOWERS_SLOT, -2);
stacks.push_back(stack);
if (curB->town->fortLevel() >= CGTownInstance::CASTLE)
{
// lower tower + upper tower
CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), false, SlotID(255), -4);
CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), false, SlotID::ARROW_TOWERS_SLOT, -4);
stacks.push_back(stack);
stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), false, SlotID(255), -3);
stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), false, SlotID::ARROW_TOWERS_SLOT, -3);
stacks.push_back(stack);
}
@@ -712,7 +712,7 @@ std::shared_ptr<CObstacleInstance> BattleInfo::getObstacleOnTile(BattleHex tile)
BattlefieldBI::BattlefieldBI BattleInfo::battlefieldTypeToBI(BFieldType bfieldType)
{
static const std::map<BFieldType, BattlefieldBI::BattlefieldBI> theMap =
static const std::map<BFieldType, BattlefieldBI::BattlefieldBI> theMap =
{
{BFieldType::CLOVER_FIELD, BattlefieldBI::CLOVER_FIELD},
{BFieldType::CURSED_GROUND, BattlefieldBI::CURSED_GROUND},
@@ -1141,7 +1141,7 @@ bool CStack::ableToRetaliate() const //FIXME: crash after clone is killed
ui8 CStack::counterAttacksTotal() const
{
//after dispell bonus should remain during current round
//after dispell bonus should remain during current round
ui8 val = 1 + valOfBonuses(Bonus::ADDITIONAL_RETALIATION);
vstd::amax(counterAttacksTotalCache, val);
return counterAttacksTotalCache;
@@ -1183,9 +1183,9 @@ ui32 CStack::calculateHealedHealthPoints(ui32 toHeal, const bool resurrect) cons
ui8 CStack::getSpellSchoolLevel(const CSpell * spell, int * outSelectedSchool) const
{
int skill = valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, spell->id));
vstd::abetween(skill, 0, 3);
return skill;
}

View File

@@ -988,10 +988,10 @@ void CGameState::initGrailPosition()
{
logGlobal->debugStream() << "\tPicking grail position";
//pick grail location
if(map->grailPos.x < 0 || map->grailRadious) //grail not set or set within a radius around some place
if(map->grailPos.x < 0 || map->grailRadius) //grail not set or set within a radius around some place
{
if(!map->grailRadious) //radius not given -> anywhere on map
map->grailRadious = map->width * 2;
if(!map->grailRadius) //radius not given -> anywhere on map
map->grailRadius = map->width * 2;
std::vector<int3> allowedPos;
static const int BORDER_WIDTH = 9; // grail must be at least 9 tiles away from border
@@ -1008,7 +1008,7 @@ void CGameState::initGrailPosition()
&& !t.visitable
&& t.terType != ETerrainType::WATER
&& t.terType != ETerrainType::ROCK
&& map->grailPos.dist2dSQ(int3(i, j, k)) <= (map->grailRadious * map->grailRadious))
&& map->grailPos.dist2dSQ(int3(i, j, k)) <= (map->grailRadius * map->grailRadius))
allowedPos.push_back(int3(i,j,k));
}
}
@@ -1638,7 +1638,7 @@ void CGameState::initFogOfWar()
if(!obj || !vstd::contains(elem.second.players, obj->tempOwner)) continue; //not a flagged object
std::unordered_set<int3, ShashInt3> tiles;
getTilesInRange(tiles, obj->getSightCenter(), obj->getSightRadious(), obj->tempOwner, 1);
getTilesInRange(tiles, obj->getSightCenter(), obj->getSightRadius(), obj->tempOwner, 1);
for(int3 tile : tiles)
{
elem.second.fogOfWarMap[tile.x][tile.y][tile.z] = 1;

View File

@@ -646,10 +646,10 @@ void CPathfinder::initializePatrol()
auto state = PATROL_NONE;
if(hero->patrol.patrolling && !getPlayer(hero->tempOwner)->human)
{
if(hero->patrol.patrolRadious)
if(hero->patrol.patrolRadius)
{
state = PATROL_RADIUS;
gs->getTilesInRange(patrolTiles, hero->patrol.initialPos, hero->patrol.patrolRadious, boost::optional<PlayerColor>(), 0, true);
gs->getTilesInRange(patrolTiles, hero->patrol.initialPos, hero->patrol.patrolRadius, boost::optional<PlayerColor>(), 0, true);
}
else
state = PATROL_LOCKED;

View File

@@ -19,6 +19,10 @@
#include "spells/CSpellHandler.h"
const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2);
const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(-3);
const SlotID SlotID::WAR_MACHINES_SLOT = SlotID(-4);
const SlotID SlotID::ARROW_TOWERS_SLOT = SlotID(-5);
const PlayerColor PlayerColor::CANNOT_DETERMINE = PlayerColor(253);
const PlayerColor PlayerColor::UNFLAGGABLE = PlayerColor(254);
const PlayerColor PlayerColor::NEUTRAL = PlayerColor(255);

View File

@@ -236,6 +236,9 @@ class SlotID : public BaseForID<SlotID, si32>
friend class CNonConstInfoCallback;
DLL_LINKAGE static const SlotID COMMANDER_SLOT_PLACEHOLDER;
DLL_LINKAGE static const SlotID SUMMONED_SLOT_PLACEHOLDER; ///<for all summoned creatures, only during battle
DLL_LINKAGE static const SlotID WAR_MACHINES_SLOT; ///<for all war machines during battle
DLL_LINKAGE static const SlotID ARROW_TOWERS_SLOT; ///<for all arrow towers during battle
bool validSlot() const
{
@@ -443,11 +446,11 @@ namespace ESpellCastProblem
namespace ECastingMode
{
enum ECastingMode
enum ECastingMode
{
HERO_CASTING, AFTER_ATTACK_CASTING, //also includes cast before attack
MAGIC_MIRROR, CREATURE_ACTIVE_CASTING, ENCHANTER_CASTING,
SPELL_LIKE_ATTACK,
SPELL_LIKE_ATTACK,
PASSIVE_CASTING//f.e. opening battle spells
};
}

View File

@@ -33,7 +33,7 @@ class CSelector : std::function<bool(const Bonus*)>
public:
CSelector() {}
template<typename T>
CSelector(const T &t, //SFINAE trick -> include this c-tor in overload resolution only if parameter is class
CSelector(const T &t, //SFINAE trick -> include this c-tor in overload resolution only if parameter is class
//(includes functors, lambdas) or function. Without that VC is going mad about ambiguities.
typename std::enable_if < boost::mpl::or_ < std::is_class<T>, std::is_function<T >> ::value>::type *dummy = nullptr)
: TBase(t)
@@ -79,7 +79,7 @@ public:
BONUS_NAME(MORALE) \
BONUS_NAME(LUCK) \
BONUS_NAME(PRIMARY_SKILL) /*uses subtype to pick skill; additional info if set: 1 - only melee, 2 - only distance*/ \
BONUS_NAME(SIGHT_RADIOUS) \
BONUS_NAME(SIGHT_RADIOUS) /*the correct word is RADIUS, but this one's already used in mods */\
BONUS_NAME(MANA_REGENERATION) /*points per turn apart from normal (1 + mysticism)*/ \
BONUS_NAME(FULL_MANA_REGENERATION) /*all mana points are replenished every day*/ \
BONUS_NAME(NONEVIL_ALIGNMENT_MIX) /*good and neutral creatures can be mixed without morale penalty*/ \
@@ -219,7 +219,7 @@ public:
BONUS_NAME(VISIONS) /* subtype - spell level */\
BONUS_NAME(NO_TERRAIN_PENALTY) /* subtype - terrain type */\
/* end of list */
#define BONUS_SOURCE_LIST \
BONUS_SOURCE(ARTIFACT)\
@@ -345,7 +345,7 @@ struct DLL_LINKAGE Bonus
static bool NTurns(const Bonus *hb)
{
return hb->duration & Bonus::N_TURNS;
}
}
static bool OneDay(const Bonus *hb)
{
return hb->duration & Bonus::ONE_DAY;
@@ -754,7 +754,7 @@ public:
: ptr(Ptr)
{
}
CSelector operator()(const T &valueToCompareAgainst) const
{
auto ptr2 = ptr; //We need a COPY because we don't want to reference this (might be outlived by lambda)

View File

@@ -55,4 +55,4 @@ struct SharedMem
delete mr;
boost::interprocess::shared_memory_object::remove("vcmi_memory");
}
};
};

View File

@@ -223,7 +223,7 @@ DLL_LINKAGE void FoWChange::applyGs( CGameState *gs )
case Obj::TOWN:
case Obj::ABANDONED_MINE:
if(vstd::contains(team->players, o->tempOwner)) //check owned observators
gs->getTilesInRange(tilesRevealed, o->getSightCenter(), o->getSightRadious(), o->tempOwner, 1);
gs->getTilesInRange(tilesRevealed, o->getSightCenter(), o->getSightRadius(), o->tempOwner, 1);
break;
}
}
@@ -1057,6 +1057,23 @@ DLL_LINKAGE void NewTurn::applyGs( CGameState *gs )
for(NewTurn::Hero h : heroes) //give mana/movement point
{
CGHeroInstance *hero = gs->getHero(h.id);
if(!hero)
{
// retreated or surrendered hero who has not been reset yet
for(auto& hp : gs->hpool.heroesPool)
{
if(hp.second->id == h.id)
{
hero = hp.second;
break;
}
}
}
if(!hero)
{
logGlobal->errorStream() << "Hero " << h.id << " not found in NewTurn::applyGs";
continue;
}
hero->movement = h.move;
hero->mana = h.mana;
}
@@ -1616,7 +1633,7 @@ DLL_LINKAGE void BattleStackAdded::applyGs(CGameState *gs)
}
CStackBasicDescriptor csbd(creID, amount);
CStack * addedStack = gs->curB->generateNewStack(csbd, attacker, SlotID(255), pos); //TODO: netpacks?
CStack * addedStack = gs->curB->generateNewStack(csbd, attacker, SlotID::SUMMONED_SLOT_PLACEHOLDER, pos); //TODO: netpacks?
if (summoned)
addedStack->state.insert(EBattleStackState::SUMMONED);

View File

@@ -113,4 +113,4 @@ Res::ResourceSet::nziterator::nziterator(const ResourceSet &RS)
if(!valid())
advance();
}
}

View File

@@ -1,2 +1,2 @@
// Creates the precompiled header
#include "StdInc.h"
#include "StdInc.h"

View File

@@ -4,4 +4,4 @@
// This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
// Here you can add specific libraries and macros which are specific to this project.
// Here you can add specific libraries and macros which are specific to this project.

View File

@@ -55,4 +55,4 @@ class DLL_LINKAGE IVCMIDirs
namespace VCMIDirs
{
extern DLL_LINKAGE const IVCMIDirs& get();
}
}

View File

@@ -1067,7 +1067,7 @@ int3 CGHeroInstance::getSightCenter() const
return getPosition(false);
}*/
int CGHeroInstance::getSightRadious() const
int CGHeroInstance::getSightRadius() const
{
return 5 + getSecSkillLevel(SecondarySkill::SCOUTING) + valOfBonuses(Bonus::SIGHT_RADIOUS); //default + scouting
}
@@ -1080,6 +1080,20 @@ si32 CGHeroInstance::manaRegain() const
return 1 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 8) + valOfBonuses(Bonus::MANA_REGENERATION); //1 + Mysticism level
}
si32 CGHeroInstance::getManaNewTurn() const
{
if(visitedTown && visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1))
{
//if hero starts turn in town with mage guild - restore all mana
return std::max(mana, manaLimit());
}
si32 res = mana + manaRegain();
res = std::min(res, manaLimit());
res = std::max(res, mana);
res = std::max(res, 0);
return res;
}
// /**
// * Places an artifact in hero's backpack. If it's a big artifact equips it
// * or discards it if it cannot be equipped.

View File

@@ -76,10 +76,10 @@ public:
struct DLL_LINKAGE Patrol
{
Patrol(){patrolling=false;initialPos=int3();patrolRadious=-1;};
Patrol(){patrolling=false;initialPos=int3();patrolRadius=-1;};
bool patrolling;
int3 initialPos;
ui32 patrolRadious;
ui32 patrolRadius;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & patrolling;
@@ -92,7 +92,7 @@ public:
patrolling = false;
initialPos = int3();
}
h & patrolRadious;
h & patrolRadius;
}
} patrol;
@@ -134,7 +134,7 @@ public:
}
//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)
int getSightRadius() const override; //sight distance (should be used if player-owned structure)
//////////////////////////////////////////////////////////////////////////
int getBoatType() const override; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
@@ -151,6 +151,7 @@ public:
ui32 getLowestCreatureSpeed() const;
int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
si32 getManaNewTurn() const; //calculate how much mana this hero is going to have the next day
int getCurrentLuck(int stack=-1, bool town=false) const;
int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored

View File

@@ -313,7 +313,7 @@ void CGDwelling::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
}
}
int CGTownInstance::getSightRadious() const //returns sight distance
int CGTownInstance::getSightRadius() const //returns sight distance
{
if (subID == ETownType::TOWER)
{
@@ -1109,7 +1109,7 @@ void CGTownInstance::battleFinished(const CGHeroInstance *hero, const BattleResu
FoWChange fw;
fw.player = hero->tempOwner;
fw.mode = 1;
cb->getTilesInRange(fw.tiles, getSightCenter(), getSightRadious(), tempOwner, 1);
cb->getTilesInRange(fw.tiles, getSightCenter(), getSightRadius(), tempOwner, 1);
cb->sendAndApply (&fw);
}
}

View File

@@ -206,7 +206,7 @@ public:
bool passableFor(PlayerColor color) const override;
//int3 getSightCenter() const override; //"center" tile from which the sight distance is calculated
int getSightRadious() const override; //returns sight distance
int getSightRadius() const override; //returns sight distance
int getBoatType() const override; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
void getOutOffsets(std::vector<int3> &offsets) const override; //offsets to obj pos when we boat can be placed. Parameter will be cleared
int getMarketEfficiency() const override; //=market count

View File

@@ -243,7 +243,7 @@ int3 CGObjectInstance::getSightCenter() const
return visitablePos();
}
int CGObjectInstance::getSightRadious() const
int CGObjectInstance::getSightRadius() const
{
return 3;
}

View File

@@ -143,7 +143,7 @@ public:
/// Returns true if player can pass through visitable tiles of this object
virtual bool passableFor(PlayerColor color) const;
/// Range of revealed map around this object, counting from getSightCenter()
virtual int getSightRadious() const;
virtual int getSightRadius() const;
/// returns (x,y,0) offset to a visitable tile of object
virtual int3 getVisitableOffset() const;
/// Called mostly during map randomization to turn random object into a regular one (e.g. "Random Monster" into "Pikeman")

View File

@@ -23,13 +23,13 @@
bool CRewardLimiter::heroAllowed(const CGHeroInstance * hero) const
{
if (dayOfWeek != 0)
if(dayOfWeek != 0)
{
if (IObjectInterface::cb->getDate(Date::DAY_OF_WEEK) != dayOfWeek)
return false;
}
for (auto & reqStack : creatures)
for(auto & reqStack : creatures)
{
size_t count = 0;
for (auto slot : hero->Slots())
@@ -42,25 +42,25 @@ bool CRewardLimiter::heroAllowed(const CGHeroInstance * hero) const
return false;
}
if (!IObjectInterface::cb->getPlayer(hero->tempOwner)->resources.canAfford(resources))
if(!IObjectInterface::cb->getPlayer(hero->tempOwner)->resources.canAfford(resources))
return false;
if (minLevel > hero->level)
if(minLevel > hero->level)
return false;
for (size_t i=0; i<primary.size(); i++)
for(size_t i=0; i<primary.size(); i++)
{
if (primary[i] > hero->getPrimSkillLevel(PrimarySkill::PrimarySkill(i)))
return false;
}
for (auto & skill : secondary)
for(auto & skill : secondary)
{
if (skill.second > hero->getSecSkillLevel(skill.first))
return false;
}
for (auto & art : artifacts)
for(auto & art : artifacts)
{
if (!hero->hasArt(art))
return false;
@@ -73,11 +73,11 @@ std::vector<ui32> CRewardableObject::getAvailableRewards(const CGHeroInstance *
{
std::vector<ui32> ret;
for (size_t i=0; i<info.size(); i++)
for(size_t i=0; i<info.size(); i++)
{
const CVisitInfo & visit = info[i];
if ((visit.limiter.numOfGrants == 0 || visit.numOfGrants < visit.limiter.numOfGrants) // reward has unlimited uses or some are still available
if((visit.limiter.numOfGrants == 0 || visit.numOfGrants < visit.limiter.numOfGrants) // reward has unlimited uses or some are still available
&& visit.limiter.heroAllowed(hero))
{
logGlobal->debugStream() << "Reward " << i << " is allowed";
@@ -87,19 +87,25 @@ std::vector<ui32> CRewardableObject::getAvailableRewards(const CGHeroInstance *
return ret;
}
CVisitInfo CRewardableObject::getVisitInfo(int index, const CGHeroInstance *) const
{
return info[index];
}
void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const
{
auto grantRewardWithMessage = [&](int index) -> void
{
logGlobal->debugStream() << "Granting reward " << index << ". Message says: " << info[index].message.toString();
auto vi = getVisitInfo(index, h);
logGlobal->debugStream() << "Granting reward " << index << ". Message says: " << vi.message.toString();
// show message only if it is not empty
if (!info[index].message.toString().empty())
if (!vi.message.toString().empty())
{
InfoWindow iw;
iw.player = h->tempOwner;
iw.soundID = soundID;
iw.text = info[index].message;
info[index].reward.loadComponents(iw.components);
iw.text = vi.message;
vi.reward.loadComponents(iw.components, h);
cb->showInfoDialog(&iw);
}
// grant reward afterwards. Note that it may remove object
@@ -112,11 +118,11 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const
sd.soundID = soundID;
sd.text = onSelect;
for (auto index : rewards)
sd.components.push_back(info[index].reward.getDisplayedComponent());
sd.components.push_back(getVisitInfo(index, h).reward.getDisplayedComponent(h));
cb->showBlockingDialog(&sd);
};
if (!wasVisited(h))
if(!wasVisited(h))
{
auto rewards = getAvailableRewards(h);
logGlobal->debugStream() << "Visiting object with " << rewards.size() << " possible rewards";
@@ -175,15 +181,15 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const
void CRewardableObject::heroLevelUpDone(const CGHeroInstance *hero) const
{
grantRewardAfterLevelup(info[selectedReward], hero);
grantRewardAfterLevelup(getVisitInfo(selectedReward, hero), hero);
}
void CRewardableObject::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
{
if (answer == 0)
if(answer == 0)
return; // player refused
if (answer > 0 && answer-1 < info.size())
if(answer > 0 && answer-1 < info.size())
{
auto list = getAvailableRewards(hero);
grantReward(list[answer - 1], hero);
@@ -205,7 +211,7 @@ void CRewardableObject::grantReward(ui32 rewardID, const CGHeroInstance * hero)
cb->sendAndApply(&cov);
cb->setObjProperty(id, ObjProperty::REWARD_SELECT, rewardID);
grantRewardBeforeLevelup(info[rewardID], hero);
grantRewardBeforeLevelup(getVisitInfo(rewardID, hero), hero);
}
void CRewardableObject::grantRewardBeforeLevelup(const CVisitInfo & info, const CGHeroInstance * hero) const
@@ -218,7 +224,7 @@ void CRewardableObject::grantRewardBeforeLevelup(const CVisitInfo & info, const
cb->giveResources(hero->tempOwner, info.reward.resources);
for (auto & entry : info.reward.secondary)
for(auto & entry : info.reward.secondary)
{
int current = hero->getSecSkillLevel(entry.first);
if( (current != 0 && current < entry.second) ||
@@ -235,11 +241,11 @@ void CRewardableObject::grantRewardBeforeLevelup(const CVisitInfo & info, const
si64 expToGive = 0;
expToGive += VLC->heroh->reqExp(hero->level+info.reward.gainedLevels) - VLC->heroh->reqExp(hero->level);
expToGive += hero->calculateXp(info.reward.gainedExp);
if (expToGive)
if(expToGive)
cb->changePrimSkill(hero, PrimarySkill::EXPERIENCE, expToGive);
// hero is not blocked by levelup dialog - grant remainer immediately
if (!cb->isVisitCoveredByAnotherQuery(this, hero))
if(!cb->isVisitCoveredByAnotherQuery(this, hero))
{
grantRewardAfterLevelup(info, hero);
}
@@ -247,7 +253,7 @@ void CRewardableObject::grantRewardBeforeLevelup(const CVisitInfo & info, const
void CRewardableObject::grantRewardAfterLevelup(const CVisitInfo & info, const CGHeroInstance * hero) const
{
if (info.reward.manaDiff || info.reward.manaPercentage >= 0)
if(info.reward.manaDiff || info.reward.manaPercentage >= 0)
{
si32 mana = hero->mana;
if (info.reward.manaPercentage >= 0)
@@ -269,7 +275,7 @@ void CRewardableObject::grantRewardAfterLevelup(const CVisitInfo & info, const C
cb->setMovePoints(&smp);
}
for (const Bonus & bonus : info.reward.bonuses)
for(const Bonus & bonus : info.reward.bonuses)
{
assert(bonus.source == Bonus::OBJECT);
assert(bonus.sid == ID);
@@ -280,16 +286,16 @@ void CRewardableObject::grantRewardAfterLevelup(const CVisitInfo & info, const C
cb->giveHeroBonus(&gb);
}
for (ArtifactID art : info.reward.artifacts)
for(ArtifactID art : info.reward.artifacts)
cb->giveHeroNewArtifact(hero, VLC->arth->artifacts[art],ArtifactPosition::FIRST_AVAILABLE);
if (!info.reward.spells.empty())
if(!info.reward.spells.empty())
{
std::set<SpellID> spellsToGive(info.reward.spells.begin(), info.reward.spells.end());
cb->changeSpells(hero, true, spellsToGive);
}
if (!info.reward.creatures.empty())
if(!info.reward.creatures.empty())
{
CCreatureSet creatures;
for (auto & crea : info.reward.creatures)
@@ -300,11 +306,11 @@ void CRewardableObject::grantRewardAfterLevelup(const CVisitInfo & info, const C
onRewardGiven(hero);
if (info.reward.removeObject)
if(info.reward.removeObject)
cb->removeObject(this);
}
bool CRewardableObject::wasVisited (PlayerColor player) const
bool CRewardableObject::wasVisited(PlayerColor player) const
{
switch (visitMode)
{
@@ -326,7 +332,7 @@ bool CRewardableObject::wasVisited (PlayerColor player) const
}
}
bool CRewardableObject::wasVisited (const CGHeroInstance * h) const
bool CRewardableObject::wasVisited(const CGHeroInstance * h) const
{
switch (visitMode)
{
@@ -341,19 +347,24 @@ bool CRewardableObject::wasVisited (const CGHeroInstance * h) const
}
}
void CRewardInfo::loadComponents(std::vector<Component> & comps) const
void CRewardInfo::loadComponents(std::vector<Component> & comps,
const CGHeroInstance * h) const
{
for (auto comp : extraComponents)
comps.push_back(comp);
if (gainedExp) comps.push_back(Component(Component::EXPERIENCE, 0, gainedExp, 0));
if (gainedExp)
{
comps.push_back(Component(
Component::EXPERIENCE, 0, h->calculateXp(gainedExp), 0));
}
if (gainedLevels) comps.push_back(Component(Component::EXPERIENCE, 0, gainedLevels, 0));
if (manaDiff) comps.push_back(Component(Component::PRIM_SKILL, 5, manaDiff, 0));
if (manaDiff) comps.push_back(Component(Component::PRIM_SKILL, 5, manaDiff, 0));
for (size_t i=0; i<primary.size(); i++)
{
if (primary[i] !=0)
if (primary[i] != 0)
comps.push_back(Component(Component::PRIM_SKILL, i, primary[i], 0));
}
@@ -376,10 +387,10 @@ void CRewardInfo::loadComponents(std::vector<Component> & comps) const
}
}
Component CRewardInfo::getDisplayedComponent() const
Component CRewardInfo::getDisplayedComponent(const CGHeroInstance * h) const
{
std::vector<Component> comps;
loadComponents(comps);
loadComponents(comps, h);
assert(!comps.empty());
return comps.front();
}
@@ -422,7 +433,7 @@ void CRewardableObject::setPropertyDer(ui8 what, ui32 val)
void CRewardableObject::newTurn() const
{
if (resetDuration != 0 && cb->getDate(Date::DAY) > 1 && cb->getDate(Date::DAY) % (resetDuration) == 1)
if (resetDuration != 0 && cb->getDate(Date::DAY) > 1 && (cb->getDate(Date::DAY) % resetDuration) == 1)
cb->setObjProperty(id, ObjProperty::REWARD_RESET, 0);
}
@@ -728,30 +739,76 @@ void CGBonusingObject::initObj()
break;
case Obj::STABLES:
configureMessage(info[0], 137, 136, soundBase::STORE);
configureBonusDuration(info[0], Bonus::ONE_WEEK, Bonus::LAND_MOVEMENT, 400, 0);
info[0].reward.movePoints = 400;
//TODO: upgrade champions to cavaliers
/*
bool someUpgradeDone = false;
for (auto i = h->Slots().begin(); i != h->Slots().end(); ++i)
{
if(i->second->type->idNumber == CreatureID::CAVALIER)
{
cb->changeStackType(StackLocation(h, i->first), VLC->creh->creatures[CreatureID::CHAMPION]);
someUpgradeDone = true;
}
}
if (someUpgradeDone)
{
grantMessage.addTxt(MetaString::ADVOB_TXT, 138);
iw.components.push_back(Component(Component::CREATURE,11,0,1));
}*/
break;
}
}
CVisitInfo CGBonusingObject::getVisitInfo(int index, const CGHeroInstance *h) const
{
if(ID == Obj::STABLES)
{
assert(index == 0);
for(auto& slot : h->Slots())
{
if(slot.second->type->idNumber == CreatureID::CAVALIER)
{
CVisitInfo vi(info[0]);
vi.message.clear();
vi.message.addTxt(MetaString::ADVOB_TXT, 138);
vi.reward.extraComponents.push_back(Component(
Component::CREATURE, CreatureID::CHAMPION, 0, 1));
return std::move(vi);
}
}
}
return info[index];
}
void CGBonusingObject::onHeroVisit(const CGHeroInstance *h) const
{
CRewardableObject::onHeroVisit(h);
if(ID == Obj::STABLES)
{
//regardless of whether this hero visited stables or not, cavaliers must be upgraded
for(auto& slot : h->Slots())
{
if(slot.second->type->idNumber == CreatureID::CAVALIER)
{
cb->changeStackType(StackLocation(h, slot.first),
VLC->creh->creatures[CreatureID::CHAMPION]);
}
}
}
}
bool CGBonusingObject::wasVisited(const CGHeroInstance * h) const
{
if(ID == Obj::STABLES)
{
for(auto& slot : h->Slots())
{
if(slot.second->type->idNumber == CreatureID::CAVALIER)
{
// always display the reward message if the hero got cavaliers
return false;
}
}
}
return CRewardableObject::wasVisited(h);
}
void CGBonusingObject::grantReward(ui32 rewardID, const CGHeroInstance * hero) const
{
if(ID == Obj::STABLES && CRewardableObject::wasVisited(hero))
{
// reward message has been displayed - do not give the actual bonus
return;
}
CRewardableObject::grantReward(rewardID, hero);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
CGOnceVisitable::CGOnceVisitable()
@@ -1003,16 +1060,30 @@ void CGVisitableOPW::initObj()
soundID = soundBase::GENIE;
onEmpty.addTxt(MetaString::ADVOB_TXT, 165);
info.resize(2);
info[0].limiter.dayOfWeek = 7; // double amount on sunday
info[0].reward.resources[Res::GOLD] = 1000;
info[1].reward.resources[Res::GOLD] = 500;
info.resize(1);
info[0].reward.resources[Res::GOLD] = 500;
info[0].message.addTxt(MetaString::ADVOB_TXT, 164);
info[1].message.addTxt(MetaString::ADVOB_TXT, 164);
break;
}
}
void CGVisitableOPW::setPropertyDer(ui8 what, ui32 val)
{
if(ID == Obj::WATER_WHEEL && what == ObjProperty::REWARD_RESET)
{
auto& reward = info[0].reward.resources[Res::GOLD];
if(info[0].numOfGrants == 0)
{
reward = 1000;
}
else
{
reward = 500;
}
}
CRewardableObject::setPropertyDer(what, val);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
void CGMagicSpring::initObj()

View File

@@ -104,9 +104,10 @@ public:
/// if set to true, object will be removed after granting reward
bool removeObject;
/// Generates list of components that describes reward
virtual void loadComponents(std::vector<Component> & comps) const;
Component getDisplayedComponent() const;
/// Generates list of components that describes reward for a specific hero
virtual void loadComponents(std::vector<Component> & comps,
const CGHeroInstance * h) const;
Component getDisplayedComponent(const CGHeroInstance * h) const;
CRewardInfo() :
gainedExp(0),
@@ -163,6 +164,7 @@ class DLL_LINKAGE CRewardableObject : public CArmedInstance
/// grants reward to hero
void grantRewardBeforeLevelup(const CVisitInfo & reward, const CGHeroInstance * hero) const;
protected:
/// controls selection of reward granted to player
enum ESelectMode
@@ -184,9 +186,11 @@ protected:
/// filters list of visit info and returns rewards that can be granted to current hero
virtual std::vector<ui32> getAvailableRewards(const CGHeroInstance * hero) const;
void grantReward(ui32 rewardID, const CGHeroInstance * hero) const;
virtual void grantReward(ui32 rewardID, const CGHeroInstance * hero) const;
/// Rewars that can be granted by an object
virtual CVisitInfo getVisitInfo(int index, const CGHeroInstance *h) const;
/// Rewards that can be granted by an object
std::vector<CVisitInfo> info;
/// MetaString's that contain text for messages for specific situations
@@ -215,8 +219,8 @@ public:
std::string getHoverText(const CGHeroInstance * hero) const override;
/// Visitability checks. Note that hero check includes check for hero owner (returns true if object was visited by player)
bool wasVisited (PlayerColor player) const override;
bool wasVisited (const CGHeroInstance * h) const override;
bool wasVisited(PlayerColor player) const override;
bool wasVisited(const CGHeroInstance * h) const override;
/// gives reward to player or ask for choice in case of multiple rewards
void onHeroVisit(const CGHeroInstance *h) const override;
@@ -262,11 +266,20 @@ public:
class DLL_LINKAGE CGBonusingObject : public CRewardableObject //objects giving bonuses to luck/morale/movement
{
protected:
CVisitInfo getVisitInfo(int index, const CGHeroInstance *h) const override;
void grantReward(ui32 rewardID, const CGHeroInstance * hero) const override;
public:
void initObj() override;
CGBonusingObject();
void onHeroVisit(const CGHeroInstance *h) const override;
bool wasVisited(const CGHeroInstance * h) const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CRewardableObject&>(*this);
@@ -306,6 +319,8 @@ public:
CGVisitableOPW();
void setPropertyDer(ui8 what, ui32 val) override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CRewardableObject&>(*this);

View File

@@ -220,7 +220,7 @@ CMapHeader::~CMapHeader()
}
CMap::CMap() : checksum(0), grailPos(-1, -1, -1), grailRadious(0), terrain(nullptr)
CMap::CMap() : checksum(0), grailPos(-1, -1, -1), grailRadius(0), terrain(nullptr)
{
allHeroes.resize(allowedHeroes.size());
allowedAbilities = VLC->heroh->getDefaultAllowedAbilities();

View File

@@ -306,7 +306,7 @@ public:
std::vector<bool> allowedAbilities;
std::list<CMapEvent> events;
int3 grailPos;
int grailRadious;
int grailRadius;
//Central lists of items in game. Position of item in the vectors below is their (instance) id.
std::vector< ConstTransitivePtr<CGObjectInstance> > objects;

View File

@@ -1350,7 +1350,7 @@ void CMapLoaderH3M::readObjects()
case Obj::GRAIL:
{
map->grailPos = objPos;
map->grailRadious = reader.readUInt32();
map->grailRadius = reader.readUInt32();
continue;
}
case Obj::RANDOM_DWELLING: //same as castle + level range
@@ -1650,8 +1650,8 @@ CGObjectInstance * CMapLoaderH3M::readHero(ObjectInstanceID idToBeGiven, const i
nhi->formation = reader.readUInt8();
loadArtifactsOfHero(nhi);
nhi->patrol.patrolRadious = reader.readUInt8();
if(nhi->patrol.patrolRadious == 0xff)
nhi->patrol.patrolRadius = reader.readUInt8();
if(nhi->patrol.patrolRadius == 0xff)
{
nhi->patrol.patrolling = false;
}

View File

@@ -16,4 +16,4 @@
#include "CDefaultSpellMechanics.h"
#include "AdventureSpellMechanics.h"
#include "BattleSpellMechanics.h"
#include "CreatureSpellMechanics.h"
#include "CreatureSpellMechanics.h"

View File

@@ -662,8 +662,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
sendAndApply(&cs);
}
cab1.takeFromArmy(this);
cab2.takeFromArmy(this); //take casualties after battle is deleted
cab1.updateArmy(this);
cab2.updateArmy(this); //take casualties after battle is deleted
//if one hero has lost we will erase him
if(battleResult.data->winner!=0 && hero1)
@@ -1324,6 +1324,25 @@ void CGameHandler::newTurn()
std::map<ui32, ConstTransitivePtr<CGHeroInstance> > pool = gs->hpool.heroesPool;
for(auto& hp : pool)
{
auto hero = hp.second;
if(hero->isInitialized() && hero->stacks.size())
{
// reset retreated or surrendered heroes
auto maxmove = hero->maxMovePoints(true);
// if movement is greater than maxmove, we should decrease it
if(hero->movement != maxmove || hero->mana < hero->manaLimit())
{
NewTurn::Hero hth;
hth.id = hero->id;
hth.move = maxmove;
hth.mana = hero->getManaNewTurn();
n.heroes.insert(hth);
}
}
}
for (auto & elem : gs->players)
{
if(elem.first == PlayerColor::NEUTRAL)
@@ -1351,7 +1370,9 @@ void CGameHandler::newTurn()
banned = h->type->heroClass;
}
else
{
sah.hid[j] = -1;
}
}
sendAndApply(&sah);
@@ -1368,11 +1389,7 @@ void CGameHandler::newTurn()
hth.id = h->id;
// TODO: this code executed when bonuses of previous day not yet updated (this happen in NewTurn::applyGs). See issue 2356
hth.move = h->maxMovePoints(gs->map->getTile(h->getPosition(false)).terType != ETerrainType::WATER, new TurnInfo(h, 1));
if(h->visitedTown && h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1)) //if hero starts turn in town with mage guild
hth.mana = std::max(h->mana, h->manaLimit()); //restore all mana
else
hth.mana = std::max((si32)(0), std::max(h->mana, std::min((si32)(h->mana + h->manaRegain()), h->manaLimit())));
hth.mana = h->getManaNewTurn();
n.heroes.insert(hth);
@@ -1852,7 +1869,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
{
obj->onHeroLeave(h);
}
this->getTilesInRange(tmh.fowRevealed, h->getSightCenter()+(tmh.end-tmh.start), h->getSightRadious(), h->tempOwner, 1);
this->getTilesInRange(tmh.fowRevealed, h->getSightCenter()+(tmh.end-tmh.start), h->getSightRadius(), h->tempOwner, 1);
};
auto doMove = [&](TryMoveHero::EResult result, EGuardLook lookForGuards,
@@ -2719,7 +2736,7 @@ bool CGameHandler::buildStructure( ObjectInstanceID tid, BuildingID requestedID,
FoWChange fw;
fw.player = t->tempOwner;
fw.mode = 1;
getTilesInRange(fw.tiles, t->getSightCenter(), t->getSightRadious(), t->tempOwner, 1);
getTilesInRange(fw.tiles, t->getSightCenter(), t->getSightRadius(), t->tempOwner, 1);
sendAndApply(&fw);
if(t->visitingHero)
@@ -3907,18 +3924,16 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
//TODO: From Strategija:
//Summon Demon is a level 2 spell.
{
StartAction start_action(ba);
sendAndApply(&start_action);
const CStack *summoner = gs->curB->battleGetStackByID(ba.stackNumber),
*destStack = gs->curB->battleGetStackByPos(ba.destinationTile, false);
CreatureID summonedType(summoner->getBonusLocalFirst(Selector::type(Bonus::DAEMON_SUMMONING))->subtype);//in case summoner can summon more than one type of monsters... scream!
BattleStackAdded bsa;
bsa.attacker = summoner->attackerOwned;
bsa.creID = CreatureID(summoner->getBonusLocalFirst(Selector::type(Bonus::DAEMON_SUMMONING))->subtype); //in case summoner can summon more than one type of monsters... scream!
bsa.creID = summonedType;
ui64 risedHp = summoner->count * summoner->valOfBonuses(Bonus::DAEMON_SUMMONING, bsa.creID.toEnum());
ui64 targetHealth = destStack->getCreature()->MaxHealth() * destStack->baseAmount;
ui64 targetHealth = destStack->getCreature()->MaxHealth() * destStack->baseAmount;//todo: ignore AGE effect
ui64 canRiseHp = std::min(targetHealth, risedHp);
ui32 canRiseAmount = canRiseHp / VLC->creh->creatures.at(bsa.creID)->MaxHealth();
@@ -3930,6 +3945,9 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
if (bsa.amount) //there's rare possibility single creature cannot rise desired type
{
StartAction start_action(ba);
sendAndApply(&start_action);
BattleStacksRemoved bsr; //remove body
bsr.stackIDs.insert(destStack->ID);
sendAndApply(&bsr);
@@ -3941,9 +3959,9 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
ssp.val = -1;
ssp.absolute = false;
sendAndApply(&ssp);
}
sendAndApply(&end_action);
sendAndApply(&end_action);
}
break;
}
case Battle::MONSTER_SPELL:
@@ -5805,11 +5823,11 @@ void CGameHandler::changeFogOfWar(int3 center, ui32 radius, PlayerColor player,
auto p = gs->getPlayer(player);
for (auto h : p->heroes)
{
getTilesInRange(observedTiles, h->getSightCenter(), h->getSightRadious(), h->tempOwner, -1);
getTilesInRange(observedTiles, h->getSightCenter(), h->getSightRadius(), h->tempOwner, -1);
}
for (auto t : p->towns)
{
getTilesInRange(observedTiles, t->getSightCenter(), t->getSightRadious(), t->tempOwner, -1);
getTilesInRange(observedTiles, t->getSightCenter(), t->getSightRadius(), t->tempOwner, -1);
}
for (auto tile : observedTiles)
vstd::erase_if_present (tiles, tile);
@@ -5878,7 +5896,8 @@ void CGameHandler::duelFinished()
return;
}
CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat)
CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance * _army, BattleInfo *bat):
army(_army)
{
heroWithDeadCommander = ObjectInstanceID();
@@ -5886,50 +5905,111 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleI
if(color == PlayerColor::UNFLAGGABLE)
color = PlayerColor::NEUTRAL;
auto killStack = [&, this](const SlotID slot, const CStackInstance * instance)
{
StackLocation sl(army, slot);
newStackCounts.push_back(TStackAndItsNewCount(sl, 0));
if(nullptr == instance)
return;
auto c = dynamic_cast <const CCommanderInstance *>(instance);
if (c) //switch commander status to dead
{
auto h = dynamic_cast <const CGHeroInstance *>(army);
if (h && h->commander == c)
heroWithDeadCommander = army->id; //TODO: unify commander handling
}
};
//1. Find removed stacks.
for(const auto & slotInfo : army->stacks)
{
const SlotID slot = slotInfo.first;
const CStackInstance * instance = slotInfo.second;
if(nullptr != instance)//just in case
{
bool found = false;
for(const CStack * sta : bat->stacks)
{
if(sta->base == instance)
{
found = true;
break;
}
}
//stack in this slot was removed == it is dead
if(!found)
killStack(slot, instance);
}
}
for(CStack *st : bat->stacks)
{
if(vstd::contains(st->state, EBattleStackState::SUMMONED)) //don't take into account summoned stacks
if(vstd::contains(st->state, EBattleStackState::SUMMONED)) //don't take into account temporary summoned stacks
continue;
if (st->owner != color) //remove only our stacks
continue;
logGlobal->debugStream() << "Calculating casualties for " << st->nodeName();
//FIXME: this info is also used in BattleInfo::calculateCasualties, refactor
st->count = std::max (0, st->count - st->resurrected);
if (!st->count && !st->base) //we can imagine stacks of war mahcines that are not spawned by artifacts?
if(st->slot == SlotID::ARROW_TOWERS_SLOT)
{
//do nothing
logGlobal->debugStream() << "Ignored arrow towers stack";
}
else if(st->slot == SlotID::WAR_MACHINES_SLOT)
{
auto warMachine = VLC->arth->creatureToMachineID(st->type->idNumber);
//catapult artifact remain even if "creature" killed in siege
if(warMachine != ArtifactID::NONE && warMachine != ArtifactID::CATAPULT)
if(warMachine == ArtifactID::NONE)
{
logGlobal->errorStream() << "Invalid creature in war machine virtual slot: " << st->nodeName();
}
//catapult artifact remain even if "creature" killed in siege
else if(warMachine != ArtifactID::CATAPULT && !st->count)
{
logGlobal->debugStream() << "War machine has been destroyed";
auto hero = dynamic_ptr_cast<CGHeroInstance> (army);
if (hero)
removedWarMachines.push_back (ArtifactLocation(hero, hero->getArtPos(warMachine, true)));
else
logGlobal->errorStream() << "War machine in army without hero";
}
}
if(!army->slotEmpty(st->slot) && st->count < army->getStackCount(st->slot))
else if(st->slot == SlotID::SUMMONED_SLOT_PLACEHOLDER)
{
StackLocation sl(army, st->slot);
if(st->alive())
newStackCounts.push_back(std::pair<StackLocation, int>(sl, st->count));
else
newStackCounts.push_back(std::pair<StackLocation, int>(sl, 0));
}
if (st->base && !st->count)
{
auto c = dynamic_cast <const CCommanderInstance *>(st->base);
if (c) //switch commander status to dead
if(st->alive() && st->count > 0)
{
auto h = dynamic_cast <const CGHeroInstance *>(army);
if (h && h->commander == c)
heroWithDeadCommander = army->id; //TODO: unify commander handling
logGlobal->debugStream() << "Stack has been permanently summoned";
//this stack was permanently summoned
const CreatureID summonedType = st->type->idNumber;
summoned[summonedType] += st->count;
}
}
else if(st->base && !army->slotEmpty(st->slot))
{
if(st->count == 0 || !st->alive())
{
killStack(st->slot, st->base);
logGlobal->debugStream() << "Stack has been destroyed";
}
else if(st->count < army->getStackCount(st->slot))
{
StackLocation sl(army, st->slot);
newStackCounts.push_back(TStackAndItsNewCount(sl, st->count));
}
}
else
{
logGlobal->warnStream() << "Unhandled stack " << st->nodeName();
}
}
}
void CasualtiesAfterBattle::takeFromArmy(CGameHandler *gh)
void CasualtiesAfterBattle::updateArmy(CGameHandler *gh)
{
for(TStackAndItsNewCount &ncount : newStackCounts)
{
@@ -5938,6 +6018,21 @@ void CasualtiesAfterBattle::takeFromArmy(CGameHandler *gh)
else
gh->eraseStack(ncount.first, true);
}
for(auto summoned_iter : summoned)
{
SlotID slot = army->getSlotFor(summoned_iter.first);
if(slot.validSlot())
{
StackLocation location(army, slot);
gh->addToSlot(location, summoned_iter.first.toCreature(), summoned_iter.second);
}
else
{
//even if it will be possible to summon anything permanently it should be checked for free slot
//necromancy is handled separately
gh->complain("No free slot to put summoned creature");
}
}
for (auto al : removedWarMachines)
{
gh->removeArtifact(al);

View File

@@ -69,13 +69,16 @@ public:
struct CasualtiesAfterBattle
{
typedef std::pair<StackLocation, int> TStackAndItsNewCount;
typedef std::map<CreatureID, TQuantity> TSummoned;
enum {ERASE = -1};
const CArmedInstance * army;
std::vector<TStackAndItsNewCount> newStackCounts;
std::vector<ArtifactLocation> removedWarMachines;
ObjectInstanceID heroWithDeadCommander; //TODO: unify stack loactions
TSummoned summoned;
ObjectInstanceID heroWithDeadCommander; //TODO: unify stack locations
CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat);
void takeFromArmy(CGameHandler *gh);
CasualtiesAfterBattle(const CArmedInstance * _army, BattleInfo *bat);
void updateArmy(CGameHandler *gh);
};
class CGameHandler : public IGameCallback, CBattleInfoCallback

View File

@@ -97,4 +97,4 @@ public:
void startListeningThread(CConnection * pc);
};
extern boost::program_options::variables_map cmdLineOptions;
extern boost::program_options::variables_map cmdLineOptions;

View File

@@ -1,2 +1,2 @@
// Creates the precompiled header
#include "StdInc.h"
#include "StdInc.h"