1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-05-31 22:59:54 +02:00

Artifact instances are now owned solely by CMap

This commit is contained in:
Ivan Savenko 2025-03-10 16:20:40 +00:00
parent 797646cc05
commit 2ca1748e96
31 changed files with 203 additions and 194 deletions

View File

@ -223,49 +223,6 @@ DLL_LINKAGE std::vector<const CArtifact*> ArtifactUtils::assemblyPossibilities(
return arts;
}
DLL_LINKAGE CArtifactInstance * ArtifactUtils::createScroll(const SpellID & spellId)
{
return ArtifactUtils::createArtifact(ArtifactID::SPELL_SCROLL, spellId);
}
DLL_LINKAGE CArtifactInstance * ArtifactUtils::createArtifact(const ArtifactID & artId, const SpellID & spellId)
{
const std::function<CArtifactInstance*(const CArtifact*)> createArtInst =
[&createArtInst, &spellId](const CArtifact * art) -> CArtifactInstance*
{
assert(art);
auto * artInst = new CArtifactInstance(art);
if(art->isCombined() && !art->isFused())
{
for(const auto & part : art->getConstituents())
artInst->addPart(createArtInst(part), ArtifactPosition::PRE_FIRST);
}
if(art->isGrowing())
{
auto bonus = std::make_shared<Bonus>();
bonus->type = BonusType::LEVEL_COUNTER;
bonus->val = 0;
artInst->addNewBonus(bonus);
}
if(art->isScroll())
{
artInst->addNewBonus(std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::SPELL,
BonusSource::ARTIFACT_INSTANCE, -1, BonusSourceID(ArtifactID(ArtifactID::SPELL_SCROLL)), BonusSubtypeID(spellId)));
}
return artInst;
};
if(artId.getNum() >= 0)
{
return createArtInst(artId.toArtifact());
}
else
{
return new CArtifactInstance(); // random, empty
}
}
DLL_LINKAGE void ArtifactUtils::insertScrrollSpellName(std::string & description, const SpellID & sid)
{
// We expect scroll description to be like this: This scroll contains the [spell name] spell which is added

View File

@ -39,8 +39,6 @@ namespace ArtifactUtils
DLL_LINKAGE bool isSlotEquipment(const ArtifactPosition & slot);
DLL_LINKAGE bool isBackpackFreeSlots(const CArtifactSet * target, const size_t reqSlots = 1);
DLL_LINKAGE std::vector<const CArtifact*> assemblyPossibilities(const CArtifactSet * artSet, const ArtifactID & aid, const bool onlyEquiped = false);
DLL_LINKAGE CArtifactInstance * createScroll(const SpellID & spellId);
DLL_LINKAGE CArtifactInstance * createArtifact(const ArtifactID & artId, const SpellID & spellId = SpellID::NONE);
DLL_LINKAGE void insertScrrollSpellName(std::string & description, const SpellID & sid);
}

View File

