1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

- Added basic mock/test generation - Added stub for terrain editing

This commit is contained in:
beegee1 2013-01-06 19:30:12 +00:00
parent d4c30667b8
commit 0311e5e6f5
28 changed files with 2096 additions and 239 deletions

View File

@ -94,6 +94,9 @@ typedef boost::int32_t si32; //signed int 32 bits (4 bytes)
typedef boost::int16_t si16; //signed int 16 bits (2 bytes) typedef boost::int16_t si16; //signed int 16 bits (2 bytes)
typedef boost::int8_t si8; //signed int 8 bits (1 byte) typedef boost::int8_t si8; //signed int 8 bits (1 byte)
// Fixed width bool data type is important for serialization
static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
#if defined _M_X64 && defined _WIN32 //Win64 -> cannot load 32-bit DLLs for video handling #if defined _M_X64 && defined _WIN32 //Win64 -> cannot load 32-bit DLLs for video handling
#define DISABLE_VIDEO #define DISABLE_VIDEO
#endif #endif

View File

@ -165,9 +165,17 @@ void updateStartInfo(std::string filename, StartInfo & sInfo, const CMapHeader *
PlayerSettings &pset = sInfo.playerInfos[i]; PlayerSettings &pset = sInfo.playerInfos[i];
pset.color = i; pset.color = i;
if(pinfo.canHumanPlay && namesIt != playerNames.cend()) if(pinfo.canHumanPlay && namesIt != playerNames.cend())
{
setPlayer(pset, namesIt++->first, playerNames); setPlayer(pset, namesIt++->first, playerNames);
}
else else
{
setPlayer(pset, 0, playerNames); setPlayer(pset, 0, playerNames);
if(!pinfo.canHumanPlay)
{
pset.compOnly = true;
}
}
pset.castle = pinfo.defaultCastle(); pset.castle = pinfo.defaultCastle();
pset.hero = pinfo.defaultHero(); pset.hero = pinfo.defaultHero();
@ -873,11 +881,6 @@ void CSelectionScreen::startScenario()
{ {
saveGameName = sInfo.mapname; saveGameName = sInfo.mapname;
} }
if(sInfo.createRandomMap)
{
// Random map generation fails for now, so don't start game...
return;
}
StartInfo * si = new StartInfo(sInfo); StartInfo * si = new StartInfo(sInfo);
CGP->removeFromGui(); CGP->removeFromGui();
@ -1138,9 +1141,9 @@ void SelectionTab::parseGames(const std::vector<ResourceID> &files, bool multi)
allItems.push_back(mapInfo); allItems.push_back(mapInfo);
} }
catch(std::exception & e) catch(const std::exception & e)
{ {
tlog3 << "Failed to process " << files[i].getName() <<": " << e.what() << std::endl; tlog3 << "Error: Failed to process " << files[i].getName() <<": " << e.what() << std::endl;
} }
} }
} }
@ -1590,8 +1593,8 @@ RandomMapTab::RandomMapTab()
// Map Size // Map Size
mapSizeBtnGroup = new CHighlightableButtonsGroup(0); mapSizeBtnGroup = new CHighlightableButtonsGroup(0);
mapSizeBtnGroup->pos.y = 81; mapSizeBtnGroup->pos.y += 81;
mapSizeBtnGroup->pos.x = 158; mapSizeBtnGroup->pos.x += 158;
const std::vector<std::string> mapSizeBtns = boost::assign::list_of("RANSIZS")("RANSIZM")("RANSIZL")("RANSIZX"); const std::vector<std::string> mapSizeBtns = boost::assign::list_of("RANSIZS")("RANSIZM")("RANSIZL")("RANSIZX");
addButtonsToGroup(mapSizeBtnGroup, mapSizeBtns, 0, 3, 47, 198); addButtonsToGroup(mapSizeBtnGroup, mapSizeBtns, 0, 3, 47, 198);
mapSizeBtnGroup->select(1, false); mapSizeBtnGroup->select(1, false);
@ -1621,8 +1624,8 @@ RandomMapTab::RandomMapTab()
const int BTNS_GROUP_LEFT_MARGIN = 67; const int BTNS_GROUP_LEFT_MARGIN = 67;
// Amount of players // Amount of players
playersCntGroup = new CHighlightableButtonsGroup(0); playersCntGroup = new CHighlightableButtonsGroup(0);
playersCntGroup->pos.y = 153; playersCntGroup->pos.y += 153;
playersCntGroup->pos.x = BTNS_GROUP_LEFT_MARGIN; playersCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
addButtonsWithRandToGroup(playersCntGroup, numberDefs, 1, 8, NUMBERS_WIDTH, 204, 212); addButtonsWithRandToGroup(playersCntGroup, numberDefs, 1, 8, NUMBERS_WIDTH, 204, 212);
playersCntGroup->onChange = [&](int btnId) playersCntGroup->onChange = [&](int btnId)
{ {
@ -1635,8 +1638,8 @@ RandomMapTab::RandomMapTab()
// Amount of teams // Amount of teams
teamsCntGroup = new CHighlightableButtonsGroup(0); teamsCntGroup = new CHighlightableButtonsGroup(0);
teamsCntGroup->pos.y = 219; teamsCntGroup->pos.y += 219;
teamsCntGroup->pos.x = BTNS_GROUP_LEFT_MARGIN; teamsCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
addButtonsWithRandToGroup(teamsCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 214, 222); addButtonsWithRandToGroup(teamsCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 214, 222);
teamsCntGroup->onChange = [&](int btnId) teamsCntGroup->onChange = [&](int btnId)
{ {
@ -1646,8 +1649,8 @@ RandomMapTab::RandomMapTab()
// Computer only players // Computer only players
compOnlyPlayersCntGroup = new CHighlightableButtonsGroup(0); compOnlyPlayersCntGroup = new CHighlightableButtonsGroup(0);
compOnlyPlayersCntGroup->pos.y = 285; compOnlyPlayersCntGroup->pos.y += 285;
compOnlyPlayersCntGroup->pos.x = BTNS_GROUP_LEFT_MARGIN; compOnlyPlayersCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
addButtonsWithRandToGroup(compOnlyPlayersCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 224, 232); addButtonsWithRandToGroup(compOnlyPlayersCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 224, 232);
compOnlyPlayersCntGroup->select(0, true); compOnlyPlayersCntGroup->select(0, true);
compOnlyPlayersCntGroup->onChange = [&](int btnId) compOnlyPlayersCntGroup->onChange = [&](int btnId)
@ -1660,8 +1663,8 @@ RandomMapTab::RandomMapTab()
// Computer only teams // Computer only teams
compOnlyTeamsCntGroup = new CHighlightableButtonsGroup(0); compOnlyTeamsCntGroup = new CHighlightableButtonsGroup(0);
compOnlyTeamsCntGroup->pos.y = 351; compOnlyTeamsCntGroup->pos.y += 351;
compOnlyTeamsCntGroup->pos.x = BTNS_GROUP_LEFT_MARGIN; compOnlyTeamsCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
addButtonsWithRandToGroup(compOnlyTeamsCntGroup, numberDefs, 0, 6, NUMBERS_WIDTH, 234, 241); addButtonsWithRandToGroup(compOnlyTeamsCntGroup, numberDefs, 0, 6, NUMBERS_WIDTH, 234, 241);
deactivateButtonsFrom(compOnlyTeamsCntGroup, 0); deactivateButtonsFrom(compOnlyTeamsCntGroup, 0);
compOnlyTeamsCntGroup->onChange = [&](int btnId) compOnlyTeamsCntGroup->onChange = [&](int btnId)
@ -1673,8 +1676,8 @@ RandomMapTab::RandomMapTab()
const int WIDE_BTN_WIDTH = 85; const int WIDE_BTN_WIDTH = 85;
// Water content // Water content
waterContentGroup = new CHighlightableButtonsGroup(0); waterContentGroup = new CHighlightableButtonsGroup(0);
waterContentGroup->pos.y = 419; waterContentGroup->pos.y += 419;
waterContentGroup->pos.x = BTNS_GROUP_LEFT_MARGIN; waterContentGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
const std::vector<std::string> waterContentBtns = boost::assign::list_of("RANNONE")("RANNORM")("RANISLD"); const std::vector<std::string> waterContentBtns = boost::assign::list_of("RANNONE")("RANNORM")("RANISLD");
addButtonsWithRandToGroup(waterContentGroup, waterContentBtns, 0, 2, WIDE_BTN_WIDTH, 243, 246); addButtonsWithRandToGroup(waterContentGroup, waterContentBtns, 0, 2, WIDE_BTN_WIDTH, 243, 246);
waterContentGroup->onChange = [&](int btnId) waterContentGroup->onChange = [&](int btnId)
@ -1684,8 +1687,8 @@ RandomMapTab::RandomMapTab()
// Monster strength // Monster strength
monsterStrengthGroup = new CHighlightableButtonsGroup(0); monsterStrengthGroup = new CHighlightableButtonsGroup(0);
monsterStrengthGroup->pos.y = 485; monsterStrengthGroup->pos.y += 485;
monsterStrengthGroup->pos.x = BTNS_GROUP_LEFT_MARGIN; monsterStrengthGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
const std::vector<std::string> monsterStrengthBtns = boost::assign::list_of("RANWEAK")("RANNORM")("RANSTRG"); const std::vector<std::string> monsterStrengthBtns = boost::assign::list_of("RANWEAK")("RANNORM")("RANSTRG");
addButtonsWithRandToGroup(monsterStrengthGroup, monsterStrengthBtns, 0, 2, WIDE_BTN_WIDTH, 248, 251); addButtonsWithRandToGroup(monsterStrengthGroup, monsterStrengthBtns, 0, 2, WIDE_BTN_WIDTH, 248, 251);
monsterStrengthGroup->onChange = [&](int btnId) monsterStrengthGroup->onChange = [&](int btnId)

View File

@ -387,9 +387,9 @@ void CGuiHandler::run()
mainFPSmng->framerateDelay(); // holds a constant FPS mainFPSmng->framerateDelay(); // holds a constant FPS
} }
} }
catch(const std::exception & ex) catch(const std::exception & e)
{ {
tlog1 << "Error: " << ex.what() << std::endl; tlog1 << "Error: " << e.what() << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }

View File

@ -0,0 +1,428 @@
// Defines terrain view patterns.
// The following properties are mandatory:
// data: the 3x3 pattern
// mapping: maps the pattern to a range of terrain view images/frames of the .DEF, e.g. 10-15
// for patterns which represent two transitions a comma can be used to distinct between dirt and sand
// e.g. 10-15, 25-35 whereas the first value is always dirt and the second sand
// The following properties are optional:
// flipMode: should the same be flipped or different images be used(see rock) or is flip not supported at all; allowed values: sameImage | diffImages; default is: sameImage
// id: the identifier for the pattern if it's referenced by other patterns
// minPoints: the minimum points to reach to validate the pattern successfully
// The following table shows the rules for the 3x3 pattern of all terrain types:
// I) normal(e.g. grass, lava, ...):
// N: Native type
// D: Dirt border
// S: Sand border
// T: Sand OR dirt border(all Ts in the pattern are replaced by dirt OR sand)
// ?: T or N
// II) dirt:
// N: Native type
// D: Dirt border
// S: Sand border
// ?: Any border
// III) sand:
// N: Native type
// S: Sand border
// IV) water:
// N: Native type
// S: Sand border
// ?: Any border
// V) rock:
// N: Native type
// S: Sand border
// ?: Any border
// Some additional info:
// Rules can be combined with comma. e.g. T, N which would be the same meaning of ?. It's most useful in combination with pattern chaining.
// Chaining of patterns is supported. To reference a another pattern you simply add the <Ref Id> to the relevant field of the pattern.
// Rules can be given points: <[Rule OR Ref Id]-Points> With the property minPoints simple conditions can be built.
{
"normal" :
[
// Standard transitions
{
"data" :
[
"?", "?", "T",
"?", "N", "N",
"T", "N", "N"
],
"mapping" : "0-3, 20-23"
},
{
"data" :
[
"?", "N", "N",
"T", "N", "N",
"?", "N", "N"
],
"mapping" : "4-7, 24-27"
},
{
"data" :
[
"?", "T", "?",
"N", "N", "N",
"N", "N", "N"
],
"mapping" : "8-11, 28-31"
},
{
"data" :
[
"N", "N", "N",
"N", "N", "N",
"N", "N", "T"
],
"mapping" : "12-15, 32-35"
},
{
"data" :
[
"T", "T", "a-1,?",
"T", "N", "N",
"a-1,?", "N", "N"
],
"mapping" : "16-17, 36-37",
"id" : "a",
"minPoints" : 1
},
{
"data" :
[
"N", "N", "N",
"N", "N", "a-1,N",
"N", "a-1,N", "T"
],
"mapping" : "18-19, 38-39",
"minPoints" : 1
},
// Mixed transitions
{
"data" :
[
"T", "N", "N",
"N", "N", "N",
"N", "N", "T"
],
"mapping" : "40, 42"
},
{
"data" :
[
"D", "N", "N",
"N", "N", "N",
"N", "N", "S"
],
"mapping" : "41"
},
{
"data" :
[
"N", "N", "D,N",
"N", "N", "D",
"S", "D", "D,N"
],
"mapping" : "43"
},
{
"data" :
[
"N", "N", "S",
"N", "N", "D",
"D,N", "D", "D,N"
],
"mapping" : "44"
},
{
"data" :
[
"N", "N", "D,N",
"N", "N", "D",
"N", "N", "S"
],
"mapping" : "45"
},
{
"data" :
[
"N", "N", "N",
"N", "N", "N",
"D,N", "D", "S"
],
"mapping" : "46"
},
{
"data" :
[
"N", "N", "D,S,N",
"N", "N", "S",
"D", "D", "D,S,N"
],
"mapping" : "47"
},
{
"data" :
[
"N", "N", "D",
"N", "N", "D",
"D,S,N", "S", "D,S,N"
],
"mapping" : "48"
},
// No transition
{
"data" :
[
"N", "N", "N",
"N", "N", "N",
"N", "N", "N"
],
"mapping" : "49-72"
}
],
"dirt" :
[
// Standard transitions
{
"data" :
[
"?", "S", "S",
"S", "N", "N",
"S", "N", "N"
],
"mapping" : "0-3"
},
{
"data" :
[
"?", "D", "D",
"S", "N", "N",
"?", "D", "D"
],
"mapping" : "4-7"
},
{
"data" :
[
"?", "S", "?",
"D", "N", "D",
"D", "N", "D"
],
"mapping" : "8-11"
},
{
"data" :
[
"D", "D", "D",
"D", "N", "N",
"D", "N", "S"
],
"mapping" : "12-15"
},
{
"data" :
[
"S", "S", "D",
"S", "N", "b-1,D",
"D", "b-1,D", "D"
],
"mapping" : "16-17",
"id" : "a",
"minPoints" : 1
},
{
"data" :
[
"D", "D", "D",
"D", "N", "a-1,D",
"D", "a-1,D", "S"
],
"mapping" : "18-19",
"id" : "b",
"minPoints" : 1
},
// Mixed transition
{
"data" :
[
"S", "D", "D",
"D", "N", "D",
"D", "D", "S"
],
"mapping" : "20"
},
// No transition
{
"data" :
[
"D", "D", "D",
"D", "N", "D",
"D", "D", "D"
],
"mapping" : "21-44"
}
],
"sand" :
[
{
"data" :
[
"S", "S", "S",
"S", "N", "S",
"S", "S", "S"
],
"mapping" : "0-23"
}
],
"water" :
[
// Standard transitions
{
"data" :
[
"S", "S", "S",
"S", "N", "N",
"S", "N", "N"
],
"mapping" : "0-3"
},
{
"data" :
[
"?", "N", "N",
"S", "N", "N",
"?", "N", "N"
],
"mapping" : "4-7"
},
{
"data" :
[
"?", "S", "?",
"N", "N", "N",
"N", "N", "N"
],
"mapping" : "8-11"
},
{
"data" :
[
"N", "N", "N",
"N", "N", "N",
"N", "N", "S"
],
"mapping" : "12-15"
},
{
"data" :
[
"S", "S", "N",
"S", "N", "N",
"N", "N", "N"
],
"mapping" : "16-17",
"id" : "a"
},
{
"data" :
[
"N", "N", "N",
"N", "N", "a-1,N",
"N", "a-1,N", "S"
],
"mapping" : "18-19",
"minPoints" : 1
},
// No transition
{
"data" :
[
"N", "N", "N",
"N", "N", "N",
"N", "N", "N"
],
"mapping" : "20-32"
}
],
"rock" :
[
// No transition
{
"data" :
[
"N", "N", "N",
"N", "N", "N",
"N", "N", "N"
],
"mapping" : "0-7"
},
// Standard transitions
{
"data" :
[
"?", "S", "?",
"S", "N", "N",
"?", "N", "N"
],
"mapping" : "8-15",
"flipMode" : "diffImages"
},
{
"data" :
[
"?", "N", "N",
"S", "N", "N",
"?", "N", "N"
],
"mapping" : "16-19",
"flipMode" : "diffImages"
},
{
"data" :
[
"?", "S", "?",
"N", "N", "N",
"N", "N", "N"
],
"mapping" : "20-23",
"flipMode" : "diffImages"
},
{
"data" :
[
"N", "N", "N",
"N", "N", "N",
"N", "N", "S"
],
"mapping" : "24-31",
"flipMode" : "diffImages"
},
{
"data" :
[
"S", "S", "N",
"S", "N", "N",
"N", "N", "N"
],
"mapping" : "32-39",
"flipMode" : "diffImages",
"id" : "a"
},
{
"data" :
[
"N", "N", "N",
"N", "N", "a-1,N",
"N", "a-1,N", "S"
],
"mapping" : "40-47",
"flipMode" : "diffImages",
"minPoints" : 1
}
]
}

View File

@ -9,6 +9,7 @@
#include "CSpellHandler.h" #include "CSpellHandler.h"
#include "CObjectHandler.h" #include "CObjectHandler.h"
#include "NetPacks.h" #include "NetPacks.h"
#include "GameConstants.h"
using namespace boost::assign; using namespace boost::assign;
@ -776,14 +777,23 @@ void CArtHandler::initAllowedArtifactsList(const std::vector<ui8> &allowed)
allowedArtifacts.push_back(artifacts[i]); allowedArtifacts.push_back(artifacts[i]);
else //check if active modules allow artifact to be every used else //check if active modules allow artifact to be every used
{ {
if (artifacts[i]->possibleSlots[ArtBearer::COMMANDER].size() && VLC->modh->modules.COMMANDERS || if ((artifacts[i]->possibleSlots[ArtBearer::COMMANDER].size() && VLC->modh->modules.COMMANDERS) ||
artifacts[i]->possibleSlots[ArtBearer::CREATURE].size() && VLC->modh->modules.STACK_ARTIFACT) (artifacts[i]->possibleSlots[ArtBearer::CREATURE].size() && VLC->modh->modules.STACK_ARTIFACT))
allowedArtifacts.push_back(artifacts[i]); allowedArtifacts.push_back(artifacts[i]);
//keep im mind that artifact can be worn by more than one type of bearer //keep im mind that artifact can be worn by more than one type of bearer
} }
} }
} }
std::vector<ui8> CArtHandler::getDefaultAllowedArtifacts() const
{
std::vector<ui8> allowedArtifacts;
allowedArtifacts.resize(127, 1);
allowedArtifacts.resize(141, 0);
allowedArtifacts.resize(GameConstants::ARTIFACTS_QUANTITY, 1);
return allowedArtifacts;
}
CArtifactInstance::CArtifactInstance() CArtifactInstance::CArtifactInstance()
{ {
init(); init();

View File

@ -256,6 +256,13 @@ public:
CArtHandler(); CArtHandler();
~CArtHandler(); ~CArtHandler();
/**
* Gets a list of default allowed artifacts.
*
* @return a list of allowed artifacts, the index is the artifact id and the value either 0 for not allowed or 1 for allowed
*/
std::vector<ui8> getDefaultAllowedArtifacts() const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & artifacts & allowedArtifacts & treasures & minors & majors & relics h & artifacts & allowedArtifacts & treasures & minors & majors & relics

View File

@ -26,6 +26,7 @@
#include "Filesystem/CResourceLoader.h" #include "Filesystem/CResourceLoader.h"
#include "GameConstants.h" #include "GameConstants.h"
#include "RMG/CMapGenOptions.h" #include "RMG/CMapGenOptions.h"
#include "RMG/CMapGenerator.h"
DLL_LINKAGE boost::rand48 ran; DLL_LINKAGE boost::rand48 ran;
class CGObjectInstance; class CGObjectInstance;
@ -872,7 +873,46 @@ void CGameState::init(StartInfo * si)
if(scenarioOps->createRandomMap) if(scenarioOps->createRandomMap)
{ {
tlog0 << "Create random map." << std::endl; tlog0 << "Create random map." << std::endl;
//TODO random map
// Create player settings for RMG
std::map<TPlayerColor, CMapGenerator::CPlayerSettings> players;
BOOST_FOREACH(auto pInfo, scenarioOps->playerInfos)
{
const PlayerSettings & startSettings = pInfo.second;
CMapGenerator::CPlayerSettings player;
player.setColor(startSettings.color);
player.setStartingTown(startSettings.castle);
if(startSettings.playerID > 0)
{
player.setPlayerType(CMapGenerator::CPlayerSettings::HUMAN);
}
else if(startSettings.compOnly)
{
player.setPlayerType(CMapGenerator::CPlayerSettings::COMP_ONLY);
}
players[player.getColor()] = player;
}
// Gen map
CMapGenerator mapGen(*scenarioOps->mapGenOptions, players, scenarioOps->seedToBeUsed);
map = mapGen.generate().release();
// Update starting options
for(auto it = scenarioOps->playerInfos.begin(); it != scenarioOps->playerInfos.end();)
{
PlayerSettings & pSettings = it->second;
if(!(map->players[pSettings.color].canHumanPlay || map->players[pSettings.color].canComputerPlay))
{
scenarioOps->playerInfos.erase(it++);
}
else
{
pSettings.compOnly = !(map->players[pSettings.color].canHumanPlay);
pSettings.team = map->players[pSettings.color].team;
pSettings.castle = map->players[pSettings.color].defaultCastle();
++it;
}
}
} }
else else
{ {
@ -902,7 +942,6 @@ void CGameState::init(StartInfo * si)
VLC->arth->initAllowedArtifactsList(map->allowedArtifact); VLC->arth->initAllowedArtifactsList(map->allowedArtifact);
tlog0 << "Map loaded!" << std::endl; tlog0 << "Map loaded!" << std::endl;
//tlog0 <<"Reading and detecting map file (together): "<<tmh.getDif()<<std::endl; //tlog0 <<"Reading and detecting map file (together): "<<tmh.getDif()<<std::endl;
tlog0 << "\tOur checksum for the map: "<< map->checksum << std::endl; tlog0 << "\tOur checksum for the map: "<< map->checksum << std::endl;
if(scenarioOps->mapfileChecksum) if(scenarioOps->mapfileChecksum)

View File

@ -498,3 +498,10 @@ std::vector<ui8> CHeroHandler::getDefaultAllowedHeroes() const
return allowedHeroes; return allowedHeroes;
} }
std::vector<ui8> CHeroHandler::getDefaultAllowedAbilities() const
{
std::vector<ui8> allowedAbilities;
allowedAbilities.resize(GameConstants::SKILL_QUANTITY, 1);
return allowedAbilities;
}

View File

@ -218,10 +218,17 @@ public:
* create a JSON config file or merge it with a existing config file which describes which heroes can be used for * create a JSON config file or merge it with a existing config file which describes which heroes can be used for
* random map generation / map editor(default map settings). (Gelu, ... should be excluded) * random map generation / map editor(default map settings). (Gelu, ... should be excluded)
* *
* @return a list of allowed heroes, the index is the hero id and the value either 0 for not allowed and 1 for allowed * @return a list of allowed heroes, the index is the hero id and the value either 0 for not allowed or 1 for allowed
*/ */
std::vector<ui8> getDefaultAllowedHeroes() const; std::vector<ui8> getDefaultAllowedHeroes() const;
/**
* Gets a list of default allowed abilities. OH3 abilities/skills are all allowed by default.
*
* @return a list of allowed abilities, the index is the ability id and the value either 0 for not allowed or 1 for allowed
*/
std::vector<ui8> getDefaultAllowedAbilities() const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & classes & heroes & expPerLevel & ballistics & terrCosts; h & classes & heroes & expPerLevel & ballistics & terrCosts;

View File

@ -1,79 +1,82 @@
project(libvcmi) project(libvcmi)
cmake_minimum_required(VERSION 2.6) cmake_minimum_required(VERSION 2.6)
include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/lib) include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/lib)
include_directories(${Boost_INCLUDE_DIRS} ${SDL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) include_directories(${Boost_INCLUDE_DIRS} ${SDL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
set(lib_SRCS set(lib_SRCS
Filesystem/CBinaryReader.cpp Filesystem/CBinaryReader.cpp
Filesystem/CFilesystemLoader.cpp Filesystem/CFilesystemLoader.cpp
Filesystem/CMemoryStream.cpp Filesystem/CMemoryStream.cpp
Filesystem/CFileInfo.cpp Filesystem/CFileInfo.cpp
Filesystem/CLodArchiveLoader.cpp Filesystem/CLodArchiveLoader.cpp
Filesystem/CResourceLoader.cpp Filesystem/CResourceLoader.cpp
Filesystem/CFileInputStream.cpp Filesystem/CFileInputStream.cpp
Filesystem/CCompressedStream.cpp Filesystem/CCompressedStream.cpp
Mapping/CCampaignHandler.cpp Mapping/CCampaignHandler.cpp
Mapping/CMap.cpp Mapping/CMap.cpp
Mapping/CMapInfo.cpp Mapping/CMapEditManager.cpp
Mapping/CMapService.cpp Mapping/CMapInfo.cpp
RMG/CMapGenOptions.cpp Mapping/CMapService.cpp
BattleAction.cpp RMG/CMapGenOptions.cpp
BattleHex.cpp RMG/CMapGenerator.cpp
BattleState.cpp BattleAction.cpp
CArtHandler.cpp BattleHex.cpp
CBattleCallback.cpp BattleState.cpp
CBuildingHandler.cpp CArtHandler.cpp
CConfigHandler.cpp CBattleCallback.cpp
CConsoleHandler.cpp CBuildingHandler.cpp
CCreatureHandler.cpp CConfigHandler.cpp
CCreatureSet.cpp CConsoleHandler.cpp
CDefObjInfoHandler.cpp CCreatureHandler.cpp
CGameInterface.cpp CCreatureSet.cpp
CGameState.cpp CDefObjInfoHandler.cpp
CGeneralTextHandler.cpp CGameInterface.cpp
CHeroHandler.cpp CGameState.cpp
CLogger.cpp CGeneralTextHandler.cpp
CModHandler.cpp CHeroHandler.cpp
CObjectHandler.cpp CLogger.cpp
CObstacleInstance.cpp CModHandler.cpp
Connection.cpp CObjectHandler.cpp
CSpellHandler.cpp CObstacleInstance.cpp
CThreadHelper.cpp Connection.cpp
CTownHandler.cpp CSpellHandler.cpp
HeroBonus.cpp CThreadHelper.cpp
IGameCallback.cpp CTownHandler.cpp
JsonNode.cpp HeroBonus.cpp
NetPacksLib.cpp IGameCallback.cpp
ResourceSet.cpp JsonNode.cpp
VCMI_Lib.cpp NetPacksLib.cpp
) ResourceSet.cpp
VCMI_Lib.cpp
set(lib_HEADERS )
Filesystem/CInputStream.h
Filesystem/ISimpleResourceLoader.h set(lib_HEADERS
AI_Base.h Filesystem/CInputStream.h
CondSh.h Filesystem/ISimpleResourceLoader.h
ConstTransitivePtr.h AI_Base.h
CScriptingModule.h CondSh.h
CStopWatch.h ConstTransitivePtr.h
GameConstants.h CRandomGenerator.h
StringConstants.h CScriptingModule.h
IGameEventsReceiver.h CStopWatch.h
int3.h GameConstants.h
Interprocess.h StringConstants.h
NetPacks.h IGameEventsReceiver.h
RegisterTypes.h int3.h
StartInfo.h Interprocess.h
UnlockGuard.h NetPacks.h
VCMIDirs.h RegisterTypes.h
vcmi_endian.h StartInfo.h
) UnlockGuard.h
VCMIDirs.h
add_library(vcmi SHARED ${lib_SRCS} ${lib_HEADERS}) vcmi_endian.h
set_target_properties(vcmi PROPERTIES XCODE_ATTRIBUTE_LD_DYLIB_INSTALL_NAME "@executable_path/libvcmi.dylib") )
target_link_libraries(vcmi ${Boost_LIBRARIES} ${SDL_LIBRARY} ${ZLIB_LIBRARIES})
add_library(vcmi SHARED ${lib_SRCS} ${lib_HEADERS})
if (NOT APPLE) # Already inside vcmiclient bundle set_target_properties(vcmi PROPERTIES XCODE_ATTRIBUTE_LD_DYLIB_INSTALL_NAME "@executable_path/libvcmi.dylib")
install(TARGETS vcmi DESTINATION ${LIB_DIR}) target_link_libraries(vcmi ${Boost_LIBRARIES} ${SDL_LIBRARY} ${ZLIB_LIBRARIES})
endif()
if (NOT APPLE) # Already inside vcmiclient bundle
install(TARGETS vcmi DESTINATION ${LIB_DIR})
endif()

View File

@ -1971,11 +1971,9 @@ bool CGTownInstance::hasCapitol() const
return hasBuilt(EBuilding::CAPITOL); return hasBuilt(EBuilding::CAPITOL);
} }
CGTownInstance::CGTownInstance() CGTownInstance::CGTownInstance()
:IShipyard(this), IMarket(this) :IShipyard(this), IMarket(this), town(nullptr), builded(0), destroyed(0), identifier(0), alignment(0xff)
{ {
builded=-1;
destroyed=-1;
town=NULL;
} }
CGTownInstance::~CGTownInstance() CGTownInstance::~CGTownInstance()

122
lib/CRandomGenerator.h Normal file
View File

@ -0,0 +1,122 @@
/*
* CRandomGenerator.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 <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/uniform_real_distribution.hpp>
#include <boost/random/variate_generator.hpp>
typedef boost::mt19937 TGenerator;
typedef boost::random::uniform_int_distribution<int> TIntDist;
typedef boost::random::uniform_real_distribution<double> TRealDist;
typedef boost::variate_generator<TGenerator &, TIntDist> TRandI;
typedef boost::variate_generator<TGenerator &, TRealDist> TRand;
/**
* The random generator randomly generates integers and real numbers("doubles") between
* a given range. This is a header only class and mainly a wrapper for
* convenient usage of the boost random API.
*/
class CRandomGenerator
{
public:
/**
* Constructor. Seeds the generator with the current time by default.
*/
CRandomGenerator()
{
gen.seed(std::time(nullptr));
}
/**
* Seeds the generator with the given value.
*
* @param value the random seed
*/
void seed(int value)
{
gen.seed(value);
}
/**
* Gets a generator which generates integers in the given range.
*
* Example how to use:
* @code
* TRandI rand = getRangeI(0, 10);
* int a = rand(); // with the operator() the next value can be obtained
* int b = rand(); // you can generate more values
* @endcode
*
* @param lower the lower boundary
* @param upper the upper boundary
* @return the generator which can be used to generate integer numbers
*/
TRandI getRangeI(int lower, int upper)
{
TIntDist range(lower, upper);
return TRandI(gen, range);
}
/**
* Gets a integer in the given range. In comparison to getRangeI it's
* a convenient method if you want to generate only one value in a given
* range.
*
* @param lower the lower boundary
* @param upper the upper boundary
* @return the generated integer
*/
int getInteger(int lower, int upper)
{
return getRangeI(lower, upper)();
}
/**
* Gets a generator which generates doubles in the given range.
*
* Example how to use:
* @code
* TRand rand = getRange(23.56, 32.10);
* double a = rand(); // with the operator() the next value can be obtained
* double b = rand(); // you can generate more values
* @endcode
*
* @param lower the lower boundary
* @param upper the upper boundary
* @return the generated double
*/
TRand getRange(double lower, double upper)
{
TRealDist range(lower, upper);
return TRand(gen, range);
}
/**
* Gets a double in the given range. In comparison to getRange it's
* a convenient method if you want to generate only one value in a given
* range.
*
* @param lower the lower boundary
* @param upper the upper boundary
* @return the generated double
*/
double getDouble(double lower, double upper)
{
return getRange(lower, upper)();
}
private:
/** The actual boost random generator. */
TGenerator gen;
};

View File

@ -378,3 +378,10 @@ void CSpellHandler::loadSpells()
risingSpells += 38, 39, 40; risingSpells += 38, 39, 40;
mindSpells += 50, 59, 60, 61, 62; mindSpells += 50, 59, 60, 61, 62;
} }
std::vector<ui8> CSpellHandler::getDefaultAllowedSpells() const
{
std::vector<ui8> allowedSpells;
allowedSpells.resize(GameConstants::SPELLS_QUANTITY, 1);
return allowedSpells;
}

