Browse Source

Added initiator-player to packs that add/remove/move objects

Ivan Savenko 2 years ago
parent
commit
8c0d78f1d9

+ 1 - 1
AI/Nullkiller/AIGateway.cpp

@@ -349,7 +349,7 @@ void AIGateway::newObject(const CGObjectInstance * obj)
 
 
 //to prevent AI from accessing objects that got deleted while they became invisible (Cover of Darkness, enemy hero moved etc.) below code allows AI to know deletion of objects out of sight
 //to prevent AI from accessing objects that got deleted while they became invisible (Cover of Darkness, enemy hero moved etc.) below code allows AI to know deletion of objects out of sight
 //see: RemoveObject::applyFirstCl, to keep AI "not cheating" do not use advantage of this and use this function just to prevent crashes
 //see: RemoveObject::applyFirstCl, to keep AI "not cheating" do not use advantage of this and use this function just to prevent crashes
-void AIGateway::objectRemoved(const CGObjectInstance * obj)
+void AIGateway::objectRemoved(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	LOG_TRACE(logAi);
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
 	NET_EVENT_HANDLER;

+ 1 - 1
AI/Nullkiller/AIGateway.h

@@ -156,7 +156,7 @@ public:
 	void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
 	void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
 	void requestRealized(PackageApplied * pa) override;
 	void requestRealized(PackageApplied * pa) override;
 	void receivedResource() override;
 	void receivedResource() override;
-	void objectRemoved(const CGObjectInstance * obj) override;
+	void objectRemoved(const CGObjectInstance * obj, const PlayerColor & initiator) override;
 	void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor) override;
 	void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor) override;
 	void heroManaPointsChanged(const CGHeroInstance * hero) override;
 	void heroManaPointsChanged(const CGHeroInstance * hero) override;
 	void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
 	void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;

+ 1 - 1
AI/VCAI/VCAI.cpp

@@ -385,7 +385,7 @@ void VCAI::newObject(const CGObjectInstance * obj)
 
 
 //to prevent AI from accessing objects that got deleted while they became invisible (Cover of Darkness, enemy hero moved etc.) below code allows AI to know deletion of objects out of sight
 //to prevent AI from accessing objects that got deleted while they became invisible (Cover of Darkness, enemy hero moved etc.) below code allows AI to know deletion of objects out of sight
 //see: RemoveObject::applyFirstCl, to keep AI "not cheating" do not use advantage of this and use this function just to prevent crashes
 //see: RemoveObject::applyFirstCl, to keep AI "not cheating" do not use advantage of this and use this function just to prevent crashes
-void VCAI::objectRemoved(const CGObjectInstance * obj)
+void VCAI::objectRemoved(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	LOG_TRACE(logAi);
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
 	NET_EVENT_HANDLER;

+ 1 - 1
AI/VCAI/VCAI.h

@@ -189,7 +189,7 @@ public:
 	void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
 	void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
 	void requestRealized(PackageApplied * pa) override;
 	void requestRealized(PackageApplied * pa) override;
 	void receivedResource() override;
 	void receivedResource() override;
-	void objectRemoved(const CGObjectInstance * obj) override;
+	void objectRemoved(const CGObjectInstance * obj, const PlayerColor & initiator) override;
 	void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor) override;
 	void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor) override;
 	void heroManaPointsChanged(const CGHeroInstance * hero) override;
 	void heroManaPointsChanged(const CGHeroInstance * hero) override;
 	void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
 	void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;

+ 2 - 2
client/CPlayerInterface.cpp

@@ -1391,10 +1391,10 @@ void CPlayerInterface::centerView (int3 pos, int focusTime)
 	CCS->curh->show();
 	CCS->curh->show();
 }
 }
 
 
-void CPlayerInterface::objectRemoved(const CGObjectInstance * obj)
+void CPlayerInterface::objectRemoved(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	if(LOCPLINT->cb->isPlayerMakingTurn(playerID) && obj->getRemovalSound())
+	if(playerID == initiator && obj->getRemovalSound())
 	{
 	{
 		waitWhileDialog();
 		waitWhileDialog();
 		CCS->soundh->playSound(obj->getRemovalSound().value());
 		CCS->soundh->playSound(obj->getRemovalSound().value());

+ 1 - 1
client/CPlayerInterface.h

@@ -140,7 +140,7 @@ protected: // Call-ins from server, should not be called directly, but only via
 	void centerView (int3 pos, int focusTime) override;
 	void centerView (int3 pos, int focusTime) override;
 	void beforeObjectPropertyChanged(const SetObjectProperty * sop) override;
 	void beforeObjectPropertyChanged(const SetObjectProperty * sop) override;
 	void objectPropertyChanged(const SetObjectProperty * sop) override;
 	void objectPropertyChanged(const SetObjectProperty * sop) override;
-	void objectRemoved(const CGObjectInstance *obj) override;
+	void objectRemoved(const CGObjectInstance *obj, const PlayerColor & initiator) override;
 	void objectRemovedAfter() override;
 	void objectRemovedAfter() override;
 	void playerBlocked(int reason, bool start) override;
 	void playerBlocked(int reason, bool start) override;
 	void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
 	void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;

+ 3 - 3
client/Client.h

@@ -161,8 +161,8 @@ public:
 	friend class CBattleCallback; //handling players actions
 	friend class CBattleCallback; //handling players actions
 
 
 	void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> & spells) override {};
 	void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> & spells) override {};
-	bool removeObject(const CGObjectInstance * obj) override {return false;};
-	void createObject(const int3 & visitablePosition, Obj type, int32_t subtype ) 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 setOwner(const CGObjectInstance * obj, PlayerColor owner) override {};
 	void setOwner(const CGObjectInstance * obj, PlayerColor owner) override {};
 	void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs = false) 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 {};
 	void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs = false) override {};
@@ -204,7 +204,7 @@ public:
 	void setMovePoints(SetMovePoints * smp) override {};
 	void setMovePoints(SetMovePoints * smp) override {};
 	void setManaPoints(ObjectInstanceID hid, int val) override {};
 	void setManaPoints(ObjectInstanceID hid, int val) override {};
 	void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) override {};
 	void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) override {};
-	void changeObjPos(ObjectInstanceID objid, int3 newPos) override {};
+	void changeObjPos(ObjectInstanceID objid, int3 newPos, const PlayerColor & initiator) override {};
 	void sendAndApply(CPackForClient * pack) override {};
 	void sendAndApply(CPackForClient * pack) override {};
 	void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override {};
 	void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override {};
 	void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override {};
 	void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override {};

+ 24 - 17
client/NetPacksClient.cpp

@@ -368,15 +368,16 @@ void ApplyFirstClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack)
 {
 {
 	CGObjectInstance *obj = gs.getObjInstance(pack.objid);
 	CGObjectInstance *obj = gs.getObjInstance(pack.objid);
 	if(CGI->mh)
 	if(CGI->mh)
-		CGI->mh->onObjectFadeOut(obj);
+		CGI->mh->onObjectFadeOut(obj, pack.initiator);
 
 
 	CGI->mh->waitForOngoingAnimations();
 	CGI->mh->waitForOngoingAnimations();
 }
 }