@ -18,6 +18,7 @@
#include "json/JsonBonus.h"
#include "mapObjectConstructors/AObjectTypeHandler.h"
#include "mapObjectConstructors/CObjectClassesHandler.h"
#include "mapping/CMap.h"
#include "serializer/JsonSerializeFormat.h"
#include "texts/CGeneralTextHandler.h"
#include "texts/CLegacyConfigParser.h"
@ -705,20 +706,28 @@ void CArtHandler::afterLoadFinalization()
}
art->nodeHasChanged();
}
}
CArtifactInstance * CArtifactSet::getArt(const ArtifactPosition & pos, bool excludeLocked) const
const CArtifactInstance * CArtifactSet::getArt(const ArtifactPosition & pos, bool excludeLocked) const
{
if(const ArtSlotInfo * si = getSlot(pos))
{
if(si->artifact && (!excludeLocked || !si->locked))
return si->artifact;
}
return nullptr;
}
ArtifactInstanceID CArtifactSet::getArtID(const ArtifactPosition & pos, bool excludeLocked) const
{
if(const ArtSlotInfo * si = getSlot(pos))
{
if(si->artifact && (!excludeLocked || !si->locked))
return si->artifact->getId();
}
return {};
}
ArtifactPosition CArtifactSet::getArtPos(const ArtifactID & aid, bool onlyWorn, bool allowLocked) const
{
for(const auto & [slot, slotInfo] : artifactsWorn)
@ -781,10 +790,10 @@ bool CArtifactSet::hasArt(const ArtifactID & aid, bool onlyWorn, bool searchComb
return false;
}
CArtifactSet::ArtPlacementMap CArtifactSet::putArtifact(const ArtifactPosition & slot, CArtifactInstance * art)
CArtifactSet::ArtPlacementMap CArtifactSet::putArtifact(const ArtifactPosition & slot, const CArtifactInstance * art)
{
ArtPlacementMap resArtPlacement;
const auto putToSlot = [this](const ArtifactPosition & targetSlot, CArtifactInstance * targetArt, bool locked)
const auto putToSlot = [this](const ArtifactPosition & targetSlot, const CArtifactInstance * targetArt, bool locked)
{
ArtSlotInfo * slotInfo;
if(targetSlot == ArtifactPosition::TRANSITION_POS)
@ -939,10 +948,10 @@ void CArtifactSet::artDeserializationFix(CBonusSystemNode *node)
{
for(auto & elem : artifactsWorn)
if(elem.second.artifact && !elem.second.locked)
node->attachTo(*elem.second.artifact);
node->attachToSource(*elem.second.artifact);
}
void CArtifactSet::serializeJsonArtifacts(JsonSerializeFormat & handler, const std::string & fieldName)
void CArtifactSet::serializeJsonArtifacts(JsonSerializeFormat & handler, const std::string & fieldName, CMap * map)
{
//todo: creature and commander artifacts
if(handler.saving && artifactsInBackpack.empty() && artifactsWorn.empty())
@ -959,7 +968,7 @@ void CArtifactSet::serializeJsonArtifacts(JsonSerializeFormat & handler, const s
switch(bearerType())
{
case ArtBearer::HERO:
serializeJsonHero(handler);
serializeJsonHero(handler, map);
break;
case ArtBearer::CREATURE:
serializeJsonCreature(handler);
@ -973,11 +982,11 @@ void CArtifactSet::serializeJsonArtifacts(JsonSerializeFormat & handler, const s
}
}
void CArtifactSet::serializeJsonHero(JsonSerializeFormat & handler)
void CArtifactSet::serializeJsonHero(JsonSerializeFormat & handler, CMap * map)
{
for(const auto & slot : ArtifactUtils::allWornSlots())
{
serializeJsonSlot(handler, slot);
serializeJsonSlot(handler, slot, map);
}
std::vector<ArtifactID> backpackTemp;
@ -993,7 +1002,7 @@ void CArtifactSet::serializeJsonHero(JsonSerializeFormat & handler)
{
for(const ArtifactID & artifactID : backpackTemp)
{
auto * artifact = ArtifactUtils::createArtifact(artifactID);
auto * artifact = map->createArtifact(artifactID);
auto slot = ArtifactPosition::BACKPACK_START + artifactsInBackpack.size();
if(artifact->getType()->canBePutAt(this, slot))
{
@ -1014,7 +1023,7 @@ void CArtifactSet::serializeJsonCommander(JsonSerializeFormat & handler)
logGlobal->error("CArtifactSet::serializeJsonCommander not implemented");
}
void CArtifactSet::serializeJsonSlot(JsonSerializeFormat & handler, const ArtifactPosition & slot)
void CArtifactSet::serializeJsonSlot(JsonSerializeFormat & handler, const ArtifactPosition & slot, CMap * map)
{
ArtifactID artifactID;
@ -1034,7 +1043,7 @@ void CArtifactSet::serializeJsonSlot(JsonSerializeFormat & handler, const Artifa
if(artifactID != ArtifactID::NONE)
{
auto * artifact = ArtifactUtils::createArtifact(artifactID.toEnum());
auto * artifact = map->createArtifact(artifactID.toEnum());
if(artifact->getType()->canBePutAt(this, slot))
{

View File

@ -22,6 +22,7 @@ VCMI_LIB_NAMESPACE_BEGIN
class CArtHandler;
class CGHeroInstance;
class CMap;
class CArtifactSet;
class CArtifactInstance;
class JsonSerializeFormat;
@ -179,11 +180,12 @@ private:
struct DLL_LINKAGE ArtSlotInfo
{
CArtifactInstance * artifact;
const CArtifactInstance * artifact;
bool locked; //if locked, then artifact points to the combined artifact
ArtSlotInfo() : artifact(nullptr), locked(false) {}
const CArtifactInstance * getArt() const;
ArtifactInstanceID getID() const;
template <typename Handler> void serialize(Handler & h)
{
@ -195,7 +197,7 @@ struct DLL_LINKAGE ArtSlotInfo
class DLL_LINKAGE CArtifactSet : public virtual Serializeable
{
public:
using ArtPlacementMap = std::map<CArtifactInstance*, ArtifactPosition>;
using ArtPlacementMap = std::map<const CArtifactInstance*, ArtifactPosition>;
std::vector<ArtSlotInfo> artifactsInBackpack; //hero's artifacts from bag
std::map<ArtifactPosition, ArtSlotInfo> artifactsWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
@ -203,7 +205,8 @@ public:
const ArtSlotInfo * getSlot(const ArtifactPosition & pos) const;
void lockSlot(const ArtifactPosition & pos);
CArtifactInstance * getArt(const ArtifactPosition & pos, bool excludeLocked = true) const;
const CArtifactInstance * getArt(const ArtifactPosition & pos, bool excludeLocked = true) const;
ArtifactInstanceID getArtID(const ArtifactPosition & pos, bool excludeLocked = true) const;
/// Looks for first artifact with given ID
ArtifactPosition getArtPos(const ArtifactID & aid, bool onlyWorn = true, bool allowLocked = true) const;
ArtifactPosition getArtPos(const CArtifactInstance * art) const;
@ -212,7 +215,7 @@ public:
bool isPositionFree(const ArtifactPosition & pos, bool onlyLockCheck = false) const;
virtual ArtBearer::ArtBearer bearerType() const = 0;
virtual ArtPlacementMap putArtifact(const ArtifactPosition & slot, CArtifactInstance * art);
virtual ArtPlacementMap putArtifact(const ArtifactPosition & slot, const CArtifactInstance * art);
virtual void removeArtifact(const ArtifactPosition & slot);
virtual ~CArtifactSet() = default;
@ -224,15 +227,15 @@ public:
void artDeserializationFix(CBonusSystemNode *node);
void serializeJsonArtifacts(JsonSerializeFormat & handler, const std::string & fieldName);
void serializeJsonArtifacts(JsonSerializeFormat & handler, const std::string & fieldName, CMap * map);
const CArtifactInstance * getCombinedArtWithPart(const ArtifactID & partId) const;
private:
void serializeJsonHero(JsonSerializeFormat & handler);
void serializeJsonHero(JsonSerializeFormat & handler, CMap * map);
void serializeJsonCreature(JsonSerializeFormat & handler);
void serializeJsonCommander(JsonSerializeFormat & handler);
void serializeJsonSlot(JsonSerializeFormat & handler, const ArtifactPosition & slot);//normal slots
void serializeJsonSlot(JsonSerializeFormat & handler, const ArtifactPosition & slot, CMap * map);//normal slots
};
// Used to try on artifacts before the claimed changes have been applied

View File

@ -17,7 +17,7 @@
VCMI_LIB_NAMESPACE_BEGIN
void CCombinedArtifactInstance::addPart(CArtifactInstance * art, const ArtifactPosition & slot)
void CCombinedArtifactInstance::addPart(const CArtifactInstance * art, const ArtifactPosition & slot)
{
auto artInst = static_cast<CArtifactInstance*>(this);
assert(vstd::contains_if(artInst->getType()->getConstituents(),
@ -27,7 +27,7 @@ void CCombinedArtifactInstance::addPart(CArtifactInstance * art, const ArtifactP
}));
assert(art->getParentNodes().size() == 1 && art->getParentNodes().front() == art->getType());
partsInfo.emplace_back(art, slot);
artInst->attachTo(*art);
artInst->attachToSource(*art);
}
bool CCombinedArtifactInstance::isPart(const CArtifactInstance * supposedPart) const
@ -173,7 +173,7 @@ void CArtifactInstance::deserializationFix()
{
setType(artTypeID.toArtifact());
for(PartInfo & part : partsInfo)
attachTo(*part.art);
attachToSource(*part.art);
}
VCMI_LIB_NAMESPACE_END

View File

@ -10,7 +10,6 @@
#pragma once
#include "bonuses/CBonusSystemNode.h"
#include "GameConstants.h"
#include "CArtHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -24,17 +23,17 @@ protected:
public:
struct PartInfo
{
CArtifactInstance * art;
const CArtifactInstance * art;
ArtifactPosition slot;
template <typename Handler> void serialize(Handler & h)
{
h & art;
h & slot;
}
PartInfo(CArtifactInstance * art = nullptr, const ArtifactPosition & slot = ArtifactPosition::PRE_FIRST)
PartInfo(const CArtifactInstance * art = nullptr, const ArtifactPosition & slot = ArtifactPosition::PRE_FIRST)
: art(art), slot(slot) {};
};
void addPart(CArtifactInstance * art, const ArtifactPosition & slot);
void addPart(const CArtifactInstance * art, const ArtifactPosition & slot);
// Checks if supposed part inst is part of this combined art inst
bool isPart(const CArtifactInstance * supposedPart) const;
bool hasParts() const;
@ -65,7 +64,7 @@ public:
void growingUp();
};
class DLL_LINKAGE CArtifactInstance
class DLL_LINKAGE CArtifactInstance final
: public CBonusSystemNode, public CCombinedArtifactInstance, public CScrollArtifactInstance, public CGrowingArtifactInstance
{
protected:

View File

@ -901,12 +901,12 @@ ArtBearer::ArtBearer CStackInstance::bearerType() const
return ArtBearer::CREATURE;
}
CStackInstance::ArtPlacementMap CStackInstance::putArtifact(const ArtifactPosition & pos, CArtifactInstance * art)
CStackInstance::ArtPlacementMap CStackInstance::putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art)
{
assert(!getArt(pos));
assert(art->canBePutAt(this, pos));
attachTo(*art);
attachToSource(*art);
return CArtifactSet::putArtifact(pos, art);
}
@ -914,7 +914,7 @@ void CStackInstance::removeArtifact(const ArtifactPosition & pos)
{
assert(getArt(pos));
detachFrom(*getArt(pos));
detachFromSource(*getArt(pos));
CArtifactSet::removeArtifact(pos);
}

View File

@ -132,7 +132,7 @@ public:
void setArmyObj(const CArmedInstance *ArmyObj);
virtual void giveStackExp(TExpType exp);
bool valid(bool allowUnrandomized) const;
ArtPlacementMap putArtifact(const ArtifactPosition & pos, CArtifactInstance * art) override;//from CArtifactSet
ArtPlacementMap putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) override;//from CArtifactSet
void removeArtifact(const ArtifactPosition & pos) override;
ArtBearer::ArtBearer bearerType() const override; //from CArtifactSet
std::string nodeName() const override; //from CBonusSystemnode

View File

@ -951,7 +951,7 @@ void CGameInfoCallback::calculatePaths(const std::shared_ptr<PathfinderConfig> &
const CArtifactInstance * CGameInfoCallback::getArtInstance( ArtifactInstanceID aid ) const
{
return gs->getMap().artInstances.at(aid.num);
return gs->getMap().getArtifactInstance(aid);
}
const CGObjectInstance * CGameInfoCallback::getObjInstance( ObjectInstanceID oid ) const

View File

@ -251,7 +251,7 @@ PlayerState * CNonConstInfoCallback::getPlayerState(const PlayerColor & color, b
CArtifactInstance * CNonConstInfoCallback::getArtInstance(const ArtifactInstanceID & aid)
{
return gs->getMap().artInstances.at(aid.num);
return gs->getMap().getArtifactInstance(aid);
}
CGObjectInstance * CNonConstInfoCallback::getObjInstance(const ObjectInstanceID & oid)

View File

@ -483,8 +483,7 @@ CGHeroInstance * CampaignState::crossoverDeserialize(const JsonNode & node, CMap
hero->serializeJsonOptions(handler);
if (map)
{
hero->serializeJsonArtifacts(handler, "artifacts");
map->addNewArtifactInstance(*hero);
hero->serializeJsonArtifacts(handler, "artifacts", map);
}
return hero;
}

View File

@ -1618,12 +1618,11 @@ void CGameState::attachArmedObjects()
bool CGameState::giveHeroArtifact(CGHeroInstance * h, const ArtifactID & aid)
{
CArtifactInstance * ai = ArtifactUtils::createArtifact(aid);
map->addNewArtifactInstance(ai);
CArtifactInstance * ai = createArtifact(aid);
auto slot = ArtifactUtils::getArtAnyPosition(h, aid);
if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot))
{
map->putArtifactInstance(*h, ai, slot);
map->putArtifactInstance(*h, ai->getId(), slot);
return true;
}
else
@ -1766,4 +1765,14 @@ ArtifactID CGameState::pickRandomArtifact(vstd::RNG & rand, int flags)
return pickRandomArtifact(rand, flags, [](const ArtifactID &) { return true; });
}
CArtifactInstance * CGameState::createScroll(const SpellID & spellId)
{
return map->createScroll(spellId);
}
CArtifactInstance * CGameState::createArtifact(const ArtifactID & artID, const SpellID & spellId)
{
return map->createArtifact(artID, spellId);
}
VCMI_LIB_NAMESPACE_END

View File

@ -107,6 +107,9 @@ public:
ArtifactID pickRandomArtifact(vstd::RNG & rand, int flags, std::function<bool(ArtifactID)> accepts);
ArtifactID pickRandomArtifact(vstd::RNG & rand, std::set<ArtifactID> filtered);
CArtifactInstance * createScroll(const SpellID & spellId);
CArtifactInstance * createArtifact(const ArtifactID & artId, const SpellID & spellId = SpellID::NONE);
/// Returns battle in which selected player is engaged, or nullptr if none.
/// Can NOT be used with neutral player, use battle by ID instead
const BattleInfo * getBattle(const PlayerColor & player) const;

View File

@ -325,10 +325,10 @@ void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero)
}
case CampaignBonusType::SPELL_SCROLL:
{
CArtifactInstance * scroll = ArtifactUtils::createScroll(SpellID(curBonus->info2));
const auto scroll = gameState->createScroll(SpellID(curBonus->info2));
const auto slot = ArtifactUtils::getArtAnyPosition(hero, scroll->getTypeId());
if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot))
gameState->map->putArtifactInstance(*hero, scroll, slot);
gameState->map->putArtifactInstance(*hero, scroll->getId(), slot);
else
logGlobal->error("Cannot give starting scroll - no free slots!");
break;
@ -432,7 +432,7 @@ void CGameStateCampaign::transferMissingArtifacts(const CampaignTravel & travelO
const auto slot = ArtifactUtils::getArtAnyPosition(receiver, artifact->getTypeId());
if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot))
gameState->map->putArtifactInstance(*receiver, artifact, slot);
gameState->map->putArtifactInstance(*receiver, artifact->getId(), slot);
else
logGlobal->error("Cannot transfer artifact - no free slots!");
}

View File

@ -405,7 +405,7 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
// hero starts with default spellbook presence status
if(!getArt(ArtifactPosition::SPELLBOOK) && getHeroType()->haveSpellBook)
{
auto artifact = ArtifactUtils::createArtifact(ArtifactID::SPELLBOOK);
auto artifact = cb->gameState()->createArtifact(ArtifactID::SPELLBOOK);
putArtifact(ArtifactPosition::SPELLBOOK, artifact);
}
}
@ -414,7 +414,7 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
if(!getArt(ArtifactPosition::MACH4))
{
auto artifact = ArtifactUtils::createArtifact(ArtifactID::CATAPULT);
auto artifact = cb->gameState()->createArtifact(ArtifactID::CATAPULT);
putArtifact(ArtifactPosition::MACH4, artifact); //everyone has a catapult
}
@ -527,7 +527,7 @@ void CGHeroInstance::initArmy(vstd::RNG & rand, IArmyDescriptor * dst)
if(!getArt(slot))
{
auto artifact = ArtifactUtils::createArtifact(aid);
auto artifact = cb->gameState()->createArtifact(aid);
putArtifact(slot, artifact);
}
else
@ -1236,12 +1236,12 @@ std::string CGHeroInstance::getBiographyTextID() const
return ""; //for random hero
}
CGHeroInstance::ArtPlacementMap CGHeroInstance::putArtifact(const ArtifactPosition & pos, CArtifactInstance * art)
CGHeroInstance::ArtPlacementMap CGHeroInstance::putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art)
{
assert(art->canBePutAt(this, pos));
if(ArtifactUtils::isSlotEquipment(pos))
attachTo(*art);
attachToSource(*art);
return CArtifactSet::putArtifact(pos, art);
}
@ -1252,7 +1252,7 @@ void CGHeroInstance::removeArtifact(const ArtifactPosition & pos)
CArtifactSet::removeArtifact(pos);
if(ArtifactUtils::isSlotEquipment(pos))
detachFrom(*art);
detachFromSource(*art);
}
bool CGHeroInstance::hasSpellbook() const
@ -1768,7 +1768,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
handler.serializeIdArray("spellBook", spells);
if(handler.saving)
CArtifactSet::serializeJsonArtifacts(handler, "artifacts");
CArtifactSet::serializeJsonArtifacts(handler, "artifacts", &cb->gameState()->getMap());
}
void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat & handler)