View File

@ -101,6 +101,13 @@ public:
std::set<TSpell> mindSpells; std::set<TSpell> mindSpells;
void loadSpells(); void loadSpells();
/**
* Gets a list of default allowed spells. OH3 spells are all allowed by default.
*
* @return a list of allowed spells, the index is the spell id and the value either 0 for not allowed or 1 for allowed
*/
std::vector<ui8> getDefaultAllowedSpells() const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & spells & damageSpells & risingSpells & mindSpells; h & spells & damageSpells & risingSpells & mindSpells;

View File

@ -6,6 +6,7 @@
#include "../CTownHandler.h" #include "../CTownHandler.h"
#include "../CHeroHandler.h" #include "../CHeroHandler.h"
#include "../CDefObjInfoHandler.h" #include "../CDefObjInfoHandler.h"
#include "../CSpellHandler.h"
SHeroName::SHeroName() : heroId(-1) SHeroName::SHeroName() : heroId(-1)
{ {
@ -13,8 +14,8 @@ SHeroName::SHeroName() : heroId(-1)
} }
PlayerInfo::PlayerInfo(): canHumanPlay(false), canComputerPlay(false), PlayerInfo::PlayerInfo(): canHumanPlay(false), canComputerPlay(false),
aiTactic(EAiTactic::RANDOM), isFactionRandom(false), mainHeroPortrait(-1), hasMainTown(true), aiTactic(EAiTactic::RANDOM), isFactionRandom(false), mainHeroPortrait(-1), hasMainTown(false),
generateHeroAtMainTown(true), team(255), generateHero(false), p7(0), hasHero(false), customHeroID(-1), powerPlaceholders(-1) generateHeroAtMainTown(false), team(255), generateHero(false), p7(0), hasHero(false), customHeroID(-1), powerPlaceholders(-1)
{ {
allowedFactions = VLC->townh->getDefaultAllowedFactions(); allowedFactions = VLC->townh->getDefaultAllowedFactions();
} }
@ -129,6 +130,7 @@ CMapHeader::CMapHeader() : version(EMapFormat::SOD), height(72), width(72),
twoLevel(true), difficulty(1), levelLimit(0), howManyTeams(0), areAnyPlayers(false) twoLevel(true), difficulty(1), levelLimit(0), howManyTeams(0), areAnyPlayers(false)
{ {
allowedHeroes = VLC->heroh->getDefaultAllowedHeroes(); allowedHeroes = VLC->heroh->getDefaultAllowedHeroes();
players.resize(GameConstants::PLAYER_LIMIT);
} }
CMapHeader::~CMapHeader() CMapHeader::~CMapHeader()
@ -138,7 +140,9 @@ CMapHeader::~CMapHeader()
CMap::CMap() : checksum(0), terrain(nullptr), grailRadious(0) CMap::CMap() : checksum(0), terrain(nullptr), grailRadious(0)
{ {
allowedAbilities = VLC->heroh->getDefaultAllowedAbilities();
allowedArtifact = VLC->arth->getDefaultAllowedArtifacts();
allowedSpell = VLC->spellh->getDefaultAllowedSpells();
} }
CMap::~CMap() CMap::~CMap()
@ -186,6 +190,7 @@ void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
} }
} }
} }
void CMap::addBlockVisTiles(CGObjectInstance * obj) void CMap::addBlockVisTiles(CGObjectInstance * obj)
{ {
for(int fx=0; fx<8; ++fx) for(int fx=0; fx<8; ++fx)
@ -286,3 +291,23 @@ void CMap::eraseArtifactInstance(CArtifactInstance * art)
assert(artInstances[art->id] == art); assert(artInstances[art->id] == art);
artInstances[art->id].dellNull(); artInstances[art->id].dellNull();
} }
void CMap::addQuest(CGObjectInstance * quest)
{
auto q = dynamic_cast<IQuestObject *>(quest);
q->quest->qid = quests.size();
quests.push_back(q->quest);
}
void CMap::initTerrain()
{
terrain = new TerrainTile**[width];
for(int i = 0; i < width; ++i)
{
terrain[i] = new TerrainTile*[height];
for(int j = 0; j < height; ++j)
{
terrain[i][j] = new TerrainTile[twoLevel ? 2 : 1];
}
}
}

