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:
@@ -1,2 +1,2 @@
|
||||
// Creates the precompiled header
|
||||
#include "StdInc.h"
|
||||
#include "StdInc.h"
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Creates the precompiled header
|
||||
#include "StdInc.h"
|
||||
#include "StdInc.h"
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Creates the precompiled header
|
||||
#include "StdInc.h"
|
||||
#include "StdInc.h"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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" :
|
||||
{
|
||||
|
||||
@@ -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" :
|
||||
{
|
||||
|
||||
@@ -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" :
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -55,4 +55,4 @@ struct SharedMem
|
||||
delete mr;
|
||||
boost::interprocess::shared_memory_object::remove("vcmi_memory");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -113,4 +113,4 @@ Res::ResourceSet::nziterator::nziterator(const ResourceSet &RS)
|
||||
|
||||
if(!valid())
|
||||
advance();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Creates the precompiled header
|
||||
#include "StdInc.h"
|
||||
#include "StdInc.h"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -55,4 +55,4 @@ class DLL_LINKAGE IVCMIDirs
|
||||
namespace VCMIDirs
|
||||
{
|
||||
extern DLL_LINKAGE const IVCMIDirs& get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -243,7 +243,7 @@ int3 CGObjectInstance::getSightCenter() const
|
||||
return visitablePos();
|
||||
}
|
||||
|
||||
int CGObjectInstance::getSightRadious() const
|
||||
int CGObjectInstance::getSightRadius() const
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
#include "CDefaultSpellMechanics.h"
|
||||
#include "AdventureSpellMechanics.h"
|
||||
#include "BattleSpellMechanics.h"
|
||||
#include "CreatureSpellMechanics.h"
|
||||
#include "CreatureSpellMechanics.h"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -97,4 +97,4 @@ public:
|
||||
void startListeningThread(CConnection * pc);
|
||||
};
|
||||
|
||||
extern boost::program_options::variables_map cmdLineOptions;
|
||||
extern boost::program_options::variables_map cmdLineOptions;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Creates the precompiled header
|
||||
#include "StdInc.h"
|
||||
#include "StdInc.h"
|
||||
|
||||
Reference in New Issue
Block a user