瀏覽代碼

Cartographer/Observatory is now configurable object

Ivan Savenko 2 年之前
父節點
當前提交
98fd939ed6

+ 13 - 0
Global.h

@@ -475,6 +475,19 @@ namespace vstd
 		}
 	}
 
+	template<typename Elem, typename Predicate>
+	void erase_if(std::unordered_set<Elem> &setContainer, Predicate pred)
+	{
+		auto itr = setContainer.begin();
+		auto endItr = setContainer.end();
+		while(itr != endItr)
+		{
+			auto tmpItr = itr++;
+			if(pred(*tmpItr))
+				setContainer.erase(tmpItr);
+		}
+	}
+
 	//works for map and std::map, maybe something else
 	template<typename Key, typename Val, typename Predicate>
 	void erase_if(std::map<Key, Val> &container, Predicate pred)

+ 1 - 1
client/NetPacksClient.cpp

@@ -177,7 +177,7 @@ void ApplyClientNetPackVisitor::visitFoWChange(FoWChange & pack)
 		}
 		if(cl.getPlayerRelations(i.first, pack.player) != PlayerRelations::ENEMIES)
 		{
-			if(pack.mode)
+			if(pack.mode == FoWChange::Mode::REVEAL)
 				i.second->tileRevealed(pack.tiles);
 			else
 				i.second->tileHidden(pack.tiles);

+ 0 - 64
config/objects/generic.json

@@ -156,70 +156,6 @@
 		}
 	},
 
-	"redwoodObservatory" : {
-		"index" :58,
-		"handler" : "observatory",
-		"base" : {
-			"sounds" : {
-				"visit" : ["LIGHTHOUSE"]
-			}
-		},
-		"types" : {
-			"object" : {
-				"index" : 0,
-				"aiValue" : 750,
-				"templates" :
-				{
-					"base" : { "animation" : "avxredw.def", "visitableFrom" : [ "---", "+++", "+++" ], "mask" : [ "VV", "VV", "VA"], "allowedTerrains":["grass", "swamp", "dirt", "sand", "lava", "rough"] },
-					"snow" : { "animation" : "avxreds0.def", "visitableFrom" : [ "---", "+++", "+++" ], "mask" : [ "VV", "VV", "VA"], "allowedTerrains":["snow"] }
-				},
-				"rmg" : {
-					"zoneLimit"	: 1,
-					"value"		: 750,
-					"rarity"	: 100
-				}
-			}
-		}
-	},
-	"pillarOfFire" : {
-		"index" :60,
-		"handler" : "observatory",
-		"base" : {
-			"sounds" : {
-				"ambient" : ["LOOPFIRE"],
-				"visit" : ["LIGHTHOUSE"]
-			}
-		},
-		"types" : {
-			"object" : {
-				"index" : 0,
-				"aiValue" : 750,
-				"rmg" : {
-					"zoneLimit"	: 1,
-					"value"		: 750,
-					"rarity"	: 100
-				}
-			}
-		}
-	},
-	"coverOfDarkness" : {
-		"index" :15,
-		"handler" : "observatory",
-		"base" : {
-			"sounds" : {
-				"visit" : ["LIGHTHOUSE"]
-			}
-		},
-		"types" : {
-			"object" : {
-				"index" : 0,
-				"aiValue" : 100,
-				"rmg" : {
-				}
-			}
-		}
-	},
-
 	"whirlpool" : {
 		"index" :111,
 		"handler" : "whirlpool",

+ 0 - 17
config/objects/moddables.json

@@ -255,23 +255,6 @@
 		}
 	},
 
-	// subtype: different revealed areas
-	"cartographer" : {
-		"index" :13,
-		"handler": "cartographer",
-		"lastReservedIndex" : 2,
-		"base" : {
-			"sounds" : {
-				"visit" : ["LIGHTHOUSE"]
-			}
-		},
-		"types" : {
-			"water" : { "index" : 0, "aiValue" : 5000, "rmg" : { "zoneLimit" : 1,  "value" : 5000, "rarity" : 20 } },
-			"land" : { "index" : 1, "aiValue": 10000, "rmg" : { "zoneLimit" : 1,  "value" : 10000, "rarity" : 20 } },
-			"subterra" : { "index" : 2, "aiValue" : 7500, "rmg" : { "zoneLimit" : 1,  "value" : 7500, "rarity" : 20 } }
-		}
-	},
-
 	// subtype: resource ID
 	"mine" : {
 		"index" :53,

+ 208 - 0
config/objects/rewardableGeneric.json

@@ -66,4 +66,212 @@
 			}
 		}
 	},
