Browse Source

Use variant identifier in netpacks where applicable

Ivan Savenko 1 year ago
parent
commit
6b81012f31
54 changed files with 391 additions and 304 deletions
  1. 2 2
      AI/Nullkiller/AIGateway.cpp
  2. 2 2
      AI/VCAI/VCAI.cpp
  3. 5 5
      CCallback.cpp
  4. 7 6
      CCallback.h
  5. 3 2
      client/Client.h
  6. 10 8
      client/NetPacksClient.cpp
  7. 6 5
      client/widgets/CAltar.cpp
  8. 1 1
      client/windows/CHeroWindow.cpp
  9. 19 6
      client/windows/CTradeWindow.cpp
  10. 5 5
      client/windows/GUIClasses.cpp
  11. 3 3
      client/windows/GUIClasses.h
  12. 2 0
      cmake_modules/VCMI_lib.cmake
  13. 2 5
      lib/CCreatureSet.cpp
  14. 1 7
      lib/CCreatureSet.h
  15. 4 2
      lib/IGameCallback.h
  16. 6 0
      lib/constants/Enumerations.h
  17. 1 1
      lib/constants/VariantIdentifier.h
  18. 7 7
      lib/mapObjects/CBank.cpp
  19. 1 1
      lib/mapObjects/CBank.h
  20. 12 19
      lib/mapObjects/CGCreature.cpp
  21. 1 12
      lib/mapObjects/CGCreature.h
  22. 5 5
      lib/mapObjects/CGDwelling.cpp
  23. 1 1
      lib/mapObjects/CGDwelling.h
  24. 5 5
      lib/mapObjects/CGHeroInstance.cpp
  25. 1 1
      lib/mapObjects/CGHeroInstance.h
  26. 1 1
      lib/mapObjects/CGMarket.cpp
  27. 7 10
      lib/mapObjects/CGObjectInstance.cpp
  28. 2 2
      lib/mapObjects/CGObjectInstance.h
  29. 11 11
      lib/mapObjects/CGTownBuilding.cpp
  30. 4 4
      lib/mapObjects/CGTownBuilding.h
  31. 12 12
      lib/mapObjects/CGTownInstance.cpp
  32. 1 1
      lib/mapObjects/CGTownInstance.h
  33. 14 15
      lib/mapObjects/CQuest.cpp
  34. 2 5
      lib/mapObjects/CQuest.h
  35. 7 7
      lib/mapObjects/CRewardableObject.cpp
  36. 1 1
      lib/mapObjects/CRewardableObject.h
  37. 1 1
      lib/mapObjects/IObjectInterface.cpp
  38. 2 1
      lib/mapObjects/IObjectInterface.h
  39. 12 12
      lib/mapObjects/MiscObjects.cpp
  40. 2 5
      lib/mapObjects/MiscObjects.h
  41. 25 19
      lib/networkPacks/NetPacksLib.cpp
  42. 69 0
      lib/networkPacks/ObjProperty.h
  43. 20 39
      lib/networkPacks/PacksForClient.h
  44. 5 3
      lib/networkPacks/PacksForServer.h
  45. 20 0
      lib/networkPacks/TradeItem.h
  46. 2 2
      lib/rewardable/Interface.cpp
  47. 2 2
      lib/spells/AdventureSpellMechanics.cpp
  48. 1 1
      lib/spells/effects/Moat.cpp
  49. 1 1
      mapeditor/inspector/armywidget.cpp
  50. 28 19
      server/CGameHandler.cpp
  51. 5 4
      server/CGameHandler.h
  52. 16 9
      server/NetPacksServer.cpp
  53. 2 2
      server/battles/BattleProcessor.cpp
  54. 4 4
      server/processors/PlayerMessageProcessor.cpp

+ 2 - 2
AI/Nullkiller/AIGateway.cpp

@@ -481,7 +481,7 @@ void AIGateway::objectPropertyChanged(const SetObjectProperty * sop)
 	NET_EVENT_HANDLER;
 	if(sop->what == ObjProperty::OWNER)
 	{
-		auto relations = myCb->getPlayerRelations(playerID, (PlayerColor)sop->val);
+		auto relations = myCb->getPlayerRelations(playerID, sop->identifier.as<PlayerColor>());
 		auto obj = myCb->getObj(sop->id, false);
 
 		if(!nullkiller) // crash protection
@@ -1419,7 +1419,7 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade
 				//TODO trade only as much as needed
 				if (toGive) //don't try to sell 0 resources
 				{
-					cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, g.resID, toGive);
+					cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, GameResID(g.resID), toGive);
 					accquiredResources = static_cast<int>(toGet * (it->resVal / toGive));
 					logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName());
 				}

+ 2 - 2
AI/VCAI/VCAI.cpp

@@ -564,7 +564,7 @@ void VCAI::objectPropertyChanged(const SetObjectProperty * sop)
 	NET_EVENT_HANDLER;
 	if(sop->what == ObjProperty::OWNER)
 	{
-		if(myCb->getPlayerRelations(playerID, (PlayerColor)sop->val) == PlayerRelations::ENEMIES)
+		if(myCb->getPlayerRelations(playerID, sop->identifier.as<PlayerColor>()) == PlayerRelations::ENEMIES)
 		{
 			//we want to visit objects owned by oppponents
 			auto obj = myCb->getObj(sop->id, false);
@@ -2160,7 +2160,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
 				//TODO trade only as much as needed
 				if (toGive) //don't try to sell 0 resources
 				{
-					cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, g.resID, toGive);
+					cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, GameResID(g.resID), toGive);
 					accquiredResources = static_cast<int>(toGet * (it->resVal / toGive));
 					logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName());
 				}

+ 5 - 5
CCallback.cpp

@@ -237,12 +237,12 @@ void CCallback::buyArtifact(const CGHeroInstance *hero, ArtifactID aid)
 	sendRequest(&pack);
 }
 
-void CCallback::trade(const IMarket * market, EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero)
+void CCallback::trade(const IMarket * market, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero)
 {
-	trade(market, mode, std::vector<ui32>(1, id1), std::vector<ui32>(1, id2), std::vector<ui32>(1, val1), hero);
+	trade(market, mode, std::vector(1, id1), std::vector(1, id2), std::vector(1, val1), hero);
 }
 
-void CCallback::trade(const IMarket * market, EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero)
+void CCallback::trade(const IMarket * market, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero)
 {
 	TradeOnMarketplace pack;
 	pack.marketId = dynamic_cast<const CGObjectInstance *>(market)->id;
@@ -254,9 +254,9 @@ void CCallback::trade(const IMarket * market, EMarketMode mode, const std::vecto
 	sendRequest(&pack);
 }
 
-void CCallback::setFormation(const CGHeroInstance * hero, bool tight)
+void CCallback::setFormation(const CGHeroInstance * hero, EArmyFormation mode)
 {
-	SetFormation pack(hero->id,tight);
+	SetFormation pack(hero->id, mode);
 	sendRequest(&pack);
 }
 

+ 7 - 6
CCallback.h

@@ -12,6 +12,7 @@
 #include "lib/CGameInfoCallback.h"
 #include "lib/battle/CPlayerBattleCallback.h"
 #include "lib/int3.h" // for int3
+#include "lib/networkPacks/TradeItem.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -78,8 +79,8 @@ public:
 	virtual bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE)=0; //if newID==-1 then best possible upgrade will be made
 	virtual void swapGarrisonHero(const CGTownInstance *town)=0;
 
-	virtual void trade(const IMarket * market, EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero = nullptr)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce
-	virtual void trade(const IMarket * market, EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr)=0;
+	virtual void trade(const IMarket * market, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero = nullptr)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce
+	virtual void trade(const IMarket * market, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr)=0;
 
 	virtual int selectionMade(int selection, QueryID queryID) =0;
 	virtual int sendQueryReply(std::optional<int32_t> reply, QueryID queryID) =0;
@@ -94,7 +95,7 @@ public:
 	virtual bool dismissCreature(const CArmedInstance *obj, SlotID stackPos)=0;
 	virtual void endTurn()=0;
 	virtual void buyArtifact(const CGHeroInstance *hero, ArtifactID aid)=0; //used to buy artifacts in towns (including spell book in the guild and war machines in blacksmith)
-	virtual void setFormation(const CGHeroInstance * hero, bool tight)=0;
+	virtual void setFormation(const CGHeroInstance * hero, EArmyFormation mode)=0;
 
 	virtual void save(const std::string &fname) = 0;
 	virtual void sendMessage(const std::string &mess, const CGObjectInstance * currentObject = nullptr) = 0;
@@ -181,9 +182,9 @@ public:
 	void endTurn() override;
 	void swapGarrisonHero(const CGTownInstance *town) override;
 	void buyArtifact(const CGHeroInstance *hero, ArtifactID aid) override;
-	void trade(const IMarket * market, EMarketMode mode, ui32 id1, ui32 id2, ui32 val1, const CGHeroInstance * hero = nullptr) override;
-	void trade(const IMarket * market, EMarketMode mode, const std::vector<ui32> & id1, const std::vector<ui32> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr) override;
-	void setFormation(const CGHeroInstance * hero, bool tight) override;
+	void trade(const IMarket * market, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero = nullptr) override;
+	void trade(const IMarket * market, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr) override;
+	void setFormation(const CGHeroInstance * hero, EArmyFormation mode) override;
 	void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero) override;
 	void save(const std::string &fname) override;
 	void sendMessage(const std::string &mess, const CGObjectInstance * currentObject = nullptr) override;

+ 3 - 2
client/Client.h

@@ -162,7 +162,7 @@ public:
 
 	void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> & spells) override {};
 	bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override {return false;};
-	void createObject(const int3 & visitablePosition, const PlayerColor & initiator, Obj type, int32_t subtype ) override {};
+	void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) override {};
 	void setOwner(const CGObjectInstance * obj, PlayerColor owner) override {};
 	void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs = false) override {};
 	void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs = false) override {};
@@ -212,7 +212,8 @@ public:
 	void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, ETileVisibility mode) override {}
 	void changeFogOfWar(std::unordered_set<int3> & tiles, PlayerColor player, ETileVisibility mode) override {}
 
-	void setObjProperty(ObjectInstanceID objid, int prop, si64 val) override {}
+	void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value) override {};
+	void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override {};
 
 	void showInfoDialog(InfoWindow * iw) override {};
 	void showInfoDialog(const std::string & msg, PlayerColor player) override {};

+ 10 - 8
client/NetPacksClient.cpp

@@ -350,15 +350,16 @@ void ApplyClientNetPackVisitor::visitGiveBonus(GiveBonus & pack)
 	cl.invalidatePaths();
 	switch(pack.who)
 	{
-	case GiveBonus::ETarget::HERO:
+	case GiveBonus::ETarget::OBJECT:
 		{
-			const CGHeroInstance *h = gs.getHero(ObjectInstanceID(pack.id));
-			callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroBonusChanged, h, pack.bonus, true);
+			const CGHeroInstance *h = gs.getHero(pack.id.as<ObjectInstanceID>());
+			if (h)
+				callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroBonusChanged, h, pack.bonus, true);
 		}
 		break;
 	case GiveBonus::ETarget::PLAYER:
 		{
-			callInterfaceIfPresent(cl, PlayerColor(pack.id), &IGameEventsReceiver::playerBonusChanged, pack.bonus, true);
+			callInterfaceIfPresent(cl, pack.id.as<PlayerColor>(), &IGameEventsReceiver::playerBonusChanged, pack.bonus, true);
 		}
 		break;
 	}
