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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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 67c5381ae8fa00ce4a80498634bd9620791cd5e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Sat, 29 Apr 2023 11:48:47 +0200 Subject: [PATCH 07/43] Add some non-zero value treasures to fix empty zones. --- Mods/vcmi/config/vcmi/rmg/symmetric/3sm3d.JSON | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Mods/vcmi/config/vcmi/rmg/symmetric/3sm3d.JSON b/Mods/vcmi/config/vcmi/rmg/symmetric/3sm3d.JSON index 08c850978..a4c75f4be 100644 --- a/Mods/vcmi/config/vcmi/rmg/symmetric/3sm3d.JSON +++ b/Mods/vcmi/config/vcmi/rmg/symmetric/3sm3d.JSON @@ -112,9 +112,8 @@ "matchTerrainToTown" : false, "treasure" : [ - { "min" : 0, "max" : 0, "density" : 1 }, - { "min" : 0, "max" : 0, "density" : 1 }, - { "min" : 0, "max" : 0, "density" : 1 } + { "min" : 100, "max" : 1000, "density" : 10 }, + { "min" : 1000, "max" : 3000, "density" : 1 } ] }, "12" : From 1899dd97c794a97814a179f975a8e09521fdd63c Mon Sep 17 00:00:00 2001 From: nordsoft Date: Fri, 21 Apr 2023 03:17:13 +0400 Subject: [PATCH 08/43] Customize colors and teams --- mapeditor/playerparams.cpp | 36 ++++++++++++ mapeditor/playerparams.h | 4 ++ mapeditor/playerparams.ui | 108 ++++++++++++++++++----------------- mapeditor/playersettings.cpp | 19 ++++-- 4 files changed, 109 insertions(+), 58 deletions(-) diff --git a/mapeditor/playerparams.cpp b/mapeditor/playerparams.cpp index daf82e1f3..fe3aded39 100644 --- a/mapeditor/playerparams.cpp +++ b/mapeditor/playerparams.cpp @@ -12,6 +12,7 @@ #include "playerparams.h" #include "ui_playerparams.h" #include "../lib/CTownHandler.h" +#include "../lib/StringConstants.h" PlayerParams::PlayerParams(MapController & ctrl, int playerId, QWidget *parent) : QWidget(parent), @@ -19,10 +20,27 @@ PlayerParams::PlayerParams(MapController & ctrl, int playerId, QWidget *parent) controller(ctrl) { ui->setupUi(this); + + //set colors and teams + ui->teamId->addItem("No team", QVariant(TeamID::NO_TEAM)); + for(int i = 0, index = 0; i < PlayerColor::PLAYER_LIMIT_I; ++i) + { + if(i == playerId || !controller.map()->players[i].canAnyonePlay()) + { + ui->playerColorCombo->addItem(QString::fromStdString(GameConstants::PLAYER_COLOR_NAMES[i]), QVariant(i)); + if(i == playerId) + ui->playerColorCombo->setCurrentIndex(index); + ++index; + } + + //add teams + ui->teamId->addItem(QString::number(i + 1), QVariant(i)); + } playerColor = playerId; assert(controller.map()->players.size() > playerColor); playerInfo = controller.map()->players[playerColor]; + ui->teamId->setCurrentIndex(playerInfo.team == TeamID::NO_TEAM ? 0 : playerInfo.team.getNum() + 1); //load factions for(auto idx : VLC->townh->getAllowedFactions()) @@ -148,3 +166,21 @@ void PlayerParams::on_mainTown_activated(int index) } } + +void PlayerParams::on_teamId_activated(int index) +{ + playerInfo.team = ui->teamId->currentData().toInt(); +} + + +void PlayerParams::on_playerColorCombo_activated(int index) +{ + int data = ui->playerColorCombo->currentData().toInt(); + if(data != playerColor) + { + controller.map()->players[playerColor].canHumanPlay = false; + controller.map()->players[playerColor].canComputerPlay = false; + playerColor = data; + } +} + diff --git a/mapeditor/playerparams.h b/mapeditor/playerparams.h index 6df0d39f2..6fddf1b29 100644 --- a/mapeditor/playerparams.h +++ b/mapeditor/playerparams.h @@ -42,6 +42,10 @@ private slots: void allowedFactionsCheck(QListWidgetItem *); + void on_teamId_activated(int index); + + void on_playerColorCombo_activated(int index); + private: Ui::PlayerParams *ui; diff --git a/mapeditor/playerparams.ui b/mapeditor/playerparams.ui index 5eeee8832..0bbe9b0c5 100644 --- a/mapeditor/playerparams.ui +++ b/mapeditor/playerparams.ui @@ -50,67 +50,45 @@ - - - - - No team - - - + + - - - - Human/CPU - - - - - - - CPU only - - - - - - - Team - - - - - - - Main town - - - - - - - Random faction - - - - + Generate hero at main - - - - - (default) - - + + + + Random faction + - + + + + Team + + + + + + + CPU only + + + + + + + Human/CPU + + + + true @@ -132,6 +110,32 @@ + + + + + (default) + + + + + + + + Main town + + + + + + + + + + Color + + + diff --git a/mapeditor/playersettings.cpp b/mapeditor/playersettings.cpp index 697436dad..ffe2c1f7c 100644 --- a/mapeditor/playersettings.cpp +++ b/mapeditor/playersettings.cpp @@ -24,11 +24,11 @@ PlayerSettings::PlayerSettings(MapController & ctrl, QWidget *parent) : int players = 0; const int minAllowedPlayers = 1; - for(auto & p : controller.map()->players) + for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; ++i) { - if(p.canAnyonePlay()) + if(controller.map()->players[i].canAnyonePlay()) { - paramWidgets.push_back(new PlayerParams(controller, players)); + paramWidgets.push_back(new PlayerParams(controller, i)); ui->playersLayout->addWidget(paramWidgets.back()); ++players; } @@ -51,22 +51,29 @@ void PlayerSettings::on_playersCount_currentIndexChanged(int index) { const auto selectedPlayerCount = index + 1; assert(selectedPlayerCount <= controller.map()->players.size()); + std::set availableColors{0, 1, 2, 3, 4, 5, 6, 7}; for(int i = 0; i < selectedPlayerCount; ++i) { if(i < paramWidgets.size()) + { + availableColors.erase(paramWidgets[i]->playerColor); continue; + } - auto & p = controller.map()->players[i]; + assert(!availableColors.empty()); + auto plColor = *availableColors.begin(); + auto & p = controller.map()->players[plColor]; p.canComputerPlay = true; - paramWidgets.push_back(new PlayerParams(controller, i)); + paramWidgets.push_back(new PlayerParams(controller, plColor)); + availableColors.erase(plColor); ui->playersLayout->addWidget(paramWidgets.back()); } assert(!paramWidgets.empty()); for(int i = paramWidgets.size() - 1; i >= selectedPlayerCount; --i) { - auto & p = controller.map()->players[i]; + auto & p = controller.map()->players[paramWidgets[i]->playerColor]; p.canComputerPlay = false; p.canHumanPlay = false; ui->playersLayout->removeWidget(paramWidgets[i]); From bab84309a52d572dfc94e569e825428fd163266f Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 22 Apr 2023 18:47:25 +0400 Subject: [PATCH 09/43] Support teams in map editor --- mapeditor/windownewmap.cpp | 117 ++++++++++++++++------ mapeditor/windownewmap.h | 18 +++- mapeditor/windownewmap.ui | 194 +++++++++++++++++-------------------- 3 files changed, 195 insertions(+), 134 deletions(-) diff --git a/mapeditor/windownewmap.cpp b/mapeditor/windownewmap.cpp index 714012d9a..cb5dc9b3f 100644 --- a/mapeditor/windownewmap.cpp +++ b/mapeditor/windownewmap.cpp @@ -31,6 +31,28 @@ WindowNewMap::WindowNewMap(QWidget *parent) : setAttribute(Qt::WA_DeleteOnClose); setWindowModality(Qt::ApplicationModal); + + for(auto * combo : {ui->humanCombo, ui->cpuCombo, ui->humanTeamsCombo, ui->cpuTeamsCombo}) + combo->clear(); + + //prepare human players combo box + for(int i = 0; i <= PlayerColor::PLAYER_LIMIT_I; ++i) + { + ui->humanCombo->addItem(!i ? randomString : QString::number(players.at(i))); + ui->humanCombo->setItemData(i, QVariant(players.at(i))); + + ui->cpuCombo->addItem(!i ? randomString : QString::number(cpuPlayers.at(i))); + ui->cpuCombo->setItemData(i, QVariant(cpuPlayers.at(i))); + + ui->humanTeamsCombo->addItem(!i ? randomString : QString::number(cpuPlayers.at(i))); + ui->humanTeamsCombo->setItemData(i, QVariant(cpuPlayers.at(i))); + + ui->cpuTeamsCombo->addItem(!i ? randomString : QString::number(cpuPlayers.at(i))); + ui->cpuTeamsCombo->setItemData(i, QVariant(cpuPlayers.at(i))); + } + + for(auto * combo : {ui->humanCombo, ui->cpuCombo, ui->humanTeamsCombo, ui->cpuTeamsCombo}) + combo->setCurrentIndex(0); loadUserSettings(); @@ -87,8 +109,17 @@ void WindowNewMap::loadUserSettings() { ui->cpuCombo->setCurrentIndex(cpuPlayers.toInt()); } - //TODO: teams when implemented - + auto teams = s.value(newMapHumanTeams); + if(teams.isValid()) + { + ui->humanTeamsCombo->setCurrentIndex(teams.toInt()); + } + auto cputeams = s.value(newMapCpuTeams); + if(cputeams.isValid()) + { + ui->cpuTeamsCombo->setCurrentIndex(cputeams.toInt()); + } + auto waterContent = s.value(newMapWaterContent); if (waterContent.isValid()) { @@ -150,7 +181,8 @@ void WindowNewMap::saveUserSettings() s.setValue(newMapPlayers,ui->humanCombo->currentIndex()); s.setValue(newMapCpuPlayers,ui->cpuCombo->currentIndex()); - //TODO: teams when implemented + s.setValue(newMapHumanTeams, ui->humanTeamsCombo->currentIndex()); + s.setValue(newMapCpuTeams, ui->cpuTeamsCombo->currentIndex()); EWaterContent::EWaterContent water = EWaterContent::RANDOM; if(ui->waterOpt1->isChecked()) @@ -272,16 +304,8 @@ void WindowNewMap::on_okButton_clicked() void WindowNewMap::on_sizeCombo_activated(int index) { - std::map> sizes - { - {0, {36, 36}}, - {1, {72, 72}}, - {2, {108, 108}}, - {3, {144, 144}}, - }; - - ui->widthTxt->setText(QString::number(sizes[index].first)); - ui->heightTxt->setText(QString::number(sizes[index].second)); + ui->widthTxt->setText(QString::number(mapSizes.at(index).first)); + ui->heightTxt->setText(QString::number(mapSizes.at(index).second)); } @@ -295,12 +319,11 @@ void WindowNewMap::on_twoLevelCheck_stateChanged(int arg1) void WindowNewMap::on_humanCombo_activated(int index) { - int humans = players.at(index); - if(humans > playerLimit) + int humans = ui->humanCombo->currentData().toInt(); + if(humans > PlayerColor::PLAYER_LIMIT_I) { - humans = playerLimit; + humans = PlayerColor::PLAYER_LIMIT_I; ui->humanCombo->setCurrentIndex(humans); - return; } mapGenOptions.setPlayerCount(humans); @@ -309,24 +332,23 @@ void WindowNewMap::on_humanCombo_activated(int index) if(teams > humans - 1) { teams = humans - 1; - //TBD + ui->humanTeamsCombo->setCurrentIndex(teams + 1); //skip one element because first is random } int cpu = mapGenOptions.getCompOnlyPlayerCount(); - if(cpu > playerLimit - humans) + if(cpu > PlayerColor::PLAYER_LIMIT_I - humans) { - cpu = playerLimit - humans; - ui->cpuCombo->setCurrentIndex(cpu + 1); + cpu = PlayerColor::PLAYER_LIMIT_I - humans; + ui->cpuCombo->setCurrentIndex(cpu + 1); //skip one element because first is random } int cpuTeams = mapGenOptions.getCompOnlyTeamCount(); //comp only players - 1 if(cpuTeams > cpu - 1) { cpuTeams = cpu - 1; - //TBD + ui->cpuTeamsCombo->setCurrentIndex(cpuTeams + 1); //skip one element because first is random } - //void setMapTemplate(const CRmgTemplate * value); updateTemplateList(); } @@ -334,15 +356,22 @@ void WindowNewMap::on_humanCombo_activated(int index) void WindowNewMap::on_cpuCombo_activated(int index) { int humans = mapGenOptions.getPlayerCount(); - int cpu = cpuPlayers.at(index); - if(cpu > playerLimit - humans) + int cpu = ui->cpuCombo->currentData().toInt(); + if(cpu > PlayerColor::PLAYER_LIMIT_I - humans) { - cpu = playerLimit - humans; - ui->cpuCombo->setCurrentIndex(cpu + 1); - return; + cpu = PlayerColor::PLAYER_LIMIT_I - humans; + ui->cpuCombo->setCurrentIndex(cpu + 1); //skip one element because first is random + } + + mapGenOptions.setCompOnlyPlayerCount(cpu); + + int cpuTeams = mapGenOptions.getCompOnlyTeamCount(); //comp only players - 1 + if(cpuTeams > cpu - 1) + { + cpuTeams = cpu - 1; + ui->cpuTeamsCombo->setCurrentIndex(cpuTeams + 1); //skip one element because first is random } - mapGenOptions.setCompOnlyPlayerCount(cpu); updateTemplateList(); } @@ -417,3 +446,33 @@ void WindowNewMap::on_checkSeed_toggled(bool checked) ui->lineSeed->setEnabled(checked); } + +void WindowNewMap::on_humanTeamsCombo_activated(int index) +{ + int humans = mapGenOptions.getPlayerCount(); + int teams = ui->humanTeamsCombo->currentData().toInt(); + if(teams >= humans) + { + teams = humans - 1; + ui->humanTeamsCombo->setCurrentIndex(teams + 1); //skip one element because first is random + } + + mapGenOptions.setTeamCount(teams); + updateTemplateList(); +} + + +void WindowNewMap::on_cpuTeamsCombo_activated(int index) +{ + int cpu = mapGenOptions.getCompOnlyPlayerCount(); + int teams = ui->cpuTeamsCombo->currentData().toInt(); + if(teams >= cpu) + { + teams = cpu - 1; + ui->cpuTeamsCombo->setCurrentIndex(teams + 1); //skip one element because first is random + } + + mapGenOptions.setCompOnlyTeamCount(teams); + updateTemplateList(); +} + diff --git a/mapeditor/windownewmap.h b/mapeditor/windownewmap.h index 7f1be66bd..76bd5dc22 100644 --- a/mapeditor/windownewmap.h +++ b/mapeditor/windownewmap.h @@ -28,11 +28,13 @@ class WindowNewMap : public QDialog const QString newMapGenerateRandom = "NewMapWindow/GenerateRandom"; const QString newMapPlayers = "NewMapWindow/Players"; //map index const QString newMapCpuPlayers = "NewMapWindow/CpuPlayers"; //map index + const QString newMapHumanTeams = "NewMapWindow/HumanTeams"; //map index + const QString newMapCpuTeams = "NewMapWindow/CpuTeams"; //map index const QString newMapWaterContent = "NewMapWindow/WaterContent"; const QString newMapMonsterStrength = "NewMapWindow/MonsterStrength"; const QString newMapTemplate = "NewMapWindow/Template"; - - const int playerLimit = 8; + + const QString randomString = "Random"; const std::map players { @@ -59,6 +61,14 @@ class WindowNewMap : public QDialog {7, 6}, {8, 7} }; + + const std::map> mapSizes + { + {0, {36, 36}}, + {1, {72, 72}}, + {2, {108, 108}}, + {3, {144, 144}}, + }; public: explicit WindowNewMap(QWidget *parent = nullptr); @@ -87,6 +97,10 @@ private slots: void on_checkSeed_toggled(bool checked); + void on_humanTeamsCombo_activated(int index); + + void on_cpuTeamsCombo_activated(int index); + private: void updateTemplateList(); diff --git a/mapeditor/windownewmap.ui b/mapeditor/windownewmap.ui index 242c6f9d7..4e83d644b 100644 --- a/mapeditor/windownewmap.ui +++ b/mapeditor/windownewmap.ui @@ -213,59 +213,60 @@ 68 - - - - - - 48 - 0 - - - - - 64 - 16777215 - - - - 0 - + + + + + + Random + + 0 - - - - - - 0 - - 0 + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 - - - - - 96 - 0 - - - - - 120 - 16777215 - - + + - Human/Computer + Computer only @@ -330,87 +331,74 @@ - - + + + + + 96 + 0 + + + + + 120 + 16777215 + + - Computer only + Human/Computer - - - - Qt::Horizontal - - + + + - 40 - 20 + 0 + 0 - - - - - - - Random - - + + + 16777215 + 16777215 + + + + 0 + 0 + + + + + + Human teams + + + + + + + 0 + - 1 - - - - - 2 - - - - - 3 - - - - - 4 - - - - - 5 - - - - - 6 - - - - - 7 + 0 - - - - Qt::Horizontal + + + + Computer teams - - - 40 - 20 - - - + From 1de9a57a7631d2cc8d54d95eaaa4aa51c958e52f Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 22 Apr 2023 20:08:52 +0400 Subject: [PATCH 10/43] Add validation for hero duplicate --- mapeditor/validator.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mapeditor/validator.cpp b/mapeditor/validator.cpp index c7a342542..c1b5892a1 100644 --- a/mapeditor/validator.cpp +++ b/mapeditor/validator.cpp @@ -74,6 +74,8 @@ std::list Validator::validate(const CMap * map) if(!hplayers) issues.emplace_back("No human players allowed to play this map", true); + std::set allHeroesOnMap; //used to find hero duplicated + //checking all objects in the map for(auto o : map->objects) { @@ -121,6 +123,9 @@ std::list Validator::validate(const CMap * map) { if(!map->allowedHeroes[ins->type->getId().getNum()]) issues.emplace_back(QString("Hero %1 is prohibited by map settings").arg(ins->type->getNameTranslated().c_str()), false); + + if(!allHeroesOnMap.insert(ins->type).second) + issues.emplace_back(QString("Hero %1 has duplicate on map").arg(ins->type->getNameTranslated().c_str()), false); } else issues.emplace_back(QString("Hero %1 has an empty type and must be removed").arg(ins->instanceName.c_str()), true); From 9aaa8c0d8907a6e53b426eff84b78648f822b0f3 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 22 Apr 2023 20:38:39 +0400 Subject: [PATCH 11/43] Fix teams switching --- mapeditor/windownewmap.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mapeditor/windownewmap.cpp b/mapeditor/windownewmap.cpp index cb5dc9b3f..e320bf899 100644 --- a/mapeditor/windownewmap.cpp +++ b/mapeditor/windownewmap.cpp @@ -331,7 +331,7 @@ void WindowNewMap::on_humanCombo_activated(int index) int teams = mapGenOptions.getTeamCount(); if(teams > humans - 1) { - teams = humans - 1; + teams = humans > 0 ? humans - 1 : CMapGenOptions::RANDOM_SIZE; ui->humanTeamsCombo->setCurrentIndex(teams + 1); //skip one element because first is random } @@ -345,7 +345,7 @@ void WindowNewMap::on_humanCombo_activated(int index) int cpuTeams = mapGenOptions.getCompOnlyTeamCount(); //comp only players - 1 if(cpuTeams > cpu - 1) { - cpuTeams = cpu - 1; + cpuTeams = cpu > 0 ? cpu - 1 : CMapGenOptions::RANDOM_SIZE; ui->cpuTeamsCombo->setCurrentIndex(cpuTeams + 1); //skip one element because first is random } @@ -368,7 +368,7 @@ void WindowNewMap::on_cpuCombo_activated(int index) int cpuTeams = mapGenOptions.getCompOnlyTeamCount(); //comp only players - 1 if(cpuTeams > cpu - 1) { - cpuTeams = cpu - 1; + cpuTeams = cpu > 0 ? cpu - 1 : CMapGenOptions::RANDOM_SIZE; ui->cpuTeamsCombo->setCurrentIndex(cpuTeams + 1); //skip one element because first is random } @@ -453,7 +453,7 @@ void WindowNewMap::on_humanTeamsCombo_activated(int index) int teams = ui->humanTeamsCombo->currentData().toInt(); if(teams >= humans) { - teams = humans - 1; + teams = humans > 0 ? humans - 1 : CMapGenOptions::RANDOM_SIZE; ui->humanTeamsCombo->setCurrentIndex(teams + 1); //skip one element because first is random } @@ -465,14 +465,14 @@ void WindowNewMap::on_humanTeamsCombo_activated(int index) void WindowNewMap::on_cpuTeamsCombo_activated(int index) { int cpu = mapGenOptions.getCompOnlyPlayerCount(); - int teams = ui->cpuTeamsCombo->currentData().toInt(); - if(teams >= cpu) + int cpuTeams = ui->cpuTeamsCombo->currentData().toInt(); + if(cpuTeams >= cpu) { - teams = cpu - 1; - ui->cpuTeamsCombo->setCurrentIndex(teams + 1); //skip one element because first is random + cpuTeams = cpu > 0 ? cpu - 1 : CMapGenOptions::RANDOM_SIZE; + ui->cpuTeamsCombo->setCurrentIndex(cpuTeams + 1); //skip one element because first is random } - mapGenOptions.setCompOnlyTeamCount(teams); + mapGenOptions.setCompOnlyTeamCount(cpuTeams); updateTemplateList(); } From dee235ea19e083d9a2a3bc15bb37b9bd3f6d55c7 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 22 Apr 2023 20:41:23 +0400 Subject: [PATCH 12/43] Implement map export to image --- mapeditor/mainwindow.cpp | 11 +++++++++++ mapeditor/mainwindow.h | 4 +++- mapeditor/mainwindow.ui | 12 +++++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index 5f3a141c5..fad723bc2 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -1234,3 +1234,14 @@ void MainWindow::on_actionPaste_triggered() } } + +void MainWindow::on_actionExport_triggered() +{ + QString fileName = QFileDialog::getSaveFileName(this, "Save to image", QCoreApplication::applicationDirPath(), "BMP (*.bmp);;JPEG (*.jpeg);;PNG (*.png)"); + if(!fileName.isNull()) + { + auto pixmap = ui->mapView->grab(); + pixmap.save(fileName); + } +} + diff --git a/mapeditor/mainwindow.h b/mapeditor/mainwindow.h index 16f5e6a18..f509b0882 100644 --- a/mapeditor/mainwindow.h +++ b/mapeditor/mainwindow.h @@ -41,7 +41,7 @@ public: void saveMap(); bool openMap(const QString &); - MapView * mapView(); + //MapView * mapView(); void loadObjectsTree(); @@ -115,6 +115,8 @@ private slots: void on_actionPaste_triggered(); + void on_actionExport_triggered(); + public slots: void treeViewSelected(const QModelIndex &selected, const QModelIndex &deselected); diff --git a/mapeditor/mainwindow.ui b/mapeditor/mainwindow.ui index e760aed36..b24467265 100644 --- a/mapeditor/mainwindow.ui +++ b/mapeditor/mainwindow.ui @@ -62,6 +62,7 @@ + @@ -750,7 +751,7 @@ 0 0 128 - 257 + 251 @@ -793,7 +794,7 @@ 0 0 128 - 257 + 251 @@ -829,7 +830,7 @@ 0 0 128 - 257 + 251 @@ -1228,6 +1229,11 @@ Ctrl+8 + + + Export as + + From e8a8e893ee99bd5e5be328b054781c5a3b46efae Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 29 Apr 2023 21:33:23 +0400 Subject: [PATCH 13/43] Export grabs whole map --- mapeditor/mainwindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index fad723bc2..a318a0b6f 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -1240,8 +1240,10 @@ void MainWindow::on_actionExport_triggered() QString fileName = QFileDialog::getSaveFileName(this, "Save to image", QCoreApplication::applicationDirPath(), "BMP (*.bmp);;JPEG (*.jpeg);;PNG (*.png)"); if(!fileName.isNull()) { - auto pixmap = ui->mapView->grab(); - pixmap.save(fileName); + QImage image(ui->mapView->scene()->sceneRect().size().toSize(), QImage::Format_RGB888); + QPainter painter(&image); + ui->mapView->scene()->render(&painter); + image.save(fileName); } } From 51a261b449fa4fcd79dc02c760280f4ddc083fe1 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 29 Apr 2023 21:38:48 +0400 Subject: [PATCH 14/43] Auto-adjust size --- mapeditor/playerparams.ui | 50 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/mapeditor/playerparams.ui b/mapeditor/playerparams.ui index 0bbe9b0c5..811fd626e 100644 --- a/mapeditor/playerparams.ui +++ b/mapeditor/playerparams.ui @@ -11,7 +11,7 @@ - + 0 0 @@ -51,10 +51,23 @@ - + + + + 0 + 0 + + + + + + 0 + 0 + + Generate hero at main @@ -62,6 +75,12 @@ + + + 0 + 0 + + Random faction @@ -76,6 +95,12 @@ + + + 0 + 0 + + CPU only @@ -83,6 +108,12 @@ + + + 0 + 0 + + Human/CPU @@ -112,6 +143,12 @@ + + + 0 + 0 + + (default) @@ -127,7 +164,14 @@ - + + + + 0 + 0 + + + From 86f5d6de6955b8bf38d60217b129c3394c3af73b Mon Sep 17 00:00:00 2001 From: Konstantin Date: Tue, 18 Apr 2023 00:11:16 +0300 Subject: [PATCH 15/43] vcmi: modernize headers --- AI/BattleAI/BattleExchangeVariant.h | 10 +--- AI/Nullkiller/AIUtility.h | 8 +-- AI/Nullkiller/Analyzers/ArmyManager.h | 7 +-- AI/Nullkiller/Analyzers/BuildAnalyzer.h | 7 ++- AI/Nullkiller/Analyzers/ObjectClusterizer.h | 16 ++---- AI/Nullkiller/Engine/DeepDecomposer.h | 2 +- AI/Nullkiller/Engine/FuzzyHelper.h | 2 +- AI/Nullkiller/Goals/AbstractGoal.h | 9 ++-- AI/Nullkiller/Pathfinding/AINodeStorage.h | 4 +- AI/Nullkiller/Pathfinding/Actors.h | 4 +- AI/VCAI/AIUtility.h | 6 +-- AI/VCAI/Goals/AbstractGoal.h | 5 +- AI/VCAI/Pathfinding/AINodeStorage.h | 2 - client/CMusicHandler.h | 2 +- client/Client.h | 4 +- client/battle/CreatureAnimation.h | 2 +- client/gui/CGuiHandler.h | 4 +- client/gui/CIntObject.h | 2 +- client/renderSDL/CTrueTypeFont.h | 2 +- client/renderSDL/SDL_Extensions.h | 42 +++++++-------- client/renderSDL/SDL_PixelAccess.h | 4 +- client/widgets/ObjectLists.h | 2 +- client/windows/InfoWindows.h | 4 +- include/vcmi/spells/Magic.h | 2 +- include/vstd/RNG.h | 4 +- lib/BattleFieldHandler.h | 8 ++- lib/CBonusTypeHandler.h | 2 +- lib/CConfigHandler.h | 2 +- lib/CCreatureSet.h | 14 ++--- lib/CHeroHandler.h | 7 ++- lib/CModHandler.h | 2 +- lib/CPathfinder.h | 11 ++-- lib/CRandomGenerator.h | 10 ++-- lib/CStack.h | 2 +- lib/CThreadHelper.h | 2 +- lib/CTownHandler.h | 2 +- lib/GameConstants.h | 8 +-- lib/HeroBonus.h | 31 ++++++----- lib/IHandlerBase.h | 4 +- lib/JsonDetail.h | 8 +-- lib/JsonNode.h | 4 +- lib/LogicalExpression.h | 58 +++++++++------------ lib/ResourceSet.h | 4 +- lib/StartInfo.h | 4 +- lib/UnlockGuard.h | 2 +- lib/filesystem/Filesystem.h | 4 +- lib/mapObjects/CObjectClassesHandler.h | 4 +- lib/mapObjects/CommonConstructors.h | 2 +- lib/mapObjects/MiscObjects.h | 4 +- lib/mapping/CMap.h | 2 +- lib/mapping/CMapEditManager.h | 2 +- lib/mapping/MapEditUtils.h | 2 +- lib/rmg/CMapGenerator.h | 2 +- lib/rmg/CZonePlacer.h | 8 +-- lib/rmg/ConnectionsPlacer.h | 2 +- lib/rmg/ObjectManager.h | 5 +- lib/rmg/ObstaclePlacer.h | 4 +- lib/rmg/RmgMap.h | 2 +- lib/rmg/RoadPlacer.h | 2 +- lib/rmg/TownPlacer.h | 3 +- lib/rmg/TreasurePlacer.h | 5 +- lib/rmg/WaterAdopter.h | 2 +- lib/rmg/WaterProxy.h | 4 +- lib/serializer/BinaryDeserializer.h | 10 ++-- lib/serializer/BinarySerializer.h | 20 +++---- lib/serializer/CSerializer.h | 32 ++++++------ lib/serializer/CTypeList.h | 17 +++--- lib/serializer/Connection.h | 4 +- lib/serializer/JsonSerializeFormat.h | 6 +-- lib/spells/CSpellHandler.h | 4 +- lib/vcmi_endian.h | 4 +- scripting/lua/LuaCallWrapper.h | 2 +- scripting/lua/LuaStack.h | 2 +- server/CGameHandler.h | 4 +- server/CQuery.h | 2 +- 75 files changed, 234 insertions(+), 273 deletions(-) diff --git a/AI/BattleAI/BattleExchangeVariant.h b/AI/BattleAI/BattleExchangeVariant.h index 6c8e0b448..2cfba35d0 100644 --- a/AI/BattleAI/BattleExchangeVariant.h +++ b/AI/BattleAI/BattleExchangeVariant.h @@ -56,10 +56,7 @@ struct EvaluationResult class BattleExchangeVariant { public: - BattleExchangeVariant() - :dpsScore(0), attackerValue() - { - } + BattleExchangeVariant(): dpsScore(0) {} int64_t trackAttack(const AttackPossibility & ap, HypotheticBattle & state); @@ -92,10 +89,7 @@ private: std::vector turnOrder; public: - BattleExchangeEvaluator(std::shared_ptr cb, std::shared_ptr env) - :cb(cb), reachabilityMap(), env(env), turnOrder() - { - } + BattleExchangeEvaluator(std::shared_ptr cb, std::shared_ptr env): cb(cb), env(env) {} EvaluationResult findBestTarget(const battle::Unit * activeStack, PotentialTargets & targets, HypotheticBattle & hb); int64_t calculateExchange(const AttackPossibility & ap, PotentialTargets & targets, HypotheticBattle & hb); diff --git a/AI/Nullkiller/AIUtility.h b/AI/Nullkiller/AIUtility.h index d06da7958..06dd99dc2 100644 --- a/AI/Nullkiller/AIUtility.h +++ b/AI/Nullkiller/AIUtility.h @@ -54,7 +54,7 @@ using namespace tbb; -typedef std::pair> dwellingContent; +using dwellingContent = std::pair>; namespace NKAI { @@ -305,10 +305,10 @@ public: public: using ptr_type = std::unique_ptr; - SharedPool(std::function()> elementFactory) - : elementFactory(elementFactory), pool(), sync(), instance_tracker(new SharedPool*(this)) + SharedPool(std::function()> elementFactory): + elementFactory(elementFactory), pool(), instance_tracker(new SharedPool *(this)) {} - + void add(std::unique_ptr t) { boost::lock_guard lock(sync); diff --git a/AI/Nullkiller/Analyzers/ArmyManager.h b/AI/Nullkiller/Analyzers/ArmyManager.h index 5b71bda48..b6f27adf9 100644 --- a/AI/Nullkiller/Analyzers/ArmyManager.h +++ b/AI/Nullkiller/Analyzers/ArmyManager.h @@ -32,13 +32,8 @@ struct SlotInfo struct ArmyUpgradeInfo { std::vector resultingArmy; - uint64_t upgradeValue; + uint64_t upgradeValue = 0; TResources upgradeCost; - - ArmyUpgradeInfo() - : resultingArmy(), upgradeValue(0), upgradeCost() - { - } }; class DLL_EXPORT IArmyManager //: public: IAbstractManager diff --git a/AI/Nullkiller/Analyzers/BuildAnalyzer.h b/AI/Nullkiller/Analyzers/BuildAnalyzer.h index 820442a72..43049b295 100644 --- a/AI/Nullkiller/Analyzers/BuildAnalyzer.h +++ b/AI/Nullkiller/Analyzers/BuildAnalyzer.h @@ -62,8 +62,11 @@ public: HeroRole townRole; bool hasSomethingToBuild; - TownDevelopmentInfo(const CGTownInstance* town) - :town(town), armyStrength(0), toBuild(), townDevelopmentCost(), requiredResources(), townRole(HeroRole::SCOUT), hasSomethingToBuild(false) + TownDevelopmentInfo(const CGTownInstance * town): + town(town), + armyStrength(0), + townRole(HeroRole::SCOUT), + hasSomethingToBuild(false) { } diff --git a/AI/Nullkiller/Analyzers/ObjectClusterizer.h b/AI/Nullkiller/Analyzers/ObjectClusterizer.h index c8137bba9..bc4118917 100644 --- a/AI/Nullkiller/Analyzers/ObjectClusterizer.h +++ b/AI/Nullkiller/Analyzers/ObjectClusterizer.h @@ -22,7 +22,7 @@ struct ClusterObjectInfo uint8_t turn; }; -typedef tbb::concurrent_hash_map ClusterObjects; +using ClusterObjects = tbb::concurrent_hash_map; struct ObjectCluster { @@ -36,11 +36,8 @@ public: } void addObject(const CGObjectInstance * object, const AIPath & path, float priority); - - ObjectCluster(const CGObjectInstance * blocker) - :objects(), blocker(blocker) - { - } + + ObjectCluster(const CGObjectInstance * blocker): blocker(blocker) {} ObjectCluster() : ObjectCluster(nullptr) { @@ -50,7 +47,7 @@ public: const CGObjectInstance * calculateCenter() const; }; -typedef tbb::concurrent_hash_map> ClusterMap; +using ClusterMap = tbb::concurrent_hash_map>; class ObjectClusterizer { @@ -67,10 +64,7 @@ public: std::vector> getLockedClusters() const; const CGObjectInstance * getBlocker(const AIPath & path) const; - ObjectClusterizer(const Nullkiller * ai) - :nearObjects(), farObjects(), blockedObjects(), ai(ai) - { - } + ObjectClusterizer(const Nullkiller * ai): ai(ai) {} private: bool shouldVisitObject(const CGObjectInstance * obj) const; diff --git a/AI/Nullkiller/Engine/DeepDecomposer.h b/AI/Nullkiller/Engine/DeepDecomposer.h index 7d11764e8..2f971f4cf 100644 --- a/AI/Nullkiller/Engine/DeepDecomposer.h +++ b/AI/Nullkiller/Engine/DeepDecomposer.h @@ -22,7 +22,7 @@ struct GoalHash } }; -typedef std::unordered_map TGoalHashSet; +using TGoalHashSet = std::unordered_map; class DeepDecomposer { diff --git a/AI/Nullkiller/Engine/FuzzyHelper.h b/AI/Nullkiller/Engine/FuzzyHelper.h index a3a2f2955..b203916ad 100644 --- a/AI/Nullkiller/Engine/FuzzyHelper.h +++ b/AI/Nullkiller/Engine/FuzzyHelper.h @@ -28,7 +28,7 @@ private: TacticalAdvantageEngine tacticalAdvantageEngine; public: - FuzzyHelper(const Nullkiller * ai) : ai(ai), tacticalAdvantageEngine() {} + FuzzyHelper(const Nullkiller * ai): ai(ai) {} ui64 estimateBankDanger(const CBank * bank); //TODO: move to another class? diff --git a/AI/Nullkiller/Goals/AbstractGoal.h b/AI/Nullkiller/Goals/AbstractGoal.h index 7c4788e72..a5b170c9f 100644 --- a/AI/Nullkiller/Goals/AbstractGoal.h +++ b/AI/Nullkiller/Goals/AbstractGoal.h @@ -81,9 +81,9 @@ namespace Goals bool operator<(const TSubgoal & rhs) const; }; - typedef std::shared_ptr TTask; - typedef std::vector TTaskVec; - typedef std::vector TGoalVec; + using TTask = std::shared_ptr; + using TTaskVec = std::vector; + using TGoalVec = std::vector; //method chaining + clone pattern #define SETTER(type, field) AbstractGoal & set ## field(const type &rhs) {field = rhs; return *this;}; @@ -107,8 +107,7 @@ namespace Goals const CGTownInstance *town; SETTER(CGTownInstance *, town) int bid; SETTER(int, bid) - AbstractGoal(EGoals goal = EGoals::INVALID) - : goalType(goal), hero() + AbstractGoal(EGoals goal = EGoals::INVALID): goalType(goal) { isAbstract = false; value = 0; diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.h b/AI/Nullkiller/Pathfinding/AINodeStorage.h index 2f77422d8..10989da5d 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.h +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.h @@ -205,14 +205,14 @@ public: inline void updateAINode(CGPathNode * node, std::function updater) { - auto aiNode = static_cast(node); + auto * aiNode = static_cast(node); updater(aiNode); } inline const CGHeroInstance * getHero(const CGPathNode * node) const { - auto aiNode = getAINode(node); + const auto * aiNode = getAINode(node); return aiNode->actor->hero; } diff --git a/AI/Nullkiller/Pathfinding/Actors.h b/AI/Nullkiller/Pathfinding/Actors.h index cf450dfbc..a4009caa8 100644 --- a/AI/Nullkiller/Pathfinding/Actors.h +++ b/AI/Nullkiller/Pathfinding/Actors.h @@ -32,9 +32,7 @@ public: virtual bool needsLastStack() const override; std::shared_ptr getActorAction() const; - HeroExchangeArmy() : CArmedInstance(true), armyCost(), requireBuyArmy(false) - { - } + HeroExchangeArmy(): CArmedInstance(true), requireBuyArmy(false) {} }; struct ExchangeResult diff --git a/AI/VCAI/AIUtility.h b/AI/VCAI/AIUtility.h index 475346f21..656bb39ab 100644 --- a/AI/VCAI/AIUtility.h +++ b/AI/VCAI/AIUtility.h @@ -23,9 +23,9 @@ class CCallback; struct creInfo; -typedef const int3 & crint3; -typedef const std::string & crstring; -typedef std::pair> dwellingContent; +using crint3 = const int3 &; +using crstring = const std::string &; +using dwellingContent = std::pair>; const int GOLD_MINE_PRODUCTION = 1000, WOOD_ORE_MINE_PRODUCTION = 2, RESOURCE_MINE_PRODUCTION = 1; const int ACTUAL_RESOURCE_COUNT = 7; diff --git a/AI/VCAI/Goals/AbstractGoal.h b/AI/VCAI/Goals/AbstractGoal.h index 677a53a18..ed4fc9430 100644 --- a/AI/VCAI/Goals/AbstractGoal.h +++ b/AI/VCAI/Goals/AbstractGoal.h @@ -76,7 +76,7 @@ namespace Goals //TODO: serialize? }; - typedef std::vector TGoalVec; + using TGoalVec = std::vector; //method chaining + clone pattern #define VSETTER(type, field) virtual AbstractGoal & set ## field(const type &rhs) {field = rhs; return *this;}; @@ -121,8 +121,7 @@ namespace Goals TSubgoal parent; VSETTER(TSubgoal, parent) EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext) - AbstractGoal(EGoals goal = EGoals::INVALID) - : goalType(goal), evaluationContext() + AbstractGoal(EGoals goal = EGoals::INVALID): goalType(goal) { priority = 0; isElementar = false; diff --git a/AI/VCAI/Pathfinding/AINodeStorage.h b/AI/VCAI/Pathfinding/AINodeStorage.h index b63ffc25e..98486612f 100644 --- a/AI/VCAI/Pathfinding/AINodeStorage.h +++ b/AI/VCAI/Pathfinding/AINodeStorage.h @@ -19,8 +19,6 @@ class CCallback; -extern boost::thread_specific_ptr cb; //for templates - struct AIPathNode : public CGPathNode { uint32_t chainMask; diff --git a/client/CMusicHandler.h b/client/CMusicHandler.h index 9c909c347..3594cf37d 100644 --- a/client/CMusicHandler.h +++ b/client/CMusicHandler.h @@ -14,7 +14,7 @@ struct _Mix_Music; struct SDL_RWops; -typedef struct _Mix_Music Mix_Music; +using Mix_Music = struct _Mix_Music; struct Mix_Chunk; class CAudioBase { diff --git a/client/Client.h b/client/Client.h index b4a89e089..9bdd3a020 100644 --- a/client/Client.h +++ b/client/Client.h @@ -61,8 +61,8 @@ namespace boost { class thread; } template class ThreadSafeVector { - typedef std::vector TVector; - typedef boost::unique_lock TLock; + using TVector = std::vector; + using TLock = boost::unique_lock; TVector items; boost::mutex mx; boost::condition_variable cond; diff --git a/client/battle/CreatureAnimation.h b/client/battle/CreatureAnimation.h index 610b397fd..2e7ac4a7f 100644 --- a/client/battle/CreatureAnimation.h +++ b/client/battle/CreatureAnimation.h @@ -68,7 +68,7 @@ namespace AnimationControls class CreatureAnimation : public CIntObject { public: - typedef std::function TSpeedController; + using TSpeedController = std::function; private: std::string name; diff --git a/client/gui/CGuiHandler.h b/client/gui/CGuiHandler.h index a1fbc9e37..768476758 100644 --- a/client/gui/CGuiHandler.h +++ b/client/gui/CGuiHandler.h @@ -80,7 +80,7 @@ private: std::vector> disposed; std::atomic continueEventHandling; - typedef std::list CIntObjectList; + using CIntObjectList = std::list; //active GUI elements (listening for events CIntObjectList lclickable; @@ -107,11 +107,9 @@ private: public: void handleElementActivate(CIntObject * elem, ui16 activityFlag); void handleElementDeActivate(CIntObject * elem, ui16 activityFlag); - public: //objs to blit std::vector> objsToBlit; - /// returns current position of mouse cursor, relative to vcmi window const Point & getCursorPosition() const; diff --git a/client/gui/CIntObject.h b/client/gui/CIntObject.h index ff26c96d4..6ecd276bd 100644 --- a/client/gui/CIntObject.h +++ b/client/gui/CIntObject.h @@ -17,7 +17,7 @@ struct SDL_Surface; class CGuiHandler; class CPicture; -typedef int32_t SDL_Keycode; +using SDL_Keycode = int32_t; using boost::logic::tribool; diff --git a/client/renderSDL/CTrueTypeFont.h b/client/renderSDL/CTrueTypeFont.h index 407d083f5..bd3fc4a53 100644 --- a/client/renderSDL/CTrueTypeFont.h +++ b/client/renderSDL/CTrueTypeFont.h @@ -17,7 +17,7 @@ VCMI_LIB_NAMESPACE_END class CBitmapFont; -typedef struct _TTF_Font TTF_Font; +using TTF_Font = struct _TTF_Font; class CTrueTypeFont : public IFont { diff --git a/client/renderSDL/SDL_Extensions.h b/client/renderSDL/SDL_Extensions.h index 417010ea1..528e52681 100644 --- a/client/renderSDL/SDL_Extensions.h +++ b/client/renderSDL/SDL_Extensions.h @@ -45,8 +45,8 @@ SDL_Color toSDL(const ColorRGBA & color); void setColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors); void setAlpha(SDL_Surface * bg, int value); -typedef void (*TColorPutter)(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B); -typedef void (*TColorPutterAlpha)(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B, const uint8_t & A); +using TColorPutter = void (*)(uint8_t *&, const uint8_t &, const uint8_t &, const uint8_t &); +using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &, const uint8_t &, const uint8_t &); void blitAt(SDL_Surface * src, int x, int y, SDL_Surface * dst); void blitAt(SDL_Surface * src, const Rect & pos, SDL_Surface * dst); @@ -57,23 +57,23 @@ typedef void (*TColorPutterAlpha)(uint8_t *&ptr, const uint8_t & R, const uint8_ void blitSurface(SDL_Surface * src, const Rect & srcRect, SDL_Surface * dst, const Point & dest); void blitSurface(SDL_Surface * src, SDL_Surface * dst, const Point & dest); - void fillSurface(SDL_Surface *dst, const SDL_Color & color); - void fillRect(SDL_Surface *dst, const Rect & dstrect, const SDL_Color & color); + void fillSurface(SDL_Surface * dst, const SDL_Color & color); + void fillRect(SDL_Surface * dst, const Rect & dstrect, const SDL_Color & color); - void updateRect(SDL_Surface *surface, const Rect & rect); + void updateRect(SDL_Surface * surface, const Rect & rect); - void putPixelWithoutRefresh(SDL_Surface *ekran, const int & x, const int & y, const uint8_t & R, const uint8_t & G, const uint8_t & B, uint8_t A = 255); + void putPixelWithoutRefresh(SDL_Surface * ekran, const int & x, const int & y, const uint8_t & R, const uint8_t & G, const uint8_t & B, uint8_t A = 255); void putPixelWithoutRefreshIfInSurf(SDL_Surface *ekran, const int & x, const int & y, const uint8_t & R, const uint8_t & G, const uint8_t & B, uint8_t A = 255); SDL_Surface * verticalFlip(SDL_Surface * toRot); //vertical flip SDL_Surface * horizontalFlip(SDL_Surface * toRot); //horizontal flip - uint32_t getPixel(SDL_Surface *surface, const int & x, const int & y, bool colorByte = false); + uint32_t getPixel(SDL_Surface * surface, const int & x, const int & y, bool colorByte = false); bool isTransparent(SDL_Surface * srf, int x, int y); //checks if surface is transparent at given position - bool isTransparent(SDL_Surface * srf, const Point & position); //checks if surface is transparent at given position + bool isTransparent(SDL_Surface * srf, const Point & position); //checks if surface is transparent at given position - uint8_t *getPxPtr(const SDL_Surface * const &srf, const int x, const int y); - TColorPutter getPutterFor(SDL_Surface * const &dest, int incrementing); //incrementing: -1, 0, 1 - TColorPutterAlpha getPutterAlphaFor(SDL_Surface * const &dest, int incrementing); //incrementing: -1, 0, 1 + uint8_t * getPxPtr(const SDL_Surface * const & srf, const int x, const int y); + TColorPutter getPutterFor(SDL_Surface * const & dest, int incrementing); //incrementing: -1, 0, 1 + TColorPutterAlpha getPutterAlphaFor(SDL_Surface * const & dest, int incrementing); //incrementing: -1, 0, 1 template int blit8bppAlphaTo24bppT(const SDL_Surface * src, const Rect & srcRect, SDL_Surface * dst, const Point & dstPoint); //blits 8 bpp surface with alpha channel to 24 bpp surface @@ -84,8 +84,8 @@ typedef void (*TColorPutterAlpha)(uint8_t *&ptr, const uint8_t & R, const uint8_ void drawLine(SDL_Surface * sur, const Point & from, const Point & dest, const SDL_Color & color1, const SDL_Color & color2); void drawLineDashed(SDL_Surface * sur, const Point & from, const Point & dest, const SDL_Color & color); - void drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const SDL_Color &color, int depth = 1); - void drawBorder(SDL_Surface * sur, const Rect &r, const SDL_Color &color, int depth = 1); + void drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const SDL_Color & color, int depth = 1); + void drawBorder(SDL_Surface * sur, const Rect & r, const SDL_Color & color, int depth = 1); void setPlayerColor(SDL_Surface * sur, PlayerColor player); //sets correct color of flags; -1 for neutral SDL_Surface * newSurface(int w, int h, SDL_Surface * mod); //creates new surface, with flags/format same as in surface given @@ -97,18 +97,18 @@ typedef void (*TColorPutterAlpha)(uint8_t *&ptr, const uint8_t & R, const uint8_ //scale surface to required size. //nearest neighbour algorithm - SDL_Surface * scaleSurfaceFast(SDL_Surface *surf, int width, int height); + SDL_Surface * scaleSurfaceFast(SDL_Surface * surf, int width, int height); // bilinear filtering. Uses fallback to scaleSurfaceFast in case of indexed surfaces - SDL_Surface * scaleSurface(SDL_Surface *surf, int width, int height); + SDL_Surface * scaleSurface(SDL_Surface * surf, int width, int height); template - void convertToGrayscaleBpp( SDL_Surface * surf, const Rect & rect ); + void convertToGrayscaleBpp(SDL_Surface * surf, const Rect & rect); void convertToGrayscale(SDL_Surface * surf, const Rect & rect); - bool isResolutionSupported(const std::vector & resolutions, const Point toTest ); + bool isResolutionSupported(const std::vector & resolutions, const Point toTest); std::vector getSupportedResolutions(); - std::vector getSupportedResolutions( int displayIndex); + std::vector getSupportedResolutions(int displayIndex); void setColorKey(SDL_Surface * surface, SDL_Color color); @@ -118,13 +118,13 @@ typedef void (*TColorPutterAlpha)(uint8_t *&ptr, const uint8_t & R, const uint8_ void setDefaultColorKeyPresize(SDL_Surface * surface); /// helper that will safely set and un-set ClipRect for SDL_Surface - class CClipRectGuard : boost::noncopyable + class CClipRectGuard: boost::noncopyable { SDL_Surface * surf; Rect oldRect; + public: - CClipRectGuard(SDL_Surface * surface, const Rect & rect): - surf(surface) + CClipRectGuard(SDL_Surface * surface, const Rect & rect): surf(surface) { CSDL_Ext::getClipRect(surf, oldRect); CSDL_Ext::setClipRect(surf, rect); diff --git a/client/renderSDL/SDL_PixelAccess.h b/client/renderSDL/SDL_PixelAccess.h index f61946ba9..ddd91a663 100644 --- a/client/renderSDL/SDL_PixelAccess.h +++ b/client/renderSDL/SDL_PixelAccess.h @@ -47,7 +47,7 @@ namespace Channels static void STRONG_INLINE set(uint8_t *ptr, uint8_t value) { - uint16_t * const pixel = (uint16_t*)ptr; + auto * const pixel = (uint16_t *)ptr; uint8_t subpx = value >> (8 - bits); *pixel = (*pixel & ~mask) | ((subpx << shift) & mask ); } @@ -226,7 +226,7 @@ STRONG_INLINE void ColorPutter<2, incrementPtr>::PutColor(uint8_t *&ptr, const u if(incrementPtr == -1) ptr -= 2; - uint16_t * const px = (uint16_t*)ptr; + auto * const px = (uint16_t *)ptr; *px = (B>>3) + ((G>>2) << 5) + ((R>>3) << 11); //drop least significant bits of 24 bpp encoded color if(incrementPtr == 1) diff --git a/client/widgets/ObjectLists.h b/client/widgets/ObjectLists.h index 9930f5fe7..f60e4cc3b 100644 --- a/client/widgets/ObjectLists.h +++ b/client/widgets/ObjectLists.h @@ -25,7 +25,7 @@ class CAnimation; class CObjectList : public CIntObject { public: - typedef std::function(size_t)> CreateFunc; + using CreateFunc = std::function(size_t)>; private: CreateFunc createObject; diff --git a/client/windows/InfoWindows.h b/client/windows/InfoWindows.h index a179c26df..0bc3bc584 100644 --- a/client/windows/InfoWindows.h +++ b/client/windows/InfoWindows.h @@ -45,8 +45,8 @@ public: class CInfoWindow : public CSimpleWindow { public: - typedef std::vector > > TButtonsInfo; - typedef std::vector> TCompsInfo; + using TButtonsInfo = std::vector>>; + using TCompsInfo = std::vector>; QueryID ID; //for identification std::shared_ptr text; std::vector> buttons; diff --git a/include/vcmi/spells/Magic.h b/include/vcmi/spells/Magic.h index af69982f2..20a21541e 100644 --- a/include/vcmi/spells/Magic.h +++ b/include/vcmi/spells/Magic.h @@ -52,7 +52,7 @@ enum class AimType class DLL_LINKAGE Problem { public: - typedef int Severity; + using Severity = int; enum ESeverity { diff --git a/include/vstd/RNG.h b/include/vstd/RNG.h index 9a8653141..53c323bcb 100644 --- a/include/vstd/RNG.h +++ b/include/vstd/RNG.h @@ -15,8 +15,8 @@ VCMI_LIB_NAMESPACE_BEGIN namespace vstd { -typedef std::function TRandI64; -typedef std::function TRand; +using TRandI64 = std::function; +using TRand = std::function; class DLL_LINKAGE RNG { diff --git a/lib/BattleFieldHandler.h b/lib/BattleFieldHandler.h index ecc8ef227..1e8a17d10 100644 --- a/lib/BattleFieldHandler.h +++ b/lib/BattleFieldHandler.h @@ -36,8 +36,12 @@ public: { } - BattleFieldInfo(BattleField battlefield, std::string identifier) - :bonuses(), isSpecial(false), battlefield(battlefield), identifier(identifier), graphics(), icon(), iconIndex(battlefield.getNum()), impassableHexes(), name(identifier) + BattleFieldInfo(BattleField battlefield, std::string identifier): + isSpecial(false), + battlefield(battlefield), + identifier(identifier), + iconIndex(battlefield.getNum()), + name(identifier) { } diff --git a/lib/CBonusTypeHandler.h b/lib/CBonusTypeHandler.h index 3cb68dfb3..acfe49c76 100644 --- a/lib/CBonusTypeHandler.h +++ b/lib/CBonusTypeHandler.h @@ -19,7 +19,7 @@ VCMI_LIB_NAMESPACE_BEGIN class JsonNode; -typedef Bonus::BonusType BonusTypeID; +using BonusTypeID = Bonus::BonusType; class DLL_LINKAGE CBonusType { diff --git a/lib/CConfigHandler.h b/lib/CConfigHandler.h index a4676af6c..2c85bef57 100644 --- a/lib/CConfigHandler.h +++ b/lib/CConfigHandler.h @@ -171,7 +171,7 @@ namespace config GUIOptions *current; // pointer to current gui options public: - typedef std::map, GUIOptions > GuiOptionsMap; + using GuiOptionsMap = std::map, GUIOptions>; GuiOptionsMap guiOptions; void init(); CConfigHandler(); diff --git a/lib/CCreatureSet.h b/lib/CCreatureSet.h index 2530e8b5c..c1011b85f 100644 --- a/lib/CCreatureSet.h +++ b/lib/CCreatureSet.h @@ -163,22 +163,18 @@ public: } }; -typedef std::map TSlots; -typedef std::map> TSimpleSlots; +using TSlots = std::map; +using TSimpleSlots = std::map>; -typedef std::pair TPairCreatureSlot; -typedef std::map TMapCreatureSlot; +using TPairCreatureSlot = std::pair; +using TMapCreatureSlot = std::map; struct DLL_LINKAGE CreatureSlotComparer { bool operator()(const TPairCreatureSlot & lhs, const TPairCreatureSlot & rhs); }; -typedef std::priority_queue< - TPairCreatureSlot, - std::vector, - CreatureSlotComparer -> TCreatureQueue; +using TCreatureQueue = std::priority_queue, CreatureSlotComparer>; class IArmyDescriptor { diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index e1ae5b740..5db6ff54f 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -198,10 +198,9 @@ public: if(!h.saving) { - for(auto i = 0; i < secSkillProbability.size(); i++) - if(secSkillProbability[i] < 0) - secSkillProbability[i] = 0; - } + for(int & i : secSkillProbability) + vstd::amax(i, 0); + } } EAlignment getAlignment() const; }; diff --git a/lib/CModHandler.h b/lib/CModHandler.h index e1a8bdec2..04eb645cb 100644 --- a/lib/CModHandler.h +++ b/lib/CModHandler.h @@ -310,7 +310,7 @@ public: missingMods(std::move(_missingMods)) { std::ostringstream _ss; - for(auto & m : missingMods) + for(const auto & m : missingMods) _ss << m.first << ' ' << m.second << std::endl; message = _ss.str(); } diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index aef8dc2ee..b8c55b157 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -42,7 +42,7 @@ struct DLL_LINKAGE NodeComparer struct DLL_LINKAGE CGPathNode { - typedef EPathfindingLayer ELayer; + using ELayer = EPathfindingLayer; enum ENodeAction : ui8 { @@ -151,10 +151,7 @@ struct DLL_LINKAGE CGPathNode return turns < 255; } - typedef boost::heap::fibonacci_heap< - CGPathNode *, - boost::heap::compare> - > TFibHeap; + using TFibHeap = boost::heap::fibonacci_heap>>; TFibHeap::handle_type pqHandle; TFibHeap* pq; @@ -173,7 +170,7 @@ struct DLL_LINKAGE CGPath struct DLL_LINKAGE CPathsInfo { - typedef EPathfindingLayer ELayer; + using ELayer = EPathfindingLayer; const CGHeroInstance * hero; int3 hpos; @@ -484,7 +481,7 @@ public: private: CGameState * gamestate; - typedef EPathfindingLayer ELayer; + using ELayer = EPathfindingLayer; std::shared_ptr config; diff --git a/lib/CRandomGenerator.h b/lib/CRandomGenerator.h index 0b14c65ee..5b76adcb1 100644 --- a/lib/CRandomGenerator.h +++ b/lib/CRandomGenerator.h @@ -14,11 +14,11 @@ VCMI_LIB_NAMESPACE_BEGIN -typedef std::mt19937 TGenerator; -typedef std::uniform_int_distribution TIntDist; -typedef std::uniform_int_distribution TInt64Dist; -typedef std::uniform_real_distribution TRealDist; -typedef std::function TRandI; +using TGenerator = std::mt19937; +using TIntDist = std::uniform_int_distribution; +using TInt64Dist = std::uniform_int_distribution; +using TRealDist = std::uniform_real_distribution; +using TRandI = std::function; /// The random generator randomly generates integers and real numbers("doubles") between /// a given range. This is a header only class and mainly a wrapper for diff --git a/lib/CStack.h b/lib/CStack.h index 920bf2d47..753a8a855 100644 --- a/lib/CStack.h +++ b/lib/CStack.h @@ -121,7 +121,7 @@ public: if(extSlot == SlotID::COMMANDER_SLOT_PLACEHOLDER) { - auto hero = dynamic_cast(army); + const auto * hero = dynamic_cast(army); assert(hero); base = hero->commander; } diff --git a/lib/CThreadHelper.h b/lib/CThreadHelper.h index a4eaf8f8b..aaa3e67f3 100644 --- a/lib/CThreadHelper.h +++ b/lib/CThreadHelper.h @@ -17,7 +17,7 @@ VCMI_LIB_NAMESPACE_BEGIN class DLL_LINKAGE CThreadHelper { public: - typedef std::function Task; + using Task = std::function; CThreadHelper(std::vector > *Tasks, int Threads); void run(); private: diff --git a/lib/CTownHandler.h b/lib/CTownHandler.h index 7bc7a6d43..407260154 100644 --- a/lib/CTownHandler.h +++ b/lib/CTownHandler.h @@ -43,7 +43,7 @@ class DLL_LINKAGE CBuilding std::string identifier; public: - typedef LogicalExpression TRequired; + using TRequired = LogicalExpression; CTown * town; // town this building belongs to TResources resources; diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 199311e88..ca36e747a 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -1325,11 +1325,11 @@ enum class EHealPower : ui8 }; // Typedef declarations -typedef si64 TExpType; -typedef si32 TBonusSubtype; -typedef si32 TQuantity; +using TExpType = si64; +using TBonusSubtype = si32; +using TQuantity = si32; -typedef int TRmgTemplateZoneId; +using TRmgTemplateZoneId = int; #undef ID_LIKE_CLASS_COMMON #undef ID_LIKE_OPERATORS diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 43ba4f4e5..cd695d7d0 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -25,14 +25,14 @@ class IPropagator; class IUpdater; class BonusList; -typedef std::shared_ptr TBonusListPtr; -typedef std::shared_ptr TConstBonusListPtr; -typedef std::shared_ptr TLimiterPtr; -typedef std::shared_ptr TPropagatorPtr; -typedef std::shared_ptr TUpdaterPtr; -typedef std::set TNodes; -typedef std::set TCNodes; -typedef std::vector TNodesVector; +using TBonusListPtr = std::shared_ptr; +using TConstBonusListPtr = std::shared_ptr; +using TLimiterPtr = std::shared_ptr; +using TPropagatorPtr = std::shared_ptr; +using TUpdaterPtr = std::shared_ptr; +using TNodes = std::set; +using TCNodes = std::set; +using TNodesVector = std::vector; class CSelector : std::function { @@ -548,7 +548,7 @@ struct DLL_LINKAGE BonusParams { bool isConverted; Bonus::BonusType type = Bonus::NONE; TBonusSubtype subtype = -1; - std::string subtypeStr = ""; + std::string subtypeStr; bool subtypeRelevant = false; Bonus::ValueType valueType = Bonus::BASE_NUMBER; bool valueTypeRelevant = false; @@ -569,7 +569,7 @@ private: class DLL_LINKAGE BonusList { public: - typedef std::vector> TInternalContainer; + using TInternalContainer = std::vector>; private: TInternalContainer bonuses; @@ -577,11 +577,11 @@ private: void changed() const; public: - typedef TInternalContainer::const_reference const_reference; - typedef TInternalContainer::value_type value_type; + using const_reference = TInternalContainer::const_reference; + using value_type = TInternalContainer::value_type; - typedef TInternalContainer::const_iterator const_iterator; - typedef TInternalContainer::iterator iterator; + using const_iterator = TInternalContainer::const_iterator; + using iterator = TInternalContainer::iterator; BonusList(bool BelongsToTree = false); BonusList(const BonusList &bonusList); @@ -628,9 +628,8 @@ public: void remove_if(Predicate pred) { BonusList newList; - for (ui32 i = 0; i < bonuses.size(); i++) + for(const auto & b : bonuses) { - auto b = bonuses[i]; if (!pred(b.get())) newList.push_back(b); } diff --git a/lib/IHandlerBase.h b/lib/IHandlerBase.h index 5d4008a6f..f513f3593 100644 --- a/lib/IHandlerBase.h +++ b/lib/IHandlerBase.h @@ -97,7 +97,7 @@ public: objects.push_back(object); - for(auto type_name : getTypeNames()) + for(const auto & type_name : getTypeNames()) registerObject(scope, type_name, name, object->getIndex()); } @@ -108,7 +108,7 @@ public: assert(objects[index] == nullptr); // ensure that this id was not loaded before objects[index] = object; - for(auto type_name : getTypeNames()) + for(const auto & type_name : getTypeNames()) registerObject(scope, type_name, name, object->getIndex()); } diff --git a/lib/JsonDetail.h b/lib/JsonDetail.h index 6ea5913bb..b817b8961 100644 --- a/lib/JsonDetail.h +++ b/lib/JsonDetail.h @@ -116,10 +116,10 @@ namespace Validation std::string makeErrorMessage(const std::string &message); }; - typedef std::function TFormatValidator; - typedef std::unordered_map TFormatMap; - typedef std::function TFieldValidator; - typedef std::unordered_map TValidatorMap; + using TFormatValidator = std::function; + using TFormatMap = std::unordered_map; + using TFieldValidator = std::function; + using TValidatorMap = std::unordered_map; /// map of known fields in schema const TValidatorMap & getKnownFieldsFor(JsonNode::JsonType type); diff --git a/lib/JsonNode.h b/lib/JsonNode.h index ba2275ece..d15ea509b 100644 --- a/lib/JsonNode.h +++ b/lib/JsonNode.h @@ -13,8 +13,8 @@ VCMI_LIB_NAMESPACE_BEGIN class JsonNode; -typedef std::map JsonMap; -typedef std::vector JsonVector; +using JsonMap = std::map; +using JsonVector = std::vector; struct Bonus; class CSelector; diff --git a/lib/LogicalExpression.h b/lib/LogicalExpression.h index a94108e3c..6bc3f36fa 100644 --- a/lib/LogicalExpression.h +++ b/lib/LogicalExpression.h @@ -30,19 +30,14 @@ namespace LogicalExpressionDetail }; template class Element; - typedef Element OperatorAny; - typedef Element OperatorAll; - typedef Element OperatorNone; + using OperatorAny = Element; + using OperatorAll = Element; + using OperatorNone = Element; - typedef ContainedClass Value; + using Value = ContainedClass; /// Variant that contains all possible elements from logical expression - typedef std::variant< - OperatorAll, - OperatorAny, - OperatorNone, - Value - > Variant; + using Variant = std::variant; /// Variant element, contains list of expressions to which operation "tag" should be applied template @@ -73,7 +68,7 @@ namespace LogicalExpressionDetail template class TestVisitor { - typedef ExpressionBase Base; + using Base = ExpressionBase; std::function classTest; @@ -119,7 +114,7 @@ namespace LogicalExpressionDetail template class PossibilityVisitor { - typedef ExpressionBase Base; + using Base = ExpressionBase; protected: std::function satisfiabilityTest; @@ -167,7 +162,7 @@ namespace LogicalExpressionDetail template class SatisfiabilityVisitor : public PossibilityVisitor { - typedef ExpressionBase Base; + using Base = ExpressionBase; public: SatisfiabilityVisitor(std::function satisfiabilityTest, @@ -202,7 +197,7 @@ namespace LogicalExpressionDetail template class FalsifiabilityVisitor : public PossibilityVisitor { - typedef ExpressionBase Base; + using Base = ExpressionBase; public: FalsifiabilityVisitor(std::function satisfiabilityTest, @@ -238,8 +233,8 @@ namespace LogicalExpressionDetail template class CandidatesVisitor { - typedef ExpressionBase Base; - typedef std::vector TValueList; + using Base = ExpressionBase; + using TValueList = std::vector; TestVisitor classTest; @@ -288,7 +283,7 @@ namespace LogicalExpressionDetail template class ForEachVisitor { - typedef ExpressionBase Base; + using Base = ExpressionBase; std::function visitor; @@ -315,7 +310,7 @@ namespace LogicalExpressionDetail template class MinimizingVisitor { - typedef ExpressionBase Base; + using Base = ExpressionBase; public: typename Base::Variant operator()(const typename Base::Value & element) const @@ -360,7 +355,7 @@ namespace LogicalExpressionDetail template class Reader { - typedef ExpressionBase Base; + using Base = ExpressionBase; std::function classParser; @@ -400,7 +395,7 @@ namespace LogicalExpressionDetail template class Writer { - typedef ExpressionBase Base; + using Base = ExpressionBase; std::function classPrinter; @@ -445,7 +440,7 @@ namespace LogicalExpressionDetail template class Printer { - typedef ExpressionBase Base; + using Base = ExpressionBase; std::function classPrinter; std::unique_ptr> statusTest; @@ -510,30 +505,27 @@ namespace LogicalExpressionDetail template class LogicalExpression { - typedef LogicalExpressionDetail::ExpressionBase Base; + using Base = LogicalExpressionDetail::ExpressionBase; + public: /// Type of values used in expressions, same as ContainedClass - typedef typename Base::Value Value; + using Value = typename Base::Value; /// Operators for use in expressions, all include vectors with operands - typedef typename Base::OperatorAny OperatorAny; - typedef typename Base::OperatorAll OperatorAll; - typedef typename Base::OperatorNone OperatorNone; + using OperatorAny = typename Base::OperatorAny; + using OperatorAll = typename Base::OperatorAll; + using OperatorNone = typename Base::OperatorNone; /// one expression entry - typedef typename Base::Variant Variant; + using Variant = typename Base::Variant; private: Variant data; public: /// Base constructor - LogicalExpression() - {} + LogicalExpression() = default; /// Constructor from variant or (implicitly) from Operator* types - LogicalExpression(const Variant & data): - data(data) - { - } + LogicalExpression(const Variant & data): data(data) {} /// Constructor that receives JsonNode as input and function that can parse Value instances LogicalExpression(const JsonNode & input, std::function parser) diff --git a/lib/ResourceSet.h b/lib/ResourceSet.h index 611b24e8d..6d879f715 100644 --- a/lib/ResourceSet.h +++ b/lib/ResourceSet.h @@ -161,8 +161,8 @@ public: ResourceSet & operator=(const TResource &rhs) { - for(int i = 0; i < container.size(); i++) - container.at(i) = rhs; + for(int & i : container) + i = rhs; return *this; } diff --git a/lib/StartInfo.h b/lib/StartInfo.h index 96acfa2b8..052f5ccf5 100644 --- a/lib/StartInfo.h +++ b/lib/StartInfo.h @@ -76,7 +76,7 @@ struct DLL_LINKAGE StartInfo EMode mode; ui8 difficulty; //0=easy; 4=impossible - typedef std::map TPlayerInfos; + using TPlayerInfos = std::map; TPlayerInfos playerInfos; //color indexed ui32 seedToBeUsed; //0 if not sure (client requests server to decide, will be send in reply pack) @@ -84,7 +84,7 @@ struct DLL_LINKAGE StartInfo ui32 mapfileChecksum; //0 if not relevant ui8 turnTime; //in minutes, 0=unlimited std::string mapname; // empty for random map, otherwise name of the map or savegame - bool createRandomMap() const { return mapGenOptions.get() != nullptr; } + bool createRandomMap() const { return mapGenOptions != nullptr; } std::shared_ptr mapGenOptions; std::shared_ptr campState; diff --git a/lib/UnlockGuard.h b/lib/UnlockGuard.h index 51ec8541d..39fb8ebb8 100644 --- a/lib/UnlockGuard.h +++ b/lib/UnlockGuard.h @@ -114,7 +114,7 @@ namespace vstd : unlock_guard >(); } - typedef unlock_guard > unlock_shared_guard; + using unlock_shared_guard = unlock_guard>; } VCMI_LIB_NAMESPACE_END diff --git a/lib/filesystem/Filesystem.h b/lib/filesystem/Filesystem.h index cdbff4f27..a6b894ab0 100644 --- a/lib/filesystem/Filesystem.h +++ b/lib/filesystem/Filesystem.h @@ -21,8 +21,8 @@ class JsonNode; /// Helper class that allows generation of a ISimpleResourceLoader entry out of Json config(s) class DLL_LINKAGE CFilesystemGenerator { - typedef std::function TLoadFunctor; - typedef std::map TLoadFunctorMap; + using TLoadFunctor = std::function; + using TLoadFunctorMap = std::map; CFilesystemList * filesystem; std::string prefix; diff --git a/lib/mapObjects/CObjectClassesHandler.h b/lib/mapObjects/CObjectClassesHandler.h index 910ee3f16..36e234108 100644 --- a/lib/mapObjects/CObjectClassesHandler.h +++ b/lib/mapObjects/CObjectClassesHandler.h @@ -241,7 +241,7 @@ public: } }; -typedef std::shared_ptr TObjectTypeHandler; +using TObjectTypeHandler = std::shared_ptr; /// Class responsible for creation of adventure map objects of specific type class DLL_LINKAGE ObjectClass @@ -283,7 +283,7 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase std::map > handlerConstructors; /// container with H3 templates, used only during loading, no need to serialize it - typedef std::multimap, std::shared_ptr> TTemplatesContainer; + using TTemplatesContainer = std::multimap, std::shared_ptr>; TTemplatesContainer legacyTemplates; TObjectTypeHandler loadSubObjectFromJson(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj, size_t index); diff --git a/lib/mapObjects/CommonConstructors.h b/lib/mapObjects/CommonConstructors.h index de0a25c82..1513d293f 100644 --- a/lib/mapObjects/CommonConstructors.h +++ b/lib/mapObjects/CommonConstructors.h @@ -197,7 +197,7 @@ struct BankConfig } }; -typedef std::vector> TPossibleGuards; +using TPossibleGuards = std::vector>; template struct DLL_LINKAGE PossibleReward diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 158e69a50..493727567 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -189,11 +189,9 @@ protected: class DLL_LINKAGE CGArtifact : public CArmedInstance { public: - CArtifactInstance *storedArtifact; + CArtifactInstance * storedArtifact = nullptr; std::string message; - CGArtifact() : CArmedInstance() {storedArtifact = nullptr;}; - void onHeroVisit(const CGHeroInstance * h) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 2c5aa3596..56dff77e3 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -160,7 +160,7 @@ struct DLL_LINKAGE EventCondition } }; -typedef LogicalExpression EventExpression; +using EventExpression = LogicalExpression; struct DLL_LINKAGE EventEffect { diff --git a/lib/mapping/CMapEditManager.h b/lib/mapping/CMapEditManager.h index 04a52b39a..b072d1828 100644 --- a/lib/mapping/CMapEditManager.h +++ b/lib/mapping/CMapEditManager.h @@ -46,7 +46,7 @@ public: void setUndoCallback(std::function functor); private: - typedef std::list > TStack; + using TStack = std::list>; void doOperation(TStack & fromStack, TStack & toStack, bool doUndo); const CMapOperation * peek(const TStack & stack) const; diff --git a/lib/mapping/MapEditUtils.h b/lib/mapping/MapEditUtils.h index dd45b95e7..474c2d946 100644 --- a/lib/mapping/MapEditUtils.h +++ b/lib/mapping/MapEditUtils.h @@ -212,7 +212,7 @@ struct DLL_LINKAGE TerrainViewPattern class DLL_LINKAGE CTerrainViewPatternConfig : public boost::noncopyable { public: - typedef std::vector TVPVector; + using TVPVector = std::vector; CTerrainViewPatternConfig(); diff --git a/lib/rmg/CMapGenerator.h b/lib/rmg/CMapGenerator.h index 162aeb406..39c66204d 100644 --- a/lib/rmg/CMapGenerator.h +++ b/lib/rmg/CMapGenerator.h @@ -26,7 +26,7 @@ class RmgMap; class CMap; class Zone; -typedef std::vector JsonVector; +using JsonVector = std::vector; /// The map generator creates a map randomly. class DLL_LINKAGE CMapGenerator: public Load::Progress diff --git a/lib/rmg/CZonePlacer.h b/lib/rmg/CZonePlacer.h index 34e24f6f9..4914739ec 100644 --- a/lib/rmg/CZonePlacer.h +++ b/lib/rmg/CZonePlacer.h @@ -22,10 +22,10 @@ class CRandomGenerator; class RmgMap; class Zone; -typedef std::vector>> TZoneVector; -typedef std::map> TZoneMap; -typedef std::map, float3> TForceVector; -typedef std::map, float> TDistanceVector; +using TZoneVector = std::vector>>; +using TZoneMap = std::map>; +using TForceVector = std::map, float3>; +using TDistanceVector = std::map, float>; class CZonePlacer { diff --git a/lib/rmg/ConnectionsPlacer.h b/lib/rmg/ConnectionsPlacer.h index ac577b687..80a8780d7 100644 --- a/lib/rmg/ConnectionsPlacer.h +++ b/lib/rmg/ConnectionsPlacer.h @@ -30,7 +30,7 @@ public: protected: void collectNeighbourZones(); - + protected: std::vector dConnections, dCompleted; std::map dNeighbourZones; diff --git a/lib/rmg/ObjectManager.h b/lib/rmg/ObjectManager.h index d3ef132dc..f8d3775dc 100644 --- a/lib/rmg/ObjectManager.h +++ b/lib/rmg/ObjectManager.h @@ -20,7 +20,7 @@ class CGObjectInstance; class ObjectTemplate; class CGCreature; -typedef std::pair TDistance; +using TDistance = std::pair; struct DistanceMaximizeFunctor { bool operator()(const TDistance & lhs, const TDistance & rhs) const @@ -38,11 +38,10 @@ public: WEIGHT = 0x00000001, DISTANCE = 0x00000010 }; - + public: MODIFICATOR(ObjectManager); - void process() override; void init() override; diff --git a/lib/rmg/ObstaclePlacer.h b/lib/rmg/ObstaclePlacer.h index b18a1bae0..8776abfb1 100644 --- a/lib/rmg/ObstaclePlacer.h +++ b/lib/rmg/ObstaclePlacer.h @@ -43,9 +43,9 @@ public: protected: int getWeightedObjects(const int3 & tile, const CMap * map, CRandomGenerator & rand, std::list & allObjects, std::vector> & weightedObjects); - typedef std::vector> ObstacleVector; + using ObstacleVector = std::vector>; std::map obstaclesBySize; - typedef std::pair ObstaclePair; + using ObstaclePair = std::pair; std::vector possibleObstacles; }; diff --git a/lib/rmg/RmgMap.h b/lib/rmg/RmgMap.h index 1144d0d22..8c52ac0a5 100644 --- a/lib/rmg/RmgMap.h +++ b/lib/rmg/RmgMap.h @@ -72,7 +72,7 @@ public: private: void assertOnMap(const int3 &tile) const; //throws - + private: Zones zones; std::map zonesPerFaction; diff --git a/lib/rmg/RoadPlacer.h b/lib/rmg/RoadPlacer.h index 092f3499f..1f4651f87 100644 --- a/lib/rmg/RoadPlacer.h +++ b/lib/rmg/RoadPlacer.h @@ -31,7 +31,7 @@ public: protected: bool createRoad(const int3 & dst); void drawRoads(bool secondary = false); //actually updates tiles - + protected: rmg::Tileset roadNodes; //tiles to be connected with roads rmg::Area roads; //all tiles with roads diff --git a/lib/rmg/TownPlacer.h b/lib/rmg/TownPlacer.h index cdcf5eeef..374df621d 100644 --- a/lib/rmg/TownPlacer.h +++ b/lib/rmg/TownPlacer.h @@ -32,9 +32,8 @@ protected: void placeTowns(ObjectManager & manager); bool placeMines(ObjectManager & manager); int3 placeMainTown(ObjectManager & manager, CGTownInstance & town); - + protected: - int totalTowns = 0; }; diff --git a/lib/rmg/TreasurePlacer.h b/lib/rmg/TreasurePlacer.h index 03968e308..cbaad3a9f 100644 --- a/lib/rmg/TreasurePlacer.h +++ b/lib/rmg/TreasurePlacer.h @@ -56,9 +56,8 @@ protected: ObjectInfo * getRandomObject(ui32 desiredValue, ui32 currentValue, ui32 maxValue, bool allowLargeObjects); std::vector prepareTreasurePile(const CTreasureInfo & treasureInfo); rmg::Object constructTreasurePile(const std::vector & treasureInfos, bool densePlacement = false); - - -protected: + +protected: std::vector possibleObjects; int minGuardedValue = 0; diff --git a/lib/rmg/WaterAdopter.h b/lib/rmg/WaterAdopter.h index ef47d3fa8..2f09241da 100644 --- a/lib/rmg/WaterAdopter.h +++ b/lib/rmg/WaterAdopter.h @@ -29,7 +29,7 @@ public: protected: void createWater(EWaterContent::EWaterContent waterContent); - + protected: rmg::Area noWaterArea, waterArea; TRmgTemplateZoneId waterZoneId; diff --git a/lib/rmg/WaterProxy.h b/lib/rmg/WaterProxy.h index de50f7354..7fd1a3361 100644 --- a/lib/rmg/WaterProxy.h +++ b/lib/rmg/WaterProxy.h @@ -49,8 +49,8 @@ protected: bool placeShipyard(Zone & land, const Lake & lake, si32 guard, RouteInfo & info); bool placeBoat(Zone & land, const Lake & lake, RouteInfo & info); - -protected: + +protected: std::vector lakes; //disconnected parts of zone. Used to work with water zones std::map lakeMap; //map tile on lakeId which is position of lake in lakes array +1 }; diff --git a/lib/serializer/BinaryDeserializer.h b/lib/serializer/BinaryDeserializer.h index c8a2588f8..c11e9b764 100644 --- a/lib/serializer/BinaryDeserializer.h +++ b/lib/serializer/BinaryDeserializer.h @@ -98,7 +98,7 @@ class DLL_LINKAGE BinaryDeserializer : public CLoaderBase } else { - auto hero = dynamic_cast(armedObj); + auto * hero = dynamic_cast(armedObj); assert(hero); assert(hero->commander); data = hero->commander; @@ -158,7 +158,7 @@ class DLL_LINKAGE BinaryDeserializer : public CLoaderBase public: const std::type_info * loadPtr(CLoaderBase &ar, void *data, ui32 pid) const override //data is pointer to the ACTUAL POINTER { - BinaryDeserializer &s = static_cast(ar); + auto & s = static_cast(ar); T *&ptr = *static_cast(data); //create new object under pointer @@ -217,7 +217,7 @@ public: assert( fileVersion != 0 ); ////that const cast is evil because it allows to implicitly overwrite const objects when deserializing typedef typename std::remove_const::type nonConstT; - nonConstT &hlp = const_cast(data); + auto & hlp = const_cast(data); hlp.serialize(*this,fileVersion); } template < typename T, typename std::enable_if < std::is_array::value, int >::type = 0 > @@ -301,7 +301,7 @@ public: if(smartPointerSerialization) { load( pid ); //get the id - std::map::iterator i = loadedPointers.find(pid); //lookup + auto i = loadedPointers.find(pid); //lookup if(i != loadedPointers.end()) { @@ -327,7 +327,7 @@ public: } else { - auto app = applier.getApplier(tid); + auto * app = applier.getApplier(tid); if(app == nullptr) { logGlobal->error("load %d %d - no loader exists", tid, pid); diff --git a/lib/serializer/BinarySerializer.h b/lib/serializer/BinarySerializer.h index df0da5ed5..d6f48a0dc 100644 --- a/lib/serializer/BinarySerializer.h +++ b/lib/serializer/BinarySerializer.h @@ -100,7 +100,7 @@ class DLL_LINKAGE BinarySerializer : public CSaverBase public: void savePtr(CSaverBase &ar, const void *data) const override { - BinarySerializer &s = static_cast(ar); + auto & s = static_cast(ar); const T *ptr = static_cast(data); //T is most derived known type, it's time to call actual serialize @@ -210,7 +210,7 @@ public: // We might have an object that has multiple inheritance and store it via the non-first base pointer. // Therefore, all pointers need to be normalized to the actual object address. auto actualPointer = typeList.castToMostDerived(data); - std::map::iterator i = savedPointers.find(actualPointer); + auto i = savedPointers.find(actualPointer); if(i != savedPointers.end()) { //this pointer has been already serialized - write only it's id @@ -275,28 +275,28 @@ public: template void save(const std::set &data) { - std::set &d = const_cast &>(data); + auto & d = const_cast &>(data); ui32 length = (ui32)d.size(); save(length); - for(typename std::set::iterator i=d.begin();i!=d.end();i++) + for(auto i = d.begin(); i != d.end(); i++) save(*i); } template void save(const std::unordered_set &data) { - std::unordered_set &d = const_cast &>(data); + auto & d = const_cast &>(data); ui32 length = (ui32)d.size(); *this & length; - for(typename std::unordered_set::iterator i=d.begin();i!=d.end();i++) + for(auto i = d.begin(); i != d.end(); i++) save(*i); } template void save(const std::list &data) { - std::list &d = const_cast &>(data); + auto & d = const_cast &>(data); ui32 length = (ui32)d.size(); *this & length; - for(typename std::list::iterator i=d.begin();i!=d.end();i++) + for(auto i = d.begin(); i != d.end(); i++) save(*i); } void save(const std::string &data) @@ -314,7 +314,7 @@ public: void save(const std::map &data) { *this & ui32(data.size()); - for(typename std::map::const_iterator i=data.begin();i!=data.end();i++) + for(auto i = data.begin(); i != data.end(); i++) { save(i->first); save(i->second); @@ -324,7 +324,7 @@ public: void save(const std::multimap &data) { *this & ui32(data.size()); - for(typename std::map::const_iterator i = data.begin(); i != data.end(); i++) + for(auto i = data.begin(); i != data.end(); i++) { save(i->first); save(i->second); diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index 2e0d83731..8f20822bd 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -76,7 +76,7 @@ class DLL_LINKAGE CSerializer vectors[&typeid(T)] = VectorizedObjectInfo(Vector, idRetriever); } - typedef std::map TTypeVecMap; + using TTypeVecMap = std::map; TTypeVecMap vectors; //entry must be a pointer to vector containing pointers to the objects of key type public: @@ -94,7 +94,7 @@ public: myType = &typeid(T); - TTypeVecMap::iterator i = vectors.find(myType); + auto i = vectors.find(myType); if(i == vectors.end()) return nullptr; else @@ -134,8 +134,8 @@ public: template struct is_serializeable { - typedef char (&Yes)[1]; - typedef char (&No)[2]; + using Yes = char (&)[1]; + using No = char (&)[2]; template static Yes test(U * data, S* arg1 = 0, @@ -149,42 +149,42 @@ struct is_serializeable template //metafunction returning CGObjectInstance if T is its derivate or T elsewise struct VectorizedTypeFor { - typedef typename + using type = typename //if - boost::mpl::eval_if, + boost::mpl::eval_if, boost::mpl::identity, //else if - boost::mpl::eval_if, + boost::mpl::eval_if, boost::mpl::identity, //else boost::mpl::identity - > >::type type; + >>::type; }; template struct VectorizedIDType { - typedef typename + using type = typename //if - boost::mpl::eval_if, + boost::mpl::eval_if, boost::mpl::identity, //else if - boost::mpl::eval_if, + boost::mpl::eval_if, boost::mpl::identity, //else if - boost::mpl::eval_if, + boost::mpl::eval_if, boost::mpl::identity, //else if - boost::mpl::eval_if, + boost::mpl::eval_if, boost::mpl::identity, //else if - boost::mpl::eval_if, + boost::mpl::eval_if, boost::mpl::identity, //else if - boost::mpl::eval_if, + boost::mpl::eval_if, boost::mpl::identity, //else boost::mpl::identity - > > > > > >::type type; + >>>>>>::type; }; /// Base class for deserializers diff --git a/lib/serializer/CTypeList.h b/lib/serializer/CTypeList.h index a4a9f8a6b..fc5b7c0ac 100644 --- a/lib/serializer/CTypeList.h +++ b/lib/serializer/CTypeList.h @@ -66,17 +66,18 @@ class DLL_LINKAGE CTypeList: public boost::noncopyable { //public: struct TypeDescriptor; - typedef std::shared_ptr TypeInfoPtr; - typedef std::weak_ptr WeakTypeInfoPtr; + using TypeInfoPtr = std::shared_ptr; + using WeakTypeInfoPtr = std::weak_ptr; struct TypeDescriptor { ui16 typeID; const char *name; std::vector children, parents; }; - typedef boost::shared_mutex TMutex; - typedef boost::unique_lock TUniqueLock; - typedef boost::shared_lock TSharedLock; + using TMutex = boost::shared_mutex; + using TUniqueLock = boost::unique_lock; + using TSharedLock = boost::shared_lock; + private: mutable TMutex mx; @@ -103,7 +104,7 @@ private: if(!casters.count(castingPair)) THROW_FORMAT("Cannot find caster for conversion %s -> %s which is needed to cast %s -> %s", from->name % to->name % fromArg->name() % toArg->name()); - auto &caster = casters.at(castingPair); + const auto & caster = casters.at(castingPair); ptr = (*caster.*CastingFunction)(ptr); //Why does unique_ptr not have operator->* ..? } @@ -150,7 +151,7 @@ public: template void * castToMostDerived(const TInput * inputPtr) const { - auto &baseType = typeid(typename std::remove_cv::type); + const auto & baseType = typeid(typename std::remove_cv::type); auto derivedType = getTypeInfo(inputPtr); if (strcmp(baseType.name(), derivedType->name()) == 0) @@ -166,7 +167,7 @@ public: template std::any castSharedToMostDerived(const std::shared_ptr inputPtr) const { - auto &baseType = typeid(typename std::remove_cv::type); + const auto & baseType = typeid(typename std::remove_cv::type); auto derivedType = getTypeInfo(inputPtr.get()); if (!strcmp(baseType.name(), derivedType->name())) diff --git a/lib/serializer/Connection.h b/lib/serializer/Connection.h index b1dfcf51f..518223246 100644 --- a/lib/serializer/Connection.h +++ b/lib/serializer/Connection.h @@ -14,8 +14,8 @@ #if BOOST_VERSION >= 107000 // Boost version >= 1.70 #include -typedef boost::asio::basic_stream_socket < boost::asio::ip::tcp > TSocket; -typedef boost::asio::basic_socket_acceptor < boost::asio::ip::tcp > TAcceptor; +using TSocket = boost::asio::basic_stream_socket; +using TAcceptor = boost::asio::basic_socket_acceptor; #else namespace boost { diff --git a/lib/serializer/JsonSerializeFormat.h b/lib/serializer/JsonSerializeFormat.h index f001f6c43..918918a3d 100644 --- a/lib/serializer/JsonSerializeFormat.h +++ b/lib/serializer/JsonSerializeFormat.h @@ -461,7 +461,7 @@ private: void doSerializeInternal(const std::string & fieldName, VType & value, const std::optional & defaultValue, Args... args) { const std::optional tempDefault = defaultValue ? std::optional(static_cast(defaultValue.value())) : std::nullopt; - IType temp = static_cast(value); + auto temp = static_cast(value); serializeInternal(fieldName, temp, tempDefault, args...); @@ -476,7 +476,7 @@ private: { if(value) { - IType temp = static_cast(value.value()); + auto temp = static_cast(value.value()); pushField(fieldName); serializeInternal(temp, args...); pop(); @@ -518,7 +518,7 @@ void JsonArraySerializer::syncSize(Container & c, JsonNode::JsonType type) template void JsonArraySerializer::serializeInt(const size_t index, T & value) { - int64_t temp = static_cast(value); + auto temp = static_cast(value); serializeInt64(index, temp); diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 60b3386a9..7c0bf8a81 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -92,8 +92,8 @@ public: } }; - typedef AnimationItem TAnimation; - typedef std::vector TAnimationQueue; + using TAnimation = AnimationItem; + using TAnimationQueue = std::vector; struct DLL_LINKAGE AnimationInfo { diff --git a/lib/vcmi_endian.h b/lib/vcmi_endian.h index e270b8eec..c1a81357d 100644 --- a/lib/vcmi_endian.h +++ b/lib/vcmi_endian.h @@ -34,13 +34,13 @@ PACKED_STRUCT_BEGIN struct unaligned_Uint32 { ui32 val; } PACKED_STRUCT_END; static inline ui16 read_unaligned_u16(const void *p) { - const struct unaligned_Uint16 *v = reinterpret_cast(p); + const auto * v = reinterpret_cast(p); return v->val; } static inline ui32 read_unaligned_u32(const void *p) { - const struct unaligned_Uint32 *v = reinterpret_cast(p); + const auto * v = reinterpret_cast(p); return v->val; } diff --git a/scripting/lua/LuaCallWrapper.h b/scripting/lua/LuaCallWrapper.h index 2876102e3..eb785fa26 100644 --- a/scripting/lua/LuaCallWrapper.h +++ b/scripting/lua/LuaCallWrapper.h @@ -31,7 +31,7 @@ struct Gens : Gens {}; template struct Gens<0, S...> { - typedef Seq type; + using type = Seq; }; template diff --git a/scripting/lua/LuaStack.h b/scripting/lua/LuaStack.h index dd5ce9497..fd273eada 100644 --- a/scripting/lua/LuaStack.h +++ b/scripting/lua/LuaStack.h @@ -100,7 +100,7 @@ public: return; } - UData * ptr = static_cast(raw); + auto * ptr = static_cast(raw); *ptr = value; luaL_getmetatable(L, KEY); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 77f34cf98..d13abea69 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -79,8 +79,8 @@ public: struct CasualtiesAfterBattle { - typedef std::pair TStackAndItsNewCount; - typedef std::map TSummoned; + using TStackAndItsNewCount = std::pair; + using TSummoned = std::map; enum {ERASE = -1}; const CArmedInstance * army; std::vector newStackCounts; diff --git a/server/CQuery.h b/server/CQuery.h index 9c7c92ddd..089e1c256 100644 --- a/server/CQuery.h +++ b/server/CQuery.h @@ -26,7 +26,7 @@ class CObjectVisitQuery; class CQuery; class Queries; -typedef std::shared_ptr QueryPtr; +using QueryPtr = std::shared_ptr; // This class represents any kind of prolonged interaction that may need to do something special after it is over. // It does not necessarily has to be "query" requiring player action, it can be also used internally within server. From 6fc3bb6690e299f50cec0e0eb99db13c47019a68 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 1 May 2023 01:15:48 +0400 Subject: [PATCH 16/43] Attempt to fix msvs build 1 --- lib/rewardable/Info.cpp | 1 - lib/rewardable/Interface.cpp | 1 - lib/rewardable/Limiter.cpp | 1 + 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/rewardable/Info.cpp b/lib/rewardable/Info.cpp index 37fb2d57d..798b2e012 100644 --- a/lib/rewardable/Info.cpp +++ b/lib/rewardable/Info.cpp @@ -23,7 +23,6 @@ #include "../CGeneralTextHandler.h" #include "../JsonNode.h" #include "../IGameCallback.h" -#include "../CPlayerState.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/rewardable/Interface.cpp b/lib/rewardable/Interface.cpp index 8e4261c83..8d87fbb70 100644 --- a/lib/rewardable/Interface.cpp +++ b/lib/rewardable/Interface.cpp @@ -18,7 +18,6 @@ #include "../spells/ISpellMechanics.h" #include "../mapObjects/MiscObjects.h" #include "../IGameCallback.h" -#include "../CPlayerState.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/rewardable/Limiter.cpp b/lib/rewardable/Limiter.cpp index deb46d30e..cbc101efc 100644 --- a/lib/rewardable/Limiter.cpp +++ b/lib/rewardable/Limiter.cpp @@ -11,6 +11,7 @@ #include "StdInc.h" #include "Limiter.h" #include "../IGameCallback.h" +#include "../CGameStateFwd.h" #include "../CPlayerState.h" VCMI_LIB_NAMESPACE_BEGIN 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 17/43] 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 c7ade44d0787a65169451526aa38a9045a65a48b Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 29 Apr 2023 17:52:31 +0400 Subject: [PATCH 18/43] Fix skills loading from mods --- lib/mapObjects/JsonRandom.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/mapObjects/JsonRandom.cpp b/lib/mapObjects/JsonRandom.cpp index 9df0770ec..a00684cbe 100644 --- a/lib/mapObjects/JsonRandom.cpp +++ b/lib/mapObjects/JsonRandom.cpp @@ -123,7 +123,9 @@ namespace JsonRandom std::set defaultStats(std::begin(PrimarySkill::names), std::end(PrimarySkill::names)); for(const auto & element : value.Vector()) { - int id = vstd::find_pos(PrimarySkill::names, loadKey(element, rng, defaultStats)); + auto key = loadKey(element, rng, defaultStats); + defaultStats.erase(key); + int id = vstd::find_pos(PrimarySkill::names, key); if(id != -1) ret[id] += loadValue(element, rng); } @@ -153,8 +155,13 @@ namespace JsonRandom for(const auto & element : value.Vector()) { - SecondarySkill id(VLC->modh->identifiers.getIdentifier(element.meta, "skill", loadKey(element, rng, defaultSkills)).value()); - ret[id] = loadValue(element, rng); + auto key = loadKey(element, rng, defaultSkills); + defaultSkills.erase(key); //avoid dupicates + if(auto identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "skill", key)) + { + SecondarySkill id(identifier.value()); + ret[id] = loadValue(element, rng); + } } } return ret; From d2e0d6a754fa92f58dde160735be46d8970ea32f Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 29 Apr 2023 17:05:44 +0400 Subject: [PATCH 19/43] Fix none-Of operator for skills --- lib/mapObjects/JsonRandom.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/mapObjects/JsonRandom.cpp b/lib/mapObjects/JsonRandom.cpp index a00684cbe..828a890cc 100644 --- a/lib/mapObjects/JsonRandom.cpp +++ b/lib/mapObjects/JsonRandom.cpp @@ -11,6 +11,8 @@ #include "StdInc.h" #include "JsonRandom.h" +#include + #include "../JsonNode.h" #include "../CRandomGenerator.h" #include "../StringConstants.h" @@ -150,7 +152,11 @@ namespace JsonRandom for(const auto & skill : VLC->skillh->objects) { IObjectInterface::cb->isAllowed(2, skill->getIndex()); - defaultSkills.insert(skill->getNameTextID()); + auto scopeAndName = vstd::splitStringToPair(skill->getJsonKey(), ':'); + if(scopeAndName.first == CModHandler::scopeBuiltin() || scopeAndName.first == value.meta) + defaultSkills.insert(scopeAndName.second); + else + defaultSkills.insert(skill->getJsonKey()); } for(const auto & element : value.Vector()) From c7bee037d2c06c56893d631651288c33e3cb95ae Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 1 May 2023 14:44:52 +0300 Subject: [PATCH 20/43] 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 832e56e005216bbbc446e1fe42c7bdb48dccc361 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 27 Apr 2023 20:21:06 +0300 Subject: [PATCH 21/43] Implementation of (very basic) shortcuts system - Added EShortcut enumeration that contains all in-game shortcuts - CIntObject::keyPressed now receive values from hotkey enumeration - On keypress, SDL key code will be translated to shortcut ID - Removed access to SDL key codes from most of engine --- Global.h | 8 +- client/CMakeLists.txt | 3 + .../adventureMap/CAdventureMapInterface.cpp | 163 ++++---- client/adventureMap/CAdventureMapInterface.h | 6 +- client/adventureMap/CAdventureOptions.cpp | 12 +- client/adventureMap/CInGameConsole.cpp | 42 +-- client/adventureMap/CInGameConsole.h | 2 +- client/battle/BattleFieldController.cpp | 2 +- client/battle/BattleInterfaceClasses.cpp | 5 +- client/battle/BattleWindow.cpp | 26 +- client/battle/BattleWindow.h | 7 +- client/gui/CGuiHandler.cpp | 105 ++---- client/gui/CGuiHandler.h | 11 +- client/gui/CIntObject.cpp | 23 +- client/gui/CIntObject.h | 18 +- client/gui/InterfaceObjectConfigurable.cpp | 48 +-- client/gui/InterfaceObjectConfigurable.h | 2 +- client/gui/Shortcut.h | 140 +++++++ client/gui/ShortcutHandler.cpp | 356 ++++++++++++++++++ client/gui/ShortcutHandler.h | 24 ++ client/lobby/CBonusSelection.cpp | 7 +- client/lobby/CLobbyScreen.cpp | 19 +- client/lobby/CSavingScreen.cpp | 4 +- client/lobby/CScenarioInfoScreen.cpp | 3 +- client/lobby/CSelectionBase.cpp | 7 +- client/lobby/CSelectionBase.h | 2 +- client/lobby/SelectionTab.cpp | 15 +- client/lobby/SelectionTab.h | 2 +- client/mainmenu/CCampaignScreen.cpp | 3 +- client/mainmenu/CMainMenu.cpp | 16 +- client/widgets/Buttons.cpp | 23 +- client/widgets/Buttons.h | 6 +- client/widgets/CArtifactHolder.cpp | 1 + client/widgets/CArtifactsOfHeroBase.cpp | 5 +- client/widgets/CComponent.cpp | 6 +- client/widgets/TextControls.cpp | 13 +- client/widgets/TextControls.h | 4 +- client/windows/CCastleInterface.cpp | 31 +- client/windows/CCastleInterface.h | 2 +- client/windows/CCreatureWindow.cpp | 16 +- client/windows/CHeroWindow.cpp | 16 +- client/windows/CKingdomInterface.cpp | 8 +- client/windows/CPuzzleWindow.cpp | 4 +- client/windows/CQuestLog.cpp | 3 +- client/windows/CSpellWindow.cpp | 56 +-- client/windows/CSpellWindow.h | 2 +- client/windows/CTradeWindow.cpp | 7 +- client/windows/CreaturePurchaseCard.cpp | 5 +- client/windows/GUIClasses.cpp | 66 ++-- client/windows/GUIClasses.h | 2 +- client/windows/InfoWindows.cpp | 14 +- client/windows/QuickRecruitmentWindow.cpp | 8 +- config/campaignSets.json | 19 +- config/mainmenu.json | 40 +- config/widgets/battleWindow.json | 22 +- .../widgets/settings/generalOptionsTab.json | 1 - .../settings/settingsMainContainer.json | 12 +- 57 files changed, 920 insertions(+), 553 deletions(-) create mode 100644 client/gui/Shortcut.h create mode 100644 client/gui/ShortcutHandler.cpp create mode 100644 client/gui/ShortcutHandler.h diff --git a/Global.h b/Global.h index b54ea4fac..e32b0da72 100644 --- a/Global.h +++ b/Global.h @@ -543,10 +543,16 @@ namespace vstd }); } + template + T advance_r(const T &obj, int change) + { + return static_cast(static_cast(obj) + change); + } + template void advance(T &obj, int change) { - obj = (T)(((int)obj) + change); + obj = advance_r(obj, change); } template diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index d4c6b53ae..fee6fe5b3 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -31,6 +31,7 @@ set(client_SRCS gui/CursorHandler.cpp gui/InterfaceObjectConfigurable.cpp gui/NotificationHandler.cpp + gui/ShortcutHandler.cpp lobby/CBonusSelection.cpp lobby/CCampaignInfoScreen.cpp @@ -159,6 +160,8 @@ set(client_HEADERS gui/InterfaceObjectConfigurable.h gui/MouseButton.h gui/NotificationHandler.h + gui/Shortcut.h + gui/ShortcutHandler.h gui/TextAlignment.h lobby/CBonusSelection.h diff --git a/client/adventureMap/CAdventureMapInterface.cpp b/client/adventureMap/CAdventureMapInterface.cpp index 3f3ee4fca..681bde8ba 100644 --- a/client/adventureMap/CAdventureMapInterface.cpp +++ b/client/adventureMap/CAdventureMapInterface.cpp @@ -33,6 +33,7 @@ #include "../gui/CursorHandler.h" #include "../render/IImage.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/TextControls.h" #include "../widgets/Buttons.h" #include "../windows/settings/SettingsMainWindow.h" @@ -95,7 +96,7 @@ CAdventureMapInterface::CAdventureMapInterface(): gems.push_back(std::make_shared(ADVOPT.gemG[g], 0, 0, ADVOPT.gemX[g], ADVOPT.gemY[g])); } - auto makeButton = [&](int textID, std::function callback, config::ButtonInfo info, int key) -> std::shared_ptr + auto makeButton = [&](int textID, std::function callback, config::ButtonInfo info, EShortcut key) -> std::shared_ptr { auto button = std::make_shared(Point(info.x, info.y), info.defName, CGI->generaltexth->zelp[textID], callback, key, info.playerColoured); for(auto image : info.additionalDefs) @@ -103,16 +104,16 @@ CAdventureMapInterface::CAdventureMapInterface(): return button; }; - kingOverview = makeButton(293, std::bind(&CAdventureMapInterface::fshowOverview,this), ADVOPT.kingOverview, SDLK_k); - underground = makeButton(294, std::bind(&CAdventureMapInterface::fswitchLevel,this), ADVOPT.underground, SDLK_u); - questlog = makeButton(295, std::bind(&CAdventureMapInterface::fshowQuestlog,this), ADVOPT.questlog, SDLK_q); - sleepWake = makeButton(296, std::bind(&CAdventureMapInterface::fsleepWake,this), ADVOPT.sleepWake, SDLK_w); - moveHero = makeButton(297, std::bind(&CAdventureMapInterface::fmoveHero,this), ADVOPT.moveHero, SDLK_m); - spellbook = makeButton(298, std::bind(&CAdventureMapInterface::fshowSpellbok,this), ADVOPT.spellbook, SDLK_c); - advOptions = makeButton(299, std::bind(&CAdventureMapInterface::fadventureOPtions,this), ADVOPT.advOptions, SDLK_a); - sysOptions = makeButton(300, std::bind(&CAdventureMapInterface::fsystemOptions,this), ADVOPT.sysOptions, SDLK_o); - nextHero = makeButton(301, std::bind(&CAdventureMapInterface::fnextHero,this), ADVOPT.nextHero, SDLK_h); - endTurn = makeButton(302, std::bind(&CAdventureMapInterface::fendTurn,this), ADVOPT.endTurn, SDLK_e); + kingOverview = makeButton(293, std::bind(&CAdventureMapInterface::fshowOverview,this), ADVOPT.kingOverview, EShortcut::ADVENTURE_KINGDOM_OVERVIEW); + underground = makeButton(294, std::bind(&CAdventureMapInterface::fswitchLevel,this), ADVOPT.underground, EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL); + questlog = makeButton(295, std::bind(&CAdventureMapInterface::fshowQuestlog,this), ADVOPT.questlog, EShortcut::ADVENTURE_QUEST_LOG); + sleepWake = makeButton(296, std::bind(&CAdventureMapInterface::fsleepWake,this), ADVOPT.sleepWake, EShortcut::ADVENTURE_TOGGLE_SLEEP); + moveHero = makeButton(297, std::bind(&CAdventureMapInterface::fmoveHero,this), ADVOPT.moveHero, EShortcut::ADVENTURE_MOVE_HERO); + spellbook = makeButton(298, std::bind(&CAdventureMapInterface::fshowSpellbok,this), ADVOPT.spellbook, EShortcut::ADVENTURE_CAST_SPELL); + advOptions = makeButton(299, std::bind(&CAdventureMapInterface::fadventureOPtions,this), ADVOPT.advOptions, EShortcut::ADVENTURE_OPTIONS); + sysOptions = makeButton(300, std::bind(&CAdventureMapInterface::fsystemOptions,this), ADVOPT.sysOptions, EShortcut::GLOBAL_OPTIONS); + nextHero = makeButton(301, std::bind(&CAdventureMapInterface::fnextHero,this), ADVOPT.nextHero, EShortcut::ADVENTURE_NEXT_HERO); + endTurn = makeButton(302, std::bind(&CAdventureMapInterface::fendTurn,this), ADVOPT.endTurn, EShortcut::ADVENTURE_END_TURN); int panelSpaceBottom = GH.screenDimensions().y - resdatabar->pos.h - 4; @@ -139,7 +140,7 @@ CAdventureMapInterface::CAdventureMapInterface(): worldViewBackConfig.y = 343 + 195; worldViewBackConfig.playerColoured = false; panelWorldView->addChildToPanel( - makeButton(288, std::bind(&CAdventureMapInterface::fworldViewBack,this), worldViewBackConfig, SDLK_ESCAPE), ACTIVATE | DEACTIVATE); + makeButton(288, std::bind(&CAdventureMapInterface::fworldViewBack,this), worldViewBackConfig, EShortcut::GLOBAL_CANCEL), ACTIVATE | DEACTIVATE); config::ButtonInfo worldViewPuzzleConfig = config::ButtonInfo(); worldViewPuzzleConfig.defName = "VWPUZ.DEF"; @@ -148,7 +149,7 @@ CAdventureMapInterface::CAdventureMapInterface(): worldViewPuzzleConfig.playerColoured = false; panelWorldView->addChildToPanel( // no help text for this one std::make_shared(Point(worldViewPuzzleConfig.x, worldViewPuzzleConfig.y), worldViewPuzzleConfig.defName, std::pair(), - std::bind(&CPlayerInterface::showPuzzleMap,LOCPLINT), SDLK_p, worldViewPuzzleConfig.playerColoured), ACTIVATE | DEACTIVATE); + std::bind(&CPlayerInterface::showPuzzleMap,LOCPLINT), EShortcut::ADVENTURE_VIEW_PUZZLE, worldViewPuzzleConfig.playerColoured), ACTIVATE | DEACTIVATE); config::ButtonInfo worldViewScale1xConfig = config::ButtonInfo(); worldViewScale1xConfig.defName = "VWMAG1.DEF"; @@ -156,7 +157,7 @@ CAdventureMapInterface::CAdventureMapInterface(): worldViewScale1xConfig.y = 23 + 195; worldViewScale1xConfig.playerColoured = false; panelWorldView->addChildToPanel( // help text is wrong for this button - makeButton(291, std::bind(&CAdventureMapInterface::fworldViewScale1x,this), worldViewScale1xConfig, SDLK_1), ACTIVATE | DEACTIVATE); + makeButton(291, std::bind(&CAdventureMapInterface::fworldViewScale1x,this), worldViewScale1xConfig, EShortcut::SELECT_INDEX_1), ACTIVATE | DEACTIVATE); config::ButtonInfo worldViewScale2xConfig = config::ButtonInfo(); worldViewScale2xConfig.defName = "VWMAG2.DEF"; @@ -164,7 +165,7 @@ CAdventureMapInterface::CAdventureMapInterface(): worldViewScale2xConfig.y = 23 + 195; worldViewScale2xConfig.playerColoured = false; panelWorldView->addChildToPanel( // help text is wrong for this button - makeButton(291, std::bind(&CAdventureMapInterface::fworldViewScale2x,this), worldViewScale2xConfig, SDLK_2), ACTIVATE | DEACTIVATE); + makeButton(291, std::bind(&CAdventureMapInterface::fworldViewScale2x,this), worldViewScale2xConfig, EShortcut::SELECT_INDEX_2), ACTIVATE | DEACTIVATE); config::ButtonInfo worldViewScale4xConfig = config::ButtonInfo(); worldViewScale4xConfig.defName = "VWMAG4.DEF"; @@ -172,7 +173,7 @@ CAdventureMapInterface::CAdventureMapInterface(): worldViewScale4xConfig.y = 23 + 195; worldViewScale4xConfig.playerColoured = false; panelWorldView->addChildToPanel( // help text is wrong for this button - makeButton(291, std::bind(&CAdventureMapInterface::fworldViewScale4x,this), worldViewScale4xConfig, SDLK_4), ACTIVATE | DEACTIVATE); + makeButton(291, std::bind(&CAdventureMapInterface::fworldViewScale4x,this), worldViewScale4xConfig, EShortcut::SELECT_INDEX_4), ACTIVATE | DEACTIVATE); config::ButtonInfo worldViewUndergroundConfig = config::ButtonInfo(); worldViewUndergroundConfig.defName = "IAM010.DEF"; @@ -180,7 +181,7 @@ CAdventureMapInterface::CAdventureMapInterface(): worldViewUndergroundConfig.x = GH.screenDimensions().x - 115; worldViewUndergroundConfig.y = 343 + 195; worldViewUndergroundConfig.playerColoured = true; - worldViewUnderground = makeButton(294, std::bind(&CAdventureMapInterface::fswitchLevel,this), worldViewUndergroundConfig, SDLK_u); + worldViewUnderground = makeButton(294, std::bind(&CAdventureMapInterface::fswitchLevel,this), worldViewUndergroundConfig, EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL); panelWorldView->addChildColorableButton(worldViewUnderground); onCurrentPlayerChanged(LOCPLINT->playerID); @@ -392,7 +393,6 @@ void CAdventureMapInterface::updateButtons() { bool state = LOCPLINT->localState->isHeroSleeping(hero); sleepWake->setIndex(state ? 1 : 0, true); - sleepWake->assignedKeys = {state ? SDLK_w : SDLK_z}; sleepWake->redraw(); } } @@ -607,17 +607,20 @@ void CAdventureMapInterface::centerOnObject(const CGObjectInstance * obj) terrain->onCenteredObject(obj); } -void CAdventureMapInterface::keyPressed(const SDL_Keycode & key) +void CAdventureMapInterface::keyPressed(EShortcut key) { if (state != EGameState::MAKING_TURN) return; + //fake mouse use to trigger onTileHovered() + GH.fakeMouseMove(); + const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); //selected hero const CGTownInstance *t = LOCPLINT->localState->getCurrentTown(); //selected town switch(key) { - case SDLK_g: + case EShortcut::ADVENTURE_THIEVES_GUILD: if(GH.topInt()->type & BLOCK_ADV_HOTKEYS) return; @@ -634,40 +637,40 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key) LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithTavern")); } return; - case SDLK_i: + case EShortcut::ADVENTURE_VIEW_SCENARIO: if(isActive()) CAdventureOptions::showScenarioInfo(); return; - case SDLK_s: + case EShortcut::GAME_SAVE_GAME: if(isActive()) GH.pushIntT(); return; - case SDLK_l: + case EShortcut::GAME_LOAD_GAME: if(isActive()) LOCPLINT->proposeLoadingGame(); return; - case SDLK_d: + case EShortcut::ADVENTURE_DIG_GRAIL: { if(h && isActive() && LOCPLINT->makingTurn) LOCPLINT->tryDiggging(h); return; } - case SDLK_p: + case EShortcut::ADVENTURE_VIEW_PUZZLE: if(isActive()) LOCPLINT->showPuzzleMap(); return; - case SDLK_v: + case EShortcut::ADVENTURE_VIEW_WORLD: if(isActive()) LOCPLINT->viewWorldMap(); return; - case SDLK_r: + case EShortcut::GAME_RESTART_GAME: if(isActive() && GH.isKeyboardCtrlDown()) { LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.adventureMap.confirmRestartGame"), [](){ GH.pushUserEvent(EUserEvent::RESTART_GAME); }, nullptr); } return; - case SDLK_SPACE: //space - try to revisit current object with selected hero + case EShortcut::ADVENTURE_VISIT_OBJECT: //space - try to revisit current object with selected hero { if(!isActive()) return; @@ -677,7 +680,7 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key) } } return; - case SDLK_RETURN: + case EShortcut::ADVENTURE_VIEW_SELECTED: { if(!isActive() || !LOCPLINT->localState->getCurrentArmy()) return; @@ -687,7 +690,7 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key) LOCPLINT->openTownWindow(t); return; } - case SDLK_ESCAPE: + case EShortcut::GLOBAL_CANCEL: { //FIXME: this case is never executed since AdvMapInt is disabled while in spellcasting mode if(!isActive() || GH.topInt().get() != this || !spellBeingCasted) @@ -696,7 +699,7 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key) abortCastingMode(); return; } - case SDLK_t: + case EShortcut::GAME_MARKETPLACE: { //act on key down if marketplace windows is not already opened if(GH.topInt()->type & BLOCK_ADV_HOTKEYS) @@ -720,77 +723,55 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key) else //if not - complain LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket")); } - else if(isActive()) //no ctrl, advmapint is on the top => switch to town + case EShortcut::ADVENTURE_NEXT_TOWN: + if(isActive() && !GH.isKeyboardCtrlDown()) //no ctrl, advmapint is on the top => switch to town { townList->selectNext(); } return; } - case SDLK_LALT: - case SDLK_RALT: - { - //fake mouse use to trigger onTileHovered() - GH.fakeMouseMove(); - return; - } - default: - { - auto direction = keyToMoveDirection(key); - - if (!direction) - return; - - if(!h || !isActive()) - return; - - if (CGI->mh->hasOngoingAnimations()) - return; - - if(*direction == Point(0,0)) - { - centerOnObject(h); - return; - } - - int3 dst = h->visitablePos() + int3(direction->x, direction->y, 0); - - if (!CGI->mh->isInMap((dst))) - return; - - if ( !LOCPLINT->localState->setPath(h, dst)) - return; - - const CGPath & path = LOCPLINT->localState->getPath(h); - - if (path.nodes.size() > 2) - onHeroChanged(h); - else - if(!path.nodes[0].turns) - LOCPLINT->moveHero(h, path); - } - return; + case EShortcut::ADVENTURE_MOVE_HERO_SW: return hotkeyMoveHeroDirectional({-1, +1}); + case EShortcut::ADVENTURE_MOVE_HERO_SS: return hotkeyMoveHeroDirectional({ 0, +1}); + case EShortcut::ADVENTURE_MOVE_HERO_SE: return hotkeyMoveHeroDirectional({+1, +1}); + case EShortcut::ADVENTURE_MOVE_HERO_WW: return hotkeyMoveHeroDirectional({-1, 0}); + case EShortcut::ADVENTURE_MOVE_HERO_EE: return hotkeyMoveHeroDirectional({+1, 0}); + case EShortcut::ADVENTURE_MOVE_HERO_NW: return hotkeyMoveHeroDirectional({-1, -1}); + case EShortcut::ADVENTURE_MOVE_HERO_NN: return hotkeyMoveHeroDirectional({ 0, -1}); + case EShortcut::ADVENTURE_MOVE_HERO_NE: return hotkeyMoveHeroDirectional({+1, -1}); } } -std::optional CAdventureMapInterface::keyToMoveDirection(const SDL_Keycode & key) +void CAdventureMapInterface::hotkeyMoveHeroDirectional(Point direction) { - switch (key) { - case SDLK_DOWN: return Point( 0, +1); - case SDLK_LEFT: return Point(-1, 0); - case SDLK_RIGHT: return Point(+1, 0); - case SDLK_UP: return Point( 0, -1); + const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); //selected hero - case SDLK_KP_1: return Point(-1, +1); - case SDLK_KP_2: return Point( 0, +1); - case SDLK_KP_3: return Point(+1, +1); - case SDLK_KP_4: return Point(-1, 0); - case SDLK_KP_5: return Point( 0, 0); - case SDLK_KP_6: return Point(+1, 0); - case SDLK_KP_7: return Point(-1, -1); - case SDLK_KP_8: return Point( 0, -1); - case SDLK_KP_9: return Point(+1, -1); + if(!h || !isActive()) + return; + + if (CGI->mh->hasOngoingAnimations()) + return; + + if(direction == Point(0,0)) + { + centerOnObject(h); + return; } - return std::nullopt; + + int3 dst = h->visitablePos() + int3(direction.x, direction.y, 0); + + if (!CGI->mh->isInMap((dst))) + return; + + if ( !LOCPLINT->localState->setPath(h, dst)) + return; + + const CGPath & path = LOCPLINT->localState->getPath(h); + + if (path.nodes.size() > 2) + onHeroChanged(h); + else + if(!path.nodes[0].turns) + LOCPLINT->moveHero(h, path); } void CAdventureMapInterface::onSelectionChanged(const CArmedInstance *sel) diff --git a/client/adventureMap/CAdventureMapInterface.h b/client/adventureMap/CAdventureMapInterface.h index d1b63ee8b..54313d8e2 100644 --- a/client/adventureMap/CAdventureMapInterface.h +++ b/client/adventureMap/CAdventureMapInterface.h @@ -116,6 +116,8 @@ private: void fnextHero(); void fendTurn(); + void hotkeyMoveHeroDirectional(Point direction); + bool isActive(); void adjustActiveness(bool aiTurnStart); //should be called every time at AI/human turn transition; blocks GUI during AI turn @@ -130,7 +132,7 @@ private: const CGObjectInstance *getActiveObject(const int3 &tile); - std::optional keyToMoveDirection(const SDL_Keycode & key); + std::optional keyToMoveDirection(EShortcut key); void endingTurn(); @@ -149,7 +151,7 @@ protected: void show(SDL_Surface * to) override; void showAll(SDL_Surface * to) override; - void keyPressed(const SDL_Keycode & key) override; + void keyPressed(EShortcut key) override; public: CAdventureMapInterface(); diff --git a/client/adventureMap/CAdventureOptions.cpp b/client/adventureMap/CAdventureOptions.cpp index a880e2757..b606c0307 100644 --- a/client/adventureMap/CAdventureOptions.cpp +++ b/client/adventureMap/CAdventureOptions.cpp @@ -17,6 +17,7 @@ #include "../lobby/CCampaignInfoScreen.h" #include "../lobby/CScenarioInfoScreen.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/Buttons.h" #include "../../CCallback.h" @@ -27,19 +28,18 @@ CAdventureOptions::CAdventureOptions() { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); - viewWorld = std::make_shared(Point(24, 23), "ADVVIEW.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_v); + viewWorld = std::make_shared(Point(24, 23), "ADVVIEW.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_WORLD); viewWorld->addCallback( [] { LOCPLINT->viewWorldMap(); }); - exit = std::make_shared(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), std::bind(&CAdventureOptions::close, this), SDLK_RETURN); - exit->assignedKeys.insert(SDLK_ESCAPE); + exit = std::make_shared(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), std::bind(&CAdventureOptions::close, this), EShortcut::GLOBAL_RETURN); - scenInfo = std::make_shared(Point(24, 198), "ADVINFO.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_i); + scenInfo = std::make_shared(Point(24, 198), "ADVINFO.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_SCENARIO); scenInfo->addCallback(CAdventureOptions::showScenarioInfo); - puzzle = std::make_shared(Point(24, 81), "ADVPUZ.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_p); + puzzle = std::make_shared(Point(24, 81), "ADVPUZ.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_PUZZLE); puzzle->addCallback(std::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT)); - dig = std::make_shared(Point(24, 139), "ADVDIG.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_d); + dig = std::make_shared(Point(24, 139), "ADVDIG.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_DIG_GRAIL); if(const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero()) dig->addCallback(std::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h)); else diff --git a/client/adventureMap/CInGameConsole.cpp b/client/adventureMap/CInGameConsole.cpp index 6008eaf76..8041f8fb9 100644 --- a/client/adventureMap/CInGameConsole.cpp +++ b/client/adventureMap/CInGameConsole.cpp @@ -17,6 +17,7 @@ #include "../PlayerLocalState.h" #include "../ClientCommandManager.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../render/Colors.h" #include "../../CCallback.h" @@ -108,30 +109,29 @@ void CInGameConsole::print(const std::string & txt) GH.totalRedraw(); // FIXME: ingame console has no parent widget set } -void CInGameConsole::keyPressed (const SDL_Keycode & key) +void CInGameConsole::keyPressed (EShortcut key) { if (LOCPLINT->cingconsole != this) return; - if(!captureAllKeys && key != SDLK_TAB) + if(!captureAllKeys && key != EShortcut::GAME_ACTIVATE_CONSOLE) return; //because user is not entering any text switch(key) { - case SDLK_TAB: - case SDLK_ESCAPE: - { - if(captureAllKeys) - { - endEnteringText(false); - } - else if(SDLK_TAB == key) - { - startEnteringText(); - } - break; - } - case SDLK_RETURN: //enter key + case EShortcut::GLOBAL_CANCEL: + if(captureAllKeys) + endEnteringText(false); + break; + + case EShortcut::GAME_ACTIVATE_CONSOLE: + if(captureAllKeys) + endEnteringText(false); + else + startEnteringText(); + break; + + case EShortcut::GLOBAL_CONFIRM: { if(!enteredText.empty() && captureAllKeys) { @@ -145,7 +145,7 @@ void CInGameConsole::keyPressed (const SDL_Keycode & key) } break; } - case SDLK_BACKSPACE: + case EShortcut::GLOBAL_BACKSPACE: { if(enteredText.size() > 1) { @@ -155,7 +155,7 @@ void CInGameConsole::keyPressed (const SDL_Keycode & key) } break; } - case SDLK_UP: //up arrow + case EShortcut::SELECT_UP: { if(previouslyEntered.empty()) break; @@ -174,7 +174,7 @@ void CInGameConsole::keyPressed (const SDL_Keycode & key) } break; } - case SDLK_DOWN: //down arrow + case EShortcut::SELECT_DOWN: { if(prevEntDisp != -1 && prevEntDisp+1 < previouslyEntered.size()) { @@ -190,10 +190,6 @@ void CInGameConsole::keyPressed (const SDL_Keycode & key) } break; } - default: - { - break; - } } } diff --git a/client/adventureMap/CInGameConsole.h b/client/adventureMap/CInGameConsole.h index 0d5266967..508fd72a9 100644 --- a/client/adventureMap/CInGameConsole.h +++ b/client/adventureMap/CInGameConsole.h @@ -47,7 +47,7 @@ public: void tick(uint32_t msPassed) override; void show(SDL_Surface * to) override; void showAll(SDL_Surface * to) override; - void keyPressed(const SDL_Keycode & key) override; + void keyPressed(EShortcut key) override; void textInputed(const std::string & enteredText) override; void textEdited(const std::string & enteredText) override; diff --git a/client/battle/BattleFieldController.cpp b/client/battle/BattleFieldController.cpp index beed22812..0d2c4f7ce 100644 --- a/client/battle/BattleFieldController.cpp +++ b/client/battle/BattleFieldController.cpp @@ -243,7 +243,7 @@ std::set BattleFieldController::getMovementRangeForHoveredStack() if (!owner.stacksController->getActiveStack()) return result; - if (!settings["battle"]["movementHighlightOnHover"].Bool()) + if (!settings["battle"]["movementHighlightOnHover"].Bool() && !GH.isKeyboardShiftDown()) return result; auto hoveredHex = getHoveredHex(); diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index ae17c4eed..0b8d76aec 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -24,6 +24,7 @@ #include "../CVideoHandler.h" #include "../gui/CursorHandler.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../render/Canvas.h" #include "../render/IImage.h" #include "../widgets/Buttons.h" @@ -406,12 +407,12 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface background->colorize(owner.playerID); pos = center(background->pos); - exit = std::make_shared(Point(384, 505), "iok6432.def", std::make_pair("", ""), [&](){ bExitf();}, SDLK_RETURN); + exit = std::make_shared(Point(384, 505), "iok6432.def", std::make_pair("", ""), [&](){ bExitf();}, EShortcut::GLOBAL_CONFIRM); exit->setBorderColor(Colors::METALLIC_GOLD); if(allowReplay) { - repeat = std::make_shared(Point(24, 505), "icn6432.def", std::make_pair("", ""), [&](){ bRepeatf();}, SDLK_ESCAPE); + repeat = std::make_shared(Point(24, 505), "icn6432.def", std::make_pair("", ""), [&](){ bRepeatf();}, EShortcut::GLOBAL_CANCEL); repeat->setBorderColor(Colors::METALLIC_GOLD); labels.push_back(std::make_shared(232, 520, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("vcmi.battleResultsWindow.applyResultsLabel"))); } diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index db15c30ba..5ccd18b6c 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -21,6 +21,7 @@ #include "../CMusicHandler.h" #include "../gui/CursorHandler.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../windows/CSpellWindow.h" #include "../widgets/Buttons.h" #include "../widgets/Images.h" @@ -182,7 +183,7 @@ void BattleWindow::deactivate() LOCPLINT->cingconsole->deactivate(); } -void BattleWindow::keyPressed(const SDL_Keycode & key) +void BattleWindow::keyPressed(EShortcut key) { if (owner.openingPlaying()) { @@ -190,35 +191,18 @@ void BattleWindow::keyPressed(const SDL_Keycode & key) return; } - if(key == SDLK_q) + if(key == EShortcut::BATTLE_TOGGLE_QUEUE) { toggleQueueVisibility(); } - else if(key == SDLK_f) + else if(key == EShortcut::BATTLE_USE_CREATURE_SPELL) { owner.actionsController->enterCreatureCastingMode(); } - else if(key == SDLK_ESCAPE) + else if(key == EShortcut::GLOBAL_CANCEL) { owner.actionsController->endCastingSpell(); } - else if(GH.isKeyboardShiftDown()) - { - // save and activate setting - Settings movementHighlightOnHover = settings.write["battle"]["movementHighlightOnHover"]; - movementHighlightOnHoverCache = movementHighlightOnHover->Bool(); - movementHighlightOnHover->Bool() = true; - } -} - -void BattleWindow::keyReleased(const SDL_Keycode & key) -{ - if(!GH.isKeyboardShiftDown()) - { - // set back to initial state - Settings movementHighlightOnHover = settings.write["battle"]["movementHighlightOnHover"]; - movementHighlightOnHover->Bool() = movementHighlightOnHoverCache; - } } void BattleWindow::clickRight(tribool down, bool previousState) diff --git a/client/battle/BattleWindow.h b/client/battle/BattleWindow.h index 1d6305a2a..e0c34c453 100644 --- a/client/battle/BattleWindow.h +++ b/client/battle/BattleWindow.h @@ -84,8 +84,7 @@ public: void activate() override; void deactivate() override; - void keyPressed(const SDL_Keycode & key) override; - void keyReleased(const SDL_Keycode& key) override; + void keyPressed(EShortcut key) override; void clickRight(tribool down, bool previousState) override; void show(SDL_Surface *to) override; void showAll(SDL_Surface *to) override; @@ -98,9 +97,5 @@ public: /// Set possible alternative options. If more than 1 - the last will be considered as default option void setAlternativeActions(const std::list &); - -private: - /// used to save the state of this setting on toggle. - bool movementHighlightOnHoverCache; }; diff --git a/client/gui/CGuiHandler.cpp b/client/gui/CGuiHandler.cpp index 10397ecb6..0e7f94ce9 100644 --- a/client/gui/CGuiHandler.cpp +++ b/client/gui/CGuiHandler.cpp @@ -13,6 +13,7 @@ #include "CIntObject.h" #include "CursorHandler.h" +#include "ShortcutHandler.h" #include "../CGameInfo.h" #include "../render/Colors.h" @@ -27,6 +28,7 @@ #include #include #include +#include #ifdef VCMI_APPLE #include @@ -93,6 +95,7 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::function(); mainFPSmng = new CFramerateManager(); mainFPSmng->init(settings["video"]["targetfps"].Integer()); isPointerRelativeMode = settings["general"]["userRelativePointer"].Bool(); @@ -356,6 +359,10 @@ void CGuiHandler::handleCurrentEvent( SDL_Event & current ) if(current.type == SDL_KEYDOWN || current.type == SDL_KEYUP) { SDL_KeyboardEvent key = current.key; + + if (key.repeat != 0) + return; // ignore periodic event resends + if(current.type == SDL_KEYDOWN && key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool()) { //TODO: we need some central place for all interface-independent hotkeys @@ -397,32 +404,35 @@ void CGuiHandler::handleCurrentEvent( SDL_Event & current ) return; } - //translate numpad keys - if(key.keysym.sym == SDLK_KP_ENTER) - { - key.keysym.sym = SDLK_RETURN; - key.keysym.scancode = SDL_SCANCODE_RETURN; - } + auto shortcutsVector = shortcutsHandler->translateKeycode(key.keysym.sym); bool keysCaptured = false; for(auto i = keyinterested.begin(); i != keyinterested.end() && continueEventHandling; i++) { - if((*i)->captureThisKey(key.keysym.sym)) + for (EShortcut shortcut : shortcutsVector) { - keysCaptured = true; - break; + if((*i)->captureThisKey(shortcut)) + { + keysCaptured = true; + break; + } } } std::list miCopy = keyinterested; for(auto i = miCopy.begin(); i != miCopy.end() && continueEventHandling; i++) - if(vstd::contains(keyinterested,*i) && (!keysCaptured || (*i)->captureThisKey(key.keysym.sym))) + { + for (EShortcut shortcut : shortcutsVector) { - if (key.state == SDL_PRESSED && key.repeat == 0) // function like key_DOWN, and not like a periodic key_Pressed check - (**i).keyPressed(key.keysym.sym); - if (key.state == SDL_RELEASED) - (**i).keyReleased(key.keysym.sym); + if(vstd::contains(keyinterested,*i) && (!keysCaptured || (*i)->captureThisKey(shortcut))) + { + if (key.state == SDL_PRESSED) + (**i).keyPressed(shortcut); + if (key.state == SDL_RELEASED) + (**i).keyReleased(shortcut); + } } + } } else if(current.type == SDL_MOUSEMOTION) { @@ -704,6 +714,10 @@ CGuiHandler::~CGuiHandler() delete terminate_cond; } +ShortcutHandler & CGuiHandler::getShortcutsHandler() +{ + return *shortcutsHandler; +} void CGuiHandler::moveCursorToPosition(const Point & position) { @@ -770,69 +784,6 @@ void CGuiHandler::drawFPSCounter() graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, Colors::YELLOW, Point(10, 10)); } -SDL_Keycode CGuiHandler::arrowToNum(SDL_Keycode key) -{ - switch(key) - { - case SDLK_DOWN: - return SDLK_KP_2; - case SDLK_UP: - return SDLK_KP_8; - case SDLK_LEFT: - return SDLK_KP_4; - case SDLK_RIGHT: - return SDLK_KP_6; - default: - throw std::runtime_error("Wrong key!"); - } -} - -SDL_Keycode CGuiHandler::numToDigit(SDL_Keycode key) -{ - -#define REMOVE_KP(keyName) case SDLK_KP_ ## keyName : return SDLK_ ## keyName; - switch(key) - { - REMOVE_KP(0) - REMOVE_KP(1) - REMOVE_KP(2) - REMOVE_KP(3) - REMOVE_KP(4) - REMOVE_KP(5) - REMOVE_KP(6) - REMOVE_KP(7) - REMOVE_KP(8) - REMOVE_KP(9) - REMOVE_KP(PERIOD) - REMOVE_KP(MINUS) - REMOVE_KP(PLUS) - REMOVE_KP(EQUALS) - - case SDLK_KP_MULTIPLY: - return SDLK_ASTERISK; - case SDLK_KP_DIVIDE: - return SDLK_SLASH; - case SDLK_KP_ENTER: - return SDLK_RETURN; - default: - return SDLK_UNKNOWN; - } -#undef REMOVE_KP -} - -bool CGuiHandler::isNumKey(SDL_Keycode key, bool number) -{ - if(number) - return key >= SDLK_KP_1 && key <= SDLK_KP_0; - else - return (key >= SDLK_KP_1 && key <= SDLK_KP_0) || key == SDLK_KP_MINUS || key == SDLK_KP_PLUS || key == SDLK_KP_EQUALS; -} - -bool CGuiHandler::isArrowKey(SDL_Keycode key) -{ - return key == SDLK_UP || key == SDLK_DOWN || key == SDLK_LEFT || key == SDLK_RIGHT; -} - bool CGuiHandler::amIGuiThread() { return inGuiThread.get() && *inGuiThread; diff --git a/client/gui/CGuiHandler.h b/client/gui/CGuiHandler.h index 768476758..be4617a60 100644 --- a/client/gui/CGuiHandler.h +++ b/client/gui/CGuiHandler.h @@ -12,8 +12,6 @@ #include "MouseButton.h" #include "../../lib/Point.h" -#include - VCMI_LIB_NAMESPACE_BEGIN template struct CondSh; @@ -24,6 +22,7 @@ VCMI_LIB_NAMESPACE_END union SDL_Event; struct SDL_MouseMotionEvent; +class ShortcutHandler; class CFramerateManager; class IStatusBar; class CIntObject; @@ -79,6 +78,8 @@ private: std::vector> disposed; + std::unique_ptr shortcutsHandler; + std::atomic continueEventHandling; using CIntObjectList = std::list; @@ -113,6 +114,8 @@ public: /// returns current position of mouse cursor, relative to vcmi window const Point & getCursorPosition() const; + ShortcutHandler & getShortcutsHandler(); + Point screenDimensions() const; /// returns true if at least one mouse button is pressed @@ -173,10 +176,6 @@ public: void breakEventHandling(); //current event won't be propagated anymore void drawFPSCounter(); // draws the FPS to the upper left corner of the screen - static SDL_Keycode arrowToNum(SDL_Keycode key); //converts arrow key to according numpad key - static SDL_Keycode numToDigit(SDL_Keycode key);//converts numpad digit key to normal digit key - static bool isNumKey(SDL_Keycode key, bool number = true); //checks if key is on numpad (numbers - check only for numpad digits) - static bool isArrowKey(SDL_Keycode key); static bool amIGuiThread(); static void pushUserEvent(EUserEvent usercode); static void pushUserEvent(EUserEvent usercode, void * userdata); diff --git a/client/gui/CIntObject.cpp b/client/gui/CIntObject.cpp index 79ed430cd..811dec3f9 100644 --- a/client/gui/CIntObject.cpp +++ b/client/gui/CIntObject.cpp @@ -11,6 +11,7 @@ #include "CIntObject.h" #include "CGuiHandler.h" +#include "Shortcut.h" #include "../renderSDL/SDL_Extensions.h" #include "../windows/CMessage.h" #include "../CMT.h" @@ -305,28 +306,23 @@ const Rect & CIntObject::center(const Point & p, bool propagate) return pos; } -bool CIntObject::captureThisKey(const SDL_Keycode & key) +bool CIntObject::captureThisKey(EShortcut key) { return captureAllKeys; } CKeyShortcut::CKeyShortcut() + : assignedKey(EShortcut::NONE) {} -CKeyShortcut::CKeyShortcut(int key) +CKeyShortcut::CKeyShortcut(EShortcut key) + : assignedKey(key) { - if (key != SDLK_UNKNOWN) - assignedKeys.insert(key); } -CKeyShortcut::CKeyShortcut(std::set Keys) - :assignedKeys(Keys) -{} - -void CKeyShortcut::keyPressed(const SDL_Keycode & key) +void CKeyShortcut::keyPressed(EShortcut key) { - if(vstd::contains(assignedKeys,key) - || vstd::contains(assignedKeys, CGuiHandler::numToDigit(key))) + if( assignedKey == key && assignedKey != EShortcut::NONE) { bool prev = mouseState(MouseButton::LEFT); updateMouseState(MouseButton::LEFT, true); @@ -335,10 +331,9 @@ void CKeyShortcut::keyPressed(const SDL_Keycode & key) } } -void CKeyShortcut::keyReleased(const SDL_Keycode & key) +void CKeyShortcut::keyReleased(EShortcut key) { - if(vstd::contains(assignedKeys,key) - || vstd::contains(assignedKeys, CGuiHandler::numToDigit(key))) + if( assignedKey == key && assignedKey != EShortcut::NONE) { bool prev = mouseState(MouseButton::LEFT); updateMouseState(MouseButton::LEFT, false); diff --git a/client/gui/CIntObject.h b/client/gui/CIntObject.h index 6ecd276bd..23d76807e 100644 --- a/client/gui/CIntObject.h +++ b/client/gui/CIntObject.h @@ -16,8 +16,7 @@ struct SDL_Surface; class CGuiHandler; class CPicture; - -using SDL_Keycode = int32_t; +enum class EShortcut; using boost::logic::tribool; @@ -111,9 +110,9 @@ public: //keyboard handling bool captureAllKeys; //if true, only this object should get info about pressed keys - virtual void keyPressed(const SDL_Keycode & key){} - virtual void keyReleased(const SDL_Keycode & key){} - virtual bool captureThisKey(const SDL_Keycode & key); //allows refining captureAllKeys against specific events (eg. don't capture ENTER) + virtual void keyPressed(EShortcut key){} + virtual void keyReleased(EShortcut key){} + virtual bool captureThisKey(EShortcut key); //allows refining captureAllKeys against specific events (eg. don't capture ENTER) virtual void textInputed(const std::string & enteredText){}; virtual void textEdited(const std::string & enteredText){}; @@ -190,12 +189,11 @@ public: class CKeyShortcut : public virtual CIntObject { public: - std::set assignedKeys; + EShortcut assignedKey; CKeyShortcut(); - CKeyShortcut(int key); - CKeyShortcut(std::set Keys); - void keyPressed(const SDL_Keycode & key) override; - void keyReleased(const SDL_Keycode & key) override; + CKeyShortcut(EShortcut key); + void keyPressed(EShortcut key) override; + void keyReleased(EShortcut key) override; }; diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 340c0e21b..13b6aa7e9 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -15,6 +15,8 @@ #include "../CGameInfo.h" #include "../CPlayerInterface.h" #include "../gui/CGuiHandler.h" +#include "../gui/ShortcutHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/CComponent.h" #include "../widgets/Buttons.h" #include "../widgets/MiscWidgets.h" @@ -25,18 +27,6 @@ #include "../../lib/CGeneralTextHandler.h" -static std::map KeycodeMap{ - {"up", SDLK_UP}, - {"down", SDLK_DOWN}, - {"left", SDLK_LEFT}, - {"right", SDLK_RIGHT}, - {"space", SDLK_SPACE}, - {"escape", SDLK_ESCAPE}, - {"backspace", SDLK_BACKSPACE}, - {"enter", SDLK_RETURN} -}; - - InterfaceObjectConfigurable::InterfaceObjectConfigurable(const JsonNode & config, int used, Point offset): InterfaceObjectConfigurable(used, offset) { @@ -211,27 +201,20 @@ std::pair InterfaceObjectConfigurable::readHintText(co return result; } -int InterfaceObjectConfigurable::readKeycode(const JsonNode & config) const +EShortcut InterfaceObjectConfigurable::readKeycode(const JsonNode & config) const { logGlobal->debug("Reading keycode"); - if(config.getType() == JsonNode::JsonType::DATA_INTEGER) - return config.Integer(); - - if(config.getType() == JsonNode::JsonType::DATA_STRING) + + if(config.getType() != JsonNode::JsonType::DATA_STRING) { - auto s = config.String(); - if(s.size() == 1) //keyboard symbol - return s[0]; - - if (KeycodeMap.count(s)) - return KeycodeMap[s]; - - logGlobal->error("Invalid keycode '%s' in interface configuration!", config.String()); - return SDLK_UNKNOWN; + logGlobal->error("Invalid keycode format in interface configuration! Expected string!", config.String()); + return EShortcut::NONE; } - logGlobal->error("Invalid keycode format in interface configuration! Expected string or integer!", config.String()); - return SDLK_UNKNOWN; + EShortcut result = GH.getShortcutsHandler().findShortcut(config.String()); + if (result == EShortcut::NONE) + logGlobal->error("Invalid keycode '%s' in interface configuration!", config.String()); + return result;; } std::shared_ptr InterfaceObjectConfigurable::buildPicture(const JsonNode & config) const @@ -340,13 +323,8 @@ std::shared_ptr InterfaceObjectConfigurable::buildButton(const JsonNode button->addCallback(std::bind(callbacks.at(config["callback"].String()), 0)); if(!config["hotkey"].isNull()) { - if(config["hotkey"].getType() == JsonNode::JsonType::DATA_VECTOR) - { - for(auto k : config["hotkey"].Vector()) - button->assignedKeys.insert(readKeycode(k)); - } - else - button->assignedKeys.insert(readKeycode(config["hotkey"])); + if(config["hotkey"].getType() == JsonNode::JsonType::DATA_STRING) + button->assignedKey = readKeycode(config["hotkey"]); } return button; } diff --git a/client/gui/InterfaceObjectConfigurable.h b/client/gui/InterfaceObjectConfigurable.h index 7506592fc..deba0481a 100644 --- a/client/gui/InterfaceObjectConfigurable.h +++ b/client/gui/InterfaceObjectConfigurable.h @@ -64,7 +64,7 @@ protected: EFonts readFont(const JsonNode &) const; std::string readText(const JsonNode &) const; std::pair readHintText(const JsonNode &) const; - int readKeycode(const JsonNode &) const; + EShortcut readKeycode(const JsonNode &) const; //basic widgets std::shared_ptr buildPicture(const JsonNode &) const; diff --git a/client/gui/Shortcut.h b/client/gui/Shortcut.h new file mode 100644 index 000000000..d739cbf7c --- /dev/null +++ b/client/gui/Shortcut.h @@ -0,0 +1,140 @@ +/* + * Shortcut.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 + +enum class EShortcut +{ + NONE, + + GLOBAL_CONFIRM, // Enter + GLOBAL_CANCEL, // Escape + GLOBAL_RETURN, // Enter, Escape + GLOBAL_FULLSCREEN, // F4 + GLOBAL_OPTIONS, + GLOBAL_BACKSPACE, + GLOBAL_MOVE_FOCUS, + + SELECT_LEFT, + SELECT_RIGHT, + SELECT_UP, + SELECT_DOWN, + SELECT_FIRST, + SELECT_LAST, + SELECT_PAGE_UP, + SELECT_PAGE_DOWN, + + SELECT_INDEX_1, + SELECT_INDEX_2, + SELECT_INDEX_3, + SELECT_INDEX_4, + SELECT_INDEX_5, + SELECT_INDEX_6, + SELECT_INDEX_7, + SELECT_INDEX_8, + + MAIN_MENU_NEW, + MAIN_MENU_LOAD, + MAIN_MENU_SCORES, + MAIN_MENU_CREDITS, + MAIN_MENU_BACK, + MAIN_MENU_QUIT, + MAIN_MENU_SINGLEPLAYER, + MAIN_MENU_MULTIPLAYER, + MAIN_MENU_CAMPAIGN, + MAIN_MENU_TUTORIAL, + MAIN_MENU_CAMPAIGN_SOD, + MAIN_MENU_CAMPAIGN_ROE, + MAIN_MENU_CAMPAIGN_AB, + MAIN_MENU_CAMPAIGN_CUSTOM, + + LOBBY_BEGIN_GAME, // b, Return + LOBBY_LOAD_GAME, // l, Return + LOBBY_SAVE_GAME, // s, Return + LOBBY_RANDOM_MAP, + LOBBY_HIDE_CHAT, + LOBBY_ADDITIONAL_OPTIONS, + LOBBY_SELECT, + + GAME_END_TURN, + GAME_LOAD_GAME, + GAME_SAVE_GAME, + GAME_RESTART_GAME, + GAME_TO_MAIN_MENU, + GAME_QUIT_GAME, + GAME_MARKETPLACE, + GAME_THIEVES_GUILD, + GAME_ACTIVATE_CONSOLE, + + ADVENTURE_OPTIONS, + ADVENTURE_TOGGLE_GRID, + ADVENTURE_TOGGLE_SLEEP, + ADVENTURE_MOVE_HERO, + ADVENTURE_VISIT_OBJECT, + ADVENTURE_MOVE_HERO_SW, + ADVENTURE_MOVE_HERO_SS, + ADVENTURE_MOVE_HERO_SE, + ADVENTURE_MOVE_HERO_WW, + ADVENTURE_MOVE_HERO_EE, + ADVENTURE_MOVE_HERO_NW, + ADVENTURE_MOVE_HERO_NN, + ADVENTURE_MOVE_HERO_NE, + ADVENTURE_VIEW_SELECTED, + ADVENTURE_NEXT_OBJECT, + ADVENTURE_NEXT_TOWN, + ADVENTURE_NEXT_HERO, + ADVENTURE_FIRST_TOWN, + ADVENTURE_FIRST_HERO, + ADVENTURE_VIEW_SCENARIO, + ADVENTURE_DIG_GRAIL, + ADVENTURE_VIEW_PUZZLE, + ADVENTURE_VIEW_WORLD, + ADVENTURE_TOGGLE_MAP_LEVEL, + ADVENTURE_KINGDOM_OVERVIEW, + ADVENTURE_QUEST_LOG, + ADVENTURE_CAST_SPELL, + ADVENTURE_END_TURN, + ADVENTURE_THIEVES_GUILD, + + BATTLE_TOGGLE_QUEUE, + BATTLE_USE_CREATURE_SPELL, + BATTLE_SURRENDER, + BATTLE_RETREAT, + BATTLE_AUTOCOMBAT, + BATTLE_CAST_SPELL, + BATTLE_WAIT, + BATTLE_DEFEND, + BATTLE_CONSOLE_UP, + BATTLE_CONSOLE_DOWN, + BATTLE_TACTICS_NEXT, + BATTLE_TACTICS_END, + + TOWN_OPEN_TAVERN, + TOWN_SWAP_ARMIES, + + RECRUITMENT_MAX, + RECRUITMENT_MIN, + RECRUITMENT_UPGRADE, // U + RECRUITMENT_UPGRADE_ALL, // A, U + + KINGDOM_HEROES, + KINGDOM_TOWNS, + + HERO_DISMISS, + HERO_COMMANDER, + HERO_LOOSE_FORMATION, + HERO_TIGHT_FORMATION, + HERO_TOGGLE_TACTICS, // b + + SPELLBOOK_TAB_ADVENTURE, + SPELLBOOK_TAB_COMBAT, + + AFTER_LAST +}; + diff --git a/client/gui/ShortcutHandler.cpp b/client/gui/ShortcutHandler.cpp new file mode 100644 index 000000000..c0bea6ec3 --- /dev/null +++ b/client/gui/ShortcutHandler.cpp @@ -0,0 +1,356 @@ +/* + * ShortcutHandler.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 "ShortcutHandler.h" +#include "Shortcut.h" +#include + +std::vector ShortcutHandler::translateKeycode(SDL_Keycode key) +{ + static const std::multimap keyToShortcut = { + {SDLK_RETURN, EShortcut::GLOBAL_CONFIRM }, + {SDLK_KP_ENTER, EShortcut::GLOBAL_CONFIRM }, + {SDLK_ESCAPE, EShortcut::GLOBAL_CANCEL }, + {SDLK_RETURN, EShortcut::GLOBAL_RETURN }, + {SDLK_KP_ENTER, EShortcut::GLOBAL_RETURN }, + {SDLK_ESCAPE, EShortcut::GLOBAL_RETURN }, + {SDLK_F4, EShortcut::GLOBAL_FULLSCREEN }, + {SDLK_BACKSPACE, EShortcut::GLOBAL_BACKSPACE }, + {SDLK_TAB, EShortcut::GLOBAL_MOVE_FOCUS }, + {SDLK_o, EShortcut::GLOBAL_OPTIONS }, + {SDLK_LEFT, EShortcut::SELECT_LEFT }, + {SDLK_RIGHT, EShortcut::SELECT_RIGHT }, + {SDLK_UP, EShortcut::SELECT_UP }, + {SDLK_DOWN, EShortcut::SELECT_DOWN }, + {SDLK_HOME, EShortcut::SELECT_FIRST }, + {SDLK_END, EShortcut::SELECT_LAST }, + {SDLK_PAGEUP, EShortcut::SELECT_PAGE_UP }, + {SDLK_PAGEDOWN, EShortcut::SELECT_PAGE_DOWN }, + {SDLK_1, EShortcut::SELECT_INDEX_1 }, + {SDLK_2, EShortcut::SELECT_INDEX_2 }, + {SDLK_3, EShortcut::SELECT_INDEX_3 }, + {SDLK_4, EShortcut::SELECT_INDEX_4 }, + {SDLK_5, EShortcut::SELECT_INDEX_5 }, + {SDLK_6, EShortcut::SELECT_INDEX_6 }, + {SDLK_7, EShortcut::SELECT_INDEX_7 }, + {SDLK_8, EShortcut::SELECT_INDEX_8 }, + {SDLK_n, EShortcut::MAIN_MENU_NEW }, + {SDLK_l, EShortcut::MAIN_MENU_LOAD }, + {SDLK_h, EShortcut::MAIN_MENU_SCORES }, + {SDLK_c, EShortcut::MAIN_MENU_CREDITS }, + {SDLK_q, EShortcut::MAIN_MENU_QUIT }, + {SDLK_b, EShortcut::MAIN_MENU_BACK }, + {SDLK_s, EShortcut::MAIN_MENU_SINGLEPLAYER }, + {SDLK_m, EShortcut::MAIN_MENU_MULTIPLAYER }, + {SDLK_c, EShortcut::MAIN_MENU_CAMPAIGN }, + {SDLK_t, EShortcut::MAIN_MENU_TUTORIAL }, + {SDLK_s, EShortcut::MAIN_MENU_CAMPAIGN_SOD }, + {SDLK_r, EShortcut::MAIN_MENU_CAMPAIGN_ROE }, + {SDLK_a, EShortcut::MAIN_MENU_CAMPAIGN_AB }, + {SDLK_c, EShortcut::MAIN_MENU_CAMPAIGN_CUSTOM }, + {SDLK_b, EShortcut::LOBBY_BEGIN_GAME }, + {SDLK_RETURN, EShortcut::LOBBY_BEGIN_GAME }, + {SDLK_KP_ENTER, EShortcut::LOBBY_BEGIN_GAME }, + {SDLK_l, EShortcut::LOBBY_LOAD_GAME }, + {SDLK_RETURN, EShortcut::LOBBY_LOAD_GAME }, + {SDLK_KP_ENTER, EShortcut::LOBBY_LOAD_GAME }, + {SDLK_s, EShortcut::LOBBY_SAVE_GAME }, + {SDLK_r, EShortcut::LOBBY_RANDOM_MAP }, + {SDLK_h, EShortcut::LOBBY_HIDE_CHAT }, + {SDLK_a, EShortcut::LOBBY_ADDITIONAL_OPTIONS }, + {SDLK_s, EShortcut::LOBBY_SELECT }, + {SDLK_e, EShortcut::GAME_END_TURN }, + {SDLK_l, EShortcut::GAME_LOAD_GAME }, + {SDLK_s, EShortcut::GAME_SAVE_GAME }, + {SDLK_r, EShortcut::GAME_RESTART_GAME }, + {SDLK_m, EShortcut::GAME_TO_MAIN_MENU }, + {SDLK_q, EShortcut::GAME_QUIT_GAME }, + {SDLK_t, EShortcut::GAME_MARKETPLACE }, + {SDLK_g, EShortcut::GAME_THIEVES_GUILD }, + {SDLK_TAB, EShortcut::GAME_ACTIVATE_CONSOLE }, + {SDLK_o, EShortcut::ADVENTURE_OPTIONS }, + {SDLK_F6, EShortcut::ADVENTURE_TOGGLE_GRID }, + {SDLK_z, EShortcut::ADVENTURE_TOGGLE_SLEEP }, + {SDLK_w, EShortcut::ADVENTURE_TOGGLE_SLEEP }, + {SDLK_m, EShortcut::ADVENTURE_MOVE_HERO }, + {SDLK_SPACE, EShortcut::ADVENTURE_VISIT_OBJECT }, + {SDLK_KP_1, EShortcut::ADVENTURE_MOVE_HERO_SW }, + {SDLK_KP_2, EShortcut::ADVENTURE_MOVE_HERO_SS }, + {SDLK_KP_3, EShortcut::ADVENTURE_MOVE_HERO_SE }, + {SDLK_KP_4, EShortcut::ADVENTURE_MOVE_HERO_WW }, + {SDLK_KP_6, EShortcut::ADVENTURE_MOVE_HERO_EE }, + {SDLK_KP_7, EShortcut::ADVENTURE_MOVE_HERO_NW }, + {SDLK_KP_8, EShortcut::ADVENTURE_MOVE_HERO_NN }, + {SDLK_KP_9, EShortcut::ADVENTURE_MOVE_HERO_NE }, + {SDLK_RETURN, EShortcut::ADVENTURE_VIEW_SELECTED }, + {SDLK_KP_ENTER, EShortcut::ADVENTURE_VIEW_SELECTED }, + // {SDLK_, EShortcut::ADVENTURE_NEXT_OBJECT }, + {SDLK_t, EShortcut::ADVENTURE_NEXT_TOWN }, + {SDLK_h, EShortcut::ADVENTURE_NEXT_HERO }, + // {SDLK_, EShortcut::ADVENTURE_FIRST_TOWN }, + // {SDLK_, EShortcut::ADVENTURE_FIRST_HERO }, + {SDLK_i, EShortcut::ADVENTURE_VIEW_SCENARIO }, + {SDLK_d, EShortcut::ADVENTURE_DIG_GRAIL }, + {SDLK_p, EShortcut::ADVENTURE_VIEW_PUZZLE }, + {SDLK_v, EShortcut::ADVENTURE_VIEW_WORLD }, + {SDLK_u, EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL}, + {SDLK_k, EShortcut::ADVENTURE_KINGDOM_OVERVIEW}, + {SDLK_q, EShortcut::ADVENTURE_QUEST_LOG }, + {SDLK_c, EShortcut::ADVENTURE_CAST_SPELL }, + {SDLK_e, EShortcut::ADVENTURE_END_TURN }, + {SDLK_g, EShortcut::ADVENTURE_THIEVES_GUILD }, + {SDLK_q, EShortcut::BATTLE_TOGGLE_QUEUE }, + {SDLK_c, EShortcut::BATTLE_USE_CREATURE_SPELL }, + {SDLK_s, EShortcut::BATTLE_SURRENDER }, + {SDLK_r, EShortcut::BATTLE_RETREAT }, + {SDLK_a, EShortcut::BATTLE_AUTOCOMBAT }, + {SDLK_c, EShortcut::BATTLE_CAST_SPELL }, + {SDLK_w, EShortcut::BATTLE_WAIT }, + {SDLK_d, EShortcut::BATTLE_DEFEND }, + {SDLK_SPACE, EShortcut::BATTLE_DEFEND }, + {SDLK_UP, EShortcut::BATTLE_CONSOLE_UP }, + {SDLK_DOWN, EShortcut::BATTLE_CONSOLE_DOWN }, + {SDLK_SPACE, EShortcut::BATTLE_TACTICS_NEXT }, + {SDLK_RETURN, EShortcut::BATTLE_TACTICS_END }, + {SDLK_KP_ENTER, EShortcut::BATTLE_TACTICS_END }, + {SDLK_t, EShortcut::TOWN_OPEN_TAVERN }, + {SDLK_SPACE, EShortcut::TOWN_SWAP_ARMIES }, + {SDLK_END, EShortcut::RECRUITMENT_MAX }, + {SDLK_HOME, EShortcut::RECRUITMENT_MIN }, + {SDLK_u, EShortcut::RECRUITMENT_UPGRADE }, + {SDLK_a, EShortcut::RECRUITMENT_UPGRADE_ALL }, + {SDLK_u, EShortcut::RECRUITMENT_UPGRADE_ALL }, + {SDLK_h, EShortcut::KINGDOM_HEROES }, + {SDLK_t, EShortcut::KINGDOM_TOWNS }, + {SDLK_d, EShortcut::HERO_DISMISS }, + {SDLK_c, EShortcut::HERO_COMMANDER }, + {SDLK_l, EShortcut::HERO_LOOSE_FORMATION }, + {SDLK_t, EShortcut::HERO_TIGHT_FORMATION }, + {SDLK_b, EShortcut::HERO_TOGGLE_TACTICS }, + {SDLK_a, EShortcut::SPELLBOOK_TAB_ADVENTURE }, + {SDLK_c, EShortcut::SPELLBOOK_TAB_COMBAT } + }; + + auto range = keyToShortcut.equal_range(key); + + // FIXME: some code expects calls to keyPressed / captureThisKey even without defined hotkeys + if (range.first == range.second) + return {EShortcut::NONE}; + + std::vector result; + + for (auto it = range.first; it != range.second; ++it) + result.push_back(it->second); + + return result; +} + +EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) +{ + static const std::map shortcutNames = { + {"globalConfirm", EShortcut::GLOBAL_CONFIRM }, + {"globalCancel", EShortcut::GLOBAL_CANCEL }, + {"globalReturn", EShortcut::GLOBAL_RETURN }, + {"globalFullscreen", EShortcut::GLOBAL_FULLSCREEN }, + {"globalOptions", EShortcut::GLOBAL_OPTIONS }, + {"globalBackspace", EShortcut::GLOBAL_BACKSPACE }, + {"globalMoveFocus", EShortcut::GLOBAL_MOVE_FOCUS }, + {"selectLeft", EShortcut::SELECT_LEFT }, + {"selectRight", EShortcut::SELECT_RIGHT }, + {"selectUp", EShortcut::SELECT_UP }, + {"selectDown", EShortcut::SELECT_DOWN }, + {"selectFirst", EShortcut::SELECT_FIRST }, + {"selectLast", EShortcut::SELECT_LAST }, + {"selectPageUp", EShortcut::SELECT_PAGE_UP }, + {"selectPageDown", EShortcut::SELECT_PAGE_DOWN }, + {"selectIndex1", EShortcut::SELECT_INDEX_1 }, + {"selectIndex2", EShortcut::SELECT_INDEX_2 }, + {"selectIndex3", EShortcut::SELECT_INDEX_3 }, + {"selectIndex4", EShortcut::SELECT_INDEX_4 }, + {"selectIndex5", EShortcut::SELECT_INDEX_5 }, + {"selectIndex6", EShortcut::SELECT_INDEX_6 }, + {"selectIndex7", EShortcut::SELECT_INDEX_7 }, + {"selectIndex8", EShortcut::SELECT_INDEX_8 }, + {"mainMenuNew", EShortcut::MAIN_MENU_NEW }, + {"mainMenuLoad", EShortcut::MAIN_MENU_LOAD }, + {"mainMenuScores", EShortcut::MAIN_MENU_SCORES }, + {"mainMenuCredits", EShortcut::MAIN_MENU_CREDITS }, + {"mainMenuQuit", EShortcut::MAIN_MENU_QUIT }, + {"mainMenuBack", EShortcut::MAIN_MENU_BACK }, + {"mainMenuSingleplayer", EShortcut::MAIN_MENU_SINGLEPLAYER }, + {"mainMenuMultiplayer", EShortcut::MAIN_MENU_MULTIPLAYER }, + {"mainMenuCampaign", EShortcut::MAIN_MENU_CAMPAIGN }, + {"mainMenuTutorial", EShortcut::MAIN_MENU_TUTORIAL }, + {"mainMenuCampaignSod", EShortcut::MAIN_MENU_CAMPAIGN_SOD }, + {"mainMenuCampaignRoe", EShortcut::MAIN_MENU_CAMPAIGN_ROE }, + {"mainMenuCampaignAb", EShortcut::MAIN_MENU_CAMPAIGN_AB }, + {"mainMenuCampaignCustom", EShortcut::MAIN_MENU_CAMPAIGN_CUSTOM }, + {"lobbyBeginGame", EShortcut::LOBBY_BEGIN_GAME }, + {"lobbyLoadGame", EShortcut::LOBBY_LOAD_GAME }, + {"lobbySaveGame", EShortcut::LOBBY_SAVE_GAME }, + {"lobbyRandomMap", EShortcut::LOBBY_RANDOM_MAP }, + {"lobbyHideChat", EShortcut::LOBBY_HIDE_CHAT }, + {"lobbyAdditionalOptions", EShortcut::LOBBY_ADDITIONAL_OPTIONS }, + {"lobbySelect", EShortcut::LOBBY_SELECT }, + {"gameEndTurn", EShortcut::GAME_END_TURN }, + {"gameLoadGame", EShortcut::GAME_LOAD_GAME }, + {"gameSaveGame", EShortcut::GAME_SAVE_GAME }, + {"gameRestartGame", EShortcut::GAME_RESTART_GAME }, + {"gameMainMenu", EShortcut::GAME_TO_MAIN_MENU }, + {"gameQuitGame", EShortcut::GAME_QUIT_GAME }, + {"gameMarketplace", EShortcut::GAME_MARKETPLACE }, + {"gameThievesGuild", EShortcut::GAME_THIEVES_GUILD }, + {"gameActivateConsole", EShortcut::GAME_ACTIVATE_CONSOLE }, + {"adventureOptions", EShortcut::ADVENTURE_OPTIONS }, + {"adventureToggleGrid", EShortcut::ADVENTURE_TOGGLE_GRID }, + {"adventureToggleSleep", EShortcut::ADVENTURE_TOGGLE_SLEEP }, + {"adventureMoveHero", EShortcut::ADVENTURE_MOVE_HERO }, + {"adventureVisitObject", EShortcut::ADVENTURE_VISIT_OBJECT }, + {"adventureMoveHeroSW", EShortcut::ADVENTURE_MOVE_HERO_SW }, + {"adventureMoveHeroSS", EShortcut::ADVENTURE_MOVE_HERO_SS }, + {"adventureMoveHeroSE", EShortcut::ADVENTURE_MOVE_HERO_SE }, + {"adventureMoveHeroWW", EShortcut::ADVENTURE_MOVE_HERO_WW }, + {"adventureMoveHeroEE", EShortcut::ADVENTURE_MOVE_HERO_EE }, + {"adventureMoveHeroNW", EShortcut::ADVENTURE_MOVE_HERO_NW }, + {"adventureMoveHeroNN", EShortcut::ADVENTURE_MOVE_HERO_NN }, + {"adventureMoveHeroNE", EShortcut::ADVENTURE_MOVE_HERO_NE }, + {"adventureViewSelected", EShortcut::ADVENTURE_VIEW_SELECTED }, + {"adventureNextObject", EShortcut::ADVENTURE_NEXT_OBJECT }, + {"adventureNextTown", EShortcut::ADVENTURE_NEXT_TOWN }, + {"adventureNextHero", EShortcut::ADVENTURE_NEXT_HERO }, + {"adventureFirstTown", EShortcut::ADVENTURE_FIRST_TOWN }, + {"adventureFirstHero", EShortcut::ADVENTURE_FIRST_HERO }, + {"adventureViewScenario", EShortcut::ADVENTURE_VIEW_SCENARIO }, + {"adventureDigGrail", EShortcut::ADVENTURE_DIG_GRAIL }, + {"adventureViewPuzzle", EShortcut::ADVENTURE_VIEW_PUZZLE }, + {"adventureViewWorld", EShortcut::ADVENTURE_VIEW_WORLD }, + {"adventureToggleMapLevel", EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL}, + {"adventureKingdomOverview", EShortcut::ADVENTURE_KINGDOM_OVERVIEW}, + {"adventureQuestLog", EShortcut::ADVENTURE_QUEST_LOG }, + {"adventureCastSpell", EShortcut::ADVENTURE_CAST_SPELL }, + {"adventureEndTurn", EShortcut::ADVENTURE_END_TURN }, + {"adventureThievesGuild", EShortcut::ADVENTURE_THIEVES_GUILD }, + {"battleToggleQueue", EShortcut::BATTLE_TOGGLE_QUEUE }, + {"battleUseCreatureSpell", EShortcut::BATTLE_USE_CREATURE_SPELL }, + {"battleSurrender", EShortcut::BATTLE_SURRENDER }, + {"battleRetreat", EShortcut::BATTLE_RETREAT }, + {"battleAutocombat", EShortcut::BATTLE_AUTOCOMBAT }, + {"battleCastSpell", EShortcut::BATTLE_CAST_SPELL }, + {"battleWait", EShortcut::BATTLE_WAIT }, + {"battleDefend", EShortcut::BATTLE_DEFEND }, + {"battleConsoleUp", EShortcut::BATTLE_CONSOLE_UP }, + {"battleConsoleDown", EShortcut::BATTLE_CONSOLE_DOWN }, + {"battleTacticsNext", EShortcut::BATTLE_TACTICS_NEXT }, + {"battleTacticsEnd", EShortcut::BATTLE_TACTICS_END }, + {"townOpenTavern", EShortcut::TOWN_OPEN_TAVERN }, + {"townSwapArmies", EShortcut::TOWN_SWAP_ARMIES }, + {"recruitmentMax", EShortcut::RECRUITMENT_MAX }, + {"recruitmentMin", EShortcut::RECRUITMENT_MIN }, + {"recruitmentUpgrade", EShortcut::RECRUITMENT_UPGRADE }, + {"recruitmentUpgradeAll", EShortcut::RECRUITMENT_UPGRADE_ALL }, + {"kingdomHeroes", EShortcut::KINGDOM_HEROES }, + {"kingdomTowns", EShortcut::KINGDOM_TOWNS }, + {"heroDismiss", EShortcut::HERO_DISMISS }, + {"heroCommander", EShortcut::HERO_COMMANDER }, + {"heroLooseFormation", EShortcut::HERO_LOOSE_FORMATION }, + {"heroTightFormation", EShortcut::HERO_TIGHT_FORMATION }, + {"heroToggleTactics", EShortcut::HERO_TOGGLE_TACTICS }, + {"spellbookTabAdventure", EShortcut::SPELLBOOK_TAB_ADVENTURE }, + {"spellbookTabCombat", EShortcut::SPELLBOOK_TAB_COMBAT } + }; + + if (shortcutNames.count(identifier)) + return shortcutNames.at(identifier); + return EShortcut::NONE; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/gui/ShortcutHandler.h b/client/gui/ShortcutHandler.h new file mode 100644 index 000000000..c88dc4ff4 --- /dev/null +++ b/client/gui/ShortcutHandler.h @@ -0,0 +1,24 @@ +/* + * ShortcutHandler.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 + +enum class EShortcut; +using SDL_Keycode = int32_t; + +class ShortcutHandler +{ +public: + /// returns list of shortcuts assigned to provided SDL keycode + std::vector translateKeycode(SDL_Keycode key); + + /// attempts to find shortcut by its unique identifier. Returns EShortcut::NONE on failure + EShortcut findShortcut(const std::string & identifier ); +}; diff --git a/client/lobby/CBonusSelection.cpp b/client/lobby/CBonusSelection.cpp index 595cb50b2..ef43218fc 100644 --- a/client/lobby/CBonusSelection.cpp +++ b/client/lobby/CBonusSelection.cpp @@ -33,6 +33,7 @@ #include "../render/IImage.h" #include "../render/CAnimation.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../../lib/filesystem/Filesystem.h" #include "../../lib/CGeneralTextHandler.h" @@ -67,9 +68,9 @@ CBonusSelection::CBonusSelection() panelBackground = std::make_shared("CAMPBRF.BMP", 456, 6); - buttonStart = std::make_shared(Point(475, 536), "CBBEGIB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::startMap, this), SDLK_RETURN); - buttonRestart = std::make_shared(Point(475, 536), "CBRESTB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::restartMap, this), SDLK_RETURN); - buttonBack = std::make_shared(Point(624, 536), "CBCANCB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::goBack, this), SDLK_ESCAPE); + buttonStart = std::make_shared(Point(475, 536), "CBBEGIB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::startMap, this), EShortcut::GLOBAL_CONFIRM); + buttonRestart = std::make_shared(Point(475, 536), "CBRESTB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::restartMap, this), EShortcut::GLOBAL_CONFIRM); + buttonBack = std::make_shared(Point(624, 536), "CBCANCB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::goBack, this), EShortcut::GLOBAL_CANCEL); campaignName = std::make_shared(481, 28, FONT_BIG, ETextAlignment::TOPLEFT, Colors::YELLOW, CSH->si->getCampaignName()); diff --git a/client/lobby/CLobbyScreen.cpp b/client/lobby/CLobbyScreen.cpp index 538fc303d..39dedcef6 100644 --- a/client/lobby/CLobbyScreen.cpp +++ b/client/lobby/CLobbyScreen.cpp @@ -17,6 +17,7 @@ #include "../CServerHandler.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/Buttons.h" #include "../windows/InfoWindows.h" @@ -40,17 +41,17 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType) { tabSel->callOnSelect = std::bind(&IServerAPI::setMapInfo, CSH, _1, nullptr); - buttonSelect = std::make_shared(Point(411, 80), "GSPBUTT.DEF", CGI->generaltexth->zelp[45], 0, SDLK_s); + buttonSelect = std::make_shared(Point(411, 80), "GSPBUTT.DEF", CGI->generaltexth->zelp[45], 0, EShortcut::LOBBY_SELECT); buttonSelect->addCallback([&]() { toggleTab(tabSel); CSH->setMapInfo(tabSel->getSelectedMapInfo()); }); - buttonOptions = std::make_shared(Point(411, 510), "GSPBUTT.DEF", CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabOpt), SDLK_a); + buttonOptions = std::make_shared(Point(411, 510), "GSPBUTT.DEF", CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabOpt), EShortcut::LOBBY_ADDITIONAL_OPTIONS); }; - buttonChat = std::make_shared(Point(619, 83), "GSPBUT2.DEF", CGI->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), SDLK_h); + buttonChat = std::make_shared(Point(619, 83), "GSPBUT2.DEF", CGI->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_HIDE_CHAT); buttonChat->addTextOverlay(CGI->generaltexth->allTexts[532], FONT_SMALL); switch(screenType) @@ -60,7 +61,7 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType) tabOpt = std::make_shared(); tabRand = std::make_shared(); tabRand->mapInfoChanged += std::bind(&IServerAPI::setMapInfo, CSH, _1, _2); - buttonRMG = std::make_shared(Point(411, 105), "GSPBUTT.DEF", CGI->generaltexth->zelp[47], 0, SDLK_r); + buttonRMG = std::make_shared(Point(411, 105), "GSPBUTT.DEF", CGI->generaltexth->zelp[47], 0, EShortcut::LOBBY_RANDOM_MAP); buttonRMG->addCallback([&]() { toggleTab(tabRand); @@ -69,30 +70,28 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType) card->iconDifficulty->addCallback(std::bind(&IServerAPI::setDifficulty, CSH, _1)); - buttonStart = std::make_shared(Point(411, 535), "SCNRBEG.DEF", CGI->generaltexth->zelp[103], std::bind(&CLobbyScreen::startScenario, this, true), SDLK_b); + buttonStart = std::make_shared(Point(411, 535), "SCNRBEG.DEF", CGI->generaltexth->zelp[103], std::bind(&CLobbyScreen::startScenario, this, true), EShortcut::LOBBY_BEGIN_GAME); initLobby(); break; } case ESelectionScreen::loadGame: { tabOpt = std::make_shared(); - buttonStart = std::make_shared(Point(411, 535), "SCNRLOD.DEF", CGI->generaltexth->zelp[103], std::bind(&CLobbyScreen::startScenario, this, true), SDLK_l); + buttonStart = std::make_shared(Point(411, 535), "SCNRLOD.DEF", CGI->generaltexth->zelp[103], std::bind(&CLobbyScreen::startScenario, this, true), EShortcut::LOBBY_LOAD_GAME); initLobby(); break; } case ESelectionScreen::campaignList: tabSel->callOnSelect = std::bind(&IServerAPI::setMapInfo, CSH, _1, nullptr); - buttonStart = std::make_shared(Point(411, 535), "SCNRLOD.DEF", CButton::tooltip(), std::bind(&CLobbyScreen::startCampaign, this), SDLK_b); + buttonStart = std::make_shared(Point(411, 535), "SCNRLOD.DEF", CButton::tooltip(), std::bind(&CLobbyScreen::startCampaign, this), EShortcut::LOBBY_BEGIN_GAME); break; } - buttonStart->assignedKeys.insert(SDLK_RETURN); - buttonBack = std::make_shared(Point(581, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], [&]() { CSH->sendClientDisconnecting(); close(); - }, SDLK_ESCAPE); + }, EShortcut::GLOBAL_CANCEL); } CLobbyScreen::~CLobbyScreen() diff --git a/client/lobby/CSavingScreen.cpp b/client/lobby/CSavingScreen.cpp index 59741d916..11392a6e6 100644 --- a/client/lobby/CSavingScreen.cpp +++ b/client/lobby/CSavingScreen.cpp @@ -15,6 +15,7 @@ #include "../CGameInfo.h" #include "../CPlayerInterface.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/Buttons.h" #include "../widgets/TextControls.h" @@ -39,8 +40,7 @@ CSavingScreen::CSavingScreen() tabSel->toggleMode(); tabSel->callOnSelect = std::bind(&CSavingScreen::changeSelection, this, _1); - buttonStart = std::make_shared(Point(411, 535), "SCNRSAV.DEF", CGI->generaltexth->zelp[103], std::bind(&CSavingScreen::saveGame, this), SDLK_s); - buttonStart->assignedKeys.insert(SDLK_RETURN); + buttonStart = std::make_shared(Point(411, 535), "SCNRSAV.DEF", CGI->generaltexth->zelp[103], std::bind(&CSavingScreen::saveGame, this), EShortcut::LOBBY_SAVE_GAME); } const CMapInfo * CSavingScreen::getMapInfo() diff --git a/client/lobby/CScenarioInfoScreen.cpp b/client/lobby/CScenarioInfoScreen.cpp index 6296f0b18..80ca9642f 100644 --- a/client/lobby/CScenarioInfoScreen.cpp +++ b/client/lobby/CScenarioInfoScreen.cpp @@ -15,6 +15,7 @@ #include "../CGameInfo.h" #include "../CPlayerInterface.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/Buttons.h" #include "../../CCallback.h" @@ -43,7 +44,7 @@ CScenarioInfoScreen::CScenarioInfoScreen() card->changeSelection(); card->iconDifficulty->setSelected(getCurrentDifficulty()); - buttonBack = std::make_shared(Point(584, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], [=](){ close();}, SDLK_ESCAPE); + buttonBack = std::make_shared(Point(584, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], [=](){ close();}, EShortcut::GLOBAL_CANCEL); } CScenarioInfoScreen::~CScenarioInfoScreen() diff --git a/client/lobby/CSelectionBase.cpp b/client/lobby/CSelectionBase.cpp index 0e561b6e6..6eba2941b 100644 --- a/client/lobby/CSelectionBase.cpp +++ b/client/lobby/CSelectionBase.cpp @@ -25,6 +25,7 @@ #include "../CPlayerInterface.h" #include "../CServerHandler.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../mainmenu/CMainMenu.h" #include "../widgets/CComponent.h" #include "../widgets/Buttons.h" @@ -84,7 +85,7 @@ CSelectionBase::CSelectionBase(ESelectionScreen type) } pos = background->center(); card = std::make_shared(); - buttonBack = std::make_shared(Point(581, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], [=](){ close();}, SDLK_ESCAPE); + buttonBack = std::make_shared(Point(581, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], [=](){ close();}, EShortcut::GLOBAL_CANCEL); } void CSelectionBase::toggleTab(std::shared_ptr tab) @@ -318,9 +319,9 @@ CChatBox::CChatBox(const Rect & rect) chatHistory->label->color = Colors::GREEN; } -void CChatBox::keyPressed(const SDL_Keycode & key) +void CChatBox::keyPressed(EShortcut key) { - if(key == SDLK_RETURN && inputBox->getText().size()) + if(key == EShortcut::GLOBAL_CONFIRM && inputBox->getText().size()) { CSH->sendMessage(inputBox->getText()); inputBox->setText(""); diff --git a/client/lobby/CSelectionBase.h b/client/lobby/CSelectionBase.h index fb219570e..412d0b08f 100644 --- a/client/lobby/CSelectionBase.h +++ b/client/lobby/CSelectionBase.h @@ -120,7 +120,7 @@ public: CChatBox(const Rect & rect); - void keyPressed(const SDL_Keycode & key) override; + void keyPressed(EShortcut key) override; void addNewMessage(const std::string & text); }; diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index 7a300d315..91382a0bf 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -17,6 +17,7 @@ #include "../CPlayerInterface.h" #include "../CServerHandler.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/CComponent.h" #include "../widgets/Buttons.h" #include "../widgets/MiscWidgets.h" @@ -281,27 +282,27 @@ void SelectionTab::clickLeft(tribool down, bool previousState) } } -void SelectionTab::keyPressed(const SDL_Keycode & key) +void SelectionTab::keyPressed(EShortcut key) { int moveBy = 0; switch(key) { - case SDLK_UP: + case EShortcut::SELECT_UP: moveBy = -1; break; - case SDLK_DOWN: + case EShortcut::SELECT_DOWN: moveBy = +1; break; - case SDLK_PAGEUP: + case EShortcut::SELECT_PAGE_UP: moveBy = -(int)listItems.size() + 1; break; - case SDLK_PAGEDOWN: + case EShortcut::SELECT_PAGE_DOWN: moveBy = +(int)listItems.size() - 1; break; - case SDLK_HOME: + case EShortcut::SELECT_FIRST: select(-slider->getValue()); return; - case SDLK_END: + case EShortcut::SELECT_LAST: select((int)curItems.size() - slider->getValue()); return; default: diff --git a/client/lobby/SelectionTab.h b/client/lobby/SelectionTab.h index d785a9a9e..097ef6ea6 100644 --- a/client/lobby/SelectionTab.h +++ b/client/lobby/SelectionTab.h @@ -66,7 +66,7 @@ public: void toggleMode(); void clickLeft(tribool down, bool previousState) override; - void keyPressed(const SDL_Keycode & key) override; + void keyPressed(EShortcut key) override; void onDoubleClick() override; diff --git a/client/mainmenu/CCampaignScreen.cpp b/client/mainmenu/CCampaignScreen.cpp index e89128b1e..b99231d5e 100644 --- a/client/mainmenu/CCampaignScreen.cpp +++ b/client/mainmenu/CCampaignScreen.cpp @@ -19,6 +19,7 @@ #include "../CPlayerInterface.h" #include "../CServerHandler.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/CComponent.h" #include "../widgets/Buttons.h" #include "../widgets/MiscWidgets.h" @@ -77,7 +78,7 @@ std::shared_ptr CCampaignScreen::createExitButton(const JsonNode & butt if(!button["help"].isNull() && button["help"].Float() > 0) help = CGI->generaltexth->zelp[(size_t)button["help"].Float()]; - return std::make_shared(Point((int)button["x"].Float(), (int)button["y"].Float()), button["name"].String(), help, [=](){ close();}, (int)button["hotkey"].Float()); + return std::make_shared(Point((int)button["x"].Float(), (int)button["y"].Float()), button["name"].String(), help, [=](){ close();}, EShortcut::GLOBAL_CANCEL); } CCampaignScreen::CCampaignButton::CCampaignButton(const JsonNode & config) diff --git a/client/mainmenu/CMainMenu.cpp b/client/mainmenu/CMainMenu.cpp index a2176f593..cf9f866d5 100644 --- a/client/mainmenu/CMainMenu.cpp +++ b/client/mainmenu/CMainMenu.cpp @@ -19,6 +19,8 @@ #include "../gui/CursorHandler.h" #include "../windows/GUIClasses.h" #include "../gui/CGuiHandler.h" +#include "../gui/ShortcutHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/CComponent.h" #include "../widgets/Buttons.h" #include "../widgets/MiscWidgets.h" @@ -230,7 +232,9 @@ std::shared_ptr CMenuEntry::createButton(CMenuScreen * parent, const Js if(posy < 0) posy = pos.h + posy; - auto result = std::make_shared(Point(posx, posy), button["name"].String(), help, command, (int)button["hotkey"].Float()); + EShortcut shortcut = GH.getShortcutsHandler().findShortcut(button["shortcut"].String()); + + auto result = std::make_shared(Point(posx, posy), button["name"].String(), help, command, shortcut); if (button["center"].Bool()) result->moveBy(Point(-result->pos.w/2, -result->pos.h/2)); @@ -382,7 +386,7 @@ CMultiMode::CMultiMode(ESelectionScreen ScreenType) buttonHotseat = std::make_shared(Point(373, 78), "MUBHOT.DEF", CGI->generaltexth->zelp[266], std::bind(&CMultiMode::hostTCP, this)); buttonHost = std::make_shared(Point(373, 78 + 57 * 1), "MUBHOST.DEF", CButton::tooltip("Host TCP/IP game", ""), std::bind(&CMultiMode::hostTCP, this)); buttonJoin = std::make_shared(Point(373, 78 + 57 * 2), "MUBJOIN.DEF", CButton::tooltip("Join TCP/IP game", ""), std::bind(&CMultiMode::joinTCP, this)); - buttonCancel = std::make_shared(Point(373, 424), "MUBCANC.DEF", CGI->generaltexth->zelp[288], [=](){ close();}, SDLK_ESCAPE); + buttonCancel = std::make_shared(Point(373, 424), "MUBCANC.DEF", CGI->generaltexth->zelp[288], [=](){ close();}, EShortcut::GLOBAL_CANCEL); } void CMultiMode::hostTCP() @@ -422,8 +426,8 @@ CMultiPlayers::CMultiPlayers(const std::string & firstPlayer, ESelectionScreen S inputNames[i]->cb += std::bind(&CMultiPlayers::onChange, this, _1); } - buttonOk = std::make_shared(Point(95, 338), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CMultiPlayers::enterSelectionScreen, this), SDLK_RETURN); - buttonCancel = std::make_shared(Point(205, 338), "MUBCANC.DEF", CGI->generaltexth->zelp[561], [=](){ close();}, SDLK_ESCAPE); + buttonOk = std::make_shared(Point(95, 338), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CMultiPlayers::enterSelectionScreen, this), EShortcut::GLOBAL_CONFIRM); + buttonCancel = std::make_shared(Point(205, 338), "MUBCANC.DEF", CGI->generaltexth->zelp[561], [=](){ close();}, EShortcut::GLOBAL_CANCEL); statusBar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(7, 381, 348, 18), 7, 381)); inputNames[0]->setText(firstPlayer, true); @@ -471,14 +475,14 @@ CSimpleJoinScreen::CSimpleJoinScreen(bool host) inputAddress->cb += std::bind(&CSimpleJoinScreen::onChange, this, _1); inputPort->cb += std::bind(&CSimpleJoinScreen::onChange, this, _1); inputPort->filters += std::bind(&CTextInput::numberFilter, _1, _2, 0, 65535); - buttonOk = std::make_shared(Point(26, 142), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CSimpleJoinScreen::connectToServer, this), SDLK_RETURN); + buttonOk = std::make_shared(Point(26, 142), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CSimpleJoinScreen::connectToServer, this), EShortcut::GLOBAL_CONFIRM); inputAddress->giveFocus(); } inputAddress->setText(host ? CServerHandler::localhostAddress : CSH->getHostAddress(), true); inputPort->setText(std::to_string(CSH->getHostPort()), true); - buttonCancel = std::make_shared(Point(142, 142), "MUBCANC.DEF", CGI->generaltexth->zelp[561], std::bind(&CSimpleJoinScreen::leaveScreen, this), SDLK_ESCAPE); + buttonCancel = std::make_shared(Point(142, 142), "MUBCANC.DEF", CGI->generaltexth->zelp[561], std::bind(&CSimpleJoinScreen::leaveScreen, this), EShortcut::GLOBAL_CANCEL); statusBar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(7, 186, 218, 18), 7, 186)); } diff --git a/client/widgets/Buttons.cpp b/client/widgets/Buttons.cpp index 10cabfd99..89d7c0428 100644 --- a/client/widgets/Buttons.cpp +++ b/client/widgets/Buttons.cpp @@ -19,6 +19,7 @@ #include "../battle/BattleInterface.h" #include "../battle/BattleInterfaceClasses.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../windows/InfoWindows.h" #include "../render/CAnimation.h" #include "../renderSDL/SDL_Extensions.h" @@ -223,7 +224,7 @@ void CButton::hover (bool on) } } -CButton::CButton(Point position, const std::string &defName, const std::pair &help, CFunctionList Callback, int key, bool playerColoredButton): +CButton::CButton(Point position, const std::string &defName, const std::pair &help, CFunctionList Callback, EShortcut key, bool playerColoredButton): CKeyShortcut(key), callback(Callback) { @@ -345,7 +346,7 @@ void CToggleBase::addCallback(std::function function) } CToggleButton::CToggleButton(Point position, const std::string &defName, const std::pair &help, - CFunctionList callback, int key, bool playerColoredButton): + CFunctionList callback, EShortcut key, bool playerColoredButton): CButton(position, defName, help, 0, key, playerColoredButton), CToggleBase(callback) { @@ -800,37 +801,37 @@ void CSlider::wheelScrolled(bool down, bool in) moveTo(value + 3 * (positive ? +scrollStep : -scrollStep)); } -void CSlider::keyPressed(const SDL_Keycode & key) +void CSlider::keyPressed(EShortcut key) { int moveDest = value; switch(key) { - case SDLK_UP: + case EShortcut::SELECT_UP: if (!horizontal) moveDest = value - scrollStep; break; - case SDLK_LEFT: + case EShortcut::SELECT_LEFT: if (horizontal) moveDest = value - scrollStep; break; - case SDLK_DOWN: + case EShortcut::SELECT_DOWN: if (!horizontal) moveDest = value + scrollStep; break; - case SDLK_RIGHT: + case EShortcut::SELECT_RIGHT: if (horizontal) moveDest = value + scrollStep; break; - case SDLK_PAGEUP: + case EShortcut::SELECT_PAGE_UP: moveDest = value - capacity + scrollStep; break; - case SDLK_PAGEDOWN: + case EShortcut::SELECT_PAGE_DOWN: moveDest = value + capacity - scrollStep; break; - case SDLK_HOME: + case EShortcut::SELECT_FIRST: moveDest = 0; break; - case SDLK_END: + case EShortcut::SELECT_LAST: moveDest = amount - capacity; break; default: diff --git a/client/widgets/Buttons.h b/client/widgets/Buttons.h index 67665cfbd..b897fff67 100644 --- a/client/widgets/Buttons.h +++ b/client/widgets/Buttons.h @@ -101,7 +101,7 @@ public: /// Constructor CButton(Point position, const std::string & defName, const std::pair & help, - CFunctionList Callback = 0, int key=0, bool playerColoredButton = false ); + CFunctionList Callback = 0, EShortcut key = {}, bool playerColoredButton = false ); /// Appearance modifiers void setIndex(size_t index, bool playerColoredButton=false); @@ -157,7 +157,7 @@ class CToggleButton : public CButton, public CToggleBase public: CToggleButton(Point position, const std::string &defName, const std::pair &help, - CFunctionList Callback = 0, int key=0, bool playerColoredButton = false ); + CFunctionList Callback = 0, EShortcut key = {}, bool playerColoredButton = false ); void clickLeft(tribool down, bool previousState) override; // bring overrides into scope @@ -276,7 +276,7 @@ public: void addCallback(std::function callback); - void keyPressed(const SDL_Keycode & key) override; + void keyPressed(EShortcut key) override; void wheelScrolled(bool down, bool in) override; void clickLeft(tribool down, bool previousState) override; void mouseMoved (const Point & cursorPosition) override; diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index 9b794090b..39e9475c0 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -11,6 +11,7 @@ #include "CArtifactHolder.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "CComponent.h" diff --git a/client/widgets/CArtifactsOfHeroBase.cpp b/client/widgets/CArtifactsOfHeroBase.cpp index 0f51ad8ad..eae959a03 100644 --- a/client/widgets/CArtifactsOfHeroBase.cpp +++ b/client/widgets/CArtifactsOfHeroBase.cpp @@ -12,6 +12,7 @@ #include "../gui/CGuiHandler.h" #include "../gui/CursorHandler.h" +#include "../gui/EShortcut.h #include "Buttons.h" @@ -82,8 +83,8 @@ void CArtifactsOfHeroBase::init( 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 = std::make_shared(Point(379, 364), "hsbtns3.def", CButton::tooltip(), [scrollHandler]() { scrollHandler(-1); }, EShortcut::SELECT_LEFT); + rightBackpackRoll = std::make_shared(Point(632, 364), "hsbtns5.def", CButton::tooltip(), [scrollHandler]() { scrollHandler(+1); }, EShortcut::SELECT_RIGHT); leftBackpackRoll->block(true); rightBackpackRoll->block(true); } diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index b88ffaf9c..a75334d7e 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -19,6 +19,7 @@ #include "../gui/CGuiHandler.h" #include "../gui/CursorHandler.h" #include "../gui/TextAlignment.h" +#include "../gui/Shortcut.h" #include "../renderSDL/SDL_Extensions.h" #include "../windows/CMessage.h" #include "../windows/InfoWindows.h" @@ -488,11 +489,12 @@ CComponentBox::CComponentBox(std::vector> assert(!components.empty()); - int key = SDLK_1; + auto key = EShortcut::SELECT_INDEX_1; for(auto & comp : _components) { comp->onSelect = std::bind(&CComponentBox::selectionChanged, this, comp); - comp->assignedKeys.insert(key++); + comp->assignedKey = key; + vstd::advance(key, 1); } selectionChanged(_components.front()); } diff --git a/client/widgets/TextControls.cpp b/client/widgets/TextControls.cpp index 783615fcd..eb677c29d 100644 --- a/client/widgets/TextControls.cpp +++ b/client/widgets/TextControls.cpp @@ -15,6 +15,7 @@ #include "../CPlayerInterface.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../windows/CMessage.h" #include "../adventureMap/CInGameConsole.h" #include "../renderSDL/SDL_Extensions.h" @@ -555,12 +556,12 @@ void CTextInput::clickLeft(tribool down, bool previousState) giveFocus(); } -void CTextInput::keyPressed(const SDL_Keycode & key) +void CTextInput::keyPressed(EShortcut key) { if(!focus) return; - if(key == SDLK_TAB) + if(key == EShortcut::GLOBAL_MOVE_FOCUS) { moveFocus(); GH.breakEventHandling(); @@ -571,9 +572,7 @@ void CTextInput::keyPressed(const SDL_Keycode & key) switch(key) { - case SDLK_DELETE: // have index > ' ' so it won't be filtered out by default section - return; - case SDLK_BACKSPACE: + case EShortcut::GLOBAL_BACKSPACE: if(!newText.empty()) { TextOperations::trimRightUnicode(newText); @@ -608,9 +607,9 @@ void CTextInput::setText(const std::string & nText, bool callCb) cb(text); } -bool CTextInput::captureThisKey(const SDL_Keycode & key) +bool CTextInput::captureThisKey(EShortcut key) { - if(key == SDLK_RETURN || key == SDLK_KP_ENTER || key == SDLK_ESCAPE) + if(key == EShortcut::GLOBAL_RETURN) return false; return true; diff --git a/client/widgets/TextControls.h b/client/widgets/TextControls.h index 729d3c814..9419671c6 100644 --- a/client/widgets/TextControls.h +++ b/client/widgets/TextControls.h @@ -225,9 +225,9 @@ public: CTextInput(const Rect & Pos, std::shared_ptr srf); void clickLeft(tribool down, bool previousState) override; - void keyPressed(const SDL_Keycode & key) override; + void keyPressed(EShortcut key) override; - bool captureThisKey(const SDL_Keycode & key) override; + bool captureThisKey(EShortcut key) override; void textInputed(const std::string & enteredText) override; void textEdited(const std::string & enteredText) override; diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 1dadf844a..08f93bedf 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -21,6 +21,7 @@ #include "../CPlayerInterface.h" #include "../PlayerLocalState.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/MiscWidgets.h" #include "../widgets/CComponent.h" #include "../widgets/Buttons.h" @@ -1184,8 +1185,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst income = std::make_shared(195, 443, FONT_SMALL, ETextAlignment::CENTER); icon = std::make_shared("ITPT", 0, 0, 15, 387); - exit = std::make_shared(Point(744, 544), "TSBTNS", CButton::tooltip(CGI->generaltexth->tcommands[8]), [&](){close();}, SDLK_RETURN); - exit->assignedKeys.insert(SDLK_ESCAPE); + exit = std::make_shared(Point(744, 544), "TSBTNS", CButton::tooltip(CGI->generaltexth->tcommands[8]), [&](){close();}, EShortcut::GLOBAL_RETURN); exit->setImageOrder(4, 5, 6, 7); auto split = std::make_shared(Point(744, 382), "TSBTNS", CButton::tooltip(CGI->generaltexth->tcommands[3]), [&]() @@ -1308,20 +1308,20 @@ void CCastleInterface::recreateIcons() } -void CCastleInterface::keyPressed(const SDL_Keycode & key) +void CCastleInterface::keyPressed(EShortcut key) { switch(key) { - case SDLK_UP: + case EShortcut::SELECT_UP: townlist->selectPrev(); break; - case SDLK_DOWN: + case EShortcut::SELECT_DOWN: townlist->selectNext(); break; - case SDLK_SPACE: + case EShortcut::TOWN_SWAP_ARMIES: heroes->swapArmies(); break; - case SDLK_t: + case EShortcut::TOWN_OPEN_TAVERN: if(town->hasBuilt(BuildingID::TAVERN)) LOCPLINT->showTavernWindow(town); break; @@ -1420,8 +1420,7 @@ CHallInterface::CHallInterface(const CGTownInstance * Town): statusbar = CGStatusBar::create(statusbarBackground); title = std::make_shared(399, 12, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, town->town->buildings.at(BuildingID(town->hallLevel()+BuildingID::VILLAGE_HALL))->getNameTranslated()); - exit = std::make_shared(Point(748, 556), "TPMAGE1.DEF", CButton::tooltip(CGI->generaltexth->hcommands[8]), [&](){close();}, SDLK_RETURN); - exit->assignedKeys.insert(SDLK_ESCAPE); + exit = std::make_shared(Point(748, 556), "TPMAGE1.DEF", CButton::tooltip(CGI->generaltexth->hcommands[8]), [&](){close();}, EShortcut::GLOBAL_RETURN); auto & boxList = town->town->clientInfo.hallSlots; boxes.resize(boxList.size()); @@ -1486,11 +1485,11 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin std::string tooltipYes = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % building->getNameTranslated()); std::string tooltipNo = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % building->getNameTranslated()); - buy = std::make_shared(Point(45, 446), "IBUY30", CButton::tooltip(tooltipYes), [&](){ buyFunc(); }, SDLK_RETURN); + buy = std::make_shared(Point(45, 446), "IBUY30", CButton::tooltip(tooltipYes), [&](){ buyFunc(); }, EShortcut::GLOBAL_CONFIRM); buy->setBorderColor(Colors::METALLIC_GOLD); buy->block(state!=7 || LOCPLINT->playerID != town->tempOwner); - cancel = std::make_shared(Point(290, 445), "ICANCEL", CButton::tooltip(tooltipNo), [&](){ close();}, SDLK_ESCAPE); + cancel = std::make_shared(Point(290, 445), "ICANCEL", CButton::tooltip(tooltipNo), [&](){ close();}, EShortcut::GLOBAL_CANCEL); cancel->setBorderColor(Colors::METALLIC_GOLD); } } @@ -1596,8 +1595,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town): title = std::make_shared(400, 12, FONT_BIG, ETextAlignment::CENTER, Colors::WHITE, fortBuilding->getNameTranslated()); std::string text = boost::str(boost::format(CGI->generaltexth->fcommands[6]) % fortBuilding->getNameTranslated()); - exit = std::make_shared(Point(748, 556), "TPMAGE1", CButton::tooltip(text), [&](){ close(); }, SDLK_RETURN); - exit->assignedKeys.insert(SDLK_ESCAPE); + exit = std::make_shared(Point(748, 556), "TPMAGE1", CButton::tooltip(text), [&](){ close(); }, EShortcut::GLOBAL_RETURN); std::vector positions = { @@ -1785,8 +1783,7 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner,std::string imagem) auto statusbarBackground = std::make_shared(background->getSurface(), barRect, 7, 556); statusbar = CGStatusBar::create(statusbarBackground); - exit = std::make_shared(Point(748, 556), "TPMAGE1.DEF", CButton::tooltip(CGI->generaltexth->allTexts[593]), [&](){ close(); }, SDLK_RETURN); - exit->assignedKeys.insert(SDLK_ESCAPE); + exit = std::make_shared(Point(748, 556), "TPMAGE1.DEF", CButton::tooltip(CGI->generaltexth->allTexts[593]), [&](){ close(); }, EShortcut::GLOBAL_RETURN); static const std::vector > positions = { @@ -1866,10 +1863,10 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art std::to_string(aid.toArtifact(CGI->artifacts())->getPrice())); std::string text = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % creature->getNameSingularTranslated()); - buy = std::make_shared(Point(42, 312), "IBUY30.DEF", CButton::tooltip(text), [&](){ close(); }, SDLK_RETURN); + buy = std::make_shared(Point(42, 312), "IBUY30.DEF", CButton::tooltip(text), [&](){ close(); }, EShortcut::GLOBAL_CONFIRM); text = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % creature->getNameSingularTranslated()); - cancel = std::make_shared(Point(224, 312), "ICANCEL.DEF", CButton::tooltip(text), [&](){ close(); }, SDLK_ESCAPE); + cancel = std::make_shared(Point(224, 312), "ICANCEL.DEF", CButton::tooltip(text), [&](){ close(); }, EShortcut::GLOBAL_CANCEL); if(possible) buy->addCallback([=](){ LOCPLINT->cb->buyArtifact(LOCPLINT->cb->getHero(hid),aid); }); diff --git a/client/windows/CCastleInterface.h b/client/windows/CCastleInterface.h index f690af9dd..55fa2f29f 100644 --- a/client/windows/CCastleInterface.h +++ b/client/windows/CCastleInterface.h @@ -246,7 +246,7 @@ public: void castleTeleport(int where); void townChange(); - void keyPressed(const SDL_Keycode & key) override; + void keyPressed(EShortcut key) override; void close(); void addBuilding(BuildingID bid); diff --git a/client/windows/CCreatureWindow.cpp b/client/windows/CCreatureWindow.cpp index 40a166ea0..6aca3b1fb 100644 --- a/client/windows/CCreatureWindow.cpp +++ b/client/windows/CCreatureWindow.cpp @@ -22,6 +22,7 @@ #include "../widgets/TextControls.h" #include "../widgets/ObjectLists.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../renderSDL/SDL_Extensions.h" #include "../../CCallback.h" @@ -284,7 +285,7 @@ CStackWindow::ButtonsSection::ButtonsSection(CStackWindow * owner, int yOffset) { LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[12], onDismiss, nullptr); }; - dismiss = std::make_shared(Point(5, 5),"IVIEWCR2.DEF", CGI->generaltexth->zelp[445], onClick, SDLK_d); + dismiss = std::make_shared(Point(5, 5),"IVIEWCR2.DEF", CGI->generaltexth->zelp[445], onClick, EShortcut::HERO_DISMISS); } if(parent->info->upgradeInfo && !parent->info->commander) @@ -321,14 +322,12 @@ CStackWindow::ButtonsSection::ButtonsSection(CStackWindow * owner, int yOffset) LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[314], resComps); } }; - auto upgradeBtn = std::make_shared(Point(221 + (int)buttonIndex * 40, 5), "stackWindow/upgradeButton", CGI->generaltexth->zelp[446], onClick, SDLK_1); + auto upgradeBtn = std::make_shared(Point(221 + (int)buttonIndex * 40, 5), "stackWindow/upgradeButton", CGI->generaltexth->zelp[446], onClick); upgradeBtn->addOverlay(std::make_shared("CPRSMALL", VLC->creh->objects[upgradeInfo.info.newID[buttonIndex]]->getIconIndex())); if(buttonsToCreate == 1) // single upgrade avaialbe - { - upgradeBtn->assignedKeys.insert(SDLK_u); - } + upgradeBtn->assignedKey = EShortcut::RECRUITMENT_UPGRADE; upgrade[buttonIndex] = upgradeBtn; } @@ -356,8 +355,7 @@ CStackWindow::ButtonsSection::ButtonsSection(CStackWindow * owner, int yOffset) parent->switchButtons[parent->activeTab]->disable(); } - exit = std::make_shared(Point(382, 5), "hsbtns.def", CGI->generaltexth->zelp[447], [=](){ parent->close(); }, SDLK_RETURN); - exit->assignedKeys.insert(SDLK_ESCAPE); + exit = std::make_shared(Point(382, 5), "hsbtns.def", CGI->generaltexth->zelp[447], [=](){ parent->close(); }, EShortcut::GLOBAL_RETURN); } CStackWindow::CommanderMainSection::CommanderMainSection(CStackWindow * owner, int yOffset) @@ -457,8 +455,8 @@ CStackWindow::CommanderMainSection::CommanderMainSection(CStackWindow * owner, i abilities = std::make_shared(onCreate, Point(38, 3+pos.h), Point(63, 0), 6, abilitiesCount); - leftBtn = std::make_shared(Point(10, pos.h + 6), "hsbtns3.def", CButton::tooltip(), [=](){ abilities->moveToPrev(); }, SDLK_LEFT); - rightBtn = std::make_shared(Point(411, pos.h + 6), "hsbtns5.def", CButton::tooltip(), [=](){ abilities->moveToNext(); }, SDLK_RIGHT); + leftBtn = std::make_shared(Point(10, pos.h + 6), "hsbtns3.def", CButton::tooltip(), [=](){ abilities->moveToPrev(); }, EShortcut::SELECT_LEFT); + rightBtn = std::make_shared(Point(411, pos.h + 6), "hsbtns5.def", CButton::tooltip(), [=](){ abilities->moveToNext(); }, EShortcut::SELECT_RIGHT); if(abilitiesCount <= 6) { diff --git a/client/windows/CHeroWindow.cpp b/client/windows/CHeroWindow.cpp index 3a91a0ede..7b350f76e 100644 --- a/client/windows/CHeroWindow.cpp +++ b/client/windows/CHeroWindow.cpp @@ -19,6 +19,7 @@ #include "../gui/CGuiHandler.h" #include "../gui/TextAlignment.h" +#include "../gui/Shortcut.h" #include "../widgets/MiscWidgets.h" #include "../widgets/CComponent.h" #include "../widgets/TextControls.h" @@ -119,22 +120,21 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero) statusbar = CGStatusBar::create(7, 559, "ADROLLVR.bmp", 660); - quitButton = std::make_shared(Point(609, 516), "hsbtns.def", CButton::tooltip(heroscrn[17]), [=](){ close(); }, SDLK_RETURN); - quitButton->assignedKeys.insert(SDLK_ESCAPE); + quitButton = std::make_shared(Point(609, 516), "hsbtns.def", CButton::tooltip(heroscrn[17]), [=](){ close(); }, EShortcut::GLOBAL_RETURN); dismissLabel = std::make_shared(CGI->generaltexth->jktexts[8], Rect(370, 430, 65, 35), 0, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE); - dismissButton = std::make_shared(Point(454, 429), "hsbtns2.def", CButton::tooltip(heroscrn[28]), [=](){ dismissCurrent(); }, SDLK_d); + dismissButton = std::make_shared(Point(454, 429), "hsbtns2.def", CButton::tooltip(heroscrn[28]), [=](){ dismissCurrent(); }, EShortcut::HERO_DISMISS); questlogLabel = std::make_shared(CGI->generaltexth->jktexts[9], Rect(510, 430, 65, 35), 0, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE); - questlogButton = std::make_shared(Point(314, 429), "hsbtns4.def", CButton::tooltip(heroscrn[0]), [=](){ LOCPLINT->showQuestLog(); }, SDLK_q); + questlogButton = std::make_shared(Point(314, 429), "hsbtns4.def", CButton::tooltip(heroscrn[0]), [=](){ LOCPLINT->showQuestLog(); }, EShortcut::ADVENTURE_QUEST_LOG); formations = std::make_shared(0); - formations->addToggle(0, std::make_shared(Point(481, 483), "hsbtns6.def", std::make_pair(heroscrn[23], heroscrn[29]), 0, SDLK_t)); - formations->addToggle(1, std::make_shared(Point(481, 519), "hsbtns7.def", std::make_pair(heroscrn[24], heroscrn[30]), 0, SDLK_l)); + formations->addToggle(0, std::make_shared(Point(481, 483), "hsbtns6.def", std::make_pair(heroscrn[23], heroscrn[29]), 0, EShortcut::HERO_TIGHT_FORMATION)); + formations->addToggle(1, std::make_shared(Point(481, 519), "hsbtns7.def", std::make_pair(heroscrn[24], heroscrn[30]), 0, EShortcut::HERO_LOOSE_FORMATION)); if(hero->commander) { - commanderButton = std::make_shared(Point(317, 18), "buttons/commander", CButton::tooltipLocalized("vcmi.heroWindow.openCommander"), [&](){ commanderWindow(); }, SDLK_c); + commanderButton = std::make_shared(Point(317, 18), "buttons/commander", CButton::tooltipLocalized("vcmi.heroWindow.openCommander"), [&](){ commanderWindow(); }, EShortcut::HERO_COMMANDER); } //right list of heroes @@ -224,7 +224,7 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded) specImage->setFrame(curHero->type->imageIndex); specName->setText(curHero->type->getSpecialtyNameTranslated()); - tacticsButton = std::make_shared(Point(539, 483), "hsbtns8.def", std::make_pair(heroscrn[26], heroscrn[31]), 0, SDLK_b); + tacticsButton = std::make_shared(Point(539, 483), "hsbtns8.def", std::make_pair(heroscrn[26], heroscrn[31]), 0, EShortcut::HERO_TOGGLE_TACTICS); tacticsButton->addHoverText(CButton::HIGHLIGHTED, CGI->generaltexth->heroscrn[25]); dismissButton->addHoverText(CButton::NORMAL, boost::str(boost::format(CGI->generaltexth->heroscrn[16]) % curHero->getNameTranslated() % curHero->type->heroClass->getNameTranslated())); diff --git a/client/windows/CKingdomInterface.cpp b/client/windows/CKingdomInterface.cpp index ee9144f9c..bcb91c921 100644 --- a/client/windows/CKingdomInterface.cpp +++ b/client/windows/CKingdomInterface.cpp @@ -17,6 +17,7 @@ #include "../CPlayerInterface.h" #include "../adventureMap/CResDataBar.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/CComponent.h" #include "../widgets/TextControls.h" #include "../widgets/MiscWidgets.h" @@ -613,15 +614,14 @@ void CKingdomInterface::generateButtons() //Main control buttons btnHeroes = std::make_shared(Point(748, 28+footerPos), "OVBUTN1.DEF", CButton::tooltip(CGI->generaltexth->overview[11], CGI->generaltexth->overview[6]), - std::bind(&CKingdomInterface::activateTab, this, 0), SDLK_h); + std::bind(&CKingdomInterface::activateTab, this, 0), EShortcut::KINGDOM_HEROES); btnHeroes->block(true); btnTowns = std::make_shared(Point(748, 64+footerPos), "OVBUTN6.DEF", CButton::tooltip(CGI->generaltexth->overview[12], CGI->generaltexth->overview[7]), - std::bind(&CKingdomInterface::activateTab, this, 1), SDLK_t); + std::bind(&CKingdomInterface::activateTab, this, 1), EShortcut::KINGDOM_TOWNS); btnExit = std::make_shared(Point(748,99+footerPos), "OVBUTN1.DEF", CButton::tooltip(CGI->generaltexth->allTexts[600]), - std::bind(&CKingdomInterface::close, this), SDLK_RETURN); - btnExit->assignedKeys.insert(SDLK_ESCAPE); + std::bind(&CKingdomInterface::close, this), EShortcut::GLOBAL_RETURN); btnExit->setImageOrder(3, 4, 5, 6); //Object list control buttons diff --git a/client/windows/CPuzzleWindow.cpp b/client/windows/CPuzzleWindow.cpp index 36e16e2c7..529350501 100644 --- a/client/windows/CPuzzleWindow.cpp +++ b/client/windows/CPuzzleWindow.cpp @@ -16,6 +16,7 @@ #include "../adventureMap/CResDataBar.h" #include "../gui/CGuiHandler.h" #include "../gui/TextAlignment.h" +#include "../gui/Shortcut.h" #include "../mapView/MapView.h" #include "../widgets/Buttons.h" #include "../widgets/Images.h" @@ -35,8 +36,7 @@ CPuzzleWindow::CPuzzleWindow(const int3 & GrailPos, double discoveredRatio) CCS->soundh->playSound(soundBase::OBELISK); - quitb = std::make_shared(Point(670, 538), "IOK6432.DEF", CButton::tooltip(CGI->generaltexth->allTexts[599]), std::bind(&CPuzzleWindow::close, this), SDLK_RETURN); - quitb->assignedKeys.insert(SDLK_ESCAPE); + quitb = std::make_shared(Point(670, 538), "IOK6432.DEF", CButton::tooltip(CGI->generaltexth->allTexts[599]), std::bind(&CPuzzleWindow::close, this), EShortcut::GLOBAL_RETURN); quitb->setBorderColor(Colors::METALLIC_GOLD); mapView = std::make_shared(Point(8,9), Point(591, 544), grailPos); diff --git a/client/windows/CQuestLog.cpp b/client/windows/CQuestLog.cpp index ea0bcb8b5..ade6ec981 100644 --- a/client/windows/CQuestLog.cpp +++ b/client/windows/CQuestLog.cpp @@ -14,6 +14,7 @@ #include "../CPlayerInterface.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/CComponent.h" #include "../adventureMap/CAdventureMapInterface.h" #include "../widgets/Buttons.h" @@ -127,7 +128,7 @@ CQuestLog::CQuestLog (const std::vector & Quests) minimap = std::make_shared(Rect(12, 12, 169, 169)); // TextBox have it's own 4 pixel padding from top at least for English. To achieve 10px from both left and top only add 6px margin description = std::make_shared("", Rect(205, 18, 385, DESCRIPTION_HEIGHT_MAX), CSlider::BROWN, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE); - ok = std::make_shared(Point(539, 398), "IOKAY.DEF", CGI->generaltexth->zelp[445], std::bind(&CQuestLog::close, this), SDLK_RETURN); + ok = std::make_shared(Point(539, 398), "IOKAY.DEF", CGI->generaltexth->zelp[445], std::bind(&CQuestLog::close, this), EShortcut::GLOBAL_CONFIRM); // Both button and lable are shifted to -2px by x and y to not make them actually look like they're on same line with quests list and ok button hideCompleteButton = std::make_shared(Point(10, 396), "sysopchk.def", CButton::tooltipLocalized("vcmi.questLog.hideComplete"), std::bind(&CQuestLog::toggleComplete, this, _1)); hideCompleteLabel = std::make_shared(46, 398, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("vcmi.questLog.hideComplete.hover")); diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index e27eecfec..3e441934d 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -23,6 +23,7 @@ #include "../battle/BattleInterface.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../widgets/MiscWidgets.h" #include "../widgets/CComponent.h" #include "../widgets/TextControls.h" @@ -409,27 +410,24 @@ void CSpellWindow::turnPageRight() CCS->videoh->openAndPlayVideo("PGTRNRGH.SMK", pos.x+13, pos.y+15); } -void CSpellWindow::keyPressed(const SDL_Keycode & key) +void CSpellWindow::keyPressed(EShortcut key) { - if(key == SDLK_ESCAPE || key == SDLK_RETURN) + switch(key) { - fexitb(); - return; - } - else - { - switch(key) - { - case SDLK_LEFT: + case EShortcut::GLOBAL_RETURN: + fexitb(); + break; + + case EShortcut::SELECT_LEFT: fLcornerb(); break; - case SDLK_RIGHT: + case EShortcut::SELECT_RIGHT: fRcornerb(); break; - case SDLK_UP: - case SDLK_DOWN: + case EShortcut::SELECT_UP: + case EShortcut::SELECT_DOWN: { - bool down = key == SDLK_DOWN; + bool down = key == EShortcut::SELECT_DOWN; static const int schoolsOrder[] = { 0, 3, 1, 2, 4 }; int index = -1; while(schoolsOrder[++index] != selectedTab); @@ -439,38 +437,12 @@ void CSpellWindow::keyPressed(const SDL_Keycode & key) selectSchool(schoolsOrder[index]); break; } - case SDLK_c: + case EShortcut::SPELLBOOK_TAB_COMBAT: fbattleSpellsb(); break; - case SDLK_a: + case EShortcut::SPELLBOOK_TAB_ADVENTURE: fadvSpellsb(); break; - default://to get rid of warnings - break; - } - - //alt + 1234567890-= casts spell from 1 - 12 slot - if(GH.isKeyboardAltDown()) - { - SDL_Keycode hlpKey = key; - if(CGuiHandler::isNumKey(hlpKey, false)) - { - if(hlpKey == SDLK_KP_PLUS) - hlpKey = SDLK_EQUALS; - else - hlpKey = CGuiHandler::numToDigit(hlpKey); - } - - static const SDL_Keycode spellSelectors[] = {SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6, SDLK_7, SDLK_8, SDLK_9, SDLK_0, SDLK_MINUS, SDLK_EQUALS}; - - int index = -1; - while(++index < std::size(spellSelectors) && spellSelectors[index] != hlpKey); - if(index >= std::size(spellSelectors)) - return; - - //try casting spell - spellAreas[index]->clickLeft(false, true); - } } } diff --git a/client/windows/CSpellWindow.h b/client/windows/CSpellWindow.h index 51f93caee..f6ce9db5b 100644 --- a/client/windows/CSpellWindow.h +++ b/client/windows/CSpellWindow.h @@ -112,7 +112,7 @@ public: void selectSchool(int school); //schools: 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic, 4 - all schools int pagesWithinCurrentTab(); - void keyPressed(const SDL_Keycode & key) override; + void keyPressed(EShortcut key) override; void show(SDL_Surface * to) override; }; diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index d994b74e6..7ad341895 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -15,6 +15,7 @@ #include "../widgets/Images.h" #include "../renderSDL/SDL_Extensions.h" #include "../gui/TextAlignment.h" +#include "../gui/Shortcut.h" #include "../widgets/Buttons.h" #include "../widgets/TextControls.h" #include "../windows/InfoWindows.h" @@ -728,8 +729,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta initItems(false); initItems(true); - ok = std::make_shared(Point(516, 520), "IOK6432.DEF", CGI->generaltexth->zelp[600], [&](){ close(); }, SDLK_RETURN); - ok->assignedKeys.insert(SDLK_ESCAPE); + ok = std::make_shared(Point(516, 520), "IOK6432.DEF", CGI->generaltexth->zelp[600], [&](){ close(); }, EShortcut::GLOBAL_RETURN); deal = std::make_shared(Point(307, 520), "TPMRKB.DEF", CGI->generaltexth->zelp[595], [&](){ makeDeal(); } ); deal->block(true); @@ -1170,8 +1170,7 @@ CAltarWindow::CAltarWindow(const IMarket * Market, const CGHeroInstance * Hero, statusBar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); - ok = std::make_shared(Point(516, 520), "IOK6432.DEF", CGI->generaltexth->zelp[568], [&](){ close();}, SDLK_RETURN); - ok->assignedKeys.insert(SDLK_ESCAPE); + ok = std::make_shared(Point(516, 520), "IOK6432.DEF", CGI->generaltexth->zelp[568], [&](){ close();}, EShortcut::GLOBAL_RETURN); deal = std::make_shared(Point(269, 520), "ALTSACR.DEF", CGI->generaltexth->zelp[585], std::bind(&CAltarWindow::makeDeal,this)); diff --git a/client/windows/CreaturePurchaseCard.cpp b/client/windows/CreaturePurchaseCard.cpp index d86504776..7a8d364f5 100644 --- a/client/windows/CreaturePurchaseCard.cpp +++ b/client/windows/CreaturePurchaseCard.cpp @@ -15,6 +15,7 @@ #include "CCreatureWindow.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../gui/TextAlignment.h" #include "../widgets/Buttons.h" #include "../widgets/TextControls.h" @@ -32,12 +33,12 @@ void CreaturePurchaseCard::initButtons() void CreaturePurchaseCard::initMaxButton() { - maxButton = std::make_shared(Point(pos.x + 52, pos.y + 180), "QuickRecruitmentWindow/QuickRecruitmentAllButton.def", CButton::tooltip(), std::bind(&CSlider::moveToMax,slider), SDLK_LSHIFT); + maxButton = std::make_shared(Point(pos.x + 52, pos.y + 180), "QuickRecruitmentWindow/QuickRecruitmentAllButton.def", CButton::tooltip(), std::bind(&CSlider::moveToMax,slider), EShortcut::RECRUITMENT_MAX); } void CreaturePurchaseCard::initMinButton() { - minButton = std::make_shared(Point(pos.x, pos.y + 180), "QuickRecruitmentWindow/QuickRecruitmentNoneButton.def", CButton::tooltip(), std::bind(&CSlider::moveToMin,slider), SDLK_LCTRL); + minButton = std::make_shared(Point(pos.x, pos.y + 180), "QuickRecruitmentWindow/QuickRecruitmentNoneButton.def", CButton::tooltip(), std::bind(&CSlider::moveToMin,slider), EShortcut::RECRUITMENT_MIN); } void CreaturePurchaseCard::initCreatureSwitcherButton() diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 7f65add98..629f1ce3d 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -28,6 +28,7 @@ #include "../gui/CGuiHandler.h" #include "../gui/CursorHandler.h" #include "../gui/TextAlignment.h" +#include "../gui/Shortcut.h" #include "../widgets/CComponent.h" #include "../widgets/MiscWidgets.h" @@ -210,9 +211,9 @@ CRecruitmentWindow::CRecruitmentWindow(const CGDwelling * Dwelling, int Level, c slider = std::make_shared(Point(176,279),135,std::bind(&CRecruitmentWindow::sliderMoved,this, _1),0,0,0,true); - maxButton = std::make_shared(Point(134, 313), "IRCBTNS.DEF", CGI->generaltexth->zelp[553], std::bind(&CSlider::moveToMax, slider), SDLK_m); - buyButton = std::make_shared(Point(212, 313), "IBY6432.DEF", CGI->generaltexth->zelp[554], std::bind(&CRecruitmentWindow::buy, this), SDLK_RETURN); - cancelButton = std::make_shared(Point(290, 313), "ICN6432.DEF", CGI->generaltexth->zelp[555], std::bind(&CRecruitmentWindow::close, this), SDLK_ESCAPE); + maxButton = std::make_shared(Point(134, 313), "IRCBTNS.DEF", CGI->generaltexth->zelp[553], std::bind(&CSlider::moveToMax, slider), EShortcut::RECRUITMENT_MAX); + buyButton = std::make_shared(Point(212, 313), "IBY6432.DEF", CGI->generaltexth->zelp[554], std::bind(&CRecruitmentWindow::buy, this), EShortcut::GLOBAL_CONFIRM); + cancelButton = std::make_shared(Point(290, 313), "ICN6432.DEF", CGI->generaltexth->zelp[555], std::bind(&CRecruitmentWindow::close, this), EShortcut::GLOBAL_CANCEL); title = std::make_shared(243, 32, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW); availableValue = std::make_shared(205, 253, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); @@ -313,8 +314,8 @@ CSplitWindow::CSplitWindow(const CCreature * creature, std::function(Point(20, 263), "IOK6432", CButton::tooltip(), std::bind(&CSplitWindow::apply, this), SDLK_RETURN); - cancel = std::make_shared(Point(214, 263), "ICN6432", CButton::tooltip(), std::bind(&CSplitWindow::close, this), SDLK_ESCAPE); + ok = std::make_shared(Point(20, 263), "IOK6432", CButton::tooltip(), std::bind(&CSplitWindow::apply, this), EShortcut::GLOBAL_CONFIRM); + cancel = std::make_shared(Point(214, 263), "ICN6432", CButton::tooltip(), std::bind(&CSplitWindow::close, this), EShortcut::GLOBAL_CANCEL); int sliderPosition = total - leftMin - rightMin; @@ -403,7 +404,7 @@ CLevelWindow::CLevelWindow(const CGHeroInstance * hero, PrimarySkill::PrimarySki } portrait = std::make_shared("PortraitsLarge", hero->portrait, 0, 170, 66); - ok = std::make_shared(Point(297, 413), "IOKAY", CButton::tooltip(), std::bind(&CLevelWindow::close, this), SDLK_RETURN); + ok = std::make_shared(Point(297, 413), "IOKAY", CButton::tooltip(), std::bind(&CLevelWindow::close, this), EShortcut::GLOBAL_CONFIRM); //%s has gained a level. mainTitle = std::make_shared(192, 33, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, boost::str(boost::format(CGI->generaltexth->allTexts[444]) % hero->getNameTranslated())); @@ -459,9 +460,9 @@ CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj) rumor = std::make_shared(rumorText, Rect(32, 190, 330, 68), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); statusbar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); - cancel = std::make_shared(Point(310, 428), "ICANCEL.DEF", CButton::tooltip(CGI->generaltexth->tavernInfo[7]), std::bind(&CTavernWindow::close, this), SDLK_ESCAPE); - recruit = std::make_shared(Point(272, 355), "TPTAV01.DEF", CButton::tooltip(), std::bind(&CTavernWindow::recruitb, this), SDLK_RETURN); - thiefGuild = std::make_shared(Point(22, 428), "TPTAV02.DEF", CButton::tooltip(CGI->generaltexth->tavernInfo[5]), std::bind(&CTavernWindow::thievesguildb, this), SDLK_t); + cancel = std::make_shared(Point(310, 428), "ICANCEL.DEF", CButton::tooltip(CGI->generaltexth->tavernInfo[7]), std::bind(&CTavernWindow::close, this), EShortcut::GLOBAL_CANCEL); + recruit = std::make_shared(Point(272, 355), "TPTAV01.DEF", CButton::tooltip(), std::bind(&CTavernWindow::recruitb, this), EShortcut::GLOBAL_CONFIRM); + thiefGuild = std::make_shared(Point(22, 428), "TPTAV02.DEF", CButton::tooltip(CGI->generaltexth->tavernInfo[5]), std::bind(&CTavernWindow::thievesguildb, this), EShortcut::ADVENTURE_THIEVES_GUILD); if(LOCPLINT->cb->getResourceAmount(EGameResID::GOLD) < GameConstants::HERO_GOLD_COST) //not enough gold { @@ -982,7 +983,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, luck[b] = std::make_shared(false, Rect(Point(212 + 490 * b, 39), Point(32, 32)), true); } - quit = std::make_shared(Point(732, 567), "IOKAY.DEF", CGI->generaltexth->zelp[600], std::bind(&CExchangeWindow::close, this), SDLK_RETURN); + quit = std::make_shared(Point(732, 567), "IOKAY.DEF", CGI->generaltexth->zelp[600], std::bind(&CExchangeWindow::close, this), EShortcut::GLOBAL_CONFIRM); if(queryID.getNum() > 0) quit->addCallback([=](){ LOCPLINT->cb->selectionMade(0, queryID); }); @@ -1098,8 +1099,8 @@ CShipyardWindow::CShipyardWindow(const TResources & cost, int state, BoatId boat goldPic = std::make_shared("RESOURCE",GameResID(EGameResID::GOLD), 0, 100, 244); woodPic = std::make_shared("RESOURCE", GameResID(EGameResID::WOOD), 0, 196, 244); - quit = std::make_shared(Point(224, 312), "ICANCEL", CButton::tooltip(CGI->generaltexth->allTexts[599]), std::bind(&CShipyardWindow::close, this), SDLK_ESCAPE); - build = std::make_shared(Point(42, 312), "IBUY30", CButton::tooltip(CGI->generaltexth->allTexts[598]), std::bind(&CShipyardWindow::close, this), SDLK_RETURN); + quit = std::make_shared(Point(224, 312), "ICANCEL", CButton::tooltip(CGI->generaltexth->allTexts[599]), std::bind(&CShipyardWindow::close, this), EShortcut::GLOBAL_CANCEL); + build = std::make_shared(Point(42, 312), "IBUY30", CButton::tooltip(CGI->generaltexth->allTexts[598]), std::bind(&CShipyardWindow::close, this), EShortcut::GLOBAL_CONFIRM); build->addCallback(onBuy); for(auto i = EGameResID::WOOD; i <= EGameResID::GOLD; vstd::advance(i, 1)) @@ -1199,9 +1200,9 @@ CTransformerWindow::CTransformerWindow(const CGHeroInstance * _hero, const CGTow 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); - convert = std::make_shared(Point(269, 416), "ALTSACR.DEF", CGI->generaltexth->zelp[591], [&](){ makeDeal(); }, SDLK_RETURN); - cancel = std::make_shared(Point(392, 416), "ICANCEL.DEF", CGI->generaltexth->zelp[592], [&](){ close(); },SDLK_ESCAPE); + all = std::make_shared(Point(146, 416), "ALTARMY.DEF", CGI->generaltexth->zelp[590], [&](){ addAll(); }, EShortcut::RECRUITMENT_UPGRADE_ALL); + convert = std::make_shared(Point(269, 416), "ALTSACR.DEF", CGI->generaltexth->zelp[591], [&](){ makeDeal(); }, EShortcut::GLOBAL_CONFIRM); + cancel = std::make_shared(Point(392, 416), "ICANCEL.DEF", CGI->generaltexth->zelp[592], [&](){ close(); },EShortcut::GLOBAL_CANCEL); statusbar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); titleLeft = std::make_shared(153, 29,FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[485]);//holding area @@ -1315,7 +1316,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket for(int i=0; i(this, goods[i], 54+i*104, 234)); - cancel = std::make_shared(Point(200, 313), "IOKAY.DEF", CGI->generaltexth->zelp[632], [&](){ close(); }, SDLK_RETURN); + cancel = std::make_shared(Point(200, 313), "IOKAY.DEF", CGI->generaltexth->zelp[632], [&](){ close(); }, EShortcut::GLOBAL_CONFIRM); statusbar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); } @@ -1353,10 +1354,10 @@ CUnivConfirmWindow::CUnivConfirmWindow(CUniversityWindow * owner_, int SKILL, bo boost::replace_first(text, "%s", CGI->skillh->getByIndex(SKILL)->getNameTranslated()); boost::replace_first(text, "%d", "2000"); - confirm = std::make_shared(Point(148, 299), "IBY6432.DEF", CButton::tooltip(hoverText, text), [=](){makeDeal(SKILL);}, SDLK_RETURN); + confirm = std::make_shared(Point(148, 299), "IBY6432.DEF", CButton::tooltip(hoverText, text), [=](){makeDeal(SKILL);}, EShortcut::GLOBAL_CONFIRM); confirm->block(!available); - cancel = std::make_shared(Point(252,299), "ICANCEL.DEF", CGI->generaltexth->zelp[631], [&](){ close(); }, SDLK_ESCAPE); + cancel = std::make_shared(Point(252,299), "ICANCEL.DEF", CGI->generaltexth->zelp[631], [&](){ close(); }, EShortcut::GLOBAL_CANCEL); statusbar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); } @@ -1376,7 +1377,7 @@ CGarrisonWindow::CGarrisonWindow(const CArmedInstance * up, const CGHeroInstance auto split = std::make_shared(Point(88, 314), "IDV6432.DEF", CButton::tooltip(CGI->generaltexth->tcommands[3], ""), [&](){ garr->splitClick(); } ); garr->addSplitBtn(split); } - quit = std::make_shared(Point(399, 314), "IOK6432.DEF", CButton::tooltip(CGI->generaltexth->tcommands[8], ""), [&](){ close(); }, SDLK_RETURN); + quit = std::make_shared(Point(399, 314), "IOK6432.DEF", CButton::tooltip(CGI->generaltexth->tcommands[8], ""), [&](){ close(); }, EShortcut::GLOBAL_CONFIRM); std::string titleText; if(down->tempOwner == up->tempOwner) @@ -1426,7 +1427,7 @@ CHillFortWindow::CHillFortWindow(const CGHeroInstance * visitor, const CGObjectI for(int i = 0; i < slotsCount; i++) { - upgrade[i] = std::make_shared(Point(107 + i * 76, 171), "", CButton::tooltip(getTextForSlot(SlotID(i))), [=](){ makeDeal(SlotID(i)); }, SDLK_1 + i); + upgrade[i] = std::make_shared(Point(107 + i * 76, 171), "", CButton::tooltip(getTextForSlot(SlotID(i))), [=](){ makeDeal(SlotID(i)); }, vstd::advance_r(EShortcut::SELECT_INDEX_1, i)); for(auto image : { "APHLF1R.DEF", "APHLF1Y.DEF", "APHLF1G.DEF" }) upgrade[i]->addImage(image); @@ -1437,11 +1438,11 @@ CHillFortWindow::CHillFortWindow(const CGHeroInstance * visitor, const CGObjectI } } - upgradeAll = std::make_shared(Point(30, 231), "", CButton::tooltip(CGI->generaltexth->allTexts[432]), [&](){ makeDeal(SlotID(slotsCount));}, SDLK_0); + upgradeAll = std::make_shared(Point(30, 231), "", CButton::tooltip(CGI->generaltexth->allTexts[432]), [&](){ makeDeal(SlotID(slotsCount));}, EShortcut::RECRUITMENT_UPGRADE_ALL); for(auto image : { "APHLF4R.DEF", "APHLF4Y.DEF", "APHLF4G.DEF" }) upgradeAll->addImage(image); - quit = std::make_shared(Point(294, 275), "IOKAY.DEF", CButton::tooltip(), std::bind(&CHillFortWindow::close, this), SDLK_RETURN); + quit = std::make_shared(Point(294, 275), "IOKAY.DEF", CButton::tooltip(), std::bind(&CHillFortWindow::close, this), EShortcut::GLOBAL_CONFIRM); statusbar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); garr = std::make_shared(108, 60, 18, Point(), hero, nullptr); @@ -1617,8 +1618,7 @@ CThievesGuildWindow::CThievesGuildWindow(const CGObjectInstance * _owner): SThievesGuildInfo tgi; //info to be displayed LOCPLINT->cb->getThievesGuildInfo(tgi, owner); - exitb = std::make_shared(Point(748, 556), "TPMAGE1", CButton::tooltip(CGI->generaltexth->allTexts[600]), [&](){ close();}, SDLK_RETURN); - exitb->assignedKeys.insert(SDLK_ESCAPE); + exitb = std::make_shared(Point(748, 556), "TPMAGE1", CButton::tooltip(CGI->generaltexth->allTexts[600]), [&](){ close();}, EShortcut::GLOBAL_RETURN); statusbar = CGStatusBar::create(3, 555, "TStatBar.bmp", 742); resdatabar = std::make_shared(); @@ -1814,7 +1814,7 @@ void CObjectListWindow::init(std::shared_ptr titleWidget_, std::stri title = std::make_shared(152, 27, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, _title); descr = std::make_shared(145, 133, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, _descr); - exit = std::make_shared( Point(228, 402), "ICANCEL.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::exitPressed, this), SDLK_ESCAPE); + exit = std::make_shared( Point(228, 402), "ICANCEL.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::exitPressed, this), EShortcut::GLOBAL_CANCEL); if(titleWidget) { @@ -1827,7 +1827,7 @@ void CObjectListWindow::init(std::shared_ptr titleWidget_, std::stri Point(14, 151), Point(0, 25), 9, items.size(), 0, 1, Rect(262, -32, 256, 256) ); list->type |= REDRAW_PARENT; - ok = std::make_shared(Point(15, 402), "IOKAY.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::elementSelected, this), SDLK_RETURN); + ok = std::make_shared(Point(15, 402), "IOKAY.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::elementSelected, this), EShortcut::GLOBAL_CONFIRM); ok->block(!list->size()); } @@ -1875,28 +1875,28 @@ void CObjectListWindow::changeSelection(size_t which) selected = which; } -void CObjectListWindow::keyPressed (const SDL_Keycode & key) +void CObjectListWindow::keyPressed (EShortcut key) { int sel = static_cast(selected); switch(key) { - break; case SDLK_UP: + break; case EShortcut::SELECT_UP: sel -=1; - break; case SDLK_DOWN: + break; case EShortcut::SELECT_DOWN: sel +=1; - break; case SDLK_PAGEUP: + break; case EShortcut::SELECT_PAGE_UP: sel -=9; - break; case SDLK_PAGEDOWN: + break; case EShortcut::SELECT_PAGE_DOWN: sel +=9; - break; case SDLK_HOME: + break; case EShortcut::SELECT_FIRST: sel = 0; - break; case SDLK_END: + break; case EShortcut::SELECT_LAST: sel = static_cast(items.size()); break; default: diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index 94f64aa29..e48718b8c 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -194,7 +194,7 @@ public: std::shared_ptr genItem(size_t index); void elementSelected();//call callback and close this window void changeSelection(size_t which); - void keyPressed(const SDL_Keycode & key) override; + void keyPressed(EShortcut key) override; }; class CTavernWindow : public CStatusbarWindow diff --git a/client/windows/InfoWindows.cpp b/client/windows/InfoWindows.cpp index a1955a681..50c11e623 100644 --- a/client/windows/InfoWindows.cpp +++ b/client/windows/InfoWindows.cpp @@ -26,6 +26,7 @@ #include "../windows/CMessage.h" #include "../renderSDL/SDL_Extensions.h" #include "../gui/CursorHandler.h" +#include "../gui/Shortcut.h" #include "../../CCallback.h" @@ -79,9 +80,6 @@ CSelWindow::CSelWindow(const std::string &Text, PlayerColor player, int charperl text = std::make_shared(Text, Rect(0, 0, 250, 100), 0, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE); - buttons.front()->assignedKeys.insert(SDLK_RETURN); //first button - reacts on enter - buttons.back()->assignedKeys.insert(SDLK_ESCAPE); //last button - reacts on escape - if (buttons.size() > 1 && askID.getNum() >= 0) //cancel button functionality { buttons.back()->addCallback([askID]() { @@ -96,8 +94,8 @@ CSelWindow::CSelWindow(const std::string &Text, PlayerColor player, int charperl addChild(comps[i].get()); components.push_back(comps[i]); comps[i]->onSelect = std::bind(&CSelWindow::selectionChange,this,i); - if(i<9) - comps[i]->assignedKeys.insert(SDLK_1+i); + if(i<8) + comps[i]->assignedKey = vstd::advance_r(EShortcut::SELECT_INDEX_1,i); } CMessage::drawIWindow(this, Text, player); } @@ -137,12 +135,6 @@ CInfoWindow::CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo text->resize(text->label->textSize); } - if(buttons.size()) - { - buttons.front()->assignedKeys.insert(SDLK_RETURN); //first button - reacts on enter - buttons.back()->assignedKeys.insert(SDLK_ESCAPE); //last button - reacts on escape - } - for(auto & comp : comps) { comp->recActions = 0xff & ~DISPOSE; diff --git a/client/windows/QuickRecruitmentWindow.cpp b/client/windows/QuickRecruitmentWindow.cpp index 67bf8ad43..75ebd8307 100644 --- a/client/windows/QuickRecruitmentWindow.cpp +++ b/client/windows/QuickRecruitmentWindow.cpp @@ -14,6 +14,7 @@ #include "../widgets/Buttons.h" #include "../widgets/CreatureCostBox.h" #include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" #include "../../CCallback.h" #include "../../lib/ResourceSet.h" #include "../../lib/CCreatureHandler.h" @@ -29,20 +30,19 @@ void QuickRecruitmentWindow::setButtons() void QuickRecruitmentWindow::setCancelButton() { - cancelButton = std::make_shared(Point((pos.w / 2) + 48, 418), "ICN6432.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_ESCAPE); + cancelButton = std::make_shared(Point((pos.w / 2) + 48, 418), "ICN6432.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::GLOBAL_CANCEL); cancelButton->setImageOrder(0, 1, 2, 3); } void QuickRecruitmentWindow::setBuyButton() { - buyButton = std::make_shared(Point((pos.w / 2) - 32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purchaseUnits(); }, SDLK_RETURN); - cancelButton->assignedKeys.insert(SDLK_ESCAPE); + buyButton = std::make_shared(Point((pos.w / 2) - 32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purchaseUnits(); }, EShortcut::GLOBAL_CONFIRM); buyButton->setImageOrder(0, 1, 2, 3); } void QuickRecruitmentWindow::setMaxButton() { - maxButton = std::make_shared(Point((pos.w/2)-112, 418), "IRCBTNS.DEF", CButton::tooltip(), [&](){ maxAllCards(cards); }, SDLK_m); + maxButton = std::make_shared(Point((pos.w/2)-112, 418), "IRCBTNS.DEF", CButton::tooltip(), [&](){ maxAllCards(cards); }, EShortcut::RECRUITMENT_MAX); maxButton->setImageOrder(0, 1, 2, 3); } diff --git a/config/campaignSets.json b/config/campaignSets.json index e378d084b..46b81217c 100644 --- a/config/campaignSets.json +++ b/config/campaignSets.json @@ -2,7 +2,7 @@ "roe" : { "images" : [ {"x": 0, "y": 0, "name":"CAMPBACK"} ], - "exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27}, + "exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN" }, "items": [ { "x":90, "y":72, "file":"DATA/GOOD1.H3C", "image":"CAMPGD1S", "video":"CGOOD1", "open": true }, @@ -22,7 +22,7 @@ {"x": 34, "y": 417, "name":"CAMP1FWX"},//one campaign have special inactive image {"x": 385, "y": 401, "name":"CAMPNOSC"},//and the last one is not present ], - "exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27}, + "exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN" }, "items": [ { "x":90, "y":72, "file":"DATA/AB.H3C", "image":"CAMP1AB7", "video":"C1ab7", "open": true }, @@ -36,7 +36,7 @@ "sod": { "images" : [ {"x": 0, "y": 0, "name":"CAMPBKX2"} ], - "exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27}, + "exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN" }, "items": [ { "x":90, "y":72, "file":"DATA/GEM.H3C", "image":"CAMPNB1", "video":"NEW", "open": true }, @@ -47,18 +47,5 @@ { "x":34, "y":417, "file":"DATA/FINAL.H3C", "image":"CAMPUA1", "video":"UNHOLY", "open": true }, { "x":404, "y":414, "file":"DATA/SECRET.H3C", "image":"CAMPSP1", "video":"SPECTRE", "open": true } ] - }, - "wog" : - { - /// wog campaigns, currently has no assigned button in campaign screen and thus unused - "images" : [ {"x": 0, "y": 0, "name":"CAMPZALL"} ], - "exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27}, - "items": - [ - { "x":90, "y":72, "file":"DATA/ZC1.H3C", "image":"CAMPZ01", "open": true}, - { "x":539, "y":72, "file":"DATA/ZC2.H3C", "image":"CAMPZ02", "open": true}, - { "x":43, "y":245, "file":"DATA/ZC3.H3C", "image":"CAMPZ03", "open": true}, - { "x":311, "y":242, "file":"DATA/ZC4.H3C", "image":"CAMPZ04", "open": true} - ] } } diff --git a/config/mainmenu.json b/config/mainmenu.json index 292e8cfb7..9cd4a6923 100644 --- a/config/mainmenu.json +++ b/config/mainmenu.json @@ -17,22 +17,22 @@ "name" : "main", "buttons": [ - {"x": 644, "y": 70, "center" : true, "name":"MMENUNG", "hotkey" : 110, "help": 3, "command": "to new"}, - {"x": 645, "y": 192, "center" : true, "name":"MMENULG", "hotkey" : 108, "help": 4, "command": "to load"}, - {"x": 643, "y": 296, "center" : true, "name":"MMENUHS", "hotkey" : 104, "help": 5, "command": "highscores"}, - {"x": 643, "y": 414, "center" : true, "name":"MMENUCR", "hotkey" : 99, "help": 6, "command": "to credits"}, - {"x": 643, "y": 520, "center" : true, "name":"MMENUQT", "hotkey" : 27, "help": 7, "command": "exit"} + {"x": 644, "y": 70, "center" : true, "name":"MMENUNG", "shortcut" : "mainMenuNew", "help": 3, "command": "to new"}, + {"x": 645, "y": 192, "center" : true, "name":"MMENULG", "shortcut" : "mainMenuLoad", "help": 4, "command": "to load"}, + {"x": 643, "y": 296, "center" : true, "name":"MMENUHS", "shortcut" : "mainMenuScores", "help": 5, "command": "highscores"}, + {"x": 643, "y": 414, "center" : true, "name":"MMENUCR", "shortcut" : "mainMenuCredits", "help": 6, "command": "to credits"}, + {"x": 643, "y": 520, "center" : true, "name":"MMENUQT", "shortcut" : "mainMenuQuit", "help": 7, "command": "exit"} ] }, { "name" : "new", "buttons": [ - {"x": 649, "y": 65, "center" : true, "name":"GTSINGL", "hotkey" : 115, "help": 10, "command": "start single"}, - {"x": 649, "y": 180, "center" : true, "name":"GTMULTI", "hotkey" : 109, "help": 12, "command": "start multi"}, - {"x": 646, "y": 298, "center" : true, "name":"GTCAMPN", "hotkey" : 99, "help": 11, "command": "to campaign"}, - {"x": 647, "y": 412, "center" : true, "name":"GTTUTOR", "hotkey" : 116, "help": 13, "command": "start tutorial"}, - {"x": 645, "y": 517, "center" : true, "name":"GTBACK", "hotkey" : 27, "help": 14, "command": "to main"} + {"x": 649, "y": 65, "center" : true, "name":"GTSINGL", "shortcut" : "mainMenuSingleplayer", "help": 10, "command": "start single"}, + {"x": 649, "y": 180, "center" : true, "name":"GTMULTI", "shortcut" : "mainMenuMultiplayer", "help": 12, "command": "start multi"}, + {"x": 646, "y": 298, "center" : true, "name":"GTCAMPN", "shortcut" : "mainMenuCampaign", "help": 11, "command": "to campaign"}, + {"x": 647, "y": 412, "center" : true, "name":"GTTUTOR", "shortcut" : "mainMenuTutorial", "help": 13, "command": "start tutorial"}, + {"x": 645, "y": 517, "center" : true, "name":"GTBACK", "shortcut" : "mainMenuBack", "help": 14, "command": "to main"} ], "images": [ {"x": 114, "y": 312, "name":"NEWGAME"} ] }, @@ -40,11 +40,11 @@ "name" : "load", "buttons": [ - {"x": 649, "y": 65, "center" : true, "name":"GTSINGL", "hotkey" : 115, "help": 10, "command": "load single"}, - {"x": 649, "y": 180, "center" : true, "name":"GTMULTI", "hotkey" : 109, "help": 12, "command": "load multi"}, - {"x": 646, "y": 298, "center" : true, "name":"GTCAMPN", "hotkey" : 99, "help": 11, "command": "load campaign"}, - {"x": 647, "y": 412, "center" : true, "name":"GTTUTOR", "hotkey" : 116, "help": 13, "command": "load tutorial"}, - {"x": 645, "y": 517, "center" : true, "name":"GTBACK", "hotkey" : 27, "help": 14, "command": "to main"} + {"x": 649, "y": 65, "center" : true, "name":"GTSINGL", "shortcut" : "mainMenuSingleplayer", "help": 10, "command": "load single"}, + {"x": 649, "y": 180, "center" : true, "name":"GTMULTI", "shortcut" : "mainMenuMultiplayer", "help": 12, "command": "load multi"}, + {"x": 646, "y": 298, "center" : true, "name":"GTCAMPN", "shortcut" : "mainMenuCampaign", "help": 11, "command": "load campaign"}, + {"x": 647, "y": 412, "center" : true, "name":"GTTUTOR", "shortcut" : "mainMenuTutorial", "help": 13, "command": "load tutorial"}, + {"x": 645, "y": 517, "center" : true, "name":"GTBACK", "shortcut" : "mainMenuBack", "help": 14, "command": "to main"} ], "images": [ {"x": 114, "y": 312, "name":"LOADGAME"} ] }, @@ -52,11 +52,11 @@ "name" : "campaign", "buttons": [ - {"x": 634, "y": 67, "center" : true, "name":"CSSSOD", "hotkey" : 119, "command": "campaigns sod"}, - {"x": 637, "y": 181, "center" : true, "name":"CSSROE", "hotkey" : 114, "command": "campaigns roe"}, - {"x": 638, "y": 301, "center" : true, "name":"CSSARM", "hotkey" : 97, "command": "campaigns ab"}, - {"x": 638, "y": 413, "center" : true, "name":"CSSCUS", "hotkey" : 99, "command": "start campaign"}, - {"x": 639, "y": 518, "center" : true, "name":"CSSEXIT", "hotkey" : 27, "command": "to new"} + {"x": 634, "y": 67, "center" : true, "name":"CSSSOD", "shortcut" : "mainMenuCampaignSod", "command": "campaigns sod"}, + {"x": 637, "y": 181, "center" : true, "name":"CSSROE", "shortcut" : "mainMenuCampaignRoe", "command": "campaigns roe"}, + {"x": 638, "y": 301, "center" : true, "name":"CSSARM", "shortcut" : "mainMenuCampaignAb", "command": "campaigns ab"}, + {"x": 638, "y": 413, "center" : true, "name":"CSSCUS", "shortcut" : "mainMenuCampaign", "command": "start campaign"}, + {"x": 639, "y": 518, "center" : true, "name":"CSSEXIT", "shortcut" : "mainMenuBack", "command": "to new"} ], } ] diff --git a/config/widgets/battleWindow.json b/config/widgets/battleWindow.json index 21261f32e..a23ecb7b1 100644 --- a/config/widgets/battleWindow.json +++ b/config/widgets/battleWindow.json @@ -22,7 +22,7 @@ "image": "icm003", "help": "core.help.381", "callback": "options", - "hotkey": "o" + "hotkey": "globalOptions" }, { @@ -32,7 +32,7 @@ "image": "icm001", "help": "core.help.379", "callback": "surrender", - "hotkey": "s" + "hotkey": "battleSurrender" }, { @@ -42,7 +42,7 @@ "image": "icm002", "help": "core.help.380", "callback": "flee", - "hotkey": "r" + "hotkey": "battleRetreat" }, { @@ -52,7 +52,7 @@ "image": "icm004", "help": "core.help.382", "callback": "autofight", - "hotkey": "a" + "hotkey": "battleAutocombat" }, { @@ -62,7 +62,7 @@ "image": "icm005", "help": "core.help.385", "callback": "spellbook", - "hotkey": "c" + "hotkey": "battleCastSpell" }, { @@ -72,7 +72,7 @@ "image": "icm006", "help": "core.help.386", "callback": "wait", - "hotkey": "w" + "hotkey": "battleWait" }, { @@ -82,7 +82,7 @@ "image": "icm007", "help": "core.help.387", "callback": "defence", - "hotkey": ["d", "space"] + "hotkey": "battleDefend" }, { @@ -92,7 +92,7 @@ "image": "ComSlide", "callback": "consoleUp", "imageOrder": [0, 1, 0, 0], - "hotkey": "up" + "hotkey": "battleConsoleUp" }, { @@ -102,7 +102,7 @@ "image": "ComSlide", "callback": "consoleDown", "imageOrder": [2, 3, 2, 2], - "hotkey": "down" + "hotkey": "battleConsoleDown" }, { @@ -118,7 +118,7 @@ "position": {"x": 213, "y": 560}, "image": "icm011", "callback": "tacticNext", - "hotkey": "space" + "hotkey": "battleTacticsNext" }, { @@ -127,7 +127,7 @@ "position": {"x": 419, "y": 560}, "image": "icm012", "callback": "tacticEnd", - "hotkey": "enter" + "hotkey": "battleTacticsEnd" } ] } diff --git a/config/widgets/settings/generalOptionsTab.json b/config/widgets/settings/generalOptionsTab.json index 61ef7b146..30c006ca8 100644 --- a/config/widgets/settings/generalOptionsTab.json +++ b/config/widgets/settings/generalOptionsTab.json @@ -47,7 +47,6 @@ "image": "settingsWindow/button32", "help": "vcmi.systemOptions.resolutionButton", "callback": "setGameResolution", - "hotkey": "g", "items": [ { diff --git a/config/widgets/settings/settingsMainContainer.json b/config/widgets/settings/settingsMainContainer.json index 014fc1d96..0e495d73f 100644 --- a/config/widgets/settings/settingsMainContainer.json +++ b/config/widgets/settings/settingsMainContainer.json @@ -103,7 +103,7 @@ "imageOrder": [1, 0, 2, 3], "help": "core.help.321", "callback": "loadGame", - "hotkey": "l" + "hotkey": "gameLoadGame" }, { "name": "saveButton", @@ -113,7 +113,7 @@ "imageOrder": [1, 0, 2, 3], "help": "core.help.322", "callback": "saveGame", - "hotkey": "s" + "hotkey": "gameSaveGame" }, { "name": "restartButton", @@ -123,7 +123,7 @@ "imageOrder": [1, 0, 2, 3], "help": "core.help.323", "callback": "restartGame", - "hotkey": "r" + "hotkey": "gameRestartGame" }, { "name": "mainMenuButton", @@ -133,7 +133,7 @@ "imageOrder": [1, 0, 2, 3], "help": "core.help.320", "callback": "returnToMainMenu", - "hotkey": "m" + "hotkey": "gameMainMenu" }, { "name": "quitButton", @@ -143,7 +143,7 @@ "imageOrder": [1, 0, 2, 3], "help": "core.help.324", "callback": "quitGame", - "hotkey": "q" + "hotkey": "gameQuitGame" }, { "name": "closeSettingsButton", @@ -153,7 +153,7 @@ "imageOrder": [1, 0, 2, 3], "help": "core.help.325", "callback": "closeWindow", - "hotkey": ["escape", "backspace"] + "hotkey": "globalReturn" } ] } From a654cc2391d38b752fcde5b500a167affaf2d350 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 28 Apr 2023 00:29:16 +0300 Subject: [PATCH 22/43] Removed vstd::advance. Added vstd::next to handle similar cases. --- AI/VCAI/Goals/CollectRes.cpp | 2 +- Global.h | 12 ++++-------- client/adventureMap/CResDataBar.cpp | 2 +- client/widgets/CComponent.cpp | 2 +- client/widgets/MiscWidgets.cpp | 2 +- client/windows/GUIClasses.cpp | 4 ++-- client/windows/InfoWindows.cpp | 2 +- lib/ResourceSet.cpp | 2 +- lib/mapObjects/CQuest.cpp | 2 +- server/CGameHandler.cpp | 2 +- 10 files changed, 14 insertions(+), 18 deletions(-) diff --git a/AI/VCAI/Goals/CollectRes.cpp b/AI/VCAI/Goals/CollectRes.cpp index 4599bed34..4a7feff61 100644 --- a/AI/VCAI/Goals/CollectRes.cpp +++ b/AI/VCAI/Goals/CollectRes.cpp @@ -170,7 +170,7 @@ TSubgoal CollectRes::whatToDoToTrade() const IMarket * m = markets.back(); //attempt trade at back (best prices) int howManyCanWeBuy = 0; - for (auto i = EGameResID::WOOD; i <= EGameResID::GOLD; vstd::advance(i, 1)) + for (GameResID i = EGameResID::WOOD; i <= EGameResID::GOLD; ++i) { if (GameResID(i) == resID) continue; diff --git a/Global.h b/Global.h index e32b0da72..a869ac611 100644 --- a/Global.h +++ b/Global.h @@ -543,16 +543,12 @@ namespace vstd }); } + /// Increments value by specific delta + /// similar to std::next but works with other types, e.g. enum class template - T advance_r(const T &obj, int change) + T next(const T &obj, int change) { - return static_cast(static_cast(obj) + change); - } - - template - void advance(T &obj, int change) - { - obj = advance_r(obj, change); + return static_cast(static_cast(obj) + change); } template diff --git a/client/adventureMap/CResDataBar.cpp b/client/adventureMap/CResDataBar.cpp index 33cb30f0a..6227b9f4b 100644 --- a/client/adventureMap/CResDataBar.cpp +++ b/client/adventureMap/CResDataBar.cpp @@ -80,7 +80,7 @@ std::string CResDataBar::buildDateString() void CResDataBar::draw(SDL_Surface * to) { //TODO: all this should be labels, but they require proper text update on change - for (auto i=GameResID(EGameResID::WOOD); i <= GameResID(EGameResID::GOLD); vstd::advance(i, 1)) + for (GameResID i=EGameResID::WOOD; i <= GameResID(EGameResID::GOLD); ++i) { std::string text = std::to_string(LOCPLINT->cb->getResourceAmount(i)); diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index a75334d7e..1aaec7be9 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -494,7 +494,7 @@ CComponentBox::CComponentBox(std::vector> { comp->onSelect = std::bind(&CComponentBox::selectionChanged, this, comp); comp->assignedKey = key; - vstd::advance(key, 1); + key = vstd::next(key, 1); } selectionChanged(_components.front()); } diff --git a/client/widgets/MiscWidgets.cpp b/client/widgets/MiscWidgets.cpp index 2e443d4d5..8d1179621 100644 --- a/client/widgets/MiscWidgets.cpp +++ b/client/widgets/MiscWidgets.cpp @@ -200,7 +200,7 @@ void CMinorResDataBar::showAll(SDL_Surface * to) { CIntObject::showAll(to); - for (EGameResID i=EGameResID::WOOD; i<=EGameResID::GOLD; vstd::advance(i, 1)) + for (GameResID i=EGameResID::WOOD; i<=EGameResID::GOLD; ++i) { std::string text = std::to_string(LOCPLINT->cb->getResourceAmount(i)); diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 629f1ce3d..bed47e646 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -1103,7 +1103,7 @@ CShipyardWindow::CShipyardWindow(const TResources & cost, int state, BoatId boat build = std::make_shared(Point(42, 312), "IBUY30", CButton::tooltip(CGI->generaltexth->allTexts[598]), std::bind(&CShipyardWindow::close, this), EShortcut::GLOBAL_CONFIRM); build->addCallback(onBuy); - for(auto i = EGameResID::WOOD; i <= EGameResID::GOLD; vstd::advance(i, 1)) + for(GameResID i = EGameResID::WOOD; i <= EGameResID::GOLD; ++i) { if(cost[i] > LOCPLINT->cb->getResourceAmount(i)) { @@ -1427,7 +1427,7 @@ CHillFortWindow::CHillFortWindow(const CGHeroInstance * visitor, const CGObjectI for(int i = 0; i < slotsCount; i++) { - upgrade[i] = std::make_shared(Point(107 + i * 76, 171), "", CButton::tooltip(getTextForSlot(SlotID(i))), [=](){ makeDeal(SlotID(i)); }, vstd::advance_r(EShortcut::SELECT_INDEX_1, i)); + upgrade[i] = std::make_shared(Point(107 + i * 76, 171), "", CButton::tooltip(getTextForSlot(SlotID(i))), [=](){ makeDeal(SlotID(i)); }, vstd::next(EShortcut::SELECT_INDEX_1, i)); for(auto image : { "APHLF1R.DEF", "APHLF1Y.DEF", "APHLF1G.DEF" }) upgrade[i]->addImage(image); diff --git a/client/windows/InfoWindows.cpp b/client/windows/InfoWindows.cpp index 50c11e623..2843aa38c 100644 --- a/client/windows/InfoWindows.cpp +++ b/client/windows/InfoWindows.cpp @@ -95,7 +95,7 @@ CSelWindow::CSelWindow(const std::string &Text, PlayerColor player, int charperl components.push_back(comps[i]); comps[i]->onSelect = std::bind(&CSelWindow::selectionChange,this,i); if(i<8) - comps[i]->assignedKey = vstd::advance_r(EShortcut::SELECT_INDEX_1,i); + comps[i]->assignedKey = vstd::next(EShortcut::SELECT_INDEX_1,i); } CMessage::drawIWindow(this, Text, player); } diff --git a/lib/ResourceSet.cpp b/lib/ResourceSet.cpp index 8603fbc84..d571d4ea8 100644 --- a/lib/ResourceSet.cpp +++ b/lib/ResourceSet.cpp @@ -149,7 +149,7 @@ void ResourceSet::nziterator::advance() { do { - vstd::advance(cur.resType, +1); + ++cur.resType; } while(cur.resType < GameConstants::RESOURCE_QUANTITY && !(cur.resVal=rs[cur.resType])); if(cur.resType >= GameConstants::RESOURCE_QUANTITY) diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index bd9163a24..a3672b4d5 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -163,7 +163,7 @@ bool CQuest::checkQuest(const CGHeroInstance * h) const case MISSION_ARMY: return checkMissionArmy(this, h); case MISSION_RESOURCES: - for(auto i = EGameResID::WOOD; i <= EGameResID::GOLD; vstd::advance(i, +1)) //including Mithril ? + for(GameResID i = EGameResID::WOOD; i <= EGameResID::GOLD; ++i) //including Mithril ? { //Quest has no direct access to callback if(CGHeroInstance::cb->getResource(h->tempOwner, i) < static_cast(m7resources[i])) return false; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index f24e0e580..6af785620 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -7029,7 +7029,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons ///Give resources to player TResources resources; resources[EGameResID::GOLD] = 100000; - for (auto i = EGameResID::WOOD; i < EGameResID::GOLD; vstd::advance(i, 1)) + for (GameResID i = EGameResID::WOOD; i < EGameResID::GOLD; ++i) resources[i] = 100; giveResources(player, resources); From dea10e6091f88d5ec4701f52fae7e6510b41dc02 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 28 Apr 2023 14:22:03 +0300 Subject: [PATCH 23/43] Use more descriptive names for hotkeys --- .../adventureMap/CAdventureMapInterface.cpp | 4 +- client/adventureMap/CInGameConsole.cpp | 6 +- client/battle/BattleInterfaceClasses.cpp | 2 +- client/gui/CGuiHandler.cpp | 8 +- client/gui/CGuiHandler.h | 4 +- client/gui/InterfaceObjectConfigurable.cpp | 2 +- client/gui/Shortcut.h | 113 ++++++++++-------- client/gui/ShortcutHandler.cpp | 82 +++++++------ client/gui/ShortcutHandler.h | 4 +- client/lobby/CBonusSelection.cpp | 4 +- client/lobby/CLobbyScreen.cpp | 2 +- client/lobby/CSelectionBase.cpp | 2 +- client/lobby/SelectionTab.cpp | 12 +- client/mainmenu/CMainMenu.cpp | 6 +- client/widgets/Buttons.cpp | 16 +-- client/widgets/CArtifactsOfHeroBase.cpp | 6 +- client/windows/CCastleInterface.cpp | 8 +- client/windows/CCreatureWindow.cpp | 4 +- client/windows/CKingdomInterface.cpp | 4 +- client/windows/CQuestLog.cpp | 2 +- client/windows/CSpellWindow.cpp | 10 +- client/windows/GUIClasses.cpp | 36 +++--- client/windows/InfoWindows.cpp | 9 ++ client/windows/QuickRecruitmentWindow.cpp | 2 +- 24 files changed, 188 insertions(+), 160 deletions(-) diff --git a/client/adventureMap/CAdventureMapInterface.cpp b/client/adventureMap/CAdventureMapInterface.cpp index 681bde8ba..4cbc1ff2b 100644 --- a/client/adventureMap/CAdventureMapInterface.cpp +++ b/client/adventureMap/CAdventureMapInterface.cpp @@ -110,7 +110,7 @@ CAdventureMapInterface::CAdventureMapInterface(): sleepWake = makeButton(296, std::bind(&CAdventureMapInterface::fsleepWake,this), ADVOPT.sleepWake, EShortcut::ADVENTURE_TOGGLE_SLEEP); moveHero = makeButton(297, std::bind(&CAdventureMapInterface::fmoveHero,this), ADVOPT.moveHero, EShortcut::ADVENTURE_MOVE_HERO); spellbook = makeButton(298, std::bind(&CAdventureMapInterface::fshowSpellbok,this), ADVOPT.spellbook, EShortcut::ADVENTURE_CAST_SPELL); - advOptions = makeButton(299, std::bind(&CAdventureMapInterface::fadventureOPtions,this), ADVOPT.advOptions, EShortcut::ADVENTURE_OPTIONS); + advOptions = makeButton(299, std::bind(&CAdventureMapInterface::fadventureOPtions,this), ADVOPT.advOptions, EShortcut::ADVENTURE_GAME_OPTIONS); sysOptions = makeButton(300, std::bind(&CAdventureMapInterface::fsystemOptions,this), ADVOPT.sysOptions, EShortcut::GLOBAL_OPTIONS); nextHero = makeButton(301, std::bind(&CAdventureMapInterface::fnextHero,this), ADVOPT.nextHero, EShortcut::ADVENTURE_NEXT_HERO); endTurn = makeButton(302, std::bind(&CAdventureMapInterface::fendTurn,this), ADVOPT.endTurn, EShortcut::ADVENTURE_END_TURN); @@ -699,7 +699,7 @@ void CAdventureMapInterface::keyPressed(EShortcut key) abortCastingMode(); return; } - case EShortcut::GAME_MARKETPLACE: + case EShortcut::GAME_OPEN_MARKETPLACE: { //act on key down if marketplace windows is not already opened if(GH.topInt()->type & BLOCK_ADV_HOTKEYS) diff --git a/client/adventureMap/CInGameConsole.cpp b/client/adventureMap/CInGameConsole.cpp index 8041f8fb9..262e180e3 100644 --- a/client/adventureMap/CInGameConsole.cpp +++ b/client/adventureMap/CInGameConsole.cpp @@ -131,7 +131,7 @@ void CInGameConsole::keyPressed (EShortcut key) startEnteringText(); break; - case EShortcut::GLOBAL_CONFIRM: + case EShortcut::GLOBAL_ACCEPT: { if(!enteredText.empty() && captureAllKeys) { @@ -155,7 +155,7 @@ void CInGameConsole::keyPressed (EShortcut key) } break; } - case EShortcut::SELECT_UP: + case EShortcut::MOVE_UP: { if(previouslyEntered.empty()) break; @@ -174,7 +174,7 @@ void CInGameConsole::keyPressed (EShortcut key) } break; } - case EShortcut::SELECT_DOWN: + case EShortcut::MOVE_DOWN: { if(prevEntDisp != -1 && prevEntDisp+1 < previouslyEntered.size()) { diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 0b8d76aec..42262df8d 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -407,7 +407,7 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface background->colorize(owner.playerID); pos = center(background->pos); - exit = std::make_shared(Point(384, 505), "iok6432.def", std::make_pair("", ""), [&](){ bExitf();}, EShortcut::GLOBAL_CONFIRM); + exit = std::make_shared(Point(384, 505), "iok6432.def", std::make_pair("", ""), [&](){ bExitf();}, EShortcut::GLOBAL_ACCEPT); exit->setBorderColor(Colors::METALLIC_GOLD); if(allowReplay) diff --git a/client/gui/CGuiHandler.cpp b/client/gui/CGuiHandler.cpp index 0e7f94ce9..f242f7ee8 100644 --- a/client/gui/CGuiHandler.cpp +++ b/client/gui/CGuiHandler.cpp @@ -95,7 +95,7 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::function(); + shortcutsHandlerInstance = std::make_unique(); mainFPSmng = new CFramerateManager(); mainFPSmng->init(settings["video"]["targetfps"].Integer()); isPointerRelativeMode = settings["general"]["userRelativePointer"].Bool(); @@ -404,7 +404,7 @@ void CGuiHandler::handleCurrentEvent( SDL_Event & current ) return; } - auto shortcutsVector = shortcutsHandler->translateKeycode(key.keysym.sym); + auto shortcutsVector = shortcutsHandler().translateKeycode(key.keysym.sym); bool keysCaptured = false; for(auto i = keyinterested.begin(); i != keyinterested.end() && continueEventHandling; i++) @@ -714,9 +714,9 @@ CGuiHandler::~CGuiHandler() delete terminate_cond; } -ShortcutHandler & CGuiHandler::getShortcutsHandler() +ShortcutHandler & CGuiHandler::shortcutsHandler() { - return *shortcutsHandler; + return *shortcutsHandlerInstance; } void CGuiHandler::moveCursorToPosition(const Point & position) diff --git a/client/gui/CGuiHandler.h b/client/gui/CGuiHandler.h index be4617a60..b0aea577b 100644 --- a/client/gui/CGuiHandler.h +++ b/client/gui/CGuiHandler.h @@ -78,7 +78,7 @@ private: std::vector> disposed; - std::unique_ptr shortcutsHandler; + std::unique_ptr shortcutsHandlerInstance; std::atomic continueEventHandling; using CIntObjectList = std::list; @@ -114,7 +114,7 @@ public: /// returns current position of mouse cursor, relative to vcmi window const Point & getCursorPosition() const; - ShortcutHandler & getShortcutsHandler(); + ShortcutHandler & shortcutsHandler(); Point screenDimensions() const; diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 13b6aa7e9..6423499d3 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -211,7 +211,7 @@ EShortcut InterfaceObjectConfigurable::readKeycode(const JsonNode & config) cons return EShortcut::NONE; } - EShortcut result = GH.getShortcutsHandler().findShortcut(config.String()); + EShortcut result = GH.shortcutsHandler().findShortcut(config.String()); if (result == EShortcut::NONE) logGlobal->error("Invalid keycode '%s' in interface configuration!", config.String()); return result;; diff --git a/client/gui/Shortcut.h b/client/gui/Shortcut.h index d739cbf7c..7579ef1dc 100644 --- a/client/gui/Shortcut.h +++ b/client/gui/Shortcut.h @@ -13,23 +13,26 @@ enum class EShortcut { NONE, - GLOBAL_CONFIRM, // Enter - GLOBAL_CANCEL, // Escape - GLOBAL_RETURN, // Enter, Escape - GLOBAL_FULLSCREEN, // F4 - GLOBAL_OPTIONS, - GLOBAL_BACKSPACE, - GLOBAL_MOVE_FOCUS, + // Global hotkeys that are available in multiple dialogs + GLOBAL_ACCEPT, // Return - Accept query + GLOBAL_CANCEL, // Escape - Cancel query + GLOBAL_RETURN, // Enter, Escape - Close current window and return to previous view + GLOBAL_FULLSCREEN, // F4 - TODO: remove hardcoded check for key + GLOBAL_OPTIONS, // 'O' - Open System Options dialog + GLOBAL_BACKSPACE, // Backspace - erase last symbol in text input + GLOBAL_MOVE_FOCUS, // Tab - move focus to next text input - SELECT_LEFT, - SELECT_RIGHT, - SELECT_UP, - SELECT_DOWN, - SELECT_FIRST, - SELECT_LAST, - SELECT_PAGE_UP, - SELECT_PAGE_DOWN, + // Movement hotkeys, usually - for moving through lists with slider + MOVE_LEFT, + MOVE_RIGHT, + MOVE_UP, + MOVE_DOWN, + MOVE_FIRST, + MOVE_LAST, + MOVE_PAGE_UP, + MOVE_PAGE_DOWN, + // Element selection - for multiple choice dialog popups SELECT_INDEX_1, SELECT_INDEX_2, SELECT_INDEX_3, @@ -39,9 +42,10 @@ enum class EShortcut SELECT_INDEX_7, SELECT_INDEX_8, - MAIN_MENU_NEW, - MAIN_MENU_LOAD, - MAIN_MENU_SCORES, + // Main menu hotkeys - for navigation between main menu windows + MAIN_MENU_NEW_GAME, + MAIN_MENU_LOAD_GAME, + MAIN_MENU_HIGH_SCORES, MAIN_MENU_CREDITS, MAIN_MENU_BACK, MAIN_MENU_QUIT, @@ -54,44 +58,39 @@ enum class EShortcut MAIN_MENU_CAMPAIGN_AB, MAIN_MENU_CAMPAIGN_CUSTOM, + // Game lobby / scenario selection LOBBY_BEGIN_GAME, // b, Return LOBBY_LOAD_GAME, // l, Return LOBBY_SAVE_GAME, // s, Return - LOBBY_RANDOM_MAP, + LOBBY_RANDOM_MAP, // Open random map tab LOBBY_HIDE_CHAT, - LOBBY_ADDITIONAL_OPTIONS, - LOBBY_SELECT, + LOBBY_ADDITIONAL_OPTIONS, // Open additional options tab + LOBBY_SELECT_SCENARIO, // Open map list tab + // In-game hotkeys, require game state but may be available in windows other than adventure map GAME_END_TURN, GAME_LOAD_GAME, GAME_SAVE_GAME, GAME_RESTART_GAME, GAME_TO_MAIN_MENU, GAME_QUIT_GAME, - GAME_MARKETPLACE, - GAME_THIEVES_GUILD, - GAME_ACTIVATE_CONSOLE, + GAME_OPEN_MARKETPLACE, + GAME_OPEN_THIEVES_GUILD, + GAME_ACTIVATE_CONSOLE, // Tab, activates in-game console - ADVENTURE_OPTIONS, - ADVENTURE_TOGGLE_GRID, - ADVENTURE_TOGGLE_SLEEP, - ADVENTURE_MOVE_HERO, - ADVENTURE_VISIT_OBJECT, - ADVENTURE_MOVE_HERO_SW, - ADVENTURE_MOVE_HERO_SS, - ADVENTURE_MOVE_HERO_SE, - ADVENTURE_MOVE_HERO_WW, - ADVENTURE_MOVE_HERO_EE, - ADVENTURE_MOVE_HERO_NW, - ADVENTURE_MOVE_HERO_NN, - ADVENTURE_MOVE_HERO_NE, - ADVENTURE_VIEW_SELECTED, - ADVENTURE_NEXT_OBJECT, + // Adventure map screen + ADVENTURE_GAME_OPTIONS, // 'o', Open CAdventureOptions window + ADVENTURE_TOGGLE_GRID, // F6, Toggles map grid + ADVENTURE_TOGGLE_SLEEP, // z,w, Toggles hero sleep status + ADVENTURE_MOVE_HERO, // Moves hero alongside set path + ADVENTURE_VISIT_OBJECT, // Revisits object hero is standing on + ADVENTURE_VIEW_SELECTED,// Open window with currently selected hero/town ADVENTURE_NEXT_TOWN, ADVENTURE_NEXT_HERO, - ADVENTURE_FIRST_TOWN, - ADVENTURE_FIRST_HERO, - ADVENTURE_VIEW_SCENARIO, + ADVENTURE_NEXT_OBJECT, // TODO: context-sensitive next object - select next hero/town, depending on current selection + ADVENTURE_FIRST_TOWN, // TODO: select first available town in the list + ADVENTURE_FIRST_HERO, // TODO: select first available hero in the list + ADVENTURE_VIEW_SCENARIO,// View Scenario Information window ADVENTURE_DIG_GRAIL, ADVENTURE_VIEW_PUZZLE, ADVENTURE_VIEW_WORLD, @@ -102,6 +101,17 @@ enum class EShortcut ADVENTURE_END_TURN, ADVENTURE_THIEVES_GUILD, + // Move hero one tile in specified direction. Bound to cursors & numpad buttons + ADVENTURE_MOVE_HERO_SW, + ADVENTURE_MOVE_HERO_SS, + ADVENTURE_MOVE_HERO_SE, + ADVENTURE_MOVE_HERO_WW, + ADVENTURE_MOVE_HERO_EE, + ADVENTURE_MOVE_HERO_NW, + ADVENTURE_MOVE_HERO_NN, + ADVENTURE_MOVE_HERO_NE, + + // Battle screen BATTLE_TOGGLE_QUEUE, BATTLE_USE_CREATURE_SPELL, BATTLE_SURRENDER, @@ -115,23 +125,28 @@ enum class EShortcut BATTLE_TACTICS_NEXT, BATTLE_TACTICS_END, + // Town screen TOWN_OPEN_TAVERN, - TOWN_SWAP_ARMIES, + TOWN_SWAP_ARMIES, // Swap garrisoned and visiting armies - RECRUITMENT_MAX, - RECRUITMENT_MIN, - RECRUITMENT_UPGRADE, // U - RECRUITMENT_UPGRADE_ALL, // A, U + // Creature & creature recruitment screen + RECRUITMENT_MAX, // Set number of creatures to recruit to max + RECRUITMENT_MIN, // Set number of creatures to recruit to min (1) + RECRUITMENT_UPGRADE, // Upgrade current creature + RECRUITMENT_UPGRADE_ALL, // Upgrade all creatures (Hill Fort / Skeleton Transformer) - KINGDOM_HEROES, - KINGDOM_TOWNS, + // Kingdom Overview window + KINGDOM_HEROES_TAB, + KINGDOM_TOWNS_TAB, + // Hero screen HERO_DISMISS, HERO_COMMANDER, HERO_LOOSE_FORMATION, HERO_TIGHT_FORMATION, HERO_TOGGLE_TACTICS, // b + // Spellbook screen SPELLBOOK_TAB_ADVENTURE, SPELLBOOK_TAB_COMBAT, diff --git a/client/gui/ShortcutHandler.cpp b/client/gui/ShortcutHandler.cpp index c0bea6ec3..263f1e463 100644 --- a/client/gui/ShortcutHandler.cpp +++ b/client/gui/ShortcutHandler.cpp @@ -14,11 +14,11 @@ #include "Shortcut.h" #include -std::vector ShortcutHandler::translateKeycode(SDL_Keycode key) +std::vector ShortcutHandler::translateKeycode(SDL_Keycode key) const { static const std::multimap keyToShortcut = { - {SDLK_RETURN, EShortcut::GLOBAL_CONFIRM }, - {SDLK_KP_ENTER, EShortcut::GLOBAL_CONFIRM }, + {SDLK_RETURN, EShortcut::GLOBAL_ACCEPT }, + {SDLK_KP_ENTER, EShortcut::GLOBAL_ACCEPT }, {SDLK_ESCAPE, EShortcut::GLOBAL_CANCEL }, {SDLK_RETURN, EShortcut::GLOBAL_RETURN }, {SDLK_KP_ENTER, EShortcut::GLOBAL_RETURN }, @@ -27,14 +27,14 @@ std::vector ShortcutHandler::translateKeycode(SDL_Keycode key) {SDLK_BACKSPACE, EShortcut::GLOBAL_BACKSPACE }, {SDLK_TAB, EShortcut::GLOBAL_MOVE_FOCUS }, {SDLK_o, EShortcut::GLOBAL_OPTIONS }, - {SDLK_LEFT, EShortcut::SELECT_LEFT }, - {SDLK_RIGHT, EShortcut::SELECT_RIGHT }, - {SDLK_UP, EShortcut::SELECT_UP }, - {SDLK_DOWN, EShortcut::SELECT_DOWN }, - {SDLK_HOME, EShortcut::SELECT_FIRST }, - {SDLK_END, EShortcut::SELECT_LAST }, - {SDLK_PAGEUP, EShortcut::SELECT_PAGE_UP }, - {SDLK_PAGEDOWN, EShortcut::SELECT_PAGE_DOWN }, + {SDLK_LEFT, EShortcut::MOVE_LEFT }, + {SDLK_RIGHT, EShortcut::MOVE_RIGHT }, + {SDLK_UP, EShortcut::MOVE_UP }, + {SDLK_DOWN, EShortcut::MOVE_DOWN }, + {SDLK_HOME, EShortcut::MOVE_FIRST }, + {SDLK_END, EShortcut::MOVE_LAST }, + {SDLK_PAGEUP, EShortcut::MOVE_PAGE_UP }, + {SDLK_PAGEDOWN, EShortcut::MOVE_PAGE_DOWN }, {SDLK_1, EShortcut::SELECT_INDEX_1 }, {SDLK_2, EShortcut::SELECT_INDEX_2 }, {SDLK_3, EShortcut::SELECT_INDEX_3 }, @@ -43,9 +43,9 @@ std::vector ShortcutHandler::translateKeycode(SDL_Keycode key) {SDLK_6, EShortcut::SELECT_INDEX_6 }, {SDLK_7, EShortcut::SELECT_INDEX_7 }, {SDLK_8, EShortcut::SELECT_INDEX_8 }, - {SDLK_n, EShortcut::MAIN_MENU_NEW }, - {SDLK_l, EShortcut::MAIN_MENU_LOAD }, - {SDLK_h, EShortcut::MAIN_MENU_SCORES }, + {SDLK_n, EShortcut::MAIN_MENU_NEW_GAME }, + {SDLK_l, EShortcut::MAIN_MENU_LOAD_GAME }, + {SDLK_h, EShortcut::MAIN_MENU_HIGH_SCORES }, {SDLK_c, EShortcut::MAIN_MENU_CREDITS }, {SDLK_q, EShortcut::MAIN_MENU_QUIT }, {SDLK_b, EShortcut::MAIN_MENU_BACK }, @@ -67,17 +67,17 @@ std::vector ShortcutHandler::translateKeycode(SDL_Keycode key) {SDLK_r, EShortcut::LOBBY_RANDOM_MAP }, {SDLK_h, EShortcut::LOBBY_HIDE_CHAT }, {SDLK_a, EShortcut::LOBBY_ADDITIONAL_OPTIONS }, - {SDLK_s, EShortcut::LOBBY_SELECT }, + {SDLK_s, EShortcut::LOBBY_SELECT_SCENARIO }, {SDLK_e, EShortcut::GAME_END_TURN }, {SDLK_l, EShortcut::GAME_LOAD_GAME }, {SDLK_s, EShortcut::GAME_SAVE_GAME }, {SDLK_r, EShortcut::GAME_RESTART_GAME }, {SDLK_m, EShortcut::GAME_TO_MAIN_MENU }, {SDLK_q, EShortcut::GAME_QUIT_GAME }, - {SDLK_t, EShortcut::GAME_MARKETPLACE }, - {SDLK_g, EShortcut::GAME_THIEVES_GUILD }, + {SDLK_t, EShortcut::GAME_OPEN_MARKETPLACE }, + {SDLK_g, EShortcut::GAME_OPEN_THIEVES_GUILD }, {SDLK_TAB, EShortcut::GAME_ACTIVATE_CONSOLE }, - {SDLK_o, EShortcut::ADVENTURE_OPTIONS }, + {SDLK_o, EShortcut::ADVENTURE_GAME_OPTIONS }, {SDLK_F6, EShortcut::ADVENTURE_TOGGLE_GRID }, {SDLK_z, EShortcut::ADVENTURE_TOGGLE_SLEEP }, {SDLK_w, EShortcut::ADVENTURE_TOGGLE_SLEEP }, @@ -91,6 +91,10 @@ std::vector ShortcutHandler::translateKeycode(SDL_Keycode key) {SDLK_KP_7, EShortcut::ADVENTURE_MOVE_HERO_NW }, {SDLK_KP_8, EShortcut::ADVENTURE_MOVE_HERO_NN }, {SDLK_KP_9, EShortcut::ADVENTURE_MOVE_HERO_NE }, + {SDLK_DOWN, EShortcut::ADVENTURE_MOVE_HERO_SS }, + {SDLK_LEFT, EShortcut::ADVENTURE_MOVE_HERO_WW }, + {SDLK_RIGHT, EShortcut::ADVENTURE_MOVE_HERO_EE }, + {SDLK_UP, EShortcut::ADVENTURE_MOVE_HERO_NN }, {SDLK_RETURN, EShortcut::ADVENTURE_VIEW_SELECTED }, {SDLK_KP_ENTER, EShortcut::ADVENTURE_VIEW_SELECTED }, // {SDLK_, EShortcut::ADVENTURE_NEXT_OBJECT }, @@ -129,8 +133,8 @@ std::vector ShortcutHandler::translateKeycode(SDL_Keycode key) {SDLK_u, EShortcut::RECRUITMENT_UPGRADE }, {SDLK_a, EShortcut::RECRUITMENT_UPGRADE_ALL }, {SDLK_u, EShortcut::RECRUITMENT_UPGRADE_ALL }, - {SDLK_h, EShortcut::KINGDOM_HEROES }, - {SDLK_t, EShortcut::KINGDOM_TOWNS }, + {SDLK_h, EShortcut::KINGDOM_HEROES_TAB }, + {SDLK_t, EShortcut::KINGDOM_TOWNS_TAB }, {SDLK_d, EShortcut::HERO_DISMISS }, {SDLK_c, EShortcut::HERO_COMMANDER }, {SDLK_l, EShortcut::HERO_LOOSE_FORMATION }, @@ -154,24 +158,24 @@ std::vector ShortcutHandler::translateKeycode(SDL_Keycode key) return result; } -EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) +EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const { static const std::map shortcutNames = { - {"globalConfirm", EShortcut::GLOBAL_CONFIRM }, + {"globalAccept", EShortcut::GLOBAL_ACCEPT }, {"globalCancel", EShortcut::GLOBAL_CANCEL }, {"globalReturn", EShortcut::GLOBAL_RETURN }, {"globalFullscreen", EShortcut::GLOBAL_FULLSCREEN }, {"globalOptions", EShortcut::GLOBAL_OPTIONS }, {"globalBackspace", EShortcut::GLOBAL_BACKSPACE }, {"globalMoveFocus", EShortcut::GLOBAL_MOVE_FOCUS }, - {"selectLeft", EShortcut::SELECT_LEFT }, - {"selectRight", EShortcut::SELECT_RIGHT }, - {"selectUp", EShortcut::SELECT_UP }, - {"selectDown", EShortcut::SELECT_DOWN }, - {"selectFirst", EShortcut::SELECT_FIRST }, - {"selectLast", EShortcut::SELECT_LAST }, - {"selectPageUp", EShortcut::SELECT_PAGE_UP }, - {"selectPageDown", EShortcut::SELECT_PAGE_DOWN }, + {"moveLeft", EShortcut::MOVE_LEFT }, + {"moveRight", EShortcut::MOVE_RIGHT }, + {"moveUp", EShortcut::MOVE_UP }, + {"moveDown", EShortcut::MOVE_DOWN }, + {"moveFirst", EShortcut::MOVE_FIRST }, + {"moveLast", EShortcut::MOVE_LAST }, + {"movePageUp", EShortcut::MOVE_PAGE_UP }, + {"movePageDown", EShortcut::MOVE_PAGE_DOWN }, {"selectIndex1", EShortcut::SELECT_INDEX_1 }, {"selectIndex2", EShortcut::SELECT_INDEX_2 }, {"selectIndex3", EShortcut::SELECT_INDEX_3 }, @@ -180,9 +184,9 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) {"selectIndex6", EShortcut::SELECT_INDEX_6 }, {"selectIndex7", EShortcut::SELECT_INDEX_7 }, {"selectIndex8", EShortcut::SELECT_INDEX_8 }, - {"mainMenuNew", EShortcut::MAIN_MENU_NEW }, - {"mainMenuLoad", EShortcut::MAIN_MENU_LOAD }, - {"mainMenuScores", EShortcut::MAIN_MENU_SCORES }, + {"mainMenuNewGame", EShortcut::MAIN_MENU_NEW_GAME }, + {"mainMenuLoadGame", EShortcut::MAIN_MENU_LOAD_GAME }, + {"mainMenuHighScores", EShortcut::MAIN_MENU_HIGH_SCORES }, {"mainMenuCredits", EShortcut::MAIN_MENU_CREDITS }, {"mainMenuQuit", EShortcut::MAIN_MENU_QUIT }, {"mainMenuBack", EShortcut::MAIN_MENU_BACK }, @@ -200,17 +204,17 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) {"lobbyRandomMap", EShortcut::LOBBY_RANDOM_MAP }, {"lobbyHideChat", EShortcut::LOBBY_HIDE_CHAT }, {"lobbyAdditionalOptions", EShortcut::LOBBY_ADDITIONAL_OPTIONS }, - {"lobbySelect", EShortcut::LOBBY_SELECT }, + {"lobbySelectScenario", EShortcut::LOBBY_SELECT_SCENARIO }, {"gameEndTurn", EShortcut::GAME_END_TURN }, {"gameLoadGame", EShortcut::GAME_LOAD_GAME }, {"gameSaveGame", EShortcut::GAME_SAVE_GAME }, {"gameRestartGame", EShortcut::GAME_RESTART_GAME }, {"gameMainMenu", EShortcut::GAME_TO_MAIN_MENU }, {"gameQuitGame", EShortcut::GAME_QUIT_GAME }, - {"gameMarketplace", EShortcut::GAME_MARKETPLACE }, - {"gameThievesGuild", EShortcut::GAME_THIEVES_GUILD }, + {"gameOpenMarketplace", EShortcut::GAME_OPEN_MARKETPLACE }, + {"gameOpenThievesGuild", EShortcut::GAME_OPEN_THIEVES_GUILD }, {"gameActivateConsole", EShortcut::GAME_ACTIVATE_CONSOLE }, - {"adventureOptions", EShortcut::ADVENTURE_OPTIONS }, + {"adventureGameOptions", EShortcut::ADVENTURE_GAME_OPTIONS }, {"adventureToggleGrid", EShortcut::ADVENTURE_TOGGLE_GRID }, {"adventureToggleSleep", EShortcut::ADVENTURE_TOGGLE_SLEEP }, {"adventureMoveHero", EShortcut::ADVENTURE_MOVE_HERO }, @@ -257,8 +261,8 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) {"recruitmentMin", EShortcut::RECRUITMENT_MIN }, {"recruitmentUpgrade", EShortcut::RECRUITMENT_UPGRADE }, {"recruitmentUpgradeAll", EShortcut::RECRUITMENT_UPGRADE_ALL }, - {"kingdomHeroes", EShortcut::KINGDOM_HEROES }, - {"kingdomTowns", EShortcut::KINGDOM_TOWNS }, + {"kingdomHeroesTab", EShortcut::KINGDOM_HEROES_TAB }, + {"kingdomTownsTab", EShortcut::KINGDOM_TOWNS_TAB }, {"heroDismiss", EShortcut::HERO_DISMISS }, {"heroCommander", EShortcut::HERO_COMMANDER }, {"heroLooseFormation", EShortcut::HERO_LOOSE_FORMATION }, diff --git a/client/gui/ShortcutHandler.h b/client/gui/ShortcutHandler.h index c88dc4ff4..46dc9937c 100644 --- a/client/gui/ShortcutHandler.h +++ b/client/gui/ShortcutHandler.h @@ -17,8 +17,8 @@ class ShortcutHandler { public: /// returns list of shortcuts assigned to provided SDL keycode - std::vector translateKeycode(SDL_Keycode key); + std::vector translateKeycode(SDL_Keycode key) const; /// attempts to find shortcut by its unique identifier. Returns EShortcut::NONE on failure - EShortcut findShortcut(const std::string & identifier ); + EShortcut findShortcut(const std::string & identifier ) const; }; diff --git a/client/lobby/CBonusSelection.cpp b/client/lobby/CBonusSelection.cpp index ef43218fc..61327b9e7 100644 --- a/client/lobby/CBonusSelection.cpp +++ b/client/lobby/CBonusSelection.cpp @@ -68,8 +68,8 @@ CBonusSelection::CBonusSelection() panelBackground = std::make_shared("CAMPBRF.BMP", 456, 6); - buttonStart = std::make_shared(Point(475, 536), "CBBEGIB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::startMap, this), EShortcut::GLOBAL_CONFIRM); - buttonRestart = std::make_shared(Point(475, 536), "CBRESTB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::restartMap, this), EShortcut::GLOBAL_CONFIRM); + buttonStart = std::make_shared(Point(475, 536), "CBBEGIB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::startMap, this), EShortcut::GLOBAL_ACCEPT); + buttonRestart = std::make_shared(Point(475, 536), "CBRESTB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::restartMap, this), EShortcut::GLOBAL_ACCEPT); buttonBack = std::make_shared(Point(624, 536), "CBCANCB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::goBack, this), EShortcut::GLOBAL_CANCEL); campaignName = std::make_shared(481, 28, FONT_BIG, ETextAlignment::TOPLEFT, Colors::YELLOW, CSH->si->getCampaignName()); diff --git a/client/lobby/CLobbyScreen.cpp b/client/lobby/CLobbyScreen.cpp index 39dedcef6..4ca4230d4 100644 --- a/client/lobby/CLobbyScreen.cpp +++ b/client/lobby/CLobbyScreen.cpp @@ -41,7 +41,7 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType) { tabSel->callOnSelect = std::bind(&IServerAPI::setMapInfo, CSH, _1, nullptr); - buttonSelect = std::make_shared(Point(411, 80), "GSPBUTT.DEF", CGI->generaltexth->zelp[45], 0, EShortcut::LOBBY_SELECT); + buttonSelect = std::make_shared(Point(411, 80), "GSPBUTT.DEF", CGI->generaltexth->zelp[45], 0, EShortcut::LOBBY_SELECT_SCENARIO); buttonSelect->addCallback([&]() { toggleTab(tabSel); diff --git a/client/lobby/CSelectionBase.cpp b/client/lobby/CSelectionBase.cpp index 6eba2941b..5475056a4 100644 --- a/client/lobby/CSelectionBase.cpp +++ b/client/lobby/CSelectionBase.cpp @@ -321,7 +321,7 @@ CChatBox::CChatBox(const Rect & rect) void CChatBox::keyPressed(EShortcut key) { - if(key == EShortcut::GLOBAL_CONFIRM && inputBox->getText().size()) + if(key == EShortcut::GLOBAL_ACCEPT && inputBox->getText().size()) { CSH->sendMessage(inputBox->getText()); inputBox->setText(""); diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index 91382a0bf..75e42d1b6 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -287,22 +287,22 @@ void SelectionTab::keyPressed(EShortcut key) int moveBy = 0; switch(key) { - case EShortcut::SELECT_UP: + case EShortcut::MOVE_UP: moveBy = -1; break; - case EShortcut::SELECT_DOWN: + case EShortcut::MOVE_DOWN: moveBy = +1; break; - case EShortcut::SELECT_PAGE_UP: + case EShortcut::MOVE_PAGE_UP: moveBy = -(int)listItems.size() + 1; break; - case EShortcut::SELECT_PAGE_DOWN: + case EShortcut::MOVE_PAGE_DOWN: moveBy = +(int)listItems.size() - 1; break; - case EShortcut::SELECT_FIRST: + case EShortcut::MOVE_FIRST: select(-slider->getValue()); return; - case EShortcut::SELECT_LAST: + case EShortcut::MOVE_LAST: select((int)curItems.size() - slider->getValue()); return; default: diff --git a/client/mainmenu/CMainMenu.cpp b/client/mainmenu/CMainMenu.cpp index cf9f866d5..662519564 100644 --- a/client/mainmenu/CMainMenu.cpp +++ b/client/mainmenu/CMainMenu.cpp @@ -232,7 +232,7 @@ std::shared_ptr CMenuEntry::createButton(CMenuScreen * parent, const Js if(posy < 0) posy = pos.h + posy; - EShortcut shortcut = GH.getShortcutsHandler().findShortcut(button["shortcut"].String()); + EShortcut shortcut = GH.shortcutsHandler().findShortcut(button["shortcut"].String()); auto result = std::make_shared(Point(posx, posy), button["name"].String(), help, command, shortcut); @@ -426,7 +426,7 @@ CMultiPlayers::CMultiPlayers(const std::string & firstPlayer, ESelectionScreen S inputNames[i]->cb += std::bind(&CMultiPlayers::onChange, this, _1); } - buttonOk = std::make_shared(Point(95, 338), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CMultiPlayers::enterSelectionScreen, this), EShortcut::GLOBAL_CONFIRM); + buttonOk = std::make_shared(Point(95, 338), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CMultiPlayers::enterSelectionScreen, this), EShortcut::GLOBAL_ACCEPT); buttonCancel = std::make_shared(Point(205, 338), "MUBCANC.DEF", CGI->generaltexth->zelp[561], [=](){ close();}, EShortcut::GLOBAL_CANCEL); statusBar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(7, 381, 348, 18), 7, 381)); @@ -475,7 +475,7 @@ CSimpleJoinScreen::CSimpleJoinScreen(bool host) inputAddress->cb += std::bind(&CSimpleJoinScreen::onChange, this, _1); inputPort->cb += std::bind(&CSimpleJoinScreen::onChange, this, _1); inputPort->filters += std::bind(&CTextInput::numberFilter, _1, _2, 0, 65535); - buttonOk = std::make_shared(Point(26, 142), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CSimpleJoinScreen::connectToServer, this), EShortcut::GLOBAL_CONFIRM); + buttonOk = std::make_shared(Point(26, 142), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CSimpleJoinScreen::connectToServer, this), EShortcut::GLOBAL_ACCEPT); inputAddress->giveFocus(); } diff --git a/client/widgets/Buttons.cpp b/client/widgets/Buttons.cpp index 89d7c0428..45ea19df4 100644 --- a/client/widgets/Buttons.cpp +++ b/client/widgets/Buttons.cpp @@ -806,32 +806,32 @@ void CSlider::keyPressed(EShortcut key) int moveDest = value; switch(key) { - case EShortcut::SELECT_UP: + case EShortcut::MOVE_UP: if (!horizontal) moveDest = value - scrollStep; break; - case EShortcut::SELECT_LEFT: + case EShortcut::MOVE_LEFT: if (horizontal) moveDest = value - scrollStep; break; - case EShortcut::SELECT_DOWN: + case EShortcut::MOVE_DOWN: if (!horizontal) moveDest = value + scrollStep; break; - case EShortcut::SELECT_RIGHT: + case EShortcut::MOVE_RIGHT: if (horizontal) moveDest = value + scrollStep; break; - case EShortcut::SELECT_PAGE_UP: + case EShortcut::MOVE_PAGE_UP: moveDest = value - capacity + scrollStep; break; - case EShortcut::SELECT_PAGE_DOWN: + case EShortcut::MOVE_PAGE_DOWN: moveDest = value + capacity - scrollStep; break; - case EShortcut::SELECT_FIRST: + case EShortcut::MOVE_FIRST: moveDest = 0; break; - case EShortcut::SELECT_LAST: + case EShortcut::MOVE_LAST: moveDest = amount - capacity; break; default: diff --git a/client/widgets/CArtifactsOfHeroBase.cpp b/client/widgets/CArtifactsOfHeroBase.cpp index eae959a03..d083cf277 100644 --- a/client/widgets/CArtifactsOfHeroBase.cpp +++ b/client/widgets/CArtifactsOfHeroBase.cpp @@ -12,7 +12,7 @@ #include "../gui/CGuiHandler.h" #include "../gui/CursorHandler.h" -#include "../gui/EShortcut.h +#include "../gui/EShortcut.h" #include "Buttons.h" @@ -83,8 +83,8 @@ void CArtifactsOfHeroBase::init( artPlace->leftClickCallback = lClickCallback; artPlace->rightClickCallback = rClickCallback; } - leftBackpackRoll = std::make_shared(Point(379, 364), "hsbtns3.def", CButton::tooltip(), [scrollHandler]() { scrollHandler(-1); }, EShortcut::SELECT_LEFT); - rightBackpackRoll = std::make_shared(Point(632, 364), "hsbtns5.def", CButton::tooltip(), [scrollHandler]() { scrollHandler(+1); }, EShortcut::SELECT_RIGHT); + leftBackpackRoll = std::make_shared(Point(379, 364), "hsbtns3.def", CButton::tooltip(), [scrollHandler]() { scrollHandler(-1); }, EShortcut::MOVE_LEFT); + rightBackpackRoll = std::make_shared(Point(632, 364), "hsbtns5.def", CButton::tooltip(), [scrollHandler]() { scrollHandler(+1); }, EShortcut::MOVE_RIGHT); leftBackpackRoll->block(true); rightBackpackRoll->block(true); } diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 08f93bedf..fbd274977 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -1312,10 +1312,10 @@ void CCastleInterface::keyPressed(EShortcut key) { switch(key) { - case EShortcut::SELECT_UP: + case EShortcut::MOVE_UP: townlist->selectPrev(); break; - case EShortcut::SELECT_DOWN: + case EShortcut::MOVE_DOWN: townlist->selectNext(); break; case EShortcut::TOWN_SWAP_ARMIES: @@ -1485,7 +1485,7 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin std::string tooltipYes = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % building->getNameTranslated()); std::string tooltipNo = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % building->getNameTranslated()); - buy = std::make_shared(Point(45, 446), "IBUY30", CButton::tooltip(tooltipYes), [&](){ buyFunc(); }, EShortcut::GLOBAL_CONFIRM); + buy = std::make_shared(Point(45, 446), "IBUY30", CButton::tooltip(tooltipYes), [&](){ buyFunc(); }, EShortcut::GLOBAL_ACCEPT); buy->setBorderColor(Colors::METALLIC_GOLD); buy->block(state!=7 || LOCPLINT->playerID != town->tempOwner); @@ -1863,7 +1863,7 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art std::to_string(aid.toArtifact(CGI->artifacts())->getPrice())); std::string text = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % creature->getNameSingularTranslated()); - buy = std::make_shared(Point(42, 312), "IBUY30.DEF", CButton::tooltip(text), [&](){ close(); }, EShortcut::GLOBAL_CONFIRM); + buy = std::make_shared(Point(42, 312), "IBUY30.DEF", CButton::tooltip(text), [&](){ close(); }, EShortcut::GLOBAL_ACCEPT); text = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % creature->getNameSingularTranslated()); cancel = std::make_shared(Point(224, 312), "ICANCEL.DEF", CButton::tooltip(text), [&](){ close(); }, EShortcut::GLOBAL_CANCEL); diff --git a/client/windows/CCreatureWindow.cpp b/client/windows/CCreatureWindow.cpp index 6aca3b1fb..4c15912d1 100644 --- a/client/windows/CCreatureWindow.cpp +++ b/client/windows/CCreatureWindow.cpp @@ -455,8 +455,8 @@ CStackWindow::CommanderMainSection::CommanderMainSection(CStackWindow * owner, i abilities = std::make_shared(onCreate, Point(38, 3+pos.h), Point(63, 0), 6, abilitiesCount); - leftBtn = std::make_shared(Point(10, pos.h + 6), "hsbtns3.def", CButton::tooltip(), [=](){ abilities->moveToPrev(); }, EShortcut::SELECT_LEFT); - rightBtn = std::make_shared(Point(411, pos.h + 6), "hsbtns5.def", CButton::tooltip(), [=](){ abilities->moveToNext(); }, EShortcut::SELECT_RIGHT); + leftBtn = std::make_shared(Point(10, pos.h + 6), "hsbtns3.def", CButton::tooltip(), [=](){ abilities->moveToPrev(); }, EShortcut::MOVE_LEFT); + rightBtn = std::make_shared(Point(411, pos.h + 6), "hsbtns5.def", CButton::tooltip(), [=](){ abilities->moveToNext(); }, EShortcut::MOVE_RIGHT); if(abilitiesCount <= 6) { diff --git a/client/windows/CKingdomInterface.cpp b/client/windows/CKingdomInterface.cpp index bcb91c921..d5b761b70 100644 --- a/client/windows/CKingdomInterface.cpp +++ b/client/windows/CKingdomInterface.cpp @@ -614,11 +614,11 @@ void CKingdomInterface::generateButtons() //Main control buttons btnHeroes = std::make_shared(Point(748, 28+footerPos), "OVBUTN1.DEF", CButton::tooltip(CGI->generaltexth->overview[11], CGI->generaltexth->overview[6]), - std::bind(&CKingdomInterface::activateTab, this, 0), EShortcut::KINGDOM_HEROES); + std::bind(&CKingdomInterface::activateTab, this, 0), EShortcut::KINGDOM_HEROES_TAB); btnHeroes->block(true); btnTowns = std::make_shared(Point(748, 64+footerPos), "OVBUTN6.DEF", CButton::tooltip(CGI->generaltexth->overview[12], CGI->generaltexth->overview[7]), - std::bind(&CKingdomInterface::activateTab, this, 1), EShortcut::KINGDOM_TOWNS); + std::bind(&CKingdomInterface::activateTab, this, 1), EShortcut::KINGDOM_TOWNS_TAB); btnExit = std::make_shared(Point(748,99+footerPos), "OVBUTN1.DEF", CButton::tooltip(CGI->generaltexth->allTexts[600]), std::bind(&CKingdomInterface::close, this), EShortcut::GLOBAL_RETURN); diff --git a/client/windows/CQuestLog.cpp b/client/windows/CQuestLog.cpp index ade6ec981..32cbdf5ee 100644 --- a/client/windows/CQuestLog.cpp +++ b/client/windows/CQuestLog.cpp @@ -128,7 +128,7 @@ CQuestLog::CQuestLog (const std::vector & Quests) minimap = std::make_shared(Rect(12, 12, 169, 169)); // TextBox have it's own 4 pixel padding from top at least for English. To achieve 10px from both left and top only add 6px margin description = std::make_shared("", Rect(205, 18, 385, DESCRIPTION_HEIGHT_MAX), CSlider::BROWN, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE); - ok = std::make_shared(Point(539, 398), "IOKAY.DEF", CGI->generaltexth->zelp[445], std::bind(&CQuestLog::close, this), EShortcut::GLOBAL_CONFIRM); + ok = std::make_shared(Point(539, 398), "IOKAY.DEF", CGI->generaltexth->zelp[445], std::bind(&CQuestLog::close, this), EShortcut::GLOBAL_ACCEPT); // Both button and lable are shifted to -2px by x and y to not make them actually look like they're on same line with quests list and ok button hideCompleteButton = std::make_shared(Point(10, 396), "sysopchk.def", CButton::tooltipLocalized("vcmi.questLog.hideComplete"), std::bind(&CQuestLog::toggleComplete, this, _1)); hideCompleteLabel = std::make_shared(46, 398, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("vcmi.questLog.hideComplete.hover")); diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index 3e441934d..3c5cd0224 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -418,16 +418,16 @@ void CSpellWindow::keyPressed(EShortcut key) fexitb(); break; - case EShortcut::SELECT_LEFT: + case EShortcut::MOVE_LEFT: fLcornerb(); break; - case EShortcut::SELECT_RIGHT: + case EShortcut::MOVE_RIGHT: fRcornerb(); break; - case EShortcut::SELECT_UP: - case EShortcut::SELECT_DOWN: + case EShortcut::MOVE_UP: + case EShortcut::MOVE_DOWN: { - bool down = key == EShortcut::SELECT_DOWN; + bool down = key == EShortcut::MOVE_DOWN; static const int schoolsOrder[] = { 0, 3, 1, 2, 4 }; int index = -1; while(schoolsOrder[++index] != selectedTab); diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index bed47e646..1d1885649 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -212,7 +212,7 @@ CRecruitmentWindow::CRecruitmentWindow(const CGDwelling * Dwelling, int Level, c slider = std::make_shared(Point(176,279),135,std::bind(&CRecruitmentWindow::sliderMoved,this, _1),0,0,0,true); maxButton = std::make_shared(Point(134, 313), "IRCBTNS.DEF", CGI->generaltexth->zelp[553], std::bind(&CSlider::moveToMax, slider), EShortcut::RECRUITMENT_MAX); - buyButton = std::make_shared(Point(212, 313), "IBY6432.DEF", CGI->generaltexth->zelp[554], std::bind(&CRecruitmentWindow::buy, this), EShortcut::GLOBAL_CONFIRM); + buyButton = std::make_shared(Point(212, 313), "IBY6432.DEF", CGI->generaltexth->zelp[554], std::bind(&CRecruitmentWindow::buy, this), EShortcut::GLOBAL_ACCEPT); cancelButton = std::make_shared(Point(290, 313), "ICN6432.DEF", CGI->generaltexth->zelp[555], std::bind(&CRecruitmentWindow::close, this), EShortcut::GLOBAL_CANCEL); title = std::make_shared(243, 32, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW); @@ -314,7 +314,7 @@ CSplitWindow::CSplitWindow(const CCreature * creature, std::function(Point(20, 263), "IOK6432", CButton::tooltip(), std::bind(&CSplitWindow::apply, this), EShortcut::GLOBAL_CONFIRM); + ok = std::make_shared(Point(20, 263), "IOK6432", CButton::tooltip(), std::bind(&CSplitWindow::apply, this), EShortcut::GLOBAL_ACCEPT); cancel = std::make_shared(Point(214, 263), "ICN6432", CButton::tooltip(), std::bind(&CSplitWindow::close, this), EShortcut::GLOBAL_CANCEL); int sliderPosition = total - leftMin - rightMin; @@ -404,7 +404,7 @@ CLevelWindow::CLevelWindow(const CGHeroInstance * hero, PrimarySkill::PrimarySki } portrait = std::make_shared("PortraitsLarge", hero->portrait, 0, 170, 66); - ok = std::make_shared(Point(297, 413), "IOKAY", CButton::tooltip(), std::bind(&CLevelWindow::close, this), EShortcut::GLOBAL_CONFIRM); + ok = std::make_shared(Point(297, 413), "IOKAY", CButton::tooltip(), std::bind(&CLevelWindow::close, this), EShortcut::GLOBAL_ACCEPT); //%s has gained a level. mainTitle = std::make_shared(192, 33, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, boost::str(boost::format(CGI->generaltexth->allTexts[444]) % hero->getNameTranslated())); @@ -461,7 +461,7 @@ CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj) statusbar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); cancel = std::make_shared(Point(310, 428), "ICANCEL.DEF", CButton::tooltip(CGI->generaltexth->tavernInfo[7]), std::bind(&CTavernWindow::close, this), EShortcut::GLOBAL_CANCEL); - recruit = std::make_shared(Point(272, 355), "TPTAV01.DEF", CButton::tooltip(), std::bind(&CTavernWindow::recruitb, this), EShortcut::GLOBAL_CONFIRM); + recruit = std::make_shared(Point(272, 355), "TPTAV01.DEF", CButton::tooltip(), std::bind(&CTavernWindow::recruitb, this), EShortcut::GLOBAL_ACCEPT); thiefGuild = std::make_shared(Point(22, 428), "TPTAV02.DEF", CButton::tooltip(CGI->generaltexth->tavernInfo[5]), std::bind(&CTavernWindow::thievesguildb, this), EShortcut::ADVENTURE_THIEVES_GUILD); if(LOCPLINT->cb->getResourceAmount(EGameResID::GOLD) < GameConstants::HERO_GOLD_COST) //not enough gold @@ -983,7 +983,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, luck[b] = std::make_shared(false, Rect(Point(212 + 490 * b, 39), Point(32, 32)), true); } - quit = std::make_shared(Point(732, 567), "IOKAY.DEF", CGI->generaltexth->zelp[600], std::bind(&CExchangeWindow::close, this), EShortcut::GLOBAL_CONFIRM); + quit = std::make_shared(Point(732, 567), "IOKAY.DEF", CGI->generaltexth->zelp[600], std::bind(&CExchangeWindow::close, this), EShortcut::GLOBAL_ACCEPT); if(queryID.getNum() > 0) quit->addCallback([=](){ LOCPLINT->cb->selectionMade(0, queryID); }); @@ -1100,7 +1100,7 @@ CShipyardWindow::CShipyardWindow(const TResources & cost, int state, BoatId boat woodPic = std::make_shared("RESOURCE", GameResID(EGameResID::WOOD), 0, 196, 244); quit = std::make_shared(Point(224, 312), "ICANCEL", CButton::tooltip(CGI->generaltexth->allTexts[599]), std::bind(&CShipyardWindow::close, this), EShortcut::GLOBAL_CANCEL); - build = std::make_shared(Point(42, 312), "IBUY30", CButton::tooltip(CGI->generaltexth->allTexts[598]), std::bind(&CShipyardWindow::close, this), EShortcut::GLOBAL_CONFIRM); + build = std::make_shared(Point(42, 312), "IBUY30", CButton::tooltip(CGI->generaltexth->allTexts[598]), std::bind(&CShipyardWindow::close, this), EShortcut::GLOBAL_ACCEPT); build->addCallback(onBuy); for(GameResID i = EGameResID::WOOD; i <= EGameResID::GOLD; ++i) @@ -1201,7 +1201,7 @@ CTransformerWindow::CTransformerWindow(const CGHeroInstance * _hero, const CGTow } all = std::make_shared(Point(146, 416), "ALTARMY.DEF", CGI->generaltexth->zelp[590], [&](){ addAll(); }, EShortcut::RECRUITMENT_UPGRADE_ALL); - convert = std::make_shared(Point(269, 416), "ALTSACR.DEF", CGI->generaltexth->zelp[591], [&](){ makeDeal(); }, EShortcut::GLOBAL_CONFIRM); + convert = std::make_shared(Point(269, 416), "ALTSACR.DEF", CGI->generaltexth->zelp[591], [&](){ makeDeal(); }, EShortcut::GLOBAL_ACCEPT); cancel = std::make_shared(Point(392, 416), "ICANCEL.DEF", CGI->generaltexth->zelp[592], [&](){ close(); },EShortcut::GLOBAL_CANCEL); statusbar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); @@ -1316,7 +1316,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket for(int i=0; i(this, goods[i], 54+i*104, 234)); - cancel = std::make_shared(Point(200, 313), "IOKAY.DEF", CGI->generaltexth->zelp[632], [&](){ close(); }, EShortcut::GLOBAL_CONFIRM); + cancel = std::make_shared(Point(200, 313), "IOKAY.DEF", CGI->generaltexth->zelp[632], [&](){ close(); }, EShortcut::GLOBAL_ACCEPT); statusbar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); } @@ -1354,7 +1354,7 @@ CUnivConfirmWindow::CUnivConfirmWindow(CUniversityWindow * owner_, int SKILL, bo boost::replace_first(text, "%s", CGI->skillh->getByIndex(SKILL)->getNameTranslated()); boost::replace_first(text, "%d", "2000"); - confirm = std::make_shared(Point(148, 299), "IBY6432.DEF", CButton::tooltip(hoverText, text), [=](){makeDeal(SKILL);}, EShortcut::GLOBAL_CONFIRM); + confirm = std::make_shared(Point(148, 299), "IBY6432.DEF", CButton::tooltip(hoverText, text), [=](){makeDeal(SKILL);}, EShortcut::GLOBAL_ACCEPT); confirm->block(!available); cancel = std::make_shared(Point(252,299), "ICANCEL.DEF", CGI->generaltexth->zelp[631], [&](){ close(); }, EShortcut::GLOBAL_CANCEL); @@ -1377,7 +1377,7 @@ CGarrisonWindow::CGarrisonWindow(const CArmedInstance * up, const CGHeroInstance auto split = std::make_shared(Point(88, 314), "IDV6432.DEF", CButton::tooltip(CGI->generaltexth->tcommands[3], ""), [&](){ garr->splitClick(); } ); garr->addSplitBtn(split); } - quit = std::make_shared(Point(399, 314), "IOK6432.DEF", CButton::tooltip(CGI->generaltexth->tcommands[8], ""), [&](){ close(); }, EShortcut::GLOBAL_CONFIRM); + quit = std::make_shared(Point(399, 314), "IOK6432.DEF", CButton::tooltip(CGI->generaltexth->tcommands[8], ""), [&](){ close(); }, EShortcut::GLOBAL_ACCEPT); std::string titleText; if(down->tempOwner == up->tempOwner) @@ -1442,7 +1442,7 @@ CHillFortWindow::CHillFortWindow(const CGHeroInstance * visitor, const CGObjectI for(auto image : { "APHLF4R.DEF", "APHLF4Y.DEF", "APHLF4G.DEF" }) upgradeAll->addImage(image); - quit = std::make_shared(Point(294, 275), "IOKAY.DEF", CButton::tooltip(), std::bind(&CHillFortWindow::close, this), EShortcut::GLOBAL_CONFIRM); + quit = std::make_shared(Point(294, 275), "IOKAY.DEF", CButton::tooltip(), std::bind(&CHillFortWindow::close, this), EShortcut::GLOBAL_ACCEPT); statusbar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); garr = std::make_shared(108, 60, 18, Point(), hero, nullptr); @@ -1827,7 +1827,7 @@ void CObjectListWindow::init(std::shared_ptr titleWidget_, std::stri Point(14, 151), Point(0, 25), 9, items.size(), 0, 1, Rect(262, -32, 256, 256) ); list->type |= REDRAW_PARENT; - ok = std::make_shared(Point(15, 402), "IOKAY.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::elementSelected, this), EShortcut::GLOBAL_CONFIRM); + ok = std::make_shared(Point(15, 402), "IOKAY.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::elementSelected, this), EShortcut::GLOBAL_ACCEPT); ok->block(!list->size()); } @@ -1881,22 +1881,22 @@ void CObjectListWindow::keyPressed (EShortcut key) switch(key) { - break; case EShortcut::SELECT_UP: + break; case EShortcut::MOVE_UP: sel -=1; - break; case EShortcut::SELECT_DOWN: + break; case EShortcut::MOVE_DOWN: sel +=1; - break; case EShortcut::SELECT_PAGE_UP: + break; case EShortcut::MOVE_PAGE_UP: sel -=9; - break; case EShortcut::SELECT_PAGE_DOWN: + break; case EShortcut::MOVE_PAGE_DOWN: sel +=9; - break; case EShortcut::SELECT_FIRST: + break; case EShortcut::MOVE_FIRST: sel = 0; - break; case EShortcut::SELECT_LAST: + break; case EShortcut::MOVE_LAST: sel = static_cast(items.size()); break; default: diff --git a/client/windows/InfoWindows.cpp b/client/windows/InfoWindows.cpp index 2843aa38c..191b4025a 100644 --- a/client/windows/InfoWindows.cpp +++ b/client/windows/InfoWindows.cpp @@ -135,6 +135,15 @@ CInfoWindow::CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo text->resize(text->label->textSize); } + if(buttons.size() == 1) + buttons.front()->assignedKey = EShortcut::GLOBAL_RETURN; + + if(buttons.size() == 2) + { + buttons.front()->assignedKey = EShortcut::GLOBAL_ACCEPT; + buttons.back()->assignedKey = EShortcut::GLOBAL_CANCEL; + } + for(auto & comp : comps) { comp->recActions = 0xff & ~DISPOSE; diff --git a/client/windows/QuickRecruitmentWindow.cpp b/client/windows/QuickRecruitmentWindow.cpp index 75ebd8307..46089f898 100644 --- a/client/windows/QuickRecruitmentWindow.cpp +++ b/client/windows/QuickRecruitmentWindow.cpp @@ -36,7 +36,7 @@ void QuickRecruitmentWindow::setCancelButton() void QuickRecruitmentWindow::setBuyButton() { - buyButton = std::make_shared(Point((pos.w / 2) - 32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purchaseUnits(); }, EShortcut::GLOBAL_CONFIRM); + buyButton = std::make_shared(Point((pos.w / 2) - 32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purchaseUnits(); }, EShortcut::GLOBAL_ACCEPT); buyButton->setImageOrder(0, 1, 2, 3); } From 6c637dd8e660240e76519c59ed853c756e41738e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 29 Apr 2023 13:48:21 +0300 Subject: [PATCH 24/43] Integrated hotkeys with InterfaceObjectConfigurable --- client/battle/BattleWindow.cpp | 90 ++++++++-------------- client/gui/InterfaceObjectConfigurable.cpp | 66 ++++++++++++++-- client/gui/InterfaceObjectConfigurable.h | 18 ++++- client/gui/Shortcut.h | 1 + client/gui/ShortcutHandler.cpp | 2 + config/widgets/battleWindow.json | 11 --- 6 files changed, 109 insertions(+), 79 deletions(-) diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 5ccd18b6c..4d9eb5eb7 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -51,19 +51,23 @@ BattleWindow::BattleWindow(BattleInterface & owner): const JsonNode config(ResourceID("config/widgets/BattleWindow.json")); - addCallback("options", std::bind(&BattleWindow::bOptionsf, this)); - addCallback("surrender", std::bind(&BattleWindow::bSurrenderf, this)); - addCallback("flee", std::bind(&BattleWindow::bFleef, this)); - addCallback("autofight", std::bind(&BattleWindow::bAutofightf, this)); - addCallback("spellbook", std::bind(&BattleWindow::bSpellf, this)); - addCallback("wait", std::bind(&BattleWindow::bWaitf, this)); - addCallback("defence", std::bind(&BattleWindow::bDefencef, this)); - addCallback("consoleUp", std::bind(&BattleWindow::bConsoleUpf, this)); - addCallback("consoleDown", std::bind(&BattleWindow::bConsoleDownf, this)); - addCallback("tacticNext", std::bind(&BattleWindow::bTacticNextStack, this)); - addCallback("tacticEnd", std::bind(&BattleWindow::bTacticPhaseEnd, this)); - addCallback("alternativeAction", std::bind(&BattleWindow::bSwitchActionf, this)); - + addShortcut(EShortcut::GLOBAL_OPTIONS, std::bind(&BattleWindow::bOptionsf, this)); + addShortcut(EShortcut::BATTLE_SURRENDER, std::bind(&BattleWindow::bSurrenderf, this)); + addShortcut(EShortcut::BATTLE_RETREAT, std::bind(&BattleWindow::bFleef, this)); + addShortcut(EShortcut::BATTLE_AUTOCOMBAT, std::bind(&BattleWindow::bAutofightf, this)); + addShortcut(EShortcut::BATTLE_CAST_SPELL, std::bind(&BattleWindow::bSpellf, this)); + addShortcut(EShortcut::BATTLE_WAIT, std::bind(&BattleWindow::bWaitf, this)); + addShortcut(EShortcut::BATTLE_DEFEND, std::bind(&BattleWindow::bDefencef, this)); + addShortcut(EShortcut::BATTLE_CONSOLE_UP, std::bind(&BattleWindow::bConsoleUpf, this)); + addShortcut(EShortcut::BATTLE_CONSOLE_DOWN, std::bind(&BattleWindow::bConsoleDownf, this)); + addShortcut(EShortcut::BATTLE_TACTICS_NEXT, std::bind(&BattleWindow::bTacticNextStack, this)); + addShortcut(EShortcut::BATTLE_TACTICS_END, std::bind(&BattleWindow::bTacticPhaseEnd, this)); + addShortcut(EShortcut::BATTLE_SELECT_ACTION, std::bind(&BattleWindow::bSwitchActionf, this)); + + addShortcut(EShortcut::BATTLE_TOGGLE_QUEUE, [this](){ this->toggleQueueVisibility();}); + addShortcut(EShortcut::BATTLE_USE_CREATURE_SPELL, [this](){ this->owner.actionsController->enterCreatureCastingMode(); }); + addShortcut(EShortcut::GLOBAL_CANCEL, [this](){ this->owner.actionsController->endCastingSpell(); }); + build(config); console = widget("console"); @@ -190,19 +194,7 @@ void BattleWindow::keyPressed(EShortcut key) owner.openingEnd(); return; } - - if(key == EShortcut::BATTLE_TOGGLE_QUEUE) - { - toggleQueueVisibility(); - } - else if(key == EShortcut::BATTLE_USE_CREATURE_SPELL) - { - owner.actionsController->enterCreatureCastingMode(); - } - else if(key == EShortcut::GLOBAL_CANCEL) - { - owner.actionsController->endCastingSpell(); - } + InterfaceObjectConfigurable::keyPressed(key); } void BattleWindow::clickRight(tribool down, bool previousState) @@ -538,40 +530,18 @@ void BattleWindow::blockUI(bool on) bool canWait = owner.stacksController->getActiveStack() ? !owner.stacksController->getActiveStack()->waitedThisTurn : false; - if(auto w = widget("options")) - w->block(on); - if(auto w = widget("flee")) - w->block(on || !owner.curInt->cb->battleCanFlee()); - if(auto w = widget("surrender")) - w->block(on || owner.curInt->cb->battleGetSurrenderCost() < 0); - if(auto w = widget("cast")) - w->block(on || owner.tacticsMode || !canCastSpells); - if(auto w = widget("wait")) - w->block(on || owner.tacticsMode || !canWait); - if(auto w = widget("defence")) - w->block(on || owner.tacticsMode); - if(auto w = widget("alternativeAction")) - w->block(on || owner.tacticsMode); - if(auto w = widget("autofight")) - w->block(owner.actionsController->spellcastingModeActive()); - - auto btactEnd = widget("tacticEnd"); - auto btactNext = widget("tacticNext"); - if(owner.tacticsMode && btactEnd && btactNext) - { - btactNext->block(on); - btactEnd->block(on); - } - else - { - auto bConsoleUp = widget("consoleUp"); - auto bConsoleDown = widget("consoleDown"); - if(bConsoleUp && bConsoleDown) - { - bConsoleUp->block(on); - bConsoleDown->block(on); - } - } + setShortcutBlocked(EShortcut::GLOBAL_OPTIONS, on); + setShortcutBlocked(EShortcut::BATTLE_RETREAT, on || !owner.curInt->cb->battleCanFlee()); + setShortcutBlocked(EShortcut::BATTLE_SURRENDER, on || owner.curInt->cb->battleGetSurrenderCost() < 0); + setShortcutBlocked(EShortcut::BATTLE_CAST_SPELL, on || owner.tacticsMode || !canCastSpells); + setShortcutBlocked(EShortcut::BATTLE_WAIT, on || owner.tacticsMode || !canWait); + setShortcutBlocked(EShortcut::BATTLE_DEFEND, on || owner.tacticsMode); + setShortcutBlocked(EShortcut::BATTLE_SELECT_ACTION, on || owner.tacticsMode); + setShortcutBlocked(EShortcut::BATTLE_AUTOCOMBAT, owner.actionsController->spellcastingModeActive()); + setShortcutBlocked(EShortcut::BATTLE_TACTICS_END, on && owner.tacticsMode); + setShortcutBlocked(EShortcut::BATTLE_TACTICS_NEXT, on && owner.tacticsMode); + setShortcutBlocked(EShortcut::BATTLE_CONSOLE_DOWN, on && !owner.tacticsMode); + setShortcutBlocked(EShortcut::BATTLE_CONSOLE_UP, on && !owner.tacticsMode); } std::optional BattleWindow::getQueueHoveredUnitId() diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 6423499d3..cc4d96422 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -201,19 +201,19 @@ std::pair InterfaceObjectConfigurable::readHintText(co return result; } -EShortcut InterfaceObjectConfigurable::readKeycode(const JsonNode & config) const +EShortcut InterfaceObjectConfigurable::readHotkey(const JsonNode & config) const { - logGlobal->debug("Reading keycode"); + logGlobal->debug("Reading hotkey"); if(config.getType() != JsonNode::JsonType::DATA_STRING) { - logGlobal->error("Invalid keycode format in interface configuration! Expected string!", config.String()); + logGlobal->error("Invalid hotket format in interface configuration! Expected string!", config.String()); return EShortcut::NONE; } EShortcut result = GH.shortcutsHandler().findShortcut(config.String()); if (result == EShortcut::NONE) - logGlobal->error("Invalid keycode '%s' in interface configuration!", config.String()); + logGlobal->error("Invalid hotkey '%s' in interface configuration!", config.String()); return result;; } @@ -320,11 +320,27 @@ std::shared_ptr InterfaceObjectConfigurable::buildButton(const JsonNode button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer()); } if(!config["callback"].isNull()) - button->addCallback(std::bind(callbacks.at(config["callback"].String()), 0)); + { + std::string callbackName = config["callback"].String(); + + if (callbacks.count(callbackName) > 0) + button->addCallback(std::bind(callbacks.at(callbackName), 0)); + else + logGlobal->error("Invalid callback '%s' in widget", callbackName ); + } if(!config["hotkey"].isNull()) { if(config["hotkey"].getType() == JsonNode::JsonType::DATA_STRING) - button->assignedKey = readKeycode(config["hotkey"]); + { + button->assignedKey = readHotkey(config["hotkey"]); + + auto target = shortcuts.find(button->assignedKey); + if (target != shortcuts.end()) + { + button->addCallback(target->second.callback); + target->second.assignedToButton = true; + } + } } return button; } @@ -430,3 +446,41 @@ std::shared_ptr InterfaceObjectConfigurable::buildWidget(JsonNode co logGlobal->error("Builder with type %s is not registered", type); return nullptr; } + +void InterfaceObjectConfigurable::setShortcutBlocked(EShortcut shortcut, bool isBlocked) +{ + auto target = shortcuts.find(key); + if (target == shortcuts.end()) + return; + + target->second.blocked = isBlocked; + + for (auto & entry : widgets) + { + auto button = std::dynamic_pointer_cast(entry.second); + + if (button && button->assignedKey == shortcut) + button->block(isBlocked); + } +} + +void InterfaceObjectConfigurable::addShortcut(EShortcut shortcut, std::function callback) +{ + assert(shortcuts.count(shortcut) == 0); + shortcuts[shortcut].callback = callback; +} + +void InterfaceObjectConfigurable::keyPressed(EShortcut key) +{ + auto target = shortcuts.find(key); + if (target == shortcuts.end()) + return; + + if (target->second.assignedToButton) + return; // will be handled by button instance + + if (target->second.blocked) + return; + + target->second.callback(); +} diff --git a/client/gui/InterfaceObjectConfigurable.h b/client/gui/InterfaceObjectConfigurable.h index deba0481a..56ddcd19b 100644 --- a/client/gui/InterfaceObjectConfigurable.h +++ b/client/gui/InterfaceObjectConfigurable.h @@ -35,7 +35,14 @@ public: InterfaceObjectConfigurable(const JsonNode & config, int used=0, Point offset=Point()); protected: - + /// Set blocked status for all buttons assotiated with provided shortcut + void setShortcutBlocked(EShortcut shortcut, bool isBlocked); + + /// Registers provided callback to be called whenever specified shortcut is triggered + void addShortcut(EShortcut shortcut, std::function callback); + + void keyPressed(EShortcut key) override; + using BuilderFunction = std::function(const JsonNode &)>; void registerBuilder(const std::string &, BuilderFunction); @@ -64,7 +71,7 @@ protected: EFonts readFont(const JsonNode &) const; std::string readText(const JsonNode &) const; std::pair readHintText(const JsonNode &) const; - EShortcut readKeycode(const JsonNode &) const; + EShortcut readHotkey(const JsonNode &) const; //basic widgets std::shared_ptr buildPicture(const JsonNode &) const; @@ -82,9 +89,16 @@ protected: std::shared_ptr buildWidget(JsonNode config) const; private: + struct ShortcutState + { + std::function callback; + mutable bool assignedToButton = false; + bool blocked = false; + }; int unnamedObjectId = 0; std::map builders; std::map> widgets; std::map> callbacks; + std::map shortcuts; }; diff --git a/client/gui/Shortcut.h b/client/gui/Shortcut.h index 7579ef1dc..956c6ce88 100644 --- a/client/gui/Shortcut.h +++ b/client/gui/Shortcut.h @@ -124,6 +124,7 @@ enum class EShortcut BATTLE_CONSOLE_DOWN, BATTLE_TACTICS_NEXT, BATTLE_TACTICS_END, + BATTLE_SELECT_ACTION, // Alternative actions toggle // Town screen TOWN_OPEN_TAVERN, diff --git a/client/gui/ShortcutHandler.cpp b/client/gui/ShortcutHandler.cpp index 263f1e463..dfd30cb84 100644 --- a/client/gui/ShortcutHandler.cpp +++ b/client/gui/ShortcutHandler.cpp @@ -126,6 +126,7 @@ std::vector ShortcutHandler::translateKeycode(SDL_Keycode key) const {SDLK_SPACE, EShortcut::BATTLE_TACTICS_NEXT }, {SDLK_RETURN, EShortcut::BATTLE_TACTICS_END }, {SDLK_KP_ENTER, EShortcut::BATTLE_TACTICS_END }, + {SDLK_s, EShortcut::BATTLE_SELECT_ACTION }, {SDLK_t, EShortcut::TOWN_OPEN_TAVERN }, {SDLK_SPACE, EShortcut::TOWN_SWAP_ARMIES }, {SDLK_END, EShortcut::RECRUITMENT_MAX }, @@ -255,6 +256,7 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const {"battleConsoleDown", EShortcut::BATTLE_CONSOLE_DOWN }, {"battleTacticsNext", EShortcut::BATTLE_TACTICS_NEXT }, {"battleTacticsEnd", EShortcut::BATTLE_TACTICS_END }, + {"battleSelectAction", EShortcut::BATTLE_SELECT_ACTION }, {"townOpenTavern", EShortcut::TOWN_OPEN_TAVERN }, {"townSwapArmies", EShortcut::TOWN_SWAP_ARMIES }, {"recruitmentMax", EShortcut::RECRUITMENT_MAX }, diff --git a/config/widgets/battleWindow.json b/config/widgets/battleWindow.json index a23ecb7b1..7548caba1 100644 --- a/config/widgets/battleWindow.json +++ b/config/widgets/battleWindow.json @@ -21,7 +21,6 @@ "position": {"x": 4, "y": 560}, "image": "icm003", "help": "core.help.381", - "callback": "options", "hotkey": "globalOptions" }, @@ -31,7 +30,6 @@ "position": {"x": 55, "y": 560}, "image": "icm001", "help": "core.help.379", - "callback": "surrender", "hotkey": "battleSurrender" }, @@ -41,7 +39,6 @@ "position": {"x": 106, "y": 560}, "image": "icm002", "help": "core.help.380", - "callback": "flee", "hotkey": "battleRetreat" }, @@ -51,7 +48,6 @@ "position": {"x": 157, "y": 560}, "image": "icm004", "help": "core.help.382", - "callback": "autofight", "hotkey": "battleAutocombat" }, @@ -61,7 +57,6 @@ "position": {"x": 646, "y": 560}, "image": "icm005", "help": "core.help.385", - "callback": "spellbook", "hotkey": "battleCastSpell" }, @@ -71,7 +66,6 @@ "position": {"x": 697, "y": 560}, "image": "icm006", "help": "core.help.386", - "callback": "wait", "hotkey": "battleWait" }, @@ -81,7 +75,6 @@ "position": {"x": 748, "y": 560}, "image": "icm007", "help": "core.help.387", - "callback": "defence", "hotkey": "battleDefend" }, @@ -90,7 +83,6 @@ "name": "consoleUp", "position": {"x": 625, "y": 560}, "image": "ComSlide", - "callback": "consoleUp", "imageOrder": [0, 1, 0, 0], "hotkey": "battleConsoleUp" }, @@ -100,7 +92,6 @@ "name": "consoleDown", "position": {"x": 625, "y": 579}, "image": "ComSlide", - "callback": "consoleDown", "imageOrder": [2, 3, 2, 2], "hotkey": "battleConsoleDown" }, @@ -117,7 +108,6 @@ "name": "tacticNext", "position": {"x": 213, "y": 560}, "image": "icm011", - "callback": "tacticNext", "hotkey": "battleTacticsNext" }, @@ -126,7 +116,6 @@ "name": "tacticEnd", "position": {"x": 419, "y": 560}, "image": "icm012", - "callback": "tacticEnd", "hotkey": "battleTacticsEnd" } ] From 0662ad49dd90eac8ec1185f2dfd9927681331d56 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 30 Apr 2023 01:47:17 +0300 Subject: [PATCH 25/43] Fix build --- client/gui/InterfaceObjectConfigurable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index cc4d96422..c2718bcdc 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -449,7 +449,7 @@ std::shared_ptr InterfaceObjectConfigurable::buildWidget(JsonNode co void InterfaceObjectConfigurable::setShortcutBlocked(EShortcut shortcut, bool isBlocked) { - auto target = shortcuts.find(key); + auto target = shortcuts.find(shortcut); if (target == shortcuts.end()) return; From e9e5355af01bd169f29917b2a27c83bc2235985b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 1 May 2023 01:25:48 +0300 Subject: [PATCH 26/43] Added commented-out wog campaigns set --- client/widgets/CArtifactsOfHeroBase.cpp | 2 +- config/campaignSets.json | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/client/widgets/CArtifactsOfHeroBase.cpp b/client/widgets/CArtifactsOfHeroBase.cpp index d083cf277..c11374166 100644 --- a/client/widgets/CArtifactsOfHeroBase.cpp +++ b/client/widgets/CArtifactsOfHeroBase.cpp @@ -12,7 +12,7 @@ #include "../gui/CGuiHandler.h" #include "../gui/CursorHandler.h" -#include "../gui/EShortcut.h" +#include "../gui/Shortcut.h" #include "Buttons.h" diff --git a/config/campaignSets.json b/config/campaignSets.json index 46b81217c..1a0cd3194 100644 --- a/config/campaignSets.json +++ b/config/campaignSets.json @@ -48,4 +48,17 @@ { "x":404, "y":414, "file":"DATA/SECRET.H3C", "image":"CAMPSP1", "video":"SPECTRE", "open": true } ] } +// "wog" : +// { +// /// wog campaigns, currently has no assigned button in campaign screen and thus unused +// "images" : [ {"x": 0, "y": 0, "name":"CAMPZALL"} ], +// "exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27}, +// "items": +// [ +// { "x":90, "y":72, "file":"DATA/ZC1.H3C", "image":"CAMPZ01", "open": true}, +// { "x":539, "y":72, "file":"DATA/ZC2.H3C", "image":"CAMPZ02", "open": true}, +// { "x":43, "y":245, "file":"DATA/ZC3.H3C", "image":"CAMPZ03", "open": true}, +// { "x":311, "y":242, "file":"DATA/ZC4.H3C", "image":"CAMPZ04", "open": true} +// ] +// } } From 59bc9326e93640406d0964e99f39229df5587117 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Thu, 27 Apr 2023 00:18:43 +0400 Subject: [PATCH 27/43] 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 28/43] 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 29/43] 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 30/43] 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 31/43] 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 32/43] 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 33/43] 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 34/43] 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 35/43] 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 36/43] 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 37/43] 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 38/43] 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 39/43] 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 40/43] 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 41/43] 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 42/43] 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 43/43] 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" : {