View File

@ -116,10 +116,10 @@ struct DLL_LINKAGE PlayerInfo
/** The list of renamed heroes. */ /** The list of renamed heroes. */
std::vector<SHeroName> heroesNames; std::vector<SHeroName> heroesNames;
/** True if the player has a main town. The default value is true. */ /** True if the player has a main town. The default value is false. */
bool hasMainTown; bool hasMainTown;
/** True if the main hero should be generated at the main town. The default value is true. */ /** True if the main hero should be generated at the main town. The default value is false. */
bool generateHeroAtMainTown; bool generateHeroAtMainTown;
/** The position of the main town. */ /** The position of the main town. */
@ -286,11 +286,9 @@ struct DLL_LINKAGE DisposedHero
} }
}; };
/// Class which manages map events.
/** /**
* The map event is an event which gives or takes resources for a specific * The map event is an event which e.g. gives or takes resources of a specific
* amount of players and can appear regularly or once a time. * amount to/from players and can appear regularly or once a time.
*/ */
class DLL_LINKAGE CMapEvent class DLL_LINKAGE CMapEvent
{ {
@ -584,7 +582,7 @@ public:
/** Specifies the victory condition. The default value is defeat all enemies. */ /** Specifies the victory condition. The default value is defeat all enemies. */
VictoryCondition victoryCondition; VictoryCondition victoryCondition;
/** A list containing information about players. */ /** A list containing information about players. The default size of the vector is GameConstants::PLAYER_LIMIT. */
std::vector<PlayerInfo> players; std::vector<PlayerInfo> players;
/** The number of teams. */ /** The number of teams. */
@ -714,10 +712,22 @@ public:
*/ */
void addNewArtifactInstance(CArtifactInstance * art); void addNewArtifactInstance(CArtifactInstance * art);
/**
* Adds the specified quest instance to the list of quests.
*
* @param quest the quest object which should be added to the list of quests
*/
void addQuest(CGObjectInstance * quest);
/**
* Initializes the terrain of the map by allocating memory.
*/
void initTerrain();
/** the checksum of the map */ /** the checksum of the map */
ui32 checksum; ui32 checksum;
/** a 3-dimensional array of terrain tiles, access is as follows: x, y, level */ /** a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground */
TerrainTile*** terrain; TerrainTile*** terrain;
/** list of rumors */ /** list of rumors */

View File

@ -0,0 +1,386 @@
#include "StdInc.h"
#include "CMapEditManager.h"
#include "../JsonNode.h"
#include "../Filesystem/CResourceLoader.h"
#include "../CDefObjInfoHandler.h"
const std::string TerrainViewPattern::FLIP_MODE_SAME_IMAGE = "sameImage";
const std::string TerrainViewPattern::FLIP_MODE_DIFF_IMAGES = "diffImages";
const std::string TerrainViewPattern::RULE_DIRT = "D";
const std::string TerrainViewPattern::RULE_SAND = "S";
const std::string TerrainViewPattern::RULE_TRANSITION = "T";
const std::string TerrainViewPattern::RULE_NATIVE = "N";
const std::string TerrainViewPattern::RULE_ANY = "?";
TerrainViewPattern::TerrainViewPattern() : minPoints(0), flipMode(FLIP_MODE_SAME_IMAGE),
terGroup(ETerrainGroup::NORMAL)
{
}
CTerrainViewPatternConfig::CTerrainViewPatternConfig()
{
const JsonNode config(ResourceID("config/terrainViewPatterns.json"));
const std::map<std::string, ETerrainGroup::ETerrainGroup> terGroups
= boost::assign::map_list_of("normal", ETerrainGroup::NORMAL)("dirt", ETerrainGroup::DIRT)
("sand", ETerrainGroup::SAND)("water", ETerrainGroup::WATER)("rock", ETerrainGroup::ROCK);
BOOST_FOREACH(auto terMapping, terGroups)
{
BOOST_FOREACH(const JsonNode & ptrnNode, config[terMapping.first].Vector())
{
TerrainViewPattern pattern;
// Read pattern data
const JsonVector & data = ptrnNode["data"].Vector();
if(data.size() != 9)
{
throw std::runtime_error("Size of pattern's data vector has to be 9.");
}
for(int i = 0; i < data.size(); ++i)
{
std::string cell = data[i].String();
boost::algorithm::erase_all(cell, " ");
std::vector<std::string> rules;
boost::split(rules, cell, boost::is_any_of(","));
BOOST_FOREACH(std::string ruleStr, rules)
{
std::vector<std::string> rule;
boost::split(rule, ruleStr, boost::is_any_of("-"));
std::pair<std::string, int> pair;
pair.first = rule[0];
if(rule.size() > 1)
{
pair.second = boost::lexical_cast<int>(rule[1]);
}
else
{
pair.second = 0;
}
pattern.data[i].push_back(pair);
}
}
// Read mapping
std::string mappingStr = ptrnNode["mapping"].String();
boost::algorithm::erase_all(mappingStr, " ");
std::vector<std::string> mappings;
boost::split(mappings, mappingStr, boost::is_any_of(","));
BOOST_FOREACH(std::string mapping, mappings)
{
std::vector<std::string> range;
boost::split(range, mapping, boost::is_any_of("-"));
pattern.mapping.push_back(std::make_pair(boost::lexical_cast<int>(range[0]),
boost::lexical_cast<int>(range.size() > 1 ? range[1] : range[0])));
}
// Read optional attributes
pattern.id = ptrnNode["id"].String();
pattern.minPoints = static_cast<int>(ptrnNode["minPoints"].Float());
pattern.flipMode = ptrnNode["flipMode"].String();
if(pattern.flipMode.empty())
{
pattern.flipMode = TerrainViewPattern::FLIP_MODE_SAME_IMAGE;
}
pattern.terGroup = terMapping.second;
patterns[terMapping.second].push_back(pattern);
}
}
}
const std::vector<TerrainViewPattern> & CTerrainViewPatternConfig::getPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const
{
return patterns.find(terGroup)->second;
}
CMapEditManager::CMapEditManager(const CTerrainViewPatternConfig * terViewPatternConfig, CMap * map, int randomSeed /*= std::time(nullptr)*/)
: map(map), terViewPatternConfig(terViewPatternConfig)
{
gen.seed(randomSeed);
}
void CMapEditManager::clearTerrain()
{
for(int i = 0; i < map->width; ++i)
{
for(int j = 0; j < map->height; ++j)
{
map->terrain[i][j][0].terType = ETerrainType::WATER;
map->terrain[i][j][0].terView = gen.getInteger(20, 32);
if(map->twoLevel)
{
map->terrain[i][j][1].terType = ETerrainType::ROCK;
map->terrain[i][j][1].terView = 0;
}
}
}
}
void CMapEditManager::drawTerrain(ETerrainType::ETerrainType terType, int posx, int posy, int width, int height, bool underground)
{
bool mapLevel = underground ? 1 : 0;
for(int i = posx; i < posx + width; ++i)
{
for(int j = posy; j < posy + height; ++j)
{
map->terrain[i][j][mapLevel].terType = terType;
}
}
//TODO there are situations where more tiles are affected implicitely
//TODO add coastal bit to extTileFlags appropriately
//updateTerrainViews(posx - 1, posy - 1, width + 2, height + 2, mapLevel);
}
void CMapEditManager::updateTerrainViews(int posx, int posy, int width, int height, int mapLevel)
{
for(int i = posx; i < posx + width; ++i)
{
for(int j = posy; j < posy + height; ++j)
{
const std::vector<TerrainViewPattern> & patterns =
terViewPatternConfig->getPatternsForGroup(getTerrainGroup(map->terrain[i][j][mapLevel].terType));
// Detect a pattern which fits best
int totalPoints, bestPattern, bestFlip = -1;
std::string transitionReplacement;
for(int i = 0; i < patterns.size(); ++i)
{
const TerrainViewPattern & pattern = patterns[i];
for(int flip = 0; flip < 3; ++flip)
{
ValidationResult valRslt = validateTerrainView(i, j, mapLevel, flip > 0 ? getFlippedPattern(pattern, flip) : pattern);
if(valRslt.result)
{
if(valRslt.points > totalPoints)
{
totalPoints = valRslt.points;
bestPattern = i;
bestFlip = flip;
transitionReplacement = valRslt.transitionReplacement;
}
break;
}
}
}
if(bestPattern == -1)
{
continue;
}
// Get mapping
const TerrainViewPattern & pattern = patterns[bestPattern];
std::pair<int, int> mapping;
if(transitionReplacement.empty())
{
mapping = pattern.mapping[0];
}
else
{
mapping = transitionReplacement == TerrainViewPattern::RULE_DIRT ? pattern.mapping[0] : pattern.mapping[1];
}
// Set terrain view
if(pattern.flipMode == TerrainViewPattern::FLIP_MODE_SAME_IMAGE)
{
map->terrain[i][j][mapLevel].terView = gen.getInteger(mapping.first, mapping.second);
map->terrain[i][j][mapLevel].extTileFlags = bestFlip;
}
else
{
int range = (mapping.second - mapping.first) / 4;
map->terrain[i][j][mapLevel].terView = gen.getInteger(mapping.first + bestFlip * range,
mapping.first + (bestFlip + 1) * range - 1);
map->terrain[i][j][mapLevel].extTileFlags = 0;
}
}
}
}
ETerrainGroup::ETerrainGroup CMapEditManager::getTerrainGroup(ETerrainType::ETerrainType terType) const
{
switch(terType)
{
case ETerrainType::DIRT:
return ETerrainGroup::DIRT;
case ETerrainType::SAND:
return ETerrainGroup::SAND;
case ETerrainType::WATER:
return ETerrainGroup::WATER;
case ETerrainType::ROCK:
return ETerrainGroup::ROCK;
default:
return ETerrainGroup::NORMAL;
}
}
CMapEditManager::ValidationResult CMapEditManager::validateTerrainView(int posx, int posy, int mapLevel, const TerrainViewPattern & pattern) const
{
ETerrainType::ETerrainType centerTerType = map->terrain[posx][posy][mapLevel].terType;
int totalPoints = 0;
std::string transitionReplacement;
for(int i = 0; i < 9; ++i)
{
// The center, middle cell can be skipped
if(i == 4)
{
continue;
}
// Get terrain group of the current cell
int cx = posx + (i % 3) - 1;
int cy = posy + (i / 3) - 1;
bool isAlien = false;
ETerrainType::ETerrainType terType;
if(cx < 0 || cx >= map->width || cy < 0 || cy >= map->height)
{
terType = centerTerType;
}
else
{
terType = map->terrain[cx][cy][mapLevel].terType;
if(terType != centerTerType)
{
isAlien = true;
}
}
// Validate all rules per cell
int topPoints = -1;
for(int j = 0; j < pattern.data[i].size(); ++j)
{
const std::pair<std::string, int> & rulePair = pattern.data[i][j];
const std::string & rule = rulePair.first;
bool isNative = (rule == TerrainViewPattern::RULE_NATIVE || rule == TerrainViewPattern::RULE_ANY) && !isAlien;
auto validationRslt = [&](bool rslt)
{
if(rslt)
{
topPoints = std::max(topPoints, rulePair.second);
}
return rslt;
};
// Validate cell with the ruleset of the pattern
bool validation;
if(pattern.terGroup == ETerrainGroup::NORMAL)
{
bool isDirt = (rule == TerrainViewPattern::RULE_DIRT
|| rule == TerrainViewPattern::RULE_TRANSITION || rule == TerrainViewPattern::RULE_ANY)
&& isAlien && !isSandType(terType);
bool isSand = (rule == TerrainViewPattern::RULE_SAND || rule == TerrainViewPattern::RULE_TRANSITION
|| rule == TerrainViewPattern::RULE_ANY)
&& isSandType(terType);
if(transitionReplacement.empty() && (rule == TerrainViewPattern::RULE_TRANSITION
|| rule == TerrainViewPattern::RULE_ANY) && (isDirt || isSand))
{
transitionReplacement = isDirt ? TerrainViewPattern::RULE_DIRT : TerrainViewPattern::RULE_SAND;
}
validation = validationRslt((isDirt && transitionReplacement != TerrainViewPattern::RULE_SAND)
|| (isSand && transitionReplacement != TerrainViewPattern::RULE_DIRT)
|| isNative);
}
else if(pattern.terGroup == ETerrainGroup::DIRT)
{
bool isSand = rule == TerrainViewPattern::RULE_SAND && isSandType(terType);
bool isDirt = rule == TerrainViewPattern::RULE_DIRT && !isSandType(terType) && !isNative;
validation = validationRslt(rule == TerrainViewPattern::RULE_ANY || isSand || isDirt || isNative);
}
else if(pattern.terGroup == ETerrainGroup::SAND || pattern.terGroup == ETerrainGroup::WATER ||
pattern.terGroup == ETerrainGroup::ROCK)
{
bool isSand = rule == TerrainViewPattern::RULE_SAND && isSandType(terType) && !isNative;
validation = validationRslt(rule == TerrainViewPattern::RULE_ANY || isSand || isNative);
}
if(!validation)
{
return ValidationResult(false);
}
}
if(topPoints == -1)
{
return ValidationResult(false);
}
else
{
totalPoints += topPoints;
}
}
if(pattern.minPoints > totalPoints)
{
return ValidationResult(false);
}
return ValidationResult(true, totalPoints, transitionReplacement);
}
bool CMapEditManager::isSandType(ETerrainType::ETerrainType terType) const
{
switch(terType)
{
case ETerrainType::WATER:
case ETerrainType::SAND:
case ETerrainType::ROCK:
return true;
default:
return false;
}
}
TerrainViewPattern CMapEditManager::getFlippedPattern(const TerrainViewPattern & pattern, int flip) const
{
if(flip == 0)
{
return pattern;
}
TerrainViewPattern ret = pattern;
if(flip == FLIP_PATTERN_HORIZONTAL || flip == FLIP_PATTERN_BOTH)
{
for(int i = 0; i < 3; ++i)
{
int y = i * 3;
std::swap(ret.data[y], ret.data[y + 2]);
}
}
if(flip == FLIP_PATTERN_VERTICAL || flip == FLIP_PATTERN_BOTH)
{
for(int i = 0; i < 3; ++i)
{
std::swap(ret.data[i], ret.data[6 + i]);
}
}
return ret;
}
void CMapEditManager::insertObject(CGObjectInstance * obj, int posx, int posy, bool underground)
{
obj->pos = int3(posx, posy, underground ? 1 : 0);
obj->id = map->objects.size();
map->objects.push_back(obj);
if(obj->ID == Obj::TOWN)
{
map->towns.push_back(static_cast<CGTownInstance *>(obj));
}
if(obj->ID == Obj::HERO)
{
map->heroes.push_back(static_cast<CGHeroInstance*>(obj));
}
map->addBlockVisTiles(obj);
}
CMapEditManager::ValidationResult::ValidationResult(bool result, int points /*= 0*/, const std::string & transitionReplacement /*= ""*/)
: result(result), points(points), transitionReplacement(transitionReplacement)
{
}

View File

@ -0,0 +1,264 @@
/*
* CMapEditManager.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 "../CRandomGenerator.h"
#include "CMap.h"
class CGObjectInstance;
namespace ETerrainGroup
{
/**
* This enumeration lists terrain groups which differ in the terrain view frames alignment.
*/
enum ETerrainGroup
{
NORMAL,
DIRT,
SAND,
WATER,
ROCK
};
}
/**
* The terrain view pattern describes a specific composition of terrain tiles
* in a 3x3 matrix and notes which terrain view frame numbers can be used.
*/
struct TerrainViewPattern
{
/** Constant for the flip mode same image. Pattern will be flipped and the same image will be used(which is given in the mapping). */
static const std::string FLIP_MODE_SAME_IMAGE;
/** Constant for the flip mode different images. Pattern will be flipped and different images will be used(mapping area is divided into 4 parts) */
static const std::string FLIP_MODE_DIFF_IMAGES;
/** Constant for the rule dirt, meaning a dirty border is required. */
static const std::string RULE_DIRT;
/** Constant for the rule sand, meaning a sandy border is required. */
static const std::string RULE_SAND;
/** Constant for the rule transition, meaning a dirty OR sandy border is required. */
static const std::string RULE_TRANSITION;
/** Constant for the rule native, meaning a native type is required. */
static const std::string RULE_NATIVE;
/** Constant for the rule any, meaning a native type, dirty OR sandy border is required. */
static const std::string RULE_ANY;
/**
* Default constructor.
*/
TerrainViewPattern();
/**
* The pattern data.
*
* It can be visualized as a 3x3 matrix:
* [ ][ ][ ]
* [ ][ ][ ]
* [ ][ ][ ]
*
* The box in the center belongs always to the native terrain type and
* is the point of origin. Depending on the terrain type different rules
* can be used. Their meaning differs also from type to type.
*
* std::vector -> several rules can be used in one cell
* std::pair -> combination of the name of the rule and a optional number of points
*/
std::array<std::vector<std::pair<std::string, int> >, 9> data;
/** The identifier of the pattern, if it's referenced from a another pattern. */
std::string id;
/**
* This describes the mapping between this pattern and the corresponding range of frames
* which should be used for the ter view.
*
* std::vector -> size=1: typical, size=2: if this pattern should map to two different types of borders
* std::pair -> 1st value: lower range, 2nd value: upper range
*/
std::vector<std::pair<int, int> > mapping;
/** The minimum points to reach to to validate the pattern successfully. */
int minPoints;
/** Describes if flipping is required and which mapping should be used. */
std::string flipMode;
/** The terrain group to which the pattern belongs to. */
ETerrainGroup::ETerrainGroup terGroup;
};
/**
* The terrain view pattern config loads pattern data from the filesystem.
*/
class CTerrainViewPatternConfig
{
public:
/**
* Constructor. Initializes the patterns data.
*/
CTerrainViewPatternConfig();
/**
* Gets the patterns for a specific group of terrain.
*
* @param terGroup the terrain group e.g. normal for grass, lava,... OR dirt OR sand,...
* @return a vector containing patterns
*/
const std::vector<TerrainViewPattern> & getPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const;
private:
/** The patterns data. */
std::map<ETerrainGroup::ETerrainGroup, std::vector<TerrainViewPattern> > patterns;
};
/**
* The map edit manager provides functionality for drawing terrain and placing
* objects on the map.
*
* TODO add undo / selection functionality for the map editor
*/
class CMapEditManager
{
public:
/**
* Constructor. The map object / terrain data has to be initialized.
*
* @param terViewPatternConfig the terrain view pattern config
* @param map the map object which should be edited
* @param randomSeed optional. the seed which is used for generating randomly terrain views
*/
CMapEditManager(const CTerrainViewPatternConfig * terViewPatternConfig, CMap * map, int randomSeed = std::time(nullptr));
/**
* Clears the terrain. The free level is filled with water and the
* underground level with rock.
*/
void clearTerrain();
/**
* Draws terrain.
*
* @param terType the type of the terrain to draw
* @param posx the x coordinate
* @param posy the y coordinate
* @param width the height of the terrain to draw
* @param height the width of the terrain to draw
* @param underground true if you want to draw at the underground, false if open
*/
void drawTerrain(ETerrainType::ETerrainType terType, int posx, int posy, int width, int height, bool underground);
/**
* Inserts an object.
*
* @param obj the object to insert
* @param posx the x coordinate
* @param posy the y coordinate
* @param underground true if you want to draw at the underground, false if open
*/
void insertObject(CGObjectInstance * obj, int posx, int posy, bool underground);
private:
/**
* The validation result struct represents the result of a pattern validation.
*/
struct ValidationResult
{
/**
* Constructor.
*
* @param result the result of the validation either true or false
* @param points optional. the points which were achieved with that pattern
* @param transitionReplacement optional. the replacement of a T rule, either D or S
*/
ValidationResult(bool result, int points = 0, const std::string & transitionReplacement = "");
/** The result of the validation. */
bool result;
/** The points which were achieved with that pattern. */
int points;
/** The replacement of a T rule, either D or S. */
std::string transitionReplacement;
};
/**
* Updates the terrain view ids in the specified area.
*
* @param posx the x coordinate
* @param posy the y coordinate
* @param width the height of the terrain to update
* @param height the width of the terrain to update
* @param mapLevel the map level, 0 for open and 1 for underground
*/
void updateTerrainViews(int posx, int posy, int width, int height, int mapLevel);
/**
* Gets the terrain group by the terrain type number.
*
* @param terType the terrain type
* @return the terrain group
*/
ETerrainGroup::ETerrainGroup getTerrainGroup(ETerrainType::ETerrainType terType) const;
/**
* Validates the terrain view of the given position and with the given pattern.
*
* @param posx the x position
* @param posy the y position
* @param mapLevel the map level, 0 for open and 1 for underground
* @param pattern the pattern to validate the terrain view with
* @return a validation result struct
*/
ValidationResult validateTerrainView(int posx, int posy, int mapLevel, const TerrainViewPattern & pattern) const;
/**
* Tests whether the given terrain type is a sand type. Sand types are: Water, Sand and Rock
*
* @param terType the terrain type to test
* @return true if the terrain type is a sand type, otherwise false
*/
bool isSandType(ETerrainType::ETerrainType terType) const;
/**
* Gets a flipped pattern.
*
* @param pattern the original pattern to flip
* @param flip the flip mode value, see FLIP_PATTERN_* constants for details
* @return the flipped pattern
*/
TerrainViewPattern getFlippedPattern(const TerrainViewPattern & pattern, int flip) const;
/** Constant for flipping a pattern horizontally. */
static const int FLIP_PATTERN_HORIZONTAL = 1;
/** Constant for flipping a pattern vertically. */
static const int FLIP_PATTERN_VERTICAL = 2;
/** Constant for flipping a pattern horizontally and vertically. */
static const int FLIP_PATTERN_BOTH = 3;
/** The map object to edit. */
CMap * map;
/** The random number generator. */
CRandomGenerator gen;
/** The terrain view pattern config. */
const CTerrainViewPatternConfig * terViewPatternConfig;
};

View File

@ -40,6 +40,6 @@ public:
template <typename Handler> void serialize(Handler &h, const int Version) template <typename Handler> void serialize(Handler &h, const int Version)
{ {
h & mapHeader & campaignHeader & scenarioOpts & fileURI & date & playerAmnt & humanPlayers; h & mapHeader & campaignHeader & scenarioOpts & fileURI & date & playerAmnt & humanPlayers;
h & actualHumanPlayers; h & actualHumanPlayers & isRandomMap;
} }
}; };

