浏览代码

Merge branch 'develop' into crash_fix4

kdmcser 5 月之前
父节点
当前提交
5645806035
共有 63 个文件被更改,包括 776 次插入758 次删除
  1. 2 2
      AI/Nullkiller/AIGateway.cpp
  2. 5 5
      AI/Nullkiller/Analyzers/ArmyManager.cpp
  3. 1 1
      AI/Nullkiller/Engine/PriorityEvaluator.cpp
  4. 2 2
      AI/Nullkiller/Pathfinding/Actors.cpp
  5. 1 1
      AI/VCAI/ArmyManager.cpp
  6. 1 1
      AI/VCAI/Goals/CompleteQuest.cpp
  7. 1 1
      AI/VCAI/Goals/GatherTroops.cpp
  8. 2 2
      AI/VCAI/VCAI.cpp
  9. 2 2
      CCallback.cpp
  10. 2 2
      CCallback.h
  11. 0 1
      client/ClientNetPackVisitors.h
  12. 1 14
      client/NetPacksClient.cpp
  13. 2 2
      client/UIHelper.cpp
  14. 2 1
      client/mapView/MapView.cpp
  15. 2 0
      client/mapView/MapView.h
  16. 11 11
      client/widgets/CGarrisonInt.cpp
  17. 1 1
      client/widgets/CGarrisonInt.h
  18. 4 4
      client/widgets/MiscWidgets.cpp
  19. 1 1
      client/windows/CCastleInterface.cpp
  20. 9 9
      client/windows/CCreatureWindow.cpp
  21. 4 0
      client/windows/CPuzzleWindow.cpp
  22. 37 47
      launcher/translation/chinese.ts
  23. 24 28
      launcher/translation/portuguese.ts
  24. 122 75
      lib/CCreatureSet.cpp
  25. 48 15
      lib/CCreatureSet.h
  26. 2 2
      lib/CGameInfoCallback.cpp
  27. 2 2
      lib/CStack.cpp
  28. 2 0
      lib/entities/artifact/CArtifact.cpp
  29. 4 4
      lib/gameState/CGameState.cpp
  30. 2 2
      lib/gameState/InfoAboutArmy.cpp
  31. 1 1
      lib/json/JsonRandom.cpp
  32. 1 1
      lib/mapObjectConstructors/DwellingInstanceConstructor.cpp
  33. 1 1
      lib/mapObjects/CArmedInstance.cpp
  34. 19 20
      lib/mapObjects/CGCreature.cpp
  35. 6 1
      lib/mapObjects/CGHeroInstance.cpp
  36. 4 0
      lib/mapObjects/CGHeroInstance.h
  37. 1 1
      lib/mapObjects/CGPandoraBox.cpp
  38. 1 1
      lib/mapObjects/CGTownInstance.cpp
  39. 3 3
      lib/mapObjects/CQuest.cpp
  40. 3 3
      lib/mapObjects/MiscObjects.cpp
  41. 3 3
      lib/mapping/MapFormatH3M.cpp
  42. 1 2
      lib/networkPacks/NetPackVisitor.h
  43. 22 65
      lib/networkPacks/NetPacksLib.cpp
  44. 0 16
      lib/networkPacks/PacksForClient.h
  45. 3 3
      lib/networkPacks/PacksForServer.h
  46. 1 1
      lib/rewardable/Interface.cpp
  47. 3 3
      lib/rewardable/Limiter.cpp
  48. 1 1
      lib/rewardable/Reward.cpp
  49. 4 1
      lib/serializer/ESerializationVersion.h
  50. 1 2
      lib/serializer/RegisterTypes.h
  51. 1 1
      lib/texts/MetaString.cpp
  52. 2 2
      mapeditor/inspector/armywidget.cpp
  53. 2 2
      mapeditor/inspector/inspector.cpp
  54. 2 2
      mapeditor/inspector/questwidget.cpp
  55. 3 3
      mapeditor/inspector/rewardswidget.cpp
  56. 168 168
      mapeditor/translation/chinese.ts
  57. 168 168
      mapeditor/translation/portuguese.ts
  58. 46 39
      server/CGameHandler.cpp
  59. 1 1
      server/CGameHandler.h
  60. 2 2
      server/NetPacksServer.cpp
  61. 1 1
      server/ServerNetPackVisitors.h
  62. 1 1
      server/battles/BattleResultProcessor.cpp
  63. 1 1
      server/queries/MapQueries.cpp

+ 2 - 2
AI/Nullkiller/AIGateway.cpp

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

+ 5 - 5
AI/Nullkiller/Analyzers/ArmyManager.cpp

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

+ 1 - 1
AI/Nullkiller/Engine/PriorityEvaluator.cpp

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

+ 2 - 2
AI/Nullkiller/Pathfinding/Actors.cpp

@@ -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();
 	}
 }
 

+ 1 - 1
AI/VCAI/ArmyManager.cpp

@@ -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();
 		}
 	}
 

+ 1 - 1
AI/VCAI/Goals/CompleteQuest.cpp

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

+ 1 - 1
AI/VCAI/Goals/GatherTroops.cpp

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

+ 2 - 2
AI/VCAI/VCAI.cpp

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

+ 2 - 2
CCallback.cpp

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

+ 2 - 2
CCallback.h

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

+ 0 - 1
client/ClientNetPackVisitors.h

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

+ 1 - 14
client/NetPacksClient.cpp

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

+ 2 - 2
client/UIHelper.cpp

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

+ 2 - 1
client/mapView/MapView.cpp

@@ -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();
 }

+ 2 - 0
client/mapView/MapView.h

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

+ 11 - 11
client/widgets/CGarrisonInt.cpp

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

+ 1 - 1
client/widgets/CGarrisonInt.h

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

+ 4 - 4
client/widgets/MiscWidgets.cpp

@@ -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())];
 				}
 			}
 		}

+ 1 - 1
client/windows/CCastleInterface.cpp

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

+ 9 - 9
client/windows/CCreatureWindow.cpp

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

+ 4 - 0
client/windows/CPuzzleWindow.cpp

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

+ 37 - 47
launcher/translation/chinese.ts

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

+ 24 - 28
launcher/translation/portuguese.ts

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

+ 122 - 75
lib/CCreatureSet.cpp

@@ -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;
+
+	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());
+
+	totalExperience	+= actualGainedExperience;
+}
+
+void CStackInstance::giveTotalStackExperience(TExpType experienceToGive)
+{
+	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 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();
+}
 
-	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
+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;

+ 48 - 15
lib/CCreatureSet.h

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

+ 2 - 2
lib/CGameInfoCallback.cpp

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

+ 2 - 2
lib/CStack.cpp

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

+ 2 - 0
lib/entities/artifact/CArtifact.cpp

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

+ 4 - 4
lib/gameState/CGameState.cpp

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

+ 2 - 2
lib/gameState/InfoAboutArmy.cpp

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

+ 1 - 1
lib/json/JsonRandom.cpp

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

+ 1 - 1
lib/mapObjectConstructors/DwellingInstanceConstructor.cpp

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

+ 1 - 1
lib/mapObjects/CArmedInstance.cpp

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

+ 19 - 20
lib/mapObjects/CGCreature.cpp

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

+ 6 - 1
lib/mapObjects/CGHeroInstance.cpp

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

+ 4 - 0
lib/mapObjects/CGHeroInstance.h

@@ -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();
 	}
 };
 

+ 1 - 1
lib/mapObjects/CGPandoraBox.cpp

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

+ 1 - 1
lib/mapObjects/CGTownInstance.cpp

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

