mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Random map generator refactoring (#762)
random map generator refactoring and improvements
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/github.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/github.yml
									
									
									
									
										vendored
									
									
								
							| @@ -24,7 +24,7 @@ jobs: | ||||
|             test: 0 | ||||
|             preset: linux-gcc-release | ||||
|           - platform: mac | ||||
|             os: macos-latest | ||||
|             os: macos-10.15 | ||||
|             test: 0 | ||||
|             pack: 1 | ||||
|             extension: dmg | ||||
|   | ||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -26,8 +26,6 @@ CMakeFiles | ||||
| Makefile | ||||
| cmake_install.cmake | ||||
| install_manifest.txt | ||||
| *_cotire.cmake | ||||
| cotire | ||||
| moc_*.cpp | ||||
| qrc_*.cpp | ||||
| ui_*.h | ||||
|   | ||||
| @@ -36,8 +36,6 @@ target_include_directories(BattleAI PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) | ||||
| target_link_libraries(BattleAI PRIVATE vcmi) | ||||
|  | ||||
| vcmi_set_output_dir(BattleAI "AI") | ||||
|  | ||||
| set_target_properties(BattleAI PROPERTIES ${PCH_PROPERTIES}) | ||||
| cotire(BattleAI) | ||||
| enable_pch(BattleAI) | ||||
|  | ||||
| install(TARGETS BattleAI RUNTIME DESTINATION ${AI_LIB_DIR} LIBRARY DESTINATION ${AI_LIB_DIR}) | ||||
|   | ||||
| @@ -18,7 +18,6 @@ target_include_directories(EmptyAI PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) | ||||
| target_link_libraries(EmptyAI PRIVATE vcmi) | ||||
|  | ||||
| vcmi_set_output_dir(EmptyAI "AI") | ||||
|  | ||||
| set_target_properties(EmptyAI PROPERTIES ${PCH_PROPERTIES}) | ||||
| enable_pch(EmptyAI) | ||||
|  | ||||
| install(TARGETS EmptyAI RUNTIME DESTINATION ${AI_LIB_DIR} LIBRARY DESTINATION ${AI_LIB_DIR}) | ||||
|   | ||||
| @@ -135,8 +135,6 @@ target_link_libraries(Nullkiller PRIVATE vcmi fuzzylite::fuzzylite) | ||||
| target_link_libraries(Nullkiller PRIVATE TBB::tbb) | ||||
|  | ||||
| vcmi_set_output_dir(Nullkiller "AI") | ||||
|  | ||||
| set_target_properties(Nullkiller PROPERTIES ${PCH_PROPERTIES}) | ||||
| cotire(Nullkiller) | ||||
| enable_pch(Nullkiller) | ||||
|  | ||||
| install(TARGETS Nullkiller RUNTIME DESTINATION ${AI_LIB_DIR} LIBRARY DESTINATION ${AI_LIB_DIR}) | ||||
|   | ||||
| @@ -18,8 +18,6 @@ target_link_libraries(StupidAI PRIVATE vcmi) | ||||
| target_include_directories(StupidAI PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) | ||||
|  | ||||
| vcmi_set_output_dir(StupidAI "AI") | ||||
|  | ||||
| set_target_properties(StupidAI PROPERTIES ${PCH_PROPERTIES}) | ||||
| cotire(StupidAI) | ||||
| enable_pch(StupidAI) | ||||
|  | ||||
| install(TARGETS StupidAI RUNTIME DESTINATION ${AI_LIB_DIR} LIBRARY DESTINATION ${AI_LIB_DIR}) | ||||
|   | ||||
| @@ -114,8 +114,6 @@ target_include_directories(VCAI PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) | ||||
| target_link_libraries(VCAI PRIVATE vcmi fuzzylite::fuzzylite) | ||||
|  | ||||
| vcmi_set_output_dir(VCAI "AI") | ||||
|  | ||||
| set_target_properties(VCAI PROPERTIES ${PCH_PROPERTIES}) | ||||
| cotire(VCAI) | ||||
| enable_pch(VCAI) | ||||
|  | ||||
| install(TARGETS VCAI RUNTIME DESTINATION ${AI_LIB_DIR} LIBRARY DESTINATION ${AI_LIB_DIR}) | ||||
|   | ||||
| @@ -5,4 +5,4 @@ brew update | ||||
| brew install smpeg2 libpng freetype qt5 ffmpeg ninja boost tbb luajit | ||||
| brew install sdl2 sdl2_ttf sdl2_image sdl2_mixer | ||||
|  | ||||
| echo CMAKE_PREFIX_PATH="/usr/local/opt/ffmpeg@4:/usr/local/opt/qt5:$CMAKE_PREFIX_PATH" >> $GITHUB_ENV | ||||
| echo CMAKE_PREFIX_PATH="$(brew --prefix)/opt/qt5:$CMAKE_PREFIX_PATH" >> $GITHUB_ENV | ||||
|   | ||||
| @@ -48,7 +48,9 @@ option(ENABLE_ERM "Enable compilation of ERM scripting module" ON) | ||||
| option(ENABLE_LUA "Enable compilation of LUA scripting module" ON) | ||||
| option(ENABLE_LAUNCHER "Enable compilation of launcher" ON) | ||||
| option(ENABLE_TEST "Enable compilation of unit tests" ON) | ||||
| option(ENABLE_PCH "Enable compilation using precompiled headers" ON) | ||||
| if(NOT ${CMAKE_VERSION} VERSION_LESS "3.16.0") | ||||
| 	option(ENABLE_PCH "Enable compilation using precompiled headers" ON) | ||||
| endif(NOT ${CMAKE_VERSION} VERSION_LESS "3.16.0") | ||||
| option(ENABLE_GITVERSION "Enable Version.cpp with Git commit hash" ON) | ||||
| option(ENABLE_DEBUG_CONSOLE "Enable debug console for Windows builds" ON) | ||||
| option(ENABLE_MULTI_PROCESS_BUILDS "Enable /MP flag for MSVS solution" ON) | ||||
| @@ -95,18 +97,14 @@ else() | ||||
| endif(ENABLE_GITVERSION) | ||||
|  | ||||
| # Precompiled header configuration | ||||
| if(ENABLE_PCH) | ||||
| 	include(cotire) | ||||
| 	set(PCH_PROPERTIES | ||||
| 		COTIRE_ENABLE_PRECOMPILED_HEADER ${ENABLE_PCH} | ||||
| 		COTIRE_ADD_UNITY_BUILD FALSE | ||||
| 		COTIRE_CXX_PREFIX_HEADER_INIT "StdInc.h" | ||||
| 	) | ||||
| else() | ||||
| 	set(PCH_PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE) | ||||
| 	macro(cotire ignore) | ||||
| 	endmacro(cotire) | ||||
| endif(ENABLE_PCH) | ||||
| if(ENABLE_PCH AND NOT ${CMAKE_VERSION} VERSION_LESS "3.16.0") | ||||
| 	macro(enable_pch name) | ||||
| 		target_precompile_headers(${name} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:<StdInc.h$<ANGLE-R>>) | ||||
| 	endmacro(enable_pch) | ||||
| else(ENABLE_PCH AND NOT ${CMAKE_VERSION} VERSION_LESS "3.16.0") | ||||
| 	macro(enable_pch ignore) | ||||
| 	endmacro(enable_pch) | ||||
| endif(ENABLE_PCH AND NOT ${CMAKE_VERSION} VERSION_LESS "3.16.0") | ||||
|  | ||||
| ############################################ | ||||
| #        Documentation section             # | ||||
|   | ||||
| @@ -186,9 +186,7 @@ target_include_directories(vcmiclient | ||||
| 	PUBLIC	${CMAKE_CURRENT_SOURCE_DIR}) | ||||
|  | ||||
| vcmi_set_output_dir(vcmiclient "") | ||||
|  | ||||
| set_target_properties(vcmiclient PROPERTIES ${PCH_PROPERTIES}) | ||||
| cotire(vcmiclient) | ||||
| enable_pch(vcmiclient) | ||||
|  | ||||
| install(TARGETS vcmiclient DESTINATION ${BIN_DIR}) | ||||
|  | ||||
|   | ||||
| @@ -11,12 +11,12 @@ | ||||
|  | ||||
| #include "../lib/CConfigHandler.h" | ||||
| #include "../lib/CSoundBase.h" | ||||
| #include "../lib/Terrain.h" | ||||
|  | ||||
| struct _Mix_Music; | ||||
| struct SDL_RWops; | ||||
| typedef struct _Mix_Music Mix_Music; | ||||
| struct Mix_Chunk; | ||||
| class Terrain; | ||||
|  | ||||
| class CAudioBase { | ||||
| protected: | ||||
|   | ||||
| @@ -443,14 +443,15 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero) | ||||
| 	} | ||||
|  | ||||
| 	wanderingHeroes -= hero; | ||||
| 	if (vstd::contains(paths, hero)) | ||||
| 		paths.erase(hero); | ||||
|  | ||||
| 	adventureInt->heroList.update(hero); | ||||
| 	if (makingTurn && newSelection) | ||||
| 		adventureInt->select(newSelection, true); | ||||
| 	else if (adventureInt->selection == hero) | ||||
| 		adventureInt->selection = nullptr; | ||||
| 	 | ||||
| 	if (vstd::contains(paths, hero)) | ||||
| 		paths.erase(hero); | ||||
| } | ||||
|  | ||||
| void CPlayerInterface::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) | ||||
| @@ -2765,7 +2766,8 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) | ||||
| 				destinationTeleport = destTeleportObj->id; | ||||
| 				destinationTeleportPos = nextCoord; | ||||
| 				doMovement(h->pos, false); | ||||
| 				if (path.nodes[i-1].action == CGPathNode::TELEPORT_BLOCKING_VISIT) | ||||
| 				if (path.nodes[i-1].action == CGPathNode::TELEPORT_BLOCKING_VISIT | ||||
| 					|| path.nodes[i-1].action == CGPathNode::TELEPORT_BATTLE) | ||||
| 				{ | ||||
| 					destinationTeleport = ObjectInstanceID(); | ||||
| 					destinationTeleportPos = int3(-1); | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
|  | ||||
| #include "ObjectLists.h" | ||||
| #include "../../lib/FunctionList.h" | ||||
| #include "Terrain.h" | ||||
|  | ||||
| class CArmedInstance; | ||||
| class CAnimation; | ||||
| @@ -30,7 +31,6 @@ struct InfoAboutTown; | ||||
| class CHeroTooltip; | ||||
| class CTownTooltip; | ||||
| class CTextBox; | ||||
| class Terrain; | ||||
|  | ||||
| /// Base UI Element for hero\town lists | ||||
| class CList : public CIntObject | ||||
|   | ||||
| @@ -1,22 +0,0 @@ | ||||
| Copyright (c) 2012-2014 Sascha Kratky | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person | ||||
| obtaining a copy of this software and associated documentation | ||||
| files (the "Software"), to deal in the Software without | ||||
| restriction, including without limitation the rights to use, | ||||
| copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the | ||||
| Software is furnished to do so, subject to the following | ||||
| conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be | ||||
| included in all copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||||
| OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||||
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | ||||
| HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||
| WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||||
| OTHER DEALINGS IN THE SOFTWARE. | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -18,16 +18,6 @@ | ||||
|   }, | ||||
|   "mines" : | ||||
|   { | ||||
|     "value" : | ||||
|     { | ||||
|       "wood" : 1500, | ||||
|       "ore" : 1500, | ||||
|       "gems" : 3500, | ||||
|       "crystal" : 3500, | ||||
|       "mercury" : 3500, | ||||
|       "sulfur" : 3500, | ||||
|       "gold" : 7000 | ||||
|     }, | ||||
|     "extraResourcesLimit" : 3 | ||||
|   }, | ||||
|   "minGuardStrength" : 2000, | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
| 		"music" : "Dirt.mp3", | ||||
| 		"tiles" : "DIRTTL", | ||||
| 		"code" : "dt", | ||||
| 		"river" : "rm", | ||||
| 		"battleFields" : ["dirt_birches", "dirt_hills", "dirt_pines"], | ||||
| 		"terrainViewPatterns" : "dirt" | ||||
| 	}, | ||||
| @@ -18,6 +19,7 @@ | ||||
| 		"music" : "Sand.mp3", | ||||
| 		"tiles" : "SANDTL", | ||||
| 		"code" : "sa", | ||||
| 		"river" : "rm", | ||||
| 		"battleFields" : ["sand_mesas"], | ||||
| 		"transitionRequired" : true, | ||||
| 		"terrainViewPatterns" : "sand" | ||||
| @@ -30,6 +32,7 @@ | ||||
| 		"music" : "Grass.mp3", | ||||
| 		"tiles" : "GRASTL", | ||||
| 		"code" : "gr", | ||||
| 		"river" : "rw", | ||||
| 		"battleFields" : ["grass_hills", "grass_pines"] | ||||
| 	}, | ||||
| 	"snow" : | ||||
| @@ -40,6 +43,7 @@ | ||||
| 		"music" : "Snow.mp3", | ||||
| 		"tiles" : "SNOWTL", | ||||
| 		"code" : "sn", | ||||
| 		"river" : "ri", | ||||
| 		"battleFields" : ["snow_mountains", "snow_trees"] | ||||
| 	}, | ||||
| 	"swamp" : | ||||
| @@ -50,6 +54,7 @@ | ||||
| 		"music" : "Swamp.mp3", | ||||
| 		"tiles" : "SWMPTL", | ||||
| 		"code" : "sw", | ||||
| 		"river" : "rw", | ||||
| 		"battleFields" : ["swamp_trees"] | ||||
| 	}, | ||||
| 	"rough" : | ||||
| @@ -60,6 +65,7 @@ | ||||
| 		"music" : "Rough.mp3", | ||||
| 		"tiles" : "ROUGTL", | ||||
| 		"code" : "rg", | ||||
| 		"river" : "rm", | ||||
| 		"battleFields" : ["rough"] | ||||
| 	}, | ||||
| 	"subterra" : | ||||
| @@ -71,7 +77,9 @@ | ||||
| 		"tiles" : "SUBBTL", | ||||
| 		"type" : "SUB", | ||||
| 		"code" : "sb", | ||||
| 		"battleFields" : ["subterranean"] | ||||
| 		"river" : "rw", | ||||
| 		"battleFields" : ["subterranean"], | ||||
| 		"rockTerrain" : "rock" | ||||
| 	}, | ||||
| 	"lava" : | ||||
| 	{ | ||||
| @@ -81,7 +89,9 @@ | ||||
| 		"music" : "Lava.mp3", | ||||
| 		"tiles" : "LAVATL", | ||||
| 		"code" : "lv", | ||||
| 		"battleFields" : ["lava"] | ||||
| 		"river" : "rl", | ||||
| 		"battleFields" : ["lava"], | ||||
| 		"rockTerrain" : "rock" | ||||
| 	}, | ||||
| 	"water" : | ||||
| 	{ | ||||
|   | ||||
| @@ -101,10 +101,7 @@ target_include_directories(vcmilauncher | ||||
| 	PUBLIC	${CMAKE_CURRENT_SOURCE_DIR} | ||||
| ) | ||||
| vcmi_set_output_dir(vcmilauncher "") | ||||
|  | ||||
| # temporary(?) disabled - generation of PCH takes too much time since cotire is trying to collect all Qt headers | ||||
| #set_target_properties(vcmilauncher PROPERTIES ${PCH_PROPERTIES}) | ||||
| #cotire(vcmilauncher) | ||||
| enable_pch(vcmilauncher) | ||||
|  | ||||
| # Copy to build directory for easier debugging | ||||
| add_custom_command(TARGET vcmilauncher POST_BUILD | ||||
|   | ||||
| @@ -19,12 +19,12 @@ | ||||
| #include "JsonNode.h" | ||||
| #include "IHandlerBase.h" | ||||
| #include "CRandomGenerator.h" | ||||
| #include "Terrain.h" | ||||
|  | ||||
| class CLegacyConfigParser; | ||||
| class CCreatureHandler; | ||||
| class CCreature; | ||||
| class JsonSerializeFormat; | ||||
| class Terrain; | ||||
|  | ||||
| class DLL_LINKAGE CCreature : public Creature, public CBonusSystemNode | ||||
| { | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
| #include "GameConstants.h" | ||||
| #include "HeroBonus.h" | ||||
| #include "IHandlerBase.h" | ||||
| #include "Terrain.h" | ||||
|  | ||||
| class CHeroClass; | ||||
| class CGameInfo; | ||||
| @@ -26,7 +27,6 @@ struct BattleHex; | ||||
| class JsonNode; | ||||
| class CRandomGenerator; | ||||
| class JsonSerializeFormat; | ||||
| class Terrain; | ||||
| class BattleField; | ||||
|  | ||||
| struct SSpecialtyInfo | ||||
|   | ||||
| @@ -81,12 +81,30 @@ set(lib_SRCS | ||||
| 		registerTypes/TypesLobbyPacks.cpp | ||||
| 		registerTypes/TypesServerPacks.cpp | ||||
|  | ||||
| 		rmg/RmgArea.cpp | ||||
| 		rmg/RmgObject.cpp | ||||
| 		rmg/RmgPath.cpp | ||||
| 		rmg/CMapGenerator.cpp | ||||
| 		rmg/CMapGenOptions.cpp | ||||
| 		rmg/CRmgTemplate.cpp | ||||
| 		rmg/CRmgTemplateStorage.cpp | ||||
| 		rmg/CRmgTemplateZone.cpp | ||||
| 		rmg/CZonePlacer.cpp | ||||
| 		rmg/TileInfo.cpp | ||||
| 		rmg/Zone.cpp | ||||
| 		rmg/Functions.cpp | ||||
| 		rmg/ObjectManager.cpp | ||||
| 		rmg/RoadPlacer.cpp | ||||
| 		rmg/TreasurePlacer.cpp | ||||
| 		rmg/RmgMap.cpp | ||||
| 		rmg/ConnectionsPlacer.cpp | ||||
| 		rmg/WaterAdopter.cpp | ||||
| 		rmg/TownPlacer.cpp | ||||
| 		rmg/WaterProxy.cpp | ||||
| 		rmg/WaterRoutes.cpp | ||||
| 		rmg/RockPlacer.cpp | ||||
| 		rmg/ObstaclePlacer.cpp | ||||
| 		rmg/RiverPlacer.cpp | ||||
| 		rmg/TerrainPainter.cpp | ||||
|  | ||||
| 		serializer/BinaryDeserializer.cpp | ||||
| 		serializer/BinarySerializer.cpp | ||||
| @@ -288,12 +306,30 @@ set(lib_HEADERS | ||||
|  | ||||
| 		registerTypes/RegisterTypes.h | ||||
|  | ||||
| 		rmg/RmgArea.h | ||||
| 		rmg/RmgObject.h | ||||
| 		rmg/RmgPath.h | ||||
| 		rmg/CMapGenerator.h | ||||
| 		rmg/CMapGenOptions.h | ||||
| 		rmg/CRmgTemplate.h | ||||
| 		rmg/CRmgTemplateStorage.h | ||||
| 		rmg/CRmgTemplateZone.h | ||||
| 		rmg/CZonePlacer.h | ||||
| 		rmg/TileInfo.h | ||||
| 		rmg/Zone.h | ||||
| 		rmg/Functions.h | ||||
| 		rmg/ObjectManager.h | ||||
| 		rmg/RoadPlacer.h | ||||
| 		rmg/TreasurePlacer.h | ||||
| 		rmg/RmgMap.h | ||||
| 		rmg/ConnectionsPlacer.h | ||||
| 		rmg/WaterAdopter.h | ||||
| 		rmg/TownPlacer.h | ||||
| 		rmg/WaterProxy.h | ||||
| 		rmg/WaterRoutes.h | ||||
| 		rmg/RockPlacer.h | ||||
| 		rmg/ObstaclePlacer.h | ||||
| 		rmg/RiverPlacer.h | ||||
| 		rmg/TerrainPainter.h | ||||
| 		rmg/float3.h | ||||
|  | ||||
| 		serializer/BinaryDeserializer.h | ||||
| @@ -424,8 +460,7 @@ endif() | ||||
|  | ||||
| vcmi_set_output_dir(vcmi "") | ||||
|  | ||||
| set_target_properties(vcmi PROPERTIES ${PCH_PROPERTIES}) | ||||
| cotire(vcmi) | ||||
| enable_pch(vcmi) | ||||
|  | ||||
| # We want to deploy assets into build directory for easier debugging without install | ||||
| add_custom_command(TARGET vcmi POST_BUILD | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "Terrain.h" | ||||
| #include "VCMI_Lib.h" | ||||
| #include "CModHandler.h" | ||||
| @@ -78,8 +79,26 @@ Terrain::Manager::Manager() | ||||
| 				auto s = terr.second["type"].String(); | ||||
| 				if(s == "LAND") info.type = Terrain::Info::Type::Land; | ||||
| 				if(s == "WATER") info.type = Terrain::Info::Type::Water; | ||||
| 				if(s == "SUB") info.type = Terrain::Info::Type::Subterranean; | ||||
| 				if(s == "ROCK") info.type = Terrain::Info::Type::Rock; | ||||
| 				if(s == "SUB") info.type = Terrain::Info::Type::Subterranean; | ||||
| 			} | ||||
| 			 | ||||
| 			if(terr.second["rockTerrain"].isNull()) | ||||
| 			{ | ||||
| 				info.rockTerrain = "rock"; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				info.rockTerrain = terr.second["rockTerrain"].String(); | ||||
| 			} | ||||
| 			 | ||||
| 			if(terr.second["river"].isNull()) | ||||
| 			{ | ||||
| 				info.river = RIVER_NAMES[0]; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				info.river = terr.second["river"].String(); | ||||
| 			} | ||||
| 			 | ||||
| 			if(terr.second["horseSoundId"].isNull()) | ||||
| @@ -114,6 +133,14 @@ Terrain::Manager::Manager() | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			if(!terr.second["prohibitTransitions"].isNull()) | ||||
| 			{ | ||||
| 				for(auto & t : terr.second["prohibitTransitions"].Vector()) | ||||
| 				{ | ||||
| 					info.prohibitTransitions.emplace_back(t.String()); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			info.transitionRequired = false; | ||||
| 			if(!terr.second["transitionRequired"].isNull()) | ||||
| 			{ | ||||
| @@ -126,7 +153,7 @@ Terrain::Manager::Manager() | ||||
| 				info.terrainViewPatterns = terr.second["terrainViewPatterns"].String(); | ||||
| 			} | ||||
| 			 | ||||
| 			terrainInfo[Terrain(terr.first)] = info; | ||||
| 			terrainInfo[terr.first] = info; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -139,15 +166,15 @@ Terrain::Manager & Terrain::Manager::get() | ||||
|  | ||||
| std::vector<Terrain> Terrain::Manager::terrains() | ||||
| { | ||||
| 	std::vector<Terrain> _terrains; | ||||
| 	std::set<Terrain> _terrains; //have to use std::set to have ordered container. Othervise de-sync is possible | ||||
| 	for(const auto & info : Terrain::Manager::get().terrainInfo) | ||||
| 		_terrains.push_back(info.first); | ||||
| 	return _terrains; | ||||
| 		_terrains.insert(info.first); | ||||
| 	return std::vector<Terrain>(_terrains.begin(), _terrains.end()); | ||||
| } | ||||
|  | ||||
| const Terrain::Info & Terrain::Manager::getInfo(const Terrain & terrain) | ||||
| { | ||||
| 	return Terrain::Manager::get().terrainInfo.at(terrain); | ||||
| 	return Terrain::Manager::get().terrainInfo.at(static_cast<std::string>(terrain)); | ||||
| } | ||||
|  | ||||
| std::ostream & operator<<(std::ostream & os, const Terrain terrainType) | ||||
|   | ||||
| @@ -37,9 +37,12 @@ public: | ||||
| 		std::string terrainText; | ||||
| 		std::string typeCode; | ||||
| 		std::string terrainViewPatterns; | ||||
| 		std::string rockTerrain; | ||||
| 		std::string river; | ||||
| 		int horseSoundId; | ||||
| 		Type type; | ||||
| 		std::vector<std::string> battleFields; | ||||
| 		std::vector<Terrain> prohibitTransitions; | ||||
| 	}; | ||||
| 	 | ||||
| 	class DLL_LINKAGE Manager | ||||
| @@ -52,7 +55,7 @@ public: | ||||
| 		static Manager & get(); | ||||
| 		Manager(); | ||||
| 		 | ||||
| 		std::map<Terrain, Info> terrainInfo; | ||||
| 		std::unordered_map<std::string, Info> terrainInfo; | ||||
| 	}; | ||||
| 	 | ||||
| 	/*enum EETerrainType | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| #include "CDrawRoadsOperation.h" | ||||
| #include "CMap.h" | ||||
|  | ||||
| const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::patterns = | ||||
| const std::vector<CDrawLinesOperation::LinePattern> CDrawLinesOperation::patterns = | ||||
| { | ||||
| 	//single tile. fall-back pattern | ||||
| 	{ | ||||
| @@ -147,21 +147,31 @@ static bool ruleIsAny(const std::string & rule) | ||||
| } | ||||
| #endif | ||||
|  | ||||
| ///CDrawRoadsOperation | ||||
| CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, const std::string & roadType, CRandomGenerator * gen): | ||||
| 	CMapOperation(map),terrainSel(terrainSel), roadType(roadType), gen(gen) | ||||
| ///CDrawLinesOperation | ||||
| CDrawLinesOperation::CDrawLinesOperation(CMap * map, const CTerrainSelection & terrainSel, CRandomGenerator * gen): | ||||
| 	CMapOperation(map), terrainSel(terrainSel), gen(gen) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void CDrawRoadsOperation::execute() | ||||
| ///CDrawRoadsOperation | ||||
| CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, const std::string & roadType, CRandomGenerator * gen): | ||||
| 	CDrawLinesOperation(map, terrainSel, gen), roadType(roadType) | ||||
| { | ||||
| } | ||||
|  | ||||
| ///CDrawRiversOperation | ||||
| CDrawRiversOperation::CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, const std::string & riverType, CRandomGenerator * gen): | ||||
| 	CDrawLinesOperation(map, terrainSel, gen), riverType(riverType) | ||||
| { | ||||
| } | ||||
|  | ||||
| void CDrawLinesOperation::execute() | ||||
| { | ||||
| 	std::set<int3> invalidated; | ||||
|  | ||||
| 	for(const auto & pos : terrainSel.getSelectedItems()) | ||||
| 	{ | ||||
| 		auto & tile = map->getTile(pos); | ||||
| 		tile.roadType = roadType; | ||||
| 		executeTile(map->getTile(pos)); | ||||
|  | ||||
| 		auto rect = extendTileAroundSafely(pos); | ||||
| 		rect.forEach([&invalidated](const int3 & pos) | ||||
| @@ -173,28 +183,17 @@ void CDrawRoadsOperation::execute() | ||||
| 	updateTiles(invalidated); | ||||
| } | ||||
|  | ||||
| void CDrawRoadsOperation::undo() | ||||
| void CDrawLinesOperation::undo() | ||||
| { | ||||
|   //TODO | ||||
| } | ||||
|  | ||||
| void CDrawRoadsOperation::redo() | ||||
| void CDrawLinesOperation::redo() | ||||
| { | ||||
|   //TODO | ||||
| } | ||||
|  | ||||
| std::string CDrawRoadsOperation::getLabel() const | ||||
| { | ||||
| 	return "Draw Roads"; | ||||
| } | ||||
|  | ||||
| bool CDrawRoadsOperation::canApplyPattern(const RoadPattern & pattern) const | ||||
| { | ||||
| 	//TODO: this method should be virtual for river support | ||||
| 	return pattern.roadMapping.first >= 0; | ||||
| } | ||||
|  | ||||
| void CDrawRoadsOperation::flipPattern(RoadPattern& pattern, int flip) const | ||||
| void CDrawLinesOperation::flipPattern(LinePattern& pattern, int flip) const | ||||
| { | ||||
| 	//todo: use cashing here and also in terrain patterns | ||||
|  | ||||
| @@ -222,13 +221,7 @@ void CDrawRoadsOperation::flipPattern(RoadPattern& pattern, int flip) const | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| bool CDrawRoadsOperation::needUpdateTile(const TerrainTile & tile) const | ||||
| { | ||||
| 	return tile.roadType != ROAD_NAMES[0]; //TODO: this method should be virtual for river support | ||||
| } | ||||
|  | ||||
| void CDrawRoadsOperation::updateTiles(std::set<int3> & invalidated) | ||||
| void CDrawLinesOperation::updateTiles(std::set<int3> & invalidated) | ||||
| { | ||||
| 	for(int3 coord : invalidated) | ||||
| 	{ | ||||
| @@ -259,25 +252,7 @@ void CDrawRoadsOperation::updateTiles(std::set<int3> & invalidated) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool CDrawRoadsOperation::tileHasSomething(const int3& pos) const | ||||
| { | ||||
| //TODO: this method should be virtual for river support | ||||
|  | ||||
|    return map->getTile(pos).roadType != ROAD_NAMES[0]; | ||||
| } | ||||
|  | ||||
|  | ||||
| void CDrawRoadsOperation::updateTile(TerrainTile & tile, const RoadPattern & pattern, const int flip) | ||||
| { | ||||
|   //TODO: this method should be virtual for river support | ||||
|  | ||||
| 	const std::pair<int, int> & mapping  = pattern.roadMapping; | ||||
|  | ||||
| 	tile.roadDir = gen->nextInt(mapping.first, mapping.second); | ||||
| 	tile.extTileFlags = (tile.extTileFlags & 0xCF) | (flip << 4); | ||||
| } | ||||
|  | ||||
| CDrawRoadsOperation::ValidationResult CDrawRoadsOperation::validateTile(const RoadPattern & pattern, const int3 & pos) | ||||
| CDrawLinesOperation::ValidationResult CDrawLinesOperation::validateTile(const LinePattern & pattern, const int3 & pos) | ||||
| { | ||||
| 	ValidationResult result(false); | ||||
|  | ||||
| @@ -294,7 +269,7 @@ CDrawRoadsOperation::ValidationResult CDrawRoadsOperation::validateTile(const Ro | ||||
| 		if((flip == FLIP_PATTERN_VERTICAL) && !(pattern.hasVFlip)) | ||||
| 			continue; | ||||
|  | ||||
| 		RoadPattern flipped = pattern; | ||||
| 		LinePattern flipped = pattern; | ||||
|  | ||||
| 		flipPattern(flipped, flip); | ||||
|  | ||||
| @@ -344,3 +319,69 @@ CDrawRoadsOperation::ValidationResult CDrawRoadsOperation::validateTile(const Ro | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| std::string CDrawRoadsOperation::getLabel() const | ||||
| { | ||||
| 	return "Draw Roads"; | ||||
| } | ||||
|  | ||||
| std::string CDrawRiversOperation::getLabel() const | ||||
| { | ||||
| 	return "Draw Rivers"; | ||||
| } | ||||
|  | ||||
| void CDrawRoadsOperation::executeTile(TerrainTile & tile) | ||||
| { | ||||
| 	tile.roadType = roadType; | ||||
| } | ||||
|  | ||||
| void CDrawRiversOperation::executeTile(TerrainTile & tile) | ||||
| { | ||||
| 	tile.riverType = riverType; | ||||
| } | ||||
|  | ||||
| bool CDrawRoadsOperation::canApplyPattern(const LinePattern & pattern) const | ||||
| { | ||||
| 	return pattern.roadMapping.first >= 0; | ||||
| } | ||||
|  | ||||
| bool CDrawRiversOperation::canApplyPattern(const LinePattern & pattern) const | ||||
| { | ||||
| 	return pattern.riverMapping.first >= 0; | ||||
| } | ||||
|  | ||||
| bool CDrawRoadsOperation::needUpdateTile(const TerrainTile & tile) const | ||||
| { | ||||
| 	return tile.roadType != ROAD_NAMES[0]; | ||||
| } | ||||
|  | ||||
| bool CDrawRiversOperation::needUpdateTile(const TerrainTile & tile) const | ||||
| { | ||||
| 	return tile.riverType != RIVER_NAMES[0]; | ||||
| } | ||||
|  | ||||
| bool CDrawRoadsOperation::tileHasSomething(const int3& pos) const | ||||
| { | ||||
| 	return map->getTile(pos).roadType != ROAD_NAMES[0]; | ||||
| } | ||||
|  | ||||
| bool CDrawRiversOperation::tileHasSomething(const int3& pos) const | ||||
| { | ||||
| 	return map->getTile(pos).riverType != RIVER_NAMES[0]; | ||||
| } | ||||
|  | ||||
| void CDrawRoadsOperation::updateTile(TerrainTile & tile, const LinePattern & pattern, const int flip) | ||||
| { | ||||
| 	const std::pair<int, int> & mapping  = pattern.roadMapping; | ||||
|  | ||||
| 	tile.roadDir = gen->nextInt(mapping.first, mapping.second); | ||||
| 	tile.extTileFlags = (tile.extTileFlags & 0b11001111) | (flip << 4); | ||||
| } | ||||
|  | ||||
| void CDrawRiversOperation::updateTile(TerrainTile & tile, const LinePattern & pattern, const int flip) | ||||
| { | ||||
| 	const std::pair<int, int> & mapping  = pattern.riverMapping; | ||||
| 	 | ||||
| 	tile.riverDir = gen->nextInt(mapping.first, mapping.second); | ||||
| 	tile.extTileFlags = (tile.extTileFlags & 0b00111111) | (flip << 2); | ||||
| } | ||||
|   | ||||
| @@ -15,17 +15,16 @@ | ||||
|  | ||||
| struct TerrainTile; | ||||
|  | ||||
| class CDrawRoadsOperation : public CMapOperation | ||||
| class CDrawLinesOperation : public CMapOperation | ||||
| { | ||||
| public: | ||||
| 	CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, const std::string & roadType, CRandomGenerator * gen); | ||||
| 	void execute() override; | ||||
| 	void undo() override; | ||||
| 	void redo() override; | ||||
| 	std::string getLabel() const override;	 | ||||
| private: | ||||
| 	 | ||||
| 	struct RoadPattern | ||||
| protected: | ||||
| 	 | ||||
| 	struct LinePattern | ||||
| 	{ | ||||
| 		std::string data[9]; | ||||
| 		std::pair<int, int> roadMapping, riverMapping; | ||||
| @@ -39,20 +38,56 @@ private: | ||||
| 		int flip; | ||||
| 	}; | ||||
| 	 | ||||
| 	static const std::vector<RoadPattern> patterns; | ||||
| 	CDrawLinesOperation(CMap * map, const CTerrainSelection & terrainSel, CRandomGenerator * gen); | ||||
| 	 | ||||
| 	void flipPattern(RoadPattern & pattern, int flip) const; | ||||
| 	virtual void executeTile(TerrainTile & tile) = 0; | ||||
| 	virtual bool canApplyPattern(const CDrawLinesOperation::LinePattern & pattern) const = 0; | ||||
| 	virtual bool needUpdateTile(const TerrainTile & tile) const = 0; | ||||
| 	virtual bool tileHasSomething(const int3 & pos) const = 0; | ||||
| 	virtual void updateTile(TerrainTile & tile, const CDrawLinesOperation::LinePattern & pattern, const int flip) = 0; | ||||
| 	 | ||||
| 	static const std::vector<LinePattern> patterns; | ||||
| 	 | ||||
| 	void flipPattern(LinePattern & pattern, int flip) const; | ||||
| 	 | ||||
| 	void updateTiles(std::set<int3> & invalidated); | ||||
| 	 | ||||
| 	ValidationResult validateTile(const RoadPattern & pattern, const int3 & pos); | ||||
| 	void updateTile(TerrainTile & tile, const RoadPattern & pattern, const int flip); | ||||
| 	 | ||||
| 	bool canApplyPattern(const RoadPattern & pattern) const; | ||||
| 	bool needUpdateTile(const TerrainTile & tile) const; | ||||
| 	bool tileHasSomething(const int3 & pos) const; | ||||
| 	ValidationResult validateTile(const LinePattern & pattern, const int3 & pos); | ||||
| 	 | ||||
| 	CTerrainSelection terrainSel; | ||||
| 	std::string roadType; | ||||
| 	CRandomGenerator * gen;	 | ||||
| 	CRandomGenerator * gen; | ||||
| }; | ||||
|  | ||||
| class CDrawRoadsOperation : public CDrawLinesOperation | ||||
| { | ||||
| public: | ||||
| 	CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, const std::string & roadType, CRandomGenerator * gen); | ||||
| 	std::string getLabel() const override; | ||||
| 	 | ||||
| protected: | ||||
| 	void executeTile(TerrainTile & tile) override; | ||||
| 	bool canApplyPattern(const CDrawLinesOperation::LinePattern & pattern) const override; | ||||
| 	bool needUpdateTile(const TerrainTile & tile) const override; | ||||
| 	bool tileHasSomething(const int3 & pos) const override; | ||||
| 	void updateTile(TerrainTile & tile, const CDrawLinesOperation::LinePattern & pattern, const int flip) override; | ||||
| 	 | ||||
| private: | ||||
| 	std::string roadType; | ||||
| }; | ||||
|  | ||||
| class CDrawRiversOperation : public CDrawLinesOperation | ||||
| { | ||||
| public: | ||||
| 	CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, const std::string & roadType, CRandomGenerator * gen); | ||||
| 	std::string getLabel() const override; | ||||
| 	 | ||||
| protected: | ||||
| 	void executeTile(TerrainTile & tile) override; | ||||
| 	bool canApplyPattern(const CDrawLinesOperation::LinePattern & pattern) const override; | ||||
| 	bool needUpdateTile(const TerrainTile & tile) const override; | ||||
| 	bool tileHasSomething(const int3 & pos) const override; | ||||
| 	void updateTile(TerrainTile & tile, const CDrawLinesOperation::LinePattern & pattern, const int flip) override; | ||||
| 	 | ||||
| private: | ||||
| 	std::string riverType; | ||||
| }; | ||||
|   | ||||
| @@ -110,7 +110,7 @@ void CTerrainSelection::deselectRange(const MapRect & rect) | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| void CTerrainSelection::setSelection(std::vector<int3> & vec) | ||||
| void CTerrainSelection::setSelection(const std::vector<int3> & vec) | ||||
| { | ||||
| 	for (auto pos : vec) | ||||
| 		this->select(pos); | ||||
| @@ -255,6 +255,13 @@ void CMapEditManager::drawRoad(const std::string & roadType, CRandomGenerator* g | ||||
| 	terrainSel.clearSelection(); | ||||
| } | ||||
|  | ||||
| void CMapEditManager::drawRiver(const std::string & riverType, CRandomGenerator* gen) | ||||
| { | ||||
| 	execute(make_unique<CDrawRiversOperation>(map, terrainSel, riverType, gen ? gen : &(this->gen))); | ||||
| 	terrainSel.clearSelection(); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| void CMapEditManager::insertObject(CGObjectInstance * obj) | ||||
| { | ||||
| @@ -818,7 +825,7 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi | ||||
| 		else | ||||
| 		{ | ||||
| 			terType = map->getTile(currentPos).terType; | ||||
| 			if(terType != centerTerType) | ||||
| 			if(terType != centerTerType && (terType.isPassable() || centerTerType.isPassable())) | ||||
| 			{ | ||||
| 				isAlien = true; | ||||
| 			} | ||||
|   | ||||
| @@ -93,7 +93,7 @@ public: | ||||
| 	void deselectRange(const MapRect & rect) override; | ||||
| 	void selectAll() override; | ||||
| 	void clearSelection() override; | ||||
| 	void setSelection(std::vector<int3> & vec); | ||||
| 	void setSelection(const std::vector<int3> & vec); | ||||
| }; | ||||
|  | ||||
| /// Selection class to select objects. | ||||
| @@ -173,6 +173,9 @@ public: | ||||
|  | ||||
| 	/// Draws roads at the current terrain selection. The selection will be cleared automatically. | ||||
| 	void drawRoad(const std::string & roadType, CRandomGenerator * gen = nullptr); | ||||
| 	 | ||||
| 	/// Draws rivers at the current terrain selection. The selection will be cleared automatically. | ||||
| 	void drawRiver(const std::string & riverType, CRandomGenerator * gen = nullptr); | ||||
|  | ||||
| 	void insertObject(CGObjectInstance * obj); | ||||
|  | ||||
|   | ||||
| @@ -208,6 +208,7 @@ void CMapGenOptions::setMapTemplate(const CRmgTemplate * value) | ||||
|  | ||||
| void CMapGenOptions::finalize(CRandomGenerator & rand) | ||||
| { | ||||
| 	logGlobal->info("RMG map: %dx%d, %s underground", getWidth(), getHeight(), getHasTwoLevels() ? "WITH" : "NO"); | ||||
| 	logGlobal->info("RMG settings: players %d, teams %d, computer players %d, computer teams %d, water %d, monsters %d", | ||||
| 		static_cast<int>(getPlayerCount()), static_cast<int>(getTeamCount()), static_cast<int>(getCompOnlyPlayerCount()), | ||||
| 		static_cast<int>(getCompOnlyTeamCount()), static_cast<int>(getWaterContent()), static_cast<int>(getMonsterStrength())); | ||||
| @@ -217,6 +218,8 @@ void CMapGenOptions::finalize(CRandomGenerator & rand) | ||||
| 		mapTemplate = getPossibleTemplate(rand); | ||||
| 	} | ||||
| 	assert(mapTemplate); | ||||
| 	 | ||||
| 	logGlobal->info("RMG template name: %s", mapTemplate->getName()); | ||||
|  | ||||
| 	if (getPlayerCount() == RANDOM_SIZE) | ||||
| 	{ | ||||
|   | ||||
| @@ -18,67 +18,32 @@ | ||||
| #include "../StringConstants.h" | ||||
| #include "../filesystem/Filesystem.h" | ||||
| #include "CZonePlacer.h" | ||||
| #include "CRmgTemplateZone.h" | ||||
| #include "../mapObjects/CObjectClassesHandler.h" | ||||
|  | ||||
| static const int3 dirs4[] = {int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0)}; | ||||
| static const int3 dirsDiagonal[] = { int3(1,1,0),int3(1,-1,0),int3(-1,1,0),int3(-1,-1,0) }; | ||||
|  | ||||
| void CMapGenerator::foreach_neighbour(const int3 &pos, std::function<void(int3& pos)> foo) | ||||
| { | ||||
| 	for(const int3 &dir : int3::getDirs()) | ||||
| 	{ | ||||
| 		int3 n = pos + dir; | ||||
| 		/*important notice: perform any translation before this function is called, | ||||
| 		so the actual map position is checked*/ | ||||
| 		if(map->isInTheMap(n)) | ||||
| 			foo(n); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CMapGenerator::foreachDirectNeighbour(const int3& pos, std::function<void(int3& pos)> foo) | ||||
| { | ||||
| 	for(const int3 &dir : dirs4) | ||||
| 	{ | ||||
| 		int3 n = pos + dir; | ||||
| 		if(map->isInTheMap(n)) | ||||
| 			foo(n); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CMapGenerator::foreachDiagonalNeighbour(const int3& pos, std::function<void(int3& pos)> foo) | ||||
| { | ||||
| 	for (const int3 &dir : dirsDiagonal) | ||||
| 	{ | ||||
| 		int3 n = pos + dir; | ||||
| 		if (map->isInTheMap(n)) | ||||
| 			foo(n); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| #include "TileInfo.h" | ||||
| #include "Zone.h" | ||||
| #include "Functions.h" | ||||
| #include "RmgMap.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "TreasurePlacer.h" | ||||
| #include "RoadPlacer.h" | ||||
|  | ||||
| CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed) : | ||||
| 	mapGenOptions(mapGenOptions), randomSeed(RandomSeed), | ||||
| 	zonesTotal(0), tiles(nullptr), prisonsRemaining(0), | ||||
|     monolithIndex(0) | ||||
| 	prisonsRemaining(0), monolithIndex(0) | ||||
| { | ||||
| 	loadConfig(); | ||||
| 	rand.setSeed(this->randomSeed); | ||||
| 	mapGenOptions.finalize(rand); | ||||
| 	map = std::make_unique<RmgMap>(mapGenOptions); | ||||
| } | ||||
|  | ||||
| int CMapGenerator::getRandomSeed() const | ||||
| { | ||||
| 	return randomSeed; | ||||
| } | ||||
|  | ||||
| void CMapGenerator::loadConfig() | ||||
| { | ||||
| 	static const std::map<std::string, Res::ERes> resMap | ||||
| 	{ | ||||
| 		{"wood", Res::ERes::WOOD}, | ||||
| 		{"ore", Res::ERes::ORE}, | ||||
| 		{"gems", Res::ERes::GEMS}, | ||||
| 		{"crystal", Res::ERes::CRYSTAL}, | ||||
| 		{"mercury", Res::ERes::MERCURY}, | ||||
| 		{"sulfur", Res::ERes::SULFUR}, | ||||
| 		{"gold", Res::ERes::GOLD}, | ||||
| 	}; | ||||
| 	static const ResourceID path("config/randomMap.json"); | ||||
| 	JsonNode randomMapJson(path); | ||||
| 	for(auto& s : randomMapJson["terrain"]["undergroundAllow"].Vector()) | ||||
| @@ -96,10 +61,6 @@ void CMapGenerator::loadConfig() | ||||
| 	{ | ||||
| 		config.waterTreasure.emplace_back(treasure["min"].Integer(), treasure["max"].Integer(), treasure["density"].Integer()); | ||||
| 	} | ||||
| 	for(auto& s : resMap) | ||||
| 	{ | ||||
| 		config.mineValues[s.second] = randomMapJson["mines"]["value"][s.first].Integer(); | ||||
| 	} | ||||
| 	config.mineExtraResources = randomMapJson["mines"]["extraResourcesLimit"].Integer(); | ||||
| 	config.minGuardStrength = randomMapJson["minGuardStrength"].Integer(); | ||||
| 	config.defaultRoadType = randomMapJson["defaultRoadType"].String(); | ||||
| @@ -128,49 +89,19 @@ const CMapGenerator::Config & CMapGenerator::getConfig() const | ||||
| 	return config; | ||||
| } | ||||
|  | ||||
| void CMapGenerator::initTiles() | ||||
| { | ||||
| 	map->initTerrain(); | ||||
|  | ||||
| 	int width = map->width; | ||||
| 	int height = map->height; | ||||
|  | ||||
| 	int level = map->twoLevel ? 2 : 1; | ||||
| 	tiles = new CTileInfo**[width]; | ||||
| 	for (int i = 0; i < width; ++i) | ||||
| 	{ | ||||
| 		tiles[i] = new CTileInfo*[height]; | ||||
| 		for (int j = 0; j < height; ++j) | ||||
| 		{ | ||||
| 			tiles[i][j] = new CTileInfo[level]; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	zoneColouring.resize(boost::extents[map->twoLevel ? 2 : 1][map->width][map->height]); | ||||
| } | ||||
|  | ||||
| CMapGenerator::~CMapGenerator() | ||||
| { | ||||
| 	if (tiles) | ||||
| 	{ | ||||
| 		int width = mapGenOptions.getWidth(); | ||||
| 		int height = mapGenOptions.getHeight(); | ||||
| 		for (int i=0; i < width; i++) | ||||
| 		{ | ||||
| 			for(int j=0; j < height; j++) | ||||
| 			{ | ||||
| 				delete [] tiles[i][j]; | ||||
| 			} | ||||
| 			delete [] tiles[i]; | ||||
| 		} | ||||
| 		delete [] tiles; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| const CMapGenOptions& CMapGenerator::getMapGenOptions() const | ||||
| { | ||||
| 	return mapGenOptions; | ||||
| } | ||||
|  | ||||
| void CMapGenerator::initPrisonsRemaining() | ||||
| { | ||||
| 	prisonsRemaining = 0; | ||||
| 	for (auto isAllowed : map->allowedHeroes) | ||||
| 	for (auto isAllowed : map->map().allowedHeroes) | ||||
| 	{ | ||||
| 		if (isAllowed) | ||||
| 			prisonsRemaining++; | ||||
| @@ -187,40 +118,26 @@ void CMapGenerator::initQuestArtsRemaining() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| const CMapGenOptions& CMapGenerator::getMapGenOptions() const | ||||
| { | ||||
| 	return mapGenOptions; | ||||
| } | ||||
|  | ||||
| CMapEditManager* CMapGenerator::getEditManager() const | ||||
| { | ||||
| 	if(!map) | ||||
| 		return nullptr; | ||||
| 	return map->getEditManager(); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<CMap> CMapGenerator::generate() | ||||
| { | ||||
| 	map = make_unique<CMap>(); | ||||
| 	try | ||||
| 	{ | ||||
| 		map->getEditManager()->getUndoManager().setUndoRedoLimit(0); | ||||
| 		//FIXME:  somehow mapGenOption is nullptr at this point :? | ||||
| 		addHeaderInfo(); | ||||
| 		initTiles(); | ||||
| 		map->initTiles(*this); | ||||
| 		initPrisonsRemaining(); | ||||
| 		initQuestArtsRemaining(); | ||||
| 		genZones(); | ||||
| 		map->calculateGuardingGreaturePositions(); //clear map so that all tiles are unguarded | ||||
| 		map->map().calculateGuardingGreaturePositions(); //clear map so that all tiles are unguarded | ||||
| 		map->addModificators(); | ||||
| 		fillZones(); | ||||
| 		//updated guarded tiles will be calculated in CGameState::initMapObjects() | ||||
| 		zones.clear(); | ||||
| 		map->getZones().clear(); | ||||
| 	} | ||||
| 	catch (rmgException &e) | ||||
| 	{ | ||||
| 		logGlobal->error("Random map generation received exception: %s", e.what()); | ||||
| 	} | ||||
| 	return std::move(map); | ||||
| 	return std::move(map->mapInstance); | ||||
| } | ||||
|  | ||||
| std::string CMapGenerator::getMapDescription() const | ||||
| @@ -239,7 +156,7 @@ std::string CMapGenerator::getMapDescription() const | ||||
|     std::stringstream ss; | ||||
|     ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, Random seed was %d, size %dx%d") + | ||||
|         ", levels %s, players %d, computers %d, water %s, monster %s, VCMI map") % mapTemplate->getName() % | ||||
| 		randomSeed % map->width % map->height % (map->twoLevel ? "2" : "1") % static_cast<int>(mapGenOptions.getPlayerCount()) % | ||||
| 		randomSeed % map->map().width % map->map().height % (map->map().twoLevel ? "2" : "1") % static_cast<int>(mapGenOptions.getPlayerCount()) % | ||||
| 		static_cast<int>(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] % | ||||
| 		monsterStrengthStr[monsterStrengthIndex]); | ||||
|  | ||||
| @@ -329,626 +246,107 @@ void CMapGenerator::addPlayerInfo() | ||||
| 		auto itTeam = RandomGeneratorUtil::nextItem(teamNumbers[j], rand); | ||||
| 		player.team = TeamID(*itTeam); | ||||
| 		teamNumbers[j].erase(itTeam); | ||||
| 		map->players[pSettings.getColor().getNum()] = player; | ||||
| 		map->map().players[pSettings.getColor().getNum()] = player; | ||||
| 	} | ||||
|  | ||||
| 	map->howManyTeams = (mapGenOptions.getTeamCount() == 0 ? mapGenOptions.getPlayerCount() : mapGenOptions.getTeamCount()) | ||||
| 	map->map().howManyTeams = (mapGenOptions.getTeamCount() == 0 ? mapGenOptions.getPlayerCount() : mapGenOptions.getTeamCount()) | ||||
| 			+ (mapGenOptions.getCompOnlyTeamCount() == 0 ? mapGenOptions.getCompOnlyPlayerCount() : mapGenOptions.getCompOnlyTeamCount()); | ||||
| } | ||||
|  | ||||
| void CMapGenerator::genZones() | ||||
| { | ||||
| 	getEditManager()->clearTerrain(&rand); | ||||
| 	getEditManager()->getTerrainSelection().selectRange(MapRect(int3(0, 0, 0), mapGenOptions.getWidth(), mapGenOptions.getHeight())); | ||||
| 	getEditManager()->drawTerrain(Terrain("grass"), &rand); | ||||
|  | ||||
| 	auto tmpl = mapGenOptions.getMapTemplate(); | ||||
| 	zones.clear(); | ||||
| 	for(const auto & option : tmpl->getZones()) | ||||
| 	{ | ||||
| 		auto zone = std::make_shared<CRmgTemplateZone>(this); | ||||
| 		zone->setOptions(*option.second.get()); | ||||
| 		zones[zone->getId()] = zone; | ||||
| 	} | ||||
|  | ||||
| 	CZonePlacer placer(this); | ||||
| 	CZonePlacer placer(*map); | ||||
| 	placer.placeZones(&rand); | ||||
| 	placer.assignZones(); | ||||
| 	 | ||||
| 	//add special zone for water | ||||
| 	zoneWater.first = zones.size() + 1; | ||||
| 	zoneWater.second = std::make_shared<CRmgTemplateZone>(this); | ||||
| 	{ | ||||
| 		rmg::ZoneOptions options; | ||||
| 		options.setId(zoneWater.first); | ||||
| 		options.setType(ETemplateZoneType::WATER); | ||||
| 		zoneWater.second->setOptions(options); | ||||
| 	} | ||||
| 	placer.assignZones(&rand); | ||||
|  | ||||
| 	logGlobal->info("Zones generated successfully"); | ||||
| } | ||||
|  | ||||
| void CMapGenerator::createWaterTreasures() | ||||
| { | ||||
| 	if(!getZoneWater()) | ||||
| 		return; | ||||
| 	 | ||||
| 	//add treasures on water | ||||
| 	for(auto & treasureInfo : getConfig().waterTreasure) | ||||
| 	{ | ||||
| 		getZoneWater().second->addTreasureInfo(treasureInfo); | ||||
| 		getZoneWater()->addTreasureInfo(treasureInfo); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CMapGenerator::prepareWaterTiles() | ||||
| { | ||||
| 	for(auto & t : zoneWater.second->getTileInfo()) | ||||
| 		if(shouldBeBlocked(t)) | ||||
| 			setOccupied(t, ETileType::POSSIBLE); | ||||
| } | ||||
|  | ||||
| void CMapGenerator::fillZones() | ||||
| { | ||||
| 	//init native town count with 0 | ||||
| 	for (auto faction : VLC->townh->getAllowedFactions()) | ||||
| 		zonesPerFaction[faction] = 0; | ||||
|  | ||||
| 	findZonesForQuestArts(); | ||||
| 	createWaterTreasures(); | ||||
|  | ||||
| 	logGlobal->info("Started filling zones"); | ||||
|  | ||||
| 	//we need info about all town types to evaluate dwellings and pandoras with creatures properly | ||||
| 	//place main town in the middle | ||||
| 	for(auto it : zones) | ||||
| 		it.second->initTownType(); | ||||
| 	 | ||||
| 	//make sure there are some free tiles in the zone | ||||
| 	for(auto it : zones) | ||||
| 		it.second->initFreeTiles(); | ||||
| 	 | ||||
| 	for(auto it : zones) | ||||
| 		it.second->createBorder(); //once direct connections are done | ||||
| 	 | ||||
| #ifdef _BETA | ||||
| 	dump(false); | ||||
| #endif | ||||
| 	 | ||||
| 	for(auto it : zones) | ||||
| 		it.second->createWater(getMapGenOptions().getWaterContent()); | ||||
| 	 | ||||
| 	zoneWater.second->waterInitFreeTiles(); | ||||
| 	 | ||||
| #ifdef _BETA | ||||
| 	dump(false); | ||||
| #endif | ||||
|  | ||||
| 	createDirectConnections(); //direct | ||||
| 	createConnections2(); //subterranean gates and monoliths | ||||
| 	 | ||||
| 	for(auto it : zones) | ||||
| 		zoneWater.second->waterConnection(*it.second); | ||||
| 	 | ||||
| 	createWaterTreasures(); | ||||
| 	zoneWater.second->initFreeTiles(); | ||||
| 	zoneWater.second->fill(); | ||||
|  | ||||
| 	std::vector<std::shared_ptr<CRmgTemplateZone>> treasureZones; | ||||
| 	for(auto it : zones) | ||||
| 	for(auto it : map->getZones()) | ||||
| 	{ | ||||
| 		it.second->fill(); | ||||
| 		it.second->initModificators(); | ||||
| 		it.second->initFreeTiles(); | ||||
| 	} | ||||
|  | ||||
| 	std::vector<std::shared_ptr<Zone>> treasureZones; | ||||
| 	for(auto it : map->getZones()) | ||||
| 	{ | ||||
| 		it.second->processModificators(); | ||||
| 		 | ||||
| 		if (it.second->getType() == ETemplateZoneType::TREASURE) | ||||
| 			treasureZones.push_back(it.second); | ||||
| 	} | ||||
| 	 | ||||
| #ifdef _BETA | ||||
| 	dump(false); | ||||
| #endif | ||||
| 		 | ||||
| 	//set apriopriate free/occupied tiles, including blocked underground rock | ||||
| 	createObstaclesCommon1(); | ||||
| 	//set back original terrain for underground zones | ||||
| 	for(auto it : zones) | ||||
| 		it.second->createObstacles1(); | ||||
| 	 | ||||
| 	createObstaclesCommon2(); | ||||
| 	//place actual obstacles matching zone terrain | ||||
| 	for(auto it : zones) | ||||
| 		it.second->createObstacles2(); | ||||
| 		 | ||||
| 	zoneWater.second->createObstacles2(); | ||||
| 	 | ||||
| #ifdef _BETA | ||||
| 	dump(false); | ||||
| #endif | ||||
|  | ||||
| 	#define PRINT_MAP_BEFORE_ROADS false | ||||
| 	if (PRINT_MAP_BEFORE_ROADS) //enable to debug | ||||
| 	{ | ||||
| 		std::ofstream out("road_debug.txt"); | ||||
| 		int levels = map->twoLevel ? 2 : 1; | ||||
| 		int width = map->width; | ||||
| 		int height = map->height; | ||||
| 		for (int k = 0; k < levels; k++) | ||||
| 		{ | ||||
| 			for (int j = 0; j<height; j++) | ||||
| 			{ | ||||
| 				for (int i = 0; i<width; i++) | ||||
| 				{ | ||||
| 					char t = '?'; | ||||
| 					switch (getTile(int3(i, j, k)).getTileType()) | ||||
| 					{ | ||||
| 					case ETileType::FREE: | ||||
| 						t = ' '; break; | ||||
| 					case ETileType::BLOCKED: | ||||
| 						t = '#'; break; | ||||
| 					case ETileType::POSSIBLE: | ||||
| 						t = '-'; break; | ||||
| 					case ETileType::USED: | ||||
| 						t = 'O'; break; | ||||
| 					} | ||||
|  | ||||
| 					out << t; | ||||
| 				} | ||||
| 				out << std::endl; | ||||
| 			} | ||||
| 			out << std::endl; | ||||
| 		} | ||||
| 		out << std::endl; | ||||
| 	} | ||||
|  | ||||
| 	for(auto it : zones) | ||||
| 	{ | ||||
| 		it.second->connectRoads(); //draw roads after everything else has been placed | ||||
| 	} | ||||
|  | ||||
| 	//find place for Grail | ||||
| 	if(treasureZones.empty()) | ||||
| 	{ | ||||
| 		for(auto it : zones) | ||||
| 			treasureZones.push_back(it.second); | ||||
| 		for(auto it : map->getZones()) | ||||
| 			if(it.second->getType() != ETemplateZoneType::WATER) | ||||
| 				treasureZones.push_back(it.second); | ||||
| 	} | ||||
| 	auto grailZone = *RandomGeneratorUtil::nextItem(treasureZones, rand); | ||||
|  | ||||
| 	map->grailPos = *RandomGeneratorUtil::nextItem(*grailZone->getFreePaths(), rand); | ||||
| 	map->map().grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths().getTiles(), rand); | ||||
|  | ||||
| 	logGlobal->info("Zones filled successfully"); | ||||
| } | ||||
|  | ||||
| void CMapGenerator::createObstaclesCommon1() | ||||
| { | ||||
| 	if(map->twoLevel) //underground | ||||
| 	{ | ||||
| 		//negative approach - create rock tiles first, then make sure all accessible tiles have no rock | ||||
| 		std::vector<int3> rockTiles; | ||||
|  | ||||
| 		for(int x = 0; x < map->width; x++) | ||||
| 		{ | ||||
| 			for(int y = 0; y < map->height; y++) | ||||
| 			{ | ||||
| 				int3 tile(x, y, 1); | ||||
| 				if (shouldBeBlocked(tile)) | ||||
| 				{ | ||||
| 					rockTiles.push_back(tile); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		getEditManager()->getTerrainSelection().setSelection(rockTiles); | ||||
| 		 | ||||
| 		//collect all rock terrain types | ||||
| 		std::vector<Terrain> rockTerrains; | ||||
| 		for(auto & terrain : Terrain::Manager::terrains()) | ||||
| 			if(!terrain.isPassable()) | ||||
| 				rockTerrains.push_back(terrain); | ||||
| 		auto rockTerrain = *RandomGeneratorUtil::nextItem(rockTerrains, rand); | ||||
| 		 | ||||
| 		getEditManager()->drawTerrain(rockTerrain, &rand); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CMapGenerator::createObstaclesCommon2() | ||||
| { | ||||
| 	if(map->twoLevel) | ||||
| 	{ | ||||
| 		//finally mark rock tiles as occupied, spawn no obstacles there | ||||
| 		for(int x = 0; x < map->width; x++) | ||||
| 		{ | ||||
| 			for(int y = 0; y < map->height; y++) | ||||
| 			{ | ||||
| 				int3 tile(x, y, 1); | ||||
| 				if(!map->getTile(tile).terType.isPassable()) | ||||
| 				{ | ||||
| 					setOccupied(tile, ETileType::USED); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//tighten obstacles to improve visuals | ||||
|  | ||||
| 	for (int i = 0; i < 3; ++i) | ||||
| 	{ | ||||
| 		int blockedTiles = 0; | ||||
| 		int freeTiles = 0; | ||||
|  | ||||
| 		for (int z = 0; z < (map->twoLevel ? 2 : 1); z++) | ||||
| 		{ | ||||
| 			for (int x = 0; x < map->width; x++) | ||||
| 			{ | ||||
| 				for (int y = 0; y < map->height; y++) | ||||
| 				{ | ||||
| 					int3 tile(x, y, z); | ||||
| 					if (!isPossible(tile)) //only possible tiles can change | ||||
| 						continue; | ||||
|  | ||||
| 					int blockedNeighbours = 0; | ||||
| 					int freeNeighbours = 0; | ||||
| 					foreach_neighbour(tile, [this, &blockedNeighbours, &freeNeighbours](int3 &pos) | ||||
| 					{ | ||||
| 						if (this->isBlocked(pos)) | ||||
| 							blockedNeighbours++; | ||||
| 						if (this->isFree(pos)) | ||||
| 							freeNeighbours++; | ||||
| 					}); | ||||
| 					if (blockedNeighbours > 4) | ||||
| 					{ | ||||
| 						setOccupied(tile, ETileType::BLOCKED); | ||||
| 						blockedTiles++; | ||||
| 					} | ||||
| 					else if (freeNeighbours > 4) | ||||
| 					{ | ||||
| 						setOccupied(tile, ETileType::FREE); | ||||
| 						freeTiles++; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		logGlobal->trace("Set %d tiles to BLOCKED and %d tiles to FREE", blockedTiles, freeTiles); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CMapGenerator::findZonesForQuestArts() | ||||
| { | ||||
| 	//we want to place arties in zones that were not yet filled (higher index) | ||||
|  | ||||
| 	for (auto connection : mapGenOptions.getMapTemplate()->getConnections()) | ||||
| 	{ | ||||
| 		auto zoneA = zones[connection.getZoneA()]; | ||||
| 		auto zoneB = zones[connection.getZoneB()]; | ||||
| 		auto zoneA = map->getZones()[connection.getZoneA()]; | ||||
| 		auto zoneB = map->getZones()[connection.getZoneB()]; | ||||
|  | ||||
| 		if (zoneA->getId() > zoneB->getId()) | ||||
| 		{ | ||||
| 			zoneB->setQuestArtZone(zoneA); | ||||
| 			if(auto * m = zoneB->getModificator<TreasurePlacer>()) | ||||
| 				zoneB->getModificator<TreasurePlacer>()->setQuestArtZone(zoneA.get()); | ||||
| 		} | ||||
| 		else if (zoneA->getId() < zoneB->getId()) | ||||
| 		{ | ||||
| 			zoneA->setQuestArtZone(zoneB); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CMapGenerator::createDirectConnections() | ||||
| { | ||||
| 	bool waterMode = getMapGenOptions().getWaterContent() != EWaterContent::NONE; | ||||
| 	 | ||||
| 	for (auto connection : mapGenOptions.getMapTemplate()->getConnections()) | ||||
| 	{ | ||||
| 		auto zoneA = zones[connection.getZoneA()]; | ||||
| 		auto zoneB = zones[connection.getZoneB()]; | ||||
|  | ||||
| 		//rearrange tiles in random order | ||||
| 		const auto & tiles = zoneA->getTileInfo(); | ||||
|  | ||||
| 		int3 guardPos(-1,-1,-1); | ||||
|  | ||||
| 		int3 posA = zoneA->getPos(); | ||||
| 		int3 posB = zoneB->getPos(); | ||||
| 		// auto zoneAid = zoneA->getId(); | ||||
| 		auto zoneBid = zoneB->getId(); | ||||
| 		 | ||||
| 		if (posA.z == posB.z) | ||||
| 		{ | ||||
| 			std::vector<int3> middleTiles; | ||||
| 			for (const auto& tile : tiles) | ||||
| 			{ | ||||
| 				if (isUsed(tile) || getZoneID(tile)==zoneWater.first) //tiles may be occupied by towns or water | ||||
| 					continue; | ||||
| 				foreachDirectNeighbour(tile, [tile, &middleTiles, this, zoneBid](int3 & pos) //must be direct since paths also also generated between direct neighbours | ||||
| 				{ | ||||
| 					if(getZoneID(pos) == zoneBid) | ||||
| 						middleTiles.push_back(tile); | ||||
| 				}); | ||||
| 			} | ||||
|  | ||||
| 			//find tiles with minimum manhattan distance from center of the mass of zone border | ||||
| 			size_t tilesCount = middleTiles.size() ? middleTiles.size() : 1; | ||||
| 			int3 middleTile = std::accumulate(middleTiles.begin(), middleTiles.end(), int3(0, 0, 0)); | ||||
| 			middleTile.x /= (si32)tilesCount; | ||||
| 			middleTile.y /= (si32)tilesCount; | ||||
| 			middleTile.z /= (si32)tilesCount; //TODO: implement division operator for int3? | ||||
| 			boost::sort(middleTiles, [middleTile](const int3 &lhs, const int3 &rhs) -> bool | ||||
| 			{ | ||||
| 				//choose tiles with both corrdinates in the middle | ||||
| 				return lhs.mandist2d(middleTile) < rhs.mandist2d(middleTile); | ||||
| 			}); | ||||
|  | ||||
| 			//remove 1/4 tiles from each side - path should cross zone borders at smooth angle | ||||
| 			size_t removedCount = tilesCount / 4; //rounded down | ||||
| 			middleTiles.erase(middleTiles.end() - removedCount, middleTiles.end()); | ||||
| 			middleTiles.erase(middleTiles.begin(), middleTiles.begin() + removedCount); | ||||
|  | ||||
| 			RandomGeneratorUtil::randomShuffle(middleTiles, rand); | ||||
| 			for (auto tile : middleTiles) | ||||
| 			{ | ||||
| 				guardPos = tile; | ||||
| 				if (guardPos.valid()) | ||||
| 				{ | ||||
| 					//zones can make paths only in their own area | ||||
| 					zoneA->connectWithCenter(guardPos, true, true); | ||||
| 					zoneB->connectWithCenter(guardPos, true, true); | ||||
|  | ||||
| 					bool monsterPresent = zoneA->addMonster(guardPos, connection.getGuardStrength(), false, true); | ||||
| 					zoneB->updateDistances(guardPos); //place next objects away from guard in both zones | ||||
|  | ||||
| 					//set free tile only after connection is made to the center of the zone | ||||
| 					if (!monsterPresent) | ||||
| 						setOccupied(guardPos, ETileType::FREE); //just in case monster is too weak to spawn | ||||
|  | ||||
| 					zoneA->addRoadNode(guardPos); | ||||
| 					zoneB->addRoadNode(guardPos); | ||||
| 					break; //we're done with this connection | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		if (!guardPos.valid()) | ||||
| 		{ | ||||
| 			if(!waterMode || posA.z != posB.z || !zoneWater.second->waterKeepConnection(connection.getZoneA(), connection.getZoneB())) | ||||
| 				connectionsLeft.push_back(connection); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CMapGenerator::createConnections2() | ||||
| { | ||||
| 	for (auto & connection : connectionsLeft) | ||||
| 	{ | ||||
| 		auto zoneA = zones[connection.getZoneA()]; | ||||
| 		auto zoneB = zones[connection.getZoneB()]; | ||||
|  | ||||
| 		int3 guardPos(-1, -1, -1); | ||||
|  | ||||
| 		int3 posA = zoneA->getPos(); | ||||
| 		int3 posB = zoneB->getPos(); | ||||
|  | ||||
| 		auto strength = connection.getGuardStrength(); | ||||
|  | ||||
| 		if (posA.z != posB.z) //try to place subterranean gates | ||||
| 		{ | ||||
| 			auto sgt = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0)->getTemplates().front(); | ||||
| 			auto tilesBlockedByObject = sgt.getBlockedOffsets(); | ||||
|  | ||||
| 			auto factory = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0); | ||||
| 			auto gate1 = factory->create(ObjectTemplate()); | ||||
| 			auto gate2 = factory->create(ObjectTemplate()); | ||||
|  | ||||
| 			while (!guardPos.valid()) | ||||
| 			{ | ||||
| 				bool continueOuterLoop = false; | ||||
| 				//find common tiles for both zones | ||||
| 				auto tileSetA = zoneA->getPossibleTiles(), | ||||
| 					tileSetB = zoneB->getPossibleTiles(); | ||||
|  | ||||
| 				std::vector<int3> tilesA(tileSetA.begin(), tileSetA.end()), | ||||
| 					tilesB(tileSetB.begin(), tileSetB.end()); | ||||
|  | ||||
| 				std::vector<int3> commonTiles; | ||||
|  | ||||
| 				auto lambda = [](const int3 & lhs, const int3 & rhs) -> bool | ||||
| 				{ | ||||
| 					//https://stackoverflow.com/questions/45966807/c-invalid-comparator-assert | ||||
| 					return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y); //ignore z coordinate | ||||
| 				}; | ||||
| 				//required for set_intersection | ||||
| 				boost::sort(tilesA, lambda); | ||||
| 				boost::sort(tilesB, lambda); | ||||
|  | ||||
| 				boost::set_intersection(tilesA, tilesB, std::back_inserter(commonTiles), lambda); | ||||
|  | ||||
| 				vstd::erase_if(commonTiles, [](const int3 &tile) -> bool | ||||
| 				{ | ||||
| 					return (!tile.x) || (!tile.y); //gates shouldn't go outside map (x = 0) and look bad at the very top (y = 0) | ||||
| 				}); | ||||
|  | ||||
| 				if (commonTiles.empty()) | ||||
| 					break; //nothing more to do | ||||
|  | ||||
| 				boost::sort(commonTiles, [posA, posB](const int3 &lhs, const int3 &rhs) -> bool | ||||
| 				{ | ||||
| 					//choose tiles which are equidistant to zone centers | ||||
| 					return (std::abs<double>(posA.dist2dSQ(lhs) - posB.dist2dSQ(lhs)) < std::abs<double>((posA.dist2dSQ(rhs) - posB.dist2dSQ(rhs)))); | ||||
| 				}); | ||||
|  | ||||
| 				for (auto tile : commonTiles) | ||||
| 				{ | ||||
| 					tile.z = posA.z; | ||||
| 					int3 otherTile = tile; | ||||
| 					otherTile.z = posB.z; | ||||
|  | ||||
| 					float distanceFromA = static_cast<float>(posA.dist2d(tile)); | ||||
| 					float distanceFromB = static_cast<float>(posB.dist2d(otherTile)); | ||||
|  | ||||
| 					if (distanceFromA > 5 && distanceFromB > 5) | ||||
| 					{ | ||||
| 						if (zoneA->areAllTilesAvailable(gate1, tile, tilesBlockedByObject) && | ||||
| 							zoneB->areAllTilesAvailable(gate2, otherTile, tilesBlockedByObject)) | ||||
| 						{ | ||||
| 							if (zoneA->getAccessibleOffset(sgt, tile).valid() && zoneB->getAccessibleOffset(sgt, otherTile).valid()) | ||||
| 							{ | ||||
| 								EObjectPlacingResult::EObjectPlacingResult result1 = zoneA->tryToPlaceObjectAndConnectToPath(gate1, tile); | ||||
| 								EObjectPlacingResult::EObjectPlacingResult result2 = zoneB->tryToPlaceObjectAndConnectToPath(gate2, otherTile); | ||||
|  | ||||
| 								if ((result1 == EObjectPlacingResult::SUCCESS) && (result2 == EObjectPlacingResult::SUCCESS)) | ||||
| 								{ | ||||
| 									zoneA->placeObject(gate1, tile); | ||||
| 									zoneA->guardObject(gate1, strength, true, true); | ||||
| 									zoneB->placeObject(gate2, otherTile); | ||||
| 									zoneB->guardObject(gate2, strength, true, true); | ||||
| 									guardPos = tile; //set to break the loop | ||||
| 									break; | ||||
| 								} | ||||
| 								else if ((result1 == EObjectPlacingResult::SEALED_OFF) || (result2 == EObjectPlacingResult::SEALED_OFF)) | ||||
| 								{ | ||||
| 									//sealed-off tiles were blocked, exit inner loop and get another tile set | ||||
| 									continueOuterLoop = true; | ||||
| 									break; | ||||
| 								} | ||||
| 								else | ||||
| 									continue; //try with another position | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				if (!continueOuterLoop) //we didn't find ANY tile - break outer loop | ||||
| 					break; | ||||
| 			} | ||||
| 			if (!guardPos.valid()) //cleanup? is this safe / enough? | ||||
| 			{ | ||||
| 				delete gate1; | ||||
| 				delete gate2; | ||||
| 			} | ||||
| 		} | ||||
| 		if (!guardPos.valid()) | ||||
| 		{ | ||||
| 			auto factory = VLC->objtypeh->getHandlerFor(Obj::MONOLITH_TWO_WAY, getNextMonlithIndex()); | ||||
| 			auto teleport1 = factory->create(ObjectTemplate()); | ||||
| 			auto teleport2 = factory->create(ObjectTemplate()); | ||||
|  | ||||
| 			zoneA->addRequiredObject(teleport1, strength); | ||||
| 			zoneB->addRequiredObject(teleport2, strength); | ||||
| 			if(auto * m = zoneA->getModificator<TreasurePlacer>()) | ||||
| 				zoneA->getModificator<TreasurePlacer>()->setQuestArtZone(zoneB.get()); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CMapGenerator::addHeaderInfo() | ||||
| { | ||||
| 	map->version = EMapFormat::VCMI; | ||||
| 	map->width = mapGenOptions.getWidth(); | ||||
| 	map->height = mapGenOptions.getHeight(); | ||||
| 	map->twoLevel = mapGenOptions.getHasTwoLevels(); | ||||
| 	map->name = VLC->generaltexth->allTexts[740]; | ||||
| 	map->description = getMapDescription(); | ||||
| 	map->difficulty = 1; | ||||
| 	map->map().version = EMapFormat::VCMI; | ||||
| 	map->map().width = mapGenOptions.getWidth(); | ||||
| 	map->map().height = mapGenOptions.getHeight(); | ||||
| 	map->map().twoLevel = mapGenOptions.getHasTwoLevels(); | ||||
| 	map->map().name = VLC->generaltexth->allTexts[740]; | ||||
| 	map->map().description = getMapDescription(); | ||||
| 	map->map().difficulty = 1; | ||||
| 	addPlayerInfo(); | ||||
| } | ||||
|  | ||||
| void CMapGenerator::checkIsOnMap(const int3& tile) const | ||||
| { | ||||
| 	if (!map->isInTheMap(tile)) | ||||
| 		throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile.toString())); | ||||
| } | ||||
|  | ||||
|  | ||||
| CMapGenerator::Zones & CMapGenerator::getZones() | ||||
| { | ||||
| 	return zones; | ||||
| } | ||||
|  | ||||
| bool CMapGenerator::isBlocked(const int3 &tile) const | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	return tiles[tile.x][tile.y][tile.z].isBlocked(); | ||||
| } | ||||
| bool CMapGenerator::shouldBeBlocked(const int3 &tile) const | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	return tiles[tile.x][tile.y][tile.z].shouldBeBlocked(); | ||||
| } | ||||
| bool CMapGenerator::isPossible(const int3 &tile) const | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	return tiles[tile.x][tile.y][tile.z].isPossible(); | ||||
| } | ||||
| bool CMapGenerator::isFree(const int3 &tile) const | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	return tiles[tile.x][tile.y][tile.z].isFree(); | ||||
| } | ||||
| bool CMapGenerator::isUsed(const int3 &tile) const | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	return tiles[tile.x][tile.y][tile.z].isUsed(); | ||||
| } | ||||
|  | ||||
| bool CMapGenerator::isRoad(const int3& tile) const | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	return tiles[tile.x][tile.y][tile.z].isRoad(); | ||||
| } | ||||
|  | ||||
| void CMapGenerator::setOccupied(const int3 &tile, ETileType::ETileType state) | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	tiles[tile.x][tile.y][tile.z].setOccupied(state); | ||||
| } | ||||
|  | ||||
| void CMapGenerator::setRoad(const int3& tile, const std::string & roadType) | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	tiles[tile.x][tile.y][tile.z].setRoadType(roadType); | ||||
| } | ||||
|  | ||||
|  | ||||
| CTileInfo CMapGenerator::getTile(const int3& tile) const | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	return tiles[tile.x][tile.y][tile.z]; | ||||
| } | ||||
|  | ||||
| TRmgTemplateZoneId CMapGenerator::getZoneID(const int3& tile) const | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	return zoneColouring[tile.z][tile.x][tile.y]; | ||||
| } | ||||
|  | ||||
| void CMapGenerator::setZoneID(const int3& tile, TRmgTemplateZoneId zid) | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	zoneColouring[tile.z][tile.x][tile.y] = zid; | ||||
| } | ||||
|  | ||||
| bool CMapGenerator::isAllowedSpell(SpellID sid) const | ||||
| { | ||||
| 	assert(sid >= 0); | ||||
| 	if (sid < map->allowedSpell.size()) | ||||
| 	{ | ||||
| 		return map->allowedSpell[sid]; | ||||
| 	} | ||||
| 	else | ||||
| 		return false; | ||||
| } | ||||
|  | ||||
| void CMapGenerator::setNearestObjectDistance(int3 &tile, float value) | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	tiles[tile.x][tile.y][tile.z].setNearestObjectDistance(value); | ||||
| } | ||||
|  | ||||
| float CMapGenerator::getNearestObjectDistance(const int3 &tile) const | ||||
| { | ||||
| 	checkIsOnMap(tile); | ||||
|  | ||||
| 	return tiles[tile.x][tile.y][tile.z].getNearestObjectDistance(); | ||||
| } | ||||
|  | ||||
| int CMapGenerator::getNextMonlithIndex() | ||||
| { | ||||
| 	if (monolithIndex >= VLC->objtypeh->knownSubObjects(Obj::MONOLITH_TWO_WAY).size()) | ||||
| @@ -961,76 +359,27 @@ int CMapGenerator::getPrisonsRemaning() const | ||||
| { | ||||
| 	return prisonsRemaining; | ||||
| } | ||||
|  | ||||
| void CMapGenerator::decreasePrisonsRemaining() | ||||
| { | ||||
| 	prisonsRemaining = std::max (0, prisonsRemaining - 1); | ||||
| } | ||||
|  | ||||
| std::vector<ArtifactID> CMapGenerator::getQuestArtsRemaning() const | ||||
| const std::vector<ArtifactID> & CMapGenerator::getQuestArtsRemaning() const | ||||
| { | ||||
| 	return questArtifacts; | ||||
| } | ||||
|  | ||||
| void CMapGenerator::banQuestArt(ArtifactID id) | ||||
| { | ||||
| 	map->allowedArtifact[id] = false; | ||||
| 	vstd::erase_if_present (questArtifacts, id); | ||||
| 	map->map().allowedArtifact[id] = false; | ||||
| 	vstd::erase_if_present(questArtifacts, id); | ||||
| } | ||||
|  | ||||
| void CMapGenerator::registerZone (TFaction faction) | ||||
| Zone * CMapGenerator::getZoneWater() const | ||||
| { | ||||
| 	zonesPerFaction[faction]++; | ||||
| 	zonesTotal++; | ||||
| } | ||||
| ui32 CMapGenerator::getZoneCount(TFaction faction) | ||||
| { | ||||
| 	return zonesPerFaction[faction]; | ||||
| } | ||||
| ui32 CMapGenerator::getTotalZoneCount() const | ||||
| { | ||||
| 	return zonesTotal; | ||||
| } | ||||
| CMapGenerator::Zones::value_type CMapGenerator::getZoneWater() const | ||||
| { | ||||
| 	return zoneWater; | ||||
| } | ||||
|  | ||||
| void CMapGenerator::dump(bool zoneId) | ||||
| { | ||||
| 	static int id = 0; | ||||
| 	std::ofstream out(boost::to_string(boost::format("zone_%d.txt") % id++)); | ||||
| 	int levels = map->twoLevel ? 2 : 1; | ||||
| 	int width =  map->width; | ||||
| 	int height = map->height; | ||||
| 	for (int k = 0; k < levels; k++) | ||||
| 	{ | ||||
| 		for(int j=0; j<height; j++) | ||||
| 		{ | ||||
| 			for (int i=0; i<width; i++) | ||||
| 			{ | ||||
| 				if(zoneId) | ||||
| 				{ | ||||
| 					out << getZoneID(int3(i, j, k)); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					char t = '?'; | ||||
| 					switch (getTile(int3(i, j, k)).getTileType()) | ||||
| 					{ | ||||
| 						case ETileType::FREE: | ||||
| 							t = ' '; break; | ||||
| 						case ETileType::BLOCKED: | ||||
| 							t = '#'; break; | ||||
| 						case ETileType::POSSIBLE: | ||||
| 							t = '-'; break; | ||||
| 						case ETileType::USED: | ||||
| 							t = 'O'; break; | ||||
| 					} | ||||
| 					out << t; | ||||
| 				} | ||||
| 			} | ||||
| 			out << std::endl; | ||||
| 		} | ||||
| 		out << std::endl; | ||||
| 	} | ||||
| 	out << std::endl; | ||||
| 	for(auto & z : map->getZones()) | ||||
| 		if(z.second->getType() == ETemplateZoneType::WATER) | ||||
| 			return z.second.get(); | ||||
| 	return nullptr; | ||||
| } | ||||
|   | ||||
| @@ -16,36 +16,15 @@ | ||||
| #include "../int3.h" | ||||
| #include "CRmgTemplate.h" | ||||
|  | ||||
| class CMap; | ||||
| class CRmgTemplate; | ||||
| class CRmgTemplateZone; | ||||
| class CMapGenOptions; | ||||
| class CTerrainViewPatternConfig; | ||||
| class CMapEditManager; | ||||
| class JsonNode; | ||||
| class CMapGenerator; | ||||
| class CTileInfo; | ||||
| class RmgMap; | ||||
| class CMap; | ||||
| class Zone; | ||||
|  | ||||
| typedef std::vector<JsonNode> JsonVector; | ||||
|  | ||||
| class rmgException : public std::exception | ||||
| { | ||||
| 	std::string msg; | ||||
| public: | ||||
| 	explicit rmgException(const std::string& _Message) : msg(_Message) | ||||
| 	{ | ||||
| 	} | ||||
|  | ||||
| 	virtual ~rmgException() throw () | ||||
| 	{ | ||||
| 	}; | ||||
|  | ||||
| 	const char *what() const throw () override | ||||
| 	{ | ||||
| 		return msg.c_str(); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| /// The map generator creates a map randomly. | ||||
| class DLL_LINKAGE CMapGenerator | ||||
| { | ||||
| @@ -57,7 +36,6 @@ public: | ||||
| 		std::vector<CTreasureInfo> waterTreasure; | ||||
| 		int shipyardGuard; | ||||
| 		int mineExtraResources; | ||||
| 		std::map<Res::ERes, int> mineValues; | ||||
| 		int minGuardStrength; | ||||
| 		std::string defaultRoadType; | ||||
| 		int treasureValueLimit; | ||||
| @@ -68,84 +46,44 @@ public: | ||||
| 		std::vector<int> questValues, questRewardValues; | ||||
| 	}; | ||||
| 	 | ||||
| 	using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>>; | ||||
|  | ||||
| 	explicit CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed = std::time(nullptr)); | ||||
| 	~CMapGenerator(); // required due to std::unique_ptr | ||||
| 	 | ||||
| 	const Config & getConfig() const; | ||||
| 	 | ||||
| 	mutable std::unique_ptr<CMap> map; | ||||
| 	CRandomGenerator rand; | ||||
| 	 | ||||
| 	CMapEditManager* getEditManager() const; | ||||
| 	const CMapGenOptions& getMapGenOptions() const; | ||||
| 	 | ||||
| 	std::unique_ptr<CMap> generate(); | ||||
|  | ||||
| 	Zones & getZones(); | ||||
| 	void createDirectConnections(); | ||||
| 	void createConnections2(); | ||||
| 	void findZonesForQuestArts(); | ||||
| 	void foreach_neighbour(const int3 &pos, std::function<void(int3& pos)> foo); | ||||
| 	void foreachDirectNeighbour(const int3 &pos, std::function<void(int3& pos)> foo); | ||||
| 	void foreachDiagonalNeighbour(const int3& pos, std::function<void(int3& pos)> foo); | ||||
|  | ||||
| 	bool isBlocked(const int3 &tile) const; | ||||
| 	bool shouldBeBlocked(const int3 &tile) const; | ||||
| 	bool isPossible(const int3 &tile) const; | ||||
| 	bool isFree(const int3 &tile) const; | ||||
| 	bool isUsed(const int3 &tile) const; | ||||
| 	bool isRoad(const int3 &tile) const; | ||||
|  | ||||
| 	void setOccupied(const int3 &tile, ETileType::ETileType state); | ||||
| 	void setRoad(const int3 &tile, const std::string & roadType); | ||||
|  | ||||
| 	CTileInfo getTile(const int3 & tile) const; | ||||
| 	bool isAllowedSpell(SpellID sid) const; | ||||
|  | ||||
| 	float getNearestObjectDistance(const int3 &tile) const; | ||||
| 	void setNearestObjectDistance(int3 &tile, float value); | ||||
|  | ||||
| 	int getNextMonlithIndex(); | ||||
| 	int getPrisonsRemaning() const; | ||||
| 	void decreasePrisonsRemaining(); | ||||
| 	std::vector<ArtifactID> getQuestArtsRemaning() const; | ||||
| 	const std::vector<ArtifactID> & getQuestArtsRemaning() const; | ||||
| 	void banQuestArt(ArtifactID id); | ||||
|  | ||||
| 	void registerZone (TFaction faction); | ||||
| 	ui32 getZoneCount(TFaction faction); | ||||
| 	ui32 getTotalZoneCount() const; | ||||
| 	 | ||||
| 	Zones::value_type getZoneWater() const; | ||||
| 	Zone * getZoneWater() const; | ||||
| 	void createWaterTreasures(); | ||||
| 	void prepareWaterTiles(); | ||||
|  | ||||
| 	TRmgTemplateZoneId getZoneID(const int3& tile) const; | ||||
| 	void setZoneID(const int3& tile, TRmgTemplateZoneId zid); | ||||
| 	int getRandomSeed() const; | ||||
| 	 | ||||
| 	void dump(bool zoneId); | ||||
|  | ||||
| private: | ||||
| 	int randomSeed; | ||||
| 	CMapGenOptions& mapGenOptions; | ||||
| 	Config config; | ||||
| 	std::unique_ptr<RmgMap> map; | ||||
| 	 | ||||
| 	std::vector<rmg::ZoneConnection> connectionsLeft; | ||||
| 	Zones zones; | ||||
| 	std::map<TFaction, ui32> zonesPerFaction; | ||||
| 	ui32 zonesTotal; //zones that have their main town only | ||||
| 	 | ||||
| 	std::pair<Zones::key_type, Zones::mapped_type> zoneWater; | ||||
|  | ||||
| 	CTileInfo*** tiles; | ||||
| 	boost::multi_array<TRmgTemplateZoneId, 3> zoneColouring; //[z][x][y] | ||||
| 	//std::pair<Zones::key_type, Zones::mapped_type> zoneWater; | ||||
|  | ||||
| 	int prisonsRemaining; | ||||
| 	//int questArtsRemaining; | ||||
| 	int monolithIndex; | ||||
| 	std::vector<ArtifactID> questArtifacts; | ||||
| 	void checkIsOnMap(const int3 &tile) const; //throws | ||||
|  | ||||
| 	/// Generation methods | ||||
| 	void loadConfig(); | ||||
| @@ -156,10 +94,7 @@ private: | ||||
| 	void initQuestArtsRemaining(); | ||||
| 	void addPlayerInfo(); | ||||
| 	void addHeaderInfo(); | ||||
| 	void initTiles(); | ||||
| 	void genZones(); | ||||
| 	void fillZones(); | ||||
| 	void createObstaclesCommon1(); | ||||
| 	void createObstaclesCommon2(); | ||||
|  | ||||
| }; | ||||
|   | ||||
| @@ -252,6 +252,11 @@ void ZoneOptions::setMonsterTypes(const std::set<TFaction> & value) | ||||
| 	monsterTypes = value; | ||||
| } | ||||
|  | ||||
| const std::set<TFaction> & ZoneOptions::getMonsterTypes() const | ||||
| { | ||||
| 	return monsterTypes; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::setMinesInfo(const std::map<TResource, ui16> & value) | ||||
| { | ||||
| 	mines = value; | ||||
| @@ -302,6 +307,26 @@ std::vector<TRmgTemplateZoneId> ZoneOptions::getConnections() const | ||||
| 	return connections; | ||||
| } | ||||
|  | ||||
| bool ZoneOptions::areTownsSameType() const | ||||
| { | ||||
| 	return townsAreSameType; | ||||
| } | ||||
|  | ||||
| bool ZoneOptions::isMatchTerrainToTown() const | ||||
| { | ||||
| 	return matchTerrainToTown; | ||||
| } | ||||
|  | ||||
| const ZoneOptions::CTownInfo & ZoneOptions::getPlayerTowns() const | ||||
| { | ||||
| 	return playerTowns; | ||||
| } | ||||
|  | ||||
| const ZoneOptions::CTownInfo & ZoneOptions::getNeutralTowns() const | ||||
| { | ||||
| 	return neutralTowns; | ||||
| } | ||||
|  | ||||
| void ZoneOptions::serializeJson(JsonSerializeFormat & handler) | ||||
| { | ||||
| 	static const std::vector<std::string> zoneTypes = | ||||
| @@ -421,6 +446,11 @@ int ZoneConnection::getGuardStrength() const | ||||
| { | ||||
| 	return guardStrength; | ||||
| } | ||||
| 	 | ||||
| bool operator==(const ZoneConnection & l, const ZoneConnection & r) | ||||
| { | ||||
| 	return l.zoneA == r.zoneA && l.zoneB == r.zoneB && l.guardStrength == r.guardStrength; | ||||
| } | ||||
|  | ||||
| void ZoneConnection::serializeJson(JsonSerializeFormat & handler) | ||||
| { | ||||
|   | ||||
| @@ -17,7 +17,6 @@ | ||||
| #include "CMapGenOptions.h" | ||||
|  | ||||
| class JsonSerializeFormat; | ||||
| class Terrain; | ||||
|  | ||||
| namespace ETemplateZoneType | ||||
| { | ||||
| @@ -58,6 +57,8 @@ public: | ||||
| 	int getGuardStrength() const; | ||||
|  | ||||
| 	void serializeJson(JsonSerializeFormat & handler); | ||||
| 	 | ||||
| 	friend bool operator==(const ZoneConnection &, const ZoneConnection &); | ||||
| private: | ||||
| 	TRmgTemplateZoneId zoneA; | ||||
| 	TRmgTemplateZoneId zoneB; | ||||
| @@ -105,8 +106,11 @@ public: | ||||
| 	const std::set<Terrain> & getTerrainTypes() const; | ||||
| 	void setTerrainTypes(const std::set<Terrain> & value); | ||||
|  | ||||
| 	const CTownInfo & getPlayerTowns() const; | ||||
| 	const CTownInfo & getNeutralTowns() const; | ||||
| 	std::set<TFaction> getDefaultTownTypes() const; | ||||
| 	const std::set<TFaction> & getTownTypes() const; | ||||
| 	const std::set<TFaction> & getMonsterTypes() const; | ||||
|  | ||||
| 	void setTownTypes(const std::set<TFaction> & value); | ||||
| 	void setMonsterTypes(const std::set<TFaction> & value); | ||||
| @@ -126,6 +130,11 @@ public: | ||||
| 	std::vector<TRmgTemplateZoneId> getConnections() const; | ||||
|  | ||||
| 	void serializeJson(JsonSerializeFormat & handler); | ||||
| 	 | ||||
| 	EMonsterStrength::EMonsterStrength zoneMonsterStrength; | ||||
| 	 | ||||
| 	bool areTownsSameType() const; | ||||
| 	bool isMatchTerrainToTown() const; | ||||
|  | ||||
| protected: | ||||
| 	TRmgTemplateZoneId id; | ||||
| @@ -141,8 +150,6 @@ protected: | ||||
| 	std::set<TFaction> townTypes; | ||||
| 	std::set<TFaction> monsterTypes; | ||||
|  | ||||
| 	EMonsterStrength::EMonsterStrength zoneMonsterStrength; | ||||
|  | ||||
| 	std::map<TResource, ui16> mines; //obligatory mines to spawn in this zone | ||||
|  | ||||
| 	std::vector<CTreasureInfo> treasureInfo; | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,237 +0,0 @@ | ||||
| /* | ||||
|  * CRmgTemplateZone.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 "../GameConstants.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "float3.h" | ||||
| #include "../int3.h" | ||||
| #include "CRmgTemplate.h" | ||||
| #include "../mapObjects/ObjectTemplate.h" | ||||
| #include <boost/heap/priority_queue.hpp> //A* | ||||
| #include "Terrain.h" | ||||
|  | ||||
| class CMapGenerator; | ||||
| class CTileInfo; | ||||
| class int3; | ||||
| class CGObjectInstance; | ||||
| class ObjectTemplate; | ||||
|  | ||||
| namespace EObjectPlacingResult | ||||
| { | ||||
| 	enum EObjectPlacingResult | ||||
| 	{ | ||||
| 		SUCCESS, | ||||
| 		CANNOT_FIT, | ||||
| 		SEALED_OFF | ||||
| 	}; | ||||
| } | ||||
| class DLL_LINKAGE CTileInfo | ||||
| { | ||||
| public: | ||||
|  | ||||
| 	CTileInfo(); | ||||
|  | ||||
| 	float getNearestObjectDistance() const; | ||||
| 	void setNearestObjectDistance(float value); | ||||
| 	bool isBlocked() const; | ||||
| 	bool shouldBeBlocked() const; | ||||
| 	bool isPossible() const; | ||||
| 	bool isFree() const; | ||||
| 	bool isUsed() const; | ||||
| 	bool isRoad() const; | ||||
| 	void setOccupied(ETileType::ETileType value); | ||||
| 	Terrain getTerrainType() const; | ||||
| 	ETileType::ETileType getTileType() const; | ||||
| 	void setTerrainType(Terrain value); | ||||
|  | ||||
| 	void setRoadType(const std::string & value); | ||||
| private: | ||||
| 	float nearestObjectDistance; | ||||
| 	ETileType::ETileType occupied; | ||||
| 	Terrain terrain; | ||||
| 	std::string roadType; | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE ObjectInfo | ||||
| { | ||||
| 	ObjectTemplate templ; | ||||
| 	ui32 value; | ||||
| 	ui16 probability; | ||||
| 	ui32 maxPerZone; | ||||
| 	//ui32 maxPerMap; //unused | ||||
| 	std::function<CGObjectInstance *()> generateObject; | ||||
|  | ||||
| 	void setTemplate (si32 type, si32 subtype, Terrain terrain); | ||||
|  | ||||
| 	ObjectInfo(); | ||||
|  | ||||
| 	bool operator==(const ObjectInfo& oi) const { return (templ == oi.templ); } | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE CTreasurePileInfo | ||||
| { | ||||
| 	std::set<int3> visitableFromBottomPositions; //can be visited only from bottom or side | ||||
| 	std::set<int3> visitableFromTopPositions; //they can be visited from any direction | ||||
| 	std::set<int3> blockedPositions; | ||||
| 	std::set<int3> occupiedPositions; //blocked + visitable | ||||
| 	int3 nextTreasurePos; | ||||
| }; | ||||
|  | ||||
| /// The CRmgTemplateZone describes a zone in a template. | ||||
| class DLL_LINKAGE CRmgTemplateZone : public rmg::ZoneOptions | ||||
| { | ||||
| public: | ||||
| 	CRmgTemplateZone(CMapGenerator * Gen); | ||||
|  | ||||
| 	void setOptions(const rmg::ZoneOptions & options); | ||||
| 	bool isUnderground() const; | ||||
|  | ||||
| 	float3 getCenter() const; | ||||
| 	void setCenter(const float3 &f); | ||||
| 	int3 getPos() const; | ||||
| 	void setPos(const int3 &pos); | ||||
| 	bool isAccessibleFromSomewhere(ObjectTemplate & appearance, const int3 & tile) const; | ||||
| 	int3 getAccessibleOffset(ObjectTemplate & appearance, const int3 & tile) const; | ||||
|  | ||||
| 	void addTile (const int3 & pos); | ||||
| 	void removeTile(const int3 & pos); | ||||
| 	void initFreeTiles (); | ||||
| 	std::set<int3> getTileInfo() const; | ||||
| 	std::set<int3> getPossibleTiles() const; | ||||
| 	std::set<int3> collectDistantTiles (float distance) const; | ||||
| 	void clearTiles(); | ||||
|  | ||||
| 	void addRequiredObject(CGObjectInstance * obj, si32 guardStrength=0); | ||||
| 	void addCloseObject(CGObjectInstance * obj, si32 guardStrength = 0); | ||||
| 	void addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget); | ||||
| 	void addObjectAtPosition(CGObjectInstance * obj, const int3 & position, si32 guardStrength=0); | ||||
|  | ||||
| 	void addToConnectLater(const int3& src); | ||||
| 	bool addMonster(int3 &pos, si32 strength, bool clearSurroundingTiles = true, bool zoneGuard = false); | ||||
| 	bool createTreasurePile(int3 &pos, float minDistance, const CTreasureInfo& treasureInfo); | ||||
| 	bool fill (); | ||||
| 	bool placeMines (); | ||||
| 	void initTownType (); | ||||
| 	void paintZoneTerrain (Terrain terrainType); | ||||
| 	void randomizeTownType(bool matchUndergroundType = false); //helper function | ||||
| 	void initTerrainType (); | ||||
| 	void createBorder(); | ||||
| 	void fractalize(); | ||||
| 	void connectLater(); | ||||
| 	EObjectPlacingResult::EObjectPlacingResult tryToPlaceObjectAndConnectToPath(CGObjectInstance * obj, const int3 & pos); //return true if the position can be connected | ||||
| 	bool createRequiredObjects(); | ||||
| 	bool createShipyard(const int3 & pos, si32 guardStrength=0); | ||||
| 	int3 createShipyard(const std::set<int3> & lake, si32 guardStrength=0); | ||||
| 	bool makeBoat(TRmgTemplateZoneId land, const int3 & coast); | ||||
| 	int3 makeBoat(TRmgTemplateZoneId land, const std::set<int3> & lake); | ||||
| 	void createTreasures(); | ||||
| 	 | ||||
| 	void createWater(EWaterContent::EWaterContent waterContent, bool debug=false); | ||||
| 	void waterInitFreeTiles(); | ||||
| 	void waterConnection(CRmgTemplateZone& dst); | ||||
| 	bool waterKeepConnection(TRmgTemplateZoneId zoneA, TRmgTemplateZoneId zoneB); | ||||
| 	const std::set<int3>& getCoastTiles() const; | ||||
| 	bool isWaterConnected(TRmgTemplateZoneId zone, const int3 & tile) const; | ||||
| 	//void computeCoastTiles(); | ||||
| 	 | ||||
| 	void createObstacles1(); | ||||
| 	void createObstacles2(); | ||||
| 	bool crunchPath(const int3 &src, const int3 &dst, bool onlyStraight, std::set<int3>* clearedTiles = nullptr); | ||||
| 	bool connectPath(const int3& src, bool onlyStraight); | ||||
| 	bool connectWithCenter(const int3& src, bool onlyStraight, bool passTroughBlocked = false); | ||||
| 	void updateDistances(const int3 & pos); | ||||
|  | ||||
| 	std::vector<int3> getAccessibleOffsets (const CGObjectInstance* object); | ||||
| 	bool areAllTilesAvailable(CGObjectInstance* obj, int3& tile, const std::set<int3>& tilesBlockedByObject) const; | ||||
|  | ||||
| 	void setQuestArtZone(std::shared_ptr<CRmgTemplateZone> otherZone); | ||||
| 	std::set<int3>* getFreePaths(); | ||||
| 	void addFreePath(const int3 &); | ||||
|  | ||||
| 	ObjectInfo getRandomObject (CTreasurePileInfo &info, ui32 desiredValue, ui32 maxValue, ui32 currentValue); | ||||
|  | ||||
| 	void placeSubterraneanGate(int3 pos, si32 guardStrength); | ||||
| 	void placeObject(CGObjectInstance* object, const int3 &pos, bool updateDistance = true); | ||||
| 	bool guardObject(CGObjectInstance* object, si32 str, bool zoneGuard = false, bool addToFreePaths = false); | ||||
| 	void placeAndGuardObject(CGObjectInstance* object, const int3 &pos, si32 str, bool zoneGuard = false); | ||||
| 	void addRoadNode(const int3 & node); | ||||
| 	void connectRoads(); //fills "roads" according to "roadNodes" | ||||
|  | ||||
| 	//A* priority queue | ||||
| 	typedef std::pair<int3, float> TDistance; | ||||
| 	struct NodeComparer | ||||
| 	{ | ||||
| 		bool operator()(const TDistance & lhs, const TDistance & rhs) const | ||||
| 		{ | ||||
| 			return (rhs.second < lhs.second); | ||||
| 		} | ||||
| 	}; | ||||
| 	boost::heap::priority_queue<TDistance, boost::heap::compare<NodeComparer>> createPriorityQueue(); | ||||
|  | ||||
| private: | ||||
| 	 | ||||
| 	//subclass to store disconnected parts of water zone | ||||
| 	struct Lake | ||||
| 	{ | ||||
| 		std::set<int3> tiles; | ||||
| 		std::set<int3> coast; | ||||
| 		std::map<int3, int> distance; | ||||
| 		std::set<TRmgTemplateZoneId> connectedZones; | ||||
| 		std::set<TRmgTemplateZoneId> keepConnections; | ||||
| 	}; | ||||
| 	 | ||||
| 	CMapGenerator * gen; | ||||
| 	 | ||||
| 	//template info | ||||
| 	si32 townType; | ||||
| 	Terrain terrainType; | ||||
| 	std::weak_ptr<CRmgTemplateZone> questArtZone; //artifacts required for Seer Huts will be placed here - or not if null | ||||
|  | ||||
| 	std::vector<ObjectInfo> possibleObjects; | ||||
| 	int minGuardedValue; | ||||
| 	 | ||||
| 	//content info | ||||
| 	std::vector<std::pair<CGObjectInstance*, ui32>> requiredObjects; | ||||
| 	std::vector<std::pair<CGObjectInstance*, ui32>> closeObjects; | ||||
| 	std::vector<std::pair<CGObjectInstance*, int3>> instantObjects; | ||||
| 	std::vector<std::pair<CGObjectInstance*, CGObjectInstance*>> nearbyObjects; | ||||
| 	std::vector<CGObjectInstance*> objects; | ||||
| 	std::map<CGObjectInstance*, int3> requestedPositions; | ||||
|  | ||||
| 	//placement info | ||||
| 	int3 pos; | ||||
| 	float3 center; | ||||
| 	std::set<int3> tileinfo; //irregular area assined to zone | ||||
| 	std::set<int3> possibleTiles; //optimization purposes for treasure generation | ||||
| 	std::set<int3> freePaths; //core paths of free tiles that all other objects will be linked to | ||||
| 	std::set<int3> coastTiles; //tiles bordered to water | ||||
|  | ||||
| 	std::set<int3> roadNodes; //tiles to be connected with roads | ||||
| 	std::set<int3> roads; //all tiles with roads | ||||
| 	std::set<int3> tilesToConnectLater; //will be connected after paths are fractalized | ||||
| 	std::vector<Lake> lakes; //disconnected parts of zone. Used to work with water zones | ||||
| 	std::map<int3, int> lakeMap; //map tile on lakeId which is position of lake in lakes array +1 | ||||
| 	 | ||||
| 	bool createRoad(const int3 &src, const int3 &dst); | ||||
| 	void drawRoads(); //actually updates tiles | ||||
|  | ||||
| 	bool pointIsIn(int x, int y); | ||||
| 	void addAllPossibleObjects (); //add objects, including zone-specific, to possibleObjects | ||||
| 	bool findPlaceForObject(CGObjectInstance* obj, si32 min_dist, int3 &pos); | ||||
| 	bool findPlaceForTreasurePile(float min_dist, int3 &pos, int value); | ||||
| 	bool canObstacleBePlacedHere(ObjectTemplate &temp, int3 &pos); | ||||
| 	void setTemplateForObject(CGObjectInstance* obj); | ||||
| 	void checkAndPlaceObject(CGObjectInstance* object, const int3 &pos); | ||||
| 	int chooseRandomAppearance(si32 ObjID) const; | ||||
| 	 | ||||
| 	bool isGuardNeededForTreasure(int value); | ||||
| }; | ||||
| @@ -11,15 +11,17 @@ | ||||
| #include "StdInc.h" | ||||
| #include "../CRandomGenerator.h" | ||||
| #include "CZonePlacer.h" | ||||
| #include "CRmgTemplateZone.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
| #include "RmgMap.h" | ||||
| #include "Zone.h" | ||||
| #include "Functions.h" | ||||
|  | ||||
| class CRandomGenerator; | ||||
|  | ||||
| CZonePlacer::CZonePlacer(CMapGenerator * Gen) | ||||
| CZonePlacer::CZonePlacer(RmgMap & map) | ||||
| 	: width(0), height(0), scaleX(0), scaleY(0), mapSize(0), gravityConstant(0), stiffnessConstant(0), | ||||
| 	gen(Gen) | ||||
| 	map(map) | ||||
| { | ||||
|  | ||||
| } | ||||
| @@ -31,7 +33,7 @@ CZonePlacer::~CZonePlacer() | ||||
|  | ||||
| int3 CZonePlacer::cords (const float3 f) const | ||||
| { | ||||
| 	return int3((si32)std::max(0.f, (f.x * gen->map->width)-1), (si32)std::max(0.f, (f.y * gen->map->height-1)), f.z); | ||||
| 	return int3((si32)std::max(0.f, (f.x * map.map().width)-1), (si32)std::max(0.f, (f.y * map.map().height-1)), f.z); | ||||
| } | ||||
|  | ||||
| float CZonePlacer::getDistance (float distance) const | ||||
| @@ -43,11 +45,15 @@ void CZonePlacer::placeZones(CRandomGenerator * rand) | ||||
| { | ||||
| 	logGlobal->info("Starting zone placement"); | ||||
|  | ||||
| 	width = gen->getMapGenOptions().getWidth(); | ||||
| 	height = gen->getMapGenOptions().getHeight(); | ||||
| 	width = map.getMapGenOptions().getWidth(); | ||||
| 	height = map.getMapGenOptions().getHeight(); | ||||
|  | ||||
| 	auto zones = gen->getZones(); | ||||
| 	bool underground = gen->getMapGenOptions().getHasTwoLevels(); | ||||
| 	auto zones = map.getZones(); | ||||
| 	vstd::erase_if(zones, [](const std::pair<TRmgTemplateZoneId, std::shared_ptr<Zone>> & pr) | ||||
| 	{ | ||||
| 		return pr.second->getType() == ETemplateZoneType::WATER; | ||||
| 	}); | ||||
| 	bool underground = map.getMapGenOptions().getHasTwoLevels(); | ||||
|  | ||||
| 	/* | ||||
| 	gravity-based algorithm | ||||
| @@ -72,7 +78,7 @@ void CZonePlacer::placeZones(CRandomGenerator * rand) | ||||
| 	float bestTotalDistance = 1e10; | ||||
| 	float bestTotalOverlap = 1e10; | ||||
|  | ||||
| 	std::map<std::shared_ptr<CRmgTemplateZone>, float3> bestSolution; | ||||
| 	std::map<std::shared_ptr<Zone>, float3> bestSolution; | ||||
|  | ||||
| 	TForceVector forces; | ||||
| 	TForceVector totalForces; //  both attraction and pushback, overcomplicated? | ||||
| @@ -173,7 +179,7 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const | ||||
| 			if (boost::optional<int> owner = zone.second->getOwner()) | ||||
| 			{ | ||||
| 				auto player = PlayerColor(*owner - 1); | ||||
| 				auto playerSettings = gen->getMapGenOptions().getPlayersSettings(); | ||||
| 				auto playerSettings = map.getMapGenOptions().getPlayersSettings(); | ||||
| 				si32 faction = CMapGenOptions::CPlayerSettings::RANDOM_TOWN; | ||||
| 				if (vstd::contains(playerSettings, player)) | ||||
| 					faction = playerSettings[player].getStartingTown(); | ||||
| @@ -351,7 +357,7 @@ void CZonePlacer::moveOneZone(TZoneMap &zones, TForceVector &totalForces, TDista | ||||
| { | ||||
| 	float maxRatio = 0; | ||||
| 	const int maxDistanceMovementRatio = static_cast<int>(zones.size() * zones.size()); //experimental - the more zones, the greater total distance expected | ||||
| 	std::shared_ptr<CRmgTemplateZone> misplacedZone; | ||||
| 	std::shared_ptr<Zone> misplacedZone; | ||||
|  | ||||
| 	float totalDistance = 0; | ||||
| 	float totalOverlap = 0; | ||||
| @@ -371,7 +377,7 @@ void CZonePlacer::moveOneZone(TZoneMap &zones, TForceVector &totalForces, TDista | ||||
|  | ||||
| 	if (maxRatio > maxDistanceMovementRatio && misplacedZone) | ||||
| 	{ | ||||
| 		std::shared_ptr<CRmgTemplateZone> targetZone; | ||||
| 		std::shared_ptr<Zone> targetZone; | ||||
| 		float3 ourCenter = misplacedZone->getCenter(); | ||||
|  | ||||
| 		if (totalDistance > totalOverlap) | ||||
| @@ -450,20 +456,24 @@ d = 0.01 * dx^3 - 0.1618 * dx^2 + 1 * dx + ... | ||||
| 	return dx * (1.0f + dx * (0.1f + dx * 0.01f)) + dy * (1.618f + dy * (-0.1618f + dy * 0.01618f)); | ||||
| } | ||||
|  | ||||
| void CZonePlacer::assignZones() | ||||
| void CZonePlacer::assignZones(CRandomGenerator * rand) | ||||
| { | ||||
| 	logGlobal->info("Starting zone colouring"); | ||||
|  | ||||
| 	auto width = gen->getMapGenOptions().getWidth(); | ||||
| 	auto height = gen->getMapGenOptions().getHeight(); | ||||
| 	auto width = map.getMapGenOptions().getWidth(); | ||||
| 	auto height = map.getMapGenOptions().getHeight(); | ||||
|  | ||||
| 	//scale to Medium map to ensure smooth results | ||||
| 	scaleX = 72.f / width; | ||||
| 	scaleY = 72.f / height; | ||||
|  | ||||
| 	auto zones = gen->getZones(); | ||||
| 	auto zones = map.getZones(); | ||||
| 	vstd::erase_if(zones, [](const std::pair<TRmgTemplateZoneId, std::shared_ptr<Zone>> & pr) | ||||
| 	{ | ||||
| 		return pr.second->getType() == ETemplateZoneType::WATER; | ||||
| 	}); | ||||
|  | ||||
| 	typedef std::pair<std::shared_ptr<CRmgTemplateZone>, float> Dpair; | ||||
| 	typedef std::pair<std::shared_ptr<Zone>, float> Dpair; | ||||
| 	std::vector <Dpair> distances; | ||||
| 	distances.reserve(zones.size()); | ||||
|  | ||||
| @@ -475,10 +485,10 @@ void CZonePlacer::assignZones() | ||||
| 		return lhs.second / lhs.first->getSize() < rhs.second / rhs.first->getSize(); | ||||
| 	}; | ||||
|  | ||||
| 	auto moveZoneToCenterOfMass = [](std::shared_ptr<CRmgTemplateZone> zone) -> void | ||||
| 	auto moveZoneToCenterOfMass = [](std::shared_ptr<Zone> zone) -> void | ||||
| 	{ | ||||
| 		int3 total(0, 0, 0); | ||||
| 		auto tiles = zone->getTileInfo(); | ||||
| 		auto tiles = zone->area().getTiles(); | ||||
| 		for (auto tile : tiles) | ||||
| 		{ | ||||
| 			total += tile; | ||||
| @@ -488,7 +498,7 @@ void CZonePlacer::assignZones() | ||||
| 		zone->setPos(int3(total.x / size, total.y / size, total.z / size)); | ||||
| 	}; | ||||
|  | ||||
| 	int levels = gen->map->twoLevel ? 2 : 1; | ||||
| 	int levels = map.map().twoLevel ? 2 : 1; | ||||
|  | ||||
| 	/* | ||||
| 	1. Create Voronoi diagram | ||||
| @@ -510,7 +520,7 @@ void CZonePlacer::assignZones() | ||||
| 					else | ||||
| 						distances.push_back(std::make_pair(zone.second, std::numeric_limits<float>::max())); | ||||
| 				} | ||||
| 				boost::min_element(distances, compareByDistance)->first->addTile(pos); //closest tile belongs to zone | ||||
| 				boost::min_element(distances, compareByDistance)->first->area().add(pos); //closest tile belongs to zone | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -539,8 +549,8 @@ void CZonePlacer::assignZones() | ||||
| 						distances.push_back (std::make_pair(zone.second, std::numeric_limits<float>::max())); | ||||
| 				} | ||||
| 				auto zone = boost::min_element(distances, compareByDistance)->first; //closest tile belongs to zone | ||||
| 				zone->addTile(pos); | ||||
| 				gen->setZoneID(pos, zone->getId()); | ||||
| 				zone->area().add(pos); | ||||
| 				map.setZoneID(pos, zone->getId()); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -555,14 +565,14 @@ void CZonePlacer::assignZones() | ||||
| 		{ | ||||
| 			if (!CREATE_FULL_UNDERGROUND) | ||||
| 			{ | ||||
| 				auto discardTile = zone.second->collectDistantTiles((float)(zone.second->getSize() + 1)); | ||||
| 				for(auto& t : discardTile) | ||||
| 					zone.second->removeTile(t); | ||||
| 				auto discardTiles = collectDistantTiles(*zone.second, zone.second->getSize() + 1.f); | ||||
| 				for(auto& t : discardTiles) | ||||
| 					zone.second->area().erase(t); | ||||
| 			} | ||||
|  | ||||
| 			//make sure that terrain inside zone is not a rock | ||||
| 			//FIXME: reorder actions? | ||||
| 			zone.second->paintZoneTerrain (Terrain("subterra")); | ||||
| 			paintZoneTerrain(*zone.second, *rand, map, Terrain("subterra")); | ||||
| 		} | ||||
| 	} | ||||
| 	logGlobal->info("Finished zone colouring"); | ||||
|   | ||||
| @@ -10,32 +10,32 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "CMapGenerator.h" | ||||
| #include "float3.h" | ||||
| #include "../int3.h" | ||||
| #include "../GameConstants.h" | ||||
|  | ||||
| class CZoneGraph; | ||||
| class CMap; | ||||
| class CRandomGenerator; | ||||
| class CRmgTemplateZone; | ||||
| class CMapGenerator; | ||||
| class RmgMap; | ||||
| class Zone; | ||||
|  | ||||
| typedef std::vector<std::pair<TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>>> TZoneVector; | ||||
| typedef std::map <TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>> TZoneMap; | ||||
| typedef std::map <std::shared_ptr<CRmgTemplateZone>, float3> TForceVector; | ||||
| typedef std::map <std::shared_ptr<CRmgTemplateZone>, float> TDistanceVector; | ||||
| typedef std::vector<std::pair<TRmgTemplateZoneId, std::shared_ptr<Zone>>> TZoneVector; | ||||
| typedef std::map<TRmgTemplateZoneId, std::shared_ptr<Zone>> TZoneMap; | ||||
| typedef std::map<std::shared_ptr<Zone>, float3> TForceVector; | ||||
| typedef std::map<std::shared_ptr<Zone>, float> TDistanceVector; | ||||
|  | ||||
| class CZonePlacer | ||||
| { | ||||
| public: | ||||
| 	explicit CZonePlacer(CMapGenerator * gen); | ||||
| 	explicit CZonePlacer(RmgMap & map); | ||||
| 	int3 cords (const float3 f) const; | ||||
| 	float metric (const int3 &a, const int3 &b) const; | ||||
| 	float getDistance(float distance) const; //additional scaling without 0 divison | ||||
| 	~CZonePlacer(); | ||||
|  | ||||
| 	void placeZones(CRandomGenerator * rand); | ||||
| 	void assignZones(); | ||||
| 	void assignZones(CRandomGenerator * rand); | ||||
| 	 | ||||
| private: | ||||
| 	void prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const bool underground, CRandomGenerator * rand); | ||||
| @@ -56,5 +56,5 @@ private: | ||||
|     //float a1, b1, c1, a2, b2, c2; | ||||
| 	//CMap * map; | ||||
| 	//std::unique_ptr<CZoneGraph> graph; | ||||
| 	CMapGenerator * gen; | ||||
| 	RmgMap & map; | ||||
| }; | ||||
|   | ||||
							
								
								
									
										280
									
								
								lib/rmg/ConnectionsPlacer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								lib/rmg/ConnectionsPlacer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | ||||
| /* | ||||
|  * ConnectionsPlacer.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "ConnectionsPlacer.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "RmgMap.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
| #include "../mapObjects/CObjectClassesHandler.h" | ||||
| #include "RmgPath.h" | ||||
| #include "RmgObject.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "Functions.h" | ||||
| #include "RoadPlacer.h" | ||||
| #include "TileInfo.h" | ||||
| #include "WaterAdopter.h" | ||||
| #include "WaterProxy.h" | ||||
| #include "TownPlacer.h" | ||||
|  | ||||
| void ConnectionsPlacer::process() | ||||
| { | ||||
| 	collectNeighbourZones(); | ||||
| 	for(auto & c : dConnections) | ||||
| 	{ | ||||
| 		if(c.getZoneA() != zone.getId() && c.getZoneB() != zone.getId()) | ||||
| 			continue; | ||||
| 		 | ||||
| 		if(vstd::contains(dCompleted, c)) | ||||
| 			continue; | ||||
| 		 | ||||
| 		selfSideDirectConnection(c); | ||||
| 	} | ||||
| 	 | ||||
| 	createBorder(map, zone); | ||||
| 	 | ||||
| 	for(auto & c : dConnections) | ||||
| 	{ | ||||
| 		if(c.getZoneA() != zone.getId() && c.getZoneB() != zone.getId()) | ||||
| 			continue; | ||||
| 		 | ||||
| 		if(vstd::contains(dCompleted, c)) | ||||
| 			continue; | ||||
| 		 | ||||
| 		selfSideIndirectConnection(c); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ConnectionsPlacer::init() | ||||
| { | ||||
| 	DEPENDENCY(WaterAdopter); | ||||
| 	DEPENDENCY(TownPlacer); | ||||
| 	POSTFUNCTION(RoadPlacer); | ||||
| 	POSTFUNCTION(ObjectManager); | ||||
| 	 | ||||
| 	for(auto c : map.getMapGenOptions().getMapTemplate()->getConnections()) | ||||
| 		addConnection(c); | ||||
| } | ||||
|  | ||||
| void ConnectionsPlacer::addConnection(const rmg::ZoneConnection& connection) | ||||
| { | ||||
| 	dConnections.push_back(connection); | ||||
| } | ||||
|  | ||||
| void ConnectionsPlacer::otherSideConnection(const rmg::ZoneConnection & connection) | ||||
| { | ||||
| 	dCompleted.push_back(connection); | ||||
| } | ||||
|  | ||||
| void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & connection) | ||||
| { | ||||
| 	bool success = false; | ||||
| 	auto otherZoneId = (connection.getZoneA() == zone.getId() ? connection.getZoneB() : connection.getZoneA()); | ||||
| 	auto & otherZone = map.getZones().at(otherZoneId); | ||||
| 	 | ||||
| 	//1. Try to make direct connection | ||||
| 	//Do if it's not prohibited by terrain settings | ||||
| 	bool directProhibited = vstd::contains(Terrain::Manager::getInfo(zone.getTerrainType()).prohibitTransitions, otherZone->getTerrainType()) | ||||
| 						 || vstd::contains(Terrain::Manager::getInfo(otherZone->getTerrainType()).prohibitTransitions, zone.getTerrainType()); | ||||
| 	auto directConnectionIterator = dNeighbourZones.find(otherZoneId); | ||||
| 	if(!directProhibited && directConnectionIterator != dNeighbourZones.end()) | ||||
| 	{ | ||||
| 		int3 guardPos(-1, -1, -1); | ||||
| 		int3 borderPos; | ||||
| 		while(!directConnectionIterator->second.empty()) | ||||
| 		{ | ||||
| 			borderPos = *RandomGeneratorUtil::nextItem(directConnectionIterator->second, generator.rand); | ||||
| 			guardPos = zone.areaPossible().nearest(borderPos); | ||||
| 			assert(borderPos != guardPos); | ||||
| 			 | ||||
| 			auto safetyGap = rmg::Area({guardPos}); | ||||
| 			safetyGap.unite(safetyGap.getBorderOutside()); | ||||
| 			safetyGap.intersect(zone.areaPossible()); | ||||
| 			if(!safetyGap.empty()) | ||||
| 			{ | ||||
| 				safetyGap.intersect(otherZone->areaPossible()); | ||||
| 				if(safetyGap.empty()) | ||||
| 					break; //successfull position | ||||
| 			} | ||||
| 			 | ||||
| 			//failed position | ||||
| 			directConnectionIterator->second.erase(borderPos); | ||||
| 			guardPos = int3(-1, -1, -1); | ||||
| 		} | ||||
| 		 | ||||
| 		if(guardPos.valid()) | ||||
| 		{ | ||||
| 			assert(zone.getModificator<ObjectManager>()); | ||||
| 			auto & manager = *zone.getModificator<ObjectManager>(); | ||||
| 			auto * monsterType = manager.chooseGuard(connection.getGuardStrength(), true); | ||||
| 			 | ||||
| 			rmg::Area border(zone.getArea().getBorder()); | ||||
| 			border.unite(otherZone->getArea().getBorder()); | ||||
| 			 | ||||
| 			auto costFunction = [&border](const int3 & s, const int3 & d) | ||||
| 			{ | ||||
| 				return 1.f / (1.f + border.distanceSqr(d)); | ||||
| 			}; | ||||
| 			 | ||||
| 			auto ourArea = zone.areaPossible() + zone.freePaths(); | ||||
| 			auto theirArea = otherZone->areaPossible() + otherZone->freePaths(); | ||||
| 			theirArea.add(guardPos); | ||||
| 			rmg::Path ourPath(ourArea), theirPath(theirArea); | ||||
| 			ourPath.connect(zone.freePaths()); | ||||
| 			ourPath = ourPath.search(guardPos, true, costFunction); | ||||
| 			theirPath.connect(otherZone->freePaths()); | ||||
| 			theirPath = theirPath.search(guardPos, true, costFunction); | ||||
| 			 | ||||
| 			if(ourPath.valid() && theirPath.valid()) | ||||
| 			{ | ||||
| 				zone.connectPath(ourPath); | ||||
| 				otherZone->connectPath(theirPath); | ||||
| 				 | ||||
| 				if(monsterType) | ||||
| 				{ | ||||
| 					rmg::Object monster(*monsterType); | ||||
| 					monster.setPosition(guardPos); | ||||
| 					manager.placeObject(monster, false, true); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					zone.areaPossible().erase(guardPos); | ||||
| 					zone.freePaths().add(guardPos); | ||||
| 					map.setOccupied(guardPos, ETileType::FREE); | ||||
| 				} | ||||
| 				 | ||||
| 				assert(zone.getModificator<RoadPlacer>()); | ||||
| 				zone.getModificator<RoadPlacer>()->addRoadNode(guardPos); | ||||
| 				 | ||||
| 				assert(otherZone->getModificator<RoadPlacer>()); | ||||
| 				otherZone->getModificator<RoadPlacer>()->addRoadNode(borderPos); | ||||
| 				 | ||||
| 				assert(otherZone->getModificator<ConnectionsPlacer>()); | ||||
| 				otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection); | ||||
| 				 | ||||
| 				success = true; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//2. connect via water | ||||
| 	bool waterMode = map.getMapGenOptions().getWaterContent() != EWaterContent::NONE; | ||||
| 	if(waterMode && zone.isUnderground() == otherZone->isUnderground()) | ||||
| 	{ | ||||
| 		if(generator.getZoneWater() && generator.getZoneWater()->getModificator<WaterProxy>()) | ||||
| 		{ | ||||
| 			if(generator.getZoneWater()->getModificator<WaterProxy>()->waterKeepConnection(connection.getZoneA(), connection.getZoneB())) | ||||
| 			{ | ||||
| 				assert(otherZone->getModificator<ConnectionsPlacer>()); | ||||
| 				otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection); | ||||
| 				success = true; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	if(success) | ||||
| 		dCompleted.push_back(connection); | ||||
| } | ||||
|  | ||||
| void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & connection) | ||||
| { | ||||
| 	bool success = false; | ||||
| 	auto otherZoneId = (connection.getZoneA() == zone.getId() ? connection.getZoneB() : connection.getZoneA()); | ||||
| 	auto & otherZone = map.getZones().at(otherZoneId); | ||||
| 	 | ||||
| 	//3. place subterrain gates | ||||
| 	if(zone.isUnderground() != otherZone->isUnderground()) | ||||
| 	{ | ||||
| 		int3 zShift(0, 0, zone.getPos().z - otherZone->getPos().z); | ||||
| 		auto commonArea = zone.areaPossible() * (otherZone->areaPossible() + zShift); | ||||
| 		if(!commonArea.empty()) | ||||
| 		{ | ||||
| 			assert(zone.getModificator<ObjectManager>()); | ||||
| 			auto & manager = *zone.getModificator<ObjectManager>(); | ||||
| 			 | ||||
| 			assert(otherZone->getModificator<ObjectManager>()); | ||||
| 			auto & managerOther = *otherZone->getModificator<ObjectManager>(); | ||||
| 			 | ||||
| 			auto factory = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0); | ||||
| 			auto gate1 = factory->create(ObjectTemplate()); | ||||
| 			auto gate2 = factory->create(ObjectTemplate()); | ||||
| 			rmg::Object rmgGate1(*gate1), rmgGate2(*gate2); | ||||
| 			rmgGate1.setTemplate(zone.getTerrainType()); | ||||
| 			rmgGate2.setTemplate(otherZone->getTerrainType()); | ||||
| 			bool guarded1 = manager.addGuard(rmgGate1, connection.getGuardStrength(), true); | ||||
| 			bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true); | ||||
| 			int minDist = 3; | ||||
| 			 | ||||
| 			rmg::Path path2(otherZone->area()); | ||||
| 			rmg::Path path1 = manager.placeAndConnectObject(commonArea, rmgGate1, [this, minDist, &path2, &rmgGate1, &zShift, guarded2, &managerOther, &rmgGate2	](const int3 & tile) | ||||
| 			{ | ||||
| 				auto ti = map.getTile(tile); | ||||
| 				float dist = ti.getNearestObjectDistance(); | ||||
| 				if(dist < minDist) | ||||
| 					return -1.f; | ||||
| 				 | ||||
| 				rmg::Area toPlace(rmgGate1.getArea() + rmgGate1.getAccessibleArea()); | ||||
| 				toPlace.translate(-zShift); | ||||
| 				 | ||||
| 				path2 = managerOther.placeAndConnectObject(toPlace, rmgGate2, minDist, guarded2, true, false); | ||||
| 				 | ||||
| 				return path2.valid() ? 1.f : -1.f; | ||||
| 			}, guarded1, true, false); | ||||
| 			 | ||||
| 			if(path1.valid() && path2.valid()) | ||||
| 			{ | ||||
| 				zone.connectPath(path1); | ||||
| 				otherZone->connectPath(path2); | ||||
| 				 | ||||
| 				manager.placeObject(rmgGate1, guarded1, true); | ||||
| 				managerOther.placeObject(rmgGate2, guarded2, true); | ||||
| 				 | ||||
| 				assert(otherZone->getModificator<ConnectionsPlacer>()); | ||||
| 				otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection); | ||||
| 				 | ||||
| 				success = true; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//4. place monoliths/portals | ||||
| 	if(!success) | ||||
| 	{ | ||||
| 		auto factory = VLC->objtypeh->getHandlerFor(Obj::MONOLITH_TWO_WAY, generator.getNextMonlithIndex()); | ||||
| 		auto teleport1 = factory->create(ObjectTemplate()); | ||||
| 		auto teleport2 = factory->create(ObjectTemplate()); | ||||
| 		 | ||||
| 		zone.getModificator<ObjectManager>()->addRequiredObject(teleport1, connection.getGuardStrength()); | ||||
| 		otherZone->getModificator<ObjectManager>()->addRequiredObject(teleport2, connection.getGuardStrength()); | ||||
| 		 | ||||
| 		assert(otherZone->getModificator<ConnectionsPlacer>()); | ||||
| 		otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection); | ||||
| 		 | ||||
| 		success = true; | ||||
| 	} | ||||
| 	 | ||||
| 	if(success) | ||||
| 		dCompleted.push_back(connection); | ||||
| } | ||||
|  | ||||
| void ConnectionsPlacer::collectNeighbourZones() | ||||
| { | ||||
| 	auto border = zone.area().getBorderOutside(); | ||||
| 	for(auto & i : border) | ||||
| 	{ | ||||
| 		if(!map.isOnMap(i)) | ||||
| 			continue; | ||||
| 		 | ||||
| 		auto zid = map.getZoneID(i); | ||||
| 		assert(zid != zone.getId()); | ||||
| 		dNeighbourZones[zid].insert(i); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										35
									
								
								lib/rmg/ConnectionsPlacer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								lib/rmg/ConnectionsPlacer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| /* | ||||
|  * ConnectionsPlacer.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 "Zone.h" | ||||
| #include "RmgArea.h" | ||||
|  | ||||
| class ConnectionsPlacer: public Modificator | ||||
| { | ||||
| public: | ||||
| 	MODIFICATOR(ConnectionsPlacer); | ||||
| 	 | ||||
| 	void process() override; | ||||
| 	void init() override; | ||||
| 	 | ||||
| 	void addConnection(const rmg::ZoneConnection& connection); | ||||
| 	 | ||||
| 	void selfSideDirectConnection(const rmg::ZoneConnection & connection); | ||||
| 	void selfSideIndirectConnection(const rmg::ZoneConnection & connection); | ||||
| 	void otherSideConnection(const rmg::ZoneConnection & connection); | ||||
| 	 | ||||
| protected: | ||||
| 	void collectNeighbourZones(); | ||||
| 	 | ||||
| protected: | ||||
| 	std::vector<rmg::ZoneConnection> dConnections, dCompleted; | ||||
| 	std::map<TRmgTemplateZoneId, rmg::Tileset> dNeighbourZones; | ||||
| }; | ||||
							
								
								
									
										220
									
								
								lib/rmg/Functions.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								lib/rmg/Functions.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,220 @@ | ||||
| /* | ||||
|  * Functions.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "Functions.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "RoadPlacer.h" | ||||
| #include "TreasurePlacer.h" | ||||
| #include "ConnectionsPlacer.h" | ||||
| #include "TownPlacer.h" | ||||
| #include "WaterProxy.h" | ||||
| #include "WaterRoutes.h" | ||||
| #include "RmgMap.h" | ||||
| #include "TileInfo.h" | ||||
| #include "RmgPath.h" | ||||
| #include "../CTownHandler.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../mapObjects/CommonConstructors.h" | ||||
| #include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h | ||||
| #include "../VCMI_Lib.h" | ||||
|  | ||||
| void createModificators(RmgMap & map) | ||||
| { | ||||
| 	for(auto & z : map.getZones()) | ||||
| 	{ | ||||
| 		auto & zone = *z.second; | ||||
| 		switch(zone.getType()) | ||||
| 		{ | ||||
| 			case ETemplateZoneType::WATER: | ||||
| 				zone.addModificator<ObjectManager>(); | ||||
| 				zone.addModificator<TreasurePlacer>(); | ||||
| 				zone.addModificator<WaterProxy>(); | ||||
| 				zone.addModificator<WaterRoutes>(); | ||||
| 				break; | ||||
| 				 | ||||
| 			default: | ||||
| 				zone.addModificator<TownPlacer>(); | ||||
| 				zone.addModificator<ObjectManager>(); | ||||
| 				zone.addModificator<ConnectionsPlacer>(); | ||||
| 				zone.addModificator<TreasurePlacer>(); | ||||
| 				zone.addModificator<RoadPlacer>(); | ||||
| 				break; | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| rmg::Tileset collectDistantTiles(const Zone& zone, int distance) | ||||
| { | ||||
| 	int distanceSq = distance * distance; | ||||
| 	auto subarea = zone.getArea().getSubarea([&zone, distanceSq](const int3 & t) | ||||
| 	{ | ||||
| 		return t.dist2dSQ(zone.getPos()) > distanceSq; | ||||
| 	}); | ||||
| 	return subarea.getTiles(); | ||||
| } | ||||
|  | ||||
| void createBorder(RmgMap & gen, Zone & zone) | ||||
| { | ||||
| 	rmg::Area borderArea(zone.getArea().getBorder()); | ||||
| 	rmg::Area borderOutsideArea(zone.getArea().getBorderOutside()); | ||||
| 	auto blockBorder = borderArea.getSubarea([&gen, &borderOutsideArea](const int3 & t) | ||||
| 	{ | ||||
| 		auto tile = borderOutsideArea.nearest(t); | ||||
| 		return gen.isOnMap(tile) && gen.getZones()[gen.getZoneID(tile)]->getType() != ETemplateZoneType::WATER; | ||||
| 	}); | ||||
| 	 | ||||
| 	for(auto & tile : blockBorder.getTilesVector()) | ||||
| 	{ | ||||
| 		if(gen.isPossible(tile)) | ||||
| 		{ | ||||
| 			gen.setOccupied(tile, ETileType::BLOCKED); | ||||
| 			zone.areaPossible().erase(tile); | ||||
| 		} | ||||
| 		 | ||||
| 		gen.foreachDirectNeighbour(tile, [&gen, &zone](int3 &nearbyPos) | ||||
| 		{ | ||||
| 			if(gen.isPossible(nearbyPos) && gen.getZoneID(nearbyPos) == zone.getId()) | ||||
| 			{ | ||||
| 				gen.setOccupied(nearbyPos, ETileType::BLOCKED); | ||||
| 				zone.areaPossible().erase(nearbyPos); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void paintZoneTerrain(const Zone & zone, CRandomGenerator & generator, RmgMap & map, const Terrain & terrainType) | ||||
| { | ||||
| 	auto v = zone.getArea().getTilesVector(); | ||||
| 	map.getEditManager()->getTerrainSelection().setSelection(v); | ||||
| 	map.getEditManager()->drawTerrain(terrainType, &generator); | ||||
| } | ||||
|  | ||||
| int chooseRandomAppearance(CRandomGenerator & generator, si32 ObjID, const Terrain & terrain) | ||||
| { | ||||
| 	auto factories = VLC->objtypeh->knownSubObjects(ObjID); | ||||
| 	vstd::erase_if(factories, [ObjID, &terrain](si32 f) | ||||
| 	{ | ||||
| 		return VLC->objtypeh->getHandlerFor(ObjID, f)->getTemplates(terrain).empty(); | ||||
| 	}); | ||||
| 	 | ||||
| 	return *RandomGeneratorUtil::nextItem(factories, generator); | ||||
| } | ||||
|  | ||||
| void initTerrainType(Zone & zone, CMapGenerator & gen) | ||||
| { | ||||
| 	if(zone.getType()==ETemplateZoneType::WATER) | ||||
| 	{ | ||||
| 		//collect all water terrain types | ||||
| 		std::vector<Terrain> waterTerrains; | ||||
| 		for(auto & terrain : Terrain::Manager::terrains()) | ||||
| 			if(terrain.isWater()) | ||||
| 				waterTerrains.push_back(terrain); | ||||
| 		 | ||||
| 		zone.setTerrainType(*RandomGeneratorUtil::nextItem(waterTerrains, gen.rand)); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if(zone.isMatchTerrainToTown() && zone.getTownType() != ETownType::NEUTRAL) | ||||
| 		{ | ||||
| 			zone.setTerrainType((*VLC->townh)[zone.getTownType()]->nativeTerrain); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			zone.setTerrainType(*RandomGeneratorUtil::nextItem(zone.getTerrainTypes(), gen.rand)); | ||||
| 		} | ||||
| 		 | ||||
| 		//TODO: allow new types of terrain? | ||||
| 		{ | ||||
| 			if(zone.isUnderground()) | ||||
| 			{ | ||||
| 				if(!vstd::contains(gen.getConfig().terrainUndergroundAllowed, zone.getTerrainType())) | ||||
| 				{ | ||||
| 					//collect all underground terrain types | ||||
| 					std::vector<Terrain> undegroundTerrains; | ||||
| 					for(auto & terrain : Terrain::Manager::terrains()) | ||||
| 						if(terrain.isUnderground()) | ||||
| 							undegroundTerrains.push_back(terrain); | ||||
| 					 | ||||
| 					zone.setTerrainType(*RandomGeneratorUtil::nextItem(undegroundTerrains, gen.rand)); | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				if(vstd::contains(gen.getConfig().terrainGroundProhibit, zone.getTerrainType()) || zone.getTerrainType().isUnderground()) | ||||
| 					zone.setTerrainType(Terrain("dirt")); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void createObstaclesCommon2(RmgMap & map, CRandomGenerator & generator) | ||||
| { | ||||
| 	if(map.map().twoLevel) | ||||
| 	{ | ||||
| 		//finally mark rock tiles as occupied, spawn no obstacles there | ||||
| 		for(int x = 0; x < map.map().width; x++) | ||||
| 		{ | ||||
| 			for(int y = 0; y < map.map().height; y++) | ||||
| 			{ | ||||
| 				int3 tile(x, y, 1); | ||||
| 				if(!map.map().getTile(tile).terType.isPassable()) | ||||
| 				{ | ||||
| 					map.setOccupied(tile, ETileType::USED); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//tighten obstacles to improve visuals | ||||
| 	 | ||||
| 	/*for (int i = 0; i < 3; ++i) | ||||
| 	{ | ||||
| 		int blockedTiles = 0; | ||||
| 		int freeTiles = 0; | ||||
| 		 | ||||
| 		for (int z = 0; z < (map.map().twoLevel ? 2 : 1); z++) | ||||
| 		{ | ||||
| 			for (int x = 0; x < map.map().width; x++) | ||||
| 			{ | ||||
| 				for (int y = 0; y < map.map().height; y++) | ||||
| 				{ | ||||
| 					int3 tile(x, y, z); | ||||
| 					if (!map.isPossible(tile)) //only possible tiles can change | ||||
| 						continue; | ||||
| 					 | ||||
| 					int blockedNeighbours = 0; | ||||
| 					int freeNeighbours = 0; | ||||
| 					map.foreach_neighbour(tile, [&map, &blockedNeighbours, &freeNeighbours](int3 &pos) | ||||
| 					{ | ||||
| 						if (map.isBlocked(pos)) | ||||
| 							blockedNeighbours++; | ||||
| 						if (map.isFree(pos)) | ||||
| 							freeNeighbours++; | ||||
| 					}); | ||||
| 					if (blockedNeighbours > 4) | ||||
| 					{ | ||||
| 						map.setOccupied(tile, ETileType::BLOCKED); | ||||
| 						blockedTiles++; | ||||
| 					} | ||||
| 					else if (freeNeighbours > 4) | ||||
| 					{ | ||||
| 						map.setOccupied(tile, ETileType::FREE); | ||||
| 						freeTiles++; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		logGlobal->trace("Set %d tiles to BLOCKED and %d tiles to FREE", blockedTiles, freeTiles); | ||||
| 	}*/ | ||||
| } | ||||
							
								
								
									
										48
									
								
								lib/rmg/Functions.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								lib/rmg/Functions.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| /* | ||||
|  * Functions.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 "Zone.h" | ||||
| #include <boost/heap/priority_queue.hpp> //A* | ||||
|  | ||||
| class RmgMap; | ||||
| class ObjectManager; | ||||
| class ObjectTemplate; | ||||
| class CMapGenerator; | ||||
|  | ||||
| class rmgException : public std::exception | ||||
| { | ||||
| 	std::string msg; | ||||
| public: | ||||
| 	explicit rmgException(const std::string& _Message) : msg(_Message) | ||||
| 	{ | ||||
| 	} | ||||
| 	 | ||||
| 	virtual ~rmgException() throw () | ||||
| 	{ | ||||
| 	}; | ||||
| 	 | ||||
| 	const char *what() const throw () override | ||||
| 	{ | ||||
| 		return msg.c_str(); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| rmg::Tileset collectDistantTiles(const Zone & zone, int distance); | ||||
|  | ||||
| void createBorder(RmgMap & gen, Zone & zone); | ||||
|  | ||||
| void paintZoneTerrain(const Zone & zone, CRandomGenerator & generator, RmgMap & map, const Terrain & terrainType); | ||||
|  | ||||
| void initTerrainType(Zone & zone, CMapGenerator & gen); | ||||
|  | ||||
| int chooseRandomAppearance(CRandomGenerator & generator, si32 ObjID, const Terrain & terrain); | ||||
|  | ||||
							
								
								
									
										417
									
								
								lib/rmg/ObjectManager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										417
									
								
								lib/rmg/ObjectManager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,417 @@ | ||||
| /* | ||||
|  * ObjectManager.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "TileInfo.h" | ||||
| #include "RmgMap.h" | ||||
| #include "RoadPlacer.h" | ||||
| #include "RiverPlacer.h" | ||||
| #include "WaterAdopter.h" | ||||
| #include "../CCreatureHandler.h" | ||||
| #include "../mapObjects/CommonConstructors.h" | ||||
| #include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
| #include "Functions.h" | ||||
| #include "RmgObject.h" | ||||
|  | ||||
| void ObjectManager::process() | ||||
| { | ||||
| 	zone.fractalize(); | ||||
| 		createRequiredObjects(); | ||||
| } | ||||
|  | ||||
| void ObjectManager::init() | ||||
| { | ||||
| 	DEPENDENCY(WaterAdopter); | ||||
| 	POSTFUNCTION(RoadPlacer); | ||||
| } | ||||
|  | ||||
| void ObjectManager::addRequiredObject(CGObjectInstance * obj, si32 strength) | ||||
| { | ||||
| 	requiredObjects.push_back(std::make_pair(obj, strength)); | ||||
| } | ||||
|  | ||||
| void ObjectManager::addCloseObject(CGObjectInstance * obj, si32 strength) | ||||
| { | ||||
| 	closeObjects.push_back(std::make_pair(obj, strength)); | ||||
| } | ||||
|  | ||||
| void ObjectManager::addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget) | ||||
| { | ||||
| 	nearbyObjects.push_back(std::make_pair(obj, nearbyTarget)); | ||||
| } | ||||
|  | ||||
| void ObjectManager::updateDistances(const rmg::Object & obj) | ||||
| { | ||||
| 	for (auto tile : zone.areaPossible().getTiles()) //don't need to mark distance for not possible tiles | ||||
| 	{ | ||||
| 		ui32 d = obj.getArea().distanceSqr(tile); //optimization, only relative distance is interesting | ||||
| 		map.setNearestObjectDistance(tile, std::min((float)d, map.getNearestObjectDistance(tile))); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| const rmg::Area & ObjectManager::getVisitableArea() const | ||||
| { | ||||
| 	return objectsVisitableArea; | ||||
| } | ||||
|  | ||||
| int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool optimizer) const | ||||
| { | ||||
| 	float bestWeight = 0.f; | ||||
| 	int3 result(-1, -1, -1); | ||||
| 	 | ||||
| 	for(const auto & tile : searchArea.getTiles()) | ||||
| 	{ | ||||
| 		obj.setPosition(tile); | ||||
| 		 | ||||
| 		if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea())) | ||||
| 			continue; | ||||
| 		 | ||||
| 		float weight = weightFunction(tile); | ||||
| 		if(weight > bestWeight) | ||||
| 		{ | ||||
| 			bestWeight = weight; | ||||
| 			result = tile; | ||||
| 			if(!optimizer) | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| 	if(result.valid()) | ||||
| 		obj.setPosition(result); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool optimizer) const | ||||
| { | ||||
| 	return findPlaceForObject(searchArea, obj, [this, min_dist](const int3 & tile) | ||||
| 	{ | ||||
| 		auto ti = map.getTile(tile); | ||||
| 		float dist = ti.getNearestObjectDistance(); | ||||
| 		if(dist < min_dist) | ||||
| 			return -1.f; | ||||
| 		 | ||||
| 		return dist; | ||||
| 	}, optimizer); | ||||
| } | ||||
|  | ||||
| rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, bool optimizer) const | ||||
| { | ||||
| 	return placeAndConnectObject(searchArea, obj, [this, min_dist](const int3 & tile) | ||||
| 	{ | ||||
| 		auto ti = map.getTile(tile); | ||||
| 		float dist = ti.getNearestObjectDistance(); | ||||
| 		if(dist < min_dist) | ||||
| 			return -1.f; | ||||
| 		 | ||||
| 		return dist; | ||||
| 	}, isGuarded, onlyStraight, optimizer); | ||||
| } | ||||
|  | ||||
| rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool isGuarded, bool onlyStraight, bool optimizer) const | ||||
| { | ||||
| 	int3 pos; | ||||
| 	auto possibleArea = searchArea; | ||||
| 	while(true) | ||||
| 	{ | ||||
| 		pos = findPlaceForObject(possibleArea, obj, weightFunction, optimizer); | ||||
| 		if(!pos.valid()) | ||||
| 		{ | ||||
| 			return rmg::Path::invalid(); | ||||
| 		} | ||||
| 		possibleArea.erase(pos); //do not place again at this point | ||||
| 		auto accessibleArea = obj.getAccessibleArea(isGuarded) * (zone.areaPossible() + zone.freePaths()); | ||||
| 		//we should exclude tiles which will be covered | ||||
| 		if(isGuarded) | ||||
| 		{ | ||||
| 			auto & guardedArea = obj.instances().back()->getAccessibleArea(); | ||||
| 			accessibleArea.intersect(guardedArea); | ||||
| 		} | ||||
| 		 | ||||
| 		auto path = zone.searchPath(accessibleArea, onlyStraight, [&obj, isGuarded](const int3 & t) | ||||
| 		{ | ||||
| 			if(isGuarded) | ||||
| 			{ | ||||
| 				auto & guardedArea =  obj.instances().back()->getAccessibleArea(); | ||||
| 				auto & unguardedArea = obj.getAccessibleArea(isGuarded); | ||||
| 				if(unguardedArea.contains(t) && !guardedArea.contains(t)) | ||||
| 					return false; | ||||
| 			} | ||||
| 			return !obj.getArea().contains(t); | ||||
| 		}); | ||||
| 		 | ||||
| 		if(path.valid()) | ||||
| 		{ | ||||
| 			return path; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool ObjectManager::createRequiredObjects() | ||||
| { | ||||
| 	logGlobal->trace("Creating required objects"); | ||||
| 	 | ||||
| 	for(const auto & object : requiredObjects) | ||||
| 	{ | ||||
| 		auto * obj = object.first; | ||||
| 		int3 pos; | ||||
| 		rmg::Object rmgObject(*obj); | ||||
| 		rmgObject.setTemplate(zone.getTerrainType()); | ||||
| 		bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY)); | ||||
| 		auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, true); | ||||
| 		 | ||||
| 		if(!path.valid()) | ||||
| 		{ | ||||
| 			logGlobal->error("Failed to fill zone %d due to lack of space", zone.getId()); | ||||
| 			return false; | ||||
| 		} | ||||
| 		 | ||||
| 		zone.connectPath(path); | ||||
| 		placeObject(rmgObject, guarded, true); | ||||
| 		 | ||||
| 		for(const auto & nearby : nearbyObjects) | ||||
| 		{ | ||||
| 			if(nearby.second != obj) | ||||
| 				continue; | ||||
| 			 | ||||
| 			rmg::Object rmgNearObject(*nearby.first); | ||||
| 			rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside()); | ||||
| 			possibleArea.intersect(zone.areaPossible()); | ||||
| 			if(possibleArea.empty()) | ||||
| 			{ | ||||
| 				rmgNearObject.clear(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			 | ||||
| 			rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), generator.rand)); | ||||
| 			placeObject(rmgNearObject, false, false); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	for(const auto & object : closeObjects) | ||||
| 	{ | ||||
| 		auto * obj = object.first; | ||||
| 		int3 pos; | ||||
| 		auto possibleArea = zone.areaPossible(); | ||||
| 		rmg::Object rmgObject(*obj); | ||||
| 		rmgObject.setTemplate(zone.getTerrainType()); | ||||
| 		bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY)); | ||||
| 		auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, | ||||
| 										  [this, &rmgObject](const int3 & tile) | ||||
| 		{ | ||||
| 			float dist = rmgObject.getArea().distanceSqr(zone.getPos()); | ||||
| 			dist *= (dist > 12.f * 12.f) ? 10.f : 1.f; //tiles closer 12 are preferrable | ||||
| 			dist = 1000000.f - dist; //some big number | ||||
| 			return dist + map.getNearestObjectDistance(tile); | ||||
| 		}, guarded, false, true); | ||||
| 		 | ||||
| 		if(!path.valid()) | ||||
| 		{ | ||||
| 			logGlobal->error("Failed to fill zone %d due to lack of space", zone.getId()); | ||||
| 			return false; | ||||
| 		} | ||||
| 		 | ||||
| 		zone.connectPath(path); | ||||
| 		placeObject(rmgObject, guarded, true); | ||||
| 		 | ||||
| 		for(const auto & nearby : nearbyObjects) | ||||
| 		{ | ||||
| 			if(nearby.second != obj) | ||||
| 				continue; | ||||
| 			 | ||||
| 			rmg::Object rmgNearObject(*nearby.first); | ||||
| 			rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside()); | ||||
| 			possibleArea.intersect(zone.areaPossible()); | ||||
| 			if(possibleArea.empty()) | ||||
| 			{ | ||||
| 				rmgNearObject.clear(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			 | ||||
| 			rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), generator.rand)); | ||||
| 			placeObject(rmgNearObject, false, false); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//create object on specific positions | ||||
| 	//TODO: implement guards | ||||
| 	for (const auto &obj : instantObjects) | ||||
| 	{ | ||||
| 		rmg::Object rmgObject(*obj.first); | ||||
| 		rmgObject.setPosition(obj.second); | ||||
| 		placeObject(rmgObject, false, false); | ||||
| 	} | ||||
| 	 | ||||
| 	requiredObjects.clear(); | ||||
| 	closeObjects.clear(); | ||||
| 	nearbyObjects.clear(); | ||||
| 	instantObjects.clear(); | ||||
| 	 | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance) | ||||
| {	 | ||||
| 	object.finalize(map); | ||||
| 	zone.areaPossible().subtract(object.getArea()); | ||||
| 	bool keepVisitable = zone.freePaths().contains(object.getVisitablePosition()); | ||||
| 	zone.freePaths().subtract(object.getArea()); //just to avoid areas overlapping | ||||
| 	if(keepVisitable) | ||||
| 		zone.freePaths().add(object.getVisitablePosition()); | ||||
| 	zone.areaUsed().unite(object.getArea()); | ||||
| 	zone.areaUsed().erase(object.getVisitablePosition()); | ||||
| 	 | ||||
| 	if(guarded) | ||||
| 	{ | ||||
| 		auto guardedArea = object.instances().back()->getAccessibleArea(); | ||||
| 		guardedArea.add(object.instances().back()->getVisitablePosition()); | ||||
| 		auto areaToBlock = object.getAccessibleArea(true); | ||||
| 		areaToBlock.subtract(guardedArea); | ||||
| 		zone.areaPossible().subtract(areaToBlock); | ||||
| 		for(auto & i : areaToBlock.getTilesVector()) | ||||
| 			if(map.isOnMap(i) && map.isPossible(i)) | ||||
| 				map.setOccupied(i, ETileType::BLOCKED); | ||||
| 	} | ||||
| 	 | ||||
| 	if(updateDistance) | ||||
| 		updateDistances(object); | ||||
| 	 | ||||
| 	for(auto * instance : object.instances()) | ||||
| 	{ | ||||
| 		objectsVisitableArea.add(instance->getVisitablePosition()); | ||||
| 		objects.push_back(&instance->object()); | ||||
| 		if(auto * m = zone.getModificator<RoadPlacer>()) | ||||
| 		{ | ||||
| 			if(instance->object().appearance.isVisitableFromTop()) | ||||
| 				m->areaForRoads().add(instance->getVisitablePosition()); | ||||
| 			else | ||||
| 			{ | ||||
| 				m->areaIsolated().add(instance->getVisitablePosition() + int3(0, -1, 0)); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	switch(object.instances().front()->object().ID) | ||||
| 	{ | ||||
| 		case Obj::TOWN: | ||||
| 		case Obj::RANDOM_TOWN: | ||||
| 		case Obj::MONOLITH_TWO_WAY: | ||||
| 		case Obj::MONOLITH_ONE_WAY_ENTRANCE: | ||||
| 		case Obj::MONOLITH_ONE_WAY_EXIT: | ||||
| 		case Obj::SUBTERRANEAN_GATE: | ||||
| 		case Obj::SHIPYARD: | ||||
| 			if(auto * m = zone.getModificator<RoadPlacer>()) | ||||
| 				m->addRoadNode(object.instances().front()->getVisitablePosition()); | ||||
| 			break; | ||||
| 			 | ||||
| 		case Obj::WATER_WHEEL: | ||||
| 			if(auto * m = zone.getModificator<RiverPlacer>()) | ||||
| 				m->addRiverNode(object.instances().front()->getVisitablePosition()); | ||||
| 			break; | ||||
| 			 | ||||
| 		default: | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CGCreature * ObjectManager::chooseGuard(si32 strength, bool zoneGuard) | ||||
| { | ||||
| 	//precalculate actual (randomized) monster strength based on this post | ||||
| 	//http://forum.vcmi.eu/viewtopic.php?p=12426#12426 | ||||
| 	 | ||||
| 	int mapMonsterStrength = map.getMapGenOptions().getMonsterStrength(); | ||||
| 	int monsterStrength = (zoneGuard ? 0 : zone.zoneMonsterStrength) + mapMonsterStrength - 1; //array index from 0 to 4 | ||||
| 	static const std::array<int, 5> value1{2500, 1500, 1000, 500, 0}; | ||||
| 	static const std::array<int, 5> value2{7500, 7500, 7500, 5000, 5000}; | ||||
| 	static const std::array<float, 5> multiplier1{0.5, 0.75, 1.0, 1.5, 1.5}; | ||||
| 	static const std::array<float, 5> multiplier2{0.5, 0.75, 1.0, 1.0, 1.5}; | ||||
| 	 | ||||
| 	int strength1 = static_cast<int>(std::max(0.f, (strength - value1.at(monsterStrength)) * multiplier1.at(monsterStrength))); | ||||
| 	int strength2 = static_cast<int>(std::max(0.f, (strength - value2.at(monsterStrength)) * multiplier2.at(monsterStrength))); | ||||
| 	 | ||||
| 	strength = strength1 + strength2; | ||||
| 	if (strength < generator.getConfig().minGuardStrength) | ||||
| 		return nullptr; //no guard at all | ||||
| 	 | ||||
| 	CreatureID creId = CreatureID::NONE; | ||||
| 	int amount = 0; | ||||
| 	std::vector<CreatureID> possibleCreatures; | ||||
| 	for(auto cre : VLC->creh->objects) | ||||
| 	{ | ||||
| 		if(cre->special) | ||||
| 			continue; | ||||
| 		if(!cre->AIValue) //bug #2681 | ||||
| 			continue; | ||||
| 		if(!vstd::contains(zone.getMonsterTypes(), cre->faction)) | ||||
| 			continue; | ||||
| 		if(((si32)(cre->AIValue * (cre->ammMin + cre->ammMax) / 2) < strength) && (strength < (si32)cre->AIValue * 100)) //at least one full monster. size between average size of given stack and 100 | ||||
| 		{ | ||||
| 			possibleCreatures.push_back(cre->idNumber); | ||||
| 		} | ||||
| 	} | ||||
| 	if(possibleCreatures.size()) | ||||
| 	{ | ||||
| 		creId = *RandomGeneratorUtil::nextItem(possibleCreatures, generator.rand); | ||||
| 		amount = strength / VLC->creh->objects[creId]->AIValue; | ||||
| 		if (amount >= 4) | ||||
| 			amount = static_cast<int>(amount * generator.rand.nextDouble(0.75, 1.25)); | ||||
| 	} | ||||
| 	else //just pick any available creature | ||||
| 	{ | ||||
| 		creId = CreatureID(132); //Azure Dragon | ||||
| 		amount = strength / VLC->creh->objects[creId]->AIValue; | ||||
| 	} | ||||
| 	 | ||||
| 	auto guardFactory = VLC->objtypeh->getHandlerFor(Obj::MONSTER, creId); | ||||
| 	 | ||||
| 	auto guard = (CGCreature *) guardFactory->create(ObjectTemplate()); | ||||
| 	guard->character = CGCreature::HOSTILE; | ||||
| 	auto  hlp = new CStackInstance(creId, amount); | ||||
| 	//will be set during initialization | ||||
| 	guard->putStack(SlotID(0), hlp); | ||||
| 	return guard; | ||||
| } | ||||
|  | ||||
| bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard) | ||||
| { | ||||
| 	auto * guard = chooseGuard(strength, zoneGuard); | ||||
| 	if(!guard) | ||||
| 		return false; | ||||
| 	 | ||||
| 	rmg::Area visitablePos({object.getVisitablePosition()}); | ||||
| 	visitablePos.unite(visitablePos.getBorderOutside()); | ||||
| 	 | ||||
| 	auto accessibleArea = object.getAccessibleArea(); | ||||
| 	accessibleArea.intersect(visitablePos); | ||||
| 	if(accessibleArea.empty()) | ||||
| 	{ | ||||
| 		delete guard; | ||||
| 		return false; | ||||
| 	} | ||||
| 	auto guardTiles = accessibleArea.getTilesVector(); | ||||
| 	auto guardPos = *std::min_element(guardTiles.begin(), guardTiles.end(), [&object](const int3 & l, const int3 & r) | ||||
| 	{ | ||||
| 		auto p = object.getVisitablePosition(); | ||||
| 		if(l.y > r.y) | ||||
| 			return true; | ||||
| 		 | ||||
| 		if(l.y == r.y) | ||||
| 			return abs(l.x - p.x) < abs(r.x - p.x); | ||||
| 		 | ||||
| 		return false; | ||||
| 	}); | ||||
| 	 | ||||
| 	auto & instance = object.addInstance(*guard); | ||||
| 	instance.setPosition(guardPos - object.getPosition()); | ||||
| 		 | ||||
| 	return true; | ||||
| } | ||||
							
								
								
									
										56
									
								
								lib/rmg/ObjectManager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								lib/rmg/ObjectManager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| /* | ||||
|  * ObjectManager.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 "Zone.h" | ||||
| #include "RmgObject.h" | ||||
|  | ||||
| class CGObjectInstance; | ||||
| class ObjectTemplate; | ||||
| class CGCreature; | ||||
|  | ||||
| class ObjectManager: public Modificator | ||||
| { | ||||
| public: | ||||
| 	MODIFICATOR(ObjectManager); | ||||
| 	 | ||||
| 	void process() override; | ||||
| 	void init() override; | ||||
| 	 | ||||
| 	void addRequiredObject(CGObjectInstance * obj, si32 guardStrength=0); | ||||
| 	void addCloseObject(CGObjectInstance * obj, si32 guardStrength = 0); | ||||
| 	void addNearbyObject(CGObjectInstance * obj, CGObjectInstance * nearbyTarget); | ||||
| 		 | ||||
| 	bool createRequiredObjects(); | ||||
| 	 | ||||
| 	int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool optimizer) const; | ||||
| 	int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool optimizer) const; | ||||
| 	 | ||||
| 	rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, bool optimizer) const; | ||||
| 	rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool isGuarded, bool onlyStraight, bool optimizer) const; | ||||
| 	 | ||||
| 	CGCreature * chooseGuard(si32 strength, bool zoneGuard = false); | ||||
| 	bool addGuard(rmg::Object & object, si32 strength, bool zoneGuard = false); | ||||
| 	void placeObject(rmg::Object & object, bool guarded, bool updateDistance); | ||||
| 	 | ||||
| 	void updateDistances(const rmg::Object & obj); | ||||
| 	 | ||||
| 	const rmg::Area & getVisitableArea() const; | ||||
| 	 | ||||
| protected: | ||||
| 	//content info | ||||
| 	std::vector<std::pair<CGObjectInstance*, ui32>> requiredObjects; | ||||
| 	std::vector<std::pair<CGObjectInstance*, ui32>> closeObjects; | ||||
| 	std::vector<std::pair<CGObjectInstance*, int3>> instantObjects; | ||||
| 	std::vector<std::pair<CGObjectInstance*, CGObjectInstance*>> nearbyObjects; | ||||
| 	std::vector<CGObjectInstance*> objects; | ||||
| 	rmg::Area objectsVisitableArea; | ||||
| }; | ||||
							
								
								
									
										188
									
								
								lib/rmg/ObstaclePlacer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								lib/rmg/ObstaclePlacer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| /* | ||||
|  * ObstaclePlacer.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "../mapObjects/CObjectClassesHandler.h" | ||||
| #include "ObstaclePlacer.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "TreasurePlacer.h" | ||||
| #include "RockPlacer.h" | ||||
| #include "WaterRoutes.h" | ||||
| #include "WaterProxy.h" | ||||
| #include "RoadPlacer.h" | ||||
| #include "RiverPlacer.h" | ||||
| #include "RmgMap.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "../CRandomGenerator.h" | ||||
| #include "Functions.h" | ||||
|  | ||||
| void ObstaclePlacer::process() | ||||
| { | ||||
| 	auto * manager = zone.getModificator<ObjectManager>(); | ||||
| 	if(!manager) | ||||
| 		return; | ||||
| 	 | ||||
| 	auto * riverManager = zone.getModificator<RiverPlacer>(); | ||||
| 	 | ||||
| 	typedef std::vector<ObjectTemplate> ObstacleVector; | ||||
| 	//obstacleVector possibleObstacles; | ||||
| 	 | ||||
| 	std::map<int, ObstacleVector> obstaclesBySize; | ||||
| 	typedef std::pair<int, ObstacleVector> ObstaclePair; | ||||
| 	std::vector<ObstaclePair> possibleObstacles; | ||||
| 	 | ||||
| 	//get all possible obstacles for this terrain | ||||
| 	for(auto primaryID : VLC->objtypeh->knownObjects()) | ||||
| 	{ | ||||
| 		for(auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID)) | ||||
| 		{ | ||||
| 			auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID); | ||||
| 			if(handler->isStaticObject()) | ||||
| 			{ | ||||
| 				for(auto temp : handler->getTemplates()) | ||||
| 				{ | ||||
| 					if(temp.canBePlacedAt(zone.getTerrainType()) && temp.getBlockMapOffset().valid()) | ||||
| 						obstaclesBySize[temp.getBlockedOffsets().size()].push_back(temp); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	for(auto o : obstaclesBySize) | ||||
| 	{ | ||||
| 		possibleObstacles.push_back(o); | ||||
| 	} | ||||
| 	boost::sort(possibleObstacles, [](const ObstaclePair &p1, const ObstaclePair &p2) -> bool | ||||
| 	{ | ||||
| 		return p1.first > p2.first; //bigger obstacles first | ||||
| 	}); | ||||
| 	 | ||||
| 	auto blockedArea = zone.area().getSubarea([this](const int3 & t) | ||||
| 	{ | ||||
| 		return map.shouldBeBlocked(t); | ||||
| 	}); | ||||
| 	blockedArea.subtract(zone.areaUsed()); | ||||
| 	zone.areaPossible().subtract(blockedArea); | ||||
| 	 | ||||
| 	 | ||||
| 	auto prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea(); | ||||
| 	 | ||||
| 	//reverse order, since obstacles begin in bottom-right corner, while the map coordinates begin in top-left | ||||
| 	auto blockedTiles = blockedArea.getTilesVector(); | ||||
| 	int tilePos = 0; | ||||
| 	while(!blockedArea.empty() && tilePos < blockedArea.getTilesVector().size()) | ||||
| 	{ | ||||
| 		auto tile = blockedArea.getTilesVector()[tilePos]; | ||||
| 		 | ||||
| 		std::list<rmg::Object> allObjects; | ||||
| 		std::vector<std::pair<rmg::Object*, int3>> weightedObjects; //obj + position | ||||
| 		int maxWeight = std::numeric_limits<int>::min(); | ||||
| 		for(int i = 0; i < possibleObstacles.size(); ++i) | ||||
| 		{ | ||||
| 			if(!possibleObstacles[i].first) | ||||
| 				continue; | ||||
| 			 | ||||
| 			for(auto & temp : possibleObstacles[i].second) | ||||
| 			{ | ||||
| 				auto handler = VLC->objtypeh->getHandlerFor(temp.id, temp.subid); | ||||
| 				auto obj = handler->create(temp); | ||||
| 				allObjects.emplace_back(*obj); | ||||
| 				rmg::Object * rmgObject = &allObjects.back(); | ||||
| 				for(auto & offset : obj->getBlockedOffsets()) | ||||
| 				{ | ||||
| 					rmgObject->setPosition(tile - offset); | ||||
| 					if(!map.isOnMap(rmgObject->getPosition())) | ||||
| 						continue; | ||||
| 					 | ||||
| 					if(!rmgObject->getArea().getSubarea([this](const int3 & t) | ||||
| 					{ | ||||
| 						return !map.isOnMap(t); | ||||
| 					}).empty()) | ||||
| 						continue; | ||||
| 					 | ||||
| 					if(prohibitedArea.overlap(rmgObject->getArea())) | ||||
| 						continue; | ||||
| 					 | ||||
| 					if(!zone.area().contains(rmgObject->getArea())) | ||||
| 						continue; | ||||
| 					 | ||||
| 					int coverageBlocked = 0; | ||||
| 					int coveragePossible = 0; | ||||
| 					//do not use area intersection in optimization purposes | ||||
| 					for(auto & t : rmgObject->getArea().getTilesVector()) | ||||
| 					{ | ||||
| 						if(map.shouldBeBlocked(t)) | ||||
| 							++coverageBlocked; | ||||
| 						if(zone.areaPossible().contains(t)) | ||||
| 							++coveragePossible; | ||||
| 					} | ||||
| 					 | ||||
| 					int coverageOverlap = possibleObstacles[i].first - coverageBlocked - coveragePossible; | ||||
| 					int weight = possibleObstacles[i].first + coverageBlocked - coverageOverlap * possibleObstacles[i].first; | ||||
| 					assert(coverageOverlap >= 0); | ||||
| 					 | ||||
| 					if(weight > maxWeight) | ||||
| 					{ | ||||
| 						weightedObjects.clear(); | ||||
| 						maxWeight = weight; | ||||
| 						weightedObjects.emplace_back(rmgObject, rmgObject->getPosition()); | ||||
| 						if(weight > 0) | ||||
| 							break; | ||||
| 					} | ||||
| 					else if(weight == maxWeight) | ||||
| 						weightedObjects.emplace_back(rmgObject, rmgObject->getPosition()); | ||||
| 					 | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			if(maxWeight > 0) | ||||
| 				break; | ||||
| 		} | ||||
| 		 | ||||
| 		if(weightedObjects.empty()) | ||||
| 		{ | ||||
| 			tilePos += 1; | ||||
| 			continue; | ||||
| 		} | ||||
| 		 | ||||
| 		auto objIter = RandomGeneratorUtil::nextItem(weightedObjects, generator.rand); | ||||
| 		objIter->first->setPosition(objIter->second); | ||||
| 		manager->placeObject(*objIter->first, false, false); | ||||
| 		blockedArea.subtract(objIter->first->getArea()); | ||||
| 		tilePos = 0; | ||||
| 		 | ||||
| 		//river processing | ||||
| 		if(riverManager) | ||||
| 		{ | ||||
| 			if(objIter->first->instances().front()->object().typeName == "mountain") | ||||
| 				riverManager->riverSource().unite(objIter->first->getArea()); | ||||
| 			if(objIter->first->instances().front()->object().typeName == "lake") | ||||
| 				riverManager->riverSink().unite(objIter->first->getArea()); | ||||
| 		} | ||||
| 		 | ||||
| 		if(maxWeight < 0) | ||||
| 			logGlobal->warn("Placed obstacle with negative weight at %s", objIter->second.toString()); | ||||
| 		 | ||||
| 		for(auto & o : allObjects) | ||||
| 		{ | ||||
| 			if(&o != objIter->first) | ||||
| 				o.clear(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ObstaclePlacer::init() | ||||
| { | ||||
| 	DEPENDENCY(ObjectManager); | ||||
| 	DEPENDENCY(TreasurePlacer); | ||||
| 	DEPENDENCY(WaterRoutes); | ||||
| 	DEPENDENCY(WaterProxy); | ||||
| 	DEPENDENCY(RoadPlacer); | ||||
| 	DEPENDENCY_ALL(RockPlacer); | ||||
| } | ||||
							
								
								
									
										21
									
								
								lib/rmg/ObstaclePlacer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								lib/rmg/ObstaclePlacer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| /* | ||||
|  * ObstaclePlacer.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 "Zone.h" | ||||
|  | ||||
| class ObstaclePlacer: public Modificator | ||||
| { | ||||
| public: | ||||
| 	MODIFICATOR(ObstaclePlacer); | ||||
| 	 | ||||
| 	void process() override; | ||||
| 	void init() override; | ||||
| }; | ||||
							
								
								
									
										404
									
								
								lib/rmg/RiverPlacer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										404
									
								
								lib/rmg/RiverPlacer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,404 @@ | ||||
| /* | ||||
|  * RiverPlacer.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "RiverPlacer.h" | ||||
| #include "Functions.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "RmgMap.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
| #include "../mapObjects/CObjectClassesHandler.h" | ||||
| #include "RmgPath.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "ObstaclePlacer.h" | ||||
| #include "WaterProxy.h" | ||||
| #include "RoadPlacer.h" | ||||
|  | ||||
| const int RIVER_DELTA_ID = 143; | ||||
| const int RIVER_DELTA_SUBTYPE = 0; | ||||
| const std::map<std::string, std::string> RIVER_DELTA_TEMPLATE_NAME | ||||
| { | ||||
| 	{RIVER_NAMES[1], "clrdelt"}, | ||||
| 	{RIVER_NAMES[2], "icedelt"}, | ||||
| 	{RIVER_NAMES[3], "muddelt"}, | ||||
| 	{RIVER_NAMES[4], "lavdelt"} | ||||
| }; | ||||
|  | ||||
| const std::array<std::array<int, 25>, 4> deltaTemplates | ||||
| { | ||||
| 	//0 - must be on ground | ||||
| 	//1 - delta entry | ||||
| 	//2 - must be on water | ||||
| 	//3 - anything | ||||
| 	//4 - prohibit river placement | ||||
| 	//5 - must be on ground + position | ||||
| 	//6 - must be on water + position | ||||
| 	std::array<int, 25>{ | ||||
| 		3, 4, 3, 4, 3, | ||||
| 		3, 4, 1, 4, 3, | ||||
| 		3, 0, 0, 0, 3, | ||||
| 		3, 0, 0, 0, 3, | ||||
| 		3, 2, 2, 6, 3 | ||||
| 	}, | ||||
| 	std::array<int, 25>{ | ||||
| 		3, 2, 2, 2, 3, | ||||
| 		3, 0, 0, 0, 3, | ||||
| 		3, 0, 0, 5, 3, | ||||
| 		3, 4, 1, 4, 3, | ||||
| 		3, 4, 3, 4, 3 | ||||
| 	}, | ||||
| 	std::array<int, 25>{ | ||||
| 		3, 3, 3, 3, 3, | ||||
| 		4, 4, 0, 0, 2, | ||||
| 		3, 1, 0, 0, 2, | ||||
| 		4, 4, 0, 0, 6, | ||||
| 		3, 3, 3, 3, 3 | ||||
| 	}, | ||||
| 	std::array<int, 25>	{ | ||||
| 		3, 3, 3, 3, 3, | ||||
| 		2, 0, 0, 4, 4, | ||||
| 		2, 0, 0, 1, 3, | ||||
| 		2, 0, 5, 4, 4, | ||||
| 		3, 3, 3, 3, 3 | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| void RiverPlacer::process() | ||||
| { | ||||
| 	preprocess(); | ||||
| 	for(auto & t : riverNodes) | ||||
| 		connectRiver(t); | ||||
| 	 | ||||
| 	if(!rivers.empty()) | ||||
| 		drawRivers(); | ||||
| } | ||||
|  | ||||
| void RiverPlacer::init() | ||||
| { | ||||
| 	DEPENDENCY_ALL(WaterProxy); | ||||
| 	DEPENDENCY(ObjectManager); | ||||
| 	DEPENDENCY(ObstaclePlacer); | ||||
| } | ||||
|  | ||||
| void RiverPlacer::drawRivers() | ||||
| { | ||||
| 	map.getEditManager()->getTerrainSelection().setSelection(rivers.getTilesVector()); | ||||
| 	map.getEditManager()->drawRiver(Terrain::Manager::getInfo(zone.getTerrainType()).river, &generator.rand); | ||||
| } | ||||
|  | ||||
| char RiverPlacer::dump(const int3 & t) | ||||
| { | ||||
| 	if(riverNodes.count(t)) | ||||
| 		return '@'; | ||||
| 	if(rivers.contains(t)) | ||||
| 		return '~'; | ||||
| 	if(sink.contains(t)) | ||||
| 		return '2'; | ||||
| 	if(source.contains(t)) | ||||
| 		return '1'; | ||||
| 	if(zone.area().contains(t)) | ||||
| 		return ' '; | ||||
| 	return '?'; | ||||
| } | ||||
|  | ||||
| void RiverPlacer::addRiverNode(const int3 & node) | ||||
| { | ||||
| 	assert(zone.area().contains(node)); | ||||
| 	riverNodes.insert(node); | ||||
| } | ||||
|  | ||||
| rmg::Area & RiverPlacer::riverSource() | ||||
| { | ||||
| 	return source; | ||||
| } | ||||
|  | ||||
| rmg::Area & RiverPlacer::riverSink() | ||||
| { | ||||
| 	return sink; | ||||
| } | ||||
|  | ||||
| rmg::Area & RiverPlacer::riverProhibit() | ||||
| { | ||||
| 	return prohibit; | ||||
| } | ||||
|  | ||||
| void RiverPlacer::prepareHeightmap() | ||||
| { | ||||
| 	rmg::Area roads; | ||||
| 	if(auto * m = zone.getModificator<RoadPlacer>()) | ||||
| 	{ | ||||
| 		roads.unite(m->getRoads()); | ||||
| 	} | ||||
| 	 | ||||
| 	for(auto & t : zone.area().getTilesVector()) | ||||
| 	{ | ||||
| 		heightMap[t] = generator.rand.nextInt(5); | ||||
| 		 | ||||
| 		if(roads.contains(t)) | ||||
| 			heightMap[t] += 30.f; | ||||
| 		 | ||||
| 		if(zone.areaUsed().contains(t)) | ||||
| 			heightMap[t] += 1000.f; | ||||
| 	} | ||||
| 	 | ||||
| 	//make grid | ||||
| 	for(int j = 0; j < map.map().height; j += 2) | ||||
| 	{ | ||||
| 		for(int i = 0; i < map.map().width; i += 2) | ||||
| 		{ | ||||
| 			int3 t{i, j, zone.getPos().z}; | ||||
| 			if(zone.area().contains(t)) | ||||
| 				heightMap[t] += 10.f; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void RiverPlacer::preprocess() | ||||
| { | ||||
| 	rmg::Area outOfMapTiles; | ||||
| 	std::map<TRmgTemplateZoneId, rmg::Area> neighbourZonesTiles; | ||||
| 	rmg::Area borderArea(zone.getArea().getBorder()); | ||||
| 	TRmgTemplateZoneId connectedToWaterZoneId = -1; | ||||
| 	for(auto & t : zone.getArea().getBorderOutside()) | ||||
| 	{ | ||||
| 		if(!map.isOnMap(t)) | ||||
| 		{ | ||||
| 			outOfMapTiles.add(t); | ||||
| 		} | ||||
| 		else if(map.getZoneID(t) != zone.getId()) | ||||
| 		{ | ||||
| 			if(map.getZones()[map.getZoneID(t)]->getType() == ETemplateZoneType::WATER) | ||||
| 				connectedToWaterZoneId = map.getZoneID(t); | ||||
| 			neighbourZonesTiles[map.getZoneID(t)].add(t); | ||||
| 		} | ||||
| 	} | ||||
| 	rmg::Area outOfMapInternal(outOfMapTiles.getBorderOutside()); | ||||
| 	outOfMapInternal.intersect(borderArea); | ||||
| 	 | ||||
| 	//looking outside map | ||||
| 	if(!outOfMapInternal.empty()) | ||||
| 	{ | ||||
| 		auto elem = *RandomGeneratorUtil::nextItem(outOfMapInternal.getTilesVector(), generator.rand); | ||||
| 		source.add(elem); | ||||
| 		outOfMapInternal.erase(elem); | ||||
| 	} | ||||
| 	if(!outOfMapInternal.empty()) | ||||
| 	{ | ||||
| 		auto elem = *RandomGeneratorUtil::nextItem(outOfMapInternal.getTilesVector(), generator.rand); | ||||
| 		sink.add(elem); | ||||
| 		outOfMapInternal.erase(elem); | ||||
| 	} | ||||
| 	 | ||||
| 	//calculate delta positions | ||||
| 	if(connectedToWaterZoneId > -1) | ||||
| 	{ | ||||
| 		auto river = Terrain::Manager::getInfo(zone.getTerrainType()).river; | ||||
| 		auto & a = neighbourZonesTiles[connectedToWaterZoneId]; | ||||
| 		auto availableArea = zone.areaPossible() + zone.freePaths(); | ||||
| 		for(auto & tileToProcess : availableArea.getTilesVector()) | ||||
| 		{ | ||||
| 			int templateId = -1; | ||||
| 			for(int tId = 0; tId < 4; ++tId) | ||||
| 			{ | ||||
| 				templateId = tId; | ||||
| 				for(int i = 0; i < 25; ++i) | ||||
| 				{ | ||||
| 					if((deltaTemplates[tId][i] == 2 || deltaTemplates[tId][i] == 6) && !a.contains(tileToProcess + int3(i % 5 - 2, i / 5 - 2, 0))) | ||||
| 					{ | ||||
| 						templateId = -1; | ||||
| 						break; | ||||
| 					} | ||||
| 					if((deltaTemplates[tId][i] < 2 || deltaTemplates[tId][i] == 5) && !availableArea.contains(tileToProcess + int3(i % 5 - 2, i / 5 - 2, 0))) | ||||
| 					{ | ||||
| 						templateId = -1; | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				if(templateId > -1) | ||||
| 					break; | ||||
| 			} | ||||
| 			 | ||||
| 			if(templateId > -1) | ||||
| 			{ | ||||
| 				for(int i = 0; i < 25; ++i) | ||||
| 				{ | ||||
| 					auto p = tileToProcess + int3(i % 5 - 2, i / 5 - 2, 0); | ||||
| 					if(deltaTemplates[templateId][i] == 1) | ||||
| 					{ | ||||
| 						sink.add(p); | ||||
| 						deltaSink.add(p); | ||||
| 						deltaOrientations[p] = templateId + 1; | ||||
| 						 | ||||
| 						//specific case: deltas for ice rivers amd mud rivers are messed :( | ||||
| 						if(river == RIVER_NAMES[2]) | ||||
| 						{ | ||||
| 							switch(deltaOrientations[p]) | ||||
| 							{ | ||||
| 								case 1: | ||||
| 									deltaOrientations[p] = 2; | ||||
| 									break; | ||||
| 								case 2: | ||||
| 									deltaOrientations[p] = 3; | ||||
| 									break; | ||||
| 								case 3: | ||||
| 									deltaOrientations[p] = 4; | ||||
| 									break; | ||||
| 								case 4: | ||||
| 									deltaOrientations[p] = 1; | ||||
| 									break; | ||||
| 							} | ||||
| 						} | ||||
| 						if(river == RIVER_NAMES[3]) | ||||
| 						{ | ||||
| 							switch(deltaOrientations[p]) | ||||
| 							{ | ||||
| 								case 1: | ||||
| 									deltaOrientations[p] = 4; | ||||
| 									break; | ||||
| 								case 2: | ||||
| 									deltaOrientations[p] = 3; | ||||
| 									break; | ||||
| 								case 3: | ||||
| 									deltaOrientations[p] = 1; | ||||
| 									break; | ||||
| 								case 4: | ||||
| 									deltaOrientations[p] = 2; | ||||
| 									break; | ||||
| 							} | ||||
| 						} | ||||
| 						 | ||||
| 						for(auto j = 0; j < 25; ++j) | ||||
| 						{ | ||||
| 							if(deltaTemplates[templateId][j] >= 5) | ||||
| 							{ | ||||
| 								deltaPositions[p] = tileToProcess + int3(j % 5 - 2, j / 5 - 2, 0); | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 					if(deltaTemplates[templateId][i] == 0 || deltaTemplates[templateId][i] == 4 || deltaTemplates[templateId][i] == 5) | ||||
| 					{ | ||||
| 						prohibit.add(p); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	prepareHeightmap(); | ||||
| 	 | ||||
| 	//decorative river | ||||
| 	if(!sink.empty() && !source.empty() && riverNodes.empty() && !zone.areaPossible().empty()) | ||||
| 	{ | ||||
| 		addRiverNode(*RandomGeneratorUtil::nextItem(source.getTilesVector(), generator.rand)); | ||||
| 	} | ||||
| 	 | ||||
| 	if(source.empty()) | ||||
| 	{ | ||||
| 		logGlobal->info("River source is empty!"); | ||||
| 		 | ||||
| 		//looking outside map | ||||
| 		 | ||||
| 		for(auto & i : heightMap) | ||||
| 		{ | ||||
| 			if(i.second > 0) | ||||
| 				source.add(i.first); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	if(sink.empty()) | ||||
| 	{ | ||||
| 		logGlobal->error("River sink is empty!"); | ||||
| 		for(auto & i : heightMap) | ||||
| 		{ | ||||
| 			if(i.second <= 0) | ||||
| 				sink.add(i.first); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void RiverPlacer::connectRiver(const int3 & tile) | ||||
| { | ||||
| 	auto river = Terrain::Manager::getInfo(zone.getTerrainType()).river; | ||||
| 	if(river.empty() || river == RIVER_NAMES[0]) | ||||
| 		return; | ||||
| 	 | ||||
| 	rmg::Area roads; | ||||
| 	if(auto * m = zone.getModificator<RoadPlacer>()) | ||||
| 	{ | ||||
| 		roads.unite(m->getRoads()); | ||||
| 	} | ||||
| 	 | ||||
| 	auto movementCost = [this, &roads](const int3 & s, const int3 & d) | ||||
| 	{ | ||||
| 		float cost = heightMap[d]; | ||||
| 		if(roads.contains(s)) | ||||
| 			cost += 1000.f; //allow road intersection, but avoid long overlaps | ||||
| 		return cost; | ||||
| 	}; | ||||
| 	 | ||||
| 	auto availableArea = zone.area() - prohibit; | ||||
| 	 | ||||
| 	rmg::Path pathToSource(availableArea); | ||||
| 	pathToSource.connect(source); | ||||
| 	pathToSource.connect(rivers); | ||||
| 	pathToSource = pathToSource.search(tile, true, movementCost); | ||||
| 	 | ||||
| 	availableArea.subtract(pathToSource.getPathArea()); | ||||
| 	 | ||||
| 	rmg::Path pathToSink(availableArea); | ||||
| 	pathToSink.connect(sink); | ||||
| 	pathToSource.connect(rivers); | ||||
| 	pathToSink = pathToSink.search(tile, true, movementCost); | ||||
| 	 | ||||
| 	if(pathToSource.getPathArea().empty() || pathToSink.getPathArea().empty()) | ||||
| 	{ | ||||
| 		logGlobal->error("Cannot build river"); | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
| 	//delta placement | ||||
| 	auto deltaPos = pathToSink.getPathArea() * deltaSink; | ||||
| 	if(!deltaPos.empty()) | ||||
| 	{ | ||||
| 		assert(deltaPos.getTilesVector().size() == 1); | ||||
| 		 | ||||
| 		auto pos = deltaPos.getTilesVector().front(); | ||||
| 		auto handler = VLC->objtypeh->getHandlerFor(RIVER_DELTA_ID, RIVER_DELTA_SUBTYPE); | ||||
| 		assert(handler->isStaticObject()); | ||||
| 		 | ||||
| 		std::vector<ObjectTemplate> tmplates; | ||||
| 		for(auto & temp : handler->getTemplates()) | ||||
| 		{ | ||||
| 			if(temp.canBePlacedAt(zone.getTerrainType())) | ||||
| 			   tmplates.push_back(temp); | ||||
| 		} | ||||
| 		 | ||||
| 		if(tmplates.size() > 3) | ||||
| 		{ | ||||
| 			if(tmplates.size() % 4 != 0) | ||||
| 				throw rmgException(boost::to_string(boost::format("River templates for (%d,%d) at terrain %s, river %s are incorrect") % RIVER_DELTA_ID % RIVER_DELTA_SUBTYPE % zone.getTerrainType() % river)); | ||||
| 			 | ||||
| 			std::string targetTemplateName = RIVER_DELTA_TEMPLATE_NAME.at(river) + std::to_string(deltaOrientations[pos]) + ".def"; | ||||
| 			for(auto & templ : tmplates) | ||||
| 			{ | ||||
| 				if(templ.animationFile == targetTemplateName) | ||||
| 				{ | ||||
| 					auto obj = handler->create(templ); | ||||
| 					rmg::Object deltaObj(*obj, deltaPositions[pos]); | ||||
| 					deltaObj.finalize(map); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	rivers.unite(pathToSource.getPathArea()); | ||||
| 	rivers.unite(pathToSink.getPathArea()); | ||||
| } | ||||
							
								
								
									
										48
									
								
								lib/rmg/RiverPlacer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								lib/rmg/RiverPlacer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| /* | ||||
|  * RiverPlacer.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 "Zone.h" | ||||
|  | ||||
| class RiverPlacer: public Modificator | ||||
| { | ||||
| public: | ||||
| 	MODIFICATOR(RiverPlacer); | ||||
| 	 | ||||
| 	void process() override; | ||||
| 	void init() override; | ||||
| 	char dump(const int3 &) override; | ||||
| 	 | ||||
| 	void addRiverNode(const int3 & node); | ||||
| 	 | ||||
| 	rmg::Area & riverSource(); | ||||
| 	rmg::Area & riverSink(); | ||||
| 	rmg::Area & riverProhibit(); | ||||
| 	 | ||||
| protected: | ||||
| 	void drawRivers(); | ||||
| 	 | ||||
| 	void preprocess(); | ||||
| 	void connectRiver(const int3 & tile); | ||||
| 	 | ||||
| 	void prepareHeightmap(); | ||||
| 	 | ||||
| private: | ||||
| 	rmg::Area rivers; | ||||
| 	rmg::Area source; | ||||
| 	rmg::Area sink; | ||||
| 	rmg::Area prohibit; | ||||
| 	rmg::Tileset riverNodes; | ||||
| 	rmg::Area deltaSink; | ||||
| 	std::map<int3, int3> deltaPositions; | ||||
| 	std::map<int3, int> deltaOrientations; | ||||
| 	 | ||||
| 	std::map<int3, int> heightMap; | ||||
| }; | ||||
							
								
								
									
										399
									
								
								lib/rmg/RmgArea.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								lib/rmg/RmgArea.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,399 @@ | ||||
| /* | ||||
|  * RmgArea.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "RmgArea.h" | ||||
| #include "CMapGenerator.h" | ||||
|  | ||||
| namespace rmg | ||||
| { | ||||
|  | ||||
| void toAbsolute(Tileset & tiles, const int3 & position) | ||||
| { | ||||
| 	Tileset temp; | ||||
| 	for(auto & tile : tiles) | ||||
| 	{ | ||||
| 		temp.insert(tile + position); | ||||
| 	} | ||||
| 	tiles = std::move(temp); | ||||
| } | ||||
|  | ||||
| void toRelative(Tileset & tiles, const int3 & position) | ||||
| { | ||||
| 	toAbsolute(tiles, -position); | ||||
| } | ||||
|  | ||||
| Area::Area(const Area & area): dTiles(area.dTiles), dTotalShiftCache(area.dTotalShiftCache) | ||||
| { | ||||
| } | ||||
|  | ||||
| Area::Area(const Area && area): dTiles(std::move(area.dTiles)), dTotalShiftCache(std::move(area.dTotalShiftCache)) | ||||
| { | ||||
| } | ||||
|  | ||||
|  | ||||
| Area & Area::operator=(const Area & area) | ||||
| { | ||||
| 	clear(); | ||||
| 	dTiles = area.dTiles; | ||||
| 	dTotalShiftCache = area.dTotalShiftCache; | ||||
| 	return *this; | ||||
| } | ||||
|  | ||||
| Area::Area(const Tileset & tiles): dTiles(tiles) | ||||
| { | ||||
| } | ||||
|  | ||||
| Area::Area(const Tileset & relative, const int3 & position): dTiles(relative), dTotalShiftCache(position) | ||||
| { | ||||
| } | ||||
|  | ||||
| void Area::invalidate() | ||||
| { | ||||
| 	getTiles(); | ||||
| 	dTilesVectorCache.clear(); | ||||
| 	dBorderCache.clear(); | ||||
| 	dBorderOutsideCache.clear(); | ||||
| } | ||||
|  | ||||
| bool Area::connected() const | ||||
| { | ||||
| 	std::list<int3> queue({*dTiles.begin()}); | ||||
| 	Tileset connected = dTiles; //use invalidated cache - ok | ||||
| 	while(!queue.empty()) | ||||
| 	{ | ||||
| 		auto t = queue.front(); | ||||
| 		connected.erase(t); | ||||
| 		queue.pop_front(); | ||||
| 		 | ||||
| 		for(auto & i : int3::getDirs()) | ||||
| 		{ | ||||
| 			if(connected.count(t + i)) | ||||
| 			{ | ||||
| 				queue.push_back(t + i); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	return connected.empty(); | ||||
| } | ||||
|  | ||||
| std::list<Area> connectedAreas(const Area & area) | ||||
| { | ||||
| 	std::list<Area> result; | ||||
| 	Tileset connected = area.getTiles(); | ||||
| 	while(!connected.empty()) | ||||
| 	{ | ||||
| 		result.emplace_back(); | ||||
| 		std::list<int3> queue({*connected.begin()}); | ||||
| 		std::set<int3> queueSet({*connected.begin()}); | ||||
| 		while(!queue.empty()) | ||||
| 		{ | ||||
| 			auto t = queue.front(); | ||||
| 			connected.erase(t); | ||||
| 			result.back().add(t); | ||||
| 			queue.pop_front(); | ||||
| 			 | ||||
| 			for(auto & i : int3::getDirs()) | ||||
| 			{ | ||||
| 				auto tile = t + i; | ||||
| 				if(!queueSet.count(tile) && connected.count(tile) && !result.back().contains(tile)) | ||||
| 				{ | ||||
| 					queueSet.insert(tile); | ||||
| 					queue.push_back(tile); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| const Tileset & Area::getTiles() const | ||||
| { | ||||
| 	if(dTotalShiftCache != int3()) | ||||
| 	{ | ||||
| 		toAbsolute(dTiles, dTotalShiftCache); | ||||
| 		dTotalShiftCache = int3(); | ||||
| 	} | ||||
| 	return dTiles; | ||||
| } | ||||
|  | ||||
| const std::vector<int3> & Area::getTilesVector() const | ||||
| { | ||||
| 	if(dTilesVectorCache.empty()) | ||||
| 	{ | ||||
| 		getTiles(); | ||||
| 		dTilesVectorCache.assign(dTiles.begin(), dTiles.end()); | ||||
| 	} | ||||
| 	return dTilesVectorCache; | ||||
| } | ||||
|  | ||||
| const Tileset & Area::getBorder() const | ||||
| { | ||||
| 	if(!dBorderCache.empty()) | ||||
| 		return dBorderCache; | ||||
| 	 | ||||
| 	//compute border cache | ||||
| 	for(auto & t : dTiles) | ||||
| 	{ | ||||
| 		for(auto & i : int3::getDirs()) | ||||
| 		{ | ||||
| 			if(!dTiles.count(t + i)) | ||||
| 			{ | ||||
| 				dBorderCache.insert(t + dTotalShiftCache); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	return dBorderCache; | ||||
| } | ||||
|  | ||||
| const Tileset & Area::getBorderOutside() const | ||||
| { | ||||
| 	if(!dBorderOutsideCache.empty()) | ||||
| 		return dBorderOutsideCache; | ||||
| 	 | ||||
| 	//compute outside border cache | ||||
| 	for(auto & t : dTiles) | ||||
| 	{ | ||||
| 		for(auto & i : int3::getDirs()) | ||||
| 		{ | ||||
| 			if(!dTiles.count(t + i)) | ||||
| 				dBorderOutsideCache.insert(t + i + dTotalShiftCache); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	return dBorderOutsideCache; | ||||
| } | ||||
|  | ||||
| DistanceMap Area::computeDistanceMap(std::map<int, Tileset> & reverseDistanceMap) const | ||||
| { | ||||
| 	reverseDistanceMap.clear(); | ||||
| 	DistanceMap result; | ||||
| 	auto area = *this; | ||||
| 	int distance = 0; | ||||
| 	 | ||||
| 	while(!area.empty()) | ||||
| 	{ | ||||
| 		for(auto & tile : area.getBorder()) | ||||
| 			result[tile] = distance; | ||||
| 		reverseDistanceMap[distance++] = area.getBorder(); | ||||
| 		area.subtract(area.getBorder()); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| bool Area::empty() const | ||||
| { | ||||
| 	return dTiles.empty(); | ||||
| } | ||||
|  | ||||
| bool Area::contains(const int3 & tile) const | ||||
| { | ||||
| 	return dTiles.count(tile - dTotalShiftCache); | ||||
| } | ||||
|  | ||||
| bool Area::contains(const std::vector<int3> & tiles) const | ||||
| { | ||||
| 	for(auto & t : tiles) | ||||
| 	{ | ||||
| 		if(!contains(t)) | ||||
| 			return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool Area::contains(const Area & area) const | ||||
| { | ||||
| 	return contains(area.getTilesVector()); | ||||
| } | ||||
|  | ||||
| bool Area::overlap(const std::vector<int3> & tiles) const | ||||
| { | ||||
| 	for(auto & t : tiles) | ||||
| 	{ | ||||
| 		if(contains(t)) | ||||
| 			return true; | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool Area::overlap(const Area & area) const | ||||
| { | ||||
| 	return overlap(area.getTilesVector()); | ||||
| } | ||||
|  | ||||
| int Area::distanceSqr(const int3 & tile) const | ||||
| { | ||||
| 	return nearest(tile).dist2dSQ(tile); | ||||
| } | ||||
|  | ||||
| int Area::distanceSqr(const Area & area) const | ||||
| { | ||||
| 	int dist = std::numeric_limits<int>::max(); | ||||
| 	int3 nearTile = *getTilesVector().begin(); | ||||
| 	int3 otherNearTile = area.nearest(nearTile); | ||||
| 	 | ||||
| 	while(dist != otherNearTile.dist2dSQ(nearTile)) | ||||
| 	{ | ||||
| 		dist = otherNearTile.dist2dSQ(nearTile); | ||||
| 		nearTile = nearest(otherNearTile); | ||||
| 		otherNearTile = area.nearest(nearTile); | ||||
| 	} | ||||
| 	 | ||||
| 	return dist; | ||||
| } | ||||
|  | ||||
| int3 Area::nearest(const int3 & tile) const | ||||
| { | ||||
| 	return findClosestTile(getTilesVector(), tile); | ||||
| } | ||||
|  | ||||
| int3 Area::nearest(const Area & area) const | ||||
| { | ||||
| 	int dist = std::numeric_limits<int>::max(); | ||||
| 	int3 nearTile = *getTilesVector().begin(); | ||||
| 	int3 otherNearTile = area.nearest(nearTile); | ||||
| 	 | ||||
| 	while(dist != otherNearTile.dist2dSQ(nearTile)) | ||||
| 	{ | ||||
| 		dist = otherNearTile.dist2dSQ(nearTile); | ||||
| 		nearTile = nearest(otherNearTile); | ||||
| 		otherNearTile = area.nearest(nearTile); | ||||
| 	} | ||||
| 	 | ||||
| 	return nearTile; | ||||
| } | ||||
|  | ||||
| Area Area::getSubarea(std::function<bool(const int3 &)> filter) const | ||||
| { | ||||
| 	Area subset; | ||||
| 	for(auto & t : getTilesVector()) | ||||
| 		if(filter(t)) | ||||
| 			subset.add(t); | ||||
| 	return subset; | ||||
| } | ||||
|  | ||||
| void Area::clear() | ||||
| { | ||||
| 	dTiles.clear(); | ||||
| 	dTotalShiftCache = int3(); | ||||
| 	invalidate(); | ||||
| } | ||||
|  | ||||
| void Area::assign(const Tileset tiles) | ||||
| { | ||||
| 	clear(); | ||||
| 	dTiles = tiles; | ||||
| } | ||||
|  | ||||
| void Area::add(const int3 & tile) | ||||
| { | ||||
| 	invalidate(); | ||||
| 	dTiles.insert(tile); | ||||
| } | ||||
|  | ||||
| void Area::erase(const int3 & tile) | ||||
| { | ||||
| 	invalidate(); | ||||
| 	dTiles.erase(tile); | ||||
| } | ||||
| void Area::unite(const Area & area) | ||||
| { | ||||
| 	invalidate(); | ||||
| 	for(auto & t : area.getTilesVector()) | ||||
| 	{ | ||||
| 		dTiles.insert(t); | ||||
| 	} | ||||
| } | ||||
| void Area::intersect(const Area & area) | ||||
| { | ||||
| 	invalidate(); | ||||
| 	Tileset result; | ||||
| 	for(auto & t : area.getTilesVector()) | ||||
| 	{ | ||||
| 		if(dTiles.count(t)) | ||||
| 			result.insert(t); | ||||
| 	} | ||||
| 	dTiles = result; | ||||
| } | ||||
|  | ||||
| void Area::subtract(const Area & area) | ||||
| { | ||||
| 	invalidate(); | ||||
| 	for(auto & t : area.getTilesVector()) | ||||
| 	{ | ||||
| 		dTiles.erase(t); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Area::translate(const int3 & shift) | ||||
| { | ||||
| 	dBorderCache.clear(); | ||||
| 	dBorderOutsideCache.clear(); | ||||
| 	 | ||||
| 	if(dTilesVectorCache.empty()) | ||||
| 	{ | ||||
| 		getTiles(); | ||||
| 		getTilesVector(); | ||||
| 	} | ||||
| 	 | ||||
| 	//avoid recomputation within std::set, use vector instead | ||||
| 	dTotalShiftCache += shift; | ||||
| 	 | ||||
| 	for(auto & t : dTilesVectorCache) | ||||
| 	{ | ||||
| 		t += shift; | ||||
| 	} | ||||
| 	//toAbsolute(dTiles, shift); | ||||
| } | ||||
|  | ||||
| Area operator- (const Area & l, const int3 & r) | ||||
| { | ||||
| 	Area result(l); | ||||
| 	result.translate(-r); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| Area operator+ (const Area & l, const int3 & r) | ||||
| { | ||||
| 	Area result(l); | ||||
| 	result.translate(r); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| Area operator+ (const Area & l, const Area & r) | ||||
| { | ||||
| 	Area result(l); | ||||
| 	result.unite(r); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| Area operator- (const Area & l, const Area & r) | ||||
| { | ||||
| 	Area result(l); | ||||
| 	result.subtract(r); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| Area operator* (const Area & l, const Area & r) | ||||
| { | ||||
| 	Area result(l); | ||||
| 	result.intersect(r); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| bool operator== (const Area & l, const Area & r) | ||||
| { | ||||
| 	return l.getTiles() == r.getTiles(); | ||||
| } | ||||
|  | ||||
| } | ||||
							
								
								
									
										83
									
								
								lib/rmg/RmgArea.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								lib/rmg/RmgArea.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| /* | ||||
|  * RmgArea.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 "../GameConstants.h" | ||||
| #include "../int3.h" | ||||
|  | ||||
| namespace rmg | ||||
| { | ||||
| 	using Tileset = std::set<int3>; | ||||
| 	using DistanceMap = std::map<int3, int>; | ||||
| 	void toAbsolute(Tileset & tiles, const int3 & position); | ||||
| 	void toRelative(Tileset & tiles, const int3 & position); | ||||
| 	 | ||||
| 	class Area | ||||
| 	{ | ||||
| 	public: | ||||
| 		Area() = default; | ||||
| 		Area(const Area &); | ||||
| 		Area(const Area &&); | ||||
| 		Area(const Tileset & tiles); | ||||
| 		Area(const Tileset & relative, const int3 & position); //create from relative positions | ||||
| 		Area & operator= (const Area &); | ||||
| 		 | ||||
| 		const Tileset & getTiles() const; | ||||
| 		const std::vector<int3> & getTilesVector() const; | ||||
| 		const Tileset & getBorder() const; //lazy cache invalidation | ||||
| 		const Tileset & getBorderOutside() const; //lazy cache invalidation | ||||
| 		 | ||||
| 		DistanceMap computeDistanceMap(std::map<int, Tileset> & reverseDistanceMap) const; | ||||
| 		 | ||||
| 		Area getSubarea(std::function<bool(const int3 &)> filter) const; | ||||
| 		 | ||||
| 		bool connected() const; //is connected | ||||
| 		bool empty() const; | ||||
| 		bool contains(const int3 & tile) const; | ||||
| 		bool contains(const std::vector<int3> & tiles) const; | ||||
| 		bool contains(const Area & area) const; | ||||
| 		bool overlap(const Area & area) const; | ||||
| 		bool overlap(const std::vector<int3> & tiles) const; | ||||
| 		int distanceSqr(const int3 & tile) const; | ||||
| 		int distanceSqr(const Area & area) const; | ||||
| 		int3 nearest(const int3 & tile) const; | ||||
| 		int3 nearest(const Area & area) const; | ||||
| 		 | ||||
| 		void clear(); | ||||
| 		void assign(const Tileset tiles); //do not use reference to allow assigment of cached data | ||||
| 		void add(const int3 & tile); | ||||
| 		void erase(const int3 & tile); | ||||
| 		void unite(const Area & area); | ||||
| 		void intersect(const Area & area); | ||||
| 		void subtract(const Area & area); | ||||
| 		void translate(const int3 & shift); | ||||
| 		 | ||||
| 		friend Area operator+ (const Area & l, const int3 & r); //translation | ||||
| 		friend Area operator- (const Area & l, const int3 & r); //translation | ||||
| 		friend Area operator+ (const Area & l, const Area & r); //union | ||||
| 		friend Area operator* (const Area & l, const Area & r); //intersection | ||||
| 		friend Area operator- (const Area & l, const Area & r); //AreaL reduced by tiles from AreaR | ||||
| 		friend bool operator== (const Area & l, const Area & r); | ||||
| 		friend std::list<Area> connectedAreas(const Area & area); | ||||
| 		 | ||||
| 	private: | ||||
| 		 | ||||
| 		void invalidate(); | ||||
| 		void computeBorderCache(); | ||||
| 		void computeBorderOutsideCache(); | ||||
| 		 | ||||
| 		mutable Tileset dTiles; | ||||
| 		mutable std::vector<int3> dTilesVectorCache; | ||||
| 		mutable Tileset dBorderCache; | ||||
| 		mutable Tileset dBorderOutsideCache; | ||||
| 		mutable int3 dTotalShiftCache; | ||||
| 	}; | ||||
| } | ||||
							
								
								
									
										343
									
								
								lib/rmg/RmgMap.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								lib/rmg/RmgMap.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,343 @@ | ||||
| /* | ||||
|  * RmgMap.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "RmgMap.h" | ||||
| #include "TileInfo.h" | ||||
| #include "CMapGenOptions.h" | ||||
| #include "Zone.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
| #include "../CTownHandler.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "RoadPlacer.h" | ||||
| #include "TreasurePlacer.h" | ||||
| #include "ConnectionsPlacer.h" | ||||
| #include "TownPlacer.h" | ||||
| #include "WaterAdopter.h" | ||||
| #include "WaterProxy.h" | ||||
| #include "WaterRoutes.h" | ||||
| #include "RockPlacer.h" | ||||
| #include "ObstaclePlacer.h" | ||||
| #include "RiverPlacer.h" | ||||
| #include "TerrainPainter.h" | ||||
| #include "Functions.h" | ||||
| #include "CMapGenerator.h" | ||||
|  | ||||
| static const int3 dirs4[] = {int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0)}; | ||||
| static const int3 dirsDiagonal[] = { int3(1,1,0),int3(1,-1,0),int3(-1,1,0),int3(-1,-1,0) }; | ||||
|  | ||||
| RmgMap::RmgMap(const CMapGenOptions& mapGenOptions) : | ||||
| 	mapGenOptions(mapGenOptions), zonesTotal(0) | ||||
| { | ||||
| 	mapInstance = std::make_unique<CMap>(); | ||||
| 	getEditManager()->getUndoManager().setUndoRedoLimit(0); | ||||
| } | ||||
|  | ||||
| void RmgMap::foreach_neighbour(const int3 &pos, std::function<void(int3& pos)> foo) | ||||
| { | ||||
| 	for(const int3 &dir : int3::getDirs()) | ||||
| 	{ | ||||
| 		int3 n = pos + dir; | ||||
| 		/*important notice: perform any translation before this function is called, | ||||
| 		 so the actual mapInstance->position is checked*/ | ||||
| 		if(mapInstance->isInTheMap(n)) | ||||
| 			foo(n); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void RmgMap::foreachDirectNeighbour(const int3& pos, std::function<void(int3& pos)> foo) | ||||
| { | ||||
| 	for(const int3 &dir : dirs4) | ||||
| 	{ | ||||
| 		int3 n = pos + dir; | ||||
| 		if(mapInstance->isInTheMap(n)) | ||||
| 			foo(n); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void RmgMap::foreachDiagonalNeighbour(const int3& pos, std::function<void(int3& pos)> foo) | ||||
| { | ||||
| 	for (const int3 &dir : dirsDiagonal) | ||||
| 	{ | ||||
| 		int3 n = pos + dir; | ||||
| 		if (mapInstance->isInTheMap(n)) | ||||
| 			foo(n); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void RmgMap::initTiles(CMapGenerator & generator) | ||||
| { | ||||
| 	mapInstance->initTerrain(); | ||||
| 	 | ||||
| 	tiles.resize(boost::extents[mapInstance->width][mapInstance->height][mapInstance->twoLevel ? 2 : 1]); | ||||
| 	zoneColouring.resize(boost::extents[mapInstance->width][mapInstance->height][mapInstance->twoLevel ? 2 : 1]); | ||||
| 	 | ||||
| 	//init native town count with 0 | ||||
| 	for (auto faction : VLC->townh->getAllowedFactions()) | ||||
| 		zonesPerFaction[faction] = 0; | ||||
| 	 | ||||
| 	getEditManager()->clearTerrain(&generator.rand); | ||||
| 	getEditManager()->getTerrainSelection().selectRange(MapRect(int3(0, 0, 0), mapGenOptions.getWidth(), mapGenOptions.getHeight())); | ||||
| 	getEditManager()->drawTerrain(Terrain("grass"), &generator.rand); | ||||
| 	 | ||||
| 	auto tmpl = mapGenOptions.getMapTemplate(); | ||||
| 	zones.clear(); | ||||
| 	for(const auto & option : tmpl->getZones()) | ||||
| 	{ | ||||
| 		auto zone = std::make_shared<Zone>(*this, generator); | ||||
| 		zone->setOptions(*option.second.get()); | ||||
| 		zones[zone->getId()] = zone; | ||||
| 	} | ||||
| 	 | ||||
| 	switch(mapGenOptions.getWaterContent()) | ||||
| 	{ | ||||
| 		case EWaterContent::NORMAL: | ||||
| 		case EWaterContent::ISLANDS: | ||||
| 			TRmgTemplateZoneId waterId = zones.size() + 1; | ||||
| 			rmg::ZoneOptions options; | ||||
| 			options.setId(waterId); | ||||
| 			options.setType(ETemplateZoneType::WATER); | ||||
| 			auto zone = std::make_shared<Zone>(*this, generator); | ||||
| 			zone->setOptions(options); | ||||
| 			zones[zone->getId()] = zone; | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void RmgMap::addModificators() | ||||
| { | ||||
| 	for(auto & z : getZones()) | ||||
| 	{ | ||||
| 		auto zone = z.second; | ||||
| 		 | ||||
| 		zone->addModificator<ObjectManager>(); | ||||
| 		zone->addModificator<TreasurePlacer>(); | ||||
| 		zone->addModificator<ObstaclePlacer>(); | ||||
| 		zone->addModificator<TerrainPainter>(); | ||||
| 		 | ||||
| 		if(zone->getType() == ETemplateZoneType::WATER) | ||||
| 		{ | ||||
| 			for(auto & z1 : getZones()) | ||||
| 			{ | ||||
| 				z1.second->addModificator<WaterAdopter>(); | ||||
| 				z1.second->getModificator<WaterAdopter>()->setWaterZone(zone->getId()); | ||||
| 			} | ||||
| 			zone->addModificator<WaterProxy>(); | ||||
| 			zone->addModificator<WaterRoutes>(); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			zone->addModificator<TownPlacer>(); | ||||
| 			zone->addModificator<ConnectionsPlacer>(); | ||||
| 			zone->addModificator<RoadPlacer>(); | ||||
| 			zone->addModificator<RiverPlacer>(); | ||||
| 		} | ||||
| 		 | ||||
| 		if(zone->isUnderground()) | ||||
| 		{ | ||||
| 			zone->addModificator<RockPlacer>(); | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| RmgMap::~RmgMap() | ||||
| { | ||||
| } | ||||
|  | ||||
| CMap & RmgMap::map() const | ||||
| { | ||||
| 	return *mapInstance; | ||||
| } | ||||
|  | ||||
| CMapEditManager* RmgMap::getEditManager() const | ||||
| { | ||||
| 	return mapInstance->getEditManager(); | ||||
| } | ||||
|  | ||||
| bool RmgMap::isOnMap(const int3 & tile) const | ||||
| { | ||||
| 	return mapInstance->isInTheMap(tile); | ||||
| } | ||||
|  | ||||
| const CMapGenOptions& RmgMap::getMapGenOptions() const | ||||
| { | ||||
| 	return mapGenOptions; | ||||
| } | ||||
|  | ||||
| void RmgMap::assertOnMap(const int3& tile) const | ||||
| { | ||||
| 	if (!mapInstance->isInTheMap(tile)) | ||||
| 		throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile.toString())); | ||||
| } | ||||
|  | ||||
| RmgMap::Zones & RmgMap::getZones() | ||||
| { | ||||
| 	return zones; | ||||
| } | ||||
|  | ||||
| bool RmgMap::isBlocked(const int3 &tile) const | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	return tiles[tile.x][tile.y][tile.z].isBlocked(); | ||||
| } | ||||
|  | ||||
| bool RmgMap::shouldBeBlocked(const int3 &tile) const | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	return tiles[tile.x][tile.y][tile.z].shouldBeBlocked(); | ||||
| } | ||||
|  | ||||
| bool RmgMap::isPossible(const int3 &tile) const | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	return tiles[tile.x][tile.y][tile.z].isPossible(); | ||||
| } | ||||
|  | ||||
| bool RmgMap::isFree(const int3 &tile) const | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	return tiles[tile.x][tile.y][tile.z].isFree(); | ||||
| } | ||||
|  | ||||
| bool RmgMap::isUsed(const int3 &tile) const | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	return tiles[tile.x][tile.y][tile.z].isUsed(); | ||||
| } | ||||
|  | ||||
| bool RmgMap::isRoad(const int3& tile) const | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	return tiles[tile.x][tile.y][tile.z].isRoad(); | ||||
| } | ||||
|  | ||||
| void RmgMap::setOccupied(const int3 &tile, ETileType::ETileType state) | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	tiles[tile.x][tile.y][tile.z].setOccupied(state); | ||||
| } | ||||
|  | ||||
| void RmgMap::setRoad(const int3& tile, const std::string & roadType) | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	tiles[tile.x][tile.y][tile.z].setRoadType(roadType); | ||||
| } | ||||
|  | ||||
| TileInfo RmgMap::getTile(const int3& tile) const | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	return tiles[tile.x][tile.y][tile.z]; | ||||
| } | ||||
|  | ||||
| TRmgTemplateZoneId RmgMap::getZoneID(const int3& tile) const | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	return zoneColouring[tile.x][tile.y][tile.z]; | ||||
| } | ||||
|  | ||||
| void RmgMap::setZoneID(const int3& tile, TRmgTemplateZoneId zid) | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	zoneColouring[tile.x][tile.y][tile.z] = zid; | ||||
| } | ||||
|  | ||||
| void RmgMap::setNearestObjectDistance(int3 &tile, float value) | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	tiles[tile.x][tile.y][tile.z].setNearestObjectDistance(value); | ||||
| } | ||||
|  | ||||
| float RmgMap::getNearestObjectDistance(const int3 &tile) const | ||||
| { | ||||
| 	assertOnMap(tile); | ||||
| 	 | ||||
| 	return tiles[tile.x][tile.y][tile.z].getNearestObjectDistance(); | ||||
| } | ||||
|  | ||||
| void RmgMap::registerZone(TFaction faction) | ||||
| { | ||||
| 	zonesPerFaction[faction]++; | ||||
| 	zonesTotal++; | ||||
| } | ||||
|  | ||||
| ui32 RmgMap::getZoneCount(TFaction faction) | ||||
| { | ||||
| 	return zonesPerFaction[faction]; | ||||
| } | ||||
|  | ||||
| ui32 RmgMap::getTotalZoneCount() const | ||||
| { | ||||
| 	return zonesTotal; | ||||
| } | ||||
|  | ||||
| bool RmgMap::isAllowedSpell(SpellID sid) const | ||||
| { | ||||
| 	assert(sid >= 0); | ||||
| 	if (sid < mapInstance->allowedSpell.size()) | ||||
| 	{ | ||||
| 		return mapInstance->allowedSpell[sid]; | ||||
| 	} | ||||
| 	else | ||||
| 		return false; | ||||
| } | ||||
|  | ||||
| void RmgMap::dump(bool zoneId) const | ||||
| { | ||||
| 	static int id = 0; | ||||
| 	std::ofstream out(boost::to_string(boost::format("zone_%d.txt") % id++)); | ||||
| 	int levels = mapInstance->twoLevel ? 2 : 1; | ||||
| 	int width =  mapInstance->width; | ||||
| 	int height = mapInstance->height; | ||||
| 	for (int k = 0; k < levels; k++) | ||||
| 	{ | ||||
| 		for(int j = 0; j < height; j++) | ||||
| 		{ | ||||
| 			for (int i = 0; i < width; i++) | ||||
| 			{ | ||||
| 				if(zoneId) | ||||
| 				{ | ||||
| 					out << getZoneID(int3(i, j, k)); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					char t = '?'; | ||||
| 					switch (getTile(int3(i, j, k)).getTileType()) | ||||
| 					{ | ||||
| 						case ETileType::FREE: | ||||
| 							t = ' '; break; | ||||
| 						case ETileType::BLOCKED: | ||||
| 							t = '#'; break; | ||||
| 						case ETileType::POSSIBLE: | ||||
| 							t = '-'; break; | ||||
| 						case ETileType::USED: | ||||
| 							t = 'O'; break; | ||||
| 					} | ||||
| 					out << t; | ||||
| 				} | ||||
| 			} | ||||
| 			out << std::endl; | ||||
| 		} | ||||
| 		out << std::endl; | ||||
| 	} | ||||
| 	out << std::endl; | ||||
| } | ||||
							
								
								
									
										81
									
								
								lib/rmg/RmgMap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								lib/rmg/RmgMap.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| /* | ||||
|  * RmgMap.cpp, 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 "../int3.h" | ||||
| #include "../GameConstants.h" | ||||
| #include "../mapping/CMap.h" | ||||
|  | ||||
| class CMapEditManager; | ||||
| class TileInfo; | ||||
| class CMapGenOptions; | ||||
| class Zone; | ||||
| class CMapGenerator; | ||||
|  | ||||
| class RmgMap | ||||
| { | ||||
| public: | ||||
| 	mutable std::unique_ptr<CMap> mapInstance; | ||||
| 	CMap & map() const; | ||||
| 	 | ||||
| 	RmgMap(const CMapGenOptions& mapGenOptions); | ||||
| 	~RmgMap(); | ||||
| 	 | ||||
| 	CMapEditManager* getEditManager() const; | ||||
| 	const CMapGenOptions& getMapGenOptions() const; | ||||
| 	 | ||||
| 	void foreach_neighbour(const int3 &pos, std::function<void(int3& pos)> foo); | ||||
| 	void foreachDirectNeighbour(const int3 &pos, std::function<void(int3& pos)> foo); | ||||
| 	void foreachDiagonalNeighbour(const int3& pos, std::function<void(int3& pos)> foo); | ||||
| 	 | ||||
| 	bool isBlocked(const int3 &tile) const; | ||||
| 	bool shouldBeBlocked(const int3 &tile) const; | ||||
| 	bool isPossible(const int3 &tile) const; | ||||
| 	bool isFree(const int3 &tile) const; | ||||
| 	bool isUsed(const int3 &tile) const; | ||||
| 	bool isRoad(const int3 &tile) const; | ||||
| 	bool isOnMap(const int3 & tile) const; | ||||
| 	 | ||||
| 	void setOccupied(const int3 &tile, ETileType::ETileType state); | ||||
| 	void setRoad(const int3 &tile, const std::string & roadType); | ||||
| 	 | ||||
| 	TileInfo getTile(const int3 & tile) const; | ||||
| 		 | ||||
| 	float getNearestObjectDistance(const int3 &tile) const; | ||||
| 	void setNearestObjectDistance(int3 &tile, float value); | ||||
| 	 | ||||
| 	TRmgTemplateZoneId getZoneID(const int3& tile) const; | ||||
| 	void setZoneID(const int3& tile, TRmgTemplateZoneId zid); | ||||
| 	 | ||||
| 	using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<Zone>>; | ||||
| 	 | ||||
| 	Zones & getZones(); | ||||
| 	 | ||||
| 	void registerZone(TFaction faction); | ||||
| 	ui32 getZoneCount(TFaction faction); | ||||
| 	ui32 getTotalZoneCount() const; | ||||
| 	void initTiles(CMapGenerator & generator); | ||||
| 	void addModificators(); | ||||
| 	 | ||||
| 	bool isAllowedSpell(SpellID sid) const; | ||||
| 	 | ||||
| 	void dump(bool zoneId) const; | ||||
| 	 | ||||
| private: | ||||
| 	void assertOnMap(const int3 &tile) const; //throws | ||||
| 	 | ||||
| private: | ||||
| 	Zones zones; | ||||
| 	std::map<TFaction, ui32> zonesPerFaction; | ||||
| 	ui32 zonesTotal; //zones that have their main town only | ||||
| 	const CMapGenOptions& mapGenOptions; | ||||
| 	boost::multi_array<TileInfo, 3> tiles; //[x][y][z] | ||||
| 	boost::multi_array<TRmgTemplateZoneId, 3> zoneColouring; //[x][y][z] | ||||
| }; | ||||
							
								
								
									
										327
									
								
								lib/rmg/RmgObject.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								lib/rmg/RmgObject.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,327 @@ | ||||
| /* | ||||
|  * RmgObject.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "RmgObject.h" | ||||
| #include "RmgMap.h" | ||||
| #include "../mapObjects/CObjectHandler.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../VCMI_Lib.h" | ||||
| #include "../mapObjects/CommonConstructors.h" | ||||
| #include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h | ||||
| #include "Functions.h" | ||||
|  | ||||
| using namespace rmg; | ||||
|  | ||||
| Object::Instance::Instance(const Object& parent, CGObjectInstance & object): dParent(parent), dObject(object) | ||||
| { | ||||
| 	setPosition(dPosition); | ||||
| } | ||||
|  | ||||
| Object::Instance::Instance(const Object& parent, CGObjectInstance & object, const int3 & position): Instance(parent, object) | ||||
| { | ||||
| 	setPosition(position); | ||||
| } | ||||
|  | ||||
| const Area & Object::Instance::getBlockedArea() const | ||||
| { | ||||
| 	if(dBlockedAreaCache.empty()) | ||||
| 	{ | ||||
| 		dBlockedAreaCache.assign(dObject.getBlockedPos()); | ||||
| 		if(dObject.isVisitable() || dBlockedAreaCache.empty()) | ||||
| 			dBlockedAreaCache.add(dObject.visitablePos()); | ||||
| 	} | ||||
| 	return dBlockedAreaCache; | ||||
| } | ||||
|  | ||||
| int3 Object::Instance::getPosition(bool isAbsolute) const | ||||
| { | ||||
| 	if(isAbsolute) | ||||
| 		return dPosition + dParent.getPosition(); | ||||
| 	else | ||||
| 		return dPosition; | ||||
| } | ||||
|  | ||||
| int3 Object::Instance::getVisitablePosition() const | ||||
| { | ||||
| 	return dObject.visitablePos(); | ||||
| } | ||||
|  | ||||
| const rmg::Area & Object::Instance::getAccessibleArea() const | ||||
| { | ||||
| 	if(dAccessibleAreaCache.empty()) | ||||
| 	{ | ||||
| 		auto neighbours = rmg::Area({getVisitablePosition()}).getBorderOutside(); | ||||
| 		rmg::Area visitable = rmg::Area(neighbours) - getBlockedArea(); | ||||
| 		for(auto & from : visitable.getTiles()) | ||||
| 		{ | ||||
| 			if(isVisitableFrom(from)) | ||||
| 				dAccessibleAreaCache.add(from); | ||||
| 		} | ||||
| 	} | ||||
| 	return dAccessibleAreaCache; | ||||
| } | ||||
|  | ||||
| void Object::Instance::setPosition(const int3 & position) | ||||
| { | ||||
| 	dPosition = position; | ||||
| 	dObject.pos = dPosition + dParent.getPosition(); | ||||
| 	 | ||||
| 	dBlockedAreaCache.clear(); | ||||
| 	dAccessibleAreaCache.clear(); | ||||
| 	dParent.dAccessibleAreaCache.clear(); | ||||
| 	dParent.dAccessibleAreaFullCache.clear(); | ||||
| 	dParent.dFullAreaCache.clear(); | ||||
| } | ||||
|  | ||||
| void Object::Instance::setPositionRaw(const int3 & position) | ||||
| { | ||||
| 	if(!dObject.pos.valid()) | ||||
| 	{ | ||||
| 		dObject.pos = dPosition + dParent.getPosition(); | ||||
| 		dBlockedAreaCache.clear(); | ||||
| 		dAccessibleAreaCache.clear(); | ||||
| 		dParent.dAccessibleAreaCache.clear(); | ||||
| 		dParent.dAccessibleAreaFullCache.clear(); | ||||
| 		dParent.dFullAreaCache.clear(); | ||||
| 	} | ||||
| 		 | ||||
| 	auto shift = position + dParent.getPosition() - dObject.pos; | ||||
| 	 | ||||
| 	dAccessibleAreaCache.translate(shift); | ||||
| 	dBlockedAreaCache.translate(shift); | ||||
| 	 | ||||
| 	dPosition = position; | ||||
| 	dObject.pos = dPosition + dParent.getPosition(); | ||||
| } | ||||
|  | ||||
| void Object::Instance::setTemplate(const Terrain & terrain) | ||||
| { | ||||
| 	if(dObject.appearance.id == Obj::NO_OBJ) | ||||
| 	{ | ||||
| 		auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrain); | ||||
| 		if(templates.empty()) | ||||
| 			throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s") % dObject.ID % dObject.subID % static_cast<std::string>(terrain))); | ||||
| 		 | ||||
| 		dObject.appearance = templates.front(); | ||||
| 	} | ||||
| 	dAccessibleAreaCache.clear(); | ||||
| 	setPosition(getPosition(false)); | ||||
| } | ||||
|  | ||||
| void Object::Instance::clear() | ||||
| { | ||||
| 	delete &dObject; | ||||
| 	dBlockedAreaCache.clear(); | ||||
| 	dAccessibleAreaCache.clear(); | ||||
| 	dParent.dAccessibleAreaCache.clear(); | ||||
| 	dParent.dAccessibleAreaFullCache.clear(); | ||||
| 	dParent.dFullAreaCache.clear(); | ||||
| } | ||||
|  | ||||
| bool Object::Instance::isVisitableFrom(const int3 & position) const | ||||
| { | ||||
| 	auto relPosition = position - getPosition(true); | ||||
| 	return dObject.appearance.isVisitableFrom(relPosition.x, relPosition.y); | ||||
| } | ||||
|  | ||||
| CGObjectInstance & Object::Instance::object() | ||||
| { | ||||
| 	return dObject; | ||||
| } | ||||
|  | ||||
| const CGObjectInstance & Object::Instance::object() const | ||||
| { | ||||
| 	return dObject; | ||||
| } | ||||
|  | ||||
| Object::Object(CGObjectInstance & object, const int3 & position) | ||||
| { | ||||
| 	addInstance(object, position); | ||||
| } | ||||
|  | ||||
| Object::Object(CGObjectInstance & object) | ||||
| { | ||||
| 	addInstance(object); | ||||
| } | ||||
|  | ||||
| Object::Object(const Object & object) | ||||
| { | ||||
| 	dStrenght = object.dStrenght; | ||||
| 	for(auto & i : object.dInstances) | ||||
| 		addInstance(const_cast<CGObjectInstance &>(i.object()), i.getPosition()); | ||||
| 	setPosition(object.getPosition()); | ||||
| } | ||||
|  | ||||
| std::list<Object::Instance*> Object::instances() | ||||
| { | ||||
| 	std::list<Object::Instance*> result; | ||||
| 	for(auto & i : dInstances) | ||||
| 		result.push_back(&i); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| std::list<const Object::Instance*> Object::instances() const | ||||
| { | ||||
| 	std::list<const Object::Instance*> result; | ||||
| 	for(const auto & i : dInstances) | ||||
| 		result.push_back(&i); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| void Object::addInstance(Instance & object) | ||||
| { | ||||
| 	//assert(object.dParent == *this); | ||||
| 	dInstances.push_back(object); | ||||
| 	dFullAreaCache.clear(); | ||||
| 	dAccessibleAreaCache.clear(); | ||||
| 	dAccessibleAreaFullCache.clear(); | ||||
| } | ||||
|  | ||||
| Object::Instance & Object::addInstance(CGObjectInstance & object) | ||||
| { | ||||
| 	dInstances.emplace_back(*this, object); | ||||
| 	dFullAreaCache.clear(); | ||||
| 	dAccessibleAreaCache.clear(); | ||||
| 	dAccessibleAreaFullCache.clear(); | ||||
| 	return dInstances.back(); | ||||
| } | ||||
|  | ||||
| Object::Instance & Object::addInstance(CGObjectInstance & object, const int3 & position) | ||||
| { | ||||
| 	dInstances.emplace_back(*this, object, position); | ||||
| 	dFullAreaCache.clear(); | ||||
| 	dAccessibleAreaCache.clear(); | ||||
| 	dAccessibleAreaFullCache.clear(); | ||||
| 	return dInstances.back(); | ||||
| } | ||||
|  | ||||
| const int3 & Object::getPosition() const | ||||
| { | ||||
| 	return dPosition; | ||||
| } | ||||
|  | ||||
| int3 Object::getVisitablePosition() const | ||||
| { | ||||
| 	assert(!dInstances.empty()); | ||||
| 	for(auto & instance : dInstances) | ||||
| 		if(!getArea().contains(instance.getVisitablePosition())) | ||||
| 			return instance.getVisitablePosition(); | ||||
| 	 | ||||
| 	return dInstances.back().getVisitablePosition(); //fallback - return position of last object | ||||
| } | ||||
|  | ||||
| const rmg::Area & Object::getAccessibleArea(bool exceptLast) const | ||||
| { | ||||
| 	if(dInstances.empty()) | ||||
| 		return dAccessibleAreaFullCache; | ||||
| 	if(exceptLast && !dAccessibleAreaCache.empty()) | ||||
| 		return dAccessibleAreaCache; | ||||
| 	if(!exceptLast && !dAccessibleAreaFullCache.empty()) | ||||
| 		return dAccessibleAreaFullCache; | ||||
| 	 | ||||
| 	for(auto i = dInstances.begin(); i != std::prev(dInstances.end()); ++i) | ||||
| 		dAccessibleAreaCache.unite(i->getAccessibleArea()); | ||||
| 	 | ||||
| 	dAccessibleAreaFullCache = dAccessibleAreaCache; | ||||
| 	dAccessibleAreaFullCache.unite(dInstances.back().getAccessibleArea()); | ||||
| 	dAccessibleAreaCache.subtract(getArea()); | ||||
| 	dAccessibleAreaFullCache.subtract(getArea()); | ||||
| 	 | ||||
| 	if(exceptLast) | ||||
| 		return dAccessibleAreaCache; | ||||
| 	else | ||||
| 		return dAccessibleAreaFullCache; | ||||
| } | ||||
|  | ||||
| void Object::setPosition(const int3 & position) | ||||
| { | ||||
| 	dAccessibleAreaCache.translate(position - dPosition); | ||||
| 	dAccessibleAreaFullCache.translate(position - dPosition); | ||||
| 	dFullAreaCache.translate(position - dPosition); | ||||
| 	 | ||||
| 	dPosition = position; | ||||
| 	for(auto& i : dInstances) | ||||
| 		i.setPositionRaw(i.getPosition()); | ||||
| } | ||||
|  | ||||
| void Object::setTemplate(const Terrain & terrain) | ||||
| { | ||||
| 	for(auto& i : dInstances) | ||||
| 		i.setTemplate(terrain); | ||||
| } | ||||
|  | ||||
| const Area & Object::getArea() const | ||||
| { | ||||
| 	if(!dFullAreaCache.empty() || dInstances.empty()) | ||||
| 		return dFullAreaCache; | ||||
| 	 | ||||
| 	for(const auto & instance : dInstances) | ||||
| 	{ | ||||
| 		dFullAreaCache.unite(instance.getBlockedArea()); | ||||
| 	} | ||||
| 	 | ||||
| 	return dFullAreaCache; | ||||
| } | ||||
|  | ||||
| void Object::Instance::finalize(RmgMap & map) | ||||
| { | ||||
| 	if(!map.isOnMap(getPosition(true))) | ||||
| 		throw rmgException(boost::to_string(boost::format("Position of object %d at %s is outside the map") % dObject.id % getPosition(true).toString())); | ||||
| 	 | ||||
| 	if (dObject.isVisitable() && !map.isOnMap(dObject.visitablePos())) | ||||
| 		throw rmgException(boost::to_string(boost::format("Visitable tile %s of object %d at %s is outside the map") % dObject.visitablePos().toString() % dObject.id % dObject.pos.toString())); | ||||
| 	 | ||||
| 	for (auto & tile : dObject.getBlockedPos()) | ||||
| 	{ | ||||
| 		if(!map.isOnMap(tile)) | ||||
| 			throw rmgException(boost::to_string(boost::format("Tile %s of object %d at %s is outside the map") % tile.toString() % dObject.id % dObject.pos.toString())); | ||||
| 	} | ||||
| 	 | ||||
| 	if (dObject.appearance.id == Obj::NO_OBJ) | ||||
| 	{ | ||||
| 		auto terrainType = map.map().getTile(getPosition(true)).terType; | ||||
| 		auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType); | ||||
| 		if (templates.empty()) | ||||
| 			throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") % dObject.ID % dObject.subID % getPosition(true).toString() % terrainType)); | ||||
| 		 | ||||
| 		setTemplate(terrainType); | ||||
| 	} | ||||
| 	 | ||||
| 	for(auto & tile : getBlockedArea().getTilesVector()) | ||||
| 	{ | ||||
| 		map.setOccupied(tile, ETileType::ETileType::USED); | ||||
| 	} | ||||
| 	 | ||||
| 	map.getEditManager()->insertObject(&dObject); | ||||
| } | ||||
|  | ||||
| void Object::finalize(RmgMap & map) | ||||
| { | ||||
| 	if(dInstances.empty()) | ||||
| 		throw rmgException("Cannot finalize object without instances"); | ||||
| 	 | ||||
| 	for(auto iter = dInstances.begin(); iter != dInstances.end(); ++iter) | ||||
| 	{ | ||||
| 		iter->finalize(map); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Object::clear() | ||||
| { | ||||
| 	for(auto & instance : dInstances) | ||||
| 		instance.clear(); | ||||
| 	dInstances.clear(); | ||||
| 	dFullAreaCache.clear(); | ||||
| 	dAccessibleAreaCache.clear(); | ||||
| 	dAccessibleAreaFullCache.clear(); | ||||
| } | ||||
|   | ||||
							
								
								
									
										87
									
								
								lib/rmg/RmgObject.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								lib/rmg/RmgObject.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| /* | ||||
|  * RmgObject.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 "../GameConstants.h" | ||||
| #include "../int3.h" | ||||
| #include "RmgArea.h" | ||||
|  | ||||
| class CGObjectInstance; | ||||
| class RmgMap; | ||||
| class Terrain; | ||||
|  | ||||
| namespace rmg { | ||||
| class Object | ||||
| { | ||||
| public: | ||||
| 	 | ||||
| 	class Instance | ||||
| 	{ | ||||
| 	public: | ||||
| 		Instance(const Object& parent, CGObjectInstance & object); | ||||
| 		Instance(const Object& parent, CGObjectInstance & object, const int3 & position); | ||||
| 		 | ||||
| 		const Area & getBlockedArea() const; | ||||
| 		 | ||||
| 		int3 getVisitablePosition() const; | ||||
| 		bool isVisitableFrom(const int3 & tile) const; | ||||
| 		const Area & getAccessibleArea() const; | ||||
| 		void setTemplate(const Terrain & terrain); //cache invalidation | ||||
| 		 | ||||
| 		int3 getPosition(bool isAbsolute = false) const; | ||||
| 		void setPosition(const int3 & position); //cache invalidation | ||||
| 		void setPositionRaw(const int3 & position); //no cache invalidation | ||||
| 		const CGObjectInstance & object() const; | ||||
| 		CGObjectInstance & object(); | ||||
| 		 | ||||
| 		void finalize(RmgMap & map); //cache invalidation | ||||
| 		void clear(); | ||||
| 		 | ||||
| 	private: | ||||
| 		mutable Area dBlockedAreaCache; | ||||
| 		int3 dPosition; | ||||
| 		mutable Area dAccessibleAreaCache; | ||||
| 		CGObjectInstance & dObject; | ||||
| 		const Object & dParent; | ||||
| 	}; | ||||
| 	 | ||||
| 	Object() = default; | ||||
| 	Object(const Object & object); | ||||
| 	Object(CGObjectInstance & object); | ||||
| 	Object(CGObjectInstance & object, const int3 & position); | ||||
| 	 | ||||
| 	void addInstance(Instance & object); | ||||
| 	Instance & addInstance(CGObjectInstance & object); | ||||
| 	Instance & addInstance(CGObjectInstance & object, const int3 & position); | ||||
| 	 | ||||
| 	std::list<Instance*> instances(); | ||||
| 	std::list<const Instance*> instances() const; | ||||
| 	 | ||||
| 	int3 getVisitablePosition() const; | ||||
| 	const Area & getAccessibleArea(bool exceptLast = false) const; | ||||
| 	 | ||||
| 	const int3 & getPosition() const; | ||||
| 	void setPosition(const int3 & position); | ||||
| 	void setTemplate(const Terrain & terrain); | ||||
| 	 | ||||
| 	const Area & getArea() const;  //lazy cache invalidation | ||||
| 	 | ||||
| 	void finalize(RmgMap & map); | ||||
| 	void clear(); | ||||
| 	 | ||||
| private: | ||||
| 	std::list<Instance> dInstances; | ||||
| 	mutable Area dFullAreaCache; | ||||
| 	mutable Area dAccessibleAreaCache, dAccessibleAreaFullCache; | ||||
| 	int3 dPosition; | ||||
| 	ui32 dStrenght; | ||||
| }; | ||||
| } | ||||
							
								
								
									
										193
									
								
								lib/rmg/RmgPath.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								lib/rmg/RmgPath.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,193 @@ | ||||
| /* | ||||
|  * RmgPath.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "RmgPath.h" | ||||
| #include <boost/heap/priority_queue.hpp> //A* | ||||
|  | ||||
| using namespace rmg; | ||||
|  | ||||
| const std::function<float(const int3 &, const int3 &)> Path::DEFAULT_MOVEMENT_FUNCTION = | ||||
| [](const int3 & src, const int3 & dst) | ||||
| {	 | ||||
| 	return 1.f; | ||||
| }; | ||||
|  | ||||
| //A* priority queue | ||||
| typedef std::pair<int3, float> TDistance; | ||||
| struct NodeComparer | ||||
| { | ||||
| 	bool operator()(const TDistance & lhs, const TDistance & rhs) const | ||||
| 	{ | ||||
| 		return (rhs.second < lhs.second); | ||||
| 	} | ||||
| }; | ||||
| boost::heap::priority_queue<TDistance, boost::heap::compare<NodeComparer>> createPriorityQueue() | ||||
| { | ||||
| 	return boost::heap::priority_queue<TDistance, boost::heap::compare<NodeComparer>>(); | ||||
| } | ||||
|  | ||||
| Path::Path(const Area & area): dArea(&area) | ||||
| { | ||||
| } | ||||
|  | ||||
| Path::Path(const Area & area, const int3 & src): dArea(&area) | ||||
| { | ||||
| 	dPath.add(src); | ||||
| } | ||||
|  | ||||
| Path::Path(const Path & path): dArea(path.dArea), dPath(path.dPath) | ||||
| { | ||||
| 	 | ||||
| } | ||||
|  | ||||
| Path & Path::operator= (const Path & path) | ||||
| { | ||||
| 	//do not modify area | ||||
| 	dPath = path.dPath; | ||||
| 	return *this; | ||||
| } | ||||
|  | ||||
| bool Path::valid() const | ||||
| { | ||||
| 	return !dPath.empty(); | ||||
| } | ||||
|  | ||||
| Path Path::invalid() | ||||
| { | ||||
| 	return Path({}); | ||||
| } | ||||
|  | ||||
| Path Path::search(const Tileset & dst, bool straight, std::function<float(const int3 &, const int3 &)> moveCostFunction) const | ||||
| { | ||||
| 	//A* algorithm taken from Wiki http://en.wikipedia.org/wiki/A*_search_algorithm | ||||
| 	if(!dArea) | ||||
| 		return Path::invalid(); | ||||
| 	 | ||||
| 	auto resultArea = *dArea + dst; | ||||
| 	Path result(resultArea); | ||||
| 	if(dst.empty()) | ||||
| 		return result; | ||||
| 	 | ||||
| 	int3 src = rmg::Area(dst).nearest(dPath); | ||||
| 	result.connect(src); | ||||
| 	 | ||||
| 	Tileset closed;    // The set of nodes already evaluated. | ||||
| 	auto open = createPriorityQueue(); // The set of tentative nodes to be evaluated, initially containing the start node | ||||
| 	std::map<int3, int3> cameFrom;  // The map of navigated nodes. | ||||
| 	std::map<int3, float> distances; | ||||
| 	 | ||||
| 	cameFrom[src] = int3(-1, -1, -1); //first node points to finish condition | ||||
| 	distances[src] = 0; | ||||
| 	open.push(std::make_pair(src, 0.f)); | ||||
| 	// Cost from start along best known path. | ||||
| 	 | ||||
| 	while(!open.empty()) | ||||
| 	{ | ||||
| 		auto node = open.top(); | ||||
| 		open.pop(); | ||||
| 		int3 currentNode = node.first; | ||||
| 		 | ||||
| 		closed.insert(currentNode); | ||||
| 		 | ||||
| 		if(dPath.contains(currentNode)) //we reached connection, stop | ||||
| 		{ | ||||
| 			// Trace the path using the saved parent information and return path | ||||
| 			int3 backTracking = currentNode; | ||||
| 			while (cameFrom[backTracking].valid()) | ||||
| 			{ | ||||
| 				result.dPath.add(backTracking); | ||||
| 				backTracking = cameFrom[backTracking]; | ||||
| 			} | ||||
| 			return result; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			auto computeTileScore = [&open, &closed, &cameFrom, ¤tNode, &distances, &moveCostFunction, &result](const int3& pos) -> void | ||||
| 			{ | ||||
| 				if(closed.count(pos)) | ||||
| 					return; | ||||
| 				 | ||||
| 				if(!result.dArea->contains(pos)) | ||||
| 					return; | ||||
| 				 | ||||
| 				float movementCost = moveCostFunction(currentNode, pos) + currentNode.dist2d(pos); | ||||
| 				 | ||||
| 				float distance = distances[currentNode] + movementCost; //we prefer to use already free paths | ||||
| 				int bestDistanceSoFar = std::numeric_limits<int>::max(); | ||||
| 				auto it = distances.find(pos); | ||||
| 				if(it != distances.end()) | ||||
| 					bestDistanceSoFar = static_cast<int>(it->second); | ||||
| 				 | ||||
| 				if(distance < bestDistanceSoFar) | ||||
| 				{ | ||||
| 					cameFrom[pos] = currentNode; | ||||
| 					open.push(std::make_pair(pos, distance)); | ||||
| 					distances[pos] = distance; | ||||
| 				} | ||||
| 			}; | ||||
| 			 | ||||
| 			auto dirs = int3::getDirs(); | ||||
| 			std::vector<int3> neighbors(dirs.begin(), dirs.end()); | ||||
| 			if(straight) | ||||
| 				neighbors = { { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0) } }; | ||||
| 			for(auto & i : neighbors) | ||||
| 			{ | ||||
| 				computeTileScore(currentNode + i); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| 	 | ||||
| 	result.dPath.clear(); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| Path Path::search(const int3 & dst, bool straight, std::function<float(const int3 &, const int3 &)> moveCostFunction) const | ||||
| { | ||||
| 	return search(Tileset{dst}, straight, moveCostFunction); | ||||
| } | ||||
|  | ||||
| Path Path::search(const Area & dst, bool straight, std::function<float(const int3 &, const int3 &)> moveCostFunction) const | ||||
| { | ||||
| 	return search(dst.getTiles(), straight, moveCostFunction); | ||||
| } | ||||
|  | ||||
| Path Path::search(const Path & dst, bool straight, std::function<float(const int3 &, const int3 &)> moveCostFunction) const | ||||
| { | ||||
| 	assert(dst.dArea == dArea); | ||||
| 	return search(dst.dPath, straight, moveCostFunction); | ||||
| } | ||||
|  | ||||
| void Path::connect(const int3 & path) | ||||
| { | ||||
| 	dPath.add(path); | ||||
| } | ||||
|  | ||||
| void Path::connect(const Tileset & path) | ||||
| { | ||||
| 	Area a(path); | ||||
| 	dPath.unite(a); | ||||
| } | ||||
|  | ||||
| void Path::connect(const Area & path) | ||||
| { | ||||
| 	dPath.unite(path); | ||||
| } | ||||
|  | ||||
| void Path::connect(const Path & path) | ||||
| { | ||||
| 	dPath.unite(path.dPath); | ||||
| } | ||||
|  | ||||
| const Area & Path::getPathArea() const | ||||
| { | ||||
| 	return dPath; | ||||
| } | ||||
							
								
								
									
										49
									
								
								lib/rmg/RmgPath.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								lib/rmg/RmgPath.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| /* | ||||
|  * RmgPath.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 "../GameConstants.h" | ||||
| #include "../int3.h" | ||||
| #include "RmgArea.h" | ||||
|  | ||||
| namespace rmg | ||||
| { | ||||
| class Path | ||||
| { | ||||
| public: | ||||
| 	const static std::function<float(const int3 &, const int3 &)> DEFAULT_MOVEMENT_FUNCTION; | ||||
| 	 | ||||
| 	Path(const Area & area); | ||||
| 	Path(const Area & area, const int3 & src); | ||||
| 	Path(const Path & path); | ||||
| 	Path & operator= (const Path & path); | ||||
| 	bool valid() const; | ||||
| 	 | ||||
| 	Path search(const Tileset & dst, bool straight, std::function<float(const int3 &, const int3 &)> moveCostFunction = DEFAULT_MOVEMENT_FUNCTION) const; | ||||
| 	Path search(const int3 & dst, bool straight, std::function<float(const int3 &, const int3 &)> moveCostFunction = DEFAULT_MOVEMENT_FUNCTION) const; | ||||
| 	Path search(const Area & dst, bool straight, std::function<float(const int3 &, const int3 &)> moveCostFunction = DEFAULT_MOVEMENT_FUNCTION) const; | ||||
| 	Path search(const Path & dst, bool straight, std::function<float(const int3 &, const int3 &)> moveCostFunction = DEFAULT_MOVEMENT_FUNCTION) const; | ||||
| 	 | ||||
| 	void connect(const Path & path); | ||||
| 	void connect(const int3 & path); //TODO: force connection? | ||||
| 	void connect(const Area & path); //TODO: force connection? | ||||
| 	void connect(const Tileset & path); //TODO: force connection? | ||||
| 	 | ||||
| 	const Area & getPathArea() const; | ||||
| 	 | ||||
| 	static Path invalid(); | ||||
| 	 | ||||
| private: | ||||
| 	 | ||||
| 	const Area * dArea = nullptr; | ||||
| 	Area dPath; | ||||
| }; | ||||
| } | ||||
							
								
								
									
										105
									
								
								lib/rmg/RoadPlacer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								lib/rmg/RoadPlacer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| /* | ||||
|  * RoadPlacer.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "RoadPlacer.h" | ||||
| #include "Functions.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "RmgMap.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
| #include "RmgPath.h" | ||||
|  | ||||
| void RoadPlacer::process() | ||||
| { | ||||
| 	connectRoads(); | ||||
| } | ||||
|  | ||||
| rmg::Area & RoadPlacer::areaForRoads() | ||||
| { | ||||
| 	return areaRoads; | ||||
| } | ||||
|  | ||||
| rmg::Area & RoadPlacer::areaIsolated() | ||||
| { | ||||
| 	return isolated; | ||||
| } | ||||
|  | ||||
| const rmg::Area & RoadPlacer::getRoads() const | ||||
| { | ||||
| 	return roads; | ||||
| } | ||||
|  | ||||
| bool RoadPlacer::createRoad(const int3 & dst) | ||||
| { | ||||
| 	auto searchArea = zone.areaPossible() + areaRoads + zone.freePaths() - isolated + roads; | ||||
| 	 | ||||
| 	rmg::Path path(searchArea); | ||||
| 	path.connect(roads); | ||||
| 	 | ||||
| 	auto res = path.search(dst, true); | ||||
| 	if(!res.valid()) | ||||
| 	{ | ||||
| 		res = path.search(dst, false, [](const int3 & src, const int3 & dst) | ||||
| 		{ | ||||
| 			float weight = dst.dist2dSQ(src); | ||||
| 			return weight * weight; | ||||
| 		}); | ||||
| 		if(!res.valid()) | ||||
| 		{ | ||||
| 			logGlobal->warn("Failed to create road"); | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	roads.unite(res.getPathArea()); | ||||
| 	return true; | ||||
| 	 | ||||
| } | ||||
|  | ||||
| void RoadPlacer::drawRoads() | ||||
| { | ||||
| 	zone.areaPossible().subtract(roads); | ||||
| 	zone.freePaths().unite(roads); | ||||
| 	map.getEditManager()->getTerrainSelection().setSelection(roads.getTilesVector()); | ||||
| 	map.getEditManager()->drawRoad(generator.getConfig().defaultRoadType, &generator.rand); | ||||
| } | ||||
|  | ||||
| void RoadPlacer::addRoadNode(const int3& node) | ||||
| { | ||||
| 	roadNodes.insert(node); | ||||
| } | ||||
|  | ||||
| void RoadPlacer::connectRoads() | ||||
| { | ||||
| 	if(roadNodes.empty()) | ||||
| 		return; | ||||
| 	 | ||||
| 	//take any tile from road nodes as destination zone for all other road nodes | ||||
| 	if(roads.empty()) | ||||
| 		roads.add(*roadNodes.begin()); | ||||
| 	 | ||||
| 	for(auto & node : roadNodes) | ||||
| 	{ | ||||
| 		createRoad(node); | ||||
| 	} | ||||
| 	 | ||||
| 	drawRoads(); | ||||
| } | ||||
|  | ||||
| char RoadPlacer::dump(const int3 & t) | ||||
| { | ||||
| 	if(roadNodes.count(t)) | ||||
| 		return '@'; | ||||
| 	if(roads.contains(t)) | ||||
| 		return '+'; | ||||
| 	if(isolated.contains(t)) | ||||
| 		return 'i'; | ||||
| 	return Modificator::dump(t); | ||||
| } | ||||
							
								
								
									
										37
									
								
								lib/rmg/RoadPlacer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								lib/rmg/RoadPlacer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| /* | ||||
|  * RoadPlacer.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 "Zone.h" | ||||
|  | ||||
| class RoadPlacer: public Modificator | ||||
| { | ||||
| public: | ||||
| 	MODIFICATOR(RoadPlacer); | ||||
| 	 | ||||
| 	void process() override; | ||||
| 	char dump(const int3 &) override; | ||||
| 	 | ||||
| 	void addRoadNode(const int3 & node); | ||||
| 	void connectRoads(); //fills "roads" according to "roadNodes" | ||||
| 	 | ||||
| 	rmg::Area & areaForRoads(); | ||||
| 	rmg::Area & areaIsolated(); | ||||
| 	const rmg::Area & getRoads() const; | ||||
| 	 | ||||
| protected: | ||||
| 	bool createRoad(const int3 & dst); | ||||
| 	void drawRoads(); //actually updates tiles | ||||
| 	 | ||||
| protected: | ||||
| 	rmg::Tileset roadNodes; //tiles to be connected with roads | ||||
| 	rmg::Area roads; //all tiles with roads | ||||
| 	rmg::Area areaRoads, isolated; | ||||
| }; | ||||
							
								
								
									
										103
									
								
								lib/rmg/RockPlacer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								lib/rmg/RockPlacer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| /* | ||||
|  * RockPlacer.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "RockPlacer.h" | ||||
| #include "TreasurePlacer.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "RoadPlacer.h" | ||||
| #include "RiverPlacer.h" | ||||
| #include "RmgMap.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "Functions.h" | ||||
| #include "../CRandomGenerator.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
|  | ||||
| void RockPlacer::process() | ||||
| { | ||||
| 	rockTerrain = Terrain::Manager::getInfo(zone.getTerrainType()).rockTerrain; | ||||
| 	assert(!rockTerrain.isPassable()); | ||||
| 	 | ||||
| 	accessibleArea = zone.freePaths() + zone.areaUsed(); | ||||
| 	if(auto * m = zone.getModificator<ObjectManager>()) | ||||
| 		accessibleArea.unite(m->getVisitableArea()); | ||||
| 	 | ||||
| 	//negative approach - create rock tiles first, then make sure all accessible tiles have no rock | ||||
| 	rockArea = zone.area().getSubarea([this](const int3 & t) | ||||
| 	{ | ||||
| 		return map.shouldBeBlocked(t); | ||||
| 	}); | ||||
| 	 | ||||
| 	for(auto & z : map.getZones()) | ||||
| 	{ | ||||
| 		if(auto * m = z.second->getModificator<RockPlacer>()) | ||||
| 		{ | ||||
| 			if(m != this && !m->isFinished()) | ||||
| 				return; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	processMap(); | ||||
| } | ||||
|  | ||||
| void RockPlacer::processMap() | ||||
| { | ||||
| 	//merge all areas | ||||
| 	for(auto & z : map.getZones()) | ||||
| 	{ | ||||
| 		if(auto * m = z.second->getModificator<RockPlacer>()) | ||||
| 		{ | ||||
| 			map.getEditManager()->getTerrainSelection().setSelection(m->rockArea.getTilesVector()); | ||||
| 			map.getEditManager()->drawTerrain(m->rockTerrain, &generator.rand); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	for(auto & z : map.getZones()) | ||||
| 	{ | ||||
| 		if(auto * m = z.second->getModificator<RockPlacer>()) | ||||
| 		{ | ||||
| 			//now make sure all accessible tiles have no additional rock on them | ||||
| 			map.getEditManager()->getTerrainSelection().setSelection(m->accessibleArea.getTilesVector()); | ||||
| 			map.getEditManager()->drawTerrain(z.second->getTerrainType(), &generator.rand); | ||||
| 			m->postProcess(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void RockPlacer::postProcess() | ||||
| { | ||||
| 	//finally mark rock tiles as occupied, spawn no obstacles there | ||||
| 	rockArea = zone.area().getSubarea([this](const int3 & t) | ||||
| 	{ | ||||
| 		return !map.map().getTile(t).terType.isPassable(); | ||||
| 	}); | ||||
| 	 | ||||
| 	zone.areaUsed().unite(rockArea); | ||||
| 	zone.areaPossible().subtract(rockArea); | ||||
| 	if(auto * m = zone.getModificator<RiverPlacer>()) | ||||
| 		m->riverProhibit().unite(rockArea); | ||||
| 	if(auto * m = zone.getModificator<RoadPlacer>()) | ||||
| 		m->areaIsolated().unite(rockArea); | ||||
| } | ||||
|  | ||||
| void RockPlacer::init() | ||||
| { | ||||
| 	POSTFUNCTION_ALL(RoadPlacer); | ||||
| 	DEPENDENCY(TreasurePlacer); | ||||
| } | ||||
|  | ||||
| char RockPlacer::dump(const int3 & t) | ||||
| { | ||||
| 	if(!map.map().getTile(t).terType.isPassable()) | ||||
| 	{ | ||||
| 		return zone.area().contains(t) ? 'R' : 'E'; | ||||
| 	} | ||||
| 	return Modificator::dump(t); | ||||
| } | ||||
							
								
								
									
										30
									
								
								lib/rmg/RockPlacer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								lib/rmg/RockPlacer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| /* | ||||
|  * RockPlacer.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 "Zone.h" | ||||
|  | ||||
| class RockPlacer: public Modificator | ||||
| { | ||||
| public: | ||||
| 	MODIFICATOR(RockPlacer); | ||||
| 	 | ||||
| 	void process() override; | ||||
| 	void init() override; | ||||
| 	char dump(const int3 &) override; | ||||
| 	 | ||||
| 	void processMap(); | ||||
| 	void postProcess(); | ||||
| 	 | ||||
| protected: | ||||
| 	 | ||||
| 	rmg::Area rockArea, accessibleArea; | ||||
| 	Terrain rockTerrain; | ||||
| }; | ||||
							
								
								
									
										35
									
								
								lib/rmg/TerrainPainter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								lib/rmg/TerrainPainter.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| /* | ||||
|  * TerrainPainter.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "TerrainPainter.h" | ||||
| #include "TownPlacer.h" | ||||
| #include "WaterAdopter.h" | ||||
| #include "WaterProxy.h" | ||||
| #include "ConnectionsPlacer.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "Functions.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "RmgMap.h" | ||||
|  | ||||
| void TerrainPainter::process() | ||||
| { | ||||
| 	initTerrainType(zone, generator); | ||||
| 	paintZoneTerrain(zone, generator.rand, map, zone.getTerrainType()); | ||||
| } | ||||
|  | ||||
| void TerrainPainter::init() | ||||
| { | ||||
| 	DEPENDENCY(TownPlacer); | ||||
| 	DEPENDENCY_ALL(WaterAdopter); | ||||
| 	POSTFUNCTION_ALL(WaterProxy); | ||||
| 	POSTFUNCTION_ALL(ConnectionsPlacer); | ||||
| 	POSTFUNCTION(ObjectManager); | ||||
| } | ||||
							
								
								
									
										21
									
								
								lib/rmg/TerrainPainter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								lib/rmg/TerrainPainter.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| /* | ||||
|  * TerrainPainter.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 "Zone.h" | ||||
|  | ||||
| class TerrainPainter: public Modificator | ||||
| { | ||||
| public: | ||||
| 	MODIFICATOR(TerrainPainter); | ||||
| 	 | ||||
| 	void process() override; | ||||
| 	void init() override; | ||||
| }; | ||||
							
								
								
									
										78
									
								
								lib/rmg/TileInfo.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								lib/rmg/TileInfo.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| /* | ||||
|  * TileInfo.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "TileInfo.h" | ||||
|  | ||||
| TileInfo::TileInfo():nearestObjectDistance(float(INT_MAX)), terrain() | ||||
| { | ||||
| 	occupied = ETileType::POSSIBLE; //all tiles are initially possible to place objects or passages | ||||
| } | ||||
|  | ||||
| float TileInfo::getNearestObjectDistance() const | ||||
| { | ||||
| 	return nearestObjectDistance; | ||||
| } | ||||
|  | ||||
| void TileInfo::setNearestObjectDistance(float value) | ||||
| { | ||||
| 	nearestObjectDistance = std::max<float>(0, value); //never negative (or unitialized) | ||||
| } | ||||
| bool TileInfo::shouldBeBlocked() const | ||||
| { | ||||
| 	return occupied == ETileType::BLOCKED; | ||||
| } | ||||
| bool TileInfo::isBlocked() const | ||||
| { | ||||
| 	return occupied == ETileType::BLOCKED || occupied == ETileType::USED; | ||||
| } | ||||
| bool TileInfo::isPossible() const | ||||
| { | ||||
| 	return occupied == ETileType::POSSIBLE; | ||||
| } | ||||
| bool TileInfo::isFree() const | ||||
| { | ||||
| 	return occupied == ETileType::FREE; | ||||
| } | ||||
|  | ||||
| bool TileInfo::isRoad() const | ||||
| { | ||||
| 	return roadType != ROAD_NAMES[0]; | ||||
| } | ||||
|  | ||||
| bool TileInfo::isUsed() const | ||||
| { | ||||
| 	return occupied == ETileType::USED; | ||||
| } | ||||
| void TileInfo::setOccupied(ETileType::ETileType value) | ||||
| { | ||||
| 	occupied = value; | ||||
| } | ||||
|  | ||||
| ETileType::ETileType TileInfo::getTileType() const | ||||
| { | ||||
| 	return occupied; | ||||
| } | ||||
|  | ||||
| Terrain TileInfo::getTerrainType() const | ||||
| { | ||||
| 	return terrain; | ||||
| } | ||||
|  | ||||
| void TileInfo::setTerrainType(Terrain value) | ||||
| { | ||||
| 	terrain = value; | ||||
| } | ||||
|  | ||||
| void TileInfo::setRoadType(const std::string & value) | ||||
| { | ||||
| 	roadType = value; | ||||
| 	//	setOccupied(ETileType::FREE); | ||||
| } | ||||
							
								
								
									
										41
									
								
								lib/rmg/TileInfo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/rmg/TileInfo.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
|  * TileInfo.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 "../GameConstants.h" | ||||
| #include "../Terrain.h" | ||||
|  | ||||
| class TileInfo | ||||
| { | ||||
| public: | ||||
| 	 | ||||
| 	TileInfo(); | ||||
| 	 | ||||
| 	float getNearestObjectDistance() const; | ||||
| 	void setNearestObjectDistance(float value); | ||||
| 	bool isBlocked() const; | ||||
| 	bool shouldBeBlocked() const; | ||||
| 	bool isPossible() const; | ||||
| 	bool isFree() const; | ||||
| 	bool isUsed() const; | ||||
| 	bool isRoad() const; | ||||
| 	void setOccupied(ETileType::ETileType value); | ||||
| 	Terrain getTerrainType() const; | ||||
| 	ETileType::ETileType getTileType() const; | ||||
| 	void setTerrainType(Terrain value); | ||||
| 	 | ||||
| 	void setRoadType(const std::string & value); | ||||
| private: | ||||
| 	float nearestObjectDistance; | ||||
| 	ETileType::ETileType occupied; | ||||
| 	Terrain terrain; | ||||
| 	std::string roadType; | ||||
| }; | ||||
							
								
								
									
										279
									
								
								lib/rmg/TownPlacer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								lib/rmg/TownPlacer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,279 @@ | ||||
| /* | ||||
|  * TownPlacer.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "TownPlacer.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "RmgMap.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
| #include "../mapObjects/CObjectClassesHandler.h" | ||||
| #include "../spells/CSpellHandler.h" //for choosing random spells | ||||
| #include "RmgPath.h" | ||||
| #include "RmgObject.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "Functions.h" | ||||
| #include "RoadPlacer.h" | ||||
| #include "WaterAdopter.h" | ||||
| #include "TileInfo.h" | ||||
|  | ||||
| void TownPlacer::process() | ||||
| { | ||||
| 	auto * manager = zone.getModificator<ObjectManager>(); | ||||
| 	if(!manager) | ||||
| 	{ | ||||
| 		logGlobal->error("ObjectManager doesn't exist for zone %d, skip modificator %s", zone.getId(), getName()); | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	placeTowns(*manager); | ||||
| 	placeMines(*manager); | ||||
| } | ||||
|  | ||||
| void TownPlacer::init() | ||||
| { | ||||
| 	POSTFUNCTION(ObjectManager); | ||||
| 	POSTFUNCTION(RoadPlacer); | ||||
| }  | ||||
|  | ||||
| void TownPlacer::placeTowns(ObjectManager & manager) | ||||
| { | ||||
| 	if((zone.getType() == ETemplateZoneType::CPU_START) || (zone.getType() == ETemplateZoneType::PLAYER_START)) | ||||
| 	{ | ||||
| 		//set zone types to player faction, generate main town | ||||
| 		logGlobal->info("Preparing playing zone"); | ||||
| 		int player_id = *zone.getOwner() - 1; | ||||
| 		auto & playerInfo = map.map().players[player_id]; | ||||
| 		PlayerColor player(player_id); | ||||
| 		if(playerInfo.canAnyonePlay()) | ||||
| 		{ | ||||
| 			player = PlayerColor(player_id); | ||||
| 			zone.setTownType(map.getMapGenOptions().getPlayersSettings().find(player)->second.getStartingTown()); | ||||
| 			 | ||||
| 			if(zone.getTownType() == CMapGenOptions::CPlayerSettings::RANDOM_TOWN) | ||||
| 				zone.setTownType(getRandomTownType(true)); | ||||
| 		} | ||||
| 		else //no player - randomize town | ||||
| 		{ | ||||
| 			player = PlayerColor::NEUTRAL; | ||||
| 			zone.setTownType(getRandomTownType()); | ||||
| 		} | ||||
| 		 | ||||
| 		auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, zone.getTownType()); | ||||
| 		 | ||||
| 		CGTownInstance * town = (CGTownInstance *) townFactory->create(ObjectTemplate()); | ||||
| 		town->tempOwner = player; | ||||
| 		town->builtBuildings.insert(BuildingID::FORT); | ||||
| 		town->builtBuildings.insert(BuildingID::DEFAULT); | ||||
| 		 | ||||
| 		for(auto spell : VLC->spellh->objects) //add all regular spells to town | ||||
| 		{ | ||||
| 			if(!spell->isSpecial() && !spell->isCreatureAbility()) | ||||
| 				town->possibleSpells.push_back(spell->id); | ||||
| 		} | ||||
| 		 | ||||
| 		auto position = placeMainTown(manager, *town); | ||||
| 		 | ||||
| 		totalTowns++; | ||||
| 		//register MAIN town of zone only | ||||
| 		map.registerZone(town->subID); | ||||
| 		 | ||||
| 		if(playerInfo.canAnyonePlay()) //configure info for owning player | ||||
| 		{ | ||||
| 			logGlobal->trace("Fill player info %d", player_id); | ||||
| 			 | ||||
| 			// Update player info | ||||
| 			playerInfo.allowedFactions.clear(); | ||||
| 			playerInfo.allowedFactions.insert(zone.getTownType()); | ||||
| 			playerInfo.hasMainTown = true; | ||||
| 			playerInfo.posOfMainTown = position; | ||||
| 			playerInfo.generateHeroAtMainTown = true; | ||||
| 			 | ||||
| 			//now create actual towns | ||||
| 			addNewTowns(zone.getPlayerTowns().getCastleCount() - 1, true, player, manager); | ||||
| 			addNewTowns(zone.getPlayerTowns().getTownCount(), false, player, manager); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			addNewTowns(zone.getPlayerTowns().getCastleCount() - 1, true, PlayerColor::NEUTRAL, manager); | ||||
| 			addNewTowns(zone.getPlayerTowns().getTownCount(), false, PlayerColor::NEUTRAL, manager); | ||||
| 		} | ||||
| 	} | ||||
| 	else //randomize town types for any other zones as well | ||||
| 	{ | ||||
| 		zone.setTownType(getRandomTownType()); | ||||
| 	} | ||||
| 	 | ||||
| 	addNewTowns(zone.getNeutralTowns().getCastleCount(), true, PlayerColor::NEUTRAL, manager); | ||||
| 	addNewTowns(zone.getNeutralTowns().getTownCount(), false, PlayerColor::NEUTRAL, manager); | ||||
| 	 | ||||
| 	if(!totalTowns) //if there's no town present, get random faction for dwellings and pandoras | ||||
| 	{ | ||||
| 		//25% chance for neutral | ||||
| 		if (generator.rand.nextInt(1, 100) <= 25) | ||||
| 		{ | ||||
| 			zone.setTownType(ETownType::NEUTRAL); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			if(zone.getTownTypes().size()) | ||||
| 				zone.setTownType(*RandomGeneratorUtil::nextItem(zone.getTownTypes(), generator.rand)); | ||||
| 			else if(zone.getMonsterTypes().size()) | ||||
| 				zone.setTownType(*RandomGeneratorUtil::nextItem(zone.getMonsterTypes(), generator.rand)); //this happens in Clash of Dragons in treasure zones, where all towns are banned | ||||
| 			else //just in any case | ||||
| 				zone.setTownType(getRandomTownType()); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town) | ||||
| { | ||||
| 	//towns are big objects and should be centered around visitable position | ||||
| 	rmg::Object rmgObject(town); | ||||
| 	auto position = manager.findPlaceForObject(zone.areaPossible(), rmgObject, [this](const int3 & t) | ||||
| 	{ | ||||
| 		float distance = zone.getPos().dist2dSQ(t); | ||||
| 		return 100000.f - distance; //some big number | ||||
| 	}, true); | ||||
| 	rmgObject.setPosition(position); | ||||
| 	manager.placeObject(rmgObject, false, true); | ||||
| 	cleanupBoundaries(rmgObject); | ||||
| 	zone.setPos(rmgObject.getVisitablePosition()); //roads lead to main town | ||||
| 	return position; | ||||
| } | ||||
|  | ||||
| bool TownPlacer::placeMines(ObjectManager & manager) | ||||
| { | ||||
| 	using namespace Res; | ||||
| 	std::vector<CGMine*> createdMines; | ||||
| 	 | ||||
| 	for(const auto & mineInfo : zone.getMinesInfo()) | ||||
| 	{ | ||||
| 		ERes res = (ERes)mineInfo.first; | ||||
| 		for(int i = 0; i < mineInfo.second; ++i) | ||||
| 		{ | ||||
| 			auto mineHandler = VLC->objtypeh->getHandlerFor(Obj::MINE, res); | ||||
| 			auto & rmginfo = mineHandler->getRMGInfo(); | ||||
| 			auto mine = (CGMine*)mineHandler->create(ObjectTemplate()); | ||||
| 			mine->producedResource = res; | ||||
| 			mine->tempOwner = PlayerColor::NEUTRAL; | ||||
| 			mine->producedQuantity = mine->defaultResProduction(); | ||||
| 			createdMines.push_back(mine); | ||||
| 			 | ||||
| 			 | ||||
| 			if(!i && (res == ERes::WOOD || res == ERes::ORE)) | ||||
| 				manager.addCloseObject(mine, rmginfo.value); //only first wood&ore mines are close | ||||
| 			else | ||||
| 				manager.addRequiredObject(mine, rmginfo.value); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//create extra resources | ||||
| 	if(int extraRes = generator.getConfig().mineExtraResources) | ||||
| 	{ | ||||
| 		for(auto * mine : createdMines) | ||||
| 		{ | ||||
| 			for(int rc = generator.rand.nextInt(1, extraRes); rc > 0; --rc) | ||||
| 			{ | ||||
| 				auto resourse = (CGResource*) VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create(ObjectTemplate()); | ||||
| 				resourse->amount = CGResource::RANDOM_AMOUNT; | ||||
| 				manager.addNearbyObject(resourse, mine); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void TownPlacer::cleanupBoundaries(const rmg::Object & rmgObject) | ||||
| { | ||||
| 	for(auto & t : rmgObject.getArea().getBorderOutside()) | ||||
| 	{ | ||||
| 		if(map.isOnMap(t)) | ||||
| 		{ | ||||
| 			map.setOccupied(t, ETileType::FREE); | ||||
| 			zone.areaPossible().erase(t); | ||||
| 			zone.freePaths().add(t); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void TownPlacer::addNewTowns(int count, bool hasFort, PlayerColor player, ObjectManager & manager) | ||||
| { | ||||
| 	for(int i = 0; i < count; i++) | ||||
| 	{ | ||||
| 		si32 subType = zone.getTownType(); | ||||
| 		 | ||||
| 		if(totalTowns>0) | ||||
| 		{ | ||||
| 			if(!zone.areTownsSameType()) | ||||
| 			{ | ||||
| 				if (zone.getTownTypes().size()) | ||||
| 					subType = *RandomGeneratorUtil::nextItem(zone.getTownTypes(), generator.rand); | ||||
| 				else | ||||
| 					subType = *RandomGeneratorUtil::nextItem(zone.getDefaultTownTypes(), generator.rand); //it is possible to have zone with no towns allowed | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, subType); | ||||
| 		auto town = (CGTownInstance *) townFactory->create(ObjectTemplate()); | ||||
| 		town->ID = Obj::TOWN; | ||||
| 		 | ||||
| 		town->tempOwner = player; | ||||
| 		if (hasFort) | ||||
| 			town->builtBuildings.insert(BuildingID::FORT); | ||||
| 		town->builtBuildings.insert(BuildingID::DEFAULT); | ||||
| 		 | ||||
| 		for(auto spell : VLC->spellh->objects) //add all regular spells to town | ||||
| 		{ | ||||
| 			if(!spell->isSpecial() && !spell->isCreatureAbility()) | ||||
| 				town->possibleSpells.push_back(spell->id); | ||||
| 		} | ||||
| 		 | ||||
| 		if(totalTowns <= 0) | ||||
| 		{ | ||||
| 			//FIXME: discovered bug with small zones - getPos is close to map boarder and we have outOfMap exception | ||||
| 			//register MAIN town of zone | ||||
| 			map.registerZone(town->subID); | ||||
| 			//first town in zone goes in the middle | ||||
| 			placeMainTown(manager, *town); | ||||
| 		} | ||||
| 		else | ||||
| 			manager.addRequiredObject(town); | ||||
| 		totalTowns++; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| si32 TownPlacer::getRandomTownType(bool matchUndergroundType) | ||||
| { | ||||
| 	auto townTypesAllowed = (zone.getTownTypes().size() ? zone.getTownTypes() : zone.getDefaultTownTypes()); | ||||
| 	if(matchUndergroundType) | ||||
| 	{ | ||||
| 		std::set<TFaction> townTypesVerify; | ||||
| 		for(TFaction factionIdx : townTypesAllowed) | ||||
| 		{ | ||||
| 			bool preferUnderground = (*VLC->townh)[factionIdx]->preferUndergroundPlacement; | ||||
| 			if(zone.isUnderground() ? preferUnderground : !preferUnderground) | ||||
| 			{ | ||||
| 				townTypesVerify.insert(factionIdx); | ||||
| 			} | ||||
| 		} | ||||
| 		if(!townTypesVerify.empty()) | ||||
| 			townTypesAllowed = townTypesVerify; | ||||
| 	} | ||||
| 	 | ||||
| 	return *RandomGeneratorUtil::nextItem(townTypesAllowed, generator.rand); | ||||
| } | ||||
|  | ||||
| int TownPlacer::getTotalTowns() const | ||||
| { | ||||
| 	return totalTowns; | ||||
| } | ||||
							
								
								
									
										37
									
								
								lib/rmg/TownPlacer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								lib/rmg/TownPlacer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| /* | ||||
|  * TownPlacer.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 "Zone.h" | ||||
|  | ||||
| class ObjectManager; | ||||
| class CGTownInstance; | ||||
|  | ||||
| class TownPlacer: public Modificator | ||||
| { | ||||
| public: | ||||
| 	MODIFICATOR(TownPlacer); | ||||
| 	 | ||||
| 	void process() override; | ||||
| 	void init() override; | ||||
| 	 | ||||
| 	int getTotalTowns() const; | ||||
| 	 | ||||
| protected: | ||||
| 	void cleanupBoundaries(const rmg::Object & rmgObject); | ||||
| 	void addNewTowns(int count, bool hasFort, PlayerColor player, ObjectManager & manager); | ||||
| 	si32 getRandomTownType(bool matchUndergroundType = false); | ||||
| 	void placeTowns(ObjectManager & manager); | ||||
| 	bool placeMines(ObjectManager & manager); | ||||
| 	int3 placeMainTown(ObjectManager & manager, CGTownInstance & town); | ||||
| 	 | ||||
| protected: | ||||
| 	 | ||||
| 	int totalTowns = 0; | ||||
| }; | ||||
							
								
								
									
										788
									
								
								lib/rmg/TreasurePlacer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										788
									
								
								lib/rmg/TreasurePlacer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,788 @@ | ||||
| /* | ||||
|  * TreasurePlacer.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "TreasurePlacer.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "Functions.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "RoadPlacer.h" | ||||
| #include "ConnectionsPlacer.h" | ||||
| #include "RmgMap.h" | ||||
| #include "TileInfo.h" | ||||
| #include "../mapObjects/CommonConstructors.h" | ||||
| #include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h | ||||
| #include "../CCreatureHandler.h" | ||||
| #include "../spells/CSpellHandler.h" //for choosing random spells | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
|  | ||||
| void TreasurePlacer::process() | ||||
| { | ||||
| 	addAllPossibleObjects(); | ||||
| 	auto * m = zone.getModificator<ObjectManager>(); | ||||
| 	if(m) | ||||
| 		createTreasures(*m); | ||||
| } | ||||
|  | ||||
| void TreasurePlacer::init() | ||||
| { | ||||
| 	DEPENDENCY(ObjectManager); | ||||
| 	DEPENDENCY(ConnectionsPlacer); | ||||
| 	POSTFUNCTION(RoadPlacer); | ||||
| } | ||||
|  | ||||
| void TreasurePlacer::setQuestArtZone(Zone * otherZone) | ||||
| { | ||||
| 	questArtZone = otherZone; | ||||
| } | ||||
|  | ||||
| void TreasurePlacer::addAllPossibleObjects() | ||||
| { | ||||
| 	ObjectInfo oi; | ||||
| 	 | ||||
| 	int numZones = static_cast<int>(map.getZones().size()); | ||||
| 	 | ||||
| 	for(auto primaryID : VLC->objtypeh->knownObjects()) | ||||
| 	{ | ||||
| 		for(auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID)) | ||||
| 		{ | ||||
| 			auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID); | ||||
| 			if(!handler->isStaticObject() && handler->getRMGInfo().value) | ||||
| 			{ | ||||
| 				for(auto temp : handler->getTemplates()) | ||||
| 				{ | ||||
| 					if(temp.canBePlacedAt(zone.getTerrainType())) | ||||
| 					{ | ||||
| 						oi.generateObject = [temp]() -> CGObjectInstance * | ||||
| 						{ | ||||
| 							return VLC->objtypeh->getHandlerFor(temp.id, temp.subid)->create(temp); | ||||
| 						}; | ||||
| 						auto rmgInfo = handler->getRMGInfo(); | ||||
| 						oi.value = rmgInfo.value; | ||||
| 						oi.probability = rmgInfo.rarity; | ||||
| 						oi.templ = temp; | ||||
| 						oi.maxPerZone = rmgInfo.zoneLimit; | ||||
| 						vstd::amin(oi.maxPerZone, rmgInfo.mapLimit / numZones); //simple, but should distribute objects evenly on large maps | ||||
| 						possibleObjects.push_back(oi); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	if(zone.getType() == ETemplateZoneType::WATER) | ||||
| 		return; | ||||
| 	 | ||||
| 	//prisons | ||||
| 	//levels 1, 5, 10, 20, 30 | ||||
| 	static int prisonsLevels = std::min(generator.getConfig().prisonExperience.size(), generator.getConfig().prisonValues.size()); | ||||
| 	for(int i = 0; i < prisonsLevels; i++) | ||||
| 	{ | ||||
| 		oi.generateObject = [i, this]() -> CGObjectInstance * | ||||
| 		{ | ||||
| 			std::vector<ui32> possibleHeroes; | ||||
| 			for(int j = 0; j < map.map().allowedHeroes.size(); j++) | ||||
| 			{ | ||||
| 				if(map.map().allowedHeroes[j]) | ||||
| 					possibleHeroes.push_back(j); | ||||
| 			} | ||||
| 			 | ||||
| 			auto hid = *RandomGeneratorUtil::nextItem(possibleHeroes, generator.rand); | ||||
| 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0); | ||||
| 			auto obj = (CGHeroInstance *) factory->create(ObjectTemplate()); | ||||
| 			 | ||||
| 			 | ||||
| 			obj->subID = hid; //will be initialized later | ||||
| 			obj->exp = generator.getConfig().prisonExperience[i]; | ||||
| 			obj->setOwner(PlayerColor::NEUTRAL); | ||||
| 			map.map().allowedHeroes[hid] = false; //ban this hero | ||||
| 			generator.decreasePrisonsRemaining(); | ||||
| 			obj->appearance = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0)->getTemplates(zone.getTerrainType()).front(); //can't init template with hero subID | ||||
| 			 | ||||
| 			return obj; | ||||
| 		}; | ||||
| 		oi.setTemplate(Obj::PRISON, 0, zone.getTerrainType()); | ||||
| 		oi.value = generator.getConfig().prisonValues[i]; | ||||
| 		oi.probability = 30; | ||||
| 		oi.maxPerZone = generator.getPrisonsRemaning() / 5; //probably not perfect, but we can't generate more prisons than hereos. | ||||
| 		possibleObjects.push_back(oi); | ||||
| 	} | ||||
| 	 | ||||
| 	//all following objects are unlimited | ||||
| 	oi.maxPerZone = std::numeric_limits<ui32>().max(); | ||||
| 	 | ||||
| 	std::vector<CCreature *> creatures; //native creatures for this zone | ||||
| 	for(auto cre : VLC->creh->objects) | ||||
| 	{ | ||||
| 		if(!cre->special && cre->faction == zone.getTownType()) | ||||
| 		{ | ||||
| 			creatures.push_back(cre); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//dwellings | ||||
| 	auto dwellingTypes = {Obj::CREATURE_GENERATOR1, Obj::CREATURE_GENERATOR4}; | ||||
| 	 | ||||
| 	for(auto dwellingType : dwellingTypes) | ||||
| 	{ | ||||
| 		auto subObjects = VLC->objtypeh->knownSubObjects(dwellingType); | ||||
| 		 | ||||
| 		if(dwellingType == Obj::CREATURE_GENERATOR1) | ||||
| 		{ | ||||
| 			//don't spawn original "neutral" dwellings that got replaced by Conflux dwellings in AB | ||||
| 			static int elementalConfluxROE[] = {7, 13, 16, 47}; | ||||
| 			for(int i = 0; i < 4; i++) | ||||
| 				vstd::erase_if_present(subObjects, elementalConfluxROE[i]); | ||||
| 		} | ||||
| 		 | ||||
| 		for(auto secondaryID : subObjects) | ||||
| 		{ | ||||
| 			auto dwellingHandler = dynamic_cast<const CDwellingInstanceConstructor *>(VLC->objtypeh->getHandlerFor(dwellingType, secondaryID).get()); | ||||
| 			auto creatures = dwellingHandler->getProducedCreatures(); | ||||
| 			if(creatures.empty()) | ||||
| 				continue; | ||||
| 			 | ||||
| 			auto cre = creatures.front(); | ||||
| 			if(cre->faction == zone.getTownType()) | ||||
| 			{ | ||||
| 				float nativeZonesCount = static_cast<float>(map.getZoneCount(cre->faction)); | ||||
| 				oi.value = static_cast<ui32>(cre->AIValue * cre->growth * (1 + (nativeZonesCount / map.getTotalZoneCount()) + (nativeZonesCount / 2))); | ||||
| 				oi.probability = 40; | ||||
| 				 | ||||
| 				for(auto tmplate : dwellingHandler->getTemplates()) | ||||
| 				{ | ||||
| 					if(tmplate.canBePlacedAt(zone.getTerrainType())) | ||||
| 					{ | ||||
| 						oi.generateObject = [tmplate, secondaryID, dwellingType]() -> CGObjectInstance * | ||||
| 						{ | ||||
| 							auto obj = VLC->objtypeh->getHandlerFor(dwellingType, secondaryID)->create(tmplate); | ||||
| 							obj->tempOwner = PlayerColor::NEUTRAL; | ||||
| 							return obj; | ||||
| 						}; | ||||
| 						 | ||||
| 						oi.templ = tmplate; | ||||
| 						possibleObjects.push_back(oi); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	for(int i = 0; i < generator.getConfig().scrollValues.size(); i++) | ||||
| 	{ | ||||
| 		oi.generateObject = [i, this]() -> CGObjectInstance * | ||||
| 		{ | ||||
| 			auto factory = VLC->objtypeh->getHandlerFor(Obj::SPELL_SCROLL, 0); | ||||
| 			auto obj = (CGArtifact *) factory->create(ObjectTemplate()); | ||||
| 			std::vector<SpellID> out; | ||||
| 			 | ||||
| 			for(auto spell : VLC->spellh->objects) //spellh size appears to be greater (?) | ||||
| 			{ | ||||
| 				if(map.isAllowedSpell(spell->id) && spell->level == i + 1) | ||||
| 				{ | ||||
| 					out.push_back(spell->id); | ||||
| 				} | ||||
| 			} | ||||
| 			auto a = CArtifactInstance::createScroll(*RandomGeneratorUtil::nextItem(out, generator.rand)); | ||||
| 			obj->storedArtifact = a; | ||||
| 			return obj; | ||||
| 		}; | ||||
| 		oi.setTemplate(Obj::SPELL_SCROLL, 0, zone.getTerrainType()); | ||||
| 		oi.value = generator.getConfig().scrollValues[i]; | ||||
| 		oi.probability = 30; | ||||
| 		possibleObjects.push_back(oi); | ||||
| 	} | ||||
| 	 | ||||
| 	//pandora box with gold | ||||
| 	for(int i = 1; i < 5; i++) | ||||
| 	{ | ||||
| 		oi.generateObject = [i]() -> CGObjectInstance * | ||||
| 		{ | ||||
| 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); | ||||
| 			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate()); | ||||
| 			obj->resources[Res::GOLD] = i * 5000; | ||||
| 			return obj; | ||||
| 		}; | ||||
| 		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType()); | ||||
| 		oi.value = i * generator.getConfig().pandoraMultiplierGold; | ||||
| 		oi.probability = 5; | ||||
| 		possibleObjects.push_back(oi); | ||||
| 	} | ||||
| 	 | ||||
| 	//pandora box with experience | ||||
| 	for(int i = 1; i < 5; i++) | ||||
| 	{ | ||||
| 		oi.generateObject = [i]() -> CGObjectInstance * | ||||
| 		{ | ||||
| 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); | ||||
| 			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate()); | ||||
| 			obj->gainedExp = i * 5000; | ||||
| 			return obj; | ||||
| 		}; | ||||
| 		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType()); | ||||
| 		oi.value = i * generator.getConfig().pandoraMultiplierExperience; | ||||
| 		oi.probability = 20; | ||||
| 		possibleObjects.push_back(oi); | ||||
| 	} | ||||
| 	 | ||||
| 	//pandora box with creatures | ||||
| 	const std::vector<int> & tierValues = generator.getConfig().pandoraCreatureValues; | ||||
| 	 | ||||
| 	auto creatureToCount = [tierValues](CCreature * creature) -> int | ||||
| 	{ | ||||
| 		if(!creature->AIValue || tierValues.empty()) //bug #2681 | ||||
| 			return 0; //this box won't be generated | ||||
| 		 | ||||
| 		int actualTier = creature->level > tierValues.size() ? | ||||
| 		tierValues.size() - 1 : | ||||
| 		creature->level - 1; | ||||
| 		float creaturesAmount = ((float)tierValues[actualTier]) / creature->AIValue; | ||||
| 		if(creaturesAmount <= 5) | ||||
| 		{ | ||||
| 			creaturesAmount = boost::math::round(creaturesAmount); //allow single monsters | ||||
| 			if(creaturesAmount < 1) | ||||
| 				return 0; | ||||
| 		} | ||||
| 		else if(creaturesAmount <= 12) | ||||
| 		{ | ||||
| 			(creaturesAmount /= 2) *= 2; | ||||
| 		} | ||||
| 		else if(creaturesAmount <= 50) | ||||
| 		{ | ||||
| 			creaturesAmount = boost::math::round(creaturesAmount / 5) * 5; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			creaturesAmount = boost::math::round(creaturesAmount / 10) * 10; | ||||
| 		} | ||||
| 		return static_cast<int>(creaturesAmount); | ||||
| 	}; | ||||
| 	 | ||||
| 	for(auto creature : creatures) | ||||
| 	{ | ||||
| 		int creaturesAmount = creatureToCount(creature); | ||||
| 		if(!creaturesAmount) | ||||
| 			continue; | ||||
| 		 | ||||
| 		oi.generateObject = [creature, creaturesAmount]() -> CGObjectInstance * | ||||
| 		{ | ||||
| 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); | ||||
| 			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate()); | ||||
| 			auto stack = new CStackInstance(creature, creaturesAmount); | ||||
| 			obj->creatures.putStack(SlotID(0), stack); | ||||
| 			return obj; | ||||
| 		}; | ||||
| 		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType()); | ||||
| 		oi.value = static_cast<ui32>((2 * (creature->AIValue) * creaturesAmount * (1 + (float)(map.getZoneCount(creature->faction)) / map.getTotalZoneCount())) / 3); | ||||
| 		oi.probability = 3; | ||||
| 		possibleObjects.push_back(oi); | ||||
| 	} | ||||
| 	 | ||||
| 	//Pandora with 12 spells of certain level | ||||
| 	for(int i = 1; i <= GameConstants::SPELL_LEVELS; i++) | ||||
| 	{ | ||||
| 		oi.generateObject = [i, this]() -> CGObjectInstance * | ||||
| 		{ | ||||
| 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); | ||||
| 			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate()); | ||||
| 			 | ||||
| 			std::vector <CSpell *> spells; | ||||
| 			for(auto spell : VLC->spellh->objects) | ||||
| 			{ | ||||
| 				if(map.isAllowedSpell(spell->id) && spell->level == i) | ||||
| 					spells.push_back(spell); | ||||
| 			} | ||||
| 			 | ||||
| 			RandomGeneratorUtil::randomShuffle(spells, generator.rand); | ||||
| 			for(int j = 0; j < std::min(12, (int)spells.size()); j++) | ||||
| 			{ | ||||
| 				obj->spells.push_back(spells[j]->id); | ||||
| 			} | ||||
| 			 | ||||
| 			return obj; | ||||
| 		}; | ||||
| 		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType()); | ||||
| 		oi.value = (i + 1) * generator.getConfig().pandoraMultiplierSpells; //5000 - 15000 | ||||
| 		oi.probability = 2; | ||||
| 		possibleObjects.push_back(oi); | ||||
| 	} | ||||
| 	 | ||||
| 	//Pandora with 15 spells of certain school | ||||
| 	for(int i = 0; i < 4; i++) | ||||
| 	{ | ||||
| 		oi.generateObject = [i, this]() -> CGObjectInstance * | ||||
| 		{ | ||||
| 			auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); | ||||
| 			auto obj = (CGPandoraBox *) factory->create(ObjectTemplate()); | ||||
| 			 | ||||
| 			std::vector <CSpell *> spells; | ||||
| 			for(auto spell : VLC->spellh->objects) | ||||
| 			{ | ||||
| 				if(map.isAllowedSpell(spell->id) && spell->school[(ESpellSchool)i]) | ||||
| 					spells.push_back(spell); | ||||
| 			} | ||||
| 			 | ||||
| 			RandomGeneratorUtil::randomShuffle(spells, generator.rand); | ||||
| 			for(int j = 0; j < std::min(15, (int)spells.size()); j++) | ||||
| 			{ | ||||
| 				obj->spells.push_back(spells[j]->id); | ||||
| 			} | ||||
| 			 | ||||
| 			return obj; | ||||
| 		}; | ||||
| 		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType()); | ||||
| 		oi.value = generator.getConfig().pandoraSpellSchool; | ||||
| 		oi.probability = 2; | ||||
| 		possibleObjects.push_back(oi); | ||||
| 	} | ||||
| 	 | ||||
| 	// Pandora box with 60 random spells | ||||
| 	 | ||||
| 	oi.generateObject = [this]() -> CGObjectInstance * | ||||
| 	{ | ||||
| 		auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); | ||||
| 		auto obj = (CGPandoraBox *) factory->create(ObjectTemplate()); | ||||
| 		 | ||||
| 		std::vector <CSpell *> spells; | ||||
| 		for(auto spell : VLC->spellh->objects) | ||||
| 		{ | ||||
| 			if(map.isAllowedSpell(spell->id)) | ||||
| 				spells.push_back(spell); | ||||
| 		} | ||||
| 		 | ||||
| 		RandomGeneratorUtil::randomShuffle(spells, generator.rand); | ||||
| 		for(int j = 0; j < std::min(60, (int)spells.size()); j++) | ||||
| 		{ | ||||
| 			obj->spells.push_back(spells[j]->id); | ||||
| 		} | ||||
| 		 | ||||
| 		return obj; | ||||
| 	}; | ||||
| 	oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType()); | ||||
| 	oi.value = generator.getConfig().pandoraSpell60; | ||||
| 	oi.probability = 2; | ||||
| 	possibleObjects.push_back(oi); | ||||
| 	 | ||||
| 	//seer huts with creatures or generic rewards | ||||
| 	 | ||||
| 	if(questArtZone) //we won't be placing seer huts if there is no zone left to place arties | ||||
| 	{ | ||||
| 		static const int genericSeerHuts = 8; | ||||
| 		int seerHutsPerType = 0; | ||||
| 		const int questArtsRemaining = static_cast<int>(generator.getQuestArtsRemaning().size()); | ||||
| 		 | ||||
| 		//general issue is that not many artifact types are available for quests | ||||
| 		 | ||||
| 		if(questArtsRemaining >= genericSeerHuts + (int)creatures.size()) | ||||
| 		{ | ||||
| 			seerHutsPerType = questArtsRemaining / (genericSeerHuts + (int)creatures.size()); | ||||
| 		} | ||||
| 		else if(questArtsRemaining >= genericSeerHuts) | ||||
| 		{ | ||||
| 			seerHutsPerType = 1; | ||||
| 		} | ||||
| 		oi.maxPerZone = seerHutsPerType; | ||||
| 		 | ||||
| 		RandomGeneratorUtil::randomShuffle(creatures, generator.rand); | ||||
| 		 | ||||
| 		auto generateArtInfo = [this](ArtifactID id) -> ObjectInfo | ||||
| 		{ | ||||
| 			ObjectInfo artInfo; | ||||
| 			artInfo.probability = std::numeric_limits<ui16>::max(); //99,9% to spawn that art in first treasure pile | ||||
| 			artInfo.maxPerZone = 1; | ||||
| 			artInfo.value = 2000; //treasure art | ||||
| 			artInfo.setTemplate(Obj::ARTIFACT, id, this->zone.getTerrainType()); | ||||
| 			artInfo.generateObject = [id]() -> CGObjectInstance * | ||||
| 			{ | ||||
| 				auto handler = VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, id); | ||||
| 				return handler->create(handler->getTemplates().front()); | ||||
| 			}; | ||||
| 			return artInfo; | ||||
| 		}; | ||||
| 		 | ||||
| 		for(int i = 0; i < std::min((int)creatures.size(), questArtsRemaining - genericSeerHuts); i++) | ||||
| 		{ | ||||
| 			auto creature = creatures[i]; | ||||
| 			int creaturesAmount = creatureToCount(creature); | ||||
| 			 | ||||
| 			if(!creaturesAmount) | ||||
| 				continue; | ||||
| 			 | ||||
| 			int randomAppearance = chooseRandomAppearance(generator.rand, Obj::SEER_HUT, zone.getTerrainType()); | ||||
| 			 | ||||
| 			oi.generateObject = [creature, creaturesAmount, randomAppearance, this, generateArtInfo]() -> CGObjectInstance * | ||||
| 			{ | ||||
| 				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); | ||||
| 				auto obj = (CGSeerHut *) factory->create(ObjectTemplate()); | ||||
| 				obj->rewardType = CGSeerHut::CREATURE; | ||||
| 				obj->rID = creature->idNumber; | ||||
| 				obj->rVal = creaturesAmount; | ||||
| 				 | ||||
| 				obj->quest->missionType = CQuest::MISSION_ART; | ||||
| 				ArtifactID artid = *RandomGeneratorUtil::nextItem(generator.getQuestArtsRemaning(), generator.rand); | ||||
| 				obj->quest->m5arts.push_back(artid); | ||||
| 				obj->quest->lastDay = -1; | ||||
| 				obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false; | ||||
| 				 | ||||
| 				generator.banQuestArt(artid); | ||||
| 				 | ||||
| 				 | ||||
| 				this->questArtZone->getModificator<TreasurePlacer>()->possibleObjects.push_back(generateArtInfo(artid)); | ||||
| 				 | ||||
| 				return obj; | ||||
| 			}; | ||||
| 			oi.setTemplate(Obj::SEER_HUT, randomAppearance, zone.getTerrainType()); | ||||
| 			oi.value = static_cast<ui32>(((2 * (creature->AIValue) * creaturesAmount * (1 + (float)(map.getZoneCount(creature->faction)) / map.getTotalZoneCount())) - 4000) / 3); | ||||
| 			oi.probability = 3; | ||||
| 			possibleObjects.push_back(oi); | ||||
| 		} | ||||
| 		 | ||||
| 		static int seerLevels = std::min(generator.getConfig().questValues.size(), generator.getConfig().questRewardValues.size()); | ||||
| 		for(int i = 0; i < seerLevels; i++) //seems that code for exp and gold reward is similiar | ||||
| 		{ | ||||
| 			int randomAppearance = chooseRandomAppearance(generator.rand, Obj::SEER_HUT, zone.getTerrainType()); | ||||
| 			 | ||||
| 			oi.setTemplate(Obj::SEER_HUT, randomAppearance, zone.getTerrainType()); | ||||
| 			oi.value = generator.getConfig().questValues[i]; | ||||
| 			oi.probability = 10; | ||||
| 			 | ||||
| 			oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance * | ||||
| 			{ | ||||
| 				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); | ||||
| 				auto obj = (CGSeerHut *) factory->create(ObjectTemplate()); | ||||
| 				 | ||||
| 				obj->rewardType = CGSeerHut::EXPERIENCE; | ||||
| 				obj->rID = 0; //unitialized? | ||||
| 				obj->rVal = generator.getConfig().questRewardValues[i]; | ||||
| 				 | ||||
| 				obj->quest->missionType = CQuest::MISSION_ART; | ||||
| 				ArtifactID artid = *RandomGeneratorUtil::nextItem(generator.getQuestArtsRemaning(), generator.rand); | ||||
| 				obj->quest->m5arts.push_back(artid); | ||||
| 				obj->quest->lastDay = -1; | ||||
| 				obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false; | ||||
| 				 | ||||
| 				generator.banQuestArt(artid); | ||||
| 				 | ||||
| 				this->questArtZone->getModificator<TreasurePlacer>()->possibleObjects.push_back(generateArtInfo(artid)); | ||||
| 				 | ||||
| 				return obj; | ||||
| 			}; | ||||
| 			 | ||||
| 			possibleObjects.push_back(oi); | ||||
| 			 | ||||
| 			oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance * | ||||
| 			{ | ||||
| 				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); | ||||
| 				auto obj = (CGSeerHut *) factory->create(ObjectTemplate()); | ||||
| 				obj->rewardType = CGSeerHut::RESOURCES; | ||||
| 				obj->rID = Res::GOLD; | ||||
| 				obj->rVal = generator.getConfig().questRewardValues[i]; | ||||
| 				 | ||||
| 				obj->quest->missionType = CQuest::MISSION_ART; | ||||
| 				ArtifactID artid = *RandomGeneratorUtil::nextItem(generator.getQuestArtsRemaning(), generator.rand); | ||||
| 				obj->quest->m5arts.push_back(artid); | ||||
| 				obj->quest->lastDay = -1; | ||||
| 				obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false; | ||||
| 				 | ||||
| 				generator.banQuestArt(artid); | ||||
| 				 | ||||
| 				this->questArtZone->getModificator<TreasurePlacer>()->possibleObjects.push_back(generateArtInfo(artid)); | ||||
| 				 | ||||
| 				return obj; | ||||
| 			}; | ||||
| 			 | ||||
| 			possibleObjects.push_back(oi); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool TreasurePlacer::isGuardNeededForTreasure(int value) | ||||
| { | ||||
| 	return zone.getType() != ETemplateZoneType::WATER && value > minGuardedValue; | ||||
| } | ||||
|  | ||||
| std::vector<ObjectInfo*> TreasurePlacer::prepareTreasurePile(const CTreasureInfo& treasureInfo) | ||||
| { | ||||
| 	std::vector<ObjectInfo*> objectInfos; | ||||
| 	int maxValue = treasureInfo.max; | ||||
| 	int minValue = treasureInfo.min; | ||||
| 	 | ||||
| 	const ui32 desiredValue = generator.rand.nextInt(minValue, maxValue); | ||||
| 	 | ||||
| 	int currentValue = 0; | ||||
| 	bool hasLargeObject = false; | ||||
| 	while(currentValue <= (int)desiredValue - 100) //no objects with value below 100 are available | ||||
| 	{ | ||||
| 		auto * oi = getRandomObject(desiredValue, currentValue, maxValue, !hasLargeObject); | ||||
| 		if(!oi) //fail | ||||
| 			break; | ||||
| 		 | ||||
| 		if(oi->templ.isVisitableFromTop()) | ||||
| 		{ | ||||
| 			objectInfos.push_back(oi); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			objectInfos.insert(objectInfos.begin(), oi); //large object shall at first place | ||||
| 			hasLargeObject = true; | ||||
| 		} | ||||
| 		 | ||||
| 		//remove from possible objects | ||||
| 		assert(oi->maxPerZone); | ||||
| 		oi->maxPerZone--; | ||||
| 		 | ||||
| 		currentValue += oi->value; | ||||
| 	} | ||||
| 	 | ||||
| 	return objectInfos; | ||||
| } | ||||
|  | ||||
| rmg::Object TreasurePlacer::constuctTreasurePile(const std::vector<ObjectInfo*> & treasureInfos) | ||||
| { | ||||
| 	rmg::Object rmgObject; | ||||
| 	for(auto & oi : treasureInfos) | ||||
| 	{ | ||||
| 		auto blockedArea = rmgObject.getArea(); | ||||
| 		auto accessibleArea = rmgObject.getAccessibleArea(); | ||||
| 		if(rmgObject.instances().empty()) | ||||
| 			accessibleArea.add(int3()); | ||||
| 		 | ||||
| 		auto * object = oi->generateObject(); | ||||
| 		object->appearance = oi->templ; | ||||
| 		auto & instance = rmgObject.addInstance(*object); | ||||
|  | ||||
| 		do | ||||
| 		{ | ||||
| 			if(accessibleArea.empty()) | ||||
| 			{ | ||||
| 				//fail - fallback | ||||
| 				rmgObject.clear(); | ||||
| 				return rmgObject; | ||||
| 			} | ||||
| 			 | ||||
| 			int3 nextPos = *RandomGeneratorUtil::nextItem(accessibleArea.getTiles(), generator.rand); | ||||
| 			instance.setPosition(nextPos - rmgObject.getPosition()); | ||||
| 			 | ||||
| 			auto instanceAccessibleArea = instance.getAccessibleArea(); | ||||
| 			if(instance.getBlockedArea().getTilesVector().size() == 1) | ||||
| 			{ | ||||
| 				if(instance.object().appearance.isVisitableFromTop() && instance.object().ID != Obj::CORPSE) | ||||
| 					instanceAccessibleArea.add(instance.getVisitablePosition()); | ||||
| 			} | ||||
| 			 | ||||
| 			//first object is good | ||||
| 			if(rmgObject.instances().size() == 1) | ||||
| 				break; | ||||
| 			 | ||||
| 			//condition for good position | ||||
| 			if(!blockedArea.overlap(instance.getBlockedArea()) && accessibleArea.overlap(instanceAccessibleArea)) | ||||
| 				break; | ||||
| 			 | ||||
| 			//fail - new position | ||||
| 			accessibleArea.erase(nextPos); | ||||
| 		} while(true); | ||||
| 	} | ||||
| 	return rmgObject; | ||||
| } | ||||
|  | ||||
| ObjectInfo * TreasurePlacer::getRandomObject(ui32 desiredValue, ui32 currentValue, ui32 maxValue, bool allowLargeObjects) | ||||
| { | ||||
| 	std::vector<std::pair<ui32, ObjectInfo*>> thresholds; //handle complex object via pointer | ||||
| 	ui32 total = 0; | ||||
| 	 | ||||
| 	//calculate actual treasure value range based on remaining value | ||||
| 	ui32 maxVal = maxValue - currentValue; | ||||
| 	ui32 minValue = static_cast<ui32>(0.25f * (desiredValue - currentValue)); | ||||
| 	 | ||||
| 	for(ObjectInfo & oi : possibleObjects) //copy constructor turned out to be costly | ||||
| 	{ | ||||
| 		if(oi.value > maxVal) | ||||
| 			break; //this assumes values are sorted in ascending order | ||||
| 		 | ||||
| 		if(!oi.templ.isVisitableFromTop() && !allowLargeObjects) | ||||
| 			continue; | ||||
| 		 | ||||
| 		if(oi.value >= minValue && oi.maxPerZone > 0) | ||||
| 		{ | ||||
| 			total += oi.probability; | ||||
| 			thresholds.push_back(std::make_pair(total, &oi)); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	if(thresholds.empty()) | ||||
| 	{ | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		int r = generator.rand.nextInt(1, total); | ||||
| 		 | ||||
| 		//binary search = fastest | ||||
| 		auto it = std::lower_bound(thresholds.begin(), thresholds.end(), r, | ||||
| 								   [](const std::pair<ui32, ObjectInfo*> &rhs, const int lhs)->bool | ||||
| 								   { | ||||
| 			return (int)rhs.first < lhs; | ||||
| 		}); | ||||
| 		return it->second; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void TreasurePlacer::createTreasures(ObjectManager & manager) | ||||
| { | ||||
| 	int mapMonsterStrength = map.getMapGenOptions().getMonsterStrength(); | ||||
| 	int monsterStrength = zone.zoneMonsterStrength + mapMonsterStrength - 1; //array index from 0 to 4 | ||||
| 	 | ||||
| 	static int minGuardedValues[] = { 6500, 4167, 3000, 1833, 1333 }; | ||||
| 	minGuardedValue = minGuardedValues[monsterStrength]; | ||||
| 	 | ||||
| 	auto valueComparator = [](const CTreasureInfo & lhs, const CTreasureInfo & rhs) -> bool | ||||
| 	{ | ||||
| 		return lhs.max > rhs.max; | ||||
| 	}; | ||||
| 	 | ||||
| 	auto restoreZoneLimits = [](const std::vector<ObjectInfo*> & treasurePile) | ||||
| 	{ | ||||
| 		for(auto * oi : treasurePile) | ||||
| 		{ | ||||
| 			oi->maxPerZone++; | ||||
| 		} | ||||
| 	}; | ||||
| 	 | ||||
| 	//place biggest treasures first at large distance, place smaller ones inbetween | ||||
| 	auto treasureInfo = zone.getTreasureInfo(); | ||||
| 	boost::sort(treasureInfo, valueComparator); | ||||
| 	 | ||||
| 	//sort treasures by ascending value so we can stop checking treasures with too high value | ||||
| 	boost::sort(possibleObjects, [](const ObjectInfo& oi1, const ObjectInfo& oi2) -> bool | ||||
| 	{ | ||||
| 		return oi1.value < oi2.value; | ||||
| 	}); | ||||
| 	 | ||||
| 	int totalDensity = 0; | ||||
| 	for (auto t : treasureInfo) | ||||
| 	{ | ||||
| 		//discard objects with too high value to be ever placed | ||||
| 		vstd::erase_if(possibleObjects, [t](const ObjectInfo& oi) -> bool | ||||
| 		{ | ||||
| 			return oi.value > t.max; | ||||
| 		}); | ||||
| 		 | ||||
| 		totalDensity += t.density; | ||||
| 		 | ||||
| 		//treasure density is inversely proportional to zone size but must be scaled back to map size | ||||
| 		//also, normalize it to zone count - higher count means relatively smaller zones | ||||
| 		 | ||||
| 		//this is squared distance for optimization purposes | ||||
| 		const float minDistance = std::max<float>((125.f / totalDensity), 2.0f); | ||||
| 		//distance lower than 2 causes objects to overlap and crash | ||||
| 		 | ||||
| 		while(true) | ||||
| 		{ | ||||
| 			auto treasurePileInfos = prepareTreasurePile(t); | ||||
| 			if(treasurePileInfos.empty()) | ||||
| 				break; | ||||
| 			 | ||||
| 			int value = std::accumulate(treasurePileInfos.begin(), treasurePileInfos.end(), 0, [](int v, const ObjectInfo * oi){return v + oi->value;}); | ||||
| 			 | ||||
| 			auto rmgObject = constuctTreasurePile(treasurePileInfos); | ||||
| 			if(rmgObject.instances().empty()) //handle incorrect placement | ||||
| 			{ | ||||
| 				restoreZoneLimits(treasurePileInfos); | ||||
| 				continue; | ||||
| 			} | ||||
| 			 | ||||
| 			//guard treasure pile | ||||
| 			bool guarded = isGuardNeededForTreasure(value); | ||||
| 			if(guarded) | ||||
| 				guarded = manager.addGuard(rmgObject, value); | ||||
| 			 | ||||
| 			int3 pos; | ||||
| 			auto possibleArea = zone.areaPossible(); | ||||
| 			 | ||||
| 			auto path = rmg::Path::invalid(); | ||||
| 			if(guarded) | ||||
| 			{ | ||||
| 				path = manager.placeAndConnectObject(possibleArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3 & tile) | ||||
| 				{ | ||||
| 					auto ti = map.getTile(tile); | ||||
| 					if(ti.getNearestObjectDistance() < minDistance) | ||||
| 						return -1.f; | ||||
| 					 | ||||
| 					auto guardedArea = rmgObject.instances().back()->getAccessibleArea(); | ||||
| 					auto areaToBlock = rmgObject.getAccessibleArea(true); | ||||
| 					areaToBlock.subtract(guardedArea); | ||||
| 					if(areaToBlock.overlap(zone.freePaths()) || areaToBlock.overlap(manager.getVisitableArea())) | ||||
| 						return -1.f; | ||||
| 					 | ||||
| 					return 1.f; | ||||
| 				}, guarded, false, false); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				path = manager.placeAndConnectObject(possibleArea, rmgObject, minDistance, guarded, false, false); | ||||
| 			} | ||||
| 			 | ||||
| 			if(path.valid()) | ||||
| 			{ | ||||
| 				//debug purposes | ||||
| 				treasureArea.unite(rmgObject.getArea()); | ||||
| 				if(guarded) | ||||
| 				{ | ||||
| 					guards.unite(rmgObject.instances().back()->getBlockedArea()); | ||||
| 					auto guardedArea = rmgObject.instances().back()->getAccessibleArea(); | ||||
| 					auto areaToBlock = rmgObject.getAccessibleArea(true); | ||||
| 					areaToBlock.subtract(guardedArea); | ||||
| 					treasureBlockArea.unite(areaToBlock); | ||||
| 				} | ||||
| 				zone.connectPath(path); | ||||
| 				manager.placeObject(rmgObject, guarded, true); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				restoreZoneLimits(treasurePileInfos); | ||||
| 				rmgObject.clear(); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| char TreasurePlacer::dump(const int3 & t) | ||||
| { | ||||
| 	if(guards.contains(t)) | ||||
| 		return '!'; | ||||
| 	if(treasureArea.contains(t)) | ||||
| 		return '$'; | ||||
| 	if(treasureBlockArea.contains(t)) | ||||
| 		return '*'; | ||||
| 	 | ||||
| 	return Modificator::dump(t); | ||||
| } | ||||
|  | ||||
|  | ||||
| ObjectInfo::ObjectInfo() | ||||
| : templ(), value(0), probability(0), maxPerZone(1) | ||||
| { | ||||
| 	 | ||||
| } | ||||
|  | ||||
| void ObjectInfo::setTemplate(si32 type, si32 subtype, Terrain terrainType) | ||||
| { | ||||
| 	auto templHandler = VLC->objtypeh->getHandlerFor(type, subtype); | ||||
| 	if(!templHandler) | ||||
| 		return; | ||||
| 	 | ||||
| 	auto templates = templHandler->getTemplates(terrainType); | ||||
| 	if(templates.empty()) | ||||
| 		return; | ||||
| 	 | ||||
| 	templ = templates.front(); | ||||
| } | ||||
							
								
								
									
										67
									
								
								lib/rmg/TreasurePlacer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								lib/rmg/TreasurePlacer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| /* | ||||
|  * TreasurePlacer.cpp, 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 "Zone.h" | ||||
| #include "../mapObjects/ObjectTemplate.h" | ||||
|  | ||||
| class CGObjectInstance; | ||||
| class ObjectManager; | ||||
| class RmgMap; | ||||
| class CMapGenerator; | ||||
|  | ||||
| struct ObjectInfo | ||||
| { | ||||
| 	ObjectTemplate templ; | ||||
| 	ui32 value = 0; | ||||
| 	ui16 probability = 0; | ||||
| 	ui32 maxPerZone = -1; | ||||
| 	//ui32 maxPerMap; //unused | ||||
| 	std::function<CGObjectInstance *()> generateObject; | ||||
| 	 | ||||
| 	void setTemplate(si32 type, si32 subtype, Terrain terrain); | ||||
| 	 | ||||
| 	ObjectInfo(); | ||||
| 	 | ||||
| 	bool operator==(const ObjectInfo& oi) const { return (templ == oi.templ); } | ||||
| }; | ||||
|  | ||||
| class TreasurePlacer: public Modificator | ||||
| { | ||||
| public: | ||||
| 	MODIFICATOR(TreasurePlacer); | ||||
| 	 | ||||
| 	void process() override; | ||||
| 	void init() override; | ||||
| 	char dump(const int3 &) override; | ||||
| 	 | ||||
| 	void createTreasures(ObjectManager & manager); | ||||
| 	 | ||||
| 	void setQuestArtZone(Zone * otherZone); | ||||
| 	void addAllPossibleObjects(); //add objects, including zone-specific, to possibleObjects | ||||
| 	 | ||||
| protected: | ||||
| 	bool isGuardNeededForTreasure(int value); | ||||
| 	 | ||||
| 	ObjectInfo * getRandomObject(ui32 desiredValue, ui32 currentValue, ui32 maxValue, bool allowLargeObjects); | ||||
| 	std::vector<ObjectInfo*> prepareTreasurePile(const CTreasureInfo & treasureInfo); | ||||
| 	rmg::Object constuctTreasurePile(const std::vector<ObjectInfo*> & treasureInfos); | ||||
| 	 | ||||
| 	 | ||||
| protected:	 | ||||
| 	std::vector<ObjectInfo> possibleObjects; | ||||
| 	int minGuardedValue = 0; | ||||
| 	 | ||||
| 	rmg::Area treasureArea; | ||||
| 	rmg::Area treasureBlockArea; | ||||
| 	rmg::Area guards; | ||||
| 	 | ||||
| 	Zone * questArtZone = nullptr; //artifacts required for Seer Huts will be placed here - or not if null | ||||
| }; | ||||
							
								
								
									
										261
									
								
								lib/rmg/WaterAdopter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								lib/rmg/WaterAdopter.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,261 @@ | ||||
| /* | ||||
|  * WaterAdopter.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "WaterAdopter.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "RmgMap.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
| #include "../mapObjects/CObjectClassesHandler.h" | ||||
| #include "RmgPath.h" | ||||
| #include "RmgObject.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "Functions.h" | ||||
| #include "RoadPlacer.h" | ||||
| #include "TreasurePlacer.h" | ||||
| #include "TownPlacer.h" | ||||
| #include "ConnectionsPlacer.h" | ||||
| #include "TileInfo.h" | ||||
|  | ||||
| void WaterAdopter::process() | ||||
| { | ||||
| 	createWater(map.getMapGenOptions().getWaterContent()); | ||||
| } | ||||
|  | ||||
| void WaterAdopter::init() | ||||
| { | ||||
| 	//make dependencies | ||||
| 	DEPENDENCY_ALL(WaterAdopter); | ||||
| 	DEPENDENCY(TownPlacer); | ||||
| 	POSTFUNCTION(ConnectionsPlacer); | ||||
| 	POSTFUNCTION(TreasurePlacer); | ||||
| } | ||||
|  | ||||
| void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent) | ||||
| { | ||||
| 	if(waterContent == EWaterContent::NONE || zone.isUnderground() || zone.getType() == ETemplateZoneType::WATER) | ||||
| 		return; //do nothing | ||||
| 	 | ||||
| 	distanceMap = zone.area().computeDistanceMap(reverseDistanceMap); | ||||
| 	 | ||||
| 	//add border tiles as water for ISLANDS | ||||
| 	if(waterContent == EWaterContent::ISLANDS) | ||||
| 	{ | ||||
| 		waterArea.unite(collectDistantTiles(zone, zone.getSize() + 1)); | ||||
| 		waterArea.unite(zone.area().getBorder()); | ||||
| 	} | ||||
| 	 | ||||
| 	//protect some parts from water for NORMAL | ||||
| 	if(waterContent == EWaterContent::NORMAL) | ||||
| 	{ | ||||
| 		waterArea.unite(collectDistantTiles(zone, zone.getSize() - 1)); | ||||
| 		auto sliceStart = RandomGeneratorUtil::nextItem(reverseDistanceMap[0], generator.rand); | ||||
| 		auto sliceEnd = RandomGeneratorUtil::nextItem(reverseDistanceMap[0], generator.rand); | ||||
| 		 | ||||
| 		//at least 25% without water | ||||
| 		bool endPassed = false; | ||||
| 		for(int counter = 0; counter < reverseDistanceMap[0].size() / 4 || !endPassed; ++sliceStart, ++counter) | ||||
| 		{ | ||||
| 			if(sliceStart == reverseDistanceMap[0].end()) | ||||
| 				sliceStart = reverseDistanceMap[0].begin(); | ||||
| 			 | ||||
| 			if(sliceStart == sliceEnd) | ||||
| 				endPassed = true; | ||||
| 			 | ||||
| 			noWaterArea.add(*sliceStart); | ||||
| 		} | ||||
| 		 | ||||
| 		rmg::Area noWaterSlice; | ||||
| 		for(int i = 1; i < reverseDistanceMap.size(); ++i) | ||||
| 		{ | ||||
| 			for(auto & t : reverseDistanceMap[i]) | ||||
| 			{ | ||||
| 				if(noWaterArea.distanceSqr(t) < 3) | ||||
| 					noWaterSlice.add(t); | ||||
| 			} | ||||
| 			noWaterArea.unite(noWaterSlice); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//generating some irregularity of coast | ||||
| 	int coastIdMax = sqrt(reverseDistanceMap.size()); //size of coastTilesMap shows the most distant tile from water | ||||
| 	assert(coastIdMax > 0); | ||||
| 	std::list<int3> tilesQueue; | ||||
| 	rmg::Tileset tilesChecked; | ||||
| 	for(int coastId = coastIdMax; coastId >= 0; --coastId) | ||||
| 	{ | ||||
| 		//amount of iterations shall be proportion of coast perimeter | ||||
| 		const int coastLength = reverseDistanceMap[coastId].size() / (coastId + 3); | ||||
| 		for(int coastIter = 0; coastIter < coastLength; ++coastIter) | ||||
| 		{ | ||||
| 			int3 tile = *RandomGeneratorUtil::nextItem(reverseDistanceMap[coastId], generator.rand); | ||||
| 			if(tilesChecked.find(tile) != tilesChecked.end()) | ||||
| 				continue; | ||||
| 			 | ||||
| 			if(map.isUsed(tile) || map.isFree(tile)) //prevent placing water nearby town | ||||
| 				continue; | ||||
| 			 | ||||
| 			tilesQueue.push_back(tile); | ||||
| 			tilesChecked.insert(tile); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//if tile is marked as water - connect it with "big" water | ||||
| 	while(!tilesQueue.empty()) | ||||
| 	{ | ||||
| 		int3 src = tilesQueue.front(); | ||||
| 		tilesQueue.pop_front(); | ||||
| 		 | ||||
| 		if(waterArea.contains(src)) | ||||
| 			continue; | ||||
| 		 | ||||
| 		waterArea.add(src); | ||||
| 				 | ||||
| 		map.foreach_neighbour(src, [&src, this, &tilesChecked, &tilesQueue](const int3 & dst) | ||||
| 		{ | ||||
| 			if(tilesChecked.count(dst)) | ||||
| 				return; | ||||
| 			 | ||||
| 			if(distanceMap[dst] >= 0 && distanceMap[src] - distanceMap[dst] == 1) | ||||
| 			{ | ||||
| 				tilesQueue.push_back(dst); | ||||
| 				tilesChecked.insert(dst); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| 	 | ||||
| 	waterArea.subtract(noWaterArea); | ||||
| 	 | ||||
| 	//start filtering of narrow places and coast atrifacts | ||||
| 	rmg::Area waterAdd; | ||||
| 	for(int coastId = 1; coastId <= coastIdMax; ++coastId) | ||||
| 	{ | ||||
| 		for(auto& tile : reverseDistanceMap[coastId]) | ||||
| 		{ | ||||
| 			//collect neighbout water tiles | ||||
| 			auto collectionLambda = [this](const int3 & t, std::set<int3> & outCollection) | ||||
| 			{ | ||||
| 				if(waterArea.contains(t)) | ||||
| 				{ | ||||
| 					reverseDistanceMap[0].insert(t); | ||||
| 					outCollection.insert(t); | ||||
| 				} | ||||
| 			}; | ||||
| 			std::set<int3> waterCoastDirect, waterCoastDiag; | ||||
| 			map.foreachDirectNeighbour(tile, std::bind(collectionLambda, std::placeholders::_1, std::ref(waterCoastDirect))); | ||||
| 			map.foreachDiagonalNeighbour(tile, std::bind(collectionLambda, std::placeholders::_1, std::ref(waterCoastDiag))); | ||||
| 			int waterCoastDirectNum = waterCoastDirect.size(); | ||||
| 			int waterCoastDiagNum = waterCoastDiag.size(); | ||||
| 			 | ||||
| 			//remove tiles which are mostly covered by water | ||||
| 			if(waterCoastDirectNum >= 3) | ||||
| 			{ | ||||
| 				waterAdd.add(tile); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if(waterCoastDiagNum == 4 && waterCoastDirectNum == 2) | ||||
| 			{ | ||||
| 				waterAdd.add(tile); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if(waterCoastDirectNum == 2 && waterCoastDiagNum >= 2) | ||||
| 			{ | ||||
| 				int3 diagSum, dirSum; | ||||
| 				for(auto & i : waterCoastDiag) | ||||
| 					diagSum += i - tile; | ||||
| 				for(auto & i : waterCoastDirect) | ||||
| 					dirSum += i - tile; | ||||
| 				if(diagSum == int3() || dirSum == int3()) | ||||
| 				{ | ||||
| 					waterAdd.add(tile); | ||||
| 					continue; | ||||
| 				} | ||||
| 				if(waterCoastDiagNum == 3 && diagSum != dirSum) | ||||
| 				{ | ||||
| 					waterAdd.add(tile); | ||||
| 					continue; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	waterArea.unite(waterAdd); | ||||
| 	 | ||||
| 	//filtering tiny "lakes" | ||||
| 	for(auto& tile : reverseDistanceMap[0]) //now it's only coast-water tiles | ||||
| 	{ | ||||
| 		if(!waterArea.contains(tile)) //for ground tiles | ||||
| 			continue; | ||||
| 		 | ||||
| 		std::vector<int3> groundCoast; | ||||
| 		map.foreachDirectNeighbour(tile, [this, &groundCoast](const int3 & t) | ||||
| 		{ | ||||
| 			if(!waterArea.contains(t) && zone.area().contains(t)) //for ground tiles of same zone | ||||
| 			{ | ||||
| 				groundCoast.push_back(t); | ||||
| 			} | ||||
| 		}); | ||||
| 		 | ||||
| 		if(groundCoast.size() >= 3) | ||||
| 		{ | ||||
| 			waterArea.erase(tile); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			if(groundCoast.size() == 2) | ||||
| 			{ | ||||
| 				if(groundCoast[0] + groundCoast[1] == int3()) | ||||
| 				{ | ||||
| 					waterArea.erase(tile); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	map.getZones()[waterZoneId]->area().unite(waterArea); | ||||
| 	zone.area().subtract(waterArea); | ||||
| 	zone.areaPossible().subtract(waterArea); | ||||
| 	distanceMap = zone.area().computeDistanceMap(reverseDistanceMap); | ||||
| } | ||||
|  | ||||
| void WaterAdopter::setWaterZone(TRmgTemplateZoneId water) | ||||
| { | ||||
| 	waterZoneId = water; | ||||
| } | ||||
|  | ||||
| rmg::Area WaterAdopter::getCoastTiles() const | ||||
| { | ||||
| 	if(reverseDistanceMap.empty()) | ||||
| 		return rmg::Area(); | ||||
| 	 | ||||
| 	return rmg::Area(reverseDistanceMap.at(0)); | ||||
| } | ||||
|  | ||||
| char WaterAdopter::dump(const int3 & t) | ||||
| { | ||||
| 	if(noWaterArea.contains(t)) | ||||
| 		return 'X'; | ||||
| 	if(waterArea.contains(t)) | ||||
| 		return '~'; | ||||
| 	 | ||||
| 	auto distanceMapIter = distanceMap.find(t); | ||||
| 	if(distanceMapIter != distanceMap.end()) | ||||
| 	{ | ||||
| 		if(distanceMapIter->second > 9) | ||||
| 			return '%'; | ||||
| 		 | ||||
| 		auto distStr = std::to_string(distanceMapIter->second); | ||||
| 		if(distStr.length() > 0) | ||||
| 			return distStr[0]; | ||||
| 	} | ||||
| 	 | ||||
| 	return Modificator::dump(t); | ||||
| } | ||||
							
								
								
									
										36
									
								
								lib/rmg/WaterAdopter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								lib/rmg/WaterAdopter.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| /* | ||||
|  * WaterAdopter.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 "Zone.h" | ||||
|  | ||||
| class WaterAdopter: public Modificator | ||||
| { | ||||
| public: | ||||
| 	MODIFICATOR(WaterAdopter); | ||||
| 	 | ||||
| 	void process() override; | ||||
| 	void init() override; | ||||
| 	char dump(const int3 &) override; | ||||
| 	 | ||||
| 	 | ||||
| 	void setWaterZone(TRmgTemplateZoneId water); | ||||
| 	 | ||||
| 	rmg::Area getCoastTiles() const; | ||||
| 	 | ||||
| protected: | ||||
| 	void createWater(EWaterContent::EWaterContent waterContent); | ||||
| 	 | ||||
| protected: | ||||
| 	rmg::Area noWaterArea, waterArea; | ||||
| 	TRmgTemplateZoneId waterZoneId; | ||||
| 	std::map<int3, int> distanceMap; | ||||
| 	std::map<int, rmg::Tileset> reverseDistanceMap; | ||||
| }; | ||||
							
								
								
									
										359
									
								
								lib/rmg/WaterProxy.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								lib/rmg/WaterProxy.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,359 @@ | ||||
| /* | ||||
|  * WaterProxy.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "WaterProxy.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "RmgMap.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
| #include "../mapObjects/CObjectClassesHandler.h" | ||||
| #include "RmgPath.h" | ||||
| #include "RmgObject.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "Functions.h" | ||||
| #include "RoadPlacer.h" | ||||
| #include "TreasurePlacer.h" | ||||
| #include "TownPlacer.h" | ||||
| #include "ConnectionsPlacer.h" | ||||
| #include "TileInfo.h" | ||||
| #include "WaterAdopter.h" | ||||
| #include "RmgArea.h" | ||||
|  | ||||
| void WaterProxy::process() | ||||
| { | ||||
| 	for(auto & t : zone.area().getTilesVector()) | ||||
| 	{ | ||||
| 		map.setZoneID(t, zone.getId()); | ||||
| 		map.setOccupied(t, ETileType::POSSIBLE); | ||||
| 	} | ||||
| 	 | ||||
| 	paintZoneTerrain(zone, generator.rand, map, zone.getTerrainType()); | ||||
| 	 | ||||
| 	//check terrain type | ||||
| 	for(auto & t : zone.area().getTilesVector()) | ||||
| 	{ | ||||
| 		assert(map.isOnMap(t)); | ||||
| 		assert(map.map().getTile(t).terType == zone.getTerrainType()); | ||||
| 	} | ||||
| 	 | ||||
| 	for(auto z : map.getZones()) | ||||
| 	{ | ||||
| 		if(z.second->getId() == zone.getId()) | ||||
| 			continue; | ||||
| 		 | ||||
| 		for(auto & t : z.second->area().getTilesVector()) | ||||
| 		{ | ||||
| 			if(map.map().getTile(t).terType == zone.getTerrainType()) | ||||
| 			{ | ||||
| 				z.second->areaPossible().erase(t); | ||||
| 				z.second->area().erase(t); | ||||
| 				zone.area().add(t); | ||||
| 				zone.areaPossible().add(t); | ||||
| 				map.setZoneID(t, zone.getId()); | ||||
| 				map.setOccupied(t, ETileType::POSSIBLE); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	if(!zone.area().contains(zone.getPos())) | ||||
| 	{ | ||||
| 		zone.setPos(zone.area().getTilesVector().front()); | ||||
| 	} | ||||
| 	 | ||||
| 	zone.initFreeTiles(); | ||||
| 	 | ||||
| 	collectLakes(); | ||||
| } | ||||
|  | ||||
| void WaterProxy::init() | ||||
| { | ||||
| 	for(auto & z : map.getZones()) | ||||
| 	{ | ||||
| 		dependency(z.second->getModificator<TownPlacer>()); | ||||
| 		dependency(z.second->getModificator<WaterAdopter>()); | ||||
| 		postfunction(z.second->getModificator<ConnectionsPlacer>()); | ||||
| 		postfunction(z.second->getModificator<ObjectManager>()); | ||||
| 	} | ||||
| 	POSTFUNCTION(TreasurePlacer); | ||||
| } | ||||
|  | ||||
| const std::vector<WaterProxy::Lake> & WaterProxy::getLakes() const | ||||
| { | ||||
| 	return lakes; | ||||
| } | ||||
|  | ||||
| void WaterProxy::collectLakes() | ||||
| { | ||||
| 	int lakeId = 0; | ||||
| 	for(auto lake : connectedAreas(zone.getArea())) | ||||
| 	{ | ||||
| 		lakes.push_back(Lake{}); | ||||
| 		lakes.back().area = lake; | ||||
| 		lakes.back().distanceMap = lake.computeDistanceMap(lakes.back().reverseDistanceMap); | ||||
| 		for(auto & t : lake.getBorderOutside()) | ||||
| 			if(map.isOnMap(t)) | ||||
| 				lakes.back().neighbourZones[map.getZoneID(t)].add(t); | ||||
| 		for(auto & t : lake.getTiles()) | ||||
| 			lakeMap[t] = lakeId; | ||||
| 		 | ||||
| 		//each lake must have at least one free tile | ||||
| 		if(!lake.overlap(zone.freePaths())) | ||||
| 			zone.freePaths().add(*lakes.back().reverseDistanceMap[lakes.back().reverseDistanceMap.size() - 1].begin()); | ||||
| 		 | ||||
| 		++lakeId; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| RouteInfo WaterProxy::waterRoute(Zone & dst) | ||||
| { | ||||
| 	RouteInfo result; | ||||
| 	 | ||||
| 	auto * adopter = dst.getModificator<WaterAdopter>(); | ||||
| 	if(!adopter) | ||||
| 		return result; | ||||
| 	 | ||||
| 	if(adopter->getCoastTiles().empty()) | ||||
| 		return result; | ||||
| 	 | ||||
| 	//block zones are not connected by template | ||||
| 	for(auto& lake : lakes) | ||||
| 	{ | ||||
| 		if(lake.neighbourZones.count(dst.getId())) | ||||
| 		{ | ||||
| 			if(!lake.keepConnections.count(dst.getId())) | ||||
| 			{ | ||||
| 				for(auto & ct : lake.neighbourZones[dst.getId()].getTiles()) | ||||
| 				{ | ||||
| 					if(map.isPossible(ct)) | ||||
| 						map.setOccupied(ct, ETileType::BLOCKED); | ||||
| 				} | ||||
| 				dst.areaPossible().subtract(lake.neighbourZones[dst.getId()]); | ||||
| 				continue; | ||||
| 			} | ||||
| 						 | ||||
| 			int zoneTowns = 0; | ||||
| 			if(auto * m = dst.getModificator<TownPlacer>()) | ||||
| 				zoneTowns = m->getTotalTowns(); | ||||
| 			 | ||||
| 			if(dst.getType() == ETemplateZoneType::PLAYER_START || dst.getType() == ETemplateZoneType::CPU_START || zoneTowns) | ||||
| 			{ | ||||
| 				if(placeShipyard(dst, lake, generator.getConfig().shipyardGuard, result)) | ||||
| 				{ | ||||
| 					logGlobal->info("Shipyard successfully placed at zone %d", dst.getId()); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					logGlobal->warn("Shipyard placement failed, trying boat at zone %d", dst.getId()); | ||||
| 					if(placeBoat(dst, lake, result)) | ||||
| 					{ | ||||
| 						logGlobal->warn("Boat successfully placed at zone %d", dst.getId()); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						logGlobal->error("Boat placement failed at zone %d", dst.getId()); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				if(placeBoat(dst, lake, result)) | ||||
| 				{ | ||||
| 					logGlobal->info("Boat successfully placed at zone %d", dst.getId()); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					logGlobal->error("Boat placement failed at zone %d", dst.getId()); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| bool WaterProxy::waterKeepConnection(TRmgTemplateZoneId zoneA, TRmgTemplateZoneId zoneB) | ||||
| { | ||||
| 	for(auto & lake : lakes) | ||||
| 	{ | ||||
| 		if(lake.neighbourZones.count(zoneA) && lake.neighbourZones.count(zoneB)) | ||||
| 		{ | ||||
| 			lake.keepConnections.insert(zoneA); | ||||
| 			lake.keepConnections.insert(zoneB); | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info) | ||||
| { | ||||
| 	auto * manager = zone.getModificator<ObjectManager>(); | ||||
| 	if(!manager) | ||||
| 		return false; | ||||
| 	 | ||||
| 	auto subObjects = VLC->objtypeh->knownSubObjects(Obj::BOAT); | ||||
| 	auto* boat = (CGBoat*)VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(subObjects, generator.rand))->create(ObjectTemplate()); | ||||
| 	 | ||||
| 	rmg::Object rmgObject(*boat); | ||||
| 	rmgObject.setTemplate(zone.getTerrainType()); | ||||
| 	 | ||||
| 	auto waterAvailable = zone.areaPossible() + zone.freePaths(); | ||||
| 	rmg::Area coast = lake.neighbourZones.at(land.getId()); //having land tiles | ||||
| 	coast.intersect(land.areaPossible() + land.freePaths()); //having only available land tiles | ||||
| 	auto boardingPositions = coast.getSubarea([&waterAvailable](const int3 & tile) //tiles where boarding is possible | ||||
| 	{ | ||||
| 		rmg::Area a({tile}); | ||||
| 		a = a.getBorderOutside(); | ||||
| 		a.intersect(waterAvailable); | ||||
| 		return !a.empty(); | ||||
| 	}); | ||||
| 	 | ||||
| 	while(!boardingPositions.empty()) | ||||
| 	{ | ||||
| 		auto boardingPosition = *boardingPositions.getTiles().begin(); | ||||
| 		rmg::Area shipPositions({boardingPosition}); | ||||
| 		auto boutside = shipPositions.getBorderOutside(); | ||||
| 		shipPositions.assign(boutside); | ||||
| 		shipPositions.intersect(waterAvailable); | ||||
| 		if(shipPositions.empty()) | ||||
| 		{ | ||||
| 			boardingPositions.erase(boardingPosition); | ||||
| 			continue; | ||||
| 		} | ||||
| 		 | ||||
| 		//try to place boat at water, create paths on water and land | ||||
| 		auto path = manager->placeAndConnectObject(shipPositions, rmgObject, 2, false, true, false); | ||||
| 		auto landPath = land.searchPath(boardingPosition, false); | ||||
| 		if(!path.valid() || !landPath.valid()) | ||||
| 		{ | ||||
| 			boardingPositions.erase(boardingPosition); | ||||
| 			continue; | ||||
| 		} | ||||
| 		 | ||||
| 		info.blocked = rmgObject.getArea(); | ||||
| 		info.visitable = rmgObject.getVisitablePosition(); | ||||
| 		info.boarding = boardingPosition; | ||||
| 		info.water = shipPositions; | ||||
| 		 | ||||
| 		zone.connectPath(path); | ||||
| 		land.connectPath(landPath); | ||||
| 		manager->placeObject(rmgObject, false, true); | ||||
| 		break; | ||||
| 	} | ||||
| 	 | ||||
| 	return !boardingPositions.empty(); | ||||
| } | ||||
|  | ||||
| bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, RouteInfo & info) | ||||
| { | ||||
| 	auto * manager = land.getModificator<ObjectManager>(); | ||||
| 	if(!manager) | ||||
| 		return false; | ||||
| 	 | ||||
| 	int subtype = chooseRandomAppearance(generator.rand, Obj::SHIPYARD, land.getTerrainType()); | ||||
| 	auto shipyard = (CGShipyard*) VLC->objtypeh->getHandlerFor(Obj::SHIPYARD, subtype)->create(ObjectTemplate()); | ||||
| 	shipyard->tempOwner = PlayerColor::NEUTRAL; | ||||
| 	 | ||||
| 	rmg::Object rmgObject(*shipyard); | ||||
| 	rmgObject.setTemplate(land.getTerrainType()); | ||||
| 	bool guarded = manager->addGuard(rmgObject, guard); | ||||
| 	 | ||||
| 	auto waterAvailable = zone.areaPossible() + zone.freePaths(); | ||||
| 	rmg::Area coast = lake.neighbourZones.at(land.getId()); //having land tiles | ||||
| 	coast.intersect(land.areaPossible() + land.freePaths()); //having only available land tiles | ||||
| 	auto boardingPositions = coast.getSubarea([&waterAvailable](const int3 & tile) //tiles where boarding is possible | ||||
| 	{ | ||||
| 		rmg::Area a({tile}); | ||||
| 		a = a.getBorderOutside(); | ||||
| 		a.intersect(waterAvailable); | ||||
| 		return !a.empty(); | ||||
| 	}); | ||||
| 	 | ||||
| 	while(!boardingPositions.empty()) | ||||
| 	{ | ||||
| 		auto boardingPosition = *boardingPositions.getTiles().begin(); | ||||
| 		rmg::Area shipPositions({boardingPosition}); | ||||
| 		auto boutside = shipPositions.getBorderOutside(); | ||||
| 		shipPositions.assign(boutside); | ||||
| 		shipPositions.intersect(waterAvailable); | ||||
| 		if(shipPositions.empty()) | ||||
| 		{ | ||||
| 			boardingPositions.erase(boardingPosition); | ||||
| 			continue; | ||||
| 		} | ||||
| 		 | ||||
| 		//try to place shipyard close to boarding position and appropriate water access | ||||
| 		auto path = manager->placeAndConnectObject(land.areaPossible(), rmgObject, [&rmgObject, &shipPositions, &boardingPosition](const int3 & tile) | ||||
| 		{ | ||||
| 			rmg::Area shipyardOut(rmgObject.getArea().getBorderOutside()); | ||||
| 			if(!shipyardOut.contains(boardingPosition) || (shipyardOut * shipPositions).empty()) | ||||
| 				return -1.f; | ||||
| 			 | ||||
| 			return 1.0f; | ||||
| 		}, guarded, true, false); | ||||
| 		 | ||||
| 		//search path to boarding position | ||||
| 		auto searchArea = land.areaPossible() - rmgObject.getArea(); | ||||
| 		rmg::Path pathToBoarding(searchArea); | ||||
| 		pathToBoarding.connect(land.freePaths()); | ||||
| 		pathToBoarding.connect(path); | ||||
| 		pathToBoarding = pathToBoarding.search(boardingPosition, false); | ||||
| 		 | ||||
| 		//make sure shipyard places ship at position we defined | ||||
| 		rmg::Area shipyardOutToBlock(rmgObject.getArea().getBorderOutside()); | ||||
| 		shipyardOutToBlock.intersect(waterAvailable); | ||||
| 		shipyardOutToBlock.subtract(shipPositions); | ||||
| 		shipPositions.subtract(shipyardOutToBlock); | ||||
| 		auto pathToBoat = zone.searchPath(shipPositions, true); | ||||
| 		 | ||||
| 		if(!path.valid() || !pathToBoarding.valid() || !pathToBoat.valid()) | ||||
| 		{ | ||||
| 			boardingPositions.erase(boardingPosition); | ||||
| 			continue; | ||||
| 		} | ||||
| 		 | ||||
| 		land.connectPath(path); | ||||
| 		land.connectPath(pathToBoarding); | ||||
| 		zone.connectPath(pathToBoat); | ||||
| 		 | ||||
| 		info.blocked = rmgObject.getArea(); | ||||
| 		info.visitable = rmgObject.getVisitablePosition(); | ||||
| 		info.boarding = boardingPosition; | ||||
| 		info.water = shipPositions; | ||||
| 		 | ||||
| 		manager->placeObject(rmgObject, guarded, true); | ||||
| 		 | ||||
| 		zone.areaPossible().subtract(shipyardOutToBlock); | ||||
| 		for(auto & i : shipyardOutToBlock.getTilesVector()) | ||||
| 			if(map.isOnMap(i) && map.isPossible(i)) | ||||
| 				map.setOccupied(i, ETileType::BLOCKED); | ||||
| 		 | ||||
| 		break; | ||||
| 	} | ||||
| 	 | ||||
| 	return !boardingPositions.empty(); | ||||
| } | ||||
|  | ||||
| char WaterProxy::dump(const int3 & t) | ||||
| { | ||||
| 	auto lakeIter = lakeMap.find(t); | ||||
| 	if(lakeIter == lakeMap.end()) | ||||
| 		return '?'; | ||||
| 	 | ||||
| 	Lake & lake = lakes[lakeMap.at(t)]; | ||||
| 	for(auto i : lake.neighbourZones) | ||||
| 	{ | ||||
| 		if(i.second.contains(t)) | ||||
| 			return lake.keepConnections.count(i.first) ? std::to_string(i.first)[0] : '='; | ||||
| 	} | ||||
| 	 | ||||
| 	return '~'; | ||||
| } | ||||
							
								
								
									
										55
									
								
								lib/rmg/WaterProxy.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								lib/rmg/WaterProxy.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| /* | ||||
|  * WaterProxy.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 "Zone.h" | ||||
|  | ||||
| struct RouteInfo | ||||
| { | ||||
| 	rmg::Area blocked; | ||||
| 	int3 visitable; | ||||
| 	int3 boarding; | ||||
| 	rmg::Area water; | ||||
| }; | ||||
|  | ||||
| class WaterProxy: public Modificator | ||||
| { | ||||
| public: | ||||
| 	MODIFICATOR(WaterProxy); | ||||
| 	 | ||||
| 	//subclass to store disconnected parts of water zone | ||||
| 	struct Lake | ||||
| 	{ | ||||
| 		rmg::Area area; //water tiles | ||||
| 		std::map<int3, int> distanceMap; //distance map for lake | ||||
| 		std::map<int, rmg::Tileset> reverseDistanceMap; | ||||
| 		std::map<TRmgTemplateZoneId, rmg::Area> neighbourZones; //zones boardered. Area - part of land | ||||
| 		std::set<TRmgTemplateZoneId> keepConnections; | ||||
| 	}; | ||||
| 		 | ||||
| 	bool waterKeepConnection(TRmgTemplateZoneId zoneA, TRmgTemplateZoneId zoneB); | ||||
| 	RouteInfo waterRoute(Zone & dst); | ||||
| 	 | ||||
| 	void process() override; | ||||
| 	void init() override; | ||||
| 	char dump(const int3 &) override; | ||||
| 	const std::vector<Lake> & getLakes() const; | ||||
| 	 | ||||
| protected: | ||||
| 	void collectLakes(); | ||||
| 	 | ||||
| 	bool placeShipyard(Zone & land, const Lake & lake, si32 guard, RouteInfo & info); | ||||
| 	bool placeBoat(Zone & land, const Lake & lake, RouteInfo & info); | ||||
| 		 | ||||
| protected:	 | ||||
| 	std::vector<Lake> lakes; //disconnected parts of zone. Used to work with water zones | ||||
| 	std::map<int3, int> lakeMap; //map tile on lakeId which is position of lake in lakes array +1 | ||||
| }; | ||||
|  | ||||
							
								
								
									
										116
									
								
								lib/rmg/WaterRoutes.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								lib/rmg/WaterRoutes.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| /* | ||||
|  * WaterRoutes.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "WaterRoutes.h" | ||||
| #include "WaterProxy.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "RmgMap.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../mapping/CMapEditManager.h" | ||||
| #include "../mapObjects/CObjectClassesHandler.h" | ||||
| #include "RmgPath.h" | ||||
| #include "RmgObject.h" | ||||
| #include "ObjectManager.h" | ||||
| #include "Functions.h" | ||||
| #include "RoadPlacer.h" | ||||
| #include "TreasurePlacer.h" | ||||
| #include "TownPlacer.h" | ||||
| #include "ConnectionsPlacer.h" | ||||
| #include "TileInfo.h" | ||||
| #include "WaterAdopter.h" | ||||
| #include "RmgArea.h" | ||||
|  | ||||
| void WaterRoutes::process() | ||||
| { | ||||
| 	auto * wproxy = zone.getModificator<WaterProxy>(); | ||||
| 	if(!wproxy) | ||||
| 		return; | ||||
| 	 | ||||
| 	for(auto & z : map.getZones()) | ||||
| 	{ | ||||
| 		if(z.first != zone.getId()) | ||||
| 			result.push_back(wproxy->waterRoute(*z.second)); | ||||
| 	} | ||||
| 	 | ||||
| 	//prohibit to place objects on sealed off lakes | ||||
| 	for(auto & lake : wproxy->getLakes()) | ||||
| 	{ | ||||
| 		if((lake.area * zone.freePaths()).getTilesVector().size() == 1) | ||||
| 		{ | ||||
| 			zone.freePaths().subtract(lake.area); | ||||
| 			zone.areaPossible().subtract(lake.area); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//prohibit to place objects on the borders | ||||
| 	for(auto & t : zone.area().getBorder()) | ||||
| 	{ | ||||
| 		if(zone.areaPossible().contains(t)) | ||||
| 		{ | ||||
| 			std::vector<int3> landTiles; | ||||
| 			map.foreachDirectNeighbour(t, [this, &landTiles, &t](const int3 & c) | ||||
| 			{ | ||||
| 				if(map.isOnMap(c) && map.getZoneID(c) != zone.getId()) | ||||
| 				{ | ||||
| 					landTiles.push_back(c - t); | ||||
| 				} | ||||
| 			}); | ||||
| 			 | ||||
| 			if(landTiles.size() == 2) | ||||
| 			{ | ||||
| 				int3 o = landTiles[0] + landTiles[1]; | ||||
| 				if(o.x * o.x * o.y * o.y == 1)  | ||||
| 				{ | ||||
| 					zone.areaPossible().erase(t); | ||||
| 					zone.areaUsed().add(t); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void WaterRoutes::init() | ||||
| { | ||||
| 	for(auto & z : map.getZones()) | ||||
| 	{ | ||||
| 		dependency(z.second->getModificator<ConnectionsPlacer>()); | ||||
| 		postfunction(z.second->getModificator<ObjectManager>()); | ||||
| 		postfunction(z.second->getModificator<TreasurePlacer>()); | ||||
| 	} | ||||
| 	dependency(zone.getModificator<WaterProxy>()); | ||||
| 	postfunction(zone.getModificator<TreasurePlacer>()); | ||||
| } | ||||
|  | ||||
| char WaterRoutes::dump(const int3 & t) | ||||
| { | ||||
| 	for(auto & i : result) | ||||
| 	{ | ||||
| 		if(t == i.boarding) | ||||
| 			return 'B'; | ||||
| 		if(t == i.visitable) | ||||
| 			return '@'; | ||||
| 		if(i.blocked.contains(t)) | ||||
| 			return '#'; | ||||
| 		if(i.water.contains(t)) | ||||
| 		{ | ||||
| 			if(zone.freePaths().contains(t)) | ||||
| 				return '+'; | ||||
| 			else | ||||
| 				return '-'; | ||||
| 		} | ||||
| 	} | ||||
| 	if(zone.freePaths().contains(t)) | ||||
| 		return '.'; | ||||
| 	if(zone.area().contains(t)) | ||||
| 		return '~'; | ||||
| 	return ' '; | ||||
| } | ||||
|  | ||||
							
								
								
									
										27
									
								
								lib/rmg/WaterRoutes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								lib/rmg/WaterRoutes.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| /* | ||||
|  * WaterRoutes.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 "Zone.h" | ||||
|  | ||||
| struct RouteInfo; | ||||
|  | ||||
| class WaterRoutes: public Modificator | ||||
| { | ||||
| public: | ||||
| 	MODIFICATOR(WaterRoutes); | ||||
| 	 | ||||
| 	void process() override; | ||||
| 	void init() override; | ||||
| 	char dump(const int3 &) override; | ||||
| 	 | ||||
| private: | ||||
| 	std::vector<RouteInfo> result; | ||||
| }; | ||||
							
								
								
									
										410
									
								
								lib/rmg/Zone.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										410
									
								
								lib/rmg/Zone.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,410 @@ | ||||
| /* | ||||
|  * Zone.cpp, 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "Zone.h" | ||||
| #include "RmgMap.h" | ||||
| #include "Functions.h" | ||||
| #include "TileInfo.h" | ||||
| #include "../mapping/CMap.h" | ||||
| #include "../CStopWatch.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "RmgPath.h" | ||||
|  | ||||
| std::function<bool(const int3 &)> AREA_NO_FILTER = [](const int3 & t) | ||||
| { | ||||
| 	return true; | ||||
| }; | ||||
|  | ||||
| Zone::Zone(RmgMap & map, CMapGenerator & generator) | ||||
| 					: ZoneOptions(), | ||||
| 					townType(ETownType::NEUTRAL), | ||||
| 					terrainType(Terrain("grass")), | ||||
| 					map(map), | ||||
| 					generator(generator) | ||||
| { | ||||
| 	 | ||||
| } | ||||
|  | ||||
| bool Zone::isUnderground() const | ||||
| { | ||||
| 	return getPos().z; | ||||
| } | ||||
|  | ||||
| void Zone::setOptions(const ZoneOptions& options) | ||||
| { | ||||
| 	ZoneOptions::operator=(options); | ||||
| } | ||||
|  | ||||
| float3 Zone::getCenter() const | ||||
| { | ||||
| 	return center; | ||||
| } | ||||
|  | ||||
| void Zone::setCenter(const float3 &f) | ||||
| { | ||||
| 	//limit boundaries to (0,1) square | ||||
| 	 | ||||
| 	//alternate solution - wrap zone around unitary square. If it doesn't fit on one side, will come out on the opposite side | ||||
| 	center = f; | ||||
| 	 | ||||
| 	center.x = static_cast<float>(std::fmod(center.x, 1)); | ||||
| 	center.y = static_cast<float>(std::fmod(center.y, 1)); | ||||
| 	 | ||||
| 	if(center.x < 0) //fmod seems to work only for positive numbers? we want to stay positive | ||||
| 		center.x = 1 - std::abs(center.x); | ||||
| 	if(center.y < 0) | ||||
| 		center.y = 1 - std::abs(center.y); | ||||
| } | ||||
|  | ||||
| int3 Zone::getPos() const | ||||
| { | ||||
| 	return pos; | ||||
| } | ||||
|  | ||||
| void Zone::setPos(const int3 &Pos) | ||||
| { | ||||
| 	pos = Pos; | ||||
| } | ||||
|  | ||||
| const rmg::Area & Zone::getArea() const | ||||
| { | ||||
| 	return dArea; | ||||
| } | ||||
|  | ||||
| rmg::Area & Zone::area() | ||||
| { | ||||
| 	return dArea; | ||||
| } | ||||
|  | ||||
| rmg::Area & Zone::areaPossible() | ||||
| { | ||||
| 	return dAreaPossible; | ||||
| } | ||||
|  | ||||
| rmg::Area & Zone::areaUsed() | ||||
| { | ||||
| 	return dAreaUsed; | ||||
| } | ||||
|  | ||||
| void Zone::clearTiles() | ||||
| { | ||||
| 	dArea.clear(); | ||||
| 	dAreaPossible.clear(); | ||||
| 	dAreaFree.clear(); | ||||
| } | ||||
|  | ||||
| void Zone::initFreeTiles() | ||||
| { | ||||
| 	rmg::Tileset possibleTiles; | ||||
| 	vstd::copy_if(dArea.getTiles(), vstd::set_inserter(possibleTiles), [this](const int3 &tile) -> bool | ||||
| 	{ | ||||
| 		return map.isPossible(tile); | ||||
| 	}); | ||||
| 	dAreaPossible.assign(possibleTiles); | ||||
| 	 | ||||
| 	if(dAreaFree.empty()) | ||||
| 	{ | ||||
| 		dAreaPossible.erase(pos); | ||||
| 		dAreaFree.add(pos); //zone must have at least one free tile where other paths go - for instance in the center | ||||
| 	} | ||||
| } | ||||
|  | ||||
| rmg::Area & Zone::freePaths() | ||||
| { | ||||
| 	return dAreaFree; | ||||
| } | ||||
|  | ||||
| si32 Zone::getTownType() const | ||||
| { | ||||
| 	return townType; | ||||
| } | ||||
|  | ||||
| void Zone::setTownType(si32 town) | ||||
| { | ||||
| 	townType = town; | ||||
| } | ||||
|  | ||||
| const Terrain & Zone::getTerrainType() const | ||||
| { | ||||
| 	return terrainType; | ||||
| } | ||||
|  | ||||
| void Zone::setTerrainType(const Terrain & terrain) | ||||
| { | ||||
| 	terrainType = terrain; | ||||
| } | ||||
|  | ||||
| rmg::Path Zone::searchPath(const rmg::Area & src, bool onlyStraight, std::function<bool(const int3 &)> areafilter) const | ||||
| ///connect current tile to any other free tile within zone | ||||
| { | ||||
| 	auto movementCost = [this](const int3 & s, const int3 & d) | ||||
| 	{ | ||||
| 		if(map.isFree(d)) | ||||
| 			return 1; | ||||
| 		else if (map.isPossible(d)) | ||||
| 			return 2; | ||||
| 		return 3; | ||||
| 	}; | ||||
| 	 | ||||
| 	auto area = (dAreaPossible + dAreaFree).getSubarea(areafilter); | ||||
| 	rmg::Path freePath(area); | ||||
| 	freePath.connect(dAreaFree); | ||||
| 	 | ||||
| 	//connect to all pieces | ||||
| 	auto goals = connectedAreas(src); | ||||
| 	for(auto & goal : goals) | ||||
| 	{ | ||||
| 		auto path = freePath.search(goal, onlyStraight, movementCost); | ||||
| 		if(path.getPathArea().empty()) | ||||
| 			return rmg::Path::invalid(); | ||||
| 		 | ||||
| 		freePath.connect(path.getPathArea()); | ||||
| 	} | ||||
| 	 | ||||
| 	return freePath; | ||||
| } | ||||
|  | ||||
| rmg::Path Zone::searchPath(const int3 & src, bool onlyStraight, std::function<bool(const int3 &)> areafilter) const | ||||
| ///connect current tile to any other free tile within zone | ||||
| { | ||||
| 	return searchPath(rmg::Area({src}), onlyStraight, areafilter); | ||||
| } | ||||
|  | ||||
| void Zone::connectPath(const rmg::Path & path) | ||||
| ///connect current tile to any other free tile within zone | ||||
| { | ||||
| 	dAreaPossible.subtract(path.getPathArea()); | ||||
| 	dAreaFree.unite(path.getPathArea()); | ||||
| 	for(auto & t : path.getPathArea().getTilesVector()) | ||||
| 		map.setOccupied(t, ETileType::FREE); | ||||
| } | ||||
|  | ||||
| void Zone::fractalize() | ||||
| { | ||||
| 	rmg::Area clearedTiles(dAreaFree); | ||||
| 	rmg::Area possibleTiles(dAreaPossible); | ||||
| 	rmg::Area tilesToIgnore; //will be erased in this iteration | ||||
| 	 | ||||
| 	//the more treasure density, the greater distance between paths. Scaling is experimental. | ||||
| 	int totalDensity = 0; | ||||
| 	for(auto ti : treasureInfo) | ||||
| 		totalDensity += ti.density; | ||||
| 	const float minDistance = 10 * 10; //squared | ||||
| 	 | ||||
| 	if(type != ETemplateZoneType::JUNCTION) | ||||
| 	{ | ||||
| 		//junction is not fractalized, has only one straight path | ||||
| 		//everything else remains blocked | ||||
| 		while(!possibleTiles.empty()) | ||||
| 		{ | ||||
| 			//link tiles in random order | ||||
| 			std::vector<int3> tilesToMakePath = possibleTiles.getTilesVector(); | ||||
| 			RandomGeneratorUtil::randomShuffle(tilesToMakePath, generator.rand); | ||||
| 			 | ||||
| 			int3 nodeFound(-1, -1, -1); | ||||
| 			 | ||||
| 			for(auto tileToMakePath : tilesToMakePath) | ||||
| 			{ | ||||
| 				//find closest free tile | ||||
| 				int3 closestTile = clearedTiles.nearest(tileToMakePath); | ||||
| 				if(closestTile.dist2dSQ(tileToMakePath) <= minDistance) | ||||
| 					tilesToIgnore.add(tileToMakePath); | ||||
| 				else | ||||
| 				{ | ||||
| 					//if tiles are not close enough, make path to it | ||||
| 					nodeFound = tileToMakePath; | ||||
| 					clearedTiles.add(nodeFound); //from now on nearby tiles will be considered handled | ||||
| 					break; //next iteration - use already cleared tiles | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			possibleTiles.subtract(tilesToIgnore); | ||||
| 			if(!nodeFound.valid()) //nothing else can be done (?) | ||||
| 				break; | ||||
| 			tilesToIgnore.clear(); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//cut straight paths towards the center. A* is too slow for that. | ||||
| 	auto areas = connectedAreas(clearedTiles); | ||||
| 	for(auto & area : areas) | ||||
| 	{ | ||||
| 		if(dAreaFree.overlap(area)) | ||||
| 			continue; //already found | ||||
| 			 | ||||
| 		auto availableArea = dAreaPossible + dAreaFree; | ||||
| 		rmg::Path path(availableArea); | ||||
| 		path.connect(dAreaFree); | ||||
| 		auto res = path.search(area, false); | ||||
| 		if(res.getPathArea().empty()) | ||||
| 		{ | ||||
| 			dAreaPossible.subtract(area); | ||||
| 			dAreaFree.subtract(area); | ||||
| 			for(auto & t : area.getTiles()) | ||||
| 				map.setOccupied(t, ETileType::BLOCKED); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			dAreaPossible.subtract(res.getPathArea()); | ||||
| 			dAreaFree.unite(res.getPathArea()); | ||||
| 			for(auto & t : res.getPathArea().getTiles()) | ||||
| 				map.setOccupied(t, ETileType::FREE); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//now block most distant tiles away from passages | ||||
| 	float blockDistance = minDistance * 0.25f; | ||||
| 	auto areaToBlock = dArea.getSubarea([this, blockDistance](const int3 & t) | ||||
| 	{ | ||||
| 		float distance = static_cast<float>(dAreaFree.distanceSqr(t)); | ||||
| 		return distance > blockDistance; | ||||
| 	}); | ||||
| 	dAreaPossible.subtract(areaToBlock); | ||||
| 	dAreaFree.subtract(areaToBlock); | ||||
| 	for(auto & t : areaToBlock.getTiles()) | ||||
| 		map.setOccupied(t, ETileType::BLOCKED); | ||||
| } | ||||
|  | ||||
| void Zone::initModificators() | ||||
| { | ||||
| 	for(auto & modificator : modificators) | ||||
| 	{ | ||||
| 		modificator->init(); | ||||
| 	} | ||||
| 	logGlobal->info("Zone %d modificators initialized", getId()); | ||||
| } | ||||
|  | ||||
| void Zone::processModificators() | ||||
| { | ||||
| 	for(auto & modificator : modificators) | ||||
| 	{ | ||||
| 		try | ||||
| 		{ | ||||
| 			modificator->run(); | ||||
| 		} | ||||
| 		catch (const rmgException & e) | ||||
| 		{ | ||||
| 			logGlobal->info("Zone %d, modificator %s - FAILED: %s", getId(), e.what()); | ||||
| 			throw e; | ||||
| 		} | ||||
| 	} | ||||
| 	logGlobal->info("Zone %d filled successfully", getId()); | ||||
| } | ||||
|  | ||||
| Modificator::Modificator(Zone & zone, RmgMap & map, CMapGenerator & generator) : zone(zone), map(map), generator(generator) | ||||
| { | ||||
| } | ||||
|  | ||||
| void Modificator::setName(const std::string & n) | ||||
| { | ||||
| 	name = n; | ||||
| } | ||||
|  | ||||
| const std::string & Modificator::getName() const | ||||
| { | ||||
| 	return name; | ||||
| } | ||||
|  | ||||
| bool Modificator::isFinished() const | ||||
| { | ||||
| 	return finished; | ||||
| } | ||||
|  | ||||
| void Modificator::run() | ||||
| { | ||||
| 	started = true; | ||||
| 	if(!finished) | ||||
| 	{ | ||||
| 		for(auto * modificator : preceeders) | ||||
| 		{ | ||||
| 			if(!modificator->started) | ||||
| 				modificator->run(); | ||||
| 		} | ||||
| 		logGlobal->info("Modificator zone %d - %s - started", zone.getId(), getName()); | ||||
| 		CStopWatch processTime; | ||||
| 		try | ||||
| 		{ | ||||
| 			process(); | ||||
| 		} | ||||
| 		catch(rmgException &e) | ||||
| 		{ | ||||
| 			logGlobal->error("Modificator %s, exception: %s", getName(), e.what()); | ||||
| 		} | ||||
| #ifdef RMG_DUMP | ||||
| 		dump(); | ||||
| #endif | ||||
| 		finished = true; | ||||
| 		logGlobal->info("Modificator zone %d - %s - done (%d ms)", zone.getId(), getName(), processTime.getDiff()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Modificator::dependency(Modificator * modificator) | ||||
| { | ||||
| 	if(modificator && modificator != this) | ||||
| 	{ | ||||
| 		if(std::find(preceeders.begin(), preceeders.end(), modificator) == preceeders.end()) | ||||
| 			preceeders.push_back(modificator); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Modificator::postfunction(Modificator * modificator) | ||||
| { | ||||
| 	if(modificator && modificator != this) | ||||
| 	{ | ||||
| 		if(std::find(modificator->preceeders.begin(), modificator->preceeders.end(), this) == modificator->preceeders.end()) | ||||
| 			modificator->preceeders.push_back(this); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Modificator::dump() | ||||
| { | ||||
| 	std::ofstream out(boost::to_string(boost::format("seed_%d_modzone_%d_%s.txt") % generator.getRandomSeed() % zone.getId() % getName())); | ||||
| 	auto & mapInstance = map.map(); | ||||
| 	int levels = mapInstance.twoLevel ? 2 : 1; | ||||
| 	int width =  mapInstance.width; | ||||
| 	int height = mapInstance.height; | ||||
| 	for (int k = 0; k < levels; k++) | ||||
| 	{ | ||||
| 		for(int j=0; j<height; j++) | ||||
| 		{ | ||||
| 			for (int i=0; i<width; i++) | ||||
| 			{ | ||||
| 				out << dump(int3(i, j, k)); | ||||
| 			} | ||||
| 			out << std::endl; | ||||
| 		} | ||||
| 		out << std::endl; | ||||
| 	} | ||||
| 	out << std::endl; | ||||
| } | ||||
|  | ||||
| char Modificator::dump(const int3 & t) | ||||
| { | ||||
| 	if(zone.freePaths().contains(t)) | ||||
| 		return '.'; //free path | ||||
| 	if(zone.areaPossible().contains(t)) | ||||
| 		return ' '; //possible | ||||
| 	if(zone.areaUsed().contains(t)) | ||||
| 		return 'U'; //used | ||||
| 	if(zone.area().contains(t)) | ||||
| 	{ | ||||
| 		if(map.shouldBeBlocked(t)) | ||||
| 			return '#'; //obstacle | ||||
| 		else | ||||
| 			return '^'; //visitable points? | ||||
| 	} | ||||
| 	return '?'; | ||||
| } | ||||
|  | ||||
| Modificator::~Modificator() | ||||
| { | ||||
| 	 | ||||
| } | ||||
							
								
								
									
										143
									
								
								lib/rmg/Zone.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								lib/rmg/Zone.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| /* | ||||
|  * Zone.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 "../GameConstants.h" | ||||
| #include "float3.h" | ||||
| #include "../int3.h" | ||||
| #include "CRmgTemplate.h" | ||||
| #include "RmgArea.h" | ||||
| #include "RmgPath.h" | ||||
| #include "RmgObject.h" | ||||
|  | ||||
| //uncomment to generate dumps afger every step of map generation | ||||
| //#define RMG_DUMP | ||||
|  | ||||
| #define MODIFICATOR(x) x(Zone & z, RmgMap & m, CMapGenerator & g): Modificator(z, m, g) {setName(#x);} | ||||
| #define DEPENDENCY(x) 		dependency(zone.getModificator<x>()); | ||||
| #define POSTFUNCTION(x)		postfunction(zone.getModificator<x>()); | ||||
| #define DEPENDENCY_ALL(x) 	for(auto & z : map.getZones()) \ | ||||
| 							{ \ | ||||
| 								dependency(z.second->getModificator<x>()); \ | ||||
| 							} | ||||
| #define POSTFUNCTION_ALL(x) for(auto & z : map.getZones()) \ | ||||
| 							{ \ | ||||
| 								postfunction(z.second->getModificator<x>()); \ | ||||
| 							} | ||||
|  | ||||
| class RmgMap; | ||||
| class CMapGenerator; | ||||
| class Zone; | ||||
|  | ||||
| class Modificator | ||||
| { | ||||
| public: | ||||
| 	Modificator() = delete; | ||||
| 	Modificator(Zone & zone, RmgMap & map, CMapGenerator & generator); | ||||
| 	 | ||||
| 	virtual void process() = 0; | ||||
| 	virtual void init() {/*override to add dependencies*/} | ||||
| 	virtual char dump(const int3 &); | ||||
| 	virtual ~Modificator(); | ||||
| 	 | ||||
| 	void setName(const std::string & n); | ||||
| 	const std::string & getName() const; | ||||
| 	 | ||||
| 	void run(); | ||||
| 	void dependency(Modificator * modificator); | ||||
| 	void postfunction(Modificator * modificator); | ||||
|  | ||||
| protected: | ||||
| 	RmgMap & map; | ||||
| 	CMapGenerator & generator; | ||||
| 	Zone & zone; | ||||
| 	 | ||||
| 	bool isFinished() const; | ||||
| 	 | ||||
| private: | ||||
| 	std::string name; | ||||
| 	bool started = false; | ||||
| 	bool finished = false; | ||||
| 	std::list<Modificator*> preceeders; //must be ordered container | ||||
| 	void dump(); | ||||
| }; | ||||
|  | ||||
| extern std::function<bool(const int3 &)> AREA_NO_FILTER; | ||||
|  | ||||
| class Zone : public rmg::ZoneOptions | ||||
| { | ||||
| public: | ||||
| 	Zone(RmgMap & map, CMapGenerator & generator); | ||||
| 	Zone(const Zone &) = delete; | ||||
| 	 | ||||
| 	void setOptions(const rmg::ZoneOptions & options); | ||||
| 	bool isUnderground() const; | ||||
| 	 | ||||
| 	float3 getCenter() const; | ||||
| 	void setCenter(const float3 &f); | ||||
| 	int3 getPos() const; | ||||
| 	void setPos(const int3 &pos); | ||||
| 	 | ||||
| 	const rmg::Area & getArea() const; | ||||
| 	rmg::Area & area(); | ||||
| 	rmg::Area & areaPossible(); | ||||
| 	rmg::Area & freePaths(); | ||||
| 	rmg::Area & areaUsed(); | ||||
| 	 | ||||
| 	void initFreeTiles(); | ||||
| 	void clearTiles(); | ||||
| 	void fractalize(); | ||||
| 	 | ||||
| 	si32 getTownType() const; | ||||
| 	void setTownType(si32 town); | ||||
| 	const Terrain & getTerrainType() const; | ||||
| 	void setTerrainType(const Terrain & terrain); | ||||
| 		 | ||||
| 	void connectPath(const rmg::Path & path); | ||||
| 	rmg::Path searchPath(const rmg::Area & src, bool onlyStraight, std::function<bool(const int3 &)> areafilter = AREA_NO_FILTER) const; | ||||
| 	rmg::Path searchPath(const int3 & src, bool onlyStraight, std::function<bool(const int3 &)> areafilter = AREA_NO_FILTER) const; | ||||
| 	 | ||||
| 	template<class T> | ||||
| 	T* getModificator() | ||||
| 	{ | ||||
| 		for(auto & m : modificators) | ||||
| 			if(auto * mm = dynamic_cast<T*>(m.get())) | ||||
| 				return mm; | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	 | ||||
| 	template<class T> | ||||
| 	void addModificator() | ||||
| 	{ | ||||
| 		modificators.emplace_back(new T(*this, map, generator)); | ||||
| 	} | ||||
| 	 | ||||
| 	void initModificators(); | ||||
| 	void processModificators(); | ||||
| 	 | ||||
| protected: | ||||
| 	CMapGenerator & generator; | ||||
| 	RmgMap & map; | ||||
| 	std::list<std::unique_ptr<Modificator>> modificators; | ||||
| 	 | ||||
| 	//placement info | ||||
| 	int3 pos; | ||||
| 	float3 center; | ||||
| 	rmg::Area dArea; //irregular area assined to zone | ||||
| 	rmg::Area dAreaPossible; | ||||
| 	rmg::Area dAreaFree; //core paths of free tiles that all other objects will be linked to | ||||
| 	rmg::Area dAreaUsed; | ||||
| 	 | ||||
| 	//template info | ||||
| 	si32 townType; | ||||
| 	Terrain terrainType; | ||||
| 	 | ||||
| }; | ||||
| @@ -45,6 +45,21 @@ ui16 CTypeList::getTypeID(const std::type_info *type, bool throws) const | ||||
| 	return descriptor->typeID; | ||||
| } | ||||
|  | ||||
| CTypeList::TypeInfoPtr CTypeList::getTypeDescriptor(ui16 typeID) const | ||||
| { | ||||
| 	auto found = std::find_if(typeInfos.begin(), typeInfos.end(), [typeID](const std::pair<const std::type_info *, TypeInfoPtr> & p) -> bool | ||||
| 		{ | ||||
| 			return p.second->typeID == typeID; | ||||
| 		}); | ||||
|  | ||||
| 	if(found != typeInfos.end()) | ||||
| 	{ | ||||
| 		return found->second; | ||||
| 	} | ||||
|  | ||||
| 	return TypeInfoPtr(); | ||||
| } | ||||
|  | ||||
| std::vector<CTypeList::TypeInfoPtr> CTypeList::castSequence(TypeInfoPtr from, TypeInfoPtr to) const | ||||
| { | ||||
| 	if(!strcmp(from->name, to->name)) | ||||
|   | ||||
| @@ -148,6 +148,8 @@ public: | ||||
| 		return getTypeID(getTypeInfo(t), throws); | ||||
| 	} | ||||
|  | ||||
| 	TypeInfoPtr getTypeDescriptor(ui16 typeID) const; | ||||
|  | ||||
| 	template<typename TInput> | ||||
| 	void * castToMostDerived(const TInput * inputPtr) const | ||||
| 	{ | ||||
|   | ||||
| @@ -18,8 +18,6 @@ add_library(vcmiERM SHARED ${lib_SRCS} ${lib_HDRS}) | ||||
| target_link_libraries(vcmiERM Boost::boost vcmi) | ||||
|  | ||||
| vcmi_set_output_dir(vcmiERM "scripting") | ||||
|  | ||||
| set_target_properties(vcmiERM PROPERTIES ${PCH_PROPERTIES}) | ||||
| cotire(vcmiERM) | ||||
| enable_pch(vcmiERM) | ||||
|  | ||||
| install(TARGETS vcmiERM DESTINATION ${SCRIPTING_LIB_DIR}) | ||||
|   | ||||
| @@ -43,9 +43,7 @@ add_library(vcmiLua SHARED ${lib_SRCS}) | ||||
| target_link_libraries(vcmiLua Boost::boost luajit::luajit vcmi) | ||||
|  | ||||
| vcmi_set_output_dir(vcmiLua "scripting") | ||||
|  | ||||
| set_target_properties(vcmiLua PROPERTIES ${PCH_PROPERTIES}) | ||||
| cotire(vcmiLua) | ||||
| enable_pch(vcmiLua) | ||||
|  | ||||
| install(TARGETS vcmiLua DESTINATION ${SCRIPTING_LIB_DIR}) | ||||
|  | ||||
|   | ||||
| @@ -43,8 +43,6 @@ if(WIN32) | ||||
| endif() | ||||
|  | ||||
| vcmi_set_output_dir(vcmiserver "") | ||||
|  | ||||
| set_target_properties(vcmiserver PROPERTIES ${PCH_PROPERTIES}) | ||||
| cotire(vcmiserver) | ||||
| enable_pch(vcmiserver) | ||||
|  | ||||
| install(TARGETS vcmiserver DESTINATION ${BIN_DIR}) | ||||
|   | ||||
| @@ -177,8 +177,7 @@ endif() | ||||
|  | ||||
| vcmi_set_output_dir(vcmitest "") | ||||
|  | ||||
| set_target_properties(vcmitest PROPERTIES ${PCH_PROPERTIES}) | ||||
| cotire(vcmitest) | ||||
| enable_pch(vcmitest) | ||||
|  | ||||
| file (GLOB_RECURSE testdata "testdata/*.*") | ||||
| foreach(resource ${testdata}) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user