Browse Source

Backpack limit part1 (move, disassemble)

SoundSSGood 2 years ago
parent
commit
96e8a1f21c

+ 1 - 1
client/CPlayerInterface.cpp

@@ -1893,7 +1893,7 @@ void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al)
 							 al.slot.num);
 			return;
 		}
-		CHeroArtPlace::askToAssemble(art, al.slot, hero);
+		CHeroArtPlace::askToAssemble(hero, al.slot);
 	}
 }
 

+ 54 - 35
client/widgets/CArtifactHolder.cpp

@@ -101,9 +101,9 @@ void CHeroArtPlace::selectSlot(bool on)
 void CHeroArtPlace::clickLeft(tribool down, bool previousState)
 {
 	//LRClickableAreaWTextComp::clickLeft(down);
-	bool inBackpack = slotID >= GameConstants::BACKPACK_START,
-		srcInBackpack = ourOwner->commonInfo->src.slotID >= GameConstants::BACKPACK_START,
-		srcInSameHero = ourOwner->commonInfo->src.AOH == ourOwner;
+	bool inBackpack = ArtifactUtils::isSlotBackpack(slotID);
+	bool srcInBackpack = ArtifactUtils::isSlotBackpack(ourOwner->commonInfo->src.slotID);
+	bool srcInSameHero = ourOwner->commonInfo->src.AOH == ourOwner;
 
 	if(ourOwner->highlightModeCallback && ourArt)
 	{
@@ -171,16 +171,16 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState)
 					else
 					{
 						setMeAsDest();
-						vstd::amin(ourOwner->commonInfo->dst.slotID, ArtifactPosition(
-							(si32)ourOwner->curHero->artifactsInBackpack.size() + GameConstants::BACKPACK_START));
-						if(srcInBackpack && srcInSameHero)
+						vstd::amin(ourOwner->commonInfo->dst.slotID, ourOwner->curHero->artifactsInBackpack.size() + GameConstants::BACKPACK_START);
+						if(ArtifactUtils::isBackpackFreeSlots(ourOwner->curHero))
 						{
-							if(!ourArt								//cannot move from backpack to AFTER backpack -> combined with vstd::amin above it will guarantee that dest is at most the last artifact
-								|| ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted
-								vstd::advance(ourOwner->commonInfo->dst.slotID, -1);
+							if(!srcInSameHero || ourOwner->commonInfo->dst.slotID != ourOwner->commonInfo->src.slotID)
+								ourOwner->realizeCurrentTransaction();
+						}
+						else
+						{
+							LOCPLINT->showInfoDialog("limit");
 						}
-						if(!srcInSameHero || ourOwner->commonInfo->dst.slotID != ourOwner->commonInfo->src.slotID)
-							ourOwner->realizeCurrentTransaction();
 					}
 				}
 			}
@@ -195,13 +195,12 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState)
 	}
 }
 