+ 3 - 3
lib/mapObjects/CQuest.cpp

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

+ 3 - 3
lib/mapObjects/MiscObjects.cpp

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

+ 3 - 3
lib/mapping/MapFormatH3M.cpp

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

+ 1 - 2
lib/networkPacks/NetPackVisitor.h

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

+ 22 - 65
lib/networkPacks/NetPacksLib.cpp

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

+ 0 - 16
lib/networkPacks/PacksForClient.h

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

+ 3 - 3
lib/networkPacks/PacksForServer.h

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

+ 1 - 1
lib/rewardable/Interface.cpp

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

+ 3 - 3
lib/rewardable/Limiter.cpp

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

+ 1 - 1
lib/rewardable/Reward.cpp

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

+ 4 - 1
lib/serializer/ESerializationVersion.h

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

+ 1 - 2
lib/serializer/RegisterTypes.h

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

+ 1 - 1
lib/texts/MetaString.cpp

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

+ 2 - 2
mapeditor/inspector/armywidget.cpp

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

+ 2 - 2
mapeditor/inspector/inspector.cpp

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

+ 2 - 2
mapeditor/inspector/questwidget.cpp

@@ -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(", "));
 

+ 3 - 3
mapeditor/inspector/rewardswidget.cpp

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

+ 168 - 168
mapeditor/translation/chinese.ts

@@ -49,105 +49,105 @@
         <location filename="../campaigneditor/campaigneditor.ui" line="14"/>
         <location filename="../campaigneditor/campaigneditor.cpp" line="134"/>
         <source>VCMI Campaign Editor</source>
-        <translation type="unfinished"></translation>
+        <translation>VCMI战役编辑器</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="36"/>
         <source>File</source>
-        <translation type="unfinished">文件</translation>
+        <translation>文件</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="45"/>
         <source>Edit</source>
-        <translation type="unfinished">编辑</translation>
+        <translation>编辑</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="52"/>
         <source>View</source>
-        <translation type="unfinished">视图</translation>
+        <translation>视图</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="64"/>
         <source>Toolbar</source>
-        <translation type="unfinished">工具栏</translation>
+        <translation>工具栏</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="104"/>
         <source>Open</source>
-        <translation type="unfinished">打开</translation>
+        <translation>打开</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="112"/>
         <source>Save</source>
-        <translation type="unfinished">保存</translation>
+        <translation>保存</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="120"/>
         <source>New</source>
-        <translation type="unfinished">新建</translation>
+        <translation>新建</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="128"/>
         <source>Save as...</source>
-        <translation type="unfinished">另存为</translation>
+        <translation>另存为</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="131"/>
         <source>Ctrl+Shift+S</source>
-        <translation type="unfinished">Ctrl+Shift+S</translation>
+        <translation>Ctrl+Shift+S</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="136"/>
         <location filename="../campaigneditor/campaigneditor.ui" line="139"/>
         <source>Campaign Properties</source>
-        <translation type="unfinished"></translation>
+        <translation>战役属性</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="150"/>
         <location filename="../campaigneditor/campaigneditor.ui" line="153"/>
         <source>Scenario Properties</source>
-        <translation type="unfinished"></translation>
+        <translation>场景属性</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="164"/>
         <location filename="../campaigneditor/campaigneditor.ui" line="167"/>
         <source>Show full background</source>
-        <translation type="unfinished"></translation>
+        <translation>显示全部背景</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="99"/>
         <source>Scenario editor</source>
-        <translation type="unfinished"></translation>
+        <translation>场景编辑器</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="122"/>
         <source>Confirmation</source>
-        <translation type="unfinished">确认</translation>
+        <translation>确认</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="122"/>
         <source>Unsaved changes will be lost, are you sure?</source>
-        <translation type="unfinished">未保存的改动会丢失,你确定要这么做吗?</translation>
+        <translation>未保存的改动会丢失,你确定要这么做吗?</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="165"/>
         <source>Open map</source>
-        <translation type="unfinished">打开地图</translation>
+        <translation>打开地图</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="167"/>
         <source>All supported campaigns (*.vcmp *.h3c);;VCMI campaigns(*.vcmp);;HoMM3 campaigns(*.h3c)</source>
-        <translation type="unfinished"></translation>
+        <translation>所有支持的战役文件 (*.vcmp *.h3c);;VCMI战役文件(*.vcmp);;英雄无敌3战役文件(*.h3c)</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="182"/>
         <source>Save campaign</source>
-        <translation type="unfinished"></translation>
+        <translation>保存战役</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="182"/>
         <source>VCMI campaigns (*.vcmp)</source>
-        <translation type="unfinished">VCMI战役文件(*.vcmp)</translation>
+        <translation>VCMI战役文件(*.vcmp)</translation>
     </message>
 </context>
 <context>
@@ -156,158 +156,158 @@
         <location filename="../campaigneditor/campaignproperties.ui" line="14"/>
         <location filename="../campaigneditor/campaignproperties.cpp" line="27"/>
         <source>Campaign Properties</source>
-        <translation type="unfinished"></translation>
+        <translation>战役属性</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="30"/>
         <source>General</source>
-        <translation type="unfinished">通用</translation>
+        <translation>通用</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="36"/>
         <source>Campaign name</source>
-        <translation type="unfinished"></translation>
+        <translation>战役名称</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="46"/>
         <source>Campaign description</source>
-        <translation type="unfinished"></translation>
+        <translation>战役描述</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="56"/>
         <source>Author</source>
-        <translation type="unfinished">作者</translation>
+        <translation>作者</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="66"/>
         <source>Author contact (e.g. e-mail)</source>
-        <translation type="unfinished"></translation>
+        <translation>作者联系方式(例如电子邮件)</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="76"/>
         <source>Campaign creation date</source>
-        <translation type="unfinished"></translation>
+        <translation>战役创建时间</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="86"/>
         <source>Campaign version</source>
-        <translation type="unfinished"></translation>
+        <translation>战役版本</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="96"/>
         <source>Music</source>
-        <translation type="unfinished"></translation>
+        <translation>音乐</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="106"/>
         <source>Scenario difficulty is user selectable</source>
-        <translation type="unfinished"></translation>
+        <translation>场景难度可由玩家设置</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="114"/>
         <location filename="../campaigneditor/campaignproperties.ui" line="197"/>
         <source>Regions</source>
-        <translation type="unfinished"></translation>
+        <translation>区域</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="120"/>
         <source>Regions Preset</source>
-        <translation type="unfinished"></translation>
+        <translation>区域预设</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="137"/>
         <source>Background</source>
-        <translation type="unfinished"></translation>
+        <translation>背景</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="147"/>
         <source>Suffix</source>
-        <translation type="unfinished"></translation>
+        <translation>后缀</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="173"/>
         <source>Prefix</source>
-        <translation type="unfinished"></translation>
+        <translation>前缀</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="183"/>
         <source>Color suffix length</source>
-        <translation type="unfinished"></translation>
+        <translation>颜色后缀长度</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="226"/>
         <source>Add</source>
-        <translation type="unfinished">添加</translation>
+        <translation>添加</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="233"/>
         <source>Remove</source>
-        <translation type="unfinished">移除</translation>
+        <translation>移除</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="258"/>
         <source>Misc</source>
-        <translation type="unfinished"></translation>
+        <translation>其他</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="264"/>
         <source>Loading background image</source>
-        <translation type="unfinished"></translation>
+        <translation>加载背景图片</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="274"/>
         <source>Video rim image</source>