View File

@ -260,7 +260,7 @@ public:
void initHero(vstd::RNG & rand);
void initHero(vstd::RNG & rand, const HeroTypeID & SUBID);
ArtPlacementMap putArtifact(const ArtifactPosition & pos, CArtifactInstance * art) override;
ArtPlacementMap putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) override;
void removeArtifact(const ArtifactPosition & pos) override;
void initExp(vstd::RNG & rand);
void initArmy(vstd::RNG & rand, IArmyDescriptor *dst = nullptr);

View File

@ -663,10 +663,8 @@ void CGArtifact::initObj(vstd::RNG & rand)
if(ID == Obj::ARTIFACT)
{
if (!storedArtifact)
{
storedArtifact = ArtifactUtils::createArtifact(ArtifactID());
cb->gameState()->getMap().addNewArtifactInstance(storedArtifact);
}
storedArtifact = cb->gameState()->createArtifact(ArtifactID());
if(!storedArtifact->getType())
storedArtifact->setType(getArtifact().toArtifact());
}
@ -813,15 +811,6 @@ void CGArtifact::blockingDialogAnswered(const CGHeroInstance *hero, int32_t answ
cb->startBattle(hero, this);
}
void CGArtifact::afterAddToMap(CMap * map)
{
//Artifacts from map objects are never removed
//FIXME: This should be revertible in map editor
if(ID == Obj::SPELL_SCROLL && storedArtifact && storedArtifact->getId().getNum() < 0)
map->addNewArtifactInstance(storedArtifact);
}
void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler)
{
handler.serializeStruct("guardMessage", message);

View File

@ -109,7 +109,6 @@ public:
void initObj(vstd::RNG & rand) override;
void pickRandomObject(vstd::RNG & rand) override;
void afterAddToMap(CMap * map) override;
BattleField getBattlefield() const override;
ArtifactID getArtifact() const;

View File

@ -194,9 +194,6 @@ CMap::~CMap()
for(auto obj : objects)
obj.dellNull();
for(auto artInstance : artInstances)
artInstance.dellNull();
resetStaticData();
}
@ -467,33 +464,10 @@ void CMap::checkForObjectives()
}
}
void CMap::addNewArtifactInstance(CArtifactSet & artSet)
{
for(const auto & [slot, slotInfo] : artSet.artifactsWorn)
{
if(!slotInfo.locked && slotInfo.getArt())
addNewArtifactInstance(slotInfo.artifact);
}
for(const auto & slotInfo : artSet.artifactsInBackpack)
addNewArtifactInstance(slotInfo.artifact);
}
void CMap::addNewArtifactInstance(ConstTransitivePtr<CArtifactInstance> art)
{
assert(art);
assert(art->getId() == -1);
art->setId(static_cast<ArtifactInstanceID>(artInstances.size()));
artInstances.emplace_back(art);
for(const auto & partInfo : art->getPartsInfo())
addNewArtifactInstance(partInfo.art);
}
void CMap::eraseArtifactInstance(CArtifactInstance * art)
void CMap::eraseArtifactInstance(ArtifactInstanceID art)
{
//TODO: handle for artifacts removed in map editor
assert(artInstances[art->getId().getNum()] == art);
artInstances[art->getId().getNum()].dellNull();
artInstances[art.getNum()] = nullptr;
}
void CMap::moveArtifactInstance(
@ -502,26 +476,29 @@ void CMap::moveArtifactInstance(
{
auto art = srcSet.getArt(srcSlot);
removeArtifactInstance(srcSet, srcSlot);
putArtifactInstance(dstSet, art, dstSlot);
putArtifactInstance(dstSet, art->getId(), dstSlot);
}
void CMap::putArtifactInstance(CArtifactSet & set, CArtifactInstance * art, const ArtifactPosition & slot)
void CMap::putArtifactInstance(CArtifactSet & set, ArtifactInstanceID artID, const ArtifactPosition & slot)
{
art->addPlacementMap(set.putArtifact(slot, art));
auto artifact = artInstances.at(artID.getNum());
artifact->addPlacementMap(set.putArtifact(slot, artifact.get()));
}
void CMap::removeArtifactInstance(CArtifactSet & set, const ArtifactPosition & slot)
{
auto art = set.getArt(slot);
assert(art);
ArtifactInstanceID artID = set.getArtID(slot);
auto artifact = artInstances.at(artID.getNum());
assert(artifact);
set.removeArtifact(slot);
CArtifactSet::ArtPlacementMap partsMap;
for(auto & part : art->getPartsInfo())
for(auto & part : artifact->getPartsInfo())
{
if(part.slot != ArtifactPosition::PRE_FIRST)
partsMap.try_emplace(part.art, ArtifactPosition::PRE_FIRST);
}
art->addPlacementMap(partsMap);
artifact->addPlacementMap(partsMap);
}
void CMap::addNewQuestInstance(std::shared_ptr<CQuest> quest)
@ -777,4 +754,52 @@ void CMap::overrideGameSettings(const JsonNode & input)
return gameSettings->loadOverrides(input);
}
CArtifactInstance * CMap::createScroll(const SpellID & spellId)
{
return createArtifact(ArtifactID::SPELL_SCROLL, spellId);
}
CArtifactInstance * CMap::createSingleArtifact(const ArtifactID & artId, const SpellID & spellId)
{
return new CArtifactInstance();
}
CArtifactInstance * CMap::createArtifact(const ArtifactID & artID, const SpellID & spellId)
{
if(!artID.hasValue())
return new CArtifactInstance(); // random, empty //TODO: make this illegal & remove?
auto art = artID.toArtifact();
auto artInst = new CArtifactInstance(art);
if(art->isCombined() && !art->isFused())
{
for(const auto & part : art->getConstituents())
artInst->addPart(createArtifact(part->getId(), spellId), ArtifactPosition::PRE_FIRST);
}
if(art->isGrowing())
{
auto bonus = std::make_shared<Bonus>();
bonus->type = BonusType::LEVEL_COUNTER;
bonus->val = 0;
artInst->addNewBonus(bonus);
}
if(art->isScroll())
{
artInst->addNewBonus(std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::SPELL,
BonusSource::ARTIFACT_INSTANCE, -1, BonusSourceID(ArtifactID(ArtifactID::SPELL_SCROLL)), BonusSubtypeID(spellId)));
}
return artInst;
}
CArtifactInstance * CMap::getArtifactInstance(const ArtifactInstanceID & artifactID)
{
return artInstances.at(artifactID.getNum()).get();
}
const CArtifactInstance * CMap::getArtifactInstance(const ArtifactInstanceID & artifactID) const
{
return artInstances.at(artifactID.getNum()).get();
}
VCMI_LIB_NAMESPACE_END

