1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00

sacrifice routine

This commit is contained in:
SoundSSGood 2024-01-16 21:54:00 +02:00
parent f66918ea14
commit c6ca6ad835
11 changed files with 90 additions and 84 deletions

View File

@ -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<std::shared_ptr<CComponent>> catapult(1, std::make_shared<CComponent>(ComponentType::ARTIFACT, ArtifactID(ArtifactID::CATAPULT)));
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult);
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312],
std::vector<std::shared_ptr<CComponent>>(1, std::make_shared<CComponent>(ComponentType::ARTIFACT, ArtifactID(ArtifactID::CATAPULT))));
return false;
}
if(isTrade)
{
if(!artPlace.getArt()->artType->isTradable())
{
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21],
std::vector<std::shared_ptr<CComponent>>(1, std::make_shared<CComponent>(ComponentType::ARTIFACT, artPlace.getArt()->getTypeId())));
return false;
}
}
return true;
};
@ -155,13 +164,11 @@ void CWindowWithArtifacts::clickPressedArtPlaceHero(CArtifactsOfHeroBase & artsI
if(isTransferAllowed)
artSetPtr->swapArtifacts(srcLoc, dstLoc);
}
else
{
if(artPlace.getArt())
else if(auto art = artPlace.getArt())
{
if(artSetPtr->getHero()->tempOwner == LOCPLINT->playerID)
{
if(checkSpecialArts(hero, artPlace))
if(checkSpecialArts(hero, artPlace, std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroAltar>> ? true : false))
artSetPtr->pickUpArtifact(artPlace);
}
else
@ -174,7 +181,6 @@ void CWindowWithArtifacts::clickPressedArtPlaceHero(CArtifactsOfHeroBase & artsI
}
}
}
}
if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroBackpack>>)
{
@ -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<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroAltar>> ||
std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMain>> ||
std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroKingdom>> ||
std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroBackpack>>)
@ -254,7 +261,6 @@ void CWindowWithArtifacts::showPopupArtPlaceHero(CArtifactsOfHeroBase & artsInst
}
// Altar window, Market window right click handler
else if constexpr(
std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroAltar>> ||
std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMarket>> ||
std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroQuickBackpack>>)
{

View File

@ -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<TradeItemSell> 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<TradeItemBuy>(), std::vector<ui32>(), 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<CArtifactsOfHeroAltar> 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<CTradeableItem> &
}
}
TExpType CAltarArtifacts::calcExpCost(const CArtifactInstance* art)
TExpType CAltarArtifacts::calcExpCost(const CArtifactInstance * art)
{
int dmp = 0;
int expOfArt = 0;

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -14,7 +14,7 @@
VCMI_LIB_NAMESPACE_BEGIN
using TradeItemSell = VariantIdentifier<GameResID, SlotID, ArtifactPosition, ArtifactInstanceID>;
using TradeItemSell = VariantIdentifier<GameResID, SlotID, ArtifactInstanceID>;
using TradeItemBuy = VariantIdentifier<GameResID, PlayerColor, ArtifactID, SecondarySkill>;
VCMI_LIB_NAMESPACE_END

View File

@ -3436,9 +3436,6 @@ bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2)
if(o1->ID == Obj::ALTAR_OF_SACRIFICE)
{
const auto visitingHero = getVisitingHero(o1);
const auto thisHero = static_cast<const CGHeroInstance*>(o2);
if(visitingHero == thisHero)
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<ArtifactPosition> & slot)
bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector<ArtifactInstanceID> & 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<const CGArtifactsAltar*>(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();

View File

@ -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<ArtifactPosition> & slot);
bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector<ArtifactInstanceID> & arts);
void spawnWanderingMonsters(CreatureID creatureID);
// Check for victory and loss conditions

View File

@ -141,6 +141,7 @@ void ApplyGhNetPackVisitor::visitExchangeArtifacts(ExchangeArtifacts & pack)
void ApplyGhNetPackVisitor::visitBulkExchangeArtifacts(BulkExchangeArtifacts & pack)
{
if(gh.getObj(pack.srcHero)->ID != MapObjectID::ALTAR_OF_SACRIFICE)
gh.throwIfWrongOwner(&pack, pack.srcHero);
if(pack.swap)
gh.throwIfWrongOwner(&pack, pack.dstHero);
@ -261,9 +262,9 @@ void ApplyGhNetPackVisitor::visitTradeOnMarketplace(TradeOnMarketplace & pack)
}
case EMarketMode::ARTIFACT_EXP:
{
std::vector<ArtifactPosition> positions;
for(auto const & slot : pack.r1)
positions.push_back(slot.as<ArtifactPosition>());
std::vector<ArtifactInstanceID> positions;
for(auto const & artInstId : pack.r1)
positions.push_back(artInstId.as<ArtifactInstanceID>());
result = gh.sacrificeArtifact(market, hero, positions);
return;