-bool CHeroArtPlace::askToAssemble(const CArtifactInstance *art, ArtifactPosition slot,
-                              const CGHeroInstance *hero)
+bool CHeroArtPlace::askToAssemble(const CGHeroInstance * hero, ArtifactPosition slot)
 {
-	assert(art);
 	assert(hero);
-	bool assembleEqipped = !ArtifactUtils::isSlotBackpack(slot);
-	auto assemblyPossibilities = art->assemblyPossibilities(hero, assembleEqipped);
+	const auto art = hero->getArt(slot);
+	assert(art);
+	auto assemblyPossibilities = art->assemblyPossibilities(hero, ArtifactUtils::isSlotEquipment(slot));
 
 	// If the artifact can be assembled, display dialog.
 	for(const auto * combination : assemblyPossibilities)
@@ -218,6 +217,26 @@ bool CHeroArtPlace::askToAssemble(const CArtifactInstance *art, ArtifactPosition
 	return false;
 }
 
+bool CHeroArtPlace::askToDisassemble(const CGHeroInstance * hero, ArtifactPosition slot)
+{
+	assert(hero);
+	const auto art = hero->getArt(slot);
+	assert(art);
+
+	if(art->canBeDisassembled())
+	{
+		if(ArtifactUtils::isSlotBackpack(slot) && !ArtifactUtils::isBackpackFreeSlots(hero, art->artType->constituents->size() - 1))
+			return false;
+		
+		LOCPLINT->showArtifactAssemblyDialog(
+			art->artType,
+			nullptr,
+			std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, false, ArtifactID()));
+		return true;
+	}
+	return false;
+}
+
 void CHeroArtPlace::clickRight(tribool down, bool previousState)
 {
 	if(ourArt && down && !locked && text.size() && !picked)  //if there is no description or it's a lock, do nothing ;]
@@ -225,18 +244,12 @@ void CHeroArtPlace::clickRight(tribool down, bool previousState)
 		if(ourOwner->allowedAssembling)
 		{
 			// If the artifact can be assembled, display dialog.
-			if(askToAssemble(ourArt, slotID, ourOwner->curHero))
+			if(askToAssemble(ourOwner->curHero, slotID))
 			{
 				return;
 			}
-
-			// Otherwise if the artifact can be diasassembled, display dialog.
-			if(ourArt->canBeDisassembled())
+			if(askToDisassemble(ourOwner->curHero, slotID))
 			{
-				LOCPLINT->showArtifactAssemblyDialog(
-					ourArt->artType,
-					nullptr,
-					std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), ourOwner->curHero, slotID, false, ArtifactID()));
 				return;
 			}
 		}
@@ -634,10 +647,16 @@ CArtifactsOfHero::~CArtifactsOfHero()
 	{
 		auto artPlace = getArtPlace(
 			ArtifactUtils::getArtifactDstPosition(curHero->artifactsTransitionPos.begin()->artifact->artType->getId(), curHero));
-		assert(artPlace);
-		assert(artPlace->ourOwner);
-		artPlace->setMeAsDest();
-		artPlace->ourOwner->realizeCurrentTransaction();
+		if(artPlace)
+		{
+			assert(artPlace->ourOwner);
+			artPlace->setMeAsDest();
+			artPlace->ourOwner->realizeCurrentTransaction();
+		}
+		else
+		{
+			//TODO remove artifact
+		}
 	}
 }
 
@@ -765,11 +784,7 @@ void CArtifactsOfHero::artifactRemoved(const ArtifactLocation &al)
 
 CArtifactsOfHero::ArtPlacePtr CArtifactsOfHero::getArtPlace(ArtifactPosition slot)
 {
-	if(slot == ArtifactPosition::TRANSITION_POS)
-	{
-		return nullptr;
-	}
-	if(slot < GameConstants::BACKPACK_START)
+	if(ArtifactUtils::isSlotEquipment(slot))
 	{
 		if(artWorn.find(slot) == artWorn.end())
 		{
@@ -777,15 +792,19 @@ CArtifactsOfHero::ArtPlacePtr CArtifactsOfHero::getArtPlace(ArtifactPosition slo
 			return nullptr;
 		}
 
-		return artWorn[ArtifactPosition(slot)];
+		return artWorn[slot];
 	}
-	else
+	if(ArtifactUtils::isSlotBackpack(slot))
 	{
 		for(ArtPlacePtr ap : backpack)
 			if(ap->slotID == slot)
 				return ap;
 		return nullptr;
 	}
+	else
+	{
+		return nullptr;
+	}
 }
 
 void CArtifactsOfHero::artifactUpdateSlots(const ArtifactLocation & al)

+ 2 - 2
client/widgets/CArtifactHolder.h

@@ -95,8 +95,8 @@ public:
 
 	void setMeAsDest(bool backpackAsVoid = true);
 	void setArtifact(const CArtifactInstance *art) override;
-	static bool askToAssemble(const CArtifactInstance *art, ArtifactPosition slot,
-	                          const CGHeroInstance *hero);
+	static bool askToAssemble(const CGHeroInstance * hero, ArtifactPosition slot);
+	static bool askToDisassemble(const CGHeroInstance * hero, ArtifactPosition slot);
 };
 
 /// Contains artifacts of hero. Distincts which artifacts are worn or backpacked