+
+	"redwoodObservatory" : {
+		"index" :58,
+		"handler" : "configurable",
+		"base" : {
+			"sounds" : {
+				"visit" : ["LIGHTHOUSE"]
+			}
+		},
+		"types" : {
+			"redwoodObservatory" : {
+				"index" : 0,
+				"aiValue" : 750,
+				"templates" :
+				{
+					"base" : { "animation" : "avxredw.def", "visitableFrom" : [ "---", "+++", "+++" ], "mask" : [ "VV", "VV", "VA"], "allowedTerrains":["grass", "swamp", "dirt", "sand", "lava", "rough"] },
+					"snow" : { "animation" : "avxreds0.def", "visitableFrom" : [ "---", "+++", "+++" ], "mask" : [ "VV", "VV", "VA"], "allowedTerrains":["snow"] }
+				},
+				"rmg" : {
+					"zoneLimit"	: 1,
+					"value"		: 750,
+					"rarity"	: 100
+				},
+				
+				"compatibilityIdentifiers" : [ "object" ],
+				"visitMode" : "unlimited",
+				"rewards" : [
+					{
+						"message" : 98,
+						"revealTiles" : {
+							"radius" : 20,
+							"surface" : 1,
+							"subterra" : 1,
+							"water" : 1,
+							"rock" : 1
+						}
+					}
+				]
+			}
+		}
+	},
+
+	"pillarOfFire" : {
+		"index" :60,
+		"handler" : "configurable",
+		"base" : {
+			"sounds" : {
+				"ambient" : ["LOOPFIRE"],
+				"visit" : ["LIGHTHOUSE"]
+			}
+		},
+		"types" : {
+			"pillarOfFire" : {
+				"index" : 0,
+				"aiValue" : 750,
+				"rmg" : {
+					"zoneLimit"	: 1,
+					"value"		: 750,
+					"rarity"	: 100
+				},
+				
+				"compatibilityIdentifiers" : [ "object" ],
+				"visitMode" : "unlimited",
+				"rewards" : [
+					{
+						"message" : 99,
+						"revealTiles" : {
+							"radius" : 20,
+							"surface" : 1,
+							"subterra" : 1,
+							"water" : 1,
+							"rock" : 1
+						}
+					}
+				]
+			}
+		}
+	},
+
+	"coverOfDarkness" : {
+		"index" :15,
+		"handler" : "configurable",
+		"base" : {
+			"sounds" : {
+				"visit" : ["LIGHTHOUSE"]
+			}
+		},
+		"types" : {
+			"coverOfDarkness" : {
+				"index" : 0,
+				"aiValue" : 100,
+				"rmg" : {
+				},
+				
+				"compatibilityIdentifiers" : [ "object" ],
+				"visitMode" : "unlimited",
+				"rewards" : [
+					{
+						"message" : 31,
+						"revealTiles" : {
+							"radius" : 20,
+							"surface" : 1,
+							"subterra" : 1,
+							"water" : 1,
+							"rock" : 1,
+							"hide" : true
+						}
+					}
+				]
+			}
+		}
+	},
+	
+	"cartographer" : {
+		"index" :13,
+		"handler": "configurable",
+		"lastReservedIndex" : 2,
+		"base" : {
+			"sounds" : {
+				"visit" : ["LIGHTHOUSE"]
+			}
+		},
+		"types" : {
+			"cartographerWater" : {
+				"index" : 0,
+				"aiValue" : 5000,
+				"rmg" : {
+					"zoneLimit" : 1,
+					"value" : 5000,
+					"rarity" : 20
+				},
+				"compatibilityIdentifiers" : [ "water" ],
+				"visitMode" : "unlimited",
+				"canRefuse" : true,
+				"rewards" : [
+					{
+						"limiter" : { "resources" : { "gold" : 1000 } },
+						"message" : 25,
+						"resources" : {
+							"gold" : -1000
+						},
+						"revealTiles" : {
+							"water" : 1
+						}
+					}
+				],
+				"onEmptyMessage" : 28,
+				"onVisitedMessage" : 24
+			},
+			"cartographerLand" : {
+				"index" : 1,
+				"aiValue": 10000,
+				"rmg" : {
+					"zoneLimit" : 1,
+					"value" : 10000,
+					"rarity" : 2
+				},
+				"compatibilityIdentifiers" : [ "land" ],
+				"visitMode" : "unlimited",
+				"canRefuse" : true,
+				"rewards" : [
+					{
+						"limiter" : { "resources" : { "gold" : 1000 } },
+						"message" : 26,
+						"resources" : {
+							"gold" : -1000
+						},
+						"revealTiles" : {
+							"surface" : 1,
+							"water" : -1,
+							"rock" : -1
+						}
+					}
+				],
+				"onEmptyMessage" : 28,
+				"onVisitedMessage" : 24
+			},
+			"cartographerSubterranean" : {
+				"index" : 2,
+				"aiValue" : 7500,
+				"rmg" : {
+					"zoneLimit" : 1,
+					"value" : 7500,
+					"rarity" : 20
+				},
+				"compatibilityIdentifiers" : [ "subterra" ],
+				"visitMode" : "unlimited",
+				"canRefuse" : true,
+				"rewards" : [
+					{
+						"limiter" : { "resources" : { "gold" : 1000 } },
+						"message" : 27,
+						"resources" : {
+							"gold" : -1000
+						},
+						"revealTiles" : {
+							"subterra" : 1,
+							"water" : -1,
+							"rock" : -1,
+							"surface" : -1
+						}
+					}
+				],
+				"onEmptyMessage" : 28,
+				"onVisitedMessage" : 24
+			}
+		}
+	}
 }

