From 1046964eefc3e6153fcbdc72ff757c6c1c42a994 Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:09:49 +0300 Subject: [PATCH 01/25] CArtifactHolder --- client/widgets/CArtifactHolder.cpp | 1892 ++++++++++++++-------------- client/widgets/CArtifactHolder.h | 248 ++-- 2 files changed, 1073 insertions(+), 1067 deletions(-) diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index 0f881e54d..dfde17f84 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -30,364 +30,25 @@ #include "../../lib/mapObjects/CGHeroInstance.h" -CHeroArtPlace::CHeroArtPlace(Point position, const CArtifactInstance * Art) - : CArtPlace(position, Art), - locked(false), - picked(false), - marked(false), - ourOwner(nullptr) +void CArtPlace::setInternals(const CArtifactInstance * artInst) { - createImage(); -} - -void CHeroArtPlace::createImage() -{ - OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); - - si32 imageIndex = 0; - - if(locked) - imageIndex = ArtifactID::ART_LOCK; - else if(ourArt) - imageIndex = ourArt->artType->getIconIndex(); - - image = std::make_shared("artifact", imageIndex); - if(!ourArt) - image->disable(); - - selection = std::make_shared("artifact", ArtifactID::ART_SELECTION); - selection->disable(); -} - -void CHeroArtPlace::lockSlot(bool on) -{ - if (locked == on) - return; - - locked = on; - - if (on) - image->setFrame(ArtifactID::ART_LOCK); - else if (ourArt) - image->setFrame(ourArt->artType->getIconIndex()); - else - image->setFrame(0); -} - -void CHeroArtPlace::pickSlot(bool on) -{ - if (picked == on) - return; - - picked = on; - if (on) - image->disable(); - else - image->enable(); -} - -void CHeroArtPlace::selectSlot(bool on) -{ - if (marked == on) - return; - - marked = on; - if (on) - selection->enable(); - else - selection->disable(); -} - -void CHeroArtPlace::clickLeft(tribool down, bool previousState) -{ - //LRClickableAreaWTextComp::clickLeft(down); - bool inBackpack = ArtifactUtils::isSlotBackpack(slotID); - bool srcInBackpack = ArtifactUtils::isSlotBackpack(ourOwner->commonInfo->src.slotID); - bool srcInSameHero = ourOwner->commonInfo->src.AOH == ourOwner; - - if(ourOwner->highlightModeCallback && ourArt) - { - if(down) - { - if(!ourArt->artType->isTradable()) //War Machine or Spellbook - { - LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]); //This item can't be traded. - } - else - { - ourOwner->unmarkSlots(false); - selectSlot(true); - ourOwner->highlightModeCallback(this); - } - } - return; - } - - // If clicked on spellbook, open it only if no artifact is held at the moment. - if(ourArt && !down && previousState && !ourOwner->commonInfo->src.AOH) - { - if(ourArt->artType->getId() == ArtifactID::SPELLBOOK) - GH.pushIntT(ourOwner->curHero, LOCPLINT, LOCPLINT->battleInt.get()); - } - - if (!down && previousState) - { - if(ourArt && ourArt->artType->getId() == ArtifactID::SPELLBOOK) - return; //this is handled separately - - if(!ourOwner->commonInfo->src.AOH) //nothing has been clicked - { - if(ourArt //to prevent selecting empty slots (bugfix to what GrayFace reported) - && ourOwner->curHero->tempOwner == LOCPLINT->playerID)//can't take art from another player - { - if(ourArt->artType->getId() == ArtifactID::CATAPULT) //catapult cannot be highlighted - { - std::vector> catapult(1, std::make_shared(CComponent::artifact, 3, 0)); - LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult); //The Catapult must be equipped. - return; - } - select(); - } - } - // Perform artifact transition - else if(ourArt != ourOwner->commonInfo->src.art) - { - if(inBackpack) // Backpack destination. - { - if(!srcInBackpack || slotID != ourOwner->commonInfo->src.slotID + 1) - { - const CArtifact * const cur = ourOwner->commonInfo->src.art->artType; - - if(cur->getId() == ArtifactID::CATAPULT) - { - //should not happen, catapult cannot be selected - logGlobal->error("Attempt to move Catapult"); - } - else if(cur->isBig()) - { - //war machines cannot go to backpack - LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % cur->getNameTranslated())); - } - else - { - setMeAsDest(); - vstd::amin(ourOwner->commonInfo->dst.slotID, ourOwner->curHero->artifactsInBackpack.size() + GameConstants::BACKPACK_START); - if(ArtifactUtils::isBackpackFreeSlots(ourOwner->curHero)) - { - if(!srcInSameHero || ourOwner->commonInfo->dst.slotID != ourOwner->commonInfo->src.slotID) - ourOwner->realizeCurrentTransaction(); - } - else - { - LOCPLINT->showInfoDialog(CGI->generaltexth->translate("core.genrltxt.152")); - } - } - } - } - //check if swap is possible - else if (fitsHere(ourOwner->commonInfo->src.art) && - (!ourArt || ourOwner->curHero->tempOwner == LOCPLINT->playerID)) - { - setMeAsDest(); - ourOwner->realizeCurrentTransaction(); - } - } - } -} - -bool CHeroArtPlace::askToAssemble(const CGHeroInstance * hero, ArtifactPosition slot) -{ - assert(hero); - const auto art = hero->getArt(slot); - assert(art); - auto assemblyPossibilities = ArtifactUtils::assemblyPossibilities(hero, art->getTypeId(), ArtifactUtils::isSlotEquipment(slot)); - - // If the artifact can be assembled, display dialog. - for(const auto * combination : assemblyPossibilities) - { - LOCPLINT->showArtifactAssemblyDialog( - art->artType, - combination, - std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combination->getId())); - - if(assemblyPossibilities.size() > 2) - logGlobal->warn("More than one possibility of assembling on %s... taking only first", art->artType->getNameTranslated()); - return true; - } - return false; -} - -bool CHeroArtPlace::askToDisassemble(const CGHeroInstance * hero, ArtifactPosition slot) -{ - assert(hero); - const auto art = hero->getArt(slot); - assert(art); - - if(art->canBeDisassembled()) - { - if(ArtifactUtils::isSlotBackpack(slot) && !ArtifactUtils::isBackpackFreeSlots(hero, art->artType->constituents->size() - 1)) - return false; - - LOCPLINT->showArtifactAssemblyDialog( - art->artType, - nullptr, - std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, false, ArtifactID())); - return true; - } - return false; -} - -void CHeroArtPlace::clickRight(tribool down, bool previousState) -{ - if(ourArt && down && !locked && text.size() && !picked) //if there is no description or it's a lock, do nothing ;] - { - if(ourOwner->allowedAssembling) - { - // If the artifact can be assembled, display dialog. - if(askToAssemble(ourOwner->curHero, slotID)) - { - return; - } - if(askToDisassemble(ourOwner->curHero, slotID)) - { - return; - } - } - - // Lastly just show the artifact description. - LRClickableAreaWTextComp::clickRight(down, previousState); - } -} - -void CArtifactsOfHero::activate() -{ - if (commonInfo->src.AOH == this && commonInfo->src.art) - CCS->curh->dragAndDropCursor("artifact", commonInfo->src.art->artType->getIconIndex()); - - CIntObject::activate(); -} - -void CArtifactsOfHero::deactivate() -{ - if (commonInfo->src.AOH == this && commonInfo->src.art) - CCS->curh->dragAndDropCursor(nullptr); - - CIntObject::deactivate(); -} - -/** - * Selects artifact slot so that the containing artifact looks like it's picked up. - */ -void CHeroArtPlace::select() -{ - if(locked) - return; - - pickSlot(true); - if(ourArt->canBeDisassembled() && ArtifactUtils::isSlotEquipment(slotID)) //worn combined artifact -> locks have to disappear - { - for(auto slot : ArtifactUtils::constituentWornSlots()) - { - auto ap = ourOwner->getArtPlace(slot); - if(ap)//getArtPlace may return null - ap->pickSlot(ourArt->isPart(ap->ourArt)); - } - } - - ourOwner->commonInfo->src.setTo(this, false); - ourOwner->commonInfo->src.slotID = ArtifactPosition::TRANSITION_POS; - - LOCPLINT->cb->swapArtifacts(ArtifactLocation(ourOwner->curHero, slotID), - ArtifactLocation(ourOwner->curHero, ArtifactPosition::TRANSITION_POS)); -} - -void CHeroArtPlace::showAll(SDL_Surface * to) -{ - if (ourArt && !picked && ourArt == ourOwner->curHero->getArt(slotID, false)) //last condition is needed for disassembling -> artifact may be gone, but we don't know yet TODO: real, nice solution - { - CIntObject::showAll(to); - } - - if(marked && active) - { - // Draw vertical bars. - for (int i = 0; i < pos.h; ++i) - { - CSDL_Ext::putPixelWithoutRefresh(to, pos.x, pos.y + i, 240, 220, 120); - CSDL_Ext::putPixelWithoutRefresh(to, pos.x + pos.w - 1, pos.y + i, 240, 220, 120); - } - - // Draw horizontal bars. - for (int i = 0; i < pos.w; ++i) - { - CSDL_Ext::putPixelWithoutRefresh(to, pos.x + i, pos.y, 240, 220, 120); - CSDL_Ext::putPixelWithoutRefresh(to, pos.x + i, pos.y + pos.h - 1, 240, 220, 120); - } - } -} - -bool CHeroArtPlace::fitsHere(const CArtifactInstance * art) const -{ - // You can place 'no artifact' anywhere. - if(!art) - return true; - - // Anything but War Machines can be placed in backpack. - if (slotID >= GameConstants::BACKPACK_START) - return !art->artType->isBig(); - - return art->canBePutAt(ArtifactLocation(ourOwner->curHero, slotID), true); -} - -void CHeroArtPlace::setMeAsDest(bool backpackAsVoid) -{ - ourOwner->commonInfo->dst.setTo(this, backpackAsVoid); -} - -void CHeroArtPlace::setArtifact(const CArtifactInstance *art) -{ - baseType = -1; //by default we don't store any component - ourArt = art; - if(!art) + baseType = -1; // By default we don't store any component + ourArt = artInst; + if(!artInst) { image->disable(); text.clear(); hoverText = CGI->generaltexth->allTexts[507]; return; } - image->enable(); - image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->getIconIndex()); - - text = art->getDescription(); - - // Display info about set - if(ourOwner && ourOwner->curHero && !art->canBeDisassembled()) + image->setFrame(artInst->artType->getIconIndex()); + if(artInst->getTypeId() == ArtifactID::SPELL_SCROLL) { - for(const auto combinedArt : art->artType->constituentOf) + auto spellID = artInst->getScrollSpellID(); + if(spellID.num >= 0) { - std::string artList; - text += "\n\n"; - text += "{" + combinedArt->getNameTranslated() + "}"; - int wornArtifacts = 0; - for(const auto part : *combinedArt->constituents) - { - if(art->artType->constituentOf.size() <= 1) - artList += "\n" + part->getNameTranslated(); - if(ourOwner->curHero->hasArt(part->getId(), true)) - wornArtifacts++; - } - text += " (" + boost::str(boost::format("%d") % wornArtifacts) + " / " + - boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList; - } - } - - if(art->artType->getId() == ArtifactID::SPELL_SCROLL) - { - int spellID = art->getScrollSpellID(); - if(spellID >= 0) - { - //add spell component info (used to provide a pic in r-click popup) + // Add spell component info (used to provide a pic in r-click popup) baseType = CComponent::spell; type = spellID; bonusValue = 0; @@ -396,587 +57,14 @@ void CHeroArtPlace::setArtifact(const CArtifactInstance *art) else { baseType = CComponent::artifact; - type = art->artType->getId(); + type = artInst->getTypeId(); bonusValue = 0; } - - if (locked) // Locks should appear as empty. - hoverText = CGI->generaltexth->allTexts[507]; - else - hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->getNameTranslated()); + text = artInst->getDescription(); } -void CArtifactsOfHero::SCommonPart::reset() -{ - src.clear(); - dst.clear(); - CCS->curh->dragAndDropCursor(nullptr); -} - -void CArtifactsOfHero::setHero(const CGHeroInstance * hero) -{ - curHero = hero; - if (curHero->artifactsInBackpack.size() > 0) - backpackPos %= curHero->artifactsInBackpack.size(); - else - backpackPos = 0; - - // Fill the slots for worn artifacts and backpack. - - for(auto p : artWorn) - { - setSlotData(p.second, p.first); - } - - scrollBackpack(0); -} - -void CArtifactsOfHero::dispose() -{ - CCS->curh->dragAndDropCursor(nullptr); -} - -void CArtifactsOfHero::scrollBackpack(int dir) -{ - int artsInBackpack = static_cast(curHero->artifactsInBackpack.size()); - backpackPos += dir; - if(backpackPos < 0)// No guarantee of modulus behavior with negative operands -> we keep it positive - backpackPos += artsInBackpack; - - if(artsInBackpack) - backpackPos %= artsInBackpack; - - std::multiset toOmit = artifactsOnAltar; - if(commonInfo->src.art) //if we picked an art from backapck, its slot has to be omitted - toOmit.insert(commonInfo->src.art); - - int omitedSoFar = 0; - - //set new data - size_t s = 0; - for( ; s < artsInBackpack; ++s) - { - - if (s < artsInBackpack) - { - auto slotID = ArtifactPosition(GameConstants::BACKPACK_START + (s + backpackPos)%artsInBackpack); - const CArtifactInstance *art = curHero->getArt(slotID); - assert(art); - if(!vstd::contains(toOmit, art)) - { - if(s - omitedSoFar < backpack.size()) - setSlotData(backpack[s-omitedSoFar], slotID); - } - else - { - toOmit -= art; - omitedSoFar++; - continue; - } - } - } - for( ; s - omitedSoFar < backpack.size(); s++) - eraseSlotData(backpack[s-omitedSoFar], ArtifactPosition(GameConstants::BACKPACK_START + (si32)s)); - - //in artifact merchant selling artifacts we may have highlight on one of backpack artifacts -> market needs update, cause artifact under highlight changed - if(highlightModeCallback) - { - for(auto & elem : backpack) - { - if(elem->marked) - { - highlightModeCallback(elem.get()); - break; - } - } - } - - //blocking scrolling if there is not enough artifacts to scroll - bool scrollingPossible = artsInBackpack - omitedSoFar > backpack.size(); - leftArtRoll->block(!scrollingPossible); - rightArtRoll->block(!scrollingPossible); - - safeRedraw(); -} - -/** - * Marks possible slots where a given artifact can be placed, except backpack. - * - * @param art Artifact checked against. - */ -void CArtifactsOfHero::markPossibleSlots(const CArtifactInstance* art, bool withRedraw) -{ - for(CArtifactsOfHero *aoh : commonInfo->participants) - for(auto p : aoh->artWorn) - p.second->selectSlot(art->canBePutAt(ArtifactLocation(aoh->curHero, p.second->slotID), true)); - - if(withRedraw) - safeRedraw(); -} - -/** - * Unamarks all slots. - */ -void CArtifactsOfHero::unmarkSlots(bool withRedraw) -{ - if(commonInfo) - for(CArtifactsOfHero *aoh : commonInfo->participants) - aoh->unmarkLocalSlots(false); - else - unmarkLocalSlots(false); - - if(withRedraw) - safeRedraw(); -} - -void CArtifactsOfHero::unmarkLocalSlots(bool withRedraw) -{ - for(auto & p : artWorn) - p.second->selectSlot(false); - - for(auto & place : backpack) - place->selectSlot(false); - - if(withRedraw) - safeRedraw(); -} - -/** - * Assigns an artifacts to an artifact place depending on it's new slot ID. - */ -void CArtifactsOfHero::setSlotData(ArtPlacePtr artPlace, ArtifactPosition slotID) -{ - if(!artPlace && slotID >= GameConstants::BACKPACK_START) //spurious call from artifactMoved in attempt to update hidden backpack slot - { - return; - } - - artPlace->pickSlot(false); - artPlace->slotID = slotID; - - if(const ArtSlotInfo *asi = curHero->getSlot(slotID)) - { - artPlace->lockSlot(asi->locked); - artPlace->setArtifact(asi->artifact); - } - else - artPlace->setArtifact(nullptr); -} - -/** - * Makes given artifact slot appear as empty with a certain slot ID. - */ -void CArtifactsOfHero::eraseSlotData(ArtPlacePtr artPlace, ArtifactPosition slotID) -{ - artPlace->pickSlot(false); - artPlace->slotID = slotID; - artPlace->setArtifact(nullptr); -} - -CArtifactsOfHero::CArtifactsOfHero(ArtPlaceMap ArtWorn, std::vector Backpack, - std::shared_ptr leftScroll, std::shared_ptr rightScroll, bool createCommonPart) - : curHero(nullptr), - artWorn(ArtWorn), - backpack(Backpack), - backpackPos(0), - commonInfo(nullptr), - leftArtRoll(leftScroll), - rightArtRoll(rightScroll), - allowedAssembling(true), - highlightModeCallback(nullptr) -{ - if(createCommonPart) - { - commonInfo = std::make_shared(); - commonInfo->participants.insert(this); - } - - // Init slots for worn artifacts. - for(auto p : artWorn) - { - p.second->ourOwner = this; - eraseSlotData(p.second, p.first); - } - - // Init slots for the backpack. - for(size_t s=0; sourOwner = this; - eraseSlotData(backpack[s], ArtifactPosition(GameConstants::BACKPACK_START + (si32)s)); - } - - leftArtRoll->addCallback(std::bind(&CArtifactsOfHero::scrollBackpack, this,-1)); - rightArtRoll->addCallback(std::bind(&CArtifactsOfHero::scrollBackpack, this,+1)); -} - -CArtifactsOfHero::CArtifactsOfHero(const Point & position, bool createCommonPart) - : curHero(nullptr), - backpackPos(0), - commonInfo(nullptr), - allowedAssembling(true), - highlightModeCallback(nullptr) -{ - if(createCommonPart) - { - commonInfo = std::make_shared(); - commonInfo->participants.insert(this); - } - - OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); - pos += position; - - std::vector slotPos = - { - Point(509,30), Point(567,240), Point(509,80), //0-2 - Point(383,68), Point(564,183), Point(509,130), //3-5 - Point(431,68), Point(610,183), Point(515,295), //6-8 - Point(383,143), Point(399,194), Point(415,245), //9-11 - Point(431,296), Point(564,30), Point(610,30), //12-14 - Point(610,76), Point(610,122), Point(610,310), //15-17 - Point(381,296) //18 - }; - - // Create slots for worn artifacts. - for(si32 g = 0; g < GameConstants::BACKPACK_START; g++) - { - artWorn[ArtifactPosition(g)] = std::make_shared(slotPos[g]); - artWorn[ArtifactPosition(g)]->ourOwner = this; - eraseSlotData(artWorn[ArtifactPosition(g)], ArtifactPosition(g)); - } - - // Create slots for the backpack. - for(int s=0; s<5; ++s) - { - auto add = std::make_shared(Point(403 + 46 * s, 365)); - - add->ourOwner = this; - eraseSlotData(add, ArtifactPosition(GameConstants::BACKPACK_START + s)); - - backpack.push_back(add); - } - - leftArtRoll = std::make_shared(Point(379, 364), "hsbtns3.def", CButton::tooltip(), [&](){ scrollBackpack(-1);}, SDLK_LEFT); - rightArtRoll = std::make_shared(Point(632, 364), "hsbtns5.def", CButton::tooltip(), [&](){ scrollBackpack(+1);}, SDLK_RIGHT); -} - -CArtifactsOfHero::~CArtifactsOfHero() -{ - dispose(); - // Artifact located in artifactsTransitionPos should be returned - if(!curHero->artifactsTransitionPos.empty()) - { - auto artPlace = getArtPlace( - ArtifactUtils::getArtAnyPosition(curHero, curHero->artifactsTransitionPos.begin()->artifact->getTypeId())); - if(artPlace) - { - assert(artPlace->ourOwner); - artPlace->setMeAsDest(); - artPlace->ourOwner->realizeCurrentTransaction(); - } - else - { - LOCPLINT->cb->eraseArtifactByClient(ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); - } - } -} - -void CArtifactsOfHero::updateParentWindow() -{ - if(CHeroWindow * chw = dynamic_cast(GH.topInt().get())) - { - chw->update(curHero, true); - } - else if(CExchangeWindow * cew = dynamic_cast(GH.topInt().get())) - { - cew->updateWidgets(); - } -} - -void CArtifactsOfHero::safeRedraw() -{ - if (active) - { - if(parent) - parent->redraw(); - else - redraw(); - } -} - -void CArtifactsOfHero::realizeCurrentTransaction() -{ - assert(commonInfo->src.AOH); - assert(commonInfo->dst.AOH); - LOCPLINT->cb->swapArtifacts(ArtifactLocation(commonInfo->src.AOH->curHero, commonInfo->src.slotID), - ArtifactLocation(commonInfo->dst.AOH->curHero, commonInfo->dst.slotID)); -} - -void CArtifactsOfHero::artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst, bool withUIUpdate) -{ - bool isCurHeroSrc = src.isHolder(curHero), - isCurHeroDst = dst.isHolder(curHero); - if(isCurHeroSrc && ArtifactUtils::isSlotBackpack(src.slot)) - updateSlot(src.slot); - if(isCurHeroDst && ArtifactUtils::isSlotBackpack(dst.slot)) - updateSlot(dst.slot); - // We need to update all slots, artifact might be combined and affect more slots - if(isCurHeroSrc || isCurHeroDst) - updateWornSlots(false); - - if(!isCurHeroSrc && !isCurHeroDst) - return; - - // When moving one artifact onto another it leads to two art movements: dst->TRANSITION_POS; src->dst - // however after first movement we pick the art from TRANSITION_POS and the second movement coming when - // we have a different artifact may look surprising... but it's valid. - - // Used when doing dragAndDrop and artifact swap multiple times - if(src.slot == ArtifactPosition::TRANSITION_POS && - commonInfo->src.slotID == ArtifactPosition::TRANSITION_POS && - commonInfo->dst.slotID == ArtifactPosition::PRE_FIRST && - isCurHeroDst) - { - auto art = curHero->getArt(ArtifactPosition::TRANSITION_POS); - assert(art); - CCS->curh->dragAndDropCursor("artifact", art->artType->getIconIndex()); - if(withUIUpdate) - markPossibleSlots(art); - - commonInfo->src.art = art; - commonInfo->src.slotID = src.slot; - } - // Artifact was taken from us - else if(commonInfo->src == src && dst.slot != ArtifactPosition::TRANSITION_POS) - { - // Expected movement from slot ot slot - assert(commonInfo->dst == dst - // Artifact moved back to backpack (eg. to make place for art we are moving) - || dst.slot == dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START - || dst.getHolderArtSet()->bearerType() != ArtBearer::HERO); - commonInfo->reset(); - unmarkSlots(); - } - else - { - // The dest artifact was moved after the swap -> we are picking it - if(commonInfo->dst == src) - { - assert(dst.slot == ArtifactPosition::TRANSITION_POS); - commonInfo->reset(); - - for(CArtifactsOfHero * aoh : commonInfo->participants) - { - if(dst.isHolder(aoh->curHero)) - { - commonInfo->src.AOH = aoh; - break; - } - } - - commonInfo->src.art = dst.getArt(); - commonInfo->src.slotID = dst.slot; - assert(commonInfo->src.AOH); - CCS->curh->dragAndDropCursor("artifact", dst.getArt()->artType->getIconIndex()); - } - if(!curHero->artifactsTransitionPos.empty() && withUIUpdate) - { - auto artInst = curHero->getArt(ArtifactPosition::TRANSITION_POS); - markPossibleSlots(artInst); - CCS->curh->dragAndDropCursor("artifact", artInst->artType->getIconIndex()); - } - } - - if(withUIUpdate) - { - updateParentWindow(); - scrollBackpack(0); - } -} - -void CArtifactsOfHero::artifactRemoved(const ArtifactLocation &al) -{ - if(al.isHolder(curHero)) - { - if(al.slot < GameConstants::BACKPACK_START) - updateWornSlots(0); - else - scrollBackpack(0); //update backpack slots - } -} - -CArtifactsOfHero::ArtPlacePtr CArtifactsOfHero::getArtPlace(ArtifactPosition slot) -{ - if(ArtifactUtils::isSlotEquipment(slot)) - { - if(artWorn.find(slot) == artWorn.end()) - { - logGlobal->error("CArtifactsOfHero::getArtPlace: invalid slot %d", slot); - return nullptr; - } - - return artWorn[slot]; - } - if(ArtifactUtils::isSlotBackpack(slot)) - { - for(ArtPlacePtr ap : backpack) - if(ap->slotID == slot) - return ap; - return nullptr; - } - else - { - return nullptr; - } -} - -void CArtifactsOfHero::artifactUpdateSlots(const ArtifactLocation & al) -{ - if(al.isHolder(curHero)) - { - if(ArtifactUtils::isSlotBackpack(al.slot)) - updateBackpackSlots(true); - else - updateWornSlots(true); - } -} - -void CArtifactsOfHero::updateWornSlots(bool redrawParent) -{ - for(auto p : artWorn) - updateSlot(p.first); - - if(redrawParent) - updateParentWindow(); -} - -void CArtifactsOfHero::updateBackpackSlots(bool redrawParent) -{ - for(auto artPlace : backpack) - updateSlot(artPlace->slotID); - scrollBackpack(0); - - if(redrawParent) - updateParentWindow(); -} - -const CGHeroInstance * CArtifactsOfHero::getHero() const -{ - return curHero; -} - -void CArtifactsOfHero::updateSlot(ArtifactPosition slotID) -{ - setSlotData(getArtPlace(slotID), slotID); -} - -CArtifactHolder::CArtifactHolder() -{ -} - -void CWindowWithArtifacts::addSet(std::shared_ptr artSet) -{ - artSets.emplace_back(artSet); -} - -std::shared_ptr CWindowWithArtifacts::getCommonPart() -{ - for(auto artSetWeak : artSets) - { - std::shared_ptr realPtr = artSetWeak.lock(); - if(realPtr) - return realPtr->commonInfo; - } - - return std::shared_ptr(); -} - -void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation &artLoc) -{ - for(auto artSetWeak : artSets) - { - std::shared_ptr realPtr = artSetWeak.lock(); - if(realPtr) - realPtr->artifactRemoved(artLoc); - } -} - -void CWindowWithArtifacts::artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc, bool withRedraw) -{ - CArtifactsOfHero * destaoh = nullptr; - - for(auto artSetWeak : artSets) - { - std::shared_ptr realPtr = artSetWeak.lock(); - if(realPtr) - { - realPtr->artifactMoved(artLoc, destLoc, withRedraw); - if(destLoc.isHolder(realPtr->getHero())) - destaoh = realPtr.get(); - } - } - - //Make sure the status bar is updated so it does not display old text - if(destaoh != nullptr && destaoh->getArtPlace(destLoc.slot) != nullptr) - { - destaoh->getArtPlace(destLoc.slot)->hover(true); - } -} - -void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation &artLoc) -{ - for(auto artSetWeak : artSets) - { - std::shared_ptr realPtr = artSetWeak.lock(); - if(realPtr) - realPtr->artifactUpdateSlots(artLoc); - } -} - -void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation &artLoc) -{ - for(auto artSetWeak : artSets) - { - std::shared_ptr realPtr = artSetWeak.lock(); - if(realPtr) - realPtr->artifactUpdateSlots(artLoc); - } -} - -void CArtifactsOfHero::SCommonPart::Artpos::clear() -{ - slotID = ArtifactPosition::PRE_FIRST; - AOH = nullptr; - art = nullptr; -} - -void CArtifactsOfHero::SCommonPart::Artpos::setTo(const CHeroArtPlace *place, bool dontTakeBackpack) -{ - slotID = place->slotID; - AOH = place->ourOwner; - - if(ArtifactUtils::isSlotBackpack(slotID) && dontTakeBackpack) - art = nullptr; - else - art = place->ourArt; -} - -bool CArtifactsOfHero::SCommonPart::Artpos::operator==(const ArtifactLocation &al) const -{ - if(!AOH) - return false; - bool ret = al.isHolder(AOH->curHero) && al.slot == slotID; - - //assert(al.getArt() == art); - return ret; -} - -bool CArtifactsOfHero::SCommonPart::Artpos::valid() -{ - assert(AOH && art); - return art == AOH->curHero->getArt(slotID); -} - -CArtPlace::CArtPlace(Point position, const CArtifactInstance * Art) : ourArt(Art) +CArtPlace::CArtPlace(Point position, const CArtifactInstance * Art) + : ourArt(Art) { image = nullptr; pos += position; @@ -993,27 +81,23 @@ void CArtPlace::clickRight(tribool down, bool previousState) LRClickableAreaWTextComp::clickRight(down, previousState); } -CCommanderArtPlace::CCommanderArtPlace(Point position, const CGHeroInstance * commanderOwner, ArtifactPosition artSlot, const CArtifactInstance * Art) : CArtPlace(position, Art), commanderOwner(commanderOwner), commanderSlotID(artSlot.num) +const CArtifactInstance * CArtPlace::getArt() +{ + return ourArt; +} + +CCommanderArtPlace::CCommanderArtPlace(Point position, const CGHeroInstance * commanderOwner, ArtifactPosition artSlot, const CArtifactInstance * Art) + : CArtPlace(position, Art), + commanderOwner(commanderOwner), + commanderSlotID(artSlot.num) { createImage(); setArtifact(Art); } -void CCommanderArtPlace::clickLeft(tribool down, bool previousState) -{ - if (ourArt && text.size() && down) - LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.commanderWindow.artifactMessage"), [this](){ returnArtToHeroCallback(); }, [](){}); -} - -void CCommanderArtPlace::clickRight(tribool down, bool previousState) -{ - if (ourArt && text.size() && down) - CArtPlace::clickRight(down, previousState); -} - void CCommanderArtPlace::createImage() { - OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE); int imageIndex = 0; if(ourArt) @@ -1046,37 +130,929 @@ void CCommanderArtPlace::returnArtToHeroCallback() } } +void CCommanderArtPlace::clickLeft(tribool down, bool previousState) +{ + if(ourArt && text.size() && down) + LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.commanderWindow.artifactMessage"), [this]() { returnArtToHeroCallback(); }, []() {}); +} + +void CCommanderArtPlace::clickRight(tribool down, bool previousState) +{ + if(ourArt && text.size() && down) + CArtPlace::clickRight(down, previousState); +} + void CCommanderArtPlace::setArtifact(const CArtifactInstance * art) { - baseType = -1; //by default we don't store any component - ourArt = art; - if (!art) + setInternals(art); +} + +CHeroArtPlace::CHeroArtPlace(Point position, const CArtifactInstance * Art) + : CArtPlace(position, Art), + locked(false), + marked(false) +{ + createImage(); +} + +void CHeroArtPlace::lockSlot(bool on) +{ + if(locked == on) + return; + + locked = on; + + if(on) + image->setFrame(ArtifactID::ART_LOCK); + else if(ourArt) + image->setFrame(ourArt->artType->getIconIndex()); + else + image->setFrame(0); +} + +bool CHeroArtPlace::isLocked() +{ + return locked; +} + +void CHeroArtPlace::selectSlot(bool on) +{ + if(marked == on) + return; + + marked = on; + if(on) + selection->enable(); + else + selection->disable(); +} + +bool CHeroArtPlace::isMarked() const +{ + return marked; +} + +void CHeroArtPlace::clickLeft(tribool down, bool previousState) +{ + if(down || !previousState) + return; + + if(leftClickCallback) + leftClickCallback(*this); +} + +void CHeroArtPlace::clickRight(tribool down, bool previousState) +{ + if(down) { + if(rightClickCallback) + rightClickCallback(*this); + } +} + +void CHeroArtPlace::showAll(SDL_Surface* to) +{ + if(ourArt) + { + CIntObject::showAll(to); + } + + if(marked && active) + { + // Draw vertical bars. + for(int i = 0; i < pos.h; ++i) + { + CSDL_Ext::putPixelWithoutRefresh(to, pos.x, pos.y + i, 240, 220, 120); + CSDL_Ext::putPixelWithoutRefresh(to, pos.x + pos.w - 1, pos.y + i, 240, 220, 120); + } + + // Draw horizontal bars. + for(int i = 0; i < pos.w; ++i) + { + CSDL_Ext::putPixelWithoutRefresh(to, pos.x + i, pos.y, 240, 220, 120); + CSDL_Ext::putPixelWithoutRefresh(to, pos.x + i, pos.y + pos.h - 1, 240, 220, 120); + } + } +} + +void CHeroArtPlace::setArtifact(const CArtifactInstance * art) +{ + setInternals(art); + if(art) + { + image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->getIconIndex()); + + if(locked) // Locks should appear as empty. + hoverText = CGI->generaltexth->allTexts[507]; + else + hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->getNameTranslated()); + } + else + { + lockSlot(false); + } +} + +void CHeroArtPlace::addCombinedArtInfo(std::map & arts) +{ + for(const auto combinedArt : arts) + { + std::string artList; + text += "\n\n"; + text += "{" + combinedArt.first->getNameTranslated() + "}"; + if(arts.size() == 1) + { + for(const auto part : *combinedArt.first->constituents) + artList += "\n" + part->getNameTranslated(); + } + text += " (" + boost::str(boost::format("%d") % combinedArt.second) + " / " + + boost::str(boost::format("%d") % combinedArt.first->constituents->size()) + ")" + artList; + } +} + +void CHeroArtPlace::createImage() +{ + OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + + si32 imageIndex = 0; + + if(locked) + imageIndex = ArtifactID::ART_LOCK; + else if(ourArt) + imageIndex = ourArt->artType->getIconIndex(); + + image = std::make_shared("artifact", imageIndex); + if(!ourArt) image->disable(); - text.clear(); + + selection = std::make_shared("artifact", ArtifactID::ART_SELECTION); + selection->disable(); +} + +CArtifactsOfHeroBase::CArtifactsOfHeroBase() + : backpackPos(0), + curHero(nullptr) +{ +} + +CArtifactsOfHeroBase::~CArtifactsOfHeroBase() +{ + // TODO: cursor handling is CWindowWithArtifacts level. Should be moved when trading, kingdom and hero window are reworked + // This will interfere with the implementation of a separate backpack window + CCS->curh->dragAndDropCursor(nullptr); + + // Artifact located in artifactsTransitionPos should be returned + if(getPickedArtifact()) + { + auto slot = ArtifactUtils::getArtAnyPosition(curHero, curHero->artifactsTransitionPos.begin()->artifact->getTypeId()); + if(slot == ArtifactPosition::PRE_FIRST) + { + LOCPLINT->cb->eraseArtifactByClient(ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); + } + else + { + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS), ArtifactLocation(curHero, slot)); + } + } +} + +void CArtifactsOfHeroBase::init( + CHeroArtPlace::ClickHandler lClickCallback, + CHeroArtPlace::ClickHandler rClickCallback, + const Point & position, + BpackScrollHandler scrollHandler) +{ + // CArtifactsOfHeroBase::init may be transform to CArtifactsOfHeroBase::CArtifactsOfHeroBase if OBJECT_CONSTRUCTION_CAPTURING is removed + OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE); + pos += position; + for(int g = 0; g < GameConstants::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(nullptr); + artPlace.second->leftClickCallback = lClickCallback; + artPlace.second->rightClickCallback = rClickCallback; + } + for(auto artPlace : backpack) + { + artPlace->setArtifact(nullptr); + artPlace->leftClickCallback = lClickCallback; + artPlace->rightClickCallback = rClickCallback; + } + leftBackpackRoll = std::make_shared(Point(379, 364), "hsbtns3.def", CButton::tooltip(), [scrollHandler]() { scrollHandler(-1); }, SDLK_LEFT); + rightBackpackRoll = std::make_shared(Point(632, 364), "hsbtns5.def", CButton::tooltip(), [scrollHandler]() { scrollHandler(+1); }, SDLK_RIGHT); + leftBackpackRoll->block(true); + rightBackpackRoll->block(true); +} + +void CArtifactsOfHeroBase::leftClickArtPlace(CHeroArtPlace & artPlace) +{ + if(leftClickCallback) + leftClickCallback(*this, artPlace); +} + +void CArtifactsOfHeroBase::rightClickArtPlace(CHeroArtPlace & artPlace) +{ + if(rightClickCallback) + rightClickCallback(*this, artPlace); +} + +void CArtifactsOfHeroBase::setHero(const CGHeroInstance * hero) +{ + curHero = hero; + if(curHero->artifactsInBackpack.size() > 0) + backpackPos %= curHero->artifactsInBackpack.size(); + else + backpackPos = 0; + + for(auto slot : artWorn) + { + setSlotData(slot.second, slot.first, *curHero); + } + scrollBackpackForArtSet(0, *curHero); +} + +const CGHeroInstance * CArtifactsOfHeroBase::getHero() const +{ + return curHero; +} + +void CArtifactsOfHeroBase::scrollBackpack(int offset) +{ + scrollBackpackForArtSet(offset, *curHero); + safeRedraw(); +} + +void CArtifactsOfHeroBase::scrollBackpackForArtSet(int offset, const CArtifactSet & artSet) +{ + // offset==-1 => to left; offset==1 => to right + using slotInc = std::function; + auto artsInBackpack = static_cast(artSet.artifactsInBackpack.size()); + auto scrollingPossible = artsInBackpack > backpack.size(); + + slotInc inc_straight = [](ArtifactPosition & slot) -> ArtifactPosition + { + return slot + 1; + }; + slotInc inc_ring = [artsInBackpack](ArtifactPosition & slot) -> ArtifactPosition + { + return ArtifactPosition(GameConstants::BACKPACK_START + (slot - GameConstants::BACKPACK_START + 1) % artsInBackpack); + }; + slotInc inc; + if(scrollingPossible) + inc = inc_ring; + else + inc = inc_straight; + + backpackPos += offset; + if(backpackPos < 0) + backpackPos += artsInBackpack; + + if(artsInBackpack) + backpackPos %= artsInBackpack; + + auto slot = ArtifactPosition(GameConstants::BACKPACK_START + backpackPos); + for(auto artPlace : backpack) + { + setSlotData(artPlace, slot, artSet); + slot = inc(slot); + } + + // Blocking scrolling if there is not enough artifacts to scroll + leftBackpackRoll->block(!scrollingPossible); + rightBackpackRoll->block(!scrollingPossible); +} + +void CArtifactsOfHeroBase::safeRedraw() +{ + if(active) + { + if(parent) + parent->redraw(); + else + redraw(); + } +} + +void CArtifactsOfHeroBase::markPossibleSlots(const CArtifactInstance * art, bool assumeDestRemoved) +{ + for(auto artPlace : artWorn) + artPlace.second->selectSlot(art->artType->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)) + { + if(artWorn.find(slot) == artWorn.end()) + { + logGlobal->error("CArtifactsOfHero::getArtPlace: invalid slot %d", slot); + return nullptr; + } + return artWorn[slot]; + } + if(ArtifactUtils::isSlotBackpack(slot)) + { + for(ArtPlacePtr artPlace : backpack) + if(artPlace->slot == slot) + return artPlace; + return nullptr; + } + else + { + return nullptr; + } +} + +void CArtifactsOfHeroBase::updateWornSlots() +{ + for(auto place : artWorn) + updateSlot(place.first); +} + +void CArtifactsOfHeroBase::updateBackpackSlots() +{ + for(auto artPlace : backpack) + updateSlot(artPlace->slot); + scrollBackpackForArtSet(0, *curHero); +} + +void CArtifactsOfHeroBase::updateSlot(const ArtifactPosition & slot) +{ + setSlotData(getArtPlace(slot), slot, *curHero); +} + +const CArtifactInstance * CArtifactsOfHeroBase::getPickedArtifact() +{ + // Returns only the picked up artifact. Not just highlighted like in the trading window. + if(!curHero || curHero->artifactsTransitionPos.empty()) + return nullptr; + else + return curHero->getArt(ArtifactPosition::TRANSITION_POS); +} + +void CArtifactsOfHeroBase::setSlotData(ArtPlacePtr artPlace, const ArtifactPosition & slot, const CArtifactSet & artSet) +{ + // Spurious call from artifactMoved in attempt to update hidden backpack slot + if(!artPlace && ArtifactUtils::isSlotBackpack(slot)) + { return; } - image->enable(); - image->setFrame(art->artType->getIconIndex()); - - text = art->getDescription(); - - if (art->artType->getId() == ArtifactID::SPELL_SCROLL) + artPlace->slot = slot; + if(auto slotInfo = artSet.getSlot(slot)) { - int spellID = art->getScrollSpellID(); - if (spellID >= 0) + artPlace->lockSlot(slotInfo->locked); + artPlace->setArtifact(slotInfo->artifact); + if(!slotInfo->artifact->canBeDisassembled()) { - //add spell component info (used to provide a pic in r-click popup) - baseType = CComponent::spell; - type = spellID; - bonusValue = 0; + // If the artifact is part of at least one combined artifact, add additional information + std::map arts; + for(const auto combinedArt : slotInfo->artifact->artType->constituentOf) + { + arts.insert(std::pair(combinedArt, 0)); + for(const auto part : *combinedArt->constituents) + if(artSet.hasArt(part->getId(), true)) + arts.at(combinedArt)++; + } + artPlace->addCombinedArtInfo(arts); } } else { - baseType = CComponent::artifact; - type = art->artType->getId(); - bonusValue = 0; + artPlace->setArtifact(nullptr); } } + +CArtifactsOfHeroMain::CArtifactsOfHeroMain(const Point & position) +{ + init( + std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1), + std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1), + position, + std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, _1)); +} + +void CArtifactsOfHeroMain::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc) +{ + LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc); +} + +void CArtifactsOfHeroMain::pickUpArtifact(CHeroArtPlace & artPlace) +{ + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, artPlace.slot), + ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); +} + +CArtifactsOfHeroKingdom::CArtifactsOfHeroKingdom(ArtPlaceMap ArtWorn, std::vector Backpack, + std::shared_ptr leftScroll, std::shared_ptr rightScroll) +{ + artWorn = ArtWorn; + backpack = Backpack; + leftBackpackRoll = leftScroll; + rightBackpackRoll = rightScroll; + + for(auto artPlace : artWorn) + { + artPlace.second->slot = artPlace.first; + artPlace.second->setArtifact(nullptr); + artPlace.second->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1); + artPlace.second->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1); + } + for(auto artPlace : backpack) + { + artPlace->setArtifact(nullptr); + artPlace->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1); + artPlace->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1); + } + leftBackpackRoll->addCallback(std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, -1)); + rightBackpackRoll->addCallback(std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, +1)); +} + +void CArtifactsOfHeroKingdom::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc) +{ + LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc); +} + +void CArtifactsOfHeroKingdom::pickUpArtifact(CHeroArtPlace & artPlace) +{ + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, artPlace.slot), + ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); +} + +CArtifactsOfHeroAltar::CArtifactsOfHeroAltar(const Point & position) +{ + init( + std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1), + std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1), + position, + std::bind(&CArtifactsOfHeroAltar::scrollBackpack, this, _1)); + visibleArtSet = std::make_shared(ArtBearer::ArtBearer::HERO); + pickedArtFromSlot = ArtifactPosition::PRE_FIRST; +}; + +void CArtifactsOfHeroAltar::setHero(const CGHeroInstance * hero) +{ + if(hero) + { + visibleArtSet->artifactsWorn = hero->artifactsWorn; + visibleArtSet->artifactsInBackpack = hero->artifactsInBackpack; + CArtifactsOfHeroBase::setHero(hero); + } +} + +void CArtifactsOfHeroAltar::updateWornSlots() +{ + for(auto place : artWorn) + setSlotData(getArtPlace(place.first), place.first, *visibleArtSet); +} + +void CArtifactsOfHeroAltar::updateBackpackSlots() +{ + for(auto artPlace : backpack) + setSlotData(getArtPlace(artPlace->slot), artPlace->slot, *visibleArtSet); +} + +void CArtifactsOfHeroAltar::scrollBackpack(int offset) +{ + CArtifactsOfHeroBase::scrollBackpackForArtSet(offset, *visibleArtSet); + safeRedraw(); +} + +void CArtifactsOfHeroAltar::pickUpArtifact(CHeroArtPlace & artPlace) +{ + if(const auto art = artPlace.getArt()) + { + pickedArtFromSlot = artPlace.slot; + artPlace.setArtifact(nullptr); + deleteFromVisible(art); + if(ArtifactUtils::isSlotBackpack(pickedArtFromSlot)) + pickedArtFromSlot = curHero->getSlotByInstance(art); + assert(pickedArtFromSlot != ArtifactPosition::PRE_FIRST); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, pickedArtFromSlot), ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); + } +} + +void CArtifactsOfHeroAltar::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc) +{ + LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc); + const auto pickedArtInst = curHero->getArt(ArtifactPosition::TRANSITION_POS); + assert(pickedArtInst); + visibleArtSet->putArtifact(dstLoc.slot, const_cast(pickedArtInst)); +} + +void CArtifactsOfHeroAltar::pickedArtMoveToAltar(const ArtifactPosition & slot) +{ + if(ArtifactUtils::isSlotBackpack(slot) || ArtifactUtils::isSlotEquipment(slot) || slot == ArtifactPosition::TRANSITION_POS) + { + assert(!curHero->getSlot(pickedArtFromSlot)->getArt()); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, slot), ArtifactLocation(curHero, pickedArtFromSlot)); + pickedArtFromSlot = ArtifactPosition::PRE_FIRST; + } +} + +void CArtifactsOfHeroAltar::deleteFromVisible(const CArtifactInstance * artInst) +{ + const auto slot = visibleArtSet->getSlotByInstance(artInst); + visibleArtSet->removeArtifact(slot); + if(ArtifactUtils::isSlotBackpack(slot)) + { + scrollBackpackForArtSet(0, *visibleArtSet); + } + else + { + if(artInst->canBeDisassembled()) + { + for(const auto part : dynamic_cast(artInst)->constituentsInfo) + { + if(part.slot != ArtifactPosition::PRE_FIRST) + getArtPlace(part.slot)->setArtifact(nullptr); + } + } + } +} + +CArtifactsOfHeroMarket::CArtifactsOfHeroMarket(const Point & position) +{ + init( + std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1), + std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1), + position, + std::bind(&CArtifactsOfHeroMarket::scrollBackpack, this, _1)); +}; + +void CArtifactsOfHeroMarket::scrollBackpack(int offset) +{ + CArtifactsOfHeroBase::scrollBackpackForArtSet(offset, *curHero); + + // We may have highlight on one of backpack artifacts + if(selectArtCallback) + { + for(auto & artPlace : backpack) + { + if(artPlace->isMarked()) + { + selectArtCallback(artPlace.get()); + break; + } + } + } + safeRedraw(); +} + +void CWindowWithArtifacts::addSet(CArtifactsOfHeroPtr artSet) +{ + artSets.emplace_back(artSet); + std::visit([this](auto artSetWeak) + { + auto artSet = artSetWeak.lock(); + artSet->leftClickCallback = std::bind(&CWindowWithArtifacts::leftClickArtPlaceHero, this, _1, _2); + artSet->rightClickCallback = std::bind(&CWindowWithArtifacts::rightClickArtPlaceHero, this, _1, _2); + }, artSet); +} + +const CGHeroInstance * CWindowWithArtifacts::getHeroPickedArtifact() +{ + auto res = getState(); + if(res.has_value()) + return std::get(res.value()); + else + return nullptr; +} + +const CArtifactInstance * CWindowWithArtifacts::getPickedArtifact() +{ + auto res = getState(); + if(res.has_value()) + return std::get(res.value()); + else + return nullptr; +} + +void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace) +{ + const auto artSetWeak = findAOHbyRef(artsInst); + assert(artSetWeak.has_value()); + + if(artPlace.isLocked()) + return; + + const auto checkSpecialArts = [](const CGHeroInstance * hero, CHeroArtPlace & artPlace) -> bool + { + if(artPlace.getArt()->getTypeId() == ArtifactID::SPELLBOOK) + { + GH.pushIntT(hero, LOCPLINT, LOCPLINT->battleInt.get()); + return false; + } + if(artPlace.getArt()->getTypeId() == ArtifactID::CATAPULT) + { + // The Catapult must be equipped + std::vector> catapult(1, std::make_shared(CComponent::artifact, 3, 0)); + LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult); + return false; + } + return true; + }; + + std::visit( + [checkSpecialArts, this, &artPlace](auto artSetWeak) -> void + { + const auto artSetPtr = artSetWeak.lock(); + constexpr auto isMainWindow = std::is_same_v>; + constexpr auto isKingdomWindow = std::is_same_v>; + constexpr auto isAltarWindow = std::is_same_v>; + constexpr auto isMarketWindow = std::is_same_v>; + + // Hero(Main, Exchange) window, Kingdom window, Altar window left click handler + if constexpr(isMainWindow || isKingdomWindow || isAltarWindow) + { + const auto pickedArtInst = getPickedArtifact(); + const auto heroPickedArt = getHeroPickedArtifact(); + const auto hero = artSetPtr->getHero(); + + if(pickedArtInst) + { + auto srcLoc = ArtifactLocation(heroPickedArt, ArtifactPosition::TRANSITION_POS); + auto dstLoc = ArtifactLocation(hero, artPlace.slot); + auto isTransferAllowed = false; + + if(ArtifactUtils::isSlotBackpack(artPlace.slot)) + { + if(pickedArtInst->artType->isBig()) + { + // War machines cannot go to backpack + LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % pickedArtInst->artType->getNameTranslated())); + } + else + { + if(ArtifactUtils::isBackpackFreeSlots(heroPickedArt)) + isTransferAllowed = true; + else + LOCPLINT->showInfoDialog(CGI->generaltexth->translate("core.genrltxt.152")); + } + } + // Check if artifact transfer is possible + else if(pickedArtInst->canBePutAt(dstLoc, true) && (!artPlace.getArt() || hero->tempOwner == LOCPLINT->playerID)) + { + isTransferAllowed = true; + } + if constexpr(isKingdomWindow) + { + if(hero != heroPickedArt) + isTransferAllowed = false; + } + if(isTransferAllowed) + artSetPtr->swapArtifacts(srcLoc, dstLoc); + } + else + { + if(artPlace.getArt()) + { + if(artSetPtr->getHero()->tempOwner == LOCPLINT->playerID) + { + if(checkSpecialArts(hero, artPlace)) + artSetPtr->pickUpArtifact(artPlace); + } + else + { + for(const auto artSlot : ArtifactUtils::unmovableSlots()) + if(artPlace.slot == artSlot) + { + LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]); + break; + } + } + } + } + } + // Market window left click handler + else if constexpr(isMarketWindow) + { + if(artSetPtr->selectArtCallback && artPlace.getArt()) + { + if(artPlace.getArt()->artType->isTradable()) + { + artSetPtr->unmarkSlots(); + artPlace.selectSlot(true); + artSetPtr->selectArtCallback(&artPlace); + } + else + { + // This item can't be traded + LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]); + } + } + } + }, artSetWeak.value()); +} + +void CWindowWithArtifacts::rightClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace) +{ + const auto artSetWeak = findAOHbyRef(artsInst); + assert(artSetWeak.has_value()); + + if(artPlace.isLocked()) + return; + + std::visit( + [&artPlace](auto artSetWeak) -> void + { + const auto artSetPtr = artSetWeak.lock(); + constexpr auto isMainWindow = std::is_same_v>; + constexpr auto isKingdomWindow = std::is_same_v>; + constexpr auto isAltarWindow = std::is_same_v>; + constexpr auto isMarketWindow = std::is_same_v>; + + // Hero(Main, Exchange) window, Kingdom window right click handler + if constexpr(isMainWindow || isKingdomWindow) + { + if(artPlace.getArt()) + { + if(ArtifactUtils::askToDisassemble(artSetPtr->getHero(), artPlace.slot)) + { + return; + } + if(ArtifactUtils::askToAssemble(artSetPtr->getHero(), artPlace.slot)) + { + return; + } + if(artPlace.text.size()) + artPlace.LRClickableAreaWTextComp::clickRight(boost::logic::tribool::true_value, false); + } + } + // Altar window, Market window right click handler + else if constexpr(isAltarWindow || isMarketWindow) + { + if(artPlace.getArt() && artPlace.text.size()) + artPlace.LRClickableAreaWTextComp::clickRight(boost::logic::tribool::true_value, false); + } + }, artSetWeak.value()); +} + +void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc) +{ + updateSlots(artLoc.slot); +} + +void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) +{ + auto curState = getState(); + if(!curState.has_value()) + // Transition state. Nothing to do here. Just skip. Need to wait for final state. + return; + + // When moving one artifact onto another it leads to two art movements: dst->TRANSITION_POS; src->dst + // However after first movement we pick the art from TRANSITION_POS and the second movement coming when + // we have a different artifact may look surprising... but it's valid. + + auto pickedArtInst = std::get(curState.value()); + assert(srcLoc.isHolder(std::get(curState.value()))); + assert(srcLoc.getArt() == pickedArtInst); + + auto artifactMovedBody = [this, withRedraw, &srcLoc, &destLoc, &pickedArtInst](auto artSetWeak) -> void + { + auto artSetPtr = artSetWeak.lock(); + if(artSetPtr) + { + const auto hero = artSetPtr->getHero(); + if(artSetPtr->active) + { + if(pickedArtInst) + { + CCS->curh->dragAndDropCursor("artifact", pickedArtInst->artType->getIconIndex()); + if(srcLoc.isHolder(hero) || !std::is_same_v>) + artSetPtr->markPossibleSlots(pickedArtInst, hero->tempOwner == LOCPLINT->playerID); + } + else + { + artSetPtr->unmarkSlots(); + CCS->curh->dragAndDropCursor(nullptr); + } + } + if(withRedraw) + { + artSetPtr->updateWornSlots(); + artSetPtr->updateBackpackSlots(); + + // Update arts bonuses on window. + // TODO rework this part when CHeroWindow and CExchangeWindow are reworked + if(auto * chw = dynamic_cast(this)) + { + chw->update(hero, true); + } + else if(auto * cew = dynamic_cast(this)) + { + cew->updateWidgets(); + } + artSetPtr->safeRedraw(); + } + + // Make sure the status bar is updated so it does not display old text + if(destLoc.isHolder(hero)) + { + if(auto artPlace = artSetPtr->getArtPlace(destLoc.slot)) + artPlace->hover(true); + } + } + }; + + for(auto artSetWeak : artSets) + std::visit(artifactMovedBody, artSetWeak); +} + +void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation & artLoc) +{ + updateSlots(artLoc.slot); +} + +void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation & artLoc) +{ + updateSlots(artLoc.slot); +} + +void CWindowWithArtifacts::updateSlots(const ArtifactPosition & slot) +{ + auto updateSlotBody = [slot](auto artSetWeak) -> void + { + if(const auto artSetPtr = artSetWeak.lock()) + { + if(ArtifactUtils::isSlotEquipment(slot)) + artSetPtr->updateWornSlots(); + else if(ArtifactUtils::isSlotBackpack(slot)) + artSetPtr->updateBackpackSlots(); + + artSetPtr->safeRedraw(); + } + }; + + for(auto artSetWeak : artSets) + std::visit(updateSlotBody, artSetWeak); +} + +std::optional> CWindowWithArtifacts::getState() +{ + const CArtifactInstance * artInst = nullptr; + const CGHeroInstance * hero = nullptr; + size_t pickedCnt = 0; + + auto getHeroArtBody = [&hero, &artInst, &pickedCnt](auto artSetWeak) -> void + { + auto artSetPtr = artSetWeak.lock(); + if(artSetPtr) + { + if(const auto art = artSetPtr->getPickedArtifact()) + { + artInst = art; + hero = artSetPtr->getHero(); + pickedCnt += hero->artifactsTransitionPos.size(); + } + } + }; + for(auto artSetWeak : artSets) + std::visit(getHeroArtBody, artSetWeak); + + // The state is possible when the hero has placed an artifact in the ArtifactPosition::TRANSITION_POS, + // and the previous artifact has not yet removed from the ArtifactPosition::TRANSITION_POS. + // This is a transitional state. Then return nullopt. + if(pickedCnt > 1) + return std::nullopt; + else + return std::make_tuple(hero, artInst); +} + +std::optional CWindowWithArtifacts::findAOHbyRef(CArtifactsOfHeroBase & artsInst) +{ + std::optional res; + + auto findAOHBody = [&res, &artsInst](auto & artSetWeak) -> void + { + if(&artsInst == artSetWeak.lock().get()) + res = artSetWeak; + }; + + for(auto artSetWeak : artSets) + { + std::visit(findAOHBody, artSetWeak); + if(res.has_value()) + return res; + } + return res; +} diff --git a/client/widgets/CArtifactHolder.h b/client/widgets/CArtifactHolder.h index 50040e476..a8362fa78 100644 --- a/client/widgets/CArtifactHolder.h +++ b/client/widgets/CArtifactHolder.h @@ -14,37 +14,40 @@ VCMI_LIB_NAMESPACE_BEGIN struct ArtifactLocation; +class CArtifactSet; +class CArtifactFittingSet; VCMI_LIB_NAMESPACE_END -class CArtifactsOfHero; +class CArtifactsOfHeroBase; class CAnimImage; class CButton; class CArtifactHolder { public: - CArtifactHolder(); - - virtual void artifactRemoved(const ArtifactLocation &artLoc)=0; - virtual void artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc, bool withRedraw)=0; - virtual void artifactDisassembled(const ArtifactLocation &artLoc)=0; - virtual void artifactAssembled(const ArtifactLocation &artLoc)=0; + virtual void artifactRemoved(const ArtifactLocation & artLoc)=0; + virtual void artifactMoved(const ArtifactLocation & artLoc, const ArtifactLocation & destLoc, bool withRedraw)=0; + virtual void artifactDisassembled(const ArtifactLocation & artLoc)=0; + virtual void artifactAssembled(const ArtifactLocation & artLoc)=0; }; class CArtPlace : public LRClickableAreaWTextComp { protected: std::shared_ptr image; - virtual void createImage()=0; -public: - const CArtifactInstance * ourArt; // should be changed only with setArtifact() + const CArtifactInstance * ourArt; + void setInternals(const CArtifactInstance * artInst); + virtual void createImage()=0; + +public: CArtPlace(Point position, const CArtifactInstance * Art = nullptr); void clickLeft(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override; + const CArtifactInstance * getArt(); - virtual void setArtifact(const CArtifactInstance *art)=0; + virtual void setArtifact(const CArtifactInstance * art)=0; }; class CCommanderArtPlace : public CArtPlace @@ -55,138 +58,165 @@ protected: void createImage() override; void returnArtToHeroCallback(); + public: CCommanderArtPlace(Point position, const CGHeroInstance * commanderOwner, ArtifactPosition artSlot, const CArtifactInstance * Art = nullptr); void clickLeft(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override; - - virtual void setArtifact(const CArtifactInstance * art) override; - + void setArtifact(const CArtifactInstance * art) override; }; -/// Artifacts can be placed there. Gets shown at the hero window class CHeroArtPlace: public CArtPlace { - std::shared_ptr selection; - - void createImage() override; - public: - // consider these members as const - change them only with appropriate methods e.g. lockSlot() - bool locked; - bool picked; - bool marked; + using ClickHandler = std::function; - ArtifactPosition slotID; //Arts::EPOS enum + backpack starting from Arts::BACKPACK_START - - CArtifactsOfHero * ourOwner; + ArtifactPosition slot; + ClickHandler leftClickCallback; + ClickHandler rightClickCallback; CHeroArtPlace(Point position, const CArtifactInstance * Art = nullptr); - void lockSlot(bool on); - void pickSlot(bool on); + bool isLocked(); void selectSlot(bool on); - + bool isMarked() const; void clickLeft(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override; - void select(); void showAll(SDL_Surface * to) override; - bool fitsHere (const CArtifactInstance * art) const; //returns true if given artifact can be placed here + void setArtifact(const CArtifactInstance * art) override; + void addCombinedArtInfo(std::map & arts); - void setMeAsDest(bool backpackAsVoid = true); - void setArtifact(const CArtifactInstance *art) override; - static bool askToAssemble(const CGHeroInstance * hero, ArtifactPosition slot); - static bool askToDisassemble(const CGHeroInstance * hero, ArtifactPosition slot); +protected: + std::shared_ptr selection; + bool locked; + bool marked; + + void createImage() override; }; -/// Contains artifacts of hero. Distincts which artifacts are worn or backpacked -class CArtifactsOfHero : public CIntObject +class CArtifactsOfHeroBase : public CIntObject { -public: +protected: using ArtPlacePtr = std::shared_ptr; + using BpackScrollHandler = std::function; + +public: using ArtPlaceMap = std::map; - - struct SCommonPart - { - struct Artpos - { - ArtifactPosition slotID; - const CArtifactsOfHero *AOH; - const CArtifactInstance *art; - - void clear(); - void setTo(const CHeroArtPlace *place, bool dontTakeBackpack); - bool valid(); - bool operator==(const ArtifactLocation &al) const; - } src, dst; - - std::set participants; // Needed to mark slots. - - void reset(); - }; - std::shared_ptr commonInfo; //when we have more than one CArtifactsOfHero in one window with exchange possibility, we use this (eg. in exchange window); to be provided externally - - std::shared_ptr leftArtRoll; - std::shared_ptr rightArtRoll; - bool allowedAssembling; - - std::multiset artifactsOnAltar; //artifacts id that are technically present in backpack but in GUI are moved to the altar - they'll be omitted in backpack slots - std::function highlightModeCallback; //if set, clicking on art place doesn't pick artifact but highlights the slot and calls this function - - void realizeCurrentTransaction(); //calls callback with parameters stored in commonInfo - void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst, bool withUIUpdate); - void artifactRemoved(const ArtifactLocation &al); - void artifactUpdateSlots(const ArtifactLocation &al); - ArtPlacePtr getArtPlace(ArtifactPosition slot);//may return null - - void setHero(const CGHeroInstance * hero); - const CGHeroInstance *getHero() const; - void dispose(); //free resources not needed after closing windows and reset state - void scrollBackpack(int dir); //dir==-1 => to left; dir==1 => to right - - void activate() override; - void deactivate() override; - - void safeRedraw(); - void markPossibleSlots(const CArtifactInstance * art, bool withRedraw = false); - void unmarkSlots(bool withRedraw = false); //unmarks slots in all visible AOHs - void unmarkLocalSlots(bool withRedraw = false); //unmarks slots in that particular AOH - void updateWornSlots(bool redrawParent = false); - void updateBackpackSlots(bool redrawParent = false); - - void updateSlot(ArtifactPosition i); - - CArtifactsOfHero(const Point& position, bool createCommonPart = false); - //Alternative constructor, used if custom artifacts positioning required (Kingdom interface) - CArtifactsOfHero(ArtPlaceMap ArtWorn, std::vector Backpack, - std::shared_ptr leftScroll, std::shared_ptr rightScroll, bool createCommonPart = false); - ~CArtifactsOfHero(); - void updateParentWindow(); - friend class CHeroArtPlace; - -private: + using ClickHandler = std::function; const CGHeroInstance * curHero; + ClickHandler leftClickCallback; + ClickHandler rightClickCallback; + + CArtifactsOfHeroBase(); + virtual ~CArtifactsOfHeroBase(); + virtual void leftClickArtPlace(CHeroArtPlace & artPlace); + virtual void rightClickArtPlace(CHeroArtPlace & artPlace); + virtual void setHero(const CGHeroInstance * hero); + virtual const CGHeroInstance * getHero() const; + virtual void scrollBackpack(int offset); + virtual void safeRedraw(); + virtual void markPossibleSlots(const CArtifactInstance * art, bool assumeDestRemoved = true); + virtual void unmarkSlots(); + virtual ArtPlacePtr getArtPlace(const ArtifactPosition & slot); + virtual void updateWornSlots(); + virtual void updateBackpackSlots(); + virtual void updateSlot(const ArtifactPosition & slot); + virtual const CArtifactInstance * getPickedArtifact(); +protected: ArtPlaceMap artWorn; + std::vector backpack; + std::shared_ptr leftBackpackRoll; + std::shared_ptr rightBackpackRoll; + int backpackPos; // Position to display artifacts in heroes backpack - std::vector backpack; //hero's visible backpack (only 5 elements!) - int backpackPos; //number of first art visible in backpack (in hero's vector) + const std::vector slotPos = + { + Point(509,30), Point(567,240), Point(509,80), //0-2 + Point(383,68), Point(564,183), Point(509,130), //3-5 + Point(431,68), Point(610,183), Point(515,295), //6-8 + Point(383,143), Point(399,194), Point(415,245), //9-11 + Point(431,296), Point(564,30), Point(610,30), //12-14 + Point(610,76), Point(610,122), Point(610,310), //15-17 + Point(381,296) //18 + }; - void eraseSlotData(ArtPlacePtr artPlace, ArtifactPosition slotID); - void setSlotData(ArtPlacePtr artPlace, ArtifactPosition slotID); + virtual void init(CHeroArtPlace::ClickHandler lClickCallback, CHeroArtPlace::ClickHandler rClickCallback, + const Point & position, BpackScrollHandler scrollHandler); + // Assigns an artifacts to an artifact place depending on it's new slot ID + virtual void setSlotData(ArtPlacePtr artPlace, const ArtifactPosition & slot, const CArtifactSet & artSet); + virtual void scrollBackpackForArtSet(int offset, const CArtifactSet & artSet); +}; + +class CArtifactsOfHeroMain : public CArtifactsOfHeroBase +{ +public: + CArtifactsOfHeroMain(const Point & position); + void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc); + void pickUpArtifact(CHeroArtPlace & artPlace); +}; + +class CArtifactsOfHeroKingdom : public CArtifactsOfHeroBase +{ +public: + CArtifactsOfHeroKingdom(ArtPlaceMap ArtWorn, std::vector Backpack, + std::shared_ptr leftScroll, std::shared_ptr rightScroll); + void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc); + void pickUpArtifact(CHeroArtPlace & artPlace); +}; + +class CArtifactsOfHeroAltar : public CArtifactsOfHeroBase +{ +public: + std::set artifactsOnAltar; + ArtifactPosition pickedArtFromSlot; + std::shared_ptr visibleArtSet; + + CArtifactsOfHeroAltar(const Point & position); + void setHero(const CGHeroInstance * hero) override; + void updateWornSlots() override; + void updateBackpackSlots() override; + void scrollBackpack(int offset) override; + void pickUpArtifact(CHeroArtPlace & artPlace); + void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc); + void pickedArtMoveToAltar(const ArtifactPosition & slot); + void deleteFromVisible(const CArtifactInstance * artInst); +}; + +class CArtifactsOfHeroMarket : public CArtifactsOfHeroBase +{ +public: + std::function selectArtCallback; + + CArtifactsOfHeroMarket(const Point & position); + void scrollBackpack(int offset) override; }; class CWindowWithArtifacts : public CArtifactHolder { - std::vector> artSets; public: - void addSet(std::shared_ptr artSet); + using CArtifactsOfHeroPtr = std::variant< + std::weak_ptr, + std::weak_ptr, + std::weak_ptr, + std::weak_ptr>; - std::shared_ptr getCommonPart(); + void addSet(CArtifactsOfHeroPtr artSet); + const CGHeroInstance * getHeroPickedArtifact(); + const CArtifactInstance * getPickedArtifact(); + void leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace); + void rightClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace); - void artifactRemoved(const ArtifactLocation &artLoc) override; - void artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc, bool withRedraw) override; - void artifactDisassembled(const ArtifactLocation &artLoc) override; - void artifactAssembled(const ArtifactLocation &artLoc) override; + void artifactRemoved(const ArtifactLocation & artLoc) override; + void artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) override; + void artifactDisassembled(const ArtifactLocation & artLoc) override; + void artifactAssembled(const ArtifactLocation & artLoc) override; + +private: + std::vector artSets; + + void updateSlots(const ArtifactPosition & slot); + std::optional> getState(); + std::optional findAOHbyRef(CArtifactsOfHeroBase & artsInst); }; From 177523d972b511e58bb5dc5a503762af104301a1 Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:17:27 +0300 Subject: [PATCH 02/25] askToAssemble, askToDisassemble --- client/CPlayerInterface.cpp | 2 +- client/widgets/CArtifactHolder.cpp | 41 ++++++++++++++++++++++++++++++ client/widgets/CArtifactHolder.h | 6 +++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 8ab3bab52..44f1ceee7 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1925,7 +1925,7 @@ void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al) al.slot.num); return; } - CHeroArtPlace::askToAssemble(hero, al.slot); + ArtifactUtils::askToAssemble(hero, al.slot); } } diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index dfde17f84..9fa59c91d 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -1056,3 +1056,44 @@ std::optional CWindowWithArtifacts::f } return res; } + +bool ArtifactUtils::askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot) +{ + assert(hero); + const auto art = hero->getArt(slot); + assert(art); + auto assemblyPossibilities = ArtifactUtils::assemblyPossibilities(hero, art->getTypeId(), ArtifactUtils::isSlotEquipment(slot)); + + for(const auto combinedArt : assemblyPossibilities) + { + LOCPLINT->showArtifactAssemblyDialog( + art->artType, + combinedArt, + std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combinedArt->getId())); + + if(assemblyPossibilities.size() > 2) + logGlobal->warn("More than one possibility of assembling on %s... taking only first", art->artType->getNameTranslated()); + return true; + } + return false; +} + +bool ArtifactUtils::askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot) +{ + assert(hero); + const auto art = hero->getArt(slot); + assert(art); + + if(art->canBeDisassembled()) + { + if(ArtifactUtils::isSlotBackpack(slot) && !ArtifactUtils::isBackpackFreeSlots(hero, art->artType->constituents->size() - 1)) + return false; + + LOCPLINT->showArtifactAssemblyDialog( + art->artType, + nullptr, + std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, false, ArtifactID())); + return true; + } + return false; +} diff --git a/client/widgets/CArtifactHolder.h b/client/widgets/CArtifactHolder.h index a8362fa78..6104c6635 100644 --- a/client/widgets/CArtifactHolder.h +++ b/client/widgets/CArtifactHolder.h @@ -220,3 +220,9 @@ private: std::optional> getState(); std::optional findAOHbyRef(CArtifactsOfHeroBase & artsInst); }; + +namespace ArtifactUtils +{ + bool askToAssemble(const CGHeroInstance* hero, const ArtifactPosition& slot); + bool askToDisassemble(const CGHeroInstance* hero, const ArtifactPosition& slot); +} From c86af54ce19a58e9e517d352770e5ceda9dd4ffa Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:18:55 +0300 Subject: [PATCH 03/25] arts move condition fix --- server/CGameHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index ca3fb4049..b7827575a 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3902,7 +3902,7 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat if(srcArtifact == nullptr) COMPLAIN_RET("No artifact to move!"); - if(destArtifact && srcPlayer != dstPlayer) + if(destArtifact && srcPlayer != dstPlayer && !isDstSlotBackpack) COMPLAIN_RET("Can't touch artifact on hero of another player!"); // Check if src/dest slots are appropriate for the artifacts exchanged. From a8220c551ff1360faa2667a2f87241aa7577b8f7 Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:33:47 +0300 Subject: [PATCH 04/25] fixed init CArtifactsOfHero --- client/widgets/CGarrisonInt.cpp | 13 +-- client/windows/CHeroWindow.cpp | 27 +++--- client/windows/CHeroWindow.h | 4 +- client/windows/CKingdomInterface.cpp | 4 +- client/windows/CKingdomInterface.h | 2 +- client/windows/CTradeWindow.cpp | 134 +++++++++++++-------------- client/windows/CTradeWindow.h | 4 +- client/windows/GUIClasses.cpp | 8 +- client/windows/GUIClasses.h | 2 +- 9 files changed, 92 insertions(+), 106 deletions(-) diff --git a/client/widgets/CGarrisonInt.cpp b/client/widgets/CGarrisonInt.cpp index 0b5b30ba7..5d1eafa74 100644 --- a/client/widgets/CGarrisonInt.cpp +++ b/client/widgets/CGarrisonInt.cpp @@ -185,20 +185,17 @@ bool CGarrisonSlot::highlightOrDropArtifact() bool artSelected = false; if (CWindowWithArtifacts* chw = dynamic_cast(GH.topInt().get())) //dirty solution { - std::shared_ptr commonInfo = chw->getCommonPart(); - const CArtifactInstance * art = nullptr; - if(commonInfo) - art = commonInfo->src.art; + const auto pickedArtInst = chw->getPickedArtifact(); - if(art) + if(pickedArtInst) { - const CGHeroInstance *srcHero = commonInfo->src.AOH->getHero(); + const auto * srcHero = chw->getHeroPickedArtifact(); artSelected = true; if (myStack) // try dropping the artifact only if the slot isn't empty { - ArtifactLocation src(srcHero, commonInfo->src.slotID); + ArtifactLocation src(srcHero, ArtifactPosition::TRANSITION_POS); ArtifactLocation dst(myStack, ArtifactPosition::CREATURE_SLOT); - if (art->canBePutAt(dst, true)) + if(pickedArtInst->canBePutAt(dst, true)) { //equip clicked stack if(dst.getArt()) { diff --git a/client/windows/CHeroWindow.cpp b/client/windows/CHeroWindow.cpp index fe8563541..3a91a0ede 100644 --- a/client/windows/CHeroWindow.cpp +++ b/client/windows/CHeroWindow.cpp @@ -42,9 +42,10 @@ TConstBonusListPtr CHeroWithMaybePickedArtifact::getAllBonuses(const CSelector & TConstBonusListPtr heroBonuses = hero->getAllBonuses(selector, limit, hero, cachingStr); TConstBonusListPtr bonusesFromPickedUpArtifact; - std::shared_ptr cp = cww->getCommonPart(); - if(cp && cp->src.art && cp->src.valid() && cp->src.AOH && cp->src.AOH->getHero() == hero) - bonusesFromPickedUpArtifact = cp->src.art->getAllBonuses(selector, limit, hero); + const auto pickedArtInst = cww->getPickedArtifact(); + + if(pickedArtInst) + bonusesFromPickedUpArtifact = pickedArtInst->getAllBonuses(selector, limit, hero); else bonusesFromPickedUpArtifact = TBonusListPtr(new BonusList()); @@ -244,7 +245,7 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded) } if(!arts) { - arts = std::make_shared(Point(-65, -8), true); + arts = std::make_shared(Point(-65, -8)); arts->setHero(curHero); addSet(arts); } @@ -354,25 +355,23 @@ void CHeroWindow::dismissCurrent() void CHeroWindow::commanderWindow() { - //bool artSelected = false; - std::shared_ptr commonInfo = getCommonPart(); + const auto pickedArtInst = getPickedArtifact(); + const auto hero = getHeroPickedArtifact(); - if(const CArtifactInstance *art = commonInfo->src.art) + if(pickedArtInst) { - const CGHeroInstance *srcHero = commonInfo->src.AOH->getHero(); - //artSelected = true; - const auto freeSlot = ArtifactUtils::getArtAnyPosition(curHero->commander, art->artType->getId()); + const auto freeSlot = ArtifactUtils::getArtAnyPosition(curHero->commander, pickedArtInst->getTypeId()); if(freeSlot < ArtifactPosition::COMMANDER_AFTER_LAST) //we don't want to put it in commander's backpack! { - ArtifactLocation src(srcHero, commonInfo->src.slotID); + ArtifactLocation src(hero, ArtifactPosition::TRANSITION_POS); ArtifactLocation dst(curHero->commander.get(), freeSlot); - if(art->canBePutAt(dst, true)) + if(pickedArtInst->canBePutAt(dst, true)) { //equip clicked stack if(dst.getArt()) { - LOCPLINT->cb->swapArtifacts (dst, ArtifactLocation(srcHero, - ArtifactUtils::getArtBackpackPosition(srcHero, art->getTypeId()))); + LOCPLINT->cb->swapArtifacts(dst, ArtifactLocation(hero, + ArtifactUtils::getArtBackpackPosition(hero, pickedArtInst->getTypeId()))); } LOCPLINT->cb->swapArtifacts(src, dst); } diff --git a/client/windows/CHeroWindow.h b/client/windows/CHeroWindow.h index 263b50009..de9b5ec40 100644 --- a/client/windows/CHeroWindow.h +++ b/client/windows/CHeroWindow.h @@ -25,7 +25,7 @@ class CHeroWindow; class LClickableAreaHero; class LRClickableAreaWText; class LRClickableAreaWTextComp; -class CArtifactsOfHero; +class CArtifactsOfHeroMain; class MoraleLuckBox; class CToggleButton; class CToggleGroup; @@ -105,7 +105,7 @@ class CHeroWindow : public CStatusbarWindow, public CGarrisonHolder, public CWin std::shared_ptr formations; std::shared_ptr garr; - std::shared_ptr arts; + std::shared_ptr arts; std::vector> labels; diff --git a/client/windows/CKingdomInterface.cpp b/client/windows/CKingdomInterface.cpp index 8ad3bb8ee..1a8d514fe 100644 --- a/client/windows/CKingdomInterface.cpp +++ b/client/windows/CKingdomInterface.cpp @@ -872,7 +872,7 @@ CHeroItem::CHeroItem(const CGHeroInstance * Hero) assert(arts1->arts.size() == 9); assert(arts2->arts.size() == 9); - CArtifactsOfHero::ArtPlaceMap arts = + CArtifactsOfHeroMain::ArtPlaceMap arts = { {ArtifactPosition::HEAD, arts1->arts[0]}, {ArtifactPosition::SHOULDERS,arts1->arts[1]}, @@ -896,7 +896,7 @@ CHeroItem::CHeroItem(const CGHeroInstance * Hero) }; - heroArts = std::make_shared(arts, backpack->arts, backpack->btnLeft, backpack->btnRight, true); + heroArts = std::make_shared(arts, backpack->arts, backpack->btnLeft, backpack->btnRight); heroArts->setHero(hero); artsTabs = std::make_shared(std::bind(&CHeroItem::onTabSelected, this, _1)); diff --git a/client/windows/CKingdomInterface.h b/client/windows/CKingdomInterface.h index 9d8448a71..10b106df8 100644 --- a/client/windows/CKingdomInterface.h +++ b/client/windows/CKingdomInterface.h @@ -309,7 +309,7 @@ class CHeroItem : public CIntObject, public CGarrisonHolder std::shared_ptr onTabSelected(size_t index); public: - std::shared_ptr heroArts; + std::shared_ptr heroArts; void updateGarrisons() override; diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index 0c16f51f4..1ebb09272 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -179,24 +179,26 @@ void CTradeWindow::CTradeableItem::clickLeft(tribool down, bool previousState) if(type == ARTIFACT_PLACEHOLDER) { CAltarWindow *aw = static_cast(mw); - if(const CArtifactInstance *movedArt = aw->arts->commonInfo->src.art) + const auto pickedArtInst = aw->getPickedArtifact(); + + auto artifactsOfHero = std::dynamic_pointer_cast(aw->arts); + if(pickedArtInst) { - aw->moveFromSlotToAltar(aw->arts->commonInfo->src.slotID, this->shared_from_this(), movedArt); + artifactsOfHero->pickedArtMoveToAltar(ArtifactPosition::TRANSITION_POS); + aw->moveArtToAltar(this->shared_from_this(), pickedArtInst); } else if(const CArtifactInstance *art = getArtInstance()) { - aw->arts->commonInfo->src.AOH = aw->arts.get(); - aw->arts->commonInfo->src.art = art; - aw->arts->commonInfo->src.slotID = aw->hero->getArtPos(art); - aw->arts->markPossibleSlots(art); - - //aw->arts->commonInfo->dst.AOH = aw->arts; - CCS->curh->dragAndDropCursor("artifact", art->artType->getIconIndex()); - - aw->arts->artifactsOnAltar.erase(art); + const auto hero = artifactsOfHero->getHero(); + const auto slot = hero->getSlotByInstance(art); + assert(slot != ArtifactPosition::PRE_FIRST); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero, slot), + ArtifactLocation(hero, ArtifactPosition::TRANSITION_POS)); + artifactsOfHero->pickedArtFromSlot = slot; + artifactsOfHero->artifactsOnAltar.erase(art); setID(-1); subtitle.clear(); - aw->deal->block(!aw->arts->artifactsOnAltar.size()); + aw->deal->block(!artifactsOfHero->artifactsOnAltar.size()); } aw->calcTotalExp(); @@ -391,14 +393,21 @@ void CTradeWindow::initItems(bool Left) yOffset = -12; } - arts = std::make_shared(Point(xOffset, yOffset), true); - arts->recActions = 255-DISPOSE; - arts->setHero(hero); - arts->allowedAssembling = false; - addSet(arts); - if(mode == EMarketMode::ARTIFACT_RESOURCE) - arts->highlightModeCallback = std::bind(&CTradeWindow::artifactSelected, this, _1); + { + auto artifactsOfHero = std::make_shared(Point(xOffset, yOffset)); + artifactsOfHero->selectArtCallback = std::bind(&CTradeWindow::artifactSelected, this, _1); + artifactsOfHero->setHero(hero); + addSet(artifactsOfHero); + arts = artifactsOfHero; + } + else + { + auto artifactsOfHero = std::make_shared(Point(xOffset, yOffset)); + artifactsOfHero->setHero(hero); + addSet(artifactsOfHero); + arts = artifactsOfHero; + } } else { @@ -630,8 +639,8 @@ void CTradeWindow::setMode(EMarketMode::EMarketMode Mode) void CTradeWindow::artifactSelected(CHeroArtPlace *slot) { assert(mode == EMarketMode::ARTIFACT_RESOURCE); - items[1][0]->setArtInstance(slot->ourArt); - if(slot->ourArt && !slot->locked) + items[1][0]->setArtInstance(slot->getArt()); + if(slot->getArt()) hLeft = items[1][0]; else hLeft = nullptr; @@ -858,7 +867,7 @@ void CMarketplaceWindow::selectionChanged(bool side) readyToTrade = readyToTrade && (hLeft->id != hRight->id); //for resource trade, two DIFFERENT resources must be selected if(mode == EMarketMode::ARTIFACT_RESOURCE && !hLeft) - arts->unmarkSlots(false); + arts->unmarkSlots(); if(readyToTrade) { @@ -1250,13 +1259,15 @@ void CAltarWindow::makeDeal() else { std::vector positions; - for(const CArtifactInstance *art : arts->artifactsOnAltar) //sacrifice each artifact on the list + auto artifactsOfHero = std::dynamic_pointer_cast(arts); + for(const CArtifactInstance * art : artifactsOfHero->artifactsOnAltar) { - positions.push_back(hero->getArtPos(art)); + positions.push_back(hero->getSlotByInstance(art)); } + std::sort(positions.begin(), positions.end(), std::greater<>()); LOCPLINT->cb->trade(market->o, mode, positions, {}, {}, hero); - arts->artifactsOnAltar.clear(); + artifactsOfHero->artifactsOnAltar.clear(); for(auto item : items[0]) { @@ -1264,7 +1275,6 @@ void CAltarWindow::makeDeal() item->subtitle = ""; } - arts->commonInfo->reset(); //arts->scrollBackpack(0); deal->block(true); } @@ -1294,12 +1304,13 @@ void CAltarWindow::SacrificeAll() } else { - for(auto i = hero->artifactsWorn.cbegin(); i != hero->artifactsWorn.cend(); i++) + auto artifactsOfHero = std::dynamic_pointer_cast(arts); + for(const auto & aw : artifactsOfHero->visibleArtSet->artifactsWorn) { - if(!i->second.locked) //ignore locks from assembled artifacts - moveFromSlotToAltar(i->first, nullptr, i->second.artifact); + if(!aw.second.locked) + moveArtToAltar(nullptr, aw.second.artifact); } - + artifactsOfHero->updateWornSlots(); SacrificeBackpack(); } redraw(); @@ -1414,7 +1425,8 @@ void CAltarWindow::calcTotalExp() } else { - for(const CArtifactInstance *art : arts->artifactsOnAltar) + auto artifactsOfHero = std::dynamic_pointer_cast(arts); + for(const CArtifactInstance * art : artifactsOfHero->artifactsOnAltar) { int dmp, valOfArt; market->getOffer(art->artType->getId(), 0, dmp, valOfArt, mode); @@ -1458,21 +1470,12 @@ int CAltarWindow::firstFreeSlot() void CAltarWindow::SacrificeBackpack() { - std::multiset toOmmit = arts->artifactsOnAltar; - - for (auto & elem : hero->artifactsInBackpack) + auto artsAltar = std::dynamic_pointer_cast(arts); + while(!artsAltar->visibleArtSet->artifactsInBackpack.empty()) { - - if(vstd::contains(toOmmit, elem.artifact)) - { - toOmmit -= elem.artifact; - continue; - } - - putOnAltar(nullptr, elem.artifact); - } - - arts->scrollBackpack(0); + if(!putOnAltar(nullptr, artsAltar->visibleArtSet->artifactsInBackpack[0].artifact)) + break; + }; calcTotalExp(); } @@ -1484,15 +1487,18 @@ void CAltarWindow::artifactPicked() void CAltarWindow::showAll(SDL_Surface * to) { CTradeWindow::showAll(to); - if(mode == EMarketMode::ARTIFACT_EXP && arts && arts->commonInfo->src.art) + if(mode == EMarketMode::ARTIFACT_EXP && arts) { - artIcon->setFrame(arts->commonInfo->src.art->artType->getIconIndex()); - artIcon->showAll(to); + if(auto pickedArt = arts->getPickedArtifact()) + { + artIcon->setFrame(pickedArt->artType->getIconIndex()); + artIcon->showAll(to); - int dmp, val; - market->getOffer(arts->commonInfo->src.art->artType->getId(), 0, dmp, val, EMarketMode::ARTIFACT_EXP); - val = static_cast(hero->calculateXp(val)); - printAtMiddleLoc(std::to_string(val), 304, 498, FONT_SMALL, Colors::WHITE, to); + int dmp, val; + market->getOffer(pickedArt->getTypeId(), 0, dmp, val, EMarketMode::ARTIFACT_EXP); + val = static_cast(hero->calculateXp(val)); + printAtMiddleLoc(std::to_string(val), 304, 498, FONT_SMALL, Colors::WHITE, to); + } } } @@ -1519,7 +1525,9 @@ bool CAltarWindow::putOnAltar(std::shared_ptr altarSlot, const C market->getOffer(art->artType->getId(), 0, dmp, val, EMarketMode::ARTIFACT_EXP); val = static_cast(hero->calculateXp(val)); - arts->artifactsOnAltar.insert(art); + auto artsAltar = std::dynamic_pointer_cast(arts); + artsAltar->artifactsOnAltar.insert(art); + artsAltar->deleteFromVisible(art); altarSlot->setArtInstance(art); altarSlot->subtitle = std::to_string(val); @@ -1527,25 +1535,11 @@ bool CAltarWindow::putOnAltar(std::shared_ptr altarSlot, const C return true; } -void CAltarWindow::moveFromSlotToAltar(ArtifactPosition slotID, std::shared_ptr altarSlot, const CArtifactInstance *art) +void CAltarWindow::moveArtToAltar(std::shared_ptr altarSlot, const CArtifactInstance *art) { - auto freeBackpackSlot = ArtifactPosition((si32)hero->artifactsInBackpack.size() + GameConstants::BACKPACK_START); - if(arts->commonInfo->src.art) - { - arts->commonInfo->dst.slotID = freeBackpackSlot; - arts->commonInfo->dst.AOH = arts.get(); - } - if(putOnAltar(altarSlot, art)) { - if(slotID < GameConstants::BACKPACK_START) - LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero, slotID), ArtifactLocation(hero, freeBackpackSlot)); - else - { - arts->commonInfo->src.clear(); - arts->commonInfo->dst.clear(); - CCS->curh->dragAndDropCursor(nullptr); - arts->unmarkSlots(false); - } + CCS->curh->dragAndDropCursor(nullptr); + arts->unmarkSlots(); } } diff --git a/client/windows/CTradeWindow.h b/client/windows/CTradeWindow.h index 49bc5e74c..c1f236cda 100644 --- a/client/windows/CTradeWindow.h +++ b/client/windows/CTradeWindow.h @@ -67,7 +67,7 @@ public: const IMarket * market; const CGHeroInstance * hero; - std::shared_ptr arts; + std::shared_ptr arts; //all indexes: 1 = left, 0 = right std::array>, 2> items; @@ -186,5 +186,5 @@ public: void artifactPicked(); int firstFreeSlot(); - void moveFromSlotToAltar(ArtifactPosition slotID, std::shared_ptr, const CArtifactInstance * art); + void moveArtToAltar(std::shared_ptr, const CArtifactInstance * art); }; diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index cd714fbb7..7f65add98 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -909,13 +909,9 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, portraits[0] = std::make_shared("PortraitsLarge", heroInst[0]->portrait, 0, 257, 13); portraits[1] = std::make_shared("PortraitsLarge", heroInst[1]->portrait, 0, 485, 13); - artifs[0] = std::make_shared(Point(-334, 150)); - artifs[0]->commonInfo = std::make_shared(); - artifs[0]->commonInfo->participants.insert(artifs[0].get()); + artifs[0] = std::make_shared(Point(-334, 150)); artifs[0]->setHero(heroInst[0]); - artifs[1] = std::make_shared(Point(96, 150)); - artifs[1]->commonInfo = artifs[0]->commonInfo; - artifs[1]->commonInfo->participants.insert(artifs[1].get()); + artifs[1] = std::make_shared(Point(98, 150)); artifs[1]->setHero(heroInst[1]); addSet(artifs[0]); diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index f676305f2..0168e5491 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -327,7 +327,7 @@ class CExchangeWindow : public CStatusbarWindow, public CGarrisonHolder, public public: std::array heroInst; - std::array, 2> artifs; + std::array, 2> artifs; void updateGarrisons() override; From 1f4897e84127c1c6c47f742d26c20443a9071ea6 Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:48:04 +0300 Subject: [PATCH 05/25] CArtifactSet::getSlotByInstance --- lib/CArtHandler.cpp | 19 +++++++++++++++++++ lib/CArtHandler.h | 1 + 2 files changed, 20 insertions(+) diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 3ae91e43b..bcea32865 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -1205,6 +1205,25 @@ const CArtifactInstance * CArtifactSet::getArtByInstanceId(const ArtifactInstanc return nullptr; } +const ArtifactPosition CArtifactSet::getSlotByInstance(const CArtifactInstance * artInst) const +{ + if(artInst) + { + for(auto & slot : artInst->artType->possibleSlots.at(bearerType())) + if(getArt(slot) == artInst) + return slot; + + auto backpackSlot = GameConstants::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 searchBackpackAssemblies, bool allowLocked) const { return getArtPosCount(aid, onlyWorn, searchBackpackAssemblies, allowLocked) > 0; diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index a4a6ff3cf..0aac12898 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -328,6 +328,7 @@ public: std::vector getAllArtPositions(const ArtifactID & aid, bool onlyWorn, bool allowLocked, bool getAll) const; std::vector getBackpackArtPositions(const ArtifactID & aid) const; const CArtifactInstance * getArtByInstanceId(const ArtifactInstanceID & artInstId) const; + const ArtifactPosition getSlotByInstance(const CArtifactInstance * artInst) const; /// Search for constituents of assemblies in backpack which do not have an ArtifactPosition const CArtifactInstance * getHiddenArt(const ArtifactID & aid) const; const CCombinedArtifactInstance * getAssemblyByConstituent(const ArtifactID & aid) const; From dcac8252f3b3a6b3272f79da7f3f46448c139dbb Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:57:45 +0300 Subject: [PATCH 06/25] fixed windows positions --- client/windows/CKingdomInterface.cpp | 4 ++-- client/windows/CTradeWindow.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/windows/CKingdomInterface.cpp b/client/windows/CKingdomInterface.cpp index 1a8d514fe..ee9144f9c 100644 --- a/client/windows/CKingdomInterface.cpp +++ b/client/windows/CKingdomInterface.cpp @@ -826,7 +826,7 @@ public: background = std::make_shared("OVSLOT", 4); pos = background->pos; for(int i=0; i<9; i++) - arts.push_back(std::make_shared(Point(270+i*48, 65))); + arts.push_back(std::make_shared(Point(269+i*48, 66))); } }; @@ -846,7 +846,7 @@ public: btnLeft = std::make_shared(Point(269, 66), "HSBTNS3", CButton::tooltip(), 0); btnRight = std::make_shared(Point(675, 66), "HSBTNS5", CButton::tooltip(), 0); for(int i=0; i<8; i++) - arts.push_back(std::make_shared(Point(295+i*48, 65))); + arts.push_back(std::make_shared(Point(294+i*48, 66))); } }; diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index 1ebb09272..d7a343aea 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -389,7 +389,7 @@ void CTradeWindow::initItems(bool Left) } else //ARTIFACT_EXP { - xOffset = -363; + xOffset = -365; yOffset = -12; } From 8b531ca6c482d1f7ea541912dbd15624caffde61 Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Sun, 23 Apr 2023 15:10:35 +0300 Subject: [PATCH 07/25] CArtifactHolder divided into files --- client/CMakeLists.txt | 12 + client/CPlayerInterface.cpp | 2 +- client/widgets/CArtifactHolder.cpp | 777 +-------------------- client/widgets/CArtifactHolder.h | 135 +--- client/widgets/CArtifactsOfHeroAltar.cpp | 109 +++ client/widgets/CArtifactsOfHeroAltar.h | 32 + client/widgets/CArtifactsOfHeroBase.cpp | 277 ++++++++ client/widgets/CArtifactsOfHeroBase.h | 67 ++ client/widgets/CArtifactsOfHeroKingdom.cpp | 54 ++ client/widgets/CArtifactsOfHeroKingdom.h | 27 + client/widgets/CArtifactsOfHeroMain.cpp | 35 + client/widgets/CArtifactsOfHeroMain.h | 26 + client/widgets/CArtifactsOfHeroMarket.cpp | 41 ++ client/widgets/CArtifactsOfHeroMarket.h | 21 + client/widgets/CWindowWithArtifacts.cpp | 363 ++++++++++ client/widgets/CWindowWithArtifacts.h | 44 ++ client/windows/CHeroWindow.h | 2 +- client/windows/CKingdomInterface.h | 2 +- client/windows/CTradeWindow.cpp | 6 +- client/windows/CTradeWindow.h | 2 +- client/windows/GUIClasses.h | 2 +- 21 files changed, 1121 insertions(+), 915 deletions(-) create mode 100644 client/widgets/CArtifactsOfHeroAltar.cpp create mode 100644 client/widgets/CArtifactsOfHeroAltar.h create mode 100644 client/widgets/CArtifactsOfHeroBase.cpp create mode 100644 client/widgets/CArtifactsOfHeroBase.h create mode 100644 client/widgets/CArtifactsOfHeroKingdom.cpp create mode 100644 client/widgets/CArtifactsOfHeroKingdom.h create mode 100644 client/widgets/CArtifactsOfHeroMain.cpp create mode 100644 client/widgets/CArtifactsOfHeroMain.h create mode 100644 client/widgets/CArtifactsOfHeroMarket.cpp create mode 100644 client/widgets/CArtifactsOfHeroMarket.h create mode 100644 client/widgets/CWindowWithArtifacts.cpp create mode 100644 client/widgets/CWindowWithArtifacts.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 0a829ed0e..2f5857cb3 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -85,6 +85,12 @@ set(client_SRCS widgets/MiscWidgets.cpp widgets/ObjectLists.cpp widgets/TextControls.cpp + widgets/CArtifactsOfHeroBase.cpp + widgets/CArtifactsOfHeroMain.cpp + widgets/CArtifactsOfHeroKingdom.cpp + widgets/CArtifactsOfHeroAltar.cpp + widgets/CArtifactsOfHeroMarket.cpp + widgets/CWindowWithArtifacts.cpp windows/CCastleInterface.cpp windows/CCreatureWindow.cpp @@ -213,6 +219,12 @@ set(client_HEADERS widgets/MiscWidgets.h widgets/ObjectLists.h widgets/TextControls.h + widgets/CArtifactsOfHeroBase.h + widgets/CArtifactsOfHeroMain.h + widgets/CArtifactsOfHeroKingdom.h + widgets/CArtifactsOfHeroAltar.h + widgets/CArtifactsOfHeroMarket.h + widgets/CWindowWithArtifacts.h windows/CCastleInterface.h windows/CCreatureWindow.h diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 44f1ceee7..16d505e40 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1925,7 +1925,7 @@ void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al) al.slot.num); return; } - ArtifactUtils::askToAssemble(hero, al.slot); + ArtifactUtilsClient::askToAssemble(hero, al.slot); } } diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index 9fa59c91d..9b794090b 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -11,13 +11,9 @@ #include "CArtifactHolder.h" #include "../gui/CGuiHandler.h" -#include "../gui/CursorHandler.h" -#include "Buttons.h" #include "CComponent.h" -#include "../windows/CHeroWindow.h" -#include "../windows/CSpellWindow.h" #include "../windows/GUIClasses.h" #include "../renderSDL/SDL_Extensions.h" #include "../CPlayerInterface.h" @@ -25,7 +21,6 @@ #include "../../CCallback.h" -#include "../../lib/CArtHandler.h" #include "../../lib/CGeneralTextHandler.h" #include "../../lib/mapObjects/CGHeroInstance.h" @@ -289,775 +284,7 @@ void CHeroArtPlace::createImage() selection->disable(); } -CArtifactsOfHeroBase::CArtifactsOfHeroBase() - : backpackPos(0), - curHero(nullptr) -{ -} - -CArtifactsOfHeroBase::~CArtifactsOfHeroBase() -{ - // TODO: cursor handling is CWindowWithArtifacts level. Should be moved when trading, kingdom and hero window are reworked - // This will interfere with the implementation of a separate backpack window - CCS->curh->dragAndDropCursor(nullptr); - - // Artifact located in artifactsTransitionPos should be returned - if(getPickedArtifact()) - { - auto slot = ArtifactUtils::getArtAnyPosition(curHero, curHero->artifactsTransitionPos.begin()->artifact->getTypeId()); - if(slot == ArtifactPosition::PRE_FIRST) - { - LOCPLINT->cb->eraseArtifactByClient(ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); - } - else - { - LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS), ArtifactLocation(curHero, slot)); - } - } -} - -void CArtifactsOfHeroBase::init( - CHeroArtPlace::ClickHandler lClickCallback, - CHeroArtPlace::ClickHandler rClickCallback, - const Point & position, - BpackScrollHandler scrollHandler) -{ - // CArtifactsOfHeroBase::init may be transform to CArtifactsOfHeroBase::CArtifactsOfHeroBase if OBJECT_CONSTRUCTION_CAPTURING is removed - OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE); - pos += position; - for(int g = 0; g < GameConstants::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(nullptr); - artPlace.second->leftClickCallback = lClickCallback; - artPlace.second->rightClickCallback = rClickCallback; - } - for(auto artPlace : backpack) - { - artPlace->setArtifact(nullptr); - artPlace->leftClickCallback = lClickCallback; - artPlace->rightClickCallback = rClickCallback; - } - leftBackpackRoll = std::make_shared(Point(379, 364), "hsbtns3.def", CButton::tooltip(), [scrollHandler]() { scrollHandler(-1); }, SDLK_LEFT); - rightBackpackRoll = std::make_shared(Point(632, 364), "hsbtns5.def", CButton::tooltip(), [scrollHandler]() { scrollHandler(+1); }, SDLK_RIGHT); - leftBackpackRoll->block(true); - rightBackpackRoll->block(true); -} - -void CArtifactsOfHeroBase::leftClickArtPlace(CHeroArtPlace & artPlace) -{ - if(leftClickCallback) - leftClickCallback(*this, artPlace); -} - -void CArtifactsOfHeroBase::rightClickArtPlace(CHeroArtPlace & artPlace) -{ - if(rightClickCallback) - rightClickCallback(*this, artPlace); -} - -void CArtifactsOfHeroBase::setHero(const CGHeroInstance * hero) -{ - curHero = hero; - if(curHero->artifactsInBackpack.size() > 0) - backpackPos %= curHero->artifactsInBackpack.size(); - else - backpackPos = 0; - - for(auto slot : artWorn) - { - setSlotData(slot.second, slot.first, *curHero); - } - scrollBackpackForArtSet(0, *curHero); -} - -const CGHeroInstance * CArtifactsOfHeroBase::getHero() const -{ - return curHero; -} - -void CArtifactsOfHeroBase::scrollBackpack(int offset) -{ - scrollBackpackForArtSet(offset, *curHero); - safeRedraw(); -} - -void CArtifactsOfHeroBase::scrollBackpackForArtSet(int offset, const CArtifactSet & artSet) -{ - // offset==-1 => to left; offset==1 => to right - using slotInc = std::function; - auto artsInBackpack = static_cast(artSet.artifactsInBackpack.size()); - auto scrollingPossible = artsInBackpack > backpack.size(); - - slotInc inc_straight = [](ArtifactPosition & slot) -> ArtifactPosition - { - return slot + 1; - }; - slotInc inc_ring = [artsInBackpack](ArtifactPosition & slot) -> ArtifactPosition - { - return ArtifactPosition(GameConstants::BACKPACK_START + (slot - GameConstants::BACKPACK_START + 1) % artsInBackpack); - }; - slotInc inc; - if(scrollingPossible) - inc = inc_ring; - else - inc = inc_straight; - - backpackPos += offset; - if(backpackPos < 0) - backpackPos += artsInBackpack; - - if(artsInBackpack) - backpackPos %= artsInBackpack; - - auto slot = ArtifactPosition(GameConstants::BACKPACK_START + backpackPos); - for(auto artPlace : backpack) - { - setSlotData(artPlace, slot, artSet); - slot = inc(slot); - } - - // Blocking scrolling if there is not enough artifacts to scroll - leftBackpackRoll->block(!scrollingPossible); - rightBackpackRoll->block(!scrollingPossible); -} - -void CArtifactsOfHeroBase::safeRedraw() -{ - if(active) - { - if(parent) - parent->redraw(); - else - redraw(); - } -} - -void CArtifactsOfHeroBase::markPossibleSlots(const CArtifactInstance * art, bool assumeDestRemoved) -{ - for(auto artPlace : artWorn) - artPlace.second->selectSlot(art->artType->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)) - { - if(artWorn.find(slot) == artWorn.end()) - { - logGlobal->error("CArtifactsOfHero::getArtPlace: invalid slot %d", slot); - return nullptr; - } - return artWorn[slot]; - } - if(ArtifactUtils::isSlotBackpack(slot)) - { - for(ArtPlacePtr artPlace : backpack) - if(artPlace->slot == slot) - return artPlace; - return nullptr; - } - else - { - return nullptr; - } -} - -void CArtifactsOfHeroBase::updateWornSlots() -{ - for(auto place : artWorn) - updateSlot(place.first); -} - -void CArtifactsOfHeroBase::updateBackpackSlots() -{ - for(auto artPlace : backpack) - updateSlot(artPlace->slot); - scrollBackpackForArtSet(0, *curHero); -} - -void CArtifactsOfHeroBase::updateSlot(const ArtifactPosition & slot) -{ - setSlotData(getArtPlace(slot), slot, *curHero); -} - -const CArtifactInstance * CArtifactsOfHeroBase::getPickedArtifact() -{ - // Returns only the picked up artifact. Not just highlighted like in the trading window. - if(!curHero || curHero->artifactsTransitionPos.empty()) - return nullptr; - else - return curHero->getArt(ArtifactPosition::TRANSITION_POS); -} - -void CArtifactsOfHeroBase::setSlotData(ArtPlacePtr artPlace, const ArtifactPosition & slot, const CArtifactSet & artSet) -{ - // Spurious call from artifactMoved in attempt to update hidden backpack slot - if(!artPlace && ArtifactUtils::isSlotBackpack(slot)) - { - return; - } - - artPlace->slot = slot; - if(auto slotInfo = artSet.getSlot(slot)) - { - artPlace->lockSlot(slotInfo->locked); - artPlace->setArtifact(slotInfo->artifact); - if(!slotInfo->artifact->canBeDisassembled()) - { - // If the artifact is part of at least one combined artifact, add additional information - std::map arts; - for(const auto combinedArt : slotInfo->artifact->artType->constituentOf) - { - arts.insert(std::pair(combinedArt, 0)); - for(const auto part : *combinedArt->constituents) - if(artSet.hasArt(part->getId(), true)) - arts.at(combinedArt)++; - } - artPlace->addCombinedArtInfo(arts); - } - } - else - { - artPlace->setArtifact(nullptr); - } -} - -CArtifactsOfHeroMain::CArtifactsOfHeroMain(const Point & position) -{ - init( - std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1), - std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1), - position, - std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, _1)); -} - -void CArtifactsOfHeroMain::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc) -{ - LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc); -} - -void CArtifactsOfHeroMain::pickUpArtifact(CHeroArtPlace & artPlace) -{ - LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, artPlace.slot), - ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); -} - -CArtifactsOfHeroKingdom::CArtifactsOfHeroKingdom(ArtPlaceMap ArtWorn, std::vector Backpack, - std::shared_ptr leftScroll, std::shared_ptr rightScroll) -{ - artWorn = ArtWorn; - backpack = Backpack; - leftBackpackRoll = leftScroll; - rightBackpackRoll = rightScroll; - - for(auto artPlace : artWorn) - { - artPlace.second->slot = artPlace.first; - artPlace.second->setArtifact(nullptr); - artPlace.second->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1); - artPlace.second->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1); - } - for(auto artPlace : backpack) - { - artPlace->setArtifact(nullptr); - artPlace->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1); - artPlace->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1); - } - leftBackpackRoll->addCallback(std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, -1)); - rightBackpackRoll->addCallback(std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, +1)); -} - -void CArtifactsOfHeroKingdom::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc) -{ - LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc); -} - -void CArtifactsOfHeroKingdom::pickUpArtifact(CHeroArtPlace & artPlace) -{ - LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, artPlace.slot), - ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); -} - -CArtifactsOfHeroAltar::CArtifactsOfHeroAltar(const Point & position) -{ - init( - std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1), - std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1), - position, - std::bind(&CArtifactsOfHeroAltar::scrollBackpack, this, _1)); - visibleArtSet = std::make_shared(ArtBearer::ArtBearer::HERO); - pickedArtFromSlot = ArtifactPosition::PRE_FIRST; -}; - -void CArtifactsOfHeroAltar::setHero(const CGHeroInstance * hero) -{ - if(hero) - { - visibleArtSet->artifactsWorn = hero->artifactsWorn; - visibleArtSet->artifactsInBackpack = hero->artifactsInBackpack; - CArtifactsOfHeroBase::setHero(hero); - } -} - -void CArtifactsOfHeroAltar::updateWornSlots() -{ - for(auto place : artWorn) - setSlotData(getArtPlace(place.first), place.first, *visibleArtSet); -} - -void CArtifactsOfHeroAltar::updateBackpackSlots() -{ - for(auto artPlace : backpack) - setSlotData(getArtPlace(artPlace->slot), artPlace->slot, *visibleArtSet); -} - -void CArtifactsOfHeroAltar::scrollBackpack(int offset) -{ - CArtifactsOfHeroBase::scrollBackpackForArtSet(offset, *visibleArtSet); - safeRedraw(); -} - -void CArtifactsOfHeroAltar::pickUpArtifact(CHeroArtPlace & artPlace) -{ - if(const auto art = artPlace.getArt()) - { - pickedArtFromSlot = artPlace.slot; - artPlace.setArtifact(nullptr); - deleteFromVisible(art); - if(ArtifactUtils::isSlotBackpack(pickedArtFromSlot)) - pickedArtFromSlot = curHero->getSlotByInstance(art); - assert(pickedArtFromSlot != ArtifactPosition::PRE_FIRST); - LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, pickedArtFromSlot), ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); - } -} - -void CArtifactsOfHeroAltar::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc) -{ - LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc); - const auto pickedArtInst = curHero->getArt(ArtifactPosition::TRANSITION_POS); - assert(pickedArtInst); - visibleArtSet->putArtifact(dstLoc.slot, const_cast(pickedArtInst)); -} - -void CArtifactsOfHeroAltar::pickedArtMoveToAltar(const ArtifactPosition & slot) -{ - if(ArtifactUtils::isSlotBackpack(slot) || ArtifactUtils::isSlotEquipment(slot) || slot == ArtifactPosition::TRANSITION_POS) - { - assert(!curHero->getSlot(pickedArtFromSlot)->getArt()); - LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, slot), ArtifactLocation(curHero, pickedArtFromSlot)); - pickedArtFromSlot = ArtifactPosition::PRE_FIRST; - } -} - -void CArtifactsOfHeroAltar::deleteFromVisible(const CArtifactInstance * artInst) -{ - const auto slot = visibleArtSet->getSlotByInstance(artInst); - visibleArtSet->removeArtifact(slot); - if(ArtifactUtils::isSlotBackpack(slot)) - { - scrollBackpackForArtSet(0, *visibleArtSet); - } - else - { - if(artInst->canBeDisassembled()) - { - for(const auto part : dynamic_cast(artInst)->constituentsInfo) - { - if(part.slot != ArtifactPosition::PRE_FIRST) - getArtPlace(part.slot)->setArtifact(nullptr); - } - } - } -} - -CArtifactsOfHeroMarket::CArtifactsOfHeroMarket(const Point & position) -{ - init( - std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1), - std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1), - position, - std::bind(&CArtifactsOfHeroMarket::scrollBackpack, this, _1)); -}; - -void CArtifactsOfHeroMarket::scrollBackpack(int offset) -{ - CArtifactsOfHeroBase::scrollBackpackForArtSet(offset, *curHero); - - // We may have highlight on one of backpack artifacts - if(selectArtCallback) - { - for(auto & artPlace : backpack) - { - if(artPlace->isMarked()) - { - selectArtCallback(artPlace.get()); - break; - } - } - } - safeRedraw(); -} - -void CWindowWithArtifacts::addSet(CArtifactsOfHeroPtr artSet) -{ - artSets.emplace_back(artSet); - std::visit([this](auto artSetWeak) - { - auto artSet = artSetWeak.lock(); - artSet->leftClickCallback = std::bind(&CWindowWithArtifacts::leftClickArtPlaceHero, this, _1, _2); - artSet->rightClickCallback = std::bind(&CWindowWithArtifacts::rightClickArtPlaceHero, this, _1, _2); - }, artSet); -} - -const CGHeroInstance * CWindowWithArtifacts::getHeroPickedArtifact() -{ - auto res = getState(); - if(res.has_value()) - return std::get(res.value()); - else - return nullptr; -} - -const CArtifactInstance * CWindowWithArtifacts::getPickedArtifact() -{ - auto res = getState(); - if(res.has_value()) - return std::get(res.value()); - else - return nullptr; -} - -void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace) -{ - const auto artSetWeak = findAOHbyRef(artsInst); - assert(artSetWeak.has_value()); - - if(artPlace.isLocked()) - return; - - const auto checkSpecialArts = [](const CGHeroInstance * hero, CHeroArtPlace & artPlace) -> bool - { - if(artPlace.getArt()->getTypeId() == ArtifactID::SPELLBOOK) - { - GH.pushIntT(hero, LOCPLINT, LOCPLINT->battleInt.get()); - return false; - } - if(artPlace.getArt()->getTypeId() == ArtifactID::CATAPULT) - { - // The Catapult must be equipped - std::vector> catapult(1, std::make_shared(CComponent::artifact, 3, 0)); - LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult); - return false; - } - return true; - }; - - std::visit( - [checkSpecialArts, this, &artPlace](auto artSetWeak) -> void - { - const auto artSetPtr = artSetWeak.lock(); - constexpr auto isMainWindow = std::is_same_v>; - constexpr auto isKingdomWindow = std::is_same_v>; - constexpr auto isAltarWindow = std::is_same_v>; - constexpr auto isMarketWindow = std::is_same_v>; - - // Hero(Main, Exchange) window, Kingdom window, Altar window left click handler - if constexpr(isMainWindow || isKingdomWindow || isAltarWindow) - { - const auto pickedArtInst = getPickedArtifact(); - const auto heroPickedArt = getHeroPickedArtifact(); - const auto hero = artSetPtr->getHero(); - - if(pickedArtInst) - { - auto srcLoc = ArtifactLocation(heroPickedArt, ArtifactPosition::TRANSITION_POS); - auto dstLoc = ArtifactLocation(hero, artPlace.slot); - auto isTransferAllowed = false; - - if(ArtifactUtils::isSlotBackpack(artPlace.slot)) - { - if(pickedArtInst->artType->isBig()) - { - // War machines cannot go to backpack - LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % pickedArtInst->artType->getNameTranslated())); - } - else - { - if(ArtifactUtils::isBackpackFreeSlots(heroPickedArt)) - isTransferAllowed = true; - else - LOCPLINT->showInfoDialog(CGI->generaltexth->translate("core.genrltxt.152")); - } - } - // Check if artifact transfer is possible - else if(pickedArtInst->canBePutAt(dstLoc, true) && (!artPlace.getArt() || hero->tempOwner == LOCPLINT->playerID)) - { - isTransferAllowed = true; - } - if constexpr(isKingdomWindow) - { - if(hero != heroPickedArt) - isTransferAllowed = false; - } - if(isTransferAllowed) - artSetPtr->swapArtifacts(srcLoc, dstLoc); - } - else - { - if(artPlace.getArt()) - { - if(artSetPtr->getHero()->tempOwner == LOCPLINT->playerID) - { - if(checkSpecialArts(hero, artPlace)) - artSetPtr->pickUpArtifact(artPlace); - } - else - { - for(const auto artSlot : ArtifactUtils::unmovableSlots()) - if(artPlace.slot == artSlot) - { - LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]); - break; - } - } - } - } - } - // Market window left click handler - else if constexpr(isMarketWindow) - { - if(artSetPtr->selectArtCallback && artPlace.getArt()) - { - if(artPlace.getArt()->artType->isTradable()) - { - artSetPtr->unmarkSlots(); - artPlace.selectSlot(true); - artSetPtr->selectArtCallback(&artPlace); - } - else - { - // This item can't be traded - LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]); - } - } - } - }, artSetWeak.value()); -} - -void CWindowWithArtifacts::rightClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace) -{ - const auto artSetWeak = findAOHbyRef(artsInst); - assert(artSetWeak.has_value()); - - if(artPlace.isLocked()) - return; - - std::visit( - [&artPlace](auto artSetWeak) -> void - { - const auto artSetPtr = artSetWeak.lock(); - constexpr auto isMainWindow = std::is_same_v>; - constexpr auto isKingdomWindow = std::is_same_v>; - constexpr auto isAltarWindow = std::is_same_v>; - constexpr auto isMarketWindow = std::is_same_v>; - - // Hero(Main, Exchange) window, Kingdom window right click handler - if constexpr(isMainWindow || isKingdomWindow) - { - if(artPlace.getArt()) - { - if(ArtifactUtils::askToDisassemble(artSetPtr->getHero(), artPlace.slot)) - { - return; - } - if(ArtifactUtils::askToAssemble(artSetPtr->getHero(), artPlace.slot)) - { - return; - } - if(artPlace.text.size()) - artPlace.LRClickableAreaWTextComp::clickRight(boost::logic::tribool::true_value, false); - } - } - // Altar window, Market window right click handler - else if constexpr(isAltarWindow || isMarketWindow) - { - if(artPlace.getArt() && artPlace.text.size()) - artPlace.LRClickableAreaWTextComp::clickRight(boost::logic::tribool::true_value, false); - } - }, artSetWeak.value()); -} - -void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc) -{ - updateSlots(artLoc.slot); -} - -void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) -{ - auto curState = getState(); - if(!curState.has_value()) - // Transition state. Nothing to do here. Just skip. Need to wait for final state. - return; - - // When moving one artifact onto another it leads to two art movements: dst->TRANSITION_POS; src->dst - // However after first movement we pick the art from TRANSITION_POS and the second movement coming when - // we have a different artifact may look surprising... but it's valid. - - auto pickedArtInst = std::get(curState.value()); - assert(srcLoc.isHolder(std::get(curState.value()))); - assert(srcLoc.getArt() == pickedArtInst); - - auto artifactMovedBody = [this, withRedraw, &srcLoc, &destLoc, &pickedArtInst](auto artSetWeak) -> void - { - auto artSetPtr = artSetWeak.lock(); - if(artSetPtr) - { - const auto hero = artSetPtr->getHero(); - if(artSetPtr->active) - { - if(pickedArtInst) - { - CCS->curh->dragAndDropCursor("artifact", pickedArtInst->artType->getIconIndex()); - if(srcLoc.isHolder(hero) || !std::is_same_v>) - artSetPtr->markPossibleSlots(pickedArtInst, hero->tempOwner == LOCPLINT->playerID); - } - else - { - artSetPtr->unmarkSlots(); - CCS->curh->dragAndDropCursor(nullptr); - } - } - if(withRedraw) - { - artSetPtr->updateWornSlots(); - artSetPtr->updateBackpackSlots(); - - // Update arts bonuses on window. - // TODO rework this part when CHeroWindow and CExchangeWindow are reworked - if(auto * chw = dynamic_cast(this)) - { - chw->update(hero, true); - } - else if(auto * cew = dynamic_cast(this)) - { - cew->updateWidgets(); - } - artSetPtr->safeRedraw(); - } - - // Make sure the status bar is updated so it does not display old text - if(destLoc.isHolder(hero)) - { - if(auto artPlace = artSetPtr->getArtPlace(destLoc.slot)) - artPlace->hover(true); - } - } - }; - - for(auto artSetWeak : artSets) - std::visit(artifactMovedBody, artSetWeak); -} - -void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation & artLoc) -{ - updateSlots(artLoc.slot); -} - -void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation & artLoc) -{ - updateSlots(artLoc.slot); -} - -void CWindowWithArtifacts::updateSlots(const ArtifactPosition & slot) -{ - auto updateSlotBody = [slot](auto artSetWeak) -> void - { - if(const auto artSetPtr = artSetWeak.lock()) - { - if(ArtifactUtils::isSlotEquipment(slot)) - artSetPtr->updateWornSlots(); - else if(ArtifactUtils::isSlotBackpack(slot)) - artSetPtr->updateBackpackSlots(); - - artSetPtr->safeRedraw(); - } - }; - - for(auto artSetWeak : artSets) - std::visit(updateSlotBody, artSetWeak); -} - -std::optional> CWindowWithArtifacts::getState() -{ - const CArtifactInstance * artInst = nullptr; - const CGHeroInstance * hero = nullptr; - size_t pickedCnt = 0; - - auto getHeroArtBody = [&hero, &artInst, &pickedCnt](auto artSetWeak) -> void - { - auto artSetPtr = artSetWeak.lock(); - if(artSetPtr) - { - if(const auto art = artSetPtr->getPickedArtifact()) - { - artInst = art; - hero = artSetPtr->getHero(); - pickedCnt += hero->artifactsTransitionPos.size(); - } - } - }; - for(auto artSetWeak : artSets) - std::visit(getHeroArtBody, artSetWeak); - - // The state is possible when the hero has placed an artifact in the ArtifactPosition::TRANSITION_POS, - // and the previous artifact has not yet removed from the ArtifactPosition::TRANSITION_POS. - // This is a transitional state. Then return nullopt. - if(pickedCnt > 1) - return std::nullopt; - else - return std::make_tuple(hero, artInst); -} - -std::optional CWindowWithArtifacts::findAOHbyRef(CArtifactsOfHeroBase & artsInst) -{ - std::optional res; - - auto findAOHBody = [&res, &artsInst](auto & artSetWeak) -> void - { - if(&artsInst == artSetWeak.lock().get()) - res = artSetWeak; - }; - - for(auto artSetWeak : artSets) - { - std::visit(findAOHBody, artSetWeak); - if(res.has_value()) - return res; - } - return res; -} - -bool ArtifactUtils::askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot) +bool ArtifactUtilsClient::askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot) { assert(hero); const auto art = hero->getArt(slot); @@ -1078,7 +305,7 @@ bool ArtifactUtils::askToAssemble(const CGHeroInstance * hero, const ArtifactPos return false; } -bool ArtifactUtils::askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot) +bool ArtifactUtilsClient::askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot) { assert(hero); const auto art = hero->getArt(slot); diff --git a/client/widgets/CArtifactHolder.h b/client/widgets/CArtifactHolder.h index 6104c6635..554a76044 100644 --- a/client/widgets/CArtifactHolder.h +++ b/client/widgets/CArtifactHolder.h @@ -15,11 +15,9 @@ VCMI_LIB_NAMESPACE_BEGIN struct ArtifactLocation; class CArtifactSet; -class CArtifactFittingSet; VCMI_LIB_NAMESPACE_END -class CArtifactsOfHeroBase; class CAnimImage; class CButton; @@ -94,135 +92,8 @@ protected: void createImage() override; }; -class CArtifactsOfHeroBase : public CIntObject +namespace ArtifactUtilsClient { -protected: - using ArtPlacePtr = std::shared_ptr; - using BpackScrollHandler = std::function; - -public: - using ArtPlaceMap = std::map; - using ClickHandler = std::function; - - const CGHeroInstance * curHero; - ClickHandler leftClickCallback; - ClickHandler rightClickCallback; - - CArtifactsOfHeroBase(); - virtual ~CArtifactsOfHeroBase(); - virtual void leftClickArtPlace(CHeroArtPlace & artPlace); - virtual void rightClickArtPlace(CHeroArtPlace & artPlace); - virtual void setHero(const CGHeroInstance * hero); - virtual const CGHeroInstance * getHero() const; - virtual void scrollBackpack(int offset); - virtual void safeRedraw(); - virtual void markPossibleSlots(const CArtifactInstance * art, bool assumeDestRemoved = true); - virtual void unmarkSlots(); - virtual ArtPlacePtr getArtPlace(const ArtifactPosition & slot); - virtual void updateWornSlots(); - virtual void updateBackpackSlots(); - virtual void updateSlot(const ArtifactPosition & slot); - virtual const CArtifactInstance * getPickedArtifact(); - -protected: - ArtPlaceMap artWorn; - std::vector backpack; - std::shared_ptr leftBackpackRoll; - std::shared_ptr rightBackpackRoll; - int backpackPos; // Position to display artifacts in heroes backpack - - const std::vector slotPos = - { - Point(509,30), Point(567,240), Point(509,80), //0-2 - Point(383,68), Point(564,183), Point(509,130), //3-5 - Point(431,68), Point(610,183), Point(515,295), //6-8 - Point(383,143), Point(399,194), Point(415,245), //9-11 - Point(431,296), Point(564,30), Point(610,30), //12-14 - Point(610,76), Point(610,122), Point(610,310), //15-17 - Point(381,296) //18 - }; - - virtual void init(CHeroArtPlace::ClickHandler lClickCallback, CHeroArtPlace::ClickHandler rClickCallback, - const Point & position, BpackScrollHandler scrollHandler); - // Assigns an artifacts to an artifact place depending on it's new slot ID - virtual void setSlotData(ArtPlacePtr artPlace, const ArtifactPosition & slot, const CArtifactSet & artSet); - virtual void scrollBackpackForArtSet(int offset, const CArtifactSet & artSet); -}; - -class CArtifactsOfHeroMain : public CArtifactsOfHeroBase -{ -public: - CArtifactsOfHeroMain(const Point & position); - void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc); - void pickUpArtifact(CHeroArtPlace & artPlace); -}; - -class CArtifactsOfHeroKingdom : public CArtifactsOfHeroBase -{ -public: - CArtifactsOfHeroKingdom(ArtPlaceMap ArtWorn, std::vector Backpack, - std::shared_ptr leftScroll, std::shared_ptr rightScroll); - void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc); - void pickUpArtifact(CHeroArtPlace & artPlace); -}; - -class CArtifactsOfHeroAltar : public CArtifactsOfHeroBase -{ -public: - std::set artifactsOnAltar; - ArtifactPosition pickedArtFromSlot; - std::shared_ptr visibleArtSet; - - CArtifactsOfHeroAltar(const Point & position); - void setHero(const CGHeroInstance * hero) override; - void updateWornSlots() override; - void updateBackpackSlots() override; - void scrollBackpack(int offset) override; - void pickUpArtifact(CHeroArtPlace & artPlace); - void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc); - void pickedArtMoveToAltar(const ArtifactPosition & slot); - void deleteFromVisible(const CArtifactInstance * artInst); -}; - -class CArtifactsOfHeroMarket : public CArtifactsOfHeroBase -{ -public: - std::function selectArtCallback; - - CArtifactsOfHeroMarket(const Point & position); - void scrollBackpack(int offset) override; -}; - -class CWindowWithArtifacts : public CArtifactHolder -{ -public: - using CArtifactsOfHeroPtr = std::variant< - std::weak_ptr, - std::weak_ptr, - std::weak_ptr, - std::weak_ptr>; - - void addSet(CArtifactsOfHeroPtr artSet); - const CGHeroInstance * getHeroPickedArtifact(); - const CArtifactInstance * getPickedArtifact(); - void leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace); - void rightClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace); - - void artifactRemoved(const ArtifactLocation & artLoc) override; - void artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) override; - void artifactDisassembled(const ArtifactLocation & artLoc) override; - void artifactAssembled(const ArtifactLocation & artLoc) override; - -private: - std::vector artSets; - - void updateSlots(const ArtifactPosition & slot); - std::optional> getState(); - std::optional findAOHbyRef(CArtifactsOfHeroBase & artsInst); -}; - -namespace ArtifactUtils -{ - bool askToAssemble(const CGHeroInstance* hero, const ArtifactPosition& slot); - bool askToDisassemble(const CGHeroInstance* hero, const ArtifactPosition& slot); + bool askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot); + bool askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot); } diff --git a/client/widgets/CArtifactsOfHeroAltar.cpp b/client/widgets/CArtifactsOfHeroAltar.cpp new file mode 100644 index 000000000..0e78ca713 --- /dev/null +++ b/client/widgets/CArtifactsOfHeroAltar.cpp @@ -0,0 +1,109 @@ +/* + * CArtifactsOfHeroAltar.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 "CArtifactsOfHeroAltar.h" + +#include "../CPlayerInterface.h" + +#include "../../CCallback.h" + +#include "../../lib/mapObjects/CGHeroInstance.h" + +CArtifactsOfHeroAltar::CArtifactsOfHeroAltar(const Point & position) + : visibleArtSet(ArtBearer::ArtBearer::HERO) +{ + init( + std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1), + std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1), + position, + std::bind(&CArtifactsOfHeroAltar::scrollBackpack, this, _1)); + pickedArtFromSlot = ArtifactPosition::PRE_FIRST; +}; + +void CArtifactsOfHeroAltar::setHero(const CGHeroInstance * hero) +{ + if(hero) + { + visibleArtSet.artifactsWorn = hero->artifactsWorn; + visibleArtSet.artifactsInBackpack = hero->artifactsInBackpack; + CArtifactsOfHeroBase::setHero(hero); + } +} + +void CArtifactsOfHeroAltar::updateWornSlots() +{ + for(auto place : artWorn) + setSlotData(getArtPlace(place.first), place.first, visibleArtSet); +} + +void CArtifactsOfHeroAltar::updateBackpackSlots() +{ + for(auto artPlace : backpack) + setSlotData(getArtPlace(artPlace->slot), artPlace->slot, visibleArtSet); +} + +void CArtifactsOfHeroAltar::scrollBackpack(int offset) +{ + CArtifactsOfHeroBase::scrollBackpackForArtSet(offset, visibleArtSet); + safeRedraw(); +} + +void CArtifactsOfHeroAltar::pickUpArtifact(CHeroArtPlace & artPlace) +{ + if(const auto art = artPlace.getArt()) + { + pickedArtFromSlot = artPlace.slot; + artPlace.setArtifact(nullptr); + deleteFromVisible(art); + if(ArtifactUtils::isSlotBackpack(pickedArtFromSlot)) + pickedArtFromSlot = curHero->getSlotByInstance(art); + assert(pickedArtFromSlot != ArtifactPosition::PRE_FIRST); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, pickedArtFromSlot), ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); + } +} + +void CArtifactsOfHeroAltar::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc) +{ + LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc); + const auto pickedArtInst = curHero->getArt(ArtifactPosition::TRANSITION_POS); + assert(pickedArtInst); + visibleArtSet.putArtifact(dstLoc.slot, const_cast(pickedArtInst)); +} + +void CArtifactsOfHeroAltar::pickedArtMoveToAltar(const ArtifactPosition & slot) +{ + if(ArtifactUtils::isSlotBackpack(slot) || ArtifactUtils::isSlotEquipment(slot) || slot == ArtifactPosition::TRANSITION_POS) + { + assert(!curHero->getSlot(pickedArtFromSlot)->getArt()); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, slot), ArtifactLocation(curHero, pickedArtFromSlot)); + pickedArtFromSlot = ArtifactPosition::PRE_FIRST; + } +} + +void CArtifactsOfHeroAltar::deleteFromVisible(const CArtifactInstance * artInst) +{ + const auto slot = visibleArtSet.getSlotByInstance(artInst); + visibleArtSet.removeArtifact(slot); + if(ArtifactUtils::isSlotBackpack(slot)) + { + scrollBackpackForArtSet(0, visibleArtSet); + } + else + { + if(artInst->canBeDisassembled()) + { + for(const auto part : dynamic_cast(artInst)->constituentsInfo) + { + if(part.slot != ArtifactPosition::PRE_FIRST) + getArtPlace(part.slot)->setArtifact(nullptr); + } + } + } +} \ No newline at end of file diff --git a/client/widgets/CArtifactsOfHeroAltar.h b/client/widgets/CArtifactsOfHeroAltar.h new file mode 100644 index 000000000..774376342 --- /dev/null +++ b/client/widgets/CArtifactsOfHeroAltar.h @@ -0,0 +1,32 @@ +/* + * CArtifactsOfHeroAltar.h, 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 + * + */ +#pragma once + +#include "CArtifactsOfHeroBase.h" + +#include "../../lib/CArtHandler.h" + +class CArtifactsOfHeroAltar : public CArtifactsOfHeroBase +{ +public: + std::set artifactsOnAltar; + ArtifactPosition pickedArtFromSlot; + CArtifactFittingSet visibleArtSet; + + CArtifactsOfHeroAltar(const Point & position); + void setHero(const CGHeroInstance * hero) override; + void updateWornSlots() override; + void updateBackpackSlots() override; + void scrollBackpack(int offset) override; + void pickUpArtifact(CHeroArtPlace & artPlace); + void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc); + void pickedArtMoveToAltar(const ArtifactPosition & slot); + void deleteFromVisible(const CArtifactInstance * artInst); +}; diff --git a/client/widgets/CArtifactsOfHeroBase.cpp b/client/widgets/CArtifactsOfHeroBase.cpp new file mode 100644 index 000000000..0f51ad8ad --- /dev/null +++ b/client/widgets/CArtifactsOfHeroBase.cpp @@ -0,0 +1,277 @@ +/* + * 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/CursorHandler.h" + +#include "Buttons.h" + +#include "../renderSDL/SDL_Extensions.h" +#include "../CPlayerInterface.h" +#include "../CGameInfo.h" + +#include "../../CCallback.h" + +#include "../../lib/mapObjects/CGHeroInstance.h" + +CArtifactsOfHeroBase::CArtifactsOfHeroBase() + : backpackPos(0), + curHero(nullptr) +{ +} + +CArtifactsOfHeroBase::~CArtifactsOfHeroBase() +{ + // TODO: cursor handling is CWindowWithArtifacts level. Should be moved when trading, kingdom and hero window are reworked + // This will interfere with the implementation of a separate backpack window + CCS->curh->dragAndDropCursor(nullptr); + + // Artifact located in artifactsTransitionPos should be returned + if(getPickedArtifact()) + { + auto slot = ArtifactUtils::getArtAnyPosition(curHero, curHero->artifactsTransitionPos.begin()->artifact->getTypeId()); + if(slot == ArtifactPosition::PRE_FIRST) + { + LOCPLINT->cb->eraseArtifactByClient(ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); + } + else + { + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS), ArtifactLocation(curHero, slot)); + } + } +} + +void CArtifactsOfHeroBase::init( + CHeroArtPlace::ClickHandler lClickCallback, + CHeroArtPlace::ClickHandler rClickCallback, + const Point & position, + BpackScrollHandler scrollHandler) +{ + // CArtifactsOfHeroBase::init may be transform to CArtifactsOfHeroBase::CArtifactsOfHeroBase if OBJECT_CONSTRUCTION_CAPTURING is removed + OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE); + pos += position; + for(int g = 0; g < GameConstants::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(nullptr); + artPlace.second->leftClickCallback = lClickCallback; + artPlace.second->rightClickCallback = rClickCallback; + } + for(auto artPlace : backpack) + { + artPlace->setArtifact(nullptr); + artPlace->leftClickCallback = lClickCallback; + artPlace->rightClickCallback = rClickCallback; + } + leftBackpackRoll = std::make_shared(Point(379, 364), "hsbtns3.def", CButton::tooltip(), [scrollHandler]() { scrollHandler(-1); }, SDLK_LEFT); + rightBackpackRoll = std::make_shared(Point(632, 364), "hsbtns5.def", CButton::tooltip(), [scrollHandler]() { scrollHandler(+1); }, SDLK_RIGHT); + leftBackpackRoll->block(true); + rightBackpackRoll->block(true); +} + +void CArtifactsOfHeroBase::leftClickArtPlace(CHeroArtPlace & artPlace) +{ + if(leftClickCallback) + leftClickCallback(*this, artPlace); +} + +void CArtifactsOfHeroBase::rightClickArtPlace(CHeroArtPlace & artPlace) +{ + if(rightClickCallback) + rightClickCallback(*this, artPlace); +} + +void CArtifactsOfHeroBase::setHero(const CGHeroInstance * hero) +{ + curHero = hero; + if(curHero->artifactsInBackpack.size() > 0) + backpackPos %= curHero->artifactsInBackpack.size(); + else + backpackPos = 0; + + for(auto slot : artWorn) + { + setSlotData(slot.second, slot.first, *curHero); + } + scrollBackpackForArtSet(0, *curHero); +} + +const CGHeroInstance * CArtifactsOfHeroBase::getHero() const +{ + return curHero; +} + +void CArtifactsOfHeroBase::scrollBackpack(int offset) +{ + scrollBackpackForArtSet(offset, *curHero); + safeRedraw(); +} + +void CArtifactsOfHeroBase::scrollBackpackForArtSet(int offset, const CArtifactSet & artSet) +{ + // offset==-1 => to left; offset==1 => to right + using slotInc = std::function; + auto artsInBackpack = static_cast(artSet.artifactsInBackpack.size()); + auto scrollingPossible = artsInBackpack > backpack.size(); + + slotInc inc_straight = [](ArtifactPosition & slot) -> ArtifactPosition + { + return slot + 1; + }; + slotInc inc_ring = [artsInBackpack](ArtifactPosition & slot) -> ArtifactPosition + { + return ArtifactPosition(GameConstants::BACKPACK_START + (slot - GameConstants::BACKPACK_START + 1) % artsInBackpack); + }; + slotInc inc; + if(scrollingPossible) + inc = inc_ring; + else + inc = inc_straight; + + backpackPos += offset; + if(backpackPos < 0) + backpackPos += artsInBackpack; + + if(artsInBackpack) + backpackPos %= artsInBackpack; + + auto slot = ArtifactPosition(GameConstants::BACKPACK_START + backpackPos); + for(auto artPlace : backpack) + { + setSlotData(artPlace, slot, artSet); + slot = inc(slot); + } + + // Blocking scrolling if there is not enough artifacts to scroll + leftBackpackRoll->block(!scrollingPossible); + rightBackpackRoll->block(!scrollingPossible); +} + +void CArtifactsOfHeroBase::safeRedraw() +{ + if(active) + { + if(parent) + parent->redraw(); + else + redraw(); + } +} + +void CArtifactsOfHeroBase::markPossibleSlots(const CArtifactInstance * art, bool assumeDestRemoved) +{ + for(auto artPlace : artWorn) + artPlace.second->selectSlot(art->artType->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)) + { + if(artWorn.find(slot) == artWorn.end()) + { + logGlobal->error("CArtifactsOfHero::getArtPlace: invalid slot %d", slot); + return nullptr; + } + return artWorn[slot]; + } + if(ArtifactUtils::isSlotBackpack(slot)) + { + for(ArtPlacePtr artPlace : backpack) + if(artPlace->slot == slot) + return artPlace; + return nullptr; + } + else + { + return nullptr; + } +} + +void CArtifactsOfHeroBase::updateWornSlots() +{ + for(auto place : artWorn) + updateSlot(place.first); +} + +void CArtifactsOfHeroBase::updateBackpackSlots() +{ + for(auto artPlace : backpack) + updateSlot(artPlace->slot); + scrollBackpackForArtSet(0, *curHero); +} + +void CArtifactsOfHeroBase::updateSlot(const ArtifactPosition & slot) +{ + setSlotData(getArtPlace(slot), slot, *curHero); +} + +const CArtifactInstance * CArtifactsOfHeroBase::getPickedArtifact() +{ + // Returns only the picked up artifact. Not just highlighted like in the trading window. + if(!curHero || curHero->artifactsTransitionPos.empty()) + return nullptr; + else + return curHero->getArt(ArtifactPosition::TRANSITION_POS); +} + +void CArtifactsOfHeroBase::setSlotData(ArtPlacePtr artPlace, const ArtifactPosition & slot, const CArtifactSet & artSet) +{ + // Spurious call from artifactMoved in attempt to update hidden backpack slot + if(!artPlace && ArtifactUtils::isSlotBackpack(slot)) + { + return; + } + + artPlace->slot = slot; + if(auto slotInfo = artSet.getSlot(slot)) + { + artPlace->lockSlot(slotInfo->locked); + artPlace->setArtifact(slotInfo->artifact); + if(!slotInfo->artifact->canBeDisassembled()) + { + // If the artifact is part of at least one combined artifact, add additional information + std::map arts; + for(const auto combinedArt : slotInfo->artifact->artType->constituentOf) + { + arts.insert(std::pair(combinedArt, 0)); + for(const auto part : *combinedArt->constituents) + if(artSet.hasArt(part->getId(), true)) + arts.at(combinedArt)++; + } + artPlace->addCombinedArtInfo(arts); + } + } + else + { + artPlace->setArtifact(nullptr); + } +} diff --git a/client/widgets/CArtifactsOfHeroBase.h b/client/widgets/CArtifactsOfHeroBase.h new file mode 100644 index 000000000..d21e5983e --- /dev/null +++ b/client/widgets/CArtifactsOfHeroBase.h @@ -0,0 +1,67 @@ +/* + * CArtifactsOfHeroBase.h, 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 + * + */ +#pragma once + +#include "CArtifactHolder.h" + +class CArtifactsOfHeroBase : public CIntObject +{ +protected: + using ArtPlacePtr = std::shared_ptr; + using BpackScrollHandler = std::function; + +public: + using ArtPlaceMap = std::map; + using ClickHandler = std::function; + + const CGHeroInstance * curHero; + ClickHandler leftClickCallback; + ClickHandler rightClickCallback; + + CArtifactsOfHeroBase(); + virtual ~CArtifactsOfHeroBase(); + virtual void leftClickArtPlace(CHeroArtPlace & artPlace); + virtual void rightClickArtPlace(CHeroArtPlace & artPlace); + virtual void setHero(const CGHeroInstance * hero); + virtual const CGHeroInstance * getHero() const; + virtual void scrollBackpack(int offset); + virtual void safeRedraw(); + virtual void markPossibleSlots(const CArtifactInstance * art, bool assumeDestRemoved = true); + virtual void unmarkSlots(); + virtual ArtPlacePtr getArtPlace(const ArtifactPosition & slot); + virtual void updateWornSlots(); + virtual void updateBackpackSlots(); + virtual void updateSlot(const ArtifactPosition & slot); + virtual const CArtifactInstance * getPickedArtifact(); + +protected: + ArtPlaceMap artWorn; + std::vector backpack; + std::shared_ptr leftBackpackRoll; + std::shared_ptr rightBackpackRoll; + int backpackPos; // Position to display artifacts in heroes backpack + + const std::vector slotPos = + { + Point(509,30), Point(567,240), Point(509,80), //0-2 + Point(383,68), Point(564,183), Point(509,130), //3-5 + Point(431,68), Point(610,183), Point(515,295), //6-8 + Point(383,143), Point(399,194), Point(415,245), //9-11 + Point(431,296), Point(564,30), Point(610,30), //12-14 + Point(610,76), Point(610,122), Point(610,310), //15-17 + Point(381,296) //18 + }; + + virtual void init(CHeroArtPlace::ClickHandler lClickCallback, CHeroArtPlace::ClickHandler rClickCallback, + const Point & position, BpackScrollHandler scrollHandler); + // Assigns an artifacts to an artifact place depending on it's new slot ID + virtual void setSlotData(ArtPlacePtr artPlace, const ArtifactPosition & slot, const CArtifactSet & artSet); + virtual void scrollBackpackForArtSet(int offset, const CArtifactSet & artSet); +}; diff --git a/client/widgets/CArtifactsOfHeroKingdom.cpp b/client/widgets/CArtifactsOfHeroKingdom.cpp new file mode 100644 index 000000000..3b9d2716d --- /dev/null +++ b/client/widgets/CArtifactsOfHeroKingdom.cpp @@ -0,0 +1,54 @@ +/* + * CArtifactsOfHeroKingdom.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 "CArtifactsOfHeroKingdom.h" + +#include "Buttons.h" + +#include "../CPlayerInterface.h" + +#include "../../CCallback.h" + +CArtifactsOfHeroKingdom::CArtifactsOfHeroKingdom(ArtPlaceMap ArtWorn, std::vector Backpack, + std::shared_ptr leftScroll, std::shared_ptr rightScroll) +{ + artWorn = ArtWorn; + backpack = Backpack; + leftBackpackRoll = leftScroll; + rightBackpackRoll = rightScroll; + + for(auto artPlace : artWorn) + { + artPlace.second->slot = artPlace.first; + artPlace.second->setArtifact(nullptr); + artPlace.second->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1); + artPlace.second->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1); + } + for(auto artPlace : backpack) + { + artPlace->setArtifact(nullptr); + artPlace->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1); + artPlace->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1); + } + leftBackpackRoll->addCallback(std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, -1)); + rightBackpackRoll->addCallback(std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, +1)); +} + +void CArtifactsOfHeroKingdom::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc) +{ + LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc); +} + +void CArtifactsOfHeroKingdom::pickUpArtifact(CHeroArtPlace & artPlace) +{ + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, artPlace.slot), + ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); +} + diff --git a/client/widgets/CArtifactsOfHeroKingdom.h b/client/widgets/CArtifactsOfHeroKingdom.h new file mode 100644 index 000000000..e79dd4b8d --- /dev/null +++ b/client/widgets/CArtifactsOfHeroKingdom.h @@ -0,0 +1,27 @@ +/* + * CArtifactsOfHeroKingdom.h, 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 + * + */ +#pragma once + +#include "CArtifactsOfHeroBase.h" + +VCMI_LIB_NAMESPACE_BEGIN + +struct ArtifactLocation; + +VCMI_LIB_NAMESPACE_END + +class CArtifactsOfHeroKingdom : public CArtifactsOfHeroBase +{ +public: + CArtifactsOfHeroKingdom(ArtPlaceMap ArtWorn, std::vector Backpack, + std::shared_ptr leftScroll, std::shared_ptr rightScroll); + void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc); + void pickUpArtifact(CHeroArtPlace & artPlace); +}; \ No newline at end of file diff --git a/client/widgets/CArtifactsOfHeroMain.cpp b/client/widgets/CArtifactsOfHeroMain.cpp new file mode 100644 index 000000000..d4038b360 --- /dev/null +++ b/client/widgets/CArtifactsOfHeroMain.cpp @@ -0,0 +1,35 @@ +/* + * CArtifactsOfHeroMain.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 "CArtifactsOfHeroMain.h" + +#include "../CPlayerInterface.h" + +#include "../../CCallback.h" + +CArtifactsOfHeroMain::CArtifactsOfHeroMain(const Point & position) +{ + init( + std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1), + std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1), + position, + std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, _1)); +} + +void CArtifactsOfHeroMain::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc) +{ + LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc); +} + +void CArtifactsOfHeroMain::pickUpArtifact(CHeroArtPlace & artPlace) +{ + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, artPlace.slot), + ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); +} \ No newline at end of file diff --git a/client/widgets/CArtifactsOfHeroMain.h b/client/widgets/CArtifactsOfHeroMain.h new file mode 100644 index 000000000..bbe57781c --- /dev/null +++ b/client/widgets/CArtifactsOfHeroMain.h @@ -0,0 +1,26 @@ +/* + * CArtifactsOfHeroMain.h, 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 + * + */ +#pragma once + +#include "CArtifactsOfHeroBase.h" + +VCMI_LIB_NAMESPACE_BEGIN + +struct ArtifactLocation; + +VCMI_LIB_NAMESPACE_END + +class CArtifactsOfHeroMain : public CArtifactsOfHeroBase +{ +public: + CArtifactsOfHeroMain(const Point & position); + void swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc); + void pickUpArtifact(CHeroArtPlace & artPlace); +}; diff --git a/client/widgets/CArtifactsOfHeroMarket.cpp b/client/widgets/CArtifactsOfHeroMarket.cpp new file mode 100644 index 000000000..74878b348 --- /dev/null +++ b/client/widgets/CArtifactsOfHeroMarket.cpp @@ -0,0 +1,41 @@ +/* + * CArtifactsOfHeroMarket.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 "CArtifactsOfHeroMarket.h" + +#include "../../lib/mapObjects/CGHeroInstance.h" + +CArtifactsOfHeroMarket::CArtifactsOfHeroMarket(const Point & position) +{ + init( + std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1), + std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1), + position, + std::bind(&CArtifactsOfHeroMarket::scrollBackpack, this, _1)); +}; + +void CArtifactsOfHeroMarket::scrollBackpack(int offset) +{ + CArtifactsOfHeroBase::scrollBackpackForArtSet(offset, *curHero); + + // We may have highlight on one of backpack artifacts + if(selectArtCallback) + { + for(auto & artPlace : backpack) + { + if(artPlace->isMarked()) + { + selectArtCallback(artPlace.get()); + break; + } + } + } + safeRedraw(); +} \ No newline at end of file diff --git a/client/widgets/CArtifactsOfHeroMarket.h b/client/widgets/CArtifactsOfHeroMarket.h new file mode 100644 index 000000000..c88ff0494 --- /dev/null +++ b/client/widgets/CArtifactsOfHeroMarket.h @@ -0,0 +1,21 @@ +/* + * CArtifactsOfHeroMarket.h, 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 + * + */ +#pragma once + +#include "CArtifactsOfHeroBase.h" + +class CArtifactsOfHeroMarket : public CArtifactsOfHeroBase +{ +public: + std::function selectArtCallback; + + CArtifactsOfHeroMarket(const Point & position); + void scrollBackpack(int offset) override; +}; diff --git a/client/widgets/CWindowWithArtifacts.cpp b/client/widgets/CWindowWithArtifacts.cpp new file mode 100644 index 000000000..01eca2323 --- /dev/null +++ b/client/widgets/CWindowWithArtifacts.cpp @@ -0,0 +1,363 @@ +/* + * CWindowWithArtifacts.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 "CWindowWithArtifacts.h" + +#include "../gui/CGuiHandler.h" +#include "../gui/CursorHandler.h" + +#include "CComponent.h" + +#include "../windows/CHeroWindow.h" +#include "../windows/CSpellWindow.h" +#include "../windows/GUIClasses.h" +#include "../CPlayerInterface.h" +#include "../CGameInfo.h" + +#include "../../lib/CGeneralTextHandler.h" +#include "../../lib/mapObjects/CGHeroInstance.h" + +void CWindowWithArtifacts::addSet(CArtifactsOfHeroPtr artSet) +{ + artSets.emplace_back(artSet); + std::visit([this](auto artSetWeak) + { + auto artSet = artSetWeak.lock(); + artSet->leftClickCallback = std::bind(&CWindowWithArtifacts::leftClickArtPlaceHero, this, _1, _2); + artSet->rightClickCallback = std::bind(&CWindowWithArtifacts::rightClickArtPlaceHero, this, _1, _2); + }, artSet); +} + +const CGHeroInstance * CWindowWithArtifacts::getHeroPickedArtifact() +{ + auto res = getState(); + if(res.has_value()) + return std::get(res.value()); + else + return nullptr; +} + +const CArtifactInstance * CWindowWithArtifacts::getPickedArtifact() +{ + auto res = getState(); + if(res.has_value()) + return std::get(res.value()); + else + return nullptr; +} + +void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace) +{ + const auto artSetWeak = findAOHbyRef(artsInst); + assert(artSetWeak.has_value()); + + if(artPlace.isLocked()) + return; + + const auto checkSpecialArts = [](const CGHeroInstance * hero, CHeroArtPlace & artPlace) -> bool + { + if(artPlace.getArt()->getTypeId() == ArtifactID::SPELLBOOK) + { + GH.pushIntT(hero, LOCPLINT, LOCPLINT->battleInt.get()); + return false; + } + if(artPlace.getArt()->getTypeId() == ArtifactID::CATAPULT) + { + // The Catapult must be equipped + std::vector> catapult(1, std::make_shared(CComponent::artifact, 3, 0)); + LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult); + return false; + } + return true; + }; + + std::visit( + [checkSpecialArts, this, &artPlace](auto artSetWeak) -> void + { + const auto artSetPtr = artSetWeak.lock(); + + // Hero(Main, Exchange) window, Kingdom window, Altar window left click handler + if constexpr( + std::is_same_v> || + std::is_same_v> || + std::is_same_v>) + { + const auto pickedArtInst = getPickedArtifact(); + const auto heroPickedArt = getHeroPickedArtifact(); + const auto hero = artSetPtr->getHero(); + + if(pickedArtInst) + { + auto srcLoc = ArtifactLocation(heroPickedArt, ArtifactPosition::TRANSITION_POS); + auto dstLoc = ArtifactLocation(hero, artPlace.slot); + auto isTransferAllowed = false; + + if(ArtifactUtils::isSlotBackpack(artPlace.slot)) + { + if(pickedArtInst->artType->isBig()) + { + // War machines cannot go to backpack + LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % pickedArtInst->artType->getNameTranslated())); + } + else + { + if(ArtifactUtils::isBackpackFreeSlots(heroPickedArt)) + isTransferAllowed = true; + else + LOCPLINT->showInfoDialog(CGI->generaltexth->translate("core.genrltxt.152")); + } + } + // Check if artifact transfer is possible + else if(pickedArtInst->canBePutAt(dstLoc, true) && (!artPlace.getArt() || hero->tempOwner == LOCPLINT->playerID)) + { + isTransferAllowed = true; + } + if constexpr(std::is_same_v>) + { + if(hero != heroPickedArt) + isTransferAllowed = false; + } + if(isTransferAllowed) + artSetPtr->swapArtifacts(srcLoc, dstLoc); + } + else + { + if(artPlace.getArt()) + { + if(artSetPtr->getHero()->tempOwner == LOCPLINT->playerID) + { + if(checkSpecialArts(hero, artPlace)) + artSetPtr->pickUpArtifact(artPlace); + } + else + { + for(const auto artSlot : ArtifactUtils::unmovableSlots()) + if(artPlace.slot == artSlot) + { + LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]); + break; + } + } + } + } + } + // Market window left click handler + else if constexpr(std::is_same_v>) + { + if(artSetPtr->selectArtCallback && artPlace.getArt()) + { + if(artPlace.getArt()->artType->isTradable()) + { + artSetPtr->unmarkSlots(); + artPlace.selectSlot(true); + artSetPtr->selectArtCallback(&artPlace); + } + else + { + // This item can't be traded + LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]); + } + } + } + }, artSetWeak.value()); +} + +void CWindowWithArtifacts::rightClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace) +{ + const auto artSetWeak = findAOHbyRef(artsInst); + assert(artSetWeak.has_value()); + + if(artPlace.isLocked()) + return; + + std::visit( + [&artPlace](auto artSetWeak) -> void + { + const auto artSetPtr = artSetWeak.lock(); + + // Hero(Main, Exchange) window, Kingdom window right click handler + if constexpr( + std::is_same_v> || + std::is_same_v>) + { + if(artPlace.getArt()) + { + if(ArtifactUtilsClient::askToDisassemble(artSetPtr->getHero(), artPlace.slot)) + { + return; + } + if(ArtifactUtilsClient::askToAssemble(artSetPtr->getHero(), artPlace.slot)) + { + return; + } + if(artPlace.text.size()) + artPlace.LRClickableAreaWTextComp::clickRight(boost::logic::tribool::true_value, false); + } + } + // Altar window, Market window right click handler + else if constexpr( + std::is_same_v> || + std::is_same_v>) + { + if(artPlace.getArt() && artPlace.text.size()) + artPlace.LRClickableAreaWTextComp::clickRight(boost::logic::tribool::true_value, false); + } + }, artSetWeak.value()); +} + +void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc) +{ + updateSlots(artLoc.slot); +} + +void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) +{ + auto curState = getState(); + if(!curState.has_value()) + // Transition state. Nothing to do here. Just skip. Need to wait for final state. + return; + + // When moving one artifact onto another it leads to two art movements: dst->TRANSITION_POS; src->dst + // However after first movement we pick the art from TRANSITION_POS and the second movement coming when + // we have a different artifact may look surprising... but it's valid. + + auto pickedArtInst = std::get(curState.value()); + assert(srcLoc.isHolder(std::get(curState.value()))); + assert(srcLoc.getArt() == pickedArtInst); + + auto artifactMovedBody = [this, withRedraw, &srcLoc, &destLoc, &pickedArtInst](auto artSetWeak) -> void + { + auto artSetPtr = artSetWeak.lock(); + if(artSetPtr) + { + const auto hero = artSetPtr->getHero(); + if(artSetPtr->active) + { + if(pickedArtInst) + { + CCS->curh->dragAndDropCursor("artifact", pickedArtInst->artType->getIconIndex()); + if(srcLoc.isHolder(hero) || !std::is_same_v>) + artSetPtr->markPossibleSlots(pickedArtInst, hero->tempOwner == LOCPLINT->playerID); + } + else + { + artSetPtr->unmarkSlots(); + CCS->curh->dragAndDropCursor(nullptr); + } + } + if(withRedraw) + { + artSetPtr->updateWornSlots(); + artSetPtr->updateBackpackSlots(); + + // Update arts bonuses on window. + // TODO rework this part when CHeroWindow and CExchangeWindow are reworked + if(auto * chw = dynamic_cast(this)) + { + chw->update(hero, true); + } + else if(auto * cew = dynamic_cast(this)) + { + cew->updateWidgets(); + } + artSetPtr->safeRedraw(); + } + + // Make sure the status bar is updated so it does not display old text + if(destLoc.isHolder(hero)) + { + if(auto artPlace = artSetPtr->getArtPlace(destLoc.slot)) + artPlace->hover(true); + } + } + }; + + for(auto artSetWeak : artSets) + std::visit(artifactMovedBody, artSetWeak); +} + +void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation & artLoc) +{ + updateSlots(artLoc.slot); +} + +void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation & artLoc) +{ + updateSlots(artLoc.slot); +} + +void CWindowWithArtifacts::updateSlots(const ArtifactPosition & slot) +{ + auto updateSlotBody = [slot](auto artSetWeak) -> void + { + if(const auto artSetPtr = artSetWeak.lock()) + { + if(ArtifactUtils::isSlotEquipment(slot)) + artSetPtr->updateWornSlots(); + else if(ArtifactUtils::isSlotBackpack(slot)) + artSetPtr->updateBackpackSlots(); + + artSetPtr->safeRedraw(); + } + }; + + for(auto artSetWeak : artSets) + std::visit(updateSlotBody, artSetWeak); +} + +std::optional> CWindowWithArtifacts::getState() +{ + const CArtifactInstance * artInst = nullptr; + const CGHeroInstance * hero = nullptr; + size_t pickedCnt = 0; + + auto getHeroArtBody = [&hero, &artInst, &pickedCnt](auto artSetWeak) -> void + { + auto artSetPtr = artSetWeak.lock(); + if(artSetPtr) + { + if(const auto art = artSetPtr->getPickedArtifact()) + { + artInst = art; + hero = artSetPtr->getHero(); + pickedCnt += hero->artifactsTransitionPos.size(); + } + } + }; + for(auto artSetWeak : artSets) + std::visit(getHeroArtBody, artSetWeak); + + // The state is possible when the hero has placed an artifact in the ArtifactPosition::TRANSITION_POS, + // and the previous artifact has not yet removed from the ArtifactPosition::TRANSITION_POS. + // This is a transitional state. Then return nullopt. + if(pickedCnt > 1) + return std::nullopt; + else + return std::make_tuple(hero, artInst); +} + +std::optional CWindowWithArtifacts::findAOHbyRef(CArtifactsOfHeroBase & artsInst) +{ + std::optional res; + + auto findAOHBody = [&res, &artsInst](auto & artSetWeak) -> void + { + if(&artsInst == artSetWeak.lock().get()) + res = artSetWeak; + }; + + for(auto artSetWeak : artSets) + { + std::visit(findAOHBody, artSetWeak); + if(res.has_value()) + return res; + } + return res; +} \ No newline at end of file diff --git a/client/widgets/CWindowWithArtifacts.h b/client/widgets/CWindowWithArtifacts.h new file mode 100644 index 000000000..e7517d9be --- /dev/null +++ b/client/widgets/CWindowWithArtifacts.h @@ -0,0 +1,44 @@ +/* + * CWindowWithArtifacts.h, 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 + * + */ +#pragma once + +#include "CArtifactHolder.h" +#include "CArtifactsOfHeroMain.h" +#include "CArtifactsOfHeroKingdom.h" +#include "CArtifactsOfHeroAltar.h" +#include "CArtifactsOfHeroMarket.h" + +class CWindowWithArtifacts : public CArtifactHolder +{ +public: + using CArtifactsOfHeroPtr = std::variant< + std::weak_ptr, + std::weak_ptr, + std::weak_ptr, + std::weak_ptr>; + + void addSet(CArtifactsOfHeroPtr artSet); + const CGHeroInstance * getHeroPickedArtifact(); + const CArtifactInstance * getPickedArtifact(); + void leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace); + void rightClickArtPlaceHero(CArtifactsOfHeroBase & artsInst, CHeroArtPlace & artPlace); + + void artifactRemoved(const ArtifactLocation & artLoc) override; + void artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) override; + void artifactDisassembled(const ArtifactLocation & artLoc) override; + void artifactAssembled(const ArtifactLocation & artLoc) override; + +private: + std::vector artSets; + + void updateSlots(const ArtifactPosition & slot); + std::optional> getState(); + std::optional findAOHbyRef(CArtifactsOfHeroBase & artsInst); +}; diff --git a/client/windows/CHeroWindow.h b/client/windows/CHeroWindow.h index de9b5ec40..81b2f5cad 100644 --- a/client/windows/CHeroWindow.h +++ b/client/windows/CHeroWindow.h @@ -10,7 +10,7 @@ #pragma once #include "../../lib/HeroBonus.h" -#include "../widgets/CArtifactHolder.h" +#include "../widgets/CWindowWithArtifacts.h" #include "../widgets/CGarrisonInt.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/client/windows/CKingdomInterface.h b/client/windows/CKingdomInterface.h index 10b106df8..f9f70ab22 100644 --- a/client/windows/CKingdomInterface.h +++ b/client/windows/CKingdomInterface.h @@ -9,7 +9,7 @@ */ #pragma once -#include "../widgets/CArtifactHolder.h" +#include "../widgets/CWindowWithArtifacts.h" #include "../widgets/CGarrisonInt.h" class CButton; diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index d7a343aea..d994b74e6 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -1305,7 +1305,7 @@ void CAltarWindow::SacrificeAll() else { auto artifactsOfHero = std::dynamic_pointer_cast(arts); - for(const auto & aw : artifactsOfHero->visibleArtSet->artifactsWorn) + for(const auto & aw : artifactsOfHero->visibleArtSet.artifactsWorn) { if(!aw.second.locked) moveArtToAltar(nullptr, aw.second.artifact); @@ -1471,9 +1471,9 @@ int CAltarWindow::firstFreeSlot() void CAltarWindow::SacrificeBackpack() { auto artsAltar = std::dynamic_pointer_cast(arts); - while(!artsAltar->visibleArtSet->artifactsInBackpack.empty()) + while(!artsAltar->visibleArtSet.artifactsInBackpack.empty()) { - if(!putOnAltar(nullptr, artsAltar->visibleArtSet->artifactsInBackpack[0].artifact)) + if(!putOnAltar(nullptr, artsAltar->visibleArtSet.artifactsInBackpack[0].artifact)) break; }; calcTotalExp(); diff --git a/client/windows/CTradeWindow.h b/client/windows/CTradeWindow.h index c1f236cda..7c93497ce 100644 --- a/client/windows/CTradeWindow.h +++ b/client/windows/CTradeWindow.h @@ -9,7 +9,7 @@ */ #pragma once -#include "../widgets/CArtifactHolder.h" +#include "../widgets/CWindowWithArtifacts.h" #include "CWindowObject.h" #include "../../lib/FunctionList.h" diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index 0168e5491..94f64aa29 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -14,7 +14,7 @@ #include "../lib/ResourceSet.h" #include "../lib/CConfigHandler.h" #include "../lib/int3.h" -#include "../widgets/CArtifactHolder.h" +#include "../widgets/CWindowWithArtifacts.h" #include "../widgets/CGarrisonInt.h" #include "../widgets/Images.h" From c7bee037d2c06c56893d631651288c33e3cb95ae Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 1 May 2023 14:44:52 +0300 Subject: [PATCH 08/25] Fix potential nullptr-to-reference conversion --- server/CVCMIServer.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 1c95a2c3e..9835cb215 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -375,10 +375,10 @@ class CVCMIServerPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor) { private: CVCMIServer & handler; - CGameHandler & gh; + std::shared_ptr gh; public: - CVCMIServerPackVisitor(CVCMIServer & handler, CGameHandler & gh) + CVCMIServerPackVisitor(CVCMIServer & handler, std::shared_ptr gh) :handler(handler), gh(gh) { } @@ -392,7 +392,10 @@ public: virtual void visitForServer(CPackForServer & serverPack) override { - gh.handleReceivedPack(&serverPack); + if (gh) + gh->handleReceivedPack(&serverPack); + else + logNetwork->error("Received pack for game server while in lobby!"); } virtual void visitForClient(CPackForClient & clientPack) override @@ -432,7 +435,7 @@ void CVCMIServer::threadHandleClient(std::shared_ptr c) break; } - CVCMIServerPackVisitor visitor(*this, *this->gh); + CVCMIServerPackVisitor visitor(*this, this->gh); pack->visit(visitor); } #ifndef _MSC_VER From 59bc9326e93640406d0964e99f39229df5587117 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Thu, 27 Apr 2023 00:18:43 +0400 Subject: [PATCH 09/25] Remove excess pointer from market interface --- AI/VCAI/Goals/CollectRes.cpp | 10 ++++--- client/CPlayerInterface.cpp | 3 +- client/windows/CTradeWindow.cpp | 48 +++++++++++++++++-------------- client/windows/GUIClasses.cpp | 17 ++++------- lib/mapObjects/CGMarket.cpp | 5 +--- lib/mapObjects/CGMarket.h | 12 ++------ lib/mapObjects/CGTownInstance.cpp | 24 +--------------- lib/mapObjects/CGTownInstance.h | 4 --- lib/registerTypes/RegisterTypes.h | 4 +-- server/CGameHandler.cpp | 6 ++-- 10 files changed, 48 insertions(+), 85 deletions(-) diff --git a/AI/VCAI/Goals/CollectRes.cpp b/AI/VCAI/Goals/CollectRes.cpp index 4599bed34..2e7c21fa7 100644 --- a/AI/VCAI/Goals/CollectRes.cpp +++ b/AI/VCAI/Goals/CollectRes.cpp @@ -149,9 +149,10 @@ TSubgoal CollectRes::whatToDoToTrade() markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool { - if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID)) + auto * o = dynamic_cast(market); + if(o && !(o->ID == Obj::TOWN && o->tempOwner == ai->playerID)) { - if (!ai->isAccessible(market->o->visitablePos())) + if(!ai->isAccessible(o->visitablePos())) return true; } return false; @@ -182,9 +183,10 @@ TSubgoal CollectRes::whatToDoToTrade() if (howManyCanWeBuy >= value) { - auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace + auto * o = dynamic_cast(m); + auto backObj = cb->getTopObj(o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace assert(backObj); - auto objid = m->o->id.getNum(); + auto objid = o->id.getNum(); if (backObj->tempOwner != ai->playerID) //top object not owned { return sptr(VisitObj(objid)); //just go there diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 14714bfa6..2c5647a6f 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1709,7 +1709,8 @@ void CPlayerInterface::stopMovement() void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInstance *visitor) { EVENT_HANDLER_CALLED_BY_CLIENT; - if (market->o->ID == Obj::ALTAR_OF_SACRIFICE) + auto * o = dynamic_cast(market); + if(o && o->ID == Obj::ALTAR_OF_SACRIFICE) { //EEMarketMode mode = market->availableModes().front(); if (market->allowsTrade(EMarketMode::ARTIFACT_EXP) && visitor->getAlignment() != EAlignment::EVIL) diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index d994b74e6..01e69e3f6 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -679,7 +679,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta std::string title; - if(market->o->ID == Obj::TOWN) + if(auto * o = dynamic_cast(market)) { switch (mode) { @@ -687,11 +687,11 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta title = (*CGI->townh)[ETownType::STRONGHOLD]->town->buildings[BuildingID::FREELANCERS_GUILD]->getNameTranslated(); break; case EMarketMode::RESOURCE_ARTIFACT: - title = (*CGI->townh)[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated(); + title = (*CGI->townh)[o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated(); sliderNeeded = false; break; case EMarketMode::ARTIFACT_RESOURCE: - title = (*CGI->townh)[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated(); + title = (*CGI->townh)[o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated(); // create image that copies part of background containing slot MISC_1 into position of slot MISC_5 // this is workaround for bug in H3 files where this slot for ragdoll on this screen is missing @@ -705,21 +705,24 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta } else { - switch(market->o->ID) + if(auto * o = dynamic_cast(market)) { - case Obj::BLACK_MARKET: - title = CGI->generaltexth->allTexts[349]; - sliderNeeded = false; - break; - case Obj::TRADING_POST: - title = CGI->generaltexth->allTexts[159]; - break; - case Obj::TRADING_POST_SNOW: - title = CGI->generaltexth->allTexts[159]; - break; - default: - title = market->o->getObjectName(); - break; + switch(o->ID) + { + case Obj::BLACK_MARKET: + title = CGI->generaltexth->allTexts[349]; + sliderNeeded = false; + break; + case Obj::TRADING_POST: + title = CGI->generaltexth->allTexts[159]; + break; + case Obj::TRADING_POST_SNOW: + title = CGI->generaltexth->allTexts[159]; + break; + default: + title = o->getObjectName(); + break; + } } } @@ -838,14 +841,15 @@ void CMarketplaceWindow::makeDeal() if(allowDeal) { + const auto * o = dynamic_cast(market); if(slider) { - LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, slider->getValue() * r1, hero); + LOCPLINT->cb->trade(o, mode, leftIdToSend, hRight->id, slider->getValue() * r1, hero); slider->moveTo(0); } else { - LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, r2, hero); + LOCPLINT->cb->trade(o, mode, leftIdToSend, hRight->id, r2, hero); } } @@ -1245,7 +1249,7 @@ void CAltarWindow::makeDeal() } } - LOCPLINT->cb->trade(market->o, mode, ids, {}, toSacrifice, hero); + LOCPLINT->cb->trade(dynamic_cast(market), mode, ids, {}, toSacrifice, hero); for(int& val : sacrificedUnits) val = 0; @@ -1266,8 +1270,8 @@ void CAltarWindow::makeDeal() } std::sort(positions.begin(), positions.end(), std::greater<>()); - LOCPLINT->cb->trade(market->o, mode, positions, {}, {}, hero); - artifactsOfHero->artifactsOnAltar.clear(); + LOCPLINT->cb->trade(dynamic_cast(market), mode, positions, {}, {}, hero); + arts->artifactsOnAltar.clear(); for(auto item : items[0]) { diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 7f65add98..16a212d68 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -1288,18 +1288,11 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket bars->setCustom("UNIVGREN", 2, 0); bars->preload(); - if(market->o->ID == Obj::TOWN) + if(auto town = dynamic_cast(_market)) { - auto town = dynamic_cast(_market); - - if(town) - { - auto faction = town->town->faction->getId(); - auto bid = town->town->getSpecialBuilding(BuildingSubID::MAGIC_UNIVERSITY)->bid; - titlePic = std::make_shared((*CGI->townh)[faction]->town->clientInfo.buildingsIcons, bid); - } - else - titlePic = std::make_shared((*CGI->townh)[ETownType::CONFLUX]->town->clientInfo.buildingsIcons, BuildingID::MAGIC_UNIVERSITY); + auto faction = town->town->faction->getId(); + auto bid = town->town->getSpecialBuilding(BuildingSubID::MAGIC_UNIVERSITY)->bid; + titlePic = std::make_shared((*CGI->townh)[faction]->town->clientInfo.buildingsIcons, bid); } else titlePic = std::make_shared("UNIVBLDG"); @@ -1321,7 +1314,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket void CUniversityWindow::makeDeal(int skill) { - LOCPLINT->cb->trade(market->o, EMarketMode::RESOURCE_SKILL, 6, skill, 1, hero); + LOCPLINT->cb->trade(dynamic_cast(market), EMarketMode::RESOURCE_SKILL, 6, skill, 1, hero); } diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index 1772bdc60..e712d22c9 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/lib/mapObjects/CGMarket.cpp @@ -177,10 +177,8 @@ const IMarket * IMarket::castFrom(const CGObjectInstance *obj, bool verbose) } } -IMarket::IMarket(const CGObjectInstance *O) - :o(O) +IMarket::IMarket() { - } std::vector IMarket::availableModes() const @@ -250,7 +248,6 @@ std::vector CGMarket::availableItemsIds(EMarketMode::EMarketMode mode) cons } CGMarket::CGMarket() - :IMarket(this) { } diff --git a/lib/mapObjects/CGMarket.h b/lib/mapObjects/CGMarket.h index 4697179d8..30447e8f1 100644 --- a/lib/mapObjects/CGMarket.h +++ b/lib/mapObjects/CGMarket.h @@ -16,12 +16,10 @@ VCMI_LIB_NAMESPACE_BEGIN class DLL_LINKAGE IMarket { public: - const CGObjectInstance *o; - - IMarket(const CGObjectInstance *O); + IMarket(); virtual ~IMarket() {} - virtual int getMarketEfficiency() const =0; + virtual int getMarketEfficiency() const = 0; virtual bool allowsTrade(EMarketMode::EMarketMode mode) const; virtual int availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const; //-1 if unlimited virtual std::vector availableItemsIds(EMarketMode::EMarketMode mode) const; @@ -30,11 +28,6 @@ public: std::vector availableModes() const; static const IMarket *castFrom(const CGObjectInstance *obj, bool verbose = true); - - template void serialize(Handler &h, const int version) - { - h & o; - } }; class DLL_LINKAGE CGMarket : public CGObjectInstance, public IMarket @@ -53,7 +46,6 @@ public: template void serialize(Handler &h, const int version) { h & static_cast(*this); - h & static_cast(*this); } }; diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 2721d8636..7107d80e5 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -220,7 +220,7 @@ bool CGTownInstance::hasCapitol() const CGTownInstance::CGTownInstance(): IShipyard(this), - IMarket(this), + IMarket(), town(nullptr), builded(0), destroyed(0), @@ -365,23 +365,6 @@ bool CGTownInstance::isBonusingBuildingAdded(BuildingID::EBuildingID bid) const return present != bonusingBuildings.end(); } -//it does not check hasBuilt because this check is in the OnHeroVisit handler -void CGTownInstance::tryAddOnePerWeekBonus(BuildingSubID::EBuildingSubID subID) -{ - auto bid = town->getBuildingType(subID); - - if(bid != BuildingID::NONE && !isBonusingBuildingAdded(bid)) - bonusingBuildings.push_back(new COPWBonus(bid, subID, this)); -} - -void CGTownInstance::tryAddVisitingBonus(BuildingSubID::EBuildingSubID subID) -{ - auto bid = town->getBuildingType(subID); - - if(bid != BuildingID::NONE && !isBonusingBuildingAdded(bid)) - bonusingBuildings.push_back(new CTownBonus(bid, subID, this)); -} - void CGTownInstance::addTownBonuses() { for(const auto & kvp : town->buildings) @@ -487,11 +470,6 @@ void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structu updateAppearance(); } -bool CGTownInstance::hasBuiltInOldWay(ETownType::ETownType type, const BuildingID & bid) const -{ - return (this->town->faction != nullptr && this->town->faction->getIndex() == type && hasBuilt(bid)); -} - void CGTownInstance::newTurn(CRandomGenerator & rand) const { if (cb->getDate(Date::DAY_OF_WEEK) == 1) //reset on new week diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index 1a8758730..dbc40e1af 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -74,7 +74,6 @@ public: { h & static_cast(*this); h & static_cast(*this); - h & static_cast(*this); h & name; h & builded; h & destroyed; @@ -212,11 +211,8 @@ private: void setOwner(const PlayerColor & owner) const; void onTownCaptured(const PlayerColor & winner) const; int getDwellingBonus(const std::vector& creatureIds, const std::vector >& dwellings) const; - bool hasBuiltInOldWay(ETownType::ETownType type, const BuildingID & bid) const; bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const; bool isBonusingBuildingAdded(BuildingID::EBuildingID bid) const; - void tryAddOnePerWeekBonus(BuildingSubID::EBuildingSubID subID); - void tryAddVisitingBonus(BuildingSubID::EBuildingSubID subID); void initOverriddenBids(); void addTownBonuses(); }; diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index 5cd0c5580..4f194d3ea 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -57,7 +57,7 @@ void registerTypesMapObjects1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); - s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); @@ -67,7 +67,7 @@ void registerTypesMapObjects1(Serializer &s) // Armed objects s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); - s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index f24e0e580..e9451c171 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4177,12 +4177,12 @@ bool CGameHandler::buyArtifact(const IMarket *m, const CGHeroInstance *h, GameRe giveResource(h->tempOwner, rid, -b1); SetAvailableArtifacts saa; - if (m->o->ID == Obj::TOWN) + if(dynamic_cast(m)) { saa.id = -1; saa.arts = CGTownInstance::merchantArtifacts; } - else if (const CGBlackMarket *bm = dynamic_cast(m->o)) //black market + else if(const CGBlackMarket *bm = dynamic_cast(m)) //black market { saa.id = bm->id.getNum(); saa.arts = bm->artifacts; @@ -4309,7 +4309,7 @@ bool CGameHandler::transformInUndead(const IMarket *market, const CGHeroInstance if (hero) army = hero; else - army = dynamic_cast(market->o); + army = dynamic_cast(market); if (!army) COMPLAIN_RET("Incorrect call to transform in undead!"); From 35a505288b347c5c827930b41467c8b532305216 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Fri, 28 Apr 2023 05:12:54 +0400 Subject: [PATCH 10/25] Fix json value reader --- lib/mapObjects/JsonRandom.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/mapObjects/JsonRandom.cpp b/lib/mapObjects/JsonRandom.cpp index 828a890cc..94eb37f60 100644 --- a/lib/mapObjects/JsonRandom.cpp +++ b/lib/mapObjects/JsonRandom.cpp @@ -32,22 +32,26 @@ namespace JsonRandom { si32 loadValue(const JsonNode & value, CRandomGenerator & rng, si32 defaultValue) { - if (value.isNull()) + if(value.isNull()) return defaultValue; - if (value.isNumber()) + if(value.isNumber()) return static_cast(value.Float()); - if (value.isVector()) + if(value.isVector()) { const auto & vector = value.Vector(); size_t index= rng.getIntRange(0, vector.size()-1)(); return loadValue(vector[index], rng, 0); } - if (!value["amount"].isNull()) - return static_cast(loadValue(value["amount"], rng, defaultValue)); - si32 min = static_cast(loadValue(value["min"], rng, 0)); - si32 max = static_cast(loadValue(value["max"], rng, 0)); - return rng.getIntRange(min, max)(); + if(value.isStruct()) + { + if (!value["amount"].isNull()) + return static_cast(loadValue(value["amount"], rng, defaultValue)); + si32 min = static_cast(loadValue(value["min"], rng, 0)); + si32 max = static_cast(loadValue(value["max"], rng, 0)); + return rng.getIntRange(min, max)(); + } + return defaultValue; } std::string loadKey(const JsonNode & value, CRandomGenerator & rng, const std::set & valuesSet) From 4b1b58b617e30556dfe7cdbc50628f7b71b8e180 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Fri, 28 Apr 2023 05:14:27 +0400 Subject: [PATCH 11/25] Change Callback to abstract --- AI/Nullkiller/AIGateway.cpp | 2 +- AI/VCAI/VCAI.cpp | 2 +- CCallback.cpp | 6 +++--- CCallback.h | 9 +++++---- client/windows/CTradeWindow.cpp | 9 ++++----- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 49edfba72..580e98cb3 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -1385,7 +1385,7 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade //TODO trade only as much as needed if (toGive) //don't try to sell 0 resources { - cb->trade(obj, EMarketMode::RESOURCE_RESOURCE, res, g.resID, toGive); + cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, g.resID, toGive); accquiredResources = static_cast(toGet * (it->resVal / toGive)); logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName()); } diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 059967627..9b4e00340 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -2130,7 +2130,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade //TODO trade only as much as needed if (toGive) //don't try to sell 0 resources { - cb->trade(obj, EMarketMode::RESOURCE_RESOURCE, res, g.resID, toGive); + cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, g.resID, toGive); accquiredResources = static_cast(toGet * (it->resVal / toGive)); logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName()); } diff --git a/CCallback.cpp b/CCallback.cpp index 8e50d7e99..c7bd94fcf 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -244,15 +244,15 @@ void CCallback::buyArtifact(const CGHeroInstance *hero, ArtifactID aid) sendRequest(&pack); } -void CCallback::trade(const CGObjectInstance * market, EMarketMode::EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero) +void CCallback::trade(const IMarket * market, EMarketMode::EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero) { trade(market, mode, std::vector(1, id1), std::vector(1, id2), std::vector(1, val1), hero); } -void CCallback::trade(const CGObjectInstance * market, EMarketMode::EMarketMode mode, const std::vector & id1, const std::vector & id2, const std::vector & val1, const CGHeroInstance * hero) +void CCallback::trade(const IMarket * market, EMarketMode::EMarketMode mode, const std::vector & id1, const std::vector & id2, const std::vector & val1, const CGHeroInstance * hero) { TradeOnMarketplace pack; - pack.marketId = market->id; + pack.marketId = dynamic_cast(market)->id; pack.heroId = hero ? hero->id : ObjectInstanceID(); pack.mode = mode; pack.r1 = id1; diff --git a/CCallback.h b/CCallback.h index a1793098d..2a70afb58 100644 --- a/CCallback.h +++ b/CCallback.h @@ -33,6 +33,7 @@ class IBattleEventsReceiver; class IGameEventsReceiver; struct ArtifactLocation; class BattleStateInfoForRetreat; +class IMarket; VCMI_LIB_NAMESPACE_END @@ -73,8 +74,8 @@ public: virtual bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE)=0; //if newID==-1 then best possible upgrade will be made virtual void swapGarrisonHero(const CGTownInstance *town)=0; - virtual void trade(const CGObjectInstance * market, EMarketMode::EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero = nullptr)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce - virtual void trade(const CGObjectInstance * market, EMarketMode::EMarketMode mode, const std::vector & id1, const std::vector & id2, const std::vector & val1, const CGHeroInstance * hero = nullptr)=0; + virtual void trade(const IMarket * market, EMarketMode::EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero = nullptr)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce + virtual void trade(const IMarket * market, EMarketMode::EMarketMode mode, const std::vector & id1, const std::vector & id2, const std::vector & val1, const CGHeroInstance * hero = nullptr)=0; virtual int selectionMade(int selection, QueryID queryID) =0; virtual int sendQueryReply(const JsonNode & reply, QueryID queryID) =0; @@ -168,8 +169,8 @@ public: void endTurn() override; void swapGarrisonHero(const CGTownInstance *town) override; void buyArtifact(const CGHeroInstance *hero, ArtifactID aid) override; - void trade(const CGObjectInstance * market, EMarketMode::EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero = nullptr) override; - void trade(const CGObjectInstance * market, EMarketMode::EMarketMode mode, const std::vector & id1, const std::vector & id2, const std::vector & val1, const CGHeroInstance * hero = nullptr) override; + void trade(const IMarket * market, EMarketMode::EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero = nullptr) override; + void trade(const IMarket * market, EMarketMode::EMarketMode mode, const std::vector & id1, const std::vector & id2, const std::vector & val1, const CGHeroInstance * hero = nullptr) override; void setFormation(const CGHeroInstance * hero, bool tight) override; void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero) override; void save(const std::string &fname) override; diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index 01e69e3f6..c45f35ad5 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -841,15 +841,14 @@ void CMarketplaceWindow::makeDeal() if(allowDeal) { - const auto * o = dynamic_cast(market); if(slider) { - LOCPLINT->cb->trade(o, mode, leftIdToSend, hRight->id, slider->getValue() * r1, hero); + LOCPLINT->cb->trade(market, mode, leftIdToSend, hRight->id, slider->getValue() * r1, hero); slider->moveTo(0); } else { - LOCPLINT->cb->trade(o, mode, leftIdToSend, hRight->id, r2, hero); + LOCPLINT->cb->trade(market, mode, leftIdToSend, hRight->id, r2, hero); } } @@ -1249,7 +1248,7 @@ void CAltarWindow::makeDeal() } } - LOCPLINT->cb->trade(dynamic_cast(market), mode, ids, {}, toSacrifice, hero); + LOCPLINT->cb->trade(market, mode, ids, {}, toSacrifice, hero); for(int& val : sacrificedUnits) val = 0; @@ -1270,7 +1269,7 @@ void CAltarWindow::makeDeal() } std::sort(positions.begin(), positions.end(), std::greater<>()); - LOCPLINT->cb->trade(dynamic_cast(market), mode, positions, {}, {}, hero); + LOCPLINT->cb->trade(market, mode, positions, {}, {}, hero); arts->artifactsOnAltar.clear(); for(auto item : items[0]) From 3d20538c1e1f93ff2d8f31e25e5990ab65d70502 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Fri, 28 Apr 2023 05:16:10 +0400 Subject: [PATCH 12/25] Implement markets --- lib/GameConstants.h | 33 +++++++--- lib/mapObjects/CGMarket.cpp | 82 ++++++++---------------- lib/mapObjects/CGMarket.h | 16 ++++- lib/mapObjects/CObjectClassesHandler.cpp | 4 +- lib/mapObjects/CommonConstructors.cpp | 60 +++++++++++++++++ lib/mapObjects/CommonConstructors.h | 23 +++++++ lib/registerTypes/RegisterTypes.h | 2 +- 7 files changed, 148 insertions(+), 72 deletions(-) diff --git a/lib/GameConstants.h b/lib/GameConstants.h index ca36e747a..5f22f9475 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -585,6 +585,16 @@ namespace BuildingSubID }; } +namespace EMarketMode +{ + enum EMarketMode + { + RESOURCE_RESOURCE, RESOURCE_PLAYER, CREATURE_RESOURCE, RESOURCE_ARTIFACT, + ARTIFACT_RESOURCE, ARTIFACT_EXP, CREATURE_EXP, CREATURE_UNDEAD, RESOURCE_SKILL, + MARTKET_AFTER_LAST_PLACEHOLDER + }; +} + namespace MappedKeys { @@ -634,6 +644,19 @@ namespace MappedKeys { "lighthouse", BuildingSubID::LIGHTHOUSE }, { "treasury", BuildingSubID::TREASURY } }; + + static const std::map MARKET_NAMES_TO_TYPES = + { + { "resource-resource", EMarketMode::RESOURCE_RESOURCE }, + { "resource-player", EMarketMode::RESOURCE_PLAYER }, + { "creature-resource", EMarketMode::CREATURE_RESOURCE }, + { "resource-artifact", EMarketMode::RESOURCE_ARTIFACT }, + { "artifact-resource", EMarketMode::ARTIFACT_RESOURCE }, + { "artifact-experience", EMarketMode::ARTIFACT_EXP }, + { "creature-experience", EMarketMode::CREATURE_EXP }, + { "creature-undead", EMarketMode::CREATURE_UNDEAD }, + { "resource-skill", EMarketMode::RESOURCE_SKILL }, + }; } namespace EAiTactic @@ -670,16 +693,6 @@ namespace ESpellCastProblem }; } -namespace EMarketMode -{ - enum EMarketMode - { - RESOURCE_RESOURCE, RESOURCE_PLAYER, CREATURE_RESOURCE, RESOURCE_ARTIFACT, - ARTIFACT_RESOURCE, ARTIFACT_EXP, CREATURE_EXP, CREATURE_UNDEAD, RESOURCE_SKILL, - MARTKET_AFTER_LAST_PLACEHOLDER - }; -} - namespace ECommander { enum SecondarySkills {ATTACK, DEFENSE, HEALTH, DAMAGE, SPEED, SPELL_POWER, CASTS, RESISTANCE}; diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index e712d22c9..71239f0c6 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/lib/mapObjects/CGMarket.cpp @@ -19,6 +19,7 @@ #include "CGTownInstance.h" #include "../GameSettings.h" #include "../CSkillHandler.h" +#include "CObjectClassesHandler.h" VCMI_LIB_NAMESPACE_BEGIN @@ -158,23 +159,10 @@ std::vector IMarket::availableItemsIds(EMarketMode::EMarketMode mode) const const IMarket * IMarket::castFrom(const CGObjectInstance *obj, bool verbose) { - switch(obj->ID) - { - case Obj::TOWN: - return dynamic_cast(obj); - case Obj::ALTAR_OF_SACRIFICE: - case Obj::BLACK_MARKET: - case Obj::TRADING_POST: - case Obj::TRADING_POST_SNOW: - case Obj::FREELANCERS_GUILD: - return dynamic_cast(obj); - case Obj::UNIVERSITY: - return dynamic_cast(obj); - default: - if(verbose) - logGlobal->error("Cannot cast to IMarket object with ID %d", obj->ID); - return nullptr; - } + auto * imarket = dynamic_cast(obj); + if(verbose && !imarket) + logGlobal->error("Cannot cast to IMarket object type %s", obj->typeName); + return imarket; } IMarket::IMarket() @@ -191,43 +179,24 @@ std::vector IMarket::availableModes() const return ret; } +void CGMarket::initObj(CRandomGenerator & rand) +{ + VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); +} + void CGMarket::onHeroVisit(const CGHeroInstance * h) const { - openWindow(EOpenWindowMode::MARKET_WINDOW,id.getNum(),h->id.getNum()); + openWindow(EOpenWindowMode::MARKET_WINDOW, id.getNum(), h->id.getNum()); } int CGMarket::getMarketEfficiency() const { - return 5; + return marketEfficacy; } bool CGMarket::allowsTrade(EMarketMode::EMarketMode mode) const { - switch(mode) - { - case EMarketMode::RESOURCE_RESOURCE: - case EMarketMode::RESOURCE_PLAYER: - switch(ID) - { - case Obj::TRADING_POST: - case Obj::TRADING_POST_SNOW: - return true; - default: - return false; - } - case EMarketMode::CREATURE_RESOURCE: - return ID == Obj::FREELANCERS_GUILD; - //case ARTIFACT_RESOURCE: - case EMarketMode::RESOURCE_ARTIFACT: - return ID == Obj::BLACK_MARKET; - case EMarketMode::ARTIFACT_EXP: - case EMarketMode::CREATURE_EXP: - return ID == Obj::ALTAR_OF_SACRIFICE; //TODO? check here for alignment of visiting hero? - would not be coherent with other checks here - case EMarketMode::RESOURCE_SKILL: - return ID == Obj::UNIVERSITY; - default: - return false; - } + return marketModes.count(mode); } int CGMarket::availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const @@ -237,14 +206,9 @@ int CGMarket::availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial std::vector CGMarket::availableItemsIds(EMarketMode::EMarketMode mode) const { - switch(mode) - { - case EMarketMode::RESOURCE_RESOURCE: - case EMarketMode::RESOURCE_PLAYER: + if(allowsTrade(mode)) return IMarket::availableItemsIds(mode); - default: - return std::vector(); - } + return std::vector(); } CGMarket::CGMarket() @@ -290,22 +254,26 @@ void CGBlackMarket::newTurn(CRandomGenerator & rand) const void CGUniversity::initObj(CRandomGenerator & rand) { + CGMarket::initObj(rand); + std::vector toChoose; + int skillsNeeded = skillsTotal - skills.size(); for(int i = 0; i < VLC->skillh->size(); ++i) { - if(cb->isAllowed(2, i)) + if(!vstd::contains(skills, i) && cb->isAllowed(2, i)) { toChoose.push_back(i); } } - if(toChoose.size() < 4) + if(toChoose.size() < skillsNeeded) { - logGlobal->warn("Warning: less then 4 available skills was found by University initializer!"); + logGlobal->warn("Warning: less then %d available skills was found by University initializer!", skillsTotal); return; } - // get 4 skills - for(int i = 0; i < 4; ++i) + // get 4 skills, excluding predefined + + for(int i = 0; i < skillsNeeded; ++i) { // move randomly one skill to selected and remove from list auto it = RandomGeneratorUtil::nextItem(toChoose, rand); @@ -322,7 +290,7 @@ std::vector CGUniversity::availableItemsIds(EMarketMode::EMarketMode mode) return skills; default: - return std::vector (); + return std::vector(); } } diff --git a/lib/mapObjects/CGMarket.h b/lib/mapObjects/CGMarket.h index 30447e8f1..c7629c609 100644 --- a/lib/mapObjects/CGMarket.h +++ b/lib/mapObjects/CGMarket.h @@ -33,9 +33,14 @@ public: class DLL_LINKAGE CGMarket : public CGObjectInstance, public IMarket { public: + + std::set marketModes; + int marketEfficacy = 5; + CGMarket(); - ///IObjectIntercae + ///IObjectInterface void onHeroVisit(const CGHeroInstance * h) const override; //open trading window + void initObj(CRandomGenerator & rand) override;//set skills for trade ///IMarket int getMarketEfficiency() const override; @@ -46,6 +51,8 @@ public: template void serialize(Handler &h, const int version) { h & static_cast(*this); + h & marketModes; + h & marketEfficacy; } }; @@ -67,7 +74,12 @@ public: class DLL_LINKAGE CGUniversity : public CGMarket { public: + int skillsTotal = 4; std::vector skills; //available skills + + //window variables + std::string title; + std::string speech; std::vector availableItemsIds(EMarketMode::EMarketMode mode) const override; void initObj(CRandomGenerator & rand) override;//set skills for trade @@ -77,6 +89,8 @@ public: { h & static_cast(*this); h & skills; + h & title; + h & speech; } }; diff --git a/lib/mapObjects/CObjectClassesHandler.cpp b/lib/mapObjects/CObjectClassesHandler.cpp index f283cac2b..2d4f662ed 100644 --- a/lib/mapObjects/CObjectClassesHandler.cpp +++ b/lib/mapObjects/CObjectClassesHandler.cpp @@ -40,6 +40,7 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER_CLASS("town", CTownInstanceConstructor); SET_HANDLER_CLASS("bank", CBankInstanceConstructor); SET_HANDLER_CLASS("boat", BoatInstanceConstructor); + SET_HANDLER_CLASS("market", MarketInstanceConstructor); SET_HANDLER_CLASS("static", CObstacleConstructor); SET_HANDLER_CLASS("", CObstacleConstructor); @@ -54,7 +55,6 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER("generic", CGObjectInstance); SET_HANDLER("cartographer", CCartographer); SET_HANDLER("artifact", CGArtifact); - SET_HANDLER("blackMarket", CGBlackMarket); SET_HANDLER("borderGate", CGBorderGate); SET_HANDLER("borderGuard", CGBorderGuard); SET_HANDLER("monster", CGCreature); @@ -65,7 +65,6 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER("keymaster", CGKeymasterTent); SET_HANDLER("lighthouse", CGLighthouse); SET_HANDLER("magi", CGMagi); - SET_HANDLER("market", CGMarket); SET_HANDLER("mine", CGMine); SET_HANDLER("obelisk", CGObelisk); SET_HANDLER("observatory", CGObservatory); @@ -82,7 +81,6 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER("monolith", CGMonolith); SET_HANDLER("subterraneanGate", CGSubterraneanGate); SET_HANDLER("whirlpool", CGWhirlpool); - SET_HANDLER("university", CGUniversity); SET_HANDLER("witch", CGWitchHut); SET_HANDLER("terrain", CGTerrainPatch); diff --git a/lib/mapObjects/CommonConstructors.cpp b/lib/mapObjects/CommonConstructors.cpp index 0ea09dd89..62e89b502 100644 --- a/lib/mapObjects/CommonConstructors.cpp +++ b/lib/mapObjects/CommonConstructors.cpp @@ -291,6 +291,66 @@ void BoatInstanceConstructor::configureObject(CGObjectInstance * object, CRandom } +void MarketInstanceConstructor::initTypeData(const JsonNode & input) +{ + for(auto & element : input["modes"].Vector()) + { + if(MappedKeys::MARKET_NAMES_TO_TYPES.count(element.String())) + marketModes.insert(MappedKeys::MARKET_NAMES_TO_TYPES.at(element.String())); + } + + marketEfficacy = input["efficacy"].isNull() ? -1 : input["efficacy"].Integer(); + predefinedOffer = input["offer"]; + + title = input["title"].String(); + speech = input["speech"].String(); +} + +CGObjectInstance * MarketInstanceConstructor::create(std::shared_ptr tmpl) const +{ + CGMarket * market = nullptr; + if(marketModes.size() == 1) + { + switch(*marketModes.begin()) + { + case EMarketMode::ARTIFACT_RESOURCE: + case EMarketMode::RESOURCE_ARTIFACT: + market = new CGBlackMarket; + break; + + case EMarketMode::RESOURCE_SKILL: + market = new CGUniversity; + break; + } + } + + if(!market) + market = new CGMarket; + + preInitObject(market); + + if(tmpl) + market->appearance = tmpl; + market->marketModes = marketModes; + if(marketEfficacy >= 0) + market->marketEfficacy = marketEfficacy; + + return market; +} + +void MarketInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const +{ + if(auto * university = dynamic_cast(object)) + { + for(auto skill : JsonRandom::loadSecondary(predefinedOffer, rng)) + university->skills.push_back(skill.first.getNum()); + + university->title = VLC->generaltexth->translate(title); + university->speech = VLC->generaltexth->translate(speech); + } +} + + bool CBankInstanceConstructor::hasNameTextID() const { return true; diff --git a/lib/mapObjects/CommonConstructors.h b/lib/mapObjects/CommonConstructors.h index 1513d293f..ff46ce7e6 100644 --- a/lib/mapObjects/CommonConstructors.h +++ b/lib/mapObjects/CommonConstructors.h @@ -255,4 +255,27 @@ public: } }; +class MarketInstanceConstructor : public CDefaultObjectTypeHandler +{ +protected: + void initTypeData(const JsonNode & config) override; + + std::set marketModes; + JsonNode predefinedOffer; + int marketEfficacy; + + std::string title, speech; + +public: + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; + void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + + template void serialize(Handler &h, const int version) + { + h & static_cast&>(*this); + h & marketModes; + h & marketEfficacy; + } +}; + VCMI_LIB_NAMESPACE_END diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index 4f194d3ea..24fb28ddb 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -89,12 +89,12 @@ void registerTypesMapObjectTypes(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); #define REGISTER_GENERIC_HANDLER(TYPENAME) s.template registerType >() REGISTER_GENERIC_HANDLER(CGObjectInstance); - REGISTER_GENERIC_HANDLER(CGMarket); REGISTER_GENERIC_HANDLER(CCartographer); REGISTER_GENERIC_HANDLER(CGArtifact); REGISTER_GENERIC_HANDLER(CGBlackMarket); From 07f7c318dcc4e1368249739fdc2f22b841b81eb4 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Fri, 28 Apr 2023 05:16:37 +0400 Subject: [PATCH 13/25] Adjust client part for markets --- client/CPlayerInterface.cpp | 5 +++- client/windows/CCastleInterface.cpp | 2 +- client/windows/GUIClasses.cpp | 36 ++++++++++++++++++++--------- client/windows/GUIClasses.h | 4 ++-- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 2c5647a6f..b4170f99a 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1720,7 +1720,10 @@ void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInsta } else { - GH.pushIntT(market, visitor, market->availableModes().front()); + if(market->allowsTrade(EMarketMode::CREATURE_UNDEAD)) + GH.pushIntT(market, visitor); + else + GH.pushIntT(market, visitor, market->availableModes().front()); } } diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 1dadf844a..2647dae90 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -774,7 +774,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil break; case BuildingSubID::CREATURE_TRANSFORMER: //Skeleton Transformer - GH.pushIntT(getHero(), town); + GH.pushIntT(town, getHero()); break; case BuildingSubID::PORTAL_OF_SUMMONING: diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 16a212d68..f30c03feb 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -1162,7 +1162,7 @@ void CTransformerWindow::makeDeal() for(auto & elem : items) { if(!elem->left) - LOCPLINT->cb->trade(town, EMarketMode::CREATURE_UNDEAD, elem->id, 0, 0, hero); + LOCPLINT->cb->trade(market, EMarketMode::CREATURE_UNDEAD, elem->id, 0, 0, hero); } } @@ -1182,21 +1182,24 @@ void CTransformerWindow::updateGarrisons() item->update(); } -CTransformerWindow::CTransformerWindow(const CGHeroInstance * _hero, const CGTownInstance * _town) +CTransformerWindow::CTransformerWindow(const IMarket * _market, const CGHeroInstance * _hero) : CStatusbarWindow(PLAYER_COLORED, "SKTRNBK"), hero(_hero), - town(_town) + market(_market) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); if(hero) army = hero; else - army = town; - - for(int i=0; i(market); //for town only + + if(army) { - if(army->getCreature(SlotID(i))) - items.push_back(std::make_shared(this, army->getStackCount(SlotID(i)), i)); + for(int i = 0; i < GameConstants::ARMY_SIZE; i++) + { + if(army->getCreature(SlotID(i))) + items.push_back(std::make_shared(this, army->getStackCount(SlotID(i)), i)); + } } all = std::make_shared(Point(146, 416), "ALTARMY.DEF", CGI->generaltexth->zelp[590], [&](){ addAll(); }, SDLK_a); @@ -1287,6 +1290,9 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket bars->setCustom("UNIVGOLD", 1, 0); bars->setCustom("UNIVGREN", 2, 0); bars->preload(); + + std::string titleStr = CGI->generaltexth->allTexts[602]; + std::string speechStr = CGI->generaltexth->allTexts[603]; if(auto town = dynamic_cast(_market)) { @@ -1294,13 +1300,21 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket auto bid = town->town->getSpecialBuilding(BuildingSubID::MAGIC_UNIVERSITY)->bid; titlePic = std::make_shared((*CGI->townh)[faction]->town->clientInfo.buildingsIcons, bid); } + else if(auto uni = dynamic_cast(_market); uni->appearance) + { + titlePic = std::make_shared(uni->appearance->animationFile, 0); + titleStr = uni->title; + speechStr = uni->speech; + } else + { titlePic = std::make_shared("UNIVBLDG"); + } titlePic->center(Point(232 + pos.x, 76 + pos.y)); - clerkSpeech = std::make_shared(CGI->generaltexth->allTexts[603], Rect(24, 129, 413, 70), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); - title = std::make_shared(231, 26, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[602]); + clerkSpeech = std::make_shared(speechStr, Rect(24, 129, 413, 70), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); + title = std::make_shared(231, 26, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, titleStr); std::vector goods = market->availableItemsIds(EMarketMode::RESOURCE_SKILL); assert(goods.size() == 4); @@ -1314,7 +1328,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket void CUniversityWindow::makeDeal(int skill) { - LOCPLINT->cb->trade(dynamic_cast(market), EMarketMode::RESOURCE_SKILL, 6, skill, 1, hero); + LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_SKILL, 6, skill, 1, hero); } diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index 94f64aa29..c47debc02 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -382,7 +382,7 @@ class CTransformerWindow : public CStatusbarWindow, public CGarrisonHolder const CArmedInstance * army;//object with army for transforming (hero or town) const CGHeroInstance * hero;//only if we have hero in town - const CGTownInstance * town;//market, town garrison is used if hero == nullptr + const IMarket * market;//market, town garrison is used if hero == nullptr std::shared_ptr titleLeft; std::shared_ptr titleRight; @@ -399,7 +399,7 @@ public: void makeDeal(); void addAll(); void updateGarrisons() override; - CTransformerWindow(const CGHeroInstance * _hero, const CGTownInstance * _town); + CTransformerWindow(const IMarket * _market, const CGHeroInstance * _hero); }; class CUniversityWindow : public CStatusbarWindow From 193cf6676acf4bcbf17cbcdf0bc238842656bcb4 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Fri, 28 Apr 2023 05:16:52 +0400 Subject: [PATCH 14/25] Adjust config for markets --- config/objects/generic.json | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/config/objects/generic.json b/config/objects/generic.json index 9838ef0ba..9601bc2f4 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -30,7 +30,8 @@ "zoneLimit" : 1, "value" : 100, "rarity" : 20 - } + }, + "modes" : ["creature-experience", "artifact-experience"] } } }, @@ -51,7 +52,9 @@ "zoneLimit" : 1, "value" : 100, "rarity" : 100 - } + }, + "modes" : ["resource-resource", "resource-player"], + "efficacy" : 5 } } }, @@ -72,7 +75,9 @@ "zoneLimit" : 1, "value" : 100, "rarity" : 100 - } + }, + "modes" : ["resource-resource", "resource-player"], + "efficacy" : 5 } } }, @@ -87,14 +92,15 @@ "zoneLimit" : 1, "value" : 100, "rarity" : 100 - } + }, + "modes" : ["creature-resource"] } } }, "blackMarket" : { "index" :7, - "handler" : "blackMarket", + "handler" : "market", "base" : { "sounds" : { "ambient" : ["LOOPMARK"], @@ -108,7 +114,8 @@ "rmg" : { "value" : 8000, "rarity" : 20 - } + }, + "modes" : ["resource-artifact"] } } }, @@ -536,7 +543,7 @@ }, "university" : { "index" :104, - "handler" : "university", + "handler" : "market", "base" : { "sounds" : { "visit" : ["GAZEBO"] @@ -549,7 +556,10 @@ "rmg" : { "value" : 2500, "rarity" : 20 - } + }, + "modes" : ["resource-skill"], + "title" : "core.genrltxt.602", + "speech" : "core.genrltxt.603" } } }, From b29fc1a5f4fddd5b848a736b6bfeaca14bc87866 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 29 Apr 2023 15:03:19 +0400 Subject: [PATCH 15/25] Fix movement bonus subtype --- lib/HeroBonus.cpp | 2 +- lib/mapObjects/MiscObjects.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 266d91b9d..971b15675 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -1755,7 +1755,7 @@ BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSu } else if(deprecatedSubtype == SecondarySkill::LOGISTICS || deprecatedSubtypeStr == "skill.logistics") { - subtype = 0; + subtype = 1; subtypeRelevant = true; valueType = Bonus::PERCENT_TO_BASE; valueTypeRelevant = true; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 0337e28e4..13c656d88 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -2145,7 +2145,7 @@ void CGLighthouse::giveBonusTo(const PlayerColor & player, bool onInit) const gb.bonus.duration = Bonus::PERMANENT; gb.bonus.source = Bonus::OBJECT; gb.bonus.sid = id.getNum(); - gb.bonus.subtype = 1; + gb.bonus.subtype = 0; // FIXME: This is really dirty hack // Proper fix would be to make CGLighthouse into bonus system node From 11840b9f6d24f6a08d2867be03c958b08697815e Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 29 Apr 2023 15:53:44 +0400 Subject: [PATCH 16/25] Get rid of hardcoded logic for markets --- client/CPlayerInterface.cpp | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index b4170f99a..cc83c4b52 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1709,22 +1709,15 @@ void CPlayerInterface::stopMovement() void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInstance *visitor) { EVENT_HANDLER_CALLED_BY_CLIENT; - auto * o = dynamic_cast(market); - if(o && o->ID == Obj::ALTAR_OF_SACRIFICE) - { - //EEMarketMode mode = market->availableModes().front(); - if (market->allowsTrade(EMarketMode::ARTIFACT_EXP) && visitor->getAlignment() != EAlignment::EVIL) - GH.pushIntT(market, visitor, EMarketMode::ARTIFACT_EXP); - else if (market->allowsTrade(EMarketMode::CREATURE_EXP) && visitor->getAlignment() != EAlignment::GOOD) - GH.pushIntT(market, visitor, EMarketMode::CREATURE_EXP); - } - else - { - if(market->allowsTrade(EMarketMode::CREATURE_UNDEAD)) - GH.pushIntT(market, visitor); - else - GH.pushIntT(market, visitor, market->availableModes().front()); - } + + if(market->allowsTrade(EMarketMode::ARTIFACT_EXP) && visitor->getAlignment() != EAlignment::EVIL) + GH.pushIntT(market, visitor, EMarketMode::ARTIFACT_EXP); + else if(market->allowsTrade(EMarketMode::CREATURE_EXP) && visitor->getAlignment() != EAlignment::GOOD) + GH.pushIntT(market, visitor, EMarketMode::CREATURE_EXP); + else if(market->allowsTrade(EMarketMode::CREATURE_UNDEAD)) + GH.pushIntT(market, visitor); + else if(!market->availableModes().empty()) + GH.pushIntT(market, visitor, market->availableModes().front()); } void CPlayerInterface::showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor) From a4fd6c1c92172570f0b3c3a3eede6c6fe6d5707e Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 29 Apr 2023 16:10:30 +0400 Subject: [PATCH 17/25] Move trading window title to config --- client/windows/CTradeWindow.cpp | 26 +++----------------------- config/objects/generic.json | 9 ++++++--- lib/mapObjects/CGMarket.h | 12 ++++++------ lib/mapObjects/CommonConstructors.cpp | 6 +++--- 4 files changed, 18 insertions(+), 35 deletions(-) diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index c45f35ad5..fc99dcbaf 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -673,7 +673,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); madeTransaction = false; - bool sliderNeeded = true; + bool sliderNeeded = (mode != EMarketMode::RESOURCE_ARTIFACT && mode != EMarketMode::ARTIFACT_RESOURCE); statusBar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); @@ -688,7 +688,6 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta break; case EMarketMode::RESOURCE_ARTIFACT: title = (*CGI->townh)[o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated(); - sliderNeeded = false; break; case EMarketMode::ARTIFACT_RESOURCE: title = (*CGI->townh)[o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated(); @@ -696,34 +695,15 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta // create image that copies part of background containing slot MISC_1 into position of slot MISC_5 // this is workaround for bug in H3 files where this slot for ragdoll on this screen is missing images.push_back(std::make_shared(background->getSurface(), Rect(20, 187, 47, 47), 18, 339 )); - sliderNeeded = false; break; default: title = CGI->generaltexth->allTexts[158]; break; } } - else + else if(auto * o = dynamic_cast(market)) { - if(auto * o = dynamic_cast(market)) - { - switch(o->ID) - { - case Obj::BLACK_MARKET: - title = CGI->generaltexth->allTexts[349]; - sliderNeeded = false; - break; - case Obj::TRADING_POST: - title = CGI->generaltexth->allTexts[159]; - break; - case Obj::TRADING_POST_SNOW: - title = CGI->generaltexth->allTexts[159]; - break; - default: - title = o->getObjectName(); - break; - } - } + title = o->title.empty() ? o->getObjectName() : o->title; } titleLabel = std::make_shared(300, 27, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, title); diff --git a/config/objects/generic.json b/config/objects/generic.json index 9601bc2f4..4121aac8b 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -54,7 +54,8 @@ "rarity" : 100 }, "modes" : ["resource-resource", "resource-player"], - "efficacy" : 5 + "efficacy" : 5, + "title" : "core.genrltxt.159" } } }, @@ -77,7 +78,8 @@ "rarity" : 100 }, "modes" : ["resource-resource", "resource-player"], - "efficacy" : 5 + "efficacy" : 5, + "title" : "core.genrltxt.159" } } }, @@ -115,7 +117,8 @@ "value" : 8000, "rarity" : 20 }, - "modes" : ["resource-artifact"] + "modes" : ["resource-artifact"], + "title" : "core.genrltxt.349" } } }, diff --git a/lib/mapObjects/CGMarket.h b/lib/mapObjects/CGMarket.h index c7629c609..785cc34cd 100644 --- a/lib/mapObjects/CGMarket.h +++ b/lib/mapObjects/CGMarket.h @@ -37,6 +37,10 @@ public: std::set marketModes; int marketEfficacy = 5; + //window variables + std::string title; + std::string speech; //currently shown only in university + CGMarket(); ///IObjectInterface void onHeroVisit(const CGHeroInstance * h) const override; //open trading window @@ -53,6 +57,8 @@ public: h & static_cast(*this); h & marketModes; h & marketEfficacy; + h & title; + h & speech; } }; @@ -76,10 +82,6 @@ class DLL_LINKAGE CGUniversity : public CGMarket public: int skillsTotal = 4; std::vector skills; //available skills - - //window variables - std::string title; - std::string speech; std::vector availableItemsIds(EMarketMode::EMarketMode mode) const override; void initObj(CRandomGenerator & rand) override;//set skills for trade @@ -89,8 +91,6 @@ public: { h & static_cast(*this); h & skills; - h & title; - h & speech; } }; diff --git a/lib/mapObjects/CommonConstructors.cpp b/lib/mapObjects/CommonConstructors.cpp index 62e89b502..0a7f960c3 100644 --- a/lib/mapObjects/CommonConstructors.cpp +++ b/lib/mapObjects/CommonConstructors.cpp @@ -335,6 +335,9 @@ CGObjectInstance * MarketInstanceConstructor::create(std::shared_ptr= 0) market->marketEfficacy = marketEfficacy; + market->title = VLC->generaltexth->translate(title); + market->speech = VLC->generaltexth->translate(speech); + return market; } @@ -344,9 +347,6 @@ void MarketInstanceConstructor::configureObject(CGObjectInstance * object, CRand { for(auto skill : JsonRandom::loadSecondary(predefinedOffer, rng)) university->skills.push_back(skill.first.getNum()); - - university->title = VLC->generaltexth->translate(title); - university->speech = VLC->generaltexth->translate(speech); } } From 68fa7aaf35bbf7880f4d2d2cb1df00c749055794 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 29 Apr 2023 16:24:53 +0400 Subject: [PATCH 18/25] Remove limitations for amount of skills --- client/windows/GUIClasses.cpp | 1 - config/objects/generic.json | 9 ++++++++- lib/mapObjects/CGMarket.cpp | 16 ---------------- lib/mapObjects/CGMarket.h | 1 - 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index f30c03feb..e76d7f2a3 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -1317,7 +1317,6 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket title = std::make_shared(231, 26, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, titleStr); std::vector goods = market->availableItemsIds(EMarketMode::RESOURCE_SKILL); - assert(goods.size() == 4); for(int i=0; i(this, goods[i], 54+i*104, 234)); diff --git a/config/objects/generic.json b/config/objects/generic.json index 4121aac8b..e6cc57d19 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -562,7 +562,14 @@ }, "modes" : ["resource-skill"], "title" : "core.genrltxt.602", - "speech" : "core.genrltxt.603" + "speech" : "core.genrltxt.603", + "offer": + [ + { "noneOf" : ["necromancy"] }, + { "noneOf" : ["necromancy"] }, + { "noneOf" : ["necromancy"] }, + { "noneOf" : ["necromancy"] } + ] } } }, diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index 71239f0c6..455904b90 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/lib/mapObjects/CGMarket.cpp @@ -257,7 +257,6 @@ void CGUniversity::initObj(CRandomGenerator & rand) CGMarket::initObj(rand); std::vector toChoose; - int skillsNeeded = skillsTotal - skills.size(); for(int i = 0; i < VLC->skillh->size(); ++i) { if(!vstd::contains(skills, i) && cb->isAllowed(2, i)) @@ -265,21 +264,6 @@ void CGUniversity::initObj(CRandomGenerator & rand) toChoose.push_back(i); } } - if(toChoose.size() < skillsNeeded) - { - logGlobal->warn("Warning: less then %d available skills was found by University initializer!", skillsTotal); - return; - } - - // get 4 skills, excluding predefined - - for(int i = 0; i < skillsNeeded; ++i) - { - // move randomly one skill to selected and remove from list - auto it = RandomGeneratorUtil::nextItem(toChoose, rand); - skills.push_back(*it); - toChoose.erase(it); - } } std::vector CGUniversity::availableItemsIds(EMarketMode::EMarketMode mode) const diff --git a/lib/mapObjects/CGMarket.h b/lib/mapObjects/CGMarket.h index 785cc34cd..82a86d682 100644 --- a/lib/mapObjects/CGMarket.h +++ b/lib/mapObjects/CGMarket.h @@ -80,7 +80,6 @@ public: class DLL_LINKAGE CGUniversity : public CGMarket { public: - int skillsTotal = 4; std::vector skills; //available skills std::vector availableItemsIds(EMarketMode::EMarketMode mode) const override; From 07b2052679fb2fa7556e7652c3e0e7b090b8b929 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 29 Apr 2023 19:08:42 +0400 Subject: [PATCH 19/25] Bit refactoring --- client/windows/CTradeWindow.cpp | 2 +- lib/mapObjects/CommonConstructors.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index fc99dcbaf..5e1cf6485 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -703,7 +703,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta } else if(auto * o = dynamic_cast(market)) { - title = o->title.empty() ? o->getObjectName() : o->title; + title = o->title; } titleLabel = std::make_shared(300, 27, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, title); diff --git a/lib/mapObjects/CommonConstructors.cpp b/lib/mapObjects/CommonConstructors.cpp index 0a7f960c3..616d37034 100644 --- a/lib/mapObjects/CommonConstructors.cpp +++ b/lib/mapObjects/CommonConstructors.cpp @@ -335,7 +335,10 @@ CGObjectInstance * MarketInstanceConstructor::create(std::shared_ptr= 0) market->marketEfficacy = marketEfficacy; - market->title = VLC->generaltexth->translate(title); + market->title = market->getObjectName(); + if(!title.empty()) + market->title = VLC->generaltexth->translate(title); + market->speech = VLC->generaltexth->translate(speech); return market; From 87ff31531f688e5367c019b8021ddd50c6421d80 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 1 May 2023 04:07:31 +0400 Subject: [PATCH 20/25] Rename parameter and remove extra check --- lib/mapObjects/CGMarket.cpp | 2 +- lib/mapObjects/CGMarket.h | 4 ++-- lib/mapObjects/CommonConstructors.cpp | 5 ++--- lib/mapObjects/CommonConstructors.h | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index 455904b90..f3f7a7789 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/lib/mapObjects/CGMarket.cpp @@ -191,7 +191,7 @@ void CGMarket::onHeroVisit(const CGHeroInstance * h) const int CGMarket::getMarketEfficiency() const { - return marketEfficacy; + return marketEfficiency; } bool CGMarket::allowsTrade(EMarketMode::EMarketMode mode) const diff --git a/lib/mapObjects/CGMarket.h b/lib/mapObjects/CGMarket.h index 82a86d682..721d4bd40 100644 --- a/lib/mapObjects/CGMarket.h +++ b/lib/mapObjects/CGMarket.h @@ -35,7 +35,7 @@ class DLL_LINKAGE CGMarket : public CGObjectInstance, public IMarket public: std::set marketModes; - int marketEfficacy = 5; + int marketEfficiency; //window variables std::string title; @@ -56,7 +56,7 @@ public: { h & static_cast(*this); h & marketModes; - h & marketEfficacy; + h & marketEfficiency; h & title; h & speech; } diff --git a/lib/mapObjects/CommonConstructors.cpp b/lib/mapObjects/CommonConstructors.cpp index 616d37034..b0be02789 100644 --- a/lib/mapObjects/CommonConstructors.cpp +++ b/lib/mapObjects/CommonConstructors.cpp @@ -299,7 +299,7 @@ void MarketInstanceConstructor::initTypeData(const JsonNode & input) marketModes.insert(MappedKeys::MARKET_NAMES_TO_TYPES.at(element.String())); } - marketEfficacy = input["efficacy"].isNull() ? -1 : input["efficacy"].Integer(); + marketEfficiency = input["efficiency"].isNull() ? 5 : input["efficiency"].Integer(); predefinedOffer = input["offer"]; title = input["title"].String(); @@ -332,8 +332,7 @@ CGObjectInstance * MarketInstanceConstructor::create(std::shared_ptrappearance = tmpl; market->marketModes = marketModes; - if(marketEfficacy >= 0) - market->marketEfficacy = marketEfficacy; + market->marketEfficiency = marketEfficiency; market->title = market->getObjectName(); if(!title.empty()) diff --git a/lib/mapObjects/CommonConstructors.h b/lib/mapObjects/CommonConstructors.h index ff46ce7e6..b0f764386 100644 --- a/lib/mapObjects/CommonConstructors.h +++ b/lib/mapObjects/CommonConstructors.h @@ -262,7 +262,7 @@ protected: std::set marketModes; JsonNode predefinedOffer; - int marketEfficacy; + int marketEfficiency; std::string title, speech; @@ -274,7 +274,7 @@ public: { h & static_cast&>(*this); h & marketModes; - h & marketEfficacy; + h & marketEfficiency; } }; From 32277a007d359624e7e4728704aaf24a21eaaa18 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 1 May 2023 14:00:07 +0400 Subject: [PATCH 21/25] Fix compilation after merge --- client/windows/CTradeWindow.cpp | 2 +- config/objects/generic.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index 5e1cf6485..41fe856bd 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -1250,7 +1250,7 @@ void CAltarWindow::makeDeal() std::sort(positions.begin(), positions.end(), std::greater<>()); LOCPLINT->cb->trade(market, mode, positions, {}, {}, hero); - arts->artifactsOnAltar.clear(); + artifactsOfHero->artifactsOnAltar.clear(); for(auto item : items[0]) { diff --git a/config/objects/generic.json b/config/objects/generic.json index e6cc57d19..c20e0b137 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -54,7 +54,7 @@ "rarity" : 100 }, "modes" : ["resource-resource", "resource-player"], - "efficacy" : 5, + "efficiency" : 5, "title" : "core.genrltxt.159" } } @@ -78,7 +78,7 @@ "rarity" : 100 }, "modes" : ["resource-resource", "resource-player"], - "efficacy" : 5, + "efficiency" : 5, "title" : "core.genrltxt.159" } } From eb577a03b51439f6cd7ce35e88a7f83eaff548c7 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 1 May 2023 15:29:56 +0400 Subject: [PATCH 22/25] Deprecating enum constants --- AI/Nullkiller/Engine/PriorityEvaluator.cpp | 8 ++++++-- AI/VCAI/Goals/CollectRes.cpp | 13 ++++++++----- config/objects/generic.json | 12 ++++++------ lib/GameConstants.h | 12 ++++++------ 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index fcc7d291d..352281429 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -297,6 +297,12 @@ int RewardEvaluator::getGoldCost(const CGObjectInstance * target, const CGHeroIn { if(!target) return 0; + + if(auto * m = dynamic_cast(target)) + { + if(m->allowsTrade(EMarketMode::RESOURCE_SKILL)) + return 2000; + } switch(target->ID) { @@ -305,8 +311,6 @@ int RewardEvaluator::getGoldCost(const CGObjectInstance * target, const CGHeroIn case Obj::SCHOOL_OF_MAGIC: case Obj::SCHOOL_OF_WAR: return 1000; - case Obj::UNIVERSITY: - return 2000; case Obj::CREATURE_GENERATOR1: case Obj::CREATURE_GENERATOR2: case Obj::CREATURE_GENERATOR3: diff --git a/AI/VCAI/Goals/CollectRes.cpp b/AI/VCAI/Goals/CollectRes.cpp index 2e7c21fa7..2fed89549 100644 --- a/AI/VCAI/Goals/CollectRes.cpp +++ b/AI/VCAI/Goals/CollectRes.cpp @@ -131,13 +131,16 @@ TSubgoal CollectRes::whatToDoToTrade() std::vector visObjs; ai->retrieveVisitableObjs(visObjs, true); - for (const CGObjectInstance * obj : visObjs) + for(const CGObjectInstance * obj : visObjs) { - if (const IMarket * m = IMarket::castFrom(obj, false)) + if(const IMarket * m = IMarket::castFrom(obj, false); m->allowsTrade(EMarketMode::RESOURCE_RESOURCE)) { - if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE)) - markets.push_back(m); - else if (obj->ID == Obj::TRADING_POST) + if(obj->ID == Obj::TOWN) + { + if(obj->tempOwner == ai->playerID) + markets.push_back(m); + } + else markets.push_back(m); } } diff --git a/config/objects/generic.json b/config/objects/generic.json index c20e0b137..e147427da 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -15,7 +15,7 @@ }, "altarOfSacrifice" : { - "index" :2, + "index" :2, //deprecated "handler" : "market", "base" : { "sounds" : { @@ -36,7 +36,7 @@ } }, "tradingPost" : { - "index" :221, + "index" :221, //deprecated "handler" : "market", "base" : { "sounds" : { @@ -60,7 +60,7 @@ } }, "tradingPostDUPLICATE" : { - "index" :99, + "index" :99, //deprecated "handler" : "market", "base" : { "sounds" : { @@ -84,7 +84,7 @@ } }, "freelancersGuild" : { - "index" :213, + "index" :213, //deprecated "handler" : "market", "types" : { "object" : { @@ -101,7 +101,7 @@ }, "blackMarket" : { - "index" :7, + "index" :7, //deprecated "handler" : "market", "base" : { "sounds" : { @@ -545,7 +545,7 @@ } }, "university" : { - "index" :104, + "index" :104, //deprecated "handler" : "market", "base" : { "sounds" : { diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 5f22f9475..2b909a031 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -763,12 +763,12 @@ public: enum EObj { NO_OBJ = -1, - ALTAR_OF_SACRIFICE = 2, + ALTAR_OF_SACRIFICE [[deprecated]] = 2, ANCHOR_POINT = 3, ARENA = 4, ARTIFACT = 5, PANDORAS_BOX = 6, - BLACK_MARKET = 7, + BLACK_MARKET [[deprecated]] = 7, BOAT = 8, BORDERGUARD = 9, KEYMASTER = 10, @@ -860,12 +860,12 @@ public: TEMPLE = 96, DEN_OF_THIEVES = 97, TOWN = 98, - TRADING_POST = 99, + TRADING_POST [[deprecated]] = 99, LEARNING_STONE = 100, TREASURE_CHEST = 101, TREE_OF_KNOWLEDGE = 102, SUBTERRANEAN_GATE = 103, - UNIVERSITY = 104, + UNIVERSITY [[deprecated]] = 104, WAGON = 105, WAR_MACHINE_FACTORY = 106, SCHOOL_OF_WAR = 107, @@ -880,7 +880,7 @@ public: RANDOM_MONSTER_L6 = 163, RANDOM_MONSTER_L7 = 164, BORDER_GATE = 212, - FREELANCERS_GUILD = 213, + FREELANCERS_GUILD [[deprecated]] = 213, HERO_PLACEHOLDER = 214, QUEST_GUARD = 215, RANDOM_DWELLING = 216, @@ -888,7 +888,7 @@ public: RANDOM_DWELLING_FACTION = 218, //subtype = faction GARRISON2 = 219, ABANDONED_MINE = 220, - TRADING_POST_SNOW = 221, + TRADING_POST_SNOW [[deprecated]] = 221, CLOVER_FIELD = 222, CURSED_GROUND2 = 223, EVIL_FOG = 224, From fdac1d66d89e971adef130ab9443d73fe41943af Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 1 May 2023 15:45:32 +0400 Subject: [PATCH 23/25] Fix warnings --- client/widgets/CArtifactHolder.cpp | 2 +- client/widgets/CArtifactsOfHeroAltar.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index 9b794090b..74172c484 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -250,7 +250,7 @@ void CHeroArtPlace::setArtifact(const CArtifactInstance * art) void CHeroArtPlace::addCombinedArtInfo(std::map & arts) { - for(const auto combinedArt : arts) + for(const auto & combinedArt : arts) { std::string artList; text += "\n\n"; diff --git a/client/widgets/CArtifactsOfHeroAltar.cpp b/client/widgets/CArtifactsOfHeroAltar.cpp index 0e78ca713..d396a80d2 100644 --- a/client/widgets/CArtifactsOfHeroAltar.cpp +++ b/client/widgets/CArtifactsOfHeroAltar.cpp @@ -99,7 +99,7 @@ void CArtifactsOfHeroAltar::deleteFromVisible(const CArtifactInstance * artInst) { if(artInst->canBeDisassembled()) { - for(const auto part : dynamic_cast(artInst)->constituentsInfo) + for(const auto & part : dynamic_cast(artInst)->constituentsInfo) { if(part.slot != ArtifactPosition::PRE_FIRST) getArtPlace(part.slot)->setArtifact(nullptr); From a507a4c1ecccddec35502ea1b1c03bc2538a9995 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 1 May 2023 15:52:35 +0400 Subject: [PATCH 24/25] Remove deprecated objects from map editor --- mapeditor/mainwindow.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index a318a0b6f..64809c012 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -540,19 +540,16 @@ void MainWindow::addGroupIntoCatalog(const std::string & groupName, bool useCust //create object to extract name std::unique_ptr temporaryObj(factory->create(templ)); QString translated = useCustomName ? tr(temporaryObj->getObjectName().c_str()) : subGroupName; + itemType->setText(translated); //do not have extra level if(singleTemplate) { - if(useCustomName) - itemType->setText(translated); itemType->setIcon(QIcon(preview)); itemType->setData(data); } else { - if(useCustomName) - itemType->setText(translated); auto * item = new QStandardItem(QIcon(preview), QString::fromStdString(templ->stringID)); item->setData(data); itemType->appendRow(item); @@ -623,9 +620,7 @@ void MainWindow::loadObjectsTree() addGroupIntoCatalog("TOWNS", true, false, Obj::SHIPYARD); addGroupIntoCatalog("TOWNS", true, false, Obj::GARRISON); addGroupIntoCatalog("TOWNS", true, false, Obj::GARRISON2); - addGroupIntoCatalog("OBJECTS", true, false, Obj::ALTAR_OF_SACRIFICE); addGroupIntoCatalog("OBJECTS", true, false, Obj::ARENA); - addGroupIntoCatalog("OBJECTS", true, false, Obj::BLACK_MARKET); addGroupIntoCatalog("OBJECTS", true, false, Obj::BUOY); addGroupIntoCatalog("OBJECTS", true, false, Obj::CARTOGRAPHER); addGroupIntoCatalog("OBJECTS", true, false, Obj::SWAN_POND); @@ -662,17 +657,13 @@ void MainWindow::loadObjectsTree() addGroupIntoCatalog("OBJECTS", true, false, Obj::TAVERN); addGroupIntoCatalog("OBJECTS", true, false, Obj::TEMPLE); addGroupIntoCatalog("OBJECTS", true, false, Obj::DEN_OF_THIEVES); - addGroupIntoCatalog("OBJECTS", true, false, Obj::TRADING_POST); - addGroupIntoCatalog("OBJECTS", true, false, Obj::TRADING_POST_SNOW); addGroupIntoCatalog("OBJECTS", true, false, Obj::LEARNING_STONE); addGroupIntoCatalog("OBJECTS", true, false, Obj::TREE_OF_KNOWLEDGE); - addGroupIntoCatalog("OBJECTS", true, false, Obj::UNIVERSITY); addGroupIntoCatalog("OBJECTS", true, false, Obj::WAGON); addGroupIntoCatalog("OBJECTS", true, false, Obj::SCHOOL_OF_WAR); addGroupIntoCatalog("OBJECTS", true, false, Obj::WAR_MACHINE_FACTORY); addGroupIntoCatalog("OBJECTS", true, false, Obj::WARRIORS_TOMB); addGroupIntoCatalog("OBJECTS", true, false, Obj::WITCH_HUT); - addGroupIntoCatalog("OBJECTS", true, false, Obj::FREELANCERS_GUILD); addGroupIntoCatalog("OBJECTS", true, false, Obj::SANCTUARY); addGroupIntoCatalog("OBJECTS", true, false, Obj::MARLETTO_TOWER); addGroupIntoCatalog("HEROES", true, false, Obj::PRISON); From b6270fae498a5d71ddfbf251e024347581f5faf0 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 1 May 2023 18:58:59 +0400 Subject: [PATCH 25/25] Remove deprecated from config --- config/objects/generic.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config/objects/generic.json b/config/objects/generic.json index e147427da..0301f45a7 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -15,7 +15,7 @@ }, "altarOfSacrifice" : { - "index" :2, //deprecated + "index" :2, "handler" : "market", "base" : { "sounds" : { @@ -36,7 +36,7 @@ } }, "tradingPost" : { - "index" :221, //deprecated + "index" :221, "handler" : "market", "base" : { "sounds" : { @@ -60,7 +60,7 @@ } }, "tradingPostDUPLICATE" : { - "index" :99, //deprecated + "index" :99, "handler" : "market", "base" : { "sounds" : { @@ -84,7 +84,7 @@ } }, "freelancersGuild" : { - "index" :213, //deprecated + "index" :213, "handler" : "market", "types" : { "object" : { @@ -101,7 +101,7 @@ }, "blackMarket" : { - "index" :7, //deprecated + "index" :7, "handler" : "market", "base" : { "sounds" : { @@ -545,7 +545,7 @@ } }, "university" : { - "index" :104, //deprecated + "index" :104, "handler" : "market", "base" : { "sounds" : {