1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Merge pull request #1112 from SoundSSGood/bulk-move-swap-artifacts-rev2

Bulk move and swap artifacts rev2
This commit is contained in:
Andrii Danylchenko
2022-11-14 08:31:48 +02:00
committed by GitHub
17 changed files with 425 additions and 178 deletions

View File

@@ -181,6 +181,12 @@ bool CCallback::assembleArtifacts (const CGHeroInstance * hero, ArtifactPosition
return true;
}
void CCallback::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap)
{
BulkExchangeArtifacts bma(srcHero, dstHero, swap);
sendRequest(&bma);
}
bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
{
if(town->tempOwner!=player)

View File

@@ -94,6 +94,10 @@ public:
virtual int bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany = 1) = 0;
virtual int bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot) = 0;
virtual int bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot) = 0;
// Moves all artifacts from one hero to another
virtual void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap) = 0;
};
class CBattleCallback : public IBattleCallback, public CPlayerBattleCallback
@@ -151,6 +155,7 @@ public:
bool dismissHero(const CGHeroInstance * hero) override;
bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2) override;
bool assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo) override;
void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap) override;
bool buildBuilding(const CGTownInstance *town, BuildingID buildingID) override;
void recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1) override;
bool dismissCreature(const CArmedInstance *obj, SlotID stackPos) override;

View File

@@ -2591,6 +2591,12 @@ void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const Artifact
if (artWin)
artWin->artifactMoved(src, dst);
}
if(!GH.objsToBlit.empty())
GH.objsToBlit.back()->redraw();
}
void CPlayerInterface::artifactPossibleAssembling(const ArtifactLocation & dst)
{
askToAssembleArtifact(dst);
}

View File

@@ -133,6 +133,7 @@ public:
void artifactRemoved(const ArtifactLocation &al) override;
void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst) override;
void artifactAssembled(const ArtifactLocation &al) override;
void artifactPossibleAssembling(const ArtifactLocation & dst) override;
void artifactDisassembled(const ArtifactLocation &al) override;
void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;

View File

@@ -275,8 +275,31 @@ void EraseArtifact::applyCl(CClient *cl)
void MoveArtifact::applyCl(CClient *cl)
{
callInterfaceIfPresent(cl, src.owningPlayer(), &IGameEventsReceiver::artifactMoved, src, dst);
callInterfaceIfPresent(cl, src.owningPlayer(), &IGameEventsReceiver::artifactPossibleAssembling, dst);
if(src.owningPlayer() != dst.owningPlayer())
{
callInterfaceIfPresent(cl, dst.owningPlayer(), &IGameEventsReceiver::artifactMoved, src, dst);
callInterfaceIfPresent(cl, dst.owningPlayer(), &IGameEventsReceiver::artifactPossibleAssembling, dst);
}
}
void BulkMoveArtifacts::applyCl(CClient * cl)
{
auto applyMove = [this, cl](std::vector<LinkedSlots> & artsPack) -> void
{
for(auto & slotToMove : artsPack)
{
auto srcLoc = ArtifactLocation(srcArtHolder, slotToMove.srcPos);
auto dstLoc = ArtifactLocation(dstArtHolder, slotToMove.dstPos);
callInterfaceIfPresent(cl, srcLoc.owningPlayer(), &IGameEventsReceiver::artifactMoved, srcLoc, dstLoc);
if(srcLoc.owningPlayer() != dstLoc.owningPlayer())
callInterfaceIfPresent(cl, dstLoc.owningPlayer(), &IGameEventsReceiver::artifactMoved, srcLoc, dstLoc);
}
};
applyMove(artsPack0);
if(swap)
applyMove(artsPack1);
}
void AssembledArtifact::applyCl(CClient *cl)

View File

