1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-05-29 22:57:49 +02:00

Merge branch 'develop' into crash_fix4

This commit is contained in:
kdmcser 2025-05-06 01:20:19 +08:00 committed by GitHub
commit 5645806035
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
63 changed files with 777 additions and 759 deletions

View File

@ -818,11 +818,11 @@ bool AIGateway::makePossibleUpgrades(const CArmedInstance * obj)
int oldValue = s->getCreature()->getAIValue();
int newValue = upgID.toCreature()->getAIValue();
if(newValue > oldValue && nullkiller->getFreeResources().canAfford(upgradeInfo.getUpgradeCostsFor(upgID) * s->count))
if(newValue > oldValue && nullkiller->getFreeResources().canAfford(upgradeInfo.getUpgradeCostsFor(upgID) * s->getCount()))
{
myCb->upgradeCreature(obj, SlotID(i), upgID);
upgraded = true;
logAi->debug("Upgraded %d %s to %s", s->count, upgradeInfo.oldID.toCreature()->getNamePluralTranslated(),
logAi->debug("Upgraded %d %s to %s", s->getCount(), upgradeInfo.oldID.toCreature()->getNamePluralTranslated(),
upgradeInfo.getUpgrade().toCreature()->getNamePluralTranslated());
}
else

View File

@ -98,7 +98,7 @@ std::vector<SlotInfo> ArmyManager::getSortedSlots(const CCreatureSet * target, c
slotInfp.creature = cre;
slotInfp.power += i.second->getPower();
slotInfp.count += i.second->count;
slotInfp.count += i.second->getCount();
}
}
@ -491,7 +491,7 @@ void ArmyManager::update()
{
for(const auto & slot : army->Slots())
{
totalArmy[slot.second->getCreatureID()].count += slot.second->count;
totalArmy[slot.second->getCreatureID()].count += slot.second->getCount();
}
}
@ -511,7 +511,7 @@ std::vector<SlotInfo> ArmyManager::convertToSlots(const CCreatureSet * army) con
SlotInfo slotInfo;
slotInfo.creature = slot.second->getCreatureID().toCreature();
slotInfo.count = slot.second->count;
slotInfo.count = slot.second->getCount();
slotInfo.power = evaluateStackPower(slotInfo.creature, slotInfo.count);
result.push_back(slotInfo);
@ -537,7 +537,7 @@ std::vector<StackUpgradeInfo> ArmyManager::getHillFortUpgrades(const CCreatureSe
return cre.toCreature()->getAIValue();
});
StackUpgradeInfo upgrade = StackUpgradeInfo(initial, strongestUpgrade, creature.second->count);
StackUpgradeInfo upgrade = StackUpgradeInfo(initial, strongestUpgrade, creature.second->getCount());
if(initial.toCreature()->getLevel() == 1)
upgrade.cost = TResources();
@ -576,7 +576,7 @@ std::vector<StackUpgradeInfo> ArmyManager::getDwellingUpgrades(const CCreatureSe
return cre.toCreature()->getAIValue();
});
StackUpgradeInfo upgrade = StackUpgradeInfo(initial, strongestUpgrade, creature.second->count);
StackUpgradeInfo upgrade = StackUpgradeInfo(initial, strongestUpgrade, creature.second->getCount());
upgrades.push_back(upgrade);
}

View File

@ -699,7 +699,7 @@ int32_t getArmyCost(const CArmedInstance * army)
for(const auto & stack : army->Slots())
{
value += stack.second->getCreatureID().toCreature()->getFullRecruitCost().marketValue() * stack.second->count;
value += stack.second->getCreatureID().toCreature()->getFullRecruitCost().marketValue() * stack.second->getCount();
}
return value;

View File

@ -362,7 +362,7 @@ HeroExchangeArmy * HeroExchangeMap::tryUpgrade(
{
const auto & targetSlot = target->getSlotFor(slot.second->getCreatureID());
target->addToSlot(targetSlot, slot.second->getCreatureID(), slot.second->count);
target->addToSlot(targetSlot, slot.second->getCreatureID(), slot.second->getCount());
}
}
@ -422,7 +422,7 @@ DwellingActor::DwellingActor(const CGDwelling * dwelling, uint64_t chainMask, bo
{
for(auto & slot : creatureSet->Slots())
{
armyCost += slot.second->getCreatureID().toCreature()->getFullRecruitCost() * slot.second->count;
armyCost += slot.second->getCreatureID().toCreature()->getFullRecruitCost() * slot.second->getCount();
}
}

View File

@ -41,7 +41,7 @@ std::vector<SlotInfo> ArmyManager::getSortedSlots(const CCreatureSet * target, c
slotInfp.creature = cre;
slotInfp.power += i.second->getPower();
slotInfp.count += i.second->count;
slotInfp.count += i.second->getCount();
}
}

View File

@ -174,7 +174,7 @@ TGoalVec CompleteQuest::missionArmy() const
for(auto creature : q.getQuest(cb)->mission.creatures)
{
solutions.push_back(sptr(GatherTroops(creature.getId().getNum(), creature.count)));
solutions.push_back(sptr(GatherTroops(creature.getId().getNum(), creature.getCount())));
}
return solutions;

View File

@ -33,7 +33,7 @@ int GatherTroops::getCreaturesCount(const CArmedInstance * army)
{
if(objid == stack.second->getCreatureID().num)
{
count += stack.second->count;
count += stack.second->getCount();
}
}

View File

@ -791,10 +791,10 @@ void makePossibleUpgrades(const CArmedInstance * obj)
{
return id.toCreature()->getAIValue();
});
if(cb->getResourceAmount().canAfford(upgradeInfo.getUpgradeCostsFor(upgID) * s->count))
if(cb->getResourceAmount().canAfford(upgradeInfo.getUpgradeCostsFor(upgID) * s->getCount()))
{
cb->upgradeCreature(obj, SlotID(i), upgID);
logAi->debug("Upgraded %d %s to %s", s->count, upgradeInfo.oldID.toCreature()->getNamePluralTranslated(),
logAi->debug("Upgraded %d %s to %s", s->getCount(), upgradeInfo.oldID.toCreature()->getNamePluralTranslated(),
upgradeInfo.getUpgrade().toCreature()->getNamePluralTranslated());
}
else

View File

@ -131,9 +131,9 @@ int CCallback::bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMa
return 0;
}
int CCallback::bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot)
int CCallback::bulkSplitAndRebalanceStack(ObjectInstanceID armyId, SlotID srcSlot)
{
BulkSmartSplitStack pack(armyId, srcSlot);
BulkSplitAndRebalanceStack pack(armyId, srcSlot);
sendRequest(pack);
return 0;
}

View File

@ -113,7 +113,7 @@ public:
// To implement high-level army management bulk actions
virtual int bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot) = 0;
virtual int bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany = 1) = 0;
virtual int bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot) = 0;
virtual int bulkSplitAndRebalanceStack(ObjectInstanceID armyId, SlotID srcSlot) = 0;
virtual int bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot) = 0;
@ -181,7 +181,7 @@ public:
int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val) override;
int bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot) override;
int bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany = 1) override;
int bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot) override;
int bulkSplitAndRebalanceStack(ObjectInstanceID armyId, SlotID srcSlot) override;
int bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot) override;
bool dismissHero(const CGHeroInstance * hero) override;
bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2) override;

View File

@ -46,7 +46,6 @@ public:
void visitInsertNewStack(InsertNewStack & pack) override;
void visitRebalanceStacks(RebalanceStacks & pack) override;
void visitBulkRebalanceStacks(BulkRebalanceStacks & pack) override;
void visitBulkSmartRebalanceStacks(BulkSmartRebalanceStacks & pack) override;
void visitPutArtifact(PutArtifact & pack) override;
void visitEraseArtifact(BulkEraseArtifacts & pack) override;
void visitBulkMoveArtifacts(BulkMoveArtifacts & pack) override;

View File

@ -263,19 +263,6 @@ void ApplyClientNetPackVisitor::visitBulkRebalanceStacks(BulkRebalanceStacks & p
}
}
void ApplyClientNetPackVisitor::visitBulkSmartRebalanceStacks(BulkSmartRebalanceStacks & pack)
{
if(!pack.moves.empty())
{
assert(pack.moves[0].srcArmy == pack.moves[0].dstArmy);
dispatchGarrisonChange(cl, pack.moves[0].srcArmy, ObjectInstanceID());
}
else if(!pack.changes.empty())
{
dispatchGarrisonChange(cl, pack.changes[0].army, ObjectInstanceID());
}
}
void ApplyClientNetPackVisitor::visitPutArtifact(PutArtifact & pack)
{
callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactPut, pack.al);
@ -880,7 +867,7 @@ void ApplyClientNetPackVisitor::visitBattleResultsApplied(BattleResultsApplied &
if(pack.raisedStack.getCreature())
callInterfaceIfPresent(cl, pack.victor, &CGameInterface::showInfoDialog, EInfoWindowMode::AUTO,
UIHelper::getNecromancyInfoWindowText(pack.raisedStack), std::vector<Component>{Component(ComponentType::CREATURE, pack.raisedStack.getId(),
pack.raisedStack.count)}, UIHelper::getNecromancyInfoWindowSound());
pack.raisedStack.getCount())}, UIHelper::getNecromancyInfoWindowSound());
callInterfaceIfPresent(cl, pack.victor, &IGameEventsReceiver::battleResultsApplied);
callInterfaceIfPresent(cl, pack.loser, &IGameEventsReceiver::battleResultsApplied);

View File