@@ -433,16 +434,17 @@ void ApplyClientNetPackVisitor::visitRemoveBonus(RemoveBonus & pack)
 	cl.invalidatePaths();
 	switch(pack.who)
 	{
-	case GiveBonus::ETarget::HERO:
+	case GiveBonus::ETarget::OBJECT:
 		{
-			const CGHeroInstance *h = gs.getHero(ObjectInstanceID(pack.whoID));
-			callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroBonusChanged, h, pack.bonus, false);
+			const CGHeroInstance *h = gs.getHero(pack.whoID.as<ObjectInstanceID>());
+			if (h)
+				callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroBonusChanged, h, pack.bonus, false);
 		}
 		break;
 	case GiveBonus::ETarget::PLAYER:
 		{
 			//const PlayerState *p = gs.getPlayerState(pack.id);
-			callInterfaceIfPresent(cl, PlayerColor(pack.whoID), &IGameEventsReceiver::playerBonusChanged, pack.bonus, false);
+			callInterfaceIfPresent(cl, pack.whoID.as<PlayerColor>(), &IGameEventsReceiver::playerBonusChanged, pack.bonus, false);
 		}
 		break;
 	}

+ 6 - 5
client/widgets/CAltar.cpp

@@ -99,14 +99,15 @@ TExpType CAltarArtifacts::calcExpAltarForHero()
 
 void CAltarArtifacts::makeDeal()
 {
-	std::vector<ui32> positions;
+	std::vector<TradeItemSell> positions;
 	for(const auto art : arts->artifactsOnAltar)
 	{
 		positions.push_back(hero->getSlotByInstance(art));
 	}
-	std::sort(positions.begin(), positions.end(), std::greater<>());
+	std::sort(positions.begin(), positions.end());
+	std::reverse(positions.begin(), positions.end());
 
-	LOCPLINT->cb->trade(market, EMarketMode::ARTIFACT_EXP, positions, {}, {}, hero);
+	LOCPLINT->cb->trade(market, EMarketMode::ARTIFACT_EXP, positions, std::vector<TradeItemBuy>(), std::vector<ui32>(), hero);
 	arts->artifactsOnAltar.clear();
 
 	for(auto item : items[0])
@@ -374,14 +375,14 @@ void CAltarCreatures::makeDeal()
 	unitsSlider->scrollTo(0);
 	expForHero->setText(std::to_string(0));
 
-	std::vector<ui32> ids;
+	std::vector<TradeItemSell> ids;
 	std::vector<ui32> toSacrifice;
 
 	for(int i = 0; i < unitsOnAltar.size(); i++)
 	{
 		if(unitsOnAltar[i])
 		{
-			ids.push_back(i);
+			ids.push_back(SlotID(i));
 			toSacrifice.push_back(unitsOnAltar[i]);
 		}
 	}

+ 1 - 1
client/windows/CHeroWindow.cpp

@@ -306,7 +306,7 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
 	formations->resetCallback();
 	//setting formations
 	formations->setSelected(curHero->formation == EArmyFormation::TIGHT ? 1 : 0);
-	formations->addCallback([=](int value){ LOCPLINT->cb->setFormation(curHero, value);});
+	formations->addCallback([=](int value){ LOCPLINT->cb->setFormation(curHero, static_cast<EArmyFormation>(value));});
 
 	morale->set(curHero);
 	luck->set(curHero);

+ 19 - 6
client/windows/CTradeWindow.cpp

@@ -503,14 +503,27 @@ void CMarketplaceWindow::makeDeal()
 
 	if(allowDeal)
 	{
-		if(slider)
+		switch(mode)
 		{
-			LOCPLINT->cb->trade(market, mode, leftIdToSend, hRight->id, slider->getValue() * r1, hero);
+		case EMarketMode::RESOURCE_RESOURCE:
+			LOCPLINT->cb->trade(market, mode, GameResID(leftIdToSend), GameResID(hRight->id), slider->getValue() * r1, hero);
 			slider->scrollTo(0);
-		}
-		else
-		{
-			LOCPLINT->cb->trade(market, mode, leftIdToSend, hRight->id, r2, hero);
+			break;
+		case EMarketMode::CREATURE_RESOURCE:
+			LOCPLINT->cb->trade(market, mode, SlotID(leftIdToSend), GameResID(hRight->id), slider->getValue() * r1, hero);
+			slider->scrollTo(0);
+			break;
+		case EMarketMode::RESOURCE_PLAYER:
+			LOCPLINT->cb->trade(market, mode, GameResID(leftIdToSend), PlayerColor(hRight->id), slider->getValue() * r1, hero);
+			slider->scrollTo(0);
+			break;
+
+		case EMarketMode::RESOURCE_ARTIFACT:
+			LOCPLINT->cb->trade(market, mode, GameResID(leftIdToSend), ArtifactID(hRight->id), r2, hero);
+			break;
+		case EMarketMode::ARTIFACT_RESOURCE:
+			LOCPLINT->cb->trade(market, mode, ArtifactInstanceID(leftIdToSend), GameResID(hRight->id), r2, hero);
+			break;
 		}
 	}
 

+ 5 - 5
client/windows/GUIClasses.cpp

@@ -988,7 +988,7 @@ void CTransformerWindow::makeDeal()
 	for(auto & elem : items)
 	{
 		if(!elem->left)
-			LOCPLINT->cb->trade(market, EMarketMode::CREATURE_UNDEAD, elem->id, 0, 0, hero);
+			LOCPLINT->cb->trade(market, EMarketMode::CREATURE_UNDEAD, SlotID(elem->id), {}, {}, hero);
 	}
 }
 
@@ -1163,13 +1163,13 @@ void CUniversityWindow::close()
 	CStatusbarWindow::close();
 }
 
-void CUniversityWindow::makeDeal(int skill)
+void CUniversityWindow::makeDeal(SecondarySkill skill)
 {
-	LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_SKILL, 6, skill, 1, hero);
+	LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_SKILL, GameResID(GameResID::GOLD), skill, 1, hero);
 }
 
 
-CUnivConfirmWindow::CUnivConfirmWindow(CUniversityWindow * owner_, int SKILL, bool available)
+CUnivConfirmWindow::CUnivConfirmWindow(CUniversityWindow * owner_, SecondarySkill SKILL, bool available)
 	: CStatusbarWindow(PLAYER_COLORED, ImagePath::builtin("UNIVERS2.PCX")),
 	owner(owner_)
 {
@@ -1204,7 +1204,7 @@ CUnivConfirmWindow::CUnivConfirmWindow(CUniversityWindow * owner_, int SKILL, bo
 	statusbar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
 }
 
-void CUnivConfirmWindow::makeDeal(int skill)
+void CUnivConfirmWindow::makeDeal(SecondarySkill skill)
 {
 	owner->makeDeal(skill);
 	close();

+ 3 - 3
client/windows/GUIClasses.h

@@ -403,7 +403,7 @@ class CUniversityWindow : public CStatusbarWindow
 public:
 	CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market, const std::function<void()> & onWindowClosed);
 
-	void makeDeal(int skill);
+	void makeDeal(SecondarySkill skill);
 	void close();
 };
 
@@ -422,10 +422,10 @@ class CUnivConfirmWindow : public CStatusbarWindow
 	std::shared_ptr<CAnimImage> costIcon;
 	std::shared_ptr<CLabel> cost;
 
-	void makeDeal(int skill);
+	void makeDeal(SecondarySkill skill);
 
 public:
-	CUnivConfirmWindow(CUniversityWindow * PARENT, int SKILL, bool available);
+	CUnivConfirmWindow(CUniversityWindow * PARENT, SecondarySkill SKILL, bool available);
 };
 
 /// Garrison window where you can take creatures out of the hero to place it on the garrison

+ 2 - 0
cmake_modules/VCMI_lib.cmake

@@ -484,12 +484,14 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/networkPacks/EOpenWindowMode.h
 		${MAIN_LIB_DIR}/networkPacks/NetPacksBase.h
 		${MAIN_LIB_DIR}/networkPacks/NetPackVisitor.h
+		${MAIN_LIB_DIR}/networkPacks/ObjProperty.h
 		${MAIN_LIB_DIR}/networkPacks/PacksForClient.h
 		${MAIN_LIB_DIR}/networkPacks/PacksForClientBattle.h
 		${MAIN_LIB_DIR}/networkPacks/PacksForLobby.h
 		${MAIN_LIB_DIR}/networkPacks/PacksForServer.h
 		${MAIN_LIB_DIR}/networkPacks/SetStackEffect.h
 		${MAIN_LIB_DIR}/networkPacks/StackLocation.h
+		${MAIN_LIB_DIR}/networkPacks/TradeItem.h
 
 		${MAIN_LIB_DIR}/pathfinder/INodeStorage.h
 		${MAIN_LIB_DIR}/pathfinder/CGPathNode.h

+ 2 - 5
lib/CCreatureSet.cpp

@@ -415,12 +415,9 @@ int CCreatureSet::stacksCount() const
 	return static_cast<int>(stacks.size());
 }
 
-void CCreatureSet::setFormation(bool tight)
+void CCreatureSet::setFormation(EArmyFormation mode)
 {
-	if (tight)
-		formation = EArmyFormation::TIGHT;
-	else
-		formation = EArmyFormation::LOOSE;
+	formation = mode;
 }
 
 void CCreatureSet::setStackCount(const SlotID & slot, TQuantity count)

+ 1 - 7
lib/CCreatureSet.h

@@ -203,12 +203,6 @@ public:
 	}
 };
 
-enum class EArmyFormation : uint8_t
-{
-	LOOSE,
-	TIGHT
-};
-
 namespace NArmyFormation
 {
 	static const std::vector<std::string> names{ "wide", "tight" };
@@ -235,7 +229,7 @@ public:
 	void addToSlot(const SlotID & slot, const CreatureID & cre, TQuantity count, bool allowMerging = true); //Adds stack to slot. Slot must be empty or with same type creature
 	void addToSlot(const SlotID & slot, CStackInstance * stack, bool allowMerging = true); //Adds stack to slot. Slot must be empty or with same type creature
 	void clearSlots() override;
-	void setFormation(bool tight);
+	void setFormation(EArmyFormation tight);
 	CArmedInstance *castToArmyObj();
 
 	//basic operations

+ 4 - 2
lib/IGameCallback.h

@@ -13,6 +13,7 @@
 
 #include "CGameInfoCallback.h" // for CGameInfoCallback
 #include "CRandomGenerator.h"
+#include "networkPacks/ObjProperty.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -73,14 +74,15 @@ public:
 class DLL_LINKAGE IGameEventCallback
 {
 public:
-	virtual void setObjProperty(ObjectInstanceID objid, int prop, si64 val) = 0;
+	virtual void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value = 0) = 0;
+	virtual void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) = 0;
 
 	virtual void showInfoDialog(InfoWindow * iw) = 0;
 	virtual void showInfoDialog(const std::string & msg, PlayerColor player) = 0;
 
 	virtual void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> &spells)=0;
 	virtual bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
-	virtual void createObject(const int3 & visitablePosition, const PlayerColor & initiator, Obj type, int32_t subtype = 0) = 0;
+	virtual void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) = 0;
 	virtual void setOwner(const CGObjectInstance * objid, PlayerColor owner)=0;
 	virtual void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false)=0;
 	virtual void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0;

+ 6 - 0
lib/constants/Enumerations.h

@@ -246,4 +246,10 @@ enum class ETileVisibility : int8_t // Fog of war change
 	REVEALED
 };
 
+enum class EArmyFormation : int8_t
+{
+	LOOSE,
+	TIGHT
+};
+
 VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/constants/VariantIdentifier.h

