2025-04-29 12:10:19 +03:00
|
|
|
/*
|
|
|
|
* CArtifactSet.cpp, part of VCMI engine
|
|
|
|
*
|
|
|
|
* Authors: listed in file AUTHORS in main folder
|
|
|
|
*
|
|
|
|
* License: GNU General Public License v2.0 or later
|
|
|
|
* Full text of license available in license.txt file, in main folder
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "StdInc.h"
|
|
|
|
#include "CArtifactSet.h"
|
|
|
|
|
|
|
|
#include "CArtifact.h"
|
|
|
|
#include "ArtifactUtils.h"
|
|
|
|
|
|
|
|
#include "../../constants/StringConstants.h"
|
|
|
|
#include "../../gameState/CGameState.h"
|
|
|
|
#include "../../mapping/CMap.h"
|
|
|
|
#include "../../serializer/JsonSerializeFormat.h"
|
|
|
|
|
|
|
|
VCMI_LIB_NAMESPACE_BEGIN
|
|
|
|
|
|
|
|
const CArtifactInstance * CArtifactSet::getArt(const ArtifactPosition & pos, bool excludeLocked) const
|
|
|
|
{
|
|
|
|
if(const ArtSlotInfo * si = getSlot(pos))
|
|
|
|
{
|
|
|
|
if(si->getArt() && (!excludeLocked || !si->locked))
|
|
|
|
return si->getArt();
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
ArtifactInstanceID CArtifactSet::getArtID(const ArtifactPosition & pos, bool excludeLocked) const
|
|
|
|
{
|
|
|
|
if(const ArtSlotInfo * si = getSlot(pos))
|
|
|
|
{
|
|
|
|
if(si->getArt() && (!excludeLocked || !si->locked))
|
|
|
|
return si->getArt()->getId();
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
ArtifactPosition CArtifactSet::getArtPos(const ArtifactID & aid, bool onlyWorn, bool allowLocked) const
|
|
|
|
{
|
|
|
|
for(const auto & [slot, slotInfo] : artifactsWorn)
|
|
|
|
{
|
|
|
|
if(slotInfo.getArt()->getTypeId() == aid && (allowLocked || !slotInfo.locked))
|
|
|
|
return slot;
|
|
|
|
}
|
|
|
|
if(!onlyWorn)
|
|
|
|
{
|
|
|
|
size_t backpackPositionIdx = ArtifactPosition::BACKPACK_START;
|
|
|
|
for(const auto & artInfo : artifactsInBackpack)
|
|
|
|
{
|
|
|
|
const auto art = artInfo.getArt();
|
|
|
|
if(art && art->getType()->getId() == aid)
|
|
|
|
return ArtifactPosition(backpackPositionIdx);
|
|
|
|
backpackPositionIdx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ArtifactPosition::PRE_FIRST;
|
|
|
|
}
|
|
|
|
|
2025-05-06 16:00:21 +03:00
|
|
|
bool CArtifactSet::hasScroll(const SpellID & spellID, bool onlyWorn) const
|
|
|
|
{
|
|
|
|
return getScrollPos(spellID, onlyWorn) != ArtifactPosition::PRE_FIRST;
|
|
|
|
}
|
|
|
|
|
|
|
|
ArtifactPosition CArtifactSet::getScrollPos(const SpellID & spellID, bool onlyWorn) const
|
|
|
|
{
|
|
|
|
for (const auto & slot : artifactsWorn)
|
|
|
|
if (slot.second.getArt() && slot.second.getArt()->isScroll() && slot.second.getArt()->getScrollSpellID() == spellID)
|
|
|
|
return slot.first;
|
|
|
|
|
|
|
|
if (!onlyWorn)
|
|
|
|
{
|
|
|
|
size_t backpackPositionIdx = ArtifactPosition::BACKPACK_START;
|
|
|
|
|
|
|
|
for (const auto & slot : artifactsInBackpack)
|
|
|
|
{
|
|
|
|
if (slot.getArt() && slot.getArt()->isScroll() && slot.getArt()->getScrollSpellID() == spellID)
|
|
|
|
return ArtifactPosition(backpackPositionIdx);
|
|
|
|
backpackPositionIdx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ArtifactPosition::PRE_FIRST;
|
|
|
|
}
|
|
|
|
|
2025-04-29 12:10:19 +03:00
|
|
|
const CArtifactInstance * CArtifactSet::getArtByInstanceId(const ArtifactInstanceID & artInstId) const
|
|
|
|
{
|
|
|
|
for(const auto & i : artifactsWorn)
|
|
|
|
if(i.second.getArt()->getId() == artInstId)
|
|
|
|
return i.second.getArt();
|
|
|
|
|
|
|
|
for(const auto & i : artifactsInBackpack)
|
|
|
|
if(i.getArt()->getId() == artInstId)
|
|
|
|
return i.getArt();
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
ArtifactPosition CArtifactSet::getArtPos(const CArtifactInstance * artInst) const
|
|
|
|
{
|
|
|
|
if(artInst)
|
|
|
|
{
|
|
|
|
for(const auto & slot : artInst->getType()->getPossibleSlots().at(bearerType()))
|
|
|
|
if(getArt(slot) == artInst)
|
|
|
|
return slot;
|
|
|
|
|
|
|
|
ArtifactPosition backpackSlot = ArtifactPosition::BACKPACK_START;
|
|
|
|
for(auto & slotInfo : artifactsInBackpack)
|
|
|
|
{
|
|
|
|
if(slotInfo.getArt() == artInst)
|
|
|
|
return backpackSlot;
|
|
|
|
backpackSlot = ArtifactPosition(backpackSlot + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ArtifactPosition::PRE_FIRST;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CArtifactSet::hasArt(const ArtifactID & aid, bool onlyWorn, bool searchCombinedParts) const
|
|
|
|
{
|
|
|
|
if(searchCombinedParts && getCombinedArtWithPart(aid))
|
|
|
|
return true;
|
|
|
|
if(getArtPos(aid, onlyWorn, searchCombinedParts) != ArtifactPosition::PRE_FIRST)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CArtifactSet::ArtPlacementMap CArtifactSet::putArtifact(const ArtifactPosition & slot, const CArtifactInstance * art)
|
|
|
|
{
|
|
|
|
ArtPlacementMap resArtPlacement;
|
|
|
|
const auto putToSlot = [this](const ArtifactPosition & targetSlot, const CArtifactInstance * targetArt, bool locked)
|
|
|
|
{
|
|
|
|
ArtSlotInfo newSlot(targetArt, locked);
|
|
|
|
|
|
|
|
if(targetSlot == ArtifactPosition::TRANSITION_POS)
|
|
|
|
{
|
|
|
|
artifactsTransitionPos = newSlot;
|
|
|
|
}
|
|
|
|
else if(ArtifactUtils::isSlotEquipment(targetSlot))
|
|
|
|
{
|
|
|
|
artifactsWorn.insert_or_assign(targetSlot, newSlot);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto position = artifactsInBackpack.begin() + targetSlot - ArtifactPosition::BACKPACK_START;
|
|
|
|
artifactsInBackpack.insert(position, newSlot);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
putToSlot(slot, art, false);
|
|
|
|
if(art->getType()->isCombined() && ArtifactUtils::isSlotEquipment(slot))
|
|
|
|
{
|
|
|
|
const CArtifactInstance * mainPart = nullptr;
|
|
|
|
for(const auto & part : art->getPartsInfo())
|
|
|
|
if(vstd::contains(part.getArtifact()->getType()->getPossibleSlots().at(bearerType()), slot)
|
|
|
|
&& (part.slot == ArtifactPosition::PRE_FIRST))
|
|
|
|
{
|
|
|
|
mainPart = part.getArtifact();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(const auto & part : art->getPartsInfo())
|
|
|
|
{
|
|
|
|
if(part.getArtifact() != mainPart)
|
|
|
|
{
|
|
|
|
auto partSlot = part.slot;
|
|
|
|
if(!part.getArtifact()->getType()->canBePutAt(this, partSlot))
|
|
|
|
partSlot = ArtifactUtils::getArtAnyPosition(this, part.getArtifact()->getTypeId());
|
|
|
|
|
|
|
|
assert(ArtifactUtils::isSlotEquipment(partSlot));
|
|
|
|
putToSlot(partSlot, part.getArtifact(), true);
|
|
|
|
resArtPlacement.emplace(part.getArtifact(), partSlot);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
resArtPlacement.emplace(part.getArtifact(), part.slot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return resArtPlacement;
|
|
|
|
}
|
|
|
|
|
|
|
|
CArtifactSet::CArtifactSet(IGameCallback * cb)
|
|
|
|
:artifactsTransitionPos(cb)
|
|
|
|
{}
|
|
|
|
|
|
|
|
void CArtifactSet::removeArtifact(const ArtifactPosition & slot)
|
|
|
|
{
|
|
|
|
const auto eraseArtSlot = [this](const ArtifactPosition & slotForErase)
|
|
|
|
{
|
|
|
|
if(slotForErase == ArtifactPosition::TRANSITION_POS)
|
|
|
|
{
|
|
|
|
artifactsTransitionPos.artifactID = {};
|
|
|
|
}
|
|
|
|
else if(ArtifactUtils::isSlotBackpack(slotForErase))
|
|
|
|
{
|
|
|
|
auto backpackSlot = ArtifactPosition(slotForErase - ArtifactPosition::BACKPACK_START);
|
|
|
|
|
|
|
|
assert(artifactsInBackpack.begin() + backpackSlot < artifactsInBackpack.end());
|
|
|
|
artifactsInBackpack.erase(artifactsInBackpack.begin() + backpackSlot);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
artifactsWorn.erase(slotForErase);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if(const auto art = getArt(slot, false))
|
|
|
|
{
|
|
|
|
if(art->isCombined())
|
|
|
|
{
|
|
|
|
for(const auto & part : art->getPartsInfo())
|
|
|
|
{
|
|
|
|
if(part.slot != ArtifactPosition::PRE_FIRST)
|
|
|
|
{
|
|
|
|
assert(getArt(part.slot, false));
|
|
|
|
assert(getArt(part.slot, false) == part.getArtifact());
|
|
|
|
}
|
|
|
|
eraseArtSlot(part.slot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
eraseArtSlot(slot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const CArtifactInstance * CArtifactSet::getCombinedArtWithPart(const ArtifactID & partId) const
|
|
|
|
{
|
|
|
|
for(const auto & slot : artifactsInBackpack)
|
|
|
|
{
|
|
|
|
auto art = slot.getArt();
|
|
|
|
if(art->isCombined())
|
|
|
|
{
|
|
|
|
for(auto & ci : art->getPartsInfo())
|
|
|
|
{
|
|
|
|
if(ci.getArtifact()->getTypeId() == partId)
|
|
|
|
return art;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ArtSlotInfo * CArtifactSet::getSlot(const ArtifactPosition & pos) const
|
|
|
|
{
|
|
|
|
if(pos == ArtifactPosition::TRANSITION_POS)
|
|
|
|
return &artifactsTransitionPos;
|
|
|
|
if(vstd::contains(artifactsWorn, pos))
|
|
|
|
return &artifactsWorn.at(pos);
|
|
|
|
if(ArtifactUtils::isSlotBackpack(pos))
|
|
|
|
{
|
|
|
|
auto backpackPos = pos - ArtifactPosition::BACKPACK_START;
|
|
|
|
if(backpackPos < 0 || backpackPos >= artifactsInBackpack.size())
|
|
|
|
return nullptr;
|
|
|
|
else
|
|
|
|
return &artifactsInBackpack[backpackPos];
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CArtifactSet::lockSlot(const ArtifactPosition & pos)
|
|
|
|
{
|
|
|
|
if(pos == ArtifactPosition::TRANSITION_POS)
|
|
|
|
artifactsTransitionPos.locked = true;
|
|
|
|
else if(ArtifactUtils::isSlotEquipment(pos))
|
|
|
|
artifactsWorn.at(pos).locked = true;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
assert(artifactsInBackpack.size() > pos - ArtifactPosition::BACKPACK_START);
|
|
|
|
(artifactsInBackpack.begin() + pos - ArtifactPosition::BACKPACK_START)->locked = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CArtifactSet::isPositionFree(const ArtifactPosition & pos, bool onlyLockCheck) const
|
|
|
|
{
|
|
|
|
if(bearerType() == ArtBearer::ALTAR)
|
|
|
|
return artifactsInBackpack.size() < GameConstants::ALTAR_ARTIFACTS_SLOTS;
|
|
|
|
|
|
|
|
if(const ArtSlotInfo *s = getSlot(pos))
|
|
|
|
return (onlyLockCheck || !s->getArt()) && !s->locked;
|
|
|
|
|
|
|
|
return true; //no slot means not used
|
|
|
|
}
|
|
|
|
|
|
|
|
void CArtifactSet::artDeserializationFix(CGameState & gs, CBonusSystemNode *node)
|
|
|
|
{
|
|
|
|
for(auto & elem : artifactsWorn)
|
|
|
|
if(elem.second.artifactID.hasValue() && !elem.second.locked)
|
|
|
|
node->attachToSource(*gs.getArtInstance(elem.second.artifactID));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CArtifactSet::serializeJsonArtifacts(JsonSerializeFormat & handler, const std::string & fieldName, CMap * map)
|
|
|
|
{
|
|
|
|
//todo: creature and commander artifacts
|
|
|
|
if(handler.saving && artifactsInBackpack.empty() && artifactsWorn.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(!handler.saving)
|
|
|
|
{
|
|
|
|
artifactsInBackpack.clear();
|
|
|
|
artifactsWorn.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto s = handler.enterStruct(fieldName);
|
|
|
|
|
|
|
|
switch(bearerType())
|
|
|
|
{
|
|
|
|
case ArtBearer::HERO:
|
|
|
|
serializeJsonHero(handler, map);
|
|
|
|
break;
|
|
|
|
case ArtBearer::CREATURE:
|
|
|
|
serializeJsonCreature(handler);
|
|
|
|
break;
|
|
|
|
case ArtBearer::COMMANDER:
|
|
|
|
serializeJsonCommander(handler);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CArtifactSet::serializeJsonHero(JsonSerializeFormat & handler, CMap * map)
|
|
|
|
{
|
|
|
|
for(const auto & slot : ArtifactUtils::allWornSlots())
|
|
|
|
{
|
|
|
|
serializeJsonSlot(handler, slot, map);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<ArtifactID> backpackTemp;
|
|
|
|
|
|
|
|
if(handler.saving)
|
|
|
|
{
|
|
|
|
backpackTemp.reserve(artifactsInBackpack.size());
|
|
|
|
for(const ArtSlotInfo & info : artifactsInBackpack)
|
|
|
|
backpackTemp.push_back(info.getArt()->getTypeId());
|
|
|
|
}
|
|
|
|
handler.serializeIdArray(NArtifactPosition::backpack, backpackTemp);
|
|
|
|
if(!handler.saving)
|
|
|
|
{
|
|
|
|
for(const ArtifactID & artifactID : backpackTemp)
|
|
|
|
{
|
|
|
|
auto * artifact = map->createArtifact(artifactID);
|
|
|
|
auto slot = ArtifactPosition::BACKPACK_START + artifactsInBackpack.size();
|
|
|
|
if(artifact->getType()->canBePutAt(this, slot))
|
|
|
|
{
|
|
|
|
auto artsMap = putArtifact(slot, artifact);
|
|
|
|
artifact->addPlacementMap(artsMap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CArtifactSet::serializeJsonCreature(JsonSerializeFormat & handler)
|
|
|
|
{
|
|
|
|
logGlobal->error("CArtifactSet::serializeJsonCreature not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
void CArtifactSet::serializeJsonCommander(JsonSerializeFormat & handler)
|
|
|
|
{
|
|
|
|
logGlobal->error("CArtifactSet::serializeJsonCommander not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
void CArtifactSet::serializeJsonSlot(JsonSerializeFormat & handler, const ArtifactPosition & slot, CMap * map)
|
|
|
|
{
|
|
|
|
ArtifactID artifactID;
|
|
|
|
|
|
|
|
if(handler.saving)
|
|
|
|
{
|
|
|
|
const ArtSlotInfo * info = getSlot(slot);
|
|
|
|
|
|
|
|
if(info != nullptr && !info->locked)
|
|
|
|
{
|
|
|
|
artifactID = info->getArt()->getTypeId();
|
|
|
|
handler.serializeId(NArtifactPosition::namesHero[slot.num], artifactID, ArtifactID::NONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
handler.serializeId(NArtifactPosition::namesHero[slot.num], artifactID, ArtifactID::NONE);
|
|
|
|
|
|
|
|
if(artifactID != ArtifactID::NONE)
|
|
|
|
{
|
|
|
|
auto * artifact = map->createArtifact(artifactID.toEnum());
|
|
|
|
|
|
|
|
if(artifact->getType()->canBePutAt(this, slot))
|
|
|
|
{
|
|
|
|
auto artsMap = putArtifact(slot, artifact);
|
|
|
|
artifact->addPlacementMap(artsMap);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
logGlobal->debug("Artifact can't be put at the specified location."); //TODO add more debugging information
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VCMI_LIB_NAMESPACE_END
|