@@ -872,41 +872,6 @@ std::function<void()> CExchangeController::onMoveArmyToRight()
return [&]() { moveArmy(true); };
}
void CExchangeController::swapArtifacts(ArtifactPosition slot)
{
bool leftHasArt = !left->isPositionFree(slot);
bool rightHasArt = !right->isPositionFree(slot);
if(!leftHasArt && !rightHasArt)
return;
ArtifactLocation leftLocation = ArtifactLocation(left, slot);
ArtifactLocation rightLocation = ArtifactLocation(right, slot);
if(leftHasArt && !left->artifactsWorn.at(slot).artifact->canBePutAt(rightLocation, true))
return;
if(rightHasArt && !right->artifactsWorn.at(slot).artifact->canBePutAt(leftLocation, true))
return;
if(leftHasArt)
{
if(rightHasArt)
{
auto art = right->getArt(slot);
cb->swapArtifacts(leftLocation, rightLocation);
cb->swapArtifacts(ArtifactLocation(right, right->getArtPos(art)), leftLocation);
}
else
cb->swapArtifacts(leftLocation, rightLocation);
}
else
{
cb->swapArtifacts(rightLocation, leftLocation);
}
}
std::vector<CArtifactInstance *> getBackpackArts(const CGHeroInstance * hero)
{
std::vector<CArtifactInstance *> result;
@@ -919,92 +884,13 @@ std::vector<CArtifactInstance *> getBackpackArts(const CGHeroInstance * hero)
return result;
}
const std::vector<ArtifactPosition> unmovablePositions = {ArtifactPosition::SPELLBOOK, ArtifactPosition::MACH4};
bool isArtRemovable(const std::pair<ArtifactPosition, ArtSlotInfo> & slot)
{
return slot.second.artifact
&& !slot.second.locked
&& !vstd::contains(unmovablePositions, slot.first);
}
// Puts all composite arts to backpack and returns their previous location
std::vector<HeroArtifact> CExchangeController::moveCompositeArtsToBackpack()
{
std::vector<const CGHeroInstance *> sides = {left, right};
std::vector<HeroArtifact> artPositions;
for(auto hero : sides)
{
for(int i = ArtifactPosition::HEAD; i < ArtifactPosition::AFTER_LAST; i++)
{
auto artPosition = ArtifactPosition(i);
auto art = hero->getArt(artPosition);
if(art && art->canBeDisassembled())
{
cb->swapArtifacts(
ArtifactLocation(hero, artPosition),
ArtifactLocation(hero, ArtifactPosition(GameConstants::BACKPACK_START)));
artPositions.push_back(HeroArtifact(hero, art, artPosition));
}
}
}
return artPositions;
}
void CExchangeController::swapArtifacts()
{
for(int i = ArtifactPosition::HEAD; i < ArtifactPosition::AFTER_LAST; i++)
{
if(vstd::contains(unmovablePositions, i))
continue;
swapArtifacts(ArtifactPosition(i));
}
auto leftHeroBackpack = getBackpackArts(left);
auto rightHeroBackpack = getBackpackArts(right);
for(auto leftArt : leftHeroBackpack)
{
cb->swapArtifacts(
ArtifactLocation(left, left->getArtPos(leftArt)),
ArtifactLocation(right, ArtifactPosition(GameConstants::BACKPACK_START)));
}
for(auto rightArt : rightHeroBackpack)
{
cb->swapArtifacts(
ArtifactLocation(right, right->getArtPos(rightArt)),
ArtifactLocation(left, ArtifactPosition(GameConstants::BACKPACK_START)));
}
}
std::function<void()> CExchangeController::onSwapArtifacts()
{
return [&]()
{
GsThread::run([=]
{
// it is not possible directly exchange composite artifacts like Angelic Alliance and Armor of Damned
auto compositeArtLocations = moveCompositeArtsToBackpack();
swapArtifacts();
for(HeroArtifact artLocation : compositeArtLocations)
{
auto target = artLocation.hero == left ? right : left;
auto currentPos = target->getArtPos(artLocation.artifact);
cb->swapArtifacts(
ArtifactLocation(target, currentPos),
ArtifactLocation(target, artLocation.artPosition));
}
view->redraw();
cb->bulkMoveArtifacts(left->id, right->id, true);
});
};
}
@@ -1160,20 +1046,8 @@ void CExchangeController::moveArtifacts(bool leftToRight)
}
GsThread::run([=]
{
while(vstd::contains_if(source->artifactsWorn, isArtRemovable))
{
auto art = std::find_if(source->artifactsWorn.begin(), source->artifactsWorn.end(), isArtRemovable);
moveArtifact(source, target, art->first);
}
while(!source->artifactsInBackpack.empty())
{
moveArtifact(source, target, source->getArtPos(source->artifactsInBackpack.begin()->artifact));
}
view->redraw();
{
cb->bulkMoveArtifacts(source->id, target->id, false);
});
}
@@ -1182,26 +1056,11 @@ void CExchangeController::moveArtifact(
const CGHeroInstance * target,
ArtifactPosition srcPosition)
{
auto artifact = source->getArt(srcPosition);
auto srcLocation = ArtifactLocation(source, srcPosition);
auto dstLocation = ArtifactLocation(target,
ArtifactUtils::getArtifactDstPosition(source->getArt(srcPosition), target, target->bearerType()));
for(auto slot : artifact->artType->possibleSlots.at(target->bearerType()))
{
auto existingArtifact = target->getArt(slot);
auto existingArtInfo = target->getSlot(slot);
ArtifactLocation destLocation(target, slot);
if(!existingArtifact
&& (!existingArtInfo || !existingArtInfo->locked)
&& artifact->canBePutAt(destLocation))
{
cb->swapArtifacts(srcLocation, ArtifactLocation(target, slot));
return;
}
}
cb->swapArtifacts(srcLocation, ArtifactLocation(target, ArtifactPosition(GameConstants::BACKPACK_START)));
cb->swapArtifacts(srcLocation, dstLocation);
}
CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID queryID)