View File

@ -64,6 +64,8 @@ class DLL_LINKAGE CMap : public CMapHeader, public GameCallbackHolder
std::unique_ptr<GameSettings> gameSettings;
std::vector< std::shared_ptr<CQuest> > quests;
std::vector< std::shared_ptr<CArtifactInstance> > artInstances;
public:
explicit CMap(IGameCallback *cb);
~CMap();
@ -83,11 +85,16 @@ public:
void removeBlockVisTiles(CGObjectInstance * obj, bool total = false);
void calculateGuardingGreaturePositions();
void addNewArtifactInstance(CArtifactSet & artSet);
void addNewArtifactInstance(ConstTransitivePtr<CArtifactInstance> art);
void eraseArtifactInstance(CArtifactInstance * art);
CArtifactInstance * createScroll(const SpellID & spellId);
CArtifactInstance * createArtifact(const ArtifactID & artId, const SpellID & spellId = SpellID::NONE);
CArtifactInstance * createSingleArtifact(const ArtifactID & artId, const SpellID & spellId = SpellID::NONE);
CArtifactInstance * getArtifactInstance(const ArtifactInstanceID & artifactID);
const CArtifactInstance * getArtifactInstance(const ArtifactInstanceID & artifactID) const;
void eraseArtifactInstance(const ArtifactInstanceID art);
void moveArtifactInstance(CArtifactSet & srcSet, const ArtifactPosition & srcSlot, CArtifactSet & dstSet, const ArtifactPosition & dstSlot);
void putArtifactInstance(CArtifactSet & set, CArtifactInstance * art, const ArtifactPosition & slot);
void putArtifactInstance(CArtifactSet & set, const ArtifactInstanceID art, const ArtifactPosition & slot);
void removeArtifactInstance(CArtifactSet & set, const ArtifactPosition & slot);
void addNewQuestInstance(std::shared_ptr<CQuest> quest);
@ -134,7 +141,6 @@ public:
//Central lists of items in game. Position of item in the vectors below is their (instance) id.
std::vector< ConstTransitivePtr<CGObjectInstance> > objects;
std::vector< ConstTransitivePtr<CGTownInstance> > towns;
std::vector< ConstTransitivePtr<CArtifactInstance> > artInstances;
std::vector< ConstTransitivePtr<CGHeroInstance> > allHeroes; //indexed by [hero_type_id]; on map, disposed, prisons, etc.
//Helper lists