@@ -13,7 +13,7 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-/// This class represents field that may contain value of multiple different identifer types
+/// This class represents field that may contain value of multiple different identifier types
 template<typename... Types>
 class VariantIdentifier
 {

+ 7 - 7
lib/mapObjects/CBank.cpp

@@ -94,12 +94,12 @@ void CBank::setConfig(const BankConfig & config)
 		setCreature (SlotID(stacksCount()), stack.type->getId(), stack.count);
 }
 
-void CBank::setPropertyDer (ui8 what, ui32 val)
+void CBank::setPropertyDer (ObjProperty what, ObjPropertyID identifier)
 {
 	switch (what)
 	{
 		case ObjProperty::BANK_DAYCOUNTER: //daycounter
-				daycounter+=val;
+				daycounter+= identifier.getNum();
 			break;
 		case ObjProperty::BANK_RESET:
 			// FIXME: Object reset must be done by separate netpack from server
@@ -119,9 +119,9 @@ void CBank::newTurn(CRandomGenerator & rand) const
 		if (resetDuration != 0)
 		{
 			if (daycounter >= resetDuration)
-				cb->setObjProperty (id, ObjProperty::BANK_RESET, 0); //daycounter 0
+				cb->setObjPropertyValue(id, ObjProperty::BANK_RESET); //daycounter 0
 			else
-				cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++
+				cb->setObjPropertyValue(id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++
 		}
 	}
 }
@@ -210,7 +210,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
 		case Obj::CRYPT:
 		{
 			GiveBonus gbonus;
-			gbonus.id = hero->id.getNum();
+			gbonus.id = hero->id;
 			gbonus.bonus.duration = BonusDuration::ONE_BATTLE;
 			gbonus.bonus.source = BonusSource::OBJECT_TYPE;
 			gbonus.bonus.sid = BonusSourceID(ID);
@@ -240,7 +240,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
 		{
 			GiveBonus gb;
 			gb.bonus = Bonus(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT_INSTANCE, -2, BonusSourceID(id), VLC->generaltexth->arraytxt[70]);
-			gb.id = hero->id.getNum();
+			gb.id = hero->id;
 			cb->giveHeroBonus(&gb);
 			textID = 107;
 			iw.components.emplace_back(ComponentType::LUCK, -2);
@@ -369,7 +369,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
 			cb->showInfoDialog(&iw);
 			cb->giveCreatures(this, hero, ourArmy, false);
 		}
-		cb->setObjProperty(id, ObjProperty::BANK_CLEAR, 0); //bc = nullptr
+		cb->setObjPropertyValue(id, ObjProperty::BANK_CLEAR); //bc = nullptr
 	}
 }
 

+ 1 - 1
lib/mapObjects/CBank.h

@@ -23,7 +23,7 @@ class DLL_LINKAGE CBank : public CArmedInstance
 	ui32 resetDuration;
 	bool coastVisitable;
 
-	void setPropertyDer(ui8 what, ui32 val) override;
+	void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
 	void doVisit(const CGHeroInstance * hero) const;
 
 public:

+ 12 - 19
lib/mapObjects/CGCreature.cpp

@@ -266,31 +266,28 @@ void CGCreature::newTurn(CRandomGenerator & rand) const
 		if (stacks.begin()->second->count < VLC->settings()->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 + VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT)) / 100);
-			cb->setObjProperty(id, ObjProperty::MONSTER_COUNT, std::min<uint32_t>(power / 1000, VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP))); //set new amount
-			cb->setObjProperty(id, ObjProperty::MONSTER_POWER, power); //increase temppower
+			cb->setObjPropertyValue(id, ObjProperty::MONSTER_COUNT, std::min<uint32_t>(power / 1000, VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP))); //set new amount
+			cb->setObjPropertyValue(id, ObjProperty::MONSTER_POWER, power); //increase temppower
 		}
 	}
 	if (VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
-		cb->setObjProperty(id, ObjProperty::MONSTER_EXP, VLC->settings()->getInteger(EGameSettings::CREATURES_DAILY_STACK_EXPERIENCE)); //for testing purpose
+		cb->setObjPropertyValue(id, ObjProperty::MONSTER_EXP, VLC->settings()->getInteger(EGameSettings::CREATURES_DAILY_STACK_EXPERIENCE)); //for testing purpose
 }
-void CGCreature::setPropertyDer(ui8 what, ui32 val)
+void CGCreature::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
 {
 	switch (what)
 	{
 		case ObjProperty::MONSTER_COUNT:
-			stacks[SlotID(0)]->count = val;
+			stacks[SlotID(0)]->count = identifier.getNum();
 			break;
 		case ObjProperty::MONSTER_POWER:
-			temppower = val;
+			temppower = identifier.getNum();
 			break;
 		case ObjProperty::MONSTER_EXP:
-			giveStackExp(val);
-			break;
-		case ObjProperty::MONSTER_RESTORE_TYPE:
-			formation.basicType = val;
+			giveStackExp(identifier.getNum());
 			break;
 		case ObjProperty::MONSTER_REFUSED_JOIN:
-			refusedJoining = val;
+			refusedJoining = identifier.getNum();
 			break;
 	}
 }
@@ -368,7 +365,7 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
 void CGCreature::fleeDecision(const CGHeroInstance *h, ui32 pursue) const
 {
 	if(refusedJoining)
-		cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, false);
+		cb->setObjPropertyValue(id, ObjProperty::MONSTER_REFUSED_JOIN, false);
 
 	if(pursue)
 	{
@@ -386,7 +383,7 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co
 	{
 		if(takenAction(h,false) == FLEE)
 		{
-			cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, true);
+			cb->setObjPropertyValue(id, ObjProperty::MONSTER_REFUSED_JOIN, true);
 			flee(h);
 		}
 		else //they fight
@@ -421,10 +418,6 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co
 void CGCreature::fight( const CGHeroInstance *h ) const
 {
 	//split stacks
-	//TODO: multiple creature types in a stack?
-	int basicType = stacks.begin()->second->type->getId();
-	cb->setObjProperty(id, ObjProperty::MONSTER_RESTORE_TYPE, basicType); //store info about creature stack
-
 	int stacksCount = getNumberOfStacks(h);
 	//source: http://heroescommunity.com/viewthread.php3?TID=27539&PID=1266335#focus
 
@@ -488,7 +481,7 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
 	{
 		//merge stacks into one
 		TSlots::const_iterator i;
-		CCreature * cre = VLC->creh->objects[formation.basicType];
+		const CCreature * cre = getCreature().toCreature();
 		for(i = stacks.begin(); i != stacks.end(); i++)
 		{
 			if(cre->isMyUpgrade(i->second->type))
@@ -513,7 +506,7 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
 				cb->moveStack(StackLocation(this, i->first), StackLocation(this, slot), i->second->count);
 		}
 
-		cb->setObjProperty(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties
+		cb->setObjPropertyValue(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties
 	}
 }
 

+ 1 - 12
lib/mapObjects/CGCreature.h

@@ -54,17 +54,6 @@ public:
 	bool containsUpgradedStack() const;
 	int getNumberOfStacks(const CGHeroInstance *hero) const;
 
-	struct DLL_LINKAGE formationInfo // info about merging stacks after battle back into one
-	{
-		si32 basicType;
-		ui8 upgrade; //random seed used to determine number of stacks and is there's upgraded stack
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & basicType;
-			h & upgrade;
-		}
-	} formation;
-
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CArmedInstance&>(*this);
@@ -80,7 +69,7 @@ public:
 		h & formation;
 	}
 protected:
-	void setPropertyDer(ui8 what, ui32 val) override;
+	void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
 	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 
 private:

+ 5 - 5
lib/mapObjects/CGDwelling.cpp

@@ -201,7 +201,7 @@ void CGDwelling::initObj(CRandomGenerator & rand)
 	}
 }
 
-void CGDwelling::setPropertyDer(ui8 what, ui32 val)
+void CGDwelling::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
 {
 	switch (what)
 	{
@@ -214,14 +214,14 @@ void CGDwelling::setPropertyDer(ui8 what, ui32 val)
 					std::vector<ConstTransitivePtr<CGDwelling> >* dwellings = &cb->gameState()->players[tempOwner].dwellings;
 					dwellings->erase (std::find(dwellings->begin(), dwellings->end(), this));
 				}
-				if (PlayerColor(val) != PlayerColor::NEUTRAL) //can new owner be neutral?
-					cb->gameState()->players[PlayerColor(val)].dwellings.emplace_back(this);
+				if (identifier.as<PlayerColor>().isValidPlayer())
+					cb->gameState()->players[identifier.as<PlayerColor>()].dwellings.emplace_back(this);
 			}
 			break;
 		case ObjProperty::AVAILABLE_CREATURE:
 			creatures.resize(1);
 			creatures[0].second.resize(1);
-			creatures[0].second[0] = CreatureID(val);
+			creatures[0].second[0] = identifier.as<CreatureID>();
 			break;
 	}
 }
@@ -300,7 +300,7 @@ void CGDwelling::newTurn(CRandomGenerator & rand) const
 
 	if(ID == Obj::REFUGEE_CAMP) //if it's a refugee camp, we need to pick an available creature
 	{
-		cb->setObjProperty(id, ObjProperty::AVAILABLE_CREATURE, VLC->creh->pickRandomMonster(rand));
+		cb->setObjPropertyID(id, ObjProperty::AVAILABLE_CREATURE, VLC->creh->pickRandomMonster(rand));
 	}
 
 	bool change = false;

+ 1 - 1
lib/mapObjects/CGDwelling.h

@@ -52,7 +52,7 @@ private:
 	void initObj(CRandomGenerator & rand) override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	void newTurn(CRandomGenerator & rand) const override;
-	void setPropertyDer(ui8 what, ui32 val) override;
+	void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
 	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
 	std::vector<Component> getPopupComponents(PlayerColor player) const override;

+ 5 - 5
lib/mapObjects/CGHeroInstance.cpp

@@ -348,7 +348,7 @@ void CGHeroInstance::initHero(CRandomGenerator & rand)
 	if (gender == EHeroGender::DEFAULT)
 		gender = type->gender;
 
-	setFormation(false);
+	setFormation(EArmyFormation::LOOSE);
 	if (!stacksCount()) //standard army//initial army
 	{
 		initArmy(rand);
@@ -501,7 +501,7 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
 			SetMovePoints smp;
 			smp.hid = id;
 			
-			cb->setManaPoints (id, manaLimit());		
+			cb->setManaPoints (id, manaLimit());
 			
 			ObjectInstanceID boatId;
 			const auto boatPos = visitablePos();
@@ -520,7 +520,7 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
 				smp.val = movementPointsLimit(true);
 			}
 			cb->giveHero(id, h->tempOwner, boatId); //recreates def and adds hero to player
-			cb->setObjProperty(id, ObjProperty::ID, Obj::HERO); //set ID to 34 AFTER hero gets correct flag color
+			cb->setObjPropertyID(id, ObjProperty::ID, Obj(Obj::HERO)); //set ID to 34 AFTER hero gets correct flag color
 			cb->setMovePoints (&smp);
 
 			h->showInfoDialog(102);
@@ -620,10 +620,10 @@ void CGHeroInstance::updateSkillBonus(const SecondarySkill & which, int val)
 		addNewBonus(std::make_shared<Bonus>(*b));
 }
 
-void CGHeroInstance::setPropertyDer( ui8 what, ui32 val )
+void CGHeroInstance::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
 {
 	if(what == ObjProperty::PRIMARY_STACK_COUNT)
-		setStackCount(SlotID(0), val);
+		setStackCount(SlotID(0), identifier.getNum());
 }
 
 double CGHeroInstance::getFightingStrength() const

+ 1 - 1
lib/mapObjects/CGHeroInstance.h

@@ -307,7 +307,7 @@ public:
 	bool isCoastVisitable() const override;
 	BattleField getBattlefield() const override;
 protected:
-	void setPropertyDer(ui8 what, ui32 val) override;//synchr
+	void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;//synchr
 	///common part of hero instance and hero definition
 	void serializeCommonOptions(JsonSerializeFormat & handler);
 

+ 1 - 1
lib/mapObjects/CGMarket.cpp

@@ -91,7 +91,7 @@ void CGBlackMarket::newTurn(CRandomGenerator & rand) const
 		return;
 
 	SetAvailableArtifacts saa;
-	saa.id = id.getNum();
+	saa.id = id;
 	cb->pickAllowedArtsSet(saa.arts, rand);
 	cb->sendAndApply(&saa);
 }

+ 7 - 10
lib/mapObjects/CGObjectInstance.cpp

@@ -181,23 +181,20 @@ void CGObjectInstance::initObj(CRandomGenerator & rand)
 	}
 }
 
-void CGObjectInstance::setProperty( ui8 what, ui32 val )
+void CGObjectInstance::setProperty( ObjProperty what, ObjPropertyID identifier )
 {
-	setPropertyDer(what, val); // call this before any actual changes (needed at least for dwellings)
+	setPropertyDer(what, identifier); // call this before any actual changes (needed at least for dwellings)
 
 	switch(what)
 	{
 	case ObjProperty::OWNER:
-		tempOwner = PlayerColor(val);
+		tempOwner = identifier.as<PlayerColor>();
 		break;
 	case ObjProperty::BLOCKVIS:
-		blockVisit = val;
+		blockVisit = identifier.getNum();
 		break;
 	case ObjProperty::ID:
-		ID = Obj(val);
-		break;
-	case ObjProperty::SUBID:
-		subID = val;
+		ID = identifier.as<MapObjectID>();
 		break;
 	}
 }
@@ -207,7 +204,7 @@ TObjectTypeHandler CGObjectInstance::getObjectHandler() const
 	return VLC->objtypeh->getHandlerFor(ID, subID);
 }
 
-void CGObjectInstance::setPropertyDer( ui8 what, ui32 val )
+void CGObjectInstance::setPropertyDer( ObjProperty what, ObjPropertyID identifier )
 {}
 
 int3 CGObjectInstance::getSightCenter() const
@@ -229,7 +226,7 @@ void CGObjectInstance::giveDummyBonus(const ObjectInstanceID & heroID, BonusDura
 {
 	GiveBonus gbonus;
 	gbonus.bonus.type = BonusType::NONE;
-	gbonus.id = heroID.getNum();
+	gbonus.id = heroID;
 	gbonus.bonus.duration = duration;
 	gbonus.bonus.source = BonusSource::OBJECT_TYPE;
 	gbonus.bonus.sid = BonusSourceID(ID);

+ 2 - 2
lib/mapObjects/CGObjectInstance.h

@@ -127,7 +127,7 @@ public:
 	void pickRandomObject(CRandomGenerator & rand) override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	/// method for synchronous update. Note: For new properties classes should override setPropertyDer instead
-	void setProperty(ui8 what, ui32 val) final;
+	void setProperty(ObjProperty what, ObjPropertyID identifier) final;
 
 	virtual void afterAddToMap(CMap * map);
 	virtual void afterRemoveFromMap(CMap * map);
@@ -154,7 +154,7 @@ public:
 
 protected:
 	/// virtual method that allows synchronously update object state on server and all clients
-	virtual void setPropertyDer(ui8 what, ui32 val);
+	virtual void setPropertyDer(ObjProperty what, ObjPropertyID identifier);
 
 	/// Called mostly during map randomization to turn random object into a regular one (e.g. "Random Monster" into "Pikeman")
 	void setType(MapObjectID ID, MapObjectSubID subID);

+ 11 - 11
lib/mapObjects/CGTownBuilding.cpp

@@ -120,12 +120,12 @@ COPWBonus::COPWBonus(const BuildingID & bid, BuildingSubID::EBuildingSubID subId
 	indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
 }
 
-void COPWBonus::setProperty(ui8 what, ui32 val)
+void COPWBonus::setProperty(ObjProperty what, ObjPropertyID identifier)
 {
 	switch (what)
 	{
 		case ObjProperty::VISITORS:
-			visitors.insert(val);
+			visitors.insert(identifier.as<ObjectInstanceID>());
 			break;
 		case ObjProperty::STRUCTURE_CLEAR_VISITORS:
 			visitors.clear();
@@ -148,7 +148,7 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
 			{
 				GiveBonus gb;
 				gb.bonus = Bonus(BonusDuration::ONE_WEEK, BonusType::MOVEMENT, BonusSource::OBJECT_TYPE, 600, BonusSourceID(Obj(Obj::STABLES)), BonusCustomSubtype::heroMovementLand, VLC->generaltexth->arraytxt[100]);
-				gb.id = heroID.getNum();
+				gb.id = heroID;
 				cb->giveHeroBonus(&gb);
 
 				SetMovePoints mp;
@@ -187,10 +187,10 @@ CTownBonus::CTownBonus(const BuildingID & index, BuildingSubID::EBuildingSubID s
 	indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
 }
 
-void CTownBonus::setProperty (ui8 what, ui32 val)
+void CTownBonus::setProperty(ObjProperty what, ObjPropertyID identifier)
 {
 	if(what == ObjProperty::VISITORS)
-		visitors.insert(ObjectInstanceID(val));
+		visitors.insert(identifier.as<ObjectInstanceID>());
 }
 
 void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
@@ -272,7 +272,7 @@ void CTownBonus::applyBonuses(CGHeroInstance * h, const BonusList & bonuses) con
 			bonus->duration = BonusDuration::ONE_DAY;
 		}
 		gb.bonus = * bonus;
-		gb.id = h->id.getNum();
+		gb.id = h->id;
 		cb->giveHeroBonus(&gb);
 
 		if(bonus->duration == BonusDuration::PERMANENT)
@@ -318,21 +318,21 @@ void CTownRewardableBuilding::newTurn(CRandomGenerator & rand) const
 	{
 		if(configuration.resetParameters.rewards)
 		{
-			cb->setObjProperty(town->id, ObjProperty::REWARD_RANDOMIZE, indexOnTV);
+			cb->setObjPropertyValue(town->id, ObjProperty::REWARD_RANDOMIZE, indexOnTV);
 		}
 		if(configuration.resetParameters.visitors)
 		{
-			cb->setObjProperty(town->id, ObjProperty::STRUCTURE_CLEAR_VISITORS, indexOnTV);
+			cb->setObjPropertyValue(town->id, ObjProperty::STRUCTURE_CLEAR_VISITORS, indexOnTV);
 		}
 	}
 }
 
-void CTownRewardableBuilding::setProperty(ui8 what, ui32 val)
+void CTownRewardableBuilding::setProperty(ObjProperty what, ObjPropertyID identifier)
 {
 	switch (what)
 	{
 		case ObjProperty::VISITORS:
-			visitors.insert(ObjectInstanceID(val));
+			visitors.insert(identifier.as<ObjectInstanceID>());
 			break;
 		case ObjProperty::STRUCTURE_CLEAR_VISITORS:
 			visitors.clear();
@@ -341,7 +341,7 @@ void CTownRewardableBuilding::setProperty(ui8 what, ui32 val)
 			initObj(cb->gameState()->getRandomGenerator());
 			break;
 		case ObjProperty::REWARD_SELECT:
-			selectedReward = val;
+			selectedReward = identifier.getNum();
 			break;
 	}
 }

+ 4 - 4
lib/mapObjects/CGTownBuilding.h

@@ -69,8 +69,8 @@ protected:
 class DLL_LINKAGE COPWBonus : public CGTownBuilding
 {///used for OPW bonusing structures
 public:
-	std::set<si32> visitors;
-	void setProperty(ui8 what, ui32 val) override;
+	std::set<ObjectInstanceID> visitors;
+	void setProperty(ObjProperty what, ObjPropertyID identifier) override;
 	void onHeroVisit (const CGHeroInstance * h) const override;
 
 	COPWBonus(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * TOWN);
@@ -89,7 +89,7 @@ class DLL_LINKAGE CTownBonus : public CGTownBuilding
 ///feel free to merge inheritance tree
 public:
 	std::set<ObjectInstanceID> visitors;
-	void setProperty(ui8 what, ui32 val) override;
+	void setProperty(ObjProperty what, ObjPropertyID identifier) override;
 	void onHeroVisit (const CGHeroInstance * h) const override;
 
 	CTownBonus(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * TOWN);
@@ -117,7 +117,7 @@ class DLL_LINKAGE CTownRewardableBuilding : public CGTownBuilding, public Reward
 	void grantReward(ui32 rewardID, const CGHeroInstance * hero) const;
 	
 public:
-	void setProperty(ui8 what, ui32 val) override;
+	void setProperty(ObjProperty what, ObjPropertyID identifier) override;
 	void onHeroVisit(const CGHeroInstance * h) const override;
 	
 	void newTurn(CRandomGenerator & rand) const override;

+ 12 - 12
lib/mapObjects/CGTownInstance.cpp

@@ -53,28 +53,28 @@ int CGTownInstance::getSightRadius() const //returns sight distance
 	return ret;
 }
 
-void CGTownInstance::setPropertyDer(ui8 what, ui32 val)
+void CGTownInstance::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
 {
 ///this is freakin' overcomplicated solution
 	switch (what)
 	{
 		case ObjProperty::STRUCTURE_ADD_VISITING_HERO:
-			bonusingBuildings[val]->setProperty(ObjProperty::VISITORS, visitingHero->id.getNum());
+			bonusingBuildings[identifier.getNum()]->setProperty(ObjProperty::VISITORS, visitingHero->id);
 			break;
 		case ObjProperty::STRUCTURE_CLEAR_VISITORS:
-			bonusingBuildings[val]->setProperty(ObjProperty::STRUCTURE_CLEAR_VISITORS, 0);
+			bonusingBuildings[identifier.getNum()]->setProperty(ObjProperty::STRUCTURE_CLEAR_VISITORS, NumericID(0));
 			break;
 		case ObjProperty::STRUCTURE_ADD_GARRISONED_HERO: //add garrisoned hero to visitors
-			bonusingBuildings[val]->setProperty(ObjProperty::VISITORS, garrisonHero->id.getNum());
+			bonusingBuildings[identifier.getNum()]->setProperty(ObjProperty::VISITORS, garrisonHero->id);
 			break;
 		case ObjProperty::BONUS_VALUE_FIRST:
-			bonusValue.first = val;
+			bonusValue.first = identifier.getNum();
 			break;
 		case ObjProperty::BONUS_VALUE_SECOND:
-			bonusValue.second = val;
+			bonusValue.second = identifier.getNum();
 			break;
 		case ObjProperty::REWARD_RANDOMIZE:
-			bonusingBuildings[val]->setProperty(ObjProperty::REWARD_RANDOMIZE, 0);
+			bonusingBuildings[identifier.getNum()]->setProperty(ObjProperty::REWARD_RANDOMIZE, NumericID(0));
 			break;
 	}
 }
@@ -534,12 +534,12 @@ void CGTownInstance::newTurn(CRandomGenerator & rand) const
 			resID = (resID==2)?1:resID;
 			int resVal = rand.nextInt(1, 4);//with size 1..4
 			cb->giveResource(tempOwner, static_cast<EGameResID>(resID), resVal);
-			cb->setObjProperty (id, ObjProperty::BONUS_VALUE_FIRST, resID);
-			cb->setObjProperty (id, ObjProperty::BONUS_VALUE_SECOND, resVal);
+			cb->setObjPropertyValue(id, ObjProperty::BONUS_VALUE_FIRST, resID);
+			cb->setObjPropertyValue(id, ObjProperty::BONUS_VALUE_SECOND, resVal);
 		}
 		
 		for(const auto * manaVortex : getBonusingBuildings(BuildingSubID::MANA_VORTEX))
-			cb->setObjProperty(id, ObjProperty::STRUCTURE_CLEAR_VISITORS, manaVortex->indexOnTV); //reset visitors for Mana Vortex
+			cb->setObjPropertyValue(id, ObjProperty::STRUCTURE_CLEAR_VISITORS, manaVortex->indexOnTV); //reset visitors for Mana Vortex
 
 		//get Mana Vortex or Stables bonuses
 		//same code is in the CGameHandler::buildStructure method
@@ -1072,9 +1072,9 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID &
 void CGTownInstance::addHeroToStructureVisitors(const CGHeroInstance *h, si64 structureInstanceID ) const
 {
 	if(visitingHero == h)
-		cb->setObjProperty(id, ObjProperty::STRUCTURE_ADD_VISITING_HERO, structureInstanceID); //add to visitors
+		cb->setObjPropertyValue(id, ObjProperty::STRUCTURE_ADD_VISITING_HERO, structureInstanceID); //add to visitors
 	else if(garrisonHero == h)
-		cb->setObjProperty(id, ObjProperty::STRUCTURE_ADD_GARRISONED_HERO, structureInstanceID); //then it must be garrisoned hero
+		cb->setObjPropertyValue(id, ObjProperty::STRUCTURE_ADD_GARRISONED_HERO, structureInstanceID); //then it must be garrisoned hero
 	else
 	{
 		//should never ever happen

+ 1 - 1
lib/mapObjects/CGTownInstance.h

@@ -211,7 +211,7 @@ public:
 		return defendingHero && garrisonHero && defendingHero != garrisonHero;
 	}
 protected:
-	void setPropertyDer(ui8 what, ui32 val) override;
+	void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
 	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
 

+ 14 - 15
lib/mapObjects/CQuest.cpp

@@ -544,18 +544,18 @@ std::vector<Component> CGSeerHut::getPopupComponents(const CGHeroInstance * hero
 	return result;
 }
 
-void CGSeerHut::setPropertyDer(ui8 what, ui32 val)
+void CGSeerHut::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
 {
 	switch(what)
 	{
-		case CGSeerHut::SEERHUT_VISITED:
+		case ObjProperty::SEERHUT_VISITED:
 		{
-			quest->activeForPlayers.emplace(val);
+			quest->activeForPlayers.emplace(identifier.as<PlayerColor>());
 			break;
 		}
-		case CGSeerHut::SEERHUT_COMPLETE:
+		case ObjProperty::SEERHUT_COMPLETE:
 		{
-			quest->isCompleted = val;
+			quest->isCompleted = identifier.getNum();
 			quest->activeForPlayers.clear();
 			break;
 		}
@@ -567,7 +567,7 @@ void CGSeerHut::newTurn(CRandomGenerator & rand) const
 	CRewardableObject::newTurn(rand);
 	if(quest->lastDay >= 0 && quest->lastDay <= cb->getDate() - 1) //time is up
 	{
-		cb->setObjProperty (id, CGSeerHut::SEERHUT_COMPLETE, true);
+		cb->setObjPropertyValue(id, ObjProperty::SEERHUT_COMPLETE, true);
 	}
 }
 
@@ -582,7 +582,7 @@ void CGSeerHut::onHeroVisit(const CGHeroInstance * h) const
 
 		if(firstVisit)
 		{
-			cb->setObjProperty(id, CGSeerHut::SEERHUT_VISITED, h->getOwner());
+			cb->setObjPropertyID(id, ObjProperty::SEERHUT_VISITED, h->getOwner());
 
 			AddQuest aq;
 			aq.quest = QuestInfo (quest, this, visitablePos());
@@ -665,7 +665,7 @@ void CGSeerHut::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
 	if(answer)
 	{
 		quest->completeQuest(cb, hero);
-		cb->setObjProperty(id, CGSeerHut::SEERHUT_COMPLETE, !quest->repeatedQuest); //mission complete
+		cb->setObjPropertyValue(id, ObjProperty::SEERHUT_COMPLETE, !quest->repeatedQuest); //mission complete
 	}
 }
 
@@ -764,7 +764,7 @@ void CGQuestGuard::onHeroVisit(const CGHeroInstance * h) const
 	if(!quest->isCompleted)
 		CGSeerHut::onHeroVisit(h);
 	else
-		cb->setObjProperty(id, CGSeerHut::SEERHUT_COMPLETE, false);
+		cb->setObjPropertyValue(id, ObjProperty::SEERHUT_COMPLETE, false);
 }
 
 bool CGQuestGuard::passableFor(PlayerColor color) const
@@ -783,15 +783,14 @@ void CGKeys::reset()
 	playerKeyMap.clear();
 }
 
-void CGKeys::setPropertyDer (ui8 what, ui32 val) //101-108 - enable key for player 1-8
+void CGKeys::setPropertyDer (ObjProperty what, ObjPropertyID identifier)
 {
-	if (what >= 101 && what <= (100 + PlayerColor::PLAYER_LIMIT_I))
+	if (what == ObjProperty::KEYMASTER_VISITED)
 	{
-		PlayerColor player(what-101);
-		playerKeyMap[player].insert(static_cast<ui8>(val));
+		playerKeyMap[identifier.as<PlayerColor>()].insert(subID);
 	}
 	else
-		logGlobal->error("Unexpected properties requested to set: what=%d, val=%d", static_cast<int>(what), val);
+		logGlobal->error("Unexpected properties requested to set: what=%d, val=%d", static_cast<int>(what), identifier.getNum());
 }
 
 bool CGKeys::wasMyColorVisited(const PlayerColor & player) const
@@ -819,7 +818,7 @@ void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const
 	int txt_id;
 	if (!wasMyColorVisited (h->getOwner()) )
 	{
-		cb->setObjProperty(id, h->tempOwner.getNum()+101, subID);
+		cb->setObjPropertyID(id, ObjProperty::KEYMASTER_VISITED, h->tempOwner);
 		txt_id=19;
 	}
 	else

+ 2 - 5
lib/mapObjects/CQuest.h

@@ -144,10 +144,7 @@ public:
 		h & seerName;
 	}
 protected:
-	static constexpr int SEERHUT_VISITED = 10;
-	static constexpr int SEERHUT_COMPLETE = 11;
-
-	void setPropertyDer(ui8 what, ui32 val) override;
+	void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
 
 	void serializeJsonOptions(JsonSerializeFormat & handler) override;
 };