View File

@@ -324,9 +324,6 @@ private:
void moveArtifacts(bool leftToRight);
void moveArtifact(const CGHeroInstance * source, const CGHeroInstance * target, ArtifactPosition srcPosition);
void moveStack(const CGHeroInstance * source, const CGHeroInstance * target, SlotID sourceSlot);
void swapArtifacts(ArtifactPosition artPosition);
std::vector<HeroArtifact> moveCompositeArtsToBackpack();
void swapArtifacts();
};
class CExchangeWindow : public CStatusbarWindow, public CGarrisonHolder, public CWindowWithArtifacts

View File

@@ -1457,4 +1457,83 @@ void CArtifactSet::serializeJsonSlot(JsonSerializeFormat & handler, const Artifa
}
}
CArtifactFittingSet::CArtifactFittingSet(ArtBearer::ArtBearer Bearer)
{
this->Bearer = Bearer;
}
void CArtifactFittingSet::setNewArtSlot(ArtifactPosition slot, CArtifactInstance * art, bool locked)
{
ArtSlotInfo & asi = retrieveNewArtSlot(slot);
asi.artifact = art;
asi.locked = locked;
}
void CArtifactFittingSet::putArtifact(ArtifactPosition pos, CArtifactInstance * art)
{
if(art && art->canBeDisassembled() && (pos < ArtifactPosition::AFTER_LAST))
{
for(auto & part : dynamic_cast<CCombinedArtifactInstance*>(art)->constituentsInfo)
{
// For the ArtFittingSet is no needed to do figureMainConstituent, just lock slots
this->setNewArtSlot(part.art->firstAvailableSlot(this), part.art, true);
}
}
else
{
this->setNewArtSlot(pos, art, false);
}
}
ArtBearer::ArtBearer CArtifactFittingSet::bearerType() const
{
return this->Bearer;
}
DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtifactDstPosition( const CArtifactInstance * artifact,
const CArtifactSet * target,
ArtBearer::ArtBearer bearer)
{
for(auto slot : artifact->artType->possibleSlots.at(bearer))
{
auto existingArtifact = target->getArt(slot);
auto existingArtInfo = target->getSlot(slot);
if(!existingArtifact
&& (!existingArtInfo || !existingArtInfo->locked)
&& artifact->canBePutAt(target, slot))
{
return slot;
}
}
return ArtifactPosition(GameConstants::BACKPACK_START);
}
DLL_LINKAGE std::vector<ArtifactPosition> ArtifactUtils::unmovablePositions()
{
return { ArtifactPosition::SPELLBOOK, ArtifactPosition::MACH4 };
}
DLL_LINKAGE bool ArtifactUtils::isArtRemovable(const std::pair<ArtifactPosition, ArtSlotInfo> & slot)
{
return slot.second.artifact
&& !slot.second.locked
&& !vstd::contains(unmovablePositions(), slot.first);
}
DLL_LINKAGE bool ArtifactUtils::checkSpellbookIsNeeded(const CGHeroInstance * heroPtr, ArtifactID artID, ArtifactPosition slot)
{
// TODO what'll happen if Titan's thunder is equipped by pickin git up or the start of game?
// Titan's Thunder creates new spellbook on equip
if(artID == ArtifactID::TITANS_THUNDER && slot == ArtifactPosition::RIGHT_HAND)
{
if(heroPtr)
{
if(heroPtr && !heroPtr->hasSpellbook())
return true;
}
}
return false;
}
VCMI_LIB_NAMESPACE_END