View File

@ -1006,9 +1006,8 @@ bool CMapLoaderH3M::loadArtifactToSlot(CGHeroInstance * hero, int slot)
// Artifact seems to be missing in game, so skip artifacts that don't fit target slot
if(ArtifactID(artifactID).toArtifact()->canBePutAt(hero, ArtifactPosition(slot)))
{
auto * artifact = ArtifactUtils::createArtifact(artifactID, scrollSpell);
map->putArtifactInstance(*hero, artifact, slot);
map->addNewArtifactInstance(artifact);
auto * artifact = map->createArtifact(artifactID, scrollSpell);
map->putArtifactInstance(*hero, artifact->getId(), slot);
}
else
{
@ -1407,8 +1406,7 @@ CGObjectInstance * CMapLoaderH3M::readArtifact(const int3 & mapPosition, std::sh
logGlobal->warn("Map '%s': Artifact %s: not implemented pickup mode %d (flags: %d)", mapName, mapPosition.toString(), pickupMode, static_cast<int>(pickupFlags));
}
object->storedArtifact = ArtifactUtils::createArtifact(artID, SpellID::NONE);
map->addNewArtifactInstance(object->storedArtifact);
object->storedArtifact = map->createArtifact(artID, SpellID::NONE);
return object;
}
@ -1418,8 +1416,7 @@ CGObjectInstance * CMapLoaderH3M::readScroll(const int3 & mapPosition, std::shar
readMessageAndGuards(object->message, object, mapPosition);
SpellID spellID = reader->readSpell32();
object->storedArtifact = ArtifactUtils::createArtifact(ArtifactID::SPELL_SCROLL, spellID.getNum());
map->addNewArtifactInstance(object->storedArtifact);
object->storedArtifact = map->createArtifact(ArtifactID::SPELL_SCROLL, spellID.getNum());
return object;
}