+
 void ApplyClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack)
 void ApplyClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack)
 {
 {
 	CGObjectInstance *obj = gs.getObjInstance(pack.objid);
 	CGObjectInstance *obj = gs.getObjInstance(pack.objid);
 	if(CGI->mh)
 	if(CGI->mh)
-		CGI->mh->onObjectFadeIn(obj);
+		CGI->mh->onObjectFadeIn(obj, pack.initiator);
 
 
 	CGI->mh->waitForOngoingAnimations();
 	CGI->mh->waitForOngoingAnimations();
 	cl.invalidatePaths();
 	cl.invalidatePaths();
@@ -447,10 +448,10 @@ void ApplyClientNetPackVisitor::visitRemoveBonus(RemoveBonus & pack)
 
 
 void ApplyFirstClientNetPackVisitor::visitRemoveObject(RemoveObject & pack)
 void ApplyFirstClientNetPackVisitor::visitRemoveObject(RemoveObject & pack)
 {
 {
-	const CGObjectInstance *o = cl.getObj(pack.id);
+	const CGObjectInstance *o = cl.getObj(pack.objectID);
 
 
 	if(CGI->mh)
 	if(CGI->mh)
-		CGI->mh->onObjectFadeOut(o);
+		CGI->mh->onObjectFadeOut(o, pack.initiator);
 
 
 	//notify interfaces about removal
 	//notify interfaces about removal
 	for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++)
 	for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++)
@@ -458,7 +459,7 @@ void ApplyFirstClientNetPackVisitor::visitRemoveObject(RemoveObject & pack)
 		//below line contains little cheat for AI so it will be aware of deletion of enemy heroes that moved or got re-covered by FoW
 		//below line contains little cheat for AI so it will be aware of deletion of enemy heroes that moved or got re-covered by FoW
 		//TODO: loose requirements as next AI related crashes appear, for example another pack.player collects object that got re-covered by FoW, unsure if AI code workarounds this
 		//TODO: loose requirements as next AI related crashes appear, for example another pack.player collects object that got re-covered by FoW, unsure if AI code workarounds this
 		if(gs.isVisible(o, i->first) || (!cl.getPlayerState(i->first)->human && o->ID == Obj::HERO && o->tempOwner != i->first))
 		if(gs.isVisible(o, i->first) || (!cl.getPlayerState(i->first)->human && o->ID == Obj::HERO && o->tempOwner != i->first))
-			i->second->objectRemoved(o);
+			i->second->objectRemoved(o, pack.initiator);
 	}
 	}
 
 
 	CGI->mh->waitForOngoingAnimations();
 	CGI->mh->waitForOngoingAnimations();
@@ -543,12 +544,12 @@ void ApplyClientNetPackVisitor::visitNewStructures(NewStructures & pack)
 	CGTownInstance *town = gs.getTown(pack.tid);
 	CGTownInstance *town = gs.getTown(pack.tid);
 	for(const auto & id : pack.bid)
 	for(const auto & id : pack.bid)
 	{
 	{
-		callInterfaceIfPresent(cl, town->tempOwner, &IGameEventsReceiver::buildChanged, town, id, 1);
+		callInterfaceIfPresent(cl, town->getOwner(), &IGameEventsReceiver::buildChanged, town, id, 1);
 	}
 	}
 
 
 	// invalidate section of map view with our object and force an update
 	// invalidate section of map view with our object and force an update
-	CGI->mh->onObjectInstantRemove(town);
-	CGI->mh->onObjectInstantAdd(town);
+	CGI->mh->onObjectInstantRemove(town, town->getOwner());
+	CGI->mh->onObjectInstantAdd(town, town->getOwner());
 
 
 }
 }
 void ApplyClientNetPackVisitor::visitRazeStructures(RazeStructures & pack)
 void ApplyClientNetPackVisitor::visitRazeStructures(RazeStructures & pack)
@@ -556,12 +557,12 @@ void ApplyClientNetPackVisitor::visitRazeStructures(RazeStructures & pack)
 	CGTownInstance * town = gs.getTown(pack.tid);
 	CGTownInstance * town = gs.getTown(pack.tid);
 	for(const auto & id : pack.bid)
 	for(const auto & id : pack.bid)
 	{
 	{
-		callInterfaceIfPresent(cl, town->tempOwner, &IGameEventsReceiver::buildChanged, town, id, 2);
+		callInterfaceIfPresent(cl, town->getOwner(), &IGameEventsReceiver::buildChanged, town, id, 2);
 	}
 	}
 
 
 	// invalidate section of map view with our object and force an update
 	// invalidate section of map view with our object and force an update
-	CGI->mh->onObjectInstantRemove(town);
-	CGI->mh->onObjectInstantAdd(town);
+	CGI->mh->onObjectInstantRemove(town, town->getOwner());
+	CGI->mh->onObjectInstantAdd(town, town->getOwner());
 }
 }
 
 
 void ApplyClientNetPackVisitor::visitSetAvailableCreatures(SetAvailableCreatures & pack)
 void ApplyClientNetPackVisitor::visitSetAvailableCreatures(SetAvailableCreatures & pack)
@@ -609,17 +610,17 @@ void ApplyClientNetPackVisitor::visitHeroRecruited(HeroRecruited & pack)
 	if(callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroCreated, h))
 	if(callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroCreated, h))
 	{
 	{
 		if(const CGTownInstance *t = gs.getTown(pack.tid))
 		if(const CGTownInstance *t = gs.getTown(pack.tid))
-			callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroInGarrisonChange, t);
+			callInterfaceIfPresent(cl, h->getOwner(), &IGameEventsReceiver::heroInGarrisonChange, t);
 	}
 	}
 	if(CGI->mh)
 	if(CGI->mh)
-		CGI->mh->onObjectInstantAdd(h);
+		CGI->mh->onObjectInstantAdd(h, h->getOwner());
 }
 }
 
 
 void ApplyClientNetPackVisitor::visitGiveHero(GiveHero & pack)
 void ApplyClientNetPackVisitor::visitGiveHero(GiveHero & pack)
 {
 {
 	CGHeroInstance *h = gs.getHero(pack.id);
 	CGHeroInstance *h = gs.getHero(pack.id);
 	if(CGI->mh)
 	if(CGI->mh)
-		CGI->mh->onObjectInstantAdd(h);
+		CGI->mh->onObjectInstantAdd(h, h->getOwner());
 	callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroCreated, h);
 	callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroCreated, h);
 }
 }
 
 
@@ -646,7 +647,10 @@ void ApplyFirstClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty &
 
 
 	// invalidate section of map view with our object and force an update with new flag color
 	// invalidate section of map view with our object and force an update with new flag color
 	if (pack.what == ObjProperty::OWNER)
 	if (pack.what == ObjProperty::OWNER)