@@ -186,7 +183,7 @@ public:
 		h & static_cast<CGObjectInstance&>(*this);
 	}
 protected:
-	void setPropertyDer(ui8 what, ui32 val) override;
+	void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
 };
 
 class DLL_LINKAGE CGKeymasterTent : public CGKeys

+ 7 - 7
lib/mapObjects/CRewardableObject.cpp

@@ -169,7 +169,7 @@ void CRewardableObject::blockingDialogAnswered(const CGHeroInstance *hero, ui32
 
 void CRewardableObject::markAsVisited(const CGHeroInstance * hero) const
 {
-	cb->setObjProperty(id, ObjProperty::REWARD_CLEARED, true);
+	cb->setObjPropertyValue(id, ObjProperty::REWARD_CLEARED, true);
 
 	ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD, id, hero->id);
 	cb->sendAndApply(&cov);
@@ -177,7 +177,7 @@ void CRewardableObject::markAsVisited(const CGHeroInstance * hero) const
 
 void CRewardableObject::grantReward(ui32 rewardID, const CGHeroInstance * hero) const
 {
-	cb->setObjProperty(id, ObjProperty::REWARD_SELECT, rewardID);
+	cb->setObjPropertyValue(id, ObjProperty::REWARD_SELECT, rewardID);
 	grantRewardBeforeLevelup(cb, configuration.info.at(rewardID), hero);
 	
 	// hero is not blocked by levelup dialog - grant remainer immediately
@@ -338,7 +338,7 @@ std::vector<Component> CRewardableObject::getPopupComponents(const CGHeroInstanc
 	return getPopupComponentsImpl(hero->getOwner(), hero);
 }
 
-void CRewardableObject::setPropertyDer(ui8 what, ui32 val)
+void CRewardableObject::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
 {
 	switch (what)
 	{
@@ -346,10 +346,10 @@ void CRewardableObject::setPropertyDer(ui8 what, ui32 val)
 			initObj(cb->gameState()->getRandomGenerator());
 			break;
 		case ObjProperty::REWARD_SELECT:
-			selectedReward = val;
+			selectedReward = identifier.getNum();
 			break;
 		case ObjProperty::REWARD_CLEARED:
-			onceVisitableObjectCleared = val;
+			onceVisitableObjectCleared = identifier.getNum();
 			break;
 	}
 }
@@ -360,11 +360,11 @@ void CRewardableObject::newTurn(CRandomGenerator & rand) const
 	{
 		if (configuration.resetParameters.rewards)
 		{
-			cb->setObjProperty(id, ObjProperty::REWARD_RANDOMIZE, 0);
+			cb->setObjPropertyValue(id, ObjProperty::REWARD_RANDOMIZE, 0);
 		}
 		if (configuration.resetParameters.visitors)
 		{
-			cb->setObjProperty(id, ObjProperty::REWARD_CLEARED, false);
+			cb->setObjPropertyValue(id, ObjProperty::REWARD_CLEARED, false);
 			ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_CLEAR, id);
 			cb->sendAndApply(&cov);
 		}

+ 1 - 1
lib/mapObjects/CRewardableObject.h

@@ -65,7 +65,7 @@ public:
 
 	void initObj(CRandomGenerator & rand) override;
 	
-	void setPropertyDer(ui8 what, ui32 val) override;
+	void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
 
 	CRewardableObject();
 	

+ 1 - 1
lib/mapObjects/IObjectInterface.cpp

@@ -49,7 +49,7 @@ void IObjectInterface::initObj(CRandomGenerator & rand)
 void IObjectInterface::pickRandomObject(CRandomGenerator & rand)
 {}
 
-void IObjectInterface::setProperty( ui8 what, ui32 val )
+void IObjectInterface::setProperty(ObjProperty what, ObjPropertyID identifier)
 {}
 
 bool IObjectInterface::wasVisited (PlayerColor player) const

+ 2 - 1
lib/mapObjects/IObjectInterface.h

@@ -10,6 +10,7 @@
 #pragma once
 
 #include "../networkPacks/EInfoWindowMode.h"
+#include "../networkPacks/ObjProperty.h"
 #include "../constants/EntityIdentifiers.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -46,7 +47,7 @@ public:
 	virtual void newTurn(CRandomGenerator & rand) const;
 	virtual void initObj(CRandomGenerator & rand); //synchr
 	virtual void pickRandomObject(CRandomGenerator & rand);
-	virtual void setProperty(ui8 what, ui32 val);//synchr
+	virtual void setProperty(ObjProperty what, ObjPropertyID identifier);//synchr
 
 	//Called when queries created DURING HERO VISIT are resolved
 	//First parameter is always hero that visited object and triggered the query

+ 12 - 12
lib/mapObjects/MiscObjects.cpp

@@ -44,10 +44,10 @@ static std::string visitedTxt(const bool visited)
 	return VLC->generaltexth->allTexts[id];
 }
 
-void CTeamVisited::setPropertyDer(ui8 what, ui32 val)
+void CTeamVisited::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
 {
-	if(what == CTeamVisited::OBJPROP_VISITED)
-		players.insert(PlayerColor(val));
+	if(what == ObjProperty::VISITED)
+		players.insert(identifier.as<PlayerColor>());
 }
 
 bool CTeamVisited::wasVisited(PlayerColor player) const
@@ -1195,13 +1195,13 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
 		cb->sendAndApply(&iw);
 
 		// increment general visited obelisks counter
-		cb->setObjProperty(id, CGObelisk::OBJPROP_INC, team.getNum());
+		cb->setObjPropertyID(id, ObjProperty::OBELISK_VISITED, team);
 		cb->showObjectWindow(this, EOpenWindowMode::PUZZLE_MAP, h, false);
 
 		// mark that particular obelisk as visited for all players in the team
 		for(const auto & color : ts->players)
 		{
-			cb->setObjProperty(id, CGObelisk::OBJPROP_VISITED, color.getNum());
+			cb->setObjPropertyID(id, ObjProperty::VISITED, color);
 		}
 	}
 	else