View File

@@ -303,8 +303,9 @@ struct DLL_LINKAGE ArtSlotInfo
ui8 locked; //if locked, then artifact points to the combined artifact
ArtSlotInfo() : locked(false) {}
const CArtifactInstance * getArt() const;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler & h, const int version)
{
h & artifact;
h & locked;
@@ -363,4 +364,28 @@ private:
void serializeJsonSlot(JsonSerializeFormat & handler, const ArtifactPosition & slot, CMap * map);//normal slots
};
// Used to try on artifacts before the claimed changes have been applied
class DLL_LINKAGE CArtifactFittingSet : public CArtifactSet
{
public:
CArtifactFittingSet(ArtBearer::ArtBearer Bearer);
void setNewArtSlot(ArtifactPosition slot, CArtifactInstance * art, bool locked);
void putArtifact(ArtifactPosition pos, CArtifactInstance * art) override;
ArtBearer::ArtBearer bearerType() const override;
protected:
ArtBearer::ArtBearer Bearer;
};
namespace ArtifactUtils
{
// Calculates where an artifact gets placed when it gets transferred from one hero to another.
DLL_LINKAGE ArtifactPosition getArtifactDstPosition( const CArtifactInstance * artifact,
const CArtifactSet * target,
ArtBearer::ArtBearer bearer);
DLL_LINKAGE std::vector<ArtifactPosition> unmovablePositions(); // TODO: Make this constexpr when the toolset is upgraded
DLL_LINKAGE bool isArtRemovable(const std::pair<ArtifactPosition, ArtSlotInfo> & slot);
DLL_LINKAGE bool checkSpellbookIsNeeded(const CGHeroInstance * heroPtr, ArtifactID artID, ArtifactPosition slot);
}
VCMI_LIB_NAMESPACE_END

View File

@@ -91,6 +91,7 @@ public:
virtual void artifactAssembled(const ArtifactLocation &al){};
virtual void artifactDisassembled(const ArtifactLocation &al){};
virtual void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst){};
virtual void artifactPossibleAssembling(const ArtifactLocation & dst) {};
virtual void heroVisit(const CGHeroInstance *visitor, const CGObjectInstance *visitedObj, bool start){};
virtual void heroCreated(const CGHeroInstance*){};

View File

