From c6ca6ad835a421d5f318281123f8dbfa737831aa Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Tue, 16 Jan 2024 21:54:00 +0200 Subject: [PATCH] sacrifice routine --- client/widgets/CWindowWithArtifacts.cpp | 46 ++++++++++--------- client/widgets/markets/CAltarArtifacts.cpp | 36 +++++++-------- client/widgets/markets/CAltarArtifacts.h | 1 + lib/CArtHandler.cpp | 7 ++- lib/constants/EntityIdentifiers.h | 2 +- lib/constants/NumericConstants.h | 1 + lib/networkPacks/NetPacksLib.cpp | 16 +++---- lib/networkPacks/TradeItem.h | 2 +- server/CGameHandler.cpp | 52 ++++++++++------------ server/CGameHandler.h | 2 +- server/NetPacksServer.cpp | 9 ++-- 11 files changed, 90 insertions(+), 84 deletions(-) diff --git a/client/widgets/CWindowWithArtifacts.cpp b/client/widgets/CWindowWithArtifacts.cpp index 0902df564..022b88ea0 100644 --- a/client/widgets/CWindowWithArtifacts.cpp +++ b/client/widgets/CWindowWithArtifacts.cpp @@ -87,7 +87,7 @@ void CWindowWithArtifacts::clickPressedArtPlaceHero(CArtifactsOfHeroBase & artsI if(artPlace.isLocked()) return; - const auto checkSpecialArts = [](const CGHeroInstance * hero, CArtPlace & artPlace) -> bool + const auto checkSpecialArts = [](const CGHeroInstance * hero, CArtPlace & artPlace, bool isTrade) -> bool { if(artPlace.getArt()->getTypeId() == ArtifactID::SPELLBOOK) { @@ -97,10 +97,19 @@ void CWindowWithArtifacts::clickPressedArtPlaceHero(CArtifactsOfHeroBase & artsI if(artPlace.getArt()->getTypeId() == ArtifactID::CATAPULT) { // The Catapult must be equipped - std::vector> catapult(1, std::make_shared(ComponentType::ARTIFACT, ArtifactID(ArtifactID::CATAPULT))); - LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult); + LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], + std::vector>(1, std::make_shared(ComponentType::ARTIFACT, ArtifactID(ArtifactID::CATAPULT)))); return false; } + if(isTrade) + { + if(!artPlace.getArt()->artType->isTradable()) + { + LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21], + std::vector>(1, std::make_shared(ComponentType::ARTIFACT, artPlace.getArt()->getTypeId()))); + return false; + } + } return true; }; @@ -155,24 +164,21 @@ void CWindowWithArtifacts::clickPressedArtPlaceHero(CArtifactsOfHeroBase & artsI if(isTransferAllowed) artSetPtr->swapArtifacts(srcLoc, dstLoc); } - else + else if(auto art = artPlace.getArt()) { - if(artPlace.getArt()) + if(artSetPtr->getHero()->tempOwner == LOCPLINT->playerID) { - if(artSetPtr->getHero()->tempOwner == LOCPLINT->playerID) - { - if(checkSpecialArts(hero, artPlace)) - artSetPtr->pickUpArtifact(artPlace); - } - else - { - for(const auto artSlot : ArtifactUtils::unmovableSlots()) - if(artPlace.slot == artSlot) - { - msg = CGI->generaltexth->allTexts[21]; - break; - } - } + if(checkSpecialArts(hero, artPlace, std::is_same_v> ? true : false)) + artSetPtr->pickUpArtifact(artPlace); + } + else + { + for(const auto artSlot : ArtifactUtils::unmovableSlots()) + if(artPlace.slot == artSlot) + { + msg = CGI->generaltexth->allTexts[21]; + break; + } } } @@ -234,6 +240,7 @@ void CWindowWithArtifacts::showPopupArtPlaceHero(CArtifactsOfHeroBase & artsInst // Hero (Main, Exchange) window, Kingdom window, Backpack window right click handler if constexpr( + std::is_same_v> || std::is_same_v> || std::is_same_v> || std::is_same_v>) @@ -254,7 +261,6 @@ void CWindowWithArtifacts::showPopupArtPlaceHero(CArtifactsOfHeroBase & artsInst } // Altar window, Market window right click handler else if constexpr( - std::is_same_v> || std::is_same_v> || std::is_same_v>) { diff --git a/client/widgets/markets/CAltarArtifacts.cpp b/client/widgets/markets/CAltarArtifacts.cpp index 1436b4262..20afc459e 100644 --- a/client/widgets/markets/CAltarArtifacts.cpp +++ b/client/widgets/markets/CAltarArtifacts.cpp @@ -67,6 +67,13 @@ CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance * CTradeBase::deselect(); }; +CAltarArtifacts::~CAltarArtifacts() +{ + // TODO: If the backpack capacity limit is enabled, artifacts may remain on the altar. + // Perhaps should be erased in CGameHandler::objectVisitEnded if id of visited object will be available + LOCPLINT->cb->bulkMoveArtifacts(altarId, heroArts->getHero()->id, false, true, true); +} + TExpType CAltarArtifacts::calcExpAltarForHero() { TExpType expOnAltar(0); @@ -79,23 +86,21 @@ TExpType CAltarArtifacts::calcExpAltarForHero() void CAltarArtifacts::makeDeal() { std::vector positions; - for(const auto art : tradeSlotsMap) + for(const auto & [artInst, altarSlot] : tradeSlotsMap) { - positions.push_back(hero->getSlotByInstance(art.first)); + positions.push_back(artInst->getId()); } - std::sort(positions.begin(), positions.end()); - std::reverse(positions.begin(), positions.end()); - LOCPLINT->cb->trade(market, EMarketMode::ARTIFACT_EXP, positions, std::vector(), std::vector(), hero); - heroArts->artifactsOnAltar.clear(); + tradeSlotsMap.clear(); + // The event for removing artifacts from the altar will not be triggered. Therefore, we clean the altar immediately. for(auto item : items[0]) { item->setID(-1); item->subtitle.clear(); } - deal->block(true); calcExpAltarForHero(); + deal->block(tradeSlotsMap.empty()); } void CAltarArtifacts::sacrificeAll() @@ -110,15 +115,8 @@ void CAltarArtifacts::sacrificeBackpack() void CAltarArtifacts::setSelectedArtifact(const CArtifactInstance * art) { - if(art) - { - selectedCost->setText(std::to_string(calcExpCost(art))); - } - else - { - selectedArt->setArtifact(nullptr); - selectedCost->setText(""); - } + selectedArt->setArtifact(art); + selectedCost->setText(art == nullptr ? "" : std::to_string(calcExpCost(art))); } std::shared_ptr CAltarArtifacts::getAOHset() const @@ -133,8 +131,8 @@ ObjectInstanceID CAltarArtifacts::getObjId() const void CAltarArtifacts::updateSlots() { - assert(altarArtifacts->artifactsInBackpack.size() <= 22); - assert(tradeSlotsMap.size() <= 22); + assert(altarArtifacts->artifactsInBackpack.size() <= GameConstants::ALTAR_ARTIFACTS_SLOTS); + assert(tradeSlotsMap.size() <= GameConstants::ALTAR_ARTIFACTS_SLOTS); auto slotsToAdd = tradeSlotsMap; for(auto & altarSlot : items[0]) @@ -209,7 +207,7 @@ void CAltarArtifacts::onSlotClickPressed(const std::shared_ptr & } } -TExpType CAltarArtifacts::calcExpCost(const CArtifactInstance* art) +TExpType CAltarArtifacts::calcExpCost(const CArtifactInstance * art) { int dmp = 0; int expOfArt = 0; diff --git a/client/widgets/markets/CAltarArtifacts.h b/client/widgets/markets/CAltarArtifacts.h index 9ced60cdf..eb5070626 100644 --- a/client/widgets/markets/CAltarArtifacts.h +++ b/client/widgets/markets/CAltarArtifacts.h @@ -16,6 +16,7 @@ class CAltarArtifacts : public CExperienceAltar { public: CAltarArtifacts(const IMarket * market, const CGHeroInstance * hero); + ~CAltarArtifacts(); TExpType calcExpAltarForHero() override; void makeDeal() override; void sacrificeAll() override; diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index e230405dc..b932542fc 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -258,7 +258,7 @@ CArtifact::CArtifact() possibleSlots[ArtBearer::HERO]; //we want to generate map entry even if it will be empty possibleSlots[ArtBearer::CREATURE]; //we want to generate map entry even if it will be empty possibleSlots[ArtBearer::COMMANDER]; - possibleSlots[ArtBearer::ALTAR].push_back(ArtifactPosition::ALTAR); + possibleSlots[ArtBearer::ALTAR]; } //This destructor should be placed here to avoid side effects @@ -477,6 +477,9 @@ CArtifact * CArtHandler::loadFromJson(const std::string & scope, const JsonNode } }); + if(art->isTradable()) + art->possibleSlots.at(ArtBearer::ALTAR).push_back(ArtifactPosition::ALTAR); + return art; } @@ -908,7 +911,7 @@ const ArtSlotInfo * CArtifactSet::getSlot(const ArtifactPosition & pos) const bool CArtifactSet::isPositionFree(const ArtifactPosition & pos, bool onlyLockCheck) const { if(bearerType() == ArtBearer::ALTAR) - return artifactsInBackpack.size() < 22; + return artifactsInBackpack.size() < GameConstants::ALTAR_ARTIFACTS_SLOTS; if(const ArtSlotInfo *s = getSlot(pos)) return (onlyLockCheck || !s->artifact) && !s->locked; diff --git a/lib/constants/EntityIdentifiers.h b/lib/constants/EntityIdentifiers.h index c83d63bdd..e745a4729 100644 --- a/lib/constants/EntityIdentifiers.h +++ b/lib/constants/EntityIdentifiers.h @@ -341,7 +341,7 @@ public: enum Type { NO_OBJ = -1, - ALTAR_OF_SACRIFICE [[deprecated]] = 2, + ALTAR_OF_SACRIFICE = 2, ANCHOR_POINT = 3, ARENA = 4, ARTIFACT = 5, diff --git a/lib/constants/NumericConstants.h b/lib/constants/NumericConstants.h index fcc3da876..fc6def5ab 100644 --- a/lib/constants/NumericConstants.h +++ b/lib/constants/NumericConstants.h @@ -51,6 +51,7 @@ namespace GameConstants constexpr ui32 BASE_MOVEMENT_COST = 100; //default cost for non-diagonal movement constexpr int64_t PLAYER_RESOURCES_CAP = 1000 * 1000 * 1000; + constexpr int ALTAR_ARTIFACTS_SLOTS = 22; } VCMI_LIB_NAMESPACE_END diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index bbe706877..ea2e4d6cf 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -1776,35 +1776,35 @@ void PutArtifact::applyGs(CGameState *gs) void EraseArtifact::applyGs(CGameState *gs) { - const auto hero = gs->getHero(al.artHolder); - assert(hero); - const auto slot = hero->getSlot(al.slot); + const auto artSet = gs->getArtSet(al.artHolder); + assert(artSet); + const auto slot = artSet->getSlot(al.slot); if(slot->locked) { logGlobal->debug("Erasing locked artifact: %s", slot->artifact->artType->getNameTranslated()); DisassembledArtifact dis; dis.al.artHolder = al.artHolder; - for(auto & slotInfo : hero->artifactsWorn) + for(auto & slotInfo : artSet->artifactsWorn) { auto art = slotInfo.second.artifact; if(art->isCombined() && art->isPart(slot->artifact)) { - dis.al.slot = hero->getArtPos(art); + dis.al.slot = artSet->getArtPos(art); break; } } assert((dis.al.slot != ArtifactPosition::PRE_FIRST) && "Failed to determine the assembly this locked artifact belongs to"); - logGlobal->debug("Found the corresponding assembly: %s", hero->getArt(dis.al.slot)->artType->getNameTranslated()); + logGlobal->debug("Found the corresponding assembly: %s", artSet->getArt(dis.al.slot)->artType->getNameTranslated()); dis.applyGs(gs); } else { logGlobal->debug("Erasing artifact %s", slot->artifact->artType->getNameTranslated()); } - auto art = hero->getArt(al.slot); + auto art = artSet->getArt(al.slot); assert(art); - art->removeFrom(*hero, al.slot); + art->removeFrom(*artSet, al.slot); } void MoveArtifact::applyGs(CGameState * gs) diff --git a/lib/networkPacks/TradeItem.h b/lib/networkPacks/TradeItem.h index 43846d1d1..fba54e63e 100644 --- a/lib/networkPacks/TradeItem.h +++ b/lib/networkPacks/TradeItem.h @@ -14,7 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN -using TradeItemSell = VariantIdentifier; +using TradeItemSell = VariantIdentifier; using TradeItemBuy = VariantIdentifier; VCMI_LIB_NAMESPACE_END diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 92d07647d..004e48fe4 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3436,10 +3436,7 @@ bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2) if(o1->ID == Obj::ALTAR_OF_SACRIFICE) { - const auto visitingHero = getVisitingHero(o1); - const auto thisHero = static_cast(o2); - if(visitingHero == thisHero) - return true; + return true; } if(o2->ID == Obj::ALTAR_OF_SACRIFICE) { @@ -3791,10 +3788,15 @@ bool CGameHandler::sacrificeCreatures(const IMarket * market, const CGHeroInstan return true; } -bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector & slot) +bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector & arts) { if (!hero) COMPLAIN_RET("You need hero to sacrifice artifact!"); + if(hero->getAlignment() == EAlignment::EVIL) + COMPLAIN_RET("Evil hero can't sacrifice artifact!"); + + assert(m); + auto altarObj = dynamic_cast(m); int expSum = 0; auto finish = [this, &hero, &expSum]() @@ -3802,34 +3804,28 @@ bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * h giveExperience(hero, hero->calculateXp(expSum)); }; - for(int i = 0; i < slot.size(); ++i) + for(const auto & artInstId : arts) { - ArtifactLocation al(hero->id, slot[i]); - const CArtifactInstance * a = hero->getArt(al.slot); - - if(!a) + if(auto art = altarObj->getArtByInstanceId(artInstId)) + { + if(art->artType->isTradable()) + { + int dmp; + int expToGive; + m->getOffer(art->getTypeId(), 0, dmp, expToGive, EMarketMode::ARTIFACT_EXP); + expSum += expToGive; + removeArtifact(ArtifactLocation(altarObj->id, altarObj->getSlotByInstance(art))); + } + else + { + COMPLAIN_RET("Cannot sacrifice not tradable artifact!"); + } + } + else { finish(); COMPLAIN_RET("Cannot find artifact to sacrifice!"); } - - const CArtifactInstance * art = hero->getArt(slot[i]); - - if(!art) - { - finish(); - COMPLAIN_RET("No artifact at position to sacrifice!"); - } - - si32 typId = art->artType->getId(); - int dmp; - int expToGive; - - m->getOffer(typId, 0, dmp, expToGive, EMarketMode::ARTIFACT_EXP); - - expSum += expToGive; - - removeArtifact(al); } finish(); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index a7dca84c2..b2136df6b 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -262,7 +262,7 @@ public: bool isPlayerOwns(CPackForServer * pack, ObjectInstanceID id); void run(bool resume); - bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector & slot); + bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector & arts); void spawnWanderingMonsters(CreatureID creatureID); // Check for victory and loss conditions diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index 2301208e9..1120d7494 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -141,7 +141,8 @@ void ApplyGhNetPackVisitor::visitExchangeArtifacts(ExchangeArtifacts & pack) void ApplyGhNetPackVisitor::visitBulkExchangeArtifacts(BulkExchangeArtifacts & pack) { - gh.throwIfWrongOwner(&pack, pack.srcHero); + if(gh.getObj(pack.srcHero)->ID != MapObjectID::ALTAR_OF_SACRIFICE) + gh.throwIfWrongOwner(&pack, pack.srcHero); if(pack.swap) gh.throwIfWrongOwner(&pack, pack.dstHero); result = gh.bulkMoveArtifacts(pack.srcHero, pack.dstHero, pack.swap, pack.equipped, pack.backpack); @@ -261,9 +262,9 @@ void ApplyGhNetPackVisitor::visitTradeOnMarketplace(TradeOnMarketplace & pack) } case EMarketMode::ARTIFACT_EXP: { - std::vector positions; - for(auto const & slot : pack.r1) - positions.push_back(slot.as()); + std::vector positions; + for(auto const & artInstId : pack.r1) + positions.push_back(artInstId.as()); result = gh.sacrificeArtifact(market, hero, positions); return;