+ 3 - 1
config/gameConfig.json

@@ -133,7 +133,9 @@
 			// if enabled, hero that wins a battle without any non-summoned troops left will retreat and become available in tavern instead of being lost
 			"retreatOnWinWithoutTroops" : true,
 			// Chances for a hero with default army to receive corresponding stack out of his predefined starting troops
-			"startingStackChances": [ 100, 88, 25]
+			"startingStackChances": [ 100, 88, 25],
+			// number of artifacts that can fit in a backpack. -1 is unlimited.
+			"backpackSize"		: -1
 		},
 
 		"towns":

+ 16 - 9
lib/CArtHandler.cpp

@@ -153,10 +153,9 @@ bool CArtifact::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, b
 	{
 		if(ArtifactUtils::isSlotBackpack(slot))
 		{
-			if(isBig())
+			if(isBig() || !ArtifactUtils::isBackpackFreeSlots(artSet))
 				return false;
 
-			//TODO backpack limit
 			return true;
 		}
 
@@ -1566,15 +1565,14 @@ DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtifactDstPosition(const Artifac
 	const auto * art = aid.toArtifact();
 	for(const auto & slot : art->possibleSlots.at(target->bearerType()))
 	{
-		const auto * existingArtInfo = target->getSlot(slot);
-
-		if((!existingArtInfo || !existingArtInfo->locked)
-			&& art->canBePutAt(target, slot))
-		{
+		if(art->canBePutAt(target, slot))
 			return slot;
-		}
 	}
-	return GameConstants::BACKPACK_START;
+	if(art->canBePutAt(target, GameConstants::BACKPACK_START))
+	{
+		return GameConstants::BACKPACK_START;
+	}
+	return ArtifactPosition::PRE_FIRST;
 }
 
 DLL_LINKAGE const std::vector<ArtifactPosition::EArtifactPosition> & ArtifactUtils::unmovableSlots()
@@ -1643,4 +1641,13 @@ DLL_LINKAGE bool ArtifactUtils::isSlotEquipment(const ArtifactPosition & slot)
 	return slot < GameConstants::BACKPACK_START && slot >= 0;
 }
 
+DLL_LINKAGE bool ArtifactUtils::isBackpackFreeSlots(const CArtifactSet * target, const size_t reqSlots)
+{
+	const auto backpackCap = VLC->settings()->getInteger(EGameSettings::HEROES_BACKPACK_CAP);
+	if(backpackCap < 0)
+		return true;
+	else
+		return target->artifactsInBackpack.size() + reqSlots <= backpackCap;
+}
+
 VCMI_LIB_NAMESPACE_END

+ 1 - 0
lib/CArtHandler.h

@@ -388,6 +388,7 @@ namespace ArtifactUtils
 	DLL_LINKAGE bool checkSpellbookIsNeeded(const CGHeroInstance * heroPtr, const ArtifactID & artID, const ArtifactPosition & slot);
 	DLL_LINKAGE bool isSlotBackpack(const ArtifactPosition & slot);
 	DLL_LINKAGE bool isSlotEquipment(const ArtifactPosition & slot);
+	DLL_LINKAGE bool isBackpackFreeSlots(const CArtifactSet * target, const size_t reqSlots = 1);
 }
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 0
lib/GameSettings.cpp

@@ -69,6 +69,7 @@ void GameSettings::load(const JsonNode & input)
 		{EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP,            "heroes",    "perPlayerTotalCap"          },
 		{EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS,   "heroes",    "retreatOnWinWithoutTroops"  },
 		{EGameSettings::HEROES_STARTING_STACKS_CHANCES,         "heroes",    "startingStackChances"       },