-		CGI->mh->onObjectInstantRemove(gs.getObjInstance(pack.id));
+	{
+		auto object = gs.getObjInstance(pack.id);
+		CGI->mh->onObjectInstantRemove(object, object->getOwner());
+	}
 }
 }
 
 
 void ApplyClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty & pack)
 void ApplyClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty & pack)
@@ -660,7 +664,10 @@ void ApplyClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty & pack)
 
 
 	// invalidate section of map view with our object and force an update with new flag color
 	// invalidate section of map view with our object and force an update with new flag color
 	if (pack.what == ObjProperty::OWNER)
 	if (pack.what == ObjProperty::OWNER)
-		CGI->mh->onObjectInstantAdd(gs.getObjInstance(pack.id));
+	{
+		auto object = gs.getObjInstance(pack.id);
+		CGI->mh->onObjectInstantAdd(object, object->getOwner());
+	}
 }
 }
 
 
 void ApplyClientNetPackVisitor::visitHeroLevelUp(HeroLevelUp & pack)
 void ApplyClientNetPackVisitor::visitHeroLevelUp(HeroLevelUp & pack)
@@ -996,7 +1003,7 @@ void ApplyClientNetPackVisitor::visitNewObject(NewObject & pack)
 
 
 	const CGObjectInstance *obj = cl.getObj(pack.createdObjectID);
 	const CGObjectInstance *obj = cl.getObj(pack.createdObjectID);
 	if(CGI->mh)
 	if(CGI->mh)
-		CGI->mh->onObjectFadeIn(obj);
+		CGI->mh->onObjectFadeIn(obj, pack.initiator);
 
 
 	for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++)
 	for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++)
 	{
 	{

+ 4 - 4
client/adventureMap/MapAudioPlayer.cpp

@@ -50,22 +50,22 @@ void MapAudioPlayer::onAfterHeroDisembark(const CGHeroInstance * obj, const int3
 		update();
 		update();
 }
 }
 
 
-void MapAudioPlayer::onObjectFadeIn(const CGObjectInstance * obj)
+void MapAudioPlayer::onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	addObject(obj);
 	addObject(obj);
 }
 }
 
 
-void MapAudioPlayer::onObjectFadeOut(const CGObjectInstance * obj)
+void MapAudioPlayer::onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	removeObject(obj);
 	removeObject(obj);
 }
 }
 
 
-void MapAudioPlayer::onObjectInstantAdd(const CGObjectInstance * obj)
+void MapAudioPlayer::onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	addObject(obj);
 	addObject(obj);
 }
 }
 
 
-void MapAudioPlayer::onObjectInstantRemove(const CGObjectInstance * obj)
+void MapAudioPlayer::onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	removeObject(obj);
 	removeObject(obj);
 }
 }

+ 5 - 4
client/adventureMap/MapAudioPlayer.h

@@ -15,6 +15,7 @@
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 class ObjectInstanceID;
 class ObjectInstanceID;
 class CArmedInstance;
 class CArmedInstance;
+class PlayerColor;
 VCMI_LIB_NAMESPACE_END
 VCMI_LIB_NAMESPACE_END
 
 
 class MapAudioPlayer : public IMapObjectObserver
 class MapAudioPlayer : public IMapObjectObserver
@@ -38,10 +39,10 @@ class MapAudioPlayer : public IMapObjectObserver
 protected:
 protected:
 	// IMapObjectObserver impl
 	// IMapObjectObserver impl
 	bool hasOngoingAnimations() override;
 	bool hasOngoingAnimations() override;
-	void onObjectFadeIn(const CGObjectInstance * obj) override;
-	void onObjectFadeOut(const CGObjectInstance * obj) override;
-	void onObjectInstantAdd(const CGObjectInstance * obj) override;
-	void onObjectInstantRemove(const CGObjectInstance * obj) override;
+	void onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator) override;
+	void onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator) override;
+	void onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator) override;
+	void onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator) override;
 
 
 	void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
 	void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
 	void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
 	void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;

+ 5 - 4
client/mapView/IMapRendererObserver.h

@@ -14,6 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 class int3;
 class int3;
 class CGObjectInstance;
 class CGObjectInstance;
 class CGHeroInstance;
 class CGHeroInstance;
+class PlayerColor;
 
 
 VCMI_LIB_NAMESPACE_END
 VCMI_LIB_NAMESPACE_END
 
 
@@ -26,16 +27,16 @@ public:
 	virtual bool hasOngoingAnimations() = 0;
 	virtual bool hasOngoingAnimations() = 0;
 
 
 	/// Plays fade-in animation and adds object to map
 	/// Plays fade-in animation and adds object to map
-	virtual void onObjectFadeIn(const CGObjectInstance * obj) = 0;
+	virtual void onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
 
 
 	/// Plays fade-out animation and removed object from map
 	/// Plays fade-out animation and removed object from map
-	virtual void onObjectFadeOut(const CGObjectInstance * obj) = 0;
+	virtual void onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
 
 
 	/// Adds object to map instantly, with no animation
 	/// Adds object to map instantly, with no animation
-	virtual void onObjectInstantAdd(const CGObjectInstance * obj) = 0;
+	virtual void onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
 
 
 	/// Removes object from map instantly, with no animation
 	/// Removes object from map instantly, with no animation
-	virtual void onObjectInstantRemove(const CGObjectInstance * obj) = 0;
+	virtual void onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
 
 
 	/// Perform hero movement animation, moving hero across terrain
 	/// Perform hero movement animation, moving hero across terrain
 	virtual void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
 	virtual void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;

+ 24 - 16
client/mapView/MapViewController.cpp

@@ -168,7 +168,7 @@ void MapViewController::tick(uint32_t timeDelta)
 		if(!hero)
 		if(!hero)
 			hero = boat->hero;
 			hero = boat->hero;
 
 
-		double heroMoveTime = LOCPLINT->makingTurn ?
+		double heroMoveTime = LOCPLINT->playerID == hero->getOwner() ?
 			settings["adventure"]["heroMoveTime"].Float() :
 			settings["adventure"]["heroMoveTime"].Float() :
 			settings["adventure"]["enemyMoveTime"].Float();
 			settings["adventure"]["enemyMoveTime"].Float();
 
 
@@ -267,31 +267,35 @@ void MapViewController::afterRender()
 	}
 	}
 }
 }
 
 
-bool MapViewController::isEventInstant(const CGObjectInstance * obj)
+bool MapViewController::isEventInstant(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
-	if (!isEventVisible(obj))
+	if (!isEventVisible(obj, initiator))
 		return true;
 		return true;
 
 
-	if(!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() <= 0)
+	if(initiator != LOCPLINT->playerID && settings["adventure"]["enemyMoveTime"].Float() <= 0)
 		return true; // instant movement speed
 		return true; // instant movement speed
 
 
-	if(LOCPLINT->makingTurn && settings["adventure"]["heroMoveTime"].Float() <= 0)
+	if(initiator == LOCPLINT->playerID && settings["adventure"]["heroMoveTime"].Float() <= 0)
 		return true; // instant movement speed
 		return true; // instant movement speed
 
 
 	return false;
 	return false;
 }
 }
 
 