@ -50,10 +50,10 @@ soundBase::soundID UIHelper::getNecromancyInfoWindowSound()
std::string UIHelper::getNecromancyInfoWindowText(const CStackBasicDescriptor & stack)
{
MetaString text;
if(stack.count > 1) // Practicing the dark arts of necromancy, ... (plural)
if(stack.getCount() > 1) // Practicing the dark arts of necromancy, ... (plural)
{
text.appendLocalString(EMetaText::GENERAL_TXT, 145);
text.replaceNumber(stack.count);
text.replaceNumber(stack.getCount());
}
else // Practicing the dark arts of necromancy, ... (singular)
{

View File

@ -51,6 +51,7 @@ BasicMapView::BasicMapView(const Point & offset, const Point & dimensions)
: model(createModel(dimensions))
, tilesCache(new MapViewCache(model))
, controller(new MapViewController(model, tilesCache))
, needFullUpdate(false)
{
OBJECT_CONSTRUCTION;
pos += offset;
@ -76,7 +77,7 @@ void BasicMapView::tick(uint32_t msPassed)
void BasicMapView::show(Canvas & to)
{
CanvasClipRectGuard guard(to, pos);
render(to, false);
render(to, needFullUpdate);
controller->afterRender();
}

View File

@ -35,6 +35,8 @@ protected:
void render(Canvas & target, bool fullUpdate);
public:
bool needFullUpdate;
BasicMapView(const Point & offset, const Point & dimensions);
~BasicMapView() override;

View File

@ -255,8 +255,8 @@ bool CGarrisonSlot::split()
}
}
int countLeft = selection->myStack ? selection->myStack->count : 0;
int countRight = myStack ? myStack->count : 0;
int countLeft = selection->myStack ? selection->myStack->getCount() : 0;
int countRight = myStack ? myStack->getCount() : 0;
auto splitFunctor = [this, selection](int amountLeft, int amountRight)
{
@ -345,7 +345,7 @@ void CGarrisonSlot::clickPressed(const Point & cursorPosition)
refr = split();
}
else if(!creature && lastHeroStackSelected) // split all except last creature
GAME->interface()->cb->splitStack(selectedObj, owner->army(upg), selection->ID, ID, selection->myStack->count - 1);
GAME->interface()->cb->splitStack(selectedObj, owner->army(upg), selection->ID, ID, selection->myStack->getCount() - 1);
else if(creature != selection->creature) // swap
GAME->interface()->cb->swapCreatures(owner->army(upg), selectedObj, ID, selection->ID);
else if(lastHeroStackSelected) // merge last stack to other hero stack
@ -388,7 +388,7 @@ void CGarrisonSlot::gesture(bool on, const Point & initialPosition, const Point
{ RadialMenuConfig::ITEM_NW, hasSameUnit, "stackMerge", "vcmi.radialWheel.mergeSameUnit", [this](){owner->bulkMergeStacks(this);} },
{ RadialMenuConfig::ITEM_NE, hasOwnEmptySlots, "stackFillOne", "vcmi.radialWheel.fillSingleUnit", [this](){owner->bulkSplitStack(this);} },
{ RadialMenuConfig::ITEM_WW, hasOwnEmptySlots, "stackSplitOne", "vcmi.radialWheel.splitSingleUnit", [this](){splitIntoParts(this->getGarrison(), 1); } },
{ RadialMenuConfig::ITEM_EE, hasOwnEmptySlots, "stackSplitEqual", "vcmi.radialWheel.splitUnitEqually", [this](){owner->bulkSmartSplitStack(this);} },
{ RadialMenuConfig::ITEM_EE, hasOwnEmptySlots, "stackSplitEqual", "vcmi.radialWheel.splitUnitEqually", [this](){owner->bulkSplitAndRebalanceStack(this);} },
{ RadialMenuConfig::ITEM_SW, hasOtherEmptySlots, "heroMove", "vcmi.radialWheel.moveUnit", [this](){owner->moveStackToAnotherArmy(this);} },
{ RadialMenuConfig::ITEM_SE, hasAnyEmptySlots, "heroSwap", "vcmi.radialWheel.splitUnit", [this](){ owner->selectSlot(this); owner->splitClick();} },
};
@ -418,7 +418,7 @@ void CGarrisonSlot::update()
creatureImage->setFrame(creature->getIconIndex());
stackCount->enable();
stackCount->setText(TextOperations::formatMetric(myStack->count, 4));
stackCount->setText(TextOperations::formatMetric(myStack->getCount(), 4));
}
else
{
@ -499,7 +499,7 @@ bool CGarrisonSlot::handleSplittingShortcuts()
if(!selected)
return true; // Some Shortcusts are pressed but there are no appropriate actions
auto units = selected->myStack->count;
auto units = selected->myStack->getCount();
if(units < 1)
return true;
@ -529,7 +529,7 @@ bool CGarrisonSlot::handleSplittingShortcuts()
if(isLCtrl && isLShift)
owner->bulkSplitStack(selected);
else if(isLShift)
owner->bulkSmartSplitStack(selected);
owner->bulkSplitAndRebalanceStack(selected);
else
splitIntoParts(selected->upg, 1); // LCtrl
}
@ -603,7 +603,7 @@ void CGarrisonInt::splitStacks(const CGarrisonSlot * from, const CArmedInstance
bool CGarrisonInt::checkSelected(const CGarrisonSlot * selected, TQuantity min) const
{
return selected && selected->myStack && selected->myStack->count > min && selected->creature;
return selected && selected->myStack && selected->myStack->getCount() > min && selected->creature;
}
void CGarrisonInt::moveStackToAnotherArmy(const CGarrisonSlot * selected)
@ -634,7 +634,7 @@ void CGarrisonInt::moveStackToAnotherArmy(const CGarrisonSlot * selected)
destSlot = srcSlot; // Same place is more preferable
const bool isLastStack = srcArmy->stacksCount() == 1 && srcArmy->needsLastStack();
auto srcAmount = selected->myStack->count - (isLastStack ? 1 : 0);
auto srcAmount = selected->myStack->getCount() - (isLastStack ? 1 : 0);
if(!srcAmount)
return;
@ -696,7 +696,7 @@ void CGarrisonInt::bulkSplitStack(const CGarrisonSlot * selected)
GAME->interface()->cb->bulkSplitStack(armedObjs[type]->id, selected->ID);
}
void CGarrisonInt::bulkSmartSplitStack(const CGarrisonSlot * selected)
void CGarrisonInt::bulkSplitAndRebalanceStack(const CGarrisonSlot * selected)
{
if(!checkSelected(selected, 1))
return;
@ -707,7 +707,7 @@ void CGarrisonInt::bulkSmartSplitStack(const CGarrisonSlot * selected)
if(!hasEmptySlot(type) && armedObjs[type]->isCreatureBalanced(selected->creature))
return;
GAME->interface()->cb->bulkSmartSplitStack(armedObjs[type]->id, selected->ID);
GAME->interface()->cb->bulkSplitAndRebalanceStack(armedObjs[type]->id, selected->ID);
}
CGarrisonInt::CGarrisonInt(const Point & position, int inx, const Point & garsOffset, const CArmedInstance * s1, const CArmedInstance * s2, bool _removableUnits, bool smallImgs, ESlotsLayout _layout)

View File

@ -134,7 +134,7 @@ public:
void bulkMoveArmy(const CGarrisonSlot * selected);
void bulkMergeStacks(const CGarrisonSlot * selected); // Gather all creatures of selected type to the selected slot from other hero/garrison slots
void bulkSplitStack(const CGarrisonSlot * selected); // Used to separate one-creature troops from main stack
void bulkSmartSplitStack(const CGarrisonSlot * selected);
void bulkSplitAndRebalanceStack(const CGarrisonSlot * selected);
/// Constructor
/// @param position Relative position to parent element

View File

@ -286,20 +286,20 @@ void CArmyTooltip::init(const InfoAboutArmy &army)
std::string subtitle;
if(army.army.isDetailed)
{
subtitle = TextOperations::formatMetric(slot.second.count, 4);
subtitle = TextOperations::formatMetric(slot.second.getCount(), 4);
}
else
{
//if =0 - we have no information about stack size at all
if(slot.second.count)
if(slot.second.getCount())
{
if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool())
{
subtitle = CCreature::getQuantityRangeStringForId((CCreature::CreatureQuantityId)slot.second.count);
subtitle = CCreature::getQuantityRangeStringForId((CCreature::CreatureQuantityId)slot.second.getCount());
}
else
{
subtitle = LIBRARY->generaltexth->arraytxt[171 + 3*(slot.second.count)];
subtitle = LIBRARY->generaltexth->arraytxt[171 + 3*(slot.second.getCount())];
}
}
}

View File