+		{EGameSettings::HEROES_BACKPACK_CAP,                    "heroes",    "backpackSize"               },
 		{EGameSettings::MARKETS_BLACK_MARKET_RESTOCK_PERIOD,    "markets",   "blackMarketRestockPeriod"   },
 		{EGameSettings::MODULE_COMMANDERS,                      "modules",   "commanders"                 },
 		{EGameSettings::MODULE_STACK_ARTIFACT,                  "modules",   "stackArtifact"              },

+ 1 - 0
lib/GameSettings.h

@@ -36,6 +36,7 @@ enum class EGameSettings
 	HEROES_PER_PLAYER_TOTAL_CAP,
 	HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS,
 	HEROES_STARTING_STACKS_CHANCES,
+	HEROES_BACKPACK_CAP,
 	MARKETS_BLACK_MARKET_RESTOCK_PERIOD,
 	MODULE_COMMANDERS,
 	MODULE_STACK_ARTIFACT,

+ 31 - 20
server/CGameHandler.cpp

@@ -3916,36 +3916,41 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat
 	const CArmedInstance *srcObj = src.relatedObj(), *dstObj = dst.relatedObj();
 
 	// Make sure exchange is even possible between the two heroes.
-	if (!isAllowedExchange(srcObj->id, dstObj->id))
+	if(!isAllowedExchange(srcObj->id, dstObj->id))
 		COMPLAIN_RET("That heroes cannot make any exchange!");
 
 	const CArtifactInstance *srcArtifact = src.getArt();
 	const CArtifactInstance *destArtifact = dst.getArt();
+	const bool isDstSlotBackpack = ArtifactUtils::isSlotBackpack(dst.slot);
 
-	if (srcArtifact == nullptr)
+	if(srcArtifact == nullptr)
 		COMPLAIN_RET("No artifact to move!");
-	if (destArtifact && srcPlayer != dstPlayer)
+	if(destArtifact && srcPlayer != dstPlayer)
 		COMPLAIN_RET("Can't touch artifact on hero of another player!");
 
 	// Check if src/dest slots are appropriate for the artifacts exchanged.
 	// Moving to the backpack is always allowed.
