mirror of
https://github.com/vcmi/vcmi.git
synced 2025-04-15 11:46:56 +02:00
Random map generator refactoring (#762)
random map generator refactoring and improvements
This commit is contained in:
parent
1644a19053
commit
6da7c9ccb5
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})
|
||||
|
Loading…
x
Reference in New Issue
Block a user