@ -403,7 +403,7 @@ void CHeroGSlot::gesture(bool on, const Point & initialPosition, const Point & f
resComps.push_back(std::make_shared<CComponent>(ComponentType::RESOURCE, static_cast<GameResID>(GameResID::GOLD), 0)); // add at least gold, when there are no costs
resComps.back()->newLine = true;
for(auto & upgradeInfo : upgradableSlots.upgradeInfos)
resComps.push_back(std::make_shared<CComponent>(ComponentType::CREATURE, upgradeInfo.second.getUpgrade(), obj->Slots().at(upgradeInfo.first)->count));
resComps.push_back(std::make_shared<CComponent>(ComponentType::CREATURE, upgradeInfo.second.getUpgrade(), obj->Slots().at(upgradeInfo.first)->getCount()));
std::string textID = upgradableSlots.canAffordAll ? "core.genrltxt.207" : "vcmi.townWindow.upgradeAll.notAllUpgradable";

View File

@ -664,7 +664,7 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
area->component.value = commander->getExpRank();
boost::replace_first(area->text, "%d", std::to_string(commander->getExpRank()));
boost::replace_first(area->text, "%d", std::to_string(LIBRARY->heroh->reqExp(commander->getExpRank() + 1)));
boost::replace_first(area->text, "%d", std::to_string(commander->experience));
boost::replace_first(area->text, "%d", std::to_string(commander->getAverageExperience()));
}
else
{
@ -674,7 +674,7 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
}
expLabel = std::make_shared<CLabel>(
pos.x + 21, pos.y + 55, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE,
TextOperations::formatMetric(stack->experience, 6));
TextOperations::formatMetric(stack->getAverageExperience(), 6));
}
if(showArt)
@ -764,7 +764,7 @@ CStackWindow::CStackWindow(const CStackInstance * stack, bool popup)
{
info->stackNode = stack;
info->creature = stack->getCreature();
info->creatureCount = stack->count;
info->creatureCount = stack->getCount();
info->popupWindow = popup;
info->owner = dynamic_cast<const CGHeroInstance *> (stack->getArmy());
init();
@ -776,7 +776,7 @@ CStackWindow::CStackWindow(const CStackInstance * stack, std::function<void()> d
{
info->stackNode = stack;
info->creature = stack->getCreature();
info->creatureCount = stack->count;
info->creatureCount = stack->getCount();
if(upgradeInfo.canUpgrade())
{
@ -968,8 +968,8 @@ std::string CStackWindow::generateStackExpDescription()
boost::replace_first(expText, "%s", creature->getNamePluralTranslated());
boost::replace_first(expText, "%s", LIBRARY->generaltexth->translate("vcmi.stackExperience.rank", rank));
boost::replace_first(expText, "%i", std::to_string(rank));
boost::replace_first(expText, "%i", std::to_string(stack->experience));
number = static_cast<int>(LIBRARY->creh->expRanks[tier][rank] - stack->experience);
boost::replace_first(expText, "%i", std::to_string(stack->getAverageExperience()));
number = static_cast<int>(LIBRARY->creh->expRanks[tier][rank] - stack->getAverageExperience());
boost::replace_first(expText, "%i", std::to_string(number));
number = LIBRARY->creh->maxExpPerBattle[tier]; //percent
@ -977,10 +977,10 @@ std::string CStackWindow::generateStackExpDescription()
number *= LIBRARY->creh->expRanks[tier].back() / 100; //actual amount
boost::replace_first(expText, "%i", std::to_string(number));
boost::replace_first(expText, "%i", std::to_string(stack->count)); //Number of Creatures in stack
boost::replace_first(expText, "%i", std::to_string(stack->getCount())); //Number of Creatures in stack
int expmin = std::max(LIBRARY->creh->expRanks[tier][std::max(rank-1, 0)], (ui32)1);
number = static_cast<int>((stack->count * (stack->experience - expmin)) / expmin); //Maximum New Recruits without losing current Rank
number = stack->getTotalExperience() / expmin - stack->getCount(); //Maximum New Recruits without losing current Rank
boost::replace_first(expText, "%i", std::to_string(number)); //TODO
boost::replace_first(expText, "%.2f", std::to_string(1)); //TODO Experience Multiplier
@ -991,7 +991,7 @@ std::string CStackWindow::generateStackExpDescription()
int expmax = LIBRARY->creh->expRanks[tier][10];
number = expmax - expmin;
boost::replace_first(expText, "%i", std::to_string(number)); //Experience after Rank 10
number = (stack->count * (expmax - expmin)) / expmin;
number = (stack->getCount() * (expmax - expmin)) / expmin;
boost::replace_first(expText, "%i", std::to_string(number)); //Maximum New Recruits to remain at Rank 10 if at Maximum Experience
return expText;

View File

@ -43,6 +43,7 @@ CPuzzleWindow::CPuzzleWindow(const int3 & GrailPos, double discoveredRatio)
quitb->setBorderColor(Colors::METALLIC_GOLD);
mapView = std::make_shared<PuzzleMapView>(Point(8,9), Point(591, 544), grailPos);
mapView->needFullUpdate = true;
logo = std::make_shared<CPicture>(ImagePath::builtin("PUZZLOGO"), 607, 3);
title = std::make_shared<CLabel>(700, 95, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, LIBRARY->generaltexth->allTexts[463]);
@ -93,4 +94,7 @@ void CPuzzleWindow::show(Canvas & to)
currentAlpha -= animSpeed;
}
CWindowObject::show(to);
if(mapView->needFullUpdate && piecesToRemove.empty())
mapView->needFullUpdate = false;
}

View File

@ -481,12 +481,12 @@ Install successfully downloaded?</source>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="82"/>
<source>Config editor</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="89"/>
<source>Open editor</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="102"/>
@ -588,7 +588,7 @@ Fullscreen Exclusive Mode - the game will cover the entirety of your screen and
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="1515"/>
<source>Ignore mute switch</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="693"/>
@ -876,37 +876,37 @@ Fullscreen Exclusive Mode - the game will cover the entirety of your screen and
<message>
<location filename="../settingsView/configeditordialog_moc.ui" line="50"/>
<source>Save</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../settingsView/configeditordialog_moc.ui" line="73"/>
<source>File:</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../settingsView/configeditordialog_moc.ui" line="86"/>
<source>Close</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../settingsView/configeditordialog_moc.cpp" line="27"/>
<source>Config editor</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../settingsView/configeditordialog_moc.cpp" line="68"/>
<source>Unsaved changes</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../settingsView/configeditordialog_moc.cpp" line="68"/>
<source>Do you want to discard changes?</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../settingsView/configeditordialog_moc.cpp" line="122"/>
<source>JSON file is not valid!</source>
<translation type="unfinished"></translation>
<translation>JSON文件格式错误</translation>
</message>
</context>
<context>
@ -1168,7 +1168,7 @@ Please select the directory with Heroes III: Complete Edition or Heroes III: Sha
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="369"/>
<source>Failed to open file: %1</source>
<translation type="unfinished"></translation>
<translation>%1</translation>
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="438"/>
@ -1318,18 +1318,19 @@ Bin (%n字节):
<message>
<location filename="../languages.cpp" line="23"/>
<source>Belarusian</source>
<translation type="unfinished"></translation>
<translation>
</translation>
</message>
<message>
<location filename="../languages.cpp" line="24"/>
<source>Bulgarian</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="25"/>
<source>Czech</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="26"/>
@ -1340,111 +1341,100 @@ Bin (%n字节):
<location filename="../languages.cpp" line="27"/>
<source>English</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="28"/>
<source>Finnish</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="29"/>
<source>French</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="30"/>
<source>German</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="31"/>
<source>Greek</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="32"/>
<source>Hungarian</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="33"/>
<source>Italian</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="34"/>
<source>Japanese</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="35"/>
<source>Korean</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="36"/>
<source>Norwegian</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="37"/>
<source>Polish</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="38"/>
<source>Portuguese</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="39"/>
<source>Romanian</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="40"/>
<source>Russian</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="41"/>
<source>Spanish</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished">西</translation>
<translation>西</translation>
</message>
<message>
<location filename="../languages.cpp" line="42"/>
<source>Swedish</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="43"/>
<source>Turkish</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="44"/>
<source>Ukrainian</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="45"/>
<source>Vietnamese</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../languages.cpp" line="67"/>
@ -1685,7 +1675,7 @@ Bin (%n字节):
<message>
<location filename="../modManager/modstateitemmodel_moc.cpp" line="54"/>
<source>Campaigns</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../modManager/modstateitemmodel_moc.cpp" line="55"/>
@ -1979,13 +1969,13 @@ To resolve this problem, please copy missing data files from Heroes III to VCMI
<location filename="../startGame/StartGameTab.cpp" line="407"/>
<location filename="../startGame/StartGameTab.cpp" line="416"/>
<source>Preset import failed</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../startGame/StartGameTab.cpp" line="407"/>
<location filename="../startGame/StartGameTab.cpp" line="416"/>
<source>Failed to import preset - data in clipboard does not looks like mod preset!</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../startGame/StartGameTab.cpp" line="432"/>

View File

@ -307,20 +307,17 @@
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="482"/>
<source>Context menu</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished">Menu de contexto</translation>
<translation>Menu de contexto</translation>
</message>
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="525"/>
<source>Open directory</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished">Abrir diretório</translation>
<translation>Abrir diretório</translation>
</message>
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="530"/>
<source>Open repository</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished">Abrir repositório</translation>
<translation>Abrir repositório</translation>
</message>
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="783"/>
@ -475,12 +472,12 @@ O download da instalação foi bem-sucedido?</translation>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="82"/>
<source>Config editor</source>
<translation type="unfinished"></translation>
<translation>Editor de configuração</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="89"/>
<source>Open editor</source>
<translation type="unfinished"></translation>
<translation>Abrir editor</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="102"/>
@ -510,8 +507,7 @@ O download da instalação foi bem-sucedido?</translation>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="547"/>
<source>Allow portrait mode</source>
<translatorcomment>AI-generated, needs review by native speaker; delete this comment afterwards</translatorcomment>
<translation type="unfinished">Permitir modo retrato</translation>
<translation>Permitir modo retrato</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="825"/>
@ -583,7 +579,7 @@ Modo de tela cheia exclusivo - o jogo cobrirá toda a sua tela e usará a resolu
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="1515"/>
<source>Ignore mute switch</source>
<translation type="unfinished"></translation>
<translation>Ignorar botão de mudo</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="693"/>
@ -875,37 +871,37 @@ Modo de tela cheia exclusivo - o jogo cobrirá toda a sua tela e usará a resolu
<message>
<location filename="../settingsView/configeditordialog_moc.ui" line="50"/>
<source>Save</source>
<translation type="unfinished"></translation>
<translation>Salvar</translation>
</message>
<message>
<location filename="../settingsView/configeditordialog_moc.ui" line="73"/>
<source>File:</source>
<translation type="unfinished"></translation>
<translation>Arquivo:</translation>
</message>
<message>
<location filename="../settingsView/configeditordialog_moc.ui" line="86"/>
<source>Close</source>
<translation type="unfinished">Fechar</translation>
<translation>Fechar</translation>
</message>
<message>
<location filename="../settingsView/configeditordialog_moc.cpp" line="27"/>
<source>Config editor</source>
<translation type="unfinished"></translation>
<translation>Editor de configuração</translation>
</message>
<message>
<location filename="../settingsView/configeditordialog_moc.cpp" line="68"/>
<source>Unsaved changes</source>
<translation type="unfinished"></translation>
<translation>Alterações não salvas</translation>
</message>
<message>
<location filename="../settingsView/configeditordialog_moc.cpp" line="68"/>
<source>Do you want to discard changes?</source>
<translation type="unfinished"></translation>
<translation>Deseja descartar as alterações?</translation>
</message>
<message>
<location filename="../settingsView/configeditordialog_moc.cpp" line="122"/>
<source>JSON file is not valid!</source>
<translation type="unfinished"></translation>
<translation>O arquivo JSON não é válido!</translation>
</message>
</context>
<context>
@ -1171,7 +1167,7 @@ Por favor, selecione o diretório com Heroes III: Complete Edition ou Heroes III
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="369"/>
<source>Failed to open file: %1</source>
<translation type="unfinished"></translation>
<translation>Falha ao abrir o arquivo: %1</translation>
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="462"/>
@ -1322,12 +1318,12 @@ Bin (%n bytes):
<message>
<location filename="../languages.cpp" line="23"/>
<source>Belarusian</source>
<translation type="unfinished"></translation>
<translation>Bielorrusso</translation>
</message>
<message>
<location filename="../languages.cpp" line="24"/>
<source>Bulgarian</source>
<translation type="unfinished"></translation>
<translation>Búlgaro</translation>
</message>
<message>
<location filename="../languages.cpp" line="25"/>
@ -1362,7 +1358,7 @@ Bin (%n bytes):
<message>
<location filename="../languages.cpp" line="31"/>
<source>Greek</source>
<translation type="unfinished"></translation>
<translation>Grego</translation>
</message>
<message>
<location filename="../languages.cpp" line="32"/>
@ -1377,7 +1373,7 @@ Bin (%n bytes):
<message>
<location filename="../languages.cpp" line="34"/>
<source>Japanese</source>
<translation type="unfinished"></translation>
<translation>Japonês</translation>
</message>
<message>
<location filename="../languages.cpp" line="35"/>
@ -1387,7 +1383,7 @@ Bin (%n bytes):
<message>
<location filename="../languages.cpp" line="36"/>
<source>Norwegian</source>
<translation type="unfinished"></translation>
<translation>Norueguês</translation>
</message>
<message>
<location filename="../languages.cpp" line="37"/>
@ -1402,7 +1398,7 @@ Bin (%n bytes):
<message>
<location filename="../languages.cpp" line="39"/>
<source>Romanian</source>
<translation type="unfinished"></translation>
<translation>Romeno</translation>
</message>
<message>
<location filename="../languages.cpp" line="40"/>
@ -1673,7 +1669,7 @@ Bin (%n bytes):
<message>
<location filename="../modManager/modstateitemmodel_moc.cpp" line="54"/>
<source>Campaigns</source>
<translation type="unfinished">Campanhas</translation>
<translation>Campanhas</translation>
</message>
<message>
<location filename="../modManager/modstateitemmodel_moc.cpp" line="55"/>
@ -1969,13 +1965,13 @@ Para resolver esse problema, copie manualmente os arquivos de dados ausentes do
<location filename="../startGame/StartGameTab.cpp" line="407"/>
<location filename="../startGame/StartGameTab.cpp" line="416"/>
<source>Preset import failed</source>
<translation type="unfinished"></translation>
<translation>Falha na importação da predefinição</translation>
</message>
<message>
<location filename="../startGame/StartGameTab.cpp" line="407"/>
<location filename="../startGame/StartGameTab.cpp" line="416"/>
<source>Failed to import preset - data in clipboard does not looks like mod preset!</source>
<translation type="unfinished"></translation>
<translation>Falha ao importar predefinição - os dados na área de transferência não parecem ser uma predefinição de mod!</translation>
</message>
<message>
<location filename="../startGame/StartGameTab.cpp" line="432"/>

View File

@ -125,7 +125,7 @@ std::vector<SlotID> CCreatureSet::getCreatureSlots(const CCreature * c, const Sl
if(!elem.second || !elem.second->getType() || elem.second->getType() != c)
continue;
if(elem.second->count == ignoreAmount || elem.second->count < 1)
if(elem.second->getCount() == ignoreAmount || elem.second->getCount() < 1)
continue;
result.push_back(elem.first);
@ -144,7 +144,7 @@ bool CCreatureSet::isCreatureBalanced(const CCreature * c, TQuantity ignoreAmoun
if(!elem.second || !elem.second->getType() || elem.second->getType() != c)
continue;
const auto count = elem.second->count;
const auto count = elem.second->getCount();
if(count == ignoreAmount || count < 1)
continue;
@ -236,20 +236,19 @@ TCreatureQueue CCreatureSet::getCreatureQueue(const SlotID & exclude) const
TQuantity CCreatureSet::getStackCount(const SlotID & slot) const
{
auto i = stacks.find(slot);
if (i != stacks.end())
return i->second->count;
else
return 0; //TODO? consider issuing a warning
if (!hasStackAtSlot(slot))
return 0;
return stacks.at(slot)->getCount();
}
TExpType CCreatureSet::getStackExperience(const SlotID & slot) const
TExpType CCreatureSet::getStackTotalExperience(const SlotID & slot) const
{
auto i = stacks.find(slot);
if (i != stacks.end())
return i->second->experience;
else
return 0; //TODO? consider issuing a warning
return stacks.at(slot)->getTotalExperience();
}
TExpType CCreatureSet::getStackAverageExperience(const SlotID & slot) const
{
return stacks.at(slot)->getAverageExperience();
}
bool CCreatureSet::mergeableStacks(std::pair<SlotID, SlotID> & out, const SlotID & preferable) const /*looks for two same stacks, returns slot positions */
@ -284,19 +283,6 @@ bool CCreatureSet::mergeableStacks(std::pair<SlotID, SlotID> & out, const SlotID
return false;
}
void CCreatureSet::sweep()
{
for(auto i=stacks.begin(); i!=stacks.end(); ++i)
{
if(!i->second->count)
{
stacks.erase(i);
sweep();
break;
}
}
}
void CCreatureSet::addToSlot(const SlotID & slot, const CreatureID & cre, TQuantity count, bool allowMerging)
{
const CCreature *c = cre.toCreature();
@ -437,23 +423,24 @@ void CCreatureSet::setFormation(EArmyFormation mode)
void CCreatureSet::setStackCount(const SlotID & slot, TQuantity count)
{
assert(hasStackAtSlot(slot));
assert(stacks[slot]->count + count > 0);
if (count > stacks[slot]->count)
stacks[slot]->experience = static_cast<TExpType>(stacks[slot]->experience * (count / static_cast<double>(stacks[slot]->count)));
stacks[slot]->count = count;
stacks.at(slot)->setCount(count);
armyChanged();
}
void CCreatureSet::giveStackExp(TExpType exp)
void CCreatureSet::giveAverageStackExperience(TExpType exp)
{
for(TSlots::const_iterator i = stacks.begin(); i != stacks.end(); i++)
i->second->giveStackExp(exp);
for(const auto & stack : stacks)
{
stack.second->giveAverageStackExperience(exp);
stack.second->nodeHasChanged();
}
}
void CCreatureSet::setStackExp(const SlotID & slot, TExpType exp)
void CCreatureSet::giveTotalStackExperience(const SlotID & slot, TExpType exp)
{
assert(hasStackAtSlot(slot));
stacks[slot]->experience = exp;
stacks[slot]->giveTotalStackExperience(exp);
stacks[slot]->nodeHasChanged();
}
void CCreatureSet::clearSlots()
@ -528,7 +515,23 @@ void CCreatureSet::joinStack(const SlotID & slot, std::unique_ptr<CStackInstance
assert(c);
//TODO move stuff
changeStackCount(slot, stack->count);
changeStackCount(slot, stack->getCount());
giveTotalStackExperience(slot, stack->getTotalExperience());
}
std::unique_ptr<CStackInstance> CCreatureSet::splitStack(const SlotID & slot, TQuantity toSplit)
{
auto & currentStack = stacks.at(slot);
assert(currentStack->getCount() > toSplit);
TExpType experienceBefore = currentStack->getTotalExperience();
currentStack->setCount(currentStack->getCount() - toSplit);
TExpType experienceAfter = currentStack->getTotalExperience();
auto newStack = std::make_unique<CStackInstance>(currentStack->cb, currentStack->getCreatureID(), toSplit);
newStack->giveTotalStackExperience(experienceBefore - experienceAfter);
return newStack;
}
void CCreatureSet::changeStackCount(const SlotID & slot, TQuantity toAdd)
@ -674,14 +677,13 @@ void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::strin
CStackInstance::CStackInstance(IGameCallback *cb, bool isHypothetic)
: CBonusSystemNode(isHypothetic)
, CStackBasicDescriptor(nullptr, 0)
, CArtifactSet(cb)
, GameCallbackHolder(cb)
, nativeTerrain(this, Selector::type()(BonusType::TERRAIN_NATIVE))
, initiative(this, Selector::type()(BonusType::STACKS_SPEED))
, totalExperience(0)
{
experience = 0;
count = 0;
setType(nullptr);
setNodeType(STACK_INSTANCE);
}
@ -689,12 +691,12 @@ CStackInstance::CStackInstance(IGameCallback *cb, const CreatureID & id, TQuanti
: CStackInstance(cb, false)
{
setType(id);
count = Count;
setCount(Count);
}
CCreature::CreatureQuantityId CStackInstance::getQuantityID() const
{
return CCreature::getQuantityID(count);
return CCreature::getQuantityID(getCount());
}
int CStackInstance::getExpRank() const
@ -706,7 +708,7 @@ int CStackInstance::getExpRank() const
{
for(int i = static_cast<int>(LIBRARY->creh->expRanks[tier].size()) - 2; i > -1; --i) //sic!
{ //exp values vary from 1st level to max exp at 11th level
if (experience >= LIBRARY->creh->expRanks[tier][i])
if (getAverageExperience() >= LIBRARY->creh->expRanks[tier][i])
return ++i; //faster, but confusing - 0 index mean 1st level of experience
}
return 0;
@ -715,7 +717,7 @@ int CStackInstance::getExpRank() const
{
for(int i = static_cast<int>(LIBRARY->creh->expRanks[0].size()) - 2; i > -1; --i)
{
if (experience >= LIBRARY->creh->expRanks[0][i])
if (getAverageExperience() >= LIBRARY->creh->expRanks[0][i])
return ++i;
}
return 0;
@ -727,17 +729,47 @@ int CStackInstance::getLevel() const
return std::max(1, getType()->getLevel());
}
void CStackInstance::giveStackExp(TExpType exp)
void CStackInstance::giveAverageStackExperience(TExpType desiredAmountPerUnit)
{
int level = getType()->getLevel();
if (!vstd::iswithin(level, 1, 7))
level = 0;
if (!canGainExperience())
return;
ui32 maxExp = LIBRARY->creh->expRanks[level].back();
int level = std::clamp(getLevel(), 1, 7);
TExpType maxAmountPerUnit = LIBRARY->creh->expRanks[level].back();
TExpType actualAmountPerUnit = std::min(desiredAmountPerUnit, maxAmountPerUnit * LIBRARY->creh->maxExpPerBattle[level]/100);
TExpType maxExperience = maxAmountPerUnit * getCount();
TExpType maxExperienceToGain = maxExperience - totalExperience;
TExpType actualGainedExperience = std::min(maxExperienceToGain, actualAmountPerUnit * getCount());
vstd::amin(exp, static_cast<TExpType>(maxExp)); //prevent exp overflow due to different types
vstd::amin(exp, (maxExp * LIBRARY->creh->maxExpPerBattle[level])/100);
vstd::amin(experience += exp, maxExp); //can't get more exp than this limit
totalExperience += actualGainedExperience;
}
void CStackInstance::giveTotalStackExperience(TExpType experienceToGive)
{
if (!canGainExperience())
return;
int level = std::clamp(getLevel(), 1, 7);
TExpType maxAmountPerUnit = LIBRARY->creh->expRanks[level].back();
TExpType maxExperience = maxAmountPerUnit * getCount();
TExpType maxExperienceToGain = maxExperience - totalExperience;
TExpType actualGainedExperience = std::min(maxExperienceToGain, experienceToGive);
totalExperience += actualGainedExperience;
}
TExpType CStackInstance::getTotalExperience() const
{
return totalExperience;
}
TExpType CStackInstance::getAverageExperience() const
{
return totalExperience / getCount();
}
bool CStackInstance::canGainExperience() const
{
return cb->getSettings().getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE);
}
void CStackInstance::setType(const CreatureID & creID)
@ -753,8 +785,8 @@ void CStackInstance::setType(const CCreature *c)
if(getCreature())
{
detachFromSource(*getCreature());
if (getCreature()->isMyUpgrade(c) && LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
experience = static_cast<TExpType>(experience * LIBRARY->creh->expAfterUpgrade / 100.0);
if (LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
totalExperience = totalExperience * LIBRARY->creh->expAfterUpgrade / 100;
}
CStackBasicDescriptor::setType(c);
@ -762,6 +794,20 @@ void CStackInstance::setType(const CCreature *c)
if(getCreature())
attachToSource(*getCreature());
}
void CStackInstance::setCount(TQuantity newCount)
{
assert(newCount >= 0);
if (newCount < getCount())
{
TExpType averageExperience = totalExperience / getCount();
totalExperience = averageExperience * newCount;
}
CStackBasicDescriptor::setCount(newCount);
}
std::string CStackInstance::bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const
{
return LIBRARY->getBth()->bonusToString(bonus, this, description);
@ -774,32 +820,28 @@ ImagePath CStackInstance::bonusToGraphics(const std::shared_ptr<Bonus> & bonus)
CArmedInstance * CStackInstance::getArmy()
{
if (armyInstanceID.hasValue())
return dynamic_cast<CArmedInstance*>(cb->gameState().getObjInstance(armyInstanceID));
return nullptr;
return armyInstance;
}
const CArmedInstance * CStackInstance::getArmy() const
{
if (armyInstanceID.hasValue())
return dynamic_cast<const CArmedInstance*>(cb->getObjInstance(armyInstanceID));
return nullptr;
return armyInstance;
}
void CStackInstance::setArmy(const CArmedInstance * ArmyObj)
void CStackInstance::setArmy(CArmedInstance * ArmyObj)
{
auto oldArmy = getArmy();
if(oldArmy)
{
detachFrom(*oldArmy);
armyInstanceID = {};
armyInstance = nullptr;
}
if(ArmyObj)
{
attachTo(const_cast<CArmedInstance&>(*ArmyObj));
armyInstanceID = ArmyObj->id;
armyInstance = ArmyObj;
}
}
@ -831,7 +873,7 @@ bool CStackInstance::valid(bool allowUnrandomized) const
std::string CStackInstance::nodeName() const
{
std::ostringstream oss;
oss << "Stack of " << count << " of ";
oss << "Stack of " << getCount() << " of ";
if(getType())
oss << getType()->getNamePluralTextID();
else
@ -861,12 +903,13 @@ TerrainId CStackInstance::getNativeTerrain() const
return getFactionID().toEntity(LIBRARY)->getNativeTerrain();
}
TerrainId CStackInstance::getCurrentTerrain() const
{
assert(getArmy() != nullptr);
return getArmy()->getCurrentTerrain();
}
CreatureID CStackInstance::getCreatureID() const
{
if(getType())
@ -877,19 +920,19 @@ CreatureID CStackInstance::getCreatureID() const
std::string CStackInstance::getName() const
{
return (count > 1) ? getType()->getNamePluralTranslated() : getType()->getNameSingularTranslated();
return (getCount() > 1) ? getType()->getNamePluralTranslated() : getType()->getNameSingularTranslated();
}
ui64 CStackInstance::getPower() const
{
assert(getType());
return static_cast<ui64>(getType()->getAIValue()) * count;
return static_cast<ui64>(getType()->getAIValue()) * getCount();
}
ui64 CStackInstance::getMarketValue() const
{
assert(getType());
return getType()->getFullRecruitCost().marketValue() * count;
return getType()->getFullRecruitCost().marketValue() * getCount();
}
ArtBearer::ArtBearer CStackInstance::bearerType() const
@ -968,9 +1011,8 @@ CCommanderInstance::CCommanderInstance(IGameCallback *cb, const CreatureID & id)
, name("Commando")
{
alive = true;
experience = 0;
level = 1;
count = 1;
setCount(1);
setType(nullptr);
setNodeType (CBonusSystemNode::COMMANDER);
secondarySkills.resize (ECommander::SPELL_POWER + 1);
@ -988,15 +1030,14 @@ void CCommanderInstance::setAlive (bool Alive)
}
}
void CCommanderInstance::giveStackExp (TExpType exp)
bool CCommanderInstance::canGainExperience() const
{
if (alive)
experience += exp;
return alive && CStackInstance::canGainExperience();
}
int CCommanderInstance::getExpRank() const
{
return LIBRARY->heroh->level (experience);
return LIBRARY->heroh->level (getTotalExperience());
}
int CCommanderInstance::getLevel() const
@ -1020,7 +1061,7 @@ ArtBearer::ArtBearer CCommanderInstance::bearerType() const
bool CCommanderInstance::gainsLevel() const
{
return experience >= LIBRARY->heroh->reqExp(level + 1);
return getTotalExperience() >= LIBRARY->heroh->reqExp(level + 1);
}
//This constructor should be placed here to avoid side effects
@ -1062,6 +1103,12 @@ void CStackBasicDescriptor::setType(const CCreature * c)
typeID = c ? c->getId() : CreatureID();
}
void CStackBasicDescriptor::setCount(TQuantity newCount)
{
assert(newCount >= 0);
count = newCount;
}
bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r)
{
return l.typeID == r.typeID && l.count == r.count;

View File

@ -33,9 +33,9 @@ class JsonSerializeFormat;
class DLL_LINKAGE CStackBasicDescriptor
{
CreatureID typeID;
public:
TQuantity count = -1; //exact quantity or quantity ID from CCreature::getQuantityID when getting info about enemy army
public:
CStackBasicDescriptor();
CStackBasicDescriptor(const CreatureID & id, TQuantity Count);
CStackBasicDescriptor(const CCreature *c, TQuantity Count);
@ -47,6 +47,7 @@ public:
TQuantity getCount() const;
virtual void setType(const CCreature * c);
virtual void setCount(TQuantity amount);
friend bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r);
@ -75,9 +76,11 @@ class DLL_LINKAGE CStackInstance : public CBonusSystemNode, public CStackBasicDe
BonusValueCache nativeTerrain;
BonusValueCache initiative;
ObjectInstanceID armyInstanceID; //stack must be part of some army, army must be part of some object
CArmedInstance * armyInstance = nullptr; //stack must be part of some army, army must be part of some object
IGameCallback * getCallback() const final { return cb; }
TExpType totalExperience;//commander needs same amount of exp as hero
public:
struct RandomStackInfo
{
@ -89,9 +92,11 @@ public:
CArmedInstance * getArmy();
const CArmedInstance * getArmy() const; //stack must be part of some army, army must be part of some object
void setArmy(const CArmedInstance *ArmyObj);
void setArmy(CArmedInstance *ArmyObj);
TExpType experience;//commander needs same amount of exp as hero
TExpType getTotalExperience() const;
TExpType getAverageExperience() const;
virtual bool canGainExperience() const;
template <typename Handler> void serialize(Handler &h)
{
@ -99,17 +104,26 @@ public:
h & static_cast<CStackBasicDescriptor&>(*this);
h & static_cast<CArtifactSet&>(*this);
if (h.hasFeature(Handler::Version::STACK_INSTANCE_ARMY_FIX))
{
// no-op
}
if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
{
h & armyInstanceID;
ObjectInstanceID dummyID;
h & dummyID;
}
else
{
std::shared_ptr<CGObjectInstance> army;
h & army;
armyInstanceID = army->id;
}
h & experience;
h & totalExperience;
if (!h.hasFeature(Handler::Version::STACK_INSTANCE_EXPERIENCE_FIX))
{
totalExperience *= getCount();
}
}
void serializeJson(JsonSerializeFormat & handler);
@ -138,7 +152,12 @@ public:
void setType(const CreatureID & creID);
void setType(const CCreature * c) final;
virtual void giveStackExp(TExpType exp);
void setCount(TQuantity amount) final;
/// Gives specified amount of stack experience that will not be scaled by unit size
void giveAverageStackExperience(TExpType exp);
void giveTotalStackExperience(TExpType exp);
bool valid(bool allowUnrandomized) const;
ArtPlacementMap putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) override;//from CArtifactSet
void removeArtifact(const ArtifactPosition & pos) override;
@ -166,9 +185,9 @@ public:
CCommanderInstance(IGameCallback *cb);
CCommanderInstance(IGameCallback *cb, const CreatureID & id);
void setAlive (bool alive);
void giveStackExp (TExpType exp) override;
void levelUp ();
bool canGainExperience() const override;
bool gainsLevel() const; //true if commander has lower level than should upon his experience
ui64 getPower() const override {return 0;};
int getExpRank() const override;
@ -255,12 +274,26 @@ public:
void setStackCount(const SlotID & slot, TQuantity count); //stack must exist!
std::unique_ptr<CStackInstance> detachStack(const SlotID & slot); //removes stack from army but doesn't destroy it (so it can be moved somewhere else or safely deleted)
void setStackType(const SlotID & slot, const CreatureID & type);
void giveStackExp(TExpType exp);
void setStackExp(const SlotID & slot, TExpType exp);
//derivative
void eraseStack(const SlotID & slot); //slot must be occupied
/// Give specified amount of experience to all units in army
/// Amount of granted experience is scaled by unit stack size
void giveAverageStackExperience(TExpType exp);
/// Give specified amount of experience to unit in specified slot
/// Amount of granted experience is not scaled by unit stack size
void giveTotalStackExperience(const SlotID & slot, TExpType exp);
/// Erased stack from specified slot. Slot must be non-empty
void eraseStack(const SlotID & slot);
/// Joins stack into stack that occupies targeted slot.
/// Slot must be non-empty and contain same creature type
void joinStack(const SlotID & slot, std::unique_ptr<CStackInstance> stack); //adds new stack to the existing stack of the same type
/// Splits off some units of specified stack and returns newly created stack
/// Slot must be non-empty and contain more units that split quantity
std::unique_ptr<CStackInstance> splitStack(const SlotID & slot, TQuantity toSplit);
void changeStackCount(const SlotID & slot, TQuantity toAdd); //stack must exist!
bool setCreature (SlotID slot, CreatureID type, TQuantity quantity) override; //replaces creature in stack; slots 0 to 6, if quantity=0 erases stack
void setToArmy(CSimpleArmy &src); //erases all our army and moves stacks from src to us; src MUST NOT be an armed object! WARNING: use it wisely. Or better do not use at all.
@ -269,7 +302,8 @@ public:
CStackInstance * getStackPtr(const SlotID & slot) const; //if stack doesn't exist, returns nullptr
const CCreature * getCreature(const SlotID & slot) const; //workaround of map issue;
int getStackCount(const SlotID & slot) const;
TExpType getStackExperience(const SlotID & slot) const;
TExpType getStackTotalExperience(const SlotID & slot) const;
TExpType getStackAverageExperience(const SlotID & slot) const;
SlotID findStack(const CStackInstance *stack) const; //-1 if none
SlotID getSlotFor(const CreatureID & creature, ui32 slotsAmount = GameConstants::ARMY_SIZE) const; //returns -1 if no slot available
SlotID getSlotFor(const CCreature *c, ui32 slotsAmount = GameConstants::ARMY_SIZE) const; //returns -1 if no slot available
@ -311,7 +345,6 @@ public:
{
return !stacks.empty();
}
void sweep();
};
VCMI_LIB_NAMESPACE_END

View File

@ -360,13 +360,13 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
doBasicDisguise(info);
for(auto & elem : info.army)
elem.second.count = 0;
elem.second.setCount(0);
};
auto doExpertDisguise = [this,h](InfoAboutHero & info)
{
for(auto & elem : info.army)
elem.second.count = 0;
elem.second.setCount(0);
const auto factionIndex = getStartInfo()->playerInfos.at(h->tempOwner).castle;

View File

@ -30,7 +30,7 @@ CStack::CStack(const CStackInstance * Base, const PlayerColor & O, int I, Battle
base(Base),
ID(I),
typeID(Base->getId()),
baseAmount(Base->count),
baseAmount(Base->getCount()),
owner(O),
slot(S),
side(Side)
@ -51,7 +51,7 @@ CStack::CStack(const CStackBasicDescriptor * stack, const PlayerColor & O, int I
CBonusSystemNode(STACK_BATTLE),
ID(I),
typeID(stack->getId()),
baseAmount(stack->count),
baseAmount(stack->getCount()),
owner(O),
slot(S),
side(Side)

View File

@ -207,6 +207,8 @@ bool CArtifact::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, b
auto possibleSlot = ArtifactUtils::getArtAnyPosition(&fittingSet, art->getId());
if(ArtifactUtils::isSlotEquipment(possibleSlot))
{
if (fittingSet.getSlot(possibleSlot) == nullptr)
fittingSet.artifactsWorn.insert(std::make_pair(possibleSlot, ArtSlotInfo(fittingSet.cb)));
fittingSet.lockSlot(possibleSlot);
}
else

View File

@ -1304,7 +1304,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
{
for(const auto & elem : ai->Slots()) //iterate through army
if(elem.second->getId() == condition.objectType.as<CreatureID>()) //it's searched creature
total += elem.second->count;
total += elem.second->getCount();
}
}
return total >= condition.value;
@ -1766,13 +1766,13 @@ void CGameState::loadGame(CLoadFile & file)
logGlobal->info("Loading game state...");
CMapHeader dummyHeader;
auto startInfo = std::make_shared<StartInfo>();
StartInfo dummyStartInfo;
ActiveModsInSaveList dummyActiveMods;
file.load(dummyHeader);
if (file.hasFeature(ESerializationVersion::NO_RAW_POINTERS_IN_SERIALIZER))
{
file.load(startInfo);
file.load(dummyStartInfo);
file.load(dummyActiveMods);
file.load(*this);
}
@ -1781,7 +1781,7 @@ void CGameState::loadGame(CLoadFile & file)
bool dummyA = false;
uint32_t dummyB = 0;
uint16_t dummyC = 0;
file.load(startInfo);
file.load(dummyStartInfo);
file.load(dummyActiveMods);
file.load(dummyA);
file.load(dummyB);

View File

@ -43,12 +43,12 @@ int ArmyDescriptor::getStrength() const
if(isDetailed)
{
for(const auto & elem : *this)
ret += elem.second.getType()->getAIValue() * elem.second.count;
ret += elem.second.getType()->getAIValue() * elem.second.getCount();
}
else
{
for(const auto & elem : *this)
ret += elem.second.getType()->getAIValue() * CCreature::estimateCreatureCount(elem.second.count);
ret += elem.second.getType()->getAIValue() * CCreature::estimateCreatureCount(elem.second.getCount());
}
return static_cast<int>(ret);
}

View File

@ -504,7 +504,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
throw JsonRandomizationException("Invalid creature picked!", value);
stack.setType(pickedCreature.toCreature());
stack.count = loadValue(value, rng, variables);
stack.setCount(loadValue(value, rng, variables));
if (!value["upgradeChance"].isNull() && !stack.getCreature()->upgrades.empty())
{
if (int(value["upgradeChance"].Float()) > rng.nextInt(99)) // select random upgrade

View File

@ -104,7 +104,7 @@ void DwellingInstanceConstructor::randomizeObject(CGDwelling * dwelling, vstd::R
JsonRandom::Variables emptyVariables;
for(auto & stack : randomizer.loadCreatures(guards, rng, emptyVariables))
{
dwelling->putStack(SlotID(dwelling->stacksCount()), std::make_unique<CStackInstance>(dwelling->cb, stack.getId(), stack.count));
dwelling->putStack(SlotID(dwelling->stacksCount()), std::make_unique<CStackInstance>(dwelling->cb, stack.getId(), stack.getCount()));
}
}
else if (dwelling->ID == Obj::CREATURE_GENERATOR1 || dwelling->ID == Obj::CREATURE_GENERATOR4)

View File

@ -175,7 +175,7 @@ void CArmedInstance::attachUnitsToArmy()
assert(getArmy() != nullptr);
for(const auto & elem : stacks)
elem.second->attachTo(*getArmy());
elem.second->setArmy(getArmy());
}
const IBonusBearer* CArmedInstance::getBonusBearer() const

View File

@ -55,9 +55,9 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
if(hero->hasVisions(this, BonusCustomSubtype::visionsMonsters))
{
MetaString ms;
ms.appendNumber(stacks.begin()->second->count);
ms.appendNumber(stacks.begin()->second->getCount());
ms.appendRawString(" ");
ms.appendName(getCreatureID(), stacks.begin()->second->count);
ms.appendName(getCreatureID(), stacks.begin()->second->getCount());
return ms.toString();
}
else
@ -288,20 +288,19 @@ void CGCreature::initObj(vstd::RNG & rand)
}
stacks[SlotID(0)]->setType(getCreature());
TQuantity &amount = stacks[SlotID(0)]->count;
const Creature * c = getCreature();
if(amount == 0)
if(stacks[SlotID(0)]->getCount() == 0)
{
amount = rand.nextInt(c->getAdvMapAmountMin(), c->getAdvMapAmountMax());
stacks[SlotID(0)]->setCount(rand.nextInt(c->getAdvMapAmountMin(), c->getAdvMapAmountMax()));
if(amount == 0) //armies with 0 creatures are illegal
if(stacks[SlotID(0)]->getCount() == 0) //armies with 0 creatures are illegal
{
logGlobal->warn("Stack cannot have 0 creatures. Check properties of %s", c->getJsonKey());
amount = 1;
stacks[SlotID(0)]->setCount(1);
}
}
temppower = stacks[SlotID(0)]->count * static_cast<int64_t>(1000);
temppower = stacks[SlotID(0)]->getCount() * static_cast<int64_t>(1000);
refusedJoining = false;
}
@ -309,7 +308,7 @@ void CGCreature::newTurn(vstd::RNG & rand) const
{//Works only for stacks of single type of size up to 2 millions
if (!notGrowingTeam)
{
if (stacks.begin()->second->count < cb->getSettings().getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP) && cb->getDate(Date::DAY_OF_WEEK) == 1 && cb->getDate(Date::DAY) > 1)
if (stacks.begin()->second->getCount() < cb->getSettings().getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP) && cb->getDate(Date::DAY_OF_WEEK) == 1 && cb->getDate(Date::DAY) > 1)
{
ui32 power = static_cast<ui32>(temppower * (100 + cb->getSettings().getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT)) / 100);
cb->setObjPropertyValue(id, ObjProperty::MONSTER_COUNT, std::min<uint32_t>(power / 1000, cb->getSettings().getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP))); //set new amount
@ -324,13 +323,13 @@ void CGCreature::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
switch (what)
{
case ObjProperty::MONSTER_COUNT:
stacks[SlotID(0)]->count = identifier.getNum();
stacks[SlotID(0)]->setCount(identifier.getNum());
break;
case ObjProperty::MONSTER_POWER:
temppower = identifier.getNum();
break;
case ObjProperty::MONSTER_EXP:
giveStackExp(identifier.getNum());
giveAverageStackExperience(identifier.getNum());
break;
case ObjProperty::MONSTER_REFUSED_JOIN:
refusedJoining = identifier.getNum();
@ -367,8 +366,8 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
bool isOurDowngrade = vstd::contains(elem.second->getCreature()->upgrades, getCreatureID());
if(isOurUpgrade || isOurDowngrade)
count += elem.second->count;
totalCount += elem.second->count;
count += elem.second->getCount();
totalCount += elem.second->getCount();
}
int sympathy = 0; // 0 if hero have no similar creatures
@ -455,7 +454,7 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co
giveReward(h);
for(auto & stack : this->stacks)
stack.second->count = getJoiningAmount();
stack.second->setCount(getJoiningAmount());
cb->tryJoiningArmy(this, h, true, true);
}
@ -538,7 +537,7 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
//first stack has to be at slot 0 -> if original one got killed, move there first remaining stack
if(!hasStackAtSlot(SlotID(0)))
cb->moveStack(StackLocation(id, stacks.begin()->first), StackLocation(id, SlotID(0)), stacks.begin()->second->count);
cb->moveStack(StackLocation(id, stacks.begin()->first), StackLocation(id, SlotID(0)), stacks.begin()->second->getCount());
while(stacks.size() > 1) //hopefully that's enough
{
@ -549,10 +548,10 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
if(slot == i->first) //no reason to move stack to its own slot
break;
else
cb->moveStack(StackLocation(id, i->first), StackLocation(id, slot), i->second->count);
cb->moveStack(StackLocation(id, i->first), StackLocation(id, slot), i->second->getCount());
}
cb->setObjPropertyValue(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties
cb->setObjPropertyValue(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->getCount() * 1000); //remember casualties
}
}
@ -615,7 +614,7 @@ int CGCreature::getNumberOfStacks(const CGHeroInstance *hero) const
else if (R4 >= 80)
split += 1;
vstd::amin(split, getStack(SlotID(0)).count); //can't divide into more stacks than creatures total
vstd::amin(split, getStack(SlotID(0)).getCount()); //can't divide into more stacks than creatures total
vstd::amin(split, 7); //can't have more than 7 stacks
return split;
@ -664,7 +663,7 @@ void CGCreature::serializeJsonOptions(JsonSerializeFormat & handler)
{
if(hasStackAtSlot(SlotID(0)))
{
si32 amount = getStack(SlotID(0)).count;
si32 amount = getStack(SlotID(0)).getCount();
handler.serializeInt("amount", amount, 0);
}
}
@ -673,7 +672,7 @@ void CGCreature::serializeJsonOptions(JsonSerializeFormat & handler)
si32 amount = 0;
handler.serializeInt("amount", amount);
auto hlp = std::make_unique<CStackInstance>(cb);
hlp->count = amount;
hlp->setCount(amount);
//type will be set during initialization
putStack(SlotID(0), std::move(hlp));
}

