/* * CArtifactsOfHeroBase.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 "CArtifactsOfHeroBase.h" #include "../gui/CGuiHandler.h" #include "../gui/Shortcut.h" #include "Buttons.h" #include "../CPlayerInterface.h" #include "../CGameInfo.h" #include "../../CCallback.h" #include "../../lib/ArtifactUtils.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/networkPacks/ArtifactLocation.h" CArtifactsOfHeroBase::CArtifactsOfHeroBase() : curHero(nullptr) { } void CArtifactsOfHeroBase::putBackPickedArtifact() { // Artifact located in artifactsTransitionPos should be returned if(const auto art = getPickedArtifact()) { auto slot = ArtifactUtils::getArtAnyPosition(curHero, art->getTypeId()); if(slot == ArtifactPosition::PRE_FIRST) { LOCPLINT->cb->eraseArtifactByClient(ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS)); } else { LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS), ArtifactLocation(curHero->id, slot)); } } } void CArtifactsOfHeroBase::init( const Point & position, const BpackScrollFunctor & scrollCallback) { // CArtifactsOfHeroBase::init may be transform to CArtifactsOfHeroBase::CArtifactsOfHeroBase if OBJECT_CONSTRUCTION is removed OBJECT_CONSTRUCTION; pos += position; for(int g = 0; g < ArtifactPosition::BACKPACK_START; g++) { artWorn[ArtifactPosition(g)] = std::make_shared(slotPos[g]); } backpack.clear(); for(int s = 0; s < 5; s++) { auto artPlace = std::make_shared(Point(403 + 46 * s, 365)); backpack.push_back(artPlace); } for(auto & artPlace : artWorn) { artPlace.second->slot = artPlace.first; artPlace.second->setArtifact(ArtifactID(ArtifactID::NONE)); } for(const auto & artPlace : backpack) { artPlace->setArtifact(ArtifactID(ArtifactID::NONE)); } leftBackpackRoll = std::make_shared(Point(379, 364), AnimationPath::builtin("hsbtns3.def"), CButton::tooltip(), [scrollCallback](){scrollCallback(true);}, EShortcut::MOVE_LEFT); rightBackpackRoll = std::make_shared(Point(632, 364), AnimationPath::builtin("hsbtns5.def"), CButton::tooltip(), [scrollCallback](){scrollCallback(false);}, EShortcut::MOVE_RIGHT); leftBackpackRoll->block(true); rightBackpackRoll->block(true); backpackScroller = std::make_shared(this, Rect(380, 30, 278, 382)); backpackScroller->setScrollingEnabled(false); setRedrawParent(true); } void CArtifactsOfHeroBase::setClickPressedArtPlacesCallback(const CArtPlace::ClickFunctor & callback) const { for(const auto & [slot, artPlace] : artWorn) artPlace->setClickPressedCallback(callback); for(const auto & artPlace : backpack) artPlace->setClickPressedCallback(callback); } void CArtifactsOfHeroBase::setShowPopupArtPlacesCallback(const CArtPlace::ClickFunctor & callback) const { for(const auto & [slot, artPlace] : artWorn) artPlace->setShowPopupCallback(callback); for(const auto & artPlace : backpack) artPlace->setShowPopupCallback(callback); } void CArtifactsOfHeroBase::clickPressedArtPlace(CComponentHolder & artPlace, const Point & cursorPosition) { if(auto ownedPlace = getArtPlace(cursorPosition)) { if(ownedPlace->isLocked()) return; if(clickPressedCallback) clickPressedCallback(*ownedPlace, cursorPosition); } } void CArtifactsOfHeroBase::showPopupArtPlace(CComponentHolder & artPlace, const Point & cursorPosition) { if(auto ownedPlace = getArtPlace(cursorPosition)) { if(ownedPlace->isLocked()) return; if(showPopupCallback) showPopupCallback(*ownedPlace, cursorPosition); } } void CArtifactsOfHeroBase::gestureArtPlace(CComponentHolder & artPlace, const Point & cursorPosition) { if(auto ownedPlace = getArtPlace(cursorPosition)) { if(ownedPlace->isLocked()) return; if(gestureCallback) gestureCallback(*ownedPlace, cursorPosition); } } void CArtifactsOfHeroBase::setHero(const CGHeroInstance * hero) { curHero = hero; if (!hero) return; for(auto slot : artWorn) { setSlotData(slot.second, slot.first); } updateBackpackSlots(); } const CGHeroInstance * CArtifactsOfHeroBase::getHero() const { return curHero; } void CArtifactsOfHeroBase::scrollBackpack(bool left) { LOCPLINT->cb->scrollBackpackArtifacts(curHero->id, left); } void CArtifactsOfHeroBase::markPossibleSlots(const CArtifact * art, bool assumeDestRemoved) { for(const auto & artPlace : artWorn) artPlace.second->selectSlot(art->canBePutAt(curHero, artPlace.second->slot, assumeDestRemoved)); } void CArtifactsOfHeroBase::unmarkSlots() { for(auto & artPlace : artWorn) artPlace.second->selectSlot(false); for(auto & artPlace : backpack) artPlace->selectSlot(false); } CArtifactsOfHeroBase::ArtPlacePtr CArtifactsOfHeroBase::getArtPlace(const ArtifactPosition & slot) { if(ArtifactUtils::isSlotEquipment(slot) && artWorn.find(slot) != artWorn.end()) return artWorn[slot]; if(ArtifactUtils::isSlotBackpack(slot) && slot - ArtifactPosition::BACKPACK_START < backpack.size()) return(backpack[slot - ArtifactPosition::BACKPACK_START]); logGlobal->error("CArtifactsOfHero::getArtPlace: invalid slot %d", slot); return nullptr; } CArtifactsOfHeroBase::ArtPlacePtr CArtifactsOfHeroBase::getArtPlace(const Point & cursorPosition) { for(const auto & [slot, artPlace] : artWorn) { if(artPlace->pos.isInside(cursorPosition)) return artPlace; } for(const auto & artPlace : backpack) { if(artPlace->pos.isInside(cursorPosition)) return artPlace; } return nullptr; } void CArtifactsOfHeroBase::updateWornSlots() { for(auto place : artWorn) updateSlot(place.first); } void CArtifactsOfHeroBase::updateBackpackSlots() { ArtifactPosition slot = ArtifactPosition::BACKPACK_START; for(const auto & artPlace : backpack) { setSlotData(artPlace, slot); slot = slot + 1; } auto scrollingPossible = static_cast(curHero->artifactsInBackpack.size()) > backpack.size(); // Blocking scrolling if there is not enough artifacts to scroll if(leftBackpackRoll) leftBackpackRoll->block(!scrollingPossible); if(rightBackpackRoll) rightBackpackRoll->block(!scrollingPossible); if (backpackScroller) backpackScroller->setScrollingEnabled(scrollingPossible); } void CArtifactsOfHeroBase::updateSlot(const ArtifactPosition & slot) { setSlotData(getArtPlace(slot), slot); } const CArtifactInstance * CArtifactsOfHeroBase::getPickedArtifact() { // Returns only the picked up artifact. Not just highlighted like in the trading window. if(curHero) return curHero->getArt(ArtifactPosition::TRANSITION_POS); else return nullptr; } void CArtifactsOfHeroBase::enableGesture() { for(auto & artPlace : artWorn) { artPlace.second->setGestureCallback(std::bind(&CArtifactsOfHeroBase::gestureArtPlace, this, _1, _2)); artPlace.second->addUsedEvents(GESTURE); } } const CArtifactInstance * CArtifactsOfHeroBase::getArt(const ArtifactPosition & slot) const { return curHero ? curHero->getArt(slot) : nullptr; } void CArtifactsOfHeroBase::enableKeyboardShortcuts() { addUsedEvents(AEventsReceiver::KEYBOARD); } void CArtifactsOfHeroBase::setSlotData(ArtPlacePtr artPlace, const ArtifactPosition & slot) { // Spurious call from artifactMoved in attempt to update hidden backpack slot if(!artPlace && ArtifactUtils::isSlotBackpack(slot)) { return; } artPlace->slot = slot; if(auto slotInfo = curHero->getSlot(slot)) { artPlace->lockSlot(slotInfo->locked); artPlace->setArtifact(slotInfo->artifact->getTypeId(), slotInfo->artifact->getScrollSpellID()); if(slotInfo->locked || slotInfo->artifact->isCombined()) return; // If the artifact is part of at least one combined artifact, add additional information std::map> arts; for(const auto combinedArt : slotInfo->artifact->getType()->getPartOf()) { assert(combinedArt->isCombined()); arts.try_emplace(combinedArt->getId()); CArtifactFittingSet fittingSet(*curHero); for(const auto part : combinedArt->getConstituents()) { const auto partSlot = fittingSet.getArtPos(part->getId(), false, false); if(partSlot != ArtifactPosition::PRE_FIRST) { arts.at(combinedArt->getId()).emplace_back(part->getId()); fittingSet.lockSlot(partSlot); } } } artPlace->addCombinedArtInfo(arts); } else { artPlace->setArtifact(ArtifactID(ArtifactID::NONE)); } } BackpackScroller::BackpackScroller(CArtifactsOfHeroBase * owner, const Rect & dimensions) : Scrollable(0, Point(), Orientation::HORIZONTAL) , owner(owner) { pos = dimensions + pos.topLeft(); setPanningStep(46); } void BackpackScroller::scrollBy(int distance) { if (distance != 0) owner->scrollBackpack(distance < 0); }