-        <translation type="unfinished"></translation>
+        <translation>视频边框图像</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="284"/>
         <source>Intro video</source>
-        <translation type="unfinished"></translation>
+        <translation>开场动画</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="294"/>
         <source>Outro video</source>
-        <translation type="unfinished"></translation>
+        <translation>结尾动画</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="45"/>
         <source>Custom</source>
-        <translation type="unfinished"></translation>
+        <translation>自定义</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="127"/>
         <source>Infix</source>
-        <translation type="unfinished"></translation>
+        <translation>中缀</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="127"/>
         <source>X</source>
-        <translation type="unfinished"></translation>
+        <translation>X</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="127"/>
         <source>Y</source>
-        <translation type="unfinished"></translation>
+        <translation>Y</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="127"/>
         <source>Label Pos X</source>
-        <translation type="unfinished"></translation>
+        <translation>标签坐标X</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="127"/>
         <source>Label Pos Y</source>
-        <translation type="unfinished"></translation>
+        <translation>标签坐标Y</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="179"/>
         <source>Fewer Scenarios</source>
-        <translation type="unfinished"></translation>
+        <translation>场景减少</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="179"/>
         <source>New Region setup supports fewer scenarios than before. Some will removed. Continue?</source>
-        <translation type="unfinished"></translation>
+        <translation>新的区域设置支持的场景数量减少,部分场景将会被移除。是否继续?</translation>
     </message>
 </context>
 <context>
@@ -661,7 +661,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1033"/>
         <source>Ctrl+O</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+O</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1043"/>
@@ -676,7 +676,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1054"/>
         <source>Ctrl+S</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+S</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1059"/>
@@ -686,7 +686,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1062"/>
         <source>Ctrl+N</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+N</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1067"/>
@@ -702,7 +702,7 @@
         <location filename="../mainwindow.ui" line="1075"/>
         <location filename="../mainwindow.ui" line="1078"/>
         <source>Campaign editor</source>
-        <translation type="unfinished"></translation>
+        <translation>战役编辑器</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1083"/>
@@ -715,7 +715,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1089"/>
         <source>Ctrl+U</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+U</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1097"/>
@@ -725,7 +725,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1100"/>
         <source>Ctrl+Shift+P</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Shift+P</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1105"/>
@@ -735,7 +735,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1108"/>
         <source>Ctrl+X</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+X</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1113"/>
@@ -745,7 +745,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1116"/>
         <source>Ctrl+C</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+C</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1121"/>
@@ -755,7 +755,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1124"/>
         <source>Ctrl+V</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+V</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1129"/>
@@ -775,7 +775,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1146"/>
         <source>Ctrl+G</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+G</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1154"/>
@@ -790,7 +790,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1160"/>
         <source>Ctrl+Enter</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Enter</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1168"/>
@@ -800,7 +800,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1171"/>
         <source>Ctrl+P</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+P</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1179"/>
@@ -811,7 +811,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1185"/>
         <source>Ctrl+Z</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Z</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1196"/>
@@ -821,7 +821,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1199"/>
         <source>Ctrl+Y</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Y</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1210"/>
@@ -831,7 +831,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1213"/>
         <source>Del</source>
-        <translation type="unfinished"></translation>
+        <translation>Del</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1221"/>
@@ -841,7 +841,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1224"/>
         <source>Ctrl+0</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+0</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1229"/>
@@ -851,7 +851,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1232"/>
         <source>Ctrl+Shift+V</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Shift+V</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1240"/>
@@ -874,7 +874,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1265"/>
         <source>Ctrl+1</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+1</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1273"/>
@@ -884,7 +884,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1276"/>
         <source>Ctrl+2</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+2</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1284"/>
@@ -894,7 +894,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1287"/>
         <source>Ctrl+3</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+3</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1295"/>
@@ -904,7 +904,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1298"/>
         <source>Ctrl+4</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+4</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1306"/>
@@ -914,7 +914,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1309"/>
         <source>Ctrl+5</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+5</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1317"/>
@@ -924,7 +924,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1320"/>
         <source>Ctrl+6</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+6</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1328"/>
@@ -934,7 +934,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1331"/>
         <source>Ctrl+7</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+7</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1339"/>
@@ -944,7 +944,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1342"/>
         <source>Ctrl+8</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+8</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1347"/>
@@ -954,7 +954,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1350"/>
         <source>Ctrl+E</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+E</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1358"/>
@@ -975,7 +975,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1372"/>
         <source>Ctrl+Shift+M</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Shift+M</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1377"/>
@@ -986,7 +986,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1383"/>
         <source>Ctrl+Shift+C</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Shift+C</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1388"/>
@@ -1026,7 +1026,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1413"/>
         <source>Ctrl++</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl++</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1418"/>
@@ -2523,287 +2523,287 @@
         <location filename="../campaigneditor/scenarioproperties.ui" line="14"/>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="32"/>
         <source>Scenario Properties</source>
-        <translation type="unfinished"></translation>
+        <translation></translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="30"/>
         <source>General</source>
-        <translation type="unfinished">通用</translation>
+        <translation>通用</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="36"/>
         <source>Region name</source>
-        <translation type="unfinished"></translation>
+        <translation>区域名称</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="50"/>
         <source>Region color</source>
-        <translation type="unfinished"></translation>
+        <translation>区域颜色</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="60"/>
         <source>Scenario name</source>
-        <translation type="unfinished"></translation>
+        <translation>场景名字</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="74"/>
         <source>Map file</source>
-        <translation type="unfinished"></translation>
+        <translation>地图文件</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="90"/>
         <source>Import...</source>
-        <translation type="unfinished"></translation>
+        <translation>导入</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="97"/>
         <source>Export...</source>
-        <translation type="unfinished"></translation>
+        <translation>导出</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="104"/>
         <location filename="../campaigneditor/scenarioproperties.ui" line="545"/>
         <source>Remove</source>
-        <translation type="unfinished">移除</translation>
+        <translation>移除</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="113"/>
         <source>Default difficulty</source>
-        <translation type="unfinished"></translation>
+        <translation>默认难度</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="123"/>
         <source>Prerequisites</source>
-        <translation type="unfinished"></translation>
+        <translation>前置条件</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="133"/>
         <source>Region right-click text</source>
-        <translation type="unfinished"></translation>
+        <translation>区域右键提示文字</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="144"/>
         <source>Prologue/Epilogue</source>
-        <translation type="unfinished"></translation>
+        <translation>序章/终章</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="150"/>
         <source>Prologue</source>
-        <translation type="unfinished"></translation>
+        <translation>序章</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="156"/>
         <location filename="../campaigneditor/scenarioproperties.ui" line="233"/>
         <source>Enabled</source>
-        <translation type="unfinished">已允许</translation>
+        <translation>启用</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="168"/>
         <location filename="../campaigneditor/scenarioproperties.ui" line="245"/>
         <source>Video</source>
-        <translation type="unfinished"></translation>
+        <translation>动画</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="185"/>
         <location filename="../campaigneditor/scenarioproperties.ui" line="262"/>
         <source>Music</source>
-        <translation type="unfinished"></translation>
+        <translation>音乐</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="202"/>
         <location filename="../campaigneditor/scenarioproperties.ui" line="279"/>
         <source>Voice</source>
-        <translation type="unfinished"></translation>
+        <translation>语音</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="214"/>
         <location filename="../campaigneditor/scenarioproperties.ui" line="291"/>
         <source>Text</source>
-        <translation type="unfinished">文本</translation>
+        <translation>文本</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="227"/>
         <source>Epilogue</source>