@@ -1228,14 +1228,14 @@ std::string CGObelisk::getHoverText(PlayerColor player) const
 	return getObjectName() + " " + visitedTxt(wasVisited(player));
 }
 
-void CGObelisk::setPropertyDer( ui8 what, ui32 val )
+void CGObelisk::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
 {
 	switch(what)
 	{
-		case CGObelisk::OBJPROP_INC:
+		case ObjProperty::OBELISK_VISITED:
 			{
-				auto progress = ++visited[TeamID(val)];
-				logGlobal->debug("Player %d: obelisk progress %d / %d", val, static_cast<int>(progress) , static_cast<int>(obeliskCount));
+				auto progress = ++visited[identifier.as<TeamID>()];
+				logGlobal->debug("Player %d: obelisk progress %d / %d", identifier.getNum(), static_cast<int>(progress) , static_cast<int>(obeliskCount));
 
 				if(progress > obeliskCount)
 				{
@@ -1246,7 +1246,7 @@ void CGObelisk::setPropertyDer( ui8 what, ui32 val )
 				break;
 			}
 		default:
-			CTeamVisited::setPropertyDer(what, val);
+			CTeamVisited::setPropertyDer(what, identifier);
 			break;
 	}
 }
@@ -1263,7 +1263,7 @@ void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const
 		if(oldOwner.isValidPlayer()) //remove bonus from old owner
 		{
 			RemoveBonus rb(GiveBonus::ETarget::PLAYER);
-			rb.whoID = oldOwner.getNum();
+			rb.whoID = oldOwner;
 			rb.source = BonusSource::OBJECT_INSTANCE;
 			rb.id = BonusSourceID(id);
 			cb->sendAndApply(&rb);
@@ -1285,7 +1285,7 @@ void CGLighthouse::giveBonusTo(const PlayerColor & player, bool onInit) const
 	GiveBonus gb(GiveBonus::ETarget::PLAYER);
 	gb.bonus.type = BonusType::MOVEMENT;
 	gb.bonus.val = 500;
-	gb.id = player.getNum();
+	gb.id = player;
 	gb.bonus.duration = BonusDuration::PERMANENT;
 	gb.bonus.source = BonusSource::OBJECT_INSTANCE;
 	gb.bonus.sid = BonusSourceID(id);

+ 2 - 5
lib/mapObjects/MiscObjects.h

@@ -29,15 +29,13 @@ public:
 	bool wasVisited (const CGHeroInstance * h) const override;
 	bool wasVisited(PlayerColor player) const override;
 	bool wasVisited(const TeamID & team) const;
-	void setPropertyDer(ui8 what, ui32 val) override;
+	void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CGObjectInstance&>(*this);
 		h & players;
 	}
-
-	static constexpr int OBJPROP_VISITED = 10;
 };
 
 class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles
@@ -361,7 +359,6 @@ class DLL_LINKAGE CGDenOfthieves : public CGObjectInstance
 class DLL_LINKAGE CGObelisk : public CTeamVisited
 {
 public:
-	static constexpr int OBJPROP_INC = 20;
 	static ui8 obeliskCount; //how many obelisks are on map
 	static std::map<TeamID, ui8> visited; //map: team_id => how many obelisks has been visited
 
@@ -375,7 +372,7 @@ public:
 		h & static_cast<CTeamVisited&>(*this);
 	}
 protected:
-	void setPropertyDer(ui8 what, ui32 val) override;
+	void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
 };
 
 class DLL_LINKAGE CGLighthouse : public CGObjectInstance

+ 25 - 19
lib/networkPacks/NetPacksLib.cpp

@@ -971,18 +971,15 @@ void GiveBonus::applyGs(CGameState *gs)
 	CBonusSystemNode *cbsn = nullptr;
 	switch(who)
 	{
-	case ETarget::HERO:
-		cbsn = gs->getHero(ObjectInstanceID(id));
+	case ETarget::OBJECT:
+		cbsn = dynamic_cast<CBonusSystemNode*>(gs->getObjInstance(id.as<ObjectInstanceID>()));
 		break;
 	case ETarget::PLAYER:
-		cbsn = gs->getPlayerState(PlayerColor(id));
-		break;
-	case ETarget::TOWN:
-		cbsn = gs->getTown(ObjectInstanceID(id));
+		cbsn = gs->getPlayerState(id.as<PlayerColor>());
 		break;
 	case ETarget::BATTLE:
 		assert(Bonus::OneBattle(&bonus));
-		cbsn = dynamic_cast<CBonusSystemNode*>(gs->getBattle(BattleID(id)));
+		cbsn = dynamic_cast<CBonusSystemNode*>(gs->getBattle(id.as<BattleID>()));
 		break;
 	}
 
@@ -1114,11 +1111,20 @@ void PlayerReinitInterface::applyGs(CGameState *gs)
 
 void RemoveBonus::applyGs(CGameState *gs)
 {
-	CBonusSystemNode * node = nullptr;
-	if (who == GiveBonus::ETarget::HERO)
-		node = gs->getHero(ObjectInstanceID(whoID));
-	else
-		node = gs->getPlayerState(PlayerColor(whoID));
+	CBonusSystemNode *node = nullptr;
+	switch(who)
+	{
+	case GiveBonus::ETarget::OBJECT:
+		node = dynamic_cast<CBonusSystemNode*>(gs->getObjInstance(whoID.as<ObjectInstanceID>()));
+		break;
+	case GiveBonus::ETarget::PLAYER:
+		node = gs->getPlayerState(whoID.as<PlayerColor>());
+		break;
+	case GiveBonus::ETarget::BATTLE:
+		assert(Bonus::OneBattle(&bonus));
+		node = dynamic_cast<CBonusSystemNode*>(gs->getBattle(whoID.as<BattleID>()));
+		break;
+	}
 
 	BonusList &bonuses = node->getExportedBonusList();
 
@@ -1483,7 +1489,7 @@ void NewObject::applyGs(CGameState *gs)
 	const TerrainTile & t = gs->map->getTile(targetPos);
 	terrainType = t.terType->getId();
 
-	auto handler = VLC->objtypeh->getHandlerFor(ID, subID);
+	auto handler = VLC->objtypeh->getHandlerFor(ID, subID.getNum());
 
 	CGObjectInstance * o = handler->create();
 	handler->configureObject(o, gs->getRandomGenerator());
@@ -1499,13 +1505,13 @@ void NewObject::applyGs(CGameState *gs)
 		cre->character = 2;
 		cre->gainedArtifact = ArtifactID::NONE;
 		cre->identifier = -1;
-		cre->addToSlot(SlotID(0), new CStackInstance(CreatureID(subID), -1)); //add placeholder stack
+		cre->addToSlot(SlotID(0), new CStackInstance(subID.as<CreatureID>(), -1)); //add placeholder stack
 	}
 
 	assert(!handler->getTemplates(terrainType).empty());
 	if (handler->getTemplates().empty())
 	{
-		logGlobal->error("Attempt to create object (%d %d) with no templates!", ID, subID);
+		logGlobal->error("Attempt to create object (%d %d) with no templates!", ID, subID.getNum());
 		return;
 	}
 
@@ -2049,9 +2055,9 @@ void SetObjectProperty::applyGs(CGameState * gs) const
 				if(state->towns.empty())
 					state->daysWithoutCastle = 0;
 			}
-			if(PlayerColor(val).isValidPlayer())
+			if(identifier.as<PlayerColor>().isValidPlayer())
 			{
-				PlayerState * p = gs->getPlayerState(PlayerColor(val));
+				PlayerState * p = gs->getPlayerState(identifier.as<PlayerColor>());
 				p->towns.emplace_back(t);
 
 				//reset counter before NewTurn to avoid no town message if game loaded at turn when one already captured
@@ -2062,12 +2068,12 @@ void SetObjectProperty::applyGs(CGameState * gs) const
 
 		CBonusSystemNode & nodeToMove = cai->whatShouldBeAttached();
 		nodeToMove.detachFrom(cai->whereShouldBeAttached(gs));
-		obj->setProperty(what,val);
+		obj->setProperty(what, identifier);
 		nodeToMove.attachTo(cai->whereShouldBeAttached(gs));
 	}
 	else //not an armed instance
 	{
-		obj->setProperty(what,val);
+		obj->setProperty(what, identifier);
 	}
 }
 

+ 69 - 0
lib/networkPacks/ObjProperty.h