View File

@ -173,7 +173,7 @@ void CMapLoaderH3M::init()
for(int f = 0; f < map->objects.size(); ++f) for(int f = 0; f < map->objects.size(); ++f)
{ {
if(!map->objects[f]->defInfo) continue; if(!map->objects[f]->defInfo) continue;
addBlockVisibleTiles(map->objects[f]); map->addBlockVisTiles(map->objects[f]);
} }
times.push_back(MapLoadingTime("blocked/visitable tiles", sw.getDiff())); times.push_back(MapLoadingTime("blocked/visitable tiles", sw.getDiff()));
@ -231,8 +231,7 @@ void CMapLoaderH3M::readHeader()
void CMapLoaderH3M::readPlayerInfo() void CMapLoaderH3M::readPlayerInfo()
{ {
mapHeader->players.resize(8); for(int i = 0; i < mapHeader->players.size(); ++i)
for(int i = 0; i < 8; ++i)
{ {
mapHeader->players[i].canHumanPlay = static_cast<bool>(buffer[pos++]); mapHeader->players[i].canHumanPlay = static_cast<bool>(buffer[pos++]);
mapHeader->players[i].canComputerPlay = static_cast<bool>(buffer[pos++]); mapHeader->players[i].canComputerPlay = static_cast<bool>(buffer[pos++]);
@ -865,7 +864,7 @@ CArtifactInstance * CMapLoaderH3M::createArtifact(int aid, int spellID /*= -1*/)
a = new CArtifactInstance(); a = new CArtifactInstance();
} }
addNewArtifactInstance(a); map->addNewArtifactInstance(a);
//TODO make it nicer //TODO make it nicer
if(a->artType && a->artType->constituents) if(a->artType && a->artType->constituents)
@ -873,31 +872,16 @@ CArtifactInstance * CMapLoaderH3M::createArtifact(int aid, int spellID /*= -1*/)
CCombinedArtifactInstance * comb = dynamic_cast<CCombinedArtifactInstance *>(a); CCombinedArtifactInstance * comb = dynamic_cast<CCombinedArtifactInstance *>(a);
BOOST_FOREACH(CCombinedArtifactInstance::ConstituentInfo & ci, comb->constituentsInfo) BOOST_FOREACH(CCombinedArtifactInstance::ConstituentInfo & ci, comb->constituentsInfo)
{ {
addNewArtifactInstance(ci.art); map->addNewArtifactInstance(ci.art);
} }
} }
return a; return a;
} }
void CMapLoaderH3M::addNewArtifactInstance(CArtifactInstance * art)
{
art->id = map->artInstances.size();
map->artInstances.push_back(art);
}
void CMapLoaderH3M::readTerrain() void CMapLoaderH3M::readTerrain()
{ {
// Allocate memory for terrain data map->initTerrain();
map->terrain = new TerrainTile**[map->width];
for(int ii = 0; ii < map->width; ii++)
{
map->terrain[ii] = new TerrainTile*[map->height];
for(int jj = 0; jj < map->height; jj++)
{
map->terrain[ii][jj] = new TerrainTile[map->twoLevel ? 2 : 1];
}
}
// Read terrain // Read terrain
for(int a = 0; a < 2; ++a) for(int a = 0; a < 2; ++a)
@ -1276,7 +1260,7 @@ void CMapLoaderH3M::readObjects()
case Obj::SEER_HUT: case Obj::SEER_HUT:
{ {
nobj = readSeerHut(); nobj = readSeerHut();
addQuest(nobj); map->addQuest(nobj);
break; break;
} }
case Obj::WITCH_HUT: case Obj::WITCH_HUT:
@ -1584,7 +1568,7 @@ void CMapLoaderH3M::readObjects()
case Obj::QUEST_GUARD: case Obj::QUEST_GUARD:
{ {
CGQuestGuard * guard = new CGQuestGuard(); CGQuestGuard * guard = new CGQuestGuard();
addQuest(guard); map->addQuest(guard);
readQuest(guard); readQuest(guard);
nobj = guard; nobj = guard;
break; break;
@ -1672,13 +1656,13 @@ void CMapLoaderH3M::readObjects()
case Obj::BORDERGUARD: case Obj::BORDERGUARD:
{ {
nobj = new CGBorderGuard(); nobj = new CGBorderGuard();
addQuest(nobj); map->addQuest(nobj);
break; break;
} }
case Obj::BORDER_GATE: case Obj::BORDER_GATE:
{ {
nobj = new CGBorderGate(); nobj = new CGBorderGate();
addQuest (nobj); map->addQuest (nobj);
break; break;
} }
case Obj::EYE_OF_MAGI: case Obj::EYE_OF_MAGI:
@ -2244,17 +2228,9 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard)
guard->quest->isCustomComplete = guard->quest->completedText.size() > 0; guard->quest->isCustomComplete = guard->quest->completedText.size() > 0;
} }
void CMapLoaderH3M::addQuest(CGObjectInstance * quest)
{
auto q = dynamic_cast<IQuestObject *>(quest);
q->quest->qid = map->quests.size();
map->quests.push_back(q->quest);
}
CGTownInstance * CMapLoaderH3M::readTown(int castleID) CGTownInstance * CMapLoaderH3M::readTown(int castleID)
{ {
CGTownInstance * nt = new CGTownInstance(); CGTownInstance * nt = new CGTownInstance();
nt->identifier = 0;
if(map->version > EMapFormat::ROE) if(map->version > EMapFormat::ROE)
{ {
nt->identifier = read_le_u32(buffer + pos); nt->identifier = read_le_u32(buffer + pos);
@ -2421,16 +2397,8 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
nt->alignment = buffer[pos]; nt->alignment = buffer[pos];
++pos; ++pos;
} }
else
{
nt->alignment = 0xff;
}
pos += 3; pos += 3;
nt->builded = 0;
nt->destroyed = 0;
nt->garrisonHero = nullptr;
return nt; return nt;
} }
@ -2549,33 +2517,6 @@ void CMapLoaderH3M::readEvents()
} }
} }
void CMapLoaderH3M::addBlockVisibleTiles(CGObjectInstance * obj)
{
for(int fx = 0; fx < 8; ++fx)
{
for(int fy = 0; fy < 6; ++fy)
{
int xVal = obj->pos.x + fx - 7;
int yVal = obj->pos.y + fy - 5;
int zVal = obj->pos.z;
if(xVal >= 0 && xVal < map->width && yVal >= 0 && yVal < map->height)
{
TerrainTile & curt = map->terrain[xVal][yVal][zVal];
if(((obj->defInfo->visitMap[fy] >> (7 - fx)) & 1))
{
curt.visitableObjects.push_back(obj);
curt.visitable = true;
}
if(!((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1))
{
curt.blockingObjects.push_back(obj);
curt.blocked = true;
}
}
}
}
}
ui8 CMapLoaderH3M::reverse(ui8 arg) ui8 CMapLoaderH3M::reverse(ui8 arg)
{ {
ui8 ret = 0; ui8 ret = 0;

View File

@ -225,13 +225,6 @@ private:
*/ */
CArtifactInstance * createArtifact(int aid, int spellID = -1); CArtifactInstance * createArtifact(int aid, int spellID = -1);
/**
* Adds the specified artifact instance to the list of artifacts of this map.
*
* @param art the artifact which should be added to the list of artifacts
*/
void addNewArtifactInstance(CArtifactInstance * art);
/** /**
* Read rumors. * Read rumors.
*/ */
@ -288,13 +281,6 @@ private:
*/ */
void readQuest(IQuestObject * guard); void readQuest(IQuestObject * guard);
/**
* Adds the specified quest instance to the list of quests.
*
* @param quest the quest object which should be added to the list of quests
*/
void addQuest(CGObjectInstance * quest);
/** /**
* Reads a town. * Reads a town.
* *
@ -318,13 +304,6 @@ private:
*/ */
void readEvents(); void readEvents();
/**
* Adds object instance to block visitable tiles.
*
* @param obj the object to add
*/
void addBlockVisibleTiles(CGObjectInstance * obj);
/** /**
* Reverses the input argument. * Reverses the input argument.
* *

View File

@ -1,8 +1,10 @@
#include "StdInc.h" #include "StdInc.h"
#include "CMapGenOptions.h" #include "CMapGenOptions.h"
#include "../GameConstants.h"
CMapGenOptions::CMapGenOptions() : width(72), height(72), hasTwoLevels(true), CMapGenOptions::CMapGenOptions() : width(72), height(72), hasTwoLevels(true),
playersCnt(-1), teamsCnt(-1), compOnlyPlayersCnt(0), compOnlyTeamsCnt(-1), playersCnt(RANDOM_SIZE), teamsCnt(RANDOM_SIZE), compOnlyPlayersCnt(0), compOnlyTeamsCnt(RANDOM_SIZE),
waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM) waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM)
{ {
@ -13,7 +15,7 @@ si32 CMapGenOptions::getWidth() const
return width; return width;
} }
void CMapGenOptions::setWidth(int value) void CMapGenOptions::setWidth(si32 value)
{ {
if(value > 0) if(value > 0)
{ {
@ -21,7 +23,7 @@ void CMapGenOptions::setWidth(int value)
} }
else else
{ {
throw std::runtime_error("Map width lower than 1 not allowed."); throw std::runtime_error("A map width lower than 1 is not allowed.");
} }
} }
@ -38,7 +40,7 @@ void CMapGenOptions::setHeight(si32 value)
} }
else else
{ {
throw std::runtime_error("Map height lower than 1 not allowed."); throw std::runtime_error("A map height lower than 1 is not allowed.");
} }
} }
@ -59,13 +61,14 @@ si8 CMapGenOptions::getPlayersCnt() const
void CMapGenOptions::setPlayersCnt(si8 value) void CMapGenOptions::setPlayersCnt(si8 value)
{ {
if((value >= 1 && value <= 8) || value == RANDOM_SIZE) if((value >= 1 && value <= GameConstants::PLAYER_LIMIT) || value == RANDOM_SIZE)
{ {
playersCnt = value; playersCnt = value;
} }
else else
{ {
throw std::runtime_error("Players count of RMG options should be between 1 and 8 or -1 for random."); throw std::runtime_error("Players count of RMG options should be between 1 and " +
boost::lexical_cast<std::string>(GameConstants::PLAYER_LIMIT) + " or CMapGenOptions::RANDOM_SIZE for random.");
} }
} }
@ -83,7 +86,7 @@ void CMapGenOptions::setTeamsCnt(si8 value)
else else
{ {
throw std::runtime_error("Teams count of RMG options should be between 0 and <" + throw std::runtime_error("Teams count of RMG options should be between 0 and <" +
boost::lexical_cast<std::string>(playersCnt) + "(players count) - 1> or -1 for random."); boost::lexical_cast<std::string>(playersCnt) + "(players count) - 1> or CMapGenOptions::RANDOM_SIZE for random.");
} }
} }
@ -94,15 +97,15 @@ si8 CMapGenOptions::getCompOnlyPlayersCnt() const
void CMapGenOptions::setCompOnlyPlayersCnt(si8 value) void CMapGenOptions::setCompOnlyPlayersCnt(si8 value)
{ {
if(value == RANDOM_SIZE || (value >= 0 && value <= 8 - playersCnt)) if(value == RANDOM_SIZE || (value >= 0 && value <= GameConstants::PLAYER_LIMIT - playersCnt))
{ {
compOnlyPlayersCnt = value; compOnlyPlayersCnt = value;
} }
else else
{ {
throw std::runtime_error(std::string("Computer only players count of RMG options should be ") + throw std::runtime_error(std::string("Computer only players count of RMG options should be ") +
"between 0 and <8 - " + boost::lexical_cast<std::string>(playersCnt) + "between 0 and <GameConstants::PLAYER_LIMIT - " + boost::lexical_cast<std::string>(playersCnt) +
"(playersCount)> or -1 for random."); "(playersCount)> or CMapGenOptions::RANDOM_SIZE for random.");
} }
} }
@ -113,7 +116,7 @@ si8 CMapGenOptions::getCompOnlyTeamsCnt() const
void CMapGenOptions::setCompOnlyTeamsCnt(si8 value) void CMapGenOptions::setCompOnlyTeamsCnt(si8 value)
{ {
if(value == RANDOM_SIZE || compOnlyPlayersCnt == RANDOM_SIZE || (value >= 0 && value <= compOnlyPlayersCnt - 1)) if(value == RANDOM_SIZE || compOnlyPlayersCnt == RANDOM_SIZE || (value >= 0 && value <= std::max(compOnlyPlayersCnt - 1, 0)))
{ {
compOnlyTeamsCnt = value; compOnlyTeamsCnt = value;
} }
@ -121,7 +124,7 @@ void CMapGenOptions::setCompOnlyTeamsCnt(si8 value)
{ {
throw std::runtime_error(std::string("Computer only teams count of RMG options should be ") + throw std::runtime_error(std::string("Computer only teams count of RMG options should be ") +
"between 0 and <" + boost::lexical_cast<std::string>(compOnlyPlayersCnt) + "between 0 and <" + boost::lexical_cast<std::string>(compOnlyPlayersCnt) +
"(compOnlyPlayersCnt) - 1> or -1 for random."); "(compOnlyPlayersCnt) - 1> or CMapGenOptions::RANDOM_SIZE for random.");
} }
} }

View File

@ -89,61 +89,61 @@ public:
void setHasTwoLevels(bool value); void setHasTwoLevels(bool value);
/** /**
* Gets the count of the players. The default value is -1 representing a random * Gets the count of the players. The default value is RANDOM_SIZE representing a random
* player count. * player count.
* *
* @return the count of the players ranging from 1 to 8 or -1 for random * @return the count of the players ranging from 1 to GameConstants::PLAYER_LIMIT or RANDOM_SIZE for random
*/ */
si8 getPlayersCnt() const; si8 getPlayersCnt() const;
/** /**
* Sets the count of the players. * Sets the count of the players.
* *
* @param value the count of the players ranging from 1 to 8, -1 for random * @param value the count of the players ranging from 1 to GameConstants::PLAYER_LIMIT, RANDOM_SIZE for random
*/ */
void setPlayersCnt(si8 value); void setPlayersCnt(si8 value);
/** /**
* Gets the count of the teams. The default value is -1 representing a random * Gets the count of the teams. The default value is RANDOM_SIZE representing a random
* team count. * team count.
* *
* @return the count of the teams ranging from 0 to <players count - 1> or -1 for random * @return the count of the teams ranging from 0 to <players count - 1> or RANDOM_SIZE for random
*/ */
si8 getTeamsCnt() const; si8 getTeamsCnt() const;
/** /**
* Sets the count of the teams * Sets the count of the teams
* *
* @param value the count of the teams ranging from 0 to <players count - 1>, -1 for random * @param value the count of the teams ranging from 0 to <players count - 1>, RANDOM_SIZE for random
*/ */
void setTeamsCnt(si8 value); void setTeamsCnt(si8 value);
/** /**
* Gets the count of the computer only players. The default value is 0. * Gets the count of the computer only players. The default value is 0.
* *
* @return the count of the computer only players ranging from 0 to <8 - players count> or -1 for random * @return the count of the computer only players ranging from 0 to <GameConstants::PLAYER_LIMIT - players count> or RANDOM_SIZE for random
*/ */
si8 getCompOnlyPlayersCnt() const; si8 getCompOnlyPlayersCnt() const;
/** /**
* Sets the count of the computer only players. * Sets the count of the computer only players.
* *
* @param value the count of the computer only players ranging from 0 to <8 - players count>, -1 for random * @param value the count of the computer only players ranging from 0 to <GameConstants::PLAYER_LIMIT - players count>, RANDOM_SIZE for random
*/ */
void setCompOnlyPlayersCnt(si8 value); void setCompOnlyPlayersCnt(si8 value);
/** /**
* Gets the count of the computer only teams. The default value is -1 representing * Gets the count of the computer only teams. The default value is RANDOM_SIZE representing
* a random computer only team count. * a random computer only team count.
* *
* @return the count of the computer only teams ranging from 0 to <comp only players - 1> or -1 for random * @return the count of the computer only teams ranging from 0 to <comp only players - 1> or RANDOM_SIZE for random
*/ */
si8 getCompOnlyTeamsCnt() const; si8 getCompOnlyTeamsCnt() const;
/** /**
* Sets the count of the computer only teams. * Sets the count of the computer only teams.
* *
* @param value the count of the computer only teams ranging from 0 to <comp only players - 1>, -1 for random * @param value the count of the computer only teams ranging from 0 to <comp only players - 1>, RANDOM_SIZE for random
*/ */
void setCompOnlyTeamsCnt(si8 value); void setCompOnlyTeamsCnt(si8 value);
@ -213,6 +213,8 @@ public:
template <typename Handler> template <typename Handler>
void serialize(Handler & h, const int version) void serialize(Handler & h, const int version)
{ {
//FIXME: Enum is not a fixed with data type. Add enum class to both enums
// later. For now it is ok.
h & width & height & hasTwoLevels & playersCnt & teamsCnt & compOnlyPlayersCnt; h & width & height & hasTwoLevels & playersCnt & teamsCnt & compOnlyPlayersCnt;
h & compOnlyTeamsCnt & waterContent & monsterStrength; h & compOnlyTeamsCnt & waterContent & monsterStrength;
} }

399
lib/RMG/CMapGenerator.cpp Normal file
View File

@ -0,0 +1,399 @@
#include "StdInc.h"
#include "CMapGenerator.h"
#include "../Mapping/CMap.h"
#include "../VCMI_Lib.h"
#include "../CGeneralTextHandler.h"
#include "../Mapping/CMapEditManager.h"
#include "../CObjectHandler.h"
#include "../CDefObjInfoHandler.h"
#include "../GameConstants.h"
#include "../CTownHandler.h"
#include "../StringConstants.h"
CMapGenerator::CMapGenerator(const CMapGenOptions & mapGenOptions, const std::map<TPlayerColor, CPlayerSettings> & players, int randomSeed) :
mapGenOptions(mapGenOptions), randomSeed(randomSeed), players(players)
{
gen.seed(randomSeed);
validateOptions();
}
CMapGenerator::~CMapGenerator()
{
}
std::unique_ptr<CMap> CMapGenerator::generate()
{
finalizeMapGenOptions();
//TODO select a template based on the map gen options or adapt it if necessary
map = std::unique_ptr<CMap>(new CMap());
addHeaderInfo();
terViewPatternConfig = std::unique_ptr<CTerrainViewPatternConfig>(new CTerrainViewPatternConfig());
mapMgr = std::unique_ptr<CMapEditManager>(new CMapEditManager(terViewPatternConfig.get(), map.get(), randomSeed));
genTerrain();
genTowns();
return std::move(map);
}
void CMapGenerator::validateOptions() const
{
int playersCnt = 0;
int compOnlyPlayersCnt = 0;
BOOST_FOREACH(const tPlayersMap::value_type & pair, players)
{
if(pair.second.getPlayerType() == CPlayerSettings::COMP_ONLY)
{
++compOnlyPlayersCnt;
}
else
{
++playersCnt;
}
}
if(mapGenOptions.getPlayersCnt() == CMapGenOptions::RANDOM_SIZE)
{
if(playersCnt != GameConstants::PLAYER_LIMIT)
{
throw std::runtime_error(std::string("If the count of players is random size, ")
+ "the count of the items in the players map should equal GameConstants::PLAYER_LIMIT.");
}
if(playersCnt == mapGenOptions.getPlayersCnt())
{
throw std::runtime_error(std::string("If the count of players is random size, ")
+ "all items in the players map should be either of player type AI or HUMAN.");
}
}
else
{
if(mapGenOptions.getCompOnlyPlayersCnt() != CMapGenOptions::RANDOM_SIZE)
{
if(playersCnt != mapGenOptions.getPlayersCnt() || compOnlyPlayersCnt != mapGenOptions.getCompOnlyPlayersCnt())
{
throw std::runtime_error(std::string("The count of players and computer only players in the players map ")
+ "doesn't conform with the specified map gen options.");
}
}
else
{
if(playersCnt != mapGenOptions.getPlayersCnt() || (playersCnt == mapGenOptions.getPlayersCnt()
&& compOnlyPlayersCnt != GameConstants::PLAYER_LIMIT - playersCnt))
{
throw std::runtime_error(std::string("If the count of players is fixed and the count of comp only players random, ")
+ "the items in the players map should equal GameConstants::PLAYER_LIMIT.");
}
}
}
if(countHumanPlayers() < 1)
{
throw std::runtime_error("1 human player is required at least");
}
BOOST_FOREACH(const tPlayersMap::value_type & pair, players)
{
if(pair.first != pair.second.getColor())
{
throw std::runtime_error("The color of an item in player settings and the key of it has to be the same.");
}
}
}
void CMapGenerator::finalizeMapGenOptions()
{
if(mapGenOptions.getPlayersCnt() == CMapGenOptions::RANDOM_SIZE)
{
mapGenOptions.setPlayersCnt(gen.getInteger(countHumanPlayers(), GameConstants::PLAYER_LIMIT));
// Remove AI players only from the end of the players map if necessary
for(auto itrev = players.end(); itrev != players.begin();)
{
auto it = itrev;
--it;
if(players.size() == mapGenOptions.getPlayersCnt())
{
break;
}
const CPlayerSettings & pSettings = it->second;
if(pSettings.getPlayerType() == CPlayerSettings::AI)
{
players.erase(it);
}
else
{
--itrev;
}
}
}
if(mapGenOptions.getTeamsCnt() == CMapGenOptions::RANDOM_SIZE)
{
mapGenOptions.setTeamsCnt(gen.getInteger(0, mapGenOptions.getPlayersCnt() - 1));
}
if(mapGenOptions.getCompOnlyPlayersCnt() == CMapGenOptions::RANDOM_SIZE)
{
mapGenOptions.setCompOnlyPlayersCnt(gen.getInteger(0, 8 - mapGenOptions.getPlayersCnt()));
int totalPlayersCnt = mapGenOptions.getPlayersCnt() + mapGenOptions.getCompOnlyPlayersCnt();
// Remove comp only players only from the end of the players map if necessary
for(auto itrev = players.end(); itrev != players.begin();)
{
auto it = itrev;
--it;
if(players.size() <= totalPlayersCnt)
{
break;
}
const CPlayerSettings & pSettings = it->second;
if(pSettings.getPlayerType() == CPlayerSettings::COMP_ONLY)
{
players.erase(it);
}
else
{
--itrev;
}
}
// Add some comp only players if necessary
int compOnlyPlayersToAdd = totalPlayersCnt - players.size();
for(int i = 0; i < compOnlyPlayersToAdd; ++i)
{
CPlayerSettings pSettings;
pSettings.setPlayerType(CPlayerSettings::COMP_ONLY);
pSettings.setColor(getNextPlayerColor());
players[pSettings.getColor()] = pSettings;
}
}
if(mapGenOptions.getCompOnlyTeamsCnt() == CMapGenOptions::RANDOM_SIZE)
{
mapGenOptions.setCompOnlyTeamsCnt(gen.getInteger(0, std::max(mapGenOptions.getCompOnlyPlayersCnt() - 1, 0)));
}
// There should be at least 2 players (1-player-maps aren't allowed)
if(mapGenOptions.getPlayersCnt() + mapGenOptions.getCompOnlyPlayersCnt() < 2)
{
CPlayerSettings pSettings;
pSettings.setPlayerType(CPlayerSettings::AI);
pSettings.setColor(getNextPlayerColor());
players[pSettings.getColor()] = pSettings;
mapGenOptions.setPlayersCnt(2);
}
// 1 team isn't allowed
if(mapGenOptions.getTeamsCnt() == 1 && mapGenOptions.getCompOnlyPlayersCnt() == 0)
{
mapGenOptions.setTeamsCnt(0);
}
if(mapGenOptions.getWaterContent() == EWaterContent::RANDOM)
{
mapGenOptions.setWaterContent(static_cast<EWaterContent::EWaterContent>(gen.getInteger(0, 2)));
}
if(mapGenOptions.getMonsterStrength() == EMonsterStrength::RANDOM)
{
mapGenOptions.setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(gen.getInteger(0, 2)));
}
}
std::string CMapGenerator::getMapDescription() const
{
const std::string waterContentStr[3] = { "none", "normal", "islands" };
const std::string monsterStrengthStr[3] = { "weak", "normal", "strong" };
std::stringstream ss;
ss << "Map created by the Random Map Generator.\nTemplate was <MOCK>, ";
ss << "Random seed was " << randomSeed << ", size " << map->width << "x";
ss << map->height << ", levels " << (map->twoLevel ? "2" : "1") << ", ";
ss << "humans " << static_cast<int>(mapGenOptions.getPlayersCnt()) << ", computers ";
ss << static_cast<int>(mapGenOptions.getCompOnlyPlayersCnt()) << ", water " << waterContentStr[mapGenOptions.getWaterContent()];
ss << ", monster " << monsterStrengthStr[mapGenOptions.getMonsterStrength()] << ", second expansion map";
BOOST_FOREACH(const tPlayersMap::value_type & pair, players)
{
const CPlayerSettings & pSettings = pair.second;
if(pSettings.getPlayerType() == CPlayerSettings::HUMAN)
{
ss << ", " << GameConstants::PLAYER_COLOR_NAMES[pSettings.getColor()] << " is human";
}
if(pSettings.getStartingTown() != CPlayerSettings::RANDOM_TOWN)
{
ss << ", " << GameConstants::PLAYER_COLOR_NAMES[pSettings.getColor()]
<< " town choice is " << ETownType::names[pSettings.getStartingTown()];
}
}
return ss.str();
}
void CMapGenerator::addPlayerInfo()
{
// Calculate which team numbers exist
std::array<std::list<int>, 2> teamNumbers; // 0= cpu/human, 1= cpu only
int teamOffset = 0;
for(int i = 0; i < 2; ++i)
{
int playersCnt = i == 0 ? mapGenOptions.getPlayersCnt() : mapGenOptions.getCompOnlyPlayersCnt();
int teamsCnt = i == 0 ? mapGenOptions.getTeamsCnt() : mapGenOptions.getCompOnlyTeamsCnt();
if(playersCnt == 0)
{
continue;
}
int playersPerTeam = playersCnt /
(teamsCnt == 0 ? playersCnt : teamsCnt);
int teamsCntNorm = teamsCnt;
if(teamsCntNorm == 0)
{
teamsCntNorm = playersCnt;
}
for(int j = 0; j < teamsCntNorm; ++j)
{
for(int k = 0; k < playersPerTeam; ++k)
{
teamNumbers[i].push_back(j + teamOffset);
}
}
for(int j = 0; j < playersCnt - teamsCntNorm * playersPerTeam; ++j)
{
teamNumbers[i].push_back(j + teamOffset);
}
teamOffset += teamsCntNorm;
}
// Team numbers are assigned randomly to every player
BOOST_FOREACH(const tPlayersMap::value_type & pair, players)
{
const CPlayerSettings & pSettings = pair.second;
PlayerInfo player;
player.canComputerPlay = true;
int j = pSettings.getPlayerType() == CPlayerSettings::COMP_ONLY ? 1 : 0;
if(j == 0)
{
player.canHumanPlay = true;
}
auto itTeam = std::next(teamNumbers[j].begin(), gen.getInteger(0, teamNumbers[j].size() - 1));
player.team = *itTeam;
teamNumbers[j].erase(itTeam);
map->players[pSettings.getColor()] = player;
}
map->howManyTeams = (mapGenOptions.getTeamsCnt() == 0 ? mapGenOptions.getPlayersCnt() : mapGenOptions.getTeamsCnt())
+ (mapGenOptions.getCompOnlyTeamsCnt() == 0 ? mapGenOptions.getCompOnlyPlayersCnt() : mapGenOptions.getCompOnlyTeamsCnt());
}
int CMapGenerator::countHumanPlayers() const
{
return static_cast<int>(std::count_if(players.begin(), players.end(), [](const std::pair<TPlayerColor, CPlayerSettings> & pair)
{
return pair.second.getPlayerType() == CPlayerSettings::HUMAN;
}));
}
void CMapGenerator::genTerrain()
{
map->initTerrain(); //FIXME nicer solution
mapMgr->clearTerrain();
mapMgr->drawTerrain(ETerrainType::GRASS, 10, 10, 20, 30, false);
}
void CMapGenerator::genTowns()
{
//FIXME mock gen
const int3 townPos[2] = { int3(17, 13, 0), int3(25,13, 0) };
const TFaction townTypes[2] = { ETownType::CASTLE, ETownType::DUNGEON };
for(auto it = players.begin(); it != players.end(); ++it)
{
TPlayerColor owner = it->first;
int pos = std::distance(players.begin(), it);
int side = pos % 2;
CGTownInstance * town = new CGTownInstance();
town->ID = Obj::TOWN;
town->subID = townTypes[side];
town->tempOwner = owner;
town->defInfo = VLC->dobjinfo->gobjs[town->ID][town->subID];
town->builtBuildings.insert(EBuilding::FORT);
town->builtBuildings.insert(-50);
mapMgr->insertObject(town, townPos[side].x, townPos[side].y + (pos / 2) * 5, false);
map->players[owner].allowedFactions.clear();
map->players[owner].allowedFactions.insert(townTypes[side]);
}
}
void CMapGenerator::addHeaderInfo()
{
map->version = EMapFormat::SOD;
map->width = mapGenOptions.getWidth();
map->height = mapGenOptions.getHeight();
map->twoLevel = mapGenOptions.getHasTwoLevels();
map->name = VLC->generaltexth->allTexts[740];
map->description = getMapDescription();
map->difficulty = 1;
addPlayerInfo();
}
TPlayerColor CMapGenerator::getNextPlayerColor() const
{
for(TPlayerColor i = 0; i < GameConstants::PLAYER_LIMIT; ++i)
{
if(players.find(i) == players.end())
{
return i;
}
}
throw std::runtime_error("Shouldn't happen. No free player color exists.");
}
CMapGenerator::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(AI)
{
}
int CMapGenerator::CPlayerSettings::getColor() const
{
return color;
}
void CMapGenerator::CPlayerSettings::setColor(int value)
{
if(value >= 0 && value < GameConstants::PLAYER_LIMIT)
{
color = value;
}
else
{
throw std::runtime_error("The color of the player is not in a valid range.");
}
}
int CMapGenerator::CPlayerSettings::getStartingTown() const
{
return startingTown;
}
void CMapGenerator::CPlayerSettings::setStartingTown(int value)
{
if(value >= -1 && value < static_cast<int>(VLC->townh->towns.size()))
{
startingTown = value;
}
else
{
throw std::runtime_error("The starting town of the player is not in a valid range.");
}
}
CMapGenerator::CPlayerSettings::EPlayerType CMapGenerator::CPlayerSettings::getPlayerType() const
{
return playerType;
}
void CMapGenerator::CPlayerSettings::setPlayerType(EPlayerType value)
{
playerType = value;
}

199
lib/RMG/CMapGenerator.h Normal file
View File

@ -0,0 +1,199 @@
/*
* CMapGenerator.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 "CMapGenOptions.h"
#include "../CRandomGenerator.h"
class CMap;
class CTerrainViewPatternConfig;
class CMapEditManager;
/**
* The map generator creates a map randomly.
*/
class CMapGenerator
{
public:
/**
* The player settings class maps the player color, starting town and human player flag.
*/
class CPlayerSettings
{
public:
enum EPlayerType
{
HUMAN,
AI,
COMP_ONLY
};
/**
* Constructor.
*/
CPlayerSettings();
/**
* Gets the color of the player. The default value is 0.
*
* @return the color of the player ranging from 0 to GameConstants::PLAYER_LIMIT - 1
*/
int getColor() const;
/**
* Sets the color of the player.
*
* @param value the color of the player ranging from 0 to GameConstants::PLAYER_LIMIT - 1
*/
void setColor(int value);
/**
* Gets the starting town of the player. The default value is RANDOM_TOWN.
*
* @return the starting town of the player ranging from 0 to town max count or RANDOM_TOWN
*/
int getStartingTown() const;
/**
* Sets the starting town of the player.
*
* @param value the starting town of the player ranging from 0 to town max count or RANDOM_TOWN
*/
void setStartingTown(int value);
/**
* Gets the type of the player. The default value is EPlayerType::AI.
*
* @return the type of the player
*/
EPlayerType getPlayerType() const;
/**
* Sets the type of the player.
*
* @param playerType the type of the player
*/
void setPlayerType(EPlayerType value);
/** Constant for a random town selection. */
static const int RANDOM_TOWN = -1;
private:
/** The color of the player. */
int color;
/** The starting town of the player. */
int startingTown;
/** The type of the player e.g. human, comp only,... */
EPlayerType playerType;
};
/**
* Constructor.
*
* @param mapGenOptions these options describe how to generate the map.
* @param players the random gen player settings
* @param randomSeed a random seed is required to get random numbers.
*/
CMapGenerator(const CMapGenOptions & mapGenOptions, const std::map<TPlayerColor, CPlayerSettings> & players, int randomSeed);
/**
* Destructor.
*/
~CMapGenerator();
/**
* Generates a map.
*
* @return the generated map object stored in a unique ptr
*/
std::unique_ptr<CMap> generate();
private:
/**
* Validates map gen options and players options. On errors exceptions will be thrown.
*/
void validateOptions() const;
/**
* Finalizes map generation options. Random sizes for various properties are
* converted to fixed values.
*/
void finalizeMapGenOptions();
/**
* Gets the map description of the generated map.
*
* @return the map description of the generated map
*/
std::string getMapDescription() const;
/**
* Adds player information.(teams, colors, etc...)
*/
void addPlayerInfo();
/**
* Counts the amount of human players.
*
* @return the amount of human players ranging from 0 to GameConstants::PLAYER_LIMIT
*/
int countHumanPlayers() const;
/**
* Generate terrain.
*/
void genTerrain();
/**
* Generate towns.
*/
void genTowns();
/**
* Adds header info(size, description, etc...)
*/
void addHeaderInfo();
/**
* Gets the next free player color.
*
* @return the next free player color
*/
TPlayerColor getNextPlayerColor() const;
/** The map options which describes the size of the map and contain player info. */
CMapGenOptions mapGenOptions;
/** The generated map. */
std::unique_ptr<CMap> map;
/** The random number generator. */
CRandomGenerator gen;
/** The random seed, it is used for the map description. */
int randomSeed;
/** The terrain view pattern config. */
std::unique_ptr<CTerrainViewPatternConfig> terViewPatternConfig;
/** The map edit manager. */
std::unique_ptr<CMapEditManager> mapMgr;
/** The random gen player settings. */
std::map<TPlayerColor, CPlayerSettings> players;
/** Typedef of the players map, so that boost foreach can be used. */
typedef std::map<TPlayerColor, CPlayerSettings> tPlayersMap;
};

View File

@ -42,6 +42,7 @@ struct PlayerSettings
std::string name; std::string name;
ui8 playerID; //0 - AI, non-0 serves as player id ui8 playerID; //0 - AI, non-0 serves as player id
bool compOnly; //true if this player is a computer only player; required for RMG
template <typename Handler> template <typename Handler>
void serialize(Handler &h, const int version) void serialize(Handler &h, const int version)
{ {
@ -55,13 +56,12 @@ struct PlayerSettings
h & name; h & name;
h & playerID; h & playerID;
h & team; h & team;
h & compOnly;
} }
PlayerSettings() PlayerSettings() : bonus(RANDOM), castle(NONE), heroPortrait(RANDOM), compOnly(false)
{ {
bonus = RANDOM;
castle = NONE;
heroPortrait = RANDOM;
} }
}; };