View File

@ -468,7 +468,7 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
{
commander = std::make_unique<CCommanderInstance>(cb, getHeroClass()->commander);
commander->setArmy(getArmy()); //TODO: separate function for setting commanders
commander->giveStackExp (exp); //after our exp is set
commander->giveTotalStackExperience(exp); //after our exp is set
}
skillsInfo = SecondarySkillsInfo();
@ -1610,6 +1610,11 @@ void CGHeroInstance::levelUp(const std::vector<SecondarySkill> & skills)
nodeHasChanged();
}
void CGHeroInstance::attachCommanderToArmy()
{
commander->setArmy(this);
}
void CGHeroInstance::levelUpAutomatically(vstd::RNG & rand)
{
while(gainsLevel())

View File

@ -352,6 +352,7 @@ protected:
private:
void levelUpAutomatically(vstd::RNG & rand);
void attachCommanderToArmy();
public:
std::string getHeroTypeName() const;
@ -396,6 +397,9 @@ public:
h & commander;
h & visitedObjects;
if(!h.saving && h.loadingGamestate)
attachCommanderToArmy();
}
};

View File

@ -138,7 +138,7 @@ void CGPandoraBox::grantRewardWithMessage(const CGHeroInstance * h, int index, b
loot.replaceName(c);
}
if(vi.reward.creatures.size() == 1 && vi.reward.creatures[0].count == 1)
if(vi.reward.creatures.size() == 1 && vi.reward.creatures[0].getCount() == 1)
txt.appendLocalString(EMetaText::ADVOB_TXT, 185);
else
txt.appendLocalString(EMetaText::ADVOB_TXT, 186);