-bool MapViewController::isEventVisible(const CGObjectInstance * obj)
+bool MapViewController::isEventVisible(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	if(adventureContext == nullptr)
 	if(adventureContext == nullptr)
 		return false;
 		return false;
 
 
-	if(!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() < 0)
+	if(initiator != LOCPLINT->playerID && settings["adventure"]["enemyMoveTime"].Float() < 0)
 		return false; // enemy move speed set to "hidden/none"
 		return false; // enemy move speed set to "hidden/none"
 
 
 	if(!GH.windows().isTopWindow(adventureInt))
 	if(!GH.windows().isTopWindow(adventureInt))
 		return false;
 		return false;
 
 
+	// do not focus on actions of other players during our turn (e.g. simturns)
+	if (LOCPLINT->makingTurn && initiator != LOCPLINT->playerID)
+		return false;
+
 	if(obj->isVisitable())
 	if(obj->isVisitable())
 		return context->isVisible(obj->visitablePos());
 		return context->isVisible(obj->visitablePos());
 	else
 	else
@@ -303,12 +307,16 @@ bool MapViewController::isEventVisible(const CGHeroInstance * obj, const int3 &
 	if(adventureContext == nullptr)
 	if(adventureContext == nullptr)
 		return false;
 		return false;
 
 
-	if(!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() < 0)
+	if(obj->getOwner() != LOCPLINT->playerID && settings["adventure"]["enemyMoveTime"].Float() < 0)
 		return false; // enemy move speed set to "hidden/none"
 		return false; // enemy move speed set to "hidden/none"
 
 
 	if(!GH.windows().isTopWindow(adventureInt))
 	if(!GH.windows().isTopWindow(adventureInt))
 		return false;
 		return false;
 
 
+	// do not focus on actions of other players during our turn (e.g. simturns)
+	if (LOCPLINT->makingTurn && obj->getOwner() != LOCPLINT->playerID)
+		return false;
+
 	if(context->isVisible(obj->convertToVisitablePos(from)))
 	if(context->isVisible(obj->convertToVisitablePos(from)))
 		return true;
 		return true;
 
 
@@ -394,7 +402,7 @@ void MapViewController::onBeforeHeroEmbark(const CGHeroInstance * obj, const int
 {
 {
 	if(isEventVisible(obj, from, dest))
 	if(isEventVisible(obj, from, dest))
 	{
 	{
-		if (!isEventInstant(obj))
+		if (!isEventInstant(obj, obj->getOwner()))
 			fadeOutObject(obj);
 			fadeOutObject(obj);
 		setViewCenter(obj->getSightCenter());
 		setViewCenter(obj->getSightCenter());
 	}
 	}
@@ -418,39 +426,39 @@ void MapViewController::onAfterHeroDisembark(const CGHeroInstance * obj, const i
 {
 {
 	if(isEventVisible(obj, from, dest))
 	if(isEventVisible(obj, from, dest))
 	{
 	{
-		if (!isEventInstant(obj))
+		if (!isEventInstant(obj, obj->getOwner()))
 			fadeInObject(obj);
 			fadeInObject(obj);
 		setViewCenter(obj->getSightCenter());
 		setViewCenter(obj->getSightCenter());
 	}
 	}
 	addObject(obj);
 	addObject(obj);
 }
 }
 
 
-void MapViewController::onObjectFadeIn(const CGObjectInstance * obj)
+void MapViewController::onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	assert(!hasOngoingAnimations());
 	assert(!hasOngoingAnimations());
 
 
-	if(isEventVisible(obj) && !isEventInstant(obj) )
+	if(isEventVisible(obj, initiator) && !isEventInstant(obj, initiator) )
 		fadeInObject(obj);
 		fadeInObject(obj);
 
 
 	addObject(obj);
 	addObject(obj);
 }
 }
 
 
-void MapViewController::onObjectFadeOut(const CGObjectInstance * obj)
+void MapViewController::onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	assert(!hasOngoingAnimations());
 	assert(!hasOngoingAnimations());
 
 
-	if(isEventVisible(obj) && !isEventInstant(obj) )
+	if(isEventVisible(obj, initiator) && !isEventInstant(obj, initiator) )
 		fadeOutObject(obj);
 		fadeOutObject(obj);
 	else
 	else
 		removeObject(obj);
 		removeObject(obj);
 }
 }
 
 
-void MapViewController::onObjectInstantAdd(const CGObjectInstance * obj)
+void MapViewController::onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	addObject(obj);
 	addObject(obj);
 };
 };
 
 
-void MapViewController::onObjectInstantRemove(const CGObjectInstance * obj)
+void MapViewController::onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	removeObject(obj);
 	removeObject(obj);
 };
 };

+ 7 - 6
client/mapView/MapViewController.h

@@ -14,6 +14,7 @@
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 struct ObjectPosInfo;
 struct ObjectPosInfo;
+class PlayerColor;
 VCMI_LIB_NAMESPACE_END
 VCMI_LIB_NAMESPACE_END
 
 
 struct MapRendererContextState;
 struct MapRendererContextState;
@@ -55,8 +56,8 @@ private:
 	Point targetTileSize = Point(32, 32);
 	Point targetTileSize = Point(32, 32);
 	bool wasInDeadZone = true;
 	bool wasInDeadZone = true;
 
 
-	bool isEventInstant(const CGObjectInstance * obj);
-	bool isEventVisible(const CGObjectInstance * obj);
+	bool isEventInstant(const CGObjectInstance * obj, const PlayerColor & initiator);
+	bool isEventVisible(const CGObjectInstance * obj, const PlayerColor & initiator);
 	bool isEventVisible(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
 	bool isEventVisible(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
 
 
 	void fadeOutObject(const CGObjectInstance * obj);
 	void fadeOutObject(const CGObjectInstance * obj);
@@ -67,10 +68,10 @@ private:
 
 
 	// IMapObjectObserver impl
 	// IMapObjectObserver impl
 	bool hasOngoingAnimations() override;
 	bool hasOngoingAnimations() override;
-	void onObjectFadeIn(const CGObjectInstance * obj) override;
-	void onObjectFadeOut(const CGObjectInstance * obj) override;
-	void onObjectInstantAdd(const CGObjectInstance * obj) override;
-	void onObjectInstantRemove(const CGObjectInstance * obj) override;
+	void onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator) override;
+	void onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator) override;
+	void onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator) override;
+	void onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator) override;
 	void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
 	void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
 	void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
 	void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
 	void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
 	void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;

+ 8 - 8
client/mapView/mapHandler.cpp

@@ -144,16 +144,16 @@ bool CMapHandler::isInMap(const int3 & tile)
 	return map->isInTheMap(tile);
 	return map->isInTheMap(tile);
 }
 }
 
 