@@ -0,0 +1,69 @@
+/*
+ * ObjProperty.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../constants/VariantIdentifier.h"
+#include "../constants/EntityIdentifiers.h"
+
+enum class ObjProperty : int8_t
+{
+	INVALID,
+	OWNER,
+	BLOCKVIS,
+	PRIMARY_STACK_COUNT,
+	VISITORS,
+	VISITED,
+	ID,
+	AVAILABLE_CREATURE,
+	MONSTER_COUNT,
+	MONSTER_POWER,
+	MONSTER_EXP,
+	MONSTER_RESTORE_TYPE,
+	MONSTER_REFUSED_JOIN,
+
+	//town-specific
+	STRUCTURE_ADD_VISITING_HERO,
+	STRUCTURE_CLEAR_VISITORS,
+	STRUCTURE_ADD_GARRISONED_HERO, //changing buildings state
+	BONUS_VALUE_FIRST,
+	BONUS_VALUE_SECOND, //used in Rampart for special building that generates resources (storing resource type and quantity)
+
+	SEERHUT_VISITED,
+	SEERHUT_COMPLETE,
+	KEYMASTER_VISITED,
+	OBELISK_VISITED,
+
+	//creature-bank specific
+	BANK_DAYCOUNTER,
+	BANK_RESET,
+	BANK_CLEAR,
+
+	//object with reward
+	REWARD_RANDOMIZE,
+	REWARD_SELECT,
+	REWARD_CLEARED
+};
+
+class NumericID : public Identifier<NumericID>
+{
+public:
+	using Identifier<NumericID>::Identifier;
+
+	static si32 decode(const std::string & identifier)
+	{
+		return std::stoi(identifier);
+	}
+	static std::string encode(const si32 index)
+	{
+		return std::to_string(index);
+	}
+};
+
+using ObjPropertyID = VariantIdentifier<NumericID, MapObjectID, ObjectInstanceID, CreatureID, PlayerColor, TeamID>;

+ 20 - 39
lib/networkPacks/PacksForClient.h

@@ -15,6 +15,7 @@
 #include "EOpenWindowMode.h"
 #include "EntityChanges.h"
 #include "NetPacksBase.h"
+#include "ObjProperty.h"
 
 #include "../CCreatureSet.h"
 #include "../MetaString.h"
@@ -366,17 +367,17 @@ struct DLL_LINKAGE SetAvailableHero : public CPackForClient
 
 struct DLL_LINKAGE GiveBonus : public CPackForClient
 {
-	enum class ETarget : ui8 { HERO, PLAYER, TOWN, BATTLE };
+	enum class ETarget : int8_t { OBJECT, PLAYER, BATTLE };
 	
-	explicit GiveBonus(ETarget Who = ETarget::HERO)
+	explicit GiveBonus(ETarget Who = ETarget::OBJECT)
 		:who(Who)
 	{
 	}
 
 	void applyGs(CGameState * gs);
 
-	ETarget who = ETarget::HERO; //who receives bonus
-	si32 id = 0; //hero. town or player id - whoever receives it
+	ETarget who = ETarget::OBJECT;
+	VariantIdentifier<ObjectInstanceID, PlayerColor, BattleID> id;
 	Bonus bonus;
 	MetaString bdescr;
 
@@ -388,7 +389,7 @@ struct DLL_LINKAGE GiveBonus : public CPackForClient
 		h & id;
 		h & bdescr;
 		h & who;
-		assert(id != -1);
+		assert(id.getNum() != -1);
 	}
 };
 
@@ -461,7 +462,7 @@ struct DLL_LINKAGE PlayerReinitInterface : public CPackForClient
 
 struct DLL_LINKAGE RemoveBonus : public CPackForClient
 {
-	explicit RemoveBonus(GiveBonus::ETarget Who = GiveBonus::ETarget::HERO)
+	explicit RemoveBonus(GiveBonus::ETarget Who = GiveBonus::ETarget::OBJECT)
 		:who(Who)
 	{
 	}
@@ -469,7 +470,7 @@ struct DLL_LINKAGE RemoveBonus : public CPackForClient
 	void applyGs(CGameState * gs);
 
 	GiveBonus::ETarget who; //who receives bonus
-	ui32 whoID = 0; //hero, town or player id - whoever loses bonus
+	VariantIdentifier<HeroTypeID, PlayerColor, BattleID, ObjectInstanceID> whoID;
 
 	//vars to identify bonus: its source
 	BonusSource source;
@@ -574,7 +575,7 @@ struct DLL_LINKAGE UpdateCastleEvents : public CPackForClient
 struct DLL_LINKAGE ChangeFormation : public CPackForClient
 {
 	ObjectInstanceID hid;
-	ui8 formation = 0;
+	EArmyFormation formation{};
 
 	void applyGs(CGameState * gs) const;
 	void visitTyped(ICPackVisitor & visitor) override;
@@ -781,9 +782,9 @@ struct DLL_LINKAGE NewObject : public CPackForClient
 	void applyGs(CGameState * gs);
 
 	/// Object ID to create
-	Obj ID;
+	MapObjectID ID;
 	/// Object secondary ID to create
-	ui32 subID = 0;
+	VariantIdentifier<MapObjectSubID, HeroTypeID, CreatureID, BoatId> subID;
 	/// Position of visitable tile of created object
 	int3 targetPos;
 	/// Which player initiated creation of this object
@@ -806,7 +807,8 @@ struct DLL_LINKAGE SetAvailableArtifacts : public CPackForClient
 {
 	void applyGs(CGameState * gs) const;
 
-	si32 id = 0; //two variants: id < 0: set artifact pool for Artifact Merchants in towns; id >= 0: set pool for adv. map Black Market (id is the id of Black Market instance then)
+	//two variants: id < 0: set artifact pool for Artifact Merchants in towns; id >= 0: set pool for adv. map Black Market (id is the id of Black Market instance then)
+	ObjectInstanceID id;
 	std::vector<const CArtifact *> arts;
 
 	void visitTyped(ICPackVisitor & visitor) override;
@@ -1207,38 +1209,16 @@ struct DLL_LINKAGE InfoWindow : public CPackForClient //103  - displays simple i
 	InfoWindow() = default;
 };
 
-namespace ObjProperty
-{
-	enum
-	{
-		OWNER = 1, BLOCKVIS = 2, PRIMARY_STACK_COUNT = 3, VISITORS = 4, VISITED = 5, ID = 6, AVAILABLE_CREATURE = 7, SUBID = 8,
-		MONSTER_COUNT = 10, MONSTER_POWER = 11, MONSTER_EXP = 12, MONSTER_RESTORE_TYPE = 13, MONSTER_REFUSED_JOIN,
-
-		//town-specific
-		STRUCTURE_ADD_VISITING_HERO, STRUCTURE_CLEAR_VISITORS, STRUCTURE_ADD_GARRISONED_HERO,  //changing buildings state
-		BONUS_VALUE_FIRST, BONUS_VALUE_SECOND, //used in Rampart for special building that generates resources (storing resource type and quantity)
-
-		//creature-bank specific
-		BANK_DAYCOUNTER, BANK_RESET, BANK_CLEAR,
-
-		//object with reward
-		REWARD_RANDOMIZE, REWARD_SELECT, REWARD_CLEARED
-	};
-}
-
 struct DLL_LINKAGE SetObjectProperty : public CPackForClient
 {
 	void applyGs(CGameState * gs) const;
 	ObjectInstanceID id;
-	ui8 what = 0; // see ObjProperty enum
-	ui32 val = 0;
+	ObjProperty what{};
+
+	ObjPropertyID identifier;
+	int32_t value = 0;
+
 	SetObjectProperty() = default;
-	SetObjectProperty(const ObjectInstanceID & ID, ui8 What, ui32 Val)
-		: id(ID)
-		, what(What)
-		, val(Val)
-	{
-	}
 
 	void visitTyped(ICPackVisitor & visitor) override;
 
@@ -1246,7 +1226,8 @@ struct DLL_LINKAGE SetObjectProperty : public CPackForClient
 	{
 		h & id;
 		h & what;
-		h & val;
+		h & identifier;
+		h & value;
 	}
 };
 

+ 5 - 3
lib/networkPacks/PacksForServer.h

@@ -11,6 +11,7 @@
 
 #include "ArtifactLocation.h"
 #include "NetPacksBase.h"
+#include "TradeItem.h"
 
 #include "../int3.h"
 #include "../battle/BattleAction.h"
@@ -472,7 +473,8 @@ struct DLL_LINKAGE TradeOnMarketplace : public CPackForServer
 	ObjectInstanceID heroId;
 
 	EMarketMode mode = EMarketMode::RESOURCE_RESOURCE;
-	std::vector<ui32> r1, r2; //mode 0: r1 - sold resource, r2 - bought res (exception: when sacrificing art r1 is art id [todo: make r2 preferred slot?]
+	std::vector<TradeItemSell> r1;
+	std::vector<TradeItemBuy> r2; //mode 0: r1 - sold resource, r2 - bought res (exception: when sacrificing art r1 is art id [todo: make r2 preferred slot?]
 	std::vector<ui32> val; //units of sold resource
 
 	void visitTyped(ICPackVisitor & visitor) override;
@@ -493,13 +495,13 @@ struct DLL_LINKAGE SetFormation : public CPackForServer
 {
 	SetFormation() = default;
 	;
-	SetFormation(const ObjectInstanceID & HID, ui8 Formation)
+	SetFormation(const ObjectInstanceID & HID, EArmyFormation Formation)
 		: hid(HID)
 		, formation(Formation)
 	{
 	}
 	ObjectInstanceID hid;
-	ui8 formation = 0;
+	EArmyFormation formation{};
 
 	void visitTyped(ICPackVisitor & visitor) override;
 

+ 20 - 0
lib/networkPacks/TradeItem.h

@@ -0,0 +1,20 @@
+/*
+ * TradeItem.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../constants/VariantIdentifier.h"
+#include "../constants/EntityIdentifiers.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+using TradeItemSell = VariantIdentifier<GameResID, SlotID, ArtifactPosition, ArtifactInstanceID>;
+using TradeItemBuy = VariantIdentifier<GameResID, PlayerColor, ArtifactID, SecondarySkill>;
+
+VCMI_LIB_NAMESPACE_END

+ 2 - 2
lib/rewardable/Interface.cpp

@@ -150,9 +150,9 @@ void Rewardable::Interface::grantRewardAfterLevelup(IGameCallback * cb, const Re
 	for(const Bonus & bonus : info.reward.bonuses)
 	{
 		GiveBonus gb;
-		gb.who = GiveBonus::ETarget::HERO;
+		gb.who = GiveBonus::ETarget::OBJECT;
 		gb.bonus = bonus;
-		gb.id = hero->id.getNum();
+		gb.id = hero->id;
 		cb->giveHeroBonus(&gb);
 	}
 

+ 2 - 2
lib/spells/AdventureSpellMechanics.cpp

@@ -83,7 +83,7 @@ ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(SpellCastEnviron
 		for(const Bonus & b : bonuses)
 		{
 			GiveBonus gb;
-			gb.id = parameters.caster->getCasterUnitId();
+			gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId());
 			gb.bonus = b;
 			env->apply(&gb);
 		}
@@ -323,7 +323,7 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
 	}
 
 	GiveBonus gb;
-	gb.id = parameters.caster->getCasterUnitId();
+	gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId());
 	gb.bonus = Bonus(BonusDuration::ONE_DAY, BonusType::NONE, BonusSource::SPELL_EFFECT, 0, BonusSourceID(owner->id));
 	env->apply(&gb);
 

+ 1 - 1
lib/spells/effects/Moat.cpp

@@ -117,7 +117,7 @@ void Moat::apply(ServerCallback * server, const Mechanics * m, const EffectTarge
 		for(auto & b : converted)
 		{
 			GiveBonus gb(GiveBonus::ETarget::BATTLE);
-			gb.id = m->battle()->getBattle()->getBattleID().getNum();
+			gb.id = m->battle()->getBattle()->getBattleID();
 			gb.bonus = b;
 			server->apply(&gb);
 		}

+ 1 - 1
mapeditor/inspector/armywidget.cpp

@@ -101,7 +101,7 @@ bool ArmyWidget::commitChanges()
 		}
 	}
 	
-	army.setFormation(ui->formationTight->isChecked());
+	army.setFormation(ui->formationTight->isChecked() ? EArmyFormation::TIGHT : EArmyFormation::LOOSE );
 	return isArmed;
 }
 

+ 28 - 19
server/CGameHandler.cpp

@@ -886,7 +886,7 @@ void CGameHandler::onNewTurn()
 	if (newMonth)
 	{
 		SetAvailableArtifacts saa;
-		saa.id = -1;
+		saa.id = ObjectInstanceID::NONE;
 		pickAllowedArtsSet(saa.arts, getRandomGenerator());
 		sendAndApply(&saa);
 	}
@@ -1328,8 +1328,8 @@ bool CGameHandler::teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui
 void CGameHandler::setOwner(const CGObjectInstance * obj, const PlayerColor owner)
 {
 	PlayerColor oldOwner = getOwner(obj->id);
-	SetObjectProperty sop(obj->id, ObjProperty::OWNER, owner.getNum());
-	sendAndApply(&sop);
+
+	setObjPropertyID(obj->id, ObjProperty::OWNER, owner);
 
 	std::set<PlayerColor> playerColors = {owner, oldOwner};
 	checkVictoryLossConditions(playerColors);
@@ -2973,12 +2973,12 @@ bool CGameHandler::buyArtifact(const IMarket *m, const CGHeroInstance *h, GameRe
 	SetAvailableArtifacts saa;
 	if(dynamic_cast<const CGTownInstance *>(m))
 	{
-		saa.id = -1;
+		saa.id = ObjectInstanceID::NONE;
 		saa.arts = CGTownInstance::merchantArtifacts;
 	}
 	else if(const CGBlackMarket *bm = dynamic_cast<const CGBlackMarket *>(m)) //black market
 	{
-		saa.id = bm->id.getNum();
+		saa.id = bm->id;
 		saa.arts = bm->artifacts;
 	}
 	else
@@ -3044,23 +3044,23 @@ bool CGameHandler::buySecSkill(const IMarket *m, const CGHeroInstance *h, Second
 	return true;
 }
 
-bool CGameHandler::tradeResources(const IMarket *market, ui32 val, PlayerColor player, ui32 id1, ui32 id2)
+bool CGameHandler::tradeResources(const IMarket *market, ui32 amountToSell, PlayerColor player, GameResID toSell, GameResID toBuy)
 {
-	TResourceCap r1 = getPlayerState(player)->resources[id1];
+	TResourceCap haveToSell = getPlayerState(player)->resources[toSell];
 
-	vstd::amin(val, r1); //can't trade more resources than have
+	vstd::amin(amountToSell, haveToSell); //can't trade more resources than have
 
 	int b1, b2; //base quantities for trade
-	market->getOffer(id1, id2, b1, b2, EMarketMode::RESOURCE_RESOURCE);
-	int units = val / b1; //how many base quantities we trade
+	market->getOffer(toSell, toBuy, b1, b2, EMarketMode::RESOURCE_RESOURCE);
+	int amountToBoy = amountToSell / b1; //how many base quantities we trade
 
-	if (val%b1) //all offered units of resource should be used, if not -> somewhere in calculations must be an error
+	if (amountToSell % b1 != 0) //all offered units of resource should be used, if not -> somewhere in calculations must be an error
 	{
 		COMPLAIN_RET("Invalid deal, not all offered units of resource were used.");
 	}
 
-	giveResource(player, GameResID(id1), - b1 * units);
-	giveResource(player, GameResID(id2), b2 * units);
+	giveResource(player, toSell, -b1 * amountToBoy);
+	giveResource(player, toBuy, b2 * amountToBoy);
 
 	return true;
 }
@@ -3144,7 +3144,7 @@ bool CGameHandler::sendResources(ui32 val, PlayerColor player, GameResID r1, Pla
 	return true;
 }
 
-bool CGameHandler::setFormation(ObjectInstanceID hid, ui8 formation)
+bool CGameHandler::setFormation(ObjectInstanceID hid, EArmyFormation formation)
 {
 	const CGHeroInstance *h = getHero(hid);
 	if (!h)
@@ -4047,8 +4047,8 @@ void CGameHandler::spawnWanderingMonsters(CreatureID creatureID)
 			createObject(*tile, PlayerColor::NEUTRAL, Obj::MONSTER, creatureID);
 			auto monsterId = getTopObj(*tile)->id;
 
-			setObjProperty(monsterId, ObjProperty::MONSTER_COUNT, count);
-			setObjProperty(monsterId, ObjProperty::MONSTER_POWER, (si64)1000*count);
+			setObjPropertyValue(monsterId, ObjProperty::MONSTER_COUNT, count);
+			setObjPropertyValue(monsterId, ObjProperty::MONSTER_POWER, (si64)1000*count);
 		}
 		tiles.erase(tile); //not use it again
 	}
@@ -4170,12 +4170,21 @@ bool CGameHandler::isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, con
 	return true;
 }
 
-void CGameHandler::setObjProperty(ObjectInstanceID objid, int prop, si64 val)
+void CGameHandler::setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value)
+{
+	SetObjectProperty sob;
+	sob.id = objid;
+	sob.what = prop;
+	sob.identifier = NumericID(value);
+	sendAndApply(&sob);
+}
+
+void CGameHandler::setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier)
 {
 	SetObjectProperty sob;
 	sob.id = objid;
 	sob.what = prop;
-	sob.val = static_cast<ui32>(val);
+	sob.identifier = identifier;
 	sendAndApply(&sob);
 }
 
@@ -4209,7 +4218,7 @@ scripting::Pool * CGameHandler::getGlobalContextPool() const
 //}
 #endif
 
-void CGameHandler::createObject(const int3 & visitablePosition, const PlayerColor & initiator, Obj type, int32_t subtype)
+void CGameHandler::createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype)
 {
 	NewObject no;
 	no.ID = type;

+ 5 - 4
server/CGameHandler.h

@@ -101,7 +101,7 @@ public:
 	//do sth
 	void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> &spells) override;
 	bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override;
-	void createObject(const int3 & visitablePosition, const PlayerColor & initiator, Obj type, int32_t subtype ) override;
+	void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) override;
 	void setOwner(const CGObjectInstance * obj, PlayerColor owner) override;
 	void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false) override;
 	void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) override;
@@ -156,7 +156,8 @@ public:
 	/// Returns hero that is currently visiting this object, or nullptr if no visit is active
 	const CGHeroInstance * getVisitingHero(const CGObjectInstance *obj);
 	bool isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero) override;
-	void setObjProperty(ObjectInstanceID objid, int prop, si64 val) override;
+	void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value) override;
+	void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override;
 	void showInfoDialog(InfoWindow * iw) override;
 	void showInfoDialog(const std::string & msg, PlayerColor player) override;
 
@@ -182,8 +183,8 @@ public:
 
 	bool queryReply( QueryID qid, std::optional<int32_t> reply, PlayerColor player );
 	bool buildBoat( ObjectInstanceID objid, PlayerColor player );
-	bool setFormation( ObjectInstanceID hid, ui8 formation );
-	bool tradeResources(const IMarket *market, ui32 val, PlayerColor player, ui32 id1, ui32 id2);
+	bool setFormation( ObjectInstanceID hid, EArmyFormation formation );
+	bool tradeResources(const IMarket *market, ui32 amountToSell, PlayerColor player, GameResID toSell, GameResID toBuy);
 	bool sacrificeCreatures(const IMarket * market, const CGHeroInstance * hero, const std::vector<SlotID> & slot, const std::vector<ui32> & count);
 	bool sendResources(ui32 val, PlayerColor player, GameResID r1, PlayerColor r2);
 	bool sellCreatures(ui32 count, const IMarket *market, const CGHeroInstance * hero, SlotID slot, GameResID resourceID);

+ 16 - 9
server/NetPacksServer.cpp

@@ -221,42 +221,49 @@ void ApplyGhNetPackVisitor::visitTradeOnMarketplace(TradeOnMarketplace & pack)
 	{
 	case EMarketMode::RESOURCE_RESOURCE:
 		for(int i = 0; i < pack.r1.size(); ++i)
-			result &= gh.tradeResources(market, pack.val[i], pack.player, pack.r1[i], pack.r2[i]);
+			result &= gh.tradeResources(market, pack.val[i], pack.player, pack.r1[i].as<GameResID>(), pack.r2[i].as<GameResID>());
 		break;
 	case EMarketMode::RESOURCE_PLAYER:
 		for(int i = 0; i < pack.r1.size(); ++i)
-			result &= gh.sendResources(pack.val[i], pack.player, GameResID(pack.r1[i]), PlayerColor(pack.r2[i]));
+			result &= gh.sendResources(pack.val[i], pack.player, pack.r1[i].as<GameResID>(), pack.r2[i].as<PlayerColor>());
 		break;
 	case EMarketMode::CREATURE_RESOURCE:
 		for(int i = 0; i < pack.r1.size(); ++i)
-			result &= gh.sellCreatures(pack.val[i], market, hero, SlotID(pack.r1[i]), GameResID(pack.r2[i]));
+			result &= gh.sellCreatures(pack.val[i], market, hero, pack.r1[i].as<SlotID>(), pack.r2[i].as<GameResID>());
 		break;
 	case EMarketMode::RESOURCE_ARTIFACT:
 		for(int i = 0; i < pack.r1.size(); ++i)
-			result &= gh.buyArtifact(market, hero, GameResID(pack.r1[i]), ArtifactID(pack.r2[i]));
+			result &= gh.buyArtifact(market, hero, pack.r1[i].as<GameResID>(), pack.r2[i].as<ArtifactID>());
 		break;
 	case EMarketMode::ARTIFACT_RESOURCE:
 		for(int i = 0; i < pack.r1.size(); ++i)
-			result &= gh.sellArtifact(market, hero, ArtifactInstanceID(pack.r1[i]), GameResID(pack.r2[i]));
+			result &= gh.sellArtifact(market, hero, pack.r1[i].as<ArtifactInstanceID>(), pack.r2[i].as<GameResID>());
 		break;
 	case EMarketMode::CREATURE_UNDEAD:
 		for(int i = 0; i < pack.r1.size(); ++i)
-			result &= gh.transformInUndead(market, hero, SlotID(pack.r1[i]));
+			result &= gh.transformInUndead(market, hero, pack.r1[i].as<SlotID>());
 		break;
 	case EMarketMode::RESOURCE_SKILL:
 		for(int i = 0; i < pack.r2.size(); ++i)
-			result &= gh.buySecSkill(market, hero, SecondarySkill(pack.r2[i]));
+			result &= gh.buySecSkill(market, hero, pack.r2[i].as<SecondarySkill>());
 		break;
 	case EMarketMode::CREATURE_EXP:
 	{
-		std::vector<SlotID> slotIDs(pack.r1.begin(), pack.r1.end());
+		std::vector<SlotID> slotIDs;
 		std::vector<ui32> count(pack.val.begin(), pack.val.end());
+
+		for(auto const & slot : pack.r1)
+			slotIDs.push_back(slot.as<SlotID>());
+
 		result = gh.sacrificeCreatures(market, hero, slotIDs, count);
 		return;
 	}
 	case EMarketMode::ARTIFACT_EXP:
 	{
-		std::vector<ArtifactPosition> positions(pack.r1.begin(), pack.r1.end());
+		std::vector<ArtifactPosition> positions;
+		for(auto const & slot : pack.r1)
+			positions.push_back(slot.as<ArtifactPosition>());
+
 		result = gh.sacrificeArtifact(market, hero, positions);
 		return;
 	}

+ 2 - 2
server/battles/BattleProcessor.cpp

@@ -122,8 +122,8 @@ void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArm
 	{
 		for(auto bonus : attackerInfo->battleBonuses)
 		{
-			GiveBonus giveBonus(GiveBonus::ETarget::HERO);
-			giveBonus.id = hero1->id.getNum();
+			GiveBonus giveBonus(GiveBonus::ETarget::OBJECT);
+			giveBonus.id = hero1->id;
 			giveBonus.bonus = bonus;
 			gameHandler->sendAndApply(&giveBonus);
 		}

+ 4 - 4
server/processors/PlayerMessageProcessor.cpp

@@ -139,8 +139,8 @@ void PlayerMessageProcessor::cheatGiveSpells(PlayerColor player, const CGHeroIns
 		gameHandler->giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
 
 	///Give all spells with bonus (to allow banned spells)
-	GiveBonus giveBonus(GiveBonus::ETarget::HERO);
-	giveBonus.id = hero->id.getNum();
+	GiveBonus giveBonus(GiveBonus::ETarget::OBJECT);
+	giveBonus.id = hero->id;
 	giveBonus.bonus = Bonus(BonusDuration::PERMANENT, BonusType::SPELLS_OF_LEVEL, BonusSource::OTHER, 0, BonusSourceID());
 	//start with level 0 to skip abilities
 	for (int level = 1; level <= GameConstants::SPELL_LEVELS; level++)
@@ -300,11 +300,11 @@ void PlayerMessageProcessor::cheatMovement(PlayerColor player, const CGHeroInsta
 
 	gameHandler->sendAndApply(&smp);
 
-	GiveBonus gb(GiveBonus::ETarget::HERO);
+	GiveBonus gb(GiveBonus::ETarget::OBJECT);
 	gb.bonus.type = BonusType::FREE_SHIP_BOARDING;
 	gb.bonus.duration = BonusDuration::ONE_DAY;
 	gb.bonus.source = BonusSource::OTHER;
-	gb.id = hero->id.getNum();
+	gb.id = hero->id;
 	gameHandler->giveHeroBonus(&gb);
 }