-	if ((!srcArtifact || !ArtifactUtils::isSlotBackpack(dst.slot))
+	if((!srcArtifact || !isDstSlotBackpack)
 		&& srcArtifact && !srcArtifact->canBePutAt(dst, true))
 		COMPLAIN_RET("Cannot move artifact!");
 
 	auto srcSlot = src.getSlot();
 	auto dstSlot = dst.getSlot();
 
-	if ((srcSlot && srcSlot->locked) || (dstSlot && dstSlot->locked))
+	if((srcSlot && srcSlot->locked) || (dstSlot && dstSlot->locked))
 		COMPLAIN_RET("Cannot move artifact locks.");
 
-	if (dst.slot >= GameConstants::BACKPACK_START && srcArtifact->artType->isBig())
+	if(isDstSlotBackpack && srcArtifact->artType->isBig())
 		COMPLAIN_RET("Cannot put big artifacts in backpack!");
-	if (src.slot == ArtifactPosition::MACH4 || dst.slot == ArtifactPosition::MACH4)
+	if(src.slot == ArtifactPosition::MACH4 || dst.slot == ArtifactPosition::MACH4)
 		COMPLAIN_RET("Cannot move catapult!");
 
-	if(ArtifactUtils::isSlotBackpack(dst.slot))
-		vstd::amin(dst.slot, ArtifactPosition(GameConstants::BACKPACK_START + (si32)dst.getHolderArtSet()->artifactsInBackpack.size()));
+	if(isDstSlotBackpack)
+	{
+		if(!ArtifactUtils::isBackpackFreeSlots(dst.getHolderArtSet()))
+			COMPLAIN_RET("Backpack is full!");
+		vstd::amin(dst.slot, GameConstants::BACKPACK_START + dst.getHolderArtSet()->artifactsInBackpack.size());
+	}
 
 	if(!(src.slot == ArtifactPosition::TRANSITION_POS && dst.slot == ArtifactPosition::TRANSITION_POS))
 	{
@@ -3953,7 +3958,7 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat
 			COMPLAIN_RET("Won't move artifact: Dest same as source!");
 
 		// Check if dst slot is occupied
-		if(!ArtifactUtils::isSlotBackpack(dst.slot) && destArtifact)
+		if(!isDstSlotBackpack && destArtifact)
 		{
 			// Previous artifact must be removed first
 			moveArtifact(dst, ArtifactLocation(dst.artHolder, ArtifactPosition::TRANSITION_POS));
@@ -4003,11 +4008,14 @@ bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID
 	{
 		assert(artifact);
 		auto dstSlot = ArtifactUtils::getArtifactDstPosition(artifact->artType->getId(), &artFittingSet);
-		artFittingSet.putArtifact(dstSlot, static_cast<ConstTransitivePtr<CArtifactInstance>>(artifact));
-		slots.push_back(BulkMoveArtifacts::LinkedSlots(srcSlot, dstSlot));
+		if(dstSlot != ArtifactPosition::PRE_FIRST)
+		{
+			artFittingSet.putArtifact(dstSlot, static_cast<ConstTransitivePtr<CArtifactInstance>>(artifact));
+			slots.push_back(BulkMoveArtifacts::LinkedSlots(srcSlot, dstSlot));
 
-		if(ArtifactUtils::checkSpellbookIsNeeded(dstHero, artifact->artType->getId(), dstSlot))
-			giveHeroNewArtifact(dstHero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
+			if(ArtifactUtils::checkSpellbookIsNeeded(dstHero, artifact->artType->getId(), dstSlot))
+				giveHeroNewArtifact(dstHero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
+		}
 	};
 
 	if(swap)
@@ -4075,18 +4083,17 @@ bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID
 bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
 {
 	const CGHeroInstance * hero = getHero(heroID);
-	const CArtifactInstance *destArtifact = hero->getArt(artifactSlot);
+	const CArtifactInstance * destArtifact = hero->getArt(artifactSlot);
 
-	if (!destArtifact)
+	if(!destArtifact)
 		COMPLAIN_RET("assembleArtifacts: there is no such artifact instance!");
 
 	if(assemble)
 	{
-		CArtifact *combinedArt = VLC->arth->objects[assembleTo];
+		CArtifact * combinedArt = VLC->arth->objects[assembleTo];
 		if(!combinedArt->constituents)
 			COMPLAIN_RET("assembleArtifacts: Artifact being attempted to assemble is not a combined artifacts!");
-		bool combineEquipped = !ArtifactUtils::isSlotBackpack(artifactSlot);
-		if(!vstd::contains(destArtifact->assemblyPossibilities(hero, combineEquipped), combinedArt))
+		if(!vstd::contains(destArtifact->assemblyPossibilities(hero, ArtifactUtils::isSlotEquipment(artifactSlot)), combinedArt))
 			COMPLAIN_RET("assembleArtifacts: It's impossible to assemble requested artifact!");
 
 		
@@ -4100,9 +4107,13 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition
 	}
 	else
 	{
-		if (!destArtifact->artType->constituents)
+		if(!destArtifact->canBeDisassembled())
 			COMPLAIN_RET("assembleArtifacts: Artifact being attempted to disassemble is not a combined artifact!");
 
+		if(ArtifactUtils::isSlotBackpack(artifactSlot)
+			&& !ArtifactUtils::isBackpackFreeSlots(hero, destArtifact->artType->constituents->size() - 1))
+			COMPLAIN_RET("assembleArtifacts: Artifact being attempted to disassemble but backpack is full!");
+
 		DisassembledArtifact da;
 		da.al = ArtifactLocation(hero, artifactSlot);
 		sendAndApply(&da);