-void CMapHandler::onObjectFadeIn(const CGObjectInstance * obj)
+void CMapHandler::onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	for(auto * observer : observers)
 	for(auto * observer : observers)
-		observer->onObjectFadeIn(obj);
+		observer->onObjectFadeIn(obj, initiator);
 }
 }
 
 
-void CMapHandler::onObjectFadeOut(const CGObjectInstance * obj)
+void CMapHandler::onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	for(auto * observer : observers)
 	for(auto * observer : observers)
-		observer->onObjectFadeOut(obj);
+		observer->onObjectFadeOut(obj, initiator);
 }
 }
 
 
 void CMapHandler::onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
 void CMapHandler::onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
@@ -180,16 +180,16 @@ void CMapHandler::onAfterHeroDisembark(const CGHeroInstance * obj, const int3 &
 		observer->onAfterHeroDisembark(obj, from, dest);
 		observer->onAfterHeroDisembark(obj, from, dest);
 }
 }
 
 
-void CMapHandler::onObjectInstantAdd(const CGObjectInstance * obj)
+void CMapHandler::onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	for(auto * observer : observers)
 	for(auto * observer : observers)
-		observer->onObjectInstantAdd(obj);
+		observer->onObjectInstantAdd(obj, initiator);
 }
 }
 
 
-void CMapHandler::onObjectInstantRemove(const CGObjectInstance * obj)
+void CMapHandler::onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	for(auto * observer : observers)
 	for(auto * observer : observers)
-		observer->onObjectInstantRemove(obj);
+		observer->onObjectInstantRemove(obj, initiator);
 }
 }
 
 
 void CMapHandler::onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
 void CMapHandler::onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest)

+ 4 - 4
client/mapView/mapHandler.h

@@ -47,10 +47,10 @@ public:
 	bool isInMap(const int3 & tile);
 	bool isInMap(const int3 & tile);
 
 
 	/// see MapObjectObserver interface
 	/// see MapObjectObserver interface
-	void onObjectFadeIn(const CGObjectInstance * obj);
-	void onObjectFadeOut(const CGObjectInstance * obj);
-	void onObjectInstantAdd(const CGObjectInstance * obj);
-	void onObjectInstantRemove(const CGObjectInstance * obj);
+	void onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator);
+	void onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator);
+	void onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator);
+	void onObjectInstantRemove(const CGObjectInstance * obj, const PlayerColor & initiator);
 	void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
 	void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
 	void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
 	void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
 	void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
 	void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest);

+ 3 - 3
lib/IGameCallback.h

@@ -89,8 +89,8 @@ public:
 	virtual void showInfoDialog(const std::string & msg, PlayerColor player) = 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 void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> &spells)=0;
-	virtual bool removeObject(const CGObjectInstance * obj)=0;
-	virtual void createObject(const int3 & visitablePosition, Obj type, int32_t subtype = 0) = 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 setOwner(const CGObjectInstance * objid, PlayerColor owner)=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 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;
 	virtual void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0;
@@ -132,7 +132,7 @@ public:
 	virtual void setMovePoints(SetMovePoints * smp)=0;
 	virtual void setMovePoints(SetMovePoints * smp)=0;
 	virtual void setManaPoints(ObjectInstanceID hid, int val)=0;
 	virtual void setManaPoints(ObjectInstanceID hid, int val)=0;
 	virtual void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) = 0;
 	virtual void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) = 0;
-	virtual void changeObjPos(ObjectInstanceID objid, int3 newPos)=0;
+	virtual void changeObjPos(ObjectInstanceID objid, int3 newPos, const PlayerColor & initiator)=0;
 	virtual void sendAndApply(CPackForClient * pack) = 0;
 	virtual void sendAndApply(CPackForClient * pack) = 0;
 	virtual void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)=0; //when two heroes meet on adventure map
 	virtual void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)=0; //when two heroes meet on adventure map
 	virtual void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) = 0;
 	virtual void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) = 0;

+ 1 - 1
lib/IGameEventsReceiver.h

@@ -129,7 +129,7 @@ public:
 	virtual void requestRealized(PackageApplied *pa){};
 	virtual void requestRealized(PackageApplied *pa){};
 	virtual void beforeObjectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged
 	virtual void beforeObjectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged
 	virtual void objectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged
 	virtual void objectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged
-	virtual void objectRemoved(const CGObjectInstance *obj){}; //eg. collected resource, picked artifact, beaten hero
+	virtual void objectRemoved(const CGObjectInstance *obj, const PlayerColor & initiator){}; //eg. collected resource, picked artifact, beaten hero
 	virtual void objectRemovedAfter(){}; //eg. collected resource, picked artifact, beaten hero
 	virtual void objectRemovedAfter(){}; //eg. collected resource, picked artifact, beaten hero
 	virtual void playerBlocked(int reason, bool start){}; //reason: 0 - upcoming battle
 	virtual void playerBlocked(int reason, bool start){}; //reason: 0 - upcoming battle
 	virtual void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) {}; //player lost or won the game
 	virtual void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) {}; //player lost or won the game

+ 16 - 4
lib/NetPacks.h

@@ -424,6 +424,8 @@ struct DLL_LINKAGE ChangeObjPos : public CPackForClient
 	ObjectInstanceID objid;
 	ObjectInstanceID objid;
 	/// New position of visitable tile of an object
 	/// New position of visitable tile of an object
 	int3 nPos;
 	int3 nPos;
+	/// Player that initiated this action, if any
+	PlayerColor initiator;
 
 
 	virtual void visitTyped(ICPackVisitor & visitor) override;
 	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 
@@ -431,6 +433,7 @@ struct DLL_LINKAGE ChangeObjPos : public CPackForClient
 	{
 	{
 		h & objid;
 		h & objid;
 		h & nPos;
 		h & nPos;
+		h & initiator;
 	}
 	}
 };
 };
 
 
@@ -613,19 +616,25 @@ struct DLL_LINKAGE ChangeFormation : public CPackForClient
 struct DLL_LINKAGE RemoveObject : public CPackForClient
 struct DLL_LINKAGE RemoveObject : public CPackForClient
 {
 {
 	RemoveObject() = default;
 	RemoveObject() = default;
-	RemoveObject(const ObjectInstanceID & ID)
-		: id(ID)
+	RemoveObject(const ObjectInstanceID & objectID, const PlayerColor & initiator)
+		: objectID(objectID)
+		, initiator(initiator)
 	{
 	{
 	}
 	}
 
 
 	void applyGs(CGameState * gs);
 	void applyGs(CGameState * gs);
 	virtual void visitTyped(ICPackVisitor & visitor) override;
 	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 
-	ObjectInstanceID id;
+	/// ID of removed object
+	ObjectInstanceID objectID;
+
+	/// Player that initiated this action, if any
+	PlayerColor initiator;
 
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{
 	{
-		h & id;
+		h & objectID;
+		h & initiator;
 	}
 	}
 };
 };
 
 