-        <translation type="unfinished"></translation>
+        <translation>终章</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="305"/>
         <source>Crossover</source>
-        <translation type="unfinished"></translation>
+        <translation>跨场景</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="311"/>
         <source>Crossover heroes retain</source>
-        <translation type="unfinished"></translation>
+        <translation>跨场景英雄可保留</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="317"/>
         <source>Experience</source>
-        <translation type="unfinished">经验值</translation>
+        <translation>经验值</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="324"/>
         <source>Primary skills</source>
-        <translation type="unfinished">主属性</translation>
+        <translation>主属性</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="331"/>
         <source>Secondary skills</source>
-        <translation type="unfinished"></translation>
+        <translation>辅助技能</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="338"/>
         <source>Spells</source>
-        <translation type="unfinished">魔法</translation>
+        <translation>魔法</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="345"/>
         <source>Artifacts</source>
-        <translation type="unfinished">宝物</translation>
+        <translation>宝物</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="378"/>
         <source>All</source>
-        <translation type="unfinished"></translation>
+        <translation>全选</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="385"/>
         <source>None</source>
-        <translation type="unfinished">无</translation>
+        <translation>清除</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="412"/>
         <source>Crossover artifacts</source>
-        <translation type="unfinished"></translation>
+        <translation>跨场景宝物</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="441"/>
         <source>Starting</source>
-        <translation type="unfinished"></translation>
+        <translation>开局</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="447"/>
         <source>Starting options are</source>
-        <translation type="unfinished"></translation>
+        <translation>开局选项</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="454"/>
         <source>Starting bonus options</source>
-        <translation type="unfinished"></translation>
+        <translation>开局奖励选项</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="461"/>
         <source>Hero crossover options</source>
-        <translation type="unfinished"></translation>
+        <translation>英雄跨场景选项</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="468"/>
         <source>Starting hero options</source>
-        <translation type="unfinished"></translation>
+        <translation>起始英雄选项</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="486"/>
         <source>Bonus</source>
-        <translation type="unfinished"></translation>
+        <translation>奖励</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="492"/>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="431"/>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="441"/>
         <source>Player position</source>
-        <translation type="unfinished"></translation>
+        <translation>玩家坐标</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="502"/>
         <source>Starting bonus option</source>
-        <translation type="unfinished"></translation>
+        <translation>开局奖励选项</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="513"/>
         <source>Crossover/ Starting hero</source>
-        <translation type="unfinished"></translation>
+        <translation>跨场景/开局英雄</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="531"/>
         <source>Add...</source>
-        <translation type="unfinished"></translation>
+        <translation>添加</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="538"/>
         <source>Edit...</source>
-        <translation type="unfinished"></translation>
+        <translation>编辑</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="115"/>
         <source>Strongest</source>
-        <translation type="unfinished"></translation>
+        <translation>强大</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="116"/>
         <source>Generated</source>
-        <translation type="unfinished"></translation>
+        <translation>一般</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="117"/>
         <source>Random</source>
-        <translation type="unfinished">随机</translation>
+        <translation>随机</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="134"/>
         <source>No map</source>
-        <translation type="unfinished"></translation>
+        <translation>没有地图</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="177"/>
         <source>Player</source>
-        <translation type="unfinished">玩家</translation>
+        <translation>玩家</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="369"/>
         <source>Open map</source>
-        <translation type="unfinished">打开地图</translation>
+        <translation>打开地图</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="369"/>
         <source>All supported maps (*.vmap *.h3m);;VCMI maps(*.vmap);;HoMM3 maps(*.h3m)</source>
-        <translation type="unfinished">所有支持的地图类型(*.vmap *.h3m);;VCMI地图(*.vmap);;英雄无敌3地图(*.h3m)</translation>
+        <translation>所有支持的地图类型(*.vmap *.h3m);;VCMI地图(*.vmap);;英雄无敌3地图(*.h3m)</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="376"/>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="402"/>
         <source>Error</source>
-        <translation type="unfinished"></translation>
+        <translation>错误</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="376"/>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="402"/>
         <source>Could not open the file.</source>
-        <translation type="unfinished"></translation>
+        <translation>无法打开文件!</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="395"/>
         <source>Save map</source>
-        <translation type="unfinished">保存地图</translation>
+        <translation>保存地图</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="395"/>
         <source>VCMI maps (*.vmap);</source>
-        <translation type="unfinished"></translation>
+        <translation>VCMI地图(*.vmap);</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="395"/>
         <source>HoMM3 maps (*.h3m);</source>
-        <translation type="unfinished"></translation>
+        <translation>英雄无敌3地图(*.h3m);</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="431"/>
         <source>Source scenario</source>
-        <translation type="unfinished"></translation>
+        <translation>来源场景</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="441"/>
         <source>Hero</source>
-        <translation type="unfinished"></translation>
+        <translation>英雄</translation>
     </message>
 </context>
 <context>
@@ -2811,12 +2811,12 @@
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="14"/>
         <source>Scenario Properties</source>
-        <translation type="unfinished"></translation>
+        <translation>场景属性</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="20"/>
         <source>Select a bonus type</source>
-        <translation type="unfinished"></translation>
+        <translation>选择奖励类型</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="27"/>
@@ -2824,13 +2824,13 @@
         <location filename="../campaigneditor/startingbonus.ui" line="110"/>
         <location filename="../campaigneditor/startingbonus.ui" line="270"/>
         <source>Spell</source>
-        <translation type="unfinished">魔法</translation>
+        <translation>魔法</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="34"/>
         <location filename="../campaigneditor/startingbonus.ui" line="134"/>
         <source>Creature</source>
-        <translation type="unfinished"></translation>
+        <translation>生物</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="41"/>
@@ -2838,26 +2838,26 @@
         <location filename="../campaigneditor/startingbonus.ui" line="190"/>
         <location filename="../campaigneditor/startingbonus.cpp" line="326"/>
         <source>Building</source>
-        <translation type="unfinished"></translation>
+        <translation>建筑</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="48"/>
         <location filename="../campaigneditor/startingbonus.ui" line="214"/>
         <location filename="../campaigneditor/startingbonus.ui" line="230"/>
         <source>Artifact</source>
-        <translation type="unfinished">宝物</translation>
+        <translation>宝物</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="55"/>
         <location filename="../campaigneditor/startingbonus.ui" line="254"/>
         <source>Spell scroll</source>
-        <translation type="unfinished"></translation>
+        <translation>魔法卷轴</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="62"/>
         <location filename="../campaigneditor/startingbonus.ui" line="294"/>
         <source>Primary skill</source>
-        <translation type="unfinished"></translation>
+        <translation>主属性</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="69"/>
@@ -2865,14 +2865,14 @@
         <location filename="../campaigneditor/startingbonus.ui" line="380"/>
         <location filename="../campaigneditor/startingbonus.cpp" line="334"/>
         <source>Secondary skill</source>
-        <translation type="unfinished"></translation>
+        <translation>辅助技能</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="76"/>
         <location filename="../campaigneditor/startingbonus.ui" line="414"/>
         <location filename="../campaigneditor/startingbonus.cpp" line="336"/>
         <source>Resource</source>
-        <translation type="unfinished">资源</translation>
+        <translation>资源</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="100"/>
@@ -2882,118 +2882,118 @@
         <location filename="../campaigneditor/startingbonus.ui" line="300"/>
         <location filename="../campaigneditor/startingbonus.ui" line="370"/>
         <source>Recipient</source>
-        <translation type="unfinished"></translation>
+        <translation>接受者</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="150"/>
         <source>Creature type</source>