View File

@ -644,7 +644,7 @@ void CGTownInstance::clearArmy() const
{
while(!stacks.empty())
{
cb->eraseStack(StackLocation(id, stacks.begin()->first));
cb->eraseStack(StackLocation(id, stacks.begin()->first), true);
}
}

View File

@ -112,15 +112,15 @@ bool CQuest::checkMissionArmy(const CQuest * q, const CCreatureSet * army)
{
if(it->second->getType() == cre->getType())
{
count += it->second->count;
count += it->second->getCount();
slotsCount++;
}
}
if(static_cast<TQuantity>(count) < cre->count) //not enough creatures of this kind
if(static_cast<TQuantity>(count) < cre->getCount()) //not enough creatures of this kind
return false;
hasExtraCreatures |= static_cast<TQuantity>(count) > cre->count;
hasExtraCreatures |= static_cast<TQuantity>(count) > cre->getCount();
}
return hasExtraCreatures || slotsCount < army->Slots().size();

View File

@ -609,7 +609,7 @@ void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer
bool CGWhirlpool::isProtected(const CGHeroInstance * h)
{
return h->hasBonusOfType(BonusType::WHIRLPOOL_PROTECTION)
|| (h->stacksCount() == 1 && h->Slots().begin()->second->count == 1)
|| (h->stacksCount() == 1 && h->Slots().begin()->second->getCount() == 1)
|| (h->stacksCount() == 0 && h->getCommander() && h->getCommander()->alive);
}
@ -1040,11 +1040,11 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
for (auto i = h->Slots().begin(); i != h->Slots().end(); i++)
{
// 1-sized stacks are not affected by sirens
if (i->second->count == 1)
if (i->second->getCount() == 1)
continue;
// tested H3 behavior: 30% (rounded up) of stack drowns
TQuantity drown = std::ceil(i->second->count * 0.3);
TQuantity drown = std::ceil(i->second->getCount() * 0.3);
if(drown)
{

View File

@ -1191,7 +1191,7 @@ std::shared_ptr<CGObjectInstance> CMapLoaderH3M::readMonster(const int3 & mapPos
}
auto hlp = std::make_unique<CStackInstance>(map->cb);
hlp->count = reader->readUInt16();
hlp->setCount(reader->readUInt16());
//type will be set during initialization
object->putStack(SlotID(0), std::move(hlp));
@ -1975,7 +1975,7 @@ void CMapLoaderH3M::readCreatureSet(CArmedInstance * out, const ObjectInstanceID
continue;
auto result = std::make_unique<CStackInstance>(map->cb);
result->count = count;
result->setCount(count);
if(creatureID < CreatureID::NONE)
{
@ -2427,7 +2427,7 @@ EQuestMission CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & positi
for(size_t hh = 0; hh < typeNumber; ++hh)
{
guard->getQuest().mission.creatures[hh].setType(reader->readCreature().toCreature());
guard->getQuest().mission.creatures[hh].count = reader->readUInt16();
guard->getQuest().mission.creatures[hh].setCount(reader->readUInt16());
}
break;
}

View File

@ -77,7 +77,6 @@ public:
virtual void visitInsertNewStack(InsertNewStack & pack) {}
virtual void visitRebalanceStacks(RebalanceStacks & pack) {}
virtual void visitBulkRebalanceStacks(BulkRebalanceStacks & pack) {}
virtual void visitBulkSmartRebalanceStacks(BulkSmartRebalanceStacks & pack) {}
virtual void visitPutArtifact(PutArtifact & pack) {}
virtual void visitEraseArtifact(BulkEraseArtifacts & pack) {}
virtual void visitBulkMoveArtifacts(BulkMoveArtifacts & pack) {}
@ -124,7 +123,7 @@ public:
virtual void visitBulkMoveArmy(BulkMoveArmy & pack) {}
virtual void visitBulkSplitStack(BulkSplitStack & pack) {}
virtual void visitBulkMergeStacks(BulkMergeStacks & pack) {}
virtual void visitBulkSmartSplitStack(BulkSmartSplitStack & pack) {}
virtual void visitBulkSmartSplitStack(BulkSplitAndRebalanceStack & pack) {}
virtual void visitDisbandCreature(DisbandCreature & pack) {}
virtual void visitBuildStructure(BuildStructure & pack) {}
virtual void visitVisitTownBuilding(VisitTownBuilding & pack) {}

View File

@ -333,11 +333,6 @@ void BulkRebalanceStacks::visitTyped(ICPackVisitor & visitor)
visitor.visitBulkRebalanceStacks(*this);
}
void BulkSmartRebalanceStacks::visitTyped(ICPackVisitor & visitor)
{
visitor.visitBulkSmartRebalanceStacks(*this);
}
void PutArtifact::visitTyped(ICPackVisitor & visitor)
{
visitor.visitPutArtifact(*this);
@ -573,7 +568,7 @@ void BulkMergeStacks::visitTyped(ICPackVisitor & visitor)
visitor.visitBulkMergeStacks(*this);
}
void BulkSmartSplitStack::visitTyped(ICPackVisitor & visitor)
void BulkSplitAndRebalanceStack::visitTyped(ICPackVisitor & visitor)
{
visitor.visitBulkSmartSplitStack(*this);
}
@ -895,7 +890,7 @@ void SetCommanderProperty::applyGs(CGameState *gs)
commander->setAlive(false);
break;
case EXPERIENCE:
commander->giveStackExp(amount); //TODO: allow setting exp for stacks via netpacks
commander->giveTotalStackExperience(amount);
commander->nodeHasChanged();
break;
}
@ -1560,17 +1555,15 @@ void RebalanceStacks::applyGs(CGameState *gs)
StackLocation src(srcObj->id, srcSlot);
StackLocation dst(dstObj->id, dstSlot);
const CCreature * srcType = srcObj->getCreature(src.slot);
[[maybe_unused]] const CCreature * srcType = srcObj->getCreature(src.slot);
const CCreature * dstType = dstObj->getCreature(dst.slot);
TQuantity srcCount = srcObj->getStackCount(src.slot);
bool stackExp = gs->getSettings().getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE);
if(srcCount == count) //moving whole stack
{
const auto c = dstObj->getCreature(dst.slot);
if(c) //stack at dest -> merge
if(dstType) //stack at dest -> merge
{
assert(c == srcType);
assert(dstType == srcType);
const auto srcHero = dynamic_cast<CGHeroInstance*>(srcObj);
const auto dstHero = dynamic_cast<CGHeroInstance*>(dstObj);
@ -1609,50 +1602,28 @@ void RebalanceStacks::applyGs(CGameState *gs)
gs->getMap().moveArtifactInstance(*srcStack, ArtifactPosition::CREATURE_SLOT, *dstStack, ArtifactPosition::CREATURE_SLOT);
}
}
if (stackExp)
{
ui64 totalExp = srcCount * srcObj->getStackExperience(src.slot) + dstObj->getStackCount(dst.slot) * dstObj->getStackExperience(dst.slot);
srcObj->eraseStack(src.slot);
dstObj->changeStackCount(dst.slot, count);
dstObj->setStackExp(dst.slot, totalExp /(dstObj->getStackCount(dst.slot))); //mean
}
else
{
srcObj->eraseStack(src.slot);
dstObj->changeStackCount(dst.slot, count);
}
auto movedStack = srcObj->detachStack(src.slot);
dstObj->joinStack(dst.slot, std::move(movedStack));
}
else //move stack to an empty slot, no exp change needed
else
{
auto stackDetached = srcObj->detachStack(src.slot);
dstObj->putStack(dst.slot, std::move(stackDetached));
auto movedStack = srcObj->detachStack(src.slot);
dstObj->putStack(dst.slot, std::move(movedStack));
}
}
else
{
[[maybe_unused]] const CCreature *c = dstObj->getCreature(dst.slot);
if(c) //stack at dest -> rebalance
auto movedStack = srcObj->splitStack(src.slot, count);
if(dstType) //stack at dest -> rebalance
{
assert(c == srcType);
if (stackExp)
{
ui64 totalExp = srcCount * srcObj->getStackExperience(src.slot) + dstObj->getStackCount(dst.slot) * dstObj->getStackExperience(dst.slot);
srcObj->changeStackCount(src.slot, -count);
dstObj->changeStackCount(dst.slot, count);
dstObj->setStackExp(dst.slot, totalExp /(srcObj->getStackCount(src.slot) + dstObj->getStackCount(dst.slot))); //mean
}
else
{
srcObj->changeStackCount(src.slot, -count);
dstObj->changeStackCount(dst.slot, count);
}
assert(dstType == srcType);
dstObj->joinStack(dst.slot, std::move(movedStack));
}
else //split stack to an empty slot
else //move new stack to an empty slot
{
srcObj->changeStackCount(src.slot, -count);
dstObj->addToSlot(dst.slot, srcType->getId(), count, false);
if (stackExp)
dstObj->setStackExp(dst.slot, srcObj->getStackExperience(src.slot));
dstObj->putStack(dst.slot, std::move(movedStack));
}
}
@ -1667,15 +1638,6 @@ void BulkRebalanceStacks::applyGs(CGameState *gs)
move.applyGs(gs);
}
void BulkSmartRebalanceStacks::applyGs(CGameState *gs)
{
for(auto & move : moves)
move.applyGs(gs);
for(auto & change : changes)
change.applyGs(gs);
}
void PutArtifact::applyGs(CGameState *gs)
{
auto art = gs->getArtInstance(id);
@ -2094,15 +2056,10 @@ void BattleResultAccepted::applyGs(CGameState *gs)
if(gs->getSettings().getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
{
if(const auto attackerArmy = gs->getArmyInstance(heroResult[BattleSide::ATTACKER].armyID))
{
attackerArmy->giveStackExp(heroResult[BattleSide::ATTACKER].exp);
attackerArmy->nodeHasChanged();
}
attackerArmy->giveAverageStackExperience(heroResult[BattleSide::ATTACKER].exp);
if(const auto defenderArmy = gs->getArmyInstance(heroResult[BattleSide::DEFENDER].armyID))
{
defenderArmy->giveStackExp(heroResult[BattleSide::DEFENDER].exp);
defenderArmy->nodeHasChanged();
}
defenderArmy->giveAverageStackExperience(heroResult[BattleSide::DEFENDER].exp);
}
}

View File

@ -968,22 +968,6 @@ struct DLL_LINKAGE BulkRebalanceStacks : CGarrisonOperationPack
}
};
struct DLL_LINKAGE BulkSmartRebalanceStacks : CGarrisonOperationPack
{
std::vector<RebalanceStacks> moves;
std::vector<ChangeStackCount> changes;
void applyGs(CGameState * gs) override;
void visitTyped(ICPackVisitor & visitor) override;
template <typename Handler>
void serialize(Handler & h)
{
h & moves;
h & changes;
}
};
struct DLL_LINKAGE CArtifactOperationPack : CPackForClient
{
};

