mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
Merge remote-tracking branch 'origin/develop' into fix_rmg_teams
# Conflicts: # lib/rmg/CMapGenOptions.cpp # lib/rmg/CMapGenOptions.h
This commit is contained in:
commit
c909bd766e
@ -9,6 +9,7 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "../../lib/ArtifactUtils.h"
|
||||
#include "../../lib/UnlockGuard.h"
|
||||
#include "../../lib/mapObjects/MapObjects.h"
|
||||
#include "../../lib/mapObjects/ObjectTemplate.h"
|
||||
@ -663,7 +664,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
||||
// TODO: Find better way to understand it is Chest of Treasures
|
||||
if(hero.validAndSet()
|
||||
&& components.size() == 2
|
||||
&& components.front().id == Component::EComponentType::RESOURCE
|
||||
&& components.front().type == ComponentType::RESOURCE
|
||||
&& (nullkiller->heroManager->getHeroRole(hero) != HeroRole::MAIN
|
||||
|| nullkiller->buildAnalyzer->getGoldPreasure() > MAX_GOLD_PEASURE))
|
||||
{
|
||||
@ -995,21 +996,21 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
|
||||
for(auto p : h->artifactsWorn)
|
||||
{
|
||||
if(p.second.artifact)
|
||||
allArtifacts.push_back(ArtifactLocation(h, p.first));
|
||||
allArtifacts.push_back(ArtifactLocation(h->id, p.first));
|
||||
}
|
||||
}
|
||||
for(auto slot : h->artifactsInBackpack)
|
||||
allArtifacts.push_back(ArtifactLocation(h, h->getArtPos(slot.artifact)));
|
||||
allArtifacts.push_back(ArtifactLocation(h->id, h->getArtPos(slot.artifact)));
|
||||
|
||||
if(otherh)
|
||||
{
|
||||
for(auto p : otherh->artifactsWorn)
|
||||
{
|
||||
if(p.second.artifact)
|
||||
allArtifacts.push_back(ArtifactLocation(otherh, p.first));
|
||||
allArtifacts.push_back(ArtifactLocation(otherh->id, p.first));
|
||||
}
|
||||
for(auto slot : otherh->artifactsInBackpack)
|
||||
allArtifacts.push_back(ArtifactLocation(otherh, otherh->getArtPos(slot.artifact)));
|
||||
allArtifacts.push_back(ArtifactLocation(otherh->id, otherh->getArtPos(slot.artifact)));
|
||||
}
|
||||
//we give stuff to one hero or another, depending on giveStuffToFirstHero
|
||||
|
||||
@ -1021,13 +1022,13 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
|
||||
|
||||
for(auto location : allArtifacts)
|
||||
{
|
||||
if(location.relatedObj() == target && location.slot < ArtifactPosition::AFTER_LAST)
|
||||
if(location.artHolder == target->id && ArtifactUtils::isSlotEquipment(location.slot))
|
||||
continue; //don't reequip artifact we already wear
|
||||
|
||||
if(location.slot == ArtifactPosition::MACH4) // don't attempt to move catapult
|
||||
continue;
|
||||
|
||||
auto s = location.getSlot();
|
||||
auto s = cb->getHero(location.artHolder)->getSlot(location.slot);
|
||||
if(!s || s->locked) //we can't move locks
|
||||
continue;
|
||||
auto artifact = s->artifact;
|
||||
@ -1038,9 +1039,9 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
|
||||
bool emptySlotFound = false;
|
||||
for(auto slot : artifact->artType->getPossibleSlots().at(target->bearerType()))
|
||||
{
|
||||
ArtifactLocation destLocation(target, slot);
|
||||
if(target->isPositionFree(slot) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move
|
||||
if(target->isPositionFree(slot) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move
|
||||
{
|
||||
ArtifactLocation destLocation(target->id, slot);
|
||||
cb->swapArtifacts(location, destLocation); //just put into empty slot
|
||||
emptySlotFound = true;
|
||||
changeMade = true;
|
||||
@ -1054,11 +1055,11 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
|
||||
auto otherSlot = target->getSlot(slot);
|
||||
if(otherSlot && otherSlot->artifact) //we need to exchange artifact for better one
|
||||
{
|
||||
ArtifactLocation destLocation(target, slot);
|
||||
//if that artifact is better than what we have, pick it
|
||||
if(compareArtifacts(artifact, otherSlot->artifact) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move
|
||||
if(compareArtifacts(artifact, otherSlot->artifact) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move
|
||||
{
|
||||
cb->swapArtifacts(location, ArtifactLocation(target, target->getArtPos(otherSlot->artifact)));
|
||||
ArtifactLocation destLocation(target->id, slot);
|
||||
cb->swapArtifacts(location, ArtifactLocation(target->id, target->getArtPos(otherSlot->artifact)));
|
||||
changeMade = true;
|
||||
break;
|
||||
}
|
||||
|
@ -318,7 +318,7 @@ bool BuildAnalyzer::hasAnyBuilding(int32_t alignment, BuildingID bid) const
|
||||
{
|
||||
for(auto tdi : developmentInfos)
|
||||
{
|
||||
if(tdi.town->subID == alignment && tdi.town->hasBuilt(bid))
|
||||
if(tdi.town->getFaction() == alignment && tdi.town->hasBuilt(bid))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ bool needToRecruitHero(const CGTownInstance * startupTown)
|
||||
|
||||
for(auto obj : ai->nullkiller->objectClusterizer->getNearbyObjects())
|
||||
{
|
||||
if((obj->ID == Obj::RESOURCE && obj->subID == GameResID(EGameResID::GOLD))
|
||||
if((obj->ID == Obj::RESOURCE && dynamic_cast<const CGResource *>(obj)->resourceID() == EGameResID::GOLD)
|
||||
|| obj->ID == Obj::TREASURE_CHEST
|
||||
|| obj->ID == Obj::CAMPFIRE
|
||||
|| obj->ID == Obj::WATER_WHEEL)
|
||||
|
@ -24,7 +24,7 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
|
||||
{
|
||||
//this one is not fuzzy anymore, just calculate weighted average
|
||||
|
||||
auto objectInfo = VLC->objtypeh->getHandlerFor(bank->ID, bank->subID)->getObjectInfo(bank->appearance);
|
||||
auto objectInfo = bank->getObjectHandler()->getObjectInfo(bank->appearance);
|
||||
|
||||
CBankInfo * bankInfo = dynamic_cast<CBankInfo *>(objectInfo.get());
|
||||
|
||||
@ -161,10 +161,7 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj)
|
||||
}
|
||||
case Obj::PYRAMID:
|
||||
{
|
||||
if(obj->subID == 0)
|
||||
return estimateBankDanger(dynamic_cast<const CBank *>(obj));
|
||||
else
|
||||
return 0;
|
||||
return estimateBankDanger(dynamic_cast<const CBank *>(obj));
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
|
@ -122,7 +122,7 @@ TResources getCreatureBankResources(const CGObjectInstance * target, const CGHer
|
||||
{
|
||||
//Fixme: unused variable hero
|
||||
|
||||
auto objectInfo = VLC->objtypeh->getHandlerFor(target->ID, target->subID)->getObjectInfo(target->appearance);
|
||||
auto objectInfo = target->getObjectHandler()->getObjectInfo(target->appearance);
|
||||
CBankInfo * bankInfo = dynamic_cast<CBankInfo *>(objectInfo.get());
|
||||
auto resources = bankInfo->getPossibleResourcesReward();
|
||||
TResources result = TResources();
|
||||
@ -139,7 +139,7 @@ TResources getCreatureBankResources(const CGObjectInstance * target, const CGHer
|
||||
|
||||
uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHeroInstance * hero)
|
||||
{
|
||||
auto objectInfo = VLC->objtypeh->getHandlerFor(target->ID, target->subID)->getObjectInfo(target->appearance);
|
||||
auto objectInfo = target->getObjectHandler()->getObjectInfo(target->appearance);
|
||||
CBankInfo * bankInfo = dynamic_cast<CBankInfo *>(objectInfo.get());
|
||||
auto creatures = bankInfo->getPossibleCreaturesReward();
|
||||
uint64_t result = 0;
|
||||
@ -467,14 +467,20 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons
|
||||
switch(target->ID)
|
||||
{
|
||||
case Obj::MINE:
|
||||
return target->subID == GameResID(EGameResID::GOLD)
|
||||
{
|
||||
auto mine = dynamic_cast<const CGMine *>(target);
|
||||
return mine->producedResource == EGameResID::GOLD
|
||||
? 0.5f
|
||||
: 0.4f * getTotalResourceRequirementStrength(target->subID) + 0.1f * getResourceRequirementStrength(target->subID);
|
||||
: 0.4f * getTotalResourceRequirementStrength(mine->producedResource) + 0.1f * getResourceRequirementStrength(mine->producedResource);
|
||||
}
|
||||
|
||||
case Obj::RESOURCE:
|
||||
return target->subID == GameResID(EGameResID::GOLD)
|
||||
{
|
||||
auto resource = dynamic_cast<const CGResource *>(target);
|
||||
return resource->resourceID() == EGameResID::GOLD
|
||||
? 0
|
||||
: 0.2f * getTotalResourceRequirementStrength(target->subID) + 0.4f * getResourceRequirementStrength(target->subID);
|
||||
: 0.2f * getTotalResourceRequirementStrength(resource->resourceID()) + 0.4f * getResourceRequirementStrength(resource->resourceID());
|
||||
}
|
||||
|
||||
case Obj::CREATURE_BANK:
|
||||
{
|
||||
@ -626,12 +632,14 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG
|
||||
const int dailyIncomeMultiplier = 5;
|
||||
const float enemyArmyEliminationGoldRewardRatio = 0.2f;
|
||||
const int32_t heroEliminationBonus = GameConstants::HERO_GOLD_COST / 2;
|
||||
auto isGold = target->subID == GameResID(EGameResID::GOLD); // TODO: other resorces could be sold but need to evaluate market power
|
||||
|
||||
switch(target->ID)
|
||||
{
|
||||
case Obj::RESOURCE:
|
||||
return isGold ? 600 : 100;
|
||||
{
|
||||
auto * res = dynamic_cast<const CGResource*>(target);
|
||||
return res->resourceID() == GameResID::GOLD ? 600 : 100;
|
||||
}
|
||||
case Obj::TREASURE_CHEST:
|
||||
return 1500;
|
||||
case Obj::WATER_WHEEL:
|
||||
@ -640,7 +648,10 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG
|
||||
return dailyIncomeMultiplier * estimateTownIncome(ai->cb.get(), target, hero);
|
||||
case Obj::MINE:
|
||||
case Obj::ABANDONED_MINE:
|
||||
return dailyIncomeMultiplier * (isGold ? 1000 : 75);
|
||||
{
|
||||
auto * mine = dynamic_cast<const CGMine*>(target);
|
||||
return dailyIncomeMultiplier * (mine->producedResource == GameResID::GOLD ? 1000 : 75);
|
||||
}
|
||||
case Obj::MYSTICAL_GARDEN:
|
||||
case Obj::WINDMILL:
|
||||
return 100;
|
||||
@ -1005,7 +1016,7 @@ public:
|
||||
|
||||
uint64_t RewardEvaluator::getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const
|
||||
{
|
||||
if(ai->buildAnalyzer->hasAnyBuilding(town->subID, bi.id))
|
||||
if(ai->buildAnalyzer->hasAnyBuilding(town->getFaction(), bi.id))
|
||||
return 0;
|
||||
|
||||
auto creaturesToUpgrade = ai->armyManager->getTotalCreaturesAvailable(bi.baseCreatureID);
|
||||
|
@ -26,7 +26,6 @@ using crint3 = const int3 &;
|
||||
using crstring = const std::string &;
|
||||
using dwellingContent = std::pair<ui32, std::vector<CreatureID>>;
|
||||
|
||||
const int GOLD_MINE_PRODUCTION = 1000, WOOD_ORE_MINE_PRODUCTION = 2, RESOURCE_MINE_PRODUCTION = 1;
|
||||
const int ACTUAL_RESOURCE_COUNT = 7;
|
||||
const int ALLOWED_ROAMING_HEROES = 8;
|
||||
|
||||
|
@ -406,7 +406,7 @@ float VisitObjEngine::evaluate(Goals::VisitObj & goal)
|
||||
else
|
||||
{
|
||||
MapObjectsEvaluator::getInstance().addObjectData(obj->ID, obj->subID, 0);
|
||||
logGlobal->error("AI met object type it doesn't know - ID: " + std::to_string(obj->ID) + ", subID: " + std::to_string(obj->subID) + " - adding to database with value " + std::to_string(objValue));
|
||||
logGlobal->error("AI met object type it doesn't know - ID: %d, subID: %d - adding to database with value %d ", obj->ID, obj->subID, objValue);
|
||||
}
|
||||
|
||||
setSharedFuzzyVariables(goal);
|
||||
|
@ -66,7 +66,7 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
|
||||
{
|
||||
//this one is not fuzzy anymore, just calculate weighted average
|
||||
|
||||
auto objectInfo = VLC->objtypeh->getHandlerFor(bank->ID, bank->subID)->getObjectInfo(bank->appearance);
|
||||
auto objectInfo = bank->getObjectHandler()->getObjectInfo(bank->appearance);
|
||||
|
||||
CBankInfo * bankInfo = dynamic_cast<CBankInfo *>(objectInfo.get());
|
||||
|
||||
@ -324,15 +324,8 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj, const VCAI * ai)
|
||||
case Obj::DRAGON_UTOPIA:
|
||||
case Obj::SHIPWRECK: //shipwreck
|
||||
case Obj::DERELICT_SHIP: //derelict ship
|
||||
// case Obj::PYRAMID:
|
||||
return estimateBankDanger(dynamic_cast<const CBank *>(obj));
|
||||
case Obj::PYRAMID:
|
||||
{
|
||||
if(obj->subID == 0)
|
||||
return estimateBankDanger(dynamic_cast<const CBank *>(obj));
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
return estimateBankDanger(dynamic_cast<const CBank *>(obj));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -43,10 +43,10 @@ TGoalVec CollectRes::getAllPossibleSubgoals()
|
||||
return resID == GameResID(EGameResID::GOLD);
|
||||
break;
|
||||
case Obj::RESOURCE:
|
||||
return obj->subID == resID;
|
||||
return dynamic_cast<const CGResource*>(obj)->resourceID() == GameResID(resID);
|
||||
break;
|
||||
case Obj::MINE:
|
||||
return (obj->subID == resID &&
|
||||
return (dynamic_cast<const CGMine*>(obj)->producedResource == GameResID(resID) &&
|
||||
(cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines
|
||||
break;
|
||||
case Obj::CAMPFIRE:
|
||||
|
@ -88,7 +88,7 @@ TGoalVec GatherTroops::getAllPossibleSubgoals()
|
||||
}
|
||||
|
||||
auto creature = VLC->creatures()->getByIndex(objid);
|
||||
if(t->subID == creature->getFaction()) //TODO: how to force AI to build unupgraded creatures? :O
|
||||
if(t->getFaction() == creature->getFaction()) //TODO: how to force AI to build unupgraded creatures? :O
|
||||
{
|
||||
auto tryFindCreature = [&]() -> std::optional<std::vector<CreatureID>>
|
||||
{
|
||||
|
@ -190,7 +190,7 @@ Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet
|
||||
if(isBlockedBorderGate(firstTileToGet))
|
||||
{
|
||||
//FIXME: this way we'll not visit gate and activate quest :?
|
||||
return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTile(firstTileToGet)->visitableObjects.back()->subID));
|
||||
return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTile(firstTileToGet)->visitableObjects.back()->getObjTypeIndex()));
|
||||
}
|
||||
|
||||
auto topObj = cb->getTopObj(firstTileToGet);
|
||||
|
@ -59,19 +59,7 @@ TResources ResourceManager::estimateIncome() const
|
||||
if (obj->ID == Obj::MINE)
|
||||
{
|
||||
auto mine = dynamic_cast<const CGMine*>(obj);
|
||||
switch (mine->producedResource.toEnum())
|
||||
{
|
||||
case EGameResID::WOOD:
|
||||
case EGameResID::ORE:
|
||||
ret[obj->subID] += WOOD_ORE_MINE_PRODUCTION;
|
||||
break;
|
||||
case EGameResID::GOLD:
|
||||
ret[EGameResID::GOLD] += GOLD_MINE_PRODUCTION;
|
||||
break;
|
||||
default:
|
||||
ret[obj->subID] += RESOURCE_MINE_PRODUCTION;
|
||||
break;
|
||||
}
|
||||
ret += mine->dailyIncome();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "BuildingManager.h"
|
||||
#include "Goals/Goals.h"
|
||||
|
||||
#include "../../lib/ArtifactUtils.h"
|
||||
#include "../../lib/UnlockGuard.h"
|
||||
#include "../../lib/mapObjects/MapObjects.h"
|
||||
#include "../../lib/mapObjects/ObjectTemplate.h"
|
||||
@ -1166,21 +1167,21 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
|
||||
for(auto p : h->artifactsWorn)
|
||||
{
|
||||
if(p.second.artifact)
|
||||
allArtifacts.push_back(ArtifactLocation(h, p.first));
|
||||
allArtifacts.push_back(ArtifactLocation(h->id, p.first));
|
||||
}
|
||||
}
|
||||
for(auto slot : h->artifactsInBackpack)
|
||||
allArtifacts.push_back(ArtifactLocation(h, h->getArtPos(slot.artifact)));
|
||||
allArtifacts.push_back(ArtifactLocation(h->id, h->getArtPos(slot.artifact)));
|
||||
|
||||
if(otherh)
|
||||
{
|
||||
for(auto p : otherh->artifactsWorn)
|
||||
{
|
||||
if(p.second.artifact)
|
||||
allArtifacts.push_back(ArtifactLocation(otherh, p.first));
|
||||
allArtifacts.push_back(ArtifactLocation(otherh->id, p.first));
|
||||
}
|
||||
for(auto slot : otherh->artifactsInBackpack)
|
||||
allArtifacts.push_back(ArtifactLocation(otherh, otherh->getArtPos(slot.artifact)));
|
||||
allArtifacts.push_back(ArtifactLocation(otherh->id, otherh->getArtPos(slot.artifact)));
|
||||
}
|
||||
//we give stuff to one hero or another, depending on giveStuffToFirstHero
|
||||
|
||||
@ -1195,10 +1196,10 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
|
||||
if(location.slot == ArtifactPosition::MACH4 || location.slot == ArtifactPosition::SPELLBOOK)
|
||||
continue; // don't attempt to move catapult and spellbook
|
||||
|
||||
if(location.relatedObj() == target && location.slot < ArtifactPosition::AFTER_LAST)
|
||||
if(location.artHolder == target->id && ArtifactUtils::isSlotEquipment(location.slot))
|
||||
continue; //don't reequip artifact we already wear
|
||||
|
||||
auto s = location.getSlot();
|
||||
auto s = cb->getHero(location.artHolder)->getSlot(location.slot);
|
||||
if(!s || s->locked) //we can't move locks
|
||||
continue;
|
||||
auto artifact = s->artifact;
|
||||
@ -1209,9 +1210,9 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
|
||||
bool emptySlotFound = false;
|
||||
for(auto slot : artifact->artType->getPossibleSlots().at(target->bearerType()))
|
||||
{
|
||||
ArtifactLocation destLocation(target, slot);
|
||||
if(target->isPositionFree(slot) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move
|
||||
if(target->isPositionFree(slot) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move
|
||||
{
|
||||
ArtifactLocation destLocation(target->id, slot);
|
||||
cb->swapArtifacts(location, destLocation); //just put into empty slot
|
||||
emptySlotFound = true;
|
||||
changeMade = true;
|
||||
@ -1225,11 +1226,11 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
|
||||
auto otherSlot = target->getSlot(slot);
|
||||
if(otherSlot && otherSlot->artifact) //we need to exchange artifact for better one
|
||||
{
|
||||
ArtifactLocation destLocation(target, slot);
|
||||
//if that artifact is better than what we have, pick it
|
||||
if(compareArtifacts(artifact, otherSlot->artifact) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move
|
||||
if(compareArtifacts(artifact, otherSlot->artifact) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move
|
||||
{
|
||||
cb->swapArtifacts(location, ArtifactLocation(target, target->getArtPos(otherSlot->artifact)));
|
||||
ArtifactLocation destLocation(target->id, slot);
|
||||
cb->swapArtifacts(location, ArtifactLocation(target->id, target->getArtPos(otherSlot->artifact)));
|
||||
changeMade = true;
|
||||
break;
|
||||
}
|
||||
@ -1759,11 +1760,11 @@ void VCAI::addVisitableObj(const CGObjectInstance * obj)
|
||||
CGTeleport::addToChannel(knownTeleportChannels, teleportObj);
|
||||
}
|
||||
|
||||
const CGObjectInstance * VCAI::lookForArt(int aid) const
|
||||
const CGObjectInstance * VCAI::lookForArt(ArtifactID aid) const
|
||||
{
|
||||
for(const CGObjectInstance * obj : ai->visitableObjs)
|
||||
{
|
||||
if(obj->ID == Obj::ARTIFACT && obj->subID == aid)
|
||||
if(obj->ID == Obj::ARTIFACT && dynamic_cast<const CGArtifact *>(obj)->getArtifact() == aid)
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -251,7 +251,7 @@ public:
|
||||
void retrieveVisitableObjs();
|
||||
virtual std::vector<const CGObjectInstance *> getFlaggedObjects() const;
|
||||
|
||||
const CGObjectInstance * lookForArt(int aid) const;
|
||||
const CGObjectInstance * lookForArt(ArtifactID aid) const;
|
||||
bool isAccessible(const int3 & pos) const;
|
||||
HeroPtr getHeroWithGrail() const;
|
||||
|
||||
|
@ -265,7 +265,7 @@ void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroIn
|
||||
assert(townOrTavern);
|
||||
assert(hero);
|
||||
|
||||
HireHero pack(HeroTypeID(hero->subID), townOrTavern->id);
|
||||
HireHero pack(hero->getHeroType(), townOrTavern->id);
|
||||
pack.player = *player;
|
||||
sendRequest(&pack);
|
||||
}
|
||||
|
@ -293,7 +293,7 @@ void CPlayerInterface::yourTurn(QueryID queryID)
|
||||
std::string msg = CGI->generaltexth->allTexts[13];
|
||||
boost::replace_first(msg, "%s", cb->getStartInfo()->playerInfos.find(playerID)->second.name);
|
||||
std::vector<std::shared_ptr<CComponent>> cmp;
|
||||
cmp.push_back(std::make_shared<CComponent>(CComponent::flag, playerID.getNum(), 0));
|
||||
cmp.push_back(std::make_shared<CComponent>(ComponentType::FLAG, playerID));
|
||||
showInfoDialog(msg, cmp);
|
||||
}
|
||||
else
|
||||
@ -326,7 +326,7 @@ void CPlayerInterface::acceptTurn(QueryID queryID)
|
||||
auto playerColor = *cb->getPlayerID();
|
||||
|
||||
std::vector<Component> components;
|
||||
components.emplace_back(Component::EComponentType::FLAG, playerColor.getNum(), 0, 0);
|
||||
components.emplace_back(ComponentType::FLAG, playerColor);
|
||||
MetaString text;
|
||||
|
||||
const auto & optDaysWithoutCastle = cb->getPlayerState(playerColor)->daysWithoutCastle;
|
||||
@ -1228,7 +1228,7 @@ void CPlayerInterface::showArtifactAssemblyDialog(const Artifact * artifact, con
|
||||
text += boost::str(boost::format(CGI->generaltexth->allTexts[732]) % assembledArtifact->getNameTranslated());
|
||||
|
||||
// Picture of assembled artifact at bottom.
|
||||
auto sc = std::make_shared<CComponent>(CComponent::artifact, assembledArtifact->getIndex(), 0);
|
||||
auto sc = std::make_shared<CComponent>(ComponentType::ARTIFACT, assembledArtifact->getId());
|
||||
scs.push_back(sc);
|
||||
}
|
||||
else
|
||||
@ -1441,7 +1441,7 @@ void CPlayerInterface::playerBlocked(int reason, bool start)
|
||||
std::string msg = CGI->generaltexth->translate("vcmi.adventureMap.playerAttacked");
|
||||
boost::replace_first(msg, "%s", cb->getStartInfo()->playerInfos.find(playerID)->second.name);
|
||||
std::vector<std::shared_ptr<CComponent>> cmp;
|
||||
cmp.push_back(std::make_shared<CComponent>(CComponent::flag, playerID.getNum(), 0));
|
||||
cmp.push_back(std::make_shared<CComponent>(ComponentType::FLAG, playerID));
|
||||
makingTurn = true; //workaround for stiff showInfoDialog implementation
|
||||
showInfoDialog(msg, cmp);
|
||||
makingTurn = false;
|
||||
@ -1753,8 +1753,7 @@ void CPlayerInterface::requestReturningToMainMenu(bool won)
|
||||
|
||||
void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al)
|
||||
{
|
||||
auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
|
||||
if(hero)
|
||||
if(auto hero = cb->getHero(al.artHolder))
|
||||
{
|
||||
auto art = hero->getArt(al.slot);
|
||||
if(art == nullptr)
|
||||
@ -1770,15 +1769,13 @@ void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al)
|
||||
void CPlayerInterface::artifactPut(const ArtifactLocation &al)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
|
||||
adventureInt->onHeroChanged(hero);
|
||||
adventureInt->onHeroChanged(cb->getHero(al.artHolder));
|
||||
}
|
||||
|
||||
void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
|
||||
adventureInt->onHeroChanged(hero);
|
||||
adventureInt->onHeroChanged(cb->getHero(al.artHolder));
|
||||
|
||||
for(auto artWin : GH.windows().findWindows<CArtifactHolder>())
|
||||
artWin->artifactRemoved(al);
|
||||
@ -1789,8 +1786,7 @@ void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
|
||||
void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
auto hero = std::visit(HeroObjectRetriever(), dst.artHolder);
|
||||
adventureInt->onHeroChanged(hero);
|
||||
adventureInt->onHeroChanged(cb->getHero(dst.artHolder));
|
||||
|
||||
bool redraw = true;
|
||||
// If a bulk transfer has arrived, then redrawing only the last art movement.
|
||||
@ -1815,8 +1811,7 @@ void CPlayerInterface::bulkArtMovementStart(size_t numOfArts)
|
||||
void CPlayerInterface::artifactAssembled(const ArtifactLocation &al)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
|
||||
adventureInt->onHeroChanged(hero);
|
||||
adventureInt->onHeroChanged(cb->getHero(al.artHolder));
|
||||
|
||||
for(auto artWin : GH.windows().findWindows<CArtifactHolder>())
|
||||
artWin->artifactAssembled(al);
|
||||
@ -1825,8 +1820,7 @@ void CPlayerInterface::artifactAssembled(const ArtifactLocation &al)
|
||||
void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
auto hero = std::visit(HeroObjectRetriever(), al.artHolder);
|
||||
adventureInt->onHeroChanged(hero);
|
||||
adventureInt->onHeroChanged(cb->getHero(al.artHolder));
|
||||
|
||||
for(auto artWin : GH.windows().findWindows<CArtifactHolder>())
|
||||
artWin->artifactDisassembled(al);
|
||||
|
@ -359,7 +359,7 @@ void HeroMovementController::moveOnce(const CGHeroInstance * h, const CGPath & p
|
||||
{
|
||||
stopMovementSound();
|
||||
logGlobal->trace("Requesting hero teleportation to %s", nextNode.coord.toString());
|
||||
LOCPLINT->cb->moveHero(h, nextCoord, false);
|
||||
LOCPLINT->cb->moveHero(h, h->pos, false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -267,14 +267,14 @@ void ApplyClientNetPackVisitor::visitBulkSmartRebalanceStacks(BulkSmartRebalance
|
||||
|
||||
void ApplyClientNetPackVisitor::visitPutArtifact(PutArtifact & pack)
|
||||
{
|
||||
callInterfaceIfPresent(cl, pack.al.owningPlayer(), &IGameEventsReceiver::artifactPut, pack.al);
|
||||
callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactPut, pack.al);
|
||||
if(pack.askAssemble)
|
||||
callInterfaceIfPresent(cl, pack.al.owningPlayer(), &IGameEventsReceiver::askToAssembleArtifact, pack.al);
|
||||
callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::askToAssembleArtifact, pack.al);
|
||||
}
|
||||
|
||||
void ApplyClientNetPackVisitor::visitEraseArtifact(EraseArtifact & pack)
|
||||
{
|
||||
callInterfaceIfPresent(cl, pack.al.owningPlayer(), &IGameEventsReceiver::artifactRemoved, pack.al);
|
||||
callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactRemoved, pack.al);
|
||||
}
|
||||
|
||||
void ApplyClientNetPackVisitor::visitMoveArtifact(MoveArtifact & pack)
|
||||
@ -286,9 +286,9 @@ void ApplyClientNetPackVisitor::visitMoveArtifact(MoveArtifact & pack)
|
||||
callInterfaceIfPresent(cl, player, &IGameEventsReceiver::askToAssembleArtifact, pack.dst);
|
||||
};
|
||||
|
||||
moveArtifact(pack.src.owningPlayer());
|
||||
if(pack.src.owningPlayer() != pack.dst.owningPlayer())
|
||||
moveArtifact(pack.dst.owningPlayer());
|
||||
moveArtifact(cl.getOwner(pack.src.artHolder));
|
||||
if(cl.getOwner(pack.src.artHolder) != cl.getOwner(pack.dst.artHolder))
|
||||
moveArtifact(cl.getOwner(pack.dst.artHolder));
|
||||
|
||||
cl.invalidatePaths(); // hero might have equipped/unequipped Angel Wings
|
||||
}
|
||||
@ -306,8 +306,8 @@ void ApplyClientNetPackVisitor::visitBulkMoveArtifacts(BulkMoveArtifacts & pack)
|
||||
}
|
||||
};
|
||||
|
||||
auto srcOwner = std::get<ConstTransitivePtr<CGHeroInstance>>(pack.srcArtHolder)->tempOwner;
|
||||
auto dstOwner = std::get<ConstTransitivePtr<CGHeroInstance>>(pack.dstArtHolder)->tempOwner;
|
||||
auto srcOwner = cl.getOwner(pack.srcArtHolder);
|
||||
auto dstOwner = cl.getOwner(pack.dstArtHolder);
|
||||
|
||||
// Begin a session of bulk movement of arts. It is not necessary but useful for the client optimization.
|
||||
callInterfaceIfPresent(cl, srcOwner, &IGameEventsReceiver::bulkArtMovementStart, pack.artsPack0.size() + pack.artsPack1.size());
|
||||
@ -321,14 +321,14 @@ void ApplyClientNetPackVisitor::visitBulkMoveArtifacts(BulkMoveArtifacts & pack)
|
||||
|
||||
void ApplyClientNetPackVisitor::visitAssembledArtifact(AssembledArtifact & pack)
|
||||
{
|
||||
callInterfaceIfPresent(cl, pack.al.owningPlayer(), &IGameEventsReceiver::artifactAssembled, pack.al);
|
||||
callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactAssembled, pack.al);
|
||||
|
||||
cl.invalidatePaths(); // hero might have equipped/unequipped Angel Wings
|
||||
}
|
||||
|
||||
void ApplyClientNetPackVisitor::visitDisassembledArtifact(DisassembledArtifact & pack)
|
||||
{
|
||||
callInterfaceIfPresent(cl, pack.al.owningPlayer(), &IGameEventsReceiver::artifactDisassembled, pack.al);
|
||||
callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactDisassembled, pack.al);
|
||||
|
||||
cl.invalidatePaths(); // hero might have equipped/unequipped Angel Wings
|
||||
}
|
||||
@ -604,7 +604,7 @@ void ApplyClientNetPackVisitor::visitSetHeroesInTown(SetHeroesInTown & pack)
|
||||
void ApplyClientNetPackVisitor::visitHeroRecruited(HeroRecruited & pack)
|
||||
{
|
||||
CGHeroInstance *h = gs.map->heroesOnMap.back();
|
||||
if(h->subID != pack.hid)
|
||||
if(h->getHeroType() != pack.hid)
|
||||
{
|
||||
logNetwork->error("Something wrong with hero recruited!");
|
||||
}
|
||||
|
@ -376,47 +376,51 @@ void CInfoBar::pushComponents(const std::vector<Component> & components, std::st
|
||||
std::array<std::pair<std::vector<Component>, int>, 10> reward_map;
|
||||
for(const auto & c : components)
|
||||
{
|
||||
switch(c.id)
|
||||
switch(c.type)
|
||||
{
|
||||
case Component::EComponentType::PRIM_SKILL:
|
||||
case Component::EComponentType::EXPERIENCE:
|
||||
case ComponentType::PRIM_SKILL:
|
||||
case ComponentType::EXPERIENCE:
|
||||
case ComponentType::LEVEL:
|
||||
case ComponentType::MANA:
|
||||
reward_map.at(0).first.push_back(c);
|
||||
reward_map.at(0).second = 8; //At most 8, cannot be more
|
||||
break;
|
||||
case Component::EComponentType::SEC_SKILL:
|
||||
case ComponentType::SEC_SKILL:
|
||||
reward_map.at(1).first.push_back(c);
|
||||
reward_map.at(1).second = 4; //At most 4
|
||||
break;
|
||||
case Component::EComponentType::SPELL:
|
||||
case ComponentType::SPELL:
|
||||
reward_map.at(2).first.push_back(c);
|
||||
reward_map.at(2).second = 4; //At most 4
|
||||
break;
|
||||
case Component::EComponentType::ARTIFACT:
|
||||
case ComponentType::ARTIFACT:
|
||||
case ComponentType::SPELL_SCROLL:
|
||||
reward_map.at(3).first.push_back(c);
|
||||
reward_map.at(3).second = 4; //At most 4, too long names
|
||||
break;
|
||||
case Component::EComponentType::CREATURE:
|
||||
case ComponentType::CREATURE:
|
||||
reward_map.at(4).first.push_back(c);
|
||||
reward_map.at(4).second = 4; //At most 4, too long names
|
||||
break;
|
||||
case Component::EComponentType::RESOURCE:
|
||||
case ComponentType::RESOURCE:
|
||||
case ComponentType::RESOURCE_PER_DAY:
|
||||
reward_map.at(5).first.push_back(c);
|
||||
reward_map.at(5).second = 7; //At most 7
|
||||
break;
|
||||
case Component::EComponentType::MORALE:
|
||||
case Component::EComponentType::LUCK:
|
||||
case ComponentType::MORALE:
|
||||
case ComponentType::LUCK:
|
||||
reward_map.at(6).first.push_back(c);
|
||||
reward_map.at(6).second = 2; //At most 2 - 1 for morale + 1 for luck
|
||||
break;
|
||||
case Component::EComponentType::BUILDING:
|
||||
case ComponentType::BUILDING:
|
||||
reward_map.at(7).first.push_back(c);
|
||||
reward_map.at(7).second = 1; //At most 1 - only large icons available AFAIK
|
||||
break;
|
||||
case Component::EComponentType::HERO_PORTRAIT:
|
||||
case ComponentType::HERO_PORTRAIT:
|
||||
reward_map.at(8).first.push_back(c);
|
||||
reward_map.at(8).second = 1; //I do not think than we even can get more than 1 hero
|
||||
break;
|
||||
case Component::EComponentType::FLAG:
|
||||
case ComponentType::FLAG:
|
||||
reward_map.at(9).first.push_back(c);
|
||||
reward_map.at(9).second = 1; //I do not think than we even can get more than 1 player in notification
|
||||
break;
|
||||
|
@ -905,7 +905,7 @@ void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterS
|
||||
const spells::Caster * BattleActionsController::getCurrentSpellcaster() const
|
||||
{
|
||||
if (heroSpellToCast)
|
||||
return owner.getActiveHero();
|
||||
return owner.currentHero();
|
||||
else
|
||||
return owner.stacksController->getActiveStack();
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/battle/CObstacleInstance.h"
|
||||
#include "../../lib/ObstacleHandler.h"
|
||||
#include "../../lib/serializer/JsonDeserializer.h"
|
||||
|
||||
BattleObstacleController::BattleObstacleController(BattleInterface & owner):
|
||||
owner(owner),
|
||||
@ -77,7 +78,14 @@ void BattleObstacleController::obstacleRemoved(const std::vector<ObstacleChanges
|
||||
continue;
|
||||
}
|
||||
|
||||
auto animation = GH.renderHandler().loadAnimation(AnimationPath::fromJson(obstacle["appearAnimation"]));
|
||||
AnimationPath animationPath;
|
||||
JsonDeserializer ser(nullptr, obstacle);
|
||||
ser.serializeStruct("appearAnimation", animationPath);
|
||||
|
||||
if(animationPath.empty())
|
||||
continue;
|
||||
|
||||
auto animation = GH.renderHandler().loadAnimation(animationPath);
|
||||
animation->preload();
|
||||
|
||||
auto first = animation->getImage(0, 0);
|
||||
@ -88,7 +96,7 @@ void BattleObstacleController::obstacleRemoved(const std::vector<ObstacleChanges
|
||||
// -> if we know how to blit obstacle, let's blit the effect in the same place
|
||||
Point whereTo = getObstaclePosition(first, obstacle);
|
||||
//AFAIK, in H3 there is no sound of obstacle removal
|
||||
owner.stacksController->addNewAnim(new EffectAnimation(owner, AnimationPath::fromJson(obstacle["appearAnimation"]), whereTo, obstacle["position"].Integer(), 0, true));
|
||||
owner.stacksController->addNewAnim(new EffectAnimation(owner, animationPath, whereTo, obstacle["position"].Integer(), 0, true));
|
||||
|
||||
obstacleAnimations.erase(oi.id);
|
||||
//so when multiple obstacles are removed, they show up one after another
|
||||
|
@ -161,7 +161,7 @@ void CBonusSelection::createBonusesIcons()
|
||||
break;
|
||||
case CampaignBonusType::BUILDING:
|
||||
{
|
||||
int faction = -1;
|
||||
FactionID faction;
|
||||
for(auto & elem : CSH->si->playerInfos)
|
||||
{
|
||||
if(elem.second.isControlledByHuman())
|
||||
|
@ -209,7 +209,6 @@ void InfoCard::changeSelection()
|
||||
return;
|
||||
|
||||
labelSaveDate->setText(mapInfo->date);
|
||||
labelMapSize->setText(std::to_string(mapInfo->mapHeader->width) + "x" + std::to_string(mapInfo->mapHeader->height));
|
||||
mapName->setText(mapInfo->getNameTranslated());
|
||||
mapDescription->setText(mapInfo->getDescriptionTranslated());
|
||||
|
||||
@ -220,8 +219,11 @@ void InfoCard::changeSelection()
|
||||
if(SEL->screenType == ESelectionScreen::campaignList)
|
||||
return;
|
||||
|
||||
iconsMapSizes->setFrame(mapInfo->getMapSizeIconId());
|
||||
const CMapHeader * header = mapInfo->mapHeader.get();
|
||||
|
||||
labelMapSize->setText(std::to_string(header->width) + "x" + std::to_string(header->height));
|
||||
iconsMapSizes->setFrame(mapInfo->getMapSizeIconId());
|
||||
|
||||
iconsVictoryCondition->setFrame(header->victoryIconIndex);
|
||||
labelVictoryConditionText->setText(header->victoryMessage.toString());
|
||||
iconsLossCondition->setFrame(header->defeatIconIndex);
|
||||
|
@ -571,7 +571,7 @@ void OptionsTab::CPlayerOptionTooltipBox::genTownWindow()
|
||||
for(auto & elem : town->creatures)
|
||||
{
|
||||
if(!elem.empty())
|
||||
components.push_back(std::make_shared<CComponent>(CComponent::creature, elem.front(), 0, CComponent::tiny));
|
||||
components.push_back(std::make_shared<CComponent>(ComponentType::CREATURE, elem.front(), 0, CComponent::tiny));
|
||||
}
|
||||
boxAssociatedCreatures = std::make_shared<CComponentBox>(components, Rect(10, 140, pos.w - 20, 140));
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ int CHighScoreInputScreen::addEntry(std::string text) {
|
||||
auto sortFunctor = [](const JsonNode & left, const JsonNode & right)
|
||||
{
|
||||
if(left["points"].Integer() == right["points"].Integer())
|
||||
return left["posFlag"].Integer() > right["posFlag"].Integer();
|
||||
return left["posFlag"].Bool() > right["posFlag"].Bool();
|
||||
return left["points"].Integer() > right["points"].Integer();
|
||||
};
|
||||
|
||||
@ -325,7 +325,6 @@ void CHighScoreInputScreen::deactivate()
|
||||
{
|
||||
CCS->videoh->close();
|
||||
CCS->soundh->stopSound(videoSoundHandle);
|
||||
CIntObject::deactivate();
|
||||
}
|
||||
|
||||
void CHighScoreInputScreen::clickPressed(const Point & cursorPosition)
|
||||
@ -361,7 +360,7 @@ void CHighScoreInputScreen::keyPressed(EShortcut key)
|
||||
}
|
||||
|
||||
CHighScoreInput::CHighScoreInput(std::string playerName, std::function<void(std::string text)> readyCB)
|
||||
: CWindowObject(0, ImagePath::builtin("HIGHNAME")), ready(readyCB)
|
||||
: CWindowObject(NEEDS_ANIMATED_BACKGROUND, ImagePath::builtin("HIGHNAME")), ready(readyCB)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
|
@ -427,11 +427,7 @@ size_t MapRendererWorldViewContext::overlayImageIndex(const int3 & coordinates)
|
||||
if(!object->visitableAt(coordinates.x, coordinates.y))
|
||||
continue;
|
||||
|
||||
ObjectPosInfo info;
|
||||
info.pos = coordinates;
|
||||
info.id = object->ID;
|
||||
info.subId = object->subID;
|
||||
info.owner = object->tempOwner;
|
||||
ObjectPosInfo info(object);
|
||||
|
||||
size_t iconIndex = selectOverlayImageForObject(info);
|
||||
|
||||
|
@ -204,11 +204,19 @@ void CAnimation::printError(size_t frame, size_t group, std::string type) const
|
||||
|
||||
CAnimation::CAnimation(const AnimationPath & Name):
|
||||
name(boost::starts_with(Name.getName(), "SPRITES") ? Name : Name.addPrefix("SPRITES/")),
|
||||
preloaded(false),
|
||||
defFile()
|
||||
preloaded(false)
|
||||
{
|
||||
if(CResourceHandler::get()->existsResource(name))
|
||||
defFile = std::make_shared<CDefFile>(name);
|
||||
{
|
||||
try
|
||||
{
|
||||
defFile = std::make_shared<CDefFile>(name);
|
||||
}
|
||||
catch ( const std::runtime_error & e)
|
||||
{
|
||||
logAnim->error("Def file %s failed to load! Reason: %s", Name.getOriginalName(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
|
@ -49,7 +49,7 @@ void CArtPlace::setInternals(const CArtifactInstance * artInst)
|
||||
if(settings["general"]["enableUiEnhancements"].Bool())
|
||||
{
|
||||
imageIndex = spellID.num;
|
||||
if(baseType != CComponent::spell)
|
||||
if(component.type != ComponentType::SPELL_SCROLL)
|
||||
{
|
||||
image->setScale(Point(pos.w, 34));
|
||||
image->setAnimationPath(AnimationPath::builtin("spellscr"), imageIndex);
|
||||
@ -57,21 +57,20 @@ void CArtPlace::setInternals(const CArtifactInstance * artInst)
|
||||
}
|
||||
}
|
||||
// Add spell component info (used to provide a pic in r-click popup)
|
||||
baseType = CComponent::spell;
|
||||
type = spellID;
|
||||
component.type = ComponentType::SPELL_SCROLL;
|
||||
component.subType = spellID;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(settings["general"]["enableUiEnhancements"].Bool() && baseType != CComponent::artifact)
|
||||
if(settings["general"]["enableUiEnhancements"].Bool() && component.type != ComponentType::ARTIFACT)
|
||||
{
|
||||
image->setScale(Point());
|
||||
image->setAnimationPath(AnimationPath::builtin("artifact"), imageIndex);
|
||||
image->moveTo(Point(pos.x, pos.y));
|
||||
}
|
||||
baseType = CComponent::artifact;
|
||||
type = artInst->getTypeId();
|
||||
component.type = ComponentType::ARTIFACT;
|
||||
component.subType = artInst->getTypeId();
|
||||
}
|
||||
bonusValue = 0;
|
||||
image->enable();
|
||||
text = artInst->getDescription();
|
||||
}
|
||||
@ -121,10 +120,11 @@ void CCommanderArtPlace::returnArtToHeroCallback()
|
||||
}
|
||||
else
|
||||
{
|
||||
ArtifactLocation src(commanderOwner->commander.get(), artifactPos);
|
||||
ArtifactLocation dst(commanderOwner, freeSlot);
|
||||
ArtifactLocation src(commanderOwner->id, artifactPos);
|
||||
src.creature = SlotID::COMMANDER_SLOT_PLACEHOLDER;
|
||||
ArtifactLocation dst(commanderOwner->id, freeSlot);
|
||||
|
||||
if(ourArt->canBePutAt(dst, true))
|
||||
if(ourArt->canBePutAt(commanderOwner, freeSlot, true))
|
||||
{
|
||||
LOCPLINT->cb->swapArtifacts(src, dst);
|
||||
setArtifact(nullptr);
|
||||
|
@ -72,7 +72,7 @@ void CArtifactsOfHeroAltar::pickUpArtifact(CHeroArtPlace & artPlace)
|
||||
if(ArtifactUtils::isSlotBackpack(pickedArtFromSlot))
|
||||
pickedArtFromSlot = curHero->getSlotByInstance(art);
|
||||
assert(pickedArtFromSlot != ArtifactPosition::PRE_FIRST);
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, pickedArtFromSlot), ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS));
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, pickedArtFromSlot), ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS));
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ void CArtifactsOfHeroAltar::pickedArtMoveToAltar(const ArtifactPosition & slot)
|
||||
if(ArtifactUtils::isSlotBackpack(slot) || ArtifactUtils::isSlotEquipment(slot) || slot == ArtifactPosition::TRANSITION_POS)
|
||||
{
|
||||
assert(curHero->getSlot(slot)->getArt());
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, slot), ArtifactLocation(curHero, pickedArtFromSlot));
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, slot), ArtifactLocation(curHero->id, pickedArtFromSlot));
|
||||
pickedArtFromSlot = ArtifactPosition::PRE_FIRST;
|
||||
}
|
||||
}
|
||||
|
@ -80,8 +80,8 @@ void CArtifactsOfHeroBackpack::swapArtifacts(const ArtifactLocation & srcLoc, co
|
||||
|
||||
void CArtifactsOfHeroBackpack::pickUpArtifact(CHeroArtPlace & artPlace)
|
||||
{
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, artPlace.slot),
|
||||
ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS));
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, artPlace.slot),
|
||||
ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS));
|
||||
}
|
||||
|
||||
void CArtifactsOfHeroBackpack::scrollBackpack(int offset)
|
||||
|
@ -39,11 +39,11 @@ void CArtifactsOfHeroBase::putBackPickedArtifact()
|
||||
auto slot = ArtifactUtils::getArtAnyPosition(curHero, curHero->artifactsTransitionPos.begin()->artifact->getTypeId());
|
||||
if(slot == ArtifactPosition::PRE_FIRST)
|
||||
{
|
||||
LOCPLINT->cb->eraseArtifactByClient(ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS));
|
||||
LOCPLINT->cb->eraseArtifactByClient(ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS), ArtifactLocation(curHero, slot));
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS), ArtifactLocation(curHero->id, slot));
|
||||
}
|
||||
}
|
||||
if(putBackPickedArtCallback)
|
||||
@ -178,7 +178,7 @@ void CArtifactsOfHeroBase::scrollBackpackForArtSet(int offset, const CArtifactSe
|
||||
void CArtifactsOfHeroBase::markPossibleSlots(const CArtifactInstance * art, bool assumeDestRemoved)
|
||||
{
|
||||
for(auto artPlace : artWorn)
|
||||
artPlace.second->selectSlot(art->artType->canBePutAt(curHero, artPlace.second->slot, assumeDestRemoved));
|
||||
artPlace.second->selectSlot(art->canBePutAt(curHero, artPlace.second->slot, assumeDestRemoved));
|
||||
}
|
||||
|
||||
void CArtifactsOfHeroBase::unmarkSlots()
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "Buttons.h"
|
||||
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/networkPacks/ArtifactLocation.h"
|
||||
@ -56,7 +57,7 @@ void CArtifactsOfHeroKingdom::swapArtifacts(const ArtifactLocation & srcLoc, con
|
||||
|
||||
void CArtifactsOfHeroKingdom::pickUpArtifact(CHeroArtPlace & artPlace)
|
||||
{
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, artPlace.slot),
|
||||
ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS));
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, artPlace.slot),
|
||||
ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS));
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "CArtifactsOfHeroMain.h"
|
||||
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/networkPacks/ArtifactLocation.h"
|
||||
@ -36,6 +37,6 @@ void CArtifactsOfHeroMain::swapArtifacts(const ArtifactLocation & srcLoc, const
|
||||
|
||||
void CArtifactsOfHeroMain::pickUpArtifact(CHeroArtPlace & artPlace)
|
||||
{
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, artPlace.slot),
|
||||
ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS));
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, artPlace.slot),
|
||||
ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS));
|
||||
}
|
||||
|
@ -39,41 +39,35 @@
|
||||
#include "../../lib/CArtHandler.h"
|
||||
#include "../../lib/CArtifactInstance.h"
|
||||
|
||||
CComponent::CComponent(Etype Type, int Subtype, int Val, ESize imageSize, EFonts font):
|
||||
perDay(false)
|
||||
CComponent::CComponent(ComponentType Type, ComponentSubType Subtype, std::optional<int32_t> Val, ESize imageSize, EFonts font)
|
||||
{
|
||||
init(Type, Subtype, Val, imageSize, font, "");
|
||||
}
|
||||
|
||||
CComponent::CComponent(Etype Type, int Subtype, std::string Val, ESize imageSize, EFonts font):
|
||||
perDay(false)
|
||||
CComponent::CComponent(ComponentType Type, ComponentSubType Subtype, const std::string & Val, ESize imageSize, EFonts font)
|
||||
{
|
||||
init(Type, Subtype, 0, imageSize, font, Val);
|
||||
init(Type, Subtype, std::nullopt, imageSize, font, Val);
|
||||
}
|
||||
|
||||
CComponent::CComponent(const Component & c, ESize imageSize, EFonts font)
|
||||
: perDay(false)
|
||||
{
|
||||
if(c.id == Component::EComponentType::RESOURCE && c.when==-1)
|
||||
perDay = true;
|
||||
|
||||
init((Etype)c.id, c.subtype, c.val, imageSize, font);
|
||||
init(c.type, c.subType, c.value, imageSize, font, "");
|
||||
}
|
||||
|
||||
void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize, EFonts fnt, std::string ValText)
|
||||
void CComponent::init(ComponentType Type, ComponentSubType Subtype, std::optional<int32_t> Val, ESize imageSize, EFonts fnt, const std::string & ValText)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
|
||||
addUsedEvents(SHOW_POPUP);
|
||||
|
||||
compType = Type;
|
||||
subtype = Subtype;
|
||||
val = Val;
|
||||
valText = ValText;
|
||||
data.type = Type;
|
||||
data.subType = Subtype;
|
||||
data.value = Val;
|
||||
|
||||
customSubtitle = ValText;
|
||||
size = imageSize;
|
||||
font = fnt;
|
||||
|
||||
assert(compType < typeInvalid);
|
||||
assert(size < sizeInvalid);
|
||||
|
||||
setSurface(getFileName()[size], (int)getIndex());
|
||||
@ -94,7 +88,7 @@ void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize, EFonts
|
||||
if (size < small)
|
||||
max = 30;
|
||||
|
||||
if(Type == Etype::resource && !valText.empty())
|
||||
if(Type == ComponentType::RESOURCE && !ValText.empty())
|
||||
max = 80;
|
||||
|
||||
std::vector<std::string> textLines = CMessage::breakText(getSubtitle(), std::max<int>(max, pos.w), font);
|
||||
@ -113,153 +107,209 @@ void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize, EFonts
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<AnimationPath> CComponent::getFileName()
|
||||
std::vector<AnimationPath> CComponent::getFileName() const
|
||||
{
|
||||
static const std::string primSkillsArr [] = {"PSKIL32", "PSKIL32", "PSKIL42", "PSKILL"};
|
||||
static const std::string secSkillsArr [] = {"SECSK32", "SECSK32", "SECSKILL", "SECSK82"};
|
||||
static const std::string resourceArr [] = {"SMALRES", "RESOURCE", "RESOURCE", "RESOUR82"};
|
||||
static const std::string creatureArr [] = {"CPRSMALL", "CPRSMALL", "CPRSMALL", "TWCRPORT"};
|
||||
static const std::string artifactArr[] = {"Artifact", "Artifact", "Artifact", "Artifact"};
|
||||
static const std::string spellsArr [] = {"SpellInt", "SpellInt", "SpellInt", "SPELLSCR"};
|
||||
static const std::string moraleArr [] = {"IMRL22", "IMRL30", "IMRL42", "imrl82"};
|
||||
static const std::string luckArr [] = {"ILCK22", "ILCK30", "ILCK42", "ilck82"};
|
||||
static const std::string heroArr [] = {"PortraitsSmall", "PortraitsSmall", "PortraitsSmall", "PortraitsLarge"};
|
||||
static const std::string flagArr [] = {"CREST58", "CREST58", "CREST58", "CREST58"};
|
||||
static const std::array<std::string, 4> primSkillsArr = {"PSKIL32", "PSKIL32", "PSKIL42", "PSKILL"};
|
||||
static const std::array<std::string, 4> secSkillsArr = {"SECSK32", "SECSK32", "SECSKILL", "SECSK82"};
|
||||
static const std::array<std::string, 4> resourceArr = {"SMALRES", "RESOURCE", "RESOURCE", "RESOUR82"};
|
||||
static const std::array<std::string, 4> creatureArr = {"CPRSMALL", "CPRSMALL", "CPRSMALL", "TWCRPORT"};
|
||||
static const std::array<std::string, 4> artifactArr = {"Artifact", "Artifact", "Artifact", "Artifact"};
|
||||
static const std::array<std::string, 4> spellsArr = {"SpellInt", "SpellInt", "SpellInt", "SPELLSCR"};
|
||||
static const std::array<std::string, 4> moraleArr = {"IMRL22", "IMRL30", "IMRL42", "imrl82"};
|
||||
static const std::array<std::string, 4> luckArr = {"ILCK22", "ILCK30", "ILCK42", "ilck82"};
|
||||
static const std::array<std::string, 4> heroArr = {"PortraitsSmall", "PortraitsSmall", "PortraitsSmall", "PortraitsLarge"};
|
||||
static const std::array<std::string, 4> flagArr = {"CREST58", "CREST58", "CREST58", "CREST58"};
|
||||
|
||||
auto gen = [](const std::string * arr) -> std::vector<AnimationPath>
|
||||
auto gen = [](const std::array<std::string, 4> & arr) -> std::vector<AnimationPath>
|
||||
{
|
||||
return { AnimationPath::builtin(arr[0]), AnimationPath::builtin(arr[1]), AnimationPath::builtin(arr[2]), AnimationPath::builtin(arr[3]) };
|
||||
};
|
||||
|
||||
switch(compType)
|
||||
switch(data.type)
|
||||
{
|
||||
case primskill: return gen(primSkillsArr);
|
||||
case secskill: return gen(secSkillsArr);
|
||||
case resource: return gen(resourceArr);
|
||||
case creature: return gen(creatureArr);
|
||||
case artifact: return gen(artifactArr);
|
||||
case experience: return gen(primSkillsArr);
|
||||
case spell: return gen(spellsArr);
|
||||
case morale: return gen(moraleArr);
|
||||
case luck: return gen(luckArr);
|
||||
case building: return std::vector<AnimationPath>(4, (*CGI->townh)[subtype]->town->clientInfo.buildingsIcons);
|
||||
case hero: return gen(heroArr);
|
||||
case flag: return gen(flagArr);
|
||||
case ComponentType::PRIM_SKILL:
|
||||
case ComponentType::EXPERIENCE:
|
||||
case ComponentType::MANA:
|
||||
case ComponentType::LEVEL:
|
||||
return gen(primSkillsArr);
|
||||
case ComponentType::SEC_SKILL:
|
||||
return gen(secSkillsArr);
|
||||
case ComponentType::RESOURCE:
|
||||
case ComponentType::RESOURCE_PER_DAY:
|
||||
return gen(resourceArr);
|
||||
case ComponentType::CREATURE:
|
||||
return gen(creatureArr);
|
||||
case ComponentType::ARTIFACT:
|
||||
return gen(artifactArr);
|
||||
case ComponentType::SPELL_SCROLL:
|
||||
case ComponentType::SPELL:
|
||||
return gen(spellsArr);
|
||||
case ComponentType::MORALE:
|
||||
return gen(moraleArr);
|
||||
case ComponentType::LUCK:
|
||||
return gen(luckArr);
|
||||
case ComponentType::BUILDING:
|
||||
return std::vector<AnimationPath>(4, (*CGI->townh)[data.subType.as<BuildingTypeUniqueID>().getFaction()]->town->clientInfo.buildingsIcons);
|
||||
case ComponentType::HERO_PORTRAIT:
|
||||
return gen(heroArr);
|
||||
case ComponentType::FLAG:
|
||||
return gen(flagArr);
|
||||
default:
|
||||
assert(0);
|
||||
return {};
|
||||
}
|
||||
assert(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t CComponent::getIndex()
|
||||
size_t CComponent::getIndex() const
|
||||
{
|
||||
switch(compType)
|
||||
switch(data.type)
|
||||
{
|
||||
case primskill: return subtype;
|
||||
case secskill: return subtype*3 + 3 + val - 1;
|
||||
case resource: return subtype;
|
||||
case creature: return CGI->creatures()->getByIndex(subtype)->getIconIndex();
|
||||
case artifact: return CGI->artifacts()->getByIndex(subtype)->getIconIndex();
|
||||
case experience: return 4;
|
||||
case spell: return (size < large) ? subtype + 1 : subtype;
|
||||
case morale: return val+3;
|
||||
case luck: return val+3;
|
||||
case building: return val;
|
||||
case hero: return CGI->heroTypes()->getByIndex(subtype)->getIconIndex();
|
||||
case flag: return subtype;
|
||||
case ComponentType::PRIM_SKILL:
|
||||
return data.subType.getNum();
|
||||
case ComponentType::EXPERIENCE:
|
||||
case ComponentType::LEVEL:
|
||||
return 4; // for whatever reason, in H3 experience icon is located in primary skills icons
|
||||
case ComponentType::MANA:
|
||||
return 5; // for whatever reason, in H3 mana points icon is located in primary skills icons
|
||||
case ComponentType::SEC_SKILL:
|
||||
return data.subType.getNum() * 3 + 3 + data.value.value_or(0) - 1;
|
||||
case ComponentType::RESOURCE:
|
||||
case ComponentType::RESOURCE_PER_DAY:
|
||||
return data.subType.getNum();
|
||||
case ComponentType::CREATURE:
|
||||
return CGI->creatures()->getById(data.subType.as<CreatureID>())->getIconIndex();
|
||||
case ComponentType::ARTIFACT:
|
||||
return CGI->artifacts()->getById(data.subType.as<ArtifactID>())->getIconIndex();
|
||||
case ComponentType::SPELL_SCROLL:
|
||||
case ComponentType::SPELL:
|
||||
return (size < large) ? data.subType.getNum() + 1 : data.subType.getNum();
|
||||
case ComponentType::MORALE:
|
||||
return data.value.value_or(0) + 3;
|
||||
case ComponentType::LUCK:
|
||||
return data.value.value_or(0) + 3;
|
||||
case ComponentType::BUILDING:
|
||||
return data.subType.as<BuildingTypeUniqueID>().getBuilding();
|
||||
case ComponentType::HERO_PORTRAIT:
|
||||
return CGI->heroTypes()->getById(data.subType.as<HeroTypeID>())->getIconIndex();
|
||||
case ComponentType::FLAG:
|
||||
return data.subType.getNum();
|
||||
default:
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string CComponent::getDescription()
|
||||
std::string CComponent::getDescription() const
|
||||
{
|
||||
switch(compType)
|
||||
switch(data.type)
|
||||
{
|
||||
case primskill: return (subtype < 4)? CGI->generaltexth->arraytxt[2+subtype] //Primary skill
|
||||
: CGI->generaltexth->allTexts[149]; //mana
|
||||
case secskill: return CGI->skillh->getByIndex(subtype)->getDescriptionTranslated(val);
|
||||
case resource: return CGI->generaltexth->allTexts[242];
|
||||
case creature: return "";
|
||||
case artifact:
|
||||
{
|
||||
auto artID = ArtifactID(subtype);
|
||||
auto description = VLC->arth->objects[artID]->getDescriptionTranslated();
|
||||
if(artID == ArtifactID::SPELL_SCROLL)
|
||||
case ComponentType::PRIM_SKILL:
|
||||
return CGI->generaltexth->arraytxt[2+data.subType.getNum()];
|
||||
case ComponentType::EXPERIENCE:
|
||||
case ComponentType::LEVEL:
|
||||
return CGI->generaltexth->allTexts[241];
|
||||
case ComponentType::MANA:
|
||||
return CGI->generaltexth->allTexts[149];
|
||||
case ComponentType::SEC_SKILL:
|
||||
return CGI->skillh->getByIndex(data.subType.getNum())->getDescriptionTranslated(data.value.value_or(0));
|
||||
case ComponentType::RESOURCE:
|
||||
case ComponentType::RESOURCE_PER_DAY:
|
||||
return CGI->generaltexth->allTexts[242];
|
||||
case ComponentType::CREATURE:
|
||||
return "";
|
||||
case ComponentType::ARTIFACT:
|
||||
return VLC->artifacts()->getById(data.subType.as<ArtifactID>())->getDescriptionTranslated();
|
||||
case ComponentType::SPELL_SCROLL:
|
||||
{
|
||||
ArtifactUtils::insertScrrollSpellName(description, SpellID(val));
|
||||
auto description = VLC->arth->objects[ArtifactID::SPELL_SCROLL]->getDescriptionTranslated();
|
||||
ArtifactUtils::insertScrrollSpellName(description, data.subType.as<SpellID>());
|
||||
return description;
|
||||
}
|
||||
return description;
|
||||
}
|
||||
case experience: return CGI->generaltexth->allTexts[241];
|
||||
case spell: return (*CGI->spellh)[subtype]->getDescriptionTranslated(val);
|
||||
case morale: return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)];
|
||||
case luck: return CGI->generaltexth->heroscrn[ 7 - (val>0) + (val<0)];
|
||||
case building: return (*CGI->townh)[subtype]->town->buildings[BuildingID(val)]->getDescriptionTranslated();
|
||||
case hero: return "";
|
||||
case flag: return "";
|
||||
}
|
||||
assert(0);
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string CComponent::getSubtitle()
|
||||
{
|
||||
if(!perDay)
|
||||
return getSubtitleInternal();
|
||||
|
||||
std::string ret = CGI->generaltexth->allTexts[3];
|
||||
boost::replace_first(ret, "%d", getSubtitleInternal());
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string CComponent::getSubtitleInternal()
|
||||
{
|
||||
//FIXME: some of these are horrible (e.g creature)
|
||||
switch(compType)
|
||||
{
|
||||
case primskill: return boost::str(boost::format("%+d %s") % val % (subtype < 4 ? CGI->generaltexth->primarySkillNames[subtype] : CGI->generaltexth->allTexts[387]));
|
||||
case secskill: return CGI->generaltexth->levels[val-1] + "\n" + CGI->skillh->getByIndex(subtype)->getNameTranslated();
|
||||
case resource: return valText.empty() ? std::to_string(val) : valText;
|
||||
case creature:
|
||||
case ComponentType::SPELL:
|
||||
return VLC->spells()->getById(data.subType.as<SpellID>())->getDescriptionTranslated(data.value.value_or(0));
|
||||
case ComponentType::MORALE:
|
||||
return CGI->generaltexth->heroscrn[ 4 - (data.value.value_or(0)>0) + (data.value.value_or(0)<0)];
|
||||
case ComponentType::LUCK:
|
||||
return CGI->generaltexth->heroscrn[ 7 - (data.value.value_or(0)>0) + (data.value.value_or(0)<0)];
|
||||
case ComponentType::BUILDING:
|
||||
{
|
||||
auto creature = CGI->creh->getByIndex(subtype);
|
||||
if ( val )
|
||||
return std::to_string(val) + " " + (val > 1 ? creature->getNamePluralTranslated() : creature->getNameSingularTranslated());
|
||||
auto index = data.subType.as<BuildingTypeUniqueID>();
|
||||
return (*CGI->townh)[index.getFaction()]->town->buildings[index.getBuilding()]->getDescriptionTranslated();
|
||||
}
|
||||
case ComponentType::HERO_PORTRAIT:
|
||||
return "";
|
||||
case ComponentType::FLAG:
|
||||
return "";
|
||||
default:
|
||||
assert(0);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string CComponent::getSubtitle() const
|
||||
{
|
||||
if (!customSubtitle.empty())
|
||||
return customSubtitle;
|
||||
|
||||
switch(data.type)
|
||||
{
|
||||
case ComponentType::PRIM_SKILL:
|
||||
if (data.value)
|
||||
return boost::str(boost::format("%+d %s") % data.value.value_or(0) % CGI->generaltexth->primarySkillNames[data.subType.getNum()]);
|
||||
else
|
||||
return val > 1 ? creature->getNamePluralTranslated() : creature->getNameSingularTranslated();
|
||||
}
|
||||
case artifact: return CGI->artifacts()->getByIndex(subtype)->getNameTranslated();
|
||||
case experience:
|
||||
return CGI->generaltexth->primarySkillNames[data.subType.getNum()];
|
||||
case ComponentType::EXPERIENCE:
|
||||
return std::to_string(data.value.value_or(0));
|
||||
case ComponentType::LEVEL:
|
||||
{
|
||||
if(subtype == 1) //+1 level - tree of knowledge
|
||||
{
|
||||
std::string level = CGI->generaltexth->allTexts[442];
|
||||
boost::replace_first(level, "1", std::to_string(val));
|
||||
return level;
|
||||
}
|
||||
std::string level = CGI->generaltexth->allTexts[442];
|
||||
boost::replace_first(level, "1", std::to_string(data.value.value_or(0)));
|
||||
return level;
|
||||
}
|
||||
case ComponentType::MANA:
|
||||
return boost::str(boost::format("%+d %s") % data.value.value_or(0) % CGI->generaltexth->allTexts[387]);
|
||||
case ComponentType::SEC_SKILL:
|
||||
return CGI->generaltexth->levels[data.value.value_or(0)-1] + "\n" + CGI->skillh->getById(data.subType.as<SecondarySkill>())->getNameTranslated();
|
||||
case ComponentType::RESOURCE:
|
||||
return std::to_string(data.value.value_or(0));
|
||||
case ComponentType::RESOURCE_PER_DAY:
|
||||
return boost::str(boost::format(CGI->generaltexth->allTexts[3]) % data.value.value_or(0));
|
||||
case ComponentType::CREATURE:
|
||||
{
|
||||
auto creature = CGI->creh->getById(data.subType.as<CreatureID>());
|
||||
if(data.value)
|
||||
return std::to_string(*data.value) + " " + (*data.value > 1 ? creature->getNamePluralTranslated() : creature->getNameSingularTranslated());
|
||||
else
|
||||
{
|
||||
return std::to_string(val); //amount of experience OR level required for seer hut;
|
||||
}
|
||||
return creature->getNamePluralTranslated();
|
||||
}
|
||||
case spell: return CGI->spells()->getByIndex(subtype)->getNameTranslated();
|
||||
case morale: return "";
|
||||
case luck: return "";
|
||||
case building:
|
||||
{
|
||||
auto building = (*CGI->townh)[subtype]->town->buildings[BuildingID(val)];
|
||||
if(!building)
|
||||
case ComponentType::ARTIFACT:
|
||||
return CGI->artifacts()->getById(data.subType.as<ArtifactID>())->getNameTranslated();
|
||||
case ComponentType::SPELL_SCROLL:
|
||||
case ComponentType::SPELL:
|
||||
return CGI->spells()->getById(data.subType.as<SpellID>())->getNameTranslated();
|
||||
case ComponentType::MORALE:
|
||||
return "";
|
||||
case ComponentType::LUCK:
|
||||
return "";
|
||||
case ComponentType::BUILDING:
|
||||
{
|
||||
logGlobal->error("Town of faction %s has no building #%d", (*CGI->townh)[subtype]->town->faction->getNameTranslated(), val);
|
||||
return (boost::format("Missing building #%d") % val).str();
|
||||
auto index = data.subType.as<BuildingTypeUniqueID>();
|
||||
auto building = (*CGI->townh)[index.getFaction()]->town->buildings[index.getBuilding()];
|
||||
if(!building)
|
||||
{
|
||||
logGlobal->error("Town of faction %s has no building #%d", (*CGI->townh)[index.getFaction()]->town->faction->getNameTranslated(), index.getBuilding().getNum());
|
||||
return (boost::format("Missing building #%d") % index.getBuilding().getNum()).str();
|
||||
}
|
||||
return building->getNameTranslated();
|
||||
}
|
||||
return building->getNameTranslated();
|
||||
}
|
||||
case hero: return "";
|
||||
case flag: return CGI->generaltexth->capColors[subtype];
|
||||
case ComponentType::HERO_PORTRAIT:
|
||||
return "";
|
||||
case ComponentType::FLAG:
|
||||
return CGI->generaltexth->capColors[data.subType.as<PlayerColor>().getNum()];
|
||||
default:
|
||||
assert(0);
|
||||
return "";
|
||||
}
|
||||
logGlobal->error("Invalid CComponent type: %d", (int)compType);
|
||||
return "";
|
||||
}
|
||||
|
||||
void CComponent::setSurface(const AnimationPath & defName, int imgPos)
|
||||
@ -299,7 +349,7 @@ CSelectableComponent::CSelectableComponent(const Component &c, std::function<voi
|
||||
init();
|
||||
}
|
||||
|
||||
CSelectableComponent::CSelectableComponent(Etype Type, int Sub, int Val, ESize imageSize, std::function<void()> OnSelect):
|
||||
CSelectableComponent::CSelectableComponent(ComponentType Type, ComponentSubType Sub, int Val, ESize imageSize, std::function<void()> OnSelect):
|
||||
CComponent(Type,Sub,Val, imageSize),onSelect(OnSelect)
|
||||
{
|
||||
setRedrawParent(true);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "../gui/CIntObject.h"
|
||||
#include "../render/EFont.h"
|
||||
#include "../../lib/filesystem/ResourcePath.h"
|
||||
#include "../../lib/networkPacks/Component.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -26,11 +27,6 @@ class CLabel;
|
||||
class CComponent : public virtual CIntObject
|
||||
{
|
||||
public:
|
||||
enum Etype
|
||||
{
|
||||
primskill, secskill, resource, creature, artifact, experience, spell, morale, luck, building, hero, flag, typeInvalid
|
||||
};
|
||||
|
||||
//NOTE: not all types have exact these sizes or have less than 4 of them. In such cases closest one will be used
|
||||
enum ESize
|
||||
{
|
||||
@ -44,29 +40,24 @@ public:
|
||||
private:
|
||||
std::vector<std::shared_ptr<CLabel>> lines;
|
||||
|
||||
size_t getIndex();
|
||||
std::vector<AnimationPath> getFileName();
|
||||
size_t getIndex() const;
|
||||
std::vector<AnimationPath> getFileName() const;
|
||||
void setSurface(const AnimationPath & defName, int imgPos);
|
||||
std::string getSubtitleInternal();
|
||||
|
||||
void init(Etype Type, int Subtype, int Val, ESize imageSize, EFonts font = FONT_SMALL, std::string ValText="");
|
||||
void init(ComponentType Type, ComponentSubType Subtype, std::optional<int32_t> Val, ESize imageSize, EFonts font, const std::string & ValText);
|
||||
|
||||
public:
|
||||
std::shared_ptr<CAnimImage> image;
|
||||
|
||||
Etype compType; //component type
|
||||
Component data;
|
||||
std::string customSubtitle;
|
||||
ESize size; //component size.
|
||||
EFonts font; //Font size of label
|
||||
int subtype; //type-dependant subtype. See getSomething methods for details
|
||||
int val; // value \ strength \ amount of component. See getSomething methods for details
|
||||
std::string valText; // value instead of amount; currently only for resource
|
||||
bool perDay; // add "per day" text to subtitle
|
||||
|
||||
std::string getDescription();
|
||||
std::string getSubtitle();
|
||||
std::string getDescription() const;
|
||||
std::string getSubtitle() const;
|
||||
|
||||
CComponent(Etype Type, int Subtype, int Val = 0, ESize imageSize=large, EFonts font = FONT_SMALL);
|
||||
CComponent(Etype Type, int Subtype, std::string Val, ESize imageSize=large, EFonts font = FONT_SMALL);
|
||||
CComponent(ComponentType Type, ComponentSubType Subtype, std::optional<int32_t> Val = std::nullopt, ESize imageSize=large, EFonts font = FONT_SMALL);
|
||||
CComponent(ComponentType Type, ComponentSubType Subtype, const std::string & Val, ESize imageSize=large, EFonts font = FONT_SMALL);
|
||||
CComponent(const Component &c, ESize imageSize=large, EFonts font = FONT_SMALL);
|
||||
|
||||
void showPopupWindow(const Point & cursorPosition) override; //call-in
|
||||
@ -86,7 +77,7 @@ public:
|
||||
|
||||
void clickPressed(const Point & cursorPosition) override; //call-in
|
||||
void clickDouble(const Point & cursorPosition) override; //call-in
|
||||
CSelectableComponent(Etype Type, int Sub, int Val, ESize imageSize=large, std::function<void()> OnSelect = nullptr);
|
||||
CSelectableComponent(ComponentType Type, ComponentSubType Sub, int Val, ESize imageSize=large, std::function<void()> OnSelect = nullptr);
|
||||
CSelectableComponent(const Component & c, std::function<void()> OnSelect = nullptr);
|
||||
};
|
||||
|
||||
|
@ -196,16 +196,18 @@ bool CGarrisonSlot::highlightOrDropArtifact()
|
||||
artSelected = true;
|
||||
if (myStack) // try dropping the artifact only if the slot isn't empty
|
||||
{
|
||||
ArtifactLocation src(srcHero, ArtifactPosition::TRANSITION_POS);
|
||||
ArtifactLocation dst(myStack, ArtifactPosition::CREATURE_SLOT);
|
||||
if(pickedArtInst->canBePutAt(dst, true))
|
||||
ArtifactLocation src(srcHero->id, ArtifactPosition::TRANSITION_POS);
|
||||
ArtifactLocation dst(getObj()->id, ArtifactPosition::CREATURE_SLOT);
|
||||
dst.creature = getSlot();
|
||||
|
||||
if(pickedArtInst->canBePutAt(myStack, ArtifactPosition::CREATURE_SLOT, true))
|
||||
{ //equip clicked stack
|
||||
if(dst.getArt())
|
||||
if(auto dstArt = myStack->getArt(ArtifactPosition::CREATURE_SLOT))
|
||||
{
|
||||
//creature can wear only one active artifact
|
||||
//if we are placing a new one, the old one will be returned to the hero's backpack
|
||||
LOCPLINT->cb->swapArtifacts(dst, ArtifactLocation(srcHero,
|
||||
ArtifactUtils::getArtBackpackPosition(srcHero, dst.getArt()->getTypeId())));
|
||||
LOCPLINT->cb->swapArtifacts(dst, ArtifactLocation(srcHero->id,
|
||||
ArtifactUtils::getArtBackpackPosition(srcHero, dstArt->getTypeId())));
|
||||
}
|
||||
LOCPLINT->cb->swapArtifacts(src, dst);
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst
|
||||
if(artPlace.getArt()->getTypeId() == ArtifactID::CATAPULT)
|
||||
{
|
||||
// The Catapult must be equipped
|
||||
std::vector<std::shared_ptr<CComponent>> catapult(1, std::make_shared<CComponent>(CComponent::artifact, 3, 0));
|
||||
std::vector<std::shared_ptr<CComponent>> catapult(1, std::make_shared<CComponent>(ComponentType::ARTIFACT, ArtifactID(ArtifactID::CATAPULT)));
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult);
|
||||
return false;
|
||||
}
|
||||
@ -122,8 +122,8 @@ void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst
|
||||
|
||||
if(pickedArtInst)
|
||||
{
|
||||
auto srcLoc = ArtifactLocation(heroPickedArt, ArtifactPosition::TRANSITION_POS);
|
||||
auto dstLoc = ArtifactLocation(hero, artPlace.slot);
|
||||
auto srcLoc = ArtifactLocation(heroPickedArt->id, ArtifactPosition::TRANSITION_POS);
|
||||
auto dstLoc = ArtifactLocation(hero->id, artPlace.slot);
|
||||
|
||||
if(ArtifactUtils::isSlotBackpack(artPlace.slot))
|
||||
{
|
||||
@ -141,7 +141,7 @@ void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst
|
||||
}
|
||||
}
|
||||
// Check if artifact transfer is possible
|
||||
else if(pickedArtInst->canBePutAt(dstLoc, true) && (!artPlace.getArt() || hero->tempOwner == LOCPLINT->playerID))
|
||||
else if(pickedArtInst->canBePutAt(hero, artPlace.slot, true) && (!artPlace.getArt() || hero->tempOwner == LOCPLINT->playerID))
|
||||
{
|
||||
isTransferAllowed = true;
|
||||
}
|
||||
@ -270,7 +270,7 @@ void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const
|
||||
// we have a different artifact may look surprising... but it's valid.
|
||||
|
||||
auto pickedArtInst = std::get<const CArtifactInstance*>(curState.value());
|
||||
assert(!pickedArtInst || destLoc.isHolder(std::get<const CGHeroInstance*>(curState.value())));
|
||||
assert(!pickedArtInst || destLoc.artHolder == std::get<const CGHeroInstance*>(curState.value())->id);
|
||||
|
||||
auto artifactMovedBody = [this, withRedraw, &destLoc, &pickedArtInst](auto artSetWeak) -> void
|
||||
{
|
||||
@ -316,7 +316,7 @@ void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const
|
||||
}
|
||||
|
||||
// Make sure the status bar is updated so it does not display old text
|
||||
if(destLoc.getHolderArtSet() == hero)
|
||||
if(destLoc.artHolder == hero->id)
|
||||
{
|
||||
if(auto artPlace = artSetPtr->getArtPlace(destLoc.slot))
|
||||
artPlace->hover(true);
|
||||
|
@ -95,16 +95,16 @@ void LRClickableAreaWTextComp::clickPressed(const Point & cursorPosition)
|
||||
LOCPLINT->showInfoDialog(text, comp);
|
||||
}
|
||||
|
||||
LRClickableAreaWTextComp::LRClickableAreaWTextComp(const Rect &Pos, int BaseType)
|
||||
: LRClickableAreaWText(Pos), baseType(BaseType), bonusValue(-1)
|
||||
LRClickableAreaWTextComp::LRClickableAreaWTextComp(const Rect &Pos, ComponentType BaseType)
|
||||
: LRClickableAreaWText(Pos)
|
||||
{
|
||||
type = -1;
|
||||
component.type = BaseType;
|
||||
}
|
||||
|
||||
std::shared_ptr<CComponent> LRClickableAreaWTextComp::createComponent() const
|
||||
{
|
||||
if(baseType >= 0)
|
||||
return std::make_shared<CComponent>(CComponent::Etype(baseType), type, bonusValue);
|
||||
if(component.type != ComponentType::NONE)
|
||||
return std::make_shared<CComponent>(component);
|
||||
else
|
||||
return std::shared_ptr<CComponent>();
|
||||
}
|
||||
@ -164,17 +164,11 @@ void CHeroArea::hover(bool on)
|
||||
void LRClickableAreaOpenTown::clickPressed(const Point & cursorPosition)
|
||||
{
|
||||
if(town)
|
||||
{
|
||||
LOCPLINT->openTownWindow(town);
|
||||
if ( type == 2 )
|
||||
LOCPLINT->castleInt->builds->buildingClicked(BuildingID::VILLAGE_HALL);
|
||||
else if ( type == 3 && town->fortLevel() )
|
||||
LOCPLINT->castleInt->builds->buildingClicked(BuildingID::FORT);
|
||||
}
|
||||
}
|
||||
|
||||
LRClickableAreaOpenTown::LRClickableAreaOpenTown(const Rect & Pos, const CGTownInstance * Town)
|
||||
: LRClickableAreaWTextComp(Pos, -1), town(Town)
|
||||
: LRClickableAreaWTextComp(Pos), town(Town)
|
||||
{
|
||||
}
|
||||
|
||||
@ -542,20 +536,21 @@ void MoraleLuckBox::set(const AFactionMember * node)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
|
||||
|
||||
const int textId[] = {62, 88}; //eg %s \n\n\n {Current Luck Modifiers:}
|
||||
const std::array textId = {62, 88}; //eg %s \n\n\n {Current Luck Modifiers:}
|
||||
const int noneTxtId = 108; //Russian version uses same text for neutral morale\luck
|
||||
const int neutralDescr[] = {60, 86}; //eg {Neutral Morale} \n\n Neutral morale means your armies will neither be blessed with extra attacks or freeze in combat.
|
||||
const int componentType[] = {CComponent::luck, CComponent::morale};
|
||||
const int hoverTextBase[] = {7, 4};
|
||||
const std::array neutralDescr = {60, 86}; //eg {Neutral Morale} \n\n Neutral morale means your armies will neither be blessed with extra attacks or freeze in combat.
|
||||
const std::array componentType = {ComponentType::LUCK, ComponentType::MORALE};
|
||||
const std::array hoverTextBase = {7, 4};
|
||||
TConstBonusListPtr modifierList = std::make_shared<const BonusList>();
|
||||
bonusValue = 0;
|
||||
|
||||
component.value = 0;
|
||||
|
||||
if(node)
|
||||
bonusValue = morale ? node->moraleValAndBonusList(modifierList) : node->luckValAndBonusList(modifierList);
|
||||
component.value = morale ? node->moraleValAndBonusList(modifierList) : node->luckValAndBonusList(modifierList);
|
||||
|
||||
int mrlt = (bonusValue>0)-(bonusValue<0); //signum: -1 - bad luck / morale, 0 - neutral, 1 - good
|
||||
int mrlt = (component.value>0)-(component.value<0); //signum: -1 - bad luck / morale, 0 - neutral, 1 - good
|
||||
hoverText = CGI->generaltexth->heroscrn[hoverTextBase[morale] - mrlt];
|
||||
baseType = componentType[morale];
|
||||
component.type = componentType[morale];
|
||||
text = CGI->generaltexth->arraytxt[textId[morale]];
|
||||
boost::algorithm::replace_first(text,"%s",CGI->generaltexth->arraytxt[neutralDescr[morale]-mrlt]);
|
||||
|
||||
@ -563,19 +558,19 @@ void MoraleLuckBox::set(const AFactionMember * node)
|
||||
|| node->getBonusBearer()->hasBonusOfType(BonusType::NON_LIVING)))
|
||||
{
|
||||
text += CGI->generaltexth->arraytxt[113]; //unaffected by morale
|
||||
bonusValue = 0;
|
||||
component.value = 0;
|
||||
}
|
||||
else if(morale && node && node->getBonusBearer()->hasBonusOfType(BonusType::NO_MORALE))
|
||||
{
|
||||
auto noMorale = node->getBonusBearer()->getBonus(Selector::type()(BonusType::NO_MORALE));
|
||||
text += "\n" + noMorale->Description();
|
||||
bonusValue = 0;
|
||||
component.value = 0;
|
||||
}
|
||||
else if (!morale && node && node->getBonusBearer()->hasBonusOfType(BonusType::NO_LUCK))
|
||||
{
|
||||
auto noLuck = node->getBonusBearer()->getBonus(Selector::type()(BonusType::NO_LUCK));
|
||||
text += "\n" + noLuck->Description();
|
||||
bonusValue = 0;
|
||||
component.value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -595,7 +590,7 @@ void MoraleLuckBox::set(const AFactionMember * node)
|
||||
else
|
||||
imageName = morale ? "IMRL42" : "ILCK42";
|
||||
|
||||
image = std::make_shared<CAnimImage>(AnimationPath::builtin(imageName), bonusValue + 3);
|
||||
image = std::make_shared<CAnimImage>(AnimationPath::builtin(imageName), *component.value + 3);
|
||||
image->moveBy(Point(pos.w/2 - image->pos.w/2, pos.h/2 - image->pos.h/2));//center icon
|
||||
}
|
||||
|
||||
@ -603,7 +598,6 @@ MoraleLuckBox::MoraleLuckBox(bool Morale, const Rect &r, bool Small)
|
||||
: morale(Morale),
|
||||
small(Small)
|
||||
{
|
||||
bonusValue = 0;
|
||||
pos = r + pos.topLeft();
|
||||
defActions = 255-DISPOSE;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../gui/CIntObject.h"
|
||||
#include "../../lib/networkPacks/Component.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -202,13 +203,12 @@ private:
|
||||
class LRClickableAreaWTextComp: public LRClickableAreaWText
|
||||
{
|
||||
public:
|
||||
int type;
|
||||
int baseType;
|
||||
int bonusValue;
|
||||
Component component;
|
||||
|
||||
void clickPressed(const Point & cursorPosition) override;
|
||||
void showPopupWindow(const Point & cursorPosition) override;
|
||||
|
||||
LRClickableAreaWTextComp(const Rect &Pos = Rect(0,0,0,0), int BaseType = -1);
|
||||
LRClickableAreaWTextComp(const Rect &Pos = Rect(0,0,0,0), ComponentType baseType = ComponentType::NONE);
|
||||
std::shared_ptr<CComponent> createComponent() const;
|
||||
};
|
||||
|
||||
@ -263,4 +263,4 @@ class SimpleLine : public CIntObject
|
||||
public:
|
||||
SimpleLine(Point pos1, Point pos2, ColorRGBA color);
|
||||
void showAll(Canvas & to) override;
|
||||
};
|
||||
};
|
||||
|
@ -157,7 +157,7 @@ void CBuildingRect::showPopupWindow(const Point & cursorPosition)
|
||||
if (bid < BuildingID::DWELL_FIRST)
|
||||
{
|
||||
CRClickPopup::createAndPush(CInfoWindow::genText(bld->getNameTranslated(), bld->getDescriptionTranslated()),
|
||||
std::make_shared<CComponent>(CComponent::building, bld->town->faction->getIndex(), bld->bid));
|
||||
std::make_shared<CComponent>(ComponentType::BUILDING, BuildingTypeUniqueID(bld->town->faction->getId(), bld->bid)));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -860,7 +860,7 @@ void CCastleBuildings::enterBlacksmith(ArtifactID artifactID)
|
||||
|
||||
void CCastleBuildings::enterBuilding(BuildingID building)
|
||||
{
|
||||
std::vector<std::shared_ptr<CComponent>> comps(1, std::make_shared<CComponent>(CComponent::building, town->subID, building));
|
||||
std::vector<std::shared_ptr<CComponent>> comps(1, std::make_shared<CComponent>(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), building)));
|
||||
LOCPLINT->showInfoDialog( town->town->buildings.find(building)->second->getDescriptionTranslated(), comps);
|
||||
}
|
||||
|
||||
@ -920,7 +920,7 @@ void CCastleBuildings::enterToTheQuickRecruitmentWindow()
|
||||
|
||||
void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID::EBuildingSubID subID, BuildingID upgrades)
|
||||
{
|
||||
std::vector<std::shared_ptr<CComponent>> comps(1, std::make_shared<CComponent>(CComponent::building,town->subID,building));
|
||||
std::vector<std::shared_ptr<CComponent>> comps(1, std::make_shared<CComponent>(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), building)));
|
||||
std::string descr = town->town->buildings.find(building)->second->getDescriptionTranslated();
|
||||
std::string hasNotProduced;
|
||||
std::string hasProduced;
|
||||
@ -969,9 +969,9 @@ void CCastleBuildings::enterMagesGuild()
|
||||
{
|
||||
const StartInfo *si = LOCPLINT->cb->getStartInfo();
|
||||
// it would be nice to find a way to move this hack to config/mapOverrides.json
|
||||
if(si && si->campState && // We're in campaign,
|
||||
if(si && si->campState && // We're in campaign,
|
||||
(si->campState->getFilename() == "DATA/YOG.H3C") && // which is "Birth of a Barbarian",
|
||||
(hero->subID == 45)) // and the hero is Yog (based on Solmyr)
|
||||
(hero->getHeroType() == 45)) // and the hero is Yog (based on Solmyr)
|
||||
{
|
||||
// "Yog has given up magic in all its forms..."
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[736]);
|
||||
@ -986,7 +986,7 @@ void CCastleBuildings::enterMagesGuild()
|
||||
CFunctionList<void()> onYes = [this](){ openMagesGuild(); };
|
||||
CFunctionList<void()> onNo = onYes;
|
||||
onYes += [hero](){ LOCPLINT->cb->buyArtifact(hero, ArtifactID::SPELLBOOK); };
|
||||
std::vector<std::shared_ptr<CComponent>> components(1, std::make_shared<CComponent>(CComponent::artifact,ArtifactID::SPELLBOOK,0));
|
||||
std::vector<std::shared_ptr<CComponent>> components(1, std::make_shared<CComponent>(ComponentType::ARTIFACT, ArtifactID(ArtifactID::SPELLBOOK)));
|
||||
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[214], onYes, onNo, components);
|
||||
}
|
||||
@ -1129,7 +1129,7 @@ void CCreaInfo::showPopupWindow(const Point & cursorPosition)
|
||||
if (showAvailable)
|
||||
GH.windows().createAndPushWindow<CDwellingInfoBox>(GH.screenDimensions().x / 2, GH.screenDimensions().y / 2, town, level);
|
||||
else
|
||||
CRClickPopup::createAndPush(genGrowthText(), std::make_shared<CComponent>(CComponent::creature, creature->getId()));
|
||||
CRClickPopup::createAndPush(genGrowthText(), std::make_shared<CComponent>(ComponentType::CREATURE, creature->getId()));
|
||||
}
|
||||
|
||||
bool CCreaInfo::getShowAvailable()
|
||||
@ -1180,7 +1180,7 @@ void CTownInfo::showPopupWindow(const Point & cursorPosition)
|
||||
{
|
||||
if(building)
|
||||
{
|
||||
auto c = std::make_shared<CComponent>(CComponent::building, building->town->faction->getIndex(), building->bid);
|
||||
auto c = std::make_shared<CComponent>(ComponentType::BUILDING, BuildingTypeUniqueID(building->town->faction->getId(), building->bid));
|
||||
CRClickPopup::createAndPush(CInfoWindow::genText(building->getNameTranslated(), building->getDescriptionTranslated()), c);
|
||||
}
|
||||
}
|
||||
@ -1526,15 +1526,24 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
|
||||
//Create components for all required resources
|
||||
std::vector<std::shared_ptr<CComponent>> components;
|
||||
|
||||
for(int i = 0; i<GameConstants::RESOURCE_QUANTITY; i++)
|
||||
for(GameResID i : GameResID::ALL_RESOURCES())
|
||||
{
|
||||
if(building->resources[i])
|
||||
{
|
||||
std::string text = std::to_string(building->resources[i]);
|
||||
int resAfterBuy = LOCPLINT->cb->getResourceAmount(GameResID(i)) - building->resources[i];
|
||||
if(resAfterBuy < 0 && state != EBuildingState::ALREADY_PRESENT && settings["general"]["enableUiEnhancements"].Bool())
|
||||
text = "{H3Red|" + std::to_string(LOCPLINT->cb->getResourceAmount(GameResID(i))) + "}" + " / " + text;
|
||||
components.push_back(std::make_shared<CComponent>(CComponent::resource, i, text, CComponent::small));
|
||||
MetaString message;
|
||||
int resourceAmount = LOCPLINT->cb->getResourceAmount(i);
|
||||
bool canAfford = resourceAmount >= building->resources[i];
|
||||
|
||||
if(!canAfford && state != EBuildingState::ALREADY_PRESENT && settings["general"]["enableUiEnhancements"].Bool())
|
||||
{
|
||||
message.appendRawString("{H3Red|%d}/%d");
|
||||
message.replaceNumber(resourceAmount);
|
||||
}
|
||||
else
|
||||
message.appendRawString("%d");
|
||||
|
||||
message.replaceNumber(building->resources[i]);
|
||||
components.push_back(std::make_shared<CComponent>(ComponentType::RESOURCE, i, message.toString(), CComponent::small));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1889,12 +1898,12 @@ CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell)
|
||||
|
||||
void CMageGuildScreen::Scroll::clickPressed(const Point & cursorPosition)
|
||||
{
|
||||
LOCPLINT->showInfoDialog(spell->getDescriptionTranslated(0), std::make_shared<CComponent>(CComponent::spell, spell->id));
|
||||
LOCPLINT->showInfoDialog(spell->getDescriptionTranslated(0), std::make_shared<CComponent>(ComponentType::SPELL, spell->id));
|
||||
}
|
||||
|
||||
void CMageGuildScreen::Scroll::showPopupWindow(const Point & cursorPosition)
|
||||
{
|
||||
CRClickPopup::createAndPush(spell->getDescriptionTranslated(0), std::make_shared<CComponent>(CComponent::spell, spell->id));
|
||||
CRClickPopup::createAndPush(spell->getDescriptionTranslated(0), std::make_shared<CComponent>(ComponentType::SPELL, spell->id));
|
||||
}
|
||||
|
||||
void CMageGuildScreen::Scroll::hover(bool on)
|
||||
@ -1935,7 +1944,7 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art
|
||||
cancelText.appendTextID("core.genrltxt.596");
|
||||
cancelText.replaceTextID(creature->getNameSingularTextID());
|
||||
|
||||
std::string costString = std::to_string(aid.toArtifact(CGI->artifacts())->getPrice());
|
||||
std::string costString = std::to_string(aid.toEntity(CGI)->getPrice());
|
||||
|
||||
title = std::make_shared<CLabel>(165, 28, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, titleString.toString());
|
||||
costText = std::make_shared<CLabel>(165, 218, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->jktexts[43]);
|
||||
|
@ -325,7 +325,7 @@ CStackWindow::ButtonsSection::ButtonsSection(CStackWindow * owner, int yOffset)
|
||||
std::vector<std::shared_ptr<CComponent>> resComps;
|
||||
for(TResources::nziterator i(totalCost); i.valid(); i++)
|
||||
{
|
||||
resComps.push_back(std::make_shared<CComponent>(CComponent::resource, i->resType, (int)i->resVal));
|
||||
resComps.push_back(std::make_shared<CComponent>(ComponentType::RESOURCE, i->resType, i->resVal));
|
||||
}
|
||||
|
||||
if(LOCPLINT->cb->getResourceAmount().canAfford(totalCost))
|
||||
@ -582,10 +582,10 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
|
||||
const CCommanderInstance * commander = parent->info->commander;
|
||||
expRankIcon = std::make_shared<CAnimImage>(AnimationPath::builtin("PSKIL42"), 4, 0, pos.x, pos.y);
|
||||
|
||||
auto area = std::make_shared<LRClickableAreaWTextComp>(Rect(pos.x, pos.y, 44, 44), CComponent::experience);
|
||||
auto area = std::make_shared<LRClickableAreaWTextComp>(Rect(pos.x, pos.y, 44, 44), ComponentType::EXPERIENCE);
|
||||
expArea = area;
|
||||
area->text = CGI->generaltexth->allTexts[2];
|
||||
area->bonusValue = commander->getExpRank();
|
||||
area->component.value = commander->getExpRank();
|
||||
boost::replace_first(area->text, "%d", std::to_string(commander->getExpRank()));
|
||||
boost::replace_first(area->text, "%d", std::to_string(CGI->heroh->reqExp(commander->getExpRank() + 1)));
|
||||
boost::replace_first(area->text, "%d", std::to_string(commander->experience));
|
||||
@ -611,8 +611,8 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
|
||||
if(art)
|
||||
{
|
||||
parent->stackArtifactIcon = std::make_shared<CAnimImage>(AnimationPath::builtin("ARTIFACT"), art->artType->getIconIndex(), 0, pos.x, pos.y);
|
||||
parent->stackArtifactHelp = std::make_shared<LRClickableAreaWTextComp>(Rect(pos, Point(44, 44)), CComponent::artifact);
|
||||
parent->stackArtifactHelp->type = art->artType->getId();
|
||||
parent->stackArtifactHelp = std::make_shared<LRClickableAreaWTextComp>(Rect(pos, Point(44, 44)), ComponentType::ARTIFACT);
|
||||
parent->stackArtifactHelp->component.subType = art->artType->getId();
|
||||
|
||||
if(parent->info->owner)
|
||||
{
|
||||
@ -974,11 +974,12 @@ void CStackWindow::removeStackArtifact(ArtifactPosition pos)
|
||||
const auto slot = ArtifactUtils::getArtBackpackPosition(info->owner, art->getTypeId());
|
||||
if(slot != ArtifactPosition::PRE_FIRST)
|
||||
{
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(info->stackNode, pos), ArtifactLocation(info->owner, slot));
|
||||
auto artLoc = ArtifactLocation(info->owner->id, pos);
|
||||
artLoc.creature = info->stackNode->armyObj->findStack(info->stackNode);
|
||||
LOCPLINT->cb->swapArtifacts(artLoc, ArtifactLocation(info->owner->id, slot));
|
||||
stackArtifactButton.reset();
|
||||
stackArtifactHelp.reset();
|
||||
stackArtifactIcon.reset();
|
||||
redraw();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,9 +119,9 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero)
|
||||
|
||||
for(int v = 0; v < GameConstants::PRIMARY_SKILLS; ++v)
|
||||
{
|
||||
auto area = std::make_shared<LRClickableAreaWTextComp>(Rect(30 + 70 * v, 109, 42, 64), CComponent::primskill);
|
||||
auto area = std::make_shared<LRClickableAreaWTextComp>(Rect(30 + 70 * v, 109, 42, 64), ComponentType::PRIM_SKILL);
|
||||
area->text = CGI->generaltexth->arraytxt[2+v];
|
||||
area->type = v;
|
||||
area->component.subType = PrimarySkill(v);
|
||||
area->hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % CGI->generaltexth->primarySkillNames[v]);
|
||||
primSkillAreas.push_back(area);
|
||||
|
||||
@ -154,7 +154,7 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero)
|
||||
for(int i = 0; i < std::min<size_t>(hero->secSkills.size(), 8u); ++i)
|
||||
{
|
||||
Rect r = Rect(i%2 == 0 ? 18 : 162, 276 + 48 * (i/2), 136, 42);
|
||||
secSkillAreas.push_back(std::make_shared<LRClickableAreaWTextComp>(r, CComponent::secskill));
|
||||
secSkillAreas.push_back(std::make_shared<LRClickableAreaWTextComp>(r, ComponentType::SEC_SKILL));
|
||||
secSkillImages.push_back(std::make_shared<CAnimImage>(secSkills, 0, 0, r.x, r.y));
|
||||
|
||||
int x = (i % 2) ? 212 : 68;
|
||||
@ -232,20 +232,21 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
|
||||
//primary skills support
|
||||
for(size_t g=0; g<primSkillAreas.size(); ++g)
|
||||
{
|
||||
primSkillAreas[g]->bonusValue = curHero->getPrimSkillLevel(static_cast<PrimarySkill>(g));
|
||||
primSkillValues[g]->setText(std::to_string(primSkillAreas[g]->bonusValue));
|
||||
int value = curHero->getPrimSkillLevel(static_cast<PrimarySkill>(g));
|
||||
primSkillAreas[g]->component.value = value;
|
||||
primSkillValues[g]->setText(std::to_string(value));
|
||||
}
|
||||
|
||||
//secondary skills support
|
||||
for(size_t g=0; g< secSkillAreas.size(); ++g)
|
||||
{
|
||||
int skill = curHero->secSkills[g].first;
|
||||
int level = curHero->getSecSkillLevel(SecondarySkill(curHero->secSkills[g].first));
|
||||
SecondarySkill skill = curHero->secSkills[g].first;
|
||||
int level = curHero->getSecSkillLevel(skill);
|
||||
std::string skillName = CGI->skillh->getByIndex(skill)->getNameTranslated();
|
||||
std::string skillValue = CGI->generaltexth->levels[level-1];
|
||||
|
||||
secSkillAreas[g]->type = skill;
|
||||
secSkillAreas[g]->bonusValue = level;
|
||||
secSkillAreas[g]->component.subType = skill;
|
||||
secSkillAreas[g]->component.value = level;
|
||||
secSkillAreas[g]->text = CGI->skillh->getByIndex(skill)->getDescriptionTranslated(level);
|
||||
secSkillAreas[g]->hoverText = boost::str(boost::format(heroscrn[21]) % skillValue % skillName);
|
||||
secSkillImages[g]->setFrame(skill*3 + level + 2);
|
||||
@ -334,20 +335,11 @@ void CHeroWindow::commanderWindow()
|
||||
if(pickedArtInst)
|
||||
{
|
||||
const auto freeSlot = ArtifactUtils::getArtAnyPosition(curHero->commander, pickedArtInst->getTypeId());
|
||||
if(freeSlot < ArtifactPosition::COMMANDER_AFTER_LAST) //we don't want to put it in commander's backpack!
|
||||
if(vstd::contains(ArtifactUtils::commanderSlots(), freeSlot)) // We don't want to put it in commander's backpack!
|
||||
{
|
||||
ArtifactLocation src(hero, ArtifactPosition::TRANSITION_POS);
|
||||
ArtifactLocation dst(curHero->commander.get(), freeSlot);
|
||||
|
||||
if(pickedArtInst->canBePutAt(dst, true))
|
||||
{ //equip clicked stack
|
||||
if(dst.getArt())
|
||||
{
|
||||
LOCPLINT->cb->swapArtifacts(dst, ArtifactLocation(hero,
|
||||
ArtifactUtils::getArtBackpackPosition(hero, pickedArtInst->getTypeId())));
|
||||
}
|
||||
LOCPLINT->cb->swapArtifacts(src, dst);
|
||||
}
|
||||
ArtifactLocation dst(curHero->id, freeSlot);
|
||||
dst.creature = SlotID::COMMANDER_SLOT_PLACEHOLDER;
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero->id, ArtifactPosition::TRANSITION_POS), dst);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -256,7 +256,7 @@ void InfoBoxAbstractHeroData::prepareMessage(std::string & text, std::shared_ptr
|
||||
break;
|
||||
case HERO_PRIMARY_SKILL:
|
||||
text = CGI->generaltexth->arraytxt[2+getSubID()];
|
||||
comp = std::make_shared<CComponent>(CComponent::primskill, getSubID(), (int)getValue());
|
||||
comp = std::make_shared<CComponent>(ComponentType::PRIM_SKILL, PrimarySkill(getSubID()), getValue());
|
||||
break;
|
||||
case HERO_MANA:
|
||||
text = CGI->generaltexth->allTexts[149];
|
||||
@ -271,7 +271,7 @@ void InfoBoxAbstractHeroData::prepareMessage(std::string & text, std::shared_ptr
|
||||
if(value)
|
||||
{
|
||||
text = CGI->skillh->getByIndex(subID)->getDescriptionTranslated((int)value);
|
||||
comp = std::make_shared<CComponent>(CComponent::secskill, subID, (int)value);
|
||||
comp = std::make_shared<CComponent>(ComponentType::SEC_SKILL, SecondarySkill(subID), (int)value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -558,7 +558,7 @@ void CSpellWindow::SpellArea::clickPressed(const Point & cursorPosition)
|
||||
//battle spell on adv map or adventure map spell during combat => display infowindow, not cast
|
||||
if((combatSpell ^ inCombat) || inCastle)
|
||||
{
|
||||
std::vector<std::shared_ptr<CComponent>> hlp(1, std::make_shared<CComponent>(CComponent::spell, mySpell->id, 0));
|
||||
std::vector<std::shared_ptr<CComponent>> hlp(1, std::make_shared<CComponent>(ComponentType::SPELL, mySpell->id));
|
||||
LOCPLINT->showInfoDialog(mySpell->getDescriptionTranslated(schoolLevel), hlp);
|
||||
}
|
||||
else if(combatSpell)
|
||||
@ -614,7 +614,7 @@ void CSpellWindow::SpellArea::showPopupWindow(const Point & cursorPosition)
|
||||
boost::algorithm::replace_first(dmgInfo, "%d", std::to_string(causedDmg));
|
||||
}
|
||||
|
||||
CRClickPopup::createAndPush(mySpell->getDescriptionTranslated(schoolLevel) + dmgInfo, std::make_shared<CComponent>(CComponent::spell, mySpell->id));
|
||||
CRClickPopup::createAndPush(mySpell->getDescriptionTranslated(schoolLevel) + dmgInfo, std::make_shared<CComponent>(ComponentType::SPELL, mySpell->id));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,8 +191,8 @@ void CTradeWindow::CTradeableItem::clickPressed(const Point & cursorPosition)
|
||||
const auto hero = aw->arts->getHero();
|
||||
const auto slot = hero->getSlotByInstance(art);
|
||||
assert(slot != ArtifactPosition::PRE_FIRST);
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero, slot),
|
||||
ArtifactLocation(hero, ArtifactPosition::TRANSITION_POS));
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero->id, slot),
|
||||
ArtifactLocation(hero->id, ArtifactPosition::TRANSITION_POS));
|
||||
aw->arts->pickedArtFromSlot = slot;
|
||||
aw->arts->artifactsOnAltar.erase(art);
|
||||
setID(-1);
|
||||
@ -667,10 +667,10 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta
|
||||
title = (*CGI->townh)[ETownType::STRONGHOLD]->town->buildings[BuildingID::FREELANCERS_GUILD]->getNameTranslated();
|
||||
break;
|
||||
case EMarketMode::RESOURCE_ARTIFACT:
|
||||
title = (*CGI->townh)[o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated();
|
||||
title = (*CGI->townh)[o->getFaction()]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated();
|
||||
break;
|
||||
case EMarketMode::ARTIFACT_RESOURCE:
|
||||
title = (*CGI->townh)[o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated();
|
||||
title = (*CGI->townh)[o->getFaction()]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated();
|
||||
|
||||
// create image that copies part of background containing slot MISC_1 into position of slot MISC_5
|
||||
// this is workaround for bug in H3 files where this slot for ragdoll on this screen is missing
|
||||
|
@ -39,7 +39,8 @@ CWindowObject::CWindowObject(int options_, const ImagePath & imageName, Point ce
|
||||
options(options_),
|
||||
background(createBg(imageName, options & PLAYER_COLORED))
|
||||
{
|
||||
assert(parent == nullptr); //Safe to remove, but windows should not have parent
|
||||
if(!(options & NEEDS_ANIMATED_BACKGROUND)) //currently workaround for highscores (currently uses window as normal control, because otherwise videos are not played in background yet)
|
||||
assert(parent == nullptr); //Safe to remove, but windows should not have parent
|
||||
|
||||
defActions = 255-DISPOSE;
|
||||
|
||||
@ -60,7 +61,8 @@ CWindowObject::CWindowObject(int options_, const ImagePath & imageName):
|
||||
options(options_),
|
||||
background(createBg(imageName, options_ & PLAYER_COLORED))
|
||||
{
|
||||
assert(parent == nullptr); //Safe to remove, but windows should not have parent
|
||||
if(!(options & NEEDS_ANIMATED_BACKGROUND)) //currently workaround for highscores (currently uses window as normal control, because otherwise videos are not played in background yet)
|
||||
assert(parent == nullptr); //Safe to remove, but windows should not have parent
|
||||
|
||||
defActions = 255-DISPOSE;
|
||||
|
||||
|
@ -38,7 +38,8 @@ public:
|
||||
PLAYER_COLORED=1, //background will be player-colored
|
||||
RCLICK_POPUP=2, // window will behave as right-click popup
|
||||
BORDERED=4, // window will have border if current resolution is bigger than size of window
|
||||
SHADOW_DISABLED=8 //this window won't display any shadow
|
||||
SHADOW_DISABLED=8, //this window won't display any shadow
|
||||
NEEDS_ANIMATED_BACKGROUND=16 //there are videos in the background that have to be played
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -401,7 +401,7 @@ CLevelWindow::CLevelWindow(const CGHeroInstance * hero, PrimarySkill pskill, std
|
||||
std::vector<std::shared_ptr<CSelectableComponent>> comps;
|
||||
for(auto & skill : skills)
|
||||
{
|
||||
auto comp = std::make_shared<CSelectableComponent>(CComponent::secskill, skill, hero->getSecSkillLevel(SecondarySkill(skill))+1, CComponent::medium);
|
||||
auto comp = std::make_shared<CSelectableComponent>(ComponentType::SEC_SKILL, skill, hero->getSecSkillLevel(SecondarySkill(skill))+1, CComponent::medium);
|
||||
comp->onChoose = std::bind(&CLevelWindow::close, this);
|
||||
comps.push_back(comp);
|
||||
}
|
||||
@ -693,9 +693,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
|
||||
else
|
||||
primSkillAreas[g]->pos = Rect(Point(pos.x + 329, pos.y + 19 + 36 * g), Point(140, 32));
|
||||
primSkillAreas[g]->text = CGI->generaltexth->arraytxt[2+g];
|
||||
primSkillAreas[g]->type = g;
|
||||
primSkillAreas[g]->bonusValue = 0;
|
||||
primSkillAreas[g]->baseType = 0;
|
||||
primSkillAreas[g]->component = Component( ComponentType::PRIM_SKILL, PrimarySkill(g));
|
||||
primSkillAreas[g]->hoverText = CGI->generaltexth->heroscrn[1];
|
||||
boost::replace_first(primSkillAreas[g]->hoverText, "%s", CGI->generaltexth->primarySkillNames[g]);
|
||||
}
|
||||
@ -708,14 +706,11 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
|
||||
//secondary skill's clickable areas
|
||||
for(int g=0; g<hero->secSkills.size(); ++g)
|
||||
{
|
||||
int skill = hero->secSkills[g].first,
|
||||
level = hero->secSkills[g].second; // <1, 3>
|
||||
SecondarySkill skill = hero->secSkills[g].first;
|
||||
int level = hero->secSkills[g].second; // <1, 3>
|
||||
secSkillAreas[b].push_back(std::make_shared<LRClickableAreaWTextComp>());
|
||||
secSkillAreas[b][g]->pos = Rect(Point(pos.x + 32 + g * 36 + b * 454 , pos.y + (qeLayout ? 83 : 88)), Point(32, 32) );
|
||||
secSkillAreas[b][g]->baseType = 1;
|
||||
|
||||
secSkillAreas[b][g]->type = skill;
|
||||
secSkillAreas[b][g]->bonusValue = level;
|
||||
secSkillAreas[b][g]->component = Component(ComponentType::SEC_SKILL, skill, level);
|
||||
secSkillAreas[b][g]->text = CGI->skillh->getByIndex(skill)->getDescriptionTranslated(level);
|
||||
|
||||
secSkillAreas[b][g]->hoverText = CGI->generaltexth->heroscrn[21];
|
||||
@ -1082,7 +1077,7 @@ void CUniversityWindow::CItem::clickPressed(const Point & cursorPosition)
|
||||
|
||||
void CUniversityWindow::CItem::showPopupWindow(const Point & cursorPosition)
|
||||
{
|
||||
CRClickPopup::createAndPush(CGI->skillh->getByIndex(ID)->getDescriptionTranslated(1), std::make_shared<CComponent>(CComponent::secskill, ID, 1));
|
||||
CRClickPopup::createAndPush(CGI->skillh->getByIndex(ID)->getDescriptionTranslated(1), std::make_shared<CComponent>(ComponentType::SEC_SKILL, ID, 1));
|
||||
}
|
||||
|
||||
void CUniversityWindow::CItem::hover(bool on)
|
||||
|
@ -375,7 +375,7 @@ class CUniversityWindow : public CStatusbarWindow
|
||||
std::shared_ptr<CLabel> name;
|
||||
std::shared_ptr<CLabel> level;
|
||||
public:
|
||||
int ID;//id of selected skill
|
||||
SecondarySkill ID;//id of selected skill
|
||||
CUniversityWindow * parent;
|
||||
|
||||
void showAll(Canvas & to) override;
|
||||
|
@ -686,8 +686,8 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/config
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/Mods
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${MAIN_LIB_DIR}/../config ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/config
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${MAIN_LIB_DIR}/../Mods ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/Mods
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${MAIN_LIB_DIR}/../config ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/config
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${MAIN_LIB_DIR}/../Mods ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/Mods
|
||||
)
|
||||
endif()
|
||||
|
||||
|
14
cmake_modules/create_link.cmake
Normal file
14
cmake_modules/create_link.cmake
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
#message(${CMAKE_ARGV0}) # cmake.exe
|
||||
#message(${CMAKE_ARGV1}) # -P
|
||||
#message(${CMAKE_ARGV2}) # thisfilename
|
||||
#message(${CMAKE_ARGV3}) # existing
|
||||
#message(${CMAKE_ARGV4}) # linkname
|
||||
if (WIN32)
|
||||
file(TO_NATIVE_PATH ${CMAKE_ARGV3} existing_native)
|
||||
file(TO_NATIVE_PATH ${CMAKE_ARGV4} linkname_native)
|
||||
execute_process(COMMAND cmd.exe /c RD /Q "${linkname_native}")
|
||||
execute_process(COMMAND cmd.exe /c mklink /J "${linkname_native}" "${existing_native}")
|
||||
else()
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_ARGV3} ${CMAKE_ARGV4})
|
||||
endif()
|
@ -16,6 +16,10 @@ have the following options:
|
||||
- [Create signing assets on macOS from terminal](https://github.com/kambala-decapitator/xcode-auto-signing-assets). In the command replace `your.bundle.id` with something like `com.MY-NAME.vcmi`. After that use the above signer tool.
|
||||
- [Sign from any OS](https://github.com/indygreg/PyOxidizer/tree/main/tugger-code-signing). You'd still need to find a way to create signing assets (private key and provisioning profile) though.
|
||||
|
||||
To install the signed ipa on your device, you can use Xcode or Apple Configurator (available on the Mac App Store for free). The latter also allows installing ipa from the command line, here's an example that assumes you have only 1 device connected to your Mac and the signed ipa is on your desktop:
|
||||
|
||||
/Applications/Apple\ Configurator.app/Contents/MacOS/cfgutil install-app ~/Desktop/vcmi.ipa
|
||||
|
||||
## Step 2: Installing Heroes III data files
|
||||
|
||||
Note: if you don't need in-game videos, you can omit downloading/copying file VIDEO.VID from data folder - it will save your time and space. The same applies to the Mp3 directory.
|
||||
|
@ -174,12 +174,11 @@ if(APPLE_IOS)
|
||||
else()
|
||||
set(RESOURCES_DESTINATION ${DATA_DIR}/launcher)
|
||||
|
||||
# Copy to build directory for easier debugging
|
||||
# Link to build directory for easier debugging
|
||||
add_custom_command(TARGET vcmilauncher POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_SOURCE_DIR}/launcher/icons ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher/icons
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_BINARY_DIR}/translation ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher/translation
|
||||
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${CMAKE_SOURCE_DIR}/launcher/icons ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher/icons
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${CMAKE_CURRENT_BINARY_DIR}/translation ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher/translation
|
||||
)
|
||||
|
||||
install(TARGETS vcmilauncher DESTINATION ${BIN_DIR})
|
||||
|
@ -90,7 +90,9 @@ void CSettingsView::loadSettings()
|
||||
ui->comboBoxFriendlyAI->setCurrentText(QString::fromStdString(settings["server"]["friendlyAI"].String()));
|
||||
ui->comboBoxNeutralAI->setCurrentText(QString::fromStdString(settings["server"]["neutralAI"].String()));
|
||||
ui->comboBoxEnemyAI->setCurrentText(QString::fromStdString(settings["server"]["enemyAI"].String()));
|
||||
|
||||
ui->comboBoxEnemyPlayerAI->setCurrentText(QString::fromStdString(settings["server"]["playerAI"].String()));
|
||||
ui->comboBoxAlliedPlayerAI->setCurrentText(QString::fromStdString(settings["server"]["alliedAI"].String()));
|
||||
|
||||
ui->spinBoxNetworkPort->setValue(settings["server"]["port"].Integer());
|
||||
|
||||
|
@ -74,6 +74,49 @@ DLL_LINKAGE const std::vector<ArtifactPosition> & ArtifactUtils::constituentWorn
|
||||
return positions;
|
||||
}
|
||||
|
||||
DLL_LINKAGE const std::vector<ArtifactPosition> & ArtifactUtils::allWornSlots()
|
||||
{
|
||||
static const std::vector<ArtifactPosition> positions =
|
||||
{
|
||||
ArtifactPosition::HEAD,
|
||||
ArtifactPosition::SHOULDERS,
|
||||
ArtifactPosition::NECK,
|
||||
ArtifactPosition::RIGHT_HAND,
|
||||
ArtifactPosition::LEFT_HAND,
|
||||
ArtifactPosition::TORSO,
|
||||
ArtifactPosition::RIGHT_RING,
|
||||
ArtifactPosition::LEFT_RING,
|
||||
ArtifactPosition::FEET,
|
||||
ArtifactPosition::MISC1,
|
||||
ArtifactPosition::MISC2,
|
||||
ArtifactPosition::MISC3,
|
||||
ArtifactPosition::MISC4,
|
||||
ArtifactPosition::MISC5,
|
||||
ArtifactPosition::MACH1,
|
||||
ArtifactPosition::MACH2,
|
||||
ArtifactPosition::MACH3,
|
||||
ArtifactPosition::MACH4,
|
||||
ArtifactPosition::SPELLBOOK
|
||||
};
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
DLL_LINKAGE const std::vector<ArtifactPosition> & ArtifactUtils::commanderSlots()
|
||||
{
|
||||
static const std::vector<ArtifactPosition> positions =
|
||||
{
|
||||
ArtifactPosition::COMMANDER1,
|
||||
ArtifactPosition::COMMANDER2,
|
||||
ArtifactPosition::COMMANDER3,
|
||||
ArtifactPosition::COMMANDER4,
|
||||
ArtifactPosition::COMMANDER5,
|
||||
ArtifactPosition::COMMANDER6
|
||||
};
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
DLL_LINKAGE bool ArtifactUtils::isArtRemovable(const std::pair<ArtifactPosition, ArtSlotInfo> & slot)
|
||||
{
|
||||
return slot.second.artifact
|
||||
@ -214,7 +257,7 @@ DLL_LINKAGE void ArtifactUtils::insertScrrollSpellName(std::string & description
|
||||
if(sid.getNum() >= 0)
|
||||
{
|
||||
if(nameStart != std::string::npos && nameEnd != std::string::npos)
|
||||
description = description.replace(nameStart, nameEnd - nameStart + 1, sid.toSpell(VLC->spells())->getNameTranslated());
|
||||
description = description.replace(nameStart, nameEnd - nameStart + 1, sid.toEntity(VLC->spells())->getNameTranslated());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,8 @@ namespace ArtifactUtils
|
||||
// TODO: Make this constexpr when the toolset is upgraded
|
||||
DLL_LINKAGE const std::vector<ArtifactPosition> & unmovableSlots();
|
||||
DLL_LINKAGE const std::vector<ArtifactPosition> & constituentWornSlots();
|
||||
DLL_LINKAGE const std::vector<ArtifactPosition> & allWornSlots();
|
||||
DLL_LINKAGE const std::vector<ArtifactPosition> & commanderSlots();
|
||||
DLL_LINKAGE bool isArtRemovable(const std::pair<ArtifactPosition, ArtSlotInfo> & slot);
|
||||
DLL_LINKAGE bool checkSpellbookIsNeeded(const CGHeroInstance * heroPtr, const ArtifactID & artID, const ArtifactPosition & slot);
|
||||
DLL_LINKAGE bool isSlotBackpack(const ArtifactPosition & slot);
|
||||
|
@ -167,7 +167,7 @@ bool CArtifact::isBig() const
|
||||
|
||||
bool CArtifact::isTradable() const
|
||||
{
|
||||
switch(id)
|
||||
switch(id.toEnum())
|
||||
{
|
||||
case ArtifactID::SPELLBOOK:
|
||||
case ArtifactID::GRAIL:
|
||||
@ -693,8 +693,8 @@ void CArtHandler::makeItCommanderArt(CArtifact * a, bool onlyCommander)
|
||||
a->possibleSlots[ArtBearer::HERO].clear();
|
||||
a->possibleSlots[ArtBearer::CREATURE].clear();
|
||||
}
|
||||
for (int i = ArtifactPosition::COMMANDER1; i <= ArtifactPosition::COMMANDER6; ++i)
|
||||
a->possibleSlots[ArtBearer::COMMANDER].push_back(ArtifactPosition(i));
|
||||
for(const auto & slot : ArtifactUtils::commanderSlots())
|
||||
a->possibleSlots[ArtBearer::COMMANDER].push_back(ArtifactPosition(slot));
|
||||
}
|
||||
|
||||
bool CArtHandler::legalArtifact(const ArtifactID & id)
|
||||
@ -975,9 +975,9 @@ const ArtSlotInfo * CArtifactSet::getSlot(const ArtifactPosition & pos) const
|
||||
}
|
||||
if(vstd::contains(artifactsWorn, pos))
|
||||
return &artifactsWorn.at(pos);
|
||||
if(pos >= ArtifactPosition::AFTER_LAST )
|
||||
if(ArtifactUtils::isSlotBackpack(pos))
|
||||
{
|
||||
int backpackPos = (int)pos - ArtifactPosition::BACKPACK_START;
|
||||
auto backpackPos = pos - ArtifactPosition::BACKPACK_START;
|
||||
if(backpackPos < 0 || backpackPos >= artifactsInBackpack.size())
|
||||
return nullptr;
|
||||
else
|
||||
@ -1080,9 +1080,9 @@ void CArtifactSet::serializeJsonArtifacts(JsonSerializeFormat & handler, const s
|
||||
|
||||
void CArtifactSet::serializeJsonHero(JsonSerializeFormat & handler, CMap * map)
|
||||
{
|
||||
for(ArtifactPosition ap = ArtifactPosition::HEAD; ap < ArtifactPosition::AFTER_LAST; ap.advance(1))
|
||||
for(const auto & slot : ArtifactUtils::allWornSlots())
|
||||
{
|
||||
serializeJsonSlot(handler, ap, map);
|
||||
serializeJsonSlot(handler, slot, map);
|
||||
}
|
||||
|
||||
std::vector<ArtifactID> backpackTemp;
|
||||
|
@ -155,9 +155,9 @@ void CArtifactInstance::setId(ArtifactInstanceID id)
|
||||
this->id = id;
|
||||
}
|
||||
|
||||
bool CArtifactInstance::canBePutAt(const ArtifactLocation & al, bool assumeDestRemoved) const
|
||||
bool CArtifactInstance::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved) const
|
||||
{
|
||||
return artType->canBePutAt(al.getHolderArtSet(), al.slot, assumeDestRemoved);
|
||||
return artType->canBePutAt(artSet, slot, assumeDestRemoved);
|
||||
}
|
||||
|
||||
bool CArtifactInstance::isCombined() const
|
||||
@ -165,15 +165,15 @@ bool CArtifactInstance::isCombined() const
|
||||
return artType->isCombined();
|
||||
}
|
||||
|
||||
void CArtifactInstance::putAt(const ArtifactLocation & al)
|
||||
void CArtifactInstance::putAt(CArtifactSet & set, const ArtifactPosition slot)
|
||||
{
|
||||
auto placementMap = al.getHolderArtSet()->putArtifact(al.slot, this);
|
||||
auto placementMap = set.putArtifact(slot, this);
|
||||
addPlacementMap(placementMap);
|
||||
}
|
||||
|
||||
void CArtifactInstance::removeFrom(const ArtifactLocation & al)
|
||||
void CArtifactInstance::removeFrom(CArtifactSet & set, const ArtifactPosition slot)
|
||||
{
|
||||
al.getHolderArtSet()->removeArtifact(al.slot);
|
||||
set.removeArtifact(slot);
|
||||
for(auto & part : partsInfo)
|
||||
{
|
||||
if(part.slot != ArtifactPosition::PRE_FIRST)
|
||||
@ -181,10 +181,10 @@ void CArtifactInstance::removeFrom(const ArtifactLocation & al)
|
||||
}
|
||||
}
|
||||
|
||||
void CArtifactInstance::move(const ArtifactLocation & src, const ArtifactLocation & dst)
|
||||
void CArtifactInstance::move(CArtifactSet & srcSet, const ArtifactPosition srcSlot, CArtifactSet & dstSet, const ArtifactPosition dstSlot)
|
||||
{
|
||||
removeFrom(src);
|
||||
putAt(dst);
|
||||
removeFrom(srcSet, srcSlot);
|
||||
putAt(dstSet, dstSlot);
|
||||
}
|
||||
|
||||
void CArtifactInstance::deserializationFix()
|
||||
|
@ -84,11 +84,12 @@ public:
|
||||
ArtifactInstanceID getId() const;
|
||||
void setId(ArtifactInstanceID id);
|
||||
|
||||
bool canBePutAt(const ArtifactLocation & al, bool assumeDestRemoved = false) const;
|
||||
bool canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot = ArtifactPosition::FIRST_AVAILABLE,
|
||||
bool assumeDestRemoved = false) const;
|
||||
bool isCombined() const;
|
||||
void putAt(const ArtifactLocation & al);
|
||||
void removeFrom(const ArtifactLocation & al);
|
||||
void move(const ArtifactLocation & src, const ArtifactLocation & dst);
|
||||
void putAt(CArtifactSet & set, const ArtifactPosition slot);
|
||||
void removeFrom(CArtifactSet & set, const ArtifactPosition slot);
|
||||
void move(CArtifactSet & srcSet, const ArtifactPosition srcSlot, CArtifactSet & dstSet, const ArtifactPosition dstSlot);
|
||||
|
||||
void deserializationFix();
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
BuildingID CBuildingHandler::campToERMU(int camp, int townType, const std::set<BuildingID> & builtBuildings)
|
||||
BuildingID CBuildingHandler::campToERMU(int camp, FactionID townType, const std::set<BuildingID> & builtBuildings)
|
||||
{
|
||||
static const std::vector<BuildingID> campToERMU =
|
||||
{
|
||||
@ -47,13 +47,13 @@ BuildingID CBuildingHandler::campToERMU(int camp, int townType, const std::set<B
|
||||
|
||||
if (i < 5) // last two levels don't have reserved horde ID. Yet another H3C weirdeness
|
||||
{
|
||||
if (vstd::contains(hordeLvlsPerTType[townType], i))
|
||||
if (vstd::contains(hordeLvlsPerTType[townType.getNum()], i))
|
||||
{
|
||||
if (camp == curPos)
|
||||
{
|
||||
if (hordeLvlsPerTType[townType][0] == i)
|
||||
if (hordeLvlsPerTType[townType.getNum()][0] == i)
|
||||
{
|
||||
BuildingID dwellingID(BuildingID::DWELL_UP_FIRST + hordeLvlsPerTType[townType][0]);
|
||||
BuildingID dwellingID(BuildingID::DWELL_UP_FIRST + hordeLvlsPerTType[townType.getNum()][0]);
|
||||
|
||||
if(vstd::contains(builtBuildings, dwellingID)) //if upgraded dwelling is built
|
||||
return BuildingID::HORDE_1_UPGR;
|
||||
@ -62,9 +62,9 @@ BuildingID CBuildingHandler::campToERMU(int camp, int townType, const std::set<B
|
||||
}
|
||||
else
|
||||
{
|
||||
if(hordeLvlsPerTType[townType].size() > 1)
|
||||
if(hordeLvlsPerTType[townType.getNum()].size() > 1)
|
||||
{
|
||||
BuildingID dwellingID(BuildingID::DWELL_UP_FIRST + hordeLvlsPerTType[townType][1]);
|
||||
BuildingID dwellingID(BuildingID::DWELL_UP_FIRST + hordeLvlsPerTType[townType.getNum()][1]);
|
||||
|
||||
if(vstd::contains(builtBuildings, dwellingID)) //if upgraded dwelling is built
|
||||
return BuildingID::HORDE_2_UPGR;
|
||||
|
@ -16,7 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
class DLL_LINKAGE CBuildingHandler
|
||||
{
|
||||
public:
|
||||
static BuildingID campToERMU(int camp, int townType, const std::set<BuildingID> & builtBuildings);
|
||||
static BuildingID campToERMU(int camp, FactionID townType, const std::set<BuildingID> & builtBuildings);
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -263,7 +263,7 @@ bool CCreature::isDoubleWide() const
|
||||
*/
|
||||
bool CCreature::isGood () const
|
||||
{
|
||||
return VLC->factions()->getByIndex(faction)->getAlignment() == EAlignment::GOOD;
|
||||
return VLC->factions()->getById(faction)->getAlignment() == EAlignment::GOOD;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -272,7 +272,7 @@ bool CCreature::isGood () const
|
||||
*/
|
||||
bool CCreature::isEvil () const
|
||||
{
|
||||
return VLC->factions()->getByIndex(faction)->getAlignment() == EAlignment::EVIL;
|
||||
return VLC->factions()->getById(faction)->getAlignment() == EAlignment::EVIL;
|
||||
}
|
||||
|
||||
si32 CCreature::maxAmount(const TResources &res) const //how many creatures can be bought
|
||||
@ -388,7 +388,7 @@ void CCreature::serializeJson(JsonSerializeFormat & handler)
|
||||
if(handler.updating)
|
||||
{
|
||||
cost.serializeJson(handler, "cost");
|
||||
handler.serializeInt("faction", faction);//TODO: unify with deferred resolve
|
||||
handler.serializeId("faction", faction);
|
||||
}
|
||||
|
||||
handler.serializeInt("level", level);
|
||||
@ -1350,34 +1350,23 @@ CCreatureHandler::~CCreatureHandler()
|
||||
|
||||
CreatureID CCreatureHandler::pickRandomMonster(CRandomGenerator & rand, int tier) const
|
||||
{
|
||||
int r = 0;
|
||||
if(tier == -1) //pick any allowed creature
|
||||
std::vector<CreatureID> allowed;
|
||||
for(const auto & creature : objects)
|
||||
{
|
||||
do
|
||||
{
|
||||
r = (*RandomGeneratorUtil::nextItem(objects, rand))->getId();
|
||||
} while (objects[r] && objects[r]->special); // find first "not special" creature
|
||||
if(creature->special)
|
||||
continue;
|
||||
|
||||
if (creature->level == tier || tier == -1)
|
||||
allowed.push_back(creature->getId());
|
||||
}
|
||||
else
|
||||
|
||||
if(allowed.empty())
|
||||
{
|
||||
assert(vstd::iswithin(tier, 1, 7));
|
||||
std::vector<CreatureID> allowed;
|
||||
for(const auto & creature : objects)
|
||||
{
|
||||
if(!creature->special && creature->level == tier)
|
||||
allowed.push_back(creature->getId());
|
||||
}
|
||||
|
||||
if(allowed.empty())
|
||||
{
|
||||
logGlobal->warn("Cannot pick a random creature of tier %d!", tier);
|
||||
return CreatureID::NONE;
|
||||
}
|
||||
|
||||
return *RandomGeneratorUtil::nextItem(allowed, rand);
|
||||
logGlobal->warn("Cannot pick a random creature of tier %d!", tier);
|
||||
return CreatureID::NONE;
|
||||
}
|
||||
assert (r >= 0); //should always be, but it crashed
|
||||
return CreatureID(r);
|
||||
|
||||
return *RandomGeneratorUtil::nextItem(allowed, rand);
|
||||
}
|
||||
|
||||
|
||||
|
@ -458,7 +458,7 @@ const CStackInstance & CCreatureSet::getStack(const SlotID & slot) const
|
||||
return *getStackPtr(slot);
|
||||
}
|
||||
|
||||
const CStackInstance * CCreatureSet::getStackPtr(const SlotID & slot) const
|
||||
CStackInstance * CCreatureSet::getStackPtr(const SlotID & slot) const
|
||||
{
|
||||
if(hasStackAtSlot(slot))
|
||||
return stacks.find(slot)->second;
|
||||
@ -870,7 +870,7 @@ ArtBearer::ArtBearer CStackInstance::bearerType() const
|
||||
CStackInstance::ArtPlacementMap CStackInstance::putArtifact(ArtifactPosition pos, CArtifactInstance * art)
|
||||
{
|
||||
assert(!getArt(pos));
|
||||
assert(art->artType->canBePutAt(this, pos));
|
||||
assert(art->canBePutAt(this, pos));
|
||||
|
||||
attachTo(*art);
|
||||
return CArtifactSet::putArtifact(pos, art);
|
||||
@ -1017,6 +1017,11 @@ const Creature * CStackBasicDescriptor::getType() const
|
||||
return type;
|
||||
}
|
||||
|
||||
CreatureID CStackBasicDescriptor::getId() const
|
||||
{
|
||||
return type->getId();
|
||||
}
|
||||
|
||||
TQuantity CStackBasicDescriptor::getCount() const
|
||||
{
|
||||
return count;
|
||||
|
@ -39,6 +39,7 @@ public:
|
||||
virtual ~CStackBasicDescriptor() = default;
|
||||
|
||||
const Creature * getType() const;
|
||||
CreatureID getId() const;
|
||||
TQuantity getCount() const;
|
||||
|
||||
virtual void setType(const CCreature * c);
|
||||
@ -253,7 +254,7 @@ public:
|
||||
void setToArmy(CSimpleArmy &src); //erases all our army and moves stacks from src to us; src MUST NOT be an armed object! WARNING: use it wisely. Or better do not use at all.
|
||||
|
||||
const CStackInstance & getStack(const SlotID & slot) const; //stack must exist
|
||||
const CStackInstance * getStackPtr(const SlotID & slot) const; //if stack doesn't exist, returns nullptr
|
||||
CStackInstance * getStackPtr(const SlotID & slot) const; //if stack doesn't exist, returns nullptr
|
||||
const CCreature * getCreature(const SlotID & slot) const; //workaround of map issue;
|
||||
int getStackCount(const SlotID & slot) const;
|
||||
TExpType getStackExperience(const SlotID & slot) const;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "gameState/TavernHeroesPool.h"
|
||||
#include "gameState/QuestInfo.h"
|
||||
#include "mapObjects/CGHeroInstance.h"
|
||||
#include "networkPacks/ArtifactLocation.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "StartInfo.h" // for StartInfo
|
||||
#include "battle/BattleInfo.h" // for BattleInfo
|
||||
@ -966,6 +967,22 @@ const CGObjectInstance * CGameInfoCallback::getObjInstance( ObjectInstanceID oid
|
||||
return gs->map->objects[oid.num];
|
||||
}
|
||||
|
||||
CArtifactSet * CGameInfoCallback::getArtSet(const ArtifactLocation & loc) const
|
||||
{
|
||||
auto hero = const_cast<CGHeroInstance*>(getHero(loc.artHolder));
|
||||
if(loc.creature.has_value())
|
||||
{
|
||||
if(loc.creature.value() == SlotID::COMMANDER_SLOT_PLACEHOLDER)
|
||||
return hero->commander;
|
||||
else
|
||||
return hero->getStackPtr(loc.creature.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
return hero;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ObjectInstanceID> CGameInfoCallback::getVisibleTeleportObjects(std::vector<ObjectInstanceID> ids, PlayerColor player) const
|
||||
{
|
||||
vstd::erase_if(ids, [&](const ObjectInstanceID & id) -> bool
|
||||
|
@ -40,6 +40,8 @@ class CGameState;
|
||||
class PathfinderConfig;
|
||||
struct TurnTimerInfo;
|
||||
|
||||
struct ArtifactLocation;
|
||||
class CArtifactSet;
|
||||
class CArmedInstance;
|
||||
class CGObjectInstance;
|
||||
class CGHeroInstance;
|
||||
@ -174,6 +176,7 @@ public:
|
||||
virtual int64_t estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const; //estimates damage of given spell; returns 0 if spell causes no dmg
|
||||
virtual const CArtifactInstance * getArtInstance(ArtifactInstanceID aid) const;
|
||||
virtual const CGObjectInstance * getObjInstance(ObjectInstanceID oid) const;
|
||||
virtual CArtifactSet * getArtSet(const ArtifactLocation & loc) const;
|
||||
//virtual const CGObjectInstance * getArmyInstance(ObjectInstanceID oid) const;
|
||||
|
||||
//objects
|
||||
|
@ -125,15 +125,17 @@ SecondarySkill CHeroClass::chooseSecSkill(const std::set<SecondarySkill> & possi
|
||||
{
|
||||
int totalProb = 0;
|
||||
for(const auto & possible : possibles)
|
||||
{
|
||||
totalProb += secSkillProbability[possible];
|
||||
}
|
||||
if (secSkillProbability.count(possible) != 0)
|
||||
totalProb += secSkillProbability.at(possible);
|
||||
|
||||
if (totalProb != 0) // may trigger if set contains only banned skills (0 probability)
|
||||
{
|
||||
auto ran = rand.nextInt(totalProb - 1);
|
||||
for(const auto & possible : possibles)
|
||||
{
|
||||
ran -= secSkillProbability[possible];
|
||||
if (secSkillProbability.count(possible) != 0)
|
||||
ran -= secSkillProbability.at(possible);
|
||||
|
||||
if(ran < 0)
|
||||
{
|
||||
return possible;
|
||||
@ -151,7 +153,7 @@ bool CHeroClass::isMagicHero() const
|
||||
|
||||
EAlignment CHeroClass::getAlignment() const
|
||||
{
|
||||
return VLC->factions()->getByIndex(faction)->getAlignment();
|
||||
return VLC->factions()->getById(faction)->getAlignment();
|
||||
}
|
||||
|
||||
int32_t CHeroClass::getIndex() const
|
||||
@ -209,7 +211,7 @@ CHeroClass::CHeroClass():
|
||||
|
||||
void CHeroClassHandler::fillPrimarySkillData(const JsonNode & node, CHeroClass * heroClass, PrimarySkill pSkill) const
|
||||
{
|
||||
const auto & skillName = NPrimarySkill::names[static_cast<int>(pSkill)];
|
||||
const auto & skillName = NPrimarySkill::names[pSkill.getNum()];
|
||||
auto currentPrimarySkillValue = static_cast<int>(node["primarySkills"][skillName].Integer());
|
||||
//minimal value is 0 for attack and defense and 1 for spell power and knowledge
|
||||
auto primarySkillLegalMinimum = (pSkill == PrimarySkill::ATTACK || pSkill == PrimarySkill::DEFENSE) ? 0 : 1;
|
||||
@ -271,8 +273,6 @@ CHeroClass * CHeroClassHandler::loadFromJson(const std::string & scope, const Js
|
||||
int probability = static_cast<int>(skillPair.second.Integer());
|
||||
VLC->identifiers()->requestIdentifier(skillPair.second.meta, "skill", skillPair.first, [heroClass, probability](si32 skillID)
|
||||
{
|
||||
if(heroClass->secSkillProbability.size() <= skillID)
|
||||
heroClass->secSkillProbability.resize(skillID + 1, -1); // -1 = override with default later
|
||||
heroClass->secSkillProbability[skillID] = probability;
|
||||
});
|
||||
}
|
||||
@ -369,11 +369,11 @@ void CHeroClassHandler::afterLoadFinalization()
|
||||
auto chance = static_cast<float>(heroClass->defaultTavernChance * faction->town->defaultTavernChance);
|
||||
heroClass->selectionProbability[faction->getId()] = static_cast<int>(sqrt(chance) + 0.5); //FIXME: replace with std::round once MVS supports it
|
||||
}
|
||||
|
||||
// set default probabilities for gaining secondary skills where not loaded previously
|
||||
heroClass->secSkillProbability.resize(VLC->skillh->size(), -1);
|
||||
for(int skillID = 0; skillID < VLC->skillh->size(); skillID++)
|
||||
{
|
||||
if(heroClass->secSkillProbability[skillID] < 0)
|
||||
if(heroClass->secSkillProbability.count(skillID) == 0)
|
||||
{
|
||||
const CSkill * skill = (*VLC->skillh)[SecondarySkill(skillID)];
|
||||
logMod->trace("%s: no probability for %s, using default", heroClass->identifier, skill->getJsonKey());
|
||||
|
@ -157,7 +157,7 @@ public:
|
||||
std::vector<int> primarySkillLowLevel; // probability (%) of getting point of primary skill when getting level
|
||||
std::vector<int> primarySkillHighLevel;// same for high levels (> 10)
|
||||
|
||||
std::vector<int> secSkillProbability; //probabilities of gaining secondary skills (out of 112), in id order
|
||||
std::map<SecondarySkill, int> secSkillProbability; //probabilities of gaining secondary skills (out of 112), in id order
|
||||
|
||||
std::map<FactionID, int> selectionProbability; //probability of selection in towns
|
||||
|
||||
@ -201,12 +201,6 @@ public:
|
||||
h & imageBattleFemale;
|
||||
h & imageMapMale;
|
||||
h & imageMapFemale;
|
||||
|
||||
if(!h.saving)
|
||||
{
|
||||
for(int & i : secSkillProbability)
|
||||
vstd::amax(i, 0);
|
||||
}
|
||||
}
|
||||
EAlignment getAlignment() const;
|
||||
};
|
||||
|
@ -120,7 +120,7 @@ DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill::LevelInf
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill & skill)
|
||||
{
|
||||
out << "Skill(" << (int)skill.id << "," << skill.identifier << "): [";
|
||||
out << "Skill(" << skill.id.getNum() << "," << skill.identifier << "): [";
|
||||
for(int i=0; i < skill.levels.size(); i++)
|
||||
out << (i ? "," : "") << skill.levels[i];
|
||||
return out << "]";
|
||||
@ -233,7 +233,7 @@ CSkill * CSkillHandler::loadFromJson(const std::string & scope, const JsonNode &
|
||||
skillAtLevel.iconMedium = levelNode["images"]["medium"].String();
|
||||
skillAtLevel.iconLarge = levelNode["images"]["large"].String();
|
||||
}
|
||||
logMod->debug("loaded secondary skill %s(%d)", identifier, (int)skill->id);
|
||||
logMod->debug("loaded secondary skill %s(%d)", identifier, skill->id.getNum());
|
||||
|
||||
return skill;
|
||||
}
|
||||
@ -262,23 +262,4 @@ std::vector<bool> CSkillHandler::getDefaultAllowed() const
|
||||
return allowedSkills;
|
||||
}
|
||||
|
||||
si32 CSkillHandler::decodeSkill(const std::string & identifier)
|
||||
{
|
||||
auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "skill", identifier);
|
||||
if(rawId)
|
||||
return rawId.value();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string CSkillHandler::encodeSkill(const si32 index)
|
||||
{
|
||||
return (*VLC->skillh)[SecondarySkill(index)]->identifier;
|
||||
}
|
||||
|
||||
std::string CSkillHandler::encodeSkillWithType(const si32 index)
|
||||
{
|
||||
return ModUtility::makeFullIdentifier("", "skill", encodeSkill(index));
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -111,11 +111,6 @@ public:
|
||||
|
||||
std::vector<bool> getDefaultAllowed() const override;
|
||||
|
||||
///json serialization helpers
|
||||
static si32 decodeSkill(const std::string & identifier);
|
||||
static std::string encodeSkill(const si32 index);
|
||||
static std::string encodeSkillWithType(const si32 index);
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & objects;
|
||||
|
@ -686,7 +686,7 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
|
||||
//MODS COMPATIBILITY FOR 0.96
|
||||
if(!ret->produce.nonZero())
|
||||
{
|
||||
switch (ret->bid) {
|
||||
switch (ret->bid.toEnum()) {
|
||||
break; case BuildingID::VILLAGE_HALL: ret->produce[EGameResID::GOLD] = 500;
|
||||
break; case BuildingID::TOWN_HALL : ret->produce[EGameResID::GOLD] = 1000;
|
||||
break; case BuildingID::CITY_HALL : ret->produce[EGameResID::GOLD] = 2000;
|
||||
|
@ -148,11 +148,11 @@ void CPrivilegedInfoCallback::getAllTiles(std::unordered_set<int3> & tiles, std:
|
||||
void CPrivilegedInfoCallback::pickAllowedArtsSet(std::vector<const CArtifact *> & out, CRandomGenerator & rand) const
|
||||
{
|
||||
for (int j = 0; j < 3 ; j++)
|
||||
out.push_back(VLC->arth->objects[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE)]);
|
||||
out.push_back(VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE).toArtifact());
|
||||
for (int j = 0; j < 3 ; j++)
|
||||
out.push_back(VLC->arth->objects[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MINOR)]);
|
||||
out.push_back(VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MINOR).toArtifact());
|
||||
|
||||
out.push_back(VLC->arth->objects[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR)]);
|
||||
out.push_back(VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR).toArtifact());
|
||||
}
|
||||
|
||||
void CPrivilegedInfoCallback::getAllowedSpells(std::vector<SpellID> & out, std::optional<ui16> level)
|
||||
|
@ -113,7 +113,7 @@ namespace JsonRandom
|
||||
if (value.empty() || value[0] != '@')
|
||||
return PrimarySkill(*VLC->identifiers()->getIdentifier(modScope, "primarySkill", value));
|
||||
else
|
||||
return PrimarySkill(loadVariable("primarySkill", value, variables, static_cast<int>(PrimarySkill::NONE)));
|
||||
return PrimarySkill(loadVariable("primarySkill", value, variables, PrimarySkill::NONE.getNum()));
|
||||
}
|
||||
|
||||
/// Method that allows type-specific object filtering
|
||||
@ -322,7 +322,7 @@ namespace JsonRandom
|
||||
for(const auto & pair : value.Struct())
|
||||
{
|
||||
PrimarySkill id = decodeKey<PrimarySkill>(pair.second.meta, pair.first, variables);
|
||||
ret[static_cast<int>(id)] += loadValue(pair.second, rng, variables);
|
||||
ret[id.getNum()] += loadValue(pair.second, rng, variables);
|
||||
}
|
||||
}
|
||||
if(value.isVector())
|
||||
@ -333,7 +333,7 @@ namespace JsonRandom
|
||||
PrimarySkill skillID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
||||
|
||||
defaultSkills.erase(skillID);
|
||||
ret[static_cast<int>(skillID)] += loadValue(element, rng, variables);
|
||||
ret[skillID.getNum()] += loadValue(element, rng, variables);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
@ -114,35 +114,35 @@ std::string MetaString::getLocalString(const std::pair<EMetaText, ui32> & txt) c
|
||||
{
|
||||
case EMetaText::ART_NAMES:
|
||||
{
|
||||
const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts());
|
||||
const auto * art = ArtifactID(ser).toEntity(VLC);
|
||||
if(art)
|
||||
return art->getNameTranslated();
|
||||
return "#!#";
|
||||
}
|
||||
case EMetaText::ART_DESCR:
|
||||
{
|
||||
const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts());
|
||||
const auto * art = ArtifactID(ser).toEntity(VLC);
|
||||
if(art)
|
||||
return art->getDescriptionTranslated();
|
||||
return "#!#";
|
||||
}
|
||||
case EMetaText::ART_EVNTS:
|
||||
{
|
||||
const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts());
|
||||
const auto * art = ArtifactID(ser).toEntity(VLC);
|
||||
if(art)
|
||||
return art->getEventTranslated();
|
||||
return "#!#";
|
||||
}
|
||||
case EMetaText::CRE_PL_NAMES:
|
||||
{
|
||||
const auto * cre = CreatureID(ser).toCreature(VLC->creatures());
|
||||
const auto * cre = CreatureID(ser).toEntity(VLC);
|
||||
if(cre)
|
||||
return cre->getNamePluralTranslated();
|
||||
return "#!#";
|
||||
}
|
||||
case EMetaText::CRE_SING_NAMES:
|
||||
{
|
||||
const auto * cre = CreatureID(ser).toCreature(VLC->creatures());
|
||||
const auto * cre = CreatureID(ser).toEntity(VLC);
|
||||
if(cre)
|
||||
return cre->getNameSingularTranslated();
|
||||
return "#!#";
|
||||
@ -157,7 +157,7 @@ std::string MetaString::getLocalString(const std::pair<EMetaText, ui32> & txt) c
|
||||
}
|
||||
case EMetaText::SPELL_NAME:
|
||||
{
|
||||
const auto * spell = SpellID(ser).toSpell(VLC->spells());
|
||||
const auto * spell = SpellID(ser).toEntity(VLC);
|
||||
if(spell)
|
||||
return spell->getNameTranslated();
|
||||
return "#!#";
|
||||
|
@ -25,19 +25,6 @@ ResourceSet::ResourceSet(const JsonNode & node)
|
||||
container[i] = static_cast<int>(node[GameConstants::RESOURCE_NAMES[i]].Float());
|
||||
}
|
||||
|
||||
ResourceSet::ResourceSet(TResource wood, TResource mercury, TResource ore, TResource sulfur, TResource crystal,
|
||||
TResource gems, TResource gold, TResource mithril)
|
||||
{
|
||||
container[GameResID(EGameResID::WOOD)] = wood;
|
||||
container[GameResID(EGameResID::MERCURY)] = mercury;
|
||||
container[GameResID(EGameResID::ORE)] = ore;
|
||||
container[GameResID(EGameResID::SULFUR)] = sulfur;
|
||||
container[GameResID(EGameResID::CRYSTAL)] = crystal;
|
||||
container[GameResID(EGameResID::GEMS)] = gems;
|
||||
container[GameResID(EGameResID::GOLD)] = gold;
|
||||
container[GameResID(EGameResID::MITHRIL)] = mithril;
|
||||
}
|
||||
|
||||
void ResourceSet::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName)
|
||||
{
|
||||
if(handler.saving && !nonZero())
|
||||
|
@ -26,12 +26,11 @@ class ResourceSet;
|
||||
class ResourceSet
|
||||
{
|
||||
private:
|
||||
std::array<TResource, GameConstants::RESOURCE_QUANTITY> container;
|
||||
std::array<TResource, GameConstants::RESOURCE_QUANTITY> container = {};
|
||||
public:
|
||||
// read resources set from json. Format example: { "gold": 500, "wood":5 }
|
||||
DLL_LINKAGE ResourceSet(const JsonNode & node);
|
||||
DLL_LINKAGE ResourceSet(TResource wood = 0, TResource mercury = 0, TResource ore = 0, TResource sulfur = 0, TResource crystal = 0,
|
||||
TResource gems = 0, TResource gold = 0, TResource mithril = 0);
|
||||
DLL_LINKAGE ResourceSet() = default;
|
||||
|
||||
|
||||
#define scalarOperator(OPSIGN) \
|
||||
|
@ -1155,7 +1155,7 @@ std::pair<const battle::Unit *, BattleHex> CBattleInfoCallback::getNearestStack(
|
||||
|
||||
BattleHex CBattleInfoCallback::getAvaliableHex(const CreatureID & creID, ui8 side, int initialPos) const
|
||||
{
|
||||
bool twoHex = VLC->creh->objects[creID]->isDoubleWide();
|
||||
bool twoHex = VLC->creatures()->getById(creID)->isDoubleWide();
|
||||
|
||||
int pos;
|
||||
if (initialPos > -1)
|
||||
@ -1663,7 +1663,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c
|
||||
|| !(spellID.toSpell()->canBeCast(this, spells::Mode::CREATURE_ACTIVE, subject)))
|
||||
continue;
|
||||
|
||||
switch (spellID)
|
||||
switch (spellID.toEnum())
|
||||
{
|
||||
case SpellID::SHIELD:
|
||||
case SpellID::FIRE_SHIELD: // not if all enemy units are shooters
|
||||
|
@ -18,7 +18,7 @@ void SideInBattle::init(const CGHeroInstance * Hero, const CArmedInstance * Army
|
||||
hero = Hero;
|
||||
armyObject = Army;
|
||||
|
||||
switch(armyObject->ID)
|
||||
switch(armyObject->ID.toEnum())
|
||||
{
|
||||
case Obj::CREATURE_GENERATOR1:
|
||||
case Obj::CREATURE_GENERATOR2:
|
||||
|
@ -218,7 +218,7 @@ int Unit::getRawSurrenderCost() const
|
||||
void UnitInfo::serializeJson(JsonSerializeFormat & handler)
|
||||
{
|
||||
handler.serializeInt("count", count);
|
||||
handler.serializeId("type", type, CreatureID::NONE);
|
||||
handler.serializeId("type", type, CreatureID(CreatureID::NONE));
|
||||
handler.serializeInt("side", side);
|
||||
handler.serializeInt("position", position);
|
||||
handler.serializeBool("summoned", summoned);
|
||||
|
@ -100,13 +100,13 @@ std::string Bonus::Description(std::optional<si32> customValue) const
|
||||
switch(source)
|
||||
{
|
||||
case BonusSource::ARTIFACT:
|
||||
str << sid.as<ArtifactID>().toArtifact(VLC->artifacts())->getNameTranslated();
|
||||
str << sid.as<ArtifactID>().toEntity(VLC)->getNameTranslated();
|
||||
break;
|
||||
case BonusSource::SPELL_EFFECT:
|
||||
str << sid.as<SpellID>().toSpell(VLC->spells())->getNameTranslated();
|
||||
str << sid.as<SpellID>().toEntity(VLC)->getNameTranslated();
|
||||
break;
|
||||
case BonusSource::CREATURE_ABILITY:
|
||||
str << sid.as<CreatureID>().toCreature(VLC->creatures())->getNamePluralTranslated();
|
||||
str << sid.as<CreatureID>().toEntity(VLC)->getNamePluralTranslated();
|
||||
break;
|
||||
case BonusSource::SECONDARY_SKILL:
|
||||
str << VLC->skills()->getById(sid.as<SecondarySkill>())->getNameTranslated();
|
||||
|
@ -114,7 +114,7 @@ CCreatureTypeLimiter::CCreatureTypeLimiter(const CCreature & creature_, bool Inc
|
||||
|
||||
void CCreatureTypeLimiter::setCreature(const CreatureID & id)
|
||||
{
|
||||
creature = VLC->creh->objects[id];
|
||||
creature = id.toCreature();
|
||||
}
|
||||
|
||||
std::string CCreatureTypeLimiter::toString() const
|
||||
@ -317,7 +317,7 @@ ILimiter::EDecision FactionLimiter::limit(const BonusLimitationContext &context)
|
||||
std::string FactionLimiter::toString() const
|
||||
{
|
||||
boost::format fmt("FactionLimiter(faction=%s)");
|
||||
fmt % VLC->factions()->getByIndex(faction)->getJsonKey();
|
||||
fmt % VLC->factions()->getById(faction)->getJsonKey();
|
||||
return fmt.str();
|
||||
}
|
||||
|
||||
@ -326,7 +326,7 @@ JsonNode FactionLimiter::toJsonNode() const
|
||||
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
|
||||
|
||||
root["type"].String() = "FACTION_LIMITER";
|
||||
root["parameters"].Vector().push_back(JsonUtils::stringNode(VLC->factions()->getByIndex(faction)->getJsonKey()));
|
||||
root["parameters"].Vector().push_back(JsonUtils::stringNode(VLC->factions()->getById(faction)->getJsonKey()));
|
||||
|
||||
return root;
|
||||
}
|
||||
|
@ -73,13 +73,13 @@ ImagePath CampaignRegions::getBackgroundName() const
|
||||
|
||||
Point CampaignRegions::getPosition(CampaignScenarioID which) const
|
||||
{
|
||||
auto const & region = regions[static_cast<int>(which)];
|
||||
auto const & region = regions[which.getNum()];
|
||||
return Point(region.xpos, region.ypos);
|
||||
}
|
||||
|
||||
ImagePath CampaignRegions::getNameFor(CampaignScenarioID which, int colorIndex, std::string type) const
|
||||
{
|
||||
auto const & region = regions[static_cast<int>(which)];
|
||||
auto const & region = regions[which.getNum()];
|
||||
|
||||
static const std::string colors[2][8] =
|
||||
{
|
||||
@ -267,24 +267,23 @@ void CampaignState::setCurrentMapAsConquered(std::vector<CGHeroInstance *> heroe
|
||||
return a->getHeroStrength() > b->getHeroStrength();
|
||||
});
|
||||
|
||||
logGlobal->info("Scenario %d of campaign %s (%s) has been completed", static_cast<int>(*currentMap), getFilename(), getNameTranslated());
|
||||
logGlobal->info("Scenario %d of campaign %s (%s) has been completed", currentMap->getNum(), getFilename(), getNameTranslated());
|
||||
|
||||
mapsConquered.push_back(*currentMap);
|
||||
auto reservedHeroes = getReservedHeroes();
|
||||
|
||||
for (auto * hero : heroes)
|
||||
{
|
||||
HeroTypeID heroType(hero->subID);
|
||||
JsonNode node = CampaignState::crossoverSerialize(hero);
|
||||
|
||||
if (reservedHeroes.count(heroType))
|
||||
if (reservedHeroes.count(hero->getHeroType()))
|
||||
{
|
||||
logGlobal->info("Hero crossover: %d (%s) exported to global pool", hero->subID, hero->getNameTranslated());
|
||||
globalHeroPool[heroType] = node;
|
||||
logGlobal->info("Hero crossover: %d (%s) exported to global pool", hero->getHeroType(), hero->getNameTranslated());
|
||||
globalHeroPool[hero->getHeroType()] = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->info("Hero crossover: %d (%s) exported to scenario pool", hero->subID, hero->getNameTranslated());
|
||||
logGlobal->info("Hero crossover: %d (%s) exported to scenario pool", hero->getHeroType(), hero->getNameTranslated());
|
||||
scenarioHeroPool[*currentMap].push_back(node);
|
||||
}
|
||||
}
|
||||
@ -321,7 +320,7 @@ std::unique_ptr<CMap> CampaignState::getMap(CampaignScenarioID scenarioId) const
|
||||
CMapService mapService;
|
||||
std::string scenarioName = getFilename().substr(0, getFilename().find('.'));
|
||||
boost::to_lower(scenarioName);
|
||||
scenarioName += ':' + std::to_string(static_cast<int>(scenarioId));
|
||||
scenarioName += ':' + std::to_string(scenarioId.getNum());
|
||||
const auto & mapContent = mapPieces.find(scenarioId)->second;
|
||||
return mapService.loadMap(mapContent.data(), mapContent.size(), scenarioName, getModName(), getEncoding());
|
||||
}
|
||||
@ -334,7 +333,7 @@ std::unique_ptr<CMapHeader> CampaignState::getMapHeader(CampaignScenarioID scena
|
||||
CMapService mapService;
|
||||
std::string scenarioName = getFilename().substr(0, getFilename().find('.'));
|
||||
boost::to_lower(scenarioName);
|
||||
scenarioName += ':' + std::to_string(static_cast<int>(scenarioId));
|
||||
scenarioName += ':' + std::to_string(scenarioId.getNum());
|
||||
const auto & mapContent = mapPieces.find(scenarioId)->second;
|
||||
return mapService.loadMapHeader(mapContent.data(), mapContent.size(), scenarioName, getModName(), getEncoding());
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <vcmi/HeroTypeService.h>
|
||||
#include <vcmi/HeroClass.h>
|
||||
#include <vcmi/HeroClassService.h>
|
||||
#include <vcmi/Services.h>
|
||||
|
||||
#include <vcmi/spells/Spell.h>
|
||||
#include <vcmi/spells/Service.h>
|
||||
@ -28,6 +29,7 @@
|
||||
#include "modding/IdentifierStorage.h"
|
||||
#include "modding/ModScope.h"
|
||||
#include "VCMI_Lib.h"
|
||||
#include "CHeroHandler.h"
|
||||
#include "CArtHandler.h"//todo: remove
|
||||
#include "CCreatureHandler.h"//todo: remove
|
||||
#include "spells/CSpellHandler.h" //todo: remove
|
||||
@ -191,12 +193,12 @@ std::string HeroTypeID::entityType()
|
||||
|
||||
const CArtifact * ArtifactIDBase::toArtifact() const
|
||||
{
|
||||
return dynamic_cast<const CArtifact*>(toArtifact(VLC->artifacts()));
|
||||
return dynamic_cast<const CArtifact*>(toEntity(VLC));
|
||||
}
|
||||
|
||||
const Artifact * ArtifactIDBase::toArtifact(const ArtifactService * service) const
|
||||
const Artifact * ArtifactIDBase::toEntity(const Services * services) const
|
||||
{
|
||||
return service->getByIndex(num);
|
||||
return services->artifacts()->getByIndex(num);
|
||||
}
|
||||
|
||||
si32 ArtifactID::decode(const std::string & identifier)
|
||||
@ -237,7 +239,12 @@ const CCreature * CreatureIDBase::toCreature() const
|
||||
return VLC->creh->objects.at(num);
|
||||
}
|
||||
|
||||
const Creature * CreatureIDBase::toCreature(const CreatureService * creatures) const
|
||||
const Creature * CreatureIDBase::toEntity(const Services * services) const
|
||||
{
|
||||
return toEntity(services->creatures());
|
||||
}
|
||||
|
||||
const Creature * CreatureIDBase::toEntity(const CreatureService * creatures) const
|
||||
{
|
||||
return creatures->getByIndex(num);
|
||||
}
|
||||
@ -271,11 +278,26 @@ const CSpell * SpellIDBase::toSpell() const
|
||||
return VLC->spellh->objects[num];
|
||||
}
|
||||
|
||||
const spells::Spell * SpellIDBase::toSpell(const spells::Service * service) const
|
||||
const spells::Spell * SpellIDBase::toEntity(const Services * services) const
|
||||
{
|
||||
return toEntity(services->spells());
|
||||
}
|
||||
|
||||
const spells::Spell * SpellIDBase::toEntity(const spells::Service * service) const
|
||||
{
|
||||
return service->getByIndex(num);
|
||||
}
|
||||
|
||||
const CHero * HeroTypeID::toHeroType() const
|
||||
{
|
||||
return dynamic_cast<const CHero*>(toEntity(VLC));
|
||||
}
|
||||
|
||||
const HeroType * HeroTypeID::toEntity(const Services * services) const
|
||||
{
|
||||
return services->heroTypes()->getByIndex(num);
|
||||
}
|
||||
|
||||
si32 SpellID::decode(const std::string & identifier)
|
||||
{
|
||||
auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "spell", identifier);
|
||||
@ -369,7 +391,7 @@ si32 FactionID::decode(const std::string & identifier)
|
||||
if(rawId)
|
||||
return rawId.value();
|
||||
else
|
||||
return FactionID::DEFAULT;
|
||||
return FactionID::DEFAULT.getNum();
|
||||
}
|
||||
|
||||
std::string FactionID::encode(const si32 index)
|
||||
@ -458,6 +480,21 @@ std::string GameResID::entityType()
|
||||
return "resource";
|
||||
}
|
||||
|
||||
const std::array<GameResID, 7> & GameResID::ALL_RESOURCES()
|
||||
{
|
||||
static const std::array allResources = {
|
||||
GameResID(WOOD),
|
||||
GameResID(MERCURY),
|
||||
GameResID(ORE),
|
||||
GameResID(SULFUR),
|
||||
GameResID(CRYSTAL),
|
||||
GameResID(GEMS),
|
||||
GameResID(GOLD)
|
||||
};
|
||||
|
||||
return allResources;
|
||||
}
|
||||
|
||||
std::string SecondarySkill::entityType()
|
||||
{
|
||||
return "secondarySkill";
|
||||
|
@ -14,10 +14,14 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class Services;
|
||||
class Artifact;
|
||||
class ArtifactService;
|
||||
class Creature;
|
||||
class CreatureService;
|
||||
class HeroType;
|
||||
class CHero;
|
||||
class HeroTypeService;
|
||||
|
||||
namespace spells
|
||||
{
|
||||
@ -186,6 +190,9 @@ public:
|
||||
DLL_LINKAGE static std::string encode(const si32 index);
|
||||
static std::string entityType();
|
||||
|
||||
const CHero * toHeroType() const;
|
||||
const HeroType * toEntity(const Services * services) const;
|
||||
|
||||
DLL_LINKAGE static const HeroTypeID NONE;
|
||||
DLL_LINKAGE static const HeroTypeID RANDOM;
|
||||
|
||||
@ -401,13 +408,9 @@ class BuildingID : public IdentifierWithEnum<BuildingID, BuildingIDBase>
|
||||
{
|
||||
public:
|
||||
using IdentifierWithEnum<BuildingID, BuildingIDBase>::IdentifierWithEnum;
|
||||
|
||||
DLL_LINKAGE static si32 decode(const std::string & identifier);
|
||||
DLL_LINKAGE static std::string encode(const si32 index);
|
||||
static std::string entityType();
|
||||
};
|
||||
|
||||
class ObjBase : public IdentifierBase
|
||||
class MapObjectBaseID : public IdentifierBase
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
@ -552,15 +555,38 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
class DLL_LINKAGE Obj : public IdentifierWithEnum<Obj, ObjBase>
|
||||
class DLL_LINKAGE MapObjectID : public IdentifierWithEnum<MapObjectID, MapObjectBaseID>
|
||||
{
|
||||
public:
|
||||
using IdentifierWithEnum<Obj, ObjBase>::IdentifierWithEnum;
|
||||
using IdentifierWithEnum<MapObjectID, MapObjectBaseID>::IdentifierWithEnum;
|
||||
|
||||
static std::string encode(int32_t index);
|
||||
static si32 decode(const std::string & identifier);
|
||||
};
|
||||
|
||||
class MapObjectSubID : public Identifier<MapObjectSubID>
|
||||
{
|
||||
public:
|
||||
constexpr MapObjectSubID(const IdentifierBase & value):
|
||||
Identifier<MapObjectSubID>(value.getNum())
|
||||
{}
|
||||
constexpr MapObjectSubID(int32_t value = -1):
|
||||
Identifier<MapObjectSubID>(value)
|
||||
{}
|
||||
|
||||
MapObjectSubID & operator =(int32_t value)
|
||||
{
|
||||
this->num = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
MapObjectSubID & operator =(const IdentifierBase & value)
|
||||
{
|
||||
this->num = value.getNum();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE RoadId : public Identifier<RoadId>
|
||||
{
|
||||
public:
|
||||
@ -607,20 +633,23 @@ public:
|
||||
TRANSITION_POS = -3,
|
||||
FIRST_AVAILABLE = -2,
|
||||
PRE_FIRST = -1, //sometimes used as error, sometimes as first free in backpack
|
||||
|
||||
// Hero
|
||||
HEAD, SHOULDERS, NECK, RIGHT_HAND, LEFT_HAND, TORSO, //5
|
||||
RIGHT_RING, LEFT_RING, FEET, //8
|
||||
MISC1, MISC2, MISC3, MISC4, //12
|
||||
MACH1, MACH2, MACH3, MACH4, //16
|
||||
SPELLBOOK, MISC5, //18
|
||||
AFTER_LAST,
|
||||
//cres
|
||||
BACKPACK_START = 19,
|
||||
|
||||
// Creatures
|
||||
CREATURE_SLOT = 0,
|
||||
COMMANDER1 = 0, COMMANDER2, COMMANDER3, COMMANDER4, COMMANDER5, COMMANDER6, COMMANDER_AFTER_LAST,
|
||||
|
||||
BACKPACK_START = 19
|
||||
|
||||
// Commander
|
||||
COMMANDER1 = 0, COMMANDER2, COMMANDER3, COMMANDER4, COMMANDER5, COMMANDER6
|
||||
};
|
||||
|
||||
static_assert (AFTER_LAST == BACKPACK_START, "incorrect number of artifact slots");
|
||||
static_assert(MISC5 < BACKPACK_START, "incorrect number of artifact slots");
|
||||
|
||||
DLL_LINKAGE static si32 decode(const std::string & identifier);
|
||||
DLL_LINKAGE static std::string encode(const si32 index);
|
||||
@ -653,7 +682,7 @@ public:
|
||||
};
|
||||
|
||||
DLL_LINKAGE const CArtifact * toArtifact() const;
|
||||
DLL_LINKAGE const Artifact * toArtifact(const ArtifactService * service) const;
|
||||
DLL_LINKAGE const Artifact * toEntity(const Services * service) const;
|
||||
};
|
||||
|
||||
class ArtifactID : public IdentifierWithEnum<ArtifactID, ArtifactIDBase>
|
||||
@ -693,7 +722,8 @@ public:
|
||||
};
|
||||
|
||||
DLL_LINKAGE const CCreature * toCreature() const;
|
||||
DLL_LINKAGE const Creature * toCreature(const CreatureService * creatures) const;
|
||||
DLL_LINKAGE const Creature * toEntity(const Services * services) const;
|
||||
DLL_LINKAGE const Creature * toEntity(const CreatureService * creatures) const;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CreatureID : public IdentifierWithEnum<CreatureID, CreatureIDBase>
|
||||
@ -811,7 +841,8 @@ public:
|
||||
};
|
||||
|
||||
const CSpell * toSpell() const; //deprecated
|
||||
const spells::Spell * toSpell(const spells::Service * service) const;
|
||||
const spells::Spell * toEntity(const Services * service) const;
|
||||
const spells::Spell * toEntity(const spells::Service * service) const;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE SpellID : public IdentifierWithEnum<SpellID, SpellIDBase>
|
||||
@ -934,9 +965,11 @@ public:
|
||||
static si32 decode(const std::string & identifier);
|
||||
static std::string encode(const si32 index);
|
||||
static std::string entityType();
|
||||
|
||||
static const std::array<GameResID, 7> & ALL_RESOURCES();
|
||||
};
|
||||
|
||||
class BuildingTypeUniqueID : public Identifier<BuildingTypeUniqueID>
|
||||
class DLL_LINKAGE BuildingTypeUniqueID : public Identifier<BuildingTypeUniqueID>
|
||||
{
|
||||
public:
|
||||
BuildingTypeUniqueID(FactionID faction, BuildingID building );
|
||||
@ -963,6 +996,7 @@ public:
|
||||
|
||||
// Deprecated
|
||||
// TODO: remove
|
||||
using Obj = MapObjectID;
|
||||
using ETownType = FactionID;
|
||||
using EGameResID = GameResID;
|
||||
using River = RiverId;
|
||||
|
@ -15,7 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
/// This class represents field that may contain value of multiple different identifer types
|
||||
template<typename... Types>
|
||||
class DLL_LINKAGE VariantIdentifier
|
||||
class VariantIdentifier
|
||||
{
|
||||
std::variant<Types...> value;
|
||||
public:
|
||||
|
@ -77,34 +77,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static CGObjectInstance * createObject(const Obj & id, int subid, const int3 & pos, const PlayerColor & owner)
|
||||
{
|
||||
CGObjectInstance * nobj;
|
||||
switch(id)
|
||||
{
|
||||
case Obj::HERO:
|
||||
{
|
||||
auto handler = VLC->objtypeh->getHandlerFor(id, VLC->heroh->objects[subid]->heroClass->getIndex());
|
||||
nobj = handler->create(handler->getTemplates().front());
|
||||
break;
|
||||
}
|
||||
case Obj::TOWN:
|
||||
nobj = new CGTownInstance();
|
||||
break;
|
||||
default: //rest of objects
|
||||
nobj = new CGObjectInstance();
|
||||
break;
|
||||
}
|
||||
nobj->ID = id;
|
||||
nobj->subID = subid;
|
||||
nobj->pos = pos;
|
||||
nobj->tempOwner = owner;
|
||||
if (id != Obj::HERO)
|
||||
nobj->appearance = VLC->objtypeh->getHandlerFor(id, subid)->getTemplates().front();
|
||||
|
||||
return nobj;
|
||||
}
|
||||
|
||||
HeroTypeID CGameState::pickNextHeroType(const PlayerColor & owner)
|
||||
{
|
||||
const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
|
||||
@ -153,223 +125,6 @@ HeroTypeID CGameState::pickUnusedHeroTypeRandomly(const PlayerColor & owner)
|
||||
return HeroTypeID::NONE; // no available heroes at all
|
||||
}
|
||||
|
||||
std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj)
|
||||
{
|
||||
switch(obj->ID)
|
||||
{
|
||||
case Obj::RANDOM_ART:
|
||||
return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE | CArtifact::ART_MINOR | CArtifact::ART_MAJOR | CArtifact::ART_RELIC));
|
||||
case Obj::RANDOM_TREASURE_ART:
|
||||
return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE));
|
||||
case Obj::RANDOM_MINOR_ART:
|
||||
return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_MINOR));
|
||||
case Obj::RANDOM_MAJOR_ART:
|
||||
return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_MAJOR));
|
||||
case Obj::RANDOM_RELIC_ART:
|
||||
return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_RELIC));
|
||||
case Obj::RANDOM_HERO:
|
||||
return std::make_pair(Obj::HERO, pickNextHeroType(obj->tempOwner));
|
||||
case Obj::RANDOM_MONSTER:
|
||||
return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator()));
|
||||
case Obj::RANDOM_MONSTER_L1:
|
||||
return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 1));
|
||||
case Obj::RANDOM_MONSTER_L2:
|
||||
return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 2));
|
||||
case Obj::RANDOM_MONSTER_L3:
|
||||
return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 3));
|
||||
case Obj::RANDOM_MONSTER_L4:
|
||||
return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 4));
|
||||
case Obj::RANDOM_RESOURCE:
|
||||
return std::make_pair(Obj::RESOURCE,getRandomGenerator().nextInt(6)); //now it's OH3 style, use %8 for mithril
|
||||
case Obj::RANDOM_TOWN:
|
||||
{
|
||||
PlayerColor align = (dynamic_cast<CGTownInstance *>(obj))->alignmentToPlayer;
|
||||
si32 f; // can be negative (for random)
|
||||
if(!align.isValidPlayer()) //same as owner / random
|
||||
{
|
||||
if(!obj->tempOwner.isValidPlayer())
|
||||
f = -1; //random
|
||||
else
|
||||
f = scenarioOps->getIthPlayersSettings(obj->tempOwner).castle;
|
||||
}
|
||||
else
|
||||
{
|
||||
f = scenarioOps->getIthPlayersSettings(align).castle;
|
||||
}
|
||||
if(f<0)
|
||||
{
|
||||
do
|
||||
{
|
||||
f = getRandomGenerator().nextInt((int)VLC->townh->size() - 1);
|
||||
}
|
||||
while ((*VLC->townh)[f]->town == nullptr); // find playable faction
|
||||
}
|
||||
return std::make_pair(Obj::TOWN,f);
|
||||
}
|
||||
case Obj::RANDOM_MONSTER_L5:
|
||||
return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 5));
|
||||
case Obj::RANDOM_MONSTER_L6:
|
||||
return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 6));
|
||||
case Obj::RANDOM_MONSTER_L7:
|
||||
return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 7));
|
||||
case Obj::RANDOM_DWELLING:
|
||||
case Obj::RANDOM_DWELLING_LVL:
|
||||
case Obj::RANDOM_DWELLING_FACTION:
|
||||
{
|
||||
auto * dwl = dynamic_cast<CGDwelling *>(obj);
|
||||
int faction;
|
||||
|
||||
//if castle alignment available
|
||||
if(auto * info = dynamic_cast<CCreGenAsCastleInfo *>(dwl->info))
|
||||
{
|
||||
faction = getRandomGenerator().nextInt((int)VLC->townh->size() - 1);
|
||||
if(info->asCastle && !info->instanceId.empty())
|
||||
{
|
||||
auto iter = map->instanceNames.find(info->instanceId);
|
||||
|
||||
if(iter == map->instanceNames.end())
|
||||
logGlobal->error("Map object not found: %s", info->instanceId);
|
||||
else
|
||||
{
|
||||
auto elem = iter->second;
|
||||
if(elem->ID==Obj::RANDOM_TOWN)
|
||||
{
|
||||
randomizeObject(elem.get()); //we have to randomize the castle first
|
||||
faction = elem->subID;
|
||||
}
|
||||
else if(elem->ID==Obj::TOWN)
|
||||
faction = elem->subID;
|
||||
else
|
||||
logGlobal->error("Map object must be town: %s", info->instanceId);
|
||||
}
|
||||
}
|
||||
else if(info->asCastle)
|
||||
{
|
||||
|
||||
for(auto & elem : map->objects)
|
||||
{
|
||||
if(!elem)
|
||||
continue;
|
||||
|
||||
if(elem->ID==Obj::RANDOM_TOWN
|
||||
&& dynamic_cast<CGTownInstance*>(elem.get())->identifier == info->identifier)
|
||||
{
|
||||
randomizeObject(elem); //we have to randomize the castle first
|
||||
faction = elem->subID;
|
||||
break;
|
||||
}
|
||||
else if(elem->ID==Obj::TOWN
|
||||
&& dynamic_cast<CGTownInstance*>(elem.get())->identifier == info->identifier)
|
||||
{
|
||||
faction = elem->subID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::set<int> temp;
|
||||
|
||||
for(int i = 0; i < info->allowedFactions.size(); i++)
|
||||
if(info->allowedFactions[i])
|
||||
temp.insert(i);
|
||||
|
||||
if(temp.empty())
|
||||
logGlobal->error("Random faction selection failed. Nothing is allowed. Fall back to random.");
|
||||
else
|
||||
faction = *RandomGeneratorUtil::nextItem(temp, getRandomGenerator());
|
||||
}
|
||||
}
|
||||
else // castle alignment fixed
|
||||
faction = obj->subID;
|
||||
|
||||
int level;
|
||||
|
||||
//if level set to range
|
||||
if(auto * info = dynamic_cast<CCreGenLeveledInfo *>(dwl->info))
|
||||
{
|
||||
level = getRandomGenerator().nextInt(info->minLevel, info->maxLevel) - 1;
|
||||
}
|
||||
else // fixed level
|
||||
{
|
||||
level = obj->subID;
|
||||
}
|
||||
|
||||
delete dwl->info;
|
||||
dwl->info = nullptr;
|
||||
|
||||
std::pair<Obj, int> result(Obj::NO_OBJ, -1);
|
||||
CreatureID cid;
|
||||
if((*VLC->townh)[faction]->town)
|
||||
cid = (*VLC->townh)[faction]->town->creatures[level][0];
|
||||
else
|
||||
{
|
||||
//neutral faction
|
||||
std::vector<CCreature*> possibleCreatures;
|
||||
std::copy_if(VLC->creh->objects.begin(), VLC->creh->objects.end(), std::back_inserter(possibleCreatures), [faction](const CCreature * c)
|
||||
{
|
||||
return c->getFaction().getNum() == faction;
|
||||
});
|
||||
assert(!possibleCreatures.empty());
|
||||
cid = (*RandomGeneratorUtil::nextItem(possibleCreatures, getRandomGenerator()))->getId();
|
||||
}
|
||||
|
||||
//NOTE: this will pick last dwelling with this creature (Mantis #900)
|
||||
//check for block map equality is better but more complex solution
|
||||
auto testID = [&](const Obj & primaryID) -> void
|
||||
{
|
||||
auto dwellingIDs = VLC->objtypeh->knownSubObjects(primaryID);
|
||||
for (si32 entry : dwellingIDs)
|
||||
{
|
||||
const auto * handler = dynamic_cast<const DwellingInstanceConstructor *>(VLC->objtypeh->getHandlerFor(primaryID, entry).get());
|
||||
|
||||
if (handler->producesCreature(VLC->creh->objects[cid]))
|
||||
result = std::make_pair(primaryID, entry);
|
||||
}
|
||||
};
|
||||
|
||||
testID(Obj::CREATURE_GENERATOR1);
|
||||
if (result.first == Obj::NO_OBJ)
|
||||
testID(Obj::CREATURE_GENERATOR4);
|
||||
|
||||
if (result.first == Obj::NO_OBJ)
|
||||
{
|
||||
logGlobal->error("Error: failed to find dwelling for %s of level %d", (*VLC->townh)[faction]->getNameTranslated(), int(level));
|
||||
result = std::make_pair(Obj::CREATURE_GENERATOR1, *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), getRandomGenerator()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return std::make_pair(Obj::NO_OBJ,-1);
|
||||
}
|
||||
|
||||
void CGameState::randomizeObject(CGObjectInstance *cur)
|
||||
{
|
||||
std::pair<Obj,int> ran = pickObject(cur);
|
||||
if(ran.first == Obj::NO_OBJ || ran.second<0) //this is not a random object, or we couldn't find anything
|
||||
{
|
||||
if(cur->ID==Obj::TOWN || cur->ID==Obj::MONSTER)
|
||||
cur->setType(cur->ID, cur->subID); // update def, if necessary
|
||||
}
|
||||
else if(ran.first==Obj::HERO)//special code for hero
|
||||
{
|
||||
auto * h = dynamic_cast<CGHeroInstance *>(cur);
|
||||
cur->setType(ran.first, ran.second);
|
||||
map->heroesOnMap.emplace_back(h);
|
||||
}
|
||||
else if(ran.first==Obj::TOWN)//special code for town
|
||||
{
|
||||
auto * t = dynamic_cast<CGTownInstance *>(cur);
|
||||
cur->setType(ran.first, ran.second);
|
||||
map->towns.emplace_back(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
cur->setType(ran.first, ran.second);
|
||||
}
|
||||
}
|
||||
|
||||
int CGameState::getDate(Date mode) const
|
||||
{
|
||||
int temp;
|
||||
@ -409,6 +164,8 @@ CGameState::CGameState()
|
||||
|
||||
CGameState::~CGameState()
|
||||
{
|
||||
// explicitly delete all ongoing battles first - BattleInfo destructor requires valid CGameState
|
||||
currentBattles.clear();
|
||||
map.dellNull();
|
||||
}
|
||||
|
||||
@ -469,7 +226,7 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Prog
|
||||
// Explicitly initialize static variables
|
||||
for(auto & elem : players)
|
||||
{
|
||||
CGKeys::playerKeyMap[elem.first] = std::set<ui8>();
|
||||
CGKeys::playerKeyMap[elem.first] = std::set<MapObjectSubID>();
|
||||
}
|
||||
for(auto & elem : teams)
|
||||
{
|
||||
@ -762,20 +519,21 @@ void CGameState::initRandomFactionsForPlayers()
|
||||
void CGameState::randomizeMapObjects()
|
||||
{
|
||||
logGlobal->debug("\tRandomizing objects");
|
||||
for(CGObjectInstance *obj : map->objects)
|
||||
for(CGObjectInstance *object : map->objects)
|
||||
{
|
||||
if(!obj) continue;
|
||||
if(!object)
|
||||
continue;
|
||||
|
||||
randomizeObject(obj);
|
||||
object->pickRandomObject(getRandomGenerator());
|
||||
|
||||
//handle Favouring Winds - mark tiles under it
|
||||
if(obj->ID == Obj::FAVORABLE_WINDS)
|
||||
if(object->ID == Obj::FAVORABLE_WINDS)
|
||||
{
|
||||
for (int i = 0; i < obj->getWidth() ; i++)
|
||||
for (int i = 0; i < object->getWidth() ; i++)
|
||||
{
|
||||
for (int j = 0; j < obj->getHeight() ; j++)
|
||||
for (int j = 0; j < object->getHeight() ; j++)
|
||||
{
|
||||
int3 pos = obj->pos - int3(i,j,0);
|
||||
int3 pos = object->pos - int3(i,j,0);
|
||||
if(map->isInTheMap(pos)) map->getTile(pos).extTileFlags |= 128;
|
||||
}
|
||||
}
|
||||
@ -808,7 +566,15 @@ void CGameState::placeStartingHero(const PlayerColor & playerColor, const HeroTy
|
||||
}
|
||||
}
|
||||
|
||||
CGObjectInstance * hero = createObject(Obj::HERO, heroTypeId.getNum(), townPos, playerColor);
|
||||
auto handler = VLC->objtypeh->getHandlerFor(Obj::HERO, heroTypeId.toHeroType()->heroClass->getIndex());
|
||||
CGObjectInstance * obj = handler->create(handler->getTemplates().front());
|
||||
CGHeroInstance * hero = dynamic_cast<CGHeroInstance *>(obj);
|
||||
|
||||
hero->ID = Obj::HERO;
|
||||
hero->setHeroType(heroTypeId);
|
||||
hero->tempOwner = playerColor;
|
||||
|
||||
hero->pos = townPos;
|
||||
hero->pos += hero->getVisitableOffset();
|
||||
map->getEditManager()->insertObject(hero);
|
||||
}
|
||||
@ -864,7 +630,7 @@ void CGameState::initHeroes()
|
||||
|
||||
hero->initHero(getRandomGenerator());
|
||||
getPlayerState(hero->getOwner())->heroes.push_back(hero);
|
||||
map->allHeroes[hero->type->getIndex()] = hero;
|
||||
map->allHeroes[hero->getHeroType().getNum()] = hero;
|
||||
}
|
||||
|
||||
// generate boats for all heroes on water
|
||||
@ -878,8 +644,6 @@ void CGameState::initHeroes()
|
||||
CGBoat * boat = dynamic_cast<CGBoat*>(handler->create());
|
||||
handler->configureObject(boat, gs->getRandomGenerator());
|
||||
|
||||
boat->ID = Obj::BOAT;
|
||||
boat->subID = hero->getBoatType().getNum();
|
||||
boat->pos = hero->pos;
|
||||
boat->appearance = handler->getTemplates().front();
|
||||
boat->id = ObjectInstanceID(static_cast<si32>(gs->map->objects.size()));
|
||||
@ -894,19 +658,23 @@ void CGameState::initHeroes()
|
||||
for(auto obj : map->objects) //prisons
|
||||
{
|
||||
if(obj && obj->ID == Obj::PRISON)
|
||||
map->allHeroes[obj->subID] = dynamic_cast<CGHeroInstance*>(obj.get());
|
||||
{
|
||||
auto * hero = dynamic_cast<CGHeroInstance*>(obj.get());
|
||||
hero->initHero(getRandomGenerator());
|
||||
map->allHeroes[hero->getHeroType().getNum()] = hero;
|
||||
}
|
||||
}
|
||||
|
||||
std::set<HeroTypeID> heroesToCreate = getUnusedAllowedHeroes(); //ids of heroes to be created and put into the pool
|
||||
for(auto ph : map->predefinedHeroes)
|
||||
{
|
||||
if(!vstd::contains(heroesToCreate, HeroTypeID(ph->subID)))
|
||||
if(!vstd::contains(heroesToCreate, ph->getHeroType()))
|
||||
continue;
|
||||
ph->initHero(getRandomGenerator());
|
||||
heroesPool->addHeroToPool(ph);
|
||||
heroesToCreate.erase(ph->type->getId());
|
||||
|
||||
map->allHeroes[ph->subID] = ph;
|
||||
map->allHeroes[ph->getHeroType().getNum()] = ph;
|
||||
}
|
||||
|
||||
for(const HeroTypeID & htype : heroesToCreate) //all not used allowed heroes go with default state into the pool
|
||||
@ -992,7 +760,7 @@ void CGameState::initStartingBonus()
|
||||
logGlobal->error("Cannot give starting artifact - no heroes!");
|
||||
break;
|
||||
}
|
||||
const Artifact * toGive = VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE).toArtifact(VLC->artifacts());
|
||||
const Artifact * toGive = VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE).toEntity(VLC);
|
||||
|
||||
CGHeroInstance *hero = elem.second.heroes[0];
|
||||
if(!giveHeroArtifact(hero, toGive->getId()))
|
||||
@ -1017,10 +785,8 @@ void CGameState::initTowns()
|
||||
for (auto & elem : map->towns)
|
||||
{
|
||||
CGTownInstance * vti =(elem);
|
||||
if(!vti->town)
|
||||
{
|
||||
vti->town = (*VLC->townh)[vti->subID]->town;
|
||||
}
|
||||
assert(vti->town);
|
||||
|
||||
if(vti->getNameTranslated().empty())
|
||||
{
|
||||
size_t nameID = getRandomGenerator().nextInt(vti->getTown()->getRandomNamesCount() - 1);
|
||||
@ -1165,7 +931,7 @@ void CGameState::initMapObjects()
|
||||
if(!obj)
|
||||
continue;
|
||||
|
||||
switch (obj->ID)
|
||||
switch(obj->ID.toEnum())
|
||||
{
|
||||
case Obj::QUEST_GUARD:
|
||||
case Obj::SEER_HUT:
|
||||
@ -2103,7 +1869,7 @@ bool CGameState::giveHeroArtifact(CGHeroInstance * h, const ArtifactID & aid)
|
||||
auto slot = ArtifactUtils::getArtAnyPosition(h, aid);
|
||||
if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot))
|
||||
{
|
||||
ai->putAt(ArtifactLocation(h, slot));
|
||||
ai->putAt(*h, slot);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -2130,12 +1896,15 @@ std::set<HeroTypeID> CGameState::getUnusedAllowedHeroes(bool alsoIncludeNotAllow
|
||||
if(hero->type)
|
||||
ret -= hero->type->getId();
|
||||
else
|
||||
ret -= HeroTypeID(hero->subID);
|
||||
ret -= hero->getHeroType();
|
||||
}
|
||||
|
||||
for(auto obj : map->objects) //prisons
|
||||
if(obj && obj->ID == Obj::PRISON)
|
||||
ret -= HeroTypeID(obj->subID);
|
||||
{
|
||||
auto * hero = dynamic_cast<const CGHeroInstance *>(obj.get());
|
||||
if(hero && hero->ID == Obj::PRISON)
|
||||
ret -= hero->getHeroType();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -2147,23 +1916,19 @@ bool CGameState::isUsedHero(const HeroTypeID & hid) const
|
||||
|
||||
CGHeroInstance * CGameState::getUsedHero(const HeroTypeID & hid) const
|
||||
{
|
||||
for(auto hero : map->heroesOnMap) //heroes instances initialization
|
||||
{
|
||||
if(hero->type && hero->type->getId() == hid)
|
||||
{
|
||||
return hero;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto obj : map->objects) //prisons
|
||||
{
|
||||
if(obj && obj->ID == Obj::PRISON )
|
||||
{
|
||||
auto * hero = dynamic_cast<CGHeroInstance *>(obj.get());
|
||||
assert(hero);
|
||||
if ( hero->type && hero->type->getId() == hid )
|
||||
return hero;
|
||||
}
|
||||
if (!obj)
|
||||
continue;
|
||||
|
||||
if ( obj->ID !=Obj::PRISON && obj->ID != Obj::HERO)
|
||||
continue;
|
||||
|
||||
auto * hero = dynamic_cast<CGHeroInstance *>(obj.get());
|
||||
assert(hero);
|
||||
|
||||
if (hero->getHeroType() == hid)
|
||||
return hero;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
@ -115,6 +115,8 @@ public:
|
||||
void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override;
|
||||
|
||||
bool giveHeroArtifact(CGHeroInstance * h, const ArtifactID & aid);
|
||||
/// picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly
|
||||
HeroTypeID pickNextHeroType(const PlayerColor & owner);
|
||||
|
||||
void apply(CPack *pack);
|
||||
BattleField battleGetBattlefieldType(int3 tile, CRandomGenerator & rand);
|
||||
@ -187,7 +189,6 @@ private:
|
||||
void initGrailPosition();
|
||||
void initRandomFactionsForPlayers();
|
||||
void randomizeMapObjects();
|
||||
void randomizeObject(CGObjectInstance *cur);
|
||||
void initPlayerStates();
|
||||
void placeStartingHeroes();
|
||||
void placeStartingHero(const PlayerColor & playerColor, const HeroTypeID & heroTypeId, int3 townPos);
|
||||
@ -214,9 +215,7 @@ private:
|
||||
CGHeroInstance * getUsedHero(const HeroTypeID & hid) const;
|
||||
bool isUsedHero(const HeroTypeID & hid) const; //looks in heroes and prisons
|
||||
std::set<HeroTypeID> getUnusedAllowedHeroes(bool alsoIncludeNotAllowed = false) const;
|
||||
std::pair<Obj,int> pickObject(CGObjectInstance *obj); //chooses type of object to be randomized, returns <type, subtype>
|
||||
HeroTypeID pickUnusedHeroTypeRandomly(const PlayerColor & owner); // picks a unused hero type randomly
|
||||
HeroTypeID pickNextHeroType(const PlayerColor & owner); // picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly
|
||||
UpgradeInfo fillUpgradeInfo(const CStackInstance &stack) const;
|
||||
|
||||
// ---- data -----
|
||||
|
@ -90,7 +90,7 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(std::vector<CampaignHeroR
|
||||
.And(Selector::subtype()(BonusSubtypeID(g)))
|
||||
.And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL));
|
||||
|
||||
cgh->getBonusLocalFirst(sel)->val = cgh->type->heroClass->primarySkillInitial[g];
|
||||
cgh->getBonusLocalFirst(sel)->val = cgh->type->heroClass->primarySkillInitial[g.getNum()];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,9 +134,9 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(std::vector<CampaignHeroR
|
||||
|
||||
bool takeable = travelOptions.artifactsKeptByHero.count(art->artType->getId());
|
||||
|
||||
ArtifactLocation al(hero, artifactPosition);
|
||||
if(!takeable && !al.getSlot()->locked) //don't try removing locked artifacts -> it crashes #1719
|
||||
al.removeArtifact();
|
||||
ArtifactLocation al(hero->id, artifactPosition);
|
||||
if(!takeable && !hero->getSlot(al.slot)->locked) //don't try removing locked artifacts -> it crashes #1719
|
||||
hero->getArt(al.slot)->removeFrom(*hero, al.slot);
|
||||
};
|
||||
|
||||
// process on copy - removal of artifact will invalidate container
|
||||
@ -213,7 +213,7 @@ void CGameStateCampaign::placeCampaignHeroes()
|
||||
std::set<HeroTypeID> heroesToRemove = campaignState->getReservedHeroes();
|
||||
|
||||
for(auto & campaignHeroReplacement : campaignHeroReplacements)
|
||||
heroesToRemove.insert(HeroTypeID(campaignHeroReplacement.hero->subID));
|
||||
heroesToRemove.insert(campaignHeroReplacement.hero->getHeroType());
|
||||
|
||||
for(auto & heroID : heroesToRemove)
|
||||
{
|
||||
@ -256,7 +256,7 @@ void CGameStateCampaign::placeCampaignHeroes()
|
||||
assert(0); // should not happen
|
||||
}
|
||||
|
||||
hero->subID = heroTypeId;
|
||||
hero->setHeroType(heroTypeId);
|
||||
gameState->map->getEditManager()->insertObject(hero);
|
||||
}
|
||||
}
|
||||
@ -300,7 +300,7 @@ void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero)
|
||||
CArtifactInstance * scroll = ArtifactUtils::createScroll(SpellID(curBonus->info2));
|
||||
const auto slot = ArtifactUtils::getArtAnyPosition(hero, scroll->getTypeId());
|
||||
if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot))
|
||||
scroll->putAt(ArtifactLocation(hero, slot));
|
||||
scroll->putAt(*hero, slot);
|
||||
else
|
||||
logGlobal->error("Cannot give starting scroll - no free slots!");
|
||||
break;
|
||||
@ -339,7 +339,7 @@ void CGameStateCampaign::replaceHeroesPlaceholders(const std::vector<CampaignHer
|
||||
if(heroPlaceholder->tempOwner.isValidPlayer())
|
||||
heroToPlace->tempOwner = heroPlaceholder->tempOwner;
|
||||
heroToPlace->pos = heroPlaceholder->pos;
|
||||
heroToPlace->type = VLC->heroh->objects[heroToPlace->subID];
|
||||
heroToPlace->type = VLC->heroh->objects[heroToPlace->getHeroType().getNum()];
|
||||
heroToPlace->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, heroToPlace->type->heroClass->getIndex())->getTemplates().front();
|
||||
|
||||
gameState->map->removeBlockVisTiles(heroPlaceholder, true);
|
||||
@ -375,7 +375,7 @@ std::vector<CampaignHeroReplacement> CGameStateCampaign::generateCampaignHeroesT
|
||||
auto * heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
|
||||
|
||||
// only 1 field must be set
|
||||
assert(heroPlaceholder->powerRank != heroPlaceholder->heroType);
|
||||
assert(heroPlaceholder->powerRank.has_value() != heroPlaceholder->heroType.has_value());
|
||||
|
||||
if(heroPlaceholder->powerRank)
|
||||
placeholdersByPower.push_back(heroPlaceholder);
|
||||
@ -396,7 +396,7 @@ std::vector<CampaignHeroReplacement> CGameStateCampaign::generateCampaignHeroesT
|
||||
|
||||
CGHeroInstance * hero = CampaignState::crossoverDeserialize(node, gameState->map);
|
||||
|
||||
logGlobal->info("Hero crossover: Loading placeholder for %d (%s)", hero->subID, hero->getNameTranslated());
|
||||
logGlobal->info("Hero crossover: Loading placeholder for %d (%s)", hero->getHeroType(), hero->getNameTranslated());
|
||||
|
||||
campaignHeroReplacements.emplace_back(hero, placeholder->id);
|
||||
}
|
||||
@ -422,7 +422,7 @@ std::vector<CampaignHeroReplacement> CGameStateCampaign::generateCampaignHeroesT
|
||||
CGHeroInstance * hero = CampaignState::crossoverDeserialize(*nodeListIter, gameState->map);
|
||||
nodeListIter++;
|
||||
|
||||
logGlobal->info("Hero crossover: Loading placeholder as %d (%s)", hero->subID, hero->getNameTranslated());
|
||||
logGlobal->info("Hero crossover: Loading placeholder as %d (%s)", hero->getHeroType(), hero->getNameTranslated());
|
||||
|
||||
campaignHeroReplacements.emplace_back(hero, placeholder->id);
|
||||
}
|
||||
@ -468,7 +468,7 @@ void CGameStateCampaign::initHeroes()
|
||||
{
|
||||
for (auto & heroe : heroes)
|
||||
{
|
||||
if (heroe->subID == chosenBonus->info1)
|
||||
if (heroe->getHeroType().getNum() == chosenBonus->info1)
|
||||
{
|
||||
giveCampaignBonusToHero(heroe);
|
||||
break;
|
||||
@ -498,7 +498,7 @@ void CGameStateCampaign::initStartingResources()
|
||||
std::vector<const PlayerSettings *> people = getHumanPlayerInfo(); //players we will give resource bonus
|
||||
for(const PlayerSettings *ps : people)
|
||||
{
|
||||
std::vector<int> res; //resources we will give
|
||||
std::vector<GameResID> res; //resources we will give
|
||||
switch (chosenBonus->info1)
|
||||
{
|
||||
case 0: case 1: case 2: case 3: case 4: case 5: case 6:
|
||||
@ -557,7 +557,7 @@ void CGameStateCampaign::initTowns()
|
||||
if(gameState->scenarioOps->campState->formatVCMI())
|
||||
newBuilding = BuildingID(chosenBonus->info1);
|
||||
else
|
||||
newBuilding = CBuildingHandler::campToERMU(chosenBonus->info1, town->subID, town->builtBuildings);
|
||||
newBuilding = CBuildingHandler::campToERMU(chosenBonus->info1, town->getFaction(), town->builtBuildings);
|
||||
|
||||
// Build granted building & all prerequisites - e.g. Mages Guild Lvl 3 should also give Mages Guild Lvl 1 & 2
|
||||
while(true)
|
||||
|
@ -25,7 +25,7 @@ std::map<HeroTypeID, CGHeroInstance*> TavernHeroesPool::unusedHeroesFromPool() c
|
||||
{
|
||||
std::map<HeroTypeID, CGHeroInstance*> pool = heroesPool;
|
||||
for(const auto & slot : currentTavern)
|
||||
pool.erase(HeroTypeID(slot.hero->subID));
|
||||
pool.erase(slot.hero->getHeroType());
|
||||
|
||||
return pool;
|
||||
}
|
||||
@ -34,7 +34,7 @@ TavernSlotRole TavernHeroesPool::getSlotRole(HeroTypeID hero) const
|
||||
{
|
||||
for (auto const & slot : currentTavern)
|
||||
{
|
||||
if (HeroTypeID(slot.hero->subID) == hero)
|
||||
if (slot.hero->getHeroType() == hero)
|
||||
return slot.role;
|
||||
}
|
||||
return TavernSlotRole::NONE;
|
||||
@ -132,7 +132,7 @@ void TavernHeroesPool::onNewDay()
|
||||
|
||||
void TavernHeroesPool::addHeroToPool(CGHeroInstance * hero)
|
||||
{
|
||||
heroesPool[HeroTypeID(hero->subID)] = hero;
|
||||
heroesPool[hero->getHeroType()] = hero;
|
||||
}
|
||||
|
||||
void TavernHeroesPool::setAvailability(HeroTypeID hero, std::set<PlayerColor> mask)
|
||||
|
@ -285,10 +285,9 @@ void CObjectClassesHandler::loadObject(std::string scope, std::string name, cons
|
||||
VLC->identifiersHandler->registerObject(scope, "object", name, object->id);
|
||||
}
|
||||
|
||||
void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, si32 ID, si32 subID)
|
||||
void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, MapObjectID ID, MapObjectSubID subID)
|
||||
{
|
||||
config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not NULL
|
||||
assert(ID < objects.size());
|
||||
assert(objects[ID]);
|
||||
|
||||
if ( subID >= objects[ID]->objects.size())
|
||||
@ -298,9 +297,8 @@ void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNo
|
||||
loadSubObject(config.meta, identifier, config, objects[ID], subID);
|
||||
}
|
||||
|
||||
void CObjectClassesHandler::removeSubObject(si32 ID, si32 subID)
|
||||
void CObjectClassesHandler::removeSubObject(MapObjectID ID, MapObjectSubID subID)
|
||||
{
|
||||
assert(ID < objects.size());
|
||||
assert(objects[ID]);
|
||||
assert(subID < objects[ID]->objects.size());
|
||||
objects[ID]->objects[subID] = nullptr;
|
||||
@ -311,7 +309,7 @@ std::vector<bool> CObjectClassesHandler::getDefaultAllowed() const
|
||||
return std::vector<bool>(); //TODO?
|
||||
}
|
||||
|
||||
TObjectTypeHandler CObjectClassesHandler::getHandlerFor(si32 type, si32 subtype) const
|
||||
TObjectTypeHandler CObjectClassesHandler::getHandlerFor(MapObjectID type, MapObjectSubID subtype) const
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -352,9 +350,9 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(CompoundMapObjectID comp
|
||||
return getHandlerFor(compoundIdentifier.primaryID, compoundIdentifier.secondaryID);
|
||||
}
|
||||
|
||||
std::set<si32> CObjectClassesHandler::knownObjects() const
|
||||
std::set<MapObjectID> CObjectClassesHandler::knownObjects() const
|
||||
{
|
||||
std::set<si32> ret;
|
||||
std::set<MapObjectID> ret;
|
||||
|
||||
for(auto * entry : objects)
|
||||
if (entry)
|
||||
@ -363,9 +361,9 @@ std::set<si32> CObjectClassesHandler::knownObjects() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::set<si32> CObjectClassesHandler::knownSubObjects(si32 primaryID) const
|
||||
std::set<MapObjectSubID> CObjectClassesHandler::knownSubObjects(MapObjectID primaryID) const
|
||||
{
|
||||
std::set<si32> ret;
|
||||
std::set<MapObjectSubID> ret;
|
||||
|
||||
if (!objects.at(primaryID))
|
||||
{
|
||||
@ -461,7 +459,7 @@ void CObjectClassesHandler::generateExtraMonolithsForRMG()
|
||||
}
|
||||
}
|
||||
|
||||
std::string CObjectClassesHandler::getObjectName(si32 type, si32 subtype) const
|
||||
std::string CObjectClassesHandler::getObjectName(MapObjectID type, MapObjectSubID subtype) const
|
||||
{
|
||||
const auto handler = getHandlerFor(type, subtype);
|
||||
if (handler && handler->hasNameTextID())
|
||||
@ -470,7 +468,7 @@ std::string CObjectClassesHandler::getObjectName(si32 type, si32 subtype) const
|
||||
return objects[type]->getNameTranslated();
|
||||
}
|
||||
|
||||
SObjectSounds CObjectClassesHandler::getObjectSounds(si32 type, si32 subtype) const
|
||||
SObjectSounds CObjectClassesHandler::getObjectSounds(MapObjectID type, MapObjectSubID subtype) const
|
||||
{
|
||||
// TODO: these objects may have subID's that does not have associated handler:
|
||||
// Prison: uses hero type as subID
|
||||
@ -479,19 +477,18 @@ SObjectSounds CObjectClassesHandler::getObjectSounds(si32 type, si32 subtype) co
|
||||
if(type == Obj::PRISON || type == Obj::HERO || type == Obj::SPELL_SCROLL)
|
||||
subtype = 0;
|
||||
|
||||
assert(type < objects.size());
|
||||
assert(objects[type]);
|
||||
assert(subtype < objects[type]->objects.size());
|
||||
|
||||
return getHandlerFor(type, subtype)->getSounds();
|
||||
}
|
||||
|
||||
std::string CObjectClassesHandler::getObjectHandlerName(si32 type) const
|
||||
std::string CObjectClassesHandler::getObjectHandlerName(MapObjectID type) const
|
||||
{
|
||||
return objects.at(type)->handlerName;
|
||||
}
|
||||
|
||||
std::string CObjectClassesHandler::getJsonKey(si32 type) const
|
||||
std::string CObjectClassesHandler::getJsonKey(MapObjectID type) const
|
||||
{
|
||||
return objects.at(type)->getJsonKey();
|
||||
}
|
||||
|
@ -105,8 +105,8 @@ public:
|
||||
void loadObject(std::string scope, std::string name, const JsonNode & data) override;
|
||||
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
|
||||
|
||||
void loadSubObject(const std::string & identifier, JsonNode config, si32 ID, si32 subID);
|
||||
void removeSubObject(si32 ID, si32 subID);
|
||||
void loadSubObject(const std::string & identifier, JsonNode config, MapObjectID ID, MapObjectSubID subID);
|
||||
void removeSubObject(MapObjectID ID, MapObjectSubID subID);
|
||||
|
||||
void beforeValidate(JsonNode & object) override;
|
||||
void afterLoadFinalization() override;
|
||||
@ -114,22 +114,22 @@ public:
|
||||
std::vector<bool> getDefaultAllowed() const override;
|
||||
|
||||
/// Queries to detect loaded objects
|
||||
std::set<si32> knownObjects() const;
|
||||
std::set<si32> knownSubObjects(si32 primaryID) const;
|
||||
std::set<MapObjectID> knownObjects() const;
|
||||
std::set<MapObjectSubID> knownSubObjects(MapObjectID primaryID) const;
|
||||
|
||||
/// returns handler for specified object (ID-based). ObjectHandler keeps ownership
|
||||
TObjectTypeHandler getHandlerFor(si32 type, si32 subtype) const;
|
||||
TObjectTypeHandler getHandlerFor(MapObjectID type, MapObjectSubID subtype) const;
|
||||
TObjectTypeHandler getHandlerFor(const std::string & scope, const std::string & type, const std::string & subtype) const;
|
||||
TObjectTypeHandler getHandlerFor(CompoundMapObjectID compoundIdentifier) const;
|
||||
|
||||
std::string getObjectName(si32 type, si32 subtype) const;
|
||||
std::string getObjectName(MapObjectID type, MapObjectSubID subtype) const;
|
||||
|
||||
SObjectSounds getObjectSounds(si32 type, si32 subtype) const;
|
||||
SObjectSounds getObjectSounds(MapObjectID type, MapObjectSubID subtype) const;
|
||||
|
||||
/// Returns handler string describing the handler (for use in client)
|
||||
std::string getObjectHandlerName(si32 type) const;
|
||||
std::string getObjectHandlerName(MapObjectID type) const;
|
||||
|
||||
std::string getJsonKey(si32 type) const;
|
||||
std::string getJsonKey(MapObjectID type) const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
|
@ -73,7 +73,7 @@ void CArmedInstance::updateMoraleBonusFromArmy()
|
||||
for(const auto & slot : Slots())
|
||||
{
|
||||
const CStackInstance * inst = slot.second;
|
||||
const CCreature * creature = VLC->creh->objects[inst->getCreatureID()];
|
||||
const auto * creature = inst->getCreatureID().toEntity(VLC);
|
||||
|
||||
factions.insert(creature->getFaction());
|
||||
// Check for undead flag instead of faction (undead mummies are neutral)
|
||||
@ -92,7 +92,7 @@ void CArmedInstance::updateMoraleBonusFromArmy()
|
||||
|
||||
for(auto f : factions)
|
||||
{
|
||||
if (VLC->factions()->getByIndex(f)->getAlignment() != EAlignment::EVIL)
|
||||
if (VLC->factions()->getById(f)->getAlignment() != EAlignment::EVIL)
|
||||
mixableFactions++;
|
||||
}
|
||||
if (mixableFactions > 0)
|
||||
|
@ -46,7 +46,7 @@ void CBank::initObj(CRandomGenerator & rand)
|
||||
{
|
||||
daycounter = 0;
|
||||
resetDuration = 0;
|
||||
VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand);
|
||||
getObjectHandler()->configureObject(this, rand);
|
||||
}
|
||||
|
||||
bool CBank::isCoastVisitable() const
|
||||
@ -79,11 +79,7 @@ std::vector<Component> CBank::getPopupComponents(PlayerColor player) const
|
||||
|
||||
for (auto const & guard : guardsAmounts)
|
||||
{
|
||||
Component comp;
|
||||
comp.id = Component::EComponentType::CREATURE;
|
||||
comp.subtype = guard.first.getNum();
|
||||
comp.val = guard.second;
|
||||
|
||||
Component comp(ComponentType::CREATURE, guard.first, guard.second);
|
||||
result.push_back(comp);
|
||||
}
|
||||
return result;
|
||||
@ -141,7 +137,7 @@ void CBank::onHeroVisit(const CGHeroInstance * h) const
|
||||
cb->sendAndApply(&cov);
|
||||
|
||||
int banktext = 0;
|
||||
switch (ID)
|
||||
switch (ID.toEnum())
|
||||
{
|
||||
case Obj::DERELICT_SHIP:
|
||||
banktext = 41;
|
||||
@ -184,7 +180,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
|
||||
if (bc)
|
||||
{
|
||||
switch (ID)
|
||||
switch (ID.toEnum())
|
||||
{
|
||||
case Obj::DERELICT_SHIP:
|
||||
textID = 43;
|
||||
@ -207,7 +203,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (ID)
|
||||
switch (ID.toEnum())
|
||||
{
|
||||
case Obj::SHIPWRECK:
|
||||
case Obj::DERELICT_SHIP:
|
||||
@ -220,7 +216,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
gbonus.bonus.sid = BonusSourceID(ID);
|
||||
gbonus.bonus.type = BonusType::MORALE;
|
||||
gbonus.bonus.val = -1;
|
||||
switch (ID)
|
||||
switch (ID.toEnum())
|
||||
{
|
||||
case Obj::SHIPWRECK:
|
||||
textID = 123;
|
||||
@ -236,7 +232,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
break;
|
||||
}
|
||||
cb->giveHeroBonus(&gbonus);
|
||||
iw.components.emplace_back(Component::EComponentType::MORALE, 0, -1, 0);
|
||||
iw.components.emplace_back(ComponentType::MORALE, -1);
|
||||
iw.soundID = soundBase::GRAVEYARD;
|
||||
break;
|
||||
}
|
||||
@ -247,7 +243,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
gb.id = hero->id.getNum();
|
||||
cb->giveHeroBonus(&gb);
|
||||
textID = 107;
|
||||
iw.components.emplace_back(Component::EComponentType::LUCK, 0, -2, 0);
|
||||
iw.components.emplace_back(ComponentType::LUCK, -2);
|
||||
break;
|
||||
}
|
||||
case Obj::CREATURE_BANK:
|
||||
@ -267,24 +263,24 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
//grant resources
|
||||
if (bc)
|
||||
{
|
||||
for (int it = 0; it < bc->resources.size(); it++)
|
||||
for (GameResID it : GameResID::ALL_RESOURCES())
|
||||
{
|
||||
if (bc->resources[it] != 0)
|
||||
{
|
||||
iw.components.emplace_back(Component::EComponentType::RESOURCE, it, bc->resources[it], 0);
|
||||
iw.components.emplace_back(ComponentType::RESOURCE, it, bc->resources[it]);
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(iw.components.back().val);
|
||||
loot.replaceLocalString(EMetaText::RES_NAMES, iw.components.back().subtype);
|
||||
cb->giveResource(hero->getOwner(), static_cast<EGameResID>(it), bc->resources[it]);
|
||||
loot.replaceNumber(bc->resources[it]);
|
||||
loot.replaceLocalString(EMetaText::RES_NAMES, it);
|
||||
cb->giveResource(hero->getOwner(), it, bc->resources[it]);
|
||||
}
|
||||
}
|
||||
//grant artifacts
|
||||
for (auto & elem : bc->artifacts)
|
||||
{
|
||||
iw.components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0);
|
||||
iw.components.emplace_back(ComponentType::ARTIFACT, elem);
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceLocalString(EMetaText::ART_NAMES, elem);
|
||||
cb->giveHeroNewArtifact(hero, VLC->arth->objects[elem], ArtifactPosition::FIRST_AVAILABLE);
|
||||
cb->giveHeroNewArtifact(hero, elem.toArtifact(), ArtifactPosition::FIRST_AVAILABLE);
|
||||
}
|
||||
//display loot
|
||||
if (!iw.components.empty())
|
||||
@ -318,14 +314,14 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
}
|
||||
for(const SpellID & spellId : bc->spells)
|
||||
{
|
||||
const auto * spell = spellId.toSpell(VLC->spells());
|
||||
const auto * spell = spellId.toEntity(VLC);
|
||||
iw.text.appendLocalString(EMetaText::SPELL_NAME, spellId);
|
||||
if(spell->getLevel() <= hero->maxSpellLevel())
|
||||
{
|
||||
if(hero->canLearnSpell(spell))
|
||||
{
|
||||
spells.insert(spellId);
|
||||
iw.components.emplace_back(Component::EComponentType::SPELL, spellId, 0, 0);
|
||||
iw.components.emplace_back(ComponentType::SPELL, spellId);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -356,7 +352,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
|
||||
for(const auto & elem : ourArmy.Slots())
|
||||
{
|
||||
iw.components.emplace_back(*elem.second);
|
||||
iw.components.emplace_back(ComponentType::CREATURE, elem.second->getId(), elem.second->getCount());
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceCreatureName(*elem.second);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ std::string CGCreature::getHoverText(PlayerColor player) const
|
||||
if(stacks.empty())
|
||||
{
|
||||
//should not happen...
|
||||
logGlobal->error("Invalid stack at tile %s: subID=%d; id=%d", pos.toString(), subID, id.getNum());
|
||||
logGlobal->error("Invalid stack at tile %s: subID=%d; id=%d", pos.toString(), getCreature(), id.getNum());
|
||||
return "INVALID_STACK";
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ std::string CGCreature::getHoverText(PlayerColor player) const
|
||||
else
|
||||
ms.appendLocalString(EMetaText::ARRAY_TXT, quantityTextIndex);
|
||||
ms.appendRawString(" ");
|
||||
ms.appendLocalString(EMetaText::CRE_PL_NAMES,subID);
|
||||
ms.appendLocalString(EMetaText::CRE_PL_NAMES, getCreature());
|
||||
hoverName = ms.toString();
|
||||
return hoverName;
|
||||
}
|
||||
@ -54,7 +54,7 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
|
||||
MetaString ms;
|
||||
ms.appendNumber(stacks.begin()->second->count);
|
||||
ms.appendRawString(" ");
|
||||
ms.appendLocalString(EMetaText::CRE_PL_NAMES,subID);
|
||||
ms.appendLocalString(EMetaText::CRE_PL_NAMES, getCreature());
|
||||
|
||||
ms.appendRawString("\n\n");
|
||||
|
||||
@ -132,7 +132,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
|
||||
BlockingDialog ynd(true,false);
|
||||
ynd.player = h->tempOwner;
|
||||
ynd.text.appendLocalString(EMetaText::ADVOB_TXT, 86);
|
||||
ynd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, subID);
|
||||
ynd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, getCreature());
|
||||
cb->showBlockingDialog(&ynd);
|
||||
break;
|
||||
}
|
||||
@ -146,13 +146,50 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
|
||||
std::string tmp = VLC->generaltexth->advobtxt[90];
|
||||
boost::algorithm::replace_first(tmp, "%d", std::to_string(getStackCount(SlotID(0))));
|
||||
boost::algorithm::replace_first(tmp, "%d", std::to_string(action));
|
||||
boost::algorithm::replace_first(tmp,"%s",VLC->creh->objects[subID]->getNamePluralTranslated());
|
||||
boost::algorithm::replace_first(tmp,"%s",VLC->creatures()->getById(getCreature())->getNamePluralTranslated());
|
||||
ynd.text.appendRawString(tmp);
|
||||
cb->showBlockingDialog(&ynd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CreatureID CGCreature::getCreature() const
|
||||
{
|
||||
return CreatureID(getObjTypeIndex().getNum());
|
||||
}
|
||||
|
||||
void CGCreature::pickRandomObject(CRandomGenerator & rand)
|
||||
{
|
||||
switch(ID.toEnum())
|
||||
{
|
||||
case MapObjectID::RANDOM_MONSTER:
|
||||
subID = VLC->creh->pickRandomMonster(rand);
|
||||
break;
|
||||
case MapObjectID::RANDOM_MONSTER_L1:
|
||||
subID = VLC->creh->pickRandomMonster(rand, 1);
|
||||
break;
|
||||
case MapObjectID::RANDOM_MONSTER_L2:
|
||||
subID = VLC->creh->pickRandomMonster(rand, 2);
|
||||
break;
|
||||
case MapObjectID::RANDOM_MONSTER_L3:
|
||||
subID = VLC->creh->pickRandomMonster(rand, 3);
|
||||
break;
|
||||
case MapObjectID::RANDOM_MONSTER_L4:
|
||||
subID = VLC->creh->pickRandomMonster(rand, 4);
|
||||
break;
|
||||
case MapObjectID::RANDOM_MONSTER_L5:
|
||||
subID = VLC->creh->pickRandomMonster(rand, 5);
|
||||
break;
|
||||
case MapObjectID::RANDOM_MONSTER_L6:
|
||||
subID = VLC->creh->pickRandomMonster(rand, 6);
|
||||
break;
|
||||
case MapObjectID::RANDOM_MONSTER_L7:
|
||||
subID = VLC->creh->pickRandomMonster(rand, 7);
|
||||
break;
|
||||
}
|
||||
ID = MapObjectID::MONSTER;
|
||||
setType(ID, subID);
|
||||
}
|
||||
|
||||
void CGCreature::initObj(CRandomGenerator & rand)
|
||||
@ -177,16 +214,16 @@ void CGCreature::initObj(CRandomGenerator & rand)
|
||||
break;
|
||||
}
|
||||
|
||||
stacks[SlotID(0)]->setType(CreatureID(subID));
|
||||
stacks[SlotID(0)]->setType(getCreature());
|
||||
TQuantity &amount = stacks[SlotID(0)]->count;
|
||||
CCreature &c = *VLC->creh->objects[subID];
|
||||
const Creature * c = VLC->creatures()->getById(getCreature());
|
||||
if(amount == 0)
|
||||
{
|
||||
amount = rand.nextInt(c.ammMin, c.ammMax);
|
||||
amount = rand.nextInt(c->getAdvMapAmountMin(), c->getAdvMapAmountMax());
|
||||
|
||||
if(amount == 0) //armies with 0 creatures are illegal
|
||||
{
|
||||
logGlobal->warn("Stack %s cannot have 0 creatures. Check properties of %s", nodeName(), c.nodeName());
|
||||
logGlobal->warn("Stack cannot have 0 creatures. Check properties of %s", c->getJsonKey());
|
||||
amount = 1;
|
||||
}
|
||||
}
|
||||
@ -252,7 +289,7 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
|
||||
powerFactor = -3;
|
||||
|
||||
std::set<CreatureID> myKindCres; //what creatures are the same kind as we
|
||||
const CCreature * myCreature = VLC->creh->objects[subID];
|
||||
const CCreature * myCreature = VLC->creh->objects[getCreature().getNum()];
|
||||
myKindCres.insert(myCreature->getId()); //we
|
||||
myKindCres.insert(myCreature->upgrades.begin(), myCreature->upgrades.end()); //our upgrades
|
||||
|
||||
@ -290,7 +327,7 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
|
||||
return JOIN_FOR_FREE;
|
||||
|
||||
else if(diplomacy * 2 + sympathy + 1 >= character)
|
||||
return VLC->creatures()->getByIndex(subID)->getRecruitCost(EGameResID::GOLD) * getStackCount(SlotID(0)); //join for gold
|
||||
return VLC->creatures()->getById(getCreature())->getRecruitCost(EGameResID::GOLD) * getStackCount(SlotID(0)); //join for gold
|
||||
}
|
||||
|
||||
//we are still here - creatures have not joined hero, flee or fight
|
||||
@ -390,7 +427,7 @@ void CGCreature::fight( const CGHeroInstance *h ) const
|
||||
if(!upgrades.empty())
|
||||
{
|
||||
auto it = RandomGeneratorUtil::nextItem(upgrades, CRandomGenerator::getDefault());
|
||||
cb->changeStackType(StackLocation(this, slotID), VLC->creh->objects[*it]);
|
||||
cb->changeStackType(StackLocation(this, slotID), it->toCreature());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -404,7 +441,7 @@ void CGCreature::flee( const CGHeroInstance * h ) const
|
||||
BlockingDialog ynd(true,false);
|
||||
ynd.player = h->tempOwner;
|
||||
ynd.text.appendLocalString(EMetaText::ADVOB_TXT,91);
|
||||
ynd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, subID);
|
||||
ynd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, getCreature());
|
||||
cb->showBlockingDialog(&ynd);
|
||||
}
|
||||
|
||||
@ -526,17 +563,17 @@ void CGCreature::giveReward(const CGHeroInstance * h) const
|
||||
if(!resources.empty())
|
||||
{
|
||||
cb->giveResources(h->tempOwner, resources);
|
||||
for(int i = 0; i < resources.size(); i++)
|
||||
for(auto const & res : GameResID::ALL_RESOURCES())
|
||||
{
|
||||
if(resources[i] > 0)
|
||||
iw.components.emplace_back(Component::EComponentType::RESOURCE, i, resources[i], 0);
|
||||
if(resources[res] > 0)
|
||||
iw.components.emplace_back(ComponentType::RESOURCE, res, resources[res]);
|
||||
}
|
||||
}
|
||||
|
||||
if(gainedArtifact != ArtifactID::NONE)
|
||||
{
|
||||
cb->giveHeroNewArtifact(h, VLC->arth->objects[gainedArtifact], ArtifactPosition::FIRST_AVAILABLE);
|
||||
iw.components.emplace_back(Component::EComponentType::ARTIFACT, gainedArtifact, 0, 0);
|
||||
cb->giveHeroNewArtifact(h, gainedArtifact.toArtifact(), ArtifactPosition::FIRST_AVAILABLE);
|
||||
iw.components.emplace_back(ComponentType::ARTIFACT, gainedArtifact);
|
||||
}
|
||||
|
||||
if(!iw.components.empty())
|
||||
|
@ -41,9 +41,11 @@ public:
|
||||
std::string getHoverText(PlayerColor player) const override;
|
||||
std::string getHoverText(const CGHeroInstance * hero) const override;
|
||||
void initObj(CRandomGenerator & rand) override;
|
||||
void pickRandomObject(CRandomGenerator & rand) override;
|
||||
void newTurn(CRandomGenerator & rand) const override;
|
||||
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
|
||||
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
|
||||
CreatureID getCreature() const;
|
||||
|
||||
//stack formation depends on position,
|
||||
bool containsUpgradedStack() const;
|
||||
|
@ -11,8 +11,10 @@
|
||||
#include "StdInc.h"
|
||||
#include "CGDwelling.h"
|
||||
#include "../serializer/JsonSerializeFormat.h"
|
||||
#include "../mapping/CMap.h"
|
||||
#include "../mapObjectConstructors/AObjectTypeHandler.h"
|
||||
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
||||
#include "../mapObjectConstructors/DwellingInstanceConstructor.h"
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
#include "../networkPacks/StackLocation.h"
|
||||
#include "../networkPacks/PacksForClient.h"
|
||||
@ -26,41 +28,10 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
CSpecObjInfo::CSpecObjInfo():
|
||||
owner(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CCreGenAsCastleInfo::serializeJson(JsonSerializeFormat & handler)
|
||||
void CGDwellingRandomizationInfo::serializeJson(JsonSerializeFormat & handler)
|
||||
{
|
||||
handler.serializeString("sameAsTown", instanceId);
|
||||
|
||||
if(!handler.saving)
|
||||
{
|
||||
asCastle = !instanceId.empty();
|
||||
allowedFactions.clear();
|
||||
}
|
||||
|
||||
if(!asCastle)
|
||||
{
|
||||
std::vector<bool> standard;
|
||||
standard.resize(VLC->townh->size(), true);
|
||||
|
||||
JsonSerializeFormat::LIC allowedLIC(standard, &FactionID::decode, &FactionID::encode);
|
||||
allowedLIC.any = allowedFactions;
|
||||
|
||||
handler.serializeLIC("allowedFactions", allowedLIC);
|
||||
|
||||
if(!handler.saving)
|
||||
{
|
||||
allowedFactions = allowedLIC.any;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCreGenLeveledInfo::serializeJson(JsonSerializeFormat & handler)
|
||||
{
|
||||
handler.serializeIdArray("allowedFactions", allowedFactions);
|
||||
handler.serializeInt("minLevel", minLevel, static_cast<uint8_t>(1));
|
||||
handler.serializeInt("maxLevel", maxLevel, static_cast<uint8_t>(7));
|
||||
|
||||
@ -72,30 +43,139 @@ void CCreGenLeveledInfo::serializeJson(JsonSerializeFormat & handler)
|
||||
}
|
||||
}
|
||||
|
||||
void CCreGenLeveledCastleInfo::serializeJson(JsonSerializeFormat & handler)
|
||||
CGDwelling::CGDwelling() = default;
|
||||
CGDwelling::~CGDwelling() = default;
|
||||
|
||||
FactionID CGDwelling::randomizeFaction(CRandomGenerator & rand)
|
||||
{
|
||||
CCreGenAsCastleInfo::serializeJson(handler);
|
||||
CCreGenLeveledInfo::serializeJson(handler);
|
||||
if (ID == Obj::RANDOM_DWELLING_FACTION)
|
||||
return FactionID(subID);
|
||||
|
||||
assert(ID == Obj::RANDOM_DWELLING || ID == Obj::RANDOM_DWELLING_LVL);
|
||||
assert(randomizationInfo.has_value());
|
||||
if (!randomizationInfo)
|
||||
return FactionID::CASTLE;
|
||||
|
||||
CGTownInstance * linkedTown = nullptr;
|
||||
|
||||
if (!randomizationInfo->instanceId.empty())
|
||||
{
|
||||
auto iter = cb->gameState()->map->instanceNames.find(randomizationInfo->instanceId);
|
||||
|
||||
if(iter == cb->gameState()->map->instanceNames.end())
|
||||
logGlobal->error("Map object not found: %s", randomizationInfo->instanceId);
|
||||
linkedTown = dynamic_cast<CGTownInstance *>(iter->second.get());
|
||||
}
|
||||
|
||||
if (randomizationInfo->identifier != 0)
|
||||
{
|
||||
for(auto & elem : cb->gameState()->map->objects)
|
||||
{
|
||||
auto town = dynamic_cast<CGTownInstance*>(elem.get());
|
||||
if(town && town->identifier == randomizationInfo->identifier)
|
||||
{
|
||||
linkedTown = town;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (linkedTown)
|
||||
{
|
||||
if(linkedTown->ID==Obj::RANDOM_TOWN)
|
||||
linkedTown->pickRandomObject(rand); //we have to randomize the castle first
|
||||
|
||||
assert(linkedTown->ID == Obj::TOWN);
|
||||
if(linkedTown->ID==Obj::TOWN)
|
||||
return linkedTown->getFaction();
|
||||
}
|
||||
|
||||
if(!randomizationInfo->allowedFactions.empty())
|
||||
return *RandomGeneratorUtil::nextItem(randomizationInfo->allowedFactions, rand);
|
||||
|
||||
|
||||
std::vector<FactionID> potentialPicks;
|
||||
|
||||
for (FactionID faction(0); faction < FactionID(VLC->townh->size()); ++faction)
|
||||
if (VLC->factions()->getById(faction)->hasTown())
|
||||
potentialPicks.push_back(faction);
|
||||
|
||||
assert(!potentialPicks.empty());
|
||||
return *RandomGeneratorUtil::nextItem(potentialPicks, rand);
|
||||
}
|
||||
|
||||
CGDwelling::CGDwelling()
|
||||
: info(nullptr)
|
||||
int CGDwelling::randomizeLevel(CRandomGenerator & rand)
|
||||
{
|
||||
if (ID == Obj::RANDOM_DWELLING_LVL)
|
||||
return subID.getNum();
|
||||
|
||||
assert(ID == Obj::RANDOM_DWELLING || ID == Obj::RANDOM_DWELLING_FACTION);
|
||||
assert(randomizationInfo.has_value());
|
||||
|
||||
if (!randomizationInfo)
|
||||
return rand.nextInt(1, 7) - 1;
|
||||
|
||||
if(randomizationInfo->minLevel == randomizationInfo->maxLevel)
|
||||
return randomizationInfo->minLevel - 1;
|
||||
|
||||
return rand.nextInt(randomizationInfo->minLevel, randomizationInfo->maxLevel) - 1;
|
||||
}
|
||||
|
||||
CGDwelling::~CGDwelling()
|
||||
void CGDwelling::pickRandomObject(CRandomGenerator & rand)
|
||||
{
|
||||
vstd::clear_pointer(info);
|
||||
if (ID == Obj::RANDOM_DWELLING || ID == Obj::RANDOM_DWELLING_LVL || ID == Obj::RANDOM_DWELLING_FACTION)
|
||||
{
|
||||
FactionID faction = randomizeFaction(rand);
|
||||
int level = randomizeLevel(rand);
|
||||
assert(faction != FactionID::NONE && faction != FactionID::NEUTRAL);
|
||||
assert(level >= 0 && level <= 6);
|
||||
randomizationInfo.reset();
|
||||
|
||||
CreatureID cid = (*VLC->townh)[faction]->town->creatures[level][0];
|
||||
|
||||
//NOTE: this will pick last dwelling with this creature (Mantis #900)
|
||||
//check for block map equality is better but more complex solution
|
||||
auto testID = [&](const Obj & primaryID) -> MapObjectSubID
|
||||
{
|
||||
auto dwellingIDs = VLC->objtypeh->knownSubObjects(primaryID);
|
||||
for (si32 entry : dwellingIDs)
|
||||
{
|
||||
const auto * handler = dynamic_cast<const DwellingInstanceConstructor *>(VLC->objtypeh->getHandlerFor(primaryID, entry).get());
|
||||
|
||||
if (handler->producesCreature(cid.toCreature()))
|
||||
return MapObjectSubID(entry);
|
||||
}
|
||||
return MapObjectSubID();
|
||||
};
|
||||
|
||||
ID = Obj::CREATURE_GENERATOR1;
|
||||
subID = testID(Obj::CREATURE_GENERATOR1);
|
||||
|
||||
if (subID == MapObjectSubID())
|
||||
{
|
||||
ID = Obj::CREATURE_GENERATOR4;
|
||||
subID = testID(Obj::CREATURE_GENERATOR4);
|
||||
}
|
||||
|
||||
if (subID == MapObjectSubID())
|
||||
{
|
||||
logGlobal->error("Error: failed to find dwelling for %s of level %d", (*VLC->townh)[faction]->getNameTranslated(), int(level));
|
||||
ID = Obj::CREATURE_GENERATOR4;
|
||||
subID = *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), rand);
|
||||
}
|
||||
|
||||
setType(ID, subID);
|
||||
}
|
||||
}
|
||||
|
||||
void CGDwelling::initObj(CRandomGenerator & rand)
|
||||
{
|
||||
switch(ID)
|
||||
switch(ID.toEnum())
|
||||
{
|
||||
case Obj::CREATURE_GENERATOR1:
|
||||
case Obj::CREATURE_GENERATOR4:
|
||||
{
|
||||
VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand);
|
||||
getObjectHandler()->configureObject(this, rand);
|
||||
|
||||
if (getOwner() != PlayerColor::NEUTRAL)
|
||||
cb->gameState()->players[getOwner()].dwellings.emplace_back(this);
|
||||
@ -121,23 +201,6 @@ void CGDwelling::initObj(CRandomGenerator & rand)
|
||||
}
|
||||
}
|
||||
|
||||
void CGDwelling::initRandomObjectInfo()
|
||||
{
|
||||
vstd::clear_pointer(info);
|
||||
switch(ID)
|
||||
{
|
||||
case Obj::RANDOM_DWELLING: info = new CCreGenLeveledCastleInfo();
|
||||
break;
|
||||
case Obj::RANDOM_DWELLING_LVL: info = new CCreGenAsCastleInfo();
|
||||
break;
|
||||
case Obj::RANDOM_DWELLING_FACTION: info = new CCreGenLeveledInfo();
|
||||
break;
|
||||
}
|
||||
|
||||
if(info)
|
||||
info->owner = this;
|
||||
}
|
||||
|
||||
void CGDwelling::setPropertyDer(ui8 what, ui32 val)
|
||||
{
|
||||
switch (what)
|
||||
@ -256,7 +319,7 @@ void CGDwelling::newTurn(CRandomGenerator & rand) const
|
||||
else
|
||||
creaturesAccumulate = VLC->settings()->getBoolean(EGameSettings::DWELLINGS_ACCUMULATE_WHEN_NEUTRAL);
|
||||
|
||||
CCreature *cre = VLC->creh->objects[creatures[i].second[0]];
|
||||
const CCreature * cre =creatures[i].second[0].toCreature();
|
||||
TQuantity amount = cre->getGrowth() * (1 + cre->valOfBonuses(BonusType::CREATURE_GROWTH_PERCENT)/100) + cre->valOfBonuses(BonusType::CREATURE_GROWTH);
|
||||
if (creaturesAccumulate && ID != Obj::REFUGEE_CAMP) //camp should not try to accumulate different kinds of creatures
|
||||
sac.creatures[i].first += amount;
|
||||
@ -281,7 +344,7 @@ void CGDwelling::updateGuards() const
|
||||
//default condition - creatures are of level 5 or higher
|
||||
for (auto creatureEntry : creatures)
|
||||
{
|
||||
if (VLC->creatures()->getByIndex(creatureEntry.second.at(0))->getLevel() >= 5 && ID != Obj::REFUGEE_CAMP)
|
||||
if (VLC->creatures()->getById(creatureEntry.second.at(0))->getLevel() >= 5 && ID != Obj::REFUGEE_CAMP)
|
||||
{
|
||||
guarded = true;
|
||||
break;
|
||||
@ -292,7 +355,7 @@ void CGDwelling::updateGuards() const
|
||||
{
|
||||
for (auto creatureEntry : creatures)
|
||||
{
|
||||
const CCreature * crea = VLC->creh->objects[creatureEntry.second.at(0)];
|
||||
const CCreature * crea = creatureEntry.second.at(0).toCreature();
|
||||
SlotID slot = getSlotFor(crea->getId());
|
||||
|
||||
if (hasStackAtSlot(slot)) //stack already exists, overwrite it
|
||||
@ -425,10 +488,7 @@ void CGDwelling::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
|
||||
|
||||
void CGDwelling::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
{
|
||||
if(!handler.saving)
|
||||
initRandomObjectInfo();
|
||||
|
||||
switch (ID)
|
||||
switch (ID.toEnum())
|
||||
{
|
||||
case Obj::WAR_MACHINE_FACTORY:
|
||||
case Obj::REFUGEE_CAMP:
|
||||
@ -437,8 +497,10 @@ void CGDwelling::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
case Obj::RANDOM_DWELLING:
|
||||
case Obj::RANDOM_DWELLING_LVL:
|
||||
case Obj::RANDOM_DWELLING_FACTION:
|
||||
info->serializeJson(handler);
|
||||
//fall through
|
||||
if (!handler.saving)
|
||||
randomizationInfo = CGDwellingRandomizationInfo();
|
||||
randomizationInfo->serializeJson(handler);
|
||||
[[fallthrough]];
|
||||
default:
|
||||
serializeJsonOwner(handler);
|
||||
break;
|
||||
|
@ -16,62 +16,39 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CGDwelling;
|
||||
|
||||
class DLL_LINKAGE CSpecObjInfo
|
||||
class DLL_LINKAGE CGDwellingRandomizationInfo
|
||||
{
|
||||
public:
|
||||
CSpecObjInfo();
|
||||
virtual ~CSpecObjInfo() = default;
|
||||
|
||||
virtual void serializeJson(JsonSerializeFormat & handler) = 0;
|
||||
|
||||
const CGDwelling * owner;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CCreGenAsCastleInfo : public virtual CSpecObjInfo
|
||||
{
|
||||
public:
|
||||
bool asCastle = false;
|
||||
ui32 identifier = 0;//h3m internal identifier
|
||||
|
||||
std::vector<bool> allowedFactions;
|
||||
std::set<FactionID> allowedFactions;
|
||||
|
||||
std::string instanceId;//vcmi map instance identifier
|
||||
void serializeJson(JsonSerializeFormat & handler) override;
|
||||
int32_t identifier = 0;//h3m internal identifier
|
||||
|
||||
uint8_t minLevel = 1;
|
||||
uint8_t maxLevel = 7; //minimal and maximal level of creature in dwelling: <1, 7>
|
||||
|
||||
void serializeJson(JsonSerializeFormat & handler);
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CCreGenLeveledInfo : public virtual CSpecObjInfo
|
||||
{
|
||||
public:
|
||||
ui8 minLevel = 1;
|
||||
ui8 maxLevel = 7; //minimal and maximal level of creature in dwelling: <1, 7>
|
||||
|
||||
void serializeJson(JsonSerializeFormat & handler) override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CCreGenLeveledCastleInfo : public CCreGenAsCastleInfo, public CCreGenLeveledInfo
|
||||
{
|
||||
public:
|
||||
CCreGenLeveledCastleInfo() = default;
|
||||
void serializeJson(JsonSerializeFormat & handler) override;
|
||||
};
|
||||
|
||||
|
||||
class DLL_LINKAGE CGDwelling : public CArmedInstance
|
||||
{
|
||||
public:
|
||||
typedef std::vector<std::pair<ui32, std::vector<CreatureID> > > TCreaturesSet;
|
||||
|
||||
CSpecObjInfo * info; //random dwelling options; not serialized
|
||||
std::optional<CGDwellingRandomizationInfo> randomizationInfo; //random dwelling options; not serialized
|
||||
TCreaturesSet creatures; //creatures[level] -> <vector of alternative ids (base creature and upgrades, creatures amount>
|
||||
|
||||
CGDwelling();
|
||||
~CGDwelling() override;
|
||||
|
||||
void initRandomObjectInfo();
|
||||
protected:
|
||||
void serializeJsonOptions(JsonSerializeFormat & handler) override;
|
||||
|
||||
private:
|
||||
FactionID randomizeFaction(CRandomGenerator & rand);
|
||||
int randomizeLevel(CRandomGenerator & rand);
|
||||
|
||||
void pickRandomObject(CRandomGenerator & rand) override;
|
||||
void initObj(CRandomGenerator & rand) override;
|
||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||
void newTurn(CRandomGenerator & rand) const override;
|
||||
|
@ -218,7 +218,10 @@ bool CGHeroInstance::canLearnSkill(const SecondarySkill & which) const
|
||||
if (getSecSkillLevel(which) > 0)
|
||||
return false;
|
||||
|
||||
if (type->heroClass->secSkillProbability[which] == 0)
|
||||
if (type->heroClass->secSkillProbability.count(which) == 0)
|
||||
return false;
|
||||
|
||||
if (type->heroClass->secSkillProbability.at(which) == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -285,27 +288,28 @@ PlayerColor CGHeroInstance::getOwner() const
|
||||
return tempOwner;
|
||||
}
|
||||
|
||||
HeroTypeID CGHeroInstance::getHeroType() const
|
||||
{
|
||||
return HeroTypeID(getObjTypeIndex().getNum());
|
||||
}
|
||||
|
||||
void CGHeroInstance::setHeroType(HeroTypeID heroType)
|
||||
{
|
||||
assert(type == nullptr);
|
||||
subID = heroType;
|
||||
}
|
||||
|
||||
void CGHeroInstance::initHero(CRandomGenerator & rand, const HeroTypeID & SUBID)
|
||||
{
|
||||
subID = SUBID.getNum();
|
||||
initHero(rand);
|
||||
}
|
||||
|
||||
void CGHeroInstance::setType(si32 ID, si32 subID)
|
||||
{
|
||||
assert(ID == Obj::HERO); // just in case
|
||||
type = VLC->heroh->objects[subID];
|
||||
|
||||
CGObjectInstance::setType(ID, type->heroClass->getIndex()); // to find object handler we must use heroClass->id
|
||||
this->subID = subID; // after setType subID used to store unique hero identify id. Check issue 2277 for details
|
||||
randomizeArmy(type->heroClass->faction);
|
||||
}
|
||||
|
||||
void CGHeroInstance::initHero(CRandomGenerator & rand)
|
||||
{
|
||||
assert(validTypes(true));
|
||||
if(!type)
|
||||
type = VLC->heroh->objects[subID];
|
||||
type = VLC->heroh->objects[getHeroType().getNum()];
|
||||
|
||||
if (ID == Obj::HERO)
|
||||
appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
|
||||
@ -566,18 +570,35 @@ void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter()
|
||||
wisdomCounter = 1;
|
||||
}
|
||||
|
||||
void CGHeroInstance::pickRandomObject(CRandomGenerator & rand)
|
||||
{
|
||||
assert(ID == Obj::HERO || ID == Obj::PRISON || ID == Obj::RANDOM_HERO);
|
||||
|
||||
if (ID == Obj::RANDOM_HERO)
|
||||
{
|
||||
ID = Obj::HERO;
|
||||
subID = cb->gameState()->pickNextHeroType(getOwner());
|
||||
type = VLC->heroh->objects[subID];
|
||||
randomizeArmy(type->heroClass->faction);
|
||||
}
|
||||
else
|
||||
type = VLC->heroh->objects[subID];
|
||||
|
||||
auto oldSubID = subID;
|
||||
|
||||
// to find object handler we must use heroClass->id
|
||||
// after setType subID used to store unique hero identify id. Check issue 2277 for details
|
||||
if (ID != Obj::PRISON)
|
||||
setType(ID, type->heroClass->getIndex());
|
||||
else
|
||||
setType(ID, 0);
|
||||
|
||||
this->subID = oldSubID;
|
||||
}
|
||||
|
||||
void CGHeroInstance::initObj(CRandomGenerator & rand)
|
||||
{
|
||||
if(!type)
|
||||
initHero(rand); //TODO: set up everything for prison before specialties are configured
|
||||
|
||||
if (ID != Obj::PRISON)
|
||||
{
|
||||
auto terrain = cb->gameState()->getTile(visitablePos())->terType->getId();
|
||||
auto customApp = VLC->objtypeh->getHandlerFor(ID, type->heroClass->getIndex())->getOverride(terrain, this);
|
||||
if (customApp)
|
||||
appearance = customApp;
|
||||
}
|
||||
}
|
||||
|
||||
void CGHeroInstance::recreateSecondarySkillsBonuses()
|
||||
@ -926,7 +947,7 @@ void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedSta
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.soundID = soundBase::pickup01 + rand.nextInt(6);
|
||||
iw.player = tempOwner;
|
||||
iw.components.emplace_back(raisedStack);
|
||||
iw.components.emplace_back(ComponentType::CREATURE, raisedStack.getId(), raisedStack.count);
|
||||
|
||||
if (raisedStack.count > 1) // Practicing the dark arts of necromancy, ... (plural)
|
||||
{
|
||||
@ -1050,7 +1071,7 @@ HeroTypeID CGHeroInstance::getPortraitSource() const
|
||||
if (customPortraitSource.isValid())
|
||||
return customPortraitSource;
|
||||
else
|
||||
return HeroTypeID(subID);
|
||||
return getHeroType();
|
||||
}
|
||||
|
||||
int32_t CGHeroInstance::getIconIndex() const
|
||||
@ -1092,7 +1113,7 @@ std::string CGHeroInstance::getBiographyTextID() const
|
||||
|
||||
CGHeroInstance::ArtPlacementMap CGHeroInstance::putArtifact(ArtifactPosition pos, CArtifactInstance * art)
|
||||
{
|
||||
assert(art->artType->canBePutAt(this, pos));
|
||||
assert(art->canBePutAt(this, pos));
|
||||
|
||||
if(ArtifactUtils::isSlotEquipment(pos))
|
||||
attachTo(*art);
|
||||
@ -1135,7 +1156,7 @@ void CGHeroInstance::removeSpellbook()
|
||||
|
||||
if(hasSpellbook())
|
||||
{
|
||||
ArtifactLocation(this, ArtifactPosition(ArtifactPosition::SPELLBOOK)).removeArtifact();
|
||||
getArt(ArtifactPosition::SPELLBOOK)->removeFrom(*this, ArtifactPosition::SPELLBOOK);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1502,7 +1523,7 @@ std::string CGHeroInstance::getHeroTypeName() const
|
||||
}
|
||||
else
|
||||
{
|
||||
return VLC->heroh->objects[subID]->getJsonKey();
|
||||
return VLC->heroh->objects[getHeroType()]->getJsonKey();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
@ -1512,7 +1533,7 @@ void CGHeroInstance::afterAddToMap(CMap * map)
|
||||
{
|
||||
auto existingHero = std::find_if(map->objects.begin(), map->objects.end(), [&](const CGObjectInstance * o) ->bool
|
||||
{
|
||||
return (o->ID == Obj::HERO || o->ID == Obj::PRISON) && o->subID == subID && o != this;
|
||||
return o && (o->ID == Obj::HERO || o->ID == Obj::PRISON) && o->subID == subID && o != this;
|
||||
});
|
||||
|
||||
if(existingHero != map->objects.end())
|
||||
@ -1529,14 +1550,14 @@ void CGHeroInstance::afterAddToMap(CMap * map)
|
||||
}
|
||||
}
|
||||
|
||||
if(ID == Obj::HERO)
|
||||
if(ID != Obj::PRISON)
|
||||
{
|
||||
map->heroesOnMap.emplace_back(this);
|
||||
}
|
||||
}
|
||||
void CGHeroInstance::afterRemoveFromMap(CMap* map)
|
||||
{
|
||||
if (ID == Obj::HERO)
|
||||
if (ID == Obj::PRISON)
|
||||
vstd::erase_if_present(map->heroesOnMap, this);
|
||||
}
|
||||
|
||||
@ -1661,7 +1682,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
|
||||
{
|
||||
auto addSkill = [this](const std::string & skillId, const std::string & levelId)
|
||||
{
|
||||
const int rawId = CSkillHandler::decodeSkill(skillId);
|
||||
const int rawId = SecondarySkill::decode(skillId);
|
||||
if(rawId < 0)
|
||||
{
|
||||
logGlobal->error("Invalid secondary skill %s", skillId);
|
||||
@ -1736,7 +1757,7 @@ void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
if(!appearance)
|
||||
{
|
||||
// crossoverDeserialize
|
||||
type = VLC->heroh->objects[subID];
|
||||
type = VLC->heroh->objects[getHeroType()];
|
||||
appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
|
||||
}
|
||||
|
||||
|
@ -231,7 +231,8 @@ public:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setType(si32 ID, si32 subID) override;
|
||||
HeroTypeID getHeroType() const;
|
||||
void setHeroType(HeroTypeID type);
|
||||
|
||||
void initHero(CRandomGenerator & rand);
|
||||
void initHero(CRandomGenerator & rand, const HeroTypeID & SUBID);
|
||||
@ -294,6 +295,7 @@ public:
|
||||
void deserializationFix();
|
||||
|
||||
void initObj(CRandomGenerator & rand) override;
|
||||
void pickRandomObject(CRandomGenerator & rand) override;
|
||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||
std::string getObjectName() const override;
|
||||
|
||||
|
@ -25,7 +25,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
void CGMarket::initObj(CRandomGenerator & rand)
|
||||
{
|
||||
VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand);
|
||||
getObjectHandler()->configureObject(this, rand);
|
||||
}
|
||||
|
||||
void CGMarket::onHeroVisit(const CGHeroInstance * h) const
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user