-        <translation type="unfinished"></translation>
+        <translation>生物类型</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="160"/>
         <location filename="../campaigneditor/startingbonus.ui" line="430"/>
         <source>Quantity</source>
-        <translation type="unfinished"></translation>
+        <translation>数量</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="310"/>
         <source>Attack skill</source>
-        <translation type="unfinished"></translation>
+        <translation>攻击力</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="320"/>
         <source>Defense skill</source>
-        <translation type="unfinished"></translation>
+        <translation>防御力</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="330"/>
         <source>Spell power</source>
-        <translation type="unfinished">力量</translation>
+        <translation>力量</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="340"/>
         <source>Knowledge</source>
-        <translation type="unfinished">知识</translation>
+        <translation>知识</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="390"/>
         <source>Mastery</source>
-        <translation type="unfinished"></translation>
+        <translation>熟练度</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="420"/>
         <source>Resource type</source>
-        <translation type="unfinished"></translation>
+        <translation>资源类型</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="34"/>
         <source>Edit Starting Bonus</source>
-        <translation type="unfinished"></translation>
+        <translation>编辑初始奖励</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="59"/>
         <source>Strongest</source>
-        <translation type="unfinished"></translation>
+        <translation>强大</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="60"/>
         <source>Generated</source>
-        <translation type="unfinished"></translation>
+        <translation>一般</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="61"/>
         <source>Random</source>
-        <translation type="unfinished">随机</translation>
+        <translation>随机</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="112"/>
         <source>Common (%1 and %2)</source>
-        <translation type="unfinished"></translation>
+        <translation>普通 (%1和%2)</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="117"/>
         <source>Rare (%1, %2, %3, %4)</source>
-        <translation type="unfinished"></translation>
+        <translation>稀有 (%1,%2,%3,%4)</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="289"/>
         <source>strongest hero</source>
-        <translation type="unfinished"></translation>
+        <translation>强大英雄</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="291"/>
         <source>generated hero</source>
-        <translation type="unfinished"></translation>
+        <translation>普通英雄</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="293"/>
         <source>random hero</source>
-        <translation type="unfinished"></translation>
+        <translation>随机英雄</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="322"/>
         <source>%1 spell for %2</source>
-        <translation type="unfinished"></translation>
+        <translation>%2 的%1魔法</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="324"/>
         <source>%1 %2 for %3</source>
-        <translation type="unfinished"></translation>
+        <translation>%3的%1 %2</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="328"/>
         <source>%1 artifact for %2</source>
-        <translation type="unfinished"></translation>
+        <translation>%2的%1宝物</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="330"/>
         <source>%1 spell scroll for %2</source>
-        <translation type="unfinished"></translation>
+        <translation>%2的%1魔法卷轴</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="332"/>
         <source>Primary skill (Attack: %1, Defense: %2, Spell: %3, Knowledge: %4) for %5</source>
-        <translation type="unfinished"></translation>
+        <translation>%5的主属性(攻击力:%1,防御力%2,魔法:%3,知识:%4)</translation>
     </message>
 </context>
 <context>

+ 168 - 168
mapeditor/translation/portuguese.ts

@@ -49,105 +49,105 @@
         <location filename="../campaigneditor/campaigneditor.ui" line="14"/>
         <location filename="../campaigneditor/campaigneditor.cpp" line="134"/>
         <source>VCMI Campaign Editor</source>
-        <translation type="unfinished"></translation>
+        <translation>Editor de Campanhas VCMI</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="36"/>
         <source>File</source>
-        <translation type="unfinished">Arquivo</translation>
+        <translation>Arquivo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="45"/>
         <source>Edit</source>
-        <translation type="unfinished">Editar</translation>
+        <translation>Editar</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="52"/>
         <source>View</source>
-        <translation type="unfinished">Visualizar</translation>
+        <translation>Visualizar</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="64"/>
         <source>Toolbar</source>
-        <translation type="unfinished">Barra de Ferramentas</translation>
+        <translation>Barra de Ferramentas</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="104"/>
         <source>Open</source>
-        <translation type="unfinished">Abrir</translation>
+        <translation>Abrir</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="112"/>
         <source>Save</source>
-        <translation type="unfinished">Salvar</translation>
+        <translation>Salvar</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="120"/>
         <source>New</source>
-        <translation type="unfinished">Novo</translation>
+        <translation>Novo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="128"/>
         <source>Save as...</source>
-        <translation type="unfinished">Salvar como...</translation>
+        <translation>Salvar como...</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="131"/>
         <source>Ctrl+Shift+S</source>
-        <translation type="unfinished">Ctrl+Shift+S</translation>
+        <translation>Ctrl+Shift+S</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="136"/>
         <location filename="../campaigneditor/campaigneditor.ui" line="139"/>
         <source>Campaign Properties</source>
-        <translation type="unfinished"></translation>
+        <translation>Propriedades da Campanha</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="150"/>
         <location filename="../campaigneditor/campaigneditor.ui" line="153"/>
         <source>Scenario Properties</source>
-        <translation type="unfinished"></translation>
+        <translation>Propriedades do Cenário</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.ui" line="164"/>
         <location filename="../campaigneditor/campaigneditor.ui" line="167"/>
         <source>Show full background</source>
-        <translation type="unfinished"></translation>
+        <translation>Mostrar fundo completo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="99"/>
         <source>Scenario editor</source>
-        <translation type="unfinished"></translation>
+        <translation>Editor de Cenário</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="122"/>
         <source>Confirmation</source>
-        <translation type="unfinished">Confirmação</translation>
+        <translation>Confirmação</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="122"/>
         <source>Unsaved changes will be lost, are you sure?</source>
-        <translation type="unfinished">As alterações não salvas serão perdidas, tem certeza?</translation>
+        <translation>As alterações não salvas serão perdidas, tem certeza?</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="165"/>
         <source>Open map</source>
-        <translation type="unfinished">Abrir mapa</translation>
+        <translation>Abrir mapa</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="167"/>
         <source>All supported campaigns (*.vcmp *.h3c);;VCMI campaigns(*.vcmp);;HoMM3 campaigns(*.h3c)</source>
-        <translation type="unfinished"></translation>
+        <translation>Todas as campanhas suportadas (*.vcmp *.h3c);;Campanhas VCMI (*.vcmp);;Campanhas HoMM3 (*.h3c)</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="182"/>
         <source>Save campaign</source>
-        <translation type="unfinished"></translation>
+        <translation>Salvar campanha</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaigneditor.cpp" line="182"/>
         <source>VCMI campaigns (*.vcmp)</source>
-        <translation type="unfinished">Campanhas VCMI (*.vcmp)</translation>
+        <translation>Campanhas VCMI (*.vcmp)</translation>
     </message>
 </context>
 <context>
@@ -156,158 +156,158 @@
         <location filename="../campaigneditor/campaignproperties.ui" line="14"/>
         <location filename="../campaigneditor/campaignproperties.cpp" line="27"/>
         <source>Campaign Properties</source>
-        <translation type="unfinished"></translation>
+        <translation>Propriedades da Campanha</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="30"/>
         <source>General</source>
-        <translation type="unfinished">Geral</translation>
+        <translation>Geral</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="36"/>
         <source>Campaign name</source>
-        <translation type="unfinished"></translation>
+        <translation>Nome da Campanha</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="46"/>
         <source>Campaign description</source>
-        <translation type="unfinished"></translation>
+        <translation>Descrição da Campanha</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="56"/>
         <source>Author</source>