@@ -803,6 +812,8 @@ struct DLL_LINKAGE NewObject : public CPackForClient
 	ui32 subID = 0;
 	ui32 subID = 0;
 	/// Position of visitable tile of created object
 	/// Position of visitable tile of created object
 	int3 targetPos;
 	int3 targetPos;
+	/// Which player initiated creation of this object
+	PlayerColor initiator;
 
 
 	ObjectInstanceID createdObjectID; //used locally, filled during applyGs
 	ObjectInstanceID createdObjectID; //used locally, filled during applyGs
 
 
@@ -813,6 +824,7 @@ struct DLL_LINKAGE NewObject : public CPackForClient
 		h & ID;
 		h & ID;
 		h & subID;
 		h & subID;
 		h & targetPos;
 		h & targetPos;
+		h & initiator;
 	}
 	}
 };
 };
 
 

+ 4 - 4
lib/NetPacksLib.cpp

@@ -1118,8 +1118,8 @@ void RemoveBonus::applyGs(CGameState *gs)
 void RemoveObject::applyGs(CGameState *gs)
 void RemoveObject::applyGs(CGameState *gs)
 {
 {
 
 
-	CGObjectInstance *obj = gs->getObjInstance(id);
-	logGlobal->debug("removing object id=%d; address=%x; name=%s", id, (intptr_t)obj, obj->getObjectName());
+	CGObjectInstance *obj = gs->getObjInstance(objectID);
+	logGlobal->debug("removing object id=%d; address=%x; name=%s", objectID, (intptr_t)obj, obj->getObjectName());
 	//unblock tiles
 	//unblock tiles
 	gs->map->removeBlockVisTiles(obj);
 	gs->map->removeBlockVisTiles(obj);
 
 
@@ -1159,7 +1159,7 @@ void RemoveObject::applyGs(CGameState *gs)
 		//return hero to the pool, so he may reappear in tavern
 		//return hero to the pool, so he may reappear in tavern
 
 
 		gs->heroesPool->addHeroToPool(beatenHero);
 		gs->heroesPool->addHeroToPool(beatenHero);
-		gs->map->objects[id.getNum()] = nullptr;
+		gs->map->objects[objectID.getNum()] = nullptr;
 
 
 		//If hero on Boat is removed, the Boat disappears
 		//If hero on Boat is removed, the Boat disappears
 		if(beatenHero->boat)
 		if(beatenHero->boat)
@@ -1210,7 +1210,7 @@ void RemoveObject::applyGs(CGameState *gs)
 		event.trigger = event.trigger.morph(patcher);
 		event.trigger = event.trigger.morph(patcher);
 	}
 	}
 	gs->map->instanceNames.erase(obj->instanceName);
 	gs->map->instanceNames.erase(obj->instanceName);
-	gs->map->objects[id.getNum()].dellNull();
+	gs->map->objects[objectID.getNum()].dellNull();
 	gs->map->calculateGuardingGreaturePositions();
 	gs->map->calculateGuardingGreaturePositions();
 }
 }
 
 

+ 3 - 3
lib/mapObjects/CGCreature.cpp

@@ -299,7 +299,7 @@ void CGCreature::fleeDecision(const CGHeroInstance *h, ui32 pursue) const
 	}
 	}
 	else
 	else
 	{
 	{
-		cb->removeObject(this);
+		cb->removeObject(this, h->getOwner());
 	}
 	}
 }
 }
 
 
@@ -400,12 +400,12 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
 	if(result.winner == 0)
 	if(result.winner == 0)
 	{
 	{
 		giveReward(hero);
 		giveReward(hero);
-		cb->removeObject(this);
+		cb->removeObject(this, hero->getOwner());
 	}
 	}
 	else if(result.winner > 1) // draw
 	else if(result.winner > 1) // draw
 	{
 	{
 		// guarded reward is lost forever on draw
 		// guarded reward is lost forever on draw
-		cb->removeObject(this);
+		cb->removeObject(this, hero->getOwner());
 	}
 	}
 	else
 	else
 	{
 	{

+ 1 - 1
lib/mapObjects/CGPandoraBox.cpp

@@ -196,7 +196,7 @@ void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answe
 		else if(getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT).empty())
 		else if(getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT).empty())
 		{
 		{
 			hero->showInfoDialog(15);
 			hero->showInfoDialog(15);
-			cb->removeObject(this);
+			cb->removeObject(this, hero->getOwner());
 		}
 		}
 		else //if it gives something without battle
 		else //if it gives something without battle
 		{
 		{

+ 1 - 1
lib/mapObjects/CQuest.cpp

@@ -980,7 +980,7 @@ void CGBorderGuard::onHeroVisit(const CGHeroInstance * h) const
 void CGBorderGuard::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
 void CGBorderGuard::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
 {
 {
 	if (answer)
 	if (answer)
-		cb->removeObject(this);
+		cb->removeObject(this, hero->getOwner());
 }
 }
 
 
 void CGBorderGuard::afterAddToMap(CMap * map)
 void CGBorderGuard::afterAddToMap(CMap * map)

+ 4 - 4
lib/mapObjects/MiscObjects.cpp

@@ -299,7 +299,7 @@ void CGResource::collectRes(const PlayerColor & player) const
 	sii.components.emplace_back(Component::EComponentType::RESOURCE,subID,amount,0);
 	sii.components.emplace_back(Component::EComponentType::RESOURCE,subID,amount,0);
 	sii.soundID = soundBase::pickup01 + CRandomGenerator::getDefault().nextInt(6);
 	sii.soundID = soundBase::pickup01 + CRandomGenerator::getDefault().nextInt(6);
 	cb->showInfoDialog(&sii);
 	cb->showInfoDialog(&sii);
-	cb->removeObject(this);
+	cb->removeObject(this, player);
 }
 }
 
 
 void CGResource::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
 void CGResource::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
@@ -797,7 +797,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
 void CGArtifact::pick(const CGHeroInstance * h) const
 void CGArtifact::pick(const CGHeroInstance * h) const
 {
 {
 	if(cb->giveHeroArtifact(h, storedArtifact, ArtifactPosition::FIRST_AVAILABLE))
 	if(cb->giveHeroArtifact(h, storedArtifact, ArtifactPosition::FIRST_AVAILABLE))
-		cb->removeObject(this);
+		cb->removeObject(this, h->getOwner());
 }
 }
 
 
 BattleField CGArtifact::getBattlefield() const
 BattleField CGArtifact::getBattlefield() const
@@ -1063,7 +1063,7 @@ void CGSignBottle::onHeroVisit( const CGHeroInstance * h ) const
 	cb->showInfoDialog(&iw);
 	cb->showInfoDialog(&iw);
 
 
 	if(ID == Obj::OCEAN_BOTTLE)
 	if(ID == Obj::OCEAN_BOTTLE)