View File

@ -31,6 +31,9 @@ namespace GameConstants
"demoniac", "heretic", "deathknight", "necromancer", "warlock", "overlord", "demoniac", "heretic", "deathknight", "necromancer", "warlock", "overlord",
"barbarian", "battlemage", "beastmaster", "witch", "planeswalker", "elementalist" "barbarian", "battlemage", "beastmaster", "witch", "planeswalker", "elementalist"
}; };
const std::string PLAYER_COLOR_NAMES [PLAYER_LIMIT] = {
"red", "blue", "tan", "green", "orange", "purple", "teal", "pink"
};
} }
namespace EAlignment namespace EAlignment

View File

@ -197,7 +197,9 @@
<ClCompile Include="Mapping\CMap.cpp" /> <ClCompile Include="Mapping\CMap.cpp" />
<ClCompile Include="Mapping\CMapInfo.cpp" /> <ClCompile Include="Mapping\CMapInfo.cpp" />
<ClCompile Include="Mapping\CMapService.cpp" /> <ClCompile Include="Mapping\CMapService.cpp" />
<ClCompile Include="Mapping\CMapEditManager.cpp" />
<ClCompile Include="RMG\CMapGenOptions.cpp" /> <ClCompile Include="RMG\CMapGenOptions.cpp" />
<ClCompile Include="RMG\CMapGenerator.cpp" />
<ClCompile Include="HeroBonus.cpp" /> <ClCompile Include="HeroBonus.cpp" />
<ClCompile Include="CBattleCallback.cpp" /> <ClCompile Include="CBattleCallback.cpp" />
<ClCompile Include="IGameCallback.cpp" /> <ClCompile Include="IGameCallback.cpp" />
@ -239,6 +241,7 @@
<ClInclude Include="CondSh.h" /> <ClInclude Include="CondSh.h" />
<ClInclude Include="Connection.h" /> <ClInclude Include="Connection.h" />
<ClInclude Include="ConstTransitivePtr.h" /> <ClInclude Include="ConstTransitivePtr.h" />
<ClInclude Include="CRandomGenerator.h" />
<ClInclude Include="CScriptingModule.h" /> <ClInclude Include="CScriptingModule.h" />
<ClInclude Include="CSpellHandler.h" /> <ClInclude Include="CSpellHandler.h" />
<ClInclude Include="CStopWatch.h" /> <ClInclude Include="CStopWatch.h" />
@ -258,7 +261,9 @@
<ClInclude Include="Mapping\CMap.h" /> <ClInclude Include="Mapping\CMap.h" />
<ClInclude Include="Mapping\CMapInfo.h" /> <ClInclude Include="Mapping\CMapInfo.h" />
<ClInclude Include="Mapping\CMapService.h" /> <ClInclude Include="Mapping\CMapService.h" />
<ClInclude Include="Mapping\CMapEditManager.h" />
<ClInclude Include="RMG\CMapGenOptions.h" /> <ClInclude Include="RMG\CMapGenOptions.h" />
<ClInclude Include="RMG\CMapGenerator.h" />
<ClInclude Include="GameConstants.h" /> <ClInclude Include="GameConstants.h" />
<ClInclude Include="HeroBonus.h" /> <ClInclude Include="HeroBonus.h" />
<ClInclude Include="CBattleCallback.h" /> <ClInclude Include="CBattleCallback.h" />