diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index 21619e41e..06c3f8afa 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -633,7 +633,7 @@ CArtifactsOfHero::~CArtifactsOfHero() if(!curHero->artifactsTransitionPos.empty()) { auto artPlace = getArtPlace( - ArtifactUtils::getArtifactDstPosition(curHero->artifactsTransitionPos.begin()->artifact, curHero)); + ArtifactUtils::getArtifactDstPosition(curHero->artifactsTransitionPos.begin()->artifact->artType->getId(), curHero)); assert(artPlace); assert(artPlace->ourOwner); artPlace->setMeAsDest(); diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 5da9c4941..7a3e0c1fd 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -805,7 +805,7 @@ void CCastleBuildings::enterBlacksmith(ArtifactID artifactID) LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % town->town->buildings.find(BuildingID::BLACKSMITH)->second->getNameTranslated())); return; } - auto art = dynamic_cast(artifactID.toArtifact(CGI->artifacts())); + auto art = artifactID.toArtifact(); int price = art->getPrice(); bool possible = LOCPLINT->cb->getResourceAmount(Res::GOLD) >= price; diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 42cdefd0b..331dace9c 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -843,7 +843,7 @@ void CExchangeController::moveArtifact( { auto srcLocation = ArtifactLocation(source, srcPosition); auto dstLocation = ArtifactLocation(target, - ArtifactUtils::getArtifactDstPosition(source->getArt(srcPosition), target)); + ArtifactUtils::getArtifactDstPosition(source->getArt(srcPosition)->artType->getId(), target)); cb->swapArtifacts(srcLocation, dstLocation); } diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index a0eefbeef..a215745bd 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -139,6 +139,71 @@ bool CArtifact::isTradable() const } } +bool CArtifact::canBeDisassembled() const +{ + return !(constituents == nullptr); +} + +bool CArtifact::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved) const +{ + if(slot == ArtifactPosition::TRANSITION_POS) + return true; + + auto simpleArtCanBePutAt = [this](const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved) -> bool + { + if(ArtifactUtils::isSlotBackpack(slot)) + { + if(isBig()) + return false; + + //TODO backpack limit + return true; + } + + auto bearerPossibleSlots = possibleSlots.at(artSet->bearerType()); + if(bearerPossibleSlots.empty()) + { + logMod->warn("Warning: artifact %s doesn't have defined allowed slots for bearer of type %s", getNameTranslated(), artSet->bearerType()); + return false; + } + if(!vstd::contains(bearerPossibleSlots, slot)) + return false; + + return artSet->isPositionFree(slot, assumeDestRemoved); + }; + + if(canBeDisassembled()) + { + if(!simpleArtCanBePutAt(artSet, slot, assumeDestRemoved)) + return false; + if(ArtifactUtils::isSlotBackpack(slot)) + return true; //TODO backpack limit + + CArtifactFittingSet fittingSet(artSet->bearerType()); + fittingSet.artifactsWorn = artSet->artifactsWorn; + if(assumeDestRemoved) + fittingSet.removeArtifact(slot); + assert(constituents); + for(const auto art : *constituents) + { + auto possibleSlot = ArtifactUtils::getArtifactDstPosition(art->getId(), &fittingSet); + if(ArtifactUtils::isSlotEquipment(possibleSlot)) + { + fittingSet.setNewArtSlot(possibleSlot, nullptr, true); + } + else + { + return false; + } + } + return true; + } + else + { + return simpleArtCanBePutAt(artSet, slot, assumeDestRemoved); + } +} + CArtifact::CArtifact() { setNodeType(ARTIFACT); @@ -800,15 +865,12 @@ std::string CArtifactInstance::getEffectiveDescription(const CGHeroInstance * he return text; } -ArtifactPosition CArtifactInstance::firstAvailableSlot(const CArtifactSet *h) const +ArtifactPosition CArtifactInstance::firstAvailableSlot(const CArtifactSet * h) const { for(const auto & slot : artType->possibleSlots.at(h->bearerType())) { - if(canBePutAt(h, slot)) //if(artType->fitsAt(h->artifWorn, slot)) - { - //we've found a free suitable slot. + if(artType->canBePutAt(h, slot)) return slot; - } } //if haven't find proper slot, use backpack @@ -825,36 +887,7 @@ ArtifactPosition CArtifactInstance::firstBackpackSlot(const CArtifactSet *h) con bool CArtifactInstance::canBePutAt(const ArtifactLocation & al, bool assumeDestRemoved) const { - return canBePutAt(al.getHolderArtSet(), al.slot, assumeDestRemoved); -} - -bool CArtifactInstance::canBePutAt(const CArtifactSet *artSet, ArtifactPosition slot, bool assumeDestRemoved) const -{ - if(slot == ArtifactPosition::TRANSITION_POS) - { - return true; - } - - if(ArtifactUtils::isSlotBackpack(slot)) - { - if(artType->isBig()) - return false; - - //TODO backpack limit - return true; - } - - auto possibleSlots = artType->possibleSlots.find(artSet->bearerType()); - if(possibleSlots == artType->possibleSlots.end()) - { - logMod->warn("Warning: artifact %s doesn't have defined allowed slots for bearer of type %s", artType->getNameTranslated(), artSet->bearerType()); - return false; - } - - if(!vstd::contains(possibleSlots->second, slot)) - return false; - - return artSet->isPositionFree(slot, assumeDestRemoved); + return artType->canBePutAt(al.getHolderArtSet(), al.slot, assumeDestRemoved); } void CArtifactInstance::putAt(ArtifactLocation al) @@ -876,7 +909,7 @@ void CArtifactInstance::removeFrom(ArtifactLocation al) bool CArtifactInstance::canBeDisassembled() const { - return bool(artType->constituents); + return artType->canBeDisassembled(); } std::vector CArtifactInstance::assemblyPossibilities(const CArtifactSet * h, bool equipped) const @@ -1007,53 +1040,6 @@ bool CArtifactInstance::isPart(const CArtifactInstance *supposedPart) const return supposedPart == this; } -bool CCombinedArtifactInstance::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved) const -{ - if(slot == ArtifactPosition::TRANSITION_POS) - return true; - if(!CArtifactInstance::canBePutAt(artSet, slot, assumeDestRemoved)) - return false; - if(ArtifactUtils::isSlotBackpack(slot)) - return true; //we can always remove combined art to the backapck - - CArtifactFittingSet fittingSet(artSet->bearerType()); - fittingSet.artifactsWorn = artSet->artifactsWorn; - auto artToRemove = fittingSet.getArt(slot); - if(assumeDestRemoved && artToRemove) - { - if(artToRemove->canBeDisassembled()) - { - auto combinedArtToRemove = dynamic_cast(artToRemove); - for(auto & part : combinedArtToRemove->constituentsInfo) - { - if(ArtifactUtils::isSlotEquipment(part.slot)) - { - fittingSet.eraseArtSlot(part.slot); - } - } - } - fittingSet.eraseArtSlot(slot); - } - for(auto & art : constituentsInfo) - { - auto possibleSlot = art.art->firstAvailableSlot(&fittingSet); - if(ArtifactUtils::isSlotEquipment(possibleSlot)) - { - fittingSet.setNewArtSlot(possibleSlot, nullptr, true); - } - else - { - return false; - } - } - return true; -} - -bool CCombinedArtifactInstance::canBeDisassembled() const -{ - return true; -} - CCombinedArtifactInstance::CCombinedArtifactInstance(CArtifact *Art) : CArtifactInstance(Art) //TODO: seems unused, but need to be written { @@ -1478,8 +1464,8 @@ void CArtifactSet::serializeJsonHero(JsonSerializeFormat & handler, CMap * map) for(const ArtifactID & artifactID : backpackTemp) { auto * artifact = CArtifactInstance::createArtifact(map, artifactID.toEnum()); - auto slot = ArtifactPosition(GameConstants::BACKPACK_START + static_cast(artifactsInBackpack.size())); - if(artifact->canBePutAt(this, slot)) + auto slot = ArtifactPosition(GameConstants::BACKPACK_START + (si32)artifactsInBackpack.size()); + if(artifact->artType->canBePutAt(this, slot)) putArtifact(slot, artifact); } } @@ -1517,7 +1503,7 @@ void CArtifactSet::serializeJsonSlot(JsonSerializeFormat & handler, const Artifa { auto * artifact = CArtifactInstance::createArtifact(map, artifactID.toEnum()); - if(artifact->canBePutAt(this, slot)) + if(artifact->artType->canBePutAt(this, slot)) { putArtifact(slot, artifact); } @@ -1550,25 +1536,45 @@ void CArtifactFittingSet::putArtifact(ArtifactPosition pos, CArtifactInstance * } } +void CArtifactFittingSet::removeArtifact(ArtifactPosition pos) +{ + // Removes the art from the CartifactSet, but does not remove it from art->constituentsInfo. + // removeArtifact is for CArtifactFittingSet only. Do not move it to the parent class. + + auto art = getArt(pos); + if(art == nullptr) + return; + if(art->canBeDisassembled()) + { + auto combinedArt = dynamic_cast(art); + for(const auto & part : combinedArt->constituentsInfo) + { + if(ArtifactUtils::isSlotEquipment(part.slot)) + eraseArtSlot(part.slot); + } + } + eraseArtSlot(pos); +} + ArtBearer::ArtBearer CArtifactFittingSet::bearerType() const { return this->Bearer; } -DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtifactDstPosition(const CArtifactInstance * artifact, - const CArtifactSet * target) +DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtifactDstPosition(const ArtifactID & aid, const CArtifactSet * target) { - for(const auto & slot : artifact->artType->possibleSlots.at(target->bearerType())) + const auto * art = aid.toArtifact(); + for(const auto & slot : art->possibleSlots.at(target->bearerType())) { const auto * existingArtInfo = target->getSlot(slot); if((!existingArtInfo || !existingArtInfo->locked) - && artifact->canBePutAt(target, slot)) + && art->canBePutAt(target, slot)) { return slot; } } - return ArtifactPosition(GameConstants::BACKPACK_START); + return GameConstants::BACKPACK_START; } DLL_LINKAGE const std::vector & ArtifactUtils::unmovableSlots() diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index a943d409b..dc811d766 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -90,6 +90,8 @@ public: virtual void levelUpArtifact (CArtifactInstance * art){}; + virtual bool canBeDisassembled() const; + virtual bool canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved = false) const; void updateFrom(const JsonNode & data); void serializeJson(JsonSerializeFormat & handler); @@ -155,7 +157,6 @@ public: ArtifactPosition firstBackpackSlot(const CArtifactSet *h) const; SpellID getGivenSpellID() const; //to be used with scrolls (and similar arts), -1 if none - virtual bool canBePutAt(const CArtifactSet *artSet, ArtifactPosition slot, bool assumeDestRemoved = false) const; bool canBePutAt(const ArtifactLocation & al, bool assumeDestRemoved = false) const; //forwards to the above one virtual bool canBeDisassembled() const; virtual void putAt(ArtifactLocation al); @@ -209,8 +210,6 @@ public: std::vector constituentsInfo; - bool canBePutAt(const CArtifactSet *artSet, ArtifactPosition slot, bool assumeDestRemoved = false) const override; - bool canBeDisassembled() const override; void putAt(ArtifactLocation al) override; void removeFrom(ArtifactLocation al) override; bool isPart(const CArtifactInstance *supposedPart) const override; @@ -371,6 +370,7 @@ class DLL_LINKAGE CArtifactFittingSet : public CArtifactSet public: CArtifactFittingSet(ArtBearer::ArtBearer Bearer); void putArtifact(ArtifactPosition pos, CArtifactInstance * art) override; + void removeArtifact(ArtifactPosition pos); ArtBearer::ArtBearer bearerType() const override; protected: @@ -380,8 +380,7 @@ protected: namespace ArtifactUtils { // Calculates where an artifact gets placed when it gets transferred from one hero to another. - DLL_LINKAGE ArtifactPosition getArtifactDstPosition(const CArtifactInstance * artifact, - const CArtifactSet * target); + DLL_LINKAGE ArtifactPosition getArtifactDstPosition(const ArtifactID & aid, const CArtifactSet * target); // TODO: Make this constexpr when the toolset is upgraded DLL_LINKAGE const std::vector & unmovableSlots(); DLL_LINKAGE const std::vector & constituentWornSlots(); diff --git a/lib/GameConstants.h b/lib/GameConstants.h index d9265ffbf..0b04fe48a 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -47,7 +47,6 @@ namespace GameConstants const int ALL_PLAYERS = 255; //bitfield - const ui16 BACKPACK_START = 19; const int CREATURES_PER_TOWN = 7; //without upgrades const int SPELL_LEVELS = 5; const int SPELL_SCHOOL_LEVELS = 4; @@ -1044,6 +1043,11 @@ public: ID_LIKE_OPERATORS(ArtifactPosition, ArtifactPosition::EArtifactPosition) +namespace GameConstants +{ + const auto BACKPACK_START = ArtifactPosition::AFTER_LAST; +} + class ArtifactID { public: diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index ef583d948..24097671d 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4002,7 +4002,7 @@ bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID std::vector & slots) -> void { assert(artifact); - auto dstSlot = ArtifactUtils::getArtifactDstPosition(artifact, &artFittingSet); + auto dstSlot = ArtifactUtils::getArtifactDstPosition(artifact->artType->getId(), &artFittingSet); artFittingSet.putArtifact(dstSlot, static_cast>(artifact)); slots.push_back(BulkMoveArtifacts::LinkedSlots(srcSlot, dstSlot));