@@ -994,18 +994,64 @@ struct EraseArtifact : CArtifactOperationPack
struct MoveArtifact : CArtifactOperationPack
{
MoveArtifact() {}
MoveArtifact(ArtifactLocation * src, ArtifactLocation * dst)
: src(*src), dst(*dst) {}
ArtifactLocation src, dst;
void applyCl(CClient *cl);
DLL_LINKAGE void applyGs(CGameState *gs);
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler & h, const int version)
{
h & src;
h & dst;
}
};
struct BulkMoveArtifacts : CArtifactOperationPack
{
struct LinkedSlots
{
ArtifactPosition srcPos;
ArtifactPosition dstPos;
LinkedSlots() {}
LinkedSlots(ArtifactPosition srcPos, ArtifactPosition dstPos)
: srcPos(srcPos), dstPos(dstPos) {}
template <typename Handler> void serialize(Handler & h, const int version)
{
h & srcPos;
h & dstPos;
}
};
TArtHolder srcArtHolder;
TArtHolder dstArtHolder;
BulkMoveArtifacts()
: swap(false) {}
BulkMoveArtifacts(TArtHolder srcArtHolder, TArtHolder dstArtHolder, bool swap)
: srcArtHolder(srcArtHolder), dstArtHolder(dstArtHolder), swap(swap) {}
void applyCl(CClient * cl);
DLL_LINKAGE void applyGs(CGameState * gs);
std::vector<LinkedSlots> artsPack0;
std::vector<LinkedSlots> artsPack1;
bool swap;
CArtifactSet * getSrcHolderArtSet();
CArtifactSet * getDstHolderArtSet();
template <typename Handler> void serialize(Handler & h, const int version)
{
h & artsPack0;
h & artsPack1;
h & srcArtHolder;
h & dstArtHolder;
h & swap;
}
};
struct AssembledArtifact : CArtifactOperationPack
{
ArtifactLocation al; //where assembly will be put
@@ -2197,6 +2243,27 @@ struct ExchangeArtifacts : public CPackForServer
}
};
struct BulkExchangeArtifacts : public CPackForServer
{
ObjectInstanceID srcHero;
ObjectInstanceID dstHero;
bool swap;
BulkExchangeArtifacts()
: swap(false) {}
BulkExchangeArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap)
: srcHero(srcHero), dstHero(dstHero), swap(swap) {}
bool applyGh(CGameHandler * gh);
template <typename Handler> void serialize(Handler & h, const int version)
{
h & static_cast<CPackForServer&>(*this);
h & srcHero;
h & dstHero;
h & swap;
}
};
struct AssembleArtifacts : public CPackForServer
{
AssembleArtifacts():assemble(false){};

View File

@@ -845,18 +845,11 @@ DLL_LINKAGE CBonusSystemNode *ArtifactLocation::getHolderNode()
DLL_LINKAGE const CArtifactInstance *ArtifactLocation::getArt() const
{
const ArtSlotInfo *s = getSlot();
if(s && s->artifact)
{
if(!s->locked)
return s->artifact;
else
{
logNetwork->warn("ArtifactLocation::getArt: This location is locked!");
return nullptr;
}
}
return nullptr;
auto s = getSlot();
if(s)
return s->getArt();
else
return nullptr;
}
DLL_LINKAGE const CArtifactSet * ArtifactLocation::getHolderArtSet() const
@@ -1093,24 +1086,82 @@ DLL_LINKAGE void EraseArtifact::applyGs(CGameState *gs)
al.removeArtifact();
}
DLL_LINKAGE void MoveArtifact::applyGs(CGameState *gs)
DLL_LINKAGE void MoveArtifact::applyGs(CGameState * gs)
{
CArtifactInstance *a = src.getArt();
CArtifactInstance * art = src.getArt();
if(dst.slot < GameConstants::BACKPACK_START)
assert(!dst.getArt());
a->move(src, dst);
art->move(src, dst);
}
//TODO what'll happen if Titan's thunder is equipped by pickin git up or the start of game?
if (a->artType->id == ArtifactID::TITANS_THUNDER && dst.slot == ArtifactPosition::RIGHT_HAND) //Titan's Thunder creates new spellbook on equip
DLL_LINKAGE void BulkMoveArtifacts::applyGs(CGameState * gs)
{
enum class EBulkArtsOp
{
auto hPtr = boost::get<ConstTransitivePtr<CGHeroInstance> >(&dst.artHolder);
if(hPtr)
BULK_MOVE,
BULK_REMOVE,
BULK_PUT
};
auto bulkArtsOperation = [this](std::vector<LinkedSlots> & artsPack,
CArtifactSet * artSet, EBulkArtsOp operation) -> void
{
int numBackpackArtifactsMoved = 0;
for(auto & slot : artsPack)
{
CGHeroInstance *h = *hPtr;
if(h && !h->hasSpellbook())
gs->giveHeroArtifact(h, ArtifactID::SPELLBOOK);
// When an object gets removed from the backpack, the backpack shrinks
// so all the following indices will be affected. Thus, we need to update
// the subsequent artifact slots to account for that
auto srcPos = slot.srcPos;
if((srcPos >= GameConstants::BACKPACK_START) && (operation != EBulkArtsOp::BULK_PUT))
{
srcPos = ArtifactPosition(srcPos.num - numBackpackArtifactsMoved);
}
auto slotInfo = artSet->getSlot(srcPos);
assert(slotInfo);
auto art = const_cast<CArtifactInstance*>(slotInfo->getArt());
assert(art);
switch(operation)
{
case EBulkArtsOp::BULK_MOVE:
const_cast<CArtifactInstance*>(art)->move(
ArtifactLocation(srcArtHolder, srcPos), ArtifactLocation(dstArtHolder, slot.dstPos));
break;
case EBulkArtsOp::BULK_REMOVE:
art->removeFrom(ArtifactLocation(dstArtHolder, srcPos));
break;
case EBulkArtsOp::BULK_PUT:
art->putAt(ArtifactLocation(srcArtHolder, slot.dstPos));
break;
default:
break;
}
if(srcPos >= GameConstants::BACKPACK_START)
{
numBackpackArtifactsMoved++;
}
}
};
if(swap)
{
// Swap
auto leftSet = getSrcHolderArtSet();
auto rightSet = getDstHolderArtSet();
CArtifactFittingSet artFittingSet(leftSet->bearerType());
artFittingSet.artifactsWorn = rightSet->artifactsWorn;
artFittingSet.artifactsInBackpack = rightSet->artifactsInBackpack;
bulkArtsOperation(artsPack1, rightSet, EBulkArtsOp::BULK_REMOVE);
bulkArtsOperation(artsPack0, leftSet, EBulkArtsOp::BULK_MOVE);
bulkArtsOperation(artsPack1, &artFittingSet, EBulkArtsOp::BULK_PUT);
}
else
{
bulkArtsOperation(artsPack0, getSrcHolderArtSet(), EBulkArtsOp::BULK_MOVE);
}
}
@@ -1712,4 +1763,24 @@ DLL_LINKAGE void EntitiesChanged::applyGs(CGameState * gs)
gs->updateEntity(change.metatype, change.entityIndex, change.data);
}
const CArtifactInstance * ArtSlotInfo::getArt() const
{
if(locked)
{
logNetwork->warn("ArtifactLocation::getArt: This location is locked!");
return nullptr;
}
return artifact;
}
CArtifactSet * BulkMoveArtifacts::getSrcHolderArtSet()
{
return boost::apply_visitor(GetBase<CArtifactSet>(), srcArtHolder);
}
CArtifactSet * BulkMoveArtifacts::getDstHolderArtSet()
{
return boost::apply_visitor(GetBase<CArtifactSet>(), dstArtHolder);
}
VCMI_LIB_NAMESPACE_END

