1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-06 09:09:40 +02:00

Fix handling of creatures in configurable town buildings

- Fixed removal of partial stacks of creatures
- It is now possible to give units via town building, but only if
visitor has slots to take them
This commit is contained in:
Ivan Savenko
2025-05-20 22:34:21 +03:00
parent 184e841b16
commit 95ce9ce509
6 changed files with 42 additions and 4 deletions

View File

@@ -635,7 +635,8 @@ List of supported slots names:
- Can be used as limiter
- Can be used as reward, to give new creatures to a hero
- If hero does not have enough free slots, game will show selection dialog to pick troops to keep
- For map objects, if hero does not have enough free slots, game will show selection dialog to pick troops to keep
- For town buildings, hero must either have free slot(s) to take them, or have creatures of this type. Othervice reward would fail to give any creatures
- It is possible to specify probability to receive upgraded creature
```json

View File

@@ -76,6 +76,7 @@ public:
virtual void giveResource(PlayerColor player, GameResID which, int val)=0;
virtual void giveResources(PlayerColor player, ResourceSet resources)=0;
virtual void giveCreatures(const CGHeroInstance * h, const CCreatureSet &creatures) =0;
virtual void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) =0;
virtual void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures, bool forceRemoval = false) =0;
virtual bool changeStackCount(const StackLocation &sl, TQuantity count, ChangeValueMode mode) =0;

View File

@@ -254,8 +254,11 @@ void Rewardable::Interface::grantRewardAfterLevelup(IGameEventCallback & gameEve
for(const auto & crea : info.reward.creatures)
creatures.addToSlot(creatures.getFreeSlot(), std::make_unique<CStackInstance>(cb, crea.getId(), crea.getCount()));
if(auto * army = dynamic_cast<const CArmedInstance*>(this)) //TODO: to fix that, CArmedInstance must be split on map instance part and interface part
auto * army = dynamic_cast<const CArmedInstance*>(this);
if (army)
gameEvents.giveCreatures(army, hero, creatures, false);
else
gameEvents.giveCreatures(hero, creatures);
}
if(info.reward.spellCast.first != SpellID::NONE)

View File

@@ -1139,6 +1139,37 @@ void CGameHandler::giveResources(PlayerColor player, TResources resources)
sendAndApply(sr);
}
void CGameHandler::giveCreatures(const CGHeroInstance * hero, const CCreatureSet &creatures)
{
if (!hero->canBeMergedWith(creatures, true))
{
complain("Unable to give creatures! Hero does not have enough free slots to receive them!");
return;
}
for (const auto & unit : creatures.Slots())
{
SlotID pos = hero->getSlotFor(unit.second->getCreature());
if (!pos.validSlot())
{
//try to merge two other stacks to make place
std::pair<SlotID, SlotID> toMerge;
if (hero->mergeableStacks(toMerge))
{
moveStack(StackLocation(hero->id, toMerge.first), StackLocation(hero->id, toMerge.second)); //merge toMerge.first into toMerge.second
pos = toMerge.first;
}
}
assert(pos.validSlot());
assert(hero->slotEmpty(pos) || hero->getCreature(pos) == unit.second->getCreature());
if (hero->hasStackAtSlot(pos))
changeStackCount(StackLocation(hero->id, pos), unit.second->getCount(), ChangeValueMode::RELATIVE);
else
insertNewStack(StackLocation(hero->id, pos), unit.second->getCreature(), unit.second->getCount());
}
}
void CGameHandler::giveCreatures(const CArmedInstance *obj, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove)
{
COMPLAIN_RET_IF(!creatures.stacksCount(), "Strange, giveCreatures called without args!");
@@ -1181,8 +1212,8 @@ void CGameHandler::takeCreatures(ObjectInstanceID objid, const std::vector<CStac
else
{
// take part of the stack
collected = stackToTake.getCount();
changeStackCount(StackLocation(army->id, armySlot.first), collected - stackToTake.getCount(), ChangeValueMode::RELATIVE);
collected = stackToTake.getCount();
}
foundSth = true;
break;
@@ -3811,7 +3842,7 @@ bool CGameHandler::changeStackCount(const StackLocation &sl, TQuantity count, Ch
auto army = dynamic_cast<const CArmedInstance*>(getObj(sl.army));
TQuantity currentCount = army->getStackCount(sl.slot);
if ((mode == ChangeValueMode::RELATIVE && count < 0)
if ((mode == ChangeValueMode::ABSOLUTE && count < 0)
|| (mode == ChangeValueMode::RELATIVE && -count > currentCount))
{
COMPLAIN_RET("Cannot take more stacks than present!");

View File

@@ -125,6 +125,7 @@ public:
void giveResource(PlayerColor player, GameResID which, int val) override;
void giveResources(PlayerColor player, TResources resources) override;
void giveCreatures(const CGHeroInstance * h, const CCreatureSet &creatures) override;
void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) override;
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures, bool forceRemoval) override;
bool changeStackType(const StackLocation &sl, const CCreature *c) override;

View File

@@ -60,6 +60,7 @@ public:
void giveResource(PlayerColor player, GameResID which, int val) override {}
void giveResources(PlayerColor player, ResourceSet resources) override {}
void giveCreatures(const CGHeroInstance * h, const CCreatureSet &creatures) override{}
void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) override {}
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures, bool forceRemoval) override {}
bool changeStackCount(const StackLocation &sl, TQuantity count, ChangeValueMode mode) override {return false;}