View File

@ -1105,15 +1105,13 @@ void CMapLoaderJson::MapObjectLoader::configure()
artID = art->getArtifact();
}
art->storedArtifact = ArtifactUtils::createArtifact(artID, spellID.getNum());
owner->map->addNewArtifactInstance(art->storedArtifact);
art->storedArtifact = owner->map->createArtifact(artID, spellID.getNum());
}
if(auto * hero = dynamic_cast<CGHeroInstance *>(instance))
{
auto o = handler.enterStruct("options");
hero->serializeJsonArtifacts(handler, "artifacts");
owner->map->addNewArtifactInstance(*hero);
hero->serializeJsonArtifacts(handler, "artifacts", owner->map);
}
}

View File

@ -1513,8 +1513,7 @@ void NewObject::applyGs(CGameState *gs)
void NewArtifact::applyGs(CGameState *gs)
{
auto art = ArtifactUtils::createArtifact(artId, spellId);
gs->getMap().addNewArtifactInstance(art);
auto art = gs->createArtifact(artId, spellId);
PutArtifact pa(art->getId(), ArtifactLocation(artHolder, pos), false);
pa.applyGs(gs);
}
@ -1732,7 +1731,7 @@ void PutArtifact::applyGs(CGameState *gs)
assert(hero);
assert(art && art->canBePutAt(hero, al.slot));
assert(ArtifactUtils::checkIfSlotValid(*hero, al.slot));
gs->getMap().putArtifactInstance(*hero, art, al.slot);
gs->getMap().putArtifactInstance(*hero, art->getId(), al.slot);
}
void BulkEraseArtifacts::applyGs(CGameState *gs)
@ -1797,7 +1796,7 @@ void BulkMoveArtifacts::applyGs(CGameState *gs)
{
auto * art = initArtSet.getArt(slotsPair.srcPos);
assert(art);
gs->getMap().putArtifactInstance(dstArtSet, art, slotsPair.dstPos);
gs->getMap().putArtifactInstance(dstArtSet, art->getId(), slotsPair.dstPos);
}
};
@ -1828,8 +1827,7 @@ void AssembledArtifact::applyGs(CGameState *gs)
return art->getId() == builtArt->getId();
}));
auto * combinedArt = new CArtifactInstance(builtArt);
gs->getMap().addNewArtifactInstance(combinedArt);
auto * combinedArt = gs->getMap().createSingleArtifact(artId);
// Find slots for all involved artifacts
std::set<ArtifactPosition, std::greater<>> slotsInvolved = { al.slot };
@ -1882,14 +1880,15 @@ void AssembledArtifact::applyGs(CGameState *gs)
}
// Put new combined artifacts
gs->getMap().putArtifactInstance(*artSet, combinedArt, al.slot);
gs->getMap().putArtifactInstance(*artSet, combinedArt->getId(), al.slot);
}
void DisassembledArtifact::applyGs(CGameState *gs)
{
auto hero = gs->getHero(al.artHolder);
assert(hero);
auto disassembledArt = hero->getArt(al.slot);
auto disassembledArtID = hero->getArtID(al.slot);
auto disassembledArt = gs->getArtInstance(disassembledArtID);
assert(disassembledArt);
const auto parts = disassembledArt->getPartsInfo();
@ -1898,10 +1897,10 @@ void DisassembledArtifact::applyGs(CGameState *gs)
{
// ArtifactPosition::PRE_FIRST is value of main part slot -> it'll replace combined artifact in its pos
auto slot = (ArtifactUtils::isSlotEquipment(part.slot) ? part.slot : al.slot);
disassembledArt->detachFrom(*part.art);
gs->getMap().putArtifactInstance(*hero, part.art, slot);
disassembledArt->detachFromSource(*part.art);
gs->getMap().putArtifactInstance(*hero, part.art->getId(), slot);
}
gs->getMap().eraseArtifactInstance(disassembledArt);
gs->getMap().eraseArtifactInstance(disassembledArt->getId());
}
void HeroVisit::applyGs(CGameState *gs)
@ -2130,12 +2129,10 @@ void BattleResultAccepted::applyGs(CGameState *gs)
if(winnerHero->getCommander() && winnerHero->getCommander()->alive)
{
for(auto & art : winnerHero->getCommander()->artifactsWorn)
art.second.artifact->growingUp();
gs->getArtInstance(art.second.getID())->growingUp();
}
for(auto & art : winnerHero->artifactsWorn)
{
art.second.artifact->growingUp();
}
gs->getArtInstance(art.second.getID())->growingUp();
}
}
@ -2503,4 +2500,11 @@ const CArtifactInstance * ArtSlotInfo::getArt() const
return artifact;
}
ArtifactInstanceID ArtSlotInfo::getID() const
{
if(locked || artifact == nullptr)
return {};
return artifact->getId();
}
VCMI_LIB_NAMESPACE_END