-        <translation type="unfinished">Autor</translation>
+        <translation>Autor</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="66"/>
         <source>Author contact (e.g. e-mail)</source>
-        <translation type="unfinished"></translation>
+        <translation>Contato do autor (ex: e-mail)</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="76"/>
         <source>Campaign creation date</source>
-        <translation type="unfinished"></translation>
+        <translation>Data de criação da campanha</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="86"/>
         <source>Campaign version</source>
-        <translation type="unfinished"></translation>
+        <translation>Versão da campanha</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="96"/>
         <source>Music</source>
-        <translation type="unfinished"></translation>
+        <translation>Música</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="106"/>
         <source>Scenario difficulty is user selectable</source>
-        <translation type="unfinished"></translation>
+        <translation>Dificuldade do cenário selecionável pelo usuário</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="114"/>
         <location filename="../campaigneditor/campaignproperties.ui" line="197"/>
         <source>Regions</source>
-        <translation type="unfinished"></translation>
+        <translation>Regiões</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="120"/>
         <source>Regions Preset</source>
-        <translation type="unfinished"></translation>
+        <translation>Predefinição de Regiões</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="137"/>
         <source>Background</source>
-        <translation type="unfinished"></translation>
+        <translation>Fundo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="147"/>
         <source>Suffix</source>
-        <translation type="unfinished"></translation>
+        <translation>Sufixo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="173"/>
         <source>Prefix</source>
-        <translation type="unfinished"></translation>
+        <translation>Prefixo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="183"/>
         <source>Color suffix length</source>
-        <translation type="unfinished"></translation>
+        <translation>Comprimento do sufixo de cor</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="226"/>
         <source>Add</source>
-        <translation type="unfinished">Adicionar</translation>
+        <translation>Adicionar</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="233"/>
         <source>Remove</source>
-        <translation type="unfinished">Remover</translation>
+        <translation>Remover</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="258"/>
         <source>Misc</source>
-        <translation type="unfinished"></translation>
+        <translation>Diversos</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="264"/>
         <source>Loading background image</source>
-        <translation type="unfinished"></translation>
+        <translation>Imagem de fundo do carregamento</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="274"/>
         <source>Video rim image</source>
-        <translation type="unfinished"></translation>
+        <translation>Imagem da borda do vídeo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="284"/>
         <source>Intro video</source>
-        <translation type="unfinished"></translation>
+        <translation>Vídeo de introdução</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.ui" line="294"/>
         <source>Outro video</source>
-        <translation type="unfinished"></translation>
+        <translation>Vídeo de finalização</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="45"/>
         <source>Custom</source>
-        <translation type="unfinished"></translation>
+        <translation>Personalizado</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="127"/>
         <source>Infix</source>
-        <translation type="unfinished"></translation>
+        <translation>Infixo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="127"/>
         <source>X</source>
-        <translation type="unfinished"></translation>
+        <translation>X</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="127"/>
         <source>Y</source>
-        <translation type="unfinished"></translation>
+        <translation>Y</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="127"/>
         <source>Label Pos X</source>
-        <translation type="unfinished"></translation>
+        <translation>Posição X do Rótulo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="127"/>
         <source>Label Pos Y</source>
-        <translation type="unfinished"></translation>
+        <translation>Posição Y do Rótulo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="179"/>
         <source>Fewer Scenarios</source>
-        <translation type="unfinished"></translation>
+        <translation>Menos Cenários</translation>
     </message>
     <message>
         <location filename="../campaigneditor/campaignproperties.cpp" line="179"/>
         <source>New Region setup supports fewer scenarios than before. Some will removed. Continue?</source>
-        <translation type="unfinished"></translation>
+        <translation>A nova configuração de Região suporta menos cenários do que antes. Alguns serão removidos. Continuar?</translation>
     </message>
 </context>
 <context>
@@ -661,7 +661,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1033"/>
         <source>Ctrl+O</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+O</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1043"/>
@@ -676,7 +676,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1054"/>
         <source>Ctrl+S</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+S</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1059"/>
@@ -686,7 +686,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1062"/>
         <source>Ctrl+N</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+N</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1067"/>
@@ -702,7 +702,7 @@
         <location filename="../mainwindow.ui" line="1075"/>
         <location filename="../mainwindow.ui" line="1078"/>
         <source>Campaign editor</source>
-        <translation type="unfinished"></translation>
+        <translation>Editor de Campanha</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1083"/>
@@ -715,7 +715,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1089"/>
         <source>Ctrl+U</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+U</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1097"/>
@@ -725,7 +725,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1100"/>
         <source>Ctrl+Shift+P</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Shift+P</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1105"/>
@@ -735,7 +735,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1108"/>
         <source>Ctrl+X</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+X</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1113"/>
@@ -745,7 +745,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1116"/>
         <source>Ctrl+C</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+C</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1121"/>
@@ -755,7 +755,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1124"/>
         <source>Ctrl+V</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+V</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1129"/>
@@ -775,7 +775,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1146"/>
         <source>Ctrl+G</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+G</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1154"/>
@@ -790,7 +790,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1160"/>
         <source>Ctrl+Enter</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Enter</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1168"/>
@@ -800,7 +800,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1171"/>
         <source>Ctrl+P</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+P</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1179"/>
@@ -811,7 +811,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1185"/>
         <source>Ctrl+Z</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Z</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1196"/>
@@ -821,7 +821,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1199"/>
         <source>Ctrl+Y</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Y</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1210"/>
@@ -831,7 +831,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1213"/>
         <source>Del</source>
-        <translation type="unfinished"></translation>
+        <translation>Del</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1221"/>
@@ -841,7 +841,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1224"/>
         <source>Ctrl+0</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+0</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1229"/>
@@ -851,7 +851,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1232"/>
         <source>Ctrl+Shift+V</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Shift+V</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1240"/>
@@ -874,7 +874,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1265"/>
         <source>Ctrl+1</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+1</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1273"/>
@@ -884,7 +884,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1276"/>
         <source>Ctrl+2</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+2</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1284"/>
@@ -894,7 +894,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1287"/>
         <source>Ctrl+3</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+3</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1295"/>
@@ -904,7 +904,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1298"/>
         <source>Ctrl+4</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+4</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1306"/>
@@ -914,7 +914,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1309"/>
         <source>Ctrl+5</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+5</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1317"/>
@@ -924,7 +924,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1320"/>
         <source>Ctrl+6</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+6</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1328"/>
@@ -934,7 +934,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1331"/>
         <source>Ctrl+7</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+7</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1339"/>
@@ -944,7 +944,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1342"/>
         <source>Ctrl+8</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+8</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1347"/>
@@ -954,7 +954,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1350"/>
         <source>Ctrl+E</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+E</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1358"/>
@@ -975,7 +975,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1372"/>
         <source>Ctrl+Shift+M</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Shift+M</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1377"/>
@@ -986,7 +986,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1383"/>
         <source>Ctrl+Shift+C</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl+Shift+C</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1388"/>
@@ -1026,7 +1026,7 @@
     <message>
         <location filename="../mainwindow.ui" line="1413"/>
         <source>Ctrl++</source>
-        <translation type="unfinished"></translation>
+        <translation>Ctrl++</translation>
     </message>
     <message>
         <location filename="../mainwindow.ui" line="1418"/>
@@ -2524,287 +2524,287 @@
         <location filename="../campaigneditor/scenarioproperties.ui" line="14"/>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="32"/>
         <source>Scenario Properties</source>
-        <translation type="unfinished"></translation>
+        <translation>Propriedades do Cenário</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="30"/>
         <source>General</source>
