diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index caaedd72e..6257722a2 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -93,6 +93,7 @@ set(client_SRCS renderSDL/SDL_Extensions.cpp widgets/Buttons.cpp + widgets/CAltar.cpp widgets/CArtifactHolder.cpp widgets/CComponent.cpp widgets/CExchangeController.cpp @@ -115,6 +116,7 @@ set(client_SRCS widgets/CWindowWithArtifacts.cpp widgets/RadialMenu.cpp + windows/CAltarWindow.cpp windows/CCastleInterface.cpp windows/CCreatureWindow.cpp windows/CHeroOverview.cpp @@ -260,6 +262,7 @@ set(client_HEADERS renderSDL/SDL_PixelAccess.h widgets/Buttons.h + widgets/CAltar.h widgets/CArtifactHolder.h widgets/CComponent.h widgets/CExchangeController.h @@ -282,6 +285,7 @@ set(client_HEADERS widgets/CWindowWithArtifacts.h widgets/RadialMenu.h + windows/CAltarWindow.h windows/CCastleInterface.h windows/CCreatureWindow.h windows/CHeroOverview.h diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 409b0f30e..2ee8b1caf 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -48,6 +48,7 @@ #include "widgets/CComponent.h" #include "widgets/CGarrisonInt.h" +#include "windows/CAltarWindow.h" #include "windows/CCastleInterface.h" #include "windows/CCreatureWindow.h" #include "windows/CHeroWindow.h" @@ -421,7 +422,7 @@ void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, Prim if (which == PrimarySkill::EXPERIENCE) { for (auto ctw : GH.windows().findWindows()) - ctw->setExpToLevel(); + ctw->updateExpToLevel(); } else adventureInt->onHeroChanged(hero); @@ -558,10 +559,10 @@ void CPlayerInterface::garrisonsChanged(std::vector ob for (auto cgh : GH.windows().findWindows()) cgh->updateGarrisons(); - for (auto cmw : GH.windows().findWindows()) + for (auto cmw : GH.windows().findWindows()) { - if (vstd::contains(objs, cmw->hero)) - cmw->garrisonChanged(); + if(vstd::contains(objs, cmw->getHero())) + cmw->updateGarrison(); } GH.windows().totalRedraw(); diff --git a/client/widgets/CAltar.cpp b/client/widgets/CAltar.cpp new file mode 100644 index 000000000..60f27c7a4 --- /dev/null +++ b/client/widgets/CAltar.cpp @@ -0,0 +1,457 @@ +/* + * CAltarWindow.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 "CAltar.h" + +#include "../widgets/CAltar.h" +#include "../gui/CGuiHandler.h" +#include "../gui/CursorHandler.h" +#include "../widgets/Buttons.h" +#include "../widgets/Slider.h" +#include "../widgets/TextControls.h" + +#include "../CGameInfo.h" +#include "../CPlayerInterface.h" + +#include "../../CCallback.h" + +#include "../../lib/networkPacks/ArtifactLocation.h" +#include "../../lib/CGeneralTextHandler.h" +#include "../../lib/mapObjects/CGHeroInstance.h" +#include "../../lib/mapObjects/CGMarket.h" + +CAltar::CAltar(const IMarket * market, const CGHeroInstance * hero) + : CTradeBase(market, hero) +{ + OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + + // Experience needed to reach next level + texts.emplace_back(std::make_shared(CGI->generaltexth->allTexts[475], Rect(15, 415, 125, 50), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW)); + // Total experience on the Altar + texts.emplace_back(std::make_shared(CGI->generaltexth->allTexts[476], Rect(15, 495, 125, 40), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW)); + deal = std::make_shared(Point(269, 520), AnimationPath::builtin("ALTSACR.DEF"), CGI->generaltexth->zelp[585], std::bind(&CAltar::makeDeal, this)); + expToLevel = std::make_shared(75, 477, FONT_SMALL, ETextAlignment::CENTER); + expForHero = std::make_shared(75, 545, FONT_SMALL, ETextAlignment::CENTER); +} + +void CAltar::deselect() +{ + hLeft = hRight = nullptr; + deal->block(true); +} + +CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance * hero) + : CAltar(market, hero) +{ + OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE); + + labels.emplace_back(std::make_shared(450, 34, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[477])); + labels.emplace_back(std::make_shared(302, 423, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[478])); + selectedCost = std::make_shared(302, 500, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); + selectedArt = std::make_shared(Point(280, 442)); + + sacrificeAllButton = std::make_shared(Point(393, 520), AnimationPath::builtin("ALTFILL.DEF"), + CGI->generaltexth->zelp[571], std::bind(&CAltar::sacrificeAll, this)); + sacrificeAllButton->block(hero->artifactsInBackpack.empty() && hero->artifactsWorn.empty()); + + sacrificeBackpackButton = std::make_shared(Point(147, 520), AnimationPath::builtin("ALTEMBK.DEF"), + CGI->generaltexth->zelp[570], std::bind(&CAltarArtifacts::sacrificeBackpack, this)); + sacrificeBackpackButton->block(hero->artifactsInBackpack.empty()); + + arts = std::make_shared(Point(-365, -10)); + arts->setHero(hero); + + int slotNum = 0; + for(auto & altarSlotPos : posSlotsAltar) + { + auto altarSlot = std::make_shared(altarSlotPos, EType::ARTIFACT_PLACEHOLDER, -1, false, slotNum++); + altarSlot->clickPressedCallback = [this](std::shared_ptr altarSlot) -> void + { + const auto pickedArtInst = arts->getPickedArtifact(); + if(pickedArtInst) + { + arts->pickedArtMoveToAltar(ArtifactPosition::TRANSITION_POS); + moveArtToAltar(altarSlot, pickedArtInst); + } + else if(const CArtifactInstance * art = altarSlot->getArtInstance()) + { + const auto hero = arts->getHero(); + const auto slot = hero->getSlotByInstance(art); + assert(slot != ArtifactPosition::PRE_FIRST); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero->id, slot), + ArtifactLocation(hero->id, ArtifactPosition::TRANSITION_POS)); + arts->pickedArtFromSlot = slot; + arts->artifactsOnAltar.erase(art); + altarSlot->setID(-1); + altarSlot->subtitle.clear(); + deal->block(!arts->artifactsOnAltar.size()); + } + calcExpAltarForHero(); + }; + altarSlot->subtitle = ""; + items.front().emplace_back(altarSlot); + } + + calcExpAltarForHero(); + deselect(); +}; + +TExpType CAltarArtifacts::calcExpAltarForHero() +{ + auto artifactsOfHero = std::dynamic_pointer_cast(arts); + TExpType expOnAltar(0); + for(const auto art : artifactsOfHero->artifactsOnAltar) + { + int dmp, expOfArt; + market->getOffer(art->artType->getId(), 0, dmp, expOfArt, EMarketMode::ARTIFACT_EXP); + expOnAltar += expOfArt; + } + auto resultExp = hero->calculateXp(expOnAltar); + expForHero->setText(std::to_string(resultExp)); + return resultExp; +} + +void CAltarArtifacts::makeDeal() +{ + std::vector positions; + for(const auto art : arts->artifactsOnAltar) + { + positions.push_back(hero->getSlotByInstance(art)); + } + std::sort(positions.begin(), positions.end(), std::greater<>()); + + LOCPLINT->cb->trade(market, EMarketMode::ARTIFACT_EXP, positions, {}, {}, hero); + arts->artifactsOnAltar.clear(); + + for(auto item : items[0]) + { + item->setID(-1); + item->subtitle = ""; + } + deal->block(true); + calcExpAltarForHero(); +} + +void CAltarArtifacts::sacrificeAll() +{ + std::vector> artsForMove; + for(const auto & slotInfo : arts->getHero()->artifactsWorn) + { + if(!slotInfo.second.locked && slotInfo.second.artifact->artType->isTradable()) + artsForMove.push_back(slotInfo.second.artifact); + } + for(auto artInst : artsForMove) + moveArtToAltar(nullptr, artInst); + arts->updateWornSlots(); + sacrificeBackpack(); +} + +void CAltarArtifacts::sacrificeBackpack() +{ + while(!arts->visibleArtSet.artifactsInBackpack.empty()) + { + if(!putArtOnAltar(nullptr, arts->visibleArtSet.artifactsInBackpack[0].artifact)) + break; + }; + calcExpAltarForHero(); +} + +void CAltarArtifacts::setSelectedArtifact(const CArtifactInstance * art) +{ + if(art) + { + selectedArt->setArtifact(art); + int dmp, exp; + market->getOffer(art->getTypeId(), 0, dmp, exp, EMarketMode::ARTIFACT_EXP); + selectedCost->setText(std::to_string(hero->calculateXp(exp))); + } + else + { + selectedArt->setArtifact(nullptr); + selectedCost->setText(""); + } +} + +void CAltarArtifacts::moveArtToAltar(std::shared_ptr altarSlot, const CArtifactInstance * art) +{ + if(putArtOnAltar(altarSlot, art)) + { + CCS->curh->dragAndDropCursor(nullptr); + arts->unmarkSlots(); + } +} + +std::shared_ptr CAltarArtifacts::getAOHset() const +{ + return arts; +} + +bool CAltarArtifacts::putArtOnAltar(std::shared_ptr altarSlot, const CArtifactInstance * art) +{ + if(!art->artType->isTradable()) + { + logGlobal->warn("Cannot put special artifact on altar!"); + return false; + } + + if(!altarSlot || altarSlot->id != -1) + { + int slotIndex = -1; + while(items[0][++slotIndex]->id >= 0 && slotIndex + 1 < items[0].size()); + slotIndex = items[0][slotIndex]->id == -1 ? slotIndex : -1; + if(slotIndex < 0) + { + logGlobal->warn("No free slots on altar!"); + return false; + } + altarSlot = items[0][slotIndex]; + } + + int dmp, exp; + market->getOffer(art->artType->getId(), 0, dmp, exp, EMarketMode::ARTIFACT_EXP); + exp = static_cast(hero->calculateXp(exp)); + + arts->artifactsOnAltar.insert(art); + altarSlot->setArtInstance(art); + altarSlot->subtitle = std::to_string(exp); + + deal->block(false); + return true; +}; + +CAltarCreatures::CAltarCreatures(const IMarket * market, const CGHeroInstance * hero) + : CAltar(market, hero) +{ + OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE); + + labels.emplace_back(std::make_shared(155, 30, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, + boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->getNameTranslated()))); + labels.emplace_back(std::make_shared(450, 30, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[479])); + texts.emplace_back(std::make_unique(CGI->generaltexth->allTexts[480], Rect(320, 56, 256, 40), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW)); + lSubtitle = std::make_shared(180, 503, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); + rSubtitle = std::make_shared(426, 503, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); + + unitsSlider = std::make_shared(Point(231, 481), 137, [this](int newVal) -> void + { + if(hLeft) + unitsOnAltar[hLeft->serial] = newVal; + if(hRight) + updateAltarSlot(hRight); + deal->block(calcExpAltarForHero() == 0); + updateControls(); + updateSubtitlesForSelected(); + }, 0, 0, 0, Orientation::HORIZONTAL); + maxUnits = std::make_shared(Point(147, 520), AnimationPath::builtin("IRCBTNS.DEF"), CGI->generaltexth->zelp[578], std::bind(&CSlider::scrollToMax, unitsSlider)); + + unitsOnAltar.resize(GameConstants::ARMY_SIZE, 0); + expPerUnit.resize(GameConstants::ARMY_SIZE, 0); + sacrificeAllButton = std::make_shared( + Point(393, 520), AnimationPath::builtin("ALTARMY.DEF"), CGI->generaltexth->zelp[579], std::bind(&CAltar::sacrificeAll, this)); + + auto clickPressed = [this](std::shared_ptr altarSlot, std::vector> & oppositeSlots, + std::shared_ptr & hCurSide, std::shared_ptr & hOppSide) -> void + { + std::shared_ptr oppositeSlot; + for(const auto & slot : oppositeSlots) + if(slot->serial == altarSlot->serial) + { + oppositeSlot = slot; + break; + } + + if(hCurSide != altarSlot && oppositeSlot) + { + hCurSide = altarSlot; + hOppSide = oppositeSlot; + updateControls(); + updateSubtitlesForSelected(); + redraw(); + } + }; + + for(int slotIdx = 0; slotIdx < GameConstants::ARMY_SIZE; slotIdx++) + { + CreatureID creatureId = CreatureID::NONE; + if(const auto & creature = hero->getCreature(SlotID(slotIdx))) + creatureId = creature->getId(); + else + continue; + + auto heroSlot = std::make_shared(posSlotsHero[slotIdx], EType::CREATURE, creatureId.num, true, slotIdx); + heroSlot->clickPressedCallback = [this, clickPressed](std::shared_ptr altarSlot) -> void + { + clickPressed(altarSlot, items[0], hLeft, hRight); + }; + heroSlot->subtitle = std::to_string(hero->getStackCount(SlotID(slotIdx))); + items[1].emplace_back(heroSlot); + } + assert(items[1].size() <= posSlotsAltar.size()); + for(const auto & heroSlot : items[1]) + { + auto altarSlot = std::make_shared(posSlotsAltar[heroSlot->serial], EType::CREATURE_PLACEHOLDER, heroSlot->id, false, heroSlot->serial); + altarSlot->pos.w = heroSlot->pos.w; altarSlot->pos.h = heroSlot->pos.h; + altarSlot->clickPressedCallback = [this, clickPressed](std::shared_ptr altarSlot) -> void + { + clickPressed(altarSlot, items[1], hRight, hLeft); + }; + items[0].emplace_back(altarSlot); + } + + readExpValues(); + calcExpAltarForHero(); + deselect(); +}; + +void CAltarCreatures::readExpValues() +{ + int dump; + for(auto heroSlot : items[1]) + { + if(heroSlot->id >= 0) + market->getOffer(heroSlot->id, 0, dump, expPerUnit[heroSlot->serial], EMarketMode::CREATURE_EXP); + } +} + +void CAltarCreatures::updateControls() +{ + int sliderAmount = 0; + if(hLeft) + { + std::optional lastSlot; + for(auto slot = SlotID(0); slot.num < GameConstants::ARMY_SIZE; slot++) + { + if(hero->getStackCount(slot) > unitsOnAltar[slot.num]) + { + if(lastSlot.has_value()) + { + lastSlot = std::nullopt; + break; + } + else + { + lastSlot = slot; + } + } + } + sliderAmount = hero->getStackCount(SlotID(hLeft->serial)); + if(lastSlot.has_value() && lastSlot.value() == SlotID(hLeft->serial)) + sliderAmount--; + } + unitsSlider->setAmount(sliderAmount); + unitsSlider->block(!unitsSlider->getAmount()); + if(hLeft) + unitsSlider->scrollTo(unitsOnAltar[hLeft->serial]); + maxUnits->block(unitsSlider->getAmount() == 0); +} + +void CAltarCreatures::updateSubtitlesForSelected() +{ + if(hLeft) + lSubtitle->setText(std::to_string(unitsSlider->getValue())); + else + lSubtitle->setText(""); + if(hRight) + rSubtitle->setText(hRight->subtitle); + else + rSubtitle->setText(""); +} + +void CAltarCreatures::updateGarrison() +{ + std::set> empty; + getEmptySlots(empty); + removeItems(empty); + readExpValues(); + for(auto & heroSlot : items[1]) + heroSlot->subtitle = std::to_string(hero->getStackCount(SlotID(heroSlot->serial))); +} + +void CAltarCreatures::deselect() +{ + CAltar::deselect(); + unitsSlider->block(true); + maxUnits->block(true); + updateSubtitlesForSelected(); +} + +TExpType CAltarCreatures::calcExpAltarForHero() +{ + TExpType expOnAltar(0); + auto oneUnitExp = expPerUnit.begin(); + for(const auto units : unitsOnAltar) + expOnAltar += *oneUnitExp++ * units; + auto resultExp = hero->calculateXp(expOnAltar); + expForHero->setText(std::to_string(resultExp)); + return resultExp; +} + +void CAltarCreatures::makeDeal() +{ + deselect(); + unitsSlider->scrollTo(0); + expForHero->setText(std::to_string(0)); + + std::vector ids; + std::vector toSacrifice; + + for(int i = 0; i < unitsOnAltar.size(); i++) + { + if(unitsOnAltar[i]) + { + ids.push_back(i); + toSacrifice.push_back(unitsOnAltar[i]); + } + } + + LOCPLINT->cb->trade(market, EMarketMode::CREATURE_EXP, ids, {}, toSacrifice, hero); + + for(int & units : unitsOnAltar) + units = 0; + + for(auto heroSlot : items[0]) + { + heroSlot->setType(CREATURE_PLACEHOLDER); + heroSlot->subtitle = ""; + } +} + +void CAltarCreatures::sacrificeAll() +{ + std::optional lastSlot; + for(auto heroSlot : items[1]) + { + auto stackCount = hero->getStackCount(SlotID(heroSlot->serial)); + if(stackCount > unitsOnAltar[heroSlot->serial]) + { + if(!lastSlot.has_value()) + lastSlot = SlotID(heroSlot->serial); + unitsOnAltar[heroSlot->serial] = stackCount; + } + } + assert(lastSlot.has_value()); + unitsOnAltar[lastSlot.value().num]--; + + if(hRight) + unitsSlider->scrollTo(unitsOnAltar[hRight->serial]); + for(auto altarSlot : items[0]) + updateAltarSlot(altarSlot); + updateSubtitlesForSelected(); + + deal->block(calcExpAltarForHero() == 0); +} + +void CAltarCreatures::updateAltarSlot(std::shared_ptr slot) +{ + auto units = unitsOnAltar[slot->serial]; + slot->setType(units > 0 ? CREATURE : CREATURE_PLACEHOLDER); + slot->subtitle = units > 0 ? + boost::str(boost::format(CGI->generaltexth->allTexts[122]) % std::to_string(hero->calculateXp(units * expPerUnit[slot->serial]))) : ""; +} \ No newline at end of file diff --git a/client/widgets/CAltar.h b/client/widgets/CAltar.h new file mode 100644 index 000000000..c6afe24b8 --- /dev/null +++ b/client/widgets/CAltar.h @@ -0,0 +1,98 @@ +/* + * CAltar.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 "../widgets/CArtifactsOfHeroAltar.h" +#include "../widgets/CTradeBase.h" + +class CSlider; + +class CAltar : public CTradeBase, public CIntObject +{ +public: + std::shared_ptr expToLevel; + std::shared_ptr expForHero; + std::shared_ptr sacrificeAllButton; + + CAltar(const IMarket * market, const CGHeroInstance * hero); + virtual ~CAltar() = default; + virtual void sacrificeAll() = 0; + virtual void deselect(); + virtual TExpType calcExpAltarForHero() = 0; +}; + +class CAltarArtifacts : public CAltar +{ +public: + CAltarArtifacts(const IMarket * market, const CGHeroInstance * hero); + TExpType calcExpAltarForHero() override; + void makeDeal() override; + void sacrificeAll() override; + void sacrificeBackpack(); + void setSelectedArtifact(const CArtifactInstance * art); + void moveArtToAltar(std::shared_ptr, const CArtifactInstance * art); + std::shared_ptr getAOHset() const; + +private: + std::shared_ptr selectedArt; + std::shared_ptr selectedCost; + std::shared_ptr sacrificeBackpackButton; + std::shared_ptr arts; + + const std::vector posSlotsAltar = + { + Point(317, 53), Point(371, 53), Point(425, 53), + Point(479, 53), Point(533, 53), Point(317, 123), + Point(371, 123), Point(425, 123), Point(479, 123), + Point(533, 123), Point(317, 193), Point(371, 193), + Point(425, 193), Point(479, 193), Point(533, 193), + Point(317, 263), Point(371, 263), Point(425, 263), + Point(479, 263), Point(533, 263), Point(398, 333), + Point(452, 333) + }; + + bool putArtOnAltar(std::shared_ptr altarSlot, const CArtifactInstance * art); +}; + +class CAltarCreatures : public CAltar +{ +public: + CAltarCreatures(const IMarket * market, const CGHeroInstance * hero); + void updateGarrison(); + void deselect() override; + TExpType calcExpAltarForHero() override; + void makeDeal() override; + void sacrificeAll() override; + void updateAltarSlot(std::shared_ptr slot); + +private: + std::shared_ptr maxUnits; + std::shared_ptr unitsSlider; + std::vector unitsOnAltar; + std::vector expPerUnit; + std::shared_ptr lSubtitle, rSubtitle; + + const std::vector posSlotsAltar = + { + Point(334, 110), Point(417, 110), Point(500, 110), + Point(334, 208), Point(417, 208), Point(500, 208), + Point(417, 306) + }; + const std::vector posSlotsHero = + { + Point(45, 110), Point(128, 110), Point(211, 110), + Point(45, 208), Point(128, 208), Point(211, 208), + Point(128, 306) + }; + + void readExpValues(); + void updateControls(); + void updateSubtitlesForSelected(); +}; diff --git a/client/widgets/CTradeBase.cpp b/client/widgets/CTradeBase.cpp index 2d9ea755f..e1e7aacde 100644 --- a/client/widgets/CTradeBase.cpp +++ b/client/widgets/CTradeBase.cpp @@ -31,6 +31,11 @@ CTradeBase::CTradeableItem::CTradeableItem(Point pos, EType Type, int ID, bool L downSelection = false; hlp = nullptr; setType(Type); + if(image) + { + this->pos.w = image->pos.w; + this->pos.h = image->pos.h; + } } void CTradeBase::CTradeableItem::setType(EType newType) @@ -124,14 +129,14 @@ void CTradeBase::CTradeableItem::showAll(Canvas & to) break; case CREATURE_PLACEHOLDER: case CREATURE: - posToSubCenter = Point(29, 76); + posToSubCenter = Point(29, 77); break; case PLAYER: posToSubCenter = Point(31, 76); break; case ARTIFACT_PLACEHOLDER: case ARTIFACT_INSTANCE: - posToSubCenter = Point(19, 55); + posToSubCenter = Point(19, 54); if (downSelection) posToSubCenter.y += 8; break; diff --git a/client/windows/CAltarWindow.cpp b/client/windows/CAltarWindow.cpp new file mode 100644 index 000000000..faabbb5ca --- /dev/null +++ b/client/windows/CAltarWindow.cpp @@ -0,0 +1,136 @@ +/* + * CAltarWindow.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 "CAltarWindow.h" + +#include "../gui/CGuiHandler.h" +#include "../render/Canvas.h" +#include "../gui/Shortcut.h" +#include "../widgets/Buttons.h" +#include "../widgets/TextControls.h" + +#include "../CGameInfo.h" + +#include "../../lib/CGeneralTextHandler.h" +#include "../../lib/CHeroHandler.h" +#include "../../lib/mapObjects/CGHeroInstance.h" + +CAltarWindow::CAltarWindow(const IMarket * market, const CGHeroInstance * hero, const std::function & onWindowClosed, EMarketMode mode) + : CWindowObject(PLAYER_COLORED, ImagePath::builtin(mode == EMarketMode::CREATURE_EXP ? "ALTARMON.bmp" : "ALTRART2.bmp")) + , hero(hero) + , windowClosedCallback(onWindowClosed) +{ + OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE); + + if(mode == EMarketMode::ARTIFACT_EXP) + createAltarArtifacts(market, hero); + else if (mode == EMarketMode::CREATURE_EXP) + createAltarCreatures(market, hero); + else + close(); + updateExpToLevel(); + statusBar = CGStatusBar::create(std::make_shared(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); +} + +void CAltarWindow::updateExpToLevel() +{ + altar->expToLevel->setText(std::to_string(CGI->heroh->reqExp(CGI->heroh->level(altar->hero->exp) + 1) - altar->hero->exp)); +} + +void CAltarWindow::updateGarrison() +{ + if(auto altarCreatures = std::static_pointer_cast(altar)) + altarCreatures->updateGarrison(); +} + +const CGHeroInstance * CAltarWindow::getHero() const +{ + return hero; +} + +void CAltarWindow::close() +{ + if(windowClosedCallback) + windowClosedCallback(); + + CWindowObject::close(); +} + +void CAltarWindow::createAltarArtifacts(const IMarket * market, const CGHeroInstance * hero) +{ + OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE); + + background = createBg(ImagePath::builtin("ALTRART2.bmp"), PLAYER_COLORED); + + auto altarArtifacts = std::make_shared(market, hero); + altar = altarArtifacts; + artSets.clear(); + addSetAndCallbacks(altarArtifacts->getAOHset()); + + changeModeButton = std::make_shared(Point(516, 421), AnimationPath::builtin("ALTSACC.DEF"), + CGI->generaltexth->zelp[572], std::bind(&CAltarWindow::createAltarCreatures, this, market, hero)); + if(altar->hero->getAlignment() == EAlignment::GOOD) + changeModeButton->block(true); + quitButton = std::make_shared(Point(516, 520), AnimationPath::builtin("IOK6432.DEF"), + CGI->generaltexth->zelp[568], std::bind(&CAltarWindow::close, this), EShortcut::GLOBAL_RETURN); + altar->setRedrawParent(true); + redraw(); +} + +void CAltarWindow::createAltarCreatures(const IMarket * market, const CGHeroInstance * hero) +{ + OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE); + + background = createBg(ImagePath::builtin("ALTARMON.bmp"), PLAYER_COLORED); + + altar = std::make_shared(market, hero); + + changeModeButton = std::make_shared(Point(516, 421), AnimationPath::builtin("ALTART.DEF"), + CGI->generaltexth->zelp[580], std::bind(&CAltarWindow::createAltarArtifacts, this, market, hero)); + if(altar->hero->getAlignment() == EAlignment::EVIL) + changeModeButton->block(true); + quitButton = std::make_shared(Point(516, 520), AnimationPath::builtin("IOK6432.DEF"), + CGI->generaltexth->zelp[568], std::bind(&CAltarWindow::close, this), EShortcut::GLOBAL_RETURN); + altar->setRedrawParent(true); + redraw(); +} + +void CAltarWindow::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) +{ + if(!getState().has_value()) + return; + + if(auto altarArtifacts = std::static_pointer_cast(altar)) + { + if(const auto pickedArt = getPickedArtifact()) + altarArtifacts->setSelectedArtifact(pickedArt); + else + altarArtifacts->setSelectedArtifact(nullptr); + } + CWindowWithArtifacts::artifactMoved(srcLoc, destLoc, withRedraw); +} + +void CAltarWindow::showAll(Canvas & to) +{ + // This func is temporary workaround for compliance with CTradeWindow + CWindowObject::showAll(to); + + if(altar->hRight) + { + to.drawBorder(Rect::createAround(altar->hRight->pos, 1), Colors::BRIGHT_YELLOW, 2); + altar->hRight->showAllAt(altar->pos.topLeft() + Point(396, 423), "", to); + } + if(altar->hLeft) + { + to.drawBorder(Rect::createAround(altar->hLeft->pos, 1), Colors::BRIGHT_YELLOW, 2); + altar->hLeft->showAllAt(altar->pos.topLeft() + Point(150, 423), "", to); + } +} diff --git a/client/windows/CAltarWindow.h b/client/windows/CAltarWindow.h new file mode 100644 index 000000000..9d1ffe2a5 --- /dev/null +++ b/client/windows/CAltarWindow.h @@ -0,0 +1,38 @@ +/* + * CAltarWindow.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 "../widgets/CAltar.h" +#include "../widgets/CWindowWithArtifacts.h" +#include "CWindowObject.h" + +class CAltarWindow : public CWindowObject, public CWindowWithArtifacts +{ +public: + CAltarWindow(const IMarket * market, const CGHeroInstance * hero, const std::function & onWindowClosed, EMarketMode mode); + void updateExpToLevel(); + void updateGarrison(); + const CGHeroInstance * getHero() const; + void close() override; + + void artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) override; + void showAll(Canvas & to) override; + +private: + const CGHeroInstance * hero; + std::shared_ptr altar; + std::shared_ptr changeModeButton; + std::shared_ptr quitButton; + std::function windowClosedCallback; + std::shared_ptr statusBar; + + void createAltarArtifacts(const IMarket * market, const CGHeroInstance * hero); + void createAltarCreatures(const IMarket * market, const CGHeroInstance * hero); +}; diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index f852824a0..04b1c59cf 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -66,14 +66,6 @@ void CTradeWindow::initTypes() itemsType[1] = ARTIFACT_INSTANCE; itemsType[0] = RESOURCE; break; - case EMarketMode::CREATURE_EXP: - itemsType[1] = CREATURE; - itemsType[0] = CREATURE_PLACEHOLDER; - break; - case EMarketMode::ARTIFACT_EXP: - itemsType[1] = ARTIFACT_TYPE; - itemsType[0] = ARTIFACT_PLACEHOLDER; - break; } } @@ -145,26 +137,7 @@ std::vector *CTradeWindow::getItemsIds(bool Left) { std::vector *ids = nullptr; - if(mode == EMarketMode::ARTIFACT_EXP) - return new std::vector(22, -1); - - if(Left) - { - switch(itemsType[1]) - { - case CREATURE: - ids = new std::vector; - for(int i = 0; i < 7; i++) - { - if(const CCreature *c = hero->getCreature(SlotID(i))) - ids->push_back(c->getId()); - else - ids->push_back(-1); - } - break; - } - } - else + if(!Left) { switch(itemsType[0]) { @@ -186,50 +159,31 @@ std::vector *CTradeWindow::getItemsIds(bool Left) void CTradeWindow::getPositionsFor(std::vector &poss, bool Left, EType type) const { - if(mode == EMarketMode::ARTIFACT_EXP && !Left) + //seven boxes: + // X X X + // X X X + // X + int h, w, x, y, dx, dy; + int leftToRightOffset; + getBaseForPositions(type, dx, dy, x, y, h, w, !Left, leftToRightOffset); + + const std::vector tmp = { - //22 boxes, 5 in row, last row: two boxes centered - int h, w, x, y, dx, dy; - h = w = 44; - x = 317; - y = 53; - dx = 54; - dy = 70; - for (int i = 0; i < 4 ; i++) - for (int j = 0; j < 5 ; j++) - poss.push_back(Rect(x + dx*j, y + dy*i, w, h)); + Rect(Point(x + 0 * dx, y + 0 * dx), Point(w, h) ), + Rect(Point(x + 1 * dx, y + 0 * dx), Point(w, h) ), + Rect(Point(x + 2 * dx, y + 0 * dx), Point(w, h) ), + Rect(Point(x + 0 * dx, y + 1 * dy), Point(w, h) ), + Rect(Point(x + 1 * dx, y + 1 * dy), Point(w, h) ), + Rect(Point(x + 2 * dx, y + 1 * dy), Point(w, h) ), + Rect(Point(x + 1 * dx, y + 2 * dy), Point(w, h) ) + }; - poss.push_back(Rect((int)(x + dx * 1.5), (y + dy * 4), w, h)); - poss.push_back(Rect((int)(x + dx * 2.5), (y + dy * 4), w, h)); - } - else + vstd::concatenate(poss, tmp); + + if(!Left) { - //seven boxes: - // X X X - // X X X - // X - int h, w, x, y, dx, dy; - int leftToRightOffset; - getBaseForPositions(type, dx, dy, x, y, h, w, !Left, leftToRightOffset); - - const std::vector tmp = - { - Rect(Point(x + 0 * dx, y + 0 * dx), Point(w, h) ), - Rect(Point(x + 1 * dx, y + 0 * dx), Point(w, h) ), - Rect(Point(x + 2 * dx, y + 0 * dx), Point(w, h) ), - Rect(Point(x + 0 * dx, y + 1 * dy), Point(w, h) ), - Rect(Point(x + 1 * dx, y + 1 * dy), Point(w, h) ), - Rect(Point(x + 2 * dx, y + 1 * dy), Point(w, h) ), - Rect(Point(x + 1 * dx, y + 2 * dy), Point(w, h) ) - }; - - vstd::concatenate(poss, tmp); - - if(!Left) - { - for(Rect &r : poss) - r.x += leftToRightOffset; - } + for(Rect &r : poss) + r.x += leftToRightOffset; } } @@ -288,9 +242,9 @@ void CTradeWindow::showAll(Canvas & to) if(readyToTrade) { if(hLeft) - hLeft->showAllAt(pos.topLeft() + selectionOffset(true), selectionSubtitle(true), to); + hLeft->showAllAt(pos.topLeft() + selectionOffset(true), updateSlotSubtitle(true), to); if(hRight) - hRight->showAllAt(pos.topLeft() + selectionOffset(false), selectionSubtitle(false), to); + hRight->showAllAt(pos.topLeft() + selectionOffset(false), updateSlotSubtitle(false), to); } } @@ -315,7 +269,6 @@ void CTradeWindow::setMode(EMarketMode Mode) { case EMarketMode::CREATURE_EXP: case EMarketMode::ARTIFACT_EXP: - GH.windows().createAndPushWindow(m, h, functor, Mode); break; default: GH.windows().createAndPushWindow(m, h, functor, Mode); @@ -612,7 +565,7 @@ bool CMarketplaceWindow::printButtonFor(EMarketMode M) const } } -void CMarketplaceWindow::garrisonChanged() +void CMarketplaceWindow::updateGarrison() { if(mode != EMarketMode::CREATURE_RESOURCE) return; @@ -642,7 +595,7 @@ void CMarketplaceWindow::artifactsChanged(bool Left) redraw(); } -std::string CMarketplaceWindow::selectionSubtitle(bool Left) const +std::string CMarketplaceWindow::updateSlotSubtitle(bool Left) const { if(Left) { @@ -811,431 +764,3 @@ void CMarketplaceWindow::updateTraderText() } traderText->setText(CGI->generaltexth->allTexts[gnrtxtnr]); } - -CAltarWindow::CAltarWindow(const IMarket * Market, const CGHeroInstance * Hero, const std::function & onWindowClosed, EMarketMode Mode) - : CTradeWindow(ImagePath::builtin(Mode == EMarketMode::CREATURE_EXP ? "ALTARMON.bmp" : "ALTRART2.bmp"), Market, Hero, onWindowClosed, Mode) -{ - OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); - - if(Mode == EMarketMode::CREATURE_EXP) - { - //%s's Creatures - labels.push_back(std::make_shared(155, 30, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, - boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->getNameTranslated()))); - - //Altar of Sacrifice - labels.push_back(std::make_shared(450, 30, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[479])); - - //To sacrifice creatures, move them from your army on to the Altar and click Sacrifice - new CTextBox(CGI->generaltexth->allTexts[480], Rect(320, 56, 256, 40), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW); - - slider = std::make_shared(Point(231, 481), 137, std::bind(&CAltarWindow::sliderMoved, this, _1), 0, 0, 0, Orientation::HORIZONTAL); - max = std::make_shared(Point(147, 520), AnimationPath::builtin("IRCBTNS.DEF"), CGI->generaltexth->zelp[578], std::bind(&CSlider::scrollToMax, slider)); - - sacrificedUnits.resize(GameConstants::ARMY_SIZE, 0); - sacrificeAll = std::make_shared(Point(393, 520), AnimationPath::builtin("ALTARMY.DEF"), CGI->generaltexth->zelp[579], std::bind(&CAltarWindow::SacrificeAll,this)); - - initItems(true); - mimicCres(); - } - else - { - //Sacrifice artifacts for experience - labels.push_back(std::make_shared(450, 34, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[477])); - //%s's Creatures - labels.push_back(std::make_shared(302, 423, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[478])); - - sacrificeAll = std::make_shared(Point(393, 520), AnimationPath::builtin("ALTFILL.DEF"), CGI->generaltexth->zelp[571], std::bind(&CAltarWindow::SacrificeAll,this)); - sacrificeAll->block(hero->artifactsInBackpack.empty() && hero->artifactsWorn.empty()); - sacrificeBackpack = std::make_shared(Point(147, 520), AnimationPath::builtin("ALTEMBK.DEF"), CGI->generaltexth->zelp[570], std::bind(&CAltarWindow::SacrificeBackpack,this)); - sacrificeBackpack->block(hero->artifactsInBackpack.empty()); - - arts = std::make_shared(Point(-365, -12)); - arts->setHero(hero); - addSetAndCallbacks(arts); - - initItems(true); - initItems(false); - artIcon = std::make_shared(AnimationPath::builtin("ARTIFACT"), 0, 0, 281, 442); - artIcon->disable(); - } - - //Experience needed to reach next level - texts.push_back(std::make_shared(CGI->generaltexth->allTexts[475], Rect(15, 415, 125, 50), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW)); - //Total experience on the Altar - texts.push_back(std::make_shared(CGI->generaltexth->allTexts[476], Rect(15, 495, 125, 40), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW)); - - 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), AnimationPath::builtin("IOK6432.DEF"), CGI->generaltexth->zelp[568], [&](){ close();}, EShortcut::GLOBAL_RETURN); - - deal = std::make_shared(Point(269, 520), AnimationPath::builtin("ALTSACR.DEF"), CGI->generaltexth->zelp[585], std::bind(&CAltarWindow::makeDeal,this)); - - if(Mode == EMarketMode::CREATURE_EXP) - { - auto changeMode = std::make_shared(Point(516, 421), AnimationPath::builtin("ALTART.DEF"), CGI->generaltexth->zelp[580], std::bind(&CTradeWindow::setMode,this, EMarketMode::ARTIFACT_EXP)); - if(Hero->getAlignment() == ::EAlignment::EVIL) - changeMode->block(true); - buttons.push_back(changeMode); - } - else if(Mode == EMarketMode::ARTIFACT_EXP) - { - auto changeMode = std::make_shared(Point(516, 421), AnimationPath::builtin("ALTSACC.DEF"), CGI->generaltexth->zelp[572], std::bind(&CTradeWindow::setMode,this, EMarketMode::CREATURE_EXP)); - if(Hero->getAlignment() == ::EAlignment::GOOD) - changeMode->block(true); - buttons.push_back(changeMode); - } - - expPerUnit.resize(GameConstants::ARMY_SIZE, 0); - getExpValues(); - - expToLevel = std::make_shared(73, 475, FONT_SMALL, ETextAlignment::CENTER); - expOnAltar = std::make_shared(73, 543, FONT_SMALL, ETextAlignment::CENTER); - - setExpToLevel(); - calcTotalExp(); - blockTrade(); -} - -CAltarWindow::~CAltarWindow() = default; - -void CAltarWindow::getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const -{ - leftToRightOffset = 289; - x = 45; - y = 110; - w = 58; - h = 64; - dx = 83; - dy = 98; -} - -void CAltarWindow::sliderMoved(int to) -{ - if(hLeft) - sacrificedUnits[hLeft->serial] = to; - if(hRight) - updateRight(hRight); - - deal->block(!to); - calcTotalExp(); - redraw(); -} - -void CAltarWindow::makeDeal() -{ - if(mode == EMarketMode::CREATURE_EXP) - { - blockTrade(); - slider->scrollTo(0); - - std::vector ids; - std::vector toSacrifice; - - for(int i = 0; i < sacrificedUnits.size(); i++) - { - if(sacrificedUnits[i]) - { - ids.push_back(i); - toSacrifice.push_back(sacrificedUnits[i]); - } - } - - LOCPLINT->cb->trade(market, mode, ids, {}, toSacrifice, hero); - - for(int& val : sacrificedUnits) - val = 0; - - for(auto item : items[0]) - { - item->setType(CREATURE_PLACEHOLDER); - item->subtitle = ""; - } - } - else - { - std::vector positions; - for(const CArtifactInstance * art : arts->artifactsOnAltar) - { - positions.push_back(hero->getSlotByInstance(art)); - } - std::sort(positions.begin(), positions.end(), std::greater<>()); - - LOCPLINT->cb->trade(market, mode, positions, {}, {}, hero); - arts->artifactsOnAltar.clear(); - - for(auto item : items[0]) - { - item->setID(-1); - item->subtitle = ""; - } - - //arts->scrollBackpack(0); - deal->block(true); - } - - calcTotalExp(); -} - -void CAltarWindow::SacrificeAll() -{ - if(mode == EMarketMode::CREATURE_EXP) - { - bool movedAnything = false; - for(auto item : items[1]) - sacrificedUnits[item->serial] = hero->getStackCount(SlotID(item->serial)); - - sacrificedUnits[items[1].front()->serial]--; - - for(auto item : items[0]) - { - updateRight(item); - if(item->type == CREATURE) - movedAnything = true; - } - - deal->block(!movedAnything); - calcTotalExp(); - } - else - { - std::vector> artsForMove; - for(const auto& slotInfo : arts->visibleArtSet.artifactsWorn) - { - if(!slotInfo.second.locked && slotInfo.second.artifact->artType->isTradable()) - artsForMove.push_back(slotInfo.second.artifact); - } - for(auto artInst : artsForMove) - moveArtToAltar(nullptr, artInst); - arts->updateWornSlots(); - SacrificeBackpack(); - } - redraw(); -} - -void CAltarWindow::selectionChanged(bool side) -{ - if(mode != EMarketMode::CREATURE_EXP) - return; - - int stackCount = 0; - for (int i = 0; i < GameConstants::ARMY_SIZE; i++) - if(hero->getStackCount(SlotID(i)) > sacrificedUnits[i]) - stackCount++; - - slider->setAmount(hero->getStackCount(SlotID(hLeft->serial)) - (stackCount == 1)); - slider->block(!slider->getAmount()); - slider->scrollTo(sacrificedUnits[hLeft->serial]); - max->block(!slider->getAmount()); - selectOppositeItem(side); - readyToTrade = true; - redraw(); -} - -void CAltarWindow::selectOppositeItem(bool side) -{ - bool oppositeSide = !side; - int pos = vstd::find_pos(items[side], side ? hLeft : hRight); - int oppositePos = vstd::find_pos(items[oppositeSide], oppositeSide ? hLeft : hRight); - - if(pos >= 0 && pos != oppositePos) - { - if(oppositeSide) - hLeft = items[oppositeSide][pos]; - else - hRight = items[oppositeSide][pos]; - - selectionChanged(oppositeSide); - } -} - -void CAltarWindow::mimicCres() -{ - OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); - std::vector positions; - getPositionsFor(positions, false, CREATURE); - - for(auto item : items[1]) - { - auto hlp = std::make_shared(positions[item->serial].topLeft(), CREATURE_PLACEHOLDER, item->id, false, item->serial); - hlp->pos = positions[item->serial] + this->pos.topLeft(); - items[0].push_back(hlp); - } -} - -Point CAltarWindow::selectionOffset(bool Left) const -{ - if(Left) - return Point(150, 421); - else - return Point(396, 421); -} - -std::string CAltarWindow::selectionSubtitle(bool Left) const -{ - if(Left && slider && hLeft) - return std::to_string(slider->getValue()); - else if(!Left && hRight) - return hRight->subtitle; - else - return ""; -} - -void CAltarWindow::artifactsChanged(bool left) -{ - -} - -void CAltarWindow::garrisonChanged() -{ - if(mode != EMarketMode::CREATURE_EXP) - return; - - std::set> empty; - getEmptySlots(empty); - - removeItems(empty); - - initSubs(true); - getExpValues(); -} - -void CAltarWindow::getExpValues() -{ - int dump; - for(auto item : items[1]) - { - if(item->id >= 0) - market->getOffer(item->id, 0, dump, expPerUnit[item->serial], EMarketMode::CREATURE_EXP); - } -} - -void CAltarWindow::calcTotalExp() -{ - int val = 0; - if(mode == EMarketMode::CREATURE_EXP) - { - for (int i = 0; i < sacrificedUnits.size(); i++) - { - val += expPerUnit[i] * sacrificedUnits[i]; - } - } - else - { - 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); - val += valOfArt; //WAS val += valOfArt * arts->artifactsOnAltar.count(*i); - } - } - val = static_cast(hero->calculateXp(val)); - expOnAltar->setText(std::to_string(val)); -} - -void CAltarWindow::setExpToLevel() -{ - expToLevel->setText(std::to_string(CGI->heroh->reqExp(CGI->heroh->level(hero->exp)+1) - hero->exp)); -} - -void CAltarWindow::blockTrade() -{ - hLeft = hRight = nullptr; - readyToTrade = false; - if(slider) - { - slider->block(true); - max->block(true); - } - deal->block(true); -} - -void CAltarWindow::updateRight(std::shared_ptr toUpdate) -{ - int val = sacrificedUnits[toUpdate->serial]; - toUpdate->setType(val ? CREATURE : CREATURE_PLACEHOLDER); - toUpdate->subtitle = val ? boost::str(boost::format(CGI->generaltexth->allTexts[122]) % std::to_string(hero->calculateXp(val * expPerUnit[toUpdate->serial]))) : ""; //%s exp -} - -int CAltarWindow::firstFreeSlot() -{ - int ret = -1; - while(items[0][++ret]->id >= 0 && ret + 1 < items[0].size()); - return items[0][ret]->id == -1 ? ret : -1; -} - -void CAltarWindow::SacrificeBackpack() -{ - while(!arts->visibleArtSet.artifactsInBackpack.empty()) - { - if(!putOnAltar(nullptr, arts->visibleArtSet.artifactsInBackpack[0].artifact)) - break; - }; - calcTotalExp(); -} - -void CAltarWindow::artifactPicked() -{ - redraw(); -} - -void CAltarWindow::showAll(Canvas & to) -{ - CTradeWindow::showAll(to); - if(mode == EMarketMode::ARTIFACT_EXP && arts) - { - if(auto pickedArt = arts->getPickedArtifact()) - { - artIcon->setFrame(pickedArt->artType->getIconIndex()); - artIcon->showAll(to); - - int dmp, val; - market->getOffer(pickedArt->getTypeId(), 0, dmp, val, EMarketMode::ARTIFACT_EXP); - val = static_cast(hero->calculateXp(val)); - - to.drawText(Point(304, 498), FONT_SMALL, Colors::WHITE, ETextAlignment::CENTER, std::to_string(val)); - } - } -} - -bool CAltarWindow::putOnAltar(std::shared_ptr altarSlot, const CArtifactInstance *art) -{ - if(!art->artType->isTradable()) //special art - { - logGlobal->warn("Cannot put special artifact on altar!"); - return false; - } - - if(!altarSlot || altarSlot->id != -1) - { - int slotIndex = firstFreeSlot(); - if(slotIndex < 0) - { - logGlobal->warn("No free slots on altar!"); - return false; - } - altarSlot = items[0][slotIndex]; - } - - int dmp, val; - market->getOffer(art->artType->getId(), 0, dmp, val, EMarketMode::ARTIFACT_EXP); - val = static_cast(hero->calculateXp(val)); - - arts->artifactsOnAltar.insert(art); - arts->deleteFromVisible(art); - altarSlot->setArtInstance(art); - altarSlot->subtitle = std::to_string(val); - - deal->block(false); - return true; -} - -void CAltarWindow::moveArtToAltar(std::shared_ptr altarSlot, const CArtifactInstance *art) -{ - if(putOnAltar(altarSlot, art)) - { - CCS->curh->dragAndDropCursor(nullptr); - arts->unmarkSlots(); - } -} diff --git a/client/windows/CTradeWindow.h b/client/windows/CTradeWindow.h index 704d8aaef..9c0763d68 100644 --- a/client/windows/CTradeWindow.h +++ b/client/windows/CTradeWindow.h @@ -45,16 +45,13 @@ public: virtual void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const = 0; virtual void selectionChanged(bool side) = 0; //true == left virtual Point selectionOffset(bool Left) const = 0; - virtual std::string selectionSubtitle(bool Left) const = 0; - virtual void garrisonChanged() = 0; + virtual std::string updateSlotSubtitle(bool Left) const = 0; + virtual void updateGarrison() = 0; virtual void artifactsChanged(bool left) = 0; protected: std::function onWindowClosed; std::shared_ptr statusBar; - std::vector> labels; std::vector> images; - std::vector> buttons; - std::vector> texts; }; class CMarketplaceWindow : public CTradeWindow @@ -78,58 +75,12 @@ public: ~CMarketplaceWindow(); Point selectionOffset(bool Left) const override; - std::string selectionSubtitle(bool Left) const override; + std::string updateSlotSubtitle(bool Left) const override; - void garrisonChanged() override; //removes creatures with count 0 from the list (apparently whole stack has been sold) + void updateGarrison() override; //removes creatures with count 0 from the list (apparently whole stack has been sold) void artifactsChanged(bool left) override; void resourceChanged(); void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const override; void updateTraderText(); }; - -class CAltarWindow : public CTradeWindow -{ - std::shared_ptr artIcon; -public: - std::vector sacrificedUnits; //[slot_nr] -> how many creatures from that slot will be sacrificed - std::vector expPerUnit; - - std::shared_ptr sacrificeAll; - std::shared_ptr sacrificeBackpack; - std::shared_ptr expToLevel; - std::shared_ptr expOnAltar; - std::shared_ptr arts; - - CAltarWindow(const IMarket * Market, const CGHeroInstance * Hero, const std::function & onWindowClosed, EMarketMode Mode); - ~CAltarWindow(); - - void getExpValues(); - - void selectionChanged(bool side) override; //true == left - void selectOppositeItem(bool side); - void SacrificeAll(); - void SacrificeBackpack(); - - void putOnAltar(int backpackIndex); - bool putOnAltar(std::shared_ptr altarSlot, const CArtifactInstance * art); - void makeDeal() override; - void showAll(Canvas & to) override; - - void blockTrade(); - void sliderMoved(int to); - void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const override; - void mimicCres(); - - Point selectionOffset(bool Left) const override; - std::string selectionSubtitle(bool Left) const override; - void garrisonChanged() override; - void artifactsChanged(bool left) override; - void calcTotalExp(); - void setExpToLevel(); - void updateRight(std::shared_ptr toUpdate); - - void artifactPicked(); - int firstFreeSlot(); - void moveArtToAltar(std::shared_ptr, const CArtifactInstance * art); -}; diff --git a/client/windows/CWindowObject.h b/client/windows/CWindowObject.h index 4746f52c8..52111aee6 100644 --- a/client/windows/CWindowObject.h +++ b/client/windows/CWindowObject.h @@ -16,8 +16,6 @@ class CGStatusBar; class CWindowObject : public WindowBase { - std::shared_ptr createBg(const ImagePath & imageName, bool playerColored); - std::vector> shadowParts; void setShadow(bool on); @@ -32,6 +30,7 @@ protected: //To display border void updateShadow(); void setBackground(const ImagePath & filename); + std::shared_ptr createBg(const ImagePath & imageName, bool playerColored); public: enum EOptions {