diff --git a/Mods/vcmi/Content/Sprites/radialMenu/upgradeCreatures.png b/Mods/vcmi/Content/Sprites/radialMenu/upgradeCreatures.png new file mode 100644 index 000000000..37538e1f7 Binary files /dev/null and b/Mods/vcmi/Content/Sprites/radialMenu/upgradeCreatures.png differ diff --git a/Mods/vcmi/Content/config/english.json b/Mods/vcmi/Content/config/english.json index 8384c2009..0b4a22d92 100644 --- a/Mods/vcmi/Content/config/english.json +++ b/Mods/vcmi/Content/config/english.json @@ -68,6 +68,7 @@ "vcmi.radialWheel.heroGetArtifacts" : "Get artifacts from other hero", "vcmi.radialWheel.heroSwapArtifacts" : "Swap artifacts with other hero", "vcmi.radialWheel.heroDismiss" : "Dismiss hero", + "vcmi.radialWheel.upgradeCreatures" : "Upgrade all creatures", "vcmi.radialWheel.moveTop" : "Move to top", "vcmi.radialWheel.moveUp" : "Move up", @@ -415,6 +416,10 @@ "vcmi.townStructure.bank.borrow" : "You enter the bank. A banker sees you and says: \"We have made a special offer for you. You can take a loan of 2500 gold from us for 5 days. You will have to repay 500 gold every day.\"", "vcmi.townStructure.bank.payBack" : "You enter the bank. A banker sees you and says: \"You have already got your loan. Pay it back before taking a new one.\"", + "vcmi.townWindow.upgradeAll.upgradable" : "Do you want to upgrade following creatures?", + "vcmi.townWindow.upgradeAll.notAllUpgradable" : "Not enough resources to upgrade all creatures. Do you want to upgrade following creatures?", + "vcmi.townWindow.upgradeAll.notUpgradable" : "Not enough resources to upgrade any creature.", + "vcmi.logicalExpressions.anyOf" : "Any of the following:", "vcmi.logicalExpressions.allOf" : "All of the following:", "vcmi.logicalExpressions.noneOf" : "None of the following:", diff --git a/Mods/vcmi/Content/config/german.json b/Mods/vcmi/Content/config/german.json index aa34553b1..ff9ba71f0 100644 --- a/Mods/vcmi/Content/config/german.json +++ b/Mods/vcmi/Content/config/german.json @@ -68,6 +68,7 @@ "vcmi.radialWheel.heroGetArtifacts" : "Artefakte von anderen Helden erhalten", "vcmi.radialWheel.heroSwapArtifacts" : "Tausche Artefakte mit anderen Helden", "vcmi.radialWheel.heroDismiss" : "Held entlassen", + "vcmi.radialWheel.upgradeCreatures" : "Alle Kreaturen aufrüsten", "vcmi.radialWheel.moveTop" : "Ganz nach oben bewegen", "vcmi.radialWheel.moveUp" : "Nach oben bewegen", @@ -415,6 +416,10 @@ "vcmi.townStructure.bank.borrow" : "Ihr betretet die Bank. Ein Bankangestellter sieht Euch und sagt: \"Wir haben ein spezielles Angebot für Euch gemacht. Ihr könnt bei uns einen Kredit von 2500 Gold für 5 Tage aufnehmen. Ihr werdet jeden Tag 500 Gold zurückzahlen müssen.\"", "vcmi.townStructure.bank.payBack" : "Ihr betretet die Bank. Ein Bankangestellter sieht Euch und sagt: \"Ihr habt Euren Kredit bereits erhalten. Zahlt Ihn ihn zurück, bevor Ihr einen neuen aufnehmt.\"", + "vcmi.townWindow.upgradeAll.upgradable" : "Folgende Kreaturen aufrüsten?", + "vcmi.townWindow.upgradeAll.notAllUpgradable" : "Nicht genügend Ressourcen um alle Kreaturen aufzurüsten. Folgende Kreaturen aufrüsten?", + "vcmi.townWindow.upgradeAll.notUpgradable" : "Nicht genügend Ressourcen um mindestens eine Kreatur aufzurüsten.", + "vcmi.logicalExpressions.anyOf" : "Eines der folgenden:", "vcmi.logicalExpressions.allOf" : "Alles der folgenden:", "vcmi.logicalExpressions.noneOf" : "Keines der folgenden:", diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 4806e2fb4..4022c8ae3 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -51,6 +51,7 @@ #include "../../lib/IGameSettings.h" #include "../../lib/spells/CSpellHandler.h" #include "../../lib/GameConstants.h" +#include "../../lib/gameState/UpgradeInfo.h" #include "../../lib/StartInfo.h" #include "../../lib/campaign/CampaignState.h" #include "../../lib/entities/building/CBuilding.h" @@ -337,17 +338,92 @@ CHeroGSlot::CHeroGSlot(int x, int y, int updown, const CGHeroInstance * h, HeroS CHeroGSlot::~CHeroGSlot() = default; +auto CHeroGSlot::getUpgradableSlots(const CArmedInstance *obj) +{ + struct result { bool isCreatureUpgradePossible; bool canAffordAny; bool canAffordAll; TResources totalCosts; std::vector> upgradeInfos; }; + + auto slots = std::map(obj->Slots().begin(), obj->Slots().end()); + std::vector> upgradeInfos; + for(auto & slot : slots) + { + auto upgradeInfo = std::make_pair(slot.first, UpgradeInfo(slot.second->getCreatureID())); + LOCPLINT->cb->fillUpgradeInfo(slot.second->armyObj, slot.first, upgradeInfo.second); + bool canUpgrade = obj->tempOwner == LOCPLINT->playerID && upgradeInfo.second.canUpgrade(); + if(canUpgrade) + upgradeInfos.push_back(upgradeInfo); + } + + std::sort(upgradeInfos.begin(), upgradeInfos.end(), [&](const std::pair & lhs, const std::pair & rhs) { + return lhs.second.oldID.toCreature()->getLevel() > rhs.second.oldID.toCreature()->getLevel(); + }); + bool creaturesToUpgrade = static_cast(upgradeInfos.size()); + + TResources costs = TResources(); + std::vector slotInfosToDelete; + for(auto & upgradeInfo : upgradeInfos) + { + TResources upgradeCosts = upgradeInfo.second.getUpgradeCosts() * slots[upgradeInfo.first]->getCount(); + if(LOCPLINT->cb->getResourceAmount().canAfford(costs + upgradeCosts)) + costs += upgradeCosts; + else + slotInfosToDelete.push_back(upgradeInfo.first); + } + upgradeInfos.erase(std::remove_if(upgradeInfos.begin(), upgradeInfos.end(), [&slotInfosToDelete](const auto& item) { + return std::count(slotInfosToDelete.begin(), slotInfosToDelete.end(), item.first); + }), upgradeInfos.end()); + + return result { creaturesToUpgrade, static_cast(upgradeInfos.size()), !slotInfosToDelete.size(), costs, upgradeInfos }; +} + void CHeroGSlot::gesture(bool on, const Point & initialPosition, const Point & finalPosition) { if(!on) return; - if(!hero) + const CArmedInstance *obj = hero; + if(upg == 0 && !obj) + obj = owner->town->getUpperArmy(); + if(!obj) return; + auto upgradableSlots = getUpgradableSlots(obj); + auto upgradeAll = [upgradableSlots, obj](){ + if(!upgradableSlots.canAffordAny) + { + LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.townWindow.upgradeAll.notUpgradable")); + return; + } + + std::vector> resComps; + for(TResources::nziterator i(upgradableSlots.totalCosts); i.valid(); i++) + resComps.push_back(std::make_shared(ComponentType::RESOURCE, i->resType, i->resVal)); + resComps.back()->newLine = true; + for(auto & upgradeInfo : upgradableSlots.upgradeInfos) + resComps.push_back(std::make_shared(ComponentType::CREATURE, upgradeInfo.second.getUpgrade(), std::nullopt)); + + std::string textID = upgradableSlots.canAffordAll ? "vcmi.townWindow.upgradeAll.upgradable" : "vcmi.townWindow.upgradeAll.notAllUpgradable"; + + LOCPLINT->showYesNoDialog(CGI->generaltexth->translate(textID), [upgradableSlots, obj](){ + for(auto & upgradeInfo : upgradableSlots.upgradeInfos) + LOCPLINT->cb->upgradeCreature(obj, upgradeInfo.first, upgradeInfo.second.getUpgrade()); + }, nullptr, resComps); + }; + if (!settings["input"]["radialWheelGarrisonSwipe"].Bool()) return; + if(!hero) + { + if(upgradableSlots.isCreatureUpgradePossible) + { + std::vector menuElements = { + { RadialMenuConfig::ITEM_WW, true, "upgradeCreatures", "vcmi.radialWheel.upgradeCreatures", [upgradeAll](){ upgradeAll(); } }, + }; + GH.windows().createAndPushWindow(pos.center(), menuElements); + } + return; + } + std::shared_ptr other = upg ? owner->garrisonedHero : owner->visitingHero; bool twoHeroes = hero && other->hero; @@ -361,11 +437,16 @@ void CHeroGSlot::gesture(bool on, const Point & initialPosition, const Point & f { RadialMenuConfig::ITEM_EE, twoHeroes, "tradeHeroes", "vcmi.radialWheel.heroExchange", [heroId, heroOtherId](){LOCPLINT->showHeroExchange(heroId, heroOtherId);} }, { RadialMenuConfig::ITEM_SW, twoHeroes, "moveArtifacts", "vcmi.radialWheel.heroGetArtifacts", [heroId, heroOtherId](){CExchangeController(heroId, heroOtherId).moveArtifacts(false, true, true);} }, { RadialMenuConfig::ITEM_SE, twoHeroes, "swapArtifacts", "vcmi.radialWheel.heroSwapArtifacts", [heroId, heroOtherId](){CExchangeController(heroId, heroOtherId).swapArtifacts(true, true);} }, - { RadialMenuConfig::ITEM_WW, true, "dismissHero", "vcmi.radialWheel.heroDismiss", [this]() + { RadialMenuConfig::ITEM_WW, true, !upgradableSlots.isCreatureUpgradePossible ? "dismissHero" : "upgradeCreatures", !upgradableSlots.isCreatureUpgradePossible ? "vcmi.radialWheel.heroDismiss" : "vcmi.radialWheel.upgradeCreatures", [this, upgradableSlots, upgradeAll]() { - CFunctionList ony = [=](){ }; - ony += [=](){ LOCPLINT->cb->dismissHero(hero); }; - LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[22], ony, nullptr); + if(upgradableSlots.isCreatureUpgradePossible) + upgradeAll(); + else + { + CFunctionList ony = [=](){ }; + ony += [=](){ LOCPLINT->cb->dismissHero(hero); }; + LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[22], ony, nullptr); + } } }, }; diff --git a/client/windows/CCastleInterface.h b/client/windows/CCastleInterface.h index 08672285a..b985db6c1 100644 --- a/client/windows/CCastleInterface.h +++ b/client/windows/CCastleInterface.h @@ -103,6 +103,8 @@ class CHeroGSlot : public CIntObject const CGHeroInstance * hero; int upg; //0 - up garrison, 1 - down garrison + auto getUpgradableSlots(const CArmedInstance *obj); + public: CHeroGSlot(int x, int y, int updown, const CGHeroInstance *h, HeroSlots * Owner); ~CHeroGSlot();