-		cb->removeObject(this);
+		cb->removeObject(this, h->getOwner());
 }
 }
 
 
 void CGSignBottle::serializeJsonOptions(JsonSerializeFormat& handler)
 void CGSignBottle::serializeJsonOptions(JsonSerializeFormat& handler)
@@ -1113,7 +1113,7 @@ void CGScholar::onHeroVisit( const CGHeroInstance * h ) const
 	}
 	}
 
 
 	cb->showInfoDialog(&iw);
 	cb->showInfoDialog(&iw);
-	cb->removeObject(this);
+	cb->removeObject(this, h->getOwner());
 }
 }
 
 
 void CGScholar::initObj(CRandomGenerator & rand)
 void CGScholar::initObj(CRandomGenerator & rand)

+ 4 - 1
lib/spells/AdventureSpellMechanics.cpp

@@ -202,6 +202,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
 		ChangeObjPos cop;
 		ChangeObjPos cop;
 		cop.objid = nearest->id;
 		cop.objid = nearest->id;
 		cop.nPos = summonPos;
 		cop.nPos = summonPos;
+		cop.initiator = parameters.caster->getCasterOwner();
 		env->apply(&cop);
 		env->apply(&cop);
 	}
 	}
 	else if(schoolLevel < 2) //none or basic level -> cannot create boat :(
 	else if(schoolLevel < 2) //none or basic level -> cannot create boat :(
@@ -217,6 +218,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
 		no.ID = Obj::BOAT;
 		no.ID = Obj::BOAT;
 		no.subID = BoatId::NECROPOLIS;
 		no.subID = BoatId::NECROPOLIS;
 		no.targetPos = summonPos;
 		no.targetPos = summonPos;
+		no.initiator = parameters.caster->getCasterOwner();
 		env->apply(&no);
 		env->apply(&no);
 	}
 	}
 	return ESpellCastResult::OK;
 	return ESpellCastResult::OK;
@@ -257,7 +259,8 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironmen
 	}
 	}
 
 
 	RemoveObject ro;
 	RemoveObject ro;
-	ro.id = t->visitableObjects.back()->id;
+	ro.initiator = parameters.caster->getCasterOwner();
+	ro.objectID = t->visitableObjects.back()->id;
 	env->apply(&ro);
 	env->apply(&ro);
 	return ESpellCastResult::OK;
 	return ESpellCastResult::OK;
 }
 }

+ 15 - 11
server/CGameHandler.cpp

@@ -1045,7 +1045,7 @@ void CGameHandler::giveSpells(const CGTownInstance *t, const CGHeroInstance *h)
 		sendAndApply(&cs);
 		sendAndApply(&cs);
 }
 }
 
 
-bool CGameHandler::removeObject(const CGObjectInstance * obj)
+bool CGameHandler::removeObject(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
 {
 	if (!obj || !getObj(obj->id))
 	if (!obj || !getObj(obj->id))
 	{
 	{
@@ -1054,7 +1054,8 @@ bool CGameHandler::removeObject(const CGObjectInstance * obj)
 	}
 	}
 
 
 	RemoveObject ro;
 	RemoveObject ro;
-	ro.id = obj->id;
+	ro.objectID = obj->id;
+	ro.initiator = initiator;
 	sendAndApply(&ro);
 	sendAndApply(&ro);
 
 
 	checkVictoryLossConditionsForAll(); //eg if monster escaped (removing objs after battle is done dircetly by endBattle, not this function)
 	checkVictoryLossConditionsForAll(); //eg if monster escaped (removing objs after battle is done dircetly by endBattle, not this function)
@@ -1110,8 +1111,9 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
 
 
 	const bool standAtObstacle = t.blocked && !t.visitable;
 	const bool standAtObstacle = t.blocked && !t.visitable;
 	const bool standAtWater = !h->boat && t.terType->isWater() && (t.visitableObjects.empty() || !t.visitableObjects.back()->isCoastVisitable());
 	const bool standAtWater = !h->boat && t.terType->isWater() && (t.visitableObjects.empty() || !t.visitableObjects.back()->isCoastVisitable());
-	
-	auto const complainRet = [&](const std::string & message){
+
+	const auto complainRet = [&](const std::string & message)
+	{
 		//send info about movement failure
 		//send info about movement failure
 		complain(message);
 		complain(message);
 		sendAndApply(&tmh);
 		sendAndApply(&tmh);
@@ -1504,11 +1506,12 @@ void CGameHandler::giveHero(ObjectInstanceID id, PlayerColor player, ObjectInsta
 	changeFogOfWar(h->pos, h->getSightRadius(), player, false);
 	changeFogOfWar(h->pos, h->getSightRadius(), player, false);
 }
 }
 
 
-void CGameHandler::changeObjPos(ObjectInstanceID objid, int3 newPos)
+void CGameHandler::changeObjPos(ObjectInstanceID objid, int3 newPos, const PlayerColor & initiator)
 {
 {
 	ChangeObjPos cop;
 	ChangeObjPos cop;
 	cop.objid = objid;
 	cop.objid = objid;
 	cop.nPos = newPos;
 	cop.nPos = newPos;
+	cop.initiator = initiator;
 	sendAndApply(&cop);
 	sendAndApply(&cop);
 }
 }
 
 
@@ -3462,7 +3465,7 @@ bool CGameHandler::buildBoat(ObjectInstanceID objid, PlayerColor playerID)
 	}
 	}
 
 
 	giveResources(playerID, -boatCost);
 	giveResources(playerID, -boatCost);
-	createObject(tile, Obj::BOAT, obj->getBoatType().getNum());
+	createObject(tile, playerID, Obj::BOAT, obj->getBoatType().getNum());
 	return true;
 	return true;
 }
 }
 
 
@@ -3538,7 +3541,7 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 			for (auto h : hlp) //eliminate heroes
 			for (auto h : hlp) //eliminate heroes
 			{
 			{
 				if (h.get())
 				if (h.get())
-					removeObject(h);
+					removeObject(h, player);
 			}
 			}
 
 
 			//player lost -> all his objects become unflagged (neutral)
 			//player lost -> all his objects become unflagged (neutral)
@@ -3587,7 +3590,7 @@ bool CGameHandler::dig(const CGHeroInstance *h)
 	if (h->diggingStatus() != EDiggingStatus::CAN_DIG) //checks for terrain and movement
 	if (h->diggingStatus() != EDiggingStatus::CAN_DIG) //checks for terrain and movement
 		COMPLAIN_RETF("Hero cannot dig (error code %d)!", static_cast<int>(h->diggingStatus()));
 		COMPLAIN_RETF("Hero cannot dig (error code %d)!", static_cast<int>(h->diggingStatus()));
 
 
-	createObject(h->visitablePos(), Obj::HOLE, 0 );
+	createObject(h->visitablePos(), h->getOwner(), Obj::HOLE, 0 );
 
 
 	//take MPs
 	//take MPs
 	SetMovePoints smp;
 	SetMovePoints smp;