View File

@ -214,14 +214,14 @@ struct DLL_LINKAGE BulkMergeStacks : public CPackForServer
}
};
struct DLL_LINKAGE BulkSmartSplitStack : public CPackForServer
struct DLL_LINKAGE BulkSplitAndRebalanceStack : public CPackForServer
{
SlotID src;
ObjectInstanceID srcOwner;
BulkSmartSplitStack() = default;
BulkSplitAndRebalanceStack() = default;
BulkSmartSplitStack(const ObjectInstanceID & srcOwner, const SlotID & src)
BulkSplitAndRebalanceStack(const ObjectInstanceID & srcOwner, const SlotID & src)
: src(src)
, srcOwner(srcOwner)
{

View File

@ -197,7 +197,7 @@ void Rewardable::Interface::grantRewardAfterLevelup(const Rewardable::VisitInfo
{
CCreatureSet creatures;
for(const auto & crea : info.reward.creatures)
creatures.addToSlot(creatures.getFreeSlot(), std::make_unique<CStackInstance>(cb, crea.getId(), crea.count));
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
cb->giveCreatures(army, hero, creatures, false);

View File

@ -85,9 +85,9 @@ bool Rewardable::Limiter::heroAllowed(const CGHeroInstance * hero) const
{
const auto & heroStack = slot.second;
if (heroStack->getType() == reqStack.getType())
count += heroStack->count;
count += heroStack->getCount();
}
if (count < reqStack.count) //not enough creatures of this kind
if (count < reqStack.getCount()) //not enough creatures of this kind
return false;
}
@ -233,7 +233,7 @@ void Rewardable::Limiter::loadComponents(std::vector<Component> & comps,
comps.emplace_back(ComponentType::SPELL, entry);
for(const auto & entry : creatures)
comps.emplace_back(ComponentType::CREATURE, entry.getId(), entry.count);
comps.emplace_back(ComponentType::CREATURE, entry.getId(), entry.getCount());
for(const auto & entry : players)
comps.emplace_back(ComponentType::FLAG, entry);

View File

@ -121,7 +121,7 @@ void Rewardable::Reward::loadComponents(std::vector<Component> & comps, const CG
}
for(const auto & entry : creatures)
comps.emplace_back(ComponentType::CREATURE, entry.getId(), entry.count);
comps.emplace_back(ComponentType::CREATURE, entry.getId(), entry.getCount());
for (size_t i=0; i<resources.size(); i++)
{

View File

@ -36,7 +36,10 @@ enum class ESerializationVersion : int32_t
MAP_HEADER_DISPOSED_HEROES, // map header contains disposed heroes list
NO_RAW_POINTERS_IN_SERIALIZER, // large rework that removed all non-owning pointers from serializer
STORE_UID_COUNTER_IN_CMAP, // fix crash caused by conflicting instanceName after loading game
STACK_INSTANCE_EXPERIENCE_FIX, // stack experience is stored as total, not as average
STACK_INSTANCE_ARMY_FIX, // remove serialization of army that owns stack instance
STORE_UID_COUNTER_IN_CMAP, // fix crash caused by conflicting instanceName after loading game
CURRENT = STORE_UID_COUNTER_IN_CMAP,
};

View File

@ -226,7 +226,6 @@ void registerTypes(Serializer &s)
s.template registerType<BulkMoveArtifacts>(173);
s.template registerType<PlayerMessageClient>(174);
s.template registerType<BulkRebalanceStacks>(175);
s.template registerType<BulkSmartRebalanceStacks>(176);
s.template registerType<SetRewardableConfiguration>(177);
s.template registerType<CPackForServer>(179);
s.template registerType<EndTurn>(180);
@ -255,7 +254,7 @@ void registerTypes(Serializer &s)
s.template registerType<PlayerMessage>(203);
s.template registerType<BulkSplitStack>(204);
s.template registerType<BulkMergeStacks>(205);
s.template registerType<BulkSmartSplitStack>(206);
s.template registerType<BulkSplitAndRebalanceStack>(206);
s.template registerType<BulkMoveArmy>(207);
s.template registerType<BulkExchangeArtifacts>(208);
s.template registerType<ManageBackpackArtifacts>(209);

View File

@ -446,7 +446,7 @@ void MetaString::replaceName(const CreatureID & id, TQuantity count) //adds sing
void MetaString::replaceName(const CStackBasicDescriptor & stack)
{
replaceName(stack.getId(), stack.count);
replaceName(stack.getId(), stack.getCount());
}
VCMI_LIB_NAMESPACE_END

View File

@ -155,8 +155,8 @@ void ArmyDelegate::updateModelData(QAbstractItemModel * model, const QModelIndex
QStringList textList;
for(const auto & [_, stack] : army.stacks)
{
if(stack->count != 0 && stack->getCreature() != nullptr)
textList += QString("%1 %2").arg(stack->count).arg(QString::fromStdString(stack->getCreature()->getNamePluralTranslated()));
if(stack->getCount() != 0 && stack->getCreature() != nullptr)
textList += QString("%1 %2").arg(stack->getCount()).arg(QString::fromStdString(stack->getCreature()->getNamePluralTranslated()));
}
setModelTextData(model, index, textList);

View File

@ -423,7 +423,7 @@ void Inspector::updateProperties(CGCreature * o)
addProperty(QObject::tr("Not growing"), o->notGrowingTeam, false);
addProperty(QObject::tr("Artifact reward"), o->gainedArtifact); //TODO: implement in setProperty
addProperty(QObject::tr("Army"), PropertyEditorPlaceholder(), true);
addProperty(QObject::tr("Amount"), o->stacks[SlotID(0)]->count, false);
addProperty(QObject::tr("Amount"), o->stacks[SlotID(0)]->getCount(), false);
//addProperty(QObject::tr("Resources reward"), o->resources); //TODO: implement in setProperty
}
@ -763,7 +763,7 @@ void Inspector::setProperty(CGCreature * o, const QString & key, const QVariant
if(key == QObject::tr("Not growing"))
o->notGrowingTeam = value.toBool();
if(key == QObject::tr("Amount"))
o->stacks[SlotID(0)]->count = value.toString().toInt();
o->stacks[SlotID(0)]->setCount(value.toString().toInt());
}
void Inspector::setProperty(CGSeerHut * o, const QString & key, const QVariant & value)

View File

@ -175,7 +175,7 @@ void QuestWidget::obtainData()
{
int index = i.getType()->getIndex();
ui->lCreatureId->setCurrentIndex(index);
ui->lCreatureAmount->setValue(i.count);
ui->lCreatureAmount->setValue(i.getCount());
onCreatureAdd(ui->lCreatures, ui->lCreatureId, ui->lCreatureAmount);
}
for(auto & i : quest.mission.heroes)
@ -489,7 +489,7 @@ void QuestDelegate::updateModelData(QAbstractItemModel * model, const QModelInde
QStringList creaturesList;
for(const auto & creature : quest.mission.creatures)
{
creaturesList += QString("%1 %2").arg(creature.count).arg(QString::fromStdString(creature.getType()->getNamePluralTranslated()));
creaturesList += QString("%1 %2").arg(creature.getCount()).arg(QString::fromStdString(creature.getType()->getNamePluralTranslated()));
}
textList += QObject::tr("Creatures: %1").arg(creaturesList.join(", "));

View File

@ -466,7 +466,7 @@ void RewardsWidget::loadCurrentVisitInfo(int index)
{
int index = i.getType()->getIndex();
ui->rCreatureId->setCurrentIndex(index);
ui->rCreatureAmount->setValue(i.count);
ui->rCreatureAmount->setValue(i.getCount());
onCreatureAdd(ui->rCreatures, ui->rCreatureId, ui->rCreatureAmount);
}
@ -534,7 +534,7 @@ void RewardsWidget::loadCurrentVisitInfo(int index)
{
int index = i.getType()->getIndex();
ui->lCreatureId->setCurrentIndex(index);
ui->lCreatureAmount->setValue(i.count);
ui->lCreatureAmount->setValue(i.getCount());
onCreatureAdd(ui->lCreatures, ui->lCreatureId, ui->lCreatureAmount);
}
@ -806,7 +806,7 @@ void RewardsDelegate::updateModelData(QAbstractItemModel * model, const QModelIn
QStringList creaturesList;
for (auto & creature : vinfo.reward.creatures)
{
creaturesList += QString("%1 %2").arg(creature.count).arg(QString::fromStdString(creature.getType()->getNamePluralTranslated()));
creaturesList += QString("%1 %2").arg(creature.getCount()).arg(QString::fromStdString(creature.getType()->getNamePluralTranslated()));
}
textList += QObject::tr("Creatures: %1").arg(creaturesList.join(", "));
if (vinfo.reward.spellCast.first != SpellID::NONE)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1151,7 +1151,7 @@ void CGameHandler::giveCreatures(const CArmedInstance *obj, const CGHeroInstance
//first we move creatures to give to make them army of object-source
for (auto & elem : creatures.Slots())
{
addToSlot(StackLocation(obj->id, obj->getSlotFor(elem.second->getCreature())), elem.second->getCreature(), elem.second->count);
addToSlot(StackLocation(obj->id, obj->getSlotFor(elem.second->getCreature())), elem.second->getCreature(), elem.second->getCount());
}
tryJoiningArmy(obj, h, remove, true);
@ -1167,14 +1167,14 @@ void CGameHandler::takeCreatures(ObjectInstanceID objid, const std::vector<CStac
for (CStackBasicDescriptor &sbd : cres)
{
TQuantity collected = 0;
while(collected < sbd.count)
while(collected < sbd.getCount())
{
bool foundSth = false;
for (auto i = obj->Slots().begin(); i != obj->Slots().end(); i++)
{
if (i->second->getType() == sbd.getType())
{
TQuantity take = std::min(sbd.count - collected, i->second->count); //collect as much cres as we can
TQuantity take = std::min(sbd.getCount() - collected, i->second->getCount()); //collect as much cres as we can
changeStackCount(StackLocation(obj->id, i->first), -take, false);
collected += take;
foundSth = true;
@ -1789,7 +1789,7 @@ bool CGameHandler::bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destA
return true;
}
bool CGameHandler::bulkSmartSplitStack(SlotID slotSrc, ObjectInstanceID srcOwner)
bool CGameHandler::bulkSplitAndRebalanceStack(SlotID slotSrc, ObjectInstanceID srcOwner)
{
if(!slotSrc.validSlot() && complain(complainInvalidSlot))
return false;
@ -1811,8 +1811,8 @@ bool CGameHandler::bulkSmartSplitStack(SlotID slotSrc, ObjectInstanceID srcOwner
if(freeSlot == SlotID() && creatureSet.isCreatureBalanced(currentCreature))
return true;
auto creatureSlots = creatureSet.getCreatureSlots(currentCreature, SlotID(-1), 1); // Ignore slots where's only 1 creature, don't ignore slotSrc
TQuantity totalCreatures = 0;
auto creatureSlots = creatureSet.getCreatureSlots(currentCreature, slotSrc, 1); // Ignore slots where's only 1 creature
TQuantity totalCreatures = creatureSet.getStackCount(slotSrc);
for(auto slot : creatureSlots)
totalCreatures += creatureSet.getStackCount(slot);
@ -1820,53 +1820,60 @@ bool CGameHandler::bulkSmartSplitStack(SlotID slotSrc, ObjectInstanceID srcOwner
if(totalCreatures <= 1 && complain("Total creatures number is invalid"))
return false;
if(freeSlot != SlotID())
creatureSlots.push_back(freeSlot);
BulkRebalanceStacks bulkSRS;
if(creatureSlots.empty() && complain("No available slots for smart rebalancing"))
return false;
// 1) merge all but one creatures back into source slot
// single creature needs to be kept, to avoid stack artifact dropping to hero backpack
for(auto slot : creatureSlots)
{
RebalanceStacks rs;
rs.srcArmy = army->id;
rs.dstArmy = army->id;
rs.srcSlot = slot;
rs.dstSlot = slotSrc;
rs.count = creatureSet.getStackCount(slot) - 1;
const auto totalCreatureSlots = creatureSlots.size();
const auto rem = totalCreatures % totalCreatureSlots;
const auto quotient = totalCreatures / totalCreatureSlots;
// totalCreatures == rem * (quotient + 1) + (totalCreatureSlots - rem) * quotient;
// Proof: r(q+1)+(s-r)q = rq+r+qs-rq = r+qs = total, where total/s = q+r/s
BulkSmartRebalanceStacks bulkSRS;
if (rs.count > 0)
bulkSRS.moves.push_back(rs);
}
// 2) split off single creature into new slot, if any
// strictly speaking, not needed, but more convenient
if(freeSlot != SlotID())
{
RebalanceStacks rs;
rs.srcArmy = rs.dstArmy = army->id;
rs.srcArmy = army->id;
rs.dstArmy = army->id;
rs.srcSlot = slotSrc;
rs.dstSlot = freeSlot;
rs.count = 1;
bulkSRS.moves.push_back(rs);
}
auto currSlot = 0;
auto check = 0;
creatureSlots.push_back(freeSlot);
}
if(creatureSlots.empty() && complain("No available slots for smart rebalancing"))
return false;
int slotsLeft = creatureSlots.size() + 1; // + srcSlot
TQuantity unitsToMove = totalCreatures - slotsLeft;
// 3) re-split creatures in a balanced way
for(auto slot : creatureSlots)
{
ChangeStackCount csc;
RebalanceStacks rs;
csc.army = army->id;
csc.slot = slot;
csc.count = (currSlot < rem)
? quotient + 1
: quotient;
csc.absoluteValue = true;
bulkSRS.changes.push_back(csc);
currSlot++;
check += csc.count;
rs.srcArmy = army->id;
rs.dstArmy = army->id;
rs.srcSlot = slotSrc;
rs.dstSlot = slot;
rs.count = vstd::divideAndCeil(unitsToMove, slotsLeft);
bulkSRS.moves.push_back(rs);
unitsToMove -= rs.count;
slotsLeft -= 1;
}
if(check != totalCreatures)
{
complain((boost::format("Failure: totalCreatures=%d but check=%d") % totalCreatures % check).str());
return false;
}
sendAndApply(bulkSRS);
return true;
}
@ -2435,7 +2442,7 @@ bool CGameHandler::upgradeCreature(ObjectInstanceID objid, SlotID pos, CreatureI
fillUpgradeInfo(obj, pos, upgradeInfo);
PlayerColor player = obj->tempOwner;
const PlayerState *p = getPlayerState(player);
int crQuantity = obj->stacks.at(pos)->count;
int crQuantity = obj->stacks.at(pos)->getCount();
//check if upgrade is possible
if (!upgradeInfo.hasUpgrades() && complain("That upgrade is not possible!"))
@ -3167,8 +3174,8 @@ bool CGameHandler::sellCreatures(ui32 count, const IMarket *market, const CGHero
const CStackInstance &s = hero->getStack(slot);
if (s.count < (TQuantity)count //can't sell more creatures than have
|| (hero->stacksCount() == 1 && hero->needsLastStack() && s.count == count)) //can't sell last stack
if (s.getCount() < static_cast<TQuantity>(count) //can't sell more creatures than have
|| (hero->stacksCount() == 1 && hero->needsLastStack() && s.getCount() == count)) //can't sell last stack
{
COMPLAIN_RET("Not enough creatures in army!");
}

View File

@ -227,7 +227,7 @@ public:
bool bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot);
bool bulkSplitStack(SlotID src, ObjectInstanceID srcOwner, si32 howMany);
bool bulkMergeStacks(SlotID slotSrc, ObjectInstanceID srcOwner);
bool bulkSmartSplitStack(SlotID slotSrc, ObjectInstanceID srcOwner);
bool bulkSplitAndRebalanceStack(SlotID slotSrc, ObjectInstanceID srcOwner);
void save(const std::string &fname);
bool load(const std::string &fname);

View File

@ -124,12 +124,12 @@ void ApplyGhNetPackVisitor::visitBulkMergeStacks(BulkMergeStacks & pack)
result = gh.bulkMergeStacks(pack.src, pack.srcOwner);
}
void ApplyGhNetPackVisitor::visitBulkSmartSplitStack(BulkSmartSplitStack & pack)
void ApplyGhNetPackVisitor::visitBulkSmartSplitStack(BulkSplitAndRebalanceStack & pack)
{
gh.throwIfWrongPlayer(connection, &pack);
gh.throwIfPlayerNotActive(connection, &pack);
result = gh.bulkSmartSplitStack(pack.src, pack.srcOwner);
result = gh.bulkSplitAndRebalanceStack(pack.src, pack.srcOwner);
}
void ApplyGhNetPackVisitor::visitDisbandCreature(DisbandCreature & pack)

View File

@ -41,7 +41,7 @@ public:
void visitBulkMoveArmy(BulkMoveArmy & pack) override;
void visitBulkSplitStack(BulkSplitStack & pack) override;
void visitBulkMergeStacks(BulkMergeStacks & pack) override;
void visitBulkSmartSplitStack(BulkSmartSplitStack & pack) override;
void visitBulkSmartSplitStack(BulkSplitAndRebalanceStack & pack) override;
void visitDisbandCreature(DisbandCreature & pack) override;
void visitBuildStructure(BuildStructure & pack) override;
void visitSpellResearch(SpellResearch & pack) override;

View File

@ -487,7 +487,7 @@ void BattleResultProcessor::battleFinalize(const BattleID & battleID, const Batt
resultsApplied.raisedStack = winnerHero->calculateNecromancy(result);
const SlotID necroSlot = resultsApplied.raisedStack.getCreature() ? winnerHero->getSlotFor(resultsApplied.raisedStack.getCreature()) : SlotID();
if(necroSlot != SlotID() && !finishingBattle->isDraw())
gameHandler->addToSlot(StackLocation(finishingBattle->winnerId, necroSlot), resultsApplied.raisedStack.getCreature(), resultsApplied.raisedStack.count);
gameHandler->addToSlot(StackLocation(finishingBattle->winnerId, necroSlot), resultsApplied.raisedStack.getCreature(), resultsApplied.raisedStack.getCount());
}
resultsApplied.battleID = battleID;

View File

@ -73,7 +73,7 @@ bool CGarrisonDialogQuery::blocksPack(const CPackForServer * pack) const
if(auto stacks = dynamic_cast<const BulkMergeStacks*>(pack))
return !vstd::contains(ourIds, stacks->srcOwner);
if(auto stacks = dynamic_cast<const BulkSmartSplitStack*>(pack))
if(auto stacks = dynamic_cast<const BulkSplitAndRebalanceStack*>(pack))
return !vstd::contains(ourIds, stacks->srcOwner);
if(auto stacks = dynamic_cast<const BulkMoveArmy*>(pack))