-        <translation type="unfinished">Geral</translation>
+        <translation>Geral</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="36"/>
         <source>Region name</source>
-        <translation type="unfinished"></translation>
+        <translation>Nome da Região</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="50"/>
         <source>Region color</source>
-        <translation type="unfinished"></translation>
+        <translation>Cor da Região</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="60"/>
         <source>Scenario name</source>
-        <translation type="unfinished"></translation>
+        <translation>Nome do Cenário</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="74"/>
         <source>Map file</source>
-        <translation type="unfinished"></translation>
+        <translation>Arquivo do mapa</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="90"/>
         <source>Import...</source>
-        <translation type="unfinished"></translation>
+        <translation>Importar...</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="97"/>
         <source>Export...</source>
-        <translation type="unfinished"></translation>
+        <translation>Exportar...</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="104"/>
         <location filename="../campaigneditor/scenarioproperties.ui" line="545"/>
         <source>Remove</source>
-        <translation type="unfinished">Remover</translation>
+        <translation>Remover</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="113"/>
         <source>Default difficulty</source>
-        <translation type="unfinished"></translation>
+        <translation>Dificuldade padrão</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="123"/>
         <source>Prerequisites</source>
-        <translation type="unfinished"></translation>
+        <translation>Pré-requisitos</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="133"/>
         <source>Region right-click text</source>
-        <translation type="unfinished"></translation>
+        <translation>Texto do clique direito da Região</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="144"/>
         <source>Prologue/Epilogue</source>
-        <translation type="unfinished"></translation>
+        <translation>Prólogo/Epílogo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="150"/>
         <source>Prologue</source>
-        <translation type="unfinished"></translation>
+        <translation>Prólogo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="156"/>
         <location filename="../campaigneditor/scenarioproperties.ui" line="233"/>
         <source>Enabled</source>
-        <translation type="unfinished">Ativado</translation>
+        <translation>Ativado</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="168"/>
         <location filename="../campaigneditor/scenarioproperties.ui" line="245"/>
         <source>Video</source>
-        <translation type="unfinished"></translation>
+        <translation>Vídeo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="185"/>
         <location filename="../campaigneditor/scenarioproperties.ui" line="262"/>
         <source>Music</source>
-        <translation type="unfinished"></translation>
+        <translation>Música</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="202"/>
         <location filename="../campaigneditor/scenarioproperties.ui" line="279"/>
         <source>Voice</source>
-        <translation type="unfinished"></translation>
+        <translation>Voz</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="214"/>
         <location filename="../campaigneditor/scenarioproperties.ui" line="291"/>
         <source>Text</source>
-        <translation type="unfinished">Texto</translation>
+        <translation>Texto</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="227"/>
         <source>Epilogue</source>
-        <translation type="unfinished"></translation>
+        <translation>Epílogo</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="305"/>
         <source>Crossover</source>
-        <translation type="unfinished"></translation>
+        <translation>Passagem</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="311"/>
         <source>Crossover heroes retain</source>
-        <translation type="unfinished"></translation>
+        <translation>Heróis mantêm na passagem</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="317"/>
         <source>Experience</source>
-        <translation type="unfinished">Experiência</translation>
+        <translation>Experiência</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="324"/>
         <source>Primary skills</source>
-        <translation type="unfinished">Habilidades primárias</translation>
+        <translation>Habilidades primárias</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="331"/>
         <source>Secondary skills</source>
-        <translation type="unfinished"></translation>
+        <translation>Habilidades secundárias</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="338"/>
         <source>Spells</source>
-        <translation type="unfinished">Feitiços</translation>
+        <translation>Feitiços</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="345"/>
         <source>Artifacts</source>
-        <translation type="unfinished">Artefatos</translation>
+        <translation>Artefatos</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="378"/>
         <source>All</source>
-        <translation type="unfinished"></translation>
+        <translation>Todos</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="385"/>
         <source>None</source>
-        <translation type="unfinished">Nenhum</translation>
+        <translation>Nenhum</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="412"/>
         <source>Crossover artifacts</source>
-        <translation type="unfinished"></translation>
+        <translation>Artefatos de passagem</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="441"/>
         <source>Starting</source>
-        <translation type="unfinished"></translation>
+        <translation>Inicial</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="447"/>
         <source>Starting options are</source>
-        <translation type="unfinished"></translation>
+        <translation>Opções iniciais são</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="454"/>
         <source>Starting bonus options</source>
-        <translation type="unfinished"></translation>
+        <translation>Opções de bônus inicial</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="461"/>
         <source>Hero crossover options</source>
-        <translation type="unfinished"></translation>
+        <translation>Opções de passagem de herói</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="468"/>
         <source>Starting hero options</source>
-        <translation type="unfinished"></translation>
+        <translation>Opções de herói inicial</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="486"/>
         <source>Bonus</source>
-        <translation type="unfinished"></translation>
+        <translation>Bônus</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="492"/>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="431"/>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="441"/>
         <source>Player position</source>
-        <translation type="unfinished"></translation>
+        <translation>Posição do jogador</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="502"/>
         <source>Starting bonus option</source>
-        <translation type="unfinished"></translation>
+        <translation>Opção de bônus inicial</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="513"/>
         <source>Crossover/ Starting hero</source>
-        <translation type="unfinished"></translation>
+        <translation>Herói de passagem/inicial</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="531"/>
         <source>Add...</source>
-        <translation type="unfinished"></translation>
+        <translation>Adicionar...</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.ui" line="538"/>
         <source>Edit...</source>
-        <translation type="unfinished"></translation>
+        <translation>Editar...</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="115"/>
         <source>Strongest</source>
-        <translation type="unfinished"></translation>
+        <translation>Mais forte</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="116"/>
         <source>Generated</source>
-        <translation type="unfinished"></translation>
+        <translation>Gerado</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="117"/>
         <source>Random</source>
-        <translation type="unfinished">Aleatório</translation>
+        <translation>Aleatório</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="134"/>
         <source>No map</source>
-        <translation type="unfinished"></translation>
+        <translation>Sem mapa</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="177"/>
         <source>Player</source>
-        <translation type="unfinished">Jogador</translation>
+        <translation>Jogador</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="369"/>
         <source>Open map</source>
-        <translation type="unfinished">Abrir mapa</translation>
+        <translation>Abrir mapa</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="369"/>
         <source>All supported maps (*.vmap *.h3m);;VCMI maps(*.vmap);;HoMM3 maps(*.h3m)</source>
-        <translation type="unfinished">Todos os mapas suportados (*.vmap *.h3m);;Mapas do VCMI (*.vmap);;Mapas do HoMM3 (*.h3m)</translation>
+        <translation>Todos os mapas suportados (*.vmap *.h3m);;Mapas VCMI (*.vmap);;Mapas HoMM3 (*.h3m)</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="376"/>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="402"/>
         <source>Error</source>
-        <translation type="unfinished"></translation>
+        <translation>Erro</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="376"/>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="402"/>
         <source>Could not open the file.</source>
-        <translation type="unfinished"></translation>
+        <translation>Não foi possível abrir o arquivo.</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="395"/>
         <source>Save map</source>
-        <translation type="unfinished">Salvar mapa</translation>
+        <translation>Salvar mapa</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="395"/>
         <source>VCMI maps (*.vmap);</source>
-        <translation type="unfinished"></translation>
+        <translation>Mapas VCMI (*.vmap);</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="395"/>
         <source>HoMM3 maps (*.h3m);</source>
-        <translation type="unfinished"></translation>
+        <translation>Mapas HoMM3 (*.h3m);</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="431"/>
         <source>Source scenario</source>
