diff --git a/client/Client.h b/client/Client.h index a90df2f06..b360d32fb 100644 --- a/client/Client.h +++ b/client/Client.h @@ -211,8 +211,8 @@ public: void removeAfterVisit(const CGObjectInstance * object) override {}; bool swapGarrisonOnSiege(ObjectInstanceID tid) override {return false;}; - void giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact * artType, ArtifactPosition pos) override {}; - bool giveHeroArtifact(const CGHeroInstance * h, const CArtifactInstance * a, ArtifactPosition pos) override {return false;}; + bool giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact * artType, ArtifactPosition pos) override {return false;} + bool giveHeroArtifact(const CGHeroInstance * h, const CArtifactInstance * a, ArtifactPosition pos) override {return false;} void putArtifact(const ArtifactLocation & al, const CArtifactInstance * a) override {}; void removeArtifact(const ArtifactLocation & al) override {}; bool moveArtifact(const ArtifactLocation & al1, const ArtifactLocation & al2) override {return false;}; diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index a28305c50..295ba6de9 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -646,7 +646,7 @@ CArtifactsOfHero::~CArtifactsOfHero() if(!curHero->artifactsTransitionPos.empty()) { auto artPlace = getArtPlace( - ArtifactUtils::getArtifactDstPosition(curHero->artifactsTransitionPos.begin()->artifact->artType->getId(), curHero)); + ArtifactUtils::getArtAnyPosition(curHero, curHero->artifactsTransitionPos.begin()->artifact->artType->getId())); if(artPlace) { assert(artPlace->ourOwner); diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index 979b93b48..db653df0c 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -806,7 +806,8 @@ void CMarketplaceWindow::makeDeal() if(!sliderValue) return; - int leftIdToSend = -1; + madeTransaction = true; + int leftIdToSend = hLeft->id; switch (mode) { case EMarketMode::CREATURE_RESOURCE: @@ -815,22 +816,29 @@ void CMarketplaceWindow::makeDeal() case EMarketMode::ARTIFACT_RESOURCE: leftIdToSend = hLeft->getArtInstance()->id.getNum(); break; + case EMarketMode::RESOURCE_ARTIFACT: + if(!ArtifactUtils::isPossibleToGetArt(hero, ArtifactID(hRight->id))) + { + LOCPLINT->showInfoDialog("no available slots"); + madeTransaction = false; + } + break; default: - leftIdToSend = hLeft->id; break; } - if(slider) + if(madeTransaction) { - LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, slider->getValue()*r1, hero); - slider->moveTo(0); + if(slider) + { + LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, slider->getValue() * r1, hero); + slider->moveTo(0); + } + else + { + LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, r2, hero); + } } - else - { - LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, r2, hero); - } - madeTransaction = true; - hLeft = nullptr; hRight = nullptr; selectionChanged(true); diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 331dace9c..84ef1a744 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)->artType->getId(), target)); + ArtifactUtils::getArtAnyPosition(target, source->getArt(srcPosition)->artType->getId())); cb->swapArtifacts(srcLocation, dstLocation); } diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index c397b957b..e3c6d026e 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -185,7 +185,7 @@ bool CArtifact::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, b assert(constituents); for(const auto art : *constituents) { - auto possibleSlot = ArtifactUtils::getArtifactDstPosition(art->getId(), &fittingSet); + auto possibleSlot = ArtifactUtils::getArtAnyPosition(&fittingSet, art->getId()); if(ArtifactUtils::isSlotEquipment(possibleSlot)) { fittingSet.setNewArtSlot(possibleSlot, nullptr, true); @@ -1091,7 +1091,7 @@ void CCombinedArtifactInstance::putAt(ArtifactLocation al) const bool suggestedPosValid = ci.art->canBePutAt(suggestedPos); if(!(inActiveSlot && suggestedPosValid)) //there is a valid suggestion where to place lock - ci.slot = ArtifactUtils::getArtifactDstPosition(ci.art->artType->getId(), al.getHolderArtSet()); + ci.slot = ArtifactUtils::getArtAnyPosition(al.getHolderArtSet(), ci.art->artType->getId()); assert(ArtifactUtils::isSlotEquipment(ci.slot)); al.getHolderArtSet()->setNewArtSlot(ci.slot, ci.art, true); //sets as lock @@ -1522,7 +1522,7 @@ void CArtifactFittingSet::putArtifact(ArtifactPosition pos, CArtifactInstance * { for(auto & part : dynamic_cast(art)->constituentsInfo) { - const auto slot = ArtifactUtils::getArtifactDstPosition(part.art->artType->getId(), this); + const auto slot = ArtifactUtils::getArtAnyPosition(this, part.art->artType->getId()); assert(slot != ArtifactPosition::PRE_FIRST); // For the ArtFittingSet is no needed to do figureMainConstituent, just lock slots this->setNewArtSlot(slot, part.art, true); @@ -1559,7 +1559,7 @@ ArtBearer::ArtBearer CArtifactFittingSet::bearerType() const return this->Bearer; } -DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtifactDstPosition(const ArtifactID & aid, const CArtifactSet * target) +DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtAnyPosition(const CArtifactSet * target, const ArtifactID & aid) { const auto * art = aid.toArtifact(); for(const auto & slot : art->possibleSlots.at(target->bearerType())) @@ -1567,6 +1567,12 @@ DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtifactDstPosition(const Artifac if(art->canBePutAt(target, slot)) return slot; } + return getArtBackpackPosition(target, aid); +} + +DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtBackpackPosition(const CArtifactSet * target, const ArtifactID & aid) +{ + const auto * art = aid.toArtifact(); if(art->canBePutAt(target, GameConstants::BACKPACK_START)) { return GameConstants::BACKPACK_START; @@ -1649,9 +1655,10 @@ DLL_LINKAGE bool ArtifactUtils::isBackpackFreeSlots(const CArtifactSet * target, return target->artifactsInBackpack.size() + reqSlots <= backpackCap; } -DLL_LINKAGE bool ArtifactUtils::isPossibleToGetArt(const CArtifactSet * target, const ArtifactID & aid) +DLL_LINKAGE bool ArtifactUtils::isPossibleToGetArt(const CArtifactSet * target, const ArtifactID & aid, ArtifactPosition slot) { - const auto slot = getArtifactDstPosition(aid, target); + if(slot == ArtifactPosition::FIRST_AVAILABLE) + slot = getArtAnyPosition(target, aid); if(isSlotEquipment(slot) || (isSlotBackpack(slot) && isBackpackFreeSlots(target))) return true; else diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index 5084d08ec..117399751 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -380,7 +380,8 @@ protected: namespace ArtifactUtils { // Calculates where an artifact gets placed when it gets transferred from one hero to another. - DLL_LINKAGE ArtifactPosition getArtifactDstPosition(const ArtifactID & aid, const CArtifactSet * target); + DLL_LINKAGE ArtifactPosition getArtAnyPosition(const CArtifactSet * target, const ArtifactID & aid); + DLL_LINKAGE ArtifactPosition getArtBackpackPosition(const CArtifactSet * target, const ArtifactID & aid); // TODO: Make this constexpr when the toolset is upgraded DLL_LINKAGE const std::vector & unmovableSlots(); DLL_LINKAGE const std::vector & constituentWornSlots(); @@ -389,7 +390,8 @@ namespace ArtifactUtils DLL_LINKAGE bool isSlotBackpack(const ArtifactPosition & slot); DLL_LINKAGE bool isSlotEquipment(const ArtifactPosition & slot); DLL_LINKAGE bool isBackpackFreeSlots(const CArtifactSet * target, const size_t reqSlots = 1); - DLL_LINKAGE bool isPossibleToGetArt(const CArtifactSet * target, const ArtifactID & aid); + DLL_LINKAGE bool isPossibleToGetArt(const CArtifactSet * target, const ArtifactID & aid, + ArtifactPosition slot = ArtifactPosition::FIRST_AVAILABLE); } VCMI_LIB_NAMESPACE_END diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 5551659a2..42e605335 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -1594,7 +1594,7 @@ void CGameState::giveCampaignBonusToHero(CGHeroInstance * hero) case CScenarioTravel::STravelBonus::SPELL_SCROLL: { CArtifactInstance * scroll = CArtifactInstance::createScroll(SpellID(curBonus->info2)); - const auto slot = ArtifactUtils::getArtifactDstPosition(scroll->artType->getId(), hero); + const auto slot = ArtifactUtils::getArtAnyPosition(hero, scroll->artType->getId()); if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot)) scroll->putAt(ArtifactLocation(hero, slot)); else @@ -1692,9 +1692,7 @@ void CGameState::initStartingBonus() CGHeroInstance *hero = elem.second.heroes[0]; if(!giveHeroArtifact(hero, toGive->getId())) - { logGlobal->error("Cannot give starting artifact - no free slots!"); - } } break; } @@ -2818,7 +2816,7 @@ bool CGameState::giveHeroArtifact(CGHeroInstance * h, const ArtifactID & aid) CArtifact * const artifact = VLC->arth->objects[aid]; //pointer to constant object CArtifactInstance * ai = CArtifactInstance::createNewArtifactInstance(artifact); map->addNewArtifactInstance(ai); - auto slot = ArtifactUtils::getArtifactDstPosition(aid, h); + auto slot = ArtifactUtils::getArtAnyPosition(h, aid); if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot)) { ai->putAt(ArtifactLocation(h, slot)); diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index 5215af8de..26b84a39d 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -109,8 +109,8 @@ public: virtual void removeAfterVisit(const CGObjectInstance *object) = 0; //object will be destroyed when interaction is over. Do not call when interaction is not ongoing! - virtual void giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact *artType, ArtifactPosition pos) = 0; - virtual bool giveHeroArtifact(const CGHeroInstance *h, const CArtifactInstance *a, ArtifactPosition pos) = 0; //pos==-1 - first free slot in backpack=0; pos==-2 - default if available or backpack + virtual bool giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact * artType, ArtifactPosition pos) = 0; + virtual bool giveHeroArtifact(const CGHeroInstance * h, const CArtifactInstance * a, ArtifactPosition pos) = 0; virtual void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) = 0; virtual void removeArtifact(const ArtifactLocation &al) = 0; virtual bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) = 0; diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index c6f19fcff..cfdbacd7d 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -238,8 +238,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const iw.components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0); loot << "%s"; loot.addReplacement(MetaString::ART_NAMES, elem); - if(ArtifactUtils::isPossibleToGetArt(hero, elem)) - cb->giveHeroNewArtifact(hero, VLC->arth->objects[elem], ArtifactPosition::FIRST_AVAILABLE); + cb->giveHeroNewArtifact(hero, VLC->arth->objects[elem], ArtifactPosition::FIRST_AVAILABLE); } //display loot if (!iw.components.empty()) diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index 6921ce0dd..10f8e66f0 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -248,8 +248,7 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const cb->giveResources(h->getOwner(), resources); for(const auto & elem : artifacts) - if(ArtifactUtils::isPossibleToGetArt(h, elem)) - cb->giveHeroNewArtifact(h, VLC->arth->objects[elem],ArtifactPosition::FIRST_AVAILABLE); + cb->giveHeroNewArtifact(h, VLC->arth->objects[elem],ArtifactPosition::FIRST_AVAILABLE); iw.components.clear(); iw.text.clear(); diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index 6b4e12cf5..1c63e7ea9 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -870,8 +870,7 @@ void CGSeerHut::completeQuest (const CGHeroInstance * h) const //reward cb->changeSecSkill(h, SecondarySkill(rID), rVal, false); break; case ARTIFACT: - if(ArtifactUtils::isPossibleToGetArt(h, VLC->arth->objects[rID]->getId())) - cb->giveHeroNewArtifact(h, VLC->arth->objects[rID],ArtifactPosition::FIRST_AVAILABLE); + cb->giveHeroNewArtifact(h, VLC->arth->objects[rID],ArtifactPosition::FIRST_AVAILABLE); break; case SPELL: { diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index dbec885ab..02e61bc76 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -1322,7 +1322,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const iw.text << message; else { - iw.text.addTxt(MetaString::ADVOB_TXT, 135); + iw.text.addTxt(MetaString::ADVOB_TXT,135); iw.text.addReplacement(MetaString::SPELL_NAME, spellID); } } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 4da0f7a62..b044da94b 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4007,7 +4007,7 @@ bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID std::vector & slots) -> void { assert(artifact); - auto dstSlot = ArtifactUtils::getArtifactDstPosition(artifact->artType->getId(), &artFittingSet); + auto dstSlot = ArtifactUtils::getArtAnyPosition(&artFittingSet, artifact->artType->getId()); if(dstSlot != ArtifactPosition::PRE_FIRST) { artFittingSet.putArtifact(dstSlot, static_cast>(artifact)); @@ -4152,7 +4152,7 @@ bool CGameHandler::buyArtifact(ObjectInstanceID hid, ArtifactID aid) const int price = art->price; COMPLAIN_RET_FALSE_IF(getPlayerState(hero->getOwner())->resources.at(Res::GOLD) < price, "Not enough gold!"); - if ((town->hasBuilt(BuildingID::BLACKSMITH) && town->town->warMachine == aid) + if ((town->hasBuilt(BuildingID::BLACKSMITH) && town->town->warMachine == aid) || (town->hasBuilt(BuildingSubID::BALLISTA_YARD) && aid == ArtifactID::BALLISTA)) { giveResource(hero->getOwner(),Res::GOLD,-price); @@ -4208,7 +4208,6 @@ bool CGameHandler::buyArtifact(const IMarket *m, const CGHeroInstance *h, Res::E COMPLAIN_RET("Cannot find selected artifact on the list"); sendAndApply(&saa); - giveHeroNewArtifact(h, VLC->arth->objects[aid], ArtifactPosition::FIRST_AVAILABLE); return true; } @@ -6838,7 +6837,11 @@ bool CGameHandler::giveHeroArtifact(const CGHeroInstance * h, const CArtifactIns if(pos == ArtifactPosition::FIRST_AVAILABLE) { - al.slot = ArtifactUtils::getArtifactDstPosition(a->artType->getId(), h); + al.slot = ArtifactUtils::getArtAnyPosition(h, a->artType->getId()); + } + else if(pos == GameConstants::BACKPACK_START) + { + al.slot = ArtifactUtils::getArtBackpackPosition(h, a->artType->getId()); } else { @@ -6846,15 +6849,12 @@ bool CGameHandler::giveHeroArtifact(const CGHeroInstance * h, const CArtifactIns al.slot = pos; } - if(ArtifactUtils::isSlotEquipment(al.slot) || ArtifactUtils::isSlotBackpack(al.slot)) - { + if(ArtifactUtils::isPossibleToGetArt(h, a->artType->getId(), al.slot)) putArtifact(al, a); - return true; - } else - { return false; - } + + return true; } void CGameHandler::putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) @@ -6865,36 +6865,31 @@ void CGameHandler::putArtifact(const ArtifactLocation &al, const CArtifactInstan sendAndApply(&pa); } -bool CGameHandler::giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact * art) +bool CGameHandler::giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact * artType, ArtifactPosition pos) { - COMPLAIN_RET_FALSE_IF(art->possibleSlots.at(ArtBearer::HERO).empty(), "Not a hero artifact!"); + assert(artType); + if(pos != ArtifactPosition::FIRST_AVAILABLE && pos != GameConstants::BACKPACK_START) + COMPLAIN_RET_FALSE_IF(!artType->canBePutAt(h, pos, false), "Cannot put artifact in that slot!"); - ArtifactPosition slot = art->possibleSlots.at(ArtBearer::HERO).front(); + CArtifactInstance * newArtInst = nullptr; + if(artType->canBeDisassembled()) + newArtInst = new CCombinedArtifactInstance(); + else + newArtInst = new CArtifactInstance(); - COMPLAIN_RET_FALSE_IF(nullptr != h->getArt(slot, false), "Hero already has artifact in slot"); - - giveHeroNewArtifact(h, art, slot); - return true; -} - -void CGameHandler::giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact *artType, ArtifactPosition pos) -{ - CArtifactInstance *a = nullptr; - if (!artType->constituents) + newArtInst->artType = artType; // *NOT* via settype -> all bonus-related stuff must be done by NewArtifact apply + if(giveHeroArtifact(h, newArtInst, pos)) { - a = new CArtifactInstance(); + NewArtifact na; + na.art = newArtInst; + sendAndApply(&na); // -> updates a!!!, will create a on other machines + return true; } else { - a = new CCombinedArtifactInstance(); + delete newArtInst; + return false; } - a->artType = artType; //*NOT* via settype -> all bonus-related stuff must be done by NewArtifact apply - - NewArtifact na; - na.art = a; - sendAndApply(&na); // -> updates a!!!, will create a on other machines - - COMPLAIN_RET_IF(!giveHeroArtifact(h, a, pos), "Cannot put artifact in that slot!"); } void CGameHandler::setBattleResult(BattleResult::EResult resultType, int victoriusSide) @@ -7044,8 +7039,11 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons cheated = true; if (!hero) return; ///Give hero all artifacts except war machines, spell scrolls and spell book - for (int g = 7; g < VLC->arth->objects.size(); ++g) //including artifacts from mods - giveHeroNewArtifact(hero, VLC->arth->objects[g], ArtifactPosition::PRE_FIRST); + for(int g = 7; g < VLC->arth->objects.size(); ++g) //including artifacts from mods + { + if(ArtifactUtils::isPossibleToGetArt(hero, VLC->arth->objects[g]->getId())) + giveHeroNewArtifact(hero, VLC->arth->objects[g], ArtifactPosition::FIRST_AVAILABLE); + } } else if (cheat == "vcmiglorfindel" || cheat == "vcmilevel") { diff --git a/server/CGameHandler.h b/server/CGameHandler.h index c89dca22d..91272ec9f 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -175,8 +175,7 @@ public: void removeAfterVisit(const CGObjectInstance *object) override; - bool giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact * art); - void giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact *artType, ArtifactPosition pos) override; + bool giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact * artType, ArtifactPosition pos = ArtifactPosition::FIRST_AVAILABLE) override; bool giveHeroArtifact(const CGHeroInstance * h, const CArtifactInstance * a, ArtifactPosition pos) override; void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) override; void removeArtifact(const ArtifactLocation &al) override; diff --git a/test/mock/mock_IGameCallback.h b/test/mock/mock_IGameCallback.h index 409e5fa3b..8f09d6c33 100644 --- a/test/mock/mock_IGameCallback.h +++ b/test/mock/mock_IGameCallback.h @@ -64,8 +64,8 @@ public: void removeAfterVisit(const CGObjectInstance *object) override {} //object will be destroyed when interaction is over. Do not call when interaction is not ongoing! - void giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact *artType, ArtifactPosition pos) override {} - bool giveHeroArtifact(const CGHeroInstance *h, const CArtifactInstance *a, ArtifactPosition pos) override {} //pos==-1 - first free slot in backpack=0; pos==-2 - default if available or backpack + bool giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact * artType, ArtifactPosition pos) override {return false;} + bool giveHeroArtifact(const CGHeroInstance * h, const CArtifactInstance * a, ArtifactPosition pos) override {return false;} void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) override {} void removeArtifact(const ArtifactLocation &al) override {} bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) override {return false;}