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:
parent
d4c30667b8
commit
0311e5e6f5
3
Global.h
3
Global.h
@ -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::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
|
||||
#define DISABLE_VIDEO
|
||||
#endif
|
||||
|
@ -165,9 +165,17 @@ void updateStartInfo(std::string filename, StartInfo & sInfo, const CMapHeader *
|
||||
PlayerSettings &pset = sInfo.playerInfos[i];
|
||||
pset.color = i;
|
||||
if(pinfo.canHumanPlay && namesIt != playerNames.cend())
|
||||
{
|
||||
setPlayer(pset, namesIt++->first, playerNames);
|
||||
}
|
||||
else
|
||||
{
|
||||
setPlayer(pset, 0, playerNames);
|
||||
if(!pinfo.canHumanPlay)
|
||||
{
|
||||
pset.compOnly = true;
|
||||
}
|
||||
}
|
||||
|
||||
pset.castle = pinfo.defaultCastle();
|
||||
pset.hero = pinfo.defaultHero();
|
||||
@ -873,11 +881,6 @@ void CSelectionScreen::startScenario()
|
||||
{
|
||||
saveGameName = sInfo.mapname;
|
||||
}
|
||||
if(sInfo.createRandomMap)
|
||||
{
|
||||
// Random map generation fails for now, so don't start game...
|
||||
return;
|
||||
}
|
||||
|
||||
StartInfo * si = new StartInfo(sInfo);
|
||||
CGP->removeFromGui();
|
||||
@ -1138,9 +1141,9 @@ void SelectionTab::parseGames(const std::vector<ResourceID> &files, bool multi)
|
||||
|
||||
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
|
||||
mapSizeBtnGroup = new CHighlightableButtonsGroup(0);
|
||||
mapSizeBtnGroup->pos.y = 81;
|
||||
mapSizeBtnGroup->pos.x = 158;
|
||||
mapSizeBtnGroup->pos.y += 81;
|
||||
mapSizeBtnGroup->pos.x += 158;
|
||||
const std::vector<std::string> mapSizeBtns = boost::assign::list_of("RANSIZS")("RANSIZM")("RANSIZL")("RANSIZX");
|
||||
addButtonsToGroup(mapSizeBtnGroup, mapSizeBtns, 0, 3, 47, 198);
|
||||
mapSizeBtnGroup->select(1, false);
|
||||
@ -1621,8 +1624,8 @@ RandomMapTab::RandomMapTab()
|
||||
const int BTNS_GROUP_LEFT_MARGIN = 67;
|
||||
// Amount of players
|
||||
playersCntGroup = new CHighlightableButtonsGroup(0);
|
||||
playersCntGroup->pos.y = 153;
|
||||
playersCntGroup->pos.x = BTNS_GROUP_LEFT_MARGIN;
|
||||
playersCntGroup->pos.y += 153;
|
||||
playersCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
|
||||
addButtonsWithRandToGroup(playersCntGroup, numberDefs, 1, 8, NUMBERS_WIDTH, 204, 212);
|
||||
playersCntGroup->onChange = [&](int btnId)
|
||||
{
|
||||
@ -1635,8 +1638,8 @@ RandomMapTab::RandomMapTab()
|
||||
|
||||
// Amount of teams
|
||||
teamsCntGroup = new CHighlightableButtonsGroup(0);
|
||||
teamsCntGroup->pos.y = 219;
|
||||
teamsCntGroup->pos.x = BTNS_GROUP_LEFT_MARGIN;
|
||||
teamsCntGroup->pos.y += 219;
|
||||
teamsCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
|
||||
addButtonsWithRandToGroup(teamsCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 214, 222);
|
||||
teamsCntGroup->onChange = [&](int btnId)
|
||||
{
|
||||
@ -1646,8 +1649,8 @@ RandomMapTab::RandomMapTab()
|
||||
|
||||
// Computer only players
|
||||
compOnlyPlayersCntGroup = new CHighlightableButtonsGroup(0);
|
||||
compOnlyPlayersCntGroup->pos.y = 285;
|
||||
compOnlyPlayersCntGroup->pos.x = BTNS_GROUP_LEFT_MARGIN;
|
||||
compOnlyPlayersCntGroup->pos.y += 285;
|
||||
compOnlyPlayersCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
|
||||
addButtonsWithRandToGroup(compOnlyPlayersCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 224, 232);
|
||||
compOnlyPlayersCntGroup->select(0, true);
|
||||
compOnlyPlayersCntGroup->onChange = [&](int btnId)
|
||||
@ -1660,8 +1663,8 @@ RandomMapTab::RandomMapTab()
|
||||
|
||||
// Computer only teams
|
||||
compOnlyTeamsCntGroup = new CHighlightableButtonsGroup(0);
|
||||
compOnlyTeamsCntGroup->pos.y = 351;
|
||||
compOnlyTeamsCntGroup->pos.x = BTNS_GROUP_LEFT_MARGIN;
|
||||
compOnlyTeamsCntGroup->pos.y += 351;
|
||||
compOnlyTeamsCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
|
||||
addButtonsWithRandToGroup(compOnlyTeamsCntGroup, numberDefs, 0, 6, NUMBERS_WIDTH, 234, 241);
|
||||
deactivateButtonsFrom(compOnlyTeamsCntGroup, 0);
|
||||
compOnlyTeamsCntGroup->onChange = [&](int btnId)
|
||||
@ -1673,8 +1676,8 @@ RandomMapTab::RandomMapTab()
|
||||
const int WIDE_BTN_WIDTH = 85;
|
||||
// Water content
|
||||
waterContentGroup = new CHighlightableButtonsGroup(0);
|
||||
waterContentGroup->pos.y = 419;
|
||||
waterContentGroup->pos.x = BTNS_GROUP_LEFT_MARGIN;
|
||||
waterContentGroup->pos.y += 419;
|
||||
waterContentGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
|
||||
const std::vector<std::string> waterContentBtns = boost::assign::list_of("RANNONE")("RANNORM")("RANISLD");
|
||||
addButtonsWithRandToGroup(waterContentGroup, waterContentBtns, 0, 2, WIDE_BTN_WIDTH, 243, 246);
|
||||
waterContentGroup->onChange = [&](int btnId)
|
||||
@ -1684,8 +1687,8 @@ RandomMapTab::RandomMapTab()
|
||||
|
||||
// Monster strength
|
||||
monsterStrengthGroup = new CHighlightableButtonsGroup(0);
|
||||
monsterStrengthGroup->pos.y = 485;
|
||||
monsterStrengthGroup->pos.x = BTNS_GROUP_LEFT_MARGIN;
|
||||
monsterStrengthGroup->pos.y += 485;
|
||||
monsterStrengthGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
|
||||
const std::vector<std::string> monsterStrengthBtns = boost::assign::list_of("RANWEAK")("RANNORM")("RANSTRG");
|
||||
addButtonsWithRandToGroup(monsterStrengthGroup, monsterStrengthBtns, 0, 2, WIDE_BTN_WIDTH, 248, 251);
|
||||
monsterStrengthGroup->onChange = [&](int btnId)
|
||||
|
@ -387,9 +387,9 @@ void CGuiHandler::run()
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
428
config/terrainViewPatterns.json
Normal file
428
config/terrainViewPatterns.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
#include "CSpellHandler.h"
|
||||
#include "CObjectHandler.h"
|
||||
#include "NetPacks.h"
|
||||
#include "GameConstants.h"
|
||||
|
||||
using namespace boost::assign;
|
||||
|
||||
@ -776,14 +777,23 @@ void CArtHandler::initAllowedArtifactsList(const std::vector<ui8> &allowed)
|
||||
allowedArtifacts.push_back(artifacts[i]);
|
||||
else //check if active modules allow artifact to be every used
|
||||
{
|
||||
if (artifacts[i]->possibleSlots[ArtBearer::COMMANDER].size() && VLC->modh->modules.COMMANDERS ||
|
||||
artifacts[i]->possibleSlots[ArtBearer::CREATURE].size() && VLC->modh->modules.STACK_ARTIFACT)
|
||||
if ((artifacts[i]->possibleSlots[ArtBearer::COMMANDER].size() && VLC->modh->modules.COMMANDERS) ||
|
||||
(artifacts[i]->possibleSlots[ArtBearer::CREATURE].size() && VLC->modh->modules.STACK_ARTIFACT))
|
||||
allowedArtifacts.push_back(artifacts[i]);
|
||||
//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()
|
||||
{
|
||||
init();
|
||||
|
@ -256,6 +256,13 @@ public:
|
||||
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)
|
||||
{
|
||||
h & artifacts & allowedArtifacts & treasures & minors & majors & relics
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "Filesystem/CResourceLoader.h"
|
||||
#include "GameConstants.h"
|
||||
#include "RMG/CMapGenOptions.h"
|
||||
#include "RMG/CMapGenerator.h"
|
||||
|
||||
DLL_LINKAGE boost::rand48 ran;
|
||||
class CGObjectInstance;
|
||||
@ -872,7 +873,46 @@ void CGameState::init(StartInfo * si)
|
||||
if(scenarioOps->createRandomMap)
|
||||
{
|
||||
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
|
||||
{
|
||||
@ -902,7 +942,6 @@ void CGameState::init(StartInfo * si)
|
||||
VLC->arth->initAllowedArtifactsList(map->allowedArtifact);
|
||||
tlog0 << "Map loaded!" << std::endl;
|
||||
|
||||
|
||||
//tlog0 <<"Reading and detecting map file (together): "<<tmh.getDif()<<std::endl;
|
||||
tlog0 << "\tOur checksum for the map: "<< map->checksum << std::endl;
|
||||
if(scenarioOps->mapfileChecksum)
|
||||
|
@ -498,3 +498,10 @@ std::vector<ui8> CHeroHandler::getDefaultAllowedHeroes() const
|
||||
|
||||
return allowedHeroes;
|
||||
}
|
||||
|
||||
std::vector<ui8> CHeroHandler::getDefaultAllowedAbilities() const
|
||||
{
|
||||
std::vector<ui8> allowedAbilities;
|
||||
allowedAbilities.resize(GameConstants::SKILL_QUANTITY, 1);
|
||||
return allowedAbilities;
|
||||
}
|
||||
|
@ -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
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
h & classes & heroes & expPerLevel & ballistics & terrCosts;
|
||||
|
@ -15,9 +15,11 @@ set(lib_SRCS
|
||||
Filesystem/CCompressedStream.cpp
|
||||
Mapping/CCampaignHandler.cpp
|
||||
Mapping/CMap.cpp
|
||||
Mapping/CMapEditManager.cpp
|
||||
Mapping/CMapInfo.cpp
|
||||
Mapping/CMapService.cpp
|
||||
RMG/CMapGenOptions.cpp
|
||||
RMG/CMapGenerator.cpp
|
||||
BattleAction.cpp
|
||||
BattleHex.cpp
|
||||
BattleState.cpp
|
||||
@ -55,6 +57,7 @@ set(lib_HEADERS
|
||||
AI_Base.h
|
||||
CondSh.h
|
||||
ConstTransitivePtr.h
|
||||
CRandomGenerator.h
|
||||
CScriptingModule.h
|
||||
CStopWatch.h
|
||||
GameConstants.h
|
||||
|
@ -1971,11 +1971,9 @@ bool CGTownInstance::hasCapitol() const
|
||||
return hasBuilt(EBuilding::CAPITOL);
|
||||
}
|
||||
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()
|
||||
|
122
lib/CRandomGenerator.h
Normal file
122
lib/CRandomGenerator.h
Normal 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;
|
||||
};
|
@ -378,3 +378,10 @@ void CSpellHandler::loadSpells()
|
||||
risingSpells += 38, 39, 40;
|
||||
mindSpells += 50, 59, 60, 61, 62;
|
||||
}
|
||||
|
||||
std::vector<ui8> CSpellHandler::getDefaultAllowedSpells() const
|
||||
{
|
||||
std::vector<ui8> allowedSpells;
|
||||
allowedSpells.resize(GameConstants::SPELLS_QUANTITY, 1);
|
||||
return allowedSpells;
|
||||
}
|
||||
|
@ -101,6 +101,13 @@ public:
|
||||
std::set<TSpell> mindSpells;
|
||||
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)
|
||||
{
|
||||
h & spells & damageSpells & risingSpells & mindSpells;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "../CTownHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CDefObjInfoHandler.h"
|
||||
#include "../CSpellHandler.h"
|
||||
|
||||
SHeroName::SHeroName() : heroId(-1)
|
||||
{
|
||||
@ -13,8 +14,8 @@ SHeroName::SHeroName() : heroId(-1)
|
||||
}
|
||||
|
||||
PlayerInfo::PlayerInfo(): canHumanPlay(false), canComputerPlay(false),
|
||||
aiTactic(EAiTactic::RANDOM), isFactionRandom(false), mainHeroPortrait(-1), hasMainTown(true),
|
||||
generateHeroAtMainTown(true), team(255), generateHero(false), p7(0), hasHero(false), customHeroID(-1), powerPlaceholders(-1)
|
||||
aiTactic(EAiTactic::RANDOM), isFactionRandom(false), mainHeroPortrait(-1), hasMainTown(false),
|
||||
generateHeroAtMainTown(false), team(255), generateHero(false), p7(0), hasHero(false), customHeroID(-1), powerPlaceholders(-1)
|
||||
{
|
||||
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)
|
||||
{
|
||||
allowedHeroes = VLC->heroh->getDefaultAllowedHeroes();
|
||||
players.resize(GameConstants::PLAYER_LIMIT);
|
||||
}
|
||||
|
||||
CMapHeader::~CMapHeader()
|
||||
@ -138,7 +140,9 @@ CMapHeader::~CMapHeader()
|
||||
|
||||
CMap::CMap() : checksum(0), terrain(nullptr), grailRadious(0)
|
||||
{
|
||||
|
||||
allowedAbilities = VLC->heroh->getDefaultAllowedAbilities();
|
||||
allowedArtifact = VLC->arth->getDefaultAllowedArtifacts();
|
||||
allowedSpell = VLC->spellh->getDefaultAllowedSpells();
|
||||
}
|
||||
|
||||
CMap::~CMap()
|
||||
@ -186,6 +190,7 @@ void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CMap::addBlockVisTiles(CGObjectInstance * obj)
|
||||
{
|
||||
for(int fx=0; fx<8; ++fx)
|
||||
@ -286,3 +291,23 @@ void CMap::eraseArtifactInstance(CArtifactInstance * art)
|
||||
assert(artInstances[art->id] == art);
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,10 +116,10 @@ struct DLL_LINKAGE PlayerInfo
|
||||
/** The list of renamed heroes. */
|
||||
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;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** 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
|
||||
* amount of players and can appear regularly or once a time.
|
||||
* The map event is an event which e.g. gives or takes resources of a specific
|
||||
* amount to/from players and can appear regularly or once a time.
|
||||
*/
|
||||
class DLL_LINKAGE CMapEvent
|
||||
{
|
||||
@ -584,7 +582,7 @@ public:
|
||||
/** Specifies the victory condition. The default value is defeat all enemies. */
|
||||
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;
|
||||
|
||||
/** The number of teams. */
|
||||
@ -714,10 +712,22 @@ public:
|
||||
*/
|
||||
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 */
|
||||
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;
|
||||
|
||||
/** list of rumors */
|
||||
|
386
lib/Mapping/CMapEditManager.cpp
Normal file
386
lib/Mapping/CMapEditManager.cpp
Normal 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)
|
||||
{
|
||||
|
||||
}
|
264
lib/Mapping/CMapEditManager.h
Normal file
264
lib/Mapping/CMapEditManager.h
Normal 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;
|
||||
};
|
@ -40,6 +40,6 @@ public:
|
||||
template <typename Handler> void serialize(Handler &h, const int Version)
|
||||
{
|
||||
h & mapHeader & campaignHeader & scenarioOpts & fileURI & date & playerAmnt & humanPlayers;
|
||||
h & actualHumanPlayers;
|
||||
h & actualHumanPlayers & isRandomMap;
|
||||
}
|
||||
};
|
||||
|
@ -173,7 +173,7 @@ void CMapLoaderH3M::init()
|
||||
for(int f = 0; f < map->objects.size(); ++f)
|
||||
{
|
||||
if(!map->objects[f]->defInfo) continue;
|
||||
addBlockVisibleTiles(map->objects[f]);
|
||||
map->addBlockVisTiles(map->objects[f]);
|
||||
}
|
||||
times.push_back(MapLoadingTime("blocked/visitable tiles", sw.getDiff()));
|
||||
|
||||
@ -231,8 +231,7 @@ void CMapLoaderH3M::readHeader()
|
||||
|
||||
void CMapLoaderH3M::readPlayerInfo()
|
||||
{
|
||||
mapHeader->players.resize(8);
|
||||
for(int i = 0; i < 8; ++i)
|
||||
for(int i = 0; i < mapHeader->players.size(); ++i)
|
||||
{
|
||||
mapHeader->players[i].canHumanPlay = 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();
|
||||
}
|
||||
|
||||
addNewArtifactInstance(a);
|
||||
map->addNewArtifactInstance(a);
|
||||
|
||||
//TODO make it nicer
|
||||
if(a->artType && a->artType->constituents)
|
||||
@ -873,31 +872,16 @@ CArtifactInstance * CMapLoaderH3M::createArtifact(int aid, int spellID /*= -1*/)
|
||||
CCombinedArtifactInstance * comb = dynamic_cast<CCombinedArtifactInstance *>(a);
|
||||
BOOST_FOREACH(CCombinedArtifactInstance::ConstituentInfo & ci, comb->constituentsInfo)
|
||||
{
|
||||
addNewArtifactInstance(ci.art);
|
||||
map->addNewArtifactInstance(ci.art);
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
void CMapLoaderH3M::addNewArtifactInstance(CArtifactInstance * art)
|
||||
{
|
||||
art->id = map->artInstances.size();
|
||||
map->artInstances.push_back(art);
|
||||
}
|
||||
|
||||
void CMapLoaderH3M::readTerrain()
|
||||
{
|
||||
// Allocate memory for terrain data
|
||||
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];
|
||||
}
|
||||
}
|
||||
map->initTerrain();
|
||||
|
||||
// Read terrain
|
||||
for(int a = 0; a < 2; ++a)
|
||||
@ -1276,7 +1260,7 @@ void CMapLoaderH3M::readObjects()
|
||||
case Obj::SEER_HUT:
|
||||
{
|
||||
nobj = readSeerHut();
|
||||
addQuest(nobj);
|
||||
map->addQuest(nobj);
|
||||
break;
|
||||
}
|
||||
case Obj::WITCH_HUT:
|
||||
@ -1584,7 +1568,7 @@ void CMapLoaderH3M::readObjects()
|
||||
case Obj::QUEST_GUARD:
|
||||
{
|
||||
CGQuestGuard * guard = new CGQuestGuard();
|
||||
addQuest(guard);
|
||||
map->addQuest(guard);
|
||||
readQuest(guard);
|
||||
nobj = guard;
|
||||
break;
|
||||
@ -1672,13 +1656,13 @@ void CMapLoaderH3M::readObjects()
|
||||
case Obj::BORDERGUARD:
|
||||
{
|
||||
nobj = new CGBorderGuard();
|
||||
addQuest(nobj);
|
||||
map->addQuest(nobj);
|
||||
break;
|
||||
}
|
||||
case Obj::BORDER_GATE:
|
||||
{
|
||||
nobj = new CGBorderGate();
|
||||
addQuest (nobj);
|
||||
map->addQuest (nobj);
|
||||
break;
|
||||
}
|
||||
case Obj::EYE_OF_MAGI:
|
||||
@ -2244,17 +2228,9 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard)
|
||||
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 * nt = new CGTownInstance();
|
||||
nt->identifier = 0;
|
||||
if(map->version > EMapFormat::ROE)
|
||||
{
|
||||
nt->identifier = read_le_u32(buffer + pos);
|
||||
@ -2421,16 +2397,8 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
|
||||
nt->alignment = buffer[pos];
|
||||
++pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
nt->alignment = 0xff;
|
||||
}
|
||||
pos += 3;
|
||||
|
||||
nt->builded = 0;
|
||||
nt->destroyed = 0;
|
||||
nt->garrisonHero = nullptr;
|
||||
|
||||
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 ret = 0;
|
||||
|
@ -225,13 +225,6 @@ private:
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@ -288,13 +281,6 @@ private:
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -318,13 +304,6 @@ private:
|
||||
*/
|
||||
void readEvents();
|
||||
|
||||
/**
|
||||
* Adds object instance to block visitable tiles.
|
||||
*
|
||||
* @param obj the object to add
|
||||
*/
|
||||
void addBlockVisibleTiles(CGObjectInstance * obj);
|
||||
|
||||
/**
|
||||
* Reverses the input argument.
|
||||
*
|
||||
|
@ -1,8 +1,10 @@
|
||||
#include "StdInc.h"
|
||||
#include "CMapGenOptions.h"
|
||||
|
||||
#include "../GameConstants.h"
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
@ -13,7 +15,7 @@ si32 CMapGenOptions::getWidth() const
|
||||
return width;
|
||||
}
|
||||
|
||||
void CMapGenOptions::setWidth(int value)
|
||||
void CMapGenOptions::setWidth(si32 value)
|
||||
{
|
||||
if(value > 0)
|
||||
{
|
||||
@ -21,7 +23,7 @@ void CMapGenOptions::setWidth(int value)
|
||||
}
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
if((value >= 1 && value <= 8) || value == RANDOM_SIZE)
|
||||
if((value >= 1 && value <= GameConstants::PLAYER_LIMIT) || value == RANDOM_SIZE)
|
||||
{
|
||||
playersCnt = value;
|
||||
}
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
if(value == RANDOM_SIZE || (value >= 0 && value <= 8 - playersCnt))
|
||||
if(value == RANDOM_SIZE || (value >= 0 && value <= GameConstants::PLAYER_LIMIT - playersCnt))
|
||||
{
|
||||
compOnlyPlayersCnt = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
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) +
|
||||
"(playersCount)> or -1 for random.");
|
||||
"between 0 and <GameConstants::PLAYER_LIMIT - " + boost::lexical_cast<std::string>(playersCnt) +
|
||||
"(playersCount)> or CMapGenOptions::RANDOM_SIZE for random.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +116,7 @@ si8 CMapGenOptions::getCompOnlyTeamsCnt() const
|
||||
|
||||
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;
|
||||
}
|
||||
@ -121,7 +124,7 @@ void CMapGenOptions::setCompOnlyTeamsCnt(si8 value)
|
||||
{
|
||||
throw std::runtime_error(std::string("Computer only teams count of RMG options should be ") +
|
||||
"between 0 and <" + boost::lexical_cast<std::string>(compOnlyPlayersCnt) +
|
||||
"(compOnlyPlayersCnt) - 1> or -1 for random.");
|
||||
"(compOnlyPlayersCnt) - 1> or CMapGenOptions::RANDOM_SIZE for random.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,61 +89,61 @@ public:
|
||||
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.
|
||||
*
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
@ -213,6 +213,8 @@ public:
|
||||
template <typename Handler>
|
||||
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 & compOnlyTeamsCnt & waterContent & monsterStrength;
|
||||
}
|
||||
|
399
lib/RMG/CMapGenerator.cpp
Normal file
399
lib/RMG/CMapGenerator.cpp
Normal 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
199
lib/RMG/CMapGenerator.h
Normal 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;
|
||||
};
|
@ -42,6 +42,7 @@ struct PlayerSettings
|
||||
|
||||
std::string name;
|
||||
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>
|
||||
void serialize(Handler &h, const int version)
|
||||
{
|
||||
@ -55,13 +56,12 @@ struct PlayerSettings
|
||||
h & name;
|
||||
h & playerID;
|
||||
h & team;
|
||||
h & compOnly;
|
||||
}
|
||||
|
||||
PlayerSettings()
|
||||
PlayerSettings() : bonus(RANDOM), castle(NONE), heroPortrait(RANDOM), compOnly(false)
|
||||
{
|
||||
bonus = RANDOM;
|
||||
castle = NONE;
|
||||
heroPortrait = RANDOM;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -31,6 +31,9 @@ namespace GameConstants
|
||||
"demoniac", "heretic", "deathknight", "necromancer", "warlock", "overlord",
|
||||
"barbarian", "battlemage", "beastmaster", "witch", "planeswalker", "elementalist"
|
||||
};
|
||||
const std::string PLAYER_COLOR_NAMES [PLAYER_LIMIT] = {
|
||||
"red", "blue", "tan", "green", "orange", "purple", "teal", "pink"
|
||||
};
|
||||
}
|
||||
|
||||
namespace EAlignment
|
||||
|
@ -197,7 +197,9 @@
|
||||
<ClCompile Include="Mapping\CMap.cpp" />
|
||||
<ClCompile Include="Mapping\CMapInfo.cpp" />
|
||||
<ClCompile Include="Mapping\CMapService.cpp" />
|
||||
<ClCompile Include="Mapping\CMapEditManager.cpp" />
|
||||
<ClCompile Include="RMG\CMapGenOptions.cpp" />
|
||||
<ClCompile Include="RMG\CMapGenerator.cpp" />
|
||||
<ClCompile Include="HeroBonus.cpp" />
|
||||
<ClCompile Include="CBattleCallback.cpp" />
|
||||
<ClCompile Include="IGameCallback.cpp" />
|
||||
@ -239,6 +241,7 @@
|
||||
<ClInclude Include="CondSh.h" />
|
||||
<ClInclude Include="Connection.h" />
|
||||
<ClInclude Include="ConstTransitivePtr.h" />
|
||||
<ClInclude Include="CRandomGenerator.h" />
|
||||
<ClInclude Include="CScriptingModule.h" />
|
||||
<ClInclude Include="CSpellHandler.h" />
|
||||
<ClInclude Include="CStopWatch.h" />
|
||||
@ -258,7 +261,9 @@
|
||||
<ClInclude Include="Mapping\CMap.h" />
|
||||
<ClInclude Include="Mapping\CMapInfo.h" />
|
||||
<ClInclude Include="Mapping\CMapService.h" />
|
||||
<ClInclude Include="Mapping\CMapEditManager.h" />
|
||||
<ClInclude Include="RMG\CMapGenOptions.h" />
|
||||
<ClInclude Include="RMG\CMapGenerator.h" />
|
||||
<ClInclude Include="GameConstants.h" />
|
||||
<ClInclude Include="HeroBonus.h" />
|
||||
<ClInclude Include="CBattleCallback.h" />
|
||||
|
Loading…
Reference in New Issue
Block a user