View File

@ -278,7 +278,7 @@ void TreasurePlacer::addScrolls()
if(map.isAllowedSpell(spellID) && spellID.toSpell()->getLevel() == i + 1)
out.push_back(spellID);
}
auto * a = ArtifactUtils::createScroll(*RandomGeneratorUtil::nextItem(out, zone.getRand()));
auto * a = map.mapInstance->createScroll(*RandomGeneratorUtil::nextItem(out, zone.getRand()));
obj->storedArtifact = a;
return obj;
};

View File

@ -330,7 +330,7 @@ public:
{
// This pointers is already loaded. The "data" needs to be pointed to it,
// so their shared state is actually shared.
data = std::static_pointer_cast<T>(itr->second);
data = std::dynamic_pointer_cast<T>(itr->second);
}
else
{

View File

@ -27,8 +27,8 @@ void CSerializer::addStdVecItems(CGameState *gs, GameLibrary *lib)
[](const CGObjectInstance &obj){ return obj.id; });
registerVectoredType<CGHeroInstance, HeroTypeID>(&gs->getMap().allHeroes,
[](const CGHeroInstance &h){ return h.getHeroType()->getId(); });
registerVectoredType<CArtifactInstance, ArtifactInstanceID>(&gs->getMap().artInstances,
[](const CArtifactInstance &artInst){ return artInst.getId(); });
// registerVectoredType<CArtifactInstance, ArtifactInstanceID>(&gs->getMap().artInstances,
// [](const CArtifactInstance &artInst){ return artInst.getId(); });
// TODO
// registerVectoredType<CQuest, si32>(&gs->getMap().quests,
// [](const CQuest &q){ return q.qid; });

View File

@ -13,12 +13,16 @@
#include "ui_heroartifactswidget.h"
#include "inspector.h"
#include "mapeditorroles.h"
#include "../mapcontroller.h"
#include "../../lib/ArtifactUtils.h"
#include "../../lib/constants/StringConstants.h"
#include "../../lib/mapping/CMap.h"
HeroArtifactsWidget::HeroArtifactsWidget(CGHeroInstance & h, QWidget * parent) :
HeroArtifactsWidget::HeroArtifactsWidget(MapController & controller, CGHeroInstance & h, QWidget * parent) :
QDialog(parent),
ui(new Ui::HeroArtifactsWidget),
controller(controller),
hero(h),
fittingSet(CArtifactFittingSet(h))
{
@ -52,7 +56,7 @@ void HeroArtifactsWidget::on_removeButton_clicked()
void HeroArtifactsWidget::onSaveArtifact(int32_t artifactIndex, ArtifactPosition slot)
{
auto artifact = ArtifactUtils::createArtifact(LIBRARY->arth->getByIndex(artifactIndex)->getId());
auto artifact = controller.map()->createArtifact(LIBRARY->arth->getByIndex(artifactIndex)->getId());
fittingSet.putArtifact(slot, artifact);
addArtifactToTable(artifactIndex, slot);
}
@ -110,13 +114,16 @@ void HeroArtifactsWidget::commitChanges()
}
}
HeroArtifactsDelegate::HeroArtifactsDelegate(CGHeroInstance & h) : BaseInspectorItemDelegate(), hero(h)
HeroArtifactsDelegate::HeroArtifactsDelegate(MapController & controller, CGHeroInstance & h)
: BaseInspectorItemDelegate()
, controller(controller)
, hero(h)
{
}
QWidget * HeroArtifactsDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
return new HeroArtifactsWidget(hero, parent);
return new HeroArtifactsWidget(controller, hero, parent);
}
void HeroArtifactsDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const