+ 1 - 1
lib/CGameInfoCallback.cpp

@@ -607,7 +607,7 @@ EBuildingState CGameInfoCallback::canBuildStructure( const CGTownInstance *t, Bu
 	{
 		const TerrainTile *tile = getTile(t->bestLocation(), false);
 
-		if(!tile || tile->terType->isLand())
+		if(!tile || !tile->terType->isWater())
 			return EBuildingState::NO_WATER; //lack of water
 	}
 

+ 5 - 25
lib/IGameCallback.cpp

@@ -88,7 +88,7 @@ void CPrivilegedInfoCallback::getTilesInRange(std::unordered_set<int3> & tiles,
 		return;
 	}
 	if(radious == CBuilding::HEIGHT_SKYSHIP) //reveal entire map
-		getAllTiles (tiles, player, -1, MapTerrainFilterMode::NONE);
+		getAllTiles (tiles, player, -1, [](auto * tile){return true;});
 	else
 	{
 		const TeamState * team = !player ? nullptr : gs->getPlayerTeam(*player);
@@ -112,7 +112,7 @@ void CPrivilegedInfoCallback::getTilesInRange(std::unordered_set<int3> & tiles,
 	}
 }
 
-void CPrivilegedInfoCallback::getAllTiles(std::unordered_set<int3> & tiles, std::optional<PlayerColor> Player, int level, MapTerrainFilterMode tileFilterMode) const
+void CPrivilegedInfoCallback::getAllTiles(std::unordered_set<int3> & tiles, std::optional<PlayerColor> Player, int level, std::function<bool(const TerrainTile *)> filter) const
 {
 	if(!!Player && !Player->isValidPlayer())
 	{
@@ -137,29 +137,9 @@ void CPrivilegedInfoCallback::getAllTiles(std::unordered_set<int3> & tiles, std:
 		{
 			for(int yd = 0; yd < gs->map->height; yd++)
 			{
-				bool isTileEligible = false;
-
-				switch(tileFilterMode)
-				{
-					case MapTerrainFilterMode::NONE:
-						isTileEligible = true;
-						break;
-					case MapTerrainFilterMode::WATER:
-						isTileEligible = getTile(int3(xd, yd, zd))->terType->isWater();
-						break;
-					case MapTerrainFilterMode::LAND:
-						isTileEligible = getTile(int3(xd, yd, zd))->terType->isLand();
-						break;
-					case MapTerrainFilterMode::LAND_CARTOGRAPHER:
-						isTileEligible = getTile(int3(xd, yd, zd))->terType->isSurfaceCartographerCompatible();
-						break;
-					case MapTerrainFilterMode::UNDERGROUND_CARTOGRAPHER:
-						isTileEligible = getTile(int3(xd, yd, zd))->terType->isUndergroundCartographerCompatible();
-						break;
-				}
-
-				if(isTileEligible)
-					tiles.insert(int3(xd, yd, zd));
+				int3 coordinates(xd, yd, zd);
+				if (filter(getTile(coordinates)))
+					tiles.insert(coordinates);
 			}
 		}
 	}

+ 1 - 11
lib/IGameCallback.h

@@ -43,15 +43,6 @@ namespace scripting
 class DLL_LINKAGE CPrivilegedInfoCallback : public CGameInfoCallback
 {
 public:
-	enum class MapTerrainFilterMode
-	{
-		NONE = 0,
-		LAND = 1,
-		WATER = 2,
-		LAND_CARTOGRAPHER = 3,
-		UNDERGROUND_CARTOGRAPHER = 4
-	};
-
 	CGameState *gameState();
 
 	//used for random spawns
@@ -66,8 +57,7 @@ public:
 						 int3::EDistanceFormula formula = int3::DIST_2D) const;
 
 	//returns all tiles on given level (-1 - both levels, otherwise number of level)
-	void getAllTiles(std::unordered_set<int3> &tiles, std::optional<PlayerColor> player = std::optional<PlayerColor>(),
-					 int level = -1, MapTerrainFilterMode tileFilterMode = MapTerrainFilterMode::NONE) const;
+	void getAllTiles(std::unordered_set<int3> &tiles, std::optional<PlayerColor> player, int level, std::function<bool(const TerrainTile *)> filter) const;
 
 	//gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant
 	void pickAllowedArtsSet(std::vector<const CArtifact *> & out, CRandomGenerator & rand) const; 

+ 7 - 1
lib/NetPacks.h

@@ -344,11 +344,17 @@ struct DLL_LINKAGE SetMovePoints : public CPackForClient
 
 struct DLL_LINKAGE FoWChange : public CPackForClient
 {
+	enum class Mode : uint8_t
+	{
+		HIDE,
+		REVEAL
+	};
+
 	void applyGs(CGameState * gs);
 
 	std::unordered_set<int3> tiles;
 	PlayerColor player;
-	ui8 mode = 0; //mode==0 - hide, mode==1 - reveal
+	Mode mode;
 	bool waitForDialogs = false;
 
 	virtual void visitTyped(ICPackVisitor & visitor) override;

+ 3 - 2
lib/NetPacksLib.cpp

@@ -924,8 +924,9 @@ void FoWChange::applyGs(CGameState *gs)
 	TeamState * team = gs->getPlayerTeam(player);
 	auto fogOfWarMap = team->fogOfWarMap;
 	for(const int3 & t : tiles)
-		(*fogOfWarMap)[t.z][t.x][t.y] = mode;
-	if (mode == 0) //do not hide too much
+		(*fogOfWarMap)[t.z][t.x][t.y] = mode != Mode::HIDE;
+
+	if (mode == Mode::HIDE) //do not hide too much
 	{
 		std::unordered_set<int3> tilesRevealed;
 		for (auto & elem : gs->map->objects)

+ 6 - 11
lib/TerrainHandler.cpp

@@ -155,9 +155,14 @@ bool TerrainType::isWater() const
 	return passabilityType & PassabilityType::WATER;
 }
 
+bool TerrainType::isRock() const
+{
+	return passabilityType & PassabilityType::ROCK;
+}
+
 bool TerrainType::isPassable() const
 {
-	return !(passabilityType & PassabilityType::ROCK);
+	return !isRock();
 }
 
 bool TerrainType::isSurface() const
@@ -170,16 +175,6 @@ bool TerrainType::isUnderground() const
 	return passabilityType & PassabilityType::SUBTERRANEAN;
 }
 
-bool TerrainType::isSurfaceCartographerCompatible() const
-{
-	return isSurface();
-}
-
-bool TerrainType::isUndergroundCartographerCompatible() const
-{
-	return isLand() && isPassable() && !isSurface();
-}
-
 bool TerrainType::isTransitionRequired() const
 {
 	return transitionRequired;

+ 3 - 2
lib/TerrainHandler.h

@@ -84,12 +84,13 @@ public:
 
 	bool isLand() const;
 	bool isWater() const;
+	bool isRock() const;
+
 	bool isPassable() const;
+
 	bool isSurface() const;
 	bool isUnderground() const;
 	bool isTransitionRequired() const;
-	bool isSurfaceCartographerCompatible() const;
-	bool isUndergroundCartographerCompatible() const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{

+ 0 - 2
lib/mapObjectConstructors/CObjectClassesHandler.cpp

@@ -71,7 +71,6 @@ CObjectClassesHandler::CObjectClassesHandler()
 	SET_HANDLER("randomDwelling", CGDwelling);
 
 	SET_HANDLER("generic", CGObjectInstance);
-	SET_HANDLER("cartographer", CCartographer);
 	SET_HANDLER("artifact", CGArtifact);
 	SET_HANDLER("borderGate", CGBorderGate);
 	SET_HANDLER("borderGuard", CGBorderGuard);
@@ -84,7 +83,6 @@ CObjectClassesHandler::CObjectClassesHandler()
 	SET_HANDLER("magi", CGMagi);
 	SET_HANDLER("mine", CGMine);
 	SET_HANDLER("obelisk", CGObelisk);
-	SET_HANDLER("observatory", CGObservatory);
 	SET_HANDLER("pandora", CGPandoraBox);
 	SET_HANDLER("prison", CGHeroInstance);
 	SET_HANDLER("questGuard", CGQuestGuard);

+ 1 - 1
lib/mapObjects/CGTownInstance.cpp

@@ -1068,7 +1068,7 @@ void CGTownInstance::onTownCaptured(const PlayerColor & winner) const
 	setOwner(winner);
 	FoWChange fw;
 	fw.player = winner;
-	fw.mode = 1;
+	fw.mode = FoWChange::Mode::REVEAL;
 	cb->getTilesInRange(fw.tiles, getSightCenter(), getSightRadius(), winner, 1);
 	cb->sendAndApply(& fw);
 }

+ 0 - 1
lib/mapObjects/CRewardableObject.h

@@ -90,6 +90,5 @@ public:
 
 // POSSIBLE
 // class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles
-// class DLL_LINKAGE CGScholar : public CGObjectInstance
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 108
lib/mapObjects/MiscObjects.cpp

@@ -842,40 +842,6 @@ void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler)
 	}
 }
 
-void CGObservatory::onHeroVisit( const CGHeroInstance * h ) const
-{
-	InfoWindow iw;
-	iw.type = EInfoWindowMode::AUTO;
-	iw.player = h->tempOwner;
-	switch (ID)
-	{
-	case Obj::REDWOOD_OBSERVATORY:
-	case Obj::PILLAR_OF_FIRE:
-	{
-		iw.text.appendLocalString(EMetaText::ADVOB_TXT,98 + (ID==Obj::PILLAR_OF_FIRE));
-
-		FoWChange fw;
-		fw.player = h->tempOwner;
-		fw.mode = 1;
-		cb->getTilesInRange (fw.tiles, pos, 20, h->tempOwner, 1);
-		cb->sendAndApply (&fw);
-		break;
-	}
-	case Obj::COVER_OF_DARKNESS:
-	{
-		iw.text.appendLocalString (EMetaText::ADVOB_TXT, 31);
-		for (auto & player : cb->gameState()->players)
-		{
-			if (cb->getPlayerStatus(player.first) == EPlayerStatus::INGAME &&
-				cb->getPlayerRelations(player.first, h->tempOwner) == PlayerRelations::ENEMIES)
-				cb->changeFogOfWar(visitablePos(), 20, player.first, true);
-		}
-		break;
-	}
-	}
-	cb->showInfoDialog(&iw);
-}
-
 void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
 {
 	if(spell == SpellID::NONE)
@@ -1175,7 +1141,7 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
 
 			FoWChange fw;
 			fw.player = h->tempOwner;
-			fw.mode = 1;
+			fw.mode = FoWChange::Mode::REVEAL;
 			fw.waitForDialogs = true;
 
 			for(const auto & it : eyelist[subID])
@@ -1321,79 +1287,6 @@ BoatId CGShipyard::getBoatType() const
 	return createdBoat;
 }
 
-void CCartographer::onHeroVisit( const CGHeroInstance * h ) const
-{
-	//if player has not bought map of this subtype yet and underground exist for stalagmite cartographer
-	if (!wasVisited(h->getOwner()) && (subID != 2 || cb->gameState()->map->twoLevel))
-	{
-		if (cb->getResource(h->tempOwner, EGameResID::GOLD) >= 1000) //if he can afford a map
-		{
-			//ask if he wants to buy one
-			int text=0;
-			switch (subID)
-			{
-				case 0:
-					text = 25;
-					break;
-				case 1:
-					text = 26;
-					break;
-				case 2:
-					text = 27;
-					break;
-				default:
-					logGlobal->warn("Unrecognized subtype of cartographer");
-			}
-			assert(text);
-			BlockingDialog bd (true, false);
-			bd.player = h->getOwner();
-			bd.text.appendLocalString (EMetaText::ADVOB_TXT, text);
-			cb->showBlockingDialog (&bd);
-		}
-		else //if he cannot afford
-		{
-			h->showInfoDialog(28);
-		}
-	}
-	else //if he already visited carographer
-	{
-		h->showInfoDialog(24);
-	}
-}
-
-void CCartographer::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
-{
-	if(answer) //if hero wants to buy map
-	{
-		cb->giveResource(hero->tempOwner, EGameResID::GOLD, -1000);
-		FoWChange fw;
-		fw.mode = 1;
-		fw.player = hero->tempOwner;
-
-		//subIDs of different types of cartographers:
-		//water = 0; land = 1; underground = 2;
-
-		IGameCallback::MapTerrainFilterMode tileFilterMode = IGameCallback::MapTerrainFilterMode::NONE;
-
-		switch(subID)
-		{
-			case 0:
-				tileFilterMode = CPrivilegedInfoCallback::MapTerrainFilterMode::WATER;
-				break;
-			case 1:
-				tileFilterMode = CPrivilegedInfoCallback::MapTerrainFilterMode::LAND_CARTOGRAPHER;
-				break;
-			case 2:
-				tileFilterMode = CPrivilegedInfoCallback::MapTerrainFilterMode::UNDERGROUND_CARTOGRAPHER;
-				break;
-		}
-
-		cb->getAllTiles(fw.tiles, hero->tempOwner, -1, tileFilterMode); //reveal appropriate tiles
-		cb->sendAndApply(&fw);
-		cb->setObjProperty(id, CCartographer::OBJPROP_VISITED, hero->tempOwner.getNum());
-	}
-}
-
 void CGDenOfthieves::onHeroVisit (const CGHeroInstance * h) const
 {
 	cb->showObjectWindow(this, EOpenWindowMode::THIEVES_GUILD, h, false);

+ 0 - 24
lib/mapObjects/MiscObjects.h

@@ -314,17 +314,6 @@ public:
 	}
 };
 