@@ -3981,7 +3984,7 @@ void CGameHandler::spawnWanderingMonsters(CreatureID creatureID)
 		{
 		{
 			auto count = cre->getRandomAmount(std::rand);
 			auto count = cre->getRandomAmount(std::rand);
 
 
-			createObject(*tile, Obj::MONSTER, creatureID);
+			createObject(*tile, PlayerColor::NEUTRAL, Obj::MONSTER, creatureID);
 			auto monsterId = getTopObj(*tile)->id;
 			auto monsterId = getTopObj(*tile)->id;
 
 
 			setObjProperty(monsterId, ObjProperty::MONSTER_COUNT, count);
 			setObjProperty(monsterId, ObjProperty::MONSTER_COUNT, count);
@@ -4123,11 +4126,12 @@ scripting::Pool * CGameHandler::getGlobalContextPool() const
 //}
 //}
 #endif
 #endif
 
 
-void CGameHandler::createObject(const int3 & visitablePosition, Obj type, int32_t subtype)
+void CGameHandler::createObject(const int3 & visitablePosition, const PlayerColor & initiator, Obj type, int32_t subtype)
 {
 {
 	NewObject no;
 	NewObject no;
 	no.ID = type;
 	no.ID = type;
-	no.subID= subtype;
+	no.subID = subtype;
+	no.initiator = initiator;
 	no.targetPos = visitablePosition;
 	no.targetPos = visitablePosition;
 	sendAndApply(&no);
 	sendAndApply(&no);
 }
 }

+ 3 - 3
server/CGameHandler.h

@@ -100,8 +100,8 @@ public:
 	//from IGameCallback
 	//from IGameCallback
 	//do sth
 	//do sth
 	void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> &spells) override;
 	void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> &spells) override;
-	bool removeObject(const CGObjectInstance * obj) override;
-	void createObject(const int3 & visitablePosition, Obj type, int32_t subtype ) 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 setOwner(const CGObjectInstance * obj, PlayerColor owner) override;
 	void setOwner(const CGObjectInstance * obj, PlayerColor owner) override;
 	void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false) 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;
 	void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) override;
@@ -145,7 +145,7 @@ public:
 	void setMovePoints(SetMovePoints * smp) override;
 	void setMovePoints(SetMovePoints * smp) override;
 	void setManaPoints(ObjectInstanceID hid, int val) override;
 	void setManaPoints(ObjectInstanceID hid, int val) override;
 	void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) override;
 	void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) override;
-	void changeObjPos(ObjectInstanceID objid, int3 newPos) override;
+	void changeObjPos(ObjectInstanceID objid, int3 newPos, const PlayerColor & initiator) override;
 	void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override;
 	void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override;
 
 
 	void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) override;
 	void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) override;

+ 1 - 1
server/NetPacksServer.cpp

@@ -44,7 +44,7 @@ void ApplyGhNetPackVisitor::visitEndTurn(EndTurn & pack)
 void ApplyGhNetPackVisitor::visitDismissHero(DismissHero & pack)
 void ApplyGhNetPackVisitor::visitDismissHero(DismissHero & pack)
 {
 {
 	gh.throwIfWrongOwner(&pack, pack.hid);
 	gh.throwIfWrongOwner(&pack, pack.hid);
-	result = gh.removeObject(gh.getObj(pack.hid));
+	result = gh.removeObject(gh.getObj(pack.hid), pack.player);
 }
 }
 
 
 void ApplyGhNetPackVisitor::visitMoveHero(MoveHero & pack)
 void ApplyGhNetPackVisitor::visitMoveHero(MoveHero & pack)

+ 3 - 3
server/battles/BattleResultProcessor.cpp

@@ -469,12 +469,12 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
 
 
 	if(finishingBattle->loserHero) //remove beaten hero
 	if(finishingBattle->loserHero) //remove beaten hero
 	{
 	{
-		RemoveObject ro(finishingBattle->loserHero->id);
+		RemoveObject ro(finishingBattle->loserHero->id, battle.battleGetArmyObject(0)->getOwner());
 		gameHandler->sendAndApply(&ro);
 		gameHandler->sendAndApply(&ro);
 	}
 	}
 	if(finishingBattle->isDraw() && finishingBattle->winnerHero) //for draw case both heroes should be removed
 	if(finishingBattle->isDraw() && finishingBattle->winnerHero) //for draw case both heroes should be removed
 	{
 	{
-		RemoveObject ro(finishingBattle->winnerHero->id);
+		RemoveObject ro(finishingBattle->winnerHero->id, battle.battleGetArmyObject(0)->getOwner());
 		gameHandler->sendAndApply(&ro);
 		gameHandler->sendAndApply(&ro);
 	}
 	}
 
 
@@ -557,7 +557,7 @@ void BattleResultProcessor::battleAfterLevelUp(const BattleID & battleID, const
 	if (result.winner != 2 && finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty()
 	if (result.winner != 2 && finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty()
 		&& (!finishingBattle->winnerHero->commander || !finishingBattle->winnerHero->commander->alive))
 		&& (!finishingBattle->winnerHero->commander || !finishingBattle->winnerHero->commander->alive))
 	{
 	{
-		RemoveObject ro(finishingBattle->winnerHero->id);
+		RemoveObject ro(finishingBattle->winnerHero->id, finishingBattle->winnerHero->getOwner());
 		gameHandler->sendAndApply(&ro);
 		gameHandler->sendAndApply(&ro);
 
 
 		if (VLC->settings()->getBoolean(EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS))
 		if (VLC->settings()->getBoolean(EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS))

+ 1 - 1
server/processors/HeroPoolProcessor.cpp

@@ -203,7 +203,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy
 	if(gameHandler->getTile(targetPos)->isWater() && !recruitedHero->boat)
 	if(gameHandler->getTile(targetPos)->isWater() && !recruitedHero->boat)
 	{
 	{
 		//Create a new boat for hero
 		//Create a new boat for hero
-		gameHandler->createObject(targetPos , Obj::BOAT, recruitedHero->getBoatType().getNum());
+		gameHandler->createObject(targetPos, player, Obj::BOAT, recruitedHero->getBoatType().getNum());
 
 
 		hr.boatId = gameHandler->getTopObj(targetPos)->id;
 		hr.boatId = gameHandler->getTopObj(targetPos)->id;
 	}
 	}

+ 1 - 1
server/queries/MapQueries.cpp

@@ -61,7 +61,7 @@ void CObjectVisitQuery::onRemoval(PlayerColor color)
 	//TODO or should it be destructor?
 	//TODO or should it be destructor?
 	//Can object visit affect 2 players and what would be desired behavior?
 	//Can object visit affect 2 players and what would be desired behavior?
 	if(removeObjectAfterVisit)
 	if(removeObjectAfterVisit)
-		gh->removeObject(visitedObject);
+		gh->removeObject(visitedObject, color);
 }
 }
 
 
 void CObjectVisitQuery::onExposure(QueryPtr topQuery)
 void CObjectVisitQuery::onExposure(QueryPtr topQuery)