View File

@ -17,12 +17,14 @@ namespace Ui {
class HeroArtifactsWidget;
}
class MapController;
class HeroArtifactsWidget : public QDialog
{
Q_OBJECT
public:
explicit HeroArtifactsWidget(CGHeroInstance &, QWidget *parent = nullptr);
explicit HeroArtifactsWidget(MapController & controller, CGHeroInstance &, QWidget *parent = nullptr);
~HeroArtifactsWidget();
void obtainData();
@ -42,6 +44,7 @@ private:
};
Ui::HeroArtifactsWidget * ui;
MapController & controller;
CGHeroInstance & hero;
CArtifactFittingSet fittingSet;
@ -55,7 +58,7 @@ class HeroArtifactsDelegate : public BaseInspectorItemDelegate
public:
using BaseInspectorItemDelegate::BaseInspectorItemDelegate;
HeroArtifactsDelegate(CGHeroInstance &);
HeroArtifactsDelegate(MapController & controller, CGHeroInstance &);
QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
void setEditorData(QWidget * editor, const QModelIndex & index) const override;
@ -63,5 +66,6 @@ public:
void updateModelData(QAbstractItemModel * model, const QModelIndex & index) const override;
private:
MapController & controller;
CGHeroInstance & hero;
};

View File

@ -38,7 +38,9 @@
#include "../mapcontroller.h"
//===============IMPLEMENT OBJECT INITIALIZATION FUNCTIONS================
Initializer::Initializer(CGObjectInstance * o, const PlayerColor & pl) : defaultPlayer(pl)
Initializer::Initializer(MapController & controller, CGObjectInstance * o, const PlayerColor & pl)
: defaultPlayer(pl)
, controller(controller)
{
logGlobal->info("New object instance initialized");
///IMPORTANT! initialize order should be from base objects to derived objects
@ -203,7 +205,7 @@ void Initializer::initialize(CGArtifact * o)
out.push_back(spell->id);
}
}
auto a = ArtifactUtils::createScroll(*RandomGeneratorUtil::nextItem(out, CRandomGenerator::getDefault()));
auto a = controller.map()->createScroll(*RandomGeneratorUtil::nextItem(out, CRandomGenerator::getDefault()));
o->storedArtifact = a;
}
}
@ -320,7 +322,7 @@ void Inspector::updateProperties(CGHeroInstance * o)
auto * delegate = new HeroSkillsDelegate(*o);
addProperty(QObject::tr("Skills"), PropertyEditorPlaceholder(), delegate, false);
addProperty(QObject::tr("Spells"), PropertyEditorPlaceholder(), new HeroSpellDelegate(*o), false);
addProperty(QObject::tr("Artifacts"), PropertyEditorPlaceholder(), new HeroArtifactsDelegate(*o), false);
addProperty(QObject::tr("Artifacts"), PropertyEditorPlaceholder(), new HeroArtifactsDelegate(controller, *o), false);
if(o->getHeroTypeID().hasValue() || o->ID == Obj::PRISON)
{ //Hero type
@ -640,7 +642,7 @@ void Inspector::setProperty(CGArtifact * o, const QString & key, const QVariant
if(o->storedArtifact && key == QObject::tr("Spell"))
{
o->storedArtifact = ArtifactUtils::createScroll(SpellID(value.toInt()));
o->storedArtifact = controller.map()->createScroll(SpellID(value.toInt()));
}
}

View File

@ -57,9 +57,10 @@ public:
//DECLARE_OBJ_TYPE(CGPandoraBox);
//DECLARE_OBJ_TYPE(CGSeerHut);
Initializer(CGObjectInstance *, const PlayerColor &);
Initializer(MapController & controller, CGObjectInstance *, const PlayerColor &);
private:
MapController & controller;
PlayerColor defaultPlayer;
};

View File

@ -144,7 +144,7 @@ void MapController::repairMap(CMap * map)
{
nih->removeSpellFromSpellbook(SpellID::SPELLBOOK_PRESET);
if(!nih->getArt(ArtifactPosition::SPELLBOOK) && type->haveSpellBook)
nih->putArtifact(ArtifactPosition::SPELLBOOK, ArtifactUtils::createArtifact(ArtifactID::SPELLBOOK));
nih->putArtifact(ArtifactPosition::SPELLBOOK, map->createArtifact(ArtifactID::SPELLBOOK));
}
}
@ -177,7 +177,7 @@ void MapController::repairMap(CMap * map)
out.push_back(spell->id);
}
}
auto a = ArtifactUtils::createScroll(*RandomGeneratorUtil::nextItem(out, CRandomGenerator::getDefault()));
auto a = map->createScroll(*RandomGeneratorUtil::nextItem(out, CRandomGenerator::getDefault()));
art->storedArtifact = a;
}
}
@ -376,7 +376,7 @@ void MapController::pasteFromClipboard(int level)
obj->pos = newPos;
obj->pos.z = level;
Initializer init(obj, defaultPlayer);
Initializer init(*this, obj, defaultPlayer);
_map->getEditManager()->insertObject(obj);
_scenes[level]->selectionObjectsView.selectObject(obj);
_mapHandler->invalidate(obj);
@ -521,7 +521,7 @@ void MapController::commitObjectCreate(int level)
newObj->pos = pos;
Initializer init(newObj, defaultPlayer);
Initializer init(*this, newObj, defaultPlayer);
_map->getEditManager()->insertObject(newObj);
_mapHandler->invalidate(newObj);