-class DLL_LINKAGE CGObservatory : public CGObjectInstance //Redwood observatory
-{
-public:
-	void onHeroVisit(const CGHeroInstance * h) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CGObjectInstance&>(*this);
-	}
-};
-
 class DLL_LINKAGE CGBoat : public CGObjectInstance, public CBonusSystemNode
 {
 public:
@@ -396,19 +385,6 @@ public:
 	}
 };
 
-class DLL_LINKAGE CCartographer : public CTeamVisited
-{
-///behaviour varies depending on surface and  floor
-public:
-	void onHeroVisit(const CGHeroInstance * h) const override;
-	void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<CTeamVisited&>(*this);
-	}
-};
-
 class DLL_LINKAGE CGDenOfthieves : public CGObjectInstance
 {
 	void onHeroVisit(const CGHeroInstance * h) const override;

+ 0 - 4
lib/registerTypes/RegisterTypes.h

@@ -56,7 +56,6 @@ void registerTypesMapObjects1(Serializer &s)
 			s.template registerType<CGMonolith, CGWhirlpool>();
 	s.template registerType<CGObjectInstance, CGSignBottle>();
 	s.template registerType<CGObjectInstance, CGScholar>();
-	s.template registerType<CGObjectInstance, CGObservatory>();
 	s.template registerType<CGObjectInstance, CGKeys>();
 		s.template registerType<CGKeys, CGKeymasterTent>();
 		s.template registerType<CGKeys, CGBorderGuard>(); s.template registerType<IQuestObject, CGBorderGuard>();
@@ -112,7 +111,6 @@ void registerTypesMapObjectTypes(Serializer &s)
 #define REGISTER_GENERIC_HANDLER(TYPENAME) s.template registerType<AObjectTypeHandler, CDefaultObjectTypeHandler<TYPENAME> >()
 
 	REGISTER_GENERIC_HANDLER(CGObjectInstance);
-	REGISTER_GENERIC_HANDLER(CCartographer);
 	REGISTER_GENERIC_HANDLER(CGArtifact);
 	REGISTER_GENERIC_HANDLER(CGBlackMarket);
 	REGISTER_GENERIC_HANDLER(CGBoat);
@@ -132,7 +130,6 @@ void registerTypesMapObjectTypes(Serializer &s)
 	REGISTER_GENERIC_HANDLER(CGMarket);
 	REGISTER_GENERIC_HANDLER(CGMine);
 	REGISTER_GENERIC_HANDLER(CGObelisk);
-	REGISTER_GENERIC_HANDLER(CGObservatory);
 	REGISTER_GENERIC_HANDLER(CGPandoraBox);
 	REGISTER_GENERIC_HANDLER(CGQuestGuard);
 	REGISTER_GENERIC_HANDLER(CGResource);
@@ -177,7 +174,6 @@ void registerTypesMapObjects2(Serializer &s)
 
 	s.template registerType<CGObjectInstance, CTeamVisited>();
 		s.template registerType<CTeamVisited, CGShrine>();
-		s.template registerType<CTeamVisited, CCartographer>();
 		s.template registerType<CTeamVisited, CGObelisk>();
 
 	//s.template registerType<CQuest>();

+ 14 - 0
lib/rewardable/Info.cpp

@@ -161,6 +161,20 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRand
 		reward.spellCast.second = source["spellCast"]["schoolLevel"].Integer();
 	}
 
