1
0
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:
Tomasz Zieliński 2023-11-07 20:54:04 +01:00
commit c909bd766e
159 changed files with 1727 additions and 1726 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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:

View File

@ -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>>
{

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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!");
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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

View File

@ -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())

View File

@ -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);

View File

@ -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));
}

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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()

View File

@ -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));
}

View File

@ -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));
}

View File

@ -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);

View File

@ -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);
};

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
};
};

View File

@ -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]);

View File

@ -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();
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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));
}
}

View File

@ -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

View File

@ -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;

View File

@ -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
};
/*

View File

@ -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)

View File

@ -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;

View File

@ -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()

View 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()

View File

@ -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.

View File

@ -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})

View File

@ -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());

View File

@ -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());
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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()

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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());

View File

@ -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;
};

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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 "#!#";

View File

@ -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())

View File

@ -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) \

View File

@ -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

View File

@ -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:

View File

@ -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);

View File

@ -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();

View File

@ -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;
}

View File

@ -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());
}

View File

@ -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";

View File

@ -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;

View File

@ -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:

View File

@ -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;

View File

@ -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 -----

View File

@ -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)

View File

@ -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)

View File

@ -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();
}

View File

@ -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)
{

View File

@ -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)

View File

@ -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);
}

View File

@ -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())

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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