-        <translation type="unfinished"></translation>
+        <translation>Cenário de origem</translation>
     </message>
     <message>
         <location filename="../campaigneditor/scenarioproperties.cpp" line="441"/>
         <source>Hero</source>
-        <translation type="unfinished"></translation>
+        <translation>Herói</translation>
     </message>
 </context>
 <context>
@@ -2812,12 +2812,12 @@
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="14"/>
         <source>Scenario Properties</source>
-        <translation type="unfinished"></translation>
+        <translation>Propriedades do Cenário</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="20"/>
         <source>Select a bonus type</source>
-        <translation type="unfinished"></translation>
+        <translation>Selecione um tipo de bônus</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="27"/>
@@ -2825,13 +2825,13 @@
         <location filename="../campaigneditor/startingbonus.ui" line="110"/>
         <location filename="../campaigneditor/startingbonus.ui" line="270"/>
         <source>Spell</source>
-        <translation type="unfinished">Feitiço</translation>
+        <translation>Feitiço</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="34"/>
         <location filename="../campaigneditor/startingbonus.ui" line="134"/>
         <source>Creature</source>
-        <translation type="unfinished"></translation>
+        <translation>Criatura</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="41"/>
@@ -2839,26 +2839,26 @@
         <location filename="../campaigneditor/startingbonus.ui" line="190"/>
         <location filename="../campaigneditor/startingbonus.cpp" line="326"/>
         <source>Building</source>
-        <translation type="unfinished"></translation>
+        <translation>Construção</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="48"/>
         <location filename="../campaigneditor/startingbonus.ui" line="214"/>
         <location filename="../campaigneditor/startingbonus.ui" line="230"/>
         <source>Artifact</source>
-        <translation type="unfinished">Artefato</translation>
+        <translation>Artefato</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="55"/>
         <location filename="../campaigneditor/startingbonus.ui" line="254"/>
         <source>Spell scroll</source>
-        <translation type="unfinished"></translation>
+        <translation>Pergaminho de feitiço</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="62"/>
         <location filename="../campaigneditor/startingbonus.ui" line="294"/>
         <source>Primary skill</source>
-        <translation type="unfinished"></translation>
+        <translation>Habilidade primária</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="69"/>
@@ -2866,14 +2866,14 @@
         <location filename="../campaigneditor/startingbonus.ui" line="380"/>
         <location filename="../campaigneditor/startingbonus.cpp" line="334"/>
         <source>Secondary skill</source>
-        <translation type="unfinished"></translation>
+        <translation>Habilidade secundária</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="76"/>
         <location filename="../campaigneditor/startingbonus.ui" line="414"/>
         <location filename="../campaigneditor/startingbonus.cpp" line="336"/>
         <source>Resource</source>
-        <translation type="unfinished">Recurso</translation>
+        <translation>Recurso</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="100"/>
@@ -2883,118 +2883,118 @@
         <location filename="../campaigneditor/startingbonus.ui" line="300"/>
         <location filename="../campaigneditor/startingbonus.ui" line="370"/>
         <source>Recipient</source>
-        <translation type="unfinished"></translation>
+        <translation>Destinatário</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="150"/>
         <source>Creature type</source>
-        <translation type="unfinished"></translation>
+        <translation>Tipo de criatura</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="160"/>
         <location filename="../campaigneditor/startingbonus.ui" line="430"/>
         <source>Quantity</source>
-        <translation type="unfinished"></translation>
+        <translation>Quantidade</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="310"/>
         <source>Attack skill</source>
-        <translation type="unfinished"></translation>
+        <translation>Habilidade de Ataque</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="320"/>
         <source>Defense skill</source>
-        <translation type="unfinished"></translation>
+        <translation>Habilidade de Defesa</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="330"/>
         <source>Spell power</source>
-        <translation type="unfinished">Poder mágico</translation>
+        <translation>Poder Mágico</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="340"/>
         <source>Knowledge</source>
-        <translation type="unfinished">Conhecimento</translation>
+        <translation>Conhecimento</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="390"/>
         <source>Mastery</source>
-        <translation type="unfinished"></translation>
+        <translation>Maestria</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.ui" line="420"/>
         <source>Resource type</source>
-        <translation type="unfinished"></translation>
+        <translation>Tipo de recurso</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="34"/>
         <source>Edit Starting Bonus</source>
-        <translation type="unfinished"></translation>
+        <translation>Editar Bônus Inicial</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="59"/>
         <source>Strongest</source>
-        <translation type="unfinished"></translation>
+        <translation>Mais forte</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="60"/>
         <source>Generated</source>
-        <translation type="unfinished"></translation>
+        <translation>Gerado</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="61"/>
         <source>Random</source>
-        <translation type="unfinished">Aleatório</translation>
+        <translation>Aleatório</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="112"/>
         <source>Common (%1 and %2)</source>
-        <translation type="unfinished"></translation>
+        <translation>Comum (%1 e %2)</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="117"/>
         <source>Rare (%1, %2, %3, %4)</source>
-        <translation type="unfinished"></translation>
+        <translation>Raro (%1, %2, %3, %4)</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="289"/>
         <source>strongest hero</source>
-        <translation type="unfinished"></translation>
+        <translation>herói mais forte</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="291"/>
         <source>generated hero</source>
-        <translation type="unfinished"></translation>
+        <translation>herói gerado</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="293"/>
         <source>random hero</source>
-        <translation type="unfinished"></translation>
+        <translation>herói aleatório</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="322"/>
         <source>%1 spell for %2</source>
-        <translation type="unfinished"></translation>
+        <translation>Feitiço %1 para %2</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="324"/>
         <source>%1 %2 for %3</source>
-        <translation type="unfinished"></translation>
+        <translation>%1 %2 para %3</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="328"/>
         <source>%1 artifact for %2</source>
-        <translation type="unfinished"></translation>
+        <translation>Artefato %1 para %2</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="330"/>
         <source>%1 spell scroll for %2</source>
-        <translation type="unfinished"></translation>
+        <translation>Pergaminho de feitiço %1 para %2</translation>
     </message>
     <message>
         <location filename="../campaigneditor/startingbonus.cpp" line="332"/>
         <source>Primary skill (Attack: %1, Defense: %2, Spell: %3, Knowledge: %4) for %5</source>
-        <translation type="unfinished"></translation>
+        <translation>Habilidade primária (Ataque: %1, Defesa: %2, Poder Mágico: %3, Conhecimento: %4) para %5</translation>
     </message>
 </context>
 <context>

+ 46 - 39
server/CGameHandler.cpp

@@ -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);
-
-	if(creatureSlots.empty() && complain("No available slots for smart rebalancing"))
-		return false;
+	BulkRebalanceStacks bulkSRS;
 
-	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
+	// 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;
 
-	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);
+
+		creatureSlots.push_back(freeSlot);
 	}
-	auto currSlot = 0;
-	auto check = 0;
 
+	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);
 
-	if(check != totalCreatures)
-	{
-		complain((boost::format("Failure: totalCreatures=%d but check=%d") % totalCreatures % check).str());
-		return false;
+		unitsToMove -= rs.count;
+		slotsLeft -= 1;
 	}
+
 	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!");
 	}

+ 1 - 1
server/CGameHandler.h

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

+ 2 - 2
server/NetPacksServer.cpp

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

+ 1 - 1
server/ServerNetPackVisitors.h

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

+ 1 - 1
server/battles/BattleResultProcessor.cpp

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

+ 1 - 1
server/queries/MapQueries.cpp

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