View File

@@ -319,6 +319,7 @@ void registerTypesClientPacks2(Serializer &s)
s.template registerType<CArtifactOperationPack, MoveArtifact>();
s.template registerType<CArtifactOperationPack, AssembledArtifact>();
s.template registerType<CArtifactOperationPack, DisassembledArtifact>();
s.template registerType<CArtifactOperationPack, BulkMoveArtifacts>();
s.template registerType<CPackForClient, SaveGameClient>();
s.template registerType<CPackForClient, PlayerMessageClient>();
@@ -358,6 +359,7 @@ void registerTypesServerPacks(Serializer &s)
s.template registerType<CPackForServer, BulkMergeStacks>();
s.template registerType<CPackForServer, BulkSmartSplitStack>();
s.template registerType<CPackForServer, BulkMoveArmy>();
s.template registerType<CPackForServer, BulkExchangeArtifacts>();
}
template<typename Serializer>

View File

@@ -3916,10 +3916,101 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat
moveArtifact(dst, ArtifactLocation(dst.artHolder, ArtifactPosition(
(si32)dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START)));
}
auto hero = boost::get<ConstTransitivePtr<CGHeroInstance>>(dst.artHolder);
if(ArtifactUtils::checkSpellbookIsNeeded(hero, srcArtifact->artType->id, dst.slot))
giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
MoveArtifact ma;
ma.src = src;
ma.dst = dst;
MoveArtifact ma(&src, &dst);
sendAndApply(&ma);
return true;
}
bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap)
{
// Make sure exchange is even possible between the two heroes.
if(!isAllowedExchange(srcHero, dstHero))
COMPLAIN_RET("That heroes cannot make any exchange!");
auto psrcHero = getHero(srcHero);
auto pdstHero = getHero(dstHero);
if((!psrcHero) || (!pdstHero))
COMPLAIN_RET("bulkMoveArtifacts: wrong hero's ID");
BulkMoveArtifacts ma(static_cast<ConstTransitivePtr<CGHeroInstance>>(psrcHero),
static_cast<ConstTransitivePtr<CGHeroInstance>>(pdstHero), swap);
auto & slotsSrcDst = ma.artsPack0;
auto & slotsDstSrc = ma.artsPack1;
if(swap)
{
auto moveArtsWorn = [this](const CGHeroInstance * srcHero, const CGHeroInstance * dstHero,
std::vector<BulkMoveArtifacts::LinkedSlots> & slots) -> void
{
for(auto & artifact : srcHero->artifactsWorn)
{
if(artifact.second.locked)
continue;
if(!ArtifactUtils::isArtRemovable(artifact))
continue;
slots.push_back(BulkMoveArtifacts::LinkedSlots(artifact.first, artifact.first));
auto art = artifact.second.getArt();
assert(art);
if(ArtifactUtils::checkSpellbookIsNeeded(dstHero, art->artType->id, artifact.first))
giveHeroNewArtifact(dstHero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
}
};
auto moveArtsInBackpack = [](const CGHeroInstance * pHero,
std::vector<BulkMoveArtifacts::LinkedSlots> & slots) -> void
{
for(auto & slotInfo : pHero->artifactsInBackpack)
{
auto slot = pHero->getArtPos(slotInfo.artifact);
slots.push_back(BulkMoveArtifacts::LinkedSlots(slot, slot));
}
};
// Move over artifacts that are worn srcHero -> dstHero
moveArtsWorn(psrcHero, pdstHero, slotsSrcDst);
// Move over artifacts that are worn dstHero -> srcHero
moveArtsWorn(pdstHero, psrcHero, slotsDstSrc);
// Move over artifacts that are in backpack srcHero -> dstHero
moveArtsInBackpack(psrcHero, slotsSrcDst);
// Move over artifacts that are in backpack dstHero -> srcHero
moveArtsInBackpack(pdstHero, slotsDstSrc);
}
else
{
// Temporary fitting set for artifacts. Used to select available slots before sending data.
CArtifactFittingSet artFittingSet(pdstHero->bearerType());
artFittingSet.artifactsInBackpack = pdstHero->artifactsInBackpack;
artFittingSet.artifactsWorn = pdstHero->artifactsWorn;
auto moveArtifact = [this, &artFittingSet, &slotsSrcDst](const CArtifactInstance * artifact,
ArtifactPosition srcSlot, const CGHeroInstance * pdstHero) -> void
{
assert(artifact);
auto dstSlot = ArtifactUtils::getArtifactDstPosition(artifact, &artFittingSet, pdstHero->bearerType());
artFittingSet.putArtifact(dstSlot, static_cast<ConstTransitivePtr<CArtifactInstance>>(artifact));
slotsSrcDst.push_back(BulkMoveArtifacts::LinkedSlots(srcSlot, dstSlot));
if(ArtifactUtils::checkSpellbookIsNeeded(pdstHero, artifact->artType->id, dstSlot))
giveHeroNewArtifact(pdstHero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
};
// Move over artifacts that are worn
for(auto & artInfo : psrcHero->artifactsWorn)
{
if(ArtifactUtils::isArtRemovable(artInfo))
{
moveArtifact(psrcHero->getArt(artInfo.first), artInfo.first, pdstHero);
}
}
// Move over artifacts that are in backpack
for(auto & slotInfo : psrcHero->artifactsInBackpack)
{
moveArtifact(psrcHero->getArt(psrcHero->getArtPos(slotInfo.artifact)), psrcHero->getArtPos(slotInfo.artifact), pdstHero);
}
}
sendAndApply(&ma);
return true;
}
@@ -3947,6 +4038,9 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition
COMPLAIN_RET("assembleArtifacts: Artifact being attempted to assemble is not a combined artifacts!");
if (!vstd::contains(destArtifact->assemblyPossibilities(hero), combinedArt))
COMPLAIN_RET("assembleArtifacts: It's impossible to assemble requested artifact!");
if(ArtifactUtils::checkSpellbookIsNeeded(hero, assembleTo, artifactSlot))
giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
AssembledArtifact aa;
aa.al = ArtifactLocation(hero, artifactSlot);

View File

@@ -180,7 +180,8 @@ public:
void giveHeroArtifact(const CGHeroInstance *h, const CArtifactInstance *a, ArtifactPosition pos) override;
void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) override;
void removeArtifact(const ArtifactLocation &al) override;
bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) override;
bool moveArtifact(const ArtifactLocation & al1, const ArtifactLocation & al2) override;
bool bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap);
void synchronizeArtifactHandlerLists();
void showCompInfo(ShowInInfobox * comp) override;

View File

@@ -370,6 +370,9 @@ bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const
}
if(auto dismiss = dynamic_ptr_cast<DisbandCreature>(pack))
return !vstd::contains(ourIds, dismiss->id);
if(auto arts = dynamic_ptr_cast<BulkExchangeArtifacts>(pack))
return !vstd::contains(ourIds, arts->srcHero) || !vstd::contains(ourIds, arts->dstHero);
if(auto dismiss = dynamic_ptr_cast<AssembleArtifacts>(pack))
return !vstd::contains(ourIds, dismiss->heroID);

View File

@@ -180,6 +180,13 @@ bool ExchangeArtifacts::applyGh(CGameHandler * gh)
return gh->moveArtifact(src, dst);
}
bool BulkExchangeArtifacts::applyGh(CGameHandler * gh)
{
const CGHeroInstance * pSrcHero = gh->getHero(srcHero);
throwOnWrongPlayer(gh, pSrcHero->getOwner());
return gh->bulkMoveArtifacts(srcHero, dstHero, swap);
}
bool AssembleArtifacts::applyGh(CGameHandler * gh)
{
throwOnWrongOwner(gh, heroID);