+	if (!source["revealTiles"].isNull())
+	{
+		auto const & entry = source["revealTiles"];
+
+		reward.revealTiles = RewardRevealTiles();
+		reward.revealTiles->radius = JsonRandom::loadValue(entry["radius"], rng, variables);
+		reward.revealTiles->hide = entry["hide"].Bool();
+
+		reward.revealTiles->scoreSurface = JsonRandom::loadValue(entry["surface"], rng, variables);
+		reward.revealTiles->scoreSubterra = JsonRandom::loadValue(entry["subterra"], rng, variables);
+		reward.revealTiles->scoreWater = JsonRandom::loadValue(entry["water"], rng, variables);
+		reward.revealTiles->scoreRock = JsonRandom::loadValue(entry["rock"], rng, variables);
+	}
+
 	for ( auto node : source["changeCreatures"].Struct() )
 	{
 		CreatureID from(VLC->identifiers()->getIdentifier(node.second.meta, "creature", node.first).value());

+ 47 - 0
lib/rewardable/Interface.cpp

@@ -12,6 +12,7 @@
 #include "Interface.h"
 
 #include "../CHeroHandler.h"
+#include "../TerrainHandler.h"
 #include "../CSoundBase.h"
 #include "../NetPacks.h"
 #include "../spells/CSpellHandler.h"
@@ -46,6 +47,52 @@ void Rewardable::Interface::grantRewardBeforeLevelup(IGameCallback * cb, const R
 
 	cb->giveResources(hero->tempOwner, info.reward.resources);
 
+	if (info.reward.revealTiles)
+	{
+		auto const & props = *info.reward.revealTiles;
+		FoWChange fw;
+
+		if (props.hide)
+			fw.mode = FoWChange::Mode::HIDE;
+		else
+			fw.mode = FoWChange::Mode::REVEAL;
+
+		fw.player = hero->tempOwner;
+
+		auto const functor = [&props](const TerrainTile * tile)
+		{
+			int score = 0;
+			if (tile->terType->isSurface())
+				score += props.scoreSurface;
+
+			if (tile->terType->isUnderground())
+				score += props.scoreSubterra;
+
+			if (tile->terType->isWater())
+				score += props.scoreWater;
+
+			if (tile->terType->isRock())
+				score += props.scoreRock;
+
+			return score > 0;
+		};
+
+		if (props.radius > 0)
+		{
+			cb->getTilesInRange(fw.tiles, hero->getSightCenter(), props.radius, hero->tempOwner, 1);
+			vstd::erase_if(fw.tiles, [&](const int3 & coord){
+				return functor(cb->getTile(coord));
+			});
+		}
+		else
+		{
+			cb->getAllTiles(fw.tiles, hero->tempOwner, -1, functor);
+		}
+
+		cb->sendAndApply(&fw);
+
+	}
+
 	for(const auto & entry : info.reward.secondary)
 	{
 		int current = hero->getSecSkillLevel(entry.first);

+ 5 - 0
lib/rewardable/Reward.cpp

@@ -18,6 +18,11 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+void Rewardable::RewardRevealTiles::serializeJson(JsonSerializeFormat & handler)
+{
+	// TODO
+}
+
 Rewardable::Reward::Reward()
 	: heroExperience(0)
 	, heroLevel(0)

+ 32 - 2
lib/rewardable/Reward.h

@@ -27,6 +27,34 @@ namespace Rewardable
 struct Reward;
 using RewardsList = std::vector<std::shared_ptr<Rewardable::Reward>>;
 
+struct RewardRevealTiles
+{
+	/// Reveal distance, if not positive - reveal entire map
+	int radius;
+	/// Reveal score of terrains with "surface" flag set
+	int scoreSurface;
+	/// Reveal score of terrains with "subterra" flag set
+	int scoreSubterra;
+	/// Reveal score of terrains with "water" flag set
+	int scoreWater;
+	/// Reveal score of terrains with "rock" flag set
+	int scoreRock;
+	/// If set, then terrain will be instead hidden for all enemies (Cover of Darkness)
+	bool hide;
+
+	void serializeJson(JsonSerializeFormat & handler);
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & radius;
+		h & scoreSurface;
+		h & scoreSubterra;
+		h & scoreWater;
+		h & scoreRock;
+		h & hide;
+	}
+};
+
 /// Reward that can be granted to a hero
 /// NOTE: eventually should replace seer hut rewards and events/pandoras
 struct DLL_LINKAGE Reward final
@@ -74,6 +102,8 @@ struct DLL_LINKAGE Reward final
 	/// list of components that will be added to reward description. First entry in list will override displayed component
 	std::vector<Component> extraComponents;
 
+	std::optional<RewardRevealTiles> revealTiles;
+
 	/// if set to true, object will be removed after granting reward
 	bool removeObject;
 
@@ -107,8 +137,8 @@ struct DLL_LINKAGE Reward final
 		h & spells;
 		h & creatures;
 		h & creaturesChange;
-		if(version >= 821)
-			h & spellCast;
+		h & revealTiles;
+		h & spellCast;
 	}
 	
 	void serializeJson(JsonSerializeFormat & handler);

+ 3 - 3
server/CGameHandler.cpp

@@ -858,7 +858,7 @@ void CGameHandler::onNewTurn()
 			if (player != PlayerColor::NEUTRAL) //do not reveal fow for neutral player
 			{
 				FoWChange fw;
-				fw.mode = 1;
+				fw.mode = FoWChange::Mode::REVEAL;
 				fw.player = player;
 				// find all hidden tiles
 				const auto fow = getPlayerTeam(player)->fogOfWarMap;
@@ -2389,7 +2389,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 	// now when everything is built - reveal tiles for lookout tower
 	FoWChange fw;
 	fw.player = t->tempOwner;
-	fw.mode = 1;
+	fw.mode = FoWChange::Mode::REVEAL;
 	getTilesInRange(fw.tiles, t->getSightCenter(), t->getSightRadius(), t->tempOwner, 1);
 	sendAndApply(&fw);
 
@@ -4135,7 +4135,7 @@ void CGameHandler::changeFogOfWar(std::unordered_set<int3> &tiles, PlayerColor p
 	FoWChange fow;
 	fow.tiles = tiles;
 	fow.player = player;
-	fow.mode = hide? 0 : 1;
+	fow.mode = hide ? FoWChange::Mode::HIDE : FoWChange::Mode::REVEAL;
 	sendAndApply(&fow);
 }
 

+ 2 - 2
server/processors/PlayerMessageProcessor.cpp

@@ -346,7 +346,7 @@ void PlayerMessageProcessor::cheatDefeat(PlayerColor player)
 void PlayerMessageProcessor::cheatMapReveal(PlayerColor player, bool reveal)
 {
 	FoWChange fc;
-	fc.mode = reveal;
+	fc.mode = reveal ? FoWChange::Mode::REVEAL : FoWChange::Mode::HIDE;
 	fc.player = player;
 	const auto & fowMap = gameHandler->gameState()->getPlayerTeam(player)->fogOfWarMap;
 	const auto & mapSize = gameHandler->gameState()->getMapSize();
@@ -356,7 +356,7 @@ void PlayerMessageProcessor::cheatMapReveal(PlayerColor player, bool reveal)
 	for(int z = 0; z < mapSize.z; z++)
 		for(int x = 0; x < mapSize.x; x++)
 			for(int y = 0; y < mapSize.y; y++)
-				if(!(*fowMap)[z][x][y] || !fc.mode)
+				if(!(*fowMap)[z][x][y] || fc.mode == FoWChange::Mode::HIDE)
 					hlp_tab[lastUnc++] = int3(x, y, z);
 
 	fc.tiles.insert(hlp_tab, hlp_tab + lastUnc);